格式化代码,websocket功能完善

This commit is contained in:
2026-02-18 21:50:05 +08:00
parent 6543e2ccdd
commit b6c133952b
101 changed files with 15829 additions and 10739 deletions
+170 -137
View File
@@ -8,7 +8,10 @@
:accept="accept"
:max-count="maxCount"
:disabled="disabled"
:show-upload-list="{ showPreviewIcon: true, showRemoveIcon: !disabled }"
:show-upload-list="{
showPreviewIcon: true,
showRemoveIcon: !disabled,
}"
@preview="handlePreview"
@change="handleChange"
@drop="handleDrop"
@@ -17,10 +20,15 @@
class="custom-upload"
:class="{ 'drag-over': isDragOver }"
>
<div v-if="fileList.length < maxCount && !disabled" class="upload-area">
<div
v-if="fileList.length < maxCount && !disabled"
class="upload-area"
>
<loading-outlined v-if="uploading" class="upload-icon" />
<plus-outlined v-else class="upload-icon" />
<div class="ant-upload-text">{{ uploading ? '上传中...' : uploadText }}</div>
<div class="ant-upload-text">
{{ uploading ? "上传中..." : uploadText }}
</div>
<div v-if="tip" class="ant-upload-tip">{{ tip }}</div>
</div>
</a-upload>
@@ -31,314 +39,339 @@
:width="800"
@cancel="handleCancel"
>
<img alt="图片预览" style="width: 100%; max-height: 600px; object-fit: contain;" :src="previewImage" />
<img
alt="图片预览"
style="width: 100%; max-height: 600px; object-fit: contain"
:src="previewImage"
/>
</a-modal>
</div>
</template>
<script setup>
import { ref, watch, computed } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'
import uploadConfig from '@/config/upload'
import { ref, watch, computed } from "vue";
import { message, Modal } from "ant-design-vue";
import { PlusOutlined, LoadingOutlined } from "@ant-design/icons-vue";
import uploadConfig from "@/config/upload";
const props = defineProps({
// 图片列表
modelValue: {
type: [Array, String],
default: () => []
default: () => [],
},
// 最大上传数量,默认1为单图上传
maxCount: {
type: Number,
default: 1
default: 1,
},
// 接受的文件类型
accept: {
type: String,
default: 'image/*'
default: "image/*",
},
// 是否禁用
disabled: {
type: Boolean,
default: false
default: false,
},
// 是否返回URL字符串(单图)或URL数组(多图)
returnUrl: {
type: Boolean,
default: true
default: true,
},
// 上传按钮文字
uploadText: {
type: String,
default: '上传图片'
default: "上传图片",
},
// 提示文字
tip: {
type: String,
default: ''
default: "",
},
// 最小宽度(像素)
minWidth: {
type: Number,
default: 0
default: 0,
},
// 最大宽度(像素)
maxWidth: {
type: Number,
default: 0
default: 0,
},
// 最小高度(像素)
minHeight: {
type: Number,
default: 0
default: 0,
},
// 最大高度(像素)
maxHeight: {
type: Number,
default: 0
default: 0,
},
// 是否删除前确认
confirmBeforeRemove: {
type: Boolean,
default: false
default: false,
},
// 自定义上传按钮内容
customUploadBtn: {
type: Function,
default: null
}
})
default: null,
},
});
const emit = defineEmits(['update:modelValue', 'change', 'preview', 'remove', 'uploadSuccess', 'uploadError'])
const emit = defineEmits([
"update:modelValue",
"change",
"preview",
"remove",
"uploadSuccess",
"uploadError",
]);
// 文件列表
const fileList = ref([])
const fileList = ref([]);
// 预览相关
const previewVisible = ref(false)
const previewImage = ref('')
const previewVisible = ref(false);
const previewImage = ref("");
const previewTitle = computed(() => {
return previewImage.value ? '图片预览' : ''
})
return previewImage.value ? "图片预览" : "";
});
// 上传状态
const uploading = ref(false)
const uploading = ref(false);
// 拖拽状态
const isDragOver = ref(false)
const isDragOver = ref(false);
// 初始化文件列表
const initFileList = () => {
if (props.modelValue) {
if (typeof props.modelValue === 'string') {
if (typeof props.modelValue === "string") {
// 单图上传,字符串格式
fileList.value = props.modelValue
? [
{
uid: '-1',
name: 'image.png',
status: 'done',
url: props.modelValue
}
]
: []
uid: "-1",
name: "image.png",
status: "done",
url: props.modelValue,
},
]
: [];
} else if (Array.isArray(props.modelValue)) {
// 多图上传,数组格式
fileList.value = props.modelValue.map((url, index) => ({
uid: `-${index}`,
name: `image${index}.png`,
status: 'done',
url: url
}))
status: "done",
url: url,
}));
}
} else {
fileList.value = []
fileList.value = [];
}
}
};
// 监听外部值变化
watch(
() => props.modelValue,
() => {
initFileList()
initFileList();
},
{ immediate: true }
)
{ immediate: true },
);
// 自定义上传
const customUpload = (options) => {
const { file, onProgress, onSuccess, onError } = options
const formData = new FormData()
formData.append(uploadConfig.filename || 'file', file)
const { file, onProgress, onSuccess, onError } = options;
const formData = new FormData();
formData.append(uploadConfig.filename || "file", file);
uploading.value = true
uploading.value = true;
uploadConfig.apiObj(formData, {
onUploadProgress: (progressEvent) => {
const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100)
onProgress({ percent }, file)
}
})
uploadConfig
.apiObj(formData, {
onUploadProgress: (progressEvent) => {
const percent = Math.round(
(progressEvent.loaded / progressEvent.total) * 100,
);
onProgress({ percent }, file);
},
})
.then((res) => {
const data = uploadConfig.parseData(res)
const data = uploadConfig.parseData(res);
if (data.code === uploadConfig.successCode) {
onSuccess(data, file)
message.success('上传成功')
emit('uploadSuccess', data, file)
onSuccess(data, file);
message.success("上传成功");
emit("uploadSuccess", data, file);
} else {
onError(new Error(data.msg || '上传失败'))
message.error(data.msg || '上传失败')
emit('uploadError', data.msg || '上传失败', file)
onError(new Error(data.msg || "上传失败"));
message.error(data.msg || "上传失败");
emit("uploadError", data.msg || "上传失败", file);
}
})
.catch((error) => {
onError(error)
message.error('上传失败:' + error.message)
emit('uploadError', error.message, file)
onError(error);
message.error("上传失败:" + error.message);
emit("uploadError", error.message, file);
})
.finally(() => {
uploading.value = false
})
}
uploading.value = false;
});
};
// 上传前校验
const beforeUpload = async (file) => {
// 文件大小校验
const maxSizeMB = uploadConfig.maxSize || 10
const maxSizeBytes = maxSizeMB * 1024 * 1024
const maxSizeMB = uploadConfig.maxSize || 10;
const maxSizeBytes = maxSizeMB * 1024 * 1024;
if (file.size > maxSizeBytes) {
message.error(`图片大小不能超过 ${maxSizeMB}MB`)
return false
message.error(`图片大小不能超过 ${maxSizeMB}MB`);
return false;
}
// 图片尺寸校验
if (props.minWidth || props.maxWidth || props.minHeight || props.maxHeight) {
if (
props.minWidth ||
props.maxWidth ||
props.minHeight ||
props.maxHeight
) {
try {
const dimensions = await getImageDimensions(file)
const { width, height } = dimensions
const dimensions = await getImageDimensions(file);
const { width, height } = dimensions;
if (props.minWidth && width < props.minWidth) {
message.error(`图片宽度不能小于 ${props.minWidth}px`)
return false
message.error(`图片宽度不能小于 ${props.minWidth}px`);
return false;
}
if (props.maxWidth && width > props.maxWidth) {
message.error(`图片宽度不能大于 ${props.maxWidth}px`)
return false
message.error(`图片宽度不能大于 ${props.maxWidth}px`);
return false;
}
if (props.minHeight && height < props.minHeight) {
message.error(`图片高度不能小于 ${props.minHeight}px`)
return false
message.error(`图片高度不能小于 ${props.minHeight}px`);
return false;
}
if (props.maxHeight && height > props.maxHeight) {
message.error(`图片高度不能大于 ${props.maxHeight}px`)
return false
message.error(`图片高度不能大于 ${props.maxHeight}px`);
return false;
}
} catch (error) {
message.error('图片尺寸校验失败')
return false
message.error("图片尺寸校验失败");
return false;
}
}
return true
}
return true;
};
// 获取图片尺寸
const getImageDimensions = (file) => {
return new Promise((resolve, reject) => {
const img = new Image()
const reader = new FileReader()
const img = new Image();
const reader = new FileReader();
reader.onload = (e) => {
img.src = e.target.result
img.src = e.target.result;
img.onload = () => {
resolve({ width: img.width, height: img.height })
}
img.onerror = reject
}
reader.onerror = reject
reader.readAsDataURL(file)
})
}
resolve({ width: img.width, height: img.height });
};
img.onerror = reject;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
};
// 处理预览
const handlePreview = async (file) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj)
file.preview = await getBase64(file.originFileObj);
}
previewImage.value = file.url || file.preview
previewVisible.value = true
emit('preview', file)
}
previewImage.value = file.url || file.preview;
previewVisible.value = true;
emit("preview", file);
};
// 获取Base64
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = (error) => reject(error)
})
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
// 处理文件列表变化
const handleChange = ({ fileList: newFileList }) => {
// 更新文件列表,确保上传成功的文件有正确的 url
const updatedFileList = newFileList.map((file) => {
// 如果文件上传成功且有响应数据但没有 url,则设置 url
if (file.status === 'done' && file.response?.src && !file.url) {
if (file.status === "done" && file.response?.src && !file.url) {
return {
...file,
url: file.response.src
}
url: file.response.src,
};
}
return file
})
return file;
});
fileList.value = updatedFileList
fileList.value = updatedFileList;
// 过滤掉失败的文件
const validFileList = updatedFileList.filter((file) => file.status !== 'error')
const validFileList = updatedFileList.filter(
(file) => file.status !== "error",
);
// 提取成功的文件URL
const successFiles = validFileList
.filter((file) => file.status === 'done' && (file.url || file.response?.src))
.map((file) => file.url || file.response?.src)
.filter(
(file) =>
file.status === "done" && (file.url || file.response?.src),
)
.map((file) => file.url || file.response?.src);
// 触发更新事件
if (props.returnUrl) {
// 返回URL字符串或数组
const value = props.maxCount === 1 ? successFiles[0] || '' : successFiles
emit('update:modelValue', value)
emit('change', value, validFileList)
const value =
props.maxCount === 1 ? successFiles[0] || "" : successFiles;
emit("update:modelValue", value);
emit("change", value, validFileList);
} else {
// 返回完整文件列表
emit('update:modelValue', validFileList)
emit('change', validFileList)
emit("update:modelValue", validFileList);
emit("change", validFileList);
}
}
};
// 拖拽相关
const handleDragEnter = (e) => {
e.preventDefault()
isDragOver.value = true
}
e.preventDefault();
isDragOver.value = true;
};
const handleDragLeave = (e) => {
e.preventDefault()
isDragOver.value = false
}
e.preventDefault();
isDragOver.value = false;
};
const handleDrop = (e) => {
e.preventDefault()
isDragOver.value = false
}
e.preventDefault();
isDragOver.value = false;
};
// 取消预览
const handleCancel = () => {
previewVisible.value = false
}
previewVisible.value = false;
};
</script>
<style scoped>