重构tagview
This commit is contained in:
parent
14358031e7
commit
c72188cc91
|
|
@ -8,6 +8,7 @@
|
|||
"build": "vue-cli-service build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@better-scroll/core": "^2.4.2",
|
||||
"clipboard": "^2.0.10",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^5.3.1",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<router-view v-slot="{ Component,route }">
|
||||
<transition name="fade-slide" mode="out-in" appear>
|
||||
<keep-alive :include="cachedViews">
|
||||
<component :is="Component" :key="route.name" />
|
||||
<component :is="Component" :key="route.name" v-if="isReload"/>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
|
|
@ -20,6 +20,10 @@
|
|||
const cachedViews = computed(()=>{
|
||||
return store.state.tagsView.cachedViews
|
||||
})
|
||||
|
||||
const isReload = computed(()=>{
|
||||
return store.state.app.isReload
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
<template>
|
||||
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
|
||||
<slot />
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted, ref,getCurrentInstance} from "vue";
|
||||
|
||||
const tagAndTagSpacing = 4 // tagAndTagSpacing
|
||||
|
||||
const left = ref(0)
|
||||
const scrollContainer = ref()
|
||||
|
||||
const scrollWrapper = computed(()=>{
|
||||
return scrollContainer.value.$refs.wrap
|
||||
})
|
||||
|
||||
const handleScroll = (e)=>{
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = scrollWrapper.value
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
|
||||
}
|
||||
|
||||
const moveToTarget = (currentTag,tagLists)=>{
|
||||
const $container = scrollContainer.value.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = scrollContainer.value.$refs.wrap$
|
||||
const _this = getCurrentInstance()
|
||||
const tagList = tagLists
|
||||
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
|
||||
if (tagList.length > 0) {
|
||||
firstTag = tagList[0]
|
||||
lastTag = tagList[tagList.length - 1]
|
||||
}
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||
} else {
|
||||
// find preTag and nextTag
|
||||
const currentIndex = tagList.findIndex(item => item === currentTag)
|
||||
const prevTag = tagList[currentIndex - 1]
|
||||
const nextTag = tagList[currentIndex + 1]
|
||||
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
|
||||
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
|
||||
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
moveToTarget
|
||||
})
|
||||
onMounted(()=>{
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
::v-deep(.el-scrollbar__bar){
|
||||
bottom: 0px;
|
||||
}
|
||||
::v-deep(.el-scrollbar__wrap){
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div ref="bsWrap" class="tags-scroll-wrap">
|
||||
<div ref="bsContent" class="tags-scroll" >
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted,nextTick } from 'vue';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
import BScroll from '@better-scroll/core';
|
||||
import type { Options } from '@better-scroll/core';
|
||||
|
||||
interface Props {
|
||||
/** better-scroll的配置: https://better-scroll.github.io/docs/zh-CN/guide/base-scroll-options.html */
|
||||
options: Options;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const bsWrap = ref<HTMLElement>();
|
||||
const instance = ref<BScroll>();
|
||||
const bsContent = ref<HTMLElement>();
|
||||
|
||||
function initBetterScroll() {
|
||||
nextTick(()=>{
|
||||
instance.value = new BScroll(bsWrap.value, props.options);
|
||||
})
|
||||
}
|
||||
|
||||
// 滚动元素发生变化,刷新BS
|
||||
const { width: wrapWidth } = useElementSize(bsWrap);
|
||||
|
||||
const { width, height } = useElementSize(bsContent);
|
||||
watch([() => wrapWidth.value, () => width.value, () => height.value], () => {
|
||||
if (instance.value) {
|
||||
instance.value.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
initBetterScroll();
|
||||
});
|
||||
|
||||
defineExpose({ instance });
|
||||
</script>
|
||||
<style scoped>
|
||||
.tags-scroll-wrap{
|
||||
width: 100%;
|
||||
}
|
||||
.tags-scroll-inner{
|
||||
/*display: flex;*/
|
||||
}
|
||||
.tags-scroll{
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,46 +1,69 @@
|
|||
<template>
|
||||
<div class="tags-view-container">
|
||||
<scroll-pane ref="rollPane" class="tags-view-wrapper" >
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
:ref="setTagRef"
|
||||
:class="isActive(tag)?'active':''"
|
||||
:key="tag.path"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
tag="span"
|
||||
class="tags-view-item"
|
||||
>
|
||||
<div class="tags-inner">
|
||||
<span >{{ tag.title }}</span>
|
||||
<el-icon @click.prevent.stop="closeSelectedTag(tag)"
|
||||
class="tag-icon"
|
||||
><circle-close-filled /></el-icon>
|
||||
<div class="tags-wrap-container">
|
||||
<div class="tags-view" ref="scrollContainer">
|
||||
<better-scroll :options="{ scrollX: true, scrollY: false, }" ref="bsScroll">
|
||||
<div class="tags-scroll-inner">
|
||||
<div v-for="tag in visitedViews"
|
||||
:ref="setTagRef"
|
||||
:path= "tag.path"
|
||||
:fullPath="tag.fullPath"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
:key="tag.path"
|
||||
class="item-tag-wrap"
|
||||
:class="isActive(tag)?'active':''"
|
||||
@click="routerGo(tag)">
|
||||
<div class="tags-view-item" >{{ tag.title }}</div>
|
||||
<el-icon @click.prevent.stop="closeSelectedTag(tag)" class="tag-icon">
|
||||
<circle-close-filled /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
</better-scroll>
|
||||
</div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="点击刷新"
|
||||
placement="bottom-end"
|
||||
>
|
||||
<div class="refresh" @click="refresh">
|
||||
<div class="refresh-inner">
|
||||
<el-icon><Refresh />
|
||||
</el-icon>
|
||||
</div></div>
|
||||
</el-tooltip>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, nextTick, onMounted, reactive, ref, watch} from "vue";
|
||||
import ScrollPane from "./ScrollPane.vue";
|
||||
import betterScroll from "./betterScroll.vue";
|
||||
import { useStore } from 'vuex'
|
||||
import {useRoute,useRouter} from 'vue-router'
|
||||
|
||||
const path = require('path')
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const refresh = ()=>{
|
||||
store.dispatch('app/setReload')
|
||||
}
|
||||
|
||||
const routes = computed(()=>{
|
||||
return store.state.permission.routes
|
||||
})
|
||||
|
||||
const visitedViews = computed(()=>{
|
||||
return store.state.tagsView.visitedViews
|
||||
})
|
||||
|
||||
const bsScroll = ref<Expose.BetterScroll>();
|
||||
let obj = new WeakMap()
|
||||
|
||||
let affixTags = ref([])
|
||||
const tags = ref([])
|
||||
// 多个ref 添加tag
|
||||
const setTagRef = (el)=>{
|
||||
if(el){
|
||||
if(!obj.get(el)){
|
||||
|
|
@ -48,10 +71,10 @@
|
|||
}
|
||||
obj.set(el,el)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const rollPane = ref()
|
||||
const rollPane = ref()
|
||||
const scrollContainer = ref()
|
||||
|
||||
function filterAffixTags(routes, basePath = '/') {
|
||||
let tags = []
|
||||
|
|
@ -76,7 +99,6 @@
|
|||
}
|
||||
|
||||
const initTags = ()=>{
|
||||
// let routesNew = routes.value.filter(item=>item.path!=='/login')
|
||||
let routesNew = routes.value
|
||||
let affixTag = affixTags.value = filterAffixTags(routesNew)
|
||||
for (const tag of affixTag) {
|
||||
|
|
@ -85,9 +107,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
function handleScroll() {
|
||||
closeMenu()
|
||||
}
|
||||
|
||||
const isActive = (rou)=> {
|
||||
return rou.path === route.path
|
||||
|
|
@ -105,6 +124,7 @@
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function toLastView(visitedViews, view) {
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
if (latestView) {
|
||||
|
|
@ -126,12 +146,36 @@
|
|||
})
|
||||
}
|
||||
|
||||
const routerGo = (tag)=>{
|
||||
router.push({
|
||||
path: tag.path,
|
||||
query: tag.query,
|
||||
})
|
||||
}
|
||||
|
||||
function handleScrollAction(currentTag,tagLists) {
|
||||
const scrollContainerRect = scrollContainer.value.getBoundingClientRect()
|
||||
let { left:currx, width:currentWidth } = currentTag.getBoundingClientRect()
|
||||
const clientX = currx + currentWidth / 2;
|
||||
const currentX = clientX - scrollContainerRect.left;
|
||||
const deltaX = currentX - scrollContainerRect.width / 2;
|
||||
if (bsScroll.value) {
|
||||
const { maxScrollX, x: leftX } = bsScroll.value.instance;
|
||||
const rightX = maxScrollX - leftX;
|
||||
const update = deltaX > 0 ? Math.max(-deltaX, rightX) : Math.min(-deltaX, -leftX);
|
||||
bsScroll.value?.instance.scrollBy(update, 0, 300);
|
||||
}
|
||||
}
|
||||
|
||||
function moveToCurrentTag(){
|
||||
nextTick(() => {
|
||||
for (const tag of tags.value) {
|
||||
if (tag.to.path === route.path) {
|
||||
rollPane.value.moveToTarget(tag,tags.value)
|
||||
if (tag.to.fullPath !== route.fullPath) {
|
||||
let path = tag.attributes.path.value
|
||||
if (path === route.path) {
|
||||
let fullPath = tag.attributes.fullPath.value
|
||||
// 移动
|
||||
handleScrollAction(tag,tags.value)
|
||||
if (fullPath !== route.fullPath) {
|
||||
store.dispatch('tagsView/updateVisitedView', route)
|
||||
}
|
||||
break
|
||||
|
|
@ -144,15 +188,23 @@
|
|||
onMounted(()=>{
|
||||
initTags()
|
||||
addTags()
|
||||
nextTick(()=>{
|
||||
setTimeout(()=>{
|
||||
moveToCurrentTag()
|
||||
},50)
|
||||
})
|
||||
|
||||
|
||||
watch(route,()=>{
|
||||
addTags()
|
||||
moveToCurrentTag()
|
||||
nextTick(()=>{
|
||||
setTimeout(()=>{
|
||||
moveToCurrentTag()
|
||||
},50)
|
||||
})
|
||||
})
|
||||
|
||||
router.beforeEach(async (to, from, next)=>{
|
||||
|
||||
|
||||
if((from.fullPath==='/error/404'||from.fullPath==='/error/401')&&to.fullPath==="/home") {
|
||||
let whiteList = ['/error/404','/error/401']
|
||||
await store.dispatch('tagsView/removeView', whiteList)
|
||||
|
|
@ -165,45 +217,45 @@
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tags-view-item {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
padding: 0 10px;
|
||||
.tags-wrap-container{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
.tags-view{
|
||||
background: white;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
/*display: flex;*/
|
||||
/*align-items: center;*/
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.tags-inner{
|
||||
overflow: hidden;
|
||||
}
|
||||
.refresh{
|
||||
//margin-left: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 22px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.refresh-inner{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
}
|
||||
.tag-icon{
|
||||
margin-left: 6px;
|
||||
}
|
||||
//.tag-icon{
|
||||
// //display: none;
|
||||
//}
|
||||
//&:hover{
|
||||
// .tag-icon{
|
||||
// display: block;
|
||||
//
|
||||
// }
|
||||
//}
|
||||
//box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
|
||||
}
|
||||
//padding-right: 10px;
|
||||
}
|
||||
.item-tag-wrap{
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
border: 1px solid #d8dce5;
|
||||
&.active .tag-icon{
|
||||
display: block;
|
||||
}
|
||||
|
|
@ -211,16 +263,30 @@
|
|||
background-color: #42b983;
|
||||
color: #fff;
|
||||
border-color: #42b983;
|
||||
/* &::before {*/
|
||||
/* content: '';*/
|
||||
/* background: #fff;*/
|
||||
/* display: inline-block;*/
|
||||
/* width: 8px;*/
|
||||
/* height: 8px;*/
|
||||
/* border-radius: 50%;*/
|
||||
/* position: relative;*/
|
||||
/* margin-right: 2px;*/
|
||||
/* }*/
|
||||
}
|
||||
}
|
||||
.item-tag-wrap:hover{
|
||||
border-color: #42b983;
|
||||
//color: #42b983;
|
||||
}
|
||||
|
||||
.tags-scroll-inner{
|
||||
display: flex;
|
||||
|
||||
flex-wrap: nowrap;
|
||||
|
||||
}
|
||||
.tag-icon{
|
||||
margin-left: 6px;
|
||||
}
|
||||
.tags-view-item {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
white-space: nowrap;
|
||||
.tags-inner{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -43,17 +43,17 @@ router.beforeEach(async(to, from, next) => {
|
|||
} catch (error) {
|
||||
next(`/login?redirect=${to.path}`)
|
||||
}
|
||||
NProgress.done()
|
||||
}
|
||||
}else{
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next()
|
||||
} else {
|
||||
next(`/login?redirect=${to.path}`)
|
||||
NProgress.done()
|
||||
|
||||
}
|
||||
NProgress.done()
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach(() => {
|
||||
NProgress.done()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const excelRouter = {
|
|||
path: 'export-excel',
|
||||
component: () => import('@/views/excel/export-excel.vue'),
|
||||
name: 'export-excel',
|
||||
meta: { title: '导出 Excel', }
|
||||
meta: { title: '导出 Excel', }
|
||||
},
|
||||
{
|
||||
path: 'export-merge-header',
|
||||
|
|
@ -30,13 +30,13 @@ const excelRouter = {
|
|||
path: 'upload-excel',
|
||||
component: () => import('@/views/excel/upload-excel.vue'),
|
||||
name: 'upload-excel',
|
||||
meta: { title: '上传 Excel', }
|
||||
meta: { title: '上传 Excel', }
|
||||
},
|
||||
{
|
||||
path: 'upload-style-excel',
|
||||
component: () => import('@/views/excel/export-style-excel.vue'),
|
||||
name: 'upload-style-excel',
|
||||
meta: { title: '自定义样式导出 Excel', }
|
||||
meta: { title: '自定义样式导出 Excel' }
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const othersRouter = {
|
|||
path: 'editor',
|
||||
component: () => import('@/views/other/editor.vue'),
|
||||
name: 'editor',
|
||||
meta: { title: '富文本编辑器', roles:['other'] }
|
||||
meta: { title: '富文本编辑器', roles:['other'] }
|
||||
},
|
||||
{
|
||||
path: 'mark-down',
|
||||
|
|
@ -28,43 +28,43 @@ const othersRouter = {
|
|||
path: 'print',
|
||||
component: () => import('@/views/other/print.vue'),
|
||||
name: 'print',
|
||||
meta: { title: '打印', }
|
||||
meta: { title: '打印', }
|
||||
},
|
||||
{
|
||||
path: 'cropper',
|
||||
component: () => import('@/views/other/cropper/index.vue'),
|
||||
name: 'cropper',
|
||||
meta: { title: '头像裁剪' }
|
||||
meta: { title: '头像裁剪' , }
|
||||
},
|
||||
{
|
||||
path: 'grid-sorter',
|
||||
component: () => import('@/views/other/grid-sorter.vue'),
|
||||
name: 'grid-sorter',
|
||||
meta: { title: '卡片拖拽', }
|
||||
meta: { title: '卡片拖拽', }
|
||||
},
|
||||
{
|
||||
path: 'splitpane',
|
||||
component: () => import('@/views/other/splitpane.vue'),
|
||||
name: 'splitpane',
|
||||
meta: { title: '分割模块', }
|
||||
meta: { title: '分割模块',}
|
||||
},
|
||||
{
|
||||
path: 'qrcode',
|
||||
component: () => import('@/views/other/qrcode.vue'),
|
||||
name: 'qrcode',
|
||||
meta: { title: '生成二维码', }
|
||||
meta: { title: '生成二维码', }
|
||||
},
|
||||
{
|
||||
path: 'right-menu',
|
||||
component: () => import('@/views/other/right-menu.vue'),
|
||||
name: 'right-menu',
|
||||
meta: { title: '右键菜单', }
|
||||
meta: { title: '右键菜单',}
|
||||
},
|
||||
{
|
||||
path: 'count',
|
||||
component: () => import('@/views/other/count.vue'),
|
||||
name: 'count',
|
||||
meta: { title: '数字自增长', }
|
||||
meta: { title: '数字自增长',}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const permissionRouter = {
|
|||
path: 'page',
|
||||
component: () => import('@/views/permission/page.vue'),
|
||||
name: 'page',
|
||||
meta: { title: '页面权限', icon: 'trend-charts', roles:['other'] }
|
||||
meta: { title: '页面权限', icon: 'trend-charts', roles:['other'] }
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ const state = {
|
|||
isCollapse: true,
|
||||
withoutAnimation:false,
|
||||
device: 'desktop',
|
||||
isReload:true,
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
|
|
@ -21,6 +22,14 @@ const mutations = {
|
|||
state.isCollapse = false
|
||||
state.withoutAnimation = withoutAnimation
|
||||
},
|
||||
|
||||
SET_RELOAD:(state) => {
|
||||
state.isReload = false
|
||||
setTimeout(()=>{
|
||||
state.isReload = true
|
||||
},50)
|
||||
},
|
||||
|
||||
}
|
||||
const actions = {
|
||||
toggleDevice({ commit }, device) {
|
||||
|
|
@ -29,6 +38,9 @@ const actions = {
|
|||
closeSideBar({ commit }, { withoutAnimation }) {
|
||||
commit('CLOSE_SIDEBAR', withoutAnimation)
|
||||
},
|
||||
setReload({commit}){
|
||||
commit('SET_RELOAD')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -32,9 +32,8 @@
|
|||
<el-divider></el-divider>
|
||||
<div style="margin-bottom: 15px"><h5>最喜欢的一句话</h5></div>
|
||||
<div>---------- 开心最重要</div>
|
||||
|
||||
<el-divider></el-divider>
|
||||
<div style="margin-bottom: 15px"><h5>如果对你有帮助的话,可以麻烦点一颗 star!</h5></div>
|
||||
<div style="margin-bottom: 15px"><h5>如果对你有帮助的话,可以麻烦点一颗 star! 你的鼓励是我继续优化的动力~~</h5></div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
|
|
|||
12
yarn.lock
12
yarn.lock
|
|
@ -940,6 +940,18 @@
|
|||
"@babel/helper-validator-identifier" "^7.16.7"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@better-scroll/core@^2.4.2":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.npmmirror.com/@better-scroll/core/-/core-2.4.2.tgz#e69470012d79923a18034c3e4931720fbb06eae5"
|
||||
integrity sha512-IqVZLnh04YpaEAy9wJDxtFK/stxVQjB9A9Wcr3Uwkj7Av1TtFpin+t/TObl53diNDG5ZJ+vck/OAthphpuugLA==
|
||||
dependencies:
|
||||
"@better-scroll/shared-utils" "^2.4.2"
|
||||
|
||||
"@better-scroll/shared-utils@^2.4.2":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.npmmirror.com/@better-scroll/shared-utils/-/shared-utils-2.4.2.tgz#1ac5c97495727093a22a8009560795dd5e3c18da"
|
||||
integrity sha512-Gy/Jfbpu+hq0u+PcjkTqyXGqAf+0dexTzEZ5IDXEVwJVLmd3cx8A73oTcAZ8QZgk4wSHvlMjXecSaptkhnNPEw==
|
||||
|
||||
"@ctrl/tinycolor@^3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f"
|
||||
|
|
|
|||
Loading…
Reference in New Issue