记录自己在云主机、轻量服务器、云电脑等云产品；Pyhton、JavaScript 等编程语言的动手实操经历和心得。
🌟 小故事：深夜的文件噩梦那是一个月黑风高的夜晚，我正在造一个“装备图鉴”的web网页。装备有很多图标文件，我知道这些图标的路径，如果一个个搜索查找复制……"天哪！有300多个文件需要从不同路径的文件夹复制出来..."估计要熬到天亮，而且还容易出错。"如果有个工具能自动根据文件路径，帮我找到这些文件并复制就好了..."于是，用测试阶段的CodeBuddy IDE生成了这么个工具。🚀 工具诞生记：多模式文件处理工具💡 灵感来源在数字化时代，我们每天都在与大量文件打交道。无论是：📊 项目迁移时需要批量复制特定文件🗂️ 整理硬盘时需要移动散落各处的文档🧹 清理系统时需要删除临时文件📋 根据清单批量处理文件传统的手动操作不仅效率低下，还容易出错。于是，多模式文件处理工具应运而生！🎯 核心功能这个工具就像一个智能的文件管家，具备三大核心能力：1. 🔍 智能搜索递归搜索：在指定根目录及所有子目录中寻找目标文件模糊匹配：即使路径不完全匹配，也能通过文件名找到目标实时反馈：搜索过程中实时显示找到/未找到的文件状态2. 🎛️ 三种操作模式复制模式：安全地复制文件到目标位置，保留原文件剪切模式：移动文件到新位置，原位置文件被删除删除模式：安全删除文件（支持回收站恢复）3. 📋 多格式支持TXT文件：每行一个文件路径，简单直接Excel文件：读取第一列的文件路径，支持复杂表格🖥️ 界面设计哲学简洁而不简单┌─────────────────────────────────────────┐ │ 🔧 多模式文件处理工具 v3.0 │ │ 作者：DarkSheep | 主页：肥羊小站 │ ├─────────────────────────────────────────┤ │ 📁 根目录：[选择文件夹] │ │ 📂 输出目录：[选择文件夹] │ │ 📄 文件列表：[选择文件] │ │ │ │ 🎛️ 操作模式：○复制 ○剪切 ○删除 │ │ │ │ [📋 加载列表] [▶️ 开始处理] │ ├─────────────────────────────────────────┤ │ 📊 处理状态： │ │ ✅ 成功：0 ❌ 失败：0 📁 总计：0 │ │ │ │ 📝 详细日志： │ │ [实时显示处理过程...] │ └─────────────────────────────────────────┘人性化交互拖拽支持：直接拖拽文件夹和文件到界面进度显示：实时显示处理进度和状态错误处理：友好的错误提示和解决建议一键导出：失败列表可导出，便于后续处理🔧 技术架构核心技术栈# GUI框架 tkinter # 跨平台图形界面 tkinter.ttk # 现代化控件样式 # 文件处理 shutil # 高效文件操作 pathlib # 现代路径处理 send2trash # 安全删除（支持回收站） # 数据处理 pandas # Excel文件读取 openpyxl # Excel格式支持 # 多线程 threading # 避免界面卡顿 queue # 线程间通信智能搜索算法def smart_search(root_dir, target_path): """智能文件搜索算法""" # 1. 直接路径匹配 if os.path.exists(os.path.join(root_dir, target_path)): return direct_match_path # 2. 文件名模糊搜索 filename = os.path.basename(target_path) for root, dirs, files in os.walk(root_dir): if filename in files: return fuzzy_match_path # 3. 未找到 return None🎨 界面美化细节现代化设计元素渐变色彩：温和的蓝绿色调，护眼舒适图标语言：直观的emoji图标，降低学习成本状态反馈：不同颜色表示不同状态（绿色=成功，红色=失败）响应式布局：窗口大小自适应，支持最小化/最大化用户体验优化# 防止界面卡顿 def process_files_threaded(self): """在后台线程处理文件，保持界面响应""" thread = threading.Thread(target=self.process_files_worker) thread.daemon = True thread.start() # 实时状态更新 def update_status(self, message, status_type="info"): """实时更新处理状态到界面""" self.status_queue.put((message, status_type)) self.root.after(100, self.check_status_queue)🌟 实际应用场景场景1：项目迁移背景：公司服务器升级，需要迁移3000+个项目文件 解决方案：将文件清单导出为Excel，使用工具批量复制 效果：原本需要2天的工作，30分钟完成场景2：硬盘整理背景：多年积累的文档散落在各个文件夹 解决方案：整理出需要归档的文件列表，批量移动到统一目录 效果：硬盘空间释放50GB，文件结构更清晰场景3：系统清理背景：需要删除大量临时文件和缓存 解决方案：生成清理清单，使用删除模式安全清理 效果：系统运行速度提升30%，支持回收站恢复📥 下载体验获取方式💾 直接下载：(抱歉，隐藏内容评论后可见)系统要求操作系统：Windows 7/8/10/11内存要求：最低512MB磁盘空间：50MB可用空间其他要求：无需安装Python环境快速开始下载exe文件到任意位置双击运行，无需安装选择目录和文件列表一键开始处理
第一次使用通义千问3-Coder-Plus生成贪食蛇游戏的学思笔记
什么是通义千问3-Coder-PlusQwen3-Coder-Plus 是阿里巴巴通义实验室推出的开源代码大模型，属于 Qwen3-Coder 系列的最强版本，专为高效编程和智能体任务设计。一、Qwen3-Coder-Plus 的核心特性架构与性能混合专家（MoE）架构：总参数 4800 亿，推理时仅激活 350 亿参数，兼顾性能与效率。长上下文支持：原生支持 256K token，通过 YaRN 技术可扩展至 1M token，适合处理大型代码库。多任务强化学习：通过执行驱动的代码强化学习（Code RL）和长时序交互（Agent RL），在 SWE-Bench 等复杂任务中达到开源模型 SOTA。代码能力表现生成与调试：支持从基础代码生成到复杂项目构建（如品牌官网生成仅需 5 分钟），代码测试和修复效率提升至分钟级。智能体编程（Agentic Coding）：可自主规划多步骤任务，调用浏览器、API 等工具完成复杂开发流程。多语言与工具集成：支持 Python、JavaScript 等主流语言，并与 VS Code、Jupyter 等开发工具无缝集成。二、API 申请与配置流程1. 获取 API 密钥步骤：访问 https://bailian.console.aliyun.com/，注册并完成实名认证。在模型市场中找到 Qwen3-Coder，创建 API 密钥并保存。免费额度：新用户可领取 100 万 token 的免费调用额度。2. 在 Roo Code 上配置 APIRoo Code 作为 AI 编程工具，需通过以下步骤接入 Qwen3-Coder：安装 Roo Code：从官网或插件市场下载安装（支持 VS Code 扩展）。配置 API 参数：打开 Roo Code 设置，选择 OpenAI 兼容模式。填写以下信息：API Key：从阿里云百炼获取的密钥。Base URL：https://dashscope.aliyuncs.com/compatible-mode/v1Model Name：qwen3-coder-plus。验证配置：输入测试提示词（如生成一个网页爬虫代码），观察返回结果是否正常。使用 web 技术栈生成一个贪吃蛇游戏我：使用 web 技术栈生成一个贪吃蛇游戏Roo Code会给出一个Todo List updated：创建项目结构实现游戏逻辑添加样式测试游戏完善游戏功能然后按照这个顺序依次创建文件，直至测试游戏。总结对于这个简单的任务，它一次就生成成功了。据说Qwen3-Coder-Plus适合需要处理复杂代码逻辑或长时序任务的场景。有机会我们让它接手一个复杂项目试试。大家觉得以下代码质量如何？入口文件index.html 贪吃蛇游戏 贪吃蛇游戏 样式文件style.cssbody { font-family: Arial, sans-serif; text-align: center; background-color: #f0f0f0; margin: 0; padding: 0; } h1 { background-color: #333; color: white; padding: 1em 0; margin: 0; } .game-container { margin: 20px auto; width: 400px; height: 400px; background-color: #fff; border: 2px solid #333; display: flex; justify-content: center; align-items: center; } canvas { border: 1px solid #000; }游戏逻辑文件script.js// 游戏配置 const config = { canvasSize: 400, gridSize: 20, initialSpeed: 150, foodColor: '#ff0000', snakeColor: '#00ff00', snakeBorderColor: '#006600' }; // 获取画布上下文 const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // 蛇的表示 let snake = [{ x: 100, y: 100 }]; // 食物的位置 let food = { x: 200, y: 200 }; // 游戏状态 let score = 0; let dx = config.gridSize; let dy = 0; let speed = config.initialSpeed; let gameInterval; // 键盘控制 document.addEventListener('keydown', changeDirection); // 开始游戏 function startGame() { gameInterval = setInterval(gameLoop, speed); } // 游戏循环 function gameLoop() { // 移动蛇 const head = { x: snake[0].x + dx, y: snake[0].y + dy }; // 插入新头到蛇的前面 snake.unshift(head); // 检查是否吃到食物 if (head.x === food.x && head.y === food.y) { // 增加分数 score++; // 生成新食物 placeFood(); } else { // 移除蛇尾 snake.pop(); } // 检查碰撞 if (checkCollision()) { // 结束游戏 endGame(); return; } // 重绘游戏 drawGame(); } // 绘制游戏 function drawGame() { // 清除画布 ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvas.width, canvas.height); // 绘制分数 ctx.fillStyle = '#000'; ctx.font = '16px Arial'; ctx.fillText('分数: ' + score, 10, 20); // 绘制蛇 for (let i = 0; i < snake.length; i++) { ctx.fillStyle = config.snakeColor; ctx.strokeStyle = config.snakeBorderColor; ctx.fillRect(snake[i].x, snake[i].y, config.gridSize, config.gridSize); ctx.strokeRect(snake[i].x, snake[i].y, config.gridSize, config.gridSize); } // 绘制食物 ctx.fillStyle = config.foodColor; ctx.fillRect(food.x, food.y, config.gridSize, config.gridSize); } // 放置食物 function placeFood() { // 计算随机位置 const newX = Math.floor(Math.random() * (config.canvasSize / config.gridSize)) * config.gridSize; const newY = Math.floor(Math.random() * (config.canvasSize / config.gridSize)) * config.gridSize; // 检查是否与蛇重叠 for (let i = 0; i < snake.length; i++) { if (newX === snake[i].x && newY === snake[i].y) { // 重试 placeFood(); return; } } // 设置食物位置 food.x = newX; food.y = newY; } // 改变方向 function changeDirection(event) { const key = event.key; // 避免反向移动 if (key === 'ArrowLeft' && dx === 0) { dx = -config.gridSize; dy = 0; } else if (key === 'ArrowRight' && dx === 0) { dx = config.gridSize; dy = 0; } else if (key === 'ArrowUp' && dy === 0) { dx = 0; dy = -config.gridSize; } else if (key === 'ArrowDown' && dy === 0) { dx = 0; dy = config.gridSize; } } // 检查碰撞 function checkCollision() { // 检查墙碰撞 if (snake[0].x < 0 || snake[0].x >= config.canvasSize || snake[0].y < 0 || snake[0].y >= config.canvasSize) { return true; } // 检查自身碰撞 for (let i = 1; i < snake.length; i++) { if (snake[0].x === snake[i].x && snake[0].y === snake[i].y) { return true; } } return false; } // 结束游戏 function endGame() { // 停止游戏循环 clearInterval(gameInterval); // 显示游戏结束 ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; ctx.fillRect(0, 0, canvas.width, canvas.height, 50); ctx.fillStyle = '#fff'; ctx.font = '30px Arial'; ctx.fillText('游戏结束', 90, 200); ctx.font = '20px Arial'; ctx.fillText('分数: ' + score, 155, 240); ctx.font = '16px Arial'; ctx.fillText('按空格键重新开始', 130, 270); // 重置游戏 document.addEventListener('keydown', restartGame); } // 重新开始游戏 function restartGame(event) { if (event.key === ' ') { // 重置蛇 snake = [{ x: 100, y: 100 }]; // 重置食物 food = { x: 200, y: 200 }; // 重置分数 score = 0; // 重置方向 dx = config.gridSize; dy = 0; // 移除事件监听器 document.removeEventListener('keydown', restartGame); // 开始新游戏 startGame(); } } // 初始化游戏 startGame();
第一次使用Git删除本地或远程仓库分支、屏蔽node_modules的学习笔记
一、前言之前建立的一个名为paid的远程仓库分支，现在不想要了，所以我学习了下Git删除本地和远程仓库的分支。这个过程虽然有些曲折，但最终还是成功了。下面是我记录的学习笔记，希望能帮助到同样在学习Git的小伙伴们🤗。二、删除本地分支1. 查看所有分支首先，我需要查看当前所有的本地分支，以便找到需要删除的分支。使用以下命令：git branch2. 切换到其他分支在删除分支之前，我需要确保当前不在要删除的分支上。如果我在要删除的分支上，需要先切换到其他分支。例如，切换到main分支：git checkout main3. 删除本地分支使用以下命令删除本地分支，例如删除名为paid的分支：git branch -d paid如果分支没有被合并，可以使用-D选项强制删除：git branch -D paid三、删除远程分支1. 查看远程分支首先，我需要查看远程仓库的所有分支，以便找到需要删除的分支。使用以下命令：git fetch origin git branch -r2. 删除远程分支使用以下命令删除远程分支，例如删除名为paid的分支：git push origin --delete paid 或 git push origin -d paid五、新建Git屏蔽Python依赖文件夹在使用Git管理Python项目时，通常会遇到需要屏蔽（忽略）后端服务器的依赖文件夹的情况，比如venv、__pycache__、*.egg-info、node_modules等。这些文件夹通常包含虚拟环境、编译后的字节码文件或打包信息、依赖文件（文件多占用空间大），不需要纳入版本控制。1. 创建或编辑.gitignore文件在项目根目录下创建或编辑.gitignore文件，这是Git用来指定哪些文件或文件夹需要被忽略的配置文件。2. 添加需要屏蔽的文件夹和文件在.gitignore文件中添加以下内容，以屏蔽常见的Python依赖文件夹和文件：# Python虚拟环境 venv/ env/ # Python编译文件 __pycache__/ *.py[cod] *$py.class # Python打包信息 *.egg-info/ dist/ build/ *.egg # Node.js依赖文件夹 node_modules/ # 其他常见忽略项 *.log *.tmp3. 提交.gitignore文件将.gitignore文件添加到Git仓库并提交：git add .gitignore git commit -m "添加.gitignore文件，屏蔽Python依赖文件夹"4. 检查忽略效果可以使用以下命令检查.gitignore文件的忽略效果：git status如果配置正确，venv、__pycache__等文件夹将不会出现在未提交的更改列表中。六、更新现有.gitignore文件以屏蔽node_modules文件夹 🛠️如果你的项目已经存在.gitignore文件，只需按以下步骤操作即可屏蔽node_modules文件夹：1. 打开.gitignore文件使用任意文本编辑器打开项目根目录下的.gitignore文件。2. 添加node_modules屏蔽规则在.gitignore文件中添加以下内容（可添加到文件末尾）：# Node.js依赖文件夹 node_modules/3. 保存文件保存对.gitignore文件的修改。4. 检查是否生效运行以下命令检查node_modules是否已被忽略：git status如果配置正确，node_modules文件夹将不会出现在未提交的更改列表中。注意事项：如果node_modules已经被提交到远程仓库，需要先从Git中删除它（即使.gitignore已设置）：git rm -r --cached node_modules git commit -m "从Git中移除node_modules" git push origin <branch-name>之后node_modules会被.gitignore规则屏蔽，不再被跟踪。完整示例.gitignore内容（含Python和Node.js屏蔽规则）：# Python虚拟环境 venv/ env/ # Python编译文件 __pycache__/ *.py[cod] *$py.class # Python打包信息 *.egg-info/ dist/ build/ *.egg # Node.js依赖文件夹 node_modules/ # 其他常见忽略项 *.log *.tmp这样就能同时屏蔽Python和Node.js的依赖文件夹啦！(๑•̀ㅂ•́)و✧
第一次使用CodeBuddy Craft智能体生成贪食蛇游戏的学思笔记
不得不感慨，国内批量代码生成与修改的AI编程智能体如雨后春笋般出现！什么是CodeBuddy Craft腾讯云代码助手是由腾讯云自研的一款开发编程提效辅助工具。2025 年 4 月重磅发布了全新软件开发智能体 Craft。输入自然语言指令，支持文件多选，AI 深度理解，自主完成多文件代码生成和改写，即刻落地可执行应用。最近一次版本更新是3.1.0 - 3.1.8，Craft 功能支持 MCP 扩展能力，支持在 MCP 市场中主流 MCP 服务一键安装、自定义配置 MCP。支持对本地文件改写、本地运行的操作的确认动作。优化 Craft 接受和拒绝交互。怎么安装CodeBuddy Craft智能体请直接访问官网https://copilot.tencent.com/setup/vscode/使用CodeBuddy Craft智能体生成贪食蛇游戏我们继续用“请使用web栈技术生成一个贪食蛇游戏”作为命题作文，看看Craft表现如何。CodeBuddy：我需要创建一个贪食蛇游戏，使用web技术栈（HTML, CSS, JavaScript）。这是一个经典的游戏，玩家控制一条蛇在屏幕上移动，吃食物并不断变长，同时避免撞到墙壁或自己的身体。首先，我需要评估这个任务的复杂度：文件数量：这个任务只需要创建3个基本文件（HTML, CSS, JavaScript）功能复杂度：贪食蛇是一个相对简单的游戏，核心逻辑可以在一个JS文件中实现预计工具调用次数：大约3-5次调用即可完成哎哟，不错哟，Craft首先是评估了任务难度。但是不知道为什么这里它中断了，没有一口气完成任务。所以我不得不再和它对话一次。我：请为我继续创建CodeBuddy ：好的，让我们开始创建贪食蛇游戏。我会按照以下步骤进行：首先创建基本的HTML文件，设置游戏画布然后创建CSS文件，设置基本样式最后创建JavaScript文件，实现游戏逻辑让我们先创建HTML文件。(具体代码我就不展示了，自己动手更有趣)很好，HTML文件已经创建成功。现在让我们创建CSS文件，为游戏添加基本样式。我们需要：设置游戏容器的布局美化画布和按钮的样式确保游戏在页面中居中显示CSS文件已经创建成功。现在让我们创建JavaScript文件，实现贪食蛇游戏的核心逻辑。我们需要实现以下功能：初始化游戏画布和上下文创建蛇和食物对象实现蛇的移动逻辑处理键盘输入控制蛇的方向检测碰撞（与墙壁、自身）实现食物生成和吃食物的逻辑更新分数游戏开始、结束和重置功能太好了，我已经成功创建了贪食蛇游戏的所有必要文件：snake.html - 包含游戏的基本HTML结构，引入了CSS和JavaScript文件snake.css - 设置了游戏的样式，包括画布、按钮和分数显示的样式snake.js - 实现了游戏的核心逻辑，包括：蛇的移动和控制食物的生成和吃食物的逻辑碰撞检测分数计算游戏开始、结束和重置功能现在游戏已经完成，我可以使用attempt_completion工具来总结我的工作并提供如何运行游戏的说明。贪食蛇游戏已创建完成我已经使用Web技术栈（HTML, CSS, JavaScript）成功创建了一个完整的贪食蛇游戏。游戏功能使用方向键（↑ ↓ ← →）控制蛇的移动吃到食物后蛇会变长，并增加分数每得100分游戏速度会增加撞到墙壁或自身会导致游戏结束可以通过"开始游戏"按钮开始或重新开始游戏文件结构snake.html - 游戏的HTML结构snake.css - 游戏的样式表snake.js - 游戏的核心逻辑实现如何运行游戏只需在浏览器中打开snake.html文件即可开始游戏。点击"开始游戏"按钮，然后使用键盘方向键控制蛇的移动。游戏截图游戏界面包含一个400x400像素的游戏画布，显示蛇（绿色）和食物（红色），以及底部的分数显示和控制按钮。扩展可能性如果您想进一步增强这个游戏，可以考虑：添加不同难度级别实现移动设备触摸控制添加音效实现高分排行榜添加障碍物希望您喜欢这个贪食蛇游戏！哎哟不错哟，一次就成功了！还给出了扩展性意见。那我们就继续让它完成一个强版的贪食蛇游戏。增强版的贪食蛇游戏我：1、添加不同难度级别；2、实现高分排行榜；3、随着难度添加障碍物这里Craft又分析了一通。但是不知道又中断了，原因不明。CodeBuddy：我需要对贪食蛇游戏进行以下三个增强：添加不同难度级别实现高分排行榜随着难度添加障碍物让我先查看现有的文件，以便了解当前的代码结构，然后进行修改。首先需要修改HTML文件，添加难度选择和高分排行榜的界面元素。我们需要：添加难度选择下拉框添加高分排行榜显示区域调整游戏界面布局以适应新增内容（相关代码我放在文章最后）现在需要更新CSS文件以适应新的布局和元素样式，包括：调整整体布局以容纳排行榜添加难度选择器的样式设计排行榜的样式添加游戏结束模态框的样式现在我需要更新JavaScript文件，实现以下功能：添加不同难度级别的逻辑（调整速度、添加障碍物）实现高分排行榜功能（本地存储）添加障碍物生成和碰撞检测更新游戏逻辑以适应新功能让我先查看现有的JavaScript代码，以便了解如何进行修改。现在我需要完全重写JavaScript文件，添加以下功能：难度级别（简单、中等、困难）障碍物生成和碰撞检测高分排行榜（使用localStorage存储）游戏结束后提交分数的模态框我将保留原有的核心游戏逻辑，但需要进行大量修改和扩展。我已经完成了所有必要的修改，让我总结一下新增的功能：难度级别系统：简单：较慢的初始速度，无障碍物，绿色蛇身中等：中等速度，5个障碍物，橙色蛇身困难：较快速度，10个障碍物，红色蛇身障碍物系统：根据难度级别生成不同数量的障碍物确保障碍物不会生成在蛇的初始位置附近添加了障碍物碰撞检测高分排行榜：使用localStorage存储每个难度级别的前10名可以切换查看不同难度的排行榜玩家可以在游戏结束后提交分数其他改进：添加了游戏结束模态框根据难度调整分数奖励优化了视觉效果（不同难度不同颜色）改进了游戏初始化逻辑现在让我使用attempt_completion来总结这些更改。贪食蛇游戏增强版已完成已成功为贪食蛇游戏添加了以下新功能：1. 难度级别简单：较慢的初始速度（6）每个食物10分无障碍物绿色蛇身中等：中等初始速度（8）每个食物15分5个障碍物橙色蛇身困难：较快初始速度（10）每个食物20分10个障碍物红色蛇身2. 障碍物系统根据难度自动生成障碍物智能障碍物放置算法，确保游戏可玩性与障碍物碰撞会导致游戏结束3. 高分排行榜每个难度级别独立的排行榜保存前10名最高分使用localStorage持久化存储可以在游戏界面直接切换查看不同难度的排行榜4. 游戏结束系统新增游戏结束模态框可以输入玩家名称提交分数提供直接重新开始的选项如何玩选择难度级别点击"开始游戏"按钮使用方向键控制蛇的移动游戏结束后可以提交分数到排行榜查看不同难度的排行榜记录游戏特点难度越高，得分越快随着分数增加，蛇的移动速度会逐渐加快每个难度都有独特的颜色主题支持本地记录保存是不是瞬间变高级了？哈哈哈。相关代码文件我丢在gitee了。（访问连接回复可见，喜欢看什么内容就给我评论吧）[hide]https://gitee.com/china_darksheep/ai-programming[/hide]
第一次使用Git创建并推送新分支到远程仓库的学习笔记
这两天我在使用百度快码comate zulu优化之前开发的魔兽汉化助手应用：UI文件的代码有2000多行，每次使用AI进行修改会消耗很多tokens，所以我想把这个文件进行模块化拆分。但是百度快码在对comate zulu进行产品调整，区分了慢速请求和快速请求。我实在受不了当前慢速请求的效率，最终还是充了cursor，花了2个小时优化好了。但突然意识到一个问题："我应该直接推送到 main 分支上吗？万一这次优化有bug怎么办？"所以，我需要创建一个新的分支，如果后期测试没有问题，可以再合并回去。于是，我决定学习如何正确创建新分支，并推送到远程仓库。新建分支前我们要查看所有分支，确认当前分支情况。操作步骤：创建并推送新分支1. 查看所有分支（确认当前分支情况）在开始之前，先检查本地和远程有哪些分支：git branch -a输出情况：* master remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/paid-branch（* 表示当前所在的分支） 可以看到我本地只有1个master分支，远程有2个分支：master和paid-branch。2. 新建本地分支并切换现在，我创建一个名为 ui_opt 的分支，并立即切换到它：git checkout -b ui_opt输出示例：Switched to a new branch 'ui_opt'以后在分支间切换可以使用git switch -c ui_opt3. 开发并提交更改在新分支上，我可以安全地修改代码，不用担心影响 main 分支：# 修改代码后，添加更改到暂存区 git add . # 提交更改 git commit -m "对UI进行了模块化拆分"4. 推送新分支到远程仓库最后需要把它推送到远程：git push -u origin ui_opt-u 的作用：建立本地分支与远程分支的追踪关系以后在这个分支上只需 git push 即可，不用再写完整命令5. 验证远程分支是否创建成功再次查看所有分支，确认远程分支已存在：git branch -a输出示例： master * ui_opt remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/paid-branch remotes/origin/ui_opt总结✅ 查看所有分支 → git branch -a ✅ 创建并切换分支 → git checkout -b 分支名 或 git switch -c 分支名 ✅ 推送新分支到远程 → git push -u origin 分支名 这样，我们就可以放心地在 ui_opt 分支上开发，而不会影响主分支的稳定性。当功能完成后，可以通过 Pull Request（PR） 或 Merge Request（MR） 将代码合并回 master 分支。 其实这个方法很多时候用于团队协作🔹 每个新功能或修复都应在独立分支上开发 🔹 分支名要有意义（如 feature/login、fix/bug-123） 🔹 定期拉取远程最新代码（git pull origin main）避免冲突 希望这篇教程能帮助你更好地管理 Git 分支！ 🚀
第一次用扣子coze开发一个魔兽文本汉化AI翻译应用
🔥 一、什么是「扣子」？今天安利一个超酷的AI开发平台——扣子！ 简单来说，扣子就像AI界的“乐高积木”，无论你是技术小白还是编程大佬，都能用它轻松拼出自己的AI应用！🎮🤖 二、扣子能做什么？1️⃣ 智能体：做个会聊天的AI伙伴！ 👉 比如：英语外教、智能客服、虚拟女友/男友（懂的都懂），只要用对话就能让它帮你干活！ 2️⃣ AI应用：开发完整功能的工具！ 👉 比如：做个AI翻译器、饮食记录助手，甚至搞个自动写行业报告的“打工人救星”！🛠️ 三、扣子的优点✅ 零代码！拖拉拽就能设计——像做PPT一样简单！ ✅ 插件超多：新闻播报、数据分析…还能自己开发插件！ ✅ 喂它你的知识：上传文件/网站，AI立马变成你的“行业专家”！💡 四、基于扣子开发一款针对魔兽文本汉化的AI翻译应用🚀 现在一起来试试吧！（一）初步搭建按照官方指导教程：《开发一个 AI 翻译应用》能完成初步搭建！（遇到卡点？评论区随时提问！👇）（二）定制调试这部分主要分为业务逻辑和用户界面优化。1、业务逻辑（1）模型选择，建议用默认的“豆包·工具调用”，资源点消耗更少。个人注册扣子coze后，每天会赠送500资源点/天，用完不能增购，没用完会重置为500点模型用量消耗价格豆包·工具调用千Tokens输入0.8 资源点0.0008元豆包·工具调用千Tokens输出2 资源点0.002元DeepSeek-V3·工具调用千Tokens输入2 资源点0.002元DeepSeek-V3·工具调用千Tokens输出8 资源点0.008元（2）模型自定义设置，将最大回复长度调整为最大值。豆包·工具调用的最大回复长度是4096 Tokens，100 Tokens 约等于 150 个中文汉字，4096 Tokens 就约等于 6144个中文汉字。（3）修改系统提示词和用户提示词官方指导教程给的系统提示词和用户提示词如下：系统提示词 # 角色 你是一个专业的翻译官，能够准确地将用户输入的内容翻译成目标语言，不进行随意扩写。 ## 技能 ### 技能 1：翻译文本 1. 当用户提供一段文本时，迅速将其翻译成目标语言。 2. 确保翻译的准确性和流畅性。 ## 限制： - 只进行翻译工作，不回答与翻译无关的问题。 - 严格按照用户要求的目标语言进行翻译，不得擅自更改。 用户提示词 将用户输入的内容{{content}}翻译成目标语言{{lang}}。我们对上述内容略加修改，以更加适合我们的应用场景。系统提示词 # 角色 你是一个专业的游戏翻译专家，能够准确地将用户输入的源语言翻译成简体中文，不进行随意扩写。 ## 技能 ### 技能 1：翻译文本 1. 当用户提供一段文本时，迅速将其翻译成目标语言。 2. 保持原文格式不变，特别是英文标点、特殊符号和标记，如{、}、|r、|n、|c、*号等。 3. 保持输出结果是以{开头，以}结尾，且中间没有其他{}符号。 4. 上下文类似的句子，请保持一致的翻译。 5. 确保翻译通顺、自然，符合中文的语言习惯和游戏领域的术语。 6. 遇到专业术语或难以直译的部分时，请选择合适的翻译方式，保证游戏玩家能准确理解。 ## 限制： - 只将源语言翻译成简体中文，不回答与翻译无关的问题。 - 禁止使用"号开头和结尾。 用户提示词 将用户输入的内容{{content}}中的{{lang}}翻译成简体中文。（4）试运行验证我们可以点击试运行验证下翻译效果。我们使用：{|cFFA085EDРаса: |r|cFF07B4C2Альянс|r|n|n|cFFA085EDУрон: |r|cFFDF7AD826-30 (тип: Обычная)|r|n|cFFA085EDСкорость атаки: |r|cFFDF7AD81,1|r|n|cFFA085EDДальность: |r|cFFDF7AD8350|r|n|cFFA085EDЗдоровье: |r|cFFDF7AD8550|r|n|cFFA085EDЗащита: |r|cFFDF7AD84 (тип: Тяжелая)|r|n|cFFA085EDВремя найма: |r|cFFDF7AD821 сек.|r|n|cFFA085EDСпособности: |r|cFFDF7AD8Способен похищать заклинания, защищен от магии, сжигает ману противника при каждой атаке|r}测试效果。效果还不错！2、用户界面这部分文本内容、背景色、边框色等风格，大家可以自由发挥。我只提2处需要调整的。（1）调整选择组件的属性由于我们在工作逻辑部分已经固定了目标语言为“简体中文”，变量{{lang}}作为源语言，所以要调整它的界面属性。标签内容和占位文案修改为：源语言增加选项内容为：英文、俄文、韩文、越文根据自己需要修改选项默认值，我主要翻译俄文，所以默认值修改为了俄文。五、体验地址我已经将这款应用发布在了扣子商店，大家有需要就去体验下吧。（遇到问题？优化建议？评论区随时提问！👇）https://www.coze.cn/s/l76lwaQ-46I/
闲来无事使用Comate Zulu生成一个类似但不是贪食蛇的小游戏？
文心快码 Coding 智能体 Zulu 正式发布！欢迎各位小伙伴们抢先体验！使用 Comate Zulu，用对话就能写代码，更加智能、简单、直观， 并且提供从代码生成到业务理解的完整智能服务——0开发经验也能做软件、做网站、做游戏，让编程技术门槛更低，让开发者自由挥洒创意！Comate Zulu发布了新一期打卡任务。只要在4月25日至5月7日活动时间有效使用 Comate Zulu，即可参加小度智能屏X7、智能音箱、按摩仪等超多好礼的抽奖！有兴趣的小伙伴可以点击我的邀请链接参与哟！https://comate.baidu.com/?inviteCode=6h2mrwpb有效使用要同时满足以下三个条件：在插件上登陆，并切换到zulu模式提问提问内容需明确涉及代码开发场景，包括但不限于： 代码生成（例：“帮我写一个俄罗斯方块小游戏”） 项目分析（例：“解释当前SpringBoot项目的分层架构”）Zulu系统返回完整回答一、第1轮对话我们第一次使用Comate Zulu时给了一个“命题作文”，详见《第一次使用Comate Zulu生成贪食蛇游戏的学思笔记》。这里我们来一个开放式作文。我：请你为我生成一个类似但不是贪食蛇的小游戏。zulu：我将为您创建一个"躲避陨石"的小游戏，玩家控制飞船躲避下落的陨石。一分钟后，它为我开发了一个"躲避陨石"的小游戏，具有以下特点：使用方向键控制飞船左右移动随着分数增加，陨石下落速度会逐渐加快游戏结束后按R键可以重新开始自适应窗口大小调整双击start meteor-dodge.html文件运行，哎呦，还不错！能玩！但是有点简单！二、第2轮对话我：游戏有点简单，请你增加一些难度zulu：我将为游戏添加不同类型的陨石，使游戏更有挑战性：4种陨石类型：普通(蓝色)、快速(红色菱形)、分裂(绿色)和追踪(黄色)难度会随分数自动提升，特殊陨石出现概率增加游戏界面显示操作说明和当前难度按R键可随时重新开始这里花了很多时间解决bug：1、画面只有飞船（三角形），不能移动，且没有陨石出现2、游戏难度增加过快，不利于新手玩家3、分裂陨石不分裂三、第3轮对话我：请你进一步丰富游戏内容zulu：我将增加一种名为护盾的游戏道具，通过收集"shield"类型的道具获得护盾，道具随机从上方掉落，颜色为青色(#00ffff)。同时增加添加"时间减缓"和"清屏炸弹"两种新道具。增加背景星空动画。这一步对游戏进行了以下优化：1、由于颜色难以记忆，所有陨石的外观统一为圆形2、给道具加上了中文标识，外观调整为圆形以外的形状。附上可玩的html代码新建一个txt文件，复制粘贴进去，修改后缀名为html即可双击运行。 Meteor Dodge Game
第一次使用InsCode IDE生成贪食蛇游戏的学思（避坑）笔记
试用版本：V2.0试用时间：2025年4月14日一、什么是InsCode IDE我今天也是闲的无聊，搜索“国内AI IDE”就看到了这款由CSDN、GitCode和华为云CodeArts联合开发的AI 跨平台集成开发环境。InsCode IDE定位于​​“让开发更智能”​​，整合了​​在线编码、AI生成、项目协作、一键部署​​四大核心能力。目前使用的AI大模型是：DeepSeek-V3、DeepSeek-R1二、测试过程看宣传视频感觉很流畅很丝滑。我仍然使用以下提示词来测试：使用 web 技术栈生成一个贪吃蛇游戏看看它能不能超过百度文心快码的Zulu智能体。1、 第一轮对话InsCode AI：我们需要创建一个使用HTML、CSS和JavaScript实现的贪吃蛇游戏。首先需要确定游戏的核心功能：蛇的移动控制、食物生成、碰撞检测、得分计算等。需要新建HTML文件作为入口，并添加对应的JavaScript逻辑和基本样式。为确保代码质量，需遵循游戏开发最佳实践，如使用Canvas渲染、合理的事件监听和游戏循环机制。InsCode AI为我创建了一个html文件。InsCode AI：贪吃蛇游戏已成功创建！游戏功能包括：方向键控制蛇的移动（←↑→↓）随机食物生成系统碰撞检测（墙壁和自身）动态计分系统（每吃10分加速）游戏结束后的重新开始功能按空格键可立即开始游戏，碰撞后会显示重新开始按钮。游戏速度会随着得分增加逐渐加快以提升挑战性。但是我懵了！！！1、代码转义有问题！2、Html、css、js都写在一个文件里！3、可想而知，根本运行不了！2、 第2轮对话InsCode AI：首先分析用户反馈的无法运行问题。通过查看文件内容发现存在HTML实体转义问题，如<和>符号未被正确还原为<和>，导致浏览器无法正确解析HTML结构。同时代码中存在初始化逻辑问题，游戏启动前缺少有效的初始方向设置，且键盘事件监听时机可能不当。需要修正转义字符并优化游戏初始化流程。还不错！我让他检查，它发现了实体转义问题！但是！它只改了几处代码！就告诉你它“已完整检查并修复贪吃蛇游戏代码”！所以运行结果是这样的：到这里我基本就放弃了！看宣传视频的时候我还在想能不能超过百度的Comate Zulu？《第一次使用Comate Zulu生成贪食蛇游戏的学思笔记》现在看来，在“使用 web 技术栈生成一个贪吃蛇游戏”方面，还不如当初字节的Trae AI。《第一次使用Trae AI生成贪食蛇游戏的学思笔记》3、 第N轮对话（N≥10）经过了N轮对话，最后我还是完成了贪食蛇游戏。附snake-game.html代码： 贪吃蛇游戏 得分: 0 开始游戏 重新开始 游戏说明: 1. 使用方向键(↑↓←→)控制蛇的移动方向 2. 吃到黄色食物增加得分 3. 撞到墙壁或自己的身体游戏结束 4. 点击"重新开始"按钮可以重玩游戏
爆肝5天使用文心快码Zulu制作了一款文件批量处理应用
一、背景上一篇《不写一行代码，只花20分钟和Zulu聊天就干完需要6小时的重复工作》提到可以进一步深化应用功能。所以我爆肝5天，使用百度文心快码（Baidu Comate）Zulu智能体，基于Python语言，制作了一款名为“蓝波文件批量处理工具”。二、介绍“蓝波文件批量处理工具”目前主要有2个核心功能：1、批量创建文件，文件类型有Word、Excel、PDF、Ppt、txt；2、批量替换文件，核心功能是：支持Excel数据快速写入WORD模板，批量创建一个或多个WORD文件。（其实就是《不写一行代码，只花20分钟和Zulu聊天就干完需要6小时的重复工作》功能的升级版）（一）批量创建用户导入一个Excel表格，应用会根据第一列单元格的的内容，批量创建多个你需要的空白文件夹或文件（二）批量替换用户导入一个Excel表格和一个Word模板，应用会根据占位符进行批量替换。主要使用场景及示例：​​1. 合同与协议​​​​场景​​：批量生成客户合同、租赁协议、采购订单等。​​示例​​：Excel中存储客户名称、地址、签约金额、日期等字段；Word模板中用{{客户姓名}}、{{金额}}等占位符，自动生成个性化合同。信函与通知​​​​场景​​：发送大量个性化通知，如录取通知书、缴费提醒、会议邀请等。​​示例​​：学校用Excel管理学生信息，Word模板中替换{{学生姓名}}、{{录取专业}}，快速生成录取信。​​- ​​3. 报告与证书​​​​场景​​：生成绩效报告、培训证书、成绩单等。​​示例​​：员工培训后，Excel记录姓名、课程名称、成绩，Word证书模板自动填充{{姓名}}和{{课程名称}}。​​- ​​4. 标签与名片​​​​场景​​：批量打印邮寄地址标签、员工名片、产品标签等。​​示例​​：Excel存储客户地址，Word模板通过{{邮编}}、{{收件人}}生成标准化邮寄标签。​​- ​​5. 法律与行政文书​​​​场景​​：法院传票、政府审批文件、授权书等。​​示例​​：法律机构用Excel管理案件信息，Word模板自动填充案号、当事人姓名等占位符。​​​​- ​​6. 市场推广​​​​场景​​：个性化营销邮件、促销传单、客户邀请函。​​示例​​：Excel存储客户偏好数据，Word模板插入{{尊称}}、{{优惠码}}，提升客户体验。​​- ​​7. 医疗与教育​​​​场景​​：生成体检报告、学生评语、课程表等。​​示例​​：医院用Excel记录患者检测结果，Word模板自动填入{{患者姓名}}和{{检测指标}}。​​- ​​8. 项目管理​​​​场景​​：任务单、项目进度报告、验收文档。​​示例​​：Excel跟踪项目成员分工，Word模板通过{{负责人}}、{{截止日期}}生成任务清单。​​​​9. 财务与账单​​​​场景​​：生成客户对账单、发票、付款凭证。​​示例​​：Excel存储客户交易记录，Word模板自动填充{{账单周期}}、{{应缴金额}}。优势总结​​​​效率提升​​：避免重复手动输入，节省时间。​​一致性保障​​：确保文档格式和内容标准化。​​减少错误​​：数据直接从Excel提取，降低人为失误。​​灵活扩展​​：支持大规模数据处理，适应不同行业需求。百度网盘下载链接（回复可见）[hide]链接: https://pan.baidu.com/s/1cHoAG_KOLB6-rC9d295d7g?pwd=usqp提取码: usqp[/hide]
不写一行代码，只花20分钟和Zulu聊天就干完需要6小时的重复工作
一、背景故事我有一个搞财会的朋友，每个季度末都得给不同的公司发通知函，告诉它们目前为止暂估了多少金额，或者缺了多少成本金额。所以她要花6~8小时把几百家公司的名字和财务数据复制粘贴到通知函模板里再集中打印。以下是她发给我的数据EXCLE表格和通知函WORD模板（稍作处理），如下图所示。二、思考我们为什么不能使用百度文心快码Comate Zulu，让它来帮我们写一个自动化的小应用来给我们打工呢？三、行动1、数据格式标准化根据多年的倒腾经验，当前的Excel文件数据和Word模板格式不利于自动化处理。（也许可以，但是AI可能要花很长时间理解）所以，我们得把Excel文件数据和Word模板进行一些微调。将Excel文件中有暂估金额和成本金额的数据放到Sheet2，而只有暂估金额金额的数据放到Sheet1.并且用标头+数据的格式替代原来文本+数据的格式。如下：这样Excel里的标题和Word的客户名称、暂估金额和成本金额就成了一一对应的关系。2、需求提炼我们通过txt文本来初步提炼下我们的应用需求：（1）从暂估函数据Excel文件的Sheet1和Sheet2中读取数据。（2）Sheet1中的客户名称和暂估金额，填到暂估函数模板A.docx的客户名称和暂估金额位置。（3）将Sheet2中的客户名称、暂估金额和成本金额，填到暂估函数模板B.docx的客户名称、暂估金额和成本金额位置。（4）不同的客户名称输出不同的文件，为方便打印，多个填充后的文档合并成一个输出文件。最后生成两种输出文档（暂估函A_合并.docx和暂估函B_合并.docx）。3、提交需求为了方便Zulu读取文档数据，新建一个文件夹存放暂估函数据、暂估函模板A、暂估函模板B。打开VsCode，打开存档数据的文件夹，点击百度文心快码插件，切换到Zulu模式，将需求提交给它。很快，Zulu经过思考理解了你的需求并给我写了一个python脚本。Zulu为了运行这个脚本，指导我安装必要的Python库来运行。安装了必要的库，Zulu开始检查Excel文件的内容，以便了解数据结构。Zulu要求我运行命令来检查，检查的结果是没有输出任何内容。它认为这可能是因为Excel文件的格式问题或者文件路径不正确。因此它开始为我们排查错误，尝试列出当前目录中的文件，以确保我们在正确的位置。最终它发现是因为我的列名中存在空格！最后运行终端命令。成功地生成了两个合并的文档：暂估函A_合并.docx 和 暂估函B_合并.docx。Zulu每次完成后都会有个总结：我们创建了一个Python脚本 process_docs.py，用于处理Excel数据和Word模板。我们从 暂估函数据.xlsx 文件中读取了Sheet1和Sheet2的数据。我们使用 暂估函模板A.docx 处理了Sheet1的数据，生成了 暂估函A_合并.docx。我们使用 暂估函模板B.docx 处理了Sheet2的数据，生成了 暂估函B_合并.docx。每个生成的文档都包含了多个客户的信息，每个客户的信息都在单独的页面上。现在，您可以打开 暂估函A_合并.docx 和 暂估函B_合并.docx 来查看结果。这两个文件应该包含了所有客户的暂估函，每个客户的信息都在单独的页面上，便于打印。四、深化Zulu用了5分钟时间实现了基本应用需求，后面我又花了15分钟深化了自己的需求：1、增加了一个选择文件的窗口界面2、要求它保留原始文档的格式和样式其中保留Word模版的格式花了最长的时间，因为涉及到不同的Pyhton库。下图是我最终实现的应用界面：其实这个应用需求未来还可以进一步深化。我们现在解决的固定场景的固定数据，如果我要处理不同的Excel模板，不同的Word模板，有多余3个以上需要替换的呢？五、使用感受从"人工肝"到"AI外挂"：效率革命的暴击全程没写一行代码，只用百度文心快码（Baidu Comate）里的Zulu工具，20分钟就搞定了她6小时的重复劳动！这感觉就像从"人工肝"模式瞬间切换成"AI外挂"，爽到飞起！Zulu的的"神仙操作需求提炼：和AI的"灵魂对话"用自然语言描述需求："把Sheet1的数据填到模板A，Sheet2填到模板B，最后合并成两个文件"。Zulu居然秒懂！这体验比跟甲方沟通顺畅100倍！生成的Python脚本逻辑清晰，连我这个代码盲都能看懂注释。智能纠错：AI比你还细心！运行脚本时突然报错？别急！Zulu主动排查发现列名里的空格是罪魁祸首！这波操作让我跪服——以前手动处理时，这种细节错误至少要检查半小时，AI却瞬间定位问题，连Debug都省了！价值爆点：省下的不止是时间💡 时间自由：6小时→20分钟省下的5小时40分钟够追半部剧、撸个铁，甚至学门新技能！对财会朋友来说，季度末再也不用加班到深夜，生活质量直线上升。💡 错误率归零：AI比人更靠谱手动复制时难免看错行、漏数据，但Zulu严格按照逻辑执行，生成的文件0错误！这对财务数据来说简直是"保命符"。💡 技能平权：不会编程？不存在的！整个流程最颠覆的是——全程没碰代码！Zulu把Python封装成"魔法对话"，连需求描述都用自然语言。这让我想起《三体》里的"智子"，只不过这次是帮人类打工的AI。总结：AI工具不是替代人，而是解放人用Zulu的过程让我深刻感受到：重复性工作正在被重新定义。它不是要抢谁的饭碗，而是让人类从机械劳动中抽身，去做更有创造力的事。就像财务朋友说的："省下的时间，我终于能分析数据而不是做表格了！"点击我的邀请码，你也来试试吧：https://comate.baidu.com/?inviteCode=6h2mrwpb六、附Python脚本import tkinter as tk from tkinter import ttk, messagebox, filedialog from openpyxl import load_workbook from docx import Document import os import threading import shutil import sys import subprocess import tempfile def count_data_rows(sheet): """统计sheet中除标题外的数据行数""" row_count = 0 for row in sheet.iter_rows(min_row=2): # 从第2行开始，跳过标题 if any(cell.value for cell in row): # 如果行中有任何非空值 row_count += 1 return row_count def replace_placeholders(doc, data): """在文档中替换占位符，保留原始格式""" # 打印调试信息 print("正在替换以下占位符：", data) def process_paragraph(paragraph, data): """处理单个段落中的占位符""" original_text = paragraph.text new_text = original_text # 收集所有运行的文本和格式 runs_with_format = [] for run in paragraph.runs: runs_with_format.append({ 'text': run.text, 'font': run.font, 'bold': run.bold, 'italic': run.italic, 'underline': run.underline, 'style': run.style }) # 替换所有占位符 for key, value in data.items(): replace_value = str(value) if value is not None else "" if key in new_text: new_text = new_text.replace(key, replace_value) # 如果文本有变化，重新构建段落 if new_text != original_text: # 清除原有内容 for run in paragraph.runs: run._element.getparent().remove(run._element) # 添加新的运行，保持原有格式 new_run = paragraph.add_run(new_text) if runs_with_format: # 使用第一个运行的格式 format_info = runs_with_format[0] new_run.font.name = format_info['font'].name new_run.font.size = format_info['font'].size new_run.font.bold = format_info['bold'] new_run.font.italic = format_info['italic'] new_run.font.underline = format_info['underline'] if format_info['style']: new_run.style = format_info['style'] # 处理所有段落 for paragraph in doc.paragraphs: if paragraph.text.strip(): # 只处理非空段落 process_paragraph(paragraph, data) # 处理表格中的段落 for table in doc.tables: for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: if paragraph.text.strip(): # 只处理非空段落 process_paragraph(paragraph, data) def append_document(target_doc_path, source_doc_path): """将源文档附加到目标文档，保留所有格式""" # 使用docxcompose合并文档 from docxcompose.composer import Composer # 打开目标文档作为主文档 master = Document(target_doc_path) composer = Composer(master) # 添加分页符 master.add_page_break() # 附加源文档 doc2 = Document(source_doc_path) composer.append(doc2) # 保存合并后的文档 composer.save(target_doc_path) def install_required_packages(status_callback=None): """安装所需的依赖包""" if status_callback: status_callback("检查并安装必要的依赖包...") try: import docxcompose if status_callback: status_callback("依赖包已安装") return True except ImportError: if status_callback: status_callback("正在安装 docxcompose 包...") try: subprocess.check_call([sys.executable, "-m", "pip", "install", "docxcompose"]) if status_callback: status_callback("依赖包安装成功") return True except Exception as e: error_msg = f"无法安装必要的依赖包 'docxcompose': {str(e)}" if status_callback: status_callback(f"错误: {error_msg}") raise Exception(error_msg) def replace_placeholders(doc, data): """在文档中替换占位符""" # 替换段落中的占位符 for paragraph in doc.paragraphs: for key, value in data.items(): if key in paragraph.text: # 保留原始格式，只替换文本 for run in paragraph.runs: if key in run.text: run.text = run.text.replace(key, str(value) if value is not None else "") # 替换表格中的占位符 for table in doc.tables: for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: for key, value in data.items(): if key in paragraph.text: for run in paragraph.runs: if key in run.text: run.text = run.text.replace(key, str(value) if value is not None else "") def append_document(target_doc_path, source_doc_path): """将源文档附加到目标文档，保留所有格式""" # 使用docxcompose合并文档 from docxcompose.composer import Composer # 打开目标文档作为主文档 master = Document(target_doc_path) composer = Composer(master) # 添加分页符 master.add_page_break() # 附加源文档 doc2 = Document(source_doc_path) composer.append(doc2) # 保存合并后的文档 composer.save(target_doc_path) def copy_and_replace_template(template_doc, target_doc, data): """复制模板内容并替换占位符，保留所有格式""" # 复制所有段落 for paragraph in template_doc.paragraphs: # 复制段落样式和对齐方式 new_paragraph = target_doc.add_paragraph() new_paragraph._p.set_attribute('style', paragraph._p.get_attribute('style')) new_paragraph.paragraph_format.alignment = paragraph.paragraph_format.alignment new_paragraph.paragraph_format.left_indent = paragraph.paragraph_format.left_indent new_paragraph.paragraph_format.right_indent = paragraph.paragraph_format.right_indent new_paragraph.paragraph_format.space_before = paragraph.paragraph_format.space_before new_paragraph.paragraph_format.space_after = paragraph.paragraph_format.space_after new_paragraph.paragraph_format.line_spacing = paragraph.paragraph_format.line_spacing # 复制段落中的所有文本和格式 for run in paragraph.runs: new_run = new_paragraph.add_run(run.text) # 复制字体格式 new_run.bold = run.bold new_run.italic = run.italic new_run.underline = run.underline new_run.font.name = run.font.name new_run.font.color.rgb = run.font.color.rgb if run.font.size: new_run.font.size = run.font.size # 复制其他格式属性 new_run.style = run.style if hasattr(run, 'math') and run.math is not None: new_run._r.append(run.math._element) # 复制所有表格 for table in template_doc.tables: # 创建新表格并复制样式 new_table = target_doc.add_table(rows=len(table.rows), cols=len(table.columns)) new_table.style = table.style new_table.alignment = table.alignment # 复制表格宽度 if hasattr(table, 'width'): new_table._tbl.tblPr.tblW.w = table._tbl.tblPr.tblW.w new_table._tbl.tblPr.tblW.type = table._tbl.tblPr.tblW.type # 复制单元格内容和格式 for i, row in enumerate(table.rows): # 复制行高 if hasattr(row, 'height'): new_table.rows[i].height = row.height for j, cell in enumerate(row.cells): new_cell = new_table.cell(i, j) # 复制单元格属性 if cell.width: new_cell.width = cell.width # 处理单元格合并 if cell.vertical_merge: new_cell._tc.tcPr.vMerge = cell._tc.tcPr.vMerge if hasattr(cell._tc.tcPr, 'hMerge') and cell._tc.tcPr.hMerge is not None: new_cell._tc.tcPr.hMerge = cell._tc.tcPr.hMerge # 复制单元格内容 # 先清空新单元格中的段落 for p in new_cell.paragraphs: p._p.getparent().remove(p._p) new_cell._paragraphs = [] # 复制单元格中的所有段落 for paragraph in cell.paragraphs: new_paragraph = new_cell.add_paragraph() new_paragraph._p.set_attribute('style', paragraph._p.get_attribute('style')) new_paragraph.paragraph_format.alignment = paragraph.paragraph_format.alignment # 复制段落中的所有文本和格式 for run in paragraph.runs: new_run = new_paragraph.add_run(run.text) new_run.bold = run.bold new_run.italic = run.italic new_run.underline = run.underline new_run.font.name = run.font.name new_run.font.color.rgb = run.font.color.rgb if run.font.size: new_run.font.size = run.font.size new_run.style = run.style # 替换占位符 replace_placeholders(target_doc, data) # 添加分页符 target_doc.add_page_break() def process_documents(excel_path, template_a_path, template_b_path, output_a_path, output_b_path, status_callback=None, progress_callback=None): """处理文档并提供状态和进度回调""" try: if status_callback: status_callback("开始处理文档...") # 加载Excel文件 if status_callback: status_callback("正在读取Excel数据...") wb = load_workbook(excel_path) sheet1 = wb['Sheet1'] sheet2 = wb['Sheet2'] # 统计数据行数 row_count_sheet1 = count_data_rows(sheet1) row_count_sheet2 = count_data_rows(sheet2) if status_callback: status_callback(f"Sheet1中有{row_count_sheet1}行数据") status_callback(f"Sheet2中有{row_count_sheet2}行数据") # 更新进度 if progress_callback: progress_callback(10) # 处理A类文档 if status_callback: status_callback("正在处理模板A的数据...") # 直接使用模板文件作为基础 shutil.copy(template_a_path, output_a_path) doc_a = Document(output_a_path) # 处理第一个公司数据 first_row = next(sheet1.iter_rows(min_row=2, max_row=2), None) if first_row: company_data = { "客户名称": first_row[0].value if first_row[0].value else "", "暂估金额": first_row[1].value if first_row[1].value else "" } # 替换第一个文档中的占位符 replace_placeholders(doc_a, company_data) doc_a.save(output_a_path) # 处理后续公司数据 for i, row in enumerate(sheet1.iter_rows(min_row=3, max_row=row_count_sheet1+1)): try: company_data = { "客户名称": row[0].value if row[0].value else "", "暂估金额": row[1].value if row[1].value else "" } # 创建临时文件 temp_file = f"temp_a_{i}.docx" shutil.copy(template_a_path, temp_file) # 替换临时文件中的占位符 temp_doc = Document(temp_file) replace_placeholders(temp_doc, company_data) temp_doc.save(temp_file) # 将临时文件附加到主文档 append_document(output_a_path, temp_file) # 删除临时文件 try: os.remove(temp_file) except Exception as e: print(f"删除临时文件错误: {e}") except Exception as e: print(f"处理A类文档第{i+1}行数据时出错: {e}") if status_callback: status_callback(f"处理A类文档第{i+1}行数据时出错: {e}") # 更新进度 if progress_callback: progress = 10 + (i + 1) / row_count_sheet1 * 40 progress_callback(progress) # 更新进度 if progress_callback: progress_callback(50) # 处理B类文档 if status_callback: status_callback("正在处理模板B的数据...") # 直接使用模板文件作为基础 shutil.copy(template_b_path, output_b_path) doc_b = Document(output_b_path) # 处理第一个公司数据 first_row = next(sheet2.iter_rows(min_row=2, max_row=2), None) if first_row: company_data = { "客户名称": first_row[0].value if first_row[0].value else "", "暂估金额": first_row[1].value if first_row[1].value else "", "成本金额": first_row[2].value if first_row[2].value else "" } # 替换第一个文档中的占位符 replace_placeholders(doc_b, company_data) doc_b.save(output_b_path) # 处理后续公司数据 for i, row in enumerate(sheet2.iter_rows(min_row=3, max_row=row_count_sheet2+1)): try: company_data = { "客户名称": row[0].value if row[0].value else "", "暂估金额": row[1].value if row[1].value else "", "成本金额": row[2].value if row[2].value else "" } # 打印调试信息，确保成本金额被正确读取 print(f"处理第{i+3}行数据 - 客户名称: {company_data['客户名称']}, 暂估金额: {company_data['暂估金额']}, 成本金额: {company_data['成本金额']}") # 创建临时文件 temp_file = f"temp_b_{i}.docx" shutil.copy(template_b_path, temp_file) # 替换临时文件中的占位符 temp_doc = Document(temp_file) replace_placeholders(temp_doc, company_data) temp_doc.save(temp_file) # 将临时文件附加到主文档 append_document(output_b_path, temp_file) # 删除临时文件 try: os.remove(temp_file) except Exception as e: print(f"删除临时文件错误: {e}") except Exception as e: print(f"处理B类文档第{i+1}行数据时出错: {e}") if status_callback: status_callback(f"处理B类文档第{i+1}行数据时出错: {e}") # 更新进度 if progress_callback: progress = 50 + (i + 1) / row_count_sheet2 * 40 progress_callback(progress) # 更新最终进度 if progress_callback: progress_callback(100) if status_callback: status_callback("所有文档处理完成！") return row_count_sheet1, row_count_sheet2 except Exception as e: error_message = f"处理文档时出错: {str(e)}" print(error_message) if status_callback: status_callback(f"错误: {error_message}") raise class EstimateDocApp: def __init__(self, root): self.root = root self.root.title("暂估函自动填写工具") self.root.geometry("700x700") # 创建主框架 main_frame = ttk.Frame(root, padding="20") main_frame.pack(fill=tk.BOTH, expand=True) # 创建文件路径选择区域 file_frame = ttk.LabelFrame(main_frame, text="文件设置", padding="10") file_frame.pack(fill=tk.X, pady=10) # Excel文件路径 ttk.Label(file_frame, text="Excel数据文件:").grid(row=0, column=0, sticky=tk.W, pady=5) self.excel_path_var = tk.StringVar(value="暂估函数据.xlsx") ttk.Entry(file_frame, textvariable=self.excel_path_var, width=50).grid(row=0, column=1, sticky=tk.W) ttk.Button(file_frame, text="浏览...", command=lambda: self.browse_file(self.excel_path_var, [("Excel文件", "*.xlsx")])).grid(row=0, column=2, padx=5) # 模板A文件路径 ttk.Label(file_frame, text="模板A文件:").grid(row=1, column=0, sticky=tk.W, pady=5) self.template_a_path_var = tk.StringVar(value="暂估函模板A.docx") ttk.Entry(file_frame, textvariable=self.template_a_path_var, width=50).grid(row=1, column=1, sticky=tk.W) ttk.Button(file_frame, text="浏览...", command=lambda: self.browse_file(self.template_a_path_var, [("Word文件", "*.docx")])).grid(row=1, column=2, padx=5) # 模板B文件路径 ttk.Label(file_frame, text="模板B文件:").grid(row=2, column=0, sticky=tk.W, pady=5) self.template_b_path_var = tk.StringVar(value="暂估函模板B.docx") ttk.Entry(file_frame, textvariable=self.template_b_path_var, width=50).grid(row=2, column=1, sticky=tk.W) ttk.Button(file_frame, text="浏览...", command=lambda: self.browse_file(self.template_b_path_var, [("Word文件", "*.docx")])).grid(row=2, column=2, padx=5) # 输出文件设置 output_frame = ttk.LabelFrame(main_frame, text="输出设置", padding="10") output_frame.pack(fill=tk.X, pady=10) # 输出文件A路径 ttk.Label(output_frame, text="输出文件A:").grid(row=0, column=0, sticky=tk.W, pady=5) self.output_a_path_var = tk.StringVar(value="暂估函A_合并.docx") ttk.Entry(output_frame, textvariable=self.output_a_path_var, width=50).grid(row=0, column=1, sticky=tk.W) ttk.Button(output_frame, text="浏览...", command=lambda: self.save_file(self.output_a_path_var, [("Word文件", "*.docx")])).grid(row=0, column=2, padx=5) # 输出文件B路径 ttk.Label(output_frame, text="输出文件B:").grid(row=1, column=0, sticky=tk.W, pady=5) self.output_b_path_var = tk.StringVar(value="暂估函B_合并.docx") ttk.Entry(output_frame, textvariable=self.output_b_path_var, width=50).grid(row=1, column=1, sticky=tk.W) ttk.Button(output_frame, text="浏览...", command=lambda: self.save_file(self.output_b_path_var, [("Word文件", "*.docx")])).grid(row=1, column=2, padx=5) # 进度和状态区域 status_frame = ttk.Frame(main_frame, padding="10") status_frame.pack(fill=tk.X, pady=10) # 进度条 ttk.Label(status_frame, text="处理进度:").pack(anchor=tk.W) self.progress = ttk.Progressbar(status_frame, length=650, mode='determinate') self.progress.pack(fill=tk.X, pady=5) # 状态文本框 ttk.Label(status_frame, text="处理状态:").pack(anchor=tk.W) self.status_text = tk.Text(status_frame, height=10, width=80, wrap=tk.WORD) self.status_text.pack(fill=tk.BOTH, expand=True, pady=5) # 滚动条 scrollbar = ttk.Scrollbar(self.status_text, orient="vertical", command=self.status_text.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.status_text.configure(yscrollcommand=scrollbar.set) # 操作按钮区域 button_frame = ttk.Frame(main_frame) button_frame.pack(fill=tk.X, pady=10) # 开始处理按钮 self.process_button = ttk.Button(button_frame, text="开始处理", command=self.start_processing) self.process_button.pack(side=tk.RIGHT, padx=5) # 打开输出文件夹按钮 self.open_button = ttk.Button(button_frame, text="打开输出文件", command=self.open_output_files) self.open_button.pack(side=tk.RIGHT, padx=5) # 初始状态 self.log("程序已启动，请设置文件路径并点击'开始处理'按钮。") def browse_file(self, path_var, file_types): """打开文件选择对话框""" filename = filedialog.askopenfilename(filetypes=file_types) if filename: path_var.set(filename) def save_file(self, path_var, file_types): """打开文件保存对话框""" filename = filedialog.asksaveasfilename(filetypes=file_types, defaultextension=file_types[0][1]) if filename: path_var.set(filename) def log(self, message): """添加日志到状态文本框""" self.status_text.insert(tk.END, message + "\n") self.status_text.see(tk.END) # 自动滚动到底部 self.root.update_idletasks() def update_progress(self, value): """更新进度条""" self.progress['value'] = value self.root.update_idletasks() def start_processing(self): """开始处理文档""" # 禁用按钮，防止重复点击 self.process_button.configure(state=tk.DISABLED) # 获取文件路径 excel_path = self.excel_path_var.get() template_a_path = self.template_a_path_var.get() template_b_path = self.template_b_path_var.get() output_a_path = self.output_a_path_var.get() output_b_path = self.output_b_path_var.get() # 验证文件路径 if not all([os.path.exists(excel_path), os.path.exists(template_a_path), os.path.exists(template_b_path)]): messagebox.showerror("错误", "请确保所有输入文件路径有效！") self.process_button.configure(state=tk.NORMAL) return # 安装必要的依赖包 try: install_required_packages(status_callback=self.log) except Exception as e: messagebox.showerror("错误", f"安装依赖包失败: {str(e)}\n请手动运行: pip install docxcompose") self.process_button.configure(state=tk.NORMAL) return # 清空状态文本框 self.status_text.delete(1.0, tk.END) self.progress['value'] = 0 # 在新线程中处理文档，避免界面卡死 thread = threading.Thread(target=self.process_in_thread, args=( excel_path, template_a_path, template_b_path, output_a_path, output_b_path )) thread.daemon = True thread.start() def process_in_thread(self, excel_path, template_a_path, template_b_path, output_a_path, output_b_path): """在线程中处理文档""" try: row_count_sheet1, row_count_sheet2 = process_documents( excel_path, template_a_path, template_b_path, output_a_path, output_b_path, status_callback=self.log, progress_callback=self.update_progress ) # 处理完成后显示消息框 self.root.after(0, lambda: messagebox.showinfo("完成", f"文档处理完成！\n" f"- 已处理Sheet1中的{row_count_sheet1}行数据\n" f"- 已处理Sheet2中的{row_count_sheet2}行数据\n" f"输出文件已保存为:\n" f"- {os.path.basename(output_a_path)}\n" f"- {os.path.basename(output_b_path)}" )) except Exception as e: self.root.after(0, lambda: messagebox.showerror("错误", f"处理过程中出现错误：\n{str(e)}")) self.log(f"错误: {str(e)}") finally: # 恢复按钮状态 self.root.after(0, lambda: self.process_button.configure(state=tk.NORMAL)) def open_output_files(self): """打开输出文件""" output_a_path = self.output_a_path_var.get() output_b_path = self.output_b_path_var.get() if os.path.exists(output_a_path): os.startfile(output_a_path) else: self.log(f"文件不存在: {output_a_path}") if os.path.exists(output_b_path): os.startfile(output_b_path) else: self.log(f"文件不存在: {output_b_path}") if __name__ == "__main__": try: # 检查并安装必要的依赖包 try: install_required_packages(status_callback=print) except Exception as e: print(f"警告: 无法自动安装依赖包: {str(e)}") print("请手动运行: pip install docxcompose") root = tk.Tk() app = EstimateDocApp(root) root.mainloop() except Exception as e: print(f"程序运行出错：{str(e)}")