Files
PartsInquiry/frontend/pages/product/submissions.vue
2025-09-29 21:38:32 +08:00

200 lines
8.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>