import { useStore } from '~/stores/index' export default defineNuxtPlugin(() => { const store = useStore() //==========Find & filter================= const 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 } const 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 }) } const 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============ const id = function() { return Math.random().toString(36).substring(2, 12).toUpperCase() } const empty = function(val) { return val === undefined || val === null || val === '' } const copy = function(val) { if (empty(val)) return val return JSON.parse(JSON.stringify(val)) } const 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] = clone(obj[key]); delete obj['isActiveClone']; } } return temp } const remove = function(arr, idx) { arr.splice(idx, 1) } const 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================= const isNumber = function(val) { if(empty(val)) return false val = val.toString().replace(/,/g, "") return !isNaN(val) } const numtoString = function(val, config = undefined) { if (empty(val)) return '0'; val = val.toString().replace(/,/g, ""); if (isNaN(val)) return; const defaultConfig = { type: 'vi-VN', decimal: undefined, mindecimal: undefined, hasUnit: false }; const finalConfig = { ...defaultConfig, ...config }; const { type, decimal, mindecimal, hasUnit } = finalConfig; const options = {}; if (typeof decimal === 'number') options.maximumFractionDigits = decimal || 0; if (mindecimal) options.minimumFractionDigits = mindecimal; const unit = hasUnit ? ' đ' : ''; return Number(val).toLocaleString(type, typeof decimal === 'number' ? options : undefined) + unit; } const formatNumber = function(val) { if(empty(val)) 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) } const formatUnit = function(val, unit, decimal, string, mindecimal) { val = 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).toFixed(decimal) } //==========Calculate================= const calc = function(fn) { return new Function('return ' + fn)() } const 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 : formatUnit(value, unit, decimal, true, decimal) var result = {success: true, value: value} } } catch(err) { var result = {success: false, value : undefined} } return result } const 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 = formatUnit(value, unit, decimal, true, decimal) return {success: true, value: value} } const 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 = multiSort(arr, {level: 'asc'}) let copy = data copy.map(v=>{ arr.map(x=>{ if(x.func) { let res = calculateFunc(v, x.cols, x.func, x.decimal, x.unit) if(res? res.success : false) v[x.name] = res.value } else { let res = calculate(v, x.tags, x.formula, x.decimal, x.unit) if(res? res.success : false) v[x.name] = res.value } }) }) return copy } const 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=>!empty(v[x])).length) } return obj } //====================Array==================== const formatArray = function(data, fields) { let args = fields.filter(v=>v.format==='number') data.map(v=>{ args.map(x=>{ v[x.name] = empty(v[x.name])? undefined : formatUnit(v[x.name], x.unit, x.decimal, true, x.decimal) }) }) return data } const 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()); } const 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 } const 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'? (empty(a[key])? 0 : this.$formatNumber(a[key])) : a[key] let val2 = format[key]==='number'? (empty(b[key])? 0 : this.$formatNumber(b[key])) : b[key] sorted = keySort(val1, val2, direction) index++ } } return sorted }) } //======================Fields==================== const 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 } const updateFields = function(pagename, field, action) { let pagedata = store[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}) } const updateSeriesFields = function(fields) { if (!Array.isArray(fields)) { fields = Object.values(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 } const 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 } //======================Export=============================== const exportExcel = function(data, filename, fields) { var _filename = filename + '.xlsx' let list = [] data.map(v=>{ let ele = {} fields.map(x=>{ let label = stripHtml(x.label) ele[label] = v[x.name] }) list.push(ele) }) var XLSX = require('xlsx') //workBook class function Workbook() { if(!(this instanceof Workbook)) return new Workbook() this.SheetNames = [] this.Sheets = {} } var exportBook = new Workbook(); var worksheet = XLSX.utils.json_to_sheet(list) exportBook.SheetNames.push('sheet1') exportBook.Sheets.sheet1 = worksheet XLSX.writeFile(exportBook, _filename) } return { provide: { find, findIndex, filter, id, empty, copy, clone, remove, stripHtml, isNumber, numtoString, formatNumber, formatUnit, calc, calculate, calculateFunc, calculateData, summary, formatArray, unique, arrayMove, multiSort, createField, updateFields, updateSeriesFields, updateSeriesFilters, exportExcel } } })