16 changed files with 12212 additions and 1 deletions
@ -1 +1,3 @@
@@ -1 +1,3 @@
|
||||
{} |
||||
{ |
||||
"git.ignoreLimitWarning": true |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
{ |
||||
"name": "GoFaster", |
||||
"version": "1.0.0", |
||||
"main": "src/main/index.js", |
||||
"scripts": { |
||||
"serve": "vue-cli-service serve", |
||||
"build": "vue-cli-service build && electron-builder", |
||||
"start": "electron .", |
||||
"dev": "concurrently \"npm run serve\" \"wait-on http://localhost:8080 && npm run start\"", |
||||
"postinstall": "electron-builder install-app-deps" |
||||
}, |
||||
"keywords": [], |
||||
"author": "", |
||||
"license": "ISC", |
||||
"description": "", |
||||
"devDependencies": { |
||||
"@electron/remote": "^2.1.3", |
||||
"@vue/cli-service": "^5.0.8", |
||||
"concurrently": "^9.2.0", |
||||
"electron": "^37.2.6", |
||||
"electron-builder": "^26.0.12", |
||||
"vue": "^3.5.18", |
||||
"wait-on": "^8.0.4" |
||||
}, |
||||
"dependencies": { |
||||
"lowdb": "^7.0.1", |
||||
"vue-router": "^4.5.1", |
||||
"vuex": "^4.0.2" |
||||
} |
||||
} |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>GoFaster</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
const { app, BrowserWindow } = require('electron') |
||||
const path = require('path') |
||||
|
||||
let mainWindow |
||||
|
||||
function createWindow() { |
||||
mainWindow = new BrowserWindow({ |
||||
width: 1200, |
||||
height: 800, |
||||
webPreferences: { |
||||
nodeIntegration: false, |
||||
contextIsolation: true, |
||||
enableRemoteModule: false, |
||||
preload: path.join(__dirname, '../preload.js') |
||||
} |
||||
}) |
||||
|
||||
// 开发模式下加载开发服务器
|
||||
if (process.env.NODE_ENV === 'development') { |
||||
mainWindow.loadURL('http://localhost:8080') |
||||
mainWindow.webContents.openDevTools() |
||||
} else { |
||||
mainWindow.loadFile(path.join(__dirname, '../public/index.html')) |
||||
} |
||||
|
||||
mainWindow.on('closed', () => { |
||||
mainWindow = null |
||||
}) |
||||
} |
||||
|
||||
app.whenReady().then(createWindow) |
||||
|
||||
app.on('window-all-closed', () => { |
||||
if (process.platform !== 'darwin') { |
||||
app.quit() |
||||
} |
||||
}) |
||||
|
||||
app.on('activate', () => { |
||||
if (mainWindow === null) { |
||||
createWindow() |
||||
} |
||||
}) |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
const { contextBridge } = require('electron') |
||||
|
||||
// 安全地暴露API给渲染进程
|
||||
contextBridge.exposeInMainWorld('electron', { |
||||
platform: process.platform |
||||
// 可以添加更多安全的API
|
||||
}) |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
<template> |
||||
<div id="app"> |
||||
<router-view /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'App' |
||||
} |
||||
</script> |
||||
|
||||
<style> |
||||
/* 全局样式 */ |
||||
#app { |
||||
font-family: Avenir, Helvetica, Arial, sans-serif; |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
color: #2c3e50; |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
</style> |
@ -0,0 +1,107 @@
@@ -0,0 +1,107 @@
|
||||
<template> |
||||
<div class="speed-test"> |
||||
<h1>GoFaster Speed Test</h1> |
||||
<button @click="startTest" :disabled="testing"> |
||||
{{ testing ? 'Testing...' : 'Start Test' }} |
||||
</button> |
||||
|
||||
<div v-if="result" class="results"> |
||||
<h3>Test Results:</h3> |
||||
<div class="result-item"> |
||||
<span class="label">Download:</span> |
||||
<span class="value">{{ result.download }} Mbps</span> |
||||
</div> |
||||
<div class="result-item"> |
||||
<span class="label">Upload:</span> |
||||
<span class="value">{{ result.upload }} Mbps</span> |
||||
</div> |
||||
<div class="result-item"> |
||||
<span class="label">Ping:</span> |
||||
<span class="value">{{ result.ping }} ms</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { ref } from 'vue' |
||||
import { useStore } from 'vuex' |
||||
|
||||
export default { |
||||
name: 'SpeedTest', |
||||
setup() { |
||||
const store = useStore() |
||||
const testing = ref(false) |
||||
const result = ref(null) |
||||
|
||||
const startTest = async () => { |
||||
testing.value = true |
||||
result.value = null |
||||
|
||||
// 模拟网络测试 (替换为真实测试逻辑) |
||||
await new Promise(resolve => setTimeout(resolve, 2000)) |
||||
|
||||
const testResult = { |
||||
download: (Math.random() * 100).toFixed(2), |
||||
upload: (Math.random() * 50).toFixed(2), |
||||
ping: (Math.random() * 100).toFixed(2), |
||||
timestamp: new Date().toISOString() |
||||
} |
||||
|
||||
// 保存结果 |
||||
await store.dispatch('saveTestResult', testResult) |
||||
result.value = testResult |
||||
testing.value = false |
||||
} |
||||
|
||||
return { testing, result, startTest } |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.speed-test { |
||||
padding: 20px; |
||||
max-width: 600px; |
||||
margin: 0 auto; |
||||
text-align: center; |
||||
} |
||||
|
||||
button { |
||||
padding: 10px 20px; |
||||
font-size: 16px; |
||||
background-color: #42b983; |
||||
color: white; |
||||
border: none; |
||||
border-radius: 4px; |
||||
cursor: pointer; |
||||
margin: 20px 0; |
||||
} |
||||
|
||||
button:disabled { |
||||
background-color: #cccccc; |
||||
cursor: not-allowed; |
||||
} |
||||
|
||||
.results { |
||||
margin-top: 20px; |
||||
padding: 15px; |
||||
background-color: #f5f5f5; |
||||
border-radius: 4px; |
||||
text-align: left; |
||||
} |
||||
|
||||
.result-item { |
||||
margin: 10px 0; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
.label { |
||||
font-weight: bold; |
||||
} |
||||
|
||||
.value { |
||||
color: #42b983; |
||||
} |
||||
</style> |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
import { createApp } from 'vue' |
||||
import App from './App.vue' |
||||
import router from './router' |
||||
import store from './store' |
||||
|
||||
// 初始化数据库
|
||||
import { initDB } from './services/db' |
||||
initDB().then(() => { |
||||
createApp(App) |
||||
.use(store) |
||||
.use(router) |
||||
.mount('#app') |
||||
}) |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router' |
||||
import Home from '@/views/Home.vue' |
||||
import History from '@/views/History.vue' |
||||
|
||||
const routes = [ |
||||
{ |
||||
path: '/', |
||||
name: 'Home', |
||||
component: Home |
||||
}, |
||||
{ |
||||
path: '/history', |
||||
name: 'History', |
||||
component: History |
||||
} |
||||
] |
||||
|
||||
const router = createRouter({ |
||||
history: createWebHashHistory(), |
||||
routes |
||||
}) |
||||
|
||||
export default router |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
import { Low, JSONFile } from 'lowdb' |
||||
import { join } from 'path' |
||||
import { app } from '@electron/remote' |
||||
|
||||
const file = join(app.getPath('userData'), 'gofaster-db.json') |
||||
const adapter = new JSONFile(file) |
||||
const db = new Low(adapter) |
||||
|
||||
// 初始化数据库
|
||||
async function initDB() { |
||||
await db.read() |
||||
db.data ||= {
|
||||
settings: {},
|
||||
activities: [],
|
||||
stats: {}
|
||||
} |
||||
await db.write() |
||||
} |
||||
|
||||
export { db, initDB } |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
import { createStore } from 'vuex' |
||||
import { db } from '../services/db' |
||||
|
||||
export default createStore({ |
||||
state: { |
||||
testResults: [] |
||||
}, |
||||
mutations: { |
||||
setTestResults(state, results) { |
||||
state.testResults = results |
||||
}, |
||||
addTestResult(state, result) { |
||||
state.testResults.unshift(result) |
||||
} |
||||
}, |
||||
actions: { |
||||
async loadTestResults({ commit }) { |
||||
await db.read() |
||||
commit('setTestResults', db.data.activities || []) |
||||
}, |
||||
async saveTestResult({ commit }, result) { |
||||
await db.read() |
||||
db.data.activities.unshift(result) |
||||
await db.write() |
||||
commit('addTestResult', result) |
||||
} |
||||
} |
||||
}) |
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
<template> |
||||
<div class="history-view"> |
||||
<h1>Test History</h1> |
||||
|
||||
<div v-if="testResults.length === 0" class="empty-state"> |
||||
No speed tests recorded yet. |
||||
</div> |
||||
|
||||
<div v-else class="history-list"> |
||||
<div v-for="(result, index) in testResults" :key="index" class="history-item"> |
||||
<div class="history-date"> |
||||
{{ formatDate(result.timestamp) }} |
||||
</div> |
||||
<div class="history-stats"> |
||||
<span class="stat download">↓ {{ result.download }} Mbps</span> |
||||
<span class="stat upload">↑ {{ result.upload }} Mbps</span> |
||||
<span class="stat ping">↔ {{ result.ping }} ms</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { computed, onMounted } from 'vue' |
||||
import { useStore } from 'vuex' |
||||
|
||||
export default { |
||||
name: 'HistoryView', |
||||
setup() { |
||||
const store = useStore() |
||||
|
||||
onMounted(() => { |
||||
store.dispatch('loadTestResults') |
||||
}) |
||||
|
||||
const testResults = computed(() => store.state.testResults) |
||||
|
||||
const formatDate = (timestamp) => { |
||||
return new Date(timestamp).toLocaleString() |
||||
} |
||||
|
||||
return { testResults, formatDate } |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.history-view { |
||||
padding: 20px; |
||||
max-width: 800px; |
||||
margin: 0 auto; |
||||
} |
||||
|
||||
.empty-state { |
||||
text-align: center; |
||||
padding: 40px; |
||||
color: #888; |
||||
} |
||||
|
||||
.history-list { |
||||
margin-top: 20px; |
||||
} |
||||
|
||||
.history-item { |
||||
padding: 15px; |
||||
margin-bottom: 10px; |
||||
background-color: #f9f9f9; |
||||
border-radius: 4px; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
} |
||||
|
||||
.history-date { |
||||
color: #666; |
||||
} |
||||
|
||||
.history-stats { |
||||
display: flex; |
||||
gap: 15px; |
||||
} |
||||
|
||||
.stat { |
||||
font-weight: bold; |
||||
} |
||||
|
||||
.download { |
||||
color: #42b983; |
||||
} |
||||
|
||||
.upload { |
||||
color: #3498db; |
||||
} |
||||
|
||||
.ping { |
||||
color: #e74c3c; |
||||
} |
||||
</style> |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<!-- src/renderer/views/Home.vue --> |
||||
<template> |
||||
<div class="home"> |
||||
<h1>Welcome to Your Electron App</h1> |
||||
<!-- 这里可以放你的首页内容 --> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'HomeView' |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
/* 可以添加一些基础样式 */ |
||||
.home { |
||||
padding: 20px; |
||||
text-align: center; |
||||
} |
||||
</style> |
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
const path = require('path') |
||||
const { defineConfig } = require('@vue/cli-service') |
||||
|
||||
module.exports = defineConfig({ |
||||
outputDir: path.resolve(__dirname, 'dist/renderer'), |
||||
publicPath: './', |
||||
|
||||
// 关键修改:配置 Electron 构建目标和排除原生模块
|
||||
configureWebpack: { |
||||
target: 'electron-renderer', // 指定为 Electron 渲染进程
|
||||
externals: { |
||||
electron: 'require("electron")', // 防止 Webpack 处理 electron 模块
|
||||
fs: 'require("fs")', // 排除 Node.js 原生模块
|
||||
path: 'require("path")', |
||||
}, |
||||
resolve: { |
||||
alias: { |
||||
'@': path.resolve(__dirname, 'src/renderer'), |
||||
// 确保 Vue 单文件组件引用时自动补全扩展名
|
||||
'views': path.resolve(__dirname, 'src/renderer/views') |
||||
}, |
||||
// 添加扩展名自动解析
|
||||
extensions: ['.js', '.vue', '.json'] |
||||
} |
||||
}, |
||||
|
||||
// 保留原有页面配置
|
||||
pages: { |
||||
index: { |
||||
entry: 'src/renderer/main.js', |
||||
template: 'public/index.html', |
||||
filename: 'index.html' |
||||
} |
||||
}, |
||||
|
||||
// 保留 Electron Builder 配置
|
||||
pluginOptions: { |
||||
electronBuilder: { |
||||
preload: 'src/preload.js', |
||||
nodeIntegration: false, |
||||
contextIsolation: true, |
||||
// 可选:明确指定主进程文件路径
|
||||
mainProcessFile: 'src/main/index.js' |
||||
} |
||||
}, |
||||
|
||||
// 开发服务器配置(与 Electron 主进程配合)
|
||||
devServer: { |
||||
port: 8080, // 确保与 wait-on 端口一致
|
||||
hot: true, |
||||
headers: { |
||||
'Access-Control-Allow-Origin': '*' |
||||
} |
||||
} |
||||
}) |
Loading…
Reference in new issue