This commit is contained in:
van
2026-09-06 20:33:15 +08:00
parent dd4832f653
commit 0c2cbc1b3e
@@ -111,12 +111,30 @@
<template v-if="resultText"> <template v-if="resultText">
<el-divider content-position="left">录单结果</el-divider> <el-divider content-position="left">录单结果</el-divider>
<p v-if="resultPieces.length > 1" class="result-pieces-hint">按条复制,每条对应一条聊天消息。</p> <p class="result-pieces-hint">
点选「录单表单」或点「回填表单」,会把内容填回上方,方便改完再录单。
<template v-if="resultPieces.length > 1">按条复制,每条对应一条聊天消息。</template>
</p>
<div class="result-pieces"> <div class="result-pieces">
<div v-for="(p, i) in resultPieces" :key="i" class="result-piece"> <div
v-for="(p, i) in resultPieces"
:key="i"
class="result-piece"
:class="{ 'is-form': isFormPiece(p), 'is-selected': selectedPieceIndex === i }"
@click="onResultPieceClick(p, i)"
>
<div class="result-piece-head"> <div class="result-piece-head">
<span class="result-piece-title">{{ p.title }}</span> <span class="result-piece-title">{{ p.title }}</span>
<el-button type="primary" plain size="mini" @click="copyPieceText(p.text)">复制本条</el-button> <div class="result-piece-actions">
<el-button
v-if="isFormPiece(p)"
type="success"
plain
size="mini"
@click.native.stop="echoFormPiece(p, i)"
>回填表单</el-button>
<el-button type="primary" plain size="mini" @click.native.stop="copyPieceText(p.text)">复制本条</el-button>
</div>
</div> </div>
<el-input <el-input
:value="p.text" :value="p.text"
@@ -124,6 +142,7 @@
:autosize="{ minRows: resultPieceMinRows(p.text), maxRows: isMobile ? 22 : 28 }" :autosize="{ minRows: resultPieceMinRows(p.text), maxRows: isMobile ? 22 : 28 }"
readonly readonly
class="result-area" class="result-area"
@click.native.stop
/> />
</div> </div>
</div> </div>
@@ -201,7 +220,8 @@ export default {
pendingCommand: '', pendingCommand: '',
modelOptions: [], modelOptions: [],
shopOptions: [], shopOptions: [],
showShopConfig: false showShopConfig: false,
selectedPieceIndex: null
} }
}, },
mounted() { mounted() {
@@ -250,6 +270,119 @@ export default {
} }
return out return out
}, },
isFormPiece(p) {
if (!p) return false
if (p.title && String(p.title).includes('录单表单')) return true
return this.formBodyLooksPersistable(p.text)
},
extractFormBody(full) {
const raw = String(full || '').replace(/\r\n/g, '\n').trim()
if (!raw) return ''
const sep = /\n-{3,}\s*落库回执\s*-{3,}\s*\n/
const m = sep.exec(raw)
return m ? raw.slice(0, m.index).trim() : raw
},
extractFormField(text, startLabel, endLabels) {
const src = String(text || '')
const idx = src.indexOf(startLabel)
if (idx < 0) return ''
let rest = src.slice(idx + startLabel.length)
let end = rest.length
;(endLabels || []).forEach(lab => {
if (!lab) return
const i = rest.indexOf(lab)
if (i >= 0 && i < end) end = i
})
return rest
.slice(0, end)
.replace(/[━—\-]{3,}/g, '')
.replace(/\n{2,}/g, '\n')
.trim()
},
parseOrderFormText(text) {
const raw = String(text || '').replace(/\r\n/g, '\n')
const isNew = raw.includes('下单人(需填):')
|| raw.includes('下单链接(必须用这个):')
|| raw.includes('—————————')
const field = (start, ends) => this.extractFormField(raw, start, ends)
if (isNew) {
return {
mark: field('分销标记:', ['第三方单号:', '—————————', '下单链接(必须用这个):']),
thirdParty: field('第三方单号:', ['—————————', '下单链接(必须用这个):']),
link: field('下单链接(必须用这个):', ['下单地址(注意带分机):', '—————————']),
address: field('下单地址(注意带分机):', ['—————————', '型号:']),
model: field('型号:', ['下单人(需填):', '下单付款(注意核对):']),
buyer: field('下单人(需填):', ['下单付款(注意核对):']),
payment: field('下单付款(注意核对):', ['后返金额(注意核对):']),
rebate: field('后返金额(注意核对):', ['订单号(需填):']),
orderId: field('订单号(需填):', ['物流链接(需填):']),
logistics: field('物流链接(需填):', ['备注(下单号码有变动', '—————————', '京粉实际价格'])
}
}
return {
mark: field('分销标记:', ['第三方单号:', '型号:']),
thirdParty: field('第三方单号:', ['型号:']),
model: field('型号:', ['链接:']),
link: field('链接:', ['下单付款:']),
payment: field('下单付款:', ['后返金额:']),
rebate: field('后返金额:', ['地址:']),
address: field('地址:', ['物流链接:']),
logistics: field('物流链接:', ['订单号:']),
orderId: field('订单号:', ['下单人:']),
buyer: field('下单人:', ['京粉实际价格:'])
}
},
applyMarkSuffixFromText(mark) {
const v = String(mark || '').trim()
if (!v || v === 'F') {
this.form.markSuffix = ''
return
}
if (v.startsWith('F-')) {
this.form.markSuffix = v.slice(2).trim()
return
}
if (v.startsWith('F') && v.length > 1) {
this.form.markSuffix = v.slice(1).replace(/^-/, '').trim()
return
}
this.form.markSuffix = v
},
applyGeneratedFormToFields(text, silent) {
const body = this.extractFormBody(text)
if (!this.formBodyLooksPersistable(body)) {
if (!silent) this.$modal.msgWarning('当前内容不是可回填的录单表单')
return false
}
const parsed = this.parseOrderFormText(body)
this.applyMarkSuffixFromText(parsed.mark)
this.applyModelWithShop(String(parsed.model || '').split('\n')[0].trim())
this.form.address = this.compressAddressOneLine(parsed.address)
this.form.link = String(parsed.link || '').trim()
this.form.thirdPartyOrderNoText = String(parsed.thirdParty || '').trim()
this.form.buyer = String(parsed.buyer || '').trim()
this.form.paymentText = String(parsed.payment || '').trim()
this.form.rebateText = String(parsed.rebate || '').trim()
this.form.orderIdText = String(parsed.orderId || '').trim()
this.form.logisticsLink = String(parsed.logistics || '').trim()
this.selectedPieceIndex = 0
if (!silent) this.$modal.msgSuccess('已回填到表单,可继续编辑后录单')
return true
},
echoFormPiece(p, i) {
if (!this.applyGeneratedFormToFields(p && p.text)) return
this.selectedPieceIndex = i
this.$nextTick(() => {
const formEl = this.$el && this.$el.querySelector('.fadan-form')
if (formEl && formEl.scrollIntoView) {
formEl.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
})
},
onResultPieceClick(p, i) {
if (!this.isFormPiece(p)) return
this.echoFormPiece(p, i)
},
resultPieceMinRows(text) { resultPieceMinRows(text) {
const n = String(text || '').split('\n').length const n = String(text || '').split('\n').length
const cap = this.isMobile ? 8 : 6 const cap = this.isMobile ? 8 : 6
@@ -501,6 +634,7 @@ export default {
applyPersistResult(mergedBody, persistMsg) { applyPersistResult(mergedBody, persistMsg) {
const msg = persistMsg || '' const msg = persistMsg || ''
this.resultText = (mergedBody || '') + '\n\n--- 落库回执 ---\n' + msg this.resultText = (mergedBody || '') + '\n\n--- 落库回执 ---\n' + msg
this.applyGeneratedFormToFields(mergedBody, true)
if (this.isPersistFailureText(msg)) { if (this.isPersistFailureText(msg)) {
this.$modal.msgError('落库未成功,请查看回执') this.$modal.msgError('落库未成功,请查看回执')
} else { } else {
@@ -576,7 +710,8 @@ export default {
} }
if (!this.canPersistOrderFields()) { if (!this.canPersistOrderFields()) {
this.resultText = merged this.resultText = merged
this.$modal.msgWarning('已生成录单文案。落库需填写:下单人、下单付款、订单号、物流链接(后返可不填,将按 0.00)') this.applyGeneratedFormToFields(merged, true)
this.$modal.msgWarning('已生成录单文案并回填。请补全下单人、下单付款、订单号、物流链接后再次录单')
return return
} }
const res2 = await executeInstruction({ command: merged }) const res2 = await executeInstruction({ command: merged })
@@ -617,8 +752,9 @@ export default {
} }
if (!this.canPersistOrderFields()) { if (!this.canPersistOrderFields()) {
this.resultText = merged this.resultText = merged
this.applyGeneratedFormToFields(merged, true)
this.verifyDialogVisible = false this.verifyDialogVisible = false
this.$modal.msgWarning('已强制生成表单。落库仍需填写:下单人、下单付款、订单号、物流链接') this.$modal.msgWarning('已强制生成并回填。请补全下单人、下单付款、订单号、物流链接后再次录单')
return return
} }
const res2 = await executeInstruction({ command: merged }) const res2 = await executeInstruction({ command: merged })
@@ -649,6 +785,7 @@ export default {
logisticsLink: '' logisticsLink: ''
} }
this.resultText = '' this.resultText = ''
this.selectedPieceIndex = null
}, },
copyResult() { copyResult() {
const t = this.resultText || '' const t = this.resultText || ''
@@ -777,6 +914,8 @@ export default {
.result-area ::v-deep .el-textarea__inner { .result-area ::v-deep .el-textarea__inner {
font-family: Consolas, 'Courier New', monospace; font-family: Consolas, 'Courier New', monospace;
font-size: 13px; font-size: 13px;
user-select: text;
-webkit-user-select: text;
} }
.result-pieces-hint { .result-pieces-hint {
@@ -805,6 +944,27 @@ export default {
font-size: 14px; font-size: 14px;
} }
.result-piece-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 6px;
flex-shrink: 0;
}
.result-piece.is-form {
padding: 10px;
border: 1px solid #d9ecff;
border-radius: 10px;
background: #f7fbff;
cursor: pointer;
}
.result-piece.is-form.is-selected {
border-color: #67c23a;
background: #f0f9eb;
}
.verify-body .verify-code { .verify-body .verify-code {
text-align: center; text-align: center;
font-size: 26px; font-size: 26px;