123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- <template>
- <RenderButtonGroup />
- </template>
- <script setup lang="tsx" name="ButtonGroup">
- import { ref, watch, computed, nextTick, onMounted, onUnmounted, onDeactivated, onActivated } from "vue";
- import type { BreakPoint } from "@/components/Grid/interface/index";
- import mittBus from "@/utils/mittBus";
- import { MittEnum } from "@/enums/mittEnum";
- import { cloneDeep, pick, get, omit, defaultsDeep } from "lodash-es";
- import { More, ArrowDown, ArrowRight } from "@element-plus/icons-vue";
- import { isArray, isFunction, isObject } from "@/utils/is";
- type Props = {
- /**
- * @description 父组件id值
- */
- target?: any;
- buttons: any;
- assemblySize: string;
- data: any;
- update: any;
- };
- const props = withDefaults(defineProps<Props>(), {
- assemblySize: ""
- });
- /**
- * @description 按钮默认宽度px
- */
- const itemWidth = 80;
- const innerwidth = ref(0);
- const skeletonItemSize = computed(() => {
- let _size = "";
- switch (props.assemblySize) {
- case "large":
- _size = "width: 90px; height: 40px";
- break;
- case "small":
- _size = "width: 64px; height: 24px";
- break;
- default:
- _size = "width: 80px; height: 32px";
- break;
- }
- return _size;
- });
- /**
- * 排除多余属性,仅展示需要的属性继承
- * @param data Object
- */
- const omitAttr = (data: any) => {
- return omit(data, ["label", "power", "clickFunc", "slotName", "render", "disabledTextCallBack"]);
- };
- /**
- * @description 监听框架属性变化
- */
- mittBus.on(MittEnum.FoldLayoutChange, layout => {
- console.log("ittBus.on(MittEnum.FoldLayoutChang :>> ", layout);
- nextTick(() => {
- resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
- });
- });
- // 监听屏幕变化
- const resize = (e: UIEvent) => {
- nextTick(() => {
- let width = (e.target as Window).innerWidth;
- if (props.target) {
- let _target = document.getElementById(props.target) as HTMLElement;
- _target && (width = _target.offsetWidth);
- }
- console.log("resize bg width :>> ", width);
- innerwidth.value = width;
- });
- };
- /**
- * @description 过滤出合法操作的按钮
- * @param data buttongroup [[],{}]
- */
- const getNotLimited = (data: any) => {
- let arr = data.map((item: any) => {
- if (isArray(item)) {
- return item.filter(itm => {
- if (isFunction(itm?.limited)) {
- return !itm?.limited();
- } else {
- return !itm?.limited;
- }
- });
- } else if (isObject(item)) {
- if (isFunction(item?.limited)) {
- return !item?.limited() && item;
- } else {
- return !item?.limited && item;
- }
- }
- });
- return arr.filter((item: any) => item);
- };
- const getIconSlot = (item: any) => {
- // console.log("item , item.icon, typeof item.icon :>> ", JSON.stringify(item), item.icon, typeof item.icon);
- let iconRender = {};
- switch (typeof item.icon) {
- case "function":
- iconRender = { icon: () => item.icon() };
- break;
- case "string":
- iconRender = {
- icon: () => {
- return <i class={["iconfont", item.icon]} />;
- }
- };
- // delete item.icon;
- break;
- default:
- break;
- }
- return iconRender;
- };
- /**
- * @description el-skeleton loading
- */
- const isLoading = computed(() => {
- if (typeof props.data == "undefined") {
- return false;
- } else {
- return Object.keys(props.data).length == 0;
- }
- });
- /**
- * @description 按钮组渲染
- */
- const buttonItemRender = (item: any) => {
- // console.log("buttonItemRender item :>> ", item);
- if (!item) return;
- let iconRender = getIconSlot(item);
- let _item = cloneDeep(item);
- typeof _item.icon == "string" && delete _item.icon;
- // let btnDisabledTxt: any = computed(() => {
- // return props.data && item.disabledTextCallBack && item.disabledTextCallBack(props.data);
- // });
- let btnDisabledTxt = props.data && item.disabledTextCallBack ? item.disabledTextCallBack(props.data) : "";
- let _loading = isFunction(item.loading) ? item.loading() : false;
- // 排除多余属性,仅展示需要的属性继承
- let btnAttr = omitAttr(_item);
- const childrenSlot = {
- template: () => {
- return <el-skeleton-item variant="button" class="mr-4" style={skeletonItemSize} />;
- },
- default: () => {
- if (item.render) {
- return item.render(item);
- } else if (item.hasOwnProperty("disabledTextCallBack")) {
- let disabled = Boolean(btnDisabledTxt);
- return (
- <el-tooltip
- class="box-item"
- disabled={!disabled}
- effect="dark"
- content={btnDisabledTxt}
- placement="top"
- enterable={false}
- hide-after={0}
- >
- <el-button
- {...btnAttr}
- class={{ "is-disabled": disabled }}
- disabled={disabled}
- loading={_loading}
- onClick={item.clickFunc}
- v-slots={iconRender}
- size={props.assemblySize}
- >
- {item.label}
- </el-button>
- </el-tooltip>
- );
- } else {
- return (
- <el-button
- size={props.assemblySize}
- {...btnAttr}
- disabled={_loading}
- loading={_loading}
- onClick={item.clickFunc}
- v-slots={iconRender}
- >
- {item.label}
- </el-button>
- );
- }
- }
- };
- return <el-skeleton animated={true} loading={isLoading.value} class="mr-12" v-slots={childrenSlot}></el-skeleton>;
- };
- /**
- * @description 折叠后,按钮组渲染
- */
- const buttonsMenuRender = (item: any) => {
- if (!item) return;
- // console.log("buttonsMenuRender item :>> ", item);
- let iconRender = getIconSlot(item);
- let _item = cloneDeep(item);
- typeof _item.icon == "string" && delete _item.icon;
- let btnDisabledTxt = props.data && item.disabledTextCallBack ? item.disabledTextCallBack(props.data) : "";
- // console.log("btnDisabledTxt :>> ", btnDisabledTxt.value);
- // 排除多余属性,仅展示需要的属性继承
- let btnAttr = omitAttr(_item);
- let _loading = isFunction(item?.loading) ? item.loading() : false;
- if (item.render) {
- // return item.render(item);
- return <el-dropdown-item>{item.render(item)}</el-dropdown-item>;
- } else if (item.hasOwnProperty("disabledTextCallBack")) {
- let disabled = Boolean(btnDisabledTxt);
- return (
- <el-dropdown-item {...btnAttr}>
- <el-tooltip
- class="box-item"
- disabled={!disabled}
- effect="dark"
- content={btnDisabledTxt}
- placement="right"
- enterable={false}
- hide-after={0}
- >
- <el-text
- text
- {...btnAttr}
- class={{ "is-disabled": disabled, "flx-1": true }}
- disabled={disabled || _loading}
- loading={_loading}
- onClick={item.clickFunc}
- v-slots={iconRender}
- >
- {item.label}
- </el-text>
- </el-tooltip>
- </el-dropdown-item>
- );
- } else {
- return (
- <el-dropdown-item {...btnAttr} disabled={_loading} loading={_loading} onClick={item.clickFunc} class="flx-1">
- {item.label}
- </el-dropdown-item>
- );
- }
- };
- const buttonGroupInnderRender = (item: any, ifMenu: boolean, placement: string) => {
- return (
- <div class="flx-center">
- <span class="flx-1">{item.label}</span>
- {!ifMenu && <el-icon>{placement == "bottom" ? <ArrowDown /> : <ArrowRight />}</el-icon>}
- </div>
- );
- };
- /**
- * @description 按钮组,下拉菜单渲染器
- * @param item
- * @param placement el-dropdown 菜单弹出位置
- */
- const bottonDropdownRender = (item: any, placement?: string) => {
- if (!item) return;
- let _item = cloneDeep(item[0]);
- let iconRender = getIconSlot(item[0]);
- let _placement = placement ?? "bottom";
- /**
- * @description 判断是否为菜单按钮
- */
- let ifMenu = !_item.hasOwnProperty("clickFunc");
- let idx = ifMenu ? 1 : 0; // 若是菜单按钮,下拉菜单跳过第一个
- let _buttonsRd = item.map((itm: any, index: number) => {
- return index >= idx && buttonsMenuRender(itm);
- });
- _buttonsRd = _buttonsRd.filter((itm: any) => itm);
- console.log("_buttonsRd menu :>> ", _buttonsRd);
- let _dropdown = {
- dropdown: () => {
- return (
- <>
- <el-dropdown-menu>{_buttonsRd}</el-dropdown-menu>
- </>
- );
- }
- };
- typeof _item.icon == "string" && delete _item.icon;
- let _btnAttr = omitAttr(_item);
- let btnDisabledTxt = props.data && _item.disabledTextCallBack ? _item.disabledTextCallBack(props.data) : "";
- let disabled = Boolean(btnDisabledTxt);
- let _loading = isFunction(_item.loading) ? _item.loading() : false;
- const childrenSlot = {
- template: () => {
- return <el-skeleton-item variant="button" class="mr-4" style={skeletonItemSize} />;
- },
- default: () => {
- return (
- <el-dropdown v-slots={_dropdown} placement={_placement} popper-class="button-group__menu">
- {_placement == "bottom" ? (
- <span>
- <el-tooltip
- class="box-item"
- disabled={!disabled}
- effect="dark"
- content={btnDisabledTxt}
- placement="top"
- enterable={false}
- hide-after={0}
- >
- <el-button
- {..._btnAttr}
- class={{ "is-disabled": disabled }}
- disabled={disabled || _loading}
- loading={_loading}
- size={props.assemblySize}
- onClick={_item.clickFunc}
- v-slots={iconRender}
- style={{ cursor: ifMenu ? "default" : "pointer" }}
- >
- {buttonGroupInnderRender(_item, ifMenu, _placement)}
- </el-button>
- </el-tooltip>
- </span>
- ) : (
- <span>
- <el-tooltip
- class="box-item"
- disabled={!disabled}
- effect="dark"
- content={btnDisabledTxt}
- placement="top"
- enterable={false}
- hide-after={0}
- >
- <el-text
- {..._btnAttr}
- class={{ "is-disabled": disabled, "menu-more__text": true }}
- disabled={disabled || _loading}
- loading={_loading}
- onClick={_item.clickFunc}
- v-slots={iconRender}
- >
- {buttonGroupInnderRender(_item, ifMenu, _placement)}
- </el-text>
- </el-tooltip>
- </span>
- )}
- </el-dropdown>
- );
- }
- };
- return <el-skeleton animated={true} loading={isLoading.value} v-slots={childrenSlot}></el-skeleton>;
- };
- const RenderButtonGroup = () => {
- let btnListRender: any = [];
- let menuListRender: any = [];
- // console.log("rProp.buttons :>> ", props.buttons);
- let butttons = [];
- if (props.buttons?.length) {
- // console.log("innerwidth.value :>> ", innerwidth.value);
- let keyIndex = Math.floor(innerwidth.value / itemWidth) - 1;
- // butttons = props.buttons.filter((item: any) => !item?.limited);
- butttons = getNotLimited(props.buttons);
- // console.log("keyIndex :>> ", keyIndex, butttons.length, innerwidth.value, butttons);
- btnListRender = butttons.map((item: any, index: number) => {
- console.log("index, keyIndex, item :>> ", index, keyIndex, item);
- if (index + 1 > keyIndex) return;
- if (isArray(item)) {
- if (item.length > 1) {
- return bottonDropdownRender(item);
- } else {
- return buttonItemRender(item[0]);
- }
- } else {
- return buttonItemRender(item);
- }
- });
- btnListRender = btnListRender.filter((item: any) => item);
- // console.log("RenderButtonGroup btnListRender :>> ", btnListRender, butttons);
- let curButtons = butttons?.filter(item => !Array.isArray(item) || item.length > 0);
- if (btnListRender.length < curButtons?.length) {
- let dropdownRender = butttons.map((item: any, index: number) => {
- if (index + 1 <= keyIndex) return;
- if (isArray(item)) {
- let _menuRender = null;
- if (item.length > 1) {
- _menuRender = bottonDropdownRender(item, "right-start");
- } else {
- _menuRender = buttonsMenuRender(item[0]);
- }
- return _menuRender;
- } else {
- return buttonsMenuRender(item);
- }
- });
- dropdownRender = dropdownRender.filter((item: any) => item);
- console.log("dropdownRender :>> ", dropdownRender);
- let _buttonsRdMenu = dropdownRender.map((itm: any) => {
- return (
- <>
- <el-dropdown-item>{itm}</el-dropdown-item>
- </>
- );
- });
- let dropdowmSlot = {
- dropdown: () => {
- return (
- <>
- <el-dropdown-menu>{_buttonsRdMenu}</el-dropdown-menu>
- </>
- );
- }
- };
- let childrenSlot = {
- template: () => {
- return <el-skeleton-item variant="button" style={skeletonItemSize} />;
- },
- default: () => {
- return (
- <>
- <el-dropdown popper-class="button-group__menu-more" v-slots={dropdowmSlot} trigger="click">
- <el-button text circle icon={More}></el-button>
- </el-dropdown>
- </>
- );
- }
- };
- menuListRender.push(<el-skeleton animated loading={isLoading.value} class="mr-12" v-slots={childrenSlot}></el-skeleton>);
- }
- }
- // console.log("btnListRender :>> ", btnListRender);
- // console.log("RenderButtonGroup menuListRender :>> ", menuListRender);
- return (
- <>
- <el-button-group class="detail-menu__btn-group">{btnListRender}</el-button-group>
- {menuListRender.length > 0 && menuListRender}
- </>
- );
- };
- watch(
- () => props.update,
- () => {
- nextTick(() => {
- resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
- });
- }
- );
- onMounted(() => {
- resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
- window.addEventListener("resize", resize);
- });
- onActivated(() => {
- resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
- window.addEventListener("resize", resize);
- });
- onUnmounted(() => {
- window.removeEventListener("resize", resize);
- });
- onDeactivated(() => {
- window.removeEventListener("resize", resize);
- });
- </script>
- <style lang="scss">
- .button-group__menu {
- .el-dropdown-menu__item {
- min-width: 60px;
- }
- }
- .button-group__menu-more {
- .el-dropdown-menu.el-dropdown-menu--default > .el-dropdown-menu__item {
- padding: 0 !important;
- }
- .menu-more__text {
- display: block;
- padding: 5px 16px;
- line-height: $space-a3;
- }
- }
- .detail-menu__btn-group {
- display: inline-flex;
- & > * {
- border: none;
- .el-button {
- border: none;
- }
- }
- & > .el-dropdown *:focus-visible {
- outline: unset;
- }
- }
- </style>
|