优化分批执行点击菜单
This commit is contained in:
parent
3571f5fb0f
commit
eb8d523d91
159
controllers/TestController.js
Normal file
159
controllers/TestController.js
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
const { chromium } = require('@playwright/test');
|
||||||
|
const LongiMainPage = require('../tests/pages/LongiMainPage');
|
||||||
|
const LongiLoginPage = require('../tests/pages/LongiLoginPage');
|
||||||
|
const menuDataService = require('../services/MenuDataService');
|
||||||
|
|
||||||
|
class TestController {
|
||||||
|
/**
|
||||||
|
* @param {Object} options - 配置选项
|
||||||
|
* @param {number} [options.batchSize=5] - 每批次测试的菜单数量
|
||||||
|
* @param {number} [options.retryCount=3] - 失败重试次数
|
||||||
|
* @param {number} [options.batchInterval=2000] - 批次间隔时间(ms)
|
||||||
|
*/
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.batchSize = options.batchSize || 5;
|
||||||
|
this.retryCount = options.retryCount || 3;
|
||||||
|
this.batchInterval = options.batchInterval || 2000;
|
||||||
|
|
||||||
|
// 浏览器配置
|
||||||
|
this.browserConfig = {
|
||||||
|
headless: false, // 使用有头模式
|
||||||
|
args: ['--start-maximized'], // 最大化窗口
|
||||||
|
slowMo: 50 // 放慢操作速度,便于观察
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行登录操作
|
||||||
|
* @param {import('@playwright/test').Page} page - Playwright页面对象
|
||||||
|
* @returns {Promise<boolean>} 登录是否成功
|
||||||
|
*/
|
||||||
|
async performLogin(page) {
|
||||||
|
const loginPage = new LongiLoginPage(page);
|
||||||
|
|
||||||
|
// 导航到登录页面
|
||||||
|
await loginPage.navigateToLoginPage();
|
||||||
|
|
||||||
|
// 点击登录按钮
|
||||||
|
const loginSuccess = await loginPage.clickLoginButton();
|
||||||
|
|
||||||
|
if (!loginSuccess) {
|
||||||
|
console.error('登录失败');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('登录成功');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集菜单数据
|
||||||
|
* @returns {Promise<Array>} - 处理后的菜单数据
|
||||||
|
*/
|
||||||
|
async collectMenuData() {
|
||||||
|
console.log('开始收集菜单数据...');
|
||||||
|
const browser = await chromium.launch(this.browserConfig);
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// 设置视窗大小
|
||||||
|
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||||
|
|
||||||
|
const mainPage = new LongiMainPage(page);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 LongiLoginPage 处理登录
|
||||||
|
const loginSuccess = await this.performLogin(page);
|
||||||
|
if (!loginSuccess) {
|
||||||
|
throw new Error('登录失败,无法收集菜单数据');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('登录成功,正在获取菜单项...');
|
||||||
|
const menuItems = await mainPage.checkAndLoadMenuItems();
|
||||||
|
console.log(`成功获取 ${menuItems.length} 个菜单项`);
|
||||||
|
return menuDataService.saveMenuData(menuItems);
|
||||||
|
} finally {
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一批菜单的测试
|
||||||
|
* @param {Array} menuBatch - 要测试的菜单数组
|
||||||
|
*/
|
||||||
|
async runBatchTest(menuBatch) {
|
||||||
|
console.log(`开始执行批次测试,包含 ${menuBatch.length} 个菜单项:`);
|
||||||
|
console.log(menuBatch.map(m => m.text).join(', '));
|
||||||
|
|
||||||
|
const browser = await chromium.launch(this.browserConfig);
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// 设置视窗大小
|
||||||
|
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||||
|
|
||||||
|
const mainPage = new LongiMainPage(page);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 LongiLoginPage 处理登录
|
||||||
|
const loginSuccess = await this.performLogin(page);
|
||||||
|
if (!loginSuccess) {
|
||||||
|
throw new Error('登录失败,无法执行菜单测试');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('登录成功,开始测试菜单项...');
|
||||||
|
|
||||||
|
// 使用 handleAllMenuClicks 方法处理所有菜单
|
||||||
|
await mainPage.handleAllMenuClicks(menuBatch);
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
const progress = menuDataService.getProgress();
|
||||||
|
const newProgress = [...progress];
|
||||||
|
for (const menuItem of menuBatch) {
|
||||||
|
if (!newProgress.includes(menuItem.id)) {
|
||||||
|
newProgress.push(menuItem.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menuDataService.saveProgress(newProgress);
|
||||||
|
} finally {
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一批要测试的菜单
|
||||||
|
* @returns {Array|null} - 下一批要测试的菜单,如果没有则返回null
|
||||||
|
*/
|
||||||
|
getNextBatch() {
|
||||||
|
const menuData = menuDataService.getMenuData();
|
||||||
|
const progress = menuDataService.getProgress();
|
||||||
|
|
||||||
|
if (!menuData) return null;
|
||||||
|
|
||||||
|
// 过滤出未测试的菜单
|
||||||
|
const remainingMenus = menuData.filter(menu => !progress.includes(menu.id));
|
||||||
|
if (remainingMenus.length === 0) return null;
|
||||||
|
|
||||||
|
// 返回下一批要测试的菜单
|
||||||
|
return remainingMenus.slice(0, this.batchSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取测试进度信息
|
||||||
|
* @returns {Object} - 进度信息
|
||||||
|
*/
|
||||||
|
getTestProgress() {
|
||||||
|
const menuData = menuDataService.getMenuData();
|
||||||
|
const progress = menuDataService.getProgress();
|
||||||
|
|
||||||
|
if (!menuData) {
|
||||||
|
return { total: 0, completed: 0, remaining: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: menuData.length,
|
||||||
|
completed: progress.length,
|
||||||
|
remaining: menuData.length - progress.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TestController;
|
||||||
@ -9,7 +9,10 @@
|
|||||||
"report": "playwright show-report",
|
"report": "playwright show-report",
|
||||||
"codegen": "playwright codegen",
|
"codegen": "playwright codegen",
|
||||||
"debug": "playwright test --debug",
|
"debug": "playwright test --debug",
|
||||||
"test:longi:check-normal:dev": "cross-env NODE_ENV=dev playwright test tests/longi-ibp/check-page-normal.test.js --headed --project=chromium"
|
"test:longi:check-normal:dev": "cross-env NODE_ENV=dev node scripts/run-tests.js",
|
||||||
|
"test:longi:check-clean": "cross-env NODE_ENV=dev node scripts/run-tests.js --clean",
|
||||||
|
"test:longi:check-collect": "cross-env NODE_ENV=dev node scripts/run-tests.js --collect-only",
|
||||||
|
"test:longi:check-clean-continue": "cross-env NODE_ENV=dev node scripts/run-tests.js --clean --continue"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"playwright",
|
"playwright",
|
||||||
|
|||||||
@ -10,6 +10,12 @@
|
|||||||
// const path = require('path');
|
// const path = require('path');
|
||||||
// const fs = require('fs');
|
// const fs = require('fs');
|
||||||
//
|
//
|
||||||
|
// // 确保在其他任何代码之前加载环境变量
|
||||||
|
// require('../config/env');
|
||||||
|
//
|
||||||
|
// const TestController = require('../controllers/TestController');
|
||||||
|
// const menuDataService = require('../services/MenuDataService');
|
||||||
|
//
|
||||||
// // 设置命令行选项
|
// // 设置命令行选项
|
||||||
// program
|
// program
|
||||||
// .version('1.0.0')
|
// .version('1.0.0')
|
||||||
@ -89,3 +95,103 @@
|
|||||||
// console.error(chalk.red('测试执行失败:'), error.message);
|
// console.error(chalk.red('测试执行失败:'), error.message);
|
||||||
// process.exit(1);
|
// process.exit(1);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// 确保在其他任何代码之前加载环境变量
|
||||||
|
require('../config/env');
|
||||||
|
|
||||||
|
const TestController = require('../controllers/TestController');
|
||||||
|
const menuDataService = require('../services/MenuDataService');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化进度信息
|
||||||
|
* @param {Object} progress - 进度对象
|
||||||
|
* @returns {string} - 格式化的进度字符串
|
||||||
|
*/
|
||||||
|
function formatProgress(progress) {
|
||||||
|
const percentage = ((progress.completed / progress.total) * 100).toFixed(2);
|
||||||
|
return `进度: ${progress.completed}/${progress.total} (${percentage}%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主执行函数
|
||||||
|
*/
|
||||||
|
async function main() {
|
||||||
|
console.log('环境变量:', {
|
||||||
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
|
BASE_URL: process.env.BASE_URL,
|
||||||
|
MENU_DATA_FILE_PATH: process.env.MENU_DATA_FILE_PATH
|
||||||
|
});
|
||||||
|
|
||||||
|
const controller = new TestController({
|
||||||
|
batchSize: 5,
|
||||||
|
retryCount: 3,
|
||||||
|
batchInterval: 2000
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理命令行参数
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
if (args.includes('--clean')) {
|
||||||
|
menuDataService.clearAll();
|
||||||
|
console.log('已清理所有数据');
|
||||||
|
if (!args.includes('--continue')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 强制重新收集菜单数据
|
||||||
|
console.log('开始收集菜单数据...');
|
||||||
|
const menuData = await controller.collectMenuData();
|
||||||
|
console.log(`成功收集 ${menuData.length} 个菜单项`);
|
||||||
|
|
||||||
|
// 清理之前的进度
|
||||||
|
menuDataService.saveProgress([]);
|
||||||
|
|
||||||
|
// 显示初始进度
|
||||||
|
const initialProgress = controller.getTestProgress();
|
||||||
|
console.log('\n初始' + formatProgress(initialProgress));
|
||||||
|
|
||||||
|
if (args.includes('--collect-only')) {
|
||||||
|
console.log('仅收集菜单数据,退出执行');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
console.log('\n开始执行测试...\n');
|
||||||
|
let retryCount = 0;
|
||||||
|
const maxRetries = 3;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const batch = controller.getNextBatch();
|
||||||
|
if (!batch || batch.length === 0) {
|
||||||
|
const finalProgress = controller.getTestProgress();
|
||||||
|
|
||||||
|
// 检查是否所有菜单都测试完成
|
||||||
|
if (finalProgress.completed < finalProgress.total && retryCount < maxRetries) {
|
||||||
|
console.log(`\n还有未完成的测试,尝试重试 (${retryCount + 1}/${maxRetries})...`);
|
||||||
|
retryCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n所有测试完成!');
|
||||||
|
console.log('最终' + formatProgress(finalProgress));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示当前进度
|
||||||
|
const currentProgress = controller.getTestProgress();
|
||||||
|
console.log('\n当前' + formatProgress(currentProgress));
|
||||||
|
|
||||||
|
// 执行当前批次
|
||||||
|
await controller.runBatchTest(batch);
|
||||||
|
|
||||||
|
// 批次间暂停
|
||||||
|
console.log(`\n等待 ${controller.batchInterval/1000} 秒后继续下一批次...\n`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, controller.batchInterval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行主函数
|
||||||
|
main().catch(error => {
|
||||||
|
console.error('执行过程中出现错误:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
77
services/MenuDataService.js
Normal file
77
services/MenuDataService.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
class MenuDataService {
|
||||||
|
constructor() {
|
||||||
|
this.dataDir = path.join(process.cwd(), 'test-data');
|
||||||
|
this.menuDataPath = path.join(this.dataDir, 'menu-data.json');
|
||||||
|
this.progressPath = path.join(this.dataDir, 'test-progress.json');
|
||||||
|
|
||||||
|
// 确保数据目录存在
|
||||||
|
if (!fs.existsSync(this.dataDir)) {
|
||||||
|
fs.mkdirSync(this.dataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存菜单数据
|
||||||
|
* @param {Array} menuItems - 从页面获取的原始菜单项
|
||||||
|
* @returns {Array} - 处理后的菜单数据
|
||||||
|
*/
|
||||||
|
saveMenuData(menuItems) {
|
||||||
|
const menuData = menuItems.map((menu, index) => ({
|
||||||
|
id: index + 1,
|
||||||
|
text: menu.text,
|
||||||
|
path: menu.path || menu.text,
|
||||||
|
hasThirdMenu: menu.hasThirdMenu,
|
||||||
|
parentMenu: menu.parentMenu
|
||||||
|
}));
|
||||||
|
fs.writeFileSync(this.menuDataPath, JSON.stringify(menuData, null, 2));
|
||||||
|
return menuData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取菜单数据
|
||||||
|
* @returns {Array|null} - 菜单数据数组,如果文件不存在则返回null
|
||||||
|
*/
|
||||||
|
getMenuData() {
|
||||||
|
if (!fs.existsSync(this.menuDataPath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return JSON.parse(fs.readFileSync(this.menuDataPath, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存测试进度
|
||||||
|
* @param {Array} completedMenus - 已完成测试的菜单ID数组
|
||||||
|
*/
|
||||||
|
saveProgress(completedMenus) {
|
||||||
|
fs.writeFileSync(this.progressPath, JSON.stringify(completedMenus, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取测试进度
|
||||||
|
* @returns {Array} - 已完成测试的菜单ID数组
|
||||||
|
*/
|
||||||
|
getProgress() {
|
||||||
|
if (!fs.existsSync(this.progressPath)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return JSON.parse(fs.readFileSync(this.progressPath, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理所有数据文件
|
||||||
|
*/
|
||||||
|
clearAll() {
|
||||||
|
if (fs.existsSync(this.menuDataPath)) {
|
||||||
|
fs.unlinkSync(this.menuDataPath);
|
||||||
|
}
|
||||||
|
if (fs.existsSync(this.progressPath)) {
|
||||||
|
fs.unlinkSync(this.progressPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出单例实例
|
||||||
|
module.exports = new MenuDataService();
|
||||||
254
test-data/menu-data.json
Normal file
254
test-data/menu-data.json
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"text": "主数据",
|
||||||
|
"path": "主数据",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"text": "业务数据",
|
||||||
|
"path": "业务数据",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"text": "电池基础数据",
|
||||||
|
"path": "电池基础数据",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"text": "主需求计划",
|
||||||
|
"path": "主需求计划",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"text": "供需粗匹配",
|
||||||
|
"path": "供需粗匹配",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"text": "产线规划",
|
||||||
|
"path": "产线规划",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"text": "电池投放计划",
|
||||||
|
"path": "电池投放计划",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"text": "主生产计划",
|
||||||
|
"path": "主生产计划",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"text": "基地生产计划",
|
||||||
|
"path": "基地生产计划",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"text": "ATP生成和发布",
|
||||||
|
"path": "ATP生成和发布",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"text": "ATP分配调整",
|
||||||
|
"path": "ATP分配调整",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"text": "ATP查询",
|
||||||
|
"path": "ATP查询",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"text": "产量预测",
|
||||||
|
"path": "产量预测",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"text": "入库预测",
|
||||||
|
"path": "入库预测",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"text": "硅片投入需求",
|
||||||
|
"path": "硅片投入需求",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"text": "硅片到货需求",
|
||||||
|
"path": "硅片到货需求",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"text": "硅片需求预测",
|
||||||
|
"path": "硅片需求预测",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"text": "硅片缺口审视",
|
||||||
|
"path": "硅片缺口审视",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"text": "电池需求数据",
|
||||||
|
"path": "电池需求数据",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"text": "基地产能利用率",
|
||||||
|
"path": "基地产能利用率",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"text": "电池生产计划",
|
||||||
|
"path": "电池生产计划",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"text": "入库节奏基础参数",
|
||||||
|
"path": "入库节奏基础参数",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"text": "入库节奏",
|
||||||
|
"path": "入库节奏",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 24,
|
||||||
|
"text": "电池基地生产计划",
|
||||||
|
"path": "电池基地生产计划",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 25,
|
||||||
|
"text": "电池入库比例",
|
||||||
|
"path": "电池入库比例",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"text": "电池入库计划",
|
||||||
|
"path": "电池入库计划",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"text": "电池要货计划",
|
||||||
|
"path": "电池要货计划",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"text": "电池发货计划",
|
||||||
|
"path": "电池发货计划",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 29,
|
||||||
|
"text": "jiahx测试",
|
||||||
|
"path": "jiahx测试",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 30,
|
||||||
|
"text": "业务参数",
|
||||||
|
"path": "业务参数",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 31,
|
||||||
|
"text": "计划对象",
|
||||||
|
"path": "计划对象",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 32,
|
||||||
|
"text": "排产优先级",
|
||||||
|
"path": "排产优先级",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 33,
|
||||||
|
"text": "逃生通道",
|
||||||
|
"path": "逃生通道",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 34,
|
||||||
|
"text": "主需求计划",
|
||||||
|
"path": "主需求计划",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 35,
|
||||||
|
"text": "电池投放",
|
||||||
|
"path": "电池投放",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 36,
|
||||||
|
"text": "供需粗匹配",
|
||||||
|
"path": "供需粗匹配",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 37,
|
||||||
|
"text": "ATP",
|
||||||
|
"path": "ATP",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 38,
|
||||||
|
"text": "电池要发货",
|
||||||
|
"path": "电池要发货",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 39,
|
||||||
|
"text": "主计划/基地计划",
|
||||||
|
"path": "主计划/基地计划",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 40,
|
||||||
|
"text": "基地计划",
|
||||||
|
"path": "基地计划",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 41,
|
||||||
|
"text": "产品系列(内部手工维护)",
|
||||||
|
"path": "产品系列(内部手工维护)",
|
||||||
|
"hasThirdMenu": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 42,
|
||||||
|
"text": "管理视图",
|
||||||
|
"path": "管理视图",
|
||||||
|
"hasThirdMenu": false
|
||||||
|
}
|
||||||
|
]
|
||||||
1
test-data/test-progress.json
Normal file
1
test-data/test-progress.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
Loading…
Reference in New Issue
Block a user