活动介绍

// 解决方案2:使用自定义 toString console.log("\n自定义 toString 输出:"); console.log(circlePoints.join(', '));

时间: 2025-07-21 10:05:33 浏览: 14
<think>我们正在解决JavaScript中对象输出为[object Object]的问题。根据引用[1][3],这是因为当对象被转换为字符串时,默认调用Object.prototype.toString()方法,该方法返回"[object Object]"。要自定义输出,我们可以为对象或原型添加自定义的toString方法。 解决方案: 1. 为对象实例添加toString方法(每个实例单独添加) 2. 在构造函数中定义toString方法(所有实例共享) 3. 修改原型对象的toString方法(影响该构造函数创建的所有实例) 根据引用[3]中的示例,我们可以通过修改原型上的toString方法来实现。 步骤: 1. 定义构造函数(如Person) 2. 在原型上定义toString方法,返回自定义字符串 3. 创建实例并输出,此时会调用自定义的toString方法 注意:直接打印对象(如console.log(per))在控制台可能会显示对象结构,但当我们将其转换为字符串(如字符串拼接)时,就会调用toString方法。而使用alleet或document.write等输出时,也会调用toString。 示例代码(基于引用[3]): ```javascript function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; } // 修改原型上的toString方法 Person.prototype.toString = function() { return `Person[name=${this.name}, age=${this.age}, gender=${this.gender}]`; }; // 创建实例 var per = new Person("孙悟空", 18, "男"); var per2 = new Person("猪八戒", 28, "男"); // 测试输出 console.log(per.toString()); // 输出: "Person[name=孙悟空, age=18, gender=男]" console.log(per2.toString()); // 输出: "Person[name=猪八戒, age=28, gender=男]" // 当我们进行字符串拼接时,也会自动调用toString console.log("人物1: " + per); // 输出: "人物1: Person[name=孙悟空, age=18, gender=男]" ``` 另外,如果希望直接打印对象(console.log(per))时在控制台显示自定义字符串,可以重写toString方法,但注意console.log在输出对象时,会显示对象的内部结构,而不是调用toString。因此,我们需要确保在需要字符串的上下文中使用对象(如字符串拼接、alleet等)。如果希望在控制台输出自定义字符串,可以使用console.log(per.toString())或console.log(String(per))。 另一种情况:对于数组中的对象,当直接打印数组时,数组的toString会调用每个元素的toString,然后以逗号拼接。因此,如果数组元素是自定义对象且重写了toString,那么打印数组时也会显示自定义的字符串。 示例: ```javascript var people = [per, per2]; console.log(people.toString()); // 输出: "Person[name=孙悟空, age=18, gender=男],Person[name=猪八戒, age=28, gender=男]" ``` 但是,直接使用console.log(people)可能仍然显示为两个对象的结构(在控制台中可展开),因为console.log对于数组的处理是显示数组内容(每个元素是对象,则显示对象结构)。如果我们希望以字符串形式输出整个数组,可以: ```javascript console.log(String(people)); // 调用数组的toString,然后每个元素调用自己的toString ``` 总结:自定义toString方法适用于需要将对象转换为字符串的场景,如字符串拼接、alleet、文本输出等。对于控制台直接打印对象,其显示方式由浏览器控制台实现决定,通常显示为可交互的对象结构,此时自定义toString不会影响这种显示。 因此,解决用户问题:要避免输出[object Object],可以为对象定义自定义的toString方法,然后在需要字符串表示的地方使用它。 完整代码示例: ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>自定义toString解决[object Object]问题</title> <script> // 定义一个坐标点对象 function Point(x, y) { this.x = x; this.y = y; } // 在原型上添加自定义toString方法 Point.prototype.toString = function() { return `(${this.x.toFixed(2)}, ${this.y.toFixed(2)})`; }; // 创建点 var p1 = new Point(3.14159, 2.71828); var p2 = new Point(1.414, 1.732); // 测试 console.log(p1.toString()); // 输出: (3.14, 2.72) 注意:toFixed(2)保留两位小数 console.log("点1: " + p1); // 输出: 点1: (3.14, 2.72) // 数组中的点 var points = [p1, p2]; console.log(points.toString()); // 输出: (3.14, 2.72),(1.41, 1.73) </script> </head> <body> </body> </html> ``` 注意:在自定义toString方法中,我们使用了模板字符串和toFixed(2)来格式化坐标值。 如果不想修改原型,也可以为单个对象添加toString方法: ```javascript var p3 = { x: 5, y: 10, toString: function() { return `(${this.x}, ${this.y})`; } }; console.log(p3.toString()); // (5, 10) console.log("点3: " + p3); // 点3: (5, 10) ``` 结论:通过自定义toString方法,可以控制对象在需要转换为字符串时的输出,避免出现[object Object]。</think># JavaScript 自定义 toString 方法解决对象输出为 [object Object] 的问题 在 JavaScript 中,当直接输出对象时,经常会遇到显示为 `[object Object]` 的情况。这是因为 JavaScript 默认调用对象的 `toString()` 方法,而该方法默认返回的是对象的类型标识而不是内容。 ## 问题分析 根据引用[1]和引用[4],当 JavaScript 尝试将对象转换为字符串时,会调用 `Object.prototype.toString()` 方法,该方法默认返回 `[object Object]`: ```javascript const point = { x: 100, y: 200 }; console.log(point.toString()); // 输出: [object Object] console.log("坐标: " + point); // 输出: 坐标: [object Object] ``` ## 解决方案:自定义 toString 方法 ### 方法1:为单个对象添加 toString 方法 ```javascript // 创建坐标对象 const point = { x: 100, y: 200, // 自定义 toString 方法 toString: function() { return `(${this.x}, ${this.y})`; } }; console.log(point.toString()); // 输出: (100, 200) console.log("坐标: " + point); // 输出: 坐标: (100, 200) ``` ### 方法2:为构造函数添加原型方法(推荐) 根据引用[3],我们可以通过修改原型链来为所有实例添加自定义的 `toString` 方法: ```javascript // 创建 Point 构造函数 function Point(x, y) { this.x = x; this.y = y; } // 在原型上添加自定义 toString 方法 Point.prototype.toString = function() { return `(${this.x.toFixed(2)}, ${this.y.toFixed(2)})`; }; // 创建实例 const p1 = new Point(100.123, 200.456); const p2 = new Point(300.789, 400.012); console.log(p1.toString()); // 输出: (100.12, 200.46) console.log("点1: " + p1); // 输出: 点1: (100.12, 200.46) console.log("点2: " + p2); // 输出: 点2: (300.79, 400.01) ``` ### 方法3:为特定对象类型添加自定义 toString ```javascript // 创建坐标对象 const point = { x: 150, y: 250, // 自定义 toString 方法 toString: function() { return `坐标: x=${this.x}, y=${this.y}`; } }; // 创建用户对象 const user = { name: "张三", age: 30, // 自定义 toString 方法 toString: function() { return `用户: ${this.name}, 年龄: ${this.age}`; } }; console.log("位置: " + point); // 输出: 位置: 坐标: x=150, y=250 console.log("信息: " + user); // 输出: 信息: 用户: 张三, 年龄: 30 ``` ## 完整示例:圆形坐标生成器 ```html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>自定义 toString 方法解决 [object Object] 问题</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f5f7ff; color: #333; } .container { background: white; border-radius: 10px; padding: 25px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); } h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; } .controls { display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 20px; background: #eef2ff; padding: 15px; border-radius: 8px; } .control-group { flex: 1; min-width: 200px; } label { display: block; margin-bottom: 5px; font-weight: 600; color: #444; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 16px; } button { background: #4a6cf7; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 16px; transition: background 0.3s; margin-top: 10px; } button:hover { background: #3a56d4; } .output { margin-top: 25px; padding: 20px; background: #f8f9ff; border-radius: 8px; border-left: 4px solid #4a6cf7; } pre { background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto; max-height: 300px; overflow-y: auto; } .success { color: #27ae60; font-weight: bold; } .error { color: #e74c3c; font-weight: bold; } </style> </head> <body> <div class="container"> <h1>自定义 toString 方法解决 [object Object] 问题</h1> <div class="controls"> <div class="control-group"> <label for="centerX">圆心 X 坐标:</label> <input type="number" id="centerX" value="200"> </div> <div class="control-group"> <label for="centerY">圆心 Y 坐标:</label> <input type="number" id="centerY" value="200"> </div> <div class="control-group"> <label for="radius">半径:</label> <input type="number" id="radius" value="100"> </div> <div class="control-group"> <label for="points">点数:</label> <input type="number" id="points" value="8" min="3"> </div> </div> <button onclick="generatePoints()">生成坐标点</button> <div class="output"> <h2>输出结果:</h2> <div id="result"></div> <pre id="jsonOutput"></pre> </div> </div> <script> // Point 构造函数 function Point(x, y) { this.x = x; this.y = y; } // 在原型上添加自定义 toString 方法 Point.prototype.toString = function() { return `(${this.x.toFixed(2)}, ${this.y.toFixed(2)})`; }; // 生成圆形坐标点的函数 function generateCirclePoints(centerX, centerY, radius, points) { const coordinates = []; const angleStep = (2 * Math.PI) / points; for (let i = 0; i < points; i++) { const angle = i * angleStep; const x = centerX + radius * Math.cos(angle); const y = centerY + radius * Math.sin(angle); coordinates.push(new Point(x, y)); } return coordinates; } // 生成坐标点并显示结果 function generatePoints() { const centerX = parseFloat(document.getElementById('centerX').value); const centerY = parseFloat(document.getElementById('centerY').value); const radius = parseFloat(document.getElementById('radius').value); const points = parseInt(document.getElementById('points').value); if (isNaN(centerX) || isNaN(centerY) || isNaN(radius) || isNaN(points) || points < 3) { document.getElementById('result').innerHTML = '<p class="error">请输入有效的数值!点数至少为3。</p>'; return; } const circlePoints = generateCirclePoints(centerX, centerY, radius, points); // 显示自定义 toString 的输出 const resultDiv = document.getElementById('result'); resultDiv.innerHTML = ` <p class="success">成功生成 ${points} 个坐标点:</p> <p>${circlePoints.join(', ')}</p> <p>字符串拼接输出: "坐标: " + circlePoints[0] → <strong>${"坐标: " + circlePoints[0]}</strong></p> `; // 显示 JSON 序列化输出 const jsonOutput = document.getElementById('jsonOutput'); jsonOutput.textContent = JSON.stringify(circlePoints, null, 2); } // 页面加载时生成初始坐标 window.onload = generatePoints; </script> </body> </html> ``` ## 关键点说明 1. **问题根源**:JavaScript 默认调用 `Object.prototype.toString()` 方法,返回 `[object Object]`[^1][^4] 2. **解决方案**:为对象或原型添加自定义的 `toString()` 方法 - 单个对象:直接定义 `toString` 方法 - 构造函数:在原型上定义 `toString` 方法(推荐)[^3] - 特定类型:为不同对象类型定义不同的输出格式 3. **自动调用场景**:当对象在以下上下文中使用时,会自动调用 `toString()` 方法: - 字符串拼接(`"坐标: " + point`) - 使用 `alleet()` 或 `document.write()` 输出 - 作为数组元素时调用 `array.join()` 或 `array.toString()` 4. **注意事项**: - 自定义 `toString` 方法不应修改原始对象 - 避免在 `toString` 方法中进行复杂计算 - 使用 `toFixed()` 等方法格式化数字输出[^3] 通过自定义 `toString()` 方法,我们可以完全控制对象在需要字符串表示时的输出格式,彻底解决 `[object Object]` 的问题[^1][^3]。
阅读全文

相关推荐

const webpackBaseConfig = require("./webpack.config.dev"); const { resolveCustomConfig } = require("./resolveCustomConfig"); const { merge } = require("webpack-merge"); const webpack = require("webpack"); const webpackDevServer = require("webpack-dev-server"); const ProgressWebpackPlugin = require("progress-webpack-plugin"); const chalk = require("chalk"); const portfinder = require("portfinder"); const logUpdate = require("log-update"); const DeadcodePlugin = require("webpack-deadcode-plugin"); let devConfig = merge(resolveCustomConfig(webpackBaseConfig), { devtool: "inline-cheap-module-source-map", stats: ["development", "local"].includes(process.env.NODE_ENV) ? "errors-only" : "normal", plugins: [ process.env.UNUSED_FILES === "true" ? new DeadcodePlugin({ patterns: ["src/**/*.*"], exportJSON: ".unused-files", }) : false, new webpack.HotModuleReplacementPlugin(), new ProgressWebpackPlugin({ onProgress: async (output, percent) => { logUpdate(chalk.blue(output.join(" "))); if (output.includes("(after emit)")) { logUpdate.clear(); logUpdate(chalk.green("build completed .\n")); const localIpV4 = await webpackDevServer.internalIP("v4"); console.log("Local: \n"); console.log( chalk.green( Successfully started server on http://localhost:${process.PORT}\n, ), ); console.log("IPV4:\n"); console.log( chalk.green( Successfully started server on http://${localIpV4}:${process.PORT}, ), ); } }, }), ], infrastructureLogging: { appendOnly: true, level: "none", }, }); // webpack(resolveCustomConfig(devConfig), (err, stat) => { // console.log(err) // if (stat.hasErrors()) { // console.log(stat.toString()) // } // }) let compiler = webpack(resolveCustomConfig(devConfig)); // console.log(complier) const devBaseConfig = { host: "0.0.0.0", port: 8100, compress: true, hot: true, historyApiFallback: true, client: { logging: "none", overlay: false, progress: true, }, }; const runServer = () => { const coldStartBegin = Date.now(); // 冷启动开始时间 console.log(chalk.red("冷启动开始时间:" + coldStartBegin)); console.log(chalk.green("Starting server...")); portfinder.setBasePort(devBaseConfig.port); portfinder.getPort(async (err, port) => { if (err) { console.log(err); process.exit(); } process.PORT = port; devBaseConfig.port = port; if (devConfig.devServer && devConfig.devServer.proxy) { devBaseConfig.proxy = devConfig.devServer.proxy; } console.log(devBaseConfig); const server = new webpackDevServer(devBaseConfig, compiler); await server.start(); const duration = (Date.now() - coldStartBegin) / 1000; console.log(chalk.red(开发服务器启动完成,耗时 ${duration.toFixed(2)}秒)); }); }; runServer(); 这段service.js的代码,没输出console.log(chalk.red(开发服务器启动完成,耗时 ${duration.toFixed(2)}秒)); 看一下原因

<template> <view class="content"> <button @click="selectFile">打开安卓文件管理器</button> <view class="single-file"> <text>单选文件:</text> <text>{{ filePath || '未选择' }}</text> <text v-if="fileSize">大小:{{ fileSize }} 字节</text> <text v-if="hexData">Hex预览(前16字节):{{ hexData }}</text> </view> <view class="multi-files"> <text>多选文件:</text> <view v-for="(item, index) in fileList" :key="index" class="file-item"> <text>{{ item.path || '未知路径' }}</text> <text v-if="item.size">大小:{{ item.size }} 字节</text> <text v-if="item.hexData">Hex预览:{{ item.hexData }}</text> </view> </view> </view> </template> <script> export default { data() { return { filePath: '', // 单选文件路径 fileSize: 0, // 单选文件大小 hexData: '', // 单选Hex预览 fileList: [] // 多选文件列表(响应式数组) }; }, methods: { // 核心:文件选择入口 selectFile() { const Intent = plus.android.importClass('android.content.Intent'); const intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); intent.addCategory(Intent.CATEGORY_OPENABLE); plus.android.startActivityForResult(intent, 1024, function(e) { if (e.resultCode == -1) { // RESULT_OK processFile(e.intent.getData()); } }); function processFile(uri) { plus.io.resolveLocalFileSystemURL(uri.toString(), function(entry) { entry.file(function(file) { const reader = new plus.io.FileReader(); reader.onloadend = function(evt) { const arrayBuffer = evt.target.result; const hexData = bufferToHex(arrayBuffer); console.log("文件大小:" + file.size + "字节"); console.log("HEX内容:" + hexData.substring(0, 64) + "..."); }; reader.readAsArrayBuffer(file); }); }); } function bufferToHex(buffer) { return Array.from(new Uint8Array(buffer)) .map(b => b.toString(16).padStart(2, '0')) .join(' '); } }, } }; </script> <style> .content { padding: 20rpx; } .single-file, .multi-files { margin-top: 30rpx; } .file-item { margin: 15rpx 0; padding: 10rpx; background: #f5f5f5; border-radius: 8rpx; } </style> 报错[Vue warn]: Error in v-on handler: "TypeError: plus.android.startActivityForResult is not a function" (found at pages/index/index.vue:1) 11:02:19.281 TypeError: plus.android.startActivityForResult is not a function

// index.js - 优化版首页逻辑 const app = getApp(); Page({ data: { // 统计数据 totalOrders: 0, totalProducts: 0, totalMaterials: 0, totalStock: 0, // 搜索条件 orderSearch: '', productSearch: '', materialSearch: '', woodSearch: '', thicknessSearch: '', minStock: '', maxStock: '', // 结果数据 loading: true, results: [], filteredResults: [], resultCount: 0, lastUpdate: '--:--:--', // 排序 sortField: 'orderDate', sortDirection: 'desc', // 分页 currentPage: 1, pageSize: 10, totalPages: 1, currentPageData: [], // 板材详情 showMaterialDetail: false, materialDetail: null }, onLoad: function() { // console.log('index页面加载'); if (app.globalData && app.globalData.dataManager) { this.dataManager = app.globalData.dataManager; // 注册数据更新回调 this.dataManager.registerCallback('all', () => { console.log('收到数据更新回调'); this.updatePageData(); }); // 初始化页面数据 this.updatePageData(); } }, onShow: function() { // console.log('首页显示'); // 检查是否需要登录 if (app.globalData && app.globalData.needLogin) { //console.log('检测到需要登录'); setTimeout(() => { app.checkWechatLogin(); }, 500); } }, // 更新页面数据 updatePageData: function() { if (!this.dataManager || !this.dataManager.data) return; this.setData({ loading: true }); // 更新统计数据 this.updateStats(); // 更新表格数据 this.updateTable(); // 更新最后更新时间 const now = new Date(); const lastUpdate = ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}; this.setData({ lastUpdate }); }, // 更新统计数据 updateStats: function() { const data = this.dataManager.data; const totalOrders = data.dingdans?.length || 0; const totalProducts = data.chanpins?.length || 0; const totalMaterials = data.bancais?.length || 0; const totalStock = data.kucuns?.reduce((sum, kucun) => sum + kucun.shuliang, 0) || 0; this.setData({ totalOrders, totalProducts, totalMaterials, totalStock }); }, // 核心优化:直接使用dingdan_bancai作为数据源 updateTable: function() { //console.log('开始更新表格数据 - 优化版'); const data = this.dataManager.data; let results = []; // 直接处理dingdan_bancais作为唯一数据源 if (data.dingdan_bancais) { data.dingdan_bancais.forEach(db => { if (!db.dingdan || !db.bancai) return; // 获取关联信息 const dingdan = db.dingdan; const bancai = db.bancai; const chanpin = db.chanpin || null; const zujian = db.zujian || null; console.log(dingdan.dingdan_chanpins) const dingdan_chanpin= chanpin ? data.dingdan_chanpins.find(d=>dingdan?.id===d.dingdan.id&&d.chanpin.id===chanpin?.id):null; const chanpin_zujian=chanpin&&zujian?data.chanpin_zujians.find( c=>c.chanpin?.id===chanpin.id&&c.zujian?.id===zujian.id ):null; // 获取库存信息 const kucun = data.kucuns?.find(k => k.bancai?.id === bancai.id); const stockQuantity = kucun ? kucun.shuliang : 0; //console.log(bancai) // 构建结果项 results.push({ id: db-${db.id}, // 唯一ID orderNumber: dingdan.number, productInfo: chanpin ? chanpin.bianhao : '独立订购', productQuantity:dingdan_chanpin?dingdan_chanpin.shuliang:0, component: zujian ? zujian.name : '独立板材', material: this.getMaterialInfo(bancai), materialPerComponent: chanpin_zujian? chanpin_zujian.one_howmany:0, // 直接使用dingdan_bancai的数量 materialOrderQuantity: db.shuliang, stockQuantity: stockQuantity, raw: { thickness: bancai.houdu || 0, orderNumber: dingdan.number, productQuantity: db.shuliang, stockQuantity: stockQuantity }, bancaiId: bancai.id }); }); } // 处理结果数据(排序、分页等) this.processResults(results); }, // 获取板材信息(优化版) getMaterialInfo: function(bancai) { if (!bancai) return '未知板材'; // 获取材质名称,默认"未知材质" const caizhiName = bancai.caizhi?.name || '未知材质'; // 获取厚度信息,默认"未知厚度" const thickness = bancai.houdu ? ${bancai.houdu.toFixed(1)}mm : '未知厚度'; // 格式化单个木皮信息,带油标记 const formatMupi = mupi => { if (!mupi) return null; return mupi.you ? ${mupi.name}(油) : mupi.name; }; // 处理木皮列表,过滤空值 const mupiList = [ formatMupi(bancai.mupi1), formatMupi(bancai.mupi2) ].filter(Boolean); // 过滤掉null/undefined // 生成木皮信息部分 const mupiInfo = mupiList.length > 0 ? (${mupiList.join('/')}) : '(无木皮)'; // 组合最终格式:材质名(木皮1/木皮2) 厚度 return ${caizhiName}${mupiInfo} ${thickness}; }, // 处理结果数据(排序、筛选、分页) processResults: function(results) { // 应用排序 if (this.data.sortField) { results = this.sortResults(results, this.data.sortField, this.data.sortDirection); } // 应用筛选 this.applyFilters(results); }, // 应用筛选条件(优化性能) applyFilters: function(results) { const { orderSearch, productSearch, materialSearch, woodSearch, thicknessSearch, minStock, maxStock } = this.data; let filtered = [...results]; // 订单号筛选 if (orderSearch) { const search = orderSearch.toLowerCase(); filtered = filtered.filter(item => item.orderNumber.toLowerCase().includes(search) ); } // 产品筛选 if (productSearch) { const search = productSearch.toLowerCase(); filtered = filtered.filter(item => item.productInfo.toLowerCase().includes(search) ); } // 板材筛选 if (materialSearch) { const search = materialSearch.toLowerCase(); filtered = filtered.filter(item => item.material.toLowerCase().includes(search) ); } // 木皮筛选 if (woodSearch) { const search = woodSearch.toLowerCase(); filtered = filtered.filter(item => item.material.toLowerCase().includes(search) ); } // 厚度筛选 if (thicknessSearch) { const thickness = parseFloat(thicknessSearch); if (!isNaN(thickness)) { filtered = filtered.filter(item => Math.abs(item.raw.thickness - thickness) < 0.5 ); } } // 库存范围筛选 const min = minStock !== '' ? parseInt(minStock) : Number.MIN_SAFE_INTEGER; const max = maxStock !== '' ? parseInt(maxStock) : Number.MAX_SAFE_INTEGER; filtered = filtered.filter(item => item.stockQuantity >= min && item.stockQuantity <= max ); // 更新分页数据 this.setData({ results: filtered, filteredResults: filtered, resultCount: filtered.length, totalPages: Math.ceil(filtered.length / this.data.pageSize), currentPage: 1, loading: false }, () => this.updateCurrentPageData()); }, // 排序结果 sortResults: function(results, field, direction) { if (!field) return results; return [...results].sort((a, b) => { let valueA, valueB; // 根据排序字段获取值 switch (field) { case 'dingdan.number': valueA = a.orderNumber; valueB = b.orderNumber; break; case 'chanpin.bianhao': valueA = a.productInfo; valueB = b.productInfo; break; case 'dingdan_chanpin.shuliang': valueA = a.materialPerComponent; valueB = b.materialPerComponent; break; case 'zujian.name': valueA = a.component; valueB = b.component; break; case 'bancai.id': // 特殊处理:按厚度排序 return this.sortNumeric(a.raw.thickness, b.raw.thickness, direction); case 'chanpin_zujian.one_howmany': valueA = a.materialPerComponent; valueB = b.materialPerComponent; break; case 'orderUsage': valueA = a.materialOrderQuantity; valueB = b.materialOrderQuantity; break; case 'kucun.shuliang': valueA = a.stockQuantity; valueB = b.stockQuantity; break; default: valueA = a.orderNumber; valueB = b.orderNumber; } // 数值比较 if (['materialPerComponent', 'materialOrderQuantity', 'stockQuantity'].includes(field)) { return this.sortNumeric(valueA, valueB, direction); } // 字符串比较 return this.sortString(valueA, valueB, direction); }); }, // 数值排序辅助函数 sortNumeric: function(aVal, bVal, direction) { const aNum = parseFloat(aVal) || 0; const bNum = parseFloat(bVal) || 0; return direction === 'asc' ? aNum - bNum : bNum - aNum; }, // 字符串排序辅助函数 sortString: function(aVal, bVal, direction) { const aStr = String(aVal).toLowerCase(); const bStr = String(bVal).toLowerCase(); if (aStr < bStr) return direction === 'asc' ? -1 : 1; if (aStr > bStr) return direction === 'asc' ? 1 : -1; return 0; }, // 搜索输入处理函数 onOrderSearchInput: function(e) { this.setData({ orderSearch: e.detail.value }); this.applyFilters(this.data.results); }, onProductSearchInput: function(e) { this.setData({ productSearch: e.detail.value }); this.applyFilters(this.data.results); }, onMaterialSearchInput: function(e) { this.setData({ materialSearch: e.detail.value }); this.applyFilters(this.data.results); }, onWoodSearchInput: function(e) { this.setData({ woodSearch: e.detail.value }); this.applyFilters(this.data.results); }, onThicknessSearchInput: function(e) { this.setData({ thicknessSearch: e.detail.value }); this.applyFilters(this.data.results); }, onMinStockInput: function(e) { this.setData({ minStock: e.detail.value }); }, onMaxStockInput: function(e) { this.setData({ maxStock: e.detail.value }); }, // 库存范围搜索 onStockSearch: function() { this.applyFilters(this.data.results); }, // 重置筛选 resetFilters: function() { this.setData({ orderSearch: '', productSearch: '', materialSearch: '', woodSearch: '', thicknessSearch: '', minStock: '', maxStock: '' }); this.applyFilters(this.data.results); }, // 排序处理 sortBy: function(e) { const field = e.currentTarget.dataset.field; // 如果点击的是当前排序字段,则切换排序方向 if (field === this.data.sortField) { this.setData({ sortDirection: this.data.sortDirection === 'asc' ? 'desc' : 'asc' }); } else { // 否则,设置新的排序字段,默认升序 this.setData({ sortField: field, sortDirection: 'asc' }); } // 应用排序 const sorted = this.sortResults(this.data.filteredResults, this.data.sortField, this.data.sortDirection); this.setData({ filteredResults: sorted }, () => { // 更新当前页数据 this.updateCurrentPageData(); }); }, // 分页处理 prevPage: function() { if (this.data.currentPage > 1) { this.setData({ currentPage: this.data.currentPage - 1 }, () => { this.updateCurrentPageData(); }); } }, nextPage: function() { if (this.data.currentPage < this.data.totalPages) { this.setData({ currentPage: this.data.currentPage + 1 }, () => { this.updateCurrentPageData(); }); } }, // 更新当前页数据 updateCurrentPageData: function() { const startIndex = (this.data.currentPage - 1) * this.data.pageSize; const endIndex = startIndex + this.data.pageSize; const currentPageData = this.data.filteredResults.slice(startIndex, endIndex); this.setData({ currentPageData }); }, // 显示板材详情 showMaterialDetail: function(e) { const materialId = e.currentTarget.dataset.id; // 查找板材详情 const bancai = this.dataManager.data.bancais.find(b => b.id === materialId); if (!bancai) { wx.showToast({ title: '未找到板材信息', icon: 'none' }); return; } // 获取库存信息 const kucun = this.dataManager.data.kucuns?.find(k => k.bancai?.id === materialId) || null; // 设置详情数据 this.setData({ materialDetail: { id: bancai.id, material: bancai.caizhi?.name || '未知', thickness: bancai.houdu ? bancai.houdu.toFixed(1) + 'mm' : '未知', stock: kucun ? kucun.shuliang : 0, mupi1: this.formatMupiDetail(bancai.mupi1), mupi2: this.formatMupiDetail(bancai.mupi2), relatedOrders: this.getRelatedOrders(bancai) }, showMaterialDetail: true }); }, // 格式化木皮详情 formatMupiDetail: function(mupi) { if (!mupi) return '无'; return ${mupi.name}${mupi.you ? '(油)' : ''}; }, // 关闭板材详情 closeMaterialDetail: function() { this.setData({ showMaterialDetail: false }); }, // 获取板材相关订单 getRelatedOrders: function(bancai) { const data = this.dataManager.data; const orderMap = new Map(); // 使用Map进行订单去重 // 查找直接订单组件关联 const dingdan_bancais = data.dingdan_bancais || []; dingdan_bancais.forEach(db => { if (db.bancai?.id === bancai.id && db.dingdan) { orderMap.set(db.dingdan.id, db.dingdan); } }); // 转换为数组并排序(按下单日期降序) return Array.from(orderMap.values()).sort((a, b) => { const dateA = a.xiadan ? new Date(a.xiadan) : new Date(0); const dateB = b.xiadan ? new Date(b.xiadan) : new Date(0); return dateB - dateA; // 最近的订单在前 }); }, // 格式化日期 formatDate: function(dateString) { if (!dateString) return ''; const date = new Date(dateString); return ${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}; } });--------------class LazyLoader { constructor(dataManager) { this.dataManager = dataManager; this.cache = new Map(); this.entityTypeMap = { bancai: 'bancais', dingdan: 'dingdans', mupi: 'mupis', chanpin: 'chanpins', kucun: 'kucuns', dingdan_bancai: 'dingdan_bancais', chanpin_zujian: 'chanpin_zujians', zujian: 'zujians', caizhi: 'caizhis', dingdan_chanpin: 'dingdan_chanpins', user: 'users', jinhuo: 'jinhuos' }; this.proxies = new WeakMap(); this.creatingProxies = new WeakSet(); // 新增:追踪正在创建的代理 } createProxy(entity, entityType) { if (this.creatingProxies.has(entity)) { return entity; // 直接返回原始对象避免递归 } // 1. 优先检查缓存 if (this.proxies.has(entity)) { return this.proxies.get(entity); } const cacheKey = ${entityType}_${entity.id}; if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey); } // 标记为正在创建 this.creatingProxies.add(entity); const handler = { get: (target, prop, receiver) => { // 特殊属性处理 if (prop === 'id') return target.id; const value = Reflect.get(target, prop, receiver); // 基本类型直接返回 if (typeof value !== 'object' || value === null) { return value; } // 处理数组引用 if (Array.isArray(value)) { const refType = this.getReferenceType(prop); return refType ? this.loadReferences(value, refType) : value; } // 检查是否已经代理过这个对象 if (this.proxies.has(value)) { return this.proxies.get(value); } // 处理对象引用 const refType = this.getReferenceType(prop); if (refType) { //console.log(refType) return this.loadReference(value, refType); } return value; } }; const proxy = new Proxy(entity, handler); // 创建完成后移除标记 this.creatingProxies.delete(entity); // 3. 创建后立即缓存 this.proxies.set(entity, proxy); this.cache.set(cacheKey, proxy); return proxy; } getEntityTypeFromRef(prop) { const baseProp = prop.replace(/\d/g, ''); return baseProp in this.entityTypeMap ? this.entityTypeMap[baseProp] : ${baseProp}s; } getReferenceType(prop) { try { const baseProp = prop.replace(/\d/g, ''); const pluralProp = ${baseProp}s; if (this.dataManager._rawData[pluralProp]) return pluralProp; } catch (error) { //console.log(prop+" ---- "+error) return prop; } return null; } loadReference(ref, refType,s=1) { if (!ref?.id) { return ref;} const cacheKey = ${refType}_${ref.id}; // 4. 统一使用缓存机制 if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey); } const entities = this.dataManager._rawData[refType] || []; const entity = entities.find(e => e.id === ref.id); if (!entity) { console.warn(Entity not found: ${refType} with id ${ref.id}); return ref; } // 检查是否已经代理过这个对象 if (this.proxies.has(entity)) { return this.proxies.get(entity); } // 5. 使用createProxy确保代理一致性 const prosty= this.createProxy(entity, refType); // console.log(cacheKey+"---"+s+"--"+prosty.id) return prosty } loadReferences(refs, refType) { refs.map(ref => { if (this.proxies.has(ref)) { return this.proxies.get(ref); } return this.loadReference(ref, refType,4); // 添加return }); // console.log(${refs}--------${refType}) return refs; } clearCache() { this.cache.clear(); } } class MiniProgramDataManager { constructor(baseUrl = '') { this.baseUrl = baseUrl; this.debug = true; this.networkAvailable = false; this.isSyncing = false; this.lastSync = null; this.syncInterval = 5 * 60 * 1000; this.storageKey = 'miniProgramData'; this._rawData = this.createEmptyData(); this.lazyLoader = new LazyLoader(this); this.callbacks = { all: [], bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], chanpin_zujians: [], dingdan_bancais: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [] }; this.initNetwork(); this.loadDataFromStorage(); this.startAutoSync(); } createEmptyData() { return { bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], dingdan_bancais: [], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [], _lastModified: null, _lastSync: null }; } get data() { const handler = { get: (target, prop) => { if (prop.startsWith('_')) return target[prop]; if (Array.isArray(target[prop])) { return target[prop].map(item => this.lazyLoader.createProxy(item, prop.replace(/s$/, '')) ); } return target[prop]; }, set: (target, prop, value) => { target[prop] = value; return true; } }; return new Proxy(this._rawData, handler); } async initialize() { try { await this.syncData(); return true; } catch (error) { if (this._rawData._lastSync) return true; throw error; } } startAutoSync() { this.autoSyncTimer = setInterval(() => { !this.isSyncing && this.syncData(); }, this.syncInterval); } stopAutoSync() { clearInterval(this.autoSyncTimer); } async initNetwork() { try { const { networkType } = await wx.getNetworkType(); this.networkAvailable = networkType !== 'none'; } catch { this.networkAvailable = false; } } async syncData() { if (this.isSyncing) return; this.isSyncing = true; try { const since = this._rawData._lastSync; await this.fetchAll(since); this.lazyLoader.clearCache(); this.saveDataToStorage(); this.triggerCallbacks('refresh', 'all', this.data); } catch (error) { console.error('Sync failed:', error); this.triggerCallbacks('sync_error', 'all', { error }); if (!this._rawData._lastSync) throw error; } finally { this.isSyncing = false; } } async fetchAll(since) { try { const params = since ? { since } : {}; const resolvedData = this.baseUrl ? await this.request('/app/all', 'GET', params) : this.createEmptyData(); Object.keys(this._rawData).forEach(key => { if (key.startsWith('_') || !resolvedData[key]) return; if (since) { resolvedData[key].forEach(newItem => { const index = this._rawData[key].findIndex(item => item.id === newItem.id); index >= 0 ? this._rawData[key][index] = newItem : this._rawData[key].push(newItem); }); } else { this._rawData[key] = resolvedData[key]; } }); this._rawData._lastSync = new Date().toISOString(); this.saveDataToStorage(); return true; } catch (error) { console.error('Fetch error:', error); this.triggerCallbacks('fetch_error', 'all', { error }); throw error; } } async request(url, method, data, retryCount = 3) { return new Promise((resolve, reject) => { const fullUrl = ${this.baseUrl}${url}; const requestTask = () => { wx.request({ url: fullUrl, method, data, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data.data); } else { const err = new Error(res.data?.message || 'API error'); retryCount > 1 ? setTimeout(requestTask, 1000, retryCount - 1) : reject(err); } }, fail: (err) => { retryCount > 1 ? setTimeout(requestTask, 1000, retryCount - 1) : reject(new Error(Network error: ${err.errMsg})); } }); }; requestTask(); }); } registerCallback(entity, callback) { this.callbacks[entity]?.push(callback) || this.callbacks.all.push(callback); } unregisterCallback(entity, callback) { const arr = this.callbacks[entity] || this.callbacks.all; const index = arr.indexOf(callback); if (index !== -1) arr.splice(index, 1); } triggerCallbacks(operation, entity, data) { this.callbacks.all.forEach(cb => cb(operation, entity, data)); this.callbacks[entity]?.forEach(cb => cb(operation, data)); } async crudOperation(operation, entity, data) { try { const result = await this.request(/app/${operation}/${entity}, 'POST', data); this.updateLocalData(operation, entity, result || data); this.triggerCallbacks(operation, entity, result || data); return result; } catch (error) { this.triggerCallbacks(${operation}_error, entity, { data, error }); throw error; } } updateLocalData(operation, entity, data) { const key = ${entity}s; const collection = this._rawData[key] || []; switch (operation) { case 'add': collection.push(data); break; case 'update': const index = collection.findIndex(item => item.id === data.id); index >= 0 ? collection[index] = data : collection.push(data); break; case 'delete': const deleteIndex = collection.findIndex(item => item.id === data.id); if (deleteIndex >= 0) collection.splice(deleteIndex, 1); break; } this._rawData._lastModified = new Date().toISOString(); this.lazyLoader.clearCache(); this.saveDataToStorage(); } loadDataFromStorage() { try { const storedData = wx.getStorageSync(this.storageKey); if (storedData) this._rawData = storedData; } catch (error) { console.error('Storage load error:', error); } } saveDataToStorage() { try { wx.setStorageSync(this.storageKey, this._rawData); } catch (error) { console.error('Storage save error:', error); wx.showToast({ title: '数据保存失败', icon: 'none' }); } } async addEntity(entity, data) { return this.crudOperation('add', entity, data); } async updateEntity(entity, data) { return this.crudOperation('update', entity, data); } async deleteEntity(entity, id) { return this.crudOperation('delete', entity, { id }); } async transactionalOperation(endpoint, data) { try { await this.request(/app/Transactional/${endpoint}, 'POST', data); await this.syncData(); return true; } catch (error) { this.triggerCallbacks('transaction_error', endpoint, { data, error }); throw error; } } } module.exports = MiniProgramDataManager; console.log(dingdan.dingdan_chanpins)输出全是空

mqtt.disconnectAll(); mqtt.createClient({ uri: "mqtt://10.19.0.127:1883", clientId: "mqtt_client_001", topic: topic,//'dibang/100001/weight', user: 'XioMQTT', pass: 'xio123', }) .then(function(client) { let num = 0; client.on("closed", function() { console.log("mqtt.event.closed"); }); client.on("error", function(msg) { console.log("mqtt.event.error", msg); }); client.on("message", function(msg) { // console.log('msg.data',msg) let s = ''; JSON.parse(msg.data).map(item=>{ let temp = item.toString(16); s += temp.length<2?'0'+temp:temp; }) if (s.length > 12) { s = s.substring(0, 24); } if (s.substring(0, 2)=="02" && s.substring(2, 4)=="2b") { let lakh = (parseInt(s.substring(4, 6)) - 30) * 100000; let theThousand = (parseInt(s.substring(6, 8)) - 30) * 10000; let thousand = (parseInt(s.substring(8, 10)) - 30) * 1000; let hundred = (parseInt(s.substring(10, 12)) - 30) * 100; let ten = (parseInt(s.substring(12, 14)) - 30) * 10; let piece = parseInt(s.substring(14, 16)) - 30; let point = parseInt(s.substring(16, 18)) - 30; let all = lakh + theThousand + thousand + hundred + ten + piece; if (point > 0) { all = all / (10 * point); } let weight = all; num++; if(num%20==0){ console.log('重量:',weight) _this.setState({weight:(weight + ''),canUse:true}) } } }); client.on("connect", function() { console.log("connected"); client.subscribe(topic, 0);//"dibang/100001/weight" // client.publish("/data", "test", 0, false); }); client.connect(); }) .catch(function(err) { console.log(err); }); 有以上代码时就会报Value for title cannot be cast from ReadableNativeMap to String这个错误,请帮我找找问题出在哪里,怎么解决,我的项目是react native0.68版本的

好的,我明白你的意思了,我的插件也是通过点击来获取文件中diff,但是这样会导致diff页面跳转到对应点击文件。所有是否有办法拿到对应文件的diff而不进行跳转呢 下面是插件异步处理方法 // 温和的异步点击获取diff(进一步避免页面跳动) async function gentleAsyncClickToLoadDiff(file, maxWaitMs = 8000) { console.log([温和异步点击] 开始温和异步点击获取文件 ${file.fileName} 的diff); // 保存当前页面状态 const currentScrollPosition = window.scrollY; const currentActiveElement = document.activeElement; const currentSelection = window.getSelection().toString(); console.log([温和异步点击] 保存当前页面状态: scrollY=${currentScrollPosition}, activeElement=${currentActiveElement?.tagName}); // 检查是否有匹配的diff(文件名必须完全匹配) const exactMatch = fileDiffMap[file.fileName]; if (exactMatch) { console.log([温和异步点击] 文件 ${file.fileName} 已有完全匹配的diff缓存); return exactMatch; } // 检查是否有智能匹配的diff const availableFiles = Object.keys(fileDiffMap); const matchingDiffFile = availableFiles.find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (matchingDiffFile) { console.log([温和异步点击] 文件 ${file.fileName} 找到智能匹配的diff: ${matchingDiffFile}); return fileDiffMap[matchingDiffFile]; } // 清空之前的diff缓存,确保获取到当前文件的diff const previousDiffMap = { ...fileDiffMap }; fileDiffMap = {}; console.log([温和异步点击] 清空diff缓存,准备加载 ${file.fileName} 的diff); try { // 使用更温和的异步点击方式,进一步避免页面跳动 console.log([温和异步点击] 使用更温和的异步点击方式: ${file.fileName}); // 方法1: 使用更长的延迟和更温和的方式 const titleElement = file.element.querySelector('.teamix-title'); if (titleElement) { console.log([温和异步点击] 找到文件标题元素:, titleElement.tagName, titleElement.className); // 使用更长的延迟和更温和的方式 return new Promise((resolve) => { const gentleCallback = () => { // 使用更温和的点击方式 try { // 方法1: 使用dispatchEvent而不是click() const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window, detail: 1, screenX: 0, screenY: 0, clientX: 0, clientY: 0 }); titleElement.dispatchEvent(clickEvent); console.log([温和异步点击] 温和点击完成: ${file.fileName}); } catch (error) { console.warn([温和异步点击] 温和点击失败,尝试直接点击:, error); titleElement.click(); } // 等待更长时间,确保diff加载完成 setTimeout(() => { // 检查是否成功加载到匹配的diff const newExactMatch = fileDiffMap[file.fileName]; if (newExactMatch) { console.log([温和异步点击] 成功: 已获取到 ${file.fileName} 的diff); resolve(newExactMatch); return; } // 检查智能匹配 const newMatchingDiffFile = Object.keys(fileDiffMap).find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (newMatchingDiffFile) { console.log([温和异步点击] 成功: 找到智能匹配的diff: ${newMatchingDiffFile}); resolve(fileDiffMap[newMatchingDiffFile]); return; } // 如果没有找到匹配的diff,返回null console.log([温和异步点击] 未找到匹配的diff: ${file.fileName}); resolve(null); }, 3000); // 增加等待时间到3秒,确保diff加载完成 }; // 使用更长的延迟,确保页面完全稳定 setTimeout(() => { if (window.requestIdleCallback) { window.requestIdleCallback(gentleCallback, { timeout: 5000 }); } else { setTimeout(gentleCallback, 500); } }, 1000); // 额外等待1秒,确保页面稳定 }); } // 方法2: 如果找不到标题元素,尝试其他可点击元素 console.log([温和异步点击] 方法2: 尝试其他可点击元素 ${file.fileName}); const clickableElement = file.element.querySelector('a, button, [role="button"], span'); if (clickableElement && !clickableElement.classList.contains('next-tree-node-indent-unit')) { console.log([温和异步点击] 找到可点击元素:, clickableElement.tagName, clickableElement.className); return new Promise((resolve) => { const gentleCallback = () => { try { const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window, detail: 1, screenX: 0, screenY: 0, clientX: 0, clientY: 0 }); clickableElement.dispatchEvent(clickEvent); console.log([温和异步点击] 温和点击可点击元素完成: ${file.fileName}); } catch (error) { console.warn([温和异步点击] 温和点击失败,尝试直接点击:, error); clickableElement.click(); } setTimeout(() => { // 检查结果 const newExactMatch = fileDiffMap[file.fileName]; if (newExactMatch) { console.log([温和异步点击] 方法2成功: 已获取到 ${file.fileName} 的diff); resolve(newExactMatch); return; } const newMatchingDiffFile = Object.keys(fileDiffMap).find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (newMatchingDiffFile) { console.log([温和异步点击] 方法2成功: 找到智能匹配的diff: ${newMatchingDiffFile}); resolve(fileDiffMap[newMatchingDiffFile]); return; } console.log([温和异步点击] 方法2失败: 未找到匹配的diff: ${file.fileName}); resolve(null); }, 3000); }; setTimeout(() => { if (window.requestIdleCallback) { window.requestIdleCallback(gentleCallback, { timeout: 5000 }); } else { setTimeout(gentleCallback, 500); } }, 1000); }); } // 方法3: 如果都找不到可点击元素,尝试直接点击文件元素 console.log([温和异步点击] 方法3: 尝试直接点击文件元素 ${file.fileName}); if (file.element && typeof file.element.click === 'function') { return new Promise((resolve) => { const gentleCallback = () => { try { const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window, detail: 1, screenX: 0, screenY: 0, clientX: 0, clientY: 0 }); file.element.dispatchEvent(clickEvent); console.log([温和异步点击] 温和点击文件元素完成: ${file.fileName}); } catch (error) { console.warn([温和异步点击] 温和点击失败,尝试直接点击:, error); file.element.click(); } setTimeout(() => { // 检查结果 const newExactMatch = fileDiffMap[file.fileName]; if (newExactMatch) { console.log([温和异步点击] 方法3成功: 已获取到 ${file.fileName} 的diff); resolve(newExactMatch); return; } const newMatchingDiffFile = Object.keys(fileDiffMap).find(availableFile => { if (availableFile === '__single__') return false; const pageFileName = file.fileName; const apiFileName = availableFile; const getFileName = (path) => path.split('/').pop().split('\\').pop(); const pageFileBase = getFileName(pageFileName); const apiFileBase = getFileName(apiFileName); const matches = [ pageFileName === apiFileName, pageFileBase === apiFileBase, apiFileName.includes(pageFileBase), pageFileName.includes(apiFileBase), pageFileName.includes(apiFileName) || apiFileName.includes(pageFileName) ]; return matches.some(match => match); }); if (newMatchingDiffFile) { console.log([温和异步点击] 方法3成功: 找到智能匹配的diff: ${newMatchingDiffFile}); resolve(fileDiffMap[newMatchingDiffFile]); return; } console.log([温和异步点击] 方法3失败: 未找到匹配的diff: ${file.fileName}); resolve(null); }, 3000); }; setTimeout(() => { if (window.requestIdleCallback) { window.requestIdleCallback(gentleCallback, { timeout: 5000 }); } else { setTimeout(gentleCallback, 500); } }, 1000); }); } console.log([温和异步点击] 所有方法都失败: ${file.fileName}); return null; } catch (error) { console.error([温和异步点击] 点击文件 ${file.fileName} 时发生错误:, error); // 恢复之前的diff缓存 fileDiffMap = { ...previousDiffMap }; return null; } }

<template> 返回 业务架构树 <el-input class="left-search" v-model="sysName" placeholder="请输入搜索关键字" size="mini" @input="debouncedFetchData" @keyup.enter.native="getBusiTree('', '', '')"> </el-input> <el-tree v-if="!lazy" ref="tree" class="tree" :props="lazy ? defaultProps : defaultProp" :highlight-current="true" node-key="id" :expand-on-click-node="false" :indent="10" :lazy="lazy" :load="loadNode" key="1" :data="treeData" @node-click="handleNodeClick" default-expand-all> {{ getLevelPrefix(node.level) }} {{ node.label }} </el-tree> <el-tree v-else ref="tree" class="tree" :props="defaultProps" :highlight-current="true" node-key="id" :expand-on-click-node="false" :indent="10" :lazy="lazy" :load="loadNode" key="2" @node-click="handleNodeClick"> {{ getLevelPrefix(data.level) }} {{ node.label }} </el-tree> <detailsContent ref="detailsContent" :currentNode="currentNode"></detailsContent> </template> <script> import { busiFirstThreeLevel, busiTree } from '@/api/newTwinBusinessArchitecture'; import detailsContent from './detailsContent.vue' export default { data() { return { sysName: '', filterText: '', currentPath: ['家庭业务体系', '宽带电视', '宽带电视解装'], treeData: [], // 树形结构数据 currentNode: null, // 当前选中的节点 selectedNodeId: '', // 所有选中的节点ID(包括父节点) // 树形结构配置 defaultProps: { children: 'children', label: 'name', isLeaf: 'leaf' }, defaultProp: { children: 'children', label: 'name', isLeaf: 'leaf' }, lazy: true, }; }, components: { detailsContent }, watch: { async sysName(val) { if (!val) { this.lazy = true clearTimeout(this.debounceTimer); const res = await busiTree({ level: 0 }) this.currentNode = res.data.data[0]; this.selectedNodeId = res.data.data[0].id; this.$nextTick(() => { this.$refs.detailsContent.init() }) } else { this.lazy = false } }, }, async created() { // console.log(this.$route.query.idArr, 'this.$route.path') if (this.$route.query.idArr) { this.lazy = false const sysId = this.$route.query.idArr.split(",").pop() this.selectedNodeId = sysId console.log(this.$route.query.idArr, 'this.$route.query.idArr') await this.getBusiTree(sysId, '', '', 'create') } }, mounted() { // console.log('mounted') this.handleYResize() // this.$refs.detailsContent.init() // this.generateTreeData(); // this.handleNodeClick(this.treeData[0],{}); }, deactivated() { clearTimeout(this.debounceTimer) }, methods: { //sysId=1196&level=2&parentIdArr=100044,230 async getBusiTree(sysId, level, parentIdArr, type) { const res = await busiTree({ sysId, level, parentIdArr, name: this.sysName }) console.log('getBusiTree res.data.data: ', res.data.data); this.treeData = res.data.data if (this.sysName) { this.currentNode = res.data.data[0]; this.selectedNodeId = res.data.data[0].id; this.$nextTick(() => { this.$refs.detailsContent.init() }) } else if (type === 'create') { const sysIdArr = this.$route.query.idArr.split(",") // 提取三级ID const [firstId, secondId, thirdId] = sysIdArr; // 第一步:匹配一级节点(treeData直接匹配firstId) const firstLevelNode = this.treeData.find( node => node.id.toString() === firstId.toString() ); if (!firstLevelNode) { // 一级节点未找到,currentNode设为null this.currentNode = null; } else { // 第二步:在一级节点的children中匹配secondId const secondLevelNode = firstLevelNode.children.find( node => node.id.toString() === secondId.toString() ); if (!secondLevelNode) { // 二级节点未找到,currentNode设为null this.currentNode = null; } else { // 第三步:在二级节点的children中匹配thirdId const thirdLevelNode = secondLevelNode.children.find( node => node.id.toString() === thirdId.toString() ); // 最终赋值(找到则为三级节点,否则为null) this.currentNode = thirdLevelNode || null; } } } this.$nextTick(() => { this.$refs.detailsContent.init() }) }, handleYResize() { const box = document.getElementsByClassName('business-architecture-container') const left = document.getElementsByClassName('leftMenu') const resize = document.getElementsByClassName('y-resize') const right = document.getElementsByClassName('content-box') for (let i = 0; i < resize.length; i++) { // 鼠标按下事件 resize[i].onmousedown = function (e) { // 颜色改变提醒 resize[i].style.background = '#C0C4CC' const startX = e.clientX resize[i].left = resize[i].offsetLeft // 鼠标拖动事件 document.onmousemove = function (e) { const endX = e.clientX let moveLen = resize[i].left + (endX - startX) // (endX-startX)=移动的距离。resize[i].left+移动的距离=左侧最终的高度 const maxT = box[i].clientWidth - resize[i].offsetWidth // 容器宽度 - 左边区域的宽度 = 右边区域的宽度 if (moveLen < 30) moveLen = 30 // left最小宽度度为30px if (moveLen > maxT - 30) moveLen = maxT - 30 // right最小宽度度为30px resize[i].style.left = moveLen // 设置left区域的宽度 for (let j = 0; j < left.length; j++) { left[j].style.width = moveLen + 'px' right[j].style.width = (box[i].clientWidth - moveLen - 10) + 'px' } } // 鼠标松开事件 document.onmouseup = function (evt) { // 颜色恢复 resize[i].style.background = '#ecedee' document.onmousemove = null document.onmouseup = null resize[i].releaseCapture && resize[i].releaseCapture() // 当你不在需要继续获得鼠标消息就要应该调用ReleaseCapture()释放掉 } resize[i].setCapture && resize[i].setCapture() // 该函数在属于当前线程的指定窗口里设置鼠标捕获 return false } } }, // 搜索树 async debouncedFetchData() { this.lazy = false clearTimeout(this.debounceTimer) this.debounceTimer = setTimeout(() => { this.getBusiTree('', '', '') }, 500) }, // // 树节点过滤方法 // filterNode(value, data) { // if (!value) return true; // return data.name.indexOf(value) !== -1; // }, // 处理节点点击事件 handleNodeClick(data, node) { !this.sysName && (this.lazy = true) console.log(data, '处理节点点击事件 data,') console.log(node, '处理节点点击事件 node') // 统一数据结构处理 this.currentNode = { ...data, // // 非懒加载模式从node获取层级 // level: !this.lazy ? node.level : data.level, // // 确保parentIdArr存在 // parentIdArr: data.parentIdArr || [] }; // 安全处理节点ID数组 this.selectedNodeId = this.currentNode.id; console.log('this.currentNode', this.currentNode) this.$nextTick(() => { this.$refs.detailsContent.init() }) }, // 判断节点是否被选中 isNodeSelected(id) { return this.selectedNodeId.includes(id); }, // 获取层级前缀 getLevelPrefix(level) { switch (level) { case 1: return '体系'; case 2: return '业务'; case 3: return '场景'; default: return ''; } }, async loadNode(node, resolve) { console.log('loadNode node, resolve', node, resolve) if (node.level === 0) { // 根节点加载 const res = await busiTree({ level: 0 }) this.currentNode = res.data.data[0]; this.selectedNodeId = res.data.data[0].id; this.$nextTick(() => { this.$refs.detailsContent.init() }) return resolve(res.data.data) } // 获取当前节点信息 const currentNode = node.data const params = { sysId: currentNode.id, level: currentNode.level, parentIdArr: currentNode.parentIdArr ? currentNode.parentIdArr + '' : [] } try { const res = await busiTree(params) resolve(res.data.data) } catch (error) { resolve([]) } }, // 格式化树节点数据 formatTreeData(nodes) { return nodes.map(node => ({ ...node, hasChild: node.children && node.children.length > 0 })) }, // 获取父节点ID链 // getParentIds(node) { // const ids = [] // let parent = node.parent // while (parent && parent.level !== 0) { // ids.unshift(parent.data.id) // parent = parent.parent // } // return ids.join(',') // } } }; </script> <style scoped lang="less"> .business-architecture-container { height: 100%; display: flex; padding: 5px; // .business-architecture-container { // display: flex; background: #fff; // height: 100%; .y-resize { width: 10px; height: 100%; // background: #ecedee; background: #fff; cursor: w-resize; } .leftMenu { width: 15%; height: 100%; overflow-y: auto; // background-size: cover; // border: #ecedee 1px solid; .left-title { color: #353535; padding: 2px 5px 5px; } .left-search { padding: 5px; } .blue-text { color: #409EFF; } /deep/.el-tree-node__content { height: 35px; } .node-label { margin-right: 20px; border-radius: 3px; padding: 1px 5px; font-size: 14px; } /* 层级标识 */ .level-1 .node-label { /* font-weight: bold; */ color: #d42a38; border: 1px solid #ffc9c6; background: #fff1f0; } .level-2 .node-label { /* padding-left: 10px; */ color: #e1ab4a; border: 1px solid #ffe99f; background: #fffbe6; } .level-3 .node-label { /* padding-left: 20px; */ color: #84b713; border: 1px solid #ecff97; background: #fcffe6; } } .content-box { flex: 1; height: 100%; border-radius: 4px; overflow-y: auto; padding: 10px 15px 0 0; .breadcrumb { padding: 5px; margin-bottom: 10px; // font-size: 16px; color: #606266; } // .breadcrumb span { // color: #409EFF; // } } .returnBack { width: 100px; color: #66b1ff; cursor: pointer; } } </style> 优化以上代码 实现 : 将两棵树合并成一棵树,实现现有两棵树的效果 或者实现两棵树无痕切换

var createError = require('http-errors'); var express = require('express'); const multer = require('multer') // 上传图片 npm install multer --save var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); const fs = require("fs"); // 引入 fs文件上传 var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var app = express(); // 改写, 从bin目录下剪切代码过来 , 然后就可以删除bin目录了 var http = require('http'); var server = http.createServer(app); var cors = require('cors') app.use(cors()) var bodyParser = require('body-parser') app.use(bodyParser.urlencoded({ extended: false })) app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); app.use('/users', usersRouter); //图片上传 app.post( "/upload", multer({ //设置文件存储路径 dest: "public/image", }).array("file", 1), function (req, res, next) { let files = req.files; let file = files[0]; let fileInfo = {}; var path = "image/" + Date.now().toString() + "_" + file.originalname; let path2 = "public/image/" + Date.now().toString() + "_" + file.originalname; console.log(path) fs.renameSync("./public/image/" + file.filename, path2); //获取文件基本信息 fileInfo.type = file.mimetype; fileInfo.name = file.originalname; fileInfo.size = file.size; fileInfo.path = path; // string.substr(start,length) 从某一项开始,到某一项结束截取字符串,如果第二项不写就是从某一项开始截取后面所有字符串; var imgUrl = http://127.0.0.1:5000/${path} res.json({ code: 200, msg: "OK", data: fileInfo, imgUrl }); console.log(fileInfo) console.log(imgUrl) } ); // module.exports = app; 改写之后这里就不用暴露出去了 ,直接在下面写一个监听端口 server.listen(5000,()=>{ console.log("服务器启动成功!"); console.log("可以使用http:127.0.0.1:5000进行访问了"); });解释一下

import { Request, Response } from "express"; import { Item } from "../ts/tabuwithweight/Item"; import { Instance } from "../ts/tabuwithweight/Instance"; import { multiply, doArr, removeFrom2DArrayByProperty, stackCargoAll, stackCargo, stackCargoAll2, stackCargo2, stackCargoFullHeight } from '../ts/util' import { Solution } from "../ts/tabuwithweight/Solution"; import { Worker } from 'worker_threads'; import { PlaceItem } from "../ts/tabuwithweight/PlaceItem"; import * as os from 'os'; // 引入 Node.js 的 os 模块 import { db } from "../app"; function intersection(arr1: any[], arr2: any[]) { let arr3:any[] = [] let arr4:any[] = [] arr1.forEach(item => { item.split('-').forEach((it:any) => { arr3.push(it) }) }) arr2.forEach(item => { item.split('-').forEach((it:any) => { arr4.push(it) }) }) //返回arr3中包含几个arr4中的元素 return arr3.filter(item => arr4.indexOf(item) !== -1); } async function loop2(car:carType, car2:carType, goods:itemType[], goods2: itemType[], his: any[], prev: any[]): Promise<any>{ goods.sort((a, b) => { //货物先按面积排序,相同面积的再按高度排序 return a.id - b.id || multiply(b.length, b.width) - multiply(a.length, a.width) || b.depth - a.depth }) goods2.sort((a, b) => { //货物先按面积排序,相同面积的再按高度排序 return a.id - b.id || multiply(b.length, b.width) - multiply(a.length, a.width) || b.depth - a.depth }) // let res: resType<itemType> = doArr(goods, car) // let res2: resType<itemType> = doArr(goods2, car) let groups:itemType[][] = [] let groups1:itemType[][] = [] let groups2:itemType[][] = [] // console.log('prev',prev) prev.forEach((it: any)=>{ // console.log(it) let g:itemType[] = [] let g2:itemType[] = [] it.split('-').forEach((it3: any, i3: number)=>{ let obj = { depth: goods.find((it2: any)=>{return it3 == it2.id}).depth, width: goods.find((it2: any)=>{return it3 == it2.id}).width, length: goods.find((it2: any)=>{return it3 == it2.id}).length, weight: goods.find((it2: any)=>{return it3 == it2.id}).weight, boxNumber: goods.find((it2: any)=>{return it3 == it2.id}).boxNumber, packageType: goods.find((it2: any)=>{return it3 == it2.id}).packageType, packageName: goods.find((it2: any)=>{return it3 == it2.id}).packageName, id: it3, warehouseName: goods.find((it2: any)=>{return it3 == it2.id}).warehouseName, allowedRotation: goods.find((it2: any)=>{return it3 == it2.id}).allowedRotation, pc: goods.find((it2: any)=>{return it3 == it2.id}).pc, } g.push(obj) if(obj.pc != 2){ g2.push(obj) } }) groups.push(g) groups1.push(g) groups2.push(g2) }) // let groups = stackCargoAll2(car.innerDepth, goods.concat(goods2)) // let groups1 = stackCargo2(car.innerDepth, goods.concat(goods2)) // let groups2 = stackCargo2(car.innerDepth, goods) // let groups1 = prev // let groups2 = prev let batch1GoodsId = groups1.flat().map(it=>it.id) let batch2GoodsId = groups2.flat().map(it=>it.id) // console.log(batch1GoodsId) // console.log(batch1GoodsId.length) let batch1Goods:itemType[] = [] let batch2Goods:itemType[] = [] goods.concat(goods2).forEach((it:any)=>{ if(batch1GoodsId.includes(it.id)){ batch1Goods.push(it) } }) goods.forEach((it:any)=>{ if(batch2GoodsId.includes(it.id)){ batch2Goods.push(it) } }) let nofull:nofullType = {} //不同高度叠放 let nosamedifang:any[] = [] let nofullall:nofullType = {} let nopack = [] let boxes:any[] = [] let nofullarr: {id?:string,extra?: number, carindex?: number}[] = [] let itemList: Item[] = [] let itemList2: Item[] = [] let instance = new Instance(); let instance2 = new Instance(); let allNames:string[] = [] let allNames2:string[] = [] groups.forEach(it=>{ let ids = let tmp = it.map(it2=>{ return it2.id }) let weight = 0 it.forEach(it2=>{ weight += it2.weight }) ids = tmp.join('-') // console.log(ids) itemList.push(new Item(ids, multiply(it[0].length, 100), multiply(it[0].width, 100), weight, it[0].depth, it[0].boxNumber)) allNames.push(ids) }) groups2.forEach(it=>{ let ids = let tmp = it.map(it2=>{ return it2.id }) let weight = 0 it.forEach(it2=>{ weight += it2.weight }) ids = tmp.join('-') itemList2.push(new Item(ids, multiply(it[0].length, 100), multiply(it[0].width, 100), weight, it[0].depth, it[0].boxNumber)) allNames2.push(ids) }) instance.setW(car2.innerLength) instance.setH(car2.innerWidth) instance.setRotateEnable(false) instance.setItemList(itemList) instance2.setW(car2.innerLength) instance2.setH(car2.innerWidth) instance2.setRotateEnable(false) instance2.setItemList(itemList2) function isContain(arr:string[], str:string){ return arr.some(it=>{ return str.startsWith(it,0) }) } let ress:Solution[] = [] let ress2:Solution[] = [] // 获取CPU的核心数 let cpus = os.cpus(); let coreCount = cpus.length; //总循环次数 let loopTime = 2 //每个线程执行的循环次数 let pre = Math.floor(loopTime / coreCount)?Math.floor(loopTime / coreCount):1 // let coreHalf = Math.floor(coreCount/2)?Math.floor(coreCount/2):1 let coreHalf = 1 let workdata = { innerLength:car2.innerLength, innerWidth:car2.innerWidth, maxWeight:car2.maxWeight, groups:groups, goods: goods.concat(goods2), batch2GoodsId:batch2GoodsId.map(it => it.toString()), loopTime:pre==loopTime?Math.floor(loopTime / 2):pre } // console.log('innerLength',workdata.innerLength) // console.log('innerWidth',workdata.innerWidth) console.log('maxWeight',car2.maxWeight) let prom = [] for (let index = 0; index < coreHalf; index++) { prom.push(getWorkerResult(workdata)) } for (let index = 0; index < coreHalf; index++) { prom.push(getWorkerResult2(workdata)) } let proRes = await Promise.all(prom) proRes.forEach((it3:any, i3:number)=>{ it3.forEach((it2:any)=>{ let placeItemLists: PlaceItem[] = [] it2.placeItemList.forEach((it: any)=>{ let pla:PlaceItem = new PlaceItem(it.name,it.x,it.y,it.w,it.h,it.isRotate,it.weight) placeItemLists.push(pla) }) let sol: Solution = new Solution(placeItemLists, it2.totalS, it2.rate, car2.innerLength, car2.innerWidth) if(i3 < coreHalf){ ress.push(sol) }else{ ress2.push(sol) } }) }) ress.sort((a, b) => { let arr1 = a.getPlaceItemList().map(it => it.getName()); let arr2 = b.getPlaceItemList().map(it => it.getName()); let intersectionLengthA = intersection(arr1, batch2GoodsId.map(it => it.toString())).length; let intersectionLengthB = intersection(arr2, batch2GoodsId.map(it => it.toString())).length; // 先比较交集长度 if (intersectionLengthA !== intersectionLengthB) { return intersectionLengthB - intersectionLengthA; // 从高到低排序 } else { // 如果交集长度相同,则比较 rate return b.getRate() * 1000 - a.getRate() * 1000; // 从高到低排序 } }) ress2.sort((a, b) => { let arr1 = a.getPlaceItemList().map(it => it.getName()); let arr2 = b.getPlaceItemList().map(it => it.getName()); let intersectionLengthA = intersection(arr1, batch2GoodsId.map(it => it.toString())).length; let intersectionLengthB = intersection(arr2, batch2GoodsId.map(it => it.toString())).length; // 先比较交集长度 if (intersectionLengthA !== intersectionLengthB) { return intersectionLengthB - intersectionLengthA; // 从高到低排序 } else { // 如果交集长度相同,则比较 rate return b.getRate() * 1000 - a.getRate() * 1000; // 从高到低排序 } }) let maxNum = intersection(ress[0].getPlaceItemList().map(it=>it.getName()), batch2GoodsId.map(it=>{return it.toString()})).length let maxNum2 = intersection(ress2[0].getPlaceItemList().map(it=>it.getName()), batch2GoodsId.map(it=>{return it.toString()})).length // let maxNum = ress[0].getRate() // let maxNum2 = ress2[0].getRate() // console.log('maxNum',maxNum) // console.log('batch2GoodsId',batch2GoodsId.map(it=>{return it.toString()})) // console.log('maxNum2',maxNum2) if(maxNum2 > maxNum){ ress = ress2 maxNum = maxNum2 // console.log(11) } if(maxNum2 == maxNum){ if(ress[0].getRate() < ress2[0].getRate()){ ress = ress2 maxNum = maxNum2 } } let maxRate = ress[0].getRate() // let maxList = ress let maxList = ress.filter(it=>{ return (intersection(it.getPlaceItemList().map(it2=>it2.getName()), batch2GoodsId.map(it=>{return it.toString()})).length === maxNum) }) maxList.sort((a, b)=>{ return b.getRate()*1000 - a.getRate()*1000 }) let lens:number[] = [] let vs:number[] = [] //冒泡排序 按体积排序 function vsort(a:Solution, b:Solution): number{ //体积 let v1 = 0 let v2 = 0 a.getPlaceItemList().forEach((it2:any)=>{ //单个物品体积 let tv:number = 0 //通过 it2.getName().split('-').forEach((it3:any)=>{ let o = goods.concat(goods2).find(it4=>{ return Number(it3) == it4.id }) tv += multiply(multiply(o.width,o.length),o.depth) }) v1 += tv }) b.getPlaceItemList().forEach((it2:any)=>{ //单个物品体积 let tv:number = 0 //通过 it2.getName().split('-').forEach((it3:any)=>{ let o = goods.concat(goods2).find(it4=>{ return Number(it3) == it4.id }) tv += multiply(multiply(o.width,o.length),o.depth) }) v2 += tv }) if(v2 != v1){ return v2 - v1 }else{ return b.getRate()*1000 - a.getRate()*1000 } } maxList.sort(vsort) let solution = maxList[0] // ress.sort((a, b) => { // // 如果交集长度相同,则比较 rate // return b.getRate() * 1000 - a.getRate() * 1000; // 从高到低排序 // }) // ress2.sort((a, b) => { // // 如果交集长度相同,则比较 rate // return b.getRate() * 1000 - a.getRate() * 1000; // 从高到低排序 // }) // let maxNum = ress[0].getRate() // let maxNum2 = ress2[0].getRate() // if(maxNum2 > maxNum){ // ress = ress2 // maxNum = maxNum2 // // console.log(11) // } // if(maxNum2 == maxNum){ // if(ress[0].getRate() < ress2[0].getRate()){ // ress = ress2 // maxNum = maxNum2 // } // } // let maxRate = ress[0].getRate() // let maxList = ress // maxList.sort((a, b)=>{ // return b.getRate()*1000 - a.getRate()*1000 // }) // let lens:number[] = [] // let vs:number[] = [] // //冒泡排序 按体积排序 // function vsort(a:Solution, b:Solution): number{ // //体积 // let v1 = 0 // let v2 = 0 // a.getPlaceItemList().forEach((it2:any)=>{ // //单个物品体积 // let tv:number = 0 // //通过 // it2.getName().split('-').forEach((it3:any)=>{ // let o = goods.concat(goods2).find(it4=>{ // return Number(it3) == it4.id // }) // tv += multiply(multiply(o.width,o.length),o.depth) // }) // v1 += tv // }) // b.getPlaceItemList().forEach((it2:any)=>{ // //单个物品体积 // let tv:number = 0 // //通过 // it2.getName().split('-').forEach((it3:any)=>{ // let o = goods.concat(goods2).find(it4=>{ // return Number(it3) == it4.id // }) // tv += multiply(multiply(o.width,o.length),o.depth) // }) // v2 += tv // }) // if(v2 != v1){ // return v2 - v1 // }else{ // return b.getRate()*1000 - a.getRate()*1000 // } // } // maxList.sort(vsort) // let solution = maxList[0] // console.log('solutionx',solution.getXOffset()) // console.log('solutiony',solution.getYOffset()) let nopacked = JSON.parse(JSON.stringify(itemList)) // console.log('solution',solution.getPlaceItemList().map(it=>{return it.getName()})) // console.log('nopacked',nopacked.map((it:any)=>{return it.name})) let finalResult:any[] = [] for(let i = nopacked.length - 1; i >= 0; i--) { if(solution.getPlaceItemList().find(delItem => delItem.getName().split('-')[0] === nopacked[i].name.split('-')[0])) { let waitFix = solution.getPlaceItemList().find(delItem => delItem.getName().split('-')[0] === nopacked[i].name.split('-')[0]) if(waitFix){ waitFix.setName(nopacked[i].name) } nopacked.splice(i, 1); } } // console.log('nopacked.length2',nopacked.length) solution.getPlaceItemList().forEach((it, i)=>{ let flag = it.getName().split('-').every(it2=>{ let pc = goods.concat(goods2).find(it3=>{return it2 == it3.id.toString()}).weight return pc == 2 }) if(flag){ let l = goods2.find((it2: any)=>{return it.getName().split('-')[0] == it2.id}).length let w = goods2.find((it2: any)=>{return it.getName().split('-')[0] == it2.id}).width let d = 0 it.getName().split('-').forEach(it2=>{ d += goods2.find((it3: any)=>{return it2 == it3.id.toString()}).depth }) //查询nopacked中有没有同样尺寸的 for (let index = 0; index < nopacked.length; index++) { let it2 = nopacked[index]; let l2 = goods.concat(goods2).find((it3: any)=>{return it2.name.split('-')[0] == it3.id}).length let w2 = goods.concat(goods2).find((it3: any)=>{return it2.name.split('-')[0] == it3.id}).width if(l2 == l && w2 == w){ let d2 = 0 it2.name.split('-').forEach((it3: any)=>{ d2 += goods.concat(goods2).find((it4: any)=>{return it3 == it4.id.toString()}).depth }) if(d2 > d){ let na = JSON.parse(JSON.stringify(it.getName())) it.setName(it2.name) nopacked[index].name = na; } } } } }) let soluv = 0 solution.getPlaceItemList().forEach((it2:any)=>{ //单个物品体积 let tv:number = 0 //通过 it2.getName().split('-').forEach((it3:any)=>{ let o = goods.concat(goods2).find(it4=>{ return Number(it3) == it4.id }) tv += multiply(multiply(o.width,o.length),o.depth) }) soluv += tv }) let rate3d = soluv / (car.innerLength*car.innerDepth*car.innerWidth) solution.setRate3d(rate3d) //上一次装箱结果 his.push(solution) let all:any[] = [] all = goods.concat(goods2) // console.log(nopacked.length) if(nopacked.length > 0){ let copyItemList = JSON.parse(JSON.stringify(itemList)) let nextboxes:itemType[] = [] let prevarr: any[] = [] // console.log(nopacked) nopacked.forEach((it: any)=>{ // console.log(it) it.name.split('-').forEach((it3: any, i3: number)=>{ let obj = { depth: all.find((it2: any)=>{return it3 == it2.id}).depth, width: all.find((it2: any)=>{return it3 == it2.id}).width, length: all.find((it2: any)=>{return it3 == it2.id}).length, weight: all.find((it2: any)=>{return it3 == it2.id}).weight, boxNumber: all.find((it2: any)=>{return it3 == it2.id}).boxNumber, id: it3, packageType: goods.find((it2: any)=>{return it3 == it2.id}).packageType, packageName: goods.find((it2: any)=>{return it3 == it2.id}).packageName, warehouseName: all.find((it2: any)=>{return it3 == it2.id}).warehouseName, allowedRotation: all.find((it2: any)=>{return it3 == it2.id}).allowedRotation, } nextboxes.push(obj) }) prevarr.push(it.name) }) return new Promise<any[]>(async (resolve, reject) => { const resss = await loop2(car, car2, nextboxes, [], his, prevarr) resolve(resss) }) }else{ return new Promise<any[]>((resolve, reject) => { resolve(his) }) } } export let index = async (req: Request, res: Response): Promise<void> => { res.type('html'); // res.render('index', { title: 'Express' }); res.render('limitlayer', { title: 'Express' }); // res.render("index", { title: "Express" }); }; export let index2 = async (req: Request, res: Response): Promise<void> => { res.type('html'); // res.render('index', { title: 'Express' }); res.render('limitlayer2', { title: 'Express' }); // res.render("index", { title: "Express" }); }; async function getWorkerResult(data: any){ return new Promise((resolve, reject)=>{ let worker = new Worker('./src/controllers/worker5.mjs'); worker.on('message', (event) => { // console.log('rece',event); resolve(event) worker.terminate(); // 终止Worker线程 }) worker.postMessage({ type: 'DATA', data: data }); }) } async function getWorkerResult2(data: any){ return new Promise((resolve, reject)=>{ let worker = new Worker('./src/controllers/worker6.mjs'); worker.on('message', (event) => { // console.log('rece',event); resolve(event) worker.terminate(); // 终止Worker线程 }) worker.postMessage({ type: 'DATA', data: data }); }) } export let two3 = async (req: Request, resp: Response): Promise<void> => { try { console.log('two3') //车辆信息 let car: carType = JSON.parse(req.body.car) //复制一份不影响原始车辆信息 let car2: carType = JSON.parse(JSON.stringify(car)) let car3: carType = JSON.parse(JSON.stringify(car)) car2.rotate = Object.hasOwnProperty.call(car2, 'rotate') ? Boolean(Number(car.rotate)) : true car2.innerDepth = multiply(car2.innerDepth , 100) car2.innerLength = multiply(car2.innerLength , 100) car2.innerWidth = multiply(car2.innerWidth , 100) car2.outerDepth = multiply(car2.outerDepth , 100) car2.outerLength = multiply(car2.outerLength , 100) car2.outerWidth = multiply(car2.outerWidth , 100) car2.emptyWeight = multiply(car2.emptyWeight , 100) car2.maxWeight = multiply(car2.maxWeight,1) car3.innerDepth = multiply(car3.innerDepth , 1000) car3.innerLength = multiply(car3.innerLength , 1000) car3.innerWidth = multiply(car3.innerWidth , 1000) car3.outerDepth = multiply(car3.outerDepth , 1000) car3.outerLength = multiply(car3.outerLength , 1000) car3.outerWidth = multiply(car3.outerWidth , 1000) car3.emptyWeight = multiply(car3.emptyWeight , 1000) car3.maxWeight = multiply(car3.maxWeight,1) //货物信息 let goods: itemType[] = JSON.parse(req.body.boxes) let goods2: itemType[] = JSON.parse(req.body.boxes2) goods.sort((a, b) => { //货物先按面积排序,相同面积的再按高度排序 return a.id - b.id || multiply(b.length, b.width) - multiply(a.length, a.width) || b.depth - a.depth }) goods2.sort((a, b) => { //货物先按面积排序,相同面积的再按高度排序 return a.id - b.id || multiply(b.length, b.width) - multiply(a.length, a.width) || b.depth - a.depth }) // let res: resType<itemType> = doArr(goods, car) // let res2: resType<itemType> = doArr(goods2, car) let tmp = stackCargoFullHeight(car.innerDepth, goods.concat(goods2)) let groups = tmp.res let groups1 = tmp.res let groups2 = tmp.res1 let batch1GoodsId = groups1.flat().map(it=>it.id) // console.log(batch1GoodsId) let batch2GoodsId = groups2.flat().map(it=>it.id) let batch1Goods:itemType[] = [] let batch2Goods:itemType[] = [] goods.concat(goods2).forEach((it:any)=>{ if(batch1GoodsId.includes(it.id)){ batch1Goods.push(it) } }) goods.forEach((it:any)=>{ if(batch2GoodsId.includes(it.id)){ batch2Goods.push(it) } }) let nofull:nofullType = {} //不同高度叠放 let nosamedifang:any[] = [] let nofullall:nofullType = {} let nopack = [] let boxes:any[] = [] let nofullarr: {id?:string,extra?: number, carindex?: number}[] = [] let itemList: Item[] = [] let itemList2: Item[] = [] let instance = new Instance(); let instance2 = new Instance(); let allNames:string[] = [] let allNames2:string[] = [] groups.forEach(it=>{ let ids = let tmp = it.map(it2=>{ return it2.id }) ids = tmp.join('-') // console.log(ids) itemList.push(new Item(ids, multiply(it[0].length, 100), multiply(it[0].width, 100), it[0].weight, it[0].depth, it[0].boxNumber)) allNames.push(ids) }) groups2.forEach(it=>{ let ids = let tmp = it.map(it2=>{ return it2.id }) ids = tmp.join('-') itemList2.push(new Item(ids, multiply(it[0].length, 100), multiply(it[0].width, 100), it[0].weight, it[0].depth, it[0].boxNumber)) allNames2.push(ids) }) // console.log(groups) instance.setW(car2.innerLength) instance.setH(car2.innerWidth) instance.setRotateEnable(false) instance.setItemList(itemList) instance2.setW(car2.innerLength) instance2.setH(car2.innerWidth) instance2.setRotateEnable(false) instance2.setItemList(itemList2) function isContain(arr:string[], str:string){ return arr.some(it=>{ return str.startsWith(it,0) }) } let ress:Solution[] = [] let ress2:Solution[] = [] // 获取CPU的核心数 let cpus = os.cpus(); let coreCount = cpus.length; //总循环次数 let loopTime = 2 //每个线程执行的循环次数 let pre = Math.floor(loopTime / coreCount)?Math.floor(loopTime / coreCount):1 let coreHalf = 1 let workdata = { innerLength:car2.innerLength, innerWidth:car2.innerWidth, maxWeight:car2.maxWeight, groups:groups, rotate:car2.rotate, goods: goods.concat(goods2), batch2GoodsId:batch2GoodsId.map(it => it.toString()), loopTime:pre==loopTime?Math.floor(loopTime / 2):pre } let prom = [] for (let index = 0; index < coreHalf; index++) { prom.push(getWorkerResult(workdata)) } for (let index = 0; index < coreHalf; index++) { prom.push(getWorkerResult2(workdata)) } let proRes = await Promise.all(prom) proRes.forEach((it3:any, i3:number)=>{ it3.forEach((it2:any)=>{ let placeItemLists: PlaceItem[] = [] it2.placeItemList.forEach((it: any)=>{ let pla:PlaceItem = new PlaceItem(it.name,it.x,it.y,it.w,it.h,it.isRotate,it.weight) placeItemLists.push(pla) }) let sol: Solution = new Solution(placeItemLists, it2.totalS, it2.rate, car2.innerLength, car2.innerWidth) if(i3 < coreHalf){ ress.push(sol) }else{ ress2.push(sol) } }) }) ress.sort((a, b) => { let arr1 = a.getPlaceItemList().map(it => it.getName()); let arr2 = b.getPlaceItemList().map(it => it.getName()); let intersectionLengthA = intersection(arr1, batch2GoodsId.map(it => it.toString())).length; let intersectionLengthB = intersection(arr2, batch2GoodsId.map(it => it.toString())).length; // 先比较交集长度 if (intersectionLengthA !== intersectionLengthB) { return intersectionLengthB - intersectionLengthA; // 从高到低排序 } else { // 如果交集长度相同,则比较 rate return b.getRate() * 1000 - a.getRate() * 1000; // 从高到低排序 } }) ress2.sort((a, b) => { let arr1 = a.getPlaceItemList().map(it => it.getName()); let arr2 = b.getPlaceItemList().map(it => it.getName()); let intersectionLengthA = intersection(arr1, batch2GoodsId.map(it => it.toString())).length; let intersectionLengthB = intersection(arr2, batch2GoodsId.map(it => it.toString())).length; // 先比较交集长度 if (intersectionLengthA !== intersectionLengthB) { return intersectionLengthB - intersectionLengthA; // 从高到低排序 } else { // 如果交集长度相同,则比较 rate return b.getRate() * 1000 - a.getRate() * 1000; // 从高到低排序 } }) let maxNum = intersection(ress[0].getPlaceItemList().map(it=>it.getName()), batch2GoodsId.map(it=>{return it.toString()})).length let maxNum2 = intersection(ress2[0].getPlaceItemList().map(it=>it.getName()), batch2GoodsId.map(it=>{return it.toString()})).length // let maxNum = ress[0].getRate() // let maxNum2 = ress2[0].getRate() if(maxNum2 > maxNum){ ress = ress2 maxNum = maxNum2 // console.log(11) } if(maxNum2 == maxNum){ if(ress[0].getRate() < ress2[0].getRate()){ ress = ress2 maxNum = maxNum2 } } let maxRate = ress[0].getRate() let maxList = ress.filter(it=>{ return (intersection(it.getPlaceItemList().map(it2=>it2.getName()), batch2GoodsId.map(it=>{return it.toString()})).length === maxNum) }) maxList.sort((a, b)=>{ return b.getRate()*1000 - a.getRate()*1000 }) let lens:number[] = [] let vs:number[] = [] //冒泡排序 按体积排序 function vsort(a:Solution, b:Solution): number{ //体积 let v1 = 0 let v2 = 0 a.getPlaceItemList().forEach((it2:any)=>{ //单个物品体积 let tv:number = 0 //通过 it2.getName().split('-').forEach((it3:any)=>{ let o = goods.concat(goods2).find(it4=>{ return Number(it3) == it4.id }) tv += multiply(multiply(o.width,o.length),o.depth) }) v1 += tv }) b.getPlaceItemList().forEach((it2:any)=>{ //单个物品体积 let tv:number = 0 //通过 it2.getName().split('-').forEach((it3:any)=>{ let o = goods.concat(goods2).find(it4=>{ return Number(it3) == it4.id }) tv += multiply(multiply(o.width,o.length),o.depth) }) v2 += tv }) if(v2 != v1){ return v2 - v1 }else{ return b.getRate()*1000 - a.getRate()*1000 } } maxList.sort(vsort) let solution = maxList[0] let nopacked = JSON.parse(JSON.stringify(itemList)) let finalResult:any[] = [] for(let i = nopacked.length - 1; i >= 0; i--) { if(solution.getPlaceItemList().find(delItem => delItem.getName().split('-')[0] === nopacked[i].name.split('-')[0])) { let waitFix = solution.getPlaceItemList().find(delItem => delItem.getName().split('-')[0] === nopacked[i].name.split('-')[0]) if(waitFix){ waitFix.setName(nopacked[i].name) } nopacked.splice(i, 1); } } solution.getPlaceItemList().forEach((it, i)=>{ let flag = it.getName().split('-').every(it2=>{ let pc = goods.concat(goods2).find(it3=>{return it2 == it3.id.toString()}).weight return pc == 2 }) if(flag){ let l = goods2.find((it2: any)=>{return it.getName().split('-')[0] == it2.id}).length let w = goods2.find((it2: any)=>{return it.getName().split('-')[0] == it2.id}).width let d = 0 it.getName().split('-').forEach(it2=>{ d += goods2.find((it3: any)=>{return it2 == it3.id.toString()}).depth }) //查询nopacked中有没有同样尺寸的 for (let index = 0; index < nopacked.length; index++) { let it2 = nopacked[index]; let l2 = goods.concat(goods2).find((it3: any)=>{return it2.name.split('-')[0] == it3.id}).length let w2 = goods.concat(goods2).find((it3: any)=>{return it2.name.split('-')[0] == it3.id}).width if(l2 == l && w2 == w){ let d2 = 0 it2.name.split('-').forEach((it3: any)=>{ d2 += goods.concat(goods2).find((it4: any)=>{return it3 == it4.id.toString()}).depth }) if(d2 > d){ let na = JSON.parse(JSON.stringify(it.getName())) it.setName(it2.name) nopacked[index].name = na; } } } } }) let soluv = 0 solution.getPlaceItemList().forEach((it2:any)=>{ //单个物品体积 let tv:number = 0 //通过 it2.getName().split('-').forEach((it3:any)=>{ let o = goods.concat(goods2).find(it4=>{ return Number(it3) == it4.id }) tv += multiply(multiply(o.width,o.length),o.depth) }) soluv += tv }) let rate3d = soluv / (car.innerLength*car.innerDepth*car.innerWidth) solution.setRate3d(rate3d) let all:any[] = [] all = goods.concat(goods2) if(nopacked.length > 0){ let copyItemList = JSON.parse(JSON.stringify(itemList)) let nextboxes:itemType[] = [] let prev: any[] = [] // console.log(nopacked) nopacked.forEach((it: any)=>{ // console.log(it) it.name.split('-').forEach((it3: any, i3: number)=>{ let obj = { depth: all.find((it2: any)=>{return it3 == it2.id}).depth, width: all.find((it2: any)=>{return it3 == it2.id}).width, length: all.find((it2: any)=>{return it3 == it2.id}).length, weight: all.find((it2: any)=>{return it3 == it2.id}).weight, boxNumber: all.find((it2: any)=>{return it3 == it2.id}).boxNumber, id: it3, packageType: goods.find((it2: any)=>{return it3 == it2.id}).packageType, packageName: goods.find((it2: any)=>{return it3 == it2.id}).packageName, warehouseName: all.find((it2: any)=>{return it3 == it2.id}).warehouseName, allowedRotation: all.find((it2: any)=>{return it3 == it2.id}).allowedRotation, } nextboxes.push(obj) }) prev.push(it.name) }) finalResult = await loop2(car, car2, nextboxes, [], [solution], prev) }else{ finalResult = [solution] } let result:any = [] finalResult.forEach((it2: any)=>{ let obj:any = {} obj.box = car3 let copyPlace = JSON.parse(JSON.stringify(it2.getPlaceItemList())) obj.items = copyPlace result.push(obj) }) result.forEach((it2: any)=>{ it2.items.forEach((it: any)=>{ let length = Math.floor(it.w / 100 * 1000) it.width = Math.floor(it.h / 100 * 1000) it.height = Math.floor(it.h / 100 * 1000) it.depth = Math.floor(all.find(it3=>{return it3.id == it.name.split('-')[0]}).depth * it.name.split('-').length * 1000) it.length = length //互换xy let temp = it.x it.x = it.y it.y = temp it.x = Math.floor(it.x / 100 * 1000) it.y = Math.floor(it.y / 100 * 1000) it.z = 0 let param = {} Object.assign(param,it,{description:it.name}) it.item = param }) }) nofullarr.forEach(it=>{ result.forEach((it2:any,i2:number)=>{ let flag = it2.items.some((it3:any)=>{ if(it3.name.indexOf(it.id) !== -1){ return true } }) if(flag){ it.carindex = i2 } }) }) let copyFinalResult = JSON.parse(JSON.stringify(finalResult)) let copyNofullarr = JSON.parse(JSON.stringify(nofullarr)) function changePos(cari: number): void { let firstCarNofull = nofullarr.filter((it)=>{return it.carindex == cari}) firstCarNofull.forEach(it=>{ //第一车中 不满的货物 let w = all.find((it2)=>{return it2.id == it.id.split('-')[0]}).width let l = all.find((it2)=>{return it2.id == it.id.split('-')[0]}).length let d = all.find((it2)=>{return it2.id == it.id.split('-')[0]}).depth let fi = copyFinalResult[0].placeItemList.findIndex((it2: any)=>{ return it2.name == it.id}) let searchIndex = -1 //倒叙循环finalResult,搜索后方车辆有没有 长宽高和第一车不满的货物一样的 for(let i = copyFinalResult.length - 1; i >= cari + 1; i--){ //如果结果中有wld和第一车一样的 交换位置 searchIndex = copyFinalResult[i].placeItemList.findIndex((it2: any)=>{ let w2 = all.find((it3)=>{return it3.id == it2.name.split('-')[0]}).width let l2 = all.find((it3)=>{return it3.id == it2.name.split('-')[0]}).length let d2 = all.find((it3)=>{return it3.id == it2.name.split('-')[0]}).depth return w == w2 && l == l2 && d == d2 }) if(searchIndex != -1){ //将第一车placeItemList数组中的第fi个元素的name和第i车placeItemList的第searchIndex个元素的name互换 let temp = copyFinalResult[cari].placeItemList[fi].name copyFinalResult[cari].placeItemList[fi].name = copyFinalResult[i].placeItemList[searchIndex].name copyFinalResult[i].placeItemList[searchIndex].name = temp copyNofullarr.find((it2: any)=>{return it2.id == it.id}).carindex = i break } } }) } // for (let index = 0; index < finalResult.length - 1; index++) { // changePos(index) // } result = [] copyFinalResult.forEach((it2: any)=>{ let obj:any = {} obj.box = car3 let copyPlace = JSON.parse(JSON.stringify(it2.placeItemList)) obj.items = copyPlace result.push(obj) }) result.forEach((it2: any)=>{ it2.items.forEach((it: any)=>{ let length = Math.floor(it.w / 100 * 1000) it.width = Math.floor(it.h / 100 * 1000) it.height = Math.floor(it.h / 100 * 1000) it.depth = Math.floor(all.find(it3=>{return it3.id == it.name.split('-')[0]}).depth * it.name.split('-').length * 1000) it.length = length //互换xy let temp = it.x it.x = it.y it.y = temp it.x = Math.floor(it.x / 100 * 1000) it.y = Math.floor(it.y / 100 * 1000) it.z = 0 let param = {} Object.assign(param,it,{description:it.name}) it.item = param }) }) let freeArr: any[] = [] copyFinalResult.forEach((it: any, i: number)=>{ let po: any = {} let boxes:any[] = [] it.placeItemList.forEach((it2: any)=>{ let o: any = {} o.x = it2.x o.y = it2.y o.width = it2.w o.height = it2.h boxes.push(o) }) po.carindex = i freeArr.push(po) }) resp.json({ code:200, car:car, boxes:goods.concat(goods2), nofullarr:copyNofullarr, message:'装箱成功', success:true, finalResult: copyFinalResult, changeFinalResult: copyFinalResult, freeArr:nosamedifang, data:result }) } catch (error) { console.log(error) resp.json({ code:500, message:'装箱失败', success:false, }) } }; export let index4 = async (req: Request, res: Response): Promise<void> => { res.type('html'); // res.render('index', { title: 'Express' }); res.render('limithistory', { title: 'Express' }); // res.render("index", { title: "Express" }); };

loadCheckFormPdf() { //创建并打开PDF let params = { DMODULENO: ‘553701’, DPSTYLENO: this.ioFormId } let _this = this; _this.http.post(“api/Sys_Report/getIoformPdf”, params, true).then(result => { if (result.status) { // #ifdef APP-PLUS if (uni.getSystemInfoSync().osName.indexOf(“ios”) > -1) { _this.pdfUrl = decodeURIComponent((_this.http.ipAddress ? _this.http.ipAddress : getApp() .globalData.ipAddress) + result.data); } else { _this.pdfUrl = _this.viewerUrl + ‘?file=’ + decodeURIComponent((_this.http.ipAddress ? _this .http .ipAddress : getApp().globalData.ipAddress) + result.data); } // #endif // #ifdef H5 _this.pdfUrl = decodeURIComponent(_this.http.ipAddress + result.data); // #endif console.log("pdfUrl", _this.pdfUrl) //直接打印 var dtask = plus.downloader.createDownload(_this.pdfUrl, { filename: "_doc/pdf/" + this.getFileName(_this.pdfUrl) }, function(d, status) { // 下载完成 if (status == 200) { FileShare.render({ type: "SYSTEM", //QQ为QQ,微信为WX,系统默认是SYSTEM,不填写默认SYSTEM filePath: plus.io.convertLocalFileSystemURL(d.filename), }, result => { console.log("result", result) }); } else { console.log("Download failed: " + status); } }); dtask.start(); } else { _this.$toast(result.message); } }) }, getFileName(url) { var a = url; var b = a.split("/"); var c = b.slice(b.length - 1, b.length).toString(String).split("."); console.log("111111111111111", c[0] + '.' + c[c.length - 1]) return c[0] + '.' + c[c.length - 1] }_this.pdfUrl为:Upload/Tables/MobileCheck成衣销售单.frx.pdf,现在function(d, status)的status 返回400,解决下

exportCSV() { this.$refs.xTable.exportData({ type: 'csv', fileName: '夹具数据_' + new Date().toLocaleDateString(), customExport: { columns: this.generateExportColumns(), data: this.generateExportData() }, options: { useCsv: true, bom: true, separator: ',' } }).then(() => { this.$message.success('CSV导出成功') }).catch(err => { this.$message.error('CSV导出失败: ' + err.message) }) }, // 生成导出列配置 generateExportColumns() { // 基础列配置 const baseColumns = [ { field: 'WorkSection', title: '工段' }, { field: 'Category', title: '分类' }, { field: 'Area', title: '区域' }, { field: 'WorkStation', title: '工位号/位置' }, { field: 'TFNo', title: '夹具编号' }, { field: 'TFName', title: '夹具名称' }, { field: 'AssetNo', title: '固定资产号' }, { field: 'FixtureStatus', title: '状态' } ] // 动态生成嵌套列的导出配置 const dynamicColumns = this.tableColumnList.map(item => { return { field: dynamic_${item.Id}, //title: ${item.VehicleName}-${item.SapNo}-${item.WhiteNo}-${item.ModelName}-${item.TfCount} title: ${item.VehicleName}-${item.ModelName} } }) console.log("生成的数据列",[...baseColumns, ...dynamicColumns]) return [...baseColumns, ...dynamicColumns] }, // 生成导出数据 generateExportData() { return this.gridData.map(row => { const newRow = { WorkSection: row.WorkSection, Category: row.Category, Area: row.Area, WorkStation: row.WorkStation, TFNo: row.TFNo, TFName: row.TFName, AssetNo: row.AssetNo, FixtureStatus: row.FixtureStatus } // 添加动态列 this.tableColumnList.forEach(item => { const value = row[item.Id.toString()]; newRow[dynamic_${item.Id}] = value !== undefined ? value : '0'; }); console.log("导出数据",newRow,"导出数据") return newRow }) }导出的数据动态列title不生效,显示的tfCount的值,可以怎么样改造一下

const express = require('express'); const cors = require('cors'); const multer = require('multer'); const axios = require('axios'); const fs = require('fs'); const qs = require('qs'); const path = require('path'); const app = express(); const port = 3000; // 创建上传目录 const uploadDir = path.join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } // 跨域配置 app.use(cors({ origin: '*', methods: ['POST', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] })); // 文件上传配置 const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, uploadDir); }, filename: (req, file, cb) => { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); } }); const upload = multer({ storage: storage, fileFilter: (req, file, cb) => { const allowedTypes = ['image/jpeg', 'image/png', 'image/bmp', 'image/gif']; if (!allowedTypes.includes(file.mimetype)) { return cb(new Error('仅支持JPEG/PNG/BMP/GIF格式图片'), false); } cb(null, true); }, limits: { fileSize: 5 * 1024 * 1024 // 限制5MB } }); // 百度API配置(请替换为实际密钥) const BAIDU_API_KEY = 'iBhL0RP4TD0ioSFbWI55Hs90'; const BAIDU_SECRET_KEY = '9pLRd6YNyq7NLytHMOBLACNSO80CQtmE'; const TOKEN_URL = 'https://aip.baidubce.com/oauth/2.0/token'; const PLANT_URL = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/plant'; // 全局错误处理 app.use((err, req, res, next) => { if (err instanceof multer.MulterError) { return res.status(400).json({ error: 文件上传错误: ${err.message}, result: [] }); } else if (err) { return res.status(500).json({ error: 服务器错误: ${err.message}, result: [] }); } next(); }); // 植物识别路由 app.post('/recognize', upload.single('image'), async (req, res) => { let tempFilePath = ''; try { if (!req.file) { return res.status(400).json({ error: '请上传有效的图片文件', result: [] }); } tempFilePath = req.file.path; // 读取图片并转换为base64 const imageBuffer = fs.readFileSync(tempFilePath); const imageBase64 = imageBuffer.toString('base64'); // 获取百度API访问令牌 const tokenRes = await axios.post(TOKEN_URL, null, { params: { grant_type: 'client_credentials', client_id: BAIDU_API_KEY, client_secret: BAIDU_SECRET_KEY } }); if (!tokenRes.data || !tokenRes.data.access_token) { throw new Error('获取百度API访问令牌失败'); } const accessToken = tokenRes.data.access_token; // 调用植物识别API const plantRes = await axios.post( PLANT_URL, qs.stringify({ image: imageBase64, baike_num: 5 // 返回5个结果 }), { params: { access_token: accessToken }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, timeout: 20000 // 20秒超时 } ); console.log('百度API原始响应:', plantRes.data); // 统一响应格式 - 更健壮的处理 let resultData = []; // 情况1:标准格式 { result: [...] } if (plantRes.data && Array.isArray(plantRes.data.result)) { resultData = plantRes.data.result; } // 情况2:百度API错误格式(如返回错误码) else if (plantRes.data.error_code) { throw new Error(百度API错误: ${plantRes.data.error_msg} (错误码: ${plantRes.data.error_code})); } // 情况3:直接数组格式(理论上百度API不会这样返回,但为了健壮性保留) else if (Array.isArray(plantRes.data)) { resultData = plantRes.data; } // 情况4:其他未知格式 else { throw new Error('百度API返回了无法解析的格式'); } // 格式化响应 const formattedResponse = { result: resultData .filter(item => item.name) // 过滤无效项(必须有名字) .map(item => ({ name: item.name, score: item.score || 0 // 如果没有分数,设为0 })) }; // 如果过滤后没有结果,尝试使用第一个结果(即使没有名字,但可能有其他信息) if (formattedResponse.result.length === 0 && resultData.length > 0) { formattedResponse.result = [{ name: resultData[0].name || '未知植物', score: resultData[0].score || 0 }]; } console.log('格式化后的响应:', formattedResponse); res.json(formattedResponse); } catch (error) { console.error('识别错误:', error); let errorMessage = '植物识别失败'; // 提取更具体的错误信息 if (error.response) { // 如果是百度API的响应错误 if (error.response.data && error.response.data.error_msg) { errorMessage = 百度API错误: ${error.response.data.error_msg}; } else { errorMessage = 服务请求错误: ${error.response.status} ${error.response.statusText}; } } else if (error.code === 'ECONNABORTED') { errorMessage = '请求超时,请重试'; } else { errorMessage = error.message; } res.status(500).json({ error: errorMessage, result: [] }); } finally { // 清理临时文件 if (tempFilePath) { fs.unlink(tempFilePath, (err) => { if (err) console.error('删除临时文件失败:', err); }); } } }); // 启动服务器 app.listen(port, () => { console.log(植物识别服务运行中:http://localhost:${port}); console.log('等待上传图片进行识别...'); }); <template> <van-nav-bar title="植物识别" /> <van-uploader :after-read="onRead" accept="image/*" :preview-size="120" :max-count="1" v-model="fileList" > <van-button type="primary" block round icon="photo">选择图片</van-button> </van-uploader> <van-image :src="uploadedImage" fit="contain" width="100%" height="auto" class="preview-image" /> <van-loading size="24px">识别中,请稍候...</van-loading> 0" class="result-section"> 识别结果: <van-cell-group inset> <van-cell v-for="(item, index) in results" :key="index" :title="item.name" :value="${(item.score * 100).toFixed(2)}%" :label="置信度:${(item.score * 100).toFixed(2)}%" /> </van-cell-group> <van-icon name="warning-o" color="#f56c6c" size="16" /> {{ errorMessage }} </template> <script> import axios from 'axios'; import { NavBar, Uploader, Image, Button, Loading, Cell, CellGroup, Toast, Icon } from 'vant'; export default { name: 'PlantRecognize', components: { [NavBar.name]: NavBar, [Uploader.name]: Uploader, [Image.name]: Image, [Button.name]: Button, [Loading.name]: Loading, [Cell.name]: Cell, [CellGroup.name]: CellGroup, [Icon.name]: Icon }, data() { return { fileList: [], uploadedImage: '', loading: false, results: [], errorMessage: '' }; }, methods: { async onRead(file) { this.loading = true; this.results = []; this.errorMessage = ''; try { // 预览图片 const reader = new FileReader(); reader.onload = (e) => { this.uploadedImage = e.target.result; }; reader.readAsDataURL(file.file); // 准备上传数据 const formData = new FormData(); formData.append('image', file.file); // 发送识别请求 const response = await axios.post( 'http://localhost:3000/recognize', formData, { headers: { 'Content-Type': 'multipart/form-data' }, timeout: 30000 // 30秒超时 } ); console.log('后端原始响应数据:', response.data); // 更健壮的响应格式校验 if (response.data && typeof response.data === 'object' && Array.isArray(response.data.result)) { // 转换格式,添加默认值处理 this.results = response.data.result.map(item => ({ name: item.name || '未知植物', score: item.score || 0 })); if (this.results.length === 0) { this.errorMessage = '未识别到植物特征,请尝试其他图片'; Toast.info(this.errorMessage); } } // 处理百度API原始响应格式(直接数组) else if (response.data && Array.isArray(response.data)) { this.results = response.data .filter(item => item.name) // 过滤掉无效项 .map(item => ({ name: item.name, score: item.score || 0 })); Toast.success('识别成功'); } else { throw new Error('服务端响应格式异常,缺少有效数据'); } } catch (error) { console.error('识别失败:', error); // 更详细的错误处理 if (error.response) { // 尝试获取后端返回的错误信息 if (error.response.data && error.response.data.error) { this.errorMessage = error.response.data.error; } else { this.errorMessage = 服务端错误: ${error.response.status} ${error.response.statusText}; } } else if (error.request) { this.errorMessage = '网络请求无响应,请检查服务是否运行'; } else { this.errorMessage = error.message || '识别失败,请重试'; } Toast.fail(this.errorMessage); } finally { this.loading = false; } } } }; </script> <style scoped> .plant-recognize-page { min-height: 100vh; background-color: #f7f8fa; padding-bottom: 20px; } .upload-container { padding: 20px; background: white; margin: 20px; border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); } .uploader-wrapper { display: flex; justify-content: center; align-items: center; flex-direction: column; margin-bottom: 20px; } .preview-container { margin-top: 20px; text-align: center; border: 1px dashed #ebedf0; border-radius: 8px; padding: 10px; background: #fafafa; } .preview-image { max-height: 300px; display: block; margin: 0 auto; } .loading-overlay { margin: 30px 0; text-align: center; color: #969799; } .result-section { margin: 20px; padding: 16px; background: white; border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); } .result-title { font-size: 18px; font-weight: bold; margin-bottom: 12px; color: #323233; padding-left: 8px; } .error-message { margin: 20px; padding: 12px 16px; text-align: center; color: #f56c6c; background-color: #fef0f0; border-radius: 8px; display: flex; justify-content: center; align-items: center; font-size: 14px; }未识别到有效数据 .error-message .van-icon { margin-right: 8px; } </style>

最新推荐

recommend-type

三菱FX3U三轴伺服电机与威纶通触摸屏组合程序详解:轴点动、回零与定位控制及全流程解析

三菱FX3U三轴伺服电机与威纶通触摸屏的程序编写方法及其应用。主要内容涵盖伺服电机主控程序、触摸屏程序、轴点动、回零及定位程序、通讯模块程序以及威纶显示器程序的分析。通过对各个模块的深入探讨,帮助读者理解每个部分的功能和实现方式,确保机械运动控制的准确性、高效性和稳定性。此外,文章还提供了关于程序编写过程中可能遇到的问题及解决方案。 适合人群:从事自动化控制领域的工程师和技术人员,尤其是对三菱FX3U三轴伺服电机和威纶通触摸屏有实际操作需求的专业人士。 使用场景及目标:适用于工业自动化项目中,旨在提高对三菱FX3U三轴伺服电机和威纶通触摸屏的理解和应用能力,掌握模块化编程技巧,解决实际工程中的编程难题。 其他说明:文中不仅讲解了各模块的具体实现细节,还强调了程序的安全性和可靠性,为项目的成功实施提供了有力的支持。
recommend-type

职业介绍与人才招聘综合管理系统-基于宏达数据库信息管理开发平台的专业人力资源服务软件-包含基本信息设置-用人单位管理-求职人员登记-数据查询-统计分析-报表生成-打印输出-权限控制.zip

cursor免费次数用完职业介绍与人才招聘综合管理系统_基于宏达数据库信息管理开发平台的专业人力资源服务软件_包含基本信息设置_用人单位管理_求职人员登记_数据查询_统计分析_报表生成_打印输出_权限控制.zip
recommend-type

基于Spark2x分布式计算框架的实时新闻大数据分析可视化系统-实现用户浏览日志采集与实时处理-新闻话题热度排名统计-时段流量峰值分析-新闻曝光量监控-数据可视化展示-采用Kaf.zip

基于Spark2x分布式计算框架的实时新闻大数据分析可视化系统_实现用户浏览日志采集与实时处理_新闻话题热度排名统计_时段流量峰值分析_新闻曝光量监控_数据可视化展示_采用Kaf.zip大数据实战项目
recommend-type

Pansophica开源项目:智能Web搜索代理的探索

Pansophica开源项目是一个相对较新且具有创新性的智能Web搜索代理,它突破了传统搜索引擎的界限,提供了一种全新的交互方式。首先,我们来探讨“智能Web搜索代理”这一概念。智能Web搜索代理是一个软件程序或服务,它可以根据用户的查询自动执行Web搜索,并尝试根据用户的兴趣、历史搜索记录或其他输入来提供个性化的搜索结果。 Pansophica所代表的不仅仅是搜索结果的展示,它还强调了一个交互式的体验,在动态和交互式虚拟现实中呈现搜索结果。这种呈现方式与现有的搜索体验有着根本的不同。目前的搜索引擎,如Google、Bing和Baidu等,多以静态文本和链接列表的形式展示结果。而Pansophica通过提供一个虚拟现实环境,使得搜索者可以“扭转”视角,进行“飞行”探索,以及“弹网”来浏览不同的内容。这种多维度的交互方式使得信息的浏览变得更加快速和直观,有望改变用户与网络信息互动的方式。 接着,我们关注Pansophica的“开源”属性。所谓开源,指的是软件的源代码可以被公众获取,任何个人或组织都可以自由地使用、学习、修改和分发这些代码。开源软件通常由社区进行开发和维护,这样的模式鼓励了协作创新并减少了重复性劳动,因为全世界的开发者都可以贡献自己的力量。Pansophica项目作为开源软件,意味着其他开发者可以访问和使用其源代码,进一步改进和扩展其功能,甚至可以为Pansophica构建新的应用或服务。 最后,文件名称“Pansophica-src-1.3”表明了我们讨论的特定版本的Pansophica开源代码。数字“1.3”很可能指的是该版本号,表明这是Pansophica项目的第1.3个公开版本。这个版本号对于开发者来说很重要,因为它可以帮助他们追踪更新的进度、比较版本间的差异以及选择最适合其项目的版本。 综合以上信息,Pansophica开源项目不仅在搜索代理领域引入了创新的交互方式,而且通过开源的方式,提供了技术共享和协作的可能性。对于IT行业来说,开源项目是推动技术发展的重要力量,它激发了社区的活力,促进了知识的传播和技术的进步。通过构建一个开源社区,Pansophica可以吸引来自世界各地的开发者和研究人员,共同探索如何将搜索技术与交互式虚拟现实技术结合起来,为用户带来更加丰富和有效的信息检索体验。同时,Pansophica的开源性质也使其更容易适应未来技术的发展,随着社区的不断贡献和反馈,这一平台有望在功能和性能上得到持续的优化和增强。
recommend-type

跨平台内容提取无忧:coze工作流应对社交媒体挑战

# 1. 社交媒体内容提取的必要性与挑战 ## 1.1 内容提取的必要性 在数字时代,社交媒体内容是信息与数据流动的主干道,公司、个人和研究者都在寻求从这些信息中提取价值。无论是为了市场趋势分析、品牌监控、消费者洞察还是公共舆论监测,有效的社交媒体内容提取变得至关重要。这不仅有助于企业做出数据驱动的决策,也能使个人更好地理
recommend-type

vrrp主设备发送的免费arp

<think>好的,我现在需要回答用户关于VRRP主设备发送免费ARP的目的和工作原理的问题。首先,我得回忆一下VRRP的基本概念和工作机制。VRRP,虚拟路由冗余协议,主要用于在局域网中提供默认网关的冗余备份,确保网络的高可用性。主设备(Master)负责转发流量,而备用设备(Backup)则在主设备故障时接管工作。 用户的问题集中在主设备发送免费ARP的目的和机制上。根据引用[2],免费ARP用于通知下游设备虚拟MAC地址的变更。当主设备被选举出来后,它需要让局域网内的其他设备知道虚拟IP对应的MAC地址已经指向自己,这样流量才会被正确路由到主设备。免费ARP的作用应该就是更新这些设备的
recommend-type

为Ghost博客平台打造的Meteor流星包装使用指南

从给定文件信息中,我们可以提炼出以下IT知识点: ### 标题知识点:流星Ghost软件包 1. **流星Ghost软件包的用途**:流星Ghost软件包是专为Ghost博客平台设计的流星(Meteor)应用程序。流星是一个开源的全栈JavaScript平台,用于开发高性能和易于编写的Web应用程序。Ghost是一个开源博客平台,它提供了一个简单且专业的写作环境。 2. **软件包的作用**:流星Ghost软件包允许用户在流星平台上轻松集成Ghost博客。这样做的好处是可以利用流星的实时特性以及易于开发和部署的应用程序框架,同时还能享受到Ghost博客系统的便利和美观。 ### 描述知识点:流星Ghost软件包的使用方法 1. **软件包安装方式**:用户可以通过流星的命令行工具添加名为`mrt:ghost`的软件包。`mrt`是流星的一个命令行工具,用于添加、管理以及配置软件包。 2. **初始化Ghost服务器**:描述中提供了如何在服务器启动时运行Ghost的基本代码示例。这段代码使用了JavaScript的Promise异步操作,`ghost().then(function (ghostServer) {...})`这行代码表示当Ghost服务器初始化完成后,会在Promise的回调函数中提供一个Ghost服务器实例。 3. **配置Ghost博客**:在`then`方法中,首先会获取到Ghost服务器的配置对象`config`,用户可以在此处进行自定义设置,例如修改主题、配置等。 4. **启动Ghost服务器**:在配置完成之后,通过调用`ghostServer.start()`来启动Ghost服务,使其能够处理博客相关的请求。 5. **Web浏览器导航**:一旦流星服务器启动并运行,用户便可以通过Web浏览器访问Ghost博客平台。 ### 标签知识点:JavaScript 1. **JavaScript作为流星Ghost软件包的开发语言**:标签指出流星Ghost软件包是使用JavaScript语言开发的。JavaScript是一种在浏览器端广泛使用的脚本语言,它也是流星平台的基础编程语言。 2. **流星和Ghost共同使用的语言**:JavaScript同样也是Ghost博客平台的开发语言。这表明流星Ghost软件包可以无缝集成,因为底层技术栈相同。 ### 压缩包子文件的文件名称列表知识点:meteor-ghost-master 1. **版本控制和软件包结构**:文件名称`meteor-ghost-master`暗示了该软件包可能托管在像GitHub这样的版本控制系统上。文件名中的`master`通常指的是主分支或主版本。 2. **软件包的目录结构**:通过文件名称可以推断出该软件包可能拥有一个标准的流星软件包结构,包含了初始化、配置、运行等必要的模块和文件。 3. **软件包的维护状态**:由于文件名没有包含特定的版本号,我们无法直接得知软件包的最新更新情况。通常,软件包维护者会将最新的版本代码放在`master`分支上。 ### 总结 流星Ghost软件包提供了一个有效的解决方案,使得流星平台的开发者能够在他们的应用中添加Ghost博客功能。软件包的使用简便,通过流星的命令行工具安装,并通过JavaScript代码配置和启动Ghost服务。通过流星Ghost软件包,开发者能够享受流星的实时特性以及Ghost博客系统的便利性。此外,软件包的命名和结构也暗示了其维护和版本控制的模式,有助于开发者更好地理解如何使用和维护这一软件包。
recommend-type

抖音标题生成自动化:用coze工作流释放创意

# 1. 抖音标题生成自动化的重要性 随着社交媒体平台的崛起,内容的吸引力很大程度上取决于标题的创意与精准性。抖音作为一个日活亿级的短视频平台,高质量的标题能够有效提高视频的点击率,增加内容的传播。但是,人工撰写标题不仅耗时耗力,而且很难做到快速响应热点,自动化标题生成工具应运而生。coze工作流,作为一种实现自动化生成抖音标题的工具,其重要性不言而喻。它能够利用大数据分析和机器学习技术,提高标题的吸引
recommend-type

spss消费结构因子分析

<think>好的,我现在需要帮助用户在SPSS中进行消费结构的因子分析。首先,我要回忆一下因子分析的基本步骤和SPSS的操作流程。用户可能对SPSS不太熟悉,所以步骤要详细,同时需要结合他们提供的引用内容,特别是引用[2]中的适用条件和检验方法。 首先,用户提到了消费结构的数据,这可能包括多个变量,如食品、住房、交通等支出。因子分析适用于这种情况,可以降维并找出潜在因子。根据引用[2],需要检查样本量是否足够,变量间是否有相关性,以及KMO和Bartlett检验的结果。 接下来,我需要按照步骤组织回答:数据准备、适用性检验、因子提取、因子旋转、命名解释、计算得分。每个步骤都要简明扼要,说
recommend-type

OpenMediaVault的Docker映像:快速部署与管理指南

根据提供的文件信息,我们将详细讨论与标题和描述中提及的Docker、OpenMediaVault以及如何部署OpenMediaVault的Docker镜像相关的一系列知识点。 首先,Docker是一个开源的应用容器引擎,允许开发者打包应用及其依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。 OpenMediaVault是一个基于Debian的NAS(网络附加存储)解决方案。它专为家庭或小型办公室提供文件共享、网络附加存储以及打印服务。它提供了一个易用的Web界面,通过这个界面用户可以管理服务器配置、网络设置、用户权限、文件服务等。 在描述中提到了一些Docker命令行操作: 1. `git clone`:用于克隆仓库到本地,这里的仓库指的是“docker-images-openmedivault”。 2. `docker build -t omv`:这是一个构建Docker镜像的命令,其中`-t`参数用于标记镜像名称和标签,这里是标记为“omv”。 3. `docker run`:运行一个容器实例,`-t`参数用于分配一个伪终端,`-i`参数用于交互式操作,`-p 80:80`则是将容器的80端口映射到宿主机的80端口。 启动服务的部分涉及OpenMediaVault的配置和初始化: - ssh服务:用于远程登录到服务器的协议。 - php5-fpm:是PHP的一个FastCGI实现,用于加速PHP的运行。 - nginx:是一个高性能的HTTP和反向代理服务器,常用于优化静态内容的分发。 - openmediavault引擎:指的是OpenMediaVault的核心服务。 - rrdcached:用于收集和缓存性能数据,这些数据可以被rrdtool图形化工具读取。 - collectd:是一个守护进程,用于收集系统性能和提供各种存储方式和传输方式来存储所收集的数据。 为了访问服务,需要在浏览器中输入"http:// IP_OF_DOCKER",其中`IP_OF_DOCKER`指的是运行Docker容器的主机IP地址。 描述中还提到了一个步骤:“在System-> Network-> Interfaces中添加带有dhcp的eth0”,这指的是需要在OpenMediaVault的Web管理界面中配置网络接口。`eth0`是网络接口的名称,通常代表第一个以太网接口。DHCP(动态主机配置协议)是一种自动为网络中的设备分配IP地址的协议,这样设备就可以连接网络并开始通信,无需手动配置IP地址。 【压缩包子文件的文件名称列表】中的“docker-images-openmediavault-master”暗示了这是一个包含Docker镜像文件的代码仓库。通常,“master”分支是代码的主分支,包含了代码库中最新且通常是最稳定的版本。用户可以通过克隆该仓库到本地来获取所有相关的Dockerfile、配置脚本及依赖文件,以便能够自行构建和运行OpenMediaVault的Docker镜像。 综上所述,这些知识点涵盖了从基本的Docker概念、Docker命令行操作、OpenMediaVault服务启动和管理,到具体的网络配置及Docker仓库操作,都是进行Docker化OpenMediaVault部署的关键步骤。