当前位置: 首页>编程语言>正文

一个人撸码!之vue3+vite+element-plus后台管理(标签页组件)

一个后台管理常常需要一个标签页来管理已经打开的页面,这里我们单独写一个组件来展示标签页数组。

一个人撸码!之vue3+vite+element-plus后台管理(标签页组件),第1张

该标签页组件只做展示不涉及操作数据。标签页数组可记录已打开的数组,还能定义什么页面需要缓存,是一个重要的功能呢。

首先,建立一个TagList.vue组件,里面代码如下

```js

<template>

<div

? ? class="tag-list-cp-container"

? ? ref="TagListRef">

? ? <div

? ? ? ? class="left"

? ? ? ? @wheel="handleScroll">

? ? ? ? <el-scrollbar

? ? ? ? ? ? ref="ElScrollbarRef"

? ? ? ? ? ? height="100%">

? ? ? ? ? ? <draggable

? ? ? ? ? ? ? ? class="scrollbar-container"

? ? ? ? ? ? ? ? item-key="sign"

? ? ? ? ? ? ? ? v-model="tagListTrans">

? ? ? ? ? ? ? ? <template #item="{element}">

? ? ? ? ? ? ? ? ? ? <div

? ? ? ? ? ? ? ? ? ? ? ? :class="{

? ? ? ? ? ? ? ? ? ? ? ? ? ? 'item':true,

? ? ? ? ? ? ? ? ? ? ? ? ? ? 'active':dataContainer.activeSign==element.sign,

? ? ? ? ? ? ? ? ? ? ? ? }"

? ? ? ? ? ? ? ? ? ? ? ? @click="handleClick(element)"

? ? ? ? ? ? ? ? ? ? ? ? @contextmenu.prevent="e=>{

? ? ? ? ? ? ? ? ? ? ? ? ? ? handleClickContext(e,element);

? ? ? ? ? ? ? ? ? ? ? ? }">

? ? ? ? ? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? ? ? ? ? class="sign icon-sign"

? ? ? ? ? ? ? ? ? ? ? ? ? ? v-if="element.showTagIcon && element.iconName"

? ? ? ? ? ? ? ? ? ? ? ? ? ? :style="'width: 15px;min-width:15px;height: 15px;'"

? ? ? ? ? ? ? ? ? ? ? ? ? ? :name="element.iconName"></SvgIcon>

? ? ? ? ? ? ? ? ? ? ? ? <div

? ? ? ? ? ? ? ? ? ? ? ? ? ? class="sign"

? ? ? ? ? ? ? ? ? ? ? ? ? ? v-else-if="dataContainer.activeSign==element.sign">

? ? ? ? ? ? ? ? ? ? ? ? </div>

? ? ? ? ? ? ? ? ? ? ? ? {{element.title}}

? ? ? ? ? ? ? ? ? ? ? ? <div

? ? ? ? ? ? ? ? ? ? ? ? ? ? v-if="!element.fixed"

? ? ? ? ? ? ? ? ? ? ? ? ? ? @click.stop="handleRemove(element)"

? ? ? ? ? ? ? ? ? ? ? ? ? ? class="bt">

? ? ? ? ? ? ? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? :style="'width:12px;height:12px;'"

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name="times"></SvgIcon>

? ? ? ? ? ? ? ? ? ? ? ? </div>

? ? ? ? ? ? ? ? ? ? ? ? <div

? ? ? ? ? ? ? ? ? ? ? ? ? ? v-if="element.isCache"

? ? ? ? ? ? ? ? ? ? ? ? ? ? class="cache"></div>

? ? ? ? ? ? ? ? ? ? </div>

? ? ? ? ? ? ? ? </template>

? ? ? ? ? ? </draggable>

? ? ? ? </el-scrollbar>

? ? </div>

? ? <div class="bt-list">

? ? ? ? <div

? ? ? ? ? ? class="bt"

? ? ? ? ? ? @click="handleOptionClick(5)">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:15px;height:15px;'"

? ? ? ? ? ? ? ? name="redo"></SvgIcon>

? ? ? ? </div>

? ? ? ? <div

? ? ? ? ? ? class="bt"

? ? ? ? ? ? @click="handleToLeft()">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:15px;height:15px;'"

? ? ? ? ? ? ? ? name="arrow-left"></SvgIcon>

? ? ? ? </div>

? ? ? ? <div

? ? ? ? ? ? class="bt"

? ? ? ? ? ? @click="handleToRight()">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:15px;height:15px;'"

? ? ? ? ? ? ? ? name="arrow-right"></SvgIcon>

? ? ? ? </div>

? ? </div>

? ? <div

? ? ? ? ref="RightOptionRef"

? ? ? ? class="right">

? ? ? ? <div

? ? ? ? ? ? @click="()=>{

? ? ? ? ? ? ? ? dataContainer.show_1 = !dataContainer.show_1;

? ? ? ? ? ? }"

? ? ? ? ? ? class="bt">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:20px;height:20px;'"

? ? ? ? ? ? ? ? name="icon-drag"></SvgIcon>

? ? ? ? </div>

? ? ? ? <div

? ? ? ? ? ? v-if="dataContainer.show_1"

? ? ? ? ? ? class="bt-list-container">

? ? ? ? ? ? <div

? ? ? ? ? ? ? ? v-if="dataContainer.tagList.length>1"

? ? ? ? ? ? ? ? class="item"

? ? ? ? ? ? ? ? @click="handleOptionClick(1)">

? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#f86464;'"

? ? ? ? ? ? ? ? ? ? name="times"></SvgIcon>

? ? ? ? ? ? ? ? 关闭当前标签页

? ? ? ? ? ? </div>

? ? ? ? ? ? <div

? ? ? ? ? ? ? ? v-if="dataContainer.tagList.length>1"

? ? ? ? ? ? ? ? class="item"

? ? ? ? ? ? ? ? @click="handleOptionClick(2)">

? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#f86464;'"

? ? ? ? ? ? ? ? ? ? name="borderverticle-fill"></SvgIcon>

? ? ? ? ? ? ? ? 关闭其他标签页

? ? ? ? ? ? </div>

? ? ? ? ? ? <div

? ? ? ? ? ? ? ? v-if="dataContainer.tagList.length>1"

? ? ? ? ? ? ? ? class="item"

? ? ? ? ? ? ? ? @click="handleOptionClick(3)">

? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#f86464;'"

? ? ? ? ? ? ? ? ? ? name="arrow-left"></SvgIcon>

? ? ? ? ? ? ? ? 关闭左边标签页

? ? ? ? ? ? </div>

? ? ? ? ? ? <div

? ? ? ? ? ? ? ? v-if="dataContainer.tagList.length>1"

? ? ? ? ? ? ? ? class="item"

? ? ? ? ? ? ? ? @click="handleOptionClick(4)">

? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#f86464;'"

? ? ? ? ? ? ? ? ? ? name="arrow-right"></SvgIcon>

? ? ? ? ? ? ? ? 关闭右边标签页

? ? ? ? ? ? </div>

? ? ? ? ? ? <div

? ? ? ? ? ? ? ? class="item re-bt"

? ? ? ? ? ? ? ? @click="handleOptionClick(5)">

? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#0072E5;'"

? ? ? ? ? ? ? ? ? ? name="redo"></SvgIcon>

? ? ? ? ? ? ? ? 刷新当前标签页

? ? ? ? ? ? </div>

? ? ? ? ? ? <div

? ? ? ? ? ? ? ? class="item"

? ? ? ? ? ? ? ? @click="handleOptionClick(6)">

? ? ? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#0072E5;'"

? ? ? ? ? ? ? ? ? ? name="expand-alt"></SvgIcon>

? ? ? ? ? ? ? ? 视图全屏(Esc键退出)

? ? ? ? ? ? </div>

? ? ? ? </div>

? ? </div>

? ? <div

? ? ? ? v-if="dataContainer.show"

? ? ? ? :style="{

? ? ? ? ? ? '--location-x':`${dataContainer.location.x || 0}px`,

? ? ? ? ? ? '--location-y':`${dataContainer.location.y || 0}px`,

? ? ? ? }"

? ? ? ? class="bt-list-container">

? ? ? ? <div

? ? ? ? ? ? class="item"

? ? ? ? ? ? @click="handleSwitchCache()">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:16px;height:16px;'"

? ? ? ? ? ? ? ? name="switch"></SvgIcon>

? ? ? ? ? ? 切换缓存状态

? ? ? ? </div>

? ? ? ? <div

? ? ? ? ? ? class="item"

? ? ? ? ? ? @click="handleSwitchFixed()">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:16px;height:16px;'"

? ? ? ? ? ? ? ? name="nail"></SvgIcon>

? ? ? ? ? ? 切换固定状态

? ? ? ? </div>

? ? ? ? <div

? ? ? ? ? ? class="item re-bt"

? ? ? ? ? ? @click="handleRefresh()">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#0072E5;'"

? ? ? ? ? ? ? ? name="redo"></SvgIcon>

? ? ? ? ? ? 刷新此标签页

? ? ? ? </div>

? ? ? ? <div

? ? ? ? ? ? class="item"

? ? ? ? ? ? @click="handleOptionClick(6)">

? ? ? ? ? ? <SvgIcon

? ? ? ? ? ? ? ? :style="'width:16px;height:16px;color:#0072E5;'"

? ? ? ? ? ? ? ? name="expand-alt"></SvgIcon>

? ? ? ? ? ? 视图全屏

? ? ? ? </div>

? ? </div>

</div>

</template>

<script>

/*

* 标签切换按钮组件

* 由外部指定数据

*/

import {

? ? defineComponent,ref,reactive,

? ? computed,onMounted,watch,toRef,

? ? onUnmounted,

? ? nextTick,

} from "vue";

import SvgIcon from "@/components/svgIcon/index.vue";

import draggable from 'vuedraggable';

export default {

? ? name: 'TagList',

? ? components: {

? ? ? ? SvgIcon,

? ? ? ? draggable,

? ? },

? ? props:{

? ? ? ? /**

? ? ? ? * 所显示的标签列表

? ? ? ? *? */

? ? ? ? /**

? ? ? ? * 一个tag例子的属性介绍

? ? ? ? */

? ? ? ? // {

? ? ? ? //? ? title:'标签一',? //标签标题

? ? ? ? //? ? sign:'/main/index',? //唯一标识

? ? ? ? //? ? fullPath:'/main/index',? //跳转地址,完整地址

? ? ? ? //? ? isCache:true,? //该标签页面是否缓存

? ? ? ? //? ? fixed:false,? //是否固定,不可删除

? ? ? ? // }

? ? ? ? tagList:{

? ? ? ? ? ? type:Array,

? ? ? ? ? ? default:()=>{

? ? ? ? ? ? ? ? return [];

? ? ? ? ? ? },

? ? ? ? },

? ? ? ? /** 当前活动的唯一标识 */

? ? ? ? activeSign:{

? ? ? ? ? ? type:[Number,String],

? ? ? ? ? ? default:0,

? ? ? ? },

? ? },

? ? emits:[

? ? ? ? 'onChange','onClick','onRemove','onOptionClick','onSwitchCache','onSwitchFixed',

? ? ? ? 'onRefresh',

? ? ],

? ? setup(props,{emit}){

? ? ? ? const ElScrollbarRef = ref(null);

? ? ? ? const TagListRef = ref(null);

? ? ? ? const RightOptionRef = ref(null);

? ? ? ? const dataContainer = reactive({

? ? ? ? ? ? tagList:toRef(props,'tagList'),

? ? ? ? ? ? activeSign:toRef(props,'activeSign'),

? ? ? ? ? ? show:false,

? ? ? ? ? ? location:{},

? ? ? ? ? ? show_1:false,

? ? ? ? });

? ? ? ? const otherDataContainer = {

? ? ? ? ? ? activeItem:null,

? ? ? ? };

? ? ? ? /** 用来排序转换的数组,由外部确定是否转换 */

? ? ? ? const tagListTrans = computed({

? ? ? ? ? ? get(){

? ? ? ? ? ? ? ? return dataContainer.tagList;

? ? ? ? ? ? },

? ? ? ? ? ? set(value){

? ? ? ? ? ? ? ? emit('onChange',value);

? ? ? ? ? ? },

? ? ? ? });

? ? ? ? /** 标签点击事件,向外部抛出 */

? ? ? ? function handleClick(item){

? ? ? ? ? ? emit('onClick',item);

? ? ? ? }

? ? ? ? /** 标签删除事件 */

? ? ? ? function handleRemove(item){

? ? ? ? ? ? emit('onRemove',item);

? ? ? ? }

? ? ? ? /** 操作事件 */

? ? ? ? function handleOptionClick(type){

? ? ? ? ? ? emit('onOptionClick',type);

? ? ? ? }

? ? ? ? /**

? ? ? ? * 鼠标滚动事件

? ? ? ? * 横向滚动标签页

? ? ? ? *? */

? ? ? ? function handleScroll(e){

? ? ? ? ? ? if(!ElScrollbarRef.value) return;

? ? ? ? ? ? /** shift + 鼠标滚轮可以横向滚动 */

? ? ? ? ? ? if(e.shiftKey) return;

? ? ? ? ? ? let el = ElScrollbarRef.value.wrapRef;

? ? ? ? ? ? let scrollLeft = el.scrollLeft;

? ? ? ? ? ? if(e.deltaY < 0){

? ? ? ? ? ? ? ? scrollLeft = scrollLeft - 30;

? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? scrollLeft = scrollLeft + 30;

? ? ? ? ? ? }

? ? ? ? ? ? el.scrollLeft = scrollLeft;

? ? ? ? }

? ? ? ? /**

? ? ? ? * 自动滚动到相应标签

? ? ? ? * 防止标签没在视区

? ? ? ? */

? ? ? ? function autoScroll(){

? ? ? ? ? ? nextTick(()=>{

? ? ? ? ? ? ? ? if(!ElScrollbarRef.value) return;

? ? ? ? ? ? ? ? let el = ElScrollbarRef.value.wrapRef;

? ? ? ? ? ? ? ? let target = el.querySelector('.item.active');

? ? ? ? ? ? ? ? if(!target) return;

? ? ? ? ? ? ? ? let rect = el.getBoundingClientRect();

? ? ? ? ? ? ? ? let rect_1 = target.getBoundingClientRect();

? ? ? ? ? ? ? ? if(rect_1.x < rect.x){

? ? ? ? ? ? ? ? ? ? // 表示在左边遮挡

? ? ? ? ? ? ? ? ? ? let scroll = rect.x - rect_1.x;

? ? ? ? ? ? ? ? ? ? el.scrollLeft = el.scrollLeft - scroll - 5;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? if((rect_1.x + rect_1.width) > (rect.x + rect.width)){

? ? ? ? ? ? ? ? ? ? // 表示在右边遮挡

? ? ? ? ? ? ? ? ? ? let scroll = rect_1.x - (rect.x + rect.width);

? ? ? ? ? ? ? ? ? ? el.scrollLeft = el.scrollLeft + scroll + rect_1.width + 5;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? });

? ? ? ? }

? ? ? ? watch(toRef(props,'activeSign'),()=>{

? ? ? ? ? ? autoScroll();

? ? ? ? });

? ? ? ? onMounted(()=>{

? ? ? ? ? ? autoScroll();

? ? ? ? });

? ? ? ? /** 鼠标右击,展示自定义右击面板 */

? ? ? ? function handleClickContext(e,item){

? ? ? ? ? ? if(!TagListRef.value) return;

? ? ? ? ? ? let el = TagListRef.value;

? ? ? ? ? ? let el_1 = e.target;

? ? ? ? ? ? let rect = el.getBoundingClientRect();

? ? ? ? ? ? let rect_1 = el_1.getBoundingClientRect();

? ? ? ? ? ? let location = {

? ? ? ? ? ? ? ? x:rect_1.x - rect.x,

? ? ? ? ? ? ? ? y:rect_1.y - rect.y + rect_1.height,

? ? ? ? ? ? };

? ? ? ? ? ? dataContainer.location = location;

? ? ? ? ? ? dataContainer.show = true;

? ? ? ? ? ? otherDataContainer.activeItem = item;

? ? ? ? }

? ? ? ? /** 初始化隐藏事件 */

? ? ? ? function initHiddenEvent(){

? ? ? ? ? ? function callbackFn(e){

? ? ? ? ? ? ? ? dataContainer.show = false;

? ? ? ? ? ? }

? ? ? ? ? ? document.addEventListener('click', callbackFn);

? ? ? ? ? ? onUnmounted(()=>{

? ? ? ? ? ? ? ? document.removeEventListener('click', callbackFn);

? ? ? ? ? ? });

? ? ? ? }

? ? ? ? initHiddenEvent();

? ? ? ? /**

? ? ? ? * 切换缓存状态

? ? ? ? * 由外部实现

? ? ? ? *? */

? ? ? ? function handleSwitchCache(){

? ? ? ? ? ? if(!otherDataContainer.activeItem) return;

? ? ? ? ? ? emit('onSwitchCache',otherDataContainer.activeItem);

? ? ? ? }

? ? ? ? /**

? ? ? ? * 切换固定状态

? ? ? ? * 由外部实现

? ? ? ? *? */

? ? ? ? function handleSwitchFixed(){

? ? ? ? ? ? if(!otherDataContainer.activeItem) return;

? ? ? ? ? ? emit('onSwitchFixed',otherDataContainer.activeItem);

? ? ? ? }

? ? ? ? /**

? ? ? ? * 刷新标签页

? ? ? ? * 由外部实现

? ? ? ? *? */

? ? ? ? function handleRefresh(){

? ? ? ? ? ? if(!otherDataContainer.activeItem) return;

? ? ? ? ? ? emit('onRefresh',otherDataContainer.activeItem);

? ? ? ? }

? ? ? ? /** 跳转到右侧 */

? ? ? ? function handleToRight(){

? ? ? ? ? ? let index = dataContainer.tagList.findIndex(item=>{

? ? ? ? ? ? ? ? return item.sign == dataContainer.activeSign;

? ? ? ? ? ? });

? ? ? ? ? ? if(index == -1) return;

? ? ? ? ? ? let target = dataContainer.tagList[index + 1];

? ? ? ? ? ? if(!target) return;

? ? ? ? ? ? handleClick(target);

? ? ? ? }

? ? ? ? /** 跳转到左侧 */

? ? ? ? function handleToLeft(){

? ? ? ? ? ? let index = dataContainer.tagList.findIndex(item=>{

? ? ? ? ? ? ? ? return item.sign == dataContainer.activeSign;

? ? ? ? ? ? });

? ? ? ? ? ? if(index == -1) return;

? ? ? ? ? ? let target = dataContainer.tagList[index - 1];

? ? ? ? ? ? if(!target) return;

? ? ? ? ? ? handleClick(target);

? ? ? ? }

? ? ? ? /** 初始化隐藏事件 */

? ? ? ? function initHiddenEvent_1(){

? ? ? ? ? ? function callbackFn(e){

? ? ? ? ? ? ? ? if(!RightOptionRef.value) return;

? ? ? ? ? ? ? ? if(!e || !e.target) return;

? ? ? ? ? ? ? ? if(RightOptionRef.value.contains(e.target)) return;

? ? ? ? ? ? ? ? dataContainer.show_1 = false;

? ? ? ? ? ? }

? ? ? ? ? ? document.addEventListener('click', callbackFn);

? ? ? ? ? ? onUnmounted(()=>{

? ? ? ? ? ? ? ? document.removeEventListener('click', callbackFn);

? ? ? ? ? ? });

? ? ? ? }

? ? ? ? initHiddenEvent_1();

? ? ? ? return {

? ? ? ? ? ? dataContainer,

? ? ? ? ? ? handleClick,

? ? ? ? ? ? handleRemove,

? ? ? ? ? ? handleOptionClick,

? ? ? ? ? ? tagListTrans,

? ? ? ? ? ? handleScroll,

? ? ? ? ? ? ElScrollbarRef,

? ? ? ? ? ? handleClickContext,

? ? ? ? ? ? TagListRef,

? ? ? ? ? ? handleSwitchCache,

? ? ? ? ? ? handleSwitchFixed,

? ? ? ? ? ? handleRefresh,

? ? ? ? ? ? handleToRight,

? ? ? ? ? ? handleToLeft,

? ? ? ? ? ? RightOptionRef,

? ? ? ? };

? ? },

}

</script>

<style scoped lang="scss">

.tag-list-cp-container {

? ? height: 100%;

? ? width: 100%;

? ? padding: 0;

? ? box-sizing: border-box;

? ? display: flex;

? ? flex-direction: row;

? ? justify-content: space-between;

? ? align-items: center;

? ? color: var(--text-color);

? ? >.left{

? ? ? ? flex: 1 1 0;

? ? ? ? width: 0;

? ? ? ? height: 100%;

? ? ? ? :deep(.el-scrollbar__bar){

? ? ? ? ? ? &.is-horizontal{

? ? ? ? ? ? ? ? height: 5px !important;

? ? ? ? ? ? ? ? opacity: 0.5;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? :deep(.el-scrollbar__view){

? ? ? ? ? ? height: 100%;

? ? ? ? }

? ? ? ? :deep(.scrollbar-container){

? ? ? ? ? ? display: flex;

? ? ? ? ? ? flex-direction: row;

? ? ? ? ? ? justify-content: flex-start;

? ? ? ? ? ? align-items: center;

? ? ? ? ? ? width: fit-content;

? ? ? ? ? ? height: 100%;

? ? ? ? ? ? .item{

? ? ? ? ? ? ? ? cursor: pointer;

? ? ? ? ? ? ? ? display: flex;

? ? ? ? ? ? ? ? flex-direction: row;

? ? ? ? ? ? ? ? justify-content: center;

? ? ? ? ? ? ? ? align-items: center;

? ? ? ? ? ? ? ? padding: 5px 8px;

? ? ? ? ? ? ? ? box-sizing: border-box;

? ? ? ? ? ? ? ? margin-left: 5px;

? ? ? ? ? ? ? ? font-size: 13px;

? ? ? ? ? ? ? ? height: 30px;

? ? ? ? ? ? ? ? width: max-content;

? ? ? ? ? ? ? ? border-radius: 3px;

? ? ? ? ? ? ? ? color: #606266;

? ? ? ? ? ? ? ? position: relative;

? ? ? ? ? ? ? ? transition: all 0.2s;

? ? ? ? ? ? ? ? &:last-child{

? ? ? ? ? ? ? ? ? ? margin-right: 5px;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? &.active{

? ? ? ? ? ? ? ? ? ? background-color: #5240ff30;

? ? ? ? ? ? ? ? ? ? color: #5240ff;

? ? ? ? ? ? ? ? ? ? font-weight: bold;

? ? ? ? ? ? ? ? ? ? box-shadow: inset 0 1px 4px #00000034;

? ? ? ? ? ? ? ? ? ? // border:1px solid rgb(196, 196, 196);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? &:hover{

? ? ? ? ? ? ? ? ? ? background-color: #5240ff30;

? ? ? ? ? ? ? ? ? ? color: #5240ff;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? >.sign{

? ? ? ? ? ? ? ? ? ? width: 10px;

? ? ? ? ? ? ? ? ? ? height: 10px;

? ? ? ? ? ? ? ? ? ? border-radius: 50%;

? ? ? ? ? ? ? ? ? ? background-color: #5240ff;

? ? ? ? ? ? ? ? ? ? margin-right: 5px;

? ? ? ? ? ? ? ? ? ? &.icon-sign{

? ? ? ? ? ? ? ? ? ? ? ? background-color: transparent;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? >.bt{

? ? ? ? ? ? ? ? ? ? width: fit-content;

? ? ? ? ? ? ? ? ? ? height: fit-content;

? ? ? ? ? ? ? ? ? ? display: flex;

? ? ? ? ? ? ? ? ? ? flex-direction: row;

? ? ? ? ? ? ? ? ? ? justify-content: center;

? ? ? ? ? ? ? ? ? ? align-items: center;

? ? ? ? ? ? ? ? ? ? margin-left: 5px;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? >.cache{

? ? ? ? ? ? ? ? ? ? width: 30%;

? ? ? ? ? ? ? ? ? ? max-width: 30px;

? ? ? ? ? ? ? ? ? ? min-width: 15px;

? ? ? ? ? ? ? ? ? ? height: 3px;

? ? ? ? ? ? ? ? ? ? border-radius: 999px;

? ? ? ? ? ? ? ? ? ? background-color: #5340ff34;

? ? ? ? ? ? ? ? ? ? position: absolute;

? ? ? ? ? ? ? ? ? ? bottom: 0;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? >.bt-list{

? ? ? ? display: flex;

? ? ? ? flex-direction: row;

? ? ? ? align-items: center;

? ? ? ? padding: 0 10px;

? ? ? ? box-sizing: border-box;

? ? ? ? border-left: 1px solid var(--border-color);

? ? ? ? box-shadow: inset 0 1px 4px #00000010;

? ? ? ? height: 100%;

? ? ? ? >*{

? ? ? ? ? ? margin: 0 10px 0 0;

? ? ? ? ? ? &:last-child{

? ? ? ? ? ? ? ? margin: 0;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? >.bt{

? ? ? ? ? ? cursor: pointer;

? ? ? ? ? ? transition: all 0.2s;

? ? ? ? ? ? height: 100%;

? ? ? ? ? ? display: flex;

? ? ? ? ? ? flex-direction: row;

? ? ? ? ? ? align-items: center;

? ? ? ? ? ? justify-content: center;

? ? ? ? ? ? &:hover{

? ? ? ? ? ? ? ? color: #5240ff;

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? >.right{

? ? ? ? width: 40px;

? ? ? ? height: 100%;

? ? ? ? border-left: 1px solid var(--border-color);

? ? ? ? box-sizing: border-box;

? ? ? ? display: flex;

? ? ? ? flex-direction: row;

? ? ? ? justify-content: center;

? ? ? ? align-items: center;

? ? ? ? position: relative;

? ? ? ? box-shadow: inset 0 1px 4px #00000010;

? ? ? ? >.bt{

? ? ? ? ? ? width: 100%;

? ? ? ? ? ? height: 100%;

? ? ? ? ? ? display: flex;

? ? ? ? ? ? flex-direction: row;

? ? ? ? ? ? justify-content: center;

? ? ? ? ? ? align-items: center;

? ? ? ? ? ? cursor: pointer;

? ? ? ? ? ? transition: all 0.2s;

? ? ? ? ? ? &:hover{

? ? ? ? ? ? ? ? color: #5240ff;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? >.bt-list-container{

? ? ? ? ? ? width: max-content;

? ? ? ? ? ? min-width: 150px;

? ? ? ? ? ? position: absolute;

? ? ? ? ? ? z-index: 9;

? ? ? ? ? ? top: calc(100% + 0px);

? ? ? ? ? ? right: 5px;

? ? ? ? ? ? background-color: rgb(255, 255, 255);

? ? ? ? ? ? box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.5);

? ? ? ? ? ? padding: 10px 0;

? ? ? ? ? ? box-sizing: border-box;

? ? ? ? ? ? border-radius: 2px;

? ? ? ? ? ? overflow: hidden;

? ? ? ? ? ? transition: opacity 0.2s;

? ? ? ? ? ? font-size: 15px;

? ? ? ? ? ? >.item{

? ? ? ? ? ? ? ? cursor: pointer;

? ? ? ? ? ? ? ? width: auto;

? ? ? ? ? ? ? ? min-width: max-content;

? ? ? ? ? ? ? ? transition: all 0.2s;

? ? ? ? ? ? ? ? padding: 13px 15px;

? ? ? ? ? ? ? ? box-sizing: border-box;

? ? ? ? ? ? ? ? display: block;

? ? ? ? ? ? ? ? color: #6b7386;

? ? ? ? ? ? ? ? text-align: left;

? ? ? ? ? ? ? ? display: flex;

? ? ? ? ? ? ? ? flex-direction: row;

? ? ? ? ? ? ? ? align-items: center;

? ? ? ? ? ? ? ? justify-content: flex-start;

? ? ? ? ? ? ? ? >*{

? ? ? ? ? ? ? ? ? ? margin-right: 10px;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? &:hover{

? ? ? ? ? ? ? ? ? ? box-shadow: inset 0 1px 4px #0000001f;

? ? ? ? ? ? ? ? ? ? background-color: #fef0f0;

? ? ? ? ? ? ? ? ? ? color: #f56c6c;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? &.re-bt{

? ? ? ? ? ? ? ? ? ? background-color: rgba(194, 224, 255, 0.5);

? ? ? ? ? ? ? ? ? ? color: #0072E5;

? ? ? ? ? ? ? ? ? ? &:hover{

? ? ? ? ? ? ? ? ? ? ? ? background-color: rgba(194, 224, 255, 0.5);

? ? ? ? ? ? ? ? ? ? ? ? color: #0072E5;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? >.bt-list-container{

? ? ? ? width: max-content;

? ? ? ? min-width: 150px;

? ? ? ? position: absolute;

? ? ? ? z-index: 9;

? ? ? ? top: var(--location-y);

? ? ? ? left: var(--location-x);

? ? ? ? background-color: rgb(255, 255, 255);

? ? ? ? box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.5);

? ? ? ? padding: 10px 0;

? ? ? ? box-sizing: border-box;

? ? ? ? border-radius: 2px;

? ? ? ? overflow: hidden;

? ? ? ? opacity: 1;

? ? ? ? transition: opacity 0.2s;

? ? ? ? font-size: 15px;

? ? ? ? >.item{

? ? ? ? ? ? cursor: pointer;

? ? ? ? ? ? width: auto;

? ? ? ? ? ? min-width: max-content;

? ? ? ? ? ? transition: all 0.2s;

? ? ? ? ? ? padding: 13px 15px;

? ? ? ? ? ? box-sizing: border-box;

? ? ? ? ? ? display: block;

? ? ? ? ? ? color: #6b7386;

? ? ? ? ? ? text-align: left;

? ? ? ? ? ? display: flex;

? ? ? ? ? ? flex-direction: row;

? ? ? ? ? ? align-items: center;

? ? ? ? ? ? justify-content: flex-start;

? ? ? ? ? ? >*{

? ? ? ? ? ? ? ? margin-right: 10px;

? ? ? ? ? ? }

? ? ? ? ? ? &:hover{

? ? ? ? ? ? ? ? box-shadow: inset 0 1px 4px #0000001f;

? ? ? ? ? ? ? ? background-color: #fef0f0;

? ? ? ? ? ? ? ? color: #f56c6c;

? ? ? ? ? ? }

? ? ? ? ? ? &.re-bt{

? ? ? ? ? ? ? ? background-color: rgba(194, 224, 255, 0.5);

? ? ? ? ? ? ? ? color: #0072E5;

? ? ? ? ? ? ? ? &:hover{

? ? ? ? ? ? ? ? ? ? background-color: rgba(194, 224, 255, 0.5);

? ? ? ? ? ? ? ? ? ? color: #0072E5;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

</style>

```

这里我们使用了el-scrollbar组件来管理滚动容器,SvgIcon来管理icon的展示,vuedraggable来管理拖拽排序。

该组件接受的数据源为 tagList,activeSign。

tagList:标签的数组。

activeSign:当前活动的标签的sign字符串,每个标签是一个对象,对象有sign唯一标识属性。

组件核心思想:该组件使用外部数据源保证组件灵活性,自身集合多种操作但不处理,抛出给外部处理。只做数据的展示。

[源码地址](https://github.com/wurencaideli/dumogu-admin/blob/master/web/src/layout/main/components/TagList.vue)

[DEMO](https://admin.dumogu.top/main/index)


https://www.xamrdz.com/lan/5h91994107.html

相关文章: