200 lines
8.3 KiB
Vue
200 lines
8.3 KiB
Vue
<template>
|
||
<view class="page">
|
||
<view class="hero">
|
||
<text class="title">我的配件提交</text>
|
||
<text class="desc">查看待审核、已通过、已驳回的记录</text>
|
||
</view>
|
||
|
||
<view class="tabs">
|
||
<view class="tab" :class="{ active: status === '' }" @click="switchStatus('')">全部</view>
|
||
<view class="tab" :class="{ active: status === 'pending' }" @click="switchStatus('pending')">待审核</view>
|
||
<view class="tab" :class="{ active: status === 'approved' }" @click="switchStatus('approved')">已通过</view>
|
||
<view class="tab" :class="{ active: status === 'rejected' }" @click="switchStatus('rejected')">已驳回</view>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="list" @scrolltolower="loadMore">
|
||
<view v-if="items.length" class="cards">
|
||
<view class="card" v-for="item in items" :key="item.id">
|
||
<view class="card-header">
|
||
<text class="model">{{ item.model || '-' }}</text>
|
||
<text :class="['status', statusClass(item.status)]">{{ statusLabel(item.status) }}</text>
|
||
</view>
|
||
<view class="card-body">
|
||
<text class="name">{{ item.name || '未填写名称' }}</text>
|
||
<text class="brand">品牌:{{ item.brand || '-' }}</text>
|
||
<text class="time">提交:{{ formatTime(item.createdAt) }}</text>
|
||
<text class="time" v-if="item.reviewedAt">审核:{{ formatTime(item.reviewedAt) }}</text>
|
||
</view>
|
||
<view class="card-footer">
|
||
<button size="mini" @click="viewDetail(item.id)">详情</button>
|
||
<button size="mini" type="primary" v-if="item.status === 'pending'" @click="notifyPending">等待审核</button>
|
||
<button size="mini" type="warn" v-else-if="item.status === 'rejected'" @click="resubmit(item)">重新提交</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-else class="empty">
|
||
<text>暂无提交记录,快去提交新的配件吧</text>
|
||
<button size="mini" class="primary" @click="goSubmit">立即提交</button>
|
||
</view>
|
||
<view v-if="loading" class="loading">加载中...</view>
|
||
<view v-else-if="finished && items.length" class="finished">没有更多了</view>
|
||
</scroll-view>
|
||
|
||
<view class="fab" @click="goSubmit">+</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { get } from '../../common/http.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
status: '',
|
||
items: [],
|
||
page: 1,
|
||
size: 20,
|
||
total: 0,
|
||
loading: false,
|
||
finished: false,
|
||
cacheUnitsLoaded: false,
|
||
cacheCategoriesLoaded: false
|
||
}
|
||
},
|
||
onShow() {
|
||
this.preloadDictionaries()
|
||
this.reload()
|
||
},
|
||
methods: {
|
||
async preloadDictionaries() {
|
||
try {
|
||
const [units, categories, templates] = await Promise.all([
|
||
this.cacheUnitsLoaded ? Promise.resolve(null) : get('/api/product-units'),
|
||
this.cacheCategoriesLoaded ? Promise.resolve(null) : get('/api/product-categories'),
|
||
get('/api/product-templates')
|
||
])
|
||
if (units) {
|
||
const list = Array.isArray(units?.list) ? units.list : (Array.isArray(units) ? units : [])
|
||
uni.setStorageSync('CACHE_UNITS', list)
|
||
this.cacheUnitsLoaded = true
|
||
}
|
||
if (categories) {
|
||
const list = Array.isArray(categories?.list) ? categories.list : (Array.isArray(categories) ? categories : [])
|
||
uni.setStorageSync('CACHE_CATEGORIES', list)
|
||
this.cacheCategoriesLoaded = true
|
||
}
|
||
if (templates) {
|
||
const list = Array.isArray(templates?.list) ? templates.list : (Array.isArray(templates) ? templates : [])
|
||
uni.setStorageSync('CACHE_TEMPLATES', list)
|
||
}
|
||
} catch (_) {
|
||
// 忽略缓存失败
|
||
}
|
||
},
|
||
switchStatus(s) {
|
||
if (this.status === s) return
|
||
this.status = s
|
||
this.reload()
|
||
},
|
||
async reload() {
|
||
this.page = 1
|
||
this.items = []
|
||
this.finished = false
|
||
await this.loadMore()
|
||
},
|
||
async loadMore() {
|
||
if (this.loading || this.finished) return
|
||
this.loading = true
|
||
try {
|
||
const params = { page: this.page, size: this.size }
|
||
if (this.status) params.status = this.status
|
||
const res = await get('/api/products/submissions', params)
|
||
const list = Array.isArray(res?.list) ? res.list : []
|
||
this.items = this.items.concat(list)
|
||
this.total = Number(res?.total || this.items.length)
|
||
if (list.length < this.size) this.finished = true
|
||
this.page += 1
|
||
} catch (e) {
|
||
console.warn('加载提交记录失败', e)
|
||
const msg = e?.message || '加载失败'
|
||
uni.showToast({ title: msg, icon: 'none' })
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
statusLabel(s) {
|
||
if (s === 'approved') return '已通过'
|
||
if (s === 'rejected') return '已驳回'
|
||
return '待审核'
|
||
},
|
||
statusClass(s) {
|
||
if (s === 'approved') return 'approved'
|
||
if (s === 'rejected') return 'rejected'
|
||
return 'pending'
|
||
},
|
||
formatTime(value) {
|
||
if (!value) return '-'
|
||
try {
|
||
const d = new Date(value)
|
||
if (!Number.isFinite(d.getTime())) return value
|
||
const y = d.getFullYear()
|
||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
const hh = String(d.getHours()).padStart(2, '0')
|
||
const mm = String(d.getMinutes()).padStart(2, '0')
|
||
return `${y}-${m}-${day} ${hh}:${mm}`
|
||
} catch (_) { return value }
|
||
},
|
||
viewDetail(id) {
|
||
uni.navigateTo({ url: `/pages/product/submission-detail?id=${id}` })
|
||
},
|
||
notifyPending() {
|
||
uni.showToast({ title: '审核中,请耐心等待', icon: 'none' })
|
||
},
|
||
resubmit(item) {
|
||
const payload = {
|
||
model: item.model,
|
||
name: item.name,
|
||
brand: item.brand,
|
||
spec: item.spec,
|
||
origin: item.origin,
|
||
unitId: item.unitId,
|
||
categoryId: item.categoryId,
|
||
remark: item.remark
|
||
}
|
||
const query = encodeURIComponent(JSON.stringify(payload))
|
||
uni.navigateTo({ url: `/pages/product/submit?prefill=${query}` })
|
||
},
|
||
goSubmit() {
|
||
uni.navigateTo({ url: '/pages/product/submit' })
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.page { display: flex; flex-direction: column; height: 100vh; background: #f6f7fb; padding-bottom: 140rpx; }
|
||
.hero { padding: 24rpx; background: #fff; box-shadow: 0 10rpx 30rpx rgba(0,0,0,0.04); }
|
||
.title { font-size: 34rpx; font-weight: 700; color: #2d3a4a; }
|
||
.desc { font-size: 24rpx; color: #7a8899; margin-top: 8rpx; }
|
||
.tabs { display: flex; background: #fff; margin: 16rpx; border-radius: 999rpx; overflow: hidden; box-shadow: inset 0 0 0 1rpx rgba(76,141,255,0.1); }
|
||
.tab { flex: 1; text-align: center; padding: 20rpx 0; font-size: 28rpx; color: #7a8899; }
|
||
.tab.active { background: linear-gradient(135deg, #4c8dff, #6ab7ff); color: #fff; font-weight: 600; }
|
||
.list { flex: 1; padding: 0 20rpx; }
|
||
.cards { display: flex; flex-direction: column; gap: 20rpx; padding-bottom: 40rpx; }
|
||
.card { background: #fff; border-radius: 18rpx; padding: 22rpx; box-shadow: 0 10rpx 30rpx rgba(0,0,0,0.05); }
|
||
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
|
||
.model { font-size: 30rpx; font-weight: 700; color: #2d3a4a; }
|
||
.status { font-size: 24rpx; padding: 6rpx 18rpx; border-radius: 999rpx; }
|
||
.status.pending { background: rgba(246, 190, 0, 0.15); color: #c47f00; }
|
||
.status.approved { background: rgba(103,194,58,0.15); color: #409eff; }
|
||
.status.rejected { background: rgba(255,87,115,0.18); color: #f56c6c; }
|
||
.card-body { display: flex; flex-direction: column; gap: 6rpx; color: #4f5969; font-size: 26rpx; }
|
||
.name { font-weight: 600; color: #2d3a4a; }
|
||
.card-footer { display: flex; gap: 12rpx; margin-top: 16rpx; }
|
||
.empty { height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #8894a3; gap: 20rpx; }
|
||
.empty .primary { background: #4c8dff; color: #fff; border-radius: 999rpx; padding: 12rpx 30rpx; }
|
||
.loading, .finished { text-align: center; padding: 20rpx 0; color: #7a8899; }
|
||
.fab { position: fixed; right: 30rpx; bottom: 120rpx; width: 100rpx; height: 100rpx; background: linear-gradient(135deg, #4c8dff, #6ab7ff); color: #fff; border-radius: 50rpx; display: flex; align-items: center; justify-content: center; font-size: 48rpx; box-shadow: 0 20rpx 40rpx rgba(0,0,0,0.2); }
|
||
</style>
|