This commit is contained in:
van
2026-03-24 01:09:04 +08:00
parent 1b41508013
commit 2f296b5fb5
2 changed files with 218 additions and 4 deletions

View File

@@ -1,4 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
import axios from 'axios'
import { getToken } from '@/utils/auth'
// JD订单列表 // JD订单列表
export function listJDOrders(query) { export function listJDOrders(query) {
@@ -166,7 +168,22 @@ export function delJDOrder(ids) {
method: 'delete' method: 'delete'
}) })
} }
1
/** 导入跟团返现 Excelmultipart不用统一 request 以免 Content-Type 覆盖) */
export function importGroupRebateExcel(formData) {
return axios
.post(process.env.VUE_APP_BASE_API + '/system/jdorder/importGroupRebateExcel', formData, {
headers: { Authorization: 'Bearer ' + getToken() }
})
.then((res) => {
const d = res.data
if (!d || d.code !== 200) {
return Promise.reject(new Error((d && d.msg) || '导入失败'))
}
return d
})
}
// 手动获取物流信息(用于调试) // 手动获取物流信息(用于调试)
export function fetchLogisticsManually(data) { export function fetchLogisticsManually(data) {
return request({ return request({

View File

@@ -89,6 +89,20 @@
<el-option label="未晒评价" :value="0" /> <el-option label="未晒评价" :value="0" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="后返备注">
<el-select
v-model="rebateRemarkScreen"
placeholder="全部"
clearable
size="small"
style="width: 160px;"
@change="onRebateRemarkScreenChange"
>
<el-option label="有导入记录" value="any" />
<el-option label="含异常项" value="abnormal" />
<el-option label="无异常项" value="ok" />
</el-select>
</el-form-item>
<!-- 搜索按钮桌面端显示移动端展开时在底部显示 --> <!-- 搜索按钮桌面端显示移动端展开时在底部显示 -->
<el-form-item> <el-form-item>
<el-button type="primary" size="small" icon="el-icon-search" @click="handleQuery">搜索</el-button> <el-button type="primary" size="small" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@@ -116,6 +130,7 @@
<el-button type="success" size="small" icon="el-icon-check" @click="handleBatchMarkRebateReceived" :loading="batchMarkLoading" title="批量将赔付金额大于0的订单标记为后返到账仅执行一次">批量标记后返到账</el-button> <el-button type="success" size="small" icon="el-icon-check" @click="handleBatchMarkRebateReceived" :loading="batchMarkLoading" title="批量将赔付金额大于0的订单标记为后返到账仅执行一次">批量标记后返到账</el-button>
<el-button type="warning" size="small" icon="el-icon-sort" @click="handleReverseSyncThirdPartyOrderNo" :loading="reverseSyncLoading" title="从腾讯文档第850行开始通过物流链接反向匹配订单将腾讯文档的单号列值写入到订单的第三方单号字段">反向同步第三方单号</el-button> <el-button type="warning" size="small" icon="el-icon-sort" @click="handleReverseSyncThirdPartyOrderNo" :loading="reverseSyncLoading" title="从腾讯文档第850行开始通过物流链接反向匹配订单将腾讯文档的单号列值写入到订单的第三方单号字段">反向同步第三方单号</el-button>
<el-button v-if="!isMobile" type="primary" size="small" icon="el-icon-document-copy" @click="handleBatchCopyExcelText" :disabled="selectedRows.length === 0" title="批量复制选中订单的录单格式Excel可粘贴">批量复制录单格式</el-button> <el-button v-if="!isMobile" type="primary" size="small" icon="el-icon-document-copy" @click="handleBatchCopyExcelText" :disabled="selectedRows.length === 0" title="批量复制选中订单的录单格式Excel可粘贴">批量复制录单格式</el-button>
<el-button v-if="!isMobile" type="primary" size="small" icon="el-icon-upload2" @click="rebateImportDialogVisible = true" title="上传跟团返现等 Excel按单号写入后返备注可多次导入累加">导入后返表</el-button>
<el-button v-if="!isMobile" type="success" size="small" icon="el-icon-document-copy" @click="handleBatchCopyRebateText" :disabled="selectedRows.length === 0" title="批量复制选中订单的后返录表格式Excel可粘贴">批量复制后返录表</el-button> <el-button v-if="!isMobile" type="success" size="small" icon="el-icon-document-copy" @click="handleBatchCopyRebateText" :disabled="selectedRows.length === 0" title="批量复制选中订单的后返录表格式Excel可粘贴">批量复制后返录表</el-button>
<el-button v-if="!isMobile" type="info" size="small" icon="el-icon-document-copy" @click="handleBatchCopySichuanCommerceText" :disabled="selectedRows.length === 0" title="批量复制选中订单的四川商贸录表格式(日期 型号 数量 地址 价格 备注 是否安排 物流)">四川商贸录表</el-button> <el-button v-if="!isMobile" type="info" size="small" icon="el-icon-document-copy" @click="handleBatchCopySichuanCommerceText" :disabled="selectedRows.length === 0" title="批量复制选中订单的四川商贸录表格式(日期 型号 数量 地址 价格 备注 是否安排 物流)">四川商贸录表</el-button>
</div> </div>
@@ -255,6 +270,14 @@
<span class="field-label">后返金额</span> <span class="field-label">后返金额</span>
<span class="field-value amount">{{ toYuan(row.rebateAmount) }}</span> <span class="field-value amount">{{ toYuan(row.rebateAmount) }}</span>
</div> </div>
<div class="field-row" v-if="row.rebateRemarkJson">
<span class="field-label">后返备注</span>
<span class="field-value">
<el-tag v-if="row.rebateRemarkHasAbnormal === 1" type="danger" size="mini">异常</el-tag>
<el-tag v-else-if="row.rebateRemarkHasAbnormal === 0" type="success" size="mini">正常</el-tag>
<span style="margin-left: 6px;">{{ rebateRemarkCount(row) }} 条记录</span>
</span>
</div>
<div class="field-row"> <div class="field-row">
<span class="field-label">下单人</span> <span class="field-label">下单人</span>
<span class="field-value">{{ row.buyer || '-' }}</span> <span class="field-value">{{ row.buyer || '-' }}</span>
@@ -366,6 +389,37 @@
<el-table-column label="后返金额" prop="rebateAmount" width="110" align="right"> <el-table-column label="后返金额" prop="rebateAmount" width="110" align="right">
<template slot-scope="scope">{{ toYuan(scope.row.rebateAmount) }}</template> <template slot-scope="scope">{{ toYuan(scope.row.rebateAmount) }}</template>
</el-table-column> </el-table-column>
<el-table-column label="后返备注" prop="rebateRemarkJson" min-width="200" show-overflow-tooltip>
<template slot-scope="scope">
<template v-if="!scope.row.rebateRemarkJson">
<span style="color: #c0c4cc;">-</span>
</template>
<template v-else>
<el-tag v-if="scope.row.rebateRemarkHasAbnormal === 1" type="danger" size="mini">异常</el-tag>
<el-tag v-else-if="scope.row.rebateRemarkHasAbnormal === 0" type="success" size="mini">正常</el-tag>
<el-popover trigger="click" placement="left" width="420">
<div class="rebate-remark-popover">
<div
v-for="(it, idx) in parseRebateRemarks(scope.row)"
:key="idx"
class="rebate-remark-item"
>
<div class="rebate-remark-title">
<b>{{ it.documentTitle || '(未命名)' }}</b>
<span class="rebate-remark-time">{{ formatRebateRemarkTime(it.uploadTime) }}</span>
</div>
<div>是否返现{{ it.whetherRebate !== undefined && it.whetherRebate !== '' ? it.whetherRebate : '(空)' }}
<el-tag v-if="it.abnormal" type="danger" size="mini">异常</el-tag>
<el-tag v-else type="success" size="mini">正常</el-tag>
</div>
<div>返现金额{{ it.rebateAmount !== undefined && it.rebateAmount !== '' ? it.rebateAmount : '-' }}</div>
</div>
</div>
<el-button slot="reference" type="text" size="small">明细{{ rebateRemarkCount(scope.row) }}</el-button>
</el-popover>
</template>
</template>
</el-table-column>
<el-table-column label="下单人" prop="buyer" width="100"/> <el-table-column label="下单人" prop="buyer" width="100"/>
<!-- 退款状态标签列多行显示 --> <!-- 退款状态标签列多行显示 -->
@@ -824,11 +878,47 @@
<!-- 分销标识接收人配置 --> <!-- 分销标识接收人配置 -->
<distribution-mark-touser-config v-model="showTouserConfig" @config-updated="handleTouserConfigUpdated" /> <distribution-mark-touser-config v-model="showTouserConfig" @config-updated="handleTouserConfigUpdated" />
<!-- 导入跟团返现 / 后返 Excel -->
<el-dialog
title="导入后返表(跟团返现等)"
:visible.sync="rebateImportDialogVisible"
width="520px"
append-to-body
@closed="rebateImportTitle = ''"
>
<p style="color: #909399; font-size: 13px; margin: 0 0 12px;">
表头需包含单号订单号是否返现返现金额列优先总共返现按单号匹配系统订单并<strong>追加</strong>一条备注记录同一订单多次上传会保留历史
</p>
<el-form label-width="88px" size="small">
<el-form-item label="文档标题">
<el-input
v-model="rebateImportTitle"
placeholder="如:跟团+返现 260316留空则用文件名"
clearable
/>
</el-form-item>
<el-form-item label="Excel">
<el-upload
drag
:show-file-list="true"
:limit="1"
accept=".xlsx,.xls"
:disabled="rebateImportLoading"
:http-request="handleRebateExcelUpload"
>
<i class="el-icon-upload" />
<div class="el-upload__text">拖到此处<em>点击上传</em></div>
<div slot="tip" class="el-upload__tip">仅解析第一个工作表未匹配到的单号会在结果中列出</div>
</el-upload>
</el-form-item>
</el-form>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { listJDOrders, updateJDOrder, delJDOrder, fetchLogisticsManually, batchMarkRebateReceived, generateExcelText } from '@/api/system/jdorder' import { listJDOrders, updateJDOrder, delJDOrder, fetchLogisticsManually, batchMarkRebateReceived, generateExcelText, importGroupRebateExcel } from '@/api/system/jdorder'
import { fillLogisticsByOrderNo, getTokenStatus, getTencentDocAuthUrl, testUserInfo, getAutoWriteConfig, reverseSyncThirdPartyOrderNo } from '@/api/jarvis/tendoc' import { fillLogisticsByOrderNo, getTokenStatus, getTencentDocAuthUrl, testUserInfo, getAutoWriteConfig, reverseSyncThirdPartyOrderNo } from '@/api/jarvis/tendoc'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import ListLayout from '@/components/ListLayout' import ListLayout from '@/components/ListLayout'
@@ -875,8 +965,14 @@ export default {
isRebateReceived: undefined, isRebateReceived: undefined,
isPriceProtected: undefined, isPriceProtected: undefined,
isInvoiceOpened: undefined, isInvoiceOpened: undefined,
isReviewPosted: undefined isReviewPosted: undefined,
hasRebateRemark: undefined,
rebateRemarkHasAbnormal: undefined
}, },
rebateRemarkScreen: undefined,
rebateImportDialogVisible: false,
rebateImportTitle: '',
rebateImportLoading: false,
// 同步物流对话框 // 同步物流对话框
syncLogisticsDialogVisible: false, syncLogisticsDialogVisible: false,
syncLogisticsLoading: false, syncLogisticsLoading: false,
@@ -1121,6 +1217,78 @@ export default {
this.queryParams.pageNum = 1 this.queryParams.pageNum = 1
this.getList() this.getList()
}, },
/** 后返备注列表筛选 */
onRebateRemarkScreenChange(v) {
this.queryParams.hasRebateRemark = undefined
this.queryParams.rebateRemarkHasAbnormal = undefined
if (v === 'any') {
this.queryParams.hasRebateRemark = true
} else if (v === 'abnormal') {
this.queryParams.rebateRemarkHasAbnormal = 1
} else if (v === 'ok') {
this.queryParams.hasRebateRemark = true
this.queryParams.rebateRemarkHasAbnormal = 0
}
this.queryParams.pageNum = 1
this.prepareQueryParams()
this.getList()
},
parseRebateRemarks(row) {
if (!row || !row.rebateRemarkJson) return []
try {
const arr = JSON.parse(row.rebateRemarkJson)
return Array.isArray(arr) ? arr : []
} catch (e) {
return []
}
},
rebateRemarkCount(row) {
return this.parseRebateRemarks(row).length
},
formatRebateRemarkTime(ts) {
if (ts == null || ts === '') return ''
const d = new Date(typeof ts === 'number' ? ts : Number(ts))
if (Number.isNaN(d.getTime())) return ''
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const h = String(d.getHours()).padStart(2, '0')
const min = String(d.getMinutes()).padStart(2, '0')
return `${y}-${m}-${day} ${h}:${min}`
},
handleRebateExcelUpload(req) {
const formData = new FormData()
formData.append('file', req.file)
const title = (this.rebateImportTitle || '').trim()
if (title) {
formData.append('documentTitle', title)
}
this.rebateImportLoading = true
importGroupRebateExcel(formData)
.then((res) => {
const data = res.data || {}
const msg = data.message || res.msg || '导入完成'
this.$message.success(msg)
const nf = data.notFoundOrderNos
if (nf && nf.length) {
const sample = nf.slice(0, 30).join(', ')
const more = nf.length > 30 ? ` …共 ${nf.length}` : ''
this.$alert(`以下单号在系统中未找到,未写入:${sample}${more}`, '未匹配单号', { type: 'warning' })
}
if (data.errors && data.errors.length) {
this.$message.warning(`部分行写入失败 ${data.errors.length} 条,详情见控制台`)
console.warn('后返表导入错误', data.errors)
}
this.rebateImportDialogVisible = false
this.getList()
})
.catch((e) => {
this.$message.error(e.message || '导入失败')
})
.finally(() => {
this.rebateImportLoading = false
})
},
resetQuery() { resetQuery() {
this.queryParams = { this.queryParams = {
pageNum: 1, pageNum: 1,
@@ -1143,8 +1311,11 @@ export default {
isRebateReceived: undefined, isRebateReceived: undefined,
isPriceProtected: undefined, isPriceProtected: undefined,
isInvoiceOpened: undefined, isInvoiceOpened: undefined,
isReviewPosted: undefined isReviewPosted: undefined,
hasRebateRemark: undefined,
rebateRemarkHasAbnormal: undefined
} }
this.rebateRemarkScreen = undefined
// 重置排序为默认降序 // 重置排序为默认降序
this.queryParams.orderBy = 'create_time' this.queryParams.orderBy = 'create_time'
this.queryParams.isAsc = 'desc' this.queryParams.isAsc = 'desc'
@@ -2535,6 +2706,32 @@ export default {
justify-content: flex-end; justify-content: flex-end;
} }
.rebate-remark-popover .rebate-remark-item {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
font-size: 13px;
line-height: 1.5;
}
.rebate-remark-popover .rebate-remark-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.rebate-remark-popover .rebate-remark-title {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.rebate-remark-popover .rebate-remark-time {
color: #909399;
font-size: 12px;
font-weight: normal;
flex-shrink: 0;
}
/* 移动端操作按钮样式 - 只在移动端生效 */ /* 移动端操作按钮样式 - 只在移动端生效 */
@media (max-width: 768px) { @media (max-width: 768px) {
.mobile-action-wrapper { .mobile-action-wrapper {