419 lines
12 KiB
Vue
419 lines
12 KiB
Vue
<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>
|