Browse Source

tmp: 暂存
1. 右键菜单制作

kindring 2 weeks ago
parent
commit
0aeabdd409

+ 78 - 0
src/components/music/common/playListInfo.vue

@@ -8,6 +8,8 @@ import {api_fetchMusic, api_likeMusic} from "@/apis/musicControl.ts";
 import {ErrorCode} from "@/types/apiTypes.ts";
 import {secondToTimeStr} from "../../../util/time.ts";
 import HideText from "@/components/public/hideText.vue";
+import {KuiDialogCmd} from "@/components/public/kui-dialog-cmd.ts";
+import addPlayList from "@/components/music/dialog/addPlayList.vue";
 
 defineComponent({name: "play-list-info"});
 
@@ -15,6 +17,10 @@ const props = defineProps({
   playList: {
     type: Object as PropType<PlayList>,
     default: () => ({})
+  },
+  windowId: {
+    type: String,
+    default: ""
   }
 })
 
@@ -28,6 +34,10 @@ const search_page = ref(1);
 
 const page_limit = 10;
 
+const emits =  defineEmits<{
+  (e: 'reload'): void
+}>()
+
 async function loadPlayListMusic(playList: PlayList, page: number, key: string){
   if (lock_loading.value)
   {
@@ -122,6 +132,35 @@ async function loadMore()
   await loadPlayListMusic(props.playList, search_page.value, search_key.value);
 }
 
+
+const playlist_dialog = new KuiDialogCmd({
+  showContent: addPlayList,
+  mountTarget: props.windowId,
+  className: 'dialog',
+  on: { },
+  onClose: closeDialogHandle
+});
+
+function closeDialogHandle()
+{
+  console.log("close dialog");
+  emits('reload');
+}
+
+function editBtnClickHandle()
+{
+  // message.info("edit btn click");
+  playlist_dialog.show({
+    playlist: props.playList
+  })
+}
+
+function deleteBtnClickHandle()
+{
+  message.info("delete btn click");
+
+}
+
 </script>
 
 <template>
@@ -135,6 +174,18 @@ async function loadMore()
         <div class="desc">{{playList.description}}</div>
         <div class="time">创建时间: {{playList.createTime}}</div>
       </div>
+      <div
+          v-if="!playList.isLike"
+          class="edit-btn"
+          @click="editBtnClickHandle">
+        <icon-svg icon-name="edit"/>
+      </div>
+      <div v-if="!playList.isLike"
+           class="delete-btn"
+           @click="deleteBtnClickHandle"
+      >
+        <icon-svg icon-name="remove"/>
+      </div>
     </div>
     <div class="music-lists">
       <div class="music-list-head">
@@ -221,5 +272,32 @@ async function loadMore()
   margin-top: 10px;
   color: #999;
 }
+.edit-btn {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  background-color: var(--color-background);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+}
+.delete-btn {
+  position: absolute;
+  top: 10px;
+  right: 50px;
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  background-color: var(--color-background);
+  color: var(--color-text-money);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+}
 
 </style>

+ 3 - 3
src/components/music/dialog/addPlayList.vue

@@ -86,7 +86,7 @@ async function submitHandle() {
     message.error('歌单名称不能为空');
     return;
   }
-  if (isEdit)
+  if (isEdit.value)
   {
     // 编辑歌单
     param.id = props.playlist.id;
@@ -94,7 +94,7 @@ async function submitHandle() {
     if (res.code === ErrorCode.success)
     {
       message.success('编辑成功');
-      emits('submit');
+      emits('close');
     }
     else {
       message.error(res.msg);
@@ -106,7 +106,7 @@ async function submitHandle() {
     if (res.code === ErrorCode.success)
     {
       message.success('创建成功');
-      emits('submit');
+      emits('close');
     }
     else {
       message.error(res.msg);

+ 4 - 0
src/components/music/dialog/playlist_menu.ts

@@ -0,0 +1,4 @@
+import KuiDialogCmd from "@/components/public/kui-dialog-cmd.ts"
+
+const music_menu: null | KuiDialogCmd = null;
+

+ 28 - 4
src/components/music/musicIndex.vue

@@ -9,7 +9,7 @@ import {fetchPlayList, fetchScanConfig, musicAppStart} from "@/apis/musicControl
 import {ErrorCode} from "@/types/apiTypes.ts";
 import {Tab, TabGroup, TabList, TabPanel, TabPanels} from "@headlessui/vue";
 import ScanListInfo from "@/components/music/common/scanListInfo.vue";
-import {KuiDialogCmd} from "@/components/public/kui-dialog-cmd.ts";
+import {KuiDialogCmd, showContextMenu} from "@/components/public/kui-dialog-cmd.ts";
 import addPlayList from "@/components/music/dialog/addPlayList.vue";
 
 defineComponent({
@@ -129,7 +129,7 @@ function changeScanList(index: number)
 }
 
 
-const kuiDialog = new KuiDialogCmd({
+const playlist_dialog = new KuiDialogCmd({
   showContent: addPlayList,
   mountTarget: props.windowId,
   className: 'dialog',
@@ -137,6 +137,19 @@ const kuiDialog = new KuiDialogCmd({
   onClose: closeDialogHandle
 });
 
+function handleRightClick(e: MouseEvent, item: PlayList) {
+  console.log(`右键菜单 ${e.clientX}, ${e.clientY}`)
+  // e.preventDefault()
+
+  showContextMenu({
+    position: { x: e.clientX, y: e.clientY },
+    menuItems: [
+      { label: '复制', action: () => console.log(item) },
+      { label: '粘贴', action: () => console.log('粘贴') }
+    ]
+  })
+}
+
 function closeDialogHandle()
 {
   loadPlayList();
@@ -146,7 +159,13 @@ function closeDialogHandle()
 function addBtnClickHandle()
 {
   message.info("add btn click");
-  kuiDialog.show()
+  playlist_dialog.show()
+}
+
+function reloadPlayList()
+{
+  loadPlayList();
+  // message.success("刷新成功");
 }
 </script>
 
@@ -188,6 +207,7 @@ function addBtnClickHandle()
                     :key="item.id"
                     :class="`list-item ${i == selectPlaylistIndex?'select-item':''}` "
                     @click="changePlayList(i)"
+                    @contextmenu="e=>handleRightClick(e, item)"
                 >
                   <div class="icon">
                     <IconSvg :icon-name="item.icon" />
@@ -220,7 +240,11 @@ function addBtnClickHandle()
         </div>
       </div>
       <div class="play-list-info">
-        <play-list-info v-if="musicViewShow === showPlayList" :play-list="playList[selectPlaylistIndex]"/>
+        <play-list-info
+            v-if="musicViewShow === showPlayList"
+            :play-list="playList[selectPlaylistIndex]"
+            @reload="reloadPlayList"
+        />
         <scan-list-info v-if="musicViewShow === showScanList" :scan-setting="scanSettings[selectScanIndex]"/>
         <music-setting v-if="musicViewShow === showSetting"
                        :window-id="windowId"/>

+ 206 - 0
src/components/public/kui-context-menu.vue

@@ -0,0 +1,206 @@
+<script setup lang="ts">
+import {computed, ref, nextTick, CSSProperties, onUnmounted} from 'vue'
+import {KuiMenuItem, KuiPosition} from "@/components/public/kui-dialog-type.ts";
+
+// import KuiContextMenu from "./kui-context-menu.vue"
+defineOptions({
+  name: 'KuiContextMenu'
+})
+
+const props = defineProps<{
+  position: KuiPosition
+  menuItems: KuiMenuItem[]
+  parentElement?: HTMLElement
+}>()
+
+const emit = defineEmits<{
+  (e: 'close'): void
+}>()
+
+const menuRef = ref<HTMLElement>()
+const subMenuRefs = ref(new Map<number, HTMLElement>())
+const activeSubMenu = ref<number | null>(null)
+
+// 样式计算
+const menuStyle = computed<CSSProperties>(() => {
+  const style: CSSProperties = {
+    left: `${props.position.x}px`,
+    top: `${props.position.y}px`,
+    visibility: 'visible'
+  }
+
+  nextTick(() => {
+    if (!menuRef.value)
+    {
+      console.log('menuRef is null !!!!!!!!!! ')
+      return style
+    }
+
+    const rect = menuRef.value.getBoundingClientRect()
+    const viewportWidth = window.innerWidth
+    const viewportHeight = window.innerHeight
+
+    // 水平调整
+    if (rect.right > viewportWidth) {
+      style.left = `${viewportWidth - rect.width - 8}px`
+    } else if (rect.left < 0) {
+      style.left = '8px'
+    }
+
+    // 垂直调整
+    if (rect.bottom > viewportHeight) {
+      if (rect.height > viewportHeight) {
+        style.maxHeight = `${viewportHeight - 20}px`
+        style.top = '10px'
+      } else {
+        style.top = `${viewportHeight - rect.height - 8}px`
+      }
+    } else if (rect.top < 0) {
+      style.top = '8px'
+    }
+
+    style.visibility = 'visible'
+  })
+
+  return style
+})
+
+// 子菜单样式计算
+const getSubMenuStyle = (index: number): CSSProperties => {
+  const parentRect = subMenuRefs.value.get(index)?.getBoundingClientRect()
+  if (!parentRect) return { visibility: 'hidden' }
+
+  return {
+    left: `${parentRect.right + 4}px`,
+    top: `${parentRect.top}px`,
+    maxHeight: `${window.innerHeight - parentRect.top - 10}px`
+  }
+}
+
+const closeMenu = () => {
+  emit('close')
+}
+
+const handleItemClick = (item: KuiMenuItem) => {
+  if (item.disabled) return
+  item.action?.()
+  closeMenu()
+}
+
+const handleSubMenuToggle = (index: number, show: boolean) => {
+  activeSubMenu.value = show ? index : null
+}
+
+// 点击外部关闭
+const clickOutsideHandler = (e: MouseEvent) => {
+  if (!menuRef.value?.contains(e.target as Node)) {
+    closeMenu()
+  }
+}
+
+// 键盘事件
+const keydownHandler = (e: KeyboardEvent) => {
+  if (e.key === 'Escape') closeMenu()
+}
+
+// 事件监听
+nextTick(()=>{
+  window.addEventListener('click', clickOutsideHandler, { passive: true, capture: true})
+  window.addEventListener('contextmenu', clickOutsideHandler, { passive: true, capture: true })
+  window.addEventListener('keydown', keydownHandler, { passive: true, capture: true })
+})
+
+
+// 清理
+onUnmounted(() => {
+  window.removeEventListener('click', clickOutsideHandler)
+  window.removeEventListener('contextmenu', clickOutsideHandler)
+  window.removeEventListener('keydown', keydownHandler)
+})
+
+</script>
+
+<template>
+  <div
+      ref="menuRef"
+      class="context-menu"
+      :style="menuStyle"
+  >
+    <div
+        v-for="(item, index) in menuItems"
+        :key="item.label"
+        ref="el => subMenuRefs.set(index, el as HTMLElement)"
+        class="menu-item"
+        :class="{ disabled: item.disabled, 'has-children': item.children }"
+        @mouseenter="handleSubMenuToggle(index, true)"
+        @mouseleave="handleSubMenuToggle(index, false)"
+        @click.stop="handleItemClick(item)"
+    >
+      <span class="menu-item-content">
+        <i v-if="item.icon" :class="item.icon" />
+        {{ item.label }}
+      </span>
+      <i v-if="item.children" class="arrow" />
+
+<!--      <KuiContextMenu-->
+<!--          v-if="item.children && activeSubMenu === index"-->
+<!--          :menu-items="item.children"-->
+<!--          :position="getSubMenuStyle(index)"-->
+<!--          class="sub-menu"-->
+<!--      />-->
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.context-menu {
+  position: fixed;
+  background: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.1);
+  min-width: 160px;
+  z-index: 9999;
+  overflow-y: auto;
+}
+
+.menu-item {
+  padding: 8px 16px;
+  cursor: pointer;
+  position: relative;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.menu-item:hover {
+  background: #f5f5f5;
+}
+
+.menu-item.disabled {
+  color: #999;
+  cursor: not-allowed;
+}
+
+.arrow {
+  border: solid #666;
+  border-width: 0 1px 1px 0;
+  display: inline-block;
+  padding: 3px;
+  transform: rotate(-45deg);
+  margin-left: 10px;
+}
+
+.sub-menu {
+  position: absolute;
+  left: 100%;
+  top: 0;
+  margin-left: 2px;
+}
+
+.menu-item-content {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+</style>

+ 40 - 18
src/components/public/kui-dialog-cmd.ts

@@ -1,23 +1,8 @@
-import {Component, h, render} from "vue";
+import { h, render} from "vue";
 import alertModel from "./alertModel.vue"
+import KuiContextMenu from "./kui-context-menu.vue"
+import {KuiDialogCmdOptions, KuiDialogType} from "./kui-dialog-type.ts";
 
-export enum KuiDialogType {
-    alert,
-    confirm,
-    prompt,
-    loading,
-    custom
-}
-export interface KuiDialogCmdOptions {
-    dialogType?: KuiDialogType;
-    showContent: string | Component;
-    mountTarget?: string;
-    className?: string;
-    onClose?: () => void;
-    beforeClose?: () => boolean;
-    onOk?: () => void;
-    on?: Record<string, (...args: any[]) => void>;
-}
 export class KuiDialogCmd {
     static defaultOptions: KuiDialogCmdOptions = {
         dialogType: KuiDialogType.custom,
@@ -51,6 +36,15 @@ export class KuiDialogCmd {
 
     show( props?: any ) {
         let eventListeners;
+
+        if (this.options.contextMenu) {
+            props = {
+                ...props,
+                position: this.options.position,
+                menuItems: this.options.menuItems
+            }
+        }
+
         if (this.options.dialogType === KuiDialogType.alert) {
             eventListeners = {
                 onOk: () => {
@@ -80,12 +74,17 @@ export class KuiDialogCmd {
             ...eventListeners,
             ...this.options.on
         });
+        console.log(this.options.showContent);
         render(vNode, this.containerEl);
         let target = document.getElementById(<string>this.options.mountTarget);
         if (!target) {
             target = document.body;
         }
+        console.log("show")
+        console.log(this.containerEl)
+        console.log(target)
         target.appendChild(this.containerEl);
+        console.log(target)
         this.targetEl = target;
         this.showFlag = true;
     }
@@ -156,3 +155,26 @@ export function showAlert( alertOptions: KuiDialogAlertOptions, mountTarget: str
     });
     return dialog;
 }
+
+export function showContextMenu(
+    options: {
+        position: { x: number; y: number }
+        menuItems: Array<{ label: string; action: () => void }>
+    },
+    mountTarget: string = "kui-root"
+): KuiDialogCmd
+{
+    const dialogOptions: KuiDialogCmdOptions = {
+        dialogType: KuiDialogType.custom,
+        showContent: KuiContextMenu,
+        mountTarget,
+        contextMenu: true,
+        position: options.position,
+        menuItems: options.menuItems,
+        className: "context-menu-wrapper"
+    }
+
+    const dialog = new KuiDialogCmd(dialogOptions)
+    dialog.show()
+    return dialog
+}

+ 51 - 0
src/components/public/kui-dialog-type.ts

@@ -0,0 +1,51 @@
+import {Component} from "vue";
+
+export enum KuiDialogType {
+    alert,
+    confirm,
+    prompt,
+    loading,
+    custom
+}
+export interface KuiDialogCmdOptions {
+    dialogType?: KuiDialogType;
+    showContent: string | Component;
+    mountTarget?: string;
+    className?: string;
+    onClose?: () => void;
+    beforeClose?: () => boolean;
+    onOk?: () => void;
+    on?: Record<string, (...args: any[]) => void>;
+    menuItems?: KuiMenuItem[];
+    position?: KuiPosition;
+    contextMenu?: boolean;
+}
+
+export interface KuiDialogContextMenu {
+    menuItems: Array<{
+        label: string;
+        value: string;
+    }>;
+    position: {
+        x: number;
+        y: number;
+    };
+}
+
+export interface KuiMenuItem {
+    label: string
+    action?: () => void
+    children?: KuiMenuItem[]
+    disabled?: boolean
+    icon?: string
+}
+
+export interface KuiPosition {
+    x: number
+    y: number
+}
+
+export interface MenuStyle extends KuiPosition {
+    visibility: 'visible' | 'hidden'
+    maxHeight?: string
+}

+ 3 - 0
src/main/control/magnet/music.ts

@@ -288,6 +288,9 @@ export async function c_playList_add(requestData: RequestData<Partial<PlayList>>
     let addParam = requestData.data as Partial<PlayList>;
     // 新添加的歌单都不为默认歌单
     addParam.isLike = false;
+    addParam.createTime = new Date().getTime();
+    addParam.playCount = 0;
+    addParam.isSync = false;
     let [err, res] = await addPlayList(addParam);
     if (err) {
         return t_gen_res(requestData, ErrorCode.db, '添加歌单失败', false)