参与单位

This commit is contained in:
马梓彭 2026-04-23 09:28:37 +08:00
parent 32d4aff297
commit d6f3960860

View File

@ -1,32 +1,48 @@
<template>
<div class="step-units">
<el-form ref="formRef" :model="localFormData" :rules="rules" label-position="right" label-width="0px">
<el-table :data="localFormData.units" border stripe style="width: 100%">
<el-table :data="unitList" border stripe style="width: 100%" row-key="tempId">
<el-table-column label="序号" width="80" align="center">
<template #default="{ $index }">{{ $index + 1 }}</template>
</el-table-column>
<el-table-column label="单位类型" width="180" align="center">
<el-table-column label="单位类型" width="200" align="center">
<template #default="{ row, $index }">
<el-form-item v-if="!readonly" :prop="`units.${$index}.unitType`" :rules="[{ required: true, message: '请选择单位类型', trigger: 'change' }]" style="margin: 0;width: 100%;">
<el-select v-model="row.unitType" placeholder="请选择" style="width: 100%" :disabled="readonly">
<!-- 编辑模式 -->
<template v-if="!readonly">
<!-- 自定义类型:显示文本输入框 -->
<el-input
v-if="shouldShowCustomInput(row)"
v-model="row.customUnitType"
placeholder="请输入单位类型"
style="width: 100%"
:disabled="readonly"
@change="syncToParent"
/>
<!-- 固定类型:显示下拉框 -->
<el-select
v-else
v-model="row.unitType"
placeholder="请选择"
style="width: 100%"
:disabled="readonly"
@change="syncToParent"
>
<el-option label="主办单位" :value="0" />
<el-option label="承办单位" :value="1" />
<el-option label="协办单位" :value="2" />
<el-option label="赞助单位" :value="3" />
</el-select>
</el-form-item>
</template>
<!-- 只读模式 -->
<span v-else>
{{ row.unitType === 0 ? '主办单位' : row.unitType === 1 ? '承办单位' : row.unitType === 2 ? '协办单位' : row.unitType === 3 ? '赞助单位' : '-' }}
{{ row.customUnitType || getUnitTypeLabel(row.unitType) }}
</span>
</template>
</el-table-column>
<el-table-column label="单位名称" min-width="220" align="center">
<template #default="{ row, $index }">
<el-form-item v-if="!readonly" :prop="`units.${$index}.unitName`" :rules="[{ required: true, message: '请输入单位名称', trigger: 'blur' }]" style="margin: 0">
<el-input v-model="row.unitName" placeholder="请输入单位名称" :disabled="readonly" />
</el-form-item>
<el-input v-if="!readonly" v-model="row.unitName" placeholder="请输入单位名称" :disabled="readonly" @change="syncToParent" />
<span v-else>{{ row.unitName }}</span>
</template>
</el-table-column>
@ -43,27 +59,32 @@
<el-table-column label="排序" width="180" align="center">
<template #default="{ row }">
<el-input-number v-if="!readonly" v-model="row.sort" :min="0" :step="1" :disabled="readonly" />
<el-input-number v-if="!readonly" v-model="row.sort" :min="0" :step="1" :disabled="readonly" @change="syncToParent" />
<span v-else>{{ row.sort }}</span>
</template>
</el-table-column>
<el-table-column v-if="!readonly" label="操作" width="120" align="center">
<el-table-column v-if="!readonly" label="操作" width="150" align="center">
<template #default="{ $index }">
<el-button text type="danger" icon="ele-Delete" @click="remove($index)" :disabled="localFormData.units.length <= 1">删除</el-button>
<el-button text type="primary" @click="toggleTypeMode($index)">
{{ shouldShowCustomInput(unitList[$index]) ? '固定类型' : '自定义类型' }}
</el-button>
<el-button text type="danger" icon="ele-Delete" @click="remove($index)" :disabled="unitList.length <= 1">删除</el-button>
</template>
</el-table-column>
</el-table>
<div v-if="!readonly" style="display: flex; justify-content: center; margin-top: 16px">
<el-button type="primary" plain icon="ele-Plus" @click="add">添加单位</el-button>
<div v-if="!readonly" style="display: flex; justify-content: center; gap: 12px; margin-top: 16px">
<el-button type="primary" plain icon="ele-Plus" @click="addPresetUnit">添加预设单位</el-button>
<el-button type="success" plain icon="ele-Edit" @click="addCustomUnit">添加自定义单位</el-button>
</div>
</el-form>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { ref, watch, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus } from '@element-plus/icons-vue';
const props = defineProps({
modelValue: { type: Object, required: true },
@ -71,53 +92,178 @@ const props = defineProps({
});
const emit = defineEmits(['update:modelValue']);
const formRef = ref(null);
const localFormData = ref({ units: [] });
const add = () => {
localFormData.value.units.push({ unitType: '', unitName: '', logoUrl: '', sort: localFormData.value.units.length });
const unitList = ref([]);
const tempIdCounter = ref(0);
// 判断是否应该显示自定义输入框 - 只受 isCustomType 布尔值控制
const shouldShowCustomInput = (row) => {
if (!row) return false;
// 完全只根据 isCustomType 布尔值判断,不受输入内容影响
return row.isCustomType === true;
};
const setting = ref(false)
// 获取临时唯一 ID
const getNextTempId = () => {
return `unit_${Date.now()}_${tempIdCounter.value++}`;
};
// 获取单位类型标签
const getUnitTypeLabel = (type) => {
const labels = {
0: '主办单位',
1: '承办单位',
2: '协办单位',
3: '赞助单位'
};
return labels[type] || '-';
};
// 同步数据到父组件 - 立即同步
const syncToParent = () => {
// 移除临时标记,只保留业务字段
const units = unitList.value.map(({ tempId, isCustomType, ...rest }) => rest);
emit('update:modelValue', {
...props.modelValue,
units
});
};
// 创建新单位对象
const createUnitObj = (isCustom = false) => ({
tempId: getNextTempId(),
unitType: 0,
unitName: '',
logoUrl: '',
sort: unitList.value.length,
customUnitType: isCustom ? '' : undefined,
isCustomType: isCustom
});
// 添加预设单位(下拉选择)
const addPresetUnit = () => {
const newUnit = createUnitObj(false);
unitList.value.push(newUnit);
nextTick(() => {
syncToParent();
});
};
// 添加自定义单位(文本输入)
const addCustomUnit = () => {
const newUnit = createUnitObj(true);
unitList.value.push(newUnit);
ElMessage.success('已添加自定义单位行,请填写信息');
nextTick(() => {
syncToParent();
});
};
// 切换输入模式
const toggleTypeMode = (index) => {
const unit = unitList.value[index];
if (shouldShowCustomInput(unit)) {
// 切换到固定类型模式
unit.isCustomType = false;
// 如果有自定义类型文本,尝试匹配预设类型
const typeMap = {
'主办单位': 0,
'承办单位': 1,
'协办单位': 2,
'赞助单位': 3
};
if (unit.customUnitType && typeMap[unit.customUnitType] !== undefined) {
unit.unitType = typeMap[unit.customUnitType];
}
} else {
// 切换到自定义类型模式
unit.isCustomType = true;
// 如果没有自定义类型文本,用当前预设类型作为默认值
if (!unit.customUnitType) {
unit.customUnitType = getUnitTypeLabel(unit.unitType);
}
}
nextTick(() => {
syncToParent();
});
};
// 删除单位
const remove = (idx) => {
unitList.value.splice(idx, 1);
// 重新计算排序
unitList.value.forEach((unit, index) => {
unit.sort = index;
});
nextTick(() => {
syncToParent();
});
};
// 初始化或重置数据
const initData = (data) => {
if (!data || !data.units || data.units.length === 0) {
// 空数据,添加一个默认预设单位
unitList.value = [createUnitObj(false)];
} else {
// 处理数据回显
unitList.value = data.units.map(unit => {
// 只根据是否存在 customUnitType 属性来判断,不考虑其值是否为空
const isCustom = unit.customUnitType !== undefined && unit.customUnitType !== null;
const newUnit = {
...unit,
tempId: getNextTempId(),
isCustomType: isCustom
};
return newUnit;
});
}
};
// 监听父组件传入的 modelValue 变化 - 总是响应,但要避免循环更新
watch(
() => props.modelValue,
async (v) => {
if (!v) return;
setting.value = true;
localFormData.value.units = v.units;
if (!localFormData.value.units.length ) add();
setting.value = false;
(newVal) => {
const currentUnits = unitList.value.map(({ tempId, isCustomType, ...rest }) => rest);
const newUnits = newVal?.units || [];
// 只有当数据真正不同时才更新
if (JSON.stringify(currentUnits) !== JSON.stringify(newUnits)) {
initData(newVal);
}
},
{ deep: true, immediate: true }
);
watch(
localFormData,
(v) => {
if(!setting.value){
emit('update:modelValue', { ...props.modelValue, ...v })
}
},
{ deep: true }
);
const handleAvatarSuccess = (response, index) => {
console.log('handleAvatarSuccess', response,index);
localFormData.value.units[index].logoUrl = response?.[0]?.url;
};
const remove = (idx) => localFormData.value.units.splice(idx, 1);
const rules = {
units: [{ type: 'array', required: true, message: '请至少添加一个参与单位', trigger: 'change' }],
if (unitList.value[index]) {
unitList.value[index].logoUrl = response?.[0]?.url;
nextTick(() => {
syncToParent();
});
}
};
const validate = async () => {
console.log('validate', formRef.value);
if (!formRef.value) return false;
if(!localFormData.value.units.length){
if (!unitList.value || unitList.value.length === 0) {
ElMessage.error('请至少添加一个参与单位');
return false;
}
return await formRef.value.validate();
// 手动验证每个行的必填字段
let isValid = true;
for (let i = 0; i < unitList.value.length; i++) {
const unit = unitList.value[i];
if (!unit.unitName || unit.unitName.trim() === '') {
ElMessage.error(`第${i + 1}行的单位名称不能为空`);
isValid = false;
break;
}
}
return isValid;
};
defineExpose({ validate });