diff --git a/miniprogram/app.json b/miniprogram/app.json index 213c23d..961f61d 100755 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -8,7 +8,8 @@ "pages/logs/logs", "pages/history/history", "pages/terms/terms", - "pages/privacy/privacy" + "pages/privacy/privacy", + "pages/coupon/coupon" ], "window": { "navigationBarTextStyle": "black", @@ -17,15 +18,6 @@ "backgroundColor": "#ffffff", "backgroundTextStyle": "light" }, - - "permission": { - "scope.camera": { - "desc": "需要使用相机拍照识别图片" - }, - "scope.writePhotosAlbum": { - "desc": "需要访问相册选择图片" - } - }, "componentFramework": "glass-easel", "sitemapLocation": "sitemap.json", "lazyCodeLoading": "requiredComponents", diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 8119d96..1be5829 100755 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -12,6 +12,7 @@ App({ onLaunch() { console.log('小程序启动') + wx.cloud.init() // 初始化登录状态 this.initLoginStatus() diff --git a/miniprogram/pages/coupon/coupon.json b/miniprogram/pages/coupon/coupon.json new file mode 100644 index 0000000..17180ce --- /dev/null +++ b/miniprogram/pages/coupon/coupon.json @@ -0,0 +1,8 @@ +{ + "navigationBarTextStyle": "black", + "navigationBarBackgroundColor": "#ffffff", + "backgroundColor": "#f8f9fa", + "backgroundTextStyle": "light", + "usingComponents": {}, + "navigationBarTitleText": "积分" +} \ No newline at end of file diff --git a/miniprogram/pages/coupon/coupon.ts b/miniprogram/pages/coupon/coupon.ts new file mode 100644 index 0000000..012a203 --- /dev/null +++ b/miniprogram/pages/coupon/coupon.ts @@ -0,0 +1,7 @@ +// coupon.ts +Page({ + data: {}, + onLoad() { + console.log('Coupon page loaded'); + }, +}); \ No newline at end of file diff --git a/miniprogram/pages/coupon/coupon.wxml b/miniprogram/pages/coupon/coupon.wxml new file mode 100644 index 0000000..5491fd2 --- /dev/null +++ b/miniprogram/pages/coupon/coupon.wxml @@ -0,0 +1,24 @@ + + + + + 积分 + 100 + + ¥1.00 + + + + 积分 + 100 + + ¥1.00 + + + + 积分 + 100 + + ¥1.00 + + \ No newline at end of file diff --git a/miniprogram/pages/coupon/coupon.wxss b/miniprogram/pages/coupon/coupon.wxss new file mode 100644 index 0000000..cb6daff --- /dev/null +++ b/miniprogram/pages/coupon/coupon.wxss @@ -0,0 +1,72 @@ +/* coupon.less */ +.coupon-container { + display: flex; + justify-content: flex-start; + align-items: flex-start; + align-content: start; + flex-wrap: wrap; + height: 100%; + padding: 0; + background-color: #f5f5f5; +} + +.coupon_box{ +background: linear-gradient(to right, #FF4B2B, #FF416C); + width: 40%; + border-radius: 12rpx; + text-align: center; + color: #fff; + font-family: 'Tahoma', sans-serif; + position: relative; + margin: 5% 5% 0 5%; +} + +.coupon_box::before{ + content: ''; + position: absolute; + top: 200rpx; + background: #f5f5f5; + width: 20rpx; + height: 30rpx; + z-index: 1; + left: -1rpx; + border-radius: 0 30rpx 30rpx 0 +} + +.coupon_box::after{ + content: ''; + position: absolute; + top: 200rpx; + background: #f5f5f5; + width: 20rpx; + height: 30rpx; + z-index: 1; + right: -1rpx; + border-radius: 30rpx 0 0 30rpx +} + +.title{ + color: rgba(255,255,255,0.75); + font-weight: 600; + font-size: 32rpx +} + +.how_much{ + font-size: 80rpx; + text-shadow: 0 0 20rpx rgba(0,0,0,0.3); text-align: center +} + +.content{ + padding: 4rpx 0 32rpx 0; + border-bottom: 4rpx dashed rgba(0,0,0,0.15); + position: relative; +} +.content::before{ + content: '100'; + position: absolute; + color: rgba(255,255,255,0.15); + top: 0%; + left: 16rpx; + font-size: 144rpx; + font-weight: bold; +} \ No newline at end of file diff --git a/miniprogram/pages/profile/profile.wxml b/miniprogram/pages/profile/profile.wxml index c6eb05f..04100ae 100755 --- a/miniprogram/pages/profile/profile.wxml +++ b/miniprogram/pages/profile/profile.wxml @@ -67,9 +67,11 @@ maxcharacter="{{6}}" /> - - - + + + + + diff --git a/miniprogram/pages/profile/profile.wxss b/miniprogram/pages/profile/profile.wxss index 497b300..f68b585 100755 --- a/miniprogram/pages/profile/profile.wxss +++ b/miniprogram/pages/profile/profile.wxss @@ -346,3 +346,17 @@ border-radius: 8rpx; margin: 20rpx; } + +/* Navigation cell styles */ +.cell-navigator { + display: block; + text-decoration: none; +} + +.cell-navigator .t-cell { + background: #ffffff; +} + +.cell-navigator::after { + border: none; +} diff --git a/miniprogram/utils/api.ts b/miniprogram/utils/api.ts index 4e1d801..00ef221 100755 --- a/miniprogram/utils/api.ts +++ b/miniprogram/utils/api.ts @@ -4,13 +4,23 @@ import { ILoginResponse, IApiResponse, IRecognitionResponse, - ExtendedWordDetail, IAuditHistoryResponse, IUserInfo, IDailySummaryResponse, YdWordDetail } from '../types/app'; import { BASE_URL } from './config'; +import { cloudConfig } from './cloud.config'; + +// 添加对 ICloud 命名空间的引用 +declare namespace ICloud { + interface CallContainerResult { + data: any; + header: Record; + statusCode: number; + errMsg: string; + } +} // 音频缓存映射 const audioCache: Map = new Map() @@ -234,128 +244,191 @@ class ApiManager { retryCount: number = 0 ): Promise> { const maxRetries = 1 // 最多重试1次(首次请求 + 1次重试) - - return new Promise(async (resolve, reject) => { - if (showLoading && retryCount === 0) { - // wx.showLoading({ title: '加载中...' }) - } - console.log('发起请求:', { - url: `${BASE_URL}${url}`, - method, - hasData: !!data, - hasHeaders: !!this.getHeaders(), - retryCount - }) + if (BASE_URL.includes('https://prod')){ + return this.wx_request(url, method, data, showLoading) + } + else { + return new Promise(async (resolve, reject) => { + if (showLoading && retryCount === 0) { + // wx.showLoading({ title: '加载中...' }) + } - wx.request({ - url: `${BASE_URL}${url}`, - method, - data, - header: this.getHeaders(), - success: async (res) => { - if (showLoading) { - wx.hideLoading() - } + console.log('发起请求:', { + url: `${BASE_URL}${url}`, + method, + hasData: !!data, + hasHeaders: !!this.getHeaders(), + retryCount + }) - console.log('请求响应:', { - statusCode: res.statusCode, - hasData: !!res.data, - retryCount, - success: true - }) - - if (res.statusCode === 200) { - const response = res.data as IApiResponse - if (response.code === 0 || response.code === 200) { - resolve(response) - } else { - // 业务错误 - const errorMsg = response.message || response.msg || '请求失败' - console.error('业务错误:', errorMsg, response) - wx.showToast({ - title: errorMsg, - icon: 'none' - }) - reject(new Error(errorMsg)) + wx.request({ + url: `${BASE_URL}${url}`, + method, + data, + header: this.getHeaders(), + success: async (res) => { + if (showLoading) { + wx.hideLoading() } - } else if (res.statusCode === 401) { - console.warn('401认证失败,尝试自动登录', { retryCount, maxRetries }) - - // 如果还有重试机会,尝试自动登录并重试请求 - if (retryCount < maxRetries) { - try { - console.log('开始401自动登录重试') - - // 清理过期的认证数据 - this.clearAuthData() - - // 进行智能登录 - const loginResult = await this.smartLogin(true) // 强制刷新token - - if (loginResult) { - console.log('401自动登录成功,重试请求') - // 登录成功后重试原请求 - try { - const retryResult = await this.request(url, method, data, false, retryCount + 1) - resolve(retryResult) - } catch (retryError) { - reject(retryError) + + console.log('请求响应:', { + statusCode: res.statusCode, + hasData: !!res.data, + retryCount, + success: true + }) + + if (res.statusCode === 200) { + const response = res.data as IApiResponse + if (response.code === 0 || response.code === 200) { + resolve(response) + } else { + // 业务错误 + const errorMsg = response.message || response.msg || '请求失败' + console.error('业务错误:', errorMsg, response) + wx.showToast({ + title: errorMsg, + icon: 'none' + }) + reject(new Error(errorMsg)) + } + } else if (res.statusCode === 401) { + console.warn('401认证失败,尝试自动登录', { retryCount, maxRetries }) + + // 如果还有重试机会,尝试自动登录并重试请求 + if (retryCount < maxRetries) { + try { + console.log('开始401自动登录重试') + + // 清理过期的认证数据 + this.clearAuthData() + + // 进行智能登录 + const loginResult = await this.smartLogin(true) // 强制刷新token + + if (loginResult) { + console.log('401自动登录成功,重试请求') + // 登录成功后重试原请求 + try { + const retryResult = await this.request(url, method, data, false, retryCount + 1) + resolve(retryResult) + } catch (retryError) { + reject(retryError) + } + } else { + console.error('401自动登录失败') + this.handleTokenExpired() + reject(new Error('登录失败,请重新登录')) } - } else { - console.error('401自动登录失败') + } catch (loginError) { + console.error('401自动登录发生错误:', loginError) this.handleTokenExpired() reject(new Error('登录失败,请重新登录')) } - } catch (loginError) { - console.error('401自动登录发生错误:', loginError) + } else { + // 重试次数耗尽,直接处理token过期 + console.error('401重试次数耗尽,跳转登录页') this.handleTokenExpired() - reject(new Error('登录失败,请重新登录')) + reject(new Error('登录已过期')) } + } else if (res.statusCode === 400) { + const response = res.data as IApiResponse + const errorMsg = response.msg || '请求失败' + console.error('400 错误:', errorMsg, response) + reject(new Error(errorMsg)) + } else if (res.statusCode === 403) { + const response = res.data as IApiResponse + const errorMsg = response.msg || '请求失败' + console.error('403 错误:', errorMsg, response) + reject(new Error(errorMsg)) + } else if (res.statusCode === 404 ) { + const response = res.data as IApiResponse + const errorMsg = response.msg || '请求失败' + console.error('404 错误:', errorMsg, response) + reject(new Error(errorMsg)) } else { - // 重试次数耗尽,直接处理token过期 - console.error('401重试次数耗尽,跳转登录页') - this.handleTokenExpired() - reject(new Error('登录已过期')) + console.error('HTTP错误:', res.statusCode, res.data) + wx.showToast({ + title: `网络请求失败 (${res.statusCode})`, + icon: 'none' + }) + reject(new Error(`HTTP ${res.statusCode}`)) } - } else if (res.statusCode === 400) { - const response = res.data as IApiResponse - const errorMsg = response.msg || '请求失败' - console.error('400 错误:', errorMsg, response) - reject(new Error(errorMsg)) - } else if (res.statusCode === 403) { - const response = res.data as IApiResponse - const errorMsg = response.msg || '请求失败' - console.error('403 错误:', errorMsg, response) - reject(new Error(errorMsg)) - } else if (res.statusCode === 404 ) { - const response = res.data as IApiResponse - const errorMsg = response.msg || '请求失败' - console.error('404 错误:', errorMsg, response) - reject(new Error(errorMsg)) - } else { - console.error('HTTP错误:', res.statusCode, res.data) + }, + fail: (error) => { + if (showLoading) { + wx.hideLoading() + } + + console.error('请求失败:', error) wx.showToast({ - title: `网络请求失败 (${res.statusCode})`, + title: '网络连接失败', icon: 'none' }) - reject(new Error(`HTTP ${res.statusCode}`)) + reject(new Error('网络连接失败: ' + JSON.stringify(error))) } + }) + }) + } + } + + // 微信云托管请求方法 + private async wx_request( + path: string, + method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET', + data?: any, + showLoading: boolean = true + ): Promise { + return new Promise((resolve, reject) => { + if (showLoading) { + wx.showLoading({ title: '加载中...' }); + } + + console.log('发起微信云托管请求:', { + path, + method, + hasData: !!data + }); + + wx.cloud.callContainer({ + config: { + env: cloudConfig.env // 使用配置文件中的环境ID }, - fail: (error) => { + path: path.startsWith('/') ? path : `/${path}`, // 确保路径以 / 开头 + method, + header: { + 'X-WX-SERVICE': cloudConfig.service // 使用配置文件中的服务名称 + }, + data: data || {}, // 确保数据对象不为 undefined + success: (res: ICloud.CallContainerResult) => { if (showLoading) { - wx.hideLoading() + wx.hideLoading(); } - - console.error('请求失败:', error) + + console.log('微信云托管请求响应:', { + statusCode: res.statusCode, + hasData: !!res.data, + success: true + }); + + // 直接返回响应数据,由调用方处理业务逻辑 + resolve(res.data); + }, + fail: (error: any) => { + if (showLoading) { + wx.hideLoading(); + } + + console.error('微信云托管请求失败:', error); wx.showToast({ title: '网络连接失败', icon: 'none' - }) - reject(new Error('网络连接失败: ' + JSON.stringify(error))) + }); + reject(new Error('网络连接失败: ' + JSON.stringify(error))); } - }) - }) + }); + }); } // 处理 token 过期 @@ -1658,3 +1731,13 @@ class ApiManager { const apiManager = new ApiManager() export default apiManager export { ApiManager } + + + + + + + + + + diff --git a/miniprogram/utils/cloud.config.ts b/miniprogram/utils/cloud.config.ts new file mode 100644 index 0000000..3fb9a73 --- /dev/null +++ b/miniprogram/utils/cloud.config.ts @@ -0,0 +1,45 @@ +// 微信云托管配置文件 +// 区分不同环境的配置 + +// 环境配置接口 +export interface CloudConfig { + env: string; + service: string; +} + +// 根据当前环境获取配置 +// 在微信小程序中,可以通过 wx.getAccountInfoSync() 获取环境信息 +const accountInfo = wx.getAccountInfoSync(); +const envVersion = accountInfo.miniProgram.envVersion || 'release'; // develop, trial, release + +// 环境配置映射 +const envConfigMap: Record = { + // 开发环境 + develop: { + env: 'prod-1g647ekk563f2652', // 开发环境云托管环境ID + service: 'prod' // 开发环境服务名称 + }, + // 体验版环境 + trial: { + env: 'prod-1g647ekk563f2652', // 体验版云托管环境ID + service: 'prod' // 体验版服务名称 + }, + // 正式版环境 + release: { + env: 'prod-1g647ekk563f2652', // 正式版云托管环境ID + service: 'prod' // 正式版服务名称 + } +}; + +// 导出当前环境的配置 +export const cloudConfig: CloudConfig = envConfigMap[envVersion] || envConfigMap['release']; + +// 如果需要自定义配置,可以覆盖默认配置 +export function setCloudConfig(config: Partial): void { + if (config.env !== undefined) { + cloudConfig.env = config.env; + } + if (config.service !== undefined) { + cloudConfig.service = config.service; + } +} \ No newline at end of file diff --git a/miniprogram/utils/config.ts b/miniprogram/utils/config.ts index c762672..4903d4e 100755 --- a/miniprogram/utils/config.ts +++ b/miniprogram/utils/config.ts @@ -2,6 +2,7 @@ // API 基础域名 export const BASE_URL = 'https://app.xhzone.cn' +// export const BASE_URL = 'https://prod-201510-4-1385696640.sh.run.tcloudbase.com' // 文件服务基础路径 export const FILE_BASE_URL = `${BASE_URL}/api/v1/file` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c87782a..b90c2df 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "miniprogram-ts-less-quickstart", - "version": "1.0.0", + "version": "25.9.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "miniprogram-ts-less-quickstart", - "version": "1.0.0", + "version": "25.9.10", "dependencies": { - "tdesign-miniprogram": "^1.10.1" + "tdesign-miniprogram": "^1.11.0" }, "devDependencies": { "miniprogram-api-typings": "^2.8.3-1" @@ -22,9 +22,9 @@ "license": "MIT" }, "node_modules/tdesign-miniprogram": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/tdesign-miniprogram/-/tdesign-miniprogram-1.10.1.tgz", - "integrity": "sha512-rKM3JYfJGB+R9G/yp6kGVN0Kc0hKqKoQnpoWm7OVrXNsYM0dlJXwTf+WsLkQtgUyfM20taqNaxXD852Bx7IseQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/tdesign-miniprogram/-/tdesign-miniprogram-1.11.0.tgz", + "integrity": "sha512-1Siv2HVrSVlFiQBQXEjBrCvvsC3NQUsGgiej2RDXOxXHAPH2nynLhJhK+wu+KptzXi5GV+XYr+PHV2Je8vgW3w==", "license": "MIT" } } diff --git a/package.json b/package.json index e67bfaf..cb1ccd7 100755 --- a/package.json +++ b/package.json @@ -10,6 +10,6 @@ "miniprogram-api-typings": "^2.8.3-1" }, "dependencies": { - "tdesign-miniprogram": "^1.10.1" + "tdesign-miniprogram": "^1.11.0" } } diff --git a/project.config.json b/project.config.json index f1ca11d..7c55e37 100755 --- a/project.config.json +++ b/project.config.json @@ -51,5 +51,5 @@ "ignore": [], "include": [] }, - "appid": "wxe739c0e6fb02eda8" + "appid": "wxd6917f77eb2723fb" } \ No newline at end of file diff --git a/project.private.config.json b/project.private.config.json index 61dfc12..8707f48 100755 --- a/project.private.config.json +++ b/project.private.config.json @@ -18,10 +18,17 @@ "checkInvalidKey": true, "ignoreDevUnusedFiles": true }, - "libVersion": "3.9.3", + "libVersion": "2.33.0", "condition": { "miniprogram": { "list": [ + { + "name": "pages/coupon/coupon", + "pathName": "pages/coupon/coupon", + "query": "", + "scene": null, + "launchMode": "default" + }, { "name": "", "pathName": "pages/result/result", diff --git a/typings/types/wx/lib.wx.cloud.d.ts b/typings/types/wx/lib.wx.cloud.d.ts index de0ce09..b722dd8 100755 --- a/typings/types/wx/lib.wx.cloud.d.ts +++ b/typings/types/wx/lib.wx.cloud.d.ts @@ -121,6 +121,12 @@ interface WxCloud { CloudID: ICloud.ICloudIDConstructor CDN: ICloud.ICDNConstructor + + // Add callContainer method for WeChat Cloud Hosting + callContainer(param: ICloud.CallContainerParam): void + callContainer( + param: RQ + ): Promise } declare namespace ICloud { @@ -228,6 +234,29 @@ declare namespace ICloud { (options: string | ArrayBuffer | ICDNFilePathSpec): CDN } // === end === + + // === API: callContainer === + interface CallContainerResult extends IAPISuccessParam { + data: any + header: Record + statusCode: number + errMsg: string + } + + interface CallContainerParam extends ICloudAPIParam { + config?: { + env?: string + } + path: string + method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT' + header?: Record + data?: any + dataType?: string + responseType?: string + timeout?: number + } + // === end === + } // === Database ===