feat(tree): add tree component and wip workspace

This commit is contained in:
落小梅 2021-10-09 14:59:38 +08:00
parent 393ff8c9e0
commit 7dbb3b0353
8 changed files with 559 additions and 38 deletions

View File

@ -0,0 +1,56 @@
::: demo
<template>
<lay-tree
:data="data"
>
</lay-tree>
</template>
<script setup>
import { ref } from 'vue'
const data = ref([{
title: '江西'
,id: 1
,children: [{
title: '南昌'
,id: 1000
,children: [{
title: '青山湖区'
,id: 10001
},{
title: '高新区'
,id: 10002
}]
},{
title: '九江'
,id: 1001
},{
title: '赣州'
,id: 1002
}]
},{
title: '广西'
,id: 2
,children: [{
title: '南宁'
,id: 2000
},{
title: '桂林'
,id: 2001
}]
},{
title: '陕西'
,id: 3
,children: [{
title: '西安'
,id: 3000
},{
title: '延安'
,id: 3001
}]
}])
</script>
:::

View File

@ -18,14 +18,10 @@
style="margin-top: 0px; margin-bottom: 0px" style="margin-top: 0px; margin-bottom: 0px"
> >
<li class="layui-nav-item"> <li class="layui-nav-item">
<a href="https://gitee.com/layui-vue/layui-vue-sample"> <a href="https://gitee.com/layui-vue/layui-vue-sample"> 案例 </a>
案例
</a>
</li> </li>
<li class="layui-nav-item"> <li class="layui-nav-item">
<a href="https://gitee.com/layui-vue/layui-vue"> <a href="https://gitee.com/layui-vue/layui-vue"> 仓库 </a>
仓库
</a>
</li> </li>
<li class="layui-nav-item"> <li class="layui-nav-item">
<a <a
@ -37,11 +33,16 @@
</ul> </ul>
</lay-header> </lay-header>
<lay-side> <lay-side>
<ul class="layui-menu layui-menu-lg layui-menu-docs" style="padding:6px"> <ul
class="layui-menu layui-menu-lg layui-menu-docs"
style="padding: 6px"
>
<li <li
:key="menu" :key="menu"
v-for="menu in menus" v-for="menu in menus"
:class="[currentPath === menu.path ? 'layui-menu-item-checked2' : '']" :class="[
currentPath === menu.path ? 'layui-menu-item-checked2' : '',
]"
@click="handleClick(menu)" @click="handleClick(menu)"
> >
<div class="layui-menu-body-title"> <div class="layui-menu-body-title">
@ -70,7 +71,7 @@ export default {
setup() { setup() {
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const currentPath = ref("hahaha") const currentPath = ref('hahaha')
watch( watch(
() => route.path, () => route.path,
@ -247,12 +248,19 @@ export default {
title: '选项卡', title: '选项卡',
subTitle: 'tab', subTitle: 'tab',
path: '/zh-CN/components/tab', path: '/zh-CN/components/tab',
},{ },
{
id: 29, id: 29,
title: '图标选择', title: '图标选择',
subTitle: 'iconPicker', subTitle: 'iconPicker',
path: '/zh-CN/components/iconPicker', path: '/zh-CN/components/iconPicker',
}, },
{
id: 30,
title: '树形组件',
subTitle: 'tree',
path: '/zh-CN/components/tree',
},
] ]
const selected = ref(1) const selected = ref(1)

View File

@ -148,7 +148,11 @@ const zhCN = [
path: '/zh-CN/components/iconPicker', path: '/zh-CN/components/iconPicker',
component: () => import('../../docs/zh-CN/components/iconPicker.md'), component: () => import('../../docs/zh-CN/components/iconPicker.md'),
meta: { title: '图标选择' }, meta: { title: '图标选择' },
} }, {
path: '/zh-CN/components/tree',
component: () => import('../../docs/zh-CN/components/tree.md'),
meta: { title: '树形组件' }
},
], ],
}, },
] ]

View File

@ -1,7 +1,7 @@
import type { App } from 'vue' import type { App } from 'vue'
import type { IDefineComponent, InstallOptions } from './module/type/index' import type { IDefineComponent, InstallOptions } from './module/type/index'
import "./css/layui.css" import './css/layui.css'
import LayAvatar from './module/avatar/index' import LayAvatar from './module/avatar/index'
import LayRadio from './module/radio/index' import LayRadio from './module/radio/index'
import LayButton from './module/button/index' import LayButton from './module/button/index'
@ -10,27 +10,27 @@ import LayButtonGroup from './module/buttonGroup/index'
import LayIcon from './module/icon/index' import LayIcon from './module/icon/index'
import LayIconPicker from './module/iconPicker/index' import LayIconPicker from './module/iconPicker/index'
import LayCard from './module/card/index' import LayCard from './module/card/index'
import LayLayout from "./module/layout/index" import LayLayout from './module/layout/index'
import LaySide from "./module/side/index" import LaySide from './module/side/index'
import LayBody from "./module/body/index" import LayBody from './module/body/index'
import LayHeader from "./module/header/index" import LayHeader from './module/header/index'
import LayFooter from "./module/footer/index" import LayFooter from './module/footer/index'
import LayLogo from "./module/logo/index" import LayLogo from './module/logo/index'
import LayPanel from "./module/panel/index" import LayPanel from './module/panel/index'
import LayProgress from "./module/progress/index" import LayProgress from './module/progress/index'
import LayCol from "./module/col/index" import LayCol from './module/col/index'
import LayRow from "./module/row/index" import LayRow from './module/row/index'
import LayInput from "./module/input/index" import LayInput from './module/input/index'
import LayBadge from "./module/badge/index" import LayBadge from './module/badge/index'
import LayBlock from "./module/block/index" import LayBlock from './module/block/index'
import LayLine from "./module/line/index" import LayLine from './module/line/index'
import LayTimeline from "./module/timeline/index" import LayTimeline from './module/timeline/index'
import LayTimelineItem from "./module/timelineItem/index" import LayTimelineItem from './module/timelineItem/index'
import LayTextarea from "./module/textarea/index" import LayTextarea from './module/textarea/index'
import LaySwitch from "./module/switch/index" import LaySwitch from './module/switch/index'
import LayCollapse from "./module/collapse/index" import LayCollapse from './module/collapse/index'
import LayCollapseItem from "./module/collapseItem/index" import LayCollapseItem from './module/collapseItem/index'
import LayContainer from "./module/container/index" import LayContainer from './module/container/index'
import LayMenu from './module/menu/index' import LayMenu from './module/menu/index'
import LayMenuItem from './module/menuItem/index' import LayMenuItem from './module/menuItem/index'
import LayMenuChildItem from './module/menuChildItem/index' import LayMenuChildItem from './module/menuChildItem/index'
@ -49,6 +49,7 @@ import LayDropdown from './module/dropdown/index'
import LayDropdownItem from './module/dropdownItem/index' import LayDropdownItem from './module/dropdownItem/index'
import LayTab from './module/tab/index' import LayTab from './module/tab/index'
import LayTabItem from './module/tabItem/index' import LayTabItem from './module/tabItem/index'
import LayTree from './module/tree/index'
const components: Record<string, IDefineComponent> = { const components: Record<string, IDefineComponent> = {
LayRadio, LayRadio,
@ -97,11 +98,12 @@ const components: Record<string, IDefineComponent> = {
LayDropdownItem, LayDropdownItem,
LayTab, LayTab,
LayTabItem, LayTabItem,
LayIconPicker LayIconPicker,
LayTree
} }
const install = (app: App, options?: InstallOptions): void => { const install = (app: App, options?: InstallOptions): void => {
const _options = options; const _options = options
app.config.globalProperties.$PROOPTIONS = _options app.config.globalProperties.$PROOPTIONS = _options
for (const key in components) { for (const key in components) {
@ -110,7 +112,7 @@ const install = (app: App, options?: InstallOptions): void => {
} }
} }
console.log("document Site : http://layui-vue.pearadmin.com") console.log('document Site : http://layui-vue.pearadmin.com')
export { export {
LayRadio, LayRadio,
@ -160,6 +162,7 @@ export {
LayTab, LayTab,
LayTabItem, LayTabItem,
LayIconPicker, LayIconPicker,
LayTree,
install, install,
} }

9
src/module/tree/index.ts Normal file
View File

@ -0,0 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
Component.install = (app: App) => {
app.component(Component.name || 'LayTree', Component)
}
export default Component as IDefineComponent

287
src/module/tree/index.vue Normal file
View File

@ -0,0 +1,287 @@
<script setup lang="ts">
import { VNode, VNodeChild } from 'vue'
interface TreeData {
/**
* 节点唯一索引值用于对指定节点进行各类操作
*/
id: string | number
/**
* 节点标题
*/
title: string | (() => string)
/**
* 节点字段名
*/
field: string | (() => string)
/**
* 子节点支持设定选项同父节点
*/
children: TreeData[]
/**
* 点击节点弹出新窗口对应的 url需开启 isJump 参数
*/
href: string | URL
/**
* 节点是否初始展开默认 false
*/
spread: boolean
/**
* 节点是否初始为选中状态如果开启复选框的话默认 false
*/
checked: boolean
/**
* 节点是否为禁用状态默认 false
*/
disabled: boolean
}
type EditAction = 'add' | 'update' | 'del'
type EditType = boolean | EditAction[]
interface TreeProps {
/**
* 数据源
*/
data?: TreeData
/**
* 是否显示复选框 默认 false
*/
showCheckbox?: boolean
/**
* 是否开启节点的操作图标默认 false
若为 true则默认显示改删图标
若为 数组则可自由配置操作图标的显示状态和顺序目前支持的操作图标有addupdatedel
edit: ['add', 'update', 'del']
*/
edit?: EditType
/**
* 是否开启手风琴模式默认 false
*/
accordion?: boolean
/**
* 是否仅允许节点左侧图标控制展开收缩默认 false即点击节点本身也可控制若为 true则只能通过节点左侧图标来展开收缩
*/
onlyIconControl?: boolean
/**
* 是否允许点击节点时弹出新窗口跳转默认 false若开启需在节点数据中设定 link 参数值为 url 格式
*/
isJump?: boolean
/**
* 是否开启连接线默认 true若设为 false则节点左侧出现三角图标
*/
showLine?: boolean
/**
* 自定义各类默认文本目前支持以下设定
*/
text?: {
/**
* 节点默认名称
*/
defaultNodeName?: () => string | string
/**
* 数据为空时的提示文本
*/
none?: () => string | string | VNode | Element
}
}
interface TreeNode {
/**
* 当前点击的节点数据
*/
data: TreeData
/**
* 节点的展开状态
*/
state: 'open' | 'close' | 'normal'
/**
* 当前节点元素
*/
elem: Element | VNode | VNodeChild
}
interface TreeEmits {
/**
* 节点被点击后触发
* @param e 事件
* @param treeNode
*/
(e: 'on-click', treeNode: TreeNode): void
/**
* 点击复选框时触发
* @param e 事件
* @param treeNode
*/
(e: 'on-check', treeNode: TreeNode): void
/**
* 操作节点的回调
* @param e 事件
* @param treeNode
*/
(e: 'on-operate', treeNode: TreeNode): void
}
const props = withDefaults(defineProps<TreeProps>(), {
showCheckbox: false,
accordion: false,
onlyIconControl: false,
isJump: false,
showLine: true,
edit: true,
})
console.log(props)
</script>
<script lang="ts">
export default {
name: 'LayTree',
}
</script>
<template>
<div id="test1" class="demo-tree demo-tree-box">
<div class="layui-tree layui-tree-line" lay-filter="LAY-tree-2">
<div
data-id="1"
class="layui-tree-set layui-tree-setHide layui-tree-spread"
>
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick layui-tree-icon"
><i class="layui-icon layui-icon-subtraction"></i></span
><span class="layui-tree-txt">江西</span>
</div>
</div>
<div
class="layui-tree-pack layui-tree-lineExtend layui-tree-showLine"
style="display: block"
>
<div data-id="1000" class="layui-tree-set layui-tree-spread">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick layui-tree-icon"
><i class="layui-icon layui-icon-subtraction"></i></span
><span class="layui-tree-txt">南昌</span>
</div>
</div>
<div
class="layui-tree-pack layui-tree-lineExtend layui-tree-showLine"
style="display: block"
>
<div data-id="10001" class="layui-tree-set">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">青山湖区</span>
</div>
</div>
</div>
<div
data-id="10002"
class="layui-tree-set layui-tree-setLineShort"
>
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">高新区</span>
</div>
</div>
</div>
</div>
</div>
<div data-id="1001" class="layui-tree-set">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">九江</span>
</div>
</div>
</div>
<div data-id="1002" class="layui-tree-set">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">赣州</span>
</div>
</div>
</div>
</div>
</div>
<div
data-id="2"
class="layui-tree-set layui-tree-setHide layui-tree-spread"
>
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick layui-tree-icon"
><i class="layui-icon layui-icon-subtraction"></i></span
><span class="layui-tree-txt">广西</span>
</div>
</div>
<div
class="layui-tree-pack layui-tree-lineExtend layui-tree-showLine"
style="display: block"
>
<div data-id="2000" class="layui-tree-set">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">南宁</span>
</div>
</div>
</div>
<div data-id="2001" class="layui-tree-set">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">桂林</span>
</div>
</div>
</div>
</div>
</div>
<div
data-id="3"
class="layui-tree-set layui-tree-setHide layui-tree-setLineShort"
>
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick layui-tree-icon"
><i class="layui-icon layui-icon-addition"></i></span
><span class="layui-tree-txt">陕西</span>
</div>
</div>
<div class="layui-tree-pack layui-tree-lineExtend">
<div data-id="3000" class="layui-tree-set">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">西安</span>
</div>
</div>
</div>
<div data-id="3001" class="layui-tree-set layui-tree-setLineShort">
<div class="layui-tree-entry">
<div class="layui-tree-main">
<span class="layui-tree-iconClick"
><i class="layui-icon layui-icon-file"></i></span
><span class="layui-tree-txt">延安</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped></style>

14
src/module/tree/props.ts Normal file
View File

@ -0,0 +1,14 @@
import { TreeEmits, TreeProps } from '/@src/module/tree/type'
export const treeProps = withDefaults(defineProps<TreeProps>(), {
showCheckbox: false,
accordion: false,
onlyIconControl: false,
isJump: false,
showLine: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
edit: true
})
export const emit = defineEmits<TreeEmits>()

140
src/module/tree/type.ts Normal file
View File

@ -0,0 +1,140 @@
/**
* defineProps
* index.vue内部单独实现一份
*/
import { VNode, VNodeChild } from 'vue'
export interface TreeData {
/**
*
*/
id: () => (string | number) | string | number
/**
*
*/
title: (() => string) | string
/**
*
*/
field: (() => string) | string
/**
*
*/
children: TreeData[]
/**
* url isJump
*/
href: string | URL
/**
* false
*/
spread: boolean
/**
* false
*/
checked: boolean
/**
* false
*/
disabled: boolean
}
type EditAction = 'add' | 'update' | 'del'
type EditType = boolean | EditAction[]
export interface TreeProps {
/**
*
*/
data?: TreeData
/**
* false
*/
showCheckbox?: boolean
/**
* false
true
addupdatedel
edit: ['add', 'update', 'del']
*/
edit?: EditType
/**
* false
*/
accordion?: boolean
/**
* false true
*/
onlyIconControl?: boolean
/**
* false link url
*/
isJump?: boolean
/**
* 线 true false
*/
showLine?: boolean
/**
*
*/
text?: {
/**
*
*/
defaultNodeName?: () => string | string
/**
*
*/
none?: () => string | string | VNode | Element
}
}
export interface TreeNode {
/**
*
*/
data: TreeData
/**
*
*/
state: 'open' | 'close' | 'normal'
/**
*
*/
elem: Element | VNode | VNodeChild
}
export interface TreeEmits {
/**
*
* @param e
* @param treeNode
*/
(e: 'on-click', treeNode: TreeNode): void
/**
*
* @param e
* @param treeNode
*/
(e: 'on-check', treeNode: TreeNode): void
/**
*
* @param e
* @param treeNode
*/
(e: 'on-operate', treeNode: TreeNode): void
}
export interface TreeExpose {
/**
*
* v-model就够使了
* eg: treeRef.getChecked()
*/
getChecked: () => TreeData
}
// 设置节点勾选 setChecked 变为v-model控制
// 实例重载 reload 变为v-model控制