添加新页面
This commit is contained in:
202
pages/mall/delivery/ratings.uvue
Normal file
202
pages/mall/delivery/ratings.uvue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<view class="ratings-page">
|
||||
<!-- 顶部总览卡片 -->
|
||||
<view class="summary-card">
|
||||
<!-- 返回按钮 -->
|
||||
<view class="back-box" @click="backToIndex">
|
||||
<text class="back-icon">‹</text>
|
||||
</view>
|
||||
|
||||
<view class="summary-left">
|
||||
<text class="score">4.9</text>
|
||||
<text class="stars">
|
||||
<text class="star-on">★</text>
|
||||
<text class="star-on">★</text>
|
||||
<text class="star-on">★</text>
|
||||
<text class="star-on">★</text>
|
||||
<text class="star-on">★</text>
|
||||
</text>
|
||||
<text class="count">共收到 128 条评价</text>
|
||||
</view>
|
||||
|
||||
<view class="summary-right">
|
||||
<view class="rate-item">
|
||||
<text class="rate-label">好评率</text>
|
||||
<text class="rate-value">96.1%</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 标签筛选 -->
|
||||
<view class="filter-bar">
|
||||
<view
|
||||
v-for="t in tabs"
|
||||
:key="t.key"
|
||||
:class="['filter-tab', currentTab===t.key?'active':'']"
|
||||
@click="switchTab(t.key)"
|
||||
>
|
||||
<text>{{ t.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 评价列表 -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="ratings-scroll"
|
||||
refresher-enabled
|
||||
:refresher-triggered="isRefreshing"
|
||||
@scrolltolower="loadMore"
|
||||
@refresherrefresh="onRefresh"
|
||||
>
|
||||
<view v-if="list.length" class="rating-list">
|
||||
<view v-for="item in list" :key="item.id" class="rating-card">
|
||||
<view class="rating-header">
|
||||
<image :src="item.avatar" class="user-avatar" mode="aspectFill" />
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ item.userName }}</text>
|
||||
<text class="rating-stars">
|
||||
<text v-for="s in 5" :key="s" :class="s<=item.score?'star-on':'star-off'">★</text>
|
||||
</text>
|
||||
</view>
|
||||
<text class="rating-time">{{ item.time }}</text>
|
||||
</view>
|
||||
|
||||
<text v-if="item.comment" class="rating-comment">{{ item.comment }}</text>
|
||||
<text v-else class="rating-comment empty">用户未写评价</text>
|
||||
|
||||
<view v-if="item.tags&&item.tags.length" class="rating-tags">
|
||||
<view v-for="tag in item.tags" :key="tag" class="tag">{{ tag }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="hasMore" class="load-tip">正在加载…</view>
|
||||
<view v-else class="load-tip">已加载全部</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="no-data">
|
||||
<text class="no-data-icon">📝</text>
|
||||
<text class="no-data-text">暂无评价记录</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
/* 返回按钮 */
|
||||
function backToIndex() {
|
||||
uni.navigateBack({ url: '/pages/mall/delivery/index' })
|
||||
}
|
||||
|
||||
/* mock 数据 */
|
||||
const currentTab = ref('all')
|
||||
const isRefreshing = ref(false)
|
||||
const hasMore = ref(true)
|
||||
const page = ref(1)
|
||||
const list = ref<any[]>([])
|
||||
|
||||
const tabs = [
|
||||
{ key: 'all', label: '全部' },
|
||||
{ key: 'good', label: '好评' },
|
||||
{ key: 'bad', label: '差评' }
|
||||
]
|
||||
|
||||
function mockList() {
|
||||
const tagPool = ['准时送达', '着装整洁', '服务热情', '配送快', '餐品完好']
|
||||
return Array.from({ length: 10 }, (_, i) => ({
|
||||
id: `${currentTab.value}_${page.value}_${i}`,
|
||||
userName: '用户' + (Math.random() * 1000).toFixed(0),
|
||||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
|
||||
score: currentTab.value === 'bad' ? Math.floor(Math.random() * 2) + 1 : Math.floor(Math.random() * 2) + 4,
|
||||
comment: Math.random() > 0.3 ? '配送很及时,服务态度很好!' : '',
|
||||
tags: tagPool.slice(0, Math.floor(Math.random() * 3) + 1),
|
||||
time: `${Math.floor(Math.random() * 60)}分钟前`
|
||||
}))
|
||||
}
|
||||
|
||||
async function fetchList(reset = false) {
|
||||
if (reset) page.value = 1
|
||||
const newList = mockList()
|
||||
if (reset) list.value = newList
|
||||
else list.value.push(...newList)
|
||||
hasMore.value = page.value < 4
|
||||
page.value++
|
||||
}
|
||||
|
||||
function switchTab(key: string) {
|
||||
if (currentTab.value === key) return
|
||||
currentTab.value = key
|
||||
fetchList(true)
|
||||
}
|
||||
|
||||
function loadMore() {
|
||||
if (!hasMore.value) return
|
||||
fetchList(false)
|
||||
}
|
||||
|
||||
function onRefresh() {
|
||||
isRefreshing.value = true
|
||||
fetchList(true).finally(() => isRefreshing.value = false)
|
||||
}
|
||||
|
||||
onMounted(() => fetchList(true))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ratings-page{min-height:100vh;background:#f5f5f5;display:flex;flex-direction:column;}
|
||||
|
||||
.summary-card{
|
||||
margin:20rpx 30rpx;
|
||||
background:#fff;border-radius:20rpx;padding:40rpx;
|
||||
display:flex;align-items:center;position:relative;
|
||||
}
|
||||
.back-box{
|
||||
position:absolute;left:30rpx;top:50%;transform:translateY(-50%);
|
||||
width:60rpx;height:60rpx;border-radius:50%;
|
||||
background:rgba(0,0,0,.05);display:flex;align-items:center;justify-content:center;
|
||||
}
|
||||
.back-box:active{background:rgba(0,0,0,.15);}
|
||||
.back-icon{font-size:40rpx;color:#333;}
|
||||
.summary-left{flex:1;display:flex;flex-direction:center;align-items:center;}
|
||||
.score{font-size:64rpx;font-weight:bold;color:#ff9500;margin-right:20rpx;}
|
||||
.stars .star-on{color:#ff9500;}
|
||||
.count{font-size:24rpx;color:#666;margin-left:20rpx;}
|
||||
.summary-right .rate-item{text-align:center;}
|
||||
.rate-label{font-size:24rpx;color:#666;}
|
||||
.rate-value{font-size:40rpx;font-weight:bold;color:#4caf50;}
|
||||
|
||||
.filter-bar{
|
||||
margin:0 30rpx 20rpx;
|
||||
background:#fff;border-radius:20rpx;padding:20rpx;
|
||||
display:flex;justify-content:space-around;
|
||||
}
|
||||
.filter-tab{
|
||||
padding:10rpx 30rpx;border-radius:30rpx;font-size:26rpx;
|
||||
background:#f0f0f0;color:#666;
|
||||
}
|
||||
.filter-tab.active{background:#74b9ff;color:#fff;}
|
||||
|
||||
.ratings-scroll{flex:1;}
|
||||
.rating-list{padding:0 30rpx 30rpx;}
|
||||
.rating-card{
|
||||
background:#fff;border-radius:16rpx;padding:30rpx;margin-bottom:20rpx;
|
||||
}
|
||||
.rating-header{display:flex;align-items:center;}
|
||||
.user-avatar{width:80rpx;height:80rpx;border-radius:50%;margin-right:20rpx;}
|
||||
.user-info{flex:1;}
|
||||
.user-name{font-size:28rpx;color:#333;}
|
||||
.rating-stars{font-size:24rpx;}
|
||||
.rating-stars .star-on{color:#ff9500;}
|
||||
.rating-stars .star-off{color:#ddd;}
|
||||
.rating-time{font-size:22rpx;color:#999;}
|
||||
.rating-comment{margin:20rpx 0;font-size:28rpx;color:#333;line-height:1.5;}
|
||||
.rating-comment.empty{color:#bbb;}
|
||||
.rating-tags{display:flex;flex-wrap:wrap;gap:10rpx;margin-top:15rpx;}
|
||||
.tag{background:#e8f4fd;color:#1976d2;font-size:22rpx;padding:4rpx 12rpx;border-radius:8rpx;}
|
||||
|
||||
.load-tip{text-align:center;font-size:24rpx;color:#999;padding:20rpx 0;}
|
||||
.no-data{text-align:center;padding:120rpx 0;}
|
||||
.no-data-icon{font-size:80rpx;}
|
||||
.no-data-text{font-size:28rpx;color:#999;margin-top:20rpx;}
|
||||
</style>
|
||||
Reference in New Issue
Block a user