2026-08-18 00:26:39 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 功能:静态检查气站管理端全部标准资源是否具备独立记录页配置。
|
2026-08-18 22:49:14 +08:00
|
|
|
|
* 版本:v1.5.0
|
2026-08-18 00:26:39 +08:00
|
|
|
|
*/
|
|
|
|
|
|
import { readFileSync, existsSync } from 'node:fs';
|
|
|
|
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
|
|
import { dirname, resolve } from 'node:path';
|
|
|
|
|
|
|
|
|
|
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
|
|
|
const contract = JSON.parse(readFileSync(resolve(root, 'src/contracts/gas-resources.json'), 'utf8'));
|
|
|
|
|
|
const routeSource = readFileSync(resolve(root, 'src/router/routes/modules/platform.ts'), 'utf8');
|
|
|
|
|
|
const routeBuilderSource = readFileSync(resolve(root, 'src/router/routes/modules/resource-route-builder.ts'), 'utf8');
|
|
|
|
|
|
const ruleSource = readFileSync(resolve(root, 'src/api/resource-page-rules.ts'), 'utf8');
|
|
|
|
|
|
const listSource = readFileSync(resolve(root, 'src/views/shared/CrudListPage.vue'), 'utf8');
|
2026-08-18 22:49:14 +08:00
|
|
|
|
const listFieldSource = readFileSync(resolve(root, 'src/views/shared/resource-list-field-display.ts'), 'utf8');
|
2026-08-18 00:26:39 +08:00
|
|
|
|
const detailSource = readFileSync(resolve(root, 'src/views/resource/ResourceDetailContent.vue'), 'utf8');
|
2026-08-18 22:49:14 +08:00
|
|
|
|
const recordSource = readFileSync(resolve(root, 'src/views/resource/ResourceRecordPage.vue'), 'utf8');
|
|
|
|
|
|
const relationSource = readFileSync(resolve(root, 'src/views/resource/use-resource-relations.ts'), 'utf8');
|
2026-08-18 00:26:39 +08:00
|
|
|
|
const displaySource = readFileSync(resolve(root, 'src/api/resource-display.ts'), 'utf8');
|
|
|
|
|
|
const resourceSource = readFileSync(resolve(root, 'src/api/resources.ts'), 'utf8');
|
|
|
|
|
|
const treeResources = new Set();
|
|
|
|
|
|
const listResources = contract.resources.filter((item) => !treeResources.has(item.name));
|
|
|
|
|
|
const createModes = new Set(['writable', 'editable', 'append_only', 'managed']);
|
|
|
|
|
|
const editModes = new Set(['writable', 'editable', 'managed']);
|
|
|
|
|
|
const editableResources = listResources.filter((item) => editModes.has(item.mode));
|
|
|
|
|
|
|
|
|
|
|
|
function fail(message) {
|
|
|
|
|
|
throw new Error(`资源独立页面检查失败:${message}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const missingRoutes = listResources
|
|
|
|
|
|
.filter((item) => !routeSource.includes(`'/${item.name}'`))
|
|
|
|
|
|
.map((item) => item.name);
|
|
|
|
|
|
if (missingRoutes.length) fail(`路由未覆盖 ${missingRoutes.join('、')}`);
|
|
|
|
|
|
|
|
|
|
|
|
const missingRules = editableResources
|
|
|
|
|
|
.filter((item) => !new RegExp(`\\n\\s*${item.name}:\\s*\\{`).test(ruleSource))
|
|
|
|
|
|
.map((item) => item.name);
|
|
|
|
|
|
if (missingRules.length) fail(`编辑字段规则未覆盖 ${missingRules.join('、')}`);
|
|
|
|
|
|
|
|
|
|
|
|
for (const marker of ["makeRecordRoute('create'", "makeRecordRoute('detail'", "makeRecordRoute('edit'"]) {
|
|
|
|
|
|
if (!routeBuilderSource.includes(marker)) fail(`通用路由生成器缺少 ${marker}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (listSource.includes('<a-drawer')) fail('列表组件仍包含 CRUD Drawer');
|
|
|
|
|
|
if (
|
|
|
|
|
|
!listSource.includes(
|
|
|
|
|
|
'[() => props.definition.resource, () => staffType.value]',
|
|
|
|
|
|
)
|
|
|
|
|
|
) {
|
|
|
|
|
|
fail('工作人员角色菜单切换未触发列表重新加载');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (existsSync(resolve(root, 'src/views/account/AccountProfilePage.vue'))) {
|
|
|
|
|
|
fail('旧账户资料组件仍存在,未统一到共享记录页');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!detailSource.includes('resourceFieldLabel(definition, column, collection.key)')) {
|
|
|
|
|
|
fail('聚合详情表格未按集合上下文解析中文字段名称');
|
|
|
|
|
|
}
|
|
|
|
|
|
for (const marker of [
|
|
|
|
|
|
"'gasorder_contract.products'",
|
|
|
|
|
|
"product_info_identity: '气瓶唯一标识'",
|
|
|
|
|
|
"product_code: '气瓶编码'",
|
|
|
|
|
|
"if (key === 'contract_status') return contractStatusLabel(Number(value))",
|
|
|
|
|
|
"terminate: '终止合同'",
|
|
|
|
|
|
"key === 'expired_at'",
|
|
|
|
|
|
"return '待补录'",
|
|
|
|
|
|
]) {
|
|
|
|
|
|
if (!displaySource.includes(marker)) fail(`合同详情中文展示缺少 ${marker}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!resourceSource.includes("visibleFor: { field: 'contract_status', values: [0] }")) {
|
|
|
|
|
|
fail('合同启用动作未限制为草稿状态');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!resourceSource.includes("visibleFor: { field: 'contract_status', values: [11, 12, 13] }")) {
|
|
|
|
|
|
fail('合同续签动作未覆盖已终止状态');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!detailSource.includes("!['deleted_at', 'DeletedAt'].includes(column)")) {
|
|
|
|
|
|
fail('聚合详情表未隐藏公共软删除字段');
|
|
|
|
|
|
}
|
|
|
|
|
|
for (const marker of [
|
|
|
|
|
|
"Number(row.contract_status) !== 0",
|
|
|
|
|
|
"![0, 1].includes(Number(row.status))",
|
|
|
|
|
|
"只有草稿状态的合同可以编辑",
|
|
|
|
|
|
"停用、已归档或已冻结的合同不可编辑",
|
|
|
|
|
|
]) {
|
|
|
|
|
|
if (!ruleSource.includes(marker)) fail(`合同编辑状态规则缺少 ${marker}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!listSource.includes('recordEditReason(record)')) {
|
|
|
|
|
|
fail('合同列表缺少编辑禁用原因提示');
|
|
|
|
|
|
}
|
2026-08-18 20:52:15 +08:00
|
|
|
|
if (
|
|
|
|
|
|
!resourceSource.includes(
|
|
|
|
|
|
"relation('delivery_basic_identity', '/delivery_basic', true, { label: '所属配送点', listLabel: '所属配送点', listRelationNameOnly: true, showIdentityCopy: true })",
|
|
|
|
|
|
)
|
|
|
|
|
|
) {
|
|
|
|
|
|
fail('用户账户列表未按文档以所属配送点名称为主展示');
|
|
|
|
|
|
}
|
2026-08-18 21:15:49 +08:00
|
|
|
|
if (
|
|
|
|
|
|
!displaySource.includes('detail.user') ||
|
|
|
|
|
|
!displaySource.includes('relation.delivery_basic_identity ?? user.delivery_basic_identity')
|
|
|
|
|
|
) {
|
|
|
|
|
|
fail('用户账户详情未恢复主档与配送点关系上下文');
|
|
|
|
|
|
}
|
2026-08-18 22:49:14 +08:00
|
|
|
|
if (
|
|
|
|
|
|
!listFieldSource.includes('const serverName = relationDisplayName(field, row)') ||
|
|
|
|
|
|
!relationSource.includes('if (relationDisplayName(field, values)) return []')
|
|
|
|
|
|
) {
|
|
|
|
|
|
fail('合同列表未优先使用服务端范围内的签约用户名称');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (
|
|
|
|
|
|
!ruleSource.includes('row.user_service_active === false') ||
|
|
|
|
|
|
!recordSource.includes("action.name !== '终止合同'")
|
|
|
|
|
|
) {
|
|
|
|
|
|
fail('签约用户转出后未限制合同编辑和业务动作');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (
|
|
|
|
|
|
!ruleSource.includes('row.contract_readonly === true') ||
|
|
|
|
|
|
!recordSource.includes('record.value.contract_readonly === true') ||
|
|
|
|
|
|
!recordSource.includes('record.contract_readonly !== true') ||
|
|
|
|
|
|
!recordSource.includes("definition.value.name === 'user_account'") ||
|
|
|
|
|
|
!listSource.includes("contract_identity: String(row.identity ?? '')") ||
|
|
|
|
|
|
!displaySource.includes("contract_readonly: '账户权限'") ||
|
|
|
|
|
|
!displaySource.includes("return '仅查看当前合同关联资料'")
|
|
|
|
|
|
) {
|
|
|
|
|
|
fail('合同范围用户详情未恢复配送点上下文或未进入完整只读模式');
|
|
|
|
|
|
}
|
2026-08-18 00:26:39 +08:00
|
|
|
|
|
|
|
|
|
|
const createCount = listResources.filter((item) => createModes.has(item.mode)).length;
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
`资源独立页面检查通过:详情 ${listResources.length} 类,新建 ${createCount} 类,编辑 ${editableResources.length} 类。`,
|
|
|
|
|
|
);
|