vue3-components-plus 3.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,263 @@
1
+ <template>
2
+ <div class="cascade-demo">
3
+ <h1>级联表单示例</h1>
4
+
5
+ <el-card class="demo-card">
6
+ <template #header>
7
+ <span>省市区三级联动</span>
8
+ </template>
9
+
10
+ <DynamicFormPlus
11
+ ref="formRef"
12
+ v-model="formData"
13
+ :config="formConfig"
14
+ :col-span="8"
15
+ :gutter="16"
16
+ label-width="100px"
17
+ @submit="handleSubmit"
18
+ />
19
+ </el-card>
20
+
21
+ <el-card class="demo-card" style="margin-top: 20px">
22
+ <template #header>
23
+ <span>表单数据</span>
24
+ </template>
25
+ <pre>{{ JSON.stringify(formData, null, 2) }}</pre>
26
+ </el-card>
27
+ </div>
28
+ </template>
29
+
30
+ <script setup lang="ts">
31
+ import { ref } from 'vue'
32
+ import { ElMessage } from 'element-plus'
33
+ import { DynamicFormPlus } from '../../packages/components/DynamicFormPlus'
34
+ import type { FormConfig } from '../../packages/components/DynamicFormPlus'
35
+
36
+ // 模拟数据源
37
+ const provinces = [
38
+ { label: '广东省', value: 'guangdong' },
39
+ { label: '浙江省', value: 'zhejiang' },
40
+ { label: '江苏省', value: 'jiangsu' },
41
+ ]
42
+
43
+ const citiesMap: Record<string, Array<{ label: string; value: string }>> = {
44
+ guangdong: [
45
+ { label: '广州市', value: 'guangzhou' },
46
+ { label: '深圳市', value: 'shenzhen' },
47
+ { label: '东莞市', value: 'dongguan' },
48
+ ],
49
+ zhejiang: [
50
+ { label: '杭州市', value: 'hangzhou' },
51
+ { label: '宁波市', value: 'ningbo' },
52
+ { label: '温州市', value: 'wenzhou' },
53
+ ],
54
+ jiangsu: [
55
+ { label: '南京市', value: 'nanjing' },
56
+ { label: '苏州市', value: 'suzhou' },
57
+ { label: '无锡市', value: 'wuxi' },
58
+ ],
59
+ }
60
+
61
+ const districtsMap: Record<string, Array<{ label: string; value: string }>> = {
62
+ guangzhou: [
63
+ { label: '天河区', value: 'tianhe' },
64
+ { label: '越秀区', value: 'yuexiu' },
65
+ { label: '海珠区', value: 'haizhu' },
66
+ ],
67
+ shenzhen: [
68
+ { label: '南山区', value: 'nanshan' },
69
+ { label: '福田区', value: 'futian' },
70
+ { label: '罗湖区', value: 'luohu' },
71
+ ],
72
+ dongguan: [
73
+ { label: '莞城区', value: 'guancheng' },
74
+ { label: '南城区', value: 'nancheng' },
75
+ { label: '东城区', value: 'dongcheng' },
76
+ ],
77
+ hangzhou: [
78
+ { label: '西湖区', value: 'xihu' },
79
+ { label: '滨江区', value: 'binjiang' },
80
+ { label: '余杭区', value: 'yuhang' },
81
+ ],
82
+ ningbo: [
83
+ { label: '海曙区', value: 'haishu' },
84
+ { label: '江北区', value: 'jiangbei' },
85
+ { label: '鄞州区', value: 'yinzhou' },
86
+ ],
87
+ wenzhou: [
88
+ { label: '鹿城区', value: 'lucheng' },
89
+ { label: '龙湾区', value: 'longwan' },
90
+ { label: '瓯海区', value: 'ouhai' },
91
+ ],
92
+ nanjing: [
93
+ { label: '玄武区', value: 'xuanwu' },
94
+ { label: '秦淮区', value: 'qinhuai' },
95
+ { label: '建邺区', value: 'jianye' },
96
+ ],
97
+ suzhou: [
98
+ { label: '姑苏区', value: 'gusu' },
99
+ { label: '吴中区', value: 'wuzhong' },
100
+ { label: '相城区', value: 'xiangcheng' },
101
+ ],
102
+ wuxi: [
103
+ { label: '梁溪区', value: 'liangxi' },
104
+ { label: '滨湖区', value: 'binhu' },
105
+ { label: '惠山区', value: 'huishan' },
106
+ ],
107
+ }
108
+
109
+ // 表单数据
110
+ const formData = ref({
111
+ province: '',
112
+ city: '',
113
+ district: '',
114
+ address: '',
115
+ })
116
+
117
+ // 表单配置
118
+ const formConfig: FormConfig = {
119
+ items: [
120
+ {
121
+ prop: 'province',
122
+ label: '省份',
123
+ component: 'el-select',
124
+ componentProps: {
125
+ placeholder: '请选择省份',
126
+ clearable: true,
127
+ },
128
+ rules: [{ required: true, message: '请选择省份', trigger: 'change' }],
129
+ span: 8,
130
+ // 静态数据源
131
+ valueTransform: {
132
+ input: (value) => value,
133
+ output: (value) => value,
134
+ },
135
+ },
136
+ {
137
+ prop: 'city',
138
+ label: '城市',
139
+ component: 'el-select',
140
+ componentProps: {
141
+ placeholder: '请先选择省份',
142
+ clearable: true,
143
+ disabled: true,
144
+ },
145
+ rules: [{ required: true, message: '请选择城市', trigger: 'change' }],
146
+ span: 8,
147
+ // 级联配置:依赖省份字段
148
+ cascade: {
149
+ dependOn: 'province',
150
+ clearFields: ['city', 'district'], // 省份变化时清除城市和区县
151
+ handler: ({ dependValues, setComponentProps, clearValue }) => {
152
+ const province = dependValues.province
153
+
154
+ if (!province) {
155
+ // 如果省份为空,禁用城市选择器
156
+ setComponentProps({
157
+ placeholder: '请先选择省份',
158
+ disabled: true,
159
+ options: [],
160
+ })
161
+ clearValue()
162
+ return
163
+ }
164
+
165
+ // 根据省份加载城市数据
166
+ const cities = citiesMap[province] || []
167
+ setComponentProps({
168
+ placeholder: '请选择城市',
169
+ disabled: false,
170
+ options: cities,
171
+ })
172
+ },
173
+ },
174
+ },
175
+ {
176
+ prop: 'district',
177
+ label: '区县',
178
+ component: 'el-select',
179
+ componentProps: {
180
+ placeholder: '请先选择城市',
181
+ clearable: true,
182
+ disabled: true,
183
+ },
184
+ rules: [{ required: true, message: '请选择区县', trigger: 'change' }],
185
+ span: 8,
186
+ // 级联配置:依赖城市字段
187
+ cascade: {
188
+ dependOn: 'city',
189
+ clearFields: ['district'], // 城市变化时清除区县
190
+ handler: ({ dependValues, setComponentProps, clearValue }) => {
191
+ const city = dependValues.city
192
+
193
+ if (!city) {
194
+ // 如果城市为空,禁用区县选择器
195
+ setComponentProps({
196
+ placeholder: '请先选择城市',
197
+ disabled: true,
198
+ options: [],
199
+ })
200
+ clearValue()
201
+ return
202
+ }
203
+
204
+ // 根据城市加载区县数据
205
+ const districts = districtsMap[city] || []
206
+ setComponentProps({
207
+ placeholder: '请选择区县',
208
+ disabled: false,
209
+ options: districts,
210
+ })
211
+ },
212
+ },
213
+ },
214
+ {
215
+ prop: 'address',
216
+ label: '详细地址',
217
+ component: 'el-input',
218
+ componentProps: {
219
+ type: 'textarea',
220
+ rows: 3,
221
+ placeholder: '请输入详细地址',
222
+ },
223
+ rules: [{ required: true, message: '请输入详细地址', trigger: 'blur' }],
224
+ span: 24,
225
+ },
226
+ ],
227
+ }
228
+
229
+ // 为省份选择器添加静态选项
230
+ formConfig.items[0].componentProps!.options = provinces
231
+
232
+ // 表单引用
233
+ const formRef = ref()
234
+
235
+ // 处理提交
236
+ function handleSubmit(data: Record<string, any>) {
237
+ console.log('表单提交:', data)
238
+ ElMessage.success('表单提交成功!')
239
+ }
240
+ </script>
241
+
242
+ <style scoped lang="scss">
243
+ .cascade-demo {
244
+ padding: 20px;
245
+
246
+ h1 {
247
+ margin-bottom: 20px;
248
+ font-size: 24px;
249
+ font-weight: bold;
250
+ }
251
+
252
+ .demo-card {
253
+ margin-bottom: 20px;
254
+ }
255
+
256
+ pre {
257
+ background-color: #f5f5f5;
258
+ padding: 12px;
259
+ border-radius: 4px;
260
+ overflow-x: auto;
261
+ }
262
+ }
263
+ </style>
@@ -0,0 +1,176 @@
1
+ <template>
2
+ <div class="dynamic-form-plus-demo">
3
+ <h1>DynamicFormPlus 组件演示</h1>
4
+
5
+ <el-card class="demo-card">
6
+ <template #header>
7
+ <div class="card-header">
8
+ <span>基础表单示例</span>
9
+ <div>
10
+ <el-radio-group v-model="currentMode" size="small">
11
+ <el-radio-button label="create">新增</el-radio-button>
12
+ <el-radio-button label="edit">编辑</el-radio-button>
13
+ <el-radio-button label="view">查看</el-radio-button>
14
+ </el-radio-group>
15
+ </div>
16
+ </div>
17
+ </template>
18
+
19
+ <DynamicFormPlus
20
+ ref="formRef"
21
+ v-model="formData"
22
+ :config="formConfig"
23
+ :mode="currentMode"
24
+ :layout="layoutMode"
25
+ :col-span="12"
26
+ :gutter="16"
27
+ label-width="120px"
28
+ @submit="handleSubmit"
29
+ @reset="handleReset"
30
+ @change="handleChange"
31
+ />
32
+ </el-card>
33
+
34
+ <el-card class="demo-card" style="margin-top: 20px;">
35
+ <template #header>
36
+ <span>表单数据</span>
37
+ </template>
38
+ <pre>{{ JSON.stringify(formData, null, 2) }}</pre>
39
+ </el-card>
40
+ </div>
41
+ </template>
42
+
43
+ <script setup lang="ts">
44
+ import { ref } from 'vue'
45
+ import { ElMessage } from 'element-plus'
46
+ import { DynamicFormPlus } from '../../packages/components/DynamicFormPlus'
47
+ import type { FormConfig, FormMode, LayoutMode } from '../../packages/components/DynamicFormPlus'
48
+
49
+ // 表单模式
50
+ const currentMode = ref<FormMode>('create')
51
+ const layoutMode = ref<LayoutMode>('grid')
52
+
53
+ // 表单数据
54
+ const formData = ref({
55
+ name: '',
56
+ email: '',
57
+ age: null,
58
+ gender: '',
59
+ description: '',
60
+ })
61
+
62
+ // 表单配置
63
+ const formConfig: FormConfig = {
64
+ items: [
65
+ {
66
+ prop: 'name',
67
+ label: '姓名',
68
+ component: 'el-input',
69
+ componentProps: {
70
+ placeholder: '请输入姓名',
71
+ clearable: true,
72
+ },
73
+ rules: [
74
+ { required: true, message: '请输入姓名', trigger: 'blur' },
75
+ { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
76
+ ],
77
+ span: 12,
78
+ },
79
+ {
80
+ prop: 'email',
81
+ label: '邮箱',
82
+ component: 'el-input',
83
+ componentProps: {
84
+ placeholder: '请输入邮箱',
85
+ clearable: true,
86
+ },
87
+ rules: [
88
+ { required: true, message: '请输入邮箱', trigger: 'blur' },
89
+ { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' },
90
+ ],
91
+ span: 12,
92
+ },
93
+ {
94
+ prop: 'age',
95
+ label: '年龄',
96
+ component: 'el-input-number',
97
+ componentProps: {
98
+ min: 1,
99
+ max: 120,
100
+ controlsPosition: 'right',
101
+ },
102
+ span: 12,
103
+ },
104
+ {
105
+ prop: 'gender',
106
+ label: '性别',
107
+ component: 'el-select',
108
+ componentProps: {
109
+ placeholder: '请选择性别',
110
+ clearable: true,
111
+ },
112
+ span: 12,
113
+ },
114
+ {
115
+ prop: 'description',
116
+ label: '描述',
117
+ component: 'el-input',
118
+ componentProps: {
119
+ type: 'textarea',
120
+ rows: 4,
121
+ placeholder: '请输入描述',
122
+ maxlength: 200,
123
+ showWordLimit: true,
124
+ },
125
+ span: 24,
126
+ },
127
+ ],
128
+ }
129
+
130
+ // 表单引用
131
+ const formRef = ref()
132
+
133
+ // 处理提交
134
+ function handleSubmit(data: Record<string, any>) {
135
+ console.log('表单提交:', data)
136
+ ElMessage.success('表单提交成功!')
137
+ }
138
+
139
+ // 处理重置
140
+ function handleReset() {
141
+ console.log('表单重置')
142
+ ElMessage.info('表单已重置')
143
+ }
144
+
145
+ // 处理字段变更
146
+ function handleChange(prop: string, value: any) {
147
+ console.log('字段变更:', prop, value)
148
+ }
149
+ </script>
150
+
151
+ <style scoped lang="scss">
152
+ .dynamic-form-plus-demo {
153
+ padding: 20px;
154
+
155
+ h1 {
156
+ margin-bottom: 20px;
157
+ font-size: 24px;
158
+ font-weight: bold;
159
+ }
160
+
161
+ .demo-card {
162
+ .card-header {
163
+ display: flex;
164
+ justify-content: space-between;
165
+ align-items: center;
166
+ }
167
+ }
168
+
169
+ pre {
170
+ background-color: #f5f5f5;
171
+ padding: 12px;
172
+ border-radius: 4px;
173
+ overflow-x: auto;
174
+ }
175
+ }
176
+ </style>
@@ -0,0 +1,270 @@
1
+ // 动态表单配置文件
2
+ export const formConfig = {
3
+ // 表单数据初始值
4
+ formData: {
5
+ name: "",
6
+ email: "",
7
+ age: null,
8
+ gender: "",
9
+ hobbies: [],
10
+ isVip: false,
11
+ score: 0,
12
+ birthday: null,
13
+ city: [],
14
+ description: "",
15
+ customField: "",
16
+ province: "",
17
+ citySelect: "",
18
+ district: "",
19
+ category: "",
20
+ subcategory: "",
21
+ },
22
+
23
+ // 表单配置项
24
+ formItems: [
25
+ {
26
+ label: "输入框",
27
+ prop: "name",
28
+ component: "input",
29
+ span: 12,
30
+ required: true,
31
+ placeholder: "请输入输入框",
32
+ props: {
33
+ clearable: true,
34
+ },
35
+ },
36
+ {
37
+ label: "邮箱",
38
+ prop: "email",
39
+ component: "input",
40
+ span: 12,
41
+ required: true,
42
+ props: {
43
+ clearable: true,
44
+ },
45
+ rules: [
46
+ { type: "email", message: "请输入正确的邮箱地址", trigger: "blur" },
47
+ ],
48
+ },
49
+ {
50
+ label: "数值输入",
51
+ prop: "age",
52
+ component: "number",
53
+ span: 12,
54
+ props: {
55
+ min: 1,
56
+ max: 120,
57
+ controlsPosition: "right",
58
+ },
59
+ },
60
+ {
61
+ label: "单选",
62
+ prop: "gender",
63
+ component: "radio-group",
64
+ span: 12,
65
+ options: [
66
+ { label: "AAA", value: "male" },
67
+ { label: "BBB", value: "female" },
68
+ ],
69
+ },
70
+ {
71
+ label: "多选",
72
+ prop: "hobbies",
73
+ component: "checkbox-group",
74
+ span: 24,
75
+ options: [
76
+ { label: "读书", value: "reading" },
77
+ { label: "运动", value: "sports" },
78
+ { label: "音乐", value: "music" },
79
+ { label: "旅行", value: "travel" },
80
+ ],
81
+ },
82
+ {
83
+ label: "滑块",
84
+ prop: "isVip",
85
+ component: "switch",
86
+ span: 12,
87
+ },
88
+ {
89
+ label: "评分",
90
+ prop: "score",
91
+ component: "el-slider",
92
+ span: 12,
93
+ props: {
94
+ min: 0,
95
+ max: 100,
96
+ showStops: true,
97
+ showTooltip: false,
98
+ },
99
+ },
100
+ {
101
+ label: "日期选择",
102
+ prop: "birthday",
103
+ component: "date-picker",
104
+ span: 12,
105
+ props: {
106
+ type: "date",
107
+ format: "YYYY-MM-DD",
108
+ valueFormat: "YYYY-MM-DD",
109
+ },
110
+ },
111
+ {
112
+ label: "城市",
113
+ prop: "city",
114
+ component: "cascader",
115
+ span: 12,
116
+ options: [
117
+ {
118
+ value: "jiangsu",
119
+ label: "江苏省",
120
+ children: [
121
+ { value: "nanjing", label: "南京市" },
122
+ { value: "suzhou", label: "苏州市" },
123
+ ],
124
+ },
125
+ {
126
+ value: "zhejiang",
127
+ label: "浙江省",
128
+ children: [
129
+ { value: "hangzhou", label: "杭州市" },
130
+ { value: "ningbo", label: "宁波市" },
131
+ ],
132
+ },
133
+ ],
134
+ props: {
135
+ clearable: true,
136
+ filterable: true,
137
+ },
138
+ },
139
+ {
140
+ label: "长文本",
141
+ prop: "description",
142
+ component: "textarea",
143
+ span: 24,
144
+ props: {
145
+ rows: 4,
146
+ maxlength: 200,
147
+ showWordLimit: true,
148
+ },
149
+ },
150
+ {
151
+ label: "关联字段",
152
+ prop: "customField",
153
+ component: "input",
154
+ span: 24,
155
+ slot: "customSlot",
156
+ },
157
+ {
158
+ label: "级联A",
159
+ prop: "province",
160
+ component: "select",
161
+ span: 8,
162
+ optionsKey: "provinces",
163
+ props: {
164
+ clearable: true,
165
+ },
166
+ },
167
+ {
168
+ label: "城市",
169
+ prop: "citySelect",
170
+ component: "select",
171
+ span: 8,
172
+ dependsOn: {
173
+ field: "province",
174
+ optionsKey: "cities",
175
+ },
176
+ props: {
177
+ clearable: true,
178
+ },
179
+ },
180
+ {
181
+ label: "区县",
182
+ prop: "district",
183
+ component: "select",
184
+ span: 8,
185
+ dependsOn: {
186
+ field: "citySelect",
187
+ optionsKey: "districts",
188
+ },
189
+ props: {
190
+ clearable: true,
191
+ },
192
+ },
193
+ ],
194
+
195
+ // 表单验证规则
196
+ formRules: {
197
+ name: [
198
+ { required: true, message: "请输入输入框", trigger: "blur" },
199
+ { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
200
+ ],
201
+ email: [{ required: true, message: "请输入邮箱地址", trigger: "blur" }],
202
+ },
203
+
204
+ // 下拉框选项数据源
205
+ optionsData: {
206
+ provinces: [
207
+ { label: "江苏省", value: "jiangsu" },
208
+ { label: "浙江省", value: "zhejiang" },
209
+ { label: "上海市", value: "shanghai" },
210
+ ],
211
+ cities: {
212
+ jiangsu: [
213
+ { label: "南京市", value: "nanjing" },
214
+ { label: "苏州市", value: "suzhou" },
215
+ { label: "无锡市", value: "wuxi" },
216
+ ],
217
+ zhejiang: [
218
+ { label: "杭州市", value: "hangzhou" },
219
+ { label: "宁波市", value: "ningbo" },
220
+ { label: "温州市", value: "wenzhou" },
221
+ ],
222
+ shanghai: [
223
+ { label: "黄浦区", value: "huangpu" },
224
+ { label: "徐汇区", value: "xuhui" },
225
+ { label: "长宁区", value: "changning" },
226
+ ],
227
+ },
228
+ districts: {
229
+ nanjing: [
230
+ { label: "玄武区", value: "xuanwu" },
231
+ { label: "秦淮区", value: "qinhuai" },
232
+ { label: "建邺区", value: "jianye" },
233
+ ],
234
+ suzhou: [
235
+ { label: "姑苏区", value: "gusu" },
236
+ { label: "虎丘区", value: "huqiu" },
237
+ { label: "吴中区", value: "wuzhong" },
238
+ ],
239
+ hangzhou: [
240
+ { label: "西湖区", value: "xihu" },
241
+ { label: "拱墅区", value: "gongshu" },
242
+ { label: "江干区", value: "jianggan" },
243
+ ],
244
+ },
245
+ categories: [
246
+ { label: "电子产品", value: "electronics" },
247
+ { label: "服装鞋帽", value: "clothing" },
248
+ { label: "图书音像", value: "books" },
249
+ ],
250
+ subcategories: {
251
+ electronics: [
252
+ { label: "手机", value: "phone" },
253
+ { label: "电脑", value: "computer" },
254
+ { label: "平板", value: "tablet" },
255
+ ],
256
+ clothing: [
257
+ { label: "男装", value: "mens" },
258
+ { label: "女装", value: "womens" },
259
+ { label: "童装", value: "kids" },
260
+ ],
261
+ books: [
262
+ { label: "小说", value: "novel" },
263
+ { label: "技术书籍", value: "tech" },
264
+ { label: "教育", value: "education" },
265
+ ],
266
+ },
267
+ },
268
+ };
269
+
270
+ export default formConfig;