879 lines
32 KiB
Vue
879 lines
32 KiB
Vue
<template>
|
||
<div>
|
||
<list-layout>
|
||
<!-- 搜索区域 -->
|
||
<template #search>
|
||
<el-form :inline="true" :model="queryParams" label-width="80px">
|
||
<el-form-item label="备注">
|
||
<el-input v-model="queryParams.remark" placeholder="单据备注" clearable size="small" @keyup.enter.native="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="分销标记">
|
||
<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-form-item>
|
||
<el-form-item label="订单号">
|
||
<el-input v-model="queryParams.orderId" placeholder="订单号" clearable size="small" @keyup.enter.native="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="下单人">
|
||
<el-input v-model="queryParams.buyer" placeholder="下单人" clearable size="small" @keyup.enter.native="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="地址">
|
||
<el-input v-model="queryParams.address" placeholder="收货地址" clearable size="small" @keyup.enter.native="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="状态">
|
||
<el-input v-model="queryParams.status" placeholder="备注/状态" clearable size="small" @keyup.enter.native="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="下单时间">
|
||
<el-date-picker
|
||
v-model="dateRange"
|
||
type="daterange"
|
||
start-placeholder="开始日期"
|
||
end-placeholder="结束日期"
|
||
value-format="yyyy-MM-dd"
|
||
size="small"
|
||
range-separator="至"
|
||
@change="handleDateRangeChange"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="完成日期">
|
||
<el-checkbox v-model="queryParams.hasFinishTime" @change="handleQuery">仅显示已完成订单</el-checkbox>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" size="small" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||
<el-button size="small" icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||
<el-button type="warning" size="small" icon="el-icon-download" @click="handleExport" v-hasPermi="['system:jdorder:export']">导出</el-button>
|
||
<el-button type="success" size="small" icon="el-icon-setting" @click="showAutoWriteConfig = true" title="配置H-TF订单自动写入腾讯文档">H-TF自动写入配置</el-button>
|
||
<el-button type="info" size="small" icon="el-icon-user" @click="handleTestUserInfo" title="测试腾讯文档用户信息接口">测试用户信息</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</template>
|
||
|
||
<!-- 表格区域 -->
|
||
<template #table>
|
||
<el-table :data="list" v-loading="loading" border stripe :default-sort="{prop: 'createTime', order: 'descending'}" @sort-change="handleSortChange" style="width: 100%;">
|
||
<el-table-column label="ID" prop="id" width="80" />
|
||
<el-table-column label="内部单号" prop="remark" width="160" sortable/>
|
||
|
||
<el-table-column label="订单号" prop="orderId" width="200">
|
||
<template slot-scope="scope">
|
||
<div>
|
||
<span style="margin-right: 8px;">{{ scope.row.orderId }}</span>
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
icon="el-icon-copy-document"
|
||
@click="copyToClipboard(scope.row.orderId)"
|
||
title="复制订单号"
|
||
>
|
||
复制
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="第三方单号" prop="thirdPartyOrderNo" width="180">
|
||
<template slot-scope="scope">
|
||
<div v-if="scope.row.thirdPartyOrderNo">
|
||
<span style="margin-right: 8px;">{{ scope.row.thirdPartyOrderNo }}</span>
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
icon="el-icon-copy-document"
|
||
@click="copyToClipboard(scope.row.thirdPartyOrderNo)"
|
||
title="复制第三方单号"
|
||
>
|
||
复制
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="标记" prop="distributionMark" width="80"/>
|
||
<el-table-column label="型号" prop="modelNumber" width="180"/>
|
||
<el-table-column label="地址" prop="address" min-width="320">
|
||
<template slot-scope="scope">
|
||
<div style="display: flex; align-items: center;">
|
||
<span class="ellipsis" style="flex:1; margin-right: 8px;" :title="scope.row.address">{{ scope.row.address }}</span>
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
icon="el-icon-copy-document"
|
||
@click="copyToClipboard(scope.row.address)"
|
||
title="复制地址"
|
||
>
|
||
复制
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="付款金额" prop="paymentAmount" width="120">
|
||
<template slot-scope="scope">{{ toYuan(scope.row.paymentAmount) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="后返金额" prop="rebateAmount" width="120">
|
||
<template slot-scope="scope">{{ toYuan(scope.row.rebateAmount) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="京粉实际价格" prop="jingfenActualPrice" width="140">
|
||
<template slot-scope="scope">{{ toYuan(scope.row.jingfenActualPrice) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="下单人" prop="buyer" width="140"/>
|
||
<el-table-column label="备注/状态" prop="status" min-width="160"/>
|
||
|
||
<el-table-column label="参与统计" prop="isCountEnabled" width="100">
|
||
<template slot-scope="scope">
|
||
<el-switch
|
||
v-model="scope.row.isCountEnabled"
|
||
:active-value="1"
|
||
:inactive-value="0"
|
||
@change="handleCountEnabledChange(scope.row)"
|
||
active-text="是"
|
||
inactive-text="否">
|
||
</el-switch>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="物流链接" prop="logisticsLink" width="200">
|
||
<template slot-scope="scope">
|
||
<div v-if="scope.row.logisticsLink">
|
||
<a :href="scope.row.logisticsLink" target="_blank" style="margin-right: 8px;">查看物流</a>
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
icon="el-icon-copy-document"
|
||
@click="copyToClipboard(scope.row.logisticsLink)"
|
||
title="复制链接"
|
||
>
|
||
复制
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="赔付金额" prop="proPriceAmount" width="140"/>
|
||
|
||
<el-table-column label="创建时间" prop="createTime" width="160" sortable="custom">
|
||
<template slot-scope="scope">{{ parseTime(scope.row.createTime) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="完成时间" prop="finishTime" width="160">
|
||
<template slot-scope="scope">{{ parseTime(scope.row.finishTime) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" fixed="right" width="240">
|
||
<template slot-scope="scope">
|
||
<el-button type="text" size="mini" style="color: #409EFF;" @click="handleSyncLogistics(scope.row)">
|
||
同步物流
|
||
</el-button>
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
style="color: #67C23A;"
|
||
@click="handleFetchLogistics(scope.row)"
|
||
:disabled="scope.row.distributionMark !== 'F' && scope.row.distributionMark !== 'PDD'"
|
||
:title="(scope.row.distributionMark !== 'F' && scope.row.distributionMark !== 'PDD') ? '仅支持F或PDD分销标识' : '获取物流信息'"
|
||
>
|
||
获取物流
|
||
</el-button>
|
||
<el-button type="text" size="mini" style="color: #f56c6c;" @click="handleDelete(scope.row)">
|
||
删除
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</template>
|
||
|
||
<!-- 分页区域 -->
|
||
<template #pagination>
|
||
<pagination
|
||
v-show="total > 0"
|
||
:total="total"
|
||
:page.sync="queryParams.pageNum"
|
||
:limit.sync="queryParams.pageSize"
|
||
@pagination="getList"
|
||
/>
|
||
</template>
|
||
</list-layout>
|
||
|
||
<!-- 获取物流信息对话框 -->
|
||
<el-dialog
|
||
title="获取物流信息(调试)"
|
||
:visible.sync="fetchLogisticsDialogVisible"
|
||
width="800px"
|
||
:close-on-click-modal="false"
|
||
>
|
||
<div v-loading="fetchLogisticsLoading">
|
||
<el-alert
|
||
v-if="fetchLogisticsResult"
|
||
:title="fetchLogisticsResult.success ? '获取成功' : '获取失败'"
|
||
:type="fetchLogisticsResult.success ? 'success' : 'error'"
|
||
:closable="false"
|
||
style="margin-bottom: 20px;"
|
||
/>
|
||
<el-form label-width="120px" v-if="fetchLogisticsResult">
|
||
<el-form-item label="错误信息" v-if="fetchLogisticsResult.error">
|
||
<el-alert
|
||
:title="fetchLogisticsResult.error"
|
||
type="error"
|
||
:closable="false"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="订单ID" v-if="fetchLogisticsResult.orderId">
|
||
<span>{{ fetchLogisticsResult.orderId }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="订单号" v-if="fetchLogisticsResult.orderNo">
|
||
<span>{{ fetchLogisticsResult.orderNo }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="分销标识" v-if="fetchLogisticsResult.distributionMark">
|
||
<span>{{ fetchLogisticsResult.distributionMark }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="物流链接" v-if="fetchLogisticsResult.logisticsLink">
|
||
<el-input
|
||
:value="fetchLogisticsResult.logisticsLink"
|
||
readonly
|
||
type="textarea"
|
||
:rows="2"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="请求URL" v-if="fetchLogisticsResult.requestUrl">
|
||
<el-input
|
||
:value="fetchLogisticsResult.requestUrl"
|
||
readonly
|
||
type="textarea"
|
||
:rows="2"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="返回数据(原始)" v-if="fetchLogisticsResult.responseRaw || fetchLogisticsResult.responseData">
|
||
<el-input
|
||
:value="fetchLogisticsResult.responseRaw || JSON.stringify(fetchLogisticsResult.responseData, null, 2)"
|
||
readonly
|
||
type="textarea"
|
||
:rows="10"
|
||
style="font-family: monospace; font-size: 12px;"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="返回数据(解析后)" v-if="fetchLogisticsResult.responseData">
|
||
<el-input
|
||
:value="JSON.stringify(fetchLogisticsResult.responseData, null, 2)"
|
||
readonly
|
||
type="textarea"
|
||
:rows="10"
|
||
style="font-family: monospace; font-size: 12px;"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div v-else style="text-align: center; padding: 40px;">
|
||
<span style="color: #999;">正在获取物流信息...</span>
|
||
</div>
|
||
</div>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="fetchLogisticsDialogVisible = false">关闭</el-button>
|
||
<el-button
|
||
type="primary"
|
||
@click="copyFetchLogisticsResult"
|
||
v-if="fetchLogisticsResult"
|
||
>
|
||
复制结果
|
||
</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 同步物流对话框 -->
|
||
<el-dialog
|
||
title="同步物流到腾讯文档"
|
||
:visible.sync="syncLogisticsDialogVisible"
|
||
width="600px"
|
||
:close-on-click-modal="false"
|
||
>
|
||
<el-form :model="syncLogisticsForm" label-width="140px">
|
||
<el-form-item label="文件ID" required>
|
||
<el-input
|
||
v-model="syncLogisticsForm.fileId"
|
||
placeholder="从腾讯文档URL中获取,例如:Dxxxxxxxxxxxxx"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="工作表ID" required>
|
||
<el-input
|
||
v-model="syncLogisticsForm.sheetId"
|
||
placeholder="从腾讯文档URL中获取,例如:BB08J2"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="表头行号">
|
||
<el-input-number
|
||
v-model="syncLogisticsForm.headerRow"
|
||
:min="1"
|
||
style="width: 100%"
|
||
/>
|
||
<div style="font-size: 12px; color: #999; margin-top: 4px;">
|
||
表头所在行号(默认第1行)
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="单号列索引(可选)">
|
||
<el-input-number
|
||
v-model="syncLogisticsForm.orderNoColumn"
|
||
:min="0"
|
||
style="width: 100%"
|
||
placeholder="不填写则自动识别"
|
||
/>
|
||
<div style="font-size: 12px; color: #999; margin-top: 4px;">
|
||
单号列的索引(从0开始),不填写则自动识别
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="物流链接列索引(可选)">
|
||
<el-input-number
|
||
v-model="syncLogisticsForm.logisticsLinkColumn"
|
||
:min="0"
|
||
style="width: 100%"
|
||
placeholder="不填写则自动识别"
|
||
/>
|
||
<div style="font-size: 12px; color: #999; margin-top: 4px;">
|
||
物流链接列的索引(从0开始),不填写则自动识别
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="说明">
|
||
<div style="font-size: 12px; color: #666; line-height: 1.6;">
|
||
<p>1. 点击"同步物流"时,如果没有token会自动打开授权页面</p>
|
||
<p>2. 完成授权后,点击"开始同步"即可自动刷新token并同步数据</p>
|
||
<p>3. 文件ID和工作表ID可以从腾讯文档URL中获取</p>
|
||
<p>4. 系统会自动从上次处理的最大行数-100开始读取,避免重复处理历史数据</p>
|
||
<p>5. 配置会自动保存,下次使用时无需重新填写</p>
|
||
</div>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="syncLogisticsDialogVisible = false">取消</el-button>
|
||
<el-button
|
||
type="primary"
|
||
:loading="syncLogisticsLoading"
|
||
@click="handleSyncLogisticsSubmit"
|
||
>
|
||
开始同步
|
||
</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- H-TF订单自动写入配置 -->
|
||
<tencent-doc-auto-write-config v-model="showAutoWriteConfig" @config-updated="handleAutoConfigUpdated" />
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listJDOrders, updateJDOrder, delJDOrder, fetchLogisticsManually } from '@/api/system/jdorder'
|
||
import { fillLogisticsByOrderNo, getTokenStatus, getTencentDocAuthUrl, testUserInfo } from '@/api/jarvis/tendoc'
|
||
import ListLayout from '@/components/ListLayout'
|
||
import TencentDocAutoWriteConfig from './components/TencentDocAutoWriteConfig'
|
||
|
||
export default {
|
||
name: 'JDOrderList',
|
||
components: {
|
||
ListLayout,
|
||
TencentDocAutoWriteConfig
|
||
},
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
list: [],
|
||
total: 0,
|
||
dateRange: [],
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 50,
|
||
remark: undefined,
|
||
distributionMark: undefined,
|
||
modelNumber: undefined,
|
||
link: undefined,
|
||
orderId: undefined,
|
||
buyer: undefined,
|
||
address: undefined,
|
||
status: undefined,
|
||
beginTime: null,
|
||
endTime: null,
|
||
orderBy: 'create_time',
|
||
isAsc: 'desc',
|
||
hasFinishTime: false
|
||
},
|
||
// 同步物流对话框
|
||
syncLogisticsDialogVisible: false,
|
||
syncLogisticsLoading: false,
|
||
syncLogisticsForm: {
|
||
fileId: '',
|
||
sheetId: '',
|
||
headerRow: 1,
|
||
orderNoColumn: null,
|
||
logisticsLinkColumn: null
|
||
},
|
||
currentOrder: null,
|
||
tokenValid: false,
|
||
tokenStatusChecked: false,
|
||
// H-TF订单自动写入配置
|
||
showAutoWriteConfig: false,
|
||
// 获取物流信息对话框
|
||
fetchLogisticsDialogVisible: false,
|
||
fetchLogisticsLoading: false,
|
||
fetchLogisticsResult: null
|
||
}
|
||
},
|
||
created() {
|
||
// 设置默认日期为今天
|
||
this.setDefaultDateRange()
|
||
this.getListWithFallback()
|
||
|
||
// 监听腾讯文档授权回调消息
|
||
this.handleOAuthMessage = (event) => {
|
||
if (event.data && event.data.type === 'tendoc_oauth_callback') {
|
||
if (event.data.success) {
|
||
this.$message.success(event.data.message || '授权成功!')
|
||
// 刷新token状态
|
||
this.checkTokenStatus()
|
||
} else {
|
||
this.$message.error(event.data.message || '授权失败')
|
||
}
|
||
}
|
||
}
|
||
window.addEventListener('message', this.handleOAuthMessage)
|
||
},
|
||
beforeDestroy() {
|
||
// 移除消息监听器
|
||
if (this.handleOAuthMessage) {
|
||
window.removeEventListener('message', this.handleOAuthMessage)
|
||
}
|
||
},
|
||
methods: {
|
||
/** 设置默认日期范围为今天 */
|
||
setDefaultDateRange() {
|
||
const today = new Date()
|
||
const todayStr = this.formatDate(today)
|
||
this.dateRange = [todayStr, todayStr]
|
||
this.queryParams.beginTime = todayStr
|
||
this.queryParams.endTime = todayStr
|
||
},
|
||
/** 格式化日期为 yyyy-MM-dd 格式 */
|
||
formatDate(date) {
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
},
|
||
getList() {
|
||
this.loading = true
|
||
listJDOrders(this.queryParams).then(res => {
|
||
this.list = (res.rows || res.data || [])
|
||
this.total = res.total || 0
|
||
this.loading = false
|
||
}).catch(() => { this.loading = false })
|
||
},
|
||
/** 智能查询列表,如果今天数据为空则查询昨天 */
|
||
async getListWithFallback() {
|
||
this.loading = true
|
||
|
||
try {
|
||
const res = await listJDOrders(this.queryParams)
|
||
this.list = (res.rows || res.data || [])
|
||
this.total = res.total || 0
|
||
|
||
// 如果今天的数据为空,且是默认查询(没有手动选择日期),则尝试查询昨天
|
||
if (this.list.length === 0 && this.isDefaultQuery()) {
|
||
this.$message.info('今天暂无慢单数据,正在查询昨天的数据...')
|
||
|
||
// 获取昨天的日期
|
||
const yesterday = new Date()
|
||
yesterday.setDate(yesterday.getDate() - 1)
|
||
const yesterdayStr = this.formatDate(yesterday)
|
||
|
||
// 更新查询参数为昨天
|
||
this.queryParams.beginTime = yesterdayStr
|
||
this.queryParams.endTime = yesterdayStr
|
||
this.dateRange = [yesterdayStr, yesterdayStr]
|
||
|
||
// 查询昨天的数据
|
||
const yesterdayRes = await listJDOrders(this.queryParams)
|
||
this.list = (yesterdayRes.rows || yesterdayRes.data || [])
|
||
this.total = yesterdayRes.total || 0
|
||
|
||
if (this.list.length > 0) {
|
||
this.$message.success(`已查询到昨天(${yesterdayStr})的慢单数据`)
|
||
} else {
|
||
this.$message.warning('昨天也没有慢单数据')
|
||
}
|
||
}
|
||
|
||
this.loading = false
|
||
} catch (error) {
|
||
this.loading = false
|
||
this.$message.error('查询失败,请稍后重试')
|
||
}
|
||
},
|
||
/** 判断是否为默认查询(今天的数据) */
|
||
isDefaultQuery() {
|
||
const today = new Date()
|
||
const todayStr = this.formatDate(today)
|
||
return this.queryParams.beginTime === todayStr && this.queryParams.endTime === todayStr
|
||
},
|
||
handleQuery() {
|
||
this.queryParams.pageNum = 1
|
||
this.getList()
|
||
},
|
||
resetQuery() {
|
||
this.queryParams = {
|
||
pageNum: 1,
|
||
pageSize: 50,
|
||
remark: undefined,
|
||
distributionMark: undefined,
|
||
modelNumber: undefined,
|
||
link: undefined,
|
||
orderId: undefined,
|
||
buyer: undefined,
|
||
address: undefined,
|
||
status: undefined,
|
||
beginTime: null,
|
||
endTime: null,
|
||
orderBy: 'create_time',
|
||
isAsc: 'desc',
|
||
hasFinishTime: false
|
||
}
|
||
// 重置排序为默认降序
|
||
this.queryParams.orderBy = 'create_time'
|
||
this.queryParams.isAsc = 'desc'
|
||
// 重置后重新设置默认日期为今天
|
||
this.setDefaultDateRange()
|
||
this.getListWithFallback()
|
||
},
|
||
/** 日期范围变化处理 */
|
||
handleDateRangeChange(dates) {
|
||
if (dates && dates.length === 2) {
|
||
this.queryParams.beginTime = dates[0]
|
||
this.queryParams.endTime = dates[1]
|
||
} else {
|
||
this.queryParams.beginTime = null
|
||
this.queryParams.endTime = null
|
||
}
|
||
},
|
||
/** 表格排序变化处理 */
|
||
handleSortChange(sortInfo) {
|
||
if (sortInfo && sortInfo.prop === 'createTime') {
|
||
if (sortInfo.order === 'ascending') {
|
||
this.queryParams.orderBy = 'create_time'
|
||
this.queryParams.isAsc = 'asc'
|
||
} else if (sortInfo.order === 'descending') {
|
||
this.queryParams.orderBy = 'create_time'
|
||
this.queryParams.isAsc = 'desc'
|
||
} else {
|
||
// 取消排序,恢复默认
|
||
this.queryParams.orderBy = 'create_time'
|
||
this.queryParams.isAsc = 'desc'
|
||
}
|
||
// 重新查询数据
|
||
this.queryParams.pageNum = 1
|
||
this.getList()
|
||
}
|
||
},
|
||
toYuan(n) {
|
||
if (n == null || n === '') return ''
|
||
const num = Number(n)
|
||
if (Number.isNaN(num)) return n
|
||
return num.toFixed(2)
|
||
},
|
||
/** 导出按钮操作 */
|
||
handleExport() {
|
||
this.download('/system/jdorder/export', this.queryParams, `京东订单数据_${new Date().getTime()}.xlsx`)
|
||
},
|
||
/** 复制到剪贴板 */
|
||
copyToClipboard(text) {
|
||
if (navigator.clipboard && window.isSecureContext) {
|
||
// 使用现代 Clipboard API
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
this.$message.success('已复制到剪贴板')
|
||
}).catch(() => {
|
||
this.fallbackCopyTextToClipboard(text)
|
||
})
|
||
} else {
|
||
// 降级方案
|
||
this.fallbackCopyTextToClipboard(text)
|
||
}
|
||
},
|
||
/** 降级复制方案 */
|
||
fallbackCopyTextToClipboard(text) {
|
||
const textArea = document.createElement('textarea')
|
||
textArea.value = text
|
||
textArea.style.position = 'fixed'
|
||
textArea.style.left = '-999999px'
|
||
textArea.style.top = '-999999px'
|
||
document.body.appendChild(textArea)
|
||
textArea.focus()
|
||
textArea.select()
|
||
|
||
try {
|
||
const successful = document.execCommand('copy')
|
||
if (successful) {
|
||
this.$message.success('已复制到剪贴板')
|
||
} else {
|
||
this.$message.error('复制失败,请手动复制')
|
||
}
|
||
} catch (err) {
|
||
this.$message.error('复制失败,请手动复制')
|
||
}
|
||
|
||
document.body.removeChild(textArea)
|
||
},
|
||
/** 处理统计开关变化 */
|
||
handleCountEnabledChange(row) {
|
||
// 调用后端API更新数据库
|
||
updateJDOrder(row).then(() => {
|
||
this.$message.success(`订单 ${row.remark} 的统计状态已更新为:${row.isCountEnabled ? '参与统计' : '不参与统计'}`)
|
||
}).catch(() => {
|
||
this.$message.error('更新失败,请稍后重试')
|
||
// 恢复原状态
|
||
row.isCountEnabled = row.isCountEnabled ? 0 : 1
|
||
})
|
||
},
|
||
/** 删除单条记录(需输入随机确认码) */
|
||
async handleDelete(row) {
|
||
const code = String(Math.floor(100000 + Math.random() * 900000))
|
||
try {
|
||
await this.$prompt(`请输入确认码以删除该订单:${code}`, '删除确认', {
|
||
confirmButtonText: '删除',
|
||
cancelButtonText: '取消',
|
||
inputPlaceholder: '请输入上方确认码',
|
||
inputValidator: (value) => {
|
||
if (value === code) return true
|
||
return '确认码不匹配,请重新输入'
|
||
},
|
||
inputErrorMessage: '确认码不匹配',
|
||
type: 'warning',
|
||
dangerouslyUseHTMLString: false
|
||
})
|
||
} catch (e) {
|
||
return
|
||
}
|
||
|
||
this.loading = true
|
||
try {
|
||
await delJDOrder(row.id)
|
||
this.$message.success('删除成功')
|
||
this.getList()
|
||
} catch (e) {
|
||
this.$message.error('删除失败,请稍后重试')
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
|
||
/** 同步物流到腾讯文档 */
|
||
async handleSyncLogistics(row) {
|
||
try {
|
||
// 先检查配置是否完整
|
||
const configRes = await this.$http.get('/jarvis/tencentDoc/config')
|
||
if (configRes.code !== 200 || !configRes.data || !configRes.data.isConfigured) {
|
||
this.$confirm('检测到尚未完成H-TF自动写入配置,是否现在配置?', '提示', {
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.showAutoWriteConfig = true
|
||
})
|
||
return
|
||
}
|
||
|
||
// 确认同步
|
||
await this.$confirm(
|
||
`将从配置的腾讯文档中搜索并填充物流链接,是否继续?\n\n` +
|
||
`文档: ${configRes.data.fileId}\n` +
|
||
`工作表: ${configRes.data.sheetId}\n` +
|
||
`数据起始行: ${configRes.data.startRow}`,
|
||
'同步物流确认',
|
||
{ type: 'info' }
|
||
)
|
||
|
||
this.syncLogisticsLoading = true
|
||
// 直接同步,不需要打开对话框
|
||
await this.handleSyncLogisticsSubmit()
|
||
} catch (e) {
|
||
if (e !== 'cancel') {
|
||
this.$message.error('操作失败:' + (e.message || '未知错误'))
|
||
}
|
||
} finally {
|
||
this.syncLogisticsLoading = false
|
||
}
|
||
},
|
||
|
||
/** 打开授权页面并等待授权完成 */
|
||
async openAuthAndWait() {
|
||
try {
|
||
// 获取授权URL
|
||
const authUrlRes = await getTencentDocAuthUrl()
|
||
if (authUrlRes.code !== 200 || !authUrlRes.data) {
|
||
this.$message.error('获取授权URL失败')
|
||
return
|
||
}
|
||
|
||
const authUrl = authUrlRes.data
|
||
|
||
// 打开授权页面
|
||
this.$message.info('正在打开授权页面,请完成授权后继续...')
|
||
const width = 600
|
||
const height = 700
|
||
const left = (window.screen.width - width) / 2
|
||
const top = (window.screen.height - height) / 2
|
||
|
||
const authWindow = window.open(
|
||
authUrl,
|
||
'腾讯文档授权',
|
||
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`
|
||
)
|
||
|
||
// 监听窗口关闭事件(备用方案,如果postMessage失效)
|
||
const checkWindowClosed = setInterval(() => {
|
||
if (authWindow.closed) {
|
||
clearInterval(checkWindowClosed)
|
||
// 等待1秒后检查token状态(作为备用验证)
|
||
setTimeout(async () => {
|
||
try {
|
||
const tokenStatusRes = await getTokenStatus()
|
||
if (tokenStatusRes.data && tokenStatusRes.data.hasToken) {
|
||
// 如果已经通过postMessage收到成功消息,这里不再重复提示
|
||
} else {
|
||
// 如果没有收到postMessage,可能是授权失败
|
||
this.$message.warning('授权未完成,请重新尝试')
|
||
}
|
||
} catch (e) {
|
||
// 静默处理,避免重复提示
|
||
}
|
||
}, 1000)
|
||
}
|
||
}, 1000)
|
||
|
||
} catch (e) {
|
||
this.$message.error('打开授权页面失败: ' + (e.message || '未知错误'))
|
||
console.error('打开授权页面失败', e)
|
||
}
|
||
},
|
||
|
||
/** 检查后端token状态 */
|
||
async checkTokenStatus() {
|
||
try {
|
||
const res = await getTokenStatus()
|
||
if (res.code === 200 && res.data) {
|
||
this.tokenValid = res.data.hasToken === true
|
||
} else {
|
||
this.tokenValid = false
|
||
}
|
||
} catch (e) {
|
||
console.error('检查token状态失败', e)
|
||
this.tokenValid = false
|
||
} finally {
|
||
this.tokenStatusChecked = true
|
||
}
|
||
},
|
||
|
||
/** 测试获取用户信息接口 */
|
||
async handleTestUserInfo() {
|
||
try {
|
||
this.$message.info('正在测试用户信息接口...')
|
||
const res = await testUserInfo()
|
||
if (res.code === 200) {
|
||
this.$message.success('测试成功!用户信息:' + JSON.stringify(res.data, null, 2))
|
||
// 显示详细信息
|
||
this.$alert(
|
||
'<pre style="text-align: left; max-height: 400px; overflow: auto;">' +
|
||
JSON.stringify(res.data, null, 2) +
|
||
'</pre>',
|
||
'用户信息测试结果',
|
||
{
|
||
dangerouslyUseHTMLString: true,
|
||
confirmButtonText: '确定',
|
||
type: 'success'
|
||
}
|
||
)
|
||
} else {
|
||
this.$message.error('测试失败:' + (res.msg || '未知错误'))
|
||
}
|
||
} catch (e) {
|
||
this.$message.error('测试失败:' + (e.message || '未知错误'))
|
||
console.error('测试用户信息失败', e)
|
||
}
|
||
},
|
||
|
||
/** 同步物流链接 */
|
||
async handleSyncLogisticsSubmit() {
|
||
try {
|
||
// 后端会自动从配置中读取 fileId、sheetId、startRow 等参数
|
||
// 前端只需传递空对象即可
|
||
const res = await fillLogisticsByOrderNo({})
|
||
|
||
if (res.code === 200) {
|
||
const data = res.data || {}
|
||
this.$message.success(
|
||
`同步成功!成功填充 ${data.filledCount || 0} 条,跳过 ${data.skippedCount || 0} 条,错误 ${data.errorCount || 0} 条`
|
||
)
|
||
} else {
|
||
this.$message.error(res.msg || '同步失败')
|
||
}
|
||
} catch (e) {
|
||
this.$message.error('同步失败:' + (e.message || '未知错误'))
|
||
console.error('同步物流失败', e)
|
||
}
|
||
},
|
||
|
||
/** 手动获取物流信息 */
|
||
async handleFetchLogistics(row) {
|
||
// 检查分销标识
|
||
if (row.distributionMark !== 'F' && row.distributionMark !== 'PDD') {
|
||
this.$message.warning('该订单的分销标识不是F或PDD,无需处理')
|
||
return
|
||
}
|
||
|
||
// 检查物流链接
|
||
if (!row.logisticsLink || !row.logisticsLink.trim()) {
|
||
this.$message.warning('该订单暂无物流链接')
|
||
return
|
||
}
|
||
|
||
this.fetchLogisticsDialogVisible = true
|
||
this.fetchLogisticsLoading = true
|
||
this.fetchLogisticsResult = null
|
||
|
||
try {
|
||
const res = await fetchLogisticsManually({ orderId: row.id })
|
||
|
||
if (res.code === 200) {
|
||
this.fetchLogisticsResult = {
|
||
success: true,
|
||
...res.data
|
||
}
|
||
this.$message.success('获取物流信息成功,数据已记录到日志文件')
|
||
} else {
|
||
this.fetchLogisticsResult = {
|
||
success: false,
|
||
error: res.msg || '获取失败'
|
||
}
|
||
this.$message.error(res.msg || '获取物流信息失败')
|
||
}
|
||
} catch (e) {
|
||
this.fetchLogisticsResult = {
|
||
success: false,
|
||
error: e.message || '请求异常'
|
||
}
|
||
this.$message.error('获取物流信息失败: ' + (e.message || '未知错误'))
|
||
console.error('获取物流信息失败', e)
|
||
} finally {
|
||
this.fetchLogisticsLoading = false
|
||
}
|
||
},
|
||
|
||
/** 复制获取物流信息的结果 */
|
||
copyFetchLogisticsResult() {
|
||
if (!this.fetchLogisticsResult) return
|
||
|
||
const resultText = JSON.stringify(this.fetchLogisticsResult, null, 2)
|
||
this.copyToClipboard(resultText)
|
||
},
|
||
|
||
/** H-TF订单自动写入配置更新后的回调 */
|
||
handleAutoConfigUpdated() {
|
||
this.$message.success('H-TF订单自动写入配置已更新')
|
||
}
|
||
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
</style>
|
||
|
||
|