|
|
@@ -1,16 +1,16 @@
|
|
|
<template>
|
|
|
<div class="app-container">
|
|
|
<el-tabs v-model="queryParams.orderStatus" @tab-click="onOrderStatusChange">
|
|
|
- <el-tab-pane v-for="item in status" :key="item.name" :label="item.label" :name="item.name"/>
|
|
|
+ <el-tab-pane v-for="item in status" :key="item.name" :label="item.label" :name="item.name" />
|
|
|
</el-tabs>
|
|
|
<el-form ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="68px">
|
|
|
<el-form-item prop="storageId">
|
|
|
<el-select v-model="queryParams.storageId" placeholder="请选择前置仓" clearable>
|
|
|
- <el-option v-for="item in storages" :key="item.id" :label="item.name" :value="item.id"/>
|
|
|
+ <el-option v-for="item in storages" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item prop="orderId">
|
|
|
- <el-input v-model="queryParams.orderId" clearable placeholder="输入订单号" @keyup.enter.native="toQuery"/>
|
|
|
+ <el-input v-model="queryParams.orderId" clearable placeholder="输入订单号" @keyup.enter.native="toQuery" />
|
|
|
</el-form-item>
|
|
|
<el-form-item prop="createTime">
|
|
|
<el-date-picker
|
|
|
@@ -39,12 +39,13 @@
|
|
|
width="200"
|
|
|
prop="orderId"
|
|
|
align="center"
|
|
|
- label="订单号">
|
|
|
+ label="订单号"
|
|
|
+ >
|
|
|
<template slot-scope="scope">
|
|
|
<el-tooltip content="点击复制订单号" placement="top">
|
|
|
<span ref="orderId" @click="copyOrderId(scope.row.orderId)">
|
|
|
{{ scope.row.orderId }}
|
|
|
- <span v-if="isToday(scope.row.createTime)" style="color: red">今</span>
|
|
|
+ <span v-if="isToday(scope.row.createTime)" style="color: red">今</span>
|
|
|
</span>+
|
|
|
</el-tooltip>
|
|
|
</template>
|
|
|
@@ -103,8 +104,8 @@
|
|
|
>
|
|
|
<template slot-scope="{row}">
|
|
|
<el-tag :type="row.shippingType==1 ? 'error' : 'success'">{{
|
|
|
- row.shippingType == 1 ? '配送' : '自提'
|
|
|
- }}
|
|
|
+ row.shippingType == 1 ? '配送' : '自提'
|
|
|
+ }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
@@ -244,7 +245,7 @@
|
|
|
@command="handleDeliveryCommand(row, $event)"
|
|
|
>
|
|
|
<el-button type="primary" size="mini">
|
|
|
- 配送方式<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ 配送方式<i class="el-icon-arrow-down el-icon--right" />
|
|
|
</el-button>
|
|
|
<el-dropdown-menu slot="dropdown">
|
|
|
<el-dropdown-item command="system">系统配送</el-dropdown-item>
|
|
|
@@ -271,7 +272,7 @@
|
|
|
@pagination="getList"
|
|
|
/>
|
|
|
<!-- 添加或修改订单对话框 -->
|
|
|
- <el-dialog title="订单详情" :visible.sync="open" width="1050px" append-to-body class="order-detail-dialog">
|
|
|
+ <el-dialog title="订单详情" :visible.sync="open" width="1050px" append-to-body class="order-detail-dialog" :custom-class ="order-detail-dialog">
|
|
|
<div class="detail-container">
|
|
|
<!-- 基本信息部分 -->
|
|
|
<div class="detail-section">
|
|
|
@@ -323,8 +324,8 @@
|
|
|
<div class="detail-item">
|
|
|
<span class="item-label">备注:</span>
|
|
|
<span class="item-value" :class="{'empty-remark': !form.remark}">
|
|
|
- {{ form.remark || '无备注' }}
|
|
|
- </span>
|
|
|
+ {{ form.remark || '无备注' }}
|
|
|
+ </span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -353,7 +354,6 @@
|
|
|
</el-row>
|
|
|
</div>
|
|
|
|
|
|
-
|
|
|
<!-- 骑手信息部分 -->
|
|
|
<div v-if="form.riderOrder" class="detail-section">
|
|
|
<h3 class="section-title">骑手信息</h3>
|
|
|
@@ -408,8 +408,8 @@
|
|
|
<div class="detail-item">
|
|
|
<span class="item-label">支付金额:</span>
|
|
|
<span class="item-value highlight">
|
|
|
- {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
|
|
|
- </span>
|
|
|
+ {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
|
|
|
+ </span>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
<el-col :span="8">
|
|
|
@@ -522,14 +522,38 @@
|
|
|
<div class="summary-row">
|
|
|
<span class="summary-label">优惠:</span>
|
|
|
<span class="summary-value highlight">
|
|
|
- ¥{{ Number(form.originalTotalPrice) - Number(form.payPrice) + Number(form.freightPrice) }}
|
|
|
- </span>
|
|
|
+ ¥{{ Number(form.originalTotalPrice) - Number(form.payPrice) + Number(form.freightPrice) }}
|
|
|
+ </span>
|
|
|
</div>
|
|
|
<div class="summary-row total-row">
|
|
|
<span class="summary-label">实付金额:</span>
|
|
|
<span class="summary-value highlight">
|
|
|
- {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
|
|
|
- </span>
|
|
|
+ {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 订单照片部分 -->
|
|
|
+ <div v-if="form.photos && form.photos.length > 0" class="detail-section">
|
|
|
+ <h3 class="section-title">订单照片</h3>
|
|
|
+ <div class="photo-gallery">
|
|
|
+ <!-- 按照片类型分组展示 -->
|
|
|
+ <div v-for="(groupData, groupType) in groupedPhotos" :key="groupType" class="photo-group">
|
|
|
+ <h4 class="photo-group-title">{{ getPhotoTypeLabel(groupType) + ' - ' + groupData[0].uploadTime }}</h4>
|
|
|
+ <div class="photo-grid">
|
|
|
+ <div
|
|
|
+ v-for="(photo) in groupData"
|
|
|
+ :key="photo.id"
|
|
|
+ class="photo-item"
|
|
|
+ @click="previewPhoto(photo.imageUrl, groupData)"
|
|
|
+ >
|
|
|
+ <img :src="photo.imageUrl" alt="订单照片" class="photo-thumbnail">
|
|
|
+ <div class="photo-overlay">
|
|
|
+ <i class="el-icon-zoom-in" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -577,20 +601,81 @@
|
|
|
>
|
|
|
<el-form>
|
|
|
<el-form-item label="核销码">
|
|
|
- <el-input v-model="verifyCode" placeholder="请输入核销码"></el-input>
|
|
|
+ <el-input v-model="verifyCode" placeholder="请输入核销码" />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<span slot="footer" class="dialog-footer">
|
|
|
- <el-button @click="verifyDialogVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="confirmCompletePickup">确认</el-button>
|
|
|
- </span>
|
|
|
+ <el-button @click="verifyDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmCompletePickup">确认</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 照片预览弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ :visible.sync="photoPreviewVisible"
|
|
|
+ :show-close="false"
|
|
|
+ :modal="true"
|
|
|
+ width="80%"
|
|
|
+ class="photo-preview-dialog"
|
|
|
+ @close="closePhotoPreview"
|
|
|
+ >
|
|
|
+ <div class="photo-preview-container">
|
|
|
+ <div class="photo-preview-header">
|
|
|
+ <span class="preview-title">{{ currentPhotoGroup.title }}</span>
|
|
|
+ <span class="preview-counter">{{ currentPhotoIndex + 1 }} / {{ currentPhotoGroup.photos.length }}</span>
|
|
|
+ <el-button
|
|
|
+ type="text"
|
|
|
+ class="close-btn"
|
|
|
+ icon="el-icon-close"
|
|
|
+ @click="closePhotoPreview"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="photo-preview-content">
|
|
|
+ <div class="photo-navigation">
|
|
|
+ <el-button
|
|
|
+ v-if="currentPhotoIndex > 0"
|
|
|
+ type="text"
|
|
|
+ class="nav-btn prev-btn"
|
|
|
+ icon="el-icon-arrow-left"
|
|
|
+ @click="prevPhoto"
|
|
|
+ />
|
|
|
+ <div class="main-photo">
|
|
|
+ <img
|
|
|
+ :src="currentPhoto.imageUrl"
|
|
|
+ :alt="currentPhoto.imageUrl"
|
|
|
+ class="preview-image"
|
|
|
+ @load="onImageLoad"
|
|
|
+ @error="onImageError"
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <el-button
|
|
|
+ v-if="currentPhotoIndex < currentPhotoGroup.photos.length - 1"
|
|
|
+ type="text"
|
|
|
+ class="nav-btn next-btn"
|
|
|
+ icon="el-icon-arrow-right"
|
|
|
+ @click="nextPhoto"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div v-if="currentPhotoGroup.photos.length > 1" class="photo-thumbnails">
|
|
|
+ <div
|
|
|
+ v-for="(photo, index) in currentPhotoGroup.photos"
|
|
|
+ :key="photo.id"
|
|
|
+ class="thumbnail-item"
|
|
|
+ :class="{ active: index === currentPhotoIndex }"
|
|
|
+ @click="setCurrentPhoto(index)"
|
|
|
+ >
|
|
|
+ <img :src="photo.imageUrl" :alt="photo.imageUrl" class="thumbnail-image">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import Clipboard from 'clipboard';
|
|
|
-import {multiply} from '@/utils/math'
|
|
|
+import Clipboard from 'clipboard'
|
|
|
+import { multiply } from '@/utils/math'
|
|
|
import {
|
|
|
listStoreOrder /** delStoreOrder, addStoreOrder, updateStoreOrder*/,
|
|
|
getStoreOrder,
|
|
|
@@ -603,8 +688,8 @@ import {
|
|
|
thirdPartyDistribution,
|
|
|
cancelOrder
|
|
|
} from '@/api/order/storeOrder'
|
|
|
-import {listAllStorage} from '@/api/storage/storage'
|
|
|
-import {getRiderByStorageId} from '@/api/rider/rider'
|
|
|
+import { listAllStorage } from '@/api/storage/storage'
|
|
|
+import { getRiderByStorageId } from '@/api/rider/rider'
|
|
|
|
|
|
export default {
|
|
|
name: 'StoreOrder',
|
|
|
@@ -616,6 +701,13 @@ export default {
|
|
|
verifyDialogVisible: false,
|
|
|
verifyCode: '',
|
|
|
currentPickupOrderId: null,
|
|
|
+ // 照片预览相关
|
|
|
+ photoPreviewVisible: false,
|
|
|
+ currentPhotoIndex: 0,
|
|
|
+ currentPhotoGroup: {
|
|
|
+ title: '',
|
|
|
+ photos: []
|
|
|
+ },
|
|
|
// 按钮loading
|
|
|
buttonLoading: false,
|
|
|
// 遮罩层
|
|
|
@@ -651,17 +743,17 @@ export default {
|
|
|
form: {},
|
|
|
// 表单校验
|
|
|
status: [
|
|
|
- {label: '待配货', name: '14'},
|
|
|
- {label: '配货中', name: '16'},
|
|
|
- {label: '待配送', name: '20'},
|
|
|
- {label: '配送中', name: '30'},
|
|
|
- {label: '已完成', name: '40,50'},
|
|
|
- {label: '配送异常', name: '32'},
|
|
|
- {label: '超时订单', name: '34'},
|
|
|
- {label: '待支付', name: '10'},
|
|
|
- {label: '已退款', name: '70'},
|
|
|
- {label: '已取消', name: '80'},
|
|
|
- {label: '全部订单', name: 'all'}
|
|
|
+ { label: '待配货', name: '14' },
|
|
|
+ { label: '配货中', name: '16' },
|
|
|
+ { label: '待配送', name: '20' },
|
|
|
+ { label: '配送中', name: '30' },
|
|
|
+ { label: '已完成', name: '40,50' },
|
|
|
+ { label: '配送异常', name: '32' },
|
|
|
+ { label: '超时订单', name: '34' },
|
|
|
+ { label: '待支付', name: '10' },
|
|
|
+ { label: '已退款', name: '70' },
|
|
|
+ { label: '已取消', name: '80' },
|
|
|
+ { label: '全部订单', name: 'all' }
|
|
|
],
|
|
|
storages: []
|
|
|
}
|
|
|
@@ -671,6 +763,32 @@ export default {
|
|
|
// const { modes, currentMode } = this.dialog
|
|
|
// return modes[currentMode]?.title || ''
|
|
|
// }
|
|
|
+
|
|
|
+ // 按照片类型分组
|
|
|
+ groupedPhotos() {
|
|
|
+ if (!this.form.photos || this.form.photos.length === 0) {
|
|
|
+ return {}
|
|
|
+ }
|
|
|
+
|
|
|
+ const groups = {}
|
|
|
+ this.form.photos.forEach(photo => {
|
|
|
+ const type = photo.screenshotType || 0
|
|
|
+ if (!groups[type]) {
|
|
|
+ groups[type] = []
|
|
|
+ }
|
|
|
+ groups[type].push(photo)
|
|
|
+ })
|
|
|
+
|
|
|
+ return groups
|
|
|
+ },
|
|
|
+
|
|
|
+ // 当前预览的照片
|
|
|
+ currentPhoto() {
|
|
|
+ if (this.currentPhotoGroup.photos && this.currentPhotoGroup.photos.length > 0) {
|
|
|
+ return this.currentPhotoGroup.photos[this.currentPhotoIndex] || {}
|
|
|
+ }
|
|
|
+ return {}
|
|
|
+ }
|
|
|
},
|
|
|
created() {
|
|
|
this.getList()
|
|
|
@@ -679,7 +797,7 @@ export default {
|
|
|
methods: {
|
|
|
multiply,
|
|
|
listAllStorage() {
|
|
|
- listAllStorage().then(({data}) => {
|
|
|
+ listAllStorage().then(({ data }) => {
|
|
|
this.storages = data
|
|
|
this.storages.forEach(storage => {
|
|
|
this.storageMap[storage.id] = storage.name
|
|
|
@@ -687,24 +805,24 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
isToday(dateString) {
|
|
|
- if (!dateString) return false;
|
|
|
- const orderDate = new Date(dateString);
|
|
|
- const today = new Date();
|
|
|
+ if (!dateString) return false
|
|
|
+ const orderDate = new Date(dateString)
|
|
|
+ const today = new Date()
|
|
|
|
|
|
return (
|
|
|
orderDate.getFullYear() === today.getFullYear() &&
|
|
|
orderDate.getMonth() === today.getMonth() &&
|
|
|
orderDate.getDate() === today.getDate()
|
|
|
- );
|
|
|
+ )
|
|
|
},
|
|
|
getRiderStatusTagType(status) {
|
|
|
const typeMap = {
|
|
|
- 0: 'warning', // 待取货 - 黄色
|
|
|
- 1: 'primary', // 配送中 - 蓝色
|
|
|
- 2: 'danger', // 超时 - 红色
|
|
|
- 3: 'danger', // 配送异常 - 红色
|
|
|
- 4: 'success', // 已完成 - 绿色
|
|
|
- 5: 'info' // 被更改分配 - 灰色
|
|
|
+ 0: 'warning', // 待取货 - 黄色
|
|
|
+ 1: 'primary', // 配送中 - 蓝色
|
|
|
+ 2: 'danger', // 超时 - 红色
|
|
|
+ 3: 'danger', // 配送异常 - 红色
|
|
|
+ 4: 'success', // 已完成 - 绿色
|
|
|
+ 5: 'info' // 被更改分配 - 灰色
|
|
|
}
|
|
|
return typeMap[status] || ''
|
|
|
},
|
|
|
@@ -720,9 +838,9 @@ export default {
|
|
|
return statusMap[status] || '未知状态'
|
|
|
},
|
|
|
showVerifyDialog(orderId) {
|
|
|
- this.currentPickupOrderId = orderId;
|
|
|
- this.verifyDialogVisible = true;
|
|
|
- this.verifyCode = '';
|
|
|
+ this.currentPickupOrderId = orderId
|
|
|
+ this.verifyDialogVisible = true
|
|
|
+ this.verifyCode = ''
|
|
|
},
|
|
|
// In methods section
|
|
|
handleThirdPartyDistribution(row) {
|
|
|
@@ -758,20 +876,20 @@ export default {
|
|
|
handleDeliveryCommand(row, command) {
|
|
|
switch (command) {
|
|
|
case 'system':
|
|
|
- this.allot(row.storeId, row.orderId, row.postId);
|
|
|
- break;
|
|
|
+ this.allot(row.storeId, row.orderId, row.postId)
|
|
|
+ break
|
|
|
case 'thirdParty':
|
|
|
- this.handleOperation(row.id, 'thirdPartyDistribution');
|
|
|
- break;
|
|
|
+ this.handleOperation(row.id, 'thirdPartyDistribution')
|
|
|
+ break
|
|
|
case 'merchant':
|
|
|
- this.handleOperation(row.id, 'merchantDistribution');
|
|
|
- break;
|
|
|
+ this.handleOperation(row.id, 'merchantDistribution')
|
|
|
+ break
|
|
|
}
|
|
|
},
|
|
|
async confirmCompletePickup() {
|
|
|
if (!this.verifyCode) {
|
|
|
- this.$message.warning('请输入核销码');
|
|
|
- return;
|
|
|
+ this.$message.warning('请输入核销码')
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
@@ -780,11 +898,11 @@ export default {
|
|
|
await completePickup({
|
|
|
id: this.currentPickupOrderId,
|
|
|
verifyCode: this.verifyCode
|
|
|
- });
|
|
|
+ })
|
|
|
|
|
|
- this.$message.success('核销成功');
|
|
|
- this.verifyDialogVisible = false;
|
|
|
- this.handleQuery();
|
|
|
+ this.$message.success('核销成功')
|
|
|
+ this.verifyDialogVisible = false
|
|
|
+ this.handleQuery()
|
|
|
} finally {
|
|
|
this.loading = false
|
|
|
}
|
|
|
@@ -792,26 +910,26 @@ export default {
|
|
|
copyOrderId(orderId) {
|
|
|
const clipboard = new Clipboard(this.$refs.orderId, {
|
|
|
text: () => orderId
|
|
|
- });
|
|
|
+ })
|
|
|
|
|
|
clipboard.on('success', () => {
|
|
|
- this.$message.success('订单号已复制');
|
|
|
- clipboard.destroy(); // 销毁 clipboard 实例
|
|
|
- });
|
|
|
+ this.$message.success('订单号已复制')
|
|
|
+ clipboard.destroy() // 销毁 clipboard 实例
|
|
|
+ })
|
|
|
|
|
|
clipboard.on('error', () => {
|
|
|
- this.$message.error('复制失败');
|
|
|
- clipboard.destroy(); // 销毁 clipboard 实例
|
|
|
- });
|
|
|
+ this.$message.error('复制失败')
|
|
|
+ clipboard.destroy() // 销毁 clipboard 实例
|
|
|
+ })
|
|
|
|
|
|
// 手动触发点击事件
|
|
|
- this.$refs.orderId.click();
|
|
|
+ this.$refs.orderId.click()
|
|
|
},
|
|
|
/** 查询订单列表 */
|
|
|
getList() {
|
|
|
this.loading = true
|
|
|
this.storeOrderList = []
|
|
|
- const params = {...this.queryParams}
|
|
|
+ const params = { ...this.queryParams }
|
|
|
if (params.createTime) {
|
|
|
params.startTime = params.createTime[0]
|
|
|
params.endTime = params.createTime[1]
|
|
|
@@ -843,7 +961,7 @@ export default {
|
|
|
this.handleQuery()
|
|
|
},
|
|
|
viewDetail(id) {
|
|
|
- getStoreOrder(id).then(({data}) => {
|
|
|
+ getStoreOrder(id).then(({ data }) => {
|
|
|
this.form = data
|
|
|
this.open = true
|
|
|
})
|
|
|
@@ -882,18 +1000,18 @@ export default {
|
|
|
},
|
|
|
getStatusTagType(status) {
|
|
|
const typeMap = {
|
|
|
- 14: 'warning', // 待配货 - 黄色
|
|
|
- 16: 'warning', // 配货中 - 黄色
|
|
|
- 20: 'primary', // 待配送 - 蓝色
|
|
|
- 30: 'primary', // 配送中 - 蓝色
|
|
|
- 40: 'success', // 已完成 - 绿色
|
|
|
- 50: 'success', // 已完成 - 绿色
|
|
|
- 32: 'danger', // 配送异常 - 红色
|
|
|
- 34: 'danger', // 超时订单 - 红色
|
|
|
- 10: 'info', // 待支付 - 灰色
|
|
|
- 70: 'danger', // 已退款 - 红色
|
|
|
- 80: 'info', // 已取消 - 灰色
|
|
|
- 90: 'info' // 已取消(系统) - 灰色
|
|
|
+ 14: 'warning', // 待配货 - 黄色
|
|
|
+ 16: 'warning', // 配货中 - 黄色
|
|
|
+ 20: 'primary', // 待配送 - 蓝色
|
|
|
+ 30: 'primary', // 配送中 - 蓝色
|
|
|
+ 40: 'success', // 已完成 - 绿色
|
|
|
+ 50: 'success', // 已完成 - 绿色
|
|
|
+ 32: 'danger', // 配送异常 - 红色
|
|
|
+ 34: 'danger', // 超时订单 - 红色
|
|
|
+ 10: 'info', // 待支付 - 灰色
|
|
|
+ 70: 'danger', // 已退款 - 红色
|
|
|
+ 80: 'info', // 已取消 - 灰色
|
|
|
+ 90: 'info' // 已取消(系统) - 灰色
|
|
|
}
|
|
|
return typeMap[status] || ''
|
|
|
},
|
|
|
@@ -933,7 +1051,7 @@ export default {
|
|
|
thirdPartyDistribution,
|
|
|
cancelOrder
|
|
|
}
|
|
|
- operations[operation]({id}).then(() => {
|
|
|
+ operations[operation]({ id }).then(() => {
|
|
|
this.$message({
|
|
|
message: '操作成功',
|
|
|
type: 'success'
|
|
|
@@ -942,7 +1060,7 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
check(val) {
|
|
|
- const loadingInstance = this.$loading({text: '正在分配中...'})
|
|
|
+ const loadingInstance = this.$loading({ text: '正在分配中...' })
|
|
|
distributeOrder({
|
|
|
orderNo: this.orderId,
|
|
|
riderId: val
|
|
|
@@ -957,12 +1075,77 @@ export default {
|
|
|
}).finally(() => {
|
|
|
loadingInstance.close()
|
|
|
})
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取照片类型标签
|
|
|
+ getPhotoTypeLabel(type) {
|
|
|
+ const typeMap = {
|
|
|
+ 0: '其他',
|
|
|
+ 1: '创建订单',
|
|
|
+ 2: '服务前',
|
|
|
+ 3: '服务中',
|
|
|
+ 4: '服务完成',
|
|
|
+ 5: '问题反馈'
|
|
|
+ }
|
|
|
+ return typeMap[type] || '其他'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 预览照片
|
|
|
+ previewPhoto(currentUrl, photoGroup) {
|
|
|
+ const currentIndex = photoGroup.findIndex(photo => photo.imageUrl === currentUrl)
|
|
|
+
|
|
|
+ this.currentPhotoGroup = {
|
|
|
+ title: this.getPhotoTypeLabel(photoGroup[0].screenshotType),
|
|
|
+ photos: photoGroup
|
|
|
+ }
|
|
|
+ this.currentPhotoIndex = Math.max(0, currentIndex)
|
|
|
+ this.photoPreviewVisible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ // 关闭照片预览
|
|
|
+ closePhotoPreview() {
|
|
|
+ this.photoPreviewVisible = false
|
|
|
+ this.currentPhotoIndex = 0
|
|
|
+ this.currentPhotoGroup = {
|
|
|
+ title: '',
|
|
|
+ photos: []
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 上一张照片
|
|
|
+ prevPhoto() {
|
|
|
+ if (this.currentPhotoIndex > 0) {
|
|
|
+ this.currentPhotoIndex--
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 下一张照片
|
|
|
+ nextPhoto() {
|
|
|
+ if (this.currentPhotoIndex < this.currentPhotoGroup.photos.length - 1) {
|
|
|
+ this.currentPhotoIndex++
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 设置当前照片
|
|
|
+ setCurrentPhoto(index) {
|
|
|
+ this.currentPhotoIndex = index
|
|
|
+ },
|
|
|
+
|
|
|
+ // 图片加载完成
|
|
|
+ onImageLoad() {
|
|
|
+ // 图片加载完成的处理
|
|
|
+ },
|
|
|
+
|
|
|
+ // 图片加载失败
|
|
|
+ onImageError() {
|
|
|
+ this.$message.error('图片加载失败')
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
.order-detail-dialog {
|
|
|
+
|
|
|
.detail-container {
|
|
|
padding: 0 10px;
|
|
|
|
|
|
@@ -1058,4 +1241,206 @@ export default {
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/* 照片相关样式 */
|
|
|
+.photo-gallery {
|
|
|
+ .photo-group {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .photo-group-title {
|
|
|
+ margin: 0 0 12px 0;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ font-weight: 500;
|
|
|
+ border-left: 3px solid #409eff;
|
|
|
+ padding-left: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .photo-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
|
+ gap: 15px;
|
|
|
+
|
|
|
+ .photo-item {
|
|
|
+ position: relative;
|
|
|
+ width: 120px;
|
|
|
+ height: 120px;
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+ border: 2px solid #e5e5e5;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #409eff;
|
|
|
+ transform: scale(1.05);
|
|
|
+
|
|
|
+ .photo-overlay {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .photo-thumbnail {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+
|
|
|
+ .photo-overlay {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ opacity: 0;
|
|
|
+ transition: opacity 0.3s ease;
|
|
|
+
|
|
|
+ i {
|
|
|
+ font-size: 24px;
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 照片预览弹窗样式 */
|
|
|
+.photo-preview-dialog {
|
|
|
+ ::v-deep .el-dialog {
|
|
|
+ margin-top: 5vh !important;
|
|
|
+ background-color: #000;
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-dialog__body {
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .photo-preview-container {
|
|
|
+ height: 80vh;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .photo-preview-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 15px 20px;
|
|
|
+ background-color: rgba(255, 255, 255, 0.1);
|
|
|
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
|
|
+
|
|
|
+ .preview-title {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-counter {
|
|
|
+ color: #ccc;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .close-btn {
|
|
|
+ color: #fff !important;
|
|
|
+ font-size: 20px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #f56c6c !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .photo-preview-content {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .photo-navigation {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ .nav-btn {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ z-index: 10;
|
|
|
+ width: 50px;
|
|
|
+ height: 50px;
|
|
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
+ border-radius: 50%;
|
|
|
+ color: #fff !important;
|
|
|
+ font-size: 20px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: rgba(0, 0, 0, 0.8);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.prev-btn {
|
|
|
+ left: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.next-btn {
|
|
|
+ right: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .main-photo {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 80px;
|
|
|
+
|
|
|
+ .preview-image {
|
|
|
+ max-width: 100%;
|
|
|
+ max-height: 60vh;
|
|
|
+ object-fit: contain;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .photo-thumbnails {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 10px;
|
|
|
+ padding: 20px;
|
|
|
+ background-color: rgba(255, 255, 255, 0.05);
|
|
|
+ overflow-x: auto;
|
|
|
+
|
|
|
+ .thumbnail-item {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+ border: 2px solid transparent;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ border-color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #66b1ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .thumbnail-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|