docs: add development plan and technical design
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,6 +25,7 @@ selenium-debug.log
|
||||
**/.idea/
|
||||
.vscode/
|
||||
**/.vscode/
|
||||
~$*.docx
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
|
||||
7
MES_Manage_View_V20/MacroinfManage_MES.code-workspace
Normal file
7
MES_Manage_View_V20/MacroinfManage_MES.code-workspace
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
]
|
||||
}
|
||||
77
MES_Manage_View_V20/今后开发计划_2026-05-27.md
Normal file
77
MES_Manage_View_V20/今后开发计划_2026-05-27.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# 今后开发计划与新增需求分析记录
|
||||
|
||||
生成日期:2026-05-27
|
||||
项目:MES_Manage_View_V20
|
||||
|
||||
## 一、排期口径
|
||||
|
||||
- 按“最终调整”数字从小到大排序。
|
||||
- 从 2026-05-28 开始排期。
|
||||
- 按工作日递推,跳过周六、周日。
|
||||
- 跳过端午节、中秋节、国庆节法定放假日期。
|
||||
- 调休日按工作日处理。
|
||||
- 暂按单开发线串行排期。
|
||||
|
||||
节假日口径:
|
||||
|
||||
- 端午节:2026-06-19 至 2026-06-21。
|
||||
- 中秋节:2026-09-25 至 2026-09-27。
|
||||
- 国庆节:2026-10-01 至 2026-10-07。
|
||||
- 调休上班日:2026-09-20、2026-10-10。
|
||||
|
||||
## 二、开发计划排期
|
||||
|
||||
| 顺序 | 需求 | 预估 | 开始 | 结束 | 备注 |
|
||||
|---:|---|---:|---|---|---|
|
||||
| 1 | 及时开工报表 | 1天 | 5.28 | 5.29 | 增加正选/反选查询 |
|
||||
| 2 | 机加件排产-表单 | 4天 | 5.29 | 6.04 | 日期筛选优化 + 历史工时查询;SAP新增字段不算MES开发 |
|
||||
| 3 | 装配已排产需求 | 12天 | 6.04 | 6.23 | 大需求,建议分阶段交付 |
|
||||
| 4 | 生产日报-送检(机加/装配) | 3天 | 6.23 | 6.26 | 建议统一报表,部门做筛选维度 |
|
||||
| 5 | 及时齐套跟踪结果报表 | 5天 | 6.26 | 7.03 | 依赖SAP缺件明细实时接口 |
|
||||
| 6 | 生产计划跟踪 | 3天 | 7.03 | 7.08 | 工序状态、颜色、标准工时、订单状态 |
|
||||
| 7 | 外协任务检验 | 3天 | 7.08 | 7.13 | 增加指派对象、供应商、SAP采购收货单号 |
|
||||
| 8 | 异常管控 | 5天 | 7.13 | 7.20 | 报工页面直接报异常,多审批人可见一人处理 |
|
||||
| 9 | 机加班次产量表 | 3天 | 7.20 | 7.23 | 新增报表 |
|
||||
| 10 | 发货通知列表 | 1天 | 7.23 | 7.24 | SAP数据已提供,可新增查询报表 |
|
||||
| 11 | 质检任务早知道 | 1天 | 7.24 | 7.27 | 依赖“装配已排产需求”的装配完成时间 |
|
||||
| 12 | 生产工艺 | 2天 | 7.27 | 7.29 | 无生产工艺时自动带出标准工艺 |
|
||||
| 13 | 工时报表 + 备注类小改 | 2天 | 7.29 | 7.31 | 指派对象、机加领料备注、机加/装配退库备注、保存提示等合并处理 |
|
||||
| 14 | 生产订单关闭 | 4天 | 7.31 | 8.06 | 批量更新SAP订单状态,风险较高 |
|
||||
| 15 | 及时检验报表 | 4天 | 8.06 | 8.12 | 报工超过2天未检验 |
|
||||
| 16 | 工序流转卡 | 1天 | 8.12 | 8.13 | 增加打印原料编码 |
|
||||
| 17 | 一楼质检大屏幕 | 4天 | 8.13 | 8.19 | 及时率、关键工序 |
|
||||
| 18 | 机加现场大屏幕 | 3天 | 8.19 | 8.24 | 缺勤、关键工序 |
|
||||
| 19 | 装配现场大屏幕 | 4天 | 8.24 | 8.28 | 缺勤、及时送检 |
|
||||
| 20 | 编程任务清单 | 4天 | 8.28 | 9.03 | 按装配类似逻辑做编程报工 |
|
||||
| 21 | 设备档案管理 | 2天 | 9.03 | 9.07 | 需先确认字段归属 |
|
||||
| 22 | 设备数据采集 | 8天 | 9.07 | 9.17 | 采集接口、落库、状态汇总 |
|
||||
| 23 | 设备指标/OEE | 6天 | 9.17 | 9.24 | 依赖设备采集和标准工时口径 |
|
||||
| 24 | 工时查询-设备采集联动 | 2天 | 9.24 | 9.29 | 程序工时字段对接采集数据 |
|
||||
| 25 | 手续确认报表 | 3天 | 9.29 | 10.09 | 装配/电气齐套列表 + 手续确认 |
|
||||
| 26 | 产品合格率(工位) | 3天 | 10.09 | 10.13 | 需确认良品/总产量口径 |
|
||||
| 27 | 让步接收记录 | 2天 | 10.13 | 10.15 | 新增查询页面 |
|
||||
| 28 | 设计任务报工模块 | 6天 | 10.15 | 10.23 | MES自建设计任务、设计工时 |
|
||||
| 29 | 工时异常报表 | 2天 | 10.23 | 10.27 | 依赖标准工时和实际单件工时 |
|
||||
| 30 | 正在加工状态 | 1天 | 10.27 | 10.28 | 机加正在进行中状态 |
|
||||
|
||||
## 三、暂不直接排期 / 需等前置条件
|
||||
|
||||
| 需求 | 原因 |
|
||||
|---|---|
|
||||
| 及时拣配(机加)MES | 等SAP数据、条码规则、状态写回规则 |
|
||||
| 及时拣配(装配)MES | 等SAP数据、条码规则、状态写回规则 |
|
||||
| 及时送检报表 | 需确认发料齐套时间/SAP字段是否稳定;若SAP数据已到,可插入0.5天 |
|
||||
| 工艺内容修改 | 已开工任务怎么处理未定 |
|
||||
| 标牌打印操作 | 需求未确定 |
|
||||
| 首检、巡检 | 需求内容为空,无法估时 |
|
||||
| 外协采购订单收检结果 | 需求内容为空,无法估时 |
|
||||
| 不合格品单据打印 | 需求内容为空,无法估时 |
|
||||
|
||||
## 四、排期结论
|
||||
|
||||
按以上单线工作日计划,已明确可做事项预计到 2026-10-28 完成。
|
||||
|
||||
实际执行建议先做 5.28 到 6.23 的前三项,因为它们优先级最高,且“装配已排产需求”会影响后面的质检早知道、日计划、及时送检等需求。
|
||||
|
||||
## 五、新增需求分析与开发记录原文
|
||||
|
||||
BIN
MES_Manage_View_V20/今后开发计划与新增需求分析记录_2026-05-27.docx
Normal file
BIN
MES_Manage_View_V20/今后开发计划与新增需求分析记录_2026-05-27.docx
Normal file
Binary file not shown.
@@ -1090,8 +1090,7 @@
|
||||
9. 看板:生产、装配、质检、机加、综合大屏。
|
||||
10. 新增需求页面:手续确认、及时检验、装配已排产需求、齐套跟踪等。
|
||||
|
||||
## 二十一、逐页详细操作说明(按 src/views 页面)
|
||||
|
||||
## 二十一、逐页详细操作说明
|
||||
本章按源码页面逐页编写。每个页面都从实际 `v-model`、表格列、按钮、弹窗、页签、存储过程和 SAP/WMS 调用中提取操作线索,转换成用户可执行的步骤。
|
||||
|
||||
通用阅读方法:
|
||||
|
||||
BIN
MES_Manage_View_V20/技术开发详细方案_2026-05-27.docx
Normal file
BIN
MES_Manage_View_V20/技术开发详细方案_2026-05-27.docx
Normal file
Binary file not shown.
3441
MES_Manage_View_V20/技术开发详细方案_2026-05-27.md
Normal file
3441
MES_Manage_View_V20/技术开发详细方案_2026-05-27.md
Normal file
File diff suppressed because it is too large
Load Diff
731
analyze_mes_v20_views.js
Normal file
731
analyze_mes_v20_views.js
Normal file
@@ -0,0 +1,731 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const root = path.resolve('MES_Manage_View_V20');
|
||||
const viewsRoot = path.join(root, 'src', 'views');
|
||||
const outFile = path.join(root, 'MES_Manage_View_V20_src_views_逐页面详细分析.md');
|
||||
|
||||
function existingSqlFiles() {
|
||||
return fs.readdirSync(root)
|
||||
.filter(x => x.toLowerCase().endsWith('.sql'))
|
||||
.map(x => path.join(root, x));
|
||||
}
|
||||
|
||||
function readText(file) {
|
||||
const buf = fs.readFileSync(file);
|
||||
if (buf.length >= 2 && buf[0] === 0xff && buf[1] === 0xfe) return buf.slice(2).toString('utf16le');
|
||||
if (buf.length >= 2 && buf[0] === 0xfe && buf[1] === 0xff) {
|
||||
const swapped = Buffer.alloc(buf.length - 2);
|
||||
for (let i = 2; i + 1 < buf.length; i += 2) {
|
||||
swapped[i - 2] = buf[i + 1];
|
||||
swapped[i - 1] = buf[i];
|
||||
}
|
||||
return swapped.toString('utf16le');
|
||||
}
|
||||
if (buf.length >= 3 && buf[0] === 0xef && buf[1] === 0xbb && buf[2] === 0xbf) return buf.slice(3).toString('utf8');
|
||||
return buf.toString('utf8');
|
||||
}
|
||||
|
||||
function walk(dir, exts, result = []) {
|
||||
if (!fs.existsSync(dir)) return result;
|
||||
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const p = path.join(dir, item.name);
|
||||
if (item.isDirectory()) walk(p, exts, result);
|
||||
else if (exts.includes(path.extname(item.name).toLowerCase())) result.push(p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function rel(file) {
|
||||
return path.relative(root, file).replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
function lineOf(text, index) {
|
||||
return text.slice(0, index).split(/\r?\n/).length;
|
||||
}
|
||||
|
||||
function uniq(arr) {
|
||||
return [...new Set((arr || []).filter(Boolean))];
|
||||
}
|
||||
|
||||
function clean(s) {
|
||||
return (s || '')
|
||||
.replace(/<[^>]*>/g, '')
|
||||
.replace(/\{\{.*?\}\}/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function escapeReg(s) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function isInComment(text, index) {
|
||||
const lineStart = text.lastIndexOf('\n', index) + 1;
|
||||
const beforeOnLine = text.slice(lineStart, index);
|
||||
if (/^\s*\/\//.test(beforeOnLine)) return true;
|
||||
const blockStart = text.lastIndexOf('/*', index);
|
||||
const blockEnd = text.lastIndexOf('*/', index);
|
||||
return blockStart > blockEnd;
|
||||
}
|
||||
|
||||
function moduleNameFromPath(relPath) {
|
||||
const parts = relPath.split('/');
|
||||
if (parts[0] !== 'src' || parts[1] !== 'views') return '其他';
|
||||
if (parts.length <= 3) return parts[2] || '根页面';
|
||||
return `${parts[2]} / ${parts.slice(3, -1).join(' / ') || path.basename(relPath)}`;
|
||||
}
|
||||
|
||||
function extractLabels(text) {
|
||||
const labels = [];
|
||||
const patterns = [
|
||||
/<el-button\b[^>]*>([\s\S]*?)<\/el-button>/g,
|
||||
/\blabel="([^"]+)"/g,
|
||||
/\bplaceholder="([^"]+)"/g,
|
||||
/\btitle="([^"]+)"/g
|
||||
];
|
||||
for (const re of patterns) {
|
||||
let m;
|
||||
while ((m = re.exec(text))) {
|
||||
const value = clean(m[1]);
|
||||
if (value && !/[{};]/.test(value) && value.length <= 48) labels.push(value);
|
||||
}
|
||||
}
|
||||
return uniq(labels).slice(0, 30);
|
||||
}
|
||||
|
||||
function extractFrontendFields(text) {
|
||||
const vModels = [];
|
||||
const tableColumns = [];
|
||||
const pushedParams = [];
|
||||
|
||||
let m;
|
||||
const vmRe = /\bv-model(?:\.[\w-]+)?="([^"]+)"/g;
|
||||
while ((m = vmRe.exec(text))) vModels.push(clean(m[1]));
|
||||
|
||||
const colRe = /<el-table-column\b([^>]*)>/g;
|
||||
while ((m = colRe.exec(text))) {
|
||||
const attrs = m[1];
|
||||
const label = /label="([^"]+)"/.exec(attrs)?.[1] || '';
|
||||
const prop = /prop="([^"]+)"/.exec(attrs)?.[1] || '';
|
||||
if (label || prop) tableColumns.push({ label: clean(label), prop: clean(prop) });
|
||||
}
|
||||
|
||||
const paramRe = /\.(?:push|unshift)\s*\(\s*\[\s*['"]([^'"]+)['"]\s*,\s*([^\]\n]+)\]/g;
|
||||
while ((m = paramRe.exec(text))) pushedParams.push({ name: clean(m[1]), expr: clean(m[2]) });
|
||||
|
||||
return {
|
||||
vModels: uniq(vModels).slice(0, 80),
|
||||
tableColumns: uniq(tableColumns.map(x => `${x.label}|${x.prop}`))
|
||||
.map(x => {
|
||||
const [label, prop] = x.split('|');
|
||||
return { label, prop };
|
||||
})
|
||||
.slice(0, 120),
|
||||
pushedParams: uniq(pushedParams.map(x => `${x.name}|${x.expr}`))
|
||||
.map(x => {
|
||||
const [name, expr] = x.split('|');
|
||||
return { name, expr };
|
||||
})
|
||||
.slice(0, 120)
|
||||
};
|
||||
}
|
||||
|
||||
function extractMethods(text) {
|
||||
const methods = [];
|
||||
const methodBlock = /methods\s*:\s*\{([\s\S]*?)\n\s*\}/m.exec(text);
|
||||
const src = methodBlock ? methodBlock[1] : text;
|
||||
const re = /^\s*([A-Za-z_$\u4e00-\u9fa5][\w$\u4e00-\u9fa5]*)\s*\([^)]*\)\s*\{/gm;
|
||||
let m;
|
||||
while ((m = re.exec(src))) methods.push(m[1]);
|
||||
return uniq(methods).slice(0, 30);
|
||||
}
|
||||
|
||||
function extractCreateData(text) {
|
||||
const calls = [];
|
||||
const re = /CreateData\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g;
|
||||
let m;
|
||||
while ((m = re.exec(text))) {
|
||||
if (isInComment(text, m.index)) continue;
|
||||
calls.push({ type: m[1], name: m[2], line: lineOf(text, m.index) });
|
||||
}
|
||||
return calls;
|
||||
}
|
||||
|
||||
function extractSapCalls(text) {
|
||||
const calls = [];
|
||||
const re = /(getB1|postB1|patchB1|posttm)\s*\(\s*([`'"])([\s\S]*?)\2/g;
|
||||
let m;
|
||||
while ((m = re.exec(text))) {
|
||||
if (isInComment(text, m.index)) continue;
|
||||
const snippet = text.slice(m.index, m.index + 1200);
|
||||
const keys = [];
|
||||
const keyRe = /["']?([\u4e00-\u9fa5A-Za-z_][\u4e00-\u9fa5A-Za-z0-9_$]*)["']?\s*:/g;
|
||||
let km;
|
||||
while ((km = keyRe.exec(snippet)) && keys.length < 24) {
|
||||
if (!['if', 'for', 'while', 'switch', 'catch', 'function'].includes(km[1])) keys.push(km[1]);
|
||||
}
|
||||
calls.push({ fn: m[1], url: clean(m[3]), line: lineOf(text, m.index), payloadKeys: uniq(keys) });
|
||||
}
|
||||
return calls;
|
||||
}
|
||||
|
||||
function extractApiImports(text) {
|
||||
const imports = [];
|
||||
const re = /from\s+['"](@\/api\/[^'"]+)['"]/g;
|
||||
let m;
|
||||
while ((m = re.exec(text))) imports.push(m[1]);
|
||||
return uniq(imports);
|
||||
}
|
||||
|
||||
function stripSqlComments(sql) {
|
||||
return sql.replace(/\/\*[\s\S]*?\*\//g, ' ').replace(/--.*$/gm, ' ');
|
||||
}
|
||||
|
||||
function compactSqlLine(s) {
|
||||
return clean(s)
|
||||
.replace(/\s*,\s*/g, ', ')
|
||||
.replace(/\s*=\s*/g, ' = ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.slice(0, 240);
|
||||
}
|
||||
|
||||
function extractSqlLines(body, regex, limit) {
|
||||
return uniq(stripSqlComments(body)
|
||||
.split(/\r?\n/)
|
||||
.map(x => x.trim())
|
||||
.filter(x => regex.test(x))
|
||||
.map(compactSqlLine))
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
||||
function extractSelectColumns(body, limit = 10) {
|
||||
const noComments = stripSqlComments(body);
|
||||
const m = /\bSELECT\b([\s\S]{0,2500}?)\bFROM\b/i.exec(noComments);
|
||||
if (!m) return [];
|
||||
return uniq(m[1].split(/\r?\n/)
|
||||
.map(x => compactSqlLine(x.replace(/^\s*,/, '')))
|
||||
.filter(x => x && !/^(TOP|DISTINCT)$/i.test(x)))
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
||||
function objectRegex(name) {
|
||||
return new RegExp(`(?:\\[dbo\\]\\.\\[${escapeReg(name)}\\]|dbo\\.\\[${escapeReg(name)}\\]|\\[${escapeReg(name)}\\]|\\b${escapeReg(name)}\\b)`, 'i');
|
||||
}
|
||||
|
||||
function extractParams(body) {
|
||||
const header = body.split(/\bAS\b/i)[0] || body.slice(0, 1000);
|
||||
const params = [];
|
||||
const re = /@([\u4e00-\u9fa5A-Za-z0-9_]+)\s+([A-Za-z0-9_\[\]\(\),]+)/g;
|
||||
let m;
|
||||
while ((m = re.exec(header))) params.push(`@${m[1]} ${m[2]}`);
|
||||
return uniq(params).slice(0, 24);
|
||||
}
|
||||
|
||||
function summarizeProc(name, body) {
|
||||
const verbs = [];
|
||||
if (/\bSELECT\b/i.test(body)) verbs.push('查询');
|
||||
if (/\bINSERT\s+INTO\b/i.test(body)) verbs.push('新增');
|
||||
if (/\bUPDATE\b/i.test(body)) verbs.push('更新');
|
||||
if (/\bDELETE\s+FROM\b/i.test(body)) verbs.push('删除');
|
||||
if (/\bOPENJSON\b|JSON_VALUE|JSON_QUERY/i.test(body)) verbs.push('解析 JSON 批量数据');
|
||||
if (/ROW_NUMBER|OFFSET|@PageSize|@PageCurrent/i.test(body)) verbs.push('分页');
|
||||
if (/SAP\.SBO_YL/i.test(body)) verbs.push('读取 SAP B1 数据库对象');
|
||||
if (/报工|工时/.test(name + body)) verbs.push('处理报工/工时');
|
||||
if (/质检|检验|不合格/.test(name + body)) verbs.push('处理质检数据');
|
||||
if (/排产|派工|计划/.test(name + body)) verbs.push('处理计划/派工');
|
||||
if (/外协/.test(name + body)) verbs.push('处理外协业务');
|
||||
return uniq(verbs).join('、') || '执行数据库业务逻辑';
|
||||
}
|
||||
|
||||
function extractSqlModel() {
|
||||
const sql = existingSqlFiles().map(f => `\n-- FILE: ${path.basename(f)}\n${readText(f)}`).join('\n');
|
||||
const objects = [];
|
||||
const objectRe = /CREATE\s+(TABLE|VIEW)\s+(?:\[dbo\]\.)?\[([^\]]+)\]/gi;
|
||||
let m;
|
||||
while ((m = objectRe.exec(sql))) objects.push({ type: m[1].toUpperCase(), name: m[2] });
|
||||
|
||||
const markers = [];
|
||||
const procRe = /(?:CREATE|ALTER)\s+(?:\s*PROCEDURE|\s*PROC)\s+(?:\[dbo\]\.)?\[([^\]]+)\]/gi;
|
||||
while ((m = procRe.exec(sql))) markers.push({ index: m.index, name: m[1] });
|
||||
|
||||
const procByName = {};
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
const start = markers[i].index;
|
||||
const nextGo = sql.slice(start).search(/\r?\nGO\r?\n/i);
|
||||
const nextProc = i + 1 < markers.length ? markers[i + 1].index : sql.length;
|
||||
const end = nextGo >= 0 ? Math.min(start + nextGo, nextProc) : nextProc;
|
||||
const body = sql.slice(start, end);
|
||||
const deps = [];
|
||||
for (const obj of objects) {
|
||||
if (objectRegex(obj.name).test(body)) deps.push(`${obj.type}:${obj.name}`);
|
||||
}
|
||||
const sapRefs = uniq(body.match(/SAP\.SBO_YL\.dbo\.[A-Za-z0-9_]+/g) || []);
|
||||
for (const s of sapRefs) deps.push(`SAP:${s}`);
|
||||
const writes = [];
|
||||
for (const obj of objects) {
|
||||
const n = escapeReg(obj.name);
|
||||
const hit = new RegExp(`\\b(INSERT\\s+INTO|UPDATE|DELETE\\s+FROM|MERGE\\s+INTO)\\s+(?:\\[dbo\\]\\.\\[${n}\\]|dbo\\.\\[${n}\\]|\\[${n}\\]|${n})`, 'i').test(body);
|
||||
if (hit) writes.push(obj.name);
|
||||
}
|
||||
const noComments = stripSqlComments(body);
|
||||
procByName[markers[i].name] = {
|
||||
name: markers[i].name,
|
||||
body,
|
||||
params: extractParams(body),
|
||||
deps: uniq(deps),
|
||||
writes: uniq(writes),
|
||||
sapRefs,
|
||||
summary: summarizeProc(markers[i].name, body),
|
||||
features: {
|
||||
selectColumns: extractSelectColumns(body),
|
||||
filters: extractSqlLines(body, /\b(WHERE|AND|OR)\b/i, 10),
|
||||
updates: extractSqlLines(body, /\b(UPDATE|SET|INSERT\s+INTO|DELETE\s+FROM|MERGE\s+INTO)\b/i, 12),
|
||||
statusLines: extractSqlLines(body, /状态|排产|派工|加工|任务|质检|关闭|完成|已报工|DocEntry|SAP/i, 18),
|
||||
grouping: extractSqlLines(body, /\b(GROUP\s+BY|HAVING|ORDER\s+BY|ROW_NUMBER|OFFSET|FETCH\s+NEXT)\b/i, 8),
|
||||
tempTables: uniq(noComments.match(/#[\u4e00-\u9fa5A-Za-z0-9_]+/g) || []).slice(0, 10),
|
||||
hasJson: /\bOPENJSON\b|JSON_VALUE|JSON_QUERY/i.test(noComments),
|
||||
hasTransaction: /\bBEGIN\s+TRAN|COMMIT\s+TRAN|ROLLBACK\s+TRAN/i.test(noComments),
|
||||
hasTryCatch: /\bTRY\b|\bCATCH\b/i.test(noComments),
|
||||
hasPagination: /@PageCurrent|@PageSize|ROW_NUMBER|OFFSET\s+.*FETCH/i.test(noComments),
|
||||
hasSap: /SAP\.SBO_YL/i.test(noComments)
|
||||
}
|
||||
};
|
||||
}
|
||||
return { objects, procByName };
|
||||
}
|
||||
|
||||
function operationByType(type) {
|
||||
return {
|
||||
'1': '旧版查询',
|
||||
'2': '旧版增删改',
|
||||
'7': '旧版批量/JSON',
|
||||
'11': '当前通用存储过程查询/执行',
|
||||
'12': '当前通用存储过程写入/业务动作',
|
||||
'2001': '导出/报表类过程'
|
||||
}[type] || `自定义类型 ${type}`;
|
||||
}
|
||||
|
||||
function procPurpose(name) {
|
||||
const rules = [
|
||||
[/查询|Get|Load|Query|Search|读取|查看/, '查询/读取数据'],
|
||||
[/新增|增加|Add|Insert/, '新增业务数据'],
|
||||
[/修改|编辑|Edit|Update|更新|保存|Save/, '更新/保存业务数据'],
|
||||
[/删除|Delete/, '删除业务数据'],
|
||||
[/Finish|确认|关闭|Close/, '确认/关闭/完成业务动作'],
|
||||
[/派工|Planned|排产|计划/, '计划排产或派工'],
|
||||
[/报工|工时|WorkTime|ProdReport/, '报工或工时处理'],
|
||||
[/质检|检验|Check|Quality/, '质量检验处理'],
|
||||
[/外协/, '外协业务处理'],
|
||||
[/登录|人员|角色|菜单|权限/, '基础权限/人员数据处理'],
|
||||
[/设备|工位|资源|Station|Resource/, '设备/工位/资源数据处理']
|
||||
];
|
||||
for (const [re, purpose] of rules) if (re.test(name)) return purpose;
|
||||
return '业务存储过程调用';
|
||||
}
|
||||
|
||||
function businessDomainFromName(name) {
|
||||
const rules = [
|
||||
[/登录|人员|角色|菜单|权限|部门|工位管理/, '系统基础资料/权限'],
|
||||
[/设备|报警|CaChe|StationStatus|工位设备/, '设备与实时数据'],
|
||||
[/排产|派工|OrderPlanned|计划/, '计划排产'],
|
||||
[/ProductCenter|ProductTask|加工中心|装配中心|报工|收料|开工|退库|发料|工时/, '生产执行/工时'],
|
||||
[/外协/, '外协协同'],
|
||||
[/图纸|工艺|Process|Drawing|Procedure|产品结构|标准/, '工艺与图纸'],
|
||||
[/质检|检验|质量|不合格|Check/, '质量检验'],
|
||||
[/销售/, '销售/订单状态'],
|
||||
[/班次/, '班次与人员排班'],
|
||||
[/加急/, '加急任务']
|
||||
];
|
||||
for (const [re, domain] of rules) if (re.test(name)) return domain;
|
||||
return '通用业务';
|
||||
}
|
||||
|
||||
function pageBusinessNarrative(page) {
|
||||
const p = page.relPath;
|
||||
const labels = page.labels.slice(0, 10);
|
||||
const procNames = uniq(page.procs.map(x => x.name)).slice(0, 6);
|
||||
const common = [];
|
||||
if (labels.length) common.push(`页面可见的操作/字段包括 ${labels.map(x => `“${x}”`).join('、')},这些是判断页面业务范围的主要线索。`);
|
||||
if (procNames.length) common.push(`后端主要围绕 ${procNames.map(x => `\`${x}\``).join('、')}${page.procs.length > 6 ? ' 等过程' : ''} 取数或提交业务动作。`);
|
||||
const rules = [
|
||||
[/PunctualWork/, '该页面用于发现“计划应开工但现场未及时开工”的任务。用户按任务状态、工位、工序、异常原因等条件筛选,页面展示生产订单、销售订单、物料、工序、指派对象、分配数量、上序合格数、加工/收料状态和加急数量,服务于生产异常提醒和催办。'],
|
||||
[/CraftMaintain/, '该页面维护产品结构树。用户可以新增、编辑、删除产品结构节点,查询现有产品结构,并为后续工艺、物料、BOM 或生产任务提供层级基础数据。'],
|
||||
[/DeviceDataHistory/, '该页面查询设备历史采集数据。用户按工位/设备、参数名称、起止时间分页查询历史记录,用于追溯设备运行参数。'],
|
||||
[/DeviceInformation/, '该页面维护设备台账和设备相关基础资料。用户可以查询设备信息,新增、修改、删除设备,维护维修记录、工时记录或点检配置。'],
|
||||
[/DeviceRealData/, '该页面展示设备实时或近实时数据。用户按工位和数据类型查询当前采集值、统计值或状态,通常用于设备运行监控。'],
|
||||
[/Devicedata/, '该页面面向设备采集数据管理,查询缓存变量和设备数据,支持按设备、参数、时间等维度查看实时或历史采集结果。'],
|
||||
[/InstallMakePlanDo|InstallMakePlan/, '该页面处理装配件排产。用户查询待排产/已排产装配任务,选择工位和人员,修改计划开工/完成时间、优先级、说明,执行单条派工、二次派工、批量确认排产,并在确认时同步 SAP 生产订单状态或创建相关请求。'],
|
||||
[/SelfMakePlandone|SelfMakePlando|SelfMakePlan/, '该页面处理自制件/机加件排产。用户查询生产订单工序任务,筛选订单、合同、物料、工序、计划时间、排产状态和指派对象,完成工位派工、人员二次派工、外协分组、计划说明维护和排产确认。'],
|
||||
[/PlannOrderClose/, '该页面用于生产订单关闭。用户筛选 SAP 未关闭或 MES 待确认订单,执行 SAP 生产订单关闭,并调用 MES 过程记录或确认订单关闭状态。'],
|
||||
[/PlanShell/, '该页面是计划外壳/计划跟踪类页面,按缺料、发货通知、完成状态等条件查询计划或装配相关任务,用于计划人员集中查看订单齐套、发货和关闭情况。'],
|
||||
[/priorityinstall/, '该页面用于装配优先级调整。用户查询装配计划任务,修改计划开工/完成时间、派工对象或优先级,用于插单和重点订单排序。'],
|
||||
[/PartDrawing/, '该页面维护零件图纸。用户按物料查询图纸、查看文件、验证名称、删除图纸并保留删除记录。'],
|
||||
[/PartDrawlook/, '该页面偏只读查看零件图纸,通常给生产或质检人员按物料快速打开图纸。'],
|
||||
[/ProcurementDrawlook/, '该页面提供采购场景的图纸查看。用户按采购物料查询图纸,查看或删除图纸记录,支持采购、外协、来料检验时查阅技术文件。'],
|
||||
[/ProcessDrawing/, '该页面维护工艺图纸。用户按物料/工艺条件查询工艺图,验证名称,查看或删除工艺图纸。'],
|
||||
[/ProcedureManagement/, '该页面维护工序/路线阶段主数据。新增或修改工序时会同步 SAP B1 的 RouteStages,保证 MES 工序与 SAP 工艺阶段一致。'],
|
||||
[/ProcessesDevelop/, '该页面用于工艺开发和生产工艺路线维护。用户围绕物料、工序、顺序、工艺参数、图纸等信息维护标准或生产用工艺路线。'],
|
||||
[/ProcessInquiry/, '该页面用于工艺路线查询。用户按物料、产品、工序等条件查看标准工艺或生产工艺,服务于排产、生产和质检核对。'],
|
||||
[/ProcessProduct/, '该页面维护产品与工艺/图纸的关系,使产品、物料、工序、图纸等基础数据形成可查询的工艺资料。'],
|
||||
[/ProductionOrder/, '该页面处理生产订单资料。用户查询 ERP/MES 生产订单,并通过过程把 SAP 生产订单同步成 MES 生产计划和工序任务。'],
|
||||
[/MachiningCenter/, '该页面是机加生产执行主界面。操作员按工位查看可执行任务和进行中任务,完成收料、开始加工、暂停/恢复、报工、辅助工序、首检/巡检/序检、生产发料查询、退库、图纸查看等动作,并写入加工中心操作记录和报工记录。'],
|
||||
[/AssemblyCenter/, '该页面是装配生产执行主界面。装配人员按工位查询装配任务,处理开工、结束/报工、送检、缺料查询、收检记录、图纸查看和退库等动作,并会读取 SAP 生产订单信息辅助判断。'],
|
||||
[/AssembleTaskManage/, '该页面管理装配任务。用户查询装配生产任务、查看工时汇总,进行派工、任务说明维护和外协/生产通知。'],
|
||||
[/ProductionTaskManage/, '该页面管理生产任务。用户按工位、订单、工序查询任务,查看工时汇总,执行派工、修改计划字段、维护任务说明和通知状态。'],
|
||||
[/ProductionTasksSum/, '该页面做生产任务汇总。用户按订单、工序、工位、时间等维度查询生产任务和工时汇总,并可编辑计划说明或派工信息。'],
|
||||
[/ProductionPlanTrack/, '该页面跟踪生产计划执行。用户查看计划任务的开工、完工、进度、延期或异常情况,用于计划员追踪订单。'],
|
||||
[/AssemblePlanTrack/, '该页面跟踪装配计划执行,展示装配任务进度、计划完成情况和异常。'],
|
||||
[/AssembleTasksSum/, '该页面汇总装配任务,按订单、工位、人员或状态统计装配执行情况。'],
|
||||
[/AssembleStatus/, '该页面查看装配现场状态,重点展示进行中任务和当前工位状态。'],
|
||||
[/DeviceStatus/, '该页面查看设备/工位生产状态,通常用于判断设备是否在加工、待机、报警或空闲。'],
|
||||
[/OutsourceMange/, '该页面管理外协任务。用户查询可外协工序,按供应商/订单/工序分组,执行外协派工并同步 SAP 采购请求或相关单据,维护外协发料、收料、质检记录、打印次数和备注。'],
|
||||
[/SaleOutsourceMange/, '该页面管理销售相关外协任务。业务与外协管理类似,但面向销售订单或销售外协场景,包含派工同步 SAP、外协发料、历史记录和质检记录查询。'],
|
||||
[/OutsourceSheet/, '该页面输出外协报表。用户按订单、合同、产品、计划号、供应商、状态分页查询外协报表,也可导出,用于外协进度、发料、收料、质检、开票等跟踪。'],
|
||||
[/PickingTask/, '该页面处理拣配任务。用户查询生产订单相关拣配/领料任务,并可能联动 SAP 生产订单或库存出入库状态。'],
|
||||
[/PersonnelBinding/, '该页面维护工位与人员绑定关系。用于限制或提示哪些人员可在指定工位报工、开工或执行任务。'],
|
||||
[/ShiftManagement/, '该页面维护班次和班次人员绑定。用户新增、修改、删除班次,给班次绑定人员,为工时计算和报工归属提供基础数据。'],
|
||||
[/Timesheet/, '该页面统计工时。用户按项目、作业者、设备、任务等维度查询工时数据,并可导出报表。'],
|
||||
[/WorkHoursCheck/, '该页面用于工时校验。质检/管理人员查询待校验报工记录,系统计算标准/实际工时,保存单条或批量校验结果;通过校验后可把工序资源或工时/出库类单据上传到 SAP。'],
|
||||
[/WorkhoursEdit/, '该页面用于编辑已产生的工时/报工记录,修正异常工时或补录说明。'],
|
||||
[/Workhours/, '该页面用于工时查询,按工序、人员、设备、时间等条件查看工序工时。'],
|
||||
[/Urgent/, '该页面维护加急任务。用户查询加急订单明细,并更新完成、转至等字段,用于生产/质检优先级管理。'],
|
||||
[/AbnormalControl/, '该页面管理生产异常。用户查询异常记录,推进异常处理、关闭或记录处理结果。'],
|
||||
[/login/, '该页面是登录入口,负责账号密码登录、角色选择、Token/Cookie 写入、路由权限初始化和密码修改入口。'],
|
||||
[/dashboard/, '该页面是系统首页或欢迎页,用于承载看板入口或基础统计展示。'],
|
||||
[/404/, '该页面是 404 错误页,用于路由不存在时展示错误信息。'],
|
||||
[/purchaseIncoming/, '该页面处理采购/来料检验。质检人员查询待检任务,查看图纸、字典和检验人员,录入合格数、不合格数、检测数据,调用过程更新来料检验结果。'],
|
||||
[/processReceive/, '该页面处理序检收检。质检人员接收工序检验任务,查询加急和字典信息,读取 SAP 工序状态,新增序检收检记录。'],
|
||||
[/processSpecial/, '该页面处理过程/特殊检验。用户按工序任务录入特殊检验结果和检测明细。'],
|
||||
[/firstSpecial/, '该页面处理首检/特殊首件检验。用户按工序和工位选择任务,录入首检数据。'],
|
||||
[/final/, '该页面处理终检任务。质检人员查询终检任务,录入终检结果,并预留 SAP 生产订单状态回写。'],
|
||||
[/materialReturn/, '该页面处理原材料退库检验。用户查询退库检验任务,读取 SAP 生产订单辅助信息,录入并更新检验结果。'],
|
||||
[/salesReturn/, '该页面处理销售退货检验。用户查询销售退货待检任务,录入检验结果并更新质检记录。'],
|
||||
[/OtherInbound/, '该页面处理其他入库检验。用户查询其他入库待检任务,录入检验结果并更新质检记录。'],
|
||||
[/QualityCenter/, '该页面是质量综合处理中心。用户集中查询质检任务、收检记录、终检/入库任务和质检明细,录入检测数据、不合格处理,并读取 SAP 生产订单信息辅助校验。'],
|
||||
[/QualityDetails/, '该页面查询质检明细。用户按订单、物料、检验类型、时间等条件查询和导出检测明细。'],
|
||||
[/QualityStandardQuery/, '该页面维护/查询检验标准。用户按物料、工序、检测项目维护理论值、上下限、量具和启用状态。'],
|
||||
[/Unqualified/, '该页面处理不合格品。用户查询不合格记录,查看处置流程和节点,推进让步放行、返工、报废等处理。'],
|
||||
[/saleorderstatus/, '该页面查询销售订单状态。用户按销售订单、合同、物料等条件查看销售订单在 SAP/MES 中的履约、生产、发货或关闭状态。'],
|
||||
[/MenuManagement/, '该页面维护菜单和权限。管理员查询、增加、修改、删除菜单/角色权限配置,用于动态路由和按钮权限。'],
|
||||
[/OperationLog/, '该页面查询操作日志或报工操作记录,用于审计用户操作和追踪异常。'],
|
||||
[/OrgManagement/, '该页面维护组织/角色类基础数据,支持新增、修改、删除和查询。'],
|
||||
[/PersonnelManagement/, '该页面维护人员账号。管理员维护人员、部门、角色、启用状态等登录和权限基础数据。'],
|
||||
[/SystemRrolemaintenance/, '该页面维护系统角色。管理员查询、新增、修改、删除角色数据。'],
|
||||
[/WorkstationManagement/, '该页面维护工位资源。管理员在 MES 侧新增、修改、删除工位,同时调用 SAP B1 Resources 新增或更新资源主数据。'],
|
||||
[/layout|Sidebar|Navbar|TagsView|AppMain|ResizeHandler/, '该文件属于系统布局组件,负责菜单、导航、标签页、内容承载或窗口自适应,不承载具体业务流程。'],
|
||||
[/login/, '该页面是登录入口,负责账号密码登录、角色选择、Token/Cookie 写入、路由权限初始化和密码修改入口。'],
|
||||
[/dashboard/, '该页面是系统首页或欢迎页,用于承载看板入口或基础统计展示。'],
|
||||
[/404/, '该页面是 404 错误页,用于路由不存在时展示错误信息。']
|
||||
];
|
||||
const found = rules.find(([re]) => re.test(p));
|
||||
return [found ? found[1] : '该页面为业务页面,具体功能由表单字段、按钮和存储过程调用共同决定。', ...common].join('\n\n');
|
||||
}
|
||||
|
||||
function pageFlow(page) {
|
||||
const p = page.relPath;
|
||||
const flows = [
|
||||
[/SelfMakePlandone|SelfMakePlando|SelfMakePlan/,
|
||||
'自制/机加排产流程:\n\n```mermaid\nflowchart LR\n A[输入订单/合同/物料/工序/时间条件] --> B[调用 MES_OrderPlanned_Query 查询待排产任务]\n B --> C[选择任务行]\n C --> D{派工方式}\n D --> E[MES_OrderPlanned_SingleTaskPlanned 单任务派工]\n D --> F[计划排产_二次派工 绑定人员]\n D --> G[计划排产_外协分组]\n E --> H[MES_OrderPlanned_EditTaskPlanned/DescriptionEdit 修改计划字段]\n F --> H\n G --> H\n H --> I[MES_OrderPlanned_Finish 确认排产]\n I --> J[PATCH /ProductionOrders(...) 回写 SAP 生产订单状态]\n J --> K[页面刷新任务状态]\n```\n'],
|
||||
[/InstallMakePlanDo|InstallMakePlan/,
|
||||
'装配排产流程:\n\n```mermaid\nflowchart LR\n A[输入订单/合同/物料/工序/时间条件] --> B[MES_OrderPlanned_Queryzp 查询装配任务]\n B --> C[选择任务]\n C --> D[Prog_MES_LoadStations 选择工位]\n D --> E[系统管理_人员管理_所有有效人员查询 选择人员]\n E --> F[计划排产_派工 或 MES_OrderPlanned_SingleTaskPlanned]\n F --> G[计划排产_二次派工]\n G --> H[MES_OrderPlanned_Finish 确认排产]\n H --> I[PATCH /ProductionOrders(...) 或 POST SAP 请求]\n I --> J[刷新排产结果]\n```\n'],
|
||||
[/MachiningCenter/,
|
||||
'机加中心执行流程:\n\n```mermaid\nflowchart LR\n A[选择工位/人员/任务条件] --> B[MES_ProductCenter_GetTask 查询可执行任务]\n B --> C[选择任务]\n C --> D[收料 MES_ProductTask_MaterialRecieved]\n D --> E[开始加工 MES_ProductTask_WorkingStart]\n E --> F{生产过程}\n F --> G[暂停/恢复 MES_ProductTask_Rest]\n F --> H[首检/巡检/序检]\n F --> I[报工 MES_ProductTask_ProdReport]\n I --> J[写入操作记录/报工记录]\n J --> K[必要时读取 SAP ProductionOrders]\n K --> L[刷新任务和进行中状态]\n```\n'],
|
||||
[/AssemblyCenter/,
|
||||
'装配中心执行流程:\n\n```mermaid\nflowchart LR\n A[选择工位/人员] --> B[MES_ProductCenter_GetTask_zp 查询装配任务]\n B --> C[查看缺料/图纸/工序]\n C --> D[MES_人员工时记录_开始 或生产任务开始]\n D --> E[执行装配]\n E --> F[MES_ProductTask_SendInspection 送检/报工]\n F --> G[收检记录/质检数据]\n G --> H[读取 SAP ProductionOrders 辅助判断]\n H --> I[刷新装配任务]\n```\n'],
|
||||
[/WorkHoursCheck/,
|
||||
'工时校验流程:\n\n```mermaid\nflowchart LR\n A[选择人员/订单/工序/日期] --> B[工时校验_获取昨日之前操作记录列表]\n B --> C[选择待校验记录]\n C --> D[usp_ValidateWorkTime 或 usp_BatchValidateWorkTime 计算工时]\n D --> E{校验结果}\n E --> F[工时校验_保存校验记录]\n E --> G[工时校验_批量保存校验记录]\n F --> H[更新校验状态]\n G --> H\n H --> I[GET/PATCH /ProductionOrders 补工序资源]\n I --> J[POST /InventoryGenExits 上传 SAP]\n J --> K[标记已上传/失败留痕]\n```\n'],
|
||||
[/purchaseIncoming|processReceive|processSpecial|firstSpecial|final|materialReturn|salesReturn|OtherInbound|QualityCenter/,
|
||||
'质检业务流程:\n\n```mermaid\nflowchart LR\n A[选择检验类型/订单/物料/工序] --> B[查询待检任务]\n B --> C[查看图纸/字典/检验人员]\n C --> D[录入检测数量、合格数、不合格数和检测明细]\n D --> E{检验类型}\n E --> F[来料/入库/退货检验更新]\n E --> G[序检收检新增或更新]\n E --> H[终检/首检/特殊检验保存]\n F --> I[写入 YL_质量检验_质检记录及明细]\n G --> I\n H --> I\n I --> J[必要时查询/回写 SAP 生产订单或单据]\n```\n'],
|
||||
[/OutsourceMange|SaleOutsourceMange|OutsourceSheet/,
|
||||
'外协业务流程:\n\n```mermaid\nflowchart LR\n A[输入订单/合同/供应商/工序条件] --> B[查询外协任务或外协报表]\n B --> C[选择外协工序/供应商]\n C --> D[计划排产_派工 或外协分组]\n D --> E[POST SAP 采购/外协相关请求]\n E --> F[外协发料查询/保存]\n F --> G[外协收料/质检记录]\n G --> H[更新打印次数/备注/报表状态]\n```\n']
|
||||
];
|
||||
const found = flows.find(([re]) => re.test(p));
|
||||
if (found) return found[1];
|
||||
if (!page.procs.length) return '该页面未发现后端存储过程调用,通常不形成完整业务流程。';
|
||||
const procChain = uniq(page.procs.map(x => x.name)).slice(0, 8).map(x => `\`${x}\``).join(' -> ');
|
||||
return `通用流程:用户输入查询/维护条件 -> 调用 ${procChain} -> 后端读写 SQL Server -> 页面刷新表格或弹窗结果。`;
|
||||
}
|
||||
|
||||
function fieldMappingTable(page) {
|
||||
const rows = [
|
||||
'| 类型 | 前端字段/显示列 | 存储过程参数或表达式 | SQL返回字段/数据库字段线索 |',
|
||||
'|---|---|---|---|'
|
||||
];
|
||||
const mappings = [];
|
||||
for (const p of page.fields.pushedParams || []) {
|
||||
const relatedProc = page.procs.find(proc => proc.sqlInfo?.params?.some(x => x.includes(`@${p.name}`)));
|
||||
const sqlField = relatedProc?.sqlInfo?.features?.selectColumns?.find(x => x.includes(p.name)) || '';
|
||||
mappings.push(['参数', p.expr, `@${p.name}`, sqlField]);
|
||||
}
|
||||
for (const c of page.fields.tableColumns || []) {
|
||||
if (!c.label && !c.prop) continue;
|
||||
const sqlField = page.procs
|
||||
.flatMap(proc => proc.sqlInfo?.features?.selectColumns || [])
|
||||
.find(x => (c.prop && x.includes(c.prop)) || (c.label && x.includes(c.label))) || '';
|
||||
mappings.push(['显示列', c.label || c.prop, '', sqlField || c.prop || c.label]);
|
||||
}
|
||||
for (const v of page.fields.vModels || []) {
|
||||
const name = v.split('.').pop();
|
||||
const param = page.fields.pushedParams.find(x => x.expr.includes(v) || x.expr.includes(name));
|
||||
mappings.push(['输入字段', v, param ? `@${param.name}` : '', '']);
|
||||
}
|
||||
const uniqRows = uniq(mappings.map(x => x.join('|'))).slice(0, 40);
|
||||
if (!uniqRows.length) return '未从页面模板中抽取到明确的 `v-model`、表格列或 `param.push` 字段映射。';
|
||||
for (const item of uniqRows) {
|
||||
const [type, front, param, sql] = item.split('|');
|
||||
rows.push(`| ${type} | \`${front || '-'}\` | ${param ? `\`${param}\`` : '-'} | ${sql ? `\`${sql}\`` : '-'} |`);
|
||||
}
|
||||
return rows.join('\n');
|
||||
}
|
||||
|
||||
function statusFlow(page) {
|
||||
const lines = [];
|
||||
for (const proc of page.procs) {
|
||||
const info = proc.sqlInfo;
|
||||
if (!info) continue;
|
||||
const status = info.features.statusLines || [];
|
||||
const useful = status.filter(x => /状态|排产|派工|加工|任务|质检|关闭|完成|已报工|DocEntry|SAP/i.test(x));
|
||||
if (useful.length) lines.push(`- \`${proc.name}\`:${useful.slice(0, 8).map(x => `\`${x}\``).join(';')}`);
|
||||
}
|
||||
if (!lines.length) return '未从当前 SQL 静态解析到明确的状态字段变更。';
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function sapInterfaceList(page) {
|
||||
if (!page.sapCalls.length) return '页面无直接 SAP Service Layer 调用。';
|
||||
const lines = ['| 行号 | 方法 | 接口 | 调用时机/影响判断 | 提交字段线索 |', '|---:|---|---|---|---|'];
|
||||
for (const c of page.sapCalls) {
|
||||
const timing = c.url.includes('ProductionOrders')
|
||||
? '读取或更新 SAP 生产订单。失败会导致 MES 与 SAP 订单状态/工序资源不同步。'
|
||||
: c.url.includes('InventoryGenExits')
|
||||
? '上传库存发出/工时相关单据。失败会导致 SAP 未生成对应业务单据。'
|
||||
: c.url.includes('Resources')
|
||||
? '新增或更新 SAP 资源主数据。失败会导致 MES 工位与 SAP 资源不一致。'
|
||||
: c.url.includes('RouteStages')
|
||||
? '新增或更新 SAP 工序阶段。失败会导致 MES 工序与 SAP RouteStages 不一致。'
|
||||
: '调用外部接口,失败会影响对应外部系统同步。';
|
||||
lines.push(`| ${c.line} | \`${c.fn}\` | \`${c.url}\` | ${timing} | ${mdList(c.payloadKeys.slice(0, 12))} |`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function dbCallChain(page) {
|
||||
if (!page.procs.length) return '无数据库调用链。';
|
||||
const lines = [];
|
||||
for (const proc of page.procs) {
|
||||
const deps = proc.sqlInfo ? depsMd(proc.sqlInfo.deps) : '过程体未解析';
|
||||
const result = proc.purpose;
|
||||
lines.push(`- \`${page.relPath}\` -> \`${proc.name}\` -> ${deps} -> ${result}`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function mdList(items, empty = '无') {
|
||||
if (!items || !items.length) return empty;
|
||||
return items.map(x => `\`${x}\``).join('、');
|
||||
}
|
||||
|
||||
function depsMd(deps) {
|
||||
if (!deps || !deps.length) return '未在 SQL 中解析到明确依赖';
|
||||
const tables = deps.filter(x => x.startsWith('TABLE:')).map(x => x.slice(6));
|
||||
const views = deps.filter(x => x.startsWith('VIEW:')).map(x => x.slice(5));
|
||||
const sap = deps.filter(x => x.startsWith('SAP:')).map(x => x.slice(4));
|
||||
const parts = [];
|
||||
if (tables.length) parts.push(`表:${mdList(tables)}`);
|
||||
if (views.length) parts.push(`视图:${mdList(views)}`);
|
||||
if (sap.length) parts.push(`SAP/链接库:${mdList(sap)}`);
|
||||
return parts.join(';') || '未在 SQL 中解析到明确依赖';
|
||||
}
|
||||
|
||||
function explainProc(proc) {
|
||||
const info = proc.sqlInfo;
|
||||
const lines = [];
|
||||
lines.push(`\`${proc.name}\` 属于 ${businessDomainFromName(proc.name)},前端以 type=${proc.type}(${proc.op})调用,页面用途是 ${proc.purpose}。`);
|
||||
if (!info) {
|
||||
lines.push('当前工作区 SQL 中没有解析到该过程体,因此只能确认前端调用关系,不能准确说明内部读写表、过滤条件和状态变更。');
|
||||
return lines.join(' ');
|
||||
}
|
||||
const f = info.features;
|
||||
lines.push(`数据库处理摘要:${info.summary}。`);
|
||||
lines.push(`读取/关联对象:${depsMd(info.deps)}。`);
|
||||
if (info.writes.length) lines.push(`写入或更新对象:${mdList(info.writes)}。`);
|
||||
if (info.params.length) lines.push(`入参/分页参数:${mdList(info.params)}。`);
|
||||
if (f.selectColumns.length) lines.push(`主要返回字段线索:${mdList(f.selectColumns)}。`);
|
||||
if (f.filters.length) lines.push(`关键过滤/匹配条件:${f.filters.map(x => `\`${x}\``).join(';')}。`);
|
||||
if (f.updates.length) lines.push(`关键写入/状态处理语句线索:${f.updates.map(x => `\`${x}\``).join(';')}。`);
|
||||
if (f.grouping.length) lines.push(`排序/分组/分页线索:${f.grouping.map(x => `\`${x}\``).join(';')}。`);
|
||||
const flags = [];
|
||||
if (f.hasPagination) flags.push('分页');
|
||||
if (f.hasJson) flags.push('JSON 批量解析');
|
||||
if (f.hasTransaction) flags.push('事务控制');
|
||||
if (f.hasTryCatch) flags.push('异常捕获');
|
||||
if (f.hasSap) flags.push('读取 SAP 数据库对象');
|
||||
if (f.tempTables.length) flags.push(`临时表 ${mdList(f.tempTables)}`);
|
||||
if (flags.length) lines.push(`实现特征:${flags.join('、')}。`);
|
||||
return lines.join(' ');
|
||||
}
|
||||
|
||||
function procTable(procs) {
|
||||
if (!procs.length) return '未发现 `CreateData(...)` 存储过程调用。';
|
||||
const rows = [
|
||||
'| 行号 | type | 存储过程 | 前端用途 | SQL摘要 | 依赖对象 |',
|
||||
'|---:|---|---|---|---|---|'
|
||||
];
|
||||
for (const p of procs) {
|
||||
const info = p.sqlInfo;
|
||||
rows.push(`| ${p.line} | ${p.type}(${p.op}) | \`${p.name}\` | ${p.purpose} | ${info ? info.summary : '未找到过程体'} | ${info ? depsMd(info.deps) : '未知'} |`);
|
||||
}
|
||||
return rows.join('\n');
|
||||
}
|
||||
|
||||
function sapTable(calls, procs) {
|
||||
const procSap = [];
|
||||
for (const p of procs) {
|
||||
if (p.sqlInfo && p.sqlInfo.sapRefs.length) procSap.push(`${p.name}: ${p.sqlInfo.sapRefs.join(', ')}`);
|
||||
}
|
||||
if (!calls.length && !procSap.length) return '未发现页面直接调用 SAP B1 Service Layer;相关存储过程也未解析到 `SAP.SBO_YL` 链接库引用。';
|
||||
const lines = [];
|
||||
if (calls.length) {
|
||||
lines.push('页面直接 SAP/接口调用:');
|
||||
for (const c of calls) lines.push(`- 第 ${c.line} 行:\`${c.fn}("${c.url}")\``);
|
||||
}
|
||||
if (procSap.length) {
|
||||
lines.push('存储过程内读取 SAP B1 数据库对象:');
|
||||
for (const s of procSap) lines.push(`- ${s}`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function techFlags(page) {
|
||||
const flags = ['Vue 2 Options API', 'Element UI'];
|
||||
if (page.procs.length) flags.push('`CreateData` + `ExecDatabase` 调用后端过程');
|
||||
else flags.push('未发现页面级后端存储过程调用');
|
||||
if (page.sapCalls.length) flags.push('SAP B1 Service Layer(`src/api/b1s.js`)');
|
||||
if (page.imports.includes('@/api/tm') || page.sapCalls.some(x => x.fn === 'posttm')) flags.push('TM/OpenAPI 接口(`src/api/tm.js`)');
|
||||
if (page.hasEcharts) flags.push('ECharts');
|
||||
if (page.hasFullCalendar) flags.push('FullCalendar');
|
||||
if (page.hasMqtt) flags.push('MQTT');
|
||||
if (page.hasPdf) flags.push('PDF/图纸预览或导出');
|
||||
if (page.hasExcel) flags.push('Excel/报表导出');
|
||||
return flags;
|
||||
}
|
||||
|
||||
function moduleSummary(pages) {
|
||||
const groups = {};
|
||||
for (const p of pages) {
|
||||
const top = p.relPath.split('/')[2] || '其他';
|
||||
groups[top] ||= [];
|
||||
groups[top].push(p);
|
||||
}
|
||||
const lines = ['| 模块 | 页面数 | 主要能力 |', '|---|---:|---|'];
|
||||
for (const [mod, ps] of Object.entries(groups)) {
|
||||
const allProcs = uniq(ps.flatMap(p => p.procs.map(x => x.name)));
|
||||
const allSap = ps.reduce((n, p) => n + p.sapCalls.length, 0);
|
||||
const ability = [];
|
||||
if (allProcs.length) ability.push(`过程 ${allProcs.length} 个`);
|
||||
if (allSap) ability.push(`直接 SAP 调用 ${allSap} 处`);
|
||||
if (ps.some(p => p.hasEcharts)) ability.push('图表');
|
||||
if (ps.some(p => p.hasMqtt)) ability.push('设备实时通信');
|
||||
lines.push(`| ${mod} | ${ps.length} | ${ability.join('、') || '布局/静态页面'} |`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
const sqlModel = extractSqlModel();
|
||||
const pages = walk(viewsRoot, ['.vue', '.js']).sort().map(file => {
|
||||
const text = readText(file);
|
||||
const relPath = rel(file);
|
||||
const procs = extractCreateData(text).map(p => ({
|
||||
...p,
|
||||
op: operationByType(p.type),
|
||||
purpose: procPurpose(p.name),
|
||||
sqlInfo: sqlModel.procByName[p.name]
|
||||
}));
|
||||
const page = {
|
||||
file,
|
||||
relPath,
|
||||
module: moduleNameFromPath(relPath),
|
||||
lines: text.split(/\r?\n/).length,
|
||||
labels: extractLabels(text),
|
||||
methods: extractMethods(text),
|
||||
fields: extractFrontendFields(text),
|
||||
procs,
|
||||
sapCalls: extractSapCalls(text),
|
||||
imports: extractApiImports(text),
|
||||
hasMqtt: /mqtt|MQTT/.test(text),
|
||||
hasEcharts: /echarts|ECharts|\$echarts/.test(text),
|
||||
hasFullCalendar: /FullCalendar|@fullcalendar/.test(text),
|
||||
hasPdf: /pdfjs|jspdf|html2canvas|pdf/i.test(text),
|
||||
hasExcel: /xlsx|exportExcel|Excel|导出/.test(text)
|
||||
};
|
||||
page.functionDesc = pageBusinessNarrative(page);
|
||||
return page;
|
||||
});
|
||||
|
||||
const now = new Date().toISOString().slice(0, 10);
|
||||
let md = '';
|
||||
md += `# MES_Manage_View_V20 src/views 逐页面详细分析\n\n`;
|
||||
md += `生成日期:${now}\n\n`;
|
||||
md += `分析范围:\`MES_Manage_View_V20/src/views\` 下 ${pages.length} 个 .vue/.js 文件;辅助读取 \`src/api\`、\`config/index.js\`、\`static/config.js\` 和 SQL 脚本。\n\n`;
|
||||
md += `## 重要说明\n\n`;
|
||||
md += `- 页面功能不是只按文件名猜测,报告同时参考页面路径、按钮/字段文案、方法名、存储过程名和 SQL 过程体。\n`;
|
||||
md += `- 存储过程处理说明来自 SQL 静态解析:读写对象、参数、返回字段、过滤条件、更新语句、分组分页、JSON、事务、SAP 链接库等。\n`;
|
||||
md += `- 如果某个过程没有解析到过程体,报告会明确说明不能准确判断内部表/视图和处理逻辑。\n\n`;
|
||||
|
||||
md += `## 技术路线总览\n\n`;
|
||||
md += `- 前端:Vue 2.7、Vue Router、Vuex、Element UI,页面以 Options API 为主。\n`;
|
||||
md += `- 数据访问:页面调用 \`this.CreateData(type, procedureName, params[, pageSize, pageCurrent])\` 组包,再调用 \`this.ExecDatabase(data)\` 访问后端。\n`;
|
||||
md += `- 后端:统一接口接收 type/name/param,执行 SQL Server 存储过程,返回 \`response.data\` 和分页信息。\n`;
|
||||
md += `- 数据库:\`YL_MESDB\`,核心对象包括生产计划/生产任务、加工中心操作记录、质检记录、设备数据、权限菜单等表和视图。\n`;
|
||||
md += `- SAP:\`src/api/b1s.js\` 通过 \`window.dt_Config.baseURLB1\` 访问 B1 Service Layer;部分过程也跨库读取 \`SAP.SBO_YL.dbo.*\`。\n\n`;
|
||||
|
||||
md += `## 模块汇总\n\n${moduleSummary(pages)}\n\n`;
|
||||
|
||||
const allSapCalls = [];
|
||||
for (const p of pages) {
|
||||
for (const c of p.sapCalls) allSapCalls.push({ page: p.relPath, ...c });
|
||||
}
|
||||
if (allSapCalls.length) {
|
||||
md += `## SAP 交互总清单\n\n`;
|
||||
md += `| 页面 | 行号 | 方法 | 接口 | 提交字段线索 |\n|---|---:|---|---|---|\n`;
|
||||
for (const c of allSapCalls) {
|
||||
md += `| \`${c.page}\` | ${c.line} | \`${c.fn}\` | \`${c.url}\` | ${mdList(c.payloadKeys.slice(0, 10))} |\n`;
|
||||
}
|
||||
md += `\n`;
|
||||
}
|
||||
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
const p = pages[i];
|
||||
md += `## ${i + 1}. ${p.relPath}\n\n`;
|
||||
md += `**页面位置**:\`${p.relPath}\` \n`;
|
||||
md += `**所属模块**:${p.module} \n`;
|
||||
md += `**源码行数**:${p.lines}\n\n`;
|
||||
md += `### 页面功能\n\n${p.functionDesc}\n\n`;
|
||||
md += `### 页面流程图\n\n${pageFlow(p)}\n\n`;
|
||||
md += `### 用户操作与页面线索\n\n`;
|
||||
md += `可见按钮/字段线索:${mdList(p.labels.slice(0, 18))}\n\n`;
|
||||
md += `主要方法线索:${mdList(p.methods.slice(0, 18))}\n\n`;
|
||||
md += `### 字段映射表\n\n${fieldMappingTable(p)}\n\n`;
|
||||
md += `### 技术路线\n\n${techFlags(p).map(x => `- ${x}`).join('\n')}\n\n`;
|
||||
md += `### 调用的存储过程总览\n\n${procTable(p.procs)}\n\n`;
|
||||
md += `### 与 SAP 的交互\n\n${sapTable(p.sapCalls, p.procs)}\n\n`;
|
||||
md += `### SAP 接口级说明\n\n${sapInterfaceList(p)}\n\n`;
|
||||
md += `### 状态流转说明\n\n${statusFlow(p)}\n\n`;
|
||||
md += `### 数据库调用链\n\n${dbCallChain(p)}\n\n`;
|
||||
md += `### 存储过程处理逻辑明细\n\n`;
|
||||
if (!p.procs.length) {
|
||||
md += `无页面级存储过程调用。\n\n`;
|
||||
} else {
|
||||
for (const proc of p.procs) md += `- ${explainProc(proc)}\n`;
|
||||
md += `\n`;
|
||||
}
|
||||
}
|
||||
|
||||
md += `## SQL 对象总览\n\n`;
|
||||
md += `脚本中解析到表/视图 ${sqlModel.objects.length} 个、存储过程 ${Object.keys(sqlModel.procByName).length} 个。\n\n`;
|
||||
md += `### 表/视图清单\n\n`;
|
||||
for (const obj of sqlModel.objects) md += `- ${obj.type === 'TABLE' ? '表' : '视图'}:\`${obj.name}\`\n`;
|
||||
|
||||
fs.writeFileSync(outFile, md, 'utf8');
|
||||
console.log(outFile);
|
||||
206
tools/create_dev_plan_docx.ps1
Normal file
206
tools/create_dev_plan_docx.ps1
Normal file
@@ -0,0 +1,206 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$PlanMarkdown,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SourceMarkdown,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$OutputDocx
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Escape-XmlText {
|
||||
param([AllowNull()][string]$Text)
|
||||
if ($null -eq $Text) { return '' }
|
||||
return [System.Security.SecurityElement]::Escape($Text)
|
||||
}
|
||||
|
||||
function Add-Paragraph {
|
||||
param(
|
||||
[System.Collections.Generic.List[string]]$Parts,
|
||||
[string]$Text,
|
||||
[string]$Style = $null
|
||||
)
|
||||
$styleXml = ''
|
||||
if ($Style) {
|
||||
$styleXml = "<w:pPr><w:pStyle w:val=`"$Style`"/></w:pPr>"
|
||||
}
|
||||
$Parts.Add("<w:p>$styleXml<w:r><w:t xml:space=`"preserve`">$(Escape-XmlText $Text)</w:t></w:r></w:p>")
|
||||
}
|
||||
|
||||
function Add-Table {
|
||||
param(
|
||||
[System.Collections.Generic.List[string]]$Parts,
|
||||
[string[]]$Headers,
|
||||
[object[]]$Rows
|
||||
)
|
||||
$tbl = New-Object System.Text.StringBuilder
|
||||
[void]$tbl.Append('<w:tbl>')
|
||||
[void]$tbl.Append('<w:tblPr><w:tblStyle w:val="TableGrid"/><w:tblW w:w="0" w:type="auto"/><w:tblBorders><w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:insideH w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:insideV w:val="single" w:sz="4" w:space="0" w:color="auto"/></w:tblBorders></w:tblPr>')
|
||||
[void]$tbl.Append('<w:tr>')
|
||||
foreach ($h in $Headers) {
|
||||
[void]$tbl.Append('<w:tc><w:tcPr><w:shd w:val="clear" w:color="auto" w:fill="D9EAF7"/></w:tcPr><w:p><w:r><w:rPr><w:b/></w:rPr><w:t xml:space="preserve">')
|
||||
[void]$tbl.Append((Escape-XmlText $h))
|
||||
[void]$tbl.Append('</w:t></w:r></w:p></w:tc>')
|
||||
}
|
||||
[void]$tbl.Append('</w:tr>')
|
||||
foreach ($row in $Rows) {
|
||||
[void]$tbl.Append('<w:tr>')
|
||||
foreach ($cell in $row) {
|
||||
[void]$tbl.Append('<w:tc><w:p><w:r><w:t xml:space="preserve">')
|
||||
[void]$tbl.Append((Escape-XmlText ([string]$cell)))
|
||||
[void]$tbl.Append('</w:t></w:r></w:p></w:tc>')
|
||||
}
|
||||
[void]$tbl.Append('</w:tr>')
|
||||
}
|
||||
[void]$tbl.Append('</w:tbl>')
|
||||
$Parts.Add($tbl.ToString())
|
||||
}
|
||||
|
||||
function Flush-TableBuffer {
|
||||
param(
|
||||
[System.Collections.Generic.List[string]]$Parts,
|
||||
[System.Collections.Generic.List[string]]$Buffer
|
||||
)
|
||||
if ($Buffer.Count -lt 2) {
|
||||
foreach ($line in $Buffer) {
|
||||
Add-Paragraph -Parts $Parts -Text $line
|
||||
}
|
||||
$Buffer.Clear()
|
||||
return
|
||||
}
|
||||
$header = $Buffer[0].Trim('|').Split('|') | ForEach-Object { $_.Trim() }
|
||||
$rows = @()
|
||||
for ($i = 2; $i -lt $Buffer.Count; $i++) {
|
||||
$rows += ,($Buffer[$i].Trim('|').Split('|') | ForEach-Object { $_.Trim() })
|
||||
}
|
||||
Add-Table -Parts $Parts -Headers $header -Rows $rows
|
||||
$Buffer.Clear()
|
||||
}
|
||||
|
||||
function Add-MarkdownContent {
|
||||
param(
|
||||
[System.Collections.Generic.List[string]]$Parts,
|
||||
[string]$MarkdownPath
|
||||
)
|
||||
$lines = [System.IO.File]::ReadAllLines($MarkdownPath, [System.Text.Encoding]::UTF8)
|
||||
$tableBuffer = New-Object System.Collections.Generic.List[string]
|
||||
$inCode = $false
|
||||
|
||||
foreach ($line in $lines) {
|
||||
if ($line.TrimStart().StartsWith('```')) {
|
||||
if ($tableBuffer.Count -gt 0) { Flush-TableBuffer -Parts $Parts -Buffer $tableBuffer }
|
||||
$inCode = -not $inCode
|
||||
Add-Paragraph -Parts $Parts -Text $line -Style 'Code'
|
||||
continue
|
||||
}
|
||||
if ($inCode) {
|
||||
Add-Paragraph -Parts $Parts -Text $line -Style 'Code'
|
||||
continue
|
||||
}
|
||||
if ($line.TrimStart().StartsWith('|') -and $line.TrimEnd().EndsWith('|')) {
|
||||
$tableBuffer.Add($line)
|
||||
continue
|
||||
}
|
||||
if ($tableBuffer.Count -gt 0) { Flush-TableBuffer -Parts $Parts -Buffer $tableBuffer }
|
||||
|
||||
if ($line.StartsWith('### ')) {
|
||||
Add-Paragraph -Parts $Parts -Text $line.Substring(4) -Style 'Heading3'
|
||||
} elseif ($line.StartsWith('## ')) {
|
||||
Add-Paragraph -Parts $Parts -Text $line.Substring(3) -Style 'Heading2'
|
||||
} elseif ($line.StartsWith('# ')) {
|
||||
Add-Paragraph -Parts $Parts -Text $line.Substring(2) -Style 'Heading1'
|
||||
} elseif ($line.Trim().StartsWith('- ')) {
|
||||
Add-Paragraph -Parts $Parts -Text ('· ' + $line.Trim().Substring(2))
|
||||
} elseif ([string]::IsNullOrWhiteSpace($line)) {
|
||||
Add-Paragraph -Parts $Parts -Text ''
|
||||
} else {
|
||||
Add-Paragraph -Parts $Parts -Text $line
|
||||
}
|
||||
}
|
||||
if ($tableBuffer.Count -gt 0) { Flush-TableBuffer -Parts $Parts -Buffer $tableBuffer }
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $PlanMarkdown)) { throw "Plan markdown not found: $PlanMarkdown" }
|
||||
if (-not (Test-Path -LiteralPath $SourceMarkdown)) { throw "Source markdown not found: $SourceMarkdown" }
|
||||
|
||||
$outputDir = Split-Path -Path $OutputDocx -Parent
|
||||
if (-not (Test-Path -LiteralPath $outputDir)) {
|
||||
New-Item -ItemType Directory -Path $outputDir | Out-Null
|
||||
}
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("docx_" + [Guid]::NewGuid().ToString('N'))
|
||||
$wordDir = Join-Path $tempRoot 'word'
|
||||
$relsDir = Join-Path $tempRoot '_rels'
|
||||
$wordRelsDir = Join-Path $wordDir '_rels'
|
||||
New-Item -ItemType Directory -Path $wordDir, $relsDir, $wordRelsDir | Out-Null
|
||||
|
||||
$parts = New-Object System.Collections.Generic.List[string]
|
||||
Add-MarkdownContent -Parts $parts -MarkdownPath $PlanMarkdown
|
||||
Add-MarkdownContent -Parts $parts -MarkdownPath $SourceMarkdown
|
||||
|
||||
$documentXml = @"
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:body>
|
||||
$($parts -join "`n")
|
||||
<w:sectPr>
|
||||
<w:pgSz w:w="11906" w:h="16838"/>
|
||||
<w:pgMar w:top="1440" w:right="1080" w:bottom="1440" w:left="1080" w:header="720" w:footer="720" w:gutter="0"/>
|
||||
</w:sectPr>
|
||||
</w:body>
|
||||
</w:document>
|
||||
"@
|
||||
|
||||
$stylesXml = @'
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:style w:type="paragraph" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/><w:rPr><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei" w:hAnsi="Microsoft YaHei"/><w:sz w:val="21"/></w:rPr></w:style>
|
||||
<w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:spacing w:before="360" w:after="120"/><w:outlineLvl w:val="0"/></w:pPr><w:rPr><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei" w:hAnsi="Microsoft YaHei"/><w:b/><w:sz w:val="30"/></w:rPr></w:style>
|
||||
<w:style w:type="paragraph" w:styleId="Heading2"><w:name w:val="heading 2"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:spacing w:before="280" w:after="100"/><w:outlineLvl w:val="1"/></w:pPr><w:rPr><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei" w:hAnsi="Microsoft YaHei"/><w:b/><w:sz w:val="26"/></w:rPr></w:style>
|
||||
<w:style w:type="paragraph" w:styleId="Heading3"><w:name w:val="heading 3"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:spacing w:before="220" w:after="80"/><w:outlineLvl w:val="2"/></w:pPr><w:rPr><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei" w:hAnsi="Microsoft YaHei"/><w:b/><w:sz w:val="23"/></w:rPr></w:style>
|
||||
<w:style w:type="paragraph" w:styleId="Code"><w:name w:val="Code"/><w:basedOn w:val="Normal"/><w:qFormat/><w:rPr><w:rFonts w:ascii="Consolas" w:eastAsia="Microsoft YaHei" w:hAnsi="Consolas"/><w:sz w:val="18"/></w:rPr></w:style>
|
||||
<w:style w:type="table" w:styleId="TableGrid"><w:name w:val="Table Grid"/><w:basedOn w:val="TableNormal"/><w:uiPriority w:val="59"/><w:tblPr><w:tblBorders><w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:insideH w:val="single" w:sz="4" w:space="0" w:color="auto"/><w:insideV w:val="single" w:sz="4" w:space="0" w:color="auto"/></w:tblBorders></w:tblPr></w:style>
|
||||
</w:styles>
|
||||
'@
|
||||
|
||||
$contentTypesXml = @'
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
||||
<Default Extension="xml" ContentType="application/xml"/>
|
||||
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
||||
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
|
||||
</Types>
|
||||
'@
|
||||
|
||||
$relsXml = @'
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
||||
</Relationships>
|
||||
'@
|
||||
|
||||
$docRelsXml = @'
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
||||
</Relationships>
|
||||
'@
|
||||
|
||||
[System.IO.File]::WriteAllText((Join-Path $tempRoot '[Content_Types].xml'), $contentTypesXml, [System.Text.Encoding]::UTF8)
|
||||
[System.IO.File]::WriteAllText((Join-Path $relsDir '.rels'), $relsXml, [System.Text.Encoding]::UTF8)
|
||||
[System.IO.File]::WriteAllText((Join-Path $wordDir 'document.xml'), $documentXml, [System.Text.Encoding]::UTF8)
|
||||
[System.IO.File]::WriteAllText((Join-Path $wordDir 'styles.xml'), $stylesXml, [System.Text.Encoding]::UTF8)
|
||||
[System.IO.File]::WriteAllText((Join-Path $wordRelsDir 'document.xml.rels'), $docRelsXml, [System.Text.Encoding]::UTF8)
|
||||
|
||||
if (Test-Path -LiteralPath $OutputDocx) {
|
||||
Remove-Item -LiteralPath $OutputDocx -Force
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
[System.IO.Compression.ZipFile]::CreateFromDirectory($tempRoot, $OutputDocx)
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force
|
||||
Write-Output $OutputDocx
|
||||
Reference in New Issue
Block a user