|
|
@@ -0,0 +1,52 @@
|
|
|
+import { CheckCircleOutlined, LockOutlined } from '@ant-design/icons';
|
|
|
+import { memo } from 'react';
|
|
|
+
|
|
|
+interface RuleCheck {
|
|
|
+ label: string;
|
|
|
+ test: (v: string) => boolean;
|
|
|
+}
|
|
|
+
|
|
|
+const ruleChecks: RuleCheck[] = [
|
|
|
+ { label: '长度 8 - 72 位', test: (v) => v.length >= 8 && v.length <= 72 },
|
|
|
+ { label: '包含大写字母(A-Z)', test: (v) => /[A-Z]/.test(v) },
|
|
|
+ { label: '包含小写字母(a-z)', test: (v) => /[a-z]/.test(v) },
|
|
|
+ { label: '包含数字(0-9)', test: (v) => /\d/.test(v) },
|
|
|
+ {
|
|
|
+ label: '包含特殊字符(如 !@#$%^&*)',
|
|
|
+ test: (v) => /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(v),
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+interface SecurityTipsProps {
|
|
|
+ newPassword?: string;
|
|
|
+}
|
|
|
+
|
|
|
+export const SecurityTips = memo(function SecurityTips({ newPassword = '' }: SecurityTipsProps) {
|
|
|
+ return (
|
|
|
+ <div className="h-full flex flex-col justify-center px-6 py-8 bg-(--ant-color-fill-quaternary) rounded-lg">
|
|
|
+ <div className="flex items-center gap-2 mb-4">
|
|
|
+ <LockOutlined className="text-xl text-(--ant-color-primary)" />
|
|
|
+ <span className="text-base font-medium">密码安全要求</span>
|
|
|
+ </div>
|
|
|
+ <ul className="flex flex-col gap-3 m-0 p-0 list-none">
|
|
|
+ {ruleChecks.map(({ label, test }) => {
|
|
|
+ const passed = newPassword.length > 0 && test(newPassword);
|
|
|
+ return (
|
|
|
+ <li
|
|
|
+ key={label}
|
|
|
+ className="flex items-center gap-2 text-sm text-(--ant-color-text-secondary)"
|
|
|
+ >
|
|
|
+ <CheckCircleOutlined
|
|
|
+ className="shrink-0 transition-colors"
|
|
|
+ style={{
|
|
|
+ color: passed ? 'var(--ant-color-primary)' : 'var(--ant-color-text-quaternary)',
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ {label}
|
|
|
+ </li>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+});
|