This commit is contained in:
2025-09-18 21:17:44 +08:00
parent e560e90970
commit bff3d0414d
49 changed files with 1063 additions and 90 deletions

View File

@@ -12,8 +12,10 @@ function requestWithFallback(options, candidates, idx, resolve, reject) {
uni.request({ ...options, url, success: (res) => {
const { statusCode, data } = res
if (statusCode >= 200 && statusCode < 300) return resolve(data)
const msg = (data && (data.message || data.error || data.msg)) || ('HTTP ' + statusCode)
uni.showToast({ title: msg, icon: 'none' })
if (idx + 1 < candidates.length) return requestWithFallback(options, candidates, idx + 1, resolve, reject)
reject(new Error('HTTP ' + statusCode))
reject(new Error(msg))
}, fail: (err) => {
if (idx + 1 < candidates.length) return requestWithFallback(options, candidates, idx + 1, resolve, reject)
reject(err)

View File

@@ -60,12 +60,24 @@
"navigationBarTitleText": "新增客户"
}
},
{
"path": "pages/customer/detail",
"style": {
"navigationBarTitleText": "客户详情"
}
},
{
"path": "pages/supplier/select",
"style": {
"navigationBarTitleText": "选择供应商"
}
},
{
"path": "pages/supplier/form",
"style": {
"navigationBarTitleText": "新增/编辑供应商"
}
},
{
"path": "pages/account/select",
"style": {

View File

@@ -0,0 +1,86 @@
<template>
<view class="page">
<view class="card">
<view class="row"><text class="label">名称</text><text v-if="!editing" class="value">{{ d.name }}</text><input v-else class="value-input" v-model="form.name" placeholder="必填" /></view>
<view class="row"><text class="label">联系人</text><text v-if="!editing" class="value">{{ d.contactName || '—' }}</text><input v-else class="value-input" v-model="form.contactName" placeholder="可选" /></view>
<view class="row"><text class="label">手机</text><text v-if="!editing" class="value">{{ d.mobile || '—' }}</text><input v-else class="value-input" v-model="form.mobile" placeholder="可选" /></view>
<view class="row"><text class="label">电话</text><text v-if="!editing" class="value">{{ d.phone || '—' }}</text><input v-else class="value-input" v-model="form.phone" placeholder="可选(座机)" /></view>
<view class="row"><text class="label">地址</text><text v-if="!editing" class="value">{{ d.address || '—' }}</text><input v-else class="value-input" v-model="form.address" placeholder="可选" /></view>
<view class="row"><text class="label">等级</text><text v-if="!editing" class="value">{{ d.level || '—' }}</text><input v-else class="value-input" v-model="form.level" placeholder="可选,如 VIP/A/B" /></view>
<view class="row"><text class="label">售价档位</text>
<text v-if="!editing" class="value">{{ d.priceLevel }}</text>
<picker v-else :range="priceLabels" :value="priceIdx" @change="onPriceChange"><view class="value">{{ priceLabels[priceIdx] }}</view></picker>
</view>
<view class="row"><text class="label">初始应收</text><text v-if="!editing" class="value">¥ {{ Number(d.arOpening||0).toFixed(2) }}</text><input v-else class="value-input" type="digit" v-model.number="form.arOpening" placeholder="0.00" /></view>
<view class="row"><text class="label">当前应收</text><text class="value emp">¥ {{ (Number(d.receivable||0)).toFixed(2) }}</text></view>
<view class="row"><text class="label">备注</text><text v-if="!editing" class="value">{{ d.remark || '—' }}</text><input v-else class="value-input" v-model="form.remark" placeholder="—" /></view>
</view>
<view class="bottom">
<button class="ghost" @click="toggleEdit">{{ editing ? '取消' : '编辑' }}</button>
<button class="primary" v-if="editing" @click="save">保存</button>
<button class="primary" v-else @click="choose">选择此客户</button>
</view>
</view>
</template>
<script>
import { get, put } from '../../common/http.js'
export default {
data(){ return { id: null, d: {}, editing: false, form: { name:'', contactName:'', mobile:'', phone:'', address:'', level:'', priceLevel:'零售价', arOpening:0, remark:'' }, priceLevels:['零售价','批发价','大单报价'], priceLabels:['零售价','批发价','大单报价'], priceIdx:0 } },
onLoad(q){ if (q && q.id) { this.id = Number(q.id); this.fetch() } },
methods: {
async fetch(){
try {
this.d = await get(`/api/customers/${this.id}`)
// 初始化表单
this.form = {
name: this.d.name || '', contactName: this.d.contactName || '', mobile: this.d.mobile || '', phone: this.d.phone || '',
address: this.d.address || '', level: this.d.level || '', priceLevel: this.d.priceLevel || 'retail', arOpening: Number(this.d.arOpening||0), remark: this.d.remark || ''
}
const idx = this.priceLevels.indexOf(this.form.priceLevel); this.priceIdx = idx >= 0 ? idx : 0
} catch(e){ uni.showToast({ title:'加载失败', icon:'none' }) }
},
toggleEdit(){ this.editing = !this.editing },
onPriceChange(e){ this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx] },
choose(){
const pages = getCurrentPages()
let targetIdx = -1
for (let i = pages.length - 2; i >= 0; i--) {
const vm = pages[i] && pages[i].$vm ? pages[i].$vm : null
if (vm && vm.order) { vm.order.customerId = this.d.id; vm.customerName = this.d.name; targetIdx = i; break }
}
if (targetIdx >= 0) {
const delta = (pages.length - 1) - targetIdx
uni.navigateBack({ delta })
} else {
uni.navigateBack()
}
},
async save(){
if (!this.form.name) return uni.showToast({ title:'请填写客户名称', icon:'none' })
try {
await put(`/api/customers/${this.id}`, this.form)
uni.showToast({ title:'已保存', icon:'success' })
this.editing = false
await this.fetch()
} catch(e) { uni.showToast({ title: e?.message || '保存失败', icon:'none' }) }
}
}
}
</script>
<style>
.page { padding-bottom: 140rpx; }
.card { background:#fff; margin: 16rpx; padding: 12rpx 16rpx; border-radius: 16rpx; }
.row { display:flex; justify-content: space-between; padding: 18rpx 8rpx; border-bottom: 1rpx solid #f3f3f3; }
.row:last-child { border-bottom: 0; }
.label { color:#666; }
.value { color:#333; max-width: 60%; text-align: right; }
.value-input { color:#333; text-align: right; flex: 1; }
.emp { color:#107e9b; font-weight: 700; }
.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); display:flex; gap: 12rpx; }
.primary { flex:1; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }
.ghost { flex:1; background:#fff; color:#107e9b; border: 2rpx solid #A0E4FF; border-radius: 999rpx; padding: 18rpx 0; }
</style>

View File

@@ -2,12 +2,12 @@
<view class="page">
<view class="field"><text class="label">客户名称</text><input class="value" v-model="form.name" placeholder="必填" /></view>
<view class="field"><text class="label">客户等级</text><input class="value" v-model="form.level" placeholder="可选,如 VIP/A/B" /></view>
<view class="field">
<text class="label">售价档位</text>
<picker :range="priceLevels" :value="priceIdx" @change="onPriceChange">
<view class="value">{{ priceLevels[priceIdx] }}</view>
</picker>
</view>
<view class="field">
<text class="label">售价档位</text>
<picker :range="priceLabels" :value="priceIdx" @change="onPriceChange">
<view class="value">{{ priceLabels[priceIdx] }}</view>
</picker>
</view>
<view class="field"><text class="label">联系人</text><input class="value" v-model="form.contactName" placeholder="可选" /></view>
<view class="field"><text class="label">手机</text><input class="value" v-model="form.mobile" placeholder="可选" /></view>
<view class="field"><text class="label">电话</text><input class="value" v-model="form.phone" placeholder="可选(座机)" /></view>
@@ -26,13 +26,14 @@ export default {
return {
id: null,
form: { name:'', level:'', priceLevel:'retail', contactName:'', mobile:'', phone:'', address:'', arOpening:0, remark:'' },
priceLevels: ['retail','distribution','wholesale','big_client'],
priceLevels: ['零售价','批发价','大单报价'],
priceLabels: ['零售价','批发价','大单报价'],
priceIdx: 0
}
},
onLoad(query) { if (query && query.id) { this.id = Number(query.id) } },
methods: {
onPriceChange(e){ this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx] },
onPriceChange(e){ this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx] },
async save() {
if (!this.form.name) return uni.showToast({ title:'请填写客户名称', icon:'none' })
try {

View File

@@ -6,7 +6,7 @@
<button size="mini" :type="debtOnly ? 'primary' : 'default'" @click="toggleDebtOnly">只看欠款</button>
</view>
<scroll-view scroll-y class="list">
<view class="item" v-for="c in customers" :key="c.id" @click="select(c)">
<view class="item" v-for="c in customers" :key="c.id" @click="openDetail(c)">
<view class="name">{{ c.name }}</view>
<view class="meta">
{{ c.mobile || '—' }}
@@ -25,6 +25,7 @@
export default {
data() { return { kw: '', debtOnly: false, customers: [] } },
onLoad() { this.search() },
onShow() { this.search() },
methods: {
toggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search() },
async search() {
@@ -35,13 +36,17 @@
},
createCustomer() { uni.navigateTo({ url: '/pages/customer/form' }) },
select(c) {
const opener = getCurrentPages()[getCurrentPages().length-2]
if (opener && opener.$vm) {
opener.$vm.order.customerId = c.id
opener.$vm.customerName = c.name
const pages = getCurrentPages()
const prev = pages.length >= 2 ? pages[pages.length - 2] : null
const vm = prev && prev.$vm ? prev.$vm : null
if (vm && vm.order) {
vm.order.customerId = c.id
vm.customerName = c.name
}
uni.navigateBack()
}
,
openDetail(c) { uni.navigateTo({ url: '/pages/customer/detail?id=' + c.id }) }
}
}
</script>

View File

@@ -156,6 +156,10 @@
uni.navigateTo({ url: '/pages/customer/select' })
return
}
if (item.key === 'supplier') {
uni.navigateTo({ url: '/pages/supplier/select' })
return
}
uni.showToast({ title: item.title + '(开发中)', icon: 'none' })
},
goProduct() {

View File

@@ -124,7 +124,7 @@
<input type="number" v-model.number="it.quantity" @input="recalc()" />
</view>
<view class="col price">
<input type="number" v-model.number="it.unitPrice" @input="recalc()" />
<input type="number" v-model.number="it.unitPrice" @input="onPriceInput(it)" />
</view>
<view class="col amount">¥ {{ (Number(it.quantity)*Number(it.unitPrice)).toFixed(2) }}</view>
</view>
@@ -139,7 +139,7 @@
</template>
<script>
import { post } from '../../common/http.js'
import { get, post } from '../../common/http.js'
import { INCOME_CATEGORIES, EXPENSE_CATEGORIES } from '../../common/constants.js'
function todayString() {
@@ -162,6 +162,9 @@
remark: ''
},
customerName: '',
customerPriceLevel: '零售价',
_lastCustomerId: null,
_priceCache: {},
supplierName: '',
items: [],
activeCategory: 'sale_income',
@@ -192,7 +195,46 @@
return Number(p.cash||0) + Number(p.bank||0) + Number(p.wechat||0)
}
},
onShow() {
if (this.biz === 'sale') {
if (this.order.customerId && this.order.customerId !== this._lastCustomerId) {
this.loadCustomerLevel(this.order.customerId).then(() => {
this._lastCustomerId = this.order.customerId
for (const it of this.items) { if (it && (it._autoPrice || !it.unitPrice)) this.autoPriceItem(it) }
})
}
for (const it of this.items) { if (it && !it.unitPrice) this.autoPriceItem(it) }
}
},
methods: {
async loadCustomerLevel(customerId) {
try {
const d = await get(`/api/customers/${customerId}`)
this.customerPriceLevel = d && d.priceLevel ? d.priceLevel : '零售价'
} catch(e) { this.customerPriceLevel = '零售价' }
},
priceFieldForLevel() {
const lvl = this.customerPriceLevel || '零售价'
if (lvl === '批发价') return 'wholesalePrice'
if (lvl === '大单报价') return 'bigClientPrice'
return 'retailPrice'
},
async autoPriceItem(it) {
if (this.biz !== 'sale') return
if (!it || !it.productId) return
const pid = it.productId
let detail = this._priceCache[pid]
if (!detail) {
try { detail = await get(`/api/products/${pid}`); this._priceCache[pid] = detail } catch(e) { return }
}
const field = this.priceFieldForLevel()
let price = Number(detail && detail[field] != null ? detail[field] : 0)
if (!price && field !== 'retailPrice') { price = Number(detail && detail.retailPrice != null ? detail.retailPrice : 0) }
it.unitPrice = price
it._autoPrice = true
this.recalc()
},
onPriceInput(it) { if (it) { it._autoPrice = false; this.recalc() } },
switchBiz(type) { this.biz = type },
onDateChange(e) { this.order.orderTime = e.detail.value },
chooseCustomer() {
@@ -214,6 +256,12 @@
const isSaleOrPurchase = (this.biz==='sale' || this.biz==='purchase')
const isCollectOrPay = (this.biz==='sale' && this.saleType==='collect') || (this.biz==='purchase' && this.purchaseType==='pay')
const saleTypeValue = this.biz==='sale' ? ('sale.' + this.saleType) : ('purchase.' + this.purchaseType)
// 前置校验:销售/进货 出入库要求有明细
if (isSaleOrPurchase && !isCollectOrPay) {
if (!this.items.length) { uni.showToast({ title: '请先选择商品', icon: 'none' }); return }
const invalid = this.items.find(it => !it.productId || Number(it.quantity||0) <= 0)
if (invalid) { uni.showToast({ title: '数量需大于0', icon: 'none' }); return }
}
const payload = isSaleOrPurchase ? (isCollectOrPay ? [
{ method: 'cash', amount: Number(this.payments.cash||0) },
{ method: 'bank', amount: Number(this.payments.bank||0) },

View File

@@ -28,7 +28,8 @@
select(p) {
const opener = getCurrentPages()[getCurrentPages().length-2]
if (opener && opener.$vm && opener.$vm.items) {
opener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: Number(p.price || 0) })
const initPrice = Number((p.retailPrice != null ? p.retailPrice : (p.price || 0)))
opener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: initPrice, _autoPrice: true })
}
uni.navigateBack()
}

View File

@@ -0,0 +1,50 @@
<template>
<view class="page">
<view class="field"><text class="label">供应商名称</text><input class="value" v-model="form.name" placeholder="必填" /></view>
<view class="field"><text class="label">联系人</text><input class="value" v-model="form.contactName" placeholder="可选" /></view>
<view class="field"><text class="label">手机</text><input class="value" v-model="form.mobile" placeholder="可选" /></view>
<view class="field"><text class="label">电话</text><input class="value" v-model="form.phone" placeholder="可选(座机)" /></view>
<view class="field"><text class="label">经营地址</text><input class="value" v-model="form.address" placeholder="可选" /></view>
<view class="field"><text class="label">初始应付款</text><input class="value" type="digit" v-model.number="form.apOpening" placeholder="默认 0.00" /></view>
<view class="field"><text class="label">应付款</text><input class="value" type="digit" v-model.number="form.apPayable" placeholder="默认 0.00" /></view>
<view class="textarea"><textarea v-model="form.remark" maxlength="200" placeholder="备注最多200字"></textarea></view>
<view class="bottom"><button class="primary" @click="save">保存</button></view>
</view>
</template>
<script>
import { post, put } from '../../common/http.js'
export default {
data() {
return {
id: null,
form: { name:'', contactName:'', mobile:'', phone:'', address:'', apOpening:0, apPayable:0, remark:'' }
}
},
onLoad(query) { if (query && query.id) { this.id = Number(query.id) } },
methods: {
async save() {
if (!this.form.name) return uni.showToast({ title:'请填写供应商名称', icon:'none' })
try {
if (this.id) await put(`/api/suppliers/${this.id}`, this.form)
else await post('/api/suppliers', this.form)
uni.showToast({ title:'保存成功', icon:'success' })
setTimeout(() => uni.navigateBack(), 500)
} catch(e) { uni.showToast({ title: e?.message || '保存失败', icon:'none' }) }
}
}
}
</script>
<style>
.page { padding-bottom: 140rpx; }
.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee; }
.label { color:#666; }
.value { color:#333; text-align: right; flex: 1; }
.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx; }
.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); }
.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }
</style>

View File

@@ -3,28 +3,37 @@
<view class="search">
<input v-model="kw" placeholder="搜索供应商名称/电话" @confirm="search" />
<button size="mini" @click="search">搜索</button>
<button size="mini" :type="debtOnly ? 'primary' : 'default'" @click="toggleDebtOnly">只看欠款</button>
</view>
<scroll-view scroll-y class="list">
<view class="item" v-for="s in suppliers" :key="s.id" @click="select(s)">
<view class="name">{{ s.name }}</view>
<view class="meta">{{ s.mobile || '—' }}</view>
<view class="meta">
{{ s.mobile || '—' }}
<text v-if="typeof s.apPayable === 'number'">应付¥ {{ Number(s.apPayable).toFixed(2) }}</text>
</view>
</view>
</scroll-view>
<view class="bottom">
<button class="primary" @click="createSupplier">新增供应商</button>
</view>
</view>
</template>
<script>
import { get } from '../../common/http.js'
export default {
data() { return { kw: '', suppliers: [] } },
data() { return { kw: '', debtOnly: false, suppliers: [] } },
onLoad() { this.search() },
methods: {
toggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search() },
async search() {
try {
const res = await get('/api/suppliers', { kw: this.kw, page: 1, size: 50 })
const res = await get('/api/suppliers', { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 })
this.suppliers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }
},
createSupplier() { uni.navigateTo({ url: '/pages/supplier/form' }) },
select(s) {
const opener = getCurrentPages()[getCurrentPages().length-2]
if (opener && opener.$vm) {

View File

@@ -1 +1 @@
{"version":3,"file":"app.js","sources":["App.vue","main.js"],"sourcesContent":["<script>\r\n\texport default {\r\n\t\tonLaunch: function() {\r\n\t\t\tconsole.log('App Launch')\r\n\t\t},\r\n\t\tonShow: function() {\r\n\t\t\tconsole.log('App Show')\r\n\t\t},\r\n\t\tonHide: function() {\r\n\t\t\tconsole.log('App Hide')\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t/*每个页面公共css */\r\n</style>\r\n","import App from './App'\r\n\r\n// #ifndef VUE3\r\nimport Vue from 'vue'\r\nimport './uni.promisify.adaptor'\r\nVue.config.productionTip = false\r\nApp.mpType = 'app'\r\nconst app = new Vue({\r\n ...App\r\n})\r\napp.$mount()\r\n// #endif\r\n\r\n// #ifdef VUE3\r\nimport { createSSRApp } from 'vue'\r\nexport function createApp() {\r\n const app = createSSRApp(App)\r\n return {\r\n app\r\n }\r\n}\r\n// #endif\r\n\r\n// 规范化 WebSocket 关闭码(仅微信小程序)\r\n// #ifdef MP-WEIXIN\r\nif (typeof uni !== 'undefined' && typeof uni.connectSocket === 'function') {\r\n const _connectSocket = uni.connectSocket\r\n uni.connectSocket = function(options) {\r\n const task = _connectSocket.call(this, options)\r\n if (task && typeof task.close === 'function') {\r\n const _close = task.close\r\n task.close = function(params = {}) {\r\n if (params && typeof params === 'object') {\r\n const codeNum = Number(params.code)\r\n const isValid = codeNum === 1000 || (codeNum >= 3000 && codeNum <= 4999)\r\n if (!isValid) {\r\n params.code = 1000\r\n if (!params.reason) params.reason = 'normalized from invalid close code'\r\n }\r\n }\r\n return _close.call(this, params)\r\n }\r\n }\r\n return task\r\n }\r\n}\r\n// #endif"],"names":["uni","createSSRApp","App"],"mappings":";;;;;;;;;;;;;;;;;;AACC,MAAK,YAAU;AAAA,EACd,UAAU,WAAW;AACpBA,kBAAAA,MAAA,MAAA,OAAA,gBAAY,YAAY;AAAA,EACxB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,gBAAA,UAAU;AAAA,EACtB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,iBAAA,UAAU;AAAA,EACvB;AACD;ACIM,SAAS,YAAY;AAC1B,QAAM,MAAMC,cAAY,aAACC,SAAG;AAC5B,SAAO;AAAA,IACL;AAAA,EACD;AACH;AAKA,IAAI,OAAOF,cAAG,UAAK,eAAe,OAAOA,cAAAA,MAAI,kBAAkB,YAAY;AACzE,QAAM,iBAAiBA,cAAAA,MAAI;AAC3BA,sBAAI,gBAAgB,SAAS,SAAS;AACpC,UAAM,OAAO,eAAe,KAAK,MAAM,OAAO;AAC9C,QAAI,QAAQ,OAAO,KAAK,UAAU,YAAY;AAC5C,YAAM,SAAS,KAAK;AACpB,WAAK,QAAQ,SAAS,SAAS,IAAI;AACjC,YAAI,UAAU,OAAO,WAAW,UAAU;AACxC,gBAAM,UAAU,OAAO,OAAO,IAAI;AAClC,gBAAM,UAAU,YAAY,OAAS,WAAW,OAAQ,WAAW;AACnE,cAAI,CAAC,SAAS;AACZ,mBAAO,OAAO;AACd,gBAAI,CAAC,OAAO;AAAQ,qBAAO,SAAS;AAAA,UACrC;AAAA,QACF;AACD,eAAO,OAAO,KAAK,MAAM,MAAM;AAAA,MAChC;AAAA,IACF;AACD,WAAO;AAAA,EACR;AACH;;;"}
{"version":3,"file":"app.js","sources":["App.vue","main.js"],"sourcesContent":["<script>\r\n\texport default {\r\n\t\tonLaunch: function() {\r\n\t\t\tconsole.log('App Launch')\r\n\t\t},\r\n\t\tonShow: function() {\r\n\t\t\tconsole.log('App Show')\r\n\t\t},\r\n\t\tonHide: function() {\r\n\t\t\tconsole.log('App Hide')\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t/*每个页面公共css */\r\n</style>\r\n","import App from './App'\r\n\r\n// #ifndef VUE3\r\nimport Vue from 'vue'\r\nimport './uni.promisify.adaptor'\r\nVue.config.productionTip = false\r\nApp.mpType = 'app'\r\nconst app = new Vue({\r\n ...App\r\n})\r\napp.$mount()\r\n// #endif\r\n\r\n// #ifdef VUE3\r\nimport { createSSRApp } from 'vue'\r\nexport function createApp() {\r\n const app = createSSRApp(App)\r\n return {\r\n app\r\n }\r\n}\r\n// #endif\r\n\r\n// 规范化 WebSocket 关闭码(仅微信小程序)\r\n// #ifdef MP-WEIXIN\r\nif (typeof uni !== 'undefined' && typeof uni.connectSocket === 'function') {\r\n const _connectSocket = uni.connectSocket\r\n uni.connectSocket = function(options) {\r\n const task = _connectSocket.call(this, options)\r\n if (task && typeof task.close === 'function') {\r\n const _close = task.close\r\n task.close = function(params = {}) {\r\n if (params && typeof params === 'object') {\r\n const codeNum = Number(params.code)\r\n const isValid = codeNum === 1000 || (codeNum >= 3000 && codeNum <= 4999)\r\n if (!isValid) {\r\n params.code = 1000\r\n if (!params.reason) params.reason = 'normalized from invalid close code'\r\n }\r\n }\r\n return _close.call(this, params)\r\n }\r\n }\r\n return task\r\n }\r\n}\r\n// #endif"],"names":["uni","createSSRApp","App"],"mappings":";;;;;;;;;;;;;;;;;;;;AACC,MAAK,YAAU;AAAA,EACd,UAAU,WAAW;AACpBA,kBAAAA,MAAA,MAAA,OAAA,gBAAY,YAAY;AAAA,EACxB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,gBAAA,UAAU;AAAA,EACtB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,iBAAA,UAAU;AAAA,EACvB;AACD;ACIM,SAAS,YAAY;AAC1B,QAAM,MAAMC,cAAY,aAACC,SAAG;AAC5B,SAAO;AAAA,IACL;AAAA,EACD;AACH;AAKA,IAAI,OAAOF,cAAG,UAAK,eAAe,OAAOA,cAAAA,MAAI,kBAAkB,YAAY;AACzE,QAAM,iBAAiBA,cAAAA,MAAI;AAC3BA,sBAAI,gBAAgB,SAAS,SAAS;AACpC,UAAM,OAAO,eAAe,KAAK,MAAM,OAAO;AAC9C,QAAI,QAAQ,OAAO,KAAK,UAAU,YAAY;AAC5C,YAAM,SAAS,KAAK;AACpB,WAAK,QAAQ,SAAS,SAAS,IAAI;AACjC,YAAI,UAAU,OAAO,WAAW,UAAU;AACxC,gBAAM,UAAU,OAAO,OAAO,IAAI;AAClC,gBAAM,UAAU,YAAY,OAAS,WAAW,OAAQ,WAAW;AACnE,cAAI,CAAC,SAAS;AACZ,mBAAO,OAAO;AACd,gBAAI,CAAC,OAAO;AAAQ,qBAAO,SAAS;AAAA,UACrC;AAAA,QACF;AACD,eAAO,OAAO,KAAK,MAAM,MAAM;AAAA,MAChC;AAAA,IACF;AACD,WAAO;AAAA,EACR;AACH;;;"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"form.js","sources":["pages/customer/form.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvY3VzdG9tZXIvZm9ybS52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"field\"><text class=\"label\">客户名称</text><input class=\"value\" v-model=\"form.name\" placeholder=\"必填\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">客户等级</text><input class=\"value\" v-model=\"form.level\" placeholder=\"可选,如 VIP/A/B\" /></view>\r\n\t\t<view class=\"field\">\r\n\t\t\t<text class=\"label\">售价档位</text>\r\n\t\t\t<picker :range=\"priceLevels\" :value=\"priceIdx\" @change=\"onPriceChange\">\r\n\t\t\t\t<view class=\"value\">{{ priceLevels[priceIdx] }}</view>\r\n\t\t\t</picker>\r\n\t\t</view>\r\n\t\t<view class=\"field\"><text class=\"label\">联系人</text><input class=\"value\" v-model=\"form.contactName\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">手机</text><input class=\"value\" v-model=\"form.mobile\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">电话</text><input class=\"value\" v-model=\"form.phone\" placeholder=\"可选(座机)\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">送货地址</text><input class=\"value\" v-model=\"form.address\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">初始应收</text><input class=\"value\" type=\"digit\" v-model.number=\"form.arOpening\" placeholder=\"默认 0.00\" /></view>\r\n\t\t<view class=\"textarea\"><textarea v-model=\"form.remark\" maxlength=\"200\" placeholder=\"备注最多200字\"></textarea></view>\r\n\r\n\t\t<view class=\"bottom\"><button class=\"primary\" @click=\"save\">保存</button></view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { post, put } from '../../common/http.js'\r\nexport default {\r\n\tdata() {\r\n\t\treturn {\r\n\t\t\tid: null,\r\n\t\t\tform: { name:'', level:'', priceLevel:'retail', contactName:'', mobile:'', phone:'', address:'', arOpening:0, remark:'' },\r\n\t\t\tpriceLevels: ['retail','distribution','wholesale','big_client'],\r\n\t\t\tpriceIdx: 0\r\n\t\t}\r\n\t},\r\n\tonLoad(query) { if (query && query.id) { this.id = Number(query.id) } },\r\n\tmethods: {\r\n\t\tonPriceChange(e){ this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx] },\r\n\t\tasync save() {\r\n\t\t\tif (!this.form.name) return uni.showToast({ title:'请填写客户名称', icon:'none' })\r\n\t\t\ttry {\r\n\t\t\t\tif (this.id) await put(`/api/customers/${this.id}`, this.form)\r\n\t\t\t\telse await post('/api/customers', this.form)\r\n\t\t\t\tuni.showToast({ title:'保存成功', icon:'success' })\r\n\t\t\t\tsetTimeout(() => uni.navigateBack(), 500)\r\n\t\t\t} catch(e) { uni.showToast({ title: e?.message || '保存失败', icon:'none' }) }\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { padding-bottom: 140rpx; }\r\n.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee; }\r\n.label { color:#666; }\r\n.value { color:#333; text-align: right; flex: 1; }\r\n.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx; }\r\n.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); }\r\n.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }\r\n</style>\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/form.vue'\nwx.createPage(MiniProgramPage)"],"names":["uni","put","post"],"mappings":";;;AAuBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO;AAAA,MACN,IAAI;AAAA,MACJ,MAAM,EAAE,MAAK,IAAI,OAAM,IAAI,YAAW,UAAU,aAAY,IAAI,QAAO,IAAI,OAAM,IAAI,SAAQ,IAAI,WAAU,GAAG,QAAO,GAAI;AAAA,MACzH,aAAa,CAAC,UAAS,gBAAe,aAAY,YAAY;AAAA,MAC9D,UAAU;AAAA,IACX;AAAA,EACA;AAAA,EACD,OAAO,OAAO;AAAE,QAAI,SAAS,MAAM,IAAI;AAAE,WAAK,KAAK,OAAO,MAAM,EAAE;AAAA,IAAE;AAAA,EAAG;AAAA,EACvE,SAAS;AAAA,IACR,cAAc,GAAE;AAAE,WAAK,WAAW,OAAO,EAAE,OAAO,KAAK;AAAG,WAAK,KAAK,aAAa,KAAK,YAAY,KAAK,QAAQ;AAAA,IAAG;AAAA,IAClH,MAAM,OAAO;AACZ,UAAI,CAAC,KAAK,KAAK;AAAM,eAAOA,cAAG,MAAC,UAAU,EAAE,OAAM,WAAW,MAAK,QAAQ;AAC1E,UAAI;AACH,YAAI,KAAK;AAAI,gBAAMC,YAAAA,IAAI,kBAAkB,KAAK,EAAE,IAAI,KAAK,IAAI;AAAA;AACxD,gBAAMC,iBAAK,kBAAkB,KAAK,IAAI;AAC3CF,sBAAG,MAAC,UAAU,EAAE,OAAM,QAAQ,MAAK,WAAW;AAC9C,mBAAW,MAAMA,cAAAA,MAAI,aAAY,GAAI,GAAG;AAAA,MACzC,SAAQ,GAAG;AAAEA,sBAAG,MAAC,UAAU,EAAE,QAAO,uBAAG,YAAW,QAAQ,MAAK,OAAQ,CAAA;AAAA,MAAE;AAAA,IAC1E;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5CA,GAAG,WAAW,eAAe;"}
{"version":3,"file":"form.js","sources":["pages/customer/form.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvY3VzdG9tZXIvZm9ybS52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"field\"><text class=\"label\">客户名称</text><input class=\"value\" v-model=\"form.name\" placeholder=\"必填\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">客户等级</text><input class=\"value\" v-model=\"form.level\" placeholder=\"可选,如 VIP/A/B\" /></view>\r\n <view class=\"field\">\r\n <text class=\"label\">售价档位</text>\r\n <picker :range=\"priceLabels\" :value=\"priceIdx\" @change=\"onPriceChange\">\r\n <view class=\"value\">{{ priceLabels[priceIdx] }}</view>\r\n </picker>\r\n </view>\r\n\t\t<view class=\"field\"><text class=\"label\">联系人</text><input class=\"value\" v-model=\"form.contactName\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">手机</text><input class=\"value\" v-model=\"form.mobile\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">电话</text><input class=\"value\" v-model=\"form.phone\" placeholder=\"可选(座机)\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">送货地址</text><input class=\"value\" v-model=\"form.address\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">初始应收</text><input class=\"value\" type=\"digit\" v-model.number=\"form.arOpening\" placeholder=\"默认 0.00\" /></view>\r\n\t\t<view class=\"textarea\"><textarea v-model=\"form.remark\" maxlength=\"200\" placeholder=\"备注最多200字\"></textarea></view>\r\n\r\n\t\t<view class=\"bottom\"><button class=\"primary\" @click=\"save\">保存</button></view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { post, put } from '../../common/http.js'\r\nexport default {\r\n\tdata() {\r\n\t\treturn {\r\n\t\t\tid: null,\r\n\t\t\tform: { name:'', level:'', priceLevel:'retail', contactName:'', mobile:'', phone:'', address:'', arOpening:0, remark:'' },\r\n priceLevels: ['零售价','批发价','大单报价'],\r\n priceLabels: ['零售价','批发价','大单报价'],\r\n\t\t\tpriceIdx: 0\r\n\t\t}\r\n\t},\r\n\tonLoad(query) { if (query && query.id) { this.id = Number(query.id) } },\r\n\tmethods: {\r\n onPriceChange(e){ this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx] },\r\n\t\tasync save() {\r\n\t\t\tif (!this.form.name) return uni.showToast({ title:'请填写客户名称', icon:'none' })\r\n\t\t\ttry {\r\n\t\t\t\tif (this.id) await put(`/api/customers/${this.id}`, this.form)\r\n\t\t\t\telse await post('/api/customers', this.form)\r\n\t\t\t\tuni.showToast({ title:'保存成功', icon:'success' })\r\n\t\t\t\tsetTimeout(() => uni.navigateBack(), 500)\r\n\t\t\t} catch(e) { uni.showToast({ title: e?.message || '保存失败', icon:'none' }) }\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { padding-bottom: 140rpx; }\r\n.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee; }\r\n.label { color:#666; }\r\n.value { color:#333; text-align: right; flex: 1; }\r\n.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx; }\r\n.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); }\r\n.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }\r\n</style>\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/form.vue'\nwx.createPage(MiniProgramPage)"],"names":["uni","put","post"],"mappings":";;;AAuBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO;AAAA,MACN,IAAI;AAAA,MACJ,MAAM,EAAE,MAAK,IAAI,OAAM,IAAI,YAAW,UAAU,aAAY,IAAI,QAAO,IAAI,OAAM,IAAI,SAAQ,IAAI,WAAU,GAAG,QAAO,GAAI;AAAA,MACtH,aAAa,CAAC,OAAM,OAAM,MAAM;AAAA,MAChC,aAAa,CAAC,OAAM,OAAM,MAAM;AAAA,MACnC,UAAU;AAAA,IACX;AAAA,EACA;AAAA,EACD,OAAO,OAAO;AAAE,QAAI,SAAS,MAAM,IAAI;AAAE,WAAK,KAAK,OAAO,MAAM,EAAE;AAAA,IAAE;AAAA,EAAG;AAAA,EACvE,SAAS;AAAA,IACN,cAAc,GAAE;AAAE,WAAK,WAAW,OAAO,EAAE,OAAO,KAAK;AAAG,WAAK,KAAK,aAAa,KAAK,YAAY,KAAK,QAAQ;AAAA,IAAG;AAAA,IACpH,MAAM,OAAO;AACZ,UAAI,CAAC,KAAK,KAAK;AAAM,eAAOA,cAAG,MAAC,UAAU,EAAE,OAAM,WAAW,MAAK,QAAQ;AAC1E,UAAI;AACH,YAAI,KAAK;AAAI,gBAAMC,YAAAA,IAAI,kBAAkB,KAAK,EAAE,IAAI,KAAK,IAAI;AAAA;AACxD,gBAAMC,iBAAK,kBAAkB,KAAK,IAAI;AAC3CF,sBAAG,MAAC,UAAU,EAAE,OAAM,QAAQ,MAAK,WAAW;AAC9C,mBAAW,MAAMA,cAAAA,MAAI,aAAY,GAAI,GAAG;AAAA,MACzC,SAAQ,GAAG;AAAEA,sBAAG,MAAC,UAAU,EAAE,QAAO,uBAAG,YAAW,QAAQ,MAAK,OAAQ,CAAA;AAAA,MAAE;AAAA,IAC1E;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7CA,GAAG,WAAW,eAAe;"}

View File

@@ -1 +1 @@
{"version":3,"file":"select.js","sources":["pages/customer/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvY3VzdG9tZXIvc2VsZWN0LnZ1ZQ"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"search\">\r\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索客户名称/电话\" @confirm=\"search\" />\r\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\r\n\t\t\t<button size=\"mini\" :type=\"debtOnly ? 'primary' : 'default'\" @click=\"toggleDebtOnly\">只看欠款</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"c in customers\" :key=\"c.id\" @click=\"select(c)\">\r\n\t\t\t\t<view class=\"name\">{{ c.name }}</view>\r\n\t\t\t\t<view class=\"meta\">\r\n\t\t\t\t\t{{ c.mobile || '—' }}\r\n\t\t\t\t\t<text v-if=\"typeof c.receivable === 'number'\">|应收:¥ {{ Number(c.receivable).toFixed(2) }}</text>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t\t<view class=\"bottom\">\r\n\t\t\t<button class=\"primary\" @click=\"createCustomer\">新增客户</button>\r\n\t\t</view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\n\timport { get } from '../../common/http.js'\r\n\texport default {\r\n\t\tdata() { return { kw: '', debtOnly: false, customers: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\ttoggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search() },\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/customers', { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 })\r\n\t\t\t\t\tthis.customers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t\t},\r\n\t\t\tcreateCustomer() { uni.navigateTo({ url: '/pages/customer/form' }) },\r\n\t\t\tselect(c) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm) {\r\n\t\t\t\t\topener.$vm.order.customerId = c.id\r\n\t\t\t\t\topener.$vm.customerName = c.name\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t.page { display:flex; flex-direction: column; height: 100vh; }\r\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; align-items:center; }\r\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n\t.list { flex:1; }\r\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n\t.name { color:#333; margin-bottom: 6rpx; }\r\n\t.meta { color:#888; font-size: 24rpx; }\r\n\t.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); }\r\n\t.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAwBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,OAAO,WAAW,CAAA;EAAM;AAAA,EAC5D,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,iBAAiB;AAAE,WAAK,WAAW,CAAC,KAAK;AAAU,WAAK;IAAU;AAAA,IAClE,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,kBAAkB,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,UAAU,MAAM,GAAG,MAAM,IAAI;AACnG,aAAK,YAAY,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC5E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,iBAAiB;AAAEA,oBAAAA,MAAI,WAAW,EAAE,KAAK,uBAAqB,CAAG;AAAA,IAAG;AAAA,IACpE,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,KAAK;AACzB,eAAO,IAAI,MAAM,aAAa,EAAE;AAChC,eAAO,IAAI,eAAe,EAAE;AAAA,MAC7B;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;AC5CD,GAAG,WAAW,eAAe;"}
{"version":3,"file":"select.js","sources":["pages/customer/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvY3VzdG9tZXIvc2VsZWN0LnZ1ZQ"],"sourcesContent":["<template>\n\t<view class=\"page\">\n\t\t<view class=\"search\">\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索客户名称/电话\" @confirm=\"search\" />\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\n\t\t\t<button size=\"mini\" :type=\"debtOnly ? 'primary' : 'default'\" @click=\"toggleDebtOnly\">只看欠款</button>\n\t\t</view>\n\t\t<scroll-view scroll-y class=\"list\">\n\t\t\t<view class=\"item\" v-for=\"c in customers\" :key=\"c.id\" @click=\"openDetail(c)\">\n\t\t\t\t<view class=\"name\">{{ c.name }}</view>\n\t\t\t\t<view class=\"meta\">\n\t\t\t\t\t{{ c.mobile || '—' }}\n\t\t\t\t\t<text v-if=\"typeof c.receivable === 'number'\">|应收:¥ {{ Number(c.receivable).toFixed(2) }}</text>\n\t\t\t\t</view>\n\t\t\t</view>\n\t\t</scroll-view>\n\t\t<view class=\"bottom\">\n\t\t\t<button class=\"primary\" @click=\"createCustomer\">新增客户</button>\n\t\t</view>\n\t</view>\n</template>\n\n<script>\n\timport { get } from '../../common/http.js'\n\texport default {\n\t\tdata() { return { kw: '', debtOnly: false, customers: [] } },\n\t\tonLoad() { this.search() },\n\t\tonShow() { this.search() },\n\t\tmethods: {\n\t\t\ttoggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search() },\n\t\t\tasync search() {\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await get('/api/customers', { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 })\n\t\t\t\t\tthis.customers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\n\t\t\t},\n\t\t\tcreateCustomer() { uni.navigateTo({ url: '/pages/customer/form' }) },\n\t\t\tselect(c) {\n\t\t\t\tconst pages = getCurrentPages()\n\t\t\t\tconst prev = pages.length >= 2 ? pages[pages.length - 2] : null\n\t\t\t\tconst vm = prev && prev.$vm ? prev.$vm : null\n\t\t\t\tif (vm && vm.order) {\n\t\t\t\t\tvm.order.customerId = c.id\n\t\t\t\t\tvm.customerName = c.name\n\t\t\t\t}\n\t\t\t\tuni.navigateBack()\n\t\t\t}\n\t\t\t,\n\t\t\topenDetail(c) { uni.navigateTo({ url: '/pages/customer/detail?id=' + c.id }) }\n\t\t}\n\t}\n</script>\n\n<style>\n\t.page { display:flex; flex-direction: column; height: 100vh; }\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; align-items:center; }\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\n\t.list { flex:1; }\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\n\t.name { color:#333; margin-bottom: 6rpx; }\n\t.meta { color:#888; font-size: 24rpx; }\n\t.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); }\n\t.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }\n</style>\n\n\n\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAwBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,OAAO,WAAW,CAAA;EAAM;AAAA,EAC5D,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,iBAAiB;AAAE,WAAK,WAAW,CAAC,KAAK;AAAU,WAAK;IAAU;AAAA,IAClE,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,kBAAkB,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,UAAU,MAAM,GAAG,MAAM,IAAI;AACnG,aAAK,YAAY,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC5E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,iBAAiB;AAAEA,oBAAAA,MAAI,WAAW,EAAE,KAAK,uBAAqB,CAAG;AAAA,IAAG;AAAA,IACpE,OAAO,GAAG;AACT,YAAM,QAAQ,gBAAgB;AAC9B,YAAM,OAAO,MAAM,UAAU,IAAI,MAAM,MAAM,SAAS,CAAC,IAAI;AAC3D,YAAM,KAAK,QAAQ,KAAK,MAAM,KAAK,MAAM;AACzC,UAAI,MAAM,GAAG,OAAO;AACnB,WAAG,MAAM,aAAa,EAAE;AACxB,WAAG,eAAe,EAAE;AAAA,MACrB;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,IAEA,WAAW,GAAG;AAAEA,oBAAAA,MAAI,WAAW,EAAE,KAAK,+BAA+B,EAAE,GAAC,CAAG;AAAA,IAAE;AAAA,EAC9E;AACD;;;;;;;;;;;;;;;;;;;;;;;;;ACjDD,GAAG,WAAW,eAAe;"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"select.js","sources":["pages/product/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9zZWxlY3QudnVl"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"search\">\r\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索商品名称/编码\" @confirm=\"search\" />\r\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"p in products\" :key=\"p.id\" @click=\"select(p)\">\r\n\t\t\t\t<view class=\"name\">{{ p.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ (p.brand||'') + ' ' + (p.model||'') + ' ' + (p.spec||'') }} · 库存:{{ p.stock ?? 0 }}</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\n\timport { get } from '../../common/http.js'\r\n\texport default {\r\n\t\tdata() { return { kw: '', products: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/products', { kw: this.kw, page: 1, size: 50 })\r\n\t\t\t\t\tthis.products = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t\t},\r\n\t\t\tselect(p) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm && opener.$vm.items) {\r\n\t\t\t\t\topener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: Number(p.price || 0) })\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t.page { display:flex; flex-direction: column; height: 100vh; }\r\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n\t.list { flex:1; }\r\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n\t.name { color:#333; margin-bottom: 6rpx; }\r\n\t.meta { color:#888; font-size: 24rpx; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAiBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,CAAA;EAAM;AAAA,EAC1C,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,gBAAI,iBAAiB,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG,MAAM,GAAC,CAAG;AACzE,aAAK,WAAW,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC3E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,OAAO,OAAO,IAAI,OAAO;AAC7C,eAAO,IAAI,MAAM,KAAK,EAAE,WAAW,EAAE,IAAI,aAAa,EAAE,MAAM,UAAU,GAAG,WAAW,OAAO,EAAE,SAAS,CAAC,GAAG;AAAA,MAC7G;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;AClCD,GAAG,WAAW,eAAe;"}
{"version":3,"file":"select.js","sources":["pages/product/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9zZWxlY3QudnVl"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"search\">\r\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索商品名称/编码\" @confirm=\"search\" />\r\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"p in products\" :key=\"p.id\" @click=\"select(p)\">\r\n\t\t\t\t<view class=\"name\">{{ p.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ (p.brand||'') + ' ' + (p.model||'') + ' ' + (p.spec||'') }} · 库存:{{ p.stock ?? 0 }}</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\n\timport { get } from '../../common/http.js'\r\n\texport default {\r\n\t\tdata() { return { kw: '', products: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/products', { kw: this.kw, page: 1, size: 50 })\r\n\t\t\t\t\tthis.products = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t\t},\r\n\t\t\tselect(p) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm && opener.$vm.items) {\r\n\t\t\t\t\tconst initPrice = Number((p.retailPrice != null ? p.retailPrice : (p.price || 0)))\r\n\t\t\t\t\topener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: initPrice, _autoPrice: true })\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t.page { display:flex; flex-direction: column; height: 100vh; }\r\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n\t.list { flex:1; }\r\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n\t.name { color:#333; margin-bottom: 6rpx; }\r\n\t.meta { color:#888; font-size: 24rpx; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAiBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,CAAA;EAAM;AAAA,EAC1C,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,gBAAI,iBAAiB,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG,MAAM,GAAC,CAAG;AACzE,aAAK,WAAW,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC3E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,OAAO,OAAO,IAAI,OAAO;AAC7C,cAAM,YAAY,OAAQ,EAAE,eAAe,OAAO,EAAE,cAAe,EAAE,SAAS,CAAG;AACjF,eAAO,IAAI,MAAM,KAAK,EAAE,WAAW,EAAE,IAAI,aAAa,EAAE,MAAM,UAAU,GAAG,WAAW,WAAW,YAAY,MAAM;AAAA,MACpH;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;ACnCD,GAAG,WAAW,eAAe;"}

View File

@@ -0,0 +1 @@
{"version":3,"file":"form.js","sources":["pages/supplier/form.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvc3VwcGxpZXIvZm9ybS52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"field\"><text class=\"label\">供应商名称</text><input class=\"value\" v-model=\"form.name\" placeholder=\"必填\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">联系人</text><input class=\"value\" v-model=\"form.contactName\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">手机</text><input class=\"value\" v-model=\"form.mobile\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">电话</text><input class=\"value\" v-model=\"form.phone\" placeholder=\"可选(座机)\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">经营地址</text><input class=\"value\" v-model=\"form.address\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">初始应付款</text><input class=\"value\" type=\"digit\" v-model.number=\"form.apOpening\" placeholder=\"默认 0.00\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">应付款</text><input class=\"value\" type=\"digit\" v-model.number=\"form.apPayable\" placeholder=\"默认 0.00\" /></view>\r\n\t\t<view class=\"textarea\"><textarea v-model=\"form.remark\" maxlength=\"200\" placeholder=\"备注最多200字\"></textarea></view>\r\n\r\n\t\t<view class=\"bottom\"><button class=\"primary\" @click=\"save\">保存</button></view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { post, put } from '../../common/http.js'\r\nexport default {\r\n\tdata() {\r\n\t\treturn {\r\n\t\t\tid: null,\r\n\t\t\tform: { name:'', contactName:'', mobile:'', phone:'', address:'', apOpening:0, apPayable:0, remark:'' }\r\n\t\t}\r\n\t},\r\n\tonLoad(query) { if (query && query.id) { this.id = Number(query.id) } },\r\n\tmethods: {\r\n\t\tasync save() {\r\n\t\t\tif (!this.form.name) return uni.showToast({ title:'请填写供应商名称', icon:'none' })\r\n\t\t\ttry {\r\n\t\t\t\tif (this.id) await put(`/api/suppliers/${this.id}`, this.form)\r\n\t\t\t\telse await post('/api/suppliers', this.form)\r\n\t\t\t\tuni.showToast({ title:'保存成功', icon:'success' })\r\n\t\t\t\tsetTimeout(() => uni.navigateBack(), 500)\r\n\t\t\t} catch(e) { uni.showToast({ title: e?.message || '保存失败', icon:'none' }) }\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { padding-bottom: 140rpx; }\r\n.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee; }\r\n.label { color:#666; }\r\n.value { color:#333; text-align: right; flex: 1; }\r\n.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx; }\r\n.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); }\r\n.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }\r\n</style>\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/supplier/form.vue'\nwx.createPage(MiniProgramPage)"],"names":["uni","put","post"],"mappings":";;;AAiBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO;AAAA,MACN,IAAI;AAAA,MACJ,MAAM,EAAE,MAAK,IAAI,aAAY,IAAI,QAAO,IAAI,OAAM,IAAI,SAAQ,IAAI,WAAU,GAAG,WAAU,GAAG,QAAO,GAAG;AAAA,IACvG;AAAA,EACA;AAAA,EACD,OAAO,OAAO;AAAE,QAAI,SAAS,MAAM,IAAI;AAAE,WAAK,KAAK,OAAO,MAAM,EAAE;AAAA,IAAE;AAAA,EAAG;AAAA,EACvE,SAAS;AAAA,IACR,MAAM,OAAO;AACZ,UAAI,CAAC,KAAK,KAAK;AAAM,eAAOA,cAAG,MAAC,UAAU,EAAE,OAAM,YAAY,MAAK,QAAQ;AAC3E,UAAI;AACH,YAAI,KAAK;AAAI,gBAAMC,YAAAA,IAAI,kBAAkB,KAAK,EAAE,IAAI,KAAK,IAAI;AAAA;AACxD,gBAAMC,iBAAK,kBAAkB,KAAK,IAAI;AAC3CF,sBAAG,MAAC,UAAU,EAAE,OAAM,QAAQ,MAAK,WAAW;AAC9C,mBAAW,MAAMA,cAAAA,MAAI,aAAY,GAAI,GAAG;AAAA,MACzC,SAAQ,GAAG;AAAEA,sBAAG,MAAC,UAAU,EAAE,QAAO,uBAAG,YAAW,QAAQ,MAAK,OAAQ,CAAA;AAAA,MAAE;AAAA,IAC1E;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnCA,GAAG,WAAW,eAAe;"}

View File

@@ -1 +1 @@
{"version":3,"file":"select.js","sources":["pages/supplier/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvc3VwcGxpZXIvc2VsZWN0LnZ1ZQ"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"search\">\r\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索供应商名称/电话\" @confirm=\"search\" />\r\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"s in suppliers\" :key=\"s.id\" @click=\"select(s)\">\r\n\t\t\t\t<view class=\"name\">{{ s.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ s.mobile || '—' }}</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\n\timport { get } from '../../common/http.js'\r\n\texport default {\r\n\t\tdata() { return { kw: '', suppliers: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/suppliers', { kw: this.kw, page: 1, size: 50 })\r\n\t\t\t\t\tthis.suppliers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t\t},\r\n\t\t\tselect(s) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm) {\r\n\t\t\t\t\topener.$vm.order.supplierId = s.id\r\n\t\t\t\t\topener.$vm.supplierName = s.name\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t.page { display:flex; flex-direction: column; height: 100vh; }\r\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n\t.list { flex:1; }\r\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n\t.name { color:#333; margin-bottom: 6rpx; }\r\n\t.meta { color:#888; font-size: 24rpx; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/supplier/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAiBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,WAAW,CAAA;EAAM;AAAA,EAC3C,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,gBAAI,kBAAkB,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG,MAAM,GAAC,CAAG;AAC1E,aAAK,YAAY,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC5E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,KAAK;AACzB,eAAO,IAAI,MAAM,aAAa,EAAE;AAChC,eAAO,IAAI,eAAe,EAAE;AAAA,MAC7B;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;ACnCD,GAAG,WAAW,eAAe;"}
{"version":3,"file":"select.js","sources":["pages/supplier/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvc3VwcGxpZXIvc2VsZWN0LnZ1ZQ"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"search\">\r\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索供应商名称/电话\" @confirm=\"search\" />\r\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\r\n\t\t\t<button size=\"mini\" :type=\"debtOnly ? 'primary' : 'default'\" @click=\"toggleDebtOnly\">只看欠款</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"s in suppliers\" :key=\"s.id\" @click=\"select(s)\">\r\n\t\t\t\t<view class=\"name\">{{ s.name }}</view>\r\n\t\t\t\t<view class=\"meta\">\r\n\t\t\t\t\t{{ s.mobile || '—' }}\r\n\t\t\t\t\t<text v-if=\"typeof s.apPayable === 'number'\">|应付:¥ {{ Number(s.apPayable).toFixed(2) }}</text>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t\t<view class=\"bottom\">\r\n\t\t\t<button class=\"primary\" @click=\"createSupplier\">新增供应商</button>\r\n\t\t</view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\n\timport { get } from '../../common/http.js'\r\n\texport default {\r\n data() { return { kw: '', debtOnly: false, suppliers: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\ttoggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search() },\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/suppliers', { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 })\r\n\t\t\t\t\tthis.suppliers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t\t},\r\n\t\t\tcreateSupplier() { uni.navigateTo({ url: '/pages/supplier/form' }) },\r\n\t\t\tselect(s) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm) {\r\n\t\t\t\t\topener.$vm.order.supplierId = s.id\r\n\t\t\t\t\topener.$vm.supplierName = s.name\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t.page { display:flex; flex-direction: column; height: 100vh; }\r\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n\t.list { flex:1; }\r\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n\t.name { color:#333; margin-bottom: 6rpx; }\r\n\t.meta { color:#888; font-size: 24rpx; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/supplier/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAwBC,MAAK,YAAU;AAAA,EACZ,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,OAAO,WAAW,CAAA;EAAM;AAAA,EAC9D,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,iBAAiB;AAAE,WAAK,WAAW,CAAC,KAAK;AAAU,WAAK;IAAU;AAAA,IAClE,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,kBAAkB,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,UAAU,MAAM,GAAG,MAAM,IAAI;AACnG,aAAK,YAAY,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC5E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,iBAAiB;AAAEA,oBAAAA,MAAI,WAAW,EAAE,KAAK,uBAAqB,CAAG;AAAA,IAAG;AAAA,IACpE,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,KAAK;AACzB,eAAO,IAAI,MAAM,aAAa,EAAE;AAChC,eAAO,IAAI,eAAe,EAAE;AAAA,MAC7B;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;AC5CD,GAAG,WAAW,eAAe;"}

View File

@@ -12,7 +12,9 @@ if (!Math) {
"./pages/product/settings.js";
"./pages/customer/select.js";
"./pages/customer/form.js";
"./pages/customer/detail.js";
"./pages/supplier/select.js";
"./pages/supplier/form.js";
"./pages/account/select.js";
"./pages/detail/index.js";
}

View File

@@ -10,7 +10,9 @@
"pages/product/settings",
"pages/customer/select",
"pages/customer/form",
"pages/customer/detail",
"pages/supplier/select",
"pages/supplier/form",
"pages/account/select",
"pages/detail/index"
],

View File

@@ -15,9 +15,11 @@ function requestWithFallback(options, candidates, idx, resolve, reject) {
const { statusCode, data } = res;
if (statusCode >= 200 && statusCode < 300)
return resolve(data);
const msg = data && (data.message || data.error || data.msg) || "HTTP " + statusCode;
common_vendor.index.showToast({ title: msg, icon: "none" });
if (idx + 1 < candidates.length)
return requestWithFallback(options, candidates, idx + 1, resolve, reject);
reject(new Error("HTTP " + statusCode));
reject(new Error(msg));
}, fail: (err) => {
if (idx + 1 < candidates.length)
return requestWithFallback(options, candidates, idx + 1, resolve, reject);

View File

@@ -0,0 +1,156 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const common_http = require("../../common/http.js");
const _sfc_main = {
data() {
return { id: null, d: {}, editing: false, form: { name: "", contactName: "", mobile: "", phone: "", address: "", level: "", priceLevel: "零售价", arOpening: 0, remark: "" }, priceLevels: ["零售价", "批发价", "大单报价"], priceLabels: ["零售价", "批发价", "大单报价"], priceIdx: 0 };
},
onLoad(q) {
if (q && q.id) {
this.id = Number(q.id);
this.fetch();
}
},
methods: {
async fetch() {
try {
this.d = await common_http.get(`/api/customers/${this.id}`);
this.form = {
name: this.d.name || "",
contactName: this.d.contactName || "",
mobile: this.d.mobile || "",
phone: this.d.phone || "",
address: this.d.address || "",
level: this.d.level || "",
priceLevel: this.d.priceLevel || "retail",
arOpening: Number(this.d.arOpening || 0),
remark: this.d.remark || ""
};
const idx = this.priceLevels.indexOf(this.form.priceLevel);
this.priceIdx = idx >= 0 ? idx : 0;
} catch (e) {
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
}
},
toggleEdit() {
this.editing = !this.editing;
},
onPriceChange(e) {
this.priceIdx = Number(e.detail.value);
this.form.priceLevel = this.priceLevels[this.priceIdx];
},
choose() {
const pages = getCurrentPages();
let targetIdx = -1;
for (let i = pages.length - 2; i >= 0; i--) {
const vm = pages[i] && pages[i].$vm ? pages[i].$vm : null;
if (vm && vm.order) {
vm.order.customerId = this.d.id;
vm.customerName = this.d.name;
targetIdx = i;
break;
}
}
if (targetIdx >= 0) {
const delta = pages.length - 1 - targetIdx;
common_vendor.index.navigateBack({ delta });
} else {
common_vendor.index.navigateBack();
}
},
async save() {
if (!this.form.name)
return common_vendor.index.showToast({ title: "请填写客户名称", icon: "none" });
try {
await common_http.put(`/api/customers/${this.id}`, this.form);
common_vendor.index.showToast({ title: "已保存", icon: "success" });
this.editing = false;
await this.fetch();
} catch (e) {
common_vendor.index.showToast({ title: (e == null ? void 0 : e.message) || "保存失败", icon: "none" });
}
}
}
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return common_vendor.e({
a: !$data.editing
}, !$data.editing ? {
b: common_vendor.t($data.d.name)
} : {
c: $data.form.name,
d: common_vendor.o(($event) => $data.form.name = $event.detail.value)
}, {
e: !$data.editing
}, !$data.editing ? {
f: common_vendor.t($data.d.contactName || "—")
} : {
g: $data.form.contactName,
h: common_vendor.o(($event) => $data.form.contactName = $event.detail.value)
}, {
i: !$data.editing
}, !$data.editing ? {
j: common_vendor.t($data.d.mobile || "—")
} : {
k: $data.form.mobile,
l: common_vendor.o(($event) => $data.form.mobile = $event.detail.value)
}, {
m: !$data.editing
}, !$data.editing ? {
n: common_vendor.t($data.d.phone || "—")
} : {
o: $data.form.phone,
p: common_vendor.o(($event) => $data.form.phone = $event.detail.value)
}, {
q: !$data.editing
}, !$data.editing ? {
r: common_vendor.t($data.d.address || "—")
} : {
s: $data.form.address,
t: common_vendor.o(($event) => $data.form.address = $event.detail.value)
}, {
v: !$data.editing
}, !$data.editing ? {
w: common_vendor.t($data.d.level || "—")
} : {
x: $data.form.level,
y: common_vendor.o(($event) => $data.form.level = $event.detail.value)
}, {
z: !$data.editing
}, !$data.editing ? {
A: common_vendor.t($data.d.priceLevel)
} : {
B: common_vendor.t($data.priceLabels[$data.priceIdx]),
C: $data.priceLabels,
D: $data.priceIdx,
E: common_vendor.o((...args) => $options.onPriceChange && $options.onPriceChange(...args))
}, {
F: !$data.editing
}, !$data.editing ? {
G: common_vendor.t(Number($data.d.arOpening || 0).toFixed(2))
} : {
H: $data.form.arOpening,
I: common_vendor.o(common_vendor.m(($event) => $data.form.arOpening = $event.detail.value, {
number: true
}))
}, {
J: common_vendor.t(Number($data.d.receivable || 0).toFixed(2)),
K: !$data.editing
}, !$data.editing ? {
L: common_vendor.t($data.d.remark || "—")
} : {
M: $data.form.remark,
N: common_vendor.o(($event) => $data.form.remark = $event.detail.value)
}, {
O: common_vendor.t($data.editing ? "取消" : "编辑"),
P: common_vendor.o((...args) => $options.toggleEdit && $options.toggleEdit(...args)),
Q: $data.editing
}, $data.editing ? {
R: common_vendor.o((...args) => $options.save && $options.save(...args))
} : {
S: common_vendor.o((...args) => $options.choose && $options.choose(...args))
});
}
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/customer/detail.js.map

View File

@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "客户详情",
"usingComponents": {}
}

View File

@@ -0,0 +1 @@
<view class="page"><view class="card"><view class="row"><text class="label">名称</text><text wx:if="{{a}}" class="value">{{b}}</text><input wx:else class="value-input" placeholder="必填" value="{{c}}" bindinput="{{d}}"/></view><view class="row"><text class="label">联系人</text><text wx:if="{{e}}" class="value">{{f}}</text><input wx:else class="value-input" placeholder="可选" value="{{g}}" bindinput="{{h}}"/></view><view class="row"><text class="label">手机</text><text wx:if="{{i}}" class="value">{{j}}</text><input wx:else class="value-input" placeholder="可选" value="{{k}}" bindinput="{{l}}"/></view><view class="row"><text class="label">电话</text><text wx:if="{{m}}" class="value">{{n}}</text><input wx:else class="value-input" placeholder="可选(座机)" value="{{o}}" bindinput="{{p}}"/></view><view class="row"><text class="label">地址</text><text wx:if="{{q}}" class="value">{{r}}</text><input wx:else class="value-input" placeholder="可选" value="{{s}}" bindinput="{{t}}"/></view><view class="row"><text class="label">等级</text><text wx:if="{{v}}" class="value">{{w}}</text><input wx:else class="value-input" placeholder="可选,如 VIP/A/B" value="{{x}}" bindinput="{{y}}"/></view><view class="row"><text class="label">售价档位</text><text wx:if="{{z}}" class="value">{{A}}</text><picker wx:else range="{{C}}" value="{{D}}" bindchange="{{E}}"><view class="value">{{B}}</view></picker></view><view class="row"><text class="label">初始应收</text><text wx:if="{{F}}" class="value">¥ {{G}}</text><input wx:else class="value-input" type="digit" placeholder="0.00" value="{{H}}" bindinput="{{I}}"/></view><view class="row"><text class="label">当前应收</text><text class="value emp">¥ {{J}}</text></view><view class="row"><text class="label">备注</text><text wx:if="{{K}}" class="value">{{L}}</text><input wx:else class="value-input" placeholder="—" value="{{M}}" bindinput="{{N}}"/></view></view><view class="bottom"><button class="ghost" bindtap="{{P}}">{{O}}</button><button wx:if="{{Q}}" class="primary" bindtap="{{R}}">保存</button><button wx:else class="primary" bindtap="{{S}}">选择此客户</button></view></view>

View File

@@ -0,0 +1,23 @@
.page { padding-bottom: 140rpx;
}
.card { background:#fff; margin: 16rpx; padding: 12rpx 16rpx; border-radius: 16rpx;
}
.row { display:flex; justify-content: space-between; padding: 18rpx 8rpx; border-bottom: 1rpx solid #f3f3f3;
}
.row:last-child { border-bottom: 0;
}
.label { color:#666;
}
.value { color:#333; max-width: 60%; text-align: right;
}
.value-input { color:#333; text-align: right; flex: 1;
}
.emp { color:#107e9b; font-weight: 700;
}
.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); display:flex; gap: 12rpx;
}
.primary { flex:1; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0;
}
.ghost { flex:1; background:#fff; color:#107e9b; border: 2rpx solid #A0E4FF; border-radius: 999rpx; padding: 18rpx 0;
}

View File

@@ -6,7 +6,8 @@ const _sfc_main = {
return {
id: null,
form: { name: "", level: "", priceLevel: "retail", contactName: "", mobile: "", phone: "", address: "", arOpening: 0, remark: "" },
priceLevels: ["retail", "distribution", "wholesale", "big_client"],
priceLevels: ["零售价", "批发价", "大单报价"],
priceLabels: ["零售价", "批发价", "大单报价"],
priceIdx: 0
};
},
@@ -42,8 +43,8 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
b: common_vendor.o(($event) => $data.form.name = $event.detail.value),
c: $data.form.level,
d: common_vendor.o(($event) => $data.form.level = $event.detail.value),
e: common_vendor.t($data.priceLevels[$data.priceIdx]),
f: $data.priceLevels,
e: common_vendor.t($data.priceLabels[$data.priceIdx]),
f: $data.priceLabels,
g: $data.priceIdx,
h: common_vendor.o((...args) => $options.onPriceChange && $options.onPriceChange(...args)),
i: $data.form.contactName,

View File

@@ -8,6 +8,9 @@ const _sfc_main = {
onLoad() {
this.search();
},
onShow() {
this.search();
},
methods: {
toggleDebtOnly() {
this.debtOnly = !this.debtOnly;
@@ -25,12 +28,17 @@ const _sfc_main = {
common_vendor.index.navigateTo({ url: "/pages/customer/form" });
},
select(c) {
const opener = getCurrentPages()[getCurrentPages().length - 2];
if (opener && opener.$vm) {
opener.$vm.order.customerId = c.id;
opener.$vm.customerName = c.name;
const pages = getCurrentPages();
const prev = pages.length >= 2 ? pages[pages.length - 2] : null;
const vm = prev && prev.$vm ? prev.$vm : null;
if (vm && vm.order) {
vm.order.customerId = c.id;
vm.customerName = c.name;
}
common_vendor.index.navigateBack();
},
openDetail(c) {
common_vendor.index.navigateTo({ url: "/pages/customer/detail?id=" + c.id });
}
}
};
@@ -51,7 +59,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
d: common_vendor.t(Number(c.receivable).toFixed(2))
} : {}, {
e: c.id,
f: common_vendor.o(($event) => $options.select(c), c.id)
f: common_vendor.o(($event) => $options.openDetail(c), c.id)
});
}),
h: common_vendor.o((...args) => $options.createCustomer && $options.createCustomer(...args))

View File

@@ -67,6 +67,10 @@ const _sfc_main = {
common_vendor.index.navigateTo({ url: "/pages/customer/select" });
return;
}
if (item.key === "supplier") {
common_vendor.index.navigateTo({ url: "/pages/supplier/select" });
return;
}
common_vendor.index.showToast({ title: item.title + "(开发中)", icon: "none" });
},
goProduct() {
@@ -79,7 +83,7 @@ const _sfc_main = {
goDetail() {
this.activeTab = "detail";
try {
common_vendor.index.__f__("log", "at pages/index/index.vue:170", "[index] goDetail → /pages/detail/index");
common_vendor.index.__f__("log", "at pages/index/index.vue:174", "[index] goDetail → /pages/detail/index");
} catch (e) {
}
common_vendor.index.navigateTo({ url: "/pages/detail/index" });

View File

@@ -22,6 +22,9 @@ const _sfc_main = {
remark: ""
},
customerName: "",
customerPriceLevel: "零售价",
_lastCustomerId: null,
_priceCache: {},
supplierName: "",
items: [],
activeCategory: "sale_income",
@@ -64,7 +67,70 @@ const _sfc_main = {
return Number(p.cash || 0) + Number(p.bank || 0) + Number(p.wechat || 0);
}
},
onShow() {
if (this.biz === "sale") {
if (this.order.customerId && this.order.customerId !== this._lastCustomerId) {
this.loadCustomerLevel(this.order.customerId).then(() => {
this._lastCustomerId = this.order.customerId;
for (const it of this.items) {
if (it && (it._autoPrice || !it.unitPrice))
this.autoPriceItem(it);
}
});
}
for (const it of this.items) {
if (it && !it.unitPrice)
this.autoPriceItem(it);
}
}
},
methods: {
async loadCustomerLevel(customerId) {
try {
const d = await common_http.get(`/api/customers/${customerId}`);
this.customerPriceLevel = d && d.priceLevel ? d.priceLevel : "零售价";
} catch (e) {
this.customerPriceLevel = "零售价";
}
},
priceFieldForLevel() {
const lvl = this.customerPriceLevel || "零售价";
if (lvl === "批发价")
return "wholesalePrice";
if (lvl === "大单报价")
return "bigClientPrice";
return "retailPrice";
},
async autoPriceItem(it) {
if (this.biz !== "sale")
return;
if (!it || !it.productId)
return;
const pid = it.productId;
let detail = this._priceCache[pid];
if (!detail) {
try {
detail = await common_http.get(`/api/products/${pid}`);
this._priceCache[pid] = detail;
} catch (e) {
return;
}
}
const field = this.priceFieldForLevel();
let price = Number(detail && detail[field] != null ? detail[field] : 0);
if (!price && field !== "retailPrice") {
price = Number(detail && detail.retailPrice != null ? detail.retailPrice : 0);
}
it.unitPrice = price;
it._autoPrice = true;
this.recalc();
},
onPriceInput(it) {
if (it) {
it._autoPrice = false;
this.recalc();
}
},
switchBiz(type) {
this.biz = type;
},
@@ -98,6 +164,17 @@ const _sfc_main = {
const isSaleOrPurchase = this.biz === "sale" || this.biz === "purchase";
const isCollectOrPay = this.biz === "sale" && this.saleType === "collect" || this.biz === "purchase" && this.purchaseType === "pay";
const saleTypeValue = this.biz === "sale" ? "sale." + this.saleType : "purchase." + this.purchaseType;
if (isSaleOrPurchase && !isCollectOrPay) {
if (!this.items.length) {
common_vendor.index.showToast({ title: "请先选择商品", icon: "none" });
return;
}
const invalid = this.items.find((it) => !it.productId || Number(it.quantity || 0) <= 0);
if (invalid) {
common_vendor.index.showToast({ title: "数量需大于0", icon: "none" });
return;
}
}
const payload = isSaleOrPurchase ? isCollectOrPay ? [
{ method: "cash", amount: Number(this.payments.cash || 0) },
{ method: "bank", amount: Number(this.payments.bank || 0) },
@@ -244,7 +321,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
c: it.quantity,
d: common_vendor.o([common_vendor.m(($event) => it.unitPrice = $event.detail.value, {
number: true
}), ($event) => $options.recalc()], idx),
}), ($event) => $options.onPriceInput(it)], idx),
e: it.unitPrice,
f: common_vendor.t((Number(it.quantity) * Number(it.unitPrice)).toFixed(2)),
g: idx

View File

@@ -20,7 +20,8 @@ const _sfc_main = {
select(p) {
const opener = getCurrentPages()[getCurrentPages().length - 2];
if (opener && opener.$vm && opener.$vm.items) {
opener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: Number(p.price || 0) });
const initPrice = Number(p.retailPrice != null ? p.retailPrice : p.price || 0);
opener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: initPrice, _autoPrice: true });
}
common_vendor.index.navigateBack();
}

View File

@@ -0,0 +1,60 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const common_http = require("../../common/http.js");
const _sfc_main = {
data() {
return {
id: null,
form: { name: "", contactName: "", mobile: "", phone: "", address: "", apOpening: 0, apPayable: 0, remark: "" }
};
},
onLoad(query) {
if (query && query.id) {
this.id = Number(query.id);
}
},
methods: {
async save() {
if (!this.form.name)
return common_vendor.index.showToast({ title: "请填写供应商名称", icon: "none" });
try {
if (this.id)
await common_http.put(`/api/suppliers/${this.id}`, this.form);
else
await common_http.post("/api/suppliers", this.form);
common_vendor.index.showToast({ title: "保存成功", icon: "success" });
setTimeout(() => common_vendor.index.navigateBack(), 500);
} catch (e) {
common_vendor.index.showToast({ title: (e == null ? void 0 : e.message) || "保存失败", icon: "none" });
}
}
}
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return {
a: $data.form.name,
b: common_vendor.o(($event) => $data.form.name = $event.detail.value),
c: $data.form.contactName,
d: common_vendor.o(($event) => $data.form.contactName = $event.detail.value),
e: $data.form.mobile,
f: common_vendor.o(($event) => $data.form.mobile = $event.detail.value),
g: $data.form.phone,
h: common_vendor.o(($event) => $data.form.phone = $event.detail.value),
i: $data.form.address,
j: common_vendor.o(($event) => $data.form.address = $event.detail.value),
k: $data.form.apOpening,
l: common_vendor.o(common_vendor.m(($event) => $data.form.apOpening = $event.detail.value, {
number: true
})),
m: $data.form.apPayable,
n: common_vendor.o(common_vendor.m(($event) => $data.form.apPayable = $event.detail.value, {
number: true
})),
o: $data.form.remark,
p: common_vendor.o(($event) => $data.form.remark = $event.detail.value),
q: common_vendor.o((...args) => $options.save && $options.save(...args))
};
}
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/supplier/form.js.map

View File

@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "新增/编辑供应商",
"usingComponents": {}
}

View File

@@ -0,0 +1 @@
<view class="page"><view class="field"><text class="label">供应商名称</text><input class="value" placeholder="必填" value="{{a}}" bindinput="{{b}}"/></view><view class="field"><text class="label">联系人</text><input class="value" placeholder="可选" value="{{c}}" bindinput="{{d}}"/></view><view class="field"><text class="label">手机</text><input class="value" placeholder="可选" value="{{e}}" bindinput="{{f}}"/></view><view class="field"><text class="label">电话</text><input class="value" placeholder="可选(座机)" value="{{g}}" bindinput="{{h}}"/></view><view class="field"><text class="label">经营地址</text><input class="value" placeholder="可选" value="{{i}}" bindinput="{{j}}"/></view><view class="field"><text class="label">初始应付款</text><input class="value" type="digit" placeholder="默认 0.00" value="{{k}}" bindinput="{{l}}"/></view><view class="field"><text class="label">应付款</text><input class="value" type="digit" placeholder="默认 0.00" value="{{m}}" bindinput="{{n}}"/></view><view class="textarea"><block wx:if="{{r0}}"><textarea maxlength="200" placeholder="备注最多200字" value="{{o}}" bindinput="{{p}}"></textarea></block></view><view class="bottom"><button class="primary" bindtap="{{q}}">保存</button></view></view>

View File

@@ -0,0 +1,15 @@
.page { padding-bottom: 140rpx;
}
.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee;
}
.label { color:#666;
}
.value { color:#333; text-align: right; flex: 1;
}
.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx;
}
.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06);
}
.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0;
}

View File

@@ -3,20 +3,27 @@ const common_vendor = require("../../common/vendor.js");
const common_http = require("../../common/http.js");
const _sfc_main = {
data() {
return { kw: "", suppliers: [] };
return { kw: "", debtOnly: false, suppliers: [] };
},
onLoad() {
this.search();
},
methods: {
toggleDebtOnly() {
this.debtOnly = !this.debtOnly;
this.search();
},
async search() {
try {
const res = await common_http.get("/api/suppliers", { kw: this.kw, page: 1, size: 50 });
const res = await common_http.get("/api/suppliers", { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 });
this.suppliers = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : [];
} catch (e) {
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
}
},
createSupplier() {
common_vendor.index.navigateTo({ url: "/pages/supplier/form" });
},
select(s) {
const opener = getCurrentPages()[getCurrentPages().length - 2];
if (opener && opener.$vm) {
@@ -33,14 +40,21 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
b: $data.kw,
c: common_vendor.o(($event) => $data.kw = $event.detail.value),
d: common_vendor.o((...args) => $options.search && $options.search(...args)),
e: common_vendor.f($data.suppliers, (s, k0, i0) => {
return {
e: $data.debtOnly ? "primary" : "default",
f: common_vendor.o((...args) => $options.toggleDebtOnly && $options.toggleDebtOnly(...args)),
g: common_vendor.f($data.suppliers, (s, k0, i0) => {
return common_vendor.e({
a: common_vendor.t(s.name),
b: common_vendor.t(s.mobile || "—"),
c: s.id,
d: common_vendor.o(($event) => $options.select(s), s.id)
};
})
c: typeof s.apPayable === "number"
}, typeof s.apPayable === "number" ? {
d: common_vendor.t(Number(s.apPayable).toFixed(2))
} : {}, {
e: s.id,
f: common_vendor.o(($event) => $options.select(s), s.id)
});
}),
h: common_vendor.o((...args) => $options.createSupplier && $options.createSupplier(...args))
};
}
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);

View File

@@ -1 +1 @@
<view class="page"><view class="search"><input placeholder="搜索供应商名称/电话" bindconfirm="{{a}}" value="{{b}}" bindinput="{{c}}"/><button size="mini" bindtap="{{d}}">搜索</button></view><scroll-view scroll-y class="list"><view wx:for="{{e}}" wx:for-item="s" wx:key="c" class="item" bindtap="{{s.d}}"><view class="name">{{s.a}}</view><view class="meta">{{s.b}}</view></view></scroll-view></view>
<view class="page"><view class="search"><input placeholder="搜索供应商名称/电话" bindconfirm="{{a}}" value="{{b}}" bindinput="{{c}}"/><button size="mini" bindtap="{{d}}">搜索</button><button size="mini" type="{{e}}" bindtap="{{f}}">只看欠款</button></view><scroll-view scroll-y class="list"><view wx:for="{{g}}" wx:for-item="s" wx:key="e" class="item" bindtap="{{s.f}}"><view class="name">{{s.a}}</view><view class="meta">{{s.b}} <text wx:if="{{s.c}}">|应付:¥ {{s.d}}</text></view></view></scroll-view><view class="bottom"><button class="primary" bindtap="{{h}}">新增供应商</button></view></view>