参与单位
This commit is contained in:
parent
32d4aff297
commit
d6f3960860
@ -1,69 +1,90 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="step-units">
|
<div class="step-units">
|
||||||
<el-form ref="formRef" :model="localFormData" :rules="rules" label-position="right" label-width="0px">
|
<el-table :data="unitList" border stripe style="width: 100%" row-key="tempId">
|
||||||
<el-table :data="localFormData.units" border stripe style="width: 100%">
|
<el-table-column label="序号" width="80" align="center">
|
||||||
<el-table-column label="序号" width="80" align="center">
|
<template #default="{ $index }">{{ $index + 1 }}</template>
|
||||||
<template #default="{ $index }">{{ $index + 1 }}</template>
|
</el-table-column>
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column label="单位类型" width="180" align="center">
|
<el-table-column label="单位类型" width="200" align="center">
|
||||||
<template #default="{ row, $index }">
|
<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-option label="主办单位" :value="0" />
|
<!-- 自定义类型:显示文本输入框 -->
|
||||||
<el-option label="承办单位" :value="1" />
|
<el-input
|
||||||
<el-option label="协办单位" :value="2" />
|
v-if="shouldShowCustomInput(row)"
|
||||||
<el-option label="赞助单位" :value="3" />
|
v-model="row.customUnitType"
|
||||||
</el-select>
|
placeholder="请输入单位类型"
|
||||||
</el-form-item>
|
style="width: 100%"
|
||||||
<span v-else>
|
:disabled="readonly"
|
||||||
{{ row.unitType === 0 ? '主办单位' : row.unitType === 1 ? '承办单位' : row.unitType === 2 ? '协办单位' : row.unitType === 3 ? '赞助单位' : '-' }}
|
@change="syncToParent"
|
||||||
</span>
|
/>
|
||||||
|
<!-- 固定类型:显示下拉框 -->
|
||||||
|
<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>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
<!-- 只读模式 -->
|
||||||
|
<span v-else>
|
||||||
|
{{ row.customUnitType || getUnitTypeLabel(row.unitType) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="单位名称" min-width="220" align="center">
|
<el-table-column label="单位名称" min-width="220" align="center">
|
||||||
<template #default="{ row, $index }">
|
<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-if="!readonly" v-model="row.unitName" placeholder="请输入单位名称" :disabled="readonly" @change="syncToParent" />
|
||||||
<el-input v-model="row.unitName" placeholder="请输入单位名称" :disabled="readonly" />
|
<span v-else>{{ row.unitName }}</span>
|
||||||
</el-form-item>
|
</template>
|
||||||
<span v-else>{{ row.unitName }}</span>
|
</el-table-column>
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column label="LogoUrl" min-width="120" align="center">
|
<el-table-column label="LogoUrl" min-width="120" align="center">
|
||||||
<template #default="{ row ,$index }">
|
<template #default="{ row, $index }">
|
||||||
<img v-if="row.logoUrl" :src="row.logoUrl" style="height: 120px;" />
|
<img v-if="row.logoUrl" :src="row.logoUrl" style="height: 120px;" />
|
||||||
<el-upload v-else-if="!readonly" class="avatar-uploader" action="/api/file/upload" :show-file-list="false" :on-success=" (response) => handleAvatarSuccess(response, $index)">
|
<el-upload v-else-if="!readonly" class="avatar-uploader" action="/api/file/upload" :show-file-list="false" :on-success=" (response) => handleAvatarSuccess(response, $index)">
|
||||||
<el-icon class="avatar-uploader-icon"><Plus /></el-icon>
|
<el-icon class="avatar-uploader-icon"><Plus /></el-icon>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="排序" width="180" align="center">
|
<el-table-column label="排序" width="180" align="center">
|
||||||
<template #default="{ row }">
|
<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>
|
<span v-else>{{ row.sort }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</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 }">
|
<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)">
|
||||||
</template>
|
{{ shouldShowCustomInput(unitList[$index]) ? '固定类型' : '自定义类型' }}
|
||||||
</el-table-column>
|
</el-button>
|
||||||
</el-table>
|
<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">
|
<div v-if="!readonly" style="display: flex; justify-content: center; gap: 12px; margin-top: 16px">
|
||||||
<el-button type="primary" plain icon="ele-Plus" @click="add">添加单位</el-button>
|
<el-button type="primary" plain icon="ele-Plus" @click="addPresetUnit">添加预设单位</el-button>
|
||||||
</div>
|
<el-button type="success" plain icon="ele-Edit" @click="addCustomUnit">添加自定义单位</el-button>
|
||||||
</el-form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<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({
|
const props = defineProps({
|
||||||
modelValue: { type: Object, required: true },
|
modelValue: { type: Object, required: true },
|
||||||
@ -71,53 +92,178 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const formRef = ref(null);
|
const unitList = ref([]);
|
||||||
const localFormData = ref({ units: [] });
|
const tempIdCounter = ref(0);
|
||||||
const add = () => {
|
|
||||||
localFormData.value.units.push({ unitType: '', unitName: '', logoUrl: '', sort: localFormData.value.units.length });
|
// 判断是否应该显示自定义输入框 - 只受 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(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
async (v) => {
|
(newVal) => {
|
||||||
if (!v) return;
|
const currentUnits = unitList.value.map(({ tempId, isCustomType, ...rest }) => rest);
|
||||||
setting.value = true;
|
const newUnits = newVal?.units || [];
|
||||||
localFormData.value.units = v.units;
|
|
||||||
if (!localFormData.value.units.length ) add();
|
// 只有当数据真正不同时才更新
|
||||||
setting.value = false;
|
if (JSON.stringify(currentUnits) !== JSON.stringify(newUnits)) {
|
||||||
|
initData(newVal);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
|
||||||
localFormData,
|
|
||||||
(v) => {
|
|
||||||
if(!setting.value){
|
|
||||||
emit('update:modelValue', { ...props.modelValue, ...v })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
const handleAvatarSuccess = (response, index) => {
|
const handleAvatarSuccess = (response, index) => {
|
||||||
console.log('handleAvatarSuccess', response,index);
|
if (unitList.value[index]) {
|
||||||
localFormData.value.units[index].logoUrl = response?.[0]?.url;
|
unitList.value[index].logoUrl = response?.[0]?.url;
|
||||||
};
|
nextTick(() => {
|
||||||
const remove = (idx) => localFormData.value.units.splice(idx, 1);
|
syncToParent();
|
||||||
|
});
|
||||||
const rules = {
|
}
|
||||||
units: [{ type: 'array', required: true, message: '请至少添加一个参与单位', trigger: 'change' }],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const validate = async () => {
|
const validate = async () => {
|
||||||
console.log('validate', formRef.value);
|
if (!unitList.value || unitList.value.length === 0) {
|
||||||
if (!formRef.value) return false;
|
|
||||||
if(!localFormData.value.units.length){
|
|
||||||
ElMessage.error('请至少添加一个参与单位');
|
ElMessage.error('请至少添加一个参与单位');
|
||||||
return false;
|
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 });
|
defineExpose({ validate });
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user