Browse Source

feat: 完善订单详情

叶静 1 month ago
parent
commit
b09de41e3c

+ 18 - 0
src/app/shop/admin/order/order.service.js

@@ -82,6 +82,24 @@ const api = {
         method: 'PUT',
         data,
       }),
+    // 联系用户
+    contact: (id) =>
+      request({
+        url: `/shop/admin/order/order/contact/${id}`,
+        method: 'POST',
+      }),
+    // 导出订单
+    export: (id) =>
+      request({
+        url: `/shop/admin/order/order/export/${id}`,
+        method: 'GET',
+      }),
+    // 关闭订单
+    close: (id) =>
+      request({
+        url: `/shop/admin/order/order/close/${id}`,
+        method: 'PUT',
+      }),
     applyRefundRefuse: (id) =>
       request({
         url: `/shop/admin/order/order/applyRefundRefuse/${id}`,

+ 391 - 1102
src/app/shop/admin/order/order/detail.vue

@@ -1,791 +1,221 @@
 <template>
   <el-container class="order-detail">
     <el-main>
-      <div class="warm-tip sa-m-b-16">
-        温馨提示
-        <br />1、如果无法发货,请及时与买家联系并说明情况后主动退款;
-        <br />2、买家申请售后,须征得买家同意后发货,否则买家有权拒收货物;
-        <br />3、订单全部退款将会退回商品库存,并且减少实际销量,订单商品上的主动退款不会退回库存和销量
+      <!-- 订单轨迹 -->
+      <div class="order-track sa-m-b-26">
+        <h3 class="sa-m-b-20">订单轨迹</h3>
+        <el-steps :active="getActiveStep()" align-center>
+          <el-step
+            v-for="(step, index) in state.orderDetail.status_steps"
+            :key="index"
+            :title="step.status_text"
+            :description="step.time"
+            :status="step.completed ? 'finish' : 'wait'"
+          />
+        </el-steps>
       </div>
-      <div class="status-content sa-m-b-16">
-        <el-row :gutter="10">
-          <el-col class="sa-col-12 left" :xs="24" :sm="12" :md="6" :lg="6" :xl="6">
-            <div class="name sa-flex sa-m-b-4">
-              <el-icon class="refresh sa-m-r-4" @click="getOrderDetail"><RefreshLeft /></el-icon>
-              {{ state.orderDetail.status_text }}
+
+      <!-- 基本信息 -->
+      <div class="basic-info sa-m-b-26">
+        <h3 class="sa-m-b-20">基本信息</h3>
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">订单编号:</span>
+              <span class="value">{{ state.orderDetail.order_sn || '--' }}</span>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">订单状态:</span>
+              <span class="value">{{ state.orderDetail.status_text || '--' }}</span>
             </div>
-            <div class="desc sa-m-b-16">
-              {{ state.orderDetail.status_desc }}
+          </el-col>
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">全部金额:</span>
+              <span class="value">৳{{ state.orderDetail.total_amount || '--' }}</span>
             </div>
-            <div class="tools sa-m-b-4">
-              <template v-if="state.orderDetail.btns">
-                <el-button
-                  v-if="state.orderDetail.btns.includes('send')"
-                  v-auth="'shop.admin.order.order.dispatch'"
-                  type="primary"
-                  @click="onOpenDispatch"
-                  >立即发货</el-button
-                >
-                <el-button
-                  v-if="state.orderDetail.btns.includes('refund')"
-                  v-auth="'shop.admin.order.order.fullrefund'"
-                  @click="onOpenRefund"
-                  >全部退款</el-button
-                >
-              </template>
+          </el-col>
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">中奖状态:</span>
+              <span class="value">{{ state.orderDetail.lottery_status || '--' }}</span>
             </div>
-            <div class="memo sa-flex">
-              <template v-if="memoForm.flag">
-                <el-input class="sa-w-120 sa-m-r-12" v-model="memoForm.data" size="small" />
-                <el-button class="is-link" type="primary" size="small" @click="onConfirmMemo"
-                  >确定</el-button
-                >
-                <el-button
-                  class="is-link"
-                  type="primary"
-                  size="small"
-                  @click="memoForm.flag = false"
-                  >取消</el-button
-                >
-              </template>
-              <!-- TODO:这里没有权限的时候有问题 -->
-              <template v-if="!memoForm.flag">
-                <div v-if="state.orderDetail.memo" class="sa-m-r-12">
-                  {{ state.orderDetail.memo }}
-                </div>
-                <el-button
-                  v-auth="'shop.admin.order.order.editmemo'"
-                  class="is-link"
-                  type="primary"
-                  size="small"
-                  @click="onChangeMemoFlag(state.orderDetail.memo)"
-                  >{{ state.orderDetail.memo ? '修改' : '添加' }}备注</el-button
-                >
-              </template>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20" class="sa-m-t-12">
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">用户昵称:</span>
+              <span class="value">{{ state.orderDetail.user_nickname || '--' }}</span>
             </div>
           </el-col>
-          <el-col class="sa-col-24 center" :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
-            <status-steps :orderDetail="state.orderDetail" />
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">支付方式:</span>
+              <span class="value">{{ state.orderDetail.payment_method || '--' }}</span>
+            </div>
           </el-col>
-          <el-col class="sa-col-12 right" :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
-            <div class="right-item">
-              <div class="label">商品总价:</div>
-              <div class="content">¥{{ state.orderDetail.goods_amount }}</div>
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">优惠金额:</span>
+              <span class="value">৳{{ state.orderDetail.discount_amount || '--' }}</span>
             </div>
-            <div class="right-item">
-              <div class="label">运费价格:</div>
-              <div class="content"> ¥{{ state.orderDetail.dispatch_amount }} </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">奖励佣金:</span>
+              <span class="value">৳{{ state.orderDetail.commission_amount || '--' }}</span>
             </div>
-            <div
-              class="right-item right-item-discount"
-              v-if="
-                state.orderDetail.ext &&
-                (state.orderDetail.ext.promo_infos ||
-                state.orderDetail.coupon_id)
-              "
-            >
-              <div class="label">活动优惠:</div>
-              <div class="content">
-                <div
-                  class="sa-flex sa-m-b-12"
-                  v-if="Number(state.orderDetail.ext.promo_discounts.full_reduce) > 0"
-                >
-                  <div class="label">满减</div>
-                  <div class="content sa-m-l-12">
-                    -¥{{ state.orderDetail.ext.promo_discounts.full_reduce }}
-                  </div>
-                </div>
-                <div
-                  class="sa-flex sa-m-b-12"
-                  v-if="Number(state.orderDetail.ext.promo_discounts.full_discount) > 0"
-                >
-                  <div class="label">满折</div>
-                  <div class="content sa-m-l-12">
-                    -¥{{ state.orderDetail.ext.promo_discounts.full_discount }}
-                  </div>
-                </div>
-                <div
-                  class="sa-flex sa-m-b-12"
-                  v-if="Number(state.orderDetail.ext.promo_discounts.full_gift) > 0"
-                >
-                  <div class="label">满赠</div>
-                  <div class="content sa-m-l-12"></div>
-                </div>
-                <div
-                  class="sa-flex sa-m-b-12"
-                  v-if="Number(state.orderDetail.ext.promo_discounts.free_shipping) > 0"
-                >
-                  <div class="label">满包邮</div>
-                  <div class="content sa-m-l-12">
-                    -¥{{ state.orderDetail.ext.promo_discounts.free_shipping }}
-                  </div>
-                </div>
-                <div v-if="state.orderDetail.coupon_id" class="right-item">
-                  <div class="label">优惠券</div>
-                  <div class="content sa-m-l-12">
-                    -¥{{ state.orderDetail.coupon_discount_fee }}
-                  </div>
-                </div>
-              </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20" class="sa-m-t-12">
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">手机号码:</span>
+              <span class="value">{{ state.orderDetail.mobile || '--' }}</span>
             </div>
-            <div class="right-item">
-              <div class="label">
-                {{['paid', 'completed'].includes(state.orderDetail.status)?'实付金额':'应付金额'}}:
-              </div>
-              <div class="content fee-content sa-flex">
-                ¥{{ state.orderDetail.pay_fee }}
-                <s
-                  v-if="state.orderDetail.pay_fee != state.orderDetail.original_pay_fee"
-                  class="original-pay-fee sa-m-l-4"
-                >
-                  {{ state.orderDetail.original_pay_fee }}
-                </s>
-                <el-button
-                  v-if="state.orderDetail.btns && state.orderDetail.btns.includes('change_fee')"
-                  v-auth="'shop.admin.order.order.changefee'"
-                  class="is-link sa-m-l-8"
-                  type="primary"
-                  size="small"
-                  @click="onChangeFee"
-                  >改价</el-button
-                >
-              </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">支付金额:</span>
+              <span class="value">৳{{ state.orderDetail.pay_amount || '--' }}</span>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="info-item">
+              <span class="label">实付金额:</span>
+              <span class="value">৳{{ state.orderDetail.actual_amount || '--' }}</span>
             </div>
           </el-col>
         </el-row>
-        <div class="remark right-item">
-          <div class="label">买家留言:</div>
-          <div class="content">
-            {{ state.orderDetail.remark || '暂无留言' }}
-          </div>
+      </div>
+      <!-- 备注信息 -->
+      <div class="memo-info sa-m-b-26">
+        <h3 class="sa-m-b-20">备注信息</h3>
+        <div class="memo sa-flex">
+          <template v-if="memoForm.flag">
+            <el-input class="sa-w-120 sa-m-r-12" v-model="memoForm.data" size="small" />
+            <el-button class="is-link" type="primary" size="small" @click="onConfirmMemo"
+              >确定</el-button
+            >
+            <el-button class="is-link" type="primary" size="small" @click="memoForm.flag = false"
+              >取消</el-button
+            >
+          </template>
+          <template v-if="!memoForm.flag">
+            <div v-if="state.orderDetail.memo" class="sa-m-r-12">
+              {{ state.orderDetail.memo }}
+            </div>
+            <el-button
+              class="is-link"
+              type="primary"
+              size="small"
+              @click="onChangeMemoFlag(state.orderDetail.memo)"
+              >{{ state.orderDetail.memo ? '修改' : '添加' }}备注</el-button
+            >
+          </template>
+        </div>
+        <div class="remark sa-m-t-12">
+          <span class="label">买家留言:</span>
+          <span class="value">{{ state.orderDetail.remark || '暂无留言' }}</span>
         </div>
       </div>
 
-      <!-- tabs内容 -->
-      <div class="tabs-content sa-m-b-16">
-        <el-tabs v-model="state.orderTabs" @tab-change="state.changeExpressStatus = false">
-          <el-tab-pane label="订单信息" name="1">
-            <el-row class="order-content" :gutter="10">
-              <el-col class="sa-col-12" :xs="24" :sm="24" :md="12" :lg="6" :xl="6">
-                <div class="title">交易信息</div>
-                <div class="item">
-                  <div class="label">订单编号:</div>
-                  <div class="content">
-                    {{ state.orderDetail.order_sn }}
-                    <sa-svg
-                      class="copy sa-m-l-4 cursor-pointer"
-                      name="sa-copy"
-                      @click="useClip(state.orderDetail.order_sn)"
-                    ></sa-svg>
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">订单来源:</div>
-                  <div class="content">
-                    {{ state.orderDetail.platform }}
-                  </div>
-                </div>
-                <div class="item" v-if="state.orderDetail.paid_time">
-                  <div class="label">付款时间:</div>
-                  <div class="content">{{ state.orderDetail.paid_time }}</div>
-                </div>
-              </el-col>
-              <el-col
-                v-if="state.orderDetail.user"
-                class="sa-col-12"
-                :xs="24"
-                :sm="24"
-                :md="12"
-                :lg="6"
-                :xl="6"
-              >
-                <div class="title">买家信息</div>
-                <div class="item">
-                  <div class="label">用户昵称:</div>
-                  <div class="content sa-flex">
-                    <sa-user-profile
-                      :user="state.orderDetail.user"
-                      :id="state.orderDetail.user_id"
-                      :isAvatar="false"
-                    />
-                  </div>
-                </div>
-              </el-col>
-              <el-col
-                v-if="state.orderDetail.address"
-                class="sa-col-12"
-                :xs="24"
-                :sm="24"
-                :md="12"
-                :lg="6"
-                :xl="6"
-              >
-                <div class="title sa-flex">
-                  收货信息
-                  <template v-if="state.orderDetail?.btns.includes('edit_consignee')">
-                    <template v-if="!state.editConsigneeFlag">
-                      <el-button
-                        v-auth="'shop.admin.order.order.editconsignee'"
-                        class="is-link sa-m-l-12"
-                        type="primary"
-                        size="small"
-                        @click="onChangeEditConsigneeFlag(true)"
-                        >修改</el-button
-                      >
-                      <el-button
-                        class="is-link sa-m-l-8"
-                        type="success"
-                        size="small"
-                        @click="
-                          useClip(
-                            `收货昵称:${state.orderDetail.address.consignee};联系方式:${state.orderDetail.address.mobile};
-                          收货地址:${state.orderDetail.address.province_name}${state.orderDetail.address.city_name}${state.orderDetail.address.district_name}${state.orderDetail.address.address}`,
-                          )
-                        "
-                        >复制</el-button
-                      >
-                    </template>
-                    <template v-if="state.editConsigneeFlag">
-                      <el-button
-                        class="is-link sa-m-l-12"
-                        type="danger"
-                        size="small"
-                        @click="state.editConsigneeFlag = false"
-                      >
-                        取消
-                      </el-button>
-                      <el-button
-                        class="is-link sa-m-l-8"
-                        type="primary"
-                        size="small"
-                        @click="onEditConsignee()"
-                        >确定</el-button
-                      >
-                    </template>
-                  </template>
-                </div>
-                <div class="item">
-                  <div class="label">收货昵称:</div>
-                  <div class="content">
-                    <span v-if="!state.editConsigneeFlag">
-                      {{ state.orderDetail.address.consignee }}
-                    </span>
-                    <el-input
-                      v-if="state.editConsigneeFlag"
-                      v-model="state.address.consignee"
-                      size="small"
-                    ></el-input>
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">联系方式:</div>
-                  <div class="content">
-                    <span v-if="!state.editConsigneeFlag">
-                      {{ state.orderDetail.address.mobile }}
-                    </span>
-                    <el-input
-                      v-if="state.editConsigneeFlag"
-                      v-model="state.address.mobile"
-                      size="small"
-                    ></el-input>
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">收货地址:</div>
-                  <div class="content">
-                    <div class="sa-m-b-4" v-if="!state.editConsigneeFlag">
-                      {{ state.orderDetail.address.province_name }}&nbsp;
-                      {{ state.orderDetail.address.city_name }}&nbsp;
-                      {{ state.orderDetail.address.district_name }}
-                    </div>
-                    <el-cascader
-                      v-if="state.editConsigneeFlag"
-                      class="sa-m-b-4"
-                      ref="addressRef"
-                      v-model="state.address.pcd"
-                      :options="state.area"
-                      :props="{ label: 'name', value: 'id' }"
-                      size="small"
-                    />
-                    <div v-if="!state.editConsigneeFlag">
-                      {{ state.orderDetail.address.address }}
-                    </div>
-                    <el-input
-                      v-if="state.editConsigneeFlag"
-                      v-model="state.address.address"
-                      size="small"
-                    ></el-input>
-                  </div>
-                </div>
-              </el-col>
-              <el-col
-                v-if="state.orderDetail.invoice_status == 1"
-                class="sa-col-12"
-                :xs="24"
-                :sm="24"
-                :md="12"
-                :lg="6"
-                :xl="6"
-              >
-                <div class="title sa-flex">
-                  发票信息
-                  <div class="sa-m-l-12">
-                    <span
-                      v-if="state.orderDetail.invoice.status != 'waiting'"
-                      class="invoice-status"
-                      >{{ state.orderDetail.invoice.status_text }}</span
-                    >
-                    <el-button
-                      v-if="state.orderDetail.invoice.status == 'waiting'"
-                      class="is-link"
-                      type="primary"
-                      text
-                      size="small"
-                      @click="onChangeInvoiceStatus(state.orderDetail.invoice)"
-                      >确认开具</el-button
-                    >
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">发票类型:</div>
-                  <div class="content">
-                    {{ state.orderDetail.invoice.type_text }}
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">抬头名称:</div>
-                  <div class="content">
-                    {{ state.orderDetail.invoice.name }}
-                  </div>
-                </div>
-                <div class="item" v-if="state.orderDetail.invoice.type === 'company'">
-                  <div class="label">税号:</div>
-                  <div class="content">
-                    {{ state.orderDetail.invoice.tax_no }}
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">手机号:</div>
-                  <div class="content">
-                    {{ state.orderDetail.invoice.mobile }}
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">金额:</div>
-                  <div class="content"> ¥{{ state.orderDetail.invoice.amount }} </div>
-                </div>
-                <div class="item" v-if="state.orderDetail.invoice.status === 'finish'">
-                  <div class="label">实际开票金额:</div>
-                  <div class="content"> ¥{{ state.orderDetail.invoice.invoice_amount }} </div>
-                </div>
-              </el-col>
-            </el-row>
-          </el-tab-pane>
-          <el-tab-pane
-            :label="`包裹${exindex + 1}`"
-            v-for="(ex, exindex) in state.orderDetail.express"
-            :key="ex"
-            :name="`${exindex + 2}`"
+      <!-- 操作按钮 -->
+      <div class="action-buttons sa-m-b-26">
+        <template v-if="state.orderDetail.btns">
+          <el-button
+            v-if="state.orderDetail.btns.includes('send')"
+            type="primary"
+            @click="onOpenDispatch"
+            >立即发货</el-button
+          >
+          <el-button v-if="state.orderDetail.btns.includes('refund')" @click="onOpenRefund"
+            >全部退款</el-button
+          >
+          <el-button v-if="state.orderDetail.btns.includes('contact_user')" @click="onContactUser"
+            >联系用户</el-button
+          >
+          <el-button v-if="state.orderDetail.btns.includes('export_order')" @click="onExportOrder"
+            >导出订单</el-button
           >
-            <el-row class="express-content" :gutter="10">
-              <el-col class="sa-col-8 left" :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
-                <div class="title sa-flex">
-                  物流信息
-                  <div class="sa-flex sa-m-l-12">
-                    <!-- 运单信息 -->
-                    <template v-if="!state.changeExpressStatus">
-                      <el-button
-                        v-auth="'shop.admin.order.order.dispatch'"
-                        class="is-link"
-                        type="primary"
-                        size="small"
-                        @click="onChangeExpressStatus(ex)"
-                        >修改运单</el-button
-                      >
-                      <el-popconfirm
-                        width="fit-content"
-                        confirm-button-text="确认"
-                        cancel-button-text="取消"
-                        title="您确定要取消运单吗?"
-                        @confirm="onCancelExpress(ex.id)"
-                      >
-                        <template #reference>
-                          <el-button
-                            v-auth="'shop.admin.order.order.dispatch'"
-                            class="is-link sa-m-l-8"
-                            type="danger"
-                            size="small"
-                            @click="cancelExpressPopover = true"
-                          >
-                            取消运单
-                          </el-button>
-                        </template>
-                      </el-popconfirm>
-                    </template>
-                    <!-- 修改运单信息 -->
-                    <template v-else>
-                      <el-button
-                        class="is-link"
-                        type="danger"
-                        size="small"
-                        @click="state.changeExpressStatus = false"
-                      >
-                        取消
-                      </el-button>
-                      <el-button
-                        class="is-link sa-m-l-8"
-                        type="primary"
-                        size="small"
-                        @click="onConfirmExpress(ex.id)"
-                        >确认</el-button
-                      >
-                    </template>
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">快递公司:</div>
-                  <div class="content">
-                    <div v-if="!state.changeExpressStatus">
-                      {{ ex.express_name }}
-                    </div>
-                    <el-select
-                      size="small"
-                      v-if="state.changeExpressStatus"
-                      v-model="express.code"
-                      placeholder="请选择快递公司"
-                      @change="onChangeExpressCode"
-                      filterable
-                      remote
-                      reserve-keyword
-                      :remote-method="remoteMethod"
-                      :loading="deliverCompany.loading"
-                    >
-                      <el-option
-                        v-for="dc in deliverCompany.data"
-                        :key="dc"
-                        :label="dc.name"
-                        :value="dc.code"
-                        :ref="`dc-${dc.code}`"
-                        :data-name="dc.name"
-                        >{{ dc.name }}&nbsp;({{ dc.code }})</el-option
-                      >
-                      <div class="sa-p-l-15 sa-p-r-27">
-                        <sa-pagination
-                          layout="total, prev, pager, next"
-                          :pageData="deliverCompany.pageData"
-                          @updateFn="getDeliverCompany"
-                        />
-                      </div>
-                    </el-select>
-                  </div>
-                </div>
-                <div class="item">
-                  <div class="label">快递单号:</div>
-                  <div class="content">
-                    <div v-if="!state.changeExpressStatus">
-                      {{ ex.express_no }}
-                    </div>
-                    <el-input
-                      size="small"
-                      v-if="state.changeExpressStatus"
-                      v-model="express.no"
-                    ></el-input>
-                  </div>
-                </div>
-                <div class="goods-item sa-flex sa-m-t-12" v-for="goods in ex.items" :key="goods">
-                  <sa-image :url="goods.goods_image" size="40"></sa-image>
-                  <div class="sa-m-l-8">
-                    <div class="goods-title sa-table-line-1 sa-m-b-4">
-                      {{ goods.goods_title }}
-                    </div>
-                    <div class="sku sa-flex">
-                      <div v-if="goods.goods_sku_text" class="sa-m-r-8">
-                        {{ goods.goods_sku_text }}
-                      </div>
-                      <div>x {{ goods.goods_num }}</div>
-                    </div>
-                  </div>
-                </div>
-              </el-col>
-              <el-col
-                v-if="state.orderDetail.user"
-                class="sa-col-16 right"
-                :xs="24"
-                :sm="24"
-                :md="16"
-                :lg="16"
-                :xl="16"
-              >
-                <div class="title sa-flex sa-row-between">
-                  <template class="sa-flex">
-                    物流状态
-                    <el-button
-                      v-auth="'shop.admin.order.order.updateexpress'"
-                      class="is-link sa-m-l-12"
-                      type="primary"
-                      size="small"
-                      @click="onUpdateExpress(ex.id, 'subscribe')"
-                      >重新订阅</el-button
-                    >
-                    <el-popover
-                      placement="top"
-                      :width="300"
-                      trigger="hover"
-                      content="如果长时间物流状态没有更新,可以尝试刷新一下。如果没有物流信息,可以尝试重新订阅一下!"
-                    >
-                      <template #reference>
-                        <el-icon class="warning sa-m-l-4">
-                          <Warning />
-                        </el-icon>
-                      </template>
-                    </el-popover>
-                  </template>
-                  <el-button
-                    v-auth="'shop.admin.order.order.updateexpress'"
-                    class="refresh"
-                    size="small"
-                    icon="RefreshRight"
-                    @click="onUpdateExpress(ex.id, 'search')"
-                  ></el-button>
-                </div>
-                <el-timeline>
-                  <el-timeline-item
-                    :class="index == 0 ? 'el-timeline-item-first' : ''"
-                    v-for="(log, index) in ex.logs"
-                    :key="index"
-                    :timestamp="log.change_date"
-                    :color="index == 0 ? 'var(--t-color-primary)' : ''"
-                  >
-                    <div class="log-content">
-                      {{ log.content }}
-                    </div>
-                    <div class="change-date sa-m-t-8">{{ log.change_date }}</div>
-                  </el-timeline-item>
-                </el-timeline>
-              </el-col>
-            </el-row>
-          </el-tab-pane>
-        </el-tabs>
+          <el-button v-if="state.orderDetail.btns.includes('close_order')" @click="onCloseOrder"
+            >关闭订单</el-button
+          >
+        </template>
       </div>
-
-      <!-- 商品列表 -->
-      <div class="goods-content">
-        <el-tabs>
-          <el-tab-pane label="商品信息">
-            <el-table class="sa-table" :data="state.orderDetail.items" stripe>
-              <el-table-column label="商品信息" min-width="250">
-                <template #default="scope">
-                  <goods-item
-                    :goods="{
-                      id: scope.row.goods_id,
-                      image: scope.row.goods_image,
-                      title: scope.row.goods_title,
-                      sku_text: scope.row.goods_sku_text,
-                    }"
-                    :isGoods="true"
-                    mode="order-detail"
-                  ></goods-item>
-                </template>
-              </el-table-column>
-              <el-table-column prop="goods_price" label="单价(元)" min-width="100" />
-              <el-table-column prop="goods_num" label="数量(件)" min-width="80" />
-              <el-table-column prop="discount_fee" label="优惠(元)" min-width="90" />
-              <el-table-column prop="pay_fee" label="支付金额" min-width="120" />
-              <el-table-column label="订单类型" min-width="100">
-                <template #default>{{ state.orderDetail.type_text }}</template>
-              </el-table-column>
-              <el-table-column prop="dispatch_type_text" label="配送方式" width="100">
-              </el-table-column>
-              <el-table-column label="发货状态" min-width="120">
-                <template #default="scope">
-                  <status-button
-                    from="detail"
-                    :order="state.orderDetail"
-                    type="dispatch"
-                    :item="scope.row"
-                  ></status-button>
-                </template>
-              </el-table-column>
-              <el-table-column label="退款状态" min-width="100">
-                <template #default="scope">
-                  <status-button
-                    from="detail"
-                    :order="state.orderDetail"
-                    type="refund"
-                    :item="scope.row"
-                    @updateList="getOrderDetail"
-                  ></status-button>
-                </template>
-              </el-table-column>
-              <el-table-column label="售后状态" min-width="120">
-                <template #default="scope">
-                  <status-button from="detail" type="aftersale" :item="scope.row"></status-button>
-                </template>
-              </el-table-column>
-              <el-table-column label="商品评价" min-width="100">
-                <template #default="scope">
-                  <status-button
-                    type="comment"
-                    :order="state.orderDetail"
-                    :item="scope.row"
-                  ></status-button>
-                </template>
-              </el-table-column>
-              <el-table-column width="100" label="配送信息">
-                  <template #default="scope">
-                      <div v-if="state.orderDetail.dispatch_status!=0">
-                          <el-popover trigger="hover" width="220">
-                              <div
-                                  v-if="scope.row.dispatch_type=='autosend' || scope.row.dispatch_type=='custom'">
-                                  <span>发货内容:</span>
-                                  <template v-if="scope.row.ext?.dispatch_content_type=='text'">
-                                      {{scope.row.ext?.dispatch_content}}
-                                  </template>
-                                  <template v-if="scope.row.ext?.dispatch_content_type=='params'"
-                                      v-for="item in scope.row.ext?.dispatch_content">
-                                      {{item.title}}-{{item.content}}
-                                  </template>
-                              </div>
-                              <template v-if="scope.row.dispatch_type!='express'" #reference>
-                                  <el-button type="primary" link>查看详情</el-button>
-                              </template>
-                          </el-popover>
-                          <div v-if="scope.row.dispatch_type=='express'">-</div>
-                      </div>
-                      <div v-else>-</div>
-                  </template>
-              </el-table-column>
-            </el-table>
-          </el-tab-pane>
-          <el-tab-pane v-if="state.orderDetail?.activity_orders?.length > 0" label="优惠信息">
-            <el-table class="sa-table" :data="state.orderDetail.activity_orders" stripe>
-              <el-table-column prop="id" label="ID" min-width="80" />
-              <el-table-column label="活动标题" min-width="120">
-                <template #default="scope">
-                  <div class="sa-table-line-1">
-                    {{ scope.row.activity_title }}
-                  </div>
-                </template>
-              </el-table-column>
-              <el-table-column label="活动类型" min-width="120" align="center">
-                <template #default="scope">
-                  <div
-                    :style="
-                      activityStatusStyle(scope.row.activity_type, {
-                        padding: '8px 12px',
-                        'border-radius': '15px',
-                      })
-                    "
-                  >
-                    {{ scope.row.activity_type_text }}
-                  </div>
-                </template>
-              </el-table-column>
-              <el-table-column label="优惠信息" min-width="120" align="center">
-                <template #default="scope">
-                  <div class="sa-table-line-1">
-                    {{ scope.row.discount_text }}
-                  </div>
-                </template>
-              </el-table-column>
-              <el-table-column
-                prop="discount_fee"
-                label="优惠金额(元)"
-                min-width="120"
-                align="center"
-              />
-              <el-table-column
-                prop="goods_amount"
-                label="参与商品金额(元)"
-                min-width="140"
-                align="center"
+      <!-- 商品信息 -->
+      <div class="goods-content sa-m-b-16">
+        <div class="sa-title sa-m-b-10">商品信息</div>
+        <el-table class="sa-table" :data="state.orderDetail.items" stripe border>
+          <el-table-column label="商品图片" width="100" align="center">
+            <template #default="{ row }">
+              <el-image
+                :src="row.goods_image"
+                style="width: 60px; height: 60px; border-radius: 4px"
+                fit="cover"
               />
-              <el-table-column label="参与商品" min-width="250" align="center">
-                <template #default="scope">
-                  <el-popover placement="top-start" :width="300" trigger="hover">
-                    <div class="discount-goods">
-                      <div class="item sa-flex" v-for="goods in scope.row.items" :key="goods">
-                        <sa-image :url="goods.goods_image" size="40"></sa-image>
-                        <div class="sa-m-l-12">
-                          <div class="sa-table-line-1">
-                            {{ goods.goods_title }}
-                          </div>
-                          <div class="">#{{ goods.goods_id }}</div>
-                        </div>
-                      </div>
-                    </div>
-                    <template #reference>
-                      <div>
-                        <span class="discount-items">
-                          {{ scope.row.items?.length }}
-                        </span>
-                        件商品
-                      </div>
-                    </template>
-                  </el-popover>
-                </template>
-              </el-table-column>
-              <el-table-column prop="create_time" label="参与时间" min-width="172" />
-            </el-table>
-          </el-tab-pane>
-        </el-tabs>
-      </div>
-
-      <!-- 支付列表 -->
-      <div class="sa-title">支付信息</div>
-      <div class="pay-content">
-        <el-table class="sa-table" :data="state.orderDetail.pays" stripe>
-          <el-table-column prop="pay_sn" label="支付单号" min-width="250" />
-          <el-table-column prop="pay_type_text" label="支付方式" min-width="80" align="center" />
-          <el-table-column prop="pay_fee" label="支付金额" min-width="120" align="center" />
-          <el-table-column prop="transaction_id" label="交易单号" min-width="280" align="center">
-            <template #default="scope">
-              <span>{{ scope.row.transaction_id || '-' }}</span>
             </template>
           </el-table-column>
-          <el-table-column label="支付状态" min-width="120" align="center">
-            <template #default="scope">
-              <span :class="['status', `status-${scope.row.status}`]">{{
-                scope.row.status_text
-              }}</span>
-            </template>
+          <el-table-column label="商品名称" prop="goods_title" min-width="200" />
+          <el-table-column label="规格" prop="goods_sku_text" width="120" align="center" />
+          <el-table-column label="数量" prop="goods_num" width="80" align="center" />
+          <el-table-column label="单价" width="120" align="center">
+            <template #default="{ row }"> ৳{{ row.goods_price || '--' }} </template>
           </el-table-column>
-          <el-table-column prop="refund_fee" label="已退款金额" min-width="120" align="center" />
-          <el-table-column prop="create_time" label="交易时间" min-width="172" align="center" />
+        </el-table>
+      </div>
+
+      <!-- 收货信息 -->
+      <div class="address-content sa-m-b-16">
+        <div class="sa-title sa-m-b-10">收货信息</div>
+        <el-table class="sa-table" :data="[state.orderDetail.address]" stripe border>
+          <el-table-column label="收件人" prop="consignee" width="120" align="center" />
+          <el-table-column label="手机号" prop="mobile" width="150" align="center" />
+          <el-table-column label="详细地址" prop="address" min-width="200" />
+          <el-table-column label="物流公司" prop="express_company" width="120" align="center" />
+          <el-table-column label="物流单号" prop="express_no" width="180" align="center" />
+        </el-table>
+      </div>
+
+      <!-- 拼团信息 -->
+      <div class="group-content" v-if="state.orderDetail.group_info">
+        <div class="sa-title sa-m-b-10"
+          >拼团信息({{ state.orderDetail.group_info.group_type }})</div
+        >
+        <el-table class="sa-table" :data="state.orderDetail.group_info.members" stripe border>
+          <el-table-column label="昵称" prop="nickname" min-width="120" align="center" />
+          <el-table-column label="手机号" prop="mobile" min-width="150" align="center" />
+          <el-table-column label="参团订单号" prop="order_sn" min-width="180" align="center" />
+          <el-table-column label="参团时间" prop="join_time" min-width="160" align="center" />
+          <el-table-column label="支付时间" prop="pay_time" min-width="160" align="center" />
+          <el-table-column label="奖励佣金" prop="commission" min-width="120" align="center" />
         </el-table>
       </div>
     </el-main>
   </el-container>
 </template>
 <script setup>
-  import { computed, onMounted, reactive, ref, unref } from 'vue';
+  import { onMounted, reactive } from 'vue';
+  import { ElMessage } from 'element-plus';
   import { api } from '../order.service';
-  import { api as dataApi } from '@/app/shop/admin/data/data.service';
   import { useModal } from '@/sheep/hooks';
-  import useClip from '@/sheep/utils/clipboard.js';
-  import useExpress from '@/app/shop/admin/data/express/express.js';
-
-  import StatusButton from './components/status.vue';
-  import { activityStatusStyle } from './status.js';
-  import GoodsItem from '@/app/shop/components/goods-item.vue';
   import OrderDispatch from './dispatch.vue';
   import OrderRefund from './refund.vue';
-  import OrderFee from './fee.vue';
-  import StatusSteps from './components/status-steps.vue';
-  import InvoiceEdit from '../invoice/edit.vue';
-  import { cloneDeep } from 'lodash';
 
   const emit = defineEmits(['modalCallBack']);
   const props = defineProps(['modal']);
 
   const state = reactive({
     orderDetail: {}, // 订单详情
-    orderStep: 1,
-    orderTabs: '1',
-    changeExpressStatus: false, // 变更运单状态
-    editConsigneeFlag: false,
-    address: {},
-    area: [],
+  });
+
+  // 备注表单
+  const memoForm = reactive({
+    flag: false,
+    data: '',
   });
 
   // 获取订单详情
@@ -793,449 +223,308 @@
     const { error, data } = await api.order.detail(props.modal.params.id);
     if (error === 0) {
       state.orderDetail = data;
-      state.orderTabs = '1';
-
-      onChangeEditConsigneeFlag(false);
-      changeOrderStep();
     }
   }
 
-  async function getArea() {
-    const { data } = await dataApi.area.select();
-    state.area = data;
+  // 获取激活步骤
+  function getActiveStep() {
+    if (!state.orderDetail.status_steps) return 0;
+    return state.orderDetail.status_steps.filter((step) => step.completed).length - 1;
   }
 
-  function changeOrderStep() {
-    if (state.orderDetail.status == 'unpaid') {
-      state.orderStep = 1;
-    } else if (state.orderDetail.status == 'paid') {
-      state.orderStep = 2;
-      switch (state.orderDetail.status_code) {
-        case 'nosend':
-          state.orderStep = 2;
-          break;
-        case 'noget':
-          state.orderStep = 3;
-          break;
-        case 'nocomment':
-          state.orderStep = 4;
-          break;
-        case 'commented':
-          state.orderStep = 4;
-          break;
-      }
-    } else if (state.orderDetail.status == 'completed') {
-      state.orderStep = 5;
+  // 发货
+  function onOpenDispatch() {
+    // 检查订单数据是否完整
+    if (!state.orderDetail || !state.orderDetail.items) {
+      ElMessage.error('订单数据加载中,请稍后再试');
+      return;
     }
-  }
 
-  function onOpenDispatch() {
     useModal(
       OrderDispatch,
       {
         title: '订单发货',
-        data: state.orderDetail,
+        params: {
+          data: state.orderDetail,
+        },
       },
       {
-        confirm: () => {
+        success: () => {
           getOrderDetail();
         },
       },
     );
   }
 
+  // 退款
   function onOpenRefund() {
     useModal(
       OrderRefund,
       {
-        title: '全部退款',
-        class: 'refund-dialog',
-        type: 'order.fullRefund',
-        data: {
+        title: '订单退款',
+        width: '600px',
+        params: {
           id: state.orderDetail.id,
-          money: state.orderDetail.pay_fee,
-        },
-      },
-      {
-        confirm: () => {
-          getOrderDetail();
         },
       },
-    );
-  }
-
-  function onChangeFee() {
-    useModal(
-      OrderFee,
-      {
-        title: '改价',
-        class: 'fee-dialog',
-        id: state.orderDetail.id,
-        pay_fee: state.orderDetail.pay_fee,
-      },
       {
-        confirm: () => {
+        success: () => {
           getOrderDetail();
         },
       },
     );
   }
 
-  // 备注
-  const memoForm = reactive({
-    flag: false,
-    data: '',
-  });
-  function onChangeMemoFlag(val) {
-    memoForm.flag = true;
-    memoForm.data = val;
-  }
-  async function onConfirmMemo() {
-    const { error } = await api.order.editMemo(state.orderDetail.id, {
-      memo: memoForm.data,
-    });
-    if (error == 0) {
-      memoForm.flag = false;
-      getOrderDetail();
+  // 联系用户
+  async function onContactUser() {
+    try {
+      const { error } = await api.order.contact(state.orderDetail.id);
+      if (error === 0) {
+        ElMessage.success('已发送联系信息');
+      }
+    } catch (err) {
+      ElMessage.error('联系用户失败');
     }
   }
 
-  const { express, deliverCompany, getDeliverCompany, onChangeExpressCode, remoteMethod } =
-    useExpress();
-
-  function onChangeExpressStatus(ex) {
-    state.changeExpressStatus = true;
-
-    express.name = ex.express_name;
-    express.code = ex.express_code;
-    express.no = ex.express_no;
-  }
-
-  async function onChangeInvoiceStatus(row) {
-    useModal(
-      InvoiceEdit,
-      {
-        title: '编辑',
-        type: 'edit',
-        data: row,
-      },
-      {
-        confirm: () => {
-          getOrderDetail();
-        },
-      },
-    );
-    // const { data } = await api.invoice.confirm(id);
-  }
-
-  // 确认修改运单
-  async function onConfirmExpress(order_express_id) {
-    await api.order.dispatch({
-      order_id: props.modal.params.id,
-      order_express_id,
-      action: 'change',
-      express: express,
-    });
-    state.changeExpressStatus = false;
-    getOrderDetail();
+  // 导出订单
+  async function onExportOrder() {
+    try {
+      const { error, data } = await api.order.export(state.orderDetail.id);
+      if (error === 0) {
+        ElMessage.success('导出成功');
+        if (data.download_url) {
+          window.open(data.download_url);
+        }
+      }
+    } catch (err) {
+      ElMessage.error('导出订单失败');
+    }
   }
 
-  // 取消运单
-  const cancelExpressPopover = ref(false);
-  async function onCancelExpress(order_express_id) {
-    await api.order.dispatch({
-      order_id: props.modal.params.id,
-      order_express_id,
-      action: 'cancel',
-    });
-    getOrderDetail();
+  // 关闭订单
+  async function onCloseOrder() {
+    try {
+      const { error } = await api.order.close(state.orderDetail.id);
+      if (error === 0) {
+        ElMessage.success('订单已关闭');
+        getOrderDetail();
+      }
+    } catch (err) {
+      ElMessage.error('关闭订单失败');
+    }
   }
 
-  async function onUpdateExpress(order_express_id, type) {
-    const { error } = await api.order.updateExpress(order_express_id, { type });
-    if (error == 0) getOrderDetail();
+  // 修改备注标志
+  function onChangeMemoFlag(memo) {
+    memoForm.flag = true;
+    memoForm.data = memo || '';
   }
 
-  // 修改收货信息
-  function onChangeEditConsigneeFlag(type) {
-    if (state.orderDetail.address) {
-      state.address = {
-        consignee: state.orderDetail.address.consignee,
-        mobile: state.orderDetail.address.mobile,
-        pcd: [
-          state.orderDetail.address.province_id,
-          state.orderDetail.address.city_id,
-          state.orderDetail.address.district_id,
-        ],
-        address: state.orderDetail.address.address,
-      };
+  // 确认备注
+  async function onConfirmMemo() {
+    try {
+      const { error } = await api.order.editMemo(state.orderDetail.id, { memo: memoForm.data });
+      if (error === 0) {
+        ElMessage.success('备注更新成功');
+        memoForm.flag = false;
+        getOrderDetail();
+      }
+    } catch (err) {
+      ElMessage.error('备注更新失败');
     }
-    state.editConsigneeFlag = type;
-  }
-  const addressRef = ref();
-  async function onEditConsignee() {
-    let label = unref(addressRef).getCheckedNodes()[0].pathLabels;
-    let tempAddress = cloneDeep(state.address);
-    tempAddress = {
-      ...tempAddress,
-      province_name: label[0],
-      province_id: state.address.pcd[0],
-      city_name: label[1],
-      city_id: state.address.pcd[1],
-      district_name: label[2],
-      district_id: state.address.pcd[2],
-    };
-    delete tempAddress.pcd;
-
-    await api.order.editConsignee(state.orderDetail.id, tempAddress);
-    getOrderDetail();
-    state.editConsigneeFlag = false;
   }
 
   onMounted(() => {
     getOrderDetail();
-    getArea();
   });
 </script>
+
 <style lang="scss" scoped>
   .order-detail {
-    .warm-tip {
-      line-height: 16px;
-      font-size: 12px;
-      color: var(--sa-font);
+    .order-track {
+      h3 {
+        font-size: 16px;
+        font-weight: 600;
+        color: var(--sa-title);
+      }
     }
-    .status-content {
-      border-radius: 8px;
-      padding: var(--sa-padding);
-      background: var(--sa-table-header-bg);
-      .left,
-      .center,
-      .right {
-        margin-bottom: var(--sa-padding);
+
+    .basic-info {
+      h3 {
+        font-size: 16px;
+        font-weight: 600;
+        color: var(--sa-title);
       }
-      .right-item {
-        line-height: 16px;
-        margin-bottom: 8px;
+
+      .info-item {
         display: flex;
         align-items: center;
-        font-size: 12px;
-        &.right-item-discount {
-          align-items: flex-start;
-          margin-bottom: 0;
-        }
+        margin-bottom: 8px;
+
         .label {
           flex-shrink: 0;
           color: var(--sa-subfont);
+          font-size: 14px;
         }
-        .content {
+
+        .value {
           color: var(--sa-subtitle);
+          font-size: 14px;
+          font-weight: 500;
         }
       }
-      .left {
-        .name {
-          line-height: 24px;
-          font-size: 18px;
-          color: var(--sa-title);
-          font-weight: 900;
-          .refresh {
-            color: var(--t-color-primary);
-            cursor: pointer;
-          }
-        }
-        .desc {
-          line-height: 16px;
-          font-size: 12px;
-          color: var(--sa-subfont);
-        }
-        .tools {
-          height: 32px;
-        }
-        .memo {
-          height: 24px;
-          line-height: 24px;
-          font-size: 12px;
-          font-weight: 400;
-          color: var(--sa-font);
-        }
-      }
-      .right {
-        .fee-content {
-          height: 16px;
-          .original-pay-fee {
-            color: #999;
-          }
-        }
-      }
-      .remark {
-        height: 32px;
-        line-height: 32px;
-        padding: 0 12px;
-        background: var(--sa-table-striped);
-        border-radius: 4px;
-      }
     }
-    .tabs-content {
-      :deep() {
-        .el-tabs {
-          .el-tabs__content {
-            padding: var(--sa-padding) var(--sa-padding) 0;
-          }
-        }
+
+    .action-buttons {
+      .el-button {
+        margin-right: 12px;
       }
-      .title {
-        height: 24px;
-        font-size: 14px;
+    }
+
+    .goods-content,
+    .address-content,
+    .group-content,
+    .memo-info {
+      .sa-title {
+        font-size: 16px;
         font-weight: 600;
         color: var(--sa-title);
-        margin-bottom: 8px;
-        display: flex;
-        align-items: center;
-        overflow: hidden;
-        .warning {
-          color: #faad14;
-        }
       }
-      .item {
-        line-height: 24px;
-        margin-bottom: 4px;
-        display: flex;
-        font-size: 12px;
-        &:last-child {
-          margin-bottom: 0;
-        }
-        .label {
-          flex-shrink: 0;
-          color: var(--sa-subfont);
-        }
-        .content {
-          color: var(--sa-subtitle);
-        }
-        .copy {
-          color: var(--el-color-primary);
-        }
-      }
-      .order-content {
-        .el-col {
-          padding-bottom: var(--sa-padding);
-        }
-        .invoice-status {
-          font-size: 12px;
-          color: var(--sa-font);
+
+      .sa-table {
+        border-radius: 8px;
+        overflow: hidden;
+        border: 1px solid #dcdfe6;
+
+        :deep(.el-table) {
+          border: none;
+          border-radius: 0;
         }
-        :deep() {
-          .nickname {
-            height: 24px;
-            line-height: 24px;
+
+        :deep(.el-table__header-wrapper) {
+          background: var(--sa-table-header-bg, #f5f7fa);
+
+          .el-table__header {
+            background: var(--sa-table-header-bg, #f5f7fa);
+
+            th {
+              background: var(--sa-table-header-bg, #f5f7fa) !important;
+              font-weight: 600;
+              color: var(--sa-title, #303133);
+              border-bottom: 1px solid #dcdfe6;
+              border-right: 1px solid #dcdfe6;
+
+              &:first-child {
+                border-left: none;
+              }
+
+              &:last-child {
+                border-right: none;
+              }
+            }
           }
         }
-      }
-      .express-content {
-        .left {
-          padding-bottom: var(--sa-padding);
-          max-height: 218px;
-          overflow: auto;
-          .goods-item {
-            .goods-title {
-              font-weight: 500;
-            }
-            .goods-title,
-            .sku {
-              height: 16px;
-              line-height: 16px;
-              font-size: 12px;
-              color: var(--sa-font);
+
+        :deep(.el-table__body-wrapper) {
+          .el-table__body {
+            tr {
+              &:hover {
+                background: #f5f7fa !important;
+              }
+
+              td {
+                border-bottom: 1px solid #dcdfe6;
+                border-right: 1px solid #dcdfe6;
+                border-left: none;
+
+                &:first-child {
+                  border-left: none;
+                }
+
+                &:last-child {
+                  border-right: none;
+                }
+              }
+
+              &:last-child td {
+                border-bottom: none;
+              }
             }
           }
         }
-        .right {
-          padding-bottom: var(--sa-padding);
-          max-height: 218px;
-          overflow: auto;
-          .title {
-            .refresh {
-              padding: 5px;
-              background-color: transparent;
-            }
+
+        // 确保边框完整性
+        :deep(.el-table--border) {
+          border: none;
+
+          &::after {
+            display: none;
           }
-          :deep() {
-            .el-timeline-item__timestamp {
-              position: absolute;
-              top: 2px;
-              left: -140px;
-              margin-top: 0;
-            }
+
+          &::before {
+            display: none;
           }
         }
-      }
-    }
-    .goods-content {
-      border-radius: 8px;
-      overflow: hidden;
-    }
-    .pay-content {
-      border-radius: 8px;
-      overflow: hidden;
-      .status {
-        &.status-unpaid {
-          color: #999;
+
+        :deep(.el-table--border .el-table__cell) {
+          border-right: 1px solid #dcdfe6;
+          border-left: none;
+
+          &:first-child {
+            border-left: none;
+          }
+
+          &:last-child {
+            border-right: none;
+          }
         }
-        &.status-paid {
-          color: #52c41a;
+
+        // 修复表格底部和左侧边框
+        :deep(.el-table__body) {
+          border-left: none;
+          border-bottom: none;
         }
-        &.status-refund {
-          color: #ff4d4f;
+
+        :deep(.el-table__header) {
+          border-left: none;
         }
       }
     }
-    :deep() {
-      .el-tabs {
-        .el-tabs__nav-wrap::after {
-          height: 0;
+
+    .group-content {
+      .sa-table {
+        width: 100% !important;
+
+        :deep(.el-table) {
+          width: 100% !important;
         }
-        .el-tabs__content {
-          background: var(--sa-table-header-bg);
-          border-radius: 8px;
+
+        :deep(.el-table__header-wrapper) {
+          width: 100% !important;
         }
-      }
-    }
-    .discount-items {
-      color: var(--el-color-primary);
-    }
 
-    .discount-goods {
-      .item {
-        padding: 8px 0;
-        border-bottom: 1px solid var(--sa-border);
-        &:last-of-type {
-          border-bottom: 0;
+        :deep(.el-table__body-wrapper) {
+          width: 100% !important;
         }
       }
     }
-    .log-content {
-      line-height: 18px;
-      font-size: 12px;
-      font-weight: 400;
-      color: var(--sa-subtitle);
-    }
-    .change-date {
-      line-height: 14px;
-      font-size: 12px;
-      font-weight: 400;
-      color: var(--sa-subtitle);
-    }
-    .el-timeline-item-first {
-      &::after {
-        content: '';
-        position: absolute;
-        top: 20px;
-        left: 4px;
-        width: 2px;
-        height: calc(50% - 16px);
-        background: var(--t-color-primary);
+
+    .memo-info {
+      .memo {
+        margin-bottom: 12px;
       }
-      .el-timeline-item__tail {
-        top: 8px;
-        bottom: 8px;
+
+      .remark {
+        .label {
+          color: var(--sa-subfont);
+          font-size: 14px;
+        }
+
+        .value {
+          color: var(--sa-subtitle);
+          font-size: 14px;
+        }
       }
     }
   }

+ 16 - 16
src/app/shop/admin/order/order/dispatch.vue

@@ -39,28 +39,28 @@
         </el-table>
         <div class="address">
           <div class="title sa-m-b-16">配送信息</div>
-          <div class="sa-m-l-16" v-if="props.modal.params.data.address">
+          <div class="sa-m-l-16" v-if="props.modal?.params?.params?.data?.address">
             <div class="sa-flex sa-m-b-8">
               <div class="label">收货信息:</div>
               <div class="content">
-                {{ props.modal.params.data.address.consignee }}&nbsp;
-                {{ props.modal.params.data.address.mobile }}
+                {{ props.modal?.params?.params?.data?.address?.consignee }}&nbsp;
+                {{ props.modal?.params?.params?.data?.address?.mobile }}
               </div>
             </div>
             <div class="sa-flex sa-col-top sa-m-b-16">
               <div class="label">收货地址:</div>
               <div class="content">
                 <div>
-                  {{ props.modal.params.data.address.province_name }}&nbsp;
-                  {{ props.modal.params.data.address.city_name }}&nbsp;
-                  {{ props.modal.params.data.address.district_name }}
+                  {{ props.modal?.params?.params?.data?.address?.province_name }}&nbsp;
+                  {{ props.modal?.params?.params?.data?.address?.city_name }}&nbsp;
+                  {{ props.modal?.params?.params?.data?.address?.district_name }}
                 </div>
-                <div>{{ props.modal.params.data.address.address }}</div>
+                <div>{{ props.modal?.params?.params?.data?.address?.address }}</div>
               </div>
             </div>
           </div>
-          <template v-if="!props.modal.params.data.address">{{
-            props.modal.params.data.address_id
+          <template v-if="!props.modal?.params?.params?.data?.address">{{
+            props.modal?.params?.params?.data?.address_id
           }}</template>
         </div>
         <div class="express">
@@ -228,8 +228,8 @@
 
   const state = reactive({
     nosendItem: computed(() => {
-      if (!props.modal.params.data.items) return [];
-      return props.modal.params.data.items.filter(
+      if (!props.modal?.params?.params?.data?.items) return [];
+      return props.modal.params.params.data.items.filter(
         (i) =>
           (i.dispatch_status == 0 || i.dispatch_status === undefined) &&
           (i.refund_status == 0 || i.refund_status === undefined) &&
@@ -246,8 +246,8 @@
 
     dispatch_type: 'express',
     customItem: computed(() => {
-      if (!props.modal.params.data.items) return [];
-      return props.modal.params.data.items.filter(
+      if (!props.modal?.params?.params?.data?.items) return [];
+      return props.modal.params.params.data.items.filter(
         (i) =>
           (i.dispatch_status == 0 || i.dispatch_status === undefined) &&
           (i.refund_status == 0 || i.refund_status === undefined) &&
@@ -277,7 +277,7 @@
             if (valid) {
               state.sendLoading = true;
               const { error } = await api.order.dispatch({
-                order_id: props.modal.params.data.id,
+                order_id: props.modal?.params?.params?.data?.id,
                 order_item_ids,
                 action: 'confirm',
                 method: 'input',
@@ -292,7 +292,7 @@
       } else if (state.method == 'api') {
         state.sendLoading = true;
         const { error } = await api.order.dispatch({
-          order_id: props.modal.params.data.id,
+          order_id: props.modal?.params?.params?.data?.id,
           order_item_ids,
           action: 'confirm',
           method: 'api',
@@ -304,7 +304,7 @@
       }
     } else if (state.dispatch_type == 'custom') {
       const { error } = await api.order.customDispatch({
-        order_id: props.modal.params.data.id,
+        order_id: props.modal?.params?.params?.data?.id,
         order_item_ids,
         custom_type: state.custom_type,
         custom_content: state.custom_content,

+ 4 - 2
src/app/shop/admin/order/order/index.vue

@@ -422,10 +422,12 @@
       OrderDispatch,
       {
         title: '订单发货',
-        data: row,
+        params: {
+          data: row,
+        },
       },
       {
-        confirm: () => {
+        success: () => {
           getData();
         },
       },

+ 0 - 1207
src/app/shop/admin/order/order/o-index.vue

@@ -1,1207 +0,0 @@
-<template>
-  <el-container class="order-page panel-block">
-    <el-header class="sa-header">
-      <el-tabs class="sa-tabs" v-model="filterParams.data.status" @tab-change="onChangeStatus">
-        <el-tab-pane
-          v-for="(sl, key) in statusList"
-          :key="sl"
-          :label="`${sl.label}${sl.num ? '(' + sl.num + ')' : ''}`"
-          :name="key"
-        ></el-tab-pane>
-      </el-tabs>
-      <div class="sa-title sa-flex sa-row-between">
-        <div class="label sa-flex">
-          <span class="left">订单列表</span>
-          <search-condition
-            :conditionLabel="filterParams.conditionLabel"
-            @deleteFilter="deleteFilter"
-          ></search-condition>
-        </div>
-        <div>
-          <el-button
-            v-auth="'shop.admin.order.order.list'"
-            class="sa-button-refresh"
-            icon="RefreshRight"
-            @click="getData()"
-          ></el-button>
-          <el-button class="sa-button-refresh" icon="Search" @click="openFilter"></el-button>
-          <el-button
-            v-auth="'shop.admin.order.order.export'"
-            :loading="exportLoading"
-            :disabled="exportLoading"
-            @click="onExport('export')"
-            >订单导出</el-button
-          >
-          <el-button
-            v-if="filterParams.data.status == 'nosend'"
-            v-auth="'shop.admin.order.order.exportdelivery'"
-            :loading="exportLoading"
-            :disabled="exportLoading"
-            @click="onExport('exportDelivery')"
-            >导出发货单</el-button
-          >
-        </div>
-      </div>
-    </el-header>
-    <el-main class="sa-p-0" v-loading="loading">
-      <el-table
-        height="100%"
-        class="sa-table"
-        :data="table.data"
-        :span-method="arraySpanMethod"
-        default-expand-all
-        @selection-change="handleSelectionChange"
-      >
-        <template #empty>
-          <sa-empty />
-        </template>
-        <el-table-column type="expand">
-          <template #default="props">
-            <el-table
-              class="sa-table sa-expand-table"
-              :data="props.row.items"
-              :span-method="objectSpanMethod"
-            >
-              <el-table-column width="88"></el-table-column>
-              <el-table-column width="324">
-                <template #default="scope">
-                  <div class="goods-item sa-flex">
-                    <sa-image :url="scope.row.goods_image" size="58" />
-                    <div class="right sa-m-l-12">
-                      <div class="goods-title sa-flex sa-table-line-1">
-                        <span class="goods-id" @click="onOpenGoodsEdit(scope.row.goods_id)"
-                          >#{{ scope.row.goods_id }}</span
-                        >
-                        {{ scope.row.goods_title }}
-                      </div>
-                      <div class="goods-sku-text sa-m-t-10">
-                        <span>商品编码:{{ scope.row.goods_sku_price_out_code }}</span>
-                      </div>
-                      <div class="sa-flex">
-                        <span class="goods-price sa-m-r-8">¥{{ scope.row.goods_price }}</span>
-                        <span class="goods-num sa-m-r-12">x{{ scope.row.goods_num }}</span>
-                        <index-status type="dispatch" :item="scope.row" />
-                        <el-button
-                          class="sa-m-l-8"
-                          v-auth="'shop.admin.order.order.dispatch'"
-                          type="primary"
-                          size="small"
-                          plain
-                          @click="onOpenManualOrder(scope.row.id)"
-                          >手动下单</el-button
-                        >
-                        <el-button
-                          class="sa-m-l-8"
-                          v-auth="'shop.admin.order.order.dispatch'"
-                          type="primary"
-                          size="small"
-                          plain
-                          v-if="
-                            scope.row.order_source === 'zkh' && scope.row.status_code === 'nosend'
-                          "
-                          @click="onOpenZkhSubmitConfirm(scope.row.id)"
-                          >震坤行下单</el-button
-                        >
-                      </div>
-                      <div class="sa-m-t-8 sa-font-24">
-                        <div v-if="scope.row.out_order_id"
-                          >外部订单号:{{ scope.row.out_order_id }}</div
-                        >
-                        <div v-if="scope.row.out_order_item_id" class="sa-m-t-5 sa-m-b-5"
-                          >外部子订单号:{{ scope.row.out_order_item_id }}</div
-                        >
-                        <div v-if="scope.row.order_source"
-                          >下单渠道:{{ formatOrderSource(scope.row.order_source) }}</div
-                        >
-                      </div>
-                    </div>
-                  </div>
-                </template>
-              </el-table-column>
-              <el-table-column min-width="140" align="center">
-                <template #default="scope">
-                  <index-status type="aftersale" :item="scope.row" @updateList="getData" />
-                </template>
-              </el-table-column>
-              <el-table-column min-width="92" align="center">
-                <template #default>
-                  <sa-user-profile :user="props.row.user" :id="props.row.user_id" mode="col" />
-                </template>
-              </el-table-column>
-              <el-table-column min-width="168" align="center">
-                <template #default>
-                  <div
-                    class="address-content sa-flex sa-flex-col sa-col-top"
-                    v-if="props.row.address"
-                  >
-                    <div class="consignee sa-m-b-6">
-                      <span>{{ props.row.address.consignee }}</span>
-                      <span class="sa-m-l-8">{{ props.row.address.mobile }}</span>
-                    </div>
-                    <div class="address-name sa-m-b-6">
-                      {{ props.row.address.province_name }}/{{ props.row.address.city_name }}/{{
-                        props.row.address.district_name
-                      }}/{{ props.row.address.town_name || '暂无' }}
-                    </div>
-                    <div class="address sa-table-line-1">
-                      {{ props.row.address.address }}
-                    </div>
-                  </div>
-                  <div v-else>{{ props.row.address_id }}</div>
-                </template>
-              </el-table-column>
-              <el-table-column prop="dispatch_type_text" min-width="168" align="center">
-              </el-table-column>
-              <el-table-column min-width="160" align="center">
-                <template #default="scope">
-                  <div class="sa-flex-col sa-col-center">
-                    <div
-                      v-if="scope.row.activity_type"
-                      class="activity-type-text sa-m-r-4"
-                      :class="scope.row.activity_type"
-                      >{{ scope.row.activity_type_text }}</div
-                    >
-                    <div class="sa-flex">
-                      ¥{{ scope.row.pay_fee }}
-                      <index-status
-                        v-if="scope.row.refund_status != 0"
-                        type="refund"
-                        :item="scope.row"
-                      />
-                    </div>
-                    <template v-if="Number(scope.row.discount_fee)">
-                      <el-popover
-                        popper-class="discount-fee-popover"
-                        placement="top"
-                        trigger="hover"
-                      >
-                        <div class="sa-flex">
-                          <div
-                            class="promo-type-text"
-                            v-for="text in scope.row.promo_types_text"
-                            :key="text"
-                            >{{ text }}</div
-                          >
-                        </div>
-                        <template #reference>
-                          <div>
-                            <div v-if="scope.row.promo_types_text?.length > 0" class="discount-fee">
-                              (优惠: ¥{{ scope.row.discount_fee }})
-                            </div>
-                          </div>
-                        </template>
-                      </el-popover>
-                      <div v-if="scope.row.promo_types_text?.length == 0" class="discount-fee">
-                        (优惠: -¥{{ scope.row.discount_fee }})
-                      </div>
-                    </template>
-                  </div>
-                </template>
-              </el-table-column>
-              <el-table-column min-width="240">
-                <template #default>
-                  <div class="sa-flex">
-                    <el-popover
-                      v-model:visible="refundPopover[props.$index]"
-                      popper-class="refund-popover"
-                      placement="top-start"
-                      :width="204"
-                      trigger="click"
-                    >
-                      <div class="title sa-flex">
-                        <el-icon><QuestionFilled /></el-icon>
-                        您同意用户进行申请退款吗?
-                      </div>
-                      <div class="sa-flex sa-row-right">
-                        <el-button
-                          v-auth="'shop.admin.order.order.applyrefundrefuse'"
-                          class="is-link"
-                          size="small"
-                          type="info"
-                          @click="onApplyRefundRefuse(props.row.id, props.$index)"
-                          >拒绝</el-button
-                        >
-                        <el-button
-                          v-auth="'shop.admin.order.order.fullrefund'"
-                          size="small"
-                          type="danger"
-                          @click="onOpenRefund(props.row, props.$index)"
-                          >同意</el-button
-                        >
-                      </div>
-                      <template #reference>
-                        <div class="apply-refund-wrap">
-                          <el-button
-                            v-if="props.row.btns?.includes('apply_refund_oper')"
-                            class="apply-refund-button sa-m-r-12"
-                            type="danger"
-                            plain
-                          >
-                            用户申请退款
-                            <el-icon><ArrowRight /></el-icon>
-                          </el-button>
-                        </div>
-                      </template>
-                    </el-popover>
-                    <el-popover
-                      v-model:visible="confirmPopover[props.$index]"
-                      popper-class="confirm-popover sa-popper"
-                      placement="top-start"
-                      :width="204"
-                      trigger="click"
-                    >
-                      <div class="title sa-flex">
-                        <el-icon>
-                          <question-filled />
-                        </el-icon>
-                        确认用户是否收货?
-                      </div>
-                      <div class="sa-flex sa-row-right">
-                        <el-button
-                          v-auth="'shop.admin.order.order.offlinerefuse'"
-                          size="small"
-                          type="danger"
-                          link
-                          @click="onOfflineRefuse(props.row.id, props.$index)"
-                          >用户拒收
-                        </el-button>
-                        <el-button
-                          v-auth="'shop.admin.order.order.offlineconfirm'"
-                          size="small"
-                          type="primary"
-                          @click="onOfflineConfirm(props.row.id, props.$index)"
-                          >确认收货
-                        </el-button>
-                      </div>
-                      <template #reference>
-                        <div class="apply-refund-wrap">
-                          <el-button
-                            v-if="props.row.btns?.includes('confirm')"
-                            class="send-button sa-m-r-12"
-                            type="primary"
-                            plain
-                          >
-                            确认收货
-                            <el-icon>
-                              <arrow-right />
-                            </el-icon>
-                          </el-button>
-                        </div>
-                      </template>
-                    </el-popover>
-
-                    <el-button
-                      v-if="props.row.btns?.includes('send')"
-                      v-auth="'shop.admin.order.order.dispatch'"
-                      class="send-button"
-                      type="primary"
-                      plain
-                      @click="onSend(props.row)"
-                    >
-                      立即发货
-                      <el-icon><ArrowRight /></el-icon>
-                    </el-button>
-                    <el-button
-                      v-auth="'shop.admin.order.order.detail'"
-                      class="is-link"
-                      type="primary"
-                      @click="detailRow(props.row.id)"
-                      >详情</el-button
-                    >
-                    <el-button
-                      v-auth="'shop.admin.order.order.action'"
-                      class="is-link"
-                      type="primary"
-                      @click="onOpenAction(props.row.id)"
-                      >日志</el-button
-                    >
-                  </div>
-                </template>
-              </el-table-column>
-            </el-table>
-          </template>
-        </el-table-column>
-        <el-table-column type="selection" width="40" />
-        <el-table-column label="商品信息" width="304">
-          <template #default="scope">
-            <div class="order-wrap sa-flex">
-              <div class="id">#{{ scope.row.id }}</div>
-              <div class="order-sn sa-flex">
-                订单号:{{ scope.row.order_sn }}
-                <sa-svg
-                  class="copy sa-m-l-4 cursor-pointer"
-                  name="sa-copy"
-                  @click="useClip(scope.row.order_sn)"
-                ></sa-svg>
-              </div>
-              <div class="create-time"> 下单时间:{{ scope.row.create_time }} </div>
-              <div class="pay-types-text">{{ scope.row.pay_types_text?.join(',') }}</div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="售后状态" min-width="140" align="center"></el-table-column>
-        <el-table-column label="下单用户" min-width="92" align="center"></el-table-column>
-        <el-table-column label="收货地址" min-width="168" align="center"></el-table-column>
-        <el-table-column label="配送方式" min-width="168" align="center"></el-table-column>
-        <el-table-column label="支付信息" min-width="160" align="center">
-          <template #default="scope">
-            <div class="sa-flex sa-row-center">
-              <el-popover popper-class="pay-fee-popover sa-popper" placement="top" trigger="hover">
-                <div>
-                  <div class="pay-fee-item">
-                    <div class="label">商品总价:</div>
-                    <div class="content">¥{{ scope.row.goods_amount }}</div>
-                  </div>
-                  <div class="pay-fee-item">
-                    <div class="label">运费价格:</div>
-                    <div class="content"> ¥{{ scope.row.dispatch_amount }} </div>
-                  </div>
-                  <div
-                    class="pay-fee-item pay-fee-item-discount"
-                    v-if="scope.row.ext && (scope.row.ext.promo_infos || scope.row.coupon_id)"
-                  >
-                    <div class="label">活动优惠:</div>
-                    <div class="content">
-                      <div
-                        class="pay-fee-item"
-                        v-if="Number(scope.row.ext.promo_discounts.full_reduce) > 0"
-                      >
-                        <div class="label sa-m-r-8">满减</div>
-                        <div class="content">
-                          -¥{{ scope.row.ext.promo_discounts.full_reduce }}
-                        </div>
-                      </div>
-                      <div
-                        class="pay-fee-item"
-                        v-if="Number(scope.row.ext.promo_discounts.full_discount) > 0"
-                      >
-                        <div class="label sa-m-r-8">满折</div>
-                        <div class="content">
-                          -¥{{ scope.row.ext.promo_discounts.full_discount }}
-                        </div>
-                      </div>
-                      <div
-                        class="pay-fee-item"
-                        v-if="Number(scope.row.ext.promo_discounts.full_gift) > 0"
-                      >
-                        <div class="label sa-m-r-8">满赠</div>
-                        <div class="content"></div>
-                      </div>
-                      <div
-                        class="pay-fee-item"
-                        v-if="Number(scope.row.ext.promo_discounts.free_shipping) > 0"
-                      >
-                        <div class="label sa-m-r-8">满包邮</div>
-                        <div class="content">
-                          -¥{{ scope.row.ext.promo_discounts.free_shipping }}
-                        </div>
-                      </div>
-                      <div v-if="scope.row.coupon_id" class="pay-fee-item">
-                        <div class="label sa-m-r-8">优惠券</div>
-                        <div class="content"> -¥{{ scope.row.coupon_discount_fee }} </div>
-                      </div>
-                    </div>
-                  </div>
-                  <div class="pay-fee-item">
-                    <div class="label"
-                      >{{
-                        ['paid', 'completed'].includes(scope.row.status) ? '实付金额' : '应付金额'
-                      }}:</div
-                    >
-                    <div class="content sa-flex">
-                      ¥{{ scope.row.pay_fee }}
-                      <s
-                        v-if="scope.row.pay_fee != scope.row.original_pay_fee"
-                        class="original-pay-fee sa-m-l-4"
-                      >
-                        {{ scope.row.original_pay_fee }}
-                      </s>
-                    </div>
-                  </div>
-                </div>
-                <template #reference>
-                  <div class="pay-fee-reference">¥{{ scope.row.pay_fee }}</div>
-                </template>
-              </el-popover>
-              <el-button
-                v-if="scope.row?.btns.includes('change_fee')"
-                class="is-link"
-                type="primary"
-                @click="onChangeFee(scope.row)"
-                >改价</el-button
-              >
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" min-width="240">
-          <template #default="scope">
-            <index-status :order="scope.row" type="status_code" />
-          </template>
-        </el-table-column>
-      </el-table>
-    </el-main>
-    <sa-view-bar>
-      <template #left>
-        <sa-batch-handle
-          :batchHandleTools="batchHandleTools"
-          :selectedLeng="table.selected.length"
-          @batchHandle="batchHandle"
-        ></sa-batch-handle>
-      </template>
-      <template #right>
-        <sa-pagination :pageData="pageData" @updateFn="getData" />
-      </template>
-    </sa-view-bar>
-  </el-container>
-</template>
-<script>
-  export default {
-    name: 'shop.admin.order.order',
-  };
-</script>
-<script setup>
-  import { computed, onMounted, reactive, ref } from 'vue';
-  import { ElMessage, ElMessageBox } from 'element-plus';
-  import { api } from '../order.service';
-  import { useModal, usePagination } from '@/sheep/hooks';
-  import { useSearch } from '@/sheep/components/sa-table/sa-search/useSearch';
-  import { composeFilter } from '@/sheep/utils';
-  import useClip from '@/sheep/utils/clipboard.js';
-  import StatusButton from './components/status.vue';
-  import IndexStatus from './components/index-status.vue';
-  import OrderDispatch from './dispatch.vue';
-  import OrderRefund from './refund.vue';
-  import OrderFee from './fee.vue';
-  import { activityStatusStyle } from './status.js';
-  import OrderBatchDispatch from './batchDispatch.vue';
-  import OrderDetail from './detail.vue';
-  import OrderAction from './action.vue';
-  import GoodsEdit from '@/app/shop/admin/goods/goods/edit.vue';
-  import { useRoute } from 'vue-router';
-  import { cloneDeep } from 'lodash';
-  import ManualOrder from './manualOrder.vue';
-  const route = useRoute();
-
-  // getType
-  const statusList = reactive({});
-  async function getType() {
-    const { data } = await api.order.getType();
-    for (var key in data) {
-      if (filterParams.tools[key] && key != 'status') {
-        filterParams.tools[key].options.data = data[key];
-      } else if (key == 'pay_type') {
-        filterParams.tools['pay.pay_type'].options.data = data[key];
-      } else if (key == 'status') {
-        data[key].forEach((s) => {
-          statusList[s.type] = { label: s.name, num: 0 };
-        });
-      }
-    }
-  }
-
-  const filterParams = reactive({
-    tools: {
-      status: { label: '订单状态', value: route.query.status || 'all' },
-      order: {
-        type: 'tinputprepend',
-        label: '请输入查询内容',
-        field: 'order',
-        order: {
-          field: 'id',
-          value: '',
-        },
-        options: [
-          {
-            label: '订单ID',
-            value: 'id',
-          },
-          {
-            label: '订单编号',
-            value: 'order_sn',
-          },
-          {
-            label: '售后单号',
-            value: 'aftersale.aftersale_sn',
-          },
-          {
-            label: '支付单号',
-            value: 'pay.pay_sn',
-          },
-          {
-            label: '交易流水号',
-            value: 'pay.transaction_id',
-          },
-        ],
-      },
-      user: {
-        type: 'tinputprepend',
-        label: '请输入查询内容',
-        field: 'user',
-        user: {
-          field: 'user_id',
-          value: '',
-        },
-        options: [
-          {
-            label: '用户ID',
-            value: 'user_id',
-          },
-          {
-            label: '用户昵称',
-            value: 'user.nickname',
-          },
-          {
-            label: '用户手机号',
-            value: 'user.mobile',
-          },
-          {
-            label: '收货人',
-            value: 'address.consignee',
-          },
-          {
-            label: '收货人手机号',
-            value: 'address.mobile',
-          },
-        ],
-      },
-      type: {
-        type: 'tselect',
-        label: '订单类型',
-        field: 'type',
-        value: '',
-        options: {
-          data: [],
-          props: {
-            label: 'name',
-            value: 'type',
-          },
-        },
-      },
-      platform: {
-        type: 'tselect',
-        label: '订单来源',
-        field: 'platform',
-        value: '',
-        options: {
-          data: [],
-          props: {
-            label: 'name',
-            value: 'type',
-          },
-        },
-      },
-      'pay.pay_type': {
-        type: 'tselect',
-        label: '支付方式',
-        field: 'pay.pay_type',
-        value: '',
-        options: {
-          data: [],
-          props: {
-            label: 'name',
-            value: 'type',
-          },
-        },
-      },
-      activity_type: {
-        type: 'tselect',
-        label: '活动类型',
-        field: 'activity_type',
-        value: '',
-        options: {
-          data: [],
-          props: {
-            label: 'name',
-            value: 'type',
-          },
-        },
-      },
-      promo_types: {
-        type: 'tselect',
-        label: '促销类型',
-        field: 'promo_types',
-        value: '',
-        options: {
-          data: [],
-          props: {
-            label: 'name',
-            value: 'type',
-          },
-        },
-      },
-      'item.goods_title': {
-        type: 'tinput',
-        label: '商品名称',
-        field: 'item.goods_title',
-        value: '',
-      },
-      create_time: {
-        type: 'tdatetimerange',
-        label: '下单时间',
-        field: 'create_time',
-        value: route.query.create_time || [],
-      },
-    },
-    data: {
-      status: route.query.status || 'all',
-      order: { field: 'id', value: '' },
-      user: { field: 'user_id', value: '' },
-      type: '',
-      platform: '',
-      'pay.pay_type': '',
-      activity_type: '',
-      promo_types: '',
-      'item.goods_title': '',
-      create_time: route.query.create_time || [],
-    },
-    conditionLabel: {},
-  });
-  const { openFilter, deleteFilter } = useSearch({ filterParams, getData });
-
-  const loading = ref(true);
-
-  // 表格
-  const table = reactive({
-    data: [],
-    order: '',
-    sort: '',
-    selected: [],
-  });
-
-  const { pageData } = usePagination();
-
-  // 下单渠道格式化函数
-  const formatOrderSource = (source) => {
-    const sourceMap = {
-      self: '自下单',
-      linkedmall: 'linkedmall',
-      zkh: '震坤行',
-    };
-    return sourceMap[source] || source;
-  };
-
-  // 获取数据
-  async function getData(page) {
-    loading.value = true;
-    if (page) pageData.page = page;
-    let tempSearch = cloneDeep(filterParams.data);
-    let search = composeFilter(tempSearch, {
-      order_sn: 'like',
-      'aftersale.aftersale_sn': 'like',
-      'pay.pay_sn': 'like',
-      'pay.transaction_id': 'like',
-      'user.nickname': 'like',
-      'user.mobile': 'like',
-      'address.consignee': 'like',
-      'address.mobile': 'like',
-      promo_types: 'find_in_set',
-      'item.goods_title': 'like',
-      create_time: 'range',
-    });
-    const { error, data } = await api.order.list({
-      page: pageData.page,
-      list_rows: pageData.list_rows,
-      ...search,
-    });
-    if (error === 0) {
-      table.data = data.orders.data;
-      pageData.page = data.orders.current_page;
-      pageData.list_rows = data.orders.per_page;
-      pageData.total = data.orders.total;
-      for (var key in statusList) {
-        statusList[key].num = data[key];
-      }
-    }
-    loading.value = false;
-  }
-
-  function onChangeStatus(tab) {
-    table.data = [];
-    getData(1);
-  }
-
-  function handleSelectionChange(val) {
-    table.selected = val;
-  }
-
-  // 导出订单/导出发货单
-  const exportLoading = ref(false);
-  async function onExport(type) {
-    exportLoading.value = true;
-    let tempSearch = cloneDeep(filterParams.data);
-    let search = composeFilter(tempSearch, {
-      order_sn: 'like',
-      'aftersale.aftersale_sn': 'like',
-      'pay.pay_sn': 'like',
-      'pay.transaction_id': 'like',
-      'user.nickname': 'like',
-      'user.mobile': 'like',
-      'address.consignee': 'like',
-      'address.mobile': 'like',
-      promo_types: 'find_in_set',
-      'item.goods_title': 'like',
-      create_time: 'range',
-    });
-    await api.order.export(type, { search: search.search });
-    exportLoading.value = false;
-  }
-
-  const batchHandleTools = [
-    {
-      type: 'all',
-      label: '批量发货',
-      auth: 'shop.admin.order.order.dispatch',
-      buttonType: 'default',
-    },
-  ];
-  function batchHandle() {
-    let order_ids = [];
-    table.selected.forEach((s) => {
-      order_ids.push(s.id);
-    });
-    useModal(
-      OrderBatchDispatch,
-      {
-        title: '批量发货',
-        order_ids,
-      },
-      {
-        confirm: () => {
-          getData();
-        },
-      },
-    );
-  }
-
-  function detailRow(id) {
-    useModal(
-      OrderDetail,
-      {
-        title: '订单详情',
-        type: 'detail',
-        id,
-      },
-      {
-        confirm: () => {
-          getData();
-        },
-        close: () => {
-          getData();
-        },
-      },
-    );
-  }
-
-  function onOpenAction(id) {
-    useModal(OrderAction, {
-      title: '日志',
-      id,
-    });
-  }
-
-  const arraySpanMethod = ({ row, column, rowIndex, columnIndex }) => {
-    if (columnIndex == 2) {
-      return [1, 4];
-    } else if (columnIndex == 3 || columnIndex == 4 || columnIndex == 5) {
-      return [0, 0];
-    }
-  };
-
-  const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
-    if (columnIndex == 0) {
-      return [0, 0];
-    }
-    if (columnIndex == 1) {
-      return [1, 2];
-    }
-    if (columnIndex == 3 || columnIndex == 4 || columnIndex == 7) {
-      if (rowIndex == 0) {
-        return {
-          rowspan: 200,
-          colspan: 1,
-        };
-      } else {
-        return {
-          rowspan: 0,
-          colspan: 0,
-        };
-      }
-    }
-  };
-
-  function onOpenGoodsEdit(id) {
-    useModal(GoodsEdit, {
-      title: '商品',
-      type: 'edit',
-      id: id,
-    });
-  }
-
-  function onSend(row) {
-    useModal(
-      OrderDispatch,
-      {
-        title: '订单发货',
-        data: row,
-      },
-      {
-        confirm: () => {
-          getData();
-        },
-      },
-    );
-  }
-
-  function onChangeFee(row) {
-    useModal(
-      OrderFee,
-      {
-        title: '改价',
-        class: 'fee-dialog',
-        id: row.id,
-        pay_fee: row.pay_fee,
-      },
-      {
-        confirm: () => {
-          getData();
-        },
-      },
-    );
-  }
-
-  function onOpenRefund(row, index) {
-    refundPopover[index] = false;
-    useModal(
-      OrderRefund,
-      {
-        title: '全部退款',
-        class: 'refund-dialog',
-        type: 'order.fullRefund',
-        data: {
-          id: row.id,
-          money: row.pay_fee,
-        },
-      },
-      {
-        confirm: () => {
-          getData();
-        },
-      },
-    );
-  }
-
-  const refundPopover = reactive({});
-  async function onApplyRefundRefuse(id, index) {
-    refundPopover[index] = false;
-    const { error } = await api.order.applyRefundRefuse(id);
-    if (error == 0) {
-      getData();
-    }
-  }
-
-  function onOpenZkhSubmitConfirm(order_item_id) {
-    ElMessageBox.confirm('确定要下单到震坤行吗?', '提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-    })
-      .then(async () => {
-        const { error } = await api.order.zkhOrderSubmit({ order_item_id });
-        if (error == 0) {
-          ElMessage.success('下单成功');
-          getData();
-        }
-      })
-      .catch(() => {
-        // 用户取消操作,不做任何处理
-      });
-  }
-
-  const confirmPopover = reactive({});
-  async function onOfflineRefuse(id, index) {
-    confirmPopover[index] = false;
-    const { error } = await api.order.offlineRefuse(id);
-    if (error == 0) {
-      getData();
-    }
-  }
-  async function onOfflineConfirm(id, index) {
-    confirmPopover[index] = false;
-    const { error } = await api.order.offlineConfirm(id);
-    if (error == 0) {
-      getData();
-    }
-  }
-
-  function onOpenManualOrder(id) {
-    useModal(
-      ManualOrder,
-      {
-        title: '手动下单',
-        id: id,
-      },
-      {
-        confirm: () => {
-          getData();
-        },
-      },
-    );
-  }
-
-  onMounted(async () => {
-    await getType();
-    getData();
-  });
-</script>
-<style lang="scss" scoped>
-  .order-page {
-    .el-main {
-      :deep() {
-        .el-table {
-          --el-table-row-hover-bg-color: var(--sa-background-hex-hover);
-          .el-table__row {
-            background: var(--sa-background-hex-hover);
-          }
-          .el-table__header-wrapper {
-            margin-bottom: 4px;
-          }
-          .el-table__expanded-cell {
-            padding: 0;
-            background: var(--sa-background-assist);
-          }
-        }
-        .sa-expand-table {
-          --el-table-row-hover-bg-color: var(--el-fill-color-light);
-          .el-table__header-wrapper {
-            margin-bottom: 0;
-          }
-          .el-table__row {
-            background: var(--el-table-tr-bg-color);
-          }
-          .el-table__body tr:hover > td.el-table__cell {
-            background-color: var(--sa-background-assist) !important;
-          }
-          .el-table__body tr.hover-row.current-row > td.el-table__cell,
-          .el-table__body tr.hover-row.el-table__row--striped.current-row > td.el-table__cell,
-          .el-table__body tr.hover-row.el-table__row--striped > td.el-table__cell,
-          .el-table__body tr.hover-row > td.el-table__cell {
-            background-color: var(--sa-background-assist) !important;
-          }
-        }
-      }
-
-      .sa-table {
-        .apply-refund {
-          font-size: 12px;
-          font-weight: 400;
-          color: #999;
-        }
-        .order-wrap {
-          position: relative;
-          line-height: 14px;
-          font-size: 12px;
-          font-weight: 400;
-          color: var(--sa-font);
-          & > div {
-            margin-right: 24px;
-          }
-          .id {
-            min-width: 80px;
-            color: var(--sa-subtitle);
-          }
-          .order-sn {
-            min-width: 228px;
-            height: 14px;
-            font-size: 12px;
-            color: var(--sa-subtitle);
-            .copy {
-              width: 12px !important;
-              height: 12px !important;
-            }
-          }
-          .create-time {
-            line-height: 14px;
-            font-size: 12px;
-            color: var(--sa-subtfont);
-          }
-          .platform-text {
-            min-width: 116px;
-          }
-          .pay-types-text {
-            margin-right: 0;
-          }
-        }
-        .address-content {
-          width: 100%;
-          display: inline-flex;
-          .consignee {
-            line-height: 14px;
-            color: var(--sa-font);
-            font-size: 12px;
-            text-align: left;
-          }
-          .address-name {
-            line-height: 16px;
-            font-size: 14px;
-            color: var(--sa-subtitle);
-            text-align: left;
-          }
-          .address {
-            height: 14px;
-            line-height: 14px;
-            font-size: 12px;
-            color: var(--sa-subfont);
-          }
-        }
-      }
-      .sa-expand-table {
-        :deep() {
-          .el-table__header-wrapper {
-            display: none;
-          }
-        }
-      }
-    }
-  }
-  .refund-popover,
-  .confirm-popover {
-    .title {
-      line-height: 20px;
-      font-size: 12px;
-      font-weight: 400;
-      color: var(--sa-font);
-      margin-bottom: 16px;
-      .el-icon {
-        font-size: 14px;
-        color: #faad14;
-        margin-right: 8px;
-      }
-    }
-    .tip {
-      font-size: 12px;
-      font-weight: 400;
-      color: var(--sa-subfont);
-    }
-  }
-  .pay-fee-reference {
-    width: fit-content;
-    color: var(--t-color-primary);
-    border-bottom: 1px dashed var(--t-color-primary);
-    cursor: pointer;
-  }
-  .pay-fee-popover {
-    .pay-fee-item {
-      line-height: 16px;
-      font-size: 12px;
-      display: flex;
-      align-items: center;
-      margin-bottom: 8px;
-      &.pay-fee-item-discount {
-        align-items: flex-start;
-      }
-      &:last-child {
-        margin-bottom: 0;
-      }
-      .label {
-        flex-shrink: 0;
-        color: var(--sa-subfont);
-      }
-      .content {
-        color: var(--sa-subtitle);
-      }
-      .original-pay-fee {
-        color: #999;
-      }
-    }
-  }
-
-  .goods-item {
-    .right {
-      flex: 1;
-      line-height: 14px;
-      font-size: 12px;
-      font-weight: 400;
-      color: var(--sa-font);
-      .goods-title {
-        height: 14px;
-        line-height: 14px;
-        font-size: 12px;
-        font-weight: 500;
-        margin-bottom: 4px;
-        .goods-id {
-          color: var(--t-color-primary);
-          cursor: pointer;
-        }
-      }
-      .goods-sku-text {
-        height: 14px;
-        line-height: 14px;
-        margin-bottom: 10px;
-      }
-      .goods-price {
-      }
-      .goods-num {
-      }
-    }
-  }
-
-  .activity-type-text {
-    width: fit-content;
-    height: 20px;
-    padding: 0 5px;
-    border-radius: 2px;
-    font-weight: 400;
-    font-size: 12px;
-    line-height: 20px;
-    text-align: center;
-    &.groupon,
-    &.groupon_ladder {
-      background: var(--t-bg-active);
-      color: var(--t-color-primary);
-    }
-    &.seckill {
-      background: rgba(255, 77, 79, 0.16);
-      color: #ff4d4f;
-    }
-  }
-
-  .discount-fee {
-    line-height: 14px;
-    font-size: 12px;
-    font-weight: 400;
-    color: var(--sa-font);
-    text-decoration-line: underline;
-  }
-  .discount-fee-popover {
-    .promo-type-text {
-      width: fit-content;
-      height: 20px;
-      padding: 0 8px;
-      background: rgba(250, 173, 20, 0.16);
-      border-radius: 10px;
-      font-weight: 400;
-      font-size: 12px;
-      color: #faad14;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-  }
-  .apply-refund-wrap {
-    width: fit-content;
-  }
-  .apply-refund-button {
-    height: 26px;
-    border-radius: 13px;
-    border: none;
-    padding: 0 8px 0 12px;
-    --el-button-bg-color: rgba(255, 77, 79, 0.16);
-  }
-  .send-button {
-    height: 26px;
-    border-radius: 13px;
-    border: none;
-    padding: 0 8px 0 12px;
-    --el-button-bg-color: var(--t-bg-active);
-  }
-</style>

+ 155 - 0
src/sheep/mock/data/order-detail.js

@@ -0,0 +1,155 @@
+/**
+ * 订单详情相关 Mock 数据
+ */
+
+// 订单详情数据
+export const orderDetailData = {
+  id: 1,
+  order_sn: '2025050612300120',
+  status: 'completed',
+  status_text: '已完成',
+  status_desc: '订单已完成,感谢您的购买',
+  
+  // 订单轨迹
+  status_steps: [
+    {
+      status: 'created',
+      status_text: '提交订单',
+      time: '2025/05/06 12:30:00',
+      completed: true
+    },
+    {
+      status: 'paid',
+      status_text: '付款',
+      time: '2025/05/06 12:35:00',
+      completed: true
+    },
+    {
+      status: 'shipped',
+      status_text: '发货',
+      time: '2025/05/06 13:30:00',
+      completed: true
+    },
+    {
+      status: 'received',
+      status_text: '平台发货',
+      time: '2025/05/06 14:30:00',
+      completed: true
+    },
+    {
+      status: 'completed',
+      status_text: '用户收货',
+      time: '2025/05/10 12:30:00',
+      completed: true
+    }
+  ],
+
+  // 基本信息
+  total_amount: '1,000',
+  lottery_status: '门店中奖',
+  user_nickname: 'Aamer Khan',
+  payment_method: '数字钱包',
+  discount_amount: '0',
+  commission_amount: '10',
+  mobile: '88042251008',
+  pay_amount: '1,000',
+  actual_amount: '1,000',
+  
+  // 用户信息
+  user_id: 1001,
+  user: {
+    id: 1001,
+    nickname: 'Aamer Khan',
+    mobile: '88042251008',
+    avatar: 'https://mall-oss.trust-will.com/storage/default/20250701/7bf12c56300454532244dec2aff808eb.jpg?x-oss-process=image/resize,w_200,h_200'
+  },
+
+  // 商品信息
+  items: [
+    {
+      id: 1,
+      goods_id: 100,
+      goods_title: 'HOLUN Classic Aviator Polarized Sunglasses, Exclusive Eyewear Brand...',
+      goods_image: 'https://mall-oss.trust-will.com/storage/default/20250701/7bf12c56300454532244dec2aff808eb.jpg?x-oss-process=image/resize,w_200,h_200',
+      goods_sku_text: 'Black Grey',
+      goods_num: 1,
+      goods_price: '1,000'
+    }
+  ],
+
+  // 收货信息
+  address: {
+    consignee: 'Aamer Khan',
+    mobile: '88042251008',
+    address: '35/66 The Bliss Koolpunt Ville 15, 1304H-HWB San Kamphaeng, San Kamphaeng District, Chiang Mai 50130',
+    express_company: 'DHL',
+    express_no: 'DH47058JBM6938'
+  },
+
+  // 拼团信息
+  group_info: {
+    group_type: '5人团',
+    members: [
+      {
+        nickname: 'Aamer Khan',
+        mobile: '88042251008',
+        order_sn: '2025050612300120',
+        join_time: '2025/05/06 12:30:30',
+        pay_time: '2025/05/06 12:31:30',
+        commission: '৳10'
+      },
+      {
+        nickname: 'ABC-1',
+        mobile: '88042251008',
+        order_sn: '2025050612300120',
+        join_time: '2025/05/06 12:30:30',
+        pay_time: '2025/05/06 12:31:30',
+        commission: '৳10'
+      },
+      {
+        nickname: 'ABC-1',
+        mobile: '88042251008',
+        order_sn: '2025050612300120',
+        join_time: '2025/06/06 12:30:30',
+        pay_time: '2025/06/06 12:31:30',
+        commission: '৳10'
+      },
+      {
+        nickname: 'ABC-1',
+        mobile: '88042251008',
+        order_sn: '2025050612300120',
+        join_time: '2025/06/06 12:30:30',
+        pay_time: '2025/06/06 12:31:30',
+        commission: '৳10'
+      },
+      {
+        nickname: 'ABC-1',
+        mobile: '88042251008',
+        order_sn: '2025050612300120',
+        join_time: '2025/06/06 12:30:30',
+        pay_time: '2025/06/06 12:31:30',
+        commission: '৳10'
+      }
+    ]
+  },
+
+  // 时间信息
+  create_time: '2025/05/06 12:30:00',
+  pay_time: '2025/05/06 12:35:00',
+  dispatch_time: '2025/05/06 13:30:00',
+  receive_time: '2025/05/10 12:30:00',
+
+  // 操作按钮
+  btns: ['send', 'refund', 'contact_user', 'export_order', 'close_order'],
+
+  // 备注
+  memo: '',
+  remark: '请尽快发货,谢谢!'
+};
+
+// 订单详情响应
+export const orderDetailResponse = {
+  error: 0,
+  msg: '获取成功',
+  data: orderDetailData
+};

+ 45 - 8
src/sheep/mock/handlers/order.js

@@ -3,6 +3,7 @@
  */
 import { http, HttpResponse } from 'msw';
 import { generateOrderList, orderStats, orderTypeData } from '../data/order.js';
+import { orderDetailResponse } from '../data/order-detail.js';
 import { createSuccessResponse, createErrorResponse } from '../utils.js';
 
 // 订单列表接口
@@ -48,17 +49,12 @@ const orderTypeHandler = http.get(
 
 // 订单详情接口
 const orderDetailHandler = http.get(
-  'https://shop.trust-will.com/shop/admin/order/order/detail/:id',
+  'https://shop.trust-will.com/shop/admin/order/order/:id',
   ({ params }) => {
     const { id } = params;
-    const orders = generateOrderList(1, 100);
-    const order = orders.find((o) => o.id == id);
 
-    if (!order) {
-      return HttpResponse.json(createErrorResponse('订单不存在', 404));
-    }
-
-    return HttpResponse.json(createSuccessResponse(order));
+    // 返回订单详情数据
+    return HttpResponse.json(orderDetailResponse);
   },
 );
 
@@ -124,6 +120,44 @@ const orderActionHandler = http.get(
   },
 );
 
+// 联系用户接口
+const contactUserHandler = http.post(
+  'https://shop.trust-will.com/shop/admin/order/order/contact/:id',
+  async ({ params }) => {
+    const { id } = params;
+
+    // 模拟联系用户
+    return HttpResponse.json(createSuccessResponse({ message: '已发送联系信息' }));
+  },
+);
+
+// 导出订单接口
+const exportOrderHandler = http.get(
+  'https://shop.trust-will.com/shop/admin/order/order/export/:id',
+  async ({ params }) => {
+    const { id } = params;
+
+    // 模拟导出订单
+    return HttpResponse.json(
+      createSuccessResponse({
+        message: '导出成功',
+        download_url: 'https://mall-oss.trust-will.com/storage/default/order_export.xlsx',
+      }),
+    );
+  },
+);
+
+// 关闭订单接口
+const closeOrderHandler = http.put(
+  'https://shop.trust-will.com/shop/admin/order/order/close/:id',
+  async ({ params }) => {
+    const { id } = params;
+
+    // 模拟关闭订单
+    return HttpResponse.json(createSuccessResponse({ message: '订单已关闭' }));
+  },
+);
+
 // 导出订单处理器
 export const orderHandlers = [
   orderListHandler,
@@ -133,4 +167,7 @@ export const orderHandlers = [
   orderRefundHandler,
   orderChangeFeeHandler,
   orderActionHandler,
+  contactUserHandler,
+  exportOrderHandler,
+  closeOrderHandler,
 ];