334 lines
14 KiB
Vue
334 lines
14 KiB
Vue
<template>
|
|
<div class="tabs is-boxed">
|
|
<ul>
|
|
<li :class="selectType.code===v.code? 'is-active fs-16' : 'fs-16'" v-for="v in fieldType">
|
|
<a @click="selectType = v"><span>{{ v.name }}</span></a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<template v-if="selectType.code==='formula'">
|
|
<b-radio :class="i===1? 'ml-5' : null" v-model="choice" v-for="(v,i) in choices" :key="i"
|
|
:native-value="v.code">
|
|
<span :class="v.code===choice? 'fsb-16' : 'fs-16'">{{v.name}}</span>
|
|
</b-radio>
|
|
<div class="has-background-light mt-3 px-3 py-3">
|
|
<div class="tags are-medium mb-0" v-if="choice==='function'">
|
|
<span :class="`tag ${func===v.code? 'is-primary' : 'is-dark'} is-rounded is-clickable`"
|
|
v-for="(v,i) in funcs" :key="i" @click="changeFunc(v)" @dblclick="addFunc(v)">{{v.name}}</span>
|
|
</div>
|
|
<template v-else>
|
|
<div class="field px-0 mx-0">
|
|
<label class="label fs-14">Chọn trường<span class="has-text-danger"> *</span> </label>
|
|
<div class="control">
|
|
<!--<b-taginput
|
|
size="is-small"
|
|
v-model="tags"
|
|
:data="fields.filter(v=>v.format==='number')"
|
|
type="is-dark is-light"
|
|
autocomplete
|
|
:open-on-focus="true"
|
|
field="caption"
|
|
icon="plus"
|
|
placeholder="Chọn trường"
|
|
>
|
|
<template slot-scope="props">
|
|
<span class="mr-3 has-text-danger">{{props.option.name}}</span>
|
|
<span :class="tags.find(v=>v.id===props.option.id)? 'has-text-dark' : ''">{{$stripHtml(props.option.label,50)}}</span>
|
|
</template>
|
|
<template slot="empty">
|
|
Không có trường thỏa mãn
|
|
</template>
|
|
</b-taginput>-->
|
|
</div>
|
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='tags')"> {{errors.find(v=>v.name==='tags').message}} </p>
|
|
</div>
|
|
<div class="field mt-3" v-if="tags.length>0">
|
|
<p class="help is-primary mb-1">Click đúp vào để thêm vào công thức tính.</p>
|
|
<div class="tags mb-2">
|
|
<span @dblclick="formula = formula? (formula + ' ' + v.name) : v.name" class="tag is-dark is-rounded is-clickable"
|
|
v-for="v in tags">
|
|
{{v.name}}
|
|
</span>
|
|
</div>
|
|
<div class="tags">
|
|
<span v-for="(v,i) in operator" :key="i">
|
|
<span @dblclick="addOperator(v)" class="tag is-primary is-rounded is-clickable mr-4">
|
|
<span class="fs-16">{{v.code}}</span>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="field mt-3 px-0 mx-0">
|
|
<label class="label fs-14">Công thức tính <span class="has-text-danger"> * </span> </label>
|
|
<p class="control">
|
|
<textarea class="textarea" rows="3" type="text" :placeholder="placeholder" v-model="formula"> </textarea>
|
|
</p>
|
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='formula')"> {{errors.find(v=>v.name==='formula').message}} </p>
|
|
</div>
|
|
</div>
|
|
<div class="field is-horizontal mt-3 px-0 mx-0">
|
|
<div class="field-body">
|
|
<div class="field">
|
|
<label class="label fs-14">Hiển thị theo <span class="has-text-danger"> * </span> </label>
|
|
<div class="control">
|
|
<b-autocomplete
|
|
size="is-small"
|
|
icon-right="magnify"
|
|
:value="selectUnit? selectUnit.name : ''"
|
|
placeholder=""
|
|
:keep-first=true
|
|
:open-on-focus=true
|
|
:data="moneyunit"
|
|
field="name"
|
|
@select="option => selectUnit = option">
|
|
</b-autocomplete>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label fs-14">Phần thập phân</label>
|
|
<div class="control">
|
|
<input class="input is-small" type="text" placeholder="" v-model="decimal">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="field px-0 mx-0">
|
|
<label class="label">Tên trường <span class="has-text-danger"> * </span> </label>
|
|
<p class="control">
|
|
<input class="input" type="text" placeholder="Tên trường phải là duy nhất" v-model="name"
|
|
:readonly="selectType? selectType.code==='formula': false">
|
|
</p>
|
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='name')"> {{errors.find(v=>v.name==='name').message}} </p>
|
|
<p class="help has-text-primary" v-else> Tên trường do hệ thống tự sinh.</p>
|
|
</div>
|
|
<div class="mt-5">
|
|
<label class="label">Mô tả<span class="has-text-danger"> *</span></label>
|
|
<div class="field has-addons">
|
|
<div class="control is-expanded" >
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="label"
|
|
/>
|
|
</div>
|
|
<div class="control">
|
|
<button class="button" @click="editLabel()">
|
|
<span><SvgIcon v-bind="{name: 'pen.svg', type: 'dark', size: 17}"></SvgIcon></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='label')"> {{errors.find(v=>v.name==='label').message}} </p>
|
|
</div>
|
|
<div class="field mt-5" v-if="selectType.code==='empty'">
|
|
<label class="label"
|
|
>Kiểu dữ liệu
|
|
<span class="has-text-danger"> * </span>
|
|
</label>
|
|
<div class="control fs-14">
|
|
<span class="mr-4" v-for="(v,i) in datatype">
|
|
<a class="icon-text" @click="changeType(v)">
|
|
<SvgIcon v-bind="{name: `radio-${radioType.code===v.code? '' : 'un'}checked.svg`, type: 'gray', size: 22}"></SvgIcon>
|
|
</a>
|
|
{{v.name}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="field mt-5">
|
|
<p class="control">
|
|
<a class="button is-primary has-text-white"
|
|
@click="selectType.code==='formula'? createField() : createEmptyField()">Tạo cột</a>
|
|
</p>
|
|
</div>
|
|
<Modal v-bind="showmodal" v-if="showmodal" @label="changeLabel" @close="close"></Modal>
|
|
</template>
|
|
<script setup>
|
|
import { useStore } from '@/stores/index'
|
|
import ScrollBox from '~/components/datatable/ScrollBox'
|
|
const emit = defineEmits(['modalevent'])
|
|
const store = useStore()
|
|
const { $id, $copy, $clone, $empty, $stripHtml, $createField, $calc, $isNumber } = useNuxtApp()
|
|
var props = defineProps({
|
|
pagename: String,
|
|
field: Object,
|
|
filters: Object,
|
|
filterData: Object,
|
|
width: String
|
|
})
|
|
const moneyunit = store.moneyunit
|
|
const datatype = store.datatype
|
|
var showmodal = ref()
|
|
var pagedata = store[props.pagename]
|
|
var selectUnit = moneyunit.find(v=>v.code==='one')
|
|
var data = []
|
|
var current = 1
|
|
var filterData = []
|
|
var loading = false
|
|
var fieldType = [{code: 'formula', name: 'Tạo công thức'}, {code: 'empty', name: 'Tạo cột rỗng'}]
|
|
var errors = []
|
|
var tags = []
|
|
var formula = undefined
|
|
var name = `f${$id().toLocaleLowerCase()}`
|
|
var label = undefined
|
|
var errors = []
|
|
var selectType = fieldType.find(v=>v.code==='empty')
|
|
var radioType = ref(datatype.find(v=>v.code==='string'))
|
|
var fields = []
|
|
var options = undefined
|
|
var columns = $copy(pagedata.fields.filter(v=>v.format==='number'))
|
|
var decimal = undefined
|
|
var choices = [{code: 'column', name: 'Dùng cột dữ liệu'}, {code: 'function', name: 'Dùng hàm số'}]
|
|
var choice = 'column'
|
|
var funcs = [{code: 'sum', name: 'Sum'}, {code: 'max', name: 'Max'}, {code: 'min', name: 'Min'}, {code: 'avg', name: 'Avg'}]
|
|
var func = 'sum'
|
|
var placeholder = 'Minh hoạ công thức: f10001 + f10002'
|
|
var args = undefined
|
|
var operator = [{code: '+', name: 'Cộng'}, {code: '-', name: 'Trừ'}, {code: '*', name: 'Nhân'}, {code: '/', name: 'Chia'}, {code: '>', name: 'Lớn hơn'},
|
|
{code: '>=', name: 'Lớn hơn hoặc bằng'}, {code: '<', name: 'Nhỏ hơn'}, {code: '<=', name: 'Nhỏ hơn hoặc bằng'}, {code: '==', name: 'Bằng'},
|
|
{code: '&&', name: 'Và'}, {code: '||', name: 'Hoặc'}, {code: 'iif', name: 'Điều kiện rẽ nhánh'}]
|
|
function editLabel() {
|
|
if($empty(label)) return
|
|
showmodal.value = {component: 'datatable/EditLabel', width: '500px', height: '300px', vbind: {label: label}}
|
|
}
|
|
function close() {
|
|
showmodal.value = null
|
|
}
|
|
function changeLabel(evt) {
|
|
label = evt
|
|
showmodal.value = null
|
|
}
|
|
function changeType(v) {
|
|
radioType.value = v
|
|
}
|
|
function addFunc(v) {
|
|
formula = (formula? formula + ' ' : '') + v.name + '(C0: C2)'
|
|
}
|
|
function addOperator(v) {
|
|
let text = v.code==='iif'? 'a>b? c : d' : v.code
|
|
formula = `${formula || ''} ${text}`
|
|
}
|
|
function changeFunc(v) {
|
|
placeholder = `${v.name}(C0:C2) hoặc ${v.name}(C0,C1,C2). C là viết tắt của cột dữ liệu, số thứ tự của cột bắt đầu từ 0`
|
|
func = v.code
|
|
}
|
|
function getFields() {
|
|
fields = pagedata? $copy(pagedata.fields) : []
|
|
fields.map(v=>v.caption = (v.label? v.label.indexOf('<')>=0 : false)? v.name : v.label)
|
|
}
|
|
function checkFunc() {
|
|
let error = false
|
|
let val = formula.trim().replaceAll(' ', '')
|
|
if(val.toLowerCase().indexOf(func)<0) error = true
|
|
let start = val.toLowerCase().indexOf('(')
|
|
let end = val.toLowerCase().indexOf(')')
|
|
if( start<0 || end<0) error = true
|
|
let content = val.substring(start+1, end)
|
|
if($empty(content)) error = true
|
|
let content1 = content.replaceAll(':', ',')
|
|
let arr = content1.split(',')
|
|
arr.map(v=>{
|
|
let arr1 = v.toLowerCase().split('c')
|
|
if(arr1.length!==2) error = true
|
|
else if(!$isNumber(arr1[1])) error = true
|
|
})
|
|
return error? 'error' : content
|
|
}
|
|
function checkValid() {
|
|
errors = []
|
|
if(tags.length===0 && choice==='column') {
|
|
errors.push({name: 'tags', message: 'Chưa chọn trường xây dựng công thức.'})
|
|
}
|
|
if(!$empty(formula)? $empty(formula.trim()) : true) {
|
|
errors.push({name: 'formula', message: 'Công thức không được bỏ trống.'})
|
|
}
|
|
if(!$empty(label)? $empty(label.trim()) : true )
|
|
errors.push({name: 'label', message: 'Mô tả không được bỏ trống.'})
|
|
else if(pagedata.fields.find(v=>v.label.toLowerCase()===label.toLowerCase())) {
|
|
errors.push({name: 'label', message: 'Mô tả bị trùng. Hãy đặt mô tả khác.'})
|
|
}
|
|
if(errors.length>0) return false
|
|
//check formula in case use column
|
|
if(choice==='column') {
|
|
let val = $copy(formula)
|
|
tags.forEach(v => {
|
|
let myRegExp = new RegExp(v.name, 'g')
|
|
val = val.replace(myRegExp, Math.random())
|
|
})
|
|
try {
|
|
let value = $calc(val)
|
|
if(isNaN(value) || value===Number.POSITIVE_INFINITY || value===Number.NEGATIVE_INFINITY) {
|
|
errors.push({name: 'formula', message: 'Công thức không hợp lệ'})
|
|
}
|
|
}
|
|
catch(err) {
|
|
console.log(err)
|
|
errors.push({name: 'formula', message: 'Công thức không hợp lệ'})
|
|
}
|
|
} else {
|
|
if(checkFunc()==='error') errors.push({name: 'formula', message: `Hàm ${func.toUpperCase()} không hợp lệ`})
|
|
}
|
|
return errors.length>0? false : true
|
|
}
|
|
function createField() {
|
|
if(!checkValid()) return
|
|
let field = $createField(name.trim(), label.trim(), 'number', true)
|
|
field.formula = formula.trim().replaceAll(' ', '')
|
|
if(choice==='function') {
|
|
field.func = func
|
|
field.vals = checkFunc()
|
|
} else field.tags = tags.map(v=>v.name)
|
|
field.level = Math.max(...pagedata.fields.map(v=>v.level? v.level : 0)) + 1
|
|
field.unit = selectUnit.detail
|
|
field.decimal = decimal
|
|
field.disable = 'search,value'
|
|
let copy = $copy(pagedata)
|
|
copy.fields.push(field)
|
|
store.commit(props.pagename, copy)
|
|
emit('newfield', field)
|
|
tags = []
|
|
formula = undefined
|
|
label = undefined
|
|
name = `f${$id()}`
|
|
emit('close')
|
|
}
|
|
function createEmptyField() {
|
|
errors = []
|
|
if(!$empty(name)? $empty(name.trim()) : true )
|
|
errors.push({name: 'name', message: 'Tên không được bỏ trống.'})
|
|
else if(pagedata.fields.find(v=>v.name.toLowerCase()===name.toLowerCase())) {
|
|
errors.push({name: 'name', message: 'Tên trường bị trùng. Hãy đặt tên khác.'})
|
|
}
|
|
if(!$empty(label)? $empty(label.trim()) : true )
|
|
errors.push({name: 'label', message: 'Mô tả không được bỏ trống.'})
|
|
else if(pagedata.fields.find(v=>v.label.toLowerCase()===label.toLowerCase())) {
|
|
errors.push({name: 'label', message: 'Mô tả bị trùng. Hãy đặt mô tả khác.'})
|
|
}
|
|
if(errors.length>0) return
|
|
let field = $createField(name.trim(), label.trim(), radioType.value.code, true)
|
|
if(selectType.code==='chart') field = createChartField()
|
|
let copy = $clone(pagedata)
|
|
copy.fields.push(field)
|
|
copy.update = {fields: copy.fields}
|
|
store.commit(props.pagename, copy)
|
|
//pagedata = copy
|
|
emit('newfield', field)
|
|
label = undefined
|
|
name = `f${$id()}`
|
|
emit('close')
|
|
}
|
|
function createChartField() {
|
|
let array = pagedata.fields.filter(v=>v.format==='number' && v.show)
|
|
if(args) array = $copy(args)
|
|
let text = ''
|
|
array.map((v,i)=>text += `'${v.name}${i<array.length-1? "'," : "'"}`)
|
|
let label = ''
|
|
array.map((v,i)=>label += `'${$stripHtml(v.label)}${i<array.length-1? "'," : "'"}`)
|
|
let field = $createField(name.trim(), label.trim(), radioType.value.code, true)
|
|
field.chart = 'yes'
|
|
field.template = `<TrendingChart class="is-clickable" v-bind="{row: row, fields: [${text}], labels: [${label}], width: '80', height: '26', 'header': ['stock_code', 'name']}"/>`
|
|
return field
|
|
}
|
|
//============
|
|
getFields()
|
|
</script> |