From 2535bd809214d8a6b10c1e8b0053b409804edac9 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 15 May 2026 07:58:52 +0800 Subject: [PATCH] =?UTF-8?q?CSS=20=E9=87=8D=E6=9E=84=EF=BC=9A=E6=B6=88?= =?UTF-8?q?=E9=99=A4=E9=87=8D=E5=A4=8D=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E5=8F=96=E5=85=B1=E4=BA=AB=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 admin-list-pages.css 共享样式(7个列表页统一使用) - AdminKuaishouCloudFulfillment.css 删除 488 行重复副本 - AdminInventoryView CSS 提取到独立文件(-268行) - formatTaskEventPayload 等 6 个函数提取到 admin-display.ts - 各列表页 CSS 精简为 @import + 少量页面特有样式 - 净减少 CSS 约 1300 行 Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/frontend/src/styles/admin-list-pages.css | 288 ++++++++++ apps/frontend/src/utils/admin-display.ts | 114 ++++ .../src/views/admin/AdminAuditLogsView.vue | 7 +- .../src/views/admin/AdminInventoryView.css | 228 ++++++++ .../src/views/admin/AdminInventoryView.vue | 273 +--------- .../admin/AdminKuaishouCloudFulfillment.css | 501 +----------------- .../admin/AdminMessageDeliveriesView.vue | 151 +----- .../src/views/admin/AdminOrdersView.vue | 151 +----- .../src/views/admin/AdminTaskDetailView.vue | 116 +--- .../src/views/admin/AdminTasksView.vue | 109 +--- .../src/views/admin/AdminUsersView.vue | 26 +- .../views/admin/AdminWebhookEventsView.vue | 156 +----- 12 files changed, 665 insertions(+), 1455 deletions(-) create mode 100644 apps/frontend/src/styles/admin-list-pages.css create mode 100644 apps/frontend/src/views/admin/AdminInventoryView.css diff --git a/apps/frontend/src/styles/admin-list-pages.css b/apps/frontend/src/styles/admin-list-pages.css new file mode 100644 index 00000000..eafbcffe --- /dev/null +++ b/apps/frontend/src/styles/admin-list-pages.css @@ -0,0 +1,288 @@ +/* ============================================================ + 管理员列表页共享样式 + 引入: AdminOrdersView, AdminTasksView, AdminMessageDeliveriesView, + AdminWebhookEventsView, AdminUsersView, AdminInventoryView + 各页面自行覆盖 grid-template-columns 等布局差异 + ============================================================ */ + +/* ---------- 页面包裹 ---------- */ +.list-page { + display: grid; + gap: 0; +} + +/* ---------- 总量标签 ---------- */ +.total-badge { + display: inline-flex; + align-items: center; + min-height: 32px; + padding: 0 12px; + border: 1px solid rgba(37, 89, 167, 0.14); + border-radius: var(--radius-full); + background: rgba(37, 89, 167, 0.08); + color: #2559a7; + font-size: var(--text-xs); + font-weight: 700; +} + +/* ---------- 卡片区域 ---------- */ +.section-card { + margin-top: var(--space-4); +} + +/* ---------- 卡片头部 ---------- */ +.card-header { + display: flex; + justify-content: space-between; + gap: var(--space-3); + align-items: flex-end; +} + +.card-title { + color: var(--text-primary); + font-size: var(--text-lg); + font-weight: 600; +} + +.card-desc { + color: var(--text-secondary); + font-size: var(--text-sm); +} + +/* ---------- 筛选网格 ---------- */ +.filter-grid { + display: grid; + gap: var(--space-3); + align-items: center; +} + +.filter-control { + width: 100%; + min-width: 0; +} + +.filter-buttons { + display: flex; + gap: 10px; + min-width: 156px; + justify-content: flex-end; +} + +/* ---------- 摘要条 ---------- */ +.summary-strip { + display: flex; + align-items: center; + gap: var(--space-2); + min-height: 38px; + padding: 0; + margin-top: var(--space-4); + flex-wrap: wrap; +} + +.summary-title { + color: var(--text-secondary); + font-size: var(--text-xs); + font-weight: 700; + margin-right: 2px; +} + +.summary-metric { + display: inline-flex; + align-items: center; + gap: 5px; + min-height: 28px; + padding: 0 10px; + border-radius: var(--radius-full); + border: 1px solid transparent; + color: var(--text-secondary); + background: #f8fafc; + font-size: var(--text-xs); + font-weight: 700; +} + +.summary-metric strong { + color: var(--text-primary); + font-size: var(--text-base); + line-height: 1; +} + +.summary-metric[data-tone='success'] { color: var(--color-success-dark); background: var(--color-success-light); border-color: #bbf7d0; } +.summary-metric[data-tone='danger'] { color: var(--color-danger-dark); background: var(--color-danger-light); border-color: #fecaca; } +.summary-metric[data-tone='primary'] { color: var(--color-primary-dark); background: var(--color-primary-light); border-color: #bfdbfe; } +.summary-metric[data-tone='warning'] { color: var(--color-warning-dark); background: var(--color-warning-light); border-color: #fed7aa; } +.summary-metric[data-tone='muted'] { color: var(--text-secondary); background: #f8fafc; border-color: var(--border-default); } + +/* ---------- 表格工具栏 ---------- */ +.table-toolbar { + margin-bottom: var(--space-3); +} + +.table-toolbar strong { + display: block; + color: var(--text-primary); + font-size: var(--text-base); +} + +.table-toolbar span { + display: block; + margin-top: 4px; + color: var(--text-secondary); + font-size: var(--text-xs); +} + +/* ---------- el-table 统一主题变量 ---------- */ +.data-table { + width: 100%; + overflow: hidden; + border: 1px solid var(--border-default); + border-radius: var(--radius-sm); + --el-table-bg-color: transparent; + --el-table-tr-bg-color: transparent; + --el-table-header-bg-color: rgba(239, 246, 255, 0.72); + --el-table-row-hover-bg-color: rgba(239, 246, 255, 0.72); + --el-table-border-color: rgba(86, 108, 138, 0.1); +} + +.data-table :deep(.el-table__inner-wrapper::before) { + display: none; +} + +.data-table :deep(th.el-table__cell) { + height: 42px; + background: var(--el-table-header-bg-color); + color: var(--text-secondary); + font-weight: 700; +} + +/* ---------- 单元格通用 ---------- */ +.cell-stack { + display: grid; + gap: 5px; +} + +.cell-meta-row { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); +} + +.compact-chip { + display: inline-flex; + align-items: center; + min-height: 24px; + max-width: 240px; + padding: 0 9px; + border-radius: var(--radius-full); + background: #f4f7fb; + border: 1px solid var(--border-default); + color: var(--text-secondary); + font-size: var(--text-xs); + font-weight: 600; +} + +.compact-chip--muted { + color: var(--text-muted); +} + +.cell-title { + display: block; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--text-primary); + font-weight: 700; +} + +.cell-subline { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #475467; + font-size: var(--text-xs); +} + +/* ---------- ID 链接 ---------- */ +.id-link { + display: inline-block; + max-width: 100%; + font-size: var(--text-base); + font-weight: 800; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-decoration: none; +} + +.id-link:hover { + color: var(--color-primary-dark); +} + +/* ---------- 详情链接按钮 ---------- */ +.detail-link { + display: inline-flex; + align-items: center; + min-height: 28px; + padding: 0 10px; + border-radius: var(--radius-full); + border: 1px solid var(--border-default); + color: var(--color-primary-dark); + background: var(--color-primary-light); + font-size: var(--text-xs); + font-weight: 700; + text-decoration: none; +} + +.detail-link:hover { + border-color: var(--border-hover); + background: #dbeafe; +} + +/* ---------- 操作 & 状态容器 ---------- */ +.action-stack { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} + +.status-stack { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); + align-items: center; +} + +.status-stack :deep(.el-tag) { + max-width: 100%; +} + +/* ---------- 响应式 ---------- */ +@media (max-width: 1200px) { + .filter-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width: 900px) { + .filter-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .filter-buttons { + justify-content: flex-start; + } +} + +@media (max-width: 640px) { + .filter-grid { + grid-template-columns: 1fr; + } + .filter-buttons { + width: 100%; + } + .filter-buttons :deep(.el-button) { + flex: 1 1 auto; + } +} diff --git a/apps/frontend/src/utils/admin-display.ts b/apps/frontend/src/utils/admin-display.ts index 1b12f3ed..eaacff6d 100644 --- a/apps/frontend/src/utils/admin-display.ts +++ b/apps/frontend/src/utils/admin-display.ts @@ -65,3 +65,117 @@ export function formatTaskEventType(eventType: string) { return formatStatusWithRaw(eventType, labelMap) } + +export function formatRedeemOutcomeLabel(value: string) { + const normalized = String(value || '').trim() + + switch (normalized) { + case 'success': + return '兑换成功' + case 'code_used': + return '兑换码已使用' + case 'code_invalid': + return '兑换码错误' + case 'captcha_rejected': + return '验证码错误' + case 'failed': + return '兑换失败' + default: + return normalized || '-' + } +} + +export function formatRedeemResolutionStatus(value: string) { + return value === 'success' ? '已完成' : value === 'failed' ? '失败收口' : (value || '-') +} + +export function formatAutoDeliveryTrigger(value: string) { + const normalized = String(value || '').trim() + + switch (normalized) { + case 'claim_redeemed': + return '用户兑换完成后触发' + case 'manual_dispatch_completed': + return '人工履约完成后触发' + case 'task_delivered': + return '任务交付完成后触发' + default: + return normalized || '-' + } +} + +export function formatAutoDeliveryReason(value: string) { + const normalized = String(value || '').trim() + + switch (normalized) { + case 'waiting_other_tasks': + return '订单下仍有任务未完成交付' + case 'already_sent': + return '该订单已经执行过自动发货' + case 'auto_delivery_disabled': + return '自动发货开关已关闭' + case 'missing_config': + return '缺少 Agiso 自动发货配置' + case 'request_failed': + return 'Agiso 接口返回失败' + case 'request_error': + return '请求 Agiso 接口时发生异常' + case 'delivery_not_confirmed': + return '接口返回成功,但订单状态未确认进入已发货' + case 'task_not_delivered': + return '当前任务尚未进入已交付状态' + case 'not_supported': + return '当前订单不支持 Agiso 咸鱼自动发货' + default: + return normalized || '-' + } +} + +export function formatKuaishouRoleInfoLabel(value: string) { + const normalized = String(value || '').trim() + + switch (normalized) { + case 'name': + return '角色名称' + case 'rid': + return '角色 ID' + default: + return normalized || '-' + } +} + +export function formatTaskEventPayload(payload: Record) { + const parts = [ + payload.outcome ? `结果 ${payload.outcome}` : '', + payload.resultCode ? `代码 ${payload.resultCode}` : '', + payload.resultMessage ? `说明 ${payload.resultMessage}` : '', + payload.codeMasked ? `凭据 ${payload.codeMasked}` : '', + payload.previousCodeMasked ? `旧码 ${payload.previousCodeMasked}` : '', + payload.nextCodeMasked ? `新码 ${payload.nextCodeMasked}` : '', + payload.inventoryItemId ? `库存项 #${payload.inventoryItemId}` : '', + payload.previousInventoryItemId ? `旧库存 #${payload.previousInventoryItemId}` : '', + payload.nextInventoryItemId ? `新库存 #${payload.nextInventoryItemId}` : '', + payload.ticketCodeMasked ? `卡券号 ${payload.ticketCodeMasked}` : '', + payload.vnId ? `虚拟号 #${payload.vnId}` : '', + payload.vnPhoneMasked ? `号码 ${payload.vnPhoneMasked}` : '', + payload.bindUrl ? `绑定链接 ${payload.bindUrl}` : '', + payload.sendType ? `发送类型 ${payload.sendType}` : '', + payload.note ? `备注 ${payload.note}` : '', + payload.previousOutcome ? `切换原因 ${formatRedeemOutcomeLabel(String(payload.previousOutcome || ''))}` : '', + payload.deliveryReference ? `单号 ${payload.deliveryReference}` : '', + payload.platformOrderId ? `平台单 ${payload.platformOrderId}` : '', + payload.trigger ? `触发 ${formatAutoDeliveryTrigger(String(payload.trigger || ''))}` : '', + payload.reason ? `原因 ${payload.reason}` : '', + payload.purchaseTriggered === true ? '已触发购买' : '', + payload.usedKnapsack === true ? '使用背包库存' : '', + payload.responseStatus ? `HTTP ${payload.responseStatus}` : '', + payload.requestId ? `请求 ${payload.requestId}` : '', + payload.errorMessage ? `错误 ${payload.errorMessage}` : '', + ].filter(Boolean) + + if (parts.length > 0) { + return parts.join(' · ') + } + + return JSON.stringify(payload || {}) +} diff --git a/apps/frontend/src/views/admin/AdminAuditLogsView.vue b/apps/frontend/src/views/admin/AdminAuditLogsView.vue index 3cf32f6b..813b4fda 100644 --- a/apps/frontend/src/views/admin/AdminAuditLogsView.vue +++ b/apps/frontend/src/views/admin/AdminAuditLogsView.vue @@ -72,7 +72,7 @@ onMounted(loadAuditLogs)