mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	
							parent
							
								
									6971ba6c53
								
							
						
					
					
						commit
						d43316f7c9
					
				@ -41,7 +41,7 @@ const permissionRouter = {
 | 
			
		||||
    title: "menus.permission",
 | 
			
		||||
    icon: "lollipop",
 | 
			
		||||
    i18n: true,
 | 
			
		||||
    rank: 3
 | 
			
		||||
    rank: 7
 | 
			
		||||
  },
 | 
			
		||||
  children: [
 | 
			
		||||
    {
 | 
			
		||||
@ -72,7 +72,7 @@ const tabsRouter = {
 | 
			
		||||
    icon: "IF-team-icontabs",
 | 
			
		||||
    title: "menus.hstabs",
 | 
			
		||||
    i18n: true,
 | 
			
		||||
    rank: 8
 | 
			
		||||
    rank: 10
 | 
			
		||||
  },
 | 
			
		||||
  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 logoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
 | 
			
		||||
import nodeTree from "@iconify-icons/ri/node-tree";
 | 
			
		||||
import ubuntuFill from "@iconify-icons/ri/ubuntu-fill";
 | 
			
		||||
addIcon("arrow-right-s-line", arrowRightSLine);
 | 
			
		||||
addIcon("arrow-left-s-line", arrowLeftSLine);
 | 
			
		||||
addIcon("logout-circle-r-line", logoutCircleRLine);
 | 
			
		||||
addIcon("node-tree", nodeTree);
 | 
			
		||||
addIcon("ubuntu-fill", ubuntuFill);
 | 
			
		||||
 | 
			
		||||
// Font Awesome 4
 | 
			
		||||
import faUser from "@iconify-icons/fa/user";
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import { routeMetaType } from "../types";
 | 
			
		||||
import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import { storageSession } from "/@/utils/storage";
 | 
			
		||||
import { useAppStoreHook } from "/@/store/modules/app";
 | 
			
		||||
import { remainingPaths } from "/@/router/modules/index";
 | 
			
		||||
import { Title } from "../../../public/serverConfig.json";
 | 
			
		||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
 | 
			
		||||
import { remainingPaths } from "/@/router/modules/index";
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,9 @@ export default {
 | 
			
		||||
  permissionPage: "Page Permission",
 | 
			
		||||
  permissionButton: "Button Permission",
 | 
			
		||||
  hstabs: "Tabs Operate",
 | 
			
		||||
  hsMenuTree: "Menu Tree",
 | 
			
		||||
  hsguide: "Guide",
 | 
			
		||||
  externalLink: "External Link"
 | 
			
		||||
  externalLink: "External Link",
 | 
			
		||||
  hsAble: "Able",
 | 
			
		||||
  hsMenuTree: "Menu Tree",
 | 
			
		||||
  hsWatermark: "Water Mark"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,9 @@ export default {
 | 
			
		||||
  permissionPage: "页面权限",
 | 
			
		||||
  permissionButton: "按钮权限",
 | 
			
		||||
  hstabs: "标签页操作",
 | 
			
		||||
  hsMenuTree: "菜单树结构",
 | 
			
		||||
  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 Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const componentsRouter = {
 | 
			
		||||
  path: "/components",
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { $t } from "/@/plugins/i18n";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const editorRouter = {
 | 
			
		||||
  path: "/editor",
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { $t } from "/@/plugins/i18n";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const errorRouter = {
 | 
			
		||||
  path: "/error",
 | 
			
		||||
@ -10,7 +10,7 @@ const errorRouter = {
 | 
			
		||||
    icon: "position",
 | 
			
		||||
    title: $t("menus.hserror"),
 | 
			
		||||
    i18n: true,
 | 
			
		||||
    rank: 7
 | 
			
		||||
    rank: 9
 | 
			
		||||
  },
 | 
			
		||||
  children: [
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { $t } from "/@/plugins/i18n";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const externalLink = {
 | 
			
		||||
  path: "/external",
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { $t } from "/@/plugins/i18n";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const flowChartRouter = {
 | 
			
		||||
  path: "/flowChart",
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { $t } from "/@/plugins/i18n";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const guideRouter = {
 | 
			
		||||
  path: "/guide",
 | 
			
		||||
@ -10,7 +10,7 @@ const guideRouter = {
 | 
			
		||||
    icon: "guide",
 | 
			
		||||
    title: $t("menus.hsguide"),
 | 
			
		||||
    i18n: true,
 | 
			
		||||
    rank: 10
 | 
			
		||||
    rank: 11
 | 
			
		||||
  },
 | 
			
		||||
  children: [
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { $t } from "/@/plugins/i18n";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const homeRouter = {
 | 
			
		||||
  path: "/",
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
// 静态路由
 | 
			
		||||
import homeRouter from "./home";
 | 
			
		||||
import ableRouter from "./able";
 | 
			
		||||
import errorRouter from "./error";
 | 
			
		||||
import guideRouter from "./guide";
 | 
			
		||||
import editorRouter from "./editor";
 | 
			
		||||
import nestedRouter from "./nested";
 | 
			
		||||
import menuTreeRouter from "./menuTree";
 | 
			
		||||
import externalLink from "./externalLink";
 | 
			
		||||
import flowChartRouter from "./flowchart";
 | 
			
		||||
import remainingRouter from "./remaining";
 | 
			
		||||
@ -21,12 +21,12 @@ import { buildHierarchyTree } from "/@/utils/tree";
 | 
			
		||||
// 原始静态路由(未做任何处理)
 | 
			
		||||
const routes = [
 | 
			
		||||
  homeRouter,
 | 
			
		||||
  ableRouter,
 | 
			
		||||
  errorRouter,
 | 
			
		||||
  guideRouter,
 | 
			
		||||
  nestedRouter,
 | 
			
		||||
  externalLink,
 | 
			
		||||
  editorRouter,
 | 
			
		||||
  menuTreeRouter,
 | 
			
		||||
  flowChartRouter,
 | 
			
		||||
  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 Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const nestedRouter = {
 | 
			
		||||
  path: "/nested",
 | 
			
		||||
@ -10,7 +10,7 @@ const nestedRouter = {
 | 
			
		||||
    title: $t("menus.hsmenus"),
 | 
			
		||||
    icon: "histogram",
 | 
			
		||||
    i18n: true,
 | 
			
		||||
    rank: 5
 | 
			
		||||
    rank: 8
 | 
			
		||||
  },
 | 
			
		||||
  children: [
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { $t } from "/@/plugins/i18n";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
 | 
			
		||||
const remainingRouter = [
 | 
			
		||||
  {
 | 
			
		||||
 | 
			
		||||
@ -8,11 +8,11 @@ import {
 | 
			
		||||
} from "vue-router";
 | 
			
		||||
import { router } from "./index";
 | 
			
		||||
import { loadEnv } from "../../build";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
import { useTimeoutFn } from "@vueuse/core";
 | 
			
		||||
import { RouteConfigs } from "/@/layout/types";
 | 
			
		||||
import { buildHierarchyTree } from "/@/utils/tree";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
const Layout = () => import("/@/layout/index.vue");
 | 
			
		||||
// https://cn.vitejs.dev/guide/features.html#glob-import
 | 
			
		||||
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 => {
 | 
			
		||||
  return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
 | 
			
		||||
};
 | 
			
		||||
@ -40,3 +42,16 @@ export const toggleClass = (
 | 
			
		||||
  className = className.replace(clsName, "");
 | 
			
		||||
  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