This commit is contained in:
van
2026-06-03 11:43:02 +08:00
parent 0a484449dc
commit de32ec4a93
3 changed files with 320 additions and 21 deletions

View File

@@ -19,6 +19,14 @@ export function listQuickRecordModelOptions() {
}) })
} }
/** 快捷录单页:型号后缀店铺选项(系统参数 quickRecord.modelShopOptions */
export function listQuickRecordShopOptions() {
return request({
url: '/system/jdorder/quickRecord/shopOptions',
method: 'get'
})
}
// JD订单详情 // JD订单详情
export function getJDOrder(id) { export function getJDOrder(id) {
return request({ return request({

View File

@@ -0,0 +1,142 @@
<template>
<el-dialog
:visible.sync="visible"
width="640px"
:close-on-click-modal="false"
append-to-body
@close="handleClose"
>
<div slot="title" class="dialog-title">
<i class="el-icon-shop"></i>
<span>快捷录单型号店铺选项</span>
</div>
<div v-loading="loading">
<el-alert
type="info"
:closable="false"
show-icon
title="每行一条,格式:短前缀(完整店名)。录单时仅将短前缀拼接到型号末尾。"
style="margin-bottom: 12px;"
/>
<el-input
v-model="configText"
type="textarea"
:rows="8"
placeholder="海尔官旗(海尔官方旗舰店)&#10;海尔厨房(海尔厨房电器官方旗舰店)"
/>
<p class="hint">参数键名quickRecord.modelShopOptions保存至系统参数</p>
</div>
<div slot="footer">
<el-button size="small" @click="handleClose">取消</el-button>
<el-button type="primary" size="small" :loading="saveLoading" @click="handleSave">保存</el-button>
</div>
</el-dialog>
</template>
<script>
import { listConfig, addConfig, updateConfig } from '@/api/system/config'
const CONFIG_KEY = 'quickRecord.modelShopOptions'
const CONFIG_NAME = '快捷录单型号店铺选项'
export default {
name: 'QuickRecordModelShopConfig',
props: {
value: {
type: Boolean,
default: false
}
},
data() {
return {
visible: false,
loading: false,
saveLoading: false,
configText: '',
configId: null
}
},
watch: {
value(val) {
this.visible = val
if (val) {
this.loadConfig()
}
},
visible(val) {
this.$emit('input', val)
}
},
methods: {
async loadConfig() {
this.loading = true
try {
const res = await listConfig({
configKey: CONFIG_KEY,
pageNum: 1,
pageSize: 10
})
const row = res && res.rows && res.rows.find(r => r && r.configKey === CONFIG_KEY)
if (row) {
this.configId = row.configId
this.configText = row.configValue || ''
} else {
this.configId = null
this.configText = ''
}
} catch (e) {
this.configId = null
this.configText = ''
} finally {
this.loading = false
}
},
async handleSave() {
this.saveLoading = true
try {
const payload = {
configName: CONFIG_NAME,
configKey: CONFIG_KEY,
configValue: (this.configText || '').trim(),
configType: 'N'
}
if (this.configId) {
payload.configId = this.configId
await updateConfig(payload)
} else {
await addConfig(payload)
}
this.$modal.msgSuccess('店铺选项已保存')
this.$emit('saved')
this.handleClose()
} catch (e) {
this.$modal.msgError('保存失败,请确认有参数管理权限')
} finally {
this.saveLoading = false
}
},
handleClose() {
this.visible = false
this.$emit('input', false)
}
}
}
</script>
<style scoped>
.dialog-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 16px;
font-weight: 600;
}
.hint {
margin: 10px 0 0;
font-size: 12px;
color: #909399;
}
</style>

View File

@@ -14,19 +14,53 @@
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="型号" required> <el-form-item label="型号" required>
<el-autocomplete <div class="model-row">
v-model="form.model" <el-autocomplete
class="model-input-full" v-model="form.model"
:fetch-suggestions="queryModels" class="model-input-base"
placeholder="可输入、可改;点选联想项会带出最近一单付款 / 后返" :fetch-suggestions="queryModels"
clearable placeholder="型号本体;点选历史型号会拆分店铺并带出付款/后返"
:trigger-on-focus="true" clearable
@select="onModelSuggestionSelect" :trigger-on-focus="true"
> @select="onModelSuggestionSelect"
<template slot-scope="{ item }"> @blur="onModelBlur"
<span class="model-suggest-label">{{ item.label }}</span> >
<template slot-scope="{ item }">
<span class="model-suggest-label">{{ item.label }}</span>
</template>
</el-autocomplete>
<el-select
v-model="form.shopPrefix"
class="model-shop-select"
:placeholder="shopOptions.length ? '店铺(必选)' : '店铺'"
clearable
filterable
>
<el-option
v-for="opt in shopOptions"
:key="opt.prefix"
:label="opt.label"
:value="opt.prefix"
/>
</el-select>
<el-button
v-if="!isMobile"
type="text"
class="model-shop-config-btn"
icon="el-icon-setting"
title="配置型号店铺选项"
@click="showShopConfig = true"
/>
</div>
<p class="model-shop-hint">
<template v-if="shopOptions.length">
落库型号 = 上方输入 + 所选店铺前缀 W5000PLUS2.0白 + 海尔厨房 W5000PLUS2.0白海尔厨房
</template> </template>
</el-autocomplete> <template v-else>
尚未配置店铺选项录单时不强制选择店铺
</template>
<el-button type="text" size="mini" @click="showShopConfig = true">配置店铺选项</el-button>
</p>
</el-form-item> </el-form-item>
<el-form-item label="下单地址" required> <el-form-item label="下单地址" required>
<el-input <el-input
@@ -114,15 +148,19 @@
<el-button type="primary" :loading="verifyLoading" @click="handleVerify">确认录单</el-button> <el-button type="primary" :loading="verifyLoading" @click="handleVerify">确认录单</el-button>
</div> </div>
</el-dialog> </el-dialog>
<quick-record-model-shop-config v-model="showShopConfig" @saved="loadShopOptions" />
</div> </div>
</template> </template>
<script> <script>
import { executeInstruction, executeInstructionWithForce } from '@/api/system/instruction' import { executeInstruction, executeInstructionWithForce } from '@/api/system/instruction'
import { listQuickRecordModelOptions } from '@/api/system/jdorder' import { listQuickRecordModelOptions, listQuickRecordShopOptions } from '@/api/system/jdorder'
import QuickRecordModelShopConfig from './components/QuickRecordModelShopConfig.vue'
export default { export default {
name: 'FadanQuickRecord', name: 'FadanQuickRecord',
components: { QuickRecordModelShopConfig },
props: { props: {
isMobile: { isMobile: {
type: Boolean, type: Boolean,
@@ -134,6 +172,7 @@ export default {
form: { form: {
markSuffix: '', markSuffix: '',
model: '', model: '',
shopPrefix: '',
address: '', address: '',
link: '', link: '',
thirdPartyOrderNoText: '', thirdPartyOrderNoText: '',
@@ -152,11 +191,14 @@ export default {
verifyMessage: '', verifyMessage: '',
verifyLoading: false, verifyLoading: false,
pendingCommand: '', pendingCommand: '',
modelOptions: [] modelOptions: [],
shopOptions: [],
showShopConfig: false
} }
}, },
mounted() { mounted() {
this.loadModelOptions() this.loadModelOptions()
this.loadShopOptions()
}, },
computed: { computed: {
dialogWidth() { dialogWidth() {
@@ -261,6 +303,59 @@ export default {
// 静默失败,不影响手输型号 // 静默失败,不影响手输型号
} }
}, },
async loadShopOptions() {
try {
const res = await listQuickRecordShopOptions()
if (!(res && (res.code === 200 || res.msg === '操作成功'))) {
return
}
const rows = Array.isArray(res.data) ? res.data : []
this.shopOptions = rows
.filter(o => o && String(o.prefix || '').trim())
.map(o => ({
prefix: String(o.prefix).trim(),
fullName: o.fullName || '',
label: o.label || o.prefix
}))
.sort((a, b) => b.prefix.length - a.prefix.length)
} catch (e) {
this.shopOptions = []
}
},
/** 从完整型号末尾匹配店铺前缀(长前缀优先),用于回显 */
parseModelWithShop(fullModel) {
const full = String(fullModel || '').trim()
if (!full || !this.shopOptions.length) {
return { base: full, prefix: '' }
}
for (const opt of this.shopOptions) {
const p = opt.prefix
if (p && full.endsWith(p) && full.length > p.length) {
return { base: full.slice(0, -p.length), prefix: p }
}
}
return { base: full, prefix: '' }
},
applyModelWithShop(fullModel) {
const parsed = this.parseModelWithShop(fullModel)
this.form.model = parsed.base
this.form.shopPrefix = parsed.prefix
return parsed
},
onModelBlur() {
if (!this.form.model || !this.shopOptions.length) return
const parsed = this.parseModelWithShop(this.form.model)
if (parsed.prefix) {
this.form.model = parsed.base
this.form.shopPrefix = parsed.prefix
}
},
buildFullModelNumber() {
const base = (this.form.model || '').trim()
const prefix = (this.form.shopPrefix || '').trim()
if (!base) return ''
return prefix ? base + prefix : base
},
queryModels(queryString, cb) { queryModels(queryString, cb) {
const q = (queryString || '').trim().toLowerCase() const q = (queryString || '').trim().toLowerCase()
const rows = this.modelOptions const rows = this.modelOptions
@@ -268,20 +363,27 @@ export default {
const m = String(o.modelNumber || '').trim() const m = String(o.modelNumber || '').trim()
if (!m) return false if (!m) return false
if (!q) return true if (!q) return true
return m.toLowerCase().includes(q) const parsed = this.parseModelWithShop(m)
return m.toLowerCase().includes(q) || parsed.base.toLowerCase().includes(q)
}) })
.slice(0, 100) .slice(0, 100)
cb( cb(
rows.map(o => ({ rows.map(o => {
value: String(o.modelNumber).trim(), const full = String(o.modelNumber).trim()
label: this.modelOptionLabel(o) const parsed = this.parseModelWithShop(full)
})) const displayBase = parsed.base || full
return {
value: full,
label: this.modelOptionLabel({ ...o, modelNumber: displayBase })
}
})
) )
}, },
/** 仅从联想列表点选时回填金额;型号本身为输入框,选后仍可继续编辑 */ /** 仅从联想列表点选时回填金额;型号拆分为本体 + 店铺前缀 */
onModelSuggestionSelect(item) { onModelSuggestionSelect(item) {
if (!item || !item.value) return if (!item || !item.value) return
const key = String(item.value).trim() const key = String(item.value).trim()
this.applyModelWithShop(key)
const hit = this.modelOptions.find(o => o && String(o.modelNumber || '').trim() === key) const hit = this.modelOptions.find(o => o && String(o.modelNumber || '').trim() === key)
if (!hit) return if (!hit) return
const payRaw = hit.lastPaymentAmount const payRaw = hit.lastPaymentAmount
@@ -291,9 +393,18 @@ export default {
}, },
buildCommand() { buildCommand() {
const mark = this.buildDistributionMark() const mark = this.buildDistributionMark()
const model = (this.form.model || '').trim() this.onModelBlur()
const model = this.buildFullModelNumber()
this.normalizeAddressField() this.normalizeAddressField()
const address = (this.form.address || '').trim() const address = (this.form.address || '').trim()
if (!(this.form.model || '').trim()) {
this.$modal.msgError('请填写型号')
return null
}
if (this.shopOptions.length && !this.form.shopPrefix) {
this.$modal.msgError('请选择型号对应店铺')
return null
}
if (!model) { if (!model) {
this.$modal.msgError('请填写型号') this.$modal.msgError('请填写型号')
return null return null
@@ -506,6 +617,7 @@ export default {
this.form = { this.form = {
markSuffix: '', markSuffix: '',
model: '', model: '',
shopPrefix: '',
address: '', address: '',
link: '', link: '',
thirdPartyOrderNoText: '', thirdPartyOrderNoText: '',
@@ -582,6 +694,43 @@ export default {
min-width: 0; min-width: 0;
} }
.model-row {
display: flex;
align-items: flex-start;
gap: 8px;
width: 100%;
}
.model-input-base {
flex: 1;
min-width: 0;
}
.model-shop-select {
width: 220px;
flex-shrink: 0;
}
.model-shop-config-btn {
flex-shrink: 0;
padding: 8px 4px;
}
.model-shop-hint {
margin: 6px 0 0;
font-size: 12px;
color: #909399;
line-height: 1.4;
}
.fadan-quick-record--mobile .model-row {
flex-wrap: wrap;
}
.fadan-quick-record--mobile .model-shop-select {
width: 100%;
}
.model-input-full { .model-input-full {
width: 100%; width: 100%;
} }