fix(ui): [UI7-2] 统一全局 Design Tokens,移除冗余字体,修复异常色值引用

这个提交包含在:
2026-03-06 09:48:37 +08:00
父节点 62fd2a5398
当前提交 73b51ac068
共修改 39 个文件,包含 253 行新增和 250 行删除
+15 -12
查看文件
@@ -1,14 +1,17 @@
<!doctype html> <!doctype html>
<html lang="zh-CN"> <html lang="zh-CN">
<head>
<meta charset="UTF-8" /> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta charset="UTF-8" />
<link href="https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;700;900&display=swap" rel="stylesheet" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@300,0&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@300,0&display=swap"
<title>海音达工作台</title> rel="stylesheet" />
</head> <title>海音达工作台</title>
<body> </head>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script> <body>
</body> <div id="root"></div>
</html> <script type="module" src="/src/main.tsx"></script>
</body>
</html>
@@ -33,8 +33,8 @@ export class ErrorBoundary extends Component<Props, State> {
return ( return (
<Card className="mx-auto mt-12 max-w-lg space-y-4 p-6 text-center"> <Card className="mx-auto mt-12 max-w-lg space-y-4 p-6 text-center">
<h2 className="text-lg font-semibold text-slate-900">页面渲染出错</h2> <h2 className="text-lg font-semibold text-zinc-900">页面渲染出错</h2>
<p className="text-sm text-slate-600"> <p className="text-sm text-zinc-600">
{this.state.error?.message ?? "发生了未知错误"} {this.state.error?.message ?? "发生了未知错误"}
</p> </p>
<div className="flex justify-center gap-3"> <div className="flex justify-center gap-3">
@@ -46,7 +46,7 @@ export class ErrorBoundary extends Component<Props, State> {
重试 重试
</button> </button>
<button <button
className="inline-flex h-11 items-center rounded-lg border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 hover:bg-slate-50" className="inline-flex h-11 items-center rounded-lg border border-zinc-200 bg-white px-4 text-sm font-medium text-zinc-700 hover:bg-zinc-50"
onClick={() => { onClick={() => {
window.location.href = "/dashboard"; window.location.href = "/dashboard";
}} }}
+1 -1
查看文件
@@ -92,7 +92,7 @@ export function Topbar() {
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="left-0 top-0 h-full w-72 max-w-[80vw] translate-x-0 translate-y-0 rounded-none rounded-r-xl p-0"> <DialogContent className="left-0 top-0 h-full w-72 max-w-[80vw] tranzinc-x-0 tranzinc-y-0 rounded-none rounded-r-xl p-0">
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
<div className="flex items-center justify-between border-b border-zinc-200 px-4 py-4"> <div className="flex items-center justify-between border-b border-zinc-200 px-4 py-4">
<div> <div>
+5 -5
查看文件
@@ -4,15 +4,15 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../lib/utils"; import { cn } from "../../lib/utils";
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-200 disabled:pointer-events-none disabled:opacity-50", "inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-300 disabled:pointer-events-none disabled:opacity-50",
{ {
variants: { variants: {
variant: { variant: {
default: "bg-primary text-white hover:bg-primary-600", default: "bg-primary text-white hover:bg-primary-light",
secondary: "border border-slate-200 bg-white text-slate-700 hover:bg-slate-50", secondary: "border border-zinc-200 bg-white text-zinc-700 hover:bg-zinc-50",
outline: "border border-slate-200 bg-transparent text-slate-900 hover:bg-slate-50", outline: "border border-zinc-200 bg-transparent text-zinc-900 hover:bg-zinc-50",
destructive: "bg-red-600 text-white hover:bg-red-700", destructive: "bg-red-600 text-white hover:bg-red-700",
ghost: "bg-transparent text-slate-700 hover:bg-slate-100", ghost: "bg-transparent text-zinc-700 hover:bg-zinc-100",
link: "bg-transparent text-primary underline-offset-4 hover:underline", link: "bg-transparent text-primary underline-offset-4 hover:underline",
}, },
size: { size: {
+3 -3
查看文件
@@ -6,7 +6,7 @@ export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDi
return ( return (
<div <div
ref={ref} ref={ref}
className={cn("rounded-xl border border-slate-200 bg-white shadow-sm", className)} className={cn("rounded-xl border border-zinc-200 bg-white shadow-sm", className)}
{...props} {...props}
/> />
); );
@@ -38,7 +38,7 @@ export const CardTitle = React.forwardRef<
React.HTMLAttributes<HTMLHeadingElement> React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
return ( return (
<h3 ref={ref} className={cn("text-base font-semibold text-slate-900", className)} {...props} /> <h3 ref={ref} className={cn("text-base font-semibold text-zinc-900", className)} {...props} />
); );
}); });
@@ -49,7 +49,7 @@ export const CardDescription = React.forwardRef<
React.HTMLAttributes<HTMLParagraphElement> React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
return ( return (
<p ref={ref} className={cn("text-sm text-slate-600", className)} {...props} /> <p ref={ref} className={cn("text-sm text-zinc-600", className)} {...props} />
); );
}); });
+3 -3
查看文件
@@ -30,7 +30,7 @@ export const DialogContent = React.forwardRef<
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed left-1/2 top-1/2 z-50 w-[92vw] max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-xl border border-slate-200 bg-white p-6 shadow-lg focus-visible:outline-none", "fixed left-1/2 top-1/2 z-50 w-[92vw] max-w-lg -tranzinc-x-1/2 -tranzinc-y-1/2 rounded-xl border border-zinc-200 bg-white p-6 shadow-lg focus-visible:outline-none",
className, className,
)} )}
{...props} {...props}
@@ -54,7 +54,7 @@ export const DialogTitle = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DialogPrimitive.Title <DialogPrimitive.Title
ref={ref} ref={ref}
className={cn("text-lg font-semibold text-slate-900", className)} className={cn("text-lg font-semibold text-zinc-900", className)}
{...props} {...props}
/> />
)); ));
@@ -67,7 +67,7 @@ export const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DialogPrimitive.Description <DialogPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-slate-600", className)} className={cn("text-sm text-zinc-600", className)}
{...props} {...props}
/> />
)); ));
+3 -3
查看文件
@@ -13,7 +13,7 @@ export const DropdownMenuContent = React.forwardRef<
ref={ref} ref={ref}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 min-w-[12rem] overflow-hidden rounded-xl border border-slate-200 bg-white p-1 text-slate-900 shadow-md", "z-50 min-w-[12rem] overflow-hidden rounded-xl border border-zinc-200 bg-white p-1 text-zinc-900 shadow-md",
className, className,
)} )}
{...props} {...props}
@@ -30,7 +30,7 @@ export const DropdownMenuItem = React.forwardRef<
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-lg px-3 py-2 text-sm outline-none focus:bg-slate-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "flex cursor-default select-none items-center rounded-lg px-3 py-2 text-sm outline-none focus:bg-zinc-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className, className,
)} )}
{...props} {...props}
@@ -45,7 +45,7 @@ export const DropdownMenuSeparator = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator <DropdownMenuPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-slate-200", className)} className={cn("-mx-1 my-1 h-px bg-zinc-200", className)}
{...props} {...props}
/> />
)); ));
+1 -1
查看文件
@@ -9,7 +9,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input <input
type={type} type={type}
className={cn( className={cn(
"flex h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-200 disabled:cursor-not-allowed disabled:opacity-50", "flex h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 placeholder:text-zinc-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-200 disabled:cursor-not-allowed disabled:opacity-50",
className, className,
)} )}
ref={ref} ref={ref}
+1 -1
查看文件
@@ -6,7 +6,7 @@ export type LabelProps = React.LabelHTMLAttributes<HTMLLabelElement>;
export function Label({ className, ...props }: LabelProps) { export function Label({ className, ...props }: LabelProps) {
return ( return (
<label <label
className={cn("text-sm font-medium leading-none text-slate-800", className)} className={cn("text-sm font-medium leading-none text-zinc-800", className)}
{...props} {...props}
/> />
); );
+1 -1
查看文件
@@ -1,5 +1,5 @@
import { cn } from "../../lib/utils"; import { cn } from "../../lib/utils";
export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) { export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn("animate-pulse rounded-md bg-slate-200", className)} {...props} />; return <div className={cn("animate-pulse rounded-md bg-zinc-200", className)} {...props} />;
} }
+1 -1
查看文件
@@ -7,7 +7,7 @@ export function Toaster() {
duration={3000} duration={3000}
toastOptions={{ toastOptions={{
className: className:
"border border-slate-200 bg-white text-slate-900 shadow-lg rounded-lg text-sm", "border border-zinc-200 bg-white text-zinc-900 shadow-lg rounded-lg text-sm",
}} }}
/> />
); );
+1 -1
查看文件
@@ -14,7 +14,7 @@ export function Spinner({
}) { }) {
return ( return (
<svg <svg
className={cn("animate-spin text-slate-400", sizeMap[size], className)} className={cn("animate-spin text-zinc-400", sizeMap[size], className)}
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
+3 -3
查看文件
@@ -15,7 +15,7 @@ export const TableHeader = React.forwardRef<
HTMLTableSectionElement, HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement> React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("bg-slate-50 text-slate-600", className)} {...props} /> <thead ref={ref} className={cn("bg-zinc-50 text-zinc-600", className)} {...props} />
)); ));
TableHeader.displayName = "TableHeader"; TableHeader.displayName = "TableHeader";
@@ -31,7 +31,7 @@ TableBody.displayName = "TableBody";
export const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>( export const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => ( ({ className, ...props }, ref) => (
<tr ref={ref} className={cn("border-t border-slate-100", className)} {...props} /> <tr ref={ref} className={cn("border-t border-zinc-100", className)} {...props} />
), ),
); );
@@ -41,7 +41,7 @@ export const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttr
({ className, ...props }, ref) => ( ({ className, ...props }, ref) => (
<th <th
ref={ref} ref={ref}
className={cn("h-10 px-3 text-left align-middle font-medium text-slate-600", className)} className={cn("h-10 px-3 text-left align-middle font-medium text-zinc-600", className)}
{...props} {...props}
/> />
), ),
+1 -1
查看文件
@@ -11,7 +11,7 @@ export const TabsList = React.forwardRef<
<TabsPrimitive.List <TabsPrimitive.List
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex h-11 items-center justify-center rounded-lg border border-slate-200 bg-white p-1 text-slate-700", "inline-flex h-11 items-center justify-center rounded-lg border border-zinc-200 bg-white p-1 text-zinc-700",
className, className,
)} )}
{...props} {...props}
+1 -1
查看文件
@@ -8,7 +8,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
return ( return (
<textarea <textarea
className={cn( className={cn(
"flex min-h-[120px] w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-200 disabled:cursor-not-allowed disabled:opacity-50", "flex min-h-[120px] w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 placeholder:text-zinc-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-200 disabled:cursor-not-allowed disabled:opacity-50",
className, className,
)} )}
ref={ref} ref={ref}
+1 -1
查看文件
@@ -2,7 +2,7 @@ import { Outlet } from "react-router-dom";
export function AuthLayout() { export function AuthLayout() {
return ( return (
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-primary-50 via-background-light to-slate-100 p-6"> <div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-primary-50 via-background-light to-zinc-100 p-6">
<div className="w-full max-w-md"> <div className="w-full max-w-md">
<Outlet /> <Outlet />
</div> </div>
+15 -15
查看文件
@@ -157,7 +157,7 @@ export function ApprovalRulesPage() {
) : null} ) : null}
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<h3 className="text-base font-semibold text-slate-900">新建审批规则</h3> <h3 className="text-base font-semibold text-zinc-900">新建审批规则</h3>
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
<div className="space-y-1"> <div className="space-y-1">
@@ -209,7 +209,7 @@ export function ApprovalRulesPage() {
<select <select
id="level1Role" id="level1Role"
data-testid="approval-rules-create-level1" data-testid="approval-rules-create-level1"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={level1Role} value={level1Role}
onChange={(event) => setLevel1Role(event.target.value as ApprovalRoleCode)} onChange={(event) => setLevel1Role(event.target.value as ApprovalRoleCode)}
> >
@@ -226,7 +226,7 @@ export function ApprovalRulesPage() {
<select <select
id="level2Role" id="level2Role"
data-testid="approval-rules-create-level2" data-testid="approval-rules-create-level2"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={level2Role} value={level2Role}
onChange={(event) => setLevel2Role(event.target.value as ApprovalRoleCode)} onChange={(event) => setLevel2Role(event.target.value as ApprovalRoleCode)}
> >
@@ -244,7 +244,7 @@ export function ApprovalRulesPage() {
<select <select
id="level3Role" id="level3Role"
data-testid="approval-rules-create-level3" data-testid="approval-rules-create-level3"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={level3Role} value={level3Role}
onChange={(event) => setLevel3Role(event.target.value as ApprovalRoleCode)} onChange={(event) => setLevel3Role(event.target.value as ApprovalRoleCode)}
> >
@@ -282,7 +282,7 @@ export function ApprovalRulesPage() {
</Card> </Card>
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<h3 className="text-base font-semibold text-slate-900">规则列表</h3> <h3 className="text-base font-semibold text-zinc-900">规则列表</h3>
{listQuery.isLoading ? <TableSkeleton columns={6} rows={5} /> : null} {listQuery.isLoading ? <TableSkeleton columns={6} rows={5} /> : null}
@@ -299,7 +299,7 @@ export function ApprovalRulesPage() {
) : null} ) : null}
{!listQuery.isLoading && !listQuery.isError ? ( {!listQuery.isLoading && !listQuery.isError ? (
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="approval-rules-table"> <Table data-testid="approval-rules-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -316,18 +316,18 @@ export function ApprovalRulesPage() {
rules.map((rule) => ( rules.map((rule) => (
<TableRow key={rule.id} data-testid="approval-rule-row"> <TableRow key={rule.id} data-testid="approval-rule-row">
<TableCell> <TableCell>
<div className="font-medium text-slate-900">{rule.name}</div> <div className="font-medium text-zinc-900">{rule.name}</div>
<div className="font-mono text-xs text-slate-500">{rule.id}</div> <div className="font-mono text-xs text-zinc-500">{rule.id}</div>
</TableCell> </TableCell>
<TableCell className="text-sm text-slate-700"> <TableCell className="text-sm text-zinc-700">
{formatAmount(rule.minAmount)} ~ {rule.maxAmount ? formatAmount(rule.maxAmount) : "∞"} {formatAmount(rule.minAmount)} ~ {rule.maxAmount ? formatAmount(rule.maxAmount) : "∞"}
</TableCell> </TableCell>
<TableCell className="text-sm text-slate-700"> <TableCell className="text-sm text-zinc-700">
{[rule.level1Role, rule.level2Role, rule.level3Role].filter(Boolean).join(" -> ") || "-"} {[rule.level1Role, rule.level2Role, rule.level3Role].filter(Boolean).join(" -> ") || "-"}
</TableCell> </TableCell>
<TableCell>{rule.priority}</TableCell> <TableCell>{rule.priority}</TableCell>
<TableCell className="text-sm"> <TableCell className="text-sm">
{rule.enabled ? <span className="text-emerald-600">启用</span> : <span className="text-slate-500">停用</span>} {rule.enabled ? <span className="text-emerald-600">启用</span> : <span className="text-zinc-500">停用</span>}
</TableCell> </TableCell>
<TableCell> <TableCell>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
@@ -359,7 +359,7 @@ export function ApprovalRulesPage() {
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={6}> <TableCell className="py-3 text-zinc-500" colSpan={6}>
暂无审批规则 暂无审批规则
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -430,7 +430,7 @@ export function ApprovalRulesPage() {
<select <select
id="editRuleLevel1" id="editRuleLevel1"
data-testid="approval-rule-edit-level1" data-testid="approval-rule-edit-level1"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={level1Role} value={level1Role}
onChange={(event) => setLevel1Role(event.target.value as ApprovalRoleCode)} onChange={(event) => setLevel1Role(event.target.value as ApprovalRoleCode)}
> >
@@ -447,7 +447,7 @@ export function ApprovalRulesPage() {
<select <select
id="editRuleLevel2" id="editRuleLevel2"
data-testid="approval-rule-edit-level2" data-testid="approval-rule-edit-level2"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={level2Role} value={level2Role}
onChange={(event) => setLevel2Role(event.target.value as ApprovalRoleCode)} onChange={(event) => setLevel2Role(event.target.value as ApprovalRoleCode)}
> >
@@ -465,7 +465,7 @@ export function ApprovalRulesPage() {
<select <select
id="editRuleLevel3" id="editRuleLevel3"
data-testid="approval-rule-edit-level3" data-testid="approval-rule-edit-level3"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={level3Role} value={level3Role}
onChange={(event) => setLevel3Role(event.target.value as ApprovalRoleCode)} onChange={(event) => setLevel3Role(event.target.value as ApprovalRoleCode)}
> >
+11 -11
查看文件
@@ -137,10 +137,10 @@ export function AuditLogsPage() {
<section className="card-std space-y-4"> <section className="card-std space-y-4">
<div className="grid gap-3 md:grid-cols-3"> <div className="grid gap-3 md:grid-cols-3">
<label className="text-sm text-slate-700"> <label className="text-sm text-zinc-700">
Action Action
<input <input
className="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2" className="mt-1 w-full rounded-lg border border-zinc-300 px-3 py-2"
onChange={(event) => { onChange={(event) => {
setPage(1); setPage(1);
setActionFilter(event.target.value.trim()); setActionFilter(event.target.value.trim());
@@ -150,10 +150,10 @@ export function AuditLogsPage() {
/> />
</label> </label>
<label className="text-sm text-slate-700"> <label className="text-sm text-zinc-700">
Target Type Target Type
<input <input
className="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2" className="mt-1 w-full rounded-lg border border-zinc-300 px-3 py-2"
onChange={(event) => { onChange={(event) => {
setPage(1); setPage(1);
setTargetTypeFilter(event.target.value.trim()); setTargetTypeFilter(event.target.value.trim());
@@ -163,9 +163,9 @@ export function AuditLogsPage() {
/> />
</label> </label>
<div className="text-sm text-slate-700"> <div className="text-sm text-zinc-700">
结果统计 结果统计
<div className="mt-1 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-slate-900"> <div className="mt-1 rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-zinc-900">
共 {data?.total ?? 0} 条 共 {data?.total ?? 0} 条
</div> </div>
</div> </div>
@@ -177,9 +177,9 @@ export function AuditLogsPage() {
</p> </p>
) : null} ) : null}
<div className="overflow-x-auto rounded-lg border border-slate-200"> <div className="overflow-x-auto rounded-lg border border-zinc-200">
<table className="min-w-full text-left text-sm"> <table className="min-w-full text-left text-sm">
<thead className="bg-slate-50 text-slate-600"> <thead className="bg-zinc-50 text-zinc-600">
<tr> <tr>
<th className="px-3 py-2">时间</th> <th className="px-3 py-2">时间</th>
<th className="px-3 py-2">Action</th> <th className="px-3 py-2">Action</th>
@@ -199,12 +199,12 @@ export function AuditLogsPage() {
{!loading && data?.items?.length ? ( {!loading && data?.items?.length ? (
data.items.map((item) => ( data.items.map((item) => (
<tr className="border-t border-slate-100" key={item.id}> <tr className="border-t border-zinc-100" key={item.id}>
<td className="px-3 py-2">{new Date(item.createdAt).toLocaleString()}</td> <td className="px-3 py-2">{new Date(item.createdAt).toLocaleString()}</td>
<td className="px-3 py-2">{item.action}</td> <td className="px-3 py-2">{item.action}</td>
<td className="px-3 py-2"> <td className="px-3 py-2">
<div>{item.actorName || item.actorId}</div> <div>{item.actorName || item.actorId}</div>
<div className="text-xs text-slate-500">{item.actorEmail}</div> <div className="text-xs text-zinc-500">{item.actorEmail}</div>
</td> </td>
<td className="px-3 py-2"> <td className="px-3 py-2">
{item.targetType} {item.targetType}
@@ -217,7 +217,7 @@ export function AuditLogsPage() {
{!loading && !(data?.items?.length ?? 0) ? ( {!loading && !(data?.items?.length ?? 0) ? (
<tr> <tr>
<td className="px-3 py-3 text-slate-500" colSpan={5}> <td className="px-3 py-3 text-zinc-500" colSpan={5}>
暂无数据 暂无数据
</td> </td>
</tr> </tr>
+18 -18
查看文件
@@ -118,7 +118,7 @@ export function BudgetsPage() {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<PageHeader title="预算管理" subtitle="你当前没有预算权限,请联系管理员开通。" /> <PageHeader title="预算管理" subtitle="你当前没有预算权限,请联系管理员开通。" />
<Card className="p-6 text-sm text-slate-600">所需权限:budget.view 或 budget.manage</Card> <Card className="p-6 text-sm text-zinc-600">所需权限:budget.view 或 budget.manage</Card>
</div> </div>
); );
} }
@@ -139,10 +139,10 @@ export function BudgetsPage() {
) : null} ) : null}
<Card className="space-y-3 p-6"> <Card className="space-y-3 p-6">
<h3 className="text-base font-semibold text-slate-900">筛选条件</h3> <h3 className="text-base font-semibold text-zinc-900">筛选条件</h3>
<div className="grid gap-3 md:grid-cols-3"> <div className="grid gap-3 md:grid-cols-3">
<select <select
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={status} value={status}
onChange={(event) => { onChange={(event) => {
setStatus(event.target.value as "active" | "archived" | ""); setStatus(event.target.value as "active" | "archived" | "");
@@ -155,7 +155,7 @@ export function BudgetsPage() {
</select> </select>
<select <select
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={departmentId} value={departmentId}
onChange={(event) => { onChange={(event) => {
setDepartmentId(event.target.value); setDepartmentId(event.target.value);
@@ -182,22 +182,22 @@ export function BudgetsPage() {
{canManageBudget ? ( {canManageBudget ? (
<Card className="space-y-3 p-6"> <Card className="space-y-3 p-6">
<h3 className="text-base font-semibold text-slate-900">新建预算池</h3> <h3 className="text-base font-semibold text-zinc-900">新建预算池</h3>
<div className="grid gap-3 md:grid-cols-3"> <div className="grid gap-3 md:grid-cols-3">
<input <input
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
placeholder="编码(例如 BGT-2026-RD)" placeholder="编码(例如 BGT-2026-RD)"
value={code} value={code}
onChange={(event) => setCode(event.target.value)} onChange={(event) => setCode(event.target.value)}
/> />
<input <input
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
placeholder="名称" placeholder="名称"
value={name} value={name}
onChange={(event) => setName(event.target.value)} onChange={(event) => setName(event.target.value)}
/> />
<select <select
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={formDepartmentId} value={formDepartmentId}
onChange={(event) => setFormDepartmentId(event.target.value)} onChange={(event) => setFormDepartmentId(event.target.value)}
> >
@@ -209,25 +209,25 @@ export function BudgetsPage() {
))} ))}
</select> </select>
<input <input
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
type="date" type="date"
value={periodStart} value={periodStart}
onChange={(event) => setPeriodStart(event.target.value)} onChange={(event) => setPeriodStart(event.target.value)}
/> />
<input <input
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
type="date" type="date"
value={periodEnd} value={periodEnd}
onChange={(event) => setPeriodEnd(event.target.value)} onChange={(event) => setPeriodEnd(event.target.value)}
/> />
<input <input
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
placeholder="预算总额(例如 100000.00)" placeholder="预算总额(例如 100000.00)"
value={allocatedAmount} value={allocatedAmount}
onChange={(event) => setAllocatedAmount(event.target.value)} onChange={(event) => setAllocatedAmount(event.target.value)}
/> />
<input <input
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
type="number" type="number"
min={1} min={1}
max={100} max={100}
@@ -254,13 +254,13 @@ export function BudgetsPage() {
) : null} ) : null}
<Card className="space-y-3 p-6"> <Card className="space-y-3 p-6">
<h3 className="text-base font-semibold text-slate-900">预算池列表</h3> <h3 className="text-base font-semibold text-zinc-900">预算池列表</h3>
{budgetsQuery.isLoading ? <TableSkeleton columns={7} rows={4} /> : null} {budgetsQuery.isLoading ? <TableSkeleton columns={7} rows={4} /> : null}
{!budgetsQuery.isLoading ? ( {!budgetsQuery.isLoading ? (
<> <>
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
@@ -277,7 +277,7 @@ export function BudgetsPage() {
{(budgetsQuery.data?.items ?? []).length ? ( {(budgetsQuery.data?.items ?? []).length ? (
(budgetsQuery.data?.items ?? []).map((item) => ( (budgetsQuery.data?.items ?? []).map((item) => (
<TableRow key={item.id}> <TableRow key={item.id}>
<TableCell className="font-medium text-slate-900">{item.code}</TableCell> <TableCell className="font-medium text-zinc-900">{item.code}</TableCell>
<TableCell>{item.name}</TableCell> <TableCell>{item.name}</TableCell>
<TableCell><StatusBadge status={item.status} /></TableCell> <TableCell><StatusBadge status={item.status} /></TableCell>
<TableCell> <TableCell>
@@ -285,7 +285,7 @@ export function BudgetsPage() {
</TableCell> </TableCell>
<TableCell> <TableCell>
{item.usedAmount} / {item.allocatedAmount} {item.usedAmount} / {item.allocatedAmount}
<div className="text-xs text-slate-500">剩余 {item.remainingAmount}</div> <div className="text-xs text-zinc-500">剩余 {item.remainingAmount}</div>
</TableCell> </TableCell>
<TableCell>{item.warningThresholdPercent}%</TableCell> <TableCell>{item.warningThresholdPercent}%</TableCell>
<TableCell> <TableCell>
@@ -300,14 +300,14 @@ export function BudgetsPage() {
归档 归档
</Button> </Button>
) : ( ) : (
<span className="text-xs text-slate-400">-</span> <span className="text-xs text-zinc-400">-</span>
)} )}
</TableCell> </TableCell>
</TableRow> </TableRow>
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={7}> <TableCell className="py-3 text-zinc-500" colSpan={7}>
暂无预算池 暂无预算池
</TableCell> </TableCell>
</TableRow> </TableRow>
+1 -1
查看文件
@@ -46,7 +46,7 @@ export function ChangePasswordPage() {
<Card className="space-y-6 border-primary-100 p-6"> <Card className="space-y-6 border-primary-100 p-6">
<div className="mb-6 space-y-2"> <div className="mb-6 space-y-2">
<p className="text-xs font-semibold uppercase tracking-wide text-primary">hyb-oa</p> <p className="text-xs font-semibold uppercase tracking-wide text-primary">hyb-oa</p>
<h1 className="text-2xl font-bold text-slate-900">修改密码</h1> <h1 className="text-2xl font-bold text-zinc-900">修改密码</h1>
<p className="text-sm text-secondary"> <p className="text-sm text-secondary">
为了账号安全,请先修改初始密码后再进入系统。 为了账号安全,请先修改初始密码后再进入系统。
</p> </p>
+29 -29
查看文件
@@ -117,8 +117,8 @@ export function DashboardPage() {
<div className="space-y-4"> <div className="space-y-4">
<PageHeader title="仪表盘" subtitle="你当前没有看板权限,请联系管理员开通。" /> <PageHeader title="仪表盘" subtitle="你当前没有看板权限,请联系管理员开通。" />
<Card className="space-y-2 p-6 text-sm"> <Card className="space-y-2 p-6 text-sm">
<p className="text-slate-700">当前用户:{me?.name ?? "-"}</p> <p className="text-zinc-700">当前用户:{me?.name ?? "-"}</p>
<p className="text-slate-500">所需权限:report.dashboard.view</p> <p className="text-zinc-500">所需权限:report.dashboard.view</p>
</Card> </Card>
</div> </div>
); );
@@ -166,36 +166,36 @@ export function DashboardPage() {
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<div className="grid gap-3 md:grid-cols-4"> <div className="grid gap-3 md:grid-cols-4">
<div className="space-y-1"> <div className="space-y-1">
<label className="text-xs text-slate-600" htmlFor="dashboard-date-from"> <label className="text-xs text-zinc-600" htmlFor="dashboard-date-from">
开始日期 开始日期
</label> </label>
<input <input
id="dashboard-date-from" id="dashboard-date-from"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
type="date" type="date"
value={dateFrom} value={dateFrom}
onChange={(event) => setDateFrom(event.target.value)} onChange={(event) => setDateFrom(event.target.value)}
/> />
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-xs text-slate-600" htmlFor="dashboard-date-to"> <label className="text-xs text-zinc-600" htmlFor="dashboard-date-to">
结束日期 结束日期
</label> </label>
<input <input
id="dashboard-date-to" id="dashboard-date-to"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
type="date" type="date"
value={dateTo} value={dateTo}
onChange={(event) => setDateTo(event.target.value)} onChange={(event) => setDateTo(event.target.value)}
/> />
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-xs text-slate-600" htmlFor="dashboard-department-id"> <label className="text-xs text-zinc-600" htmlFor="dashboard-department-id">
部门 部门
</label> </label>
<select <select
id="dashboard-department-id" id="dashboard-department-id"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={departmentId} value={departmentId}
onChange={(event) => setDepartmentId(event.target.value)} onChange={(event) => setDepartmentId(event.target.value)}
> >
@@ -230,26 +230,26 @@ export function DashboardPage() {
<div className="grid gap-4 md:grid-cols-4"> <div className="grid gap-4 md:grid-cols-4">
<Card className="p-4"> <Card className="p-4">
<p className="text-xs text-slate-500">报销单总数</p> <p className="text-xs text-zinc-500">报销单总数</p>
<p className="mt-1 text-2xl font-semibold text-slate-900"> <p className="mt-1 text-2xl font-semibold text-zinc-900">
{loading ? "..." : overviewQuery.data?.totalCount ?? 0} {loading ? "..." : overviewQuery.data?.totalCount ?? 0}
</p> </p>
</Card> </Card>
<Card className="p-4"> <Card className="p-4">
<p className="text-xs text-slate-500">报销总金额</p> <p className="text-xs text-zinc-500">报销总金额</p>
<p className="mt-1 text-2xl font-semibold text-slate-900"> <p className="mt-1 text-2xl font-semibold text-zinc-900">
{loading ? "..." : toCurrency(overviewQuery.data?.totalAmount)} {loading ? "..." : toCurrency(overviewQuery.data?.totalAmount)}
</p> </p>
</Card> </Card>
<Card className="p-4"> <Card className="p-4">
<p className="text-xs text-slate-500">已付款金额</p> <p className="text-xs text-zinc-500">已付款金额</p>
<p className="mt-1 text-2xl font-semibold text-slate-900"> <p className="mt-1 text-2xl font-semibold text-zinc-900">
{loading ? "..." : toCurrency(overviewQuery.data?.paidAmount)} {loading ? "..." : toCurrency(overviewQuery.data?.paidAmount)}
</p> </p>
</Card> </Card>
<Card className="p-4"> <Card className="p-4">
<p className="text-xs text-slate-500">审批中数量</p> <p className="text-xs text-zinc-500">审批中数量</p>
<p className="mt-1 text-2xl font-semibold text-slate-900"> <p className="mt-1 text-2xl font-semibold text-zinc-900">
{loading ? "..." : overviewQuery.data?.reviewingCount ?? 0} {loading ? "..." : overviewQuery.data?.reviewingCount ?? 0}
</p> </p>
</Card> </Card>
@@ -257,7 +257,7 @@ export function DashboardPage() {
<div className="grid gap-4 lg:grid-cols-2"> <div className="grid gap-4 lg:grid-cols-2">
<Card className="space-y-3 p-6"> <Card className="space-y-3 p-6">
<h3 className="text-base font-semibold text-slate-900">费用趋势</h3> <h3 className="text-base font-semibold text-zinc-900">费用趋势</h3>
{trendPoints.length ? ( {trendPoints.length ? (
<div className="space-y-2"> <div className="space-y-2">
{trendPoints.map((item) => { {trendPoints.map((item) => {
@@ -265,13 +265,13 @@ export function DashboardPage() {
const width = `${Math.max(5, Math.round((amount / maxTrendAmount) * 100))}%`; const width = `${Math.max(5, Math.round((amount / maxTrendAmount) * 100))}%`;
return ( return (
<div key={item.date} className="space-y-1"> <div key={item.date} className="space-y-1">
<div className="flex items-center justify-between text-xs text-slate-600"> <div className="flex items-center justify-between text-xs text-zinc-600">
<span>{item.date}</span> <span>{item.date}</span>
<span> <span>
{toCurrency(item.amount)} / {item.count} 笔 {toCurrency(item.amount)} / {item.count} 笔
</span> </span>
</div> </div>
<div className="h-2 rounded-full bg-slate-100"> <div className="h-2 rounded-full bg-zinc-100">
<div className="h-2 rounded-full bg-primary" style={{ width }} /> <div className="h-2 rounded-full bg-primary" style={{ width }} />
</div> </div>
</div> </div>
@@ -279,12 +279,12 @@ export function DashboardPage() {
})} })}
</div> </div>
) : ( ) : (
<p className="text-sm text-slate-500">暂无趋势数据</p> <p className="text-sm text-zinc-500">暂无趋势数据</p>
)} )}
</Card> </Card>
<Card className="space-y-3 p-6"> <Card className="space-y-3 p-6">
<h3 className="text-base font-semibold text-slate-900">费用分类 TopN</h3> <h3 className="text-base font-semibold text-zinc-900">费用分类 TopN</h3>
{topCategories.length ? ( {topCategories.length ? (
<div className="space-y-2"> <div className="space-y-2">
{topCategories.map((item) => { {topCategories.map((item) => {
@@ -292,7 +292,7 @@ export function DashboardPage() {
const width = `${Math.max(5, Math.round((amount / maxCategoryAmount) * 100))}%`; const width = `${Math.max(5, Math.round((amount / maxCategoryAmount) * 100))}%`;
return ( return (
<div key={item.code} className="space-y-1"> <div key={item.code} className="space-y-1">
<div className="flex items-center justify-between text-xs text-slate-600"> <div className="flex items-center justify-between text-xs text-zinc-600">
<span> <span>
{item.name} ({item.code}) {item.name} ({item.code})
</span> </span>
@@ -300,7 +300,7 @@ export function DashboardPage() {
{toCurrency(item.amount)} / {item.count} 笔 {toCurrency(item.amount)} / {item.count} 笔
</span> </span>
</div> </div>
<div className="h-2 rounded-full bg-slate-100"> <div className="h-2 rounded-full bg-zinc-100">
<div className="h-2 rounded-full bg-emerald-500" style={{ width }} /> <div className="h-2 rounded-full bg-emerald-500" style={{ width }} />
</div> </div>
</div> </div>
@@ -308,22 +308,22 @@ export function DashboardPage() {
})} })}
</div> </div>
) : ( ) : (
<p className="text-sm text-slate-500">暂无分类统计</p> <p className="text-sm text-zinc-500">暂无分类统计</p>
)} )}
</Card> </Card>
</div> </div>
<Card className="space-y-2 p-6 text-sm"> <Card className="space-y-2 p-6 text-sm">
<h3 className="text-base font-semibold text-slate-900">审批效率</h3> <h3 className="text-base font-semibold text-zinc-900">审批效率</h3>
<p className="text-slate-700"> <p className="text-zinc-700">
已统计通过单据: 已统计通过单据:
<span className="ml-1 font-medium text-slate-900"> <span className="ml-1 font-medium text-zinc-900">
{approvalEfficiencyQuery.data?.approvedCount ?? 0} {approvalEfficiencyQuery.data?.approvedCount ?? 0}
</span> </span>
</p> </p>
<p className="text-slate-700"> <p className="text-zinc-700">
平均审批耗时(小时): 平均审批耗时(小时):
<span className="ml-1 font-medium text-slate-900"> <span className="ml-1 font-medium text-zinc-900">
{approvalEfficiencyQuery.data?.averageHours?.toFixed(2) ?? "0.00"} {approvalEfficiencyQuery.data?.averageHours?.toFixed(2) ?? "0.00"}
</span> </span>
</p> </p>
+15 -15
查看文件
@@ -81,13 +81,13 @@ export function DelegationsPage() {
) : null} ) : null}
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<h3 className="text-base font-semibold text-slate-900">创建委派</h3> <h3 className="text-base font-semibold text-zinc-900">创建委派</h3>
<div className="grid gap-3 md:grid-cols-2"> <div className="grid gap-3 md:grid-cols-2">
<label className="space-y-1 text-sm"> <label className="space-y-1 text-sm">
<span className="font-medium text-slate-700">委派给(审批人候选)</span> <span className="font-medium text-zinc-700">委派给(审批人候选)</span>
<select <select
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="delegations-create-delegate" data-testid="delegations-create-delegate"
onChange={(event) => setDelegateEmployeeId(event.target.value)} onChange={(event) => setDelegateEmployeeId(event.target.value)}
value={delegateEmployeeId} value={delegateEmployeeId}
@@ -99,15 +99,15 @@ export function DelegationsPage() {
</option> </option>
))} ))}
</select> </select>
<p className="text-xs text-slate-500"> <p className="text-xs text-zinc-500">
说明:当前 MVP 未接入员工搜索,这里先复用“转审候选”列表作为可选目标。 说明:当前 MVP 未接入员工搜索,这里先复用“转审候选”列表作为可选目标。
</p> </p>
</label> </label>
<label className="space-y-1 text-sm"> <label className="space-y-1 text-sm">
<span className="font-medium text-slate-700">范围</span> <span className="font-medium text-zinc-700">范围</span>
<select <select
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="delegations-create-scope" data-testid="delegations-create-scope"
onChange={(event) => setScope(event.target.value as "all" | "reimbursement_submit")} onChange={(event) => setScope(event.target.value as "all" | "reimbursement_submit")}
value={scope} value={scope}
@@ -118,7 +118,7 @@ export function DelegationsPage() {
</label> </label>
<label className="space-y-1 text-sm md:col-span-2"> <label className="space-y-1 text-sm md:col-span-2">
<span className="font-medium text-slate-700">有效期结束(可选)</span> <span className="font-medium text-zinc-700">有效期结束(可选)</span>
<Input <Input
data-testid="delegations-create-valid-to" data-testid="delegations-create-valid-to"
onChange={(event) => setValidTo(event.target.value)} onChange={(event) => setValidTo(event.target.value)}
@@ -142,9 +142,9 @@ export function DelegationsPage() {
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<div className="flex flex-wrap items-center justify-between gap-2"> <div className="flex flex-wrap items-center justify-between gap-2">
<h3 className="text-base font-semibold text-slate-900">我的委派</h3> <h3 className="text-base font-semibold text-zinc-900">我的委派</h3>
<select <select
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="delegations-status-filter" data-testid="delegations-status-filter"
onChange={(event) => onChange={(event) =>
setStatus(event.target.value as "active" | "scheduled" | "expired" | "revoked" | "all") setStatus(event.target.value as "active" | "scheduled" | "expired" | "revoked" | "all")
@@ -175,8 +175,8 @@ export function DelegationsPage() {
{listQuery.data ? ( {listQuery.data ? (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<p className="mb-2 text-sm font-medium text-slate-800">我发起的委派</p> <p className="mb-2 text-sm font-medium text-zinc-800">我发起的委派</p>
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="delegations-initiated-table"> <Table data-testid="delegations-initiated-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -211,7 +211,7 @@ export function DelegationsPage() {
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={5}> <TableCell className="py-3 text-zinc-500" colSpan={5}>
暂无数据 暂无数据
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -222,8 +222,8 @@ export function DelegationsPage() {
</div> </div>
<div> <div>
<p className="mb-2 text-sm font-medium text-slate-800">我接收的委派</p> <p className="mb-2 text-sm font-medium text-zinc-800">我接收的委派</p>
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="delegations-received-table"> <Table data-testid="delegations-received-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -245,7 +245,7 @@ export function DelegationsPage() {
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={4}> <TableCell className="py-3 text-zinc-500" colSpan={4}>
暂无数据 暂无数据
</TableCell> </TableCell>
</TableRow> </TableRow>
+9 -9
查看文件
@@ -104,7 +104,7 @@ export function DepartmentsPage() {
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<h3 className="text-base font-semibold text-slate-900">新建部门</h3> <h3 className="text-base font-semibold text-zinc-900">新建部门</h3>
<Button <Button
data-testid="departments-toggle-archived" data-testid="departments-toggle-archived"
variant="secondary" variant="secondary"
@@ -136,7 +136,7 @@ export function DepartmentsPage() {
<select <select
id="deptParent" id="deptParent"
data-testid="departments-create-parent" data-testid="departments-create-parent"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={parentId} value={parentId}
onChange={(event) => setParentId(event.target.value)} onChange={(event) => setParentId(event.target.value)}
> >
@@ -165,7 +165,7 @@ export function DepartmentsPage() {
</Card> </Card>
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<h3 className="text-base font-semibold text-slate-900">部门列表</h3> <h3 className="text-base font-semibold text-zinc-900">部门列表</h3>
{listQuery.isLoading ? <TableSkeleton columns={4} rows={4} /> : null} {listQuery.isLoading ? <TableSkeleton columns={4} rows={4} /> : null}
@@ -182,7 +182,7 @@ export function DepartmentsPage() {
) : null} ) : null}
{!listQuery.isLoading && !listQuery.isError ? ( {!listQuery.isLoading && !listQuery.isError ? (
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="departments-table"> <Table data-testid="departments-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -199,11 +199,11 @@ export function DepartmentsPage() {
return ( return (
<TableRow data-testid="department-row" key={item.id}> <TableRow data-testid="department-row" key={item.id}>
<TableCell> <TableCell>
<div className="font-medium text-slate-900">{item.name}</div> <div className="font-medium text-zinc-900">{item.name}</div>
<div className="text-xs text-slate-500 font-mono">{item.id}</div> <div className="text-xs text-zinc-500 font-mono">{item.id}</div>
</TableCell> </TableCell>
<TableCell>{parentName}</TableCell> <TableCell>{parentName}</TableCell>
<TableCell className="text-xs text-slate-600"> <TableCell className="text-xs text-zinc-600">
{item.archivedAt ? item.archivedAt.slice(0, 19).replace("T", " ") : "-"} {item.archivedAt ? item.archivedAt.slice(0, 19).replace("T", " ") : "-"}
</TableCell> </TableCell>
<TableCell> <TableCell>
@@ -219,7 +219,7 @@ export function DepartmentsPage() {
归档 归档
</Button> </Button>
) : ( ) : (
<span className="text-xs text-slate-500">已归档</span> <span className="text-xs text-zinc-500">已归档</span>
)} )}
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -227,7 +227,7 @@ export function DepartmentsPage() {
}) })
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={4}> <TableCell className="py-3 text-zinc-500" colSpan={4}>
暂无部门 暂无部门
</TableCell> </TableCell>
</TableRow> </TableRow>
+16 -16
查看文件
@@ -140,7 +140,7 @@ export function FeatureFlagsPage() {
) : null} ) : null}
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<h3 className="text-base font-semibold text-slate-900">开关列表</h3> <h3 className="text-base font-semibold text-zinc-900">开关列表</h3>
{flagsQuery.isLoading ? <TableSkeleton columns={6} rows={2} /> : null} {flagsQuery.isLoading ? <TableSkeleton columns={6} rows={2} /> : null}
@@ -157,7 +157,7 @@ export function FeatureFlagsPage() {
) : null} ) : null}
{!flagsQuery.isLoading && !flagsQuery.isError ? ( {!flagsQuery.isLoading && !flagsQuery.isError ? (
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="feature-flags-table"> <Table data-testid="feature-flags-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -177,8 +177,8 @@ export function FeatureFlagsPage() {
return ( return (
<TableRow key={item.flagKey} data-testid={`feature-flag-row-${item.flagKey}`}> <TableRow key={item.flagKey} data-testid={`feature-flag-row-${item.flagKey}`}>
<TableCell> <TableCell>
<div className="font-medium text-slate-900">{toDisplayName(item.flagKey)}</div> <div className="font-medium text-zinc-900">{toDisplayName(item.flagKey)}</div>
<div className="font-mono text-xs text-slate-500">{item.flagKey}</div> <div className="font-mono text-xs text-zinc-500">{item.flagKey}</div>
</TableCell> </TableCell>
<TableCell className="text-sm"> <TableCell className="text-sm">
@@ -194,14 +194,14 @@ export function FeatureFlagsPage() {
{item.effectiveEnabled ? "ON" : "OFF"} {item.effectiveEnabled ? "ON" : "OFF"}
</span> </span>
</div> </div>
<div className="text-xs text-slate-500">reason: {item.effectiveReason}</div> <div className="text-xs text-zinc-500">reason: {item.effectiveReason}</div>
</TableCell> </TableCell>
<TableCell className="space-y-2 text-sm"> <TableCell className="space-y-2 text-sm">
<div> <div>
<Label className="mb-1 block text-xs text-slate-500">override</Label> <Label className="mb-1 block text-xs text-zinc-500">override</Label>
<select <select
className="h-10 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={draft.overrideMode} value={draft.overrideMode}
onChange={(event) => onChange={(event) =>
patchDraft(item.flagKey, (prev) => ({ patchDraft(item.flagKey, (prev) => ({
@@ -219,9 +219,9 @@ export function FeatureFlagsPage() {
</div> </div>
<div> <div>
<Label className="mb-1 block text-xs text-slate-500">rollout</Label> <Label className="mb-1 block text-xs text-zinc-500">rollout</Label>
<select <select
className="h-10 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={draft.rolloutType} value={draft.rolloutType}
onChange={(event) => onChange={(event) =>
patchDraft(item.flagKey, (prev) => ({ patchDraft(item.flagKey, (prev) => ({
@@ -247,7 +247,7 @@ export function FeatureFlagsPage() {
{draft.rolloutType === "department_allowlist" ? ( {draft.rolloutType === "department_allowlist" ? (
<div className="space-y-2"> <div className="space-y-2">
<select <select
className="h-10 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value="" value=""
onChange={(event) => { onChange={(event) => {
const selected = event.target.value; const selected = event.target.value;
@@ -266,12 +266,12 @@ export function FeatureFlagsPage() {
))} ))}
</select> </select>
<div className="max-h-24 overflow-auto rounded border border-slate-200 p-2 text-xs"> <div className="max-h-24 overflow-auto rounded border border-zinc-200 p-2 text-xs">
{draft.departmentIds.length ? ( {draft.departmentIds.length ? (
draft.departmentIds.map((departmentId) => ( draft.departmentIds.map((departmentId) => (
<div <div
key={departmentId} key={departmentId}
className="mb-1 flex items-center justify-between rounded bg-slate-50 px-2 py-1" className="mb-1 flex items-center justify-between rounded bg-zinc-50 px-2 py-1"
> >
<span>{departmentsById.get(departmentId) ?? departmentId}</span> <span>{departmentsById.get(departmentId) ?? departmentId}</span>
<button <button
@@ -289,18 +289,18 @@ export function FeatureFlagsPage() {
</div> </div>
)) ))
) : ( ) : (
<p className="text-slate-500">暂无白名单部门</p> <p className="text-zinc-500">暂无白名单部门</p>
)} )}
</div> </div>
</div> </div>
) : ( ) : (
<span className="text-slate-500">全量</span> <span className="text-zinc-500">全量</span>
)} )}
</TableCell> </TableCell>
<TableCell> <TableCell>
<textarea <textarea
className="min-h-24 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="min-h-24 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
placeholder="填写本次变更说明" placeholder="填写本次变更说明"
value={draft.note} value={draft.note}
onChange={(event) => onChange={(event) =>
@@ -310,7 +310,7 @@ export function FeatureFlagsPage() {
})) }))
} }
/> />
<p className="mt-1 text-xs text-slate-500">version: {draft.version}</p> <p className="mt-1 text-xs text-zinc-500">version: {draft.version}</p>
</TableCell> </TableCell>
<TableCell> <TableCell>
+1 -1
查看文件
@@ -5,7 +5,7 @@ export function ForbiddenPage() {
<div className="flex min-h-screen items-center justify-center bg-background-light p-6"> <div className="flex min-h-screen items-center justify-center bg-background-light p-6">
<div className="card-std max-w-lg text-center"> <div className="card-std max-w-lg text-center">
<p className="text-sm font-semibold uppercase tracking-wide text-primary">403</p> <p className="text-sm font-semibold uppercase tracking-wide text-primary">403</p>
<h1 className="mt-2 text-2xl font-bold text-slate-900">无权限访问</h1> <h1 className="mt-2 text-2xl font-bold text-zinc-900">无权限访问</h1>
<p className="mt-2 text-sm text-secondary"> <p className="mt-2 text-sm text-secondary">
当前账号没有访问该页面的权限,请联系管理员或返回可访问页面。 当前账号没有访问该页面的权限,请联系管理员或返回可访问页面。
</p> </p>
+2 -2
查看文件
@@ -62,9 +62,9 @@ export function LoginPage() {
<Card className="space-y-6 border-primary-100 p-6"> <Card className="space-y-6 border-primary-100 p-6">
<div className="mb-6"> <div className="mb-6">
<p className="text-xs font-semibold uppercase tracking-wide text-primary">hyb-oa</p> <p className="text-xs font-semibold uppercase tracking-wide text-primary">hyb-oa</p>
<h1 className="mt-2 text-2xl font-bold text-slate-900">登录工作台</h1> <h1 className="mt-2 text-2xl font-bold text-zinc-900">登录工作台</h1>
<p className="mt-2 text-sm text-secondary">使用邮箱/手机号与密码登录。</p> <p className="mt-2 text-sm text-secondary">使用邮箱/手机号与密码登录。</p>
<p className="mt-1 text-xs text-slate-500">忘记密码:请联系管理员重置。</p> <p className="mt-1 text-xs text-zinc-500">忘记密码:请联系管理员重置。</p>
</div> </div>
<form className="space-y-4" onSubmit={handleSubmit}> <form className="space-y-4" onSubmit={handleSubmit}>
+13 -13
查看文件
@@ -108,12 +108,12 @@ export function MobileApprovalsPage() {
) : null} ) : null}
<Card className="space-y-3 p-4" data-testid="mobile-approvals-list"> <Card className="space-y-3 p-4" data-testid="mobile-approvals-list">
<h3 className="text-base font-semibold text-slate-900">待办任务</h3> <h3 className="text-base font-semibold text-zinc-900">待办任务</h3>
{tasksQuery.isLoading ? <div className="flex items-center gap-2 text-sm text-slate-500"><Spinner size="sm" />加载中...</div> : null} {tasksQuery.isLoading ? <div className="flex items-center gap-2 text-sm text-zinc-500"><Spinner size="sm" />加载中...</div> : null}
{!tasksQuery.isLoading && pendingTasks.length === 0 ? ( {!tasksQuery.isLoading && pendingTasks.length === 0 ? (
<p className="text-sm text-slate-500">暂无待办任务。</p> <p className="text-sm text-zinc-500">暂无待办任务。</p>
) : null} ) : null}
<div className="space-y-2"> <div className="space-y-2">
@@ -123,32 +123,32 @@ export function MobileApprovalsPage() {
className={`w-full rounded-lg border px-3 py-2 text-left text-sm ${ className={`w-full rounded-lg border px-3 py-2 text-left text-sm ${
selectedTaskId === task.id selectedTaskId === task.id
? "border-primary bg-primary-50 text-primary" ? "border-primary bg-primary-50 text-primary"
: "border-slate-200 bg-white text-slate-800" : "border-zinc-200 bg-white text-zinc-800"
}`} }`}
data-testid={`mobile-approvals-task-${task.id}`} data-testid={`mobile-approvals-task-${task.id}`}
onClick={() => setSelectedTaskId(task.id)} onClick={() => setSelectedTaskId(task.id)}
type="button" type="button"
> >
<div className="font-medium">{task.stepName}</div> <div className="font-medium">{task.stepName}</div>
<div className="text-xs text-slate-500">任务ID: {task.id}</div> <div className="text-xs text-zinc-500">任务ID: {task.id}</div>
<div className="text-xs text-slate-500">状态: {task.status}</div> <div className="text-xs text-zinc-500">状态: {task.status}</div>
</button> </button>
))} ))}
</div> </div>
</Card> </Card>
<Card className="space-y-3 p-4" data-testid="mobile-approvals-detail"> <Card className="space-y-3 p-4" data-testid="mobile-approvals-detail">
<h3 className="text-base font-semibold text-slate-900">任务详情与动作</h3> <h3 className="text-base font-semibold text-zinc-900">任务详情与动作</h3>
{!selectedTaskId ? <p className="text-sm text-slate-500">请先选择任务。</p> : null} {!selectedTaskId ? <p className="text-sm text-zinc-500">请先选择任务。</p> : null}
{selectedTaskId && detailQuery.isLoading ? ( {selectedTaskId && detailQuery.isLoading ? (
<p className="text-sm text-slate-500">加载任务详情中...</p> <p className="text-sm text-zinc-500">加载任务详情中...</p>
) : null} ) : null}
{selectedTask && ( {selectedTask && (
<> <>
<div className="rounded-lg border border-slate-200 p-3 text-sm"> <div className="rounded-lg border border-zinc-200 p-3 text-sm">
<div>任务状态:{selectedTask.task.status}</div> <div>任务状态:{selectedTask.task.status}</div>
<div>步骤:{selectedTask.task.stepName}</div> <div>步骤:{selectedTask.task.stepName}</div>
<div>报销单号:{selectedTask.reimbursement?.formNumber ?? "-"}</div> <div>报销单号:{selectedTask.reimbursement?.formNumber ?? "-"}</div>
@@ -193,10 +193,10 @@ export function MobileApprovalsPage() {
</Button> </Button>
</div> </div>
<div className="space-y-2 rounded-lg border border-slate-200 p-3 text-sm"> <div className="space-y-2 rounded-lg border border-zinc-200 p-3 text-sm">
<p className="font-medium text-slate-800">转审</p> <p className="font-medium text-zinc-800">转审</p>
<select <select
className="h-10 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="mobile-approvals-transfer-to" data-testid="mobile-approvals-transfer-to"
onChange={(event) => setTransferToApproverId(event.target.value)} onChange={(event) => setTransferToApproverId(event.target.value)}
value={transferToApproverId} value={transferToApproverId}
+9 -9
查看文件
@@ -142,7 +142,7 @@ export function MobileCapturePage() {
<Card className="space-y-3 p-4"> <Card className="space-y-3 p-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-base font-semibold text-slate-900">拍照/选择文件</h3> <h3 className="text-base font-semibold text-zinc-900">拍照/选择文件</h3>
<span className={isOnline ? "text-emerald-600" : "text-orange-600"}> <span className={isOnline ? "text-emerald-600" : "text-orange-600"}>
{isOnline ? "在线" : "离线"} {isOnline ? "在线" : "离线"}
</span> </span>
@@ -160,7 +160,7 @@ export function MobileCapturePage() {
type="file" type="file"
/> />
<div className="text-sm text-slate-600"> <div className="text-sm text-zinc-600">
当前文件:{selectedFile ? `${selectedFile.name} (${Math.ceil(selectedFile.size / 1024)} KB)` : "未选择"} 当前文件:{selectedFile ? `${selectedFile.name} (${Math.ceil(selectedFile.size / 1024)} KB)` : "未选择"}
</div> </div>
@@ -216,20 +216,20 @@ export function MobileCapturePage() {
<Card className="space-y-3 p-4" data-testid="mobile-capture-queue"> <Card className="space-y-3 p-4" data-testid="mobile-capture-queue">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-base font-semibold text-slate-900">离线上传队列</h3> <h3 className="text-base font-semibold text-zinc-900">离线上传队列</h3>
<span className="text-sm text-slate-500">{queuedCountLabel}</span> <span className="text-sm text-zinc-500">{queuedCountLabel}</span>
</div> </div>
{queueQuery.isLoading ? <div className="flex items-center gap-2 text-sm text-slate-500"><Spinner size="sm" />加载中...</div> : null} {queueQuery.isLoading ? <div className="flex items-center gap-2 text-sm text-zinc-500"><Spinner size="sm" />加载中...</div> : null}
{!queueQuery.isLoading && queuedItems.length === 0 ? ( {!queueQuery.isLoading && queuedItems.length === 0 ? (
<p className="text-sm text-slate-500">暂无待上传文件。</p> <p className="text-sm text-zinc-500">暂无待上传文件。</p>
) : null} ) : null}
{queuedItems.map((item) => ( {queuedItems.map((item) => (
<div key={item.id} className="rounded-lg border border-slate-200 px-3 py-2 text-sm"> <div key={item.id} className="rounded-lg border border-zinc-200 px-3 py-2 text-sm">
<div className="font-medium text-slate-800">{item.fileName}</div> <div className="font-medium text-zinc-800">{item.fileName}</div>
<div className="text-slate-500">创建时间:{formatTime(item.createdAt)}</div> <div className="text-zinc-500">创建时间:{formatTime(item.createdAt)}</div>
<div className="mt-2"> <div className="mt-2">
<Button <Button
data-testid={`mobile-capture-remove-${item.id}`} data-testid={`mobile-capture-remove-${item.id}`}
+1 -1
查看文件
@@ -5,7 +5,7 @@ export function NotFoundPage() {
<div className="flex min-h-screen items-center justify-center bg-background-light p-6"> <div className="flex min-h-screen items-center justify-center bg-background-light p-6">
<div className="card-std max-w-lg text-center"> <div className="card-std max-w-lg text-center">
<p className="text-sm font-semibold uppercase tracking-wide text-primary">404</p> <p className="text-sm font-semibold uppercase tracking-wide text-primary">404</p>
<h1 className="mt-2 text-2xl font-bold text-slate-900">页面不存在</h1> <h1 className="mt-2 text-2xl font-bold text-zinc-900">页面不存在</h1>
<p className="mt-2 text-sm text-secondary">请检查地址,或返回工作台首页继续操作。</p> <p className="mt-2 text-sm text-secondary">请检查地址,或返回工作台首页继续操作。</p>
<div className="mt-5 flex justify-center"> <div className="mt-5 flex justify-center">
<Link className="btn-primary" to="/dashboard"> <Link className="btn-primary" to="/dashboard">
+7 -7
查看文件
@@ -102,7 +102,7 @@ export function NotificationsPage() {
</TabsList> </TabsList>
</Tabs> </Tabs>
<div className="text-sm text-slate-600"> <div className="text-sm text-zinc-600">
未读 {listQuery.data?.unreadCount ?? 0} 条 未读 {listQuery.data?.unreadCount ?? 0} 条
</div> </div>
</div> </div>
@@ -122,7 +122,7 @@ export function NotificationsPage() {
) : null} ) : null}
{!listQuery.isLoading && !listQuery.isError ? ( {!listQuery.isLoading && !listQuery.isError ? (
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="notifications-table"> <Table data-testid="notifications-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -139,15 +139,15 @@ export function NotificationsPage() {
return ( return (
<TableRow data-testid="notification-row" key={item.id}> <TableRow data-testid="notification-row" key={item.id}>
<TableCell> <TableCell>
<div className="font-medium text-slate-900">{item.title}</div> <div className="font-medium text-zinc-900">{item.title}</div>
{item.body ? ( {item.body ? (
<div className="mt-1 text-xs text-slate-500">{item.body}</div> <div className="mt-1 text-xs text-zinc-500">{item.body}</div>
) : null} ) : null}
</TableCell> </TableCell>
<TableCell className="text-xs text-slate-600"> <TableCell className="text-xs text-zinc-600">
{item.createdAt.slice(0, 19).replace("T", " ")} {item.createdAt.slice(0, 19).replace("T", " ")}
</TableCell> </TableCell>
<TableCell className="text-xs text-slate-600"> <TableCell className="text-xs text-zinc-600">
{item.readAt ? "已读" : "未读"} {item.readAt ? "已读" : "未读"}
</TableCell> </TableCell>
<TableCell> <TableCell>
@@ -177,7 +177,7 @@ export function NotificationsPage() {
}) })
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={4}> <TableCell className="py-3 text-zinc-500" colSpan={4}>
暂无通知 暂无通知
</TableCell> </TableCell>
</TableRow> </TableRow>
+7 -7
查看文件
@@ -10,30 +10,30 @@ export function ProfilePage() {
<section className="card-std"> <section className="card-std">
<h3 className="card-title">个人信息</h3> <h3 className="card-title">个人信息</h3>
<dl className="grid gap-3 text-sm text-slate-700 md:grid-cols-2"> <dl className="grid gap-3 text-sm text-zinc-700 md:grid-cols-2">
<div> <div>
<dt className="text-secondary">姓名</dt> <dt className="text-secondary">姓名</dt>
<dd className="font-medium text-slate-900">{me?.name ?? "-"}</dd> <dd className="font-medium text-zinc-900">{me?.name ?? "-"}</dd>
</div> </div>
<div> <div>
<dt className="text-secondary">员工号</dt> <dt className="text-secondary">员工号</dt>
<dd className="font-medium text-slate-900">{me?.employeeNo ?? "-"}</dd> <dd className="font-medium text-zinc-900">{me?.employeeNo ?? "-"}</dd>
</div> </div>
<div> <div>
<dt className="text-secondary">邮箱</dt> <dt className="text-secondary">邮箱</dt>
<dd className="font-medium text-slate-900">{me?.email ?? "-"}</dd> <dd className="font-medium text-zinc-900">{me?.email ?? "-"}</dd>
</div> </div>
<div> <div>
<dt className="text-secondary">手机号</dt> <dt className="text-secondary">手机号</dt>
<dd className="font-medium text-slate-900">{me?.phone ?? "-"}</dd> <dd className="font-medium text-zinc-900">{me?.phone ?? "-"}</dd>
</div> </div>
<div> <div>
<dt className="text-secondary">部门ID</dt> <dt className="text-secondary">部门ID</dt>
<dd className="font-medium text-slate-900">{me?.departmentId ?? "-"}</dd> <dd className="font-medium text-zinc-900">{me?.departmentId ?? "-"}</dd>
</div> </div>
<div> <div>
<dt className="text-secondary">直属上级ID</dt> <dt className="text-secondary">直属上级ID</dt>
<dd className="font-medium text-slate-900">{me?.directManagerId ?? "-"}</dd> <dd className="font-medium text-zinc-900">{me?.directManagerId ?? "-"}</dd>
</div> </div>
</dl> </dl>
</section> </section>
+14 -14
查看文件
@@ -292,7 +292,7 @@ export function ReimbursementDetailPage() {
{budgetHint ? ( {budgetHint ? (
<Card className="space-y-2 p-6 text-sm" data-testid="reimbursement-budget-hint"> <Card className="space-y-2 p-6 text-sm" data-testid="reimbursement-budget-hint">
<h3 className="text-base font-semibold text-slate-900">预算提示</h3> <h3 className="text-base font-semibold text-zinc-900">预算提示</h3>
<div>预算池:{budgetHint.budgetCode} / {budgetHint.budgetName}</div> <div>预算池:{budgetHint.budgetCode} / {budgetHint.budgetName}</div>
<div> <div>
本次提交后预计占用:{budgetHint.nextUsedAmount} / {budgetHint.allocatedAmount} 本次提交后预计占用:{budgetHint.nextUsedAmount} / {budgetHint.allocatedAmount}
@@ -308,7 +308,7 @@ export function ReimbursementDetailPage() {
当前金额已达到预算预警阈值,提交后会记录预算预警审计。 当前金额已达到预算预警阈值,提交后会记录预算预警审计。
</p> </p>
) : ( ) : (
<p className="text-slate-500">当前预算充足,可继续提交。</p> <p className="text-zinc-500">当前预算充足,可继续提交。</p>
)} )}
</Card> </Card>
) : null} ) : null}
@@ -316,8 +316,8 @@ export function ReimbursementDetailPage() {
<Card className="space-y-4 p-6" data-testid="reimbursement-items"> <Card className="space-y-4 p-6" data-testid="reimbursement-items">
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between"> <div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div className="space-y-1"> <div className="space-y-1">
<h3 className="text-base font-semibold text-slate-900">报销明细</h3> <h3 className="text-base font-semibold text-zinc-900">报销明细</h3>
<p className="text-xs text-slate-500">明细变更会自动重算总金额。</p> <p className="text-xs text-zinc-500">明细变更会自动重算总金额。</p>
</div> </div>
{canEditItems ? ( {canEditItems ? (
@@ -331,7 +331,7 @@ export function ReimbursementDetailPage() {
新增明细 新增明细
</Button> </Button>
) : ( ) : (
<p className="text-xs text-slate-500">当前状态不可编辑明细</p> <p className="text-xs text-zinc-500">当前状态不可编辑明细</p>
)} )}
</div> </div>
@@ -344,7 +344,7 @@ export function ReimbursementDetailPage() {
</p> </p>
) : null} ) : null}
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="reimbursement-items-table"> <Table data-testid="reimbursement-items-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -360,10 +360,10 @@ export function ReimbursementDetailPage() {
detail.items.map((item) => ( detail.items.map((item) => (
<TableRow data-testid="reimbursement-item-row" key={item.id}> <TableRow data-testid="reimbursement-item-row" key={item.id}>
<TableCell> <TableCell>
<div className="font-medium text-slate-900"> <div className="font-medium text-zinc-900">
{item.expenseType?.name ?? item.expenseTypeId} {item.expenseType?.name ?? item.expenseTypeId}
</div> </div>
<div className="text-xs text-slate-500">{item.expenseType?.code ?? "-"}</div> <div className="text-xs text-zinc-500">{item.expenseType?.code ?? "-"}</div>
</TableCell> </TableCell>
<TableCell>{item.expenseDate ?? "-"}</TableCell> <TableCell>{item.expenseDate ?? "-"}</TableCell>
<TableCell>{item.amount}</TableCell> <TableCell>{item.amount}</TableCell>
@@ -397,7 +397,7 @@ export function ReimbursementDetailPage() {
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={5}> <TableCell className="py-3 text-zinc-500" colSpan={5}>
暂无明细 暂无明细
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -431,20 +431,20 @@ export function ReimbursementDetailPage() {
/> />
<Card className="space-y-2 p-6" data-testid="reimbursement-invoices"> <Card className="space-y-2 p-6" data-testid="reimbursement-invoices">
<h3 className="text-base font-semibold text-slate-900">关联票据</h3> <h3 className="text-base font-semibold text-zinc-900">关联票据</h3>
{(detail.invoices ?? []).length ? ( {(detail.invoices ?? []).length ? (
<div className="space-y-2 text-sm"> <div className="space-y-2 text-sm">
{detail.invoices.map((item) => ( {detail.invoices.map((item) => (
<div className="rounded-lg border border-slate-200 px-3 py-2" key={item.id}> <div className="rounded-lg border border-zinc-200 px-3 py-2" key={item.id}>
<div className="font-medium text-slate-900">{item.id}</div> <div className="font-medium text-zinc-900">{item.id}</div>
<div className="text-slate-600"> <div className="text-zinc-600">
状态 {item.status} / 发票 {item.invoiceCode || "-"}-{item.invoiceNumber || "-"} 状态 {item.status} / 发票 {item.invoiceCode || "-"}-{item.invoiceNumber || "-"}
</div> </div>
</div> </div>
))} ))}
</div> </div>
) : ( ) : (
<p className="text-sm text-slate-500">暂无关联票据</p> <p className="text-sm text-zinc-500">暂无关联票据</p>
)} )}
</Card> </Card>
+1 -1
查看文件
@@ -97,7 +97,7 @@ export function ReimbursementsPage() {
<div className="flex flex-col gap-2 sm:flex-row sm:items-center"> <div className="flex flex-col gap-2 sm:flex-row sm:items-center">
{showProxyOwnerSelector ? ( {showProxyOwnerSelector ? (
<select <select
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="reimbursements-owner-select" data-testid="reimbursements-owner-select"
onChange={(event) => setProxyOwnerEmployeeId(event.target.value)} onChange={(event) => setProxyOwnerEmployeeId(event.target.value)}
value={proxyOwnerEmployeeId} value={proxyOwnerEmployeeId}
+6 -6
查看文件
@@ -189,7 +189,7 @@ export function UserManagementPage() {
placeholder="搜索工号/姓名/手机号/邮箱" placeholder="搜索工号/姓名/手机号/邮箱"
/> />
<select <select
className="h-11 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="users-status-filter" data-testid="users-status-filter"
value={status} value={status}
onChange={(e) => { onChange={(e) => {
@@ -216,7 +216,7 @@ export function UserManagementPage() {
</p> </p>
) : null} ) : null}
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="users-table"> <Table data-testid="users-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -232,14 +232,14 @@ export function UserManagementPage() {
listQuery.data?.items.map((item) => ( listQuery.data?.items.map((item) => (
<TableRow data-testid="users-row" key={item.id}> <TableRow data-testid="users-row" key={item.id}>
<TableCell> <TableCell>
<div className="font-medium text-slate-900"> <div className="font-medium text-zinc-900">
{item.employeeNo} / {item.name} {item.employeeNo} / {item.name}
</div> </div>
<div className="text-xs text-slate-500 font-mono">{item.id}</div> <div className="text-xs text-zinc-500 font-mono">{item.id}</div>
</TableCell> </TableCell>
<TableCell className="text-sm"> <TableCell className="text-sm">
<div>{item.email}</div> <div>{item.email}</div>
<div className="text-slate-500">{item.phone}</div> <div className="text-zinc-500">{item.phone}</div>
</TableCell> </TableCell>
<TableCell><StatusBadge status={item.status} /></TableCell> <TableCell><StatusBadge status={item.status} /></TableCell>
<TableCell className="text-xs">{formatRoleCodes(item.roleCodes)}</TableCell> <TableCell className="text-xs">{formatRoleCodes(item.roleCodes)}</TableCell>
@@ -283,7 +283,7 @@ export function UserManagementPage() {
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell colSpan={5} className="py-3 text-slate-500"> <TableCell colSpan={5} className="py-3 text-zinc-500">
暂无数据 暂无数据
</TableCell> </TableCell>
</TableRow> </TableRow>
+14 -14
查看文件
@@ -157,7 +157,7 @@ export function WorkflowTaskDetailPage() {
</Card> </Card>
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<h3 className="text-base font-semibold text-slate-900">审批动作</h3> <h3 className="text-base font-semibold text-zinc-900">审批动作</h3>
{actionError ? ( {actionError ? (
<p <p
@@ -179,8 +179,8 @@ export function WorkflowTaskDetailPage() {
</Button> </Button>
</div> </div>
<div className="space-y-2 rounded-lg border border-slate-200 p-4"> <div className="space-y-2 rounded-lg border border-zinc-200 p-4">
<p className="text-sm font-medium text-slate-800">驳回</p> <p className="text-sm font-medium text-zinc-800">驳回</p>
<Textarea <Textarea
data-testid="workflow-task-reject-comment" data-testid="workflow-task-reject-comment"
onChange={(event) => setRejectComment(event.target.value)} onChange={(event) => setRejectComment(event.target.value)}
@@ -198,8 +198,8 @@ export function WorkflowTaskDetailPage() {
</Button> </Button>
</div> </div>
<div className="space-y-2 rounded-lg border border-slate-200 p-4"> <div className="space-y-2 rounded-lg border border-zinc-200 p-4">
<p className="text-sm font-medium text-slate-800">退回</p> <p className="text-sm font-medium text-zinc-800">退回</p>
<Textarea <Textarea
data-testid="workflow-task-return-comment" data-testid="workflow-task-return-comment"
onChange={(event) => setReturnComment(event.target.value)} onChange={(event) => setReturnComment(event.target.value)}
@@ -217,10 +217,10 @@ export function WorkflowTaskDetailPage() {
</Button> </Button>
</div> </div>
<div className="space-y-2 rounded-lg border border-slate-200 p-4"> <div className="space-y-2 rounded-lg border border-zinc-200 p-4">
<p className="text-sm font-medium text-slate-800">转审</p> <p className="text-sm font-medium text-zinc-800">转审</p>
<select <select
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="workflow-task-transfer-to-approver" data-testid="workflow-task-transfer-to-approver"
onChange={(event) => setSelectedApprover(event.target.value)} onChange={(event) => setSelectedApprover(event.target.value)}
value={selectedApprover} value={selectedApprover}
@@ -256,20 +256,20 @@ export function WorkflowTaskDetailPage() {
</Card> </Card>
<Card className="space-y-3 p-6"> <Card className="space-y-3 p-6">
<h3 className="text-base font-semibold text-slate-900">处理历史</h3> <h3 className="text-base font-semibold text-zinc-900">处理历史</h3>
<div className="space-y-2"> <div className="space-y-2">
{detail.history.length ? ( {detail.history.length ? (
detail.history.map((item) => ( detail.history.map((item) => (
<div className="rounded-lg border border-slate-200 px-3 py-2 text-sm" key={item.id}> <div className="rounded-lg border border-zinc-200 px-3 py-2 text-sm" key={item.id}>
<div className="font-medium text-slate-900"> <div className="font-medium text-zinc-900">
{item.action} · {item.actorName || item.actorId} {item.action} · {item.actorName || item.actorId}
</div> </div>
<div className="text-slate-600">{item.comment || "-"}</div> <div className="text-zinc-600">{item.comment || "-"}</div>
<div className="text-xs text-slate-500">{item.createdAt || "-"}</div> <div className="text-xs text-zinc-500">{item.createdAt || "-"}</div>
</div> </div>
)) ))
) : ( ) : (
<p className="text-sm text-slate-500">暂无历史记录</p> <p className="text-sm text-zinc-500">暂无历史记录</p>
)} )}
</div> </div>
</Card> </Card>
+4 -4
查看文件
@@ -78,7 +78,7 @@ export function WorkflowTasksPage() {
) : null} ) : null}
{!query.isLoading && !query.isError ? ( {!query.isLoading && !query.isError ? (
<div className="rounded-lg border border-slate-200"> <div className="rounded-lg border border-zinc-200">
<Table data-testid="workflow-tasks-table"> <Table data-testid="workflow-tasks-table">
<TableHeader> <TableHeader>
<tr> <tr>
@@ -94,8 +94,8 @@ export function WorkflowTasksPage() {
query.data?.items.map((task) => ( query.data?.items.map((task) => (
<TableRow data-testid="workflow-task-row" key={task.id}> <TableRow data-testid="workflow-task-row" key={task.id}>
<TableCell> <TableCell>
<div className="font-medium text-slate-900">{task.stepName}</div> <div className="font-medium text-zinc-900">{task.stepName}</div>
<div className="text-xs text-slate-500">{task.id}</div> <div className="text-xs text-zinc-500">{task.id}</div>
</TableCell> </TableCell>
<TableCell className="font-mono text-xs"> <TableCell className="font-mono text-xs">
{task.workflowInstance?.entityId} {task.workflowInstance?.entityId}
@@ -116,7 +116,7 @@ export function WorkflowTasksPage() {
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell className="py-3 text-slate-500" colSpan={5}> <TableCell className="py-3 text-zinc-500" colSpan={5}>
暂无数据 暂无数据
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -39,7 +39,7 @@ export function ReimbursementActions({
return ( return (
<Card className="space-y-4 p-6"> <Card className="space-y-4 p-6">
<h3 className="text-base font-semibold text-slate-900">可执行动作</h3> <h3 className="text-base font-semibold text-zinc-900">可执行动作</h3>
{actionError ? ( {actionError ? (
<p <p
@@ -61,8 +61,8 @@ export function ReimbursementActions({
</Button> </Button>
</div> </div>
<div className="space-y-2 rounded-lg border border-slate-200 p-4"> <div className="space-y-2 rounded-lg border border-zinc-200 p-4">
<p className="text-sm font-medium text-slate-800">撤回报销</p> <p className="text-sm font-medium text-zinc-800">撤回报销</p>
<Textarea <Textarea
data-testid="reimbursement-withdraw-comment" data-testid="reimbursement-withdraw-comment"
onChange={(event) => setWithdrawComment(event.target.value)} onChange={(event) => setWithdrawComment(event.target.value)}
@@ -81,8 +81,8 @@ export function ReimbursementActions({
</div> </div>
{canPay ? ( {canPay ? (
<div className="space-y-2 rounded-lg border border-slate-200 p-4"> <div className="space-y-2 rounded-lg border border-zinc-200 p-4">
<p className="text-sm font-medium text-slate-800">财务支付</p> <p className="text-sm font-medium text-zinc-800">财务支付</p>
<Input <Input
data-testid="reimbursement-pay-voucher-no" data-testid="reimbursement-pay-voucher-no"
onChange={(event) => setPaymentVoucherNo(event.target.value)} onChange={(event) => setPaymentVoucherNo(event.target.value)}
@@ -96,7 +96,7 @@ export function ReimbursementActions({
value={paymentDate} value={paymentDate}
/> />
<select <select
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="reimbursement-pay-channel" data-testid="reimbursement-pay-channel"
onChange={(event) => onChange={(event) =>
setPaymentChannel(event.target.value as PaymentChannel) setPaymentChannel(event.target.value as PaymentChannel)
@@ -59,13 +59,13 @@ export function ReimbursementItemEditor({
}; };
return ( return (
<div className="space-y-3 rounded-lg border border-slate-200 p-4" data-testid="reimbursement-item-editor"> <div className="space-y-3 rounded-lg border border-zinc-200 p-4" data-testid="reimbursement-item-editor">
<p className="text-sm font-medium text-slate-800"> <p className="text-sm font-medium text-zinc-800">
{mode === "create" ? "新增明细" : "编辑明细"} {mode === "create" ? "新增明细" : "编辑明细"}
</p> </p>
{expenseTypesLoading ? ( {expenseTypesLoading ? (
<p className="text-sm text-slate-500">加载费用类型中...</p> <p className="text-sm text-zinc-500">加载费用类型中...</p>
) : null} ) : null}
{expenseTypesError ? ( {expenseTypesError ? (
@@ -74,11 +74,11 @@ export function ReimbursementItemEditor({
<div className="grid gap-3 md:grid-cols-4"> <div className="grid gap-3 md:grid-cols-4">
<div className="space-y-1"> <div className="space-y-1">
<label className="text-xs text-slate-600" htmlFor="expenseTypeId"> <label className="text-xs text-zinc-600" htmlFor="expenseTypeId">
费用类型 费用类型
</label> </label>
<select <select
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
data-testid="reimbursement-item-expense-type" data-testid="reimbursement-item-expense-type"
disabled={expenseTypes.length === 0 || isPending} disabled={expenseTypes.length === 0 || isPending}
id="expenseTypeId" id="expenseTypeId"
@@ -95,7 +95,7 @@ export function ReimbursementItemEditor({
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-xs text-slate-600" htmlFor="expenseDate"> <label className="text-xs text-zinc-600" htmlFor="expenseDate">
发生日期 发生日期
</label> </label>
<Input <Input
@@ -109,7 +109,7 @@ export function ReimbursementItemEditor({
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-xs text-slate-600" htmlFor="amount"> <label className="text-xs text-zinc-600" htmlFor="amount">
金额 金额
</label> </label>
<Input <Input
@@ -123,7 +123,7 @@ export function ReimbursementItemEditor({
</div> </div>
<div className="space-y-1 md:col-span-4"> <div className="space-y-1 md:col-span-4">
<label className="text-xs text-slate-600" htmlFor="itemDescription"> <label className="text-xs text-zinc-600" htmlFor="itemDescription">
说明 说明
</label> </label>
<Textarea <Textarea
@@ -167,7 +167,7 @@ export function CreateUserDialog({
<select <select
id="departmentId" id="departmentId"
data-testid="users-create-department" data-testid="users-create-department"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={departmentId} value={departmentId}
onChange={(event) => setDepartmentId(event.target.value)} onChange={(event) => setDepartmentId(event.target.value)}
> >
@@ -187,7 +187,7 @@ export function CreateUserDialog({
<select <select
id="directManagerId" id="directManagerId"
data-testid="users-create-direct-manager" data-testid="users-create-direct-manager"
className="h-11 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm" className="h-11 w-full rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm"
value={directManagerId} value={directManagerId}
onChange={(event) => setDirectManagerId(event.target.value)} onChange={(event) => setDirectManagerId(event.target.value)}
> >