From 5fef6ae2b1a3f236e5bf4d9e431947dbd1dec704 Mon Sep 17 00:00:00 2001 From: jacy-dukang Date: Tue, 7 Jul 2026 17:43:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AFwebadmin=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/admin-web/src/admin.css | 66 ++++++++++++++++++- .../src/components/AdminCellLine.tsx | 34 ++++++++++ apps/admin-web/src/layouts/AdminLayout.tsx | 6 ++ apps/admin-web/src/lib/ellipsis-title.ts | 37 +++++++++++ apps/admin-web/src/pages/HqLogsPage.tsx | 19 +++--- apps/admin-web/src/pages/PartnerLogsPage.tsx | 16 ++--- apps/admin-web/src/pages/StoreLogsPage.tsx | 20 +++--- apps/admin-web/src/pages/UserLogsPage.tsx | 11 ++-- 8 files changed, 175 insertions(+), 34 deletions(-) create mode 100644 apps/admin-web/src/components/AdminCellLine.tsx create mode 100644 apps/admin-web/src/lib/ellipsis-title.ts diff --git a/apps/admin-web/src/admin.css b/apps/admin-web/src/admin.css index 42e62cd..cd49eea 100644 --- a/apps/admin-web/src/admin.css +++ b/apps/admin-web/src/admin.css @@ -15,10 +15,74 @@ body, display: none; } -.admin-table-nowrap .ant-table-cell { +/* + * 后台内容行:不换行 + 超出省略 + * 覆盖主内容区、Drawer、Modal 内的表格与描述列表 + */ +.admin-layout .ant-table-cell, +.ant-drawer .ant-table-cell, +.ant-modal .ant-table-cell, +.admin-layout .ant-descriptions-item-content, +.ant-drawer .ant-descriptions-item-content, +.ant-modal .ant-descriptions-item-content { white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 0; } +.admin-layout .ant-descriptions-item-content, +.ant-drawer .ant-descriptions-item-content, +.ant-modal .ant-descriptions-item-content { + max-width: 100%; +} + +/* 表头同样不换行 */ +.admin-layout .ant-table-thead > tr > th, +.ant-drawer .ant-table-thead > tr > th, +.ant-modal .ant-table-thead > tr > th { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* 操作列、含交互控件的不截断 */ +.admin-layout .ant-table-cell:has(.ant-btn), +.ant-drawer .ant-table-cell:has(.ant-btn), +.ant-modal .ant-table-cell:has(.ant-btn), +.admin-layout .ant-table-cell:has(.ant-space), +.ant-drawer .ant-table-cell:has(.ant-space), +.ant-modal .ant-table-cell:has(.ant-space) { + overflow: visible; + text-overflow: clip; + max-width: none; +} + +/* 代码块、表单区域保持原样 */ +.admin-layout pre, +.ant-drawer pre, +.ant-modal pre, +.admin-layout .ant-form-item-control-input, +.ant-drawer .ant-form-item-control-input, +.ant-modal .ant-form-item-control-input { + white-space: pre-wrap; + overflow: auto; + text-overflow: unset; + max-width: none; +} + +/* 表格单元格双行信息合并为单行省略 */ +.admin-cell-line { + display: block; + max-width: 100%; +} + +.admin-cell-line-secondary { + color: rgba(0, 0, 0, 0.45); + font-size: 12px; +} + +.admin-table-nowrap .ant-table-cell, .admin-table-nowrap .ant-table-cell-ellipsis { white-space: nowrap; } diff --git a/apps/admin-web/src/components/AdminCellLine.tsx b/apps/admin-web/src/components/AdminCellLine.tsx new file mode 100644 index 0000000..ab210df --- /dev/null +++ b/apps/admin-web/src/components/AdminCellLine.tsx @@ -0,0 +1,34 @@ +import { Typography } from 'antd'; + +type AdminCellLineProps = { + primary?: string | null; + secondary?: string | null; + separator?: string; +}; + +/** 表格单元格:主/副信息同一行展示,超出省略,hover 显示全文 */ +export function AdminCellLine({ + primary, + secondary, + separator = ' · ', +}: AdminCellLineProps) { + const main = primary?.trim() || '—'; + const sub = secondary?.trim(); + const full = sub ? `${main}${separator}${sub}` : main; + + return ( + + {main} + {sub ? ( + + {separator} + {sub} + + ) : null} + + ); +} diff --git a/apps/admin-web/src/layouts/AdminLayout.tsx b/apps/admin-web/src/layouts/AdminLayout.tsx index 5a71690..139d7b1 100644 --- a/apps/admin-web/src/layouts/AdminLayout.tsx +++ b/apps/admin-web/src/layouts/AdminLayout.tsx @@ -16,6 +16,7 @@ import { FileTextOutlined, } from '@ant-design/icons'; import { clearAuth, request, type HqProfile } from '../lib/api'; +import { bindAdminEllipsisTitle } from '../lib/ellipsis-title'; const { Header, Sider, Content } = Layout; @@ -105,6 +106,10 @@ export default function AdminLayout() { contentRef.current?.scrollTo({ top: 0, left: 0 }); }, [location.pathname]); + useEffect(() => { + return bindAdminEllipsisTitle(); + }, []); + function logout() { clearAuth(); navigate('/login'); @@ -164,6 +169,7 @@ export default function AdminLayout() {
diff --git a/apps/admin-web/src/lib/ellipsis-title.ts b/apps/admin-web/src/lib/ellipsis-title.ts new file mode 100644 index 0000000..82d6045 --- /dev/null +++ b/apps/admin-web/src/lib/ellipsis-title.ts @@ -0,0 +1,37 @@ +const ELLIPSIS_TARGETS = + '.ant-table-cell, .ant-descriptions-item-content, .ant-typography'; + +const ELLIPSIS_SKIP = + '.ant-btn, .ant-input, .ant-select, .ant-picker, .ant-checkbox, .ant-radio, pre, textarea, input, select, button, a.ant-btn'; + +function isTruncated(el: HTMLElement): boolean { + return el.scrollWidth > el.clientWidth + 1; +} + +function resolveEllipsisElement(target: EventTarget | null): HTMLElement | null { + if (!(target instanceof HTMLElement)) return null; + const cell = target.closest(ELLIPSIS_TARGETS); + if (!cell) return null; + if (cell.closest('.admin-cell-line')) return null; + if (cell.closest(ELLIPSIS_SKIP)) return null; + if (cell.querySelector(ELLIPSIS_SKIP)) return null; + return cell; +} + +export function bindAdminEllipsisTitle(root: HTMLElement | Document = document) { + const onMouseOver = (event: MouseEvent) => { + const el = resolveEllipsisElement(event.target); + if (!el) return; + if (isTruncated(el)) { + const text = el.innerText.replace(/\s+/g, ' ').trim(); + if (text && el.getAttribute('title') !== text) { + el.setAttribute('title', text); + } + } else { + el.removeAttribute('title'); + } + }; + + root.addEventListener('mouseover', onMouseOver, true); + return () => root.removeEventListener('mouseover', onMouseOver, true); +} diff --git a/apps/admin-web/src/pages/HqLogsPage.tsx b/apps/admin-web/src/pages/HqLogsPage.tsx index 16c32b9..bf552e5 100644 --- a/apps/admin-web/src/pages/HqLogsPage.tsx +++ b/apps/admin-web/src/pages/HqLogsPage.tsx @@ -5,6 +5,7 @@ import { import type { ColumnsType } from 'antd/es/table'; import { useSearchParams } from 'react-router-dom'; import { request } from '../lib/api'; +import { AdminCellLine } from '../components/AdminCellLine'; import { fmtTime } from '../lib/constants'; import { HQ_OPERATION_ACTION_OPTIONS, resolveHqOperationLabel } from '../lib/hq-log'; import { useAdminList } from '../lib/useAdminList'; @@ -55,14 +56,13 @@ export default function HqLogsPage() { { title: '时间', dataIndex: 'createdAt', width: 160, render: fmtTime }, { title: '操作人', - width: 160, + width: 200, + ellipsis: true, render: (_, r) => ( -
-
{r.hqName || '—'}
-
- {r.hqPhone || ''} {r.hqAccountId ? `(#${r.hqAccountId})` : ''} -
-
+ ), }, { @@ -170,7 +170,10 @@ export default function HqLogsPage() { {detail.id} {fmtTime(detail.createdAt)} - {detail.hqName} / {detail.hqPhone} (ID: {detail.hqAccountId}) + {detail.hqRole || '—'} diff --git a/apps/admin-web/src/pages/PartnerLogsPage.tsx b/apps/admin-web/src/pages/PartnerLogsPage.tsx index 9580874..02ba7a8 100644 --- a/apps/admin-web/src/pages/PartnerLogsPage.tsx +++ b/apps/admin-web/src/pages/PartnerLogsPage.tsx @@ -9,6 +9,7 @@ import { type PartnerLogCategory, } from '../lib/partner-log'; import { request } from '../lib/api'; +import { AdminCellLine } from '../components/AdminCellLine'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; @@ -71,21 +72,20 @@ export default function PartnerLogsPage() { { title: '合伙人', width: 180, + ellipsis: true, render: (_, r) => ( -
-
{r.companyName || '—'}
-
{r.partnerId}
-
+ ), }, { title: '账号', width: 150, + ellipsis: true, render: (_, r) => ( -
-
{r.accountName || '—'}
-
{r.accountPhone || r.partnerAccountId || '—'}
-
+ ), }, { diff --git a/apps/admin-web/src/pages/StoreLogsPage.tsx b/apps/admin-web/src/pages/StoreLogsPage.tsx index 5f9e193..99fb995 100644 --- a/apps/admin-web/src/pages/StoreLogsPage.tsx +++ b/apps/admin-web/src/pages/StoreLogsPage.tsx @@ -9,6 +9,7 @@ import { type StoreLogCategory, } from '../lib/store-log'; import { request } from '../lib/api'; +import { AdminCellLine } from '../components/AdminCellLine'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; @@ -82,21 +83,16 @@ export default function StoreLogsPage() { const columns: ColumnsType = [ { title: '时间', dataIndex: 'createdAt', width: 160, render: fmtTime }, { - title: '门店', width: 180, - render: (_, r) => ( -
-
{r.storeName || '—'}
-
{r.storeId}
-
- ), + title: '门店', width: 180, ellipsis: true, + render: (_, r) => , }, { - title: '账号', width: 150, + title: '账号', width: 150, ellipsis: true, render: (_, r) => ( -
-
{r.accountName || '—'}
-
{r.accountPhone || r.storeAccountId || '系统/HQ'}
-
+ ), }, { diff --git a/apps/admin-web/src/pages/UserLogsPage.tsx b/apps/admin-web/src/pages/UserLogsPage.tsx index 7073bb5..c66044e 100644 --- a/apps/admin-web/src/pages/UserLogsPage.tsx +++ b/apps/admin-web/src/pages/UserLogsPage.tsx @@ -4,6 +4,7 @@ import type { ColumnsType } from 'antd/es/table'; import { USER_LOG_CATEGORY_OPTIONS, resolveUserLogCategory, USER_LOG_CATEGORY_LABELS, type UserLogCategory } from '../lib/user-log'; import { useSearchParams } from 'react-router-dom'; import { request } from '../lib/api'; +import { AdminCellLine } from '../components/AdminCellLine'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; @@ -63,12 +64,12 @@ export default function UserLogsPage() { const columns: ColumnsType = [ { title: '时间', dataIndex: 'createdAt', width: 160, render: fmtTime }, { - title: '用户', width: 160, + title: '用户', width: 180, ellipsis: true, render: (_, r) => ( -
-
{r.nickname || r.userNo || '—'}
-
{r.phone || r.userId || '—'}
-
+ ), }, {