优化资产编辑表单验证和服务资产配置,新增上下线时间联动校验、主图必填校验

This commit is contained in:
WUSIJIAN
2025-12-23 13:10:26 +08:00
parent ecd75164ae
commit f30ddaf400

View File

@@ -45,6 +45,7 @@
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
class="w100" class="w100"
@change="onOnlineTimeChange"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -57,6 +58,7 @@
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
class="w100" class="w100"
:disabled-date="disabledOfflineDate"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -139,19 +141,11 @@
</el-row> </el-row>
</template> </template>
<!-- 资产描述 -->
<el-divider content-position="left">资产描述</el-divider>
<el-form-item label="描述内容" label-width="100px">
<div class="editor-wrapper">
<Editor v-model="ruleForm.description" height="200px" placeholder="请输入资产描述" />
</div>
</el-form-item>
<!-- 图片上传 --> <!-- 图片上传 -->
<el-divider content-position="left">图片信息</el-divider> <el-divider content-position="left">图片信息</el-divider>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="主图"> <el-form-item label="主图" prop="mainImage">
<el-upload <el-upload
class="avatar-uploader" class="avatar-uploader"
:show-file-list="false" :show-file-list="false"
@@ -181,6 +175,16 @@
</el-col> </el-col>
</el-row> </el-row>
<!-- 资产描述 -->
<el-divider content-position="left">资产描述</el-divider>
<el-form-item label="描述内容" label-width="100px">
<div class="editor-wrapper">
<Editor v-model="ruleForm.description" height="200px" placeholder="请输入资产描述" />
</div>
</el-form-item>
<!-- 实物资产配置 --> <!-- 实物资产配置 -->
<template v-if="ruleForm.type === 'physical'"> <template v-if="ruleForm.type === 'physical'">
<el-divider content-position="left">实物资产配置</el-divider> <el-divider content-position="left">实物资产配置</el-divider>
@@ -204,19 +208,19 @@
<!-- 预订配置 --> <!-- 预订配置 -->
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="6"> <el-col :span="6">
<el-form-item label="最小提前"> <el-form-item label="最小提前" prop="serviceAssetConfig.booking.minAdvance">
<el-input-number v-model="ruleForm.serviceAssetConfig.booking.minAdvance" :min="0" class="w100" /> <el-input-number v-model="ruleForm.serviceAssetConfig.booking.minAdvance" :min="0" class="w100" />
<span class="unit-text">分钟</span> <span class="unit-text">分钟</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="最小时长"> <el-form-item label="最小时长" prop="serviceAssetConfig.booking.minDuration">
<el-input-number v-model="ruleForm.serviceAssetConfig.booking.minDuration" :min="0" class="w100" /> <el-input-number v-model="ruleForm.serviceAssetConfig.booking.minDuration" :min="0" class="w100" />
<span class="unit-text">分钟</span> <span class="unit-text">分钟</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="取消提前"> <el-form-item label="取消提前" prop="serviceAssetConfig.booking.cancelWindow">
<el-input-number v-model="ruleForm.serviceAssetConfig.booking.cancelWindow" :min="0" class="w100" /> <el-input-number v-model="ruleForm.serviceAssetConfig.booking.cancelWindow" :min="0" class="w100" />
<span class="unit-text">分钟</span> <span class="unit-text">分钟</span>
</el-form-item> </el-form-item>
@@ -224,7 +228,7 @@
</el-row> </el-row>
<!-- 时间段配置 --> <!-- 时间段配置 -->
<el-form-item label="时间段"> <el-form-item label="服务时间" prop="serviceAssetConfig.schedule.timeSlots">
<div class="config-list-container"> <div class="config-list-container">
<div v-for="(slot, index) in ruleForm.serviceAssetConfig.schedule.timeSlots" :key="index" class="config-list-item"> <div v-for="(slot, index) in ruleForm.serviceAssetConfig.schedule.timeSlots" :key="index" class="config-list-item">
<el-select v-model="slot.dayOfWeek" placeholder="星期" style="width: 100px"> <el-select v-model="slot.dayOfWeek" placeholder="星期" style="width: 100px">
@@ -249,7 +253,7 @@
</el-form-item> </el-form-item>
<!-- 例外日期配置 --> <!-- 例外日期配置 -->
<el-form-item label="例外日期"> <el-form-item label="休息时间">
<div class="config-list-container"> <div class="config-list-container">
<div v-for="(exc, index) in ruleForm.serviceAssetConfig.schedule.exceptions" :key="index" class="config-list-item"> <div v-for="(exc, index) in ruleForm.serviceAssetConfig.schedule.exceptions" :key="index" class="config-list-item">
<el-select v-model="exc.exceptionType" placeholder="类型" style="width: 100px" @change="onExceptionTypeChange(exc)"> <el-select v-model="exc.exceptionType" placeholder="类型" style="width: 100px" @change="onExceptionTypeChange(exc)">
@@ -267,7 +271,7 @@
<el-input v-model="exc.reason" placeholder="原因" style="width: 120px" /> <el-input v-model="exc.reason" placeholder="原因" style="width: 120px" />
<el-button type="danger" :icon="Delete" circle size="small" @click="removeException(index)" /> <el-button type="danger" :icon="Delete" circle size="small" @click="removeException(index)" />
</div> </div>
<el-button type="primary" :icon="Plus" size="small" @click="addException">添加例外</el-button> <el-button type="primary" :icon="Plus" size="small" @click="addException">添加休息时间</el-button>
</div> </div>
</el-form-item> </el-form-item>
</template> </template>
@@ -361,6 +365,7 @@ interface RuleForm {
}; };
}; };
metadata: Record<string, any>; metadata: Record<string, any>;
mainImage?: string;
} }
const emit = defineEmits(['getAssetList']); const emit = defineEmits(['getAssetList']);
@@ -450,6 +455,7 @@ const getInitialForm = (): RuleForm => ({
}, },
}, },
metadata: {}, metadata: {},
mainImage: '',
}); });
const ruleForm = reactive<RuleForm>(getInitialForm()); const ruleForm = reactive<RuleForm>(getInitialForm());
@@ -462,11 +468,42 @@ const validateOfflineTime = (_rule: any, value: string, callback: Function) => {
} }
}; };
const disabledOfflineDate = (time: Date) => {
if (!ruleForm.onlineTime) return false;
return time.getTime() < new Date(ruleForm.onlineTime).setHours(0, 0, 0, 0);
};
const validateTimeSlots = (_rule: any, value: TimeSlot[], callback: Function) => {
if (!value || value.length === 0) {
callback(new Error('请至少添加一个服务时间段'));
return;
}
for (let i = 0; i < value.length; i++) {
const slot = value[i];
if (!slot.dayOfWeek || !slot.startTime || !slot.endTime || !slot.capacity) {
callback(new Error(`${i + 1} 行服务时间配置不完整`));
return;
}
}
callback();
};
const onOnlineTimeChange = () => {
if (ruleForm.offlineTime) {
formRef.value?.validateField('offlineTime');
}
};
const rules: FormRules = { const rules: FormRules = {
name: [{ required: true, message: '资产名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '资产名称不能为空', trigger: 'blur' }],
type: [{ required: true, message: '请选择资产类型', trigger: 'change' }], type: [{ required: true, message: '请选择资产类型', trigger: 'change' }],
categoryId: [{ required: true, message: '请选择资产分类', trigger: 'change' }], categoryId: [{ required: true, message: '请选择资产分类', trigger: 'change' }],
offlineTime: [{ validator: validateOfflineTime, trigger: 'change' }], offlineTime: [{ validator: validateOfflineTime, trigger: 'change' }],
mainImage: [{ required: true, message: '请上传主图', trigger: 'change' }],
'serviceAssetConfig.booking.minAdvance': [{ required: true, message: '请输入最小提前时间', trigger: 'blur' }],
'serviceAssetConfig.booking.minDuration': [{ required: true, message: '请输入最小时长', trigger: 'blur' }],
'serviceAssetConfig.booking.cancelWindow': [{ required: true, message: '请输入取消提前时间', trigger: 'blur' }],
'serviceAssetConfig.schedule.timeSlots': [{ validator: validateTimeSlots, trigger: 'change' }],
}; };
// 主图上传处理 // 主图上传处理
@@ -474,6 +511,8 @@ const handleMainImageChange = (file: UploadFile) => {
if (file.raw) { if (file.raw) {
mainImageFile.value = file.raw; mainImageFile.value = file.raw;
mainImagePreview.value = URL.createObjectURL(file.raw); mainImagePreview.value = URL.createObjectURL(file.raw);
ruleForm.mainImage = 'set'; // 标记已上传
formRef.value?.validateField('mainImage');
} }
}; };
@@ -495,20 +534,34 @@ const handleRemove = (file: UploadFile) => {
// 时间段操作 // 时间段操作
const addTimeSlot = () => { const addTimeSlot = () => {
if (!ruleForm.serviceAssetConfig.schedule) {
ruleForm.serviceAssetConfig.schedule = { timeSlots: [], exceptions: [] };
}
if (!ruleForm.serviceAssetConfig.schedule.timeSlots) {
ruleForm.serviceAssetConfig.schedule.timeSlots = [];
}
ruleForm.serviceAssetConfig.schedule.timeSlots.push({ ruleForm.serviceAssetConfig.schedule.timeSlots.push({
dayOfWeek: '1', dayOfWeek: '1',
startTime: '09:00', startTime: '09:00',
endTime: '18:00', endTime: '18:00',
capacity: 100, capacity: 100,
}); });
formRef.value?.validateField('serviceAssetConfig.schedule.timeSlots');
}; };
const removeTimeSlot = (index: number) => { const removeTimeSlot = (index: number) => {
ruleForm.serviceAssetConfig.schedule.timeSlots.splice(index, 1); ruleForm.serviceAssetConfig.schedule.timeSlots.splice(index, 1);
formRef.value?.validateField('serviceAssetConfig.schedule.timeSlots');
}; };
// 例外日期操作 // 例外日期操作
const addException = () => { const addException = () => {
if (!ruleForm.serviceAssetConfig.schedule) {
ruleForm.serviceAssetConfig.schedule = { timeSlots: [], exceptions: [] };
}
if (!ruleForm.serviceAssetConfig.schedule.exceptions) {
ruleForm.serviceAssetConfig.schedule.exceptions = [];
}
ruleForm.serviceAssetConfig.schedule.exceptions.push({ ruleForm.serviceAssetConfig.schedule.exceptions.push({
exceptionType: 'date', exceptionType: 'date',
date: '', date: '',
@@ -596,6 +649,7 @@ const openDialog = (row?: any, edit?: boolean) => {
// 主图预览 // 主图预览
if (data.imageUrl) { if (data.imageUrl) {
mainImagePreview.value = formatImageUrl(data.imageUrl); mainImagePreview.value = formatImageUrl(data.imageUrl);
ruleForm.mainImage = data.imageUrl;
} }
// 图片列表 // 图片列表
@@ -615,6 +669,31 @@ const openDialog = (row?: any, edit?: boolean) => {
} }
if (data.type === 'service' && data.serviceAssetConfig) { if (data.type === 'service' && data.serviceAssetConfig) {
Object.assign(ruleForm.serviceAssetConfig, data.serviceAssetConfig); Object.assign(ruleForm.serviceAssetConfig, data.serviceAssetConfig);
// 确保 schedule 对象存在
if (!ruleForm.serviceAssetConfig.schedule) {
ruleForm.serviceAssetConfig.schedule = { timeSlots: [], exceptions: [] };
}
// 确保数组存在,防止后端返回 null 或 undefined 导致 push 报错
if (!ruleForm.serviceAssetConfig.schedule.exceptions) {
ruleForm.serviceAssetConfig.schedule.exceptions = [];
} else {
// 补充缺失的 exceptionType
ruleForm.serviceAssetConfig.schedule.exceptions.forEach((exc) => {
if (!exc.exceptionType) {
if (exc.date) {
exc.exceptionType = 'date';
} else if (exc.dayOfWeek) {
exc.exceptionType = 'dayOfWeek';
} else {
// 默认值
exc.exceptionType = 'date';
}
}
});
}
if (!ruleForm.serviceAssetConfig.schedule.timeSlots) {
ruleForm.serviceAssetConfig.schedule.timeSlots = [];
}
} }
// 元数据 // 元数据