<template>
|
<u-popup :show="dialogVisitable"
|
mode="bottom"
|
:round="16"
|
@close="cancel">
|
<view class="popup-content">
|
<view class="popup-header">
|
<text class="popup-title">查看附件</text>
|
<view class="close-icon"
|
@click="cancel">
|
<u-icon name="close"
|
size="14"
|
color="#666" />
|
</view>
|
</view>
|
<view class="popup-body">
|
<view class="tabs">
|
<view v-for="tab in tabs"
|
:key="tab.key"
|
class="tab-item"
|
:class="{ active: currentType === tab.key }"
|
@click="currentType = tab.key">
|
{{ tab.label }} ({{ getCurrentList(tab.key).length }})
|
</view>
|
</view>
|
<view class="file-list"
|
v-if="getCurrentList(currentType).length">
|
<view v-for="(file, index) in getCurrentList(currentType)"
|
:key="index"
|
class="file-item"
|
@click="previewFile(file)">
|
<image v-if="isImageFile(file)"
|
:src="file.url"
|
class="thumb"
|
mode="aspectFill" />
|
<view v-else
|
class="video-thumb">
|
<u-icon name="video"
|
size="28"
|
color="#1677ff" />
|
<text class="video-text">视频</text>
|
</view>
|
<text class="name">{{ file.name || "附件" }}</text>
|
</view>
|
</view>
|
<view v-else
|
class="empty">
|
<text>暂无附件</text>
|
</view>
|
</view>
|
</view>
|
</u-popup>
|
<u-popup :show="showVideoPopup"
|
mode="center"
|
:round="10"
|
@close="closeVideoPopup">
|
<view class="video-container">
|
<video :src="videoUrl"
|
controls
|
autoplay
|
class="video-player" />
|
</view>
|
</u-popup>
|
</template>
|
|
<script setup>
|
import { ref } from "vue";
|
import config from "@/config";
|
|
const dialogVisitable = ref(false);
|
const currentType = ref("before");
|
const showVideoPopup = ref(false);
|
const videoUrl = ref("");
|
const filesMap = ref({
|
before: [],
|
after: [],
|
issue: [],
|
});
|
|
const tabs = [
|
{ key: "before", label: "生产前" },
|
{ key: "after", label: "生产中" },
|
{ key: "issue", label: "生产后" },
|
];
|
|
const normalizeUrl = raw => {
|
if (!raw) return "";
|
const url = String(raw).trim();
|
if (!url) return "";
|
if (/^https?:\/\//i.test(url)) return url;
|
if (url.startsWith("/")) return `${config.fileUrl}${url}`;
|
if (/^[a-zA-Z]:\\/.test(url)) {
|
const normalized = url.replace(/\\/g, "/");
|
const idx = normalized.indexOf("/prod/");
|
if (idx >= 0) {
|
const relative = normalized.slice(idx + "/prod/".length);
|
return `${config.fileUrl}/${relative}`;
|
}
|
return normalized;
|
}
|
return `${config.fileUrl}/${url.replace(/^\//, "")}`;
|
};
|
|
const isImageFile = file => {
|
if (file?.contentType?.startsWith("image/")) return true;
|
const name = String(file?.name || file?.bucketFilename || "").toLowerCase();
|
return /\.(jpg|jpeg|png|gif|bmp|webp)$/.test(name);
|
};
|
|
const normalizeFile = (file, type) => ({
|
...file,
|
type,
|
url: normalizeUrl(file?.url || file?.downloadUrl),
|
name: file?.originalFilename || file?.bucketFilename || file?.name,
|
});
|
|
const getCurrentList = type => filesMap.value[type] || [];
|
|
const previewFile = file => {
|
if (isImageFile(file)) {
|
const urls = getCurrentList(currentType.value)
|
.filter(item => isImageFile(item))
|
.map(item => item.url);
|
uni.previewImage({
|
urls,
|
current: file.url,
|
});
|
return;
|
}
|
videoUrl.value = file.url;
|
showVideoPopup.value = true;
|
};
|
|
const closeVideoPopup = () => {
|
showVideoPopup.value = false;
|
videoUrl.value = "";
|
};
|
|
const openDialog = row => {
|
const allList = Array.isArray(row?.commonFileList) ? row.commonFileList : [];
|
const beforeList = Array.isArray(row?.commonFileListBefore)
|
? row.commonFileListBefore
|
: allList.filter(item => item?.type === 10);
|
const afterList = Array.isArray(row?.commonFileListAfter)
|
? row.commonFileListAfter
|
: allList.filter(item => item?.type === 11);
|
const issueList = Array.isArray(row?.commonFileListIssue)
|
? row.commonFileListIssue
|
: allList.filter(item => item?.type === 12);
|
filesMap.value = {
|
before: beforeList.map(item => normalizeFile(item, "before")).filter(item => item.url),
|
after: afterList.map(item => normalizeFile(item, "after")).filter(item => item.url),
|
issue: issueList.map(item => normalizeFile(item, "issue")).filter(item => item.url),
|
};
|
currentType.value = "before";
|
dialogVisitable.value = true;
|
};
|
|
const cancel = () => {
|
dialogVisitable.value = false;
|
closeVideoPopup();
|
filesMap.value = {
|
before: [],
|
after: [],
|
issue: [],
|
};
|
};
|
|
defineExpose({ openDialog });
|
</script>
|
|
<style scoped lang="scss">
|
.popup-content {
|
width: 100vw;
|
max-height: 82vh;
|
background: #fff;
|
border-radius: 24rpx 24rpx 0 0;
|
overflow: hidden;
|
padding-bottom: env(safe-area-inset-bottom);
|
}
|
|
.popup-header {
|
padding: 24rpx 20rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
text-align: center;
|
position: relative;
|
}
|
|
.popup-title {
|
font-size: 32rpx;
|
color: #1f1f1f;
|
font-weight: 600;
|
}
|
|
.popup-body {
|
padding: 20rpx 20rpx 26rpx;
|
max-height: 68vh;
|
overflow-y: auto;
|
}
|
|
.tabs {
|
display: flex;
|
background: #f4f5f8;
|
border-radius: 12rpx;
|
padding: 6rpx;
|
margin-bottom: 20rpx;
|
}
|
|
.tab-item {
|
flex: 1;
|
text-align: center;
|
padding: 12rpx 0;
|
color: #666;
|
font-size: 24rpx;
|
border-radius: 10rpx;
|
}
|
|
.tab-item.active {
|
background: #1677ff;
|
color: #fff;
|
font-weight: 600;
|
}
|
|
.file-list {
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
gap: 16rpx;
|
}
|
|
.file-item {
|
background: #fafafa;
|
border-radius: 12rpx;
|
padding: 10rpx;
|
}
|
|
.thumb {
|
width: 100%;
|
height: 180rpx;
|
border-radius: 8rpx;
|
}
|
|
.video-thumb {
|
width: 100%;
|
height: 180rpx;
|
border-radius: 8rpx;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
background: #edf3ff;
|
}
|
|
.video-text {
|
font-size: 22rpx;
|
color: #1677ff;
|
margin-top: 6rpx;
|
}
|
|
.name {
|
margin-top: 8rpx;
|
font-size: 22rpx;
|
color: #333;
|
display: block;
|
word-break: break-all;
|
}
|
|
.empty {
|
text-align: center;
|
color: #999;
|
padding: 40rpx 0;
|
}
|
|
.video-container {
|
width: 94vw;
|
background: #000;
|
}
|
|
.video-player {
|
width: 94vw;
|
height: 55vw;
|
}
|
|
.close-icon {
|
position: absolute;
|
right: 24rpx;
|
top: 50%;
|
transform: translateY(-50%);
|
width: 44rpx;
|
height: 44rpx;
|
border-radius: 50%;
|
background: #f5f5f5;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
</style>
|