Files
web/app/components/datatable/ContextMenu.vue
2026-03-02 09:45:33 +07:00

541 lines
17 KiB
Vue

<template>
<span class="tooltip">
<a
class="mr-4"
@click="checkFilter() ? false : $emit('modalevent', { name: 'dosort', data: 'az' })"
>
<SvgIcon
v-bind="{ name: 'az.svg', type: checkFilter() ? 'grey' : 'primary', size: 22 }"
></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
v-html="'Sắp xếp tăng dần'"
></span>
</span>
<span class="tooltip">
<a
class="mr-4"
@click="checkFilter() ? false : $emit('modalevent', { name: 'dosort', data: 'za' })"
>
<SvgIcon
v-bind="{ name: 'az.svg', type: checkFilter() ? 'grey' : 'primary', size: 22 }"
></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Sắp xếp giảm dần</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="moveLeft()">
<SvgIcon v-bind="{ name: 'left5.png', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Chuyển cột sang trái</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="moveRight()">
<SvgIcon v-bind="{ name: 'right5.png', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Chuyển cột sang phải</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="resizeWidth()">
<SvgIcon v-bind="{ name: 'thick.svg', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Tăng độ rộng cột</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="resizeWidth(true)">
<SvgIcon v-bind="{ name: 'thin.svg', type: 'primary', size: 23 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Giảm độ rộng cột</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="hideField()">
<SvgIcon v-bind="{ name: 'eye-off.svg', type: 'primary', size: 23 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Ẩn cột</span
>
</span>
<!-- <template v-if="store.login ? store.login.is_admin : false"> -->
<span class="tooltip">
<a class="mr-4" @click="currentField.mandatory ? false : doRemove()">
<SvgIcon v-bind="{ name: 'bin.svg', type: 'primary', size: 23 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Xóa cột</span
>
</span>
<span class="tooltip">
<a
class="mr-4"
:class="currentField.format === 'number' ? null : 'has-text-grey-light'"
@click="
currentField.format === 'number'
? $emit('modalevent', { name: 'copyfield', data: currentField })
: false
"
>
<SvgIcon v-bind="{ name: 'copy.svg', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Sao chép cột</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="fieldList()">
<SvgIcon v-bind="{ name: 'menu4.png', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Danh sách cột</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="createField()">
<SvgIcon v-bind="{ name: 'add.png', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Tạo cột mới</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="tableOption()">
<SvgIcon v-bind="{ name: 'more.svg', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Tùy chọn bảng</span
>
</span>
<span class="tooltip">
<a class="mr-4" @click="saveSetting()">
<SvgIcon v-bind="{ name: 'save.svg', type: 'primary', size: 22 }"></SvgIcon>
</a>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Lưu thiết lập</span
>
</span>
<!-- </template> -->
<div class="panel-tabs mb-2">
<a
v-for="(v, i) in getMenu().filter((x) =>
currentField.format === 'number'
? currentField.formula
? true
: x.code !== 'formula'
: !['filter', 'formula'].find((y) => y === x.code)
)"
:key="i"
:class="selectTab.code === v.code ? 'is-active' : 'has-text-primary'"
@click="changeTab(v)"
>
{{ v.name }}
</a>
</div>
<div v-if="currentTab === 'detail'">
<p class="fs-14 mt-3">
<strong> Tên trường: </strong> {{ currentField.name }}
<a @click="copyContent(currentField.name)">
<span class="tooltip">
<SvgIcon
class="ml-1"
v-bind="{ name: 'copy.svg', type: 'primary', size: 16 }"
></SvgIcon>
<span
class="tooltiptext"
style="top: 100%; bottom: unset; min-width: max-content; left: 25px"
>Copy</span
>
</span>
</a>
</p>
<label class="label fs-14 mt-3">Mô tả<span class="has-text-danger"> *</span></label>
<div class="field has-addons">
<div class="control is-expanded">
<input
class="input fs-14"
type="text"
@change="changeLabel($event.target.value)"
v-model="label"
/>
</div>
<div class="control">
<button class="button" @click="editLabel()">
<SvgIcon v-bind="{ name: 'pen.svg', type: 'dark', size: 19 }"></SvgIcon>
</button>
</div>
</div>
<p class="help is-danger" v-if="errors.find((v) => v.name === 'label')">
{{ errors.find((v) => v.name === "label").msg }}
</p>
<div class="field mt-3">
<label class="label fs-14"
>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">
<span class="icon-text" v-if="radioType === v">
<SvgIcon
v-bind="{ name: 'radio-checked.svg', type: 'gray', size: 22 }"
></SvgIcon>
</span>
{{ v.name }}
</span>
</div>
</div>
<div class="field is-horizontal" v-if="currentField.format === 'number'">
<div class="field-body">
<div class="field">
<label class="label fs-14"
>Đơn vị <span class="has-text-danger"> * </span>
</label>
<div class="control">
<SearchBox
v-bind="{
vdata: moneyunit,
field: 'name',
column: ['name'],
first: true,
position: 'is-top-left',
}"
@option="selected('_account', $event)"
></SearchBox>
</div>
<p class="help has-text-danger" v-if="errors.find((v) => v.name === 'unit')">
{{ errors.find((v) => v.name === "unit").msg }}
</p>
</div>
<div class="field is-narrow">
<label class="label fs-14">Phần thập phân</label>
<div class="control">
<input
class="input"
type="text"
placeholder=""
v-model="decimal"
@input="changeDecimal($event.target.value)"
/>
</div>
</div>
</div>
</div>
<div class="field is-horizontal mt-3">
<div class="field-body">
<div class="field">
<label class="label fs-14">Định dạng nâng cao</label>
<p class="control fs-14">
<span
class="mr-4"
v-for="(v, i) in colorchoice.filter((v) => v.code !== 'condition')"
>
<a class="icon-text" @click="changeTemplate(v)">
<SvgIcon
v-bind="{
name: `radio-${radioTemplate === v.code ? '' : 'un'}checked.svg`,
type: 'gray',
size: 22,
}"
>
</SvgIcon>
</a>
{{ v.name }}
</span>
</p>
</div>
</div>
</div>
<p class="mt-3" v-if="radioTemplate === 'option'">
<button class="button is-primary is-small has-text-white" @click="showSidebar()">
<span class="fs-14">{{
`${currentField.template ? "Sửa" : "Tạo"} định dạng`
}}</span>
</button>
</p>
</div>
<div v-else-if="currentTab === 'value'">
<ScrollBox
v-bind="{
data: props.filterData,
name: props.field.name,
maxheight: '380px',
perpage: 20,
}"
@selected="doSelect"
/>
</div>
<Modal
v-bind="showmodal"
v-if="showmodal"
@label="changeLabel"
@updatefields="updateFields"
@close="close"
></Modal>
</template>
<script setup>
import { useStore } from "@/stores/index";
import ScrollBox from "~/components/datatable/ScrollBox";
const store = useStore();
const {
$copy,
$stripHtml,
$clone,
$arrayMove,
$snackbar,
$copyToClipboard,
} = useNuxtApp();
var props = defineProps({
pagename: String,
field: Object,
filters: Object,
filterData: Object,
width: String,
});
const emit = defineEmits(["modalevent", "changepos", "close"]);
var colorchoice = store.colorchoice;
var errors = [];
var currentTab = ref("value");
var currentField = $copy(props.field);
var pagedata = store[props.pagename];
var fields = [];
var label = currentField.label;
var showmodal = ref();
const checkFilter = function () {};
const getMenu = function () {
let field = currentField;
field.disable = "display,tooltip";
let arr = field.disable ? field.disable.split(",") : undefined;
let array = arr
? store.menuchoice.filter((v) => arr.findIndex((x) => x === v.code) < 0)
: store.menuchoice;
//if (store.login ? !(store.login.is_admin === false) : true) array = [array[0]];
return array;
};
var selectTab = getMenu()[0];
var datatype = store.datatype;
var current = 1;
var value1 = undefined;
var value2 = undefined;
var moneyunit = store.moneyunit;
var radioType = store.datatype.find((v) => v.code === currentField.format);
var selectUnit =
currentField.format === "number"
? moneyunit.find((v) => v.detail === currentField.unit)
: undefined;
var bgcolor = undefined;
var radioBGcolor = colorchoice.find((v) => v.code === "none");
var color = undefined;
var radioColor = colorchoice.find((v) => v.code === "none");
var textsize = undefined;
var radioSize = colorchoice.find((v) => v.code === "none");
var minwidth = undefined;
var radioWidth = colorchoice.find((v) => v.code === "none");
var radioMaxWidth = colorchoice.find((v) => v.code === "none");
var maxwidth = undefined;
var selectAlign = undefined;
var radioAlign = colorchoice.find((v) => v.code === "none");
var radioTemplate = ref(
colorchoice.find((v) => v.code === (currentField.template ? "option" : "none"))["code"]
);
var selectPlacement = store.placement.find((v) => v.code === "is-right");
var selectScheme = store.colorscheme.find((v) => v.code === "is-primary");
var radioTooltip = store.colorchoice.find((v) => v.code === "none");
var selectField = undefined;
var tags = currentField.tags
? currentField.tags.map((v) => fields.find((x) => x.name === v))
: [];
var formula = currentField.formula ? currentField.formula : undefined;
var decimal = currentField.decimal;
let shortmenu = store.menuchoice.filter((x) =>
currentField.format === "number"
? currentField.formula
? true
: x.code !== "formula"
: !["filter", "formula"].find((y) => y === x.code)
);
var selectTab = shortmenu.find((v) => selectTab.code === v.code)
? selectTab
: menuchoice.find((v) => v.code === "value");
var search = undefined;
// if(selectTab.code==='value') {
// let self = this
// setTimeout(function() {self.$refs[currentField.name]? self.$refs[currentField.name].focus() : false}, 50)
// }
//==============================================================
function moveLeft() {
let copy = $clone(pagedata);
let i = copy.fields.findIndex((v) => v.name === props.field.name);
let idx = i - 1 >= 0 ? i - 1 : copy.fields.length - 1;
$arrayMove(copy.fields, i, idx);
copy.update = { fields: copy.fields };
store.commit(props.pagename, copy);
emit("changepos");
}
function moveRight() {
let copy = $clone(pagedata);
let i = copy.fields.findIndex((v) => v.name === props.field.name);
let idx = copy.fields.length - 1 > i ? i + 1 : 0;
$arrayMove(copy.fields, i, idx);
copy.update = { fields: copy.fields };
store.commit(props.pagename, copy);
emit("changepos");
}
function hideField() {
let copy = $clone(store[props.pagename]);
let found = copy.fields.find((v) => v.name === props.field.name);
found.show = false;
copy.update = { fields: copy.fields };
store.commit(props.pagename, copy);
emit("close");
}
function doRemove() {
let copy = $clone(store[props.pagename]);
let idx = copy.fields.findIndex((v) => v.name === props.field.name);
copy.fields.splice(idx, 1);
copy.update = { fields: copy.fields };
store.commit(props.pagename, copy);
emit("close");
}
function fieldList() {
showmodal.value = {
component: "datatable/TableOption",
vbind: { pagename: props.pagename },
title: "Danh sách cột",
width: "50%",
height: "630px",
};
}
function tableOption() {
showmodal.value = {
component: "datatable/TableSetting",
vbind: { pagename: props.pagename },
title: "Tùy chọn bảng",
width: "40%",
height: "400px",
};
}
const getFields = function () {
fields = pagedata ? $copy(pagedata.fields) : [];
fields.map(
(v) => (v.caption = (v.label ? v.label.indexOf("<") >= 0 : false) ? v.name : v.label)
);
};
const doSelect = function (evt) {
emit("modalevent", { name: "selected", data: evt[props.field.name] });
};
const changeLabel = function (text) {
currentField.label = text;
updateFields(currentField);
};
function editLabel() {
showmodal.value = {
component: "datatable/EditLabel",
width: "500px",
height: "300px",
vbind: { label: label },
};
}
const changeTemplate = function (v) {
radioTemplate.value = v.code;
let copy = $copy(currentField);
copy.template = v.code;
updateFields(copy);
};
function createField() {
showmodal.value = {
component: "datatable/NewField",
vbind: { pagename: props.pagename },
title: "Tạo cột mới",
width: "50%",
height: "630px",
};
}
function copyContent(value) {
$copyToClipboard(value);
}
function close() {
showmodal.value = undefined;
}
const updateFields = function (field, type) {
let copy = $clone(store[props.pagename]);
let idx = copy.fields.findIndex((v) => v.name === field.name);
copy.fields[idx] = field;
store.commit(props.pagename, copy);
};
const changeTab = function (v) {
currentTab.value = v.code;
selectTab = v;
};
const saveSetting = function () {
showmodal.value = {
component: "datatable/MenuSave",
vbind: { pagename: props.pagename, classify: 4 },
title: "Lưu thiết lập",
width: "500px",
height: "400px",
};
};
const showSidebar = function () {
let event = { name: "template", field: currentField };
let title = "Danh sách cột";
if (event.name === "bgcolor")
title = `Đổi màu nền: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
else if (event.name === "color")
title = `Đổi màu chữ: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
else if (event.name === "template")
title = `Định dạng nâng cao: ${$stripHtml(event.field.label, 30)}`;
showmodal.value = {
component: "datatable/FormatOption",
vbind: { event: event, currentField: currentField, pagename: props.pagename },
width: "850px",
height: "700px",
title: title,
};
};
function resizeWidth(minus) {
let val = maxwidth || minwidth || 80;
val = minus ? parseInt(val - 0.1 * val) : parseInt(val + 0.1 * val);
if (val > 1000) return $snackbar("Độ rộng cột lớn hơn giới hạn cho phép");
else if (val < 20) return $snackbar("Độ rộng cột nhỏ hơn giới hạn cho phép");
radioMaxWidth = store.colorchoice.find((v) => v.code === "option");
radioWidth = store.colorchoice.find((v) => v.code === "option");
maxwidth = val;
currentField.maxwidth = val;
minwidth = val;
currentField.minwidth = val;
updateFields(currentField);
}
</script>