525 lines
16 KiB
Vue
525 lines
16 KiB
Vue
<template>
|
|
<div class="app-container">
|
|
<div class="detail-container">
|
|
<el-card class="box-card" shadow="hover">
|
|
<!-- 标题和操作按钮 -->
|
|
<div class="card-header">
|
|
<div class="header-title">
|
|
<span class="title">订单详情</span>
|
|
<dict-tag :options="dict.type.ss_order_status" :value="order.status"/>
|
|
</div>
|
|
<div class="operation-buttons">
|
|
<el-button
|
|
type="primary"
|
|
icon="el-icon-card"
|
|
size="small"
|
|
@click="handleRefund"
|
|
v-has-permi="['system:room:refund']"
|
|
>退款</el-button>
|
|
<el-button
|
|
type="danger"
|
|
icon="el-icon-close"
|
|
size="small"
|
|
@click="handleClose"
|
|
v-has-permi="['system:room:close']"
|
|
>结束订单</el-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 左侧详情信息 -->
|
|
<el-row :span="24">
|
|
<el-col :span="20">
|
|
<div class="detail-left">
|
|
<!-- 所有info-section -->
|
|
<div class="info-sections">
|
|
<!-- 基本信息 -->
|
|
<div class="info-section">
|
|
<h3>
|
|
<i class="el-icon-info"></i>
|
|
基本信息
|
|
</h3>
|
|
<el-descriptions :column="3" border>
|
|
<el-descriptions-item label="订单号">{{ order.orderNo }}</el-descriptions-item>
|
|
<el-descriptions-item label="用户">
|
|
<router-link :to="`/user/detail/${order.userId}`" class="link-type">
|
|
{{ order.userName }}
|
|
</router-link>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="预约时间">
|
|
<template v-if="order.reserveStartTime && order.reserveEndTime">
|
|
{{ parseTime(order.reserveStartTime) }} 至 {{ parseTime(order.reserveEndTime) }}
|
|
</template>
|
|
<template v-else>
|
|
--
|
|
</template>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="房间/设施">
|
|
<router-link :to="`/system/roomDetail/index/${order.roomId}`" class="link-type">
|
|
{{ order.roomName }}
|
|
</router-link>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="店铺">
|
|
<router-link :to="`/system/storeDetail/index/${order.storeId}`" class="link-type">
|
|
{{ order.storeName }}
|
|
</router-link>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="大门">
|
|
<router-link
|
|
v-if="order.gateSn"
|
|
:to="`/system/deviceDetail/index/${order.gateId}`"
|
|
class="link-type">
|
|
{{ order.gateSn }}
|
|
</router-link>
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
</div>
|
|
|
|
<!-- 套餐信息 -->
|
|
<div class="info-section">
|
|
<h3>
|
|
<i class="el-icon-goods"></i>
|
|
套餐信息
|
|
</h3>
|
|
<el-descriptions :column="3" border>
|
|
<el-descriptions-item label="收费模式">
|
|
<dict-tag :options="dict.type.ss_fee_rule_mode" :value="order.mode"/>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="套餐名称">{{ order.ruleName }}</el-descriptions-item>
|
|
<el-descriptions-item label="套餐时长">{{ order.duration }} 小时</el-descriptions-item>
|
|
</el-descriptions>
|
|
</div>
|
|
|
|
<!-- 商户信息 -->
|
|
<div class="info-section">
|
|
<h3>
|
|
<i class="el-icon-office-building"></i>
|
|
商户信息
|
|
</h3>
|
|
<el-descriptions :column="3" border>
|
|
<el-descriptions-item label="商户名称">
|
|
<router-link :to="`/user/detail/${order.merchantId}`" class="link-type">
|
|
{{ order.merchantName }}
|
|
</router-link>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="商户ID">{{ order.merchantId }}</el-descriptions-item>
|
|
</el-descriptions>
|
|
</div>
|
|
|
|
<!-- 支付信息 -->
|
|
<div class="info-section">
|
|
<h3>
|
|
<i class="el-icon-money"></i>
|
|
支付信息
|
|
</h3>
|
|
<el-descriptions :column="3" border>
|
|
<el-descriptions-item label="支付方式">
|
|
<dict-tag :options="dict.type.ss_pay_type" :value="order.payType"/>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="支付状态">
|
|
<dict-tag :options="dict.type.et_order_pay_status" :value="order.paid"/>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="支付时间">
|
|
{{ parseTime(order.payTime) }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="订单金额">{{ order.totalFee | dv }} 元</el-descriptions-item>
|
|
<el-descriptions-item label="实付金额">{{ order.payFee | dv }} 元</el-descriptions-item>
|
|
<el-descriptions-item label="手续费">{{ order.handlingCharge | dv }} 元</el-descriptions-item>
|
|
<el-descriptions-item label="平台服务费">{{ order.platformServiceFee | dv }} 元</el-descriptions-item>
|
|
<el-descriptions-item label="押金扣除">{{ order.depositDeduction | dv }} 元</el-descriptions-item>
|
|
<el-descriptions-item label="扣除金额">{{ order.deductionAmount | dv }} 元</el-descriptions-item>
|
|
</el-descriptions>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-col>
|
|
<el-col :span="4">
|
|
<!-- 右侧操作履历 -->
|
|
<div class="detail-right">
|
|
<div class="info-sections history-section">
|
|
<h3>
|
|
<i class="el-icon-time" style="color: #E6A23C;"></i>
|
|
操作履历
|
|
</h3>
|
|
<el-timeline class="padding0">
|
|
<el-timeline-item
|
|
v-for="activity in order.orderOpers"
|
|
:key="activity.operId"
|
|
:type="getTimelineItemType(activity.operType)"
|
|
:timestamp="activity.createTime"
|
|
:color="getTimelineItemColor(activity.operType)"
|
|
>
|
|
<div class="timeline-content">
|
|
<span class="operation-type" :style="{ color: getTimelineItemColor(activity.operType) }">
|
|
{{ dict.type.ss_order_oper_type[activity.operType].label }}
|
|
</span>
|
|
<el-tooltip placement="right" effect="light" popper-class="operation-tooltip">
|
|
<div slot="content">
|
|
<div>{{ activity.details }}</div>
|
|
<div v-if="activity.beforeAmount !== activity.afterAmount">
|
|
金额变更: {{ activity.beforeAmount }}元 -> {{ activity.afterAmount }}元
|
|
</div>
|
|
<div>操作人: {{ activity.operPhone }}</div>
|
|
</div>
|
|
<i
|
|
class="el-icon-info tooltip-icon"
|
|
:style="{ color: getTimelineItemColor(activity.operType) }"
|
|
></i>
|
|
</el-tooltip>
|
|
</div>
|
|
</el-timeline-item>
|
|
</el-timeline>
|
|
</div>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
</el-card>
|
|
|
|
<!-- 分账明细标签页 -->
|
|
<el-card class="box-card detail-tabs" shadow="hover">
|
|
<el-tabs v-model="activeTab">
|
|
<el-tab-pane label="分账明细" name="detail" :lazy="true">
|
|
<detail v-if="order.orderNo != null"
|
|
:query="{
|
|
orderNo: order.orderNo
|
|
}"
|
|
/>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
</el-card>
|
|
</div>
|
|
<!-- 退款弹窗 -->
|
|
<refund-dialog :show.sync="showRefund" :pay-id="order.payId" :amount="order.payFee" @success="getDetail(orderId)" />
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { getOrder } from "@/api/system/order";
|
|
import { parseTime } from '@/utils/ruoyi'
|
|
import Detail from '@/views/system/detail/index.vue'
|
|
import RefundDialog from './components/RefundDialog'
|
|
import Log from "@/views/system/commandLog/index.vue";
|
|
|
|
export default {
|
|
name: "OrderDetail",
|
|
dicts: ['ss_order_status', 'ss_pay_type', 'et_order_pay_status', 'ss_fee_rule_mode', 'ss_order_oper_type'],
|
|
components: {
|
|
Log,
|
|
RefundDialog,
|
|
Detail
|
|
},
|
|
data() {
|
|
return {
|
|
// 订单信息
|
|
order: {},
|
|
// 退款弹窗显示状态
|
|
showRefund: false,
|
|
// 当前激活的标签页
|
|
activeTab: 'detail',
|
|
// 订单ID
|
|
orderId: null,
|
|
|
|
// 域名
|
|
domain: '',
|
|
}
|
|
},
|
|
created() {
|
|
this.orderId = this.$route.params.orderId;
|
|
this.getDetail(this.orderId);
|
|
},
|
|
methods: {
|
|
/** 获取订单详情 */
|
|
async getDetail(orderId) {
|
|
try {
|
|
const response = await getOrder(orderId);
|
|
this.order = response.data;
|
|
} catch (error) {
|
|
console.error('获取订单详情失败:', error);
|
|
this.$message.error('获取订单详情失败');
|
|
}
|
|
},
|
|
|
|
/** 处理退款 */
|
|
handleRefund() {
|
|
this.showRefund = true;
|
|
},
|
|
|
|
/** 处理结束订单 */
|
|
handleClose() {
|
|
this.$confirm(`确定结束订单【${this.order.orderNo}】吗?`, {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
// closeBill({billId: this.order.billId}).then(res => {
|
|
// if (res.code === 200) {
|
|
// this.$message.success("操作成功");
|
|
// this.getDetail(this.order.orderId);
|
|
// }
|
|
// })
|
|
}).catch(() => {})
|
|
},
|
|
|
|
/** 获取时间轴节点的类型 */
|
|
getTimelineItemType(operType) {
|
|
const typeMap = {
|
|
'1': 'primary', // 下单
|
|
'2': 'success', // 支付
|
|
'3': 'warning', // 更换包间
|
|
'4': 'info', // 结束订单
|
|
'6': 'warning', // 退款
|
|
'7': 'danger', // 用户取消
|
|
'8': 'danger' // 系统自动取消
|
|
};
|
|
return typeMap[operType] || 'info';
|
|
},
|
|
|
|
/** 获取时间轴节点的颜色 */
|
|
getTimelineItemColor(operType) {
|
|
const colorMap = {
|
|
'1': '#409EFF', // 下单 - 蓝色
|
|
'2': '#67C23A', // 支付 - 绿色
|
|
'3': '#E6A23C', // 更换包间 - 黄色
|
|
'4': '#909399', // 结束订单 - 灰色
|
|
'6': '#E6A23C', // 退款 - 黄色
|
|
'7': '#F56C6C', // 用户取消 - 红色
|
|
'8': '#F56C6C' // 系统自动取消 - 红色
|
|
};
|
|
return colorMap[operType] || '#909399';
|
|
},
|
|
|
|
/** 获取操作图标 */
|
|
getOperationIcon(operType) {
|
|
const iconMap = {
|
|
'1': 'el-icon-plus', // 下单 - 加号图标
|
|
'2': 'el-icon-check', // 支付 - 对勾图标
|
|
'3': 'el-icon-refresh', // 更换包间 - 刷新图标
|
|
'4': 'el-icon-circle-close', // 结束订单 - 关闭圆圈图标
|
|
'6': 'el-icon-back', // 退款 - 返回图标
|
|
'7': 'el-icon-close', // 用户取消 - 关闭图标
|
|
'8': 'el-icon-warning' // 系统自动取消 - 警告图标
|
|
};
|
|
return iconMap[operType] || 'el-icon-info';
|
|
},
|
|
|
|
/** 获取提示内容 */
|
|
getTooltipContent(activity) {
|
|
let content = [];
|
|
|
|
// 添加操作详情
|
|
if (activity.details) {
|
|
content.push(activity.details);
|
|
}
|
|
|
|
// 添加金额变更信息
|
|
if (activity.beforeAmount !== activity.afterAmount) {
|
|
content.push(`金额变更: ${activity.beforeAmount}元 -> ${activity.afterAmount}元`);
|
|
}
|
|
|
|
// 添加操作人信息
|
|
if (activity.operPhone) {
|
|
content.push(`操作人: ${activity.operPhone}`);
|
|
}
|
|
|
|
return content.join('\n');
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.app-container {
|
|
padding: 24px;
|
|
background-color: #f5f7fa;
|
|
min-height: calc(100vh - 84px);
|
|
|
|
.box-card {
|
|
margin-bottom: 24px;
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
|
}
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 10px;
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
.header-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
|
|
.title {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #303133;
|
|
}
|
|
}
|
|
}
|
|
|
|
.operation-buttons {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.info-sections {
|
|
padding: 0 20px;
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.info-section {
|
|
padding: 20px 0;
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
|
|
& + .info-section {
|
|
border-top: 1px solid #ebeef5;
|
|
}
|
|
|
|
h3 {
|
|
margin: 0 0 16px;
|
|
color: #303133;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
i {
|
|
margin-right: 8px;
|
|
font-size: 18px;
|
|
color: #409EFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
.detail-tabs {
|
|
margin-top: 24px;
|
|
}
|
|
|
|
.link-type {
|
|
color: #1890ff;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
|
|
&:hover {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
:deep(.el-descriptions) {
|
|
.el-descriptions-item__label {
|
|
background-color: #f5f7fa;
|
|
font-weight: 500;
|
|
min-width: 90px;
|
|
}
|
|
|
|
.el-descriptions-item__content {
|
|
padding: 12px 16px;
|
|
}
|
|
}
|
|
|
|
.detail-container {
|
|
width: 100%;
|
|
min-height: calc(100vh - 132px);
|
|
}
|
|
|
|
.detail-left {
|
|
.box-card {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.detail-right {
|
|
|
|
.history-section {
|
|
h3 {
|
|
margin-bottom: 20px;
|
|
}
|
|
}
|
|
}
|
|
.padding0{
|
|
padding: 0 !important;
|
|
}
|
|
.history-section {
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
|
|
:deep(.el-timeline) {
|
|
padding: 20px;
|
|
margin: 0 -20px;
|
|
|
|
.el-timeline-item__content {
|
|
.timeline-content {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.operation-type {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.tooltip-icon {
|
|
font-size: 16px;
|
|
cursor: help;
|
|
opacity: 0.8;
|
|
transition: opacity 0.2s;
|
|
display: inline-block;
|
|
width: 16px;
|
|
height: 16px;
|
|
line-height: 16px;
|
|
text-align: center;
|
|
|
|
&:hover {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
.el-timeline-item__timestamp {
|
|
color: #909399;
|
|
font-size: 12px;
|
|
line-height: 1.4;
|
|
padding-top: 4px;
|
|
}
|
|
|
|
.el-timeline-item__node {
|
|
background-color: #fff;
|
|
border: 2px solid currentColor;
|
|
}
|
|
|
|
.el-timeline-item__tail {
|
|
border-left: 2px solid #e4e7ed;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 添加全局样式 */
|
|
:deep(.operation-tooltip) {
|
|
.el-tooltip__popper {
|
|
max-width: 300px;
|
|
line-height: 1.5;
|
|
|
|
div {
|
|
margin: 4px 0;
|
|
|
|
&:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|