dct 7 mesiacov pred
rodič
commit
54aefdd1c5

+ 48 - 0
src/api/order.js

@@ -1,5 +1,6 @@
 import request from '@/utils/request'
 import util from '@/utils/util'
+import { useCache } from 'element-plus/es/components/virtual-list/src/hooks/use-cache.mjs';
 
 export function getOrders(params) {
   return request({
@@ -95,6 +96,53 @@ export function reviewRefundOrder(refundId, isApproved, remark) {
   });
 }
 
+export function getRefundOrderById(id) {
+  return request({
+    url: `/TourOrder/Refund/${id}`,
+    method: 'get'
+  });
+}
+
+export function getOrderRefundList(params) {
+  return request({
+    url: `/TourOrderRefund/Search`,
+    method: 'post',
+    data: params
+  });
+}
+
+export function getOrderRefundFields() {
+  return request({
+    url: `/TourOrderRefund/Paginator`,
+    method: 'post',
+    data: {
+      useCache: false,
+    }
+  });
+}
+
+// 退款审核成功会退款,如遇退款失败调用此接口重新退款
+export function wxRefundOrder(refundId) {
+  return request({
+    url: `/WxPortal/Refund/${refundId}`,
+    method: 'post'
+  });
+}
+
+export function getPaymentInfoById(id) {
+  return request({
+    url: `/TourOrderPayment/${id}`,
+    method: 'get'
+  });
+}
+
+export function getPaymentInfoByOrderId(orderId) {
+  return request({
+    url: `/TourOrderPayment/ByOrder/${orderId}`,
+    method: 'get'
+  });
+}
+
 export function getScheduleByOrderId(id) {
   return request({
     url: `/TourOrderDailySchedule/ByOrder/${id}`,

+ 56 - 51
src/layout/components/menuTree.vue

@@ -1,56 +1,61 @@
 <template>
   <div class="menu-list">
-    <!-- <el-sub-menu index="/travelAgency" v-if="isAdmin">
-      <template #title>
-        <span>旅行社</span>
-      </template>
-      <el-menu-item index="/travelAgency/list" v-if="isAdmin">
-        <span>旅行社管理</span>
-      </el-menu-item>
-    </el-sub-menu>
-    <el-sub-menu index="/product" v-if="isAdmin">
-      <template #title>
-        <span>产品</span>
-      </template>
-      <el-menu-item index="/product/category">
-        <span>产品分类</span>
-      </el-menu-item>
-      <el-menu-item index="/product/list">
-        <span>产品管理</span>
-      </el-menu-item>
-    </el-sub-menu>
-    <el-sub-menu index="/order">
-      <template #title>
-        <span>订单</span>
-      </template>
-      <el-menu-item index="/order/list">
-        <span>订单管理</span>
-      </el-menu-item>
-      <el-menu-item index="/order/review" v-if="isAdmin">
-        <span>订单审核</span>
-      </el-menu-item>
-    </el-sub-menu>
-    <el-sub-menu index="/sys" v-if="isAdmin">
-      <template #title>
-        <span>系统</span>
-      </template>
-      <el-menu-item index="/sys/company">
-        <span>公司管理</span>
-      </el-menu-item>
-      <el-menu-item index="/sys/department">
-        <span>部门管理</span>
-      </el-menu-item>
-      <el-menu-item index="/sys/user">
-        <span>用户管理</span>
-      </el-menu-item>
-      <el-menu-item index="/sys/role">
-        <span>角色管理</span>
-      </el-menu-item>
-      <el-menu-item index="/sys/menu">
-        <span>菜单管理</span>
-      </el-menu-item>
-    </el-sub-menu> -->
-    <el-sub-menu v-for="menu in menuData" :key="menu.id" :index="menu.value">
+    <template v-if="menuData.length === 0">
+      <el-sub-menu index="/travelAgency" v-if="isAdmin">
+        <template #title>
+          <span>旅行社</span>
+        </template>
+        <el-menu-item index="/travelAgency/list" v-if="isAdmin">
+          <span>旅行社管理</span>
+        </el-menu-item>
+      </el-sub-menu>
+      <el-sub-menu index="/product" v-if="isAdmin">
+        <template #title>
+          <span>产品</span>
+        </template>
+        <el-menu-item index="/product/category">
+          <span>产品分类</span>
+        </el-menu-item>
+        <el-menu-item index="/product/list">
+          <span>产品管理</span>
+        </el-menu-item>
+      </el-sub-menu>
+      <el-sub-menu index="/order">
+        <template #title>
+          <span>订单</span>
+        </template>
+        <el-menu-item index="/order/list">
+          <span>订单管理</span>
+        </el-menu-item>
+        <el-menu-item index="/order/review" v-if="isAdmin">
+          <span>订单审核</span>
+        </el-menu-item>
+        <el-menu-item index="/order/refund" v-if="isAdmin">
+          <span>订单退款</span>
+        </el-menu-item>
+      </el-sub-menu>
+      <el-sub-menu index="/sys" v-if="isAdmin">
+        <template #title>
+          <span>系统</span>
+        </template>
+        <el-menu-item index="/sys/company">
+          <span>公司管理</span>
+        </el-menu-item>
+        <el-menu-item index="/sys/department">
+          <span>部门管理</span>
+        </el-menu-item>
+        <el-menu-item index="/sys/user">
+          <span>用户管理</span>
+        </el-menu-item>
+        <el-menu-item index="/sys/role">
+          <span>角色管理</span>
+        </el-menu-item>
+        <el-menu-item index="/sys/menu">
+          <span>菜单管理</span>
+        </el-menu-item>
+      </el-sub-menu>
+    </template>
+    <el-sub-menu v-else v-for="menu in menuData" :key="menu.id" :index="menu.value">
       <template #title>
         <span>{{ menu.name }}</span>
       </template>

+ 5 - 0
src/router/index.js

@@ -81,6 +81,11 @@ const routes = [
         name: 'orderReview',
         component: () => import('@/views/order/review'),
       },
+      {
+        path: 'refund',
+        name: 'orderRefund',
+        component: () => import('@/views/order/refund'),
+      },
     ],
   },
   {

+ 53 - 0
src/utils/order.js

@@ -0,0 +1,53 @@
+export const getOrderStatusType = (status) => {
+  const types = {
+    'Draft': 'info',
+    'Submitted': 'warning',
+    'Approved': 'success',
+    'Paid': 'success',
+    'Confirmed': 'success',
+    'Processing': 'warning',
+    'Completed': 'success',
+    'Cancelled': 'danger',
+    'Cancelling': 'warning',
+    'Rejected': 'danger'
+  }
+  return types[status] || 'info'
+}
+
+export const getOrderStatusText = (status) => {
+  const texts = {
+    'Draft': '草稿',
+    'Submitted': '已提交',
+    'Approved': '已审核',
+    'Paid': '已支付',
+    'Confirmed': '已确认',
+    'Processing': '处理中',
+    'Completed': '已完成',
+    'Cancelled': '已取消',
+    'Cancelling': '取消中',
+    'Rejected': '已驳回'
+  }
+  return texts[status] || status
+}
+
+export const getPaymentStatusType = (status) => {
+  const types = {
+    'Unpaid': 'danger',
+    'Partial': 'warning',
+    'Paid': 'success',
+    'Refunding': 'info',
+    'Refunded': 'info'
+  }
+  return types[status] || 'info'
+}
+
+export const getPaymentStatusText = (status) => {
+  const texts = {
+    'Unpaid': '未支付',
+    'Partial': '部分支付',
+    'Paid': '已支付',
+    'Refunding': '退款中',
+    'Refunded': '已退款'
+  }
+  return texts[status] || status
+}

+ 163 - 0
src/views/order/detial.vue

@@ -0,0 +1,163 @@
+<template>
+  <el-dialog v-model="dialogVisible" title="订单详情" width="80%" :before-close="handleClose">
+    <div v-loading="loading" class="order-detail">
+      <el-descriptions title="订单信息" :column="2" border>
+        <el-descriptions-item label="订单号">{{ orderData.order?.orderNo }}</el-descriptions-item>
+        <el-descriptions-item label="旅行社">{{ orderData.order?.agencyName }}</el-descriptions-item>
+        <el-descriptions-item label="出行日期">
+          {{ orderData.order?.travelStartDate }} 至 {{ orderData.order?.travelEndDate }}
+        </el-descriptions-item>
+        <el-descriptions-item label="行程天数">{{ orderData.order?.travelDays }}天</el-descriptions-item>
+        <el-descriptions-item label="游客人数">{{ orderData.order?.touristCount }}人</el-descriptions-item>
+        <el-descriptions-item label="订单金额">¥{{ orderData.order?.totalAmount }}</el-descriptions-item>
+        <el-descriptions-item label="订单状态">
+          <el-tag :type="getOrderStatusType(orderData.order?.status)">{{ getOrderStatusText(orderData.order?.status)
+            }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="支付状态">
+          <el-tag :type="getPaymentStatusType(orderData.order?.paymentStatus)">{{
+            getPaymentStatusText(orderData.order?.paymentStatus) }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="支付方式">{{ orderData.order?.paymentMethod === 'Online' ? '在线支付' : orderData.order?.paymentMethod }}</el-descriptions-item>
+        <el-descriptions-item label="支付时间">{{ orderData.order?.paymentDate }}</el-descriptions-item>
+        <el-descriptions-item label="备注" :span="2">{{ orderData.order?.remark }}</el-descriptions-item>
+      </el-descriptions>
+
+      <el-divider />
+
+      <h3>行程安排</h3>
+      <el-timeline>
+        <el-timeline-item v-for="schedule in orderData.schedules" :key="schedule.id" :timestamp="schedule.scheduleDate">
+          <h4>{{ schedule.title }}</h4>
+          <p>{{ schedule.description }}</p>
+        </el-timeline-item>
+      </el-timeline>
+
+      <el-divider />
+
+      <h3>产品明细</h3>
+      <el-table :data="orderData.details" border max-height="400" stripe>
+        <el-table-column prop="productCode" label="产品编号" width="120" />
+        <el-table-column prop="productName" label="产品名称" />
+        <el-table-column prop="productType" label="产品类型" width="100">
+          <template #default="scope">
+            <el-tag :type="getProductTypeTag(scope.row.productType)">
+              {{ ProductTypes[scope.row.productType] || scope.row.productType }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="useDate" label="使用日期" width="180" />
+        <el-table-column prop="quantity" label="数量" width="80" />
+        <el-table-column prop="unitPrice" label="单价" width="100">
+          <template #default="{ row }">¥{{ row.unitPrice }}</template>
+        </el-table-column>
+        <el-table-column prop="totalAmount" label="总价" width="100">
+          <template #default="{ row }">¥{{ row.totalAmount }}</template>
+        </el-table-column>
+        <el-table-column prop="confirmStatus" label="确认状态" width="100" />
+      </el-table>
+
+      <template v-if="orderData.paymentInfo.length > 0">
+        <h3>支付记录</h3>
+        <el-table :data="orderData.paymentInfo" border size="small" max-height="300" stripe>
+          <el-table-column type="index" label="序号" align="center" width="60"></el-table-column>
+          <el-table-column prop="paymentNo" label="支付单号" min-width="180" show-overflow-tooltip></el-table-column>
+          <el-table-column prop="paymentAmount" label="支付金额" width="120" align="right">
+            <template #default="scope">
+              ¥{{ scope.row.paymentAmount?.toFixed(2) || '0.00' }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="paymentMethod" label="支付方式" width="120" align="center">
+            <template #default="scope">
+              {{ scope.row.paymentMethod === 'Online' ? '在线支付' : scope.row.paymentMethod }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="status" label="支付状态" width="100" align="center">
+            <template #default="scope">
+              <el-tag :type="scope.row.status === 'Pending' ? 'warning' : 'success'">
+                {{ scope.row.status === 'Pending' ? '待支付' : scope.row.status === 'Success' ? '已支付' : scope.row.status }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="paymentDate" label="支付时间" width="160">
+            <template #default="scope">
+              {{ scope.row.paymentDate ? moment(scope.row.paymentDate).format('YYYY-MM-DD HH:mm') : '-' }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="createDate" label="创建时间" width="160">
+            <template #default="scope">
+              {{ scope.row.createDate ? moment(scope.row.createDate).format('YYYY-MM-DD HH:mm') : '-' }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="remark" label="备注" min-width="200" show-overflow-tooltip></el-table-column>
+        </el-table>
+      </template>
+    </div>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, reactive } from 'vue'
+import { getOrderDetailById, getPaymentInfoByOrderId } from '@/api/order'
+import { ProductTypes } from '@/constant'
+import { getOrderStatusText, getOrderStatusType, getPaymentStatusText, getPaymentStatusType } from '@/utils/order';
+import moment from 'moment';
+
+const dialogVisible = ref(false)
+const loading = ref(false)
+const orderData = reactive({
+  order: null,
+  schedules: [],
+  details: [],
+  paymentInfo: []
+})
+
+const open = async (orderID) => {
+  dialogVisible.value = true
+  loading.value = true
+  try {
+    const response = await getOrderDetailById(orderID)
+    if (response.code === 200) {
+      Object.assign(orderData, response.data)
+    } else {
+      console.error('获取订单详情失败:', response.message)
+    }
+    const paymentResponse = await getPaymentInfoByOrderId(orderID)
+    if (paymentResponse.code === 200) {
+      orderData.paymentInfo = paymentResponse.data
+    } else {
+      console.error('获取支付信息失败:', paymentResponse.message)
+    }
+  } catch (error) {
+    console.error('获取订单详情失败:', error)
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleClose = () => {
+  dialogVisible.value = false
+}
+
+const getProductTypeTag = (type) => {
+  const tags = {
+    'Hotel': 'warning',
+    'Scenic': 'success',
+    'Restaurant': 'danger',
+    'Other': 'info'
+  };
+  return tags[type] || 'info';
+}
+
+defineExpose({ open })
+</script>
+
+<style scoped>
+.order-detail {
+  padding: 20px;
+}
+
+h3 {
+  margin: 20px 0 10px;
+}
+</style>

+ 19 - 57
src/views/order/index.vue

@@ -122,13 +122,14 @@
             <!-- <el-button type="success" link size="small" @click="audit(scope.row)">审核</el-button> -->
             <el-button type="info" link size="small" @click="attachment(scope.row)">附件</el-button>
             <el-button type="info" link size="small" @click="editSchedule(scope.row)">行程</el-button>
-            <el-button type="primary" link size="small" @click="editTourists(scope.row)">游客</el-button>
+            <el-button type="info" link size="small" @click="editTourists(scope.row)">游客</el-button>
+            <el-button type="info" link size="small" @click="view(scope.row)">详情</el-button>
           </div>
           <div>
-            <el-button v-if="userAccountId === 'testuser' && orderCanPay(scope.row)" type="warning" link size="small"
+            <el-button v-if="userAccountId === 'testuser' && orderCanPay(scope.row)" type="success" link size="small"
               @click="payment(scope.row)">收款</el-button>
-            <el-button v-if="!['Completed', 'Cancelled'].includes(scope.row.status)" type="primary" link size="small"
-              @click="cancel(scope.row)">取消</el-button>
+            <!-- <el-button v-if="!['Completed', 'Cancelled'].includes(scope.row.status)" type="warning" link size="small"
+              @click="cancel(scope.row)">取消</el-button> -->
             <el-button v-if="scope.row.status === 'Submitted'" type="danger" link size="small"
               @click="del(scope.row)">删除</el-button>
           </div>
@@ -913,7 +914,7 @@
         </span>
       </template>
     </el-dialog>
-
+    <order-detail-dialog ref="orderDetailDialog" :orderId="currentOrderId"></order-detail-dialog>
   </el-card>
 </template>
 <script>
@@ -945,7 +946,8 @@ import moment from 'moment/moment';
 import { ProductTypes, FileTypeMap } from '@/constant';
 import utils from '@/utils/util';
 import QRCode from 'qrcode';
-import { getAuthUrl } from '@/api/pay';
+import { getOrderStatusText, getOrderStatusType, getPaymentStatusText, getPaymentStatusType } from '@/utils/order';
+import OrderDetailDialog from './detial.vue';
 
 const QRCodeOpts = {
   errorCorrectionLevel: 'H', //容错级别
@@ -963,7 +965,9 @@ const QRCodeOpts = {
 
 export default {
   name: "OrderManage",
-  components: {},
+  components: {
+    OrderDetailDialog,
+  },
   data() {
     const access_token = sessionStorage.getItem('access_token') || '';
     const token_type = sessionStorage.getItem('token_type') || '';
@@ -1217,6 +1221,10 @@ export default {
       this.forms.page = val;
       this.getOrderList();
     },
+    view(row) {
+      if (!row.id) return;
+      this.$refs.orderDetailDialog.open(row.id);
+    },
     async edit(row) {
       this.isAdd = false;
       this.searchProductList();
@@ -1427,21 +1435,6 @@ export default {
         .catch(() => {
         });
     },
-    getOrderStatusType(status) {
-      const types = {
-        'Draft': 'info',
-        'Submitted': 'warning',
-        'Approved': 'success',
-        'Paid': 'success',
-        'Confirmed': 'success',
-        'Processing': 'warning',
-        'Completed': 'success',
-        'Cancelled': 'danger',
-        'Cancelling': 'warning',
-        'Rejected': 'danger'
-      }
-      return types[status] || 'info'
-    },
     addProductDetail() {
       if (!this.currUseDate) {
         this.$message.error('请先选择使用日期');
@@ -1603,41 +1596,10 @@ export default {
         }
       });
     },
-    getOrderStatusText(status) {
-      const texts = {
-        'Draft': '草稿',
-        'Submitted': '已提交',
-        'Approved': '已审核',
-        'Paid': '已支付',
-        'Confirmed': '已确认',
-        'Processing': '处理中',
-        'Completed': '已完成',
-        'Cancelled': '已取消',
-        'Cancelling': '取消中',
-        'Rejected': '已驳回'
-      }
-      return texts[status] || status
-    },
-    getPaymentStatusType(status) {
-      const types = {
-        'Unpaid': 'danger',
-        'Partial': 'warning',
-        'Paid': 'success',
-        'Refunding': 'info',
-        'Refunded': 'info'
-      }
-      return types[status] || 'info'
-    },
-    getPaymentStatusText(status) {
-      const texts = {
-        'Unpaid': '未支付',
-        'Partial': '部分支付',
-        'Paid': '已支付',
-        'Refunding': '退款中',
-        'Refunded': '已退款'
-      }
-      return texts[status] || status
-    },
+    getOrderStatusType,
+    getOrderStatusText,
+    getPaymentStatusType,
+    getPaymentStatusText,
     orderCanPay(row) {
       return row.paymentStatus !== 'Paid';
     },

+ 507 - 0
src/views/order/refund.vue

@@ -0,0 +1,507 @@
+<template>
+  <el-card class="main-card">
+    <template #header>
+      <el-breadcrumb separator-class="el-icon-arrow-right">
+        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
+        <el-breadcrumb-item>订单退款</el-breadcrumb-item>
+      </el-breadcrumb>
+    </template>
+    <el-form :model="forms" ref="forms" label-width="70px" class="search-form">
+      <el-row style="align-items: center">
+        <el-form-item label="关键词" prop="keyword">
+          <el-col>
+            <el-input v-model="forms.keyword" prefix-icon="Search"></el-input>
+          </el-col>
+        </el-form-item>
+        <el-form-item label="退款状态" prop="refundStatus" style="margin-left: 10px;">
+          <el-select v-model="forms.refundStatus" :empty-values="[null, undefined]" :value-on-clear="null" clearable
+            style="width: 100px">
+            <el-option label="全部" value=""></el-option>
+            <el-option label="待审核" value="Pending"></el-option>
+            <el-option label="已通过" value="Approved"></el-option>
+            <el-option label="已驳回" value="Rejected"></el-option>
+            <el-option label="退款中" value="Refunding"></el-option>
+            <el-option label="已退款" value="Refunded"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="" label-width="20px">
+          <el-button type="primary" icon="search" @click="submit">
+            搜索
+          </el-button>
+          <el-button icon="refresh" @click="reset">
+            重置
+          </el-button>
+        </el-form-item>
+      </el-row>
+    </el-form>
+
+    <el-table class="table" ref="table" :data="tableData" row-key="id" border size="small">
+      <el-table-column type="index" label="序号" align="center" width="60"></el-table-column>
+      <el-table-column prop="refundNo" label="退款单号" min-width="180" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="orderID" label="订单ID" min-width="220" show-overflow-tooltip>
+        <template #default="scope">
+          <el-link type="primary" @click="viewOrderDetail(scope.row.orderID)">{{ scope.row.orderID }}</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column prop="refundAmount" label="退款金额" width="120" align="right">
+        <template #default="scope">
+          ¥{{ scope.row.refundAmount?.toFixed(2) || '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="reason" label="退款原因" min-width="150" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="auditStatus" label="审核状态" width="100" align="center">
+        <template #default="scope">
+          <el-tag :type="getRefundStatusType(scope.row.auditStatus)">
+            {{ getRefundStatusText(scope.row.auditStatus) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="status" label="退款状态" width="100" align="center">
+        <template #default="scope">
+          <el-tag :type="getRefundStatusType(scope.row.status)">
+            {{ getRefundStatusText(scope.row.status) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="refundMethod" label="退款方式" width="100" align="center">
+        <template #default="scope">
+          {{ scope.row.refundMethod === 'Online' ? '在线退款' : scope.row.refundMethod }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="refundUserName" label="申请人" width="120"></el-table-column>
+      <el-table-column prop="refundDate" label="申请时间" width="160">
+        <template #default="scope">
+          {{ scope.row.refundDate ? moment(scope.row.refundDate).format('YYYY-MM-DD HH:mm') : '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="auditUserName" label="审核人" width="120"></el-table-column>
+      <el-table-column prop="auditDate" label="审核时间" width="160">
+        <template #default="scope">
+          {{ scope.row.auditDate ? moment(scope.row.auditDate).format('YYYY-MM-DD HH:mm') : '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="remark" label="备注" min-width="200" show-overflow-tooltip></el-table-column>
+
+      <el-table-column fixed="right" label="操作" width="120">
+        <template #default="scope">
+          <el-button v-if="scope.row.auditStatus === 'Approved'" type="primary" link size="small"
+            @click="refund(scope.row)">退费</el-button>
+          <el-button v-else type="primary" link size="small" @click="reviewRefund(scope.row)">审核</el-button>
+          <el-button type="info" link size="small" @click="paymentRecord(scope.row)">支付记录</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-pagination class="pagination" background layout="total, sizes, prev, pager, next"
+      :page-sizes="[10, 20, 50, 100]" :page-size="forms.rows" :total="total" @size-change="sizeHandler"
+      @current-change="pageHandler">
+    </el-pagination>
+
+    <!-- 退款详情对话框 -->
+    <el-dialog title="退款详情" width="800px" v-model="detailDialogVisible" destroy-on-close>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="订单ID">{{ detailData.orderID }}</el-descriptions-item>
+        <el-descriptions-item label="旅行社">{{ detailData.agencyName }}</el-descriptions-item>
+        <el-descriptions-item label="订单总额">¥{{ detailData.totalAmount }}</el-descriptions-item>
+        <el-descriptions-item label="已支付">¥{{ detailData.paidAmount }}</el-descriptions-item>
+        <el-descriptions-item label="申请退款">¥{{ detailData.refundAmount }}</el-descriptions-item>
+        <el-descriptions-item label="实退金额">¥{{ detailData.actualRefundAmount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="退款状态">
+          <el-tag :type="getRefundStatusType(detailData.refundStatus)">
+            {{ getRefundStatusText(detailData.refundStatus) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="申请时间">
+          {{ detailData.applyDate ? moment(detailData.applyDate).format('YYYY-MM-DD HH:mm:ss') : '-' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="退款原因" :span="2">{{ detailData.refundReason || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="审核人" v-if="detailData.auditUserName">
+          {{ detailData.auditUserName }}
+        </el-descriptions-item>
+        <el-descriptions-item label="审核时间" v-if="detailData.auditDate">
+          {{ moment(detailData.auditDate).format('YYYY-MM-DD HH:mm:ss') }}
+        </el-descriptions-item>
+        <el-descriptions-item label="审核备注" :span="2" v-if="detailData.auditRemark">
+          {{ detailData.auditRemark }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </el-dialog>
+
+    <!-- 退款审核对话框 -->
+    <el-dialog title="退款审核" width="600px" v-model="auditDialogVisible" destroy-on-close @close="closeAudit">
+      <el-form :model="auditForm" :rules="auditRules" ref="auditFormRef" label-width="100px">
+        <el-form-item label="订单ID">
+          <span>{{ auditForm.orderID }}</span>
+        </el-form-item>
+        <el-form-item label="退款金额">
+          <span>¥{{ auditForm.refundAmount }}</span>
+        </el-form-item>
+        <el-form-item label="退款原因">
+          <span>{{ auditForm.refundReason }}</span>
+        </el-form-item>
+        <el-form-item label="审核结果" prop="isApproved">
+          <el-radio-group v-model="auditForm.isApproved">
+            <el-radio :label="true">通过</el-radio>
+            <el-radio :label="false">驳回</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="审核备注" prop="remark">
+          <el-input v-model="auditForm.remark" type="textarea" :rows="3" placeholder="请输入审核备注">
+          </el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="auditDialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitAudit">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 退款操作对话框 -->
+    <el-dialog title="退款确认" width="500px" v-model="refundDialogVisible" destroy-on-close @close="closeRefund">
+      <el-alert title="请确认退款信息" type="warning" :closable="false" show-icon style="margin-bottom: 20px">
+      </el-alert>
+      <el-descriptions :column="1" border>
+        <el-descriptions-item label="订单ID">{{ refundForm.orderID }}</el-descriptions-item>
+        <el-descriptions-item label="实退金额">¥{{ refundForm.actualRefundAmount }}</el-descriptions-item>
+        <el-descriptions-item label="退款方式">
+          <el-select v-model="refundForm.refundMethod" style="width: 100%">
+            <el-option label="原路退回" value="Original"></el-option>
+            <el-option label="银行转账" value="BankTransfer"></el-option>
+            <el-option label="现金退款" value="Cash"></el-option>
+          </el-select>
+        </el-descriptions-item>
+      </el-descriptions>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="refundDialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitRefund">确认退款</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 订单支付记录对话框 -->
+    <el-dialog title="订单支付记录" width="900px" v-model="paymentRecordDialogVisible" destroy-on-close>
+      <el-table :data="paymentRecordData" border size="small">
+        <el-table-column type="index" label="序号" align="center" width="60"></el-table-column>
+        <el-table-column prop="paymentNo" label="支付单号" min-width="180" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="paymentAmount" label="支付金额" width="120" align="right">
+          <template #default="scope">
+            ¥{{ scope.row.paymentAmount?.toFixed(2) || '0.00' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="paymentMethod" label="支付方式" width="120" align="center">
+          <template #default="scope">
+            {{ scope.row.paymentMethod === 'Online' ? '在线支付' : scope.row.paymentMethod }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="支付状态" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="scope.row.status === 'Pending' ? 'warning' : 'success'">
+              {{ scope.row.status === 'Pending' ? '待支付' : scope.row.status === 'Success' ? '已支付' : scope.row.status }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="paymentDate" label="支付时间" width="160">
+          <template #default="scope">
+            {{ scope.row.paymentDate ? moment(scope.row.paymentDate).format('YYYY-MM-DD HH:mm') : '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="createDate" label="创建时间" width="160">
+          <template #default="scope">
+            {{ scope.row.createDate ? moment(scope.row.createDate).format('YYYY-MM-DD HH:mm') : '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="remark" label="备注" min-width="200" show-overflow-tooltip></el-table-column>
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="paymentRecordDialogVisible = false">关 闭</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <order-detail-dialog ref="orderDetailDialog" :orderId="currentOrderId"></order-detail-dialog>
+  </el-card>
+</template>
+
+<script>
+import {
+  getOrderRefundList,
+  getOrderRefundFields,
+  getOrderRefundInfo,
+  reviewRefundOrder,
+  processRefund,
+  wxRefundOrder,
+  getPaymentInfoByOrderId,
+} from '@/api/order';
+import moment from 'moment';
+import { getOrderStatusText, getOrderStatusType, getPaymentStatusText, getPaymentStatusType } from '@/utils/order';
+import OrderDetailDialog from './detial.vue';
+
+export default {
+  name: "RefundManage",
+  components: { OrderDetailDialog },
+  data() {
+    return {
+      forms: {
+        keyword: "",
+        refundStatus: "",
+        columns: [
+          { label: "订单ID", prop: "orderID" },
+          { label: "旅行社名称", prop: "agencyName" },
+          { label: "订单总额", prop: "totalAmount" },
+          { label: "已支付金额", prop: "paidAmount" },
+          { label: "申请退款", prop: "refundAmount" },
+          { label: "实退金额", prop: "actualRefundAmount" },
+          { label: "退款原因", prop: "refundReason" },
+          { label: "退款状态", prop: "refundStatus" },
+          { label: "申请时间", prop: "applyDate" },
+        ],
+        sorters: [],
+        filters: [],
+        page: 1,
+        rows: 10,
+      },
+      tableData: [],
+      total: 0,
+
+      // 详情对话框
+      detailDialogVisible: false,
+      detailData: {},
+
+      // 审核对话框
+      auditDialogVisible: false,
+      auditForm: {
+        id: '',
+        orderID: '',
+        refundAmount: 0,
+        refundReason: '',
+        isApproved: true,
+        actualRefundAmount: 0,
+        remark: '',
+      },
+      auditRules: {
+        isApproved: [
+          { required: true, message: "请选择审核结果", trigger: "change" }
+        ],
+      },
+
+      // 退款对话框
+      refundDialogVisible: false,
+      refundForm: {
+        id: '',
+        orderID: '',
+        actualRefundAmount: 0,
+        refundMethod: 'Original',
+      },
+      paymentRecordDialogVisible: false,
+      paymentRecordData: [],
+    };
+  },
+  methods: {
+    moment,
+
+    sizeHandler(val) {
+      this.forms.rows = val;
+      this.getRefundList();
+    },
+
+    pageHandler(val) {
+      this.forms.page = val;
+      this.getRefundList();
+    },
+
+    submit() {
+      this.forms.page = 1;
+      this.buildFilters();
+      this.getRefundList();
+    },
+
+    reset() {
+      this.forms.keyword = '';
+      this.forms.refundStatus = '';
+      this.forms.page = 1;
+      this.buildFilters();
+      this.getRefundList();
+    },
+
+    buildFilters() {
+      this.forms.filters = [];
+
+      if (this.forms.refundStatus) {
+        this.forms.filters.push({
+          name: "refundStatus",
+          operate: 0,
+          value: this.forms.refundStatus,
+        });
+      }
+    },
+
+    getRefundList() {
+      getOrderRefundList(this.forms || {}).then((res) => {
+        this.tableData = res.data?.data || [];
+        this.total = res.data?.records || 0;
+      });
+    },
+
+    viewDetail(row) {
+      getOrderRefundInfo(row.orderId).then((res) => {
+        this.detailData = res.data || {};
+        this.detailDialogVisible = true;
+      });
+    },
+
+    closeAudit() {
+      this.$refs.auditFormRef?.resetFields();
+      this.auditForm = {
+        id: '',
+        refundAmount: 0,
+        refundReason: '',
+        isApproved: true,
+        actualRefundAmount: 0,
+        remark: '',
+      };
+    },
+    reviewRefund(row) {
+      this.auditForm = {
+        id: row.id,
+        orderID: row.orderID,
+        refundAmount: row.refundAmount,
+        refundReason: row.auditRemark,
+        isApproved: true,
+        actualRefundAmount: row.refundAmount,
+        remark: '',
+      };
+      this.auditDialogVisible = true;
+    },
+
+    submitAudit() {
+      this.$refs.auditFormRef.validate((valid) => {
+        if (!valid) return;
+
+        const { id, isApproved, remark } = this.auditForm;
+
+        reviewRefundOrder(id, isApproved, remark).then((res) => {
+          if (res.code === 200) {
+            this.$message.success('审核成功');
+            this.auditDialogVisible = false;
+            this.getRefundList();
+          }
+        });
+      });
+    },
+
+    refund(row) {
+      this.refundForm = {
+        id: row.id,
+        orderID: row.orderID,
+        actualRefundAmount: row.actualRefundAmount,
+        refundMethod: 'Original',
+      };
+      this.refundDialogVisible = true;
+    },
+
+    closeRefund() {
+      this.refundForm = {
+        id: '',
+        orderID: '',
+        actualRefundAmount: 0,
+        refundMethod: 'Original',
+      };
+    },
+
+    submitRefund() {
+      const { id, refundMethod } = this.refundForm;
+
+      this.$confirm('确认执行退款操作?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        processRefund(id, refundMethod).then((res) => {
+          if (res.code === 200) {
+            this.$message.success('退款成功');
+            this.refundDialogVisible = false;
+            this.getRefundList();
+          }
+        });
+      });
+    },
+    refund(row) {
+      this.$confirm('确认执行退款操作?线上退款可能失败,需要再次确认', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        wxRefundOrder(row.id).then((res) => {
+          if (res.code === 200) {
+            this.$message.success('操作退款成功,请注意查看退款状态');
+            this.getRefundList();
+          }
+        });
+      }).catch(() => {
+        // 用户取消操作
+      });
+    },
+    paymentRecord(row) {
+      getPaymentInfoByOrderId(row.orderID).then((res) => {
+        this.paymentRecordData = res.data || [];
+        this.paymentRecordDialogVisible = true;
+      });
+    },
+
+    getOrderStatusType,
+    getOrderStatusText,
+    getPaymentStatusType,
+    getPaymentStatusText,
+
+    getRefundStatusType(status) {
+      const types = {
+        'Pending': 'warning',
+        'Approved': 'success',
+        'Rejected': 'danger',
+        'Refunding': 'info',
+        'Refunded': 'success',
+      };
+      return types[status] || 'info';
+    },
+
+    getRefundStatusText(status) {
+      const texts = {
+        'Pending': '待审核',
+        'Approved': '已通过',
+        'Rejected': '已驳回',
+        'Refunding': '退款中',
+        'Refunded': '已退款',
+      };
+      return texts[status] || status;
+    },
+    viewOrderDetail(orderID) {
+      this.$refs.orderDetailDialog.open(orderID);
+    },
+  },
+
+  async created() {
+    const res = await getOrderRefundFields();
+    this.forms.columns = res.data?.columns || []
+    this.getRefundList();
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.search-form {
+  .el-form-item {
+    margin-bottom: 10px;
+  }
+}
+
+.main-card {
+  margin: 10px;
+}
+
+.pagination {
+  margin-top: 20px;
+  text-align: right;
+}
+</style>

+ 19 - 54
src/views/order/review.vue

@@ -82,7 +82,10 @@
       <el-table-column fixed="right" label="操作" width="320">
         <template #default="scope">
           <!-- <el-button type="primary" link size="small" @click="edit(scope.row)">编辑</el-button> -->
-          <el-button type="success" link size="small" @click="audit(scope.row)">审核</el-button>
+          <el-button type="success" link size="small" @click="audit(scope.row)">
+            {{ scope.row.status === 'Cancelling' ? '退费' : '订单' }}审核
+          </el-button>
+          <el-button type="info" link size="small" @click="view(scope.row)">详情</el-button>
           <!-- <el-button type="warning" link size="small" @click="payment(scope.row)">收款</el-button>
           <el-button type="info" link size="small" @click="editSchedule(scope.row)">行程</el-button>
           <el-button type="primary" link size="small" @click="editTourists(scope.row)">游客</el-button>
@@ -114,8 +117,8 @@
         </span>
       </template>
     </el-dialog>
+    <order-detail-dialog ref="orderDetailDialog" :orderId="currentOrderId"></order-detail-dialog>
   </el-card>
-
 </template>
 <script>
 import request from '@/utils/request'
@@ -124,10 +127,14 @@ import { cloneDeep, groupBy } from 'lodash-es';
 import moment from 'moment/moment';
 import { ProductTypes } from '@/constant';
 import utils from '@/utils/util';
+import { getOrderStatusText, getOrderStatusType, getPaymentStatusText, getPaymentStatusType } from '@/utils/order';
+import OrderDetailDialog from './detial.vue';
 
 export default {
-  name: "OrderManage",
-  components: {},
+  name: "OrderReview",
+  components: {
+    OrderDetailDialog
+  },
   data() {
     return {
       forms: {
@@ -213,6 +220,10 @@ export default {
       this.formData.status = row.status;
       this.formDialogVisible = true;
     },
+    view(row) {
+      if (!row.id) return;
+      this.$refs.orderDetailDialog.open(row.id);
+    },
     close() {
       this.$refs.formRef.resetFields();
       this.formData = {
@@ -296,56 +307,10 @@ export default {
         .catch(() => {
         });
     },
-    getOrderStatusType(status) {
-      const types = {
-        'Draft': 'info',
-        'Submitted': 'warning',
-        'Approved': 'success',
-        'Paid': 'success',
-        'Confirmed': 'success',
-        'Processing': 'warning',
-        'Completed': 'success',
-        'Cancelled': 'danger',
-        'Cancelling': 'warning',
-        'Rejected': 'danger'
-      }
-      return types[status] || 'info'
-    },
-    getOrderStatusText(status) {
-      const texts = {
-        'Draft': '草稿',
-        'Submitted': '已提交',
-        'Approved': '已审核',
-        'Paid': '已支付',
-        'Confirmed': '已确认',
-        'Processing': '处理中',
-        'Completed': '已完成',
-        'Cancelled': '已取消',
-        'Cancelling': '取消中',
-        'Rejected': '已驳回'
-      }
-      return texts[status] || status
-    },
-    getPaymentStatusType(status) {
-      const types = {
-        'Unpaid': 'danger',
-        'Partial': 'warning',
-        'Paid': 'success',
-        'Refunding': 'info',
-        'Refunded': 'info'
-      }
-      return types[status] || 'info'
-    },
-    getPaymentStatusText(status) {
-      const texts = {
-        'Unpaid': '未支付',
-        'Partial': '部分支付',
-        'Paid': '已支付',
-        'Refunding': '退款中',
-        'Refunded': '已退款'
-      }
-      return texts[status] || status
-    },
+    getOrderStatusType,
+    getOrderStatusText,
+    getPaymentStatusType,
+    getPaymentStatusText,
   },
   created() {
     this.getOrderList();