DG9 统一全站 UI 布局样板迁移
关联 Issue: DG9-0..DG9-12
这个提交包含在:
@@ -999,11 +999,11 @@ export function MobileApprovalsPage() {
|
||||
) : null}
|
||||
|
||||
{!isPushDetail && !canAccessRemoteApprovals ? (
|
||||
<section className="mobile-card space-y-3" data-testid="mobile-approvals-progress">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-zinc-900">我发起的进度</h3>
|
||||
<p className="mt-1 text-sm text-zinc-500">报销、请假、采购进度</p>
|
||||
<section className="mobile-card mobile-page-stack" data-testid="mobile-approvals-progress">
|
||||
<div className="mobile-section-header">
|
||||
<div className="min-w-0">
|
||||
<h3 className="mobile-section-title">我发起的进度</h3>
|
||||
<p className="mobile-list-card__subtitle mt-1">报销、请假、采购进度</p>
|
||||
</div>
|
||||
<span className="badge-neutral">{employeeProgressCards.length} 条</span>
|
||||
</div>
|
||||
@@ -1028,7 +1028,7 @@ export function MobileApprovalsPage() {
|
||||
<div className="mobile-empty-state">暂无申请进度</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="mobile-page-stack">
|
||||
{employeeProgressCards.map((item) => (
|
||||
<Link
|
||||
className="mobile-list-card block"
|
||||
@@ -1037,12 +1037,14 @@ export function MobileApprovalsPage() {
|
||||
to={item.to}
|
||||
state={{ from: currentMobileSource }}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="mobile-list-card__header">
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-semibold text-zinc-900">{item.title}</div>
|
||||
{item.subtitle ? <div className="mt-1 truncate text-xs text-zinc-500">{item.subtitle}</div> : null}
|
||||
<div className="mobile-list-card__title">{item.title}</div>
|
||||
{item.subtitle ? <div className="mobile-list-card__subtitle mt-1">{item.subtitle}</div> : null}
|
||||
</div>
|
||||
<StatusBadge status={item.status} />
|
||||
<span className="mobile-status-slot">
|
||||
<StatusBadge status={item.status} />
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
@@ -1051,13 +1053,13 @@ export function MobileApprovalsPage() {
|
||||
) : null}
|
||||
|
||||
{!isPushDetail && canAccessRemoteApprovals ? (
|
||||
<section className="mobile-card space-y-4" data-testid="mobile-approvals-list">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-zinc-900">{listTitle}</h3>
|
||||
<p className="mt-1 text-sm text-zinc-500">{filteredTaskCards.length} 条</p>
|
||||
<section className="mobile-card mobile-page-stack" data-testid="mobile-approvals-list">
|
||||
<div className="mobile-section-header">
|
||||
<div className="min-w-0">
|
||||
<h3 className="mobile-section-title">{listTitle}</h3>
|
||||
<p className="mobile-list-card__subtitle mt-1">{filteredTaskCards.length} 条</p>
|
||||
</div>
|
||||
<span className={`text-sm ${isOnline ? "text-emerald-600" : "text-orange-600"}`}>{isOnline ? "在线" : "离线"}</span>
|
||||
<span className={`mobile-section-meta ${isOnline ? "text-emerald-600" : "text-orange-600"}`}>{isOnline ? "在线" : "离线"}</span>
|
||||
</div>
|
||||
|
||||
<Tabs value={taskTab} onValueChange={handleTaskTabChange}>
|
||||
@@ -1195,7 +1197,7 @@ export function MobileApprovalsPage() {
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="mobile-page-stack">
|
||||
{filteredTaskCards.map((task) => {
|
||||
const isSelected = selectedTaskId === task.id;
|
||||
const isBatchSelected = selectedBatchTaskIds.includes(task.id);
|
||||
@@ -1204,7 +1206,7 @@ export function MobileApprovalsPage() {
|
||||
return (
|
||||
<div
|
||||
key={task.id}
|
||||
className={`mobile-list-card flex gap-3 ${
|
||||
className={`mobile-list-card grid grid-cols-[auto_minmax(0,1fr)] gap-3 ${
|
||||
isBatchSelected ? "border-primary bg-primary-50" : "border-zinc-200 bg-white"
|
||||
}`}
|
||||
>
|
||||
@@ -1221,12 +1223,14 @@ export function MobileApprovalsPage() {
|
||||
data-testid={`mobile-approvals-task-${task.id}`}
|
||||
onClick={() => toggleBatchTask(task.id)}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="font-medium text-zinc-900">{task.title}</div>
|
||||
<StatusBadge status={task.status} label={workflowStatusLabel(task.status)} />
|
||||
<div className="mobile-list-card__header">
|
||||
<div className="mobile-list-card__title">{task.title}</div>
|
||||
<span className="mobile-status-slot">
|
||||
<StatusBadge status={task.status} label={workflowStatusLabel(task.status)} />
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">{task.subtitle}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
<div className="mobile-list-card__subtitle mt-1">{task.subtitle}</div>
|
||||
<div className="mt-1 min-w-0 truncate text-xs text-zinc-500">
|
||||
{joinBusinessMeta([task.applicantName ? `申请人:${task.applicantName}` : null, task.metric ? `金额/天数:${task.metric}` : null]) || "暂无摘要"}
|
||||
</div>
|
||||
{task.isCached ? <div className="mt-1 text-xs text-zinc-500">来源:离线缓存</div> : null}
|
||||
@@ -1245,12 +1249,14 @@ export function MobileApprovalsPage() {
|
||||
onClick={() => handleTaskSelection(task.id)}
|
||||
type="button"
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="font-medium text-zinc-900">{task.title}</div>
|
||||
<StatusBadge status={task.status} label={workflowStatusLabel(task.status)} />
|
||||
<div className="mobile-list-card__header">
|
||||
<div className="mobile-list-card__title">{task.title}</div>
|
||||
<span className="mobile-status-slot">
|
||||
<StatusBadge status={task.status} label={workflowStatusLabel(task.status)} />
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">{task.subtitle}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
<div className="mobile-list-card__subtitle mt-1">{task.subtitle}</div>
|
||||
<div className="mt-1 min-w-0 truncate text-xs text-zinc-500">
|
||||
{joinBusinessMeta([task.applicantName ? `申请人:${task.applicantName}` : null, task.metric ? `金额/天数:${task.metric}` : null]) || "暂无摘要"}
|
||||
</div>
|
||||
{task.isCached ? <div className="mt-1 text-xs text-zinc-500">来源:离线缓存</div> : null}
|
||||
@@ -1262,8 +1268,8 @@ export function MobileApprovalsPage() {
|
||||
) : null}
|
||||
|
||||
{isPushDetail ? (
|
||||
<section className="mobile-card scroll-mt-4 space-y-3" data-testid="mobile-approvals-detail">
|
||||
<h3 className="text-base font-semibold text-zinc-900">处理</h3>
|
||||
<section className="mobile-card mobile-page-stack scroll-mt-4" data-testid="mobile-approvals-detail">
|
||||
<h3 className="mobile-section-title">处理</h3>
|
||||
|
||||
{!selectedTaskId ? <p className="text-sm text-zinc-500">请先选择任务。</p> : null}
|
||||
|
||||
@@ -1286,12 +1292,14 @@ export function MobileApprovalsPage() {
|
||||
{selectedTask ? (
|
||||
<>
|
||||
<div className="mobile-list-card text-sm">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="mobile-list-card__header">
|
||||
<div className="min-w-0">
|
||||
<div className="truncate font-semibold text-zinc-900">{selectedDetailTitle}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">{joinBusinessMeta([`节点:${selectedTask.task.stepName}`, `创建:${formatDateTime(selectedTask.task.createdAt)}`])}</div>
|
||||
<div className="mobile-list-card__title">{selectedDetailTitle}</div>
|
||||
<div className="mobile-list-card__subtitle mt-1">{joinBusinessMeta([`节点:${selectedTask.task.stepName}`, `创建:${formatDateTime(selectedTask.task.createdAt)}`])}</div>
|
||||
</div>
|
||||
<StatusBadge status={selectedTask.task.status} label={workflowStatusLabel(selectedTask.task.status)} />
|
||||
<span className="mobile-status-slot">
|
||||
<StatusBadge status={selectedTask.task.status} label={workflowStatusLabel(selectedTask.task.status)} />
|
||||
</span>
|
||||
</div>
|
||||
<div className="mobile-field-list mt-3">
|
||||
<div className="mobile-field-row">
|
||||
@@ -1343,10 +1351,10 @@ export function MobileApprovalsPage() {
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2" data-testid="mobile-approvals-history">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h4 className="text-sm font-semibold text-zinc-900">处理历史</h4>
|
||||
<span className="text-xs text-zinc-500">{selectedTask.history.length} 条</span>
|
||||
<div className="mobile-page-stack" data-testid="mobile-approvals-history">
|
||||
<div className="mobile-section-header">
|
||||
<h4 className="mobile-section-title">处理历史</h4>
|
||||
<span className="mobile-section-meta">{selectedTask.history.length} 条</span>
|
||||
</div>
|
||||
{selectedTask.history.length ? (
|
||||
selectedTask.history.map((item) => (
|
||||
@@ -1381,7 +1389,7 @@ export function MobileApprovalsPage() {
|
||||
<p className="text-xs text-zinc-500">离线仅保存通过 / 驳回草稿,联网同步成功后才会正式提交。</p>
|
||||
) : null}
|
||||
|
||||
<div className="grid gap-2 min-[390px]:grid-cols-3">
|
||||
<div className="mobile-sticky-action-grid">
|
||||
<Button
|
||||
className="min-h-12 w-full"
|
||||
data-testid="mobile-approvals-approve"
|
||||
@@ -1470,10 +1478,10 @@ export function MobileApprovalsPage() {
|
||||
) : null}
|
||||
|
||||
{!isPushDetail && shouldShowDraftQueue ? (
|
||||
<section className="mobile-card space-y-3" data-testid="mobile-approvals-queue">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h3 className="text-base font-semibold text-zinc-900">离线草稿</h3>
|
||||
<span className="text-sm text-zinc-500">{currentQueueItems.length ? `待同步 ${currentQueueItems.length}` : "无草稿"}</span>
|
||||
<section className="mobile-card mobile-page-stack" data-testid="mobile-approvals-queue">
|
||||
<div className="mobile-section-header">
|
||||
<h3 className="mobile-section-title">离线草稿</h3>
|
||||
<span className="mobile-section-meta">{currentQueueItems.length ? `待同步 ${currentQueueItems.length}` : "无草稿"}</span>
|
||||
</div>
|
||||
|
||||
{currentQueueItems.length ? (
|
||||
@@ -1495,13 +1503,27 @@ export function MobileApprovalsPage() {
|
||||
|
||||
{currentQueueItems.map((item) => (
|
||||
<div key={item.id} className="mobile-list-card text-sm">
|
||||
<div className="font-medium text-zinc-900">
|
||||
<div className="mobile-list-card__title">
|
||||
{publicReference(item.taskSnapshot.reference) ?? entityLabel(item.taskSnapshot.entityType)} · {item.action === "approve" ? "通过" : "驳回"}
|
||||
</div>
|
||||
<div className="text-zinc-500">业务:{entityLabel(item.taskSnapshot.entityType)} · 步骤:{item.taskSnapshot.stepName ?? "-"}</div>
|
||||
<div className="text-zinc-500">状态:{queueStatusLabel(item.status)} · 更新时间:{formatDateTime(item.updatedAt)}</div>
|
||||
<div className="text-zinc-500">意见:{item.comment ?? "-"}</div>
|
||||
<div className="text-zinc-500">最近错误:{item.lastError ?? "-"}</div>
|
||||
<div className="mobile-field-list mt-2">
|
||||
<div className="mobile-field-row">
|
||||
<div className="mobile-field-label">业务</div>
|
||||
<div className="mobile-field-value">{entityLabel(item.taskSnapshot.entityType)} · 步骤:{item.taskSnapshot.stepName ?? "-"}</div>
|
||||
</div>
|
||||
<div className="mobile-field-row">
|
||||
<div className="mobile-field-label">状态</div>
|
||||
<div className="mobile-field-value">{queueStatusLabel(item.status)} · 更新时间:{formatDateTime(item.updatedAt)}</div>
|
||||
</div>
|
||||
<div className="mobile-field-row">
|
||||
<div className="mobile-field-label">意见</div>
|
||||
<div className="mobile-field-value">{item.comment ?? "-"}</div>
|
||||
</div>
|
||||
<div className="mobile-field-row">
|
||||
<div className="mobile-field-label">最近错误</div>
|
||||
<div className="mobile-field-value">{item.lastError ?? "-"}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 grid gap-2 sm:flex sm:flex-wrap">
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
在新工单中引用
屏蔽一个用户