优化PUT请求传参机制,实现最小化传参以减少网络传输和提高性能,在请求拦截器中自动计算差异只传递修改过的字段,同时新增通用的表单差异比较工具函数,

This commit is contained in:
WUSIJIAN
2026-01-13 18:06:53 +08:00
parent cdbda577cc
commit f8af956f06
6 changed files with 289 additions and 24 deletions

View File

@@ -445,6 +445,7 @@ import type { FormInstance, FormRules } from 'element-plus';
import { Plus, Delete } from '@element-plus/icons-vue';
import { getAsset, createAsset, updateAsset, uploadAssetImage } from '/@/api/assets/asset';
import { getCategoryTree, getCategory } from '/@/api/assets/category';
import { createFormDiff } from '/@/utils/diffUtils';
import Editor from '/@/components/editor/index.vue';
import type { UploadFile, UploadUserFile, UploadRequestOptions } from 'element-plus';
@@ -555,6 +556,8 @@ const dialogVisible = ref(false);
const dialogImageUrl = ref('');
// 图片拼接
const fileAddressPrefix = ref('');
// 使用通用工具函数保存原始数据,用于最小化传参
const assetFormDiff = createFormDiff<Record<string, any>>();
const formatImageUrl = (url?: string) => {
if (!url) return '';
@@ -1021,6 +1024,9 @@ const openDialog = (row?: any, edit?: boolean) => {
categoryAttrs.value = [];
});
}
// 保存原始数据用于最小化传参
assetFormDiff.saveOriginal(JSON.parse(JSON.stringify(ruleForm)));
})
.finally(() => {
formLoading.value = false;
@@ -1187,7 +1193,21 @@ const onSubmit = async () => {
if (valid) {
submitLoading.value = true;
try {
const requestBody = await buildRequestBody();
const fullRequestBody = await buildRequestBody();
let requestBody: any;
if (isEdit.value) {
// 编辑模式:通过 _originalData 让拦截器自动处理最小化传参
const originalData = assetFormDiff.getOriginal();
requestBody = {
...fullRequestBody,
_originalData: originalData,
};
} else {
// 新增模式:传递所有字段
requestBody = fullRequestBody;
}
const request = isEdit.value ? updateAsset(requestBody) : createAsset(requestBody);
await request;

View File

@@ -174,6 +174,7 @@ import { ref, reactive } from 'vue';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { ElMessageBox } from 'element-plus';
import { listAssetSkus, createAssetSku, updateAssetSku, deleteAssetSku, getAssetSku, getAsset, uploadAssetImage, getSpecsUnitOptions } from '/@/api/assets/asset';
import { createFormDiff } from '/@/utils/diffUtils';
import type { UploadRequestOptions, UploadUserFile } from 'element-plus';
interface SpecValueItem {
@@ -228,6 +229,9 @@ const skuForm = reactive({
const specValuesList = ref<SpecValueItem[]>([{ key: '', value: '' }]);
const specValuesMap = reactive<Record<string, string>>({});
// 使用通用工具函数保存原始数据,用于最小化传参
const skuFormDiff = createFormDiff<Record<string, any>>();
const specValuesMapDiff = createFormDiff<Record<string, string>>();
const skuRules: FormRules = {
skuName: [{ required: true, message: '请输入SKU名称', trigger: 'blur' }],
@@ -402,6 +406,19 @@ const onEditSku = async (row: any) => {
skuForm.specsUnit = data.specsUnit || '';
}
skuForm.specsCount = data.specsCount || 1;
// 使用工具函数保存原始数据用于最小化传参
skuFormDiff.saveOriginal({
skuName: skuForm.skuName,
price: skuForm.price,
stock: skuForm.stock,
unlimitedStock: skuForm.unlimitedStock,
status: skuForm.status,
sort: skuForm.sort,
imageUrl: skuForm.imageUrl,
description: skuForm.description,
specsUnit: skuForm.specsUnit,
specsCount: skuForm.specsCount,
});
// 图片预览回显
if (data.imageUrl) {
skuImagePreview.value = formatImageUrl(data.imageUrl);
@@ -437,6 +454,8 @@ const onEditSku = async (row: any) => {
} else {
specValuesList.value = [{ key: '', value: '' }];
}
// 保存原始规格属性数据
specValuesMapDiff.saveOriginal({ ...specValuesMap });
} catch (error) {
skuFormVisible.value = false;
} finally {
@@ -599,7 +618,7 @@ const onSubmitSku = async () => {
});
// 构建 specsUnit 对象格式
let specsUnitObj = undefined;
let specsUnitObj: { key: string; value: string } | undefined = undefined;
if (skuForm.specsUnit) {
const unitOption = specsUnitOptions.value.find((opt) => opt.key === skuForm.specsUnit);
specsUnitObj = {
@@ -608,26 +627,57 @@ const onSubmitSku = async () => {
};
}
const data = {
assetId: assetId.value,
assetName: assetName.value,
skuName: skuForm.skuName,
imageUrl: skuForm.imageUrl || undefined,
specValues: specValues.length > 0 ? specValues : undefined,
price: Math.round(skuForm.price * 100),
unlimitedStock: skuForm.unlimitedStock,
stock: skuForm.stock,
sort: skuForm.sort,
status: skuForm.status,
description: skuForm.description,
specsUnit: specsUnitObj,
specsCount: skuForm.specsCount || undefined,
};
try {
if (isEditSku.value) {
await updateAssetSku({ ...data, id: editSkuId.value });
// 编辑模式:使用工具函数获取修改过的字段
const currentFormData = {
skuName: skuForm.skuName,
price: skuForm.price,
stock: skuForm.stock,
unlimitedStock: skuForm.unlimitedStock,
status: skuForm.status,
sort: skuForm.sort,
imageUrl: skuForm.imageUrl,
description: skuForm.description,
specsUnit: skuForm.specsUnit,
specsCount: skuForm.specsCount,
};
const changedFields = skuFormDiff.getChanges(currentFormData, {
alwaysInclude: ['id'],
transformers: {
price: (val) => Math.round(val * 100),
specsUnit: () => specsUnitObj,
imageUrl: (val) => val || undefined,
},
});
// 添加 id
const changedData: Record<string, any> = { id: editSkuId.value, ...changedFields };
// 比较规格属性
if (specValuesMapDiff.hasChanges(specValuesMap) && specValues.length > 0) {
changedData.specValues = specValues;
}
await updateAssetSku(changedData as any);
} else {
// 新增模式:传递所有字段
const data = {
assetId: assetId.value,
assetName: assetName.value,
skuName: skuForm.skuName,
imageUrl: skuForm.imageUrl || undefined,
specValues: specValues.length > 0 ? specValues : undefined,
price: Math.round(skuForm.price * 100),
unlimitedStock: skuForm.unlimitedStock,
stock: skuForm.stock,
sort: skuForm.sort,
status: skuForm.status,
description: skuForm.description,
specsUnit: specsUnitObj,
specsCount: skuForm.specsCount || undefined,
};
await createAssetSku(data);
}
ElMessage.success(isEditSku.value ? '编辑成功' : '添加成功');

View File

@@ -126,6 +126,7 @@ import { ElMessage } from 'element-plus';
import { Plus, Delete } from '@element-plus/icons-vue';
import { getCategoryTree, getCategory, addCategory, updateCategory, getCategoryAttrTypeOptions } from '/@/api/assets/category';
import { getDicts } from '/@/api/system/dict/data';
import { createFormDiff } from '/@/utils/diffUtils';
interface CategoryRow {
id: string;
@@ -181,6 +182,8 @@ const attrTypeOptions = ref<{ key: string; value: string }[]>([]);
const dictTypeOptions = ref<DictInfo[]>([]);
const dictValueOptions = ref<any[]>([]);
const dictLoading = ref(false);
// 使用通用工具函数保存原始数据,用于最小化传参
const categoryFormDiff = createFormDiff<Record<string, any>>();
const ruleForm = reactive<RuleForm>({
id: '',
@@ -362,7 +365,12 @@ const openDialog = (row?: CategoryRow | string, edit?: boolean) => {
}
}
});
// 保存原始数据用于最小化传参
categoryFormDiff.saveOriginal(JSON.parse(JSON.stringify(ruleForm)));
});
} else {
// 保存原始数据用于最小化传参
categoryFormDiff.saveOriginal(JSON.parse(JSON.stringify(ruleForm)));
}
});
} else if (row && typeof row === 'string') {
@@ -432,10 +440,16 @@ const onSubmit = () => {
options: [],
};
});
const submitData = { ...ruleForm, attrs: processedAttrs };
if (isEdit.value) {
// 修改
// 编辑模式:通过 _originalData 让拦截器自动处理最小化传参
const originalData = categoryFormDiff.getOriginal();
const submitData = {
...ruleForm,
attrs: processedAttrs,
_originalData: originalData,
};
updateCategory(submitData)
.then(() => {
ElMessage.success('修改成功');
@@ -446,7 +460,8 @@ const onSubmit = () => {
submitLoading.value = false;
});
} else {
// 新增
// 新增模式:传递所有字段
const submitData = { ...ruleForm, attrs: processedAttrs };
addCategory(submitData)
.then(() => {
ElMessage.success('添加成功');