9.17/1
This commit is contained in:
@@ -43,3 +43,4 @@
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,3 +48,4 @@
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<view class="tab" :class="{ active: activeTab==='home' }" @click="activeTab='home'">
|
||||
<text>首页</text>
|
||||
</view>
|
||||
<view class="tab" :class="{ active: activeTab==='product' }" @click="activeTab='product'">
|
||||
<view class="tab" :class="{ active: activeTab==='product' }" @click="goProduct">
|
||||
<text>货品</text>
|
||||
</view>
|
||||
<view class="tab primary" @click="onCreateOrder">
|
||||
@@ -99,6 +99,7 @@
|
||||
loadingNotices: false,
|
||||
noticeError: '',
|
||||
features: [
|
||||
{ key: 'product', title: '货品', img: '/static/icons/product.png', emoji: '📦' },
|
||||
{ key: 'customer', title: '客户', img: '/static/icons/customer.png', emoji: '👥' },
|
||||
{ key: 'sale', title: '销售', img: '/static/icons/sale.png', emoji: '💰' },
|
||||
{ key: 'account', title: '账户', img: '/static/icons/account.png', emoji: '💳' },
|
||||
@@ -118,12 +119,14 @@
|
||||
methods: {
|
||||
async fetchMetrics() {
|
||||
try {
|
||||
const d = await get('/api/metrics/overview')
|
||||
const d = await get('/api/dashboard/overview')
|
||||
const toNum = v => (typeof v === 'number' ? v : Number(v || 0))
|
||||
this.kpi = {
|
||||
todaySales: (d && d.todaySales) || '0.00',
|
||||
monthSales: (d && d.monthSales) || '0.00',
|
||||
monthProfit: (d && d.monthProfit) || '0.00',
|
||||
stockCount: (d && d.stockCount) || '0'
|
||||
...this.kpi,
|
||||
todaySales: toNum(d && d.todaySalesAmount).toFixed(2),
|
||||
monthSales: toNum(d && d.monthSalesAmount).toFixed(2),
|
||||
monthProfit: toNum(d && d.monthGrossProfit).toFixed(2),
|
||||
stockCount: String((d && d.stockTotalQuantity) != null ? d.stockTotalQuantity : 0)
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略错误,保留默认值
|
||||
@@ -145,8 +148,16 @@
|
||||
}
|
||||
},
|
||||
onFeatureTap(item) {
|
||||
if (item.key === 'product') {
|
||||
uni.navigateTo({ url: '/pages/product/list' })
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: item.title + '(开发中)', icon: 'none' })
|
||||
},
|
||||
goProduct() {
|
||||
this.activeTab = 'product'
|
||||
uni.navigateTo({ url: '/pages/product/list' })
|
||||
},
|
||||
onCreateOrder() {
|
||||
uni.navigateTo({ url: '/pages/order/create' })
|
||||
},
|
||||
|
||||
67
frontend/pages/product/categories.vue
Normal file
67
frontend/pages/product/categories.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="toolbar">
|
||||
<input v-model.trim="name" placeholder="新类别名称" />
|
||||
<button size="mini" @click="create">新增</button>
|
||||
</view>
|
||||
<scroll-view scroll-y class="list">
|
||||
<view class="item" v-for="c in list" :key="c.id">
|
||||
<input v-model.trim="c.name" />
|
||||
<view class="ops">
|
||||
<button size="mini" @click="update(c)">保存</button>
|
||||
<button size="mini" type="warn" @click="remove(c)">删除</button>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { get, post, put, del } from '../../common/http.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { name: '', list: [] }
|
||||
},
|
||||
onLoad() { this.reload() },
|
||||
methods: {
|
||||
async reload() {
|
||||
try {
|
||||
const res = await get('/api/product-categories')
|
||||
this.list = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||
} catch (_) {}
|
||||
},
|
||||
async create() {
|
||||
if (!this.name) return
|
||||
await post('/api/product-categories', { name: this.name })
|
||||
this.name = ''
|
||||
this.reload()
|
||||
},
|
||||
async update(c) {
|
||||
await put('/api/product-categories/' + c.id, { name: c.name })
|
||||
uni.showToast({ title: '已保存', icon: 'success' })
|
||||
},
|
||||
async remove(c) {
|
||||
uni.showModal({ content: '确定删除该类别?', success: async (r) => {
|
||||
if (!r.confirm) return
|
||||
await del('/api/product-categories/' + c.id)
|
||||
this.reload()
|
||||
}})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||
.toolbar { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }
|
||||
.toolbar input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||
.list { flex:1; }
|
||||
.item { display:flex; gap: 12rpx; align-items:center; padding: 16rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||
.item input { flex:1; background:#f7f7f7; border-radius: 10rpx; padding: 12rpx; }
|
||||
.ops { display:flex; gap: 10rpx; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
213
frontend/pages/product/form.vue
Normal file
213
frontend/pages/product/form.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<scroll-view scroll-y class="page">
|
||||
<view class="card">
|
||||
<view class="row">
|
||||
<text class="label">商品名称</text>
|
||||
<input v-model.trim="form.name" placeholder="必填" />
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">条形码</text>
|
||||
<input v-model.trim="form.barcode" placeholder="可扫码或输入" />
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<button size="mini" @click="scan">扫码</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">品牌/型号/规格/产地</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<input v-model.trim="form.brand" placeholder="品牌" />
|
||||
</view>
|
||||
<view class="row">
|
||||
<input v-model.trim="form.model" placeholder="型号" />
|
||||
</view>
|
||||
<view class="row">
|
||||
<input v-model.trim="form.spec" placeholder="规格" />
|
||||
</view>
|
||||
<view class="row">
|
||||
<input v-model.trim="form.origin" placeholder="产地" />
|
||||
</view>
|
||||
<view class="row">
|
||||
<picker mode="selector" :range="unitNames" @change="onPickUnit">
|
||||
<view class="picker">主单位:{{ unitLabel }}</view>
|
||||
</picker>
|
||||
<picker mode="selector" :range="categoryNames" @change="onPickCategory">
|
||||
<view class="picker">类别:{{ categoryLabel }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="row">
|
||||
<text class="label">库存与安全库存</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<input type="number" v-model.number="form.stock" placeholder="当前库存" />
|
||||
<input type="number" v-model.number="form.safeMin" placeholder="安全库存下限" />
|
||||
<input type="number" v-model.number="form.safeMax" placeholder="安全库存上限" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="row">
|
||||
<text class="label">价格(进价/零售/批发/大单)</text>
|
||||
</view>
|
||||
<view class="row prices">
|
||||
<input type="number" v-model.number="form.purchasePrice" placeholder="进货价" />
|
||||
<input type="number" v-model.number="form.retailPrice" placeholder="零售价" />
|
||||
<input type="number" v-model.number="form.wholesalePrice" placeholder="批发价" />
|
||||
<input type="number" v-model.number="form.bigClientPrice" placeholder="大单价" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="label">图片</text>
|
||||
<ImageUploader v-model="form.images" :formData="{ ownerType: 'product' }" />
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="label">备注</text>
|
||||
<textarea v-model.trim="form.remark" placeholder="可选" auto-height />
|
||||
</view>
|
||||
|
||||
<view class="fixed">
|
||||
<button type="default" @click="save(false)">保存</button>
|
||||
<button type="primary" @click="save(true)">保存并继续</button>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ImageUploader from '../../components/ImageUploader.vue'
|
||||
import { get, post, put } from '../../common/http.js'
|
||||
|
||||
export default {
|
||||
components: { ImageUploader },
|
||||
data() {
|
||||
return {
|
||||
id: '',
|
||||
form: {
|
||||
name: '', barcode: '', brand: '', model: '', spec: '', origin: '',
|
||||
categoryId: '', unitId: '',
|
||||
stock: null, safeMin: null, safeMax: null,
|
||||
purchasePrice: null, retailPrice: null, wholesalePrice: null, bigClientPrice: null,
|
||||
images: [], remark: ''
|
||||
},
|
||||
units: [],
|
||||
categories: []
|
||||
}
|
||||
},
|
||||
onLoad(query) {
|
||||
this.id = query?.id || ''
|
||||
this.bootstrap()
|
||||
},
|
||||
computed: {
|
||||
unitNames() { return this.units.map(u => u.name) },
|
||||
categoryNames() { return this.categories.map(c => c.name) },
|
||||
unitLabel() {
|
||||
const u = this.units.find(x => String(x.id) === String(this.form.unitId))
|
||||
return u ? u.name : '选择单位'
|
||||
},
|
||||
categoryLabel() {
|
||||
const c = this.categories.find(x => String(x.id) === String(this.form.categoryId))
|
||||
return c ? c.name : '选择类别'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async bootstrap() {
|
||||
await Promise.all([this.fetchUnits(), this.fetchCategories()])
|
||||
if (this.id) this.loadDetail()
|
||||
},
|
||||
async fetchUnits() {
|
||||
try {
|
||||
const res = await get('/api/product-units')
|
||||
this.units = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||
} catch (_) {}
|
||||
},
|
||||
async fetchCategories() {
|
||||
try {
|
||||
const res = await get('/api/product-categories')
|
||||
this.categories = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||
} catch (_) {}
|
||||
},
|
||||
onPickUnit(e) {
|
||||
const idx = Number(e.detail.value); const u = this.units[idx]
|
||||
this.form.unitId = u ? u.id : ''
|
||||
},
|
||||
onPickCategory(e) {
|
||||
const idx = Number(e.detail.value); const c = this.categories[idx]
|
||||
this.form.categoryId = c ? c.id : ''
|
||||
},
|
||||
scan() {
|
||||
uni.scanCode({ onlyFromCamera: false, success: (res) => {
|
||||
this.form.barcode = res.result
|
||||
}})
|
||||
},
|
||||
async loadDetail() {
|
||||
try {
|
||||
const data = await get('/api/products/' + this.id)
|
||||
Object.assign(this.form, {
|
||||
name: data.name,
|
||||
barcode: data.barcode, brand: data.brand, model: data.model, spec: data.spec, origin: data.origin,
|
||||
categoryId: data.categoryId, unitId: data.unitId,
|
||||
stock: data.stock,
|
||||
safeMin: data.safeMin, safeMax: data.safeMax,
|
||||
purchasePrice: data.purchasePrice, retailPrice: data.retailPrice,
|
||||
wholesalePrice: data.wholesalePrice, bigClientPrice: data.bigClientPrice,
|
||||
images: (data.images || []).map(i => i.url || i)
|
||||
})
|
||||
} catch (_) {}
|
||||
},
|
||||
validate() {
|
||||
if (!this.form.name) { uni.showToast({ title: '请填写名称', icon: 'none' }); return false }
|
||||
if (this.form.safeMin != null && this.form.safeMax != null && Number(this.form.safeMin) > Number(this.form.safeMax)) {
|
||||
uni.showToast({ title: '安全库存区间不合法', icon: 'none' }); return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
buildPayload() {
|
||||
const f = this.form
|
||||
return {
|
||||
name: f.name, barcode: f.barcode, brand: f.brand, model: f.model, spec: f.spec, origin: f.origin,
|
||||
categoryId: f.categoryId || null, unitId: f.unitId,
|
||||
safeMin: f.safeMin, safeMax: f.safeMax,
|
||||
prices: {
|
||||
purchasePrice: f.purchasePrice, retailPrice: f.retailPrice, wholesalePrice: f.wholesalePrice, bigClientPrice: f.bigClientPrice
|
||||
},
|
||||
stock: f.stock,
|
||||
images: f.images,
|
||||
remark: f.remark
|
||||
}
|
||||
},
|
||||
async save(goOn) {
|
||||
if (!this.validate()) return
|
||||
const payload = this.buildPayload()
|
||||
try {
|
||||
if (this.id) await put('/api/products/' + this.id, payload)
|
||||
else await post('/api/products', payload)
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
if (goOn && !this.id) {
|
||||
this.form = { name: '', barcode: '', brand: '', model: '', spec: '', origin: '', categoryId: '', unitId: '', stock: null, safeMin: null, safeMax: null, purchasePrice: null, retailPrice: null, wholesalePrice: null, bigClientPrice: null, images: [], remark: '' }
|
||||
} else {
|
||||
setTimeout(() => uni.navigateBack(), 400)
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '保存失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page { background:#f6f6f6; height: 100vh; }
|
||||
.card { background:#fff; margin: 16rpx; padding: 16rpx; border-radius: 12rpx; }
|
||||
.row { display:flex; gap: 12rpx; align-items: center; margin-bottom: 12rpx; }
|
||||
.label { width: 180rpx; color:#666; }
|
||||
.row input { flex:1; background:#f7f7f7; border-radius: 10rpx; padding: 12rpx; }
|
||||
.picker { padding: 8rpx 12rpx; background:#f0f0f0; border-radius: 10rpx; color:#666; margin-left: 8rpx; }
|
||||
.prices input { width: 30%; }
|
||||
.fixed { position: fixed; left: 0; right: 0; bottom: 0; background:#fff; padding: 12rpx 16rpx; display:flex; gap: 16rpx; }
|
||||
</style>
|
||||
|
||||
|
||||
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>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -47,3 +47,4 @@
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
45
frontend/pages/product/settings.vue
Normal file
45
frontend/pages/product/settings.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="item">
|
||||
<text>隐藏零库存商品</text>
|
||||
<switch :checked="settings.hideZeroStock" @change="(e)=>update('hideZeroStock', e.detail.value)" />
|
||||
</view>
|
||||
<view class="item">
|
||||
<text>隐藏进货价</text>
|
||||
<switch :checked="settings.hidePurchasePrice" @change="(e)=>update('hidePurchasePrice', e.detail.value)" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { get, put } from '../../common/http.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { settings: { hideZeroStock: false, hidePurchasePrice: false } }
|
||||
},
|
||||
onLoad() { this.load() },
|
||||
methods: {
|
||||
async load() {
|
||||
try {
|
||||
const res = await get('/api/product-settings')
|
||||
this.settings = { hideZeroStock: !!res?.hideZeroStock, hidePurchasePrice: !!res?.hidePurchasePrice }
|
||||
} catch (_) {}
|
||||
},
|
||||
async update(key, val) {
|
||||
const next = { ...this.settings, [key]: val }
|
||||
this.settings = next
|
||||
try { await put('/api/product-settings', next) } catch (_) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page { background:#fff; }
|
||||
.item { display:flex; justify-content: space-between; align-items:center; padding: 20rpx; border-bottom: 1rpx solid #f1f1f1; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
67
frontend/pages/product/units.vue
Normal file
67
frontend/pages/product/units.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="toolbar">
|
||||
<input v-model.trim="name" placeholder="新单位名称" />
|
||||
<button size="mini" @click="create">新增</button>
|
||||
</view>
|
||||
<scroll-view scroll-y class="list">
|
||||
<view class="item" v-for="u in list" :key="u.id">
|
||||
<input v-model.trim="u.name" />
|
||||
<view class="ops">
|
||||
<button size="mini" @click="update(u)">保存</button>
|
||||
<button size="mini" type="warn" @click="remove(u)">删除</button>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { get, post, put, del } from '../../common/http.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { name: '', list: [] }
|
||||
},
|
||||
onLoad() { this.reload() },
|
||||
methods: {
|
||||
async reload() {
|
||||
try {
|
||||
const res = await get('/api/product-units')
|
||||
this.list = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||
} catch (_) {}
|
||||
},
|
||||
async create() {
|
||||
if (!this.name) return
|
||||
await post('/api/product-units', { name: this.name })
|
||||
this.name = ''
|
||||
this.reload()
|
||||
},
|
||||
async update(u) {
|
||||
await put('/api/product-units/' + u.id, { name: u.name })
|
||||
uni.showToast({ title: '已保存', icon: 'success' })
|
||||
},
|
||||
async remove(u) {
|
||||
uni.showModal({ content: '确定删除该单位?', success: async (r) => {
|
||||
if (!r.confirm) return
|
||||
await del('/api/product-units/' + u.id)
|
||||
this.reload()
|
||||
}})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||
.toolbar { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }
|
||||
.toolbar input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||
.list { flex:1; }
|
||||
.item { display:flex; gap: 12rpx; align-items:center; padding: 16rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||
.item input { flex:1; background:#f7f7f7; border-radius: 10rpx; padding: 12rpx; }
|
||||
.ops { display:flex; gap: 10rpx; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,3 +48,4 @@
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user