微调样式

This commit is contained in:
sijinhui 2024-05-26 13:17:35 +08:00
parent a697822445
commit b7a216c35b
8 changed files with 212 additions and 82 deletions

View File

@ -1,23 +1,31 @@
"use client";
import { signIn } from "next-auth/react";
import React, { useState, useEffect, useRef, use } from "react";
import React, { useState, useEffect, useRef, use, useMemo } from "react";
import { isName } from "@/lib/auth_list";
import {
Form,
Tabs,
Input,
Button,
InputRef,
notification as notificationModule,
NotificationArgsProps,
} from "antd";
import { UserOutlined, MailOutlined } from "@ant-design/icons";
import {
UserOutlined,
MailOutlined,
LoadingOutlined,
AudioOutlined,
} from "@ant-design/icons";
import type { FormProps, TabsProps } from "antd";
import { SignInOptions } from "next-auth/react";
import { getSession } from "next-auth/react";
export default function UserLoginCore() {
const [loading, setLoading] = useState(false);
const [capLoading, setCapLoading] = useState(false);
const [timeLeft, setTimeLeft] = useState(0);
const [loginForm] = Form.useForm();
const [loginMethod, setLoginMethod] = useState<"common" | "cap">("common");
const [notification, notificationContextHolder] =
@ -37,11 +45,53 @@ export default function UserLoginCore() {
}
};
useEffect(() => {
let timer = undefined;
if (timeLeft > 0) {
timer = setTimeout(() => {
setTimeLeft(timeLeft - 1);
}, 1000);
}
return () => clearTimeout(timer);
}, [timeLeft]);
const capIcon = useMemo(() => {
if (capLoading) {
return (
<LoadingOutlined
style={{
fontSize: 16,
color: "rgb(234, 149, 24)",
}}
/>
);
}
return <></>;
}, [capLoading]);
const sendCap = () => {
loginForm.validateFields().then((values) => {
setCapLoading(true);
signIn("email", {
redirect: false,
email: values.email,
}).then((result) => {
console.log("33333333333", result);
setCapLoading(false);
setTimeLeft(60);
});
});
// const email = loginForm.getFieldValue("email");
// console.log('----------', email)
};
// const [error, setError] = useState(false);
type FieldType = {
username?: string;
password?: string;
email?: string;
cap?: string;
};
const onFinish: FormProps<FieldType>["onFinish"] = (values) => {
setLoading(true);
@ -52,7 +102,20 @@ export default function UserLoginCore() {
if (loginMethod === "cap") {
loginProvider = "email";
signInOptions = { ...signInOptions, email: values.email };
signInOptions = {
...signInOptions,
email: values.email,
cap: values.cap,
};
fetch(
`/api/auth/callback/email?token=${values.cap}&email=${values.email}`,
).then((result) => {
console.log("------------", result);
if (result.redirected) {
window.location.href = result.url;
}
});
return;
} else {
loginProvider = "credentials";
signInOptions = {
@ -257,26 +320,63 @@ export default function UserLoginCore() {
type: "email",
message: "The input is not valid E-mail!",
},
// {
// validator: async (_, value) => {
// const username_value = loginForm.getFieldValue("username");
// if (value && username_value) {
// return Promise.reject(new Error("Field must be unique!"));
// }
// },
// },
{
required: true,
},
]}
>
<Input
prefix={
<MailOutlined style={{ color: "rgba(0,0,0,.25)" }} />
}
addonAfter={
<button
onClick={sendCap}
disabled={capLoading || timeLeft > 0}
className="align-bottom"
>
{capLoading ? (
<span style={{ width: "70px" }}>
<LoadingOutlined
style={{
fontSize: 16,
color: "rgb(234, 149, 24)",
}}
/>
</span>
) : timeLeft > 0 ? (
<span style={{ color: "gray" }}>
{timeLeft}
</span>
) : (
"发送验证码"
)}
</button>
}
size="middle"
placeholder="邮箱验证,测试阶段"
className={
"text-sm font-medium text-stone-600 dark:text-stone-400"
}
/>
</Form.Item>
<Form.Item
name="cap"
rules={[
{
len: 4,
message: "Make sure it's at 4 characters",
},
]}
>
<Input
size="middle"
placeholder="验证码"
className={
"text-sm font-medium text-stone-600 dark:text-stone-400"
}
/>
</Form.Item>
</>
)}
</div>

View File

@ -126,3 +126,7 @@ input {
#set-password-form {
text-align: right !important;
}
#login-form .ant-input-group-addon {
background-color: white;
}

View File

@ -550,8 +550,7 @@
.chat-input-panel {
position: relative;
width: 100%;
padding: 20px;
padding-top: 10px;
padding: 10px 20px 25px;
box-sizing: border-box;
flex-direction: column;
border-top: var(--border-in-light);
@ -667,6 +666,10 @@
position: absolute;
right: 100px;
bottom: 32px;
@media only screen and (max-width: 600px) {
right: 110px;
}
}
@media only screen and (max-width: 600px) {
@ -677,4 +680,20 @@
.chat-input-send {
bottom: 30px;
}
.bottom-tip {
padding-left: 60px;
padding-right: 60px;
}
}
.bottom-tip {
text-align: center;
margin-top: -25px;
font-size: .75rem;
line-height: 1.5rem;
//margin-bottom: .5rem;
//padding-top: .5rem;
position: relative;
z-index: 1;
color: #7d7d7d !important;
}

View File

@ -1786,6 +1786,19 @@ function _Chat() {
/>
</label>
</div>
<div
className={styles["bottom-tip"]}
// style={{
// color: "var(--text-secondary)",
// fontSize: ".75rem",
// lineHeight: "1rem",
// textAlign: "center",
// paddingBottom: ".5rem",
// paddingTop: ".5rem"
// }}
>
<span>AI </span>
</div>
{showExport && (
<ExportMessageModal onClose={() => setShowExport(false)} />

View File

@ -677,7 +677,7 @@ export function Settings() {
</div>
</div>
</div>
<div className={styles["settings"]}>
<div className={styles["settings"]} id="user-settings">
<List>
<ListItem title={Locale.Settings.Avatar}>
<Popover

View File

@ -112,7 +112,7 @@ const cn = {
if (submitKey === String(SubmitKey.Enter)) {
inputHints += "Shift + Enter 换行";
}
return inputHints + "/ 触发补全,: 触发命令";
return inputHints + "/ 内置提示词";
},
Send: "发送",
Config: {

View File

@ -224,20 +224,20 @@ input[type="range"]::-ms-thumb:hover {
@include thumbHover();
}
//input[type="number"],
//input[type="text"],
//input[type="password"] {
// appearance: none;
// border-radius: 10px;
// border: var(--border-in-light);
// min-height: 36px;
// box-sizing: border-box;
// background: var(--white);
// color: var(--black);
// padding: 0 10px;
// max-width: 50%;
// font-family: inherit;
//}
#user-settings {
input[type="number"], input[type="text"], input[type="password"] {
appearance: none;
border-radius: 10px;
border: var(--border-in-light);
min-height: 36px;
box-sizing: border-box;
background: var(--white);
color: var(--black);
padding: 0 10px;
max-width: 50%;
font-family: inherit;
}
}
div.math {
overflow-x: auto;

View File

@ -8,8 +8,10 @@ import { User } from "@prisma/client";
import { isEmail, isName } from "@/lib/auth_list";
import {createTransport} from "nodemailer";
import { comparePassword, hashPassword } from "@/lib/utils";
import { randomInt, randomBytes } from "crypto";
const SECURE_COOKIES:boolean = !!process.env.SECURE_COOKIES;
let verificationTokens = new Map();
export const authOptions: NextAuthOptions = {
@ -44,28 +46,38 @@ export const authOptions: NextAuthOptions = {
},
},
from: process.env.EMAIL_FROM,
maxAge: 5 * 60,
async generateVerificationToken() {
return randomBytes(4).toString("hex").substring(0, 4);
},
async sendVerificationRequest({
identifier: email,
url,
token,
provider: { server, from, name },
theme,
}) {
/* your function */
console.log('send mail,', email, url, server, from, )
const { host } = new URL(url)
const transport = createTransport(server)
const result = await transport.sendMail({
to: email,
from: from,
subject: `Sign in to ${host}`,
html: email_html({ url, host, theme }),
})
// const token = randomInt(1000, 10000);
verificationTokens.set(email, token);
/* your function */
const { host } = new URL(url)
// console.log('send mail,-----', email, host, token )
const transport = createTransport(server)
const result = await transport.sendMail({
to: email,
from: from,
subject: `Your sign-in code for ${host}`,
text: email_text({url, token, host}),
html: email_html({ url, token, host, theme }),
})
const failed = result.rejected.concat(result.pending).filter(Boolean)
console.log('[result],', result)
if (failed.length) {
throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`)
}
},
}),
CredentialsProvider({
@ -290,6 +302,7 @@ function cleanPassword(input: string): string {
/**
* Email HTML body
* Insert invisible space into domains from being turned into a hyperlink by email
@ -298,50 +311,31 @@ function cleanPassword(input: string): string {
*
* @note We don't add the email address to avoid needing to escape it, if you do, remember to sanitize it!
*/
function email_html(params: { url: string, host: string, theme: Theme }) {
const { url, host, theme } = params
function email_text(params: { url: string, token: number|string, host: string}) {
const { url, token, host } = params;
return `Sign in to ${host}\n\nYour sign-in code is: ${token}\n\nOr click on this link to sign in:\n${url}\n\n`;
}
function email_html(params: { url: string, token: number|string, host: string, theme: Theme }) {
const { url, token, host, theme } = params
const escapedHost = host.replace(/\./g, "&#8203;.")
const escapedUrl = url.replace(/\./g, "&#8203;.")
const brandColor = theme.brandColor || "#346df1"
const color = {
background: "#f9f9f9",
text: "#444",
mainBackground: "#fff",
buttonBackground: brandColor,
buttonBorder: brandColor,
buttonText: theme.buttonText || "#fff",
}
// const brandColor = theme.brandColor || "#346df1"
// const color = {
// background: "#f9f9f9",
// text: "#444",
// mainBackground: "#fff",
// buttonBackground: brandColor,
// buttonBorder: brandColor,
// buttonText: theme.buttonText || "#fff",
// }
return `
<body style="background: ${color.background};">
<table width="100%" border="0" cellspacing="20" cellpadding="0"
style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
<tr>
<td align="center"
style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
Sign in to <strong>${escapedHost}</strong>
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
target="_blank"
style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
in</a></td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center"
style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
If you did not request this email you can safely ignore it.
</td>
</tr>
</table>
</body>
`
return `<p>Sign in to <strong>${escapedHost}</strong></p>
<p>Your sign-in code is: <strong>${token}</strong></p>
<p>Or click on this link to sign in:</p>
<p><a href="${url}">${escapedUrl}</a></p>
<p>If you did not request this email, you can safely ignore it.</p>`;
}