fix: 修复项目列表新增/编辑的bug

This commit is contained in:
Zzz1zz
2025-08-13 01:26:00 +08:00
parent 194e0faa72
commit ddb35b10f1
4 changed files with 92 additions and 76 deletions
+5 -4
View File
@@ -14,16 +14,16 @@
## Props
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| ---------- | ------------ | ---------------- | ---------------------------- |
| modelValue | UploadFile[] | [] | 文件列表,支持 v-model |
| uploadType | UploadType | - | 上传类型:1 合同模版,2 图片 |
| maxSize | number | 10 * 1024 * 1024 | 最大文件大小(字节) |
| maxSize | number | 10 _ 1024 _ 1024 | 最大文件大小(字节) |
| maxCount | number | - | 最大上传数量 |
## Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| ----------------- | -------------------------------------------- | ------------------ |
| update:modelValue | (value: UploadFile[]) | 文件列表更新时触发 |
| upload-success | (file: UploadFile, response: UploadResponse) | 上传成功时触发 |
| upload-error | (error: Error) | 上传失败时触发 |
@@ -42,7 +42,8 @@ interface UploadFile {
interface UploadResponse {
filename: string;
size: number;
url: string;
signedPutURL: string;
objectKey?: string;
}
type UploadType = 1 | 2; // 1合同模版,2图片
+52 -41
View File
@@ -1,7 +1,7 @@
<template>
<a-upload
:list-type="listType"
:accept="'image/*'"
accept="image/*"
:custom-request="customRequest"
:file-list="fileList"
:multiple="maxCount > 1"
@@ -22,48 +22,52 @@
url?: string;
status: 'done' | 'uploading' | 'error';
percent?: number;
objectKey?: string;
}
interface UploadResponse {
filename: string;
size: number;
url: string;
signedPutURL: string;
objectKey: string;
}
const props = defineProps<{
modelValue: string[]; // 只暴露图片url数组
objectKeys: string[]; // 提交用的 Key 数组
signedPutURL: string[]; // 显示用 URL 数组
uploadType: UploadType;
maxSize?: number; // 默认10MB
maxCount?: number; // 最大上传数量,默认1(单图)
listType?: 'text' | 'picture' | 'picture-card'; // 上传样式
maxSize?: number;
maxCount?: number;
listType?: 'text' | 'picture' | 'picture-card';
}>();
const emit = defineEmits<{
(e: 'update:modelValue', value: string[]): void;
(e: 'upload-success', url: string): void;
(e: 'update:objectKeys', value: string[]): void;
(e: 'update:signedPutURL', value: string[]): void;
(
e: 'upload-success',
payload: { objectKey: string; signedPutURL: string }
): void;
(e: 'upload-error', error: Error): void;
}>();
const fileList = ref<UploadFile[]>([]);
// 默认最大上传数:1(单图)
const maxCount = props.maxCount ?? 1;
// listType 默认值
const listType = props.listType ?? 'picture-card';
// 将外部 url[] 映射为 UploadFile[]
// 回显处理(编辑/刷新时)
watch(
() => props.modelValue,
(newVal) => {
fileList.value = (newVal || []).map((url, index) => ({
uid: `${index}`,
name: `图片-${index + 1}`,
url,
() => [props.objectKeys, props.signedPutURL],
([keys, urls]) => {
fileList.value = (keys || []).map((key, idx) => ({
uid: `${idx}`,
name: key.split('/').pop() || `图片-${idx + 1}`,
url: urls[idx] || '',
objectKey: key,
status: 'done',
}));
},
{ immediate: true }
{ immediate: true, deep: true }
);
// 自定义上传请求
@@ -80,15 +84,12 @@
'image/webp',
];
// 文件大小校验
if (file.size > maxSize) {
onError(
new Error(`文件大小不能超过${Math.round(maxSize / 1024 / 1024)}MB`)
);
return;
}
// 文件类型校验
if (!allowedTypes.includes(file.type)) {
onError(new Error('只支持 JPG、PNG、GIF、WEBP 格式的图片'));
return;
@@ -114,44 +115,54 @@
clearInterval(progressInterval);
if (code === 200 && data) {
const response = data as UploadResponse;
const res = data as UploadResponse;
fileItem.status = 'done';
fileItem.url = response.url;
fileItem.url = res.signedPutURL;
fileItem.objectKey = res.objectKey;
onProgress({ percent: 100 });
onSuccess(response);
onSuccess(res);
// 新的图片 url
let updatedUrls = [...props.modelValue];
// 更新父组件两个数组
let newKeys = [...props.objectKeys];
let newUrls = [...props.signedPutURL];
if (maxCount === 1) {
updatedUrls = [response.url];
newKeys = [res.objectKey];
newUrls = [res.signedPutURL];
} else {
updatedUrls.push(response.url);
// 截取至 maxCount
updatedUrls = updatedUrls.slice(0, maxCount);
newKeys.push(res.objectKey);
newUrls.push(res.signedPutURL);
newKeys = newKeys.slice(0, maxCount);
newUrls = newUrls.slice(0, maxCount);
}
emit('update:modelValue', updatedUrls);
emit('upload-success', response.url);
emit('update:objectKeys', newKeys);
emit('update:signedPutURL', newUrls);
emit('upload-success', {
objectKey: res.objectKey,
signedPutURL: res.signedPutURL,
});
} else {
const error = new Error(message || '上传失败');
onError(error);
emit('upload-error', error);
}
} catch (error) {
const err = error instanceof Error ? error : new Error('上传失败');
} catch (e) {
const err = e instanceof Error ? e : new Error('上传失败');
onError(err);
emit('upload-error', err);
}
};
// 同步 fileList(删除时触发)
// 删除文件时同步更新父组件
const handleChange = (newFileList: UploadFile[]) => {
const validUrls = newFileList
const newKeys = newFileList
.filter((f) => f.status === 'done' && f.objectKey)
.map((f) => f.objectKey!);
const newUrls = newFileList
.filter((f) => f.status === 'done' && f.url)
.map((f) => f.url!);
emit('update:modelValue', validUrls);
emit('update:objectKeys', newKeys);
emit('update:signedPutURL', newUrls);
};
</script>
+4 -3
View File
@@ -72,15 +72,16 @@ export type ProjectListRes = HttpResponse<{
projectsList?: ProjectItem[];
}>;
export interface CreateProjectParam {
id?: number;
id?: number | null;
type: ProjectType; // 项目类型:1写字楼,2商业综合体,3园区
name: string; // 楼栋名称
province_id: number | null; // 省份id
city_id: number | null; // 城市id
district_id: number | null; // 区域id
address: string; // 详细地址
images: string[]; // 图片数组
buildings: BuildingItem[]; // 楼栋数据
objectKeys: string[]; // 存储给后端的 key
signedPutURL: string[]; // 存储已签名 URL
buildings: BuildingItem[];
}
export interface BuildingInfo extends BaseEntity {
projectId: number; // 项目id
@@ -104,12 +104,13 @@
</a-col>
</a-row>
</a-form-item>
<a-form-item label="项目图片" field="images">
<a-form-item label="项目图片" field="objectKeys">
<ImageUpload
v-model="form.images"
v-model:object-keys="form.objectKeys"
v-model:signed-put-u-r-l="form.signedPutURL"
:upload-type="2"
:max-count="5"
:list-type="'picture-card'"
list-type="picture-card"
/>
</a-form-item>
<a-form-item>
@@ -214,13 +215,15 @@
const formRef = ref<FormInstance>();
const defaultForm: CreateProjectParam = {
id: null,
type: 1 as ProjectType, // 项目类型:1写字楼,2商业综合体,3园区
name: '', // 楼栋名称
province_id: null, // 省份id
city_id: null, // 城市id
district_id: null, // 区域id
address: '', // 详细地址
images: [], // 图片数组
objectKeys: [], // 图片数组
signedPutURL: [], // 图片数组
buildings: [createEmptyBuilding()], // 楼栋数据
};
const form = reactive<CreateProjectParam>({ ...defaultForm });
@@ -306,7 +309,7 @@
});
}
if (record?.imageUrls?.length) {
record.images = record?.imageUrls;
record.objectKeys = record?.imageUrls;
}
form.name = record.name;
Object.keys(form).forEach((key) => {
@@ -330,7 +333,6 @@
},
{ immediate: true }
);
const handleOk = async () => {
if (isLoading.value) return;
try {
@@ -345,9 +347,10 @@
id: item?.id,
};
});
const params = {
const params: any = {
...form,
};
delete params.signedPutURL;
const res = props?.isEdit
? await updateProject(params)
: await createProject(params);