- 为多个 Popup 组件添加统一的动画持续时间和最小高度样式 - 在多个表单标题和标签前添加相应功能图标提升可读性 - 引入 Icon 组件并在合适位置应用 - 调整部分组件导入顺序以符合规范 - 优化表格列渲染逻辑以支持图标显示 - 修复部分 JSX 结构以改善布局展示效果
212 lines
6.8 KiB
TypeScript
212 lines
6.8 KiB
TypeScript
import { Button, Input, Popup, SafeArea, Toast } from "@nutui/nutui-react-taro";
|
||
import { useEffect, useState } from "react";
|
||
import auth from "@/services/auth";
|
||
import { userStore } from "@/store/user-store";
|
||
import Taro from "@tarojs/taro";
|
||
import { aesEncrypt } from "@/utils/aes";
|
||
import Captcha from "@/components/captcha";
|
||
import { Image, Text, View } from "@tarojs/components";
|
||
import base from "@/hocs/base";
|
||
import { Icon } from "@/components";
|
||
import dayjs from "dayjs";
|
||
import { APP_VERSION } from "@/constant";
|
||
|
||
export default base(function Page() {
|
||
const setUser = userStore((state) => state.setUser);
|
||
const [showBasic, setShowBasic] = useState<boolean>(false);
|
||
|
||
const [username, setUsername] = useState<string>();
|
||
const [password, setPassword] = useState<string>();
|
||
|
||
const [channel, setChannel] = useState<BusinessAPI.ChannelVO>();
|
||
|
||
const initChannel = async () => {
|
||
const { data } = await auth.channel.selectChannelByDomain({
|
||
domain: "operation.erp.qilincloud168.com",
|
||
});
|
||
|
||
setChannel(data.data);
|
||
};
|
||
|
||
useEffect(() => {
|
||
initChannel().then();
|
||
}, []);
|
||
|
||
return (
|
||
<View
|
||
style={{
|
||
//@ts-ignore
|
||
"--nutui-input-padding": 0,
|
||
}}
|
||
>
|
||
<Popup
|
||
duration={150}
|
||
style={{
|
||
minHeight: "auto",
|
||
}}
|
||
visible={showBasic}
|
||
onClose={() => {
|
||
setShowBasic(false);
|
||
}}
|
||
>
|
||
<Captcha
|
||
handleClick={async (token: string) => {
|
||
const formData = {
|
||
token: token,
|
||
username: username!,
|
||
password: aesEncrypt(password!),
|
||
};
|
||
const { data } = await auth.userAuth.passwordLogin(formData);
|
||
if (data.success) {
|
||
await setUser();
|
||
setShowBasic(false);
|
||
|
||
Taro.reLaunch({
|
||
url: `/pages/main/index/index`,
|
||
});
|
||
}
|
||
}}
|
||
/>
|
||
</Popup>
|
||
<View className="flex flex-1 flex-col items-center justify-center px-4 pb-20">
|
||
<View className="mt-16 mb-8 w-full max-w-md text-center">
|
||
{/* Logo展示区域 */}
|
||
<View className="mb-4 flex justify-center">
|
||
{channel?.logo && (
|
||
<Image src={channel.logo} className="h-16 w-16" />
|
||
)}
|
||
</View>
|
||
<View className="mb-4 text-xl font-bold text-gray-900">
|
||
{channel?.title}
|
||
</View>
|
||
<View className="mx-auto h-px w-24 bg-gray-200"></View>
|
||
</View>
|
||
|
||
<View className="flex w-full max-w-md flex-col gap-4">
|
||
<View>
|
||
<View className="mb-2 block text-sm font-bold text-gray-900">
|
||
账号
|
||
</View>
|
||
<View className="border-primary-200 flex w-full flex-row items-center justify-between rounded-xl border-4 border-gray-300 pr-4">
|
||
<View className="flex size-10 items-center justify-center">
|
||
<Icon
|
||
name={"user"}
|
||
size={24}
|
||
color={"var(--color-neutral-darkest)"}
|
||
/>
|
||
</View>
|
||
<Input
|
||
type="text"
|
||
id="username"
|
||
name="username"
|
||
style={{
|
||
// @ts-ignore
|
||
"--nutui-input-font-size": "var(--text-sm)",
|
||
"--nutui-input-lineheight":
|
||
"var(--tw-leading, var(--text-sm--line-height))",
|
||
"--nutui-color-title": "#9CA3AF",
|
||
}}
|
||
placeholder="请输入您的手机号"
|
||
value={username}
|
||
onChange={(value) => {
|
||
setUsername(value);
|
||
}}
|
||
/>
|
||
</View>
|
||
</View>
|
||
|
||
<View>
|
||
<View className="mb-2 block text-sm font-bold text-gray-900">
|
||
密码
|
||
</View>
|
||
<View className="border-primary-200 flex w-full flex-row items-center justify-between rounded-xl border-4 border-gray-300 pr-4">
|
||
<View className="flex size-10 items-center justify-center">
|
||
<Icon
|
||
name={"lock"}
|
||
size={24}
|
||
color={"var(--color-neutral-darkest)"}
|
||
/>
|
||
</View>
|
||
<Input
|
||
style={{
|
||
// @ts-ignore
|
||
"--nutui-input-font-size": "var(--text-sm)",
|
||
"--nutui-input-lineheight":
|
||
"var(--tw-leading, var(--text-sm--line-height))",
|
||
"--nutui-color-title": "#9CA3AF",
|
||
}}
|
||
type="password"
|
||
id="password"
|
||
name="password"
|
||
placeholder="请输入您的密码"
|
||
value={password}
|
||
onChange={(value) => {
|
||
setPassword(value);
|
||
}}
|
||
/>
|
||
</View>
|
||
</View>
|
||
|
||
<Button
|
||
block
|
||
size={"xlarge"}
|
||
type={"primary"}
|
||
onClick={async () => {
|
||
if (!username || !password) {
|
||
Toast.show("toast", {
|
||
title: "",
|
||
content: "请输入手机号和密码",
|
||
icon: "warn",
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (username.trim() === "" || password.trim() === "") {
|
||
Toast.show("toast", {
|
||
title: "",
|
||
content: "请输入手机号和密码",
|
||
icon: "warn",
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 密码强度校验
|
||
const passwordRegex =
|
||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
||
if (!passwordRegex.test(password)) {
|
||
Toast.show("toast", {
|
||
title: "",
|
||
content:
|
||
"密码必须至少8个字符,包含至少一个大写字母、一个小写字母、一个数字和一个特殊字符",
|
||
icon: "warn",
|
||
});
|
||
return;
|
||
}
|
||
|
||
setShowBasic(true);
|
||
}}
|
||
>
|
||
登 录
|
||
</Button>
|
||
|
||
<View className="pt-2 text-center">
|
||
<Text className="text-sm text-gray-600 hover:text-gray-800">
|
||
忘记密码?
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
<View className="fixed bottom-0 flex w-full flex-col py-4 text-center">
|
||
<Text className="text-xs text-gray-500">版本号:{APP_VERSION}</Text>
|
||
<Text className="mt-1 text-xs text-gray-500">
|
||
© {dayjs().format("YYYY")} {channel?.technicalSupport} 版权所有
|
||
</Text>
|
||
<SafeArea position={"bottom"} />
|
||
</View>
|
||
|
||
<SafeArea position={"bottom"} />
|
||
</View>
|
||
);
|
||
});
|