Files
sunProduct/src/views/system/HandleLog/addHandleLog.vue
2026-09-02 17:32:15 +08:00

438 lines
13 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
:file-size="5"
:height="500"
:min-height="300"
:model-value="form.content"
:read-only="false"
@update:model-value="handleContent"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="求助人姓名" prop="seekName">
<el-input v-model.trim="form.seekName" maxlength="20" placeholder="请输入求助人姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="phone">
<el-input v-model.trim="form.phone" maxlength="50" placeholder="请输入联系方式" show-word-limit />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="办事时间" prop="handleTime">
<el-date-picker
v-model="form.handleTime"
format="YYYY-MM-DD HH:mm"
placeholder="请选择"
type="datetime"
value-format="YYYY-MM-DD HH:mm"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="办事地点" prop="handlePosition">
<el-input
v-model.trim="form.handlePosition"
:rows="3"
maxlength="200"
placeholder="请输入办事地点"
show-word-limit
type="textarea"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="附属材料">
<el-upload
ref="uploadRef"
:action="uploadUrl"
:auto-upload="false"
:before-upload="beforeAvatarUpload"
:file-list="fileList"
:headers="headerToken()"
:limit="9"
:multiple="true"
:on-change="handleProgress"
:on-exceed="handleExceed"
:on-remove="handleAvatarRemove"
:show-file-list="true"
accept=".doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif,.bmp,.webp,.zip"
class="upload-demo"
name="files"
>
<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 lang="ts" setup>
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';
import { validator } from '@/utils/Reg'; // 确保引入图标组件
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' }],
handleTime: [{ required: true, message: '请输入办事时间', trigger: 'blur' }],
handlePosition: [{ required: true, message: '请输入办事地点', trigger: 'blur' }],
seekName: [
{ required: true, message: '请选择求助人', trigger: 'blur' },
{
validator: (_: any, value: string | number) => validator(value, 'Character'),
message: '请输入中文,字母,数字,-,_等字符',
trigger: 'blur'
}
],
phone: [
{ required: false, message: '请输入联系方式', trigger: 'blur' },
{
validator: (_: any, value: string | number, callback: any) => {
if (value === '' || value === '') {
return callback();
}
return !validator(value, 'fullFilterReg') ? callback() : callback(new Error('请输入正确的联系方式'));
}
}
]
};
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() {
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];
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 lang="scss" scoped>
.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>