├── .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 | 
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 | 
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 |
10 |
11 |
12 |
13 |
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 |
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 |
11 |
12 |
13 |
14 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/src/renderer/src/components/Intro/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
18 |
19 | {{ active.title }}
20 | {{ config.skipLabel }}
21 |
22 |
{{ active.content }}
23 |
44 |
45 |
46 |
47 |
48 |
171 |
172 |
--------------------------------------------------------------------------------
/src/renderer/src/components/KLine/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
70.350 -0.650 -0.92%
12 |
13 |
委比:95.4%
14 |
委差:95.4%
15 |
16 |
17 |
卖一70.44
18 |
5200
19 |
20 |
21 |
买一70.350
22 |
22.53万
23 |
24 |
25 |
昨收71.00
26 |
开盘71.850
27 |
28 |
29 |
涨跌-0.650
30 |
最高70.150
31 |
32 |
33 |
涨幅-0.954%
34 |
最低70.130
35 |
36 |
37 |
总量4667万
38 |
金额39.95亿
39 |
40 |
41 |
换手0.21
42 |
量比0.46
43 |
44 |
45 |
外盘2544万
46 |
内盘1389万
47 |
48 |
49 |
总股本1223亿
50 |
流通股124亿
51 |
52 |
53 |
总市值14333亿
54 |
流通值14333亿
55 |
56 |
57 |
TTM市盈12.456
58 |
市净率1.389
59 |
60 |
61 |
62 |
63 |
64 |
494 |
495 |
--------------------------------------------------------------------------------
/src/renderer/src/components/Layout/foot.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
32 |
33 |
123 |
--------------------------------------------------------------------------------
/src/renderer/src/components/Layout/header.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
28 |
29 |
30 |
31 |
32 |
67 |
--------------------------------------------------------------------------------
/src/renderer/src/components/Layout/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
20 |
21 |
26 |
27 |
--------------------------------------------------------------------------------
/src/renderer/src/components/Layout/leftMenu.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
14 |
64 |
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 |
2 |
3 |
4 |
9 |
10 |
11 |
![]()
12 |
13 |
14 |
15 | {{ message.sender }}{{ message.time }}
18 |
19 |
24 |
![]()
25 |
26 |
32 |
33 |
34 |
35 |
36 |
37 |
44 |
45 |
46 |
47 |
98 |
99 |
--------------------------------------------------------------------------------
/src/renderer/src/views/AiChat/components/Header.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 | 小融
13 |
14 |
15 |
16 |
19 |
20 |
34 |
--------------------------------------------------------------------------------
/src/renderer/src/views/AiChat/components/SideBar.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 | 探索
13 | 新建对话
14 |
15 |
16 |
23 |
24 |
{{ item.name }}
25 |
26 |
27 |
28 |
29 |
30 |
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 |
10 |
11 |
12 |
13 |
14 |
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 |
10 |
11 |
12 |
Hello,朋友
13 |
欢迎使用FT金融终端!
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
登录
22 |
23 |
24 |
25 | {{ state.info }}
26 |
27 | 忘记密码
28 |
29 |
30 |
31 |
32 |
72 |
73 |
--------------------------------------------------------------------------------
/src/renderer/src/views/main/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
68 |
69 |
82 |
83 |
--------------------------------------------------------------------------------