调整部分前端布局和后端增删改逻辑

This commit is contained in:
焦龙言
2026-09-28 18:29:10 +08:00
parent 17d991a74a
commit a539b4eb71
57 changed files with 87382 additions and 895 deletions
@@ -159,21 +159,95 @@ test("PDA 路由和工作台入口覆盖五类现场业务", async () => {
assert.equal(pagePaths.has(pagePath), true, `${pagePath} 路由缺失`);
}
const workSource = await readFile(
path.join(appRoot, "src/pages/work/index.vue"),
"utf8",
const [workSource, menuSource] = await Promise.all([
readFile(path.join(appRoot, "src/pages/work/index.vue"), "utf8"),
readFile(path.join(appRoot, "src/config/businessMenu.js"), "utf8"),
]);
assert.match(workSource, /hasPermi\(entry\.permission\)/);
assert.match(menuSource, /business:inbound:list/);
assert.match(menuSource, /business:filling:list/);
assert.match(menuSource, /business:shipment:list/);
assert.match(menuSource, /business:usage:list/);
assert.match(menuSource, /business:return:list/);
assert.match(menuSource, /pages\/business\/inbound\/index/);
assert.match(menuSource, /pages\/business\/filling\/index/);
assert.match(menuSource, /pages\/business\/shipment\/index/);
assert.match(menuSource, /pages\/business\/usage\/index/);
assert.match(menuSource, /pages\/business\/return\/index/);
});
test("首页展示真实运营驾驶舱,不保留模板入口和虚假统计", async () => {
const source = await readFile(path.join(appRoot, "src/pages/index.vue"), "utf8");
assert.match(source, /listScanExceptions/);
assert.match(source, /最近扫码异常/);
assert.match(source, /TestBatchBanner/);
assert.doesNotMatch(
source,
/快捷入口|新闻|计划|请假申请|周报|工作效率|新通知|待办任务|85%/,
);
assert.match(workSource, /hasPermiOr/);
assert.match(workSource, /business:inbound/);
assert.match(workSource, /business:filling/);
assert.match(workSource, /business:shipment/);
assert.match(workSource, /business:usage/);
assert.match(workSource, /business:return/);
assert.match(workSource, /pages\/business\/inbound\/index/);
assert.match(workSource, /pages\/business\/filling\/index/);
assert.match(workSource, /pages\/business\/shipment\/index/);
assert.match(workSource, /pages\/business\/usage\/index/);
assert.match(workSource, /pages\/business\/return\/index/);
});
test("工作台只展示真实业务入口并使用后端列表权限", async () => {
const [workSource, menuModule] = await Promise.all([
readFile(path.join(appRoot, "src/pages/work/index.vue"), "utf8"),
importSource("src/config/businessMenu.js"),
]);
assert.deepEqual(
menuModule.businessMenuEntries.map(({ title, permission, path: entryPath }) => ({
title,
permission,
path: entryPath,
})),
[
{
title: "仓库入库",
permission: "business:inbound:list",
path: "/pages/business/inbound/index",
},
{
title: "工厂装填",
permission: "business:filling:list",
path: "/pages/business/filling/index",
},
{
title: "发货出库",
permission: "business:shipment:list",
path: "/pages/business/shipment/index",
},
{
title: "客户现场使用",
permission: "business:usage:list",
path: "/pages/business/usage/index",
},
{
title: "退回仓库",
permission: "business:return:list",
path: "/pages/business/return/index",
},
],
);
assert.match(workSource, /businessMenuEntries/);
assert.match(workSource, /hasPermi\(entry\.permission\)/);
assert.match(workSource, /navigateTo\(entry\.path\)/);
assert.doesNotMatch(workSource, /AI 生产力|智能对话|图像生成|系统管理|handleBuilding|模块建设中/);
});
test("运营驾驶舱 API 对接现有扫码异常接口", async () => {
const source = await readFile(path.join(appRoot, "src/api/business/dashboard.js"), "utf8");
assert.match(source, /export function listScanExceptions\(/);
assert.match(source, /url:\s*["']\/business\/dashboard\/scan-exceptions["']/);
assert.match(source, /method:\s*["']get["']/);
});
test("APP 页面标题与后端业务菜单名称一致", async () => {
const [pages, fillingSource] = await Promise.all([
JSON.parse(await readFile(path.join(appRoot, "src/pages.json"), "utf8")),
readFile(path.join(appRoot, "src/pages/business/filling/index.vue"), "utf8"),
]);
const fillingPage = pages.pages.find((page) => page.path === "pages/business/filling/index");
assert.equal(fillingPage?.style?.navigationBarTitleText, "工厂装填");
assert.match(fillingSource, />工厂装填</);
});
test("PDA 测试模式附加批次请求头并在五个业务页持续提示", async () => {
@@ -0,0 +1,79 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const readSource = (relativePath) =>
readFileSync(resolve(appRoot, relativePath), "utf8");
test("APP 首页展示 PC 端核心运营统计并保护移动端横向空间", () => {
const source = readSource("src/pages/index.vue");
for (const className of [
"dashboard-overview",
"dashboard-kpi-grid",
"dashboard-trend-card",
"dashboard-distribution-card",
"dashboard-trend-range",
"dashboard-status-list",
]) {
assert.match(source, new RegExp(`class=\\"[^\\"]*${className}[^\\"]*\\"`));
}
for (const label of [
"当前库存托盘",
"剩余包数",
"今日入库单",
"今日出库单",
"待确认单据",
"入库 / 出库趋势",
"托盘流转状态分布",
]) {
assert.match(source, new RegExp(label));
}
assert.match(source, /listPalletInventory/);
assert.match(source, /listShipmentReport/);
assert.match(source, /listInbound/);
assert.match(source, /listShipment/);
assert.match(source, /listReturn/);
assert.match(source, /TREND_RANGE_OPTIONS/);
assert.match(source, /overflow-x:\s*hidden/);
assert.match(source, /min-width:\s*0/);
});
test("APP 首页统计保留加载、空数据和部分失败状态", () => {
const source = readSource("src/pages/index.vue");
assert.match(source, /statsLoading/);
assert.match(source, /statsError/);
assert.match(source, /dashboard-stat-empty/);
assert.match(source, /dashboard-stat-error/);
assert.match(source, /formatMetric/);
assert.match(source, /--/);
});
test("APP 首页扫码异常支持默认收起并通过标题展开", () => {
const source = readSource("src/pages/index.vue");
assert.match(source, /const exceptionsExpanded = ref\(false\)/);
assert.match(source, /function toggleExceptionList\(\)/);
assert.match(source, /class="[^"]*exception-header[^"]*"/);
assert.match(source, /@click="toggleExceptionList"/);
assert.match(source, /exception-toggle-icon/);
assert.match(source, /v-else-if="exceptionsExpanded" class="exception-list"/);
});
test("APP 首页报表 API 复用现有只读报表路径", () => {
const inventorySource = readSource("src/api/report/palletInventory.js");
const shipmentSource = readSource("src/api/report/shipment.js");
assert.match(inventorySource, /export function listPalletInventory\(/);
assert.match(inventorySource, /url:\s*["']\/report\/pallet-inventory\/list["']/);
assert.match(inventorySource, /method:\s*["']get["']/);
assert.match(shipmentSource, /export function listShipmentReport\(/);
assert.match(shipmentSource, /url:\s*["']\/report\/shipment\/list["']/);
assert.match(shipmentSource, /method:\s*["']get["']/);
});
@@ -0,0 +1,38 @@
import assert from "node:assert/strict";
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const loginPagePath = resolve(appRoot, "src/pages/login.vue");
const loginPageSource = readFileSync(loginPagePath, "utf8");
test("APP 登录页沿用 PC 端的品牌背景与登录卡片结构", () => {
assert.match(loginPageSource, /class="login-page"/);
assert.match(loginPageSource, /\/static\/login-background\.jpg/);
assert.match(loginPageSource, /class="login-card"/);
assert.match(loginPageSource, /globalConfig\.appInfo\.name/);
assert.doesNotMatch(
loginPageSource,
/bg-gradient-to-br from-blue-50 to-indigo-100/,
);
assert.ok(
existsSync(resolve(appRoot, "src/static/login-background.jpg")),
"APP 登录页需要包含与 PC 端一致的背景资源",
);
});
test("APP 登录页保留验证码刷新与回车登录交互", () => {
assert.match(loginPageSource, /@click="getCode"/);
assert.match(loginPageSource, /@confirm="handleLogin"/);
assert.match(loginPageSource, /captchaEnabled/);
});
test("APP 登录页为白色 Sylvan 字标提供深色承载层", () => {
assert.match(loginPageSource, /class="brand-logo-frame"/);
assert.match(
loginPageSource,
/\.brand-logo-frame\s*\{[\s\S]*background:\s*#28225c/,
);
});
@@ -0,0 +1,47 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const readSource = (relativePath) =>
readFileSync(resolve(appRoot, relativePath), "utf8");
const homeSource = readSource("src/pages/index.vue");
const workSource = readSource("src/pages/work/index.vue");
const bannerSource = readSource("src/components/business/TestBatchBanner.vue");
const modeSwitchSource = readSource(
"src/components/business/TestBatchModeSwitch.vue",
);
test("首页使用受约束的运营布局并保护横向空间", () => {
assert.match(homeSource, /class="dashboard-page"/);
assert.match(homeSource, /class="dashboard-scroll"/);
assert.match(homeSource, /class="dashboard-content"/);
assert.match(homeSource, /class="dashboard-hero"/);
assert.match(homeSource, /class="dashboard-metric-grid"/);
assert.match(homeSource, /class="exception-list"/);
assert.match(homeSource, /class="exception-item"/);
assert.match(homeSource, /overflow-x:\s*hidden/);
assert.match(homeSource, /overflow-wrap:\s*anywhere/);
});
test("工作台入口在窄屏下保持单列和可换行", () => {
assert.match(workSource, /class="work-page"/);
assert.match(workSource, /class="work-scroll"/);
assert.match(workSource, /class="work-content"/);
assert.match(workSource, /class="work-entry-list"/);
assert.match(workSource, /class="work-entry"/);
assert.match(workSource, /class="work-entry-copy"/);
assert.match(workSource, /overflow-x:\s*hidden/);
assert.match(workSource, /overflow-wrap:\s*anywhere/);
});
test("测试批次提示和切换区不会撑开页面", () => {
assert.match(bannerSource, /class="test-batch-banner"/);
assert.match(bannerSource, /overflow-wrap:\s*anywhere/);
assert.match(modeSwitchSource, /class="test-batch-switch"/);
assert.match(modeSwitchSource, /class="test-batch-picker"/);
assert.match(modeSwitchSource, /overflow-wrap:\s*anywhere/);
});