水印模块
import { createGlobalStyle } from 'styled-components'// 卡片水印// 防止表格遮挡水印const Water = createGlobalStyle`.ant-card {// prettier-ignorebackground-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 `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${options.width}" height="${options.height}">${svgPath}</svg>`}const loginRules = {token: {type: 'string',required: true,},}class EggController extends Controller {async print() {const accountInfo = await this.ctx.adminUser.getAccountInfo()const { isLogin, name, mobile } = accountInfoconst 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.bodythis.ctx.cookieUtils.setToken(token)return this.ctx.resolve()}async logout() {const { deprecateTokenKey } = this.app.configthis.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.keyssetOpenKeys(keys)}return (<div style={{ overflow: 'hidden' }} onClick={onMenuItemClick}>{defaultDom}</div>)}// 基础菜单// 参考文档 http://docs.xiaobangtouzi.com/pages/viewpage.action?pageId=6529045const menuItemRender = (menuItemProps, defaultDom) => {const onItemClick = () => {setOpenKeys(menuItemProps.keys)const { name: targetAppName } = getAppByPath(menuItemProps.path)const httpReload = sourceAppName !== targetAppNameconst isReplacePath = pathname === menuItemProps.pathswitch (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 (<div style={{ overflow: 'hidden' }} onClick={onItemClick}>{defaultDom}</div>)}const children = (<Fragment><Water /><div id='sub-app'>{/* 首页友情提示 */}{pathname === '/' && <Dashboard />}{/* 子应用匹配成功显示默认加载动画 */}{hasMatched && <PageLoading />}</div>{/* 异步加载样式 */}{hasMatched && <link rel='stylesheet' href={sourceManifest.mainStyle} />}</Fragment>)// 没有侧边栏const withoutLayout = wxworkAuthBlackList.includes(pathname) || pathname.startsWith(mobileLayoutPrefix)if (withoutLayout) {return <ConfigProvider autoInsertSpaceInButton={false}>{children}</ConfigProvider>}return (<ConfigProvider autoInsertSpaceInButton={false}><ProLayoutmenuProps={{ openKeys }}logo={iconLogo}settings={layoutSettings}collapsed={collapsed}onCollapse={onCollapse}subMenuItemRender={subMenuItemRender}menuItemRender={menuItemRender}menuDataRender={menuDataRender}footerRender={false}rightContentRender={props => <HeaderContent {...props} />}{...layoutSettings}>{children}</ProLayout></ConfigProvider>)}const connector = ({ global: { collapsed } }) => ({ collapsed })export default connect(connector)(BasicLayout)
