1
This commit is contained in:
@@ -211,6 +211,12 @@ export const dynamicRoutes = [
|
|||||||
component: () => import('@/views/system/jd-instruction/index'),
|
component: () => import('@/views/system/jd-instruction/index'),
|
||||||
name: 'JdInstructionIndex',
|
name: 'JdInstructionIndex',
|
||||||
meta: { title: '指令执行', icon: 'form' }
|
meta: { title: '指令执行', icon: 'form' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'quick-record',
|
||||||
|
component: () => import('@/views/system/jd-instruction/fadan-quick-record'),
|
||||||
|
name: 'JdInstructionQuickRecord',
|
||||||
|
meta: { title: '快捷录单', icon: 'edit' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,391 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container mobile-fadan">
|
<fadan-quick-record :is-mobile="true" />
|
||||||
|
|
||||||
<el-card class="box-card">
|
|
||||||
<el-form :model="form" label-position="top" class="fadan-form">
|
|
||||||
<el-form-item label="分销标记(默认 F)">
|
|
||||||
<el-input v-model="form.mark" placeholder="一般为 F" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="型号" required>
|
|
||||||
<el-input v-model="form.model" placeholder="必填" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="下单地址" required>
|
|
||||||
<el-input v-model="form.address" type="textarea" :rows="3" placeholder="必填,与中控「生」指令第 6 行一致" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="转链链接(可空)">
|
|
||||||
<el-input v-model="form.link" type="textarea" :rows="2" placeholder="可空;留空则后台按型号尝试自动填充京粉链接" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="数量">
|
|
||||||
<el-input-number v-model="form.qty" :min="1" :max="99" controls-position="right" style="width: 100%;" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-divider content-position="left">落库必填(与中控录单一致)</el-divider>
|
|
||||||
<el-form-item label="下单人">
|
|
||||||
<el-input v-model="form.buyer" placeholder="落库必填" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="下单付款(元)">
|
|
||||||
<el-input v-model="form.paymentText" placeholder="落库必填,如 2999.00" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="后返金额(元)">
|
|
||||||
<el-input v-model="form.rebateText" placeholder="不写则按 0.00 落库" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="订单号(京东)">
|
|
||||||
<el-input v-model="form.orderIdText" placeholder="落库必填,与表单「订单号(需填)」一致" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="物流链接">
|
|
||||||
<el-input v-model="form.logisticsLink" type="textarea" :rows="2" placeholder="落库必填" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<div class="btn-row">
|
|
||||||
<el-button type="primary" size="medium" :loading="loading" @click="generate">录单</el-button>
|
|
||||||
<el-button size="medium" @click="resetForm">重置表单</el-button>
|
|
||||||
<el-button v-if="resultText" size="medium" @click="copyResult">复制录单</el-button>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<template v-if="resultText">
|
|
||||||
<el-divider content-position="left">录单结果</el-divider>
|
|
||||||
<el-input v-model="resultText" type="textarea" :rows="16" readonly class="result-area" />
|
|
||||||
</template>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
title="地址/单号重复验证"
|
|
||||||
:visible.sync="verifyDialogVisible"
|
|
||||||
width="92%"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
:close-on-press-escape="false"
|
|
||||||
append-to-body
|
|
||||||
>
|
|
||||||
<div class="verify-body">
|
|
||||||
<el-alert :title="verifyMessage" type="warning" :closable="false" />
|
|
||||||
<div class="verify-code">{{ verifyCode }}</div>
|
|
||||||
<el-input v-model="verifyInput" placeholder="请输入上方四位验证码" maxlength="4" @keyup.enter.native="handleVerify" />
|
|
||||||
</div>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
|
||||||
<el-button @click="verifyDialogVisible = false">取消</el-button>
|
|
||||||
<el-button type="primary" :loading="verifyLoading" @click="handleVerify">确认录单</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { executeInstruction, executeInstructionWithForce } from '@/api/system/instruction'
|
import FadanQuickRecord from '@/views/system/jd-instruction/fadan-quick-record.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MobileFadan',
|
name: 'MobileFadan',
|
||||||
data() {
|
components: { FadanQuickRecord }
|
||||||
return {
|
|
||||||
form: {
|
|
||||||
mark: 'F',
|
|
||||||
model: '',
|
|
||||||
address: '',
|
|
||||||
link: '',
|
|
||||||
qty: 1,
|
|
||||||
buyer: '',
|
|
||||||
paymentText: '',
|
|
||||||
rebateText: '',
|
|
||||||
orderIdText: '',
|
|
||||||
logisticsLink: ''
|
|
||||||
},
|
|
||||||
loading: false,
|
|
||||||
resultText: '',
|
|
||||||
resultListFromLast: [],
|
|
||||||
verifyDialogVisible: false,
|
|
||||||
verifyCode: '',
|
|
||||||
verifyInput: '',
|
|
||||||
verifyMessage: '',
|
|
||||||
verifyLoading: false,
|
|
||||||
pendingCommand: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
buildCommand() {
|
|
||||||
const mark = (this.form.mark || 'F').trim()
|
|
||||||
const model = (this.form.model || '').trim()
|
|
||||||
const address = (this.form.address || '').trim()
|
|
||||||
if (!model) {
|
|
||||||
this.$modal.msgError('请填写型号')
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (!address) {
|
|
||||||
this.$modal.msgError('请填写地址')
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const link = (this.form.link || '').trim()
|
|
||||||
const qty = Number(this.form.qty) > 0 ? Number(this.form.qty) : 1
|
|
||||||
const lines = ['生', mark, model, link, String(qty), address]
|
|
||||||
return lines.join('\n')
|
|
||||||
},
|
|
||||||
injectOptional(raw) {
|
|
||||||
if (!raw) return raw
|
|
||||||
let t = String(raw).replace(/\r\n/g, '\n')
|
|
||||||
const buyer = (this.form.buyer || '').trim()
|
|
||||||
const pay = (this.form.paymentText || '').trim()
|
|
||||||
const rebate = (this.form.rebateText || '').trim()
|
|
||||||
const orderId = (this.form.orderIdText || '').trim()
|
|
||||||
const logistics = (this.form.logisticsLink || '').trim()
|
|
||||||
if (buyer) {
|
|
||||||
t = t.replace(/(下单人(需填):)\n\n/, `$1\n${buyer}\n\n`)
|
|
||||||
}
|
|
||||||
if (pay) {
|
|
||||||
t = t.replace(/(下单付款(注意核对):)\n\n/, `$1\n${pay}\n\n`)
|
|
||||||
}
|
|
||||||
if (rebate) {
|
|
||||||
t = t.replace(/(后返金额(注意核对):)\n\n/, `$1\n${rebate}\n\n`)
|
|
||||||
} else {
|
|
||||||
t = t.replace(/(后返金额(注意核对):)\n\n/, `$1\n0.00\n\n`)
|
|
||||||
}
|
|
||||||
if (orderId) {
|
|
||||||
t = t.replace(/(订单号(需填):)\n\n/, `$1\n${orderId}\n\n`)
|
|
||||||
}
|
|
||||||
if (logistics) {
|
|
||||||
t = t.replace(/(物流链接(需填):)\n\n/, `$1\n${logistics}\n\n`)
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
},
|
|
||||||
/** 中控「单」录单:表单正文已含「单:」开头,直接作为 command 即可落库 */
|
|
||||||
formBodyLooksPersistable(text) {
|
|
||||||
const t = (text || '').trim()
|
|
||||||
return t.startsWith('单')
|
|
||||||
},
|
|
||||||
canPersistOrderFields() {
|
|
||||||
const buyer = (this.form.buyer || '').trim()
|
|
||||||
const pay = (this.form.paymentText || '').trim()
|
|
||||||
const orderId = (this.form.orderIdText || '').trim()
|
|
||||||
const logistics = (this.form.logisticsLink || '').trim()
|
|
||||||
return !!(buyer && pay && orderId && logistics)
|
|
||||||
},
|
|
||||||
formatInstructionData(data) {
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
return data.length ? data.join('\n\n') : ''
|
|
||||||
}
|
|
||||||
if (typeof data === 'string') {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
},
|
|
||||||
isPersistFailureText(text) {
|
|
||||||
const s = String(text || '')
|
|
||||||
return s.includes('[炸弹]') || s.includes('录单警告')
|
|
||||||
},
|
|
||||||
applyPersistResult(mergedBody, persistMsg) {
|
|
||||||
const msg = persistMsg || ''
|
|
||||||
this.resultText = (mergedBody || '') + '\n\n--- 落库回执 ---\n' + msg
|
|
||||||
if (this.isPersistFailureText(msg)) {
|
|
||||||
this.$modal.msgError('落库未成功,请查看回执')
|
|
||||||
} else {
|
|
||||||
this.$modal.msgSuccess('已生成表单并落库')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
extractFirstResponse(data) {
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
return data.length ? String(data[0]) : ''
|
|
||||||
}
|
|
||||||
if (typeof data === 'string') {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
},
|
|
||||||
checkDuplicateError(resultList) {
|
|
||||||
if (!resultList || !resultList.length) return false
|
|
||||||
for (let i = 0; i < resultList.length; i++) {
|
|
||||||
const s = resultList[i]
|
|
||||||
if (typeof s === 'string' && (
|
|
||||||
s.includes('ERROR_CODE:ADDRESS_DUPLICATE') ||
|
|
||||||
s.includes('ERROR_CODE:ORDER_NUMBER_DUPLICATE')
|
|
||||||
)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
showVerifyDialog(cmd) {
|
|
||||||
this.pendingCommand = cmd
|
|
||||||
this.verifyCode = String(Math.floor(1000 + Math.random() * 9000))
|
|
||||||
this.verifyInput = ''
|
|
||||||
let hasOrder = false
|
|
||||||
let hasAddr = false
|
|
||||||
const list = this.resultListFromLast || []
|
|
||||||
list.forEach(s => {
|
|
||||||
if (typeof s !== 'string') return
|
|
||||||
if (s.includes('ERROR_CODE:ORDER_NUMBER_DUPLICATE')) hasOrder = true
|
|
||||||
if (s.includes('ERROR_CODE:ADDRESS_DUPLICATE')) hasAddr = true
|
|
||||||
})
|
|
||||||
if (hasOrder && hasAddr) {
|
|
||||||
this.verifyMessage = '检测到订单编号与地址可能重复,输入验证码后可强制录单'
|
|
||||||
} else if (hasOrder) {
|
|
||||||
this.verifyMessage = '检测到订单编号可能重复,输入验证码后可强制录单'
|
|
||||||
} else {
|
|
||||||
this.verifyMessage = '检测到地址可能重复,输入验证码后可强制录单'
|
|
||||||
}
|
|
||||||
this.verifyDialogVisible = true
|
|
||||||
},
|
|
||||||
async generate() {
|
|
||||||
const cmd = this.buildCommand()
|
|
||||||
if (!cmd) return
|
|
||||||
this.loading = true
|
|
||||||
this.resultText = ''
|
|
||||||
try {
|
|
||||||
const res = await executeInstruction({ command: cmd })
|
|
||||||
if (!(res && (res.code === 200 || res.msg === '操作成功'))) {
|
|
||||||
this.$modal.msgError((res && res.msg) || '生成表单失败')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const list = Array.isArray(res.data) ? res.data : (res.data ? [String(res.data)] : [])
|
|
||||||
if (this.checkDuplicateError(list)) {
|
|
||||||
this.resultListFromLast = list
|
|
||||||
this.showVerifyDialog(cmd)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const rawForm = this.extractFirstResponse(res.data)
|
|
||||||
const merged = this.injectOptional(rawForm)
|
|
||||||
if (!this.formBodyLooksPersistable(merged)) {
|
|
||||||
this.resultText = merged
|
|
||||||
this.$modal.msgError('生成结果不是可落库表单,请重试或联系管理员')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.canPersistOrderFields()) {
|
|
||||||
this.resultText = merged
|
|
||||||
this.$modal.msgWarning('已生成录单文案。落库需填写:下单人、下单付款、订单号、物流链接(后返可不填,将按 0.00)')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res2 = await executeInstruction({ command: merged })
|
|
||||||
if (!(res2 && (res2.code === 200 || res2.msg === '操作成功'))) {
|
|
||||||
this.$modal.msgError((res2 && res2.msg) || '落库失败')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.applyPersistResult(merged, this.formatInstructionData(res2.data))
|
|
||||||
} catch (e) {
|
|
||||||
this.$modal.msgError('请求失败')
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async handleVerify() {
|
|
||||||
if (!this.verifyInput || this.verifyInput.length !== 4) {
|
|
||||||
this.$modal.msgError('请输入四位验证码')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.verifyInput !== this.verifyCode) {
|
|
||||||
this.$modal.msgError('验证码不正确')
|
|
||||||
this.verifyInput = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.verifyLoading = true
|
|
||||||
try {
|
|
||||||
const res = await executeInstructionWithForce({ command: this.pendingCommand })
|
|
||||||
if (!(res && (res.code === 200 || res.msg === '操作成功'))) {
|
|
||||||
this.$modal.msgError((res && res.msg) || '执行失败')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const merged = this.injectOptional(this.extractFirstResponse(res.data))
|
|
||||||
if (!this.formBodyLooksPersistable(merged)) {
|
|
||||||
this.resultText = merged
|
|
||||||
this.verifyDialogVisible = false
|
|
||||||
this.$modal.msgError('生成结果不是可落库表单')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.canPersistOrderFields()) {
|
|
||||||
this.resultText = merged
|
|
||||||
this.verifyDialogVisible = false
|
|
||||||
this.$modal.msgWarning('已强制生成表单。落库仍需填写:下单人、下单付款、订单号、物流链接')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res2 = await executeInstruction({ command: merged })
|
|
||||||
this.verifyDialogVisible = false
|
|
||||||
if (!(res2 && (res2.code === 200 || res2.msg === '操作成功'))) {
|
|
||||||
this.$modal.msgError((res2 && res2.msg) || '落库失败')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.applyPersistResult(merged, this.formatInstructionData(res2.data))
|
|
||||||
} catch (e) {
|
|
||||||
this.$modal.msgError('请求失败')
|
|
||||||
} finally {
|
|
||||||
this.verifyLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetForm() {
|
|
||||||
this.form = {
|
|
||||||
mark: 'F',
|
|
||||||
model: '',
|
|
||||||
address: '',
|
|
||||||
link: '',
|
|
||||||
qty: 1,
|
|
||||||
buyer: '',
|
|
||||||
paymentText: '',
|
|
||||||
rebateText: '',
|
|
||||||
orderIdText: '',
|
|
||||||
logisticsLink: ''
|
|
||||||
}
|
|
||||||
this.resultText = ''
|
|
||||||
},
|
|
||||||
copyResult() {
|
|
||||||
const t = this.resultText || ''
|
|
||||||
if (!t) return
|
|
||||||
if (navigator.clipboard) {
|
|
||||||
navigator.clipboard.writeText(t).then(() => {
|
|
||||||
this.$modal.msgSuccess('已复制')
|
|
||||||
}).catch(() => this.fallbackCopy(t))
|
|
||||||
} else {
|
|
||||||
this.fallbackCopy(t)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fallbackCopy(text) {
|
|
||||||
const ta = document.createElement('textarea')
|
|
||||||
ta.value = text
|
|
||||||
document.body.appendChild(ta)
|
|
||||||
ta.focus()
|
|
||||||
ta.select()
|
|
||||||
try {
|
|
||||||
document.execCommand('copy')
|
|
||||||
this.$modal.msgSuccess('已复制')
|
|
||||||
} catch (e) {
|
|
||||||
this.$modal.msgError('复制失败')
|
|
||||||
}
|
|
||||||
document.body.removeChild(ta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.mobile-fadan {
|
|
||||||
padding-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-card {
|
|
||||||
margin: 0 12px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fadan-form ::v-deep .el-form-item {
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-row {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-area ::v-deep .el-textarea__inner {
|
|
||||||
font-family: Consolas, 'Courier New', monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verify-body .verify-code {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 26px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #409eff;
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.box-card {
|
|
||||||
margin: 0 8px 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
409
src/views/system/jd-instruction/fadan-quick-record.vue
Normal file
409
src/views/system/jd-instruction/fadan-quick-record.vue
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container fadan-quick-record" :class="{ 'fadan-quick-record--mobile': isMobile }">
|
||||||
|
<el-card class="box-card">
|
||||||
|
<el-form :model="form" label-position="top" class="fadan-form">
|
||||||
|
<el-form-item label="分销标记(默认 F)">
|
||||||
|
<el-input v-model="form.mark" placeholder="一般为 F" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="型号" required>
|
||||||
|
<el-input v-model="form.model" placeholder="必填" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下单地址" required>
|
||||||
|
<el-input v-model="form.address" type="textarea" :rows="3" placeholder="必填,与中控「生」指令第 6 行一致" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="转链链接(可空)">
|
||||||
|
<el-input v-model="form.link" type="textarea" :rows="2" placeholder="可空;留空则后台按型号尝试自动填充京粉链接" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数量">
|
||||||
|
<el-input-number v-model="form.qty" :min="1" :max="99" controls-position="right" :style="qtyInputStyle" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-divider content-position="left">落库必填(与中控录单一致)</el-divider>
|
||||||
|
<el-form-item label="下单人">
|
||||||
|
<el-input v-model="form.buyer" placeholder="落库必填" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下单付款(元)">
|
||||||
|
<el-input v-model="form.paymentText" placeholder="落库必填,如 2999.00" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="后返金额(元)">
|
||||||
|
<el-input v-model="form.rebateText" placeholder="不写则按 0.00 落库" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="订单号(京东)">
|
||||||
|
<el-input v-model="form.orderIdText" placeholder="落库必填,与表单「订单号(需填)」一致" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="物流链接">
|
||||||
|
<el-input v-model="form.logisticsLink" type="textarea" :rows="2" placeholder="落库必填" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<div class="btn-row">
|
||||||
|
<el-button type="primary" size="medium" :loading="loading" @click="generate">录单</el-button>
|
||||||
|
<el-button size="medium" @click="resetForm">重置表单</el-button>
|
||||||
|
<el-button v-if="resultText" size="medium" @click="copyResult">复制录单</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template v-if="resultText">
|
||||||
|
<el-divider content-position="left">录单结果</el-divider>
|
||||||
|
<el-input v-model="resultText" type="textarea" :rows="isMobile ? 16 : 20" readonly class="result-area" />
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
title="地址/单号重复验证"
|
||||||
|
:visible.sync="verifyDialogVisible"
|
||||||
|
:width="dialogWidth"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<div class="verify-body">
|
||||||
|
<el-alert :title="verifyMessage" type="warning" :closable="false" />
|
||||||
|
<div class="verify-code">{{ verifyCode }}</div>
|
||||||
|
<el-input v-model="verifyInput" placeholder="请输入上方四位验证码" maxlength="4" @keyup.enter.native="handleVerify" />
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="verifyDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="verifyLoading" @click="handleVerify">确认录单</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { executeInstruction, executeInstructionWithForce } from '@/api/system/instruction'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FadanQuickRecord',
|
||||||
|
props: {
|
||||||
|
isMobile: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
mark: 'F',
|
||||||
|
model: '',
|
||||||
|
address: '',
|
||||||
|
link: '',
|
||||||
|
qty: 1,
|
||||||
|
buyer: '',
|
||||||
|
paymentText: '',
|
||||||
|
rebateText: '',
|
||||||
|
orderIdText: '',
|
||||||
|
logisticsLink: ''
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
resultText: '',
|
||||||
|
resultListFromLast: [],
|
||||||
|
verifyDialogVisible: false,
|
||||||
|
verifyCode: '',
|
||||||
|
verifyInput: '',
|
||||||
|
verifyMessage: '',
|
||||||
|
verifyLoading: false,
|
||||||
|
pendingCommand: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dialogWidth() {
|
||||||
|
return this.isMobile ? '92%' : '480px'
|
||||||
|
},
|
||||||
|
qtyInputStyle() {
|
||||||
|
return this.isMobile
|
||||||
|
? { width: '100%' }
|
||||||
|
: { width: '200px' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
buildCommand() {
|
||||||
|
const mark = (this.form.mark || 'F').trim()
|
||||||
|
const model = (this.form.model || '').trim()
|
||||||
|
const address = (this.form.address || '').trim()
|
||||||
|
if (!model) {
|
||||||
|
this.$modal.msgError('请填写型号')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (!address) {
|
||||||
|
this.$modal.msgError('请填写地址')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const link = (this.form.link || '').trim()
|
||||||
|
const qty = Number(this.form.qty) > 0 ? Number(this.form.qty) : 1
|
||||||
|
const lines = ['生', mark, model, link, String(qty), address]
|
||||||
|
return lines.join('\n')
|
||||||
|
},
|
||||||
|
injectOptional(raw) {
|
||||||
|
if (!raw) return raw
|
||||||
|
let t = String(raw).replace(/\r\n/g, '\n')
|
||||||
|
const buyer = (this.form.buyer || '').trim()
|
||||||
|
const pay = (this.form.paymentText || '').trim()
|
||||||
|
const rebate = (this.form.rebateText || '').trim()
|
||||||
|
const orderId = (this.form.orderIdText || '').trim()
|
||||||
|
const logistics = (this.form.logisticsLink || '').trim()
|
||||||
|
if (buyer) {
|
||||||
|
t = t.replace(/(下单人(需填):)\n\n/, `$1\n${buyer}\n\n`)
|
||||||
|
}
|
||||||
|
if (pay) {
|
||||||
|
t = t.replace(/(下单付款(注意核对):)\n\n/, `$1\n${pay}\n\n`)
|
||||||
|
}
|
||||||
|
if (rebate) {
|
||||||
|
t = t.replace(/(后返金额(注意核对):)\n\n/, `$1\n${rebate}\n\n`)
|
||||||
|
} else {
|
||||||
|
t = t.replace(/(后返金额(注意核对):)\n\n/, `$1\n0.00\n\n`)
|
||||||
|
}
|
||||||
|
if (orderId) {
|
||||||
|
t = t.replace(/(订单号(需填):)\n\n/, `$1\n${orderId}\n\n`)
|
||||||
|
}
|
||||||
|
if (logistics) {
|
||||||
|
t = t.replace(/(物流链接(需填):)\n\n/, `$1\n${logistics}\n\n`)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
},
|
||||||
|
formBodyLooksPersistable(text) {
|
||||||
|
const t = (text || '').trim()
|
||||||
|
return t.startsWith('单')
|
||||||
|
},
|
||||||
|
canPersistOrderFields() {
|
||||||
|
const buyer = (this.form.buyer || '').trim()
|
||||||
|
const pay = (this.form.paymentText || '').trim()
|
||||||
|
const orderId = (this.form.orderIdText || '').trim()
|
||||||
|
const logistics = (this.form.logisticsLink || '').trim()
|
||||||
|
return !!(buyer && pay && orderId && logistics)
|
||||||
|
},
|
||||||
|
formatInstructionData(data) {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return data.length ? data.join('\n\n') : ''
|
||||||
|
}
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
isPersistFailureText(text) {
|
||||||
|
const s = String(text || '')
|
||||||
|
return s.includes('[炸弹]') || s.includes('录单警告')
|
||||||
|
},
|
||||||
|
applyPersistResult(mergedBody, persistMsg) {
|
||||||
|
const msg = persistMsg || ''
|
||||||
|
this.resultText = (mergedBody || '') + '\n\n--- 落库回执 ---\n' + msg
|
||||||
|
if (this.isPersistFailureText(msg)) {
|
||||||
|
this.$modal.msgError('落库未成功,请查看回执')
|
||||||
|
} else {
|
||||||
|
this.$modal.msgSuccess('已生成表单并落库')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extractFirstResponse(data) {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return data.length ? String(data[0]) : ''
|
||||||
|
}
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
checkDuplicateError(resultList) {
|
||||||
|
if (!resultList || !resultList.length) return false
|
||||||
|
for (let i = 0; i < resultList.length; i++) {
|
||||||
|
const s = resultList[i]
|
||||||
|
if (typeof s === 'string' && (
|
||||||
|
s.includes('ERROR_CODE:ADDRESS_DUPLICATE') ||
|
||||||
|
s.includes('ERROR_CODE:ORDER_NUMBER_DUPLICATE')
|
||||||
|
)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
showVerifyDialog(cmd) {
|
||||||
|
this.pendingCommand = cmd
|
||||||
|
this.verifyCode = String(Math.floor(1000 + Math.random() * 9000))
|
||||||
|
this.verifyInput = ''
|
||||||
|
let hasOrder = false
|
||||||
|
let hasAddr = false
|
||||||
|
const list = this.resultListFromLast || []
|
||||||
|
list.forEach(s => {
|
||||||
|
if (typeof s !== 'string') return
|
||||||
|
if (s.includes('ERROR_CODE:ORDER_NUMBER_DUPLICATE')) hasOrder = true
|
||||||
|
if (s.includes('ERROR_CODE:ADDRESS_DUPLICATE')) hasAddr = true
|
||||||
|
})
|
||||||
|
if (hasOrder && hasAddr) {
|
||||||
|
this.verifyMessage = '检测到订单编号与地址可能重复,输入验证码后可强制录单'
|
||||||
|
} else if (hasOrder) {
|
||||||
|
this.verifyMessage = '检测到订单编号可能重复,输入验证码后可强制录单'
|
||||||
|
} else {
|
||||||
|
this.verifyMessage = '检测到地址可能重复,输入验证码后可强制录单'
|
||||||
|
}
|
||||||
|
this.verifyDialogVisible = true
|
||||||
|
},
|
||||||
|
async generate() {
|
||||||
|
const cmd = this.buildCommand()
|
||||||
|
if (!cmd) return
|
||||||
|
this.loading = true
|
||||||
|
this.resultText = ''
|
||||||
|
try {
|
||||||
|
const res = await executeInstruction({ command: cmd })
|
||||||
|
if (!(res && (res.code === 200 || res.msg === '操作成功'))) {
|
||||||
|
this.$modal.msgError((res && res.msg) || '生成表单失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const list = Array.isArray(res.data) ? res.data : (res.data ? [String(res.data)] : [])
|
||||||
|
if (this.checkDuplicateError(list)) {
|
||||||
|
this.resultListFromLast = list
|
||||||
|
this.showVerifyDialog(cmd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const rawForm = this.extractFirstResponse(res.data)
|
||||||
|
const merged = this.injectOptional(rawForm)
|
||||||
|
if (!this.formBodyLooksPersistable(merged)) {
|
||||||
|
this.resultText = merged
|
||||||
|
this.$modal.msgError('生成结果不是可落库表单,请重试或联系管理员')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.canPersistOrderFields()) {
|
||||||
|
this.resultText = merged
|
||||||
|
this.$modal.msgWarning('已生成录单文案。落库需填写:下单人、下单付款、订单号、物流链接(后返可不填,将按 0.00)')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res2 = await executeInstruction({ command: merged })
|
||||||
|
if (!(res2 && (res2.code === 200 || res2.msg === '操作成功'))) {
|
||||||
|
this.$modal.msgError((res2 && res2.msg) || '落库失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.applyPersistResult(merged, this.formatInstructionData(res2.data))
|
||||||
|
} catch (e) {
|
||||||
|
this.$modal.msgError('请求失败')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handleVerify() {
|
||||||
|
if (!this.verifyInput || this.verifyInput.length !== 4) {
|
||||||
|
this.$modal.msgError('请输入四位验证码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.verifyInput !== this.verifyCode) {
|
||||||
|
this.$modal.msgError('验证码不正确')
|
||||||
|
this.verifyInput = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.verifyLoading = true
|
||||||
|
try {
|
||||||
|
const res = await executeInstructionWithForce({ command: this.pendingCommand })
|
||||||
|
if (!(res && (res.code === 200 || res.msg === '操作成功'))) {
|
||||||
|
this.$modal.msgError((res && res.msg) || '执行失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const merged = this.injectOptional(this.extractFirstResponse(res.data))
|
||||||
|
if (!this.formBodyLooksPersistable(merged)) {
|
||||||
|
this.resultText = merged
|
||||||
|
this.verifyDialogVisible = false
|
||||||
|
this.$modal.msgError('生成结果不是可落库表单')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.canPersistOrderFields()) {
|
||||||
|
this.resultText = merged
|
||||||
|
this.verifyDialogVisible = false
|
||||||
|
this.$modal.msgWarning('已强制生成表单。落库仍需填写:下单人、下单付款、订单号、物流链接')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res2 = await executeInstruction({ command: merged })
|
||||||
|
this.verifyDialogVisible = false
|
||||||
|
if (!(res2 && (res2.code === 200 || res2.msg === '操作成功'))) {
|
||||||
|
this.$modal.msgError((res2 && res2.msg) || '落库失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.applyPersistResult(merged, this.formatInstructionData(res2.data))
|
||||||
|
} catch (e) {
|
||||||
|
this.$modal.msgError('请求失败')
|
||||||
|
} finally {
|
||||||
|
this.verifyLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.form = {
|
||||||
|
mark: 'F',
|
||||||
|
model: '',
|
||||||
|
address: '',
|
||||||
|
link: '',
|
||||||
|
qty: 1,
|
||||||
|
buyer: '',
|
||||||
|
paymentText: '',
|
||||||
|
rebateText: '',
|
||||||
|
orderIdText: '',
|
||||||
|
logisticsLink: ''
|
||||||
|
}
|
||||||
|
this.resultText = ''
|
||||||
|
},
|
||||||
|
copyResult() {
|
||||||
|
const t = this.resultText || ''
|
||||||
|
if (!t) return
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(t).then(() => {
|
||||||
|
this.$modal.msgSuccess('已复制')
|
||||||
|
}).catch(() => this.fallbackCopy(t))
|
||||||
|
} else {
|
||||||
|
this.fallbackCopy(t)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fallbackCopy(text) {
|
||||||
|
const ta = document.createElement('textarea')
|
||||||
|
ta.value = text
|
||||||
|
document.body.appendChild(ta)
|
||||||
|
ta.focus()
|
||||||
|
ta.select()
|
||||||
|
try {
|
||||||
|
document.execCommand('copy')
|
||||||
|
this.$modal.msgSuccess('已复制')
|
||||||
|
} catch (e) {
|
||||||
|
this.$modal.msgError('复制失败')
|
||||||
|
}
|
||||||
|
document.body.removeChild(ta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.fadan-quick-record {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadan-quick-record--mobile .box-card {
|
||||||
|
margin: 0 12px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadan-quick-record:not(.fadan-quick-record--mobile) .box-card {
|
||||||
|
max-width: 960px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadan-form ::v-deep .el-form-item {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-area ::v-deep .el-textarea__inner {
|
||||||
|
font-family: Consolas, 'Courier New', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verify-body .verify-code {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #409eff;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.fadan-quick-record--mobile .box-card {
|
||||||
|
margin: 0 8px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user