From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/mp/menu/components/MenuPreviewer.vue | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 226 insertions(+), 0 deletions(-)
diff --git a/src/views/mp/menu/components/MenuPreviewer.vue b/src/views/mp/menu/components/MenuPreviewer.vue
new file mode 100644
index 0000000..77b024a
--- /dev/null
+++ b/src/views/mp/menu/components/MenuPreviewer.vue
@@ -0,0 +1,226 @@
+<template>
+ <draggable
+ v-model="menuList"
+ item-key="id"
+ ghost-class="draggable-ghost"
+ :animation="400"
+ @end="onParentDragEnd"
+ >
+ <template #item="{ element: parent, index: x }">
+ <div class="menu_bottom">
+ <!-- 涓�绾ц彍鍗� -->
+ <div
+ @click="menuClicked(parent, x)"
+ class="menu_item"
+ :class="{ active: props.activeIndex === `${x}` }"
+ >
+ <Icon icon="ep:fold" color="black" />{{ parent.name }}
+ </div>
+ <!-- 浠ヤ笅涓轰簩绾ц彍鍗�-->
+ <div class="submenu" v-if="props.parentIndex === x && parent.children">
+ <draggable
+ v-model="parent.children"
+ item-key="id"
+ ghost-class="draggable-ghost"
+ :animation="400"
+ @end="onChildDragEnd"
+ >
+ <template #item="{ element: child, index: y }">
+ <div class="menu_bottom subtitle">
+ <div
+ class="menu_subItem"
+ v-if="parent.children"
+ :class="{ active: props.activeIndex === `${x}-${y}` }"
+ @click="subMenuClicked(child, x, y)"
+ >
+ {{ child.name }}
+ </div>
+ </div>
+ </template>
+ </draggable>
+ <!-- 浜岀骇鑿滃崟鍔犲彿锛� 褰撻暱搴� 灏忎簬 5 鎵嶆樉绀轰簩绾ц彍鍗曠殑鍔犲彿 -->
+ <div
+ class="menu_bottom menu_addicon"
+ v-if="!parent.children || parent.children.length < 5"
+ @click="addSubMenu(x, parent)"
+ >
+ <Icon icon="ep:plus" class="plus" />
+ </div>
+ </div>
+ </div>
+ </template>
+ </draggable>
+
+ <!-- 涓�绾ц彍鍗曞姞鍙� -->
+ <div class="menu_bottom menu_addicon" v-if="menuList.length < 3" @click="addMenu">
+ <Icon icon="ep:plus" class="plus" />
+ </div>
+</template>
+
+<script lang="ts" setup>
+import { Menu } from './types'
+import draggable from 'vuedraggable'
+
+const props = defineProps<{
+ modelValue: Menu[]
+ activeIndex: string
+ parentIndex: number
+ accountId: number
+}>()
+
+const emit = defineEmits<{
+ (e: 'update:modelValue', v: Menu[])
+ (e: 'menu-clicked', parent: Menu, x: number)
+ (e: 'submenu-clicked', child: Menu, x: number, y: number)
+}>()
+
+const menuList = computed<Menu[]>({
+ get: () => props.modelValue,
+ set: (val) => emit('update:modelValue', val)
+})
+
+// 娣诲姞妯悜涓�绾ц彍鍗�
+const addMenu = () => {
+ const index = menuList.value.length
+ const menu = {
+ name: '鑿滃崟鍚嶇О',
+ children: [],
+ reply: {
+ // 鐢ㄤ簬瀛樺偍鍥炲鍐呭
+ type: 'text',
+ accountId: props.accountId // 淇濊瘉缁勪欢閲岋紝鍙互浣跨敤鍒板搴旂殑鍏紬鍙�
+ }
+ }
+ menuList.value[index] = menu
+ menuClicked(menu, index - 1)
+}
+
+// 娣诲姞妯悜浜岀骇鑿滃崟锛沺arent 琛ㄧず瑕佹搷浣滅殑鐖惰彍鍗�
+const addSubMenu = (i: number, parent: any) => {
+ const subMenuKeyLength = parent.children.length // 鑾峰彇浜岀骇鑿滃崟key闀垮害
+ const addButton = {
+ name: '瀛愯彍鍗曞悕绉�',
+ reply: {
+ // 鐢ㄤ簬瀛樺偍鍥炲鍐呭
+ type: 'text',
+ accountId: props.accountId // 淇濊瘉缁勪欢閲岋紝鍙互浣跨敤鍒板搴旂殑鍏紬鍙�
+ }
+ }
+ parent.children[subMenuKeyLength] = addButton
+ subMenuClicked(parent.children[subMenuKeyLength], i, subMenuKeyLength)
+}
+
+const menuClicked = (parent: Menu, x: number) => {
+ emit('menu-clicked', parent, x)
+}
+
+const subMenuClicked = (child: Menu, x: number, y: number) => {
+ emit('submenu-clicked', child, x, y)
+}
+
+/**
+ * 澶勭悊涓�绾ц彍鍗曞睍寮�鍚庤鎷栧姩锛屾縺娲�(灞曞紑)鍘熸潵娲诲姩鐨勪竴绾ц彍鍗�
+ *
+ * @param oldIndex: 涓�绾ц彍鍗曟嫋鍔ㄥ墠鐨勪綅缃�
+ * @param newIndex: 涓�绾ц彍鍗曟嫋鍔ㄥ悗鐨勪綅缃�
+ */
+const onParentDragEnd = ({ oldIndex, newIndex }) => {
+ // 浜岀骇鑿滃崟娌℃湁灞曞紑锛岀洿鎺ヨ繑鍥�
+ if (props.activeIndex === '__MENU_NOT_SELECTED__') {
+ return
+ }
+
+ // 浣跨敤涓�涓緟鍔╂暟缁勬潵妯℃嫙鑿滃崟绉诲姩锛岀劧鍚庢壘鍒板睍寮�鐨勪簩绾ц彍鍗曠殑鏂颁笅鏍嘸newParent`
+ const positions = new Array<boolean>(menuList.value.length).fill(false)
+ positions[props.parentIndex] = true
+ const [out] = positions.splice(oldIndex, 1) // 绉诲嚭鑿滃崟锛屼繚瀛樺埌鍙橀噺out
+ positions.splice(newIndex, 0, out) // 鎶妎ut鍙橀噺鎻掑叆琚Щ鍑虹殑鑿滃崟
+ const newParentIndex = positions.indexOf(true)
+
+ // 鎵惧埌鑿滃崟鍏冪礌锛岃Е鍙戜竴绾ц彍鍗曠偣鍑�
+ const parent = menuList.value[newParentIndex]
+ emit('menu-clicked', parent, newParentIndex)
+}
+
+/**
+ * 澶勭悊浜岀骇鑿滃崟灞曞紑鍚庤鎷栧姩锛屾縺娲昏鎷栧姩鐨勮彍鍗�
+ *
+ * @param newIndex 浜岀骇鑿滃崟鎷栧姩鍚庣殑浣嶇疆
+ */
+const onChildDragEnd = ({ newIndex }) => {
+ const x = props.parentIndex
+ const y = newIndex
+ const children = menuList.value[x]?.children
+ if (children && children?.length > 0) {
+ const child = children[y]
+ emit('submenu-clicked', child, x, y)
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.menu_bottom {
+ position: relative;
+ display: block;
+ float: left;
+ width: 85.5px;
+ text-align: center;
+ cursor: pointer;
+ background-color: #fff;
+ border: 1px solid #ebedee;
+ box-sizing: border-box;
+
+ &.menu_addicon {
+ height: 46px;
+ line-height: 46px;
+
+ .plus {
+ color: #2bb673;
+ }
+ }
+
+ .menu_item {
+ display: flex;
+ width: 100%;
+ height: 44px;
+ line-height: 44px;
+ // text-align: center;
+ box-sizing: border-box;
+ align-items: center;
+ justify-content: center;
+
+ &.active {
+ border: 1px solid #2bb673;
+ }
+ }
+
+ .menu_subItem {
+ height: 44px;
+ line-height: 44px;
+ text-align: center;
+ box-sizing: border-box;
+
+ &.active {
+ border: 1px solid #2bb673;
+ }
+ }
+}
+
+/* 绗簩绾ц彍鍗� */
+.submenu {
+ position: absolute;
+ bottom: 45px;
+ width: 85.5px;
+
+ .subtitle {
+ background-color: #fff;
+ box-sizing: border-box;
+ }
+}
+
+.draggable-ghost {
+ background: #f7fafc;
+ border: 1px solid #4299e1;
+ opacity: 0.5;
+}
+</style>
--
Gitblit v1.8.0