webman迁移
This commit is contained in:
404
dafuweng-webman/web/src/utils/common.ts
Normal file
404
dafuweng-webman/web/src/utils/common.ts
Normal file
@@ -0,0 +1,404 @@
|
||||
import * as elIcons from '@element-plus/icons-vue'
|
||||
import { useTitle } from '@vueuse/core'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { isArray, isNull, trim, trimStart } from 'lodash-es'
|
||||
import type { App } from 'vue'
|
||||
import { nextTick } from 'vue'
|
||||
import type { TranslateOptions } from 'vue-i18n'
|
||||
import { i18n } from '../lang'
|
||||
import { useSiteConfig } from '../stores/siteConfig'
|
||||
import { getUrl } from './axios'
|
||||
import Icon from '/@/components/icon/index.vue'
|
||||
import router from '/@/router/index'
|
||||
import { adminBaseRoutePath } from '/@/router/static/adminBase'
|
||||
import { useMemberCenter } from '/@/stores/memberCenter'
|
||||
import { useNavTabs } from '/@/stores/navTabs'
|
||||
|
||||
export function registerIcons(app: App) {
|
||||
/*
|
||||
* 全局注册 Icon
|
||||
* 使用方式: <Icon name="name" size="size" color="color" />
|
||||
* 详见<待完善>
|
||||
*/
|
||||
app.component('Icon', Icon)
|
||||
|
||||
/*
|
||||
* 全局注册element Plus的icon
|
||||
*/
|
||||
const icons = elIcons as any
|
||||
for (const i in icons) {
|
||||
app.component(`el-icon-${icons[i].name}`, icons[i])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载网络css文件
|
||||
* @param url css资源url
|
||||
*/
|
||||
export function loadCss(url: string): void {
|
||||
const link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = url
|
||||
link.crossOrigin = 'anonymous'
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载网络js文件
|
||||
* @param url js资源url
|
||||
*/
|
||||
export function loadJs(url: string): void {
|
||||
const link = document.createElement('script')
|
||||
link.src = url
|
||||
document.body.appendChild(link)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据路由 meta.title 设置浏览器标题
|
||||
*/
|
||||
export function setTitleFromRoute() {
|
||||
nextTick(() => {
|
||||
if (typeof router.currentRoute.value.meta.title != 'string') {
|
||||
return
|
||||
}
|
||||
const webTitle = i18n.global.te(router.currentRoute.value.meta.title)
|
||||
? i18n.global.t(router.currentRoute.value.meta.title)
|
||||
: router.currentRoute.value.meta.title
|
||||
const title = useTitle()
|
||||
const siteConfig = useSiteConfig()
|
||||
title.value = `${webTitle}${siteConfig.siteName ? ' - ' + siteConfig.siteName : ''}`
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浏览器标题
|
||||
* @param webTitle 新的标题
|
||||
*/
|
||||
export function setTitle(webTitle: string) {
|
||||
if (router.currentRoute.value) {
|
||||
router.currentRoute.value.meta.title = webTitle
|
||||
}
|
||||
nextTick(() => {
|
||||
const title = useTitle()
|
||||
const siteConfig = useSiteConfig()
|
||||
title.value = `${webTitle}${siteConfig.siteName ? ' - ' + siteConfig.siteName : ''}`
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是外部链接
|
||||
* @param path
|
||||
*/
|
||||
export function isExternal(path: string): boolean {
|
||||
return /^(https?|ftp|mailto|tel):/.test(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局防抖
|
||||
* 与 _.debounce 不同的是,间隔期间如果再次传递不同的函数,两个函数也只会执行一次
|
||||
* @param fn 执行函数
|
||||
* @param ms 间隔毫秒数
|
||||
*/
|
||||
export const debounce = (fn: Function, ms: number) => {
|
||||
return (...args: any[]) => {
|
||||
if (window.lazy) {
|
||||
clearTimeout(window.lazy)
|
||||
}
|
||||
window.lazy = window.setTimeout(() => {
|
||||
fn(...args)
|
||||
}, ms)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据pk字段的值从数组中获取key
|
||||
* @param arr
|
||||
* @param pk
|
||||
* @param value
|
||||
*/
|
||||
export const getArrayKey = (arr: any, pk: string, value: any): any => {
|
||||
for (const key in arr) {
|
||||
if (arr[key][pk] == value) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单重置
|
||||
* @param formEl
|
||||
*/
|
||||
export const onResetForm = (formEl?: FormInstance | null) => {
|
||||
typeof formEl?.resetFields == 'function' && formEl.resetFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据构建为ElTree的data {label:'', children: []}
|
||||
* @param data
|
||||
*/
|
||||
export const buildJsonToElTreeData = (data: any): ElTreeData[] => {
|
||||
if (typeof data == 'object') {
|
||||
const childrens = []
|
||||
for (const key in data) {
|
||||
childrens.push({
|
||||
label: key + ': ' + data[key],
|
||||
children: buildJsonToElTreeData(data[key]),
|
||||
})
|
||||
}
|
||||
return childrens
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在后台应用内
|
||||
* @param path 不传递则通过当前路由 path 检查
|
||||
*/
|
||||
export const isAdminApp = (path = '') => {
|
||||
const regex = new RegExp(`^${adminBaseRoutePath}`)
|
||||
if (path) {
|
||||
return regex.test(path)
|
||||
}
|
||||
if (regex.test(getCurrentRoutePath())) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为手机设备
|
||||
*/
|
||||
export const isMobile = () => {
|
||||
return !!navigator.userAgent.match(
|
||||
/android|webos|ip(hone|ad|od)|opera (mini|mobi|tablet)|iemobile|windows.+(phone|touch)|mobile|fennec|kindle (Fire)|Silk|maemo|blackberry|playbook|bb10\; (touch|kbd)|Symbian(OS)|Ubuntu Touch/i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个文件路径中获取文件名
|
||||
* @param path 文件路径
|
||||
*/
|
||||
export const getFileNameFromPath = (path: string) => {
|
||||
const paths = path.split('/')
|
||||
return paths[paths.length - 1]
|
||||
}
|
||||
|
||||
export function auth(node: string): boolean
|
||||
export function auth(node: { name: string; subNodeName?: string }): boolean
|
||||
|
||||
/**
|
||||
* 鉴权
|
||||
* 提供 string 将根据当前路由 path 自动拼接和鉴权,还可以提供路由的 name 对象进行鉴权
|
||||
* @param node
|
||||
*/
|
||||
export function auth(node: string | { name: string; subNodeName?: string }) {
|
||||
const store = isAdminApp() ? useNavTabs() : useMemberCenter()
|
||||
if (typeof node === 'string') {
|
||||
const path = getCurrentRoutePath()
|
||||
if (store.state.authNode.has(path)) {
|
||||
const subNodeName = path + (path == '/' ? '' : '/') + node
|
||||
if (store.state.authNode.get(path)!.some((v: string) => v == subNodeName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 节点列表中没有找到 name
|
||||
if (!node.name || !store.state.authNode.has(node.name)) return false
|
||||
|
||||
// 无需继续检查子节点或未找到子节点
|
||||
if (!node.subNodeName || store.state.authNode.get(node.name)?.includes(node.subNodeName)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源完整地址
|
||||
* @param relativeUrl 资源相对地址
|
||||
* @param domain 指定域名
|
||||
*/
|
||||
export const fullUrl = (relativeUrl: string, domain = '') => {
|
||||
const siteConfig = useSiteConfig()
|
||||
if (!domain) {
|
||||
domain = siteConfig.cdnUrl ? siteConfig.cdnUrl : getUrl()
|
||||
}
|
||||
if (!relativeUrl) return domain
|
||||
|
||||
const regUrl = new RegExp(/^http(s)?:\/\//)
|
||||
const regexImg = new RegExp(/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i)
|
||||
if (!domain || regUrl.test(relativeUrl) || regexImg.test(relativeUrl)) {
|
||||
return relativeUrl
|
||||
}
|
||||
|
||||
let url = domain + relativeUrl
|
||||
if (domain === siteConfig.cdnUrl && siteConfig.cdnUrlParams) {
|
||||
const separator = url.includes('?') ? '&' : '?'
|
||||
url += separator + siteConfig.cdnUrlParams
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路由 path
|
||||
*/
|
||||
export const getCurrentRoutePath = () => {
|
||||
let path = router.currentRoute.value.path
|
||||
if (path == '/') path = trimStart(window.location.hash, '#')
|
||||
if (path.indexOf('?') !== -1) path = path.replace(/\?.*/, '')
|
||||
return path
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取根据当前路由路径动态加载的语言翻译
|
||||
* @param key 无需语言路径的翻译key,亦可使用完整路径
|
||||
* @param named — 命名插值的值
|
||||
* @param options — 其他翻译选项
|
||||
* @returns — Translated message
|
||||
*/
|
||||
export const __ = (key: string, named?: Record<string, unknown>, options?: TranslateOptions<string>) => {
|
||||
let langPath = ''
|
||||
const path = getCurrentRoutePath()
|
||||
if (isAdminApp()) {
|
||||
langPath = path.slice(path.indexOf(adminBaseRoutePath) + adminBaseRoutePath.length)
|
||||
langPath = trim(langPath, '/').replaceAll('/', '.')
|
||||
} else {
|
||||
langPath = trim(path, '/').replaceAll('/', '.')
|
||||
}
|
||||
langPath = langPath ? langPath + '.' + key : key
|
||||
return i18n.global.te(langPath)
|
||||
? i18n.global.t(langPath, named ?? {}, options ? options : {})
|
||||
: i18n.global.t(key, named ?? {}, options ? options : {})
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件类型效验,前端根据服务端配置进行初步检查
|
||||
* @param fileName 文件名
|
||||
* @param fileType 文件 mimeType,不一定存在
|
||||
*/
|
||||
export const checkFileMimetype = (fileName: string, fileType: string) => {
|
||||
if (!fileName) return false
|
||||
const siteConfig = useSiteConfig()
|
||||
const allowedSuffixes = isArray(siteConfig.upload.allowedSuffixes)
|
||||
? siteConfig.upload.allowedSuffixes
|
||||
: siteConfig.upload.allowedSuffixes.toLowerCase().split(',')
|
||||
|
||||
const allowedMimeTypes = isArray(siteConfig.upload.allowedMimeTypes)
|
||||
? siteConfig.upload.allowedMimeTypes
|
||||
: siteConfig.upload.allowedMimeTypes.toLowerCase().split(',')
|
||||
|
||||
const fileSuffix = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase()
|
||||
if (allowedSuffixes.includes(fileSuffix) || allowedSuffixes.includes('.' + fileSuffix)) {
|
||||
return true
|
||||
}
|
||||
if (fileType && allowedMimeTypes.includes(fileType)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一组资源的完整地址
|
||||
* @param relativeUrls 资源相对地址
|
||||
* @param domain 指定域名
|
||||
*/
|
||||
export const arrayFullUrl = (relativeUrls: string | string[], domain = '') => {
|
||||
if (typeof relativeUrls === 'string') {
|
||||
relativeUrls = relativeUrls == '' ? [] : relativeUrls.split(',')
|
||||
}
|
||||
for (const key in relativeUrls) {
|
||||
relativeUrls[key] = fullUrl(relativeUrls[key], domain)
|
||||
}
|
||||
return relativeUrls
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间戳
|
||||
* @param dateTime 时间戳,默认使用当前时间戳
|
||||
* @param fmt 格式化方式,默认:yyyy-mm-dd hh:MM:ss
|
||||
*/
|
||||
export const timeFormat = (dateTime: string | number | null = null, fmt = 'yyyy-mm-dd hh:MM:ss') => {
|
||||
if (dateTime == 'none') {
|
||||
return i18n.global.t('None')
|
||||
}
|
||||
|
||||
if (isNull(dateTime)) {
|
||||
dateTime = Number(new Date())
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. 秒级时间戳(10位)需要转换为毫秒级,才能供 Date 对象直接使用
|
||||
* 2. yyyy-mm-dd 也是10位,使用 isFinite 进行排除
|
||||
*/
|
||||
if (String(dateTime).length === 10 && isFinite(Number(dateTime))) {
|
||||
dateTime = +dateTime * 1000
|
||||
}
|
||||
|
||||
let date = new Date(dateTime)
|
||||
if (isNaN(date.getTime())) {
|
||||
date = new Date(Number(dateTime))
|
||||
if (isNaN(date.getTime())) {
|
||||
return 'Invalid Date'
|
||||
}
|
||||
}
|
||||
|
||||
let ret
|
||||
const opt: anyObj = {
|
||||
'y+': date.getFullYear().toString(), // 年
|
||||
'm+': (date.getMonth() + 1).toString(), // 月
|
||||
'd+': date.getDate().toString(), // 日
|
||||
'h+': date.getHours().toString(), // 时
|
||||
'M+': date.getMinutes().toString(), // 分
|
||||
's+': date.getSeconds().toString(), // 秒
|
||||
}
|
||||
for (const k in opt) {
|
||||
ret = new RegExp('(' + k + ')').exec(fmt)
|
||||
if (ret) {
|
||||
fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : padStart(opt[k], ret[1].length, '0'))
|
||||
}
|
||||
}
|
||||
return fmt
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串补位
|
||||
*/
|
||||
const padStart = (str: string, maxLength: number, fillString = ' ') => {
|
||||
if (str.length >= maxLength) return str
|
||||
|
||||
const fillLength = maxLength - str.length
|
||||
let times = Math.ceil(fillLength / fillString.length)
|
||||
while ((times >>= 1)) {
|
||||
fillString += fillString
|
||||
if (times === 1) {
|
||||
fillString += fillString
|
||||
}
|
||||
}
|
||||
return fillString.slice(0, fillLength) + str
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前时间生成问候语
|
||||
*/
|
||||
export const getGreet = () => {
|
||||
const now = new Date()
|
||||
const hour = now.getHours()
|
||||
let greet = ''
|
||||
|
||||
if (hour < 5) {
|
||||
greet = i18n.global.t('utils.Late at night, pay attention to your body!')
|
||||
} else if (hour < 9) {
|
||||
greet = i18n.global.t('utils.good morning!') + i18n.global.t('utils.welcome back')
|
||||
} else if (hour < 12) {
|
||||
greet = i18n.global.t('utils.Good morning!') + i18n.global.t('utils.welcome back')
|
||||
} else if (hour < 14) {
|
||||
greet = i18n.global.t('utils.Good noon!') + i18n.global.t('utils.welcome back')
|
||||
} else if (hour < 18) {
|
||||
greet = i18n.global.t('utils.good afternoon') + i18n.global.t('utils.welcome back')
|
||||
} else if (hour < 24) {
|
||||
greet = i18n.global.t('utils.Good evening') + i18n.global.t('utils.welcome back')
|
||||
} else {
|
||||
greet = i18n.global.t('utils.Hello!') + i18n.global.t('utils.welcome back')
|
||||
}
|
||||
return greet
|
||||
}
|
||||
Reference in New Issue
Block a user