hurx il y a 1 mois
Parent
commit
b2fba42264

+ 1 - 1
src/api/pc/enterprise/address.ts

@@ -5,7 +5,7 @@ import request from '@/utils/request';
  */
 export function getAddressInfo(id: number | string) {
   return request({
-    url: `/pc/enterprise/address/${id}`,
+    url: `/customer/address/${id}`,
     method: 'get'
   });
 }

+ 33 - 4
src/api/pc/enterprise/order.ts

@@ -45,6 +45,17 @@ export function getOrderProducts(orderIds: number[]) {
   });
 }
 
+/**
+ * 根据订单ID查询订单商品明细
+ */
+export function getOrderProductsWithAvailableQty(orderIds: number[]) {
+  return request({
+    url: '/order/pcOrder/productsWithAvailableQty',
+    method: 'get',
+    params: { orderIds: orderIds.join(',') }
+  });
+}
+
 /**
  * 取消订单
  */
@@ -60,10 +71,18 @@ export function cancelOrder(data: { id: number; orderStatus?: string }) {
  * 审核订单
  * 企业客户审核自己企业的订单
  */
-export function checkOrderStatus(data: { id: number; checkStatus: string }) {
+// export function checkOrderStatus(data: { id: number; checkStatus: string }) {
+//   return request({
+//     url: '/order/pcOrder/checkStatus',
+//     method: 'put',
+//     data: data
+//   });
+// }
+
+export function checkOrderStatus(data: any) {
   return request({
-    url: '/order/pcOrder/checkStatus',
-    method: 'put',
+    url: '/order/pcOrder/checkOrder',
+    method: 'post',
     data: data
   });
 }
@@ -100,7 +119,7 @@ export function batchConfirmation(orderIds: number[]) {
   });
 }
 
-// ==================== 订单评价 ====================
+// ==================== 订单管理 ====================
 
 /**
  * 查询当前企业的已评价订单
@@ -209,3 +228,13 @@ export function closeOrderFlow(id: number) {
     method: 'put'
   });
 }
+
+//=============== 订单退货原因 ===============
+
+export function getReturnReason(params?: any) {
+  return request({
+    url: '/system/pcOrderReturnReason/list',
+    method: 'get',
+    params: params
+  });
+}

+ 0 - 1
src/views/enterprise/companyInfo/index.vue

@@ -169,7 +169,6 @@ const companyData = reactive({
 const loadEnterpriseInfo = async () => {
   try {
     const res = await getEnterpriseInfo();
-    console.log('企业信息完整数据:', res.data); // 调试用,查看完整数据结构
     if (res.code === 200 && res.data) {
       const data = res.data;
       const salesInfo = data.customerSalesInfoVo || {};

+ 171 - 2
src/views/order/afterSale/index.vue

@@ -179,6 +179,48 @@
         <el-button type="danger" @click="handleSubmitComplaint">确定</el-button>
       </template>
     </el-dialog>
+
+    <el-dialog v-model="returnDialogVisible" title="查看详情" width="900px" class="return-detail-dialog">
+      <div v-loading="returnLoading" class="return-detail-body">
+        <div v-for="item in returnItemList" :key="item.id" class="return-product-row">
+          <div class="product-left">
+            <div class="product-image">
+              <el-image :src="item.productImage" fit="contain">
+                <template #error>
+                  <div class="image-placeholder">
+                    <el-icon :size="30" color="#ccc"><Picture /></el-icon>
+                  </div>
+                </template>
+              </el-image>
+            </div>
+
+            <div class="product-info">
+              <div class="product-name">{{ item.productName || '-' }}</div>
+              <div class="product-meta">商品编号:{{ item.productNo || '-' }}</div>
+              <div class="product-meta">单位:{{ item.productUnit || '-' }}</div>
+            </div>
+          </div>
+
+          <div class="product-right">
+            <div class="right-line">
+              <span class="k">退货数量:</span><span class="v">{{ item.returnQuantity || 0 }}</span>
+            </div>
+            <div class="right-line">
+              <span class="k">单价:</span><span class="v">¥{{ formatMoney(getItemPrice(item)) }}</span>
+            </div>
+            <div class="right-line">
+              <span class="k">小计:</span><span class="v">¥{{ formatMoney(getItemSubtotal(item)) }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <template #footer>
+        <div>
+          <el-button @click="returnDialogVisible = false">取消</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -210,6 +252,10 @@ const complaintFormRef = ref();
 const currentComplaint = ref<any>(null);
 const loading = ref(false);
 const pagination = reactive({ page: 1, pageSize: 5, total: 0 });
+const returnDialogVisible = ref(false);
+const returnLoading = ref(false);
+const returnItemList = ref<any[]>([]);
+
 const mainTabs = [
   { key: 'return', label: '退换货', icon: RefreshRight },
   { key: 'repair', label: '返修/维修', icon: Tools },
@@ -332,8 +378,33 @@ const getStatusClass = (status: string) => {
   const map: Record<string, string> = { 0: 'warning', 1: 'success', 2: 'danger', 3: 'success', 4: 'info' };
   return map[status] || 'info';
 };
-const handleViewDetail = (item: any) => {
-  console.log('查看详情', item);
+const handleViewDetail = async (item: any) => {
+  returnDialogVisible.value = true;
+  await getOrderReturnInfo(item.id).then((res) => {
+    returnItemList.value = res.data.orderReturnItemList;
+  });
+};
+
+const formatMoney = (val: number | string) => {
+  const num = Number(val) || 0;
+  return num.toFixed(2);
+};
+
+const getItemPrice = (item: any) => {
+  if (item?.returnPrice != null && item.returnPrice !== '') return Number(item.returnPrice) || 0;
+  if (item?.price != null && item.price !== '') return Number(item.price) || 0;
+  if (item?.orderPrice != null && item.orderPrice !== '') return Number(item.orderPrice) || 0;
+
+  const qty = Number(item?.returnQuantity) || 0;
+  const total = Number(item?.totalAmount) || 0;
+  if (!qty) return 0;
+  return total / qty;
+};
+
+const getItemSubtotal = (item: any) => {
+  if (item?.totalAmount != null && item.totalAmount !== '') return Number(item.totalAmount) || 0;
+  const qty = Number(item?.returnQuantity) || 0;
+  return getItemPrice(item) * qty;
 };
 const handleAddComplaint = () => {
   currentComplaint.value = null;
@@ -432,6 +503,104 @@ const loadComplaintData = async () => {
     }
   }
 }
+
+.return-detail-dialog {
+  .return-detail-body {
+    border: 1px solid #eee;
+    border-radius: 4px;
+    overflow: hidden;
+    background: #fff;
+  }
+
+  .return-product-row {
+    display: flex;
+    justify-content: space-between;
+    gap: 20px;
+    padding: 16px;
+    border-bottom: 1px solid #f0f0f0;
+    background: #fff;
+
+    &:nth-child(odd) {
+      background: #f7fafc;
+    }
+
+    &:last-child {
+      border-bottom: none;
+    }
+  }
+
+  .product-left {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    gap: 14px;
+    min-width: 0;
+  }
+
+  .product-image {
+    width: 60px;
+    height: 60px;
+    background: #f5f5f5;
+    border-radius: 4px;
+    overflow: hidden;
+    flex-shrink: 0;
+
+    .el-image {
+      width: 100%;
+      height: 100%;
+    }
+
+    .image-placeholder {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+
+  .product-info {
+    min-width: 0;
+  }
+
+  .product-name {
+    font-size: 14px;
+    color: #333;
+    line-height: 1.4;
+    margin-bottom: 6px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .product-meta {
+    font-size: 12px;
+    color: #999;
+    line-height: 1.6;
+  }
+
+  .product-right {
+    width: 160px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    gap: 6px;
+    color: #333;
+    font-size: 12px;
+  }
+
+  .right-line {
+    display: flex;
+    justify-content: flex-start;
+    gap: 6px;
+    .k {
+      color: #666;
+    }
+    .v {
+      color: #333;
+    }
+  }
+}
 .status-tabs {
   display: flex;
   gap: 10px;

+ 15 - 8
src/views/order/orderAudit/index.vue

@@ -101,14 +101,14 @@
             </div>
             <div class="product-cell status-cell">
               <span :class="['status-text', getStatusClass(order.checkStatus)]">{{ getStatusText(order.checkStatus) }}</span>
-              <el-button type="primary" link size="small">查看订单轨迹</el-button>
+              <!-- <el-button type="primary" link size="small">查看订单轨迹</el-button> -->
               <template v-if="order.checkStatus !== '0' && activeMainTab === 'myAudit'">
-                <el-button type="primary" link size="small">查看审批流</el-button>
+                <!-- <el-button type="primary" link size="small">查看审批流</el-button> -->
               </template>
               <template v-if="activeMainTab === 'myApply'">
                 <span v-if="order.checkStatus === '1'" class="result-text success">审批通过</span>
                 <span v-else-if="order.checkStatus === '2'" class="result-text danger">审批驳回</span>
-                <el-button type="primary" link size="small">查看审批流</el-button>
+                <!-- <el-button type="primary" link size="small">查看审批流</el-button> -->
               </template>
               <el-button v-if="order.fileCount" type="primary" link size="small"> 审核文件({{ order.fileCount }}) </el-button>
             </div>
@@ -215,6 +215,12 @@ const queryParams = reactive({
   filter1: ''
 });
 
+const checkOrderData = reactive({
+  orderId: undefined,
+  checkStatus: '',
+  checkRemark: ''
+});
+
 const auditForm = reactive({ opinion: '' });
 const auditRules = { opinion: [{ required: true, message: '请输入审批意见', trigger: 'blur' }] };
 
@@ -361,11 +367,12 @@ const getStatusClass = (checkStatus: string) => {
 };
 
 const handleViewDetail = (order: any) => {
-  router.push(`/order/orderManage/detail/${order.id}`);
+  router.push(`/order/orderManage/detail?orderId=${order.id}`);
 };
 
 const handleApprove = (order: any) => {
   currentAuditOrder.value = order;
+  checkOrderData.orderId = order.id;
   currentAuditAction.value = 'approve';
   auditDialogTitle.value = '审批通过';
   auditForm.opinion = '';
@@ -374,6 +381,7 @@ const handleApprove = (order: any) => {
 
 const handleReject = (order: any) => {
   currentAuditOrder.value = order;
+  checkOrderData.orderId = order.id;
   currentAuditAction.value = 'reject';
   auditDialogTitle.value = '审批拒绝';
   auditForm.opinion = '';
@@ -399,10 +407,9 @@ const handleSubmitAudit = async () => {
 
   try {
     const checkStatus = currentAuditAction.value === 'approve' ? '1' : '2';
-    await checkOrderStatus({
-      id: currentAuditOrder.value.id,
-      checkStatus: checkStatus
-    });
+    checkOrderData.checkStatus = checkStatus;
+    checkOrderData.checkRemark = auditForm.opinion;
+    await checkOrderStatus(checkOrderData);
 
     ElMessage.success(currentAuditAction.value === 'approve' ? '审批通过' : '已拒绝');
     auditDialogVisible.value = false;

+ 824 - 0
src/views/order/orderManage/applyAfter.vue

@@ -0,0 +1,824 @@
+<template>
+  <div class="apply-after-container">
+    <div class="page-header">
+      <div class="page-inner header-inner">
+        <div class="header-left">
+          <div class="header-icon">
+            <el-icon><Clock /></el-icon>
+          </div>
+          <div class="header-title">申请售后</div>
+        </div>
+
+        <div class="header-center">
+          <div class="header-item">
+            <span class="label">订单编号</span>
+            <span class="value">{{ orderInfo.orderNo }}</span>
+          </div>
+          <div class="header-item">
+            <span class="label">下单时间</span>
+            <span class="value">{{ orderInfo.orderTime }}</span>
+          </div>
+        </div>
+
+        <div class="header-right">
+          <el-button @click="handleViewEvaluation">查看评价</el-button>
+          <el-button @click="handleBuyAgain">再次购买</el-button>
+        </div>
+      </div>
+    </div>
+
+    <div class="content">
+      <div class="page-inner">
+        <div class="panel">
+          <div class="panel-title">选择服务类型:</div>
+          <div class="service-type">
+            <el-radio-group v-model="form.serviceType" size="large">
+              <el-radio-button label="1">退换货</el-radio-button>
+              <el-radio-button label="3">维修</el-radio-button>
+            </el-radio-group>
+            <div class="service-tip">如商品出现质量问题,退货价格按照销售价格退货!</div>
+          </div>
+
+          <div class="section-title">提交数量</div>
+
+          <div class="summary-bar" v-if="selectedProducts.length > 0">
+            <div class="summary-left">
+              <span>商品总数:{{ summary.totalKinds }}个</span>
+              <span>本单提交商品总数量:{{ summary.totalQty }}个</span>
+              <span>退货总金额:¥ {{ summary.totalAmount }}</span>
+            </div>
+            <el-icon class="summary-close" @click="handleClearProducts"><Close /></el-icon>
+          </div>
+
+          <div class="product-table">
+            <div class="table-header">
+              <div class="col col-product">商品信息</div>
+              <div class="col col-price">单价</div>
+              <div class="col col-sold">销售数量</div>
+              <div class="col col-available">未售后数量</div>
+              <div class="col col-qty">退货数量</div>
+              <div class="col col-amount">退货金额</div>
+              <div class="col col-op">操作</div>
+            </div>
+
+            <div v-if="selectedProducts.length === 0" class="table-empty">
+              <el-empty description="暂无商品" />
+            </div>
+
+            <div v-for="item in selectedProducts" :key="item.productId" class="table-row">
+              <div class="col col-product">
+                <div class="product-info">
+                  <div class="product-image">
+                    <el-image :src="item.productImage" fit="cover">
+                      <template #error>
+                        <div class="image-placeholder"></div>
+                      </template>
+                    </el-image>
+                  </div>
+                  <div class="product-meta">
+                    <div class="name">{{ item.name }}</div>
+                    <div class="spec">{{ item.spec }}</div>
+                  </div>
+                </div>
+              </div>
+              <div class="col col-price">¥{{ formatMoney(item.unitPrice) }}</div>
+              <div class="col col-sold">{{ item.soldQty }}</div>
+              <div class="col col-available">{{ item.availableQty }}</div>
+              <div class="col col-qty">
+                <el-input-number
+                  v-model="item.returnQuantity"
+                  :min="0"
+                  :max="item.availableQty"
+                  controls-position="right"
+                  size="small"
+                  :controls="false"
+                  @change="handleQtyChange"
+                />
+              </div>
+              <div class="col col-amount">¥{{ formatMoney(item.returnQuantity * item.unitPrice) }}</div>
+              <div class="col col-op">
+                <el-button type="danger" link @click="handleRemoveProduct(item.productId)">删除</el-button>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="panel">
+          <div class="panel-title">售后服务单信息:</div>
+          <el-form ref="formRef" :model="form" :rules="rules" label-width="80px" class="after-form">
+            <el-form-item label="退货原因" prop="returnReasonId">
+              <el-select v-model="form.returnReasonId" placeholder="请选择" style="width: 200px" @change="handleReturnReasonChange">
+                <el-option v-for="r in reasonOptions" :key="r.id" :label="r.returnReasonName" :value="r.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="问题描述" prop="problemDescription">
+              <el-input v-model="form.problemDescription" type="textarea" :rows="5" placeholder="请输入问题描述" />
+            </el-form-item>
+            <el-form-item label="凭证图片">
+              <el-upload v-model:file-list="form.voucherPhotoArray" list-type="picture-card" action="#" :auto-upload="false" :limit="5">
+                <el-icon><Plus /></el-icon>
+              </el-upload>
+            </el-form-item>
+          </el-form>
+        </div>
+
+        <div class="panel">
+          <div class="panel-title">选择取件方式:</div>
+          <el-radio-group v-model="form.returnMethod" class="pickup-type">
+            <el-radio label="1">上门取件</el-radio>
+            <el-radio label="2">快递配送</el-radio>
+          </el-radio-group>
+
+          <div class="pickup-info" v-if="form.returnMethod === '1'">
+            <div class="pickup-row">
+              <span class="k">顾客姓名:</span><span class="v">{{ form.chargebackName }}</span>
+            </div>
+            <div class="pickup-row">
+              <span class="k">手机号:</span><span class="v">{{ form.chargebackPhone }}</span>
+            </div>
+            <div class="pickup-row">
+              <span class="k">取件时间:</span>
+              <el-date-picker v-model="form.chargebackPickupTime" type="datetime" placeholder="请选择" style="width: 240px" />
+            </div>
+            <div class="pickup-row">
+              <span class="k">取件地址:</span><span class="v">{{ form.chargebackAddress }}</span>
+            </div>
+            <div class="pickup-row hint">提交服务单后,售后专员可能与您电话沟通,请保持手机畅通</div>
+            <div class="pickup-row">
+              <el-button plain @click="handleChangeAddress">切换地址</el-button>
+            </div>
+          </div>
+
+          <div class="pickup-info" v-else>
+            <div class="pickup-row hint">请在提交后按提示将商品寄回,售后专员将与您确认寄送信息。</div>
+          </div>
+        </div>
+
+        <div class="footer">
+          <el-button type="danger" class="submit-btn" @click="handleSubmit">提交</el-button>
+        </div>
+      </div>
+    </div>
+
+    <el-dialog v-model="addressDialogVisible" title="切换地址" width="900px" class="address-dialog">
+      <div v-loading="addressLoading" class="address-list">
+        <div
+          v-for="addr in addressList"
+          :key="addr.id"
+          :class="['address-item', { active: selectedAddressId === String(addr.id) }]"
+          @click="handleSelectAddress(addr)"
+        >
+          <div class="address-left">
+            <div class="line">收货人: {{ addr.consignee || addr.receiverName || addr.name || '-' }}</div>
+            <div class="line">联系方式: {{ addr.phone || addr.mobile || '-' }}</div>
+            <div class="line">收货地址: {{ formatAddress(addr) }}</div>
+          </div>
+          <div class="address-right">
+            <span v-if="isDefaultAddress(addr)" class="default-tag">默认地址</span>
+          </div>
+        </div>
+      </div>
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="handleConfirmAddress">确认</el-button>
+          <el-button @click="addressDialogVisible = false">取消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, reactive, ref } from 'vue';
+import { ElMessage } from 'element-plus';
+import { Clock, Close, Plus } from '@element-plus/icons-vue';
+
+import { useRouter, useRoute } from 'vue-router';
+import { getOrderInfo, getOrderProductsWithAvailableQty, getReturnReason } from '@/api/pc/enterprise/order';
+import { getEnterpriseInfo, getAddressList } from '@/api/pc/enterprise';
+import { getOrderReturnInfo, addOrderReturn } from '@/api/pc/enterprise/orderReturn';
+import type { OrderReturn } from '@/api/pc/enterprise/orderReturnTypes';
+import { el } from 'element-plus/es/locale/index.mjs';
+
+const router = useRouter();
+const route = useRoute();
+const loading = ref(false);
+const orderId = ref<any>(0);
+
+const addressDialogVisible = ref(false);
+const addressLoading = ref(false);
+const addressList = ref<any[]>([]);
+const selectedAddressId = ref<string>('');
+const selectedAddress = ref<any>(null);
+
+const orderInfo = reactive({
+  orderId: 0,
+  orderNo: '',
+  orderTime: '',
+  freight: '0.00',
+  totalAmount: '0.00',
+  address: '',
+  receiverName: '',
+  receiverPhone: '',
+  deliveryTime: '',
+  purchaseReason: '',
+  costType: '',
+  remark: ''
+});
+
+type ApplyProduct = {
+  productId: number;
+  productNo: string;
+  productUnit: string;
+  productImage: string;
+  productName: string;
+  orderProductId: number;
+  name: string;
+  spec: string;
+  unitPrice: number;
+  soldQty: number;
+  totalAmount: number;
+  reasonDetail: string;
+  availableQty: number;
+  returnQuantity: number;
+};
+
+const selectedProducts = ref<ApplyProduct[]>([]);
+
+const formRef = ref();
+
+const form = reactive({
+  orderId: 0,
+  orderNo: '',
+  customerNo: '',
+  orderAmount: 0,
+  returnAmount: '',
+  serviceType: '1',
+  returnReasonId: '',
+  returnReason: '',
+  problemDescription: '',
+  returnProductNum: 0,
+  afterSaleAmount: '',
+  voucherPhotoArray: [],
+  voucherPhoto: '',
+  returnMethod: '1',
+  chargebackPickupTime: '' as any,
+  chargebackName: '',
+  chargebackPhone: '',
+  chargebackAddress: '',
+  orderReturnItemList: []
+});
+
+const rules = {
+  returnReasonId: [{ required: true, message: '请选择退货原因', trigger: 'change' }],
+  problemDescription: [{ required: true, message: '请输入问题描述', trigger: 'blur' }]
+};
+
+const reasonOptions = ref([]);
+
+const summary = computed(() => {
+  const list = selectedProducts.value;
+  const totalKinds = list.length;
+  const totalQty = list.reduce((sum, p) => sum + (Number(p.returnQuantity) || 0), 0);
+  const totalAmountNum = list.reduce((sum, p) => sum + (Number(p.returnQuantity) || 0) * (Number(p.unitPrice) || 0), 0);
+  return {
+    totalKinds,
+    totalQty,
+    totalAmount: formatMoney(totalAmountNum)
+  };
+});
+
+const formatMoney = (val: number | string) => {
+  const num = Number(val) || 0;
+  return num.toFixed(2);
+};
+
+const handleQtyChange = () => {
+  // computed 会自动更新
+};
+
+const handleRemoveProduct = (id: number) => {
+  selectedProducts.value = selectedProducts.value.filter((p) => p.productId != id);
+};
+
+const isDefaultAddress = (addr: any) => {
+  const v = addr?.isDefault ?? addr?.defaultFlag;
+  return v === 0 || v === '0' || v === 1 || v === '1' || v === true;
+};
+
+const formatAddress = (addr: any) => {
+  const parts = [addr?.provincialCityCountry].filter(Boolean);
+  const detail = addr?.detailAddress || addr?.address || addr?.fullAddress || '';
+  const prefix = parts.join('');
+  return `${prefix}${detail ? (prefix ? ' ' : '') + detail : ''}`.trim() || '-';
+};
+
+const loadAddressList = async () => {
+  try {
+    addressLoading.value = true;
+    const res = await getAddressList();
+    if (res.code === 200) {
+      const rows = res.rows || res.data || [];
+      addressList.value = Array.isArray(rows) ? rows : [];
+
+      const defaultAddr = addressList.value.find((a) => isDefaultAddress(a));
+      const initAddr = selectedAddress.value || defaultAddr || addressList.value[0];
+      if (initAddr) {
+        selectedAddress.value = initAddr;
+        selectedAddressId.value = String(initAddr.id);
+      }
+    }
+  } catch (error) {
+    console.error('加载地址列表失败:', error);
+    ElMessage.error('加载地址列表失败');
+  } finally {
+    addressLoading.value = false;
+  }
+};
+
+const handleSelectAddress = (addr: any) => {
+  selectedAddress.value = addr;
+  selectedAddressId.value = String(addr.id);
+};
+
+const handleConfirmAddress = () => {
+  if (!selectedAddress.value) {
+    ElMessage.warning('请选择地址');
+    return;
+  }
+
+  const addr = selectedAddress.value;
+  form.chargebackName = addr.consignee || addr.receiverName || addr.name || form.chargebackName;
+  form.chargebackPhone = addr.phone || addr.mobile || form.chargebackPhone;
+  form.chargebackAddress = formatAddress(addr);
+  addressDialogVisible.value = false;
+};
+
+// 加载退货原因列表
+const loadReturnReason = async () => {
+  try {
+    const res = await getReturnReason();
+    if (res.code === 200) {
+      reasonOptions.value = res.rows || [];
+    }
+  } catch (error) {
+    console.error('加载退货原因列表失败:', error);
+  }
+};
+
+// 加载企业信息
+const loadEnterpriseInfo = async () => {
+  try {
+    const res = await getEnterpriseInfo();
+    if (res.code === 200 && res.data) {
+      const data = res.data;
+      const salesInfo = data.customerSalesInfoVo || {};
+      const businessInfo = data.customerBusinessInfoVo || {};
+
+      // 辅助函数:处理空值,空字符串也转为 '-'
+      const formatValue = (value: any) => (value && value !== '' ? value : '-');
+
+      form.chargebackName = formatValue(data.customerName);
+
+      form.chargebackPhone = formatValue(data.landline);
+      form.chargebackAddress = formatValue(data.provincialCityCounty) + ' ' + formatValue(data.address);
+    }
+  } catch (error) {
+    console.error('加载企业信息失败:', error);
+    ElMessage.error('加载企业信息失败');
+  }
+};
+
+const handleClearProducts = () => {
+  selectedProducts.value = [];
+};
+
+const handleChangeAddress = () => {
+  addressDialogVisible.value = true;
+};
+
+const handleViewEvaluation = () => {
+  ElMessage.info('查看评价');
+};
+
+const handleBuyAgain = () => {
+  ElMessage.info('再次购买');
+};
+
+// 加载订单详情
+const loadOrderDetail = async () => {
+  try {
+    loading.value = true;
+    const res = await getOrderInfo(orderId.value);
+    if (res.code === 200 && res.data) {
+      const order = res.data;
+      // 映射订单信息
+      orderInfo.orderId = order.id;
+      orderInfo.orderNo = order.orderNo;
+      form.orderId = order.id;
+      form.orderNo = order.orderNo;
+      form.orderAmount = order.payableAmount || '0.00';
+      orderInfo.orderTime = order.orderTime;
+      orderInfo.freight = order.shippingFee || '0.00';
+      orderInfo.totalAmount = order.payableAmount || '0.00';
+      orderInfo.deliveryTime = order.expectedDeliveryTime || '';
+      orderInfo.purchaseReason = order.purchaseReason || '';
+      orderInfo.remark = order.remark || '';
+      form.customerNo = order.customerCode;
+
+      // 获取商品信息
+      const productsRes = await getOrderProductsWithAvailableQty([orderId.value]);
+      if (productsRes.code === 200 && productsRes.rows) {
+        selectedProducts.value = productsRes.rows.map((p: any) => {
+          const soldQty = Number(p.orderQuantity) || 0;
+          const afterSaleQty = Number(p.afterSaleQuantity) || 0;
+          // const availableQty = Math.max(soldQty - afterSaleQty, 0);
+          const availableQty = Math.max(p.availableQty, 0);
+          const unitPrice = Number(p.orderPrice) || 0;
+
+          return {
+            productId: p.productId,
+            productNo: p.productNo,
+            productUnit: p.productUnit || '',
+            productImage: p.productImage || '',
+            name: p.productName || '',
+            spec: `${p.productUnit || ''} ${p.productNo || ''}`.trim(),
+            unitPrice,
+            soldQty,
+            availableQty,
+            returnQuantity: availableQty > 0 ? 1 : 0,
+            orderProductId: p.id,
+            productName: p.productName,
+            reasonDetail: form.returnReason
+          } as ApplyProduct;
+        });
+      }
+    }
+  } catch (error) {
+    console.error('加载订单详情失败:', error);
+    ElMessage.error('加载订单详情失败');
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleReturnReasonChange = (value: string) => {
+  const reason = reasonOptions.value.find((r) => r.id === value);
+  if (reason) {
+    form.returnReason = reason.returnReasonName;
+  }
+};
+
+onMounted(() => {
+  const paramId = route.query.orderId;
+
+  // 直接使用字符串,不转换为数字,避免精度丢失
+  orderId.value = paramId as string;
+  if (orderId.value) {
+    loadOrderDetail();
+  } else {
+    console.error('订单ID无效,无法加载订单详情');
+  }
+  loadReturnReason();
+  loadEnterpriseInfo();
+  loadAddressList();
+});
+
+const handleSubmit = async () => {
+  if (selectedProducts.value.length === 0) {
+    ElMessage.warning('请至少选择一个商品');
+    return;
+  }
+
+  const hasQty = selectedProducts.value.some((p) => Number(p.returnQuantity) > 0);
+  if (!hasQty) {
+    ElMessage.warning('请填写退货数量');
+    return;
+  }
+
+  const valid = await formRef.value?.validate();
+  if (!valid) return;
+
+  form.returnProductNum = summary.value.totalQty;
+  form.afterSaleAmount = summary.value.totalAmount;
+  form.returnAmount = summary.value.totalAmount;
+  form.voucherPhoto = form.voucherPhotoArray.join(',');
+  form.orderReturnItemList = selectedProducts.value;
+  const res = await addOrderReturn(form as any);
+  if (res.code == 200) {
+    ElMessage.success('申请成功');
+    router.push('/order/afterSale');
+  } else {
+    ElMessage.error('申请失败');
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.apply-after-container {
+  padding: 20px;
+  background: #fff;
+  min-height: 100%;
+  width: 100%;
+}
+
+.page-inner {
+  width: 100%;
+  max-width: 1200px;
+  margin: 0 auto;
+}
+
+.page-header {
+  height: 72px;
+  background: #fff;
+  border-bottom: 1px solid #eee;
+
+  .header-inner {
+    height: 100%;
+    display: flex;
+    align-items: center;
+    padding: 0 20px;
+    gap: 20px;
+  }
+
+  .header-left {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+
+    .header-icon {
+      width: 36px;
+      height: 36px;
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      border: 2px solid #e60012;
+      color: #e60012;
+    }
+
+    .header-title {
+      color: #e60012;
+      font-size: 16px;
+      font-weight: 600;
+    }
+  }
+
+  .header-center {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    gap: 50px;
+    color: #666;
+    font-size: 13px;
+
+    .header-item {
+      .label {
+        color: #999;
+        margin-right: 10px;
+      }
+      .value {
+        color: #333;
+      }
+    }
+  }
+
+  .header-right {
+    display: flex;
+    gap: 10px;
+  }
+}
+
+.content {
+  padding: 16px 20px 30px;
+}
+
+.panel {
+  background: #fff;
+  border: 1px solid #eee;
+  border-radius: 4px;
+  padding: 16px 18px;
+  margin-bottom: 16px;
+}
+
+.panel-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 12px;
+}
+
+.service-type {
+  .service-tip {
+    margin-top: 8px;
+    font-size: 12px;
+    color: #999;
+  }
+}
+
+.section-title {
+  margin-top: 16px;
+  margin-bottom: 10px;
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+}
+
+.summary-bar {
+  background: #eef8ee;
+  border: 1px solid #d8f0d8;
+  color: #18a058;
+  border-radius: 2px;
+  padding: 10px 12px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 12px;
+
+  .summary-left {
+    display: flex;
+    gap: 18px;
+    font-size: 13px;
+  }
+  .summary-close {
+    cursor: pointer;
+    color: #18a058;
+  }
+}
+
+.product-table {
+  border: 1px solid #eee;
+
+  .table-header {
+    display: flex;
+    background: #fafafa;
+    border-bottom: 1px solid #eee;
+    font-size: 12px;
+    color: #666;
+    font-weight: 600;
+  }
+
+  .table-row {
+    display: flex;
+    border-bottom: 1px solid #f0f0f0;
+    &:last-child {
+      border-bottom: none;
+    }
+  }
+
+  .col {
+    padding: 12px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-right: 1px solid #f0f0f0;
+    min-height: 88px;
+
+    &:last-child {
+      border-right: none;
+    }
+  }
+
+  .col-product {
+    flex: 1;
+    justify-content: flex-start;
+  }
+  .col-price,
+  .col-sold,
+  .col-available,
+  .col-qty,
+  .col-amount {
+    width: 110px;
+  }
+  .col-op {
+    width: 80px;
+  }
+
+  .table-empty {
+    padding: 20px 0;
+  }
+
+  .product-info {
+    display: flex;
+    gap: 12px;
+    align-items: center;
+    width: 100%;
+  }
+  .product-image {
+    width: 60px;
+    height: 60px;
+    background: #f5f5f5;
+    border-radius: 4px;
+    overflow: hidden;
+    flex-shrink: 0;
+    .el-image {
+      width: 100%;
+      height: 100%;
+    }
+    .image-placeholder {
+      width: 100%;
+      height: 100%;
+      background: #f5f5f5;
+    }
+  }
+  .product-meta {
+    .name {
+      font-size: 13px;
+      color: #333;
+      line-height: 1.4;
+    }
+    .spec {
+      margin-top: 4px;
+      font-size: 12px;
+      color: #999;
+    }
+  }
+}
+
+.after-form {
+  :deep(.el-form-item__label) {
+    color: #333;
+  }
+}
+
+.pickup-type {
+  margin-bottom: 12px;
+}
+
+.pickup-info {
+  font-size: 13px;
+  color: #666;
+
+  .pickup-row {
+    margin-bottom: 10px;
+    .k {
+      color: #999;
+      margin-right: 6px;
+    }
+    &.hint {
+      color: #999;
+    }
+  }
+}
+
+.footer {
+  display: flex;
+  justify-content: center;
+  padding: 10px 0 20px;
+  .submit-btn {
+    width: 120px;
+  }
+}
+
+.address-dialog {
+  .address-list {
+    max-height: 520px;
+    overflow: auto;
+  }
+
+  .address-item {
+    display: flex;
+    justify-content: space-between;
+    gap: 10px;
+    padding: 14px 16px;
+    border: 1px solid #e5e5e5;
+    margin-bottom: 12px;
+    cursor: pointer;
+
+    &:hover {
+      border-color: #d9d9d9;
+    }
+
+    &.active {
+      border-color: #e60012;
+    }
+
+    .address-left {
+      .line {
+        font-size: 13px;
+        color: #333;
+        line-height: 1.8;
+      }
+    }
+
+    .address-right {
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+      min-width: 80px;
+    }
+
+    .default-tag {
+      color: #e60012;
+      font-size: 12px;
+    }
+  }
+
+  .dialog-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 10px;
+  }
+}
+</style>

+ 4 - 8
src/views/order/orderManage/detail.vue

@@ -52,14 +52,14 @@
             ><template #default="{ row }">¥{{ row.price }}</template></el-table-column
           >
           <el-table-column label="数量" width="150" align="center"
-            ><template #default="{ row }"><el-input-number v-model="row.quantity" :min="1" size="small" disabled /></template
+            ><template #default="{ row }"><el-input-number v-model="row.quantity" :min="1" size="small" :controls="false" disabled /></template
           ></el-table-column>
           <el-table-column prop="subtotal" label="小计" width="100" align="center"
             ><template #default="{ row }">¥{{ row.subtotal }}</template></el-table-column
           >
-          <el-table-column label="操作" width="80" align="center"
+          <!-- <el-table-column label="操作" width="80" align="center"
             ><template #default><el-button type="danger" link size="small">清除</el-button></template></el-table-column
-          >
+          > -->
         </el-table>
         <div class="product-summary">
           共{{ productList.length }}件商品 运费:¥{{ orderInfo.freight }} 共计<span class="total-price">¥{{ orderInfo.totalAmount }}</span>
@@ -221,7 +221,6 @@ const loadOrderDetail = async () => {
       }
     }
   } catch (error) {
-    console.error('加载订单详情失败:', error);
     ElMessage.error('加载订单详情失败');
   } finally {
     loading.value = false;
@@ -229,12 +228,9 @@ const loadOrderDetail = async () => {
 };
 
 onMounted(() => {
-  const paramId = route.params.orderNo || route.params.id;
-  console.log('路由参数:', route.params);
-  console.log('获取到的参数:', paramId);
+  const paramId = route.query.orderId;
   // 直接使用字符串,不转换为数字,避免精度丢失
   orderId.value = paramId as string;
-  console.log('订单ID:', orderId.value);
   if (orderId.value) {
     loadOrderDetail();
   } else {

+ 49 - 16
src/views/order/orderManage/index.vue

@@ -82,6 +82,15 @@
           <span class="order-info">订单号:{{ order.orderNo }}</span>
           <span class="order-info">下单人:{{ order.orderPerson }}</span>
           <span class="order-info">部门:{{ order.department }}</span>
+          <el-button
+            class="expand-btn"
+            v-for="action in getOrderActions(order)"
+            :key="action"
+            type="primary"
+            link
+            @click="handleAction(action, order)"
+            >{{ action }}</el-button
+          >
           <el-button type="primary" link class="expand-btn" @click="handleExpand(order)"
             >{{ order.expanded ? '收起' : '展开' }} <el-icon><ArrowDown /></el-icon
           ></el-button>
@@ -114,16 +123,16 @@
             </div>
             <div class="product-cell status-cell" v-if="itemIndex === 0">
               <span class="status-text" :style="{ color: getStatusColor(order.status) }">{{ order.statusText }}</span>
-              <el-button type="primary" link size="small" @click="handleViewDetail(order)">查看订单轨迹</el-button>
+              <!-- <el-button type="primary" link size="small" @click="handleViewDetail(order)">查看订单轨迹</el-button> -->
               <template v-if="order.auditStatus"
                 ><span :class="['audit-status', getAuditStatusClass(order.auditStatus)]">{{
                   order.auditStatus == '0' ? '待审批' : order.auditStatus == '1' ? '审批通过' : '审批驳回'
-                }}</span
-                ><el-button type="primary" link size="small">查看审批流</el-button></template
-              >
+                }}</span>
+                <!-- <el-button type="primary" link size="small">查看审批流</el-button> -->
+              </template>
               <el-button v-if="order.fileCount" type="primary" link size="small">审核文件({{ order.fileCount }})</el-button>
             </div>
-            <div class="product-cell action-cell" v-if="itemIndex === 0">
+            <!-- <div class="product-cell action-cell" v-if="itemIndex === 0">
               <el-button
                 v-for="action in getOrderActions(order)"
                 :key="action"
@@ -133,7 +142,7 @@
                 @click="handleAction(action, order)"
                 >{{ action }}</el-button
               >
-            </div>
+            </div> -->
           </div>
         </div>
         <!-- 显示更多商品提示 -->
@@ -327,12 +336,25 @@ const loadDeptTree = async () => {
 };
 
 /** 单个加入购物车 */
-const handleAddCart = (item: any) => {
-  addProductShoppingCart({ productId: item.id, productNum: 1 }).then((res: any) => {
-    if (res.code == 200) {
-      ElMessage.success('已加入购物车');
-    }
-  });
+const handleAddCart = async (order: any) => {
+  const res = await getOrderProducts([order.id]);
+  if (res.code === 200 && res.rows) {
+    const products = res.rows.map((p: any) => ({
+      productId: p.productId,
+      productNum: p.orderQuantity || 0
+    }));
+
+    const promises = products.map((item) => addProductShoppingCart({ productId: item.productId, productNum: item.productNum }));
+    Promise.all(promises).then(() => {
+      ElMessage.success(`已将${products.length}件商品加入购物车`);
+    });
+  }
+
+  // addProductShoppingCart({ productId: item.id, productNum: 1 }).then((res: any) => {
+  //   if (res.code == 200) {
+  //     ElMessage.success('已加入购物车');
+  //   }
+  // });
 };
 
 // 获取订单列表
@@ -415,16 +437,20 @@ const getOrderActions = (order: any) => {
   const actions: string[] = [];
   switch (order.status) {
     case 'preOrder':
-      actions.push('再次购买', '加入采购单', '取消订单');
+      // actions.push('再次购买', '加入采购单', '取消订单');
+      actions.push('再次购买');
       break;
     case 'shipping':
-      actions.push('再次购买', '加入采购单', '取消订单');
+      // actions.push('再次购买', '加入采购单', '取消订单');
+      actions.push('再次购买');
       break;
     case 'completed':
-      actions.push('评价', '再次购买', '加入采购单', '申请售后', '删除订单', '查看发票');
+      // actions.push('评价', '再次购买', '加入采购单', '申请售后', '删除订单', '查看发票');
+      actions.push('再次购买', '申请售后');
       break;
     case 'cancelled':
-      actions.push('再次购买', '加入采购单', '删除订单');
+      // actions.push('再次购买', '加入采购单', '删除订单');
+      actions.push('再次购买');
       break;
   }
   return actions;
@@ -525,6 +551,10 @@ const handleOrderCheck = () => {
 const handleViewDetail = (order: any) => {
   router.push(`/order/orderManage/detail/${order.id}`);
 };
+
+const handleApplyAfter = (order: any) => {
+  router.push(`/order/orderManage/applyAfter?orderId=${order.id}`);
+};
 const handleQuery = () => {
   queryParams.pageNum = 1;
   fetchOrderList();
@@ -554,6 +584,9 @@ const handleAction = (action: string, order: any) => {
     case '再次购买':
       handleAddCart(order);
       break;
+    case '申请售后':
+      handleApplyAfter(order);
+      break;
     default:
       ElMessage.info(`${action}功能开发中`);
   }

+ 5 - 8
src/views/organization/approvalFlow/index.vue

@@ -9,8 +9,8 @@
       <el-table-column prop="flowName" label="流程名称" min-width="200" />
       <el-table-column prop="status" label="流程状态" min-width="120">
         <template #default="{ row }">
-          <span :class="['status-text', row.status === '1' ? 'active' : '']">
-            {{ row.status === '1' ? '生效' : '禁用' }}
+          <span :class="['status-text', row.status === '0' ? 'active' : '']">
+            {{ row.status === '0' ? '生效' : '禁用' }}
           </span>
         </template>
       </el-table-column>
@@ -18,12 +18,9 @@
       <el-table-column label="操作" width="200" align="center">
         <template #default="{ row }">
           <el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
-          <el-button
-            :type="row.status === '1' ? 'warning' : 'success'"
-            link
-            size="small"
-            @click="handleToggleStatus(row)"
-          >{{ row.status === '1' ? '关闭' : '开启' }}</el-button>
+          <el-button :type="row.status === '1' ? 'warning' : 'success'" link size="small" @click="handleToggleStatus(row)">{{
+            row.status === '1' ? '关闭' : '开启'
+          }}</el-button>
           <el-button type="danger" link size="small" @click="handleDisable(row)">删除</el-button>
         </template>
       </el-table-column>