diff --git a/client/index.html b/client/index.html index 30cb628c..332b057a 100644 --- a/client/index.html +++ b/client/index.html @@ -4,8 +4,6 @@ - 海音达工作台 diff --git a/client/package-lock.json b/client/package-lock.json index 25c53d02..9e9132f7 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,6 +8,7 @@ "name": "client", "version": "0.1.0", "dependencies": { + "@material-symbols/font-400": "^0.42.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-slot": "^1.2.4", @@ -2145,6 +2146,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@material-symbols/font-400": { + "version": "0.42.3", + "resolved": "https://registry.npmjs.org/@material-symbols/font-400/-/font-400-0.42.3.tgz", + "integrity": "sha512-HWpHvDapOOkUAwO5607ixoxQHAI38qrXO+A7/y7l5cYzD6lcEsoGPh9SEh0We3//dd7H/j/NCiRua4kMgMELyw==", + "license": "Apache-2.0" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/client/package.json b/client/package.json index 887df397..ce603d03 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "dependencies": { + "@material-symbols/font-400": "^0.42.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-slot": "^1.2.4", diff --git a/client/src/api/errorMessages.ts b/client/src/api/errorMessages.ts index 612f4648..77917f53 100644 --- a/client/src/api/errorMessages.ts +++ b/client/src/api/errorMessages.ts @@ -1,3 +1,13 @@ +function readStringList(value: unknown): string[] { + if (!Array.isArray(value)) { + return []; + } + + return value + .map((item) => (typeof item === "string" ? item.trim() : "")) + .filter((item) => item.length > 0); +} + export function mapApiErrorMessage( code: string, details?: Record, @@ -75,6 +85,15 @@ export function mapApiErrorMessage( } if (code === "VALID_001") { + const passwordErrors = readStringList(details?.password); + if (passwordErrors.length) { + return `新密码不符合要求:${passwordErrors.join(";")}。`; + } + + if (details?.["x-device-fingerprint"] === "missing") { + return "当前设备校验信息缺失,请刷新页面后重试。"; + } + if (details?.payment === "already_paid_conflict") { return "该报销单已支付且付款信息冲突,请刷新后确认。"; } diff --git a/client/src/index.css b/client/src/index.css index d990eb14..281474a4 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -3,7 +3,7 @@ - Extracted from Stitch MCP AI-generated Design Preview (B2B SaaS Theme) */ -@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"); +@import "@material-symbols/font-400/outlined.css"; @import "./styles/mobile-touch.css"; @tailwind base; diff --git a/client/src/pages/ChangePasswordPage.tsx b/client/src/pages/ChangePasswordPage.tsx index fb6b688c..e6876b93 100644 --- a/client/src/pages/ChangePasswordPage.tsx +++ b/client/src/pages/ChangePasswordPage.tsx @@ -7,6 +7,12 @@ import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; +const PASSWORD_REQUIREMENTS = [ + "至少 12 位", + "包含大写字母、小写字母和数字", + "不要使用公司名、常见顺序或过于简单的组合", +]; + export function ChangePasswordPage() { const navigate = useNavigate(); const { changePassword } = useAuth(); @@ -29,10 +35,10 @@ export function ChangePasswordPage() { setSubmitting(true); try { await changePassword(oldPassword, newPassword); - navigate("/dashboard", { replace: true }); + navigate("/portal", { replace: true }); } catch (err) { if (isApiError(err)) { - setError(`${err.code}: ${mapApiErrorMessage(err.code, err.details, err.message)}`); + setError(mapApiErrorMessage(err.code, err.details, err.message)); } else { setError("修改密码失败,请稍后重试"); } @@ -76,6 +82,16 @@ export function ChangePasswordPage() { type="password" value={newPassword} /> +

密码要求:{PASSWORD_REQUIREMENTS.join(";")}。

+ + +
+

建议的新密码规则

+
diff --git a/client/src/pages/LoginPage.tsx b/client/src/pages/LoginPage.tsx index 370b51b9..c7b2bfb6 100644 --- a/client/src/pages/LoginPage.tsx +++ b/client/src/pages/LoginPage.tsx @@ -27,8 +27,8 @@ export function LoginPage() { const [searchParams] = useSearchParams(); const { login } = useAuth(); - const [identifier, setIdentifier] = useState("employee@hyinda.com"); - const [password, setPassword] = useState("EmployeePassword1234"); + const [identifier, setIdentifier] = useState(""); + const [password, setPassword] = useState(""); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); @@ -60,7 +60,7 @@ export function LoginPage() { } } catch (err) { if (isApiError(err)) { - setError(`${err.code}: ${mapApiErrorMessage(err.code, err.details, err.message)}`); + setError(mapApiErrorMessage(err.code, err.details, err.message)); } else if (err instanceof TypeError) { setError("无法连接后端服务,请确认 server 已启动(http://127.0.0.1:3000)。"); } else { diff --git a/client/tailwind.config.js b/client/tailwind.config.js index 50bb5869..775bea8c 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -18,8 +18,30 @@ export default { border: "#e4e4e7" // zinc-200 }, fontFamily: { - sans: ["Inter", "sans-serif"], - display: ["Inter", "sans-serif"] + sans: [ + "system-ui", + "-apple-system", + "BlinkMacSystemFont", + "Segoe UI", + "PingFang SC", + "Hiragino Sans GB", + "Microsoft YaHei", + "Noto Sans CJK SC", + "Noto Sans SC", + "sans-serif" + ], + display: [ + "system-ui", + "-apple-system", + "BlinkMacSystemFont", + "Segoe UI", + "PingFang SC", + "Hiragino Sans GB", + "Microsoft YaHei", + "Noto Sans CJK SC", + "Noto Sans SC", + "sans-serif" + ] }, borderRadius: { DEFAULT: "0.25rem", diff --git a/deploy/trialrun/nginx-hyb-oa.conf b/deploy/trialrun/nginx-hyb-oa.conf index 4033a5b2..47526391 100644 --- a/deploy/trialrun/nginx-hyb-oa.conf +++ b/deploy/trialrun/nginx-hyb-oa.conf @@ -32,6 +32,21 @@ server { client_max_body_size 20m; + location ^~ /assets/ { + proxy_hide_header Cache-Control; + add_header Cache-Control "public, max-age=31536000, immutable" always; + proxy_pass http://hyb_oa_upstream; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port 443; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } + location / { limit_req zone=oa_general burst=20 nodelay; proxy_pass http://hyb_oa_upstream;