refactor: 标签页重构,采用响应式storage

This commit is contained in:
xiaoxian521 2021-06-23 14:24:38 +08:00
parent 6a89af382f
commit 8177de106b
10 changed files with 181 additions and 236 deletions

14
package-lock.json generated
View File

@ -27,7 +27,7 @@
"path-to-regexp": "^6.2.0",
"pinia": "^2.0.0-beta.2",
"resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.1",
"responsive-storage": "^1.0.4",
"v-contextmenu": "^3.0.0",
"vue": "^3.1.1",
"vue-i18n": "^9.1.6",
@ -2476,9 +2476,9 @@
}
},
"node_modules/responsive-storage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/responsive-storage/-/responsive-storage-1.0.1.tgz",
"integrity": "sha512-p9HXODNHkdRUgaJ+mm6qKhsQCgWo1bGHAUlvbb4II5yJnb189Hrb8kKxHfG1KlbrnAQ2wR60a2BLq1AoDLp2nA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/responsive-storage/-/responsive-storage-1.0.4.tgz",
"integrity": "sha512-egiborkG1SMM5rQYMb0J0tdnDa/yH4h4ptA/fEcMAEoSaAe9j+Z4TMHFMb8x3BrM02StNuO8sNwbwVxLj+xzcA==",
"dependencies": {
"vue": "^3.1.1"
}
@ -5067,9 +5067,9 @@
}
},
"responsive-storage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/responsive-storage/-/responsive-storage-1.0.1.tgz",
"integrity": "sha512-p9HXODNHkdRUgaJ+mm6qKhsQCgWo1bGHAUlvbb4II5yJnb189Hrb8kKxHfG1KlbrnAQ2wR60a2BLq1AoDLp2nA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/responsive-storage/-/responsive-storage-1.0.4.tgz",
"integrity": "sha512-egiborkG1SMM5rQYMb0J0tdnDa/yH4h4ptA/fEcMAEoSaAe9j+Z4TMHFMb8x3BrM02StNuO8sNwbwVxLj+xzcA==",
"requires": {
"vue": "^3.1.1"
}

View File

@ -11,7 +11,7 @@
},
"husky": {
"hooks": {
"commit-msg": "node scripts/verify-commit.ts"
"commit-msg": "node scripts/verify-commit.js"
}
},
"dependencies": {
@ -35,7 +35,7 @@
"path-to-regexp": "^6.2.0",
"pinia": "^2.0.0-beta.2",
"resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.1",
"responsive-storage": "^1.0.4",
"v-contextmenu": "^3.0.0",
"vue": "^3.1.1",
"vue-i18n": "^9.1.6",

22
scripts/verify-commit.js Normal file
View File

@ -0,0 +1,22 @@
const chalk = require("chalk")
const msgPath = process.env.HUSKY_GIT_PARAMS
const msg = require("fs").readFileSync(msgPath, "utf-8").trim()
const commitRE = /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types)(\(.+\))?: .{1,50}/
if (!commitRE.test(msg)) {
console.error(
` ${chalk.bgRed.white(" ERROR ")} ${chalk.red(
"不合法的 commit 消息格式"
)}\n\n` +
chalk.red(" 请使用正确的提交格式:\n\n") +
` ${chalk.green("feat: add 'comments' option")}\n` +
` ${chalk.green("fix: handle events on blur (close #28)")}\n\n` +
chalk.red(
" 请查看 git commit 提交规范https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md \n"
)
)
process.exit(1)
}

View File

@ -1,23 +0,0 @@
const chalk = require("chalk");
const msgPath = process.env.HUSKY_GIT_PARAMS;
const msg = require("fs").readFileSync(msgPath, "utf-8").trim();
const commitRE = /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types)(\(.+\))?: .{1,50}/;
if (!commitRE.test(msg)) {
console.log();
console.error(
` ${chalk.bgRed.white(" ERROR ")} ${chalk.red(
"不合法的 commit 消息格式"
)}\n\n` +
chalk.red(" 请使用正确的提交格式:\n\n") +
` ${chalk.green("feat: add 'comments' option")}\n` +
` ${chalk.green("fix: handle events on blur (close #28)")}\n\n` +
chalk.red(
" 请查看 git commit 提交规范https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md \n"
)
);
process.exit(1);
}

View File

@ -34,7 +34,6 @@ import { useRoute, useRouter } from "vue-router";
import { useAppStoreHook } from "/@/store/modules/app";
import SidebarItem from "./SidebarItem.vue";
import { algorithm } from "../../../utils/algorithm";
import { useDynamicRoutesHook } from "../tag/tagsHook";
import { emitter } from "/@/utils/mitt";
import Logo from "./Logo.vue";
import { storageLocal } from "/@/utils/storage";
@ -62,8 +61,6 @@ export default defineComponent({
return path;
});
const { dynamicRouteTags } = useDynamicRoutesHook();
const menuSelect = (indexPath: string): void => {
let parentPath = "";
let parentPathIndex = indexPath.lastIndexOf("/");
@ -74,7 +71,11 @@ export default defineComponent({
function findCurrentRoute(routes) {
return routes.map((item, key) => {
if (item.path === indexPath) {
dynamicRouteTags(indexPath, parentPath, item);
//
emitter.emit("changLayoutRoute", {
indexPath,
parentPath
});
} else {
if (item.children) findCurrentRoute(item.children);
}
@ -82,7 +83,6 @@ export default defineComponent({
return;
}
findCurrentRoute(algorithm.increaseIndexes(router));
emitter.emit("changLayoutRoute", indexPath);
};
onBeforeMount(() => {

View File

@ -72,8 +72,6 @@
</template>
<script lang='ts'>
import { useDynamicRoutesHook } from "./tagsHook";
import { useRoute, useRouter } from "vue-router";
import {
ref,
watchEffect,
@ -83,22 +81,39 @@ import {
nextTick,
getCurrentInstance
} from "vue";
import { useRoute, useRouter } from "vue-router";
import { storageLocal } from "/@/utils/storage";
import { emitter } from "/@/utils/mitt";
import { toggleClass, removeClass, hasClass } from "/@/utils/operate";
import { templateRef } from "@vueuse/core";
import { homeRoute } from "./type";
let refreshButton = "refresh-button";
let routerArrays = [
{
path: "/welcome",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
showLink: true,
savedPosition: false
}
}
];
export default {
computed: {
dynamicTagList() {
if (
!this.$storage.routesInStorage ||
this.$storage.routesInStorage.length === 0
) {
this.$storage.routesInStorage = routerArrays;
}
return this.$storage.routesInStorage;
}
},
setup() {
let vm: any;
const {
deleteDynamicTag,
dynamicRouteTags,
dRoutes,
routesLength
} = useDynamicRoutesHook();
let st: any;
const route = useRoute();
const router = useRouter();
const showTags = ref(storageLocal.getItem("tagsVal") || false);
@ -116,21 +131,21 @@ export default {
icon: "el-icon-close",
text: "关闭当前标签页",
divided: false,
disabled: unref(routesLength) > 1 ? false : true,
disabled: routerArrays.length > 1 ? false : true,
show: true
},
{
icon: "el-icon-more",
text: "关闭其他标签页",
divided: true,
disabled: unref(routesLength) > 2 ? false : true,
disabled: routerArrays.length > 2 ? false : true,
show: true
},
{
icon: "el-icon-minus",
text: "关闭全部标签页",
divided: false,
disabled: unref(routesLength) > 1 ? false : true,
disabled: routerArrays.length > 1 ? false : true,
show: true
}
]);
@ -148,30 +163,33 @@ export default {
//
let currentSelect = ref({});
function deleteMenu(item) {
let tagslen = storageLocal.getItem("routesInStorage").length;
if (tagslen === 2) {
Array.from([1, 2, 3]).forEach(v => {
tagsViews.value[v].disabled = true;
});
}
if (tagslen === 3) {
tagsViews.value[2].disabled = true;
}
deleteDynamicTag(item, route.path);
}
// tabview
let stop = watchEffect(() => {
let parentPath = route.path.slice(0, route.path.lastIndexOf("/"));
dynamicRouteTags(route.path, parentPath, route);
function dynamicRouteTag(value: string, parentPath: string): void {
const hasValue = st.routesInStorage.some((item: any) => {
return item.path === value;
});
setTimeout(() => {
//
stop();
function concatPath(arr: object[], value: string, parentPath: string) {
if (!hasValue) {
arr.forEach((arrItem: any) => {
let pathConcat = parentPath + arrItem.path;
if (arrItem.path === value || pathConcat === value) {
routerArrays.push({
path: value,
meta: arrItem.meta
});
st.routesInStorage = routerArrays;
} else {
if (arrItem.children && arrItem.children.length > 0) {
concatPath(arrItem.children, value, parentPath);
}
}
});
}
}
concatPath(router.options.routes, value, parentPath);
}
//
function onFresh() {
toggleClass(true, refreshButton, document.querySelector(".rotate"));
const { path, fullPath } = unref(route);
@ -183,6 +201,57 @@ export default {
}, 600);
}
function deleteDynamicTag(obj: any, current: any, other: any) {
let valueIndex: number = routerArrays.findIndex((item: any) => {
return item.path === obj.path;
});
if (other) {
st.routesInStorage = routerArrays = [
{
path: "/welcome",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
showLink: true,
savedPosition: false
}
},
obj
];
router.push(obj.path);
Array.from([2]).forEach(v => {
tagsViews.value[v].disabled = true;
});
} else {
//
routerArrays.splice(valueIndex, 1);
st.routesInStorage = routerArrays;
}
if (current === obj.path) {
// tagtag
let newRoute: any = routerArrays.slice(-1);
nextTick(() => {
router.push({
path: newRoute[0].path
});
});
}
}
function deleteMenu(item, other = false) {
if (routerArrays.length === 2) {
Array.from([1, 2, 3]).forEach(v => {
tagsViews.value[v].disabled = true;
});
}
if (routerArrays.length === 3) {
tagsViews.value[2].disabled = true;
}
deleteDynamicTag(item, route.path, other);
}
function onClickDrop(key, item, selectRoute) {
if (item && item.disabled) return;
//
@ -199,17 +268,20 @@ export default {
break;
case 2:
//
dRoutes.value = selectRoute
? [homeRoute, { path: selectRoute.path, meta: selectRoute.meta }]
: [homeRoute, { path: route.path, meta: route.meta }];
storageLocal.setItem("routesInStorage", dRoutes.value);
tagsViews.value[2].disabled = true;
if (selectRoute) router.push(selectRoute.path);
selectRoute
? deleteMenu(
{
path: selectRoute.path,
meta: selectRoute.meta
},
true
)
: deleteMenu({ path: route.path, meta: route.meta }, true);
break;
case 3:
//
dRoutes.value = [homeRoute];
storageLocal.setItem("routesInStorage", dRoutes.value);
routerArrays.splice(1, routerArrays.length);
st.routesInStorage = routerArrays;
router.push("/welcome");
Array.from([1, 2, 3]).forEach(v => {
tagsViews.value[v].disabled = true;
@ -235,6 +307,12 @@ export default {
Array.from([1, 2, 3]).forEach(v => {
tagsViews.value[v].show = true;
});
} else if (st.routesInStorage.length === 2) {
//
tagsViews.value[2].show = false;
Array.from([0, 1, 3]).forEach(v => {
tagsViews.value[v].show = true;
});
} else {
Array.from([0, 1, 2, 3]).forEach(v => {
tagsViews.value[v].show = true;
@ -260,17 +338,6 @@ export default {
visible.value = false;
}
watch(
() => visible.value,
val => {
if (val) {
document.body.addEventListener("click", closeMenu);
} else {
document.body.removeEventListener("click", closeMenu);
}
}
);
//
function onMouseenter(item, index) {
if (index) activeIndex.value = index;
@ -299,8 +366,20 @@ export default {
}
}
watch(
() => visible.value,
val => {
if (val) {
document.body.addEventListener("click", closeMenu);
} else {
document.body.removeEventListener("click", closeMenu);
}
}
);
onBeforeMount(() => {
vm = getCurrentInstance();
st = vm.appContext.app.config.globalProperties.$storage;
emitter.on("tagViewsChange", key => {
if (unref(showTags) === key) return;
@ -311,14 +390,16 @@ export default {
showModel.value = key;
});
emitter.on("changLayoutRoute", indexPath => {
let currentLen = storageLocal.getItem("routesInStorage").length;
if (currentLen === 1) {
//
emitter.on("changLayoutRoute", ({ indexPath, parentPath }) => {
dynamicRouteTag(indexPath, parentPath);
if (routerArrays.length === 2) {
Array.from([1, 3]).forEach(v => {
tagsViews.value[v].disabled = false;
});
}
if (currentLen >= 2) {
if (routerArrays.length > 2) {
Array.from([1, 2, 3]).forEach(v => {
tagsViews.value[v].disabled = false;
});
@ -327,7 +408,6 @@ export default {
});
return {
dynamicTagList: useDynamicRoutesHook().dRoutes,
deleteMenu,
showTags,
onFresh,

View File

@ -1,120 +0,0 @@
import { reactive, toRefs, unref, nextTick, computed } from "vue";
import { storageLocal } from "/@/utils/storage";
import { useRouter } from "vue-router";
import { homeRoute } from "./type";
interface InterDynamic {
dRoutes: object[];
[propName: string]: any;
}
// 默认显示首页tag
let dynamic: InterDynamic = reactive({
dRoutes: [
{
path: "/welcome",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
showLink: true,
savedPosition: false,
},
},
],
});
export function useDynamicRoutesHook() {
const router = useRouter();
const routesLength = computed(() => {
return storageLocal.getItem("routesInStorage")
? storageLocal.getItem("routesInStorage").length
: 0;
});
// 返回当前路由组成的数组
const routesStorageLists = computed(() => {
return storageLocal.getItem("routesInStorage")
? storageLocal.getItem("routesInStorage")
: [];
});
/**
* @param value string menu对应的路由path
* @param parentPath string
*/
const dynamicRouteTags = (
value: string,
parentPath: string,
route: any
): void => {
nextTick(() => {
if (unref(routesStorageLists).length > 2) {
dynamic.dRoutes = unref(routesStorageLists);
return;
}
});
const hasValue = dynamic.dRoutes.some((item: any) => {
return item.path === value;
});
if (route) {
let ramStorage = storageLocal.getItem("routesInStorage");
nextTick(() => {
if (ramStorage) {
let currentIndex = ramStorage.findIndex((v) => v.path === route.path);
if (currentIndex !== -1) return;
ramStorage.push({ path: route.path, meta: route.meta });
storageLocal.setItem("routesInStorage", ramStorage);
}
});
}
function concatPath(arr: object[], value: string, parentPath: string) {
if (!hasValue) {
arr.forEach((arrItem: any) => {
let pathConcat = parentPath + "/" + arrItem.path;
if (arrItem.path === value || pathConcat === value) {
dynamic.dRoutes.push({ path: value, meta: arrItem.meta });
unref(routesLength) === 0
? storageLocal.setItem("routesInStorage", dynamic.dRoutes)
: [];
} else {
if (arrItem.children && arrItem.children.length > 0) {
concatPath(arrItem.children, value, parentPath);
}
}
});
}
}
concatPath(router.options.routes, value, parentPath);
};
/**
* @param value any tag路由
* @param current objct
*/
const deleteDynamicTag = async (obj: any, current: any): Promise<any> => {
let valueIndex: number = dynamic.dRoutes.findIndex((item: any) => {
return item.path === obj.path;
});
// 从当前匹配到的路径中删除
await dynamic.dRoutes.splice(valueIndex, 1);
storageLocal.setItem("routesInStorage", dynamic.dRoutes);
if (current === obj.path) {
// 如果删除当前激活tag就自动切换到最后一个tag
let newRoute: any = dynamic.dRoutes.slice(-1);
nextTick(() => {
router.push({
path: newRoute[0].path,
});
});
}
};
return {
...toRefs(dynamic),
dynamicRouteTags,
deleteDynamicTag,
routesLength,
routesStorageLists,
};
}

View File

@ -1,15 +0,0 @@
interface RouteModel {
readonly path: string;
readonly meta: object;
}
// 固定首页路由
export const homeRoute: RouteModel = {
path: "/welcome",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
showLink: true,
savedPosition: false,
},
};

View File

@ -26,6 +26,7 @@ app.config.globalProperties.$config = getConfig();
import Storage from "responsive-storage";
app.use(Storage, {
// 默认显示首页tag
routesInStorage: {
type: String,
default: Storage.getData(undefined, "routesInStorage") ?? [

View File

@ -14,7 +14,7 @@ const nestedRouter = {
},
children: [
{
path: "menu1",
path: "/nested/menu1",
component: () => import("/@/views/nested/menu1/index.vue"),
name: "Menu1",
meta: {
@ -25,7 +25,7 @@ const nestedRouter = {
redirect: "/nested/menu1/menu1-1",
children: [
{
path: "menu1-1",
path: "/nested/menu1/menu1-1",
component: () => import("/@/views/nested/menu1/menu1-1/index.vue"),
name: "Menu1-1",
meta: {
@ -35,7 +35,7 @@ const nestedRouter = {
},
},
{
path: "menu1-2",
path: "/nested/menu1/menu1-2",
component: () => import("/@/views/nested/menu1/menu1-2/index.vue"),
name: "Menu1-2",
redirect: "/nested/menu1/menu1-2/menu1-2-1",
@ -46,7 +46,7 @@ const nestedRouter = {
},
children: [
{
path: "menu1-2-1",
path: "/nested/menu1/menu1-2/menu1-2-1",
component: () =>
import("/@/views/nested/menu1/menu1-2/menu1-2-1/index.vue"),
name: "Menu1-2-1",
@ -57,7 +57,7 @@ const nestedRouter = {
},
},
{
path: "menu1-2-2",
path: "/nested/menu1/menu1-2/menu1-2-2",
component: () =>
import("/@/views/nested/menu1/menu1-2/menu1-2-2/index.vue"),
name: "Menu1-2-2",
@ -70,7 +70,7 @@ const nestedRouter = {
],
},
{
path: "menu1-3",
path: "/nested/menu1/menu1-3",
component: () => import("/@/views/nested/menu1/menu1-3/index.vue"),
name: "Menu1-3",
meta: {
@ -82,7 +82,7 @@ const nestedRouter = {
],
},
{
path: "menu2",
path: "/nested/menu2",
name: "Menu2",
component: () => import("/@/views/nested/menu2/index.vue"),
meta: {