diff --git a/web/src/components/accounts/AccountManagementList.tsx b/web/src/components/accounts/AccountManagementList.tsx index f8bac82..6a5cf21 100644 --- a/web/src/components/accounts/AccountManagementList.tsx +++ b/web/src/components/accounts/AccountManagementList.tsx @@ -32,6 +32,7 @@ interface Row { douyin_number?: string; tags?: string[]; runtime_status?: string; + business_status: 'normal' | 'muted' | 'banned' | 'deleted'; environment?: EnvironmentView; work_count?: number; latest_published_at?: string | null; @@ -220,6 +221,20 @@ export default function AccountManagementList() { ); } }, + { + title: '业务状态', + dataIndex: 'business_status', + render: (status: Row['business_status']) => { + const statuses = { + normal: { label: '正常', color: 'success' }, + muted: { label: '禁言', color: 'warning' }, + banned: { label: '封禁', color: 'error' }, + deleted: { label: '注销', color: 'default' }, + }; + const { label, color } = statuses[status]; + return {label}; + }, + }, { title: '操作', key: 'actions', diff --git a/web/tests/account-business-status.test.cjs b/web/tests/account-business-status.test.cjs new file mode 100644 index 0000000..a2186ea --- /dev/null +++ b/web/tests/account-business-status.test.cjs @@ -0,0 +1,68 @@ +const assert = require('node:assert/strict'); +const { readFileSync } = require('node:fs'); +const { createRequire } = require('node:module'); +const { test } = require('node:test'); +const vm = require('node:vm'); +const ts = require('typescript'); + +const source = ts.createSourceFile( + 'AccountManagementList.tsx', + readFileSync(`${__dirname}/../src/components/accounts/AccountManagementList.tsx`, 'utf8'), + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.TSX, +); +const requireWeb = createRequire(`${__dirname}/../package.json`); +const { Tag } = requireWeb('antd'); +let columns; +function visit(node) { + if (ts.isVariableDeclaration(node) && node.name.getText(source) === 'columns') { + columns = node.initializer.elements; + } + ts.forEachChild(node, visit); +} +visit(source); + +function property(column, key) { + return column.properties.find( + (prop) => ts.isPropertyAssignment(prop) && prop.name.getText(source) === key, + )?.initializer; +} + +function businessColumn() { + const column = columns.find((item) => property(item, 'title')?.text === '业务状态'); + assert.ok(column, '我的账号列表必须有业务状态列'); + const { outputText } = ts.transpileModule(`exports.column = (${column.getText(source)});`, { + compilerOptions: { jsx: ts.JsxEmit.ReactJSX, module: ts.ModuleKind.CommonJS }, + }); + const context = { exports: {}, require: requireWeb, Tag }; + vm.runInNewContext(outputText, context); + return context.exports.column; +} + +for (const [status, label, color] of [ + ['normal', '正常', 'success'], + ['muted', '禁言', 'warning'], + ['banned', '封禁', 'error'], + ['deleted', '注销', 'default'], +]) { + test(`业务状态 ${status} 显示“${label}”及对应颜色`, () => { + const column = businessColumn(); + assert.equal(column.dataIndex, 'business_status'); + const result = column.render(status, { business_status: status, runtime_status: 'paused' }); + assert.equal(result.type, Tag); + assert.equal(result.props.children, label); + assert.equal(result.props.color, color); + assert.equal(result.props.style, undefined, '使用默认 Tag 样式'); + }); +} + +test('业务状态列紧邻现有状态列,运行状态判定保持不变', () => { + const titles = columns.map((column) => property(column, 'title')?.text); + const index = titles.indexOf('业务状态'); + assert.ok(index > 0, '必须有独立的业务状态列'); + assert.equal(titles[index - 1], '状态'); + assert.equal(titles[index + 1], '操作'); + const readiness = property(columns[index - 1], 'render').getText(source); + assert.match(readiness, /accountReadiness\(account, account\.environment \?\? null\)/); +});