mirror of
https://github.com/fugary/simple-element-plus-template.git
synced 2025-12-31 03:17:49 +00:00
tabs优化
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
/* eslint-env node */
|
/* eslint-env node */
|
||||||
export default {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
'extends': [
|
extends: [
|
||||||
|
'plugin:vue/vue3-essential',
|
||||||
|
'eslint:recommended',
|
||||||
'plugin:vue/vue3-recommended',
|
'plugin:vue/vue3-recommended',
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'@vue/eslint-config-standard'
|
'@vue/eslint-config-standard'
|
||||||
@@ -10,6 +12,6 @@ export default {
|
|||||||
ecmaVersion: 'latest'
|
ecmaVersion: 'latest'
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'vue/multi-word-component-names': "off"
|
'vue/multi-word-component-names': 'off'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
78
src/components/common-form-control/index.vue
Normal file
78
src/components/common-form-control/index.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} CommonFormOption
|
||||||
|
* @property {'input'|'input-number'|'cascader'|'radio'
|
||||||
|
* |'radio-group'|'checkbox'|'checkbox-group'|'date-picker'
|
||||||
|
* |'time-picker'|'switch'|'select'|'option'|'slider'|'transfer'|'upload'} type 类型
|
||||||
|
* @property {any} value
|
||||||
|
* @property {any} config
|
||||||
|
* @property {string} prop
|
||||||
|
* @property {string} label
|
||||||
|
* @property {string} placeholder
|
||||||
|
* @property {{clearable:boolean,disabled:boolean}} attrs
|
||||||
|
* @property {[CommonFormOption]} children 子节点
|
||||||
|
* @property {Array<RuleItem>} rules 子节点
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @type {CommonFormOption}
|
||||||
|
*/
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'input'
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
prop: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
rules: { type: Array, default: () => [] },
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
attrs: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const inputType = computed(() => {
|
||||||
|
return `el-${props.type}`
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="inputType"
|
||||||
|
:prop="prop"
|
||||||
|
v-bind="attrs"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
>
|
||||||
|
<template v-if="children&&children.length">
|
||||||
|
<common-form-control
|
||||||
|
v-for="(childItem, index) in children"
|
||||||
|
:key="index"
|
||||||
|
:type="childItem.type"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template />
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -2,7 +2,26 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
/**
|
||||||
|
* @typedef {Object} CommonMenuItem 菜单对象
|
||||||
|
* @property {boolean} isDropdown 是否是下拉Dropdown样式
|
||||||
|
* @property {boolean} isSplit 是否是分割元素
|
||||||
|
* @property {string} menuCls 自定义样式
|
||||||
|
* @property {string} index 路由地址
|
||||||
|
* @property {Object} route 路由
|
||||||
|
* @property {string} icon 图标
|
||||||
|
* @property {number} iconSize 图标大小
|
||||||
|
* @property {string} label 菜单显示名称
|
||||||
|
* @property {string} labelKey 菜单显示名称的Key,国际化需要
|
||||||
|
* @method iconIf 图标计算函数
|
||||||
|
* @method click 点击事件
|
||||||
|
* @property {[CommonMenuItem]} children 子菜单
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @type {Object}
|
||||||
|
* @property {CommonMenuItem} menuItem 菜单对象
|
||||||
|
* @property index 序号
|
||||||
|
*/
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
menuItem: {
|
menuItem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -81,7 +100,7 @@ const dropdownClick = menuItem => {
|
|||||||
:class="menuCls"
|
:class="menuCls"
|
||||||
@click="menuItem.click&&menuItem.click()"
|
@click="menuItem.click&&menuItem.click()"
|
||||||
>
|
>
|
||||||
<el-dropdown>
|
<el-dropdown class="common-dropdown">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<common-icon
|
<common-icon
|
||||||
:size="menuItem.iconSize"
|
:size="menuItem.iconSize"
|
||||||
@@ -133,5 +152,10 @@ const dropdownClick = menuItem => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.common-dropdown {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.common-dropdown .el-icon {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -31,12 +31,22 @@ const selectHistoryTab = path => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const removeHistoryTab = path => {
|
const removeHistoryTab = path => {
|
||||||
const lastTab = tabsViewStore.removeHistoryTab(path)
|
const lastTab = tabsViewStore.removeHistoryTab({ path })
|
||||||
if (lastTab) {
|
if (lastTab) {
|
||||||
selectHistoryTab(lastTab)
|
selectHistoryTab(lastTab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refreshHistoryTab = tab => {
|
||||||
|
const time = new Date().getTime()
|
||||||
|
router.push(`${tab.path}?${time}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeOtherHistoryTabs = tab => {
|
||||||
|
tabsViewStore.removeOtherHistoryTabs(tab)
|
||||||
|
selectHistoryTab(tab.path)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -52,6 +62,9 @@ const removeHistoryTab = path => {
|
|||||||
<tabs-view-item
|
<tabs-view-item
|
||||||
v-for="item in tabsViewStore.historyTabs"
|
v-for="item in tabsViewStore.historyTabs"
|
||||||
:key="item.path"
|
:key="item.path"
|
||||||
|
:refresh-history-tab="refreshHistoryTab"
|
||||||
|
:remove-history-tab="removeHistoryTab"
|
||||||
|
:remove-other-history-tabs="removeOtherHistoryTabs"
|
||||||
:tab-item="item"
|
:tab-item="item"
|
||||||
/>
|
/>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|||||||
@@ -6,10 +6,16 @@ import { useTabsViewStore } from '@/stores/TabsViewStore'
|
|||||||
const tabsViewStore = useTabsViewStore()
|
const tabsViewStore = useTabsViewStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
/**
|
||||||
|
* @type RouteRecordRaw
|
||||||
|
*/
|
||||||
tabItem: {
|
tabItem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
},
|
||||||
|
removeHistoryTab: Function,
|
||||||
|
removeOtherHistoryTabs: Function,
|
||||||
|
refreshHistoryTab: Function
|
||||||
})
|
})
|
||||||
|
|
||||||
const menuName = computed(() => {
|
const menuName = computed(() => {
|
||||||
@@ -26,13 +32,33 @@ const menuInfo = computed(() => {
|
|||||||
:name="tabItem.path"
|
:name="tabItem.path"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span class="custom-tabs-label">
|
<el-dropdown trigger="contextmenu">
|
||||||
<common-icon
|
<span class="custom-tabs-label">
|
||||||
v-if="tabsViewStore.isShowTabIcon && menuInfo && menuInfo.icon"
|
<common-icon
|
||||||
:icon="menuInfo.icon"
|
v-if="tabsViewStore.isShowTabIcon && menuInfo && menuInfo.icon"
|
||||||
/>
|
:icon="menuInfo.icon"
|
||||||
<span>{{ menuName }}</span>
|
/>
|
||||||
</span>
|
<span>{{ menuName }}</span>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
@click="refreshHistoryTab(tabItem)"
|
||||||
|
>
|
||||||
|
<common-icon icon="refresh" />
|
||||||
|
{{ $t('common.label.refresh') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="removeHistoryTab(tabItem.path)">
|
||||||
|
<common-icon icon="close" />
|
||||||
|
{{ $t('common.label.close') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="removeOtherHistoryTabs(tabItem)">
|
||||||
|
<common-icon icon="close" />
|
||||||
|
{{ $t('common.label.closeOther') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import CommonIcon from '@/components/common-icon/index.vue'
|
import CommonIcon from '@/components/common-icon/index.vue'
|
||||||
import CommonInput from '@/components/common-form-input/index.vue'
|
import CommonFormControl from '@/components/common-form-control/index.vue'
|
||||||
import CommonMenu from '@/components/common-menu/index.vue'
|
import CommonMenu from '@/components/common-menu/index.vue'
|
||||||
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
||||||
import CommonTabsView from '@/components/common-tabs-view/index.vue'
|
import CommonTabsView from '@/components/common-tabs-view/index.vue'
|
||||||
@@ -13,7 +13,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
install (Vue) {
|
install (Vue) {
|
||||||
Vue.component('CommonIcon', CommonIcon)
|
Vue.component('CommonIcon', CommonIcon)
|
||||||
Vue.component('CommonInput', CommonInput)
|
Vue.component('CommonFormControl', CommonFormControl)
|
||||||
Vue.component('CommonMenu', CommonMenu)
|
Vue.component('CommonMenu', CommonMenu)
|
||||||
Vue.component('CommonMenuItem', CommonMenuItem)
|
Vue.component('CommonMenuItem', CommonMenuItem)
|
||||||
Vue.component('CommonTabsView', CommonTabsView)
|
Vue.component('CommonTabsView', CommonTabsView)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ common.label.login = '登录'
|
|||||||
common.label.index = '首页'
|
common.label.index = '首页'
|
||||||
common.label.settings = '设置'
|
common.label.settings = '设置'
|
||||||
common.label.close = '关闭'
|
common.label.close = '关闭'
|
||||||
|
common.label.refresh = '刷新'
|
||||||
|
common.label.closeOther = '关闭其他'
|
||||||
common.label.langCn = '中文'
|
common.label.langCn = '中文'
|
||||||
common.label.langEn = 'English'
|
common.label.langEn = 'English'
|
||||||
common.label.language = '语言'
|
common.label.language = '语言'
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ common.label.login = 'Login'
|
|||||||
common.label.index = 'Home'
|
common.label.index = 'Home'
|
||||||
common.label.settings = 'Settings'
|
common.label.settings = 'Settings'
|
||||||
common.label.close = 'Close'
|
common.label.close = 'Close'
|
||||||
|
common.label.refresh = 'Refresh'
|
||||||
|
common.label.closeOther = 'Close Others'
|
||||||
common.label.langCn = '中文'
|
common.label.langCn = '中文'
|
||||||
common.label.langEn = 'English'
|
common.label.langEn = 'English'
|
||||||
common.label.language = 'Language'
|
common.label.language = 'Language'
|
||||||
|
|||||||
@@ -1,21 +1,35 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} TabsViewStore
|
||||||
|
* @property {boolean} isTabMode 是否开启tab模式
|
||||||
|
* @property {boolean} isCachedTabMode 是否开启tab缓存
|
||||||
|
* @property {boolean} isShowTabIcon 是否显示tab的图标
|
||||||
|
* @property {[import('vue-router').RouteRecordRaw]} historyTabs 历史tab列表
|
||||||
|
* @property {[string]} cachedTabs 缓存的tab列表
|
||||||
|
* @method removeHistoryTab
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @return {TabsViewStore}
|
||||||
|
*/
|
||||||
export const useTabsViewStore = defineStore('tabsView', () => {
|
export const useTabsViewStore = defineStore('tabsView', () => {
|
||||||
const isTabMode = ref(true)
|
const isTabMode = ref(true)
|
||||||
const isCachedTabMode = ref(true)
|
const isCachedTabMode = ref(true)
|
||||||
const isShowTabIcon = ref(true)
|
const isShowTabIcon = ref(true)
|
||||||
|
/**
|
||||||
|
* @type {{value: [import('vue-router').RouteRecordRaw]}}
|
||||||
|
*/
|
||||||
const historyTabs = ref([])
|
const historyTabs = ref([])
|
||||||
|
/**
|
||||||
|
* @type {{value: [string]}}
|
||||||
|
*/
|
||||||
const cachedTabs = ref([])
|
const cachedTabs = ref([])
|
||||||
|
|
||||||
const clearHistoryTabs = () => {
|
const clearHistoryTabs = () => {
|
||||||
if (historyTabs.value.length) {
|
if (historyTabs.value.length) {
|
||||||
const tab = historyTabs.value[0]
|
const tab = historyTabs.value[0]
|
||||||
historyTabs.value = [tab]
|
removeOtherHistoryTabs(tab)
|
||||||
cachedTabs.value = []
|
|
||||||
if (isCachedTabMode.value && tab.name) {
|
|
||||||
cachedTabs.value = [tab.name]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,21 +40,28 @@ export const useTabsViewStore = defineStore('tabsView', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addHistoryTab = tab => {
|
const addHistoryTab = (tab, insertIdx) => {
|
||||||
// 添加tab
|
// 添加tab
|
||||||
if (isTabMode.value) {
|
if (isTabMode.value) {
|
||||||
const idx = historyTabs.value.findIndex(v => v.path === tab.path)
|
const idx = historyTabs.value.findIndex(v => v.path === tab.path)
|
||||||
if (idx < 0) {
|
if (idx < 0) {
|
||||||
historyTabs.value.push(Object.assign({}, tab)) // 可能是Proxy,需要解析出来
|
if (insertIdx !== undefined) {
|
||||||
|
historyTabs.value.splice(insertIdx, 0, tab)
|
||||||
|
} else {
|
||||||
|
historyTabs.value.push(Object.assign({}, tab)) // 可能是Proxy,需要解析出来
|
||||||
|
}
|
||||||
if (isCachedTabMode.value && tab.name) {
|
if (isCachedTabMode.value && tab.name) {
|
||||||
cachedTabs.value.push(tab.name)
|
console.info('=======================add tab', tab.name)
|
||||||
|
if (!cachedTabs.value.includes(tab.name)) {
|
||||||
|
cachedTabs.value.push(tab.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const removeHistoryTab = path => {
|
const removeHistoryTab = tab => {
|
||||||
if (historyTabs.value.length > 1) {
|
if (historyTabs.value.length > 1) {
|
||||||
const idx = historyTabs.value.findIndex(v => v.path === path)
|
const idx = historyTabs.value.findIndex(v => v.path === tab.path)
|
||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
removeCachedTab(historyTabs.value[idx])
|
removeCachedTab(historyTabs.value[idx])
|
||||||
// 删除tab
|
// 删除tab
|
||||||
@@ -59,6 +80,14 @@ export const useTabsViewStore = defineStore('tabsView', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const removeOtherHistoryTabs = tab => {
|
||||||
|
historyTabs.value = [tab]
|
||||||
|
cachedTabs.value = []
|
||||||
|
if (isCachedTabMode.value && tab.name) {
|
||||||
|
cachedTabs.value = [tab.name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isTabMode,
|
isTabMode,
|
||||||
isCachedTabMode,
|
isCachedTabMode,
|
||||||
@@ -81,6 +110,7 @@ export const useTabsViewStore = defineStore('tabsView', () => {
|
|||||||
isShowTabIcon.value = !isShowTabIcon.value
|
isShowTabIcon.value = !isShowTabIcon.value
|
||||||
},
|
},
|
||||||
removeHistoryTab,
|
removeHistoryTab,
|
||||||
|
removeOtherHistoryTabs,
|
||||||
clearHistoryTabs,
|
clearHistoryTabs,
|
||||||
findHistoryTab,
|
findHistoryTab,
|
||||||
addHistoryTab
|
addHistoryTab
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ const showLeftMenu = computed(() => {
|
|||||||
name="slide-fade"
|
name="slide-fade"
|
||||||
mode="out-in"
|
mode="out-in"
|
||||||
>
|
>
|
||||||
<KeepAlive :include="tabsViewStore.cachedTabs">
|
<KeepAlive
|
||||||
|
:include="tabsViewStore.cachedTabs"
|
||||||
|
:max="10"
|
||||||
|
>
|
||||||
<component
|
<component
|
||||||
:is="Component"
|
:is="Component"
|
||||||
:key="route.fullPath"
|
:key="route.fullPath"
|
||||||
@@ -44,7 +47,7 @@ const showLeftMenu = computed(() => {
|
|||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
</el-main>
|
</el-main>
|
||||||
<global-settings/>
|
<global-settings />
|
||||||
</el-container>
|
</el-container>
|
||||||
</el-container>
|
</el-container>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const globalConfigStore = useGlobalConfigStore()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-drawer v-model="globalConfigStore.isShowSettings" direction="rtl">
|
<el-drawer v-model="globalConfigStore.isShowSettings" direction="rtl" :size="350">
|
||||||
<template #header>
|
<template #header>
|
||||||
<strong>{{ $t('common.label.settings') }}</strong>
|
<strong>{{ $t('common.label.settings') }}</strong>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user