From 8adf612d5b771a479a99e3ca204ab086adf88f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E8=BF=9C=E6=88=90?= Date: Mon, 8 Nov 2021 20:48:37 +0800 Subject: [PATCH 1/2] init --- .../devui/accordion/src/accordion-list.tsx | 33 ++++-- .../devui/accordion/src/accordion-menu.tsx | 55 ++++++++- .../devui/accordion/src/accordion-types.ts | 63 ++++++++++ .../devui/accordion/src/accordion.scss | 40 +++++-- .../devui/accordion/src/accordion.tsx | 61 +--------- .../devui/dropdown/src/dropdown.scss | 2 +- .../docs/components/accordion/index.md | 108 +++++++++++++++++- 7 files changed, 280 insertions(+), 82 deletions(-) create mode 100644 packages/devui-vue/devui/accordion/src/accordion-types.ts diff --git a/packages/devui-vue/devui/accordion/src/accordion-list.tsx b/packages/devui-vue/devui/accordion/src/accordion-list.tsx index 362cae3e..0213622b 100644 --- a/packages/devui-vue/devui/accordion/src/accordion-list.tsx +++ b/packages/devui-vue/devui/accordion/src/accordion-list.tsx @@ -1,11 +1,16 @@ -import { defineComponent } from 'vue' -import { AccordionMenuItem } from './accordion.type' +import { defineComponent, toRefs } from 'vue' +import type { AccordionMenuItem } from './accordion.type' +import DAccordionMenu from './accordion-menu' +import { accordionProps } from './accordion-types' export default defineComponent({ name: 'DAccordionList', inheritAttrs: false, + components: { + DAccordionMenu + }, props: { - data: { + data: { type: Array as () => Array, default: null }, @@ -13,28 +18,32 @@ export default defineComponent({ type: Number, default: 0 }, - parent: { + parent: { type: Object as () => AccordionMenuItem, default: null }, innerListTemplate: Boolean, + ...accordionProps, }, setup(props, ctx) { + console.log('list', props) + const { + childrenKey + } = toRefs(props) return () => { return (!props.innerListTemplate || props.deepth === 0) && } } diff --git a/packages/devui-vue/devui/accordion/src/accordion-menu.tsx b/packages/devui-vue/devui/accordion/src/accordion-menu.tsx index c5280a3b..e79c63d0 100644 --- a/packages/devui-vue/devui/accordion/src/accordion-menu.tsx +++ b/packages/devui-vue/devui/accordion/src/accordion-menu.tsx @@ -1,13 +1,58 @@ -import { defineComponent } from 'vue' +import { computed, defineComponent, toRefs } from 'vue' +import { AccordionMenuItem } from './accordion.type' +import DAccordionList from './accordion-list' +import { accordionProps } from './accordion-types' export default defineComponent({ name: 'DAccordionMenu', props: { - + item: Object as () => AccordionMenuItem, + deepth: { + type: Number, + default: 0 + }, + parent: { + type: Object as () => AccordionMenuItem, + default: null + }, + ...accordionProps }, - setup() { - return () => { - return
  • d-accordion-menu
  • + setup(props) { + const { item, deepth } = toRefs(props) + + + const menuItemClasses = computed(() => { + return (keyOpen === undefined && props.autoOpenActiveMenu) + ? childActived + : keyOpen + }) + + const keyOpen = computed(() => { + return item?.value[props.openKey]; + }) + const childActived = computed(() => { + // return props.routerLinkActived || props.hasActiveChildren + }) + + return () => { + return ( + <> +
    + {item.value.title} +
    + + + + ) } } }) \ No newline at end of file diff --git a/packages/devui-vue/devui/accordion/src/accordion-types.ts b/packages/devui-vue/devui/accordion/src/accordion-types.ts new file mode 100644 index 00000000..b64c0445 --- /dev/null +++ b/packages/devui-vue/devui/accordion/src/accordion-types.ts @@ -0,0 +1,63 @@ +import { ExtractPropTypes } from "vue"; +import { AccordionMenuType } from "./accordion.type"; + +export const accordionProps = { + data: { + type: Array as () => Array | AccordionMenuType, + default: null, + }, + /* Key值定义, 用于自定义数据结构 */ + titleKey: { type: String, default: "title" }, // 标题的key,item[titleKey]类型为string,为标题显示内容 + loadingKey: { type: String, default: "loading" }, // 子菜单动态加载item[loadingKey]类型为boolean + childrenKey: { type: String, default: "children" }, // 子菜单Key + disabledKey: { type: String, default: "disabled" }, // 是否禁用Key + activeKey: { type: String, default: "active" }, // 菜单是否激活/选中 + openKey: { type: String, default: "open" }, // 菜单是否打开 + + /* 菜单模板 */ + menuItemTemplate: { type: String, default: "" }, // 可展开菜单内容条模板 + itemTemplate: { type: String, default: "" }, // 可点击菜单内容条模板 + + menuToggle: { + type: Function as unknown as () => (event: MouseEvent) => void, + default: null, + }, // 可展开菜单展开事件 + itemClick: { + type: Function as unknown as () => (event: MouseEvent) => void, + default: null, + }, // 可点击菜单点击事件 + activeItemChange: { + type: Function as unknown as () => (event: MouseEvent) => void, + default: null, + }, + + /** 高级选项和模板 */ + restrictOneOpen: { type: Boolean, default: false }, // 限制一级菜单同时只能打开一个 + autoOpenActiveMenu: { type: Boolean, default: false }, // 自动展开活跃菜单 + showNoContent: { type: Boolean, default: true }, // 没有内容的时候是否显示没有数据 + noContentTemplate: { type: String, default: "" }, // 没有内容的时候使用自定义模板 + loadingTemplate: { type: String, default: "" }, // 加载中使用自定义模板 + innerListTemplate: { type: String, default: "" }, // 可折叠菜单内容完全自定义,用做折叠面板 + + /* 内置路由/链接/动态判断路由或链接类型 */ + linkType: { + type: String as () => + | "routerLink" + | "hrefLink" + | "dependOnLinkTypeKey" + | "" + | string, + default: "", + }, + linkTypeKey: { type: String, default: "linkType" }, // linkType为'dependOnLinkTypeKey'时指定对象linkType定义区 + linkKey: { type: String, default: "link" }, // 链接内容的key + linkTargetKey: { type: String, default: "target" }, // 链接目标窗口的key + linkDefaultTarget: { type: String, default: "_self" }, // 不设置target的时候target默认值 + + accordionType: { + type: String as () => "normal" | "embed", + default: "normal", + }, +} as const; + +export type AccordionProps = ExtractPropTypes; diff --git a/packages/devui-vue/devui/accordion/src/accordion.scss b/packages/devui-vue/devui/accordion/src/accordion.scss index a6f4317d..6aa628b2 100644 --- a/packages/devui-vue/devui/accordion/src/accordion.scss +++ b/packages/devui-vue/devui/accordion/src/accordion.scss @@ -3,6 +3,7 @@ @import '../../style/theme/font'; @import '../../style/theme/shadow'; @import '../../style/theme/corner'; +@import '../../style/core/animation'; :host { display: block; @@ -188,17 +189,40 @@ d-accordion-item-routerlink { } } + .devui-accordion-splitter::before { + content: ''; + display: block; + width: 2px; + height: 18px; + background: $devui-form-control-line-active; + position: absolute; + top: 11px; + left: 0; + opacity: 0; + } + &.devui-router-active, &.active { &:not(.open) .devui-accordion-splitter::before { - content: ''; - display: block; - width: 2px; - height: 18px; - background: $devui-form-control-line-active; - position: absolute; - top: 11px; - left: 0; + opacity: 1; + } + } +} + +.devui-accordion-show-animate .devui-accordion-item-title { + transition: + font-weight $devui-animation-duration-fast $devui-animation-ease-in-out-smooth, + background-color $devui-animation-duration-fast $devui-animation-ease-in-out-smooth; + + .devui-accordion-splitter::before { + transform: scaleY(0); + transition: transform $devui-animation-duration-slow $devui-animation-ease-in-out-smooth; + } + + &.devui-router-active, + &.active { + &:not(.open) .devui-accordion-splitter::before { + transform: scaleY(1); } } } diff --git a/packages/devui-vue/devui/accordion/src/accordion.tsx b/packages/devui-vue/devui/accordion/src/accordion.tsx index 9935f71d..d46165a0 100644 --- a/packages/devui-vue/devui/accordion/src/accordion.tsx +++ b/packages/devui-vue/devui/accordion/src/accordion.tsx @@ -1,65 +1,16 @@ -import { defineComponent, reactive } from 'vue' +import { defineComponent, toRefs } from 'vue' import AccordionList from './accordion-list' -import { AccordionMenuType } from './accordion.type' +import { accordionProps } from './accordion-types' import './accordion.scss' export default defineComponent({ name: 'DAccordion', - props: { - data: { - type: Array as () => Array | AccordionMenuType, - default: null - }, - /* Key值定义, 用于自定义数据结构 */ - titleKey: { type : String, default: 'title' }, // 标题的key,item[titleKey]类型为string,为标题显示内容 - loadingKey: { type : String, default: 'loading' }, // 子菜单动态加载item[loadingKey]类型为boolean - childrenKey: { type : String, default: 'children' }, // 子菜单Key - disabledKey: { type : String, default: 'disabled' }, // 是否禁用Key - activeKey: { type : String, default: 'active' }, // 菜单是否激活/选中 - openKey: { type : String, default: 'open' }, // 菜单是否打开 - - /* 菜单模板 */ - menuItemTemplate: { type: String, default: '' }, // 可展开菜单内容条模板 - itemTemplate: { type: String, default: '' }, // 可点击菜单内容条模板 - - menuToggle: { - type: Function as unknown as () => ((event: MouseEvent) => void), - default: null - }, // 可展开菜单展开事件 - itemClick: { - type: Function as unknown as () => ((event: MouseEvent) => void), - default: null - }, // 可点击菜单点击事件 - activeItemChange: { - type: Function as unknown as () => ((event: MouseEvent) => void), - default: null - }, - - /** 高级选项和模板 */ - restrictOneOpen: { type: Boolean, default: false }, // 限制一级菜单同时只能打开一个 - autoOpenActiveMenu: { type: Boolean, default: false }, // 自动展开活跃菜单 - showNoContent: { type: Boolean, default: true }, // 没有内容的时候是否显示没有数据 - noContentTemplate: { type: String, default: '' }, // 没有内容的时候使用自定义模板 - loadingTemplate: { type: String, default: '' }, // 加载中使用自定义模板 - innerListTemplate: { type: String, default: '' }, // 可折叠菜单内容完全自定义,用做折叠面板 - - /* 内置路由/链接/动态判断路由或链接类型 */ - linkType: { - type: String as () => 'routerLink' | 'hrefLink' | 'dependOnLinkTypeKey' | '' | string, - default: '' - }, - linkTypeKey: { type: String, default: 'linkType' }, // linkType为'dependOnLinkTypeKey'时指定对象linkType定义区 - linkKey: { type: String, default: 'link' }, // 链接内容的key - linkTargetKey: { type: String, default: 'target' }, // 链接目标窗口的key - linkDefaultTarget: { type: String, default: '_self' }, // 不设置target的时候target默认值 - - accordionType: { type: String as () => 'normal' | 'embed', default: 'normal' }, - }, + props: accordionProps, setup(props) { - const { data, accordionType } = reactive(props) - + const { data, accordionType } = toRefs(props) + console.log(111, toRefs(props)) return () => { - return
    + return
    + + +
    + Only one level-1 menu can be expanded. +
    +
    Embedded menu (no shadow)
    + + + +``` \ No newline at end of file -- Gitee From e2947575b91b79d05eae82c7f365e78ce27618db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E8=BF=9C=E6=88=90?= Date: Wed, 10 Nov 2021 08:22:25 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0innerListTemplate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devui/accordion/src/accordion-list.tsx | 84 +++++++++------ .../devui/accordion/src/accordion.tsx | 100 +++++++++++++++++- .../docs/components/accordion/index.md | 9 +- 3 files changed, 150 insertions(+), 43 deletions(-) diff --git a/packages/devui-vue/devui/accordion/src/accordion-list.tsx b/packages/devui-vue/devui/accordion/src/accordion-list.tsx index 0213622b..3fba7abb 100644 --- a/packages/devui-vue/devui/accordion/src/accordion-list.tsx +++ b/packages/devui-vue/devui/accordion/src/accordion-list.tsx @@ -3,6 +3,7 @@ import type { AccordionMenuItem } from './accordion.type' import DAccordionMenu from './accordion-menu' import { accordionProps } from './accordion-types' + export default defineComponent({ name: 'DAccordionList', inheritAttrs: false, @@ -25,44 +26,59 @@ export default defineComponent({ innerListTemplate: Boolean, ...accordionProps, }, - setup(props, ctx) { - console.log('list', props) + setup(props, {attrs, slots}) { const { - childrenKey + childrenKey, + innerListTemplate, + deepth } = toRefs(props) return () => { - return (!props.innerListTemplate || props.deepth === 0) &&
      - {props.data.map(item => { - return
    • - {/* // TODO 菜单类型 d-accordion-menu */} - {childrenKey !== undefined && } - {/*
      -
      { item.title }
      - { - // TODO 子菜单 d-accordion-list + return ( + <> + { + !innerListTemplate.value && +
        + {props.data.map(item => { + return
      • + {/* // TODO 菜单类型 d-accordion-menu */} + {childrenKey !== undefined && } + {/*
        +
        { item.title }
        + { + // TODO 子菜单 d-accordion-list + } +
        +
          + { item.children?.map(component => { + return
        • + { + // TODO 路由链接 d-accordion-item-routerlink + } +
          + +
          + { component.title } + { component.done && 已完成 } +
          +
          +
        • + })} +
        +
        +
        */} +
      • } -
        -
          - { item.children?.map(component => { - return
        • - { - // TODO 路由链接 d-accordion-item-routerlink - } -
          - -
          - { component.title } - { component.done && 已完成 } -
          -
          -
        • - })} -
        -
        -
      */} -
    • - })} -
    + )} + + } + { + innerListTemplate.value && deepth.value !== 0 && +
    + {slots.default ? slots.innerListTemplate() : ''} +
    + } + + ) } } }) \ No newline at end of file diff --git a/packages/devui-vue/devui/accordion/src/accordion.tsx b/packages/devui-vue/devui/accordion/src/accordion.tsx index d46165a0..f3cb838a 100644 --- a/packages/devui-vue/devui/accordion/src/accordion.tsx +++ b/packages/devui-vue/devui/accordion/src/accordion.tsx @@ -1,14 +1,104 @@ -import { defineComponent, toRefs } from 'vue' +import { defineComponent, onBeforeUpdate, onMounted, ref, SetupContext, toRefs, watch } from 'vue' import AccordionList from './accordion-list' -import { accordionProps } from './accordion-types' +import { accordionProps, AccordionProps } from './accordion-types' +import { AccordionItemClickEvent, AccordionMenuItem, AccordionMenuToggleEvent } from './accordion.type' import './accordion.scss' export default defineComponent({ name: 'DAccordion', props: accordionProps, - setup(props) { - const { data, accordionType } = toRefs(props) - console.log(111, toRefs(props)) + setup(props: AccordionProps, { emit }) { + const { data, childrenKey, activeKey, openKey ,accordionType, autoOpenActiveMenu , restrictOneOpen} = toRefs(props) + + let clickActiveItem: AccordionMenuItem | undefined = undefined //记录用户点击的激活菜单项 + + const flatten = (arr: Array, childrenKey = 'children', includeParent = false, includeLeaf = true) => { + return arr.reduce((acc, cur) => { + const children = cur[childrenKey]; + if (children === undefined) { + if (includeLeaf) { + acc.push(cur); + } + } else { + if (includeParent) { + acc.push(cur); + } + if (Array.isArray(children)) { + acc.push(...flatten(children, childrenKey, includeParent)); + } + } + return acc; + }, []); + } + + const initActiveItem = () => { + const activeItem = flatten(data.value, childrenKey.value) + .filter(item => item[activeKey.value]).pop(); + if (activeItem) { + if (!clickActiveItem) { + activeItemFn(activeItem); + } + } else { + clickActiveItem = undefined; + } + } + + // 激活子菜单项并去掉其他子菜单的激活 + const activeItemFn = (item) => { + if (clickActiveItem && clickActiveItem[activeKey.value]) { + clickActiveItem[activeKey.value] = false; + } + item[activeKey.value] = true; + clickActiveItem = item; + emit('activeItemChange', clickActiveItem) + } + // 打开或关闭一级菜单,如果有限制只能展开一项则关闭其他一级菜单 + const openMenuFn = (item, open) => { + if (open && restrictOneOpen.value) { + data.value.forEach(itemtemp => { itemtemp[openKey.value] = false; }); + } + item[openKey.value] = open; + } + + // 点击了可点击菜单 + const itemClickFn = (itemEvent: AccordionItemClickEvent) => { + const prevActiveItem = clickActiveItem; + activeItemFn(itemEvent.item); + emit('itemClick', {...itemEvent, prevActiveItem: prevActiveItem}); + } + + const linkItemClickFn = (itemEvent: AccordionItemClickEvent) => { + const prevActiveItem = clickActiveItem; + clickActiveItem = itemEvent.item; + emit('itemClick', {...itemEvent, prevActiveItem: prevActiveItem}); + } + + // 打开或关闭可折叠菜单 + const menuToggleFn = (menuEvent: AccordionMenuToggleEvent) => { + openMenuFn(menuEvent.item, menuEvent.open); + emit('menuToggle', menuEvent); + } + + const cleanOpenData = () => { + flatten(data.value, childrenKey.value, true, false).forEach( + item => item[openKey.value] = undefined + ) + } + + + onMounted(() => { + if (data.value) { + initActiveItem(); + } + }) + + watch(() => autoOpenActiveMenu.value, (current, preV) => { + console.log('cur, new', current, preV) + if (current && preV === false) { + cleanOpenData(); + } + }) + return () => { return
    Only one level-1 menu can be expanded.
    -
    Embedded menu (no shadow)
    +
    Embedded menu (no shadow)