图片水印实现
水印模块
import { createGlobalStyle } from 'styled-components' // 卡片水印 // 防止表格遮挡水印 const Water = createGlobalStyle` .ant-card { // prettier-ignore background-position: 0 0, 160PX 160PX !important; background-repeat: repeat, repeat !important; background-image: url('${window.location.origin}/script/account/print.svg'), url('${window.location.origin}/script/account/print.svg') !important; } .ant-table-placeholder { background: transparent !important; } ` export default Water
获取水印
app / router.js
router.get('/script/account/print.svg', controller.script.account.print)
app / controller / script / account.js
'use strict' const { Controller } = require('egg') const path = require('path') const TextToSVG = require('text-to-svg') const textToSVG = TextToSVG.loadSync(path.resolve(__dirname, '../../fonts/SourceHanSansCN-Light.otf')) const attributes = { fill: '#ededed', stroke: '#ededed', transform: 'rotate(-20, 90 75)' } const options = { anchor: 'center middle', x: 160, y: 160, width: 320, height: 320, fontSize: 18, letterSpacing: 0.05, attributes } const getSvgContent = text => { const svgPath = textToSVG.getPath(text, options) return `` } const loginRules = { token: { type: 'string', required: true, }, } class EggController extends Controller { async print() { const accountInfo = await this.ctx.adminUser.getAccountInfo() const { isLogin, name, mobile } = accountInfo const content = isLogin ? `${name}${String(mobile).slice(7)}` : '小帮规划' this.ctx.type = 'image/svg+xml' this.ctx.body = getSvgContent(content) } async infojs() { const token = await this.ctx.cookieUtils.getToken() const accountInfo = await this.ctx.adminUser.getAccountInfo() const info = Object.assign({ token }, accountInfo) this.ctx.type = 'application/javascript' this.ctx.body = ` window.APP_ACCOUNT = ${JSON.stringify(info)}; ` } async login() { try { this.ctx.validator.validateBody(loginRules) } catch (error) { return this.ctx.reject(10004, error.message) } const { token } = this.ctx.request.body this.ctx.cookieUtils.setToken(token) return this.ctx.resolve() } async logout() { const { deprecateTokenKey } = this.app.config this.ctx.cookieUtils.removeToken() this.ctx.cookies.set(deprecateTokenKey, null, this.ctx.cookieUtils.cookieOptions) return this.ctx.redirect('/') } } module.exports = EggController
依赖
package.json
"@types/styled-components": "^5.0.1", "babel-plugin-styled-components": "^1.10.7", "styled-components": "^5.0.1",
配置
.umirc.js
extraBabelPlugins: [ [ 'babel-plugin-styled-components', { ssr: false, fileName: false, displayName: false, }, ], ],
使用
import { useState, useEffect, Fragment } from 'react' import { ConfigProvider } from 'antd' import ProLayout from '@ant-design/pro-layout' import Dashboard from '@xb/layouts/Dashboard' import Water from '@xb/components/Water' import PageLoading from '@xb/components/PageLoading' import HeaderContent from '@xb/components/HeaderContent' import router from 'umi/router' import { registerApplication, start } from 'single-spa' import { connect } from 'dva' import iconLogo from '@xb/assets/logo.gif' // 布局默认样式配置 const layoutSettings = { navTheme: 'dark', primaryColor: '#1890ff', layout: 'sidemenu', contentWidth: 'Fluid', fixedHeader: true, autoHideHeader: false, fixSiderbar: true, menu: { locale: true, }, title: '小帮规划', pwa: false, colorWeak: false, } const { mobileLayoutPrefix, wxworkAuthBlackList, menuList, subappList } = window.APP_STATE // 获取扁平菜单标识 const getFlattenKeys = menuList => { let flattenKeys = [] menuList.forEach(item => { flattenKeys.push({ path: item.path, keys: item.keys, }) if (item.children) { flattenKeys = [...flattenKeys, ...getFlattenKeys(item.children)] } }) return flattenKeys } // 判断当前菜单是否展开 const isMenuExpanded = (currentKeys = [], menuKeys = []) => { const currentKey = currentKeys.join(',') const menuKey = menuKeys.join(',') return currentKey.indexOf(menuKey) !== -1 } // 获取父级菜单展开键 const getFatherKeys = (keys = []) => { return keys.filter((item, index) => index + 1 !== keys.length) } // 获取默认展开菜单 const flattenKeys = getFlattenKeys(menuList) const defaultMenu = flattenKeys.find(item => item.path === window.location.pathname) const defaultOpenKeys = defaultMenu ? defaultMenu.keys : [] const BasicLayout = ({ collapsed, location, dispatch }) => { const [openKeys, setOpenKeys] = useState(defaultOpenKeys) const { pathname } = location // 获取应用名称 const getAppByPath = pathname => { const row = subappList.find(({ manifest }) => { return manifest.paths.some(prefix => pathname.startsWith(prefix)) }) return Object.assign({}, row) } // 应用注册 useEffect(() => { subappList.forEach(({ name, manifest }) => { const loadingFunction = () => window.System.import(manifest.mainScript) const activityFunction = location => { const isMatched = manifest.paths.some(prefix => location.pathname.startsWith(prefix)) return isMatched } registerApplication(name, loadingFunction, activityFunction) start() }) }, []) // 侧边栏开关 const onCollapse = collapsed => { dispatch({ type: 'global/changeLayoutCollapsed', payload: Boolean(collapsed), }) } // 获取当前子应用 const { name: sourceAppName, manifest: sourceManifest } = getAppByPath(pathname) const hasMatched = Boolean(sourceAppName && sourceManifest) // 菜单数据 const menuDataRender = () => menuList // 父级菜单 const subMenuItemRender = (menuItemProps, defaultDom) => { const onMenuItemClick = () => { const isExpanded = isMenuExpanded(openKeys, menuItemProps.keys) const keys = isExpanded ? getFatherKeys(menuItemProps.keys) : menuItemProps.keys setOpenKeys(keys) } return ({defaultDom}) } // 基础菜单 // 参考文档 http://docs.xiaobangtouzi.com/pages/viewpage.action?pageId=6529045 const menuItemRender = (menuItemProps, defaultDom) => { const onItemClick = () => { setOpenKeys(menuItemProps.keys) const { name: targetAppName } = getAppByPath(menuItemProps.path) const httpReload = sourceAppName !== targetAppName const isReplacePath = pathname === menuItemProps.path switch (menuItemProps.type) { case 0: return window.open(window.APP_HERO.Linker.getDeprecatedAdminUrl(menuItemProps.path)) case 1: return window.open(window.APP_HERO.Linker.getInsuranceAdminUrl(menuItemProps.path)) case 2: if (httpReload) { return isReplacePath ? window.location.replace(menuItemProps.path) : window.open(menuItemProps.path, '_self') } return isReplacePath ? router.replace({ pathname: menuItemProps.path }) : router.push({ pathname: menuItemProps.path }) case 3: return window.open(menuItemProps.originalUrl) default: return null } } return ({defaultDom}) } const children = () // 没有侧边栏 const withoutLayout = wxworkAuthBlackList.includes(pathname) || pathname.startsWith(mobileLayoutPrefix) if (withoutLayout) { return {/* 首页友情提示 */} {pathname === '/' &&{/* 异步加载样式 */} {hasMatched && }} {/* 子应用匹配成功显示默认加载动画 */} {hasMatched && } {children} } return () } const connector = ({ global: { collapsed } }) => ({ collapsed }) export default connect(connector)(BasicLayout) } {...layoutSettings} > {children}