factory_mp/components/xtUpload.vue

304 lines
7.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view style="width: 100%;">
<button @click="choseFiles" size="mini" type="primary" :disabled="disabled">选择</button>
<view v-for="(item, index) in fileList" :key="index" style="height: 16px; margin-bottom: 12px;">
<view style="display:flex; justify-content: space-between;">
<view style="color:blue" @click="preview(item)">{{ item.name }}</view>
<view style="color:red;font-weight: bold;" @click="handleRemove(index)" v-if="disabled == false">×</view>
</view>
<view style="height:2px"></view>
<progress :percent="item.percent" activeColor="#4caf50" backgroundColor="#e0e0e0" stroke-width="2" />
</view>
</view>
</template>
<script setup>
// FILE_TYPE_DOC = 10
// FILE_TYPE_VIDEO = 20
// FILE_TYPE_AUDIO = 30
// FILE_TYPE_PIC = 40
// FILE_TYPE_OTHER = 50
import config from '@/utils/config.js'
import { ref, onMounted, watch, defineEmits } from 'vue';
const emit = defineEmits(["update:modelValue", "update:obj"]);
const props = defineProps({
xtype: { type: String, default: "path" }, // path 或 id
modelValue: { type: [Array, String], default: "pending" },
obj: { type: Object, default: () => ({}) },
accept: { type: String, default: '' },
countMax: { type: Number, default: 1 },
sizeMax: { type: Number, default: 1024 * 1024 * 5 },
disabled: { type: Boolean, default: false },
});
// ⚠ reactive([]) 不能整体赋值,用 ref 更合理
const fileList = ref([])
const disabled = ref(props.disabled)
// header / action
const header = {
Authorization: `Bearer ${uni.getStorageSync('access')}`
}
const action = `${config.baseUrl}/file/`
const inited = ref(false)
onMounted(() => {
checkPropsUntilReady()
})
const checkPropsUntilReady = () => {
if (inited.value) return
if (props.modelValue !== "pending") {
initFileList()
inited.value = true
return
}
// ⭐ 每 200ms 查一次(非常轻量)
setTimeout(checkPropsUntilReady, 200)
}
const changeVal = () => {
let val = null;
if (props.xtype == "id") {
if (props.countMax > 1){
val= []
fileList.value.forEach(item => {
if(item.id){
val.push(item.id)
}
})
}else if (props.countMax == 1){
val = null;
if (fileList.value && fileList.value.length > 0 && fileList.value[0].id) {
val = fileList.value[0].id
}
}
}else if (props.xtype == "path"){
if (props.countMax > 1){
val = []
fileList.value.forEach(item => {
if(item.path && item.percent == 100){
val.push(item.path)
}
})
}else if (props.countMax == 1){
val = null;
if (fileList.value && fileList.value.length > 0 && fileList.value[0].path && fileList.value[0].percent == 100) {
val = fileList.value[0].path
}
}
}
let obj = null;
emit("update:modelValue", val)
if (props.countMax > 1) {
obj = fileList.value
emit("update:obj", obj)
}else{
if (fileList.value && fileList.value.length > 0) {
obj = fileList.value[0]
emit("update:obj", obj)
} else {
emit("update:obj", obj)
}
}
console.log(val, obj)
}
const getFileType = (path) =>{
let type = path.substring(path.lastIndexOf('.') + 1, path.length).toLowerCase()
if (type === 'png' || type === 'jpg' || type === 'jpeg' || type === 'gif' || type === 'bmp') {
return 40
}
else if (type === 'mp4' || type === 'avi' || type === 'rmvb') {
return 20
}
else if (type === 'mp3' || type === 'wav' || type === 'wma') {
return 30
}
return 10
}
/**
* 初始化 fileList
*/
const initFileList = () => {
if (props.countMax > 1) {
let clist = []
// xtype = id 情况
if (props.xtype === "id" && Array.isArray(props.obj) && props.obj.length > 0) {
props.obj.forEach(item => {
clist.push({
...item,
url: `${config.hostUrl}${item.path}`,
percent: 100
})
})
fileList.value = clist
}
else if (props.xtype === "path" && props.modelValue) {
props.modelValue.forEach(item => {
clist.push({
path: item,
url: `${config.hostUrl}${item}`,
percent: 100,
type: getFileType(item)
})
})
fileList.value = clist
}
} else if (props.countMax == 1) {
let clist = []
if (props.xtype === "id" && props.modelValue) {
clist.push({
...props.obj,
url: `${config.hostUrl}${props.obj.path}`,
percent: 100
})
fileList.value = clist
} else if (props.xtype === "path" && props.modelValue) {
clist.push({
path: props.modelValue,
url: `${config.hostUrl}${props.modelValue}`,
percent: 100,
type: getFileType(props.obj.path)
})
fileList.value = clist
}
}
}
const handleRemove = (index) => {
let upTask = fileList.value[index].upTask
if (upTask) {
upTask.abort()
}
fileList.value.splice(index, 1)
changeVal()
}
const preview = (item) => {
if (item.type == 40) {
// 获取所有类型为 40 的图片 URL
const urls = fileList.value
.filter(file => file.type === 40)
.map(file => file.url)
// 找到当前图片的索引
const currentIndex = urls.indexOf(item.url)
uni.previewImage({
current: currentIndex >= 0 ? currentIndex : 0,
urls: urls,
})
} else if (item.type == 10) {
uni.downloadFile({
url: item.url,
success: function (res) {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: filePath,
showMenu: true,
success: function (res) {
console.log('打开文档成功');
}
});
}
});
} else if (item.type == 20) {
uni.showToast({
title: "暂不支持预览该文件",
icon : 'none'
})
} else if (item.type == 30) {
uni.showToast({
title: "暂不支持预览该文件",
icon : 'none'
})
} else {
uni.showToast({
title: "暂不支持预览该文件",
icon : 'none'
})
}
}
const choseFiles = () => {
if (fileList.value.length >= props.countMax) {
uni.showToast({
title: `最多只能上传${props.countMax}个文件`,
icon: 'none'
})
return
}
uni.chooseFile({
count: props.countMax - fileList.value.length,
success(res) {
const tempFiles = res.tempFiles;
tempFiles.forEach(item => {
// 文件大小校验(单位:字节)
if (item.size > props.sizeMax * 1024 * 1024) {
uni.showToast({
title: `文件大小不能超过${props.sizeMax}M`,
icon: 'none'
})
return;
}
// 创建上传任务
const upTask = uni.uploadFile({
url: action,
filePath: item.path,
name: 'file',
formData: {},
header: header,
success: (uploadRes) => {
const data = JSON.parse(uploadRes.data || "{}")
if (data) {
fileList.value.forEach((file, index) => {
if (file.path === item.path) {
fileList.value[index].url = `${config.hostUrl}${data.path}`
fileList.value[index].path = data.path
fileList.value[index].percent = 100
fileList.value[index].id = data.id
fileList.value[index].upTask = null
}
})
// console.log("fv", fileList.value)
changeVal()
}
},
fail: (err) => {
console.log("上传失败:", err)
}
})
// 添加到列表
fileList.value.push({
name: item.name,
url: null,
path: item.path,
percent: 0,
type: getFileType(item.name),
upTask: upTask
})
// 🔥 关键修复:关联上传进度
upTask.onProgressUpdate((res) => {
fileList.value.forEach((file, index) => {
if (file.path === item.path) {
fileList.value[index].percent = res.progress
}
})
});
})
}
})
}
</script>