@chenchaolong/plugin-trade-compliance-workbench 1.0.42 → 1.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +4 -4
- package/dist/lib/remote-ui/pages/analytics.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/analytics.js +44 -156
- package/dist/lib/remote-ui/pages/analytics.js.map +1 -1
- package/dist/lib/remote-ui/styles.css +56 -30
- package/dist/lib/workbench.service.js +61 -24
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../../src/lib/remote-ui/pages/analytics.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../../src/lib/remote-ui/pages/analytics.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiC,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAsB,sBAAsB,EAAmC,oBAAoB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAErM,OAAO,2BAA2B,CAAA;AAElC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,aAAa,CAAA;IACnB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACzE,CAAA;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,kBAAkB,GAAG,cAAc,CAI1G;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,kBAAkB,2CAgE7G;AAqXD,wBAAgB,uBAAuB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,2CAiB3E;AA8ID,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,SAAS,oBAAoB,EAAE,GAAG,kBAAkB,EAAE,CAYtG"}
|
|
@@ -1,50 +1,41 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Decimal } from 'decimal.js';
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { DataGrid,
|
|
4
|
+
import { DataGrid, RiskNotice } from '../components/data-grid.js';
|
|
5
5
|
import './analytics-prototype.css';
|
|
6
6
|
export function analyticsPageSizeQuery(query, pageSize) {
|
|
7
7
|
return query.mode === 'PURCHASE'
|
|
8
8
|
? { ...query, page: 1, pageSize }
|
|
9
9
|
: { ...query, page: 1, pageSize };
|
|
10
10
|
}
|
|
11
|
-
const purchaseFilters = [
|
|
12
|
-
{ type: 'date-range', key: 'orderDate', label: '下单日期' },
|
|
13
|
-
{ type: 'text', key: 'supplier', label: '供应商' },
|
|
14
|
-
{ type: 'text', key: 'productName', label: '商品名称' },
|
|
15
|
-
{ type: 'text', key: 'hsCode', label: '海关编码' }
|
|
16
|
-
];
|
|
17
|
-
const salesFilters = [
|
|
18
|
-
{ type: 'date-range', key: 'orderDate', label: '下单日期' },
|
|
19
|
-
{ type: 'text', key: 'customer', label: '客户' },
|
|
20
|
-
{ type: 'text', key: 'productName', label: '商品名称' },
|
|
21
|
-
{ type: 'text', key: 'hsCode', label: '海关编码' }
|
|
22
|
-
];
|
|
23
11
|
export function AnalyticsPage({ data, committedQuery, busy, error, onQuery, onExportXlsx }) {
|
|
24
12
|
const fallbackQuery = React.useMemo(() => queryFromData(data), [data.mode, data.dimension, data.details.page, data.details.pageSize]);
|
|
25
13
|
const committed = committedQuery ?? fallbackQuery;
|
|
26
14
|
const [mode, setMode] = React.useState(committed.mode);
|
|
27
15
|
const [dimension, setDimension] = React.useState(committed.dimension);
|
|
28
16
|
const [filters, setFilters] = React.useState(() => filterStateFromQuery(committed));
|
|
17
|
+
const [period, setPeriod] = React.useState(() => committed.filters.orderDate ? 'custom' : 'year');
|
|
29
18
|
const [activeDialog, setActiveDialog] = React.useState(null);
|
|
30
19
|
const [drilldown, setDrilldown] = React.useState(null);
|
|
31
|
-
const [
|
|
20
|
+
const [infoModal, setInfoModal] = React.useState(null);
|
|
32
21
|
React.useEffect(() => {
|
|
33
22
|
setMode(committed.mode);
|
|
34
23
|
setDimension(committed.dimension);
|
|
35
24
|
setFilters(filterStateFromQuery(committed));
|
|
25
|
+
setPeriod(committed.filters.orderDate ? 'custom' : 'year');
|
|
36
26
|
}, [committed]);
|
|
37
|
-
const dimensions = mode === 'PURCHASE' ? [['SUPPLIER', '供应商'], ['PRODUCT', '商品名称']] : [['CUSTOMER', '客户'], ['PRODUCT', '商品名称']];
|
|
38
27
|
const typedFilters = toAnalyticsFilters(filters);
|
|
39
28
|
const queryFor = (nextMode, nextDimension, page, nextFilters) => nextMode === 'PURCHASE'
|
|
40
29
|
? { mode: nextMode, dimension: nextDimension === 'SUPPLIER' ? 'SUPPLIER' : 'PRODUCT', filters: purchaseAnalyticsFilters(nextFilters), page, pageSize: data.details.pageSize || 20 }
|
|
41
30
|
: { mode: nextMode, dimension: nextDimension === 'CUSTOMER' ? 'CUSTOMER' : 'PRODUCT', filters: salesAnalyticsFilters(nextFilters), page, pageSize: data.details.pageSize || 20 };
|
|
42
31
|
const runQuery = (page = 1, nextMode = mode, nextDimension = dimension) => onQuery(queryFor(nextMode, nextDimension, page, typedFilters));
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
32
|
+
const applyPeriod = (nextPeriod) => {
|
|
33
|
+
const orderDate = nextPeriod === 'year' ? yearRange(data.currentYear) : quickRange(3);
|
|
34
|
+
const nextFilters = { ...typedFilters, orderDate };
|
|
35
|
+
setPeriod(nextPeriod);
|
|
36
|
+
setFilters(current => ({ ...current, orderDate }));
|
|
37
|
+
void onQuery(queryFor(mode, dimension, 1, nextFilters));
|
|
46
38
|
};
|
|
47
|
-
const switchDimension = (nextDimension) => { void runQuery(1, mode, nextDimension); };
|
|
48
39
|
const exportCurrent = (scope) => {
|
|
49
40
|
if (!onExportXlsx)
|
|
50
41
|
return;
|
|
@@ -52,7 +43,7 @@ export function AnalyticsPage({ data, committedQuery, busy, error, onQuery, onEx
|
|
|
52
43
|
void onExportXlsx(request.mode === 'PURCHASE' ? { mode: request.mode, dimension: request.dimension, filters: request.filters, format: 'XLSX' } : { mode: request.mode, dimension: request.dimension, filters: request.filters, format: 'XLSX' });
|
|
53
44
|
setActiveDialog(null);
|
|
54
45
|
};
|
|
55
|
-
return _jsxs("section", { className: "tcw-analytics-page", "aria-label": "\u6570\u636E\u5206\u6790", children: [_jsxs("div", { className: "tcw-analytics-cockpit-hero", children: [_jsxs("div", { children: [_jsx("h1", { children: "\u5916\u8D38\u5408\u89C4\u7ECF\u8425\u9A7E\u9A76\u8231" }), _jsx("p", { children: "\u7ECF\u8425\u8D44\u91D1\u3001\u5408\u540C\u5BA1\u6838\u3001\u5546\u54C1\u7BA1\u63A7\u3001\u5236\u88C1\u547D\u4E2D\u3001\u5229\u6DA6\u5F02\u5E38\u7EDF\u4E00\u76D1\u63A7" })] }), _jsxs("div", { className: "tcw-analytics-page-actions", children: [onExportXlsx && _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", "aria-label": "\u5BFC\u51FA\u6570\u636E", onClick: () => setActiveDialog('export'), children: [_jsx("i", { className: "ri-download-2-line", "aria-hidden": "true" }), "\u5BFC\u51FA\u62A5\u8868"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: () => void runQuery(), children: [_jsx("i", { className: "ri-refresh-line", "aria-hidden": "true" }), "\u5237\u65B0"] })] })] }),
|
|
46
|
+
return _jsxs("section", { className: "tcw-analytics-page", "aria-label": "\u6570\u636E\u5206\u6790", children: [_jsxs("div", { className: "tcw-analytics-cockpit-hero", children: [_jsxs("div", { children: [_jsx("h1", { children: "\u5916\u8D38\u5408\u89C4\u7ECF\u8425\u9A7E\u9A76\u8231" }), _jsx("p", { children: "\u7ECF\u8425\u8D44\u91D1\u3001\u5408\u540C\u5BA1\u6838\u3001\u5546\u54C1\u7BA1\u63A7\u3001\u5236\u88C1\u547D\u4E2D\u3001\u5229\u6DA6\u5F02\u5E38\u7EDF\u4E00\u76D1\u63A7" })] }), _jsxs("div", { className: "tcw-analytics-page-actions", children: [onExportXlsx && _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", "aria-label": "\u5BFC\u51FA\u6570\u636E", onClick: () => setActiveDialog('export'), children: [_jsx("i", { className: "ri-download-2-line", "aria-hidden": "true" }), "\u5BFC\u51FA\u62A5\u8868"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: () => void runQuery(), children: [_jsx("i", { className: "ri-refresh-line", "aria-hidden": "true" }), "\u5237\u65B0"] })] })] }), _jsx("div", { className: "tcw-analytics-query", children: _jsxs("div", { className: "tcw-analytics-toolbar", children: [_jsx("span", { className: "tcw-analytics-label", children: "\u7EDF\u8BA1\u5468\u671F\uFF1A" }), _jsxs("div", { className: "tcw-segmented-control tcw-analytics-period", role: "group", "aria-label": "\u7EDF\u8BA1\u65F6\u95F4", children: [_jsxs("button", { type: "button", "aria-pressed": period === 'year', onClick: () => applyPeriod('year'), children: [_jsx("i", { className: "ri-calendar-event-line", "aria-hidden": "true" }), "\u672C\u5E74"] }), _jsxs("button", { type: "button", "aria-pressed": period === 'month', onClick: () => applyPeriod('month'), children: [_jsx("i", { className: "ri-calendar-check-line", "aria-hidden": "true" }), "\u672C\u6708"] }), _jsxs("button", { type: "button", "aria-pressed": period === 'custom', onClick: () => { setPeriod('custom'); setActiveDialog('filter'); }, children: [_jsx("i", { className: "ri-calendar-2-line", "aria-hidden": "true" }), "\u81EA\u5B9A\u4E49\u533A\u95F4"] })] })] }) }), _jsx(AnalyticsCockpit, { data: data, onDrilldown: setDrilldown, onInfo: setInfoModal }), error && _jsx(RiskNotice, { children: error }), busy && _jsxs("div", { className: "tcw-inline-loading", role: "status", children: [_jsx("span", { className: "tcw-spinner", "aria-hidden": "true" }), "\u6570\u636E\u52A0\u8F7D\u4E2D"] }), drilldown && _jsx(AnalyticsDrilldownPanel, { data: data, drilldown: drilldown, onClose: () => setDrilldown(null) }), infoModal && _jsx(AnalyticsInfoModal, { info: infoModal, onClose: () => setInfoModal(null) }), _jsx(AdvancedFilterModal, { active: activeDialog === 'filter', filters: filters, onFilterChange: (key, value) => setFilters(current => ({ ...current, [key]: value })), onApply: () => { setPeriod('custom'); void runQuery(); setActiveDialog(null); }, onReset: () => { setPeriod('year'); setFilters({ orderDate: yearRange(data.currentYear) }); void onQuery(queryFor(mode, dimension, 1, { orderDate: yearRange(data.currentYear) })); setActiveDialog(null); }, onClose: () => setActiveDialog(null) }), onExportXlsx && _jsx(BusinessExportModal, { active: activeDialog === 'export', total: data.details.total, onClose: () => setActiveDialog(null), onExport: exportCurrent })] });
|
|
56
47
|
}
|
|
57
48
|
const analyticsExportFields = [
|
|
58
49
|
{ key: 'label', label: '客户/供应商名称' },
|
|
@@ -62,13 +53,13 @@ const analyticsExportFields = [
|
|
|
62
53
|
{ key: 'status', label: '审核状态' },
|
|
63
54
|
{ key: 'createdAt', label: '创建时间' }
|
|
64
55
|
];
|
|
65
|
-
function AnalyticsCockpit({ data, onDrilldown }) {
|
|
56
|
+
function AnalyticsCockpit({ data, onDrilldown, onInfo }) {
|
|
66
57
|
const dashboard = data.dashboard;
|
|
67
|
-
const customerValueRows =
|
|
68
|
-
const receivableRiskRows =
|
|
69
|
-
const supplierRiskRows =
|
|
70
|
-
const salesFileRows =
|
|
71
|
-
const riskOrderRows =
|
|
58
|
+
const customerValueRows = dashboard.customerValueTop5.map(item => ({ key: item.customerId, name: item.customerName, salesAmountCny: item.salesAmountCny, grossProfitRate: item.estimatedGrossProfitRate, unreceivedCny: item.unreceivedCny, riskStatus: item.riskStatus }));
|
|
59
|
+
const receivableRiskRows = dashboard.receivableRiskTop5.map(item => ({ key: item.customerId, name: item.customerName, unreceivedCny: item.unreceivedCny, orderCount: item.orderCount, latestOrderDate: item.latestOrderDate }));
|
|
60
|
+
const supplierRiskRows = dashboard.supplierRiskTop5.map(item => ({ key: item.supplierId, name: item.supplierName, unpaidCny: item.unpaidCny, sanctionHitCount: item.sanctionHitCount, productRiskHitCount: item.productRiskHitCount, purchaseOrderCount: item.purchaseOrderCount }));
|
|
61
|
+
const salesFileRows = dashboard.salesFileReviews.map(item => ({ key: item.fileId, fileName: item.fileName, fileType: item.fileType, orderNo: item.salesOrderNo, reason: item.reason, status: '待复核' }));
|
|
62
|
+
const riskOrderRows = dashboard.riskOrders.map(item => ({ key: item.orderKey, orderNo: item.orderNo, partyName: item.partyName, riskType: item.riskType, riskAmountCny: item.riskAmountCny, status: item.status }));
|
|
72
63
|
const monthlyDeltas = monthlyDeltaLabels(dashboard.monthlyTrend);
|
|
73
64
|
const kpis = [
|
|
74
65
|
{ label: '采购总额', value: compactMoney(dashboard.kpis.purchaseTotalCny), subtext: monthlyDeltas.purchase, fullValue: `${money(dashboard.kpis.purchaseTotalCny)} CNY`, icon: 'ri-shopping-cart-line', tone: 'money' },
|
|
@@ -82,13 +73,13 @@ function AnalyticsCockpit({ data, onDrilldown }) {
|
|
|
82
73
|
{ label: '合规待办数', value: `${dashboard.kpis.complianceTodoCount} 项`, subtext: `队列 ${dashboard.todos.length} 类`, icon: 'ri-shield-check-line', tone: 'warn' },
|
|
83
74
|
{ label: '风险敞口金额', value: compactMoney(dashboard.kpis.riskExposureCny), subtext: `资金风险占比 ${amountRatio(dashboard.kpis.riskExposureCny, addMoney(dashboard.kpis.salesTotalCny, dashboard.kpis.purchaseTotalCny))}`, fullValue: `${money(dashboard.kpis.riskExposureCny)} CNY`, icon: 'ri-shield-flash-line', tone: 'risk' }
|
|
84
75
|
];
|
|
85
|
-
return _jsxs("section", { className: "tcw-analytics-cockpit", "aria-label": "\u5916\u8D38\u5408\u89C4\u7ECF\u8425\u9A7E\u9A76\u8231", children: [_jsx("div", { className: "tcw-analytics-kpi-grid", children: kpis.map(item => _jsxs("button", { type: "button", className: `is-${item.tone}`, title: 'fullValue' in item ? item.fullValue : item.value, onClick: () => onDrilldown({ title: `${item.label}下钻明细`, tone: item.tone === 'success' || item.tone === 'warn' ? 'money' : item.tone }), children: [_jsx("span", { children: item.label }), _jsx("i", { className: item.icon, "aria-hidden": "true" }), _jsx("strong", { children: item.value }), _jsx("small", { children: item.subtext.startsWith('较上月') ? _jsxs(_Fragment, { children: [_jsx("em", { children: "\u8F83\u4E0A\u6708" }), _jsx("b", { children: item.subtext.replace('较上月 ', '') })] }) : item.subtext })] }, item.label)) }), _jsxs("div", { className: "tcw-analytics-cockpit-layout", children: [_jsx(Panel, { title: "\u5408\u89C4\u5F85\u529E\u961F\u5217", icon: "ri-list-check-3", className: "tcw-analytics-panel--queue", action: _jsx(PanelLink, {
|
|
76
|
+
return _jsxs("section", { className: "tcw-analytics-cockpit", "aria-label": "\u5916\u8D38\u5408\u89C4\u7ECF\u8425\u9A7E\u9A76\u8231", children: [_jsx("div", { className: "tcw-analytics-kpi-grid", children: kpis.map(item => _jsxs("button", { type: "button", className: `is-${item.tone}`, title: 'fullValue' in item ? item.fullValue : item.value, onClick: () => onDrilldown({ title: `${item.label}下钻明细`, tone: item.tone === 'success' || item.tone === 'warn' ? 'money' : item.tone }), children: [_jsx("span", { children: item.label }), _jsx("i", { className: item.icon, "aria-hidden": "true" }), _jsx("strong", { children: item.value }), _jsx("small", { children: item.subtext.startsWith('较上月') ? _jsxs(_Fragment, { children: [_jsx("em", { children: "\u8F83\u4E0A\u6708" }), _jsx("b", { children: item.subtext.replace('较上月 ', '') })] }) : item.subtext })] }, item.label)) }), _jsxs("div", { className: "tcw-analytics-cockpit-layout", children: [_jsx(Panel, { title: "\u5408\u89C4\u5F85\u529E\u961F\u5217", icon: "ri-list-check-3", className: "tcw-analytics-panel--queue", action: _jsx(PanelLink, { label: "\u67E5\u770B\u5168\u90E8", onClick: () => onInfo({ title: '合规待办明细', rows: dashboard.todos.map(todo => ({ label: todo.label, value: `${todo.count} 项 · ${todo.description}`, actionLabel: '处理' })) }) }), children: _jsx(TodoQueue, { rows: dashboard.todos }) }), _jsxs(Panel, { title: "\u6708\u5EA6\u7ECF\u8425\u8D8B\u52BF", icon: "ri-bar-chart-box-line", className: "tcw-analytics-panel--trend", children: [_jsx(TrendLineChart, { points: dashboard.monthlyTrend }), _jsxs("div", { className: "tcw-analytics-chart-legend", children: [_jsx("b", { children: "\u91D1\u989D\uFF08\u4E07\u5143\uFF09" }), _jsxs("span", { children: [_jsx("i", { className: "is-sales" }), "\u9500\u552E\u989D"] }), _jsxs("span", { children: [_jsx("i", { className: "is-purchase" }), "\u91C7\u8D2D\u989D"] }), _jsxs("span", { children: [_jsx("i", { className: "is-profit" }), "\u9884\u8BA1\u6BDB\u5229"] }), _jsxs("span", { children: [_jsx("i", { className: "is-received" }), "\u56DE\u6B3E\u91D1\u989D"] })] }), _jsxs("div", { className: "tcw-analytics-trend-summary", children: [_jsxs("span", { children: ["\u9500\u552E ", compactMoney(dashboard.kpis.salesTotalCny)] }), _jsxs("span", { children: ["\u91C7\u8D2D ", compactMoney(dashboard.kpis.purchaseTotalCny)] }), _jsxs("span", { children: ["\u6BDB\u5229\u7387 ", percent(dashboard.kpis.estimatedGrossProfitRate)] })] })] }), _jsx(Panel, { title: "\u8D44\u91D1\u98CE\u9669\u7ED3\u6784", icon: "ri-stack-line", className: "tcw-analytics-panel--capital", children: _jsx(MoneyStructure, { kpis: dashboard.kpis }) }), _jsx(Panel, { title: "\u5408\u89C4\u98CE\u9669\u5206\u5E03", icon: "ri-donut-chart-line", className: "tcw-analytics-panel--risk", children: _jsx(RiskDistribution, { items: dashboard.riskDistribution, onOpen: item => onInfo({ title: `${item.label}明细`, rows: [{ label: item.label, value: `${item.count} 项${item.amountCny ? ` · ${money(item.amountCny)} CNY` : ''}`, actionLabel: '查看' }] }) }) }), _jsx(Panel, { title: "\u9500\u552E\u6587\u4EF6\u590D\u6838", icon: "ri-file-search-line", className: "tcw-analytics-panel--files", action: _jsx(PanelLink, { label: "\u67E5\u770B\u5168\u90E8", onClick: () => onInfo({ title: '销售文件复核明细', rows: salesFileRows.map(row => ({ label: row.fileName, value: `${row.fileType} · ${row.orderNo} · ${row.status}`, actionLabel: '复核' })) }) }), children: _jsx(SalesFileReviews, { rows: salesFileRows }) }), _jsx(Panel, { title: "\u98CE\u9669\u8BA2\u5355\u660E\u7EC6", icon: "ri-file-warning-line", className: "tcw-analytics-panel--risk-orders", action: _jsx(PanelLink, { label: "\u67E5\u770B\u5168\u90E8", onClick: () => onInfo({ title: '风险订单明细', rows: riskOrderRows.map(row => ({ label: row.orderNo, value: `${row.partyName} · ${row.riskType} · ${money(row.riskAmountCny)} CNY`, actionLabel: '跟进' })) }) }), children: _jsx(RiskOrders, { rows: riskOrderRows }) }), _jsx(Panel, { title: "\u6307\u6807\u6570\u636E\u6765\u6E90", icon: "ri-database-2-line", className: "tcw-analytics-panel--source", action: _jsx(PanelLink, { label: "\u67E5\u770B\u8BE6\u60C5", onClick: () => onInfo({ title: '指标数据来源', rows: dashboard.dataSources.map(source => ({ label: source.label, value: source.description, actionLabel: '查看' })) }) }), children: _jsx(DataSourceList, { rows: dashboard.dataSources, onOpen: source => onInfo({ title: source.label, rows: [{ label: source.label, value: source.description, actionLabel: '查看' }] }) }) }), _jsx(Panel, { title: "\u5BA2\u6237\u4EF7\u503C TOP5", icon: "ri-vip-crown-line", className: "tcw-analytics-panel--customer", children: _jsx(CustomerValue, { rows: customerValueRows }) }), _jsx(Panel, { title: "\u672A\u6536\u98CE\u9669 TOP5", icon: "ri-bank-line", className: "tcw-analytics-panel--receivable", children: _jsx(ReceivableRisk, { rows: receivableRiskRows }) }), _jsx(Panel, { title: "\u4F9B\u5E94\u5546\u98CE\u9669 TOP5", icon: "ri-truck-line", className: "tcw-analytics-panel--supplier", children: _jsx(SupplierRisk, { rows: supplierRiskRows }) })] })] });
|
|
86
77
|
}
|
|
87
78
|
function Panel({ title, icon, className = '', action, children }) {
|
|
88
79
|
return _jsxs("section", { className: `tcw-analytics-panel ${className}`.trim(), children: [_jsxs("header", { children: [_jsxs("strong", { children: [_jsx("i", { className: icon, "aria-hidden": "true" }), title] }), action] }), children] });
|
|
89
80
|
}
|
|
90
|
-
function PanelLink({
|
|
91
|
-
return _jsxs("
|
|
81
|
+
function PanelLink({ label, onClick }) {
|
|
82
|
+
return _jsxs("button", { type: "button", className: "tcw-analytics-panel-link", onClick: onClick, children: [label, _jsx("i", { className: "ri-arrow-right-s-line", "aria-hidden": "true" })] });
|
|
92
83
|
}
|
|
93
84
|
function MoneyStructure({ kpis }) {
|
|
94
85
|
const items = [
|
|
@@ -100,7 +91,7 @@ function MoneyStructure({ kpis }) {
|
|
|
100
91
|
const total = Math.max(...items.map(item => Number(item.value) || 0), 1);
|
|
101
92
|
return _jsx("div", { className: "tcw-analytics-money-structure", children: items.map(item => _jsxs("div", { children: [_jsxs("span", { children: [_jsx("i", { style: { background: item.color } }), item.label] }), _jsxs("b", { children: [money(item.value), " CNY"] }), _jsx("em", { children: _jsx("i", { style: { width: `${Math.max(6, (Number(item.value) || 0) / total * 100)}%`, background: item.color } }) })] }, item.label)) });
|
|
102
93
|
}
|
|
103
|
-
function RiskDistribution({ items }) {
|
|
94
|
+
function RiskDistribution({ items, onOpen }) {
|
|
104
95
|
const total = items.reduce((sum, item) => sum + item.count, 0);
|
|
105
96
|
const colors = ['#1769ff', '#26b6c8', '#ff9f1c', '#8b5cf6', '#5ac85a', '#ff4d55'];
|
|
106
97
|
const segments = items.length ? items.map((item, index) => ({ item, color: colors[index % colors.length] })) : [{ item: { key: 'empty', label: '暂无风险', count: 1, amountCny: '0' }, color: '#e7edf5' }];
|
|
@@ -111,13 +102,13 @@ function RiskDistribution({ items }) {
|
|
|
111
102
|
return `${color} ${start.toFixed(2)}% ${cursor.toFixed(2)}%`;
|
|
112
103
|
}).join(', ');
|
|
113
104
|
const style = { '--risk-donut': `conic-gradient(${conic})` };
|
|
114
|
-
return _jsxs("div", { className: "tcw-analytics-risk-distribution", children: [_jsxs("div", { className: "tcw-analytics-donut", style: style, children: [_jsx("strong", { children: "\u5408\u8BA1" }), _jsx("span", { children: total })] }), _jsx("dl", { children: items.map((item, index) => _jsxs("
|
|
105
|
+
return _jsxs("div", { className: "tcw-analytics-risk-distribution", children: [_jsxs("div", { className: "tcw-analytics-donut", style: style, children: [_jsx("strong", { children: "\u5408\u8BA1" }), _jsx("span", { children: total })] }), _jsx("dl", { children: items.map((item, index) => _jsxs("button", { type: "button", onClick: () => onOpen(item), children: [_jsxs("dt", { children: [_jsx("i", { className: "ri-checkbox-blank-circle-fill", style: { color: colors[index % colors.length] }, "aria-hidden": "true" }), item.label] }), _jsxs("dd", { children: [item.count, " (", percentRatio(item.count, total), ")"] })] }, item.key)) })] });
|
|
115
106
|
}
|
|
116
107
|
function CustomerValue({ rows }) {
|
|
117
108
|
return rows.length ? _jsxs("table", { className: "tcw-analytics-dashboard-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "\u5BA2\u6237/\u4F9B\u5E94\u5546" }), _jsx("th", { children: "\u9500\u552E\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09" }), _jsx("th", { children: "\u6BDB\u5229\u7387" }), _jsx("th", { children: "\u98CE\u9669" })] }) }), _jsx("tbody", { children: rows.map(row => _jsxs("tr", { children: [_jsx("td", { title: row.name, children: row.name }), _jsx("td", { children: money(row.salesAmountCny) }), _jsx("td", { children: percent(row.grossProfitRate) }), _jsx("td", { children: _jsx(RiskBadge, { status: row.riskStatus }) })] }, row.key)) })] }) : _jsx(EmptyText, { text: "\u6682\u65E0\u5BA2\u6237\u4EF7\u503C\u6392\u540D" });
|
|
118
109
|
}
|
|
119
110
|
function ReceivableRisk({ rows }) {
|
|
120
|
-
return rows.length ? _jsxs("table", { className: "tcw-analytics-dashboard-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "\u5BA2\u6237
|
|
111
|
+
return rows.length ? _jsxs("table", { className: "tcw-analytics-dashboard-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "\u5BA2\u6237" }), _jsx("th", { children: "\u672A\u6536\u6B3E\u91D1\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09" }), _jsx("th", { children: "\u8BA2\u5355\u6570" }), _jsx("th", { children: "\u6700\u8FD1\u8BA2\u5355" })] }) }), _jsx("tbody", { children: rows.map(row => _jsxs("tr", { children: [_jsx("td", { title: row.name, children: row.name }), _jsx("td", { children: money(row.unreceivedCny) }), _jsx("td", { children: row.orderCount }), _jsx("td", { children: shortDate(row.latestOrderDate) })] }, row.key)) })] }) : _jsx(EmptyText, { text: "\u6682\u65E0\u672A\u6536\u98CE\u9669" });
|
|
121
112
|
}
|
|
122
113
|
function SupplierRisk({ rows }) {
|
|
123
114
|
return rows.length ? _jsxs("table", { className: "tcw-analytics-dashboard-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "\u4F9B\u5E94\u5546" }), _jsx("th", { children: "\u672A\u4ED8\u6B3E\u91D1\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09" }), _jsx("th", { children: "\u5546\u54C1\u547D\u4E2D" }), _jsx("th", { children: "\u5236\u88C1\u547D\u4E2D" })] }) }), _jsx("tbody", { children: rows.map(row => _jsxs("tr", { children: [_jsx("td", { title: row.name, children: row.name }), _jsx("td", { children: money(row.unpaidCny) }), _jsx("td", { children: row.productRiskHitCount }), _jsx("td", { children: row.sanctionHitCount })] }, row.key)) })] }) : _jsx(EmptyText, { text: "\u6682\u65E0\u4F9B\u5E94\u5546\u98CE\u9669" });
|
|
@@ -128,6 +119,12 @@ function SalesFileReviews({ rows }) {
|
|
|
128
119
|
function RiskOrders({ rows }) {
|
|
129
120
|
return rows.length ? _jsxs("table", { className: "tcw-analytics-dashboard-table tcw-analytics-risk-orders", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "\u8BA2\u5355\u53F7" }), _jsx("th", { children: "\u5BA2\u6237/\u4F9B\u5E94\u5546" }), _jsx("th", { children: "\u98CE\u9669\u7C7B\u578B" }), _jsx("th", { children: "\u8BA2\u5355\u91D1\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09" }), _jsx("th", { children: "\u72B6\u6001" })] }) }), _jsx("tbody", { children: rows.map(row => _jsxs("tr", { children: [_jsx("td", { children: row.orderNo }), _jsx("td", { title: row.partyName, children: row.partyName }), _jsx("td", { children: _jsx("em", { children: row.riskType }) }), _jsx("td", { children: money(row.riskAmountCny) }), _jsx("td", { children: _jsx("span", { children: row.status }) })] }, row.key)) })] }) : _jsx(EmptyText, { text: "\u6682\u65E0\u98CE\u9669\u8BA2\u5355" });
|
|
130
121
|
}
|
|
122
|
+
function DataSourceList({ rows, onOpen }) {
|
|
123
|
+
return _jsx("div", { className: "tcw-analytics-source-list", children: rows.map(source => _jsxs("button", { type: "button", onClick: () => onOpen(source), children: [_jsx("i", { className: "ri-links-line", "aria-hidden": "true" }), _jsx("strong", { children: source.label }), _jsx("span", { children: source.description })] }, source.label)) });
|
|
124
|
+
}
|
|
125
|
+
function TodoQueue({ rows }) {
|
|
126
|
+
return rows.length ? _jsxs("table", { className: "tcw-analytics-todo-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "\u5F85\u529E\u4E8B\u9879" }), _jsx("th", { children: "\u6570\u91CF" }), _jsx("th", { children: "\u4F18\u5148\u7EA7" })] }) }), _jsx("tbody", { children: rows.map(row => _jsxs("tr", { children: [_jsx("td", { title: row.description, children: row.label }), _jsx("td", { children: row.count }), _jsx("td", { children: _jsx(RiskBadge, { status: row.priority === 'high' ? 'HIGH' : row.priority === 'medium' ? 'MEDIUM' : 'LOW' }) })] }, row.key)) })] }) : _jsx(EmptyText, { text: "\u6682\u65E0\u5408\u89C4\u5F85\u529E" });
|
|
127
|
+
}
|
|
131
128
|
function RiskBadge({ status }) {
|
|
132
129
|
const label = status === 'HIGH' ? '高' : status === 'MEDIUM' ? '中' : status === 'LOW' ? '低' : status;
|
|
133
130
|
return _jsx("span", { className: `tcw-analytics-risk-badge is-${status.toLocaleLowerCase()}`, children: label });
|
|
@@ -135,12 +132,14 @@ function RiskBadge({ status }) {
|
|
|
135
132
|
function TrendLineChart({ points }) {
|
|
136
133
|
const months = Array.from({ length: 12 }, (_, index) => points.find(point => point.month === index + 1) ?? { month: index + 1, purchaseAmountCny: '0', salesAmountCny: '0', estimatedGrossProfitCny: '0', receivedCny: '0' });
|
|
137
134
|
const max = Math.max(...months.flatMap(point => [Number(point.purchaseAmountCny) || 0, Number(point.salesAmountCny) || 0, Number(point.estimatedGrossProfitCny) || 0]), 1);
|
|
135
|
+
const roundedMax = Math.max(1, Math.ceil(max / 10000 / 4) * 4);
|
|
136
|
+
const yTicks = Array.from({ length: 5 }, (_, index) => roundedMax - (roundedMax / 4) * index);
|
|
138
137
|
const line = (key) => months.map((point, index) => {
|
|
139
|
-
const x =
|
|
138
|
+
const x = 56 + index * (516 / 11);
|
|
140
139
|
const y = 132 - ((Number(point[key]) || 0) / max) * 104;
|
|
141
140
|
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
142
141
|
}).join(' ');
|
|
143
|
-
return _jsx("div", { className: "tcw-analytics-line-chart", children: _jsxs("svg", { viewBox: "0 0 600
|
|
142
|
+
return _jsx("div", { className: "tcw-analytics-line-chart", children: _jsxs("svg", { viewBox: "0 0 600 170", role: "img", "aria-label": "\u6708\u5EA6\u7ECF\u8425\u8D8B\u52BF\u6298\u7EBF\u56FE", children: [_jsx("g", { className: "tcw-analytics-line-grid", children: [28, 54, 80, 106, 132].map((y, index) => _jsxs(React.Fragment, { children: [_jsx("line", { x1: "56", x2: "576", y1: y, y2: y }), _jsx("text", { className: "tcw-analytics-trend-axis", x: "42", y: y + 4, children: formatChartTick(yTicks[index]) })] }, y)) }), _jsx("polyline", { className: "is-purchase", points: line('purchaseAmountCny') }), _jsx("polyline", { className: "is-sales", points: line('salesAmountCny') }), _jsx("polyline", { className: "is-profit", points: line('estimatedGrossProfitCny') }), _jsx("polyline", { className: "is-received", points: line('receivedCny') }), months.map((point, index) => _jsx("text", { x: 56 + index * (516 / 11), y: "156", children: point.month }, point.month))] }) });
|
|
144
143
|
}
|
|
145
144
|
function monthlyDeltaLabels(points) {
|
|
146
145
|
const sorted = [...points].sort((a, b) => a.month - b.month);
|
|
@@ -161,131 +160,14 @@ function monthlyDeltaLabels(points) {
|
|
|
161
160
|
profit: `较上月 ${deltaLabel(latest?.estimatedGrossProfitCny, previous?.estimatedGrossProfitCny)}`
|
|
162
161
|
};
|
|
163
162
|
}
|
|
164
|
-
function customerValueRowsFromData(data) {
|
|
165
|
-
if (data.dashboard.customerValueTop5.length)
|
|
166
|
-
return data.dashboard.customerValueTop5.map(item => ({
|
|
167
|
-
key: item.customerId,
|
|
168
|
-
name: item.customerName,
|
|
169
|
-
salesAmountCny: item.salesAmountCny,
|
|
170
|
-
grossProfitRate: item.estimatedGrossProfitRate,
|
|
171
|
-
unreceivedCny: item.unreceivedCny,
|
|
172
|
-
riskStatus: item.riskStatus
|
|
173
|
-
}));
|
|
174
|
-
const source = data.top5.length ? data.top5.map(item => {
|
|
175
|
-
const detail = findDetail(data, item.key, item.label);
|
|
176
|
-
return detail ?? { key: item.key, label: item.label, orderCount: 0, amountCny: item.amountCny };
|
|
177
|
-
}) : data.details.items;
|
|
178
|
-
return source.slice(0, 5).map(row => ({
|
|
179
|
-
key: row.key,
|
|
180
|
-
name: row.label,
|
|
181
|
-
salesAmountCny: row.amountCny,
|
|
182
|
-
grossProfitRate: amountRatio(estimatedGrossProfit(row), row.amountCny, false),
|
|
183
|
-
unreceivedCny: row.unreceivedCny ?? row.unpaidCny ?? '0',
|
|
184
|
-
riskStatus: riskStatusForAmount(row.unreceivedCny ?? row.unpaidCny ?? row.unrefundedCny)
|
|
185
|
-
}));
|
|
186
|
-
}
|
|
187
|
-
function receivableRiskRowsFromData(data) {
|
|
188
|
-
if (data.dashboard.receivableRiskTop5.length)
|
|
189
|
-
return data.dashboard.receivableRiskTop5.map(item => ({
|
|
190
|
-
key: item.customerId,
|
|
191
|
-
name: item.customerName,
|
|
192
|
-
unreceivedCny: item.unreceivedCny,
|
|
193
|
-
orderCount: item.orderCount,
|
|
194
|
-
latestOrderDate: item.latestOrderDate
|
|
195
|
-
}));
|
|
196
|
-
return data.details.items
|
|
197
|
-
.map(row => ({ key: row.key, name: row.label, unreceivedCny: row.unreceivedCny ?? row.unpaidCny ?? '0', orderCount: row.orderCount, latestOrderDate: null }))
|
|
198
|
-
.filter(row => Number(row.unreceivedCny) > 0)
|
|
199
|
-
.sort((a, b) => Number(b.unreceivedCny) - Number(a.unreceivedCny))
|
|
200
|
-
.slice(0, 5);
|
|
201
|
-
}
|
|
202
|
-
function supplierRiskRowsFromData(data) {
|
|
203
|
-
if (data.dashboard.supplierRiskTop5.length)
|
|
204
|
-
return data.dashboard.supplierRiskTop5.map(item => ({
|
|
205
|
-
key: item.supplierId,
|
|
206
|
-
name: item.supplierName,
|
|
207
|
-
unpaidCny: item.unpaidCny,
|
|
208
|
-
sanctionHitCount: item.sanctionHitCount,
|
|
209
|
-
productRiskHitCount: item.productRiskHitCount,
|
|
210
|
-
purchaseOrderCount: item.purchaseOrderCount
|
|
211
|
-
}));
|
|
212
|
-
return data.details.items
|
|
213
|
-
.map(row => ({
|
|
214
|
-
key: row.key,
|
|
215
|
-
name: row.label,
|
|
216
|
-
unpaidCny: row.unpaidCny ?? row.unreceivedCny ?? '0',
|
|
217
|
-
sanctionHitCount: 0,
|
|
218
|
-
productRiskHitCount: Number(row.productQuantity) > 0 ? 1 : 0,
|
|
219
|
-
purchaseOrderCount: row.orderCount
|
|
220
|
-
}))
|
|
221
|
-
.filter(row => Number(row.unpaidCny) > 0)
|
|
222
|
-
.sort((a, b) => Number(b.unpaidCny) - Number(a.unpaidCny))
|
|
223
|
-
.slice(0, 5);
|
|
224
|
-
}
|
|
225
|
-
function salesFileRowsFromData(data) {
|
|
226
|
-
if (data.dashboard.salesFileReviews.length)
|
|
227
|
-
return data.dashboard.salesFileReviews.map(item => ({
|
|
228
|
-
key: item.fileId,
|
|
229
|
-
fileName: item.fileName,
|
|
230
|
-
fileType: item.fileType,
|
|
231
|
-
orderNo: item.salesOrderNo,
|
|
232
|
-
reason: item.reason,
|
|
233
|
-
status: '待复核'
|
|
234
|
-
}));
|
|
235
|
-
return data.details.items
|
|
236
|
-
.filter(row => hasRiskAmount(row))
|
|
237
|
-
.slice(0, 5)
|
|
238
|
-
.map(row => ({ key: `review-${row.key}`, fileName: `${row.label}合规资料`, fileType: '业务单据', orderNo: row.key, reason: riskLabelForRow(row), status: '待复核' }));
|
|
239
|
-
}
|
|
240
|
-
function riskOrderRowsFromData(data) {
|
|
241
|
-
if (data.dashboard.riskOrders.length)
|
|
242
|
-
return data.dashboard.riskOrders.map(item => ({
|
|
243
|
-
key: item.orderKey,
|
|
244
|
-
orderNo: item.orderNo,
|
|
245
|
-
partyName: item.partyName,
|
|
246
|
-
riskType: item.riskType,
|
|
247
|
-
riskAmountCny: item.riskAmountCny,
|
|
248
|
-
status: item.status
|
|
249
|
-
}));
|
|
250
|
-
return data.details.items
|
|
251
|
-
.map(row => ({ key: `risk-${row.key}`, orderNo: row.key, partyName: row.label, riskType: riskLabelForRow(row), riskAmountCny: largestRiskAmount(row), status: '待复核' }))
|
|
252
|
-
.filter(row => Number(row.riskAmountCny) > 0)
|
|
253
|
-
.sort((a, b) => Number(b.riskAmountCny) - Number(a.riskAmountCny))
|
|
254
|
-
.slice(0, 5);
|
|
255
|
-
}
|
|
256
|
-
function findDetail(data, key, label) {
|
|
257
|
-
return data.details.items.find(row => row.key === key || row.label === label);
|
|
258
|
-
}
|
|
259
|
-
function estimatedGrossProfit(row) {
|
|
260
|
-
if (row.receivedCny || row.paidCny)
|
|
261
|
-
return new Decimal(row.receivedCny ?? row.amountCny).minus(row.paidCny ?? 0).toString();
|
|
262
|
-
return '0';
|
|
263
|
-
}
|
|
264
|
-
function riskStatusForAmount(value) {
|
|
265
|
-
const number = Number(value) || 0;
|
|
266
|
-
if (number <= 0)
|
|
267
|
-
return 'LOW';
|
|
268
|
-
return number >= 100_000 ? 'HIGH' : 'MEDIUM';
|
|
269
|
-
}
|
|
270
|
-
function hasRiskAmount(row) {
|
|
271
|
-
return Number(row.unreceivedCny ?? 0) > 0 || Number(row.unpaidCny ?? 0) > 0 || Number(row.unrefundedCny ?? 0) > 0;
|
|
272
|
-
}
|
|
273
|
-
function riskLabelForRow(row) {
|
|
274
|
-
const risks = [
|
|
275
|
-
Number(row.unreceivedCny ?? 0) > 0 ? '未收款' : '',
|
|
276
|
-
Number(row.unpaidCny ?? 0) > 0 ? '未付款' : '',
|
|
277
|
-
Number(row.unrefundedCny ?? 0) > 0 ? '未退税' : ''
|
|
278
|
-
].filter(Boolean);
|
|
279
|
-
return risks.length ? risks.join('/') : '合规待复核';
|
|
280
|
-
}
|
|
281
|
-
function largestRiskAmount(row) {
|
|
282
|
-
return String(Math.max(Number(row.unreceivedCny ?? 0), Number(row.unpaidCny ?? 0), Number(row.unrefundedCny ?? 0)));
|
|
283
|
-
}
|
|
284
163
|
function EmptyText({ text }) {
|
|
285
164
|
return _jsx("small", { className: "tcw-analytics-empty", children: text });
|
|
286
165
|
}
|
|
287
|
-
function AdvancedFilterModal({ active,
|
|
288
|
-
return _jsx("div", { className: "tcw-analytics-modal-deck", hidden: !active, "aria-hidden": !active, children: _jsx(AnalyticsModal, { title: "\
|
|
166
|
+
function AdvancedFilterModal({ active, filters, onFilterChange, onApply, onReset, onClose }) {
|
|
167
|
+
return _jsx("div", { className: "tcw-analytics-modal-deck", hidden: !active, "aria-hidden": !active, children: _jsx(AnalyticsModal, { title: "\u81EA\u5B9A\u4E49\u533A\u95F4", active: active, onClose: onClose, footer: _jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onReset, children: [_jsx("i", { className: "ri-restart-line", "aria-hidden": "true" }), "\u91CD\u7F6E"] }), _jsxs("button", { type: "button", className: "tcw-button tcw-button--primary", onClick: onApply, children: [_jsx("i", { className: "ri-check-line", "aria-hidden": "true" }), "\u786E\u5B9A"] })] }), children: _jsx("div", { className: "tcw-analytics-modal-grid", children: _jsx(ModalRange, { label: "\u7EDF\u8BA1\u65E5\u671F", value: filters.orderDate, onChange: value => onFilterChange('orderDate', value) }) }) }) });
|
|
168
|
+
}
|
|
169
|
+
function AnalyticsInfoModal({ info, onClose }) {
|
|
170
|
+
return _jsx("div", { className: "tcw-analytics-modal-deck", "aria-hidden": false, children: _jsx(AnalyticsModal, { title: info.title, active: true, onClose: onClose, footer: _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onClose, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u5173\u95ED"] }), children: info.rows.length ? _jsxs("table", { className: "tcw-analytics-info-list", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "\u540D\u79F0" }), _jsx("th", { children: "\u660E\u7EC6" }), _jsx("th", { children: "\u64CD\u4F5C" })] }) }), _jsx("tbody", { children: info.rows.map(row => _jsxs("tr", { children: [_jsx("td", { children: row.label }), _jsx("td", { children: row.value }), _jsx("td", { children: _jsxs("button", { type: "button", className: "tcw-analytics-info-action", children: [_jsx("i", { className: "ri-eye-line", "aria-hidden": "true" }), row.actionLabel ?? '查看'] }) })] }, `${row.label}-${row.value}`)) })] }) : _jsx(EmptyText, { text: "\u6682\u65E0\u660E\u7EC6" }) }) });
|
|
289
171
|
}
|
|
290
172
|
function BusinessExportModal({ active, total, onClose, onExport }) {
|
|
291
173
|
return _jsx("div", { className: "tcw-analytics-modal-deck", hidden: !active, "aria-hidden": !active, children: _jsx(AnalyticsModal, { title: "\u4E1A\u52A1\u5BFC\u51FA", active: active, onClose: onClose, footer: _jsxs("button", { type: "button", className: "tcw-button tcw-button--secondary", onClick: onClose, children: [_jsx("i", { className: "ri-close-line", "aria-hidden": "true" }), "\u53D6\u6D88"] }), children: _jsxs("div", { className: "tcw-analytics-export-panel", children: [_jsxs("button", { type: "button", onClick: () => onExport('summary'), children: [_jsx("i", { className: "ri-dashboard-line", "aria-hidden": "true" }), _jsx("strong", { children: "\u7BA1\u7406\u6C47\u603B\u62A5\u8868" }), _jsx("span", { children: "\u6307\u6807\u6C47\u603B\u3001TOP \u6392\u540D\u548C\u8D8B\u52BF\u6570\u636E\uFF0C\u9002\u5408\u7ECF\u8425\u590D\u76D8\u3002" })] }), _jsxs("button", { type: "button", onClick: () => onExport('details'), children: [_jsx("i", { className: "ri-file-list-3-line", "aria-hidden": "true" }), _jsx("strong", { children: "\u5F53\u524D\u660E\u7EC6\u6E05\u5355" }), _jsxs("span", { children: ["\u5BFC\u51FA\u5F53\u524D\u7B5B\u9009\u540E\u7684 ", total, " \u6761\u660E\u7EC6\u6570\u636E\u3002"] })] }), _jsxs("button", { type: "button", onClick: () => onExport('exceptions'), children: [_jsx("i", { className: "ri-alarm-warning-line", "aria-hidden": "true" }), _jsx("strong", { children: "\u5F02\u5E38/\u98CE\u9669\u6E05\u5355" }), _jsx("span", { children: "\u672A\u6536\u6B3E\u3001\u672A\u4ED8\u6B3E\u3001\u672A\u9000\u7A0E\u548C\u5408\u89C4\u5F85\u5904\u7406\u4E8B\u9879\u3002" })] })] }) }) });
|
|
@@ -323,6 +205,12 @@ function quickRange(index) {
|
|
|
323
205
|
start.setDate(1);
|
|
324
206
|
return { from: start.toISOString().slice(0, 10), to: end };
|
|
325
207
|
}
|
|
208
|
+
function yearRange(year) {
|
|
209
|
+
return { from: `${year}-01-01`, to: `${year}-12-31` };
|
|
210
|
+
}
|
|
211
|
+
function formatChartTick(value) {
|
|
212
|
+
return value >= 1 ? value.toLocaleString('zh-CN', { maximumFractionDigits: 0 }) : value.toLocaleString('zh-CN', { maximumFractionDigits: 1 });
|
|
213
|
+
}
|
|
326
214
|
function PrototypeState({ index, title, area, children }) {
|
|
327
215
|
return _jsxs("section", { className: "tcw-analytics-prototype-state", style: area ? { gridArea: area } : undefined, "aria-label": `${index} ${title}`, children: [_jsxs("div", { className: "tcw-analytics-prototype-label", children: [_jsx("span", { className: "tcw-analytics-prototype-index", children: index }), _jsx("strong", { children: title })] }), children] });
|
|
328
216
|
}
|