10.19
This commit is contained in:
16
src/utils/auth.js
Normal file
16
src/utils/auth.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const TokenKey = 'Admin-Token'
|
||||
|
||||
export function getToken() {
|
||||
return Cookies.get(TokenKey)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
console.log('setToken=' + token)
|
||||
return Cookies.set(TokenKey, token)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return Cookies.remove(TokenKey)
|
||||
}
|
||||
20
src/utils/blobToBase64.js
Normal file
20
src/utils/blobToBase64.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* @Author: gtz
|
||||
* @Date: 2021-03-01 16:54:38
|
||||
* @LastEditors: gtz
|
||||
* @LastEditTime: 2021-03-01 16:57:00
|
||||
* @Description: file content
|
||||
*/
|
||||
|
||||
export function blobToBase64(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fileReader = new FileReader()
|
||||
fileReader.onload = (e) => {
|
||||
resolve(e.target.result)
|
||||
}
|
||||
fileReader.readAsDataURL(blob)
|
||||
fileReader.onerror = () => {
|
||||
reject(new Error('blobToBase64 error'))
|
||||
}
|
||||
})
|
||||
}
|
||||
4
src/utils/bus.js
Normal file
4
src/utils/bus.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import VueBus from 'vue-bus'
|
||||
|
||||
Vue.use(VueBus)
|
||||
15
src/utils/cache.js
Normal file
15
src/utils/cache.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* @Date: 2021-01-23 16:19:01
|
||||
* @LastEditors: guo
|
||||
* @LastEditTime: 2021-01-23 16:54:29
|
||||
* @FilePath: \basic-admin\src\utils\cache.js
|
||||
* @Description: 带缓存的接口请求
|
||||
*/
|
||||
|
||||
// import request from './request'
|
||||
|
||||
export default function(reqPromise) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
32
src/utils/clipboard.js
Normal file
32
src/utils/clipboard.js
Normal file
@@ -0,0 +1,32 @@
|
||||
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.destroy()
|
||||
})
|
||||
clipboard.on('error', () => {
|
||||
clipboardError()
|
||||
clipboard.destroy()
|
||||
})
|
||||
clipboard.onClick(event)
|
||||
}
|
||||
35
src/utils/error-log.js
Normal file
35
src/utils/error-log.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import Vue from 'vue'
|
||||
import store from '@/store'
|
||||
import { isString, isArray } from '@/utils/validate'
|
||||
import settings from '@/settings'
|
||||
|
||||
// you can set in settings.js
|
||||
// errorLog:'production' | ['production', 'development']
|
||||
const { errorLog: needErrorLog } = settings
|
||||
|
||||
function checkNeed() {
|
||||
const env = process.env.NODE_ENV
|
||||
if (isString(needErrorLog)) {
|
||||
return env === needErrorLog
|
||||
}
|
||||
if (isArray(needErrorLog)) {
|
||||
return needErrorLog.includes(env)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (checkNeed()) {
|
||||
Vue.config.errorHandler = function(err, vm, info, a) {
|
||||
// Don't ask me why I use Vue.nextTick, it just a hack.
|
||||
// detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500
|
||||
Vue.nextTick(() => {
|
||||
store.dispatch('errorLog/addErrorLog', {
|
||||
err,
|
||||
vm,
|
||||
info,
|
||||
url: window.location.href
|
||||
})
|
||||
console.error(err, info)
|
||||
})
|
||||
}
|
||||
}
|
||||
21
src/utils/get-page-title.js
Normal file
21
src/utils/get-page-title.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* @Author: gtz
|
||||
* @Date: 2021-01-27 10:07:42
|
||||
* @LastEditors: gtz
|
||||
* @LastEditTime: 2021-02-26 09:11:54
|
||||
* @Description: file content
|
||||
*/
|
||||
import i18n from '@/lang/i18n'
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const language = Cookies.get('language')
|
||||
|
||||
const title = i18n.title[language] || 'Vue Element Admin'
|
||||
|
||||
export default function getPageTitle(pageTitle) {
|
||||
// 暂时注释,不需要模块名
|
||||
// if (pageTitle) {
|
||||
// return `${pageTitle} - ${title}`
|
||||
// }
|
||||
return `${title}`
|
||||
}
|
||||
35
src/utils/helpers/index.js
Normal file
35
src/utils/helpers/index.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @Author: lb
|
||||
* @Date: 2022-04-19 15:20:00
|
||||
* @LastEditors: lb
|
||||
* @LastEditTime: 2022-04-19 15:20:00
|
||||
* 一些不依赖vue实例的辅助函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 从服务器返回的数据提取需要的部分
|
||||
* 使用说明:
|
||||
* this.dataForm = refineData(res.data, ['id', 'name', 'code']) // 假如只需要服务器所返回的id name code三个字段
|
||||
* @param {any} responseRecords
|
||||
* @param {Array<string>} fields
|
||||
* @returns
|
||||
*/
|
||||
export function refineData(responseRecords, fields) {
|
||||
const _ = {}
|
||||
for (const field of fields) {
|
||||
if (field) { _[field] = Object.prototype.hasOwnProperty.call(responseRecords, field) ? responseRecords[field] : null }
|
||||
}
|
||||
return _
|
||||
}
|
||||
|
||||
/**
|
||||
* 从tree相关的接口响应体中解析出 el-cascader 组件需要的 {value, label, children? } 格式的值
|
||||
* @param {*} dataList 服务器返回的列表
|
||||
*/
|
||||
export function parseTree(dataList) {
|
||||
const _ = []
|
||||
dataList.forEach(item => {
|
||||
_.push({ value: item.id, label: item.name, children: item.children ? parseTree(item.children) : null })
|
||||
})
|
||||
return _
|
||||
}
|
||||
12
src/utils/i18n.js
Normal file
12
src/utils/i18n.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// translate router.meta.title, be used in breadcrumb sidebar tagsview
|
||||
export function generateTitle(title) {
|
||||
const hasKey = this.$te('route.' + title)
|
||||
|
||||
if (hasKey) {
|
||||
// $t :this method from vue-i18n, inject in @/lang/index.js
|
||||
const translatedTitle = this.$t('route.' + title)
|
||||
|
||||
return translatedTitle
|
||||
}
|
||||
return title
|
||||
}
|
||||
409
src/utils/index.js
Normal file
409
src/utils/index.js
Normal file
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* Created by PanJiaChen on 16/11/18.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse the time to string
|
||||
* @param {(Object|string|number)} time
|
||||
* @param {string} cFormat
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function parseTime(time, cFormat) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null
|
||||
}
|
||||
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string')) {
|
||||
if ((/^[0-9]+$/.test(time))) {
|
||||
// support "1548221490638"
|
||||
time = parseInt(time)
|
||||
} else {
|
||||
// support safari
|
||||
// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
|
||||
time = time.replace(new RegExp(/-/gm), '/')
|
||||
}
|
||||
}
|
||||
|
||||
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(/{([ymdhisa])+}/g, (result, key) => {
|
||||
const value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
|
||||
return value.toString().padStart(2, '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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just a simple version of deep copy
|
||||
* Has a lot of edge cases bug
|
||||
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
|
||||
* @param {Object} source
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function deepClone(source) {
|
||||
if (!source && typeof source !== 'object') {
|
||||
throw new Error('error arguments', 'deepClone')
|
||||
}
|
||||
const targetObj = source.constructor === Array ? [] : {}
|
||||
Object.keys(source).forEach(keys => {
|
||||
if (source[keys] && typeof source[keys] === 'object') {
|
||||
targetObj[keys] = deepClone(source[keys])
|
||||
} else {
|
||||
targetObj[keys] = source[keys]
|
||||
}
|
||||
})
|
||||
return targetObj
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 objFilter(obj) {
|
||||
const newObj = {}
|
||||
Object.keys(obj).forEach(item => {
|
||||
if (obj[item] !== undefined && obj[item] !== null && obj[item] !== '') {
|
||||
newObj[item] = obj[item]
|
||||
}
|
||||
})
|
||||
return newObj
|
||||
}
|
||||
|
||||
// 数据字典值转换
|
||||
export function dictChange(arr, { key = 'id', value = 'name' }) {
|
||||
const table = {}
|
||||
arr.forEach(item => {
|
||||
table[item[key]] = item[value]
|
||||
})
|
||||
return table
|
||||
}
|
||||
|
||||
// 数据字典过滤器
|
||||
export function dictFilter(obj) {
|
||||
return function(val) {
|
||||
return obj?.[val]
|
||||
}
|
||||
}
|
||||
|
||||
// 特定key的值替换
|
||||
export function arrInsertVal(arr, key, obj) {
|
||||
return arr.map(item => {
|
||||
if (item[key] && obj[key]) {
|
||||
return {
|
||||
...item,
|
||||
[key]: obj[key]
|
||||
}
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 判断某个时间是否先于另一个时间
|
||||
import moment from 'moment'
|
||||
export function timeIsBefore(a, b) {
|
||||
if (a && b) {
|
||||
return moment(a).isBefore(b)
|
||||
}
|
||||
}
|
||||
|
||||
// tableHeight
|
||||
export function tableHeight(n) {
|
||||
return window.innerHeight - n
|
||||
}
|
||||
455
src/utils/metricflow.js
Normal file
455
src/utils/metricflow.js
Normal file
@@ -0,0 +1,455 @@
|
||||
/* written by Huoyo/Chang Zhang ,please respect copyright*/
|
||||
/* you can use it freely but can not plagiarize it to pretend that you are the owner*/
|
||||
|
||||
export function MetricFlow(domId, options) {
|
||||
const defaultOptions = {
|
||||
'flow': (undefined !== options && options.hasOwnProperty('flow')) ? options['flow'] : 'horizontal',
|
||||
'link-start-offsetx': (undefined !== options && options.hasOwnProperty('link-start-offsetx')) ? 2 + options['link-start-offsetx'] : 2,
|
||||
'link-start-offsety': (undefined !== options && options.hasOwnProperty('link-start-offsety')) ? -4 + options['link-start-offsety'] : -4,
|
||||
'link-end-offsetx': (undefined !== options && options.hasOwnProperty('link-end-offsetx')) ? -20 + options['link-end-offsetx'] : -20,
|
||||
'link-end-offsety': (undefined !== options && options.hasOwnProperty('link-end-offsety')) ? -4 + options['link-end-offsety'] : -4,
|
||||
'link-width-offset': (undefined !== options && options.hasOwnProperty('link-width-offset')) ? 3 + options['link-width-offset'] : 3,
|
||||
'link-color': (undefined !== options && options.hasOwnProperty('link-color')) ? options['link-color'] : '#4b804b',
|
||||
'node-distance-x': (undefined !== options && options.hasOwnProperty('node-distance-offsetx')) ? 100 + options['node-distance-offsetx'] : 100,
|
||||
'node-distance-y': (undefined !== options && options.hasOwnProperty('node-distance-offsety')) ? 300 + options['node-distance-offsety'] : 300
|
||||
}
|
||||
const styleDom = document.getElementsByTagName('style')[0]
|
||||
if (styleDom !== undefined) {
|
||||
styleDom.innerHTML += '.ko-node{position:absolute;background-color:#5f665f;border:2px solid #4b804b;border-radius:5px;width:auto;}.ko-node .ko-node-title{padding-left:17px;padding-right:10px;background-color:#4b804b;color:white;}.ko-node .ko-node-body{list-style:none;margin-top:5px;margin-bottom:1px;}.ko-node li{padding-right:10px;margin-left:-23px;color:white;border-bottom:1px solid lightslategray;padding-bottom:2px;font-size:10px;}'
|
||||
} else {
|
||||
const style = document.createElement('style')
|
||||
style.type = 'text/css'
|
||||
style.rel = 'stylesheet'
|
||||
style.appendChild(document.createTextNode('.ko-node{position:absolute;background-color:#5f665f;border:2px solid #4b804b;border-radius:5px;width:auto;}.ko-node .ko-node-title{padding-left:17px;padding-right:10px;background-color:#4b804b;color:white;}.ko-node .ko-node-body{list-style:none;margin-top:5px;margin-bottom:1px;}.ko-node li{padding-right:10px;margin-left:-23px;color:white;border-bottom:1px solid lightslategray;padding-bottom:2px;font-size:10px;}'))
|
||||
const head = document.getElementsByTagName('head')[0]
|
||||
head.appendChild(style)
|
||||
}
|
||||
const o = {}
|
||||
o.dx = defaultOptions['node-distance-x']
|
||||
o.dy = defaultOptions['node-distance-y']
|
||||
o.allNodes = new Map()
|
||||
o.back = document.getElementById(domId)
|
||||
o.back.className = 'ko-node-back'
|
||||
const backWidth = Number(o.back.getAttribute('width').replace('px', '').replace('%', '')) * 2
|
||||
const backHeight = Number(o.back.getAttribute('height').replace('px', '').replace('%', '')) * 1.5
|
||||
o.back.innerHTML = '<div id="methods"></div><svg id="svgBack" class="ko-node-back" xmlns="http://www.w3.org/2000/svg" version="1.1" height="' + backHeight + '" width="' + backWidth + '" zdrive="true" cbor="false"><g id="points" stroke="' + defaultOptions['link-color'] + '" stroke-width="' + defaultOptions['link-width-offset'] + '" fill="white"></g><g id="relations" stroke="' + defaultOptions['link-color'] + '" stroke-width="' + defaultOptions['link-width-offset'] + '" fill="none"></g><defs><marker id=\'arrow\' markerWidth=\'13\' markerHeight=\'13\' refx=\'6\' refy=\'7\' orient=\'auto\'><path d=\'M2,10 L6,7 L2,4 L2,10\' style="fill:' + defaultOptions['link-color'] + '"/></marker> </defs></svg>'
|
||||
o.svgBack = document.getElementById('svgBack')
|
||||
o.nodesBack = document.getElementById('methods')
|
||||
o.points = document.getElementById('points')
|
||||
o.relations = document.getElementById('relations')
|
||||
let levelInit
|
||||
|
||||
function getXy(nodeData, i, w, h) {
|
||||
const rootX = nodeData['x']
|
||||
const rootY = nodeData['y']
|
||||
const level = nodeData['level']
|
||||
const children = nodeData['children']
|
||||
const dy = o.dy / Math.pow(2, level)
|
||||
const childrenLength = children.length
|
||||
let childX
|
||||
let childY
|
||||
if (defaultOptions['flow'] === 'vertical') {
|
||||
if (childrenLength === 1) {
|
||||
childX = rootX
|
||||
childY = rootY + h + o.dx
|
||||
} else if (childrenLength > 1) {
|
||||
if (i === 0) {
|
||||
childX = rootX - (childrenLength - 1) * (w + dy) / 2
|
||||
childY = rootY + h + o.dx
|
||||
levelInit.set(level, [childX, childY])
|
||||
} else {
|
||||
childX = levelInit.get(level)[0] + i * (w + dy)
|
||||
childY = levelInit.get(level)[1]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (childrenLength === 1) {
|
||||
childX = rootX + w + o.dx
|
||||
childY = rootY
|
||||
} else if (childrenLength > 1) {
|
||||
if (i === 0) {
|
||||
childX = rootX + w + o.dx
|
||||
childY = rootY - (childrenLength - 1) * (h + dy) / 2
|
||||
levelInit.set(level, [childX, childY])
|
||||
} else {
|
||||
childX = levelInit.get(level)[0]
|
||||
childY = levelInit.get(level)[1] + i * (h + dy)
|
||||
}
|
||||
}
|
||||
}
|
||||
return [childX, childY]
|
||||
}
|
||||
|
||||
function recurseNode(nodes, formDataFunc) {
|
||||
let rootDom
|
||||
if (formDataFunc !== null && formDataFunc !== undefined) {
|
||||
rootDom = o.createNode(formDataFunc(nodes))
|
||||
} else {
|
||||
rootDom = o.createNode(nodes)
|
||||
} if (nodes.hasOwnProperty('children')) {
|
||||
const children = nodes['children']
|
||||
if (children === null || children === undefined) {
|
||||
return
|
||||
}
|
||||
const rootDomW = Number(rootDom.getAttribute('width'))
|
||||
const rootDomH = Number(rootDom.getAttribute('height'))
|
||||
for (const index in children) {
|
||||
const childData = children[index]
|
||||
childData['from'] = nodes['id']
|
||||
if (childData.hasOwnProperty('x') === false) {
|
||||
const newXy = getXy(nodes, index, rootDomW, rootDomH)
|
||||
childData['x'] = newXy[0]
|
||||
childData['y'] = newXy[1]
|
||||
}
|
||||
childData['level'] = nodes['level'] + 1
|
||||
recurseNode(childData, formDataFunc)
|
||||
}
|
||||
}
|
||||
}
|
||||
o.createNodes = function(nodesData, formDataFunc) {
|
||||
if (nodesData instanceof Array) {
|
||||
for (const index in nodesData) {
|
||||
const createData = nodesData[index]
|
||||
if (formDataFunc !== null && formDataFunc !== undefined) {
|
||||
if (createData.hasOwnProperty('children')) {
|
||||
o.createNodes(createData, formDataFunc)
|
||||
} else {
|
||||
o.createNode(formDataFunc(createData))
|
||||
}
|
||||
} else {
|
||||
if (createData.hasOwnProperty('children')) {
|
||||
o.createNodes(createData)
|
||||
} else {
|
||||
o.createNode(createData)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nodesData.hasOwnProperty('children')) {
|
||||
if (nodesData.hasOwnProperty('x') === false || nodesData.hasOwnProperty('y') === false) {
|
||||
console.log('invalid data')
|
||||
return
|
||||
}
|
||||
levelInit = new Map()
|
||||
nodesData['level'] = 1
|
||||
recurseNode(nodesData, formDataFunc)
|
||||
levelInit = null
|
||||
} else {
|
||||
if (formDataFunc !== null && formDataFunc !== undefined) {
|
||||
o.createNode(formDataFunc(nodesData))
|
||||
} else {
|
||||
o.createNode(nodesData)
|
||||
}
|
||||
}
|
||||
}
|
||||
o.createNode = function(param, x, y) {
|
||||
if (o.allNodes.get(param['id']) !== undefined) {
|
||||
const node = document.getElementById(param['id'])
|
||||
return node
|
||||
}
|
||||
let nodeText = "<div funcs id='idValue' style='top:topValuepx!important;left:leftValuepx!important;background-color: nodeBackgroundColor;border: 2px solid nodeBorderColor;otherStyle' class='ko-node'> <div class='ko-node-title' style='color: titleColor;background-color: titleBackColor;titleOtherStyle'>dataTitle</div>liBody</div>"
|
||||
if (param.hasOwnProperty('data')) {
|
||||
nodeText = nodeText.replace('liBody', "<ul class='ko-node-body'>liBody</ul>")
|
||||
}
|
||||
let liText = ''
|
||||
const liTextTemp = '<li class="ko-node-li" style="background-color: liBackColor;liOtherStyle">LiText</li>'
|
||||
for (const index in param.data) {
|
||||
const liBackColor = param.data[index]['background-color'] || '#5f665f'
|
||||
const liOtherStyle = param.data[index]['style'] || ''
|
||||
liText += liTextTemp.replace('LiText', param.data[index]['name']).replace('liBackColor', liBackColor).replace('liOtherStyle', liOtherStyle)
|
||||
}
|
||||
const titleColor = param['title'].hasOwnProperty('color') ? param['title']['color'] : 'white'
|
||||
const titleBackColor = param['title'].hasOwnProperty('color') ? param['title']['background-color'] : '#4b804b'
|
||||
const titleOtherStyle = param['title'].hasOwnProperty('color') ? param['title']['style'] : ''
|
||||
const nodeBackgroundColor = param.hasOwnProperty('background-color') ? param['background-color'] : '#5f665f'
|
||||
const nodeBorderColor = param.hasOwnProperty('border-color') ? param['border-color'] : '#4b804b'
|
||||
const otherStyle = param.hasOwnProperty('style') ? param['style'] : ''
|
||||
const functionText = getFunctionsText(param)
|
||||
nodeText = nodeText.replace('nodeBorderColor', nodeBorderColor).replace('nodeBackgroundColor', nodeBackgroundColor).replace('titleColor', titleColor).replace('titleBackColor', titleBackColor).replace('dataTitle', param['title']['name']).replace('idValue', param['id']).replace('topValue', (param['y'] || y)).replace('leftValue', (param['x'] || x)).replace('liBody', liText).replace('otherStyle', otherStyle).replace('titleOtherStyle', titleOtherStyle).replace('funcs', functionText)
|
||||
o.nodesBack.innerHTML += nodeText
|
||||
const node = document.getElementById(param['id'])
|
||||
node.setAttribute('x', (param['x'] || x))
|
||||
node.setAttribute('y', (param['y'] || y))
|
||||
node.setAttribute('width', node.offsetWidth)
|
||||
node.setAttribute('height', node.offsetHeight)
|
||||
const nodeObject = new Map()
|
||||
nodeObject.set('ins', [])
|
||||
nodeObject.set('outs', [])
|
||||
nodeObject.set('node', node)
|
||||
o.allNodes.set(node.id, nodeObject)
|
||||
if (param.hasOwnProperty('from')) {
|
||||
const fromId = param['from']
|
||||
if (typeof (fromId) === 'string') {
|
||||
const from = document.getElementById(fromId)
|
||||
if (from !== undefined) {
|
||||
o.createLink(from, node)
|
||||
}
|
||||
} else if (fromId instanceof Array) {
|
||||
for (const i in fromId) {
|
||||
const from = document.getElementById(fromId[i])
|
||||
if (from !== undefined) {
|
||||
o.createLink(from, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
function getFunctionsText(param) {
|
||||
let text = ''
|
||||
if (param.hasOwnProperty('click')) {
|
||||
text += ' onclick="' + param['click'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('dblclick')) {
|
||||
text += ' ondblclick="' + param['dblclick'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('mousedown')) {
|
||||
text += ' onmousedown="' + param['mousedown'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('mouseenter')) {
|
||||
text += ' onmouseenter="' + param['mouseenter'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('mouseleave')) {
|
||||
text += ' onmouseleave="' + param['mouseleave'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('mousemove')) {
|
||||
text += ' onmousemove="' + param['mousemove'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('mouseover')) {
|
||||
text += ' onmouseover="' + param['mouseover'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('mouseout')) {
|
||||
text += ' onmouseout="' + param['mouseout'] + '(event);"'
|
||||
}
|
||||
if (param.hasOwnProperty('mouseup')) {
|
||||
text += ' onmouseup="' + param['mouseup'] + '(event);"'
|
||||
}
|
||||
return text
|
||||
}
|
||||
o.createSimLink = function(source, target) {
|
||||
const sourceTop = Number(source.getAttribute('y'))
|
||||
const sourceLeft = Number(source.getAttribute('x'))
|
||||
var sourceWidth = Number(source.getAttribute('width'))
|
||||
var sourceHeight = Number(source.getAttribute('height'))
|
||||
const targetTop = Number(target.getAttribute('y'))
|
||||
const targetLeft = Number(target.getAttribute('x'))
|
||||
var targetHeight = Number(target.getAttribute('height'))
|
||||
var targetWidth = Number(target.getAttribute('width'))
|
||||
// 变量未声明报错添加
|
||||
let startX, startY, endX, endY
|
||||
if (defaultOptions['flow'] === 'vertical') {
|
||||
o.points.innerHTML += "<circle id='pointstart" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (sourceLeft + sourceWidth / 2 + defaultOptions['link-start-offsetx'] - 10) + "' cy='" + (sourceTop + sourceHeight + defaultOptions['link-start-offsety']) + "' r='4' />"
|
||||
o.points.innerHTML += "<circle id='pointend" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (targetLeft + targetWidth / 2 + defaultOptions['link-end-offsetx'] + 10) + "' cy='" + (targetTop + defaultOptions['link-end-offsety'] - 8) + "' r='4' />"
|
||||
startX = sourceLeft + sourceWidth / 2 + defaultOptions['link-start-offsetx'] - 10
|
||||
startY = sourceTop + sourceHeight + defaultOptions['link-start-offsety'] + 2
|
||||
endX = targetLeft + targetWidth / 2 + defaultOptions['link-end-offsetx'] + 10
|
||||
endY = targetTop + defaultOptions['link-end-offsety'] - 10
|
||||
} else {
|
||||
o.points.innerHTML += "<circle id='pointstart" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (sourceLeft + sourceWidth - 5 + defaultOptions['link-start-offsetx']) + "' cy='" + (sourceTop + sourceHeight / 2 + defaultOptions['link-start-offsety']) + "' r='4' />"
|
||||
o.points.innerHTML += "<circle id='pointend" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (targetLeft + defaultOptions['link-end-offsetx'] + 4) + "' cy='" + (targetTop + targetHeight / 2 + defaultOptions['link-end-offsety']) + "' r='4' />"
|
||||
startX = sourceLeft + sourceWidth + defaultOptions['link-start-offsetx']
|
||||
startY = sourceTop + sourceHeight / 2 + defaultOptions['link-start-offsety']
|
||||
endX = targetLeft + defaultOptions['link-end-offsetx']
|
||||
endY = targetTop + targetHeight / 2 + defaultOptions['link-end-offsety']
|
||||
}
|
||||
o.relations.innerHTML += "<line id='line-" + source.id + '-' + target.id + "' x1='" + startX + "' y1='" + startY + "' x2='" + endX + "' y2='" + endY + "' marker-end='url(#arrow)'/>"
|
||||
}
|
||||
o.createLink = function(source, target) {
|
||||
const sourceOuts = o.allNodes.get(source.id).get('outs')
|
||||
const targetIns = o.allNodes.get(target.id).get('ins')
|
||||
if (sourceOuts.indexOf('line' + source.id + target.id) > -1) {
|
||||
return
|
||||
}
|
||||
const sourceTop = Number(source.getAttribute('y'))
|
||||
const sourceLeft = Number(source.getAttribute('x'))
|
||||
var sourceWidth = Number(source.getAttribute('width'))
|
||||
var sourceHeight = Number(source.getAttribute('height'))
|
||||
const targetTop = Number(target.getAttribute('y'))
|
||||
const targetLeft = Number(target.getAttribute('x'))
|
||||
var targetHeight = Number(target.getAttribute('height'))
|
||||
var targetWidth = Number(target.getAttribute('width'))
|
||||
// 变量未声明报错添加
|
||||
let startX, startY, endX, endY
|
||||
if (defaultOptions['flow'] === 'vertical') {
|
||||
o.points.innerHTML += "<circle id='pointstart" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (sourceLeft + sourceWidth / 2 + defaultOptions['link-start-offsetx'] - 10) + "' cy='" + (sourceTop + sourceHeight + defaultOptions['link-start-offsety']) + "' r='4' />"
|
||||
o.points.innerHTML += "<circle id='pointend" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (targetLeft + targetWidth / 2 + defaultOptions['link-end-offsetx'] + 10) + "' cy='" + (targetTop + defaultOptions['link-end-offsety'] - 8) + "' r='4' />"
|
||||
startX = sourceLeft + sourceWidth / 2 + defaultOptions['link-start-offsetx'] - 10
|
||||
startY = sourceTop + sourceHeight + defaultOptions['link-start-offsety'] + 2
|
||||
endX = targetLeft + targetWidth / 2 + defaultOptions['link-end-offsetx'] + 10
|
||||
endY = targetTop + defaultOptions['link-end-offsety'] - 10
|
||||
} else {
|
||||
o.points.innerHTML += "<circle id='pointstart" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (sourceLeft + sourceWidth - 5 + defaultOptions['link-start-offsetx']) + "' cy='" + (sourceTop + sourceHeight / 2 + defaultOptions['link-start-offsety']) + "' r='4' />"
|
||||
o.points.innerHTML += "<circle id='pointend" + source.getAttribute('id') + target.getAttribute('id') + "' cx='" + (targetLeft + defaultOptions['link-end-offsetx'] + 4) + "' cy='" + (targetTop + targetHeight / 2 + defaultOptions['link-end-offsety']) + "' r='4' />"
|
||||
startX = sourceLeft + sourceWidth + defaultOptions['link-start-offsetx']
|
||||
startY = sourceTop + sourceHeight / 2 + defaultOptions['link-start-offsety']
|
||||
endX = targetLeft + defaultOptions['link-end-offsetx']
|
||||
endY = targetTop + targetHeight / 2 + defaultOptions['link-end-offsety']
|
||||
}
|
||||
o.relations.innerHTML += "<line id='line-" + source.id + '-' + target.id + "' x1='" + startX + "' y1='" + startY + "' x2='" + endX + "' y2='" + endY + "' marker-end='url(#arrow)'/>"
|
||||
sourceOuts.push('line-' + source.id + '-' + target.id)
|
||||
targetIns.push('line-' + source.id + '-' + target.id)
|
||||
}
|
||||
|
||||
function getMouseTarget(eventElement) {
|
||||
const clssName = eventElement.getAttribute('class')
|
||||
if (clssName === 'ko-node') {
|
||||
return eventElement
|
||||
} else if (clssName === 'ko-node-li') {
|
||||
return eventElement.parentNode.parentNode
|
||||
} else if (clssName === 'ko-node-title') {
|
||||
return eventElement.parentNode
|
||||
} else if (clssName === 'ko-node-body') {
|
||||
return eventElement.parentNode
|
||||
} else {
|
||||
return eventElement
|
||||
}
|
||||
}
|
||||
|
||||
function reDrawNodeLines(node) {
|
||||
const objectData = o.allNodes.get(node.id)
|
||||
if (objectData === undefined) {
|
||||
return
|
||||
}
|
||||
const inIds = objectData.get('ins')
|
||||
for (const index in inIds) {
|
||||
const lineId = inIds[index]
|
||||
const lineIdSplit = lineId.split('-')
|
||||
const sourceId = lineIdSplit[1]
|
||||
const targetId = lineIdSplit[2]
|
||||
if (document.getElementById(lineId) !== undefined) {
|
||||
document.getElementById(lineId).remove()
|
||||
document.getElementById('pointstart' + sourceId + targetId).remove()
|
||||
document.getElementById('pointend' + sourceId + targetId).remove()
|
||||
o.createSimLink(document.getElementById(sourceId), node)
|
||||
}
|
||||
}
|
||||
const outIds = o.allNodes.get(node.id).get('outs')
|
||||
for (const index in outIds) {
|
||||
const lineId = outIds[index]
|
||||
const lineIdSplit = lineId.split('-')
|
||||
const sourceId = lineIdSplit[1]
|
||||
const targetId = lineIdSplit[2]
|
||||
if (document.getElementById(lineId) !== undefined) {
|
||||
document.getElementById(lineId).remove()
|
||||
document.getElementById('pointstart' + sourceId + targetId).remove()
|
||||
document.getElementById('pointend' + sourceId + targetId).remove()
|
||||
o.createSimLink(node, document.getElementById(targetId))
|
||||
}
|
||||
}
|
||||
}
|
||||
o.reDrawLines = function() {
|
||||
o.allNodes.forEach(function(nodeData, nodeId) {
|
||||
reDrawNodeLines(document.getElementById(nodeId))
|
||||
})
|
||||
}
|
||||
o.moveNode
|
||||
o.moveNodeX = 0
|
||||
o.moveNodeY = 0
|
||||
o.back.onmousedown = function(e) {
|
||||
const mouseTarget = getMouseTarget(e.target)
|
||||
const clssName = mouseTarget.getAttribute('class')
|
||||
o.moveNodeX = e.clientX
|
||||
o.moveNodeY = e.clientY
|
||||
if (clssName === 'ko-node') {
|
||||
o.moveNode = mouseTarget
|
||||
o.moveNode.style.cursor = 'move'
|
||||
o.moveNode.onmousemove = nodeMove
|
||||
o.moveNode.onmouseup = nodeMoveClose
|
||||
}
|
||||
}
|
||||
o.svgBack.onmousedown = function(e) {
|
||||
const mouseTarget = getMouseTarget(e.target)
|
||||
const clssName = mouseTarget.getAttribute('class')
|
||||
o.moveNodeX = e.clientX
|
||||
o.moveNodeY = e.clientY
|
||||
if (clssName === 'ko-node') {
|
||||
o.moveNode = mouseTarget
|
||||
o.moveNode.style.cursor = 'move'
|
||||
o.moveNode.onmousemove = nodeMove
|
||||
o.moveNode.onmouseup = nodeMoveClose
|
||||
} else {
|
||||
o.moveNode = mouseTarget
|
||||
o.moveNode.style.cursor = 'move'
|
||||
o.moveNode.onmousemove = backMove
|
||||
o.moveNode.onmouseup = nodeMoveClose
|
||||
}
|
||||
}
|
||||
|
||||
function nodeMove(e) {
|
||||
e = e || window.event
|
||||
const offsetX = o.moveNodeX - e.clientX
|
||||
const offsetY = o.moveNodeY - e.clientY
|
||||
o.moveNodeX = e.clientX
|
||||
o.moveNodeY = e.clientY
|
||||
const xOff = (o.moveNode.offsetLeft - offsetX)
|
||||
const yOff = (o.moveNode.offsetTop - offsetY)
|
||||
if (Number.isNaN(xOff) || Number.isNaN(yOff)) {
|
||||
return
|
||||
}
|
||||
o.moveNode.style.top = yOff + 'px'
|
||||
o.moveNode.style.left = xOff + 'px'
|
||||
o.moveNode.setAttribute('x', xOff)
|
||||
o.moveNode.setAttribute('y', yOff)
|
||||
reDrawNodeLines(o.moveNode)
|
||||
}
|
||||
|
||||
function backMove(e) {
|
||||
e = e || window.event
|
||||
const offsetX = o.moveNodeX - e.clientX
|
||||
const offsetY = o.moveNodeY - e.clientY
|
||||
o.moveNodeX = e.clientX
|
||||
o.moveNodeY = e.clientY
|
||||
o.allNodes.forEach(function(nodeData, nodeId) {
|
||||
const moveNode = document.getElementById(nodeId)
|
||||
moveNode.style.top = (moveNode.offsetTop - offsetY) + 'px'
|
||||
moveNode.style.left = (moveNode.offsetLeft - offsetX) + 'px'
|
||||
moveNode.setAttribute('x', (moveNode.offsetLeft - offsetX))
|
||||
moveNode.setAttribute('y', (moveNode.offsetTop - offsetY))
|
||||
reDrawNodeLines(moveNode)
|
||||
})
|
||||
}
|
||||
|
||||
function nodeMoveClose(e) {
|
||||
o.moveNode.style.cursor = 'default'
|
||||
o.moveNode.onmouseup = null
|
||||
o.moveNode.onmousemove = null
|
||||
o.moveNodeX = null
|
||||
o.moveNodeY = null
|
||||
}
|
||||
o.back.onmousemove = function(e) {
|
||||
var nx = e.clientX
|
||||
var ny = e.clientY
|
||||
var nl = nx - (o.moveNodeX - o.l)
|
||||
var nt = ny - (o.moveNodeY - o.t)
|
||||
if (o.isDragNode === true) {
|
||||
o.moveNode.style.left = nl + 'px'
|
||||
o.moveNode.style.top = nt + 'px'
|
||||
o.reDrawLines(o.moveNode)
|
||||
} else if (o.isDragBack === true) {
|
||||
o.allNodes.forEach(function(value, nodeKey) {
|
||||
const pmoveNode = document.getElementById(nodeKey)
|
||||
const nl = nx - o.moveNodeX
|
||||
const nt = ny - o.moveNodeY
|
||||
if (nl > 10) {
|
||||
pmoveNode.style.left = Number(pmoveNode.style.left.replace('px', '')) + 10 + 'px'
|
||||
} else if (nl < -10) {
|
||||
pmoveNode.style.left = Number(pmoveNode.style.left.replace('px', '')) - 10 + 'px'
|
||||
}
|
||||
if (nt > 6) {
|
||||
pmoveNode.style.top = Number(pmoveNode.style.top.replace('px', '')) + 10 + 'px'
|
||||
} else if (nt < -6) {
|
||||
pmoveNode.style.top = Number(pmoveNode.style.top.replace('px', '')) - 10 + 'px'
|
||||
}
|
||||
})
|
||||
o.reDrawLines()
|
||||
}
|
||||
}
|
||||
o.back.onmouseup = function(e) {
|
||||
o.isDragNode = false
|
||||
o.isDragBack = false
|
||||
if ((o.moveNode !== undefined) && (o.moveNode.hasOwnProperty('style') === true) && (o.moveNode.style.hasOwnProperty('cursor') === true)) {
|
||||
o.moveNode.style.cursor = 'default'
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
||||
25
src/utils/open-window.js
Normal file
25
src/utils/open-window.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
*Created by PanJiaChen on 16/11/29.
|
||||
* @param {Sting} url
|
||||
* @param {Sting} title
|
||||
* @param {Number} w
|
||||
* @param {Number} h
|
||||
*/
|
||||
export default function openWindow(url, title, w, h) {
|
||||
// Fixes dual-screen position Most browsers Firefox
|
||||
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
|
||||
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top
|
||||
|
||||
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width
|
||||
const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height
|
||||
|
||||
const left = ((width / 2) - (w / 2)) + dualScreenLeft
|
||||
const top = ((height / 2) - (h / 2)) + dualScreenTop
|
||||
const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left)
|
||||
|
||||
// Puts focus on the newWindow
|
||||
if (window.focus) {
|
||||
newWindow.focus()
|
||||
}
|
||||
}
|
||||
|
||||
25
src/utils/permission.js
Normal file
25
src/utils/permission.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import store from '@/store'
|
||||
|
||||
/**
|
||||
* @param {Array} value
|
||||
* @returns {Boolean}
|
||||
* @example see @/views/permission/directive.vue
|
||||
*/
|
||||
export default function checkPermission(value) {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const roles = store.getters && store.getters.roles
|
||||
const permissionRoles = value
|
||||
|
||||
const hasPermission = roles.some(role => {
|
||||
return permissionRoles.includes(role)
|
||||
})
|
||||
|
||||
if (!hasPermission) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error(`need roles! Like v-permission="['admin','editor']"`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
157
src/utils/request.js
Normal file
157
src/utils/request.js
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* @Date: 2020-12-14 09:07:03
|
||||
* @LastEditors: gtz
|
||||
* @LastEditTime: 2022-05-26 10:58:42
|
||||
* @FilePath: \mt-bus-fe\src\utils\request.js
|
||||
* @Description:
|
||||
*/
|
||||
import axios from 'axios'
|
||||
import { MessageBox, Message, Loading } from 'element-ui'
|
||||
import store from '@/store'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import i18n from '@/lang'
|
||||
|
||||
// create an axios instance
|
||||
const service = axios.create({
|
||||
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
|
||||
// withCredentials: true, // send cookies when cross-domain requests
|
||||
timeout: 15000 // request timeout
|
||||
})
|
||||
/*
|
||||
当页面有两个接口时,第一个接口loading的close事件会直接将第二个接口的loading实例也close
|
||||
每次创建Loading实例的时候判断当前是否存在,如果当前还没有Loading实例就创建一个,
|
||||
如果有就不会再创建而是计数;每次关闭的时候判断当前的计数,
|
||||
如果是0了就关闭,否则也计数减一,直到为0的时候表示当前所有页面所有接口都返回结束了,
|
||||
此时执行关闭Loading.close()操作
|
||||
*/
|
||||
|
||||
let loadingInstance = null
|
||||
function startLoading() {
|
||||
loadingInstance = Loading.service({
|
||||
fullscreen: false,
|
||||
text: '拼命加载中...',
|
||||
background: 'rgba(0, 0, 0, 0.1)'
|
||||
})
|
||||
}
|
||||
function endLoading() {
|
||||
loadingInstance.close()
|
||||
}
|
||||
let needLoadingRequestCount = 0
|
||||
function showFullScreenLoading() {
|
||||
if (needLoadingRequestCount === 0) {
|
||||
startLoading()
|
||||
}
|
||||
needLoadingRequestCount++
|
||||
}
|
||||
function tryHideFullScreenLoading() {
|
||||
if (needLoadingRequestCount <= 0) return
|
||||
needLoadingRequestCount--
|
||||
if (needLoadingRequestCount === 0) {
|
||||
endLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// request interceptor
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
// do something before request is sent
|
||||
showFullScreenLoading()
|
||||
if (store.getters.token) {
|
||||
// let each request carry token
|
||||
// ['X-Token'] is a custom headers key
|
||||
// please modify it according to the actual situation
|
||||
config.headers['token'] = getToken()
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
// do something with request error
|
||||
tryHideFullScreenLoading()
|
||||
console.log(error) // for debug
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
/**
|
||||
* If you want to get http information such as headers or status
|
||||
* Please return response => response
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine the request status by custom code
|
||||
* Here is just an example
|
||||
* You can also judge the status by HTTP Status Code
|
||||
*/
|
||||
response => {
|
||||
tryHideFullScreenLoading()
|
||||
// console.log(response)
|
||||
const res = response.data
|
||||
// if the custom code is not 20000, it is judged as an error.
|
||||
if (response.config.responseType === 'blob') {
|
||||
return response
|
||||
}
|
||||
if (res.code !== 0 && res.code !== 401) {
|
||||
Message({
|
||||
message: res.msg || 'Error',
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
|
||||
// 具体看框架对响应码的定义
|
||||
if (res.code === 401) {
|
||||
// to re-login
|
||||
// to do 添加i18n
|
||||
MessageBox.confirm('登录信息失效,请重新登录', '提示', {
|
||||
confirmButtonText: '去登录',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
store.dispatch('user/resetToken').then(() => {
|
||||
location.reload()
|
||||
})
|
||||
})
|
||||
}
|
||||
return Promise.reject(new Error(res.msg || 'Error'))
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
},
|
||||
error => {
|
||||
tryHideFullScreenLoading()
|
||||
console.log('err' + error) // for debug
|
||||
if ('err' + error === 'errError: timeout of 15000ms exceeded') {
|
||||
Message({
|
||||
message: i18n.t('errorLog.timeoutTip'),
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
} else {
|
||||
Message({
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
|
||||
export function requestWithCache(obj) {
|
||||
const name = (obj.subname || '') + obj.url
|
||||
const result = localStorage.getItem(name)
|
||||
if (result) {
|
||||
return Promise.resolve(JSON.parse(result))
|
||||
} else {
|
||||
return service(obj).then(res => {
|
||||
localStorage.setItem(name, JSON.stringify(res))
|
||||
setTimeout(() => {
|
||||
localStorage.removeItem(name)
|
||||
}, 60000)
|
||||
return res
|
||||
})
|
||||
}
|
||||
}
|
||||
36
src/utils/requestWithCache.js
Normal file
36
src/utils/requestWithCache.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Date: 2021-01-26 10:39:44
|
||||
* @LastEditors: gtz
|
||||
* @LastEditTime: 2021-01-27 11:25:20
|
||||
* @FilePath: \basic-admin\src\utils\requestWithCache.js
|
||||
* @Description: 带缓存的接口请求
|
||||
*/
|
||||
|
||||
import request from './request'
|
||||
|
||||
function getStorage(key) {
|
||||
const result = localStorage.getItem(key)
|
||||
if (result) {
|
||||
return JSON.parse(result)
|
||||
}
|
||||
}
|
||||
// function setStorage(key, value) {
|
||||
// if (key) {
|
||||
// return localStorage.setItem(key, JSON.stringify(value))
|
||||
// }
|
||||
// }
|
||||
|
||||
export default function({ url, method, data, ...rest }) {
|
||||
// 判断是否有缓存数据
|
||||
if (getStorage(url)) {
|
||||
return getStorage(url)
|
||||
} else {
|
||||
// setStorage(key, value)
|
||||
return request({
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
...rest
|
||||
})
|
||||
}
|
||||
}
|
||||
58
src/utils/scroll-to.js
Normal file
58
src/utils/scroll-to.js
Normal file
@@ -0,0 +1,58 @@
|
||||
Math.easeInOutQuad = function(t, b, c, d) {
|
||||
t /= d / 2
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t + b
|
||||
}
|
||||
t--
|
||||
return -c / 2 * (t * (t - 2) - 1) + b
|
||||
}
|
||||
|
||||
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||
var requestAnimFrame = (function() {
|
||||
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
|
||||
})()
|
||||
|
||||
/**
|
||||
* Because it's so fucking difficult to detect the scrolling element, just move them all
|
||||
* @param {number} amount
|
||||
*/
|
||||
function move(amount) {
|
||||
document.documentElement.scrollTop = amount
|
||||
document.body.parentNode.scrollTop = amount
|
||||
document.body.scrollTop = amount
|
||||
}
|
||||
|
||||
function position() {
|
||||
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} to
|
||||
* @param {number} duration
|
||||
* @param {Function} callback
|
||||
*/
|
||||
export function scrollTo(to, duration, callback) {
|
||||
const start = position()
|
||||
const change = to - start
|
||||
const increment = 20
|
||||
let currentTime = 0
|
||||
duration = (typeof (duration) === 'undefined') ? 500 : duration
|
||||
var animateScroll = function() {
|
||||
// increment the time
|
||||
currentTime += increment
|
||||
// find the value with the quadratic in-out easing function
|
||||
var val = Math.easeInOutQuad(currentTime, start, change, duration)
|
||||
// move the document.body
|
||||
move(val)
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll)
|
||||
} else {
|
||||
if (callback && typeof (callback) === 'function') {
|
||||
// the animation is done so lets callback
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
animateScroll()
|
||||
}
|
||||
39
src/utils/tree.js
Normal file
39
src/utils/tree.js
Normal file
@@ -0,0 +1,39 @@
|
||||
export const getListItems = (root, fVisit) => {
|
||||
const queue = Array.isArray(root) ? [...root] : [root]
|
||||
while (queue.length) {
|
||||
const node = queue.shift()
|
||||
fVisit && fVisit(node)
|
||||
const children = node.l
|
||||
if (children && children.length) {
|
||||
for (let i = 0; i < children.length; i++) queue.push(children[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
export const getTreeItems = (menus, idName = 'id', pIdName = 'parentId') => {
|
||||
if (menus === null || menus.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
// 删除 所有 children,以防止多次调用
|
||||
menus.forEach((item) => {
|
||||
delete item.children
|
||||
})
|
||||
// 将数据存储为 以 id 为 KEY 的 map 索引数据列
|
||||
const map = {}
|
||||
menus.forEach((item) => {
|
||||
map[item[idName]] = item
|
||||
})
|
||||
// console.log(map);
|
||||
const val = []
|
||||
menus.forEach((item) => {
|
||||
// 以当前遍历项,的pid,去map对象中找到索引的id
|
||||
const parent = map[item[pIdName]]
|
||||
// 好绕啊,如果找到索引,那么说明此项不在顶级当中,那么需要把此项添加到,他对应的父级中
|
||||
if (parent) {
|
||||
(parent.children || (parent.children = [])).push(item)
|
||||
} else {
|
||||
// 如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级
|
||||
val.push(item)
|
||||
}
|
||||
})
|
||||
return val
|
||||
}
|
||||
89
src/utils/validate.js
Normal file
89
src/utils/validate.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Created by PanJiaChen on 16/11/18.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
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
|
||||
// TODO:检查手机号码
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validURL(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) {
|
||||
const reg = /^[a-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validUpperCase(str) {
|
||||
const reg = /^[A-Z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validAlphabets(str) {
|
||||
const reg = /^[A-Za-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validEmail(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)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isString(str) {
|
||||
if (typeof str === 'string' || str instanceof String) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} arg
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isArray(arg) {
|
||||
if (typeof Array.isArray === 'undefined') {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]'
|
||||
}
|
||||
return Array.isArray(arg)
|
||||
}
|
||||
Reference in New Issue
Block a user