This commit is contained in:
Viet An
2026-06-11 09:16:36 +07:00
parent 7325230280
commit 03f3112573
5 changed files with 228 additions and 304 deletions

View File

@@ -28,14 +28,14 @@
v-for="(v, i) in filters" v-for="(v, i) in filters"
:key="i" :key="i"
> >
<div class="tags has-addons is-marginless"> <div class="tags has-addons">
<a <a
class="tag is-primary is-light is-marginless" class="tag is-primary is-light"
@click="showCondition(v)" @click="showCondition(v)"
>{{ v.label.indexOf(">") >= 0 ? $stripHtml(v.label, 30) : v.label }}</a >{{ v.label.indexOf(">") >= 0 ? $stripHtml(v.label, 30) : v.label }}</a
> >
<a <a
class="tag is-delete is-marginless" class="tag is-delete"
@click="removeFilter(i)" @click="removeFilter(i)"
></a> ></a>
</div> </div>
@@ -51,7 +51,7 @@
</div> </div>
</div> </div>
<div <div
class="table-container mb-0" class="table-container rounded mb-0"
ref="container" ref="container"
id="docid" id="docid"
> >
@@ -124,6 +124,7 @@
@changepage="changePage" @changepage="changePage"
/> />
</div> </div>
<!-- <pre class="fs-12">debug: pagename: {{ pagename }}</pre> -->
<Modal <Modal
v-if="showmodal" v-if="showmodal"
v-bind="showmodal" v-bind="showmodal"
@@ -145,7 +146,6 @@ const {
$deleterow, $deleterow,
$empty, $empty,
$find, $find,
$getdata,
$getEditRights, $getEditRights,
$formatNumber, $formatNumber,
$multiSort, $multiSort,
@@ -180,12 +180,6 @@ let tablesetting = $copy(pagedata.tablesetting || store.tablesetting);
if (!Array.isArray(tablesetting)) { if (!Array.isArray(tablesetting)) {
tablesetting = Object.values(tablesetting); tablesetting = Object.values(tablesetting);
} }
// console.log("props.pagename", props.pagename);
// console.log("pagedata.tablesetting", pagedata.tablesetting);
// console.log("store.tablesetting", store.tablesetting);
// console.log("tablesetting", tablesetting);
// var tablesetting = $copy(store.tablesetting);
// const tablesettingObj = Object.fromEntries(tablesetting.map((v) => [v.code, v]));
var perPage = Number($find(tablesetting, { code: "per-page" }, "detail")) || 20; var perPage = Number($find(tablesetting, { code: "per-page" }, "detail")) || 20;
var filters = $copy(pagedata.filters || []); var filters = $copy(pagedata.filters || []);
var currentField; var currentField;
@@ -357,142 +351,56 @@ const getStyle = function (field, record) {
if (field.maxwidth) val += ` max-width:${field.maxwidth}px; `; if (field.maxwidth) val += ` max-width:${field.maxwidth}px; `;
return val; return val;
}; };
const getDetail = (code) => tablesetting.find((v) => v.code === code)?.detail || "";
const getSettingStyle = function (name, field) { const getSettingStyle = function (name, field) {
if (!tablesetting || tablesetting.length === 0) { /** @type {CSSStyleDeclaration} */
// manual temp fix let styles;
tablesetting = [
{ switch (name) {
id: 4, case "container":
code: "per-page", styles = {
name: "Số dòng trong 1 trang", minHeight: `${getDetail("container-height")}rem`,
detail: "20", };
}, break;
{ case "table":
id: 8, styles = {
code: "header-filter-color", fontSize: `${getDetail("table-font-size")}px`,
name: "Màu chữ khi áp dụng Filter", backgroundColor: getDetail("table-background"),
detail: "#00cc66", color: getDetail("table-font-color"),
}, };
{ break;
id: 10, case "header":
code: "background", styles = {
name: "Màu nền background", backgroundColor: getDetail("header-background"),
detail: "#363636", borderColor: getDetail("header-border-color"),
}, minWidth: `${field.minwidth}px`,
{ maxWidth: `${field.maxwidth}px`,
id: 11, };
code: "table-background", break;
name: "Màu nền của bảng", case "menu":
detail: "#ffffff", const width = field?.menuwidth || getDetail("menu-width");
}, styles = {
{ overflow: "auto",
id: 12, width: `${width}rem`,
code: "table-font-color", minHeight: `${getDetail("menu-min-height")}rem`,
name: "Mầu chữ trong bảng", maxHeight: `${getDetail("menu-max-height")}rem`,
detail: "hsl(0, 0%, 14%)", };
}, break;
{ case "dropdown":
id: 13, const isFiltered = filters.find((v) => v.name === field.name);
code: "table-font-size",
name: "Cỡ chữ trong bảng", styles = {
detail: "12", fontSize: `${getDetail("header-font-size")}px`,
}, color: getDetail(isFiltered ? "header-filter-color" : "header-font-color"),
{ };
id: 14, default:
code: "header-background", break;
name: "Màu nền tiêu đề",
detail: "var(--bulma-primary-soft)",
},
{
id: 15,
code: "header-font-color",
name: "Màu chữ tiêu đề",
detail: "var(--bulma-primary-40)",
},
{
id: 16,
code: "header-font-size",
name: "Cỡ chữ tiêu đề",
detail: "12",
},
{
id: 17,
code: "container-height",
name: "Chiều cao container",
detail: "38",
},
{
id: 18,
code: "header-arrow",
name: "Mũi tên trỏ xuống",
detail: "no",
},
{
id: 19,
code: "menu-width",
name: "Chiều rộng menu",
detail: "20.6",
},
{
id: 20,
code: "menu-min-height",
name: "Chiều cao menu (nhỏ nhất)",
detail: "32",
},
{
id: 21,
code: "menu-max-height",
name: "Chiều cao menu (lớn nhất)",
detail: "37",
},
{
id: 22,
code: "show-menu",
name: "Hiển thị menu",
detail: "yes",
},
{
id: 23,
code: "note",
name: "Ghi chú",
detail: "@",
},
{
id: 24,
code: "td-border",
name: "Đường viền",
detail: "border: 1px solid #dbdbdb;",
},
];
} }
let value = ""; return styles;
if (name === "container") {
value = "min-height:" + tablesetting.find((v) => v.code === "container-height").detail + "rem; ";
} else if (name === "table") {
value += "background-color:" + tablesetting.find((v) => v.code === "table-background").detail + "; ";
value += "font-size:" + tablesetting.find((v) => v.code === "table-font-size").detail + "px;";
value += "color:" + tablesetting.find((v) => v.code === "table-font-color").detail + "; ";
} else if (name === "header") {
value += "background-color:" + tablesetting.find((v) => v.code === "header-background").detail + "; ";
if (field.minwidth) value += " min-width: " + field.minwidth + "px; ";
if (field.maxwidth) value += " max-width: " + field.maxwidth + "px; ";
} else if (name === "menu") {
let arg = tablesetting.find((v) => v.code === "menu-width").detail;
arg = field ? (field.menuwidth ? field.menuwidth : arg) : arg;
value += "width:" + arg + "rem; ";
value += "min-height:" + tablesetting.find((v) => v.code === "menu-min-height").detail + "rem; ";
value += "max-height:" + tablesetting.find((v) => v.code === "menu-max-height").detail + "rem; ";
value += "overflow:auto; ";
} else if (name === "dropdown") {
value += "font-size:" + tablesetting.find((v) => v.code === "header-font-size").detail + "px; ";
let found = filters.find((v) => v.name === field.name);
found
? (value += "color:" + tablesetting.find((v) => v.code === "header-filter-color").detail + "; ")
: (value += "color:" + tablesetting.find((v) => v.code === "header-font-color").detail + "; ");
}
return value;
}; };
function changePage(page) { function changePage(page) {
currentPage = page; currentPage = page;
updateShow(); updateShow();
@@ -628,8 +536,8 @@ const updateData = async function (newVal) {
//change attribute //change attribute
fields = $copy(newVal.columns); fields = $copy(newVal.columns);
let _fields = fields.filter((v) => v.show); let _fields = fields.filter((v) => v.show);
data.map((v) => { data.forEach((v) => {
_fields.map((x) => (v[`${x.name}color`] = getStyle(x, v))); _fields.forEach((x) => (v[`${x.name}color`] = getStyle(x, v)));
}); });
return updateShow(); return updateShow();
} }
@@ -647,7 +555,7 @@ const updateData = async function (newVal) {
} else fields = $copy(pagedata.fields); } else fields = $copy(pagedata.fields);
if (newVal.data || newVal.fields) { if (newVal.data || newVal.fields) {
let copy = $copy(newVal.data || data); let copy = $copy(newVal.data || data);
this.data = $calculateData(copy, fields); data = $calculateData(copy, fields);
let fields = fields.filter((v) => v.show); let fields = fields.filter((v) => v.show);
data.map((v) => { data.map((v) => {
fields.map((x) => (v[`${x.name}color`] = getStyle(x, v))); fields.map((x) => (v[`${x.name}color`] = getStyle(x, v)));
@@ -678,6 +586,9 @@ const tableStyle = getSettingStyle("table");
setTimeout(() => updateShow(), 200); setTimeout(() => updateShow(), 200);
</script> </script>
<style scoped> <style scoped>
th a {
color: inherit;
}
:deep(.table tbody tr:hover td, .table tbody tr:hover th) { :deep(.table tbody tr:hover td, .table tbody tr:hover th) {
--bulma-table-row-hover-background-color: var(--bulma-scheme-main-ter); --bulma-table-row-hover-background-color: var(--bulma-scheme-main-ter);
background-color: var(--bulma-table-row-hover-background-color); background-color: var(--bulma-table-row-hover-background-color);

View File

@@ -1,5 +1,6 @@
<template> <template>
<TimeOption <TimeOption
v-if="timeopt"
v-bind="{ v-bind="{
pagename: vpagename, pagename: vpagename,
api: api, api: api,
@@ -16,20 +17,18 @@
@manual-refresh="manualRefresh" @manual-refresh="manualRefresh"
@refresh-data="refreshData" @refresh-data="refreshData"
@import="openImportModal" @import="openImportModal"
class="mb-3" />
v-if="timeopt"
></TimeOption>
<DataTable <DataTable
v-if="pagedata"
v-bind="{ pagename: vpagename }" v-bind="{ pagename: vpagename }"
@edit="edit" @edit="edit"
@insert="insert" @insert="insert"
@dataevent="dataEvent" @dataevent="dataEvent"
v-if="pagedata"
/> />
<Modal <Modal
@close="showmodal = undefined"
v-bind="showmodal"
v-if="showmodal" v-if="showmodal"
v-bind="showmodal"
@close="showmodal = undefined"
/> />
</template> </template>
@@ -38,6 +37,7 @@ import TimeOption from "~/components/datatable/TimeOption";
import { useStore } from "~/stores/index"; import { useStore } from "~/stores/index";
// [FIX] Thêm onActivated, onDeactivated để xử lý KeepAlive // [FIX] Thêm onActivated, onDeactivated để xử lý KeepAlive
import { ref, watch, onBeforeUnmount, onActivated, onDeactivated } from "vue"; import { ref, watch, onBeforeUnmount, onActivated, onDeactivated } from "vue";
import DataTable from "~/components/datatable/DataTable.vue";
const emit = defineEmits(["modalevent", "dataevent", "dataUpdated"]); const emit = defineEmits(["modalevent", "dataevent", "dataUpdated"]);
const store = useStore(); const store = useStore();
@@ -408,7 +408,7 @@ const getApi = async () => {
pagedata.value = $setpage(vpagename, row, obj); pagedata.value = $setpage(vpagename, row, obj);
const copy = $clone(pagedata.value); const copy = $clone(pagedata.value);
copy.data = data; copy.data = data;
copy.update = { data: data }; copy.update = { data };
store.commit(vpagename, copy); store.commit(vpagename, copy);
}; };

View File

@@ -8,7 +8,7 @@
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
:value="tablesetting.find((v) => v.code === 'table-font-size').detail" :value="getDetail('table-font-size')"
@change="changeSetting($event.target.value, 'table-font-size')" @change="changeSetting($event.target.value, 'table-font-size')"
/> />
</p> </p>
@@ -21,7 +21,7 @@
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
:value="tablesetting.find((v) => v.code === 'header-font-size').detail" :value="getDetail('header-font-size')"
@change="changeSetting($event.target.value, 'header-font-size')" @change="changeSetting($event.target.value, 'header-font-size')"
/> />
</p> </p>
@@ -34,7 +34,7 @@
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
:value="tablesetting.find((v) => v.code === 'per-page').detail" :value="getDetail('per-page')"
@change="changeSetting($event.target.value, 'per-page')" @change="changeSetting($event.target.value, 'per-page')"
/> />
</p> </p>
@@ -46,14 +46,14 @@
<p class="control is-flex is-gap-1"> <p class="control is-flex is-gap-1">
<input <input
type="color" type="color"
:value="tablesetting.find((v) => v.code === 'table-background').detail" :value="getDetail('table-background')"
@change="changeSetting($event.target.value, 'table-background')" @change="changeSetting($event.target.value, 'table-background')"
/> />
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
placeholder="#f29384, var(--bulma-blue)" placeholder="#f29384, var(--bulma-blue)"
:value="tablesetting.find((v) => v.code === 'table-background').detail" :value="getDetail('table-background')"
@change="changeSetting($event.target.value, 'table-background')" @change="changeSetting($event.target.value, 'table-background')"
/> />
</p> </p>
@@ -65,14 +65,14 @@
<p class="control is-flex is-gap-1"> <p class="control is-flex is-gap-1">
<input <input
type="color" type="color"
:value="tablesetting.find((v) => v.code === 'table-font-color').detail" :value="getDetail('table-font-color')"
@change="changeSetting($event.target.value, 'table-font-color')" @change="changeSetting($event.target.value, 'table-font-color')"
/> />
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
placeholder="#f29384, var(--bulma-blue)" placeholder="#f29384, var(--bulma-blue)"
:value="tablesetting.find((v) => v.code === 'table-font-color').detail" :value="getDetail('table-font-color')"
@change="changeSetting($event.target.value, 'table-font-color')" @change="changeSetting($event.target.value, 'table-font-color')"
/> />
</p> </p>
@@ -84,14 +84,14 @@
<p class="control is-flex is-gap-1"> <p class="control is-flex is-gap-1">
<input <input
type="color" type="color"
:value="tablesetting.find((v) => v.code === 'header-font-color').detail" :value="getDetail('header-font-color')"
@change="changeSetting($event.target.value, 'header-font-color')" @change="changeSetting($event.target.value, 'header-font-color')"
/> />
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
placeholder="#f29384, var(--bulma-blue)" placeholder="#f29384, var(--bulma-blue)"
:value="tablesetting.find((v) => v.code === 'header-font-color').detail" :value="getDetail('header-font-color')"
@change="changeSetting($event.target.value, 'header-font-color')" @change="changeSetting($event.target.value, 'header-font-color')"
/> />
</p> </p>
@@ -110,7 +110,7 @@
class="input fs-13 h-full" class="input fs-13 h-full"
type="text" type="text"
placeholder="#f29384, var(--bulma-blue)" placeholder="#f29384, var(--bulma-blue)"
:value="tablesetting.find((v) => v.code === 'header-background').detail" :value="getDetail('header-background')"
@change="changeSetting($event.target.value, 'header-background')" @change="changeSetting($event.target.value, 'header-background')"
/> />
@@ -118,7 +118,7 @@
ref="renderedHeaderBg" ref="renderedHeaderBg"
class="is-hidden" class="is-hidden"
:style="{ :style="{
backgroundColor: tablesetting.find((v) => v.code === 'header-background').detail, backgroundColor: getDetail('header-background'),
}" }"
></div> ></div>
</div> </div>
@@ -130,14 +130,14 @@
<p class="control is-flex is-gap-1"> <p class="control is-flex is-gap-1">
<input <input
type="color" type="color"
:value="tablesetting.find((v) => v.code === 'header-filter-color').detail" :value="getDetail('header-filter-color')"
@change="changeSetting($event.target.value, 'header-filter-color')" @change="changeSetting($event.target.value, 'header-filter-color')"
/> />
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
placeholder="#f29384, var(--bulma-blue)" placeholder="#f29384, var(--bulma-blue)"
:value="tablesetting.find((v) => v.code === 'header-filter-color').detail" :value="getDetail('header-filter-color')"
@change="changeSetting($event.target.value, 'header-filter-color')" @change="changeSetting($event.target.value, 'header-filter-color')"
/> />
</p> </p>
@@ -150,11 +150,7 @@
<input <input
class="input fs-13" class="input fs-13"
type="text" type="text"
:value=" :value="getDetail('td-border')"
tablesetting.find((v) => v.code === 'td-border')
? tablesetting.find((v) => v.code === 'td-border').detail
: undefined
"
@change="changeSetting($event.target.value, 'td-border')" @change="changeSetting($event.target.value, 'td-border')"
/> />
</p> </p>
@@ -175,15 +171,20 @@ const props = defineProps({
}); });
const emit = defineEmits(["close"]); const emit = defineEmits(["close"]);
const { $copy, $clone, $empty, $store } = useNuxtApp(); const { $copy, $clone, $empty, $store } = useNuxtApp();
var pagedata = $clone($store[props.pagename]); let pagedata = $clone($store[props.pagename]);
if (!pagedata.tablesetting) {
pagedata.tablesetting = [];
}
if (!Array.isArray(pagedata.tablesetting)) { if (!Array.isArray(pagedata.tablesetting)) {
pagedata.tablesetting = Object.values(pagedata.tablesetting); pagedata.tablesetting = Object.values(pagedata.tablesetting);
} }
const getDetail = (code) => tablesetting.find((v) => v.code === code)?.detail || "";
const errors = ref([]); const errors = ref([]);
let radioNote = "no"; let radioNote = "no";
var tablesetting = pagedata.tablesetting; var tablesetting = pagedata.tablesetting;
let found = tablesetting.find((v) => v.code === "note"); let found = getDetail("note");
if (found ? found.detail !== "@" : false) radioNote = "yes"; if (found !== "@") radioNote = "yes";
function changeSetting(value, code) { function changeSetting(value, code) {
if (code === "note" && $empty(value)) return; if (code === "note" && $empty(value)) return;

View File

@@ -1,157 +1,164 @@
<template> <template>
<div <div
v-if="timeRanges || !enableTime" v-if="timeRanges || !enableTime"
class="has-text-primary fixed-grid has-12-cols" class="block is-flex is-justify-content-space-between is-flex-wrap-wrap"
style="border-bottom: 2px solid var(--bulma-grey-lighter)" style="gap: 0.5rem 0"
> >
<div class="grid mb-3"> <div
<div v-if="enableTime"
v-if="enableTime" class="is-flex is-align-items-center is-flex-wrap-wrap"
class="cell is-col-span-7 is-flex is-align-items-center is-flex-wrap-wrap" style="gap: 0.5rem 0.75rem"
style="gap: 0.5rem 1rem" >
> <span class="icon-text is-flex-wrap-nowrap font-semibold has-text-orange">
<Caption <span class="icon">
:title="lang === 'vi' ? 'Thời gian' : 'Time'" <Icon
type="has-text-orange" name="material-symbols:history-rounded"
/> :size="18"
<div />
</span>
<span
v-if="this.store.viewport > 2"
style="text-wrap: nowrap"
>{{ lang === "vi" ? "Thời gian" : "Time" }}</span
>
</span>
<div class="buttons has-addons">
<button
v-for="v in timeRanges" v-for="v in timeRanges"
:key="v.code" :key="v.code"
:class="['is-flex is-align-items-center', v.code !== current && 'is-clickable']" :class="['timeRange button is-small fs-13 rounded-md is-light', v.code === current && 'is-orange']"
style="gap: 0.35rem" style="gap: 0.4rem"
@click="v.code !== current && changeOption(v)" @click="v.code !== current && changeOption(v)"
> >
<span <span
:class="v.code === current ? 'font-bold has-text-orange' : 'font-medium has-text-grey'" :class="v.code === current ? 'font-bold has-text-orange' : 'font-medium'"
style="text-wrap: nowrap" style="text-wrap: nowrap"
> >
{{ v.name }} {{ v.name }}
</span> </span>
<span <span
:class="[ :class="[
'tag rounded-md w-5 h-6 fs-13', 'count rounded-md h-full px-1',
v.code === current v.code === current ? 'font-bold has-text-orange has-background-orange-90' : 'font-medium has-text-grey',
? 'font-bold has-text-orange has-background-orange-90'
: 'font-medium has-text-grey has-background-grey-white-ter',
]" ]"
>{{ v.count }}</span >{{ v.count }}</span
> >
</div> </button>
<span
v-if="newDataAvailable"
class="has-text-danger-50 px-3 py-1.5 has-background-danger-95 rounded-md is-italic fs-14"
> dữ liệu mới, vui lòng làm mới.</span
>
</div> </div>
<div <span
class="cell is-col-span-5 is-flex is-align-items-center is-flex-wrap-wrap" v-if="newDataAvailable"
style="gap: 0.25rem 0.5rem" class="has-text-danger-50 px-3 py-1.5 has-background-danger-95 rounded-md is-italic fs-14"
> >
<Caption dữ liệu mới, vui lòng làm mới.
:title="lang === 'vi' ? `Tìm ${this.store.viewport > 2 ? 'kiếm' : ''}` : 'Search'" </span>
type="has-text-orange" </div>
/> <div
<input :class="['is-flex is-align-items-center is-flex-wrap-wrap', !enableTime && 'ml-auto']"
id="input" style="gap: 0.25rem 0.5rem"
type="text" >
v-model="text" <Caption
@keyup="startSearch" :title="lang === 'vi' ? `Tìm ${this.store.viewport > 2 ? 'kiếm' : ''}` : 'Search'"
:placeholder="lang === 'vi' ? 'Nhập từ khóa...' : 'Enter keyword...'" type="has-text-orange"
class="input is-orange fs-12" />
:style="{ <input
maxWidth: '150px', id="input"
width: this.store.viewport === 1 ? '150px' : 'auto', type="text"
}" v-model="text"
/> @keyup="startSearch"
<div class="field has-addons is-flex-wrap-wrap is-align-items-center"> :placeholder="lang === 'vi' ? 'Nhập từ khóa...' : 'Enter keyword...'"
<p class="input is-orange fs-12 max-w-50"
v-if="importdata && $getEditRights()" :style="{
class="control" width: this.store.viewport === 1 ? '150px' : 'auto',
}"
/>
<div class="field has-addons is-flex-wrap-wrap is-align-items-center">
<p
v-if="importdata && $getEditRights()"
class="control"
>
<button
class="button is-ghost has-text-orange fs-14"
@click="openImport()"
> >
<button <span class="icon">
class="button is-ghost has-text-orange fs-14" <Icon
@click="openImport()" name="material-symbols:upload-rounded"
> :size="22"
<span class="icon"> />
<Icon
name="material-symbols:upload-rounded"
:size="22"
/>
</span>
</button>
<span
class="tooltiptext has-background-orange-soft has-text-orange-bold"
style="top: 110%; bottom: unset; min-width: max-content; left: -45px"
>
Nhập dữ liệu
</span> </span>
</p> </button>
<p <span
v-if="enableAdd && $getEditRights()" class="tooltiptext has-background-orange-soft has-text-orange-bold"
class="control" style="top: 110%; bottom: unset; min-width: max-content; left: -45px"
> >
<button Nhập dữ liệu
class="button is-ghost has-text-orange fs-14" </span>
@click="$emit('add')" </p>
> <p
<span class="icon"> v-if="enableAdd && $getEditRights()"
<Icon class="control"
name="material-symbols:add-rounded" >
:size="22" <button
/> class="button is-ghost has-text-orange fs-14"
</span> @click="$emit('add')"
</button> >
<span <span class="icon">
class="tooltiptext has-background-orange-soft has-text-orange-bold" <Icon
style="top: 110%; bottom: unset; min-width: max-content; left: -45px" name="material-symbols:add-rounded"
> :size="22"
Thêm mới />
</span> </span>
</p> </button>
<p class="control"> <span
<button class="tooltiptext has-background-orange-soft has-text-orange-bold"
class="button is-ghost has-text-orange fs-14" style="top: 110%; bottom: unset; min-width: max-content; left: -45px"
@click="$emit('excel')" >
> Thêm mới
<span class="icon"> </span>
<Icon </p>
name="mdi:microsoft-excel" <p class="control">
:size="22" <button
/> class="button is-ghost has-text-orange fs-14"
</span> @click="$emit('excel')"
</button> >
<span <span class="icon">
class="tooltiptext has-background-orange-soft has-text-orange-bold" <Icon
style="top: 110%; bottom: unset; min-width: max-content; left: -45px" name="mdi:microsoft-excel"
> :size="22"
Xuất Excel />
</span> </span>
</p> </button>
<p class="control"> <span
<button class="tooltiptext has-background-orange-soft has-text-orange-bold"
class="button is-ghost has-text-orange fs-14" style="top: 110%; bottom: unset; min-width: max-content; left: -45px"
@click="$emit('refresh-data')" >
> Xuất Excel
<span class="icon"> </span>
<Icon </p>
name="material-symbols:refresh-rounded" <p class="control">
:size="22" <button
/> class="button is-ghost has-text-orange fs-14"
</span> @click="$emit('refresh-data')"
</button> >
<span <span class="icon">
class="tooltiptext has-background-orange-soft has-text-orange-bold" <Icon
style="top: 110%; bottom: unset; min-width: max-content; left: -45px" name="material-symbols:refresh-rounded"
> :size="22"
Làm mới />
</span> </span>
</p> </button>
<Icon <span
v-if="loading" class="tooltiptext has-background-orange-soft has-text-orange-bold"
name="svg-spinners:180-ring-with-bg" style="top: 110%; bottom: unset; min-width: max-content; left: -45px"
:size="22" >
/> Làm mới
</div> </span>
</p>
<Icon
v-if="loading"
name="svg-spinners:180-ring-with-bg"
:size="22"
/>
</div> </div>
</div> </div>
<Modal <Modal
@@ -443,3 +450,8 @@ export default {
}, },
}; };
</script> </script>
<style scoped>
.timeRange.is-orange:hover .count {
background-color: var(--bulma-orange-85) !important;
}
</style>

View File

@@ -6,7 +6,7 @@ const props = defineProps({
<template> <template>
<div <div
class="w-full h-4" class="size-5 rounded-full"
:style="{ backgroundColor: color, outline: '1px solid var(--bulma-grey-lighter)' }" :style="{ backgroundColor: color }"
></div> ></div>
</template> </template>