diff --git a/src/actions/auth.ts b/src/actions/auth.ts index a7f277e..4672e9d 100644 --- a/src/actions/auth.ts +++ b/src/actions/auth.ts @@ -16,12 +16,13 @@ export async function login(props: { username: string password: string remember: boolean + mode: 'phone_code' | 'password' }): Promise { // 尝试登录 const result = await callByDevice('/api/auth/token', { ...props, grant_type: 'password', - login_type: 'phone_code', + login_type: props.mode, }) if (!result.success) { diff --git a/src/actions/dashboard.ts b/src/actions/dashboard.ts index e29185b..2f5e765 100644 --- a/src/actions/dashboard.ts +++ b/src/actions/dashboard.ts @@ -4,32 +4,32 @@ import {ApiResponse, ExtraResp} from '@/lib/api' import {callByUser} from './base' import {listAnnouncements} from './announcement' -type listAccountReq = { +type statisticsResourceUsageReq = { resource_no?: string create_after?: Date create_before?: Date } -type listAccountResp = { +type statisticsResourceUsageResp = { date: string count: number }[] -export async function listAccount(props: listAccountReq) { - return await callByUser('/api/resource/statistics/usage', props) +export async function statisticsResourceUsage(props: statisticsResourceUsageReq) { + return await callByUser('/api/resource/statistics/usage', props) } export async function statisticsResourceFree() { return await callByUser<{ long: { - ResourceCount: number - ResourceDailyFreeSum: number - ResourceQuotaSum: number + resource_count: number + resource_daily_free_sum: number + resource_quota_sum: number } short: { - ResourceCount: number - ResourceDailyFreeSum: number - ResourceQuotaSum: number + resource_count: number + resource_daily_free_sum: number + resource_quota_sum: number } }>('/api/resource/statistics/free') } @@ -37,7 +37,7 @@ export async function statisticsResourceFree() { type listInitializationResp = { anno: ExtraResp free: ExtraResp - usage: ExtraResp + usage: ExtraResp } export async function listInitialization(): Promise> { const free = await statisticsResourceFree() @@ -59,7 +59,7 @@ export async function listInitialization(): Promise +const pwdSchema = zod.object({ + username: zod.string().min(11, '请输入正确的手机号码').max(11, '请输入正确的手机号码'), + password: zod.string().min(6, '请输入至少6位密码'), + remember: zod.boolean().default(false), +}) + +type FormValues = zod.infer export default function LoginPage(props: LoginPageProps) { const router = useRouter() const [submitting, setSubmitting] = useState(false) const [countdown, setCountdown] = useState(0) const [showCaptcha, setShowCaptcha] = useState(false) + const [loginMode, setLoginMode] = useState<'sms' | 'password'>('sms') + const [showPwd, setShowPwd] = useState(false) const timerRef = useRef(undefined) const form = useForm({ - resolver: zodResolver(formSchema), + resolver: zodResolver(loginMode === 'sms' ? smsSchema : pwdSchema), defaultValues: { username: '', password: '', @@ -103,7 +110,6 @@ export default function LoginPage(props: LoginPageProps) { toast.error(resp.message) return true } - setShowCaptcha(false) waiting = parseInt(resp.message) console.log(resp.message) @@ -132,12 +138,30 @@ export default function LoginPage(props: LoginPageProps) { return prev - 1 }) }, 1000) - return false }, [username]) // 处理表单提交 const onSubmit = async (values: FormValues) => { + // 密码登录时增加更严格的校验 + if (loginMode === 'password') { + const pwd = values.password || '' + // 至少6位,包含字母和数字 + if (pwd.length < 6) { + form.setError('password', { + type: 'manual', + message: '密码长度至少6位', + }) + return + } + if (!/[A-Za-z]/.test(pwd) || !/[0-9]/.test(pwd)) { + form.setError('password', { + type: 'manual', + message: '密码需包含字母和数字', + }) + return + } + } try { setSubmitting(true) @@ -153,7 +177,7 @@ export default function LoginPage(props: LoginPageProps) { if (!values.password) { form.setError('password', { type: 'manual', - message: '请输入验证码', + message: loginMode === 'sms' ? '请输入验证码' : '请输入密码', }) return } @@ -163,11 +187,12 @@ export default function LoginPage(props: LoginPageProps) { username: values.username, password: values.password, remember: values.remember, + mode: loginMode === 'sms' ? 'phone_code' : 'password', // 后端区分登录方式 }) // 登录失败 if (!result.success) { - throw new Error(result.message || '请检查手机号码和验证码是否正确') + throw new Error(result.message || '请检查账号和密码/验证码是否正确') } // 登录成功 @@ -193,7 +218,6 @@ export default function LoginPage(props: LoginPageProps) { const params = useSearchParams() const redirect = params.get('redirect') - const refreshProfile = useProfileStore(store => store.refreshProfile) // ====================== @@ -207,7 +231,6 @@ export default function LoginPage(props: LoginPageProps) { `flex justify-center xl:justify-end items-center`, )}> 背景图 - logo @@ -217,8 +240,32 @@ export default function LoginPage(props: LoginPageProps) { 登录/注册 - + {/* 登录方式切换 */} + + + + + + className="space-y-6" onSubmit={onSubmit} form={form}> {({id, field}) => ( @@ -231,29 +278,65 @@ export default function LoginPage(props: LoginPageProps) { /> )} - - - {({id, field}) => ( -
- - -
- )} + + {({id, field}) => + loginMode === 'sms' ? ( +
+ + +
+ ) : ( +
+ + +
+ ) + }
- {({id, field}) => (
@@ -268,7 +351,6 @@ export default function LoginPage(props: LoginPageProps) {
)}
-
-

登录即表示您同意 《用户协议》 @@ -289,14 +370,14 @@ export default function LoginPage(props: LoginPageProps) { - {/* 图形验证码弹窗 */} - - + {loginMode === 'sms' && ( + + )} ) } diff --git a/src/app/(home)/@footer/page.tsx b/src/app/(home)/@footer/page.tsx index 9d4eafc..3f4d1fc 100644 --- a/src/app/(home)/@footer/page.tsx +++ b/src/app/(home)/@footer/page.tsx @@ -50,7 +50,7 @@ export default function Footer(props: FooterProps) { + initialData?: ExtraResp } export default function Charts({initialData}: ChartsProps) { // const [submittedData, setSubmittedData] = useState>() - const [submittedData, setSubmittedData] = useState>(initialData || []) + const [submittedData, setSubmittedData] = useState>(initialData || []) const formSchema = zod.object({ resource_no: zod.string().optional(), create_after: zod.date().optional(), @@ -58,7 +52,7 @@ export default function Charts({initialData}: ChartsProps) { create_before: value.create_before ?? today, } - const resp = await listAccount(res) + const resp = await statisticsResourceUsage(res) if (!resp.success) { toast.error('接口请求失败:' + resp.message) return @@ -149,7 +143,7 @@ const config = { } satisfies ChartConfig type DashboardChartProps = { - data: ExtraResp + data: ExtraResp } function DashboardChart(props: DashboardChartProps) { diff --git a/src/app/admin/(dashboard)/page.tsx b/src/app/admin/(dashboard)/page.tsx index 89e14b8..04a99ea 100644 --- a/src/app/admin/(dashboard)/page.tsx +++ b/src/app/admin/(dashboard)/page.tsx @@ -9,7 +9,7 @@ import Charts from './_client/charts' import UserCenter from './_client/userCenter' import soon from './_assets/coming-soon.svg' import mask from './_assets/Mask group.webp' -import {Button} from '@/components/ui/button' +import {ExtraResp} from '@/lib/api' export type DashboardPageProps = {} @@ -43,12 +43,7 @@ export default async function DashboardPage(props: DashboardPageProps) { {/* 磁贴集 */} {initData && ( - + )} {/* 图表 */} @@ -69,13 +64,9 @@ export default async function DashboardPage(props: DashboardPageProps) { ) } -type DashboardChartProps = { - short_term: string - short_term_monthly: string - long_term: string - long_term_monthly: string -} -function Pins(props: DashboardChartProps) { +type DashboardPinsProps = ExtraResp['free'] + +function Pins(props: DashboardPinsProps) { return (

{/* 短效 */} @@ -91,7 +82,7 @@ function Pins(props: DashboardChartProps) {

包时

当日可提取数量 - {props.short_term} + {props.short.resource_daily_free_sum}

@@ -99,7 +90,7 @@ function Pins(props: DashboardChartProps) {

包量

剩余可提取数量 - {props.short_term_monthly} + {props.short.resource_quota_sum}

@@ -118,7 +109,7 @@ function Pins(props: DashboardChartProps) {

包时

当日可提取数量 - {props.long_term} + {props.long.resource_daily_free_sum}

@@ -126,7 +117,7 @@ function Pins(props: DashboardChartProps) {

包量

剩余可提取数量 - {props.long_term_monthly} + {props.long.resource_quota_sum}

diff --git a/src/app/admin/profile/page.tsx b/src/app/admin/profile/page.tsx index cfe8bc2..a10788b 100644 --- a/src/app/admin/profile/page.tsx +++ b/src/app/admin/profile/page.tsx @@ -324,7 +324,16 @@ function PasswordForm(props: { type Schema = z.infer const form = useForm({ - resolver: zodResolver(schema), + resolver: zodResolver( + schema.refine( + data => + /^(?=.*[a-z])(?=.*[A-Z]).{6,}$/.test(data.password), + { + message: '密码需包含大小写字母,且不少于6位', + path: ['password'], + }, + ), + ), defaultValues: { phone: '', captcha: '', @@ -333,6 +342,7 @@ function PasswordForm(props: { confirm_password: '', }, }) + const router = useRouter() const handler = form.handleSubmit(async (value) => { try { const resp = await updatePassword({ @@ -344,8 +354,10 @@ function PasswordForm(props: { throw new Error(resp.message) } - toast.success(`保存成功`) + toast.success(`保存成功,请重新登录`) setOpen(false) + // 立即跳转到登录页 + router.replace('/login') } catch (e) { console.error(e) diff --git a/src/app/admin/whitelist/page.tsx b/src/app/admin/whitelist/page.tsx index c4e243d..e55ff85 100644 --- a/src/app/admin/whitelist/page.tsx +++ b/src/app/admin/whitelist/page.tsx @@ -230,14 +230,14 @@ export default function WhitelistPage(props: WhitelistPageProps) { 添加白名单 - + */} {/* 数据表 */} diff --git a/src/components/composites/extract/index.tsx b/src/components/composites/extract/index.tsx index e9b7035..40ed092 100644 --- a/src/components/composites/extract/index.tsx +++ b/src/components/composites/extract/index.tsx @@ -276,7 +276,11 @@ const FormFields = memo(() => { {...field} id={id} type="number" - onChange={e => field.onChange(Number(e.target.value))} + min={1} + onChange={(e) => { + const value = Math.max(1, Number(e.target.value)) + field.onChange(value) + }} className="h-10" placeholder="输入提取数量" />