Skip to content

Commit d818819

Browse files
feat: tree
1 parent 063fa47 commit d818819

4 files changed

Lines changed: 79 additions & 129 deletions

File tree

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

Lines changed: 50 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import type { DynamicFormValue } from '../../type'
33
import { computed, onMounted, reactive } from 'vue'
44
5-
import { ElMessage, ElMessageBox } from 'element-plus'
5+
import { MsgConfirm, MsgError, MsgSuccess, MsgWarning } from '@/utils/message'
66
const props = defineProps<{ modelValue: DynamicFormValue }>()
77
const emit = defineEmits(['update:modelValue'])
88
const formValue = computed({
@@ -93,15 +93,6 @@ function openAddChildDialog(node: TreeNode) {
9393
addDialog.formList = [createEmptyRow()]
9494
}
9595
96-
function appendAddRow() {
97-
addDialog.formList.push(createEmptyRow())
98-
}
99-
100-
function removeAddRow(index: number) {
101-
if (addDialog.formList.length === 1) return
102-
addDialog.formList.splice(index, 1)
103-
}
104-
10596
function closeAddDialog() {
10697
addDialog.visible = false
10798
addDialog.mode = 'root'
@@ -115,7 +106,7 @@ function submitAdd() {
115106
.filter((item) => item.label && item.value)
116107
117108
if (!validList.length) {
118-
ElMessage.warning('请至少填写一条完整数据')
109+
MsgWarning('请至少填写一条完整数据')
119110
return
120111
}
121112
@@ -126,7 +117,7 @@ function submitAdd() {
126117
} else {
127118
const parent = addDialog.parentNode
128119
if (!parent) {
129-
ElMessage.error('未找到父节点')
120+
MsgError('未找到父节点')
130121
return
131122
}
132123
@@ -136,7 +127,7 @@ function submitAdd() {
136127
parent.children.push(...newNodes)
137128
}
138129
139-
ElMessage.success('保存成功')
130+
MsgSuccess('保存成功')
140131
closeAddDialog()
141132
}
142133
@@ -161,32 +152,32 @@ function submitEdit() {
161152
const value = editDialog.form.value.trim()
162153
163154
if (!label || !value) {
164-
ElMessage.warning('标签和选项值不能为空')
155+
MsgWarning('标签和选项值不能为空')
165156
return
166157
}
167158
168159
if (!editDialog.targetNode) {
169-
ElMessage.error('未找到父节点')
160+
MsgError('未找到父节点')
170161
return
171162
}
172163
173164
editDialog.targetNode.label = label
174165
editDialog.targetNode.value = value
175166
176-
ElMessage.success('保存成功')
167+
MsgSuccess('保存成功')
177168
closeEditDialog()
178169
}
179170
180171
/* -------------------- 删除 -------------------- */
181172
182173
function handleDelete(node: TreeNode) {
183-
ElMessageBox.confirm(`确定删除「${node.label}」吗?`, '提示', { type: 'warning' })
174+
MsgConfirm('提示', `确定删除「${node.label}」吗?`, { type: 'warning' })
184175
.then(() => {
185176
const removed = removeNodeById(formValue.value.treeData, node.id)
186177
if (removed) {
187-
ElMessage.success('删除成功')
178+
MsgSuccess('删除成功')
188179
} else {
189-
ElMessage.error('删除失败')
180+
MsgError('删除失败')
190181
}
191182
})
192183
.catch(() => {})
@@ -216,45 +207,34 @@ function removeNodeById(list: TreeNode[], targetId: string): boolean {
216207
</script>
217208

218209
<template>
219-
<el-form-item prop="treeData" :rules="[{ message: '选项必填', blur: 'change', type: 'array', min: 1 }]">
210+
<el-form-item prop="treeData" class="mk-hide-asterisk" :rules="[{ required: true, message: '请添加选项', blur: 'change', type: 'array', min: 1 }]">
220211
<template #label>
221212
<div class="flex-between">
222-
<span>
223-
选项
224-
<span class="color-danger">*</span>
225-
</span>
226-
<div class="flex">
227-
<el-checkbox v-model="formValue.multiple" label="允许多选" size="large" class="pr-8" />
213+
<span class="mk-required">选项</span>
214+
<div class="flex gap-3">
215+
<el-checkbox v-model="formValue.multiple" label="允许多选" />
228216
<el-button link type="primary" @click="openAddRootDialog">
229-
<MkIcon name="icon_add_outlined" class="mr-4"></MkIcon>
217+
<MkIcon name="icon_add_outlined"></MkIcon>
230218
</el-button>
231219
</div>
232220
</div>
233221
</template>
234-
<el-card shadow="never" class="border-r-6 w-full" style="--el-card-padding: 8px">
222+
<el-card shadow="never" class="w-full" style="--el-card-padding: 12px">
235223
<el-tree :data="formValue.treeData" node-key="id" default-expand-all :expand-on-click-node="false" :props="treeProps" class="option-tree">
236224
<template #default="{ data, node }">
237-
<div class="flex-between w-full">
238-
<div class="ellipsis" :title="`${data.label}-${data.value}`" style="max-width: 350px">
239-
<span>{{ data.label }}-{{ data.value }}</span>
240-
</div>
241-
242-
<div>
243-
<span class="mr-4" v-if="node.level < 5">
244-
<el-button link @click.stop="openAddChildDialog(data)">
245-
<MkIcon name="icon_add_outlined" class="color-secondary"></MkIcon>
246-
</el-button>
247-
</span>
248-
<span class="mr-4">
249-
<el-button link @click.stop="openEditDialog(data)">
250-
<MkIcon name="icon_edit_outlined" class="color-secondary"></MkIcon>
251-
</el-button>
252-
</span>
253-
<span>
254-
<el-button link @click.stop="handleDelete(data)">
255-
<MkIcon name="icon_delete-trash_outlined" class="color-secondary"></MkIcon>
256-
</el-button>
257-
</span>
225+
<div class="flex-between min-w-0 flex-1 gap-2">
226+
<div class="min-w-0 flex-1 truncate" :title="`${data.label}-${data.value}`">{{ data.label }}-{{ data.value }}</div>
227+
<div class="flex shrink-0 items-center">
228+
<el-button text @click.stop="openEditDialog(data)">
229+
<MkIcon name="icon_edit_outlined"></MkIcon>
230+
</el-button>
231+
<el-button text @click.stop="openAddChildDialog(data)" v-if="node.level < 5">
232+
<MkIcon name="icon_add_outlined"></MkIcon>
233+
</el-button>
234+
235+
<el-button text @click.stop="handleDelete(data)">
236+
<MkIcon name="icon_delete-trash_outlined"></MkIcon>
237+
</el-button>
258238
</div>
259239
</div>
260240
</template>
@@ -283,94 +263,40 @@ function removeNodeById(list: TreeNode[], targetId: string): boolean {
283263
/>
284264
</el-form-item>
285265
<!-- 添加弹窗 -->
286-
<MkDialog
287-
v-model="addDialog.visible"
288-
:title="addDialog.mode === 'root' ? '添加一级选项' : '添加子选项'"
289-
label-position="top"
290-
require-asterisk-position="right"
291-
@submit.prevent
292-
>
293-
<el-scrollbar>
294-
<el-row :gutter="8" style="margin-right: 10px" class="tag-list-max-list">
295-
<template v-for="(item, index) in addDialog.formList" :key="index">
296-
<el-col :span="12">
297-
<el-form-item>
298-
<template #label>
299-
{{ index === 0 ? '标签' : '' }}
300-
<span class="color-danger" v-if="index === 0"> *</span>
301-
</template>
302-
<el-input v-model.trim="item.label" class="w-full" placeholder="请输入选项标签" maxlength="50"></el-input>
303-
</el-form-item>
304-
</el-col>
305-
<el-col :span="11">
306-
<el-form-item class="w-full">
307-
<template #label>
308-
{{ index === 0 ? '选项值' : '' }}
309-
<span class="color-danger" v-if="index === 0">*</span>
310-
</template>
311-
<el-input v-model.trim="item.value" placeholder="请输入选项值" maxlength="100"></el-input>
312-
</el-form-item>
313-
</el-col>
314-
<el-col :span="1">
315-
<el-button
316-
:disabled="addDialog.formList.length === 1"
317-
link
318-
@click="removeAddRow(index)"
319-
:style="{ marginTop: index === 0 ? '35px' : '12px' }"
320-
>
321-
<MkIcon name="icon_delete-trash_outlined"></MkIcon>
322-
</el-button>
323-
</el-col>
266+
<MkDialog v-model="addDialog.visible" :title="addDialog.mode === 'root' ? '添加一级选项' : '添加子选项'">
267+
<el-form :model="addDialog" label-position="top" require-asterisk-position="right" @submit.prevent>
268+
<MkFormList v-model="addDialog.formList" :default-item="createEmptyRow" item-key="key">
269+
<template #default="{ index, item: option }">
270+
<el-form-item :label="index === 0 ? '标签' : ''" :required="index === 0" class="min-w-0 flex-1 small">
271+
<el-input v-model.trim="option.label" placeholder="请输入选项标签" maxlength="50" />
272+
</el-form-item>
273+
<el-form-item :label="index === 0 ? '选项值' : ''" :required="index === 0" class="min-w-0 flex-1 small">
274+
<el-input v-model.trim="option.value" placeholder="请输入选项值" maxlength="100" />
275+
</el-form-item>
324276
</template>
325-
</el-row>
326-
</el-scrollbar>
327-
<el-button link type="primary" @click="appendAddRow">
328-
<MkIcon name="icon_add_outlined" class="mr-4" />
329-
添加
330-
</el-button>
277+
</MkFormList>
278+
</el-form>
331279
<template #footer>
332-
<el-button @click="closeAddDialog">取消</el-button>
280+
<el-button plain @click="closeAddDialog">取消</el-button>
333281
<el-button type="primary" @click="submitAdd">添加</el-button>
334282
</template>
335283
</MkDialog>
336284

337285
<!-- 编辑弹窗 -->
338-
<MkDialog
339-
:close-on-click-modal="true"
340-
:close-on-press-escape="true"
341-
v-model="editDialog.visible"
342-
title="编辑"
343-
width="520px"
344-
destroy-on-close
345-
label-position="top"
346-
require-asterisk-position="right"
347-
@submit.prevent
348-
>
349-
<el-row :gutter="8">
350-
<el-col :span="12">
351-
<el-form-item>
352-
<template #label>
353-
标签
354-
<span class="color-danger"> *</span>
355-
</template>
286+
<MkDialog :close-on-click-modal="true" :close-on-press-escape="true" v-model="editDialog.visible" title="编辑">
287+
<el-form :model="editDialog.form" label-position="top" require-asterisk-position="right" @submit.prevent>
288+
<div class="flex gap-2">
289+
<el-form-item label="标签" required class="min-w-0 flex-1 small">
356290
<el-input v-model.trim="editDialog.form.label" placeholder="请输入选项标签" maxlength="50" />
357291
</el-form-item>
358-
</el-col>
359-
<el-col :span="12">
360-
<el-form-item class="w-full">
361-
<template #label>
362-
选项值
363-
<span class="color-danger">*</span>
364-
</template>
292+
<el-form-item label="选项值" required class="min-w-0 flex-1 small">
365293
<el-input v-model.trim="editDialog.form.value" placeholder="请输入选项值" maxlength="100" />
366294
</el-form-item>
367-
</el-col>
368-
</el-row>
369-
295+
</div>
296+
</el-form>
370297
<template #footer>
371-
<el-button @click="closeEditDialog">取消</el-button>
298+
<el-button plain @click="closeEditDialog">取消</el-button>
372299
<el-button type="primary" @click="submitEdit">保存</el-button>
373300
</template>
374301
</MkDialog>
375302
</template>
376-
<style lang="scss" scoped></style>

ui/src/components/mk-dynamics-form/items/upload/UploadInput.vue

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import type { DynamicFormValue } from '../../type'
33
import { computed, inject, ref, useAttrs } from 'vue'
4-
import { ElMessage } from 'element-plus'
4+
import { MsgWarning } from '@/utils/message'
55
import type { FormField } from '@/components/mk-dynamics-form/type'
66
import { downloadByURL, getAttrsArray, getFileUrl } from '@/utils/common'
77
import { getFileExtension, getFileIconUrl } from '@/utils/icon'
@@ -45,7 +45,9 @@ const images = computed(() => filesWithUrl.value.filter(ofType(imageExtensions))
4545
const audioFiles = computed(() => filesWithUrl.value.filter(ofType(audioExtensions)))
4646
const videoFiles = computed(() => filesWithUrl.value.filter(ofType(videoExtensions)))
4747
// 非图片/音频/视频的(文档、压缩包等)统一走下载卡片
48-
const downloadFiles = computed(() => filesWithUrl.value.filter((f: DynamicFormValue) => !ofType([...imageExtensions, ...audioExtensions, ...videoExtensions])(f)))
48+
const downloadFiles = computed(() =>
49+
filesWithUrl.value.filter((f: DynamicFormValue) => !ofType([...imageExtensions, ...audioExtensions, ...videoExtensions])(f)),
50+
)
4951
5052
function downloadFile(item: DynamicFormValue) {
5153
downloadByURL(item.url, item.name)
@@ -56,18 +58,18 @@ const loading = ref<boolean>(false)
5658
const uploadFile = async (file: DynamicFormValue, fileList: DynamicFormValue[]) => {
5759
fileList.splice(fileList.indexOf(file), 1)
5860
if (fileArray.value.find((f: DynamicFormValue) => f.name === file.name)) {
59-
ElMessage.warning('文件名重复')
61+
MsgWarning('文件名重复')
6062
6163
return
6264
}
6365
const maxFileSize = (props.formField as DynamicFormValue).max_file_size
6466
if (file.size / 1024 / 1024 > maxFileSize) {
65-
ElMessage.warning('文件大小不能超过 ' + maxFileSize + 'MB')
67+
MsgWarning('文件大小不能超过 ' + maxFileSize + 'MB')
6668
return
6769
}
6870
6971
if (fileList.length > attrs.limit) {
70-
ElMessage.warning('最多只能上传 ' + attrs.limit + ' 个文件')
72+
MsgWarning('最多只能上传 ' + attrs.limit + ' 个文件')
7173
return
7274
}
7375
upload(file.raw, loading).then((ok: DynamicFormValue) => {

ui/src/styles/STYLE_README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ Tailwind v4 的 `border` 只设置边框宽度和样式,不提供 `--tw-border
397397
所有 `el-button``:active``.is-active` 视觉统一复用各自的 hover 样式,不额外加深背景、
398398
边框或文字颜色;普通、plain、text、link、circle 及各语义类型按钮均遵循该规则。
399399

400+
Tree 的 `.el-tree-node__content` 统一保留 4px 底部间距,使同级节点及展开后的父子节点行
401+
保持一致间距;规则维护在 `element-plus.scss` 的 tree 分组。
402+
400403
按钮式多选使用 `el-checkbox-group``el-checkbox-button`,外观与按钮式单选一致:
401404
组容器提供圆角边框、4px 内边距和间距,选中项使用主题色文字与浅色背景,不显示相邻按钮连接阴影。
402405
规则统一维护在 `element-plus.scss` 的 checkbox 分组;仅包含 `el-checkbox-button` 的组容器

ui/src/styles/element-plus.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,25 @@
11721172
}
11731173
}
11741174

1175+
/* tree */
1176+
.el-tree {
1177+
--el-tree-node-content-height: 32px;
1178+
--el-tree-node-hover-bg-color: var(--mk-N900-transparent-10);
1179+
--el-tree-text-color: var(--el-text-color-primary);
1180+
--el-tree-expand-icon-color: var(--mk-N600);
1181+
.el-tree-node__content {
1182+
border-radius: var(--el-border-radius-base);
1183+
margin-bottom: var(--spacing);
1184+
padding-right: calc(var(--spacing) * 2);
1185+
}
1186+
.el-tree-node__expand-icon {
1187+
font-size: 14px;
1188+
}
1189+
.el-tree__empty-block {
1190+
min-height: 22px;
1191+
}
1192+
}
1193+
11751194
/* upload */
11761195
.el-upload {
11771196
--el-upload-dragger-padding-horizontal: 40px;

0 commit comments

Comments
 (0)