mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 04:08:44 +08:00 
			
		
		
		
	初始化项目,自 v1.7.1 版本开始
This commit is contained in:
		
							
								
								
									
										100
									
								
								src/utils/Logger.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/utils/Logger.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
const isArray = function (obj: any): boolean {
 | 
			
		||||
  return Object.prototype.toString.call(obj) === '[object Array]'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Logger = () => {}
 | 
			
		||||
 | 
			
		||||
Logger.typeColor = function (type: string) {
 | 
			
		||||
  let color = ''
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case 'primary':
 | 
			
		||||
      color = '#2d8cf0'
 | 
			
		||||
      break
 | 
			
		||||
    case 'success':
 | 
			
		||||
      color = '#19be6b'
 | 
			
		||||
      break
 | 
			
		||||
    case 'info':
 | 
			
		||||
      color = '#909399'
 | 
			
		||||
      break
 | 
			
		||||
    case 'warn':
 | 
			
		||||
      color = '#ff9900'
 | 
			
		||||
      break
 | 
			
		||||
    case 'error':
 | 
			
		||||
      color = '#f03f14'
 | 
			
		||||
      break
 | 
			
		||||
    default:
 | 
			
		||||
      color = '#35495E'
 | 
			
		||||
      break
 | 
			
		||||
  }
 | 
			
		||||
  return color
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.print = function (type = 'default', text: any, back = false) {
 | 
			
		||||
  if (typeof text === 'object') {
 | 
			
		||||
    // 如果是對象則調用打印對象方式
 | 
			
		||||
    isArray(text) ? console.table(text) : console.dir(text)
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  if (back) {
 | 
			
		||||
    // 如果是打印帶背景圖的
 | 
			
		||||
    console.log(
 | 
			
		||||
      `%c ${text} `,
 | 
			
		||||
      `background:${Logger.typeColor(type)}; padding: 2px; border-radius: 4px; color: #fff;`
 | 
			
		||||
    )
 | 
			
		||||
  } else {
 | 
			
		||||
    console.log(
 | 
			
		||||
      `%c ${text} `,
 | 
			
		||||
      `border: 1px solid ${Logger.typeColor(type)};
 | 
			
		||||
        padding: 2px; border-radius: 4px;
 | 
			
		||||
        color: ${Logger.typeColor(type)};`
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.printBack = function (type = 'primary', text) {
 | 
			
		||||
  this.print(type, text, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.pretty = function (type = 'primary', title, text) {
 | 
			
		||||
  if (typeof text === 'object') {
 | 
			
		||||
    console.group('Console Group', title)
 | 
			
		||||
    console.log(
 | 
			
		||||
      `%c ${title}`,
 | 
			
		||||
      `background:${Logger.typeColor(type)};border:1px solid ${Logger.typeColor(type)};
 | 
			
		||||
        padding: 1px; border-radius: 4px; color: #fff;`
 | 
			
		||||
    )
 | 
			
		||||
    isArray(text) ? console.table(text) : console.dir(text)
 | 
			
		||||
    console.groupEnd()
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  console.log(
 | 
			
		||||
    `%c ${title} %c ${text} %c`,
 | 
			
		||||
    `background:${Logger.typeColor(type)};border:1px solid ${Logger.typeColor(type)};
 | 
			
		||||
      padding: 1px; border-radius: 4px 0 0 4px; color: #fff;`,
 | 
			
		||||
    `border:1px solid ${Logger.typeColor(type)};
 | 
			
		||||
      padding: 1px; border-radius: 0 4px 4px 0; color: ${Logger.typeColor(type)};`,
 | 
			
		||||
    'background:transparent'
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.prettyPrimary = function (title, ...text) {
 | 
			
		||||
  text.forEach((t) => this.pretty('primary', title, t))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.prettySuccess = function (title, ...text) {
 | 
			
		||||
  text.forEach((t) => this.pretty('success', title, t))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.prettyWarn = function (title, ...text) {
 | 
			
		||||
  text.forEach((t) => this.pretty('warn', title, t))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.prettyError = function (title, ...text) {
 | 
			
		||||
  text.forEach((t) => this.pretty('error', title, t))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Logger.prettyInfo = function (title, ...text) {
 | 
			
		||||
  text.forEach((t) => this.pretty('info', title, t))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Logger
 | 
			
		||||
							
								
								
									
										92
									
								
								src/utils/auth.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/utils/auth.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
import { useCache } from '@/hooks/web/useCache'
 | 
			
		||||
import { TokenType } from '@/api/login/types'
 | 
			
		||||
import { decrypt, encrypt } from '@/utils/jsencrypt'
 | 
			
		||||
 | 
			
		||||
const { wsCache } = useCache()
 | 
			
		||||
 | 
			
		||||
const AccessTokenKey = 'ACCESS_TOKEN'
 | 
			
		||||
const RefreshTokenKey = 'REFRESH_TOKEN'
 | 
			
		||||
 | 
			
		||||
// 获取token
 | 
			
		||||
export const getAccessToken = () => {
 | 
			
		||||
  // 此处与TokenKey相同,此写法解决初始化时Cookies中不存在TokenKey报错
 | 
			
		||||
  return wsCache.get(AccessTokenKey) ? wsCache.get(AccessTokenKey) : wsCache.get('ACCESS_TOKEN')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 刷新token
 | 
			
		||||
export const getRefreshToken = () => {
 | 
			
		||||
  return wsCache.get(RefreshTokenKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置token
 | 
			
		||||
export const setToken = (token: TokenType) => {
 | 
			
		||||
  wsCache.set(RefreshTokenKey, token.refreshToken, { exp: token.expiresTime })
 | 
			
		||||
  wsCache.set(AccessTokenKey, token.accessToken)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除token
 | 
			
		||||
export const removeToken = () => {
 | 
			
		||||
  wsCache.delete(AccessTokenKey)
 | 
			
		||||
  wsCache.delete(RefreshTokenKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 格式化token(jwt格式) */
 | 
			
		||||
export const formatToken = (token: string): string => {
 | 
			
		||||
  return 'Bearer ' + token
 | 
			
		||||
}
 | 
			
		||||
// ========== 账号相关 ==========
 | 
			
		||||
 | 
			
		||||
const LoginFormKey = 'LOGINFORM'
 | 
			
		||||
 | 
			
		||||
export type LoginFormType = {
 | 
			
		||||
  tenantName: string
 | 
			
		||||
  username: string
 | 
			
		||||
  password: string
 | 
			
		||||
  rememberMe: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getLoginForm = () => {
 | 
			
		||||
  const loginForm: LoginFormType = wsCache.get(LoginFormKey)
 | 
			
		||||
  if (loginForm) {
 | 
			
		||||
    loginForm.password = decrypt(loginForm.password) as string
 | 
			
		||||
  }
 | 
			
		||||
  return loginForm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setLoginForm = (loginForm: LoginFormType) => {
 | 
			
		||||
  loginForm.password = encrypt(loginForm.password) as string
 | 
			
		||||
  wsCache.set(LoginFormKey, loginForm, { exp: 30 * 24 * 60 * 60 })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const removeLoginForm = () => {
 | 
			
		||||
  wsCache.delete(LoginFormKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ========== 租户相关 ==========
 | 
			
		||||
 | 
			
		||||
const TenantIdKey = 'TENANT_ID'
 | 
			
		||||
const TenantNameKey = 'TENANT_NAME'
 | 
			
		||||
 | 
			
		||||
export const getTenantName = () => {
 | 
			
		||||
  return wsCache.get(TenantNameKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setTenantName = (username: string) => {
 | 
			
		||||
  wsCache.set(TenantNameKey, username, { exp: 30 * 24 * 60 * 60 })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const removeTenantName = () => {
 | 
			
		||||
  wsCache.delete(TenantNameKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getTenantId = () => {
 | 
			
		||||
  return wsCache.get(TenantIdKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setTenantId = (username: string) => {
 | 
			
		||||
  wsCache.set(TenantIdKey, username)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const removeTenantId = () => {
 | 
			
		||||
  wsCache.delete(TenantIdKey)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										153
									
								
								src/utils/color.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/utils/color.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,153 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 判断是否 十六进制颜色值.
 | 
			
		||||
 * 输入形式可为 #fff000 #f00
 | 
			
		||||
 *
 | 
			
		||||
 * @param   String  color   十六进制颜色值
 | 
			
		||||
 * @return  Boolean
 | 
			
		||||
 */
 | 
			
		||||
export const isHexColor = (color: string) => {
 | 
			
		||||
  const reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-f]{6})$/
 | 
			
		||||
  return reg.test(color)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * RGB 颜色值转换为 十六进制颜色值.
 | 
			
		||||
 * r, g, 和 b 需要在 [0, 255] 范围内
 | 
			
		||||
 *
 | 
			
		||||
 * @return  String          类似#ff00ff
 | 
			
		||||
 * @param r
 | 
			
		||||
 * @param g
 | 
			
		||||
 * @param b
 | 
			
		||||
 */
 | 
			
		||||
export const rgbToHex = (r: number, g: number, b: number) => {
 | 
			
		||||
  // tslint:disable-next-line:no-bitwise
 | 
			
		||||
  const hex = ((r << 16) | (g << 8) | b).toString(16)
 | 
			
		||||
  return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Transform a HEX color to its RGB representation
 | 
			
		||||
 * @param {string} hex The color to transform
 | 
			
		||||
 * @returns The RGB representation of the passed color
 | 
			
		||||
 */
 | 
			
		||||
export const hexToRGB = (hex: string, opacity?: number) => {
 | 
			
		||||
  let sHex = hex.toLowerCase()
 | 
			
		||||
  if (isHexColor(hex)) {
 | 
			
		||||
    if (sHex.length === 4) {
 | 
			
		||||
      let sColorNew = '#'
 | 
			
		||||
      for (let i = 1; i < 4; i += 1) {
 | 
			
		||||
        sColorNew += sHex.slice(i, i + 1).concat(sHex.slice(i, i + 1))
 | 
			
		||||
      }
 | 
			
		||||
      sHex = sColorNew
 | 
			
		||||
    }
 | 
			
		||||
    const sColorChange: number[] = []
 | 
			
		||||
    for (let i = 1; i < 7; i += 2) {
 | 
			
		||||
      sColorChange.push(parseInt('0x' + sHex.slice(i, i + 2)))
 | 
			
		||||
    }
 | 
			
		||||
    return opacity
 | 
			
		||||
      ? 'RGBA(' + sColorChange.join(',') + ',' + opacity + ')'
 | 
			
		||||
      : 'RGB(' + sColorChange.join(',') + ')'
 | 
			
		||||
  }
 | 
			
		||||
  return sHex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const colorIsDark = (color: string) => {
 | 
			
		||||
  if (!isHexColor(color)) return
 | 
			
		||||
  const [r, g, b] = hexToRGB(color)
 | 
			
		||||
    .replace(/(?:\(|\)|rgb|RGB)*/g, '')
 | 
			
		||||
    .split(',')
 | 
			
		||||
    .map((item) => Number(item))
 | 
			
		||||
  return r * 0.299 + g * 0.578 + b * 0.114 < 192
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Darkens a HEX color given the passed percentage
 | 
			
		||||
 * @param {string} color The color to process
 | 
			
		||||
 * @param {number} amount The amount to change the color by
 | 
			
		||||
 * @returns {string} The HEX representation of the processed color
 | 
			
		||||
 */
 | 
			
		||||
export const darken = (color: string, amount: number) => {
 | 
			
		||||
  color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color
 | 
			
		||||
  amount = Math.trunc((255 * amount) / 100)
 | 
			
		||||
  return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight(
 | 
			
		||||
    color.substring(2, 4),
 | 
			
		||||
    amount
 | 
			
		||||
  )}${subtractLight(color.substring(4, 6), amount)}`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Lightens a 6 char HEX color according to the passed percentage
 | 
			
		||||
 * @param {string} color The color to change
 | 
			
		||||
 * @param {number} amount The amount to change the color by
 | 
			
		||||
 * @returns {string} The processed color represented as HEX
 | 
			
		||||
 */
 | 
			
		||||
export const lighten = (color: string, amount: number) => {
 | 
			
		||||
  color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color
 | 
			
		||||
  amount = Math.trunc((255 * amount) / 100)
 | 
			
		||||
  return `#${addLight(color.substring(0, 2), amount)}${addLight(
 | 
			
		||||
    color.substring(2, 4),
 | 
			
		||||
    amount
 | 
			
		||||
  )}${addLight(color.substring(4, 6), amount)}`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Suma el porcentaje indicado a un color (RR, GG o BB) hexadecimal para aclararlo */
 | 
			
		||||
/**
 | 
			
		||||
 * Sums the passed percentage to the R, G or B of a HEX color
 | 
			
		||||
 * @param {string} color The color to change
 | 
			
		||||
 * @param {number} amount The amount to change the color by
 | 
			
		||||
 * @returns {string} The processed part of the color
 | 
			
		||||
 */
 | 
			
		||||
const addLight = (color: string, amount: number) => {
 | 
			
		||||
  const cc = parseInt(color, 16) + amount
 | 
			
		||||
  const c = cc > 255 ? 255 : cc
 | 
			
		||||
  return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculates luminance of an rgb color
 | 
			
		||||
 * @param {number} r red
 | 
			
		||||
 * @param {number} g green
 | 
			
		||||
 * @param {number} b blue
 | 
			
		||||
 */
 | 
			
		||||
const luminanace = (r: number, g: number, b: number) => {
 | 
			
		||||
  const a = [r, g, b].map((v) => {
 | 
			
		||||
    v /= 255
 | 
			
		||||
    return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)
 | 
			
		||||
  })
 | 
			
		||||
  return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculates contrast between two rgb colors
 | 
			
		||||
 * @param {string} rgb1 rgb color 1
 | 
			
		||||
 * @param {string} rgb2 rgb color 2
 | 
			
		||||
 */
 | 
			
		||||
const contrast = (rgb1: string[], rgb2: number[]) => {
 | 
			
		||||
  return (
 | 
			
		||||
    (luminanace(~~rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) /
 | 
			
		||||
    (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05)
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines what the best text color is (black or white) based con the contrast with the background
 | 
			
		||||
 * @param hexColor - Last selected color by the user
 | 
			
		||||
 */
 | 
			
		||||
export const calculateBestTextColor = (hexColor: string) => {
 | 
			
		||||
  const rgbColor = hexToRGB(hexColor.substring(1))
 | 
			
		||||
  const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0])
 | 
			
		||||
 | 
			
		||||
  return contrastWithBlack >= 12 ? '#000000' : '#FFFFFF'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Subtracts the indicated percentage to the R, G or B of a HEX color
 | 
			
		||||
 * @param {string} color The color to change
 | 
			
		||||
 * @param {number} amount The amount to change the color by
 | 
			
		||||
 * @returns {string} The processed part of the color
 | 
			
		||||
 */
 | 
			
		||||
const subtractLight = (color: string, amount: number) => {
 | 
			
		||||
  const cc = parseInt(color, 16) - amount
 | 
			
		||||
  const c = cc < 0 ? 0 : cc
 | 
			
		||||
  return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										218
									
								
								src/utils/constants.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/utils/constants.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,218 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Created by 芋道源码
 | 
			
		||||
 *
 | 
			
		||||
 * 枚举类
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// 全局通用状态枚举
 | 
			
		||||
export const CommonStatusEnum = {
 | 
			
		||||
  ENABLE: 0, // 开启
 | 
			
		||||
  DISABLE: 1 // 禁用
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 菜单的类型枚举
 | 
			
		||||
 */
 | 
			
		||||
export const SystemMenuTypeEnum = {
 | 
			
		||||
  DIR: 1, // 目录
 | 
			
		||||
  MENU: 2, // 菜单
 | 
			
		||||
  BUTTON: 3 // 按钮
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 角色的类型枚举
 | 
			
		||||
 */
 | 
			
		||||
export const SystemRoleTypeEnum = {
 | 
			
		||||
  SYSTEM: 1, // 内置角色
 | 
			
		||||
  CUSTOM: 2 // 自定义角色
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 数据权限的范围枚举
 | 
			
		||||
 */
 | 
			
		||||
export const SystemDataScopeEnum = {
 | 
			
		||||
  ALL: 1, // 全部数据权限
 | 
			
		||||
  DEPT_CUSTOM: 2, // 指定部门数据权限
 | 
			
		||||
  DEPT_ONLY: 3, // 部门数据权限
 | 
			
		||||
  DEPT_AND_CHILD: 4, // 部门及以下数据权限
 | 
			
		||||
  DEPT_SELF: 5 // 仅本人数据权限
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 代码生成模板类型
 | 
			
		||||
 */
 | 
			
		||||
export const InfraCodegenTemplateTypeEnum = {
 | 
			
		||||
  CRUD: 1, // 基础 CRUD
 | 
			
		||||
  TREE: 2, // 树形 CRUD
 | 
			
		||||
  SUB: 3 // 主子表 CRUD
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 任务状态的枚举
 | 
			
		||||
 */
 | 
			
		||||
export const InfraJobStatusEnum = {
 | 
			
		||||
  INIT: 0, // 初始化中
 | 
			
		||||
  NORMAL: 1, // 运行中
 | 
			
		||||
  STOP: 2 // 暂停运行
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * API 异常数据的处理状态
 | 
			
		||||
 */
 | 
			
		||||
export const InfraApiErrorLogProcessStatusEnum = {
 | 
			
		||||
  INIT: 0, // 未处理
 | 
			
		||||
  DONE: 1, // 已处理
 | 
			
		||||
  IGNORE: 2 // 已忽略
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户的社交平台的类型枚举
 | 
			
		||||
 */
 | 
			
		||||
export const SystemUserSocialTypeEnum = {
 | 
			
		||||
  DINGTALK: {
 | 
			
		||||
    title: '钉钉',
 | 
			
		||||
    type: 20,
 | 
			
		||||
    source: 'dingtalk',
 | 
			
		||||
    img: 'https://s1.ax1x.com/2022/05/22/OzMDRs.png'
 | 
			
		||||
  },
 | 
			
		||||
  WECHAT_ENTERPRISE: {
 | 
			
		||||
    title: '企业微信',
 | 
			
		||||
    type: 30,
 | 
			
		||||
    source: 'wechat_enterprise',
 | 
			
		||||
    img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 支付渠道枚举
 | 
			
		||||
 */
 | 
			
		||||
export const PayChannelEnum = {
 | 
			
		||||
  WX_PUB: {
 | 
			
		||||
    code: 'wx_pub',
 | 
			
		||||
    name: '微信 JSAPI 支付'
 | 
			
		||||
  },
 | 
			
		||||
  WX_LITE: {
 | 
			
		||||
    code: 'wx_lite',
 | 
			
		||||
    name: '微信小程序支付'
 | 
			
		||||
  },
 | 
			
		||||
  WX_APP: {
 | 
			
		||||
    code: 'wx_app',
 | 
			
		||||
    name: '微信 APP 支付'
 | 
			
		||||
  },
 | 
			
		||||
  ALIPAY_PC: {
 | 
			
		||||
    code: 'alipay_pc',
 | 
			
		||||
    name: '支付宝 PC 网站支付'
 | 
			
		||||
  },
 | 
			
		||||
  ALIPAY_WAP: {
 | 
			
		||||
    code: 'alipay_wap',
 | 
			
		||||
    name: '支付宝 WAP 网站支付'
 | 
			
		||||
  },
 | 
			
		||||
  ALIPAY_APP: {
 | 
			
		||||
    code: 'alipay_app',
 | 
			
		||||
    name: '支付宝 APP 支付'
 | 
			
		||||
  },
 | 
			
		||||
  ALIPAY_QR: {
 | 
			
		||||
    code: 'alipay_qr',
 | 
			
		||||
    name: '支付宝扫码支付'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 支付类型枚举
 | 
			
		||||
 */
 | 
			
		||||
export const PayType = {
 | 
			
		||||
  WECHAT: 'WECHAT',
 | 
			
		||||
  ALIPAY: 'ALIPAY'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 支付订单状态枚举
 | 
			
		||||
 */
 | 
			
		||||
export const PayOrderStatusEnum = {
 | 
			
		||||
  WAITING: {
 | 
			
		||||
    status: 0,
 | 
			
		||||
    name: '未支付'
 | 
			
		||||
  },
 | 
			
		||||
  SUCCESS: {
 | 
			
		||||
    status: 10,
 | 
			
		||||
    name: '已支付'
 | 
			
		||||
  },
 | 
			
		||||
  CLOSED: {
 | 
			
		||||
    status: 20,
 | 
			
		||||
    name: '未支付'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 支付订单回调状态枚举
 | 
			
		||||
 */
 | 
			
		||||
export const PayOrderNotifyStatusEnum = {
 | 
			
		||||
  NO: {
 | 
			
		||||
    status: 0,
 | 
			
		||||
    name: '未通知'
 | 
			
		||||
  },
 | 
			
		||||
  SUCCESS: {
 | 
			
		||||
    status: 10,
 | 
			
		||||
    name: '通知成功'
 | 
			
		||||
  },
 | 
			
		||||
  FAILURE: {
 | 
			
		||||
    status: 20,
 | 
			
		||||
    name: '通知失败'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 支付订单退款状态枚举
 | 
			
		||||
 */
 | 
			
		||||
export const PayOrderRefundStatusEnum = {
 | 
			
		||||
  NO: {
 | 
			
		||||
    status: 0,
 | 
			
		||||
    name: '未退款'
 | 
			
		||||
  },
 | 
			
		||||
  SOME: {
 | 
			
		||||
    status: 10,
 | 
			
		||||
    name: '部分退款'
 | 
			
		||||
  },
 | 
			
		||||
  ALL: {
 | 
			
		||||
    status: 20,
 | 
			
		||||
    name: '全部退款'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 支付退款订单状态枚举
 | 
			
		||||
 */
 | 
			
		||||
export const PayRefundStatusEnum = {
 | 
			
		||||
  CREATE: {
 | 
			
		||||
    status: 0,
 | 
			
		||||
    name: '退款订单生成'
 | 
			
		||||
  },
 | 
			
		||||
  SUCCESS: {
 | 
			
		||||
    status: 1,
 | 
			
		||||
    name: '退款成功'
 | 
			
		||||
  },
 | 
			
		||||
  FAILURE: {
 | 
			
		||||
    status: 2,
 | 
			
		||||
    name: '退款失败'
 | 
			
		||||
  },
 | 
			
		||||
  PROCESSING_NOTIFY: {
 | 
			
		||||
    status: 3,
 | 
			
		||||
    name: '退款中,渠道通知结果'
 | 
			
		||||
  },
 | 
			
		||||
  PROCESSING_QUERY: {
 | 
			
		||||
    status: 4,
 | 
			
		||||
    name: '退款中,系统查询结果'
 | 
			
		||||
  },
 | 
			
		||||
  UNKNOWN_RETRY: {
 | 
			
		||||
    status: 5,
 | 
			
		||||
    name: '状态未知,请重试'
 | 
			
		||||
  },
 | 
			
		||||
  UNKNOWN_QUERY: {
 | 
			
		||||
    status: 6,
 | 
			
		||||
    name: '状态未知,系统查询结果'
 | 
			
		||||
  },
 | 
			
		||||
  CLOSE: {
 | 
			
		||||
    status: 99,
 | 
			
		||||
    name: '退款关闭'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										127
									
								
								src/utils/dict.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/utils/dict.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 数据字典工具类
 | 
			
		||||
 */
 | 
			
		||||
import { useDictStoreWithOut } from '@/store/modules/dict'
 | 
			
		||||
import { ElementPlusInfoType } from '@/types/elementPlus'
 | 
			
		||||
 | 
			
		||||
const dictStore = useDictStoreWithOut()
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取 dictType 对应的数据字典数组
 | 
			
		||||
 *
 | 
			
		||||
 * @param dictType 数据类型
 | 
			
		||||
 * @returns {*|Array} 数据字典数组
 | 
			
		||||
 */
 | 
			
		||||
export interface DictDataType {
 | 
			
		||||
  dictType: string
 | 
			
		||||
  label: string
 | 
			
		||||
  value: string | number | boolean
 | 
			
		||||
  colorType: ElementPlusInfoType | ''
 | 
			
		||||
  cssClass: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getDictOptions = (dictType: string) => {
 | 
			
		||||
  return dictStore.getDictByType(dictType)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getIntDictOptions = (dictType: string) => {
 | 
			
		||||
  const dictOption: DictDataType[] = []
 | 
			
		||||
  const dictOptions: DictDataType[] = getDictOptions(dictType)
 | 
			
		||||
  dictOptions.forEach((dict: DictDataType) => {
 | 
			
		||||
    dictOption.push({
 | 
			
		||||
      ...dict,
 | 
			
		||||
      value: parseInt(dict.value + '')
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return dictOption
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getStrDictOptions = (dictType: string) => {
 | 
			
		||||
  const dictOption: DictDataType[] = []
 | 
			
		||||
  const dictOptions: DictDataType[] = getDictOptions(dictType)
 | 
			
		||||
  dictOptions.forEach((dict: DictDataType) => {
 | 
			
		||||
    dictOption.push({
 | 
			
		||||
      ...dict,
 | 
			
		||||
      value: dict.value + ''
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
  return dictOption
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getBoolDictOptions = (dictType: string) => {
 | 
			
		||||
  const dictOption: DictDataType[] = []
 | 
			
		||||
  const dictOptions: DictDataType[] = getDictOptions(dictType)
 | 
			
		||||
  dictOptions.forEach((dict: DictDataType) => {
 | 
			
		||||
    dictOption.push({
 | 
			
		||||
      ...dict,
 | 
			
		||||
      value: dict.value + '' === 'true' ? true : false
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
  return dictOption
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getDictObj = (dictType: string, value: any) => {
 | 
			
		||||
  const dictOptions: DictDataType[] = getDictOptions(dictType)
 | 
			
		||||
  dictOptions.forEach((dict: DictDataType) => {
 | 
			
		||||
    if (dict.value === value.toString()) {
 | 
			
		||||
      return dict
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum DICT_TYPE {
 | 
			
		||||
  USER_TYPE = 'user_type',
 | 
			
		||||
  COMMON_STATUS = 'common_status',
 | 
			
		||||
  SYSTEM_TENANT_PACKAGE_ID = 'system_tenant_package_id',
 | 
			
		||||
 | 
			
		||||
  // ========== SYSTEM 模块 ==========
 | 
			
		||||
  SYSTEM_USER_SEX = 'system_user_sex',
 | 
			
		||||
  SYSTEM_MENU_TYPE = 'system_menu_type',
 | 
			
		||||
  SYSTEM_ROLE_TYPE = 'system_role_type',
 | 
			
		||||
  SYSTEM_DATA_SCOPE = 'system_data_scope',
 | 
			
		||||
  SYSTEM_NOTICE_TYPE = 'system_notice_type',
 | 
			
		||||
  SYSTEM_OPERATE_TYPE = 'system_operate_type',
 | 
			
		||||
  SYSTEM_LOGIN_TYPE = 'system_login_type',
 | 
			
		||||
  SYSTEM_LOGIN_RESULT = 'system_login_result',
 | 
			
		||||
  SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code',
 | 
			
		||||
  SYSTEM_SMS_TEMPLATE_TYPE = 'system_sms_template_type',
 | 
			
		||||
  SYSTEM_SMS_SEND_STATUS = 'system_sms_send_status',
 | 
			
		||||
  SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status',
 | 
			
		||||
  SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type',
 | 
			
		||||
  SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
 | 
			
		||||
  SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
 | 
			
		||||
  SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type',
 | 
			
		||||
 | 
			
		||||
  // ========== INFRA 模块 ==========
 | 
			
		||||
  INFRA_BOOLEAN_STRING = 'infra_boolean_string',
 | 
			
		||||
  INFRA_REDIS_TIMEOUT_TYPE = 'infra_redis_timeout_type',
 | 
			
		||||
  INFRA_JOB_STATUS = 'infra_job_status',
 | 
			
		||||
  INFRA_JOB_LOG_STATUS = 'infra_job_log_status',
 | 
			
		||||
  INFRA_API_ERROR_LOG_PROCESS_STATUS = 'infra_api_error_log_process_status',
 | 
			
		||||
  INFRA_CONFIG_TYPE = 'infra_config_type',
 | 
			
		||||
  INFRA_CODEGEN_TEMPLATE_TYPE = 'infra_codegen_template_type',
 | 
			
		||||
  INFRA_CODEGEN_SCENE = 'infra_codegen_scene',
 | 
			
		||||
  INFRA_FILE_STORAGE = 'infra_file_storage',
 | 
			
		||||
 | 
			
		||||
  // ========== BPM 模块 ==========
 | 
			
		||||
  BPM_MODEL_CATEGORY = 'bpm_model_category',
 | 
			
		||||
  BPM_MODEL_FORM_TYPE = 'bpm_model_form_type',
 | 
			
		||||
  BPM_TASK_ASSIGN_RULE_TYPE = 'bpm_task_assign_rule_type',
 | 
			
		||||
  BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
 | 
			
		||||
  BPM_PROCESS_INSTANCE_RESULT = 'bpm_process_instance_result',
 | 
			
		||||
  BPM_TASK_ASSIGN_SCRIPT = 'bpm_task_assign_script',
 | 
			
		||||
  BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type',
 | 
			
		||||
 | 
			
		||||
  // ========== PAY 模块 ==========
 | 
			
		||||
  PAY_CHANNEL_WECHAT_VERSION = 'pay_channel_wechat_version', // 微信渠道版本
 | 
			
		||||
  PAY_CHANNEL_ALIPAY_SIGN_TYPE = 'pay_channel_alipay_sign_type', // 支付渠道支付宝算法类型
 | 
			
		||||
  PAY_CHANNEL_ALIPAY_MODE = 'pay_channel_alipay_mode', // 支付宝公钥类型
 | 
			
		||||
  PAY_CHANNEL_ALIPAY_SERVER_TYPE = 'pay_channel_alipay_server_type', // 支付宝网关地址
 | 
			
		||||
  PAY_CHANNEL_CODE_TYPE = 'pay_channel_code_type', // 支付渠道编码类型
 | 
			
		||||
  PAY_ORDER_NOTIFY_STATUS = 'pay_order_notify_status', // 商户支付订单回调状态
 | 
			
		||||
  PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
 | 
			
		||||
  PAY_ORDER_REFUND_STATUS = 'pay_order_refund_status', // 商户支付订单退款状态
 | 
			
		||||
  PAY_REFUND_ORDER_STATUS = 'pay_refund_order_status', // 退款订单状态
 | 
			
		||||
  PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type' // 退款订单类别
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										289
									
								
								src/utils/domUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								src/utils/domUtils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,289 @@
 | 
			
		||||
import { isServer } from './is'
 | 
			
		||||
const ieVersion = isServer ? 0 : Number((document as any).documentMode)
 | 
			
		||||
const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g
 | 
			
		||||
const MOZ_HACK_REGEXP = /^moz([A-Z])/
 | 
			
		||||
 | 
			
		||||
export interface ViewportOffsetResult {
 | 
			
		||||
  left: number
 | 
			
		||||
  top: number
 | 
			
		||||
  right: number
 | 
			
		||||
  bottom: number
 | 
			
		||||
  rightIncludeBody: number
 | 
			
		||||
  bottomIncludeBody: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
const trim = function (string: string) {
 | 
			
		||||
  return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
const camelCase = function (name: string) {
 | 
			
		||||
  return name
 | 
			
		||||
    .replace(SPECIAL_CHARS_REGEXP, function (_, __, letter, offset) {
 | 
			
		||||
      return offset ? letter.toUpperCase() : letter
 | 
			
		||||
    })
 | 
			
		||||
    .replace(MOZ_HACK_REGEXP, 'Moz$1')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export function hasClass(el: Element, cls: string) {
 | 
			
		||||
  if (!el || !cls) return false
 | 
			
		||||
  if (cls.indexOf(' ') !== -1) {
 | 
			
		||||
    throw new Error('className should not contain space.')
 | 
			
		||||
  }
 | 
			
		||||
  if (el.classList) {
 | 
			
		||||
    return el.classList.contains(cls)
 | 
			
		||||
  } else {
 | 
			
		||||
    return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export function addClass(el: Element, cls: string) {
 | 
			
		||||
  if (!el) return
 | 
			
		||||
  let curClass = el.className
 | 
			
		||||
  const classes = (cls || '').split(' ')
 | 
			
		||||
 | 
			
		||||
  for (let i = 0, j = classes.length; i < j; i++) {
 | 
			
		||||
    const clsName = classes[i]
 | 
			
		||||
    if (!clsName) continue
 | 
			
		||||
 | 
			
		||||
    if (el.classList) {
 | 
			
		||||
      el.classList.add(clsName)
 | 
			
		||||
    } else if (!hasClass(el, clsName)) {
 | 
			
		||||
      curClass += ' ' + clsName
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!el.classList) {
 | 
			
		||||
    el.className = curClass
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export function removeClass(el: Element, cls: string) {
 | 
			
		||||
  if (!el || !cls) return
 | 
			
		||||
  const classes = cls.split(' ')
 | 
			
		||||
  let curClass = ' ' + el.className + ' '
 | 
			
		||||
 | 
			
		||||
  for (let i = 0, j = classes.length; i < j; i++) {
 | 
			
		||||
    const clsName = classes[i]
 | 
			
		||||
    if (!clsName) continue
 | 
			
		||||
 | 
			
		||||
    if (el.classList) {
 | 
			
		||||
      el.classList.remove(clsName)
 | 
			
		||||
    } else if (hasClass(el, clsName)) {
 | 
			
		||||
      curClass = curClass.replace(' ' + clsName + ' ', ' ')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!el.classList) {
 | 
			
		||||
    el.className = trim(curClass)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getBoundingClientRect(element: Element): DOMRect | number {
 | 
			
		||||
  if (!element || !element.getBoundingClientRect) {
 | 
			
		||||
    return 0
 | 
			
		||||
  }
 | 
			
		||||
  return element.getBoundingClientRect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取当前元素的left、top偏移
 | 
			
		||||
 *   left:元素最左侧距离文档左侧的距离
 | 
			
		||||
 *   top:元素最顶端距离文档顶端的距离
 | 
			
		||||
 *   right:元素最右侧距离文档右侧的距离
 | 
			
		||||
 *   bottom:元素最底端距离文档底端的距离
 | 
			
		||||
 *   rightIncludeBody:元素最左侧距离文档右侧的距离
 | 
			
		||||
 *   bottomIncludeBody:元素最底端距离文档最底部的距离
 | 
			
		||||
 *
 | 
			
		||||
 * @description:
 | 
			
		||||
 */
 | 
			
		||||
export function getViewportOffset(element: Element): ViewportOffsetResult {
 | 
			
		||||
  const doc = document.documentElement
 | 
			
		||||
 | 
			
		||||
  const docScrollLeft = doc.scrollLeft
 | 
			
		||||
  const docScrollTop = doc.scrollTop
 | 
			
		||||
  const docClientLeft = doc.clientLeft
 | 
			
		||||
  const docClientTop = doc.clientTop
 | 
			
		||||
 | 
			
		||||
  const pageXOffset = window.pageXOffset
 | 
			
		||||
  const pageYOffset = window.pageYOffset
 | 
			
		||||
 | 
			
		||||
  const box = getBoundingClientRect(element)
 | 
			
		||||
 | 
			
		||||
  const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect
 | 
			
		||||
 | 
			
		||||
  const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0)
 | 
			
		||||
  const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0)
 | 
			
		||||
  const offsetLeft = retLeft + pageXOffset
 | 
			
		||||
  const offsetTop = rectTop + pageYOffset
 | 
			
		||||
 | 
			
		||||
  const left = offsetLeft - scrollLeft
 | 
			
		||||
  const top = offsetTop - scrollTop
 | 
			
		||||
 | 
			
		||||
  const clientWidth = window.document.documentElement.clientWidth
 | 
			
		||||
  const clientHeight = window.document.documentElement.clientHeight
 | 
			
		||||
  return {
 | 
			
		||||
    left: left,
 | 
			
		||||
    top: top,
 | 
			
		||||
    right: clientWidth - rectWidth - left,
 | 
			
		||||
    bottom: clientHeight - rectHeight - top,
 | 
			
		||||
    rightIncludeBody: clientWidth - left,
 | 
			
		||||
    bottomIncludeBody: clientHeight - top
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export const on = function (
 | 
			
		||||
  element: HTMLElement | Document | Window,
 | 
			
		||||
  event: string,
 | 
			
		||||
  handler: EventListenerOrEventListenerObject
 | 
			
		||||
): void {
 | 
			
		||||
  if (element && event && handler) {
 | 
			
		||||
    element.addEventListener(event, handler, false)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export const off = function (
 | 
			
		||||
  element: HTMLElement | Document | Window,
 | 
			
		||||
  event: string,
 | 
			
		||||
  handler: any
 | 
			
		||||
): void {
 | 
			
		||||
  if (element && event && handler) {
 | 
			
		||||
    element.removeEventListener(event, handler, false)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export const once = function (el: HTMLElement, event: string, fn: EventListener): void {
 | 
			
		||||
  const listener = function (this: any, ...args: unknown[]) {
 | 
			
		||||
    if (fn) {
 | 
			
		||||
      // @ts-ignore
 | 
			
		||||
      fn.apply(this, args)
 | 
			
		||||
    }
 | 
			
		||||
    off(el, event, listener)
 | 
			
		||||
  }
 | 
			
		||||
  on(el, event, listener)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export const getStyle =
 | 
			
		||||
  ieVersion < 9
 | 
			
		||||
    ? function (element: Element | any, styleName: string) {
 | 
			
		||||
        if (isServer) return
 | 
			
		||||
        if (!element || !styleName) return null
 | 
			
		||||
        styleName = camelCase(styleName)
 | 
			
		||||
        if (styleName === 'float') {
 | 
			
		||||
          styleName = 'styleFloat'
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
          switch (styleName) {
 | 
			
		||||
            case 'opacity':
 | 
			
		||||
              try {
 | 
			
		||||
                return element.filters.item('alpha').opacity / 100
 | 
			
		||||
              } catch (e) {
 | 
			
		||||
                return 1.0
 | 
			
		||||
              }
 | 
			
		||||
            default:
 | 
			
		||||
              return element.style[styleName] || element.currentStyle
 | 
			
		||||
                ? element.currentStyle[styleName]
 | 
			
		||||
                : null
 | 
			
		||||
          }
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          return element.style[styleName]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    : function (element: Element | any, styleName: string) {
 | 
			
		||||
        if (isServer) return
 | 
			
		||||
        if (!element || !styleName) return null
 | 
			
		||||
        styleName = camelCase(styleName)
 | 
			
		||||
        if (styleName === 'float') {
 | 
			
		||||
          styleName = 'cssFloat'
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
          const computed = (document as any).defaultView.getComputedStyle(element, '')
 | 
			
		||||
          return element.style[styleName] || computed ? computed[styleName] : null
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          return element.style[styleName]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export function setStyle(element: Element | any, styleName: any, value: any) {
 | 
			
		||||
  if (!element || !styleName) return
 | 
			
		||||
 | 
			
		||||
  if (typeof styleName === 'object') {
 | 
			
		||||
    for (const prop in styleName) {
 | 
			
		||||
      if (Object.prototype.hasOwnProperty.call(styleName, prop)) {
 | 
			
		||||
        setStyle(element, prop, styleName[prop])
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    styleName = camelCase(styleName)
 | 
			
		||||
    if (styleName === 'opacity' && ieVersion < 9) {
 | 
			
		||||
      element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')'
 | 
			
		||||
    } else {
 | 
			
		||||
      element.style[styleName] = value
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export const isScroll = (el: Element, vertical: any) => {
 | 
			
		||||
  if (isServer) return
 | 
			
		||||
 | 
			
		||||
  const determinedDirection = vertical !== null || vertical !== undefined
 | 
			
		||||
  const overflow = determinedDirection
 | 
			
		||||
    ? vertical
 | 
			
		||||
      ? getStyle(el, 'overflow-y')
 | 
			
		||||
      : getStyle(el, 'overflow-x')
 | 
			
		||||
    : getStyle(el, 'overflow')
 | 
			
		||||
 | 
			
		||||
  return overflow.match(/(scroll|auto)/)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export const getScrollContainer = (el: Element, vertical?: any) => {
 | 
			
		||||
  if (isServer) return
 | 
			
		||||
 | 
			
		||||
  let parent: any = el
 | 
			
		||||
  while (parent) {
 | 
			
		||||
    if ([window, document, document.documentElement].includes(parent)) {
 | 
			
		||||
      return window
 | 
			
		||||
    }
 | 
			
		||||
    if (isScroll(parent, vertical)) {
 | 
			
		||||
      return parent
 | 
			
		||||
    }
 | 
			
		||||
    parent = parent.parentNode
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return parent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export const isInContainer = (el: Element, container: any) => {
 | 
			
		||||
  if (isServer || !el || !container) return false
 | 
			
		||||
 | 
			
		||||
  const elRect = el.getBoundingClientRect()
 | 
			
		||||
  let containerRect
 | 
			
		||||
 | 
			
		||||
  if ([window, document, document.documentElement, null, undefined].includes(container)) {
 | 
			
		||||
    containerRect = {
 | 
			
		||||
      top: 0,
 | 
			
		||||
      right: window.innerWidth,
 | 
			
		||||
      bottom: window.innerHeight,
 | 
			
		||||
      left: 0
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    containerRect = container.getBoundingClientRect()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    elRect.top < containerRect.bottom &&
 | 
			
		||||
    elRect.bottom > containerRect.top &&
 | 
			
		||||
    elRect.right > containerRect.left &&
 | 
			
		||||
    elRect.left < containerRect.right
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/utils/download.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/utils/download.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
const download0 = (data: Blob, fileName: string, mineType: string) => {
 | 
			
		||||
  // 创建 blob
 | 
			
		||||
  const blob = new Blob([data], { type: mineType })
 | 
			
		||||
  // 创建 href 超链接,点击进行下载
 | 
			
		||||
  window.URL = window.URL || window.webkitURL
 | 
			
		||||
  const href = URL.createObjectURL(blob)
 | 
			
		||||
  const downA = document.createElement('a')
 | 
			
		||||
  downA.href = href
 | 
			
		||||
  downA.download = fileName
 | 
			
		||||
  downA.click()
 | 
			
		||||
  // 销毁超连接
 | 
			
		||||
  window.URL.revokeObjectURL(href)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const download = {
 | 
			
		||||
  // 下载 Excel 方法
 | 
			
		||||
  excel: (data: Blob, fileName: string) => {
 | 
			
		||||
    download0(data, fileName, 'application/vnd.ms-excel')
 | 
			
		||||
  },
 | 
			
		||||
  // 下载 Word 方法
 | 
			
		||||
  word: (data: Blob, fileName: string) => {
 | 
			
		||||
    download0(data, fileName, 'application/msword')
 | 
			
		||||
  },
 | 
			
		||||
  // 下载 Zip 方法
 | 
			
		||||
  zip: (data: Blob, fileName: string) => {
 | 
			
		||||
    download0(data, fileName, 'application/zip')
 | 
			
		||||
  },
 | 
			
		||||
  // 下载 Html 方法
 | 
			
		||||
  html: (data: Blob, fileName: string) => {
 | 
			
		||||
    download0(data, fileName, 'text/html')
 | 
			
		||||
  },
 | 
			
		||||
  // 下载 Markdown 方法
 | 
			
		||||
  markdown: (data: Blob, fileName: string) => {
 | 
			
		||||
    download0(data, fileName, 'text/markdown')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default download
 | 
			
		||||
							
								
								
									
										8
									
								
								src/utils/env.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/utils/env.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
export const isDevMode = () => {
 | 
			
		||||
  const dev = import.meta.env.VITE_DEV
 | 
			
		||||
  if (dev && dev === true) {
 | 
			
		||||
    return true
 | 
			
		||||
  } else {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										157
									
								
								src/utils/filt.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/utils/filt.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
			
		||||
export const openWindow = (
 | 
			
		||||
  url: string,
 | 
			
		||||
  opt?: {
 | 
			
		||||
    target?: '_self' | '_blank' | string
 | 
			
		||||
    noopener?: boolean
 | 
			
		||||
    noreferrer?: boolean
 | 
			
		||||
  }
 | 
			
		||||
) => {
 | 
			
		||||
  const { target = '__blank', noopener = true, noreferrer = true } = opt || {}
 | 
			
		||||
  const feature: string[] = []
 | 
			
		||||
 | 
			
		||||
  noopener && feature.push('noopener=yes')
 | 
			
		||||
  noreferrer && feature.push('noreferrer=yes')
 | 
			
		||||
 | 
			
		||||
  window.open(url, target, feature.join(','))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @description: base64 to blob
 | 
			
		||||
 */
 | 
			
		||||
export const dataURLtoBlob = (base64Buf: string): Blob => {
 | 
			
		||||
  const arr = base64Buf.split(',')
 | 
			
		||||
  const typeItem = arr[0]
 | 
			
		||||
  const mime = typeItem.match(/:(.*?);/)![1]
 | 
			
		||||
  const bstr = window.atob(arr[1])
 | 
			
		||||
  let n = bstr.length
 | 
			
		||||
  const u8arr = new Uint8Array(n)
 | 
			
		||||
  while (n--) {
 | 
			
		||||
    u8arr[n] = bstr.charCodeAt(n)
 | 
			
		||||
  }
 | 
			
		||||
  return new Blob([u8arr], { type: mime })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * img url to base64
 | 
			
		||||
 * @param url
 | 
			
		||||
 */
 | 
			
		||||
export const urlToBase64 = (url: string, mineType?: string): Promise<string> => {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>
 | 
			
		||||
    const ctx = canvas!.getContext('2d')
 | 
			
		||||
 | 
			
		||||
    const img = new Image()
 | 
			
		||||
    img.crossOrigin = ''
 | 
			
		||||
    img.onload = function () {
 | 
			
		||||
      if (!canvas || !ctx) {
 | 
			
		||||
        return reject()
 | 
			
		||||
      }
 | 
			
		||||
      canvas.height = img.height
 | 
			
		||||
      canvas.width = img.width
 | 
			
		||||
      ctx.drawImage(img, 0, 0)
 | 
			
		||||
      const dataURL = canvas.toDataURL(mineType || 'image/png')
 | 
			
		||||
      canvas = null
 | 
			
		||||
      resolve(dataURL)
 | 
			
		||||
    }
 | 
			
		||||
    img.src = url
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Download online pictures
 | 
			
		||||
 * @param url
 | 
			
		||||
 * @param filename
 | 
			
		||||
 * @param mime
 | 
			
		||||
 * @param bom
 | 
			
		||||
 */
 | 
			
		||||
export const downloadByOnlineUrl = (
 | 
			
		||||
  url: string,
 | 
			
		||||
  filename: string,
 | 
			
		||||
  mime?: string,
 | 
			
		||||
  bom?: BlobPart
 | 
			
		||||
) => {
 | 
			
		||||
  urlToBase64(url).then((base64) => {
 | 
			
		||||
    downloadByBase64(base64, filename, mime, bom)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Download pictures based on base64
 | 
			
		||||
 * @param buf
 | 
			
		||||
 * @param filename
 | 
			
		||||
 * @param mime
 | 
			
		||||
 * @param bom
 | 
			
		||||
 */
 | 
			
		||||
export const downloadByBase64 = (buf: string, filename: string, mime?: string, bom?: BlobPart) => {
 | 
			
		||||
  const base64Buf = dataURLtoBlob(buf)
 | 
			
		||||
  downloadByData(base64Buf, filename, mime, bom)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Download according to the background interface file stream
 | 
			
		||||
 * @param {*} data
 | 
			
		||||
 * @param {*} filename
 | 
			
		||||
 * @param {*} mime
 | 
			
		||||
 * @param {*} bom
 | 
			
		||||
 */
 | 
			
		||||
export const downloadByData = (data: BlobPart, filename: string, mime?: string, bom?: BlobPart) => {
 | 
			
		||||
  const blobData = typeof bom !== 'undefined' ? [bom, data] : [data]
 | 
			
		||||
  const blob = new Blob(blobData, { type: mime || 'application/octet-stream' })
 | 
			
		||||
 | 
			
		||||
  const blobURL = window.URL.createObjectURL(blob)
 | 
			
		||||
  const tempLink = document.createElement('a')
 | 
			
		||||
  tempLink.style.display = 'none'
 | 
			
		||||
  tempLink.href = blobURL
 | 
			
		||||
  tempLink.setAttribute('download', filename)
 | 
			
		||||
  if (typeof tempLink.download === 'undefined') {
 | 
			
		||||
    tempLink.setAttribute('target', '_blank')
 | 
			
		||||
  }
 | 
			
		||||
  document.body.appendChild(tempLink)
 | 
			
		||||
  tempLink.click()
 | 
			
		||||
  document.body.removeChild(tempLink)
 | 
			
		||||
  window.URL.revokeObjectURL(blobURL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Download file according to file address
 | 
			
		||||
 * @param {*} sUrl
 | 
			
		||||
 */
 | 
			
		||||
export const downloadByUrl = ({
 | 
			
		||||
  url,
 | 
			
		||||
  target = '_blank',
 | 
			
		||||
  fileName
 | 
			
		||||
}: {
 | 
			
		||||
  url: string
 | 
			
		||||
  target?: '_self' | '_blank'
 | 
			
		||||
  fileName?: string
 | 
			
		||||
}): boolean => {
 | 
			
		||||
  const isChrome = window.navigator.userAgent.toLowerCase().indexOf('chrome') > -1
 | 
			
		||||
  const isSafari = window.navigator.userAgent.toLowerCase().indexOf('safari') > -1
 | 
			
		||||
 | 
			
		||||
  if (/(iP)/g.test(window.navigator.userAgent)) {
 | 
			
		||||
    console.error('Your browser does not support download!')
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
  if (isChrome || isSafari) {
 | 
			
		||||
    const link = document.createElement('a')
 | 
			
		||||
    link.href = url
 | 
			
		||||
    link.target = target
 | 
			
		||||
 | 
			
		||||
    if (link.download !== undefined) {
 | 
			
		||||
      link.download = fileName || url.substring(url.lastIndexOf('/') + 1, url.length)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (document.createEvent) {
 | 
			
		||||
      const e = document.createEvent('MouseEvents')
 | 
			
		||||
      e.initEvent('click', true, true)
 | 
			
		||||
      link.dispatchEvent(e)
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (url.indexOf('?') === -1) {
 | 
			
		||||
    url += '?download'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  openWindow(url, { target })
 | 
			
		||||
  return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								src/utils/formCreate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/utils/formCreate.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 针对 https://github.com/xaboy/form-create-designer 封装的工具类
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// 编码表单 Conf
 | 
			
		||||
export const encodeConf = (designerRef: object) => {
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  return JSON.stringify(designerRef.value.getOption())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 编码表单 Fields
 | 
			
		||||
export const encodeFields = (designerRef: object) => {
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  const rule = designerRef.value.getRule()
 | 
			
		||||
  const fields: string[] = []
 | 
			
		||||
  rule.forEach((item) => {
 | 
			
		||||
    fields.push(JSON.stringify(item))
 | 
			
		||||
  })
 | 
			
		||||
  return fields
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 解码表单 Fields
 | 
			
		||||
export const decodeFields = (fields: string[]) => {
 | 
			
		||||
  const rule: object[] = []
 | 
			
		||||
  fields.forEach((item) => {
 | 
			
		||||
    rule.push(JSON.parse(item))
 | 
			
		||||
  })
 | 
			
		||||
  return rule
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置表单的 Conf 和 Fields
 | 
			
		||||
export const setConfAndFields = (designerRef: object, conf: string, fields: string) => {
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  designerRef.value.setOption(JSON.parse(conf))
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  designerRef.value.setRule(decodeFields(fields))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置表单的 Conf 和 Fields
 | 
			
		||||
export const setConfAndFields2 = (
 | 
			
		||||
  detailPreview: object,
 | 
			
		||||
  conf: string,
 | 
			
		||||
  fields: string,
 | 
			
		||||
  value?: object
 | 
			
		||||
) => {
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  detailPreview.value.option = JSON.parse(conf)
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  detailPreview.value.rule = decodeFields(fields)
 | 
			
		||||
  if (value) {
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    detailPreview.value.value = value
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								src/utils/formRules.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/utils/formRules.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
// 必填项
 | 
			
		||||
export const required = {
 | 
			
		||||
  required: true,
 | 
			
		||||
  message: t('common.required')
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										176
									
								
								src/utils/formatTime.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/utils/formatTime.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 时间日期转换
 | 
			
		||||
 * @param date 当前时间,new Date() 格式
 | 
			
		||||
 * @param format 需要转换的时间格式字符串
 | 
			
		||||
 * @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd`
 | 
			
		||||
 * @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ"
 | 
			
		||||
 * @description format 星期:"YYYY-mm-dd HH:MM:SS WWW"
 | 
			
		||||
 * @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ"
 | 
			
		||||
 * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
 | 
			
		||||
 * @returns 返回拼接后的时间字符串
 | 
			
		||||
 */
 | 
			
		||||
export function formatDate(date: Date, format: string): string {
 | 
			
		||||
  const we = date.getDay() // 星期
 | 
			
		||||
  const z = getWeek(date) // 周
 | 
			
		||||
  const qut = Math.floor((date.getMonth() + 3) / 3).toString() // 季度
 | 
			
		||||
  const opt: { [key: string]: string } = {
 | 
			
		||||
    'Y+': date.getFullYear().toString(), // 年
 | 
			
		||||
    'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
 | 
			
		||||
    'd+': date.getDate().toString(), // 日
 | 
			
		||||
    'H+': date.getHours().toString(), // 时
 | 
			
		||||
    'M+': date.getMinutes().toString(), // 分
 | 
			
		||||
    'S+': date.getSeconds().toString(), // 秒
 | 
			
		||||
    'q+': qut // 季度
 | 
			
		||||
  }
 | 
			
		||||
  // 中文数字 (星期)
 | 
			
		||||
  const week: { [key: string]: string } = {
 | 
			
		||||
    '0': '日',
 | 
			
		||||
    '1': '一',
 | 
			
		||||
    '2': '二',
 | 
			
		||||
    '3': '三',
 | 
			
		||||
    '4': '四',
 | 
			
		||||
    '5': '五',
 | 
			
		||||
    '6': '六'
 | 
			
		||||
  }
 | 
			
		||||
  // 中文数字(季度)
 | 
			
		||||
  const quarter: { [key: string]: string } = {
 | 
			
		||||
    '1': '一',
 | 
			
		||||
    '2': '二',
 | 
			
		||||
    '3': '三',
 | 
			
		||||
    '4': '四'
 | 
			
		||||
  }
 | 
			
		||||
  if (/(W+)/.test(format))
 | 
			
		||||
    format = format.replace(
 | 
			
		||||
      RegExp.$1,
 | 
			
		||||
      RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]
 | 
			
		||||
    )
 | 
			
		||||
  if (/(Q+)/.test(format))
 | 
			
		||||
    format = format.replace(
 | 
			
		||||
      RegExp.$1,
 | 
			
		||||
      RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]
 | 
			
		||||
    )
 | 
			
		||||
  if (/(Z+)/.test(format))
 | 
			
		||||
    format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + '')
 | 
			
		||||
  for (const k in opt) {
 | 
			
		||||
    const r = new RegExp('(' + k + ')').exec(format)
 | 
			
		||||
    // 若输入的长度不为1,则前面补零
 | 
			
		||||
    if (r)
 | 
			
		||||
      format = format.replace(
 | 
			
		||||
        r[1],
 | 
			
		||||
        RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0')
 | 
			
		||||
      )
 | 
			
		||||
  }
 | 
			
		||||
  return format
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取当前日期是第几周
 | 
			
		||||
 * @param dateTime 当前传入的日期值
 | 
			
		||||
 * @returns 返回第几周数字值
 | 
			
		||||
 */
 | 
			
		||||
export function getWeek(dateTime: Date): number {
 | 
			
		||||
  const temptTime = new Date(dateTime.getTime())
 | 
			
		||||
  // 周几
 | 
			
		||||
  const weekday = temptTime.getDay() || 7
 | 
			
		||||
  // 周1+5天=周六
 | 
			
		||||
  temptTime.setDate(temptTime.getDate() - weekday + 1 + 5)
 | 
			
		||||
  let firstDay = new Date(temptTime.getFullYear(), 0, 1)
 | 
			
		||||
  const dayOfWeek = firstDay.getDay()
 | 
			
		||||
  let spendDay = 1
 | 
			
		||||
  if (dayOfWeek != 0) spendDay = 7 - dayOfWeek + 1
 | 
			
		||||
  firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay)
 | 
			
		||||
  const d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86400000)
 | 
			
		||||
  const result = Math.ceil(d / 7)
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前`
 | 
			
		||||
 * @param param 当前时间,new Date() 格式或者字符串时间格式
 | 
			
		||||
 * @param format 需要转换的时间格式字符串
 | 
			
		||||
 * @description param 10秒:  10 * 1000
 | 
			
		||||
 * @description param 1分:   60 * 1000
 | 
			
		||||
 * @description param 1小时: 60 * 60 * 1000
 | 
			
		||||
 * @description param 24小时:60 * 60 * 24 * 1000
 | 
			
		||||
 * @description param 3天:   60 * 60* 24 * 1000 * 3
 | 
			
		||||
 * @returns 返回拼接后的时间字符串
 | 
			
		||||
 */
 | 
			
		||||
export function formatPast(param: string | Date, format = 'YYYY-mm-dd HH:MM:SS'): string {
 | 
			
		||||
  // 传入格式处理、存储转换值
 | 
			
		||||
  let t: any, s: number
 | 
			
		||||
  // 获取js 时间戳
 | 
			
		||||
  let time: number = new Date().getTime()
 | 
			
		||||
  // 是否是对象
 | 
			
		||||
  typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param)
 | 
			
		||||
  // 当前时间戳 - 传入时间戳
 | 
			
		||||
  time = Number.parseInt(`${time - t}`)
 | 
			
		||||
  if (time < 10000) {
 | 
			
		||||
    // 10秒内
 | 
			
		||||
    return '刚刚'
 | 
			
		||||
  } else if (time < 60000 && time >= 10000) {
 | 
			
		||||
    // 超过10秒少于1分钟内
 | 
			
		||||
    s = Math.floor(time / 1000)
 | 
			
		||||
    return `${s}秒前`
 | 
			
		||||
  } else if (time < 3600000 && time >= 60000) {
 | 
			
		||||
    // 超过1分钟少于1小时
 | 
			
		||||
    s = Math.floor(time / 60000)
 | 
			
		||||
    return `${s}分钟前`
 | 
			
		||||
  } else if (time < 86400000 && time >= 3600000) {
 | 
			
		||||
    // 超过1小时少于24小时
 | 
			
		||||
    s = Math.floor(time / 3600000)
 | 
			
		||||
    return `${s}小时前`
 | 
			
		||||
  } else if (time < 259200000 && time >= 86400000) {
 | 
			
		||||
    // 超过1天少于3天内
 | 
			
		||||
    s = Math.floor(time / 86400000)
 | 
			
		||||
    return `${s}天前`
 | 
			
		||||
  } else {
 | 
			
		||||
    // 超过3天
 | 
			
		||||
    const date = typeof param === 'string' || 'object' ? new Date(param) : param
 | 
			
		||||
    return formatDate(date, format)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 时间问候语
 | 
			
		||||
 * @param param 当前时间,new Date() 格式
 | 
			
		||||
 * @description param 调用 `formatAxis(new Date())` 输出 `上午好`
 | 
			
		||||
 * @returns 返回拼接后的时间字符串
 | 
			
		||||
 */
 | 
			
		||||
export function formatAxis(param: Date): string {
 | 
			
		||||
  const hour: number = new Date(param).getHours()
 | 
			
		||||
  if (hour < 6) return '凌晨好'
 | 
			
		||||
  else if (hour < 9) return '早上好'
 | 
			
		||||
  else if (hour < 12) return '上午好'
 | 
			
		||||
  else if (hour < 14) return '中午好'
 | 
			
		||||
  else if (hour < 17) return '下午好'
 | 
			
		||||
  else if (hour < 19) return '傍晚好'
 | 
			
		||||
  else if (hour < 22) return '晚上好'
 | 
			
		||||
  else return '夜里好'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 将毫秒,转换成时间字符串。例如说,xx 分钟
 | 
			
		||||
 *
 | 
			
		||||
 * @param ms 毫秒
 | 
			
		||||
 * @returns {string} 字符串
 | 
			
		||||
 */
 | 
			
		||||
export function formatPast2(ms) {
 | 
			
		||||
  const day = Math.floor(ms / (24 * 60 * 60 * 1000))
 | 
			
		||||
  const hour = Math.floor(ms / (60 * 60 * 1000) - day * 24)
 | 
			
		||||
  const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60)
 | 
			
		||||
  const second = Math.floor(ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60)
 | 
			
		||||
  if (day > 0) {
 | 
			
		||||
    return day + '天' + hour + '小时' + minute + '分钟'
 | 
			
		||||
  }
 | 
			
		||||
  if (hour > 0) {
 | 
			
		||||
    return hour + '小时' + minute + '分钟'
 | 
			
		||||
  }
 | 
			
		||||
  if (minute > 0) {
 | 
			
		||||
    return minute + '分钟'
 | 
			
		||||
  }
 | 
			
		||||
  if (second > 0) {
 | 
			
		||||
    return second + '秒'
 | 
			
		||||
  } else {
 | 
			
		||||
    return 0 + '秒'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										139
									
								
								src/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @param component 需要注册的组件
 | 
			
		||||
 * @param alias 组件别名
 | 
			
		||||
 * @returns any
 | 
			
		||||
 */
 | 
			
		||||
export const withInstall = <T>(component: T, alias?: string) => {
 | 
			
		||||
  const comp = component as any
 | 
			
		||||
  comp.install = (app: any) => {
 | 
			
		||||
    app.component(comp.name || comp.displayName, component)
 | 
			
		||||
    if (alias) {
 | 
			
		||||
      app.config.globalProperties[alias] = component
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return component as T & Plugin
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param str 需要转下划线的驼峰字符串
 | 
			
		||||
 * @returns 字符串下划线
 | 
			
		||||
 */
 | 
			
		||||
export const humpToUnderline = (str: string): string => {
 | 
			
		||||
  return str.replace(/([A-Z])/g, '-$1').toLowerCase()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param str 需要转驼峰的下划线字符串
 | 
			
		||||
 * @returns 字符串驼峰
 | 
			
		||||
 */
 | 
			
		||||
export const underlineToHump = (str: string): string => {
 | 
			
		||||
  if (!str) return ''
 | 
			
		||||
  return str.replace(/\-(\w)/g, (_, letter: string) => {
 | 
			
		||||
    return letter.toUpperCase()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
 | 
			
		||||
  dom.style.setProperty(prop, val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 查找数组对象的某个下标
 | 
			
		||||
 * @param {Array} ary 查找的数组
 | 
			
		||||
 * @param {Functon} fn 判断的方法
 | 
			
		||||
 */
 | 
			
		||||
// eslint-disable-next-line
 | 
			
		||||
export const findIndex = <T = Recordable>(ary: Array<T>, fn: Fn): number => {
 | 
			
		||||
  if (ary.findIndex) {
 | 
			
		||||
    return ary.findIndex(fn)
 | 
			
		||||
  }
 | 
			
		||||
  let index = -1
 | 
			
		||||
  ary.some((item: T, i: number, ary: Array<T>) => {
 | 
			
		||||
    const ret: T = fn(item, i, ary)
 | 
			
		||||
    if (ret) {
 | 
			
		||||
      index = i
 | 
			
		||||
      return ret
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  return index
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const trim = (str: string) => {
 | 
			
		||||
  return str.replace(/(^\s*)|(\s*$)/g, '')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {Date | number | string} time 需要转换的时间
 | 
			
		||||
 * @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss
 | 
			
		||||
 */
 | 
			
		||||
export const formatTime = (time: Date | number | string, fmt: string) => {
 | 
			
		||||
  if (!time) return ''
 | 
			
		||||
  else {
 | 
			
		||||
    const date = new Date(time)
 | 
			
		||||
    const o = {
 | 
			
		||||
      'M+': date.getMonth() + 1,
 | 
			
		||||
      'd+': date.getDate(),
 | 
			
		||||
      'H+': date.getHours(),
 | 
			
		||||
      'm+': date.getMinutes(),
 | 
			
		||||
      's+': date.getSeconds(),
 | 
			
		||||
      'q+': Math.floor((date.getMonth() + 3) / 3),
 | 
			
		||||
      S: date.getMilliseconds()
 | 
			
		||||
    }
 | 
			
		||||
    if (/(y+)/.test(fmt)) {
 | 
			
		||||
      fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
 | 
			
		||||
    }
 | 
			
		||||
    for (const k in o) {
 | 
			
		||||
      if (new RegExp('(' + k + ')').test(fmt)) {
 | 
			
		||||
        fmt = fmt.replace(
 | 
			
		||||
          RegExp.$1,
 | 
			
		||||
          RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return fmt
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 生成随机字符串
 | 
			
		||||
 */
 | 
			
		||||
export const toAnyString = () => {
 | 
			
		||||
  const str: string = 'xxxxx-xxxxx-4xxxx-yxxxx-xxxxx'.replace(/[xy]/g, (c: string) => {
 | 
			
		||||
    const r: number = (Math.random() * 16) | 0
 | 
			
		||||
    const v: number = c === 'x' ? r : (r & 0x3) | 0x8
 | 
			
		||||
    return v.toString()
 | 
			
		||||
  })
 | 
			
		||||
  return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const generateUUID = () => {
 | 
			
		||||
  if (typeof crypto === 'object') {
 | 
			
		||||
    if (typeof crypto.randomUUID === 'function') {
 | 
			
		||||
      return crypto.randomUUID()
 | 
			
		||||
    }
 | 
			
		||||
    if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
 | 
			
		||||
      const callback = (c: any) => {
 | 
			
		||||
        const num = Number(c)
 | 
			
		||||
        return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(
 | 
			
		||||
          16
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  let timestamp = new Date().getTime()
 | 
			
		||||
  let performanceNow =
 | 
			
		||||
    (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0
 | 
			
		||||
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
 | 
			
		||||
    let random = Math.random() * 16
 | 
			
		||||
    if (timestamp > 0) {
 | 
			
		||||
      random = (timestamp + random) % 16 | 0
 | 
			
		||||
      timestamp = Math.floor(timestamp / 16)
 | 
			
		||||
    } else {
 | 
			
		||||
      random = (performanceNow + random) % 16 | 0
 | 
			
		||||
      performanceNow = Math.floor(performanceNow / 16)
 | 
			
		||||
    }
 | 
			
		||||
    return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										105
									
								
								src/utils/is.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/utils/is.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
// copy to vben-admin
 | 
			
		||||
 | 
			
		||||
const toString = Object.prototype.toString
 | 
			
		||||
 | 
			
		||||
export const is = (val: unknown, type: string) => {
 | 
			
		||||
  return toString.call(val) === `[object ${type}]`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isDef = <T = unknown>(val?: T): val is T => {
 | 
			
		||||
  return typeof val !== 'undefined'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isUnDef = <T = unknown>(val?: T): val is T => {
 | 
			
		||||
  return !isDef(val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isObject = (val: any): val is Record<any, any> => {
 | 
			
		||||
  return val !== null && is(val, 'Object')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isEmpty = <T = unknown>(val: T): val is T => {
 | 
			
		||||
  if (isArray(val) || isString(val)) {
 | 
			
		||||
    return val.length === 0
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (val instanceof Map || val instanceof Set) {
 | 
			
		||||
    return val.size === 0
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (isObject(val)) {
 | 
			
		||||
    return Object.keys(val).length === 0
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isDate = (val: unknown): val is Date => {
 | 
			
		||||
  return is(val, 'Date')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isNull = (val: unknown): val is null => {
 | 
			
		||||
  return val === null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isNullAndUnDef = (val: unknown): val is null | undefined => {
 | 
			
		||||
  return isUnDef(val) && isNull(val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isNullOrUnDef = (val: unknown): val is null | undefined => {
 | 
			
		||||
  return isUnDef(val) || isNull(val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isNumber = (val: unknown): val is number => {
 | 
			
		||||
  return is(val, 'Number')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
 | 
			
		||||
  return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isString = (val: unknown): val is string => {
 | 
			
		||||
  return is(val, 'String')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isFunction = (val: unknown): val is Function => {
 | 
			
		||||
  return typeof val === 'function'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isBoolean = (val: unknown): val is boolean => {
 | 
			
		||||
  return is(val, 'Boolean')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isRegExp = (val: unknown): val is RegExp => {
 | 
			
		||||
  return is(val, 'RegExp')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isArray = (val: any): val is Array<any> => {
 | 
			
		||||
  return val && Array.isArray(val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isWindow = (val: any): val is Window => {
 | 
			
		||||
  return typeof window !== 'undefined' && is(val, 'Window')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isElement = (val: unknown): val is Element => {
 | 
			
		||||
  return isObject(val) && !!val.tagName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isMap = (val: unknown): val is Map<any, any> => {
 | 
			
		||||
  return is(val, 'Map')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isServer = typeof window === 'undefined'
 | 
			
		||||
 | 
			
		||||
export const isClient = !isServer
 | 
			
		||||
 | 
			
		||||
export const isUrl = (path: string): boolean => {
 | 
			
		||||
  const reg =
 | 
			
		||||
    /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
 | 
			
		||||
  return reg.test(path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isDark = (): boolean => {
 | 
			
		||||
  return window.matchMedia('(prefers-color-scheme: dark)').matches
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								src/utils/jsencrypt.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/utils/jsencrypt.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
import { JSEncrypt } from 'jsencrypt'
 | 
			
		||||
 | 
			
		||||
// 密钥对生成 http://web.chacuo.net/netrsakeypair
 | 
			
		||||
 | 
			
		||||
const publicKey =
 | 
			
		||||
  'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
 | 
			
		||||
  'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
 | 
			
		||||
 | 
			
		||||
const privateKey =
 | 
			
		||||
  'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
 | 
			
		||||
  '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
 | 
			
		||||
  'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
 | 
			
		||||
  'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
 | 
			
		||||
  'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
 | 
			
		||||
  'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
 | 
			
		||||
  'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
 | 
			
		||||
  'UP8iWi1Qw0Y='
 | 
			
		||||
 | 
			
		||||
// 加密
 | 
			
		||||
export const encrypt = (txt: string) => {
 | 
			
		||||
  const encryptor = new JSEncrypt()
 | 
			
		||||
  encryptor.setPublicKey(publicKey) // 设置公钥
 | 
			
		||||
  return encryptor.encrypt(txt) // 对数据进行加密
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 解密
 | 
			
		||||
export const decrypt = (txt: string) => {
 | 
			
		||||
  const encryptor = new JSEncrypt()
 | 
			
		||||
  encryptor.setPrivateKey(privateKey) // 设置私钥
 | 
			
		||||
  return encryptor.decrypt(txt) // 对数据进行解密
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/utils/propTypes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/utils/propTypes.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
import { createTypes, VueTypesInterface, VueTypeValidableDef } from 'vue-types'
 | 
			
		||||
import { CSSProperties } from 'vue'
 | 
			
		||||
 | 
			
		||||
// 自定义扩展vue-types
 | 
			
		||||
type PropTypes = VueTypesInterface & {
 | 
			
		||||
  readonly style: VueTypeValidableDef<CSSProperties>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const propTypes = createTypes({
 | 
			
		||||
  func: undefined,
 | 
			
		||||
  bool: undefined,
 | 
			
		||||
  string: undefined,
 | 
			
		||||
  number: undefined,
 | 
			
		||||
  object: undefined,
 | 
			
		||||
  integer: undefined
 | 
			
		||||
}) as PropTypes
 | 
			
		||||
 | 
			
		||||
// 需要自定义扩展的类型
 | 
			
		||||
// see: https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html#the-extend-method
 | 
			
		||||
// propTypes.extend([
 | 
			
		||||
//   {
 | 
			
		||||
//     name: 'style',
 | 
			
		||||
//     getter: true,
 | 
			
		||||
//     type: [String, Object],
 | 
			
		||||
//     default: undefined
 | 
			
		||||
//   }
 | 
			
		||||
// ])
 | 
			
		||||
export { propTypes }
 | 
			
		||||
							
								
								
									
										224
									
								
								src/utils/routerHelper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								src/utils/routerHelper.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
			
		||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
 | 
			
		||||
import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
 | 
			
		||||
import { isUrl } from '@/utils/is'
 | 
			
		||||
import { omit, cloneDeep } from 'lodash-es'
 | 
			
		||||
 | 
			
		||||
const modules = import.meta.glob('../views/**/*.{vue,tsx}')
 | 
			
		||||
 | 
			
		||||
/* Layout */
 | 
			
		||||
export const Layout = () => import('@/layout/Layout.vue')
 | 
			
		||||
 | 
			
		||||
export const getParentLayout = () => {
 | 
			
		||||
  return () =>
 | 
			
		||||
    new Promise((resolve) => {
 | 
			
		||||
      resolve({
 | 
			
		||||
        name: 'ParentLayout'
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 按照路由中meta下的rank等级升序来排序路由
 | 
			
		||||
export const ascending = (arr: any[]) => {
 | 
			
		||||
  arr.forEach((v) => {
 | 
			
		||||
    if (v?.meta?.rank === null) v.meta.rank = undefined
 | 
			
		||||
    if (v?.meta?.rank === 0) {
 | 
			
		||||
      if (v.name !== 'home' && v.path !== '/') {
 | 
			
		||||
        console.warn('rank only the home page can be 0')
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  return arr.sort((a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
 | 
			
		||||
    return a?.meta?.rank - b?.meta?.rank
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormalized => {
 | 
			
		||||
  if (!route) return route
 | 
			
		||||
  const { matched, ...opt } = route
 | 
			
		||||
  return {
 | 
			
		||||
    ...opt,
 | 
			
		||||
    matched: (matched
 | 
			
		||||
      ? matched.map((item) => ({
 | 
			
		||||
          meta: item.meta,
 | 
			
		||||
          name: item.name,
 | 
			
		||||
          path: item.path
 | 
			
		||||
        }))
 | 
			
		||||
      : undefined) as RouteRecordNormalized[]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 后端控制路由生成
 | 
			
		||||
export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => {
 | 
			
		||||
  const res: AppRouteRecordRaw[] = []
 | 
			
		||||
  const modulesRoutesKeys = Object.keys(modules)
 | 
			
		||||
  for (const route of routes) {
 | 
			
		||||
    const meta = {
 | 
			
		||||
      title: route.name,
 | 
			
		||||
      icon: route.icon,
 | 
			
		||||
      hidden: !route.visible,
 | 
			
		||||
      noCache: !route.keepAlive,
 | 
			
		||||
      alwaysShow:
 | 
			
		||||
        route.children &&
 | 
			
		||||
        route.children.length === 1 &&
 | 
			
		||||
        (route.alwaysShow !== undefined ? route.alwaysShow : true)
 | 
			
		||||
    }
 | 
			
		||||
    // 路由地址转首字母大写驼峰,作为路由名称,适配keepAlive
 | 
			
		||||
    let data: AppRouteRecordRaw = {
 | 
			
		||||
      path: route.path,
 | 
			
		||||
      name:
 | 
			
		||||
        route.componentName && route.componentName.length > 0
 | 
			
		||||
          ? route.componentName
 | 
			
		||||
          : toCamelCase(route.path, true),
 | 
			
		||||
      redirect: route.redirect,
 | 
			
		||||
      meta: meta
 | 
			
		||||
    }
 | 
			
		||||
    //处理顶级非目录路由
 | 
			
		||||
    if (!route.children && route.parentId == 0 && route.component) {
 | 
			
		||||
      data.component = Layout
 | 
			
		||||
      data.meta = {}
 | 
			
		||||
      data.name = toCamelCase(route.path, true) + 'Parent'
 | 
			
		||||
      data.redirect = ''
 | 
			
		||||
      meta.alwaysShow = true
 | 
			
		||||
      const childrenData: AppRouteRecordRaw = {
 | 
			
		||||
        path: '',
 | 
			
		||||
        name: toCamelCase(route.path, true),
 | 
			
		||||
        redirect: route.redirect,
 | 
			
		||||
        meta: meta
 | 
			
		||||
      }
 | 
			
		||||
      const index = route?.component
 | 
			
		||||
        ? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component))
 | 
			
		||||
        : modulesRoutesKeys.findIndex((ev) => ev.includes(route.path))
 | 
			
		||||
      childrenData.component = modules[modulesRoutesKeys[index]]
 | 
			
		||||
      data.children = [childrenData]
 | 
			
		||||
    } else {
 | 
			
		||||
      // 目录
 | 
			
		||||
      if (route.children) {
 | 
			
		||||
        data.component = Layout
 | 
			
		||||
        data.redirect = getRedirect(route.path, route.children)
 | 
			
		||||
        // 外链
 | 
			
		||||
      } else if (isUrl(route.path)) {
 | 
			
		||||
        data = {
 | 
			
		||||
          path: '/external-link',
 | 
			
		||||
          component: Layout,
 | 
			
		||||
          meta: {
 | 
			
		||||
            name: route.name
 | 
			
		||||
          },
 | 
			
		||||
          children: [data]
 | 
			
		||||
        } as AppRouteRecordRaw
 | 
			
		||||
        // 菜单
 | 
			
		||||
      } else {
 | 
			
		||||
        // 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会根path保持一致)
 | 
			
		||||
        const index = route?.component
 | 
			
		||||
          ? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component))
 | 
			
		||||
          : modulesRoutesKeys.findIndex((ev) => ev.includes(route.path))
 | 
			
		||||
        data.component = modules[modulesRoutesKeys[index]]
 | 
			
		||||
      }
 | 
			
		||||
      if (route.children) {
 | 
			
		||||
        data.children = generateRoute(route.children)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    res.push(data as AppRouteRecordRaw)
 | 
			
		||||
  }
 | 
			
		||||
  return res
 | 
			
		||||
}
 | 
			
		||||
export const getRedirect = (parentPath: string, children: AppCustomRouteRecordRaw[]) => {
 | 
			
		||||
  if (!children || children.length == 0) {
 | 
			
		||||
    return parentPath
 | 
			
		||||
  }
 | 
			
		||||
  const path = generateRoutePath(parentPath, children[0].path)
 | 
			
		||||
  // 递归子节点
 | 
			
		||||
  if (children[0].children) return getRedirect(path, children[0].children)
 | 
			
		||||
}
 | 
			
		||||
const generateRoutePath = (parentPath: string, path: string) => {
 | 
			
		||||
  if (parentPath.endsWith('/')) {
 | 
			
		||||
    parentPath = parentPath.slice(0, -1) // 移除默认的 /
 | 
			
		||||
  }
 | 
			
		||||
  if (!path.startsWith('/')) {
 | 
			
		||||
    path = '/' + path
 | 
			
		||||
  }
 | 
			
		||||
  return parentPath + path
 | 
			
		||||
}
 | 
			
		||||
export const pathResolve = (parentPath: string, path: string) => {
 | 
			
		||||
  if (isUrl(path)) return path
 | 
			
		||||
  const childPath = path.startsWith('/') || !path ? path : `/${path}`
 | 
			
		||||
  return `${parentPath}${childPath}`.replace(/\/\//g, '/')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 路由降级
 | 
			
		||||
export const flatMultiLevelRoutes = (routes: AppRouteRecordRaw[]) => {
 | 
			
		||||
  const modules: AppRouteRecordRaw[] = cloneDeep(routes)
 | 
			
		||||
  for (let index = 0; index < modules.length; index++) {
 | 
			
		||||
    const route = modules[index]
 | 
			
		||||
    if (!isMultipleRoute(route)) {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
    promoteRouteLevel(route)
 | 
			
		||||
  }
 | 
			
		||||
  return modules
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 层级是否大于2
 | 
			
		||||
const isMultipleRoute = (route: AppRouteRecordRaw) => {
 | 
			
		||||
  if (!route || !Reflect.has(route, 'children') || !route.children?.length) {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const children = route.children
 | 
			
		||||
 | 
			
		||||
  let flag = false
 | 
			
		||||
  for (let index = 0; index < children.length; index++) {
 | 
			
		||||
    const child = children[index]
 | 
			
		||||
    if (child.children?.length) {
 | 
			
		||||
      flag = true
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return flag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 生成二级路由
 | 
			
		||||
const promoteRouteLevel = (route: AppRouteRecordRaw) => {
 | 
			
		||||
  let router: Router | null = createRouter({
 | 
			
		||||
    routes: [route as RouteRecordRaw],
 | 
			
		||||
    history: createWebHashHistory()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const routes = router.getRoutes()
 | 
			
		||||
  addToChildren(routes, route.children || [], route)
 | 
			
		||||
  router = null
 | 
			
		||||
 | 
			
		||||
  route.children = route.children?.map((item) => omit(item, 'children'))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 添加所有子菜单
 | 
			
		||||
const addToChildren = (
 | 
			
		||||
  routes: RouteRecordNormalized[],
 | 
			
		||||
  children: AppRouteRecordRaw[],
 | 
			
		||||
  routeModule: AppRouteRecordRaw
 | 
			
		||||
) => {
 | 
			
		||||
  for (let index = 0; index < children.length; index++) {
 | 
			
		||||
    const child = children[index]
 | 
			
		||||
    const route = routes.find((item) => item.name === child.name)
 | 
			
		||||
    if (!route) {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
    routeModule.children = routeModule.children || []
 | 
			
		||||
    if (!routeModule.children.find((item) => item.name === route.name)) {
 | 
			
		||||
      routeModule.children?.push(route as unknown as AppRouteRecordRaw)
 | 
			
		||||
    }
 | 
			
		||||
    if (child.children?.length) {
 | 
			
		||||
      addToChildren(routes, child.children, routeModule)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const toCamelCase = (str: string, upperCaseFirst: boolean) => {
 | 
			
		||||
  str = (str || '').toLowerCase().replace(/-(.)/g, function (group1: string) {
 | 
			
		||||
    return group1.toUpperCase()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  if (upperCaseFirst && str) {
 | 
			
		||||
    str = str.charAt(0).toUpperCase() + str.slice(1)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return str
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										301
									
								
								src/utils/tree.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								src/utils/tree.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,301 @@
 | 
			
		||||
interface TreeHelperConfig {
 | 
			
		||||
  id: string
 | 
			
		||||
  children: string
 | 
			
		||||
  pid: string
 | 
			
		||||
}
 | 
			
		||||
const DEFAULT_CONFIG: TreeHelperConfig = {
 | 
			
		||||
  id: 'id',
 | 
			
		||||
  children: 'children',
 | 
			
		||||
  pid: 'pid'
 | 
			
		||||
}
 | 
			
		||||
export const defaultProps = {
 | 
			
		||||
  children: 'children',
 | 
			
		||||
  label: 'name',
 | 
			
		||||
  value: 'id'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
 | 
			
		||||
 | 
			
		||||
// tree from list
 | 
			
		||||
export const listToTree = <T = any>(list: any[], config: Partial<TreeHelperConfig> = {}): T[] => {
 | 
			
		||||
  const conf = getConfig(config) as TreeHelperConfig
 | 
			
		||||
  const nodeMap = new Map()
 | 
			
		||||
  const result: T[] = []
 | 
			
		||||
  const { id, children, pid } = conf
 | 
			
		||||
 | 
			
		||||
  for (const node of list) {
 | 
			
		||||
    node[children] = node[children] || []
 | 
			
		||||
    nodeMap.set(node[id], node)
 | 
			
		||||
  }
 | 
			
		||||
  for (const node of list) {
 | 
			
		||||
    const parent = nodeMap.get(node[pid])
 | 
			
		||||
    ;(parent ? parent.children : result).push(node)
 | 
			
		||||
  }
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const treeToList = <T = any>(tree: any, config: Partial<TreeHelperConfig> = {}): T => {
 | 
			
		||||
  config = getConfig(config)
 | 
			
		||||
  const { children } = config
 | 
			
		||||
  const result: any = [...tree]
 | 
			
		||||
  for (let i = 0; i < result.length; i++) {
 | 
			
		||||
    if (!result[i][children!]) continue
 | 
			
		||||
    result.splice(i + 1, 0, ...result[i][children!])
 | 
			
		||||
  }
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const findNode = <T = any>(
 | 
			
		||||
  tree: any,
 | 
			
		||||
  func: Fn,
 | 
			
		||||
  config: Partial<TreeHelperConfig> = {}
 | 
			
		||||
): T | null => {
 | 
			
		||||
  config = getConfig(config)
 | 
			
		||||
  const { children } = config
 | 
			
		||||
  const list = [...tree]
 | 
			
		||||
  for (const node of list) {
 | 
			
		||||
    if (func(node)) return node
 | 
			
		||||
    node[children!] && list.push(...node[children!])
 | 
			
		||||
  }
 | 
			
		||||
  return null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const findNodeAll = <T = any>(
 | 
			
		||||
  tree: any,
 | 
			
		||||
  func: Fn,
 | 
			
		||||
  config: Partial<TreeHelperConfig> = {}
 | 
			
		||||
): T[] => {
 | 
			
		||||
  config = getConfig(config)
 | 
			
		||||
  const { children } = config
 | 
			
		||||
  const list = [...tree]
 | 
			
		||||
  const result: T[] = []
 | 
			
		||||
  for (const node of list) {
 | 
			
		||||
    func(node) && result.push(node)
 | 
			
		||||
    node[children!] && list.push(...node[children!])
 | 
			
		||||
  }
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const findPath = <T = any>(
 | 
			
		||||
  tree: any,
 | 
			
		||||
  func: Fn,
 | 
			
		||||
  config: Partial<TreeHelperConfig> = {}
 | 
			
		||||
): T | T[] | null => {
 | 
			
		||||
  config = getConfig(config)
 | 
			
		||||
  const path: T[] = []
 | 
			
		||||
  const list = [...tree]
 | 
			
		||||
  const visitedSet = new Set()
 | 
			
		||||
  const { children } = config
 | 
			
		||||
  while (list.length) {
 | 
			
		||||
    const node = list[0]
 | 
			
		||||
    if (visitedSet.has(node)) {
 | 
			
		||||
      path.pop()
 | 
			
		||||
      list.shift()
 | 
			
		||||
    } else {
 | 
			
		||||
      visitedSet.add(node)
 | 
			
		||||
      node[children!] && list.unshift(...node[children!])
 | 
			
		||||
      path.push(node)
 | 
			
		||||
      if (func(node)) {
 | 
			
		||||
        return path
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const findPathAll = (tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}) => {
 | 
			
		||||
  config = getConfig(config)
 | 
			
		||||
  const path: any[] = []
 | 
			
		||||
  const list = [...tree]
 | 
			
		||||
  const result: any[] = []
 | 
			
		||||
  const visitedSet = new Set(),
 | 
			
		||||
    { children } = config
 | 
			
		||||
  while (list.length) {
 | 
			
		||||
    const node = list[0]
 | 
			
		||||
    if (visitedSet.has(node)) {
 | 
			
		||||
      path.pop()
 | 
			
		||||
      list.shift()
 | 
			
		||||
    } else {
 | 
			
		||||
      visitedSet.add(node)
 | 
			
		||||
      node[children!] && list.unshift(...node[children!])
 | 
			
		||||
      path.push(node)
 | 
			
		||||
      func(node) && result.push([...path])
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const filter = <T = any>(
 | 
			
		||||
  tree: T[],
 | 
			
		||||
  func: (n: T) => boolean,
 | 
			
		||||
  config: Partial<TreeHelperConfig> = {}
 | 
			
		||||
): T[] => {
 | 
			
		||||
  config = getConfig(config)
 | 
			
		||||
  const children = config.children as string
 | 
			
		||||
  function listFilter(list: T[]) {
 | 
			
		||||
    return list
 | 
			
		||||
      .map((node: any) => ({ ...node }))
 | 
			
		||||
      .filter((node) => {
 | 
			
		||||
        node[children] = node[children] && listFilter(node[children])
 | 
			
		||||
        return func(node) || (node[children] && node[children].length)
 | 
			
		||||
      })
 | 
			
		||||
  }
 | 
			
		||||
  return listFilter(tree)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const forEach = <T = any>(
 | 
			
		||||
  tree: T[],
 | 
			
		||||
  func: (n: T) => any,
 | 
			
		||||
  config: Partial<TreeHelperConfig> = {}
 | 
			
		||||
): void => {
 | 
			
		||||
  config = getConfig(config)
 | 
			
		||||
  const list: any[] = [...tree]
 | 
			
		||||
  const { children } = config
 | 
			
		||||
  for (let i = 0; i < list.length; i++) {
 | 
			
		||||
    // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿
 | 
			
		||||
    if (func(list[i])) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    children && list[i][children] && list.splice(i + 1, 0, ...list[i][children])
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @description: Extract tree specified structure
 | 
			
		||||
 */
 | 
			
		||||
export const treeMap = <T = any>(
 | 
			
		||||
  treeData: T[],
 | 
			
		||||
  opt: { children?: string; conversion: Fn }
 | 
			
		||||
): T[] => {
 | 
			
		||||
  return treeData.map((item) => treeMapEach(item, opt))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @description: Extract tree specified structure
 | 
			
		||||
 */
 | 
			
		||||
export const treeMapEach = (
 | 
			
		||||
  data: any,
 | 
			
		||||
  { children = 'children', conversion }: { children?: string; conversion: Fn }
 | 
			
		||||
) => {
 | 
			
		||||
  const haveChildren = Array.isArray(data[children]) && data[children].length > 0
 | 
			
		||||
  const conversionData = conversion(data) || {}
 | 
			
		||||
  if (haveChildren) {
 | 
			
		||||
    return {
 | 
			
		||||
      ...conversionData,
 | 
			
		||||
      [children]: data[children].map((i: number) =>
 | 
			
		||||
        treeMapEach(i, {
 | 
			
		||||
          children,
 | 
			
		||||
          conversion
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    return {
 | 
			
		||||
      ...conversionData
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 递归遍历树结构
 | 
			
		||||
 * @param treeDatas 树
 | 
			
		||||
 * @param callBack 回调
 | 
			
		||||
 * @param parentNode 父节点
 | 
			
		||||
 */
 | 
			
		||||
export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => {
 | 
			
		||||
  treeDatas.forEach((element) => {
 | 
			
		||||
    const newNode = callBack(element, parentNode) || element
 | 
			
		||||
    if (element.children) {
 | 
			
		||||
      eachTree(element.children, callBack, newNode)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 构造树型结构数据
 | 
			
		||||
 * @param {*} data 数据源
 | 
			
		||||
 * @param {*} id id字段 默认 'id'
 | 
			
		||||
 * @param {*} parentId 父节点字段 默认 'parentId'
 | 
			
		||||
 * @param {*} children 孩子节点字段 默认 'children'
 | 
			
		||||
 */
 | 
			
		||||
export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => {
 | 
			
		||||
  if (!Array.isArray(data)) {
 | 
			
		||||
    console.warn('data must be an array')
 | 
			
		||||
    return []
 | 
			
		||||
  }
 | 
			
		||||
  const config = {
 | 
			
		||||
    id: id || 'id',
 | 
			
		||||
    parentId: parentId || 'parentId',
 | 
			
		||||
    childrenList: children || 'children'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const childrenListMap = {}
 | 
			
		||||
  const nodeIds = {}
 | 
			
		||||
  const tree: any[] = []
 | 
			
		||||
 | 
			
		||||
  for (const d of data) {
 | 
			
		||||
    const parentId = d[config.parentId]
 | 
			
		||||
    if (childrenListMap[parentId] == null) {
 | 
			
		||||
      childrenListMap[parentId] = []
 | 
			
		||||
    }
 | 
			
		||||
    nodeIds[d[config.id]] = d
 | 
			
		||||
    childrenListMap[parentId].push(d)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const d of data) {
 | 
			
		||||
    const parentId = d[config.parentId]
 | 
			
		||||
    if (nodeIds[parentId] == null) {
 | 
			
		||||
      tree.push(d)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const t of tree) {
 | 
			
		||||
    adaptToChildrenList(t)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function adaptToChildrenList(o) {
 | 
			
		||||
    if (childrenListMap[o[config.id]] !== null) {
 | 
			
		||||
      o[config.childrenList] = childrenListMap[o[config.id]]
 | 
			
		||||
    }
 | 
			
		||||
    if (o[config.childrenList]) {
 | 
			
		||||
      for (const c of o[config.childrenList]) {
 | 
			
		||||
        adaptToChildrenList(c)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return tree
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 构造树型结构数据
 | 
			
		||||
 * @param {*} data 数据源
 | 
			
		||||
 * @param {*} id id字段 默认 'id'
 | 
			
		||||
 * @param {*} parentId 父节点字段 默认 'parentId'
 | 
			
		||||
 * @param {*} children 孩子节点字段 默认 'children'
 | 
			
		||||
 * @param {*} rootId 根Id 默认 0
 | 
			
		||||
 */
 | 
			
		||||
export const handleTree2 = (data, id, parentId, children, rootId) => {
 | 
			
		||||
  id = id || 'id'
 | 
			
		||||
  parentId = parentId || 'parentId'
 | 
			
		||||
  children = children || 'children'
 | 
			
		||||
  rootId =
 | 
			
		||||
    rootId ||
 | 
			
		||||
    Math.min(
 | 
			
		||||
      ...data.map((item) => {
 | 
			
		||||
        return item[parentId]
 | 
			
		||||
      })
 | 
			
		||||
    ) ||
 | 
			
		||||
    0
 | 
			
		||||
  //对源数据深度克隆
 | 
			
		||||
  const cloneData = JSON.parse(JSON.stringify(data))
 | 
			
		||||
  //循环所有项
 | 
			
		||||
  const treeData = cloneData.filter((father) => {
 | 
			
		||||
    const branchArr = cloneData.filter((child) => {
 | 
			
		||||
      //返回每一项的子级数组
 | 
			
		||||
      return father[id] === child[parentId]
 | 
			
		||||
    })
 | 
			
		||||
    branchArr.length > 0 ? (father.children = branchArr) : ''
 | 
			
		||||
    //返回第一层
 | 
			
		||||
    return father[parentId] === rootId
 | 
			
		||||
  })
 | 
			
		||||
  return treeData !== '' ? treeData : data
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/utils/tsxHelper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/utils/tsxHelper.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import { Slots } from 'vue'
 | 
			
		||||
import { isFunction } from '@/utils/is'
 | 
			
		||||
 | 
			
		||||
export const getSlot = (slots: Slots, slot = 'default', data?: Recordable) => {
 | 
			
		||||
  // Reflect.has 判断一个对象是否存在某个属性
 | 
			
		||||
  if (!slots || !Reflect.has(slots, slot)) {
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
  if (!isFunction(slots[slot])) {
 | 
			
		||||
    console.error(`${slot} is not a function!`)
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
  const slotFn = slots[slot]
 | 
			
		||||
  if (!slotFn) return null
 | 
			
		||||
  return slotFn(data)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user