1
This commit is contained in:
@@ -175,3 +175,12 @@ export function fetchLogisticsManually(data) {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 订单搜索工具接口(返回简易字段)
|
||||||
|
export function searchOrders(query) {
|
||||||
|
return request({
|
||||||
|
url: '/system/jdorder/tools/search',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -85,6 +85,12 @@ export const constantRoutes = [
|
|||||||
component: () => import('@/views/public/CommentGenerator'),
|
component: () => import('@/views/public/CommentGenerator'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
// 订单搜索工具(内部使用,不易被发现的路径)
|
||||||
|
{
|
||||||
|
path: '/tools/order-search',
|
||||||
|
component: () => import('@/views/public/OrderSearch'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
// 公开订单提交页(不使用 Layout,无侧边栏)
|
// 公开订单提交页(不使用 Layout,无侧边栏)
|
||||||
{
|
{
|
||||||
path: '/public/order-submit',
|
path: '/public/order-submit',
|
||||||
|
|||||||
472
src/views/public/OrderSearch.vue
Normal file
472
src/views/public/OrderSearch.vue
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
<template>
|
||||||
|
<div class="order-search-container">
|
||||||
|
<div class="search-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>订单搜索工具</h3>
|
||||||
|
<span class="header-desc">快速搜索下好的订单</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-form">
|
||||||
|
<el-form :model="searchForm" label-width="100px" label-position="top">
|
||||||
|
<el-form-item label="单号搜索">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.orderNo"
|
||||||
|
placeholder="请输入订单号/第三方单号/内部单号(至少5个字符)"
|
||||||
|
clearable
|
||||||
|
size="medium"
|
||||||
|
@keyup.enter.native="handleSearch"
|
||||||
|
@input="handleOrderNoInput"
|
||||||
|
/>
|
||||||
|
<div class="input-tip">
|
||||||
|
<i class="el-icon-info"></i>
|
||||||
|
至少输入5个字符,将自动过滤TF、H、F、PDD等搜索词
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="地址搜索">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.address"
|
||||||
|
placeholder="请输入收货地址关键词(至少3个字符)"
|
||||||
|
clearable
|
||||||
|
size="medium"
|
||||||
|
@keyup.enter.native="handleSearch"
|
||||||
|
@input="handleAddressInput"
|
||||||
|
/>
|
||||||
|
<div class="input-tip">
|
||||||
|
<i class="el-icon-info"></i>
|
||||||
|
至少输入3个字符进行模糊搜索
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="medium"
|
||||||
|
icon="el-icon-search"
|
||||||
|
@click="handleSearch"
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="!canSearch"
|
||||||
|
>
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
size="medium"
|
||||||
|
icon="el-icon-refresh"
|
||||||
|
@click="handleReset"
|
||||||
|
>
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索结果 -->
|
||||||
|
<div v-if="hasSearched" class="result-section">
|
||||||
|
<el-divider>
|
||||||
|
<span>搜索结果(共 {{ total }} 条)</span>
|
||||||
|
</el-divider>
|
||||||
|
|
||||||
|
<div v-if="loading" class="loading-container">
|
||||||
|
<i class="el-icon-loading"></i>
|
||||||
|
<span>搜索中...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="orderList.length === 0" class="empty-container">
|
||||||
|
<el-empty description="未找到匹配的订单" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="order-list">
|
||||||
|
<el-table
|
||||||
|
:data="orderList"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
style="width: 100%"
|
||||||
|
:default-sort="{prop: 'createTime', order: 'descending'}"
|
||||||
|
>
|
||||||
|
<el-table-column label="内部单号" prop="remark" width="140" />
|
||||||
|
<el-table-column label="京东单号" prop="orderId" width="180" />
|
||||||
|
<el-table-column label="第三方单号" prop="thirdPartyOrderNo" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.thirdPartyOrderNo">{{ scope.row.thirdPartyOrderNo }}</span>
|
||||||
|
<span v-else style="color: #999;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="型号" prop="modelNumber" width="160" />
|
||||||
|
<el-table-column label="地址" prop="address" min-width="280" show-overflow-tooltip />
|
||||||
|
<el-table-column label="退款状态" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.isRefunded === 1 ? 'warning' : 'info'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ scope.row.isRefunded === 1 ? '已退款' : '未退款' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="后返到账" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.isRebateReceived === 1 ? 'success' : 'info'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ scope.row.isRebateReceived === 1 ? '已到账' : '未到账' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="赔付金额" prop="proPriceAmount" width="110" align="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ formatAmount(scope.row.proPriceAmount) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="订单状态" prop="orderStatus" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag
|
||||||
|
v-if="scope.row.orderStatus != null"
|
||||||
|
:type="getOrderStatusType(scope.row.orderStatus)"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ getOrderStatusText(scope.row.orderStatus) }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else style="color: #999;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="备注" prop="status" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column label="创建时间" prop="createTime" width="160">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ parseTime(scope.row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-container" v-if="total > 0">
|
||||||
|
<el-pagination
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="queryParams.pageNum"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="queryParams.pageSize"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="total"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listJDOrders, searchOrders } from '@/api/system/jdorder'
|
||||||
|
import { parseTime } from '@/utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OrderSearch',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchForm: {
|
||||||
|
orderNo: '',
|
||||||
|
address: ''
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
hasSearched: false,
|
||||||
|
orderList: [],
|
||||||
|
total: 0,
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20
|
||||||
|
},
|
||||||
|
// 需要过滤的搜索词
|
||||||
|
filteredKeywords: ['TF', 'H', 'F', 'PDD']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
canSearch() {
|
||||||
|
const orderNo = (this.searchForm.orderNo || '').trim()
|
||||||
|
const address = (this.searchForm.address || '').trim()
|
||||||
|
|
||||||
|
// 至少有一个搜索条件满足要求
|
||||||
|
const orderNoValid = orderNo.length >= 5 && !this.isFilteredKeyword(orderNo)
|
||||||
|
const addressValid = address.length >= 3
|
||||||
|
|
||||||
|
return orderNoValid || addressValid
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 检查是否是过滤的关键词
|
||||||
|
isFilteredKeyword(keyword) {
|
||||||
|
const upperKeyword = keyword.toUpperCase()
|
||||||
|
return this.filteredKeywords.some(kw => upperKeyword.includes(kw.toUpperCase()))
|
||||||
|
},
|
||||||
|
// 处理单号输入
|
||||||
|
handleOrderNoInput(value) {
|
||||||
|
// 如果输入的是过滤关键词,清空
|
||||||
|
if (value && value.length < 5) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.isFilteredKeyword(value)) {
|
||||||
|
this.$message.warning('该搜索词已被过滤,请输入其他关键词')
|
||||||
|
this.searchForm.orderNo = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 处理地址输入
|
||||||
|
handleAddressInput(value) {
|
||||||
|
// 地址输入不需要特殊处理
|
||||||
|
},
|
||||||
|
// 执行搜索
|
||||||
|
async handleSearch() {
|
||||||
|
if (!this.canSearch) {
|
||||||
|
this.$message.warning('请输入有效的搜索条件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderNo = (this.searchForm.orderNo || '').trim()
|
||||||
|
const address = (this.searchForm.address || '').trim()
|
||||||
|
|
||||||
|
// 验证搜索条件
|
||||||
|
if (orderNo && (orderNo.length < 5 || this.isFilteredKeyword(orderNo))) {
|
||||||
|
this.$message.warning('单号搜索至少需要5个字符,且不能包含TF、H、F、PDD等关键词')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address && address.length < 3) {
|
||||||
|
this.$message.warning('地址搜索至少需要3个字符')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!orderNo && !address) {
|
||||||
|
this.$message.warning('请至少输入一个搜索条件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
this.hasSearched = true
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构建查询参数
|
||||||
|
const queryParams = {
|
||||||
|
pageNum: this.queryParams.pageNum,
|
||||||
|
pageSize: this.queryParams.pageSize,
|
||||||
|
orderSearch: orderNo || undefined,
|
||||||
|
address: address || undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用专门的搜索接口
|
||||||
|
const res = await searchOrders(queryParams)
|
||||||
|
|
||||||
|
if (res && (res.code === 200 || res.msg === '操作成功')) {
|
||||||
|
const list = (res.rows || res.data || [])
|
||||||
|
|
||||||
|
// 处理退款相关字段的默认值
|
||||||
|
this.orderList = list.map(item => ({
|
||||||
|
...item,
|
||||||
|
isRefunded: item.isRefunded != null ? item.isRefunded : 0,
|
||||||
|
isRefundReceived: item.isRefundReceived != null ? item.isRefundReceived : 0,
|
||||||
|
isRebateReceived: item.isRebateReceived != null ? item.isRebateReceived : 0
|
||||||
|
}))
|
||||||
|
|
||||||
|
this.total = res.total || 0
|
||||||
|
|
||||||
|
if (this.orderList.length === 0) {
|
||||||
|
this.$message.info('未找到匹配的订单')
|
||||||
|
} else {
|
||||||
|
this.$message.success(`找到 ${this.total} 条匹配的订单`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message.error(res && res.msg ? res.msg : '搜索失败')
|
||||||
|
this.orderList = []
|
||||||
|
this.total = 0
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索失败:', error)
|
||||||
|
this.$message.error('搜索失败,请稍后重试')
|
||||||
|
this.orderList = []
|
||||||
|
this.total = 0
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 重置搜索
|
||||||
|
handleReset() {
|
||||||
|
this.searchForm = {
|
||||||
|
orderNo: '',
|
||||||
|
address: ''
|
||||||
|
}
|
||||||
|
this.orderList = []
|
||||||
|
this.total = 0
|
||||||
|
this.hasSearched = false
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.queryParams.pageSize = 20
|
||||||
|
},
|
||||||
|
// 分页大小变化
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.queryParams.pageSize = val
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.handleSearch()
|
||||||
|
},
|
||||||
|
// 当前页变化
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.queryParams.pageNum = val
|
||||||
|
this.handleSearch()
|
||||||
|
},
|
||||||
|
// 格式化金额
|
||||||
|
formatAmount(amount) {
|
||||||
|
if (amount == null || amount === '') return '-'
|
||||||
|
const num = Number(amount)
|
||||||
|
if (Number.isNaN(num)) return amount
|
||||||
|
return num.toFixed(2)
|
||||||
|
},
|
||||||
|
// 解析时间
|
||||||
|
parseTime(time) {
|
||||||
|
if (!time) return '-'
|
||||||
|
return parseTime(time, '{y}-{m}-{d} {h}:{i}:{s}')
|
||||||
|
},
|
||||||
|
// 获取订单状态类型
|
||||||
|
getOrderStatusType(status) {
|
||||||
|
if (status == null) return 'info'
|
||||||
|
const statusStr = String(status)
|
||||||
|
if (statusStr.includes('完成') || statusStr.includes('成功')) return 'success'
|
||||||
|
if (statusStr.includes('退款') || statusStr.includes('失败')) return 'warning'
|
||||||
|
if (statusStr.includes('取消')) return 'danger'
|
||||||
|
return 'info'
|
||||||
|
},
|
||||||
|
// 获取订单状态文本
|
||||||
|
getOrderStatusText(status) {
|
||||||
|
if (status == null) return '-'
|
||||||
|
return String(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.order-search-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
max-width: 1400px;
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background: linear-gradient(135deg, #409EFF 0%, #67C23A 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-desc {
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-tip {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-tip i {
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-section {
|
||||||
|
padding: 0 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container i {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
|
animation: rotating 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotating {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-container {
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-list {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.order-search-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
padding: 16px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-section {
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
Reference in New Issue
Block a user