consumer模块完成度95%,准备部署消费者端测试
53
App.uvue
@@ -2,16 +2,12 @@
|
|||||||
import { setIsLoggedIn, setUserProfile, getCurrentUser } from '@/utils/store.uts'
|
import { setIsLoggedIn, setUserProfile, getCurrentUser } from '@/utils/store.uts'
|
||||||
import supa from '@/components/supadb/aksupainstance.uts'
|
import supa from '@/components/supadb/aksupainstance.uts'
|
||||||
|
|
||||||
// 自动登录凭据(开发测试用)
|
|
||||||
const AUTO_LOGIN_EMAIL = 'test@mall.com'
|
|
||||||
const AUTO_LOGIN_PASSWORD = 'Hf2152111'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onLaunch: function () {
|
onLaunch: function () {
|
||||||
console.log('App Launch')
|
console.log('App Launch')
|
||||||
|
|
||||||
// 尝试自动登录并跳转(使用 Promise 链)
|
// 检查是否已有有效会话,有则恢复登录状态
|
||||||
this.tryAutoLogin()
|
this.checkExistingSession()
|
||||||
},
|
},
|
||||||
onShow: function () {
|
onShow: function () {
|
||||||
console.log('App Show')
|
console.log('App Show')
|
||||||
@@ -20,11 +16,11 @@
|
|||||||
console.log('App Hide')
|
console.log('App Hide')
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
tryAutoLogin: function(): void {
|
checkExistingSession: function(): void {
|
||||||
// 检查是否已有有效会话
|
// 检查是否已有有效会话
|
||||||
const session = supa.getSession()
|
const session = supa.getSession()
|
||||||
if (session.user != null) {
|
if (session.user != null) {
|
||||||
console.log('已有有效会话,跳过自动登录')
|
console.log('已有有效会话,恢复登录状态')
|
||||||
setIsLoggedIn(true)
|
setIsLoggedIn(true)
|
||||||
uni.reLaunch({ url: '/pages/mall/consumer/index' })
|
uni.reLaunch({ url: '/pages/mall/consumer/index' })
|
||||||
return
|
return
|
||||||
@@ -39,49 +35,14 @@
|
|||||||
console.log('会话恢复成功')
|
console.log('会话恢复成功')
|
||||||
setIsLoggedIn(true)
|
setIsLoggedIn(true)
|
||||||
uni.reLaunch({ url: '/pages/mall/consumer/index' })
|
uni.reLaunch({ url: '/pages/mall/consumer/index' })
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// 恢复失败,执行自动登录
|
|
||||||
this.doAutoLogin()
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
console.log('会话恢复失败,尝试自动登录')
|
console.log('会话恢复失败,需要重新登录')
|
||||||
this.doAutoLogin()
|
|
||||||
})
|
})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行自动登录
|
// 没有有效会话,显示登录页
|
||||||
this.doAutoLogin()
|
console.log('无有效会话,显示登录页')
|
||||||
},
|
|
||||||
doAutoLogin: function(): void {
|
|
||||||
console.log('开始自动登录...')
|
|
||||||
supa.signIn(AUTO_LOGIN_EMAIL, AUTO_LOGIN_PASSWORD).then((result) => {
|
|
||||||
if (result.user != null) {
|
|
||||||
console.log('自动登录成功')
|
|
||||||
setIsLoggedIn(true)
|
|
||||||
|
|
||||||
// 保存用户ID到本地存储
|
|
||||||
const uid = result.user.getString('id')
|
|
||||||
if (uid != null) {
|
|
||||||
uni.setStorageSync('user_id', uid)
|
|
||||||
console.log('用户ID已保存:', uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户资料
|
|
||||||
getCurrentUser().then(() => {
|
|
||||||
console.log('获取用户资料成功')
|
|
||||||
}).catch((e) => {
|
|
||||||
console.log('获取用户资料失败(忽略)')
|
|
||||||
})
|
|
||||||
|
|
||||||
// 直接跳转到首页
|
|
||||||
uni.reLaunch({ url: '/pages/mall/consumer/index' })
|
|
||||||
} else {
|
|
||||||
console.log('自动登录失败,用户需要手动登录')
|
|
||||||
}
|
|
||||||
}).catch((e) => {
|
|
||||||
console.error('自动登录异常:', e)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
//export const SUPA_URL: string = 'http://192.168.1.61:18000'
|
//export const SUPA_URL: string = 'http://192.168.1.61:18000'
|
||||||
//export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlLTEiLCJpYXQiOjE3Njk2NzY0OTgsImV4cCI6MTkyNzM1NjQ5OH0.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
|
//export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlLTEiLCJpYXQiOjE3Njk2NzY0OTgsImV4cCI6MTkyNzM1NjQ5OH0.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
|
||||||
export const SUPA_URL: string = 'http://192.168.1.61:18000'
|
export const SUPA_URL: string = 'http://119.146.131.237:9126'
|
||||||
export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlLTEiLCJpYXQiOjE3Njk2NzY0OTgsImV4cCI6MTkyNzM1NjQ5OH0.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
|
export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlLTEiLCJpYXQiOjE3Njk2NzY0OTgsImV4cCI6MTkyNzM1NjQ5OH0.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
|
||||||
|
|
||||||
// WebSocket 实时连接(内网使用 ws:// 而非 wss://)
|
// WebSocket 实时连接(内网使用 ws:// 而非 wss://)
|
||||||
export const WS_URL: string = 'ws://192.168.1.61:18000/realtime/v1/websocket'
|
export const WS_URL: string = 'ws://119.146.131.237:9126/realtime/v1/websocket'
|
||||||
//export const WS_URL: string = 'ws://localhost:18000/realtime/v1/websocket'
|
//export const WS_URL: string = 'ws://localhost:18000/realtime/v1/websocket'
|
||||||
|
|
||||||
// 备用配置(已注释,如需切换可取消注释)
|
// 备用配置(已注释,如需切换可取消注释)
|
||||||
|
|||||||
20
check_icons.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
|
||||||
|
def check_icon_brightness(file_path):
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
return None
|
||||||
|
img = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)
|
||||||
|
if img is None:
|
||||||
|
return None
|
||||||
|
# 计算非透明区域的平均亮度
|
||||||
|
return np.mean(img)
|
||||||
|
|
||||||
|
icon_dir = r'D:\companyproject\mall\pages\mall\consumer\icons'
|
||||||
|
fav = os.path.join(icon_dir, 'favorite.png')
|
||||||
|
fav_active = os.path.join(icon_dir, 'favorite-active.png')
|
||||||
|
|
||||||
|
print(f"favorite.png brightness: {check_icon_brightness(fav)}")
|
||||||
|
print(f"favorite-active.png brightness: {check_icon_brightness(fav_active)}")
|
||||||
@@ -442,41 +442,24 @@ export class AkSupaQueryBuilder {
|
|||||||
if (res["data"] == null) res["data"] = {};
|
if (res["data"] == null) res["data"] = {};
|
||||||
return res;
|
return res;
|
||||||
} // 新增:支持类型转换的执行方法
|
} // 新增:支持类型转换的执行方法
|
||||||
async executeAs<T = any>() : Promise<AkReqResponse<T | Array<T>>> {
|
async executeAs<T>() : Promise<AkReqResponse<T>> {
|
||||||
const result = await this.execute();
|
const result = await this.execute();
|
||||||
|
|
||||||
// 如果原始 data 是 null,直接返回 null
|
|
||||||
if (result.data == null) {
|
if (result.data == null) {
|
||||||
const aaa = {
|
return result as AkReqResponse<T>;
|
||||||
status: result.status,
|
|
||||||
data: null,
|
|
||||||
headers: result.headers,
|
|
||||||
error: result.error,
|
|
||||||
total: result.total,
|
|
||||||
page: result.page,
|
|
||||||
limit: result.limit,
|
|
||||||
hasmore: result.hasmore,
|
|
||||||
origin: result.origin
|
|
||||||
}
|
|
||||||
return aaa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试类型转换
|
let convertedData : any | null = null;
|
||||||
let convertedData : T | Array<T> | null = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(result.data)) {
|
if (Array.isArray(result.data)) {
|
||||||
// 处理数组数据
|
|
||||||
const dataArray = result.data;
|
const dataArray = result.data;
|
||||||
const convertedArray : Array<T> = [];
|
const convertedArray : Array<any> = [];
|
||||||
//console.log(convertedArray)
|
|
||||||
for (let i = 0; i < dataArray.length; i++) {
|
for (let i = 0; i < dataArray.length; i++) {
|
||||||
const item = dataArray[i];
|
const item = dataArray[i];
|
||||||
if (item instanceof UTSJSONObject) {
|
if (item instanceof UTSJSONObject) {
|
||||||
// #ifdef APP-ANDROID
|
// #ifdef APP-ANDROID
|
||||||
// //console.log(item)
|
|
||||||
const parsed = item.parse<T>();
|
const parsed = item.parse<T>();
|
||||||
// //console.log('ak parsed')
|
|
||||||
// #endif
|
// #endif
|
||||||
// #ifndef APP-ANDROID
|
// #ifndef APP-ANDROID
|
||||||
const parsed = item as T;
|
const parsed = item as T;
|
||||||
@@ -485,10 +468,9 @@ export class AkSupaQueryBuilder {
|
|||||||
convertedArray.push(parsed);
|
convertedArray.push(parsed);
|
||||||
} else {
|
} else {
|
||||||
console.warn('转换失败,使用原始对象:', item);
|
console.warn('转换失败,使用原始对象:', item);
|
||||||
convertedArray.push(item as T);
|
convertedArray.push(item);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 将普通对象转换为 UTSJSONObject 后再 parse
|
|
||||||
const jsonObj = new UTSJSONObject(item);
|
const jsonObj = new UTSJSONObject(item);
|
||||||
// #ifdef APP-ANDROID
|
// #ifdef APP-ANDROID
|
||||||
const parsed = jsonObj.parse<T>();
|
const parsed = jsonObj.parse<T>();
|
||||||
@@ -501,15 +483,14 @@ export class AkSupaQueryBuilder {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.warn('转换失败,使用原始对象:', item);
|
console.warn('转换失败,使用原始对象:', item);
|
||||||
convertedArray.push(item as T);
|
convertedArray.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
convertedData = convertedArray;
|
convertedData = convertedArray;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 处理单个对象
|
const convertedArray : Array<any> = [];
|
||||||
const convertedArray : Array<T> = [];
|
|
||||||
if (result.data instanceof UTSJSONObject) {
|
if (result.data instanceof UTSJSONObject) {
|
||||||
const parsed = result.data.parse<T>();
|
const parsed = result.data.parse<T>();
|
||||||
|
|
||||||
@@ -534,23 +515,10 @@ export class AkSupaQueryBuilder {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('数据类型转换失败,使用原始数据:', e);
|
console.warn('数据类型转换失败,使用原始数据:', e);
|
||||||
console.log(result.data)
|
console.log(result.data)
|
||||||
// 转换失败时,使用原始数据
|
convertedData = result.data as any;
|
||||||
convertedData = result.data as T | Array<T>;
|
|
||||||
}
|
}
|
||||||
result.data = convertedData
|
result.data = convertedData
|
||||||
const aaa = result as AkReqResponse<T | Array<T>
|
return result as AkReqResponse<T>;
|
||||||
// const aaa = {
|
|
||||||
// status: result.status,
|
|
||||||
// data: convertedData,
|
|
||||||
// headers: result.headers,
|
|
||||||
// error: result.error,
|
|
||||||
// total: result.total,
|
|
||||||
// page: result.page,
|
|
||||||
// limit: result.limit,
|
|
||||||
// hasmore: result.hasmore,
|
|
||||||
// origin: result.origin
|
|
||||||
// }
|
|
||||||
return aaa;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
pages.json
@@ -914,32 +914,32 @@
|
|||||||
{
|
{
|
||||||
"pagePath": "pages/mall/consumer/index",
|
"pagePath": "pages/mall/consumer/index",
|
||||||
"text": "首页",
|
"text": "首页",
|
||||||
"iconPath": "static/tabbar/home.svg",
|
"iconPath": "static/tabbar/home.png",
|
||||||
"selectedIconPath": "static/tabbar/home-active.svg"
|
"selectedIconPath": "static/tabbar/home.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pagePath": "pages/mall/consumer/category",
|
"pagePath": "pages/mall/consumer/category",
|
||||||
"text": "分类",
|
"text": "分类",
|
||||||
"iconPath": "static/tabbar/category.svg",
|
"iconPath": "static/tabbar/category.png",
|
||||||
"selectedIconPath": "static/tabbar/category-active.svg"
|
"selectedIconPath": "static/tabbar/category.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pagePath": "pages/mall/consumer/messages",
|
"pagePath": "pages/mall/consumer/messages",
|
||||||
"text": "消息",
|
"text": "消息",
|
||||||
"iconPath": "static/tabbar/messages.svg",
|
"iconPath": "static/tabbar/message.png",
|
||||||
"selectedIconPath": "static/tabbar/messages-active.svg"
|
"selectedIconPath": "static/tabbar/message.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pagePath": "pages/mall/consumer/cart",
|
"pagePath": "pages/mall/consumer/cart",
|
||||||
"text": "购物车",
|
"text": "购物车",
|
||||||
"iconPath": "static/tabbar/cart.svg",
|
"iconPath": "static/tabbar/cart.png",
|
||||||
"selectedIconPath": "static/tabbar/cart-active.svg"
|
"selectedIconPath": "static/tabbar/cart.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pagePath": "pages/mall/consumer/profile",
|
"pagePath": "pages/mall/consumer/profile",
|
||||||
"text": "我的",
|
"text": "我的",
|
||||||
"iconPath": "static/tabbar/profile.svg",
|
"iconPath": "static/tabbar/user.png",
|
||||||
"selectedIconPath": "static/tabbar/profile-active.svg"
|
"selectedIconPath": "static/tabbar/user.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,52 +1,70 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page-container">
|
<view class="page-container">
|
||||||
<scroll-view class="address-edit-page" direction="vertical">
|
<scroll-view class="address-edit-scroll" scroll-y="true">
|
||||||
<view class="form-group">
|
<view class="address-edit-content">
|
||||||
<view class="form-item">
|
<!-- 基础信息组 -->
|
||||||
<text class="label">收货人</text>
|
<view class="form-group">
|
||||||
<input class="input" v-model="formData.name" placeholder="请填写收货人姓名" />
|
<view class="form-item">
|
||||||
</view>
|
<text class="label">收货人</text>
|
||||||
<view class="form-item">
|
<input class="input" v-model="formData.name" placeholder="请填写收货人姓名" placeholder-class="placeholder" />
|
||||||
<text class="label">手机号码</text>
|
</view>
|
||||||
<input class="input" v-model="formData.phone" type="number" maxlength="11" placeholder="请填写手机号码" />
|
<view class="form-item">
|
||||||
</view>
|
<text class="label">手机号码</text>
|
||||||
<view class="form-item">
|
<input class="input" v-model="formData.phone" type="number" maxlength="11" placeholder="请填写手机号码" placeholder-class="placeholder" />
|
||||||
<text class="label">所在地区</text>
|
</view>
|
||||||
<input class="input" v-model="regionString" placeholder="省市区县、乡镇等" />
|
<view class="form-item">
|
||||||
</view>
|
<text class="label">所在地区</text>
|
||||||
<view class="form-item">
|
<input class="input" v-model="regionString" placeholder="省市区县、乡镇等" placeholder-class="placeholder" />
|
||||||
<text class="label">详细地址</text>
|
<text class="arrow-icon">›</text>
|
||||||
<input class="input" v-model="formData.detail" placeholder="街道、楼牌号等" />
|
</view>
|
||||||
</view>
|
<view class="form-item detail-item">
|
||||||
</view>
|
<text class="label">详细地址</text>
|
||||||
|
<textarea class="textarea" v-model="formData.detail" placeholder="街道、楼牌号等" placeholder-class="placeholder" maxlength="100"></textarea>
|
||||||
<view class="form-group">
|
</view>
|
||||||
<view class="form-item">
|
|
||||||
<text class="label">智能填写</text>
|
|
||||||
<textarea class="smart-textarea" v-model="smartInput" placeholder="粘贴姓名+电话+地址,自动识别填充" @input="parseSmartInput" maxlength="200"></textarea>
|
|
||||||
<text class="smart-tip">示例:张三 13800138000 北京市朝阳区三里屯SOHO A座</text>
|
|
||||||
</view>
|
|
||||||
<view class="form-item">
|
|
||||||
<text class="label">标签</text>
|
|
||||||
<view class="tags-container">
|
|
||||||
<text
|
|
||||||
v-for="tag in tags"
|
|
||||||
:key="tag"
|
|
||||||
class="tag-item"
|
|
||||||
:class="{ active: formData.label === tag }"
|
|
||||||
@click="selectTag(tag)"
|
|
||||||
>{{ tag }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
<view class="form-item switch-item">
|
|
||||||
<text class="label">设为默认收货地址</text>
|
|
||||||
<switch :checked="formData.isDefault" color="#ff5000" @change="onSwitchChange" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="footer-btn">
|
<!-- 标签与默认设置组 -->
|
||||||
<button class="save-btn" @click="saveAddress">保存</button>
|
<view class="form-group">
|
||||||
<button v-if="isEdit" class="delete-btn" @click="deleteAddress">删除收货地址</button>
|
<view class="form-item label-section">
|
||||||
|
<text class="label">地址标签</text>
|
||||||
|
<view class="tags-container">
|
||||||
|
<view
|
||||||
|
v-for="tag in tags"
|
||||||
|
:key="tag"
|
||||||
|
class="tag-item"
|
||||||
|
:class="{ active: formData.label === tag }"
|
||||||
|
@click="selectTag(tag)"
|
||||||
|
>
|
||||||
|
<text class="tag-text" :class="{ 'tag-text-active': formData.label === tag }">{{ tag }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="form-item switch-item">
|
||||||
|
<view class="switch-label-group">
|
||||||
|
<text class="label">设为默认地址</text>
|
||||||
|
<text class="sub-label">下单时优先使用该地址</text>
|
||||||
|
</view>
|
||||||
|
<switch :checked="formData.isDefault" color="#ff5000" @change="onSwitchChange" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 智能识别组 -->
|
||||||
|
<view class="form-group smart-group">
|
||||||
|
<view class="smart-header">
|
||||||
|
<text class="smart-title">智能填写</text>
|
||||||
|
<text class="smart-clear" v-if="smartInput" @click="smartInput = ''">清空</text>
|
||||||
|
</view>
|
||||||
|
<textarea class="smart-textarea" v-model="smartInput" placeholder="粘贴整段地址,自动识别姓名、电话、地址" @input="parseSmartInput" maxlength="200"></textarea>
|
||||||
|
<view class="smart-footer">
|
||||||
|
<text class="smart-tip">示例:张三,13800138000,北京市朝阳区...</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作按钮 -->
|
||||||
|
<view class="footer-actions">
|
||||||
|
<button class="save-btn" @click="saveAddress">保存地址</button>
|
||||||
|
<button v-if="isEdit" class="delete-btn" @click="deleteAddress">删除此地址</button>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
@@ -371,35 +389,54 @@ const deleteAddress = () => {
|
|||||||
<style>
|
<style>
|
||||||
.page-container {
|
.page-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
background-color: #f8f8f8;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.address-edit-page {
|
.address-edit-scroll {
|
||||||
flex: 1; /* Replace min-height: 100vh */
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-edit-content {
|
||||||
|
padding: 12px;
|
||||||
|
padding-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
background-color: white;
|
background-color: #ffffff;
|
||||||
margin-bottom: 15px;
|
border-radius: 12px;
|
||||||
padding: 0 15px;
|
padding: 0 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-item {
|
.form-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 1px solid #f5f5f5;
|
padding: 18px 0;
|
||||||
padding: 15px 0;
|
border-bottom: 1px solid #f9f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-item:last-child {
|
.form-item:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item .label {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
@@ -408,54 +445,152 @@ const deleteAddress = () => {
|
|||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch-item {
|
.textarea {
|
||||||
justify-content: space-between;
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #ccc;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标签选择 */
|
||||||
|
.label-section {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-section .label {
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tags-container {
|
.tags-container {
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-item {
|
.tag-item {
|
||||||
font-size: 12px;
|
padding: 6px 18px;
|
||||||
color: #666;
|
background-color: #f7f7f7;
|
||||||
border: 1px solid #ddd;
|
border-radius: 20px;
|
||||||
padding: 4px 12px;
|
margin-right: 12px;
|
||||||
border-radius: 15px;
|
border: 1px solid transparent;
|
||||||
margin-right: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-item.active {
|
.tag-item.active {
|
||||||
background-color: #ff5000;
|
background-color: #fff1eb;
|
||||||
color: white;
|
|
||||||
border-color: #ff5000;
|
border-color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-btn {
|
.tag-text {
|
||||||
margin-top: 30px;
|
font-size: 13px;
|
||||||
padding: 0 15px;
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-text-active {
|
||||||
|
color: #ff5000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 开关项 */
|
||||||
|
.switch-item {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-label-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 智能填写 */
|
||||||
|
.smart-group {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-clear {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-footer {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-tip {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部按钮 */
|
||||||
|
.footer-actions {
|
||||||
|
margin-top: 32px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-btn {
|
.save-btn {
|
||||||
background-color: #ff5000;
|
background-color: #ff5000;
|
||||||
color: white;
|
color: #ffffff;
|
||||||
border-radius: 25px;
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
height: 44px;
|
font-weight: bold;
|
||||||
line-height: 44px;
|
border-radius: 24px;
|
||||||
border: none;
|
border: none;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(255, 80, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete-btn {
|
.delete-btn {
|
||||||
background-color: white;
|
background-color: #ffffff;
|
||||||
color: #333;
|
color: #ee0a24;
|
||||||
border-radius: 25px;
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
height: 44px;
|
border-radius: 24px;
|
||||||
line-height: 44px;
|
border: 1px solid #f0f0f0;
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
463
pages/mall/consumer/address-edit_gbk.txt
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page-container">
|
||||||
|
<scroll-view class="address-edit-page" direction="vertical">
|
||||||
|
<view class="form-group">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">鏀惰揣浜?/text>
|
||||||
|
<input class="input" v-model="formData.name" placeholder="璇峰~鍐欐敹璐т汉濮撳悕" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">鎵嬫満鍙风爜</text>
|
||||||
|
<input class="input" v-model="formData.phone" type="number" maxlength="11" placeholder="璇峰~鍐欐墜鏈哄彿鐮? />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">鎵?鍦ㄥ湴鍖?/text>
|
||||||
|
<input class="input" v-model="regionString" placeholder="鐪佸競鍖哄幙銆佷埂闀囩瓑" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">璇︾粏鍦板潃</text>
|
||||||
|
<input class="input" v-model="formData.detail" placeholder="琛楅亾銆佹ゼ鐗屽彿绛? />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-group">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">鏅鸿兘濉啓</text>
|
||||||
|
<textarea class="smart-textarea" v-model="smartInput" placeholder="绮樿创濮撳悕+鐢佃瘽+鍦板潃锛岃嚜鍔ㄨ瘑鍒~鍏? @input="parseSmartInput" maxlength="200"></textarea>
|
||||||
|
<text class="smart-tip">绀轰緥锛氬紶涓?13800138000 鍖椾含甯傛湞闃冲尯涓夐噷灞疭OHO A搴?/text>
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">鏍囩</text>
|
||||||
|
<view class="tags-container">
|
||||||
|
<text
|
||||||
|
v-for="tag in tags"
|
||||||
|
:key="tag"
|
||||||
|
class="tag-item"
|
||||||
|
:class="{ active: formData.label === tag }"
|
||||||
|
@click="selectTag(tag)"
|
||||||
|
>{{ tag }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="form-item switch-item">
|
||||||
|
<text class="label">璁句负榛樿鏀惰揣鍦板潃</text>
|
||||||
|
<switch :checked="formData.isDefault" color="#ff5000" @change="onSwitchChange" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="footer-btn">
|
||||||
|
<button class="save-btn" @click="saveAddress">淇濆瓨</button>
|
||||||
|
<button v-if="isEdit" class="delete-btn" @click="deleteAddress">鍒犻櫎鏀惰揣鍦板潃</button>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="uts">
|
||||||
|
import { ref, reactive, computed } from 'vue'
|
||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { supabaseService, AddAddressParams, UpdateAddressParams } from '@/utils/supabaseService.uts'
|
||||||
|
|
||||||
|
type Address = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
phone: string
|
||||||
|
province: string
|
||||||
|
city: string
|
||||||
|
district: string
|
||||||
|
detail: string
|
||||||
|
isDefault: boolean
|
||||||
|
label?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const addressId = ref('')
|
||||||
|
const regionString = ref('')
|
||||||
|
const tags = ['瀹?, '鍏徃', '瀛︽牎']
|
||||||
|
const smartInput = ref('')
|
||||||
|
|
||||||
|
type AddressForm = {
|
||||||
|
name: string
|
||||||
|
phone: string
|
||||||
|
detail: string
|
||||||
|
isDefault: boolean
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
name: '',
|
||||||
|
phone: '',
|
||||||
|
detail: '',
|
||||||
|
isDefault: false,
|
||||||
|
label: ''
|
||||||
|
} as AddressForm)
|
||||||
|
|
||||||
|
const loadAddress = async (id: string) => {
|
||||||
|
try {
|
||||||
|
// 浠嶴upabase鍔犺浇鍦板潃璇︽儏
|
||||||
|
const address = await supabaseService.getAddressById(id)
|
||||||
|
if (address != null) {
|
||||||
|
formData.name = address.recipient_name
|
||||||
|
formData.phone = address.phone
|
||||||
|
formData.detail = address.detail_address
|
||||||
|
formData.isDefault = address.is_default
|
||||||
|
formData.label = address.label ?? ''
|
||||||
|
regionString.value = `${address.province} ${address.city} ${address.district}`.trim()
|
||||||
|
} else {
|
||||||
|
// 濡傛灉Supabase娌℃湁鎵惧埌锛屽皾璇曚粠鏈湴瀛樺偍鍔犺浇
|
||||||
|
const storedAddresses = uni.getStorageSync('addresses')
|
||||||
|
if (storedAddresses != null) {
|
||||||
|
const addresses = JSON.parse(storedAddresses as string) as Address[]
|
||||||
|
const localAddress = addresses.find(item => item.id === id)
|
||||||
|
if (localAddress != null) {
|
||||||
|
formData.name = localAddress.name
|
||||||
|
formData.phone = localAddress.phone
|
||||||
|
formData.detail = localAddress.detail
|
||||||
|
formData.isDefault = localAddress.isDefault
|
||||||
|
formData.label = localAddress.label ?? ''
|
||||||
|
regionString.value = `${localAddress.province} ${localAddress.city} ${localAddress.district}`.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('鍔犺浇鍦板潃璇︽儏澶辫触:', error)
|
||||||
|
// 澶辫触鏃朵粠鏈湴瀛樺偍鍔犺浇
|
||||||
|
const storedAddresses = uni.getStorageSync('addresses')
|
||||||
|
if (storedAddresses != null) {
|
||||||
|
try {
|
||||||
|
const addresses = JSON.parse(storedAddresses as string) as Address[]
|
||||||
|
const address = addresses.find(item => item.id === id)
|
||||||
|
if (address != null) {
|
||||||
|
formData.name = address.name
|
||||||
|
formData.phone = address.phone
|
||||||
|
formData.detail = address.detail
|
||||||
|
formData.isDefault = address.isDefault
|
||||||
|
formData.label = address.label ?? ''
|
||||||
|
regionString.value = `${address.province} ${address.city} ${address.district}`.trim()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('瑙f瀽鏈湴鍦板潃鏁版嵁澶辫触', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad((options) => {
|
||||||
|
if (options['id'] != null) {
|
||||||
|
isEdit.value = true
|
||||||
|
addressId.value = options['id'] as string
|
||||||
|
loadAddress(addressId.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectTag = (tag: string) => {
|
||||||
|
if (formData.label === tag) {
|
||||||
|
formData.label = ''
|
||||||
|
} else {
|
||||||
|
formData.label = tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSwitchChange = (e: UniSwitchChangeEvent) => {
|
||||||
|
formData.isDefault = e.detail.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveAddress = async () => {
|
||||||
|
if (formData.name == '') {
|
||||||
|
uni.showToast({ title: '璇峰~鍐欐敹璐т汉', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (formData.phone == '') {
|
||||||
|
uni.showToast({ title: '璇峰~鍐欐墜鏈哄彿鐮?, icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (regionString.value == '') {
|
||||||
|
uni.showToast({ title: '璇峰~鍐欐墍鍦ㄥ湴鍖?, icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (formData.detail == '') {
|
||||||
|
uni.showToast({ title: '璇峰~鍐欒缁嗗湴鍧?', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绠?鍗曡В鏋愬湴鍖猴紙杩欓噷绠?鍖栧鐞嗭紝瀹為檯搴斾娇鐢ㄩ?夋嫨鍣級
|
||||||
|
const regions = regionString.value.split(' ')
|
||||||
|
const province = regions[0] ?? ''
|
||||||
|
const city = regions[1] ?? ''
|
||||||
|
const district = regions.slice(2).join(' ')
|
||||||
|
|
||||||
|
// 鏋勫缓鍦板潃瀵硅薄
|
||||||
|
const addressData = {
|
||||||
|
recipient_name: formData.name,
|
||||||
|
phone: formData.phone,
|
||||||
|
province: province,
|
||||||
|
city: city,
|
||||||
|
district: district,
|
||||||
|
detail_address: formData.detail,
|
||||||
|
postal_code: '', // 濡傛灉闇?瑕佸彲浠ユ坊鍔犻偖鏀跨紪鐮佸瓧娈?
|
||||||
|
is_default: formData.isDefault,
|
||||||
|
label: formData.label
|
||||||
|
} as AddAddressParams
|
||||||
|
|
||||||
|
let success = false
|
||||||
|
|
||||||
|
if (isEdit.value) {
|
||||||
|
// 鏇存柊鍦板潃
|
||||||
|
const updateData = {
|
||||||
|
recipient_name: formData.name,
|
||||||
|
phone: formData.phone,
|
||||||
|
province: province,
|
||||||
|
city: city,
|
||||||
|
district: district,
|
||||||
|
detail_address: formData.detail,
|
||||||
|
postal_code: '',
|
||||||
|
is_default: formData.isDefault,
|
||||||
|
label: formData.label
|
||||||
|
} as UpdateAddressParams
|
||||||
|
success = await supabaseService.updateAddress(addressId.value, updateData)
|
||||||
|
} else {
|
||||||
|
// 娣诲姞鏂板湴鍧?
|
||||||
|
success = await supabaseService.addAddress(addressData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
// 鍚屾椂鏇存柊鏈湴瀛樺偍浣滀负缂撳瓨
|
||||||
|
const storedAddresses = uni.getStorageSync('addresses')
|
||||||
|
let addresses: Address[] = []
|
||||||
|
if (storedAddresses != null) {
|
||||||
|
try {
|
||||||
|
addresses = JSON.parse(storedAddresses as string) as Address[]
|
||||||
|
} catch (e) {
|
||||||
|
addresses = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 濡傛灉璁句负榛樿锛屽彇娑堝叾浠栭粯璁?
|
||||||
|
if (formData.isDefault) {
|
||||||
|
addresses.forEach(item => {
|
||||||
|
item.isDefault = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEdit.value) {
|
||||||
|
const index = addresses.findIndex(item => item.id === addressId.value)
|
||||||
|
if (index !== -1) {
|
||||||
|
addresses[index] = {
|
||||||
|
...addresses[index],
|
||||||
|
name: formData.name,
|
||||||
|
phone: formData.phone,
|
||||||
|
province: province,
|
||||||
|
city: city,
|
||||||
|
district: district,
|
||||||
|
detail: formData.detail,
|
||||||
|
isDefault: formData.isDefault,
|
||||||
|
label: formData.label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const newAddress: Address = {
|
||||||
|
id: `addr_${Date.now()}`, // 涓存椂ID锛屽疄闄呯敱Supabase鐢熸垚
|
||||||
|
name: formData.name,
|
||||||
|
phone: formData.phone,
|
||||||
|
province: province,
|
||||||
|
city: city,
|
||||||
|
district: district,
|
||||||
|
detail: formData.detail,
|
||||||
|
isDefault: formData.isDefault,
|
||||||
|
label: formData.label
|
||||||
|
}
|
||||||
|
addresses.push(newAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.setStorageSync('addresses', JSON.stringify(addresses))
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '淇濆瓨鎴愬姛',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
console.error('淇濆瓨鍦板潃澶辫触')
|
||||||
|
uni.showToast({
|
||||||
|
title: '淇濆瓨澶辫触',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseSmartInput = () => {
|
||||||
|
const input = smartInput.value.trim()
|
||||||
|
if (input == '') return
|
||||||
|
|
||||||
|
// 鎻愬彇鎵嬫満鍙?
|
||||||
|
const phoneRegex = /(1[3-9]\d{9})/
|
||||||
|
const phoneMatch = input.match(phoneRegex)
|
||||||
|
if (phoneMatch != null) {
|
||||||
|
formData.phone = phoneMatch[0] ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鎻愬彇濮撳悕锛堝彇绗竴涓?-4浣嶄腑鏂囷級
|
||||||
|
const nameRegex = /([\u4e00-\u9fa5]{2,4})/
|
||||||
|
const nameMatch = input.match(nameRegex)
|
||||||
|
if (nameMatch != null) {
|
||||||
|
formData.name = nameMatch[0] ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鍘绘帀濮撳悕鍜岀數璇濆悗鍓╀綑浣滀负鍦板潃
|
||||||
|
let addrText = input
|
||||||
|
if (formData.name != '') addrText = addrText.replace(formData.name, '')
|
||||||
|
if (formData.phone != '') addrText = addrText.replace(formData.phone, '')
|
||||||
|
addrText = addrText.replace(/[锛?;锛沑s]+/g, ' ').trim()
|
||||||
|
|
||||||
|
// 瑙f瀽鐪佸競鍖?
|
||||||
|
const pattern1 = /^(.*?鐪??(.*?甯??(.*?[鍖哄幙])?(.*)$/
|
||||||
|
const m = addrText.match(pattern1)
|
||||||
|
if (m != null) {
|
||||||
|
const province = m[1] ?? ''
|
||||||
|
const city = m[2] ?? ''
|
||||||
|
const district = m[3] ?? ''
|
||||||
|
const detail = m[4] ?? ''
|
||||||
|
regionString.value = `${province.trim()} ${city.trim()} ${district.trim()}`.trim()
|
||||||
|
formData.detail = detail.trim()
|
||||||
|
} else {
|
||||||
|
formData.detail = addrText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const deleteAddress = () => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '鎻愮ず',
|
||||||
|
content: '纭畾瑕佸垹闄よ鍦板潃鍚楋紵',
|
||||||
|
success: (res: UniShowModalResult) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 璋冪敤Supabase鏈嶅姟鍒犻櫎鍦板潃
|
||||||
|
supabaseService.deleteAddress(addressId.value).then((success) => {
|
||||||
|
if (success) {
|
||||||
|
// 鍚屾椂浠庢湰鍦板瓨鍌ㄤ腑绉婚櫎
|
||||||
|
const storedAddresses = uni.getStorageSync('addresses')
|
||||||
|
if (storedAddresses != null) {
|
||||||
|
try {
|
||||||
|
let addresses = JSON.parse(storedAddresses as string) as Address[]
|
||||||
|
addresses = addresses.filter(item => item.id !== addressId.value)
|
||||||
|
uni.setStorageSync('addresses', JSON.stringify(addresses))
|
||||||
|
} catch (e) {
|
||||||
|
console.error('瑙f瀽鏈湴鍦板潃鏁版嵁澶辫触', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '鍒犻櫎鎴愬姛',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
console.error('鍒犻櫎鍦板潃澶辫触')
|
||||||
|
uni.showToast({
|
||||||
|
title: '鍒犻櫎澶辫触',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-edit-page {
|
||||||
|
flex: 1; /* Replace min-height: 100vh */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
background-color: white;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 80px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-item {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-item {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-item.active {
|
||||||
|
background-color: #ff5000;
|
||||||
|
color: white;
|
||||||
|
border-color: #ff5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-btn {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
background-color: #ff5000;
|
||||||
|
color: white;
|
||||||
|
border-radius: 25px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
background-color: white;
|
||||||
|
color: #333;
|
||||||
|
border-radius: 25px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="address-list-page">
|
<view class="address-list-page">
|
||||||
<view class="address-list">
|
<view class="address-list">
|
||||||
<view v-if="addresses.length === 0" class="empty-state">
|
<view v-if="addresses.length === 0" class="empty-state">
|
||||||
<text class="empty-icon">📍</text>
|
<text class="empty-icon">馃搷</text>
|
||||||
<text class="empty-text">暂无收货地址</text>
|
<text class="empty-text">鏆傛棤鏀惰揣鍦板潃</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else v-for="(item, index) in addresses" :key="item.id" class="address-item" @click="selectAddress(item)">
|
<view v-else v-for="(item, index) in addresses" :key="item.id" class="address-item" @click="selectAddress(item)">
|
||||||
@@ -11,24 +11,24 @@
|
|||||||
<view class="item-header">
|
<view class="item-header">
|
||||||
<text class="user-name">{{ item.name }}</text>
|
<text class="user-name">{{ item.name }}</text>
|
||||||
<text class="user-phone">{{ item.phone }}</text>
|
<text class="user-phone">{{ item.phone }}</text>
|
||||||
<text v-if="item.isDefault" class="default-tag">默认</text>
|
<text v-if="item.isDefault" class="default-tag">榛樿</text>
|
||||||
<text v-if="item.label" class="label-tag">{{ item.label }}</text>
|
<text v-if="item.label" class="label-tag">{{ item.label }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="address-text">{{ getFullAddress(item) }}</text>
|
<text class="address-text">{{ getFullAddress(item) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-actions">
|
<view class="item-actions">
|
||||||
<view class="action-item" @click.stop="editAddress(item.id)">
|
<view class="action-item" @click.stop="editAddress(item.id)">
|
||||||
<text class="action-icon">📝</text>
|
<text class="action-icon">馃摑</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-item" @click.stop="deleteAddress(item.id)">
|
<view class="action-item" @click.stop="deleteAddress(item.id)">
|
||||||
<text class="action-icon"><EFBFBD>️</text>
|
<text class="action-icon">锟斤笍</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="footer-btn">
|
<view class="footer-btn">
|
||||||
<button class="add-btn" @click="addAddress">新建收货地址</button>
|
<button class="add-btn" @click="addAddress">鏂板缓鏀惰揣鍦板潃</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -81,18 +81,18 @@ const loadAddresses = () => {
|
|||||||
addresses.value = []
|
addresses.value = []
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 初始Mock数据
|
// 鍒濆Mock鏁版嵁
|
||||||
addresses.value = [
|
addresses.value = [
|
||||||
{
|
{
|
||||||
id: 'addr_001',
|
id: 'addr_001',
|
||||||
name: '张三',
|
name: '寮犱笁',
|
||||||
phone: '13800138000',
|
phone: '13800138000',
|
||||||
province: '北京市',
|
province: '鍖椾含甯?,
|
||||||
city: '北京市',
|
city: '鍖椾含甯?,
|
||||||
district: '朝阳区',
|
district: '鏈濋槼鍖?,
|
||||||
detail: '三里屯SOHO A座',
|
detail: '涓夐噷灞疭OHO A搴?,
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
label: '公司'
|
label: '鍏徃'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
uni.setStorageSync('addresses', JSON.stringify(addresses.value))
|
uni.setStorageSync('addresses', JSON.stringify(addresses.value))
|
||||||
@@ -109,11 +109,11 @@ const addAddress = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除地址
|
// 鍒犻櫎鍦板潃
|
||||||
const deleteAddress = (id: string) => {
|
const deleteAddress = (id: string) => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '鎻愮ず',
|
||||||
content: '确定要删除该地址吗?',
|
content: '纭畾瑕佸垹闄よ鍦板潃鍚楋紵',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
const index = addresses.value.findIndex(addr => addr.id === id)
|
const index = addresses.value.findIndex(addr => addr.id === id)
|
||||||
@@ -121,7 +121,7 @@ const deleteAddress = (id: string) => {
|
|||||||
addresses.value.splice(index, 1)
|
addresses.value.splice(index, 1)
|
||||||
uni.setStorageSync('addresses', JSON.stringify(addresses.value))
|
uni.setStorageSync('addresses', JSON.stringify(addresses.value))
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '删除成功',
|
title: '鍒犻櫎鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ const selectAddress = (item: Address) => {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-left: 1px solid #f0f0f0;
|
border-left: 1px solid #f0f0f0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; /* 竖向排列图标 */
|
flex-direction: column; /* 绔栧悜鎺掑垪鍥炬爣 */
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
@@ -176,7 +176,7 @@ const selectAddress = (item: Address) => {
|
|||||||
padding-bottom: calc(10px + env(safe-area-inset-bottom));
|
padding-bottom: calc(10px + env(safe-area-inset-bottom));
|
||||||
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
|
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center; /* 居中显示 */
|
justify-content: center; /* 灞呬腑鏄剧ず */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,11 +188,11 @@ const selectAddress = (item: Address) => {
|
|||||||
height: 44px;
|
height: 44px;
|
||||||
line-height: 44px;
|
line-height: 44px;
|
||||||
border: none;
|
border: none;
|
||||||
width: 100%; /* 默认占满 */
|
width: 100%; /* 榛樿鍗犳弧 */
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式布局优化 */
|
/* 鍝嶅簲寮忓竷灞€浼樺寲 */
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.address-list {
|
.address-list {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
@@ -209,11 +209,13 @@ const selectAddress = (item: Address) => {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
|
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
|
||||||
border-radius: 12px 12px 0 0; /* 桌面端加点圆角更美观 */
|
border-radius: 12px 12px 0 0; /* 妗岄潰绔姞鐐瑰渾瑙掓洿缇庤 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-btn {
|
.add-btn {
|
||||||
width: 300px; /* 桌面端限制宽度 */
|
width: 300px; /* 妗岄潰绔檺鍒跺搴?*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="address-list-page">
|
<view class="address-list-page">
|
||||||
<view class="address-list">
|
<view class="address-list">
|
||||||
<view v-if="addresses.length === 0" class="empty-state">
|
<view v-if="addresses.length === 0" class="empty-state">
|
||||||
@@ -180,21 +180,113 @@ const selectAddress = (item: Address) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.address-list-page {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 12px;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-item {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-phone {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.default-tag {
|
||||||
|
background-color: #fff1eb;
|
||||||
|
color: #ff5000;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 6px;
|
||||||
|
border: 1px solid #ff5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-tag {
|
||||||
|
background-color: #eef5ff;
|
||||||
|
color: #007aff;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.5;
|
||||||
|
lines: 2;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
.item-actions {
|
.item-actions {
|
||||||
padding: 10px;
|
padding-left: 16px;
|
||||||
border-left: 1px solid #f0f0f0;
|
border-left: 1px solid #f0f0f0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; /* 竖向排列图标 */
|
flex-direction: row; /* 改为横向排列图标更符合习惯 */
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-item {
|
.action-item {
|
||||||
margin-bottom: 15px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-item:last-child {
|
.empty-state {
|
||||||
margin-bottom: 0px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 64px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-btn {
|
.footer-btn {
|
||||||
@@ -248,3 +340,4 @@ const selectAddress = (item: Address) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 地址管理页面 -->
|
<!-- 地址管理页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="address-page">
|
<view class="address-page">
|
||||||
<!-- 顶部栏 -->
|
<!-- 顶部栏 -->
|
||||||
@@ -920,3 +920,4 @@ const cancelNewAddress = () => {
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="apply-refund-page">
|
<view class="apply-refund-page">
|
||||||
<view class="section">
|
<view class="section">
|
||||||
<view class="section-title">退款类型</view>
|
<view class="section-title">退款类型</view>
|
||||||
<radio-group @change="handleTypeChange" class="type-group">
|
<radio-group @change="handleTypeChange" class="type-group">
|
||||||
<label class="type-item">
|
<label class="type-item">
|
||||||
<radio value="1" :checked="refundType === 1" color="#ff4444" class="type-radio" />
|
<radio value="1" :checked="refundType === 1" color="#ff5000" class="type-radio" />
|
||||||
<text>仅退款</text>
|
<text>仅退款</text>
|
||||||
</label>
|
</label>
|
||||||
<label class="type-item">
|
<label class="type-item">
|
||||||
<radio value="2" :checked="refundType === 2" color="#ff4444" class="type-radio" />
|
<radio value="2" :checked="refundType === 2" color="#ff5000" class="type-radio" />
|
||||||
<text>退货退款</text>
|
<text>退货退款</text>
|
||||||
</label>
|
</label>
|
||||||
</radio-group>
|
</radio-group>
|
||||||
@@ -133,20 +133,29 @@ const handleReasonChange = (e: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const submitRefund = async () => {
|
const submitRefund = async () => {
|
||||||
|
console.log('=== 提交退款 ===')
|
||||||
|
console.log('refundReason:', refundReason.value)
|
||||||
|
console.log('refundAmount:', refundAmount.value)
|
||||||
|
console.log('maxAmount:', maxAmount.value)
|
||||||
|
|
||||||
if (refundReason.value == '') {
|
if (refundReason.value == '') {
|
||||||
uni.showToast({ title: '请选择退款原因', icon: 'none' })
|
uni.showToast({ title: '请选择退款原因', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount = parseFloat(refundAmount.value)
|
const amount = parseFloat(refundAmount.value)
|
||||||
|
console.log('解析后金额:', amount)
|
||||||
|
|
||||||
if (isNaN(amount) || amount <= 0 || amount > maxAmount.value) {
|
if (isNaN(amount) || amount <= 0 || amount > maxAmount.value) {
|
||||||
uni.showToast({ title: '请输入有效的退款金额', icon: 'none' })
|
uni.showToast({ title: '请输入有效的退款金额', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
|
uni.showLoading({ title: '提交中...' })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('调用 createRefund, orderId:', orderId.value)
|
||||||
const result = await supabaseService.createRefund({
|
const result = await supabaseService.createRefund({
|
||||||
order_id: orderId.value,
|
order_id: orderId.value,
|
||||||
refund_type: refundType.value,
|
refund_type: refundType.value,
|
||||||
@@ -155,13 +164,13 @@ const submitRefund = async () => {
|
|||||||
description: description.value
|
description: description.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('createRefund 结果:', JSON.stringify(result))
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
uni.showToast({ title: '提交成功', icon: 'success' })
|
uni.showToast({ title: '提交成功', icon: 'success' })
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Go back to orders listing focused on refund type?
|
|
||||||
// or stay here? User probably wants to see list.
|
|
||||||
// Since profile redirects "Refunds" to orders list, let's go there.
|
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
}, 1500)
|
}, 1500)
|
||||||
} else {
|
} else {
|
||||||
@@ -169,6 +178,7 @@ const submitRefund = async () => {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('提交退款失败', err)
|
console.error('提交退款失败', err)
|
||||||
|
uni.hideLoading()
|
||||||
uni.showToast({ title: '提交异常', icon: 'none' })
|
uni.showToast({ title: '提交异常', icon: 'none' })
|
||||||
} finally {
|
} finally {
|
||||||
submitting.value = false
|
submitting.value = false
|
||||||
@@ -278,10 +288,11 @@ const submitRefund = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
background-color: #ff4444;
|
background-color: #ff5000;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-radius: 22px;
|
border-radius: 22px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="add-card-page">
|
<view class="add-card-page">
|
||||||
<view class="form-container">
|
<view class="form-container">
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="bank-cards-page">
|
<view class="bank-cards-page">
|
||||||
<view class="card-list">
|
<view class="card-list">
|
||||||
<view v-for="card in cards" :key="card.id" class="card-item" :class="getCardClass(card.bank_name)">
|
<view v-for="card in cards" :key="card.id" class="card-item" :class="getCardClass(card.bank_name)">
|
||||||
|
|||||||
1561
pages/mall/consumer/cart copy.uvue
Normal file
@@ -1,4 +1,4 @@
|
|||||||
<!-- pages/mall/consumer/cart.uvue -->
|
<!-- pages/mall/consumer/cart.uvue -->
|
||||||
<template>
|
<template>
|
||||||
<view class="cart-page">
|
<view class="cart-page">
|
||||||
<!-- 智能顶部导航栏 - 与消息页保持一致 -->
|
<!-- 智能顶部导航栏 - 与消息页保持一致 -->
|
||||||
@@ -118,6 +118,10 @@
|
|||||||
<view v-if="recommendProducts.length > 0" class="recommend-section">
|
<view v-if="recommendProducts.length > 0" class="recommend-section">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<text class="section-title">猜你喜欢</text>
|
<text class="section-title">猜你喜欢</text>
|
||||||
|
<view class="refresh-btn" @click="refreshRecommend">
|
||||||
|
<text class="refresh-icon">🔄</text>
|
||||||
|
<text class="refresh-text">换一批</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="recommend-list">
|
<view class="recommend-list">
|
||||||
<view
|
<view
|
||||||
@@ -281,6 +285,35 @@ onMounted(() => {
|
|||||||
initPage()
|
initPage()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const refreshRecommend = async () => {
|
||||||
|
try {
|
||||||
|
// 使用 searchProducts 获取销售额排序的 6 个产品
|
||||||
|
const hotResp = await supabaseService.searchProducts('', 1, 6, 'sales')
|
||||||
|
const recommends = hotResp.data
|
||||||
|
|
||||||
|
if (recommends.length > 0) {
|
||||||
|
recommendProducts.value = recommends.map((p: Product): RecommendProduct => {
|
||||||
|
return {
|
||||||
|
id: p.id,
|
||||||
|
shopId: p.merchant_id ?? 'unknown',
|
||||||
|
shopName: p.shop_name ?? '商城推荐',
|
||||||
|
name: p.name,
|
||||||
|
price: p.base_price ?? p.market_price ?? 0,
|
||||||
|
image: p.main_image_url ?? p.image_url ?? '/static/images/default-product.png',
|
||||||
|
skuId: '',
|
||||||
|
merchant_id: p.merchant_id ?? ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
uni.showToast({
|
||||||
|
title: '已更新推荐',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('刷新推荐失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
const loadCartData = async () => {
|
const loadCartData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -746,9 +779,9 @@ const goToCheckout = () => {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: #4CAF50;
|
background-color: #ff5000;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(255, 80, 0, 0.15);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@@ -943,10 +976,9 @@ const goToCheckout = () => {
|
|||||||
background-color: #ff5000;
|
background-color: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
display: flex;
|
text-align: center;
|
||||||
align-items: center;
|
line-height: 18px;
|
||||||
justify-content: center;
|
font-size: 12px;
|
||||||
font-size: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.unselected-icon {
|
.unselected-icon {
|
||||||
@@ -1012,29 +1044,30 @@ const goToCheckout = () => {
|
|||||||
|
|
||||||
.quantity-control {
|
.quantity-control {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 显式设置横向排列 */
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 24px;
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quantity-btn {
|
.quantity-btn {
|
||||||
width: 24px;
|
width: 28px;
|
||||||
height: 24px;
|
height: 28px;
|
||||||
display: flex;
|
text-align: center;
|
||||||
align-items: center;
|
line-height: 28px;
|
||||||
justify-content: center;
|
font-size: 16px;
|
||||||
font-size: 14px;
|
color: #333;
|
||||||
color: #666;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quantity-value {
|
.quantity-value {
|
||||||
width: 30px;
|
min-width: 36px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
line-height: 24px;
|
line-height: 28px;
|
||||||
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 推荐商品 */
|
/* 推荐商品 */
|
||||||
@@ -1047,6 +1080,27 @@ const goToCheckout = () => {
|
|||||||
|
|
||||||
.section-header {
|
.section-header {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
@@ -1504,3 +1558,4 @@ const goToCheckout = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
25
pages/mall/consumer/cart_debug.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<!-- pages/mall/consumer/cart.uvue -->
|
||||||
|
<template>
|
||||||
|
<view class="cart-page">
|
||||||
|
<!-- 鏅鸿兘椤堕儴瀵艰埅鏍?- 涓庢秷鎭〉淇濇寔涓€鑷?-->
|
||||||
|
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
|
<view class="nav-container">
|
||||||
|
<text class="nav-title">璐墿杞?/text>
|
||||||
|
<view class="nav-actions">
|
||||||
|
<view class="action-btn" @click="toggleManageMode">
|
||||||
|
<text class="action-icon">{{ isManageMode ? '鉁? : '鈿欙笍' }}</text>
|
||||||
|
<text class="action-text">{{ isManageMode ? '瀹屾垚' : '绠$悊' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 瀵艰埅鏍忓崰浣嶇 - 闇€瑕佸寘鍚玸tatusBarHeight + 瀵艰埅鏍忛珮搴?4px -->
|
||||||
|
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||||
|
|
||||||
|
<!-- 璐墿杞﹀唴瀹?-->
|
||||||
|
<scroll-view
|
||||||
|
:scroll-y="true"
|
||||||
|
class="cart-content"
|
||||||
|
:show-scrollbar="false"
|
||||||
|
:enhanced="true"
|
||||||
@@ -1,36 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="category-page">
|
<view class="category-page">
|
||||||
<!-- 顶部搜索栏 -->
|
<!-- 椤堕儴鎼滅储鏍?-->
|
||||||
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view class="search-container">
|
<view class="search-container">
|
||||||
<view class="search-box" @click="navigateToSearch" :style="{ height: '30px' }">
|
<view class="search-box" @click="navigateToSearch" :style="{ height: '30px' }">
|
||||||
<!-- 模拟输入框 -->
|
<!-- 妯℃嫙杈撳叆妗?-->
|
||||||
<text class="search-placeholder">请输入药品名称、症状或品牌</text>
|
<text class="search-placeholder">璇疯緭鍏ヨ嵂鍝佸悕绉般€佺棁鐘舵垨鍝佺墝</text>
|
||||||
|
|
||||||
<!-- 扫码图标 -->
|
<!-- 鎵爜鍥炬爣 -->
|
||||||
<view class="nav-icon-btn" @click.stop="onScan">
|
<view class="nav-icon-btn" @click.stop="onScan">
|
||||||
<text class="nav-icon">🔳</text>
|
<text class="nav-icon">馃敵</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 相机图标 -->
|
<!-- 鐩告満鍥炬爣 -->
|
||||||
<view class="nav-camera-btn" @click.stop="onCamera">
|
<view class="nav-camera-btn" @click.stop="onCamera">
|
||||||
<text class="nav-camera-icon">📷</text>
|
<text class="nav-camera-icon">馃摲</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 搜索按钮 -->
|
<!-- 鎼滅储鎸夐挳 -->
|
||||||
<view class="nav-inner-search-btn" :style="{ height: '22px' }">
|
<view class="nav-inner-search-btn" :style="{ height: '22px' }">
|
||||||
<text class="nav-inner-search-text">搜索</text>
|
<text class="nav-inner-search-text">鎼滅储</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 导航栏占位 - 需要包含statusBarHeight + 搜索框高度44px -->
|
<!-- 瀵艰埅鏍忓崰浣?- 闇€瑕佸寘鍚玸tatusBarHeight + 鎼滅储妗嗛珮搴?4px -->
|
||||||
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||||
|
|
||||||
<!-- 分类内容区 -->
|
<!-- 鍒嗙被鍐呭鍖?-->
|
||||||
<view class="category-content">
|
<view class="category-content">
|
||||||
<!-- 左侧一级分类 -->
|
<!-- 宸︿晶涓€绾у垎绫?-->
|
||||||
<scroll-view :scroll-y="true" class="primary-category">
|
<scroll-view :scroll-y="true" class="primary-category">
|
||||||
<view
|
<view
|
||||||
v-for="item in primaryCategories"
|
v-for="item in primaryCategories"
|
||||||
@@ -44,20 +44,20 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 右侧商品列表 -->
|
<!-- 鍙充晶鍟嗗搧鍒楄〃 -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
:scroll-y="true"
|
:scroll-y="true"
|
||||||
class="product-content"
|
class="product-content"
|
||||||
@scrolltolower="loadMore"
|
@scrolltolower="loadMore"
|
||||||
:lower-threshold="50"
|
:lower-threshold="50"
|
||||||
>
|
>
|
||||||
<!-- 分类标题 -->
|
<!-- 鍒嗙被鏍囬 -->
|
||||||
<view class="category-header">
|
<view class="category-header">
|
||||||
<text class="category-title">{{ currentCategoryName }}</text>
|
<text class="category-title">{{ currentCategoryName }}</text>
|
||||||
<text class="category-desc">{{ currentCategoryDesc }}</text>
|
<text class="category-desc">{{ currentCategoryDesc }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品网格 -->
|
<!-- 鍟嗗搧缃戞牸 -->
|
||||||
<view v-if="productList.length > 0" class="product-grid">
|
<view v-if="productList.length > 0" class="product-grid">
|
||||||
<view
|
<view
|
||||||
v-for="product in productList"
|
v-for="product in productList"
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
/>
|
/>
|
||||||
<text class="product-name" :lines="2">{{ product.name }}</text>
|
<text class="product-name" :lines="2">{{ product.name }}</text>
|
||||||
<view class="product-bottom">
|
<view class="product-bottom">
|
||||||
<text class="product-price">¥{{ product.base_price ?? product.price ?? 0 }}</text>
|
<text class="product-price">楼{{ product.base_price ?? product.price ?? 0 }}</text>
|
||||||
<view class="product-add-btn" @click.stop="addToCart(product)">
|
<view class="product-add-btn" @click.stop="addToCart(product)">
|
||||||
<text class="add-icon">+</text>
|
<text class="add-icon">+</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -80,16 +80,16 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 绌虹姸鎬?-->
|
||||||
<view v-else class="empty-state">
|
<view v-else class="empty-state">
|
||||||
<text class="empty-icon">💊</text>
|
<text class="empty-icon">馃拪</text>
|
||||||
<text class="empty-text">暂无相关药品</text>
|
<text class="empty-text">鏆傛棤鐩稿叧鑽搧</text>
|
||||||
<text class="empty-desc">该分类下暂无商品,敬请期待</text>
|
<text class="empty-desc">璇ュ垎绫讳笅鏆傛棤鍟嗗搧锛屾暚璇锋湡寰?/text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载更多提示 -->
|
<!-- 鍔犺浇鏇村鎻愮ず -->
|
||||||
<view v-if="hasMore" class="load-more">
|
<view v-if="hasMore" class="load-more">
|
||||||
<text class="load-text">上拉加载更多</text>
|
<text class="load-text">涓婃媺鍔犺浇鏇村</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
@@ -109,38 +109,38 @@ type LocalCategory = {
|
|||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式数据
|
// 鍝嶅簲寮忔暟鎹?
|
||||||
const statusBarHeight = ref(0)
|
const statusBarHeight = ref(0)
|
||||||
const headerHeight = ref(44) // 默认头部高度
|
const headerHeight = ref(44) // 榛樿澶撮儴楂樺害
|
||||||
const primaryCategories = ref<LocalCategory[]>([])
|
const primaryCategories = ref<LocalCategory[]>([])
|
||||||
const productList = ref<Product[]>([])
|
const productList = ref<Product[]>([])
|
||||||
const activePrimary = ref<string>('')
|
const activePrimary = ref<string>('')
|
||||||
const cartCount = ref(3)
|
const cartCount = ref(3)
|
||||||
const hasMore = ref(true)
|
const hasMore = ref(true)
|
||||||
const hasLoadedFromParams = ref(false) // 标记是否已通过参数加载
|
const hasLoadedFromParams = ref(false) // 鏍囪鏄惁宸查€氳繃鍙傛暟鍔犺浇
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
// 获取当前分类信息
|
// 鑾峰彇褰撳墠鍒嗙被淇℃伅
|
||||||
const currentCategoryName = ref('')
|
const currentCategoryName = ref('')
|
||||||
const currentCategoryDesc = ref('')
|
const currentCategoryDesc = ref('')
|
||||||
|
|
||||||
// 页面参数
|
// 椤甸潰鍙傛暟
|
||||||
const pageParams = ref<any>({})
|
const pageParams = ref<any>({})
|
||||||
|
|
||||||
// 加载商品数据
|
// 鍔犺浇鍟嗗搧鏁版嵁
|
||||||
async function loadProducts(): Promise<void> {
|
async function loadProducts(): Promise<void> {
|
||||||
if (loading.value) return
|
if (loading.value) return
|
||||||
if (activePrimary.value == '') {
|
if (activePrimary.value == '') {
|
||||||
console.warn('activePrimary为空,无法加载商品')
|
console.warn('activePrimary涓虹┖锛屾棤娉曞姞杞藉晢鍝?)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
console.log('开始加载商品,分类ID:', activePrimary.value, '页码:', currentPage.value)
|
console.log('寮€濮嬪姞杞藉晢鍝侊紝鍒嗙被ID:', activePrimary.value, '椤电爜:', currentPage.value)
|
||||||
const response = await supabaseService.getProductsByCategory(activePrimary.value, currentPage.value)
|
const response = await supabaseService.getProductsByCategory(activePrimary.value, currentPage.value)
|
||||||
console.log('商品加载结果:', {
|
console.log('鍟嗗搧鍔犺浇缁撴灉:', {
|
||||||
dataCount: response.data.length,
|
dataCount: response.data.length,
|
||||||
total: response.total,
|
total: response.total,
|
||||||
hasmore: response.hasmore,
|
hasmore: response.hasmore,
|
||||||
@@ -155,16 +155,16 @@ async function loadProducts(): Promise<void> {
|
|||||||
|
|
||||||
hasMore.value = response.hasmore
|
hasMore.value = response.hasmore
|
||||||
|
|
||||||
// 更新当前分类信息
|
// 鏇存柊褰撳墠鍒嗙被淇℃伅
|
||||||
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === activePrimary.value)
|
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === activePrimary.value)
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
currentCategoryName.value = category.name
|
currentCategoryName.value = category.name
|
||||||
currentCategoryDesc.value = category.description
|
currentCategoryDesc.value = category.description
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('商品列表加载完成,当前总数量:', productList.value.length)
|
console.log('鍟嗗搧鍒楄〃鍔犺浇瀹屾垚锛屽綋鍓嶆€绘暟閲?', productList.value.length)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载商品数据失败:', error)
|
console.error('鍔犺浇鍟嗗搧鏁版嵁澶辫触:', error)
|
||||||
if (currentPage.value === 1) {
|
if (currentPage.value === 1) {
|
||||||
productList.value = []
|
productList.value = []
|
||||||
}
|
}
|
||||||
@@ -176,22 +176,22 @@ async function loadProducts(): Promise<void> {
|
|||||||
async function loadCategories(): Promise<void> {
|
async function loadCategories(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const categoriesData = await supabaseService.getCategories()
|
const categoriesData = await supabaseService.getCategories()
|
||||||
console.log('加载分类数据成功,数量:', categoriesData.length)
|
console.log('鍔犺浇鍒嗙被鏁版嵁鎴愬姛锛屾暟閲?', categoriesData.length)
|
||||||
|
|
||||||
// 映射数据并添加默认颜色,防止选中时背景透明导致文字看不清
|
// 鏄犲皠鏁版嵁骞舵坊鍔犻粯璁ら鑹诧紝闃叉閫変腑鏃惰儗鏅€忔槑瀵艰嚧鏂囧瓧鐪嬩笉娓?
|
||||||
// 过滤掉医药健康相关分类
|
// 杩囨护鎺夊尰鑽仴搴风浉鍏冲垎绫?
|
||||||
const categories: LocalCategory[] = []
|
const categories: LocalCategory[] = []
|
||||||
const rawList = categoriesData as any[]
|
const rawList = categoriesData as any[]
|
||||||
for (let i = 0; i < rawList.length; i++) {
|
for (let i = 0; i < rawList.length; i++) {
|
||||||
const raw = rawList[i]
|
const raw = rawList[i]
|
||||||
const catObj = (raw instanceof UTSJSONObject) ? (raw as UTSJSONObject) : (JSON.parse(JSON.stringify(raw)) as UTSJSONObject)
|
const catObj = (raw instanceof UTSJSONObject) ? (raw as UTSJSONObject) : (JSON.parse(JSON.stringify(raw)) as UTSJSONObject)
|
||||||
const name = catObj.getString('name') ?? ''
|
const name = catObj.getString('name') ?? ''
|
||||||
if (name.includes('医药') || name.includes('健康')) {
|
if (name.includes('鍖昏嵂') || name.includes('鍋ュ悍')) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const id = catObj.getString('id') ?? ''
|
const id = catObj.getString('id') ?? ''
|
||||||
const description = catObj.getString('description') ?? ''
|
const description = catObj.getString('description') ?? ''
|
||||||
const icon = catObj.getString('icon') ?? catObj.getString('icon_url') ?? '📦'
|
const icon = catObj.getString('icon') ?? catObj.getString('icon_url') ?? '馃摝'
|
||||||
const color = catObj.getString('color') ?? '#4CAF50'
|
const color = catObj.getString('color') ?? '#4CAF50'
|
||||||
categories.push({
|
categories.push({
|
||||||
id,
|
id,
|
||||||
@@ -204,36 +204,36 @@ async function loadCategories(): Promise<void> {
|
|||||||
|
|
||||||
if (categories.length > 0) {
|
if (categories.length > 0) {
|
||||||
primaryCategories.value = categories
|
primaryCategories.value = categories
|
||||||
// 如果没有通过参数设置分类,则设置默认选中一个分类
|
// 濡傛灉娌℃湁閫氳繃鍙傛暟璁剧疆鍒嗙被锛屽垯璁剧疆榛樿閫変腑涓€涓垎绫?
|
||||||
if (activePrimary.value == '') {
|
if (activePrimary.value == '') {
|
||||||
// 优先查找"厨具"相关的分类作为默认
|
// 浼樺厛鏌ユ壘"鍘ㄥ叿"鐩稿叧鐨勫垎绫讳綔涓洪粯璁?
|
||||||
const defaultCategory = categories.find((c: LocalCategory): boolean => c.name.includes('厨具')) ?? categories[0]
|
const defaultCategory = categories.find((c: LocalCategory): boolean => c.name.includes('鍘ㄥ叿')) ?? categories[0]
|
||||||
|
|
||||||
activePrimary.value = defaultCategory.id
|
activePrimary.value = defaultCategory.id
|
||||||
console.log('设置默认分类为:', defaultCategory.name, 'ID:', defaultCategory.id)
|
console.log('璁剧疆榛樿鍒嗙被涓?', defaultCategory.name, 'ID:', defaultCategory.id)
|
||||||
currentCategoryName.value = defaultCategory.name
|
currentCategoryName.value = defaultCategory.name
|
||||||
currentCategoryDesc.value = defaultCategory.description
|
currentCategoryDesc.value = defaultCategory.description
|
||||||
} else {
|
} else {
|
||||||
// 如果已经选中了分类(可能来自Storage),更新显示信息
|
// 濡傛灉宸茬粡閫変腑浜嗗垎绫伙紙鍙兘鏉ヨ嚜Storage锛夛紝鏇存柊鏄剧ず淇℃伅
|
||||||
const current = categories.find((c: LocalCategory): boolean => c.id == activePrimary.value)
|
const current = categories.find((c: LocalCategory): boolean => c.id == activePrimary.value)
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
currentCategoryName.value = current.name
|
currentCategoryName.value = current.name
|
||||||
currentCategoryDesc.value = current.description
|
currentCategoryDesc.value = current.description
|
||||||
// 如果此时没有商品列表(且没有正在加载),可能需要加载
|
// 濡傛灉姝ゆ椂娌℃湁鍟嗗搧鍒楄〃锛堜笖娌℃湁姝e湪鍔犺浇锛夛紝鍙兘闇€瑕佸姞杞?
|
||||||
if (productList.value.length === 0 && !loading.value) {
|
if (productList.value.length === 0 && !loading.value) {
|
||||||
loadProducts()
|
loadProducts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('从Supabase获取的分类数据为空')
|
console.warn('浠嶴upabase鑾峰彇鐨勫垎绫绘暟鎹负绌?)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载分类数据失败:', error)
|
console.error('鍔犺浇鍒嗙被鏁版嵁澶辫触:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载更多
|
// 鍔犺浇鏇村
|
||||||
function loadMore(): void {
|
function loadMore(): void {
|
||||||
if (hasMore.value && !loading.value) {
|
if (hasMore.value && !loading.value) {
|
||||||
currentPage.value++
|
currentPage.value++
|
||||||
@@ -241,72 +241,72 @@ function loadMore(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择一级分类
|
// 閫夋嫨涓€绾у垎绫?
|
||||||
async function selectPrimaryCategory(categoryId: string): Promise<void> {
|
async function selectPrimaryCategory(categoryId: string): Promise<void> {
|
||||||
console.log('=== selectPrimaryCategory函数开始执行 ===')
|
console.log('=== selectPrimaryCategory鍑芥暟寮€濮嬫墽琛?===')
|
||||||
console.log('传入的categoryId:', categoryId)
|
console.log('浼犲叆鐨刢ategoryId:', categoryId)
|
||||||
console.log('当前时间:', Date.now())
|
console.log('褰撳墠鏃堕棿:', Date.now())
|
||||||
|
|
||||||
// 验证categoryId是否有效
|
// 楠岃瘉categoryId鏄惁鏈夋晥
|
||||||
if (categoryId == '') {
|
if (categoryId == '') {
|
||||||
console.error('categoryId为空,尝试使用第一个分类')
|
console.error('categoryId涓虹┖锛屽皾璇曚娇鐢ㄧ涓€涓垎绫?)
|
||||||
if (primaryCategories.value.length > 0) {
|
if (primaryCategories.value.length > 0) {
|
||||||
categoryId = primaryCategories.value[0].id
|
categoryId = primaryCategories.value[0].id
|
||||||
} else {
|
} else {
|
||||||
console.error('没有可用的分类')
|
console.error('娌℃湁鍙敤鐨勫垎绫?)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('验证后的categoryId:', categoryId)
|
console.log('楠岃瘉鍚庣殑categoryId:', categoryId)
|
||||||
console.log('当前activePrimary的值:', activePrimary.value)
|
console.log('褰撳墠activePrimary鐨勫€?', activePrimary.value)
|
||||||
|
|
||||||
// 更新活动分类
|
// 鏇存柊娲诲姩鍒嗙被
|
||||||
activePrimary.value = categoryId
|
activePrimary.value = categoryId
|
||||||
console.log('更新后的activePrimary:', activePrimary.value)
|
console.log('鏇存柊鍚庣殑activePrimary:', activePrimary.value)
|
||||||
|
|
||||||
// 更新当前分类信息
|
// 鏇存柊褰撳墠鍒嗙被淇℃伅
|
||||||
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === categoryId)
|
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === categoryId)
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
currentCategoryName.value = category.name
|
currentCategoryName.value = category.name
|
||||||
currentCategoryDesc.value = category.description
|
currentCategoryDesc.value = category.description
|
||||||
console.log('✅ 找到分类:', category.name, '描述:', category.description)
|
console.log('鉁?鎵惧埌鍒嗙被:', category.name, '鎻忚堪:', category.description)
|
||||||
} else {
|
} else {
|
||||||
console.error('❌ 未找到分类ID:', categoryId, ',使用第一个分类')
|
console.error('鉂?鏈壘鍒板垎绫籌D:', categoryId, '锛屼娇鐢ㄧ涓€涓垎绫?)
|
||||||
// 如果找不到对应的分类,使用第一个分类
|
// 濡傛灉鎵句笉鍒板搴旂殑鍒嗙被锛屼娇鐢ㄧ涓€涓垎绫?
|
||||||
if (primaryCategories.value.length > 0) {
|
if (primaryCategories.value.length > 0) {
|
||||||
const firstCategory = primaryCategories.value[0]
|
const firstCategory = primaryCategories.value[0]
|
||||||
currentCategoryName.value = firstCategory.name
|
currentCategoryName.value = firstCategory.name
|
||||||
currentCategoryDesc.value = firstCategory.description
|
currentCategoryDesc.value = firstCategory.description
|
||||||
activePrimary.value = firstCategory.id
|
activePrimary.value = firstCategory.id
|
||||||
categoryId = firstCategory.id
|
categoryId = firstCategory.id
|
||||||
console.log('使用默认分类:', firstCategory.name)
|
console.log('浣跨敤榛樿鍒嗙被:', firstCategory.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('准备加载商品数据...')
|
console.log('鍑嗗鍔犺浇鍟嗗搧鏁版嵁...')
|
||||||
|
|
||||||
// 重置分页并加载
|
// 閲嶇疆鍒嗛〉骞跺姞杞?
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
hasMore.value = true
|
hasMore.value = true
|
||||||
await loadProducts()
|
await loadProducts()
|
||||||
|
|
||||||
console.log('✅ 加载商品数据成功')
|
console.log('鉁?鍔犺浇鍟嗗搧鏁版嵁鎴愬姛')
|
||||||
console.log('分类:', categoryId)
|
console.log('鍒嗙被:', categoryId)
|
||||||
console.log('商品数量:', productList.value.length)
|
console.log('鍟嗗搧鏁伴噺:', productList.value.length)
|
||||||
console.log('商品列表:', productList.value)
|
console.log('鍟嗗搧鍒楄〃:', productList.value)
|
||||||
|
|
||||||
// 验证数据是否已正确更新
|
// 楠岃瘉鏁版嵁鏄惁宸叉纭洿鏂?
|
||||||
console.log('数据更新验证:')
|
console.log('鏁版嵁鏇存柊楠岃瘉:')
|
||||||
console.log('activePrimary:', activePrimary.value)
|
console.log('activePrimary:', activePrimary.value)
|
||||||
console.log('currentCategoryName:', currentCategoryName.value)
|
console.log('currentCategoryName:', currentCategoryName.value)
|
||||||
console.log('currentCategoryDesc:', currentCategoryDesc.value)
|
console.log('currentCategoryDesc:', currentCategoryDesc.value)
|
||||||
console.log('productList长度:', productList.value.length)
|
console.log('productList闀垮害:', productList.value.length)
|
||||||
|
|
||||||
console.log('=== selectPrimaryCategory函数执行完成 ===')
|
console.log('=== selectPrimaryCategory鍑芥暟鎵ц瀹屾垚 ===')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生命周期
|
// 鐢熷懡鍛ㄦ湡
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadCategories().then(() => {
|
loadCategories().then(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -317,160 +317,160 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页面加载时处理参数 - 这是处理分类切换的主要入口
|
// 椤甸潰鍔犺浇鏃跺鐞嗗弬鏁?- 杩欐槸澶勭悊鍒嗙被鍒囨崲鐨勪富瑕佸叆鍙?
|
||||||
onLoad((options: any) => {
|
onLoad((options: any) => {
|
||||||
// 获取系统状态栏高度
|
// 鑾峰彇绯荤粺鐘舵€佹爮楂樺害
|
||||||
const systemInfo = uni.getSystemInfoSync()
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
statusBarHeight.value = systemInfo.statusBarHeight
|
statusBarHeight.value = systemInfo.statusBarHeight
|
||||||
console.log('=== category页面onLoad被调用 ===')
|
console.log('=== category椤甸潰onLoad琚皟鐢?===')
|
||||||
console.log('页面加载时间:', Date.now())
|
console.log('椤甸潰鍔犺浇鏃堕棿:', Date.now())
|
||||||
console.log('传入的options参数:', options)
|
console.log('浼犲叆鐨刼ptions鍙傛暟:', options)
|
||||||
console.log('当前活动分类:', activePrimary.value)
|
console.log('褰撳墠娲诲姩鍒嗙被:', activePrimary.value)
|
||||||
|
|
||||||
let categoryId = ''
|
let categoryId = ''
|
||||||
let categoryName = ''
|
let categoryName = ''
|
||||||
|
|
||||||
// 首先检查传入的options参数
|
// 棣栧厛妫€鏌ヤ紶鍏ョ殑options鍙傛暟
|
||||||
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
||||||
const optCategoryId = optObj.getString('categoryId') ?? ''
|
const optCategoryId = optObj.getString('categoryId') ?? ''
|
||||||
if (optCategoryId !== '') {
|
if (optCategoryId !== '') {
|
||||||
categoryId = optCategoryId
|
categoryId = optCategoryId
|
||||||
categoryName = optObj.getString('name') ?? ''
|
categoryName = optObj.getString('name') ?? ''
|
||||||
console.log('✅ onLoad中找到分类参数:', categoryId, categoryName)
|
console.log('鉁?onLoad涓壘鍒板垎绫诲弬鏁?', categoryId, categoryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果options中没有,尝试从getCurrentPages()获取
|
// 濡傛灉options涓病鏈夛紝灏濊瘯浠巊etCurrentPages()鑾峰彇
|
||||||
if (categoryId == '') {
|
if (categoryId == '') {
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
if (pages.length > 0) {
|
if (pages.length > 0) {
|
||||||
const currentPage = pages[pages.length - 1]
|
const currentPage = pages[pages.length - 1]
|
||||||
const rawPageOptions = currentPage.options ?? {}
|
const rawPageOptions = currentPage.options ?? {}
|
||||||
console.log('从getCurrentPages()获取参数:', rawPageOptions)
|
console.log('浠巊etCurrentPages()鑾峰彇鍙傛暟:', rawPageOptions)
|
||||||
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
||||||
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
||||||
if (pageCategoryId !== '') {
|
if (pageCategoryId !== '') {
|
||||||
categoryId = pageCategoryId
|
categoryId = pageCategoryId
|
||||||
categoryName = pageOptObj.getString('name') ?? ''
|
categoryName = pageOptObj.getString('name') ?? ''
|
||||||
console.log('✅ 从getCurrentPages()找到分类参数:', categoryId, categoryName)
|
console.log('鉁?浠巊etCurrentPages()鎵惧埌鍒嗙被鍙傛暟:', categoryId, categoryName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有找到分类ID,则选中对应的分类
|
// 濡傛灉鏈夋壘鍒板垎绫籌D锛屽垯閫変腑瀵瑰簲鐨勫垎绫?
|
||||||
if (categoryId != '') {
|
if (categoryId != '') {
|
||||||
hasLoadedFromParams.value = true
|
hasLoadedFromParams.value = true
|
||||||
console.log('✅ 准备选中分类:', categoryId)
|
console.log('鉁?鍑嗗閫変腑鍒嗙被:', categoryId)
|
||||||
console.log('分类名称:', categoryName ?? '未指定')
|
console.log('鍒嗙被鍚嶇О:', categoryName ?? '鏈寚瀹?)
|
||||||
|
|
||||||
// 检查是否需要更新分类
|
// 妫€鏌ユ槸鍚﹂渶瑕佹洿鏂板垎绫?
|
||||||
if (activePrimary.value !== categoryId) {
|
if (activePrimary.value !== categoryId) {
|
||||||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '涓庣洰鏍囧垎绫?', categoryId, '涓嶅悓锛岄渶瑕佹洿鏂?)
|
||||||
console.log('准备调用selectPrimaryCategory函数...')
|
console.log('鍑嗗璋冪敤selectPrimaryCategory鍑芥暟...')
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
} else {
|
} else {
|
||||||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
console.log('褰撳墠鍒嗙被宸茬粡鏄洰鏍囧垎绫伙紝浣嗗彲鑳界敤鎴锋兂瑕佸埛鏂伴〉闈?)
|
||||||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '鐩爣鍒嗙被:', categoryId)
|
||||||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
// 鍗充娇鍒嗙被鐩稿悓锛屼篃閲嶆柊鍔犺浇鏁版嵁锛岀‘淇濇暟鎹槸鏈€鏂扮殑
|
||||||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
// 娣诲姞涓€涓皬鐨勫欢杩燂紝纭繚椤甸潰瀹屽叏鏄剧ず鍚庡啀鏇存柊鏁版嵁
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ onLoad中未找到分类参数,将使用从数据库加载的第一个分类')
|
console.log('鈿狅笍 onLoad涓湭鎵惧埌鍒嗙被鍙傛暟锛屽皢浣跨敤浠庢暟鎹簱鍔犺浇鐨勭涓€涓垎绫?)
|
||||||
// 不再使用硬编码的默认分类,loadCategories 会设置第一个分类
|
// 涓嶅啀浣跨敤纭紪鐮佺殑榛樿鍒嗙被锛宭oadCategories 浼氳缃涓€涓垎绫?
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== category页面onLoad执行完成 ===')
|
console.log('=== category椤甸潰onLoad鎵ц瀹屾垚 ===')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页面显示时也检查参数,确保从其他页面返回时能正确显示
|
// 椤甸潰鏄剧ず鏃朵篃妫€鏌ュ弬鏁帮紝纭繚浠庡叾浠栭〉闈㈣繑鍥炴椂鑳芥纭樉绀?
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
console.log('=== category页面onShow被调用 ===')
|
console.log('=== category椤甸潰onShow琚皟鐢?===')
|
||||||
console.log('页面显示时间:', Date.now())
|
console.log('椤甸潰鏄剧ず鏃堕棿:', Date.now())
|
||||||
console.log('当前活动分类:', activePrimary.value)
|
console.log('褰撳墠娲诲姩鍒嗙被:', activePrimary.value)
|
||||||
|
|
||||||
// 1. 优先检查 Storage 中的参数 (由首页传入)
|
// 1. 浼樺厛妫€鏌?Storage 涓殑鍙傛暟 (鐢遍椤典紶鍏?
|
||||||
const storageCategoryId = (uni.getStorageSync('selectedCategory') as string) ?? ''
|
const storageCategoryId = (uni.getStorageSync('selectedCategory') as string) ?? ''
|
||||||
if (storageCategoryId !== '') {
|
if (storageCategoryId !== '') {
|
||||||
console.log('✅ onShow中找到Storage分类参数:', storageCategoryId)
|
console.log('鉁?onShow涓壘鍒癝torage鍒嗙被鍙傛暟:', storageCategoryId)
|
||||||
hasLoadedFromParams.value = true
|
hasLoadedFromParams.value = true
|
||||||
// 清除Storage,防止下次误读
|
// 娓呴櫎Storage锛岄槻姝笅娆¤璇?
|
||||||
uni.removeStorageSync('selectedCategory')
|
uni.removeStorageSync('selectedCategory')
|
||||||
|
|
||||||
if (activePrimary.value !== storageCategoryId) {
|
if (activePrimary.value !== storageCategoryId) {
|
||||||
selectPrimaryCategory(storageCategoryId)
|
selectPrimaryCategory(storageCategoryId)
|
||||||
}
|
}
|
||||||
// 如果分类还没加载完,这里设置了ID,等loadCategories完成后会自动匹配信息
|
// 濡傛灉鍒嗙被杩樻病鍔犺浇瀹岋紝杩欓噷璁剧疆浜咺D锛岀瓑loadCategories瀹屾垚鍚庝細鑷姩鍖归厤淇℃伅
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在onShow中,我们也需要检查是否有新的参数
|
// 鍦╫nShow涓紝鎴戜滑涔熼渶瑕佹鏌ユ槸鍚︽湁鏂扮殑鍙傛暟
|
||||||
// 因为当从主页再次点击分类跳转过来时,可能不会触发onLoad
|
// 鍥犱负褰撲粠涓婚〉鍐嶆鐐瑰嚮鍒嗙被璺宠浆杩囨潵鏃讹紝鍙兘涓嶄細瑙﹀彂onLoad
|
||||||
// 而是触发onShow
|
// 鑰屾槸瑙﹀彂onShow
|
||||||
|
|
||||||
// 获取当前页面实例和参数
|
// 鑾峰彇褰撳墠椤甸潰瀹炰緥鍜屽弬鏁?
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
if (pages.length > 0) {
|
if (pages.length > 0) {
|
||||||
const currentPage = pages[pages.length - 1]
|
const currentPage = pages[pages.length - 1]
|
||||||
const rawPageOptions = currentPage.options ?? {}
|
const rawPageOptions = currentPage.options ?? {}
|
||||||
console.log('onShow中获取参数:', rawPageOptions)
|
console.log('onShow涓幏鍙栧弬鏁?', rawPageOptions)
|
||||||
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
||||||
|
|
||||||
// 检查是否有分类参数
|
// 妫€鏌ユ槸鍚︽湁鍒嗙被鍙傛暟
|
||||||
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
||||||
if (pageCategoryId !== '') {
|
if (pageCategoryId !== '') {
|
||||||
hasLoadedFromParams.value = true
|
hasLoadedFromParams.value = true
|
||||||
const categoryId = pageCategoryId
|
const categoryId = pageCategoryId
|
||||||
const categoryName = pageOptObj.getString('name') ?? ''
|
const categoryName = pageOptObj.getString('name') ?? ''
|
||||||
|
|
||||||
console.log('✅ onShow中找到分类参数:', categoryId, categoryName)
|
console.log('鉁?onShow涓壘鍒板垎绫诲弬鏁?', categoryId, categoryName)
|
||||||
console.log('URL中的时间戳参数:', pageOptObj.getString('timestamp') ?? '')
|
console.log('URL涓殑鏃堕棿鎴冲弬鏁?', pageOptObj.getString('timestamp') ?? '')
|
||||||
console.log('URL中的随机参数:', pageOptObj.getString('random') ?? '')
|
console.log('URL涓殑闅忔満鍙傛暟:', pageOptObj.getString('random') ?? '')
|
||||||
|
|
||||||
// 检查是否需要更新分类
|
// 妫€鏌ユ槸鍚﹂渶瑕佹洿鏂板垎绫?
|
||||||
if (activePrimary.value !== categoryId) {
|
if (activePrimary.value !== categoryId) {
|
||||||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '涓庣洰鏍囧垎绫?', categoryId, '涓嶅悓锛岄渶瑕佹洿鏂?)
|
||||||
console.log('准备调用selectPrimaryCategory函数...')
|
console.log('鍑嗗璋冪敤selectPrimaryCategory鍑芥暟...')
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
} else {
|
} else {
|
||||||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
console.log('褰撳墠鍒嗙被宸茬粡鏄洰鏍囧垎绫伙紝浣嗗彲鑳界敤鎴锋兂瑕佸埛鏂伴〉闈?)
|
||||||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '鐩爣鍒嗙被:', categoryId)
|
||||||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
// 鍗充娇鍒嗙被鐩稿悓锛屼篃閲嶆柊鍔犺浇鏁版嵁锛岀‘淇濇暟鎹槸鏈€鏂扮殑
|
||||||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
// 娣诲姞涓€涓皬鐨勫欢杩燂紝纭繚椤甸潰瀹屽叏鏄剧ず鍚庡啀鏇存柊鏁版嵁
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ onShow中未找到分类参数')
|
console.log('鈿狅笍 onShow涓湭鎵惧埌鍒嗙被鍙傛暟')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== category页面onShow执行完成 ===')
|
console.log('=== category椤甸潰onShow鎵ц瀹屾垚 ===')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 添加到购物车
|
// 娣诲姞鍒拌喘鐗╄溅
|
||||||
async function addToCart(product: Product): Promise<void> {
|
async function addToCart(product: Product): Promise<void> {
|
||||||
uni.showLoading({ title: '检查商品...' })
|
uni.showLoading({ title: '妫€鏌ュ晢鍝?..' })
|
||||||
try {
|
try {
|
||||||
const pid = (product.id ?? '').toString()
|
const pid = (product.id ?? '').toString()
|
||||||
const merchantId = product.merchant_id ?? ''
|
const merchantId = product.merchant_id ?? ''
|
||||||
if (pid === '') {
|
if (pid === '') {
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
uni.showToast({ title: '商品无效', icon: 'none' })
|
uni.showToast({ title: '鍟嗗搧鏃犳晥', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查商品是否有SKU
|
// 妫€鏌ュ晢鍝佹槸鍚︽湁SKU
|
||||||
const skus = await supabaseService.getProductSkus(pid)
|
const skus = await supabaseService.getProductSkus(pid)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
|
|
||||||
if (skus.length > 0) {
|
if (skus.length > 0) {
|
||||||
// 有规格,提示并跳转到商品详情页选择规格
|
// 鏈夎鏍硷紝鎻愮ず骞惰烦杞埌鍟嗗搧璇︽儏椤甸€夋嫨瑙勬牸
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请选择规格',
|
title: '璇烽€夋嫨瑙勬牸',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -479,31 +479,31 @@ async function addToCart(product: Product): Promise<void> {
|
|||||||
})
|
})
|
||||||
}, 500)
|
}, 500)
|
||||||
} else {
|
} else {
|
||||||
// 无规格,直接加入购物车
|
// 鏃犺鏍硷紝鐩存帴鍔犲叆璐墿杞?
|
||||||
uni.showLoading({ title: '添加中...' })
|
uni.showLoading({ title: '娣诲姞涓?..' })
|
||||||
const success = await supabaseService.addToCart(pid, 1, '', merchantId)
|
const success = await supabaseService.addToCart(pid, 1, '', merchantId)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
if (success) {
|
if (success) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已添加到购物车',
|
title: '宸叉坊鍔犲埌璐墿杞?,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
cartCount.value++
|
cartCount.value++
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '添加失败,请先登录',
|
title: '娣诲姞澶辫触锛岃鍏堢櫥褰?,
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('添加到购物车异常', e)
|
console.error('娣诲姞鍒拌喘鐗╄溅寮傚父', e)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
uni.showToast({ title: '操作失败', icon: 'none' })
|
uni.showToast({ title: '鎿嶄綔澶辫触', icon: 'none' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导航函数
|
// 瀵艰埅鍑芥暟
|
||||||
function navigateToSearch(): void { uni.navigateTo({ url: '/pages/mall/consumer/search' }) }
|
function navigateToSearch(): void { uni.navigateTo({ url: '/pages/mall/consumer/search' }) }
|
||||||
function navigateToCart(): void { uni.navigateTo({ url: '/pages/mall/consumer/cart' }) }
|
function navigateToCart(): void { uni.navigateTo({ url: '/pages/mall/consumer/cart' }) }
|
||||||
function navigateToProduct(product: Product): void {
|
function navigateToProduct(product: Product): void {
|
||||||
@@ -519,43 +519,43 @@ function navigateToProduct(product: Product): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 相机功能
|
// 鐩告満鍔熻兘
|
||||||
function onCamera(): void {
|
function onCamera(): void {
|
||||||
uni.chooseImage({
|
uni.chooseImage({
|
||||||
count: 1,
|
count: 1,
|
||||||
sourceType: ['camera'],
|
sourceType: ['camera'],
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('相机拍摄成功:', res.tempFilePaths[0])
|
console.log('鐩告満鎷嶆憚鎴愬姛:', res.tempFilePaths[0])
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已拍摄,正在识别...',
|
title: '宸叉媿鎽勶紝姝e湪璇嗗埆...',
|
||||||
icon: 'loading'
|
icon: 'loading'
|
||||||
})
|
})
|
||||||
// 这里可以添加后续的识别逻辑
|
// 杩欓噷鍙互娣诲姞鍚庣画鐨勮瘑鍒€昏緫
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '识别成功',
|
title: '璇嗗埆鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('相机调用失败:', err)
|
console.error('鐩告満璋冪敤澶辫触:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扫码功能
|
// 鎵爜鍔熻兘
|
||||||
function onScan(): void {
|
function onScan(): void {
|
||||||
uni.scanCode({
|
uni.scanCode({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('扫码成功:', res)
|
console.log('鎵爜鎴愬姛:', res)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '扫码成功: ' + res.result,
|
title: '鎵爜鎴愬姛: ' + res.result,
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('扫码失败:', err)
|
console.error('鎵爜澶辫触:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -571,7 +571,7 @@ function onScan(): void {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
||||||
}
|
}
|
||||||
/* 搜索栏 */
|
/* 鎼滅储鏍?*/
|
||||||
.search-bar {
|
.search-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -582,13 +582,13 @@ function onScan(): void {
|
|||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏占位 */
|
/* 瀵艰埅鏍忓崰浣?*/
|
||||||
.navbar-placeholder {
|
.navbar-placeholder {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索栏 */
|
/* 鎼滅储鏍?*/
|
||||||
/* 导航栏搜索框容器内边距调整 */
|
/* 瀵艰埅鏍忔悳绱㈡瀹瑰櫒鍐呰竟璺濊皟鏁?*/
|
||||||
.search-container {
|
.search-container {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -601,12 +601,12 @@ function onScan(): void {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索框 hover 效果 */
|
/* 鎼滅储妗?hover 鏁堟灉 */
|
||||||
.search-box:hover {
|
.search-box:hover {
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏搜索框容器内边距调整 */
|
/* 瀵艰埅鏍忔悳绱㈡瀹瑰櫒鍐呰竟璺濊皟鏁?*/
|
||||||
.search-box {
|
.search-box {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
@@ -631,7 +631,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-inner-search-text {
|
.nav-inner-search-text {
|
||||||
font-size: 12px; /* 字体稍微变小 */
|
font-size: 12px; /* 瀛椾綋绋嶅井鍙樺皬 */
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
@@ -664,7 +664,7 @@ function onScan(): void {
|
|||||||
border-right-width: 1px;
|
border-right-width: 1px;
|
||||||
border-right-style: solid;
|
border-right-style: solid;
|
||||||
border-right-color: #ddd;
|
border-right-color: #ddd;
|
||||||
border-right: 1px solid #ddd; /* 修复UVUE样式 */
|
border-right: 1px solid #ddd; /* 淇UVUE鏍峰紡 */
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,23 +672,23 @@ function onScan(): void {
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索按钮高度微调 */
|
/* 鎼滅储鎸夐挳楂樺害寰皟 */
|
||||||
.nav-inner-search-btn {
|
.nav-inner-search-btn {
|
||||||
padding: 0 12px; /* 减小内边距 */
|
padding: 0 12px; /* 鍑忓皬鍐呰竟璺?*/
|
||||||
background-color: #87CEEB; /* 天空蓝 */
|
background-color: #87CEEB; /* 澶╃┖钃?*/
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 24px; /* 随搜索框高度减小而减小 */
|
height: 24px; /* 闅忔悳绱㈡楂樺害鍑忓皬鑰屽噺灏?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.cart-badge {
|
.cart-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5px;
|
top: -5px;
|
||||||
right: -5px;
|
right: -5px;
|
||||||
background: #FF5722;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
min-width: 18px;
|
min-width: 18px;
|
||||||
@@ -701,7 +701,7 @@ function onScan(): void {
|
|||||||
border: 2px solid #4CAF50;
|
border: 2px solid #4CAF50;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分类内容区 */
|
/* 鍒嗙被鍐呭鍖?*/
|
||||||
.category-content {
|
.category-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -714,7 +714,7 @@ function onScan(): void {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 左侧一级分类 */
|
/* 宸︿晶涓€绾у垎绫?*/
|
||||||
.primary-category {
|
.primary-category {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
flex: 0 0 100px;
|
flex: 0 0 100px;
|
||||||
@@ -741,7 +741,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.primary-item:hover {
|
.primary-item:hover {
|
||||||
transform: translateY(-2px); /* 悬停时向上浮动 */
|
transform: translateY(-2px); /* 鎮仠鏃跺悜涓婃诞鍔?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-item.active {
|
.primary-item.active {
|
||||||
@@ -752,7 +752,7 @@ function onScan(): void {
|
|||||||
.primary-icon {
|
.primary-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
margin-right: 0; /* 移除右边距 */
|
margin-right: 0; /* 绉婚櫎鍙宠竟璺?*/
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/* display: block; removed for uniapp-x support */
|
/* display: block; removed for uniapp-x support */
|
||||||
}
|
}
|
||||||
@@ -763,7 +763,7 @@ function onScan(): void {
|
|||||||
/* display: block; removed for uniapp-x support */
|
/* display: block; removed for uniapp-x support */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 右侧内容区 */
|
/* 鍙充晶鍐呭鍖?*/
|
||||||
.product-content {
|
.product-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -790,7 +790,7 @@ function onScan(): void {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 商品网格 */
|
/* 鍟嗗搧缃戞牸 */
|
||||||
.product-grid {
|
.product-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -896,7 +896,7 @@ function onScan(): void {
|
|||||||
margin-right: 6px; /* gap replacement */
|
margin-right: 6px; /* gap replacement */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 绌虹姸鎬?*/
|
||||||
.empty-state {
|
.empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -924,7 +924,7 @@ function onScan(): void {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加载更多 */
|
/* 鍔犺浇鏇村 */
|
||||||
.load-more {
|
.load-more {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
@@ -939,9 +939,9 @@ function onScan(): void {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 响应式设计 ===== */
|
/* ===== 鍝嶅簲寮忚璁?===== */
|
||||||
|
|
||||||
/* 小屏手机 (小于414px) */
|
/* 灏忓睆鎵嬫満 (灏忎簬414px) */
|
||||||
@media screen and (max-width: 414px) {
|
@media screen and (max-width: 414px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
@@ -1051,7 +1051,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 中屏手机/小平板 (415px-768px) */
|
/* 涓睆鎵嬫満/灏忓钩鏉?(415px-768px) */
|
||||||
@media screen and (min-width: 415px) and (max-width: 768px) {
|
@media screen and (min-width: 415px) and (max-width: 768px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -1075,7 +1075,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 平板设备 (769px-1024px) */
|
/* 骞虫澘璁惧 (769px-1024px) */
|
||||||
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -1109,7 +1109,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 桌面端 (1025px以上) */
|
/* 妗岄潰绔?(1025px浠ヤ笂) */
|
||||||
@media screen and (min-width: 1025px) {
|
@media screen and (min-width: 1025px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -1188,7 +1188,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 大桌面端 (1400px以上) */
|
/* 澶ф闈㈢ (1400px浠ヤ笂) */
|
||||||
@media screen and (min-width: 1400px) {
|
@media screen and (min-width: 1400px) {
|
||||||
.category-content {
|
.category-content {
|
||||||
max-width: 1600px;
|
max-width: 1600px;
|
||||||
@@ -1267,3 +1267,5 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1467
pages/mall/consumer/category copy 3.uvue
Normal file
@@ -1,36 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="category-page">
|
<view class="category-page">
|
||||||
<!-- 顶部搜索栏 -->
|
<!-- 椤堕儴鎼滅储鏍?-->
|
||||||
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view class="search-container">
|
<view class="search-container">
|
||||||
<view class="search-box" @click="navigateToSearch" :style="{ height: '30px' }">
|
<view class="search-box" @click="navigateToSearch" :style="{ height: '30px' }">
|
||||||
<!-- 模拟输入框 -->
|
<!-- 妯℃嫙杈撳叆妗?-->
|
||||||
<text class="search-placeholder">请输入药品名称、症状或品牌</text>
|
<text class="search-placeholder">璇疯緭鍏ヨ嵂鍝佸悕绉般€佺棁鐘舵垨鍝佺墝</text>
|
||||||
|
|
||||||
<!-- 扫码图标 -->
|
<!-- 鎵爜鍥炬爣 -->
|
||||||
<view class="nav-icon-btn" @click.stop="onScan">
|
<view class="nav-icon-btn" @click.stop="onScan">
|
||||||
<text class="nav-icon">🔳</text>
|
<text class="nav-icon">馃敵</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 相机图标 -->
|
<!-- 鐩告満鍥炬爣 -->
|
||||||
<view class="nav-camera-btn" @click.stop="onCamera">
|
<view class="nav-camera-btn" @click.stop="onCamera">
|
||||||
<text class="nav-camera-icon">📷</text>
|
<text class="nav-camera-icon">馃摲</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 搜索按钮 -->
|
<!-- 鎼滅储鎸夐挳 -->
|
||||||
<view class="nav-inner-search-btn" :style="{ height: '22px' }">
|
<view class="nav-inner-search-btn" :style="{ height: '22px' }">
|
||||||
<text class="nav-inner-search-text">搜索</text>
|
<text class="nav-inner-search-text">鎼滅储</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 导航栏占位 - 需要包含statusBarHeight + 搜索框高度44px -->
|
<!-- 瀵艰埅鏍忓崰浣?- 闇€瑕佸寘鍚玸tatusBarHeight + 鎼滅储妗嗛珮搴?4px -->
|
||||||
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||||
|
|
||||||
<!-- 分类内容区 -->
|
<!-- 鍒嗙被鍐呭鍖?-->
|
||||||
<view class="category-content">
|
<view class="category-content">
|
||||||
<!-- 左侧一级分类 -->
|
<!-- 宸︿晶涓€绾у垎绫?-->
|
||||||
<scroll-view scroll-y class="primary-category">
|
<scroll-view scroll-y class="primary-category">
|
||||||
<view
|
<view
|
||||||
v-for="item in primaryCategories"
|
v-for="item in primaryCategories"
|
||||||
@@ -44,20 +44,20 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 右侧商品列表 -->
|
<!-- 鍙充晶鍟嗗搧鍒楄〃 -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
scroll-y
|
scroll-y
|
||||||
class="product-content"
|
class="product-content"
|
||||||
@scrolltolower="loadMore"
|
@scrolltolower="loadMore"
|
||||||
:lower-threshold="50"
|
:lower-threshold="50"
|
||||||
>
|
>
|
||||||
<!-- 分类标题 -->
|
<!-- 鍒嗙被鏍囬 -->
|
||||||
<view class="category-header">
|
<view class="category-header">
|
||||||
<text class="category-title">{{ currentCategoryName }}</text>
|
<text class="category-title">{{ currentCategoryName }}</text>
|
||||||
<text class="category-desc">{{ currentCategoryDesc }}</text>
|
<text class="category-desc">{{ currentCategoryDesc }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品网格 -->
|
<!-- 鍟嗗搧缃戞牸 -->
|
||||||
<view v-if="productList.length > 0" class="product-grid">
|
<view v-if="productList.length > 0" class="product-grid">
|
||||||
<view
|
<view
|
||||||
v-for="product in productList"
|
v-for="product in productList"
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
/>
|
/>
|
||||||
<text class="product-name" :lines="2">{{ product.name }}</text>
|
<text class="product-name" :lines="2">{{ product.name }}</text>
|
||||||
<view class="product-bottom">
|
<view class="product-bottom">
|
||||||
<text class="product-price">¥{{ product.base_price ?? product.price ?? 0 }}</text>
|
<text class="product-price">楼{{ product.base_price ?? product.price ?? 0 }}</text>
|
||||||
<view class="product-add-btn" @click.stop="addToCart(product)">
|
<view class="product-add-btn" @click.stop="addToCart(product)">
|
||||||
<text class="add-icon">+</text>
|
<text class="add-icon">+</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -80,16 +80,16 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 绌虹姸鎬?-->
|
||||||
<view v-else class="empty-state">
|
<view v-else class="empty-state">
|
||||||
<text class="empty-icon">💊</text>
|
<text class="empty-icon">馃拪</text>
|
||||||
<text class="empty-text">暂无相关药品</text>
|
<text class="empty-text">鏆傛棤鐩稿叧鑽搧</text>
|
||||||
<text class="empty-desc">该分类下暂无商品,敬请期待</text>
|
<text class="empty-desc">璇ュ垎绫讳笅鏆傛棤鍟嗗搧锛屾暚璇锋湡寰?/text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载更多提示 -->
|
<!-- 鍔犺浇鏇村鎻愮ず -->
|
||||||
<view v-if="hasMore" class="load-more">
|
<view v-if="hasMore" class="load-more">
|
||||||
<text class="load-text">上拉加载更多</text>
|
<text class="load-text">涓婃媺鍔犺浇鏇村</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
@@ -109,38 +109,38 @@ type LocalCategory = {
|
|||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式数据
|
// 鍝嶅簲寮忔暟鎹?
|
||||||
const statusBarHeight = ref(0)
|
const statusBarHeight = ref(0)
|
||||||
const headerHeight = ref(44) // 默认头部高度
|
const headerHeight = ref(44) // 榛樿澶撮儴楂樺害
|
||||||
const primaryCategories = ref<LocalCategory[]>([])
|
const primaryCategories = ref<LocalCategory[]>([])
|
||||||
const productList = ref<Product[]>([])
|
const productList = ref<Product[]>([])
|
||||||
const activePrimary = ref<string>('')
|
const activePrimary = ref<string>('')
|
||||||
const cartCount = ref(3)
|
const cartCount = ref(3)
|
||||||
const hasMore = ref(true)
|
const hasMore = ref(true)
|
||||||
const hasLoadedFromParams = ref(false) // 标记是否已通过参数加载
|
const hasLoadedFromParams = ref(false) // 鏍囪鏄惁宸查€氳繃鍙傛暟鍔犺浇
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
// 获取当前分类信息
|
// 鑾峰彇褰撳墠鍒嗙被淇℃伅
|
||||||
const currentCategoryName = ref('')
|
const currentCategoryName = ref('')
|
||||||
const currentCategoryDesc = ref('')
|
const currentCategoryDesc = ref('')
|
||||||
|
|
||||||
// 页面参数
|
// 椤甸潰鍙傛暟
|
||||||
const pageParams = ref<any>({})
|
const pageParams = ref<any>({})
|
||||||
|
|
||||||
// 加载商品数据
|
// 鍔犺浇鍟嗗搧鏁版嵁
|
||||||
async function loadProducts(): Promise<void> {
|
async function loadProducts(): Promise<void> {
|
||||||
if (loading.value) return
|
if (loading.value) return
|
||||||
if (activePrimary.value == '') {
|
if (activePrimary.value == '') {
|
||||||
console.warn('activePrimary为空,无法加载商品')
|
console.warn('activePrimary涓虹┖锛屾棤娉曞姞杞藉晢鍝?)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
console.log('开始加载商品,分类ID:', activePrimary.value, '页码:', currentPage.value)
|
console.log('寮€濮嬪姞杞藉晢鍝侊紝鍒嗙被ID:', activePrimary.value, '椤电爜:', currentPage.value)
|
||||||
const response = await supabaseService.getProductsByCategory(activePrimary.value, currentPage.value)
|
const response = await supabaseService.getProductsByCategory(activePrimary.value, currentPage.value)
|
||||||
console.log('商品加载结果:', {
|
console.log('鍟嗗搧鍔犺浇缁撴灉:', {
|
||||||
dataCount: response.data.length,
|
dataCount: response.data.length,
|
||||||
total: response.total,
|
total: response.total,
|
||||||
hasmore: response.hasmore,
|
hasmore: response.hasmore,
|
||||||
@@ -155,16 +155,16 @@ async function loadProducts(): Promise<void> {
|
|||||||
|
|
||||||
hasMore.value = response.hasmore
|
hasMore.value = response.hasmore
|
||||||
|
|
||||||
// 更新当前分类信息
|
// 鏇存柊褰撳墠鍒嗙被淇℃伅
|
||||||
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === activePrimary.value)
|
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === activePrimary.value)
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
currentCategoryName.value = category.name
|
currentCategoryName.value = category.name
|
||||||
currentCategoryDesc.value = category.description
|
currentCategoryDesc.value = category.description
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('商品列表加载完成,当前总数量:', productList.value.length)
|
console.log('鍟嗗搧鍒楄〃鍔犺浇瀹屾垚锛屽綋鍓嶆€绘暟閲?', productList.value.length)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载商品数据失败:', error)
|
console.error('鍔犺浇鍟嗗搧鏁版嵁澶辫触:', error)
|
||||||
if (currentPage.value === 1) {
|
if (currentPage.value === 1) {
|
||||||
productList.value = []
|
productList.value = []
|
||||||
}
|
}
|
||||||
@@ -176,22 +176,22 @@ async function loadProducts(): Promise<void> {
|
|||||||
async function loadCategories(): Promise<void> {
|
async function loadCategories(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const categoriesData = await supabaseService.getCategories()
|
const categoriesData = await supabaseService.getCategories()
|
||||||
console.log('加载分类数据成功,数量:', categoriesData.length)
|
console.log('鍔犺浇鍒嗙被鏁版嵁鎴愬姛锛屾暟閲?', categoriesData.length)
|
||||||
|
|
||||||
// 映射数据并添加默认颜色,防止选中时背景透明导致文字看不清
|
// 鏄犲皠鏁版嵁骞舵坊鍔犻粯璁ら鑹诧紝闃叉閫変腑鏃惰儗鏅€忔槑瀵艰嚧鏂囧瓧鐪嬩笉娓?
|
||||||
// 过滤掉医药健康相关分类
|
// 杩囨护鎺夊尰鑽仴搴风浉鍏冲垎绫?
|
||||||
const categories: LocalCategory[] = []
|
const categories: LocalCategory[] = []
|
||||||
const rawList = categoriesData as any[]
|
const rawList = categoriesData as any[]
|
||||||
for (let i = 0; i < rawList.length; i++) {
|
for (let i = 0; i < rawList.length; i++) {
|
||||||
const raw = rawList[i]
|
const raw = rawList[i]
|
||||||
const catObj = (raw instanceof UTSJSONObject) ? (raw as UTSJSONObject) : (JSON.parse(JSON.stringify(raw)) as UTSJSONObject)
|
const catObj = (raw instanceof UTSJSONObject) ? (raw as UTSJSONObject) : (JSON.parse(JSON.stringify(raw)) as UTSJSONObject)
|
||||||
const name = catObj.getString('name') ?? ''
|
const name = catObj.getString('name') ?? ''
|
||||||
if (name.includes('医药') || name.includes('健康')) {
|
if (name.includes('鍖昏嵂') || name.includes('鍋ュ悍')) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const id = catObj.getString('id') ?? ''
|
const id = catObj.getString('id') ?? ''
|
||||||
const description = catObj.getString('description') ?? ''
|
const description = catObj.getString('description') ?? ''
|
||||||
const icon = catObj.getString('icon') ?? catObj.getString('icon_url') ?? '📦'
|
const icon = catObj.getString('icon') ?? catObj.getString('icon_url') ?? '馃摝'
|
||||||
const color = catObj.getString('color') ?? '#4CAF50'
|
const color = catObj.getString('color') ?? '#4CAF50'
|
||||||
categories.push({
|
categories.push({
|
||||||
id,
|
id,
|
||||||
@@ -204,36 +204,36 @@ async function loadCategories(): Promise<void> {
|
|||||||
|
|
||||||
if (categories.length > 0) {
|
if (categories.length > 0) {
|
||||||
primaryCategories.value = categories
|
primaryCategories.value = categories
|
||||||
// 如果没有通过参数设置分类,则设置默认选中一个分类
|
// 濡傛灉娌℃湁閫氳繃鍙傛暟璁剧疆鍒嗙被锛屽垯璁剧疆榛樿閫変腑涓€涓垎绫?
|
||||||
if (activePrimary.value == '') {
|
if (activePrimary.value == '') {
|
||||||
// 优先查找"厨具"相关的分类作为默认
|
// 浼樺厛鏌ユ壘"鍘ㄥ叿"鐩稿叧鐨勫垎绫讳綔涓洪粯璁?
|
||||||
const defaultCategory = categories.find((c: LocalCategory): boolean => c.name.includes('厨具')) ?? categories[0]
|
const defaultCategory = categories.find((c: LocalCategory): boolean => c.name.includes('鍘ㄥ叿')) ?? categories[0]
|
||||||
|
|
||||||
activePrimary.value = defaultCategory.id
|
activePrimary.value = defaultCategory.id
|
||||||
console.log('设置默认分类为:', defaultCategory.name, 'ID:', defaultCategory.id)
|
console.log('璁剧疆榛樿鍒嗙被涓?', defaultCategory.name, 'ID:', defaultCategory.id)
|
||||||
currentCategoryName.value = defaultCategory.name
|
currentCategoryName.value = defaultCategory.name
|
||||||
currentCategoryDesc.value = defaultCategory.description
|
currentCategoryDesc.value = defaultCategory.description
|
||||||
} else {
|
} else {
|
||||||
// 如果已经选中了分类(可能来自Storage),更新显示信息
|
// 濡傛灉宸茬粡閫変腑浜嗗垎绫伙紙鍙兘鏉ヨ嚜Storage锛夛紝鏇存柊鏄剧ず淇℃伅
|
||||||
const current = categories.find((c: LocalCategory): boolean => c.id == activePrimary.value)
|
const current = categories.find((c: LocalCategory): boolean => c.id == activePrimary.value)
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
currentCategoryName.value = current.name
|
currentCategoryName.value = current.name
|
||||||
currentCategoryDesc.value = current.description
|
currentCategoryDesc.value = current.description
|
||||||
// 如果此时没有商品列表(且没有正在加载),可能需要加载
|
// 濡傛灉姝ゆ椂娌℃湁鍟嗗搧鍒楄〃锛堜笖娌℃湁姝e湪鍔犺浇锛夛紝鍙兘闇€瑕佸姞杞?
|
||||||
if (productList.value.length === 0 && !loading.value) {
|
if (productList.value.length === 0 && !loading.value) {
|
||||||
loadProducts()
|
loadProducts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('从Supabase获取的分类数据为空')
|
console.warn('浠嶴upabase鑾峰彇鐨勫垎绫绘暟鎹负绌?)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载分类数据失败:', error)
|
console.error('鍔犺浇鍒嗙被鏁版嵁澶辫触:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载更多
|
// 鍔犺浇鏇村
|
||||||
function loadMore(): void {
|
function loadMore(): void {
|
||||||
if (hasMore.value && !loading.value) {
|
if (hasMore.value && !loading.value) {
|
||||||
currentPage.value++
|
currentPage.value++
|
||||||
@@ -241,72 +241,72 @@ function loadMore(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择一级分类
|
// 閫夋嫨涓€绾у垎绫?
|
||||||
async function selectPrimaryCategory(categoryId: string): Promise<void> {
|
async function selectPrimaryCategory(categoryId: string): Promise<void> {
|
||||||
console.log('=== selectPrimaryCategory函数开始执行 ===')
|
console.log('=== selectPrimaryCategory鍑芥暟寮€濮嬫墽琛?===')
|
||||||
console.log('传入的categoryId:', categoryId)
|
console.log('浼犲叆鐨刢ategoryId:', categoryId)
|
||||||
console.log('当前时间:', Date.now())
|
console.log('褰撳墠鏃堕棿:', Date.now())
|
||||||
|
|
||||||
// 验证categoryId是否有效
|
// 楠岃瘉categoryId鏄惁鏈夋晥
|
||||||
if (categoryId == '') {
|
if (categoryId == '') {
|
||||||
console.error('categoryId为空,尝试使用第一个分类')
|
console.error('categoryId涓虹┖锛屽皾璇曚娇鐢ㄧ涓€涓垎绫?)
|
||||||
if (primaryCategories.value.length > 0) {
|
if (primaryCategories.value.length > 0) {
|
||||||
categoryId = primaryCategories.value[0].id
|
categoryId = primaryCategories.value[0].id
|
||||||
} else {
|
} else {
|
||||||
console.error('没有可用的分类')
|
console.error('娌℃湁鍙敤鐨勫垎绫?)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('验证后的categoryId:', categoryId)
|
console.log('楠岃瘉鍚庣殑categoryId:', categoryId)
|
||||||
console.log('当前activePrimary的值:', activePrimary.value)
|
console.log('褰撳墠activePrimary鐨勫€?', activePrimary.value)
|
||||||
|
|
||||||
// 更新活动分类
|
// 鏇存柊娲诲姩鍒嗙被
|
||||||
activePrimary.value = categoryId
|
activePrimary.value = categoryId
|
||||||
console.log('更新后的activePrimary:', activePrimary.value)
|
console.log('鏇存柊鍚庣殑activePrimary:', activePrimary.value)
|
||||||
|
|
||||||
// 更新当前分类信息
|
// 鏇存柊褰撳墠鍒嗙被淇℃伅
|
||||||
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === categoryId)
|
const category = primaryCategories.value.find((cat: LocalCategory): boolean => cat.id === categoryId)
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
currentCategoryName.value = category.name
|
currentCategoryName.value = category.name
|
||||||
currentCategoryDesc.value = category.description
|
currentCategoryDesc.value = category.description
|
||||||
console.log('✅ 找到分类:', category.name, '描述:', category.description)
|
console.log('鉁?鎵惧埌鍒嗙被:', category.name, '鎻忚堪:', category.description)
|
||||||
} else {
|
} else {
|
||||||
console.error('❌ 未找到分类ID:', categoryId, ',使用第一个分类')
|
console.error('鉂?鏈壘鍒板垎绫籌D:', categoryId, '锛屼娇鐢ㄧ涓€涓垎绫?)
|
||||||
// 如果找不到对应的分类,使用第一个分类
|
// 濡傛灉鎵句笉鍒板搴旂殑鍒嗙被锛屼娇鐢ㄧ涓€涓垎绫?
|
||||||
if (primaryCategories.value.length > 0) {
|
if (primaryCategories.value.length > 0) {
|
||||||
const firstCategory = primaryCategories.value[0]
|
const firstCategory = primaryCategories.value[0]
|
||||||
currentCategoryName.value = firstCategory.name
|
currentCategoryName.value = firstCategory.name
|
||||||
currentCategoryDesc.value = firstCategory.description
|
currentCategoryDesc.value = firstCategory.description
|
||||||
activePrimary.value = firstCategory.id
|
activePrimary.value = firstCategory.id
|
||||||
categoryId = firstCategory.id
|
categoryId = firstCategory.id
|
||||||
console.log('使用默认分类:', firstCategory.name)
|
console.log('浣跨敤榛樿鍒嗙被:', firstCategory.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('准备加载商品数据...')
|
console.log('鍑嗗鍔犺浇鍟嗗搧鏁版嵁...')
|
||||||
|
|
||||||
// 重置分页并加载
|
// 閲嶇疆鍒嗛〉骞跺姞杞?
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
hasMore.value = true
|
hasMore.value = true
|
||||||
await loadProducts()
|
await loadProducts()
|
||||||
|
|
||||||
console.log('✅ 加载商品数据成功')
|
console.log('鉁?鍔犺浇鍟嗗搧鏁版嵁鎴愬姛')
|
||||||
console.log('分类:', categoryId)
|
console.log('鍒嗙被:', categoryId)
|
||||||
console.log('商品数量:', productList.value.length)
|
console.log('鍟嗗搧鏁伴噺:', productList.value.length)
|
||||||
console.log('商品列表:', productList.value)
|
console.log('鍟嗗搧鍒楄〃:', productList.value)
|
||||||
|
|
||||||
// 验证数据是否已正确更新
|
// 楠岃瘉鏁版嵁鏄惁宸叉纭洿鏂?
|
||||||
console.log('数据更新验证:')
|
console.log('鏁版嵁鏇存柊楠岃瘉:')
|
||||||
console.log('activePrimary:', activePrimary.value)
|
console.log('activePrimary:', activePrimary.value)
|
||||||
console.log('currentCategoryName:', currentCategoryName.value)
|
console.log('currentCategoryName:', currentCategoryName.value)
|
||||||
console.log('currentCategoryDesc:', currentCategoryDesc.value)
|
console.log('currentCategoryDesc:', currentCategoryDesc.value)
|
||||||
console.log('productList长度:', productList.value.length)
|
console.log('productList闀垮害:', productList.value.length)
|
||||||
|
|
||||||
console.log('=== selectPrimaryCategory函数执行完成 ===')
|
console.log('=== selectPrimaryCategory鍑芥暟鎵ц瀹屾垚 ===')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生命周期
|
// 鐢熷懡鍛ㄦ湡
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadCategories().then(() => {
|
loadCategories().then(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -317,160 +317,160 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页面加载时处理参数 - 这是处理分类切换的主要入口
|
// 椤甸潰鍔犺浇鏃跺鐞嗗弬鏁?- 杩欐槸澶勭悊鍒嗙被鍒囨崲鐨勪富瑕佸叆鍙?
|
||||||
onLoad((options: any) => {
|
onLoad((options: any) => {
|
||||||
// 获取系统状态栏高度
|
// 鑾峰彇绯荤粺鐘舵€佹爮楂樺害
|
||||||
const systemInfo = uni.getSystemInfoSync()
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
statusBarHeight.value = systemInfo.statusBarHeight
|
statusBarHeight.value = systemInfo.statusBarHeight
|
||||||
console.log('=== category页面onLoad被调用 ===')
|
console.log('=== category椤甸潰onLoad琚皟鐢?===')
|
||||||
console.log('页面加载时间:', Date.now())
|
console.log('椤甸潰鍔犺浇鏃堕棿:', Date.now())
|
||||||
console.log('传入的options参数:', options)
|
console.log('浼犲叆鐨刼ptions鍙傛暟:', options)
|
||||||
console.log('当前活动分类:', activePrimary.value)
|
console.log('褰撳墠娲诲姩鍒嗙被:', activePrimary.value)
|
||||||
|
|
||||||
let categoryId = ''
|
let categoryId = ''
|
||||||
let categoryName = ''
|
let categoryName = ''
|
||||||
|
|
||||||
// 首先检查传入的options参数
|
// 棣栧厛妫€鏌ヤ紶鍏ョ殑options鍙傛暟
|
||||||
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
||||||
const optCategoryId = optObj.getString('categoryId') ?? ''
|
const optCategoryId = optObj.getString('categoryId') ?? ''
|
||||||
if (optCategoryId !== '') {
|
if (optCategoryId !== '') {
|
||||||
categoryId = optCategoryId
|
categoryId = optCategoryId
|
||||||
categoryName = optObj.getString('name') ?? ''
|
categoryName = optObj.getString('name') ?? ''
|
||||||
console.log('✅ onLoad中找到分类参数:', categoryId, categoryName)
|
console.log('鉁?onLoad涓壘鍒板垎绫诲弬鏁?', categoryId, categoryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果options中没有,尝试从getCurrentPages()获取
|
// 濡傛灉options涓病鏈夛紝灏濊瘯浠巊etCurrentPages()鑾峰彇
|
||||||
if (categoryId == '') {
|
if (categoryId == '') {
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
if (pages.length > 0) {
|
if (pages.length > 0) {
|
||||||
const currentPage = pages[pages.length - 1]
|
const currentPage = pages[pages.length - 1]
|
||||||
const rawPageOptions = currentPage.options ?? {}
|
const rawPageOptions = currentPage.options ?? {}
|
||||||
console.log('从getCurrentPages()获取参数:', rawPageOptions)
|
console.log('浠巊etCurrentPages()鑾峰彇鍙傛暟:', rawPageOptions)
|
||||||
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
||||||
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
||||||
if (pageCategoryId !== '') {
|
if (pageCategoryId !== '') {
|
||||||
categoryId = pageCategoryId
|
categoryId = pageCategoryId
|
||||||
categoryName = pageOptObj.getString('name') ?? ''
|
categoryName = pageOptObj.getString('name') ?? ''
|
||||||
console.log('✅ 从getCurrentPages()找到分类参数:', categoryId, categoryName)
|
console.log('鉁?浠巊etCurrentPages()鎵惧埌鍒嗙被鍙傛暟:', categoryId, categoryName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有找到分类ID,则选中对应的分类
|
// 濡傛灉鏈夋壘鍒板垎绫籌D锛屽垯閫変腑瀵瑰簲鐨勫垎绫?
|
||||||
if (categoryId != '') {
|
if (categoryId != '') {
|
||||||
hasLoadedFromParams.value = true
|
hasLoadedFromParams.value = true
|
||||||
console.log('✅ 准备选中分类:', categoryId)
|
console.log('鉁?鍑嗗閫変腑鍒嗙被:', categoryId)
|
||||||
console.log('分类名称:', categoryName ?? '未指定')
|
console.log('鍒嗙被鍚嶇О:', categoryName ?? '鏈寚瀹?)
|
||||||
|
|
||||||
// 检查是否需要更新分类
|
// 妫€鏌ユ槸鍚﹂渶瑕佹洿鏂板垎绫?
|
||||||
if (activePrimary.value !== categoryId) {
|
if (activePrimary.value !== categoryId) {
|
||||||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '涓庣洰鏍囧垎绫?', categoryId, '涓嶅悓锛岄渶瑕佹洿鏂?)
|
||||||
console.log('准备调用selectPrimaryCategory函数...')
|
console.log('鍑嗗璋冪敤selectPrimaryCategory鍑芥暟...')
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
} else {
|
} else {
|
||||||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
console.log('褰撳墠鍒嗙被宸茬粡鏄洰鏍囧垎绫伙紝浣嗗彲鑳界敤鎴锋兂瑕佸埛鏂伴〉闈?)
|
||||||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '鐩爣鍒嗙被:', categoryId)
|
||||||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
// 鍗充娇鍒嗙被鐩稿悓锛屼篃閲嶆柊鍔犺浇鏁版嵁锛岀‘淇濇暟鎹槸鏈€鏂扮殑
|
||||||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
// 娣诲姞涓€涓皬鐨勫欢杩燂紝纭繚椤甸潰瀹屽叏鏄剧ず鍚庡啀鏇存柊鏁版嵁
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ onLoad中未找到分类参数,将使用从数据库加载的第一个分类')
|
console.log('鈿狅笍 onLoad涓湭鎵惧埌鍒嗙被鍙傛暟锛屽皢浣跨敤浠庢暟鎹簱鍔犺浇鐨勭涓€涓垎绫?)
|
||||||
// 不再使用硬编码的默认分类,loadCategories 会设置第一个分类
|
// 涓嶅啀浣跨敤纭紪鐮佺殑榛樿鍒嗙被锛宭oadCategories 浼氳缃涓€涓垎绫?
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== category页面onLoad执行完成 ===')
|
console.log('=== category椤甸潰onLoad鎵ц瀹屾垚 ===')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页面显示时也检查参数,确保从其他页面返回时能正确显示
|
// 椤甸潰鏄剧ず鏃朵篃妫€鏌ュ弬鏁帮紝纭繚浠庡叾浠栭〉闈㈣繑鍥炴椂鑳芥纭樉绀?
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
console.log('=== category页面onShow被调用 ===')
|
console.log('=== category椤甸潰onShow琚皟鐢?===')
|
||||||
console.log('页面显示时间:', Date.now())
|
console.log('椤甸潰鏄剧ず鏃堕棿:', Date.now())
|
||||||
console.log('当前活动分类:', activePrimary.value)
|
console.log('褰撳墠娲诲姩鍒嗙被:', activePrimary.value)
|
||||||
|
|
||||||
// 1. 优先检查 Storage 中的参数 (由首页传入)
|
// 1. 浼樺厛妫€鏌?Storage 涓殑鍙傛暟 (鐢遍椤典紶鍏?
|
||||||
const storageCategoryId = (uni.getStorageSync('selectedCategory') as string) ?? ''
|
const storageCategoryId = (uni.getStorageSync('selectedCategory') as string) ?? ''
|
||||||
if (storageCategoryId !== '') {
|
if (storageCategoryId !== '') {
|
||||||
console.log('✅ onShow中找到Storage分类参数:', storageCategoryId)
|
console.log('鉁?onShow涓壘鍒癝torage鍒嗙被鍙傛暟:', storageCategoryId)
|
||||||
hasLoadedFromParams.value = true
|
hasLoadedFromParams.value = true
|
||||||
// 清除Storage,防止下次误读
|
// 娓呴櫎Storage锛岄槻姝笅娆¤璇?
|
||||||
uni.removeStorageSync('selectedCategory')
|
uni.removeStorageSync('selectedCategory')
|
||||||
|
|
||||||
if (activePrimary.value !== storageCategoryId) {
|
if (activePrimary.value !== storageCategoryId) {
|
||||||
selectPrimaryCategory(storageCategoryId)
|
selectPrimaryCategory(storageCategoryId)
|
||||||
}
|
}
|
||||||
// 如果分类还没加载完,这里设置了ID,等loadCategories完成后会自动匹配信息
|
// 濡傛灉鍒嗙被杩樻病鍔犺浇瀹岋紝杩欓噷璁剧疆浜咺D锛岀瓑loadCategories瀹屾垚鍚庝細鑷姩鍖归厤淇℃伅
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在onShow中,我们也需要检查是否有新的参数
|
// 鍦╫nShow涓紝鎴戜滑涔熼渶瑕佹鏌ユ槸鍚︽湁鏂扮殑鍙傛暟
|
||||||
// 因为当从主页再次点击分类跳转过来时,可能不会触发onLoad
|
// 鍥犱负褰撲粠涓婚〉鍐嶆鐐瑰嚮鍒嗙被璺宠浆杩囨潵鏃讹紝鍙兘涓嶄細瑙﹀彂onLoad
|
||||||
// 而是触发onShow
|
// 鑰屾槸瑙﹀彂onShow
|
||||||
|
|
||||||
// 获取当前页面实例和参数
|
// 鑾峰彇褰撳墠椤甸潰瀹炰緥鍜屽弬鏁?
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
if (pages.length > 0) {
|
if (pages.length > 0) {
|
||||||
const currentPage = pages[pages.length - 1]
|
const currentPage = pages[pages.length - 1]
|
||||||
const rawPageOptions = currentPage.options ?? {}
|
const rawPageOptions = currentPage.options ?? {}
|
||||||
console.log('onShow中获取参数:', rawPageOptions)
|
console.log('onShow涓幏鍙栧弬鏁?', rawPageOptions)
|
||||||
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
const pageOptObj = (rawPageOptions instanceof UTSJSONObject) ? (rawPageOptions as UTSJSONObject) : (JSON.parse(JSON.stringify(rawPageOptions)) as UTSJSONObject)
|
||||||
|
|
||||||
// 检查是否有分类参数
|
// 妫€鏌ユ槸鍚︽湁鍒嗙被鍙傛暟
|
||||||
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
const pageCategoryId = pageOptObj.getString('categoryId') ?? ''
|
||||||
if (pageCategoryId !== '') {
|
if (pageCategoryId !== '') {
|
||||||
hasLoadedFromParams.value = true
|
hasLoadedFromParams.value = true
|
||||||
const categoryId = pageCategoryId
|
const categoryId = pageCategoryId
|
||||||
const categoryName = pageOptObj.getString('name') ?? ''
|
const categoryName = pageOptObj.getString('name') ?? ''
|
||||||
|
|
||||||
console.log('✅ onShow中找到分类参数:', categoryId, categoryName)
|
console.log('鉁?onShow涓壘鍒板垎绫诲弬鏁?', categoryId, categoryName)
|
||||||
console.log('URL中的时间戳参数:', pageOptObj.getString('timestamp') ?? '')
|
console.log('URL涓殑鏃堕棿鎴冲弬鏁?', pageOptObj.getString('timestamp') ?? '')
|
||||||
console.log('URL中的随机参数:', pageOptObj.getString('random') ?? '')
|
console.log('URL涓殑闅忔満鍙傛暟:', pageOptObj.getString('random') ?? '')
|
||||||
|
|
||||||
// 检查是否需要更新分类
|
// 妫€鏌ユ槸鍚﹂渶瑕佹洿鏂板垎绫?
|
||||||
if (activePrimary.value !== categoryId) {
|
if (activePrimary.value !== categoryId) {
|
||||||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '涓庣洰鏍囧垎绫?', categoryId, '涓嶅悓锛岄渶瑕佹洿鏂?)
|
||||||
console.log('准备调用selectPrimaryCategory函数...')
|
console.log('鍑嗗璋冪敤selectPrimaryCategory鍑芥暟...')
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
} else {
|
} else {
|
||||||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
console.log('褰撳墠鍒嗙被宸茬粡鏄洰鏍囧垎绫伙紝浣嗗彲鑳界敤鎴锋兂瑕佸埛鏂伴〉闈?)
|
||||||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '鐩爣鍒嗙被:', categoryId)
|
||||||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
// 鍗充娇鍒嗙被鐩稿悓锛屼篃閲嶆柊鍔犺浇鏁版嵁锛岀‘淇濇暟鎹槸鏈€鏂扮殑
|
||||||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
// 娣诲姞涓€涓皬鐨勫欢杩燂紝纭繚椤甸潰瀹屽叏鏄剧ず鍚庡啀鏇存柊鏁版嵁
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ onShow中未找到分类参数')
|
console.log('鈿狅笍 onShow涓湭鎵惧埌鍒嗙被鍙傛暟')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== category页面onShow执行完成 ===')
|
console.log('=== category椤甸潰onShow鎵ц瀹屾垚 ===')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 添加到购物车
|
// 娣诲姞鍒拌喘鐗╄溅
|
||||||
async function addToCart(product: Product): Promise<void> {
|
async function addToCart(product: Product): Promise<void> {
|
||||||
uni.showLoading({ title: '检查商品...' })
|
uni.showLoading({ title: '妫€鏌ュ晢鍝?..' })
|
||||||
try {
|
try {
|
||||||
const pid = (product.id ?? '').toString()
|
const pid = (product.id ?? '').toString()
|
||||||
const merchantId = product.merchant_id ?? ''
|
const merchantId = product.merchant_id ?? ''
|
||||||
if (pid === '') {
|
if (pid === '') {
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
uni.showToast({ title: '商品无效', icon: 'none' })
|
uni.showToast({ title: '鍟嗗搧鏃犳晥', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查商品是否有SKU
|
// 妫€鏌ュ晢鍝佹槸鍚︽湁SKU
|
||||||
const skus = await supabaseService.getProductSkus(pid)
|
const skus = await supabaseService.getProductSkus(pid)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
|
|
||||||
if (skus.length > 0) {
|
if (skus.length > 0) {
|
||||||
// 有规格,提示并跳转到商品详情页选择规格
|
// 鏈夎鏍硷紝鎻愮ず骞惰烦杞埌鍟嗗搧璇︽儏椤甸€夋嫨瑙勬牸
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请选择规格',
|
title: '璇烽€夋嫨瑙勬牸',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -479,31 +479,31 @@ async function addToCart(product: Product): Promise<void> {
|
|||||||
})
|
})
|
||||||
}, 500)
|
}, 500)
|
||||||
} else {
|
} else {
|
||||||
// 无规格,直接加入购物车
|
// 鏃犺鏍硷紝鐩存帴鍔犲叆璐墿杞?
|
||||||
uni.showLoading({ title: '添加中...' })
|
uni.showLoading({ title: '娣诲姞涓?..' })
|
||||||
const success = await supabaseService.addToCart(pid, 1, '', merchantId)
|
const success = await supabaseService.addToCart(pid, 1, '', merchantId)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
if (success) {
|
if (success) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已添加到购物车',
|
title: '宸叉坊鍔犲埌璐墿杞?,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
cartCount.value++
|
cartCount.value++
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '添加失败,请先登录',
|
title: '娣诲姞澶辫触锛岃鍏堢櫥褰?,
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('添加到购物车异常', e)
|
console.error('娣诲姞鍒拌喘鐗╄溅寮傚父', e)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
uni.showToast({ title: '操作失败', icon: 'none' })
|
uni.showToast({ title: '鎿嶄綔澶辫触', icon: 'none' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导航函数
|
// 瀵艰埅鍑芥暟
|
||||||
function navigateToSearch(): void { uni.navigateTo({ url: '/pages/mall/consumer/search' }) }
|
function navigateToSearch(): void { uni.navigateTo({ url: '/pages/mall/consumer/search' }) }
|
||||||
function navigateToCart(): void { uni.navigateTo({ url: '/pages/mall/consumer/cart' }) }
|
function navigateToCart(): void { uni.navigateTo({ url: '/pages/mall/consumer/cart' }) }
|
||||||
function navigateToProduct(product: Product): void {
|
function navigateToProduct(product: Product): void {
|
||||||
@@ -519,43 +519,43 @@ function navigateToProduct(product: Product): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 相机功能
|
// 鐩告満鍔熻兘
|
||||||
function onCamera(): void {
|
function onCamera(): void {
|
||||||
uni.chooseImage({
|
uni.chooseImage({
|
||||||
count: 1,
|
count: 1,
|
||||||
sourceType: ['camera'],
|
sourceType: ['camera'],
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('相机拍摄成功:', res.tempFilePaths[0])
|
console.log('鐩告満鎷嶆憚鎴愬姛:', res.tempFilePaths[0])
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已拍摄,正在识别...',
|
title: '宸叉媿鎽勶紝姝e湪璇嗗埆...',
|
||||||
icon: 'loading'
|
icon: 'loading'
|
||||||
})
|
})
|
||||||
// 这里可以添加后续的识别逻辑
|
// 杩欓噷鍙互娣诲姞鍚庣画鐨勮瘑鍒€昏緫
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '识别成功',
|
title: '璇嗗埆鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('相机调用失败:', err)
|
console.error('鐩告満璋冪敤澶辫触:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扫码功能
|
// 鎵爜鍔熻兘
|
||||||
function onScan(): void {
|
function onScan(): void {
|
||||||
uni.scanCode({
|
uni.scanCode({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('扫码成功:', res)
|
console.log('鎵爜鎴愬姛:', res)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '扫码成功: ' + res.result,
|
title: '鎵爜鎴愬姛: ' + res.result,
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('扫码失败:', err)
|
console.error('鎵爜澶辫触:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -571,7 +571,7 @@ function onScan(): void {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
||||||
}
|
}
|
||||||
/* 搜索栏 */
|
/* 鎼滅储鏍?*/
|
||||||
.search-bar {
|
.search-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -582,13 +582,13 @@ function onScan(): void {
|
|||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏占位 */
|
/* 瀵艰埅鏍忓崰浣?*/
|
||||||
.navbar-placeholder {
|
.navbar-placeholder {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索栏 */
|
/* 鎼滅储鏍?*/
|
||||||
/* 导航栏搜索框容器内边距调整 */
|
/* 瀵艰埅鏍忔悳绱㈡瀹瑰櫒鍐呰竟璺濊皟鏁?*/
|
||||||
.search-container {
|
.search-container {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -601,12 +601,12 @@ function onScan(): void {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索框 hover 效果 */
|
/* 鎼滅储妗?hover 鏁堟灉 */
|
||||||
.search-box:hover {
|
.search-box:hover {
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏搜索框容器内边距调整 */
|
/* 瀵艰埅鏍忔悳绱㈡瀹瑰櫒鍐呰竟璺濊皟鏁?*/
|
||||||
.search-box {
|
.search-box {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
@@ -631,7 +631,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-inner-search-text {
|
.nav-inner-search-text {
|
||||||
font-size: 12px; /* 字体稍微变小 */
|
font-size: 12px; /* 瀛椾綋绋嶅井鍙樺皬 */
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
@@ -664,7 +664,7 @@ function onScan(): void {
|
|||||||
border-right-width: 1px;
|
border-right-width: 1px;
|
||||||
border-right-style: solid;
|
border-right-style: solid;
|
||||||
border-right-color: #ddd;
|
border-right-color: #ddd;
|
||||||
border-right: 1px solid #ddd; /* 修复UVUE样式 */
|
border-right: 1px solid #ddd; /* 淇UVUE鏍峰紡 */
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,23 +672,23 @@ function onScan(): void {
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索按钮高度微调 */
|
/* 鎼滅储鎸夐挳楂樺害寰皟 */
|
||||||
.nav-inner-search-btn {
|
.nav-inner-search-btn {
|
||||||
padding: 0 12px; /* 减小内边距 */
|
padding: 0 12px; /* 鍑忓皬鍐呰竟璺?*/
|
||||||
background-color: #87CEEB; /* 天空蓝 */
|
background-color: #87CEEB; /* 澶╃┖钃?*/
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 24px; /* 随搜索框高度减小而减小 */
|
height: 24px; /* 闅忔悳绱㈡楂樺害鍑忓皬鑰屽噺灏?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.cart-badge {
|
.cart-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5px;
|
top: -5px;
|
||||||
right: -5px;
|
right: -5px;
|
||||||
background: #FF5722;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
min-width: 18px;
|
min-width: 18px;
|
||||||
@@ -701,7 +701,7 @@ function onScan(): void {
|
|||||||
border: 2px solid #4CAF50;
|
border: 2px solid #4CAF50;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分类内容区 */
|
/* 鍒嗙被鍐呭鍖?*/
|
||||||
.category-content {
|
.category-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
@@ -715,10 +715,10 @@ function onScan(): void {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 左侧一级分类 */
|
/* 宸︿晶涓€绾у垎绫?*/
|
||||||
.primary-category {
|
.primary-category {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 100%; /* 占满父容器高度 */
|
height: 100%; /* 鍗犳弧鐖跺鍣ㄩ珮搴?*/
|
||||||
margin-right: 20px; /* gap replacement */
|
margin-right: 20px; /* gap replacement */
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -729,7 +729,7 @@ function onScan(): void {
|
|||||||
|
|
||||||
.primary-item {
|
.primary-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; /* 图标和文字垂直排列 */
|
flex-direction: column; /* 鍥炬爣鍜屾枃瀛楀瀭鐩存帓鍒?*/
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 12px 8px;
|
padding: 12px 8px;
|
||||||
@@ -742,7 +742,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.primary-item:hover {
|
.primary-item:hover {
|
||||||
transform: translateY(-2px); /* 悬停时向上浮动 */
|
transform: translateY(-2px); /* 鎮仠鏃跺悜涓婃诞鍔?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-item.active {
|
.primary-item.active {
|
||||||
@@ -753,7 +753,7 @@ function onScan(): void {
|
|||||||
.primary-icon {
|
.primary-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
margin-right: 0; /* 移除右边距 */
|
margin-right: 0; /* 绉婚櫎鍙宠竟璺?*/
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/* display: block; removed for uniapp-x support */
|
/* display: block; removed for uniapp-x support */
|
||||||
}
|
}
|
||||||
@@ -764,11 +764,11 @@ function onScan(): void {
|
|||||||
/* display: block; removed for uniapp-x support */
|
/* display: block; removed for uniapp-x support */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 右侧内容区 */
|
/* 鍙充晶鍐呭鍖?*/
|
||||||
.product-content {
|
.product-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%; /* 占满父容器高度 */
|
height: 100%; /* 鍗犳弧鐖跺鍣ㄩ珮搴?*/
|
||||||
padding: 0; /* 移除内边距,交给内部元素 */
|
padding: 0; /* 绉婚櫎鍐呰竟璺濓紝浜ょ粰鍐呴儴鍏冪礌 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-header {
|
.category-header {
|
||||||
@@ -792,7 +792,7 @@ function onScan(): void {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 商品网格 */
|
/* 鍟嗗搧缃戞牸 */
|
||||||
.product-grid {
|
.product-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -898,7 +898,7 @@ function onScan(): void {
|
|||||||
margin-right: 6px; /* gap replacement */
|
margin-right: 6px; /* gap replacement */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 绌虹姸鎬?*/
|
||||||
.empty-state {
|
.empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -926,7 +926,7 @@ function onScan(): void {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加载更多 */
|
/* 鍔犺浇鏇村 */
|
||||||
.load-more {
|
.load-more {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
@@ -941,9 +941,9 @@ function onScan(): void {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 响应式设计 ===== */
|
/* ===== 鍝嶅簲寮忚璁?===== */
|
||||||
|
|
||||||
/* 小屏手机 (小于414px) */
|
/* 灏忓睆鎵嬫満 (灏忎簬414px) */
|
||||||
@media screen and (max-width: 414px) {
|
@media screen and (max-width: 414px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
@@ -961,19 +961,19 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.primary-category {
|
.primary-category {
|
||||||
width: 80px; /* 减小宽度 */
|
width: 80px; /* 鍑忓皬瀹藉害 */
|
||||||
/* display: flex; 移除flex布局,保持默认 */
|
/* display: flex; 绉婚櫎flex甯冨眬锛屼繚鎸侀粯璁?*/
|
||||||
/* flex-wrap: wrap; 移除换行 */
|
/* flex-wrap: wrap; 绉婚櫎鎹㈣ */
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
margin-right: 10px; /* Gap replacement */
|
margin-right: 10px; /* Gap replacement */
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-item {
|
.primary-item {
|
||||||
/* width: calc(25% - 8px); 移除百分比宽度 */
|
/* width: calc(25% - 8px); 绉婚櫎鐧惧垎姣斿搴?*/
|
||||||
width: auto; /* 恢复自动宽度 */
|
width: auto; /* 鎭㈠鑷姩瀹藉害 */
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 8px 4px;
|
padding: 8px 4px;
|
||||||
/* text-align: center; 已经在通用样式中设置 */
|
/* text-align: center; 宸茬粡鍦ㄩ€氱敤鏍峰紡涓缃?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-icon {
|
.primary-icon {
|
||||||
@@ -989,7 +989,7 @@ function onScan(): void {
|
|||||||
.product-grid {
|
.product-grid {
|
||||||
/* grid-template-columns: repeat(2, 1fr); REMOVED */
|
/* grid-template-columns: repeat(2, 1fr); REMOVED */
|
||||||
/* gap: 8px; REMOVED */
|
/* gap: 8px; REMOVED */
|
||||||
padding: 0 4px 20px 4px; /* 增加底部内边距 */
|
padding: 0 4px 20px 4px; /* 澧炲姞搴曢儴鍐呰竟璺?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card {
|
.product-card {
|
||||||
@@ -997,12 +997,12 @@ function onScan(): void {
|
|||||||
margin: 1%;
|
margin: 1%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 手机端商品卡片极简模式 - 仿照主页样式 */
|
/* 鎵嬫満绔晢鍝佸崱鐗囨瀬绠€妯″紡 - 浠跨収涓婚〉鏍峰紡 */
|
||||||
.product-spec,
|
.product-spec,
|
||||||
.manufacturer,
|
.manufacturer,
|
||||||
.original-price,
|
.original-price,
|
||||||
.sales-info,
|
.sales-info,
|
||||||
.product-badge { /* 分类页也隐藏角标,保持整洁 */
|
.product-badge { /* 鍒嗙被椤典篃闅愯棌瑙掓爣锛屼繚鎸佹暣娲?*/
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1011,7 +1011,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-image {
|
.product-image {
|
||||||
height: 100px; /* 由于分类页右侧空间更窄,图片高度设得更小一点 */
|
height: 100px; /* 鐢变簬鍒嗙被椤靛彸渚х┖闂存洿绐勶紝鍥剧墖楂樺害璁惧緱鏇村皬涓€鐐?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-name {
|
.product-name {
|
||||||
@@ -1043,7 +1043,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-meta {
|
.product-meta {
|
||||||
display: none; /* 隐藏整个元数据行 */
|
display: none; /* 闅愯棌鏁翠釜鍏冩暟鎹 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
@@ -1060,7 +1060,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 中屏手机/小平板 (415px-768px) */
|
/* 涓睆鎵嬫満/灏忓钩鏉?(415px-768px) */
|
||||||
@media screen and (min-width: 415px) and (max-width: 768px) {
|
@media screen and (min-width: 415px) and (max-width: 768px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -1073,7 +1073,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 平板设备 (769px-1024px) */
|
/* 骞虫澘璁惧 (769px-1024px) */
|
||||||
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -1086,7 +1086,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 桌面端 (1025px以上) */
|
/* 妗岄潰绔?(1025px浠ヤ笂) */
|
||||||
@media screen and (min-width: 1025px) {
|
@media screen and (min-width: 1025px) {
|
||||||
.search-container {
|
.search-container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
@@ -1163,7 +1163,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 大桌面端 (1400px以上) */
|
/* 澶ф闈㈢ (1400px浠ヤ笂) */
|
||||||
@media screen and (min-width: 1400px) {
|
@media screen and (min-width: 1400px) {
|
||||||
.category-content {
|
.category-content {
|
||||||
max-width: 1600px;
|
max-width: 1600px;
|
||||||
@@ -1244,3 +1244,5 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="category-page">
|
<view class="category-page">
|
||||||
<!-- 顶部搜索栏 -->
|
<!-- 顶部搜索栏 -->
|
||||||
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
@@ -727,9 +727,9 @@ function onScan(): void {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: #4CAF50;
|
background-color: #ff5000;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(255, 80, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏占位 */
|
/* 导航栏占位 */
|
||||||
@@ -848,7 +848,7 @@ function onScan(): void {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
border: 2px solid #4CAF50;
|
border: 2px solid #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分类内容区 */
|
/* 分类内容区 */
|
||||||
@@ -967,7 +967,7 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sub-category-item.active {
|
.sub-category-item.active {
|
||||||
background: #4CAF50;
|
background: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-category-item.active .sub-category-name {
|
.sub-category-item.active .sub-category-name {
|
||||||
@@ -1076,7 +1076,7 @@ function onScan(): void {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* gap: 6px; */
|
/* gap: 6px; */
|
||||||
background: #4CAF50;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -1107,7 +1107,7 @@ function onScan(): void {
|
|||||||
|
|
||||||
.empty-icon {
|
.empty-icon {
|
||||||
font-size: 60px;
|
font-size: 60px;
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1464,3 +1464,4 @@ function onScan(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="category-page">
|
<view class="category-page">
|
||||||
<!-- 顶部搜索栏 -->
|
<!-- 椤堕儴鎼滅储鏍?-->
|
||||||
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="search-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view class="search-container">
|
<view class="search-container">
|
||||||
<view class="search-box" @click="navigateToSearch" :style="{ height: '30px' }">
|
<view class="search-box" @click="navigateToSearch" :style="{ height: '30px' }">
|
||||||
<!-- 模拟输入框 -->
|
<!-- 妯℃嫙杈撳叆妗?-->
|
||||||
<text class="search-placeholder">请输入药品名称、症状或品牌</text>
|
<text class="search-placeholder">璇疯緭鍏ヨ嵂鍝佸悕绉般€佺棁鐘舵垨鍝佺墝</text>
|
||||||
|
|
||||||
<!-- 扫码图标 -->
|
<!-- 鎵爜鍥炬爣 -->
|
||||||
<view class="nav-icon-btn" @click.stop="onScan">
|
<view class="nav-icon-btn" @click.stop="onScan">
|
||||||
<text class="nav-icon">🔳</text>
|
<text class="nav-icon">馃敵</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 相机图标 -->
|
<!-- 鐩告満鍥炬爣 -->
|
||||||
<view class="nav-camera-btn" @click.stop="onCamera">
|
<view class="nav-camera-btn" @click.stop="onCamera">
|
||||||
<text class="nav-camera-icon">📷</text>
|
<text class="nav-camera-icon">馃摲</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 搜索按钮 -->
|
<!-- 鎼滅储鎸夐挳 -->
|
||||||
<view class="nav-inner-search-btn" :style="{ height: '22px' }">
|
<view class="nav-inner-search-btn" :style="{ height: '22px' }">
|
||||||
<text class="nav-inner-search-text">搜索</text>
|
<text class="nav-inner-search-text">鎼滅储</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 分类内容区 -->
|
<!-- 鍒嗙被鍐呭鍖?-->
|
||||||
<view
|
<view
|
||||||
class="category-content"
|
class="category-content"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
height: `calc(100vh - ${statusBarHeight + headerHeight + 10}px)`
|
height: `calc(100vh - ${statusBarHeight + headerHeight + 10}px)`
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- 左侧一级分类 -->
|
<!-- 宸︿晶涓€绾у垎绫?-->
|
||||||
<scroll-view scroll-y class="primary-category">
|
<scroll-view scroll-y class="primary-category">
|
||||||
<view
|
<view
|
||||||
v-for="item in primaryCategories"
|
v-for="item in primaryCategories"
|
||||||
@@ -47,15 +47,15 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 右侧商品列表 -->
|
<!-- 鍙充晶鍟嗗搧鍒楄〃 -->
|
||||||
<scroll-view scroll-y class="product-content">
|
<scroll-view scroll-y class="product-content">
|
||||||
<!-- 分类标题 -->
|
<!-- 鍒嗙被鏍囬 -->
|
||||||
<view class="category-header">
|
<view class="category-header">
|
||||||
<text class="category-title">{{ currentCategoryName }}</text>
|
<text class="category-title">{{ currentCategoryName }}</text>
|
||||||
<text class="category-desc">{{ currentCategoryDesc }}</text>
|
<text class="category-desc">{{ currentCategoryDesc }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品网格 -->
|
<!-- 鍟嗗搧缃戞牸 -->
|
||||||
<view v-if="productList.length > 0" class="product-grid">
|
<view v-if="productList.length > 0" class="product-grid">
|
||||||
<view
|
<view
|
||||||
v-for="product in productList"
|
v-for="product in productList"
|
||||||
@@ -75,34 +75,34 @@
|
|||||||
|
|
||||||
<view class="price-section">
|
<view class="price-section">
|
||||||
<view class="current-price">
|
<view class="current-price">
|
||||||
<text class="price-symbol">¥</text>
|
<text class="price-symbol">楼</text>
|
||||||
<text class="price-value">{{ product.price }}</text>
|
<text class="price-value">{{ product.price }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="original-price" v-if="product.originalPrice > product.price">
|
<text class="original-price" v-if="product.originalPrice > product.price">
|
||||||
¥{{ product.originalPrice }}
|
楼{{ product.originalPrice }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="product-meta">
|
<view class="product-meta">
|
||||||
<text class="manufacturer">{{ product.manufacturer }}</text>
|
<text class="manufacturer">{{ product.manufacturer }}</text>
|
||||||
<view class="sales-info">
|
<view class="sales-info">
|
||||||
<text class="sales-count">已售{{ product.sales }}</text>
|
<text class="sales-count">宸插敭{{ product.sales }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 绌虹姸鎬?-->
|
||||||
<view v-else class="empty-state">
|
<view v-else class="empty-state">
|
||||||
<text class="empty-icon">💊</text>
|
<text class="empty-icon">馃拪</text>
|
||||||
<text class="empty-text">暂无相关药品</text>
|
<text class="empty-text">鏆傛棤鐩稿叧鑽搧</text>
|
||||||
<text class="empty-desc">该分类下暂无商品,敬请期待</text>
|
<text class="empty-desc">璇ュ垎绫讳笅鏆傛棤鍟嗗搧锛屾暚璇锋湡寰?/text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载更多提示 -->
|
<!-- 鍔犺浇鏇村鎻愮ず -->
|
||||||
<view v-if="hasMore" class="load-more">
|
<view v-if="hasMore" class="load-more">
|
||||||
<text class="load-text">上拉加载更多</text>
|
<text class="load-text">涓婃媺鍔犺浇鏇村</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
@@ -114,49 +114,49 @@ import { ref, onMounted } from 'vue'
|
|||||||
import supabaseService from '@/utils/supabaseService.uts'
|
import supabaseService from '@/utils/supabaseService.uts'
|
||||||
import type { Category, Product } from '@/utils/supabaseService.uts'
|
import type { Category, Product } from '@/utils/supabaseService.uts'
|
||||||
|
|
||||||
// 响应式数据
|
// 鍝嶅簲寮忔暟鎹?
|
||||||
const statusBarHeight = ref(0)
|
const statusBarHeight = ref(0)
|
||||||
const headerHeight = ref(44) // 默认头部高度
|
const headerHeight = ref(44) // 榛樿澶撮儴楂樺害
|
||||||
const primaryCategories = ref<Category[]>([])
|
const primaryCategories = ref<Category[]>([])
|
||||||
const productList = ref<Product[]>([])
|
const productList = ref<Product[]>([])
|
||||||
const activePrimary = ref<string>('')
|
const activePrimary = ref<string>('')
|
||||||
const cartCount = ref(3)
|
const cartCount = ref(3)
|
||||||
const hasMore = ref(true)
|
const hasMore = ref(true)
|
||||||
|
|
||||||
// 获取当前分类信息
|
// 鑾峰彇褰撳墠鍒嗙被淇℃伅
|
||||||
const currentCategoryName = ref('')
|
const currentCategoryName = ref('')
|
||||||
const currentCategoryDesc = ref('')
|
const currentCategoryDesc = ref('')
|
||||||
|
|
||||||
// 页面参数
|
// 椤甸潰鍙傛暟
|
||||||
const pageParams = ref<any>({})
|
const pageParams = ref<any>({})
|
||||||
|
|
||||||
|
|
||||||
// 生命周期
|
// 鐢熷懡鍛ㄦ湡
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
await loadCategories()
|
await loadCategories()
|
||||||
await loadProducts()
|
await loadProducts()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 添加加载分类的方法
|
// 娣诲姞鍔犺浇鍒嗙被鐨勬柟娉?
|
||||||
const loadCategories = async () => {
|
const loadCategories = async () => {
|
||||||
const categories = await supabaseService.getCategories()
|
const categories = await supabaseService.getCategories()
|
||||||
if (categories.length > 0) {
|
if (categories.length > 0) {
|
||||||
primaryCategories.value = categories
|
primaryCategories.value = categories
|
||||||
// 设置默认选中第一个分类
|
// 璁剧疆榛樿閫変腑绗竴涓垎绫?
|
||||||
if (!activePrimary.value && categories[0]) {
|
if (!activePrimary.value && categories[0]) {
|
||||||
activePrimary.value = categories[0].id
|
activePrimary.value = categories[0].id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载商品数据
|
// 鍔犺浇鍟嗗搧鏁版嵁
|
||||||
const loadProducts = async () => {
|
const loadProducts = async () => {
|
||||||
if (activePrimary.value) {
|
if (activePrimary.value) {
|
||||||
const response = await supabaseService.getProductsByCategory(activePrimary.value)
|
const response = await supabaseService.getProductsByCategory(activePrimary.value)
|
||||||
productList.value = response.data
|
productList.value = response.data
|
||||||
hasMore.value = response.hasmore
|
hasMore.value = response.hasmore
|
||||||
|
|
||||||
// 更新当前分类信息
|
// 鏇存柊褰撳墠鍒嗙被淇℃伅
|
||||||
const category = primaryCategories.value.find(cat => cat.id === activePrimary.value)
|
const category = primaryCategories.value.find(cat => cat.id === activePrimary.value)
|
||||||
if (category) {
|
if (category) {
|
||||||
currentCategoryName.value = category.name
|
currentCategoryName.value = category.name
|
||||||
@@ -165,130 +165,130 @@ const loadProducts = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 页面加载时处理参数 - 这是处理分类切换的主要入口
|
// 椤甸潰鍔犺浇鏃跺鐞嗗弬鏁?- 杩欐槸澶勭悊鍒嗙被鍒囨崲鐨勪富瑕佸叆鍙?
|
||||||
onLoad((options: any) => {
|
onLoad((options: any) => {
|
||||||
console.log('=== category页面onLoad被调用 ===')
|
console.log('=== category椤甸潰onLoad琚皟鐢?===')
|
||||||
console.log('页面加载时间:', Date.now())
|
console.log('椤甸潰鍔犺浇鏃堕棿:', Date.now())
|
||||||
console.log('传入的options参数:', options)
|
console.log('浼犲叆鐨刼ptions鍙傛暟:', options)
|
||||||
console.log('当前活动分类:', activePrimary.value)
|
console.log('褰撳墠娲诲姩鍒嗙被:', activePrimary.value)
|
||||||
|
|
||||||
let categoryId = ''
|
let categoryId = ''
|
||||||
let categoryName = ''
|
let categoryName = ''
|
||||||
|
|
||||||
// 首先检查传入的options参数
|
// 棣栧厛妫€鏌ヤ紶鍏ョ殑options鍙傛暟
|
||||||
if (options && options.categoryId) {
|
if (options && options.categoryId) {
|
||||||
categoryId = options.categoryId
|
categoryId = options.categoryId
|
||||||
categoryName = options.name || ''
|
categoryName = options.name || ''
|
||||||
console.log('✅ onLoad中找到分类参数:', categoryId, categoryName)
|
console.log('鉁?onLoad涓壘鍒板垎绫诲弬鏁?', categoryId, categoryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果options中没有,尝试从getCurrentPages()获取
|
// 濡傛灉options涓病鏈夛紝灏濊瘯浠巊etCurrentPages()鑾峰彇
|
||||||
if (!categoryId) {
|
if (!categoryId) {
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
if (pages.length > 0) {
|
if (pages.length > 0) {
|
||||||
const currentPage = pages[pages.length - 1]
|
const currentPage = pages[pages.length - 1]
|
||||||
const pageOptions = currentPage.options || {}
|
const pageOptions = currentPage.options || {}
|
||||||
console.log('从getCurrentPages()获取参数:', pageOptions)
|
console.log('浠巊etCurrentPages()鑾峰彇鍙傛暟:', pageOptions)
|
||||||
|
|
||||||
if (pageOptions.categoryId) {
|
if (pageOptions.categoryId) {
|
||||||
categoryId = pageOptions.categoryId
|
categoryId = pageOptions.categoryId
|
||||||
categoryName = pageOptions.name || ''
|
categoryName = pageOptions.name || ''
|
||||||
console.log('✅ 从getCurrentPages()找到分类参数:', categoryId, categoryName)
|
console.log('鉁?浠巊etCurrentPages()鎵惧埌鍒嗙被鍙傛暟:', categoryId, categoryName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有找到分类ID,则选中对应的分类
|
// 濡傛灉鏈夋壘鍒板垎绫籌D锛屽垯閫変腑瀵瑰簲鐨勫垎绫?
|
||||||
if (categoryId) {
|
if (categoryId) {
|
||||||
console.log('✅ 准备选中分类:', categoryId)
|
console.log('鉁?鍑嗗閫変腑鍒嗙被:', categoryId)
|
||||||
console.log('分类名称:', categoryName || '未指定')
|
console.log('鍒嗙被鍚嶇О:', categoryName || '鏈寚瀹?)
|
||||||
|
|
||||||
// 检查是否需要更新分类
|
// 妫€鏌ユ槸鍚﹂渶瑕佹洿鏂板垎绫?
|
||||||
if (activePrimary.value !== categoryId) {
|
if (activePrimary.value !== categoryId) {
|
||||||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '涓庣洰鏍囧垎绫?', categoryId, '涓嶅悓锛岄渶瑕佹洿鏂?)
|
||||||
console.log('准备调用selectPrimaryCategory函数...')
|
console.log('鍑嗗璋冪敤selectPrimaryCategory鍑芥暟...')
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
} else {
|
} else {
|
||||||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
console.log('褰撳墠鍒嗙被宸茬粡鏄洰鏍囧垎绫伙紝浣嗗彲鑳界敤鎴锋兂瑕佸埛鏂伴〉闈?)
|
||||||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '鐩爣鍒嗙被:', categoryId)
|
||||||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
// 鍗充娇鍒嗙被鐩稿悓锛屼篃閲嶆柊鍔犺浇鏁版嵁锛岀‘淇濇暟鎹槸鏈€鏂扮殑
|
||||||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
// 娣诲姞涓€涓皬鐨勫欢杩燂紝纭繚椤甸潰瀹屽叏鏄剧ず鍚庡啀鏇存柊鏁版嵁
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ onLoad中未找到分类参数,将使用从数据库加载的第一个分类')
|
console.log('鈿狅笍 onLoad涓湭鎵惧埌鍒嗙被鍙傛暟锛屽皢浣跨敤浠庢暟鎹簱鍔犺浇鐨勭涓€涓垎绫?)
|
||||||
// 不再使用硬编码的默认分类,loadCategories 会设置第一个分类
|
// 涓嶅啀浣跨敤纭紪鐮佺殑榛樿鍒嗙被锛宭oadCategories 浼氳缃涓€涓垎绫?
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== category页面onLoad执行完成 ===')
|
console.log('=== category椤甸潰onLoad鎵ц瀹屾垚 ===')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页面显示时也检查参数,确保从其他页面返回时能正确显示
|
// 椤甸潰鏄剧ず鏃朵篃妫€鏌ュ弬鏁帮紝纭繚浠庡叾浠栭〉闈㈣繑鍥炴椂鑳芥纭樉绀?
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
console.log('=== category页面onShow被调用 ===')
|
console.log('=== category椤甸潰onShow琚皟鐢?===')
|
||||||
console.log('页面显示时间:', Date.now())
|
console.log('椤甸潰鏄剧ず鏃堕棿:', Date.now())
|
||||||
console.log('当前活动分类:', activePrimary.value)
|
console.log('褰撳墠娲诲姩鍒嗙被:', activePrimary.value)
|
||||||
|
|
||||||
// 在onShow中,我们也需要检查是否有新的参数
|
// 鍦╫nShow涓紝鎴戜滑涔熼渶瑕佹鏌ユ槸鍚︽湁鏂扮殑鍙傛暟
|
||||||
// 因为当从主页再次点击分类跳转过来时,可能不会触发onLoad
|
// 鍥犱负褰撲粠涓婚〉鍐嶆鐐瑰嚮鍒嗙被璺宠浆杩囨潵鏃讹紝鍙兘涓嶄細瑙﹀彂onLoad
|
||||||
// 而是触发onShow
|
// 鑰屾槸瑙﹀彂onShow
|
||||||
|
|
||||||
// 获取当前页面实例和参数
|
// 鑾峰彇褰撳墠椤甸潰瀹炰緥鍜屽弬鏁?
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
if (pages.length > 0) {
|
if (pages.length > 0) {
|
||||||
const currentPage = pages[pages.length - 1]
|
const currentPage = pages[pages.length - 1]
|
||||||
const pageOptions = currentPage.options || {}
|
const pageOptions = currentPage.options || {}
|
||||||
console.log('onShow中获取参数:', pageOptions)
|
console.log('onShow涓幏鍙栧弬鏁?', pageOptions)
|
||||||
|
|
||||||
// 检查是否有分类参数
|
// 妫€鏌ユ槸鍚︽湁鍒嗙被鍙傛暟
|
||||||
if (pageOptions.categoryId) {
|
if (pageOptions.categoryId) {
|
||||||
const categoryId = pageOptions.categoryId
|
const categoryId = pageOptions.categoryId
|
||||||
const categoryName = pageOptions.name || ''
|
const categoryName = pageOptions.name || ''
|
||||||
|
|
||||||
console.log('✅ onShow中找到分类参数:', categoryId, categoryName)
|
console.log('鉁?onShow涓壘鍒板垎绫诲弬鏁?', categoryId, categoryName)
|
||||||
console.log('URL中的时间戳参数:', pageOptions.timestamp)
|
console.log('URL涓殑鏃堕棿鎴冲弬鏁?', pageOptions.timestamp)
|
||||||
console.log('URL中的随机参数:', pageOptions.random)
|
console.log('URL涓殑闅忔満鍙傛暟:', pageOptions.random)
|
||||||
|
|
||||||
// 检查是否需要更新分类
|
// 妫€鏌ユ槸鍚﹂渶瑕佹洿鏂板垎绫?
|
||||||
if (activePrimary.value !== categoryId) {
|
if (activePrimary.value !== categoryId) {
|
||||||
console.log('当前分类:', activePrimary.value, '与目标分类:', categoryId, '不同,需要更新')
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '涓庣洰鏍囧垎绫?', categoryId, '涓嶅悓锛岄渶瑕佹洿鏂?)
|
||||||
console.log('准备调用selectPrimaryCategory函数...')
|
console.log('鍑嗗璋冪敤selectPrimaryCategory鍑芥暟...')
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
} else {
|
} else {
|
||||||
console.log('当前分类已经是目标分类,但可能用户想要刷新页面')
|
console.log('褰撳墠鍒嗙被宸茬粡鏄洰鏍囧垎绫伙紝浣嗗彲鑳界敤鎴锋兂瑕佸埛鏂伴〉闈?)
|
||||||
console.log('当前分类:', activePrimary.value, '目标分类:', categoryId)
|
console.log('褰撳墠鍒嗙被:', activePrimary.value, '鐩爣鍒嗙被:', categoryId)
|
||||||
// 即使分类相同,也重新加载数据,确保数据是最新的
|
// 鍗充娇鍒嗙被鐩稿悓锛屼篃閲嶆柊鍔犺浇鏁版嵁锛岀‘淇濇暟鎹槸鏈€鏂扮殑
|
||||||
// 添加一个小的延迟,确保页面完全显示后再更新数据
|
// 娣诲姞涓€涓皬鐨勫欢杩燂紝纭繚椤甸潰瀹屽叏鏄剧ず鍚庡啀鏇存柊鏁版嵁
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
selectPrimaryCategory(categoryId)
|
selectPrimaryCategory(categoryId)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ onShow中未找到分类参数')
|
console.log('鈿狅笍 onShow涓湭鎵惧埌鍒嗙被鍙傛暟')
|
||||||
console.log('尝试从URL中解析参数...')
|
console.log('灏濊瘯浠嶶RL涓В鏋愬弬鏁?..')
|
||||||
|
|
||||||
// 尝试从当前页面的URL中解析参数
|
// 灏濊瘯浠庡綋鍓嶉〉闈㈢殑URL涓В鏋愬弬鏁?
|
||||||
const currentUrl = currentPage.route || ''
|
const currentUrl = currentPage.route || ''
|
||||||
console.log('当前页面路由:', currentUrl)
|
console.log('褰撳墠椤甸潰璺敱:', currentUrl)
|
||||||
|
|
||||||
// 如果URL中有查询参数,尝试解析
|
// 濡傛灉URL涓湁鏌ヨ鍙傛暟锛屽皾璇曡В鏋?
|
||||||
if (currentPage.$page && currentPage.$page.fullPath) {
|
if (currentPage.$page && currentPage.$page.fullPath) {
|
||||||
const fullPath = currentPage.$page.fullPath
|
const fullPath = currentPage.$page.fullPath
|
||||||
console.log('完整路径:', fullPath)
|
console.log('瀹屾暣璺緞:', fullPath)
|
||||||
|
|
||||||
// 尝试解析查询参数
|
// 灏濊瘯瑙f瀽鏌ヨ鍙傛暟
|
||||||
const queryIndex = fullPath.indexOf('?')
|
const queryIndex = fullPath.indexOf('?')
|
||||||
if (queryIndex > -1) {
|
if (queryIndex > -1) {
|
||||||
const queryString = fullPath.substring(queryIndex + 1)
|
const queryString = fullPath.substring(queryIndex + 1)
|
||||||
console.log('查询字符串:', queryString)
|
console.log('鏌ヨ瀛楃涓?', queryString)
|
||||||
|
|
||||||
// 简单解析查询参数
|
// 绠€鍗曡В鏋愭煡璇㈠弬鏁?
|
||||||
const params = new URLSearchParams(queryString)
|
const params = new URLSearchParams(queryString)
|
||||||
const urlCategoryId = params.get('categoryId')
|
const urlCategoryId = params.get('categoryId')
|
||||||
if (urlCategoryId) {
|
if (urlCategoryId) {
|
||||||
console.log('✅ 从URL解析到分类参数:', urlCategoryId)
|
console.log('鉁?浠嶶RL瑙f瀽鍒板垎绫诲弬鏁?', urlCategoryId)
|
||||||
selectPrimaryCategory(urlCategoryId)
|
selectPrimaryCategory(urlCategoryId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,78 +296,78 @@ onShow(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== category页面onShow执行完成 ===')
|
console.log('=== category椤甸潰onShow鎵ц瀹屾垚 ===')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 选择一级分类
|
// 閫夋嫨涓€绾у垎绫?
|
||||||
const selectPrimaryCategory = async (categoryId: string) => {
|
const selectPrimaryCategory = async (categoryId: string) => {
|
||||||
console.log('=== selectPrimaryCategory函数开始执行 ===')
|
console.log('=== selectPrimaryCategory鍑芥暟寮€濮嬫墽琛?===')
|
||||||
console.log('传入的categoryId:', categoryId)
|
console.log('浼犲叆鐨刢ategoryId:', categoryId)
|
||||||
console.log('当前时间:', Date.now())
|
console.log('褰撳墠鏃堕棿:', Date.now())
|
||||||
|
|
||||||
// 验证categoryId是否有效
|
// 楠岃瘉categoryId鏄惁鏈夋晥
|
||||||
if (!categoryId) {
|
if (!categoryId) {
|
||||||
console.error('categoryId为空,尝试使用第一个分类')
|
console.error('categoryId涓虹┖锛屽皾璇曚娇鐢ㄧ涓€涓垎绫?)
|
||||||
if (primaryCategories.value.length > 0) {
|
if (primaryCategories.value.length > 0) {
|
||||||
categoryId = primaryCategories.value[0].id
|
categoryId = primaryCategories.value[0].id
|
||||||
} else {
|
} else {
|
||||||
console.error('没有可用的分类')
|
console.error('娌℃湁鍙敤鐨勫垎绫?)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('验证后的categoryId:', categoryId)
|
console.log('楠岃瘉鍚庣殑categoryId:', categoryId)
|
||||||
console.log('当前activePrimary的值:', activePrimary.value)
|
console.log('褰撳墠activePrimary鐨勫€?', activePrimary.value)
|
||||||
|
|
||||||
// 更新活动分类
|
// 鏇存柊娲诲姩鍒嗙被
|
||||||
activePrimary.value = categoryId
|
activePrimary.value = categoryId
|
||||||
console.log('更新后的activePrimary:', activePrimary.value)
|
console.log('鏇存柊鍚庣殑activePrimary:', activePrimary.value)
|
||||||
|
|
||||||
// 更新当前分类信息
|
// 鏇存柊褰撳墠鍒嗙被淇℃伅
|
||||||
const category = primaryCategories.value.find(cat => cat.id === categoryId)
|
const category = primaryCategories.value.find(cat => cat.id === categoryId)
|
||||||
if (category) {
|
if (category) {
|
||||||
currentCategoryName.value = category.name
|
currentCategoryName.value = category.name
|
||||||
currentCategoryDesc.value = category.description
|
currentCategoryDesc.value = category.description
|
||||||
console.log('✅ 找到分类:', category.name, '描述:', category.description)
|
console.log('鉁?鎵惧埌鍒嗙被:', category.name, '鎻忚堪:', category.description)
|
||||||
} else {
|
} else {
|
||||||
console.error('❌ 未找到分类ID:', categoryId, ',使用第一个分类')
|
console.error('鉂?鏈壘鍒板垎绫籌D:', categoryId, '锛屼娇鐢ㄧ涓€涓垎绫?)
|
||||||
// 如果找不到对应的分类,使用第一个分类
|
// 濡傛灉鎵句笉鍒板搴旂殑鍒嗙被锛屼娇鐢ㄧ涓€涓垎绫?
|
||||||
if (primaryCategories.value.length > 0) {
|
if (primaryCategories.value.length > 0) {
|
||||||
const firstCategory = primaryCategories.value[0]
|
const firstCategory = primaryCategories.value[0]
|
||||||
currentCategoryName.value = firstCategory.name
|
currentCategoryName.value = firstCategory.name
|
||||||
currentCategoryDesc.value = firstCategory.description
|
currentCategoryDesc.value = firstCategory.description
|
||||||
activePrimary.value = firstCategory.id
|
activePrimary.value = firstCategory.id
|
||||||
categoryId = firstCategory.id
|
categoryId = firstCategory.id
|
||||||
console.log('使用默认分类:', firstCategory.name)
|
console.log('浣跨敤榛樿鍒嗙被:', firstCategory.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('准备加载商品数据...')
|
console.log('鍑嗗鍔犺浇鍟嗗搧鏁版嵁...')
|
||||||
|
|
||||||
// 加载对应商品 - 使用 Supabase 服务
|
// 鍔犺浇瀵瑰簲鍟嗗搧 - 浣跨敤 Supabase 鏈嶅姟
|
||||||
const response = await supabaseService.getProductsByCategory(categoryId)
|
const response = await supabaseService.getProductsByCategory(categoryId)
|
||||||
productList.value = response.data
|
productList.value = response.data
|
||||||
hasMore.value = response.hasmore
|
hasMore.value = response.hasmore
|
||||||
|
|
||||||
console.log('✅ 加载商品数据成功')
|
console.log('鉁?鍔犺浇鍟嗗搧鏁版嵁鎴愬姛')
|
||||||
console.log('分类:', categoryId)
|
console.log('鍒嗙被:', categoryId)
|
||||||
console.log('商品数量:', response.data.length)
|
console.log('鍟嗗搧鏁伴噺:', response.data.length)
|
||||||
console.log('商品列表:', response.data)
|
console.log('鍟嗗搧鍒楄〃:', response.data)
|
||||||
|
|
||||||
// 验证数据是否已正确更新
|
// 楠岃瘉鏁版嵁鏄惁宸叉纭洿鏂?
|
||||||
console.log('数据更新验证:')
|
console.log('鏁版嵁鏇存柊楠岃瘉:')
|
||||||
console.log('activePrimary:', activePrimary.value)
|
console.log('activePrimary:', activePrimary.value)
|
||||||
console.log('currentCategoryName:', currentCategoryName.value)
|
console.log('currentCategoryName:', currentCategoryName.value)
|
||||||
console.log('currentCategoryDesc:', currentCategoryDesc.value)
|
console.log('currentCategoryDesc:', currentCategoryDesc.value)
|
||||||
console.log('productList长度:', productList.value.length)
|
console.log('productList闀垮害:', productList.value.length)
|
||||||
|
|
||||||
console.log('=== selectPrimaryCategory函数执行完成 ===')
|
console.log('=== selectPrimaryCategory鍑芥暟鎵ц瀹屾垚 ===')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加到购物车
|
// 娣诲姞鍒拌喘鐗╄溅
|
||||||
const addToCart = (product: any) => {
|
const addToCart = (product: any) => {
|
||||||
// 获取现有购物车数据
|
// 鑾峰彇鐜版湁璐墿杞︽暟鎹?
|
||||||
const cartData = uni.getStorageSync('cart')
|
const cartData = uni.getStorageSync('cart')
|
||||||
let cartItems: any[] = []
|
let cartItems: any[] = []
|
||||||
|
|
||||||
@@ -375,41 +375,41 @@ const addToCart = (product: any) => {
|
|||||||
try {
|
try {
|
||||||
cartItems = JSON.parse(cartData as string) as any[]
|
cartItems = JSON.parse(cartData as string) as any[]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('解析购物车数据失败', e)
|
console.error('瑙f瀽璐墿杞︽暟鎹け璐?, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查商品是否已存在
|
// 妫€鏌ュ晢鍝佹槸鍚﹀凡瀛樺湪
|
||||||
const existingItem = cartItems.find((item: any) => item.id === product.id)
|
const existingItem = cartItems.find((item: any) => item.id === product.id)
|
||||||
|
|
||||||
if (existingItem) {
|
if (existingItem) {
|
||||||
existingItem.quantity++
|
existingItem.quantity++
|
||||||
} else {
|
} else {
|
||||||
// 添加新商品
|
// 娣诲姞鏂板晢鍝?
|
||||||
cartItems.push({
|
cartItems.push({
|
||||||
id: product.id,
|
id: product.id,
|
||||||
shopId: product.shopId || 'shop_default',
|
shopId: product.shopId || 'shop_default',
|
||||||
shopName: product.shopName || product.manufacturer || '自营店铺',
|
shopName: product.shopName || product.manufacturer || '鑷惀搴楅摵',
|
||||||
name: product.name,
|
name: product.name,
|
||||||
price: product.price,
|
price: product.price,
|
||||||
image: product.image,
|
image: product.image,
|
||||||
spec: product.specification || '默认规格',
|
spec: product.specification || '榛樿瑙勬牸',
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
selected: true
|
selected: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存回存储
|
// 淇濆瓨鍥炲瓨鍌?
|
||||||
uni.setStorageSync('cart', JSON.stringify(cartItems))
|
uni.setStorageSync('cart', JSON.stringify(cartItems))
|
||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已添加到购物车',
|
title: '宸叉坊鍔犲埌璐墿杞?,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
cartCount.value++
|
cartCount.value++
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导航函数
|
// 瀵艰埅鍑芥暟
|
||||||
const navigateToSearch = () => uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
const navigateToSearch = () => uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
||||||
const navigateToCart = () => uni.navigateTo({ url: '/pages/medicine/cart' })
|
const navigateToCart = () => uni.navigateTo({ url: '/pages/medicine/cart' })
|
||||||
const navigateToProduct = (product: any) => {
|
const navigateToProduct = (product: any) => {
|
||||||
@@ -418,43 +418,43 @@ const navigateToProduct = (product: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 相机功能
|
// 鐩告満鍔熻兘
|
||||||
const onCamera = () => {
|
const onCamera = () => {
|
||||||
uni.chooseImage({
|
uni.chooseImage({
|
||||||
count: 1,
|
count: 1,
|
||||||
sourceType: ['camera'],
|
sourceType: ['camera'],
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('相机拍摄成功:', res.tempFilePaths[0])
|
console.log('鐩告満鎷嶆憚鎴愬姛:', res.tempFilePaths[0])
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已拍摄,正在识别...',
|
title: '宸叉媿鎽勶紝姝e湪璇嗗埆...',
|
||||||
icon: 'loading'
|
icon: 'loading'
|
||||||
})
|
})
|
||||||
// 这里可以添加后续的识别逻辑
|
// 杩欓噷鍙互娣诲姞鍚庣画鐨勮瘑鍒€昏緫
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '识别成功',
|
title: '璇嗗埆鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('相机调用失败:', err)
|
console.error('鐩告満璋冪敤澶辫触:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扫码功能
|
// 鎵爜鍔熻兘
|
||||||
const onScan = () => {
|
const onScan = () => {
|
||||||
uni.scanCode({
|
uni.scanCode({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('扫码成功:', res)
|
console.log('鎵爜鎴愬姛:', res)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '扫码成功: ' + res.result,
|
title: '鎵爜鎴愬姛: ' + res.result,
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('扫码失败:', err)
|
console.error('鎵爜澶辫触:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -469,7 +469,7 @@ const onScan = () => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
||||||
}
|
}
|
||||||
/* 搜索栏 */
|
/* 鎼滅储鏍?*/
|
||||||
.search-bar {
|
.search-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -480,10 +480,10 @@ const onScan = () => {
|
|||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索栏 */
|
/* 鎼滅储鏍?*/
|
||||||
/* 导航栏搜索框容器内边距调整 */
|
/* 瀵艰埅鏍忔悳绱㈡瀹瑰櫒鍐呰竟璺濊皟鏁?*/
|
||||||
.search-container {
|
.search-container {
|
||||||
height: 44px; /* 调整为与消息页一致的高度 */
|
height: 44px; /* 璋冩暣涓轰笌娑堟伅椤典竴鑷寸殑楂樺害 */
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -493,12 +493,12 @@ const onScan = () => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索框 hover 效果 */
|
/* 鎼滅储妗?hover 鏁堟灉 */
|
||||||
.search-box:hover {
|
.search-box:hover {
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏搜索框容器内边距调整 */
|
/* 瀵艰埅鏍忔悳绱㈡瀹瑰櫒鍐呰竟璺濊皟鏁?*/
|
||||||
.search-box {
|
.search-box {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
@@ -506,12 +506,12 @@ const onScan = () => {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
padding: 0 4px 0 12px;
|
padding: 0 4px 0 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* UVUE 显式设置 row */
|
flex-direction: row; /* UVUE 鏄惧紡璁剧疆 row */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 32px; /* 减小高度,与顶部高度44px适配,略小于顶部高度 */
|
height: 32px; /* 鍑忓皬楂樺害锛屼笌椤堕儴楂樺害44px閫傞厤锛岀暐灏忎簬椤堕儴楂樺害 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-placeholder {
|
.search-placeholder {
|
||||||
@@ -524,7 +524,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-inner-search-text {
|
.nav-inner-search-text {
|
||||||
font-size: 12px; /* 字体稍微变小 */
|
font-size: 12px; /* 瀛椾綋绋嶅井鍙樺皬 */
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@@ -557,7 +557,7 @@ const onScan = () => {
|
|||||||
border-right-width: 1px;
|
border-right-width: 1px;
|
||||||
border-right-style: solid;
|
border-right-style: solid;
|
||||||
border-right-color: #ddd;
|
border-right-color: #ddd;
|
||||||
border-right: 1px solid #ddd; /* 修复UVUE样式 */
|
border-right: 1px solid #ddd; /* 淇UVUE鏍峰紡 */
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,23 +565,23 @@ const onScan = () => {
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 搜索按钮高度微调 */
|
/* 鎼滅储鎸夐挳楂樺害寰皟 */
|
||||||
.nav-inner-search-btn {
|
.nav-inner-search-btn {
|
||||||
padding: 0 12px; /* 减小内边距 */
|
padding: 0 12px; /* 鍑忓皬鍐呰竟璺?*/
|
||||||
background-color: #87CEEB; /* 天空蓝 */
|
background-color: #87CEEB; /* 澶╃┖钃?*/
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 24px; /* 随搜索框高度减小而减小 */
|
height: 24px; /* 闅忔悳绱㈡楂樺害鍑忓皬鑰屽噺灏?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.cart-badge {
|
.cart-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5px;
|
top: -5px;
|
||||||
right: -5px;
|
right: -5px;
|
||||||
background: #FF5722;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
min-width: 18px;
|
min-width: 18px;
|
||||||
@@ -594,36 +594,36 @@ const onScan = () => {
|
|||||||
border: 2px solid #4CAF50;
|
border: 2px solid #4CAF50;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分类内容区 */
|
/* 鍒嗙被鍐呭鍖?*/
|
||||||
.category-content {
|
.category-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 强制水平排列 */
|
flex-direction: row; /* 寮哄埗姘村钩鎺掑垪 */
|
||||||
/* margin-top: 44px; 已通过 style 动态绑定 */
|
/* margin-top: 44px; 宸查€氳繃 style 鍔ㄦ€佺粦瀹?*/
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
/* height: calc(100vh - 44px); 已通过 style 动态绑定 */
|
/* height: calc(100vh - 44px); 宸查€氳繃 style 鍔ㄦ€佺粦瀹?*/
|
||||||
overflow: hidden; /* 防止整体滚动 */
|
overflow: hidden; /* 闃叉鏁翠綋婊氬姩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 左侧一级分类 */
|
/* 宸︿晶涓€绾у垎绫?*/
|
||||||
.primary-category {
|
.primary-category {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 100%; /* 占满父容器高度 */
|
height: 100%; /* 鍗犳弧鐖跺鍣ㄩ珮搴?*/
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow-y: auto; /* 允许内部滚动 */
|
overflow-y: auto; /* 鍏佽鍐呴儴婊氬姩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-item {
|
.primary-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; /* 图标和文字垂直排列 */
|
flex-direction: column; /* 鍥炬爣鍜屾枃瀛楀瀭鐩存帓鍒?*/
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 12px 8px;
|
padding: 12px 8px;
|
||||||
@@ -636,7 +636,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.primary-item:hover {
|
.primary-item:hover {
|
||||||
transform: translateY(-2px); /* 悬停时向上浮动 */
|
transform: translateY(-2px); /* 鎮仠鏃跺悜涓婃诞鍔?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-item.active {
|
.primary-item.active {
|
||||||
@@ -647,7 +647,7 @@ const onScan = () => {
|
|||||||
.primary-icon {
|
.primary-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
margin-right: 0; /* 移除右边距 */
|
margin-right: 0; /* 绉婚櫎鍙宠竟璺?*/
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -658,12 +658,12 @@ const onScan = () => {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 右侧内容区 */
|
/* 鍙充晶鍐呭鍖?*/
|
||||||
.product-content {
|
.product-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%; /* 占满父容器高度 */
|
height: 100%; /* 鍗犳弧鐖跺鍣ㄩ珮搴?*/
|
||||||
padding: 0; /* 移除内边距,交给内部元素 */
|
padding: 0; /* 绉婚櫎鍐呰竟璺濓紝浜ょ粰鍐呴儴鍏冪礌 */
|
||||||
overflow-y: auto; /* 允许内部滚动 */
|
overflow-y: auto; /* 鍏佽鍐呴儴婊氬姩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-header {
|
.category-header {
|
||||||
@@ -687,7 +687,7 @@ const onScan = () => {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 商品网格 */
|
/* 鍟嗗搧缃戞牸 */
|
||||||
.product-grid {
|
.product-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
@@ -713,7 +713,7 @@ const onScan = () => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
background: #FF5722;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
@@ -763,13 +763,13 @@ const onScan = () => {
|
|||||||
|
|
||||||
.price-symbol {
|
.price-symbol {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #FF5722;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price-value {
|
.price-value {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #FF5722;
|
color: #ff5000;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,7 +822,7 @@ const onScan = () => {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 绌虹姸鎬?*/
|
||||||
.empty-state {
|
.empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -850,7 +850,7 @@ const onScan = () => {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加载更多 */
|
/* 鍔犺浇鏇村 */
|
||||||
.load-more {
|
.load-more {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
@@ -865,29 +865,29 @@ const onScan = () => {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 响应式设计 ===== */
|
/* ===== 鍝嶅簲寮忚璁?===== */
|
||||||
|
|
||||||
/* 小屏手机 (小于414px) */
|
/* 灏忓睆鎵嬫満 (灏忎簬414px) */
|
||||||
@media screen and (max-width: 414px) {
|
@media screen and (max-width: 414px) {
|
||||||
.category-content {
|
.category-content {
|
||||||
/* flex-direction: column; 移除这一行,保持 row 布局 */
|
/* flex-direction: column; 绉婚櫎杩欎竴琛岋紝淇濇寔 row 甯冨眬 */
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-category {
|
.primary-category {
|
||||||
width: 80px; /* 减小宽度 */
|
width: 80px; /* 鍑忓皬瀹藉害 */
|
||||||
/* display: flex; 移除flex布局,保持默认 */
|
/* display: flex; 绉婚櫎flex甯冨眬锛屼繚鎸侀粯璁?*/
|
||||||
/* flex-wrap: wrap; 移除换行 */
|
/* flex-wrap: wrap; 绉婚櫎鎹㈣ */
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-item {
|
.primary-item {
|
||||||
/* width: calc(25% - 8px); 移除百分比宽度 */
|
/* width: calc(25% - 8px); 绉婚櫎鐧惧垎姣斿搴?*/
|
||||||
width: auto; /* 恢复自动宽度 */
|
width: auto; /* 鎭㈠鑷姩瀹藉害 */
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 8px 4px;
|
padding: 8px 4px;
|
||||||
/* text-align: center; 已经在通用样式中设置 */
|
/* text-align: center; 宸茬粡鍦ㄩ€氱敤鏍峰紡涓缃?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-icon {
|
.primary-icon {
|
||||||
@@ -901,17 +901,17 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-grid {
|
.product-grid {
|
||||||
grid-template-columns: repeat(2, 1fr); /* 改为双列显示 */
|
grid-template-columns: repeat(2, 1fr); /* 鏀逛负鍙屽垪鏄剧ず */
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 0 4px 20px 4px; /* 增加底部内边距 */
|
padding: 0 4px 20px 4px; /* 澧炲姞搴曢儴鍐呰竟璺?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 手机端商品卡片极简模式 - 仿照主页样式 */
|
/* 鎵嬫満绔晢鍝佸崱鐗囨瀬绠€妯″紡 - 浠跨収涓婚〉鏍峰紡 */
|
||||||
.product-spec,
|
.product-spec,
|
||||||
.manufacturer,
|
.manufacturer,
|
||||||
.original-price,
|
.original-price,
|
||||||
.sales-info,
|
.sales-info,
|
||||||
.product-badge { /* 分类页也隐藏角标,保持整洁 */
|
.product-badge { /* 鍒嗙被椤典篃闅愯棌瑙掓爣锛屼繚鎸佹暣娲?*/
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -920,7 +920,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-image {
|
.product-image {
|
||||||
height: 100px; /* 由于分类页右侧空间更窄,图片高度设得更小一点 */
|
height: 100px; /* 鐢变簬鍒嗙被椤靛彸渚х┖闂存洿绐勶紝鍥剧墖楂樺害璁惧緱鏇村皬涓€鐐?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-name {
|
.product-name {
|
||||||
@@ -951,7 +951,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-meta {
|
.product-meta {
|
||||||
display: none; /* 隐藏整个元数据行 */
|
display: none; /* 闅愯棌鏁翠釜鍏冩暟鎹 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
@@ -968,7 +968,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 中屏手机/小平板 (415px-768px) */
|
/* 涓睆鎵嬫満/灏忓钩鏉?(415px-768px) */
|
||||||
@media screen and (min-width: 415px) and (max-width: 768px) {
|
@media screen and (min-width: 415px) and (max-width: 768px) {
|
||||||
.product-grid {
|
.product-grid {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
@@ -976,7 +976,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 平板设备 (769px-1024px) */
|
/* 骞虫澘璁惧 (769px-1024px) */
|
||||||
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
||||||
.product-grid {
|
.product-grid {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
@@ -984,7 +984,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 桌面端 (1025px以上) */
|
/* 妗岄潰绔?(1025px浠ヤ笂) */
|
||||||
@media screen and (min-width: 1025px) {
|
@media screen and (min-width: 1025px) {
|
||||||
.category-content {
|
.category-content {
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
@@ -1054,7 +1054,7 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 大桌面端 (1400px以上) */
|
/* 澶ф闈㈢ (1400px浠ヤ笂) */
|
||||||
@media screen and (min-width: 1400px) {
|
@media screen and (min-width: 1400px) {
|
||||||
.category-content {
|
.category-content {
|
||||||
max-width: 1600px;
|
max-width: 1600px;
|
||||||
@@ -1129,3 +1129,4 @@ const onScan = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<!-- pages/mall/consumer/chat.uvue -->
|
<!-- pages/mall/consumer/chat.uvue -->
|
||||||
<template>
|
<template>
|
||||||
<view class="chat-page">
|
<view class="chat-page">
|
||||||
<!-- 聊天头部 -->
|
<!-- 聊天头部 -->
|
||||||
<view class="chat-header" :style="{ paddingTop: navPaddingTop }">
|
<view class="chat-header" :style="{ paddingTop: navPaddingTop }">
|
||||||
<view class="header-back" @click="goBack">
|
<view class="header-back" @click="goBack">
|
||||||
<text class="back-icon">‹</text>
|
<text class="back-icon">🔙</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="header-info">
|
<view class="header-info">
|
||||||
<text class="chat-title">{{ headerTitle }}</text>
|
<text class="chat-title">{{ headerTitle }}</text>
|
||||||
<text class="chat-status">在线</text>
|
<text class="chat-status">在线</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="header-actions">
|
<view class="header-actions">
|
||||||
<text class="action-icon" @click="showMoreActions">⋮</text>
|
<text class="action-icon" @click="showMoreActions">⋯</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 聊天输入区 -->
|
<!-- 聊天输入框 -->
|
||||||
<view class="chat-input">
|
<view class="chat-input">
|
||||||
<view class="input-tools">
|
<view class="input-tools">
|
||||||
<text class="tool-icon" @click="showEmojiPicker">😊</text>
|
<text class="tool-icon" @click="showEmojiPicker">😊</text>
|
||||||
@@ -129,11 +129,11 @@ import type { AkSupaRealtimeChannel } from '@/components/supadb/aksupa.uts'
|
|||||||
import { getCurrentUser } from '@/utils/store.uts'
|
import { getCurrentUser } from '@/utils/store.uts'
|
||||||
|
|
||||||
type UiChatMessage = {
|
type UiChatMessage = {
|
||||||
id: string
|
id: string
|
||||||
viewId: string
|
viewId: string
|
||||||
type: string
|
type: string
|
||||||
content: string
|
content: string
|
||||||
time: string
|
time: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
@@ -151,7 +151,7 @@ const isInitialLoading = ref<boolean>(true)
|
|||||||
let realtimeChannel: AkSupaRealtimeChannel | null = null
|
let realtimeChannel: AkSupaRealtimeChannel | null = null
|
||||||
|
|
||||||
// 模拟表情列表
|
// 模拟表情列表
|
||||||
const emojiList = ['😊', '😂', '🤣', '😍', '😘', '🥰', '😭', '😡', '👍', '👏', '🙏', '🎉', '❤️', '🔥', '⭐']
|
const emojiList = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
||||||
|
|
||||||
function scrollToBottom() : void {
|
function scrollToBottom() : void {
|
||||||
if (messages.value.length === 0) return
|
if (messages.value.length === 0) return
|
||||||
@@ -161,13 +161,13 @@ function scrollToBottom() : void {
|
|||||||
const targetId = 'msg-' + lastMsg.id
|
const targetId = 'msg-' + lastMsg.id
|
||||||
|
|
||||||
// 关键点:在 UVue 安卓端,直接连续赋值可能被合并。
|
// 关键点:在 UVue 安卓端,直接连续赋值可能被合并。
|
||||||
// 我们先清除 ID,然后在下一帧赋值,确保 scroll-view 监听到变化。
|
// 我们先清空 ID,然后在下一帧赋值,确保 scroll-view 监听到变化。
|
||||||
scrollToView.value = ''
|
scrollToView.value = ''
|
||||||
|
|
||||||
// 增加多次尝试,确保在 DOM 彻底完成渲染(包含由于高度计算引起的多次排版)后定位。
|
// 增加多次尝试,确保在 DOM 彻底完成渲染(包含由于高度计算引起的多次排版)后定位。
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToView.value = targetId
|
scrollToView.value = targetId
|
||||||
console.log('[scrollToBottom] 发起第一次滚动定位:', targetId)
|
console.log('[scrollToBottom] 发起第一次滚动定位', targetId)
|
||||||
|
|
||||||
// 二次校准:针对长消息或图片导致的高度变化
|
// 二次校准:针对长消息或图片导致的高度变化
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -184,98 +184,98 @@ function getCurrentTime(): string {
|
|||||||
const now = new Date()
|
const now = new Date()
|
||||||
const hours = now.getHours().toString().padStart(2, '0')
|
const hours = now.getHours().toString().padStart(2, '0')
|
||||||
const minutes = now.getMinutes().toString().padStart(2, '0')
|
const minutes = now.getMinutes().toString().padStart(2, '0')
|
||||||
return `${hours}:${minutes}`
|
return ${hours}:
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupRealtimeSubscription(): void {
|
function setupRealtimeSubscription(): void {
|
||||||
console.log('开始建立聊天实时订阅...')
|
console.log('开始建立聊天实时订阅...')
|
||||||
console.log('当前用户ID:', currentUserId.value, '商家ID:', merchantId.value)
|
console.log('当前用户ID:', currentUserId.value, '商家ID:', merchantId.value)
|
||||||
|
|
||||||
realtimeChannel = supa.channel('chat-messages-' + Date.now().toString())
|
realtimeChannel = supa.channel('chat-messages-' + Date.now().toString())
|
||||||
.on('postgres_changes', {
|
.on('postgres_changes', {
|
||||||
event: 'INSERT',
|
event: 'INSERT',
|
||||||
schema: 'public',
|
schema: 'public',
|
||||||
table: 'ml_chat_messages'
|
table: 'ml_chat_messages'
|
||||||
}, (payload: any) => {
|
}, (payload: any) => {
|
||||||
console.log('=== 收到实时订阅回调 ===')
|
console.log('=== 收到实时订阅回调 ===')
|
||||||
const payloadObj = (payload instanceof UTSJSONObject) ? (payload as UTSJSONObject) : (JSON.parse(JSON.stringify(payload ?? {})) as UTSJSONObject)
|
const payloadObj = (payload instanceof UTSJSONObject) ? (payload as UTSJSONObject) : (JSON.parse(JSON.stringify(payload ?? {})) as UTSJSONObject)
|
||||||
const newMsgAny = payloadObj.get('new')
|
const newMsgAny = payloadObj.get('new')
|
||||||
if (newMsgAny == null) {
|
if (newMsgAny == null) {
|
||||||
console.log('newMsgAny 为空,跳过')
|
console.log('newMsgAny 为空,跳过')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newMsg = (newMsgAny instanceof UTSJSONObject) ? (newMsgAny as UTSJSONObject) : (JSON.parse(JSON.stringify(newMsgAny)) as UTSJSONObject)
|
const newMsg = (newMsgAny instanceof UTSJSONObject) ? (newMsgAny as UTSJSONObject) : (JSON.parse(JSON.stringify(newMsgAny)) as UTSJSONObject)
|
||||||
console.log('收到新消息:', newMsg)
|
console.log('收到新消息', newMsg)
|
||||||
|
|
||||||
const senderId = newMsg.getString('sender_id') ?? ''
|
const senderId = newMsg.getString('sender_id') ?? ''
|
||||||
const receiverId = newMsg.getString('receiver_id') ?? ''
|
const receiverId = newMsg.getString('receiver_id') ?? ''
|
||||||
const msgId = newMsg.getString('id') ?? ''
|
const msgId = newMsg.getString('id') ?? ''
|
||||||
const content = newMsg.getString('content') ?? ''
|
const content = newMsg.getString('content') ?? ''
|
||||||
|
|
||||||
console.log('=== 消息详情 ===')
|
console.log('=== 消息详情 ===')
|
||||||
console.log('消息ID:', msgId)
|
console.log('消息ID:', msgId)
|
||||||
console.log('发送者ID:', senderId)
|
console.log('发送者ID:', senderId)
|
||||||
console.log('接收者ID:', receiverId)
|
console.log('接收者ID:', receiverId)
|
||||||
console.log('当前用户ID:', currentUserId.value)
|
console.log('当前用户ID:', currentUserId.value)
|
||||||
console.log('商家ID:', merchantId.value)
|
console.log('商家ID:', merchantId.value)
|
||||||
console.log('消息内容:', content)
|
console.log('消息内容:', content)
|
||||||
|
|
||||||
// 检查消息是否已经在列表中(避免重复)
|
// 检查消息是否已经在列表中(避免重复)
|
||||||
for (let i = 0; i < messages.value.length; i++) {
|
for (let i = 0; i < messages.value.length; i++) {
|
||||||
if (messages.value[i].id == msgId) {
|
if (messages.value[i].id == msgId) {
|
||||||
console.log('消息已存在,跳过')
|
console.log('消息已存在,跳过')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断消息类型
|
// 判断消息类型
|
||||||
const isMyMessage = (senderId == currentUserId.value)
|
const isMyMessage = (senderId == currentUserId.value)
|
||||||
const isForMe = (receiverId == currentUserId.value)
|
const isForMe = (receiverId == currentUserId.value)
|
||||||
const isRelatedToCurrentChat = (senderId == merchantId.value || receiverId == merchantId.value)
|
const isRelatedToCurrentChat = (senderId == merchantId.value || receiverId == merchantId.value)
|
||||||
|
|
||||||
console.log('=== 条件判断 ===')
|
console.log('=== 条件判断 ===')
|
||||||
console.log('isMyMessage:', isMyMessage)
|
console.log('isMyMessage:', isMyMessage)
|
||||||
console.log('isForMe:', isForMe)
|
console.log('isForMe:', isForMe)
|
||||||
console.log('isRelatedToCurrentChat:', isRelatedToCurrentChat)
|
console.log('isRelatedToCurrentChat:', isRelatedToCurrentChat)
|
||||||
|
|
||||||
// 如果消息与当前聊天无关,跳过
|
// 如果消息与当前聊天无关,跳过
|
||||||
if (!isRelatedToCurrentChat) {
|
if (!isRelatedToCurrentChat) {
|
||||||
console.log('消息与当前聊天无关,跳过')
|
console.log('消息与当前聊天无关,跳过')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是自己发送的消息,或者是发给自己的消息,都显示
|
// 如果是自己发送的消息,或者是发给自己的消息,都显示
|
||||||
if (isMyMessage || isForMe) {
|
if (isMyMessage || isForMe) {
|
||||||
const createdAt = newMsg.getString('created_at') ?? new Date().toISOString()
|
const createdAt = newMsg.getString('created_at') ?? new Date().toISOString()
|
||||||
const date = new Date(createdAt)
|
const date = new Date(createdAt)
|
||||||
const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
const timeStr = ${date.getHours().toString().padStart(2, '0')}:
|
||||||
|
|
||||||
// 生成安全的 viewId
|
// 生成安全从 viewId
|
||||||
const safeViewId = 'msg_' + msgId.replace(/[^a-zA-Z0-9]/g, '_')
|
const safeViewId = 'msg_' + msgId.replace(/[^a-zA-Z0-9]/g, '_')
|
||||||
|
|
||||||
const incomingMsg: UiChatMessage = {
|
const incomingMsg: UiChatMessage = {
|
||||||
id: msgId,
|
id: msgId,
|
||||||
viewId: safeViewId,
|
viewId: safeViewId,
|
||||||
type: isMyMessage ? 'sent' : 'received',
|
type: isMyMessage ? 'sent' : 'received',
|
||||||
content: content,
|
content: content,
|
||||||
time: timeStr
|
time: timeStr
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== 添加新消息到列表 ===')
|
console.log('=== 添加新消息到列表 ===')
|
||||||
console.log('消息类型:', incomingMsg.type)
|
console.log('消息类型:', incomingMsg.type)
|
||||||
console.log('消息内容:', incomingMsg.content)
|
console.log('消息内容:', incomingMsg.content)
|
||||||
messages.value.push(incomingMsg)
|
messages.value.push(incomingMsg)
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
} else {
|
} else {
|
||||||
console.log('条件不满足,不添加消息')
|
console.log('条件不满足,不添加消息')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.subscribe((status: string, err: any | null) => {
|
.subscribe((status: string, err: any | null) => {
|
||||||
console.log('订阅状态:', status)
|
console.log('订阅状态', status)
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
console.log('订阅错误:', err)
|
console.log('订阅错误:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadChatHistory(): Promise<void> {
|
async function loadChatHistory(): Promise<void> {
|
||||||
@@ -300,7 +300,7 @@ async function loadChatHistory(): Promise<void> {
|
|||||||
for (let i = 0; i < sortedRawMsgs.length; i++) {
|
for (let i = 0; i < sortedRawMsgs.length; i++) {
|
||||||
const m = sortedRawMsgs[i]
|
const m = sortedRawMsgs[i]
|
||||||
const date = new Date(m.created_at ?? new Date().toISOString())
|
const date = new Date(m.created_at ?? new Date().toISOString())
|
||||||
const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
const timeStr = ${date.getHours().toString().padStart(2, '0')}:
|
||||||
|
|
||||||
const sender = m.sender_id ?? ''
|
const sender = m.sender_id ?? ''
|
||||||
const msgType = (currentUserId.value != '' && sender == currentUserId.value) ? 'sent' : 'received'
|
const msgType = (currentUserId.value != '' && sender == currentUserId.value) ? 'sent' : 'received'
|
||||||
@@ -333,42 +333,42 @@ function onScrollToUpper(e: any): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadMerchantInfo(): Promise<void> {
|
async function loadMerchantInfo(): Promise<void> {
|
||||||
if (merchantId.value == '') return
|
if (merchantId.value == '') return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await supa
|
const response = await supa
|
||||||
.from('ml_shops')
|
.from('ml_shops')
|
||||||
.select('shop_logo, shop_name')
|
.select('shop_logo, shop_name')
|
||||||
.eq('merchant_id', merchantId.value)
|
.eq('merchant_id', merchantId.value)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.execute()
|
.execute()
|
||||||
|
|
||||||
if (response.error != null) {
|
if (response.error != null) {
|
||||||
console.error('[loadMerchantInfo] 获取商家信息失败:', response.error)
|
console.error('[loadMerchantInfo] 获取商家信息失败:', response.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawData = response.data
|
const rawData = response.data
|
||||||
if (rawData == null) return
|
if (rawData == null) return
|
||||||
|
|
||||||
const rawList = rawData as any[]
|
const rawList = rawData as any[]
|
||||||
if (rawList.length == 0) return
|
if (rawList.length == 0) return
|
||||||
|
|
||||||
const shopData = rawList[0]
|
const shopData = rawList[0]
|
||||||
const shopObj = JSON.parse(JSON.stringify(shopData)) as UTSJSONObject
|
const shopObj = JSON.parse(JSON.stringify(shopData)) as UTSJSONObject
|
||||||
|
|
||||||
const logo = shopObj.getString('shop_logo')
|
const logo = shopObj.getString('shop_logo')
|
||||||
if (logo != null && logo != '') {
|
if (logo != null && logo != '') {
|
||||||
merchantAvatar.value = logo
|
merchantAvatar.value = logo
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = shopObj.getString('shop_name')
|
const name = shopObj.getString('shop_name')
|
||||||
if (name != null && name != '' && headerTitle.value == '在线客服') {
|
if (name != null && name != '' && headerTitle.value == '在线客服') {
|
||||||
headerTitle.value = name
|
headerTitle.value = name
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[loadMerchantInfo] 获取商家信息异常:', e)
|
console.error('[loadMerchantInfo] 获取商家信息异常:', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
@@ -379,33 +379,33 @@ onLoad((options: any) => {
|
|||||||
// 状态栏高度 + 10px 原有顶部内边距
|
// 状态栏高度 + 10px 原有顶部内边距
|
||||||
navPaddingTop.value = (statusBarH + 10) + 'px'
|
navPaddingTop.value = (statusBarH + 10) + 'px'
|
||||||
|
|
||||||
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
||||||
const mid = optObj.getString('merchantId') ?? ''
|
const mid = optObj.getString('merchantId') ?? ''
|
||||||
if (mid !== '') {
|
if (mid !== '') {
|
||||||
merchantId.value = mid
|
merchantId.value = mid
|
||||||
}
|
}
|
||||||
const mname = optObj.getString('merchantName') ?? ''
|
const mname = optObj.getString('merchantName') ?? ''
|
||||||
if (mname !== '') {
|
if (mname !== '') {
|
||||||
headerTitle.value = mname
|
headerTitle.value = mname
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
supabaseService.ensureSession().then((uid) => {
|
supabaseService.ensureSession().then((uid) => {
|
||||||
if (uid != null) {
|
if (uid != null) {
|
||||||
currentUserId.value = uid
|
currentUserId.value = uid
|
||||||
} else {
|
} else {
|
||||||
getCurrentUser().then((user) => {
|
getCurrentUser().then((user) => {
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
currentUserId.value = user.id ?? ''
|
currentUserId.value = user.id ?? ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMerchantInfo()
|
loadMerchantInfo()
|
||||||
loadChatHistory()
|
loadChatHistory()
|
||||||
setupRealtimeSubscription()
|
setupRealtimeSubscription()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -425,7 +425,7 @@ const sendMessage = async () => {
|
|||||||
if (merchantId.value != '') {
|
if (merchantId.value != '') {
|
||||||
console.log('[sendMessage] 开始发送消息到:', merchantId.value)
|
console.log('[sendMessage] 开始发送消息到:', merchantId.value)
|
||||||
const success = await supabaseService.sendMessage(merchantId.value, content)
|
const success = await supabaseService.sendMessage(merchantId.value, content)
|
||||||
console.log('[sendMessage] 发送结果:', success)
|
console.log('[sendMessage] 发送结果', success)
|
||||||
if (!success) {
|
if (!success) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '发送失败',
|
title: '发送失败',
|
||||||
@@ -687,13 +687,23 @@ const goBack = () => {
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-text {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.message-time {
|
.message-time {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #999;
|
color: #999;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 聊天输入区 */
|
/* 聊天输入框 */
|
||||||
.chat-input {
|
.chat-input {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
@@ -781,3 +791,4 @@ const goBack = () => {
|
|||||||
|
|
||||||
/* 响应式适配 removed for strict uv-app-x compliance */
|
/* 响应式适配 removed for strict uv-app-x compliance */
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- pages/mall/consumer/chat.uvue -->
|
<!-- pages/mall/consumer/chat.uvue -->
|
||||||
<template>
|
<template>
|
||||||
<view class="chat-page">
|
<view class="chat-page">
|
||||||
<!-- 聊天头部 -->
|
<!-- 聊天头部 -->
|
||||||
@@ -7,11 +7,15 @@
|
|||||||
<text class="back-icon">‹</text>
|
<text class="back-icon">‹</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="header-info">
|
<view class="header-info">
|
||||||
<text class="chat-title">{{ headerTitle }}</text>
|
<view class="header-info-text-wrapper">
|
||||||
<text class="chat-status">在线</text>
|
<text class="chat-title">{{ headerTitle }}</text>
|
||||||
|
<text class="chat-status">在线</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="header-actions">
|
<view class="header-actions">
|
||||||
<text class="action-icon" @click="showMoreActions">⋮</text>
|
<view class="action-icon" @click="showMoreActions">
|
||||||
|
<text class="action-icon-text">⋯</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -20,7 +24,7 @@
|
|||||||
scroll-y="true"
|
scroll-y="true"
|
||||||
class="chat-content"
|
class="chat-content"
|
||||||
:scroll-into-view="scrollToView"
|
:scroll-into-view="scrollToView"
|
||||||
:scroll-with-animation="false"
|
:scroll-with-animation="true"
|
||||||
:show-scrollbar="false"
|
:show-scrollbar="false"
|
||||||
upper-threshold="100"
|
upper-threshold="100"
|
||||||
@scrolltoupper="onScrollToUpper"
|
@scrolltoupper="onScrollToUpper"
|
||||||
@@ -53,7 +57,7 @@
|
|||||||
/>
|
/>
|
||||||
<view class="message-content-wrapper">
|
<view class="message-content-wrapper">
|
||||||
<text class="sender-name">{{ headerTitle }}</text>
|
<text class="sender-name">{{ headerTitle }}</text>
|
||||||
<view class="message-bubble">
|
<view class="message-bubble received-bubble">
|
||||||
<text class="message-text">{{ message.content }}</text>
|
<text class="message-text">{{ message.content }}</text>
|
||||||
<text class="message-time">{{ message.time }}</text>
|
<text class="message-time">{{ message.time }}</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -158,25 +162,34 @@ function scrollToBottom() : void {
|
|||||||
|
|
||||||
// 获取最后一条消息的 ID
|
// 获取最后一条消息的 ID
|
||||||
const lastMsg = messages.value[messages.value.length - 1]
|
const lastMsg = messages.value[messages.value.length - 1]
|
||||||
const targetId = 'msg-' + lastMsg.id
|
const targetId = lastMsg.viewId
|
||||||
|
|
||||||
// 关键点:在 UVue 安卓端,直接连续赋值可能被合并。
|
// 关键点:在 UVue 安卓端,直接连续赋值可能被合并。
|
||||||
// 我们先清除 ID,然后在下一帧赋值,确保 scroll-view 监听到变化。
|
// 我们先清空 ID,然后在下一帧赋值,确保 scroll-view 监听到变化。
|
||||||
scrollToView.value = ''
|
scrollToView.value = ''
|
||||||
|
|
||||||
// 增加多次尝试,确保在 DOM 彻底完成渲染(包含由于高度计算引起的多次排版)后定位。
|
// 延迟更久一点,确保安卓端列表排版彻底完成
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToView.value = targetId
|
scrollToView.value = targetId
|
||||||
console.log('[scrollToBottom] 发起第一次滚动定位:', targetId)
|
console.log('[scrollToBottom] 发起滚动定位:', targetId)
|
||||||
|
|
||||||
// 二次校准:针对长消息或图片导致的高度变化
|
// 分级校准:针对长消息或渲染抖动导致的高度变化
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToView.value = ''
|
scrollToView.value = ''
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToView.value = targetId
|
scrollToView.value = targetId
|
||||||
console.log('[scrollToBottom] 二次校准完成:', targetId)
|
console.log('[scrollToBottom] 第一阶段校准:', targetId)
|
||||||
}, 50)
|
}, 50)
|
||||||
}, 100)
|
}, 500)
|
||||||
|
|
||||||
|
// 最终深度校准(针对首屏数据较多时)
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollToView.value = ''
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollToView.value = targetId
|
||||||
|
console.log('[scrollToBottom] 最终校准:', targetId)
|
||||||
|
}, 50)
|
||||||
|
}, 1200)
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +535,7 @@ const goBack = () => {
|
|||||||
<style>
|
<style>
|
||||||
.chat-page {
|
.chat-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
flex: 1;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -552,7 +565,12 @@ const goBack = () => {
|
|||||||
|
|
||||||
.header-info {
|
.header-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: center;
|
}
|
||||||
|
|
||||||
|
.header-info-text-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-title {
|
.chat-title {
|
||||||
@@ -571,7 +589,11 @@ const goBack = () => {
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #333;
|
color: #333;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-icon-text {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 聊天内容区 */
|
/* 聊天内容区 */
|
||||||
@@ -591,7 +613,9 @@ const goBack = () => {
|
|||||||
|
|
||||||
/* 系统消息 */
|
/* 系统消息 */
|
||||||
.message-item.system {
|
.message-item.system {
|
||||||
text-align: center;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,12 +625,14 @@ const goBack = () => {
|
|||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 时间分割线 */
|
/* 时间分割线 */
|
||||||
.time-divider {
|
.time-divider {
|
||||||
text-align: center;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,6 +642,7 @@ const goBack = () => {
|
|||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 消息项 */
|
/* 消息项 */
|
||||||
@@ -644,7 +671,7 @@ const goBack = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message-content-wrapper {
|
.message-content-wrapper {
|
||||||
max-width: 70%;
|
width: 260px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@@ -655,9 +682,11 @@ const goBack = () => {
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||||
align-self: flex-start; /* 关键:根据内容宽度自适应,不撑满 */
|
}
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-all;
|
.received-bubble {
|
||||||
|
align-self: flex-start;
|
||||||
|
border-top-left-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble.me {
|
.message-bubble.me {
|
||||||
@@ -666,10 +695,6 @@ const goBack = () => {
|
|||||||
border-top-right-radius: 2px;
|
border-top-right-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble:not(.me) {
|
|
||||||
border-top-left-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sender-name {
|
.sender-name {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #999;
|
color: #999;
|
||||||
@@ -682,8 +707,6 @@ const goBack = () => {
|
|||||||
color: #333;
|
color: #333;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-all;
|
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,3 +804,4 @@ const goBack = () => {
|
|||||||
|
|
||||||
/* 响应式适配 removed for strict uv-app-x compliance */
|
/* 响应式适配 removed for strict uv-app-x compliance */
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="chat-page">
|
<view class="chat-page">
|
||||||
<!-- 聊天头部 -->
|
<!-- 鑱婂ぉ澶撮儴 -->
|
||||||
<view class="chat-header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="chat-header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view class="header-content">
|
<view class="header-content">
|
||||||
<view class="header-back" @click="goBack">
|
<view class="header-back" @click="goBack">
|
||||||
<text class="back-icon">‹</text>
|
<text class="back-icon">鈥?/text>
|
||||||
</view>
|
</view>
|
||||||
<view class="header-info">
|
<view class="header-info">
|
||||||
<text class="chat-title">{{ merchantName || '在线客服' }}</text>
|
<text class="chat-title">{{ merchantName || '鍦ㄧ嚎瀹㈡湇' }}</text>
|
||||||
<text class="chat-status">在线</text>
|
<text class="chat-status">鍦ㄧ嚎</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="header-actions">
|
<view class="header-actions">
|
||||||
<text class="action-icon" @click="showMoreActions">⋮</text>
|
<text class="action-icon" @click="showMoreActions">鈰?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 聊天内容 -->
|
<!-- 鑱婂ぉ鍐呭 -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
scroll-y
|
scroll-y
|
||||||
class="chat-content"
|
class="chat-content"
|
||||||
@@ -24,29 +24,29 @@
|
|||||||
scroll-with-animation
|
scroll-with-animation
|
||||||
@scrolltoupper="loadMoreHistory"
|
@scrolltoupper="loadMoreHistory"
|
||||||
>
|
>
|
||||||
<!-- 占位,防止内容被头部遮挡 -->
|
<!-- 鍗犱綅锛岄槻姝㈠唴瀹硅澶撮儴閬尅 -->
|
||||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||||
|
|
||||||
<!-- 聊天消息列表 -->
|
<!-- 鑱婂ぉ娑堟伅鍒楄〃 -->
|
||||||
<view class="chat-messages">
|
<view class="chat-messages">
|
||||||
<!-- 系统提示 -->
|
<!-- 绯荤粺鎻愮ず -->
|
||||||
<view class="message-item system">
|
<view class="message-item system">
|
||||||
<text class="system-text">已连接到商家,开始聊天吧</text>
|
<text class="system-text">宸茶繛鎺ュ埌鍟嗗锛屽紑濮嬭亰澶╁惂</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 消息项 -->
|
<!-- 娑堟伅椤?-->
|
||||||
<view
|
<view
|
||||||
v-for="(message, index) in messages"
|
v-for="(message, index) in messages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
:class="['message-item', message.type]"
|
:class="['message-item', message.type]"
|
||||||
:id="'msg-' + message.id"
|
:id="'msg-' + message.id"
|
||||||
>
|
>
|
||||||
<!-- 时间显示逻辑:每5分钟显示一次时间 -->
|
<!-- 鏃堕棿鏄剧ず閫昏緫锛氭瘡5鍒嗛挓鏄剧ず涓€娆℃椂闂?-->
|
||||||
<view v-if="shouldShowTime(index)" class="time-divider">
|
<view v-if="shouldShowTime(index)" class="time-divider">
|
||||||
<text>{{ formatTime(message.rawTime) }}</text>
|
<text>{{ formatTime(message.rawTime) }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 对方消息 -->
|
<!-- 瀵规柟娑堟伅 -->
|
||||||
<view v-if="message.type === 'received'" class="message-wrapper">
|
<view v-if="message.type === 'received'" class="message-wrapper">
|
||||||
<image
|
<image
|
||||||
class="avatar"
|
class="avatar"
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 我的消息 -->
|
<!-- 鎴戠殑娑堟伅 -->
|
||||||
<view v-else class="message-wrapper me">
|
<view v-else class="message-wrapper me">
|
||||||
<view class="message-content-wrapper">
|
<view class="message-content-wrapper">
|
||||||
<view class="message-bubble me">
|
<view class="message-bubble me">
|
||||||
@@ -75,24 +75,24 @@
|
|||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 底部填充,防止被输入框遮挡 -->
|
<!-- 搴曢儴濉厖锛岄槻姝㈣杈撳叆妗嗛伄鎸?-->
|
||||||
<view style="height: 20px;"></view>
|
<view style="height: 20px;"></view>
|
||||||
<view id="bottom-anchor" style="height: 1px;"></view>
|
<view id="bottom-anchor" style="height: 1px;"></view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 聊天输入区 -->
|
<!-- 鑱婂ぉ杈撳叆鍖?-->
|
||||||
<view class="chat-input-area">
|
<view class="chat-input-area">
|
||||||
<view class="input-tools">
|
<view class="input-tools">
|
||||||
<text class="tool-icon" @click="toggleEmoji">😊</text>
|
<text class="tool-icon" @click="toggleEmoji">馃槉</text>
|
||||||
<!-- <text class="tool-icon" @click="chooseImage">📷</text> -->
|
<!-- <text class="tool-icon" @click="chooseImage">馃摲</text> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
<input
|
<input
|
||||||
class="message-input"
|
class="message-input"
|
||||||
v-model="inputMessage"
|
v-model="inputMessage"
|
||||||
placeholder="请输入消息..."
|
placeholder="璇疯緭鍏ユ秷鎭?.."
|
||||||
:adjust-position="true"
|
:adjust-position="true"
|
||||||
confirm-type="send"
|
confirm-type="send"
|
||||||
@confirm="sendMessage"
|
@confirm="sendMessage"
|
||||||
@@ -102,11 +102,11 @@
|
|||||||
:class="{ active: inputMessage.trim().length > 0 }"
|
:class="{ active: inputMessage.trim().length > 0 }"
|
||||||
@click="sendMessage"
|
@click="sendMessage"
|
||||||
>
|
>
|
||||||
发送
|
鍙戦€?
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 表情选择器 (简化版) -->
|
<!-- 琛ㄦ儏閫夋嫨鍣?(绠€鍖栫増) -->
|
||||||
<scroll-view scroll-y v-if="showEmoji" class="emoji-picker">
|
<scroll-view scroll-y v-if="showEmoji" class="emoji-picker">
|
||||||
<view class="emoji-grid">
|
<view class="emoji-grid">
|
||||||
<text
|
<text
|
||||||
@@ -131,13 +131,13 @@ import type { ChatMessage } from '@/utils/supabaseService.uts'
|
|||||||
import supa from '@/components/supadb/aksupainstance.uts'
|
import supa from '@/components/supadb/aksupainstance.uts'
|
||||||
// import { getCurrentUser } from '@/utils/store.uts'
|
// import { getCurrentUser } from '@/utils/store.uts'
|
||||||
|
|
||||||
// 界面状态
|
// 鐣岄潰鐘舵€?
|
||||||
const statusBarHeight = ref(0)
|
const statusBarHeight = ref(0)
|
||||||
const scrollToView = ref('')
|
const scrollToView = ref('')
|
||||||
const showEmoji = ref(false)
|
const showEmoji = ref(false)
|
||||||
const inputMessage = ref('')
|
const inputMessage = ref('')
|
||||||
|
|
||||||
// 业务数据
|
// 涓氬姟鏁版嵁
|
||||||
const merchantId = ref('')
|
const merchantId = ref('')
|
||||||
const merchantName = ref('')
|
const merchantName = ref('')
|
||||||
const merchantLogo = ref('')
|
const merchantLogo = ref('')
|
||||||
@@ -145,38 +145,38 @@ const userAvatar = ref('')
|
|||||||
const currentUserId = ref('')
|
const currentUserId = ref('')
|
||||||
const messages = ref<any[]>([])
|
const messages = ref<any[]>([])
|
||||||
|
|
||||||
const emojiList = ['😊', '😂', '👍', '👌', '❤️', '🌹', '🙏', '🎉', '😡', '😭', '🤔', '👋', '🤝', '💊', '🏥']
|
const emojiList = ['馃槉', '馃槀', '馃憤', '馃憣', '鉂わ笍', '馃尮', '馃檹', '馃帀', '馃槨', '馃槶', '馃', '馃憢', '馃', '馃拪', '馃彞']
|
||||||
let realtimeChannel: any | null = null
|
let realtimeChannel: any | null = null
|
||||||
|
|
||||||
onLoad((options: any) => {
|
onLoad((options: any) => {
|
||||||
// 获取状态栏高度
|
// 鑾峰彇鐘舵€佹爮楂樺害
|
||||||
const sys = uni.getSystemInfoSync()
|
const sys = uni.getSystemInfoSync()
|
||||||
statusBarHeight.value = sys.statusBarHeight ?? 0
|
statusBarHeight.value = sys.statusBarHeight ?? 0
|
||||||
|
|
||||||
// 获取参数
|
// 鑾峰彇鍙傛暟
|
||||||
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
|
||||||
const mid = optObj.getString('merchantId') ?? ''
|
const mid = optObj.getString('merchantId') ?? ''
|
||||||
if (mid !== '') {
|
if (mid !== '') {
|
||||||
merchantId.value = mid
|
merchantId.value = mid
|
||||||
merchantName.value = optObj.getString('merchantName') ?? '商家'
|
merchantName.value = optObj.getString('merchantName') ?? '鍟嗗'
|
||||||
merchantLogo.value = optObj.getString('merchantLogo') ?? ''
|
merchantLogo.value = optObj.getString('merchantLogo') ?? ''
|
||||||
console.log('开始聊天,商家ID:', merchantId.value)
|
console.log('寮€濮嬭亰澶╋紝鍟嗗ID:', merchantId.value)
|
||||||
} else {
|
} else {
|
||||||
merchantName.value = '平台客服'
|
merchantName.value = '骞冲彴瀹㈡湇'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户
|
// 鑾峰彇褰撳墠鐢ㄦ埛
|
||||||
const uid = supabaseService.getCurrentUserId()
|
const uid = supabaseService.getCurrentUserId()
|
||||||
if (uid != null) {
|
if (uid != null) {
|
||||||
currentUserId.value = uid
|
currentUserId.value = uid
|
||||||
// 简单获取一下头像,实际应该从Profile获取
|
// 绠€鍗曡幏鍙栦竴涓嬪ご鍍忥紝瀹為檯搴旇浠嶱rofile鑾峰彇
|
||||||
userAvatar.value = 'https://picsum.photos/100'
|
userAvatar.value = 'https://picsum.photos/100'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载历史消息
|
// 鍔犺浇鍘嗗彶娑堟伅
|
||||||
loadHistory()
|
loadHistory()
|
||||||
|
|
||||||
// 开启实时订阅
|
// 寮€鍚疄鏃惰闃?
|
||||||
startRealtimeSubscription()
|
startRealtimeSubscription()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -186,15 +186,15 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 加载历史记录
|
// 鍔犺浇鍘嗗彶璁板綍
|
||||||
const loadHistory = async () => {
|
const loadHistory = async () => {
|
||||||
let rawMsgs: ChatMessage[] = []
|
let rawMsgs: ChatMessage[] = []
|
||||||
|
|
||||||
if (merchantId.value) {
|
if (merchantId.value) {
|
||||||
// 获取与特定商家的聊天
|
// 鑾峰彇涓庣壒瀹氬晢瀹剁殑鑱婂ぉ
|
||||||
rawMsgs = await supabaseService.getChatMessages(merchantId.value)
|
rawMsgs = await supabaseService.getChatMessages(merchantId.value)
|
||||||
} else {
|
} else {
|
||||||
// 获取所有(比如客服)
|
// 鑾峰彇鎵€鏈夛紙姣斿瀹㈡湇锛?
|
||||||
rawMsgs = await supabaseService.getUserChatMessages()
|
rawMsgs = await supabaseService.getUserChatMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,14 +205,14 @@ const loadHistory = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loadMoreHistory = () => {
|
const loadMoreHistory = () => {
|
||||||
// TODO: 实现下拉加载更多历史
|
// TODO: 瀹炵幇涓嬫媺鍔犺浇鏇村鍘嗗彶
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开启实时订阅
|
// 寮€鍚疄鏃惰闃?
|
||||||
const startRealtimeSubscription = () => {
|
const startRealtimeSubscription = () => {
|
||||||
if (currentUserId.value == '') return
|
if (currentUserId.value == '') return
|
||||||
|
|
||||||
console.log('开启消息监听...')
|
console.log('寮€鍚秷鎭洃鍚?..')
|
||||||
const filterObj = ({
|
const filterObj = ({
|
||||||
event: 'INSERT',
|
event: 'INSERT',
|
||||||
schema: 'public',
|
schema: 'public',
|
||||||
@@ -223,12 +223,12 @@ const startRealtimeSubscription = () => {
|
|||||||
'postgres_changes',
|
'postgres_changes',
|
||||||
filterObj,
|
filterObj,
|
||||||
(payload: any) => {
|
(payload: any) => {
|
||||||
console.log('收到新消息:', payload)
|
console.log('鏀跺埌鏂版秷鎭?', payload)
|
||||||
const payloadObj = (payload instanceof UTSJSONObject) ? (payload as UTSJSONObject) : (JSON.parse(JSON.stringify(payload ?? {})) as UTSJSONObject)
|
const payloadObj = (payload instanceof UTSJSONObject) ? (payload as UTSJSONObject) : (JSON.parse(JSON.stringify(payload ?? {})) as UTSJSONObject)
|
||||||
const newAny = payloadObj.get('new')
|
const newAny = payloadObj.get('new')
|
||||||
if (newAny == null) return
|
if (newAny == null) return
|
||||||
const newMsg = (newAny instanceof UTSJSONObject) ? (newAny as UTSJSONObject) : (JSON.parse(JSON.stringify(newAny)) as UTSJSONObject)
|
const newMsg = (newAny instanceof UTSJSONObject) ? (newAny as UTSJSONObject) : (JSON.parse(JSON.stringify(newAny)) as UTSJSONObject)
|
||||||
// 只有来自当前聊天的商家的消息才显示,或者如果是全局客服模式
|
// 鍙湁鏉ヨ嚜褰撳墠鑱婂ぉ鐨勫晢瀹剁殑娑堟伅鎵嶆樉绀猴紝鎴栬€呭鏋滄槸鍏ㄥ眬瀹㈡湇妯″紡
|
||||||
const senderId = newMsg.getString('sender_id') ?? ''
|
const senderId = newMsg.getString('sender_id') ?? ''
|
||||||
if (senderId === merchantId.value || merchantId.value == '') {
|
if (senderId === merchantId.value || merchantId.value == '') {
|
||||||
const formatted = formatMessage({
|
const formatted = formatMessage({
|
||||||
@@ -237,14 +237,14 @@ const startRealtimeSubscription = () => {
|
|||||||
msg_type: newMsg.getString('msg_type') ?? '',
|
msg_type: newMsg.getString('msg_type') ?? '',
|
||||||
sender_id: senderId,
|
sender_id: senderId,
|
||||||
receiver_id: newMsg.getString('receiver_id') ?? '',
|
receiver_id: newMsg.getString('receiver_id') ?? '',
|
||||||
is_from_user: false, // 收到的一定不是自己发的
|
is_from_user: false, // 鏀跺埌鐨勪竴瀹氫笉鏄嚜宸卞彂鐨?
|
||||||
created_at: newMsg.getString('created_at') ?? ''
|
created_at: newMsg.getString('created_at') ?? ''
|
||||||
} as ChatMessage)
|
} as ChatMessage)
|
||||||
|
|
||||||
messages.value.push(formatted)
|
messages.value.push(formatted)
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
|
|
||||||
// 震动提示
|
// 闇囧姩鎻愮ず
|
||||||
uni.vibrateShort({})
|
uni.vibrateShort({})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,11 +252,11 @@ const startRealtimeSubscription = () => {
|
|||||||
.subscribe()
|
.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化消息
|
// 鏍煎紡鍖栨秷鎭?
|
||||||
const formatMessage = (m: ChatMessage): any => {
|
const formatMessage = (m: ChatMessage): any => {
|
||||||
// 如果 sender_id 是自己,就是 'sent',否则 'received'
|
// 濡傛灉 sender_id 鏄嚜宸憋紝灏辨槸 'sent'锛屽惁鍒?'received'
|
||||||
// 注意:数据库字段 is_from_user 有时可能只是标记是否由C端用户发起,
|
// 娉ㄦ剰锛氭暟鎹簱瀛楁 is_from_user 鏈夋椂鍙兘鍙槸鏍囪鏄惁鐢盋绔敤鎴峰彂璧凤紝
|
||||||
// 最准确的是对比 id
|
// 鏈€鍑嗙‘鐨勬槸瀵规瘮 id
|
||||||
let isMe = false
|
let isMe = false
|
||||||
if (currentUserId.value) {
|
if (currentUserId.value) {
|
||||||
isMe = m.sender_id === currentUserId.value
|
isMe = m.sender_id === currentUserId.value
|
||||||
@@ -277,7 +277,7 @@ const sendMessage = async () => {
|
|||||||
const text = inputMessage.value.trim()
|
const text = inputMessage.value.trim()
|
||||||
if (text == '') return
|
if (text == '') return
|
||||||
|
|
||||||
// 乐观更新 UI
|
// 涔愯鏇存柊 UI
|
||||||
const tempId = 'temp_' + Date.now()
|
const tempId = 'temp_' + Date.now()
|
||||||
const tempMsg = {
|
const tempMsg = {
|
||||||
id: tempId,
|
id: tempId,
|
||||||
@@ -291,21 +291,21 @@ const sendMessage = async () => {
|
|||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
showEmoji.value = false
|
showEmoji.value = false
|
||||||
|
|
||||||
// 发送请求
|
// 鍙戦€佽姹?
|
||||||
// 注意:如果 merchantId 为空,sendChatMessage 第二个参数传 null,会变成无主消息
|
// 娉ㄦ剰锛氬鏋?merchantId 涓虹┖锛宻endChatMessage 绗簩涓弬鏁颁紶 null锛屼細鍙樻垚鏃犱富娑堟伅
|
||||||
const success = await supabaseService.sendChatMessage(text, merchantId.value ? merchantId.value : null)
|
const success = await supabaseService.sendChatMessage(text, merchantId.value ? merchantId.value : null)
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
uni.showToast({ title: '发送失败', icon: 'none' })
|
uni.showToast({ title: '鍙戦€佸け璐?, icon: 'none' })
|
||||||
// 这里可以加一个重试按钮或移除消息
|
// 杩欓噷鍙互鍔犱竴涓噸璇曟寜閽垨绉婚櫎娑堟伅
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
// 延时滚动以确保视图更新
|
// 寤舵椂婊氬姩浠ョ‘淇濊鍥炬洿鏂?
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToView.value = 'bottom-anchor'
|
scrollToView.value = 'bottom-anchor'
|
||||||
// Hack: 重置再设置以强制触发
|
// Hack: 閲嶇疆鍐嶈缃互寮哄埗瑙﹀彂
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToView.value = 'msg-' + (messages.value.length > 0 ? messages.value[messages.value.length-1].id : '')
|
scrollToView.value = 'msg-' + (messages.value.length > 0 ? messages.value[messages.value.length-1].id : '')
|
||||||
}, 50)
|
}, 50)
|
||||||
@@ -320,13 +320,13 @@ const formatTime = (isoString: string): string => {
|
|||||||
const date = new Date(isoString)
|
const date = new Date(isoString)
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
|
|
||||||
// 如果是今天,显示 HH:mm
|
// 濡傛灉鏄粖澶╋紝鏄剧ず HH:mm
|
||||||
if (date.toDateString() === now.toDateString()) {
|
if (date.toDateString() === now.toDateString()) {
|
||||||
const h = date.getHours().toString().padStart(2, '0')
|
const h = date.getHours().toString().padStart(2, '0')
|
||||||
const m = date.getMinutes().toString().padStart(2, '0')
|
const m = date.getMinutes().toString().padStart(2, '0')
|
||||||
return `${h}:${m}`
|
return `${h}:${m}`
|
||||||
}
|
}
|
||||||
// 否则显示 MM-DD HH:mm
|
// 鍚﹀垯鏄剧ず MM-DD HH:mm
|
||||||
const mo = (date.getMonth() + 1).toString().padStart(2, '0')
|
const mo = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||||
const d = date.getDate().toString().padStart(2, '0')
|
const d = date.getDate().toString().padStart(2, '0')
|
||||||
const h = date.getHours().toString().padStart(2, '0')
|
const h = date.getHours().toString().padStart(2, '0')
|
||||||
@@ -340,7 +340,7 @@ const shouldShowTime = (index: number): boolean => {
|
|||||||
const curr = messages.value[index]
|
const curr = messages.value[index]
|
||||||
const t1 = new Date(prev.rawTime).getTime()
|
const t1 = new Date(prev.rawTime).getTime()
|
||||||
const t2 = new Date(curr.rawTime).getTime()
|
const t2 = new Date(curr.rawTime).getTime()
|
||||||
// 间隔超过5分钟(300000ms)显示时间
|
// 闂撮殧瓒呰繃5鍒嗛挓(300000ms)鏄剧ず鏃堕棿
|
||||||
return (t2 - t1) > 300000
|
return (t2 - t1) > 300000
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,10 +357,10 @@ const insertEmoji = (emoji: string) => {
|
|||||||
|
|
||||||
const showMoreActions = () => {
|
const showMoreActions = () => {
|
||||||
uni.showActionSheet({
|
uni.showActionSheet({
|
||||||
itemList: ['清空记录', '投诉商家'],
|
itemList: ['娓呯┖璁板綍', '鎶曡瘔鍟嗗'],
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.tapIndex === 0) {
|
if (res.tapIndex === 0) {
|
||||||
messages.value = [] // 仅本地清空
|
messages.value = [] // 浠呮湰鍦版竻绌?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -620,3 +620,4 @@ const showMoreActions = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 结算页面 -->
|
<!-- 结算页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="checkout-page">
|
<view class="checkout-page">
|
||||||
<scroll-view class="checkout-content" scroll-y>
|
<scroll-view class="checkout-content" scroll-y>
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
<text class="popup-close" @click="showAddressPopup = false">×</text>
|
<text class="popup-close" @click="showAddressPopup = false">×</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view class="address-list-container" scroll-y>
|
<scroll-view class="address-list-container" scroll-y="true" :scroll-with-animation="true">
|
||||||
<!-- 登录提示 -->
|
<!-- 登录提示 -->
|
||||||
<view v-if="isLoggedIn == false" class="login-prompt" @click="goToLogin">
|
<view v-if="isLoggedIn == false" class="login-prompt" @click="goToLogin">
|
||||||
<text class="login-prompt-icon">🔒</text>
|
<text class="login-prompt-icon">🔒</text>
|
||||||
@@ -210,53 +210,63 @@
|
|||||||
<view class="address-form-popup" @click.stop>
|
<view class="address-form-popup" @click.stop>
|
||||||
<view class="form-header">
|
<view class="form-header">
|
||||||
<text class="form-title">新建收货地址</text>
|
<text class="form-title">新建收货地址</text>
|
||||||
<text class="form-close" @click="cancelNewAddress">×</text>
|
<view class="form-close-btn" @click="cancelNewAddress">
|
||||||
|
<text class="form-close-icon">✕</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view class="form-content" scroll-y>
|
<scroll-view class="form-content" scroll-y="true">
|
||||||
<view class="form-item">
|
<view class="form-section">
|
||||||
<text class="form-label">收货人</text>
|
<view class="form-item">
|
||||||
<input class="form-input" v-model="newAddress.recipient_name"
|
<text class="form-label">收货人</text>
|
||||||
placeholder="请输入收货人姓名" />
|
<input class="form-input" v-model="newAddress.recipient_name"
|
||||||
</view>
|
placeholder="请输入收货人姓名" />
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label">手机号</text>
|
<text class="form-label">手机号</text>
|
||||||
<input class="form-input" v-model="newAddress.phone"
|
<input class="form-input" v-model="newAddress.phone"
|
||||||
placeholder="请输入手机号码" type="number" />
|
placeholder="请输入手机号码" type="number" />
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="form-item">
|
|
||||||
<text class="form-label">智能填写地址</text>
|
|
||||||
<textarea class="form-textarea smart-address-input"
|
|
||||||
v-model="smartAddressInput"
|
|
||||||
placeholder="粘贴如:北京市朝阳区三里屯SOHO A座 张三 13800138000"
|
|
||||||
@input="parseSmartAddress"
|
|
||||||
maxlength="200" />
|
|
||||||
<text class="smart-tip">自动识别:地址+姓名+电话(支持粘贴文本)</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="form-item">
|
|
||||||
<text class="form-label">所在地区</text>
|
|
||||||
<view class="region-inputs">
|
|
||||||
<input class="form-input region-input form-input-readonly" v-model="newAddress.province"
|
|
||||||
placeholder="省" readonly />
|
|
||||||
<input class="form-input region-input form-input-readonly" v-model="newAddress.city"
|
|
||||||
placeholder="市" readonly />
|
|
||||||
<input class="form-input region-input form-input-readonly" v-model="newAddress.district"
|
|
||||||
placeholder="区/县" readonly />
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form-item">
|
<view class="form-section">
|
||||||
<text class="form-label">详细地址</text>
|
<view class="form-item">
|
||||||
<textarea class="form-textarea" v-model="newAddress.detail"
|
<view class="label-row">
|
||||||
placeholder="街道、小区、楼栋、门牌号等"
|
<text class="form-label">智能填写</text>
|
||||||
maxlength="100" />
|
<text class="smart-tag">识别姓名/电话/地址</text>
|
||||||
|
</view>
|
||||||
|
<textarea class="form-textarea smart-address-input"
|
||||||
|
v-model="smartAddressInput"
|
||||||
|
placeholder="粘贴收货信息文本,自动拆分字段"
|
||||||
|
@input="parseSmartAddress"
|
||||||
|
maxlength="200" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-section">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">所在地区</text>
|
||||||
|
<view class="region-inputs">
|
||||||
|
<input class="form-input region-input form-input-readonly" v-model="newAddress.province"
|
||||||
|
placeholder="省" readonly />
|
||||||
|
<input class="form-input region-input form-input-readonly" v-model="newAddress.city"
|
||||||
|
placeholder="市" readonly />
|
||||||
|
<input class="form-input region-input form-input-readonly" v-model="newAddress.district"
|
||||||
|
placeholder="区/县" readonly />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">详细地址</text>
|
||||||
|
<textarea class="form-textarea detail-textarea" v-model="newAddress.detail"
|
||||||
|
placeholder="如街道、楼栋、门牌号等"
|
||||||
|
maxlength="100" />
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form-item checkbox-item">
|
<view class="form-item checkbox-item">
|
||||||
<view class="checkbox-wrapper" @click="newAddress.is_default = newAddress.is_default ? false : true">
|
<view class="checkbox-wrapper" @click="newAddress.is_default = !newAddress.is_default">
|
||||||
<view :class="['checkbox', { checked: newAddress.is_default }]">
|
<view :class="['checkbox', { checked: newAddress.is_default }]">
|
||||||
<text v-if="newAddress.is_default" class="checkbox-check">✓</text>
|
<text v-if="newAddress.is_default" class="checkbox-check">✓</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -266,8 +276,7 @@
|
|||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<view class="form-buttons">
|
<view class="form-buttons">
|
||||||
<button class="form-cancel-btn" @click="cancelNewAddress">取消</button>
|
<button class="form-submit-btn" @click="saveNewAddress">保存并使用</button>
|
||||||
<button class="form-submit-btn" @click="saveNewAddress">保存</button>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -1511,58 +1520,259 @@ const goToLogin = () => {
|
|||||||
/* 弹窗样式 */
|
/* 弹窗样式 */
|
||||||
.address-popup-mask, .address-form-mask, .confirm-popup-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 9998; }
|
.address-popup-mask, .address-form-mask, .confirm-popup-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 9998; }
|
||||||
.address-popup-mask { display: flex; align-items: flex-end; justify-content: center; }
|
.address-popup-mask { display: flex; align-items: flex-end; justify-content: center; }
|
||||||
.address-form-mask, .confirm-popup-mask { display: flex; align-items: center; justify-content: center; }
|
.address-form-mask, .confirm-popup-mask { display: flex; align-items: center; justify-content: center; z-index: 10000; }
|
||||||
.address-popup { background-color: #ffffff; width: 100%; height: 500px; border-radius: 20px 20px 0 0; display: flex; flex-direction: column; }
|
.address-popup {
|
||||||
.address-form-popup { background-color: #ffffff; width: 90%; max-width: 500px; height: 600px; border-radius: 12px; display: flex; flex-direction: column; }
|
background-color: #ffffff;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 20px 20px 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
.address-form-popup {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
width: 92%;
|
||||||
|
max-width: 500px;
|
||||||
|
height: 85vh;
|
||||||
|
border-radius: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 0.5px solid #eee;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-title { font-size: 17px; font-weight: bold; color: #333333; }
|
||||||
|
.popup-close { position: absolute; right: 16px; font-size: 20px; color: #999999; padding: 4px; }
|
||||||
|
|
||||||
|
.address-list-container {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-address-item {
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
border-radius: 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-address-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-address-name { font-size: 15px; font-weight: bold; color: #333333; margin-right: 12px; }
|
||||||
|
.popup-address-phone { font-size: 14px; color: #666666; margin-right: 8px; }
|
||||||
|
.popup-default-tag { background-color: #fff0eb; padding: 1px 6px; border-radius: 4px; }
|
||||||
|
.popup-tag-text { color: #ff5000; font-size: 10px; }
|
||||||
|
.popup-address-detail { font-size: 13px; color: #666; line-height: 1.4; }
|
||||||
|
|
||||||
|
.popup-selected-indicator {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: #ff5000;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-empty-address { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 20px; }
|
||||||
|
.popup-empty-icon { font-size: 48px; margin-bottom: 12px; opacity: 0.3; }
|
||||||
|
.popup-empty-text { font-size: 14px; color: #999; }
|
||||||
|
|
||||||
|
.popup-add-address-btn {
|
||||||
|
background-color: #ff5000;
|
||||||
|
margin: 12px 16px 30px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 22px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-btn-icon { color: #ffffff; font-size: 20px; margin-right: 6px; font-weight: 300; }
|
||||||
|
.popup-btn-text { color: #ffffff; font-size: 15px; font-weight: bold; }
|
||||||
|
|
||||||
|
.address-form-popup {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
width: 92%;
|
||||||
|
max-width: 500px;
|
||||||
|
height: 85vh;
|
||||||
|
border-radius: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-header {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 0.5px solid #eee;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-title { font-size: 17px; font-weight: bold; color: #333333; }
|
||||||
|
.form-close-btn { position: absolute; right: 12px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; }
|
||||||
|
.form-close-icon { font-size: 18px; color: #999; }
|
||||||
|
|
||||||
|
.form-content {
|
||||||
|
flex: 1;
|
||||||
|
height: 100px; /* 进一步减小初始高度,确保安卓端 flex:1 接管 */
|
||||||
|
min-height: 0;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-form-popup {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
width: 92%;
|
||||||
|
max-width: 500px;
|
||||||
|
height: 80vh; /* 稍微压低高度确保不在边缘产生冲突 */
|
||||||
|
border-radius: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 0 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 0.5px solid #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-tag {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #ff5000;
|
||||||
|
background-color: #fff0eb;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input-readonly { color: #888; }
|
||||||
|
|
||||||
|
.region-inputs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.region-input { flex: 1; text-align: center; }
|
||||||
|
|
||||||
|
.form-textarea {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smart-address-input { height: 80px; }
|
||||||
|
.detail-textarea { height: 60px; }
|
||||||
|
|
||||||
|
.checkbox-item {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-wrapper { display: flex; flex-direction: row; align-items: center; }
|
||||||
|
.checkbox { width: 18px; height: 18px; border: 1.5px solid #ddd; border-radius: 50%; margin-right: 10px; display: flex; align-items: center; justify-content: center; }
|
||||||
|
.checkbox.checked { background-color: #ff5000; border-color: #ff5000; }
|
||||||
|
.checkbox-check { color: #ffffff; font-size: 12px; }
|
||||||
|
.checkbox-label { font-size: 14px; color: #333; }
|
||||||
|
|
||||||
|
.form-buttons {
|
||||||
|
padding: 12px 16px 30px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-submit-btn {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #ff5000;
|
||||||
|
color: #ffffff;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
border-radius: 22px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确认保存弹窗 */
|
||||||
|
.confirm-popup-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 9998; display: flex; align-items: center; justify-content: center; }
|
||||||
.confirm-popup { background-color: #ffffff; width: 80%; max-width: 320px; border-radius: 12px; overflow: hidden; }
|
.confirm-popup { background-color: #ffffff; width: 80%; max-width: 320px; border-radius: 12px; overflow: hidden; }
|
||||||
|
|
||||||
.popup-header, .form-header { padding: 20px 15px; border-bottom: 1px solid #e5e5e5; display: flex; align-items: center; justify-content: space-between; }
|
.confirm-header { padding: 24px 0 12px; text-align: center; }
|
||||||
.popup-title, .form-title { font-size: 18px; font-weight: bold; color: #333333; }
|
.confirm-title { font-size: 17px; font-weight: bold; color: #333; }
|
||||||
.popup-close, .form-close { font-size: 24px; color: #999999; padding: 5px; }
|
.confirm-content { padding: 0 24px 24px; text-align: center; }
|
||||||
|
.confirm-message { font-size: 14px; color: #666; line-height: 1.5; }
|
||||||
.address-list-container, .form-content { flex: 1; padding: 15px; }
|
.confirm-buttons { display: flex; flex-direction: row; border-top: 0.5px solid #eee; }
|
||||||
.popup-address-item { padding: 15px; margin-bottom: 10px; border: 1px solid #e5e5e5; border-radius: 8px; position: relative; }
|
.confirm-btn { flex: 1; height: 48px; line-height: 48px; text-align: center; font-size: 16px; background-color: #ffffff; border: none; border-radius: 0; }
|
||||||
.popup-address-header { display: flex; align-items: center; margin-bottom: 10px; }
|
.confirm-btn.cancel { color: #666; border-right: 0.5px solid #eee; }
|
||||||
.popup-address-name { font-size: 16px; font-weight: bold; color: #333333; margin-right: 15px; }
|
.confirm-btn.confirm { color: #ff5000; font-weight: bold; }
|
||||||
.popup-address-phone { font-size: 14px; color: #666666; margin-right: 10px; }
|
|
||||||
.popup-default-tag { background-color: #ff4757; padding: 2px 8px; border-radius: 10px; }
|
|
||||||
.popup-tag-text { color: #ffffff; font-size: 12px; }
|
|
||||||
.popup-address-detail { font-size: 14px; color: #333333; line-height: 1.4; }
|
|
||||||
|
|
||||||
.popup-empty-address { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; }
|
|
||||||
.popup-empty-icon { font-size: 60px; margin-bottom: 15px; }
|
|
||||||
.popup-empty-text { font-size: 16px; color: #999999; }
|
|
||||||
.popup-add-address-btn { background-color: #007aff; margin: 15px; padding: 15px; border-radius: 8px; display: flex; align-items: center; justify-content: center; }
|
|
||||||
.popup-btn-icon { color: #ffffff; font-size: 20px; margin-right: 10px; }
|
|
||||||
.popup-btn-text { color: #ffffff; font-size: 16px; font-weight: bold; }
|
|
||||||
|
|
||||||
.form-item { margin-bottom: 20px; }
|
|
||||||
.form-label { font-size: 14px; color: #333333; margin-bottom: 8px; }
|
|
||||||
.form-input { width: 100%; padding: 12px; border: 1px solid #e5e5e5; border-radius: 8px; font-size: 14px; color: #333333; box-sizing: border-box; }
|
|
||||||
.form-input-readonly { background-color: #f9f9f9; color: #666666; }
|
|
||||||
.region-inputs { display: flex; justify-content: space-between; }
|
|
||||||
.region-input { flex: 1; margin-right: 10px; }
|
|
||||||
.region-input:last-child { margin-right: 0; }
|
|
||||||
.form-textarea { width: 100%; min-height: 80px; padding: 12px; border: 1px solid #e5e5e5; border-radius: 8px; font-size: 14px; color: #333333; box-sizing: border-box; }
|
|
||||||
.smart-address-input { min-height: 60px; }
|
|
||||||
.smart-tip { font-size: 12px; color: #999999; margin-top: 5px; }
|
|
||||||
.checkbox-item { margin-top: 20px; }
|
|
||||||
.checkbox-wrapper { display: flex; align-items: center; }
|
|
||||||
.checkbox { width: 20px; height: 20px; border: 1px solid #e5e5e5; border-radius: 4px; margin-right: 10px; display: flex; align-items: center; justify-content: center; }
|
|
||||||
.checkbox.checked { background-color: #007aff; border-color: #007aff; }
|
|
||||||
.checkbox-check { color: #ffffff; font-size: 14px; }
|
|
||||||
.checkbox-label { font-size: 14px; color: #333333; }
|
|
||||||
|
|
||||||
.form-buttons { display: flex; padding: 15px; border-top: 1px solid #e5e5e5; }
|
|
||||||
.form-cancel-btn { flex: 1; background-color: #f5f5f5; color: #333333; padding: 12px; border-radius: 8px; font-size: 16px; border: none; margin-right: 10px; }
|
|
||||||
.form-submit-btn { flex: 1; background-color: #007aff; color: #ffffff; padding: 12px; border-radius: 8px; font-size: 16px; border: none; }
|
|
||||||
|
|
||||||
.confirm-header { padding: 20px 0 10px; text-align: center; }
|
|
||||||
.confirm-title { font-size: 18px; font-weight: bold; color: #333333; }
|
|
||||||
.confirm-content { padding: 0 20px 20px; text-align: center; }
|
|
||||||
.confirm-message { font-size: 16px; color: #666666; }
|
|
||||||
.confirm-buttons { display: flex; border-top: 1px solid #e5e5e5; }
|
|
||||||
.confirm-btn { flex: 1; height: 50px; line-height: 50px; text-align: center; font-size: 16px; background-color: #ffffff; border: none; border-radius: 0; }
|
|
||||||
.confirm-btn.cancel { color: #666666; border-right: 1px solid #e5e5e5; }
|
|
||||||
.confirm-btn.confirm { color: #007aff; font-weight: bold; }
|
|
||||||
</style>
|
</style>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="coupons-page">
|
<view class="coupons-page">
|
||||||
<view class="coupon-list">
|
<view class="coupon-list">
|
||||||
<view v-if="coupons.length === 0" class="empty-state">
|
<view v-if="coupons.length === 0" class="empty-state">
|
||||||
|
|||||||
@@ -4526,10 +4526,38 @@
|
|||||||
- 使用单行字符串格式:`'*, table1(*), table2(field1, field2)'`
|
- 使用单行字符串格式:`'*, table1(*), table2(field1, field2)'`
|
||||||
- 如果关联查询失败,使用替代方案
|
- 如果关联查询失败,使用替代方案
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
一百二十七、Android 特有 UI 与交互规范(最新补充)
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
1. 图标兼容性(Emoji 风险)
|
||||||
|
- **现象**:在 Android 真机上直接使用 Emoji(如 🛒、❤️)可能导致显示不全、模糊或样式不统一。
|
||||||
|
- **规范**:所有功能图标**必须使用本地 PNG 图片**。
|
||||||
|
- **路径**:底部导航图标放在 `static/tabbar/`,业务小图标放在 `pages/mall/consumer/icons/`。
|
||||||
|
- **TabBar 特别强调**:`pages.json` 中的 `tabBar` 图标路径严禁使用 `.svg`,必须使用 `.png` 以确保安卓稳定性。
|
||||||
|
|
||||||
|
2. 布局高度计算修复
|
||||||
|
- **场景**:Chat 页面或带有固定底部的详情页。
|
||||||
|
- **规范**:`scroll-view` 的父级必须是 `flex: column`,且调用 `scroll-view` 时应配合 `flex: 1; height: 0;`。
|
||||||
|
- **双重滚动校准**:Android 初始渲染可能导致滚动不到底。
|
||||||
|
```uts
|
||||||
|
setTimeout(() => { this.scrollTop = 99999 }, 200); // 首次快速定位
|
||||||
|
setTimeout(() => { this.scrollTop = 99999 + Math.random() }, 500); // 二次精准校准
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 空状态展示(去行业化文案)
|
||||||
|
- **规范**:在数据加载中或为空时,严禁使用行业特定干扰词(如“暂无药品”)。
|
||||||
|
- **方案**:统一使用“正在加载商品...”或“该分类下暂无商品”,并辅以 `loading-state` 动画,提升加载感知。
|
||||||
|
|
||||||
|
4. SKU 解析与展示规范
|
||||||
|
- **规范**:从数据库返回的 JSON 规格数据(如 `sku_spec_attr`)必须通过格式化函数转换为易读文本。
|
||||||
|
- **示例**:`[{"key":"规格","value":"10mg*7片"}]` -> `规格: 10mg*7片`。
|
||||||
|
- **技巧**:在 `uts` 中处理 `UTSJSONObject` 时,推荐使用 `getString("value")` 进行容错处理。
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
文档结束
|
文档结束
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*最后更新:2026-03-03*
|
*最后更新:2026-03-04*
|
||||||
|
|||||||
@@ -1,304 +1,591 @@
|
|||||||
|
<!-- 收藏页面 -->
|
||||||
<template>
|
<template>
|
||||||
<scroll-view class="favorites-page" direction="vertical">
|
<view class="favorites-page">
|
||||||
<view class="product-grid">
|
<view class="favorites-header">
|
||||||
<view v-if="favorites.length === 0" class="empty-state">
|
<view v-if="favorites.length > 0" class="header-actions">
|
||||||
<text class="empty-icon">❤️</text>
|
<text class="action-btn" @click="toggleEditMode">{{ isEditMode ? '完成' : '编辑' }}</text>
|
||||||
<text class="empty-text">暂无收藏商品</text>
|
<text class="action-btn" @click="clearAll">清空</text>
|
||||||
<button class="go-shopping-btn" @click="goShopping">去逛逛</button>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else v-for="(product, index) in favorites" :key="index" class="product-item" @click="goToDetail(product.id)">
|
<scroll-view class="favorites-content" :scroll-y="true">
|
||||||
<image :src="product.main_image_url" class="product-image" mode="aspectFill" />
|
<view v-if="favorites.length === 0 && !isLoading" class="empty-favorites">
|
||||||
<text class="product-name" :lines="2">{{ product.name }}</text>
|
<text class="empty-icon">❤️</text>
|
||||||
<view class="product-bottom">
|
<text class="empty-text">暂无收藏商品</text>
|
||||||
<text class="product-price">¥{{ product.price }}</text>
|
<text class="empty-subtext">快去收藏喜欢的商品吧</text>
|
||||||
<view class="product-add-btn" @click.stop="addToCart(product)">
|
<button class="go-shopping-btn" @click="goShopping">去逛逛</button>
|
||||||
<text class="add-icon">+</text>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
<view class="product-group">
|
||||||
</view>
|
<view class="group-items">
|
||||||
</view>
|
<view v-for="(product, index) in favorites" :key="index" class="product-item">
|
||||||
</scroll-view>
|
<view v-if="isEditMode" class="item-selector" @click="toggleSelect(product)">
|
||||||
|
<view :class="['select-icon', { selected: product.selected === true }]">
|
||||||
|
<text v-if="product.selected === true" class="icon-text">✓</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="item-content" @click="viewProduct(product)">
|
||||||
|
<image class="product-image" :src="product.main_image_url" mode="aspectFill" />
|
||||||
|
<text class="product-name" :lines="2">{{ product.name }}</text>
|
||||||
|
<view class="product-bottom">
|
||||||
|
<text class="product-price">¥{{ product.price }}</text>
|
||||||
|
<view v-if="!isEditMode" class="product-add-btn" @click.stop="addToCart(product)">
|
||||||
|
<text class="add-icon">+</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="isLoading" class="loading-more">
|
||||||
|
<text class="loading-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="!isLoading && favorites.length > 0" class="no-more">
|
||||||
|
<text class="no-more-text">没有更多了</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<view v-if="isEditMode && favorites.length > 0" class="edit-bar">
|
||||||
|
<view class="select-all" @click="toggleSelectAll">
|
||||||
|
<view :class="['all-select-icon', { selected: isAllSelected }]">
|
||||||
|
<text v-if="isAllSelected" class="icon-text">✓</text>
|
||||||
|
</view>
|
||||||
|
<text class="select-all-text">全选</text>
|
||||||
|
</view>
|
||||||
|
<view class="delete-btn" @click="deleteSelected">
|
||||||
|
<text class="delete-text">删除({{ selectedCount }})</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||||
import type { Product } from '@/utils/supabaseService.uts'
|
|
||||||
|
|
||||||
const favorites = ref<Array<Product>>([])
|
type FavoriteType = {
|
||||||
|
id: string
|
||||||
const addToCart = async (product: Product) => {
|
name: string
|
||||||
uni.showLoading({ title: '检查商品...' })
|
price: number
|
||||||
try {
|
main_image_url: string
|
||||||
const merchantId = product.merchant_id ?? product.shop_id ?? ''
|
merchant_id: string
|
||||||
|
selected: boolean
|
||||||
// 检查商品是否有SKU
|
|
||||||
const skus = await supabaseService.getProductSkus(product.id)
|
|
||||||
uni.hideLoading()
|
|
||||||
|
|
||||||
if (skus.length > 0) {
|
|
||||||
// 有规格,提示并跳转到商品详情页选择规格
|
|
||||||
uni.showToast({ title: '请选择规格', icon: 'none' })
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/mall/consumer/product-detail?id=' + product.id
|
|
||||||
})
|
|
||||||
}, 500)
|
|
||||||
} else {
|
|
||||||
// 无规格,直接加入购物车
|
|
||||||
uni.showLoading({ title: '添加中...' })
|
|
||||||
const success = await supabaseService.addToCart(product.id, 1, '', merchantId)
|
|
||||||
uni.hideLoading()
|
|
||||||
if (success) {
|
|
||||||
uni.showToast({ title: '已添加到购物车', icon: 'success' })
|
|
||||||
} else {
|
|
||||||
uni.showToast({ title: '添加失败', icon: 'none' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('添加到购物车异常', e)
|
|
||||||
uni.hideLoading()
|
|
||||||
uni.showToast({ title: '操作失败', icon: 'none' })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const favorites = ref<FavoriteType[]>([])
|
||||||
|
const isEditMode = ref<boolean>(false)
|
||||||
|
const isLoading = ref<boolean>(false)
|
||||||
|
|
||||||
|
const selectedCount = computed((): number => {
|
||||||
|
return favorites.value.filter((item): Boolean => item.selected === true).length
|
||||||
|
})
|
||||||
|
|
||||||
|
const isAllSelected = computed((): boolean => {
|
||||||
|
return favorites.value.length > 0 && favorites.value.every((item): Boolean => item.selected === true)
|
||||||
|
})
|
||||||
|
|
||||||
const loadFavorites = async () => {
|
const loadFavorites = async () => {
|
||||||
const res = await supabaseService.getFavorites()
|
isLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await supabaseService.getFavorites()
|
||||||
|
console.log('收藏数据加载完成,数量:', res.length)
|
||||||
|
|
||||||
const productList: Product[] = []
|
const productList: FavoriteType[] = []
|
||||||
for (let i = 0; i < res.length; i++) {
|
for (let i = 0; i < res.length; i++) {
|
||||||
const item = res[i]
|
const item = res[i]
|
||||||
let prod: any | null = null
|
let prod: any | null = null
|
||||||
let itemObj: UTSJSONObject | null = null
|
let itemObj: UTSJSONObject | null = null
|
||||||
|
|
||||||
if (item instanceof UTSJSONObject) {
|
if (item instanceof UTSJSONObject) {
|
||||||
itemObj = item as UTSJSONObject
|
itemObj = item as UTSJSONObject
|
||||||
prod = itemObj.get('ml_products')
|
prod = itemObj.get('ml_products')
|
||||||
} else {
|
} else {
|
||||||
itemObj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
|
itemObj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
|
||||||
prod = itemObj.get('ml_products')
|
prod = itemObj.get('ml_products')
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = '/static/default-product.png'
|
let image = '/static/default-product.png'
|
||||||
let id = ''
|
let id = ''
|
||||||
let name = '未知商品'
|
let name = '未知商品'
|
||||||
let price = 0
|
let price = 0
|
||||||
let sales = 0
|
let merchantId = ''
|
||||||
let merchantId = ''
|
|
||||||
|
|
||||||
if (prod != null) {
|
if (prod != null) {
|
||||||
let prodObj: UTSJSONObject
|
let prodObj: UTSJSONObject
|
||||||
if (prod instanceof UTSJSONObject) {
|
if (prod instanceof UTSJSONObject) {
|
||||||
prodObj = prod as UTSJSONObject
|
prodObj = prod as UTSJSONObject
|
||||||
} else {
|
} else {
|
||||||
prodObj = JSON.parse(JSON.stringify(prod)) as UTSJSONObject
|
prodObj = JSON.parse(JSON.stringify(prod)) as UTSJSONObject
|
||||||
}
|
}
|
||||||
|
|
||||||
id = prodObj.getString('id') ?? ''
|
id = prodObj.getString('id') ?? ''
|
||||||
name = prodObj.getString('name') ?? '未知商品'
|
name = prodObj.getString('name') ?? '未知商品'
|
||||||
price = prodObj.getNumber('base_price') ?? 0
|
price = prodObj.getNumber('base_price') ?? 0
|
||||||
image = prodObj.getString('main_image_url') ?? image
|
image = prodObj.getString('main_image_url') ?? image
|
||||||
sales = prodObj.getNumber('sale_count') ?? 0
|
merchantId = prodObj.getString('merchant_id') ?? ''
|
||||||
merchantId = prodObj.getString('merchant_id') ?? ''
|
|
||||||
|
|
||||||
if (image === '/static/default-product.png') {
|
if (image === '/static/default-product.png') {
|
||||||
const imgUrls = prodObj.getString('image_urls')
|
const imgUrls = prodObj.getString('image_urls')
|
||||||
if (imgUrls != null) {
|
if (imgUrls != null) {
|
||||||
try {
|
try {
|
||||||
const arr = JSON.parse(imgUrls)
|
const arr = JSON.parse(imgUrls)
|
||||||
if (Array.isArray(arr) && arr.length > 0) {
|
if (Array.isArray(arr) && arr.length > 0) {
|
||||||
image = arr[0] as string
|
image = arr[0] as string
|
||||||
}
|
}
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (itemObj != null) {
|
if (itemObj != null) {
|
||||||
id = itemObj.getString('target_id') ?? ''
|
id = itemObj.getString('target_id') ?? ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const product: Product = {
|
const product: FavoriteType = {
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
price: price,
|
price: price,
|
||||||
category_id: '',
|
main_image_url: image,
|
||||||
merchant_id: merchantId,
|
merchant_id: merchantId,
|
||||||
main_image_url: image,
|
selected: false
|
||||||
sale_count: sales
|
}
|
||||||
} as Product
|
productList.push(product)
|
||||||
productList.push(product)
|
}
|
||||||
}
|
favorites.value = productList
|
||||||
favorites.value = productList
|
console.log('收藏列表更新完成,数量:', favorites.value.length)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载收藏失败', e)
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleEditMode = () => {
|
||||||
|
isEditMode.value = !isEditMode.value
|
||||||
|
for (let i = 0; i < favorites.value.length; i++) {
|
||||||
|
favorites.value[i].selected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearAll = () => {
|
||||||
|
if (favorites.value.length === 0) return
|
||||||
|
|
||||||
|
uni.showModal({
|
||||||
|
title: '清空收藏',
|
||||||
|
content: '确定要清空所有收藏商品吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showLoading({ title: '清空中...' })
|
||||||
|
|
||||||
|
const productIds: string[] = []
|
||||||
|
for (let i = 0; i < favorites.value.length; i++) {
|
||||||
|
productIds.push(favorites.value[i].id)
|
||||||
|
}
|
||||||
|
|
||||||
|
let completed = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < productIds.length; i++) {
|
||||||
|
supabaseService.toggleFavorite(productIds[i]).then(() => {
|
||||||
|
completed++
|
||||||
|
if (completed === productIds.length) {
|
||||||
|
uni.hideLoading()
|
||||||
|
favorites.value = []
|
||||||
|
uni.showToast({
|
||||||
|
title: '已清空',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
completed++
|
||||||
|
if (completed === productIds.length) {
|
||||||
|
uni.hideLoading()
|
||||||
|
loadFavorites()
|
||||||
|
uni.showToast({
|
||||||
|
title: '部分清空失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleSelect = (item: FavoriteType) => {
|
||||||
|
item.selected = !(item.selected === true)
|
||||||
|
favorites.value = [...favorites.value]
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleSelectAll = () => {
|
||||||
|
const newSelectedState = !isAllSelected.value
|
||||||
|
for (let i = 0; i < favorites.value.length; i++) {
|
||||||
|
favorites.value[i].selected = newSelectedState
|
||||||
|
}
|
||||||
|
favorites.value = [...favorites.value]
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSelected = () => {
|
||||||
|
const selectedItems = favorites.value.filter((item): Boolean => item.selected === true)
|
||||||
|
if (selectedItems.length === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请选择要删除的商品',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除收藏',
|
||||||
|
content: `确定要删除选中的 ${selectedItems.length} 个商品吗?`,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showLoading({ title: '删除中...' })
|
||||||
|
|
||||||
|
let completed = 0
|
||||||
|
const total = selectedItems.length
|
||||||
|
|
||||||
|
for (let i = 0; i < selectedItems.length; i++) {
|
||||||
|
supabaseService.toggleFavorite(selectedItems[i].id).then(() => {
|
||||||
|
completed++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
loadFavorites()
|
||||||
|
uni.showToast({
|
||||||
|
title: '已删除',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
completed++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
loadFavorites()
|
||||||
|
uni.showToast({
|
||||||
|
title: '部分删除失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewProduct = (product: FavoriteType) => {
|
||||||
|
if (isEditMode.value) {
|
||||||
|
toggleSelect(product)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/mall/consumer/product-detail?id=${product.id}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addToCart = async (product: FavoriteType) => {
|
||||||
|
uni.showLoading({ title: '检查商品...' })
|
||||||
|
try {
|
||||||
|
const merchantId = product.merchant_id ?? ''
|
||||||
|
|
||||||
|
const skus = await supabaseService.getProductSkus(product.id)
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (skus.length > 0) {
|
||||||
|
uni.showToast({ title: '请选择规格', icon: 'none' })
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/mall/consumer/product-detail?id=' + product.id
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
uni.showLoading({ title: '添加中...' })
|
||||||
|
const success = await supabaseService.addToCart(product.id, 1, '', merchantId)
|
||||||
|
uni.hideLoading()
|
||||||
|
if (success) {
|
||||||
|
uni.showToast({ title: '已添加到购物车', icon: 'success' })
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '添加失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('添加到购物车异常', e)
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '操作失败', icon: 'none' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const goShopping = () => {
|
const goShopping = () => {
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: '/pages/mall/consumer/index'
|
url: '/pages/mall/consumer/index'
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const goToDetail = (id: string) => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pages/mall/consumer/product-detail?productId=${id}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeFavorite = (id: string) => {
|
|
||||||
uni.showModal({
|
|
||||||
title: '取消收藏',
|
|
||||||
content: '确定要取消收藏该商品吗?',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
supabaseService.toggleFavorite(id).then((isStillFavorite) => {
|
|
||||||
if (!isStillFavorite) {
|
|
||||||
const index = favorites.value.findIndex((item): Boolean => {
|
|
||||||
return item.id === id
|
|
||||||
})
|
|
||||||
if (index !== -1) {
|
|
||||||
favorites.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
uni.showToast({
|
|
||||||
title: '已取消收藏',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: '取消失败',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadFavorites()
|
loadFavorites()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
.favorites-page {
|
.favorites-page {
|
||||||
padding: 15px;
|
display: flex;
|
||||||
background-color: #f5f5f5;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-grid {
|
.favorites-header {
|
||||||
display: flex;
|
background-color: #ffffff;
|
||||||
flex-direction: row;
|
padding: 15px;
|
||||||
flex-wrap: wrap;
|
border-bottom: 1px solid #e5e5e5;
|
||||||
justify-content: space-between;
|
display: flex;
|
||||||
width: 100%;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
.header-actions {
|
||||||
width: 100%;
|
display: flex;
|
||||||
display: flex;
|
flex-direction: row;
|
||||||
flex-direction: column;
|
flex: 1;
|
||||||
align-items: center;
|
justify-content: flex-end;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
padding-top: 100px;
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
color: #007aff;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 5px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorites-content {
|
||||||
|
flex: 1;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-favorites {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 80px 20px;
|
||||||
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-icon {
|
.empty-icon {
|
||||||
font-size: 60px;
|
font-size: 80px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
color: #ddd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-text {
|
.empty-text {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #999;
|
color: #666666;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-subtext {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999999;
|
||||||
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.go-shopping-btn {
|
.go-shopping-btn {
|
||||||
background-color: #ff5000;
|
background-color: #007aff;
|
||||||
color: white;
|
color: #ffffff;
|
||||||
padding: 8px 24px;
|
padding: 10px 40px;
|
||||||
border-radius: 20px;
|
border-radius: 25px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border: none;
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-group {
|
||||||
|
background-color: #ffffff;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-items {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-item {
|
.product-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 48%;
|
width: 48%;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-selector {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
z-index: 10;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(255,255,255,0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-icon.selected {
|
||||||
|
background-color: #007aff;
|
||||||
|
border-color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-text {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-image {
|
.product-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 170px;
|
height: 170px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-name {
|
.product-name {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-bottom {
|
.product-bottom {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 8px 8px;
|
padding: 0 8px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-price {
|
.product-price {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #ff5000;
|
color: #ff5000;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-add-btn {
|
.product-add-btn {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
background-color: #ff5000;
|
background-color: #ff5000;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-icon {
|
.add-icon {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PC/Tablet Responsive */
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.product-item {
|
.product-item {
|
||||||
width: 31% !important;
|
width: 32% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.product-item {
|
.product-item {
|
||||||
width: 15% !important;
|
width: 16% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-grid, .header {
|
.favorites-content, .favorites-header {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-more,
|
||||||
|
.no-more {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text,
|
||||||
|
.no-more-text {
|
||||||
|
color: #999999;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-bar {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-all {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-select-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-select-icon.selected {
|
||||||
|
background-color: #007aff;
|
||||||
|
border-color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-all-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
background-color: #ff4757;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-text {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 足迹页面 -->
|
<!-- 足迹页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="footprint-page">
|
<view class="footprint-page">
|
||||||
<view class="footprint-header">
|
<view class="footprint-header">
|
||||||
@@ -747,3 +747,4 @@ onMounted(() => {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
BIN
pages/mall/consumer/icons/customer-service.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
pages/mall/consumer/icons/favorite-active.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
pages/mall/consumer/icons/favorite.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
2672
pages/mall/consumer/index copy 2.uvue
Normal file
@@ -1,4 +1,4 @@
|
|||||||
<!-- pages/mall/consumer/index.uvue -->
|
<!-- pages/mall/consumer/index.uvue -->
|
||||||
<template>
|
<template>
|
||||||
<view class="medic-home">
|
<view class="medic-home">
|
||||||
<!-- 智能顶部导航栏 - 添加滚动隐藏效果 -->
|
<!-- 智能顶部导航栏 - 添加滚动隐藏效果 -->
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
<text class="service-desc">三甲医生在线</text>
|
<text class="service-desc">三甲医生在线</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="service-card" @click="navigateToPrescription">
|
<view class="service-card" @click="navigateToPrescription">
|
||||||
<view class="service-icon" style="background: #4CAF50;">
|
<view class="service-icon" style="background: #ff5000;">
|
||||||
<text class="service-icon-text">📋</text>
|
<text class="service-icon-text">📋</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="service-name">电子处方</text>
|
<text class="service-name">电子处方</text>
|
||||||
@@ -595,7 +595,7 @@ const familyItems = [
|
|||||||
name: '消毒酒精',
|
name: '消毒酒精',
|
||||||
desc: '环境消毒',
|
desc: '环境消毒',
|
||||||
icon: '🧪',
|
icon: '🧪',
|
||||||
color: '#4CAF50',
|
color: '#ff5000',
|
||||||
categoryId: 'external'
|
categoryId: 'external'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1032,9 +1032,9 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: #4CAF50;
|
background-color: #ff5000;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(255, 80, 0, 0.15);
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1244,7 +1244,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-pill.active .tab-text {
|
.tab-pill.active .tab-text {
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,7 +1256,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.section-title.active {
|
.section-title.active {
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1292,7 +1292,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
.category-card:hover {
|
.category-card:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
border-color: var(--card-color, #4CAF50);
|
border-color: var(--card-color, #ff5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 二级分类样式 */
|
/* 二级分类样式 */
|
||||||
@@ -1371,7 +1371,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
border-radius: 22px;
|
border-radius: 22px;
|
||||||
background: var(--card-color, #4CAF50);
|
background: var(--card-color, #ff5000);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -1494,7 +1494,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
|
|
||||||
.news-more {
|
.news-more {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
/* cursor: pointer; removed for uvue support */
|
/* cursor: pointer; removed for uvue support */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1663,7 +1663,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
|
|
||||||
.section-icon {
|
.section-icon {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
margin-right: 8px; /* Replacement for gap */
|
margin-right: 8px; /* Replacement for gap */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1697,9 +1697,9 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sort-tab.active {
|
.sort-tab.active {
|
||||||
background: #4CAF50;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #4CAF50;
|
border-color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-tab:hover {
|
.sort-tab:hover {
|
||||||
@@ -1707,7 +1707,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sort-tab.active:hover {
|
.sort-tab.active:hover {
|
||||||
background: #388E3C;
|
background: #e64a00;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 产品网格 */
|
/* 产品网格 */
|
||||||
@@ -1786,7 +1786,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* gap: 6px; removed */
|
/* gap: 6px; removed */
|
||||||
background: #4CAF50;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -1918,9 +1918,9 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.filter-item.active {
|
.filter-item.active {
|
||||||
background: #4CAF50;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #4CAF50;
|
border-color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-item:hover {
|
.filter-item:hover {
|
||||||
@@ -1928,7 +1928,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.filter-item.active:hover {
|
.filter-item.active:hover {
|
||||||
background: #388E3C;
|
background: #e64a00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recommend-grid {
|
.recommend-grid {
|
||||||
@@ -2055,7 +2055,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
background: #4CAF50;
|
background: #ff5000;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -2064,7 +2064,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.add-to-cart:hover {
|
.add-to-cart:hover {
|
||||||
background: #388E3C;
|
background: #e64a00;
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2142,7 +2142,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border: 3px solid #f0f0f0;
|
border: 3px solid #f0f0f0;
|
||||||
border-top-color: #4CAF50;
|
border-top-color: #ff5000;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
@@ -2642,7 +2642,7 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
|
|
||||||
.sort-tab.active,
|
.sort-tab.active,
|
||||||
.filter-item.active {
|
.filter-item.active {
|
||||||
background: #4CAF50;
|
background: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2653,12 +2653,12 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
|
|
||||||
.sort-tab.active:hover,
|
.sort-tab.active:hover,
|
||||||
.filter-item.active:hover {
|
.filter-item.active:hover {
|
||||||
background: #388E3C;
|
background: #e64a00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tool-item {
|
.nav-tool-item {
|
||||||
background: rgba(76, 175, 80, 0.2);
|
background: rgba(255, 80, 0, 0.2);
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.manufacturer,
|
.manufacturer,
|
||||||
@@ -2669,3 +2669,4 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="logistics-page">
|
<view class="logistics-page">
|
||||||
<view class="logistics-header">
|
<view class="logistics-header">
|
||||||
<view class="product-info">
|
<view class="product-info">
|
||||||
@@ -296,3 +296,4 @@ onMounted(() => {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
1373
pages/mall/consumer/messages copy.uvue
Normal file
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="messages-page">
|
<view class="messages-page">
|
||||||
<!-- 智能顶部导航栏 - 与主页保持一致 -->
|
<!-- 智能顶部导航栏 - 与主页保持一致 -->
|
||||||
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
<view
|
<view
|
||||||
v-for="message in serviceMessages"
|
v-for="message in serviceMessages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
:class="['message-item', { unread: !message.read, active: message.active }]"
|
:class="['message-item', { active: message.active }]"
|
||||||
@click="startChatWithService(message)"
|
@click="startChatWithService(message)"
|
||||||
>
|
>
|
||||||
<view class="message-icon-wrapper">
|
<view class="message-icon-wrapper">
|
||||||
@@ -88,6 +88,7 @@
|
|||||||
<view v-else class="message-icon-default" :style="{ backgroundColor: message.color }">
|
<view v-else class="message-icon-default" :style="{ backgroundColor: message.color }">
|
||||||
<text class="message-icon-text">{{ message.icon }}</text>
|
<text class="message-icon-text">{{ message.icon }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="message.unreadCount > 0" class="unread-dot"></view>
|
||||||
<view v-if="message.online" class="online-dot"></view>
|
<view v-if="message.online" class="online-dot"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="message-content">
|
<view class="message-content">
|
||||||
@@ -96,17 +97,11 @@
|
|||||||
<text class="message-title">{{ message.title }}</text>
|
<text class="message-title">{{ message.title }}</text>
|
||||||
<text v-if="message.role" class="message-role">{{ message.role }}</text>
|
<text v-if="message.role" class="message-role">{{ message.role }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="message-header-right">
|
<text class="message-time">{{ message.time }}</text>
|
||||||
<text class="message-time">{{ message.time }}</text>
|
|
||||||
<text v-if="message.unreadCount > 0" class="message-unread-count">{{ message.unreadCount }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="message-preview-wrapper">
|
<view class="message-preview-wrapper">
|
||||||
<text class="message-preview">{{ message.content }}</text>
|
<text class="message-preview">{{ message.content }}</text>
|
||||||
<text v-if="message.lastMessage" class="last-message">{{ message.lastMessage }}</text>
|
<text v-if="message.unreadCount > 0" class="message-unread-count">{{ message.unreadCount > 99 ? '99+' : message.unreadCount }}</text>
|
||||||
</view>
|
|
||||||
<view v-if="message.tags" class="message-tags">
|
|
||||||
<text v-for="tag in message.tags" :key="tag" class="message-tag">{{ tag }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -123,19 +118,24 @@
|
|||||||
<view
|
<view
|
||||||
v-for="message in systemMessages"
|
v-for="message in systemMessages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
:class="['message-item', { unread: !message.read }]"
|
class="message-item"
|
||||||
@click="viewSystemMessage(message)"
|
@click="viewSystemMessage(message)"
|
||||||
>
|
>
|
||||||
<view class="message-icon-wrapper">
|
<view class="message-icon-wrapper">
|
||||||
<text class="message-icon">📢</text>
|
<text class="message-icon-text">📢</text>
|
||||||
|
<view v-if="!message.read" class="unread-dot"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="message-content">
|
<view class="message-content">
|
||||||
<view class="message-header">
|
<view class="message-header">
|
||||||
<text class="message-title">{{ message.title }}</text>
|
<view class="message-title-wrapper">
|
||||||
|
<text class="message-title">{{ message.title }}</text>
|
||||||
|
<text v-if="message.important" class="important-tag">重要</text>
|
||||||
|
</view>
|
||||||
<text class="message-time">{{ message.time }}</text>
|
<text class="message-time">{{ message.time }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="message-preview">{{ message.content }}</text>
|
<view class="message-preview-wrapper">
|
||||||
<view v-if="message.important" class="important-tag">重要</view>
|
<text class="message-preview">{{ message.content }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -145,22 +145,27 @@
|
|||||||
<view
|
<view
|
||||||
v-for="message in orderMessages"
|
v-for="message in orderMessages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
:class="['message-item', { unread: !message.read }]"
|
class="message-item"
|
||||||
@click="viewOrderMessage(message)"
|
@click="viewOrderMessage(message)"
|
||||||
>
|
>
|
||||||
<view class="message-icon-wrapper">
|
<view class="message-icon-wrapper">
|
||||||
<text class="message-icon">📦</text>
|
<text class="message-icon-text">📦</text>
|
||||||
|
<view v-if="!message.read" class="unread-dot"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="message-content">
|
<view class="message-content">
|
||||||
<view class="message-header">
|
<view class="message-header">
|
||||||
<text class="message-title">{{ message.title }}</text>
|
<view class="message-title-wrapper">
|
||||||
|
<text class="message-title">{{ message.title }}</text>
|
||||||
|
<view v-if="message.status" class="order-status-tag" :class="message.status">
|
||||||
|
{{ message.statusText }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<text class="message-time">{{ message.time }}</text>
|
<text class="message-time">{{ message.time }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="message-preview">{{ message.content }}</text>
|
<view class="message-preview-wrapper">
|
||||||
<text class="order-info" v-if="message.order_no">订单号: {{ message.order_no }}</text>
|
<text class="message-preview">{{ message.content }}</text>
|
||||||
<view v-if="message.status" class="order-status" :class="message.status">
|
|
||||||
{{ message.statusText }}
|
|
||||||
</view>
|
</view>
|
||||||
|
<text class="order-no-text" v-if="message.order_no">订单号: {{ message.order_no }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -649,9 +654,9 @@ const onRefresh = () => {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: #4CAF50;
|
background-color: #ff5000;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(255, 80, 0, 0.15);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@@ -758,8 +763,8 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-item.active {
|
.tab-item.active {
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
border-bottom-color: #4CAF50;
|
border-bottom-color: #ff5000;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,8 +831,8 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.service-status.online {
|
.service-status.online {
|
||||||
background: #E8F5E9;
|
background: #FFF3E0;
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-desc {
|
.service-desc {
|
||||||
@@ -885,37 +890,31 @@ const onRefresh = () => {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
flex-direction: row;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
align-items: center; /* 居中对齐头像和内容 */
|
||||||
transition: all 0.3s ease;
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||||
/* cursor: pointer; removed for uniapp-x support */
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-item:hover {
|
.message-item:active {
|
||||||
transform: translateY(-2px);
|
opacity: 0.7;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-item.unread {
|
.message-item.unread {
|
||||||
background-color: #f0f9f0;
|
background-color: white; /* 保持白色背景,通过红点示未读 */
|
||||||
border-left: 3px solid #4CAF50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-item.active {
|
|
||||||
border: 1px solid #4CAF50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-icon-wrapper {
|
.message-icon-wrapper {
|
||||||
width: 50px;
|
width: 48px;
|
||||||
height: 50px;
|
height: 48px;
|
||||||
border-radius: 25px;
|
border-radius: 24px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f8fafc;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 15px;
|
margin-right: 12px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -923,30 +922,40 @@ const onRefresh = () => {
|
|||||||
.message-icon-default {
|
.message-icon-default {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 25px;
|
border-radius: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-icon-text {
|
.message-icon-text {
|
||||||
font-size: 24px;
|
font-size: 22px;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-avatar {
|
.message-avatar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 25px;
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unread-dot {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: #ff5000;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1.5px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.online-dot {
|
.online-dot {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 2px;
|
bottom: 0;
|
||||||
right: 2px;
|
right: 0;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
background-color: #4CAF50;
|
background-color: #4cd964;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
}
|
}
|
||||||
@@ -954,81 +963,83 @@ const onRefresh = () => {
|
|||||||
.message-content {
|
.message-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-header {
|
.message-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-title-wrapper {
|
.message-title-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin-right: 10px;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-title {
|
.message-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #333;
|
color: #1a1a1a;
|
||||||
font-weight: bold;
|
font-weight: 500;
|
||||||
/* display: block; REMOVED for uniapp-x support */
|
|
||||||
margin-bottom: 4px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-role {
|
.message-role {
|
||||||
font-size: 12px;
|
font-size: 10px;
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
background: #E8F5E9;
|
background: #fff0eb;
|
||||||
padding: 2px 8px;
|
padding: 1px 6px;
|
||||||
border-radius: 10px;
|
border-radius: 4px;
|
||||||
/* display: inline-block; REMOVED for uniapp-x support */
|
margin-left: 6px;
|
||||||
}
|
flex-shrink: 0;
|
||||||
|
|
||||||
.message-header-right {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-end;
|
|
||||||
/* gap: 4px; removed for uniapp-x support */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-time {
|
.message-time {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #999;
|
color: #999;
|
||||||
white-space: nowrap;
|
margin-left: 10px;
|
||||||
margin-bottom: 4px; /* replaced gap */
|
flex-shrink: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.message-unread-count {
|
|
||||||
font-size: 11px;
|
|
||||||
color: white;
|
|
||||||
background: #FF5722;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 10px;
|
|
||||||
min-width: 18px;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-preview-wrapper {
|
.message-preview-wrapper {
|
||||||
margin-bottom: 8px;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-preview {
|
.message-preview {
|
||||||
font-size: 14px;
|
flex: 1;
|
||||||
|
font-size: 13px;
|
||||||
color: #666;
|
color: #666;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
margin-bottom: 4px;
|
|
||||||
/* display: -webkit-box; REMOVED for uniapp-x support */
|
|
||||||
/* -webkit-line-clamp: 2; REMOVED for uniapp-x support */
|
|
||||||
/* -webkit-box-orient: vertical; REMOVED for uniapp-x support */
|
|
||||||
lines: 2; /* UTS text truncation */
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis; /* Ensure standard CSS property is present */
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-unread-count {
|
||||||
|
font-size: 10px;
|
||||||
|
color: white;
|
||||||
|
background: #ff5000;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 10px;
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: bold;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.last-message {
|
.last-message {
|
||||||
@@ -1055,8 +1066,8 @@ const onRefresh = () => {
|
|||||||
|
|
||||||
.order-info {
|
.order-info {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
background-color: #E8F5E9;
|
background-color: #FFF3E0;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
/* display: inline-block; REMOVED for uniapp-x support */
|
/* display: inline-block; REMOVED for uniapp-x support */
|
||||||
@@ -1085,8 +1096,8 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.order-status.completed {
|
.order-status.completed {
|
||||||
background: #E8F5E9;
|
background: #FFF3E0;
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.important-tag {
|
.important-tag {
|
||||||
@@ -1155,26 +1166,65 @@ const onRefresh = () => {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.order-no-text {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-tag {
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-left: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-tag.shipping {
|
||||||
|
background: #e3f2fd;
|
||||||
|
color: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-tag.processing {
|
||||||
|
background: #fff8e1;
|
||||||
|
color: #ffb300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-tag.completed {
|
||||||
|
background: #fff0eb;
|
||||||
|
color: #ff5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.important-tag {
|
||||||
|
background-color: #ff5000;
|
||||||
|
color: white;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-left: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 空状态 */
|
||||||
.empty-messages {
|
.empty-messages {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 80px 20px;
|
padding-top: 100px;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-icon {
|
.empty-icon {
|
||||||
font-size: 80px;
|
font-size: 60px;
|
||||||
color: #ddd;
|
margin-bottom: 16px;
|
||||||
margin-bottom: 20px;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-title {
|
.empty-title {
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
color: #666;
|
color: #333;
|
||||||
margin-bottom: 10px;
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-desc {
|
.empty-desc {
|
||||||
@@ -1191,7 +1241,7 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.action-button {
|
.action-button {
|
||||||
background: linear-gradient(135deg, #4CAF50, #2E7D32);
|
background: linear-gradient(135deg, #ff5000, #e64a00);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
@@ -1310,7 +1360,7 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-item.active {
|
.tab-item.active {
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-service-info,
|
.customer-service-info,
|
||||||
@@ -1370,3 +1420,4 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="messages-page">
|
<view class="messages-page">
|
||||||
<!-- 智能顶部导航栏 - 与主页保持一致 -->
|
<!-- 鏅鸿兘椤堕儴瀵艰埅鏍?- 涓庝富椤典繚鎸佷竴鑷?-->
|
||||||
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view class="nav-container">
|
<view class="nav-container">
|
||||||
<text class="nav-title">消息中心</text>
|
<text class="nav-title">娑堟伅涓績</text>
|
||||||
<view class="nav-actions">
|
<view class="nav-actions">
|
||||||
<view class="action-btn" @click="clearAllUnread">
|
<view class="action-btn" @click="clearAllUnread">
|
||||||
<text class="action-icon">🧹</text>
|
<text class="action-icon">馃Ч</text>
|
||||||
<text class="action-text">一键已读</text>
|
<text class="action-text">涓€閿凡璇?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 导航栏占位符 -->
|
<!-- 瀵艰埅鏍忓崰浣嶇 -->
|
||||||
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 10) + 'px' }"></view>
|
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 10) + 'px' }"></view>
|
||||||
|
|
||||||
<!-- 消息分类标签 - 固定在顶部,方便随时切换 -->
|
<!-- 娑堟伅鍒嗙被鏍囩 - 鍥哄畾鍦ㄩ《閮紝鏂逛究闅忔椂鍒囨崲 -->
|
||||||
<view class="tabs-container">
|
<view class="tabs-container">
|
||||||
<view class="message-tabs">
|
<view class="message-tabs">
|
||||||
<view
|
<view
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 消息列表内容区 -->
|
<!-- 娑堟伅鍒楄〃鍐呭鍖?-->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
scroll-y
|
scroll-y
|
||||||
class="messages-content"
|
class="messages-content"
|
||||||
@@ -41,37 +41,37 @@
|
|||||||
:scroll-top="scrollTop"
|
:scroll-top="scrollTop"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- 客服消息 -->
|
<!-- 瀹㈡湇娑堟伅 -->
|
||||||
<view v-if="activeTab === 'service'" class="message-section">
|
<view v-if="activeTab === 'service'" class="message-section">
|
||||||
<!-- 在线客服卡片 -->
|
<!-- 鍦ㄧ嚎瀹㈡湇鍗$墖 -->
|
||||||
<view class="customer-service-info">
|
<view class="customer-service-info">
|
||||||
<view class="service-header">
|
<view class="service-header">
|
||||||
<text class="service-title">康乐医药在线客服</text>
|
<text class="service-title">搴蜂箰鍖昏嵂鍦ㄧ嚎瀹㈡湇</text>
|
||||||
<text class="service-status online">在线</text>
|
<text class="service-status online">鍦ㄧ嚎</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="service-desc">专业医药顾问在线解答,服务时间 9:00-22:00</text>
|
<text class="service-desc">涓撲笟鍖昏嵂椤鹃棶鍦ㄧ嚎瑙g瓟锛屾湇鍔℃椂闂?9:00-22:00</text>
|
||||||
|
|
||||||
<view class="service-categories">
|
<view class="service-categories">
|
||||||
<view class="category-item" @click="startQuickService('用药咨询')">
|
<view class="category-item" @click="startQuickService('鐢ㄨ嵂鍜ㄨ')">
|
||||||
<text class="category-icon">💊</text>
|
<text class="category-icon">馃拪</text>
|
||||||
<text class="category-name">用药咨询</text>
|
<text class="category-name">鐢ㄨ嵂鍜ㄨ</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="category-item" @click="startQuickService('处方咨询')">
|
<view class="category-item" @click="startQuickService('澶勬柟鍜ㄨ')">
|
||||||
<text class="category-icon">📋</text>
|
<text class="category-icon">馃搵</text>
|
||||||
<text class="category-name">处方咨询</text>
|
<text class="category-name">澶勬柟鍜ㄨ</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="category-item" @click="startQuickService('副作用咨询')">
|
<view class="category-item" @click="startQuickService('鍓綔鐢ㄥ挩璇?)">
|
||||||
<text class="category-icon">⚠️</text>
|
<text class="category-icon">鈿狅笍</text>
|
||||||
<text class="category-name">副作用咨询</text>
|
<text class="category-name">鍓綔鐢ㄥ挩璇?/text>
|
||||||
</view>
|
</view>
|
||||||
<view class="category-item" @click="startQuickService('药品配送')">
|
<view class="category-item" @click="startQuickService('鑽搧閰嶉€?)">
|
||||||
<text class="category-icon">🚚</text>
|
<text class="category-icon">馃殮</text>
|
||||||
<text class="category-name">药品配送</text>
|
<text class="category-name">鑽搧閰嶉€?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 客服消息列表 -->
|
<!-- 瀹㈡湇娑堟伅鍒楄〃 -->
|
||||||
<view
|
<view
|
||||||
v-for="message in serviceMessages"
|
v-for="message in serviceMessages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
@@ -111,14 +111,14 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 客服系统提示 -->
|
<!-- 瀹㈡湇绯荤粺鎻愮ず -->
|
||||||
<view class="service-tips">
|
<view class="service-tips">
|
||||||
<text class="tip-icon">💡</text>
|
<text class="tip-icon">馃挕</text>
|
||||||
<text class="tip-text">温馨提示:请勿相信任何要求转账、付款的信息,谨防诈骗</text>
|
<text class="tip-text">娓╅Θ鎻愮ず锛氳鍕跨浉淇′换浣曡姹傝浆璐︺€佷粯娆剧殑淇℃伅锛岃皑闃茶瘓楠?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 系统通知 -->
|
<!-- 绯荤粺閫氱煡 -->
|
||||||
<view v-if="activeTab === 'system'" class="message-section">
|
<view v-if="activeTab === 'system'" class="message-section">
|
||||||
<view
|
<view
|
||||||
v-for="message in systemMessages"
|
v-for="message in systemMessages"
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
@click="viewSystemMessage(message)"
|
@click="viewSystemMessage(message)"
|
||||||
>
|
>
|
||||||
<view class="message-icon-wrapper">
|
<view class="message-icon-wrapper">
|
||||||
<text class="message-icon">📢</text>
|
<text class="message-icon">馃摙</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="message-content">
|
<view class="message-content">
|
||||||
<view class="message-header">
|
<view class="message-header">
|
||||||
@@ -135,12 +135,12 @@
|
|||||||
<text class="message-time">{{ message.time }}</text>
|
<text class="message-time">{{ message.time }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="message-preview">{{ message.content }}</text>
|
<text class="message-preview">{{ message.content }}</text>
|
||||||
<view v-if="message.important" class="important-tag">重要</view>
|
<view v-if="message.important" class="important-tag">閲嶈</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单消息 -->
|
<!-- 璁㈠崟娑堟伅 -->
|
||||||
<view v-if="activeTab === 'order'" class="message-section">
|
<view v-if="activeTab === 'order'" class="message-section">
|
||||||
<view
|
<view
|
||||||
v-for="message in orderMessages"
|
v-for="message in orderMessages"
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
@click="viewOrderMessage(message)"
|
@click="viewOrderMessage(message)"
|
||||||
>
|
>
|
||||||
<view class="message-icon-wrapper">
|
<view class="message-icon-wrapper">
|
||||||
<text class="message-icon">📦</text>
|
<text class="message-icon">馃摝</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="message-content">
|
<view class="message-content">
|
||||||
<view class="message-header">
|
<view class="message-header">
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
<text class="message-time">{{ message.time }}</text>
|
<text class="message-time">{{ message.time }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="message-preview">{{ message.content }}</text>
|
<text class="message-preview">{{ message.content }}</text>
|
||||||
<text class="order-info" v-if="message.order_no">订单号: {{ message.order_no }}</text>
|
<text class="order-info" v-if="message.order_no">璁㈠崟鍙? {{ message.order_no }}</text>
|
||||||
<view v-if="message.status" class="order-status" :class="message.status">
|
<view v-if="message.status" class="order-status" :class="message.status">
|
||||||
{{ message.statusText }}
|
{{ message.statusText }}
|
||||||
</view>
|
</view>
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 优惠活动 -->
|
<!-- 浼樻儬娲诲姩 -->
|
||||||
<view v-if="activeTab === 'promo'" class="message-section">
|
<view v-if="activeTab === 'promo'" class="message-section">
|
||||||
<view
|
<view
|
||||||
v-for="message in promoMessages"
|
v-for="message in promoMessages"
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
@click="viewPromoMessage(message)"
|
@click="viewPromoMessage(message)"
|
||||||
>
|
>
|
||||||
<view class="message-icon-wrapper">
|
<view class="message-icon-wrapper">
|
||||||
<text class="message-icon">🎁</text>
|
<text class="message-icon">馃巵</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="message-content">
|
<view class="message-content">
|
||||||
<view class="message-header">
|
<view class="message-header">
|
||||||
@@ -183,30 +183,30 @@
|
|||||||
</view>
|
</view>
|
||||||
<text class="message-preview">{{ message.content }}</text>
|
<text class="message-preview">{{ message.content }}</text>
|
||||||
<view v-if="message.coupon" class="coupon-info" @click.stop="claimCoupon(message)">
|
<view v-if="message.coupon" class="coupon-info" @click.stop="claimCoupon(message)">
|
||||||
<text class="coupon-text">{{ message.coupon }}优惠券</text>
|
<text class="coupon-text">{{ message.coupon }}浼樻儬鍒?/text>
|
||||||
<text class="coupon-expiry">有效期至 {{ message.expiry }}</text>
|
<text class="coupon-expiry">鏈夋晥鏈熻嚦 {{ message.expiry }}</text>
|
||||||
<text class="coupon-action">{{ message.claimed ? '已领取' : '点击领取' }}</text>
|
<text class="coupon-action">{{ message.claimed ? '宸查鍙? : '鐐瑰嚮棰嗗彇' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 绌虹姸鎬?-->
|
||||||
<view v-if="!loading && currentMessages.length === 0 && activeTab !== 'service'" class="empty-messages">
|
<view v-if="!loading && currentMessages.length === 0 && activeTab !== 'service'" class="empty-messages">
|
||||||
<text class="empty-icon">💬</text>
|
<text class="empty-icon">馃挰</text>
|
||||||
<text class="empty-title">暂无消息</text>
|
<text class="empty-title">鏆傛棤娑堟伅</text>
|
||||||
<text class="empty-desc">暂时没有新消息</text>
|
<text class="empty-desc">鏆傛椂娌℃湁鏂版秷鎭?/text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部安全区域 -->
|
<!-- 搴曢儴瀹夊叏鍖哄煙 -->
|
||||||
<view class="safe-area"></view>
|
<view class="safe-area"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 底部固定按钮 -->
|
<!-- 搴曢儴鍥哄畾鎸夐挳 -->
|
||||||
<view class="floating-action">
|
<view class="floating-action">
|
||||||
<button class="action-button" @click="startNewChat">
|
<button class="action-button" @click="startNewChat">
|
||||||
<text class="button-icon">✏️</text>
|
<text class="button-icon">鉁忥笍</text>
|
||||||
<text class="button-text">新建聊天</text>
|
<text class="button-text">鏂板缓鑱婂ぉ</text>
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -216,7 +216,7 @@
|
|||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import { supabaseService, type Notification, type ChatMessage } from '@/utils/supabaseService.uts'
|
import { supabaseService, type Notification, type ChatMessage } from '@/utils/supabaseService.uts'
|
||||||
|
|
||||||
// 响应式数据
|
// 鍝嶅簲寮忔暟鎹?
|
||||||
const activeTab = ref<string>('service')
|
const activeTab = ref<string>('service')
|
||||||
const refreshing = ref<boolean>(false)
|
const refreshing = ref<boolean>(false)
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -225,32 +225,32 @@ const statusBarHeight = ref(0)
|
|||||||
const scrollTop = ref(0)
|
const scrollTop = ref(0)
|
||||||
const scrollHeight = ref(0)
|
const scrollHeight = ref(0)
|
||||||
|
|
||||||
// 初始化页面布局数据
|
// 鍒濆鍖栭〉闈㈠竷灞€鏁版嵁
|
||||||
const initPage = () => {
|
const initPage = () => {
|
||||||
const systemInfo = uni.getSystemInfoSync()
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
statusBarHeight.value = systemInfo.statusBarHeight || 0
|
statusBarHeight.value = systemInfo.statusBarHeight || 0
|
||||||
|
|
||||||
// 计算滚动区域高度:屏幕高度 - 状态栏 - 导航栏(44) - 标签栏(42)
|
// 璁$畻婊氬姩鍖哄煙楂樺害锛氬睆骞曢珮搴?- 鐘舵€佹爮 - 瀵艰埅鏍?44) - 鏍囩鏍?42)
|
||||||
const windowHeight = systemInfo.windowHeight
|
const windowHeight = systemInfo.windowHeight
|
||||||
scrollHeight.value = windowHeight - statusBarHeight.value - 44 - 42
|
scrollHeight.value = windowHeight - statusBarHeight.value - 44 - 42
|
||||||
}
|
}
|
||||||
|
|
||||||
// 消息分类标签
|
// 娑堟伅鍒嗙被鏍囩
|
||||||
const messageTabs = reactive([
|
const messageTabs = reactive([
|
||||||
{ id: 'service', name: '客服消息', unread: 5 },
|
{ id: 'service', name: '瀹㈡湇娑堟伅', unread: 5 },
|
||||||
{ id: 'system', name: '系统通知', unread: 3 },
|
{ id: 'system', name: '绯荤粺閫氱煡', unread: 3 },
|
||||||
{ id: 'order', name: '订单消息', unread: 2 },
|
{ id: 'order', name: '璁㈠崟娑堟伅', unread: 2 },
|
||||||
{ id: 'promo', name: '优惠活动', unread: 2 }
|
{ id: 'promo', name: '浼樻儬娲诲姩', unread: 2 }
|
||||||
])
|
])
|
||||||
|
|
||||||
// Mock 客服消息数据
|
// Mock 瀹㈡湇娑堟伅鏁版嵁
|
||||||
const serviceMessages = reactive<any[]>([])
|
const serviceMessages = reactive<any[]>([])
|
||||||
const systemMessages = reactive<any[]>([])
|
const systemMessages = reactive<any[]>([])
|
||||||
const orderMessages = reactive<any[]>([])
|
const orderMessages = reactive<any[]>([])
|
||||||
// Mock 优惠活动数据
|
// Mock 浼樻儬娲诲姩鏁版嵁
|
||||||
const promoMessages = reactive<any[]>([])
|
const promoMessages = reactive<any[]>([])
|
||||||
|
|
||||||
// 计算当前显示的消息
|
// 璁$畻褰撳墠鏄剧ず鐨勬秷鎭?
|
||||||
const currentMessages = computed(() => {
|
const currentMessages = computed(() => {
|
||||||
switch (activeTab.value) {
|
switch (activeTab.value) {
|
||||||
case 'system': return systemMessages
|
case 'system': return systemMessages
|
||||||
@@ -261,14 +261,14 @@ const currentMessages = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 生命周期
|
// 鐢熷懡鍛ㄦ湡
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log('Messages Page Mounted')
|
console.log('Messages Page Mounted')
|
||||||
initPage()
|
initPage()
|
||||||
loadMessages()
|
loadMessages()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 简单的日期格式化
|
// 绠€鍗曠殑鏃ユ湡鏍煎紡鍖?
|
||||||
const formatTime = (isoString: string): string => {
|
const formatTime = (isoString: string): string => {
|
||||||
if (!isoString) return ''
|
if (!isoString) return ''
|
||||||
try {
|
try {
|
||||||
@@ -278,22 +278,22 @@ const formatTime = (isoString: string): string => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载消息
|
// 鍔犺浇娑堟伅
|
||||||
const loadMessages = async () => {
|
const loadMessages = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 清空现有Mock数据
|
// 娓呯┖鐜版湁Mock鏁版嵁
|
||||||
serviceMessages.length = 0
|
serviceMessages.length = 0
|
||||||
systemMessages.length = 0
|
systemMessages.length = 0
|
||||||
orderMessages.length = 0
|
orderMessages.length = 0
|
||||||
promoMessages.length = 0
|
promoMessages.length = 0
|
||||||
|
|
||||||
// 1. 获取通知 (系统、订单、优惠)
|
// 1. 鑾峰彇閫氱煡 (绯荤粺銆佽鍗曘€佷紭鎯?
|
||||||
const notes = await supabaseService.getUserNotifications()
|
const notes = await supabaseService.getUserNotifications()
|
||||||
|
|
||||||
notes.forEach((note: Notification) => {
|
notes.forEach((note: Notification) => {
|
||||||
// 这里使用 any 类型构建对象,以匹配 reactive 数组的结构
|
// 杩欓噷浣跨敤 any 绫诲瀷鏋勫缓瀵硅薄锛屼互鍖归厤 reactive 鏁扮粍鐨勭粨鏋?
|
||||||
const item = {
|
const item = {
|
||||||
id: note.id,
|
id: note.id,
|
||||||
title: note.title,
|
title: note.title,
|
||||||
@@ -301,10 +301,10 @@ const loadMessages = async () => {
|
|||||||
time: formatTime(note.created_at || ''),
|
time: formatTime(note.created_at || ''),
|
||||||
read: note.is_read,
|
read: note.is_read,
|
||||||
type: note.type, // 'system', 'order', 'promotion' => 'promo'
|
type: note.type, // 'system', 'order', 'promotion' => 'promo'
|
||||||
// 默认填充字段以避免渲染报错
|
// 榛樿濉厖瀛楁浠ラ伩鍏嶆覆鏌撴姤閿?
|
||||||
avatar: note.icon_url,
|
avatar: note.icon_url,
|
||||||
important: note.type === 'system', // 简单逻辑
|
important: note.type === 'system', // 绠€鍗曢€昏緫
|
||||||
coupon: '点击查看',
|
coupon: '鐐瑰嚮鏌ョ湅',
|
||||||
expiry: '',
|
expiry: '',
|
||||||
claimed: false,
|
claimed: false,
|
||||||
order_no: '',
|
order_no: '',
|
||||||
@@ -330,15 +330,15 @@ const loadMessages = async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 2. 获取客服消息 (Chat)
|
// 2. 鑾峰彇瀹㈡湇娑堟伅 (Chat)
|
||||||
const chats = await supabaseService.getUserChatMessages()
|
const chats = await supabaseService.getUserChatMessages()
|
||||||
if (chats.length > 0) {
|
if (chats.length > 0) {
|
||||||
// 简单处理:将最新一条显示为"在线客服"会话
|
// 绠€鍗曞鐞嗭細灏嗘渶鏂颁竴鏉℃樉绀轰负"鍦ㄧ嚎瀹㈡湇"浼氳瘽
|
||||||
const lastMsg = chats[0]
|
const lastMsg = chats[0]
|
||||||
serviceMessages.push({
|
serviceMessages.push({
|
||||||
id: lastMsg.id,
|
id: lastMsg.id,
|
||||||
title: '在线客服',
|
title: '鍦ㄧ嚎瀹㈡湇',
|
||||||
role: '客服专员',
|
role: '瀹㈡湇涓撳憳',
|
||||||
content: lastMsg.content,
|
content: lastMsg.content,
|
||||||
lastMessage: lastMsg.content,
|
lastMessage: lastMsg.content,
|
||||||
time: formatTime(lastMsg.created_at || ''),
|
time: formatTime(lastMsg.created_at || ''),
|
||||||
@@ -347,8 +347,8 @@ const loadMessages = async () => {
|
|||||||
avatar: '/static/icons/service-avatar.png',
|
avatar: '/static/icons/service-avatar.png',
|
||||||
online: true,
|
online: true,
|
||||||
unreadCount: chats.filter((m: ChatMessage) => !m.is_read && !m.is_from_user).length,
|
unreadCount: chats.filter((m: ChatMessage) => !m.is_read && !m.is_from_user).length,
|
||||||
tags: ['官方客服'],
|
tags: ['瀹樻柟瀹㈡湇'],
|
||||||
icon: '👩💼',
|
icon: '馃懇鈥嶐煉?,
|
||||||
color: '#2196F3',
|
color: '#2196F3',
|
||||||
important: false,
|
important: false,
|
||||||
coupon: '',
|
coupon: '',
|
||||||
@@ -359,21 +359,21 @@ const loadMessages = async () => {
|
|||||||
statusText: ''
|
statusText: ''
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 如果没有真实数据,保留一个默认客服入口
|
// 濡傛灉娌℃湁鐪熷疄鏁版嵁锛屼繚鐣欎竴涓粯璁ゅ鏈嶅叆鍙?
|
||||||
serviceMessages.push({
|
serviceMessages.push({
|
||||||
id: 'default_service',
|
id: 'default_service',
|
||||||
title: '在线客服',
|
title: '鍦ㄧ嚎瀹㈡湇',
|
||||||
role: '智能助手',
|
role: '鏅鸿兘鍔╂墜',
|
||||||
content: '有问题请随时联系我们',
|
content: '鏈夐棶棰樿闅忔椂鑱旂郴鎴戜滑',
|
||||||
lastMessage: '欢迎咨询',
|
lastMessage: '娆㈣繋鍜ㄨ',
|
||||||
time: '刚刚',
|
time: '鍒氬垰',
|
||||||
read: true,
|
read: true,
|
||||||
type: 'service',
|
type: 'service',
|
||||||
avatar: '/static/icons/service-avatar.png',
|
avatar: '/static/icons/service-avatar.png',
|
||||||
online: true,
|
online: true,
|
||||||
unreadCount: 0,
|
unreadCount: 0,
|
||||||
tags: ['自动回复'],
|
tags: ['鑷姩鍥炲'],
|
||||||
icon: '🤖',
|
icon: '馃',
|
||||||
color: '#2196F3',
|
color: '#2196F3',
|
||||||
important: false,
|
important: false,
|
||||||
coupon: '',
|
coupon: '',
|
||||||
@@ -386,14 +386,14 @@ const loadMessages = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('加载消息失败', e)
|
console.error('鍔犺浇娑堟伅澶辫触', e)
|
||||||
} finally {
|
} finally {
|
||||||
updateUnreadCount()
|
updateUnreadCount()
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新未读数量
|
// 鏇存柊鏈鏁伴噺
|
||||||
const updateUnreadCount = () => {
|
const updateUnreadCount = () => {
|
||||||
let totalUnread = 0
|
let totalUnread = 0
|
||||||
|
|
||||||
@@ -416,14 +416,14 @@ const updateUnreadCount = () => {
|
|||||||
unreadCount.value = totalUnread
|
unreadCount.value = totalUnread
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换标签
|
// 鍒囨崲鏍囩
|
||||||
const switchTab = (tabId: string) => {
|
const switchTab = (tabId: string) => {
|
||||||
activeTab.value = tabId
|
activeTab.value = tabId
|
||||||
// 切换标签时回到顶部,使用微小变化触发滚动更新
|
// 鍒囨崲鏍囩鏃跺洖鍒伴《閮紝浣跨敤寰皬鍙樺寲瑙﹀彂婊氬姩鏇存柊
|
||||||
scrollTop.value = scrollTop.value === 0 ? 0.01 : 0
|
scrollTop.value = scrollTop.value === 0 ? 0.01 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开始与客服聊天
|
// 寮€濮嬩笌瀹㈡湇鑱婂ぉ
|
||||||
const startChatWithService = (message: any) => {
|
const startChatWithService = (message: any) => {
|
||||||
message.read = true
|
message.read = true
|
||||||
message.unreadCount = 0
|
message.unreadCount = 0
|
||||||
@@ -434,26 +434,26 @@ const startChatWithService = (message: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 快速开始服务
|
// 蹇€熷紑濮嬫湇鍔?
|
||||||
const startQuickService = (category: string) => {
|
const startQuickService = (category: string) => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/mall/consumer/chat?category=${encodeURIComponent(category)}`
|
url: `/pages/mall/consumer/chat?category=${encodeURIComponent(category)}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新建聊天
|
// 鏂板缓鑱婂ぉ
|
||||||
const startNewChat = () => {
|
const startNewChat = () => {
|
||||||
uni.showActionSheet({
|
uni.showActionSheet({
|
||||||
itemList: ['用药咨询', '处方咨询', '副作用咨询', '药品配送', '其他问题'],
|
itemList: ['鐢ㄨ嵂鍜ㄨ', '澶勬柟鍜ㄨ', '鍓綔鐢ㄥ挩璇?, '鑽搧閰嶉€?, '鍏朵粬闂'],
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const categories = ['用药咨询', '处方咨询', '副作用咨询', '药品配送', '其他问题']
|
const categories = ['鐢ㄨ嵂鍜ㄨ', '澶勬柟鍜ㄨ', '鍓綔鐢ㄥ挩璇?, '鑽搧閰嶉€?, '鍏朵粬闂']
|
||||||
const category = categories[res.tapIndex]
|
const category = categories[res.tapIndex]
|
||||||
startQuickService(category)
|
startQuickService(category)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看系统消息
|
// 鏌ョ湅绯荤粺娑堟伅
|
||||||
const viewSystemMessage = (message: any) => {
|
const viewSystemMessage = (message: any) => {
|
||||||
message.read = true
|
message.read = true
|
||||||
updateUnreadCount()
|
updateUnreadCount()
|
||||||
@@ -462,7 +462,7 @@ const viewSystemMessage = (message: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看订单消息
|
// 鏌ョ湅璁㈠崟娑堟伅
|
||||||
const viewOrderMessage = (message: any) => {
|
const viewOrderMessage = (message: any) => {
|
||||||
message.read = true
|
message.read = true
|
||||||
updateUnreadCount()
|
updateUnreadCount()
|
||||||
@@ -471,7 +471,7 @@ const viewOrderMessage = (message: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看优惠活动
|
// 鏌ョ湅浼樻儬娲诲姩
|
||||||
const viewPromoMessage = (message: any) => {
|
const viewPromoMessage = (message: any) => {
|
||||||
message.read = true
|
message.read = true
|
||||||
updateUnreadCount()
|
updateUnreadCount()
|
||||||
@@ -480,22 +480,22 @@ const viewPromoMessage = (message: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 领取优惠券
|
// 棰嗗彇浼樻儬鍒?
|
||||||
const claimCoupon = (message: any) => {
|
const claimCoupon = (message: any) => {
|
||||||
if (message.claimed) {
|
if (message.claimed) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '您已领取该优惠券',
|
title: '鎮ㄥ凡棰嗗彇璇ヤ紭鎯犲埜',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
message.claimed = true
|
message.claimed = true
|
||||||
// 保存领取状态到本地存储,供个人页读取
|
// 淇濆瓨棰嗗彇鐘舵€佸埌鏈湴瀛樺偍锛屼緵涓汉椤佃鍙?
|
||||||
const claimedCouponsCount = uni.getStorageSync('claimedCoupons') || 0
|
const claimedCouponsCount = uni.getStorageSync('claimedCoupons') || 0
|
||||||
uni.setStorageSync('claimedCoupons', (claimedCouponsCount as number) + 1)
|
uni.setStorageSync('claimedCoupons', (claimedCouponsCount as number) + 1)
|
||||||
|
|
||||||
// 保存详细的优惠券信息到 myCoupons 列表
|
// 淇濆瓨璇︾粏鐨勪紭鎯犲埜淇℃伅鍒?myCoupons 鍒楄〃
|
||||||
const myCoupons = uni.getStorageSync('myCoupons')
|
const myCoupons = uni.getStorageSync('myCoupons')
|
||||||
let couponsList: any[] = []
|
let couponsList: any[] = []
|
||||||
if (myCoupons) {
|
if (myCoupons) {
|
||||||
@@ -515,16 +515,16 @@ const claimCoupon = (message: any) => {
|
|||||||
uni.setStorageSync('myCoupons', JSON.stringify(couponsList))
|
uni.setStorageSync('myCoupons', JSON.stringify(couponsList))
|
||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '领取成功',
|
title: '棰嗗彇鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除所有未读
|
// 娓呴櫎鎵€鏈夋湭璇?
|
||||||
const clearAllUnread = () => {
|
const clearAllUnread = () => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '确认操作',
|
title: '纭鎿嶄綔',
|
||||||
content: '确定要标记所有消息为已读吗?',
|
content: '纭畾瑕佹爣璁版墍鏈夋秷鎭负宸茶鍚楋紵',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
serviceMessages.forEach(msg => {
|
serviceMessages.forEach(msg => {
|
||||||
@@ -539,7 +539,7 @@ const clearAllUnread = () => {
|
|||||||
unreadCount.value = 0
|
unreadCount.value = 0
|
||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已标记所有消息为已读',
|
title: '宸叉爣璁版墍鏈夋秷鎭负宸茶',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -547,14 +547,14 @@ const clearAllUnread = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下拉刷新
|
// 涓嬫媺鍒锋柊
|
||||||
const onRefresh = () => {
|
const onRefresh = () => {
|
||||||
refreshing.value = true
|
refreshing.value = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loadMessages()
|
loadMessages()
|
||||||
refreshing.value = false
|
refreshing.value = false
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '刷新成功',
|
title: '鍒锋柊鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
@@ -562,17 +562,17 @@ const onRefresh = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* 页面结构优化 - 避免双滚动条 */
|
/* 椤甸潰缁撴瀯浼樺寲 - 閬垮厤鍙屾粴鍔ㄦ潯 */
|
||||||
.messages-page {
|
.messages-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden; /* 关键:防止body滚动 */
|
overflow: hidden; /* 鍏抽敭锛氶槻姝ody婊氬姩 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 智能导航栏 */
|
/* 鏅鸿兘瀵艰埅鏍?*/
|
||||||
.smart-navbar {
|
.smart-navbar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -581,42 +581,42 @@ const onRefresh = () => {
|
|||||||
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
|
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
||||||
/* height: 50px; 移除固定高度,由内容决定 */
|
/* height: 50px; 绉婚櫎鍥哄畾楂樺害锛岀敱鍐呭鍐冲畾 */
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 显式设置行方向 */
|
flex-direction: row; /* 鏄惧紡璁剧疆琛屾柟鍚?*/
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0; /* 防止被压缩 */
|
flex-shrink: 0; /* 闃叉琚帇缂?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-container {
|
.nav-container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 关键修复:UVUE默认是column,必须显式设置为row才能横向排列 */
|
flex-direction: row; /* 鍏抽敭淇锛歎VUE榛樿鏄痗olumn锛屽繀椤绘樉寮忚缃负row鎵嶈兘妯悜鎺掑垪 */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: 44px; /* 调整为标准高度 44px */
|
height: 44px; /* 璋冩暣涓烘爣鍑嗛珮搴?44px */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-title {
|
.nav-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: white;
|
color: white;
|
||||||
flex: 1; /* 自适应宽度 */
|
flex: 1; /* 鑷€傚簲瀹藉害 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-actions {
|
.nav-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 显式设置行方向 */
|
flex-direction: row; /* 鏄惧紡璁剧疆琛屾柟鍚?*/
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 显式设置行方向 */
|
flex-direction: row; /* 鏄惧紡璁剧疆琛屾柟鍚?*/
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
@@ -640,32 +640,32 @@ const onRefresh = () => {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏占位符 */
|
/* 瀵艰埅鏍忓崰浣嶇 */
|
||||||
.navbar-placeholder {
|
.navbar-placeholder {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 消息分类标签容器 */
|
/* 娑堟伅鍒嗙被鏍囩瀹瑰櫒 */
|
||||||
.tabs-container {
|
.tabs-container {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
border-bottom: 1px solid #e0e0e0;
|
border-bottom: 1px solid #e0e0e0;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
z-index: 900;
|
z-index: 900;
|
||||||
height: 42px; /* 减小高度,更紧凑 */
|
height: 42px; /* 鍑忓皬楂樺害锛屾洿绱у噾 */
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-tabs {
|
.message-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 横向排列 */
|
flex-direction: row; /* 妯悜鎺掑垪 */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
/* overflow-x: auto; 移除自动滚动,改为自适应宽度 */
|
/* overflow-x: auto; 绉婚櫎鑷姩婊氬姩锛屾敼涓鸿嚜閫傚簲瀹藉害 */
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
/* gap: 4px; removed for uniapp-x support */
|
/* gap: 4px; removed for uniapp-x support */
|
||||||
justify-content: space-between; /* 均匀分布 */
|
justify-content: space-between; /* 鍧囧寑鍒嗗竷 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-tabs::-webkit-scrollbar {
|
.message-tabs::-webkit-scrollbar {
|
||||||
@@ -675,17 +675,17 @@ const onRefresh = () => {
|
|||||||
.tab-item {
|
.tab-item {
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
display: flex; /* 改为 flex 布局 */
|
display: flex; /* 鏀逛负 flex 甯冨眬 */
|
||||||
flex-direction: row; /* 关键:横向排列 文字和数字 */
|
flex-direction: row; /* 鍏抽敭锛氭í鍚戞帓鍒?鏂囧瓧鍜屾暟瀛?*/
|
||||||
align-items: center; /* 垂直居中 */
|
align-items: center; /* 鍨傜洿灞呬腑 */
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-bottom: 3px solid transparent;
|
border-bottom: 3px solid transparent;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
white-space: nowrap; /* 防止换行 */
|
white-space: nowrap; /* 闃叉鎹㈣ */
|
||||||
flex: 1; /* 关键:均分宽度,消除滚动条 */
|
flex: 1; /* 鍏抽敭锛氬潎鍒嗗搴︼紝娑堥櫎婊氬姩鏉?*/
|
||||||
min-width: 0; /* 允许压缩 */
|
min-width: 0; /* 鍏佽鍘嬬缉 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item.active {
|
.tab-item.active {
|
||||||
@@ -695,14 +695,14 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-name {
|
.tab-name {
|
||||||
font-size: 14px; /* 微调字体大小适配小屏 */
|
font-size: 14px; /* 寰皟瀛椾綋澶у皬閫傞厤灏忓睆 */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 徽标样式优化 - 放在文字右边 */
|
/* 寰芥爣鏍峰紡浼樺寲 - 鏀惧湪鏂囧瓧鍙宠竟 */
|
||||||
.tab-badge {
|
.tab-badge {
|
||||||
background-color: #FF5722;
|
background-color: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
padding: 1px 5px;
|
padding: 1px 5px;
|
||||||
@@ -710,24 +710,24 @@ const onRefresh = () => {
|
|||||||
min-width: 16px;
|
min-width: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-left: 4px; /* 距离文字的间距 */
|
margin-left: 4px; /* 璺濈鏂囧瓧鐨勯棿璺?*/
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 消息内容区 */
|
/* 娑堟伅鍐呭鍖?*/
|
||||||
.messages-content {
|
.messages-content {
|
||||||
flex: 1; /* 关键:自适应剩余高度 */
|
flex: 1; /* 鍏抽敭锛氳嚜閫傚簲鍓╀綑楂樺害 */
|
||||||
height: 0; /* 关键:强制flex item计算高度 */
|
height: 0; /* 鍏抽敭锛氬己鍒秄lex item璁$畻楂樺害 */
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 客服信息区域 */
|
/* 瀹㈡湇淇℃伅鍖哄煙 */
|
||||||
.customer-service-info {
|
.customer-service-info {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -802,7 +802,7 @@ const onRefresh = () => {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 消息项 */
|
/* 娑堟伅椤?*/
|
||||||
.message-section {
|
.message-section {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
@@ -931,7 +931,7 @@ const onRefresh = () => {
|
|||||||
.message-unread-count {
|
.message-unread-count {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: white;
|
color: white;
|
||||||
background: #FF5722;
|
background: #ff5000;
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
min-width: 18px;
|
min-width: 18px;
|
||||||
@@ -1000,7 +1000,7 @@ const onRefresh = () => {
|
|||||||
|
|
||||||
.order-status.processing {
|
.order-status.processing {
|
||||||
background: #FFF3E0;
|
background: #FFF3E0;
|
||||||
color: #FF9800;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-status.completed {
|
.order-status.completed {
|
||||||
@@ -1010,7 +1010,7 @@ const onRefresh = () => {
|
|||||||
|
|
||||||
.important-tag {
|
.important-tag {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: #FF5722;
|
background-color: #ff5000;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 3px 8px;
|
padding: 3px 8px;
|
||||||
@@ -1019,7 +1019,7 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.coupon-info {
|
.coupon-info {
|
||||||
background: linear-gradient(135deg, #FF9800, #FF5722);
|
background: linear-gradient(135deg, #ff5000, #ff5000);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
@@ -1048,7 +1048,7 @@ const onRefresh = () => {
|
|||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 客服系统提示 */
|
/* 瀹㈡湇绯荤粺鎻愮ず */
|
||||||
.service-tips {
|
.service-tips {
|
||||||
background: #FFF3E0;
|
background: #FFF3E0;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -1061,7 +1061,7 @@ const onRefresh = () => {
|
|||||||
|
|
||||||
.tip-icon {
|
.tip-icon {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: #FF9800;
|
color: #ff5000;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
@@ -1072,7 +1072,7 @@ const onRefresh = () => {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 绌虹姸鎬?*/
|
||||||
.empty-messages {
|
.empty-messages {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -1099,7 +1099,7 @@ const onRefresh = () => {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 底部浮动按钮 */
|
/* 搴曢儴娴姩鎸夐挳 */
|
||||||
.floating-action {
|
.floating-action {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
@@ -1133,7 +1133,7 @@ const onRefresh = () => {
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式适配 */
|
/* 鍝嶅簲寮忛€傞厤 */
|
||||||
@media screen and (max-width: 320px) {
|
@media screen and (max-width: 320px) {
|
||||||
.tab-name {
|
.tab-name {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@@ -1167,17 +1167,17 @@ const onRefresh = () => {
|
|||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 平板和桌面端优化标签栏显示 */
|
/* 骞虫澘鍜屾闈㈢浼樺寲鏍囩鏍忔樉绀?*/
|
||||||
.message-tabs {
|
.message-tabs {
|
||||||
justify-content: flex-start; /* 左对齐 */
|
justify-content: flex-start; /* 宸﹀榻?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item {
|
.tab-item {
|
||||||
padding: 0 24px; /* 增加点击区域 */
|
padding: 0 24px; /* 澧炲姞鐐瑰嚮鍖哄煙 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 平板设备 */
|
/* 骞虫澘璁惧 */
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.smart-navbar {
|
.smart-navbar {
|
||||||
padding: 0 30px;
|
padding: 0 30px;
|
||||||
@@ -1200,7 +1200,7 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 暗黑模式适配 */
|
/* 鏆楅粦妯″紡閫傞厤 */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.messages-page {
|
.messages-page {
|
||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
@@ -1276,3 +1276,4 @@ const onRefresh = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 消费者端 - 订单详情页 -->
|
<!-- 消费者端 - 订单详情页 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="order-detail-page">
|
<view class="order-detail-page">
|
||||||
<scroll-view scroll-y="true" class="scroll-content">
|
<scroll-view scroll-y="true" class="scroll-content">
|
||||||
|
|||||||
1418
pages/mall/consumer/orders copy 2.uvue
Normal file
@@ -1,23 +1,23 @@
|
|||||||
<!-- pages/mall/consumer/orders.uvue -->
|
<!-- pages/mall/consumer/orders.uvue -->
|
||||||
<template>
|
<template>
|
||||||
<view class="orders-page">
|
<view class="orders-page">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 椤堕儴鏍囬鏍?-->
|
||||||
<view class="orders-header">
|
<view class="orders-header">
|
||||||
<view class="header-search full-width">
|
<view class="header-search full-width">
|
||||||
<input
|
<input
|
||||||
class="search-input"
|
class="search-input"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="搜索订单号或商品名称"
|
placeholder="鎼滅储璁㈠崟鍙锋垨鍟嗗搧鍚嶇О"
|
||||||
:value="searchKeyword"
|
:value="searchKeyword"
|
||||||
@input="onSearchInput"
|
@input="onSearchInput"
|
||||||
@confirm="onSearchConfirm"
|
@confirm="onSearchConfirm"
|
||||||
/>
|
/>
|
||||||
<text v-if="searchKeyword" class="search-clear" @click="clearSearch">×</text>
|
<text v-if="searchKeyword" class="search-clear" @click="clearSearch">脳</text>
|
||||||
<text v-else class="search-icon">🔍</text>
|
<text v-else class="search-icon">馃攳</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单状态筛选 -->
|
<!-- 璁㈠崟鐘舵€佺瓫閫?-->
|
||||||
<view class="order-tabs">
|
<view class="order-tabs">
|
||||||
<scroll-view scroll-x class="tab-scroll" :show-scrollbar="false">
|
<scroll-view scroll-x class="tab-scroll" :show-scrollbar="false">
|
||||||
<view class="tab-container">
|
<view class="tab-container">
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单列表 -->
|
<!-- 璁㈠崟鍒楄〃 -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
scroll-y
|
scroll-y
|
||||||
class="orders-content"
|
class="orders-content"
|
||||||
@@ -44,15 +44,15 @@
|
|||||||
@refresherrefresh="onRefresh"
|
@refresherrefresh="onRefresh"
|
||||||
@scrolltolower="loadMore"
|
@scrolltolower="loadMore"
|
||||||
>
|
>
|
||||||
<!-- 空状态 -->
|
<!-- 绌虹姸鎬?-->
|
||||||
<view v-if="!loading && orders.length === 0" class="empty-orders">
|
<view v-if="!loading && orders.length === 0" class="empty-orders">
|
||||||
<text class="empty-icon">📦</text>
|
<text class="empty-icon">馃摝</text>
|
||||||
<text class="empty-title">暂无订单</text>
|
<text class="empty-title">鏆傛棤璁㈠崟</text>
|
||||||
<text class="empty-desc">去逛逛,发现心仪的商品</text>
|
<text class="empty-desc">鍘婚€涢€涳紝鍙戠幇蹇冧华鐨勫晢鍝?/text>
|
||||||
<button class="go-shopping-btn" @click="goShopping">去逛逛</button>
|
<button class="go-shopping-btn" @click="goShopping">鍘婚€涢€?/button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单列表 -->
|
<!-- 璁㈠崟鍒楄〃 -->
|
||||||
<view v-else class="order-list">
|
<view v-else class="order-list">
|
||||||
<view
|
<view
|
||||||
v-for="order in orders"
|
v-for="order in orders"
|
||||||
@@ -60,19 +60,19 @@
|
|||||||
class="order-card"
|
class="order-card"
|
||||||
@click="viewOrderDetail(order.id)"
|
@click="viewOrderDetail(order.id)"
|
||||||
>
|
>
|
||||||
<!-- 订单头部:显示店铺名称 -->
|
<!-- 璁㈠崟澶撮儴锛氭樉绀哄簵閾哄悕绉?-->
|
||||||
<view class="order-card-header">
|
<view class="order-card-header">
|
||||||
<view class="shop-info">
|
<view class="shop-info">
|
||||||
<text class="shop-icon">🏪</text>
|
<text class="shop-icon">馃彧</text>
|
||||||
<text class="shop-name">{{ order.shop_name != null && order.shop_name != '' ? order.shop_name : '自营店铺' }}</text>
|
<text class="shop-name">{{ order.shop_name != null && order.shop_name != '' ? order.shop_name : '鑷惀搴楅摵' }}</text>
|
||||||
<text class="arrow-right">›</text>
|
<text class="arrow-right">鈥?/text>
|
||||||
</view>
|
</view>
|
||||||
<text :class="['order-status', getStatusClass(order.status)]">
|
<text :class="['order-status', getStatusClass(order.status)]">
|
||||||
{{ getStatusText(order.status) }}
|
{{ getStatusText(order.status) }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单商品 -->
|
<!-- 璁㈠崟鍟嗗搧 -->
|
||||||
<view class="order-products">
|
<view class="order-products">
|
||||||
<view
|
<view
|
||||||
v-for="product in order.products"
|
v-for="product in order.products"
|
||||||
@@ -91,68 +91,68 @@
|
|||||||
<text class="product-spec">{{ product.spec }}</text>
|
<text class="product-spec">{{ product.spec }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="product-footer">
|
<view class="product-footer">
|
||||||
<text class="product-price">¥{{ product.price }}</text>
|
<text class="product-price">楼{{ product.price }}</text>
|
||||||
<text class="product-quantity">x{{ product.quantity }}</text>
|
<text class="product-quantity">x{{ product.quantity }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单汇总信息 -->
|
<!-- 璁㈠崟姹囨€讳俊鎭?-->
|
||||||
<view class="order-summary">
|
<view class="order-summary">
|
||||||
<text class="order-time">{{ formatDate(order.create_time) }}</text>
|
<text class="order-time">{{ formatDate(order.create_time) }}</text>
|
||||||
<view class="summary-right">
|
<view class="summary-right">
|
||||||
<text class="summary-label">共{{ order.products.length }}件商品 实付:</text>
|
<text class="summary-label">鍏眥{ order.products.length }}浠跺晢鍝?瀹炰粯:</text>
|
||||||
<text class="summary-price">¥{{ order.total_amount }}</text>
|
<text class="summary-price">楼{{ order.total_amount }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单操作 -->
|
<!-- 璁㈠崟鎿嶄綔 -->
|
||||||
<view class="order-actions" @click.stop="">
|
<view class="order-actions" @click.stop="">
|
||||||
<view v-if="order.status === 1" class="action-buttons">
|
<view v-if="order.status === 1" class="action-buttons">
|
||||||
<button class="action-btn cancel" @click="cancelOrder(order.id)">取消订单</button>
|
<button class="action-btn cancel" @click="cancelOrder(order.id)">鍙栨秷璁㈠崟</button>
|
||||||
<button class="action-btn pay" @click="payOrder(order.id)">立即支付</button>
|
<button class="action-btn pay" @click="payOrder(order.id)">绔嬪嵆鏀粯</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="order.status === 2" class="action-buttons">
|
<view v-if="order.status === 2" class="action-buttons">
|
||||||
<button class="action-btn remind" @click="remindShipping(order.id)">提醒发货</button>
|
<button class="action-btn remind" @click="remindShipping(order.id)">鎻愰啋鍙戣揣</button>
|
||||||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
<button class="action-btn refund" @click.stop="onApplyRefund(order)">鐢宠鍞悗</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="order.status === 3" class="action-buttons">
|
<view v-if="order.status === 3" class="action-buttons">
|
||||||
<button class="action-btn view" @click="viewLogistics(order.id)">查看物流</button>
|
<button class="action-btn view" @click="viewLogistics(order.id)">鏌ョ湅鐗╂祦</button>
|
||||||
<button class="action-btn confirm" @click="confirmReceipt(order.id)">确认收货</button>
|
<button class="action-btn confirm" @click="confirmReceipt(order.id)">纭鏀惰揣</button>
|
||||||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
<button class="action-btn refund" @click.stop="onApplyRefund(order)">鐢宠鍞悗</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="order.status === 4" class="action-buttons">
|
<view v-if="order.status === 4" class="action-buttons">
|
||||||
<button class="action-btn review" @click="goReview(order)">评价</button>
|
<button class="action-btn review" @click="goReview(order)">璇勪环</button>
|
||||||
<button class="action-btn refund" @click.stop="onApplyRefund(order)">申请售后</button>
|
<button class="action-btn refund" @click.stop="onApplyRefund(order)">鐢宠鍞悗</button>
|
||||||
<button class="action-btn repurchase" @click="repurchase(order)">再次购买</button>
|
<button class="action-btn repurchase" @click="repurchase(order)">鍐嶆璐拱</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="order.status === 5" class="action-buttons">
|
<view v-if="order.status === 5" class="action-buttons">
|
||||||
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
<button class="action-btn view" @click="viewOrderDetail(order.id)">鏌ョ湅璇︽儏</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载更多 -->
|
<!-- 鍔犺浇鏇村 -->
|
||||||
<view v-if="loadingMore" class="loading-more">
|
<view v-if="loadingMore" class="loading-more">
|
||||||
<view class="loading-spinner"></view>
|
<view class="loading-spinner"></view>
|
||||||
<text>加载中...</text>
|
<text>鍔犺浇涓?..</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="!hasMore && orders.length > 0" class="no-more">
|
<view v-if="!hasMore && orders.length > 0" class="no-more">
|
||||||
<text>没有更多订单了</text>
|
<text>娌℃湁鏇村璁㈠崟浜?/text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 安全区域 -->
|
<!-- 瀹夊叏鍖哄煙 -->
|
||||||
<view class="safe-area"></view>
|
<view class="safe-area"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 底部导航 -->
|
<!-- 搴曢儴瀵艰埅 -->
|
||||||
<view class="tabbar-placeholder"></view>
|
<view class="tabbar-placeholder"></view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -162,14 +162,14 @@ import { ref, reactive, onMounted, computed } from 'vue'
|
|||||||
import { onShow, onLoad, onBackPress } from '@dcloudio/uni-app'
|
import { onShow, onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||||
|
|
||||||
// 定义标签页类型
|
// 瀹氫箟鏍囩椤电被鍨?
|
||||||
type OrderTabItem = {
|
type OrderTabItem = {
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义订单产品类型
|
// 瀹氫箟璁㈠崟浜у搧绫诲瀷
|
||||||
type OrderProduct = {
|
type OrderProduct = {
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
@@ -179,7 +179,7 @@ type OrderProduct = {
|
|||||||
quantity: number
|
quantity: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义订单类型
|
// 瀹氫箟璁㈠崟绫诲瀷
|
||||||
type OrderItem = {
|
type OrderItem = {
|
||||||
id: string,
|
id: string,
|
||||||
order_no: string,
|
order_no: string,
|
||||||
@@ -193,7 +193,7 @@ type OrderItem = {
|
|||||||
products: OrderProduct[]
|
products: OrderProduct[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式数据
|
// 鍝嶅簲寮忔暟鎹?
|
||||||
const orders = ref<OrderItem[]>([])
|
const orders = ref<OrderItem[]>([])
|
||||||
const allOrdersList = ref<OrderItem[]>([]) // Store all fetched orders for client-side filtering
|
const allOrdersList = ref<OrderItem[]>([]) // Store all fetched orders for client-side filtering
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -204,20 +204,20 @@ const page = ref<number>(1)
|
|||||||
const activeTab = ref<string>('all')
|
const activeTab = ref<string>('all')
|
||||||
const searchKeyword = ref<string>('')
|
const searchKeyword = ref<string>('')
|
||||||
|
|
||||||
// 订单标签页 - 使用 ref 以便整体替换
|
// 璁㈠崟鏍囩椤?- 浣跨敤 ref 浠ヤ究鏁翠綋鏇挎崲
|
||||||
const orderTabs = ref<OrderTabItem[]>([
|
const orderTabs = ref<OrderTabItem[]>([
|
||||||
{ id: 'all', name: '全部', count: 0 },
|
{ id: 'all', name: '鍏ㄩ儴', count: 0 },
|
||||||
{ id: 'pending', name: '待付款', count: 0 },
|
{ id: 'pending', name: '寰呬粯娆?, count: 0 },
|
||||||
{ id: 'shipping', name: '待发货', count: 0 },
|
{ id: 'shipping', name: '寰呭彂璐?, count: 0 },
|
||||||
{ id: 'delivering', name: '待收货', count: 0 },
|
{ id: 'delivering', name: '寰呮敹璐?, count: 0 },
|
||||||
{ id: 'completed', name: '已完成', count: 0 },
|
{ id: 'completed', name: '宸插畬鎴?, count: 0 },
|
||||||
{ id: 'cancelled', name: '已取消', count: 0 }
|
{ id: 'cancelled', name: '宸插彇娑?, count: 0 }
|
||||||
])
|
])
|
||||||
|
|
||||||
// Removed Mock Data
|
// Removed Mock Data
|
||||||
|
|
||||||
|
|
||||||
// 辅助函数:获取状态码
|
// 杈呭姪鍑芥暟锛氳幏鍙栫姸鎬佺爜
|
||||||
const getStatusByTab = (tabId: string): number => {
|
const getStatusByTab = (tabId: string): number => {
|
||||||
if (tabId == 'pending') return 1
|
if (tabId == 'pending') return 1
|
||||||
if (tabId == 'shipping') return 2
|
if (tabId == 'shipping') return 2
|
||||||
@@ -227,11 +227,11 @@ const getStatusByTab = (tabId: string): number => {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化规格对象为友好的文本 - 必须在 parseSpecText 之前定义
|
// 鏍煎紡鍖栬鏍煎璞′负鍙嬪ソ鐨勬枃鏈?- 蹇呴』鍦?parseSpecText 涔嬪墠瀹氫箟
|
||||||
function formatSpecObj(obj: any): string {
|
function formatSpecObj(obj: any): string {
|
||||||
if (obj == null) return ''
|
if (obj == null) return ''
|
||||||
if (typeof obj !== 'object') {
|
if (typeof obj !== 'object') {
|
||||||
// 非对象类型直接返回字符串形式
|
// 闈炲璞$被鍨嬬洿鎺ヨ繑鍥炲瓧绗︿覆褰㈠紡
|
||||||
if (typeof obj === 'string') return obj
|
if (typeof obj === 'string') return obj
|
||||||
if (typeof obj === 'number') return obj.toString()
|
if (typeof obj === 'number') return obj.toString()
|
||||||
return ''
|
return ''
|
||||||
@@ -244,14 +244,14 @@ function formatSpecObj(obj: any): string {
|
|||||||
|
|
||||||
const specObj = objParsed as UTSJSONObject
|
const specObj = objParsed as UTSJSONObject
|
||||||
|
|
||||||
// 使用 JSON.stringify 获取所有键
|
// 浣跨敤 JSON.stringify 鑾峰彇鎵€鏈夐敭
|
||||||
const specObjStr = JSON.stringify(specObj)
|
const specObjStr = JSON.stringify(specObj)
|
||||||
const specObjForKeys = JSON.parse(specObjStr) as UTSJSONObject
|
const specObjForKeys = JSON.parse(specObjStr) as UTSJSONObject
|
||||||
|
|
||||||
// 手动提取键值对
|
// 鎵嬪姩鎻愬彇閿€煎
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
|
|
||||||
// 尝试获取已知字段
|
// 灏濊瘯鑾峰彇宸茬煡瀛楁
|
||||||
const colorVal = specObjForKeys.getString('Color')
|
const colorVal = specObjForKeys.getString('Color')
|
||||||
if (colorVal != null && colorVal != '') {
|
if (colorVal != null && colorVal != '') {
|
||||||
parts.push('Color: ' + colorVal)
|
parts.push('Color: ' + colorVal)
|
||||||
@@ -262,14 +262,14 @@ function formatSpecObj(obj: any): string {
|
|||||||
parts.push('Size: ' + sizeVal)
|
parts.push('Size: ' + sizeVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultVal = specObjForKeys.getString('默认')
|
const defaultVal = specObjForKeys.getString('榛樿')
|
||||||
if (defaultVal != null && defaultVal != '') {
|
if (defaultVal != null && defaultVal != '') {
|
||||||
parts.push('默认: ' + defaultVal)
|
parts.push('榛樿: ' + defaultVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有匹配到已知字段,尝试直接显示 JSON
|
// 濡傛灉娌℃湁鍖归厤鍒板凡鐭ュ瓧娈碉紝灏濊瘯鐩存帴鏄剧ず JSON
|
||||||
if (parts.length === 0) {
|
if (parts.length === 0) {
|
||||||
// 尝试遍历对象
|
// 灏濊瘯閬嶅巻瀵硅薄
|
||||||
const objAny = specObjForKeys as any
|
const objAny = specObjForKeys as any
|
||||||
if (objAny != null) {
|
if (objAny != null) {
|
||||||
return specObjStr.replace(/[{}"]/g, '').replace(/:/g, ': ').replace(/,/g, ' | ')
|
return specObjStr.replace(/[{}"]/g, '').replace(/:/g, ': ').replace(/,/g, ' | ')
|
||||||
@@ -282,11 +282,11 @@ function formatSpecObj(obj: any): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数:解析规格文本
|
// 杈呭姪鍑芥暟锛氳В鏋愯鏍兼枃鏈?
|
||||||
function parseSpecText(specs: any): string {
|
function parseSpecText(specs: any): string {
|
||||||
if (specs == null) return ''
|
if (specs == null) return ''
|
||||||
if (typeof specs === 'string') {
|
if (typeof specs === 'string') {
|
||||||
// 如果是 JSON 字符串,尝试解析
|
// 濡傛灉鏄?JSON 瀛楃涓诧紝灏濊瘯瑙f瀽
|
||||||
if (specs.startsWith('{') || specs.startsWith('[')) {
|
if (specs.startsWith('{') || specs.startsWith('[')) {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(specs)
|
const parsed = JSON.parse(specs)
|
||||||
@@ -298,13 +298,13 @@ function parseSpecText(specs: any): string {
|
|||||||
}
|
}
|
||||||
return specs
|
return specs
|
||||||
}
|
}
|
||||||
// 对于对象类型,格式化显示
|
// 瀵逛簬瀵硅薄绫诲瀷锛屾牸寮忓寲鏄剧ず
|
||||||
return formatSpecObj(specs)
|
return formatSpecObj(specs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数:更新标签计数
|
// 杈呭姪鍑芥暟锛氭洿鏂版爣绛捐鏁?
|
||||||
const updateTabsCounts = (allOrders: OrderItem[]) => {
|
const updateTabsCounts = (allOrders: OrderItem[]) => {
|
||||||
// 计算各状态数量
|
// 璁$畻鍚勭姸鎬佹暟閲?
|
||||||
const countAll = allOrders.length
|
const countAll = allOrders.length
|
||||||
const countPending = allOrders.filter((o: OrderItem) => o.status === 1).length
|
const countPending = allOrders.filter((o: OrderItem) => o.status === 1).length
|
||||||
const countShipping = allOrders.filter((o: OrderItem) => o.status === 2).length
|
const countShipping = allOrders.filter((o: OrderItem) => o.status === 2).length
|
||||||
@@ -312,7 +312,7 @@ const updateTabsCounts = (allOrders: OrderItem[]) => {
|
|||||||
const countCompleted = allOrders.filter((o: OrderItem) => o.status === 4).length
|
const countCompleted = allOrders.filter((o: OrderItem) => o.status === 4).length
|
||||||
const countCancelled = allOrders.filter((o: OrderItem) => o.status === 5).length
|
const countCancelled = allOrders.filter((o: OrderItem) => o.status === 5).length
|
||||||
|
|
||||||
// 更新数组元素
|
// 鏇存柊鏁扮粍鍏冪礌
|
||||||
orderTabs.value[0].count = countAll
|
orderTabs.value[0].count = countAll
|
||||||
orderTabs.value[1].count = countPending
|
orderTabs.value[1].count = countPending
|
||||||
orderTabs.value[2].count = countShipping
|
orderTabs.value[2].count = countShipping
|
||||||
@@ -321,7 +321,7 @@ const updateTabsCounts = (allOrders: OrderItem[]) => {
|
|||||||
orderTabs.value[5].count = countCancelled
|
orderTabs.value[5].count = countCancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数:按标签筛选订单
|
// 杈呭姪鍑芥暟锛氭寜鏍囩绛涢€夎鍗?
|
||||||
const filterOrdersByTab = () => {
|
const filterOrdersByTab = () => {
|
||||||
if (activeTab.value === 'all') {
|
if (activeTab.value === 'all') {
|
||||||
orders.value = allOrdersList.value
|
orders.value = allOrdersList.value
|
||||||
@@ -333,20 +333,20 @@ const filterOrdersByTab = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载订单数据
|
// 鍔犺浇璁㈠崟鏁版嵁
|
||||||
const loadOrders = async () => {
|
const loadOrders = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch all orders from Supabase (status=0)
|
// Fetch all orders from Supabase (status=0)
|
||||||
const fetchedOrders = await supabaseService.getOrders(0)
|
const fetchedOrders = await supabaseService.getOrders(0)
|
||||||
console.log('[loadOrders] 获取到订单数量:', fetchedOrders.length)
|
console.log('[loadOrders] 鑾峰彇鍒拌鍗曟暟閲?', fetchedOrders.length)
|
||||||
|
|
||||||
// Map to View Model
|
// Map to View Model
|
||||||
const mappedOrders: OrderItem[] = []
|
const mappedOrders: OrderItem[] = []
|
||||||
for (let i = 0; i < fetchedOrders.length; i++) {
|
for (let i = 0; i < fetchedOrders.length; i++) {
|
||||||
const order = fetchedOrders[i]
|
const order = fetchedOrders[i]
|
||||||
// 使用 JSON 序列化转换
|
// 浣跨敤 JSON 搴忓垪鍖栬浆鎹?
|
||||||
const orderStr = JSON.stringify(order)
|
const orderStr = JSON.stringify(order)
|
||||||
const orderParsed = JSON.parse(orderStr)
|
const orderParsed = JSON.parse(orderStr)
|
||||||
if (orderParsed == null) continue
|
if (orderParsed == null) continue
|
||||||
@@ -355,13 +355,13 @@ const loadOrders = async () => {
|
|||||||
const itemsRaw = orderObj.get('ml_order_items')
|
const itemsRaw = orderObj.get('ml_order_items')
|
||||||
const productsList: OrderProduct[] = []
|
const productsList: OrderProduct[] = []
|
||||||
|
|
||||||
console.log('[loadOrders] 订单商品数据:', itemsRaw)
|
console.log('[loadOrders] 璁㈠崟鍟嗗搧鏁版嵁:', itemsRaw)
|
||||||
|
|
||||||
if (itemsRaw != null) {
|
if (itemsRaw != null) {
|
||||||
// 先检查是否为数组
|
// 鍏堟鏌ユ槸鍚︿负鏁扮粍
|
||||||
if (Array.isArray(itemsRaw)) {
|
if (Array.isArray(itemsRaw)) {
|
||||||
const items = itemsRaw as any[]
|
const items = itemsRaw as any[]
|
||||||
console.log('[loadOrders] 商品数量:', items.length)
|
console.log('[loadOrders] 鍟嗗搧鏁伴噺:', items.length)
|
||||||
for (let j = 0; j < items.length; j++) {
|
for (let j = 0; j < items.length; j++) {
|
||||||
const item = items[j]
|
const item = items[j]
|
||||||
const itemStr = JSON.stringify(item)
|
const itemStr = JSON.stringify(item)
|
||||||
@@ -378,11 +378,11 @@ const loadOrders = async () => {
|
|||||||
const imageUrl = itemObj.getString('image_url')
|
const imageUrl = itemObj.getString('image_url')
|
||||||
const quantity = itemObj.getNumber('quantity')
|
const quantity = itemObj.getNumber('quantity')
|
||||||
|
|
||||||
console.log('[loadOrders] 商品:', productName, '图片:', imageUrl, '规格:', specText)
|
console.log('[loadOrders] 鍟嗗搧:', productName, '鍥剧墖:', imageUrl, '瑙勬牸:', specText)
|
||||||
|
|
||||||
const productItem: OrderProduct = {
|
const productItem: OrderProduct = {
|
||||||
id: productId ?? '',
|
id: productId ?? '',
|
||||||
name: productName ?? '未知商品',
|
name: productName ?? '鏈煡鍟嗗搧',
|
||||||
price: price ?? 0,
|
price: price ?? 0,
|
||||||
image: imageUrl ?? '/static/default-product.png',
|
image: imageUrl ?? '/static/default-product.png',
|
||||||
spec: specText,
|
spec: specText,
|
||||||
@@ -403,8 +403,8 @@ const loadOrders = async () => {
|
|||||||
const paidAmount = orderObj.getNumber('paid_amount')
|
const paidAmount = orderObj.getNumber('paid_amount')
|
||||||
const merchantId = orderObj.getString('merchant_id')
|
const merchantId = orderObj.getString('merchant_id')
|
||||||
|
|
||||||
// 从关联查询的 ml_shops 表获取店铺名称
|
// 浠庡叧鑱旀煡璇㈢殑 ml_shops 琛ㄨ幏鍙栧簵閾哄悕绉?
|
||||||
let shopName = '自营店铺'
|
let shopName = '鑷惀搴楅摵'
|
||||||
const shopsRaw = orderObj.get('ml_shops')
|
const shopsRaw = orderObj.get('ml_shops')
|
||||||
if (shopsRaw != null) {
|
if (shopsRaw != null) {
|
||||||
const shopStr = JSON.stringify(shopsRaw)
|
const shopStr = JSON.stringify(shopsRaw)
|
||||||
@@ -417,16 +417,16 @@ const loadOrders = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (merchantId != null && merchantId != '') {
|
} else if (merchantId != null && merchantId != '') {
|
||||||
shopName = '商家店铺'
|
shopName = '鍟嗗搴楅摵'
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[loadOrders] 订单号:', orderNo, '店铺:', shopName, '商品数:', productsList.length)
|
console.log('[loadOrders] 璁㈠崟鍙?', orderNo, '搴楅摵:', shopName, '鍟嗗搧鏁?', productsList.length)
|
||||||
|
|
||||||
// 如果没有商品数据,添加一个占位商品
|
// 濡傛灉娌℃湁鍟嗗搧鏁版嵁锛屾坊鍔犱竴涓崰浣嶅晢鍝?
|
||||||
if (productsList.length === 0) {
|
if (productsList.length === 0) {
|
||||||
const placeholderProduct: OrderProduct = {
|
const placeholderProduct: OrderProduct = {
|
||||||
id: 'placeholder',
|
id: 'placeholder',
|
||||||
name: '订单商品',
|
name: '璁㈠崟鍟嗗搧',
|
||||||
price: totalAmount ?? paidAmount ?? 0,
|
price: totalAmount ?? paidAmount ?? 0,
|
||||||
image: '/static/default-product.png',
|
image: '/static/default-product.png',
|
||||||
spec: '',
|
spec: '',
|
||||||
@@ -450,7 +450,7 @@ const loadOrders = async () => {
|
|||||||
mappedOrders.push(mappedOrder)
|
mappedOrders.push(mappedOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by created_at desc - 直接使用 OrderItem 类型访问属性
|
// Sort by created_at desc - 鐩存帴浣跨敤 OrderItem 绫诲瀷璁块棶灞炴€?
|
||||||
mappedOrders.sort((a: OrderItem, b: OrderItem) => {
|
mappedOrders.sort((a: OrderItem, b: OrderItem) => {
|
||||||
const timeA = new Date(a.create_time).getTime()
|
const timeA = new Date(a.create_time).getTime()
|
||||||
const timeB = new Date(b.create_time).getTime()
|
const timeB = new Date(b.create_time).getTime()
|
||||||
@@ -466,14 +466,14 @@ const loadOrders = async () => {
|
|||||||
filterOrdersByTab()
|
filterOrdersByTab()
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载订单异常:', err)
|
console.error('鍔犺浇璁㈠崟寮傚父:', err)
|
||||||
uni.showToast({ title: '加载订单失败', icon: 'none' })
|
uni.showToast({ title: '鍔犺浇璁㈠崟澶辫触', icon: 'none' })
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生命周期
|
// 鐢熷懡鍛ㄦ湡
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
if (options == null) return
|
if (options == null) return
|
||||||
const statusVal = options['status']
|
const statusVal = options['status']
|
||||||
@@ -487,9 +487,9 @@ onLoad((options) => {
|
|||||||
if (typeVal != null) {
|
if (typeVal != null) {
|
||||||
const type = typeVal as string
|
const type = typeVal as string
|
||||||
if (type === 'pending') activeTab.value = 'pending'
|
if (type === 'pending') activeTab.value = 'pending'
|
||||||
else if (type === 'shipped') activeTab.value = 'delivering' // 映射到待收货
|
else if (type === 'shipped') activeTab.value = 'delivering' // 鏄犲皠鍒板緟鏀惰揣
|
||||||
else if (type === 'review') activeTab.value = 'completed' // 映射到已完成
|
else if (type === 'review') activeTab.value = 'completed' // 鏄犲皠鍒板凡瀹屾垚
|
||||||
else if (type === 'refund') activeTab.value = 'all' // 申请售后默认显示全部
|
else if (type === 'refund') activeTab.value = 'all' // 鐢宠鍞悗榛樿鏄剧ず鍏ㄩ儴
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -503,12 +503,12 @@ const formatDate = (isoString: string): string => {
|
|||||||
return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数:获取当前订单数据(必须在 performSearch 之前定义)
|
// 杈呭姪鍑芥暟锛氳幏鍙栧綋鍓嶈鍗曟暟鎹紙蹇呴』鍦?performSearch 涔嬪墠瀹氫箟锛?
|
||||||
function getCurrentOrderData(): OrderItem[] {
|
function getCurrentOrderData(): OrderItem[] {
|
||||||
return allOrdersList.value
|
return allOrdersList.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索执行函数(必须在 onSearchInput 等之前定义)
|
// 鎼滅储鎵ц鍑芥暟锛堝繀椤诲湪 onSearchInput 绛変箣鍓嶅畾涔夛級
|
||||||
const performSearch = () => {
|
const performSearch = () => {
|
||||||
const keyword = searchKeyword.value.trim().toLowerCase()
|
const keyword = searchKeyword.value.trim().toLowerCase()
|
||||||
if (keyword == '') {
|
if (keyword == '') {
|
||||||
@@ -516,17 +516,17 @@ const performSearch = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在当前订单数据中搜索
|
// 鍦ㄥ綋鍓嶈鍗曟暟鎹腑鎼滅储
|
||||||
const allOrders = getCurrentOrderData()
|
const allOrders = getCurrentOrderData()
|
||||||
const filtered = allOrders.filter((order: any) => {
|
const filtered = allOrders.filter((order: any) => {
|
||||||
const orderObj = order as Record<string, any>
|
const orderObj = order as Record<string, any>
|
||||||
// 搜索订单号
|
// 鎼滅储璁㈠崟鍙?
|
||||||
const orderNo = orderObj['order_no'] as string
|
const orderNo = orderObj['order_no'] as string
|
||||||
if (orderNo != null && orderNo.toLowerCase().includes(keyword)) {
|
if (orderNo != null && orderNo.toLowerCase().includes(keyword)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索商品名称
|
// 鎼滅储鍟嗗搧鍚嶇О
|
||||||
const products = orderObj['products']
|
const products = orderObj['products']
|
||||||
if (products != null && Array.isArray(products)) {
|
if (products != null && Array.isArray(products)) {
|
||||||
return products.some((product: any) => {
|
return products.some((product: any) => {
|
||||||
@@ -542,7 +542,7 @@ const performSearch = () => {
|
|||||||
orders.value = filtered
|
orders.value = filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索相关函数
|
// 鎼滅储鐩稿叧鍑芥暟
|
||||||
const onSearchInput = (e: any) => {
|
const onSearchInput = (e: any) => {
|
||||||
const eObj = e as Record<string, any>
|
const eObj = e as Record<string, any>
|
||||||
const detail = eObj['detail'] as Record<string, any>
|
const detail = eObj['detail'] as Record<string, any>
|
||||||
@@ -568,23 +568,23 @@ const formatSpec = (specs: any): string => {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换标签
|
// 鍒囨崲鏍囩
|
||||||
const switchTab = (tabId: string) => {
|
const switchTab = (tabId: string) => {
|
||||||
activeTab.value = tabId
|
activeTab.value = tabId
|
||||||
filterOrdersByTab()
|
filterOrdersByTab()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态文本
|
// 鑾峰彇鐘舵€佹枃鏈?
|
||||||
const getStatusText = (status: number): string => {
|
const getStatusText = (status: number): string => {
|
||||||
if (status == 1) return '待付款'
|
if (status == 1) return '寰呬粯娆?
|
||||||
if (status == 2) return '待发货'
|
if (status == 2) return '寰呭彂璐?
|
||||||
if (status == 3) return '待收货'
|
if (status == 3) return '寰呮敹璐?
|
||||||
if (status == 4) return '已完成'
|
if (status == 4) return '宸插畬鎴?
|
||||||
if (status == 5) return '已取消'
|
if (status == 5) return '宸插彇娑?
|
||||||
return '未知状态'
|
return '鏈煡鐘舵€?
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态类名
|
// 鑾峰彇鐘舵€佺被鍚?
|
||||||
const getStatusClass = (status: number): string => {
|
const getStatusClass = (status: number): string => {
|
||||||
if (status == 1) return 'status-pending'
|
if (status == 1) return 'status-pending'
|
||||||
if (status == 2) return 'status-shipping'
|
if (status == 2) return 'status-shipping'
|
||||||
@@ -594,41 +594,41 @@ const getStatusClass = (status: number): string => {
|
|||||||
return 'status-unknown'
|
return 'status-unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下拉刷新
|
// 涓嬫媺鍒锋柊
|
||||||
const onRefresh = () => {
|
const onRefresh = () => {
|
||||||
refreshing.value = true
|
refreshing.value = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loadOrders()
|
loadOrders()
|
||||||
refreshing.value = false
|
refreshing.value = false
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '刷新成功',
|
title: '鍒锋柊鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上拉加载更多
|
// 涓婃媺鍔犺浇鏇村
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
if (loadingMore.value || !hasMore.value) return
|
if (loadingMore.value || !hasMore.value) return
|
||||||
|
|
||||||
// 暂未实现分页,直接返回
|
// 鏆傛湭瀹炵幇鍒嗛〉锛岀洿鎺ヨ繑鍥?
|
||||||
hasMore.value = false
|
hasMore.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 订单操作函数
|
// 璁㈠崟鎿嶄綔鍑芥暟
|
||||||
const cancelOrder = (orderId: string) => {
|
const cancelOrder = (orderId: string) => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '确认取消',
|
title: '纭鍙栨秷',
|
||||||
content: '确定要取消此订单吗?',
|
content: '纭畾瑕佸彇娑堟璁㈠崟鍚楋紵',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
// 这里应该是实际的API调用
|
// 杩欓噷搴旇鏄疄闄呯殑API璋冪敤
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '订单已取消',
|
title: '璁㈠崟宸插彇娑?,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新订单状态
|
// 鏇存柊璁㈠崟鐘舵€?
|
||||||
const index = orders.value.findIndex((o: any) => {
|
const index = orders.value.findIndex((o: any) => {
|
||||||
const obj = o as Record<string, any>
|
const obj = o as Record<string, any>
|
||||||
return obj['id'] === orderId
|
return obj['id'] === orderId
|
||||||
@@ -651,7 +651,7 @@ const payOrder = (orderId: string) => {
|
|||||||
|
|
||||||
const remindShipping = (orderId: string) => {
|
const remindShipping = (orderId: string) => {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已提醒卖家发货',
|
title: '宸叉彁閱掑崠瀹跺彂璐?,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -662,7 +662,7 @@ const viewLogistics = (orderId: string) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// goReview 必须在 doConfirmReceipt 之前定义,因为 doConfirmReceipt 会调用它
|
// goReview 蹇呴』鍦?doConfirmReceipt 涔嬪墠瀹氫箟锛屽洜涓?doConfirmReceipt 浼氳皟鐢ㄥ畠
|
||||||
const goReview = (order: any) => {
|
const goReview = (order: any) => {
|
||||||
const orderObj = order as Record<string, any>
|
const orderObj = order as Record<string, any>
|
||||||
const products = orderObj['products'] as any[]
|
const products = orderObj['products'] as any[]
|
||||||
@@ -678,18 +678,18 @@ const goReview = (order: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const doConfirmReceipt = async (orderId: string) => {
|
const doConfirmReceipt = async (orderId: string) => {
|
||||||
uni.showLoading({ title: '处理中...' })
|
uni.showLoading({ title: '澶勭悊涓?..' })
|
||||||
try {
|
try {
|
||||||
const result = await supabaseService.confirmReceipt(orderId)
|
const result = await supabaseService.confirmReceipt(orderId)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '收货成功',
|
title: '鏀惰揣鎴愬姛',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新本地状态
|
// 鏇存柊鏈湴鐘舵€?
|
||||||
const index = orders.value.findIndex((o: any) => {
|
const index = orders.value.findIndex((o: any) => {
|
||||||
const obj = o as Record<string, any>
|
const obj = o as Record<string, any>
|
||||||
return obj['id'] === orderId
|
return obj['id'] === orderId
|
||||||
@@ -700,7 +700,7 @@ const doConfirmReceipt = async (orderId: string) => {
|
|||||||
orders.value = [...orders.value]
|
orders.value = [...orders.value]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到评价页面
|
// 璺宠浆鍒拌瘎浠烽〉闈?
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const order = orders.value.find((o: any) => {
|
const order = orders.value.find((o: any) => {
|
||||||
const obj = o as Record<string, any>
|
const obj = o as Record<string, any>
|
||||||
@@ -712,14 +712,14 @@ const doConfirmReceipt = async (orderId: string) => {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: result.error ?? '确认收货失败',
|
title: result.error ?? '纭鏀惰揣澶辫触',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '系统异常',
|
title: '绯荤粺寮傚父',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -727,8 +727,8 @@ const doConfirmReceipt = async (orderId: string) => {
|
|||||||
|
|
||||||
const confirmReceipt = (orderId: string) => {
|
const confirmReceipt = (orderId: string) => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '确认收货',
|
title: '纭鏀惰揣',
|
||||||
content: '请确认您已收到商品,且商品无误',
|
content: '璇风‘璁ゆ偍宸叉敹鍒板晢鍝侊紝涓斿晢鍝佹棤璇?,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
doConfirmReceipt(orderId)
|
doConfirmReceipt(orderId)
|
||||||
@@ -739,13 +739,13 @@ const confirmReceipt = (orderId: string) => {
|
|||||||
|
|
||||||
const repurchase = (order: any) => {
|
const repurchase = (order: any) => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '再次购买',
|
title: '鍐嶆璐拱',
|
||||||
content: '确定要将这些商品加入购物车吗?',
|
content: '纭畾瑕佸皢杩欎簺鍟嗗搧鍔犲叆璐墿杞﹀悧锛?,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
// 这里应该是实际的API调用
|
// 杩欓噷搴旇鏄疄闄呯殑API璋冪敤
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已加入购物车',
|
title: '宸插姞鍏ヨ喘鐗╄溅',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -767,7 +767,7 @@ const onApplyRefund = (order: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导航函数
|
// 瀵艰埅鍑芥暟
|
||||||
const navigateToSearch = () => {
|
const navigateToSearch = () => {
|
||||||
uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
||||||
}
|
}
|
||||||
@@ -794,7 +794,7 @@ const goShopping = () => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 头部 */
|
/* 澶撮儴 */
|
||||||
.orders-header {
|
.orders-header {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
@@ -856,7 +856,7 @@ const goShopping = () => {
|
|||||||
/* cursor: pointer; removed */
|
/* cursor: pointer; removed */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 标签页 */
|
/* 鏍囩椤?*/
|
||||||
.order-tabs {
|
.order-tabs {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-bottom: 1px solid #e5e5e5;
|
border-bottom: 1px solid #e5e5e5;
|
||||||
@@ -879,15 +879,15 @@ const goShopping = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-item {
|
.tab-item {
|
||||||
/* 移除 flex: 1,改为自适应宽度或固定最小宽度 */
|
/* 绉婚櫎 flex: 1锛屾敼涓鸿嚜閫傚簲瀹藉害鎴栧浐瀹氭渶灏忓搴?*/
|
||||||
padding: 15px 15px; /* 增加水平内边距 */
|
padding: 15px 15px; /* 澧炲姞姘村钩鍐呰竟璺?*/
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
white-space: nowrap; /* 防止文字换行 */
|
white-space: nowrap; /* 闃叉鏂囧瓧鎹㈣ */
|
||||||
flex-shrink: 0; /* 防止被压缩 */
|
flex-shrink: 0; /* 闃叉琚帇缂?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item.active {
|
.tab-item.active {
|
||||||
@@ -930,14 +930,14 @@ const goShopping = () => {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 内容区 */
|
/* 鍐呭鍖?*/
|
||||||
.orders-content {
|
.orders-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 绌虹姸鎬?*/
|
||||||
.empty-orders {
|
.empty-orders {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -973,7 +973,7 @@ const goShopping = () => {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 订单列表 */
|
/* 璁㈠崟鍒楄〃 */
|
||||||
.order-list {
|
.order-list {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
@@ -986,7 +986,7 @@ const goShopping = () => {
|
|||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 订单头部 */
|
/* 璁㈠崟澶撮儴 */
|
||||||
.order-header {
|
.order-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -1025,14 +1025,14 @@ const goShopping = () => {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 订单商品 */
|
/* 璁㈠崟鍟嗗搧 */
|
||||||
.order-products {
|
.order-products {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-product {
|
.order-product {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 显式声明横向排列 */
|
flex-direction: row; /* 鏄惧紡澹版槑妯悜鎺掑垪 */
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@@ -1046,16 +1046,16 @@ const goShopping = () => {
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
flex-shrink: 0; /* 防止图片被压缩 */
|
flex-shrink: 0; /* 闃叉鍥剧墖琚帇缂?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-info {
|
.product-info {
|
||||||
flex: 1; /* 占据右侧剩余所有空间 */
|
flex: 1; /* 鍗犳嵁鍙充晶鍓╀綑鎵€鏈夌┖闂?*/
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
overflow: hidden; /* 防止文字溢出 */
|
overflow: hidden; /* 闃叉鏂囧瓧婧㈠嚭 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-top-info {
|
.product-top-info {
|
||||||
@@ -1098,7 +1098,7 @@ const goShopping = () => {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 订单信息 */
|
/* 璁㈠崟淇℃伅 */
|
||||||
.order-info {
|
.order-info {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-top: 1px solid #f5f5f5;
|
border-top: 1px solid #f5f5f5;
|
||||||
@@ -1138,7 +1138,7 @@ const goShopping = () => {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 订单操作 */
|
/* 璁㈠崟鎿嶄綔 */
|
||||||
.order-actions {
|
.order-actions {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
@@ -1193,7 +1193,7 @@ const goShopping = () => {
|
|||||||
border-color: #ff5000;
|
border-color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加载更多 */
|
/* 鍔犺浇鏇村 */
|
||||||
.loading-more {
|
.loading-more {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1218,18 +1218,18 @@ const goShopping = () => {
|
|||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 安全区域 */
|
/* 瀹夊叏鍖哄煙 */
|
||||||
.safe-area {
|
.safe-area {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 底部导航占位 */
|
/* 搴曢儴瀵艰埅鍗犱綅 */
|
||||||
.tabbar-placeholder {
|
.tabbar-placeholder {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式适配 */
|
/* 鍝嶅簲寮忛€傞厤 */
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.order-list {
|
.order-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1254,7 +1254,7 @@ const goShopping = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 订单卡片新样式 */
|
/* 璁㈠崟鍗$墖鏂版牱寮?*/
|
||||||
.order-card-header {
|
.order-card-header {
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1356,3 +1356,5 @@ const goShopping = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- pages/mall/consumer/orders.uvue -->
|
<!-- pages/mall/consumer/orders.uvue -->
|
||||||
<template>
|
<template>
|
||||||
<view class="orders-page">
|
<view class="orders-page">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
@@ -74,9 +74,12 @@
|
|||||||
<text class="shop-name">{{ order.shop_name != null && order.shop_name != '' ? order.shop_name : '自营店铺' }}</text>
|
<text class="shop-name">{{ order.shop_name != null && order.shop_name != '' ? order.shop_name : '自营店铺' }}</text>
|
||||||
<text class="arrow-right">›</text>
|
<text class="arrow-right">›</text>
|
||||||
</view>
|
</view>
|
||||||
<text :class="['order-status', getStatusClass(order.status)]">
|
<view class="status-row">
|
||||||
{{ getStatusText(order.status) }}
|
<text :class="['order-status', getStatusClass(order.status)]">
|
||||||
</text>
|
{{ getStatusText(order.status) }}
|
||||||
|
</text>
|
||||||
|
<text class="more-btn" @click.stop="showOrderMenu(order)">⋯</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 订单商品 -->
|
<!-- 订单商品 -->
|
||||||
@@ -141,6 +144,16 @@
|
|||||||
<view v-if="order.status === 5" class="action-buttons">
|
<view v-if="order.status === 5" class="action-buttons">
|
||||||
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view v-if="order.status === 6" class="action-buttons">
|
||||||
|
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||||||
|
<button class="action-btn refund" @click="viewRefundProgress(order.id)">退款进度</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="order.status === 7" class="action-buttons">
|
||||||
|
<button class="action-btn view" @click="viewOrderDetail(order.id)">查看详情</button>
|
||||||
|
<button class="action-btn repurchase" @click="repurchase(order)">再次购买</button>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -218,6 +231,7 @@ const orderTabs = ref<OrderTabItem[]>([
|
|||||||
{ id: 'shipping', name: '待发货', count: 0 },
|
{ id: 'shipping', name: '待发货', count: 0 },
|
||||||
{ id: 'delivering', name: '待收货', count: 0 },
|
{ id: 'delivering', name: '待收货', count: 0 },
|
||||||
{ id: 'completed', name: '已完成', count: 0 },
|
{ id: 'completed', name: '已完成', count: 0 },
|
||||||
|
{ id: 'aftersale', name: '售后', count: 0 },
|
||||||
{ id: 'cancelled', name: '已取消', count: 0 }
|
{ id: 'cancelled', name: '已取消', count: 0 }
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -234,6 +248,7 @@ const getStatusByTab = (tabId: string): number => {
|
|||||||
if (tabId == 'delivering') return 3
|
if (tabId == 'delivering') return 3
|
||||||
if (tabId == 'completed') return 4
|
if (tabId == 'completed') return 4
|
||||||
if (tabId == 'cancelled') return 5
|
if (tabId == 'cancelled') return 5
|
||||||
|
if (tabId == 'aftersale') return 6
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,27 +329,31 @@ function parseSpecText(specs: any): string {
|
|||||||
|
|
||||||
// 辅助函数:更新标签计数
|
// 辅助函数:更新标签计数
|
||||||
const updateTabsCounts = (allOrders: OrderItem[]) => {
|
const updateTabsCounts = (allOrders: OrderItem[]) => {
|
||||||
// 计算各状态数量
|
|
||||||
const countAll = allOrders.length
|
const countAll = allOrders.length
|
||||||
const countPending = allOrders.filter((o: OrderItem) => o.status === 1).length
|
const countPending = allOrders.filter((o: OrderItem) => o.status === 1).length
|
||||||
const countShipping = allOrders.filter((o: OrderItem) => o.status === 2).length
|
const countShipping = allOrders.filter((o: OrderItem) => o.status === 2).length
|
||||||
const countDelivering = allOrders.filter((o: OrderItem) => o.status === 3).length
|
const countDelivering = allOrders.filter((o: OrderItem) => o.status === 3).length
|
||||||
const countCompleted = allOrders.filter((o: OrderItem) => o.status === 4).length
|
const countCompleted = allOrders.filter((o: OrderItem) => o.status === 4).length
|
||||||
const countCancelled = allOrders.filter((o: OrderItem) => o.status === 5).length
|
const countCancelled = allOrders.filter((o: OrderItem) => o.status === 5).length
|
||||||
|
const countAftersale = allOrders.filter((o: OrderItem) => o.status === 6 || o.status === 7).length
|
||||||
|
|
||||||
// 更新数组元素
|
|
||||||
orderTabs.value[0].count = countAll
|
orderTabs.value[0].count = countAll
|
||||||
orderTabs.value[1].count = countPending
|
orderTabs.value[1].count = countPending
|
||||||
orderTabs.value[2].count = countShipping
|
orderTabs.value[2].count = countShipping
|
||||||
orderTabs.value[3].count = countDelivering
|
orderTabs.value[3].count = countDelivering
|
||||||
orderTabs.value[4].count = countCompleted
|
orderTabs.value[4].count = countCompleted
|
||||||
orderTabs.value[5].count = countCancelled
|
orderTabs.value[5].count = countAftersale
|
||||||
|
orderTabs.value[6].count = countCancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数:按标签筛选订单
|
// 辅助函数:按标签筛选订单
|
||||||
const filterOrdersByTab = () => {
|
const filterOrdersByTab = () => {
|
||||||
if (activeTab.value === 'all') {
|
if (activeTab.value === 'all') {
|
||||||
orders.value = allOrdersList.value
|
orders.value = allOrdersList.value
|
||||||
|
} else if (activeTab.value === 'aftersale') {
|
||||||
|
orders.value = allOrdersList.value.filter((o: OrderItem) => {
|
||||||
|
return o.status === 6 || o.status === 7
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
const targetStatus = getStatusByTab(activeTab.value)
|
const targetStatus = getStatusByTab(activeTab.value)
|
||||||
orders.value = allOrdersList.value.filter((o: OrderItem) => {
|
orders.value = allOrdersList.value.filter((o: OrderItem) => {
|
||||||
@@ -489,7 +508,7 @@ onLoad((options) => {
|
|||||||
const statusVal = options['status']
|
const statusVal = options['status']
|
||||||
if (statusVal != null) {
|
if (statusVal != null) {
|
||||||
const status = statusVal as string
|
const status = statusVal as string
|
||||||
if (['all', 'pending', 'shipping', 'delivering', 'completed', 'cancelled'].includes(status)) {
|
if (['all', 'pending', 'shipping', 'delivering', 'completed', 'aftersale', 'cancelled'].includes(status)) {
|
||||||
activeTab.value = status
|
activeTab.value = status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -608,6 +627,47 @@ const getStatusClass = (status: number): string => {
|
|||||||
return 'status-unknown'
|
return 'status-unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 联系卖家
|
||||||
|
const contactSeller = (order: OrderItem) => {
|
||||||
|
if (order.merchant_id != '') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/mall/consumer/chat?merchantId=${order.merchant_id}`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '暂无卖家联系方式',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除订单
|
||||||
|
const deleteOrder = (orderId: string) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除订单',
|
||||||
|
content: '确定要删除此订单吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showLoading({ title: '删除中...' })
|
||||||
|
supabaseService.deleteOrder(orderId).then(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '订单已删除',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
loadOrders()
|
||||||
|
}).catch(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 下拉刷新
|
// 下拉刷新
|
||||||
const onRefresh = () => {
|
const onRefresh = () => {
|
||||||
refreshing.value = true
|
refreshing.value = true
|
||||||
@@ -636,22 +696,28 @@ const cancelOrder = (orderId: string) => {
|
|||||||
content: '确定要取消此订单吗?',
|
content: '确定要取消此订单吗?',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
// 这里应该是实际的API调用
|
uni.showLoading({ title: '取消中...' })
|
||||||
uni.showToast({
|
supabaseService.cancelOrder(orderId).then((success) => {
|
||||||
title: '订单已取消',
|
uni.hideLoading()
|
||||||
icon: 'success'
|
if (success) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '订单已取消',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
loadOrders()
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '取消失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '取消失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新订单状态
|
|
||||||
const index = orders.value.findIndex((o: any) => {
|
|
||||||
const obj = o as Record<string, any>
|
|
||||||
return obj['id'] === orderId
|
|
||||||
})
|
|
||||||
if (index !== -1) {
|
|
||||||
const orderObj = orders.value[index] as Record<string, any>
|
|
||||||
orderObj['status'] = 5
|
|
||||||
orders.value = [...orders.value]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -663,7 +729,35 @@ const payOrder = (orderId: string) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const remindShipping = (orderId: string) => {
|
const remindShipping = async (orderId: string) => {
|
||||||
|
// 基础提醒
|
||||||
|
uni.showLoading({ title: '正在提醒...' })
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 查找订单中的商家ID
|
||||||
|
const order = orders.value.find(o => o.id === orderId)
|
||||||
|
if (order != null) {
|
||||||
|
const merchantId = order.merchant_id
|
||||||
|
const orderNo = order.order_no
|
||||||
|
|
||||||
|
if (merchantId != '') {
|
||||||
|
// 向商家发送自动催单消息
|
||||||
|
const message = `你好,我的订单[${orderNo}]还没有发货,请尽快安排,谢谢。`
|
||||||
|
const success = await supabaseService.sendChatMessage(message, merchantId)
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
console.log('催单消息发送成功')
|
||||||
|
} else {
|
||||||
|
console.warn('催单消息发送失败,可能是由于网络原因')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('提醒发货异常:', e)
|
||||||
|
} finally {
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '已提醒卖家发货',
|
title: '已提醒卖家发货',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
@@ -752,19 +846,81 @@ const confirmReceipt = (orderId: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const repurchase = (order: any) => {
|
const repurchase = (order: any) => {
|
||||||
uni.showModal({
|
const orderObj = order as Record<string, any>
|
||||||
title: '再次购买',
|
const products = orderObj['products'] as any[]
|
||||||
content: '确定要将这些商品加入购物车吗?',
|
|
||||||
success: (res) => {
|
if (products == null || products.length === 0) {
|
||||||
if (res.confirm) {
|
uni.showToast({
|
||||||
// 这里应该是实际的API调用
|
title: '订单无商品',
|
||||||
uni.showToast({
|
icon: 'none'
|
||||||
title: '已加入购物车',
|
})
|
||||||
icon: 'success'
|
return
|
||||||
})
|
}
|
||||||
|
|
||||||
|
uni.showLoading({ title: '处理中...' })
|
||||||
|
|
||||||
|
let completed = 0
|
||||||
|
const total = products.length
|
||||||
|
let successCount = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < products.length; i++) {
|
||||||
|
const pObj = products[i] as Record<string, any>
|
||||||
|
const productId = pObj['id'] as string
|
||||||
|
const merchantId = orderObj['merchant_id'] as string
|
||||||
|
|
||||||
|
if (productId != null && productId !== '') {
|
||||||
|
supabaseService.addToCart(productId, 1, '', merchantId ?? '').then((success) => {
|
||||||
|
completed++
|
||||||
|
if (success) successCount++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (successCount > 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `已添加${successCount}件商品`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '添加失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
completed++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (successCount > 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `已添加${successCount}件商品`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '添加失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
completed++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (successCount > 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `已添加${successCount}件商品`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '添加失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewOrderDetail = (orderId: string) => {
|
const viewOrderDetail = (orderId: string) => {
|
||||||
@@ -781,6 +937,65 @@ const onApplyRefund = (order: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const viewRefundProgress = (orderId: string) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/mall/consumer/refund?orderId=${orderId}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理订单操作
|
||||||
|
const handleOrderAction = (order: OrderItem, action: string) => {
|
||||||
|
if (action === '取消订单') {
|
||||||
|
cancelOrder(order.id)
|
||||||
|
} else if (action === '联系卖家') {
|
||||||
|
contactSeller(order)
|
||||||
|
} else if (action === '提醒发货') {
|
||||||
|
remindShipping(order.id)
|
||||||
|
} else if (action === '申请退款' || action === '申请售后') {
|
||||||
|
onApplyRefund(order)
|
||||||
|
} else if (action === '查看物流') {
|
||||||
|
viewLogistics(order.id)
|
||||||
|
} else if (action === '确认收货') {
|
||||||
|
confirmReceipt(order.id)
|
||||||
|
} else if (action === '再次购买') {
|
||||||
|
repurchase(order)
|
||||||
|
} else if (action === '删除订单') {
|
||||||
|
deleteOrder(order.id)
|
||||||
|
} else if (action === '退款进度') {
|
||||||
|
viewRefundProgress(order.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示订单操作菜单
|
||||||
|
const showOrderMenu = (order: OrderItem) => {
|
||||||
|
const status = order.status
|
||||||
|
let actions: string[] = []
|
||||||
|
|
||||||
|
if (status === 1) {
|
||||||
|
actions = ['取消订单', '联系卖家']
|
||||||
|
} else if (status === 2) {
|
||||||
|
actions = ['提醒发货', '申请退款', '联系卖家']
|
||||||
|
} else if (status === 3) {
|
||||||
|
actions = ['查看物流', '确认收货', '申请退款', '联系卖家']
|
||||||
|
} else if (status === 4) {
|
||||||
|
actions = ['申请售后', '再次购买', '联系卖家']
|
||||||
|
} else if (status === 5) {
|
||||||
|
actions = ['删除订单', '再次购买', '联系卖家']
|
||||||
|
} else if (status === 6) {
|
||||||
|
actions = ['退款进度', '联系卖家']
|
||||||
|
} else if (status === 7) {
|
||||||
|
actions = ['再次购买', '联系卖家']
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: actions,
|
||||||
|
success: (res) => {
|
||||||
|
const action = actions[res.tapIndex]
|
||||||
|
handleOrderAction(order, action)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 导航函数
|
// 导航函数
|
||||||
const navigateToSearch = () => {
|
const navigateToSearch = () => {
|
||||||
uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
uni.navigateTo({ url: '/pages/mall/consumer/search' })
|
||||||
@@ -799,12 +1014,14 @@ const goShopping = () => {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.orders-page {
|
.orders-page {
|
||||||
width: 100%;
|
position: absolute;
|
||||||
height: 100vh;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,9 +1033,8 @@ const goShopping = () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
/* position: sticky; removed */
|
|
||||||
/* top: 0; removed */
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-search.full-width {
|
.header-search.full-width {
|
||||||
@@ -879,10 +1095,29 @@ const goShopping = () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
height: 50px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 安卓端滚动修复 */
|
||||||
|
.orders-content {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 0; /* 关键:强制 flex: 1 生效 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-header {
|
||||||
|
background-color: white;
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item-fixed {
|
.tab-item-fixed {
|
||||||
padding: 15px 15px;
|
padding: 0 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -891,6 +1126,7 @@ const goShopping = () => {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item-fixed.active {
|
.tab-item-fixed.active {
|
||||||
@@ -958,8 +1194,8 @@ const goShopping = () => {
|
|||||||
/* 内容区 */
|
/* 内容区 */
|
||||||
.orders-content {
|
.orders-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 0; /* 关键:强制让 flex:1 在安卓端生效,防止内容撑开父容器 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 空状态 */
|
||||||
@@ -1001,6 +1237,8 @@ const goShopping = () => {
|
|||||||
/* 订单列表 */
|
/* 订单列表 */
|
||||||
.order-list {
|
.order-list {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-card {
|
.order-card {
|
||||||
@@ -1009,6 +1247,8 @@ const goShopping = () => {
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 订单头部 */
|
/* 订单头部 */
|
||||||
@@ -1268,15 +1508,20 @@ const goShopping = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 20px;
|
width: 100%;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-content: flex-start;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-card {
|
.order-card {
|
||||||
width: 48%;
|
width: 48%;
|
||||||
margin: 0 1% 20px 1%;
|
margin: 0 1% 20px 1%;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||||
|
flex: none;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1284,6 +1529,8 @@ const goShopping = () => {
|
|||||||
.order-card {
|
.order-card {
|
||||||
width: 31%;
|
width: 31%;
|
||||||
margin: 0 1% 20px 1%;
|
margin: 0 1% 20px 1%;
|
||||||
|
flex: none;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1297,6 +1544,19 @@ const goShopping = () => {
|
|||||||
border-bottom: 1px solid #f9f9f9;
|
border-bottom: 1px solid #f9f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-btn {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #999;
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.shop-info {
|
.shop-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -1389,3 +1649,4 @@ const goShopping = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="payment-success-page">
|
<view class="payment-success-page">
|
||||||
<view class="success-content">
|
<view class="success-content">
|
||||||
<view class="icon-wrapper">
|
<view class="icon-wrapper">
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
|
||||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||||
|
|
||||||
const orderId = ref('')
|
const orderId = ref('')
|
||||||
@@ -180,10 +179,16 @@ const goHome = () => {
|
|||||||
.btn {
|
.btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
|
line-height: 45px;
|
||||||
|
text-align: center;
|
||||||
border-radius: 22.5px;
|
border-radius: 22.5px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-btn {
|
.primary-btn {
|
||||||
@@ -198,3 +203,4 @@ const goHome = () => {
|
|||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 支付页面 -->
|
<!-- 支付页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="payment-page">
|
<view class="payment-page">
|
||||||
<view class="payment-content">
|
<view class="payment-content">
|
||||||
@@ -110,7 +110,6 @@
|
|||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref, onMounted, watch, computed, onUnmounted } from 'vue'
|
import { ref, onMounted, watch, computed, onUnmounted } from 'vue'
|
||||||
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
|
||||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||||
|
|
||||||
type PaymentMethodType = {
|
type PaymentMethodType = {
|
||||||
@@ -925,3 +924,4 @@ onUnmounted(() => {
|
|||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="points-page">
|
<view class="points-page">
|
||||||
<view class="points-header">
|
<view class="points-header">
|
||||||
<view class="points-info">
|
<view class="points-info">
|
||||||
|
|||||||
1584
pages/mall/consumer/product-detail copy.uvue
Normal file
@@ -1,4 +1,4 @@
|
|||||||
<!-- 消费者端 - 商品详情页 -->
|
<!-- 消费者端 - 商品详情页 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="product-detail-page">
|
<view class="product-detail-page">
|
||||||
<scroll-view class="page-scroll" scroll-y="true">
|
<scroll-view class="page-scroll" scroll-y="true">
|
||||||
@@ -42,54 +42,54 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 优惠券入口 (新增) -->
|
<!-- 优惠券入口 (新增) -->
|
||||||
<view class="coupon-entry" @click="showCouponModal" v-if="coupons.length > 0">
|
<view class="detail-cell coupon-entry" @click="showCouponModal" v-if="coupons.length > 0">
|
||||||
<view class="coupon-entry-left">
|
<text class="cell-label">优惠</text>
|
||||||
<text class="coupon-entry-label">优惠</text>
|
<view class="cell-content flex-row">
|
||||||
<view class="coupon-tags-row">
|
<text class="coupon-tag" v-for="(coupon, index) in coupons.slice(0, 2)" :key="index">
|
||||||
<text class="coupon-tag" v-for="(coupon, index) in coupons.slice(0, 2)" :key="index">
|
{{ coupon.name }}
|
||||||
{{ coupon.name }}
|
</text>
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
<text class="coupon-arrow">领券 ></text>
|
<text class="cell-arrow">领券 ></text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品参数 -->
|
<!-- 商品参数 -->
|
||||||
<view class="params-section" @click="showParamsModal">
|
<view class="detail-cell params-section" @click="showParamsModal">
|
||||||
<text class="params-title">商品参数</text>
|
<text class="cell-label">参数</text>
|
||||||
<view class="params-summary">
|
<view class="cell-content">
|
||||||
<text class="params-item" v-if="product.specification">规格: {{ product.specification }}</text>
|
<text class="params-summary-text">{{ getParamsSummary() }}</text>
|
||||||
<text class="params-item" v-if="product.expiry_date">有效期: {{ product.expiry_date }}</text>
|
|
||||||
<text class="params-item" v-if="product.approval_number">批准文号: {{ product.approval_number }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
<text class="params-arrow">></text>
|
<text class="cell-arrow">></text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 规格选择 -->
|
<!-- 规格选择 -->
|
||||||
<view class="spec-section" @click="showSpecModal" v-if="productSkus.length > 0">
|
<view class="detail-cell spec-section" @click="showSpecModal" v-if="productSkus.length > 0">
|
||||||
<text class="spec-title">规格</text>
|
<text class="cell-label">规格</text>
|
||||||
<text class="spec-selected">{{ selectedSpec ?? '请选择规格' }}</text>
|
<view class="cell-content">
|
||||||
<text class="spec-arrow">></text>
|
<text class="spec-selected">{{ selectedSpec ?? '请选择规格' }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="cell-arrow">></text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 数量选择 -->
|
<!-- 数量选择 -->
|
||||||
<view class="quantity-section">
|
<view class="detail-cell quantity-section">
|
||||||
<text class="quantity-title">数量</text>
|
<text class="cell-label">数量</text>
|
||||||
<view class="quantity-selector">
|
<view class="cell-content flex-row align-center justify-between">
|
||||||
<view class="quantity-btn minus" @click="decreaseQuantity">
|
<view class="quantity-selector flex-row align-center">
|
||||||
<text class="quantity-btn-text">-</text>
|
<view class="quantity-btn minus" @click="decreaseQuantity">
|
||||||
</view>
|
<text class="quantity-btn-text">-</text>
|
||||||
<input class="quantity-input"
|
</view>
|
||||||
type="number"
|
<input class="quantity-input"
|
||||||
:value="quantity.toString()"
|
type="number"
|
||||||
:min="1"
|
:value="quantity.toString()"
|
||||||
:max="getMaxQuantity()"
|
:min="1"
|
||||||
@input="validateQuantity" />
|
:max="getMaxQuantity()"
|
||||||
<view class="quantity-btn plus" @click="increaseQuantity">
|
@input="validateQuantity" />
|
||||||
<text class="quantity-btn-text">+</text>
|
<view class="quantity-btn plus" @click="increaseQuantity">
|
||||||
|
<text class="quantity-btn-text">+</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<text class="quantity-stock">库存{{ getAvailableStock() }}件</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="quantity-stock">库存{{ getAvailableStock() }}件</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品详情 -->
|
<!-- 商品详情 -->
|
||||||
@@ -111,23 +111,23 @@
|
|||||||
<!-- 底部操作栏 -->
|
<!-- 底部操作栏 -->
|
||||||
<view class="bottom-actions">
|
<view class="bottom-actions">
|
||||||
<view class="action-buttons">
|
<view class="action-buttons">
|
||||||
<!-- 客服按钮 (新增) -->
|
<!-- 客服按钮 -->
|
||||||
<view class="action-btn" @click="contactMerchant">
|
<view class="action-btn" @click="contactMerchant">
|
||||||
<text class="action-icon">💬</text>
|
<image src="/static/icons/customer-service.png" class="action-icon-img" />
|
||||||
<text class="action-text">客服</text>
|
<text class="action-text">客服</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-btn" @click="goToCart">
|
<view class="action-btn" @click="goToCart">
|
||||||
<text class="action-icon">🛒</text>
|
<image src="/static/tabbar/cart.png" class="action-icon-img" />
|
||||||
<text class="action-text">购物车</text>
|
<text class="action-text">购物车</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-btn" @click="toggleFavorite">
|
<view class="action-btn" @click="toggleFavorite">
|
||||||
<text class="action-icon">{{ isFavorite ? '❤️' : '🤍' }}</text>
|
<image :src="isFavorite ? '/static/icons/favorite.png' : '/static/icons/favorite-active.png'" class="action-icon-img" />
|
||||||
<text class="action-text">{{ isFavorite ? '已收藏' : '收藏' }}</text>
|
<text class="action-text">{{ isFavorite ? '已收藏' : '收藏' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="btn-group">
|
<view class="btn-group">
|
||||||
<button class="cart-btn" @click="addToCart">加入购物车</button>
|
<button class="cart-btn" @click="addToCart">加入购物车</button>
|
||||||
<button class="buy-btn" @click="buyNow">立即购买</button>
|
<button class="buy-btn" @click="buyNow">立即购买</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -696,8 +696,17 @@ export default {
|
|||||||
getSkuSpecText(sku: ProductSkuType): string {
|
getSkuSpecText(sku: ProductSkuType): string {
|
||||||
if (sku.specifications != null) {
|
if (sku.specifications != null) {
|
||||||
const specs = sku.specifications as UTSJSONObject
|
const specs = sku.specifications as UTSJSONObject
|
||||||
// 简化处理,直接返回 JSON 字符串
|
let specStr = ''
|
||||||
return JSON.stringify(specs)
|
// 在 UTS 中遍历 UTSJSONObject 的推荐方式
|
||||||
|
for (const key in specs) {
|
||||||
|
const val = specs[key]
|
||||||
|
if (val != null) {
|
||||||
|
specStr += (specStr === '' ? '' : ' ') + val.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (specStr !== '') {
|
||||||
|
return specStr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sku.sku_code ?? ''
|
return sku.sku_code ?? ''
|
||||||
},
|
},
|
||||||
@@ -877,6 +886,15 @@ export default {
|
|||||||
|
|
||||||
hideParamsModal() {
|
hideParamsModal() {
|
||||||
this.showParams = false
|
this.showParams = false
|
||||||
|
},
|
||||||
|
|
||||||
|
getParamsSummary(): string {
|
||||||
|
let summary = ''
|
||||||
|
if (this.product.specification != null && (this.product.specification as string) != '') summary += '规格 '
|
||||||
|
if (this.product.expiry_date != null && (this.product.expiry_date as string) != '') summary += '有效期 '
|
||||||
|
if (this.product.approval_number != null && (this.product.approval_number as string) != '') summary += '批准文号 '
|
||||||
|
const finalSummary = summary.trim()
|
||||||
|
return finalSummary != '' ? finalSummary : '查看详情'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -936,7 +954,7 @@ export default {
|
|||||||
.current-price {
|
.current-price {
|
||||||
font-size: 48rpx;
|
font-size: 48rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #ff4444;
|
color: #ff5000;
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1006,44 +1024,6 @@ export default {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Coupon Entry Styles */
|
|
||||||
.coupon-entry {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 30rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.coupon-entry-left {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.coupon-entry-label {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: #333;
|
|
||||||
width: 120rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.coupon-tags-row {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.coupon-tag {
|
|
||||||
font-size: 20rpx;
|
|
||||||
color: #ff4444;
|
|
||||||
border: 1px solid #ff4444;
|
|
||||||
padding: 2rpx 10rpx;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
margin-right: 15rpx;
|
|
||||||
}
|
|
||||||
.coupon-arrow {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modal Popup Styles */
|
/* Modal Popup Styles */
|
||||||
.popup-mask {
|
.popup-mask {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1100,7 +1080,7 @@ export default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-right: 1px dashed #ffccc7;
|
border-right: 1px dashed #ffccc7;
|
||||||
color: #ff4444;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
.coupon-amount {
|
.coupon-amount {
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
@@ -1135,7 +1115,7 @@ export default {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
.coupon-btn {
|
.coupon-btn {
|
||||||
background-color: #ff4444;
|
background-color: #ff5000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
@@ -1145,48 +1125,86 @@ export default {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spec-section {
|
.product-description {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 30rpx;
|
padding: 30rpx;
|
||||||
|
padding-bottom: 140rpx;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.spec-title {
|
.section-title {
|
||||||
font-size: 30rpx;
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
width: 120rpx;
|
margin-bottom: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spec-selected {
|
.description-text {
|
||||||
flex: 1;
|
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #666;
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spec-arrow {
|
/* 统一Cell样式优化 */
|
||||||
|
.detail-cell {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 32rpx 30rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1rpx solid #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-label {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #999;
|
color: #999;
|
||||||
|
width: 90rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-content {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-arrow {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ccc;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 覆盖具体板块样式 */
|
||||||
|
.coupon-entry, .params-section, .spec-section {
|
||||||
|
margin-bottom: 0; /* 连在一起显示 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.quantity-section {
|
.quantity-section {
|
||||||
background-color: #fff;
|
margin-bottom: 20rpx; /* 数量选择作为最后一项保留底边距 */
|
||||||
padding: 30rpx;
|
border-bottom: none;
|
||||||
margin-bottom: 20rpx;
|
}
|
||||||
|
|
||||||
|
.params-summary-text, .spec-selected {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-between {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quantity-title {
|
/* 数量加减器样式 */
|
||||||
font-size: 30rpx;
|
|
||||||
color: #333;
|
|
||||||
width: 120rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity-selector {
|
.quantity-selector {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1rpx solid #e5e5e5;
|
border: 1rpx solid #e5e5e5;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
@@ -1230,26 +1248,6 @@ export default {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-description {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 30rpx;
|
|
||||||
padding-bottom: 140rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #666;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-actions {
|
.bottom-actions {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -1280,8 +1278,9 @@ export default {
|
|||||||
min-width: 80rpx;
|
min-width: 80rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-icon {
|
.action-icon-img {
|
||||||
font-size: 40rpx;
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
margin-bottom: 4rpx;
|
margin-bottom: 4rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1308,12 +1307,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cart-btn {
|
.cart-btn {
|
||||||
background-color: #ffa726;
|
background-color: #ff5000;
|
||||||
|
opacity: 0.8;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buy-btn {
|
.buy-btn {
|
||||||
background-color: #ff4444;
|
background-color: #ff5000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1378,7 +1378,7 @@ export default {
|
|||||||
|
|
||||||
.spec-price {
|
.spec-price {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #ff4444;
|
color: #ff5000;
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1550,3 +1550,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1786
pages/mall/consumer/profile copy 2.uvue
Normal file
1336
pages/mall/consumer/profile copy.uvue
Normal file
@@ -1,28 +1,28 @@
|
|||||||
<!-- 消费者端 - 个人中心 -->
|
<!-- 消费者端 - 个人中心 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="consumer-profile">
|
<view class="consumer-profile">
|
||||||
<!-- 智能顶部导航栏 - 与消息页保持一致 -->
|
<!-- 智能顶部导航栏 - 与消息页保持一致 -->
|
||||||
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view class="nav-container">
|
<view class="nav-container">
|
||||||
<!-- 头像 -->
|
<!-- 基础用户信息:头像和昵称 -->
|
||||||
<image
|
<view class="nav-user-basic" @click="editProfile">
|
||||||
:src="userInfo.avatar_url != '' ? userInfo.avatar_url : '/static/default-avatar.png'"
|
<image
|
||||||
class="nav-avatar"
|
:src="userInfo.avatar_url != '' ? userInfo.avatar_url : '/static/default-avatar.png'"
|
||||||
@click="editProfile"
|
class="nav-avatar"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 用户信息横向排列 (名字、积分、余额、优惠券) -->
|
|
||||||
<view class="nav-user-stats">
|
|
||||||
<text class="nav-user-name">{{ userInfo.nickname != '' ? userInfo.nickname : userInfo.phone }}</text>
|
<text class="nav-user-name">{{ userInfo.nickname != '' ? userInfo.nickname : userInfo.phone }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 用户资产横向排列 (积分、余额、优惠券) -->
|
||||||
|
<view class="nav-user-stats">
|
||||||
<view class="nav-stat-item" @click="goToPoints">
|
<view class="nav-stat-item" @click="goToPoints">
|
||||||
<text class="nav-stat-label">积分</text>
|
<text class="nav-stat-label">积分</text>
|
||||||
<text class="nav-stat-value">{{ userStats.points }}</text>
|
<text class="nav-stat-value">{{ userStats.points }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="nav-stat-item">
|
<view class="nav-stat-item" @click="goToWallet">
|
||||||
<text class="nav-stat-label">余额</text>
|
<text class="nav-stat-label">余额</text>
|
||||||
<text class="nav-stat-value" @click="goToWallet">¥{{ userStats.balance }}</text>
|
<text class="nav-stat-value">¥{{ userStats.balance }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="nav-stat-item" @click="goToCoupons">
|
<view class="nav-stat-item" @click="goToCoupons">
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 设置按钮 (右侧) -->
|
<!-- 设置按钮 (最右侧) -->
|
||||||
<view class="nav-actions">
|
<view class="nav-actions">
|
||||||
<view class="action-btn" @click="goToSettings">
|
<view class="action-btn" @click="goToSettings">
|
||||||
<text class="action-icon">⚙️</text>
|
<text class="action-icon">⚙️</text>
|
||||||
@@ -88,7 +88,10 @@
|
|||||||
|
|
||||||
<!-- 订单状态快捷入口 -->
|
<!-- 订单状态快捷入口 -->
|
||||||
<view class="order-shortcuts">
|
<view class="order-shortcuts">
|
||||||
<view class="section-title">我的订单</view>
|
<view class="section-header-row">
|
||||||
|
<text class="section-title">我的订单</text>
|
||||||
|
<text class="view-all" @click="goToOrders(currentOrderTab)">查看更多 ></text>
|
||||||
|
</view>
|
||||||
<view class="order-tabs">
|
<view class="order-tabs">
|
||||||
<view class="order-tab" :class="{ active: currentOrderTab === 'all' }" @click="switchOrderTab('all')">
|
<view class="order-tab" :class="{ active: currentOrderTab === 'all' }" @click="switchOrderTab('all')">
|
||||||
<text class="tab-icon">📋</text>
|
<text class="tab-icon">📋</text>
|
||||||
@@ -117,7 +120,6 @@
|
|||||||
<view class="recent-orders">
|
<view class="recent-orders">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<text class="section-title">{{ getOrderSectionTitle() }}</text>
|
<text class="section-title">{{ getOrderSectionTitle() }}</text>
|
||||||
<text class="view-all" @click="goToOrders(currentOrderTab)">查看更多 ></text>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="filteredOrders.length === 0" class="empty-orders">
|
<view v-if="filteredOrders.length === 0" class="empty-orders">
|
||||||
@@ -126,22 +128,36 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-for="order in filteredOrders" :key="order.id" class="order-item" @click="viewOrderDetail(order)">
|
<view v-for="order in filteredOrders" :key="order.id" class="order-item" @click="viewOrderDetail(order)">
|
||||||
<view class="order-header">
|
<view class="order-item-header">
|
||||||
<text class="order-no">订单号: {{ order.order_no }}</text>
|
<view class="order-shop">
|
||||||
<text class="order-status" :class="getOrderStatusClass(order.status)">{{ getOrderStatusText(order.status) }}</text>
|
<text class="shop-icon">🏪</text>
|
||||||
</view>
|
<text class="shop-name">{{ getOrderShopName(order) }}</text>
|
||||||
<view class="order-content">
|
<text class="shop-arrow">›</text>
|
||||||
<image :src="getOrderMainImage(order)" class="order-image" mode="aspectFill" />
|
</view>
|
||||||
<view class="order-info">
|
<view class="status-row">
|
||||||
<text class="order-title">{{ getOrderTitle(order) }}</text>
|
<text class="order-status-text" :class="getOrderStatusClass(order.status)">{{ getOrderStatusText(order.status) }}</text>
|
||||||
<text class="order-amount">¥{{ order.actual_amount }}</text>
|
<text class="more-btn" @click.stop="showOrderMenu(order)">⋯</text>
|
||||||
<text class="order-time">{{ formatTime(order.created_at) }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="order-actions">
|
<view class="order-item-content">
|
||||||
<button v-if="order.status === 1" class="action-btn pay" @click.stop="payOrder(order)">立即支付</button>
|
<image :src="getOrderMainImage(order)" class="order-item-image" mode="aspectFill" />
|
||||||
<button v-if="order.status === 3" class="action-btn confirm" @click.stop="confirmReceive(order)">确认收货</button>
|
<view class="order-item-info">
|
||||||
<button v-if="order.status === 4" class="action-btn review" @click.stop="reviewOrder(order)">评价</button>
|
<view class="order-title-row">
|
||||||
|
<text class="order-item-title">{{ getOrderTitle(order) }}</text>
|
||||||
|
<text class="order-item-price">¥{{ order.actual_amount }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="order-spec-row">
|
||||||
|
<text class="order-item-spec">{{ getOrderSpec(order) }}</text>
|
||||||
|
<text class="order-item-num">x{{ getOrderItemCount(order) }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="order-item-time">{{ formatDateTime(order.created_at) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="order-item-actions">
|
||||||
|
<button v-if="order.status === 1" class="order-action-btn pay" @click.stop="payOrder(order)">立即支付</button>
|
||||||
|
<button v-if="order.status === 3" class="order-action-btn confirm" @click.stop="confirmReceive(order)">确认收货</button>
|
||||||
|
<button v-if="order.status === 4" class="order-action-btn review" @click.stop="reviewOrder(order)">评价</button>
|
||||||
|
<button v-if="order.status === 2 || order.status === 3" class="order-action-btn secondary" @click.stop="viewOrderDetail(order)">查看物流</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -290,6 +306,8 @@ type OrderItemType = {
|
|||||||
actual_amount: number
|
actual_amount: number
|
||||||
created_at: string
|
created_at: string
|
||||||
ml_order_items: any | null
|
ml_order_items: any | null
|
||||||
|
ml_shops: any | null
|
||||||
|
items_count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -407,13 +425,22 @@ export default {
|
|||||||
actualAmount = totalAmount != null ? totalAmount : 0
|
actualAmount = totalAmount != null ? totalAmount : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mlOrderItems = o.get('ml_order_items')
|
||||||
|
|
||||||
|
let itemsCount = 0
|
||||||
|
if (mlOrderItems != null && Array.isArray(mlOrderItems)) {
|
||||||
|
itemsCount = (mlOrderItems as any[]).length
|
||||||
|
}
|
||||||
|
|
||||||
const orderItem: OrderItemType = {
|
const orderItem: OrderItemType = {
|
||||||
id: o.getString('id') ?? '',
|
id: o.getString('id') ?? '',
|
||||||
order_no: o.getString('order_no') ?? '',
|
order_no: o.getString('order_no') ?? '',
|
||||||
status: status,
|
status: status,
|
||||||
actual_amount: actualAmount,
|
actual_amount: actualAmount,
|
||||||
created_at: o.getString('created_at') ?? '',
|
created_at: o.getString('created_at') ?? '',
|
||||||
ml_order_items: o.get('ml_order_items')
|
ml_order_items: mlOrderItems,
|
||||||
|
ml_shops: o.get('ml_shops'),
|
||||||
|
items_count: itemsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
mappedOrders.push(orderItem)
|
mappedOrders.push(orderItem)
|
||||||
@@ -629,6 +656,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getOrderStatusText(status: number): string {
|
getOrderStatusText(status: number): string {
|
||||||
|
if (status === 6) return '退款中'
|
||||||
|
if (status === 7) return '已退款'
|
||||||
const statusTexts = ['异常', '待支付', '待发货', '待收货', '已完成', '已取消']
|
const statusTexts = ['异常', '待支付', '待发货', '待收货', '已完成', '已取消']
|
||||||
if (status >= 0 && status < statusTexts.length) {
|
if (status >= 0 && status < statusTexts.length) {
|
||||||
return statusTexts[status]
|
return statusTexts[status]
|
||||||
@@ -637,6 +666,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getOrderStatusClass(status: number): string {
|
getOrderStatusClass(status: number): string {
|
||||||
|
if (status === 6) return 'refunding'
|
||||||
|
if (status === 7) return 'refunded'
|
||||||
const statusClasses = ['error', 'pending', 'processing', 'shipping', 'completed', 'cancelled']
|
const statusClasses = ['error', 'pending', 'processing', 'shipping', 'completed', 'cancelled']
|
||||||
if (status >= 0 && status < statusClasses.length) {
|
if (status >= 0 && status < statusClasses.length) {
|
||||||
return statusClasses[status]
|
return statusClasses[status]
|
||||||
@@ -644,6 +675,242 @@ export default {
|
|||||||
return 'error'
|
return 'error'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showOrderMenu(order: OrderItemType) {
|
||||||
|
const status = order.status
|
||||||
|
let actions: string[] = []
|
||||||
|
|
||||||
|
if (status === 1) {
|
||||||
|
actions = ['取消订单', '联系卖家']
|
||||||
|
} else if (status === 2) {
|
||||||
|
actions = ['提醒发货', '申请退款', '联系卖家']
|
||||||
|
} else if (status === 3) {
|
||||||
|
actions = ['查看物流', '确认收货', '申请退款', '联系卖家']
|
||||||
|
} else if (status === 4) {
|
||||||
|
actions = ['申请售后', '再次购买', '联系卖家']
|
||||||
|
} else if (status === 5) {
|
||||||
|
actions = ['删除订单', '再次购买', '联系卖家']
|
||||||
|
} else if (status === 6) {
|
||||||
|
actions = ['退款进度', '联系卖家']
|
||||||
|
} else if (status === 7) {
|
||||||
|
actions = ['再次购买', '联系卖家']
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: actions,
|
||||||
|
success: (res) => {
|
||||||
|
const action = actions[res.tapIndex]
|
||||||
|
this.handleOrderAction(order, action)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOrderAction(order: OrderItemType, action: string) {
|
||||||
|
if (action === '取消订单') {
|
||||||
|
this.cancelOrderAction(order)
|
||||||
|
} else if (action === '联系卖家') {
|
||||||
|
this.contactSeller(order)
|
||||||
|
} else if (action === '提醒发货') {
|
||||||
|
this.remindShipping(order)
|
||||||
|
} else if (action === '申请退款' || action === '申请售后') {
|
||||||
|
this.applyRefund(order)
|
||||||
|
} else if (action === '查看物流') {
|
||||||
|
this.viewLogistics(order.id)
|
||||||
|
} else if (action === '确认收货') {
|
||||||
|
this.confirmReceive(order)
|
||||||
|
} else if (action === '再次购买') {
|
||||||
|
this.repurchase(order)
|
||||||
|
} else if (action === '删除订单') {
|
||||||
|
this.deleteOrder(order.id)
|
||||||
|
} else if (action === '退款进度') {
|
||||||
|
this.viewRefundProgress(order.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelOrderAction(order: OrderItemType) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认取消',
|
||||||
|
content: '确定要取消此订单吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showLoading({ title: '取消中...' })
|
||||||
|
supabaseService.cancelOrder(order.id).then(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '订单已取消', icon: 'success' })
|
||||||
|
this.loadOrders()
|
||||||
|
}).catch(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '取消失败', icon: 'none' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
contactSeller(order: OrderItemType) {
|
||||||
|
const merchantId = order.ml_shops != null ? this.getMerchantIdFromOrder(order) : ''
|
||||||
|
if (merchantId !== '') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/mall/consumer/chat?merchantId=${merchantId}`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '暂无卖家联系方式', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getMerchantIdFromOrder(order: OrderItemType): string {
|
||||||
|
const shopsRaw = order.ml_shops
|
||||||
|
if (shopsRaw != null) {
|
||||||
|
const shopStr = JSON.stringify(shopsRaw)
|
||||||
|
const shopParsed = JSON.parse(shopStr)
|
||||||
|
if (shopParsed != null) {
|
||||||
|
const shopObj = shopParsed as UTSJSONObject
|
||||||
|
return shopObj.getString('merchant_id') ?? ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
|
||||||
|
remindShipping(order: OrderItemType) {
|
||||||
|
uni.showLoading({ title: '提醒中...' })
|
||||||
|
const merchantId = order.ml_shops != null ? this.getMerchantIdFromOrder(order) : ''
|
||||||
|
if (merchantId !== '') {
|
||||||
|
const message = `你好,我的订单[${order.order_no}]还没有发货,请尽快安排,谢谢。`
|
||||||
|
supabaseService.sendChatMessage(message, merchantId).then(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '已提醒卖家发货', icon: 'success' })
|
||||||
|
}).catch(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '提醒失败', icon: 'none' })
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '无法联系卖家', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
applyRefund(order: OrderItemType) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/mall/consumer/apply-refund?orderId=${order.id}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
viewLogistics(orderId: string) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/mall/consumer/logistics?orderId=${orderId}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
repurchase(order: OrderItemType) {
|
||||||
|
uni.showLoading({ title: '处理中...' })
|
||||||
|
const itemsRaw = order.ml_order_items
|
||||||
|
if (itemsRaw == null || (itemsRaw as any[]).length === 0) {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '订单无商品', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = itemsRaw as any[]
|
||||||
|
let completed = 0
|
||||||
|
const total = items.length
|
||||||
|
let successCount = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const itemStr = JSON.stringify(items[i])
|
||||||
|
const itemParsed = JSON.parse(itemStr)
|
||||||
|
if (itemParsed == null) {
|
||||||
|
completed++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (successCount > 0) {
|
||||||
|
uni.showToast({ title: `已添加${successCount}件商品`, icon: 'success' })
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '添加失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemObj = itemParsed as UTSJSONObject
|
||||||
|
const productId = itemObj.getString('product_id') ?? ''
|
||||||
|
const merchantId = order.ml_shops != null ? this.getMerchantIdFromOrder(order) : ''
|
||||||
|
|
||||||
|
if (productId !== '') {
|
||||||
|
supabaseService.addToCart(productId, 1, '', merchantId).then((success) => {
|
||||||
|
completed++
|
||||||
|
if (success) successCount++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (successCount > 0) {
|
||||||
|
uni.showToast({ title: `已添加${successCount}件商品`, icon: 'success' })
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '添加失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
completed++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (successCount > 0) {
|
||||||
|
uni.showToast({ title: `已添加${successCount}件商品`, icon: 'success' })
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '添加失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
completed++
|
||||||
|
if (completed === total) {
|
||||||
|
uni.hideLoading()
|
||||||
|
if (successCount > 0) {
|
||||||
|
uni.showToast({ title: `已添加${successCount}件商品`, icon: 'success' })
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '添加失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteOrder(orderId: string) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除订单',
|
||||||
|
content: '确定要删除此订单吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showLoading({ title: '删除中...' })
|
||||||
|
supabaseService.deleteOrder(orderId).then(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '订单已删除', icon: 'success' })
|
||||||
|
this.loadOrders()
|
||||||
|
}).catch(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '删除失败', icon: 'none' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
viewRefundProgress(orderId: string) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/mall/consumer/refund?orderId=${orderId}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getOrderShopName(order: OrderItemType): string {
|
||||||
|
const shopsRaw = order.ml_shops
|
||||||
|
if (shopsRaw != null) {
|
||||||
|
const shopStr = JSON.stringify(shopsRaw)
|
||||||
|
const shopParsed = JSON.parse(shopStr)
|
||||||
|
if (shopParsed != null) {
|
||||||
|
const shopObj = shopParsed as UTSJSONObject
|
||||||
|
const name = shopObj.getString('shop_name')
|
||||||
|
if (name != null && name !== '') return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '自营店铺'
|
||||||
|
},
|
||||||
|
|
||||||
getOrderMainImage(order: OrderItemType): string {
|
getOrderMainImage(order: OrderItemType): string {
|
||||||
const itemsRaw = order.ml_order_items
|
const itemsRaw = order.ml_order_items
|
||||||
if (itemsRaw == null) return '/static/product1.jpg'
|
if (itemsRaw == null) return '/static/product1.jpg'
|
||||||
@@ -675,14 +942,80 @@ export default {
|
|||||||
const pName = itemObj.getString('product_name')
|
const pName = itemObj.getString('product_name')
|
||||||
const name = (pName != null && pName !== '') ? pName : '商品'
|
const name = (pName != null && pName !== '') ? pName : '商品'
|
||||||
|
|
||||||
if (items.length > 1) {
|
|
||||||
return name + ' 等' + items.length + '件商品'
|
|
||||||
}
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
return '精选商品'
|
return '精选商品'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getOrderSpec(order: OrderItemType): string {
|
||||||
|
const itemsRaw = order.ml_order_items
|
||||||
|
if (itemsRaw == null) return ''
|
||||||
|
const items = itemsRaw as any[]
|
||||||
|
if (items.length > 0) {
|
||||||
|
const firstItem = items[0]
|
||||||
|
const itemStr = JSON.stringify(firstItem)
|
||||||
|
const itemParsed = JSON.parse(itemStr)
|
||||||
|
if (itemParsed == null) return ''
|
||||||
|
const itemObj = itemParsed as UTSJSONObject
|
||||||
|
const specRaw = itemObj.get('specifications')
|
||||||
|
if (specRaw == null) return ''
|
||||||
|
|
||||||
|
if (typeof specRaw === 'string') {
|
||||||
|
const specStr = specRaw as string
|
||||||
|
if (specStr.startsWith('{')) {
|
||||||
|
try {
|
||||||
|
const specObj = JSON.parse(specStr) as UTSJSONObject
|
||||||
|
const parts: string[] = []
|
||||||
|
const color = specObj.get('Color')
|
||||||
|
if (color != null) parts.push('颜色: ' + color)
|
||||||
|
const size = specObj.get('Size')
|
||||||
|
if (size != null) parts.push('尺码: ' + size)
|
||||||
|
|
||||||
|
if (parts.length > 0) return parts.join(' ')
|
||||||
|
return specStr.replace(/[{}"]/g, '')
|
||||||
|
} catch (e) {
|
||||||
|
return specStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return specStr
|
||||||
|
}
|
||||||
|
return JSON.stringify(specRaw).replace(/[{}"]/g, '')
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
|
||||||
|
getOrderItemCount(order: OrderItemType): number {
|
||||||
|
if (order.items_count != null && order.items_count > 0) {
|
||||||
|
return order.items_count
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
|
||||||
|
getOrderShopName(order: OrderItemType): string {
|
||||||
|
const shopsRaw = order.ml_shops
|
||||||
|
if (shopsRaw != null) {
|
||||||
|
const shopStr = JSON.stringify(shopsRaw)
|
||||||
|
const shopParsed = JSON.parse(shopStr)
|
||||||
|
if (shopParsed != null) {
|
||||||
|
const shopObj = shopParsed as UTSJSONObject
|
||||||
|
const name = shopObj.getString('shop_name')
|
||||||
|
if (name != null && name !== '') return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '自营店铺'
|
||||||
|
},
|
||||||
|
|
||||||
|
formatDateTime(timeStr: string): string {
|
||||||
|
if (timeStr == null || timeStr === '') return ''
|
||||||
|
const date = new Date(timeStr)
|
||||||
|
const y = date.getFullYear()
|
||||||
|
const m = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||||
|
const d = date.getDate().toString().padStart(2, '0')
|
||||||
|
const h = date.getHours().toString().padStart(2, '0')
|
||||||
|
const i = date.getMinutes().toString().padStart(2, '0')
|
||||||
|
return `${y}-${m}-${d} ${h}:${i}`
|
||||||
|
},
|
||||||
|
|
||||||
formatTime(timeStr: string): string {
|
formatTime(timeStr: string): string {
|
||||||
const date = new Date(timeStr)
|
const date = new Date(timeStr)
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
@@ -753,11 +1086,21 @@ export default {
|
|||||||
content: '确认已收到商品吗?',
|
content: '确认已收到商品吗?',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
uni.showToast({
|
uni.showLoading({ title: '处理中...' })
|
||||||
title: '确认收货成功',
|
supabaseService.confirmOrderReceived(order.id).then(() => {
|
||||||
icon: 'success'
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '确认收货成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
this.loadOrders()
|
||||||
|
}).catch(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
this.refreshData()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -891,9 +1234,9 @@ export default {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: #4CAF50;
|
background-color: #ff5000;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
|
box-shadow: 0 2px 12px rgba(255, 80, 0, 0.15);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@@ -912,23 +1255,28 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 导航栏用户信息区域 */
|
/* 导航栏用户信息区域 */
|
||||||
|
.nav-user-basic {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-user-stats {
|
.nav-user-stats {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start; /* 靠左对齐,紧跟头像 */
|
justify-content: flex-end; /* 将积分、余额、券推向右侧,但在设置按钮之前 */
|
||||||
margin-right: 12px;
|
margin-right: 8px;
|
||||||
overflow: hidden; /* 防止溢出 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-user-name {
|
.nav-user-name {
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: white;
|
color: white;
|
||||||
margin-right: 12px;
|
width: 70px; /* 进一步压缩名字宽度 */
|
||||||
/* max-width: 30%; REMOVED */
|
|
||||||
width: 100px; /* Use fixed width approx */
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -938,31 +1286,31 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.15); /* 降低透明度更清爽 */
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
padding: 2px 8px;
|
padding: 2px 6px;
|
||||||
margin-right: 8px;
|
margin-right: 4px; /* 减小间距 */
|
||||||
flex-shrink: 0; /* 防止被压缩 */
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-stat-label {
|
.nav-stat-label {
|
||||||
font-size: 11px;
|
font-size: 10px; /* 调小字体 */
|
||||||
color: rgba(255, 255, 255, 0.9);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
margin-right: 4px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-stat-value {
|
.nav-stat-value {
|
||||||
font-size: 12px;
|
font-size: 11px; /* 调小字体 */
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-avatar {
|
.nav-avatar {
|
||||||
width: 36px;
|
width: 32px; /* 缩小头像 */
|
||||||
height: 36px;
|
height: 32px;
|
||||||
border-radius: 18px;
|
border-radius: 16px;
|
||||||
border: 2px solid rgba(255, 255, 255, 0.8);
|
border: 1.5px solid rgba(255, 255, 255, 0.8);
|
||||||
margin-right: 12px;
|
margin-right: 6px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1010,6 +1358,23 @@ export default {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-header-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header-row .section-title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-all {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
.order-tabs {
|
.order-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 显式横向排列 */
|
flex-direction: row; /* 显式横向排列 */
|
||||||
@@ -1041,12 +1406,12 @@ export default {
|
|||||||
/* 选中状态的Tab */
|
/* 选中状态的Tab */
|
||||||
.order-tab.active .tab-icon,
|
.order-tab.active .tab-icon,
|
||||||
.order-tab.active .tab-text {
|
.order-tab.active .tab-text {
|
||||||
color: #4CAF50;
|
color: #ff5000;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-tab.active {
|
.order-tab.active {
|
||||||
background-color: #f0f9f0;
|
background-color: #fff8f5;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1054,7 +1419,7 @@ export default {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 10%; /* 调整位置 */
|
right: 10%; /* 调整位置 */
|
||||||
background-color: #ff4444;
|
background-color: #ff5000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
padding: 1px 5px;
|
padding: 1px 5px;
|
||||||
@@ -1085,114 +1450,200 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.order-item {
|
.order-item {
|
||||||
padding: 25rpx 0;
|
background-color: #fff;
|
||||||
border-bottom: 1rpx solid #f5f5f5;
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-item:last-child {
|
.order-item-header {
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 15rpx;
|
margin-bottom: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-no {
|
.status-row {
|
||||||
font-size: 26rpx;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-status {
|
|
||||||
font-size: 24rpx;
|
|
||||||
padding: 6rpx 12rpx;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-status.pending {
|
|
||||||
background-color: #ffa726;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-status.processing {
|
|
||||||
background-color: #2196f3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-status.shipping {
|
|
||||||
background-color: #9c27b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-status.completed {
|
|
||||||
background-color: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.order-content {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 15rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-image {
|
.more-btn {
|
||||||
width: 100rpx;
|
font-size: 18px;
|
||||||
height: 100rpx;
|
color: #999;
|
||||||
border-radius: 8rpx;
|
margin-left: 8px;
|
||||||
margin-right: 20rpx;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-info {
|
.order-shop {
|
||||||
flex: 1;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-title {
|
.shop-icon {
|
||||||
font-size: 26rpx;
|
font-size: 32rpx;
|
||||||
color: #333;
|
margin-right: 8rpx;
|
||||||
margin-bottom: 8rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-amount {
|
.shop-name {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #ff4444;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 5rpx;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-time {
|
.shop-arrow {
|
||||||
font-size: 22rpx;
|
font-size: 28rpx;
|
||||||
|
color: #ccc;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-text.pending {
|
||||||
|
color: #ff5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-text.processing {
|
||||||
|
color: #ff9500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-text.shipping {
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-text.completed {
|
||||||
|
color: #34c759;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-text.refunding {
|
||||||
|
color: #ff9500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status-text.refunded {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-actions {
|
.order-status-text.cancelled {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-item-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
flex-direction: row;
|
||||||
/* gap: 15rpx; REMOVED */
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-actions .action-btn {
|
.order-item-image {
|
||||||
margin-left: 15px; /* Replace gap */
|
width: 140rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
background-color: #f8f8f8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
.order-item-info {
|
||||||
padding: 12rpx 25rpx;
|
flex: 1;
|
||||||
border-radius: 20rpx;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-title-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-item-title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
lines: 2;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-item-price {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-spec-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-item-spec {
|
||||||
|
flex: 1;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
border: none;
|
color: #999;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
lines: 1;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn.pay {
|
.order-item-time {
|
||||||
background-color: #ff4444;
|
font-size: 22rpx;
|
||||||
color: #fff;
|
color: #999;
|
||||||
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn.confirm {
|
.order-item-num {
|
||||||
background-color: #4caf50;
|
font-size: 24rpx;
|
||||||
color: #fff;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn.review {
|
.order-item-actions {
|
||||||
background-color: #ffa726;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-action-btn {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
padding: 10rpx 28rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-action-btn.pay {
|
||||||
|
background-color: #ff5000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
border-color: #ff5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-action-btn.confirm {
|
||||||
|
background-color: #ff5000;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #ff5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-action-btn.review {
|
||||||
|
color: #ff5000;
|
||||||
|
border-color: #ff5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-action-btn.secondary {
|
||||||
|
color: #666;
|
||||||
|
border-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-grid {
|
.service-grid {
|
||||||
@@ -1227,7 +1678,7 @@ export default {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5rpx;
|
top: -5rpx;
|
||||||
right: 10rpx;
|
right: 10rpx;
|
||||||
background-color: #ff4444;
|
background-color: #ff5000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 18rpx;
|
font-size: 18rpx;
|
||||||
padding: 4rpx 6rpx;
|
padding: 4rpx 6rpx;
|
||||||
@@ -1332,3 +1783,4 @@ export default {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="red-packets-page">
|
<view class="red-packets-page">
|
||||||
<view class="tab-header" style="position: fixed; top: 0; left: 0; right: 0; z-index: 10;">
|
<view class="tab-header" style="position: fixed; top: 0; left: 0; right: 0; z-index: 10;">
|
||||||
<text
|
<text
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="review-page">
|
<view class="review-page">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<text class="title">服务评价</text>
|
<text class="title">服务评价</text>
|
||||||
@@ -160,3 +160,4 @@ const submitReview = () => {
|
|||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 退款页面 -->
|
<!-- 退款页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="refund-page">
|
<view class="refund-page">
|
||||||
<!-- 顶部栏 -->
|
<!-- 顶部栏 -->
|
||||||
@@ -663,7 +663,7 @@ const goBack = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status-pending {
|
.status-pending {
|
||||||
background-color: #ffa726;
|
background-color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-processing {
|
.status-processing {
|
||||||
@@ -834,8 +834,8 @@ const goBack = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.action-btn.review {
|
.action-btn.review {
|
||||||
border-color: #ffa726;
|
border-color: #ff5000;
|
||||||
color: #ffa726;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn.delete {
|
.action-btn.delete {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 评价页面 -->
|
<!-- 评价页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="review-page">
|
<view class="review-page">
|
||||||
<!-- 顶部栏 -->
|
<!-- 顶部栏 -->
|
||||||
@@ -658,7 +658,7 @@ const goBack = (): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.star-icon.active {
|
.star-icon.active {
|
||||||
color: #ffa726;
|
color: #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rating-text {
|
.rating-text {
|
||||||
@@ -850,3 +850,4 @@ const goBack = (): void => {
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="search-page">
|
<view class="search-page">
|
||||||
<!-- 搜索头部 -->
|
<!-- 搜索头部 -->
|
||||||
<view class="search-header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="search-header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
@@ -486,33 +486,23 @@ const performSearch = async (): Promise<void> => {
|
|||||||
try {
|
try {
|
||||||
const prodResp = await supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
|
const prodResp = await supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
|
||||||
|
|
||||||
let shopRespData: Array<any> = []
|
let shopList: Array<ShopResultType> = []
|
||||||
if (currentPage.value === 1 && activeSort.value === 'default') {
|
if (currentPage.value === 1 && activeSort.value === 'default') {
|
||||||
const shopResp = await supabaseService.searchShops(keyword)
|
const shopResp = await supabaseService.searchShops(keyword)
|
||||||
if (shopResp.data != null) {
|
if (shopResp.data != null && shopResp.data.length > 0) {
|
||||||
const rawData = shopResp.data
|
for (let i: number = 0; i < shopResp.data.length; i++) {
|
||||||
for (let i: number = 0; i < rawData.length; i++) {
|
const s = shopResp.data[i]
|
||||||
shopRespData.push(rawData[i])
|
const shopItem: ShopResultType = {
|
||||||
|
id: s.id ?? '',
|
||||||
|
name: s.shop_name ?? '',
|
||||||
|
logo: s.shop_logo ?? '/static/shop_logo_default.png',
|
||||||
|
productCount: s.product_count ?? 0
|
||||||
|
}
|
||||||
|
shopList.push(shopItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
searchShopResults.value = shopList
|
||||||
if (shopRespData.length > 0) {
|
|
||||||
const shopList: Array<ShopResultType> = []
|
|
||||||
for (let i: number = 0; i < shopRespData.length; i++) {
|
|
||||||
const s = shopRespData[i] as UTSJSONObject
|
|
||||||
const shopItem: ShopResultType = {
|
|
||||||
id: s.getString('id') ?? '',
|
|
||||||
name: s.getString('shop_name') ?? '',
|
|
||||||
logo: s.getString('shop_logo') ?? '/static/shop_logo_default.png',
|
|
||||||
productCount: s.getNumber('product_count') ?? 0
|
|
||||||
}
|
|
||||||
shopList.push(shopItem)
|
|
||||||
}
|
|
||||||
searchShopResults.value = shopList
|
|
||||||
} else {
|
|
||||||
searchShopResults.value = []
|
|
||||||
}
|
|
||||||
|
|
||||||
const prodData = prodResp.data != null ? prodResp.data : []
|
const prodData = prodResp.data != null ? prodResp.data : []
|
||||||
const resultList: Array<SearchResultType> = []
|
const resultList: Array<SearchResultType> = []
|
||||||
@@ -1344,7 +1334,7 @@ const goBack = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.guess-item {
|
.guess-item {
|
||||||
width: 24%; /* 猜你喜欢在平板上显示4列 */
|
width: 24%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1352,20 +1342,42 @@ const goBack = () => {
|
|||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
.results-list {
|
.results-list {
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-item {
|
.result-item {
|
||||||
width: 24%;
|
width: 19%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guess-item {
|
.guess-item {
|
||||||
width: 16%; /* 猜你喜欢在桌面上显示6列 */
|
width: 16%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 桌面端调整图片高度 */
|
|
||||||
.product-image {
|
.product-image {
|
||||||
height: 160px;
|
height: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.guess-grid {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大屏幕 (1440px以上) */
|
||||||
|
@media screen and (min-width: 1440px) {
|
||||||
|
.result-item {
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guess-item {
|
||||||
|
width: 12%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-item {
|
.result-item {
|
||||||
@@ -1518,3 +1530,4 @@ const goBack = () => {
|
|||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 设置页面 -->
|
<!-- 设置页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="settings-page">
|
<view class="settings-page">
|
||||||
<!-- 顶部栏 -->
|
<!-- 顶部栏 -->
|
||||||
@@ -825,3 +825,4 @@ const deleteAccount = () => {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}/* text-decoration: underline; REMOVED */
|
}/* text-decoration: underline; REMOVED */
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="shop-detail-page">
|
<view class="shop-detail-page">
|
||||||
<scroll-view class="page-scroll" scroll-y="true" @scrolltolower="onScrollToLower" refresher-enabled="true" @refresherrefresh="onRefresherRefresh" :refresher-triggered="isRefresherTriggered">
|
<scroll-view class="page-scroll" scroll-y="true" @scrolltolower="onScrollToLower" refresher-enabled="true" @refresherrefresh="onRefresherRefresh" :refresher-triggered="isRefresherTriggered">
|
||||||
<!-- 店铺头部信息 -->
|
<!-- 店铺头部信息 -->
|
||||||
@@ -601,8 +601,8 @@ const goToProduct = (id: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.follow-btn {
|
.follow-btn {
|
||||||
background-color: #ff4444;
|
background-color: #ff5000;
|
||||||
border: 1px solid #ff4444;
|
border: 1px solid #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.follow-btn .action-text {
|
.follow-btn .action-text {
|
||||||
@@ -658,7 +658,7 @@ const goToProduct = (id: string) => {
|
|||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
.coupon-amount {
|
.coupon-amount {
|
||||||
color: #ff4444;
|
color: #ff5000;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
@@ -671,7 +671,7 @@ const goToProduct = (id: string) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #ff4444;
|
background-color: #ff5000;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.coupon-btn-label {
|
.coupon-btn-label {
|
||||||
@@ -692,7 +692,7 @@ const goToProduct = (id: string) => {
|
|||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
border-left: 4px solid #ff4444;
|
border-left: 4px solid #ff5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-grid {
|
.product-grid {
|
||||||
@@ -782,3 +782,4 @@ const goToProduct = (id: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="followed-shops-page">
|
<view class="followed-shops-page">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<text class="header-title">我关注的店铺</text>
|
<text class="header-title">我关注的店铺</text>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="my-subs">
|
<view class="my-subs">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<text class="title">我的订阅</text>
|
<text class="title">我的订阅</text>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="plan-detail">
|
<view class="plan-detail">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<text class="title">订阅方案详情</text>
|
<text class="title">订阅方案详情</text>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="sub-plan-list">
|
<view class="sub-plan-list">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<text class="title">软件订阅</text>
|
<text class="title">软件订阅</text>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="subscribe-checkout">
|
<view class="subscribe-checkout">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<text class="title">确认订阅</text>
|
<text class="title">确认订阅</text>
|
||||||
|
|||||||
@@ -1,84 +1,84 @@
|
|||||||
<!-- 钱包页面 -->
|
<!-- 閽卞寘椤甸潰 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="wallet-page">
|
<view class="wallet-page">
|
||||||
<!-- 顶部栏 -->
|
<!-- 椤堕儴鏍?-->
|
||||||
<view class="wallet-header">
|
<view class="wallet-header">
|
||||||
<text class="back-btn" @click="goBack">‹</text>
|
<text class="back-btn" @click="goBack">鈥?/text>
|
||||||
<text class="header-title">我的钱包</text>
|
<text class="header-title">鎴戠殑閽卞寘</text>
|
||||||
<text class="more-btn" @click="showMoreActions">···</text>
|
<text class="more-btn" @click="showMoreActions">路路路</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view class="wallet-content" scroll-y>
|
<scroll-view class="wallet-content" scroll-y>
|
||||||
<!-- 余额概览 -->
|
<!-- 浣欓姒傝 -->
|
||||||
<view class="balance-overview">
|
<view class="balance-overview">
|
||||||
<text class="balance-label">账户余额</text>
|
<text class="balance-label">璐︽埛浣欓</text>
|
||||||
<text class="balance-value">¥{{ balance.toFixed(2) }}</text>
|
<text class="balance-value">楼{{ balance.toFixed(2) }}</text>
|
||||||
<view class="balance-actions">
|
<view class="balance-actions">
|
||||||
<button class="action-btn recharge" @click="recharge">充值</button>
|
<button class="action-btn recharge" @click="recharge">鍏呭€?/button>
|
||||||
<button class="action-btn withdraw" @click="withdraw">提现</button>
|
<button class="action-btn withdraw" @click="withdraw">鎻愮幇</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 资产统计 -->
|
<!-- 璧勪骇缁熻 -->
|
||||||
<view class="assets-stats">
|
<view class="assets-stats">
|
||||||
<view class="stat-item">
|
<view class="stat-item">
|
||||||
<text class="stat-label">累计充值</text>
|
<text class="stat-label">绱鍏呭€?/text>
|
||||||
<text class="stat-value">¥{{ stats.totalRecharge.toFixed(2) }}</text>
|
<text class="stat-value">楼{{ stats.totalRecharge.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item">
|
||||||
<text class="stat-label">累计消费</text>
|
<text class="stat-label">绱娑堣垂</text>
|
||||||
<text class="stat-value">¥{{ stats.totalConsume.toFixed(2) }}</text>
|
<text class="stat-value">楼{{ stats.totalConsume.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item">
|
||||||
<text class="stat-label">累计提现</text>
|
<text class="stat-label">绱鎻愮幇</text>
|
||||||
<text class="stat-value">¥{{ stats.totalWithdraw.toFixed(2) }}</text>
|
<text class="stat-value">楼{{ stats.totalWithdraw.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 快捷功能 -->
|
<!-- 蹇嵎鍔熻兘 -->
|
||||||
<view class="quick-actions">
|
<view class="quick-actions">
|
||||||
<view class="action-grid">
|
<view class="action-grid">
|
||||||
<view class="action-item" @click="goToCoupons">
|
<view class="action-item" @click="goToCoupons">
|
||||||
<text class="action-icon">🎫</text>
|
<text class="action-icon">馃帿</text>
|
||||||
<text class="action-text">优惠券</text>
|
<text class="action-text">浼樻儬鍒?/text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-item" @click="goToRedPackets">
|
<view class="action-item" @click="goToRedPackets">
|
||||||
<text class="action-icon">🧧</text>
|
<text class="action-icon">馃Ё</text>
|
||||||
<text class="action-text">红包</text>
|
<text class="action-text">绾㈠寘</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-item" @click="goToPoints">
|
<view class="action-item" @click="goToPoints">
|
||||||
<text class="action-icon">⭐</text>
|
<text class="action-icon">猸?/text>
|
||||||
<text class="action-text">积分</text>
|
<text class="action-text">绉垎</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-item" @click="goToBankCards">
|
<view class="action-item" @click="goToBankCards">
|
||||||
<text class="action-icon">💳</text>
|
<text class="action-icon">馃挸</text>
|
||||||
<text class="action-text">银行卡</text>
|
<text class="action-text">閾惰鍗?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 交易记录 -->
|
<!-- 浜ゆ槗璁板綍 -->
|
||||||
<view class="transactions-section">
|
<view class="transactions-section">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<text class="section-title">交易记录</text>
|
<text class="section-title">浜ゆ槗璁板綍</text>
|
||||||
<view class="filter-tabs">
|
<view class="filter-tabs">
|
||||||
<text :class="['filter-tab', { active: activeFilter === 'all' }]"
|
<text :class="['filter-tab', { active: activeFilter === 'all' }]"
|
||||||
@click="changeFilter('all')">全部</text>
|
@click="changeFilter('all')">鍏ㄩ儴</text>
|
||||||
<text :class="['filter-tab', { active: activeFilter === 'income' }]"
|
<text :class="['filter-tab', { active: activeFilter === 'income' }]"
|
||||||
@click="changeFilter('income')">收入</text>
|
@click="changeFilter('income')">鏀跺叆</text>
|
||||||
<text :class="['filter-tab', { active: activeFilter === 'expense' }]"
|
<text :class="['filter-tab', { active: activeFilter === 'expense' }]"
|
||||||
@click="changeFilter('expense')">支出</text>
|
@click="changeFilter('expense')">鏀嚭</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 绌虹姸鎬?-->
|
||||||
<view v-if="transactions.length === 0 && !isLoading" class="empty-transactions">
|
<view v-if="transactions.length === 0 && !isLoading" class="empty-transactions">
|
||||||
<text class="empty-icon">💰</text>
|
<text class="empty-icon">馃挵</text>
|
||||||
<text class="empty-text">暂无交易记录</text>
|
<text class="empty-text">鏆傛棤浜ゆ槗璁板綍</text>
|
||||||
<text class="empty-subtext">快去使用钱包功能吧</text>
|
<text class="empty-subtext">蹇幓浣跨敤閽卞寘鍔熻兘鍚?/text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 交易列表 -->
|
<!-- 浜ゆ槗鍒楄〃 -->
|
||||||
<view class="transactions-list">
|
<view class="transactions-list">
|
||||||
<view v-for="transaction in transactions"
|
<view v-for="transaction in transactions"
|
||||||
:key="transaction.id"
|
:key="transaction.id"
|
||||||
@@ -94,47 +94,47 @@
|
|||||||
<view class="transaction-right">
|
<view class="transaction-right">
|
||||||
<text :class="['transaction-amount',
|
<text :class="['transaction-amount',
|
||||||
{ income: transaction.amount > 0, expense: transaction.amount < 0 }]">
|
{ income: transaction.amount > 0, expense: transaction.amount < 0 }]">
|
||||||
{{ transaction.amount > 0 ? '+' : '' }}¥{{ Math.abs(transaction.amount).toFixed(2) }}
|
{{ transaction.amount > 0 ? '+' : '' }}楼{{ Math.abs(transaction.amount).toFixed(2) }}
|
||||||
</text>
|
</text>
|
||||||
<text class="transaction-balance">余额: ¥{{ transaction.current_balance.toFixed(2) }}</text>
|
<text class="transaction-balance">浣欓: 楼{{ transaction.current_balance.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载更多 -->
|
<!-- 鍔犺浇鏇村 -->
|
||||||
<view v-if="isLoading" class="loading-more">
|
<view v-if="isLoading" class="loading-more">
|
||||||
<text class="loading-text">加载中...</text>
|
<text class="loading-text">鍔犺浇涓?..</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="!hasMore && transactions.length > 0" class="no-more">
|
<view v-if="!hasMore && transactions.length > 0" class="no-more">
|
||||||
<text class="no-more-text">没有更多记录了</text>
|
<text class="no-more-text">娌℃湁鏇村璁板綍浜?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 安全提示 -->
|
<!-- 瀹夊叏鎻愮ず -->
|
||||||
<view class="security-tips">
|
<view class="security-tips">
|
||||||
<text class="tip-title">安全提示</text>
|
<text class="tip-title">瀹夊叏鎻愮ず</text>
|
||||||
<text class="tip-item">1. 请妥善保管您的支付密码</text>
|
<text class="tip-item">1. 璇峰Ε鍠勪繚绠℃偍鐨勬敮浠樺瘑鐮?/text>
|
||||||
<text class="tip-item">2. 不要向他人透露您的账户信息</text>
|
<text class="tip-item">2. 涓嶈鍚戜粬浜洪€忛湶鎮ㄧ殑璐︽埛淇℃伅</text>
|
||||||
<text class="tip-item">3. 定期修改密码以确保账户安全</text>
|
<text class="tip-item">3. 瀹氭湡淇敼瀵嗙爜浠ョ‘淇濊处鎴峰畨鍏?/text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 充值弹窗 -->
|
<!-- 鍏呭€煎脊绐?-->
|
||||||
<view v-if="showRechargePopup" class="recharge-popup">
|
<view v-if="showRechargePopup" class="recharge-popup">
|
||||||
<view class="popup-mask" @click="closeRechargePopup"></view>
|
<view class="popup-mask" @click="closeRechargePopup"></view>
|
||||||
<view class="popup-content">
|
<view class="popup-content">
|
||||||
<view class="popup-header">
|
<view class="popup-header">
|
||||||
<text class="popup-title">充值</text>
|
<text class="popup-title">鍏呭€?/text>
|
||||||
<text class="popup-close" @click="closeRechargePopup">×</text>
|
<text class="popup-close" @click="closeRechargePopup">脳</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="popup-body">
|
<view class="popup-body">
|
||||||
<text class="amount-label">充值金额</text>
|
<text class="amount-label">鍏呭€奸噾棰?/text>
|
||||||
<view class="amount-input">
|
<view class="amount-input">
|
||||||
<text class="currency-symbol">¥</text>
|
<text class="currency-symbol">楼</text>
|
||||||
<input class="amount-field"
|
<input class="amount-field"
|
||||||
v-model="rechargeAmount"
|
v-model="rechargeAmount"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="请输入充值金额"
|
placeholder="璇疯緭鍏ュ厖鍊奸噾棰?
|
||||||
focus />
|
focus />
|
||||||
</view>
|
</view>
|
||||||
<view class="quick-amounts">
|
<view class="quick-amounts">
|
||||||
@@ -142,17 +142,17 @@
|
|||||||
:key="amount"
|
:key="amount"
|
||||||
:class="['quick-amount', { active: rechargeAmount === amount.toString() }]"
|
:class="['quick-amount', { active: rechargeAmount === amount.toString() }]"
|
||||||
@click="selectQuickAmount(amount)">
|
@click="selectQuickAmount(amount)">
|
||||||
¥{{ amount }}
|
楼{{ amount }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="recharge-tip">单笔充值最低10元,最高5000元</text>
|
<text class="recharge-tip">鍗曠瑪鍏呭€兼渶浣?0鍏冿紝鏈€楂?000鍏?/text>
|
||||||
</view>
|
</view>
|
||||||
<view class="popup-footer">
|
<view class="popup-footer">
|
||||||
<button class="cancel-btn" @click="closeRechargePopup">取消</button>
|
<button class="cancel-btn" @click="closeRechargePopup">鍙栨秷</button>
|
||||||
<button class="confirm-btn"
|
<button class="confirm-btn"
|
||||||
:class="{ disabled: !canRecharge }"
|
:class="{ disabled: !canRecharge }"
|
||||||
@click="confirmRecharge">
|
@click="confirmRecharge">
|
||||||
确认充值
|
纭鍏呭€?
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -207,31 +207,31 @@ const showRechargePopup = ref<boolean>(false)
|
|||||||
const rechargeAmount = ref<string>('')
|
const rechargeAmount = ref<string>('')
|
||||||
const quickAmounts = [50, 100, 200, 500, 1000]
|
const quickAmounts = [50, 100, 200, 500, 1000]
|
||||||
|
|
||||||
// 计算属性
|
// 璁$畻灞炴€?
|
||||||
const canRecharge = computed(() => {
|
const canRecharge = computed(() => {
|
||||||
const amount = parseFloat(rechargeAmount.value)
|
const amount = parseFloat(rechargeAmount.value)
|
||||||
return !isNaN(amount) && amount >= 10 && amount <= 5000
|
return !isNaN(amount) && amount >= 10 && amount <= 5000
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听过滤器变化
|
// 鐩戝惉杩囨护鍣ㄥ彉鍖?
|
||||||
watch(activeFilter, () => {
|
watch(activeFilter, () => {
|
||||||
resetTransactions()
|
resetTransactions()
|
||||||
loadTransactions()
|
loadTransactions()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 生命周期
|
// 鐢熷懡鍛ㄦ湡
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadWalletData()
|
loadWalletData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 重置交易记录
|
// 閲嶇疆浜ゆ槗璁板綍
|
||||||
const resetTransactions = () => {
|
const resetTransactions = () => {
|
||||||
transactions.value = []
|
transactions.value = []
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
hasMore.value = true
|
hasMore.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载钱包数据
|
// 鍔犺浇閽卞寘鏁版嵁
|
||||||
const loadWalletData = async () => {
|
const loadWalletData = async () => {
|
||||||
const userId = getCurrentUserId()
|
const userId = getCurrentUserId()
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
@@ -247,7 +247,7 @@ const loadWalletData = async () => {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载余额信息
|
// 鍔犺浇浣欓淇℃伅
|
||||||
const loadBalance = async () => {
|
const loadBalance = async () => {
|
||||||
const userId = getCurrentUserId()
|
const userId = getCurrentUserId()
|
||||||
if (!userId) return
|
if (!userId) return
|
||||||
@@ -260,7 +260,7 @@ const loadBalance = async () => {
|
|||||||
.single()
|
.single()
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
console.error('加载钱包失败:', error)
|
console.error('鍔犺浇閽卞寘澶辫触:', error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,11 +273,11 @@ const loadBalance = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载钱包异常:', err)
|
console.error('鍔犺浇閽卞寘寮傚父:', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载交易记录
|
// 鍔犺浇浜ゆ槗璁板綍
|
||||||
const loadTransactions = async (loadMore: boolean = false) => {
|
const loadTransactions = async (loadMore: boolean = false) => {
|
||||||
if (isLoading.value || (!hasMore.value && loadMore)) {
|
if (isLoading.value || (!hasMore.value && loadMore)) {
|
||||||
return
|
return
|
||||||
@@ -297,20 +297,20 @@ const loadTransactions = async (loadMore: boolean = false) => {
|
|||||||
.eq('user_id', userId)
|
.eq('user_id', userId)
|
||||||
.order('created_at', { ascending: false })
|
.order('created_at', { ascending: false })
|
||||||
|
|
||||||
// 根据过滤器筛选
|
// 鏍规嵁杩囨护鍣ㄧ瓫閫?
|
||||||
if (activeFilter.value === 'income') {
|
if (activeFilter.value === 'income') {
|
||||||
query = query.gt('change_amount', 0)
|
query = query.gt('change_amount', 0)
|
||||||
} else if (activeFilter.value === 'expense') {
|
} else if (activeFilter.value === 'expense') {
|
||||||
query = query.lt('change_amount', 0)
|
query = query.lt('change_amount', 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页
|
// 鍒嗛〉
|
||||||
query = query.range((page - 1) * pageSize.value, page * pageSize.value - 1)
|
query = query.range((page - 1) * pageSize.value, page * pageSize.value - 1)
|
||||||
|
|
||||||
const { data, error } = await query
|
const { data, error } = await query
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
console.error('加载交易记录失败:', error)
|
console.error('鍔犺浇浜ゆ槗璁板綍澶辫触:', error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,47 +326,47 @@ const loadTransactions = async (loadMore: boolean = false) => {
|
|||||||
|
|
||||||
hasMore.value = newTransactions.length === pageSize.value
|
hasMore.value = newTransactions.length === pageSize.value
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载交易记录异常:', err)
|
console.error('鍔犺浇浜ゆ槗璁板綍寮傚父:', err)
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户ID
|
// 鑾峰彇褰撳墠鐢ㄦ埛ID
|
||||||
const getCurrentUserId = (): string => {
|
const getCurrentUserId = (): string => {
|
||||||
const userStore = uni.getStorageSync('userInfo')
|
const userStore = uni.getStorageSync('userInfo')
|
||||||
return userStore?.id || ''
|
return userStore?.id || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取交易图标
|
// 鑾峰彇浜ゆ槗鍥炬爣
|
||||||
const getTransactionIcon = (type: string): string => {
|
const getTransactionIcon = (type: string): string => {
|
||||||
const icons: Record<string, string> = {
|
const icons: Record<string, string> = {
|
||||||
recharge: '💳',
|
recharge: '馃挸',
|
||||||
consume: '🛒',
|
consume: '馃洅',
|
||||||
withdraw: '🏦',
|
withdraw: '馃彟',
|
||||||
refund: '🔄',
|
refund: '馃攧',
|
||||||
reward: '🎁',
|
reward: '馃巵',
|
||||||
income: '💰',
|
income: '馃挵',
|
||||||
expense: '📤'
|
expense: '馃摛'
|
||||||
}
|
}
|
||||||
return icons[type] || '💰'
|
return icons[type] || '馃挵'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取交易标题
|
// 鑾峰彇浜ゆ槗鏍囬
|
||||||
const getTransactionTitle = (type: string): string => {
|
const getTransactionTitle = (type: string): string => {
|
||||||
const titles: Record<string, string> = {
|
const titles: Record<string, string> = {
|
||||||
recharge: '账户充值',
|
recharge: '璐︽埛鍏呭€?,
|
||||||
consume: '商品消费',
|
consume: '鍟嗗搧娑堣垂',
|
||||||
withdraw: '余额提现',
|
withdraw: '浣欓鎻愮幇',
|
||||||
refund: '订单退款',
|
refund: '璁㈠崟閫€娆?,
|
||||||
reward: '活动奖励',
|
reward: '娲诲姩濂栧姳',
|
||||||
income: '收入',
|
income: '鏀跺叆',
|
||||||
expense: '支出'
|
expense: '鏀嚭'
|
||||||
}
|
}
|
||||||
return titles[type] || '交易'
|
return titles[type] || '浜ゆ槗'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化时间
|
// 鏍煎紡鍖栨椂闂?
|
||||||
const formatTime = (timeStr: string): string => {
|
const formatTime = (timeStr: string): string => {
|
||||||
const date = new Date(timeStr)
|
const date = new Date(timeStr)
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||||
@@ -376,14 +376,14 @@ const formatTime = (timeStr: string): string => {
|
|||||||
return `${month}-${day} ${hours}:${minutes}`
|
return `${month}-${day} ${hours}:${minutes}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示更多操作
|
// 鏄剧ず鏇村鎿嶄綔
|
||||||
const showMoreActions = () => {
|
const showMoreActions = () => {
|
||||||
uni.showActionSheet({
|
uni.showActionSheet({
|
||||||
itemList: ['交易记录', '安全设置', '帮助中心'],
|
itemList: ['浜ゆ槗璁板綍', '瀹夊叏璁剧疆', '甯姪涓績'],
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
switch (res.tapIndex) {
|
switch (res.tapIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
// 交易记录已经在当前页
|
// 浜ゆ槗璁板綍宸茬粡鍦ㄥ綋鍓嶉〉
|
||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
@@ -400,72 +400,72 @@ const showMoreActions = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 充值
|
// 鍏呭€?
|
||||||
const recharge = () => {
|
const recharge = () => {
|
||||||
showRechargePopup.value = true
|
showRechargePopup.value = true
|
||||||
rechargeAmount.value = ''
|
rechargeAmount.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提现
|
// 鎻愮幇
|
||||||
const withdraw = () => {
|
const withdraw = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/withdraw'
|
url: '/pages/mall/consumer/withdraw'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到优惠券
|
// 璺宠浆鍒颁紭鎯犲埜
|
||||||
const goToCoupons = () => {
|
const goToCoupons = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/coupons'
|
url: '/pages/mall/consumer/coupons'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到红包
|
// 璺宠浆鍒扮孩鍖?
|
||||||
const goToRedPackets = () => {
|
const goToRedPackets = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/red-packets'
|
url: '/pages/mall/consumer/red-packets'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到积分
|
// 璺宠浆鍒扮Н鍒?
|
||||||
const goToPoints = () => {
|
const goToPoints = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/points'
|
url: '/pages/mall/consumer/points'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到银行卡
|
// 璺宠浆鍒伴摱琛屽崱
|
||||||
const goToBankCards = () => {
|
const goToBankCards = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/bank-cards'
|
url: '/pages/mall/consumer/bank-cards'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换过滤器
|
// 鍒囨崲杩囨护鍣?
|
||||||
const changeFilter = (filter: string) => {
|
const changeFilter = (filter: string) => {
|
||||||
activeFilter.value = filter
|
activeFilter.value = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载更多
|
// 鍔犺浇鏇村
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
if (hasMore.value && !isLoading.value) {
|
if (hasMore.value && !isLoading.value) {
|
||||||
loadTransactions(true)
|
loadTransactions(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择快捷金额
|
// 閫夋嫨蹇嵎閲戦
|
||||||
const selectQuickAmount = (amount: number) => {
|
const selectQuickAmount = (amount: number) => {
|
||||||
rechargeAmount.value = amount.toString()
|
rechargeAmount.value = amount.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认充值
|
// 纭鍏呭€?
|
||||||
const confirmRecharge = async () => {
|
const confirmRecharge = async () => {
|
||||||
if (!canRecharge.value) return
|
if (!canRecharge.value) return
|
||||||
|
|
||||||
const amount = parseFloat(rechargeAmount.value)
|
const amount = parseFloat(rechargeAmount.value)
|
||||||
if (isNaN(amount)) return
|
if (isNaN(amount)) return
|
||||||
|
|
||||||
// 这里应该跳转到支付页面进行充值
|
// 杩欓噷搴旇璺宠浆鍒版敮浠橀〉闈㈣繘琛屽厖鍊?
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/mall/consumer/payment?type=recharge&amount=${amount}`
|
url: `/pages/mall/consumer/payment?type=recharge&amount=${amount}`
|
||||||
})
|
})
|
||||||
@@ -473,13 +473,13 @@ const confirmRecharge = async () => {
|
|||||||
closeRechargePopup()
|
closeRechargePopup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭充值弹窗
|
// 鍏抽棴鍏呭€煎脊绐?
|
||||||
const closeRechargePopup = () => {
|
const closeRechargePopup = () => {
|
||||||
showRechargePopup.value = false
|
showRechargePopup.value = false
|
||||||
rechargeAmount.value = ''
|
rechargeAmount.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回
|
// 杩斿洖
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
}
|
}
|
||||||
@@ -948,3 +948,4 @@ const goBack = () => {
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 钱包页面 -->
|
<!-- 钱包页面 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="wallet-page">
|
<view class="wallet-page">
|
||||||
<!-- 顶部栏 -->
|
<!-- 顶部栏 -->
|
||||||
|
|||||||
@@ -1,82 +1,82 @@
|
|||||||
<!-- 钱包页面 -->
|
<!-- 閽卞寘椤甸潰 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="wallet-page">
|
<view class="wallet-page">
|
||||||
<!-- 顶部栏 -->
|
<!-- 椤堕儴鏍?-->
|
||||||
<!--<view class="wallet-header">
|
<!--<view class="wallet-header">
|
||||||
<text class="back-btn" @click="goBack">‹</text>
|
<text class="back-btn" @click="goBack">鈥?/text>
|
||||||
</view>-->
|
</view>-->
|
||||||
|
|
||||||
<scroll-view class="wallet-content" scroll-y>
|
<scroll-view class="wallet-content" scroll-y>
|
||||||
<!-- 余额概览 -->
|
<!-- 浣欓姒傝 -->
|
||||||
<view class="balance-overview">
|
<view class="balance-overview">
|
||||||
<text class="balance-label">账户余额</text>
|
<text class="balance-label">璐︽埛浣欓</text>
|
||||||
<text class="balance-value">¥{{ balance.toFixed(2) }}</text>
|
<text class="balance-value">楼{{ balance.toFixed(2) }}</text>
|
||||||
<view class="balance-actions">
|
<view class="balance-actions">
|
||||||
<button class="action-btn recharge" @click="recharge">充值</button>
|
<button class="action-btn recharge" @click="recharge">鍏呭€?/button>
|
||||||
<button class="action-btn withdraw" @click="withdraw">提现</button>
|
<button class="action-btn withdraw" @click="withdraw">鎻愮幇</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 资产统计 -->
|
<!-- 璧勪骇缁熻 -->
|
||||||
<view class="assets-stats">
|
<view class="assets-stats">
|
||||||
<view class="stat-item">
|
<view class="stat-item">
|
||||||
<text class="stat-label">累计充值</text>
|
<text class="stat-label">绱鍏呭€?/text>
|
||||||
<text class="stat-value">¥{{ stats.totalRecharge.toFixed(2) }}</text>
|
<text class="stat-value">楼{{ stats.totalRecharge.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item">
|
||||||
<text class="stat-label">累计消费</text>
|
<text class="stat-label">绱娑堣垂</text>
|
||||||
<text class="stat-value">¥{{ stats.totalConsume.toFixed(2) }}</text>
|
<text class="stat-value">楼{{ stats.totalConsume.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item">
|
||||||
<text class="stat-label">累计提现</text>
|
<text class="stat-label">绱鎻愮幇</text>
|
||||||
<text class="stat-value">¥{{ stats.totalWithdraw.toFixed(2) }}</text>
|
<text class="stat-value">楼{{ stats.totalWithdraw.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 快捷功能 -->
|
<!-- 蹇嵎鍔熻兘 -->
|
||||||
<view class="quick-actions">
|
<view class="quick-actions">
|
||||||
<view class="action-grid">
|
<view class="action-grid">
|
||||||
<view class="action-item" @click="goToCoupons">
|
<view class="action-item" @click="goToCoupons">
|
||||||
<text class="action-icon">🎫</text>
|
<text class="action-icon">馃帿</text>
|
||||||
<text class="action-text">优惠券</text>
|
<text class="action-text">浼樻儬鍒?/text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-item" @click="goToRedPackets">
|
<view class="action-item" @click="goToRedPackets">
|
||||||
<text class="action-icon">🧧</text>
|
<text class="action-icon">馃Ё</text>
|
||||||
<text class="action-text">红包</text>
|
<text class="action-text">绾㈠寘</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-item" @click="goToPoints">
|
<view class="action-item" @click="goToPoints">
|
||||||
<text class="action-icon">⭐</text>
|
<text class="action-icon">猸?/text>
|
||||||
<text class="action-text">积分</text>
|
<text class="action-text">绉垎</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-item" @click="goToBankCards">
|
<view class="action-item" @click="goToBankCards">
|
||||||
<text class="action-icon">💳</text>
|
<text class="action-icon">馃挸</text>
|
||||||
<text class="action-text">银行卡</text>
|
<text class="action-text">閾惰鍗?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 交易记录 -->
|
<!-- 浜ゆ槗璁板綍 -->
|
||||||
<view class="transactions-section">
|
<view class="transactions-section">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<text class="section-title">交易记录</text>
|
<text class="section-title">浜ゆ槗璁板綍</text>
|
||||||
<view class="filter-tabs">
|
<view class="filter-tabs">
|
||||||
<text :class="['filter-tab', { active: activeFilter === 'all' }]"
|
<text :class="['filter-tab', { active: activeFilter === 'all' }]"
|
||||||
@click="changeFilter('all')">全部</text>
|
@click="changeFilter('all')">鍏ㄩ儴</text>
|
||||||
<text :class="['filter-tab', { active: activeFilter === 'income' }]"
|
<text :class="['filter-tab', { active: activeFilter === 'income' }]"
|
||||||
@click="changeFilter('income')">收入</text>
|
@click="changeFilter('income')">鏀跺叆</text>
|
||||||
<text :class="['filter-tab', { active: activeFilter === 'expense' }]"
|
<text :class="['filter-tab', { active: activeFilter === 'expense' }]"
|
||||||
@click="changeFilter('expense')">支出</text>
|
@click="changeFilter('expense')">鏀嚭</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 绌虹姸鎬?-->
|
||||||
<view v-if="transactions.length === 0 && !isLoading" class="empty-transactions">
|
<view v-if="transactions.length === 0 && !isLoading" class="empty-transactions">
|
||||||
<text class="empty-icon">💰</text>
|
<text class="empty-icon">馃挵</text>
|
||||||
<text class="empty-text">暂无交易记录</text>
|
<text class="empty-text">鏆傛棤浜ゆ槗璁板綍</text>
|
||||||
<text class="empty-subtext">快去使用钱包功能吧</text>
|
<text class="empty-subtext">蹇幓浣跨敤閽卞寘鍔熻兘鍚?/text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 交易列表 -->
|
<!-- 浜ゆ槗鍒楄〃 -->
|
||||||
<view class="transactions-list">
|
<view class="transactions-list">
|
||||||
<view v-for="transaction in transactions"
|
<view v-for="transaction in transactions"
|
||||||
:key="transaction.id"
|
:key="transaction.id"
|
||||||
@@ -92,47 +92,47 @@
|
|||||||
<view class="transaction-right">
|
<view class="transaction-right">
|
||||||
<text :class="['transaction-amount',
|
<text :class="['transaction-amount',
|
||||||
{ income: transaction.amount > 0, expense: transaction.amount < 0 }]">
|
{ income: transaction.amount > 0, expense: transaction.amount < 0 }]">
|
||||||
{{ transaction.amount > 0 ? '+' : '' }}¥{{ Math.abs(transaction.amount).toFixed(2) }}
|
{{ transaction.amount > 0 ? '+' : '' }}楼{{ Math.abs(transaction.amount).toFixed(2) }}
|
||||||
</text>
|
</text>
|
||||||
<text class="transaction-balance">余额: ¥{{ transaction.current_balance.toFixed(2) }}</text>
|
<text class="transaction-balance">浣欓: 楼{{ transaction.current_balance.toFixed(2) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载更多 -->
|
<!-- 鍔犺浇鏇村 -->
|
||||||
<view v-if="isLoading" class="loading-more">
|
<view v-if="isLoading" class="loading-more">
|
||||||
<text class="loading-text">加载中...</text>
|
<text class="loading-text">鍔犺浇涓?..</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="!hasMore && transactions.length > 0" class="no-more">
|
<view v-if="!hasMore && transactions.length > 0" class="no-more">
|
||||||
<text class="no-more-text">没有更多记录了</text>
|
<text class="no-more-text">娌℃湁鏇村璁板綍浜?/text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 安全提示 -->
|
<!-- 瀹夊叏鎻愮ず -->
|
||||||
<view class="security-tips">
|
<view class="security-tips">
|
||||||
<text class="tip-title">安全提示</text>
|
<text class="tip-title">瀹夊叏鎻愮ず</text>
|
||||||
<text class="tip-item">1. 请妥善保管您的支付密码</text>
|
<text class="tip-item">1. 璇峰Ε鍠勪繚绠℃偍鐨勬敮浠樺瘑鐮?/text>
|
||||||
<text class="tip-item">2. 不要向他人透露您的账户信息</text>
|
<text class="tip-item">2. 涓嶈鍚戜粬浜洪€忛湶鎮ㄧ殑璐︽埛淇℃伅</text>
|
||||||
<text class="tip-item">3. 定期修改密码以确保账户安全</text>
|
<text class="tip-item">3. 瀹氭湡淇敼瀵嗙爜浠ョ‘淇濊处鎴峰畨鍏?/text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 充值弹窗 -->
|
<!-- 鍏呭€煎脊绐?-->
|
||||||
<view v-if="showRechargePopup" class="recharge-popup">
|
<view v-if="showRechargePopup" class="recharge-popup">
|
||||||
<view class="popup-mask" @click="closeRechargePopup"></view>
|
<view class="popup-mask" @click="closeRechargePopup"></view>
|
||||||
<view class="popup-content">
|
<view class="popup-content">
|
||||||
<view class="popup-header">
|
<view class="popup-header">
|
||||||
<text class="popup-title">充值</text>
|
<text class="popup-title">鍏呭€?/text>
|
||||||
<text class="popup-close" @click="closeRechargePopup">×</text>
|
<text class="popup-close" @click="closeRechargePopup">脳</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="popup-body">
|
<view class="popup-body">
|
||||||
<text class="amount-label">充值金额</text>
|
<text class="amount-label">鍏呭€奸噾棰?/text>
|
||||||
<view class="amount-input">
|
<view class="amount-input">
|
||||||
<text class="currency-symbol">¥</text>
|
<text class="currency-symbol">楼</text>
|
||||||
<input class="amount-field"
|
<input class="amount-field"
|
||||||
v-model="rechargeAmount"
|
v-model="rechargeAmount"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="请输入充值金额"
|
placeholder="璇疯緭鍏ュ厖鍊奸噾棰?
|
||||||
focus />
|
focus />
|
||||||
</view>
|
</view>
|
||||||
<view class="quick-amounts">
|
<view class="quick-amounts">
|
||||||
@@ -140,17 +140,17 @@
|
|||||||
:key="amount"
|
:key="amount"
|
||||||
:class="['quick-amount', { active: rechargeAmount === amount.toString() }]"
|
:class="['quick-amount', { active: rechargeAmount === amount.toString() }]"
|
||||||
@click="selectQuickAmount(amount)">
|
@click="selectQuickAmount(amount)">
|
||||||
¥{{ amount }}
|
楼{{ amount }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="recharge-tip">单笔充值最低10元,最高5000元</text>
|
<text class="recharge-tip">鍗曠瑪鍏呭€兼渶浣?0鍏冿紝鏈€楂?000鍏?/text>
|
||||||
</view>
|
</view>
|
||||||
<view class="popup-footer">
|
<view class="popup-footer">
|
||||||
<button class="cancel-btn" @click="closeRechargePopup">取消</button>
|
<button class="cancel-btn" @click="closeRechargePopup">鍙栨秷</button>
|
||||||
<button class="confirm-btn"
|
<button class="confirm-btn"
|
||||||
:class="{ disabled: !canRecharge }"
|
:class="{ disabled: !canRecharge }"
|
||||||
@click="confirmRecharge">
|
@click="confirmRecharge">
|
||||||
确认充值
|
纭鍏呭€?
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -205,31 +205,31 @@ const showRechargePopup = ref<boolean>(false)
|
|||||||
const rechargeAmount = ref<string>('')
|
const rechargeAmount = ref<string>('')
|
||||||
const quickAmounts = [50, 100, 200, 500, 1000]
|
const quickAmounts = [50, 100, 200, 500, 1000]
|
||||||
|
|
||||||
// 计算属性
|
// 璁$畻灞炴€?
|
||||||
const canRecharge = computed(() => {
|
const canRecharge = computed(() => {
|
||||||
const amount = parseFloat(rechargeAmount.value)
|
const amount = parseFloat(rechargeAmount.value)
|
||||||
return !isNaN(amount) && amount >= 10 && amount <= 5000
|
return !isNaN(amount) && amount >= 10 && amount <= 5000
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听过滤器变化
|
// 鐩戝惉杩囨护鍣ㄥ彉鍖?
|
||||||
watch(activeFilter, () => {
|
watch(activeFilter, () => {
|
||||||
resetTransactions()
|
resetTransactions()
|
||||||
loadTransactions()
|
loadTransactions()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 生命周期
|
// 鐢熷懡鍛ㄦ湡
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadWalletData()
|
loadWalletData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 重置交易记录
|
// 閲嶇疆浜ゆ槗璁板綍
|
||||||
const resetTransactions = () => {
|
const resetTransactions = () => {
|
||||||
transactions.value = []
|
transactions.value = []
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
hasMore.value = true
|
hasMore.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载钱包数据
|
// 鍔犺浇閽卞寘鏁版嵁
|
||||||
const loadWalletData = async () => {
|
const loadWalletData = async () => {
|
||||||
const userId = getCurrentUserId()
|
const userId = getCurrentUserId()
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
@@ -245,7 +245,7 @@ const loadWalletData = async () => {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载余额信息
|
// 鍔犺浇浣欓淇℃伅
|
||||||
const loadBalance = async () => {
|
const loadBalance = async () => {
|
||||||
const userId = getCurrentUserId()
|
const userId = getCurrentUserId()
|
||||||
if (!userId) return
|
if (!userId) return
|
||||||
@@ -258,7 +258,7 @@ const loadBalance = async () => {
|
|||||||
.single()
|
.single()
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
console.error('加载钱包失败:', error)
|
console.error('鍔犺浇閽卞寘澶辫触:', error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,11 +271,11 @@ const loadBalance = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载钱包异常:', err)
|
console.error('鍔犺浇閽卞寘寮傚父:', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载交易记录
|
// 鍔犺浇浜ゆ槗璁板綍
|
||||||
const loadTransactions = async (loadMore: boolean = false) => {
|
const loadTransactions = async (loadMore: boolean = false) => {
|
||||||
if (isLoading.value || (!hasMore.value && loadMore)) {
|
if (isLoading.value || (!hasMore.value && loadMore)) {
|
||||||
return
|
return
|
||||||
@@ -295,20 +295,20 @@ const loadTransactions = async (loadMore: boolean = false) => {
|
|||||||
.eq('user_id', userId)
|
.eq('user_id', userId)
|
||||||
.order('created_at', { ascending: false })
|
.order('created_at', { ascending: false })
|
||||||
|
|
||||||
// 根据过滤器筛选
|
// 鏍规嵁杩囨护鍣ㄧ瓫閫?
|
||||||
if (activeFilter.value === 'income') {
|
if (activeFilter.value === 'income') {
|
||||||
query = query.gt('change_amount', 0)
|
query = query.gt('change_amount', 0)
|
||||||
} else if (activeFilter.value === 'expense') {
|
} else if (activeFilter.value === 'expense') {
|
||||||
query = query.lt('change_amount', 0)
|
query = query.lt('change_amount', 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页
|
// 鍒嗛〉
|
||||||
query = query.range((page - 1) * pageSize.value, page * pageSize.value - 1)
|
query = query.range((page - 1) * pageSize.value, page * pageSize.value - 1)
|
||||||
|
|
||||||
const { data, error } = await query
|
const { data, error } = await query
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
console.error('加载交易记录失败:', error)
|
console.error('鍔犺浇浜ゆ槗璁板綍澶辫触:', error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,47 +324,47 @@ const loadTransactions = async (loadMore: boolean = false) => {
|
|||||||
|
|
||||||
hasMore.value = newTransactions.length === pageSize.value
|
hasMore.value = newTransactions.length === pageSize.value
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载交易记录异常:', err)
|
console.error('鍔犺浇浜ゆ槗璁板綍寮傚父:', err)
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户ID
|
// 鑾峰彇褰撳墠鐢ㄦ埛ID
|
||||||
const getCurrentUserId = (): string => {
|
const getCurrentUserId = (): string => {
|
||||||
const userStore = uni.getStorageSync('userInfo')
|
const userStore = uni.getStorageSync('userInfo')
|
||||||
return userStore?.id || ''
|
return userStore?.id || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取交易图标
|
// 鑾峰彇浜ゆ槗鍥炬爣
|
||||||
const getTransactionIcon = (type: string): string => {
|
const getTransactionIcon = (type: string): string => {
|
||||||
const icons: Record<string, string> = {
|
const icons: Record<string, string> = {
|
||||||
recharge: '💳',
|
recharge: '馃挸',
|
||||||
consume: '🛒',
|
consume: '馃洅',
|
||||||
withdraw: '🏦',
|
withdraw: '馃彟',
|
||||||
refund: '🔄',
|
refund: '馃攧',
|
||||||
reward: '🎁',
|
reward: '馃巵',
|
||||||
income: '💰',
|
income: '馃挵',
|
||||||
expense: '📤'
|
expense: '馃摛'
|
||||||
}
|
}
|
||||||
return icons[type] || '💰'
|
return icons[type] || '馃挵'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取交易标题
|
// 鑾峰彇浜ゆ槗鏍囬
|
||||||
const getTransactionTitle = (type: string): string => {
|
const getTransactionTitle = (type: string): string => {
|
||||||
const titles: Record<string, string> = {
|
const titles: Record<string, string> = {
|
||||||
recharge: '账户充值',
|
recharge: '璐︽埛鍏呭€?,
|
||||||
consume: '商品消费',
|
consume: '鍟嗗搧娑堣垂',
|
||||||
withdraw: '余额提现',
|
withdraw: '浣欓鎻愮幇',
|
||||||
refund: '订单退款',
|
refund: '璁㈠崟閫€娆?,
|
||||||
reward: '活动奖励',
|
reward: '娲诲姩濂栧姳',
|
||||||
income: '收入',
|
income: '鏀跺叆',
|
||||||
expense: '支出'
|
expense: '鏀嚭'
|
||||||
}
|
}
|
||||||
return titles[type] || '交易'
|
return titles[type] || '浜ゆ槗'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化时间
|
// 鏍煎紡鍖栨椂闂?
|
||||||
const formatTime = (timeStr: string): string => {
|
const formatTime = (timeStr: string): string => {
|
||||||
const date = new Date(timeStr)
|
const date = new Date(timeStr)
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||||
@@ -374,14 +374,14 @@ const formatTime = (timeStr: string): string => {
|
|||||||
return `${month}-${day} ${hours}:${minutes}`
|
return `${month}-${day} ${hours}:${minutes}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示更多操作
|
// 鏄剧ず鏇村鎿嶄綔
|
||||||
const showMoreActions = () => {
|
const showMoreActions = () => {
|
||||||
uni.showActionSheet({
|
uni.showActionSheet({
|
||||||
itemList: ['交易记录', '安全设置', '帮助中心'],
|
itemList: ['浜ゆ槗璁板綍', '瀹夊叏璁剧疆', '甯姪涓績'],
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
switch (res.tapIndex) {
|
switch (res.tapIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
// 交易记录已经在当前页
|
// 浜ゆ槗璁板綍宸茬粡鍦ㄥ綋鍓嶉〉
|
||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
@@ -398,72 +398,72 @@ const showMoreActions = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 充值
|
// 鍏呭€?
|
||||||
const recharge = () => {
|
const recharge = () => {
|
||||||
showRechargePopup.value = true
|
showRechargePopup.value = true
|
||||||
rechargeAmount.value = ''
|
rechargeAmount.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提现
|
// 鎻愮幇
|
||||||
const withdraw = () => {
|
const withdraw = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/withdraw'
|
url: '/pages/mall/consumer/withdraw'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到优惠券
|
// 璺宠浆鍒颁紭鎯犲埜
|
||||||
const goToCoupons = () => {
|
const goToCoupons = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/coupons'
|
url: '/pages/mall/consumer/coupons'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到红包
|
// 璺宠浆鍒扮孩鍖?
|
||||||
const goToRedPackets = () => {
|
const goToRedPackets = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/red-packets'
|
url: '/pages/mall/consumer/red-packets'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到积分
|
// 璺宠浆鍒扮Н鍒?
|
||||||
const goToPoints = () => {
|
const goToPoints = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/points'
|
url: '/pages/mall/consumer/points'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转到银行卡
|
// 璺宠浆鍒伴摱琛屽崱
|
||||||
const goToBankCards = () => {
|
const goToBankCards = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/mall/consumer/bank-cards'
|
url: '/pages/mall/consumer/bank-cards'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换过滤器
|
// 鍒囨崲杩囨护鍣?
|
||||||
const changeFilter = (filter: string) => {
|
const changeFilter = (filter: string) => {
|
||||||
activeFilter.value = filter
|
activeFilter.value = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载更多
|
// 鍔犺浇鏇村
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
if (hasMore.value && !isLoading.value) {
|
if (hasMore.value && !isLoading.value) {
|
||||||
loadTransactions(true)
|
loadTransactions(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择快捷金额
|
// 閫夋嫨蹇嵎閲戦
|
||||||
const selectQuickAmount = (amount: number) => {
|
const selectQuickAmount = (amount: number) => {
|
||||||
rechargeAmount.value = amount.toString()
|
rechargeAmount.value = amount.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认充值
|
// 纭鍏呭€?
|
||||||
const confirmRecharge = async () => {
|
const confirmRecharge = async () => {
|
||||||
if (!canRecharge.value) return
|
if (!canRecharge.value) return
|
||||||
|
|
||||||
const amount = parseFloat(rechargeAmount.value)
|
const amount = parseFloat(rechargeAmount.value)
|
||||||
if (isNaN(amount)) return
|
if (isNaN(amount)) return
|
||||||
|
|
||||||
// 这里应该跳转到支付页面进行充值
|
// 杩欓噷搴旇璺宠浆鍒版敮浠橀〉闈㈣繘琛屽厖鍊?
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/mall/consumer/payment?type=recharge&amount=${amount}`
|
url: `/pages/mall/consumer/payment?type=recharge&amount=${amount}`
|
||||||
})
|
})
|
||||||
@@ -471,20 +471,20 @@ const confirmRecharge = async () => {
|
|||||||
closeRechargePopup()
|
closeRechargePopup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭充值弹窗
|
// 鍏抽棴鍏呭€煎脊绐?
|
||||||
const closeRechargePopup = () => {
|
const closeRechargePopup = () => {
|
||||||
showRechargePopup.value = false
|
showRechargePopup.value = false
|
||||||
rechargeAmount.value = ''
|
rechargeAmount.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回
|
// 杩斿洖
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 响应式布局优化 */
|
/* 鍝嶅簲寮忓竷灞€浼樺寲 */
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.wallet-content {
|
.wallet-content {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -518,11 +518,11 @@ const goBack = () => {
|
|||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
.wallet-page {
|
.wallet-page {
|
||||||
flex-direction: row; /* 大屏下改为横向布局 */
|
flex-direction: row; /* 澶у睆涓嬫敼涓烘í鍚戝竷灞€ */
|
||||||
}
|
}
|
||||||
|
|
||||||
.wallet-header {
|
.wallet-header {
|
||||||
display: none; /* 大屏下隐藏顶部栏 */
|
display: none; /* 澶у睆涓嬮殣钘忛《閮ㄦ爮 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.wallet-content {
|
.wallet-content {
|
||||||
@@ -982,3 +982,4 @@ const goBack = () => {
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page-container">
|
<view class="page-container">
|
||||||
<view class="card">
|
<view class="card">
|
||||||
<view class="section-title">提现至</view>
|
<view class="section-title">提现至</view>
|
||||||
|
|||||||
@@ -75,7 +75,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref, onShow } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||||
|
|
||||||
type ProfileType = {
|
type ProfileType = {
|
||||||
|
|||||||
@@ -160,6 +160,10 @@ const account = ref<string>('')
|
|||||||
const password = ref<string>('')
|
const password = ref<string>('')
|
||||||
const captcha = ref<string>('')
|
const captcha = ref<string>('')
|
||||||
|
|
||||||
|
// 测试账号(开发测试用)
|
||||||
|
const TEST_ACCOUNT = 'test@mall.com'
|
||||||
|
const TEST_PASSWORD = 'Hf2152111'
|
||||||
|
|
||||||
const isLoading = ref<boolean>(false)
|
const isLoading = ref<boolean>(false)
|
||||||
|
|
||||||
const codeDisabled = ref<boolean>(false)
|
const codeDisabled = ref<boolean>(false)
|
||||||
@@ -193,6 +197,9 @@ const checkLoginStatus = (): void => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkLoginStatus()
|
checkLoginStatus()
|
||||||
|
// 自动填充测试账号密码
|
||||||
|
account.value = TEST_ACCOUNT
|
||||||
|
password.value = TEST_PASSWORD
|
||||||
})
|
})
|
||||||
|
|
||||||
const validateAccount = (): boolean => {
|
const validateAccount = (): boolean => {
|
||||||
|
|||||||
@@ -351,7 +351,8 @@ const onWeightInput = (e: UniInputEvent): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const showGenderPickerNow = (): void => {
|
const showGenderPickerNow = (): void => {
|
||||||
const idx = genderOptions.indexOf(profile.value.gender)
|
const genderValue = profile.value.gender
|
||||||
|
const idx = genderValue != null ? genderOptions.indexOf(genderValue) : -1
|
||||||
tempGenderIndex.value = [idx >= 0 ? idx : 0]
|
tempGenderIndex.value = [idx >= 0 ? idx : 0]
|
||||||
showGenderPicker.value = true
|
showGenderPicker.value = true
|
||||||
}
|
}
|
||||||
@@ -401,10 +402,17 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
flex: 1;
|
/* 使用 100% 替代 100vh 以增强安卓兼容性 */
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-section {
|
.top-section {
|
||||||
|
flex-shrink: 0;
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
@@ -412,14 +420,15 @@ onMounted(() => {
|
|||||||
|
|
||||||
.main-section {
|
.main-section {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
/* 显式声明高度为 0 是 flex 容器内 scroll-view 生效的关键 */
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-container {
|
.profile-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
static/icons/cart.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
static/icons/customer-service.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
static/icons/favorite-active.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
static/icons/favorite.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
static/tabbar/camera.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
static/tabbar/cart.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
static/tabbar/category.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
static/tabbar/home-active.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
static/tabbar/home.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
static/tabbar/hot.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
static/tabbar/message.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
static/tabbar/scan.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/tabbar/user.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
@@ -369,70 +369,6 @@ export class AkReq {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增:支持类型转换的请求方法
|
|
||||||
static async requestAs<T = any>(options : AkReqOptions, skipRefresh ?: boolean) : Promise<AkReqResponse<T|Array<T>>> {
|
|
||||||
const response = await this.request(options, skipRefresh);
|
|
||||||
|
|
||||||
// 如果原始 data 是 null,直接返回 null
|
|
||||||
// if (response.data == null) {
|
|
||||||
// return {
|
|
||||||
// status: response.status,
|
|
||||||
// data: null,
|
|
||||||
// headers: response.headers,
|
|
||||||
// error: response.error,
|
|
||||||
// total: response.total,
|
|
||||||
// page: response.page,
|
|
||||||
// limit: response.limit,
|
|
||||||
// hasmore: response.hasmore,
|
|
||||||
// origin: response.origin
|
|
||||||
// } as AkReqResponse<T|Array<T>>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 尝试类型转换
|
|
||||||
let convertedData: T | null = null;
|
|
||||||
try {
|
|
||||||
// #ifdef APP-ANDROID
|
|
||||||
if (response.data instanceof UTSJSONObject) {
|
|
||||||
convertedData = response.data.parse<T>();
|
|
||||||
} else if (Array.isArray(response.data)) {
|
|
||||||
const convertedArray: Array<any> = [];
|
|
||||||
const dataArray = response.data;
|
|
||||||
for (let i = 0; i < dataArray.length; i++) {
|
|
||||||
const item = dataArray[i];
|
|
||||||
if (item instanceof UTSJSONObject) {
|
|
||||||
const parsed = item.parse<T>();
|
|
||||||
if (parsed != null) {
|
|
||||||
convertedArray.push(parsed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
convertedArray.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
convertedData = convertedArray as T;
|
|
||||||
}
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifndef APP-ANDROID
|
|
||||||
convertedData = response.data as T;
|
|
||||||
// #endif
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('类型转换失败,使用原始 UTSJSONObject:', e);
|
|
||||||
// 转换失败时,返回原始 UTSJSONObject
|
|
||||||
convertedData = response.data as T;
|
|
||||||
}
|
|
||||||
const aaa = {
|
|
||||||
status: response.status,
|
|
||||||
data: convertedData!!,
|
|
||||||
headers: response.headers,
|
|
||||||
error: response.error,
|
|
||||||
total: response.total,
|
|
||||||
page: response.page,
|
|
||||||
limit: response.limit,
|
|
||||||
hasmore: response.hasmore,
|
|
||||||
origin: response.origin
|
|
||||||
} ;
|
|
||||||
return aaa
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AkReq;
|
export default AkReq;
|
||||||