基于 Electron + Vue 3 + TypeScript + Ant Design Vue + Pinia
{
"dependencies": {
"ant-design-vue": "4.2.6", // UI 组件库
"pinia": "3.0.4", // 状态管理
"vue-router": "4.6.3", // 路由管理
"dayjs": "1.11.19", // 日期处理
"@ant-design/icons-vue": "7.0.1" // 图标库
}
}- ✅ Pinia 状态管理 -
src/renderer/src/stores/index.ts - ✅ Vue Router 路由 -
src/renderer/src/router/index.ts - ✅ Ant Design Vue UI -
src/renderer/src/main.ts
{
"dependencies": {
"@prisma/client": "6.19.0", // ORM 数据库客户端
"prisma": "6.19.0", // Prisma CLI
"axios": "1.13.2", // HTTP 请求
"cheerio": "1.1.2", // HTML 解析
"playwright": "1.56.1", // 浏览器自动化
"turndown": "7.2.2", // HTML 转 Markdown
"p-limit": "7.2.0", // 并发控制
"pino": "10.1.0", // 日志库
"fs-extra": "11.3.2", // 文件系统增强
"zod": "4.1.12" // 类型验证
}
}- ✅ 复制后端代码 -
src/main/core/*(来自 wechat-spider-node) - ✅ Prisma 配置 -
prisma/schema.prisma - ✅ 生成 Prisma Client -
pnpm prisma generate - ✅ 环境变量 -
.env
WeChat-spider-UI/
├── src/
│ ├── main/ # Electron 主进程
│ │ ├── index.ts # 主进程入口
│ │ ├── core/ # 后端核心代码(来自 wechat-spider-node)
│ │ │ ├── wechat/ # 微信爬虫逻辑
│ │ │ ├── storage/ # 存储逻辑
│ │ │ ├── media/ # 媒体下载
│ │ │ ├── config/ # 配置管理
│ │ │ ├── logger/ # 日志
│ │ │ └── types/ # 类型定义
│ │ ├── services/ # Service 层(待实现)
│ │ └── ipc/ # IPC 处理器(待实现)
│ │
│ ├── preload/ # 预加载脚本
│ │ ├── index.ts # IPC API 暴露
│ │ └── index.d.ts # TypeScript 类型定义
│ │
│ └── renderer/ # 渲染进程 (Vue 3 应用)
│ └── src/
│ ├── main.ts # Vue 应用入口
│ ├── App.vue # 根组件(带侧边栏布局)
│ ├── router/ # 路由配置
│ │ └── index.ts
│ ├── stores/ # Pinia 状态管理
│ │ └── index.ts
│ ├── views/ # 页面组件
│ │ ├── Dashboard.vue # 仪表盘
│ │ ├── Login.vue # 登录页
│ │ ├── Scraper.vue # 爬取任务
│ │ ├── Articles.vue # 文章管理
│ │ └── Settings.vue # 设置页
│ ├── components/ # 通用组件
│ ├── api/ # IPC 调用封装(待实现)
│ └── types/ # 前端类型定义(待实现)
│
├── prisma/ # Prisma 数据库
│ ├── schema.prisma # 数据库模型
│ └── migrations/ # 迁移文件
│
├── .env # 环境变量
├── package.json # 项目配置
└── electron.vite.config.ts # Electron Vite 配置
┌────────────────────────────────────────────┐
│ 侧边栏导航 │ 主内容区域 │
│ - 仪表盘 │ ┌────────────────────────┐ │
│ - 登录 │ │ │ │
│ - 爬取任务 │ │ Router View │ │
│ - 文章管理 │ │ (页面内容) │ │
│ - 设置 │ │ │ │
│ │ └────────────────────────┘ │
└────────────────────────────────────────────┘
-
Dashboard.vue - 仪表盘
- 简单卡片展示
- 待实现:统计数据、趋势图
-
Login.vue - 登录页
- 登录状态显示
- 扫码登录按钮
- 待实现:与主进程 IPC 通信
-
Scraper.vue - 爬取任务
- 表单输入(公众号名称、爬取范围)
- 存储选项
- 待实现:任务提交、进度显示
-
Articles.vue - 文章管理
- 表格列表
- 搜索功能
- 待实现:数据加载、详情查看
-
Settings.vue - 设置页
- Tab 分页(存储设置、爬取设置)
- 表单配置
- 待实现:保存配置到主进程
创建 src/main/services/ 目录,封装业务逻辑:
// src/main/services/ScraperService.ts
import { WeChatScraper } from '../core/wechat/scraper'
import { StorageRouter } from '../core/storage'
export class ScraperService {
async scrapeAccount(accountName: string, options: any) {
const scraper = new WeChatScraper()
const articles = await scraper.scrapeAccount(accountName, options)
const storage = new StorageRouter()
await storage.saveArticles(articles)
return articles
}
}需要创建的 Service:
ScraperService.ts- 爬取业务LoginService.ts- 登录管理ArticleService.ts- 文章查询ConfigService.ts- 配置管理
// src/main/ipc/scraper.ts
import { ipcMain } from 'electron'
import { ScraperService } from '../services/ScraperService'
export function registerScraperIPC() {
const service = new ScraperService()
ipcMain.handle('scraper:scrape-account', async (_, accountName, options) => {
return await service.scrapeAccount(accountName, options)
})
ipcMain.handle('scraper:get-progress', async () => {
return await service.getProgress()
})
}// src/preload/index.ts
const api = {
scraper: {
scrapeAccount: (name: string, options: any) =>
ipcRenderer.invoke('scraper:scrape-account', name, options),
getProgress: () =>
ipcRenderer.invoke('scraper:get-progress'),
onProgress: (callback: (progress: any) => void) =>
ipcRenderer.on('scraper:progress', (_, progress) => callback(progress))
}
}
contextBridge.exposeInMainWorld('api', api)// src/renderer/src/api/scraper.ts
export const scraperApi = {
async scrapeAccount(accountName: string, options: any) {
return await window.api.scraper.scrapeAccount(accountName, options)
},
onProgress(callback: (progress: any) => void) {
window.api.scraper.onProgress(callback)
}
}<script setup lang="ts">
import { scraperApi } from '@/api/scraper'
const handleScrape = async () => {
// 监听进度
scraperApi.onProgress((progress) => {
console.log('Progress:', progress)
})
// 开始爬取
const result = await scraperApi.scrapeAccount(accountName, options)
console.log('Result:', result)
}
</script>// src/renderer/src/stores/scraper.ts
import { defineStore } from 'pinia'
import { scraperApi } from '@/api/scraper'
export const useScraperStore = defineStore('scraper', {
state: () => ({
tasks: [],
progress: null
}),
actions: {
async scrapeAccount(name: string, options: any) {
const result = await scraperApi.scrapeAccount(name, options)
this.tasks.push(result)
return result
}
}
})// src/renderer/src/types/index.ts
export interface ScrapeOptions {
rangeType: 'days' | 'count' | 'all'
days?: number
count?: number
storage: ('local' | 'database')[]
}
export interface Article {
id: string
title: string
url: string
accountName: string
publishTime: string
content?: string
}
export interface ScrapeProgress {
status: 'searching' | 'fetching' | 'saving' | 'completed'
current: number
total: number
currentArticle?: string
}# 安装依赖
pnpm install
# 生成 Prisma Client
pnpm prisma generate
# 初始化数据库
pnpm prisma db push
# 开发模式
pnpm dev
# 构建应用
pnpm build
# TypeScript 类型检查
pnpm type-checkElectron 应用中,主进程和渲染进程的路径不同:
-
开发环境:
- 渲染进程:
http://localhost:5173 - 主进程:
src/main/
- 渲染进程:
-
生产环境:
- 渲染进程:
file://dist/renderer/index.html - 主进程:
dist/main/
- 渲染进程:
建议使用 Electron 的用户数据目录:
import { app } from 'electron'
import path from 'path'
const userDataPath = app.getPath('userData')
const dbPath = path.join(userDataPath, 'wechat-spider.db')
// 更新 .env
process.env.DATABASE_URL = `file:${dbPath}`- ✅ 已使用
contextBridge安全暴露 API ⚠️ 当前sandbox: false,建议后续改为true⚠️ 敏感数据(token、cookie)应使用safeStorage加密
- 长时间任务使用 Worker 线程
- 大列表使用虚拟滚动(
a-table-v2) - 路由懒加载(已实现)
-
IPC 类型定义不完整
- 需要完善
src/preload/index.d.ts - 添加
window.api的全局类型声明
- 需要完善
-
错误处理不统一
- Service 层需要统一错误类型
- IPC 调用需要错误边界
-
日志系统未集成
pino已安装但未配置- 需要区分主进程和渲染进程日志
文档版本: v1.0 最后更新: 2024-01-15 状态: 基础架构已完成,待实现业务逻辑