1
This commit is contained in:
@@ -27,6 +27,14 @@ export function listQuickRecordShopOptions() {
|
||||
})
|
||||
}
|
||||
|
||||
/** 一键迁移:拆分 model_number 末尾店铺前缀到 model_shop */
|
||||
export function migrateModelShopSplit() {
|
||||
return request({
|
||||
url: '/system/jdorder/migrateModelShop',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// JD订单详情
|
||||
export function getJDOrder(id) {
|
||||
return request({
|
||||
|
||||
@@ -356,25 +356,33 @@ export default {
|
||||
if (!base) return ''
|
||||
return prefix ? base + prefix : base
|
||||
},
|
||||
fullModelFromOption(o) {
|
||||
if (!o) return ''
|
||||
const base = String(o.modelNumber || '').trim()
|
||||
const shop = String(o.modelShop || '').trim()
|
||||
if (!base) return shop
|
||||
if (!shop) return base
|
||||
if (base.endsWith(shop)) return base
|
||||
return base + shop
|
||||
},
|
||||
queryModels(queryString, cb) {
|
||||
const q = (queryString || '').trim().toLowerCase()
|
||||
const rows = this.modelOptions
|
||||
.filter(o => {
|
||||
const m = String(o.modelNumber || '').trim()
|
||||
if (!m) return false
|
||||
const full = this.fullModelFromOption(o)
|
||||
if (!full) return false
|
||||
if (!q) return true
|
||||
const parsed = this.parseModelWithShop(m)
|
||||
return m.toLowerCase().includes(q) || parsed.base.toLowerCase().includes(q)
|
||||
const parsed = this.parseModelWithShop(full)
|
||||
return full.toLowerCase().includes(q) || parsed.base.toLowerCase().includes(q)
|
||||
})
|
||||
.slice(0, 100)
|
||||
cb(
|
||||
rows.map(o => {
|
||||
const full = String(o.modelNumber).trim()
|
||||
const full = this.fullModelFromOption(o)
|
||||
const parsed = this.parseModelWithShop(full)
|
||||
const displayBase = parsed.base || full
|
||||
return {
|
||||
value: full,
|
||||
label: this.modelOptionLabel({ ...o, modelNumber: displayBase })
|
||||
label: this.modelOptionLabel({ ...o, modelNumber: parsed.base || full })
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -383,8 +391,13 @@ export default {
|
||||
onModelSuggestionSelect(item) {
|
||||
if (!item || !item.value) return
|
||||
const key = String(item.value).trim()
|
||||
const hit = this.modelOptions.find(o => this.fullModelFromOption(o) === key)
|
||||
if (hit && hit.modelShop) {
|
||||
this.form.model = String(hit.modelNumber || '').trim()
|
||||
this.form.shopPrefix = String(hit.modelShop || '').trim()
|
||||
} else {
|
||||
this.applyModelWithShop(key)
|
||||
const hit = this.modelOptions.find(o => o && String(o.modelNumber || '').trim() === key)
|
||||
}
|
||||
if (!hit) return
|
||||
const payRaw = hit.lastPaymentAmount
|
||||
const rebateRaw = hit.lastRebateAmount
|
||||
|
||||
@@ -64,7 +64,12 @@
|
||||
<el-input v-model="queryParams.distributionMark" placeholder="分销标记" clearable size="small" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号">
|
||||
<el-input v-model="queryParams.modelNumber" placeholder="型号" clearable size="small" @keyup.enter.native="handleQuery" />
|
||||
<el-input v-model="queryParams.modelNumber" placeholder="型号(含店铺拼接也可搜)" clearable size="small" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺">
|
||||
<el-select v-model="queryParams.modelShop" placeholder="全部" clearable filterable size="small" style="width: 140px">
|
||||
<el-option v-for="opt in shopOptions" :key="opt.prefix" :label="opt.label" :value="opt.prefix" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="型号不含">
|
||||
<el-input v-model="queryParams.modelNumberExclude" placeholder="多个用逗号或空格,如 130,645,90" clearable size="small" @keyup.enter.native="handleQuery" />
|
||||
@@ -191,6 +196,7 @@
|
||||
<el-button class="jd-tb-muted" plain size="small" icon="el-icon-setting" @click="showAutoWriteConfig = true" title="配置H-TF订单自动写入腾讯文档">腾峰文档配置</el-button>
|
||||
<el-button type="primary" plain size="small" icon="el-icon-monitor" @click="showPushMonitor = true" title="查看推送监控和历史记录">推送监控</el-button>
|
||||
<el-button class="jd-tb-muted" plain size="small" icon="el-icon-user" @click="showTouserConfig = true" title="配置分销标识对应的企业微信接收人">接收人配置</el-button>
|
||||
<el-button v-if="!isMobile" class="jd-tb-muted" plain size="small" icon="el-icon-scissors" :loading="modelShopMigrateLoading" @click="handleMigrateModelShop" title="按已配置店铺选项,将型号末尾前缀拆到「型号店铺」字段">拆分型号店铺</el-button>
|
||||
<el-button class="jd-tb-muted" plain size="small" icon="el-icon-check" @click="handleBatchMarkRebateReceived" :loading="batchMarkLoading" title="批量将赔付金额大于0的订单标记为后返到账(仅执行一次)">批量标记后返到账</el-button>
|
||||
<el-button v-if="!isMobile" type="primary" plain size="small" icon="el-icon-upload2" @click="rebateImportDialogVisible = true" title="可一次选多个 Excel,提交后由后台依次导入;详情见后返上传记录">导入后返表</el-button>
|
||||
<el-button v-if="!isMobile" class="jd-tb-muted" plain size="small" icon="el-icon-folder-opened" @click="openRebateUploadRecordDialog" title="查看历史上传的后返表原件并可重新下载">后返上传记录</el-button>
|
||||
@@ -353,7 +359,7 @@
|
||||
>{{ row.distributionMark }}</span>
|
||||
<span class="card-summary-sep"> · </span>
|
||||
</template>
|
||||
<span class="card-summary-meta">{{ row.modelNumber || '—' }} · 付{{ toYuan(row.paymentAmount) }} · 返{{ toYuan(row.rebateAmount) }}</span>
|
||||
<span class="card-summary-meta">{{ fullModelNumber(row) || '—' }} · 付{{ toYuan(row.paymentAmount) }} · 返{{ toYuan(row.rebateAmount) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -396,15 +402,26 @@
|
||||
</div>
|
||||
<div class="field-row field-row--model" @click.stop>
|
||||
<span class="field-label">型号</span>
|
||||
<div class="field-value field-value--third-party-edit">
|
||||
<div class="field-value field-value--third-party-edit jd-mobile-model-edit">
|
||||
<el-input
|
||||
v-model="row.modelNumber"
|
||||
class="jd-mobile-model-input"
|
||||
size="small"
|
||||
clearable
|
||||
placeholder="型号(单行,约20字),失焦或保存"
|
||||
placeholder="型号本体"
|
||||
@blur="onModelNumberBlur(row)"
|
||||
/>
|
||||
<el-select
|
||||
v-model="row.modelShop"
|
||||
class="jd-mobile-model-shop"
|
||||
size="small"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="店铺"
|
||||
@change="onModelShopChange(row)"
|
||||
>
|
||||
<el-option v-for="opt in shopOptions" :key="opt.prefix" :label="opt.label" :value="opt.prefix" />
|
||||
</el-select>
|
||||
<el-button type="primary" size="mini" plain @click="onModelNumberSave(row)">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -650,18 +667,29 @@
|
||||
</el-table-column>
|
||||
|
||||
<!-- 业务信息列:型号单行撑满列宽,约 20 字以内不挤;更长在输入框内横向滚动,不换行以免行高乱跳 -->
|
||||
<el-table-column label="型号" prop="modelNumber" min-width="232" class-name="jd-col-model">
|
||||
<el-table-column label="型号" prop="modelNumber" min-width="280" class-name="jd-col-model">
|
||||
<template slot-scope="scope">
|
||||
<div class="jd-cell-stretch jd-cell-stretch--model">
|
||||
<div class="jd-cell-stretch jd-cell-stretch--model jd-model-edit-row">
|
||||
<el-input
|
||||
v-model="scope.row.modelNumber"
|
||||
class="jd-order-input-model"
|
||||
size="mini"
|
||||
clearable
|
||||
placeholder="型号(约20字)"
|
||||
@blur="onModelNumberBlur(scope.row)"
|
||||
@keyup.enter.native="onModelNumberBlur(scope.row)"
|
||||
placeholder="型号本体"
|
||||
@blur="onModelFieldBlur(scope.row)"
|
||||
@keyup.enter.native="onModelFieldBlur(scope.row)"
|
||||
/>
|
||||
<el-select
|
||||
v-model="scope.row.modelShop"
|
||||
class="jd-order-model-shop"
|
||||
size="mini"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="店铺"
|
||||
@change="onModelShopChange(scope.row)"
|
||||
>
|
||||
<el-option v-for="opt in shopOptions" :key="opt.prefix" :label="opt.label" :value="opt.prefix" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -1314,7 +1342,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listJDOrders, getJDOrder, updateJDOrder, normalizeJDOrderPutPayload, delJDOrder, fetchLogisticsManually, batchMarkRebateReceived, generateExcelText, importGroupRebateExcelBatch, listGroupRebateExcelUploads, deleteGroupRebateUpload, recalcProfitBatch, syncAutoProfitBatch } from '@/api/system/jdorder'
|
||||
import { listJDOrders, getJDOrder, updateJDOrder, normalizeJDOrderPutPayload, delJDOrder, fetchLogisticsManually, batchMarkRebateReceived, generateExcelText, importGroupRebateExcelBatch, listGroupRebateExcelUploads, deleteGroupRebateUpload, recalcProfitBatch, syncAutoProfitBatch, listQuickRecordShopOptions, migrateModelShopSplit } from '@/api/system/jdorder'
|
||||
import { fillLogisticsByOrderNo, getTokenStatus, getTencentDocAuthUrl, testUserInfo, getAutoWriteConfig, reverseSyncThirdPartyOrderNo } from '@/api/jarvis/tendoc'
|
||||
import { mapGetters } from 'vuex'
|
||||
import ListLayout from '@/components/ListLayout'
|
||||
@@ -1343,6 +1371,10 @@ export default {
|
||||
_distMarkBaseline: {},
|
||||
/** 列表加载时的型号快照,失焦保存时与当前值比较避免重复请求 */
|
||||
_modelNumberBaseline: {},
|
||||
/** 列表加载时的型号店铺快照 */
|
||||
_modelShopBaseline: {},
|
||||
shopOptions: [],
|
||||
modelShopMigrateLoading: false,
|
||||
loading: false,
|
||||
list: [],
|
||||
total: 0,
|
||||
@@ -1354,6 +1386,7 @@ export default {
|
||||
orderSearch: undefined,
|
||||
distributionMark: undefined,
|
||||
modelNumber: undefined,
|
||||
modelShop: undefined,
|
||||
modelNumberExclude: undefined,
|
||||
link: undefined,
|
||||
buyer: undefined,
|
||||
@@ -1554,8 +1587,7 @@ export default {
|
||||
// 设置默认日期为今天
|
||||
this.setDefaultDateRange()
|
||||
this.getListWithFallback()
|
||||
|
||||
// 监听腾讯文档授权回调消息
|
||||
this.loadShopOptions()
|
||||
this.handleOAuthMessage = (event) => {
|
||||
if (event.data && event.data.type === 'tendoc_oauth_callback') {
|
||||
if (event.data.success) {
|
||||
@@ -1754,14 +1786,17 @@ export default {
|
||||
normalizeOrderListItem(item) {
|
||||
const dist = this.normalizeDistributionMarkInput(item.distributionMark)
|
||||
const modelNum = this.normalizeModelNumberInput(item.modelNumber)
|
||||
const modelShop = this.normalizeModelShopInput(item.modelShop)
|
||||
if (item.id != null) {
|
||||
this.$set(this._distMarkBaseline, item.id, dist)
|
||||
this.$set(this._modelNumberBaseline, item.id, modelNum)
|
||||
this.$set(this._modelShopBaseline, item.id, modelShop)
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
distributionMark: dist,
|
||||
modelNumber: modelNum,
|
||||
modelShop: modelShop,
|
||||
isRefunded: item.isRefunded != null ? item.isRefunded : 0,
|
||||
isRefundReceived: item.isRefundReceived != null ? item.isRefundReceived : 0,
|
||||
isRebateReceived: item.isRebateReceived != null ? item.isRebateReceived : 0,
|
||||
@@ -1795,6 +1830,7 @@ export default {
|
||||
|
||||
this._distMarkBaseline = {}
|
||||
this._modelNumberBaseline = {}
|
||||
this._modelShopBaseline = {}
|
||||
const list = (res.rows || res.data || [])
|
||||
this.list = list.map(item => this.normalizeOrderListItem(item))
|
||||
this.total = res.total || 0
|
||||
@@ -2059,6 +2095,7 @@ export default {
|
||||
orderSearch: undefined,
|
||||
distributionMark: undefined,
|
||||
modelNumber: undefined,
|
||||
modelShop: undefined,
|
||||
modelNumberExclude: undefined,
|
||||
link: undefined,
|
||||
buyer: undefined,
|
||||
@@ -2272,20 +2309,92 @@ export default {
|
||||
if (v == null) return ''
|
||||
return String(v).trim()
|
||||
},
|
||||
/** 型号失焦或点保存时写入数据库(与基线一致则不调接口) */
|
||||
saveModelNumberIfChanged(row, successMsg) {
|
||||
normalizeModelShopInput(v) {
|
||||
if (v == null) return ''
|
||||
return String(v).trim()
|
||||
},
|
||||
fullModelNumber(row) {
|
||||
if (!row) return ''
|
||||
const base = this.normalizeModelNumberInput(row.modelNumber)
|
||||
const shop = this.normalizeModelShopInput(row.modelShop)
|
||||
if (!base) return shop
|
||||
if (!shop) return base
|
||||
if (base.endsWith(shop)) return base
|
||||
return base + shop
|
||||
},
|
||||
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(),
|
||||
label: o.label || o.prefix
|
||||
}))
|
||||
.sort((a, b) => b.prefix.length - a.prefix.length)
|
||||
} catch (e) {
|
||||
this.shopOptions = []
|
||||
}
|
||||
},
|
||||
parseModelWithShopFromOptions(fullModel) {
|
||||
const full = String(fullModel || '').trim()
|
||||
if (!full || !this.shopOptions.length) return { base: full, shop: '' }
|
||||
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), shop: p }
|
||||
}
|
||||
}
|
||||
return { base: full, shop: '' }
|
||||
},
|
||||
/** 型号/店铺变更后写入数据库(与基线一致则不调接口) */
|
||||
saveModelFieldsIfChanged(row, successMsg) {
|
||||
if (row == null || row.id == null) return
|
||||
const next = this.normalizeModelNumberInput(row.modelNumber)
|
||||
row.modelNumber = next
|
||||
const base = this._modelNumberBaseline[row.id] != null ? this._modelNumberBaseline[row.id] : ''
|
||||
if (next === base) return
|
||||
const parsed = this.parseModelWithShopFromOptions(row.modelNumber)
|
||||
if (parsed.prefix) {
|
||||
row.modelNumber = parsed.base
|
||||
row.modelShop = parsed.shop
|
||||
}
|
||||
const nextBase = this.normalizeModelNumberInput(row.modelNumber)
|
||||
const nextShop = this.normalizeModelShopInput(row.modelShop)
|
||||
row.modelNumber = nextBase
|
||||
row.modelShop = nextShop
|
||||
const baseNum = this._modelNumberBaseline[row.id] != null ? this._modelNumberBaseline[row.id] : ''
|
||||
const baseShop = this._modelShopBaseline[row.id] != null ? this._modelShopBaseline[row.id] : ''
|
||||
if (nextBase === baseNum && nextShop === baseShop) return
|
||||
this.persistOrderRow(row, successMsg)
|
||||
},
|
||||
onModelNumberBlur(row) {
|
||||
this.saveModelNumberIfChanged(row)
|
||||
this.onModelFieldBlur(row)
|
||||
},
|
||||
onModelNumberSave(row) {
|
||||
this.saveModelNumberIfChanged(row, '型号已保存')
|
||||
this.saveModelFieldsIfChanged(row, '型号已保存')
|
||||
},
|
||||
onModelFieldBlur(row) {
|
||||
this.saveModelFieldsIfChanged(row)
|
||||
},
|
||||
onModelShopChange(row) {
|
||||
this.saveModelFieldsIfChanged(row, '型号店铺已保存')
|
||||
},
|
||||
handleMigrateModelShop() {
|
||||
this.$confirm('将按「快捷录单店铺选项」配置,扫描全部订单:若型号末尾匹配店铺前缀,则写入「型号店铺」并从型号中截掉。是否继续?', '拆分型号店铺', {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.modelShopMigrateLoading = true
|
||||
return migrateModelShopSplit()
|
||||
}).then(res => {
|
||||
const data = (res && res.data) || {}
|
||||
this.$message.success(data.message || '迁移完成')
|
||||
this.getList()
|
||||
}).catch(e => {
|
||||
if (e !== 'cancel') {
|
||||
this.$message.error((e && e.message) || '迁移失败')
|
||||
}
|
||||
}).finally(() => {
|
||||
this.modelShopMigrateLoading = false
|
||||
})
|
||||
},
|
||||
onOrderSellingPriceTypeChange(row) {
|
||||
row.sellingPriceManual = 0
|
||||
@@ -2341,7 +2450,7 @@ export default {
|
||||
/** 复制篮子群「发货摘要」:型号 + 整段地址(去空白) + 物流短链 */
|
||||
buildBasketShipSummaryText(row) {
|
||||
if (!row) return ''
|
||||
const model = (row.modelNumber != null ? String(row.modelNumber).trim() : '') || '—'
|
||||
const model = this.fullModelNumber(row) || '—'
|
||||
const addrRaw = row.address != null ? String(row.address).trim() : ''
|
||||
const addr = (addrRaw.replace(/\s+/g, '')) || '—'
|
||||
const url = this.normalizeBasketLogisticsUrl(row.logisticsLink)
|
||||
@@ -3080,7 +3189,7 @@ export default {
|
||||
// 既有顺序保持不变
|
||||
parts.push(toLine(row.remark)) // 内部单号
|
||||
parts.push(toLine(row.orderId)) // 订单号
|
||||
parts.push(toLine(row.modelNumber)) // 型号
|
||||
parts.push(toLine(this.fullModelNumber(row))) // 型号
|
||||
parts.push(toLine(row.thirdPartyOrderNo)) // 第三方单号
|
||||
parts.push(toLine(row.distributionMark)) // 分销标记
|
||||
parts.push(toLine(row.address)) // 地址
|
||||
@@ -3113,7 +3222,7 @@ export default {
|
||||
? row.thirdPartyOrderNo : (row.remark || '')
|
||||
|
||||
// 型号
|
||||
const modelNumber = row.modelNumber || ''
|
||||
const modelNumber = this.fullModelNumber(row)
|
||||
|
||||
// 数量(固定为1)
|
||||
const quantity = '1'
|
||||
@@ -3196,7 +3305,7 @@ export default {
|
||||
const orderId = row.orderId || ''
|
||||
|
||||
// 型号:modelNumber
|
||||
const modelNumber = row.modelNumber || ''
|
||||
const modelNumber = this.fullModelNumber(row)
|
||||
|
||||
// 返现金额(团长):空
|
||||
const leaderRebateAmount = ''
|
||||
@@ -3384,7 +3493,7 @@ export default {
|
||||
? row.thirdPartyOrderNo : (row.remark || '')
|
||||
|
||||
// 型号
|
||||
const modelNumber = row.modelNumber || ''
|
||||
const modelNumber = this.fullModelNumber(row)
|
||||
|
||||
// 数量(固定为1)
|
||||
const quantity = '1'
|
||||
@@ -3480,7 +3589,7 @@ export default {
|
||||
const orderId = row.orderId || ''
|
||||
|
||||
// 型号:modelNumber
|
||||
const modelNumber = row.modelNumber || ''
|
||||
const modelNumber = this.fullModelNumber(row)
|
||||
|
||||
// 返现金额(团长):空
|
||||
const leaderRebateAmount = ''
|
||||
@@ -3564,7 +3673,7 @@ export default {
|
||||
dateStr = `${year}/${month}/${day}`
|
||||
}
|
||||
|
||||
const modelNumber = row.modelNumber || ''
|
||||
const modelNumber = this.fullModelNumber(row)
|
||||
const quantity = (row.productCount != null && row.productCount !== '') ? String(row.productCount) : '1'
|
||||
const address = row.address || ''
|
||||
const priceStr = row.paymentAmount != null ? row.paymentAmount.toFixed(2) : ''
|
||||
@@ -4113,6 +4222,33 @@ export default {
|
||||
text-overflow: clip;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.order-table ::v-deep .jd-model-edit-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.order-table ::v-deep .jd-model-edit-row .jd-order-input-model {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.order-table ::v-deep .jd-model-edit-row .jd-order-model-shop {
|
||||
width: 108px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.jd-mobile-model-edit {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
.jd-mobile-model-edit .jd-mobile-model-input {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
.jd-mobile-model-edit .jd-mobile-model-shop {
|
||||
width: 100%;
|
||||
}
|
||||
.order-table ::v-deep td.jd-col-model .cell {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
|
||||
Reference in New Issue
Block a user