|
@@ -0,0 +1,225 @@
|
|
|
|
+<template>
|
|
|
|
+ <Dropdown
|
|
|
|
+ placement="bottom"
|
|
|
|
+ v-if="current.length"
|
|
|
|
+ v-model:open="visible"
|
|
|
|
+ :trigger="['click']"
|
|
|
|
+ >
|
|
|
|
+ <div class="fsd">
|
|
|
|
+ <ui-input
|
|
|
|
+ type="text"
|
|
|
|
+ readonly
|
|
|
|
+ :modelValue="[current[current.length - 1]] .map((i: any) => i.title).join('/')"
|
|
|
|
+ width="100%"
|
|
|
|
+ >
|
|
|
|
+ <template #icon>
|
|
|
|
+ <DownOutlined />
|
|
|
|
+ </template>
|
|
|
|
+ </ui-input>
|
|
|
|
+ </div>
|
|
|
|
+ <!-- <DownOutlined /> -->
|
|
|
|
+ <template #overlay>
|
|
|
|
+ <div class="flat" @click.stop="visible = true">
|
|
|
|
+ <div>
|
|
|
|
+ <span
|
|
|
|
+ v-for="item in items"
|
|
|
|
+ @click.stop="
|
|
|
|
+ () => {
|
|
|
|
+ handleClick(item), !item.children && (visible = false);
|
|
|
|
+ }
|
|
|
|
+ "
|
|
|
|
+ :class="{ active: current?.includes(item) }"
|
|
|
|
+ >
|
|
|
|
+ {{ item.title }}
|
|
|
|
+ <DownOutlined v-if="item.children?.length" class="jdd" />
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+ <div>
|
|
|
|
+ <template v-if="current[0].children">
|
|
|
|
+ <span
|
|
|
|
+ v-for="item in current[0].children"
|
|
|
|
+ @click.stop="
|
|
|
|
+ () => {
|
|
|
|
+ handleClick(item), (visible = false);
|
|
|
|
+ }
|
|
|
|
+ "
|
|
|
|
+ :class="{ active: current?.includes(item) }"
|
|
|
|
+ >
|
|
|
|
+ {{ item.title }}
|
|
|
|
+ </span>
|
|
|
|
+ </template>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ </Dropdown>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
+import { styleTypes } from "@/api";
|
|
|
|
+import { computed, ref, watchEffect } from "vue";
|
|
|
|
+import { Menu, Dropdown } from "ant-design-vue";
|
|
|
|
+import { DownOutlined } from "@ant-design/icons-vue";
|
|
|
|
+import { taggings, getTaggingStyle } from "@/store";
|
|
|
|
+
|
|
|
|
+const props = defineProps<{ value: number; all?: boolean; count?: boolean }>();
|
|
|
|
+const emit = defineEmits<{ (e: "update:value", v: number): void }>();
|
|
|
|
+const allType = { name: "全部", id: -1 };
|
|
|
|
+const getTypeCount = (item: any) => {
|
|
|
|
+ if (item.id === allType.id) {
|
|
|
|
+ return taggings.value.length;
|
|
|
|
+ } else if (item.children) {
|
|
|
|
+ return item.children.reduceRight((c: number, item: any) => {
|
|
|
|
+ return (
|
|
|
|
+ c +
|
|
|
|
+ taggings.value.filter((tag) => {
|
|
|
|
+ return getTaggingStyle(tag.styleId)?.typeId === item.id;
|
|
|
|
+ }).length
|
|
|
|
+ );
|
|
|
|
+ }, 0);
|
|
|
|
+ } else {
|
|
|
|
+ return taggings.value.filter((tag) => {
|
|
|
|
+ return getTaggingStyle(tag.styleId)?.typeId === item.id;
|
|
|
|
+ }).length;
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+const open = ref(false);
|
|
|
|
+
|
|
|
|
+const getItems = (types = styleTypes): any => {
|
|
|
|
+ return types
|
|
|
|
+ .map((item) => {
|
|
|
|
+ let count = 0;
|
|
|
|
+ if (props.count) {
|
|
|
|
+ count = getTypeCount(item);
|
|
|
|
+ }
|
|
|
|
+ return {
|
|
|
|
+ label: item.name + (props.count ? ` (${count}) ` : ""),
|
|
|
|
+ title: item.name,
|
|
|
|
+ count,
|
|
|
|
+ key: item.id,
|
|
|
|
+ children: "children" in item ? getItems(item.children) : null,
|
|
|
|
+ };
|
|
|
|
+ })
|
|
|
|
+ .filter((item) => !props.count || item.count || item.key === allType.id);
|
|
|
|
+};
|
|
|
|
+const getCurrentItem = (type: number, all = items.value): any => {
|
|
|
|
+ for (const item of all) {
|
|
|
|
+ if (type === item.key) {
|
|
|
|
+ return [item];
|
|
|
|
+ } else if ("children" in item && item.children) {
|
|
|
|
+ const citem = getCurrentItem(type, item.children);
|
|
|
|
+ if (citem.length) {
|
|
|
|
+ return [item, ...citem];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return [];
|
|
|
|
+};
|
|
|
|
+const types = computed(() => {
|
|
|
|
+ if (props.all) {
|
|
|
|
+ return [allType, ...styleTypes];
|
|
|
|
+ } else {
|
|
|
|
+ return styleTypes;
|
|
|
|
+ }
|
|
|
|
+});
|
|
|
|
+const visible = ref(false);
|
|
|
|
+const items = computed(() => getItems(types.value));
|
|
|
|
+const current = computed(() => getCurrentItem(props.value));
|
|
|
|
+
|
|
|
|
+if (props.count && props.all) {
|
|
|
|
+ watchEffect(() => {
|
|
|
|
+ if (
|
|
|
|
+ current.value.length === 0 ||
|
|
|
|
+ current.value[current.value.length - 1].count === 0
|
|
|
|
+ ) {
|
|
|
|
+ emit("update:value", allType.id);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+const handleClick = (info: any) => {
|
|
|
|
+ emit("update:value", info.key);
|
|
|
|
+};
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
+span {
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+
|
|
|
|
+ .count {
|
|
|
|
+ color: var(--color-main-normal);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.current {
|
|
|
|
+ width: 100%;
|
|
|
|
+ display: flex;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.flat {
|
|
|
|
+ width: 370px;
|
|
|
|
+ height: 588px;
|
|
|
|
+
|
|
|
|
+ background: rgba(27, 27, 28, 0.8);
|
|
|
|
+ box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ display: flex;
|
|
|
|
+
|
|
|
|
+ > div {
|
|
|
|
+ height: 100%;
|
|
|
|
+ overflow-y: auto;
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+
|
|
|
|
+ &:first-child {
|
|
|
|
+ width: 120px;
|
|
|
|
+ border-right: 1px solid #4a4a4a;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &:last-child {
|
|
|
|
+ flex: 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ span {
|
|
|
|
+ padding: 12px 20px;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ color: #ffffff;
|
|
|
|
+ position: relative;
|
|
|
|
+
|
|
|
|
+ .jdd {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 50%;
|
|
|
|
+ right: 5px;
|
|
|
|
+ color: currentColor;
|
|
|
|
+ transform: translateY(-50%) rotate(-90deg);
|
|
|
|
+ padding: 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &.active {
|
|
|
|
+ color: #00c8af;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.fsd {
|
|
|
|
+ position: relative;
|
|
|
|
+ width: 100%;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ &::after {
|
|
|
|
+ content: "";
|
|
|
|
+ position: absolute;
|
|
|
|
+ inset: 0;
|
|
|
|
+ z-index: 999;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|
|
|
|
+
|
|
|
|
+<style>
|
|
|
|
+.ant-dropdown-menu-sub.ant-dropdown-menu-vertical {
|
|
|
|
+ margin-left: -5px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.ant-dropdown-menu {
|
|
|
|
+ border-radius: 2px !important;
|
|
|
|
+}
|
|
|
|
+</style>
|