9.17/1
This commit is contained in:
133
frontend/pages/product/list.vue
Normal file
133
frontend/pages/product/list.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<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>
|
||||
|
||||
<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">{{ it.name }}</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 class="fab" @click="openForm()">+</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() {
|
||||
this.fetchCategories()
|
||||
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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||
.tabs { display:flex; background:#fff; }
|
||||
.tab { flex:1; text-align:center; padding: 20rpx 0; color:#666; }
|
||||
.tab.active { color:#18b566; font-weight: 600; }
|
||||
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; align-items: center; }
|
||||
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||
.picker { padding: 8rpx 12rpx; background:#f0f0f0; border-radius: 10rpx; color:#666; }
|
||||
.list { flex:1; }
|
||||
.item { display:flex; padding: 20rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||
.thumb { width: 120rpx; height: 120rpx; border-radius: 12rpx; margin-right: 16rpx; background:#fafafa; }
|
||||
.content { flex:1; }
|
||||
.name { color:#333; margin-bottom: 6rpx; font-weight: 600; }
|
||||
.meta { color:#888; font-size: 24rpx; }
|
||||
.price { margin-left: 20rpx; color:#f60; }
|
||||
.empty { height: 60vh; display:flex; align-items:center; justify-content:center; color:#999; }
|
||||
.fab { position: fixed; right: 30rpx; bottom: 120rpx; width: 100rpx; height: 100rpx; background:#18b566; 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>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user