358 lines
13 KiB
JavaScript
358 lines
13 KiB
JavaScript
import Vue from 'vue'
|
|
Vue.use( {
|
|
install(Vue) {
|
|
//==========Find & filter=================
|
|
Vue.prototype.$find = function(arr, obj, attr) {
|
|
const keys = Object.keys(obj)
|
|
let found = arr.find(v=>{
|
|
let valid = true
|
|
keys.map(key=>{
|
|
let val = obj[key]
|
|
if(valid===false) return false
|
|
else if(Array.isArray(val)) {
|
|
if(val.findIndex(x=>x===v[key]) <0) valid = false
|
|
} else if(!(v[key]===val)) valid = false
|
|
})
|
|
return valid
|
|
})
|
|
return found? (attr? found[attr] : found) : undefined
|
|
}
|
|
|
|
Vue.prototype.$findIndex = function(arr, obj) {
|
|
const keys = Object.keys(obj)
|
|
return arr.findIndex(v=>{
|
|
let valid = true
|
|
keys.map(key=>{
|
|
let val = obj[key]
|
|
if(valid===false) return false
|
|
else if(Array.isArray(val)) {
|
|
if(val.findIndex(x=>x===v[key]) <0) valid = false
|
|
} else if(!(v[key]===val)) valid = false
|
|
})
|
|
return valid
|
|
})
|
|
}
|
|
|
|
Vue.prototype.$filter = function(arr, obj, attr) {
|
|
const keys = Object.keys(obj)
|
|
let rows = arr.filter(v=>{
|
|
let valid = true
|
|
keys.map(key=>{
|
|
let val = obj[key]
|
|
if(valid===false) return false
|
|
else if(Array.isArray(val)) {
|
|
if(val.findIndex(x=>x===v[key]) <0) valid = false
|
|
} else if(!(v[key]===val)) valid = false
|
|
})
|
|
return valid
|
|
})
|
|
return attr? rows.map(v=>v[attr]) : rows
|
|
}
|
|
|
|
//=========Empty & copy============
|
|
Vue.prototype.$id = function() {
|
|
return Math.random().toString(36).substr(2, 9)
|
|
}
|
|
|
|
Vue.prototype.$empty = function(val) {
|
|
if(val === undefined || val === null || val === '' || val==="") return true
|
|
return false
|
|
},
|
|
|
|
Vue.prototype.$copy = function(val) {
|
|
if(val === undefined || val === null || val === '' || val==="") return val
|
|
return JSON.parse(JSON.stringify(val))
|
|
}
|
|
|
|
Vue.prototype.$clone = function(obj) {
|
|
if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
|
|
return obj;
|
|
if (obj instanceof Date)
|
|
var temp = new obj.constructor(); //or new Date(obj);
|
|
else
|
|
var temp = obj.constructor();
|
|
for (var key in obj) {
|
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
obj['isActiveClone'] = null;
|
|
temp[key] = this.$clone(obj[key]);
|
|
delete obj['isActiveClone'];
|
|
}
|
|
}
|
|
return temp
|
|
}
|
|
|
|
Vue.prototype.$delete = function(arr, idx) {
|
|
arr.splice(idx, 1)
|
|
}
|
|
|
|
Vue.prototype.$stripHtml = function(html, length) {
|
|
if(!html) return null
|
|
else if(typeof html!=='string') return html
|
|
if(html? html.indexOf('<')<0 : false) {
|
|
return length? (html.length > length? html.substring(0, length) + '...' : html ) : html
|
|
}
|
|
var tmp = document.createElement("DIV")
|
|
tmp.innerHTML = html
|
|
var val = tmp.textContent || tmp.innerText || ""
|
|
return length? (val.length > length? val.substring(0, length) + '...' : val ) : val
|
|
}
|
|
|
|
//==========Convert=================
|
|
Vue.prototype.$isNumber = function(val) {
|
|
if(val === undefined || val === null || val === '' || val==="") return false
|
|
val = val.toString().replace(/,/g, "")
|
|
return !isNaN(val)
|
|
}
|
|
|
|
Vue.prototype.$numtoString = function(val, type, decimal, mindecimal) {
|
|
if(val === undefined || val === "" || val==='' || val === null) return
|
|
val = val.toString().replace(/,/g, "")
|
|
if(isNaN(val)) return
|
|
let f = decimal? {maximumFractionDigits: decimal || 0} : {}
|
|
if(mindecimal) f['minimumFractionDigits'] = mindecimal
|
|
return decimal? Number(val).toLocaleString(type || 'en-EN', f) : Number(val).toLocaleString(type || 'en-EN')
|
|
}
|
|
|
|
Vue.prototype.$formatNumber = function(val) {
|
|
if(val === undefined || val === "" || val==='' || val === null) return
|
|
val = val.toString().replace(/,/g, "")
|
|
if (val.indexOf('%') >0) {
|
|
val = val.replace(/%/g, "")
|
|
return isNaN(val)? undefined : Number(val)/100
|
|
}
|
|
return isNaN(val)? undefined : Number(val)
|
|
}
|
|
|
|
Vue.prototype.$formatUnit = function(val, unit, decimal, string, mindecimal) {
|
|
val = this.$formatNumber(val)
|
|
if(val===undefined) return
|
|
let percentage = (unit===0.01 || unit==="0.01")? '%' : ''
|
|
val = unit? val/Number(unit) : val
|
|
let f = {maximumFractionDigits: decimal || 0}
|
|
if(mindecimal) f['minimumFractionDigits'] = mindecimal
|
|
return string? (val.toLocaleString('en-EN', f) + percentage) : val
|
|
}
|
|
|
|
//==========Calculate=================
|
|
Vue.prototype.$calc = function(fn) {
|
|
return new Function('return ' + fn)()
|
|
}
|
|
|
|
Vue.prototype.$calculate = function(row, tags, formula, decimal, unit) {
|
|
let val = this.$copy(formula)
|
|
let valid = 0
|
|
tags.forEach(v => {
|
|
let myRegExp = new RegExp(v, 'g')
|
|
let res = this.$formatNumber(row[v])
|
|
if(res) valid = 1
|
|
val = val.replace(myRegExp, `(${res || 0})`)
|
|
})
|
|
if(valid===0) return {success: false, value : undefined} //all values is null
|
|
//calculate
|
|
try {
|
|
let value = this.$calc(val)
|
|
if(isNaN(value) || value===Number.POSITIVE_INFINITY || value===Number.NEGATIVE_INFINITY) {
|
|
var result = {success: false, value : value}
|
|
} else {
|
|
value = (value===true || value===false)? value : this.$formatUnit(value, unit, decimal, true, decimal)
|
|
var result = {success: true, value: value}
|
|
}
|
|
}
|
|
catch(err) {
|
|
var result = {success: false, value : undefined}
|
|
}
|
|
return result
|
|
}
|
|
|
|
Vue.prototype.$calculateFunc = function(row, cols, func, decimal, unit) {
|
|
let value
|
|
let arr1 = cols.map(v=>this.$formatNumber(row[v]))
|
|
let arr = arr1.filter(v=>v)
|
|
if(arr.length===0) return {success: false, value : undefined}
|
|
if(func==='max') value = Math.max(...arr)
|
|
else if(func==='min') value = Math.min(...arr)
|
|
else if(func==='sum') value = arr.reduce((a, b) => a + b, 0)
|
|
else if(func==='avg') {
|
|
let total = arr.reduce((a, b) => a + b, 0)
|
|
value = total / cols.length
|
|
}
|
|
if(!value) return {success: false, value: undefined}
|
|
value = this.$formatUnit(value, unit, decimal, true, decimal)
|
|
return {success: true, value: value}
|
|
}
|
|
|
|
Vue.prototype.$calculateData = function(data, fields) {
|
|
let arr = this.$copy(fields.filter(h=>h.formula))
|
|
if(arr.length===0) return data
|
|
let arr1 = arr.filter(v=>v.func)
|
|
arr1.map(v=>{
|
|
if(v.vals.indexOf(':')>=0) {
|
|
let arr2 = v.vals.toLowerCase().replaceAll('c', '').split(':')
|
|
let cols = []
|
|
for (let i = parseInt(arr2[0]); i <= parseInt(arr2[1]); i++) {
|
|
let field = fields.length>i? fields[i] : undefined
|
|
if(field? (field.format==='number' && field.name!==v.name) : false) cols.push(field.name)
|
|
}
|
|
v.cols = cols
|
|
} else {
|
|
let arr2 = v.vals.toLowerCase().replaceAll('c', '').split(',')
|
|
let cols = []
|
|
arr2.map(v=>{
|
|
let i = parseInt(v)
|
|
let field = fields.length>i? fields[i] : undefined
|
|
if(field? (field.format==='number' && field.name!==v.name) : false) cols.push(field.name)
|
|
})
|
|
v.cols = cols
|
|
}
|
|
})
|
|
arr = this.$multiSort(arr, {level: 'asc'})
|
|
let copy = data
|
|
copy.map(v=>{
|
|
arr.map(x=>{
|
|
if(x.func) {
|
|
let res = this.$calculateFunc(v, x.cols, x.func, x.decimal, x.unit)
|
|
if(res? res.success : false) v[x.name] = res.value
|
|
} else {
|
|
let res = this.$calculate(v, x.tags, x.formula, x.decimal, x.unit)
|
|
if(res? res.success : false) v[x.name] = res.value
|
|
}
|
|
})
|
|
})
|
|
return copy
|
|
}
|
|
|
|
Vue.prototype.$summary = function(arr, fields, type) {
|
|
let obj = {}
|
|
if(type==='total') {
|
|
fields.map(x=> obj[x] = arr.map(v=>v[x]? v[x] : 0).reduce((a, b) => a + b, 0))
|
|
} else if(type==='min') {
|
|
fields.map(x=>obj[x] = Math.min(...arr.map(v=>v[x])))
|
|
}
|
|
else if(type==='max') {
|
|
fields.map(x=>obj[x] = Math.max(...arr.map(v=>v[x])))
|
|
}
|
|
else if(type==='count') {
|
|
fields.map(x=>obj[x] = arr.map(v=>!this.$empty(v[x])).length)
|
|
}
|
|
return obj
|
|
}
|
|
|
|
//====================Array====================
|
|
Vue.prototype.$formatArray = function(data, fields) {
|
|
let args = fields.filter(v=>v.format==='number')
|
|
data.map(v=>{
|
|
args.map(x=>{
|
|
v[x.name] = this.$empty(v[x.name])? undefined : this.$formatUnit(v[x.name], x.unit, x.decimal, true, x.decimal)
|
|
})
|
|
})
|
|
return data
|
|
}
|
|
|
|
Vue.prototype.$unique = function(arr, keyProps) {
|
|
const kvArray = arr.map(entry => {
|
|
const key = keyProps.map(k => entry[k]).join('|');
|
|
return [key, entry];
|
|
});
|
|
const map = new Map(kvArray);
|
|
return Array.from(map.values());
|
|
}
|
|
|
|
Vue.prototype.$arrayMove = function(arr, old_index, new_index) {
|
|
if (new_index >= arr.length) {
|
|
var k = new_index - arr.length + 1;
|
|
while (k--) {
|
|
arr.push(undefined);
|
|
}
|
|
}
|
|
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
|
return arr; // for testing
|
|
}
|
|
|
|
Vue.prototype.$multiSort = function(array, sortObject = {}, format = {}) {
|
|
const sortKeys = Object.keys(sortObject)
|
|
|
|
// Return array if no sort object is supplied.
|
|
if (!sortKeys.length) return array
|
|
|
|
// Change the values of the sortObject keys to -1, 0, or 1.
|
|
for (let key in sortObject)
|
|
sortObject[key] = sortObject[key] === 'desc' || sortObject[key] === -1 ? -1 : (sortObject[key] === 'skip' || sortObject[key] === 0 ? 0 : 1)
|
|
|
|
const keySort = (a, b, direction) => {
|
|
direction = direction !== null ? direction : 1
|
|
if (a === b) return 0
|
|
|
|
// If b > a, multiply by -1 to get the reverse direction.
|
|
return a > b ? direction : -1 * direction;
|
|
}
|
|
|
|
return array.sort((a, b) => {
|
|
let sorted = 0, index = 0
|
|
|
|
// Loop until sorted (-1 or 1) or until the sort keys have been processed.
|
|
while (sorted === 0 && index < sortKeys.length) {
|
|
const key = sortKeys[index]
|
|
if (key) {
|
|
const direction = sortObject[key]
|
|
let val1 = format[key]==='number'? (this.$empty(a[key])? 0 : this.$formatNumber(a[key])) : a[key]
|
|
let val2 = format[key]==='number'? (this.$empty(b[key])? 0 : this.$formatNumber(b[key])) : b[key]
|
|
sorted = keySort(val1, val2, direction)
|
|
index++
|
|
}
|
|
}
|
|
return sorted
|
|
})
|
|
}
|
|
|
|
//======================Fields====================
|
|
Vue.prototype.$createField = function(name,label,format,show, minwidth) {
|
|
let field = {name: name, label: label, format: format, show: show, minwidth: minwidth}
|
|
if(format==='number') {
|
|
field.unit = '1'
|
|
field.textalign = 'right'
|
|
}
|
|
return field
|
|
}
|
|
|
|
Vue.prototype.$updateFields = function(pagename, field, action) {
|
|
let pagedata = this.$store.state[pagename]
|
|
let copy = this.$copy(pagedata.fields)
|
|
let idx = this.$findIndex(copy, {name: field.name})
|
|
if(action==='delete') {
|
|
this.$delete(copy, idx)
|
|
if(pagedata.filters? pagedata.filters.length>0 : false) {
|
|
let index = this.$findIndex(pagedata.filters, {name: field.name})
|
|
if(index>=0) {
|
|
let copyFilter = this.$copy(this.pagedata.filters)
|
|
this.$delete(copyFilter, index)
|
|
this.$store.commit('updateState', {name: pagename, key: 'filterby', data: copyFilter})
|
|
}
|
|
}
|
|
} else {
|
|
idx>=0? copy[idx] = field : copy.push(field)
|
|
}
|
|
this.$store.commit("updateState", {name: pagename, key: "fields", data: copy})
|
|
},
|
|
|
|
Vue.prototype.$updateSeriesFields = function(fields) {
|
|
fields.filter(v=>v.series).map(field=>{
|
|
let obj = field.api==='finitem'? this.$findPeriod(field.series, 'period') : this.$findPeriod(field.series)
|
|
field.period = field.api==='finitem'? obj.code : obj
|
|
let idx = field.label.indexOf('[')
|
|
field.label = (idx>0? field.label.substring(0, idx) : field.label) + '[' + (field.api==='finitem'? obj.show : obj) + ']'
|
|
})
|
|
return fields
|
|
}
|
|
|
|
Vue.prototype.$updateSeriesFilters = function(filters, fields) {
|
|
if(fields.filter(v=>v.series).length===0) return filters
|
|
filters.map(v=>{
|
|
let found = fields.find(x=>x.name===v.name)
|
|
if(found? found.series : false) {
|
|
v.label = found.label
|
|
}
|
|
})
|
|
return filters
|
|
}
|
|
}
|
|
}) |