declare const process: { cwd(): string }; declare function require(name: string): { readFileSync?: (path: string, encoding: string) => string; join?: (...parts: string[]) => string; }; const { readFileSync } = require("node:fs"); const { join } = require("node:path"); if (!readFileSync || !join) { throw new Error("测试运行环境缺少文件读取能力"); } const root = process.cwd(); const view = readFileSync(join(root, "src/views/CommissionsView.vue"), "utf-8"); const css = readFileSync(join(root, "src/assets/styles.css"), "utf-8"); assertIncludes(view, "view-stack commissions-page"); assertIncludes(view, "summary-strip commission-summary-strip"); assertIncludes(view, "commission-list-panel"); assertIncludes(view, "commission-filter-bar"); assertIncludes(view, "commission-keyword-field"); assertIncludes(view, "commission-month-field"); assertIncludes(view, "commission-search-button"); assertIncludes(view, "row-action-group commission-row-actions"); assertIncludes(view, "table-action-icon"); assertIncludes(view, "title=\"编辑提成\""); assertIncludes(view, "title=\"删除提成\""); assertIncludes(view, "sourceTypeLabel"); assertIncludes(view, "manual: \"手工录入\""); assertIncludes(view, "excel: \"Excel导入\""); assertIncludes(view, "dingtalk: \"钉钉同步\""); assertIncludes(view, "{{ sourceTypeLabel(record.source_type) }}"); assertNotIncludes(view, "{{ record.source_type }}"); assertIncludes(view, "openCreateModal"); assertIncludes(view, "showCommissionModal"); assertIncludes(view, "closeCommissionModal"); assertIncludes(view, "Teleport to=\"body\""); assertIncludes(view, "modal-backdrop commission-modal-backdrop"); assertIncludes(view, "profile-modal commission-modal"); assertIncludes(view, "aria-label=\"提成维护\""); assertIncludes(view, "commission-form"); assertIncludes(view, "commission-form-grid"); assertIncludes(view, "commission-business-field"); assertIncludes(view, "commission-remark-field"); assertIncludes(view, "rows=\"2\""); assertIncludes(view, "commission-form-actions"); assertIncludes(view, "@click=\"openCreateModal\""); assertIncludes(view, "@click=\"closeCommissionModal\""); assertNotIncludes(view, "field span-row"); assertNotIncludes(view, "commission-entry-panel"); const pageBlock = cssBlock(".commissions-page"); assertIncludes(pageBlock, "gap: 14px;"); const summaryBlock = cssBlock(".commission-summary-strip"); assertIncludes(summaryBlock, "display: grid;"); assertIncludes(summaryBlock, "grid-template-columns: repeat(3, minmax(0, 1fr));"); assertIncludes(summaryBlock, "padding: 8px;"); const summaryMetricBlock = cssBlock(".commission-summary-strip .summary-metric"); assertIncludes(summaryMetricBlock, "min-height: 58px;"); assertIncludes(summaryMetricBlock, "padding: 8px 12px;"); const summaryStrongBlock = cssBlock(".commission-summary-strip .summary-copy strong"); assertIncludes(summaryStrongBlock, "font-size: 20px;"); const listPanelBlock = cssBlock(".commission-list-panel"); assertIncludes(listPanelBlock, "overflow: hidden;"); const listHeaderBlock = cssBlock(".commission-list-header"); assertIncludes(listHeaderBlock, "align-items: center;"); const filterBlock = cssBlock(".commission-filter-bar"); assertIncludes(filterBlock, "grid-template-columns: minmax(220px, 420px) 180px 104px minmax(0, 1fr);"); assertIncludes(filterBlock, "align-items: end;"); const searchButtonBlock = cssBlock(".commission-search-button"); assertIncludes(searchButtonBlock, "width: 104px;"); const rowActionsBlock = cssBlock(".commission-row-actions"); assertIncludes(rowActionsBlock, "display: inline-flex;"); assertIncludes(rowActionsBlock, "justify-content: flex-end;"); const actionIconBlock = cssBlock(".table-action-icon"); assertIncludes(actionIconBlock, "width: 34px;"); assertIncludes(actionIconBlock, "height: 34px;"); const modalBlock = cssBlock(".commission-modal"); assertIncludes(modalBlock, "width: min(900px, 100%);"); assertIncludes(modalBlock, "max-height: min(720px, calc(100vh - 48px));"); const formBlock = cssBlock(".commission-form"); assertIncludes(formBlock, "padding: 14px var(--panel-padding) var(--panel-padding);"); const formGridBlock = cssBlock(".commission-form-grid"); assertIncludes(formGridBlock, "grid-template-columns: minmax(220px, 1.2fr) minmax(160px, 0.75fr) minmax(190px, 1fr) minmax(150px, 0.7fr);"); assertIncludes(formGridBlock, "gap: 10px 14px;"); const remarkFieldBlock = cssBlock(".commission-remark-field textarea"); assertIncludes(remarkFieldBlock, "min-height: 44px;"); assertIncludes(remarkFieldBlock, "max-height: 76px;"); const formActionsBlock = cssBlock(".commission-form-actions"); assertIncludes(formActionsBlock, "align-self: end;"); assertIncludes(formActionsBlock, "justify-content: flex-end;"); assertIncludes(css, ".commission-business-field,\n .commission-remark-field,\n .commission-form-actions {\n grid-column: auto;"); assertIncludes(css, ".commission-summary-strip,\n .commission-form-grid,\n .commission-filter-bar {\n grid-template-columns: 1fr;"); function cssBlock(selector: string): string { const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{([\\s\\S]*?)\\n\\}`, "m"); const match = css.match(pattern); if (!match) { throw new Error(`缺少样式块 ${selector}`); } return match[1]; } function assertIncludes(content: string, expected: string): void { if (!content.includes(expected)) { throw new Error(`期望包含 ${expected}`); } } function assertNotIncludes(content: string, unexpected: string): void { if (content.includes(unexpected)) { throw new Error(`不应包含 ${unexpected}`); } } function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }