2
This commit is contained in:
@@ -5,20 +5,22 @@
|
||||
<text class="title">编辑货品</text>
|
||||
<text class="sub">完善基础信息与价格</text>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view v-if="form.platformStatus==='platform'" class="tip platform">平台推荐货品,建议谨慎修改核心字段</view>
|
||||
<view v-else-if="form.sourceSubmissionId" class="tip custom">此货品源于我的提交,审核通过后已入库</view>
|
||||
<view class="section">
|
||||
<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>
|
||||
<view class="row">
|
||||
<text class="label">条形码</text>
|
||||
<input class="input-long" v-model.trim="form.barcode" placeholder="可扫码或输入" />
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button size="mini" class="picker-btn" @click="chooseAndScanBarcode">图片识码</button>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<button size="mini" class="picker-btn" @click="chooseAndScanBarcode">图片识码</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">品牌/型号/规格/产地</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<input v-model.trim="form.brand" placeholder="品牌" />
|
||||
@@ -42,7 +44,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="section">
|
||||
<view class="row">
|
||||
<text class="label">库存与安全库存</text>
|
||||
</view>
|
||||
@@ -53,7 +55,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="section">
|
||||
<view class="row">
|
||||
<text class="label">价格(进价/零售/批发/大单)</text>
|
||||
</view>
|
||||
@@ -65,17 +67,17 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="section">
|
||||
<text class="label">图片</text>
|
||||
<ImageUploader v-model="form.images" :formData="{ ownerType: 'product' }" />
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="section">
|
||||
<text class="label">备注</text>
|
||||
<textarea v-model.trim="form.remark" placeholder="可选" auto-height />
|
||||
</view>
|
||||
|
||||
<view class="fixed">
|
||||
<view class="fixed" :style="{ bottom: (keyboardHeight || 0) + 'px' }">
|
||||
<button class="ghost" @click="save(false)">保存</button>
|
||||
<button class="primary" @click="save(true)">保存并继续</button>
|
||||
</view>
|
||||
@@ -84,7 +86,7 @@
|
||||
|
||||
<script>
|
||||
import ImageUploader from '../../components/ImageUploader.vue'
|
||||
import { get, post, put } from '../../common/http.js'
|
||||
import { get, post, put, upload } from '../../common/http.js'
|
||||
|
||||
export default {
|
||||
components: { ImageUploader },
|
||||
@@ -96,15 +98,21 @@ export default {
|
||||
categoryId: '', unitId: '',
|
||||
stock: null, safeMin: null, safeMax: null,
|
||||
purchasePrice: null, retailPrice: null, wholesalePrice: null, bigClientPrice: null,
|
||||
images: [], remark: ''
|
||||
images: [], remark: '',
|
||||
platformStatus: '', sourceSubmissionId: ''
|
||||
},
|
||||
units: [],
|
||||
categories: []
|
||||
categories: [],
|
||||
keyboardHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad(query) {
|
||||
this.id = query?.id || ''
|
||||
this.bootstrap()
|
||||
this.initKeyboardListener()
|
||||
},
|
||||
onUnload() {
|
||||
this.disposeKeyboardListener()
|
||||
},
|
||||
computed: {
|
||||
unitNames() { return this.units.map(u => u.name) },
|
||||
@@ -123,6 +131,22 @@ export default {
|
||||
await Promise.all([this.fetchUnits(), this.fetchCategories()])
|
||||
if (this.id) this.loadDetail()
|
||||
},
|
||||
initKeyboardListener() {
|
||||
try {
|
||||
this.__keyboardListener = (e) => {
|
||||
const h = (e && (e.height || e.targetHeight || 0)) || 0
|
||||
this.keyboardHeight = h
|
||||
}
|
||||
uni.onKeyboardHeightChange && uni.onKeyboardHeightChange(this.__keyboardListener)
|
||||
} catch (_) {}
|
||||
},
|
||||
disposeKeyboardListener() {
|
||||
try {
|
||||
if (this.__keyboardListener && uni.offKeyboardHeightChange) {
|
||||
uni.offKeyboardHeightChange(this.__keyboardListener)
|
||||
}
|
||||
} catch (_) {}
|
||||
},
|
||||
async fetchUnits() {
|
||||
try {
|
||||
const res = await get('/api/product-units')
|
||||
@@ -143,11 +167,27 @@ export default {
|
||||
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 chooseAndScanBarcode() {
|
||||
try {
|
||||
const chooseRes = await uni.chooseImage({ count: 1, sourceType: ['camera','album'], sizeType: ['compressed'] })
|
||||
let filePath = chooseRes.tempFilePaths[0]
|
||||
try {
|
||||
const comp = await uni.compressImage({ src: filePath, quality: 80 })
|
||||
filePath = comp.tempFilePath || filePath
|
||||
} catch (e) {}
|
||||
const data = await upload('/api/barcode/scan', filePath, {}, 'file')
|
||||
if (data && data.success && data.barcode) {
|
||||
this.form.barcode = data.barcode
|
||||
uni.showToast({ title: '识别成功', icon: 'success', mask: false })
|
||||
return
|
||||
}
|
||||
const msg = (data && (data.message || data.error || data.msg)) || '未识别'
|
||||
uni.showToast({ title: msg, icon: 'none', mask: false })
|
||||
} catch (e) {
|
||||
const msg = (e && e.message) ? String(e.message) : '网络异常或服务不可用'
|
||||
uni.showToast({ title: msg, icon: 'none', mask: false })
|
||||
}
|
||||
},
|
||||
async loadDetail() {
|
||||
try {
|
||||
const data = await get('/api/products/' + this.id)
|
||||
@@ -159,7 +199,10 @@ export default {
|
||||
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)
|
||||
images: (data.images || []).map(i => i.url || i),
|
||||
remark: data.remark || '',
|
||||
platformStatus: data.platformStatus || '',
|
||||
sourceSubmissionId: data.sourceSubmissionId || ''
|
||||
})
|
||||
} catch (_) {}
|
||||
},
|
||||
@@ -185,19 +228,20 @@ export default {
|
||||
}
|
||||
},
|
||||
async save(goOn) {
|
||||
try { uni.hideKeyboard && uni.hideKeyboard() } catch (_) {}
|
||||
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' })
|
||||
uni.showToast({ title: '保存成功', icon: 'success', mask: false })
|
||||
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: '' }
|
||||
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: '', platformStatus: '', sourceSubmissionId: '' }
|
||||
} else {
|
||||
setTimeout(() => uni.navigateBack(), 400)
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '保存失败', icon: 'none' })
|
||||
uni.showToast({ title: '保存失败', icon: 'none', mask: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,19 +249,28 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page { background:$uni-bg-color; height: 100vh; }
|
||||
.hero.small { margin: 16rpx; padding: 16rpx; background: #ffffff; border: 2rpx solid $uni-border-color; border-radius: 16rpx; }
|
||||
.hero.small .title { font-size: 32rpx; font-weight: 800; color: $uni-text-color; }
|
||||
.hero.small .sub { margin-left: 12rpx; color: $uni-text-color-grey; font-size: 24rpx; }
|
||||
.card { background:#ffffff; margin: 16rpx; padding: 16rpx; border-radius: 16rpx; border: 2rpx solid $uni-border-color; }
|
||||
.row { display:flex; gap: 12rpx; align-items: center; margin-bottom: 12rpx; }
|
||||
.label { width: 180rpx; color:$uni-text-color-grey; }
|
||||
.row input { flex:1; background:$uni-bg-color-hover; border-radius: 12rpx; padding: 14rpx; color:$uni-text-color; border: 2rpx solid $uni-border-color; }
|
||||
.picker { padding: 10rpx 14rpx; background:$uni-bg-color-hover; border-radius: 12rpx; color:$uni-text-color-grey; margin-left: 8rpx; border: 2rpx solid $uni-border-color; }
|
||||
.page { background:$uni-bg-color; min-height: 100vh; padding-bottom: 160rpx; box-sizing: border-box; }
|
||||
.hero.small { margin: 22rpx 24rpx 12rpx; padding: 0 4rpx 18rpx; color: $uni-text-color; border-bottom: 2rpx solid rgba(94,124,174,0.12); }
|
||||
.hero.small .title { font-size: 34rpx; font-weight: 800; }
|
||||
.hero.small .sub { display: block; margin-top: 6rpx; color: $uni-text-color-grey; font-size: 24rpx; }
|
||||
.section { margin: 0 24rpx 28rpx; padding-bottom: 6rpx; border-bottom: 2rpx solid rgba(94,124,174,0.10); }
|
||||
.section:last-of-type { border-bottom: 0; margin-bottom: 0; }
|
||||
.section .row:first-child .label { font-weight: 700; color: $uni-text-color; }
|
||||
.row { display:flex; gap: 8rpx; align-items: center; margin-top: 18rpx; }
|
||||
.row .input-long { flex: 1.2; }
|
||||
.row:first-child { margin-top: 0; }
|
||||
.label { width: 150rpx; color:$uni-text-color-grey; font-size: 26rpx; }
|
||||
.row input { flex:1; background:#f7f9fc; border-radius: 14rpx; padding: 18rpx 20rpx; color:$uni-text-color; border: 0; box-shadow: inset 0 0 0 2rpx rgba(134,155,191,0.06); }
|
||||
.picker-btn { background:#ffffff; border: 2rpx solid rgba($uni-color-primary, .45); color:$uni-color-primary; padding: 0 24rpx; border-radius: 999rpx; font-size: 24rpx; }
|
||||
.picker { padding: 16rpx 22rpx; background:#f7f9fc; border-radius: 14rpx; color:$uni-text-color-grey; margin-left: 8rpx; border: 0; box-shadow: inset 0 0 0 2rpx rgba(134,155,191,0.06); }
|
||||
.prices input { width: 30%; }
|
||||
.fixed { position: fixed; left: 0; right: 0; bottom: 0; background:#ffffff; padding: 12rpx 16rpx; display:flex; gap: 16rpx; border-top: 2rpx solid $uni-border-color; }
|
||||
.section textarea { width: 100%; min-height: 160rpx; background:#f7f9fc; border-radius: 14rpx; padding: 20rpx 22rpx; box-sizing: border-box; color:$uni-text-color; border: 0; box-shadow: inset 0 0 0 2rpx rgba(134,155,191,0.06); }
|
||||
.fixed { position: fixed; left: 0; right: 0; bottom: env(safe-area-inset-bottom); background:#ffffff; padding: 16rpx 16rpx calc(16rpx + constant(safe-area-inset-bottom)) 16rpx; display:flex; gap: 16rpx; box-shadow: 0 -6rpx 18rpx rgba(24,55,105,0.08); z-index: 999; }
|
||||
.fixed .primary { flex:1; background: $uni-color-primary; color:#fff; border-radius: 999rpx; padding: 18rpx 0; font-weight: 700; }
|
||||
.fixed .ghost { flex:1; background:#ffffff; color:$uni-color-primary; border: 2rpx solid rgba($uni-color-primary, .45); border-radius: 999rpx; padding: 18rpx 0; }
|
||||
.tip { margin: 0 30rpx 20rpx; padding: 16rpx 20rpx; border-radius: 16rpx; font-size: 24rpx; }
|
||||
.tip.platform { background: rgba(45,140,240,0.12); color: #2d8cf0; }
|
||||
.tip.custom { background: rgba(103,194,58,0.12); color: #67c23a; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user