This commit is contained in:
van
2026-03-24 16:24:00 +08:00
parent 0810a2b46a
commit 58e48e3ebf
3 changed files with 99 additions and 44 deletions

View File

@@ -61,9 +61,8 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
# 请求体相关配置重要支持POST请求
proxy_set_header Content-Type $content_type;
proxy_set_header Content-Length $content_length;
# 勿手动设置 Content-Type / Content-Lengthmultipart 上传必须原样转发(含 boundary
# 且分块请求时 $content_length 可能为空会导致后端报「not a multipart request」。
proxy_pass_request_headers on;
proxy_pass_request_body on;
@@ -96,9 +95,6 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
# POST请求支持
proxy_set_header Content-Type $content_type;
proxy_set_header Content-Length $content_length;
proxy_pass_request_headers on;
proxy_pass_request_body on;
@@ -124,8 +120,6 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Content-Type $content_type;
proxy_set_header Content-Length $content_length;
proxy_pass_request_headers on;
proxy_pass_request_body on;
@@ -147,9 +141,6 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
# POST请求支持
proxy_set_header Content-Type $content_type;
proxy_set_header Content-Length $content_length;
proxy_pass_request_headers on;
proxy_pass_request_body on;

View File

@@ -178,12 +178,18 @@ export function listGroupRebateExcelUploads(query) {
})
}
/** 导入跟团返现 Excelmultipart */
export function importGroupRebateExcel(formData) {
/** 删除后返表上传记录,并回滚对应订单后返备注(新导入数据 */
export function deleteGroupRebateUpload(id) {
return request({
url: '/system/jdorder/groupRebateUpload/' + id,
method: 'delete'
})
}
function postGroupRebateMultipart(url, formData) {
return axios
.post(process.env.VUE_APP_BASE_API + '/system/jdorder/importGroupRebateExcel', formData, {
.post(process.env.VUE_APP_BASE_API + url, formData, {
headers: { Authorization: 'Bearer ' + getToken() },
// 全局 axios.defaults 为 application/json会覆盖 multipart必须去掉由浏览器自动带 boundary
transformRequest: [
(data, headers) => {
if (data instanceof FormData) {
@@ -200,12 +206,22 @@ export function importGroupRebateExcel(formData) {
.then((res) => {
const d = res.data
if (!d || d.code !== 200) {
return Promise.reject(new Error((d && d.msg) || '导入失败'))
return Promise.reject(new Error((d && d.msg) || '请求失败'))
}
return d
})
}
/** 导入跟团返现 Excelmultipart单文件 */
export function importGroupRebateExcel(formData) {
return postGroupRebateMultipart('/system/jdorder/importGroupRebateExcel', formData)
}
/** 批量导入后返表multipart多个 files 字段) */
export function importGroupRebateExcelBatch(formData) {
return postGroupRebateMultipart('/system/jdorder/importGroupRebateExcelBatch', formData)
}
// 手动获取物流信息(用于调试)
export function fetchLogisticsManually(data) {
return request({

View File

@@ -130,7 +130,7 @@
<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 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="primary" size="small" icon="el-icon-upload2" @click="rebateImportDialogVisible = true" title="可一次选多个 Excel提交后由后台依次导入详情见后返上传记录">导入后返表</el-button>
<el-button v-if="!isMobile" type="info" size="small" icon="el-icon-folder-opened" @click="openRebateUploadRecordDialog" title="查看历史上传的后返表原件并可重新下载">后返上传记录</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>
@@ -880,38 +880,48 @@
<!-- 分销标识接收人配置 -->
<distribution-mark-touser-config v-model="showTouserConfig" @config-updated="handleTouserConfigUpdated" />
<!-- 导入跟团返现 / 后返 Excel -->
<!-- 导入跟团返现 / 后返 Excel支持多文件一次提交后台依次处理 -->
<el-dialog
title="导入后返表(跟团返现等)"
:visible.sync="rebateImportDialogVisible"
width="520px"
append-to-body
@closed="rebateImportTitle = ''"
@closed="onRebateImportDialogClosed"
>
<p style="color: #909399; font-size: 13px; margin: 0 0 12px;">
表头需包含单号订单号是否返现返现金额列优先总共返现按单号匹配系统订单并<strong>追加</strong>一条备注记录同一订单多次上传会保留历史
表头需包含单号订单号是否返现返现金额列优先总共返现可选<strong>多个</strong> Excel提交后台处理后由服务端依次导入不弹逐条结果请到后返上传记录查看
</p>
<el-form label-width="88px" size="small">
<el-form-item label="文档标题">
<el-input
v-model="rebateImportTitle"
placeholder="如:跟团+返现 260316留空则用文件名"
placeholder="仅选 1 个文件时可用;多文件时各文件用各自文件名"
clearable
/>
</el-form-item>
<el-form-item label="Excel">
<el-upload
ref="rebateExcelUploader"
drag
multiple
:show-file-list="true"
:limit="1"
:limit="30"
:auto-upload="false"
accept=".xlsx,.xls"
:disabled="rebateImportLoading"
:http-request="handleRebateExcelUpload"
:on-exceed="onRebateExcelExceed"
>
<i class="el-icon-upload" />
<div class="el-upload__text">拖到此处<em>点击上传</em></div>
<div slot="tip" class="el-upload__tip">仅解析第一个工作表未匹配到的单号会在结果中列出</div>
<div class="el-upload__text">拖到此处<em>点击选择</em>可多选</div>
<div slot="tip" class="el-upload__tip">单次最多 30 个文件仅解析每个文件的第一个工作表</div>
</el-upload>
<el-button
type="primary"
size="small"
:loading="rebateImportLoading"
style="margin-top: 10px"
@click="submitRebateExcelBatch"
>提交后台处理</el-button>
</el-form-item>
</el-form>
</el-dialog>
@@ -942,7 +952,7 @@
<el-table-column label="未匹配" prop="notFoundCount" width="72" align="center" />
<el-table-column label="上传人" prop="createBy" width="100" show-overflow-tooltip />
<el-table-column label="时间" prop="createTime" width="155" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<el-table-column label="操作" width="148" align="center" fixed="right">
<template slot-scope="scope">
<el-button
type="text"
@@ -950,6 +960,11 @@
:disabled="!scope.row.filePath"
@click="downloadRebateUploadFile(scope.row)"
>下载</el-button>
<el-button
type="text"
size="small"
@click="confirmDeleteRebateUpload(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -965,7 +980,7 @@
</template>
<script>
import { listJDOrders, updateJDOrder, delJDOrder, fetchLogisticsManually, batchMarkRebateReceived, generateExcelText, importGroupRebateExcel, listGroupRebateExcelUploads } from '@/api/system/jdorder'
import { listJDOrders, updateJDOrder, delJDOrder, fetchLogisticsManually, batchMarkRebateReceived, generateExcelText, importGroupRebateExcelBatch, listGroupRebateExcelUploads, deleteGroupRebateUpload } from '@/api/system/jdorder'
import { fillLogisticsByOrderNo, getTokenStatus, getTencentDocAuthUrl, testUserInfo, getAutoWriteConfig, reverseSyncThirdPartyOrderNo } from '@/api/jarvis/tendoc'
import { mapGetters } from 'vuex'
import ListLayout from '@/components/ListLayout'
@@ -1311,38 +1326,51 @@ export default {
const min = String(d.getMinutes()).padStart(2, '0')
return `${y}-${m}-${day} ${h}:${min}`
},
handleRebateExcelUpload(req) {
clearRebateExcelUploaderFiles() {
this.$nextTick(() => {
const u = this.$refs.rebateExcelUploader
if (u && typeof u.clearFiles === 'function') {
u.clearFiles()
}
})
},
onRebateImportDialogClosed() {
this.rebateImportTitle = ''
this.clearRebateExcelUploaderFiles()
},
onRebateExcelExceed() {
this.$message.warning('单次最多选择 30 个文件')
},
submitRebateExcelBatch() {
const u = this.$refs.rebateExcelUploader
const uploadFiles = (u && u.uploadFiles) ? u.uploadFiles : []
const files = uploadFiles.map((f) => f.raw).filter(Boolean)
if (!files.length) {
this.$message.warning('请先选择 Excel 文件')
return
}
const formData = new FormData()
formData.append('file', req.file)
files.forEach((file) => formData.append('files', file))
const title = (this.rebateImportTitle || '').trim()
if (title) {
if (files.length === 1 && title) {
formData.append('documentTitle', title)
}
this.rebateImportLoading = true
importGroupRebateExcel(formData)
importGroupRebateExcelBatch(formData)
.then((res) => {
const data = res.data || {}
const msg = data.message || res.msg || (data.success === false ? '导入未完成' : '导入完成')
if (data.success === false) {
const msg = data.message || res.msg || '已处理'
if (data.failCount > 0) {
this.$message.warning(msg)
} else {
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.clearRebateExcelUploaderFiles()
this.rebateImportDialogVisible = false
this.getList()
})
.catch((e) => {
this.$message.error(e.message || '导入失败')
this.$message.error(e.message || '提交失败')
})
.finally(() => {
this.rebateImportLoading = false
@@ -1373,6 +1401,26 @@ export default {
const name = row.originalFilename || `rebate_upload_${row.id}.xlsx`
this.download(`/system/jdorder/groupRebateUpload/download/${row.id}`, {}, name)
},
confirmDeleteRebateUpload(row) {
const title = row.documentTitle || row.originalFilename || ('ID ' + row.id)
this.$confirm(
`将删除上传记录「${title}」,并尝试从订单中移除本次导入写入的后返备注。历史导入(无上传批次标记)可能无法自动清除订单备注,是否继续?`,
'删除后返上传记录',
{ type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' }
).then(() => {
return deleteGroupRebateUpload(row.id)
}).then((res) => {
const data = res.data || {}
const msg = data.message || res.msg || '已删除'
this.$message.success(msg)
this.fetchRebateUploadList()
this.getList()
}).catch((e) => {
if (e !== 'cancel') {
this.$message.error((e && e.message) || '删除失败')
}
})
},
resetQuery() {
this.queryParams = {
pageNum: 1,