前端代码

This commit is contained in:
范强强
2024-02-19 11:12:59 +08:00
parent 498f091352
commit c66445edd4
1436 changed files with 528601 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
import { getVmParentByName } from '@/utils/util'
const FormTypes = {
normal: 'normal',
input: 'input',
inputNumber: 'inputNumber',
checkbox: 'checkbox',
select: 'select',
date: 'date',
datetime: 'datetime',
upload: 'upload',
file: 'file',
image: 'image',
popup: 'popup',
list_multi: 'list_multi',
sel_search: 'sel_search',
sel_search_async: 'sel_search_async',
radio: 'radio',
checkbox_meta: 'checkbox_meta',
input_pop: 'input_pop',
sel_depart: 'sel_depart',
sel_user: 'sel_user',
slot: 'slot',
hidden: 'hidden'
}
const VALIDATE_NO_PASSED = Symbol('')
export { FormTypes, VALIDATE_NO_PASSED }
/**
* 获取指定的 $refs 对象
* 有时候可能会遇到组件未挂载到页面中的情况,导致无法获取 $refs 中的某个对象
* 这个方法可以等待挂载完成之后再返回 $refs 的对象,避免报错
* @author sunjianlei
**/
export function getRefPromise (vm, name) {
return new Promise((resolve) => {
(function next () {
const ref = vm.$refs[name]
if (ref) {
resolve(ref)
} else {
setTimeout(() => {
next()
}, 10)
}
})()
})
}
/**
* 一次性验证主表单和所有的次表单
* @param form 主表单 form 对象
* @param cases 接收一个数组,每项都是一个JEditableTable实例
* @returns {Promise<any>}
* @author sunjianlei
*/
export function validateFormAndTables (form, cases) {
if (!(form && typeof form.validateFields === 'function')) {
throw new Error(`form 参数需要的是一个form对象,而传入的却是${typeof form}`)
}
const options = {}
return new Promise((resolve, reject) => {
// 验证主表表单
form.validateFields((err, values) => {
err ? reject({ error: VALIDATE_NO_PASSED }) : resolve(values)
})
}).then(values => {
Object.assign(options, { formValue: values })
// 验证所有子表的表单
return validateTables(cases)
}).then(all => {
Object.assign(options, { tablesValue: all })
return Promise.resolve(options)
}).catch(error => {
return Promise.reject(error)
})
}
/**
* 一次性验证主表单和所有的次表单(新版本)
* @param form 主表单 form 对象
* @param values
* @param cases 接收一个数组,每项都是一个JEditableTable实例
* @returns {Promise<any>}
* @author sunjianlei
*/
export function validateFormModelAndTables (form, values, cases) {
if (!(form && typeof form.validate === 'function')) {
throw new Error(`form 参数需要的是一个form对象,而传入的却是${typeof form}`)
}
const options = {}
return new Promise((resolve, reject) => {
// 验证主表表单
form.validate((valid) => {
valid ? resolve(values) : reject({ error: VALIDATE_NO_PASSED })
})
}).then(values => {
Object.assign(options, { formValue: values })
// 验证所有子表的表单
return validateTables(cases)
}).then(all => {
Object.assign(options, { tablesValue: all })
return Promise.resolve(options)
}).catch(error => {
return Promise.reject(error)
})
}
/**
* 验证并获取一个或多个表格的所有值
* @param cases 接收一个数组,每项都是一个JEditableTable实例
* @param deleteTempId 是否删除临时ID,如果设为true,行编辑就不返回新增行的ID,ID需要后台生成
* @author sunjianlei
*/
export function validateTables (cases, deleteTempId) {
if (!(cases instanceof Array)) {
throw new Error(`'validateTables'函数的'cases'参数需要的是一个数组,而传入的却是${typeof cases}`)
}
return new Promise((resolve, reject) => {
const tables = []
let index = 0
if (!cases || cases.length === 0) {
resolve()
}
(function next () {
const vm = cases[index]
vm.getAll(true, deleteTempId).then(all => {
tables[index] = all
// 判断校验是否全部完成,完成返回成功,否则继续进行下一步校验
if (++index === cases.length) {
resolve(tables)
} else {
(
next()
)
}
}, error => {
// 出现未验证通过的表单,不再进行下一步校验,直接返回失败并跳转到该表格
if (error === VALIDATE_NO_PASSED) {
// 尝试获取tabKey,如果在ATab组件内即可获取
let paneKey
const tabPane = getVmParentByName(vm, 'ATabPane')
if (tabPane) {
paneKey = tabPane.$vnode.key
}
reject({ error: VALIDATE_NO_PASSED, index, paneKey })
}
reject(error)
})
})()
})
}
+106
View File
@@ -0,0 +1,106 @@
/**
* LunarFullCalendar 公共 js
*
* @version 1.0.0
* @author sunjianlei
*
* */
import { getRefPromise } from '@/utils/JEditableTableUtil'
/* 日历的视图类型 */
const calendarViewType = {
month: 'month', // 月视图
basicWeek: 'basicWeek', // 基础周视图
basicDay: 'basicDay', // 基础天视图
agendaWeek: 'agendaWeek', // 议程周视图
agendaDay: 'agendaDay' // 议程天视图
}
/* 定义默认视图 */
const defaultView = calendarViewType.month
/* 定义日历默认配置 */
const defaultSettings = {
locale: 'zh-cn',
// 按钮文字
buttonText: {
today: '今天',
month: '月',
week: '周',
day: '日'
},
// 头部排列方式
header: {
left: 'prev,next, today',
center: 'title',
right: 'hide, custom, month,agendaWeek,agendaDay'
},
// 点击今天日列表图
eventLimitClick: 'day',
// 隐藏超出的事件
eventLimit: true,
// 设置每周开始日期为周日
firstDay: 0,
// 默认显示视图
defaultView,
timeFormat: 'H:mm',
axisFormat: 'H:mm',
// agenda视图下是否显示all-day
allDaySlot: true,
// agenda视图下all-day的显示文本
allDayText: '全天',
// 时区默认本地的
timezone: 'local',
// 周视图和日视同的左侧时间显示
slotLabelFormat: 'HH:mm',
// 设置第二天阈值
nextDayThreshold: '00:00:00'
}
/** 提供了一些增强方法 */
const CalendarMixins = {
data () {
return {
calenderCurrentViewType: defaultView
}
},
methods: {
getCalendarConfigEventHandler () {
return {
// 处理 view changed 事件
viewRender: (view, element) => {
const { type } = view
const lastViewType = this.calenderCurrentViewType
this.calenderCurrentViewType = type
if (typeof this.handleViewRender === 'function') {
this.handleViewRender(type, view, element)
}
if (lastViewType !== this.calenderCurrentViewType && typeof this.handleViewChanged === 'function') {
this.handleViewChanged(type, view, element)
}
}
}
},
/** 获取 LunarFullCalendar 实例,ref = baseCalendar */
getCalendar (fn) {
return getRefPromise(this, 'baseCalendar').then(fn)
},
calendarEmit (name, data) {
this.getCalendar(ref => ref.$emit(name, data))
},
/** 强制重新加载所有的事件(日程) */
calendarReloadEvents () {
this.calendarEmit('reload-events')
}
}
}
export { defaultSettings, calendarViewType, CalendarMixins }
+248
View File
@@ -0,0 +1,248 @@
import { SYS_BUTTON_AUTH, USER_AUTH } from '@/store/mutation-types'
export function disabledAuthFilter (code, formData) {
if (nodeDisabledAuth(code, formData)) {
return true
} else {
return globalDisabledAuth(code)
}
}
function nodeDisabledAuth (code, formData) {
let permissionList = []
try {
if (formData) {
const bpmList = formData.permissionList
permissionList = bpmList.filter(item => item.type + '' === '2')
// for (let bpm of bpmList) {
// if(bpm.type == '2') {
// permissionList.push(bpm);
// }
// }
} else {
return false
}
} catch (e) {
// console.log("页面权限异常----", e);
}
if (permissionList.length === 0) {
return false
}
console.log('流程节点页面权限禁用--NODE--开始')
const permissions = []
for (const item of permissionList) {
if (item.type + '' === '2') {
permissions.push(item.action)
}
}
// console.log("页面权限----"+code);
if (!permissions.includes(code)) {
return false
} else {
for (const item2 of permissionList) {
if (code === item2.action) {
console.log('流程节点页面权限禁用--NODE--生效')
return true
}
}
}
return false
}
function globalDisabledAuth (code) {
const permissionList = []
const allPermissionList = []
// let authList = Vue.ls.get(USER_AUTH);
const authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || '[]')
for (const auth of authList) {
if (auth.type + '' === '2') {
permissionList.push(auth)
}
}
// console.log("页面禁用权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
const allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || '[]')
for (const gauth of allAuthList) {
if (gauth.type + '' === '2') {
allPermissionList.push(gauth)
}
}
// 设置全局配置是否有命中
let gFlag = false// 禁用命中
let invalidFlag = false// 无效命中
if (allPermissionList && allPermissionList.length > 0) {
for (const itemG of allPermissionList) {
if (code === itemG.action) {
if (itemG.status + '' === '0') {
invalidFlag = true
break
} else {
gFlag = true
break
}
}
}
}
if (invalidFlag) {
return false
}
if (permissionList === null || permissionList === '' || permissionList === undefined || permissionList.length <= 0) {
return gFlag
}
const permissions = []
for (const item of permissionList) {
if (item.type + '' === '2') {
permissions.push(item.action)
}
}
// console.log("页面禁用权限----"+code);
if (!permissions.includes(code)) {
return gFlag
} else {
for (const item2 of permissionList) {
if (code === item2.action) {
gFlag = false
}
}
return gFlag
}
}
export function colAuthFilter (columns, pre) {
const authList = getNoAuthCols(pre)
return columns.filter(item => {
return hasColoum(item, authList)
})
}
/**
* 【子表行编辑】实现两个功能:
* 1、隐藏JEditableTable无权限的字段
* 2、禁用JEditableTable无权限的字段
* @param columns
* @param pre
* @returns {*}
*/
export function colAuthFilterJEditableTable (columns, pre) {
const authList = getAllShowAndDisabledAuthCols(pre)
return columns.filter(item => {
let oneAuth = authList.find(auth => {
return auth.action === pre + item.key
})
if (!oneAuth) {
return true
}
// 代码严谨处理,防止一个授权标识,配置多次
if (oneAuth instanceof Array) {
oneAuth = oneAuth[0]
}
// 禁用逻辑
if (oneAuth.type + '' === '2' && !oneAuth.isAuth) {
item.disabled = true
return true
}
// 隐藏逻辑逻辑
return !(oneAuth.type + '' === '1' && !oneAuth.isAuth)
})
}
function hasColoum (item, authList) {
return !authList.includes(item.dataIndex)
}
// 权限无效时不做控制,有效时控制,只能控制 显示不显示
// 根据授权码前缀获取未授权的列信息
export function getNoAuthCols (pre) {
if (!pre || pre.length === 0) {
return []
}
const permissionList = []
const allPermissionList = []
// let authList = Vue.ls.get(USER_AUTH);
const authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || '[]')
for (const auth of authList) {
// 显示策略,有效状态
if (auth.type + '' === '1' && startWith(auth.action, pre)) {
permissionList.push(substrPre(auth.action, pre))
}
}
// console.log("页面禁用权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
const allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || '[]')
for (const gauth of allAuthList) {
// 显示策略,有效状态
if (gauth.type + '' === '1' && gauth.status + '' === '1' && startWith(gauth.action, pre)) {
allPermissionList.push(substrPre(gauth.action, pre))
}
}
return allPermissionList.filter(item => {
return !permissionList.includes(item)
})
}
/**
* 将Online的行编辑按钮权限,添加至本地存储
*/
export function addOnlineBtAuth2Storage (pre, authList) {
const allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || '[]')
const newAuthList = allAuthList.filter(item => {
if (!item.action) {
return true
}
return item.action.indexOf(pre) < 0
})
if (authList && authList.length > 0) {
for (const item of authList) {
newAuthList.push({
action: pre + item,
type: 1,
status: 1
})
}
const temp = JSON.parse(sessionStorage.getItem(USER_AUTH) || '[]')
const newArr = temp.filter(item => {
if (!item.action) {
return true
}
return item.action.indexOf(pre) < 0 || authList.indexOf(item.action.replace(pre, '')) < 0
})
sessionStorage.setItem(USER_AUTH, JSON.stringify(newArr))
}
sessionStorage.setItem(SYS_BUTTON_AUTH, JSON.stringify(newAuthList))
}
/**
* 额外增加方法【用于行编辑组件】
* date: 2020-04-05
* author: scott
* @param pre
* @returns {*[]}
*/
function getAllShowAndDisabledAuthCols (pre) {
// 用户拥有的权限
const userAuthList = JSON.parse(sessionStorage.getItem(USER_AUTH) || '[]')
// 全部权限配置
const allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || '[]')
return allAuthList.map(function (item) {
const hasAuthArray = userAuthList.filter(u => u.action === item.action)
if (hasAuthArray && hasAuthArray.length > 0) {
item.isAuth = true
}
return item
})
}
function startWith (str, pre) {
if (pre == null || pre + '' === '' || str == null || str + '' === '' || str.length === 0 || pre.length > str.length) {
return false
}
return str.substr(0, pre.length) + '' === pre + ''
}
function substrPre (str, pre) {
return str.substr(pre.length)
}
+37
View File
@@ -0,0 +1,37 @@
const VueAxios = {
vm: {},
// eslint-disable-next-line no-unused-vars
install (Vue, router = {}, instance) {
if (this.installed) {
return
}
this.installed = true
if (!instance) {
// eslint-disable-next-line no-console
console.error('You have to install axios')
return
}
Vue.axios = instance
Object.defineProperties(Vue.prototype, {
axios: {
get: function get () {
return instance
}
},
$http: {
get: function get () {
return instance
}
}
})
}
}
export {
VueAxios
// eslint-disable-next-line no-undef
// instance as axios
}
+37
View File
@@ -0,0 +1,37 @@
// 判断是否IE<11浏览器
export function isIE () {
return navigator.userAgent.indexOf('compatible') > -1 && navigator.userAgent.indexOf('MSIE') > -1
}
export function isIE11 () {
return navigator.userAgent.indexOf('Trident') > -1 && navigator.userAgent.indexOf('rv:11.0') > -1
}
// 判断是否IE的Edge浏览器
export function isEdge () {
return navigator.userAgent.indexOf('Edge') > -1 && !isIE()
}
export function getIEVersion () {
const userAgent = navigator.userAgent // 取得浏览器的userAgent字符串
const isIEFlag = isIE()
const isIE11Flag = isIE11()
const isEdgeFlag = isEdge()
if (isIEFlag) {
const reIE = new RegExp('MSIE (\\d+\\.\\d+);')
reIE.test(userAgent)
const fIEVersion = parseFloat(RegExp.$1)
if (fIEVersion === 7 || fIEVersion === 8 || fIEVersion === 9 || fIEVersion === 10) {
return fIEVersion
} else {
return 6// IE版本<7
}
} else if (isEdgeFlag) {
return 'edge'
} else if (isIE11Flag) {
return 11
} else {
return -1
}
}
+52
View File
@@ -0,0 +1,52 @@
import { getFileAccessHttpUrl } from '@/api/manage'
const getFileName = (path) => {
if (path.lastIndexOf('\\') >= 0) {
const reg = new RegExp('\\\\', 'g')
path = path.replace(reg, '/')
}
return path.substring(path.lastIndexOf('/') + 1)
}
const uidGenerator = () => {
return '-' + parseInt(Math.random() * 10000 + 1, 10)
}
const getFilePaths = (uploadFiles) => {
const arr = []
if (!uploadFiles) {
return ''
}
for (let a = 0; a < uploadFiles.length; a++) {
arr.push(uploadFiles[a].response.message)
}
if (arr && arr.length > 0) {
return arr.join(',')
}
return ''
}
const getUploadFileList = (paths) => {
if (!paths) {
return []
}
const fileList = []
const arr = paths.split(',')
for (let a = 0; a < arr.length; a++) {
if (!arr[a]) {
continue
} else {
fileList.push({
uid: uidGenerator(),
name: getFileName(arr[a]),
status: 'done',
url: getFileAccessHttpUrl(arr[a]),
response: {
status: 'history',
message: arr[a]
}
})
}
}
return fileList
}
export { getFilePaths, getUploadFileList }
@@ -0,0 +1,22 @@
/*
*
* 这里填写用户自定义的表达式
* 可用在Online表单的默认值表达式中使用
* 需要外部使用的变量或方法一定要 export,否则无法识别
* 示例:
* export const name = '张三'; // const 是常量
* export let age = 17; // 看情况 export const 还是 let ,两者都可正常使用
* export function content(arg) { // export 方法,可传参数,使用时要加括号,值一定要return回去,可以返回Promise
* return 'content' + arg;
* }
* export const address = (arg) => content(arg) + ' | 北京市'; // export 箭头函数也可以
*
*/
/** 字段默认值官方示例:获取地址 */
export function demoFieldDefValGetAddress (arg) {
if (!arg) {
arg = '朝阳区'
}
return `北京市 ${arg}`
}
+23
View File
@@ -0,0 +1,23 @@
import enquireJs from 'enquire.js'
const enquireScreen = function (call) {
// tablet
const handler = {
match: function () {
call && call(0)
},
unmatch: function () {
call && call(-1)
}
}
// mobile
const handler2 = {
match: () => {
call && call(1)
}
}
enquireJs.register('screen and (max-width: 1087.99px)', handler)
enquireJs.register('screen and (max-width: 767.99px)', handler2)
}
export default enquireScreen
+16
View File
@@ -0,0 +1,16 @@
import { axios } from '@/utils/request'
/**
* 获取RSA公钥
* @returns {*}
*/
export function getRSAPublicKey () {
return axios({
url: `/sys/getRSAPublicKey`,
method: 'get',
timeout: 5000,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,128 @@
import md5 from 'md5'
// 签名密钥串(前后端要一致,正式发布请自行修改)
const signatureSecret = 'dd05f1c54d63749eda95f9fa6d49v442a'
export default class signMd5Utils {
/**
* json参数升序
* @param jsonObj 发送参数
*/
static sortAsc (jsonObj) {
const arr = []
let num = 0
for (const i in jsonObj) {
arr[num] = i
num++
}
const sortArr = arr.sort()
const sortObj = {}
for (const i in sortArr) {
sortObj[sortArr[i]] = jsonObj[sortArr[i]]
}
return sortObj
}
/**
* @param url 请求的url,应该包含请求参数(url的?后面的参数)
* @param requestParams 请求参数(POST的JSON参数)
* @returns {string} 获取签名
*/
static getSign (url, requestParams) {
const urlParams = this.parseQueryString(url)
const jsonObj = this.mergeObject(urlParams, requestParams)
// console.log("sign jsonObj: ",jsonObj)
const requestBody = this.sortAsc(jsonObj)
console.log('sign requestBody: ', requestBody)
return md5(JSON.stringify(requestBody) + signatureSecret).toUpperCase()
}
/**
* @param url 请求的url
* @returns {{}} 将url中请求参数组装成json对象(url的?后面的参数)
*/
static parseQueryString (url) {
const urlReg = /^[^\?]+\?([\w\W]+)$/
const paramReg = /([^&=]+)=([\w\W]*?)(&|$|#)/g
const urlArray = urlReg.exec(url)
const result = {}
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
// 【这边条件没有encode】带条件参数例子:/sys/dict/getDictItems/sys_user,realname,id,username!='admin'%20order%20by%20create_time
let lastpathVariable = url.substring(url.lastIndexOf('/') + 1)
if (lastpathVariable.includes(',')) {
if (lastpathVariable.includes('?')) {
lastpathVariable = lastpathVariable.substring(0, lastpathVariable.indexOf('?'))
}
// 解决Sign 签名校验失败 #2728
result['x-path-variable'] = decodeURIComponent(lastpathVariable)
}
if (urlArray && urlArray[1]) {
const paramString = urlArray[1]; let paramResult
while ((paramResult = paramReg.exec(paramString)) != null) {
// 数字值转为string类型,前后端加密规则保持一致
if (this.myIsNaN(paramResult[2])) {
paramResult[2] = paramResult[2].toString()
}
result[paramResult[1]] = paramResult[2]
}
}
return result
}
/**
* @returns {*} 将两个对象合并成一个
*/
static mergeObject (objectOne, objectTwo) {
if (objectTwo && Object.keys(objectTwo).length > 0) {
for (const key in objectTwo) {
if (Object.prototype.hasOwnProperty.call(objectTwo, key) === true) {
// 数字值转为string类型,前后端加密规则保持一致
if (this.myIsNaN(objectTwo[key])) {
objectTwo[key] = objectTwo[key].toString()
}
objectOne[key] = objectTwo[key]
}
}
}
return objectOne
}
static urlEncode (param, key, encode) {
if (param == null) return ''
let paramStr = ''
const t = typeof (param)
if (t === 'string' || t === 'number' || t === 'boolean') {
paramStr += '&' + key + '=' + ((encode == null || encode) ? encodeURIComponent(param) : param)
} else {
for (const i in param) {
const k = key == null ? i : key + (param instanceof Array ? '[' + i + ']' : '.' + i)
paramStr += this.urlEncode(param[i], k, encode)
}
}
return paramStr
};
static getDateTimeToString () {
const date_ = new Date()
const year = date_.getFullYear()
let month = date_.getMonth() + 1
let day = date_.getDate()
if (month < 10) month = '0' + month
if (day < 10) day = '0' + day
let hours = date_.getHours()
let mins = date_.getMinutes()
let secs = date_.getSeconds()
const msecs = date_.getMilliseconds()
if (hours < 10) hours = '0' + hours
if (mins < 10) mins = '0' + mins
if (secs < 10) secs = '0' + secs
if (msecs < 10) secs = '0' + msecs
return year + '' + month + '' + day + '' + hours + '' + mins + '' + secs
}
// true:数值型的,false:非数值型
static myIsNaN (value) {
return typeof value === 'number' && !isNaN(value)
}
}
+29
View File
@@ -0,0 +1,29 @@
import Vue from 'vue'
import * as dayjs from 'dayjs'
Vue.filter('NumberFormat', function (value) {
if (!value) {
return '0'
}
// 将整数部分逢三一断
return value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
})
Vue.filter('dayjs', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return dayjs(dataStr).format(pattern)
})
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return dayjs(dataStr).format(pattern)
})
/** 字符串超长截取省略号显示 */
Vue.filter('ellipsis', function (value, vlength = 25) {
if (!value) {
return ''
}
if (value.length > vlength) {
return value.slice(0, vlength) + '...'
}
return value
})
+196
View File
@@ -0,0 +1,196 @@
import { USER_AUTH, SYS_BUTTON_AUTH } from '@/store/mutation-types'
const hasPermission = {
install (Vue) {
Vue.directive('has', {
inserted: (el, binding, vnode) => {
// console.time()
// 节点权限处理,如果命中则不进行全局权限处理
if (!filterNodePermission(el, binding, vnode)) {
filterGlobalPermission(el, binding, vnode)
}
}
})
}
}
/**
* 流程节点权限控制
*/
export function filterNodePermission (el, binding, vnode) {
const permissionList = []
try {
const obj = vnode.context.$props.formData
if (obj) {
const bpmList = obj.permissionList
for (const bpm of bpmList) {
if (bpm.type + '' !== '2') {
permissionList.push(bpm)
}
}
} else {
return false
}
} catch (e) {
// console.log("页面权限异常----", e);
}
if (permissionList === null || permissionList === '' || permissionList === undefined || permissionList.length <= 0) {
// el.parentNode.removeChild(el)
return false
}
console.log('流程节点页面权限--NODE--')
const permissions = []
for (const item of permissionList) {
if (item.type + '' !== '2') {
permissions.push(item.action)
}
}
// console.log("页面权限----"+permissions);
// console.log("页面权限----"+binding.value);
if (!permissions.includes(binding.value)) {
// el.parentNode.removeChild(el)
return false
} else {
for (const item2 of permissionList) {
if (binding.value === item2.action) {
return true
}
}
}
return false
}
/**
* 全局权限控制
*/
export function filterGlobalPermission (el, binding) {
const permissionList = []
const allPermissionList = []
// let authList = Vue.ls.get(USER_AUTH);
const authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || '[]')
for (const auth of authList) {
if (auth.type + '' !== '2') {
permissionList.push(auth)
}
}
// console.log("页面权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
const allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || '[]')
for (const gauth of allAuthList) {
if (gauth.type + '' !== '2') {
allPermissionList.push(gauth)
}
}
// 设置全局配置是否有命中
let invalidFlag = false// 无效命中
if (allPermissionList && allPermissionList.length > 0) {
for (const itemG of allPermissionList) {
if (binding.value === itemG.action) {
if (itemG.status + '' === '0') {
invalidFlag = true
break
}
}
}
}
if (invalidFlag) {
return
}
if (permissionList === null || permissionList === '' || permissionList === undefined || permissionList.length <= 0) {
el.parentNode.removeChild(el)
return
}
const permissions = []
for (const item of permissionList) {
// 权限策略1显示2禁用
if (item.type + '' !== '2') {
// update--begin--autor:wangshuai-----date:20200729------for:按钮权限,授权标识的提示信息是多个用逗号分隔逻辑处理 gitee#I1OUGU-------
if (item.action) {
if (item.action.includes(',')) {
const split = item.action.split(',')
for (let i = 0; i < split.length; i++) {
if (!split[i] || split[i].length === 0) {
continue
}
permissions.push(split[i])
}
} else {
permissions.push(item.action)
}
}
// update--end--autor:wangshuai-----date:20200729------for:按钮权限,授权标识的提示信息是多个用逗号分隔逻辑处理 gitee#I1OUGU------
}
}
if (!permissions.includes(binding.value)) {
el.parentNode.removeChild(el)
}
}
/**
* 根据授权标识是否拥有该权限,用于tab页签的权限
*/
export function isHasPermission (permissionIdentifier) {
const permissionList = []
const allPermissionList = []
// let authList = Vue.ls.get(USER_AUTH);
const authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || '[]')
for (const auth of authList) {
if (auth.type + '' !== '2') {
permissionList.push(auth)
}
}
// console.log("页面权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
const allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || '[]')
for (const gauth of allAuthList) {
if (gauth.type + '' !== '2') {
allPermissionList.push(gauth)
}
}
// 设置全局配置是否有命中
let invalidFlag = false// 无效命中
if (allPermissionList && allPermissionList.length > 0) {
for (const itemG of allPermissionList) {
if (permissionIdentifier === itemG.action) {
if (itemG.status + '' === '0') {
invalidFlag = true
break
}
}
}
}
if (invalidFlag) {
return
}
if (permissionList === null || permissionList === '' || permissionList === undefined || permissionList.length <= 0) {
return false
}
const permissions = []
for (const item of permissionList) {
// 权限策略1显示2禁用
if (item.type + '' !== '2') {
// update--begin--autor:wangshuai-----date:20200729------for:按钮权限,授权标识的提示信息是多个用逗号分隔逻辑处理 gitee#I1OUGU-------
if (item.action) {
if (item.action.includes(',')) {
const split = item.action.split(',')
for (let i = 0; i < split.length; i++) {
if (!split[i] || split[i].length === 0) {
continue
}
permissions.push(split[i])
}
} else {
permissions.push(item.action)
}
}
// update--end--autor:wangshuai-----date:20200729------for:按钮权限,授权标识的提示信息是多个用逗号分隔逻辑处理 gitee#I1OUGU------
}
}
if (!permissions.includes(permissionIdentifier)) {
return false
}
return true
}
export default hasPermission
+40
View File
@@ -0,0 +1,40 @@
import Vue from 'vue'
import { USER_INFO } from '../store/mutation-types'
import { Base64 } from 'js-base64'
import md5 from 'md5'
function fillZero (str) {
let realNum
if (str < 10) {
realNum = '0' + str
} else {
realNum = str
}
return realNum
}
export function dealUrl (fileUrl) {
const userInfo = Vue.ls.get(USER_INFO)
// 获取当前时间
const date = new Date()
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
const time = `${year}${fillZero(month)}${fillZero(day)}${fillZero(hour)}${fillZero(minute)}${fillZero(second)}`
// 水印内容
const watermarkTxt = `${userInfo.username} ${userInfo.realname} ${time}`
// 客户环境预览需要加的加密参数
const watermarkSign = md5(watermarkTxt + 'cnhtc')
return `${window._CONFIG.onlinePreviewDomainURL}?url=${encodeURIComponent(Base64.encode(fileUrl))}&watermarkTxt=${encodeURIComponent(watermarkTxt)}&watermarkSign=${watermarkSign}`
}
/**
* kkFile预览文件并添加水印
* @param fileUrl
*/
export const kkFilePreview = (fileUrl) => {
window.open(dealUrl(fileUrl))
}
+40
View File
@@ -0,0 +1,40 @@
// import Vue from 'vue'
import { mapState } from 'vuex'
// const mixinsComputed = Vue.config.optionMergeStrategies.computed
// const mixinsMethods = Vue.config.optionMergeStrategies.methods
const mixin = {
computed: {
...mapState({
layoutMode: state => state.app.layout,
navTheme: state => state.app.theme,
primaryColor: state => state.app.color,
colorWeak: state => state.app.weak,
multipage: state => state.app.multipage, // 多页签设置
fixedHeader: state => state.app.fixedHeader,
fixSiderbar: state => state.app.fixSiderbar,
contentWidth: state => state.app.contentWidth,
autoHideHeader: state => state.app.autoHideHeader,
sidebarOpened: state => state.app.sidebar.opened
})
}
}
const mixinDevice = {
computed: {
...mapState({
device: state => state.app.device
})
},
methods: {
isMobile () {
return this.device === 'mobile'
},
isDesktop () {
return this.device === 'desktop'
}
}
}
export { mixin, mixinDevice }
+82
View File
@@ -0,0 +1,82 @@
import { generateEmptyFileTemplate, queryFileInfoByEditId } from '../api/workCenter'
import { USER_INFO } from '../store/mutation-types'
import Vue from 'vue'
/**
* 获取文件信息
* @param fileId
* @returns {Promise<unknown>}
*/
const queryFileInfo = (fileId) => {
return new Promise(resolve => {
let fileInfo
queryFileInfoByEditId({ id: fileId }).then(res => {
if (res.success) {
fileInfo = res.result
}
}).finally(() => {
resolve(fileInfo)
})
})
}
/**
* 生成空白模板并获取模板路径
* @returns {Promise<unknown>}
*/
const getEmptyFileTemplate = (params) => {
return new Promise(resolve => {
let fileInfo
generateEmptyFileTemplate(params).then(res => {
if (res.success) {
fileInfo = res.result
}
}).finally(() => {
resolve(fileInfo)
})
})
}
/**
* 跳转在线编辑
* @param params Object {
* standard_number: 标准编号,
* substitute_standard_number: 代替标准号,
* implementation_date: 实施日期,
* release_date: 发布日期,
* standard_name: 标准名称
* }
* @param fileId 文件id,有的话是编辑,没有是新增
*/
export const onlineEditor = async (params, fileId) => {
let fileInfo
if (fileId) {
fileInfo = await queryFileInfo(fileId)
} else {
const param = {
model: `${params.standard_number} ${params.standard_name}` // 处理文件名
}
fileInfo = await getEmptyFileTemplate(param)
}
if (!fileInfo) {
return false
}
// 获取用户信息
const userInfo = Vue.ls.get(USER_INFO)
// 截取文件后缀名
const fileSuffix = (fileInfo.fileName ? fileInfo.fileName.split('.')[fileInfo.fileName.split('.').length - 1] : '').toLowerCase()
// Q/ZZ 部分截取
const standardNumberPrefix = params.standard_number ? params.standard_number.split(' ')[0] : null
const businessParams = {
standCode: params.standard_number,
dtqbbhbuss: params.substitute_standard_number,
standName: params.standard_name,
standEnName: params.standard_english_name,
issueTime: params.release_date,
putTime: params.implementation_date,
standardNumberPrefix: standardNumberPrefix
}
const url = `${window._CONFIG.onlyOfficeUrl}/editorPage?filePath=${fileInfo.editFilePath}&fileName=${fileInfo.fileName}&fileType=${fileSuffix}&fileId=${fileInfo.id}&key=${fileInfo.id}&userId=${userInfo.id}&userName=${userInfo.realname}&business=${JSON.stringify(businessParams)}`
window.open(url, '_blank')
return fileInfo.id
}
+8
View File
@@ -0,0 +1,8 @@
export function actionToObject (json) {
try {
return JSON.parse(json)
} catch (e) {
console.log('err', e.message)
}
return []
}
+14
View File
@@ -0,0 +1,14 @@
import { ACCESS_TOKEN } from '@/store/mutation-types'
import Vue from 'vue'
const getToken = () => Vue.ls.get(ACCESS_TOKEN)
// 预览pdf
const previewPdf = (id) => {
const url = `${window._CONFIG.domianURL}/sys/common/view/${id}?type=view&at=${getToken()}`
return `${process.env.BASE_URL}pdfjs/web/viewer.html?file=` + encodeURIComponent(url) + '&.pdf'
}
export {
previewPdf
}
+112
View File
@@ -0,0 +1,112 @@
/**
* 该文件截取自 "ant-design-vue/es/_util/props-util.js" 文件,并对其做出特殊修改
*/
function classNames () {
const classes = []
for (let i = 0; i < arguments.length; i++) {
const arg = arguments[i]
if (!arg) continue
const argType = typeof arg
if (argType === 'string' || argType === 'number') {
classes.push(arg)
} else if (Array.isArray(arg) && arg.length) {
const inner = classNames.apply(null, arg)
if (inner) {
classes.push(inner)
}
} else if (argType === 'object') {
for (const key in arg) {
if (Object.prototype.hasOwnProperty.call(arg, key) && arg[key]) {
classes.push(key)
}
}
}
}
return classes.join(' ')
}
const camelizeRE = /-(\w)/g
function camelize (str) {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
}
function objectCamelize (obj) {
const res = {}
Object.keys(obj).forEach(k => (res[camelize(k)] = obj[k]))
return res
}
function parseStyleText (cssText = '', camel) {
const res = {}
const listDelimiter = /;(?![^(]*\))/g
const propertyDelimiter = /:(.+)/
cssText.split(listDelimiter).forEach(function (item) {
if (item) {
const tmp = item.split(propertyDelimiter)
if (tmp.length > 1) {
const k = camel ? camelize(tmp[0].trim()) : tmp[0].trim()
res[k] = tmp[1].trim()
}
}
})
return res
}
export function getClass (ele) {
let data = {}
if (ele.data) {
data = ele.data
} else if (ele.$vnode && ele.$vnode.data) {
data = ele.$vnode.data
}
const tempCls = data.class || {}
const staticClass = data.staticClass
let cls = {}
staticClass &&
staticClass.split(' ').forEach(c => {
cls[c.trim()] = true
})
if (typeof tempCls === 'string') {
tempCls.split(' ').forEach(c => {
cls[c.trim()] = true
})
} else if (Array.isArray(tempCls)) {
classNames(tempCls)
.split(' ')
.forEach(c => {
cls[c.trim()] = true
})
} else {
cls = { ...cls, ...tempCls }
}
return cls
}
export function getStyle (ele, camel) {
getClass(ele)
let data = {}
if (ele.data) {
data = ele.data
} else if (ele.$vnode && ele.$vnode.data) {
data = ele.$vnode.data
}
// update-begin-author:sunjianlei date:20200303 for: style 和 staticStyle 可以共存
let style = data.style || {}
let staticStyle = data.staticStyle
staticStyle = staticStyle ? objectCamelize(data.staticStyle) : {}
// update-end-author:sunjianlei date:20200303 for: style 和 staticStyle 可以共存
if (typeof style === 'string') {
style = parseStyleText(style, camel)
} else if (camel && style) {
// 驼峰化
style = objectCamelize(style)
}
return { ...staticStyle, ...style }
}
+184
View File
@@ -0,0 +1,184 @@
import Vue from 'vue'
import axios from 'axios'
import store from '@/store'
import { VueAxios } from './axios'
import router from '@/router/index'
import { ACCESS_TOKEN, TENANT_ID } from '@/store/mutation-types'
import { Modal } from 'ant-design-vue'
/**
* 【指定 axios的 baseURL】
* 如果手工指定 baseURL: '/laws-sinotruk'
* 则映射后端域名,通过 vue.config.js
* @type {*|string}
*/
const apiBaseUrl = window._CONFIG.domianURL || '/laws-sinotruk'
// console.log("apiBaseUrl= ",apiBaseUrl)
// 创建 axios 实例
const service = axios.create({
// baseURL: '/laws-sinotruk',
baseURL: apiBaseUrl, // api base_url
timeout: 30 * 60 * 1000 // 请求超时时间
})
const err = (error) => {
if (error.response) {
const data = error.response.data
const token = Vue.ls.get(ACCESS_TOKEN)
console.log('------异常响应------', token)
console.log('------异常响应------', error.response.status)
switch (error.response.status) {
case 403:
Vue.prototype.$Jnotification.error({ message: '系统提示', description: '拒绝访问', duration: 4 })
break
case 500:
console.log('------error.response------', error.response)
// update-begin- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
if (error.response.request.responseType === 'blob') {
blobToJson(data)
break
}
// update-end- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
if (data.includes('Token失效')) {
// update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
// if (/wxwork|dingtalk/i.test(navigator.userAgent)) {
// Vue.prototype.$Jmessage.loading('登录已过期,正在重新登陆', 0)
// } else {
// Vue.prototype.$Jmodal.error({
// title: '登录已过期',
// content: '很抱歉,登录已过期,请重新登录',
// okText: '重新登录',
// mask: false,
// onOk: () => {
// store.dispatch('Logout')
// }
// })
// }
// update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
}
break
case 404:
Vue.prototype.$Jnotification.error({ message: '系统提示', description: '很抱歉,资源未找到!', duration: 4 })
break
case 504:
Vue.prototype.$Jnotification.error({ message: '系统提示', description: '网络超时' })
break
case 401:
Vue.prototype.$Jnotification.error({ message: '系统提示', description: '未授权,请重新登录', duration: 4 })
if (token) {
store.dispatch('Logout').then(() => {
setTimeout(() => {
window.location.reload()
}, 1500)
})
}
break
default:
Vue.prototype.$Jnotification.error({
message: '系统提示',
description: data.message,
duration: 4
})
break
}
} else if (error.message) {
if (error.message.includes('timeout')) {
Vue.prototype.$Jnotification.error({ message: '系统提示', description: '网络超时' })
} else {
Vue.prototype.$Jnotification.error({ message: '系统提示', description: error.message })
}
}
return Promise.reject(error)
}
// request interceptor
service.interceptors.request.use(config => {
const token = Vue.ls.get(ACCESS_TOKEN)
if (token) {
config.headers['X-Access-Token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
}
// update-begin--author:sunjianlei---date:20200723---for 如果当前在low-app环境,并且携带了appId,就向Header里传递appId
const $route = router.currentRoute
if ($route && $route.name && $route.name.startsWith('low-app') && $route.params.appId) {
config.headers['X-Low-App-ID'] = $route.params.appId
}
// update-end--author:sunjianlei---date:20200723---for 如果当前在low-app环境,并且携带了appId,就向Header里传递appId
// update-begin-author:taoyan date:2020707 for:多租户
let tenantid = Vue.ls.get(TENANT_ID)
if (!tenantid) {
tenantid = 0
}
config.headers['tenant-id'] = tenantid
let language = localStorage.getItem('language') || ''
if (!language) {
language = 'zh-cn'
}
const cut = language === 'zh-cn' ? 'zh' : 'en'
config.headers.Language = cut
// update-end-author:taoyan date:2020707 for:多租户
if (config.method === 'get') {
if (config.url.indexOf('sys/dict/getDictItems') < 0) {
config.params = {
_t: Date.parse(new Date()) / 1000,
...config.params
}
}
}
return config
}, (error) => {
return Promise.reject(error)
})
// response interceptor
service.interceptors.response.use((response) => {
return response.data
}, err)
const installer = {
vm: {},
install (Vue, router = {}) {
Vue.use(VueAxios, router, service)
}
}
/**
* Blob解析
* @param data
*/
function blobToJson (data) {
const fileReader = new FileReader()
const token = Vue.ls.get(ACCESS_TOKEN)
fileReader.onload = function () {
try {
const jsonData = JSON.parse(this.result) // 说明是普通对象数据,后台转换失败
console.log('jsonData', jsonData)
if (jsonData.status === 500) {
console.log('token----------》', token)
if (token && jsonData.message.includes('Token失效')) {
Modal.error({
title: '登录已过期',
content: '很抱歉,登录已过期,请重新登录',
okText: '重新登录',
mask: false,
onOk: () => {
store.dispatch('Logout').then(() => {
Vue.ls.remove(ACCESS_TOKEN)
window.location.reload()
})
}
})
}
}
} catch (err) {
// 解析成对象失败,说明是正常的文件流
console.log('blob解析fileReader返回err', err)
}
}
fileReader.readAsText(data)
}
export {
installer as VueAxios,
service as axios
}
+106
View File
@@ -0,0 +1,106 @@
const validateMobile = (rule, value, callback) => {
const reg = /^1(3|4|5|7|8)\d{9}$/
if (!reg.test(value)) {
callback(new Error('请输入正确手机号'))
} else {
callback()
}
}
const validateEn = (rule, value, callback) => {
const reg = /^[_a-zA-Z0-9]+$/
const reg2 = /^.{4,18}$/
// 长度为6到18个字符
if (value !== '' && !reg.test(value)) {
callback(new Error('只允许字母、数字、下划线'))
} else if (value !== '' && !reg2.test(value)) {
callback(new Error('长度6到18个字符'))
} else {
callback()
}
}
export const rules = {
mobile: [{
required: true,
message: '请输入手机号',
trigger: 'blur'
}, { validator: validateMobile, trigger: 'blur' }],
userName: [{
required: true, message: '请输入用户名', trigger: 'blur'
}, { validator: validateEn }],
email: [
{ required: false, type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
],
// 验证自然数
naturalNumber: /^(([0-9]*[1-9][0-9]*)|(0+))$/,
naturalNumberMsg: '请输入自然数',
// 英文
english: /^.[A-Za-z]+$/,
englishMsg: '请输入英文字符',
// 座机
telephone: /^\d{3}-\d{7,8}|\d{4}-\d{7,8}$/,
telephoneMsg: '请输入正确的座机号',
// 银行卡号码
bankCard: /^[1-9]\d{9,19}$/,
bankCardMsg: '请输入正确的银行卡号码',
// 证件号码
IDNumber: /^[a-z0-9A-Z]{0,50}$/,
IDNumberMsg: '请输入正确的证件号码',
// 身份证号码,包括15位和18位的
IDCard: /(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{7}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)/,
IDCardMsg: '请输入正确的身份证号码',
// QQ号码
qq: /^[1-9]\d{4,11}$/,
qqMsg: '请输入正确的QQ号码',
// 网址, 仅支持http和https开头的
url: /^(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-.,@?^=%&:/~+#]*[\w\-@?^=%&/~+#])?$/,
urlMsg: '请输入以http和https开头的网址',
// 0到20位的英文字符和数字
enNum0to20: /^[a-z0-9A-Z]{0,20}$/,
enNum0to20Msg: '请输入20位以内的英文字符和数字',
// 2到100位的中英文字符和空格
cnEnSpace2to100: /^[a-zA-Z\u4E00-\u9FA5\s*]{2,100}$/,
cnEnSpace2to100Msg: '请输入2到100位的中英文字符和空格',
// 数字和换行符
numLinefeed: /^[0-9\n*]+$/,
numLinefeedMsg: '请输入数字和换行符',
// 255位以内的字符
char0to255: /^.{0,255}$/,
char0to255Msg: '请输入255位以内的字符',
required: function (min, max) {
const rule = [{ required: true, message: '', trigger: 'blur' }]
if (min) {
const r = { min: min, message: '最小长度' + min + '位字符' }
rule.push(r)
}
if (max) {
const m = { max: max, message: '最大长度' + max + '位字符' }
rule.push(m)
}
return rule
},
select: function () {
return [{ required: true, message: '', trigger: 'change' }]
},
checked: function (min, max) {
const rule = [{ required: true, type: 'array', message: '', trigger: 'change' }]
if (min) {
const r = { type: 'array', min: min, message: '最少选择' + min + '项' }
rule.push(r)
}
if (max) {
const m = { type: 'array', max: max, message: '最多选择' + max + '项' }
rule.push(m)
}
return rule
}
}
/**
* @description 排序值验证,排序值不可以大于255
*/
export const validateOrder = function (rule, value, callback) {
if (parseInt(value) > 255) {
return callback(new Error('排序值不可以大于255'))
} else {
callback()
}
}
+77
View File
@@ -0,0 +1,77 @@
/**
* Set storage
*
* @param name
* @param content
* @param maxAge
*/
export const setStore = (name, content, maxAge = null) => {
if (!global.window || !name) {
return
}
if (typeof content !== 'string') {
content = JSON.stringify(content)
}
const storage = global.window.localStorage
storage.setItem(name, content)
if (maxAge && !isNaN(parseInt(maxAge))) {
const timeout = parseInt(new Date().getTime() / 1000)
storage.setItem(`${name}_expire`, timeout + maxAge)
}
}
/**
* Get storage
*
* @param name
* @returns {*}
*/
export const getStore = name => {
if (!global.window || !name) {
return
}
const content = window.localStorage.getItem(name)
const _expire = window.localStorage.getItem(`${name}_expire`)
if (_expire) {
const now = parseInt(new Date().getTime() / 1000)
if (now > _expire) {
return
}
}
try {
return JSON.parse(content)
} catch (e) {
return content
}
}
/**
* Clear storage
*
* @param name
*/
export const clearStore = name => {
if (!global.window || !name) {
return
}
window.localStorage.removeItem(name)
window.localStorage.removeItem(`${name}_expire`)
}
/**
* Clear all storage
*/
export const clearAll = () => {
if (!global.window || !name) {
return
}
window.localStorage.clear()
}
+717
View File
@@ -0,0 +1,717 @@
import Vue from 'vue'
import * as api from '@/api/api'
import { isURL } from '@/utils/validate'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { getFileInfo } from '@/api/api'
import { dealUrl, kkFilePreview } from './kkFilePreview'
import { previewPdf } from './previewPdf'
// import onlineCommons from '@/components/onlineForm/onlineForm'
//
export function timeFix () {
const time = new Date()
const hour = time.getHours()
return hour < 9 ? '早上好' : (hour <= 11 ? '上午好' : (hour <= 13 ? '中午好' : (hour < 20 ? '下午好' : '晚上好')))
}
export function welcome () {
const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
const index = Math.floor((Math.random() * arr.length))
return arr[index]
}
/**
* 触发 window.resize
*/
export function triggerWindowResizeEvent () {
const event = document.createEvent('HTMLEvents')
event.initEvent('resize', true, true)
event.eventType = 'message'
window.dispatchEvent(event)
}
/**
* 过滤对象中为空的属性
* @param obj
* @returns {*}
*/
export function filterObj (obj) {
if (!(typeof obj === 'object')) {
return
}
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key) && (obj[key] == null || obj[key] === undefined || obj[key] === '')) {
delete obj[key]
}
}
return obj
}
/**
* 时间格式化
* @param value
* @param fmt
* @returns {*}
*/
export function formatDate (value, fmt) {
const regPos = /^\d+(\.\d+)?$/
if (regPos.test(value)) {
// 如果是数字
const getDate = new Date(value)
const o = {
'M+': getDate.getMonth() + 1,
'd+': getDate.getDate(),
'h+': getDate.getHours(),
'm+': getDate.getMinutes(),
's+': getDate.getSeconds(),
'q+': Math.floor((getDate.getMonth() + 3) / 3),
S: getDate.getMilliseconds()
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (getDate.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
} else {
// TODO
value = value.trim()
return value.substr(0, fmt.length)
}
}
// 生成首页路由
export function generateIndexRouter (data) {
const redirectPath = findFirstRoute(data)
return [{
path: '/',
name: 'dashboard',
// component: () => import('@/components/layouts/BasicLayout'),
component: resolve => require(['@/components/layouts/TabLayout'], resolve),
meta: { title: '首页' },
redirect: redirectPath,
children: [
...generateChildRouters(data)
]
}, {
path: '*', redirect: '/404', hidden: true
}]
}
// 生成嵌套路由(子路由)
function generateChildRouters (data) {
const routers = []
for (const item of data) {
let component = ''
if (item.component.indexOf('layouts') >= 0) {
component = 'components/' + item.component
} else {
component = 'views/' + item.component
}
const URL = (item.meta.url || '').replace(/{{([^}}]+)?}}/g, (s1, s2) => evil(s2)) // URL支持{{ window.xxx }}占位符变量
if (isURL(URL) || (item.meta.url && item.meta.url.indexOf('{{') === 0)) {
item.meta.url = URL
}
let componentPath
// if (item.component === 'modules/online/cgform/OnlCgformHeadList') {
// componentPath = onlineCommons.OnlCgformHeadList
// } else if (item.component + '' === 'modules/online/cgform/OnlCgformCopyList') {
// componentPath = onlineCommons.OnlCgformCopyList
// } else if (item.component + '' === 'modules/online/cgform/auto/OnlCgformAutoList') {
// componentPath = onlineCommons.OnlCgformAutoList
// } else if (item.component + '' === 'modules/online/cgform/auto/OnlCgformTreeList') {
// componentPath = onlineCommons.OnlCgformTreeList
// } else if (item.component + '' === 'modules/online/cgform/auto/erp/OnlCgformErpList') {
// componentPath = onlineCommons.OnlCgformErpList
// } else if (item.component + '' === 'modules/online/cgform/auto/tab/OnlCgformTabList') {
// componentPath = onlineCommons.OnlCgformTabList
// } else if (item.component + '' === 'modules/online/cgform/auto/innerTable/OnlCgformInnerTableList') {
// componentPath = onlineCommons.OnlCgformInnerTableList
// } else if (item.component + '' === 'modules/online/cgreport/OnlCgreportHeadList') {
// componentPath = onlineCommons.OnlCgreportHeadList
// } else if (item.component + '' === 'modules/online/cgreport/auto/OnlCgreportAutoList') {
// componentPath = onlineCommons.OnlCgreportAutoList
// } else {
componentPath = resolve => require(['@/' + component + '.vue'], resolve)
// }
const menu = {
path: item.path,
name: item.name,
redirect: item.redirect,
component: componentPath,
// component: resolve => require(['@/' + component+'.vue'], resolve),
hidden: item.hidden,
meta: {
title: item.meta.title,
icon: item.meta.icon,
url: item.meta.url,
permissionList: item.meta.permissionList,
keepAlive: item.meta.keepAlive,
/* update_begin author:wuxianquan date:20190908 for:赋值 */
internalOrExternal: item.meta.internalOrExternal,
/* update_end author:wuxianquan date:20190908 for:赋值 */
componentName: item.meta.componentName
}
}
if (item.alwaysShow) {
menu.alwaysShow = true
menu.redirect = menu.path
}
if (item.children && item.children.length > 0) {
menu.children = [...generateChildRouters(item.children)]
}
// --update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
// 判断是否生成路由
if (item.route && item.route === '0') {
// console.log(' 不生成路由 item.route '+item.route);
// console.log(' 不生成路由 item.path '+item.path);
} else {
routers.push(menu)
}
// --update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
}
return routers
}
/**
* 深度克隆对象、数组
* @param obj 被克隆的对象
* @return 克隆后的对象
*/
export function cloneObject (obj) {
return JSON.parse(JSON.stringify(obj))
}
/**
* 随机生成数字
*
* 示例:生成长度为 12 的随机数:randomNumber(12)
* 示例:生成 3~23 之间的随机数:randomNumber(3, 23)
*
* @param1 最小值 | 长度
* @param2 最大值
* @return int 生成后的数字
*/
export function randomNumber () {
// 生成 最小值 到 最大值 区间的随机数
const random = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min)
}
if (arguments.length === 1) {
const [length] = arguments
// 生成指定长度的随机数字,首位一定不是 0
const nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
return parseInt(nums.join(''))
} else if (arguments.length >= 2) {
const [min, max] = arguments
return random(min, max)
} else {
return Number.NaN
}
}
/**
* 随机生成字符串
* @param length 字符串的长度
* @param chats 可选字符串区间(只会生成传入的字符串中的字符)
* @return string 生成的字符串
*/
export function randomString (length, chats) {
if (!length) length = 1
if (!chats) chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
let str = ''
for (let i = 0; i < length; i++) {
const num = randomNumber(0, chats.length - 1)
str += chats[num]
}
return str
}
/**
* 随机生成uuid
* @return string 生成的uuid
*/
export function randomUUID () {
const chats = '0123456789abcdef'
return randomString(32, chats)
}
/**
* 下划线转驼峰
* @param string
* @returns {*}
*/
export function underLine2CamelCase (string) {
return string.replace(/_([a-z])/g, function (all, letter) {
return letter.toUpperCase()
})
}
/**
* 判断是否显示办理按钮
* @param bpmStatus
* @returns {*}
*/
export function showDealBtn (bpmStatus) {
return bpmStatus + '' !== '1' && bpmStatus + '' !== '3' && bpmStatus + '' !== '4'
}
/**
* 增强CSS,可以在页面上输出全局css
* @param css 要增强的css
* @param id style标签的id,可以用来清除旧样式
*/
export function cssExpand (css, id) {
const style = document.createElement('style')
style.type = 'text/css'
style.innerHTML = `@charset "UTF-8"; ${css}`
// 清除旧样式
if (id) {
const $style = document.getElementById(id)
if ($style != null) $style.outerHTML = ''
style.id = id
}
// 应用新样式
document.head.appendChild(style)
}
/** 用于js增强事件,运行JS代码,可以传参 */
// options 所需参数:
// 参数名 类型 说明
// vm VueComponent vue实例
// event Object event对象
// jsCode String 待执行的js代码
// errorMessage String 执行出错后的提示(控制台)
export function jsExpand (options = {}) {
// 绑定到window上的keyName
const windowKeyName = 'J_CLICK_EVENT_OPTIONS'
if (typeof window[windowKeyName] !== 'object') {
window[windowKeyName] = {}
}
// 随机生成JS增强的执行id,防止冲突
const id = randomString(16, 'qwertyuioplkjhgfdsazxcvbnm'.toUpperCase())
// 封装按钮点击事件
const code = `
(function (o_${id}) {
try {
(function (globalEvent, vm) {
${options.jsCode}
})(o_${id}.event, o_${id}.vm)
} catch (e) {
o_${id}.error(e)
}
o_${id}.done()
})(window['${windowKeyName}']['EVENT_${id}'])
`
// 创建script标签
const script = document.createElement('script')
// 将需要传递的参数挂载到window对象上
window[windowKeyName]['EVENT_' + id] = {
vm: options.vm,
event: options.event,
// 当执行完成时,无论如何都会调用的回调事件
done () {
// 执行完后删除新增的 script 标签不会撤销执行结果(已产生的结果不会被撤销)
script.outerHTML = ''
delete window[windowKeyName]['EVENT_' + id]
},
// 当js运行出错的时候调用的事件
error (e) {
console.group(`${options.errorMessage || '用户自定义JS增强代码运行出错'}${new Date()}`)
console.error(e)
console.groupEnd()
}
}
// 将事件挂载到document中
script.innerHTML = code
document.body.appendChild(script)
}
/**
* 重复值验证工具方法
*
* 使用示例:
* { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
*
* @param tableName 被验证的表名
* @param fieldName 被验证的字段名
* @param fieldVal 被验证的值
* @param dataId 数据ID,可空
* @param callback
*/
export function validateDuplicateValue (isEncrypt, tableName, fieldName, fieldVal, dataId, callback) {
if (fieldVal) {
const confusionCode = getConfusionCode(tableName, fieldName)
const params = { confusionCode, fieldVal, dataId }
// 是否数据库中存储的加密信息
if (isEncrypt) {
params.encrypt = 1
}
api.duplicateCheck(params).then(res => {
res.success ? callback() : callback(res.message)
}).catch(err => {
callback(err.message || err)
})
} else {
callback()
}
}
/**
* 根据编码校验规则code,校验传入的值是否合法
*
* 使用示例:
* { validator: (rule, value, callback) => validateCheckRule('common', value, callback) }
*
* @param ruleCode 编码校验规则 code
* @param value 被验证的值
* @param callback
*/
export function validateCheckRule (ruleCode, value, callback) {
if (ruleCode && value) {
value = encodeURIComponent(value)
api.checkRuleByCode({ ruleCode, value }).then(res => {
res.success ? callback() : callback(res.message)
}).catch(err => {
callback(err.message || err)
})
} else {
callback()
}
}
/**
* 如果值不存在就 push 进数组,反之不处理
* @param array 要操作的数据
* @param value 要添加的值
* @param key 可空,如果比较的是对象,可能存在地址不一样但值实际上是一样的情况,可以传此字段判断对象中唯一的字段,例如 id。不传则直接比较实际值
* @returns {boolean} 成功 push 返回 true,不处理返回 false
*/
export function pushIfNotExist (array, value, key) {
for (const item of array) {
if (key && (item[key] === value[key])) {
return false
} else if (item === value) {
return false
}
}
array.push(value)
return true
}
/**
* 可用于判断是否成功
* @type {symbol}
*/
export const succeedSymbol = Symbol('')
/**
* 可用于判断是否失败
* @type {symbol}
*/
export const failedSymbol = Symbol('')
/**
* 使 promise 无论如何都会 resolve,除非传入的参数不是一个Promise对象或返回Promise对象的方法
* 一般用在 Promise.all 中
*
* @param promise 可传Promise对象或返回Promise对象的方法
* @returns {Promise<any>}
*/
export function alwaysResolve (promise) {
return new Promise((resolve, reject) => {
let p = promise
if (typeof promise === 'function') {
p = promise()
}
if (p instanceof Promise) {
p.then(data => {
resolve({ type: succeedSymbol, data })
}).catch(error => {
resolve({ type: failedSymbol, error })
})
} else {
reject('alwaysResolve: 传入的参数不是一个Promise对象或返回Promise对象的方法')
}
})
}
/**
* 简单实现防抖方法
*
* 防抖(debounce)函数在第一次触发给定的函数时,不立即执行函数,而是给出一个期限值(delay),比如100ms。
* 如果100ms内再次执行函数,就重新开始计时,直到计时结束后再真正执行函数。
* 这样做的好处是如果短时间内大量触发同一事件,只会执行一次函数。
*
* @param fn 要防抖的函数
* @param delay 防抖的毫秒数
* @returns {Function}
*/
export function simpleDebounce (fn, delay = 100) {
let timer = null
return function () {
const args = arguments
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
/**
* 不用正则的方式替换所有值
* @param text 被替换的字符串
* @param checker 替换前的内容
* @param replacer 替换后的内容
* @returns {String} 替换后的字符串
*/
export function replaceAll (text, checker, replacer) {
const lastText = text
text = text.replace(checker, replacer)
if (lastText !== text) {
return replaceAll(text, checker, replacer)
}
return text
}
/**
* 获取事件冒泡路径,兼容 IE11EdgeChromeFirefoxSafari
* 目前使用的地方:JEditableTable Span模式
*/
export function getEventPath (event) {
const target = event.target
const path = (event.composedPath && event.composedPath()) || event.path
if (path != null) {
return (path.indexOf(window) < 0) ? path.concat(window) : path
}
if (target === window) {
return [window]
}
const getParents = (node, memo) => {
memo = memo || []
const parentNode = node.parentNode
if (!parentNode) {
return memo
} else {
return getParents(parentNode, memo.concat(parentNode))
}
}
return [target].concat(getParents(target), window)
}
/**
* 根据组件名获取父级
* @param vm
* @param name
* @returns {Vue | null|null|Vue}
*/
export function getVmParentByName (vm, name) {
const parent = vm.$parent
if (parent && parent.$options) {
if (parent.$options.name === name) {
return parent
} else {
const res = getVmParentByName(parent, name)
if (res) {
return res
}
}
}
return null
}
/**
* 使一个值永远不会为(null | undefined
*
* @param value 要处理的值
* @param def 默认值,如果value为(null | undefined)则返回的默认值,可不传,默认为''
*/
export function neverNull (value, def) {
return value == null ? (neverNull(def, '')) : value
}
/**
* 根据元素值移除数组中的一个元素
* @param array 数组
* @param prod 属性名
* @param value 属性值
* @returns {string}
*/
export function removeArrayElement (array, prod, value) {
let index = -1
for (let i = 0; i < array.length; i++) {
if (array[i][prod] + '' === value + '') {
index = i
break
}
}
if (index >= 0) {
array.splice(index, 1)
}
}
export function findFirstRoute (data) {
if (data[0]) {
if (data[0] && data[0].children && data[0].children.length > 0) {
return findFirstRoute(data[0].children)
} else {
return data[0].path
}
} else {
return '/'
}
}
/** 判断是否是OAuth2APP环境 */
export function isOAuth2AppEnv () {
return /wxwork|dingtalk/i.test(navigator.userAgent)
}
/**
* 获取积木报表打印地址
* @param url
* @param id
* @param open 是否自动打开
* @returns {*}
*/
export function getReportPrintUrl (url, id, open) {
// URL支持{{ window.xxx }}占位符变量
url = url.replace(/{{([^}]+)?}}/g, (s1, s2) => evil(s2))
if (url.includes('?')) {
url += '&'
} else {
url += '?'
}
url += `id=${id}`
url += `&token=${Vue.ls.get(ACCESS_TOKEN)}`
if (open) {
window.open(url)
}
return url
}
/**
* 解决eval的eslint问题
* @param fn
* @returns {*}
*/
export function evil (fn) {
const Fn = Function // 一个变量指向Function,防止有些前端编译工具报错
return new Fn('return ' + fn)()
}
/**
* 将url的参数拆分成对象
* @param url
* @returns {{id: *, url}}
*/
export function urlToParams (url) {
const commonUrl = window._CONFIG.staticDomainURL
// url截取参数的部分
const paramsStr = url.slice(url.indexOf('?') + 1)
const paramsObj = {
url,
id: url.slice(url.indexOf(commonUrl) + commonUrl.length + 1, url.indexOf('?'))
}
paramsStr.split('&').forEach(item => {
const arr = item.split('=')
paramsObj[arr[0]] = arr[1]
})
return paramsObj
}
/**
* 重复校验:表名和字段名获取混淆code
* @param tableName
* @param fieldName
* @return {string}
*/
export function getConfusionCode (tableName, fieldName) {
// map对象对应数据库中 sys_confusion 表
const map = {
sys_user: {
username: 'sys_username',
phone: 'user_phone',
email: 'user_email'
},
sys_role: {
role_code: 'sys_rolecode',
role_name: 'sys_rolename'
},
sys_dict: {
dict_code: 'sys_dictcode'
},
sys_depart: {
depart_name: 'depart_name'
},
sys_check_rule: {
rule_code: 'check_rule_code'
},
sys_data_source: {
code: 'data_source_code'
},
sys_fill_rule: {
rule_code: 'fill_rule_code'
},
sys_sms_template: {
template_code: 'sms_template_code'
},
sys_permission: {
perms: 'sys_permission_perms'
},
sys_position: {
code: 'sys_position_code'
}
}
if (map[tableName] && map[tableName][fieldName]) {
return map[tableName][fieldName]
}
return ''
}
/**
* 通过文件id在当前窗口预览文件
* @param fileId
*/
export function previewFileCurrentWindowByFileId (fileId) {
return new Promise((resolve, reject) => {
getFileInfo({ id: fileId }).then(res => {
if (res.success) {
const file = res.result || {}
// 支持预览的文件后缀
const CAN_PREVIEW_FILE_SUFFIX = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']
const fileSuffix = file.fileName ? file.fileName.split('.')[file.fileName.split('.').length - 1] : ''
const canPreview = CAN_PREVIEW_FILE_SUFFIX.some(tt => fileSuffix.toLowerCase() === tt)
// 判断是否为可预览格式的文件
if (!canPreview) {
reject('该文件类型不支持预览')
}
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.fileName}`
const FILE_TYPE_IMGS = ['jpg', 'jpeg', 'png', 'raw']
const FILE_TYPE_PDF = 'pdf'
// pdf预览
if (canPreview && FILE_TYPE_PDF.includes(fileSuffix)) {
const url = previewPdf(file.id)
window.open(url, '_self')
return
}
// 其余可预览文件仍使用KKFile进行预览
const kkFileUrl = dealUrl(fileFullUrl)
window.open(kkFileUrl, '_self')
}
})
})
}
+41
View File
@@ -0,0 +1,41 @@
/**
* 邮箱
* @param {*} s
* 之前的正则 /^(([^<>()\[\]\\.,;:\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,}))$/
*/
export function isEmail(s) {
return /^\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/.test(s)
}
/**
* 手机号码
* @param {*} s
*/
export function isMobile (s) {
return /^1[0-9]{10}$/.test(s)
}
/**
* 电话号码
* @param {*} s
*/
export function isPhone (s) {
return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s)
}
/**
* URL地址
* @param {*} s
*/
export function isURL (s) {
return /^http[s]?:\/\/.*/.test(s)
}
/**
* 手机号或座机号
*
* /^((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(17[0135678])|(18[0-9])|(19[8|9])|(16[6]))\d{8}$/
* */
export function phoneReg(s) {
return /(^[1][3,4,5,6,7,8,9][0-9]{9}$)|(^0\d{2,3}-?\d{7,8}$)/.test(s)
}
+5
View File
@@ -0,0 +1,5 @@
import Bus from 'vue'
const install = function (Vue) {
Vue.prototype.$bus = new Bus()
}
export default { install }
+46
View File
@@ -0,0 +1,46 @@
import Vue from 'vue'
Vue.directive('watermark', {
bind: function (el, binding) {
// 水印文字,父元素,画布宽度,画布高度,字体,文字颜色,画布横坐标
function addWaterMarker (str, parentNode, width, height, font, textColor, fillTextX = '10') {
// 检查父元素是否包含子元素
const elementContains = (parent, child) => parent !== child && parent.contains(child)
const flag = elementContains(parentNode, document.querySelector('canvas'))
// 防止重复创建
if (!flag) {
const can = document.createElement('canvas')
parentNode.appendChild(can)
can.width = width || 300
can.height = height || 140
can.style.display = 'none'
const cans = can.getContext('2d')
cans.rotate(-20 * Math.PI / 180)
cans.font = font || window._CONFIG.watermarkConfig.font
cans.fillStyle = textColor || '#DDDDDD'
cans.textAlign = 'left'
cans.textBaseline = 'Middle'
cans.fillText(str, fillTextX, can.height)
// 设置背景图(整个项目中都添加水印建议使用此方法)
// parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";
// 创建div 定位覆盖(某个元素,如图片添加水印建议使用此方法)
const div = document.createElement('div')
div.id = str
div.style.pointerEvents = 'none'
div.style.top = '0'
div.style.left = '0'
div.style.position = 'absolute'
div.style.zIndex = '100000'
div.style.width = '100%'
div.style.height = '100%'
div.style.background = 'url(' + can.toDataURL('image/png') + ')'
parentNode.appendChild(div)
}
}
if (binding.value.text) {
addWaterMarker(binding.value.text, el, binding.value.width, binding.value.height, binding.value.font, binding.value.textColor, binding.value.fillTextX)
}
}
})