From 94f2fa94b743427f97ef3ccc0f958defc7d50c91 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Fri, 20 Sep 2024 16:31:57 +0800 Subject: [PATCH] v2.2 --- ecosystem.config.js | 20 + src/app/login/page.tsx | 2 +- src/ui/(console)/create/create-tabs.tsx | 16 +- src/ui/login/login-form.tsx | 531 ++++++++++++----------- src/ui/login/register-form.tsx | 541 ++++++++++++------------ src/ui/page/page-expect.tsx | 4 +- 6 files changed, 584 insertions(+), 530 deletions(-) create mode 100644 ecosystem.config.js diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..cc444fa --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,20 @@ +module.exports = { + apps: [ + { + name: 'nextjs-app', // 应用名称 + script: 'npm', // 使用 npm 脚本 + args: 'run start', // 启动应用的命令 + cwd: './', // 项目的路径 + watch: false, // 是否监控文件变化 + env: { + NODE_ENV: 'production', // 生产环境变量 + }, + // 在启动前执行构建 + exec_mode: 'fork', // 使用 fork 模式 + instances: 1, // 启动的实例数量 + autorestart: true, // 自动重启 + max_memory_restart: '1G', // 内存超过 1G 时自动重启 + pre_start: 'npm run build', // 启动前执行构建 + }, + ], +}; diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 37a65eb..a2bb3bc 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -22,7 +22,7 @@ export default function Page() { className="absolute top-0 left-0 w-full h-full object-cover z-[-1]" > diff --git a/src/ui/(console)/create/create-tabs.tsx b/src/ui/(console)/create/create-tabs.tsx index e3faa34..4f1399f 100644 --- a/src/ui/(console)/create/create-tabs.tsx +++ b/src/ui/(console)/create/create-tabs.tsx @@ -28,41 +28,41 @@ export default function CreateTabs() { value: "text", component: , videoTemp: - "http://file.typeframes.com.cn/img-to-video/cn/demo_video.webm", + "https://file.typeframes.com.cn/img-to-video/cn/demo_video.webm", }, { name: t("aiImageVideoGenerator"), value: "image", component: , videoTemp: - "http://file.typeframes.com.cn/img-to-video/cn/demo_image.webm", + "https://file.typeframes.com.cn/img-to-video/cn/demo_image.webm", }, { name: t("aiTiktokVideoGenerator"), value: "tiktok", component: , videoTemp: - "http://file.typeframes.com.cn/create-tiktok-video/en/demo_video.webm", + "https://file.typeframes.com.cn/create-tiktok-video/en/demo_video.webm", ImageTemp: - "http://file.typeframes.com.cn/create-tiktok-video/en/demo_demo.webm", + "https://file.typeframes.com.cn/create-tiktok-video/en/demo_demo.webm", }, { name: t("aiTalkingAvatarVideoCreator"), value: "avatar", component: , videoTemp: - "http://file.typeframes.com.cn/create-Avatar-video/en/demo_video.webm", + "https://file.typeframes.com.cn/create-Avatar-video/en/demo_video.webm", ImageTemp: - "http://file.typeframes.com.cn/create-Avatar-video/en/demo_video.webm", + "https://file.typeframes.com.cn/create-Avatar-video/en/demo_video.webm", }, { name: t("aiMusicVideoGenerator"), value: "music", component: , videoTemp: - "http://file.typeframes.com.cn/music-to-video/cn/demo_video.webm", + "https://file.typeframes.com.cn/music-to-video/cn/demo_video.webm", ImageTemp: - "http://file.typeframes.com.cn/music-to-video/cn/demo_video.webm", + "https://file.typeframes.com.cn/music-to-video/cn/demo_video.webm", }, ]; diff --git a/src/ui/login/login-form.tsx b/src/ui/login/login-form.tsx index 63445da..0f84807 100644 --- a/src/ui/login/login-form.tsx +++ b/src/ui/login/login-form.tsx @@ -12,275 +12,296 @@ import { useTranslations } from "next-intl"; import Link from "next/link"; interface LoginFormProps { - toRegister: () => void; + toRegister: () => void; } type LoginMethod = "password" | "code"; export default function LoginForm({ toRegister }: LoginFormProps) { - const t = useTranslations("loginForm"); - const tTerms = useTranslations("terms"); // 用于获取用户协议的标题 - const tGlobal = useTranslations(); // 用于获取全局翻译,如 "agreeTo" 和 "agreeToTerms" - const [loginMethod, setLoginMethod] = useState("password"); - const [username, setUsername] = useState(""); - const [phone, setPhone] = useState(""); - const [password, setPassword] = useState(""); - const [captcha, setCaptcha] = useState(""); - const [captchaTimer, setCaptchaTimer] = useState(0); - const [agreed, setAgreed] = useState(true); // 默认勾选 + const t = useTranslations("loginForm"); + const tTerms = useTranslations("terms"); // 用于获取用户协议的标题 + const tGlobal = useTranslations(); // 用于获取全局翻译,如 "agreeTo" 和 "agreeToTerms" + const [loginMethod, setLoginMethod] = useState("password"); + const [username, setUsername] = useState(""); + const [phone, setPhone] = useState(""); + const [password, setPassword] = useState(""); + const [captcha, setCaptcha] = useState(""); + const [captchaTimer, setCaptchaTimer] = useState(0); + const [agreed, setAgreed] = useState(true); // 默认勾选 - const router = useRouter(); - const { addToast } = useToast(); + const router = useRouter(); + const { addToast } = useToast(); - const setUser = useUserStore((state) => state.setUser); + const setUser = useUserStore((state) => state.setUser); - const { - fetchData: login, - loading: loginLoading, - error: loginError, - data: loginData, - } = useFetch({ - url: "/api/login/", - method: "POST", - }); + const { + fetchData: login, + loading: loginLoading, + error: loginError, + data: loginData, + } = useFetch({ + url: "/api/login/", + method: "POST", + }); - const { - fetchData: fetchCaptcha, - loading: captchaLoading, - error: captchaError, - data: captchaData, - } = useFetch({ - url: "/api/send-verification-sms/", - method: "POST", - }); - const { - fetchData: GetUserinfo, - loading: userinfoLoading, - data: userinfoData, - } = useFetch({ - url: "/api/profile/", - method: "POST", - }); + const { + fetchData: fetchCaptcha, + loading: captchaLoading, + error: captchaError, + data: captchaData, + } = useFetch({ + url: "/api/send-verification-sms/", + method: "POST", + }); + const { + fetchData: GetUserinfo, + loading: userinfoLoading, + data: userinfoData, + } = useFetch({ + url: "/api/profile/", + method: "POST", + }); - useEffect(() => { - let timer: NodeJS.Timeout; - if (captchaTimer > 0) { - timer = setInterval(() => { - setCaptchaTimer((prev) => prev - 1); - }, 1000); - } - return () => clearInterval(timer); - }, [captchaTimer]); + useEffect(() => { + let timer: NodeJS.Timeout; + if (captchaTimer > 0) { + timer = setInterval(() => { + setCaptchaTimer((prev) => prev - 1); + }, 1000); + } + return () => clearInterval(timer); + }, [captchaTimer]); - const handleGetCaptcha = async () => { - if (!phone) { - addToast(t("enterPhoneNumber"), "error"); - return; - } - await fetchCaptcha({ phone_number: phone }); - setCaptchaTimer(60); - }; - - const handleSubmit = async (event: React.FormEvent) => { - event.preventDefault(); - - if (!agreed) { - addToast(tGlobal("agreeToTerms"), "error"); - return; - } - - // 校验非空字段 - if (loginMethod === "password" && !username) { - addToast(t("enterUsername"), "error"); - return; - } - if (loginMethod === "code" && !phone) { - addToast(t("enterPhone"), "error"); - return; - } - - if (loginMethod === "password" && !password) { - addToast(t("enterPassword"), "error"); - return; - } - - if (loginMethod === "code" && !captcha) { - addToast(t("enterCaptcha"), "error"); - return; - } - - const loginData = { - login_type: loginMethod, - ...(loginMethod === "password" && { username, password }), - ...(loginMethod === "code" && { phone, code: captcha }), + const handleGetCaptcha = async () => { + if (!phone) { + addToast(t("enterPhoneNumber"), "error"); + return; + } + await fetchCaptcha({ phone_number: phone }); + setCaptchaTimer(60); }; - // 调用登录函数发送登录请求 - login(loginData).then(() => { - addToast(t("loginSuccess"), "success"); + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); - GetUserinfo().then((res) => { - console.log("userinfo", res); - setUser(res); - }); - router.replace("/create"); - }); - }; + if (!agreed) { + addToast(tGlobal("agreeToTerms"), "error"); + return; + } - return ( -
-
- -
- - {t("orUseLoginMethod")} - -
+ // 校验非空字段 + if (loginMethod === "password" && !username) { + addToast(t("enterUsername"), "error"); + return; + } + if (loginMethod === "code" && !phone) { + addToast(t("enterPhone"), "error"); + return; + } + + if (loginMethod === "password" && !password) { + addToast(t("enterPassword"), "error"); + return; + } + + if (loginMethod === "code" && !captcha) { + addToast(t("enterCaptcha"), "error"); + return; + } + + const loginData = { + login_type: loginMethod, + ...(loginMethod === "password" && { username, password }), + ...(loginMethod === "code" && { phone, code: captcha }), + }; + + // 调用登录函数发送登录请求 + login(loginData).then(() => { + addToast(t("loginSuccess"), "success"); + + GetUserinfo().then((res) => { + console.log("userinfo", res); + setUser(res); + }); + router.replace("/create"); + }); + }; + + return ( +
+
+ +
+ + {t("orUseLoginMethod")} + +
+
+ +
+ + +
+ + + + + {t("passwordLogin")} + +
+
+ + + + + {t("codeLogin")} + +
+
+
+ + {loginMethod === "password" && ( + <> + + + setUsername(e.target.value) + } + className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" + placeholder={t("emailOrPhone")} + /> + + + + + setPassword(e.target.value) + } + className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" + placeholder={t("yourPassword")} + /> + + + )} + + {loginMethod === "code" && ( + <> + + + setPhone(e.target.value) + } + className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" + placeholder={t("phone")} + /> + + +
+ + setCaptcha(e.target.value) + } + className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" + placeholder={t("captcha")} + /> + +
+
+ + )} + + {/* 用户协议复选框 */} + +
+ +
+
+ + + {loginLoading ? ( + + ) : ( + {t("login")} + )} + +
+
+ + +
- -
- - -
- - - - - {t("passwordLogin")} - -
-
- - - - - {t("codeLogin")} - -
-
-
- - {loginMethod === "password" && ( - <> - - setUsername(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" - placeholder={t("emailOrPhone")} - /> - - - - setPassword(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" - placeholder={t("yourPassword")} - /> - - - )} - - {loginMethod === "code" && ( - <> - - setPhone(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" - placeholder={t("phone")} - /> - - -
- setCaptcha(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 outline-none focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder:text-gray-500" - placeholder={t("captcha")} - /> - -
-
- - )} - - {/* 用户协议复选框 */} - -
- -
-
- - - {t("login")} - {loginLoading ? ( - - ) : ( - "" - )} - -
-
- - -
-
- ); + ); } diff --git a/src/ui/login/register-form.tsx b/src/ui/login/register-form.tsx index 35fb833..e3049de 100644 --- a/src/ui/login/register-form.tsx +++ b/src/ui/login/register-form.tsx @@ -11,7 +11,7 @@ import { useTranslations } from "next-intl"; import Link from "next/link"; interface RegisterFormProps { - back: () => void; + back: () => void; } const plans: Plan[] = ["phone", "email"]; @@ -19,283 +19,296 @@ const plans: Plan[] = ["phone", "email"]; type Plan = "phone" | "email"; export default function RegisterForm({ back }: RegisterFormProps) { - const t = useTranslations("registerForm"); - const tTerms = useTranslations("terms"); // 用于获取用户协议的标题 - const tGlobal = useTranslations(); // 用于获取全局翻译,如 "agreeTo" 和 "agreeToTerms" - const [selected, setSelected] = useState(plans[0]); - const [email, setEmail] = useState(""); - const [phone, setPhone] = useState(""); - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [confirmPassword, setConfirmPassword] = useState(""); - const [captcha, setCaptcha] = useState(""); - const [captchaTimer, setCaptchaTimer] = useState(0); - const [agreed, setAgreed] = useState(true); // 默认勾选 + const t = useTranslations("registerForm"); + const tTerms = useTranslations("terms"); // 用于获取用户协议的标题 + const tGlobal = useTranslations(); // 用于获取全局翻译,如 "agreeTo" 和 "agreeToTerms" + const [selected, setSelected] = useState(plans[0]); + const [email, setEmail] = useState(""); + const [phone, setPhone] = useState(""); + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [captcha, setCaptcha] = useState(""); + const [captchaTimer, setCaptchaTimer] = useState(0); + const [agreed, setAgreed] = useState(true); // 默认勾选 - const { addToast } = useToast(); + const { addToast } = useToast(); - // 获取验证码 - const { - fetchData: fetchVerificationCode, - data: verificationData, - loading: verificationLoading, - error: verificationError, - } = useFetch({ - url: "/api/send-verification-sms/", - method: "POST", - }); + // 获取验证码 + const { + fetchData: fetchVerificationCode, + data: verificationData, + loading: verificationLoading, + error: verificationError, + } = useFetch({ + url: "/api/send-verification-sms/", + method: "POST", + }); - // 注册 - const { - fetchData: register, - data: registerData, - loading: registerLoading, - error: registerError, - } = useFetch({ - url: "/api/register/", - method: "POST", - }); + // 注册 + const { + fetchData: register, + data: registerData, + loading: registerLoading, + error: registerError, + } = useFetch({ + url: "/api/register/", + method: "POST", + }); - useEffect(() => { - let timer: NodeJS.Timeout; - if (captchaTimer > 0) { - timer = setInterval(() => { - setCaptchaTimer((prev) => prev - 1); - }, 1000); - } - return () => clearInterval(timer); - }, [captchaTimer]); + useEffect(() => { + let timer: NodeJS.Timeout; + if (captchaTimer > 0) { + timer = setInterval(() => { + setCaptchaTimer((prev) => prev - 1); + }, 1000); + } + return () => clearInterval(timer); + }, [captchaTimer]); - const handleGetCaptcha = async () => { - if (selected === "phone" && !isValidPhoneNumber(phone)) { - addToast(t("invalidPhoneNumber"), "error"); - return; - } + const handleGetCaptcha = async () => { + if (selected === "phone" && !isValidPhoneNumber(phone)) { + addToast(t("invalidPhoneNumber"), "error"); + return; + } - if (selected === "email" && !email) { - addToast(t("enterEmail"), "error"); - return; - } + if (selected === "email" && !email) { + addToast(t("enterEmail"), "error"); + return; + } - const data = - selected === "phone" ? { phone_number: phone } : { email: email }; + const data = + selected === "phone" ? { phone_number: phone } : { email: email }; - await fetchVerificationCode(data); - setCaptchaTimer(60); - }; - - useEffect(() => { - if (verificationData?.code === 200) { - addToast(t("captchaSent"), "success"); - setCaptchaTimer(60); - } - }, [verificationData, verificationError]); - - const handleSubmit = async (event: React.FormEvent) => { - event.preventDefault(); // 阻止表单默认提交行为 - - if (!agreed) { - addToast(tGlobal("agreeToTerms"), "error"); - return; - } - - // 校验非空字段 - if (!username || !password || !confirmPassword || !captcha) { - addToast(t("completeInformation"), "error"); - return; - } - - if (password !== confirmPassword) { - addToast(t("passwordMismatch"), "error"); - return; - } - - // 检查 email 或 phone 是否有效 - if (selected === "email" && !email) { - addToast(t("enterEmail"), "error"); - return; - } - - if (selected === "phone" && !phone) { - addToast(t("enterPhoneNumber"), "error"); - return; - } - - // 构建注册请求参数 - const registerData = { - code: captcha, - confirm_password: confirmPassword, - password, - username, - registerMethod: selected, - ...(selected === "email" && { email }), // 仅当 selected 为 "email" 时添加 email 字段 - ...(selected === "phone" && { phone }), // 仅当 selected 为 "phone" 时添加 phone 字段 + await fetchVerificationCode(data); + setCaptchaTimer(60); }; - // 调用 register 函数发送注册请求 - await register(registerData); - }; + useEffect(() => { + if (verificationData?.code === 200) { + addToast(t("captchaSent"), "success"); + setCaptchaTimer(60); + } + }, [verificationData, verificationError]); - return ( -
-
- -
- {t("register")} -
-
{/* 占位符,使标题居中 */} -
- -
- - - {plans.map((plan) => ( -
- - - - - {t(plan)} - + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); // 阻止表单默认提交行为 + + if (!agreed) { + addToast(tGlobal("agreeToTerms"), "error"); + return; + } + + // 校验非空字段 + if (!username || !password || !confirmPassword || !captcha) { + addToast(t("completeInformation"), "error"); + return; + } + + if (password !== confirmPassword) { + addToast(t("passwordMismatch"), "error"); + return; + } + + // 检查 email 或 phone 是否有效 + if (selected === "email" && !email) { + addToast(t("enterEmail"), "error"); + return; + } + + if (selected === "phone" && !phone) { + addToast(t("enterPhoneNumber"), "error"); + return; + } + + // 构建注册请求参数 + const registerData = { + code: captcha, + confirm_password: confirmPassword, + password, + username, + registerMethod: selected, + ...(selected === "email" && { email }), // 仅当 selected 为 "email" 时添加 email 字段 + ...(selected === "phone" && { phone }), // 仅当 selected 为 "phone" 时添加 phone 字段 + }; + + // 调用 register 函数发送注册请求 + await register(registerData); + }; + + return ( +
+
+ +
+ {t("register")}
- ))} - - - - {selected === "email" && ( - - setEmail(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" - placeholder={t("email")} - /> - - )} - {selected === "phone" && ( - - setPhone(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" - placeholder={t("phoneNumber")} - /> - - )} - - {/* 用户名 */} - - setUsername(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" - placeholder={t("username")} - /> - - - {/* 密码 */} - - setPassword(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" - placeholder={t("password")} - /> - - - {/* 确认密码 */} - - setConfirmPassword(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" - placeholder={t("confirmPassword")} - /> - - - {/* 验证码 */} - -
- setCaptcha(e.target.value)} - className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" - placeholder={t("captcha")} - /> - +
{/* 占位符,使标题居中 */}
-
+ +
+ + + {plans.map((plan) => ( +
+ + + + + {t(plan)} + +
+ ))} +
+
- {/* 用户协议复选框 */} - -
- -
-
+ {selected === "email" && ( + + setEmail(e.target.value)} + className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" + placeholder={t("email")} + /> + + )} + {selected === "phone" && ( + + setPhone(e.target.value)} + className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" + placeholder={t("phoneNumber")} + /> + + )} - - {t("submit")} - {registerLoading ? ( - - ) : null} - + {/* 用户名 */} + + setUsername(e.target.value)} + className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" + placeholder={t("username")} + /> + + + {/* 密码 */} + + setPassword(e.target.value)} + className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" + placeholder={t("password")} + /> + + + {/* 确认密码 */} + + setConfirmPassword(e.target.value)} + className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" + placeholder={t("confirmPassword")} + /> + + + {/* 验证码 */} + +
+ setCaptcha(e.target.value)} + className="h-10 input border-gray-600 bg-gray-800 focus:outline-none w-full rounded-lg text-sm text-gray-300 placeholder-gray-500" + placeholder={t("captcha")} + /> + +
+
+ + {/* 用户协议复选框 */} + +
+ +
+
+ + + {t("submit")} + {registerLoading ? ( + + ) : null} + +
+
- -
- ); + ); } diff --git a/src/ui/page/page-expect.tsx b/src/ui/page/page-expect.tsx index 538860d..0678b20 100644 --- a/src/ui/page/page-expect.tsx +++ b/src/ui/page/page-expect.tsx @@ -31,7 +31,7 @@ export default function PageExpect() { loop muted autoPlay - src="http://file.typeframes.com.cn/create-tiktok-video/en/demo_image.webm" + src="https://file.typeframes.com.cn/create-tiktok-video/en/demo_image.webm" preload="auto" >
@@ -42,7 +42,7 @@ export default function PageExpect() { loop muted autoPlay - src="http://file.typeframes.com.cn/music-to-video/cn/demo_image.webm" + src="https://file.typeframes.com.cn/music-to-video/cn/demo_image.webm" preload="auto" >