Files
starRiverProperty/docs/superpowers/plans/2026-04-29-frontend-source.md
T
反编译工作区 e8672a3c7b fix: relocate cwos-portal decompiled output to correct path; remove nested directory
Former-commit-id: dc30d42a8c55ed8b2382a41dc2434233fbed9930
2026-04-29 12:09:48 +08:00

1350 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: 创建目录结构**
```bash
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`:
```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: 安装依赖**
```bash
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**
```bash
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: 编写解包脚本**
```javascript
#!/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 进行解包测试**
```bash
cd frontend-source/scripts
node unpack-webpack.js ../frontend/cwos-portal/static/js/app.32c5fb6ab7ff973ea14d.js /tmp/cwos-portal-unpack-test
```
- [ ] **Step 3: 验证输出**
```bash
ls /tmp/cwos-portal-unpack-test/ | wc -l
# 预期: > 10 个模块文件
head -5 /tmp/cwos-portal-unpack-test/module_*.js
```
- [ ] **Step 4: Commit**
```bash
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: 编写格式化脚本**
```bash
#!/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: 测试格式化**
```bash
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**
```bash
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: 编写提取脚本**
```javascript
#!/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 提取**
```bash
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**
```bash
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: 编写提取脚本**
```javascript
#!/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: 测试路由提取**
```bash
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**
```bash
git add frontend-source/scripts/extract-router.js
git commit -m "feat: add router table extractor script"
```
---
## 阶段 2-AP0 — cwos-portal 反编译验证
### Task 5: cwos-portal 解包与格式化
**Files:**
- Create: `frontend-source/decompiled/cwos-portal/`
- [ ] **Step 1: 定位 cwos-portal 的 JS bundle**
```bash
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**
```bash
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: 格式化所有模块**
```bash
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 清单**
```bash
cd frontend-source/scripts
node extract-api-calls.js /tmp/cwos-portal-formatted ../decompiled/cwos-portal/api-calls.json
```
- [ ] **Step 5: 提取路由表**
```bash
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/**
```bash
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`:
```markdown
# 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**
```bash
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**
```bash
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: 组织文件并复制静态资源**
```bash
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`:
```markdown
# 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**
```bash
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**
```bash
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: 组织文件**
```bash
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`:
```markdown
# 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**
```bash
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**
```bash
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: 组织文件**
```bash
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`:
```markdown
# 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**
```bash
git add frontend-source/decompiled/front_acs/
git commit -m "feat: add front_acs decompiled source"
```
---
## 阶段 2-CP2-P3 批量反编译
### Task 9: Canoe 系列批量反编译
**Files:**
- Create: `frontend-source/decompiled/canoe-{account,car,device,person,person-h5}/`
- [ ] **Step 1: 批量解包脚本**
```bash
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**
```bash
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: 批量处理脚本**
```bash
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**
```bash
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`:
```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`:
```javascript
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`:
```javascript
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
};
```
- [ ] **Step 4: 创建入口文件**
写入 `frontend-source/projects/cwos-portal/public/index.html`:
```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`:
```javascript
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`:
```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`:
```javascript
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`:
```javascript
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`:
```javascript
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: 安装依赖并验证构建**
```bash
cd frontend-source/projects/cwos-portal && npm install
npm run build
```
- [ ] **Step 9: Commit**
```bash
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 一致,调整应用名)
```bash
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**
```bash
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`:
```markdown
# 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**
```bash
git add frontend-source/AGENTS.md
git commit -m "docs: add frontend-source AGENTS.md"
```
### Task 14: 最终校验
- [ ] **Step 1: 校验目录完整性**
```bash
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 个核心项目可构建**
```bash
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**
```bash
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** |