后端webadmin调整显示布局
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<Typography.Text
|
||||
className="admin-cell-line"
|
||||
ellipsis={{ tooltip: full }}
|
||||
style={{ maxWidth: '100%' }}
|
||||
>
|
||||
{main}
|
||||
{sub ? (
|
||||
<span className="admin-cell-line-secondary">
|
||||
{separator}
|
||||
{sub}
|
||||
</span>
|
||||
) : null}
|
||||
</Typography.Text>
|
||||
);
|
||||
}
|
||||
@@ -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() {
|
||||
</Header>
|
||||
<div
|
||||
ref={contentRef}
|
||||
className="admin-layout"
|
||||
style={{ flex: 1, minHeight: 0, overflow: 'auto' }}
|
||||
>
|
||||
<Content style={{ margin: 24 }}>
|
||||
|
||||
@@ -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<HTMLElement>(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);
|
||||
}
|
||||
@@ -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) => (
|
||||
<div>
|
||||
<div>{r.hqName || '—'}</div>
|
||||
<div style={{ color: '#999', fontSize: 12 }}>
|
||||
{r.hqPhone || ''} {r.hqAccountId ? `(#${r.hqAccountId})` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<AdminCellLine
|
||||
primary={r.hqName}
|
||||
secondary={[r.hqPhone, r.hqAccountId ? `#${r.hqAccountId}` : ''].filter(Boolean).join(' ') || null}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -170,7 +170,10 @@ export default function HqLogsPage() {
|
||||
<Descriptions.Item label="日志 ID">{detail.id}</Descriptions.Item>
|
||||
<Descriptions.Item label="时间">{fmtTime(detail.createdAt)}</Descriptions.Item>
|
||||
<Descriptions.Item label="操作人">
|
||||
{detail.hqName} / {detail.hqPhone} (ID: {detail.hqAccountId})
|
||||
<AdminCellLine
|
||||
primary={detail.hqName}
|
||||
secondary={[detail.hqPhone, detail.hqAccountId ? `ID:${detail.hqAccountId}` : ''].filter(Boolean).join(' ') || null}
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="角色">{detail.hqRole || '—'}</Descriptions.Item>
|
||||
<Descriptions.Item label="行为">
|
||||
|
||||
@@ -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) => (
|
||||
<div>
|
||||
<div>{r.companyName || '—'}</div>
|
||||
<div style={{ color: '#999', fontSize: 12 }}>{r.partnerId}</div>
|
||||
</div>
|
||||
<AdminCellLine primary={r.companyName} secondary={r.partnerId} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
render: (_, r) => (
|
||||
<div>
|
||||
<div>{r.accountName || '—'}</div>
|
||||
<div style={{ color: '#999', fontSize: 12 }}>{r.accountPhone || r.partnerAccountId || '—'}</div>
|
||||
</div>
|
||||
<AdminCellLine
|
||||
primary={r.accountName}
|
||||
secondary={r.accountPhone || r.partnerAccountId}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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<Row> = [
|
||||
{ title: '时间', dataIndex: 'createdAt', width: 160, render: fmtTime },
|
||||
{
|
||||
title: '门店', width: 180,
|
||||
render: (_, r) => (
|
||||
<div>
|
||||
<div>{r.storeName || '—'}</div>
|
||||
<div style={{ color: '#999', fontSize: 12 }}>{r.storeId}</div>
|
||||
</div>
|
||||
),
|
||||
title: '门店', width: 180, ellipsis: true,
|
||||
render: (_, r) => <AdminCellLine primary={r.storeName} secondary={r.storeId} />,
|
||||
},
|
||||
{
|
||||
title: '账号', width: 150,
|
||||
title: '账号', width: 150, ellipsis: true,
|
||||
render: (_, r) => (
|
||||
<div>
|
||||
<div>{r.accountName || '—'}</div>
|
||||
<div style={{ color: '#999', fontSize: 12 }}>{r.accountPhone || r.storeAccountId || '系统/HQ'}</div>
|
||||
</div>
|
||||
<AdminCellLine
|
||||
primary={r.accountName}
|
||||
secondary={r.accountPhone || r.storeAccountId || '系统/HQ'}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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<Row> = [
|
||||
{ title: '时间', dataIndex: 'createdAt', width: 160, render: fmtTime },
|
||||
{
|
||||
title: '用户', width: 160,
|
||||
title: '用户', width: 180, ellipsis: true,
|
||||
render: (_, r) => (
|
||||
<div>
|
||||
<div>{r.nickname || r.userNo || '—'}</div>
|
||||
<div style={{ color: '#999', fontSize: 12 }}>{r.phone || r.userId || '—'}</div>
|
||||
</div>
|
||||
<AdminCellLine
|
||||
primary={r.nickname || r.userNo}
|
||||
secondary={r.phone || r.userId}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user