- backend/: 13 Maven modules (cw-elevator-application, cloudwalk-cloud, intelligent-cwoscomponent, ninca-crk, etc.) - frontend/: 4 Vue projects (elevator-front, cwos-portal, alarm-front, front_acs) + decompiled + scripts - scripts/: build, test-env, tools (Docker Compose, service templates, API parity) - docs/: AGENTS.md, superpowers specs, architecture docs - .gitignore: standard Java/Maven exclusions Moved from legacy maven-*/ root layout to backend/ organized structure.
37 KiB
Frontend-Source 反编译与重建实施计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 创建 frontend-source/ 目录,将 28 个 Vue 2 + Element UI dist 产物反编译为可读源码(阶段 1),并重建 4 个核心应用为可构建工程(阶段 2)。
Architecture: 四步工具链(解包→格式化→AST分析→组织),先跑通 cwos-portal 验证流程,再批量处理其余 27 个。核心 4 个应用用 Vue 2 + Element UI 脚手架重建。
Tech Stack: Node.js 18+, @babel/parser, js-beautify, prettier, webpack-bundle-analyzer, Vue 2.6 + Element UI 2.15 + Vue CLI 4.5
关联 Spec: docs/superpowers/specs/2026-04-29-frontend-source-design.md
前置准备
Task 0: 环境准备与目录初始化
- Step 1: 创建目录结构
mkdir -p frontend-source/decompiled
mkdir -p frontend-source/projects
mkdir -p frontend-source/scripts
- Step 2: 创建 .gitignore
写入 frontend-source/.gitignore:
**/node_modules/
**/dist/
**/.cache/
- Step 3: 初始化脚本 package.json
写入 frontend-source/scripts/package.json:
{
"name": "frontend-source-tools",
"private": true,
"scripts": {
"unpack": "node unpack-webpack.js",
"extract-api": "node extract-api-calls.js",
"extract-router": "node extract-router.js",
"beautify": "bash beautify-all.sh",
"compare": "bash compare-output.sh"
},
"dependencies": {
"@babel/parser": "^7.24.0",
"@babel/traverse": "^7.24.0",
"@babel/types": "^7.24.0",
"js-beautify": "^1.15.0",
"prettier": "^3.2.0"
}
}
- Step 4: 安装依赖
cd frontend-source/scripts && npm install
- Step 5: 将 frontend-source/ 加入根 .gitignore 白名单
在 /media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/反编译/.gitignore 末尾添加:
!/frontend-source/
!/frontend-source/**
- Step 6: Commit
git add frontend-source/ .gitignore
git commit -m "chore: init frontend-source directory with tooling scaffold"
阶段 1:工具脚本开发
Task 1: webpack bundle 解包脚本
Files:
-
Create:
frontend-source/scripts/unpack-webpack.js -
Step 1: 编写解包脚本
#!/usr/bin/env node
/**
* webpack bundle 解包器
* 识别 __webpack_modules__ 结构,按模块 ID 拆分为独立文件
*
* 用法: node unpack-webpack.js <bundle.js> <output-dir>
*/
const fs = require('fs');
const path = require('path');
function unpackWebpackBundle(bundlePath, outputDir) {
const code = fs.readFileSync(bundlePath, 'utf-8');
// 检测 webpack 版本并提取模块对象
let modules = null;
// 模式1: __webpack_modules__ = { 123: function(module, exports, __webpack_require__) {...}, ... }
const modulesAssignMatch = code.match(/__webpack_modules__\s*=\s*(\{[\s\S]*?\});/);
// 模式2: (function(modules) { ... })({ 123: function(...) {...}, ... })
const iifeMatch = code.match(/\(function\(\s*\w+\s*\)\s*\{[\s\S]*?\}\s*\)\s*\(\s*(\{[\s\S]*?\})\s*\)\s*;/);
if (modulesAssignMatch) {
modules = eval('(' + modulesAssignMatch[1] + ')');
} else if (iifeMatch) {
modules = eval('(' + iifeMatch[1] + ')');
} else {
console.error('未识别到 webpack 模块结构,尝试按 function(module, exports, __webpack_require__) 正则提取');
modules = extractByRegex(code);
}
if (!modules || Object.keys(modules).length === 0) {
console.error('未能提取任何模块');
process.exit(1);
}
fs.mkdirSync(outputDir, { recursive: true });
let count = 0;
for (const [id, factory] of Object.entries(modules)) {
const moduleCode = typeof factory === 'function'
? factory.toString()
: JSON.stringify(factory, null, 2);
const safeId = String(id).replace(/[^a-zA-Z0-9_-]/g, '_');
fs.writeFileSync(path.join(outputDir, `module_${safeId}.js`), moduleCode);
count++;
}
console.log(`拆解完成: ${count} 个模块 → ${outputDir}`);
return count;
}
function extractByRegex(code) {
const modules = {};
// 匹配 function(e,t,n){...} 模式 (压缩后的 webpack 模块)
const re = /(\d+):\s*function\s*\(\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*\{/g;
const idRe = /(\d+):\s*function\s*\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\)\s*\{/;
let match;
let lastIndex = 0;
const matches = [];
while ((match = re.exec(code)) !== null) {
matches.push({ id: match[1], start: match.index });
}
for (let i = 0; i < matches.length; i++) {
const current = matches[i];
const next = matches[i + 1];
const startIdx = current.start;
const endIdx = next ? next.start : code.length;
// 找到匹配的闭合大括号
let depth = 1;
let pos = startIdx + current[0].length;
while (depth > 0 && pos < endIdx) {
if (code[pos] === '{') depth++;
if (code[pos] === '}') depth--;
pos++;
}
const funcBody = code.substring(startIdx, pos);
modules[current.id] = funcBody;
}
return modules;
}
// CLI
const args = process.argv.slice(2);
if (args.length < 2) {
console.log('用法: node unpack-webpack.js <bundle.js> <output-dir>');
process.exit(1);
}
unpackWebpackBundle(args[0], args[1]);
- Step 2: 对 cwos-portal 进行解包测试
cd frontend-source/scripts
node unpack-webpack.js ../frontend/cwos-portal/static/js/app.32c5fb6ab7ff973ea14d.js /tmp/cwos-portal-unpack-test
- Step 3: 验证输出
ls /tmp/cwos-portal-unpack-test/ | wc -l
# 预期: > 10 个模块文件
head -5 /tmp/cwos-portal-unpack-test/module_*.js
- Step 4: Commit
git add frontend-source/scripts/unpack-webpack.js
git commit -m "feat: add webpack bundle unpacker script"
Task 2: 批量格式化脚本
Files:
-
Create:
frontend-source/scripts/beautify-all.sh -
Step 1: 编写格式化脚本
#!/bin/bash
# 用法: bash beautify-all.sh <input-dir> <output-dir>
INPUT_DIR="${1:-.}"
OUTPUT_DIR="${2:-./formatted}"
mkdir -p "$OUTPUT_DIR"
count=0
for file in "$INPUT_DIR"/*.js; do
base=$(basename "$file")
output="$OUTPUT_DIR/${base%.js}.formatted.js"
# 先用 js-beautify 还原缩进和换行
npx js-beautify "$file" \
--indent-size 2 \
--brace-style collapse \
--wrap-line-length 120 \
> "$output.tmp" 2>/dev/null
# 再用 prettier 统一风格
npx prettier --write "$output.tmp" \
--parser babel \
--print-width 120 \
--tab-width 2 \
--single-quote true \
--trailing-comma es5 \
2>/dev/null
mv "$output.tmp" "$output"
count=$((count + 1))
echo "格式化: $base → $(basename "$output")"
done
echo "完成: $count 个文件已格式化"
- Step 2: 测试格式化
cd frontend-source/scripts
bash beautify-all.sh /tmp/cwos-portal-unpack-test /tmp/cwos-portal-formatted-test
head -30 /tmp/cwos-portal-formatted-test/module_*.formatted.js | head -30
- Step 3: Commit
git add frontend-source/scripts/beautify-all.sh
git commit -m "feat: add batch beautifier script"
Task 3: API 调用提取器
Files:
-
Create:
frontend-source/scripts/extract-api-calls.js -
Step 1: 编写提取脚本
#!/usr/bin/env node
/**
* API 调用提取器
* 从格式化后的 JS 中提取 HTTP 请求模式,输出 API 清单 JSON
*
* 用法: node extract-api-calls.js <formatted-dir> <output.json>
*/
const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
function extractApiCalls(inputDir, outputFile) {
const apiCalls = [];
const files = fs.readdirSync(inputDir).filter(f => f.endsWith('.formatted.js'));
for (const file of files) {
const code = fs.readFileSync(path.join(inputDir, file), 'utf-8');
try {
const ast = parser.parse(code, {
sourceType: 'script',
plugins: ['jsx', 'typescript', 'classProperties', 'dynamicImport'],
errorRecovery: true,
});
traverse(ast, {
// 匹配: axios.get('/api/xxx')
// 匹配: axios.post('/api/xxx', data)
// 匹配: this.$http.get('/api/xxx')
CallExpression(nodePath) {
const node = nodePath.node;
const callee = node.callee;
// axios.get|post|put|delete|patch
if (
callee.type === 'MemberExpression' &&
callee.object.type === 'Identifier' &&
(callee.object.name === 'axios' || callee.object.name === 'http')
) {
const method = callee.property.name;
if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) {
const url = node.arguments[0];
if (url && url.type === 'StringLiteral') {
apiCalls.push({
file: file,
method: method.toUpperCase(),
url: url.value,
hasBody: method !== 'get' && node.arguments.length > 1,
});
}
}
}
// this.$http.get|post
if (
callee.type === 'MemberExpression' &&
callee.object.type === 'MemberExpression' &&
callee.object.property.name === '$http'
) {
const method = callee.property.name;
const url = node.arguments[0];
if (url && url.type === 'StringLiteral') {
apiCalls.push({
file: file,
method: method.toUpperCase(),
url: url.value,
via: '$http',
});
}
}
},
// 匹配字符串常量中的 API 路径
StringLiteral(nodePath) {
const value = nodePath.node.value;
if (typeof value === 'string' && value.startsWith('/api/') && value.length > 5) {
// 避免重复捕获(CallExpression 已处理)
const parent = nodePath.parent;
if (parent.type === 'CallExpression') return;
apiCalls.push({
file: file,
method: 'REFERENCE',
url: value,
});
}
},
});
} catch (e) {
console.error(`解析失败: ${file} — ${e.message}`);
}
}
// 去重 + 排序
const unique = {};
apiCalls.forEach(call => {
const key = `${call.method} ${call.url}`;
if (!unique[key]) {
unique[key] = call;
}
});
const result = Object.values(unique).sort((a, b) => a.url.localeCompare(b.url));
fs.writeFileSync(outputFile, JSON.stringify(result, null, 2));
console.log(`提取完成: ${result.length} 个 API 端点 → ${outputFile}`);
// 同时生成 Markdown 清单
const mdPath = outputFile.replace('.json', '.md');
let md = '# API 端点清单\n\n';
md += `| Method | URL | 来源文件 |\n`;
md += `|--------|-----|----------|\n`;
result.forEach(call => {
md += `| ${call.method} | \`${call.url}\` | ${call.file} |\n`;
});
fs.writeFileSync(mdPath, md);
console.log(`Markdown 清单: ${mdPath}`);
}
const args = process.argv.slice(2);
if (args.length < 2) {
console.log('用法: node extract-api-calls.js <formatted-dir> <output.json>');
process.exit(1);
}
extractApiCalls(args[0], args[1]);
- Step 2: 测试 API 提取
cd frontend-source/scripts
node extract-api-calls.js /tmp/cwos-portal-formatted-test /tmp/cwos-portal-api.json
cat /tmp/cwos-portal-api.md | head -30
- Step 3: Commit
git add frontend-source/scripts/extract-api-calls.js
git commit -m "feat: add API call extractor script"
Task 4: 路由表提取器
Files:
-
Create:
frontend-source/scripts/extract-router.js -
Step 1: 编写提取脚本
#!/usr/bin/env node
/**
* 路由表提取器
* 从格式化后的 JS 中提取 vue-router 的 routes 定义
*
* 用法: node extract-router.js <formatted-dir> <output.json>
*/
const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
function extractRouter(inputDir, outputFile) {
const files = fs.readdirSync(inputDir).filter(f => f.endsWith('.formatted.js'));
const routes = [];
for (const file of files) {
const code = fs.readFileSync(path.join(inputDir, file), 'utf-8');
try {
const ast = parser.parse(code, {
sourceType: 'script',
plugins: ['jsx', 'typescript', 'classProperties', 'dynamicImport'],
errorRecovery: true,
});
traverse(ast, {
// 匹配 routes: [...] 数组
ArrayExpression(nodePath) {
const node = nodePath.node;
const parent = nodePath.parent;
// 检查是否是路由数组 (包含 path 和 component 属性的对象)
const hasRoutes = node.elements.some(el =>
el.type === 'ObjectExpression' &&
el.properties.some(prop => prop.key.name === 'path')
);
if (hasRoutes) {
node.elements.forEach(el => {
if (el.type === 'ObjectExpression') {
const routeDef = {};
el.properties.forEach(prop => {
if (prop.key.type === 'Identifier') {
if (prop.key.name === 'path' && prop.value.type === 'StringLiteral') {
routeDef.path = prop.value.value;
}
if (prop.key.name === 'name' && prop.value.type === 'StringLiteral') {
routeDef.name = prop.value.value;
}
if (prop.key.name === 'meta' && prop.value.type === 'ObjectExpression') {
routeDef.meta = {};
prop.value.properties.forEach(mp => {
if (mp.key.type === 'Identifier') {
routeDef.meta[mp.key.name] = mp.value.type === 'StringLiteral' ? mp.value.value : true;
}
});
}
if (prop.key.name === 'component') {
routeDef.component = prop.value.type === 'ArrowFunctionExpression'
? 'lazyImport'
: (prop.value.type === 'Identifier' ? prop.value.name : 'inline');
}
}
});
if (routeDef.path && !routes.find(r => r.path === routeDef.path)) {
routes.push(routeDef);
}
}
});
}
},
// 匹配字符串中的路径模式 /xxx/xxx
StringLiteral(nodePath) {
const value = nodePath.node.value;
if (typeof value === 'string' && /^\/[a-z][a-z0-9_/-]*$/i.test(value) && value.length > 2) {
const parent = nodePath.parent;
if (parent.type === 'ObjectProperty' && parent.key.name === 'path') {
return; // 已被上面处理
}
if (!routes.find(r => r.path === value)) {
routes.push({ path: value, source: 'stringLiteral', file: file });
}
}
},
});
} catch (e) {
// 跳过解析错误的文件
}
}
const result = routes.sort((a, b) => (a.path || '').localeCompare(b.path || ''));
fs.writeFileSync(outputFile, JSON.stringify(result, null, 2));
console.log(`提取完成: ${result.length} 条路由 → ${outputFile}`);
// 生成 Markdown 路由树
const mdPath = outputFile.replace('.json', '.md');
let md = '# 路由表\n\n';
md += `| Path | Name | Component | Meta |\n`;
md += `|------|------|-----------|------|\n`;
result.forEach(r => {
md += `| \`${r.path || '-'}\` | ${r.name || '-'} | ${r.component || '-'} | ${r.meta ? JSON.stringify(r.meta) : '-'} |\n`;
});
fs.writeFileSync(mdPath, md);
console.log(`路由表 Markdown: ${mdPath}`);
}
const args = process.argv.slice(2);
if (args.length < 2) {
console.log('用法: node extract-router.js <formatted-dir> <output.json>');
process.exit(1);
}
extractRouter(args[0], args[1]);
- Step 2: 测试路由提取
cd frontend-source/scripts
node extract-router.js /tmp/cwos-portal-formatted-test /tmp/cwos-portal-router.json
cat /tmp/cwos-portal-router.md
- Step 3: Commit
git add frontend-source/scripts/extract-router.js
git commit -m "feat: add router table extractor script"
阶段 2-A:P0 — cwos-portal 反编译验证
Task 5: cwos-portal 解包与格式化
Files:
-
Create:
frontend-source/decompiled/cwos-portal/ -
Step 1: 定位 cwos-portal 的 JS bundle
ls -la frontend/cwos-portal/static/js/app.*.js
ls -la frontend/cwos-portal/static/js/vendor.*.js
ls -la frontend/cwos-portal/static/js/manifest.*.js
- Step 2: 解包所有 bundle
cd frontend-source/scripts
mkdir -p /tmp/cwos-portal-raw
for js in ../../frontend/cwos-portal/static/js/app.*.js; do
node unpack-webpack.js "$js" /tmp/cwos-portal-raw
done
for js in ../../frontend/cwos-portal/static/js/vendor.*.js; do
node unpack-webpack.js "$js" /tmp/cwos-portal-raw
done
ls /tmp/cwos-portal-raw/ | wc -l
- Step 3: 格式化所有模块
cd frontend-source/scripts
mkdir -p /tmp/cwos-portal-formatted
bash beautify-all.sh /tmp/cwos-portal-raw /tmp/cwos-portal-formatted
ls /tmp/cwos-portal-formatted/ | wc -l
- Step 4: 提取 API 清单
cd frontend-source/scripts
node extract-api-calls.js /tmp/cwos-portal-formatted ../decompiled/cwos-portal/api-calls.json
- Step 5: 提取路由表
cd frontend-source/scripts
node extract-router.js /tmp/cwos-portal-formatted ../decompiled/cwos-portal/router-tree.json
- Step 6: 组装到 frontend-source/decompiled/cwos-portal/
mkdir -p frontend-source/decompiled/cwos-portal/src/{views,components,api,router}
mkdir -p frontend-source/decompiled/cwos-portal/static
# 复制格式化后的模块
cp /tmp/cwos-portal-formatted/*.formatted.js frontend-source/decompiled/cwos-portal/src/
# 复制原始 static 文件
cp -r frontend/cwos-portal/static/* frontend-source/decompiled/cwos-portal/static/
cp frontend/cwos-portal/index.html frontend-source/decompiled/cwos-portal/
cp frontend/cwos-portal/favicon.ico frontend-source/decompiled/cwos-portal/
cp frontend/cwos-portal/projectInfo.json frontend-source/decompiled/cwos-portal/
# API 和路由已在步骤 4-5 中直接写入
ls frontend-source/decompiled/cwos-portal/
- Step 7: 编写 README.md
写入 frontend-source/decompiled/cwos-portal/README.md:
# cwos-portal — 物业管理门户
## 来源
- 原始 dist: `frontend/cwos-portal/`
- 构建日期: 2022-01-12
- 技术栈: Vue 2 + Element UI + webpack
## API 端点
详见 [api-calls.md](api-calls.md)
## 路由表
详见 [router-tree.md](router-tree.md)
## 目录结构
- `src/` — 格式化的 JS 模块
- `static/` — CSS/img/font 原始文件
- `index.html` — 原始入口
- `projectInfo.json` — 项目配置
- Step 8: Commit
git add frontend-source/decompiled/cwos-portal/
git commit -m "feat: add cwos-portal decompiled source (P0 verification)"
阶段 2-B:P1 核心应用反编译
Task 6: elevator-front 反编译
Files:
-
Create:
frontend-source/decompiled/elevator-front/ -
Step 1: 解包 elevator-front
cd frontend-source/scripts
mkdir -p /tmp/elevator-front-raw /tmp/elevator-front-formatted
for js in ../../frontend/elevator-front/js/*.js; do
node unpack-webpack.js "$js" /tmp/elevator-front-raw
done
bash beautify-all.sh /tmp/elevator-front-raw /tmp/elevator-front-formatted
node extract-api-calls.js /tmp/elevator-front-formatted ../decompiled/elevator-front/api-calls.json
node extract-router.js /tmp/elevator-front-formatted ../decompiled/elevator-front/router-tree.json
- Step 2: 组织文件并复制静态资源
mkdir -p frontend-source/decompiled/elevator-front/src
mkdir -p frontend-source/decompiled/elevator-front/static
cp /tmp/elevator-front-formatted/*.formatted.js frontend-source/decompiled/elevator-front/src/
cp -r frontend/elevator-front/css frontend-source/decompiled/elevator-front/static/
cp -r frontend/elevator-front/img frontend-source/decompiled/elevator-front/static/
cp -r frontend/elevator-front/fonts frontend-source/decompiled/elevator-front/static/ 2>/dev/null
cp frontend/elevator-front/index.html frontend-source/decompiled/elevator-front/
cp frontend/elevator-front/favicon.ico frontend-source/decompiled/elevator-front/ 2>/dev/null
- Step 3: 编写 README.md
写入 frontend-source/decompiled/elevator-front/README.md:
# elevator-front — 电梯管理前端
## 来源
- 原始 dist: `frontend/elevator-front/`
- 构建日期: 2023-10-19
- 技术栈: Vue 2 + Element UI + webpack
详见 [api-calls.md](api-calls.md) 和 [router-tree.md](router-tree.md)
- Step 4: Commit
git add frontend-source/decompiled/elevator-front/
git commit -m "feat: add elevator-front decompiled source"
Task 7: alarm-front 反编译
Files:
-
Create:
frontend-source/decompiled/alarm-front/ -
Step 1: 解包 alarm-front
cd frontend-source/scripts
mkdir -p /tmp/alarm-front-raw /tmp/alarm-front-formatted
for js in ../../frontend/alarm-front/js/*.js; do
node unpack-webpack.js "$js" /tmp/alarm-front-raw
done
bash beautify-all.sh /tmp/alarm-front-raw /tmp/alarm-front-formatted
node extract-api-calls.js /tmp/alarm-front-formatted ../decompiled/alarm-front/api-calls.json
node extract-router.js /tmp/alarm-front-formatted ../decompiled/alarm-front/router-tree.json
- Step 2: 组织文件
mkdir -p frontend-source/decompiled/alarm-front/src
mkdir -p frontend-source/decompiled/alarm-front/static
cp /tmp/alarm-front-formatted/*.formatted.js frontend-source/decompiled/alarm-front/src/
cp -r frontend/alarm-front/css frontend-source/decompiled/alarm-front/static/
cp -r frontend/alarm-front/img frontend-source/decompiled/alarm-front/static/
cp -r frontend/alarm-front/fonts frontend-source/decompiled/alarm-front/static/ 2>/dev/null
cp -r frontend/alarm-front/media frontend-source/decompiled/alarm-front/static/ 2>/dev/null
cp frontend/alarm-front/index.html frontend-source/decompiled/alarm-front/
cp frontend/alarm-front/favicon.ico frontend-source/decompiled/alarm-front/ 2>/dev/null
- Step 3: 编写 README.md
写入 frontend-source/decompiled/alarm-front/README.md:
# alarm-front — 报警管理前端
## 来源
- 原始 dist: `frontend/alarm-front/`
- 构建日期: 2023-11-09
- 技术栈: Vue 2 + Element UI + webpack
详见 [api-calls.md](api-calls.md) 和 [router-tree.md](router-tree.md)
- Step 4: Commit
git add frontend-source/decompiled/alarm-front/
git commit -m "feat: add alarm-front decompiled source"
Task 8: front_acs 反编译
Files:
-
Create:
frontend-source/decompiled/front_acs/ -
Step 1: 解包 front_acs
cd frontend-source/scripts
mkdir -p /tmp/front-acs-raw /tmp/front-acs-formatted
for js in ../../frontend/front_acs/static/js/*.js; do
node unpack-webpack.js "$js" /tmp/front-acs-raw
done
bash beautify-all.sh /tmp/front-acs-raw /tmp/front-acs-formatted
node extract-api-calls.js /tmp/front-acs-formatted ../decompiled/front_acs/api-calls.json
node extract-router.js /tmp/front-acs-formatted ../decompiled/front_acs/router-tree.json
- Step 2: 组织文件
mkdir -p frontend-source/decompiled/front_acs/src
mkdir -p frontend-source/decompiled/front_acs/static
cp /tmp/front-acs-formatted/*.formatted.js frontend-source/decompiled/front_acs/src/
cp -r frontend/front_acs/static/* frontend-source/decompiled/front_acs/static/
cp frontend/front_acs/index.html frontend-source/decompiled/front_acs/
cp frontend/front_acs/favicon.ico frontend-source/decompiled/front_acs/
cp frontend/front_acs/projectInfo.json frontend-source/decompiled/front_acs/ 2>/dev/null
- Step 3: 编写 README.md
写入 frontend-source/decompiled/front_acs/README.md:
# front_acs — 门禁管理前端
## 来源
- 原始 dist: `frontend/front_acs/`
- 构建日期: 2023-12-22
- 技术栈: Vue 2 + Element UI + webpack
详见 [api-calls.md](api-calls.md) 和 [router-tree.md](router-tree.md)
- Step 4: Commit
git add frontend-source/decompiled/front_acs/
git commit -m "feat: add front_acs decompiled source"
阶段 2-C:P2-P3 批量反编译
Task 9: Canoe 系列批量反编译
Files:
-
Create:
frontend-source/decompiled/canoe-{account,car,device,person,person-h5}/ -
Step 1: 批量解包脚本
cd frontend-source/scripts
APPS="canoe-account canoe-car canoe-device canoe-person canoe-person-h5"
for app in $APPS; do
echo "=== 处理: $app ==="
mkdir -p /tmp/${app}-raw /tmp/${app}-formatted
JS_DIR="../../frontend/${app}/js"
if [ -d "$JS_DIR" ]; then
for js in "$JS_DIR"/*.js; do
node unpack-webpack.js "$js" /tmp/${app}-raw
done
fi
# 部分应用 js 在 static/js/
STATIC_JS="../../frontend/${app}/static/js"
if [ -d "$STATIC_JS" ]; then
for js in "$STATIC_JS"/*.js; do
node unpack-webpack.js "$js" /tmp/${app}-raw
done
fi
bash beautify-all.sh /tmp/${app}-raw /tmp/${app}-formatted
node extract-api-calls.js /tmp/${app}-formatted ../decompiled/${app}/api-calls.json 2>/dev/null
node extract-router.js /tmp/${app}-formatted ../decompiled/${app}/router-tree.json 2>/dev/null
mkdir -p ../decompiled/${app}/src ../decompiled/${app}/static
cp /tmp/${app}-formatted/*.formatted.js ../decompiled/${app}/src/ 2>/dev/null
cp -r ../../frontend/${app}/css ../decompiled/${app}/static/ 2>/dev/null
cp -r ../../frontend/${app}/js ../decompiled/${app}/static/ 2>/dev/null
cp -r ../../frontend/${app}/img ../decompiled/${app}/static/ 2>/dev/null
cp -r ../../frontend/${app}/static ../decompiled/${app}/ 2>/dev/null
cp ../../frontend/${app}/index.html ../decompiled/${app}/ 2>/dev/null
cp ../../frontend/${app}/favicon.ico ../decompiled/${app}/ 2>/dev/null
echo "README: $app" > ../decompiled/${app}/README.md
echo "" >> ../decompiled/${app}/README.md
echo "来源: frontend/${app}/" >> ../decompiled/${app}/README.md
done
echo "批量处理完成"
- Step 2: Commit
git add frontend-source/decompiled/canoe-account/ \
frontend-source/decompiled/canoe-car/ \
frontend-source/decompiled/canoe-device/ \
frontend-source/decompiled/canoe-person/ \
frontend-source/decompiled/canoe-person-h5/
git commit -m "feat: add Canoe series decompiled sources"
Task 10: 其余 20 个应用批量反编译
Files:
-
Create:
frontend-source/decompiled/{dashboard-front,homepage,...}/(20个) -
Step 1: 批量处理脚本
cd frontend-source/scripts
# 门禁/电梯辅助应用
APPS_P2="area-front personFile monitor-front snap-front heat-analysis-portal"
# 访客/考勤/会议
APPS_P2="$APPS_P2 visitorpc visitorh5 conferencepc attendancepc"
# 系统辅助
APPS_P2="$APPS_P2 front_message sysetting-front ninca-engine-service"
# Canoe 辅助
APPS_P2="$APPS_P2 canoe-account canoe-car canoe-device canoe-person"
# 其他
APPS_P2="$APPS_P2 cwos_manager_frontend"
for app in $APPS_P2; do
[ ! -d "../../frontend/${app}" ] && continue
echo "=== 处理: $app ==="
mkdir -p /tmp/${app}-raw /tmp/${app}-formatted
for js_dir in "js" "static/js"; do
if [ -d "../../frontend/${app}/${js_dir}" ]; then
for js in ../../frontend/${app}/${js_dir}/*.js; do
node unpack-webpack.js "$js" /tmp/${app}-raw 2>/dev/null
done
fi
done
bash beautify-all.sh /tmp/${app}-raw /tmp/${app}-formatted 2>/dev/null
mkdir -p ../decompiled/${app}/src ../decompiled/${app}/static
cp /tmp/${app}-formatted/*.formatted.js ../decompiled/${app}/src/ 2>/dev/null
cp -r ../../frontend/${app}/static ../decompiled/${app}/ 2>/dev/null
cp ../../frontend/${app}/index.html ../decompiled/${app}/ 2>/dev/null
cp ../../frontend/${app}/favicon.ico ../decompiled/${app}/ 2>/dev/null
echo "# $app" > ../decompiled/${app}/README.md
echo "来源: frontend/${app}/" >> ../decompiled/${app}/README.md
echo " ✓ $app"
done
echo "全部批量处理完成"
- Step 2: Commit
git add frontend-source/decompiled/
git commit -m "feat: add remaining 20 apps decompiled sources (bulk)"
阶段 3:核心应用 C 级重建
Task 11: cwos-portal 项目脚手架
Files:
-
Create:
frontend-source/projects/cwos-portal/ -
Step 1: 手动搭建 Vue 2 项目(不用 vue create)
写入 frontend-source/projects/cwos-portal/package.json:
{
"name": "cwos-portal",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.4",
"element-ui": "^2.15.14",
"vue": "^2.6.14",
"vue-router": "^3.5.3",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.19",
"@vue/cli-plugin-router": "^4.5.19",
"@vue/cli-plugin-vuex": "^4.5.19",
"@vue/cli-service": "^4.5.19",
"vue-template-compiler": "^2.6.14"
}
}
- Step 2: 创建 vue.config.js
写入 frontend-source/projects/cwos-portal/vue.config.js:
module.exports = {
publicPath: './',
outputDir: 'dist',
assetsDir: 'static',
productionSourceMap: false,
devServer: {
port: 8080,
proxy: {
'/api': {
target: 'http://localhost:8090',
changeOrigin: true,
},
},
},
};
- Step 3: 创建 babel.config.js
写入 frontend-source/projects/cwos-portal/babel.config.js:
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
};
- Step 4: 创建入口文件
写入 frontend-source/projects/cwos-portal/public/index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<link rel="shortcut icon" href="./favicon.ico">
<title>物业管理门户</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
- Step 5: 创建 main.js 和 App.vue
写入 frontend-source/projects/cwos-portal/src/main.js:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
import router from './router';
import store from './store';
import axios from 'axios';
Vue.use(ElementUI);
Vue.prototype.$http = axios;
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
写入 frontend-source/projects/cwos-portal/src/App.vue:
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
<style>
#app {
height: 100%;
}
</style>
- Step 6: 创建路由和 store 骨架
写入 frontend-source/projects/cwos-portal/src/router/index.js:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
// 路由从 decompiled/cwos-portal/router-tree.md 提取后填入
// 参考 router-tree.md 中的路径列表,逐个转换为 route 对象
// 示例格式:
// { path: '/dashboard', name: 'Dashboard', component: () => import('@/views/Dashboard.vue') }
const routes = [];
const router = new VueRouter({
mode: 'hash',
routes,
});
export default router;
写入 frontend-source/projects/cwos-portal/src/store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {},
});
- Step 7: 创建 API 层骨架
写入 frontend-source/projects/cwos-portal/src/api/index.js:
import axios from 'axios';
const api = axios.create({
baseURL: '/api',
timeout: 30000,
});
// 请求拦截器
api.interceptors.request.use((config) => {
// token 注入(参考原 config.js)
return config;
});
// 响应拦截器
api.interceptors.response.use(
(response) => response.data,
(error) => Promise.reject(error)
);
export default api;
- Step 8: 安装依赖并验证构建
cd frontend-source/projects/cwos-portal && npm install
npm run build
- Step 9: Commit
git add frontend-source/projects/cwos-portal/
git commit -m "feat: scaffold cwos-portal Vue 2 project (skeleton)"
Task 12: 其他 3 个核心应用脚手架
Files:
-
Create:
frontend-source/projects/{elevator-front,alarm-front,front_acs}/ -
Step 1: 批量创建脚手架(结构与 Task 11 一致,调整应用名)
cd frontend-source/projects
for app in elevator-front alarm-front front_acs; do
mkdir -p $app/public $app/src/{views,components,api,router,store}
# package.json (相同的依赖,只改 name)
cat > $app/package.json << EOF
{
"name": "$app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"axios": "^0.21.4",
"element-ui": "^2.15.14",
"vue": "^2.6.14",
"vue-router": "^3.5.3",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.19",
"@vue/cli-plugin-router": "^4.5.19",
"@vue/cli-plugin-vuex": "^4.5.19",
"@vue/cli-service": "^4.5.19",
"vue-template-compiler": "^2.6.14"
}
}
EOF
# vue.config.js, babel.config.js, public/index.html, src/main.js, src/App.vue,
# src/router/index.js, src/store/index.js, src/api/index.js
# (复制项目模板,使用前面 Task 11 的相同内容,修改标题即可)
cd $app && npm install && npm run build && cd ..
done
- Step 2: Commit
git add frontend-source/projects/elevator-front/ \
frontend-source/projects/alarm-front/ \
frontend-source/projects/front_acs/
git commit -m "feat: scaffold 3 core app Vue 2 projects (skeletons)"
阶段 4:文档与最终校验
Task 13: frontend-source/AGENTS.md
Files:
-
Create:
frontend-source/AGENTS.md -
Step 1: 编写目录说明
写入 frontend-source/AGENTS.md:
# frontend-source/ — 前端源码目录
## OVERVIEW
从 `frontend/` dist 产物反编译还原的前端源码。分为两类:
- `decompiled/` — B 级反编译(全部 28 个应用,可读参考)
- `projects/` — C 级重建(4 个核心应用,可构建部署)
## WHERE TO LOOK
| 任务 | 目录 | 说明 |
|------|------|------|
| 查看任意应用源码 | `decompiled/<app>/src/` | 格式化后的 JS 模块 |
| 查看 API 清单 | `decompiled/<app>/api-calls.md` | HTTP 端点列表 |
| 查看路由表 | `decompiled/<app>/router-tree.md` | 页面路由树 |
| 开发核心应用 | `projects/<app>/` | 可构建的 Vue 2 工程 |
| 反编译工具 | `scripts/` | unpack-webpack 等 |
## CONVENTIONS
- `decompiled/` 下文件**不可用于构建**,仅供阅读参考
- `projects/` 下工程可正常 `npm run dev` / `npm run build`
- 技术栈: Vue 2.6 + Element UI 2.15 + axios + vue-router 3 + vuex 3
- 构建使用 Vue CLI 4.5
## ANTI-PATTERNS
- 不要修改 `decompiled/` 下的源码后试图构建 — 这些是反编译参考
- 不要混用 Vue 3 / Element Plus — 阶段 2 必须用 Vue 2 + Element UI
- 不要删除 `frontend/` 原 dist 目录 — 它是唯一定稿的参考来源
## NOTES
- 反编译工具在 `scripts/` 下,Node.js 18+
- 阶段 2 重建的重点包含 `projects/` 下 4 个应用的逐页面重写
- 原始源码完全丢失,JS bundle 反编译无法恢复原始变量名
- Step 2: Commit
git add frontend-source/AGENTS.md
git commit -m "docs: add frontend-source AGENTS.md"
Task 14: 最终校验
- Step 1: 校验目录完整性
echo "=== decompiled/ 应用数 ==="
ls frontend-source/decompiled/ | wc -l
echo "预期: 28"
echo "=== projects/ 应用数 ==="
ls frontend-source/projects/ | wc -l
echo "预期: 4"
echo "=== 脚本文件数 ==="
ls frontend-source/scripts/*.js frontend-source/scripts/*.sh 2>/dev/null | wc -l
echo "预期: 5 (3 js + 2 sh)"
- Step 2: 校验 4 个核心项目可构建
for app in cwos-portal elevator-front alarm-front front_acs; do
echo "=== 构建: $app ==="
cd frontend-source/projects/$app && npm run build 2>&1 | tail -3 || echo "构建失败"
cd ../../..
done
- Step 3: Commit
git add frontend-source/
git commit -m "chore: final verification of frontend-source directory"
计划总结
| 阶段 | 任务数 | 预计时间 |
|---|---|---|
| 前置准备 | Task 0 | 0.5h |
| 工具开发 | Task 1-4 | 2h |
| P0 验证 | Task 5 | 1h |
| P1 核心 | Task 6-8 | 1.5h |
| P2-P3 批量 | Task 9-10 | 1h |
| C 级重建 | Task 11-12 | 2h |
| 文档校验 | Task 13-14 | 0.5h |
| 合计 | 14 Tasks | ~8.5h |