c68dddfc01
新增 ONLYOFFICE 配置签名、内部内容接口、容器编排与反向代理。 打通网页端和桌面端独立预览工作区,完善文档入口、布局及帮助体验。 补充桌面安全发布门禁、开发脚本、使用文档和前后端测试。
207 lines
5.9 KiB
JavaScript
207 lines
5.9 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
|
|
const main = readFileSync("src/styles/main.css", "utf8");
|
|
const unified = readFileSync("src/styles/unified-page.css", "utf8");
|
|
const webLayout = readFileSync("src/components/WebLayout.vue", "utf8");
|
|
|
|
const requiredMainTokens = [
|
|
"--ctms-primary",
|
|
"--ctms-text-main",
|
|
"--ctms-bg-base",
|
|
"--ctms-radius",
|
|
"--ctms-shadow",
|
|
"--ctms-brand-900",
|
|
"--ctms-brand-700",
|
|
"--ctms-neutral-100",
|
|
"--ctms-neutral-300",
|
|
"--ctms-focus-ring"
|
|
];
|
|
|
|
const requiredUnifiedTokens = [
|
|
"--unified-shell-bg",
|
|
"--unified-shell-radius",
|
|
".unified-action-bar",
|
|
".unified-section",
|
|
".ctms-page-shell",
|
|
".ctms-page-header-row",
|
|
".ctms-page-content-grid"
|
|
];
|
|
|
|
const missing = [
|
|
...requiredMainTokens.filter((t) => !main.includes(t)),
|
|
...requiredUnifiedTokens.filter((t) => !unified.includes(t))
|
|
];
|
|
|
|
const requiredWebEdgeTokens = [
|
|
".web-layout-container .ctms-route-shell > *",
|
|
".web-layout-container .ctms-route-shell > .page > .table-card"
|
|
];
|
|
|
|
for (const token of requiredWebEdgeTokens) {
|
|
if (!main.includes(token)) {
|
|
missing.push(`src/styles/main.css:${token}`);
|
|
}
|
|
}
|
|
|
|
if (!/\.web-layout-container \.content-wrapper\s*\{[^}]*padding:\s*0;/s.test(webLayout)) {
|
|
missing.push("src/components/WebLayout.vue:web content wrapper edge alignment");
|
|
}
|
|
|
|
if (!/\.web-layout-container \.content-wrapper\s*\{[^}]*height:\s*100%;[^}]*min-height:\s*0;/s.test(webLayout)) {
|
|
missing.push("src/components/WebLayout.vue:web content height chain");
|
|
}
|
|
|
|
if (webLayout.includes("padding: 18px 24px 24px;") || webLayout.includes("padding: 22px 32px 32px;")) {
|
|
missing.push("src/components/WebLayout.vue:large-screen content padding override");
|
|
}
|
|
|
|
const pageContractChecks = [
|
|
{
|
|
file: "src/views/ia/ProjectMilestones.vue",
|
|
groups: [
|
|
["ctms-page-shell", "page"],
|
|
["unified-action-bar", "table-card-toolbar"],
|
|
["ctms-table-card", "table-card"]
|
|
]
|
|
},
|
|
{
|
|
file: "src/views/ia/SubjectManagement.vue",
|
|
groups: [
|
|
["ctms-page-shell", "page"],
|
|
["unified-action-bar", "table-card-toolbar"],
|
|
["ctms-table-card", "table-card"],
|
|
["subject-table"]
|
|
]
|
|
},
|
|
{
|
|
file: "src/views/ia/RiskIssueSae.vue",
|
|
groups: [
|
|
["ctms-page-shell", "page"],
|
|
["unified-action-bar", "table-card-toolbar"],
|
|
["ctms-table-card", "table-card"],
|
|
["risk-table"]
|
|
]
|
|
},
|
|
{
|
|
file: "src/views/ia/RiskIssuePd.vue",
|
|
groups: [
|
|
["ctms-page-shell", "page"],
|
|
["unified-action-bar", "table-card-toolbar"],
|
|
["ctms-table-card", "table-card"],
|
|
["risk-table"]
|
|
]
|
|
},
|
|
{
|
|
file: "src/views/ia/RiskIssueMonitoringVisits.vue",
|
|
groups: [
|
|
["ctms-page-shell", "page"],
|
|
["unified-action-bar", "monitoring-toolbar", "template-toolbar", "toolbar"],
|
|
["ctms-table-card", "table-card", "monitoring-table", "issue-table-panel"],
|
|
["issue-table"]
|
|
]
|
|
}
|
|
];
|
|
|
|
for (const { file, groups } of pageContractChecks) {
|
|
const content = readFileSync(file, "utf8");
|
|
for (const group of groups) {
|
|
if (!group.some((className) => content.includes(className))) {
|
|
missing.push(`${file}:${group.join("|")}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
const overview = readFileSync("src/views/ia/ProjectOverview.vue", "utf8");
|
|
const requiredOverviewGroups = [
|
|
["ctms-page-shell", "page"],
|
|
["kpi", "overview-card"],
|
|
["unified-section", "overview-container"]
|
|
];
|
|
|
|
for (const group of requiredOverviewGroups) {
|
|
if (!group.some((className) => overview.includes(className))) {
|
|
missing.push(`src/views/ia/ProjectOverview.vue:${group.join("|")}`);
|
|
}
|
|
}
|
|
|
|
const unifiedShellPages = [
|
|
"src/views/fees/ContractFees.vue",
|
|
"src/views/documents/DocumentList.vue"
|
|
];
|
|
|
|
const requiredUnifiedShellClasses = [
|
|
"ctms-page-shell",
|
|
"unified-action-bar",
|
|
"unified-shell"
|
|
];
|
|
|
|
for (const file of unifiedShellPages) {
|
|
const content = readFileSync(file, "utf8");
|
|
for (const className of requiredUnifiedShellClasses) {
|
|
if (!content.includes(className)) {
|
|
missing.push(`${file}:${className}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
const router = readFileSync("src/router/index.ts", "utf8");
|
|
const legacyFileVersionRouteIndex = router.indexOf('name: "FileVersionManagement"');
|
|
const canonicalDocumentRouteIndex = router.indexOf('path: "trial/:trialId/documents"', legacyFileVersionRouteIndex);
|
|
const legacyFileVersionRoute =
|
|
legacyFileVersionRouteIndex >= 0 && canonicalDocumentRouteIndex > legacyFileVersionRouteIndex
|
|
? router.slice(legacyFileVersionRouteIndex, canonicalDocumentRouteIndex)
|
|
: "";
|
|
|
|
if (router.includes("FileVersionManagement.vue")) {
|
|
missing.push("src/router/index.ts:legacy FileVersionManagement component import");
|
|
}
|
|
|
|
if (!legacyFileVersionRoute.includes("component: DocumentList")) {
|
|
missing.push("src/router/index.ts:FileVersionManagement->DocumentList");
|
|
}
|
|
|
|
if (!router.includes('next({ name: "DocumentList", params: { trialId: studyStore.currentStudy.id }, replace: true });')) {
|
|
missing.push("src/router/index.ts:legacy file version canonical redirect");
|
|
}
|
|
|
|
const adminProjectDetail = readFileSync("src/views/admin/ProjectDetail.vue", "utf8");
|
|
const requiredAdminProjectDetailClasses = [
|
|
"ctms-page-shell",
|
|
"unified-action-bar",
|
|
"unified-section"
|
|
];
|
|
|
|
for (const className of requiredAdminProjectDetailClasses) {
|
|
if (!adminProjectDetail.includes(className)) {
|
|
missing.push(`src/views/admin/ProjectDetail.vue:${className}`);
|
|
}
|
|
}
|
|
|
|
const stateComponents = [
|
|
"src/components/StateLoading.vue",
|
|
"src/components/StateEmpty.vue",
|
|
"src/components/StateError.vue"
|
|
];
|
|
|
|
const requiredStateClasses = [
|
|
"ctms-state",
|
|
"ctms-state-title",
|
|
"ctms-state-desc"
|
|
];
|
|
|
|
for (const file of stateComponents) {
|
|
const content = readFileSync(file, "utf8");
|
|
for (const className of requiredStateClasses) {
|
|
if (!content.includes(className)) {
|
|
missing.push(`${file}:${className}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (missing.length) {
|
|
console.error("UI contract check failed. Missing:", missing.join(", "));
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log("UI contract check passed");
|