diff --git a/.env.development b/.env.development
index 3d63d7f..a376891 100644
--- a/.env.development
+++ b/.env.development
@@ -1,4 +1,3 @@
VITE_API_URL=http://192.168.1.3:9528
VITE_TRADINGVIEW_LIBRARY_URL=http://192.168.1.5:6173
-VITE_TRADINGVIEW_DATA_API_URL=https://demo-feed-data.tradingview.com
-VITE_APP_VERSION=0.0.1
\ No newline at end of file
+VITE_TRADINGVIEW_DATA_API_URL=https://demo-feed-data.tradingview.com
\ No newline at end of file
diff --git a/.env.production b/.env.production
index 2c09e7e..28ba411 100644
--- a/.env.production
+++ b/.env.production
@@ -1,4 +1,3 @@
VITE_API_URL=http://192.168.1.3:9527
VITE_TRADINGVIEW_LIBRARY_URL=http://192.168.1.5:6173
-VITE_TRADINGVIEW_DATA_API_URL=https://demo-feed-data.tradingview.com
-VITE_APP_VERSION=0.0.1
\ No newline at end of file
+VITE_TRADINGVIEW_DATA_API_URL=https://demo-feed-data.tradingview.com
\ No newline at end of file
diff --git a/app.update.md b/APP_UPDATE.md
similarity index 100%
rename from app.update.md
rename to APP_UPDATE.md
diff --git a/VERSION_MANAGEMENT.md b/VERSION_MANAGEMENT.md
new file mode 100644
index 0000000..c06c232
--- /dev/null
+++ b/VERSION_MANAGEMENT.md
@@ -0,0 +1,474 @@
+# 版本管理方案
+
+## 🎯 问题
+
+前端每次更新版本,后端也需要手动同步版本信息,容易出错且效率低。
+
+## ✅ 解决方案
+
+### 方案一:自动生成 version.json(已实现)⭐⭐⭐
+
+前端打包时自动生成 `version.json` 文件,后端可以直接读取。
+
+#### 工作流程
+
+```
+1. 开发者修改 package.json 中的 version
+ ↓
+2. 运行 pnpm run build
+ ↓
+3. Vite 自动读取 package.json 版本
+ ↓
+4. 构建完成后生成 dist/version.json
+ ↓
+5. 部署到服务器
+ ↓
+6. 后端通过 HTTP 请求获取前端版本
+```
+
+#### 生成的 version.json 格式
+
+```json
+{
+ "version": "0.0.1",
+ "buildTime": "2025-12-30T10:30:00.000Z",
+ "gitCommit": "abc123",
+ "environment": "production"
+}
+```
+
+#### 后端获取版本的方式
+
+**方式 1:直接读取静态文件(推荐)**
+
+如果前端部署在 Nginx/CDN,后端可以直接访问:
+
+```bash
+curl https://your-domain.com/version.json
+```
+
+后端 API 实现示例:
+
+```typescript
+// Node.js / Elysia 示例
+app.get('/api/app/version', async (ctx) => {
+ // 从前端静态资源读取版本
+ const response = await fetch('https://your-frontend-domain.com/version.json')
+ const frontendVersion = await response.json()
+
+ const { platform, currentVersion } = ctx.query
+
+ return {
+ version: frontendVersion.version,
+ forceUpdate: compareVersion(currentVersion, frontendVersion.version) < 0,
+ updateMessage: '修复了一些问题',
+ updateUrl: platform === 'ios'
+ ? 'https://apps.apple.com/app/xxx'
+ : 'https://play.google.com/store/apps/details?id=xxx',
+ minSupportVersion: '0.0.1',
+ }
+})
+```
+
+**方式 2:部署时同步到后端**
+
+在 CI/CD 中,部署前端时自动同步 version.json 到后端:
+
+```yaml
+# GitHub Actions / GitLab CI 示例
+- name: Deploy Frontend
+ run: |
+ pnpm run build
+ # 上传到前端服务器
+ rsync -avz dist/ user@frontend-server:/var/www/html/
+ # 同时将 version.json 复制到后端
+ scp dist/version.json user@backend-server:/app/frontend-version.json
+```
+
+后端直接读取本地文件:
+
+```typescript
+import fs from 'fs'
+
+app.get('/api/app/version', async (ctx) => {
+ const versionFile = fs.readFileSync('/app/frontend-version.json', 'utf-8')
+ const { version } = JSON.parse(versionFile)
+
+ return {
+ version,
+ forceUpdate: false,
+ // ...
+ }
+})
+```
+
+---
+
+## 📝 使用步骤
+
+### 1. 修改版本号
+
+只需修改 `package.json` 中的 `version` 字段:
+
+```json
+{
+ "version": "1.2.3" // 只改这里
+}
+```
+
+### 2. 构建项目
+
+```bash
+pnpm run build
+```
+
+构建完成后会在 `dist/` 目录生成 `version.json`:
+
+```
+dist/
+├── index.html
+├── assets/
+└── version.json ← 自动生成
+```
+
+### 3. 部署
+
+将整个 `dist/` 目录部署到服务器,`version.json` 会一起部署。
+
+---
+
+## 🔧 原生应用版本同步
+
+### iOS (Xcode)
+
+修改 `ios/App/App/Info.plist`:
+
+```xml
+CFBundleShortVersionString
+1.2.3
+```
+
+### Android (build.gradle)
+
+修改 `android/app/build.gradle`:
+
+```gradle
+versionName "1.2.3" // 与 package.json 保持一致
+```
+
+### 自动化脚本(可选)
+
+创建 `scripts/sync-version.js`:
+
+```javascript
+#!/usr/bin/env node
+import fs from 'fs'
+import { execSync } from 'child_process'
+
+const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf-8'))
+const version = packageJson.version
+
+console.log(`Syncing version ${version} to native projects...`)
+
+// iOS
+const infoPlist = './ios/App/App/Info.plist'
+let plistContent = fs.readFileSync(infoPlist, 'utf-8')
+plistContent = plistContent.replace(
+ /CFBundleShortVersionString<\/key>\s*.*?<\/string>/,
+ `CFBundleShortVersionString\n\t${version}`
+)
+fs.writeFileSync(infoPlist, plistContent)
+
+// Android (需要安装 gradle 解析库或手动更新)
+console.log('Please manually update android/app/build.gradle versionName')
+
+console.log('✓ Version synced!')
+```
+
+在 `package.json` 中添加脚本:
+
+```json
+{
+ "scripts": {
+ "version:sync": "node scripts/sync-version.js",
+ "build:ios": "npm run version:sync && ionic cap sync ios",
+ "build:android": "npm run version:sync && ionic cap sync android"
+ }
+}
+```
+
+---
+
+## 🎨 版本号规范
+
+遵循**语义化版本 (Semantic Versioning)**:
+
+```
+主版本号.次版本号.修订号
+ ↓ ↓ ↓
+ 1 . 2 . 3
+
+- 主版本号:不兼容的 API 修改
+- 次版本号:向下兼容的功能新增
+- 修订号:向下兼容的问题修正
+```
+
+### 版本更新示例
+
+- `0.0.1` → `0.0.2` - 修复 bug
+- `0.0.2` → `0.1.0` - 新增功能
+- `0.1.0` → `1.0.0` - 重大更新
+
+---
+
+## 🚀 CI/CD 集成
+
+### GitHub Actions 示例
+
+```yaml
+name: Build and Deploy
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: '18'
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - name: Build
+ run: pnpm run build
+ env:
+ NODE_ENV: production
+
+ - name: Deploy to server
+ run: |
+ rsync -avz dist/ user@server:/var/www/html/
+
+ - name: Notify backend
+ run: |
+ curl -X POST https://your-backend.com/api/webhook/frontend-deployed \
+ -H "Content-Type: application/json" \
+ -d '{"version": "'$(node -p "require('./package.json').version")'"}'
+```
+
+---
+
+## 📊 方案对比
+
+| 方案 | 优点 | 缺点 | 推荐度 |
+|-----|------|------|--------|
+| **自动生成 version.json** | 完全自动化、前后端解耦 | 需要后端额外请求 | ⭐⭐⭐⭐⭐ |
+| 手动同步 | 简单直接 | 容易忘记、出错 | ⭐ |
+| 共享配置文件 | 统一管理 | 需要 Monorepo | ⭐⭐⭐ |
+| 环境变量 | 灵活 | 部署时需手动设置 | ⭐⭐ |
+
+---
+
+## ✅ 总结
+
+1. **前端**:只需修改 `package.json` 的 `version` 字段
+2. **构建**:运行 `pnpm run build` 自动生成 `version.json`
+3. **后端**:通过 HTTP 请求或读取文件获取版本信息
+4. **原生应用**:可选使用脚本自动同步版本号
+
+**一次配置,永久受益!** 🎉
+
+
+## 后端 API 实现示例 - 版本检查接口
+
+```ts
+import { Elysia, t } from 'elysia'
+
+// 版本比较工具函数
+function compareVersion(version1: string, version2: string): number {
+ const v1Parts = version1.split('.').map(Number)
+ const v2Parts = version2.split('.').map(Number)
+ const maxLength = Math.max(v1Parts.length, v2Parts.length)
+
+ for (let i = 0; i < maxLength; i++) {
+ const v1Part = v1Parts[i] || 0
+ const v2Part = v2Parts[i] || 0
+
+ if (v1Part < v2Part) return -1
+ if (v1Part > v2Part) return 1
+ }
+
+ return 0
+}
+
+// 方案一:从前端静态资源读取版本(推荐)
+async function getFrontendVersionFromURL(): Promise<{
+ version: string
+ buildTime: string
+ gitCommit: string
+ environment: string
+}> {
+ const response = await fetch('https://your-frontend-domain.com/version.json')
+ return await response.json()
+}
+
+// 方案二:从本地文件读取(适用于前后端部署在同一服务器)
+import fs from 'fs'
+
+function getFrontendVersionFromFile(): {
+ version: string
+ buildTime: string
+ gitCommit: string
+ environment: string
+} {
+ const content = fs.readFileSync('/app/frontend-version.json', 'utf-8')
+ return JSON.parse(content)
+}
+
+// 应用商店链接配置
+const APP_STORE_URLS = {
+ ios: 'https://apps.apple.com/app/id123456789',
+ android: 'https://play.google.com/store/apps/details?id=riwa.ionic.app',
+}
+
+// 版本策略配置(可存储在数据库)
+const VERSION_POLICIES = {
+ minSupportVersion: '0.0.1', // 最低支持版本
+ forceUpdateVersions: ['0.0.1'], // 需要强制更新的版本列表
+ updateMessages: {
+ 'zh-CN': '修复了一些问题并优化了性能',
+ 'en-US': 'Bug fixes and performance improvements',
+ },
+}
+
+// Elysia 路由定义
+const app = new Elysia()
+ .get(
+ '/api/app/version',
+ async ({ query, headers }) => {
+ const { platform, currentVersion } = query
+ const lang = headers['accept-language']?.split(',')[0] || 'en-US'
+
+ try {
+ // 获取前端版本信息
+ const frontendVersion = await getFrontendVersionFromURL()
+ // 或使用本地文件: const frontendVersion = getFrontendVersionFromFile()
+
+ // 判断是否需要更新
+ const hasUpdate = compareVersion(currentVersion, frontendVersion.version) < 0
+
+ // 判断是否强制更新
+ let forceUpdate = VERSION_POLICIES.forceUpdateVersions.includes(currentVersion)
+
+ // 检查是否低于最低支持版本
+ if (compareVersion(currentVersion, VERSION_POLICIES.minSupportVersion) < 0) {
+ forceUpdate = true
+ }
+
+ // 获取更新链接
+ const updateUrl = platform === 'ios'
+ ? APP_STORE_URLS.ios
+ : platform === 'android'
+ ? APP_STORE_URLS.android
+ : ''
+
+ // 获取更新说明(多语言)
+ const updateMessage = VERSION_POLICIES.updateMessages[lang]
+ || VERSION_POLICIES.updateMessages['en-US']
+
+ return {
+ version: frontendVersion.version,
+ buildNumber: parseInt(frontendVersion.version.replace(/\./g, '')),
+ buildTime: frontendVersion.buildTime,
+ gitCommit: frontendVersion.gitCommit,
+ forceUpdate,
+ updateMessage,
+ updateUrl,
+ minSupportVersion: VERSION_POLICIES.minSupportVersion,
+ releaseNotes: [
+ '修复了已知问题',
+ '优化了应用性能',
+ '改进了用户界面',
+ ],
+ }
+ } catch (error) {
+ console.error('Failed to get frontend version:', error)
+
+ // 降级处理:返回当前版本,不强制更新
+ return {
+ version: currentVersion,
+ forceUpdate: false,
+ updateMessage: '',
+ updateUrl: '',
+ }
+ }
+ },
+ {
+ query: t.Object({
+ platform: t.Union([t.Literal('ios'), t.Literal('android'), t.Literal('web')]),
+ currentVersion: t.String(),
+ }),
+ response: t.Object({
+ version: t.String(),
+ buildNumber: t.Optional(t.Number()),
+ buildTime: t.Optional(t.String()),
+ gitCommit: t.Optional(t.String()),
+ forceUpdate: t.Boolean(),
+ updateMessage: t.Optional(t.String()),
+ updateUrl: t.Optional(t.String()),
+ minSupportVersion: t.Optional(t.String()),
+ releaseNotes: t.Optional(t.Array(t.String())),
+ }),
+ }
+ )
+
+export default app
+
+/**
+ * 使用示例:
+ *
+ * 1. 启动后端服务
+ * 2. 前端请求:GET /api/app/version?platform=ios¤tVersion=0.0.1
+ * 3. 后端响应:
+ * {
+ * "version": "0.0.2",
+ * "buildTime": "2025-12-30T11:42:43.425Z",
+ * "forceUpdate": false,
+ * "updateMessage": "修复了一些问题并优化了性能",
+ * "updateUrl": "https://apps.apple.com/app/id123456789",
+ * "minSupportVersion": "0.0.1"
+ * }
+ */
+
+/**
+ * 数据库存储方案(可选)
+ *
+ * 如果需要更灵活的版本策略管理,可以将配置存储在数据库:
+ *
+ * CREATE TABLE app_versions (
+ * id SERIAL PRIMARY KEY,
+ * platform VARCHAR(20) NOT NULL,
+ * version VARCHAR(20) NOT NULL,
+ * min_support_version VARCHAR(20),
+ * force_update BOOLEAN DEFAULT FALSE,
+ * update_message_zh TEXT,
+ * update_message_en TEXT,
+ * update_url TEXT,
+ * release_notes JSONB,
+ * created_at TIMESTAMP DEFAULT NOW()
+ * );
+ *
+ * 然后从数据库查询版本策略:
+ * const policy = await db.query(
+ * 'SELECT * FROM app_versions WHERE platform = $1 ORDER BY created_at DESC LIMIT 1',
+ * [platform]
+ * )
+ */
+
+```
\ No newline at end of file
diff --git a/package.json b/package.json
index c2f0264..0a17724 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"preview": "vite preview",
"run:ios": "ionic capacitor run ios -l --external",
"run:android": "ionic capacitor run android -l --external",
- "proxy": "ionic config set -g proxy http://192.168.1.36:9527",
+ "proxy": "ionic config set -g proxy http://192.168.1.3:9527",
"test:e2e": "cypress run",
"test:unit": "vitest",
"lint": "eslint",
@@ -81,6 +81,7 @@
"eslint": "^9.39.1",
"eslint-plugin-format": "^1.1.0",
"eslint-plugin-vue": "^10.6.2",
+ "jiti": "^2.6.1",
"jsdom": "^27.3.0",
"lint-staged": "^16.2.7",
"simple-git-hooks": "^2.13.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0a9730f..d3ae835 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -195,6 +195,9 @@ importers:
eslint-plugin-vue:
specifier: ^10.6.2
version: 10.6.2(@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@2.6.1)))(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)))
+ jiti:
+ specifier: ^2.6.1
+ version: 2.6.1
jsdom:
specifier: ^27.3.0
version: 27.3.0(postcss@8.5.6)
diff --git a/scripts/build.ts b/scripts/build.ts
new file mode 100644
index 0000000..80ccd96
--- /dev/null
+++ b/scripts/build.ts
@@ -0,0 +1,25 @@
+import fs from "node:fs";
+import path from "node:path";
+import process from "node:process";
+
+export interface VersionPluginOptions {
+ version: string;
+}
+
+export function generateVersion(options: VersionPluginOptions) {
+ return {
+ name: "generate-version",
+ closeBundle() {
+ const versionInfo = {
+ version: options.version,
+ buildTime: new Date().toISOString(),
+ environment: process.env.NODE_ENV,
+ };
+
+ // 写入到 dist 目录
+ const distPath = path.resolve(process.cwd(), "dist/version.json");
+ fs.writeFileSync(distPath, JSON.stringify(versionInfo, null, 2));
+ console.log(`✓ Generated version.json: ${options.version}`);
+ },
+ };
+}
diff --git a/src/composables/useAppUpdate.ts b/src/composables/useAppUpdate.ts
index 919816b..a9083f2 100644
--- a/src/composables/useAppUpdate.ts
+++ b/src/composables/useAppUpdate.ts
@@ -58,8 +58,8 @@ export function useAppUpdate() {
return appInfo.version;
}
- // Web 平台从环境变量或 package.json 获取
- return import.meta.env.VITE_APP_VERSION;
+ // Web 平台从编译时注入的全局变量获取(来自 package.json)
+ return __APP_VERSION__;
}
catch (error) {
console.error("获取当前版本失败:", error);
diff --git a/tsconfig.node.json b/tsconfig.node.json
index a2365d8..b35a97e 100644
--- a/tsconfig.node.json
+++ b/tsconfig.node.json
@@ -10,5 +10,5 @@
},
"allowSyntheticDefaultImports": true
},
- "include": ["vite.config.ts", "tailwind.config.ts"]
+ "include": ["vite.config.ts", "tailwind.config.ts", "scripts/**/*.ts"]
}
diff --git a/types/env.d.ts b/types/env.d.ts
index 31ee272..acaa264 100644
--- a/types/env.d.ts
+++ b/types/env.d.ts
@@ -12,9 +12,10 @@ interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_TRADINGVIEW_LIBRARY_URL: string;
readonly VITE_TRADINGVIEW_DATA_API_URL: string;
- readonly VITE_APP_VERSION: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
+
+declare const __APP_VERSION__: string;
diff --git a/vault.md b/vault.md
deleted file mode 100644
index badf08e..0000000
--- a/vault.md
+++ /dev/null
@@ -1,12 +0,0 @@
-open float Y 开盘价 每一个纬度(例如天纬度,就是一天的第一笔成交价格)的第一笔成交价格
-high float Y 最高价
-low float Y 最低价
-close float Y 收盘价
-settle float Y 结算价 每一个纬度(某一个时间段之内的金额总和 / 交易量)
-
-接口
-代币化分类接口
-代币化列表接口
-代币化详情接口
-代币化数据接口
-买卖接口
diff --git a/vite.config.ts b/vite.config.ts
index e9cc781..d53b27c 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,3 +1,4 @@
+import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import tailwindcss from "@tailwindcss/vite";
@@ -11,9 +12,13 @@ import icons from "unplugin-icons/vite";
import { IonicResolver } from "unplugin-vue-components/resolvers";
import components from "unplugin-vue-components/vite";
import { defineConfig } from "vite";
+import { generateVersion } from "./scripts/build";
dotenv.config({ path: `.env.${process.env.NODE_ENV}` });
+const packageJson = JSON.parse(fs.readFileSync("./package.json", "utf-8"));
+const appVersion = packageJson.version;
+
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
@@ -34,9 +39,12 @@ export default defineConfig({
directoryAsNamespace: true,
resolvers: [IonicResolver(), iconsResolver({ prefix: "i" })],
}),
+ generateVersion({
+ version: appVersion,
+ }),
],
define: {
- APP_VERSION: JSON.stringify(process.env.VITE_APP_VERSION || "0.0.1"),
+ __APP_VERSION__: JSON.stringify(appVersion),
},
resolve: {
alias: {