// ================ 通用工具函数 ================ /** * 延迟函数（毫秒） * @param {number} ms 毫秒数 */ function delay(ms) { var start = new Date().getTime(); while (new Date().getTime() < start + ms) {} } /** * 将列号转换为字母 * @param {number} column 列号 * @returns {string} 列字母 */ function columnToLetter(column) { var temp, letter = ''; while (column > 0) { temp = (column - 1) % 26; letter = String.fromCharCode(temp + 65) + letter; column = (column - temp - 1) / 26; } return letter; } /** * 验证当前工作表名称 * @param {string|Array} expectedSheetNames 预期的工作表名称或数组 * @returns {boolean} 是否验证通过 */ function validateSheet(expectedSheetNames) { var sheet = Application.ActiveSheet; var sheetName = sheet.Name; // 如果是字符串数组，检查是否在数组中 if (Array.isArray(expectedSheetNames)) { if (!expectedSheetNames.includes(sheetName)) { MsgBox("当前工作表不是【" + expectedSheetNames.join("】或【") + "】表，请切换到正确的工作表！"); return false; } return true; } // 如果是单个字符串 if (sheetName !== expectedSheetNames) { MsgBox("当前工作表不是【" + expectedSheetNames + "】表，请切换到正确的工作表！"); return false; } return true; } /** * 获取所有选中的单元格 * @returns {Array|null} 选中单元格数组或null（无选择） */ function getAllSelectedCells() { var selection = Application.Selection; if (selection.Count === 0) { alert("请选择至少一个单元格"); return null; } var selectedCells = []; var areas = selection.Areas; for (var a = 1; a <= areas.Count; a++) { var area = areas.Item(a); for (var i = 1; i <= area.Count; i++) { selectedCells.push(area.Item(i)); } } return selectedCells; } /** * 显示处理结果消息 * @param {number} successCount 成功数量 * @param {number} failCount 失败数量 * @param {Array} failMessages 失败消息数组 */ function showResultMessage(successCount, failCount, failMessages) { var resultMessage = "处理完成:\n"; resultMessage += "成功: " + successCount + " 个\n"; resultMessage += "失败: " + failCount + " 个"; if (failCount > 0 && failMessages.length > 0) { resultMessage += "\n\n失败详情:\n" + failMessages.join("\n"); } alert(resultMessage); } /** * 在上方搜索最近匹配项 * @param {object} sheet 工作表对象 * @param {string} searchKey 搜索关键字 * @param {number} startRow 起始行 * @param {number} column 搜索列 * @returns {object|null} 找到的单元格或null */ function findClosestMatchAbove(sheet, searchKey, startRow, column) { var foundCell = null; var closestDistance = Infinity; for (var row = startRow - 1; row >= 1; row--) { var cell = sheet.Cells.Item(row, column); if (cell.Text == searchKey) { var distance = startRow - row; if (distance < closestDistance) { closestDistance = distance; foundCell = cell; } } } return foundCell; } /** * 批量处理优化函数 * @param {Function} processingFunc 处理函数 * @param {string} errorMessage 错误消息 */ function batchProcessing(processingFunc, errorMessage) { Application.ScreenUpdating = false; Application.Calculation = -4135; // xlCalculationManual Application.EnableEvents = false; try { processingFunc(); } catch (error) { MsgBox(errorMessage + ": " + error.message); throw error; } finally { Application.ScreenUpdating = true; Application.Calculation = -4105; // xlCalculationAutomatic Application.EnableEvents = true; } } /** * 从日期字符串中提取"日"部分 * @param {string} dateString 日期字符串 * @returns {number} 日期号 */ function getDayFromDate(dateString) { if (!dateString || dateString === "") return 0; // 处理 "2025/8/20" 格式 var parts = dateString.split('/'); if (parts.length >= 3) { return parseInt(parts[2]); } // 处理 "2025-8-20" 格式 parts = dateString.split('-'); if (parts.length >= 3) { return parseInt(parts[2]); } return 0; } /** * 检查值是否为零 * @param {string} value 值 * @returns {boolean} 是否为零 */ function isZeroValue(value) { if (value === "0" || value === "0.0" || value === "0.00") { return true; } var numberValue = parseFloat(value); return numberValue === 0; } /** * 检查单元格是否有内容（非空且不是0） * @param {object} cell 单元格对象 * @returns {boolean} 是否有内容 */ function hasContent(cell) { var text = cell.Text; return text && text !== "" && text !== "0" && text !== "0.0" && text !== "0.00"; } /** * 检查单元格是否为空（空字符串或null） * @param {object} cell 单元格对象 * @returns {boolean} 是否为空 */ function isEmptyCell(cell) { var text = cell.Text; return !text || text === "" || text.trim() === ""; } // ================ 动态工作簿查找功能 ================ /** * 通过通配符名称获取工作簿 * @param {string} pattern 工作簿名称模式（如"三香原物料库存*.xlsx"） * @returns {object} 工作簿对象 */ function getWorkbookByPattern(pattern) { try { // 从模式中提取基础名称 var baseName = pattern.replace("*", "").replace(".xlsx", "").replace(".xls", ""); // 先尝试获取已打开的工作簿 for (var i = 1; i <= Application.Workbooks.Count; i++) { var wb = Application.Workbooks.Item(i); if (wb.Name.indexOf(baseName) === 0 && (wb.Name.indexOf(".xlsx") > -1 || wb.Name.indexOf(".xls") > -1)) { return wb; } } // 如果没有找到已打开的，尝试从默认路径打开 // 获取目录下的文件 var fso = new ActiveXObject("Scripting.FileSystemObject"); var folderPath = "D:\\analysis\\盘点\\"; var folder = fso.GetFolder(folderPath); var files = new Enumerator(folder.Files); for (; !files.atEnd(); files.moveNext()) { var file = files.item(); var fileName = file.Name; if (fileName.indexOf(baseName) === 0 && (fileName.indexOf(".xlsx") > -1 || fileName.indexOf(".xls") > -1)) { var fullPath = folderPath + fileName; return Application.Workbooks.Open(fullPath); } } throw new Error(`无法找到匹配模式 ${pattern} 的工作簿`); } catch (error) { throw new Error(`无法找到或打开工作簿: ${error.message}`); } } // ================ 出入库粘贴功能（合并版） ================ /** * 出入库粘贴处理（通用版） */ function processStockUniversal() { var sheet = Application.ActiveSheet; var sheetName = sheet.Name; if (sheetName === "出库") { processStock("out"); } else if (sheetName === "入库") { processStock("in"); } else { MsgBox("当前工作表不是【出库】或【入库】表，请切换到正确的工作表！"); } } /** * 出入库粘贴处理 * @param {string} stockType 类型："out"出库，"in"入库 */ function processStock(stockType) { var config = { "out": { sheetName: "出库", resultColumn: 7 }, "in": { sheetName: "入库", resultColumn: 10 } }; var cfg = config[stockType]; if (!validateSheet(cfg.sheetName)) return; var sheet = Application.ActiveSheet; var selectedCells = getAllSelectedCells(); if (!selectedCells) return; var successCount = 0; var failCount = 0; var failMessages = []; for (var i = 0; i < selectedCells.length; i++) { var targetCell = selectedCells[i]; var targetRow = targetCell.Row; try { var searchKey = sheet.Cells.Item(targetRow, 2).Text; if (!searchKey) { failCount++; failMessages.push("行" + targetRow + ": B列单元格没有内容"); continue; } var foundCell = findClosestMatchAbove(sheet, searchKey, targetRow, 2); if (foundCell) { var resultCell = sheet.Cells.Item(foundCell.Row, cfg.resultColumn); targetCell.Formula = resultCell.Text; successCount++; } else { failCount++; failMessages.push("行" + targetRow + ": 未在B列上方找到与 '" + searchKey + "' 匹配的单元格"); } } catch (e) { failCount++; failMessages.push("行" + targetRow + ": 处理错误 - " + e.message); } } showResultMessage(successCount, failCount, failMessages); } // ================ 日盘粘贴功能 ================ /** * 日盘应当结余粘贴处理 * @param {string} materialType 类型："原料"或"包装" */ function processDailyStock(materialType) { var config = { "原料": { targetSheet: "原料", targetColumn: 5 }, "包装": { targetSheet: "包装", targetColumn: 6 } }; var cfg = config[materialType]; if (!validateSheet("日盘")) return; var activeSheet = Application.ActiveSheet; var selection = Application.Selection; if (selection.Count === 0) { MsgBox("请至少选中一个单元格"); return; } var materialSheet = null; for (var i = 1; i <= Application.ActiveWorkbook.Sheets.Count; i++) { var sheet = Application.ActiveWorkbook.Sheets.Item(i); if (sheet.Name === cfg.targetSheet) { materialSheet = sheet; break; } } if (!materialSheet) { MsgBox("找不到名为'" + cfg.targetSheet + "'的工作表"); return; } var lastRow = materialSheet.UsedRange.Rows.Count; var successCount = 0; var failCount = 0; for (var i = 1; i <= selection.Count; i++) { var selectedCell = selection.Item(i); var row = selectedCell.Row; var searchValue = activeSheet.Cells(row, 2).Text; var found = false; for (var j = 1; j <= lastRow; j++) { var cellValue = materialSheet.Cells(j, 3).Text; if (cellValue && cellValue.toString() === searchValue.toString()) { materialSheet.Cells(j, cfg.targetColumn).Formula = selectedCell.Text; found = true; successCount++; break; } } if (!found) { failCount++; } } var resultMsg = "操作完成！\n"; resultMsg += "成功匹配并填充: " + successCount + " 个\n"; resultMsg += "未找到匹配项: " + failCount + " 个"; MsgBox(resultMsg); } // ================ 日盘清空单元格功能 ================ /** * 日盘清空单元格 */ function clearDailyCells() { if (!validateSheet("日盘")) return; var sheet = Application.ActiveSheet; var lastRow = sheet.UsedRange.Rows.Count; if (lastRow <= 1) { Console.log("只有一行数据，无需清空"); return; } var columnsToClear = [2, 4, 5, 7, 11]; for (var i = 0; i < columnsToClear.length; i++) { var col = columnsToClear[i]; var range = sheet.Range( sheet.Cells(2, col), sheet.Cells(lastRow, col) ); range.ClearContents(); } MsgBox("B、D、E、G、K列已清空完成（除首行）"); } // ================ 自动填充原物料公式功能 ================ /** * 自动填充原物料公式 */ function autoFillMaterialFormulas() { var sheet = Application.ActiveSheet; var sheetName = sheet.Name; if (sheetName !== "原料" && sheetName !== "包装") { MsgBox("当前工作表不是【原料或包装】表，请切换到正确的工作表！"); return; } // 查找有数据的"领用"列 var resultColumns = []; var headerRow = 2; var lastCol = sheet.UsedRange.Columns.Count; for (var col = 1; col <= lastCol; col++) { var headerValue = sheet.Cells(headerRow, col).Text; if (headerValue === "领用") { var hasData = false; for (var row = 3; row <= sheet.UsedRange.Rows.Count; row++) { if (sheet.Cells(row, col).Text !== "") { hasData = true; break; } } if (hasData) { var colLetter = columnToLetter(col); resultColumns.push(colLetter); } } } // 根据工作表类型配置公式 var formulaConfig = []; if (sheetName === "原料") { formulaConfig = [ { column: "E", formula: "0" }, { column: "F", formula: "=SUMIF($K$2:$BT$2,$K$2,K{row}:BT{row})" }, { column: "G", formula: "=SUMIF($K$2:$BT$2,$L$2,K{row}:BT{row})" }, { column: "H", formula: "=E{row}+F{row}-G{row}" }, { column: "J", formula: "=+IF($H{row}-$I{row}<0,\"不满足\",\"\")" }, ]; } else if (sheetName === "包装") { formulaConfig = [ { column: "F", formula: "0" }, { column: "G", formula: "=SUMIF($L$2:$BU$2,$L$2,L{row}:BU{row})" }, { column: "H", formula: "=SUMIF($L$2:$BU$2,$M$2,L{row}:BU{row})" }, { column: "J", formula: "=F{row}+G{row}-H{row}" }, { column: "K", formula: "=+IF($J{row}-$I{row}<0,\"不满足\",\"\")" }, ]; } // 添加领用列公式 for (var i = 0; i < resultColumns.length; i++) { formulaConfig.push({ column: resultColumns[i], formula: "0" }); } // 填充公式 var lastRow = sheet.UsedRange.Rows.Count; var filledCount = 0; for (var i = 0; i < formulaConfig.length; i++) { var col = formulaConfig[i].column; var formulaTemplate = formulaConfig[i].formula; var colNum = sheet.Range(col + "1").Column; for (var row = 2; row <= lastRow; row++) { var cell = sheet.Cells.Item(row, colNum); if (!cell.Formula || cell.Formula === "") { var finalFormula = formulaTemplate.replace(/\{row\}/g, row); cell.Formula = finalFormula; filledCount++; } } } Application.StatusBar = "已为 " + formulaConfig.length + " 列填充 " + filledCount + " 个公式"; delay(1500); Application.StatusBar = false; } // ================ 日盘自动填充数量功能 ================ /** * 日盘自动填充数量 */ function autoFillDailyQuantity() { if (!validateSheet("日盘")) return; var sheet = Application.ActiveSheet; var lastRow = sheet.Range("C" + sheet.Rows.Count).End(-4162).Row; // xlUp = -4162 // 公式映射表 var formulaRules = { "0": function(row) { return "=D" + row + "*EVALUATE(SUBSTITUTE(SUBSTITUTE(C" + row + ",\"kg\",\"\"),\"L\",\"\"))+E" + row; }, "件箱": function(row) { return "=D" + row + "+IF(E" + row + "=\"\",0,E" + row + ")"; }, "条": function(row) { return "=VALUE(LEFT(C" + row + ",2))*D" + row + "+IF(E" + row + "=\"\",0,E" + row + ")"; }, "卷": function(row) { return "=D" + row + "*EVALUATE(SUBSTITUTE(SUBSTITUTE(C" + row + ",\"卷/箱\",\"\"),\"卷/袋\",\"\"))+IF(E" + row + "=\"\",0,E" + row + ")"; } }; for (var row = 1; row <= lastRow; row++) { var cellF = sheet.Range("F" + row).Text; var cellB = sheet.Range("B" + row).Text; var cellC = sheet.Range("C" + row).Text; var formula = ""; if (cellF === "0") { formula = formulaRules["0"](row); } else if (cellF.includes("件") || cellF.includes("箱")) { formula = formulaRules["件箱"](row); } else if (cellF.includes("条")) { formula = formulaRules["条"](row); } else if (cellF.includes("卷")) { formula = formulaRules["卷"](row); } else if (cellF.includes("套")) { formula = "=D" + row + "*EVALUATE(SUBSTITUTE(C" + row + ",\"套/件\",\"\"))+IF(E" + row + "=\"\",0,E" + row + ")"; } else if (cellF.includes("包")) { formula = "=VALUE(LEFT(C" + row + ",1))*D" + row + "+IF(E" + row + "=\"\",0,E" + row + ")"; } else if (cellF.includes("个") || cellF.includes("只")) { if (cellB.includes("纸箱") || cellB.includes("隔板") || cellC === "0" || cellC.includes("个/捆")) { formula = formulaRules["件箱"](row); } else { formula = "=D" + row + "*EVALUATE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(C" + row + ",\"套/件\",\"\"),\"个/捆\",\"\"),\"卷/袋\",\"\"),\"只/箱\",\"\"),\"个/箱\",\"\"),\"个/包\",\"\"),\"只/包\",\"\"),\"套/箱\",\"\"),\"个/件\",\"\"),\"只/件\",\"\"),\"个/件\",\"\"))+IF(E" + row + "=\"\",0,E" + row + ")"; } } else if (cellF.includes("盒")) { formula = "=D" + row + "*EVALUATE(SUBSTITUTE(C" + row + ",\"盒/箱\",\"\"))+IF(E" + row + "=\"\",0,E" + row + ")"; } if (formula) { sheet.Range("G" + row).Formula = formula; } } MsgBox("公式填充完成！"); } // ================ 出入库自动填充数量功能（合并版） ================ /** * 出入库自动填充数量处理（通用版） */ function autoFillStockQuantityUniversal() { var sheet = Application.ActiveSheet; var sheetName = sheet.Name; if (sheetName === "出库") { autoFillStockQuantity("out"); } else if (sheetName === "入库") { autoFillStockQuantity("in"); } else { MsgBox("当前工作表不是【出库】或【入库】表，请切换到正确的工作表！"); } } /** * 出入库自动填充数量处理 * @param {string} stockType 类型："out"出库，"in"入库 */ function autoFillStockQuantity(stockType) { var config = { "out": { sheetName: "出库", startRow: 19345, searchCol: "G", fillCol: "H", unitCol: "C", qtyCol: "D", specCol: "I", materialUnitCol: "J" }, "in": { sheetName: "入库", startRow: 4947, searchCol: "J", fillCol: "K", unitCol: "D", qtyCol: "E", specCol: "L", materialUnitCol: "M" } }; var cfg = config[stockType]; if (!validateSheet(cfg.sheetName)) return; var sheet = Application.ActiveSheet; var lastRow = sheet.UsedRange.Rows.Count; // 性能优化：批量处理，减少与Excel的交互 Application.ScreenUpdating = false; Application.Calculation = -4135; // xlCalculationManual Application.EnableEvents = false; var processedCount = 0; var skippedCount = 0; var errorCount = 0; var zeroFilledCount = 0; // 记录填充0的数量 try { // 使用单行处理，避免数组操作问题 for (var row = cfg.startRow; row <= lastRow; row++) { try { // 检查A列是否有日期（第一列） var dateCell = sheet.Cells.Item(row, 1); if (isEmptyCell(dateCell)) { // A列没有日期，跳过该行 skippedCount++; continue; } var targetCell = sheet.Range(cfg.fillCol + row); // 检查目标单元格是否已有内容 if (hasContent(targetCell)) { // 已有内容，跳过 skippedCount++; continue; } var searchValue = sheet.Range(cfg.searchCol + row).Text; if (!searchValue) { // 搜索列为空，跳过 continue; } var unitValue = sheet.Range(cfg.unitCol + row).Text; var qtyText = sheet.Range(cfg.qtyCol + row).Text; var specValue = sheet.Range(cfg.specCol + row).Text; var materialUnitValue = sheet.Range(cfg.materialUnitCol + row).Text; var qtyValue = parseFloat(qtyText) || 0; var result = calculateQuantity(unitValue, qtyValue, specValue, searchValue, materialUnitValue, stockType); if (result !== null) { // 使用.Formula属性写入 targetCell.Formula = result.toString(); processedCount++; // 记录填充0的数量 if (result === 0) { zeroFilledCount++; } } else { // 结果为null，根据不同类型处理 if (stockType === "out") { // 出库：填充0（满足需求1） targetCell.Formula = "0"; processedCount++; zeroFilledCount++; } // 入库：不填充，保持原样（满足需求2） } // 每处理1000行显示一次进度 if (row % 1000 === 0) { Application.StatusBar = "正在处理 " + cfg.sheetName + "，已处理: " + row + "/" + lastRow; } } catch (e) { // 单行错误不中断整个处理过程 errorCount++; continue; } } // 填充完成后再显示提示 var msg = cfg.sheetName + " " + cfg.fillCol + "列填充完成！\n"; msg += "成功处理: " + processedCount + " 行\n"; if (stockType === "out") { msg += "其中填充0: " + zeroFilledCount + " 行\n"; } msg += "跳过无日期/已有内容: " + skippedCount + " 行\n"; if (errorCount > 0) { msg += "处理错误: " + errorCount + " 行\n"; } MsgBox(msg); } catch (error) { MsgBox("处理过程中出错: " + error.message); } finally { Application.ScreenUpdating = true; Application.Calculation = -4105; // xlCalculationAutomatic Application.EnableEvents = true; Application.StatusBar = false; } } /** * 计算数量逻辑 * @param {string} unit 单位 * @param {number} qty 数量 * @param {string} spec 规格 * @param {string} searchValue 搜索值 * @param {string} materialUnit 物料单位 * @param {string} stockType 出入库类型 * @returns {number|null} 计算结果 */ function calculateQuantity(unit, qty, spec, searchValue, materialUnit, stockType) { var result = null; // 处理规格包含L或kg的情况 if (spec && (spec.includes("L") || spec.includes("kg"))) { // 处理普通单位 if (["袋","桶","包","盒","瓶","罐","块","个","条","片"].includes(unit)) { var match = spec.match(/\*(\d+\.?\d*)(L|kg)/); if (match) { var quantity = parseFloat(match[1]); result = qty * quantity; } } // 处理"箱"、"件"、"筐"单位 else if (["箱","件","筐"].includes(unit)) { var numbers = spec.match(/\d+\.?\d*/g); if (numbers) { result = qty * numbers.reduce(function(a, b) { return a * parseFloat(b); }, 1); } } // 处理"斤"单位 else if (unit === "斤") { result = qty / 2; } // 处理kg/公斤单位 else if (["kg", "公斤","KG","千克"].includes(unit)) { result = qty; } // 处理"吨"单位（仅入库） else if (stockType === "in" && unit === "吨") { result = qty * 1000; } } // 处理其他规格情况 else { // 简单单位直接取数量 var simpleUnits = ["盒","卷","条","张","个","PS","PCS","只","套","包"]; if (simpleUnits.includes(unit)) { result = qty; } // 处理箱/件单位 else if (["箱","件"].includes(unit)) { if (["件","箱"].includes(materialUnit)) { result = qty; } else if (materialUnit === "条") { var numbers = spec.match(/^\d{2,}/); if (numbers) { result = qty * parseInt(numbers[0].substring(0, 2), 10); } } else if (["个","卷","套","盒"].includes(materialUnit)) { var numbers = spec.match(/\d+\.?\d*/g); if (numbers) { result = qty * numbers.reduce(function(a, b) { return a * parseFloat(b); }, 1); } } } } // 特殊规则（仅出库） if (stockType === "out" && unit === "板") { if (searchValue.includes("好成麦芽糖浆M70型")) result = qty * 8 * 75; else if (searchValue.includes("南华白砂糖（一级）")) result = qty * 20 * 50; else if (searchValue.includes("海藻糖") || searchValue.includes("香飘飘糯米糍粉（糕点预拌粉）1")) { result = qty * 40 * 25; } } return result; } // ================ 出入库自动填充领用功能（合并版） ================ /** * 出入库自动填充领用处理（通用版） */ function autoFillStockUsageUniversal() { var sheet = Application.ActiveSheet; var sheetName = sheet.Name; if (sheetName === "出库") { autoFillStockUsage("out"); } else if (sheetName === "入库") { autoFillStockUsage("in"); } else { MsgBox("当前工作表不是【出库】或【入库】表，请切换到正确的工作表！"); } } /** * 出入库自动填充领用处理 * @param {string} stockType 类型："out"出库，"in"入库 */ function autoFillStockUsage(stockType) { var config = { "out": { sheetName: "出库", searchCol: 7, quantityCol: 8, typeCol: 10, rawOffset: 12, packOffset: 13 }, "in": { sheetName: "入库", searchCol: 10, quantityCol: 11, typeCol: 13, rawOffset: 11, packOffset: 12 } }; batchProcessing(function() { var log = processStockUsage(stockType, config[stockType]); showUsageLog(log, stockType); }, "执行过程中出错"); } /** * 处理出入库领用数据 * @param {string} stockType 类型 * @param {object} config 配置 * @returns {object} 日志对象 */ function processStockUsage(stockType, config) { var log = { successCount: 0, failCount: 0, messages: [], clearedCount: 0 }; var sourceSheet = Application.ActiveSheet; var selectedCell = Application.Selection; if (sourceSheet.Name !== config.sheetName) { log.messages.push("错误: 当前工作表不是【" + config.sheetName + "】表"); return log; } if (!selectedCell) { log.messages.push("错误: 未选择单元格"); return log; } var selectedRow = selectedCell.Row; var fullDateText = sourceSheet.Cells(selectedRow, 1).Text; var dayOfMonth = getDayFromDate(fullDateText); if (dayOfMonth === 0) { log.messages.push("错误: 无法从日期中提取日信息"); return log; } // 使用动态工作簿名称匹配 var destBook; try { destBook = getWorkbookByPattern("三香原物料库存*.xlsx"); } catch (error) { log.messages.push("错误: " + error.message); return log; } if (!destBook) { log.messages.push("错误: 无法找到或打开原物料库存工作簿"); return log; } var rawSheet = destBook.Worksheets.Item("原料"); var packSheet = destBook.Worksheets.Item("包装"); // 计算目标列 var rawTargetCol = config.rawOffset + (dayOfMonth - 1) * 2; var packTargetCol = config.packOffset + (dayOfMonth - 1) * 2; // 收集数据 var lastRowSource = sourceSheet.UsedRange.Rows.Count; var rawDataToSync = []; var packDataToSync = []; for (var i = 1; i <= lastRowSource; i++) { var rowDateText = sourceSheet.Cells(i, 1).Text; if (rowDateText && rowDateText === fullDateText) { var searchValue = sourceSheet.Cells(i, config.searchCol).Text; var typeValue = sourceSheet.Cells(i, config.typeCol).Text; if (searchValue) { var quantityValue = sourceSheet.Cells(i, config.quantityCol).Text; var quantity = parseFloat(quantityValue) || 0; var isTypeColumnZero = isZeroValue(typeValue); if (isTypeColumnZero) { rawDataToSync.push({ row: i, product: searchValue, quantity: quantity }); } else { packDataToSync.push({ row: i, product: searchValue, quantity: quantity }); } } } } // 处理数据 - 修复：对于入库表，也需要清空未匹配的单元格 var rawLog = syncDataToSheetWithClear(rawDataToSync, rawSheet, rawTargetCol, "原料", stockType); var packLog = syncDataToSheetWithClear(packDataToSync, packSheet, packTargetCol, "包装", stockType); // 合并日志 log.successCount = rawLog.successCount + packLog.successCount; log.failCount = rawLog.failCount + packLog.failCount; log.messages = log.messages.concat(rawLog.messages, packLog.messages); log.clearedCount = rawLog.clearedCount + packLog.clearedCount; // 保存工作簿 try { destBook.Save(); } catch (e) { log.messages.push("警告: 保存工作簿时出错 - " + e.message); } return log; } /** * 同步数据到工作表（清空未匹配的单元格） * @param {Array} dataToSync 要同步的数据 * @param {object} targetSheet 目标工作表 * @param {number} targetCol 目标列 * @param {string} sheetType 工作表类型 * @param {string} stockType 出入库类型 * @returns {object} 日志对象 */ function syncDataToSheetWithClear(dataToSync, targetSheet, targetCol, sheetType, stockType) { var log = { successCount: 0, failCount: 0, messages: [], clearedCount: 0 }; // 按产品汇总数量 var productSums = {}; for (var i = 0; i < dataToSync.length; i++) { var item = dataToSync[i]; var productName = item.product; if (productSums[productName]) { productSums[productName] += item.quantity; } else { productSums[productName] = item.quantity; } } // 获取目标工作表产品映射 var lastRowDest = targetSheet.UsedRange.Rows.Count; var productMap = {}; // 从第3行开始扫描 for (var i = 3; i <= lastRowDest; i++) { var productName = targetSheet.Cells(i, 3).Text; if (productName) { productMap[productName] = i; } } // 记录哪些产品有数据需要更新 var productsToUpdate = {}; for (var productName in productSums) { productsToUpdate[productName] = true; } // 清空未匹配到的单元格（填充0）- 修复：入库表也需要清空 for (var i = 3; i <= lastRowDest; i++) { var productName = targetSheet.Cells(i, 3).Text; if (productName && productName !== "" && !productsToUpdate[productName]) { // 检查当前单元格是否有数据（非空且不是0） var currentCell = targetSheet.Cells(i, targetCol); var currentValue = currentCell.Text; // 只有当单元格有数据时才清空（填充0） if (hasContent(currentCell)) { targetSheet.Cells(i, targetCol).Formula = "0"; log.clearedCount++; log.messages.push(sheetType + "表行" + i + " [" + productName + "] 已清空为0"); } } } // 写入新数据 var updatedCount = 0; for (var productName in productSums) { if (productSums.hasOwnProperty(productName)) { if (productMap[productName]) { var rowNum = productMap[productName]; targetSheet.Cells(rowNum, targetCol).Formula = productSums[productName].toString(); updatedCount++; log.successCount++; } else { log.messages.push(sheetType + "表中未找到产品: " + productName); log.failCount++; } } } log.messages.push(sheetType + "数据同步完成! 更新 " + updatedCount + " 个产品"); // 记录清空信息 if (log.clearedCount > 0) { log.messages.push("已清空 " + log.clearedCount + " 个未匹配产品的数据"); } return log; } /** * 显示领用填充日志 * @param {object} log 日志对象 * @param {string} stockType 出入库类型 */ function showUsageLog(log, stockType) { var logMessage = (stockType === "out" ? "出库" : "入库") + "自动填充领用完成！\n\n"; logMessage += "成功处理: " + log.successCount + " 个\n"; logMessage += "失败处理: " + log.failCount + " 个\n"; logMessage += "清空未匹配数据: " + log.clearedCount + " 个\n\n"; if (log.messages.length > 0) { logMessage += "详细信息:\n"; // 只显示前15条详细信息，避免消息过长 var maxMessages = Math.min(log.messages.length, 15); for (var i = 0; i < maxMessages; i++) { logMessage += log.messages[i] + "\n"; } if (log.messages.length > 15) { logMessage += "... 还有 " + (log.messages.length - 15) + " 条信息未显示\n"; } } MsgBox(logMessage); } // ================ 批量修改品名功能 ================ /** * 批量修改品名 */ function batchModifyProductNames() { if (!validateSheet("修改品名")) return; Application.DisplayAlerts = false; Application.ScreenUpdating = false; try { var selectedRange = Application.Selection; if (!selectedRange) { MsgBox("请先选中包含替换规则的单元格区域"); return; } var activeSheet = Application.ActiveSheet; var replaceRules = {}; // 构建替换规则 for (var i = 1; i <= selectedRange.Rows.Count; i++) { var row = selectedRange.Row + i - 1; var oldValue = activeSheet.Cells(row, 1).Text; var newValue = activeSheet.Cells(row, 2).Text; if (oldValue && newValue) { replaceRules[oldValue.trim()] = newValue.trim(); } } if (Object.keys(replaceRules).length === 0) { MsgBox("选中的区域没有有效的替换规则"); return; } // 确认替换规则 var confirmMessage = "请确认以下替换规则：\n\n"; for (var key in replaceRules) { confirmMessage += "将 \"" + key + "\" 替换为 \"" + replaceRules[key] + "\"\n"; } confirmMessage += "\n是否继续执行替换操作？"; if (MsgBox(confirmMessage, 4, "确认替换规则") !== 6) { return; } // 定义需要处理的工作簿配置 var fileConfigs = [ { "file_name": "进销明细-副本.xlsx", "sheets": [ {"name": "入库", "col": "J"}, {"name": "出库", "col": "G"} ] }, { "file_name": "三香原物料库存*.xlsx", // 使用通配符 "sheets": [ {"name": "原料", "col": "C"}, {"name": "包装", "col": "C"} ] }, { "file_name": "原物料名称.xlsx", "sheets": [ {"name": "Sheet1", "col": "A"}, {"name": "Sheet2", "col": "A"} ] } ]; var allResults = []; var processedFiles = 0; var detailedLog = "替换完成详情：\n\n"; // 处理所有工作簿 for (var i = 0; i < fileConfigs.length; i++) { var config = fileConfigs[i]; var workbook; var isWorkbookOpenedByUs = false; // 如果是通配符模式 if (config.file_name.includes("*")) { try { workbook = getWorkbookByPattern(config.file_name); isWorkbookOpenedByUs = true; } catch (e) { continue; } } else { // 精确匹配文件名 workbook = findOpenWorkbook(config.file_name); // 如果工作簿没有打开，尝试自动打开 if (!workbook) { try { workbook = Application.Workbooks.Open(Application.ActiveWorkbook.Path + "\\" + config.file_name); isWorkbookOpenedByUs = true; } catch (e) { continue; } } } if (!workbook) continue; var fileResults = []; var workbookName = workbook.Name; var workbookLog = ""; for (var j = 0; j < config.sheets.length; j++) { var sheetConfig = config.sheets[j]; try { var sheet = getSheetByName(workbook, sheetConfig.name); if (sheet) { var sheetResults = processReplacements(sheet, sheetConfig.col, replaceRules, workbookName); fileResults = fileResults.concat(sheetResults); if (sheetResults.length > 0) { if (!workbookLog) { workbookLog = "工作簿: " + workbookName + "\n"; } workbookLog += " 工作表: " + sheetConfig.name + " - 替换了 " + sheetResults.length + " 个单元格\n"; } } } catch (e) { // 静默处理错误 } } if (fileResults.length > 0) { allResults = allResults.concat(fileResults); processedFiles++; detailedLog += workbookLog + "\n"; try { workbook.Save(); } catch (e) { // 静默处理保存错误 } } // 只有当我们打开的工作簿才需要关闭 if (isWorkbookOpenedByUs) { try { workbook.Close(false); } catch (e) { // 静默处理关闭错误 } } } // 显示最终结果 if (allResults.length > 0) { detailedLog += "总计: 共处理了 " + processedFiles + " 个工作簿，替换了 " + allResults.length + " 个单元格"; MsgBox(detailedLog); } else { MsgBox("没有找到需要替换的内容。"); } } catch (error) { MsgBox("发生错误: " + error.message); } finally { Application.ScreenUpdating = true; Application.DisplayAlerts = true; } } /** * 查找已打开的工作簿 * @param {string} fileName 文件名 * @returns {object|null} 工作簿对象或null */ function findOpenWorkbook(fileName) { try { for (var i = 1; i <= Application.Workbooks.Count; i++) { var workbook = Application.Workbooks.Item(i); if (workbook.Name === fileName) { return workbook; } } return null; } catch (e) { return null; } } /** * 通过名称获取工作表 * @param {object} workbook 工作簿 * @param {string} sheetName 工作表名 * @returns {object|null} 工作表对象或null */ function getSheetByName(workbook, sheetName) { try { return workbook.Worksheets.Item(sheetName); } catch (e) { for (var i = 1; i <= workbook.Worksheets.Count; i++) { var currentSheet = workbook.Worksheets.Item(i); if (currentSheet.Name.replace(/\s+/g, '') === sheetName.replace(/\s+/g, '')) { return currentSheet; } } return null; } } /** * 处理工作表替换 * @param {object} sheet 工作表 * @param {string} column 列字母 * @param {object} replaceRules 替换规则 * @param {string} workbookName 工作簿名称 * @returns {Array} 替换结果数组 */ function processReplacements(sheet, column, replaceRules, workbookName) { var results = []; var usedRange = sheet.UsedRange; if (!usedRange) return results; var lastRow = usedRange.Rows.Count; for (var row = 1; row <= lastRow; row++) { var cellRange = sheet.Range(column + row); var cellText = cellRange.Text; if (cellText && cellText !== "") { var cellValue = cellText.trim(); if (replaceRules.hasOwnProperty(cellValue)) { try { var oldValue = cellText; cellRange.Formula = replaceRules[cellValue]; results.push({ workbookName: workbookName, sheetName: sheet.Name, cellAddress: column + row, oldValue: oldValue, newValue: replaceRules[cellValue] }); } catch (e) { continue; } } } } return results; } // ================ 向下兼容的函数（确保原有按钮仍可工作） ================ /** * 向下兼容：出库粘贴 */ function demo1() { processStock("out"); } /** * 向下兼容：入库粘贴 */ function demo2() { processStock("in"); } /** * 向下兼容：原料应当结余粘贴 */ function demo3() { processDailyStock("原料"); } /** * 向下兼容：物料应当结余粘贴 */ function demo4() { processDailyStock("包装"); } /** * 向下兼容：日盘清空单元格 */ function demo5() { clearDailyCells(); } /** * 向下兼容：自动填充原物料公式 */ function demo6() { autoFillMaterialFormulas(); } /** * 向下兼容：日盘自动填充数量 */ function demo7() { autoFillDailyQuantity(); } /** * 向下兼容：出库自动填充数量 */ function demo8() { autoFillStockQuantity("out"); } /** * 向下兼容：出库自动填充领用 */ function demo9() { autoFillStockUsage("out"); } /** * 向下兼容：批量修改品名 */ function demo10() { batchModifyProductNames(); } /** * 向下兼容：入库自动填充数量 */ function demo11() { autoFillStockQuantity("in"); } /** * 向下兼容：入库自动填充领用 */ function demo12() { autoFillStockUsage("in"); } // ================ 功能区按钮配置 ================ /** * 当工作簿打开时创建功能区按钮 */ function Workbook_Open() { // 功能区1：出入库操作 var bar1 = Application.CommandBars.Add('出入库操作'); bar1.Visible = true; var btn1 = bar1.Controls.Add(1); // msoControlButton btn1.Caption = '出入库粘贴'; btn1.OnAction = 'processStockUniversal'; // 功能区2：日盘操作 var bar2 = Application.CommandBars.Add('日盘操作'); bar2.Visible = true; var btn2 = bar2.Controls.Add(1); btn2.Caption = '原料应当结余粘贴'; btn2.OnAction = 'demo3'; var btn3 = bar2.Controls.Add(1); btn3.Caption = '物料应当结余粘贴'; btn3.OnAction = 'demo4'; // 功能区3：清空与公式 var bar3 = Application.CommandBars.Add('清空与公式'); bar3.Visible = true; var btn4 = bar3.Controls.Add(1); btn4.Caption = '日盘清空单元格'; btn4.OnAction = 'demo5'; var btn5 = bar3.Controls.Add(1); btn5.Caption = '自动填充原物料公式'; btn5.OnAction = 'demo6'; // 功能区4：数量填充 var bar4 = Application.CommandBars.Add('数量填充'); bar4.Visible = true; var btn6 = bar4.Controls.Add(1); btn6.Caption = '日盘自动填充数量'; btn6.OnAction = 'demo7'; var btn7 = bar4.Controls.Add(1); btn7.Caption = '出入库自动填充数量'; btn7.OnAction = 'autoFillStockQuantityUniversal'; // 功能区5：数据同步 var bar5 = Application.CommandBars.Add('数据同步'); bar5.Visible = true; var btn8 = bar5.Controls.Add(1); btn8.Caption = '出入库自动填充领用'; btn8.OnAction = 'autoFillStockUsageUniversal'; // 功能区6：其他功能 var bar6 = Application.CommandBars.Add('其他功能'); bar6.Visible = true; var btn9 = bar6.Controls.Add(1); btn9.Caption = '批量修改品名'; btn9.OnAction = 'demo10'; Console.log("所有功能区按钮已加载完成。"); }
【保姆级教程】利用宝塔面板+Docker搭建一个优秀的密码管理器——Bitwarden
更新 最新搭建方法更加简单，详情请见：https://blog.laoda.de/archives/docker-compose-install-vaultwarden 正文 Bitwarden 是一款开源密码管理器，它会将所有密码加密存储在服务器上，它的工作方式与 LastPass、1Password 或 Dashlane 相同。 官方的版本搭建对服务器要求很高，搭建不容易，GitHub上有人用 Rust 实现了 Bitwarden 服务器，项目叫 vaultwarden，并且提供了 Docker 镜像，这个实现更进一步降低了对机器配置的要求，并且 Docker 镜像体积很小，部署非常方便。这个项目目前在GitHub也有9.8k的star，非常受欢迎。 此外，官方服务器中需要付费订阅的一些功能，在这个实现中是免费的。 这篇文章就利用宝塔面板来docker搭建Bitwarden。 视频 一：简介 项目：https://github.com/dani-garcia/vaultwarden 二：要求 aapanel 6.8.13 （宝塔海外版） 一个解析好的域名 腾讯轻量云香港服务器 （演示系统：Debian 10） 三：部署 aapanel安装 官方地址：https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial Centos : yum install -y wget && wget -O install.sh http://www.aapanel.com/script/install_6.0_en.sh && bash install.sh Ubuntu/Deepin : wget -O install.sh http://www.aapanel.com/script/install-ubuntu_6.0_en.sh && sudo bash install.sh Debian : wget -O install.sh http://www.aapanel.com/script/install-ubuntu_6.0_en.sh && bash install.sh Docker安装 官方地址：https://docs.docker.com/engine/install/debian/ sudo apt-get update sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io sudo docker run hello-world systemctl enable docker #设置docker开机自动启动 systemctl status docker #查看docker状态 正式部署 用Docker来部署，两行命令就够了，命令如下： docker run -d --name bitwardenrs \ --restart unless-stopped \ -e WEBSOCKET_ENABLED=true \ -v /www/wwwroot/demo/:/data/ \ -p 6666:80 \ -p 3012:3012 \ vaultwarden/server:latest 注意：/www/wwwroot/demo/ 请修为自己的路径 安装截图，如下： 四：设置反向代理 上面的设置好之后，我们还需要设置反向代理才可以打开网站，但是在设置之前，我们需要新建一个站点，并且设置好SSL证书。这些还是用宝塔面板来操作。我们需要把http://127.0.0.1:6666设置反向代理，如图： 代码如下： location / { proxy_pass http://127.0.0.1:6666/; rewrite ^/(.*)$ /$1 break; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade-Insecure-Requests 1; proxy_set_header X-Forwarded-Proto https; } Nginx Proxy Manager反代（2023年02月09日添加） 注意域名和IP还有端口改成自己实际使用的。 这边可以全部打勾。 这边直接复制反代的部分到Advanced （这里包含了下面自动同步的部分） 代码如下： location /admin { return 404; } location /notifications/hub { proxy_pass http://127.0.0.1:3012; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location /notifications/hub/negotiate { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://127.0.0.1:6666; } 同样地，http://127.0.0.1:3012以及 http://127.0.0.1:6666注意改成你自己的IP和端口。 五：登录Bitwarden 设置好反向代理之后，我们就可以打开站点，如图： 注意：创建账号，需要在开启了ssl证书的情况下才会成功。 注册账号后，就可以用这个账号来登录Bitwarden了，如图： 六：配置 因为现在的状态是所有人都可以注册这个网站，这个东西只是自己使用，所以我们需要关闭掉注册，使用下面的命令。使用之前可以在宝塔面板中删除掉之前的容器，然后运行以下命令来重新创建容器并开启禁止用户注册的功能。 不必担心，因为指定了 volume 映射，删除容器后不会删除数据。SIGNUPS_ALLOWED=false 代表禁止注册！ 6.1、禁用新用户的注册 docker stop bitwardenrs #停止容器 docker rm -f bitwardenrs #删除容器 docker run -d --name bitwardenrs \ --restart unless-stopped \ -e SIGNUPS_ALLOWED=false \ -e WEBSOCKET_ENABLED=true \ -v /www/wwwroot/demo/:/data/ \ -p 6666:80 \ -p 3012:3012 \ vaultwarden/server:latest 运行完在容器列表里就又可以重新看到了，然后再去试下创建账号就会出现一个不能创建账号的错误提示： 七：设置自动同步 bitwarden 默认是不会自动同步的，不管你是添加或者删除又或是修改了一条记录，都只是先保存在本地，只有当你手动点一下同步时才会进行同步。此时我们可以打开 WebSockets notifications 功能，这样手机修改后会立刻自动同步到云端。所以，我们还需要上面的反向代理。 打开网站配置文件，直接复制过去就可以了。（可以把前面第四步的反向代理那段替换掉） 注意把6666改成你自己的端口： location / { proxy_pass http://127.0.0.1:6666; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } location /notifications/hub { proxy_pass http://127.0.0.1:3012; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location /notifications/hub/negotiate { proxy_pass http://127.0.0.1:6666; } # # Optionally add extra authentication besides the AUTH_TOKEN （除了AUTH_TOKEN之外，可以选择添加额外的认证。） # # If you don't want this, leave this part out 如果你想用的话，可以把这部分注释取消 # location /admin { # # See: https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/ # auth_basic "Private"; # auth_basic_user_file /path/to/htpasswd_file; # proxy_set_header Host $host; # proxy_set_header X-Real-IP $remote_addr; # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_set_header X-Forwarded-Proto $scheme; # proxy_pass http://<SERVER>:6666; # } # 加入robots.txt 防止搜索引擎爬虫抓取（可选） # location = /robots.txt { # root /www/wwwroot/bitwarden; # } 如果你需要robots.txt，可以取消注释（把代码前面的#去掉即可）咕咕这边提供一下robots.txt的内容，/www/wwwroot/bitwarden这个地址取决于你的robots.txt的位置。 User-agent: * Disallow: / 八：下载客户端 登录Bitwarden，在右上角点击头像，然后点击【获取应用】，如图： 我们看到，基本所有都支持了， 用chrome插件来演示一下。如图： 点击小齿轮，输入我们搭建的网站地址： 保存登陆即可。 自动生成密码展示： 小纸条功能展示： 同理，手机端也一样操作即可。 这样我们使用Chrome也可以方便的管理自己的密码了，最重要的是Bitwarden完全开源，完全免费。 十：总结 目前比较流行的密码管理软件有 1Password、LastPass 、KeePass、Enpass以及SafeInCloud等，但是完全免费开源的只有Bitwarden。我们只需要借助Docker就可以很容易搭建一个自己的密码管理平台。 bitwarden优点：全平台，免费、开源，在安卓上体验很好，有多种双重验证，自动填充功能正常，有密码泄露检测，适应大部分 APP，可以自定义字段，可以正则匹配网址，可以自定义图标，会根据网址或 APP 自动获取 ico，可以指纹解锁，中文翻译很好。 咕咕鸽也是才开始使用，对这个工具还不是非常熟悉，欢迎大家在评论区和本鸽分享一下它的其他好玩的功能！ 参考资料 https://www.daniao.org/7213.html https://github.com/dani-garcia/vaultwarden https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial https://docs.docker.com/engine/install/debian/
minio一、使用docker部署minio1、拉取镜像docker pull minio/minio 2、创建目录mkdir -p /home/minio/config mkdir -p /home/minio/data 3、创建Minio容器并运行docker run -p 9002:9002 -p 9001:9001 \ --net=host \ --name minio \ -d --restart=always \ -e "MINIO_ACCESS_KEY=admin" \ -e "MINIO_SECRET_KEY=zhou1409573580" \ -v /home/minio/data:/data \ -v /home/minio/config:/root/.minio \ minio/minio server \ /data --console-address ":9001" -address ":9002"
小袁口算脚本抓包代码import re import time import sys import threading import argparse import adbutils from mitmproxy import http from mitmproxy.tools.main import mitmdump auto_jump = True # 拦截 HTTP 响应，检查 URL 并逐个字段替换 def response(flow: http.HTTPFlow): # 匹配目标 URL url_pattern = re.compile(r"https?://xyks\.yuanfudao\.com/leo-game-pk/(iphone|android)/math/pk/match.+") # 如果 URL 匹配 if url_pattern.match(flow.request.url): # 如果响应类型是 JSON if "application/json" in flow.response.headers.get("Content-Type", ""): # 读取原始响应体 response_text = flow.response.text # 使用正则表达式逐个替换字段 response_text = re.sub(r'"answer":"[^"]+"', '"answer":"1"', response_text) response_text = re.sub(r'"answers":\[[^\]]+\]', '"answers":["1"]', response_text) # 更新修改后的响应体 flow.response.text = response_text threading.Thread(target=answer_write, args=(len(re.findall(r'answers', flow.response.text)),)).start() # 检测提交 if auto_jump and re.compile(r"https?://xyks\.yuanfudao\.com/leo-game-pk/(iphone|android)/math/pk/submit.+").match(flow.request.url): threading.Thread(target=jump_to_next).start() def jump_to_next(): # 结束，自动进下一局 device = adbutils.adb.device() time.sleep(3) command = "input tap 540 1520" device.shell(command) # “开心收下”按钮的坐标 time.sleep(0.3) command = "input tap 780 1820" device.shell(command) # “继续”按钮的坐标 time.sleep(0.3) command = "input tap 510 1700" device.shell(command) # “继续PK”按钮的坐标 def swipe_screen(): xy = [[1310, 540], [1410, 560]] command = f"input swipe {xy[0][1]} {xy[0][0]} {xy[1][1]} {xy[1][0]} 0" device = adbutils.adb.device() device.shell(command) def answer_write(answer): time.sleep(13) for _ in range(answer): swipe_screen() time.sleep(0.01) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Mitmproxy script") parser.add_argument("-P", "--port", type=int, default=8080, help="Port to listen on") parser.add_argument("-H", "--host", type=str, default="0.0.0.0", help="Host to listen on") args = parser.parse_args() sys.argv = ["mitmdump", "-s", __file__, "--listen-host", args.host, "--listen-port", str(args.port)] mitmdump()
Handsome美化美化记录自定义CSS该修改项位于Handsome主题后台-外观-设置外观-开发者设置-自定义CSS​文章顶部内容居中文章顶部内容居中header.bg-light.lter.wrapper-md { text-align: center; } 首页文章标题居中首页文章标题居中.post_title_wrapper { justify-content: center; } 首页文章日期居中首页文章日期居中.text-muted.post-item-foot-icon.text-ellipsis.list-inline { text-align: center; background-color: #fff; } 标签字体颜色标签字体颜色.badge,.padder-v-ssm{ color:#fff; } 赞赏按钮跳动赞赏按钮跳动/*赞赏按钮跳动*/ .btn-pay { animation: star 0.5s ease-in-out infinite alternate; } @keyframes star { from { transform: scale(1); } to { transform: scale(1.1); } } 标题悬停颜色标题悬停颜色.index-post-title a:hover { color: #2ebaae; } 全站页面纯白全站页面使用白色时，需要在后台设置-外观设置-主题色调自定义搭配​修改为white-white-white​全站页面纯白/*白色整体背景*/ div#alllayout { background-color: #fff; } .night div#alllayout { background-color: #1d1f20; } /*左侧-顶部-底部*/ .bg-white{ background-color: #fff; } /*页面底部*/ .bg-light { background-color: #fff; } /*首页标题背景变白*/ .bg-light .lter, .bg-light.lter { background-color: #fff; } /* 右侧背景 */ .bg-white-only{ background-color:#fff; } /*文章页面变白*/ #post-panel { background: white; } /*登录后前台头像下变白*/ li.wrapper.b-b.m-b-sm.bg-light.m-t-n-xs { background-color: #fff; } 局部阴影局部阴影/*博客信息-搜索框-幻灯片 阴影*/ .box-shadow-wrap-normal { box-shadow: 0 0px 4px rgba(0, 0, 0, 0.16); } /*盒子四周阴影*/ .app.container { box-shadow: 0 0 20px rgba(0, 0, 0, 0.16)!important; } /*文章圆角-阴影*/ .panel { box-shadow: 0 0px 2px 2px rgba(0, 0, 0, .13); border-radius: 6px!important; } /*文章标题阴影*/ .bg-white-pure { background: white; box-shadow: 0 0px 2px 2px rgba(0, 0, 0, .13); } 获取焦点放大首页文章图片获取焦点放大/*首页文章图片获取焦点放大*/ .item-thumb { cursor: pointer; transition: all .6s; } .item-thumb:hover { transform: scale(1.05); } 文章悬浮效果文章悬浮效果/* 文章悬浮效果 */ .blog-post .panel:not(article){transition:all .3s} .blog-post .panel:not(article):hover{transform:translateY(-10px)} 滚动条效果滚动条效果/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ ::-webkit-scrollbar { width:9px; height:8px; } /*定义滚动条轨道*/ ::-webkit-scrollbar-track { background-color:white; -webkit-border-radius: 0em; -moz-border-radius: 0em; border-radius: 0em; } /*定义滑块 内阴影+圆角*/ ::-webkit-scrollbar-thumb { background-color: #ff676c; background-image: -webkit-linear-gradient(45deg,rgba(255,255,255,.4) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.4) 50%,rgba(255,255,255,.4) 75%,transparent 75%,transparent); -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em; cursor: pointer; } 自定义JavaScript该修改项位于Handsome主题后台-外观-设置外观-开发者设置-自定义Javascript​左侧彩色图标&右侧彩色标签云左侧彩色图标&右侧彩色标签云需要将以下代码添加到PJAX->PJAX回调函数中​‍//左侧图标多彩 let leftHeader=document.querySelectorAll("span.nav-icon>svg,span.nav-icon>i"); let leftHeaderColorArr=["#ff3300","#ff3399","#54d100","#9900cc","#0033ff","#FF6699","#FF33FF","#ff8100","#33CC00","#FF1493","#8A2BE2","#8B3E2F","#00CC33"]; leftHeader.forEach( tag=>{ tagsColor=leftHeaderColorArr[Math.floor(Math.random()*leftHeaderColorArr.length)]; tag.style.color=tagsColor } ); // 右侧彩色标签云 let tags = document.querySelectorAll("#tag_cloud a,#tag_cloud-post a"); let infos = document.querySelectorAll(".badge"); let colorArr = ["#0089ff", "#00c919", "#ff4747", "#c052ff", "#ff8939","#ff1200","#ff3399","#ffde00","#6000ff"]; tags.forEach(tag => { tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)]; tag.style.backgroundColor = tagsColor; }); infos.forEach(info => { infosColor = colorArr[Math.floor(Math.random() * colorArr.length)]; info.style.backgroundColor = infosColor; }); function addNumber(a) { var length = document.getElementById("comment").value.length; if(length> 0){ document.getElementById("comment").focus() document.getElementById("comment").value += '\n' + a + new Date }else{ document.getElementById("comment").focus() document.getElementById("comment").value += a + new Date } } $(function(){ $("#PageLoading").fadeOut(400); $("#body").css('overflow',''); }); 其他修改修改源文件记得备份哦~博主文字介绍动态化博主文字介绍动态化将代码里的 这是我的介绍​文字内容修改成需要展示的文字内容即可，顺便那个❤也是可以改的。 将以下代码放到 主题后台-外观-设置外观-初级设置-博主的介绍​内即可 放之前记得先清空博主的介绍栏内的所有内容页面-底部页脚美化底部页脚美化修改文件/themes/handsome/component/footer.php​版权代码或者删除 <?php $this->options->BottomInfo();?> <?php $this->options->BottomleftInfo(); ?> 以下代码放在外观设置-开发者设置-自定义css中​‍/*底部页脚css*/ .github-badge { display: inline-block; border-radius: 4px; text-shadow: none; font-size: 12px; color: #fff; line-height: 15px; background-color:#4d4d4d; text-shadow:0px 0.86px 0.2px #3333336b; } .github-badge .badge-subject { display: inline-block; background-color: #4d4d4d; padding: 4px 4px 4px 6px; border-top-left-radius: 4px; border-bottom-left-radius: 4px } .github-badge .badge-value { display: inline-block; padding: 4px 6px 4px 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px } .github-badge .bg-blue { background-color: #00a1fd; } .github-badge .bg-orange { background-color: #ffa500; } .github-badge .bg-green { background-color: #3bca6e; } .github-badge .bg-purple{ background-color:#ab34e9; } .github-badge .bg-red{ background-color:#ff1010; } 以下代码放在外观设置-开发者设置-博客底部左侧信息中​‍ Copyright ©2024 猫不吃鱼 | 京ICP备 2022013581号-1 以下代码放在外观设置-开发者设置-博客底部右侧信息中​‍ 本站由 腾讯云提供服务 | Theme by Handsome 自定义字体自定义字体选择字体，然后去字体转换把 otf、ttf​转换成 woff​格式。将转换成功后的字体上传至网站目录中,默认字体目录是 /usr/themes/handsome/assets/fonts​文件夹里。前往 外观设置-开发者设置-自定义css中​添加以下代码/*自定义字体*/ @font-face{font-family:HarmonyOS_Sans_SC_Medium;font-style:normal;font-display:swap; src:url(填写字体所在的位置) format('woff2')} *{font-family:HarmonyOS_Sans_SC_Medium} body {font- family: HarmonyOS!important;} 自定义右键美化自定义右键美化以下代码放在开发者设置➡自定义输出body尾部的HTML代码​中需要引入JS文件https://lib.baomitu.com/layer/3.1.1/layer.js​,以下代码已经添加，无需再次引入。 首页 复制 搜索 前进 后退 重载网页 和我当邻居 给我留言吧 右侧添加人生倒计时（岁月不待人）右侧添加人生倒计时（岁月不待人）首先找到网站根目录 /usr/themes/handsome/component/sidebar.php​文件，找到 ​这一行内容。 在该内容向上两行找到 <?php endif; ?>​，在该行紧接着下面添加如下代码： <?php _me("岁月不待人") ?> 今日已经过去小时 这周已经过去天 本月已经过去天 今年已经过去个月 改好后保存退出，进入网站根目录 /usr/themes/handsome/assets/js/​的文件夹创建一个名为 timeinfo.js​的文件，并将以下代码放进该文件后，保存并退出‍function init_life_time() { function getAsideLifeTime() { /* 当前时间戳 */ let nowDate = +new Date(); /* 今天开始时间戳 */ let todayStartDate = new Date(new Date().toLocaleDateString()).getTime(); /* 今天已经过去的时间 */ let todayPassHours = (nowDate - todayStartDate) / 1000 / 60 / 60; /* 今天已经过去的时间比 */ let todayPassHoursPercent = (todayPassHours / 24) * 100; $('#dayProgress .title span').html(parseInt(todayPassHours)); $('#dayProgress .progress .progress-inner').css('width', parseInt(todayPassHoursPercent) + '%'); $('#dayProgress .progress .progress-percentage').html(parseInt(todayPassHoursPercent) + '%'); /* 当前周几 */ let weeks = { 0: 7, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6 }; let weekDay = weeks[new Date().getDay()]; let weekDayPassPercent = (weekDay / 7) * 100; $('#weekProgress .title span').html(weekDay); $('#weekProgress .progress .progress-inner').css('width', parseInt(weekDayPassPercent) + '%'); $('#weekProgress .progress .progress-percentage').html(parseInt(weekDayPassPercent) + '%'); let year = new Date().getFullYear(); let date = new Date().getDate(); let month = new Date().getMonth() + 1; let monthAll = new Date(year, month, 0).getDate(); let monthPassPercent = (date / monthAll) * 100; $('#monthProgress .title span').html(date); $('#monthProgress .progress .progress-inner').css('width', parseInt(monthPassPercent) + '%'); $('#monthProgress .progress .progress-percentage').html(parseInt(monthPassPercent) + '%'); let yearPass = (month / 12) * 100; $('#yearProgress .title span').html(month); $('#yearProgress .progress .progress-inner').css('width', parseInt(yearPass) + '%'); $('#yearProgress .progress .progress-percentage').html(parseInt(yearPass) + '%'); } getAsideLifeTime(); setInterval(() => { getAsideLifeTime(); }, 1000); } init_life_time() js文件弄好后，在 设置外观-开发者设置-自定义输出body尾部的HTML代码​处添加以下代码。注意第一行timeinfo.js前面的域名要改成你自己的域名‍ 最后一步，去自定义CSS里面添加以下代码‍/* 时间流逝 */ .sidebar-count .content { padding: 15px } .sidebar-count .content .item { margin-bottom: 15px } .sidebar-count .content .item:last-child { margin-bottom: 0 } .sidebar-count .content .item .title { font-size: 12px; color: var(--minor); margin-bottom: 5px; display: flex; align-items: center } .sidebar-count .content .item .title span { color: var(--theme); font-weight: 500; font-size: 14px; margin: 0 5px } .sidebar-count .content .item .progress { display: flex; align-items: center } .sidebar-count .content .item .progress .progress-bar { height: 10px; border-radius: 5px; overflow: hidden; background: var(--classC); width: 0; min-width: 0; flex: 1; margin-right: 5px } @keyframes progress { 0% { background-position: 0 0 } 100% { background-position: 30px 0 } } .sidebar-count .content .item .progress .progress-bar .progress-inner { width: 0; height: 100%; border-radius: 5px; transition: width 0.35s; -webkit-animation: progress 750ms linear infinite; animation: progress 750ms linear infinite } .sidebar-count .content .item .progress .progress-bar .progress-inner-1 { background: #bde6ff; background-image: linear-gradient(135deg, #50bfff 25%, transparent 25%, transparent 50%, #50bfff 50%, #50bfff 75%, transparent 75%, transparent 100%); background-size: 30px 30px } .sidebar-count .content .item .progress .progress-bar .progress-inner-2 { background: #ffd980; background-image: linear-gradient(135deg, #f7ba2a 25%, transparent 25%, transparent 50%, #f7ba2a 50%, #f7ba2a 75%, transparent 75%, transparent 100%); background-size: 30px 30px } .sidebar-count .content .item .progress .progress-bar .progress-inner-3 { background: #ffa9a9; background-image: linear-gradient(135deg, #ff4949 25%, transparent 25%, transparent 50%, #ff4949 50%, #ff4949 75%, transparent 75%, transparent 100%); background-size: 30px 30px } .sidebar-count .content .item .progress .progress-bar .progress-inner-4 { background: #67c23a; background-image: linear-gradient(135deg, #4f9e28 25%, transparent 25%, transparent 50%, #4f9e28 50%, #4f9e28 75%, transparent 75%, transparent 100%); background-size: 30px 30px } .sidebar-count .content .item .progress .progress-percentage { color: var(--info) } 步骤完成以后就可以去查看效果了~新年灯笼新年灯笼以下代码放在外观设置-开发者设置-自定义css中​‍@media only screen and (max-width: 760px) { .deng-box, .deng-box1 { display:none; } } @media only screen and (min-width: 768px) and (max-width: 1024px) { .right { float: left!important; } } .tc_dl1{ position: relative; left: -13.625px; width:70.75px; } .tc_dl1 img{width: 95px;/* height: 488px;*/justify-content: center;} .tc_dl2{position: relative;left:-13.625px;width:70.75px;} .tc_dl2 img{width: 95px;} .deng-box { position: fixed; top:-3rem; left: -3rem; z-index: 9999; } .deng-box1 { position: fixed; top: -3rem; right: -3rem; z-index: 9999; } .deng-box1 .deng { position: relative; width: 120px; height: 90px; margin: 50px; background: #d8000f; background: rgba(216, 0, 15, 0.8); border-radius: 50% 50%; -webkit-transform-origin: 50% -100px; -webkit-animation: swing 5s infinite ease-in-out; box-shadow: -5px 5px 30px 4px rgba(252, 144, 61, 1); } .deng { position: relative; width: 120px; height: 90px; margin: 50px; background: #d8000f; background: rgba(216, 0, 15, 0.8); border-radius: 50% 50%; -webkit-transform-origin: 50% -100px; -webkit-animation: swing 3s infinite ease-in-out; box-shadow: -5px 5px 50px 4px rgba(250, 108, 0, 1); } .deng-a { width: 100px; height: 90px; background: #d8000f; background: rgba(216, 0, 15, 0.1); margin: 12px 8px 8px 10px; border-radius: 50% 50%; border: 2px solid #dc8f03; } .deng-b { width: 45px; height: 154px; background: #d8000f; background: rgba(216, 0, 15, 0.1); margin: -4px 8px 8px 26px; border-radius: 50% 50%; border: 2px solid #dc8f03; } .xian { position: absolute; top: -20px; left: 60px; width: 2px; height: 20px; background: #dc8f03; } .shui-a { position: relative; width: 5px; height: 20px; margin: -5px 0 0 59px; -webkit-animation: swing 4s infinite ease-in-out; -webkit-transform-origin: 50% -45px; background: #ffa500; border-radius: 0 0 5px 5px; } .shui-b { position: absolute; top: 22px; left: -2px; width: 10px; height: 10px; background: #dc8f03; border-radius: 50%; } .shui-c { position: absolute; top: 28px; left: -2px; width: 10px; height: 35px; background: #ffa500; border-radius: 0 0 0 5px; } .deng:before { position: absolute; top: -7px; left: 29px; height: 12px; width: 60px; content: " "; display: block; z-index: 999; border-radius: 5px 5px 0 0; border: solid 1px #dc8f03; background: #ffa500; background: linear-gradient(to right, #dc8f03, #ffa500, #dc8f03, #ffa500, #dc8f03); } .deng:after { position: absolute; bottom: -7px; left: 10px; height: 12px; width: 60px; content: " "; display: block; margin-left: 20px; border-radius: 0 0 5px 5px; border: solid 1px #dc8f03; background: #ffa500; background: linear-gradient(to right, #dc8f03, #ffa500, #dc8f03, #ffa500, #dc8f03); } .deng-t { font-family: "楷体"; font-size: 26px; color: #f9b842; font-weight: bold; line-height: 37px; text-align: center; padding-top:10px; } .night .deng-t, .night .deng-box, .night .deng-box1 { background: transparent !important; } @-moz-keyframes swing { 0% { -moz-transform: rotate(-10deg); } 50% { -moz-transform: rotate(10deg); } 100% { -moz-transform: rotate(-10deg); } } @-webkit-keyframes swing { 0% { box-shadow: -5px 5px 50px 4px gold; -webkit-transform: rotate(-10deg); } 50% { box-shadow: -5px 5px 50px 4px rgba(250, 108, 0, 1); -webkit-transform: rotate(10deg); } 100% { box-shadow: -5px 5px 50px 4px gold; -webkit-transform: rotate(-10deg); } } #envelope{width:35px;margin-bottom:25px;} 以下代码放在开发者设置➡自定义输出body尾部的HTML代码​中img标签中的所有src均可更换‍ 新春 快乐 插件推荐鼠标美化插件HoerMouse鼠标美化插件HoerMouse插件下载：食用方法：* 下载插件，解压到usr/plugins/​目录中 修改文件夹名为HoerMouse 进入后台设置激活插件 UserAgent插件UserAgent插件插件下载：食用方法：操作前请做好相应备份工作，此插件只针对于Handsome​主题 下载插件，解压到usr/plugins​目录中 修改文件夹名为UserAgent 进入后台设置激活插件 修改Handsome​主题component​目录下comments.php​，代码大概在99~100​行左右。 ‍<?php $stars = $comments->stars; echo ($stars) ? $stars : ""; ?> 添加代码<?php UserAgent_Plugin::get_useragent($comments->agent,$comments->ip); ?> ​，修改后代码如下：‍<?php $stars = $comments->stars; echo ($stars) ? $stars : ""; ?> <?php UserAgent_Plugin::get_useragent($comments->agent,$comments->ip); ?> 原文链接:点击此处跳转邮箱评论通知插件CommentToMail邮箱评论通知插件CommentToMail插件下载：食用方法：* 下载插件，解压到usr/plugins/​目录中 修改文件夹名为CommentToMail 进入后台设置激活插件 看板娘插件PioModify看板娘插件PioModify插件下载：食用方法：* 下载插件，解压到usr/plugins/​目录中 修改文件夹名为PioModify 进入后台设置激活插件 网站优化Typecho开启Gzip加速Typecho开启Gzip加速找到Typecho安装目录中的config.inc.php​文件，添加一行代码即可。/* 开启gzip压缩 */ ob_start('ob_gzhandler'); 设置gravatar国内源设置gravatar国内源找到Typecho安装目录中的config.inc.php​文件，添加一行代码即可更换为国内源define('__TYPECHO_GRAVATAR_PREFIX__', 'https://cdn.v2ex.com/gravatar/'); ​Handsome​主题可以直接在外观设置-速度优化-Gravatar镜像源地址​中填入https://cdn.v2ex.com/gravatar/​国内镜像源：V2EX：https://cdn.v2ex.com/gravatar/极客族：https://sdn.geekzu.org/avatar/loli：https://gravatar.loli.net/avatar/
解决unicode编码中文乱码问题 import re content = "\\u002F哈哈" content = re.sub(r'(\\u[\s\S]{4})',lambda x:x.group(1).encode("utf-8").decode("unicode-escape"),content) ==> /哈哈 代码 def processing_content(self, content): """处理 正文 unicode 问题""" content = str(content).replace('\\\"', '"').replace('\\n', '').replace('\\r', '').replace('\\t', '') content = re.sub(r'(\\u[\s\S]{4})', lambda x: x.group(1).encode("utf-8").decode("unicode-escape"), content) return content ‍
ddddocr库使用#import ddddocr def get_code(url): """ 获取验证码 """ yzm = request.get(url).content ocr = ddddocr.DdddOcr(show_ad = False) res = ocr.classification(yzm) return res 识别base64图片def get_img_code(base64_data): image_data = base64.b64decode(base64_data) with open('1.png', 'wb') as f: f.write(image_data) ocr = ddddocr.DdddOcr(show_ad=False) img_code = ocr.classification(image_data) img_code = get_code_result(img_code) logger.info('验证码：' + str(img_code)) return img_code