Files
PartsInquiry/frontend/pages/product/list.vue
2025-09-27 22:57:59 +08:00

161 lines
5.9 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="tabs">
<view class="tab" :class="{active: tab==='all'}" @click="switchTab('all')">全部</view>
<view class="tab" :class="{active: tab==='category'}" @click="switchTab('category')">按类别</view>
<view class="tab extra" @click="goMySubmissions">我的提交</view>
</view>
<view class="search">
<input v-model.trim="query.kw" placeholder="输入名称/条码/规格查询" @confirm="reload" />
<picker mode="selector" :range="categoryNames" v-if="tab==='category'" @change="onPickCategory">
<view class="picker">{{ categoryLabel }}</view>
</picker>
<button size="mini" @click="reload">查询</button>
</view>
<scroll-view scroll-y class="list" @scrolltolower="loadMore">
<block v-if="items.length">
<view class="item" v-for="it in items" :key="it.id" @click="openForm(it.id)">
<image v-if="it.cover" :src="it.cover" class="thumb" mode="aspectFill" />
<view class="content">
<view class="name">
<text>{{ it.name }}</text>
<text v-if="it.platformStatus==='platform'" class="tag-platform">平台推荐</text>
<text v-else-if="it.sourceSubmissionId" class="tag-custom">我的提交</text>
</view>
<view class="meta">{{ it.brand || '-' }} {{ it.model || '' }} {{ it.spec || '' }}</view>
<view class="meta">库存{{ it.stock ?? 0 }}
<text class="price">零售价¥{{ (it.retailPrice ?? it.price ?? 0).toFixed(2) }}</text>
</view>
</view>
</view>
</block>
<view v-else class="empty">
<text>暂无数据点击右上角新增</text>
</view>
</scroll-view>
<!-- 保留我的提交页的此处不显示 -->
</view>
</template>
<script>
import { get } from '../../common/http.js'
export default {
data() {
return {
items: [],
query: { kw: '', page: 1, size: 20, categoryId: '' },
finished: false,
loading: false,
tab: 'all',
categories: []
}
},
onLoad() {
const hasToken = (() => { try { return !!uni.getStorageSync('TOKEN') } catch(e){ return false } })()
if (!hasToken) {
this.items = []
this.categories = []
uni.showToast({ title: '请登录使用该功能', icon: 'none' })
return
}
this.fetchCategories()
this.reload()
},
onShow() {
const hasToken = (() => { try { return !!uni.getStorageSync('TOKEN') } catch(e){ return false } })()
if (!hasToken) return
// 从创建/编辑页返回时,确保刷新最新列表
this.reload()
},
computed: {
categoryNames() { return this.categories.map(c => c.name) },
categoryLabel() {
const c = this.categories.find(x => String(x.id) === String(this.query.categoryId))
return c ? '类别:' + c.name : '选择类别'
}
},
methods: {
switchTab(t) {
this.tab = t
this.query.categoryId = ''
this.reload()
},
onPickCategory(e) {
const idx = Number(e.detail.value)
const c = this.categories[idx]
this.query.categoryId = c ? c.id : ''
this.reload()
},
async fetchCategories() {
try {
const res = await get('/api/product-categories', {})
this.categories = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
} catch (_) {}
},
reload() {
this.items = []
this.query.page = 1
this.finished = false
this.loadMore()
},
async loadMore() {
if (this.loading || this.finished) return
this.loading = true
try {
const params = { kw: this.query.kw, page: this.query.page, size: this.query.size }
if (this.tab === 'category' && this.query.categoryId) params.categoryId = this.query.categoryId
const res = await get('/api/products', params)
const list = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
this.items = this.items.concat(list)
if (list.length < this.query.size) this.finished = true
this.query.page += 1
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
this.loading = false
}
},
openForm(id) {
const url = '/pages/product/form' + (id ? ('?id=' + id) : '')
uni.navigateTo({ url })
},
goMySubmissions() {
uni.navigateTo({ url: '/pages/product/submissions' })
}
}
}
</script>
<style lang="scss">
.page { display:flex; flex-direction: column; height: 100vh; }
.tabs { display:flex; background:$uni-bg-color-grey; }
.tab { flex:1; text-align:center; padding: 20rpx 0; color:$uni-text-color-grey; }
.tab.active { color:$uni-color-primary; font-weight: 600; }
.tab.extra { flex: 0 0 180rpx; color:$uni-color-primary; font-weight: 600; }
.search { display:flex; gap: 12rpx; padding: 16rpx; background:$uni-bg-color-grey; align-items: center; }
.search input { flex:1; background:$uni-bg-color-hover; border-radius: 12rpx; padding: 12rpx; color: $uni-text-color; }
.picker { padding: 8rpx 12rpx; background:$uni-bg-color-hover; border-radius: 10rpx; color:$uni-text-color-grey; }
.list { flex:1; }
.item { display:flex; padding: 20rpx; background:$uni-bg-color-grey; border-bottom: 1rpx solid $uni-border-color; }
.thumb { width: 120rpx; height: 120rpx; border-radius: 12rpx; margin-right: 16rpx; background:$uni-bg-color-hover; }
.content { flex:1; }
.name { color:$uni-text-color; margin-bottom: 6rpx; font-weight: 600; display:flex; align-items:center; gap: 12rpx; }
.tag-platform { font-size: 22rpx; color:#fff; background:#2d8cf0; padding: 4rpx 10rpx; border-radius: 8rpx; }
.tag-custom { font-size: 22rpx; color:#fff; background:#67c23a; padding: 4rpx 10rpx; border-radius: 8rpx; }
.meta { color:$uni-text-color-grey; font-size: 24rpx; }
.price { margin-left: 20rpx; color:$uni-color-primary; }
.empty { height: 60vh; display:flex; align-items:center; justify-content:center; color:$uni-text-color-grey; }
.fab { position: fixed; right: 30rpx; bottom: 120rpx; width: 100rpx; height: 100rpx; background:$uni-color-primary; color:#fff; border-radius: 50rpx; text-align:center; line-height: 100rpx; font-size: 48rpx; box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.15); }
</style>