Files
estate/src/views/system/HandleLog/addHandleLog.vue
2026-08-01 10:11:38 +08:00

419 lines
12 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>
<div class="ResidentMainBox p-2 w-full">
<PageHeader></PageHeader>
<div class="ResidentMainBody">
<el-card>
<template #header>
<div class="title">基本信息</div>
</template>
<div class="addBuildingFormBody">
<div class="formbox">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-row>
<el-col>
<el-form-item label="办事内容" prop="content">
<editor
@update:model-value="handleContent"
:model-value="form.content"
:height="500"
:min-height="300"
:read-only="false"
:file-size="5"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="求助人姓名" prop="seekName">
<el-input maxlength="20" v-model.trim="form.seekName" placeholder="请输入求助人姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="phone">
<el-input maxlength="50" show-word-limit v-model.trim="form.phone" placeholder="请输入联系方式" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="办事时间" prop="handleTime">
<el-date-picker
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm"
v-model="form.handleTime"
type="datetime"
placeholder="请选择"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="办事地点" prop="handlePosition">
<el-input
maxlength="200"
type="textarea"
:rows="3"
show-word-limit
v-model.trim="form.handlePosition"
placeholder="请输入办事地点"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="附属材料">
<el-upload
class="upload-demo"
:file-list="fileList"
:action="uploadUrl"
:multiple="true"
:auto-upload="false"
:limit="9"
ref="uploadRef"
:show-file-list="true"
name="files"
:on-exceed="handleExceed"
:headers="headerToken()"
:on-remove="handleAvatarRemove"
:on-change="handleProgress"
:before-upload="beforeAvatarUpload"
accept=".doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif,.bmp,.webp,.zip"
>
<div class="uploadBtn flex flex-items-center">
<el-icon><Upload /></el-icon>
<span>上传文件</span>
</div>
<template #tip>
<div>仅支持doc/docx/xls/xlsx/jpg/png/gif/bmp/webp/zip等格式</div>
<span style="font-size: smaller">最多上传 {{ MAX_FILE_COUNT }} 个文件</span>
</template>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="handleBack">返回</el-button>
</el-form-item>
</el-form>
</div>
</div>
</el-card>
</div>
</div>
</template>
<script setup lang="ts">
import editor from '@/components/Editor/index.vue';
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { addHandleLog, getHandleLog, updateHandleLog, uploadAttachments } from '@/api/system/HandleLog';
import { UploadFiles, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus';
import { useUserStore } from '@/store/modules/user';
import { ElMessage } from 'element-plus';
import { Upload } from '@element-plus/icons-vue'; // 确保引入图标组件╬╬╬
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/handleLog/uploadAttachments';
const route = useRoute();
const router = useRouter();
const formRef = ref();
const form = ref({
'id': null,
'content': '',
'seekName': '',
'phone': '',
'handleTime': '',
handleLogFileList: [],
'handlePosition': ''
});
const rules = {
content: [{ required: true, message: '请输入办事内容', trigger: 'blur' }],
seekName: [{ required: true, message: '请选择求助人', trigger: 'blur' }]
};
const userid = ref();
// ! 富文本 =============================================================================================================
function handleContent(val: string) {
form.value.content = val.trim();
}
// ! 富文本 =============================================================================================================
interface CustomUploadFile extends UploadUserFile {
ossId?: string;
}
const fileList = ref<CustomUploadFile[]>([]);
function headerToken() {
const useStore = useUserStore();
return {
Authorization: 'Bearer ' + useStore.token
};
}
const handleAvatarRemove: UploadProps['onRemove'] = (uploadFile: UploadUserFile, uploadFiles: UploadFiles) => {
fileList.value = uploadFiles;
};
// 定义大小限制常量 (单位: MB)
const MAX_SINGLE_FILE_SIZE = 10 * 1024 * 1024; // 10MB
const MAX_TOTAL_FILE_SIZE = 20 * 1024 * 1024; // 20MB
const MAX_FILE_COUNT = 9; // 最大上传文件数量
const uploadRef = ref();
const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
ElMessage.warning(`最多只能上传 ${MAX_FILE_COUNT} 个文件,当前已上传 ${uploadFiles.length} 个文件`);
};
const handleProgress: UploadProps['onChange'] = (uploadFile: UploadUserFile, uploadFiles: UploadFiles) => {
// 先校验当前触发的文件类型
if (!beforeAvatarUpload(uploadFile)) {
uploadRef.value?.handleRemove(uploadFile);
return;
}
const validFiles: CustomUploadFile[] = [];
let newFilesTotalSize = 0;
let hasError = false;
let errorMsg = '';
// 遍历所有文件,校验新文件
for (const file of uploadFiles) {
if (file.raw) {
if (file.raw.size > MAX_SINGLE_FILE_SIZE) {
if (!hasError) {
errorMsg = `文件 ${file.name} 大小超过 10MB已自动移除`;
hasError = true;
}
continue;
}
newFilesTotalSize += file.raw.size;
}
validFiles.push({
...file,
ossId: (file as CustomUploadFile).ossId ?? null
});
}
if (hasError) {
ElMessage.error(errorMsg);
fileList.value = validFiles;
return;
}
if (newFilesTotalSize > MAX_TOTAL_FILE_SIZE) {
ElMessage.error(`所有新上传文件的总大小不能超过 20MB当前已选新文件总大小约为 ${(newFilesTotalSize / 1024 / 1024).toFixed(2)}MB`);
const newFiles = validFiles.filter((f) => f.raw);
if (newFiles.length > 0) {
const lastNewFile = newFiles[newFiles.length - 1];
const filteredFiles = validFiles.filter((f) => f.uid !== lastNewFile.uid);
fileList.value = filteredFiles;
} else {
fileList.value = validFiles;
}
return;
}
// 只有文件列表真正变化时才更新,避免不必要的响应式触发
const currentList = fileList.value;
if (validFiles.length !== currentList.length || validFiles.some((f, i) => f.uid !== currentList[i]?.uid)) {
fileList.value = validFiles;
}
};
const blockedExts = ['.exe', '.bat', '.cmd', '.msi'];
const allowExts = [
// word
'.doc',
'.docx',
'.dot',
'.dotx',
// excel
'.xls',
'.xlsx',
'.xlsm',
'.xlsb',
// img
'.jpg',
'.jpeg',
'.png',
'.gif',
'.bmp',
'.webp',
// archive
'.zip',
// 专业/矢量图
'.psd',
'.ai',
'.svg',
'.tif',
'.tiff'
];
const beforeAvatarUpload = (rawFile: UploadUserFile) => {
const ext = rawFile.name.toLowerCase().slice(rawFile.name.lastIndexOf('.'));
if (blockedExts.includes(ext)) {
ElMessage.warning(`禁止上传 ${ext} 文件`);
return false;
}
if (!allowExts.includes(ext)) {
ElMessage.warning('只能上传word,excel,图片等文件');
return false;
}
return true;
};
// ! 初始 ====================================================================================================
function init() {
console.log('init');
getHandleLog(userid.value).then((res) => {
form.value = {
...form.value,
...res.data
};
fileList.value = res.data.handleLogFileList.map((item) => {
return {
name: item.fileName,
url: item.filePath,
ossId: item.ossId
};
});
});
}
if (route.query.id) {
userid.value = route.query.id;
init();
}
// 提交
const submitForm = async () => {
// 筛选出需要上传的新文件
const newFiles = fileList.value.filter((item) => item.raw);
const oldFiles = fileList.value
.filter((item) => !item.raw)
.map((item) => {
return {
fileName: item.name,
filePath: item.url,
ossId: item.ossId
};
});
const files = [...oldFiles];
console.log('oldFiles', files);
if (newFiles.length > 0) {
const formData = new FormData();
newFiles.forEach((item) => {
if (item.raw) {
formData.append('files', item.raw);
}
});
try {
const res = await uploadAttachments(formData);
if (res.data && res.data.success && res.data.success.length > 0) {
res.data.success.forEach((item) => {
const obj = {
fileName: item.fileName,
filePath: item.url,
ossId: item.ossId
};
files.push(obj);
});
}
} catch (error) {
console.error('上传失败', error);
ElMessage.error('文件上传失败');
return; // 上传失败则终止提交
}
}
form.value.handleLogFileList = files;
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (userid.value) {
updateHandleLog(form.value).then(() => {
ElMessage.success('编辑成功');
handleBack();
});
} else {
addHandleLog(form.value).then(() => {
ElMessage.success('添加成功');
handleBack();
});
}
}
});
};
// 返回
function handleBack() {
router.push({
path: '/repair/handlelog'
});
}
</script>
<style scoped lang="scss">
.ResidentMainBox {
display: flex;
flex-direction: column;
.ResidentMainBody {
margin-top: 20px;
flex: 1;
.formbox {
width: 1000px;
margin: 0 auto;
.upload-demo {
width: 100%;
min-width: 200px;
&:deep(.el-upload) {
width: 100%;
}
&:deep(.el-upload-list) {
max-height: 250px;
overflow-y: auto;
overflow-x: hidden;
}
}
.uploadBtn {
height: 30px;
line-height: 20px;
border-radius: 5px;
background-color: rgba(255, 255, 255, 1);
color: rgba(16, 16, 16, 1);
padding-left: 20px;
font-size: 14px;
border: 1px solid rgba(204, 204, 204, 1);
width: 100%;
border: 1px solid #ccc;
span {
margin-left: 10px;
}
}
}
:deep(.el-date-editor.el-input, .el-date-editor.el-input__wrapper) {
width: 380px;
}
&:deep(div.el-step__head.is-process) {
& > div.el-step__line {
width: 0;
border: 1px dashed gray;
background: transparent;
}
}
&:deep(.el-step.is-vertical .el-step__line) {
top: 20px;
}
&:deep(div.el-step__head.is-finish) {
& > div.el-step__line {
width: 0;
border: 1px solid var(--el-color-primary);
background: transparent;
}
}
}
}
</style>