refactor(front): 重构前端组件和 API

- 更新了多个组件的样式和结构
- 重命名了一些 API 文件
- 添加了新的 API 接口
- 优化了部分代码的可读性和性能
This commit is contained in:
2025-07-23 20:58:15 +08:00
parent 091c056cd0
commit 902173a9e9
111 changed files with 1841 additions and 1198 deletions
+88
View File
@@ -0,0 +1,88 @@
import { FormatDateTimeShortStr } from '@/utils/CsUtil'
import { getToken } from '@/utils/auth'
import axios from 'axios'
/**
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function HasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
/**
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function AddClass(ele, cls) {
if (!HasClass(ele, cls)) ele.className += ' ' + cls
}
/**
* Remove class from element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function RemoveClass(ele, cls) {
if (HasClass(ele, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
ele.className = ele.className.replace(reg, ' ')
}
}
/**
* @param {HTMLElement} element
* @param {string} className
*/
export function ToggleClass(element, className) {
if (!element || !className) {
return
}
let classString = element.className
const nameIndex = classString.indexOf(className)
if (nameIndex === -1) {
classString += '' + className
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length)
}
element.className = classString
}
/**
* 下载文件
* @param obj 后端返回的数据
* @param name 文件名
* @param suffix 文件后缀
* @constructor
*/
export function DownloadFile(obj, name, suffix) {
const url = window.URL.createObjectURL(new Blob([obj]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
const fileName = FormatDateTimeShortStr(new Date()) + '-' + name + '.' + suffix
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
/**
* 上传文件
* @param api 上传地址
* @param file 上传的文件
* @returns {Promise<axios.AxiosResponse<any>>}
*/
export function UploadFile(api, file) {
const data = new FormData()
data.append('file', file)
const config = {
headers: { 'Authorization': getToken() }
}
return axios.post(api, data, config)
}
+330
View File
@@ -0,0 +1,330 @@
import store from '@/store'
import dayjs from 'dayjs'
/**
* 权限校验
* @param value
* @returns {boolean|*}
*/
export function CheckPermissions(value) {
if (value && value instanceof Array && value.length > 0) {
// 拥有的权限
const roles = store.getters && store.getters.roles
// 需要的权限
const permissionRoles = value
return roles.some(role => {
return permissionRoles.includes(role)
})
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}
/**
* 字符串转数字
* @param value
* @returns {Number|*}
*/
export function StringToNumber(value) {
try {
return value ? Number(value) : value
} catch (e) {
return value
}
}
/**
* 保留几位小数
* @param num Number
* @param accuracy 精度
* @returns {string|number}
*/
export function NumberToFixed(num, accuracy) {
return num instanceof Number ? num.toFixed(accuracy) : num
}
/**
* 浅拷贝
* @param source 源对象
* @param target 目标对象
* @returns {*}
*/
export function CopyObject(source, target) {
return Object.assign(target, source)
}
/**
* 深拷贝对象
* @param obj
*/
export function DeepCopyObject(obj) {
return JSON.parse(JSON.stringify(obj))
}
/**
* 统计对象属性等于targetValue的数量
* @param arr 对象数组
* @param propKey 属性名称
* @param targetValue 目标值
* @returns {*|number}
*/
export function CountArraysObjectByPropKey(arr, propKey, targetValue) {
if (!Array.isArray(arr)) return 0
return arr.filter(item => item[propKey] === targetValue).length
}
/**
* 根据value获取label
* @param options /
* @param value /
* @returns {*}
*/
export function GetOptionLabelByValue(options, value) {
if (options instanceof Array) {
const find = options.find(f => f.value === value)
if (find) {
return find.label
}
return ''
}
}
/**
* 格式化字节数字符串
* @param value
* @returns {string}
* @constructor
*/
export function FormatBytesStr(value) {
if (typeof value !== 'number' || isNaN(value) || value <= 0) {
return '0 Bytes'
}
const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const index = Math.floor(Math.log(value) / Math.log(1024))
const unit = units[Math.min(index, units.length - 1)]
const result = (value / Math.pow(1024, index)).toFixed(2)
return `${result} ${unit}`
}
/**
* 格式化日期字符串
* @param originVal
* @returns {string}
* @constructor
*/
export function FormatDateStr(originVal) {
if (originVal === undefined) {
return ''
}
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
return `${y}-${m}-${d}`
}
/**
* 格式化日期时间字符串
* @param originVal
* @returns {string}
* @constructor
*/
export function FormatDateTimeStr(originVal) {
if (originVal) {
return dayjs(originVal).format('YYYY-MM-DD HH:mm:ss')
}
return ''
}
/**
* 格式化日期时间字符串,不带分割符号
* @param originVal
* @returns {string}
* @constructor
*/
export function FormatDateTimeShortStr(originVal) {
if (originVal) {
return dayjs(originVal).format('YYYYMMDDHHmmss')
}
return ''
}
/**
* 格式化表格行列中的日期时间字符串
* @param row
* @param column
* @param cellValue
* @param index
* @returns {string}
* @constructor
*/
export function FormatRowDateTimeStr(row, column, cellValue, index) {
if (cellValue === undefined) {
return ''
}
const dt = new Date(cellValue)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
/**
* 获取变量类型
* @param value
* @returns {string}
* @constructor
*/
export function GetValueType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
}
/**
* 数组去重
* @param arr
* @returns {any[]}
* @constructor
*/
export function UniqueArrays(arr) {
return [...new Set(arr)]
}
/**
* 手机号掩码
* @param mobile
* @returns {*|string}
* @constructor
*/
export function MaskMobile(mobile) {
if (mobile.length > 7) {
return mobile.substr(0, 3) + '****' + mobile.substr(7)
}
return mobile
}
/**
* 邮箱掩码
* @param email
* @returns {string}
* @constructor
*/
export function MaskEmail(email) {
if (String(email).indexOf('@') > 0) {
const str = email.split('@')
let _s = ''
if (str[0].length > 3) {
for (let i = 0; i < str[0].length - 3; i++) {
_s += '*'
}
}
return str[0].substr(0, 3) + _s + '@' + str[1]
}
return email
}
/**
* 获取Url中的查询参数
* @param {string} url
* @returns {Object}
*/
export function GetUrlQueryArgs(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
/**
* 将Url中的查询参数转成对象
* @param {string} url
* @returns {Object}
*/
export function ParseUrlQueryArgsToObject(url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, ' ') +
'"}'
)
}
/**
* 合并两个属性不一致的对象
* @param {Object} target
* @param {(Object|Array)} source
* @returns {Object}
*/
export function MergeObject(target, source) {
if (typeof target !== 'object') {
target = {}
}
if (Array.isArray(source)) {
return source.slice()
}
Object.keys(source).forEach(property => {
const sourceProperty = source[property]
if (typeof sourceProperty === 'object') {
target[property] = MergeObject(target[property], sourceProperty)
} else {
target[property] = sourceProperty
}
})
return target
}
/**
* 防抖动
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function DeBounce(func, wait, immediate = false) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
@@ -1,90 +1,98 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
/**
* 是否链接
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
export function IsExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
// /**
// * 是否有效用户名
// * @param {string} str
// * @returns {Boolean}
// */
// export function IsValidUsername(str) {
// const validMap = ['admin', 'editor']
// return validMap.indexOf(str.trim()) >= 0
// }
/**
* 是否有效链接
* @param {string} url
* @returns {Boolean}
*/
export function validURL(url) {
export function IsValidURL(url) {
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/**
* 是否小写字母
* @param {string} str
* @returns {Boolean}
*/
export function validLowerCase(str) {
export function IsValidLowerCaseLetter(str) {
const reg = /^[a-z]+$/
return reg.test(str)
}
/**
* 是否大写字母
* @param {string} str
* @returns {Boolean}
*/
export function validUpperCase(str) {
export function IsValidUpperCase(str) {
const reg = /^[A-Z]+$/
return reg.test(str)
}
/**
* 是否包含大、小写字母
* @param {string} str
* @returns {Boolean}
*/
export function validAlphabets(str) {
export function IsValidLetter(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
/**
* 是否邮箱
* @param {string} email
* @returns {Boolean}
*/
export function validEmail(email) {
export function IsValidEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return reg.test(email)
}
export function isvalidPhone(phone) {
const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
return reg.test(phone)
}
// /**
// * 是否手机号
// * @param phone
// * @returns {boolean}
// * @constructor
// */
// export function IsValidPhone(phone) {
// const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
// return reg.test(phone)
// }
/**
* 是否是字符串
* @param {string} str
* @returns {Boolean}
*/
export function isString(str) {
if (typeof str === 'string' || str instanceof String) {
return true
}
return false
export function IsString(str) {
return typeof str === 'string' || str instanceof String
}
/**
* 是否数组
* @param {Array} arg
* @returns {Boolean}
*/
export function isArray(arg) {
export function IsArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'
}
@@ -97,7 +105,7 @@ export function isArray(arg) {
* @param value
* @param callback
*/
export function validateIP(rule, value, callback) {
export function IsValidIP(rule, value, callback) {
if (value === '' || value === undefined || value == null) {
callback()
} else {
@@ -111,7 +119,7 @@ export function validateIP(rule, value, callback) {
}
/* 是否手机号码或者固话*/
export function validatePhoneTwo(rule, value, callback) {
export function IsValidPhoneTwo(rule, value, callback) {
const reg = /^((0\d{2,3}-\d{7,8})|(1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}))$/
if (value === '' || value === undefined || value == null) {
callback()
@@ -125,7 +133,7 @@ export function validatePhoneTwo(rule, value, callback) {
}
/* 是否固话*/
export function validateTelephone(rule, value, callback) {
export function IsValidTelephone(rule, value, callback) {
const reg = /0\d{2}-\d{7,8}/
if (value === '' || value === undefined || value == null) {
callback()
@@ -139,7 +147,7 @@ export function validateTelephone(rule, value, callback) {
}
/* 是否手机号码*/
export function validatePhone(rule, value, callback) {
export function IsValidPhone(rule, value, callback) {
const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
if (value === '' || value === undefined || value == null) {
callback()
@@ -153,7 +161,7 @@ export function validatePhone(rule, value, callback) {
}
/* 是否身份证号码*/
export function validateIdNo(rule, value, callback) {
export function IsValidIdNumber(rule, value, callback) {
const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
if (value === '' || value === undefined || value == null) {
callback()
+42
View File
@@ -0,0 +1,42 @@
import store from '@/store'
import Vue from 'vue'
/**
* WebSocket客户端
*/
export default function() {
const user = store.getters && store.getters.user
const webSocketApi = store.getters && store.getters.websocketApi
this.context = Vue.prototype
this.wsUri = webSocketApi.replace('{sid}', user.username)
/**
* 初始化WebSocket连接
* @param handleError -> function (e) { e是异常本身 }
* @param handleMessage -> function (e) { e.data是message }
*/
this.connect = function(handleError, handleMessage) {
this.wsClient = new WebSocket(this.wsUri)
// 连接发生错误
this.wsClient.onerror = handleError
// 收到消息
this.wsClient.onmessage = handleMessage
return this
}
/**
* 发送客户端数据
* @param data 客户端数据
*/
this.sendData = function(data) {
this.wsClient.send(JSON.stringify(data))
}
/**
* 用于关闭ws连接
*/
this.close = function() {
console.log('关闭连接')
if (this.wsClient.readyState === 1) {
this.sendData([{}])
this.wsClient.close()
}
}
}
-36
View File
@@ -1,36 +0,0 @@
import Vue from 'vue'
import Clipboard from 'clipboard'
function clipboardSuccess() {
Vue.prototype.$message({
message: 'Copy successfully',
type: 'success',
duration: 1500
})
}
function clipboardError() {
Vue.prototype.$message({
message: 'Copy failed',
type: 'error'
})
}
export default function handleClipboard(text, event) {
const clipboard = new Clipboard(event.target, {
text: () => text
})
clipboard.on('success', () => {
clipboardSuccess()
clipboard.off('error')
clipboard.off('success')
clipboard.destroy()
})
clipboard.on('error', () => {
clipboardError()
clipboard.off('error')
clipboard.off('success')
clipboard.destroy()
})
clipboard.onClick(event)
}
-20
View File
@@ -1,20 +0,0 @@
import Vue from 'vue'
/**
* 防止重复提交指令<br/>
* 例子: <el-button v-prevent-re-click >提交</el-button>
*/
const preventReClick = Vue.directive('preventReClick', {
inserted(el, binding) {
el.addEventListener('click', () => {
if (!el.disabled) {
el.disabled = true
setTimeout(() => {
el.disabled = false
}, binding.value || 3000)
}
})
}
})
export { preventReClick }
-216
View File
@@ -1,216 +0,0 @@
/* eslint-disable */
/**
* Date对象的补充函数,包括类似Python中的strftime()
* 阿债 https://gitee.com/azhai/datetime.js
*/
Date.prototype.toMidnight = function() {
this.setHours(0)
this.setMinutes(0)
this.setSeconds(0)
this.setMilliseconds(0)
return this
}
Date.prototype.daysAgo = function(days, midnight) {
days = days ? days - 0 : 0
const date = new Date(this.getTime() - days * 8.64E7)
return midnight ? date.toMidnight() : date
}
Date.prototype.monthBegin = function(offset) {
offset = offset ? offset - 0 : 0
const days = this.getDate() - 1 - offset
return this.daysAgo(days, true)
}
Date.prototype.quarterBegin = function() {
const month = this.getMonth() - this.getMonth() % 3
return new Date(this.getFullYear(), month, 1).toMidnight()
}
Date.prototype.yearBegin = function() {
return new Date(this.getFullYear(), 0, 1).toMidnight()
}
Date.prototype.strftime = function(format, local) {
if (!format) {
const str = new Date(this.getTime() + 2.88E7).toISOString()
return str.substr(0, 16).replace('T', ' ')
}
local = local && local.startsWith('zh') ? 'zh' : 'en'
const padZero = function(str, len) {
const pads = len - str.toString().length
return (pads && pads > 0 ? '0'.repeat(pads) : '') + str
}
format = format.replace('%F', '%Y-%m-%d')
format = format.replace(/%D|%x/, '%m/%d/%y')
format = format.replace(/%T|%X/, '%H:%M:%S')
format = format.replace('%R', '%H:%M')
format = format.replace('%r', '%H:%M:%S %p')
format = format.replace('%c', '%a %b %e %H:%M:%S %Y')
const _this = this
return format.replace(/%[A-Za-z%]/g, function(f) {
let ans = f
switch (f) {
case '%%':
ans = '%'
break
case '%Y':
case '%G':
ans = _this.getFullYear()
break
case '%y':
ans = _this.getFullYear() % 100
break
case '%C':
ans = _this.getFullYear() / 100
break
case '%m':
case '%n':
ans = _this.getMonth() + 1
break
case '%B':
local = local.startsWith('en') ? 'english' : local
case '%b':
const m = _this.getMonth()
ans = local_labels.monthes[local][m]
break
case '%d':
case '%e':
ans = _this.getDate()
break
case '%j':
ans = _this.getDaysOfYear()
break
case '%U':
case '%W':
const ws = _this.getWeeksOfYear(f === '%W')
ans = padZero(ws, 2)
break
case '%w':
ans = _this.getDay()
case '%u':
ans = ans === 0 ? 7 : ans
break
case '%A':
local = local.startsWith('en') ? 'english' : local
case '%a':
const d = _this.getDay()
ans = local_labels.weekdays[local][d]
break
case '%H':
case '%k':
ans = _this.getHours()
break
case '%I':
case '%l':
ans = _this.getHours()
ans = ans % 12
break
case '%M':
ans = _this.getMinutes()
break
case '%S':
ans = _this.getSeconds()
break
case '%s':
ans = parseInt(_this.getTime() / 1E3)
break
case '%f':
const ms = _this.getMilliseconds()
ans = padZero(ms * 1E3, 6)
break
case '%P':
local = local.startsWith('en') ? 'english' : local
case '%p':
const h = _this.getHours()
ans = local_labels.meridians[local][h < 12 ? 0 : 1]
break
case '%z':
let tzo = _this.getTimezoneOffset()
const sign = tzo < 0 ? '-' : '+'
tzo = Math.abs(tzo)
const ho = padZero(tzo / 60, 2)
const mo = padZero(tzo % 60, 2)
ans = sign + ho + mo
break
default:
break
}
if (f === '%C' || f === '%y' || f === '%m' || f === '%d' || f === '%H' || f === '%M' || f === '%S') {
ans = padZero(ans, 2)
}
return ans.toString()
})
}
Date.prototype.humanize = function(local) {
local = local && local.startsWith('zh') ? 'zh' : 'en'
const result = this.strftime('', local)
const days = (Date.today() - this.toMidnight().getTime()) / 8.64E7
if (days <= -10 || days >= 10) {
return result
}
const labels = local_labels.dayagos[local]
let lbl = ''
if (days === 0 || days === 1) {
lbl = labels[days]
} else if (days === -1) {
lbl = labels[2]
} else if (days >= 2) {
lbl = days + labels[3]
} else {
lbl = days + labels[4]
}
return lbl + result.substr(10, 6)
}
const local_labels = {
monthes: {
english: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
zh: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
},
weekdays: {
english: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
en: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
zh: ['日', '一', '二', '三', '四', '五', '六']
},
meridians: {
english: ['a.m.', 'p.m.'],
en: ['AM', 'PM'],
zh: ['上午', '下午']
},
dayagos: {
english: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'],
en: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'],
zh: ['今天', '昨天', '明天', '天前', '天后']
}
}
export default Date
-385
View File
@@ -1,385 +0,0 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
import dayjs from 'dayjs'
/**
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'undefined' || time === null || time === 'null') {
return ''
} else if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
/**
* @param {number} time
* @param {string} option
* @returns {string}
*/
export function formatTime(time, option) {
if (('' + time).length === 10) {
time = parseInt(time) * 1000
} else {
time = +time
}
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
// less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time, option)
} else {
return (
d.getMonth() +
1 +
'月' +
d.getDate() +
'日' +
d.getHours() +
'时' +
d.getMinutes() +
'分'
)
}
}
/**
* @param {string} url
* @returns {Object}
*/
export function getQueryObject(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
/**
* @param {string} input value
* @returns {number} output value
*/
export function byteLength(str) {
// returns the byte length of an utf8 string
let s = str.length
for (var i = str.length - 1; i >= 0; i--) {
const code = str.charCodeAt(i)
if (code > 0x7f && code <= 0x7ff) s++
else if (code > 0x7ff && code <= 0xffff) s += 2
if (code >= 0xDC00 && code <= 0xDFFF) i--
}
return s
}
/**
* @param {Array} actual
* @returns {Array}
*/
export function cleanArray(actual) {
const newArray = []
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i])
}
}
return newArray
}
/**
* @param {Object} json
* @returns {Array}
*/
export function param(json) {
if (!json) return ''
return cleanArray(
Object.keys(json).map(key => {
if (json[key] === undefined) return ''
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
})
).join('&')
}
/**
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, ' ') +
'"}'
)
}
/**
* @param {string} val
* @returns {string}
*/
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
/**
* Merges two objects, giving the last one precedence
* @param {Object} target
* @param {(Object|Array)} source
* @returns {Object}
*/
export function objectMerge(target, source) {
if (typeof target !== 'object') {
target = {}
}
if (Array.isArray(source)) {
return source.slice()
}
Object.keys(source).forEach(property => {
const sourceProperty = source[property]
if (typeof sourceProperty === 'object') {
target[property] = objectMerge(target[property], sourceProperty)
} else {
target[property] = sourceProperty
}
})
return target
}
/**
* @param {HTMLElement} element
* @param {string} className
*/
export function toggleClass(element, className) {
if (!element || !className) {
return
}
let classString = element.className
const nameIndex = classString.indexOf(className)
if (nameIndex === -1) {
classString += '' + className
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length)
}
element.className = classString
}
/**
* @param {string} type
* @returns {Date}
*/
export function getTime(type) {
if (type === 'start') {
return new Date().getTime() - 3600 * 1000 * 24 * 90
} else {
return new Date(new Date().toDateString())
}
}
/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
/**
* @param {Array} arr
* @returns {Array}
*/
export function uniqueArr(arr) {
return Array.from(new Set(arr))
}
/**
* @returns {string}
*/
export function createUniqueString() {
const timestamp = +new Date() + ''
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
return (+(randomNum + timestamp)).toString(32)
}
/**
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function hasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
/**
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += ' ' + cls
}
/**
* Remove class from element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
ele.className = ele.className.replace(reg, ' ')
}
}
// 替换邮箱字符
export function regEmail(email) {
if (String(email).indexOf('@') > 0) {
const str = email.split('@')
let _s = ''
if (str[0].length > 3) {
for (var i = 0; i < str[0].length - 3; i++) {
_s += '*'
}
}
var new_email = str[0].substr(0, 3) + _s + '@' + str[1]
}
return new_email
}
// 替换手机字符
export function regMobile(mobile) {
if (mobile.length > 7) {
var new_mobile = mobile.substr(0, 3) + '****' + mobile.substr(7)
}
return new_mobile
}
// 下载文件
export function downloadFile(obj, name, suffix) {
const url = window.URL.createObjectURL(new Blob([obj]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
const fileName = parseTime(new Date()) + '-' + name + '.' + suffix
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// 数组去重
export const uniqueArrays = (arr) => [...new Set(arr)]
// 获取变量类型
export const type = (value) => Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
// 深拷贝对象
export const deepClone = (obj) => JSON.parse(JSON.stringify(obj))
// 格式化日期时间字符串
export const formatDateTimeStr = (date) => {
if (date) {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
}
return ''
}
-20
View File
@@ -1,20 +0,0 @@
import Vue from 'vue'
/**
* 防止重复提交指令<br/>
* 例子: <el-button v-prevent-re-click >提交</el-button>
*/
const preventReClick = Vue.directive('preventReClick', {
inserted(el, binding) {
el.addEventListener('click', () => {
if (!el.disabled) {
el.disabled = true
setTimeout(() => {
el.disabled = false
}, binding.value || 3000)
}
})
}
})
export { preventReClick }
-137
View File
@@ -1,137 +0,0 @@
import store from '@/store'
const PermissionUtil = {
/**
* 权限校验
* @param value
* @returns {boolean|*}
*/
validate: function(value) {
if (value && value instanceof Array && value.length > 0) {
// 拥有的权限
const roles = store.getters && store.getters.roles
// 需要的权限
const permissionRoles = value
return roles.some(role => {
return permissionRoles.includes(role)
})
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}
}
const StringUtil = {
/**
* 字符串转数字
* @param value
* @returns {Number|*}
*/
toNumber: function(value) {
if (value) {
return Number(value)
}
return 0
},
/**
* 保留几位小数
* @param num Number
* @param accuracy 精度
* @returns {string|number}
*/
retailNum: function(num, accuracy) {
return num instanceof Number ? num.toFixed(accuracy) : 0
}
}
const ObjectUtil = {
/**
* 浅拷贝
* @param source 源对象
* @param target 目标对象
* @returns {*}
*/
copy: function(source, target) {
return Object.assign(target, source)
}
}
const LabelUtil = {
/**
* 根据value获取label
* @param options /
* @param value /
* @returns {*}
*/
getLabel: function(options, value) {
if (options instanceof Array) {
const find = options.find(f => f.value === value)
if (find) {
return find.label
}
return ''
}
}
}
const SizeUtil = {
formatBytes: function(value) {
if (typeof value !== 'number' || isNaN(value) || value <= 0) {
return '0 Bytes'
}
const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const index = Math.floor(Math.log(value) / Math.log(1024))
const unit = units[Math.min(index, units.length - 1)]
const result = (value / Math.pow(1024, index)).toFixed(2)
return `${result} ${unit}`
}
}
const DateUtil = {
formatDate: function(originVal) {
if (originVal === undefined) {
return ''
}
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
return `${y}-${m}-${d}`
},
formatDateTime: function(originVal) {
if (originVal === undefined) {
return ''
}
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
},
formatRowDateTime: function(row, column, cellValue, index) {
if (cellValue === undefined) {
return ''
}
const dt = new Date(cellValue)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
}
export {
PermissionUtil,
StringUtil,
ObjectUtil,
LabelUtil,
DateUtil,
SizeUtil
}
-23
View File
@@ -1,23 +0,0 @@
import store from '@/store'
/**
* @param {Array} value
* @returns {Boolean}
* @example see @/views/permission/directive.vue
*/
export default {
install(Vue) {
Vue.prototype.checkPer = (value) => {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
return roles.some(role => {
return permissionRoles.includes(role)
})
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
import Date from './datetime.js'
import Date from '../plugins/DateExtendPlugin.js'
export const calendarBaseShortcuts = [{
text: '今天',
-11
View File
@@ -1,11 +0,0 @@
import axios from 'axios'
import { getToken } from '@/utils/auth'
export function upload(api, file) {
var data = new FormData()
data.append('file', file)
const config = {
headers: { 'Authorization': getToken() }
}
return axios.post(api, data, config)
}
-42
View File
@@ -1,42 +0,0 @@
/**
* @param context 上下文,例如 this
* @param wsUri websocket地址
* @constructor
*/
function EasyWebSocket(context, wsUri) {
this.context = context
this.wsUri = wsUri
this.webSocket = null
/**
* 初始化WebSocket连接
* @param handleError -> function (e) { e是异常本身 }
* @param handleMessage -> function (e) { e.data是message }
*/
this.connect = function(handleError, handleMessage) {
this.webSocket = new WebSocket(this.wsUri)
// 连接发生错误
this.webSocket.onerror = handleError
// 收到消息
this.webSocket.onmessage = handleMessage
return this
}
/**
* 发送客户端数据
* @param data 客户端数据
*/
this.sendData = function(data) {
this.webSocket.send(JSON.stringify(data))
}
/**
* 用于关闭ws连接
*/
this.close = function() {
console.info('关闭连接')
if (this.webSocket.readyState === 1) {
this.sendData([{}])
this.webSocket.close()
}
}
}
export default EasyWebSocket