lw-cdp-ui 1.1.69 → 1.1.71

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,140 @@
1
+ <template>
2
+
3
+ <draggable :list="formItems"
4
+ item-key="name"
5
+ group="people"
6
+ ghost-class="draggable-ghost"
7
+ class="draggable-item"
8
+ :class="{ cur: formItems.name == curIndex }">
9
+ <template #item="{element: item, index}">
10
+ <el-col :span="item.span || 24"
11
+ :class="{ borderItem: !isBorder, cur: item.name == curIndex }"
12
+ v-if="!hideHandle(item)">
13
+ <!-- 组合/列表 -->
14
+ <template v-if="['object', 'list'].includes(item.component)">
15
+ <el-form-item :label="item.label"
16
+ class="padding-item"
17
+ @click.capture="changeClick(item)"
18
+ :class="{ borderItem: !isBorder }">
19
+ <JsonItem :formItems="item.value"
20
+ :form="form"
21
+ @curClick="changeClick"></JsonItem>
22
+ </el-form-item>
23
+ </template>
24
+ <FormItem v-else
25
+ :item="item"
26
+ :form="form"
27
+ @click="changeClick(item)">
28
+ <!-- 没有组件是component值 就是插槽名称 -->
29
+ <template v-if="!['input', 'upload', 'checkbox', 'checkboxGroup', 'switch', 'select', 'treeSelect', 'cascader', 'date', 'number', 'radio', 'color', 'rate', 'slider','tags'].includes(item.component)"
30
+ v-slot:[item.component]>
31
+ <slot :name="item.component"
32
+ :itemCur="item"
33
+ :formCur="form">
34
+ <el-tag type="danger">
35
+ [{{ item.component }}]
36
+ 没有这个默认组件也未自定义插槽内容
37
+ </el-tag>
38
+ </slot>
39
+ </template>
40
+ </FormItem>
41
+ </el-col>
42
+ </template>
43
+ </draggable>
44
+ </template>
45
+
46
+ <script>
47
+ import FormItem from '../lwFormMini/FormItem'
48
+ import draggable from 'vuedraggable'
49
+ export default {
50
+ name: 'JsonItem',
51
+ components: {
52
+ FormItem,
53
+ draggable
54
+ },
55
+ props: {
56
+ formItems: {
57
+ type: Object,
58
+ default: []
59
+ },
60
+ form: {
61
+ type: Object,
62
+ default: () => { }
63
+ },
64
+ // 是否是显示边界
65
+ isBorder: { type: Boolean, default: false }
66
+ },
67
+ computed: {
68
+ curIndex() {
69
+ return this.$store.state?.curIndex || ''
70
+ }
71
+ },
72
+ emits: ['curClick'],
73
+ methods: {
74
+ //处理动态隐藏
75
+ hideHandle(item) {
76
+ if (typeof item?.hideHandle === 'string') {
77
+ const func = new Function('form', `return ${item?.hideHandle.replace(/\$/g, "form")}`);
78
+ return func(this.form);
79
+ } else if (typeof item?.hideHandle === 'boolean') {
80
+ return item.hideHandle
81
+ }
82
+ return false
83
+ },
84
+ changeClick(item) {
85
+ this.$store.state.curIndex = item.name
86
+ this.$emit('curClick', item)
87
+ }
88
+ }
89
+ }
90
+ </script>
91
+
92
+ <style lang="scss" scoped>
93
+ .draggable-item {
94
+ width: 100%;
95
+ min-height: 40px;
96
+ display: flex;
97
+ flex-wrap: wrap;
98
+ border: 1px dashed var(--el-border-color-light);
99
+ background-color: rgba($color: #000000, $alpha: 0.01);
100
+ > div {
101
+ .title-name {
102
+ margin-top: 0;
103
+ }
104
+ &:first-child {
105
+ .title-name {
106
+ border-top: 0;
107
+ }
108
+ }
109
+ }
110
+ }
111
+ .padding-item {
112
+ padding: 10px;
113
+ background-color: rgba($color: #000000, $alpha: 0.01);
114
+ }
115
+
116
+ :deep(.el-col) {
117
+ border: 1px dashed transparent;
118
+ }
119
+ :deep(.cur) {
120
+ border: 1px dashed var(--el-color-primary) !important;
121
+ }
122
+
123
+ .borderItem {
124
+ border: 1px dashed var(--el-border-color-light);
125
+ box-sizing: border-box;
126
+ padding: 10px;
127
+ cursor: move;
128
+ &:hover {
129
+ border: 1px dashed var(--el-border-color);
130
+ background-color: rgba($color: #000000, $alpha: 0.01);
131
+ }
132
+ }
133
+
134
+ :deep(.draggable-ghost) {
135
+ background: var(--el-border-color-light);
136
+ border: 2px dashed var(--el-border-color);
137
+ padding: 10px;
138
+ width: 100%;
139
+ }
140
+ </style>
@@ -0,0 +1,203 @@
1
+ <template>
2
+ <el-form :model="item"
3
+ class="form-list"
4
+ label-position="top">
5
+ <div class="form-title">基础配置</div>
6
+ <div class="form-body">
7
+ <el-form-item label="属性名称">
8
+ <el-input v-model="item.name"
9
+ placeholder="请输入"
10
+ clearable />
11
+ </el-form-item>
12
+ <el-form-item label="标题">
13
+ <el-input v-model="item.label"
14
+ placeholder="请输入"
15
+ clearable />
16
+ </el-form-item>
17
+ <el-form-item v-if="item?.options"
18
+ label="输入提示">
19
+ <el-input v-model="item.options.placeholder"
20
+ placeholder="请输入"
21
+ clearable />
22
+ </el-form-item>
23
+ <el-form-item v-if="item?.options"
24
+ label="禁用">
25
+ <el-switch v-model="item.options.disabled"
26
+ active-text="启用"
27
+ inactive-text="禁用" />
28
+ </el-form-item>
29
+ </div>
30
+
31
+ <div class="form-title">布局配置</div>
32
+ <div class="form-body">
33
+ <el-form-item label="栅格">
34
+ <div class="form-slider">
35
+ <el-slider v-model="item.span"
36
+ :step="4"
37
+ show-stops
38
+ :min="4"
39
+ :max="24" />
40
+ </div>
41
+ </el-form-item>
42
+ </div>
43
+
44
+ <div v-if="item?.options"
45
+ class="form-title">组件属性配置</div>
46
+ <div v-if="item?.options"
47
+ class="form-body">
48
+ <template v-if="item.component === 'input'">
49
+ <el-form-item label="最大长度">
50
+ <el-input-number v-model="item.options.maxlength"
51
+ :min="1"
52
+ controls-position="right"
53
+ placeholder="请输入最大长度" />
54
+ </el-form-item>
55
+
56
+ <el-form-item label="行数">
57
+ <el-input-number v-model="item.options.rows"
58
+ :min="1"
59
+ controls-position="right"
60
+ placeholder="请输入行数" />
61
+ </el-form-item>
62
+ <el-form-item label="输入类型">
63
+ <el-select v-model="item.options.type"
64
+ placeholder="请选择输入类型">
65
+ <el-option label="文本"
66
+ value="text" />
67
+ <el-option label="密码"
68
+ value="password" />
69
+ <el-option label="邮箱"
70
+ value="email" />
71
+ </el-select>
72
+ </el-form-item>
73
+ </template>
74
+ <template
75
+ v-if="['select','radio','checkbox', 'checkboxGroup'].includes(item.component)">
76
+ <el-form-item label="选项">
77
+ <div v-for="(option, index) in item.options.items"
78
+ :key="index"
79
+ class="form-item">
80
+ <el-input v-model="option.label"
81
+ placeholder="选项名称"
82
+ clearable />
83
+ <el-input v-model="option.value"
84
+ placeholder="选项值"
85
+ clearable />
86
+ <el-button type="danger"
87
+ @click="item.options.items.splice(index, 1)">删除</el-button>
88
+ </div>
89
+ <el-button type="primary"
90
+ size="small"
91
+ style="width: 100%;"
92
+ @click="item.options.items.push({ label: '', value: '' })">新增选项</el-button>
93
+ </el-form-item>
94
+
95
+ </template>
96
+
97
+ <el-form-item v-if="item.component === 'select'"
98
+ label="多选">
99
+ <el-switch v-model="item.options.multiple"
100
+ active-text="允许"
101
+ inactive-text="不允许" />
102
+ </el-form-item>
103
+
104
+ <el-form-item v-if="item.component === 'switch'"
105
+ label="开关文字">
106
+ <el-input v-model="item.options.activeText"
107
+ placeholder="开文字"
108
+ clearable />
109
+ <el-input v-model="item.options.inactiveText"
110
+ placeholder="关文字"
111
+ clearable />
112
+ </el-form-item>
113
+
114
+ <template v-if="['rate','number'].includes(item.component)">
115
+ <el-form-item v-if="item.component === 'rate'"
116
+ label="最小值">
117
+ <el-input-number v-model="item.options.min"
118
+ controls-position="right"
119
+ placeholder="最小值" />
120
+ </el-form-item>
121
+ <el-form-item label="最大值">
122
+ <el-input-number v-model="item.options.max"
123
+ controls-position="right"
124
+ placeholder="最大值" />
125
+ </el-form-item>
126
+ </template>
127
+
128
+ <template v-if="item.component === 'upload'"
129
+ v-for="(_item, _index) in item?.options.items"
130
+ :key="_index">
131
+ <el-form-item label="数量限制">
132
+ <el-input-number v-model="_item.limit"
133
+ :min="1"
134
+ controls-position="right"
135
+ placeholder="最大文件数量" />
136
+ </el-form-item>
137
+ <el-form-item label="文件大小">
138
+ <el-input-number v-model="_item.maxSize"
139
+ :min="1"
140
+ controls-position="right"
141
+ placeholder="最大文件数量" />
142
+ </el-form-item>
143
+ <el-form-item label="文件类型">
144
+ <el-input v-model="_item.accept"
145
+ placeholder="请输入文件类型,如 .jpg,.png" />
146
+ </el-form-item>
147
+ <el-form-item label="禁用">
148
+ <el-switch v-model="_item.disabled"
149
+ active-text="启用"
150
+ inactive-text="禁用" />
151
+ </el-form-item>
152
+ </template>
153
+
154
+ </div>
155
+ </el-form>
156
+ </template>
157
+
158
+ <script>
159
+ export default {
160
+ name: 'StatsConfig',
161
+ props: {
162
+ item: {
163
+ type: Object,
164
+ default: () => ({})
165
+ },
166
+ form: {
167
+ type: Object,
168
+ default: () => ({})
169
+ },
170
+ isBorder: {
171
+ type: Boolean,
172
+ default: false
173
+ }
174
+ }
175
+ };
176
+ </script>
177
+
178
+ <style lang="scss" scoped>
179
+ .form-list {
180
+ max-height: calc(100vh - 137px);
181
+ .form-title {
182
+ font-size: 12px;
183
+ color: #000;
184
+ font-weight: bold;
185
+ padding: 15px 10px;
186
+ background-color: var(--el-fill-color-lighter);
187
+ }
188
+ .form-body {
189
+ padding: 10px;
190
+ }
191
+ .form-slider {
192
+ padding: 0 10px;
193
+ width: 100%;
194
+ }
195
+ .form-item {
196
+ width: 100%;
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 5px;
200
+ margin-bottom: 10px;
201
+ }
202
+ }
203
+ </style>
@@ -0,0 +1,299 @@
1
+ <template>
2
+ <el-container>
3
+ <el-aside width="200px"
4
+ class="form-json-body">
5
+ <div class="menu-list">
6
+ <el-collapse v-model="activeNames"
7
+ @change="handleChange">
8
+ <el-collapse-item v-for="item in menuList"
9
+ :title="item.title"
10
+ :name="item.title"
11
+ class="menu-list-title">
12
+ <draggable class="menu-list-group"
13
+ :list="item.list"
14
+ :group="{ name: 'people', pull: 'clone', put: false }"
15
+ :clone="cloneItem"
16
+ item-key="name">
17
+ <template #item="{ element }">
18
+ <div class="list-group-item">
19
+ {{ element.label }}
20
+ </div>
21
+ </template>
22
+ </draggable>
23
+ </el-collapse-item>
24
+ </el-collapse>
25
+ </div>
26
+
27
+ </el-aside>
28
+ <el-main class="form-json-body">
29
+ <el-form ref="form"
30
+ :model="form"
31
+ :label-width="localConfig.labelWidth"
32
+ :label-position="$i18n.locale == 'en-us' ? 'top' : localConfig.labelPosition"
33
+ v-loading="loading"
34
+ :disabled="isView"
35
+ element-loading-text="Loading...">
36
+ <el-row :gutter="15">
37
+ <JsonItem :formItems="localConfig.formItems"
38
+ :isBorder="isBorder"
39
+ :form="form"
40
+ @curClick="selectItem">
41
+ </JsonItem>
42
+ </el-row>
43
+
44
+ </el-form>
45
+ </el-main>
46
+ <el-aside width="300px"
47
+ class="aside-right">
48
+ <StatsConfig :item="curItem" />
49
+ </el-aside>
50
+ </el-container>
51
+ </template>
52
+
53
+ <script>
54
+ import JsonItem from './JsonItem.vue'
55
+ import StatsConfig from './StatsConfig.vue'
56
+ import MenuList from './menuList'
57
+ import draggable from 'vuedraggable'
58
+ export default {
59
+ components: {
60
+ JsonItem,
61
+ StatsConfig,
62
+ draggable
63
+ },
64
+ props: {
65
+ modelValue: { type: Object, default: () => { } },
66
+ /**
67
+ * 配置项
68
+ * {
69
+ * formItems: [{
70
+ * span: 24,
71
+ * component: 'input', // 可选项有 input, textarea, select, cascader, date, number, radio, checkbox, switch, color, rate, slider, tags, divider, upload, 自定义名称
72
+ * name: 'name', // 表单字段名称
73
+ * label: '名称', // 表单字段名称
74
+ * message: '提示信息', // 表单字段名称
75
+ * value: '1', // 表单字段值
76
+ * options: {
77
+ * name: 'name', // 表单字段名称
78
+ * type: 'input|', // 输入框类型
79
+ * placeholder: '请输入', // 表单字段名称
80
+ * disabled: false, // 表单字段名称
81
+ * maxlength: 10, // 表单字段名称
82
+ * append: '元', // 表单字段名称
83
+ * items: [{
84
+ * value: '1',
85
+ * label: '选项1'
86
+ * }, {
87
+ * value: '2',
88
+ * label: '选项2'
89
+ * }]
90
+ * }
91
+ * ...
92
+ * }]
93
+ * labelWidth: '100px', // 表单域标签的宽度
94
+ * labelPosition: 'top', // 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 label-width
95
+ * ...
96
+ * }
97
+ * **/
98
+ config: { type: Object, default: () => { } },
99
+ // 是否显示加载中
100
+ loading: { type: Boolean, default: false },
101
+ // 是否是查看模式
102
+ isView: { type: Boolean, default: false },
103
+ // 是否是显示边界
104
+ isBorder: { type: Boolean, default: false }
105
+ },
106
+ data() {
107
+ return {
108
+ form: {},
109
+ localConfig: {},
110
+ curItem: {},
111
+ scrollObserverEnabled: true,
112
+ renderLoading: false,
113
+ activeNames: '布局组件',
114
+ menuList: MenuList
115
+ }
116
+ },
117
+ emits: ['update:config'],
118
+ watch: {
119
+ modelValue: {
120
+ handler(val, old) {
121
+ if (this.hasConfig) {
122
+ let form = this.flattenObject(val)
123
+ this.form = Object.assign(this.form, form)
124
+ }
125
+ },
126
+ deep: true
127
+ },
128
+ config() {
129
+ this.render()
130
+ },
131
+ form: {
132
+ handler(val) {
133
+ this.$emit("update:modelValue", this.unflattenObject(val))
134
+ },
135
+ deep: true
136
+ },
137
+ localConfig: {
138
+ handler(val) {
139
+ this.$emit("update:config", val)
140
+ },
141
+ deep: true
142
+ },
143
+ },
144
+ computed: {
145
+ hasConfig() {
146
+ return Object.keys(this.config).length > 0
147
+ },
148
+ hasValue() {
149
+ return Object.keys(this.modelValue).length > 0
150
+ },
151
+ },
152
+ mounted() {
153
+ if (this.hasConfig) {
154
+ this.render()
155
+ }
156
+ },
157
+ methods: {
158
+ /**
159
+ * 渲染表单数据。
160
+ * 遍历配置中的表单项,根据不同组件类型初始化表单数据。
161
+ * 对于复选框和上传组件,将选项值存储在对象中。
162
+ * 对于其他组件,直接将值赋给表单数据。
163
+ * 如果存在当前值,则将其与表单数据进行深度合并。
164
+ */
165
+ render() {
166
+ this.form = {}
167
+ this.config.formItems.forEach((item) => {
168
+ if (item.component == 'checkbox' || item.component == 'upload') {
169
+ if (item.name) {
170
+ const value = {}
171
+ item?.options.items.forEach((option) => {
172
+ value[option.name] = option.value
173
+ })
174
+ this.form[item.name] = value
175
+ } else if (item?.options?.items) {
176
+ item?.options.items.forEach((option) => {
177
+ this.form[option.name] = option.value
178
+ })
179
+ }
180
+ } else {
181
+ if (item.component == 'number') {
182
+ this.form[item.name] = item?.value ? Number(item.value) : item?.options?.min || 0
183
+ } else {
184
+ this.form[item.name] = item.value
185
+ }
186
+
187
+ }
188
+ })
189
+
190
+ if (this.hasValue) {
191
+ let form = this.flattenObject(this.modelValue)
192
+ this.form = Object.assign(this.form, form)
193
+ }
194
+ this.localConfig = this.config
195
+ this.curItem = this.config
196
+ },
197
+ flattenObject(obj, prefix = '') {
198
+ let result = {};
199
+
200
+ for (let key in obj) {
201
+ if (obj.hasOwnProperty(key)) {
202
+ const newKey = prefix ? `${prefix}.${key}` : key;
203
+
204
+ if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
205
+ // 递归处理嵌套对象
206
+ Object.assign(result, this.flattenObject(obj[key], newKey));
207
+ } else {
208
+ result[newKey] = obj[key];
209
+ }
210
+ }
211
+ }
212
+
213
+ return result;
214
+ },
215
+ unflattenObject(obj) {
216
+ const result = {};
217
+ for (let key in obj) {
218
+ if (obj.hasOwnProperty(key)) {
219
+ const keys = key.split('.'); // 按 `.` 分割键名
220
+ keys.reduce((acc, part, index) => {
221
+ if (index === keys.length - 1) {
222
+ acc[part] = obj[key];
223
+ } else {
224
+ acc[part] = acc[part] || {}; // 如果没有该键,创建一个空对象
225
+ }
226
+ return acc[part];
227
+ }, result);
228
+ }
229
+ }
230
+
231
+ return result;
232
+ },
233
+ //数据验证
234
+ validate(valid, obj) {
235
+ return this.$refs.form.validate(valid, obj)
236
+ },
237
+ clearValidate(valid, obj) {
238
+ return this.$refs.form.clearValidate()
239
+ },
240
+ scrollToField(prop) {
241
+ return this.$refs.form.scrollToField(prop)
242
+ },
243
+ resetFields() {
244
+ return this.$refs.form.resetFields()
245
+ },
246
+ // 拷贝数据
247
+ cloneItem(item) {
248
+ let clone = JSON.parse(JSON.stringify(item));
249
+ return {
250
+ ...clone,
251
+ name: `${item.component}-${this.$tool.getUUID('', 2)}`
252
+ };
253
+ },
254
+ // 选中Item
255
+ selectItem(item) {
256
+ console.log(item)
257
+ this.curItem = item
258
+ }
259
+ }
260
+ }
261
+ </script>
262
+
263
+ <style lang="scss" scoped>
264
+ .menu-list {
265
+ .menu-list-title {
266
+ :deep(.el-collapse-item__header) {
267
+ font-weight: bold;
268
+ padding-left: 10px;
269
+ background-color: var(--el-menu-bg-color);
270
+ }
271
+ }
272
+ :deep(.el-collapse-item__content){
273
+ padding-bottom: 10px;
274
+ }
275
+ .menu-list-group {
276
+ display: flex;
277
+ flex-wrap: wrap;
278
+ gap: 10px;
279
+ padding: 10px;
280
+ .list-group-item {
281
+ width: calc(50% - 5px);
282
+ background-color: var(--el-fill-color-light);
283
+ color: var(--el-button-text-color);
284
+ padding: 8px 10px;
285
+ font-size: 12px;
286
+ text-align: center;
287
+ cursor: move;
288
+ }
289
+ }
290
+ }
291
+
292
+ .aside-right {
293
+ border-right: 0;
294
+ border-left: 1px solid var(--el-border-color-light);
295
+ }
296
+ .form-json-body {
297
+ max-height: calc(100vh - 137px);
298
+ }
299
+ </style>