From ec8ff4f6259404460a0b4437e29a40986c2c4b9d Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 12 十月 2026 18:31:43 +0800
Subject: [PATCH] feat(workflow): 集成自动化审批功能并优化多个业务模块
---
scripts/check-analytics-permissions.cjs | 62 +++++++++++++++++++++++++++++++
1 files changed, 62 insertions(+), 0 deletions(-)
diff --git a/scripts/check-analytics-permissions.cjs b/scripts/check-analytics-permissions.cjs
new file mode 100644
index 0000000..5295a60
--- /dev/null
+++ b/scripts/check-analytics-permissions.cjs
@@ -0,0 +1,62 @@
+const fs = require('node:fs');
+const path = require('node:path');
+const ts = require('typescript');
+const assert = require('node:assert/strict');
+const { parse, compileTemplate } = require('vue/compiler-sfc');
+const root = path.join(__dirname, '../src/views/dashboard/analytics');
+function evaluate(source, mocks) {
+ const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText;
+ const module = { exports: {} };
+ new Function('require', 'module', 'exports', 'defineOptions', 'defineProps', 'defineEmits', js)(
+ (id) => mocks[id] || {}, module, module.exports, () => {}, () => ({ repairActiveCount: 0 }), () => () => {});
+ return module.exports;
+}
+const vue = { computed: (getter) => ({ get value() { return getter(); } }), ref: (value) => ({ value }) };
+async function scenario(codes, names, expected) {
+ const store = { accessRoutes: [{ name: 'Parent', children: names.map((name) => ({ name })) }] };
+ const home = evaluate(fs.readFileSync(path.join(root, 'use-home-access.ts'), 'utf8'), {
+ vue, '@vben/stores': { useAccessStore: () => store },
+ '@vben/access': { useAccess: () => ({ hasAccessByCodes: (requested) => requested.some((code) => codes.includes(code)) }) },
+ });
+ const calls = [];
+ const mocks = { '../use-home-access': home, './use-home-access': home,
+ 'vue-router': { useRouter: () => ({ push: () => {} }) }, './data': { defaultSummary: {} } };
+ for (const [id, method] of [
+ ['#/api/mes/home', 'getHomeSummary'], ['#/api/mes/home', 'getProductionTrend'],
+ ['#/api/mes/home', 'getWorkOrderStatusDistribution'], ['#/api/erp/purchase/order', 'getPurchaseOrderPage'],
+ ['#/api/erp/sale/order', 'getSaleOrderPage'], ['#/api/mes/wm/materialstock', 'getMaterialStockPage'],
+ ['#/api/bpm/task', 'getTaskTodoPage'],
+ ]) {
+ mocks[id] ||= {};
+ mocks[id][method] = async () => { calls.push(method); return method.startsWith('getHome') ? {} : method.endsWith('Page') ? { total: 3 } : []; };
+ }
+ mocks['@vben/plugins/echarts'] = { useEcharts: () => ({ renderEcharts: () => {} }) };
+ for (const file of ['index.vue', 'modules/biz-cards.vue', 'modules/production-trend.vue', 'modules/alert-panel.vue']) {
+ const mounted = [];
+ mocks.vue = { ...vue, onMounted: (fn) => mounted.push(fn) };
+ const { descriptor } = parse(fs.readFileSync(path.join(root, file), 'utf8'));
+ evaluate(descriptor.scriptSetup.content, mocks);
+ mounted.forEach((fn) => fn());
+ }
+ await new Promise((resolve) => setImmediate(resolve));
+ assert.deepEqual(calls.sort(), expected.sort());
+ const access = home.useHomeAccess();
+ store.accessRoutes = [{ name: 'Forbidden', meta: { menuVisibleWithForbidden: true }, children: [{ name: 'Child' }] }];
+ assert.equal(access.canNavigate('Forbidden'), false);
+ assert.equal(access.canNavigate('Child'), false);
+}
+(async () => {
+ await scenario([], [], []);
+ await scenario(['erp:purchase-order:query'], ['ErpPurchaseOrder'], ['getPurchaseOrderPage']);
+ await scenario(['erp:purchase-order:query'], [], []);
+ await scenario(['mes:home:query'], [], ['getHomeSummary', 'getProductionTrend', 'getWorkOrderStatusDistribution']);
+ await scenario(['*:*:*'], ['ErpPurchaseOrder', 'ErpSaleOrder', 'MesWmMaterialStock', 'BpmTaskTodo'],
+ ['getHomeSummary', 'getProductionTrend', 'getWorkOrderStatusDistribution', 'getPurchaseOrderPage', 'getSaleOrderPage', 'getMaterialStockPage', 'getTaskTodoPage']);
+ for (const file of ['index.vue', ...fs.readdirSync(path.join(root, 'modules')).filter((f) => f.endsWith('.vue')).map((f) => `modules/${f}`)]) {
+ const { descriptor, errors } = parse(fs.readFileSync(path.join(root, file), 'utf8'));
+ assert.equal(errors.length, 0, file);
+ const result = compileTemplate({ source: descriptor.template.content, filename: file, id: file });
+ assert.equal(result.errors.length, 0, `${file}: ${result.errors}`);
+ }
+ console.log('PASS: 5 permission scenarios, forbidden routes, and all analytics Vue templates');
+})();
--
Gitblit v1.9.3