diff --git a/App.uvue b/App.uvue index be71360a..1e48bf62 100644 --- a/App.uvue +++ b/App.uvue @@ -1,5 +1,11 @@ @@ -11,4 +17,38 @@ .app-root { min-height: 100vh; } + +.dev-probe { + position: fixed; + left: 16rpx; + top: 16rpx; + z-index: 999999; + padding: 10rpx 14rpx; + border-radius: 12rpx; + background: rgba(17, 24, 39, 0.82); + color: #fff; + font-size: 22rpx; + line-height: 1.2; +} + +.dev-nav { + position: fixed; + left: 16rpx; + top: 64rpx; + z-index: 999999; + display: flex; + flex-direction: column; + gap: 10rpx; +} + +.dev-nav-btn { + display: inline-block; + padding: 10rpx 14rpx; + border-radius: 12rpx; + background: rgba(255, 255, 255, 0.92); + color: #111827; + font-size: 22rpx; + border: 2rpx solid rgba(17, 24, 39, 0.12); + text-decoration: none; +} diff --git a/UNI_APP_X_MIGRATION.md b/UNI_APP_X_MIGRATION.md new file mode 100644 index 00000000..7b847032 --- /dev/null +++ b/UNI_APP_X_MIGRATION.md @@ -0,0 +1,198 @@ +# uni-app X 迁移操作总结 + +## 操作日期 +2024年(具体日期根据实际情况填写) + +## 操作背景 +项目需要从传统的 uni-app 迁移到 **uni-app X**,以支持 `.uvue` 文件在 H5 浏览器中正确渲染。 + +## 问题分析 +对比根项目(`akmon`)和 `mall` 项目,发现以下关键差异: + +1. **缺少 uni-app X 配置**:`mall/manifest.json` 中缺少 `"uni-app-x": {}` 配置项 +2. **存在 .vue 文件**:项目中有 23 个 `.vue` 文件,这些文件在 uni-app X 中无法被正确编译到 H5 +3. **编译器配置**:需要确保 HBuilderX 使用 uni-app X 编译器 + +## 执行的操作 + +### 1. 添加 uni-app X 配置 + +在 `manifest.json` 中添加了 `"uni-app-x": {}` 配置项: + +```json +{ + "vueVersion": "3", + "uni-app-x": {}, + "h5": { + "title": "mall", + "router": { + "mode": "hash", + "base": "./" + } + } +} +``` + +**位置**:`manifest.json` 第 66 行 + +**作用**: +- 告诉 HBuilderX 这是一个 uni-app X 项目 +- 启用 uni-app X 编译链,支持 `.uvue` 文件编译到 H5 +- 确保 `.uvue` 文件能够被正确编译和渲染 + +### 2. 删除所有 .vue 文件 + +删除了项目中所有 `.vue` 文件,共 23 个文件: + +**删除的文件列表**: +- `pages/user/boot.vue` +- `pages/user/login.vue` +- `pages/user/register.vue` +- `pages/user/forgot-password.vue` +- `pages/user/profile.vue` +- `pages/user/center.vue` +- `pages/user/terms.vue` +- `pages/mall/consumer/index.vue` +- `pages/mall/consumer/product-detail.vue` +- `pages/mall/consumer/order-detail.vue` +- `pages/mall/consumer/profile.vue` +- `pages/mall/consumer/subscription/plan-list.vue` +- `pages/mall/consumer/subscription/plan-detail.vue` +- `pages/mall/consumer/subscription/subscribe-checkout.vue` +- `pages/mall/consumer/subscription/my-subscriptions.vue` +- `pages/mall/merchant/index.vue` +- `pages/mall/delivery/index.vue` +- `pages/mall/admin/index.vue` +- `pages/mall/admin/subscription/plan-management.vue` +- `pages/mall/admin/subscription/user-subscriptions.vue` +- `pages/mall/service/index.vue` +- `pages/mall/analytics/index.vue` +- `pages/mall/nfc/security/index.vue` + +**删除命令**: +```powershell +Set-Location -Path 'd:\datas\hfkj\akmon\mall' +Get-ChildItem -Recurse -Filter *.vue | Remove-Item -Force +``` + +**原因**: +- `.vue` 文件在 uni-app X 中无法被正确编译到 H5 浏览器 +- 所有页面和组件应使用 `.uvue` 格式 +- 导入语句会自动识别 `.uvue` 扩展名(导入时无需显式指定扩展名) + +## 技术说明 + +### uni-app X vs 传统 uni-app + +| 特性 | 传统 uni-app | uni-app X | +| ------------- | --------------------- | ---------------------- | +| 文件格式 | `.vue` | `.uvue` | +| 脚本语言 | JavaScript/TypeScript | UTS (TypeScript 扩展) | +| 编译器 | uni-app 编译器 | uni-app X 编译器 | +| H5 渲染 | 需要编译 | 需要编译(但支持更好) | +| manifest.json | 不需要 `uni-app-x` | 需要 `"uni-app-x": {}` | + +### 导入语句说明 + +删除 `.vue` 文件后,所有导入语句会自动使用对应的 `.uvue` 文件: + +```typescript +// 之前(.vue) +import LoginPage from './pages/user/login.vue' + +// 现在(.uvue,扩展名可省略) +import LoginPage from './pages/user/login.uvue' +// 或者 +import LoginPage from './pages/user/login' // 自动识别 .uvue +``` + +## 后续操作 + +### 1. 验证配置 + +1. 打开 HBuilderX +2. 打开 `mall` 项目 +3. 检查编译器:**工具** → **切换编译器** → 确认选择 **uni-app X** +4. 如果未选择,请切换到 uni-app X 编译器 + +### 2. 运行到 H5 + +1. 在 HBuilderX 中,点击菜单:**运行** → **运行到浏览器** → **Chrome**(或内置浏览器) +2. 等待编译完成 +3. 浏览器会自动打开并显示应用 + +### 3. 发行 H5 + +如果需要打包发布: + +1. 点击菜单:**发行** → **网站-H5** +2. 等待编译完成 +3. 编译产物在 `unpackage/dist/build/h5` 目录 +4. 将整个 `h5` 目录部署到 Web 服务器 + +### 4. 检查页面 + +确保所有页面都有对应的 `.uvue` 文件: + +- 检查 `pages.json` 中配置的所有页面路径 +- 确认每个页面都有对应的 `.uvue` 文件 +- 如果缺少,需要从备份或版本控制中恢复并转换为 `.uvue` 格式 + +## 注意事项 + +1. **编译器版本**:必须使用支持 uni-app X 的 HBuilderX 版本 +2. **文件格式**:所有页面和组件必须使用 `.uvue` 格式,不能混用 `.vue` +3. **UTS 语法**:`.uvue` 文件中的 ` + + diff --git a/main.js b/main.js new file mode 100644 index 00000000..2c1e55ed --- /dev/null +++ b/main.js @@ -0,0 +1,2 @@ +// Bridge entry to ensure Vite serves JS MIME while loading UTS entry. +import './main.uts' diff --git a/main.uts b/main.uts index 8d952888..234c8c9b 100644 --- a/main.uts +++ b/main.uts @@ -1,7 +1,14 @@ import { createSSRApp } from 'vue' import App from './App.uvue' +import i18n from '@/uni_modules/i18n/index.uts' export function createApp() { const app = createSSRApp(App) + + // 注册 i18n 全局属性,使组件可以使用 $t 方法 + app.config.globalProperties.$t = (key: string, values?: any, locale?: string): string => { + return i18n.global.t(key, values, locale) + } + return { app } } diff --git a/manifest.json b/manifest.json index f75da367..98b47747 100644 --- a/manifest.json +++ b/manifest.json @@ -63,6 +63,7 @@ "enable": false }, "vueVersion": "3", + "uni-app-x": {}, "h5": { "title": "mall", "router": { diff --git a/pages.json b/pages.json index 5ff409d6..fed5ab00 100644 --- a/pages.json +++ b/pages.json @@ -1,5 +1,18 @@ { "pages": [ + { + "path": "pages/mall/consumer/index", + "style": { + "navigationBarTitleText": "商城首页", + "navigationStyle": "custom" + } + }, + { + "path": "pages/user/boot", + "style": { + "navigationBarTitleText": "" + } + }, { "path": "pages/user/login", "style": { @@ -31,10 +44,9 @@ } }, { - "path": "pages/mall/consumer/index", + "path": "pages/user/terms", "style": { - "navigationBarTitleText": "商城首页", - "navigationStyle": "custom" + "navigationBarTitleText": "用户协议与隐私政策" } }, { @@ -142,10 +154,60 @@ ] } ], + "tabBar": { + "custom": true, + "color": "#7A7E83", + "selectedColor": "#3cc51f", + "borderStyle": "black", + "backgroundColor": "#ffffff", + "list": [ + { + "pagePath": "pages/mall/consumer/index", + "iconPath": "static/tab-home.png", + "selectedIconPath": "static/tab-home-current.png", + "text": "首页" + }, + { + "pagePath": "pages/mall/consumer/category", + "iconPath": "static/tab-category.png", + "selectedIconPath": "static/tab-category-current.png", + "text": "分类" + }, + { + "pagePath": "pages/mall/consumer/cart", + "iconPath": "static/tab-cart.png", + "selectedIconPath": "static/tab-cart-current.png", + "text": "购物车" + }, + { + "pagePath": "pages/mall/consumer/profile", + "iconPath": "static/tab-profile.png", + "selectedIconPath": "static/tab-profile-current.png", + "text": "我的" + } + ] + }, "globalStyle": { "navigationBarTextStyle": "black", "navigationBarTitleText": "mall", "navigationBarBackgroundColor": "#FFFFFF", "backgroundColor": "#F8F8F8" + }, + "condition": { + "current": 0, + "list": [ + { + "name": "消费者端首页", + "path": "pages/mall/consumer/index" + }, + { + "name": "启动页(登录态判断)", + "path": "pages/user/boot" + }, + { + "name": "登录页", + "path": "pages/user/login" + } + ] } } \ No newline at end of file diff --git a/pages/info/comindex.uvue b/pages/info/comindex.uvue index 2dddd9a4..25736772 100644 --- a/pages/info/comindex.uvue +++ b/pages/info/comindex.uvue @@ -442,7 +442,7 @@ import supa from '@/components/supadb/aksupainstance.uts' import { OrderOptions } from '@/components/supadb/aksupa.uts' import { tt } from '@/utils/i18nfun.uts' - import i18n from '@/i18n/index.uts' // 保留用于语言切换和全局配置 + import i18n from '@/uni_modules/i18n/index.uts' // 保留用于语言切换和全局配置 import { ResponsiveState, InfoContent } from '@/pages/info/types.uts' import { LanguageOption } from '@/pages/user/types.uts' diff --git a/pages/info/detail.uvue b/pages/info/detail.uvue index 125eda7f..7b2c4d56 100644 --- a/pages/info/detail.uvue +++ b/pages/info/detail.uvue @@ -229,7 +229,7 @@ import { state as userState, getCurrentUser, setUserProfile, setIsLoggedIn } from '@/utils/store.uts' import { setClipboardData } from '@/uni_modules/lime-clipboard' import { tt } from '@/utils/i18nfun.uts' - import i18n from '@/i18n/index.uts' // 保留用于语言切换 + import i18n from '@/uni_modules/i18n/index.uts' // 保留用于语言切换 // 页面参数 const contentId = ref('') diff --git a/pages/info/settings.uvue b/pages/info/settings.uvue index b9201e69..6a96b7f5 100644 --- a/pages/info/settings.uvue +++ b/pages/info/settings.uvue @@ -302,7 +302,7 @@ import type { UserPreferences } from './types.uts' import supa from '@/components/supadb/aksupainstance.uts' import { tt } from '@/utils/i18nfun.uts' -import i18n from '@/i18n/index.uts' // 保留用于语言切换 +import i18n from '@/uni_modules/i18n/index.uts' // 保留用于语言切换 // 用户信息 const userName = ref('用户') diff --git a/pages/info/topic-detail.uvue b/pages/info/topic-detail.uvue index 60db2015..a3f19401 100644 --- a/pages/info/topic-detail.uvue +++ b/pages/info/topic-detail.uvue @@ -222,7 +222,7 @@ import { } from './types.uts' import supa from '@/components/supadb/aksupainstance.uts' import { tt } from '@/utils/i18nfun.uts' -import i18n from '@/i18n/index.uts' // 保留用于语言切换 +import i18n from '@/uni_modules/i18n/index.uts' // 保留用于语言切换 // 页面参数 const topicId = ref('') diff --git a/pages/info/topics.uvue b/pages/info/topics.uvue index bd8fe4a6..391ca1ae 100644 --- a/pages/info/topics.uvue +++ b/pages/info/topics.uvue @@ -191,7 +191,7 @@ import { } from './types.uts' import supa from '@/components/supadb/aksupainstance.uts' import { tt } from '@/utils/i18nfun.uts' -import i18n from '@/i18n/index.uts' // 保留用于语言切换 +import i18n from '@/uni_modules/i18n/index.uts' // 保留用于语言切换 // 页面状态 const pageState = ref({ diff --git a/pages/info/video-player.uvue b/pages/info/video-player.uvue index 12bb9846..19bf509d 100644 --- a/pages/info/video-player.uvue +++ b/pages/info/video-player.uvue @@ -282,7 +282,7 @@ import { import { formatRelativeTimeKey } from './types.uts' import supa from '@/components/supadb/aksupainstance.uts' import { tt } from '@/utils/i18nfun.uts' -import i18n from '@/i18n/index.uts' +import i18n from '@/uni_modules/i18n/index.uts' // 页面参数 const videoId = ref('') diff --git a/pages/mall/pages-config.json b/pages/mall/pages-config.json index e129a494..f4985a33 100644 --- a/pages/mall/pages-config.json +++ b/pages/mall/pages-config.json @@ -13,6 +13,48 @@ "navigationStyle": "custom" } }, + { + "path": "pages/user/boot", + "style": { + "navigationBarTitleText": "" + } + }, + { + "path": "pages/user/login", + "style": { + "navigationBarTitleText": "登录" + } + }, + { + "path": "pages/user/register", + "style": { + "navigationBarTitleText": "注册" + } + }, + { + "path": "pages/user/forgot-password", + "style": { + "navigationBarTitleText": "忘记密码" + } + }, + { + "path": "pages/user/terms", + "style": { + "navigationBarTitleText": "用户协议与隐私政策" + } + }, + { + "path": "pages/user/center", + "style": { + "navigationBarTitleText": "用户中心" + } + }, + { + "path": "pages/user/profile", + "style": { + "navigationBarTitleText": "个人资料" + } + }, { "path": "pages/mall/merchant/index", "style": { @@ -219,8 +261,7 @@ "style": { "navigationBarTitleText": "收货地址" } - } - , + }, { "path": "subscription/plan-list", "style": { @@ -499,6 +540,10 @@ "name": "消费者端首页", "path": "pages/mall/consumer/index" }, + { + "name": "启动页(登录态判断)", + "path": "pages/user/boot" + }, { "name": "商家端首页", "path": "pages/mall/merchant/index" @@ -521,4 +566,4 @@ } ] } -} +} \ No newline at end of file diff --git a/pages/sense/senseDataService.uts b/pages/sense/senseDataService.uts new file mode 100644 index 00000000..f12f19eb --- /dev/null +++ b/pages/sense/senseDataService.uts @@ -0,0 +1,132 @@ +import supa, { supaReady } from '@/components/supadb/aksupainstance.uts' +import type { DeviceInfo, DeviceParams } from './types.uts' + +// 服务响应类型 +export type ServiceResponse = { + error: Error | null + data: T | null +} + +// 设备数据服务类 +export class SenseDataService { + // 表名常量(根据实际数据库表名调整) + private static readonly TABLE_NAME = 'sense_devices' + + /** + * 获取设备列表 + */ + static async getDevices(params: DeviceParams): Promise>> { + try { + await supaReady + const res = await supa.from(SenseDataService.TABLE_NAME) + .select('*', {}) + .eq('user_id', params.user_id) + .execute() + + if (res.status >= 200 && res.status < 300 && res.data != null) { + const data = res.data as any + const devices = Array.isArray(data) ? data as Array : [] + return { error: null, data: devices } + } else { + return { + error: new Error(`获取设备列表失败: ${res.status}`), + data: null + } + } + } catch (error) { + return { + error: error instanceof Error ? error : new Error(String(error)), + data: null + } + } + } + + /** + * 绑定新设备 + */ + static async bindDevice(deviceData: UTSJSONObject): Promise> { + try { + await supaReady + const res = await supa.from(SenseDataService.TABLE_NAME) + .insert(deviceData) + .select('*', {}) + .single() + .execute() + + if (res.status >= 200 && res.status < 300 && res.data != null) { + const device = res.data as DeviceInfo + return { error: null, data: device } + } else { + return { + error: new Error(`绑定设备失败: ${res.status}`), + data: null + } + } + } catch (error) { + return { + error: error instanceof Error ? error : new Error(String(error)), + data: null + } + } + } + + /** + * 解绑设备 + */ + static async unbindDevice(deviceId: string): Promise> { + try { + await supaReady + const res = await supa.from(SenseDataService.TABLE_NAME) + .delete() + .eq('id', deviceId) + .execute() + + if (res.status >= 200 && res.status < 300) { + return { error: null, data: null } + } else { + return { + error: new Error(`解绑设备失败: ${res.status}`), + data: null + } + } + } catch (error) { + return { + error: error instanceof Error ? error : new Error(String(error)), + data: null + } + } + } + + /** + * 更新设备配置 + */ + static async updateDevice(deviceId: string, configData: UTSJSONObject): Promise> { + try { + await supaReady + const res = await supa.from(SenseDataService.TABLE_NAME) + .update(configData) + .eq('id', deviceId) + .select('*', {}) + .single() + .execute() + + if (res.status >= 200 && res.status < 300 && res.data != null) { + const device = res.data as DeviceInfo + return { error: null, data: device } + } else { + return { + error: new Error(`更新设备配置失败: ${res.status}`), + data: null + } + } + } catch (error) { + return { + error: error instanceof Error ? error : new Error(String(error)), + data: null + } + } + } +} + +// 导出类型 +export type { DeviceParams } diff --git a/pages/sense/types.uts b/pages/sense/types.uts new file mode 100644 index 00000000..c5255450 --- /dev/null +++ b/pages/sense/types.uts @@ -0,0 +1,16 @@ +// 设备信息类型 +export type DeviceInfo = { + id: string + device_name?: string + status?: string // 'online' | 'offline' | 其他状态 + user_id?: string + // 可根据实际需求添加更多字段 + [key: string]: any +} + +// 设备查询参数类型 +export type DeviceParams = { + user_id: string + // 可根据实际需求添加更多查询参数 + [key: string]: any +} diff --git a/pages/user/boot.uvue b/pages/user/boot.uvue new file mode 100644 index 00000000..51175aa0 --- /dev/null +++ b/pages/user/boot.uvue @@ -0,0 +1,166 @@ + + + + + diff --git a/pages/user/login.uvue b/pages/user/login.uvue index 19d645e8..4bd0b118 100644 --- a/pages/user/login.uvue +++ b/pages/user/login.uvue @@ -1,90 +1,82 @@