|
|
@@ -0,0 +1,313 @@
|
|
|
+<template>
|
|
|
+ <!-- 订单状态日志抽屉 -->
|
|
|
+ <el-drawer v-model="drawerVisible" title="变更物流状态" size="38%" :destroy-on-close="true" :close-on-click-modal="true">
|
|
|
+ <div class="status-log-container">
|
|
|
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
|
|
+ <el-form-item label="物流单号" prop="logisticNos">
|
|
|
+ <el-input v-model="form.logisticNos" disabled />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="包裹单号" prop="packageNo">
|
|
|
+ <el-input v-model="form.packageNo" disabled />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="所在状态" prop="statusName">
|
|
|
+ <el-select v-model="form.statusName" placeholder="请选择状态" style="width: 100%" @change="handleStatusChange">
|
|
|
+ <el-option
|
|
|
+ v-for="item in statusOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ :disabled="isStatusDisabled(item.value)"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="发货人" prop="shipper">
|
|
|
+ <el-input v-model="form.shipper" disabled />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="操作时间" prop="operateTime">
|
|
|
+ <el-date-picker v-model="form.operateTime" type="date" placeholder="请选择操作时间" style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="快递员手机号" prop="courierPhone">
|
|
|
+ <el-input v-model="form.courierPhone" disabled />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="上传图片" prop="images" v-if="form.statusName === '已签收'">
|
|
|
+ <div class="image-selector-wrapper">
|
|
|
+ <!-- 修改点 1: 使用 !selectedImages.length 替代 length === 0,并确保样式一致 -->
|
|
|
+ <div
|
|
|
+ v-if="!selectedImages || selectedImages.length === 0"
|
|
|
+ class="upload-box"
|
|
|
+ @click="showFileSelector = true"
|
|
|
+ style="
|
|
|
+ width: 150px;
|
|
|
+ height: 150px;
|
|
|
+ border: 2px dashed #d9d9d9;
|
|
|
+ border-radius: 4px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div style="text-align: center; color: #8c939d">
|
|
|
+ <el-icon :size="40" style="margin-bottom: 8px">
|
|
|
+ <Plus />
|
|
|
+ </el-icon>
|
|
|
+ <div style="font-size: 14px">点击上传</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 修改点 2: 列表渲染区域 -->
|
|
|
+ <div v-else class="selected-images-list">
|
|
|
+ <div
|
|
|
+ v-for="(img, index) in selectedImages"
|
|
|
+ :key="img.url || index"
|
|
|
+ style="position: relative; display: inline-block; margin: 0 8px 8px 0"
|
|
|
+ >
|
|
|
+ <!-- 确保 img.url 存在才渲染,防止报错 -->
|
|
|
+ <el-image
|
|
|
+ v-if="img.url"
|
|
|
+ :src="img.url"
|
|
|
+ style="width: 150px; height: 150px"
|
|
|
+ fit="cover"
|
|
|
+ :preview-src-list="selectedImages.filter((i) => i.url).map((i) => i.url)"
|
|
|
+ />
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ :icon="Delete"
|
|
|
+ circle
|
|
|
+ size="small"
|
|
|
+ style="position: absolute; top: 5px; right: 5px; z-index: 10"
|
|
|
+ @click="removeImage(index)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 修改点 3: 如果未满 3 张,显示“继续添加”按钮 -->
|
|
|
+ <div
|
|
|
+ v-if="selectedImages.length < 3"
|
|
|
+ class="upload-box-more"
|
|
|
+ @click="showFileSelector = true"
|
|
|
+ style="
|
|
|
+ width: 150px;
|
|
|
+ height: 150px;
|
|
|
+ border: 2px dashed #d9d9d9;
|
|
|
+ border-radius: 4px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ vertical-align: top;
|
|
|
+ background-color: #f5f7fa; /* 加个背景色区分 */
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-icon :size="30" color="#8c939d">
|
|
|
+ <Plus />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 文件选择器 -->
|
|
|
+ <FileSelector v-model="showFileSelector" :multiple="true" :allowed-types="[1]" title="选择图片" @confirm="handleImagesSelected" />
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div class="drawer-footer">
|
|
|
+ <el-button @click="closeDrawer">取消</el-button>
|
|
|
+ <el-button type="primary" @click="handleSubmit" :loading="submitLoading">保存</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-drawer>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup name="AddOrderStatusLogDrawer" lang="ts">
|
|
|
+import { ref, reactive } from 'vue';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+import { Plus, Delete } from '@element-plus/icons-vue';
|
|
|
+import { addOrderStatusLog } from '@/api/order/orderStatusLog';
|
|
|
+import FileSelector from '@/components/FileSelector/index.vue';
|
|
|
+
|
|
|
+const drawerVisible = ref(false);
|
|
|
+const submitLoading = ref(false);
|
|
|
+const formRef = ref<any>(null);
|
|
|
+const showFileSelector = ref(false);
|
|
|
+const selectedImages = ref<any[]>([]);
|
|
|
+
|
|
|
+// 表单数据
|
|
|
+const form = reactive({
|
|
|
+ orderNo: '',
|
|
|
+ orderId: undefined as string | number | undefined,
|
|
|
+ customerNo: '',
|
|
|
+ customerId: undefined as string | number | undefined,
|
|
|
+ deliverMethod: '0',
|
|
|
+ statusName: '',
|
|
|
+ logisticNos: '',
|
|
|
+ packageNo: '',
|
|
|
+ shipper: '',
|
|
|
+ operateTime: '',
|
|
|
+ courierPhone: '',
|
|
|
+ images: '',
|
|
|
+ status: '0'
|
|
|
+});
|
|
|
+
|
|
|
+// 状态顺序定义
|
|
|
+const statusOrder = ['已下单', '已揽件', '运输中', '派件中', '已签收'];
|
|
|
+
|
|
|
+// 当前订单状态
|
|
|
+const currentStatus = ref('');
|
|
|
+
|
|
|
+// 状态选项
|
|
|
+const statusOptions = [
|
|
|
+ { label: '已下单', value: '已下单' },
|
|
|
+ { label: '已揽件', value: '已揽件' },
|
|
|
+ { label: '运输中', value: '运输中' },
|
|
|
+ { label: '派件中', value: '派件中' },
|
|
|
+ { label: '已签收', value: '已签收' }
|
|
|
+];
|
|
|
+const rules = {
|
|
|
+ statusName: [{ required: true, message: '请选择状态名称', trigger: 'change' }]
|
|
|
+};
|
|
|
+
|
|
|
+// 打开抽屉
|
|
|
+const openDrawer = (orderData?: any, currentOrderStatus?: string) => {
|
|
|
+ if (orderData) {
|
|
|
+ form.orderNo = orderData.orderNo || '';
|
|
|
+ form.orderId = orderData.id;
|
|
|
+ form.shipper = orderData.deliverMan || '';
|
|
|
+ form.courierPhone = orderData.phone || '';
|
|
|
+ form.packageNo = orderData.deliverCode || '';
|
|
|
+ form.logisticNos = orderData.deliverCode || '';
|
|
|
+ form.customerNo = orderData.customerNo || '';
|
|
|
+ form.customerId = orderData.customerId;
|
|
|
+ form.operateTime = orderData.createTime || '';
|
|
|
+ form.deliverMethod = orderData.deliverMethod || '0';
|
|
|
+ form.statusName = currentOrderStatus || '';
|
|
|
+ form.images = orderData.images || '';
|
|
|
+
|
|
|
+ selectedImages.value = [];
|
|
|
+ if (form.images) {
|
|
|
+ const urls = form.images.split(',');
|
|
|
+ selectedImages.value = urls.filter((u) => u && u.trim()).map((u) => ({ url: u.trim(), name: 'Image' }));
|
|
|
+ }
|
|
|
+
|
|
|
+ currentStatus.value = currentOrderStatus || '';
|
|
|
+ }
|
|
|
+ drawerVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 关闭抽屉
|
|
|
+const closeDrawer = () => {
|
|
|
+ drawerVisible.value = false;
|
|
|
+ resetForm();
|
|
|
+};
|
|
|
+
|
|
|
+// 判断是否禁用某个状态选项
|
|
|
+const isStatusDisabled = (statusValue: string) => {
|
|
|
+ if (!currentStatus.value) return false;
|
|
|
+ const currentIndex = statusOrder.indexOf(currentStatus.value);
|
|
|
+ const targetIndex = statusOrder.indexOf(statusValue);
|
|
|
+ // 如果目标状态在当前状态之前(索引更小),则禁用
|
|
|
+ return targetIndex < currentIndex;
|
|
|
+};
|
|
|
+
|
|
|
+// 重置表单
|
|
|
+const resetForm = () => {
|
|
|
+ form.statusName = '';
|
|
|
+ form.logisticNos = '';
|
|
|
+ form.packageNo = '';
|
|
|
+ form.shipper = '';
|
|
|
+ form.operateTime = '';
|
|
|
+ form.courierPhone = '';
|
|
|
+ form.images = '';
|
|
|
+ selectedImages.value = [];
|
|
|
+ showFileSelector.value = false;
|
|
|
+ currentStatus.value = '';
|
|
|
+};
|
|
|
+
|
|
|
+// 处理选择的图片 (优化健壮性)
|
|
|
+const handleImagesSelected = (files: any[]) => {
|
|
|
+ if (!files || files.length === 0) {
|
|
|
+ showFileSelector.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const remainingSlots = 3 - selectedImages.value.length;
|
|
|
+ if (remainingSlots <= 0) {
|
|
|
+ ElMessage.warning('最多上传 3 张图片');
|
|
|
+ showFileSelector.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const filesToAdd = files.slice(0, remainingSlots);
|
|
|
+
|
|
|
+ // 映射为标准格式 { url, name }
|
|
|
+ const newItems = filesToAdd
|
|
|
+ .map((f) => ({
|
|
|
+ url: f.fileUrl || f.url || f.path, // 兼容多种返回字段
|
|
|
+ name: f.fileName || 'Image'
|
|
|
+ }))
|
|
|
+ .filter((item) => item.url); // 过滤掉没有 url 的无效项
|
|
|
+
|
|
|
+ if (newItems.length > 0) {
|
|
|
+ // 重新赋值数组以触发视图更新
|
|
|
+ selectedImages.value = [...selectedImages.value, ...newItems];
|
|
|
+ form.images = selectedImages.value.map((img) => img.url).join(',');
|
|
|
+ }
|
|
|
+
|
|
|
+ showFileSelector.value = false;
|
|
|
+};
|
|
|
+
|
|
|
+// 移除已选择的图片
|
|
|
+const removeImage = (index: number) => {
|
|
|
+ selectedImages.value.splice(index, 1);
|
|
|
+ form.images = selectedImages.value.map((img) => img.url).join(',');
|
|
|
+};
|
|
|
+
|
|
|
+// 状态变化处理
|
|
|
+const handleStatusChange = () => {
|
|
|
+ // 当状态变化时,如果不是已签收,清空图片
|
|
|
+ if (form.statusName !== '已签收') {
|
|
|
+ form.images = '';
|
|
|
+ selectedImages.value = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+const handleSubmit = async () => {
|
|
|
+ await formRef.value?.validate();
|
|
|
+ submitLoading.value = true;
|
|
|
+ try {
|
|
|
+ await addOrderStatusLog(form);
|
|
|
+ ElMessage.success('保存成功');
|
|
|
+ closeDrawer();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存失败:', error);
|
|
|
+ ElMessage.error('保存失败');
|
|
|
+ } finally {
|
|
|
+ submitLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 暴露方法给父组件
|
|
|
+defineExpose({
|
|
|
+ openDrawer
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.status-log-container {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.drawer-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+</style>
|