|
|
@@ -1,605 +1,686 @@
|
|
|
<template>
|
|
|
- <el-dialog v-model="visible" title="派单调度" width="900px" top="5vh" destroy-on-close append-to-body>
|
|
|
- <div class="dispatch-dialog-content">
|
|
|
- <!-- Top: Order Info -->
|
|
|
- <div class="dispatch-order-info box-card" v-if="order">
|
|
|
- <div class="list-card order-card" style="margin: 0; box-shadow: none; cursor: default; border: none">
|
|
|
- <div class="card-left">
|
|
|
- <div class="type-tag" :class="order.typeCode">
|
|
|
- {{ getShortType(order.typeCode) }}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="card-main">
|
|
|
- <template v-if="order.typeCode === 'transport'">
|
|
|
- <div class="row-addr" :title="order.fromAddress">
|
|
|
- <span class="tag pick">取</span> {{ order.fromAddress }}
|
|
|
- </div>
|
|
|
- <div class="row-addr" :title="order.toAddress">
|
|
|
- <span class="tag drop">送</span> {{ order.toAddress }}
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template v-else>
|
|
|
- <div class="row-addr" :title="order.toAddress"><span class="tag home">址</span> {{ order.toAddress }}</div>
|
|
|
- </template>
|
|
|
- <div class="row-time" style="margin-top: 4px">
|
|
|
- <el-icon>
|
|
|
- <Clock />
|
|
|
- </el-icon> {{ order.serviceTime }}
|
|
|
- <span class="days-tag" v-if="order.daysLater">{{ order.daysLater }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- Current Rider Info (For Re-dispatch) -->
|
|
|
- <div class="current-rider-section" v-if="currentRider">
|
|
|
- <div class="select-header" style="margin-bottom: 8px">
|
|
|
- <span class="tit">当前派单履约者</span>
|
|
|
- </div>
|
|
|
- <div class="list-card rider-card"
|
|
|
- style="margin-bottom: 20px; border: 1px solid #e4e7ed; background: #fafafa; cursor: default">
|
|
|
- <div class="card-left relative">
|
|
|
- <el-avatar :src="currentRider.avatar" :size="40" />
|
|
|
- <div class="dot" :class="currentRider.status"></div>
|
|
|
- </div>
|
|
|
- <div class="card-main">
|
|
|
- <div class="row-1" style="justify-content: space-between; align-items: flex-start">
|
|
|
- <div style="display: flex; align-items: baseline; gap: 8px">
|
|
|
- <span class="r-name">{{ currentRider.name }}</span>
|
|
|
- <dict-tag :options="sys_user_sex" :value="currentRider.gender ?? currentRider.sex" />
|
|
|
- <span class="r-phone">{{ currentRider.maskPhone }}</span>
|
|
|
- </div>
|
|
|
- <div class="status-right">
|
|
|
- <span class="status-badge" :class="currentRider.status">{{ getRiderStatusText(currentRider.status)
|
|
|
- }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="row-2 categories-row" style="margin-top: 6px; display: flex; gap: 4px; flex-wrap: wrap">
|
|
|
- <el-tag v-for="cat in (currentRider.tags || [])" :key="cat" size="small" :type="getTagType(cat)" effect="plain">{{
|
|
|
- getTagText(cat)
|
|
|
- }}</el-tag>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="row-3 time-row" style="margin-top: 4px">
|
|
|
- <span class="last-time">下一单: {{ currentRider.nextOrderTime || '14:30' }}</span>
|
|
|
+ <el-dialog v-model="dialogVisible" title="派单调度" width="900px" top="5vh" destroy-on-close append-to-body>
|
|
|
+ <div class="dispatch-dialog-content">
|
|
|
+ <!-- Top: Order Info (OrderDispatch Style) -->
|
|
|
+ <div class="dispatch-order-info" v-if="order">
|
|
|
+ <div class="list-card order-card" style="margin:0; box-shadow:none; cursor:default; border:none;">
|
|
|
+ <div class="card-left">
|
|
|
+ <div class="type-tag" :class="order.typeCode">
|
|
|
+ {{ getShortType(order.typeCode) }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-main">
|
|
|
+ <template v-if="order.typeCode === 'transport'">
|
|
|
+ <div class="row-addr" :title="order.pickAddr">
|
|
|
+ <span class="tag pick">取</span> {{ order.pickAddr }}
|
|
|
+ </div>
|
|
|
+ <div class="row-addr" :title="order.dropAddr">
|
|
|
+ <span class="tag drop">送</span> {{ order.dropAddr }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div class="row-addr" :title="order.address">
|
|
|
+ <span class="tag home">址</span> {{ order.address }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div class="row-time" style="margin-top: 4px;">
|
|
|
+ <el-icon>
|
|
|
+ <Clock />
|
|
|
+ </el-icon> {{ order.time }}
|
|
|
+ <span class="days-tag" v-if="order.daysLater">{{ order.daysLater }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 新增右侧按钮组 -->
|
|
|
+ <div class="card-right" style="display: flex; align-items: center; gap: 10px; padding-left: 20px;">
|
|
|
+ <el-button type="primary" size="small" plain round icon="User" @click="openCustomerDetail" :loading="orderInfoLoading">用户档案</el-button>
|
|
|
+ <el-button type="success" size="small" plain round @click="openPetDetail" :loading="orderInfoLoading" style="margin-left: 0;">宠物档案</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- Middle: Rider Selection -->
|
|
|
- <div class="dispatch-rider-select">
|
|
|
- <div class="select-header">
|
|
|
- <span class="tit">选择履约者</span>
|
|
|
- <el-input v-model="searchQuery" placeholder="搜索履约者姓名/手机号" prefix-icon="Search" clearable
|
|
|
- style="width: 240px" />
|
|
|
- </div>
|
|
|
|
|
|
- <div class="rider-grid-wrapper">
|
|
|
- <el-scrollbar height="400px">
|
|
|
- <div class="rider-grid">
|
|
|
- <div v-for="rider in filteredRiders" :key="rider.id" class="list-card rider-card select-card"
|
|
|
- :class="{ active: selectedId === rider.id }" @click="selectedId = rider.id">
|
|
|
- <div class="card-left relative">
|
|
|
- <el-avatar :src="rider.avatar" :size="40" />
|
|
|
- <div class="dot" :class="rider.status"></div>
|
|
|
+ <!-- Current Rider Info (If Exists) -->
|
|
|
+ <div class="current-rider-section" v-if="currentRider">
|
|
|
+ <div class="select-header" style="margin-bottom:8px;">
|
|
|
+ <span class="tit">当前派单履约者</span>
|
|
|
</div>
|
|
|
- <div class="card-main">
|
|
|
- <div class="row-1" style="justify-content: space-between; align-items: flex-start">
|
|
|
- <div style="display: flex; align-items: baseline; gap: 8px">
|
|
|
- <span class="r-name">{{ rider.name }}</span>
|
|
|
- <dict-tag :options="sys_user_sex" :value="rider.gender ?? rider.sex" />
|
|
|
- <span class="r-phone">{{ rider.maskPhone }}</span>
|
|
|
+ <div class="list-card rider-card"
|
|
|
+ style="margin-bottom: 20px; border: 1px solid #e4e7ed; background:#fafafa; cursor:default;">
|
|
|
+ <div class="card-left relative">
|
|
|
+ <el-avatar :src="currentRider.avatar" :size="40" />
|
|
|
</div>
|
|
|
- <div class="status-right">
|
|
|
- <span class="status-badge" :class="rider.status">{{ getRiderStatusText(rider.status) }}</span>
|
|
|
+ <div class="card-main">
|
|
|
+ <div class="row-1"
|
|
|
+ style="justify-content: space-between; align-items: flex-start; display: flex;">
|
|
|
+ <div style="display:flex; align-items:baseline; gap:8px;">
|
|
|
+ <span class="r-name">{{ currentRider.name || '--' }}</span>
|
|
|
+ <span class="r-phone">{{ currentRider.phone || '--' }}</span>
|
|
|
+ <dict-tag :options="sys_user_sex" :value="currentRider.gender" />
|
|
|
+ <el-tag v-if="currentRider.status" size="small" :type="getStatusType(currentRider.status)" effect="plain">
|
|
|
+ {{ getStatusText(currentRider.status) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="row-2 categories-row"
|
|
|
+ style="margin-top: 6px; display:flex; gap:4px; flex-wrap:wrap;">
|
|
|
+ <el-tag v-for="typeId in (currentRider.serviceTypes ? String(currentRider.serviceTypes).split(',') : [])" :key="typeId" size="small"
|
|
|
+ type="primary" effect="plain">{{ getServiceTypeText(typeId) }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="row-3 time-row" style="margin-top: 4px;">
|
|
|
+ <span class="last-time">下一单: {{ currentRider.nextOrderTime || '-' }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="row-2 categories-row" style="margin-top: 6px; display: flex; gap: 4px; flex-wrap: wrap">
|
|
|
- <el-tag v-for="cat in (rider.tags || [])" :key="cat" size="small" :type="getTagType(cat)" effect="plain">{{
|
|
|
- getTagText(cat)
|
|
|
- }}</el-tag>
|
|
|
- </div>
|
|
|
+ <!-- Middle: Rider Selection -->
|
|
|
+ <div class="dispatch-rider-select">
|
|
|
+ <div class="select-header">
|
|
|
+ <span class="tit">选择履约者</span>
|
|
|
+ <el-input v-model="dispatchSearchQuery" placeholder="搜索履约者姓名/手机号" prefix-icon="Search" clearable
|
|
|
+ style="width: 240px" />
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="row-3 time-row" style="margin-top: 4px">
|
|
|
- <span class="last-time">下一单: {{ rider.nextOrderTime || '14:30' }}</span>
|
|
|
- </div>
|
|
|
+ <div class="rider-grid-wrapper">
|
|
|
+ <el-scrollbar class="rider-scroll">
|
|
|
+ <div class="rider-grid">
|
|
|
+ <div v-for="rider in filteredDispatchRiders" :key="rider.id"
|
|
|
+ class="list-card rider-card select-card"
|
|
|
+ :class="{ active: selectedRiderId === rider.id }" @click="selectedRiderId = rider.id">
|
|
|
+ <!-- Reusing Rider Card Layout -->
|
|
|
+ <div class="card-left relative">
|
|
|
+ <el-avatar :src="rider.avatar" :size="40" />
|
|
|
+ </div>
|
|
|
+ <div class="card-main">
|
|
|
+ <div class="row-1" style="justify-content: space-between; align-items: flex-start; display: flex;">
|
|
|
+ <div style="display:flex; align-items:baseline; gap:8px;">
|
|
|
+ <span class="r-name">{{ rider.name || '--' }}</span>
|
|
|
+ <span class="r-phone">{{ rider.phone || '--' }}</span>
|
|
|
+ <dict-tag :options="sys_user_sex" :value="rider.gender" />
|
|
|
+ </div>
|
|
|
+ <el-tag v-if="rider.status" size="small" :type="getStatusType(rider.status)" effect="plain">
|
|
|
+ {{ getStatusText(rider.status) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="row-2 categories-row"
|
|
|
+ style="margin-top: 6px; display:flex; gap:4px; flex-wrap:wrap;">
|
|
|
+ <el-tag v-for="typeId in (rider.serviceTypes ? String(rider.serviceTypes).split(',') : [])" :key="typeId" size="small"
|
|
|
+ type="primary" effect="plain">{{ getServiceTypeText(typeId) }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="row-3 time-row" style="margin-top: 4px">
|
|
|
+ <span class="last-time">下一单: {{ rider.nextOrderTime || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Selected Check -->
|
|
|
+ <div class="selected-mark" v-if="selectedRiderId === rider.id">
|
|
|
+ <el-icon>
|
|
|
+ <Check />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="filteredDispatchRiders.length === 0" class="empty-text">暂无符合条件的履约者</div>
|
|
|
+ </div>
|
|
|
+ </el-scrollbar>
|
|
|
</div>
|
|
|
|
|
|
- <!-- Selected Check -->
|
|
|
- <div class="selected-mark" v-if="selectedId === rider.id">
|
|
|
- <el-icon>
|
|
|
- <Check />
|
|
|
- </el-icon>
|
|
|
+ <div class="rider-pagination" style="margin-top: 20px;">
|
|
|
+ <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize"
|
|
|
+ :page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next" :total="total"
|
|
|
+ @current-change="loadRiders" @size-change="handlePageSizeChange" />
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <div v-if="filteredRiders.length === 0" class="empty-text">暂无符合条件的履约者</div>
|
|
|
+ <!-- Bottom: Fee & Submit -->
|
|
|
+ <div class="dispatch-footer">
|
|
|
+ <div class="fee-input">
|
|
|
+ <span class="label">服务费用:</span>
|
|
|
+ <el-input-number v-model="dispatchFee" :min="0" :precision="2" :step="10" placeholder="请输入"
|
|
|
+ style="width: 140px;" />
|
|
|
+ <span class="unit">元</span>
|
|
|
+ </div>
|
|
|
+ <div class="btns">
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :disabled="!canSubmit" @click="handleDispatchSubmit">确认派单</el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </el-scrollbar>
|
|
|
</div>
|
|
|
+ </el-dialog>
|
|
|
|
|
|
- <div class="rider-pagination" style="margin-top: 20px;">
|
|
|
- <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize"
|
|
|
- :page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next" :total="total"
|
|
|
- @current-change="loadRiders" @size-change="handlePageSizeChange" />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- Bottom: Fee & Submit -->
|
|
|
- <div class="dispatch-footer">
|
|
|
- <div class="fee-input">
|
|
|
- <span class="label">服务费用:</span>
|
|
|
- <el-input-number v-model="fee" :min="0" :precision="2" :step="10" placeholder="请输入" style="width: 140px" />
|
|
|
- <span class="unit">元</span>
|
|
|
- </div>
|
|
|
- <div class="btns">
|
|
|
- <el-button @click="visible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="handleSubmit">确认派单</el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-dialog>
|
|
|
+ <CustomerDetailDrawer v-model:visible="customerDialogVisible" :customer-id="customerId" />
|
|
|
+ <PetDetailDrawer v-model:visible="petDialogVisible" :pet-id="petId" />
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed, watch } from 'vue';
|
|
|
-import { ElMessage } from 'element-plus';
|
|
|
-import { Check, Clock, Search } from '@element-plus/icons-vue';
|
|
|
-import { useDict } from '@/utils/dict';
|
|
|
-import DictTag from '@/components/DictTag/index.vue';
|
|
|
-
|
|
|
-const { sys_user_sex } = useDict('sys_user_sex');
|
|
|
-import { pageFulfillerOnOrder } from '@/api/fulfiller/pool';
|
|
|
-import { listAllTag } from '@/api/fulfiller/tag';
|
|
|
+import { ref, computed, watch, getCurrentInstance, toRefs } from 'vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import { pageFulfillerOnOrder } from '@/api/fulfiller/pool'
|
|
|
+import { listAllTag } from '@/api/fulfiller/tag'
|
|
|
+import { getSubOrderInfo } from '@/api/order/subOrder/index'
|
|
|
+import { listServiceOnOrder } from '@/api/service/list/index'
|
|
|
+import CustomerDetailDrawer from './CustomerDetailDrawer.vue'
|
|
|
+import PetDetailDrawer from './PetDetailDrawer.vue'
|
|
|
|
|
|
const props = defineProps({
|
|
|
- modelValue: { type: Boolean, default: false },
|
|
|
- order: { type: Object, default: () => null },
|
|
|
- currentRider: { type: Object, default: () => null }
|
|
|
-});
|
|
|
+ visible: Boolean,
|
|
|
+ order: Object
|
|
|
+})
|
|
|
+const emit = defineEmits(['update:visible', 'submit'])
|
|
|
|
|
|
-const emit = defineEmits(['update:modelValue', 'submit']);
|
|
|
+const { proxy } = getCurrentInstance();
|
|
|
+const { sys_user_sex } = toRefs(proxy.useDict('sys_user_sex'));
|
|
|
|
|
|
-const visible = computed({
|
|
|
- get: () => props.modelValue,
|
|
|
- set: (val) => emit('update:modelValue', val)
|
|
|
-});
|
|
|
+const dialogVisible = computed({
|
|
|
+ get: () => props.visible,
|
|
|
+ set: (val) => emit('update:visible', val)
|
|
|
+})
|
|
|
|
|
|
-const searchQuery = ref('');
|
|
|
-const selectedId = ref(null);
|
|
|
-const fee = ref(0);
|
|
|
+const ridersList = ref([])
|
|
|
+const total = ref(0)
|
|
|
+const pageNum = ref(1)
|
|
|
+const pageSize = ref(10)
|
|
|
|
|
|
-const ridersList = ref([]);
|
|
|
-const total = ref(0);
|
|
|
-const pageNum = ref(1);
|
|
|
-const pageSize = ref(10);
|
|
|
-
|
|
|
-const allTags = ref([]);
|
|
|
+const allTags = ref([])
|
|
|
const tagMap = computed(() => {
|
|
|
- const map = {};
|
|
|
- for (const t of (allTags.value || [])) {
|
|
|
- if (t && t.id !== undefined && t.id !== null) map[t.id] = t;
|
|
|
- }
|
|
|
- return map;
|
|
|
-});
|
|
|
+ const map = {}
|
|
|
+ for (const t of (allTags.value || [])) {
|
|
|
+ if (t && t.id !== undefined && t.id !== null) map[t.id] = t
|
|
|
+ }
|
|
|
+ return map
|
|
|
+})
|
|
|
+
|
|
|
+const currentRider = ref(null)
|
|
|
+const dispatchSearchQuery = ref('')
|
|
|
+const selectedRiderId = ref(null)
|
|
|
+const dispatchFee = ref(0)
|
|
|
+
|
|
|
+const customerDialogVisible = ref(false)
|
|
|
+const petDialogVisible = ref(false)
|
|
|
+const customerId = ref(null)
|
|
|
+const petId = ref(null)
|
|
|
+const orderInfoLoading = ref(false)
|
|
|
|
|
|
const loadAllTags = async () => {
|
|
|
- if (allTags.value && allTags.value.length > 0) return;
|
|
|
- try {
|
|
|
- const res = await listAllTag({ category: 'fulfiller' });
|
|
|
- allTags.value = res?.data || [];
|
|
|
- } catch {
|
|
|
- allTags.value = [];
|
|
|
- }
|
|
|
-};
|
|
|
+ if (allTags.value && allTags.value.length > 0) return
|
|
|
+ try {
|
|
|
+ const res = await listAllTag({ category: 'fulfiller' })
|
|
|
+ allTags.value = res?.data || []
|
|
|
+ } catch {
|
|
|
+ allTags.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const serviceOptions = ref([])
|
|
|
+const loadServiceOptions = async () => {
|
|
|
+ if (serviceOptions.value.length > 0) return
|
|
|
+ try {
|
|
|
+ const res = await listServiceOnOrder()
|
|
|
+ serviceOptions.value = res?.data || []
|
|
|
+ } catch { /* ignore */ }
|
|
|
+}
|
|
|
+
|
|
|
+const getServiceTypeText = (id) => {
|
|
|
+ const s = serviceOptions.value.find(item => String(item.id) === String(id))
|
|
|
+ return s ? s.name : String(id)
|
|
|
+}
|
|
|
|
|
|
const loadRiders = async () => {
|
|
|
- try {
|
|
|
- const res = await pageFulfillerOnOrder({
|
|
|
- content: searchQuery.value || undefined,
|
|
|
- pageNum: pageNum.value,
|
|
|
- pageSize: pageSize.value,
|
|
|
- service: props.order?.service
|
|
|
- });
|
|
|
- const list = res?.rows || [];
|
|
|
- ridersList.value = list.map(r => ({
|
|
|
- ...r,
|
|
|
- nextOrderTime: r.nextOrderTime || '14:30' // 使用假数据代替下一单时间
|
|
|
- }));
|
|
|
- total.value = res?.total || 0;
|
|
|
- } catch {
|
|
|
- ridersList.value = [];
|
|
|
- total.value = 0;
|
|
|
- }
|
|
|
-};
|
|
|
+ try {
|
|
|
+ const res = await pageFulfillerOnOrder({
|
|
|
+ content: dispatchSearchQuery.value || undefined,
|
|
|
+ pageNum: pageNum.value,
|
|
|
+ pageSize: pageSize.value,
|
|
|
+ service: props.order?.service
|
|
|
+ })
|
|
|
+ const list = res?.rows || []
|
|
|
+ ridersList.value = list.map(r => ({
|
|
|
+ ...r,
|
|
|
+ nextOrderTime: r.nextOrderTime || '-',
|
|
|
+ gender: r.gender ?? r.sex
|
|
|
+ }))
|
|
|
+ total.value = res?.total || 0
|
|
|
+
|
|
|
+ if (props.order?.riderId) {
|
|
|
+ currentRider.value = ridersList.value.find(r => r.id === props.order.riderId) || null
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ ridersList.value = []
|
|
|
+ total.value = 0
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
const handlePageSizeChange = (size) => {
|
|
|
- pageSize.value = size;
|
|
|
- pageNum.value = 1;
|
|
|
- loadRiders();
|
|
|
-};
|
|
|
-
|
|
|
-watch(searchQuery, () => {
|
|
|
- pageNum.value = 1;
|
|
|
- loadRiders();
|
|
|
-});
|
|
|
-
|
|
|
-// Reset form when dialog opens
|
|
|
-watch(visible, (val) => {
|
|
|
- if (val) {
|
|
|
- searchQuery.value = '';
|
|
|
- selectedId.value = null;
|
|
|
- fee.value = 0;
|
|
|
- pageNum.value = 1;
|
|
|
- loadAllTags();
|
|
|
- loadRiders();
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
-const filteredRiders = computed(() => {
|
|
|
- return ridersList.value || [];
|
|
|
-});
|
|
|
-
|
|
|
-const handleSubmit = () => {
|
|
|
- if (!selectedId.value) {
|
|
|
- ElMessage.warning('请选择履约者');
|
|
|
- return;
|
|
|
- }
|
|
|
- if (!fee.value) {
|
|
|
- ElMessage.warning('请输入服务费用');
|
|
|
- return;
|
|
|
- }
|
|
|
- emit('submit', { order: props.order, riderId: selectedId.value, fee: fee.value });
|
|
|
-};
|
|
|
-
|
|
|
-// Helpers
|
|
|
+ pageSize.value = size
|
|
|
+ pageNum.value = 1
|
|
|
+ loadRiders()
|
|
|
+}
|
|
|
+
|
|
|
+watch(() => props.visible, (val) => {
|
|
|
+ if (val && props.order) {
|
|
|
+ currentRider.value = null
|
|
|
+ dispatchSearchQuery.value = ''
|
|
|
+ selectedRiderId.value = null
|
|
|
+ // price 单位为分,转成元显示
|
|
|
+ dispatchFee.value = props.order?.price ? Number((props.order.price / 100).toFixed(2)) : 0
|
|
|
+ if (props.order?.riderId) {
|
|
|
+ currentRider.value = {
|
|
|
+ id: props.order.riderId,
|
|
|
+ gender: props.order.riderGender ?? props.order.riderSex
|
|
|
+ }
|
|
|
+ }
|
|
|
+ pageNum.value = 1
|
|
|
+ loadAllTags()
|
|
|
+ loadServiceOptions()
|
|
|
+ loadRiders()
|
|
|
+
|
|
|
+ // 获取订单详细信息
|
|
|
+ customerId.value = null
|
|
|
+ petId.value = null
|
|
|
+ orderInfoLoading.value = true
|
|
|
+ getSubOrderInfo(props.order.id).then((res) => {
|
|
|
+ if(res.data) {
|
|
|
+ // 如果 usrCustomer / usrPet 是对象则取其 id,如果是 ID 直接取
|
|
|
+ customerId.value = res.data.usrCustomer?.id || res.data.usrCustomer
|
|
|
+ petId.value = res.data.usrPet?.id || res.data.usrPet
|
|
|
+
|
|
|
+ // 接到详情后,把真实的金额放进去(后端金额单位为分)
|
|
|
+ if (res.data.price !== undefined && res.data.price !== null) {
|
|
|
+ dispatchFee.value = Number((res.data.price / 100).toFixed(2))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已经有履约者且不是在列表中找到的,从详情中补全性别
|
|
|
+ if (props.order?.riderId && !currentRider.value) {
|
|
|
+ currentRider.value = {
|
|
|
+ id: props.order.riderId,
|
|
|
+ name: res.data.fulfillerName,
|
|
|
+ gender: res.data.fulfillerGender ?? res.data.fulfillerSex,
|
|
|
+ status: res.data.fulfillerStatus
|
|
|
+ }
|
|
|
+ } else if (currentRider.value && (res.data.fulfillerGender || res.data.fulfillerSex)) {
|
|
|
+ currentRider.value.gender = res.data.fulfillerGender ?? res.data.fulfillerSex
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }).catch((e) => {
|
|
|
+ console.error('获取订单详细信息失败', e)
|
|
|
+ }).finally(() => {
|
|
|
+ orderInfoLoading.value = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const openCustomerDetail = () => {
|
|
|
+ if (!customerId.value) {
|
|
|
+ ElMessage.warning('未能获取到用户信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ customerDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const openPetDetail = () => {
|
|
|
+ if (!petId.value) {
|
|
|
+ ElMessage.warning('未能获取到宠物信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ petDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
const getTagText = (tagId) => {
|
|
|
- const t = tagMap.value?.[tagId];
|
|
|
- return t?.name || String(tagId);
|
|
|
-};
|
|
|
+ const t = tagMap.value?.[tagId]
|
|
|
+ return t?.name || String(tagId)
|
|
|
+}
|
|
|
+
|
|
|
const getTagType = (tagId) => {
|
|
|
- const t = tagMap.value?.[tagId];
|
|
|
- const type = t?.colorType;
|
|
|
- if (type === 'success' || type === 'warning' || type === 'danger' || type === 'info') return type;
|
|
|
- return '';
|
|
|
-};
|
|
|
-const getShortType = (code) => {
|
|
|
- const map = { 'transport': '接送', 'feeding': '喂遛', 'washing': '洗护' };
|
|
|
- return map[code] || '订单';
|
|
|
-};
|
|
|
-const getRiderStatusText = (status) => {
|
|
|
- const statusMap = {
|
|
|
- resting: '休息',
|
|
|
- busy: '接单中',
|
|
|
- disabled: '禁用'
|
|
|
- };
|
|
|
- return statusMap[status] || status;
|
|
|
-};
|
|
|
+ const t = tagMap.value?.[tagId]
|
|
|
+ const type = t?.colorType
|
|
|
+ if (type === 'success' || type === 'warning' || type === 'danger' || type === 'info') return type
|
|
|
+ return ''
|
|
|
+}
|
|
|
|
|
|
+watch(dispatchSearchQuery, () => {
|
|
|
+ pageNum.value = 1
|
|
|
+ loadRiders()
|
|
|
+})
|
|
|
+
|
|
|
+const getShortType = (code) => {
|
|
|
+ const map = { 'transport': '接送', 'feeding': '喂遛', 'washing': '洗护' }
|
|
|
+ return map[code] || '订单'
|
|
|
+}
|
|
|
+
|
|
|
+const getStatusText = (status) => {
|
|
|
+ const statusMap = {
|
|
|
+ resting: '休息',
|
|
|
+ busy: '接单中',
|
|
|
+ disabled: '禁用'
|
|
|
+ }
|
|
|
+ return statusMap[status] || status
|
|
|
+}
|
|
|
+
|
|
|
+const getStatusType = (status) => {
|
|
|
+ const typeMap = {
|
|
|
+ resting: 'info',
|
|
|
+ busy: 'success',
|
|
|
+ disabled: 'danger'
|
|
|
+ }
|
|
|
+ return typeMap[status] || 'info'
|
|
|
+}
|
|
|
+
|
|
|
+const filteredDispatchRiders = computed(() => {
|
|
|
+ return ridersList.value || []
|
|
|
+})
|
|
|
+
|
|
|
+const canSubmit = computed(() => {
|
|
|
+ return !!selectedRiderId.value && !!dispatchFee.value
|
|
|
+})
|
|
|
+
|
|
|
+const handleDispatchSubmit = () => {
|
|
|
+ if (!selectedRiderId.value) {
|
|
|
+ ElMessage.warning('请选择履约者')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!dispatchFee.value) {
|
|
|
+ ElMessage.warning('请输入服务费用')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const rider = ridersList.value.find(r => r.id === selectedRiderId.value)
|
|
|
+ emit('submit', {
|
|
|
+ riderId: rider.id,
|
|
|
+ riderName: rider.name,
|
|
|
+ fee: dispatchFee.value
|
|
|
+ })
|
|
|
+ dialogVisible.value = false
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
+/* Dispatch Dialog Styles */
|
|
|
.list-card {
|
|
|
- background: #fff;
|
|
|
- border: 1px solid #ebeef5;
|
|
|
- border-radius: 8px;
|
|
|
- padding: 12px;
|
|
|
- margin-bottom: 10px;
|
|
|
- display: flex;
|
|
|
- align-items: stretch;
|
|
|
- gap: 12px;
|
|
|
- transition: all 0.2s;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 12px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ display: flex;
|
|
|
+ align-items: stretch;
|
|
|
+ gap: 12px;
|
|
|
+ transition: all 0.2s;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.list-card:hover {
|
|
|
+ border-color: #c6e2ff;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
}
|
|
|
|
|
|
.card-left {
|
|
|
- flex-shrink: 0;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
}
|
|
|
|
|
|
.order-card .type-tag {
|
|
|
- width: 40px;
|
|
|
- height: 40px;
|
|
|
- border-radius: 8px;
|
|
|
- color: #fff;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 12px;
|
|
|
- font-weight: bold;
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 8px;
|
|
|
+ color: #fff;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
|
|
|
.type-tag.transport {
|
|
|
- background: #e6a23c;
|
|
|
+ background: #e6a23c;
|
|
|
}
|
|
|
|
|
|
.type-tag.feeding {
|
|
|
- background: #67c23a;
|
|
|
+ background: #67c23a;
|
|
|
}
|
|
|
|
|
|
.type-tag.washing {
|
|
|
- background: #409eff;
|
|
|
+ background: #409eff;
|
|
|
}
|
|
|
|
|
|
.card-main {
|
|
|
- flex: 1;
|
|
|
- overflow: hidden;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- justify-content: flex-start;
|
|
|
- gap: 4px;
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 4px;
|
|
|
}
|
|
|
|
|
|
-.row-1 {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
+.row-addr {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #303133;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ line-height: 1.5;
|
|
|
}
|
|
|
|
|
|
-.o-type {
|
|
|
- font-weight: bold;
|
|
|
- font-size: 14px;
|
|
|
- color: #303133;
|
|
|
+.row-addr .tag {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #fff;
|
|
|
+ padding: 1px 4px;
|
|
|
+ border-radius: 4px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ transform: scale(0.9);
|
|
|
}
|
|
|
|
|
|
-.row-2 {
|
|
|
- font-size: 12px;
|
|
|
- color: #606266;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
+.tag.pick {
|
|
|
+ background: #409eff;
|
|
|
}
|
|
|
|
|
|
-.row-3 {
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
+.tag.drop {
|
|
|
+ background: #e6a23c;
|
|
|
}
|
|
|
|
|
|
-.card-right {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: flex-end;
|
|
|
- justify-content: center;
|
|
|
- gap: 8px;
|
|
|
- margin-left: 8px;
|
|
|
- flex-shrink: 0;
|
|
|
+.tag.home {
|
|
|
+ background: #67c23a;
|
|
|
}
|
|
|
|
|
|
-.card-right .actions {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
+.row-time {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
}
|
|
|
|
|
|
-.rider-card .card-left .dot {
|
|
|
- position: absolute;
|
|
|
- bottom: 0;
|
|
|
- right: 0;
|
|
|
- width: 10px;
|
|
|
- height: 10px;
|
|
|
- border-radius: 50%;
|
|
|
- border: 2px solid #fff;
|
|
|
+.days-tag {
|
|
|
+ color: #f56c6c;
|
|
|
+ background: #fef0f0;
|
|
|
+ padding: 0 4px;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 11px;
|
|
|
+ border: 1px solid #fde2e2;
|
|
|
+ transform: scale(0.95);
|
|
|
}
|
|
|
|
|
|
-.dot.online {
|
|
|
- background: #67c23a;
|
|
|
+.dispatch-order-info {
|
|
|
+ background: #f5f7fa;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ display: block;
|
|
|
}
|
|
|
|
|
|
-.dot.busy {
|
|
|
- background: #409eff;
|
|
|
+.dispatch-rider-select .select-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 10px;
|
|
|
}
|
|
|
|
|
|
-.dot.offline {
|
|
|
- background: #909399;
|
|
|
+.dispatch-rider-select .tit {
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 14px;
|
|
|
}
|
|
|
|
|
|
-.r-name {
|
|
|
- font-size: 14px;
|
|
|
- font-weight: bold;
|
|
|
- color: #303133;
|
|
|
+.rider-scroll {
|
|
|
+ max-height: 45vh;
|
|
|
}
|
|
|
|
|
|
-.r-phone {
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
+.rider-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 12px;
|
|
|
+ padding-right: 10px;
|
|
|
}
|
|
|
|
|
|
-.cat-tag {
|
|
|
- background: #f4f4f5;
|
|
|
- color: #909399;
|
|
|
- font-size: 10px;
|
|
|
- padding: 1px 4px;
|
|
|
- border-radius: 2px;
|
|
|
+.rider-pagination {
|
|
|
+ margin-top: 10px;
|
|
|
}
|
|
|
|
|
|
-.cat-tag.cat-transport {
|
|
|
- background: #e6f7ff;
|
|
|
- color: #1890ff;
|
|
|
- border: 1px solid #91d5ff;
|
|
|
+.rider-card.select-card {
|
|
|
+ cursor: pointer;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ position: relative;
|
|
|
+ transition: all 0.2s;
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
|
|
|
|
-.cat-tag.cat-feeding {
|
|
|
- background: #f6ffed;
|
|
|
- color: #52c41a;
|
|
|
- border: 1px solid #b7eb8f;
|
|
|
+.rider-card.select-card:hover {
|
|
|
+ border-color: #409eff;
|
|
|
}
|
|
|
|
|
|
-.cat-tag.cat-washing {
|
|
|
- background: #fff0f6;
|
|
|
- color: #eb2f96;
|
|
|
- border: 1px solid #ffadd2;
|
|
|
+.rider-card.select-card.active {
|
|
|
+ border-color: #409eff;
|
|
|
+ background-color: #ecf5ff;
|
|
|
}
|
|
|
|
|
|
-.status-badge {
|
|
|
- font-size: 11px;
|
|
|
- padding: 2px 6px;
|
|
|
- border-radius: 4px;
|
|
|
- display: inline-block;
|
|
|
- font-weight: bold;
|
|
|
+.selected-mark {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ right: 0;
|
|
|
+ background: #409eff;
|
|
|
+ color: #fff;
|
|
|
+ border-bottom-left-radius: 6px;
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 12px;
|
|
|
}
|
|
|
|
|
|
-.status-badge.online {
|
|
|
- background: #f0f9eb;
|
|
|
- color: #67c23a;
|
|
|
+.rider-card .card-left .dot {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ right: 0;
|
|
|
+ width: 10px;
|
|
|
+ height: 10px;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: 2px solid #fff;
|
|
|
}
|
|
|
|
|
|
-.status-badge.busy {
|
|
|
- background: #ecf5ff;
|
|
|
- color: #409eff;
|
|
|
+.dot.online {
|
|
|
+ background: #67c23a;
|
|
|
}
|
|
|
|
|
|
-.status-badge.offline {
|
|
|
- background: #f4f4f5;
|
|
|
- color: #909399;
|
|
|
+.dot.busy {
|
|
|
+ background: #409eff;
|
|
|
}
|
|
|
|
|
|
-.status-badge.disabled {
|
|
|
- background: #fef0f0;
|
|
|
- color: #f56c6c;
|
|
|
+.dot.offline {
|
|
|
+ background: #909399;
|
|
|
}
|
|
|
|
|
|
-.last-time {
|
|
|
- font-size: 11px;
|
|
|
- color: #999;
|
|
|
+.r-name {
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ margin-right: 8px;
|
|
|
}
|
|
|
|
|
|
-.dispatch-order-info {
|
|
|
- background: #f5f7fa;
|
|
|
- padding: 10px;
|
|
|
- border-radius: 4px;
|
|
|
- margin-bottom: 20px;
|
|
|
- border: 1px solid #e4e7ed;
|
|
|
+.r-phone {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
}
|
|
|
|
|
|
-.dispatch-rider-select .select-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 10px;
|
|
|
+.status-badge {
|
|
|
+ font-size: 11px;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 4px;
|
|
|
+ display: inline-block;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
|
|
|
-.dispatch-rider-select .tit {
|
|
|
- font-weight: bold;
|
|
|
- font-size: 14px;
|
|
|
+.status-badge.online {
|
|
|
+ background: #f0f9eb;
|
|
|
+ color: #67c23a;
|
|
|
}
|
|
|
|
|
|
-.rider-grid {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(2, 1fr);
|
|
|
- gap: 12px;
|
|
|
- padding-right: 10px;
|
|
|
+.status-badge.busy {
|
|
|
+ background: #ecf5ff;
|
|
|
+ color: #409eff;
|
|
|
}
|
|
|
|
|
|
-.rider-card.select-card {
|
|
|
- cursor: pointer;
|
|
|
- border: 1px solid #dcdfe6;
|
|
|
- position: relative;
|
|
|
- transition: all 0.2s;
|
|
|
- margin-bottom: 0;
|
|
|
+.status-badge.offline {
|
|
|
+ background: #f4f4f5;
|
|
|
+ color: #909399;
|
|
|
}
|
|
|
|
|
|
-.rider-card.select-card:hover {
|
|
|
- border-color: #409eff;
|
|
|
+.cat-tag {
|
|
|
+ background: #f4f4f5;
|
|
|
+ color: #909399;
|
|
|
+ font-size: 10px;
|
|
|
+ padding: 1px 4px;
|
|
|
+ border-radius: 2px;
|
|
|
+ margin-right: 4px;
|
|
|
}
|
|
|
|
|
|
-.rider-card.select-card.active {
|
|
|
- border-color: #409eff;
|
|
|
- background-color: #ecf5ff;
|
|
|
+.cat-tag.cat-transport {
|
|
|
+ background: #e6f7ff;
|
|
|
+ color: #1890ff;
|
|
|
+ border: 1px solid #91d5ff;
|
|
|
}
|
|
|
|
|
|
-.selected-mark {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- right: 0;
|
|
|
- background: #409eff;
|
|
|
- color: #fff;
|
|
|
- border-bottom-left-radius: 6px;
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 12px;
|
|
|
+.cat-tag.cat-feeding {
|
|
|
+ background: #f6ffed;
|
|
|
+ color: #52c41a;
|
|
|
+ border: 1px solid #b7eb8f;
|
|
|
}
|
|
|
|
|
|
-.dispatch-footer {
|
|
|
- margin-top: 20px;
|
|
|
- padding-top: 20px;
|
|
|
- border-top: 1px solid #ebeef5;
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
+.cat-tag.cat-washing {
|
|
|
+ background: #fff0f6;
|
|
|
+ color: #eb2f96;
|
|
|
+ border: 1px solid #ffadd2;
|
|
|
}
|
|
|
|
|
|
-.dispatch-footer .fee-input {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
- font-size: 14px;
|
|
|
+.last-time {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #999;
|
|
|
}
|
|
|
|
|
|
.empty-text {
|
|
|
- text-align: center;
|
|
|
- color: #909399;
|
|
|
- padding: 20px;
|
|
|
- width: 100%;
|
|
|
- grid-column: span 2;
|
|
|
+ text-align: center;
|
|
|
+ color: #909399;
|
|
|
+ padding: 20px;
|
|
|
+ width: 100%;
|
|
|
+ grid-column: span 2;
|
|
|
}
|
|
|
|
|
|
-/* Order Card New Layout */
|
|
|
-.row-addr {
|
|
|
- font-size: 13px;
|
|
|
- color: #303133;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- line-height: 1.5;
|
|
|
-}
|
|
|
-
|
|
|
-.row-addr .tag {
|
|
|
- font-size: 11px;
|
|
|
- color: #fff;
|
|
|
- padding: 1px 4px;
|
|
|
- border-radius: 4px;
|
|
|
- flex-shrink: 0;
|
|
|
- transform: scale(0.9);
|
|
|
-}
|
|
|
-
|
|
|
-.tag.pick {
|
|
|
- background: #409eff;
|
|
|
-}
|
|
|
-
|
|
|
-.tag.drop {
|
|
|
- background: #e6a23c;
|
|
|
-}
|
|
|
-
|
|
|
-.tag.home {
|
|
|
- background: #67c23a;
|
|
|
-}
|
|
|
-
|
|
|
-.row-time {
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
+.dispatch-footer {
|
|
|
+ margin-top: 20px;
|
|
|
+ padding-top: 20px;
|
|
|
+ border-top: 1px solid #ebeef5;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
}
|
|
|
|
|
|
-.days-tag {
|
|
|
- color: #f56c6c;
|
|
|
- background: #fef0f0;
|
|
|
- padding: 0 4px;
|
|
|
- border-radius: 4px;
|
|
|
- font-size: 11px;
|
|
|
- border: 1px solid #fde2e2;
|
|
|
- transform: scale(0.95);
|
|
|
+.dispatch-footer .fee-input {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ font-size: 14px;
|
|
|
}
|
|
|
</style>
|