自定义vue通用左侧菜单组件(未完善版本)

使用到的技术:

vue3、pinia、view-ui-plus

实现的功能:

传入一个菜单数组数据,自动生成一个左侧菜单栏。菜单栏可以添加、删除、展开、重命名,拖动插入位置等。

效果预览:

自定义vue通用左侧菜单组件(未完善版本)_第1张图片自定义vue通用左侧菜单组件(未完善版本)_第2张图片自定义vue通用左侧菜单组件(未完善版本)_第3张图片自定义vue通用左侧菜单组件(未完善版本)_第4张图片

代码:

c-menu-wrap.vue





c-menu.vue





menu.ts

import { defineStore } from 'pinia'
import { ref, nextTick } from 'vue'
import { randomString } from '@/utils/index.js'

export const useMenuStore = defineStore('menu', () => {

	const menuList = ref([
		{
			id: 1,
			val: '标题1',
			type: 'folder'
		},
		{
			id: 2,
			val: '标题1',
			type: 'folder',
			children: [{
				id: 21,
				val: '标题2',
				type: 'folder',
				children: [
					{
						id: 211,
						val: '标题3',
						type: 'file',
						content: '123'
					}, 
					{
						id: 212,
						val: '标题3',
						type: 'file',
						content: '345'
					}
				]
			}]
		}
	])
	const preMenuList = ref([])

	const currentMenuId = ref('')
	const isShowPopper = ref(false)
	const modal = ref(null)
	const modalX = ref(0)
	const modalY = ref(0)

	const editItem = ref(null) // 当前标题编辑对象(文件夹或文件)
	const editInput = ref(null)
	const selectItem = ref(null) // 当前选中的文件对象
	const preSelectItem = ref(null)

	const isEdit = ref(false) // 文档是否开启编辑状态
	const isNew = ref(false) // 是否新建
	const isEditRename = ref(false) // 是否重命名

	const dragItem = ref(null) // 当前拖动元素
	const dropPosition = ref(0) // 拖动元素插入的位置:0 中,1 上,2 下

	// 点击菜单
	function doMenuAction(e:any, item: any) {

		hidePopper()
	
		if(item.type === 'folder') {
			item.showSub = !item.showSub
		} 
		// 选中的文档
		else if(item.type === 'file') {
			if(item.id !== currentMenuId.value) {
				isEdit.value = false
				currentMenuId.value = item.id.toString()
				selectItem.value = JSON.parse(JSON.stringify(item))
				preSelectItem.value = JSON.parse(JSON.stringify(selectItem.value))
			}
		}
	}

	// 显示更多菜单
	function showPopper(param: any) {
		hidePopper()

		isShowPopper.value = true

		let e = param.e
		let item = param.item

		editItem.value = item

		nextTick(() => {
			
			let _w = modal.value?.offsetWidth || 0
			let _h = modal.value?.offsetHeight || 0
			
			modalX.value = e.clientX + _w > window.innerWidth ? window.innerWidth - _w : e.clientX+2
			modalY.value = e.clientY + _h > window.innerHeight ? window.innerHeight - _h : e.clientY+2
			
		})
	}

	// 隐藏更多菜单
	function hidePopper() {
		console.log('隐藏更多菜单')
		

		if(isNew.value || (!isNew.value && isEditRename.value)) {	
			menuList.value = JSON.parse(JSON.stringify(preMenuList.value))
		}

		isNew.value = false
		isEditRename.value = false
		editItem.value = null
		isShowPopper.value = false
	}

	

	// 确定修改文档标题
	function editTitle() {
		// 新建
		if(isNew.value) {
			console.log('确定新建标题', editItem.value)
			if(editItem.value && editItem.value.type === 'file') {
				currentMenuId.value = editItem.value.id
				isEdit.value = true
				selectItem.value = JSON.parse(JSON.stringify(editItem.value))
				preSelectItem.value = JSON.parse(JSON.stringify(selectItem.value ))
			}
		} 
		// 修改
		else {
			console.log('确定修改标题', editItem.value)
			if(editItem.value && editItem.value.type === 'file' && currentMenuId.value === editItem.value.id) {
				
				selectItem.value = JSON.parse(JSON.stringify(editItem.value))
				preSelectItem.value = JSON.parse(JSON.stringify(selectItem.value ))
			}
		}

		isNew.value = false
		isEditRename.value = false
		editItem.value = null
	}

	function callBack(name: string) {
		console.log('callBack: ', name)
	}

	// 开启重命名
	function openRename() {
		preMenuList.value = JSON.parse(JSON.stringify(menuList.value))

		isShowPopper.value = false
		isEditRename.value = true

		nextTick(() => {
			editInput.value && editInput.value[0] && editInput.value[0].focus()
		})
	}

	// 全部展开
	function expandAll(_list: Array = []) {
	
		if(!_list || _list.length <= 0 || !(_list instanceof Array)) {
			_list = menuList.value
		}
		for(let i = 0; i < _list.length; i++) {
			if(_list[i].children && _list[i].children.length > 0) {
				_list[i].showSub = true
				expandAll(_list[i].children)
			}
		}
	}

	// 创建文件夹或文档
	function createNew(type: number) {
		isNew.value = true

		isShowPopper.value = false
		isEditRename.value = true

		preMenuList.value = JSON.parse(JSON.stringify(menuList.value))

		// 文件夹
		if(type === 1) {
			// 新建文件夹
			if(!editItem.value) {
				console.log('新建文件夹')
				editItem.value = JSON.parse(JSON.stringify({
					id: randomString(32),
					val: '',
					type: 'folder',
				}))

				menuList.value.push(editItem.value)
			} 
			// 新建子文件夹
			else {
				console.log('新建子文件夹')
				findParentMenu(menuList.value, type)
			}
		} 
		// 文档
		else if(type === 2) {
			// 新建文档
			if(!editItem.value) {
				console.log('新建文档')
				editItem.value = JSON.parse(JSON.stringify({
					id: randomString(32),
					val: '',
					type: 'file',
				}))

				menuList.value.push(editItem.value)
			} 
			// 新建子文档
			else {
				console.log('新建子文档')
				findParentMenu(menuList.value, type)
			}
		}

		nextTick(() => {
			editInput.value && editInput.value[0] && editInput.value[0].focus()
		})
		
	}

	function findParentMenu(list: Array, type: number) {
		console.log(type)
		
		for(let i = 0; i < list.length; i++) {
			if(list[i].id === editItem.value.id) {
				list[i].showSub = true
				editItem.value = JSON.parse(JSON.stringify({
					id: randomString(32),
					val: '',
					type: type === 1 ? 'folder' : 'file',
				}))

				if(!list[i].children) {
					list[i].children = []
				}

				list[i].children.push(editItem.value)
				break
			} else {
				if(list[i].children && list[i].children.length > 0) {
					findParentMenu(list[i].children, type)
				}
			}
		}
	}

	return {
		editInput,
		isEdit,
		isNew,
		isEditRename,
		isShowPopper,
		modal,
		modalX,
		modalY,
		currentMenuId,
		editItem,
		menuList,
		preMenuList,
		selectItem,
		preSelectItem,
		dragItem,
		dropPosition,

		doMenuAction,
		expandAll,
		createNew,
		openRename,
		editTitle,
		showPopper,
		hidePopper,
		callBack
	}

})

使用

import cMenuWrap from '@/components/menu/c-menu-wrap.vue'

你可能感兴趣的:(Vue,JS插件,vue.js,前端,javascript)