diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json index 158b50de..743881b3 100644 --- a/.hbuilderx/launch.json +++ b/.hbuilderx/launch.json @@ -7,6 +7,10 @@ "packageName" : "com.huawei.hisuite", "playground" : "standard", "type" : "uni-app:app-android" + }, + { + "playground" : "standard", + "type" : "uni-app:app-ios" } ] } diff --git a/ak/config_fixed.uts b/ak/config_fixed.uts deleted file mode 100644 index 2d9e6455..00000000 --- a/ak/config_fixed.uts +++ /dev/null @@ -1,37 +0,0 @@ -// Supabase 配置 -// 内网环境 - 本地部署的 Supabase -// IP: 192.168.1.62 -// IP: 192.168.1.62 -// Kong HTTP Port: 8000 -//自己的配置自己解开即可 -//export const SUPA_URL: string = 'http://192.168.1.61:18000' -//export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlLTEiLCJpYXQiOjE3Njk2NzY0OTgsImV4cCI6MTkyNzM1NjQ5OH0.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' -export const SUPA_URL: string = 'http://192.168.1.61:18000' -export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlLTEiLCJpYXQiOjE3Njk2NzY0OTgsImV4cCI6MTkyNzM1NjQ5OH0.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' - -// WebSocket 实时连接(内网使用 ws:// 而非 wss://) -// export const WS_URL: string = 'ws://192.168.1.61:18000/realtime/v1/websocket' -export const WS_URL: string = 'ws://192.168.1.61:18000/realtime/v1/websocket' - -// 备用配置(已注释,如需切换可取消注释) -// 开发环境 - 其他内网地址 -// export const SUPA_URL: string = 'http://192.168.0.150:8080' -// export const SUPA_KEY: string = 'your-anon-key' -// export const WS_URL: string = 'ws://192.168.0.150:8080/realtime/v1/websocket' - -// 生产环境 - Supabase 云服务(已注释) -// export const SUPA_URL: string = 'https://ak3.oulog.com' -// export const SUPA_KEY: string = 'your-anon-key' -// export const WS_URL: string = 'wss://ak3.oulog.com/realtime/v1/websocket' - -// 指向你的 Supabase 服务(开发/私有部署) -// export const SUPA_URL: string = 'http://192.168.1.64:3000' -// export const SUPA_KEY: string = 'your-anon-key' -// export const WS_URL: string = 'ws://192.168.1.64:3000/realtime/v1' - -// 路由配置 -export const HOME_REDIRECT: string = '/pages/mall/consumer/index' -export const TABORPAGE: string = '/pages/mall/consumer/index' - -// 测试模式:放开任意跳转(禁用启动页/登录/401 的强制重定向) -export const IS_TEST_MODE: boolean = true diff --git a/components/supadb/aksupa.uts b/components/supadb/aksupa.uts index 8dad3a69..b1a2914b 100644 --- a/components/supadb/aksupa.uts +++ b/components/supadb/aksupa.uts @@ -116,12 +116,28 @@ export class AkSupaQueryBuilder { private _addCond(afield : string, op : string, value : any | null) : AkSupaQueryBuilder { //console.log('add cond:', op, afield, value) const field = encodeURIComponent(afield)!! - // 将 null 转换为字符串 'null',避免构造对象时缺少 value 属性 - let safeValue = value; + // 将值安全存储,避免安卓端类型转换问题 + let safeValue: any | null = value; if (value === null) { safeValue = 'null'; + } else if (Array.isArray(value)) { + // 数组类型保持原样,用于 in 操作符 + safeValue = value; + } else if (typeof value === 'number') { + // 数字类型保持原样 + safeValue = value; + } else if (typeof value === 'boolean') { + // 布尔类型保持原样 + safeValue = value; + } else if (typeof value !== 'string') { + // 其他类型尝试转换为字符串 + try { + safeValue = value.toString(); + } catch (e) { + safeValue = ''; + } } - this._conditions.push({ field, op, value: safeValue, logic: this._nextLogic }); + this._conditions.push({ field, op, value: safeValue ?? '', logic: this._nextLogic }); //console.log(this._conditions) this._nextLogic = 'and'; return this; @@ -209,6 +225,23 @@ export class AkSupaQueryBuilder { //console.log('设置 range:', from, 'to', to); return this; } + + // 辅助函数:安全地将值转换为字符串 + private _valToStr(val: any): string { + if (val == null) return ''; + try { + // 尝试直接调用 toString + return val.toString(); + } catch (e) { + try { + // 尝试 JSON 序列化 + return JSON.stringify(val); + } catch (e2) { + return ''; + } + } + } + // 将 _conditions 强类型直接转换为 Supabase/PostgREST 查询字符串(不再用 UTSJSONObject 做中转) private _buildFilter() : string | null { if (this._conditions.length == 0 && (this._orString==null || this._orString == "")) { @@ -236,12 +269,13 @@ export class AkSupaQueryBuilder { const op = cond.op; const val = cond.value; if ((op == 'in' || op == 'not.in') && Array.isArray(val)) { - params.push(`${k}=${op}.(${val.map(x => typeof x == 'object' ? encodeURIComponent(JSON.stringify(x)) : encodeURIComponent(x.toString())).join(',')})`); + params.push(`${k}=${op}.(${val.map(x => this._valToStr(x)).map(x => encodeURIComponent(x)).join(',')})`); } else if ((op == 'is' || op == 'not.is') && (val == null || val == 'null')) { params.push(`${k}=${op}.null`); + } else if (op == 'like' || op == 'ilike') { + params.push(`${k}=${op}.${this._valToStr(val)}`); } else { - const opvalstr: string = (typeof val == 'object') ? JSON.stringify(val) : (val as string); - params.push(`${k}=${op}.${encodeURIComponent(opvalstr)}`); + params.push(`${k}=${op}.${encodeURIComponent(this._valToStr(val))}`); } } // 处理 or 条件 @@ -251,17 +285,21 @@ export class AkSupaQueryBuilder { const op = o.op; const val = o.value; if (op == "in" && Array.isArray(val)) { - return `${k}.in.(${val.map(x => encodeURIComponent(x as string)).join(",")})`; + return `${k}.in.(${val.map(x => encodeURIComponent(this._valToStr(x))).join(",")})`; } if (op == "is" && (val == null)) { return `${k}.is.null`; } - return `${k}.${op}.${encodeURIComponent(val as string)}`; + if (op == "like" || op == "ilike") { + return `${k}.${op}.${this._valToStr(val)}`; + } + return `${k}.${op}.${encodeURIComponent(this._valToStr(val))}`; }).join(","); params.push(`or=(${orStr})`); } if (this._orString!=null && this._orString !== "") { - params.push(`or=(${encodeURIComponent(this._orString!!)})`); + console.log('[AkSupaQueryBuilder] or字符串:', this._orString) + params.push(`or=(${this._orString!!})`); } return params.length > 0 ? params.join('&') : null; } @@ -316,7 +354,7 @@ export class AkSupaQueryBuilder { async execute() : Promise> { //console.log('execute') const filter = this._buildFilter(); - //console.log('execute', filter) + console.log('[AkSupaQueryBuilder] execute - 表:', this._table, 'filter:', filter) let res : any; switch (this._action) { case 'select': { diff --git a/pages/main/cart.uvue b/pages/main/cart.uvue index 3a446d51..58facd48 100644 --- a/pages/main/cart.uvue +++ b/pages/main/cart.uvue @@ -145,6 +145,8 @@ + + @@ -966,7 +968,89 @@ const goToCheckout = () => { /* 必须设置 height: 0 或 overflow: hidden 可以在 flex 容器中正确收缩 */ height: 0px; width: 100%; - padding-bottom: 60px; /* 为底部结算栏留出空间 */ + padding-bottom: 20px; /* 减小内边距,因为结算栏已在内容流中 */ +} + +/* 购物车操作栏 (移至推荐商品上方) */ +.cart-action-bar { + background-color: white; + margin: 10px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0,0,0,0.05); + z-index: 10; +} + +.action-bar-content { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 12px 15px; +} + +.action-left { + display: flex; + flex-direction: row; + align-items: center; +} + +.action-right { + display: flex; + flex-direction: row; + align-items: center; +} + +.select-all { + display: flex; + flex-direction: row; + align-items: center; +} + +.select-all-text { + font-size: 14px; + color: #333; + margin-left: 8px; +} + +.total-info { + display: flex; + flex-direction: row; + align-items: baseline; + margin-right: 15px; +} + +.total-text { + font-size: 14px; + color: #666; +} + +.total-price { + font-size: 18px; + color: #ff5000; + font-weight: bold; + margin-left: 4px; +} + +.checkout-btn { + background-color: #ff5000; + color: white; + border: none; + border-radius: 20px; + padding: 8px 15px; + font-size: 14px; + font-weight: bold; + min-width: 100px; +} + +.delete-btn { + background-color: #fff; + color: #ff5000; + border: 1px solid #ff5000; + border-radius: 20px; + padding: 8px 15px; + font-size: 14px; + font-weight: bold; + min-width: 100px; } /* 空购物车 */ @@ -1672,5 +1756,11 @@ const goToCheckout = () => { width: 1400px; /* max-width -> width */ } } + + .tabbar-safe-area { + height: 100px; + width: 100%; + background-color: transparent; + } diff --git a/pages/mall/consumer/checkout.uvue b/pages/mall/consumer/checkout.uvue index 07d7c51b..f8d52891 100644 --- a/pages/mall/consumer/checkout.uvue +++ b/pages/mall/consumer/checkout.uvue @@ -3,30 +3,30 @@ - + + + 📍 + - {{ selectedAddress!!.recipient_name }} - {{ selectedAddress!!.phone }} - - 默认 - - - {{ getFullAddress(selectedAddress!!) }} + {{ selectedAddress!!.recipient_name }} + {{ selectedAddress!!.phone }} + + 默认 + + + {{ getFullAddress(selectedAddress!!) }} 请选择收货地址 - + + + - - - - 共 {{ checkoutItems.length }} 件商品 - - + @@ -34,27 +34,25 @@ {{ group.shopName }} + - {{ item.product_name }} - {{ formatSpecs(item.sku_specifications) }} - + + {{ item.product_name }} ¥{{ item.price }} + + + {{ formatSpecs(item.sku_specifications) }} ×{{ item.quantity }} + + + 小计: + ¥{{ (item.price * item.quantity).toFixed(2) }} + - - - - 配送方式 - 快递 免邮 - - - 小计: - ¥{{ getGroupTotal(group) }} - @@ -63,70 +61,76 @@ - - 配送方式 - - - {{ option.name }} - ¥{{ option.price }} - + + + 配送方式 + + + {{ option.name }} + + + {{ deliveryOptions.find(opt => opt.id === selectedDelivery)?.description }} + 费用: ¥{{ deliveryOptions.find(opt => opt.id === selectedDelivery)?.price.toFixed(2) }} + - - 优惠券 - - {{ selectedCoupon.template?.name ?? '已选择优惠券 (¥' + (selectedCoupon.template?.discount_value ?? 0) + ')' }} - 选择优惠券 - + + + 优惠券 + + {{ selectedCoupon.template?.name ?? '已选择优惠券' }} + 暂无可用优惠券 + + - - 买家留言 - \r\n \r\n \r\n \r\n \r\n \r\n \r\n 地址标签\r\n \r\n \r\n {{ tag }}\r\n \r\n \r\n \r\n \r\n \r\n 设为默认地址\r\n 下单时优先使用该地址\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n 智能填写\r\n 清空\r\n \r\n \r\n \r\n 示例:张三,13800138000,北京市朝阳区...\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n","\r\n\r\n\r\n\r\n\r\n\r\n\r\n","\r\n