From 832503210b7ce880011af825b0a1554dd29f1ad0 Mon Sep 17 00:00:00 2001 From: LCJ-MinYa <1049468118@qq.com> Date: Tue, 14 Oct 2025 17:52:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8gemini=E6=94=B9?= =?UTF-8?q?=E9=80=A0=E9=A6=96=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GEMINI.md | 42 +++++++- package.json | 1 + scripts/statistics.mjs | 186 ++++++++++++++++++++++++++++++++++ src/views/welcome/config.json | 60 +++++++++++ src/views/welcome/index.vue | 99 ++++++++---------- 5 files changed, 325 insertions(+), 63 deletions(-) create mode 100644 scripts/statistics.mjs create mode 100644 src/views/welcome/config.json diff --git a/GEMINI.md b/GEMINI.md index c3703a6..42a850a 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -42,6 +42,11 @@ vue3-mgt-template/ ## 开发规范 +### **对Gemini的特别指令** +- **严格遵守规范**:当Gemini为本项目生成或修改任何代码时,**必须** 严格遵守本文件中定义的所有开发规范。 +- **中文优先**:所有代码注释、日志输出(例如 `console.log`)、以及提交信息(Commit Message)**必须** 使用中文。 + + ### 代码风格 - 使用 TypeScript 严格模式 - 组件命名采用 PascalCase (如 `TaskCard.vue`) @@ -56,12 +61,41 @@ vue3-mgt-template/ - 日志输出请使用中文 ## 对话记录 + +### 本次会话总结 (2025年10月14日) + +本次会话主要围绕 **首页动态化改造** 展开,完成了以下核心功能: + +1. **创建页面统计脚本**: + * 在 `scripts/` 目录下创建了 `statistics.mjs` 脚本。 + * 该脚本能够统计 `demo`, `python`, `utils`, `screen`, `template` 等多个模块下的页面数量、标题、最后修改日期等信息。 + * 脚本能够智能处理动态路由(如 `demo` 模块)和静态路由(如 `template` 模块)两种不同定义方式。 + * 在 `package.json` 中添加了 `pnpm run stats` 命令来执行此脚本。 + +2. **生成动态配置文件**: + * 脚本的统计结果会输出到 `/src/views/welcome/config.json` 文件中,作为首页的数据源。 + +3. **首页组件改造**: + * 重构了 `/src/views/welcome/index.vue` 组件。 + * 移除了原有的静态模拟数据。 + * 组件现在直接从 `config.json` 导入数据,实现了首页内容的动态更新。 + * 在页面前端添加了分类的中文名映射,优化了展示效果。 + +4. **过程中的迭代与修复**: + * 根据用户反馈,修复了统计脚本中错误的过滤逻辑,确保了统计数据的准确性。 + * 根据用户需求,多次优化了页面标题的生成规则。 + * 根据用户反馈,为所有由Gemini生成的代码和日志添加了中文注释,并强化了此项规范。 + - gemini在与我每次对话中,需要将重要信息更新到GEMINI.md文件中,方便下次重新运行时gemini理解 ## 开发任务 +### 规范 +使用了删除线的就代表已执行完成 -### 已完成 - -### 未完成 +### 任务列表 * 项目首页/welcome这个路由下是默认的的首页介绍页面,但是现在这个页面是一个静态的页面,是由一些模拟数据渲染的,我现在希望完成功能如下: -- 在package.json中添加一个命令,当执行这个命令的时候可以本地统计/src/views下的/demo,/python,/utils,/screen,这四个模块下分别有多少个页面,页面维度请按照router路由来统计,例如/python文件夹有五个文件夹,这就是5个页面。当统计完成后在/src/views/welcome中创建或者更新config.json文件,以便后续页面可以直接读取json配置文件 \ No newline at end of file +- ~~在package.json中添加一个命令,当执行这个命令的时候可以本地统计/src/views下的/demo,/python,/utils,/screen,这四个模块下分别有多少个页面,页面维度请按照router路由来统计,例如/python文件夹有五个文件夹,这就是5个页面。当统计完成后在/src/views/welcome中创建或者更新config.json文件,以便后续页面可以直接读取json配置文件~~ + +- ~~请读取/src/view/welcome中页面,其中页面的数据来源为/scripts/statistics.mjs,请更新该脚本,以便获得更多的信息,来满足/src/view/welcome页面所需数据的渲染~~ + +- ~~将/src/view/welcome/config.json作为数据源,渲染到/src/view/welcome的vue组件中~~ diff --git a/package.json b/package.json index 3073979..2541c03 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"", "lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/", "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint", + "stats": "node scripts/statistics.mjs", "preinstall": "npx only-allow pnpm" }, "dependencies": { diff --git a/scripts/statistics.mjs b/scripts/statistics.mjs new file mode 100644 index 0000000..d4ea50b --- /dev/null +++ b/scripts/statistics.mjs @@ -0,0 +1,186 @@ + + +import { globSync } from 'glob'; +import fs from 'fs'; +import path from 'path'; + +// 脚本从项目根目录运行,因此路径是相对于根目录的。 +const projectRoot = process.cwd(); +const viewsPath = path.join(projectRoot, 'src', 'views'); +const routerModulesPath = path.join(projectRoot, 'src', 'router', 'modules'); + +/** + * 从动态路由模块文件中解析出 titleArr 变量 + * @param {string} filePath - 路由模块文件的路径 + * @returns {Array} - 包含标题映射的数组 + */ +function parseTitleArr(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const match = content.match(/const\s+titleArr\s*=\s*(\[[\s\S]*?\]);/); + if (match && match[1]) { + return new Function(`return ${match[1]}`)(); + } + } catch (e) { + console.error(`读取或解析文件失败: ${filePath}`, e); + } + return []; +} + +/** + * 从静态路由模块文件中解析出路径和标题的映射 + * @param {string} filePath - 路由模块文件的路径 + * @returns {Map} - 路径名到标题的映射 + */ +function parseStaticRouteTitles(filePath) { + const titleMap = new Map(); + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const childrenMatch = content.match(/children:\s*(\[[\s\S]*?\])/); + if (!childrenMatch) return titleMap; + + const routesRegex = /{\s*path:[^,]+,\s*name:[^,]+,\s*component:[^,]+,\s*meta:\s*{\s*title:\s*['|"]([\s\S]+?)['|"]/g; + let match; + while ((match = routesRegex.exec(childrenMatch[1])) !== null) { + // This is a bit of a hack, we need to get the path from the component string + const componentMatch = match[0].match(/component:\s*\(\)\s*=>\s*import\(['|"]@\/views([\s\S]+?)['|"]\)/); + if (componentMatch && componentMatch[1]) { + const routePathKey = path.basename(path.dirname(componentMatch[1])); + titleMap.set(routePathKey, match[1]); + } + } + } catch (e) { + console.error(`读取或解析静态路由文件失败: ${filePath}`, e); + } + return titleMap; +} + + +/** + * 根据新规则获取页面标题 + * @param {string} file - 文件的绝对路径 + * @param {Array|Map} titleSource - 标题数组或标题Map + * @returns {string} - 最终的页面标题 + */ +function getTitle(file, titleSource) { + const parentDirName = path.basename(path.dirname(file)); + const fileName = path.basename(file, '.vue'); + const key = (fileName === 'index') ? parentDirName : fileName; + + if (Array.isArray(titleSource)) { // 处理 titleArr + const foundTitle = titleSource.find(item => item.key === key); + if (foundTitle && foundTitle.title) { + return foundTitle.title; + } + } else if (titleSource instanceof Map) { // 处理静态路由的 titleMap + if (titleSource.has(key)) { + return titleSource.get(key); + } + } + + // Fallback 逻辑 + return key; +} + +// --- 解析所有模块的标题信息 --- +const demoTitleArr = parseTitleArr(path.join(routerModulesPath, 'demo.ts')); +const pythonTitleArr = parseTitleArr(path.join(routerModulesPath, 'python.ts')); +const templateTitleMap = parseStaticRouteTitles(path.join(routerModulesPath, 'template.ts')); + +// --- 统计计算 --- + +let allNotes = []; + +// 1. 统计 demo 模块 +const demoFiles = globSync(path.join(viewsPath, 'demo', '**/*.vue')).filter(p => !p.includes('/components/')); +demoFiles.forEach(file => { + const stats = fs.statSync(file); + allNotes.push({ + path: path.relative(viewsPath, file).replace(/\\/g, '/'), + title: getTitle(file, demoTitleArr), + category: 'demo', + date: stats.mtime.toISOString(), + }); +}); + +// 2. 统计 python 模块 +const pythonFiles = globSync(path.join(viewsPath, 'python', '**/*.vue')); +pythonFiles.forEach(file => { + const stats = fs.statSync(file); + allNotes.push({ + path: path.relative(viewsPath, file).replace(/\\/g, '/'), + title: getTitle(file, pythonTitleArr), + category: 'python', + date: stats.mtime.toISOString(), + }); +}); + +// 3. 统计 template 模块 +const templateFiles = globSync(path.join(viewsPath, 'template', '**/*.vue')).filter(p => !p.includes('/components/')); +templateFiles.forEach(file => { + const stats = fs.statSync(file); + allNotes.push({ + path: path.relative(viewsPath, file).replace(/\\/g, '/'), + title: getTitle(file, templateTitleMap), + category: 'template', + date: stats.mtime.toISOString(), + }); +}); + +// 4. 统计 screen 和 utils (根据 remaining.ts) +const screenFile = path.join(viewsPath, 'screen', 'index.vue'); +if (fs.existsSync(screenFile)) { + const stats = fs.statSync(screenFile); + allNotes.push({ + path: 'screen/index.vue', + title: '大屏可视化', // 硬编码 + category: 'screen', + date: stats.mtime.toISOString(), + }); +} + +const utilsFile = path.join(viewsPath, 'utils', 'htmlToImg.vue'); +if (fs.existsSync(utilsFile)) { + const stats = fs.statSync(utilsFile); + allNotes.push({ + path: 'utils/htmlToImg.vue', + title: 'html转图片', // 硬编码 + category: 'utils', + date: stats.mtime.toISOString(), + }); +} + +// --- 数据整合 --- + +allNotes.sort((a, b) => new Date(b.date) - new Date(a.date)); + +const categoryStats = allNotes.reduce((acc, note) => { + acc[note.category] = (acc[note.category] || 0) + 1; + return acc; +}, {}); + +const oneWeekAgo = new Date(); +oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); + +const weeklyAdded = allNotes.filter(note => new Date(note.date) > oneWeekAgo).length; + +const finalData = { + totalNotes: allNotes.length, + categories: Object.keys(categoryStats).length, + lastUpdated: allNotes.length > 0 ? allNotes[0].date : new Date().toISOString(), + weeklyAdded: weeklyAdded, + categoryChartData: Object.entries(categoryStats).map(([name, value]) => ({ name, value })), + recentNotes: allNotes.slice(0, 5).map(note => ({ + path: note.path, + title: note.title, + category: note.category, + date: note.date.split('T')[0] + })) +}; + +// --- 写入 JSON 文件 --- +const outputPath = path.join(viewsPath, 'welcome', 'config.json'); +fs.writeFileSync(outputPath, JSON.stringify(finalData, null, 4)); + +console.log(`页面统计信息已成功生成到: ${outputPath}`); +console.log("分类统计:", JSON.stringify(finalData.categoryChartData, null, 4)); diff --git a/src/views/welcome/config.json b/src/views/welcome/config.json new file mode 100644 index 0000000..1cde6e2 --- /dev/null +++ b/src/views/welcome/config.json @@ -0,0 +1,60 @@ +{ + "totalNotes": 74, + "categories": 5, + "lastUpdated": "2025-10-14T03:06:31.162Z", + "weeklyAdded": 5, + "categoryChartData": [ + { + "name": "demo", + "value": 63 + }, + { + "name": "python", + "value": 5 + }, + { + "name": "utils", + "value": 1 + }, + { + "name": "template", + "value": 4 + }, + { + "name": "screen", + "value": 1 + } + ], + "recentNotes": [ + { + "path": "demo/deletdNodeModules/index.vue", + "title": "删除 node_modules 文件夹非常耗时解决办法", + "category": "demo", + "date": "2025-10-14" + }, + { + "path": "demo/asyncDynComp/index.vue", + "title": "异步动态加载组件", + "category": "demo", + "date": "2025-10-11" + }, + { + "path": "demo/asyncDynComp/moduleA/item2.vue", + "title": "item2", + "category": "demo", + "date": "2025-10-11" + }, + { + "path": "demo/asyncDynComp/moduleA/item1.vue", + "title": "item1", + "category": "demo", + "date": "2025-10-11" + }, + { + "path": "demo/dataSafe/index.vue", + "title": "数据安全-加密解密与掩码", + "category": "demo", + "date": "2025-10-10" + } + ] +} \ No newline at end of file diff --git a/src/views/welcome/index.vue b/src/views/welcome/index.vue index 445f1c5..174a6d4 100644 --- a/src/views/welcome/index.vue +++ b/src/views/welcome/index.vue @@ -1,3 +1,4 @@ +