Skip to content

Commit 0d3bb2a

Browse files
feat: dynamics-form
1 parent 638c204 commit 0d3bb2a

42 files changed

Lines changed: 853 additions & 1300 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎ui/src/components/COMPONENT_README.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,10 @@ const roleSettings = defineModel<{ roleId: string; workspaceIds: string[] }[]>({
712712
</MkFormList>
713713
```
714714

715-
`addText` 设置添加按钮文案。删除按钮及第一行与后续行的对齐由组件统一处理。
715+
`addText` 设置添加按钮文案,`showAddButton` 默认为 `true`;添加入口由业务布局单独提供时传入
716+
`:show-add-button="false"`。`firstRowHasLabel` 默认为 `true`:第一行删除按钮使用 `mt-8`,后续行
717+
使用 `mt-0.5`;并列表单项没有 label 时传入 `:first-row-has-label="false"`。删除成功后通过
718+
`remove(item, index)` 返回被删除的行数据和原索引,业务组件可处理关联状态,不需要再次修改列表。
716719

717720
### MkSearchList
718721

‎ui/src/components/mk-dynamics-form/Demo.vue‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ const validateForm = async () => {
177177
</div>
178178

179179
<!-- 添加/编辑字段弹窗 -->
180-
<el-dialog
180+
<MkDialog
181181
v-model="dialogVisible"
182182
:title="isEdit ? '编辑字段' : '添加字段'"
183183
width="600px"
@@ -196,5 +196,5 @@ const validateForm = async () => {
196196
<el-button @click="dialogVisible = false">取消</el-button>
197197
<el-button type="primary" @click="submitField">确定</el-button>
198198
</template>
199-
</el-dialog>
199+
</MkDialog>
200200
</template>

‎ui/src/components/mk-dynamics-form/FormItem.vue‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const emit = defineEmits<{
5959
}>()
6060
6161
const loading = ref(false)
62-
const fieldComponentRef = ref<DynamicFieldComponent>()
62+
const componentFormRef = ref<DynamicFieldComponent>()
6363
const triggerSubscriptions: Array<{
6464
event: string
6565
handler: (value: unknown) => void
@@ -188,7 +188,7 @@ onBeforeUnmount(() => {
188188
189189
function validate() {
190190
if (props.formField.trigger_type === 'CHILD_FORMS') {
191-
return fieldComponentRef.value?.validate?.() ?? Promise.resolve()
191+
return componentFormRef.value?.validate?.() ?? Promise.resolve()
192192
}
193193
return Promise.resolve()
194194
}
@@ -203,7 +203,6 @@ defineExpose({ validate })
203203
:style="formItemStyle"
204204
:prop="formField.field"
205205
:rules="validationRules"
206-
:class="formField.required_asterisk ? 'hide-asterisk' : ''"
207206
>
208207
<template v-if="formField.label" #label>
209208
<FormItemLabel v-if="isString(formField.label)" :form-field="formField" />
@@ -219,7 +218,7 @@ defineExpose({ validate })
219218
<component
220219
:is="getFieldComponent(formField.input_type)"
221220
v-if="getFieldComponent(formField.input_type)"
222-
ref="fieldComponentRef"
221+
ref="componentFormRef"
223222
v-model="itemValue"
224223
:view="view"
225224
:form-field="formField"

‎ui/src/components/mk-dynamics-form/constructor/BasicInfoConstructor.vue‎

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,9 @@ const ruleFormRef = ref<FormInstance>()
4444
const componentFormRef = ref<DynamicFormConstructorExpose>()
4545
4646
const rules: FormRules<DynamicFormConstructorState> = {
47-
label: [{ required: true, message: '显示名称 为必填属性' }],
48-
field: [{ required: true, message: '参数 为必填属性' }],
49-
required: [{ required: true, message: '是否必填 为必填属性' }],
50-
input_type: [{ required: true, message: '组建类型 为必填属性' }],
47+
label: [{ required: true, message: '请输入显示名称' }],
48+
field: [{ required: true, message: '请输入参数' }],
49+
input_type: [{ required: true, message: '请选择组件类型' }],
5150
}
5251
5352
const validate = () => (ruleFormRef.value ? ruleFormRef.value.validate() : Promise.resolve())
@@ -102,7 +101,7 @@ const updateForm = (value: DynamicFormConstructorState) => {
102101
defineExpose({ validate, getData, render })
103102
</script>
104103
<template>
105-
<el-form @submit.prevent ref="ruleFormRef" class="mb-24" label-width="auto" :model="form">
104+
<el-form @submit.prevent ref="ruleFormRef" label-width="auto" :model="form">
106105
<el-form-item label="参数" :required="true" prop="field" :rules="rules.field">
107106
<el-input v-model="form.field" :maxlength="64" placeholder="请输入参数" show-word-limit />
108107
</el-form-item>
@@ -117,13 +116,7 @@ defineExpose({ validate, getData, render })
117116
placeholder="请输入参数提示说明"
118117
/>
119118
</el-form-item>
120-
<el-form-item
121-
label="是否必填"
122-
:required="true"
123-
prop="required"
124-
:rules="rules.required"
125-
@click.prevent
126-
>
119+
<el-form-item label="是否必填" prop="required" @click.prevent>
127120
<el-switch v-model="form.required" :active-value="true" :inactive-value="false" />
128121
</el-form-item>
129122
<el-form-item label="组件类型" :required="true" prop="input_type" :rules="rules.input_type">
Lines changed: 76 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
<script setup lang="ts">
2-
import type { DynamicFormValue } from '../../type'
32
import { computed, onBeforeMount } from 'vue'
4-
const dateTypeOptions = [
3+
import { dateFormat, datetimeFormat } from '@/utils/time'
4+
import type { DynamicFormConstructorOption, DynamicFormValue } from '../../type'
5+
6+
type DatePickerType = 'year' | 'month' | 'date' | 'datetime'
7+
type DateFormatOption = Pick<DynamicFormConstructorOption, 'value'>
8+
9+
const defaultDatePickerType: DatePickerType = 'datetime'
10+
11+
const dateTypeOptions: DynamicFormConstructorOption[] = [
512
{
613
label: '年',
714
value: 'year',
@@ -19,28 +26,32 @@ const dateTypeOptions = [
1926
value: 'datetime',
2027
},
2128
]
22-
const dateFormatOptions: DynamicFormValue = {
29+
const dateFormatOptions: Record<DatePickerType, DateFormatOption[]> = {
2330
year: [{ value: 'YYYY' }],
2431
month: [{ value: 'YYYY-MM' }],
2532
date: [{ value: 'YYYY-MM-DD' }],
2633
datetime: [{ value: 'YYYY-MM-DD HH:mm:ss' }],
2734
}
2835
29-
const padDatePart = (value: number) => String(value).padStart(2, '0')
30-
const formatCurrentDate = (format: string) => {
36+
const getCurrentDateValue = (type: DatePickerType) => {
3137
const now = new Date()
32-
return format
33-
.replace('YYYY', String(now.getFullYear()))
34-
.replace('MM', padDatePart(now.getMonth() + 1))
35-
.replace('DD', padDatePart(now.getDate()))
36-
.replace('HH', padDatePart(now.getHours()))
37-
.replace('mm', padDatePart(now.getMinutes()))
38-
.replace('ss', padDatePart(now.getSeconds()))
38+
if (type === 'datetime') {
39+
return datetimeFormat(now) as string
40+
}
41+
42+
const currentDate = dateFormat(now) as string
43+
if (type === 'year') return currentDate.slice(0, 4)
44+
if (type === 'month') return currentDate.slice(0, 7)
45+
return currentDate
3946
}
4047
4148
const handleDateTypeChange = () => {
42-
formValue.value.format = dateFormatOptions[formValue.value.type][0].value
43-
formValue.value.default_value = formatCurrentDate(formValue.value.format)
49+
const datePickerType = formValue.value.type as DatePickerType
50+
const defaultFormat = dateFormatOptions[datePickerType][0]?.value
51+
if (!defaultFormat) return
52+
53+
formValue.value.format = defaultFormat
54+
formValue.value.default_value = getCurrentDateValue(datePickerType)
4455
}
4556
const props = defineProps<{
4657
modelValue: DynamicFormValue
@@ -55,6 +66,13 @@ const formValue = computed({
5566
},
5667
})
5768
69+
const defaultValueUnselected = computed({
70+
get: () => formValue.value.show_default_value === false,
71+
set: (value: boolean) => {
72+
formValue.value.show_default_value = !value
73+
},
74+
})
75+
5876
const getData = () => {
5977
return {
6078
input_type: 'DatePicker',
@@ -74,77 +92,77 @@ const render = (formData: DynamicFormValue) => {
7492
}
7593
defineExpose({ getData, render })
7694
onBeforeMount(() => {
77-
formValue.value.type = 'datetime'
95+
formValue.value.type = defaultDatePickerType
7896
formValue.value.format = 'YYYY-MM-DD HH:mm:ss'
79-
formValue.value.default_value = formatCurrentDate(formValue.value.format)
97+
formValue.value.default_value = getCurrentDateValue(defaultDatePickerType)
8098
if (formValue.value.show_default_value === undefined) {
8199
formValue.value.show_default_value = true
82100
}
83101
})
84102
</script>
85103

86104
<template>
87-
<el-form-item label="时间类型" required>
88-
<el-select @change="handleDateTypeChange" v-model="formValue.type" placeholder="请选择时间类型">
89-
<el-option
90-
v-for="inputTypeOption in dateTypeOptions"
91-
:key="inputTypeOption.value"
92-
:label="inputTypeOption.label"
93-
:value="inputTypeOption.value"
94-
/>
95-
</el-select>
96-
</el-form-item>
97-
<el-form-item label="格式" required>
98-
<el-select
99-
v-model="formValue.format"
100-
filterable
101-
default-first-option
102-
allow-create
103-
placeholder="请选择格式"
104-
>
105-
<el-option
106-
v-for="inputTypeOption in dateFormatOptions[formValue.type]"
107-
:key="inputTypeOption.value"
108-
:label="inputTypeOption.value"
109-
:value="inputTypeOption.value"
110-
/>
111-
</el-select>
112-
</el-form-item>
105+
<div class="flex gap-4">
106+
<el-form-item class="min-w-0 flex-1" label="时间类型" required>
107+
<el-select
108+
v-model="formValue.type"
109+
placeholder="请选择时间类型"
110+
@change="handleDateTypeChange"
111+
>
112+
<el-option
113+
v-for="inputTypeOption in dateTypeOptions"
114+
:key="inputTypeOption.value"
115+
:label="inputTypeOption.label"
116+
:value="inputTypeOption.value"
117+
/>
118+
</el-select>
119+
</el-form-item>
120+
<el-form-item class="min-w-0 flex-1" label="格式" required>
121+
<el-select
122+
v-model="formValue.format"
123+
filterable
124+
default-first-option
125+
allow-create
126+
placeholder="请选择格式"
127+
>
128+
<el-option
129+
v-for="inputTypeOption in dateFormatOptions[formValue.type as DatePickerType]"
130+
:key="inputTypeOption.value"
131+
:label="inputTypeOption.value"
132+
:value="inputTypeOption.value"
133+
/>
134+
</el-select>
135+
</el-form-item>
136+
</div>
113137
<el-form-item
114-
class="defaultValueItem"
138+
class="mk-hide-asterisk"
115139
:required="formValue.required"
116140
prop="default_value"
117-
label="默认值"
118141
:rules="
119142
formValue.required
120143
? [
121144
{
122145
required: true,
123-
message: '默认值为必填属性',
146+
message: '请输入默认值',
124147
},
125148
]
126149
: []
127150
"
128151
>
129-
<div class="defaultValueCheckbox">
130-
<el-checkbox v-model="formValue.show_default_value" label="显示默认值" />
131-
</div>
152+
<template #label>
153+
<div class="flex-between">
154+
<span :class="formValue.required ? 'mk-required' : ''">默认值</span>
155+
<el-checkbox v-model="defaultValueUnselected" label="未选中项" />
156+
</div>
157+
</template>
132158
<el-date-picker
133159
v-model="formValue.default_value"
134160
:type="formValue.type"
135161
placeholder="选择日期"
136162
:format="formValue.format"
137163
:value-format="formValue.format"
164+
class="w-full!"
138165
/>
139166
</el-form-item>
140167
</template>
141-
<style lang="scss" scoped>
142-
.defaultValueItem {
143-
position: relative;
144-
.defaultValueCheckbox {
145-
position: absolute;
146-
right: 0;
147-
top: -35px;
148-
}
149-
}
150-
</style>
168+
<style lang="scss" scoped></style>

‎ui/src/components/mk-dynamics-form/constructor/items/JsonInputConstructor.vue‎

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const referenceVariableRule = {
7575
required: true,
7676
validator: (_rule: unknown, value: DynamicFormValue, callback: DynamicFormValidatorCallback) => {
7777
if (!(Array.isArray(value) && value.length > 1)) {
78-
callback('引用变量必填')
78+
callback('请输入引用变量')
7979
}
8080
8181
return true
@@ -99,30 +99,20 @@ onMounted(() => {
9999
</script>
100100

101101
<template>
102-
<el-form-item v-if="getModel">
103-
<template #label>
104-
<div class="flex-between">赋值方式</div>
105-
</template>
102+
<el-form-item v-if="getModel" label="赋值方式">
103+
<!-- // TODO 赋值方式待调整 -->
104+
<el-radio-group v-model="formValue.default_value_assignment_method">
105+
<el-radio :value="item.value" v-for="(item, index) in assignmentMethodOptions" :key="index">
106+
<span class="flex align-center">
107+
{{ item.label }}
106108

107-
<el-row class="w-full">
108-
<el-radio-group v-model="formValue.default_value_assignment_method">
109-
<el-radio
110-
:value="item.value"
111-
size="large"
112-
v-for="(item, index) in assignmentMethodOptions"
113-
:key="index"
114-
>
115-
<span class="flex align-center">
116-
{{ item.label }}
117-
118-
<el-tooltip effect="dark" placement="right" v-if="item.value === 'ref_variables'">
119-
<template #content> 变量的值必须符合: JSON 格式 </template>
120-
<MkIcon name="icon_warning_filled" class="app-warning-icon ml-4"></MkIcon>
121-
</el-tooltip>
122-
</span>
123-
</el-radio>
124-
</el-radio-group>
125-
</el-row>
109+
<el-tooltip placement="right" v-if="item.value === 'ref_variables'">
110+
<template #content> 变量的值必须符合: JSON 格式 </template>
111+
<MkIcon name="icon_info_outlined"></MkIcon>
112+
</el-tooltip>
113+
</span>
114+
</el-radio>
115+
</el-radio-group>
126116
</el-form-item>
127117
<el-form-item
128118
v-if="formValue.default_value_assignment_method === 'ref_variables'"
@@ -140,26 +130,20 @@ onMounted(() => {
140130
</el-form-item>
141131

142132
<el-form-item
143-
class="defaultValueItem"
144-
label="默认值"
133+
class="mk-hide-asterisk"
145134
:required="formValue.required"
146135
v-if="formValue.default_value_assignment_method === 'custom'"
147136
prop="default_value"
148137
:rules="[defaultValueRule]"
149138
>
150-
<div class="defaultValueCheckbox">
151-
<el-checkbox v-model="formValue.show_default_value" label="显示默认值" />
152-
</div>
139+
<template #label>
140+
<div class="flex-between">
141+
<span :class="formValue.required ? 'mk-required' : ''">默认值</span>
142+
<el-checkbox v-model="formValue.show_default_value" label="显示默认值" />
143+
</div>
144+
</template>
153145
<JsonInput ref="jsonInputRef" v-model="formValue.default_value"> </JsonInput>
154146
</el-form-item>
155147
</template>
156-
<style lang="scss" scoped>
157-
.defaultValueItem {
158-
position: relative;
159-
.defaultValueCheckbox {
160-
position: absolute;
161-
right: 0;
162-
top: -35px;
163-
}
164-
}
165-
</style>
148+
149+
<style lang="scss" scoped></style>

0 commit comments

Comments
 (0)