|
|
@@ -31,7 +31,7 @@
|
|
|
@click="emit('command', 'complete', order)">确认完成</el-button>
|
|
|
</template>
|
|
|
|
|
|
- <template v-if="[3, 4].includes(order.status)">
|
|
|
+ <template v-if="[3, 4].includes(order.status) && order.mode == 0">
|
|
|
<el-button icon="Notebook" @click="emit('care-summary', order)">护理小结</el-button>
|
|
|
</template>
|
|
|
|
|
|
@@ -241,9 +241,18 @@
|
|
|
<p class="p-desc">{{ step.desc }}</p>
|
|
|
<div class="p-media" v-if="step.media && step.media.length">
|
|
|
<div v-for="(item, i) in step.media" :key="i" class="media-item">
|
|
|
+ <!-- 图片类型 -->
|
|
|
<el-image v-if="item.type === 'image'" :src="item.url"
|
|
|
- :preview-src-list="step.media.map(m => m.url)" fit="cover"
|
|
|
+ :preview-src-list="step.media.filter(m => m.type === 'image').map(m => m.url)" fit="cover"
|
|
|
class="p-img" :preview-teleported="true" />
|
|
|
+
|
|
|
+ <!-- 视频类型:由于后端没给第一帧,这里采用 video 标签 preload 方式尝试展示 -->
|
|
|
+ <div v-else-if="item.type === 'video'" class="p-video-box" @click="openVideoPreview(item.url)">
|
|
|
+ <video :src="item.url" preload="metadata" class="p-img p-video"></video>
|
|
|
+ <div class="play-icon-overlay">
|
|
|
+ <el-icon><VideoPlay /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -274,15 +283,40 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-drawer>
|
|
|
+
|
|
|
+ <!-- 视频播放弹窗 -->
|
|
|
+ <el-dialog v-model="videoPreview.visible" title="视频播放" width="800px" append-to-body @closed="videoPreview.url = ''">
|
|
|
+ <div style="width: 100%; display: flex; justify-content: center; background: #000; border-radius: 4px; overflow: hidden;">
|
|
|
+ <video v-if="videoPreview.url" :src="videoPreview.url" controls autoplay style="max-width: 100%; max-height: 70vh;"></video>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed, watch } from 'vue'
|
|
|
+import { ref, reactive, computed, watch } from 'vue'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
import { getPet } from '@/api/archieves/pet'
|
|
|
import { getCustomer } from '@/api/archieves/customer'
|
|
|
import { listSubOrderLog } from '@/api/order/subOrderLog'
|
|
|
|
|
|
+// 视频判定辅助函数
|
|
|
+const isVideo = (url) => {
|
|
|
+ if (!url) return false;
|
|
|
+ const videoExts = ['.mp4', '.mov', '.avi', '.wmv', '.webm', '.ogg'];
|
|
|
+ return videoExts.some(ext => String(url).toLowerCase().endsWith(ext));
|
|
|
+}
|
|
|
+
|
|
|
+// 视频预览状态
|
|
|
+const videoPreview = reactive({
|
|
|
+ visible: false,
|
|
|
+ url: ''
|
|
|
+})
|
|
|
+
|
|
|
+const openVideoPreview = (url) => {
|
|
|
+ videoPreview.url = url;
|
|
|
+ videoPreview.visible = true;
|
|
|
+}
|
|
|
+
|
|
|
const props = defineProps({
|
|
|
visible: Boolean,
|
|
|
order: Object
|
|
|
@@ -481,18 +515,19 @@ const currentOrderSteps = computed(() => {
|
|
|
const serviceProgressSteps = computed(() => {
|
|
|
const list = fulfillerLogs.value || []
|
|
|
return list.map((i) => {
|
|
|
- const photos = (i?.photos || '')
|
|
|
- .split(',')
|
|
|
- .map(s => s.trim())
|
|
|
- .filter(Boolean)
|
|
|
- .map(url => ({ type: 'image', url }))
|
|
|
+ // 使用 photoUrls 展示,而非 photos
|
|
|
+ const media = (i?.photoUrls || []).map(url => {
|
|
|
+ const type = isVideo(url) ? 'video' : 'image';
|
|
|
+ return { type, url }
|
|
|
+ });
|
|
|
+
|
|
|
return {
|
|
|
title: i?.title || '--',
|
|
|
time: i?.createTime || i?.time || '',
|
|
|
- icon: undefined,
|
|
|
- color: '#ff9900',
|
|
|
+ icon: i.step === 4 || i.step === 99 ? 'CircleCheck' : undefined,
|
|
|
+ color: i.step === 4 || i.step === 99 ? '#67c23a' : '#ff9900',
|
|
|
desc: i?.content || '',
|
|
|
- media: photos
|
|
|
+ media: media
|
|
|
}
|
|
|
})
|
|
|
})
|
|
|
@@ -797,6 +832,40 @@ const handleExportLogs = () => {
|
|
|
border-radius: 4px;
|
|
|
border: 1px solid #e4e7ed;
|
|
|
cursor: pointer;
|
|
|
+ background: #f5f7fa;
|
|
|
+}
|
|
|
+
|
|
|
+.p-video-box {
|
|
|
+ position: relative;
|
|
|
+ width: 80px;
|
|
|
+ height: 80px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.p-video {
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+
|
|
|
+.play-icon-overlay {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ background: rgba(0,0,0,0.4);
|
|
|
+ color: #fff;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 18px;
|
|
|
+ transition: all 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.p-video-box:hover .play-icon-overlay {
|
|
|
+ background: rgba(0,0,0,0.6);
|
|
|
+ transform: translate(-50%, -50%) scale(1.1);
|
|
|
}
|
|
|
|
|
|
/* New Transport Split Styles */
|