Files
starRiverProperty/docs/superpowers/plans/2026-04-29-frontend-source.md
T
hpd840321 7b2bd307f1 Initial commit: reorganized source tree
- 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.
2026-05-09 09:56:45 +08:00

37 KiB
Raw Blame History

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-AP0 — 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-BP1 核心应用反编译

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-CP2-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