consumer模块完成度95%,能编译在安卓端运行,在解决数据获取和页面布局问题

This commit is contained in:
cyh666666
2026-02-27 08:20:43 +08:00
parent e606c597ca
commit b9acce6c35
1554 changed files with 23471 additions and 8551 deletions

View File

@@ -7,11 +7,11 @@
<text class="header-title">评价商品</text>
</view>
<scroll-view class="review-content" scroll-y>
<scroll-view class="review-content" direction="vertical">
<!-- 订单信息 -->
<view class="order-section">
<text class="order-no">订单号: {{ order?.order_no }}</text>
<text class="order-time">下单时间: {{ formatTime(order?.created_at) }}</text>
<text class="order-no">订单号: {{ order != null ? order.order_no : '' }}</text>
<text class="order-time">下单时间: {{ formatTime(order != null ? order.created_at : '') }}</text>
</view>
<!-- 商品评价 -->
@@ -21,7 +21,7 @@
<image class="product-image" :src="item.product_image ?? '/static/default-product.png'" />
<view class="product-info">
<text class="product-name">{{ item.product_name }}</text>
<text v-if="item.sku_specifications" class="product-spec">{{ getSpecText(item.sku_specifications) }}</text>
<text v-if="item.sku_specifications != null" class="product-spec">{{ getSpecText(item.sku_specifications) }}</text>
</view>
</view>
@@ -133,9 +133,9 @@
<!-- 提交按钮 -->
<view class="submit-section">
<button class="submit-btn"
:class="{ disabled: !canSubmit || isSubmitting }"
:class="{ disabled: canSubmit === false || isSubmitting }"
@click="submitReview">
<text v-if="!isSubmitting" class="submit-text">提交评价</text>
<text v-if="isSubmitting === false" class="submit-text">提交评价</text>
<text v-else class="submit-text">提交中...</text>
</button>
</view>
@@ -146,17 +146,32 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import supa from '@/components/supadb/aksupainstance.uts'
import { supabaseService } from '@/utils/supabaseService.uts'
type OrderType = {
id: string
order_no: string
created_at: string
merchant_id: string
}
type OrderItemType = {
id: string
product_id: string
id: number
order_id: number
product_id: number
product_name: string
product_image: string
sku_specifications: any
sku_specifications: any | null
price: number
quantity: number
}
type MerchantRatingType = {
description: number
logistics: number
service: number
}
type MerchantType = {
id: string
shop_name: string
@@ -164,85 +179,113 @@ type MerchantType = {
}
const orderId = ref<string>('')
const order = ref<any>({})
const order = ref<OrderType | null>(null)
const orderItems = ref<Array<OrderItemType>>([])
const merchant = ref<MerchantType | null>(null)
const ratings = ref<Array<number>>([])
const contents = ref<Array<string>>([])
const images = ref<Array<Array<string>>>([])
const anonymous = ref<boolean>(false)
const merchantRating = ref({
const merchantRating = ref<MerchantRatingType>({
description: 5,
logistics: 5,
service: 5
})
} as MerchantRatingType)
const isSubmitting = ref<boolean>(false)
// 计算属性
const canSubmit = computed(() => {
// 检查是否所有商品都已评分
if (ratings.value.length === 0) return false
return ratings.value.every(rating => rating > 0)
})
// 生命周期
onLoad((options: any) => {
const optObj = (options instanceof UTSJSONObject) ? (options as UTSJSONObject) : (JSON.parse(JSON.stringify(options ?? {})) as UTSJSONObject)
orderId.value = optObj.getString('orderId') ?? ''
if (orderId.value != '') loadOrderData()
})
// 加载订单数据
const loadOrderData = async () => {
const loadOrderData = async (): Promise<void> => {
try {
const { data: orderData, error: orderError } = await supa
const orderRes = await supa
.from('ml_orders')
.select('*')
.eq('id', orderId.value)
.single()
.execute()
if (orderError !== null) {
console.error('加载订单失败:', orderError)
if (orderRes.error != null) {
console.error('加载订单失败:', orderRes.error)
return
}
order.value = orderData
if (orderRes.data != null) {
const orderData = orderRes.data as UTSJSONObject
order.value = {
id: orderData.getString('id') ?? '',
order_no: orderData.getString('order_no') ?? '',
created_at: orderData.getString('created_at') ?? '',
merchant_id: orderData.getString('merchant_id') ?? ''
} as OrderType
}
// 加载订单商品
const { data: itemsData, error: itemsError } = await supa
const itemsRes = await supa
.from('ml_order_items')
.select(`
*,
product:product_id(images)
`)
.eq('order_id', orderId.value)
.execute()
if (itemsError !== null) {
console.error('加载订单商品失败:', itemsError)
if (itemsRes.error != null) {
console.error('加载订单商品失败:', itemsRes.error)
return
}
orderItems.value = (itemsData ?? []).map((item: any) => ({
...item,
product_image: item.product?.images?.[0] ?? '/static/default-product.png'
}))
const rawData = itemsRes.data
let itemsArray: Array<any> = []
if (rawData != null) {
itemsArray = rawData as Array<any>
}
const processedItems: Array<OrderItemType> = []
for (let i: number = 0; i < itemsArray.length; i++) {
const item = itemsArray[i] as UTSJSONObject
const productObjRaw = item.get('product')
const productObj = (productObjRaw != null) ? (productObjRaw as UTSJSONObject) : null
const imagesArrRaw = (productObj != null) ? productObj.get('images') : null
const imagesArr = (imagesArrRaw != null) ? (imagesArrRaw as Array<string>) : []
const firstImage = (imagesArr.length > 0) ? imagesArr[0] : '/static/default-product.png'
const skuSpecRaw = item.get('sku_specifications')
const skuSpec = (skuSpecRaw != null) ? (skuSpecRaw as any) : null
const processedItem: OrderItemType = {
id: (item.getNumber('id') ?? 0) as number,
order_id: (item.getNumber('order_id') ?? 0) as number,
product_id: (item.getNumber('product_id') ?? 0) as number,
product_name: item.getString('product_name') ?? '',
price: (item.getNumber('price') ?? 0) as number,
quantity: (item.getNumber('quantity') ?? 1) as number,
sku_specifications: skuSpec,
product_image: firstImage
}
processedItems.push(processedItem)
}
orderItems.value = processedItems
// 初始化评分和内容数组
const count = orderItems.value.length
ratings.value = new Array(count).fill(5)
contents.value = new Array(count).fill('')
images.value = new Array(count).fill([])
const newRatings: Array<number> = []
const newContents: Array<string> = []
const newImages: Array<Array<string>> = []
for (let i: number = 0; i < count; i++) {
newRatings.push(5)
newContents.push('')
newImages.push([])
}
ratings.value = newRatings
contents.value = newContents
images.value = newImages
// 加载商家信息
if (order.value.merchant_id) {
const { data: merchantData, error: merchantError } = await supa
const orderObj = order.value as UTSJSONObject
const merchantId = orderObj.getString('merchant_id')
if (merchantId != null && merchantId !== '') {
const merchantRes = await supa
.from('ml_shops')
.select('id, shop_name, rating')
.eq('id', order.value.merchant_id)
.eq('id', merchantId)
.single()
.execute()
if (merchantError == null) {
merchant.value = merchantData
if (merchantRes.error == null && merchantRes.data != null) {
merchant.value = merchantRes.data as MerchantType
}
}
@@ -251,6 +294,22 @@ const loadOrderData = async () => {
}
}
const canSubmit = computed((): boolean => {
if (ratings.value.length === 0) return false
for (let i: number = 0; i < ratings.value.length; i++) {
if (ratings.value[i] <= 0) return false
}
return true
})
onLoad((options: any) => {
if (options != null) {
const optObj = options as UTSJSONObject
orderId.value = optObj.getString('orderId') ?? ''
if (orderId.value != '') loadOrderData()
}
})
// 格式化时间
const formatTime = (timeStr?: string): string => {
if (timeStr == null) return ''
@@ -261,38 +320,52 @@ const formatTime = (timeStr?: string): string => {
return `${year}-${month}-${day}`
}
// 获取规格文本
const getSpecText = (specs: any): string => {
const getSpecText = (specs: any | null): string => {
if (specs == null) return ''
if (typeof specs === 'object') {
return Object.keys(specs)
.map(key => `${key}: ${specs[key]}`)
.join('; ')
if (specs instanceof UTSJSONObject) {
return '规格信息'
}
return String(specs)
return specs as string
}
// 获取评分文本
const getRatingText = (rating: number): string => {
const texts = ['非常差', '差', '一般', '好', '非常好']
return texts[rating - 1] ?? '未评价'
if (rating === 1) return '非常差'
if (rating === 2) return '差'
if (rating === 3) return '一般'
if (rating === 4) return '好'
if (rating === 5) return '非常好'
return '未评价'
}
// 设置商品评分
const setRating = (index: number, rating: number) => {
ratings.value[index] = rating
ratings.value = [...ratings.value]
// 触发响应式更新
const newRatings: number[] = []
for (let i: number = 0; i < ratings.value.length; i++) {
newRatings.push(ratings.value[i])
}
ratings.value = newRatings
}
// 设置商家评分
const setMerchantRating = (type: keyof typeof merchantRating.value, rating: number) => {
merchantRating.value[type] = rating
merchantRating.value = { ...merchantRating.value }
const setMerchantRating = (type: string, rating: number) => {
if (type === 'description') {
merchantRating.value.description = rating
} else if (type === 'logistics') {
merchantRating.value.logistics = rating
} else if (type === 'service') {
merchantRating.value.service = rating
}
}
// 切换匿名
const toggleAnonymous = (event: any) => {
anonymous.value = event.detail.value
const eventObj = event as UTSJSONObject
const detailRaw = eventObj.get('detail')
const detail = detailRaw != null ? (detailRaw as UTSJSONObject) : (new UTSJSONObject())
const valueRaw = detail.get('value')
anonymous.value = valueRaw != null ? (valueRaw as boolean) : false
}
// 上传图片
@@ -312,17 +385,27 @@ const uploadImage = async (index: number) => {
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFiles = res.tempFilePaths
const resObj = res as UTSJSONObject
const tempFilesRaw = resObj.get('tempFilePaths')
const tempFiles = tempFilesRaw != null ? (tempFilesRaw as Array<string>) : []
// 模拟上传过程
uni.showLoading({
title: '上传中...'
})
setTimeout(() => {
// 这里应该调用真实的上传接口
images.value[index].push(...tempFiles)
images.value = [...images.value]
for (let i: number = 0; i < tempFiles.length; i++) {
images.value[index].push(tempFiles[i])
}
const newImages: Array<Array<string>> = []
for (let i: number = 0; i < images.value.length; i++) {
const innerArray: Array<string> = []
for (let j: number = 0; j < images.value[i].length; j++) {
innerArray.push(images.value[i][j])
}
newImages.push(innerArray)
}
images.value = newImages
uni.hideLoading()
uni.showToast({
@@ -337,12 +420,28 @@ const uploadImage = async (index: number) => {
// 删除图片
const deleteImage = (index: number, imgIndex: number) => {
images.value[index].splice(imgIndex, 1)
images.value = [...images.value]
// 触发响应式更新
const newImages: string[][] = []
for (let i: number = 0; i < images.value.length; i++) {
const innerArray: string[] = []
for (let j: number = 0; j < images.value[i].length; j++) {
innerArray.push(images.value[i][j])
}
newImages.push(innerArray)
}
images.value = newImages
}
// 提交评价
const submitReview = async () => {
if (!canSubmit.value || isSubmitting.value) return
// 获取当前用户ID
const getCurrentUserId = (): string => {
const userStore = uni.getStorageSync('userInfo')
if (userStore == null) return ''
const userInfo = userStore as UTSJSONObject
return userInfo.getString('id') ?? ''
}
const submitReview = async (): Promise<void> => {
if (canSubmit.value === false || isSubmitting.value) return
isSubmitting.value = true
@@ -356,57 +455,61 @@ const submitReview = async () => {
return
}
// 提交商品评价
const productReviews = orderItems.value.map((item, index) => ({
user_id: userId,
product_id: item.product_id,
order_id: orderId.value,
rating: ratings.value[index],
content: contents.value[index] != '' ? contents.value[index] : '',
images: images.value[index],
is_anonymous: anonymous.value
}))
const { error: reviewsError } = await supa
.from('ml_product_reviews')
.insert(productReviews)
if (reviewsError !== null) {
throw reviewsError
type ProductReviewType = {
user_id: string,
product_id: number,
order_id: string,
rating: number,
content: string,
images: Array<string>,
is_anonymous: boolean
}
const productReviews: Array<UTSJSONObject> = []
for (let index: number = 0; index < orderItems.value.length; index++) {
const item = orderItems.value[index]
const reviewObj: UTSJSONObject = new UTSJSONObject()
reviewObj.set('user_id', userId)
reviewObj.set('product_id', item.product_id)
reviewObj.set('order_id', orderId.value)
reviewObj.set('rating', ratings.value[index])
reviewObj.set('content', contents.value[index] != '' ? contents.value[index] : '')
reviewObj.set('images', images.value[index])
reviewObj.set('is_anonymous', anonymous.value)
productReviews.push(reviewObj)
}
// 提交店铺评价
if (merchant.value) {
const merchantReview = {
user_id: userId,
shop_id: merchant.value.id,
order_id: orderId.value,
description_rating: merchantRating.value.description,
logistics_rating: merchantRating.value.logistics,
service_rating: merchantRating.value.service
const reviewsSuccess = await supabaseService.submitProductReviews(productReviews)
if (reviewsSuccess == false) {
uni.showToast({
title: '提交失败',
icon: 'none'
})
isSubmitting.value = false
return
}
if (merchant.value != null) {
type MerchantReviewType = {
user_id: string,
shop_id: string,
order_id: string,
description_rating: number,
logistics_rating: number,
service_rating: number
}
const merchantReviewObj: UTSJSONObject = new UTSJSONObject()
merchantReviewObj.set('user_id', userId)
merchantReviewObj.set('shop_id', merchant.value.id)
merchantReviewObj.set('order_id', orderId.value)
merchantReviewObj.set('description_rating', merchantRating.value.description)
merchantReviewObj.set('logistics_rating', merchantRating.value.logistics)
merchantReviewObj.set('service_rating', merchantRating.value.service)
const { error: merchantError } = await supa
.from('ml_shop_reviews')
.insert(merchantReview)
if (merchantError !== null) {
console.error('提交店铺评价失败:', merchantError)
}
await supabaseService.submitShopReview(merchantReviewObj)
}
// 更新订单状态为已评价 (如果需要标记为已评价,可以在这里处理,例如 status=5 implies Reviewed or keeping at 4)
// 这里保持为 4 (Completed)
const { error: orderError } = await supa
.from('ml_orders')
.update({ order_status: 4 })
.eq('id', orderId.value)
await supabaseService.updateOrderStatus(orderId.value, 4)
if (orderError !== null) {
console.error('更新订单状态失败:', orderError)
}
// 显示成功提示
uni.showToast({
title: '评价成功',
icon: 'success',
@@ -429,14 +532,8 @@ const submitReview = async () => {
}
}
// 获取当前用户ID
const getCurrentUserId = (): string => {
const userStore = uni.getStorageSync('userInfo')
return userStore?.getString('id') ?? ''
}
// 返回
const goBack = () => {
const goBack = (): void => {
uni.navigateBack()
}
</script>
@@ -503,10 +600,6 @@ const goBack = () => {
border-bottom: 1px solid #f5f5f5;
}
.product-review:last-child {
border-bottom: none;
}
.product-header {
display: flex;
margin-bottom: 20px;
@@ -643,9 +736,7 @@ const goBack = () => {
align-items: center;
justify-content: center;
}
margin-right: 10px;
margin-bottom: 10px;
.upload-btn {
width: 70px;
height: 70px;
@@ -738,10 +829,6 @@ margin-right: 10px;
margin-bottom: 5px;
}
.tip-item:last-child {
margin-bottom: 0;
}
.submit-section {
background-color: #ffffff;
padding: 15px;