top推荐
This commit is contained in:
191
schoolNewsWeb/src/components/base/DynamicParamForm.vue
Normal file
191
schoolNewsWeb/src/components/base/DynamicParamForm.vue
Normal file
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<div class="params-container" v-if="paramsSchema && paramsSchema.length">
|
||||
<div
|
||||
v-for="param in paramsSchema"
|
||||
:key="param.name"
|
||||
class="param-item"
|
||||
>
|
||||
<span class="param-label">
|
||||
{{ param.description }}
|
||||
<span class="param-type">({{ param.type }})</span>
|
||||
</span>
|
||||
|
||||
<!-- 文本输入框 -->
|
||||
<el-input
|
||||
v-if="param.type === 'Input'"
|
||||
v-model="innerValue[param.name]"
|
||||
:placeholder="`请输入${param.description}`"
|
||||
clearable
|
||||
/>
|
||||
|
||||
<!-- 数字输入框 -->
|
||||
<el-input-number
|
||||
v-else-if="param.type === 'InputNumber'"
|
||||
v-model="innerNumberMap[param.name]"
|
||||
:placeholder="`请输入${param.description}`"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<el-date-picker
|
||||
v-else-if="param.type === 'DatePicker'"
|
||||
v-model="innerValue[param.name]"
|
||||
type="date"
|
||||
:placeholder="`请选择${param.description}`"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
/>
|
||||
|
||||
<!-- 日期范围选择器 -->
|
||||
<el-date-picker
|
||||
v-else-if="param.type === 'DateRangePicker'"
|
||||
v-model="innerValue[param.name]"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
/>
|
||||
|
||||
<!-- 布尔开关 -->
|
||||
<el-switch
|
||||
v-else-if="param.type === 'Switch'"
|
||||
v-model="innerBoolMap[param.name]"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
/>
|
||||
|
||||
<!-- 下拉选择器 -->
|
||||
<el-select
|
||||
v-else-if="param.type === 'Select'"
|
||||
v-model="innerValue[param.name]"
|
||||
:placeholder="`请选择${param.description}`"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in param.options || []"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, watch, reactive } from 'vue';
|
||||
|
||||
interface ParamOption {
|
||||
label: string;
|
||||
value: string | number | boolean;
|
||||
}
|
||||
|
||||
interface ParamSchemaItem {
|
||||
name: string;
|
||||
description: string;
|
||||
type:
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'DatePicker'
|
||||
| 'DateRangePicker'
|
||||
| 'Switch'
|
||||
| 'Select';
|
||||
valueType?: string;
|
||||
value?: any;
|
||||
required?: boolean;
|
||||
options?: ParamOption[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
paramsSchema: ParamSchemaItem[];
|
||||
modelValue: Record<string, any>;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: Record<string, any>): void;
|
||||
}>();
|
||||
|
||||
// 内部值使用 reactive,保证双向绑定
|
||||
const innerValue = reactive<Record<string, any>>({});
|
||||
|
||||
// 为了兼容不同类型控件,做一些视图层的映射
|
||||
const innerNumberMap = computed({
|
||||
get() {
|
||||
return innerValue as Record<string, number | undefined>;
|
||||
},
|
||||
set(val: Record<string, number | undefined>) {
|
||||
Object.assign(innerValue, val);
|
||||
}
|
||||
});
|
||||
|
||||
const innerBoolMap = computed({
|
||||
get() {
|
||||
return innerValue as Record<string, boolean | undefined>;
|
||||
},
|
||||
set(val: Record<string, boolean | undefined>) {
|
||||
Object.assign(innerValue, val);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化 / 外部变更时同步
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
Object.keys(innerValue).forEach((k) => delete innerValue[k]);
|
||||
if (val) {
|
||||
Object.assign(innerValue, val);
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
// 内部变更时向外同步
|
||||
watch(
|
||||
innerValue,
|
||||
(val) => {
|
||||
emit('update:modelValue', { ...val });
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.params-container {
|
||||
padding: 12px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.param-item {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.param-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
|
||||
.param-type {
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -12,3 +12,4 @@ export { default as GenericSelector } from './GenericSelector.vue';
|
||||
export { default as TreeNode } from './TreeNode.vue';
|
||||
export { default as Notice } from './Notice.vue';
|
||||
export { default as ChangeHome } from './ChangeHome.vue';
|
||||
export { default as DynamicParamForm} from './DynamicParamForm.vue'
|
||||
Reference in New Issue
Block a user