mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-08 01:17:23 +08:00
parent
6971ba6c53
commit
d43316f7c9
@ -41,7 +41,7 @@ const permissionRouter = {
|
|||||||
title: "menus.permission",
|
title: "menus.permission",
|
||||||
icon: "lollipop",
|
icon: "lollipop",
|
||||||
i18n: true,
|
i18n: true,
|
||||||
rank: 3
|
rank: 7
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@ -72,7 +72,7 @@ const tabsRouter = {
|
|||||||
icon: "IF-team-icontabs",
|
icon: "IF-team-icontabs",
|
||||||
title: "menus.hstabs",
|
title: "menus.hstabs",
|
||||||
i18n: true,
|
i18n: true,
|
||||||
rank: 8
|
rank: 10
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -52,10 +52,12 @@ import arrowRightSLine from "@iconify-icons/ri/arrow-right-s-line";
|
|||||||
import arrowLeftSLine from "@iconify-icons/ri/arrow-left-s-line";
|
import arrowLeftSLine from "@iconify-icons/ri/arrow-left-s-line";
|
||||||
import logoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import logoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||||
import nodeTree from "@iconify-icons/ri/node-tree";
|
import nodeTree from "@iconify-icons/ri/node-tree";
|
||||||
|
import ubuntuFill from "@iconify-icons/ri/ubuntu-fill";
|
||||||
addIcon("arrow-right-s-line", arrowRightSLine);
|
addIcon("arrow-right-s-line", arrowRightSLine);
|
||||||
addIcon("arrow-left-s-line", arrowLeftSLine);
|
addIcon("arrow-left-s-line", arrowLeftSLine);
|
||||||
addIcon("logout-circle-r-line", logoutCircleRLine);
|
addIcon("logout-circle-r-line", logoutCircleRLine);
|
||||||
addIcon("node-tree", nodeTree);
|
addIcon("node-tree", nodeTree);
|
||||||
|
addIcon("ubuntu-fill", ubuntuFill);
|
||||||
|
|
||||||
// Font Awesome 4
|
// Font Awesome 4
|
||||||
import faUser from "@iconify-icons/fa/user";
|
import faUser from "@iconify-icons/fa/user";
|
||||||
|
@ -5,6 +5,7 @@ import { routeMetaType } from "../types";
|
|||||||
import { transformI18n } from "/@/plugins/i18n";
|
import { transformI18n } from "/@/plugins/i18n";
|
||||||
import { storageSession } from "/@/utils/storage";
|
import { storageSession } from "/@/utils/storage";
|
||||||
import { useAppStoreHook } from "/@/store/modules/app";
|
import { useAppStoreHook } from "/@/store/modules/app";
|
||||||
|
import { remainingPaths } from "/@/router/modules/index";
|
||||||
import { Title } from "../../../public/serverConfig.json";
|
import { Title } from "../../../public/serverConfig.json";
|
||||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||||
import { remainingPaths } from "/@/router/modules/index";
|
import { remainingPaths } from "/@/router/modules/index";
|
||||||
|
@ -32,7 +32,9 @@ export default {
|
|||||||
permissionPage: "Page Permission",
|
permissionPage: "Page Permission",
|
||||||
permissionButton: "Button Permission",
|
permissionButton: "Button Permission",
|
||||||
hstabs: "Tabs Operate",
|
hstabs: "Tabs Operate",
|
||||||
hsMenuTree: "Menu Tree",
|
|
||||||
hsguide: "Guide",
|
hsguide: "Guide",
|
||||||
externalLink: "External Link"
|
externalLink: "External Link",
|
||||||
|
hsAble: "Able",
|
||||||
|
hsMenuTree: "Menu Tree",
|
||||||
|
hsWatermark: "Water Mark"
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,9 @@ export default {
|
|||||||
permissionPage: "页面权限",
|
permissionPage: "页面权限",
|
||||||
permissionButton: "按钮权限",
|
permissionButton: "按钮权限",
|
||||||
hstabs: "标签页操作",
|
hstabs: "标签页操作",
|
||||||
hsMenuTree: "菜单树结构",
|
|
||||||
hsguide: "引导页",
|
hsguide: "引导页",
|
||||||
externalLink: "外链"
|
externalLink: "外链",
|
||||||
|
hsAble: "功能",
|
||||||
|
hsMenuTree: "菜单树结构",
|
||||||
|
hsWatermark: "水印"
|
||||||
};
|
};
|
||||||
|
37
src/router/modules/able.ts
Normal file
37
src/router/modules/able.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { $t } from "/@/plugins/i18n";
|
||||||
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
|
const ableRouter = {
|
||||||
|
path: "/able",
|
||||||
|
name: "components",
|
||||||
|
component: Layout,
|
||||||
|
redirect: "/able/menuTree",
|
||||||
|
meta: {
|
||||||
|
icon: "ubuntu-fill",
|
||||||
|
title: $t("menus.hsAble"),
|
||||||
|
i18n: true,
|
||||||
|
rank: 3
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/able/menuTree",
|
||||||
|
name: "reMenuTree",
|
||||||
|
component: () => import("/@/views/able/menu-tree.vue"),
|
||||||
|
meta: {
|
||||||
|
title: $t("menus.hsMenuTree"),
|
||||||
|
i18n: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/able/watermark",
|
||||||
|
name: "reWatermark",
|
||||||
|
component: () => import("/@/views/able/watermark.vue"),
|
||||||
|
meta: {
|
||||||
|
title: $t("menus.hsWatermark"),
|
||||||
|
i18n: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ableRouter;
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const componentsRouter = {
|
const componentsRouter = {
|
||||||
path: "/components",
|
path: "/components",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const editorRouter = {
|
const editorRouter = {
|
||||||
path: "/editor",
|
path: "/editor",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const errorRouter = {
|
const errorRouter = {
|
||||||
path: "/error",
|
path: "/error",
|
||||||
@ -10,7 +10,7 @@ const errorRouter = {
|
|||||||
icon: "position",
|
icon: "position",
|
||||||
title: $t("menus.hserror"),
|
title: $t("menus.hserror"),
|
||||||
i18n: true,
|
i18n: true,
|
||||||
rank: 7
|
rank: 9
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const externalLink = {
|
const externalLink = {
|
||||||
path: "/external",
|
path: "/external",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const flowChartRouter = {
|
const flowChartRouter = {
|
||||||
path: "/flowChart",
|
path: "/flowChart",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const guideRouter = {
|
const guideRouter = {
|
||||||
path: "/guide",
|
path: "/guide",
|
||||||
@ -10,7 +10,7 @@ const guideRouter = {
|
|||||||
icon: "guide",
|
icon: "guide",
|
||||||
title: $t("menus.hsguide"),
|
title: $t("menus.hsguide"),
|
||||||
i18n: true,
|
i18n: true,
|
||||||
rank: 10
|
rank: 11
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const homeRouter = {
|
const homeRouter = {
|
||||||
path: "/",
|
path: "/",
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// 静态路由
|
// 静态路由
|
||||||
import homeRouter from "./home";
|
import homeRouter from "./home";
|
||||||
|
import ableRouter from "./able";
|
||||||
import errorRouter from "./error";
|
import errorRouter from "./error";
|
||||||
import guideRouter from "./guide";
|
import guideRouter from "./guide";
|
||||||
import editorRouter from "./editor";
|
import editorRouter from "./editor";
|
||||||
import nestedRouter from "./nested";
|
import nestedRouter from "./nested";
|
||||||
import menuTreeRouter from "./menuTree";
|
|
||||||
import externalLink from "./externalLink";
|
import externalLink from "./externalLink";
|
||||||
import flowChartRouter from "./flowchart";
|
import flowChartRouter from "./flowchart";
|
||||||
import remainingRouter from "./remaining";
|
import remainingRouter from "./remaining";
|
||||||
@ -21,12 +21,12 @@ import { buildHierarchyTree } from "/@/utils/tree";
|
|||||||
// 原始静态路由(未做任何处理)
|
// 原始静态路由(未做任何处理)
|
||||||
const routes = [
|
const routes = [
|
||||||
homeRouter,
|
homeRouter,
|
||||||
|
ableRouter,
|
||||||
errorRouter,
|
errorRouter,
|
||||||
guideRouter,
|
guideRouter,
|
||||||
nestedRouter,
|
nestedRouter,
|
||||||
externalLink,
|
externalLink,
|
||||||
editorRouter,
|
editorRouter,
|
||||||
menuTreeRouter,
|
|
||||||
flowChartRouter,
|
flowChartRouter,
|
||||||
componentsRouter
|
componentsRouter
|
||||||
];
|
];
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
|
||||||
import Layout from "/@/layout/index.vue";
|
|
||||||
|
|
||||||
const menuTreeRouter = {
|
|
||||||
path: "/menuTree",
|
|
||||||
name: "reMenuTree",
|
|
||||||
component: Layout,
|
|
||||||
redirect: "/menuTree/index",
|
|
||||||
meta: {
|
|
||||||
icon: "node-tree",
|
|
||||||
title: $t("menus.hsMenuTree"),
|
|
||||||
i18n: true,
|
|
||||||
rank: 9
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "/menuTree/index",
|
|
||||||
name: "reMenuTree",
|
|
||||||
component: () => import("/@/views/menu-tree/index.vue"),
|
|
||||||
meta: {
|
|
||||||
title: $t("menus.hsMenuTree"),
|
|
||||||
i18n: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export default menuTreeRouter;
|
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const nestedRouter = {
|
const nestedRouter = {
|
||||||
path: "/nested",
|
path: "/nested",
|
||||||
@ -10,7 +10,7 @@ const nestedRouter = {
|
|||||||
title: $t("menus.hsmenus"),
|
title: $t("menus.hsmenus"),
|
||||||
icon: "histogram",
|
icon: "histogram",
|
||||||
i18n: true,
|
i18n: true,
|
||||||
rank: 5
|
rank: 8
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { $t } from "/@/plugins/i18n";
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
|
|
||||||
const remainingRouter = [
|
const remainingRouter = [
|
||||||
{
|
{
|
||||||
|
@ -8,11 +8,11 @@ import {
|
|||||||
} from "vue-router";
|
} from "vue-router";
|
||||||
import { router } from "./index";
|
import { router } from "./index";
|
||||||
import { loadEnv } from "../../build";
|
import { loadEnv } from "../../build";
|
||||||
import Layout from "/@/layout/index.vue";
|
|
||||||
import { useTimeoutFn } from "@vueuse/core";
|
import { useTimeoutFn } from "@vueuse/core";
|
||||||
import { RouteConfigs } from "/@/layout/types";
|
import { RouteConfigs } from "/@/layout/types";
|
||||||
import { buildHierarchyTree } from "/@/utils/tree";
|
import { buildHierarchyTree } from "/@/utils/tree";
|
||||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||||
|
const Layout = () => import("/@/layout/index.vue");
|
||||||
// https://cn.vitejs.dev/guide/features.html#glob-import
|
// https://cn.vitejs.dev/guide/features.html#glob-import
|
||||||
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
|
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import type { FunctionArgs } from "@vueuse/core";
|
||||||
|
|
||||||
export const hasClass = (ele: RefType<any>, cls: string): any => {
|
export const hasClass = (ele: RefType<any>, cls: string): any => {
|
||||||
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
|
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
|
||||||
};
|
};
|
||||||
@ -40,3 +42,16 @@ export const toggleClass = (
|
|||||||
className = className.replace(clsName, "");
|
className = className.replace(clsName, "");
|
||||||
targetEl.className = flag ? `${className} ${clsName} ` : className;
|
targetEl.className = flag ? `${className} ${clsName} ` : className;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function useRafThrottle<T extends FunctionArgs>(fn: T): T {
|
||||||
|
let locked = false;
|
||||||
|
// @ts-ignore
|
||||||
|
return function (...args) {
|
||||||
|
if (locked) return;
|
||||||
|
locked = true;
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
fn.apply(this, args);
|
||||||
|
locked = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
116
src/utils/watermark.ts
Normal file
116
src/utils/watermark.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
ref,
|
||||||
|
Ref,
|
||||||
|
unref,
|
||||||
|
shallowRef,
|
||||||
|
onBeforeUnmount,
|
||||||
|
getCurrentInstance
|
||||||
|
} from "vue";
|
||||||
|
import { isDef } from "/@/utils/is";
|
||||||
|
import { useRafThrottle } from "/@/utils/operate";
|
||||||
|
import { addResizeListener, removeResizeListener } from "/@/utils/resize";
|
||||||
|
|
||||||
|
const domSymbol = Symbol("watermark-dom");
|
||||||
|
|
||||||
|
type attr = {
|
||||||
|
font?: string;
|
||||||
|
fillStyle?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useWatermark(
|
||||||
|
appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>
|
||||||
|
) {
|
||||||
|
const func = useRafThrottle(function () {
|
||||||
|
const el = unref(appendEl);
|
||||||
|
if (!el) return;
|
||||||
|
const { clientHeight: height, clientWidth: width } = el;
|
||||||
|
updateWatermark({ height, width });
|
||||||
|
});
|
||||||
|
const id = domSymbol.toString();
|
||||||
|
const watermarkEl = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
const domId = unref(watermarkEl);
|
||||||
|
watermarkEl.value = undefined;
|
||||||
|
const el = unref(appendEl);
|
||||||
|
if (!el) return;
|
||||||
|
domId && el.removeChild(domId);
|
||||||
|
removeResizeListener(el, func);
|
||||||
|
};
|
||||||
|
|
||||||
|
function createBase64(str: string, attr?: attr) {
|
||||||
|
const can = document.createElement("canvas");
|
||||||
|
const width = 300;
|
||||||
|
const height = 240;
|
||||||
|
Object.assign(can, { width, height });
|
||||||
|
|
||||||
|
const cans = can.getContext("2d");
|
||||||
|
if (cans) {
|
||||||
|
cans.rotate((-20 * Math.PI) / 120);
|
||||||
|
cans.font = attr?.font ?? "15px Reggae One";
|
||||||
|
cans.fillStyle = attr?.fillStyle ?? "rgba(0, 0, 0, 0.15)";
|
||||||
|
cans.textAlign = "left";
|
||||||
|
cans.textBaseline = "middle";
|
||||||
|
cans.fillText(str, width / 20, height);
|
||||||
|
}
|
||||||
|
return can.toDataURL("image/png");
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWatermark(
|
||||||
|
options: {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
str?: string;
|
||||||
|
attr?: attr;
|
||||||
|
} = {}
|
||||||
|
) {
|
||||||
|
const el = unref(watermarkEl);
|
||||||
|
if (!el) return;
|
||||||
|
if (isDef(options.width)) {
|
||||||
|
el.style.width = `${options.width}px`;
|
||||||
|
}
|
||||||
|
if (isDef(options.height)) {
|
||||||
|
el.style.height = `${options.height}px`;
|
||||||
|
}
|
||||||
|
if (isDef(options.str)) {
|
||||||
|
el.style.background = `url(${createBase64(
|
||||||
|
options.str,
|
||||||
|
options.attr
|
||||||
|
)}) left top repeat`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createWatermark = (str: string, attr?: attr) => {
|
||||||
|
if (unref(watermarkEl)) {
|
||||||
|
updateWatermark({ str, attr });
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
const div = document.createElement("div");
|
||||||
|
watermarkEl.value = div;
|
||||||
|
div.id = id;
|
||||||
|
div.style.pointerEvents = "none";
|
||||||
|
div.style.top = "0px";
|
||||||
|
div.style.left = "0px";
|
||||||
|
div.style.position = "absolute";
|
||||||
|
div.style.zIndex = "100000";
|
||||||
|
const el = unref(appendEl);
|
||||||
|
if (!el) return id;
|
||||||
|
const { clientHeight: height, clientWidth: width } = el;
|
||||||
|
updateWatermark({ str, width, height, attr });
|
||||||
|
el.appendChild(div);
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
function setWatermark(str: string, attr?: attr) {
|
||||||
|
createWatermark(str, attr);
|
||||||
|
addResizeListener(document.documentElement, func);
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
if (instance) {
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { setWatermark, clear };
|
||||||
|
}
|
13
src/views/able/watermark.vue
Normal file
13
src/views/able/watermark.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useWatermark } from "/@/utils/watermark";
|
||||||
|
const { setWatermark, clear } = useWatermark();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-button @click="setWatermark('vue-pure-admin')">创建</el-button>
|
||||||
|
<el-button @click="clear">清除</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
Loading…
x
Reference in New Issue
Block a user