feat: base 增加xtUpload组件

This commit is contained in:
caoqianming 2025-12-05 10:58:09 +08:00
parent 115869eace
commit 65777783a6
1 changed files with 278 additions and 0 deletions

278
components/xtUpload.vue Normal file
View File

@ -0,0 +1,278 @@
<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: undefined },
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(() => {
disabled.value = true
checkPropsUntilReady()
})
const checkPropsUntilReady = () => {
if (inited.value) return
if (props.modelValue !== undefined) {
initFileList()
if (props.disabled == false) {
disabled.value = false
}
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,
})
}
}
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>