feat: 优化响应布局
parent
b06b7585d8
commit
c0b4c0d319
|
|
@ -39,7 +39,7 @@ function ListTable<T extends Record<string, any>>({
|
||||||
showSizeChanger: { showSearch: false },
|
showSizeChanger: { showSearch: false },
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
},
|
},
|
||||||
scroll = { x: "max-content" },
|
scroll = { x: "max(100%, 960px)" },
|
||||||
onRowClick,
|
onRowClick,
|
||||||
selectedRow,
|
selectedRow,
|
||||||
loading = false,
|
loading = false,
|
||||||
|
|
@ -49,7 +49,7 @@ function ListTable<T extends Record<string, any>>({
|
||||||
}: ListTableProps<T>) {
|
}: ListTableProps<T>) {
|
||||||
const mergedScroll = React.useMemo(() => {
|
const mergedScroll = React.useMemo(() => {
|
||||||
return {
|
return {
|
||||||
x: "max-content",
|
x: "max(100%, 960px)",
|
||||||
...scroll,
|
...scroll,
|
||||||
};
|
};
|
||||||
}, [scroll]);
|
}, [scroll]);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
.long-text-preview {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.long-text-preview-popover {
|
||||||
|
max-width: min(720px, 70vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
.long-text-preview-popover__content {
|
||||||
|
max-width: min(680px, 66vw);
|
||||||
|
max-height: 360px;
|
||||||
|
margin: 0 !important;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
user-select: text;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.long-text-preview-popover__content--code {
|
||||||
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Popover, Typography } from "antd";
|
||||||
|
import type { TextProps } from "antd/es/typography/Text";
|
||||||
|
import "./LongTextPreview.css";
|
||||||
|
|
||||||
|
const { Paragraph, Text } = Typography;
|
||||||
|
|
||||||
|
type LongTextPreviewProps = {
|
||||||
|
value?: string | number | null;
|
||||||
|
emptyText?: string;
|
||||||
|
code?: boolean;
|
||||||
|
copyable?: boolean;
|
||||||
|
className?: string;
|
||||||
|
textType?: TextProps["type"];
|
||||||
|
strong?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LongTextPreview({
|
||||||
|
value,
|
||||||
|
emptyText = "-",
|
||||||
|
code = false,
|
||||||
|
copyable = true,
|
||||||
|
className,
|
||||||
|
textType,
|
||||||
|
strong,
|
||||||
|
}: LongTextPreviewProps) {
|
||||||
|
const text = value === undefined || value === null || value === "" ? emptyText : String(value);
|
||||||
|
const contentClassName = [
|
||||||
|
"long-text-preview-popover__content",
|
||||||
|
code ? "long-text-preview-popover__content--code" : undefined,
|
||||||
|
].filter(Boolean).join(" ");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
overlayClassName="long-text-preview-popover"
|
||||||
|
placement="topLeft"
|
||||||
|
content={
|
||||||
|
<Paragraph className={contentClassName} copyable={copyable ? { text } : false}>
|
||||||
|
{text}
|
||||||
|
</Paragraph>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text code={code} type={textType} strong={strong} className={["long-text-preview", className].filter(Boolean).join(" ")}>
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -88,3 +88,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.permissions-route-text {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-route-text--component {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-name-cell {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-name-cell .ant-space-item:last-child {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-name-cell .long-text-preview {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import { usePermission } from "@/hooks/usePermission";
|
||||||
import PageContainer from "@/components/shared/PageContainer";
|
import PageContainer from "@/components/shared/PageContainer";
|
||||||
import DataListPanel from "@/components/shared/DataListPanel";
|
import DataListPanel from "@/components/shared/DataListPanel";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
|
import LongTextPreview from "@/components/shared/LongTextPreview";
|
||||||
import type { SysPermission } from "@/types";
|
import type { SysPermission } from "@/types";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
|
|
@ -453,18 +454,22 @@ export default function Permissions() {
|
||||||
title: t("permissions.permName"),
|
title: t("permissions.permName"),
|
||||||
dataIndex: "name",
|
dataIndex: "name",
|
||||||
key: "name",
|
key: "name",
|
||||||
|
width: 360,
|
||||||
|
ellipsis: { showTitle: false },
|
||||||
render: (text: string, record: TreePermission) => {
|
render: (text: string, record: TreePermission) => {
|
||||||
let icon = <CheckSquareOutlined style={{ color: "#52c41a" }} aria-hidden="true" />;
|
let icon = <CheckSquareOutlined style={{ color: "#52c41a" }} aria-hidden="true" />;
|
||||||
if (record.permType === "directory") icon = <FolderOutlined style={{ color: "#faad14" }} aria-hidden="true" />;
|
if (record.permType === "directory") icon = <FolderOutlined style={{ color: "#faad14" }} aria-hidden="true" />;
|
||||||
if (record.permType === "menu") icon = <MenuOutlined style={{ color: "#1890ff" }} aria-hidden="true" />;
|
if (record.permType === "menu") icon = <MenuOutlined style={{ color: "#1890ff" }} aria-hidden="true" />;
|
||||||
return <Space>{icon}<Text strong={record.level === 1}>{text}</Text></Space>;
|
return <Space className="permissions-name-cell">{icon}<LongTextPreview value={text} strong={record.level === 1} copyable={false} /></Space>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("permissions.permCode"),
|
title: t("permissions.permCode"),
|
||||||
dataIndex: "code",
|
dataIndex: "code",
|
||||||
key: "code",
|
key: "code",
|
||||||
render: (text: string) => text ? <Tag color="blue" className="tabular-nums">{text}</Tag> : "-"
|
width: 220,
|
||||||
|
ellipsis: { showTitle: false },
|
||||||
|
render: (text: string) => text ? <LongTextPreview value={text} code className="tabular-nums" /> : "-"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("permissions.permType"),
|
title: t("permissions.permType"),
|
||||||
|
|
@ -478,15 +483,16 @@ export default function Permissions() {
|
||||||
return <Tag color={color}>{item ? item.itemLabel : type}</Tag>;
|
return <Tag color={color}>{item ? item.itemLabel : type}</Tag>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ title: t("permissions.sort"), dataIndex: "sortOrder", width: 80, className: "tabular-nums" },
|
{ title: t("permissions.sort"), dataIndex: "sortOrder", width: 72, className: "tabular-nums" },
|
||||||
{
|
{
|
||||||
title: t("permissions.route"),
|
title: t("permissions.route"),
|
||||||
key: "route",
|
key: "route",
|
||||||
ellipsis: true,
|
width: 220,
|
||||||
|
ellipsis: { showTitle: false },
|
||||||
render: (_: unknown, record: TreePermission) => (
|
render: (_: unknown, record: TreePermission) => (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{record.path && <Text type="secondary" style={{ fontSize: "12px" }} className="tabular-nums">{record.path}</Text>}
|
{record.path && <LongTextPreview value={record.path} textType="secondary" className="tabular-nums permissions-route-text" />}
|
||||||
{record.component && <Text type="secondary" style={{ fontSize: "11px" }}>{record.component}</Text>}
|
{record.component && <LongTextPreview value={record.component} textType="secondary" className="permissions-route-text permissions-route-text--component" />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -510,7 +516,7 @@ export default function Permissions() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("common.action"),
|
title: t("common.action"),
|
||||||
width: 150,
|
width: 132,
|
||||||
fixed: "right" as const,
|
fixed: "right" as const,
|
||||||
render: (_: unknown, record: TreePermission) => (
|
render: (_: unknown, record: TreePermission) => (
|
||||||
<Space>
|
<Space>
|
||||||
|
|
@ -583,7 +589,8 @@ export default function Permissions() {
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
size="middle"
|
size="middle"
|
||||||
scroll={{ x: 'max-content', y: '100%' }}
|
scroll={{ x: 'max(100%, 1080px)', y: '100%' }}
|
||||||
|
tableLayout="fixed"
|
||||||
expandable={{
|
expandable={{
|
||||||
defaultExpandAllRows: false,
|
defaultExpandAllRows: false,
|
||||||
rowExpandable: (record) => record.permType !== "button" && !!record.children?.length,
|
rowExpandable: (record) => record.permType !== "button" && !!record.children?.length,
|
||||||
|
|
|
||||||
|
|
@ -590,7 +590,7 @@ const AiModels: React.FC = () => {
|
||||||
columns={resolvedTableColumns}
|
columns={resolvedTableColumns}
|
||||||
dataSource={data}
|
dataSource={data}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 960px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
|
|
|
||||||
|
|
@ -473,7 +473,7 @@ export default function ClientManagement() {
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={pagedRecords}
|
dataSource={pagedRecords}
|
||||||
loading={loading || groupLoading || platformLoading}
|
loading={loading || groupLoading || platformLoading}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 960px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ export default function ExternalAppManagement() {
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={pagedRecords}
|
dataSource={pagedRecords}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 1000px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
|
|
|
||||||
|
|
@ -559,7 +559,7 @@ const HotWords: React.FC = () => {
|
||||||
dataSource={data}
|
dataSource={data}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 1000px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import DataListPanel from "@/components/shared/DataListPanel";
|
||||||
import ListTable from "@/components/shared/ListTable/ListTable";
|
import ListTable from "@/components/shared/ListTable/ListTable";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
import SummaryStatCards from "@/components/shared/SummaryStatCards";
|
import SummaryStatCards from "@/components/shared/SummaryStatCards";
|
||||||
|
import LongTextPreview from "@/components/shared/LongTextPreview";
|
||||||
import { listLicenses, type LicenseVO } from "@/api/business/license";
|
import { listLicenses, type LicenseVO } from "@/api/business/license";
|
||||||
import "./LicenseManagement.css";
|
import "./LicenseManagement.css";
|
||||||
|
|
||||||
|
|
@ -105,9 +106,10 @@ export default function LicenseManagement() {
|
||||||
title: "授权标识",
|
title: "授权标识",
|
||||||
key: "license",
|
key: "license",
|
||||||
width: 320,
|
width: 320,
|
||||||
|
ellipsis: { showTitle: false },
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space direction="vertical" size={0}>
|
<Space direction="vertical" size={0} style={{ width: "100%" }}>
|
||||||
<Text strong className="tabular-nums">{record.licenseSerial}</Text>
|
<LongTextPreview value={record.licenseSerial} strong className="tabular-nums" />
|
||||||
{/*<Text type="secondary" className="tabular-nums">{record.licenseCode}</Text>*/}
|
{/*<Text type="secondary" className="tabular-nums">{record.licenseCode}</Text>*/}
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
|
|
@ -131,7 +133,8 @@ export default function LicenseManagement() {
|
||||||
dataIndex: "deviceCode",
|
dataIndex: "deviceCode",
|
||||||
key: "deviceCode",
|
key: "deviceCode",
|
||||||
width: 240,
|
width: 240,
|
||||||
render: (value) => <Text className="tabular-nums">{value || "-"}</Text>,
|
ellipsis: { showTitle: false },
|
||||||
|
render: (value) => <LongTextPreview value={value} className="tabular-nums" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "过期时间",
|
title: "过期时间",
|
||||||
|
|
@ -184,7 +187,7 @@ export default function LicenseManagement() {
|
||||||
dataSource={pagedRecords}
|
dataSource={pagedRecords}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 960px)", y: "100%" }}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
</SectionCard>
|
</SectionCard>
|
||||||
|
|
|
||||||
|
|
@ -456,7 +456,7 @@ export default function MeetingPointsManagement() {
|
||||||
columns={overviewColumns}
|
columns={overviewColumns}
|
||||||
dataSource={overviewRows}
|
dataSource={overviewRows}
|
||||||
loading={false}
|
loading={false}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 760px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
) : activeTabKey === "personal" ? (
|
) : activeTabKey === "personal" ? (
|
||||||
|
|
@ -466,7 +466,7 @@ export default function MeetingPointsManagement() {
|
||||||
columns={personalAccountColumns}
|
columns={personalAccountColumns}
|
||||||
dataSource={pagedPersonalAccounts}
|
dataSource={pagedPersonalAccounts}
|
||||||
loading={false}
|
loading={false}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 760px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -476,7 +476,7 @@ export default function MeetingPointsManagement() {
|
||||||
columns={ledgerColumns}
|
columns={ledgerColumns}
|
||||||
dataSource={records}
|
dataSource={records}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 960px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -923,7 +923,7 @@ const Meetings: React.FC = () => {
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
scroll={{ x: "max-content", y: "calc(100vh - 430px)" }}
|
scroll={{ x: "max(100%, 1180px)", y: "calc(100vh - 430px)" }}
|
||||||
onRow={(record) => ({ onClick: () => handleOpenMeeting(record) })}
|
onRow={(record) => ({ onClick: () => handleOpenMeeting(record) })}
|
||||||
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="开启您的第一场会议分析" /> }}
|
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="开启您的第一场会议分析" /> }}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -457,7 +457,7 @@ const PromptTemplates: React.FC = () => {
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 1400px)", y: "100%" }}
|
||||||
onRow={(record) => ({ onClick: () => showDetail(record) })}
|
onRow={(record) => ({ onClick: () => showDetail(record) })}
|
||||||
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无可用模板" /> }}
|
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无可用模板" /> }}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -798,7 +798,7 @@ export default function ScreenSaverManagement() {
|
||||||
locale={{
|
locale={{
|
||||||
emptyText: <Empty description="暂无屏保素材" />,
|
emptyText: <Empty description="暂无屏保素材" />,
|
||||||
}}
|
}}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 1080px)", y: "100%" }}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
</SectionCard>
|
</SectionCard>
|
||||||
|
|
|
||||||
|
|
@ -708,7 +708,7 @@ const SpeakerReg: React.FC = () => {
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={filteredSpeakers}
|
dataSource={filteredSpeakers}
|
||||||
loading={listLoading}
|
loading={listLoading}
|
||||||
scroll={{ x: "max-content", y: "100%" }}
|
scroll={{ x: "max(100%, 960px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -393,7 +393,7 @@ export default function TenantMeetingPointsSettings() {
|
||||||
dataSource={records}
|
dataSource={records}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
totalCount={total}
|
totalCount={total}
|
||||||
scroll={{x: "max-content", y: "100%"}}
|
scroll={{x: "max(100%, 1280px)", y: "100%"}}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { useDict } from "@/hooks/useDict";
|
||||||
import { usePermission } from "@/hooks/usePermission";
|
import { usePermission } from "@/hooks/usePermission";
|
||||||
import PageContainer from "@/components/shared/PageContainer";
|
import PageContainer from "@/components/shared/PageContainer";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
|
import LongTextPreview from "@/components/shared/LongTextPreview";
|
||||||
import type { OrgNode, SysOrg, SysTenant } from "@/types";
|
import type { OrgNode, SysOrg, SysTenant } from "@/types";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
|
|
@ -130,8 +131,8 @@ export default function Orgs() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: t("orgs.orgName"), dataIndex: "orgName", key: "orgName", render: (text: string) => <Text strong>{text}</Text> },
|
{ title: t("orgs.orgName"), dataIndex: "orgName", key: "orgName", width: 320, ellipsis: { showTitle: false }, render: (text: string) => <LongTextPreview value={text} strong /> },
|
||||||
{ title: t("orgs.orgCode"), dataIndex: "orgCode", key: "orgCode", width: 150, render: (text: string) => <Tag className="tabular-nums">{text || "-"}</Tag> },
|
{ title: t("orgs.orgCode"), dataIndex: "orgCode", key: "orgCode", width: 180, ellipsis: { showTitle: false }, render: (text: string) => <LongTextPreview value={text} code className="tabular-nums" /> },
|
||||||
{ title: t("orgs.sort"), dataIndex: "sortOrder", width: 100, className: "tabular-nums" },
|
{ title: t("orgs.sort"), dataIndex: "sortOrder", width: 100, className: "tabular-nums" },
|
||||||
{
|
{
|
||||||
title: t("common.status"),
|
title: t("common.status"),
|
||||||
|
|
@ -188,7 +189,7 @@ export default function Orgs() {
|
||||||
</div>
|
</div>
|
||||||
<div className="orgs-table-shell">
|
<div className="orgs-table-shell">
|
||||||
{selectedTenantId !== undefined ? (
|
{selectedTenantId !== undefined ? (
|
||||||
<Table rowKey="id" columns={columns} dataSource={treeData} loading={loading} pagination={false} size="middle" scroll={{ y: "100%" }} expandable={{ defaultExpandAllRows: true }} />
|
<Table rowKey="id" columns={columns} dataSource={treeData} loading={loading} pagination={false} size="middle" scroll={{ x: "max(100%, 920px)", y: "100%" }} tableLayout="fixed" expandable={{ defaultExpandAllRows: true }} />
|
||||||
) : (
|
) : (
|
||||||
<div className="app-page__empty-state"><Empty description={t("orgs.selectTenant")} /></div>
|
<div className="app-page__empty-state"><Empty description={t("orgs.selectTenant")} /></div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { useDict } from "@/hooks/useDict";
|
||||||
import { usePermission } from "@/hooks/usePermission";
|
import { usePermission } from "@/hooks/usePermission";
|
||||||
import PageContainer from "@/components/shared/PageContainer";
|
import PageContainer from "@/components/shared/PageContainer";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
|
import LongTextPreview from "@/components/shared/LongTextPreview";
|
||||||
import { getStandardPagination } from "@/utils/pagination";
|
import { getStandardPagination } from "@/utils/pagination";
|
||||||
import type { SysDictItem, SysDictType } from "@/types";
|
import type { SysDictItem, SysDictType } from "@/types";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
@ -243,10 +244,11 @@ export default function Dictionaries() {
|
||||||
dataSource={items}
|
dataSource={items}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
size="middle"
|
size="middle"
|
||||||
scroll={{ y: "calc(100vh - 320px)" }}
|
scroll={{ x: "max(100%, 760px)", y: "calc(100vh - 320px)" }}
|
||||||
|
tableLayout="fixed"
|
||||||
columns={[
|
columns={[
|
||||||
{ title: t("dicts.itemLabel"), dataIndex: "itemLabel", render: (text: string) => <Text strong>{text}</Text> },
|
{ title: t("dicts.itemLabel"), dataIndex: "itemLabel", width: 220, ellipsis: { showTitle: false }, render: (text: string) => <LongTextPreview value={text} strong /> },
|
||||||
{ title: t("dicts.itemValue"), dataIndex: "itemValue", className: "tabular-nums" },
|
{ title: t("dicts.itemValue"), dataIndex: "itemValue", width: 220, ellipsis: { showTitle: false }, render: (text: string) => <LongTextPreview value={text} code className="tabular-nums" /> },
|
||||||
{ title: t("dicts.sort"), dataIndex: "sortOrder", width: 80, className: "tabular-nums" },
|
{ title: t("dicts.sort"), dataIndex: "sortOrder", width: 80, className: "tabular-nums" },
|
||||||
{
|
{
|
||||||
title: t("common.status"),
|
title: t("common.status"),
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import DataListPanel from "@/components/shared/DataListPanel";
|
||||||
import ListTable from "@/components/shared/ListTable/ListTable";
|
import ListTable from "@/components/shared/ListTable/ListTable";
|
||||||
import AppPagination from "@/components/shared/AppPagination";
|
import AppPagination from "@/components/shared/AppPagination";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
|
import LongTextPreview from "@/components/shared/LongTextPreview";
|
||||||
import { getDefaultPageSize } from "@/utils/pagination";
|
import { getDefaultPageSize } from "@/utils/pagination";
|
||||||
import type { SysLog, SysTenant, UserProfile } from "@/types";
|
import type { SysLog, SysTenant, UserProfile } from "@/types";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
@ -19,7 +20,7 @@ const DEFAULT_TABLE_PAGE_SIZE = getDefaultPageSize("table");
|
||||||
|
|
||||||
export default function Logs() {
|
export default function Logs() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [activeTab, setActiveTab] = useState("OPERATION");
|
const [activeTab, setActiveTab] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [data, setData] = useState<SysLog[]>([]);
|
const [data, setData] = useState<SysLog[]>([]);
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
|
|
@ -42,7 +43,7 @@ export default function Logs() {
|
||||||
sortField: "createdAt",
|
sortField: "createdAt",
|
||||||
sortOrder: "descend" as any
|
sortOrder: "descend" as any
|
||||||
});
|
});
|
||||||
const { items: logTypeDict } = useDict("sys_log_type");
|
const { items: logTypeDict, loading: logTypeLoading } = useDict("sys_log_type");
|
||||||
const { items: logStatusDict } = useDict("sys_log_status");
|
const { items: logStatusDict } = useDict("sys_log_status");
|
||||||
|
|
||||||
const userProfile = useMemo(() => {
|
const userProfile = useMemo(() => {
|
||||||
|
|
@ -60,21 +61,36 @@ export default function Logs() {
|
||||||
if (!isPlatformAdmin || !params.tenantId) return t("logsExt.allTenants");
|
if (!isPlatformAdmin || !params.tenantId) return t("logsExt.allTenants");
|
||||||
return tenants.find((tenant) => tenant.id === params.tenantId)?.tenantName || t("logsExt.tenantId", { id: params.tenantId });
|
return tenants.find((tenant) => tenant.id === params.tenantId)?.tenantName || t("logsExt.tenantId", { id: params.tenantId });
|
||||||
}, [isPlatformAdmin, params.tenantId, tenants, t]);
|
}, [isPlatformAdmin, params.tenantId, tenants, t]);
|
||||||
const activeLogTypeLabel = useMemo(() => {
|
const tabItems = useMemo(() => {
|
||||||
const dictLabel = logTypeDict.find((item) => item.itemValue === activeTab)?.itemLabel;
|
if (logTypeDict.length > 0) {
|
||||||
if (dictLabel) return dictLabel;
|
return logTypeDict.map((item) => ({
|
||||||
return activeTab === "OPERATION" ? t("logs.opLog") : t("logs.loginLog");
|
key: item.itemValue,
|
||||||
}, [activeTab, logTypeDict, t]);
|
label: item.itemLabel,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
const tabItems = logTypeDict.length > 0
|
if (logTypeLoading) {
|
||||||
? logTypeDict.map((item) => ({
|
return [];
|
||||||
key: item.itemValue,
|
}
|
||||||
label: item.itemLabel,
|
|
||||||
}))
|
return [
|
||||||
: [
|
|
||||||
{ key: "OPERATION", label: t("logs.opLog") },
|
{ key: "OPERATION", label: t("logs.opLog") },
|
||||||
{ key: "LOGIN", label: t("logs.loginLog") },
|
{ key: "LOGIN", label: t("logs.loginLog") },
|
||||||
];
|
];
|
||||||
|
}, [logTypeDict, logTypeLoading, t]);
|
||||||
|
|
||||||
|
const activeLogTypeLabel = useMemo(() => {
|
||||||
|
const activeItem = tabItems.find((item) => item.key === activeTab) || tabItems[0];
|
||||||
|
if (activeItem?.label) return activeItem.label;
|
||||||
|
return activeTab === "LOGIN" ? t("logs.loginLog") : t("logs.opLog");
|
||||||
|
}, [activeTab, tabItems, t]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!tabItems.length) return;
|
||||||
|
if (!activeTab || !tabItems.some((item) => item.key === activeTab)) {
|
||||||
|
setActiveTab(tabItems[0].key);
|
||||||
|
}
|
||||||
|
}, [activeTab, tabItems]);
|
||||||
|
|
||||||
const cleanAction = (
|
const cleanAction = (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
|
|
@ -85,13 +101,14 @@ export default function Logs() {
|
||||||
okButtonProps={{ danger: true, loading: cleaning }}
|
okButtonProps={{ danger: true, loading: cleaning }}
|
||||||
onConfirm={() => void handleClean()}
|
onConfirm={() => void handleClean()}
|
||||||
>
|
>
|
||||||
<Button danger className="logs-clean-action" icon={<DeleteOutlined aria-hidden="true" />} loading={cleaning}>
|
<Button danger className="logs-clean-action" icon={<DeleteOutlined aria-hidden="true" />} loading={cleaning} disabled={!activeTab}>
|
||||||
{t("logsExt.cleanCurrent", { type: activeLogTypeLabel })}
|
{t("logsExt.cleanCurrent", { type: activeLogTypeLabel })}
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadData = async (currentParams = params) => {
|
const loadData = async (currentParams = params) => {
|
||||||
|
if (!activeTab) return;
|
||||||
const requestSeq = requestSeqRef.current + 1;
|
const requestSeq = requestSeqRef.current + 1;
|
||||||
requestSeqRef.current = requestSeq;
|
requestSeqRef.current = requestSeq;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -172,6 +189,7 @@ export default function Logs() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClean = async () => {
|
const handleClean = async () => {
|
||||||
|
if (!activeTab) return;
|
||||||
setCleaning(true);
|
setCleaning(true);
|
||||||
try {
|
try {
|
||||||
await cleanLogs(activeTab, isPlatformAdmin ? params.tenantId : undefined);
|
await cleanLogs(activeTab, isPlatformAdmin ? params.tenantId : undefined);
|
||||||
|
|
@ -207,8 +225,9 @@ export default function Logs() {
|
||||||
title: activeTab === "OPERATION" ? t("logsExt.actionLabel") : t("logs.opDetail"),
|
title: activeTab === "OPERATION" ? t("logsExt.actionLabel") : t("logs.opDetail"),
|
||||||
dataIndex: activeTab === "OPERATION" ? "actionName" : "operation",
|
dataIndex: activeTab === "OPERATION" ? "actionName" : "operation",
|
||||||
key: activeTab === "OPERATION" ? "actionName" : "operation",
|
key: activeTab === "OPERATION" ? "actionName" : "operation",
|
||||||
ellipsis: true,
|
width: activeTab === "OPERATION" ? 360 : 300,
|
||||||
render: (_: string, record: SysLog) => <Text type="secondary">{record.actionName || record.operation}</Text>
|
ellipsis: { showTitle: false },
|
||||||
|
render: (_: string, record: SysLog) => <LongTextPreview value={record.actionName || record.operation} textType="secondary" />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("logs.ip"),
|
title: t("logs.ip"),
|
||||||
|
|
@ -393,7 +412,7 @@ export default function Logs() {
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
totalCount={total}
|
totalCount={total}
|
||||||
scroll={{ y: "100%" }}
|
scroll={{ x: "max(100%, 1080px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import DataListPanel from "@/components/shared/DataListPanel";
|
||||||
import ListTable from "@/components/shared/ListTable/ListTable";
|
import ListTable from "@/components/shared/ListTable/ListTable";
|
||||||
import AppPagination from "@/components/shared/AppPagination";
|
import AppPagination from "@/components/shared/AppPagination";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
|
import LongTextPreview from "@/components/shared/LongTextPreview";
|
||||||
import type { SysParamQuery, SysParamVO } from "@/types";
|
import type { SysParamQuery, SysParamVO } from "@/types";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
|
|
@ -160,6 +161,7 @@ export default function SysParams() {
|
||||||
title: t("sysParams.paramKey"),
|
title: t("sysParams.paramKey"),
|
||||||
dataIndex: "paramKey",
|
dataIndex: "paramKey",
|
||||||
key: "paramKey",
|
key: "paramKey",
|
||||||
|
width: 320,
|
||||||
render: (text: string, record: SysParamVO) => (
|
render: (text: string, record: SysParamVO) => (
|
||||||
<Space direction="vertical" size={0}>
|
<Space direction="vertical" size={0}>
|
||||||
<Text className="param-key-text">{text}</Text>
|
<Text className="param-key-text">{text}</Text>
|
||||||
|
|
@ -172,6 +174,7 @@ export default function SysParams() {
|
||||||
dataIndex: "paramValue",
|
dataIndex: "paramValue",
|
||||||
key: "paramValue",
|
key: "paramValue",
|
||||||
width: 360,
|
width: 360,
|
||||||
|
ellipsis: { showTitle: false },
|
||||||
render: (text: string, record: SysParamVO) => {
|
render: (text: string, record: SysParamVO) => {
|
||||||
const type = record.paramType;
|
const type = record.paramType;
|
||||||
// Boolean: 彩色标签显示 是/否
|
// Boolean: 彩色标签显示 是/否
|
||||||
|
|
@ -181,40 +184,10 @@ export default function SysParams() {
|
||||||
}
|
}
|
||||||
// JSON: 等宽字体,以纯字符串显示
|
// JSON: 等宽字体,以纯字符串显示
|
||||||
if (isType(type, "JSON")) {
|
if (isType(type, "JSON")) {
|
||||||
return (
|
return <LongTextPreview value={text} code />;
|
||||||
<Tooltip title={text}>
|
|
||||||
<Text
|
|
||||||
code
|
|
||||||
style={{
|
|
||||||
display: "block",
|
|
||||||
maxWidth: "100%",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
whiteSpace: "nowrap"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{text}
|
|
||||||
</Text>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// Number / String: 等宽代码文本 + Tooltip
|
// 数字和字符串:等宽代码文本,悬浮查看完整内容。
|
||||||
return (
|
return <LongTextPreview value={text} code />;
|
||||||
<Tooltip title={text}>
|
|
||||||
<Text
|
|
||||||
code
|
|
||||||
style={{
|
|
||||||
display: "block",
|
|
||||||
maxWidth: "100%",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
whiteSpace: "nowrap"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{text}
|
|
||||||
</Text>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -224,7 +197,7 @@ export default function SysParams() {
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (type: string) => <Tag className="param-type-tag">{type || t("sysParamsExt.defaultType")}</Tag>
|
render: (type: string) => <Tag className="param-type-tag">{type || t("sysParamsExt.defaultType")}</Tag>
|
||||||
},
|
},
|
||||||
{ title: t("sysParams.description"), dataIndex: "description", key: "description", ellipsis: true },
|
{ title: t("sysParams.description"), dataIndex: "description", key: "description", width: 360, ellipsis: true },
|
||||||
{
|
{
|
||||||
title: t("common.status"),
|
title: t("common.status"),
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
|
|
@ -298,7 +271,7 @@ export default function SysParams() {
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={data}
|
dataSource={data}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
scroll={{ y: "100%" }}
|
scroll={{ x: "max(100%, 1350px)", y: "100%" }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</DataListPanel>
|
</DataListPanel>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue