优化 GitHub Pages 部署配置,支持 master 分支,更新 Node.js 版本,调整构建命令,添加详细部署说明

This commit is contained in:
hxuanyu 2025-06-27 16:46:13 +08:00
parent 5064d43bdb
commit b6accfc111
7 changed files with 259 additions and 22 deletions

View File

@ -3,7 +3,7 @@ name: Deploy to GitHub Pages
on: on:
# 当推送到main分支时触发 # 当推送到main分支时触发
push: push:
branches: [ main ] branches: [ main, master ]
# 允许手动触发工作流 # 允许手动触发工作流
workflow_dispatch: workflow_dispatch:
@ -30,14 +30,13 @@ jobs:
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18' node-version: '20'
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
- name: Build for GitHub Pages
- name: Build run: npm run build:github
run: npm run build
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

78
DEPLOYMENT.md Normal file
View File

@ -0,0 +1,78 @@
# 部署说明
本项目支持多种部署方式,每种方式都有对应的构建命令。
## 🚀 部署方式
### 1. GitHub Pages自动部署
**特点**:推送代码到 `main``master` 分支后自动部署
**URL格式**`https://username.github.io/obs-overlay-widget/`
**设置步骤**
1. 推送代码到GitHub仓库
2. 在仓库设置中启用GitHub Pages
- 进入 Settings → Pages
- Source 选择 "GitHub Actions"
3. GitHub Actions会自动构建和部署
### 2. 手动部署(任意服务器)
**特点**适用于任何Web服务器包括Apache、Nginx、静态托管服务等
**构建命令**
```bash
npm run build
```
**部署步骤**
1. 运行构建命令
2. 将 `dist` 文件夹中的所有文件上传到服务器
3. 可以部署到根目录或任何子目录
### 3. GitHub Pages手动构建
**特点**如果你需要手动为GitHub Pages构建
**构建命令**
```bash
npm run build:github
```
## 📝 构建命令说明
| 命令 | 用途 | base路径 | 适用场景 |
|------|------|----------|----------|
| `npm run dev` | 开发服务器 | `/` | 本地开发 |
| `npm run build` | 生产构建 | `./` | 手动部署到任意服务器 |
| `npm run build:github` | GitHub Pages构建 | `/obs-overlay-widget/` | 手动GitHub Pages部署 |
| `npm run preview` | 预览构建结果 | - | 本地测试构建结果 |
## 🛠️ 技术说明
### Base路径配置
项目使用智能的base路径配置
- **开发模式**:使用 `/` 作为base路径
- **生产模式**
- 默认使用 `./`(相对路径),适用于任何部署位置
- 如果设置了 `VITE_BASE_PATH` 环境变量,则使用指定路径
### 自动化部署
GitHub Actions工作流`.github/workflows/deploy.yml`)会:
1. 检测代码变更
2. 安装依赖
3. 使用 `npm run build:github` 构建项目
4. 自动部署到GitHub Pages
## 🔧 自定义部署
如果你的仓库名不是 `obs-overlay-widget`,需要修改:
1. **vite.config.ts** 中的仓库名
2. **package.json**`build:github` 脚本的路径
例如,如果仓库名是 `my-widget`
```json
"build:github": "vue-tsc -b && cross-env VITE_BASE_PATH=/my-widget/ vite build"
```

109
package-lock.json generated
View File

@ -15,8 +15,10 @@
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.4",
"@vitejs/plugin-vue": "^6.0.0", "@vitejs/plugin-vue": "^6.0.0",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"cross-env": "^7.0.3",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"vite": "^7.0.0", "vite": "^7.0.0",
"vue-tsc": "^2.2.10" "vue-tsc": "^2.2.10"
@ -862,6 +864,16 @@
"@types/lodash": "*" "@types/lodash": "*"
} }
}, },
"node_modules/@types/node": {
"version": "24.0.4",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-24.0.4.tgz",
"integrity": "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.8.0"
}
},
"node_modules/@types/web-bluetooth": { "node_modules/@types/web-bluetooth": {
"version": "0.0.16", "version": "0.0.16",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
@ -1193,6 +1205,40 @@
"balanced-match": "^1.0.0" "balanced-match": "^1.0.0"
} }
}, },
"node_modules/cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz",
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"cross-spawn": "^7.0.1"
},
"bin": {
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},
"engines": {
"node": ">=10.14",
"npm": ">=6",
"yarn": ">=1"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
@ -1343,6 +1389,13 @@
"he": "bin/he" "he": "bin/he"
} }
}, },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"license": "ISC"
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@ -1435,6 +1488,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
@ -1522,6 +1585,29 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
@ -1562,6 +1648,13 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"dev": true,
"license": "MIT"
},
"node_modules/vite": { "node_modules/vite": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.0.0.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-7.0.0.tgz",
@ -1696,6 +1789,22 @@
"peerDependencies": { "peerDependencies": {
"typescript": ">=5.0.0" "typescript": ">=5.0.0"
} }
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
} }
} }
} }

View File

@ -6,6 +6,7 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc -b && vite build", "build": "vue-tsc -b && vite build",
"build:github": "vue-tsc -b && cross-env VITE_BASE_PATH=/obs-overlay-widget/ vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
@ -16,8 +17,10 @@
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.4",
"@vitejs/plugin-vue": "^6.0.0", "@vitejs/plugin-vue": "^6.0.0",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"cross-env": "^7.0.3",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"vite": "^7.0.0", "vite": "^7.0.0",
"vue-tsc": "^2.2.10" "vue-tsc": "^2.2.10"

View File

@ -182,7 +182,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'; import { ref, computed, watch, onMounted } from 'vue';
import type { TimerConfig } from './types'; import type { TimerConfig, TimerMode } from './types';
// Define props with default values // Define props with default values
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -271,12 +271,13 @@ onMounted(() => {
// Preset styles // Preset styles
const presets = { const presets = {
gaming: { gaming: {
mode: 'countdown', mode: 'countdown' as TimerMode,
duration: 1800, // 30 duration: 1800, // 30
format: 'mm:ss', format: 'mm:ss',
fontSize: 64, fontSize: 64,
fontFamily: 'Impact', fontFamily: 'Impact',
fontWeight: 'bold', fontWeight: 'bold',
color: '#ff6b35',
useGradient: true, useGradient: true,
gradientColors: ['#ff6b35', '#f7931e'], gradientColors: ['#ff6b35', '#f7931e'],
textShadow: true, textShadow: true,
@ -288,10 +289,13 @@ const presets = {
warningThreshold: 60, warningThreshold: 60,
warningColor: '#ff0000', warningColor: '#ff0000',
playSound: true, playSound: true,
soundVolume: 80 soundVolume: 80,
autoStart: false,
showMilliseconds: false,
finishedColor: '#ff0000'
}, },
meeting: { meeting: {
mode: 'countdown', mode: 'countdown' as TimerMode,
duration: 3600, // 60 duration: 3600, // 60
format: 'hh:mm:ss', format: 'hh:mm:ss',
fontSize: 52, fontSize: 52,
@ -299,6 +303,7 @@ const presets = {
fontWeight: 'normal', fontWeight: 'normal',
color: '#2ecc71', color: '#2ecc71',
useGradient: false, useGradient: false,
gradientColors: ['#2ecc71', '#27ae60'],
textShadow: true, textShadow: true,
shadowColor: 'rgba(0, 0, 0, 0.3)', shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 4, shadowBlur: 4,
@ -309,24 +314,36 @@ const presets = {
warningColor: '#f39c12', warningColor: '#f39c12',
finishedColor: '#e74c3c', finishedColor: '#e74c3c',
playSound: true, playSound: true,
soundVolume: 60 soundVolume: 60,
autoStart: false,
showMilliseconds: false
}, },
workout: { workout: {
mode: 'stopwatch', mode: 'stopwatch' as TimerMode,
duration: 0,
format: 'mm:ss', format: 'mm:ss',
fontSize: 72, fontSize: 72,
fontFamily: 'JetBrains Mono', fontFamily: 'JetBrains Mono',
fontWeight: 'bold', fontWeight: 'bold',
color: '#e74c3c',
useGradient: true, useGradient: true,
gradientColors: ['#e74c3c', '#c0392b'], gradientColors: ['#e74c3c', '#c0392b'],
textShadow: true, textShadow: true,
shadowColor: 'rgba(0, 0, 0, 0.5)', shadowColor: 'rgba(0, 0, 0, 0.5)',
shadowBlur: 6, shadowBlur: 6,
showMilliseconds: true, showMilliseconds: true,
autoStart: false autoStart: false,
showProgress: false,
progressColor: '#e74c3c',
progressHeight: 4,
warningThreshold: 0,
warningColor: '#f39c12',
finishedColor: '#e74c3c',
playSound: false,
soundVolume: 50
}, },
minimal: { minimal: {
mode: 'countdown', mode: 'countdown' as TimerMode,
duration: 300, duration: 300,
format: 'mm:ss', format: 'mm:ss',
fontSize: 48, fontSize: 48,
@ -334,10 +351,20 @@ const presets = {
fontWeight: 'lighter', fontWeight: 'lighter',
color: '#ffffff', color: '#ffffff',
useGradient: false, useGradient: false,
gradientColors: ['#ffffff', '#f8f9fa'],
textShadow: false, textShadow: false,
shadowColor: 'rgba(0, 0, 0, 0.5)',
shadowBlur: 4,
showProgress: false, showProgress: false,
progressColor: '#ffffff',
progressHeight: 4,
warningThreshold: 30,
warningColor: '#f39c12',
finishedColor: '#e74c3c',
playSound: false, playSound: false,
autoStart: false soundVolume: 50,
autoStart: false,
showMilliseconds: false
} }
}; };

View File

@ -23,7 +23,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'; import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import type { TimerConfig, TimerStatus, TimerMode } from './types'; import type { TimerConfig, TimerStatus } from './types';
// Define props with default values // Define props with default values
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -125,8 +125,8 @@ const progressFillStyle = computed(() => {
// //
const containerStyle = computed(() => ({ const containerStyle = computed(() => ({
textAlign: 'center', textAlign: 'center' as const,
userSelect: 'none' userSelect: 'none' as const
})); }));
// //
@ -292,7 +292,7 @@ watch(() => props.config.duration, (newDuration) => {
} }
}); });
watch(() => props.config.mode, (newMode) => { watch(() => props.config.mode, () => {
resetTimer(); resetTimer();
}); });

View File

@ -1,8 +1,29 @@
import { defineConfig } from 'vite' import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig(({ command, mode }) => {
plugins: [vue()], // 加载环境变量
base: process.env.NODE_ENV === 'production' ? '/obs-overlay-widget/' : '/', const env = loadEnv(mode, process.cwd(), '')
// 根据构建环境动态设置base路径
const base = (() => {
// 开发模式使用根路径
if (command === 'serve') {
return '/'
}
// 生产模式检查是否有指定的base路径用于GitHub Pages
if (env.VITE_BASE_PATH) {
return env.VITE_BASE_PATH
}
// 默认使用相对路径,适用于任何部署场景
return './'
})()
return {
plugins: [vue()],
base,
}
}) })