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