浏览代码

订单列表奖惩、备注完成

Huanyi 1 月之前
父节点
当前提交
a46f251b45

+ 8 - 0
src/api/order/subOrder/index.ts

@@ -38,3 +38,11 @@ export const cancelSubOrder = (data: { orderId: string | number; }) => {
         data
     });
 };
+
+export const remarkSubOrder = (data: { orderId: string | number; remark: string; }) => {
+    return request({
+        url: '/order/subOrder/remark',
+        method: 'put',
+        data
+    });
+};

+ 1 - 0
src/api/order/subOrder/types.ts

@@ -33,4 +33,5 @@ export interface SubOrderVO {
     splitType?: string;
     detail?: any;
     serviceTime?: string;
+    remark?: string;
 }

+ 81 - 79
src/views/order/orderList/components/RewardDialog.vue

@@ -1,99 +1,101 @@
 <template>
-    <el-dialog v-model="dialogVisible" title="奖惩操作" width="500px">
-        <div v-if="order" style="padding: 0 10px;">
-            <div style="margin-bottom: 20px; font-size: 14px; color: #606266; line-height: 1.6; background: #fdf6ec; padding: 10px; border-radius: 4px;">
-                 <div>奖惩履约者:<span style="font-weight: bold; color: #303133;">{{ order.fulfillerName || '未指派' }}</span></div>
-                 <div style="font-size: 13px; margin-top: 4px;">订单号:{{ order.orderNo }}</div> 
-                 <div style="font-size: 13px; margin-top: 4px; display:flex; align-items:center; gap:6px;">
-                     服务类型:
-                     <el-tag :type="getTypeTag(order.type)" size="small">{{ getTypeName(order.type) }}</el-tag>
-                     <el-tag v-if="order.type === 'transport' && order.transportType === 'round'" size="small" effect="plain" type="warning">往返</el-tag>
-                     <el-tag v-if="order.splitType === 'pick'" size="small" effect="dark" color="#409eff" style="border:none; color:white;">接</el-tag>
-                     <el-tag v-if="order.splitType === 'drop'" size="small" effect="dark" color="#67c23a" style="border:none; color:white;">送</el-tag>
-                     <el-tag v-if="order.type === 'transport' && order.transportType === 'pick' && !order.splitType" size="small" effect="plain">单程接</el-tag>
-                     <el-tag v-if="order.type === 'transport' && order.transportType === 'drop' && !order.splitType" size="small" effect="plain" type="success">单程送</el-tag>
-                 </div>
-            </div>
-            
-            <el-form :model="rewardForm" label-width="80px">
-                <el-form-item label="操作类型">
-                    <el-radio-group v-model="rewardForm.type">
-                        <el-radio label="reward">奖励 (增加)</el-radio>
-                        <el-radio label="punish">惩罚 (扣除)</el-radio>
-                    </el-radio-group>
-                </el-form-item>
-                <el-form-item label="调整项目">
-                    <el-radio-group v-model="rewardForm.item">
-                        <el-radio label="points">积分</el-radio>
-                        <el-radio label="amount">金额 (元)</el-radio>
-                    </el-radio-group>
-                </el-form-item>
-                <el-form-item label="数额" required>
-                    <el-input-number v-model="rewardForm.value" :min="1" :step="10" />
-                </el-form-item>
-                <el-form-item label="原因备注" required>
-                    <el-input 
-                        v-model="rewardForm.reason" 
-                        type="textarea" 
-                        :rows="3" 
-                        placeholder="请输入奖惩原因..." 
-                    />
-                </el-form-item>
-            </el-form>
+  <el-dialog v-model="dialogVisible" title="奖惩操作" width="500px">
+    <div v-if="order" style="padding: 0 10px">
+      <div style="margin-bottom: 20px; font-size: 14px; color: #606266; line-height: 1.6; background: #fdf6ec; padding: 10px; border-radius: 4px">
+        <div>
+          奖惩履约者:<span style="font-weight: bold; color: #303133">{{ order.fulfillerName || '未指派' }}</span>
         </div>
-        <template #footer>
-            <el-button @click="dialogVisible = false">取消</el-button>
-            <el-button type="primary" @click="handleSubmit">确认执行</el-button>
-        </template>
-    </el-dialog>
+        <div style="font-size: 13px; margin-top: 4px">订单号:{{ order.orderNo }}</div>
+        <div style="font-size: 13px; margin-top: 4px; display: flex; align-items: center; gap: 6px">
+          服务类型:
+          <el-tag :type="getTypeTag(order.type)" size="small">{{ getTypeName(order.type) }}</el-tag>
+          <el-tag v-if="order.type === 'transport' && order.transportType === 'round'" size="small" effect="plain" type="warning">往返</el-tag>
+          <el-tag v-if="order.splitType === 'pick'" size="small" effect="dark" color="#409eff" style="border: none; color: white">接</el-tag>
+          <el-tag v-if="order.splitType === 'drop'" size="small" effect="dark" color="#67c23a" style="border: none; color: white">送</el-tag>
+          <el-tag v-if="order.type === 'transport' && order.transportType === 'pick' && !order.splitType" size="small" effect="plain">单程接</el-tag>
+          <el-tag v-if="order.type === 'transport' && order.transportType === 'drop' && !order.splitType" size="small" effect="plain" type="success"
+            >单程送</el-tag
+          >
+        </div>
+      </div>
+
+      <el-form :model="rewardForm" label-width="80px">
+        <el-form-item label="操作类型">
+          <el-radio-group v-model="rewardForm.type">
+            <el-radio label="reward">奖励 (增加)</el-radio>
+            <el-radio label="punish">惩罚 (扣除)</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="调整项目">
+          <el-radio-group v-model="rewardForm.item">
+            <el-radio label="points">积分</el-radio>
+            <el-radio label="balance">金额 (元)</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="数额" required>
+          <el-input-number v-model="rewardForm.value" :min="1" :step="10" />
+        </el-form-item>
+        <el-form-item label="原因备注" required>
+          <el-input v-model="rewardForm.reason" type="textarea" :rows="3" placeholder="请输入奖惩原因..." />
+        </el-form-item>
+      </el-form>
+    </div>
+    <template #footer>
+      <el-button @click="dialogVisible = false">取消</el-button>
+      <el-button type="primary" @click="handleSubmit">确认执行</el-button>
+    </template>
+  </el-dialog>
 </template>
 
 <script setup>
-import { reactive, computed, watch } from 'vue'
-import { ElMessage } from 'element-plus'
+import { reactive, computed, watch } from 'vue';
+import { ElMessage } from 'element-plus';
 
 const props = defineProps({
-    visible: Boolean,
-    order: Object
-})
-const emit = defineEmits(['update:visible', 'submit'])
+  visible: Boolean,
+  order: Object
+});
+const emit = defineEmits(['update:visible', 'submit']);
 
 const dialogVisible = computed({
-    get: () => props.visible,
-    set: (val) => emit('update:visible', val)
-})
+  get: () => props.visible,
+  set: (val) => emit('update:visible', val)
+});
 
 const rewardForm = reactive({
-    type: 'reward',
-    item: 'points',
-    value: 10,
-    reason: ''
-})
+  type: 'reward',
+  item: 'points',
+  value: 10,
+  reason: ''
+});
 
-watch(() => props.visible, (val) => {
+watch(
+  () => props.visible,
+  (val) => {
     if (val) {
-        rewardForm.type = 'reward'
-        rewardForm.item = 'points'
-        rewardForm.value = 10
-        rewardForm.reason = ''
+      rewardForm.type = 'reward';
+      rewardForm.item = 'points';
+      rewardForm.value = 10;
+      rewardForm.reason = '';
     }
-})
+  }
+);
 
 const getTypeTag = (type) => {
-    const map = { transport: '', feeding: 'warning', washing: 'success' }
-    return map[type]
-}
+  const map = { transport: '', feeding: 'warning', washing: 'success' };
+  return map[type];
+};
 const getTypeName = (type) => {
-    const map = { transport: '宠物接送', feeding: '上门喂遛', washing: '上门洗护' }
-    return map[type]
-}
+  const map = { transport: '宠物接送', feeding: '上门喂遛', washing: '上门洗护' };
+  return map[type];
+};
 
 const handleSubmit = () => {
-    if(!rewardForm.reason) {
-        ElMessage.warning('请输入奖惩原因')
-        return
-    }
-    emit('submit', rewardForm)
-    dialogVisible.value = false
-}
+  if (!rewardForm.reason) {
+    ElMessage.warning('请输入奖惩原因');
+    return;
+  }
+  emit('submit', rewardForm);
+  dialogVisible.value = false;
+};
 </script>

+ 87 - 48
src/views/order/orderList/index.vue

@@ -7,17 +7,11 @@
           <div class="right-panel">
             <el-radio-group v-model="filters.service" size="default" @change="handleSearch">
               <el-radio-button label="">全部类型</el-radio-button>
-              <el-radio-button v-for="item in serviceOptions" :key="item.id" :label="item.id">{{ item.name }}</el-radio-button>
+              <el-radio-button v-for="item in serviceOptions" :key="item.id" :label="item.id">{{ item.name
+              }}</el-radio-button>
             </el-radio-group>
-            <el-input
-              v-model="filters.content"
-              placeholder="订单号/商户/宠主/手机号"
-              class="search-input"
-              prefix-icon="Search"
-              clearable
-              @clear="handleSearch"
-              @keyup.enter="handleSearch"
-            />
+            <el-input v-model="filters.content" placeholder="订单号/商户/宠主/手机号" class="search-input" prefix-icon="Search"
+              clearable @clear="handleSearch" @keyup.enter="handleSearch" />
             <el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
           </div>
         </div>
@@ -33,17 +27,20 @@
         </el-tabs>
       </template>
 
-      <el-table :data="tableData" style="width: 100%" v-loading="loading" :header-cell-style="{ background: '#f5f7fa' }">
+      <el-table :data="tableData" style="width: 100%" v-loading="loading"
+        :header-cell-style="{ background: '#f5f7fa' }">
         <el-table-column prop="code" label="订单号" width="170" fixed="left" />
 
         <el-table-column label="服务类型" width="190">
           <template #default="{ row }">
             <div class="service-type-cell">
               <el-tag>{{ getServiceName(row.service) }}</el-tag>
-              <el-tag v-if="getServiceModeTag(row)" class="sub-tag" type="warning" effect="plain">{{ getServiceModeTag(row) }}</el-tag>
-              <el-tag v-if="getServiceOrderTypeTag(row)" class="sub-tag" :type="getServiceOrderTypeTag(row).type" effect="dark">{{
-                getServiceOrderTypeTag(row).label
-              }}</el-tag>
+              <el-tag v-if="getServiceModeTag(row)" class="sub-tag" type="warning" effect="plain">{{
+                getServiceModeTag(row) }}</el-tag>
+              <el-tag v-if="getServiceOrderTypeTag(row)" class="sub-tag" :type="getServiceOrderTypeTag(row).type"
+                effect="dark">{{
+                  getServiceOrderTypeTag(row).label
+                }}</el-tag>
             </div>
           </template>
         </el-table-column>
@@ -109,7 +106,7 @@
           <template #default="{ row }">
             <div v-if="row.fulfillerName" class="fulfiller-info">
               <span class="fulfiller-name">{{ row.fulfillerName }}</span>
-              <span class="fulfiller-fee" v-if="row.price !== null && row.price !== undefined">¥{{ row.price }}</span>
+              <span class="fulfiller-fee" v-if="row.price !== null && row.price !== undefined">¥{{ row.price / 100.0 }}</span>
             </div>
             <span v-else class="text-gray">暂未指派</span>
           </template>
@@ -119,11 +116,15 @@
           <template #default="{ row }">
             <div class="op-cell">
               <el-button link type="primary" size="small" @click="handleDetail(row)">详情</el-button>
-              <el-button v-if="row.status === 0" link type="success" size="small" @click="openDispatchDialog(row)">派单</el-button>
-              <el-button v-if="[1, 2].includes(row.status)" link type="warning" size="small" @click="openDispatchDialog(row)">重新派单</el-button>
-              <el-button v-if="[0, 1].includes(row.status)" link type="danger" size="small" @click="handleCancel(row)">取消</el-button>
-
-              <el-dropdown v-if="[2, 3, 4].includes(row.status)" trigger="click" @command="(cmd) => handleCommand(cmd, row)">
+              <el-button v-if="row.status === 0" link type="success" size="small"
+                @click="openDispatchDialog(row)">派单</el-button>
+              <el-button v-if="[1, 2].includes(row.status)" link type="warning" size="small"
+                @click="openDispatchDialog(row)">重新派单</el-button>
+              <el-button v-if="[0, 1].includes(row.status)" link type="danger" size="small"
+                @click="handleCancel(row)">取消</el-button>
+
+              <el-dropdown v-if="[2, 3, 4].includes(row.status)" trigger="click"
+                @command="(cmd) => handleCommand(cmd, row)">
                 <span class="el-dropdown-link">
                   更多<el-icon class="el-icon--right">
                     <ArrowDown />
@@ -144,29 +145,18 @@
       </el-table>
 
       <div class="pagination-container">
-        <el-pagination
-          v-model:current-page="pagination.current"
-          v-model:page-size="pagination.size"
-          :page-sizes="[10, 20, 50, 100]"
-          layout="total, sizes, prev, pager, next, jumper"
-          :total="pagination.total"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-        />
+        <el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.size"
+          :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
+          @size-change="handleSizeChange" @current-change="handleCurrentChange" />
       </div>
     </el-card>
 
     <!-- 组件 -->
-    <OrderDetailDrawer
-      v-model:visible="detailVisible"
-      :order="currentOrder"
-      @dispatch="openDispatchDialog"
-      @cancel="handleCancel"
-      @command="handleCommand"
-      @care-summary="openCareSummary"
-    />
+    <OrderDetailDrawer v-model:visible="detailVisible" :order="currentOrder" @dispatch="openDispatchDialog"
+      @cancel="handleCancel" @command="handleCommand" @care-summary="openCareSummary" />
 
-    <DispatchDialog v-model:visible="dispatchDialogVisible" :order="currentDispatchOrder" @submit="handleDispatchSubmit" />
+    <DispatchDialog v-model:visible="dispatchDialogVisible" :order="currentDispatchOrder"
+      @submit="handleDispatchSubmit" />
 
     <CareSummaryDrawer v-model:visible="careSummaryVisible" :order="careSummaryOrder" @submit="saveCareSummary" />
 
@@ -189,8 +179,10 @@ import { listSubOrder } from '@/api/order/subOrder/index';
 import { dispatchSubOrder } from '@/api/order/subOrder/index';
 import { getSubOrderInfo } from '@/api/order/subOrder/index';
 import { cancelSubOrder } from '@/api/order/subOrder/index';
+import { remarkSubOrder } from '@/api/order/subOrder/index';
 import { listOnStore as listAreaStationOnStore } from '@/api/system/areaStation';
 import { getStore } from '@/api/system/store';
+import { reward } from '@/api/fulfiller/pool';
 
 const loading = ref(false);
 
@@ -471,7 +463,7 @@ const handleDetail = async (row) => {
         }
       };
     }
-  } catch {}
+  } catch { }
   detailVisible.value = true;
 };
 
@@ -570,22 +562,69 @@ const saveCareSummary = (text) => {
 
 // 奖惩
 const openRewardDialog = (row) => {
-  currentOperateRow.value = row;
+  const typeName = getServiceName(row?.service);
+  const isTransport = row?.mode === 1 || row?.mode === '1';
+  const typeCode = isTransport ? 'transport' : typeName?.includes('喂') || typeName?.includes('遛') ? 'feeding' : 'washing';
+
+  const t = row?.subOrderType ?? row?.type;
+  const transportType =
+    typeof t === 'number' ? (t === 0 ? 'pick' : t === 1 ? 'drop' : t === 2 ? 'round' : undefined) :
+      (t === 'pick' || t === 'drop' || t === 'round' ? t : undefined);
+
+  currentOperateRow.value = {
+    ...row,
+    orderNo: row?.code || row?.orderNo,
+    type: typeCode,
+    transportType,
+    splitType: row?.splitType
+  };
   rewardDialogVisible.value = true;
 };
-const handleRewardSubmit = (form) => {
-  ElMessage.success(`操作成功:${form.type === 'reward' ? '奖励' : '惩罚'}已执行`);
+const handleRewardSubmit = async (form) => {
+  if (!currentOperateRow.value?.fulfiller) {
+    ElMessage.warning('当前订单未指派履约者,无法执行奖惩操作');
+    return;
+  }
+  try {
+    await reward({
+      fulfillerId: currentOperateRow.value.fulfiller,
+      type: form.type,
+      target: form.item,
+      amount: form.value,
+      reason: form.reason
+    });
+    ElMessage.success(`操作成功:${form.type === 'reward' ? '奖励' : '惩罚'}已执行`);
+    handleSearch();
+  } catch {
+    // Error handled by interceptor
+  }
 };
 
 // 备注
 const openRemarkDialog = (row) => {
-  currentOperateRow.value = row;
+  currentOperateRow.value = {
+    ...row,
+    orderNo: row?.code || row?.orderNo
+  };
   remarkDialogVisible.value = true;
 };
-const handleRemarkSubmit = (text) => {
-  if (currentOperateRow.value) {
-    currentOperateRow.value.remark = text;
-    ElMessage.success('备注已更新');
+const handleRemarkSubmit = async (text) => {
+  if (!currentOperateRow.value?.id) {
+    ElMessage.warning('订单信息不存在');
+    return;
+  }
+  try {
+    await remarkSubOrder({
+      orderId: currentOperateRow.value.id,
+      remark: text
+    });
+    if (currentOperateRow.value) {
+      currentOperateRow.value.remark = text;
+    }
+    ElMessage.success('备注已保存');
+    handleSearch();
+  } catch {
+    // Error handled by interceptor
   }
 };