├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.yaml ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── README.md ├── build ├── entitlements.mac.plist ├── icon.icns ├── icon.ico ├── icon.png ├── icon1.ico └── icon1.png ├── dev-app-update.yml ├── electron-builder.yml ├── electron.vite.config.mjs ├── package-lock.json ├── package.json ├── resources └── icon.png └── src ├── main └── index.js ├── preload └── index.js └── renderer ├── index.html └── src ├── App.vue ├── assets ├── CMB.png ├── ai.png ├── base.css ├── home.png ├── icon.png ├── logo.svg └── main.css ├── components ├── ECharts │ ├── chalk.json │ └── index.vue ├── Intro │ └── index.vue ├── KLine │ ├── data.js │ └── index.vue └── Layout │ ├── foot.vue │ ├── header.vue │ ├── index.vue │ └── leftMenu.vue ├── main.js ├── router └── index.js └── views ├── AiChat ├── SSEService.js ├── components │ ├── ChatList.vue │ ├── Header.vue │ └── SideBar.vue ├── img │ ├── CMB.png │ ├── ai.png │ └── user.png ├── index.vue └── util.js ├── login └── index.vue └── main └── index.vue /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | .gitignore 5 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | extends: [ 6 | 'eslint:recommended', 7 | 'plugin:vue/vue3-recommended', 8 | '@electron-toolkit', 9 | '@vue/eslint-config-prettier' 10 | ], 11 | rules: { 12 | 'vue/require-default-prop': 'off', 13 | 'vue/multi-word-component-names': 'off' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | .DS_Store 5 | *.log* 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | electron_mirror=https://npmmirror.com/mirrors/electron/ 2 | electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | pnpm-lock.yaml 4 | LICENSE.md 5 | tsconfig.json 6 | tsconfig.*.json 7 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | singleQuote: true 2 | semi: false 3 | printWidth: 100 4 | trailingComma: none 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Main Process", 6 | "type": "node", 7 | "request": "launch", 8 | "cwd": "${workspaceRoot}", 9 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", 10 | "windows": { 11 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" 12 | }, 13 | "runtimeArgs": ["--sourcemap"], 14 | "env": { 15 | "REMOTE_DEBUGGING_PORT": "9222" 16 | } 17 | }, 18 | { 19 | "name": "Debug Renderer Process", 20 | "port": 9222, 21 | "request": "attach", 22 | "type": "chrome", 23 | "webRoot": "${workspaceFolder}/src/renderer", 24 | "timeout": 60000, 25 | "presentation": { 26 | "hidden": true 27 | } 28 | } 29 | ], 30 | "compounds": [ 31 | { 32 | "name": "Debug All", 33 | "configurations": ["Debug Main Process", "Debug Renderer Process"], 34 | "presentation": { 35 | "order": 1 36 | } 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[typescript]": { 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | }, 5 | "[javascript]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "[json]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FT金融终端 2 | ## electron-vite-vue-ft 金融终端项目 3 | 4 | 带大家一步步实现一个简易的金融终端系统的核心功能 5 | 【持续更新中...】 6 | 7 | ![014eaf5b0e6731a8012043d8b8e1f6.jpg](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c5dfe1033a37492ca35e617a6ece4d32~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1920&h=1200&s=5984905&e=gif&f=151&b=1c192f) 8 | 9 | ## 开发说明 10 | ``` bash 11 | # install dependencies 12 | npm install 13 | 14 | npm run dev 15 | 16 | npm run build 17 | 18 | ``` 19 | ## 掘金配套系列文章 20 | 21 | ![《金融行业前端探索》(1)开篇使用electron-vite-vue 搭建一个金融终端框架之登录功能](https://juejin.cn/post/7344625554530451496) 22 | -------------------------------------------------------------------------------- /build/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.cs.allow-unsigned-executable-memory 8 | 9 | com.apple.security.cs.allow-dyld-environment-variables 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/build/icon.icns -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/build/icon.ico -------------------------------------------------------------------------------- /build/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/build/icon.png -------------------------------------------------------------------------------- /build/icon1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/build/icon1.ico -------------------------------------------------------------------------------- /build/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/build/icon1.png -------------------------------------------------------------------------------- /dev-app-update.yml: -------------------------------------------------------------------------------- 1 | provider: generic 2 | url: https://example.com/auto-updates 3 | updaterCacheDirName: electron-vite-vue-ft-updater 4 | -------------------------------------------------------------------------------- /electron-builder.yml: -------------------------------------------------------------------------------- 1 | appId: com.electron.app 2 | productName: electron-vite-vue-ft 3 | directories: 4 | buildResources: build 5 | files: 6 | - '!**/.vscode/*' 7 | - '!src/*' 8 | - '!electron.vite.config.{js,ts,mjs,cjs}' 9 | - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' 10 | - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' 11 | asarUnpack: 12 | - resources/** 13 | win: 14 | executableName: electron-vite-vue-ft 15 | nsis: 16 | artifactName: ${name}-${version}-setup.${ext} 17 | shortcutName: ${productName} 18 | uninstallDisplayName: ${productName} 19 | createDesktopShortcut: always 20 | mac: 21 | entitlementsInherit: build/entitlements.mac.plist 22 | extendInfo: 23 | - NSCameraUsageDescription: Application requests access to the device's camera. 24 | - NSMicrophoneUsageDescription: Application requests access to the device's microphone. 25 | - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. 26 | - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. 27 | notarize: false 28 | dmg: 29 | artifactName: ${name}-${version}.${ext} 30 | linux: 31 | target: 32 | - AppImage 33 | - snap 34 | - deb 35 | maintainer: electronjs.org 36 | category: Utility 37 | appImage: 38 | artifactName: ${name}-${version}.${ext} 39 | npmRebuild: false 40 | publish: 41 | provider: generic 42 | url: https://example.com/auto-updates 43 | electronDownload: 44 | mirror: https://npmmirror.com/mirrors/electron/ 45 | -------------------------------------------------------------------------------- /electron.vite.config.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 3 | * @Version: 1.0 4 | * @Autor: codercao 5 | * @Date: 2024-03-07 22:56:26 6 | * @LastEditors: codercao 7 | * @LastEditTime: 2024-03-08 22:59:55 8 | */ 9 | import { resolve } from 'path' 10 | import { defineConfig, externalizeDepsPlugin } from 'electron-vite' 11 | import vue from '@vitejs/plugin-vue' 12 | 13 | export default defineConfig({ 14 | main: { 15 | plugins: [externalizeDepsPlugin()] 16 | }, 17 | preload: { 18 | plugins: [externalizeDepsPlugin()] 19 | }, 20 | renderer: { 21 | resolve: { 22 | alias: { 23 | '@renderer': resolve('src/renderer/src') 24 | } 25 | }, 26 | plugins: [vue()] 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FT金融终端", 3 | "version": "1.0.0", 4 | "description": "An Electron application with Vue", 5 | "main": "./out/main/index.js", 6 | "author": "codercao", 7 | "homepage": "https://electron-vite.org", 8 | "scripts": { 9 | "format": "prettier --write .", 10 | "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", 11 | "start": "electron-vite preview", 12 | "dev": "electron-vite dev", 13 | "build": "electron-vite build", 14 | "postinstall": "electron-builder install-app-deps", 15 | "build:unpack": "npm run build && electron-builder --dir", 16 | "build:win": "npm run build && electron-builder --win", 17 | "build:mac": "npm run build && electron-builder --mac", 18 | "build:linux": "npm run build && electron-builder --linux" 19 | }, 20 | "dependencies": { 21 | "@electron-toolkit/preload": "^3.0.0", 22 | "@electron-toolkit/utils": "^3.0.0", 23 | "@element-plus/icons-vue": "^2.3.1", 24 | "echarts": "^5.5.0", 25 | "electron-updater": "^6.1.7", 26 | "element-plus": "^2.6.1", 27 | "vue-router": "^4.3.0" 28 | }, 29 | "devDependencies": { 30 | "@electron-toolkit/eslint-config": "^1.0.1", 31 | "@rushstack/eslint-patch": "^1.6.1", 32 | "@vitejs/plugin-vue": "^5.0.3", 33 | "@vue/eslint-config-prettier": "^9.0.0", 34 | "electron": "^28.2.0", 35 | "electron-builder": "^24.9.1", 36 | "electron-vite": "^2.0.0", 37 | "eslint": "^8.56.0", 38 | "eslint-plugin-vue": "^9.20.1", 39 | "prettier": "^3.2.4", 40 | "sass": "^1.71.1", 41 | "vite": "^5.0.12", 42 | "vue": "^3.4.15" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/resources/icon.png -------------------------------------------------------------------------------- /src/main/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 3 | * @Version: 1.0 4 | * @Autor: codercao 5 | * @Date: 2024-03-07 22:56:26 6 | * @LastEditors: codercao 7 | * @LastEditTime: 2024-04-01 21:42:23 8 | */ 9 | import { app, shell, BrowserWindow, ipcMain, Tray, Menu, screen } from 'electron' 10 | import { join } from 'path' 11 | import { electronApp, optimizer, is } from '@electron-toolkit/utils' 12 | 13 | // package.json 14 | import pkg from '../../package.json' 15 | 16 | import icon from '../../resources/icon.png?asset' 17 | 18 | let mainWindow, loginWindow 19 | const winURL = is.dev ? `http://localhost:5173` : `file://${__dirname}/index.html` 20 | 21 | const loginURL = is.dev ? `http://localhost:5173/login` : `file://${__dirname}/index.html/login` 22 | 23 | const path = require('path') 24 | const ApplicationName = pkg.name 25 | // 托盘对象 26 | let appTray = null 27 | // 是否可以退出 28 | let trayClose = false 29 | // 系统托盘右键菜单 30 | let trayMenuTemplate 31 | // 系统托盘图标 32 | let iconPath 33 | // 图标的上上下文 34 | let contextMenu 35 | // 图标闪烁定时器 36 | let flashTrayTimer 37 | // 单一实例 38 | 39 | /** 40 | * 设置系统托盘 41 | */ 42 | function createTray() { 43 | // 创建 Tray 对象并设置图标 44 | appTray = new Tray(icon) 45 | // 创建菜单并设置到 Tray 对象中 46 | trayMenuTemplate = [ 47 | { 48 | label: 'FT金融终端', 49 | click: function () { 50 | // 打开外部链接 51 | shell.openExternal('https://github.com/HongqingCao/electron-vite-vue-ft') 52 | } 53 | }, 54 | { 55 | label: '退出FT', 56 | click: function () { 57 | // 退出 58 | trayClose = true 59 | app.quit() 60 | } 61 | } 62 | ] 63 | 64 | appTray.setToolTip('FT金融终端') 65 | // 图标的上上下文 66 | contextMenu = Menu.buildFromTemplate(trayMenuTemplate) 67 | 68 | // 设置此图标的上下文菜单 69 | appTray.setContextMenu(contextMenu) 70 | // 主窗口显示隐藏切换 71 | appTray.on('click', () => { 72 | // 清楚图标闪烁定时器 73 | clearInterval(flashTrayTimer) 74 | flashTrayTimer = null 75 | // 还原图标 76 | appTray.setImage(icon) 77 | if (loginWindow) { 78 | loginWindow.isVisible() ? loginWindow.hide() : loginWindow.show() 79 | } 80 | 81 | if (mainWindow) { 82 | mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show() 83 | } 84 | }) 85 | } 86 | 87 | /** 88 | * 创建登录窗口 89 | */ 90 | function createLoginWindow() { 91 | if (loginWindow) { 92 | return 93 | } 94 | 95 | loginWindow = new BrowserWindow({ 96 | show: true, 97 | width: 768, 98 | height: 480, 99 | frame: false, // 无边框 100 | transparent: true, // 透明 101 | resizable: false, 102 | webPreferences: { 103 | preload: join(__dirname, '../preload/index.js'), 104 | sandbox: false 105 | } 106 | }) 107 | 108 | loginWindow.loadURL(loginURL) 109 | 110 | loginWindow.once('ready-to-show', () => { 111 | loginWindow.show() 112 | }) 113 | 114 | loginWindow.on('closed', () => { 115 | loginWindow = null 116 | }) 117 | } 118 | 119 | function createMainWindow() { 120 | // Create the browser window 121 | mainWindow = new BrowserWindow({ 122 | height: 800, 123 | width: 1200, 124 | show: false, 125 | frame: false, 126 | autoHideMenuBar: true, 127 | ...(process.platform === 'linux' ? { icon } : {}), 128 | webPreferences: { 129 | preload: join(__dirname, '../preload/index.js'), 130 | sandbox: false 131 | } 132 | }) 133 | 134 | mainWindow.on('ready-to-show', () => { 135 | mainWindow.show() 136 | }) 137 | 138 | mainWindow.webContents.setWindowOpenHandler((details) => { 139 | shell.openExternal(details.url) 140 | return { action: 'deny' } 141 | }) 142 | 143 | // HMR for renderer base on electron-vite cli. 144 | // Load the remote URL for development or the local html file for production. 145 | if (is.dev && process.env['ELECTRON_RENDERER_URL']) { 146 | mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) 147 | } else { 148 | mainWindow.loadFile(join(__dirname, '../renderer/index.html')) 149 | } 150 | } 151 | 152 | // This method will be called when Electron has finished 153 | // initialization and is ready to create browser windows. 154 | // Some APIs can only be used after this event occurs. 155 | app.whenReady().then(() => { 156 | // Set app user model id for windows 157 | electronApp.setAppUserModelId('com.electron') 158 | 159 | app.on('browser-window-created', (_, window) => { 160 | optimizer.watchWindowShortcuts(window) 161 | }) 162 | 163 | ipcMain.on('minimize', () => { 164 | if (loginWindow) { 165 | loginWindow.minimize() 166 | } 167 | if (mainWindow) { 168 | mainWindow.minimize() 169 | } 170 | }) 171 | 172 | ipcMain.on('close', () => { 173 | if (loginWindow) { 174 | loginWindow.destroy() 175 | } 176 | if (mainWindow) { 177 | mainWindow.destroy() 178 | } 179 | }) 180 | 181 | ipcMain.on('maximize', () => { 182 | mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize() 183 | }) 184 | 185 | // 打开主页 186 | ipcMain.on('openMainWindow', () => { 187 | if (!mainWindow) { 188 | createMainWindow() 189 | } 190 | loginWindow.destroy() 191 | loginWindow = null 192 | }) 193 | 194 | ipcMain.on('openLoginWindow', () => { 195 | if (!loginWindow) { 196 | createLoginWindow() 197 | } 198 | mainWindow.destroy() 199 | loginWindow.show() 200 | loginWindow.focus() 201 | }) 202 | 203 | createLoginWindow() 204 | createTray() 205 | app.on('activate', function () { 206 | if (BrowserWindow.getAllWindows().length === 0) createLoginWindow() 207 | }) 208 | }) 209 | 210 | // Quit when all windows are closed, except on macOS. There, it's common 211 | // for applications and their menu bar to stay active until the user quits 212 | // explicitly with Cmd + Q. 213 | app.on('window-all-closed', () => { 214 | if (process.platform !== 'darwin') { 215 | app.quit() 216 | } 217 | }) 218 | 219 | // In this file you can include the rest of your app"s specific main process 220 | // code. You can also put them in separate files and require them here. 221 | -------------------------------------------------------------------------------- /src/preload/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 3 | * @Version: 1.0 4 | * @Autor: codercao 5 | * @Date: 2024-03-07 22:56:26 6 | * @LastEditors: codercao 7 | * @LastEditTime: 2024-03-09 23:17:18 8 | */ 9 | import { contextBridge } from 'electron' 10 | import { electronAPI } from '@electron-toolkit/preload' 11 | 12 | // Custom APIs for renderer 13 | const api = {} 14 | 15 | // Use `contextBridge` APIs to expose Electron APIs to 16 | // renderer only if context isolation is enabled, otherwise 17 | // just add to the DOM global. 18 | console.log("process",process) 19 | if (process.contextIsolated) { 20 | try { 21 | contextBridge.exposeInMainWorld('electron', electronAPI) 22 | contextBridge.exposeInMainWorld('api', api) 23 | } catch (error) { 24 | console.error(error) 25 | } 26 | } else { 27 | window.electron = electronAPI 28 | window.api = api 29 | } 30 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | Electron 14 | 15 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/renderer/src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 14 | 19 | 20 | 55 | -------------------------------------------------------------------------------- /src/renderer/src/assets/CMB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/src/renderer/src/assets/CMB.png -------------------------------------------------------------------------------- /src/renderer/src/assets/ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/src/renderer/src/assets/ai.png -------------------------------------------------------------------------------- /src/renderer/src/assets/base.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ev-c-white: #ffffff; 3 | --ev-c-white-soft: #f8f8f8; 4 | --ev-c-white-mute: #f2f2f2; 5 | 6 | --ev-c-black: #1b1b1f; 7 | --ev-c-black-soft: #222222; 8 | --ev-c-black-mute: #282828; 9 | 10 | --ev-c-gray-1: #515c67; 11 | --ev-c-gray-2: #414853; 12 | --ev-c-gray-3: #32363f; 13 | 14 | --ev-c-text-1: rgba(255, 255, 245, 0.86); 15 | --ev-c-text-2: rgba(235, 235, 245, 0.6); 16 | --ev-c-text-3: rgba(235, 235, 245, 0.38); 17 | 18 | --ev-button-alt-border: transparent; 19 | --ev-button-alt-text: var(--ev-c-text-1); 20 | --ev-button-alt-bg: var(--ev-c-gray-3); 21 | --ev-button-alt-hover-border: transparent; 22 | --ev-button-alt-hover-text: var(--ev-c-text-1); 23 | --ev-button-alt-hover-bg: var(--ev-c-gray-2); 24 | } 25 | 26 | :root { 27 | --color-background: var(--ev-c-black); 28 | --color-background-soft: var(--ev-c-black-soft); 29 | --color-background-mute: var(--ev-c-black-mute); 30 | 31 | --color-text: var(--ev-c-text-1); 32 | } 33 | 34 | *, 35 | *::before, 36 | *::after { 37 | box-sizing: border-box; 38 | margin: 0; 39 | font-weight: normal; 40 | } 41 | 42 | ul { 43 | list-style: none; 44 | } 45 | 46 | body { 47 | min-height: 100vh; 48 | color: var(--color-text); 49 | background: var(--color-background); 50 | line-height: 1.6; 51 | font-family: 52 | Inter, 53 | -apple-system, 54 | BlinkMacSystemFont, 55 | 'Segoe UI', 56 | Roboto, 57 | Oxygen, 58 | Ubuntu, 59 | Cantarell, 60 | 'Fira Sans', 61 | 'Droid Sans', 62 | 'Helvetica Neue', 63 | sans-serif; 64 | text-rendering: optimizeLegibility; 65 | -webkit-font-smoothing: antialiased; 66 | -moz-osx-font-smoothing: grayscale; 67 | } 68 | -------------------------------------------------------------------------------- /src/renderer/src/assets/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/src/renderer/src/assets/home.png -------------------------------------------------------------------------------- /src/renderer/src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/src/renderer/src/assets/icon.png -------------------------------------------------------------------------------- /src/renderer/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /src/renderer/src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | 3 | body { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | overflow: hidden; 8 | background-image: url('./wavy-lines.svg'); 9 | background-size: cover; 10 | user-select: none; 11 | } 12 | 13 | code { 14 | font-weight: 600; 15 | padding: 3px 5px; 16 | border-radius: 2px; 17 | background-color: var(--color-background-mute); 18 | font-family: 19 | ui-monospace, 20 | SFMono-Regular, 21 | SF Mono, 22 | Menlo, 23 | Consolas, 24 | Liberation Mono, 25 | monospace; 26 | font-size: 85%; 27 | } 28 | 29 | #app { 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | flex-direction: column; 34 | margin-bottom: 80px; 35 | } 36 | 37 | .logo { 38 | margin-bottom: 20px; 39 | -webkit-user-drag: none; 40 | height: 128px; 41 | width: 128px; 42 | will-change: filter; 43 | transition: filter 300ms; 44 | } 45 | 46 | .logo:hover { 47 | filter: drop-shadow(0 0 1.2em #6988e6aa); 48 | } 49 | 50 | .creator { 51 | font-size: 14px; 52 | line-height: 16px; 53 | color: var(--ev-c-text-2); 54 | font-weight: 600; 55 | margin-bottom: 10px; 56 | } 57 | 58 | .text { 59 | font-size: 28px; 60 | color: var(--ev-c-text-1); 61 | font-weight: 700; 62 | line-height: 32px; 63 | text-align: center; 64 | margin: 0 10px; 65 | padding: 16px 0; 66 | } 67 | 68 | .tip { 69 | font-size: 16px; 70 | line-height: 24px; 71 | color: var(--ev-c-text-2); 72 | font-weight: 600; 73 | } 74 | 75 | .vue { 76 | background: -webkit-linear-gradient(315deg, #42d392 25%, #647eff); 77 | background-clip: text; 78 | -webkit-background-clip: text; 79 | -webkit-text-fill-color: transparent; 80 | font-weight: 700; 81 | } 82 | 83 | .actions { 84 | display: flex; 85 | padding-top: 32px; 86 | margin: -6px; 87 | flex-wrap: wrap; 88 | justify-content: flex-start; 89 | } 90 | 91 | .action { 92 | flex-shrink: 0; 93 | padding: 6px; 94 | } 95 | 96 | .action a { 97 | cursor: pointer; 98 | text-decoration: none; 99 | display: inline-block; 100 | border: 1px solid transparent; 101 | text-align: center; 102 | font-weight: 600; 103 | white-space: nowrap; 104 | border-radius: 20px; 105 | padding: 0 20px; 106 | line-height: 38px; 107 | font-size: 14px; 108 | border-color: var(--ev-button-alt-border); 109 | color: var(--ev-button-alt-text); 110 | background-color: var(--ev-button-alt-bg); 111 | } 112 | 113 | .action a:hover { 114 | border-color: var(--ev-button-alt-hover-border); 115 | color: var(--ev-button-alt-hover-text); 116 | background-color: var(--ev-button-alt-hover-bg); 117 | } 118 | 119 | .versions { 120 | position: absolute; 121 | bottom: 30px; 122 | margin: 0 auto; 123 | padding: 15px 0; 124 | font-family: 'Menlo', 'Lucida Console', monospace; 125 | display: inline-flex; 126 | overflow: hidden; 127 | align-items: center; 128 | border-radius: 22px; 129 | background-color: #202127; 130 | backdrop-filter: blur(24px); 131 | } 132 | 133 | .versions li { 134 | display: block; 135 | float: left; 136 | border-right: 1px solid var(--ev-c-gray-1); 137 | padding: 0 20px; 138 | font-size: 14px; 139 | line-height: 14px; 140 | opacity: 0.8; 141 | &:last-child { 142 | border: none; 143 | } 144 | } 145 | 146 | @media (max-width: 720px) { 147 | .text { 148 | font-size: 20px; 149 | } 150 | } 151 | 152 | @media (max-width: 620px) { 153 | .versions { 154 | display: none; 155 | } 156 | } 157 | 158 | @media (max-width: 350px) { 159 | .tip, 160 | .actions { 161 | display: none; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/renderer/src/components/ECharts/chalk.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "themeName": "chalk", 4 | "theme": { 5 | "seriesCnt": "3", 6 | "backgroundColor": "rgba(41,52,65,1)", 7 | "titleColor": "#ffffff", 8 | "subtitleColor": "#dddddd", 9 | "textColorShow": false, 10 | "textColor": "#333", 11 | "markTextColor": "#293441", 12 | "color": [ 13 | "#fc97af", 14 | "#87f7cf", 15 | "#f7f494", 16 | "#72ccff", 17 | "#f7c5a0", 18 | "#d4a4eb", 19 | "#d2f5a6", 20 | "#76f2f2" 21 | ], 22 | "borderColor": "#ccc", 23 | "borderWidth": 0, 24 | "visualMapColor": [ 25 | "#fc97af", 26 | "#87f7cf" 27 | ], 28 | "legendTextColor": "#999999", 29 | "kColor": "#fc97af", 30 | "kColor0": "transparent", 31 | "kBorderColor": "#fc97af", 32 | "kBorderColor0": "#87f7cf", 33 | "kBorderWidth": "2", 34 | "lineWidth": "3", 35 | "symbolSize": "0", 36 | "symbol": "circle", 37 | "symbolBorderWidth": "4", 38 | "lineSmooth": true, 39 | "graphLineWidth": "1", 40 | "graphLineColor": "#ffffff", 41 | "mapLabelColor": "#893448", 42 | "mapLabelColorE": "rgb(137,52,72)", 43 | "mapBorderColor": "#999999", 44 | "mapBorderColorE": "#eb8146", 45 | "mapBorderWidth": 0.5, 46 | "mapBorderWidthE": 1, 47 | "mapAreaColor": "#f3f3f3", 48 | "mapAreaColorE": "rgba(255,178,72,1)", 49 | "axes": [ 50 | { 51 | "type": "all", 52 | "name": "通用坐标轴", 53 | "axisLineShow": true, 54 | "axisLineColor": "#666666", 55 | "axisTickShow": false, 56 | "axisTickColor": "#333", 57 | "axisLabelShow": true, 58 | "axisLabelColor": "#aaaaaa", 59 | "splitLineShow": false, 60 | "splitLineColor": [ 61 | "#e6e6e6" 62 | ], 63 | "splitAreaShow": false, 64 | "splitAreaColor": [ 65 | "rgba(250,250,250,0.05)", 66 | "rgba(200,200,200,0.02)" 67 | ] 68 | }, 69 | { 70 | "type": "category", 71 | "name": "类目坐标轴", 72 | "axisLineShow": true, 73 | "axisLineColor": "#333", 74 | "axisTickShow": true, 75 | "axisTickColor": "#333", 76 | "axisLabelShow": true, 77 | "axisLabelColor": "#333", 78 | "splitLineShow": false, 79 | "splitLineColor": [ 80 | "#4a3e3e" 81 | ], 82 | "splitAreaShow": false, 83 | "splitAreaColor": [ 84 | "rgba(250,250,250,0.3)", 85 | "rgba(200,200,200,0.3)" 86 | ] 87 | }, 88 | { 89 | "type": "value", 90 | "name": "数值坐标轴", 91 | "axisLineShow": true, 92 | "axisLineColor": "#333", 93 | "axisTickShow": true, 94 | "axisTickColor": "#333", 95 | "axisLabelShow": true, 96 | "axisLabelColor": "#333", 97 | "splitLineShow": true, 98 | "splitLineColor": [ 99 | "#4a3e3e" 100 | ], 101 | "splitAreaShow": false, 102 | "splitAreaColor": [ 103 | "rgba(250,250,250,0.3)", 104 | "rgba(200,200,200,0.3)" 105 | ] 106 | }, 107 | { 108 | "type": "log", 109 | "name": "对数坐标轴", 110 | "axisLineShow": true, 111 | "axisLineColor": "#333", 112 | "axisTickShow": true, 113 | "axisTickColor": "#333", 114 | "axisLabelShow": true, 115 | "axisLabelColor": "#333", 116 | "splitLineShow": true, 117 | "splitLineColor": [ 118 | "#4a3e3e" 119 | ], 120 | "splitAreaShow": false, 121 | "splitAreaColor": [ 122 | "rgba(250,250,250,0.3)", 123 | "rgba(200,200,200,0.3)" 124 | ] 125 | }, 126 | { 127 | "type": "time", 128 | "name": "时间坐标轴", 129 | "axisLineShow": true, 130 | "axisLineColor": "#333", 131 | "axisTickShow": true, 132 | "axisTickColor": "#333", 133 | "axisLabelShow": true, 134 | "axisLabelColor": "#333", 135 | "splitLineShow": true, 136 | "splitLineColor": [ 137 | "#4a3e3e" 138 | ], 139 | "splitAreaShow": false, 140 | "splitAreaColor": [ 141 | "rgba(250,250,250,0.3)", 142 | "rgba(200,200,200,0.3)" 143 | ] 144 | } 145 | ], 146 | "axisSeperateSetting": false, 147 | "toolboxColor": "#999999", 148 | "toolboxEmphasisColor": "#666666", 149 | "tooltipAxisColor": "#cccccc", 150 | "tooltipAxisWidth": 1, 151 | "timelineLineColor": "#87f7cf", 152 | "timelineLineWidth": 1, 153 | "timelineItemColor": "#87f7cf", 154 | "timelineItemColorE": "#f7f494", 155 | "timelineCheckColor": "#fc97af", 156 | "timelineCheckBorderColor": "#fc97af", 157 | "timelineItemBorderWidth": 1, 158 | "timelineControlColor": "#87f7cf", 159 | "timelineControlBorderColor": "#87f7cf", 160 | "timelineControlBorderWidth": 0.5, 161 | "timelineLabelColor": "#87f7cf", 162 | "datazoomBackgroundColor": "rgba(255,255,255,0)", 163 | "datazoomDataColor": "rgba(114,204,255,1)", 164 | "datazoomFillColor": "rgba(114,204,255,0.2)", 165 | "datazoomHandleColor": "#72ccff", 166 | "datazoomHandleWidth": "100", 167 | "datazoomLabelColor": "#333333" 168 | } 169 | } -------------------------------------------------------------------------------- /src/renderer/src/components/ECharts/index.vue: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 3 | * @Version: 1.0 4 | * @Autor: codercao 5 | * @Date: 2024-03-30 22:34:25 6 | * @LastEditors: codercao 7 | * @LastEditTime: 2024-04-01 21:46:49 8 | */ 9 | 10 | 13 | 14 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /src/renderer/src/components/Intro/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 47 | 48 | 171 | 172 | -------------------------------------------------------------------------------- /src/renderer/src/components/KLine/index.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 494 | 495 | -------------------------------------------------------------------------------- /src/renderer/src/components/Layout/foot.vue: -------------------------------------------------------------------------------- 1 | 9 | 33 | 123 | -------------------------------------------------------------------------------- /src/renderer/src/components/Layout/header.vue: -------------------------------------------------------------------------------- 1 | 9 | 32 | 67 | -------------------------------------------------------------------------------- /src/renderer/src/components/Layout/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 21 | 26 | 27 | -------------------------------------------------------------------------------- /src/renderer/src/components/Layout/leftMenu.vue: -------------------------------------------------------------------------------- 1 | 9 | 65 | 66 | 81 | -------------------------------------------------------------------------------- /src/renderer/src/main.js: -------------------------------------------------------------------------------- 1 | //import './assets/main.css' 2 | 3 | import { createApp } from 'vue' 4 | import App from './App.vue' 5 | import router from './router' 6 | import ElementPlus from 'element-plus' 7 | import 'element-plus/dist/index.css' 8 | import * as ElementPlusIconsVue from '@element-plus/icons-vue' 9 | import 'element-plus/theme-chalk/dark/css-vars.css' 10 | const app = createApp(App) 11 | 12 | 13 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) { 14 | app.component(key, component) 15 | } 16 | 17 | 18 | app.use(router) 19 | app.use(ElementPlus) 20 | app.mount('#app') 21 | -------------------------------------------------------------------------------- /src/renderer/src/router/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 3 | * @Version: 1.0 4 | * @Autor: codercao 5 | * @Date: 2024-03-08 23:46:15 6 | * @LastEditors: codercao 7 | * @LastEditTime: 2024-04-21 00:18:10 8 | */ 9 | import { createRouter, createWebHistory } from 'vue-router' 10 | //import HomeView from '../views/HomeView.vue' 11 | 12 | const router = createRouter({ 13 | history: createWebHistory(import.meta.env.BASE_URL), 14 | routes: [ 15 | { 16 | path: '/', 17 | name: 'home', 18 | component: () => import('../views/main/index.vue') 19 | }, 20 | { 21 | path: '/login', 22 | name: 'login', 23 | component: () => import('../views/login/index.vue') 24 | }, 25 | { 26 | path: '/aiChat', 27 | name: 'aiChart', 28 | component: () => import('../views/AiChat/index.vue') 29 | } 30 | ] 31 | }) 32 | 33 | export default router 34 | -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/SSEService.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 3 | * @Version: 1.0 4 | * @Autor: codercao 5 | * @Date: 2024-04-20 23:20:01 6 | * @LastEditors: codercao 7 | * @LastEditTime: 2024-04-21 20:43:01 8 | */ 9 | export default class SSEService { 10 | constructor(endpoint) { 11 | this.endpoint = endpoint; 12 | this.eventSource = null; 13 | this.messageListeners = []; 14 | this.errorListeners = []; 15 | } 16 | 17 | connect() { 18 | this.eventSource = new EventSource(this.endpoint); 19 | this.eventSource.addEventListener("message", this.handleSSEMessage); 20 | this.eventSource.addEventListener("error", this.handleSSEError); 21 | } 22 | 23 | disconnect() { 24 | if (this.eventSource) { 25 | this.eventSource.close(); 26 | this.eventSource = null; 27 | } 28 | } 29 | 30 | handleSSEMessage = (event) => { 31 | this.messageListeners.forEach((listener) => listener(event.data)); 32 | }; 33 | 34 | handleSSEError = (event) => { 35 | this.errorListeners.forEach((listener) => listener(event)); 36 | }; 37 | 38 | addMessageListener(listener) { 39 | this.messageListeners.push(listener); 40 | } 41 | 42 | removeMessageListener(listener) { 43 | this.messageListeners = this.messageListeners.filter( 44 | (l) => l !== listener 45 | ); 46 | } 47 | 48 | addErrorListener(listener) { 49 | this.errorListeners.push(listener); 50 | } 51 | 52 | removeErrorListener(listener) { 53 | this.errorListeners = this.errorListeners.filter((l) => l !== listener); 54 | } 55 | } -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/components/ChatList.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 98 | 99 | -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/components/Header.vue: -------------------------------------------------------------------------------- 1 | 9 | 15 | 16 | 19 | 20 | 34 | -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/components/SideBar.vue: -------------------------------------------------------------------------------- 1 | 9 | 31 | 32 | 53 | 54 | -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/img/CMB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/src/renderer/src/views/AiChat/img/CMB.png -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/img/ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/src/renderer/src/views/AiChat/img/ai.png -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/img/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HongqingCao/electron-vite-vue-ft/6f3a234bfbea151dcae16d9cb40539b92170819f/src/renderer/src/views/AiChat/img/user.png -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 15 | 16 | 28 | 29 | 36 | -------------------------------------------------------------------------------- /src/renderer/src/views/AiChat/util.js: -------------------------------------------------------------------------------- 1 | var SIGN_REGEXP = /([yMdhsm])(\1*)/g 2 | var DEFAULT_PATTERN = 'yyyy-MM-dd' 3 | 4 | function padding (s, len) { 5 | len = len - (s + '').length 6 | for (let i = 0; i < len; i++) { 7 | s = '0' + s 8 | } 9 | return s 10 | } 11 | 12 | export default { 13 | getQueryStringByName: function (name) { 14 | var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i') 15 | var r = window.location.search.substr(1).match(reg) 16 | var context = '' 17 | if (r != null) 18 | context = r[2] 19 | reg = null 20 | r = null 21 | return context == null || context == '' || context == 'undefined' ? '' : context 22 | }, 23 | formatDate: { 24 | format: function (date, pattern) { 25 | pattern = pattern || DEFAULT_PATTERN 26 | return pattern.replace(SIGN_REGEXP, function ($0) { 27 | switch ($0.charAt(0)) { 28 | case 'y': 29 | return padding(date.getFullYear(), $0.length) 30 | case 'M': 31 | return padding(date.getMonth() + 1, $0.length) 32 | case 'd': 33 | return padding(date.getDate(), $0.length) 34 | case 'w': 35 | return date.getDay() + 1 36 | case 'h': 37 | return padding(date.getHours(), $0.length) 38 | case 'm': 39 | return padding(date.getMinutes(), $0.length) 40 | case 's': 41 | return padding(date.getSeconds(), $0.length) 42 | } 43 | }) 44 | }, 45 | parse: function (dateString, pattern) { 46 | var matchs1 = pattern.match(SIGN_REGEXP) 47 | var matchs2 = dateString.match(/(\d)+/g) 48 | if (matchs1.length == matchs2.length) { 49 | var _date = new Date(1970, 0, 1) 50 | for (var i = 0; i < matchs1.length; i++) { 51 | var _int = parseInt(matchs2[i]) 52 | var sign = matchs1[i] 53 | switch (sign.charAt(0)) { 54 | case 'y': 55 | _date.setFullYear(_int) 56 | break 57 | case 'M': 58 | _date.setMonth(_int - 1) 59 | break 60 | case 'd': 61 | _date.setDate(_int) 62 | break 63 | case 'h': 64 | _date.setHours(_int) 65 | break 66 | case 'm': 67 | _date.setMinutes(_int) 68 | break 69 | case 's': 70 | _date.setSeconds(_int) 71 | break 72 | } 73 | } 74 | return _date 75 | } 76 | return null 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/renderer/src/views/login/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 32 | 72 | 73 | -------------------------------------------------------------------------------- /src/renderer/src/views/main/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | 68 | 69 | 82 | 83 | --------------------------------------------------------------------------------