# finance-front **Repository Path**: lanjing12138/finance-front ## Basic Information - **Project Name**: finance-front - **Description**: 未来智财-PC端 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-20 - **Last Updated**: 2026-05-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 未来智财 · 开发规格文档 > **应用名称**:未来智财 > **Slogan**:你的私人财务助理——基于 Dify 智能体工作流的企业级对话应用 > **技术框架**:Electron(桌面端)+ React(渲染层) > **目标平台**:Windows / macOS > **文档用途**:交付 Claude Code 完整实现 --- ## 目录 1. [项目概览](#1-项目概览) 2. [技术栈与依赖](#2-技术栈与依赖) 3. [目录结构](#3-目录结构) 4. [功能模块详述](#4-功能模块详述) - 4.1 登录界面 - 4.2 主界面布局 - 4.3 智能体对话窗口 - 4.4 多模态输入框 - 4.5 Dify API 对接 - 4.6 历史对话管理 - 4.7 用户头像与菜单 - 4.8 设置窗口 - 4.9 主题切换 5. [数据模型](#5-数据模型) 6. [IPC 通信协议](#6-ipc-通信协议) 7. [Dify API 集成规范](#7-dify-api-集成规范) 8. [UI/UX 设计规范](#8-uiux-设计规范) 9. [构建与打包](#9-构建与打包) 10. [开发任务清单](#10-开发任务清单) --- ## 1. 项目概览 ### 1.1 产品定位 「未来智财」是一款面向企业用户的桌面端财务智能体对话应用。用户通过文字、语音、文档、图片等多模态方式向财务智能体提问,应用将请求转发至后端 Dify 工作流,并以流式方式展示回复内容。 ### 1.2 核心功能 | 功能 | 描述 | |------|------| | 账号登录 | 启动弹出登录对话框,UI 展示,接口预留,可跳过 | | 智能体对话 | 主界面聊天窗口,支持流式输出 | | 多模态输入 | 文字 / 语音 / 文档(doc/docx/xls/xlsx/ppt/pptx)/ 图片(jpg/png 等) | | Dify 对接 | 转发至可配置的 Dify API,流式接收回复 | | 历史对话 | 左侧栏展示历史列表,点击切换,支持新建对话 | | 主题切换 | 亮色 / 暗色模式,手动或跟随系统 | | 用户菜单 | 左下角头像,点击展开退出登录 / 设置 | | 设置窗口 | 开机自启、Dify API 配置、关于信息 | --- ## 2. 技术栈与依赖 ### 2.1 核心依赖 ```json { "dependencies": { "electron": "^30.0.0", "react": "^18.3.0", "react-dom": "^18.3.0", "electron-store": "^10.0.0", "electron-updater": "^6.1.0", "marked": "^12.0.0", "highlight.js": "^11.9.0", "uuid": "^10.0.0" }, "devDependencies": { "@vitejs/plugin-react": "^4.3.0", "vite": "^5.4.0", "electron-builder": "^24.13.0", "concurrently": "^8.2.0" } } ``` ### 2.2 关键 Electron API 参考官方文档: - 主进程:`app`, `BrowserWindow`, `ipcMain`, `shell`, `dialog`, `Tray`, `Menu` - 渲染进程:`ipcRenderer`, `contextBridge` - 系统集成:`nativeTheme`(主题)、`app.setLoginItemSettings`(开机自启) --- ## 3. 目录结构 ``` 未来智财/ ├── package.json ├── vite.config.js # Vite 渲染层配置 ├── electron.vite.config.js # electron-vite 配置(可选) │ ├── electron/ # 主进程代码 │ ├── main.js # 入口:创建窗口、注册 IPC │ ├── preload.js # contextBridge 暴露 API │ ├── store.js # electron-store 封装 │ ├── dify.js # Dify API 请求(流式) │ ├── tray.js # 系统托盘(可选) │ └── updater.js # 自动更新(可选) │ ├── src/ # 渲染进程(React) │ ├── main.jsx # React 入口 │ ├── App.jsx # 根组件:路由 Login / Main │ ├── index.css # 全局样式 + CSS 变量 │ │ │ ├── pages/ │ │ ├── LoginPage.jsx # 登录界面 │ │ └── MainPage.jsx # 主界面 │ │ │ ├── components/ │ │ ├── Sidebar/ │ │ │ ├── Sidebar.jsx # 左侧栏容器 │ │ │ ├── ConversationList.jsx # 历史对话列表 │ │ │ └── UserAvatar.jsx # 用户头像 + 菜单 │ │ │ │ │ ├── Chat/ │ │ │ ├── ChatWindow.jsx # 对话消息列表 │ │ │ ├── MessageBubble.jsx # 单条消息气泡 │ │ │ └── TypingIndicator.jsx # 流式输出中动效 │ │ │ │ │ ├── Input/ │ │ │ ├── InputBar.jsx # 输入框主容器 │ │ │ ├── VoiceButton.jsx # 语音输入按钮 │ │ │ └── FileUploader.jsx # 文件上传按钮 │ │ │ │ │ └── Settings/ │ │ ├── SettingsModal.jsx # 设置弹窗 │ │ ├── DifyConfig.jsx # Dify 配置面板 │ │ └── AboutPanel.jsx # 关于面板 │ │ │ ├── store/ │ │ ├── useAppStore.js # Zustand 全局状态(或 useContext) │ │ └── useTheme.js # 主题状态管理 │ │ │ └── utils/ │ ├── markdown.js # Markdown 渲染工具 │ └── fileHelper.js # 文件类型校验工具 │ ├── assets/ │ ├── icon.png # 应用图标 │ └── logo.svg # 品牌 Logo │ └── dist/ # 构建输出(gitignore) ``` --- ## 4. 功能模块详述 ### 4.1 登录界面 **触发时机**:应用启动后,在主窗口显示前弹出登录对话框(`BrowserWindow` modal 或渲染层覆盖层)。 **UI 元素**: - 应用 Logo + 名称「未来智财」 - Slogan 文字 - 用户名输入框(`placeholder="请输入账号"`) - 密码输入框(`type="password"`,`placeholder="请输入密码"`) - 「登录」按钮(点击后提示"功能开发中",直接跳转主界面) - 「跳过,直接进入」文字链接(点击直接进入主界面) - 底部版权信息 **实现要点**: ```jsx // LoginPage.jsx 核心逻辑 const handleLogin = () => { // 接口预留,暂不调用后端 // TODO: 调用真实登录接口 onLoginSuccess({ username, avatar: null }); }; const handleSkip = () => { onLoginSuccess({ username: '访客', avatar: null }); }; ``` **状态持久化**:登录状态保存至 `electron-store`,下次启动时若已登录则直接跳过登录页(可在设置中退出登录重置)。 --- ### 4.2 主界面布局 ``` ┌─────────────────────────────────────────────────────┐ │ 标题栏(自定义 frameless + 拖拽区域) │ ├────────────┬────────────────────────────────────────┤ │ │ 顶部:Logo + Slogan + 主题切换按钮 │ │ 左侧栏 ├────────────────────────────────────────┤ │ (240px) │ │ │ │ 对话消息列表区域 │ │ 历史对话 │ (可滚动,flex-grow) │ │ 列表 │ │ │ ├────────────────────────────────────────┤ │ │ 输入框区域(固定底部) │ ├────────────┤ │ │ 用户头像 │ │ │ + 菜单 │ │ └────────────┴────────────────────────────────────────┘ ``` **窗口配置**(`electron/main.js`): ```javascript const mainWindow = new BrowserWindow({ width: 1200, height: 800, minWidth: 900, minHeight: 600, frame: false, // 自定义标题栏 titleBarStyle: 'hidden', trafficLightPosition: { x: 16, y: 16 }, // macOS 红绿灯位置 webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, }, show: false, // 加载完成后再显示,避免白屏 }); mainWindow.once('ready-to-show', () => mainWindow.show()); ``` --- ### 4.3 智能体对话窗口 **ChatWindow.jsx 功能**: - 消息列表竖向排列,用户消息靠右,AI 消息靠左 - 支持 Markdown 渲染(代码高亮、表格、列表) - 流式输出时末尾显示打字光标动效(`|` 闪烁) - 新消息自动滚动到底部(`scrollIntoView`) - 空对话时展示欢迎语和功能提示卡片 **MessageBubble.jsx 属性**: ```typescript interface MessageBubbleProps { role: 'user' | 'assistant'; content: string; // 支持 Markdown files?: UploadedFile[]; // 附带文件列表 timestamp: number; isStreaming?: boolean; // 是否正在流式输出 } ``` **Markdown 渲染**: ```javascript // utils/markdown.js import { marked } from 'marked'; import hljs from 'highlight.js'; marked.setOptions({ highlight: (code, lang) => { const language = hljs.getLanguage(lang) ? lang : 'plaintext'; return hljs.highlight(code, { language }).value; }, breaks: true, }); export const renderMarkdown = (text) => marked.parse(text); ``` --- ### 4.4 多模态输入框 **InputBar.jsx 布局**: ``` ┌──────────────────────────────────────────────────┐ │ 已上传文件预览(横向滚动缩略图列表) │ ├──────────────────────────────────────────────────┤ │ [📎] [🎤] 文本输入区(自动扩展高度) [发送▶] │ └──────────────────────────────────────────────────┘ ``` **支持的输入类型**: | 类型 | 说明 | |------|------| | 文本 | `textarea`,`Shift+Enter` 换行,`Enter` 发送 | | 语音 | 点击麦克风按钮,使用 Web Speech API 转文字,回填输入框 | | 文件上传 | 点击附件按钮,打开文件选择对话框 | | 拖放 | 拖拽文件到输入框区域自动上传 | **文件类型白名单**(`utils/fileHelper.js`): ```javascript export const ALLOWED_EXTENSIONS = [ // 文档 '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', // 图片 '.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', ]; export const MAX_FILE_SIZE_MB = 20; // 单文件最大 20MB export const validateFile = (file) => { const ext = path.extname(file.name).toLowerCase(); if (!ALLOWED_EXTENSIONS.includes(ext)) { return { valid: false, error: `不支持的文件类型:${ext}` }; } if (file.size > MAX_FILE_SIZE_MB * 1024 * 1024) { return { valid: false, error: `文件超过 ${MAX_FILE_SIZE_MB}MB 限制` }; } return { valid: true }; }; ``` **语音输入实现**(`VoiceButton.jsx`): ```javascript const startRecording = () => { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { alert('当前浏览器不支持语音输入'); return; } const recognition = new SpeechRecognition(); recognition.lang = 'zh-CN'; recognition.continuous = false; recognition.interimResults = false; recognition.onresult = (e) => { const transcript = e.results[0][0].transcript; onTranscript(transcript); // 回填到输入框 }; recognition.start(); setIsRecording(true); }; ``` --- ### 4.5 Dify API 对接 #### 4.5.1 主进程侧实现(`electron/dify.js`) 所有 HTTP 请求在**主进程**发起(规避渲染进程 CORS 限制)。 ```javascript // electron/dify.js const { net } = require('electron'); /** * 发送对话请求并通过 IPC 流式推送内容到渲染进程 * @param {BrowserWindow} win - 主窗口 * @param {Object} config - { baseUrl, apiKey } * @param {Object} payload - { query, conversationId, files, userId } */ async function sendDifyMessage(win, config, payload) { const { baseUrl, apiKey } = config; const url = `${baseUrl}/v1/chat-messages`; const body = { inputs: {}, query: payload.query, response_mode: 'streaming', conversation_id: payload.conversationId || '', user: payload.userId || 'electron-user', files: payload.files || [], }; const request = net.request({ method: 'POST', url, headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, }); request.on('response', (response) => { let buffer = ''; response.on('data', (chunk) => { buffer += chunk.toString(); const lines = buffer.split('\n'); buffer = lines.pop(); // 保留不完整行 for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6).trim(); if (data === '[DONE]') { win.webContents.send('dify:stream-done'); return; } try { const parsed = JSON.parse(data); // Dify 流式事件类型 if (parsed.event === 'message') { win.webContents.send('dify:stream-chunk', { answer: parsed.answer, conversationId: parsed.conversation_id, messageId: parsed.message_id, }); } else if (parsed.event === 'message_end') { win.webContents.send('dify:stream-done', { conversationId: parsed.conversation_id, metadata: parsed.metadata, }); } else if (parsed.event === 'error') { win.webContents.send('dify:stream-error', parsed); } } catch (e) { // 忽略解析错误 } } } }); response.on('error', (err) => { win.webContents.send('dify:stream-error', { message: err.message }); }); }); request.on('error', (err) => { win.webContents.send('dify:stream-error', { message: err.message }); }); request.write(JSON.stringify(body)); request.end(); } /** * 上传文件到 Dify 获取 file_id */ async function uploadFileToDify(config, filePath, mimeType) { // 使用 form-data 上传,返回 { id, name, size, mime_type } // 实现略,使用 net.request 发送 multipart/form-data } module.exports = { sendDifyMessage, uploadFileToDify }; ``` #### 4.5.2 IPC 注册(`electron/main.js`) ```javascript const { sendDifyMessage, uploadFileToDify } = require('./dify'); const store = require('./store'); // 发送消息 ipcMain.handle('dify:send', async (event, payload) => { const config = store.get('difyConfig'); if (!config?.baseUrl || !config?.apiKey) { return { error: 'Dify 未配置,请在设置中填写 API 地址和 Key' }; } await sendDifyMessage(mainWindow, config, payload); return { ok: true }; }); // 上传文件 ipcMain.handle('dify:upload-file', async (event, { filePath, mimeType }) => { const config = store.get('difyConfig'); return await uploadFileToDify(config, filePath, mimeType); }); // 停止生成(发送 stop 请求到 Dify) ipcMain.handle('dify:stop', async (event, { taskId }) => { const config = store.get('difyConfig'); // POST /v1/chat-messages/:task_id/stop }); ``` #### 4.5.3 渲染进程监听(`useAppStore.js`) ```javascript // 启动流式监听 useEffect(() => { const removeChunk = window.electronAPI.on('dify:stream-chunk', (data) => { setMessages(prev => { const last = prev[prev.length - 1]; if (last?.role === 'assistant' && last.isStreaming) { return [...prev.slice(0, -1), { ...last, content: last.content + data.answer }]; } return prev; }); }); const removeDone = window.electronAPI.on('dify:stream-done', (data) => { setMessages(prev => { const last = prev[prev.length - 1]; if (last?.role === 'assistant') { return [...prev.slice(0, -1), { ...last, isStreaming: false }]; } return prev; }); if (data?.conversationId) setCurrentConversationId(data.conversationId); }); return () => { removeChunk(); removeDone(); }; }, []); ``` --- ### 4.6 历史对话管理 **数据来源**:对话列表持久化在 `electron-store`,每次对话完成后同步写入。 **Sidebar.jsx 功能**: - 顶部「+ 新建对话」按钮 - 历史对话列表(`ConversationList.jsx`) - 每项显示:对话标题(取第一条用户消息前 20 字)、时间 - 当前激活项高亮 - 右键或悬浮显示「删除」按钮 - 列表可滚动,超出高度时显示滚动条 **对话状态管理**: ```javascript // store/useAppStore.js(Zustand 示例) const useAppStore = create((set, get) => ({ conversations: [], // 历史对话列表 currentConversationId: null,// 当前对话 ID messages: [], // 当前对话消息 createNewConversation: () => { const id = uuid(); const newConv = { id, title: '新对话', createdAt: Date.now(), messages: [] }; set(state => ({ conversations: [newConv, ...state.conversations], currentConversationId: id, messages: [], })); window.electronAPI.saveConversations([newConv, ...get().conversations]); }, switchConversation: (id) => { const conv = get().conversations.find(c => c.id === id); set({ currentConversationId: id, messages: conv?.messages || [] }); }, deleteConversation: (id) => { const updated = get().conversations.filter(c => c.id !== id); set({ conversations: updated }); if (get().currentConversationId === id) { set({ currentConversationId: null, messages: [] }); } window.electronAPI.saveConversations(updated); }, })); ``` --- ### 4.7 用户头像及菜单 **UserAvatar.jsx**: - 固定在左侧栏底部 - 显示圆形头像(暂用首字母占位,颜色随机固定) - 显示用户名(截断超长文字) - 点击弹出 Popover 菜单: - ⚙️ 设置 - 🚪 退出登录 ```jsx const UserAvatar = ({ user, onOpenSettings, onLogout }) => { const initial = user.username?.[0]?.toUpperCase() || 'U'; return (
}>
{initial}
{user.username}
); }; ``` --- ### 4.8 设置窗口 **触发方式**:点击用户菜单「设置」,以**模态弹窗**形式出现(不另开 BrowserWindow,在渲染层实现 Modal)。 **设置面板 Tab 结构**: #### Tab 1:通用 | 设置项 | 类型 | 说明 | |--------|------|------| | 开机自启动 | Switch 开关 | 调用 `app.setLoginItemSettings({ openAtLogin })` | | 主题 | 单选(亮色 / 暗色 / 跟随系统) | 持久化,下次启动生效 | | 语言 | 下拉(暂仅中文) | 预留 | #### Tab 2:Dify 配置 | 配置项 | 类型 | 说明 | |--------|------|------| | API 地址 | 文本输入 | 例:`https://api.dify.ai` | | API Key | 文本输入(密码型) | Dify 应用的 Secret Key | | 应用 ID | 文本输入(可选) | 用于标识 Dify 应用 | | 「测试连接」按钮 | Button | 发送测试请求,显示成功/失败状态 | | 「保存」按钮 | Button | 写入 electron-store | #### Tab 3:关于 - 应用名称 + Logo - 版本号(从 `package.json` 读取) - Slogan - 开发信息 / 开源协议链接 - 「检查更新」按钮(预留) **IPC 配置读写**: ```javascript // preload.js contextBridge.exposeInMainWorld('electronAPI', { getSettings: () => ipcRenderer.invoke('settings:get'), saveSettings: (settings) => ipcRenderer.invoke('settings:save', settings), testDifyConnection: (config) => ipcRenderer.invoke('dify:test', config), setAutoLaunch: (enabled) => ipcRenderer.invoke('app:set-auto-launch', enabled), }); ``` --- ### 4.9 主题切换 **实现方案**:CSS 变量 + `data-theme` 属性 + `nativeTheme` 监听。 **CSS 变量定义**(`index.css`): ```css :root[data-theme="light"] { --bg-primary: #ffffff; --bg-secondary: #f5f5f7; --bg-sidebar: #f0f0f2; --text-primary: #1d1d1f; --text-secondary: #6e6e73; --border-color: #d1d1d6; --accent-color: #0071e3; --bubble-user-bg: #0071e3; --bubble-user-text: #ffffff; --bubble-ai-bg: #f5f5f7; --bubble-ai-text: #1d1d1f; --shadow: 0 2px 20px rgba(0,0,0,0.08); } :root[data-theme="dark"] { --bg-primary: #1c1c1e; --bg-secondary: #2c2c2e; --bg-sidebar: #242426; --text-primary: #f5f5f7; --text-secondary: #98989d; --border-color: #3a3a3c; --accent-color: #0a84ff; --bubble-user-bg: #0a84ff; --bubble-user-text: #ffffff; --bubble-ai-bg: #2c2c2e; --bubble-ai-text: #f5f5f7; --shadow: 0 2px 20px rgba(0,0,0,0.4); } ``` **主题逻辑**(`useTheme.js`): ```javascript export const useTheme = () => { const [theme, setTheme] = useState('system'); // 'light' | 'dark' | 'system' const applyTheme = (t) => { const resolved = t === 'system' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : t; document.documentElement.setAttribute('data-theme', resolved); }; useEffect(() => { const saved = localStorage.getItem('theme') || 'system'; setTheme(saved); applyTheme(saved); // 监听系统主题变化 const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handler = () => { if (theme === 'system') applyTheme('system'); }; mediaQuery.addEventListener('change', handler); return () => mediaQuery.removeEventListener('change', handler); }, [theme]); const changeTheme = (t) => { setTheme(t); localStorage.setItem('theme', t); applyTheme(t); window.electronAPI.saveSettings({ theme: t }); }; return { theme, changeTheme }; }; ``` --- ## 5. 数据模型 ```typescript // 用户 interface User { username: string; avatar?: string; // base64 或 URL,暂为 null } // 对话(持久化单元) interface Conversation { id: string; // uuid title: string; // 自动从第一条消息截取 createdAt: number; // timestamp updatedAt: number; difyConversationId?: string; // Dify 侧 conversation_id messages: Message[]; } // 消息 interface Message { id: string; role: 'user' | 'assistant'; content: string; // Markdown 文本 files?: UploadedFile[]; timestamp: number; isStreaming?: boolean; } // 上传文件 interface UploadedFile { localPath: string; // 本地路径(主进程读取用) name: string; size: number; mimeType: string; difyFileId?: string; // 上传到 Dify 后的 file_id previewUrl?: string; // 图片预览 blob URL } // 应用设置 interface AppSettings { theme: 'light' | 'dark' | 'system'; autoLaunch: boolean; difyConfig: { baseUrl: string; apiKey: string; appId?: string; }; user?: User; } ``` --- ## 6. IPC 通信协议 ### 主进程 → 渲染进程(事件推送) | 事件名 | Payload | 说明 | |--------|---------|------| | `dify:stream-chunk` | `{ answer: string, conversationId: string, messageId: string }` | 流式内容片段 | | `dify:stream-done` | `{ conversationId: string, metadata?: object }` | 流式完成 | | `dify:stream-error` | `{ message: string }` | 流式错误 | | `app:theme-changed` | `{ theme: 'light' \| 'dark' }` | 系统主题变化 | ### 渲染进程 → 主进程(invoke,双向) | 通道名 | 参数 | 返回 | 说明 | |--------|------|------|------| | `dify:send` | `{ query, conversationId?, files?, userId }` | `{ ok } \| { error }` | 发送消息 | | `dify:upload-file` | `{ filePath, mimeType }` | `{ id, name }` | 上传文件 | | `dify:stop` | `{ taskId }` | `{ ok }` | 停止生成 | | `dify:test` | `{ baseUrl, apiKey }` | `{ ok } \| { error }` | 测试连接 | | `settings:get` | — | `AppSettings` | 读取全部设置 | | `settings:save` | `Partial` | `{ ok }` | 保存设置 | | `app:set-auto-launch` | `boolean` | `{ ok }` | 设置开机自启 | | `app:get-version` | — | `string` | 获取版本号 | | `dialog:open-file` | `{ filters }` | `string[] \| null` | 打开文件对话框 | ### Preload 暴露接口 ```javascript // electron/preload.js const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { // 发送 invoke send: (channel, data) => ipcRenderer.invoke(channel, data), // 监听事件(返回取消函数) on: (channel, callback) => { const handler = (_, ...args) => callback(...args); ipcRenderer.on(channel, handler); return () => ipcRenderer.removeListener(channel, handler); }, // 便捷方法 getSettings: () => ipcRenderer.invoke('settings:get'), saveSettings: (s) => ipcRenderer.invoke('settings:save', s), sendDifyMessage: (payload) => ipcRenderer.invoke('dify:send', payload), uploadFile: (payload) => ipcRenderer.invoke('dify:upload-file', payload), stopGeneration: (taskId) => ipcRenderer.invoke('dify:stop', { taskId }), testDifyConnection: (config) => ipcRenderer.invoke('dify:test', config), setAutoLaunch: (enabled) => ipcRenderer.invoke('app:set-auto-launch', enabled), getAppVersion: () => ipcRenderer.invoke('app:get-version'), openFileDialog: (filters) => ipcRenderer.invoke('dialog:open-file', { filters }), saveConversations: (convs) => ipcRenderer.invoke('settings:save', { conversations: convs }), }); ``` --- ## 7. Dify API 集成规范 ### 7.1 发送对话消息 ``` POST {baseUrl}/v1/chat-messages Authorization: Bearer {apiKey} Content-Type: application/json { "inputs": {}, "query": "用户输入的文字", "response_mode": "streaming", "conversation_id": "", // 空字符串表示新建对话 "user": "electron-user-001", "files": [ { "type": "image", // image / document "transfer_method": "remote_url", // 或 local_file "url": "...", // transfer_method=remote_url 时 "upload_file_id": "...", // transfer_method=local_file 时,先调用上传接口获取 } ] } ``` ### 7.2 上传文件 ``` POST {baseUrl}/v1/files/upload Authorization: Bearer {apiKey} Content-Type: multipart/form-data file: user: electron-user-001 ``` 响应:`{ id, name, size, mime_type, created_by, created_at }` ### 7.3 流式响应事件 | event | 包含字段 | 说明 | |-------|---------|------| | `message` | `answer`, `conversation_id`, `message_id`, `created_at` | 文字片段 | | `message_end` | `conversation_id`, `metadata` | 生成完毕 | | `error` | `status`, `code`, `message` | 错误 | | `ping` | — | 保活 | ### 7.4 停止生成 ``` POST {baseUrl}/v1/chat-messages/{task_id}/stop Authorization: Bearer {apiKey} Content-Type: application/json { "user": "electron-user-001" } ``` --- ## 8. UI/UX 设计规范 ### 8.1 品牌色彩 | 名称 | 亮色值 | 暗色值 | 用途 | |------|--------|--------|------| | 主色 Accent | `#0071e3` | `#0a84ff` | 按钮、链接、选中态 | | 背景主 | `#ffffff` | `#1c1c1e` | 主内容区 | | 背景次 | `#f5f5f7` | `#2c2c2e` | 卡片、气泡 | | 侧栏背景 | `#f0f0f2` | `#242426` | 左侧栏 | | 文字主 | `#1d1d1f` | `#f5f5f7` | 正文 | | 文字次 | `#6e6e73` | `#98989d` | 副文字、时间戳 | ### 8.2 字体 - 主字体:`-apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif` - 代码字体:`'JetBrains Mono', 'Cascadia Code', monospace` ### 8.3 间距与圆角 - 基础间距单位:`8px` - 消息气泡圆角:`12px`(用户侧右下角为 `4px`) - 按钮圆角:`8px` - 模态弹窗圆角:`16px` - 侧栏宽度:`240px`(固定,不可拖拽) ### 8.4 动效 - 消息出现:`opacity 0.2s ease, transform 0.2s ease`(从下方淡入) - 流式输出光标:`blink 1s step-end infinite` - 主题切换:`background-color 0.25s ease, color 0.25s ease` - 弹窗:`scale 0.95→1 + opacity 0.15s ease` ### 8.5 顶部区域 主界面顶部展示区: ``` [ Logo图标 未来智财 ] 你的私人财务助理——基于 Dify 智能体工作流的企业级对话应用 [ 🌙/☀️ ] ``` - Logo + 应用名居左 - Slogan 居中(文字颜色用 `--text-secondary`) - 主题切换按钮居右 - 整体高度 `56px`,下方有 `1px` 分割线 --- ## 9. 构建与打包 ### 9.1 开发环境启动 ```bash # 安装依赖 npm install # 同时启动 Vite 渲染层和 Electron 主进程 npm run dev ``` **package.json scripts**: ```json { "scripts": { "dev": "concurrently \"vite\" \"electron .\"", "build": "vite build && electron-builder", "build:win": "electron-builder --win", "build:mac": "electron-builder --mac" } } ``` ### 9.2 electron-builder 配置 ```json { "build": { "appId": "com.weizhi.caifa", "productName": "未来智财", "directories": { "output": "release" }, "files": ["dist/**/*", "electron/**/*"], "win": { "target": "nsis", "icon": "assets/icon.ico" }, "mac": { "target": "dmg", "icon": "assets/icon.icns", "category": "public.app-category.finance" }, "nsis": { "oneClick": false, "allowToChangeInstallationDirectory": true, "installerIcon": "assets/icon.ico", "installerHeaderIcon": "assets/icon.ico", "createDesktopShortcut": true, "createStartMenuShortcut": true, "shortcutName": "未来智财" } } } ``` ### 9.3 Vite 配置要点 ```javascript // vite.config.js import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], base: './', // 打包后使用相对路径 build: { outDir: 'dist', emptyOutDir: true, }, server: { port: 5173, }, }); ``` **Electron 加载渲染层**: ```javascript // electron/main.js if (process.env.NODE_ENV === 'development') { mainWindow.loadURL('http://localhost:5173'); mainWindow.webContents.openDevTools(); } else { mainWindow.loadFile(path.join(__dirname, '../dist/index.html')); } ``` --- ## 10. 开发任务清单 Claude Code 按以下顺序实现,确保每个阶段可独立运行验证: ### Phase 1:项目骨架 - [ ] 初始化 `package.json`,安装全部依赖 - [ ] 配置 `vite.config.js` + `electron/main.js` 基础窗口 - [ ] 配置 `preload.js` contextBridge 基础结构 - [ ] 实现 CSS 变量主题系统(亮色/暗色) - [ ] 搭建 React 根组件路由(Login ↔ Main) ### Phase 2:主界面布局 - [ ] 实现左侧栏 `Sidebar.jsx` 基础结构 - [ ] 实现顶部 Header(Logo + Slogan + 主题切换) - [ ] 实现主内容区 `ChatWindow.jsx` 空状态 - [ ] 实现底部 `InputBar.jsx` 基础文本输入 + 发送 ### Phase 3:登录界面 - [ ] 实现 `LoginPage.jsx` 完整 UI - [ ] 实现跳过登录逻辑 - [ ] 登录状态持久化(electron-store) ### Phase 4:对话核心 - [ ] 实现 `MessageBubble.jsx` + Markdown 渲染 - [ ] 实现流式打字动效 - [ ] 对接主进程 Dify 流式请求(`electron/dify.js`) - [ ] 完整 IPC 链路联调 ### Phase 5:多模态输入 - [ ] 文件上传按钮 + 文件类型校验 - [ ] 文件预览缩略图(图片显示预览,文档显示图标) - [ ] 语音输入按钮(Web Speech API) - [ ] 拖拽上传支持 ### Phase 6:历史对话 - [ ] 历史对话列表渲染 - [ ] 新建对话功能 - [ ] 切换对话(加载历史消息) - [ ] 删除对话 - [ ] 对话持久化(electron-store) ### Phase 7:设置与用户菜单 - [ ] 用户头像 + 弹出菜单 - [ ] 设置弹窗 3 个 Tab 实现 - [ ] Dify API 配置 + 测试连接 - [ ] 开机自启开关 - [ ] 退出登录逻辑 ### Phase 8:打磨与打包 - [ ] 动效添加 - [ ] 错误状态处理(网络失败、Dify 配置缺失等) - [ ] 窗口控制按钮(关闭/最小化/最大化,frameless 模式) - [ ] electron-builder 打包配置 - [ ] macOS / Windows 双平台验证 --- ## 附录:参考文档 - Electron 官方文档:https://www.electronjs.org/docs/latest - Electron API 参考:https://www.electronjs.org/docs/latest/api/app - Dify API 文档:https://docs.dify.ai/api-reference/chat - electron-store:https://github.com/sindresorhus/electron-store - Marked.js:https://marked.js.org - Vite:https://vitejs.dev --- *文档版本:v1.0 | 生成日期:2026-05-19*