This commit is contained in:
van
2026-09-06 20:48:28 +08:00
parent 629d0e8e07
commit c34abb73c2
@@ -168,29 +168,33 @@
<el-input <el-input
v-model="historyKeyword" v-model="historyKeyword"
clearable clearable
placeholder="搜型号、地址、下单人、订单号、分销标记" placeholder="搜型号、地址、下单人、订单号、分销标记;待填写在前"
prefix-icon="el-icon-search" prefix-icon="el-icon-search"
@keyup.enter.native="searchHistory" @keyup.enter.native="searchHistory"
@clear="searchHistory" @clear="searchHistory"
/> />
<el-button type="primary" :loading="historyLoading" @click="searchHistory">搜索</el-button> <el-button type="primary" :loading="historyLoading" @click="searchHistory">搜索</el-button>
</div> </div>
<p class="history-hint">待填写(生成后未补全)排在前面,已落库记录也保留。</p>
<div class="history-list" v-loading="historyLoading"> <div class="history-list" v-loading="historyLoading">
<div v-if="!historyList.length && !historyLoading" class="history-empty">暂无历史记录</div> <div v-if="!historyList.length && !historyLoading" class="history-empty">暂无历史记录</div>
<div <div
v-for="row in historyList" v-for="row in historyList"
:key="row.id" :key="row.key"
class="history-item" class="history-item"
@click="applyHistoryOrder(row)" :class="{ 'is-unfilled': !row.filled }"
@click="applyHistoryItem(row)"
> >
<div class="history-item-top"> <div class="history-item-top">
<span class="history-model">{{ historyFullModel(row) || '无型号' }}</span> <span class="history-model">{{ historyItemModel(row) || '无型号' }}</span>
<span class="history-time">{{ formatHistoryTime(row) }}</span> <span class="history-time">{{ formatHistoryTime(row) }}</span>
</div> </div>
<div class="history-item-meta"> <div class="history-item-meta">
<el-tag v-if="!row.filled" type="warning" size="mini">待填写</el-tag>
<el-tag v-else type="success" size="mini">已落库</el-tag>
<span v-if="row.distributionMark">{{ row.distributionMark }}</span> <span v-if="row.distributionMark">{{ row.distributionMark }}</span>
<span v-if="row.buyer">{{ row.buyer }}</span> <span v-if="row.buyer">{{ row.buyer }}</span>
<span v-if="row.paymentAmount != null">付款 {{ formatMoneyText(row.paymentAmount) }}</span> <span v-if="row.paymentAmount != null && row.paymentAmount !== ''">付款 {{ formatHistoryMoney(row.paymentAmount) }}</span>
<span v-if="row.orderId">{{ row.orderId }}</span> <span v-if="row.orderId">{{ row.orderId }}</span>
</div> </div>
<div v-if="row.address" class="history-item-addr">{{ row.address }}</div> <div v-if="row.address" class="history-item-addr">{{ row.address }}</div>
@@ -204,7 +208,7 @@
:page-size="historyPageSize" :page-size="historyPageSize"
:total="historyTotal" :total="historyTotal"
:pager-count="isMobile ? 5 : 7" :pager-count="isMobile ? 5 : 7"
@current-change="loadHistory" @current-change="sliceHistoryPage"
/> />
</div> </div>
</el-dialog> </el-dialog>
@@ -218,7 +222,7 @@
</template> </template>
<script> <script>
import { executeInstruction, executeInstructionWithForce } from '@/api/system/instruction' import { executeInstruction, executeInstructionWithForce, getHistory } from '@/api/system/instruction'
import { listJDOrders, listQuickRecordModelOptions, listQuickRecordShopOptions } from '@/api/system/jdorder' import { listJDOrders, listQuickRecordModelOptions, listQuickRecordShopOptions } from '@/api/system/jdorder'
import QuickRecordModelShopConfig from './components/QuickRecordModelShopConfig.vue' import QuickRecordModelShopConfig from './components/QuickRecordModelShopConfig.vue'
@@ -261,6 +265,7 @@ export default {
historyVisible: false, historyVisible: false,
historyLoading: false, historyLoading: false,
historyKeyword: '', historyKeyword: '',
historyAll: [],
historyList: [], historyList: [],
historyPage: 1, historyPage: 1,
historyPageSize: 15, historyPageSize: 15,
@@ -720,20 +725,200 @@ export default {
this.historyPage = 1 this.historyPage = 1
this.loadHistory() this.loadHistory()
}, },
sliceHistoryPage() {
const start = (this.historyPage - 1) * this.historyPageSize
this.historyList = this.historyAll.slice(start, start + this.historyPageSize)
},
extractHistoryParts(item) {
const raw = String(item || '')
const idx = raw.indexOf(' | ')
return {
time: idx > 0 ? raw.slice(0, idx) : '',
text: idx > 0 ? raw.slice(idx + 3) : raw
}
},
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 {
remark: field('单:', ['分销标记:']),
mark: field('分销标记:', ['第三方单号:', '—————————', '下单链接(必须用这个):']),
thirdParty: field('第三方单号:', ['—————————', '下单链接(必须用这个):']),
link: field('下单链接(必须用这个):', ['下单地址(注意带分机):', '—————————']),
address: field('下单地址(注意带分机):', ['—————————', '型号:']),
model: field('型号:', ['下单人(需填):', '下单付款(注意核对):']),
buyer: field('下单人(需填):', ['下单付款(注意核对):']),
payment: field('下单付款(注意核对):', ['后返金额(注意核对):']),
rebate: field('后返金额(注意核对):', ['订单号(需填):']),
orderId: field('订单号(需填):', ['物流链接(需填):']),
logistics: field('物流链接(需填):', ['备注(下单号码有变动', '—————————', '京粉实际价格'])
}
}
return {
remark: field('单:', ['备注:', '分销标记:']),
mark: field('分销标记:', ['第三方单号:', '型号:']),
thirdParty: field('第三方单号:', ['型号:']),
model: field('型号:', ['链接:']),
link: field('链接:', ['下单付款:']),
payment: field('下单付款:', ['后返金额:']),
rebate: field('后返金额:', ['地址:']),
address: field('地址:', ['物流链接:']),
logistics: field('物流链接:', ['订单号:']),
orderId: field('订单号:', ['下单人:']),
buyer: field('下单人:', ['京粉实际价格:'])
}
},
isPersistFilledText(parsed) {
if (!parsed) return false
return !!(
String(parsed.buyer || '').trim()
&& String(parsed.payment || '').trim()
&& String(parsed.orderId || '').trim()
&& String(parsed.logistics || '').trim()
)
},
isPersistFilledOrder(row) {
if (!row) return false
return !!(
String(row.buyer || '').trim()
&& row.paymentAmount != null && row.paymentAmount !== ''
&& String(row.orderId || '').trim()
&& String(row.logisticsLink || '').trim()
)
},
historyItemMatchesKeyword(item, kw) {
if (!kw) return true
const blob = [
item.modelNumber,
item.modelShop,
this.historyItemModel(item),
item.address,
item.buyer,
item.orderId,
item.distributionMark,
item.thirdPartyOrderNo,
item.remark
].join(' ').toLowerCase()
return blob.includes(kw.toLowerCase())
},
normalizeDraftItem(rawItem) {
const parts = this.extractHistoryParts(rawItem)
const body = this.extractFormBody(parts.text)
if (!this.formBodyLooksPersistable(body)) return null
const parsed = this.parseOrderFormText(body)
const modelFull = String(parsed.model || '').split('\n')[0].trim()
const shopParsed = this.parseModelWithShop(modelFull)
return {
key: 'draft-' + (parsed.remark || parts.time || modelFull),
source: 'draft',
filled: this.isPersistFilledText(parsed),
remark: String(parsed.remark || '').trim(),
distributionMark: String(parsed.mark || '').trim(),
modelNumber: shopParsed.base || modelFull,
modelShop: shopParsed.prefix,
address: this.compressAddressOneLine(parsed.address),
link: String(parsed.link || '').trim(),
thirdPartyOrderNo: String(parsed.thirdParty || '').trim(),
buyer: String(parsed.buyer || '').trim(),
paymentAmount: String(parsed.payment || '').trim(),
rebateAmount: String(parsed.rebate || '').trim(),
orderId: String(parsed.orderId || '').trim(),
logisticsLink: String(parsed.logistics || '').trim(),
orderTime: parts.time,
rawText: body
}
},
normalizeOrderItem(row) {
if (!row) return null
return {
key: 'order-' + (row.id || row.remark || row.orderId),
source: 'order',
filled: this.isPersistFilledOrder(row),
remark: row.remark || '',
distributionMark: row.distributionMark || '',
modelNumber: row.modelNumber || '',
modelShop: row.modelShop || '',
address: row.address || '',
link: row.link || '',
thirdPartyOrderNo: row.thirdPartyOrderNo || '',
buyer: row.buyer || '',
paymentAmount: row.paymentAmount,
rebateAmount: row.rebateAmount,
orderId: row.orderId || '',
logisticsLink: row.logisticsLink || '',
orderTime: row.orderTime || row.createTime,
id: row.id
}
},
async loadHistory() { async loadHistory() {
this.historyLoading = true this.historyLoading = true
try { try {
const kw = (this.historyKeyword || '').trim() const kw = (this.historyKeyword || '').trim()
const res = await listJDOrders({ const [histRes, orderRes] = await Promise.all([
pageNum: this.historyPage, getHistory('response', 300, kw || undefined),
pageSize: this.historyPageSize, listJDOrders({
orderBy: 'id', pageNum: 1,
isAsc: 'desc', pageSize: 100,
quickRecordSearch: kw || undefined orderBy: 'id',
isAsc: 'desc',
quickRecordSearch: kw || undefined
})
])
const orders = ((orderRes && orderRes.rows) || []).map(this.normalizeOrderItem).filter(Boolean)
const persistedRemarks = new Set(
orders.map(o => String(o.remark || '').trim()).filter(Boolean)
)
const draftMap = new Map()
;((histRes && histRes.data) || []).forEach(raw => {
const draft = this.normalizeDraftItem(raw)
if (!draft) return
if (kw && !this.historyItemMatchesKeyword(draft, kw)) return
const remark = draft.remark
if (remark && persistedRemarks.has(remark)) return
if (remark && draftMap.has(remark)) return
const fallbackKey = draft.key
if (!remark && draftMap.has(fallbackKey)) return
draftMap.set(remark || fallbackKey, draft)
}) })
this.historyList = (res && res.rows) || [] const drafts = Array.from(draftMap.values()).filter(d => !d.filled)
this.historyTotal = (res && res.total) || 0 const unfilledOrders = orders.filter(o => !o.filled)
const filledOrders = orders.filter(o => o.filled)
this.historyAll = [...drafts, ...unfilledOrders, ...filledOrders]
this.historyTotal = this.historyAll.length
if ((this.historyPage - 1) * this.historyPageSize >= this.historyTotal) {
this.historyPage = 1
}
this.sliceHistoryPage()
} catch (e) { } catch (e) {
this.historyAll = []
this.historyList = [] this.historyList = []
this.historyTotal = 0 this.historyTotal = 0
this.$modal.msgError('加载历史记录失败') this.$modal.msgError('加载历史记录失败')
@@ -741,7 +926,7 @@ export default {
this.historyLoading = false this.historyLoading = false
} }
}, },
historyFullModel(row) { historyItemModel(row) {
if (!row) return '' if (!row) return ''
const base = String(row.modelNumber || '').trim() const base = String(row.modelNumber || '').trim()
const shop = String(row.modelShop || '').trim() const shop = String(row.modelShop || '').trim()
@@ -750,6 +935,11 @@ export default {
if (base.endsWith(shop)) return base if (base.endsWith(shop)) return base
return base + shop return base + shop
}, },
formatHistoryMoney(v) {
if (v == null || v === '') return ''
if (typeof v === 'number') return this.formatMoneyText(v)
return String(v)
},
formatHistoryTime(row) { formatHistoryTime(row) {
const raw = (row && (row.orderTime || row.createTime)) || '' const raw = (row && (row.orderTime || row.createTime)) || ''
if (!raw) return '' if (!raw) return ''
@@ -772,26 +962,26 @@ export default {
} }
this.form.markSuffix = v this.form.markSuffix = v
}, },
applyHistoryOrder(row) { applyHistoryItem(row) {
if (!row) return if (!row) return
this.applyMarkSuffixFromText(row.distributionMark) this.applyMarkSuffixFromText(row.distributionMark)
if (row.modelShop) { if (row.modelShop) {
this.form.model = String(row.modelNumber || '').trim() this.form.model = String(row.modelNumber || '').trim()
this.form.shopPrefix = String(row.modelShop || '').trim() this.form.shopPrefix = String(row.modelShop || '').trim()
} else { } else {
this.applyModelWithShop(this.historyFullModel(row)) this.applyModelWithShop(this.historyItemModel(row))
} }
this.form.address = this.compressAddressOneLine(row.address) this.form.address = this.compressAddressOneLine(row.address)
this.form.link = String(row.link || '').trim() this.form.link = String(row.link || '').trim()
this.form.thirdPartyOrderNoText = String(row.thirdPartyOrderNo || '').trim() this.form.thirdPartyOrderNoText = String(row.thirdPartyOrderNo || '').trim()
this.form.buyer = String(row.buyer || '').trim() this.form.buyer = String(row.buyer || '').trim()
this.form.paymentText = this.formatMoneyText(row.paymentAmount) this.form.paymentText = this.formatHistoryMoney(row.paymentAmount)
this.form.rebateText = this.formatMoneyText(row.rebateAmount) this.form.rebateText = this.formatHistoryMoney(row.rebateAmount)
this.form.orderIdText = String(row.orderId || '').trim() this.form.orderIdText = String(row.orderId || '').trim()
this.form.logisticsLink = String(row.logisticsLink || '').trim() this.form.logisticsLink = String(row.logisticsLink || '').trim()
this.resultText = '' this.resultText = row.rawText || ''
this.historyVisible = false this.historyVisible = false
this.$modal.msgSuccess('已回填历史记录,可修改后录单') this.$modal.msgSuccess(row.filled ? '已回填历史记录,可修改后录单' : '已回填待填写表单,请补全后录单')
this.$nextTick(() => { this.$nextTick(() => {
const formEl = this.$el && this.$el.querySelector('.fadan-form') const formEl = this.$el && this.$el.querySelector('.fadan-form')
if (formEl && formEl.scrollIntoView) { if (formEl && formEl.scrollIntoView) {
@@ -970,6 +1160,12 @@ export default {
flex: 1; flex: 1;
} }
.history-hint {
margin: 0 0 10px;
font-size: 12px;
color: #909399;
}
.history-list { .history-list {
max-height: 52vh; max-height: 52vh;
overflow-y: auto; overflow-y: auto;
@@ -997,6 +1193,11 @@ export default {
background: #f5faff; background: #f5faff;
} }
.history-item.is-unfilled {
border-color: #f5dab1;
background: #fdf6ec;
}
.history-item-top { .history-item-top {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;