801 lines
21 KiB
Vue
801 lines
21 KiB
Vue
<template>
|
|
<div class="report report-daily-page">
|
|
<div class="report-header mb-4">
|
|
<h2 class="header-report-title">Báo cáo tình hình giao dịch & thanh toán theo ngày</h2>
|
|
<div class="select-date">
|
|
<span class="label-date">Chọn ngày báo cáo:</span>
|
|
<Datepicker v-bind="{ record: infoReport, attr: 'date-report' }" @date="select('date-report', $event)" />
|
|
</div>
|
|
</div>
|
|
<div class="report-body">
|
|
<!-- Report Product -->
|
|
<div class="report-product">
|
|
<div class="fixed-grid has-6-cols-desktop has-3-cols-tablet has-2-cols-mobile">
|
|
<div class="grid">
|
|
<div class="cell">
|
|
<p class="cell-label">Tổng số sản phẩm</p>
|
|
<span class="cell-value">{{ totalProduct }}</span>
|
|
</div>
|
|
|
|
<div
|
|
class="cell event"
|
|
v-for="product in saleStatusProductSummary"
|
|
:style="{ color: product.color }"
|
|
@click="handlerStatusProduct($event, product)"
|
|
>
|
|
<p class="cell-label">{{ product.name }}</p>
|
|
<span class="cell-value">{{ product.count || 0 }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="chart-report-product">
|
|
<div class="fixed-grid has-1-cols has-3-cols-desktop">
|
|
<div class="grid">
|
|
<div class="cell">
|
|
<div class="chart-status-product">
|
|
<client-only>
|
|
<highcharts-vue-chart :highcharts="Highcharts" :options="chartProductStatusOptions" />
|
|
</client-only>
|
|
</div>
|
|
</div>
|
|
<div class="cell">
|
|
<div class="chart-zone-product">
|
|
<client-only>
|
|
<highcharts-vue-chart :highcharts="Highcharts" :options="chartProductZoneOptions" />
|
|
</client-only>
|
|
</div>
|
|
</div>
|
|
<div class="cell">
|
|
<div class="chart-product-dealer">
|
|
<client-only>
|
|
<highcharts-vue-chart :highcharts="Highcharts" :options="chartProductDealerOptions" />
|
|
</client-only>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="chart-cart-product">
|
|
<client-only>
|
|
<highcharts-vue-chart :highcharts="Highcharts" :options="chartStatusProductDealerOptions" />
|
|
</client-only>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- End Report Porduct -->
|
|
</div>
|
|
<Modal @close="handlerCloseModal" v-bind="showModal" v-if="showModal.lock"> </Modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from "vue";
|
|
import Highcharts from "highcharts";
|
|
import { Chart as HighchartsVueChart } from "highcharts-vue";
|
|
|
|
import Datepicker from "~/components/datepicker/Datepicker";
|
|
|
|
const { $numtoString, $findapi, $getapi, $store, $getdata, $mode } = useNuxtApp();
|
|
|
|
const dealer = $store.dealer;
|
|
const isVietnamese = computed(() => $store.lang.toLowerCase() === "vi");
|
|
const today = new Date().toISOString().split("T")[0];
|
|
|
|
let selectedChartPie = null;
|
|
|
|
const productsResponse = ref(null);
|
|
const totalProduct = ref(0);
|
|
|
|
const infoReport = ref({
|
|
"date-report": today,
|
|
});
|
|
|
|
const showModal = ref({
|
|
title: null,
|
|
lock: false,
|
|
});
|
|
|
|
const dataStatusProduct = ref({
|
|
totalProduct: 0,
|
|
totalValue: 0,
|
|
statusCode: null,
|
|
statusName: null,
|
|
totalLandArea: 0,
|
|
totalBuildingArea: 0,
|
|
totalFloorArea: 0,
|
|
listProduct: [],
|
|
});
|
|
|
|
const listSaleStatus = await $getdata("salestatus");
|
|
// const listZoneType = await $getdata("zonetype");
|
|
|
|
const select = (fieldName, value) => {
|
|
infoReport.value[fieldName] = value;
|
|
};
|
|
|
|
function buildDateRange(date) {
|
|
if (!date) return null;
|
|
const dateTemp = new Date(date).toISOString().split("T")[0];
|
|
const start = new Date(`${dateTemp}T00:00:00.000Z`);
|
|
const end = new Date(`${dateTemp}T23:59:59.999Z`);
|
|
|
|
return {
|
|
start: start.toISOString(),
|
|
end: end.toISOString(),
|
|
};
|
|
}
|
|
|
|
const groupByField = (list, { keyField, nameField, nameTransform, colorField }) => {
|
|
if (!Array.isArray(list) || list.length === 0) return [];
|
|
|
|
const map = new Map();
|
|
|
|
for (const item of list) {
|
|
const key = item[keyField];
|
|
if (!key) continue;
|
|
|
|
let group = map.get(key);
|
|
|
|
if (!group) {
|
|
group = {
|
|
key,
|
|
name: nameTransform ? nameTransform(item[nameField]) : nameField ? item[nameField] : key,
|
|
color: colorField ? item[colorField] : null,
|
|
count: 0,
|
|
children: [],
|
|
};
|
|
map.set(key, group);
|
|
}
|
|
|
|
group.count++;
|
|
group.children.push(item);
|
|
}
|
|
|
|
return [...map.values()];
|
|
};
|
|
|
|
function buildGroupMap(list = []) {
|
|
if (!Array.isArray(list)) return new Map();
|
|
|
|
return new Map(list.filter((item) => item?.key != null).map((item) => [item.key, item]));
|
|
}
|
|
|
|
function mergeMasterWithGroup(masterList = [], groupMap) {
|
|
if (!Array.isArray(masterList)) return [];
|
|
|
|
return masterList.map((item) => {
|
|
const found = groupMap.get(item.code);
|
|
|
|
return {
|
|
...item,
|
|
count: found?.count ?? 0,
|
|
children: found?.children ?? [],
|
|
};
|
|
});
|
|
}
|
|
|
|
const formatVniDate = (date) => {
|
|
const dateTemp = date ? new Date(date) : new Date();
|
|
const d = String(dateTemp.getDate()).padStart(2, "0");
|
|
const m = String(dateTemp.getMonth() + 1).padStart(2, "0");
|
|
const y = dateTemp.getFullYear();
|
|
return `${d}/${m}/${y}`;
|
|
};
|
|
|
|
function formatVND(amount, decimals = 2) {
|
|
if (!amount || isNaN(amount)) return "0 đ";
|
|
|
|
if (amount >= 1_000_000_000) return `${(amount / 1_000_000_000).toFixed(decimals)} tỷ`;
|
|
|
|
if (amount >= 1_000_000) return `${(amount / 1_000_000).toFixed(decimals)} triệu`;
|
|
|
|
return `${amount.toLocaleString("vi-VN")} đ`;
|
|
}
|
|
|
|
function colorStatus(value) {
|
|
if (value > 100) {
|
|
return "#006400";
|
|
}
|
|
|
|
if (value >= 90 && value <= 100) {
|
|
return "#2ECC71";
|
|
}
|
|
|
|
if (value >= 75 && value < 90) {
|
|
return "#F1C40F";
|
|
}
|
|
|
|
if (value >= 60 && value < 75) {
|
|
return "#E67E22";
|
|
}
|
|
|
|
if (value < 60) {
|
|
return "#E74C3C";
|
|
}
|
|
|
|
return "#BDC3C7";
|
|
}
|
|
|
|
function calcPercentage(part, whole, decimals = 1) {
|
|
const numerator = Number(part) || 0;
|
|
const denominator = Number(whole) || 0;
|
|
|
|
if (denominator === 0) return 0;
|
|
|
|
return Number(((numerator / denominator) * 100).toFixed(decimals));
|
|
}
|
|
|
|
function buildChartSeriesByStatus(statusList = [], parents = [], keyGroup = "status") {
|
|
const parentStatusMaps = parents.map(
|
|
(parent) => new Map((parent[keyGroup] ?? []).map((st) => [st.key ?? st.code, st.count ?? 0]))
|
|
);
|
|
|
|
return statusList.map((status) => ({
|
|
name: status.name?.split(". ")[1] ?? status.name,
|
|
color: status.color || undefined,
|
|
data: parentStatusMaps.map((map) => ({
|
|
y: map.get(status.code) ?? 0,
|
|
key: status.code,
|
|
})),
|
|
}));
|
|
}
|
|
|
|
let scrollPosition = 0;
|
|
|
|
const toggleBodyScroll = (isActive) => {
|
|
if (isActive) {
|
|
scrollPosition = window.scrollY || document.documentElement.scrollTop;
|
|
document.body.classList.add("popup-open");
|
|
document.body.style.top = `-${scrollPosition}px`;
|
|
} else {
|
|
document.body.classList.remove("popup-open");
|
|
document.body.style.top = "";
|
|
window.scrollTo(0, scrollPosition);
|
|
}
|
|
};
|
|
|
|
// <====== Fetch product list ======>
|
|
let foundProduct = $findapi("product");
|
|
foundProduct.params.values =
|
|
"id,code,trade_code,zone_code,type__code,type__name,zone_type__code,zone_type__code,zone_type__name,direction__code,direction__name,land_lot_size,lot_area,building_area,number_of_floors,cart__code,cart__name,cart__dealer__code,cart__dealer__name,origin_price,txnprd__sale_price,status__code,status__name,status__color,status__sale_status__code,status__sale_status__name,status__sale_status__color";
|
|
if (dealer?.code) {
|
|
foundProduct.params.filter = { cart__dealer__code: dealer?.code };
|
|
foundProduct.params.exclude = { status__sale_status__code: "not-sold" };
|
|
}
|
|
|
|
async function fetchProducts() {
|
|
try {
|
|
const [productRes] = await $getapi([foundProduct]);
|
|
productsResponse.value = productRes?.data?.rows ?? [];
|
|
totalProduct.value = productsResponse.value?.length;
|
|
} catch (error) {
|
|
if ($mode === "dev") {
|
|
console.error("Call api product error", error);
|
|
}
|
|
productsResponse.value = [];
|
|
totalProduct.value = 0;
|
|
}
|
|
}
|
|
fetchProducts();
|
|
// <====== End fetch product list ======>
|
|
|
|
// <====== Group status product ======>
|
|
const productGroupStatus = computed(() =>
|
|
groupByField(productsResponse.value, {
|
|
keyField: "status__sale_status__code",
|
|
nameField: "status__sale_status__name",
|
|
nameTransform: (name) => name?.split(". ")[1] ?? name,
|
|
colorField: "status__sale_status__color",
|
|
})
|
|
);
|
|
|
|
const productGroupStatusMap = computed(() => buildGroupMap(productGroupStatus.value));
|
|
|
|
const saleStatusProductSummary = computed(() => {
|
|
const merged = mergeMasterWithGroup(listSaleStatus || [], productGroupStatusMap.value);
|
|
|
|
return merged.map((item) => {
|
|
const name = item.name?.split(". ")[1] ?? item.name;
|
|
return {
|
|
...item,
|
|
name,
|
|
};
|
|
});
|
|
});
|
|
|
|
const handlerStatusProduct = (e, data) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
updateProductStatusSummary(data);
|
|
showModal.value = {
|
|
component: "report/TableReportProduct",
|
|
lock: true,
|
|
title: `Danh sách sản phẩm - Trạng thái: ${data.name} (${formatVniDate(infoReport["date-date-report"])})`,
|
|
width: "80%",
|
|
height: "80%",
|
|
vbind: { data: dataStatusProduct.value },
|
|
};
|
|
handlerOpenModal();
|
|
};
|
|
|
|
function updateProductStatusSummary(statusData = {}) {
|
|
const listProduct = (statusData.children || []).map((item) => {
|
|
const buildingArea = Number(item.building_area ?? 0);
|
|
const floors = Number(item.number_of_floors ?? 0);
|
|
const totalBuildingArea = buildingArea * floors;
|
|
const total_built_area = $numtoString(totalBuildingArea);
|
|
item.txnprd__sale_price = item.txnprd__sale_price ?? item.origin_price;
|
|
return {
|
|
...item,
|
|
total_built_area,
|
|
};
|
|
});
|
|
|
|
const totals = listProduct.reduce(
|
|
(acc, product) => {
|
|
const salePrice = Number(product.txnprd__sale_price ?? 0);
|
|
const landArea = Number(product.lot_area ?? 0);
|
|
const buildingArea = Number(product.building_area ?? 0);
|
|
const floors = Number(product.number_of_floors ?? 0);
|
|
|
|
acc.totalValue += salePrice;
|
|
acc.totalLandArea += landArea;
|
|
acc.totalBuildingArea += buildingArea;
|
|
acc.totalFloorArea += buildingArea * floors;
|
|
|
|
return acc;
|
|
},
|
|
{
|
|
totalValue: 0,
|
|
totalLandArea: 0,
|
|
totalBuildingArea: 0,
|
|
totalFloorArea: 0,
|
|
}
|
|
);
|
|
|
|
dataStatusProduct.value = {
|
|
statusCode: statusData.code || statusData.key,
|
|
statusName: statusData.name ?? null,
|
|
totalProduct: statusData.count ?? 0,
|
|
totalValue: totals.totalValue,
|
|
totalLandArea: $numtoString(totals.totalLandArea),
|
|
totalBuildingArea: $numtoString(totals.totalBuildingArea),
|
|
totalFloorArea: $numtoString(totals.totalFloorArea),
|
|
listProduct: listProduct,
|
|
};
|
|
}
|
|
|
|
// Options chart status product
|
|
const chartSelectedStatusProductHandler = () => ({
|
|
select() {
|
|
selectedChartPie = this;
|
|
const item = productGroupStatus.value.find((i) => i.key === this.options.key);
|
|
updateProductStatusSummary(item);
|
|
|
|
showModal.value = {
|
|
component: "report/TableReportProduct",
|
|
lock: true,
|
|
title: `Danh sách sản phẩm - Trạng thái: ${this.options.name} (${formatVniDate(infoReport["date-date-report"])})`,
|
|
width: "80%",
|
|
height: "80%",
|
|
vbind: { data: dataStatusProduct.value },
|
|
};
|
|
handlerOpenModal();
|
|
},
|
|
|
|
unselect() {
|
|
if (selectedChartPie !== this) return;
|
|
selectedChartPie = null;
|
|
updateProductStatusSummary();
|
|
},
|
|
});
|
|
|
|
const chartProductStatusOptions = computed(() => {
|
|
return {
|
|
chart: {
|
|
type: "pie",
|
|
zooming: {
|
|
type: "xy",
|
|
},
|
|
panning: {
|
|
enabled: true,
|
|
type: "xy",
|
|
},
|
|
},
|
|
title: {
|
|
text: isVietnamese ? "Trạng thái sản phẩm" : "Product status",
|
|
},
|
|
subtitle: {
|
|
text: `Dữ liệu ngày: ${formatVniDate(infoReport.value["date-report"])} - ${totalProduct.value} ${
|
|
isVietnamese ? "Sản phẩm" : "Products"
|
|
}`,
|
|
},
|
|
plotOptions: {
|
|
pie: {
|
|
allowPointSelect: true,
|
|
cursor: "pointer",
|
|
dataLabels: [
|
|
{
|
|
enabled: true,
|
|
distance: 20,
|
|
format: "{point.name}: {point.y:,.0f}", // GIÁ TRỊ
|
|
},
|
|
{
|
|
enabled: true,
|
|
distance: -40,
|
|
format: "({point.percentage:.1f}%)", // GIÁ TRỊ
|
|
style: {
|
|
fontSize: "0.75em",
|
|
textOutline: "none",
|
|
opacity: 0.7,
|
|
},
|
|
filter: {
|
|
operator: ">",
|
|
property: "percentage",
|
|
value: 10,
|
|
},
|
|
},
|
|
],
|
|
point: {
|
|
events: chartSelectedStatusProductHandler(),
|
|
},
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
name: isVietnamese ? "Số lượng" : "Quantity",
|
|
colorByPoint: true,
|
|
data: productGroupStatus.value.map((item) => {
|
|
return {
|
|
name: item.name,
|
|
y: item.count,
|
|
key: item.key,
|
|
color: item.color,
|
|
};
|
|
}),
|
|
},
|
|
],
|
|
};
|
|
});
|
|
|
|
// <====== End group status product ======>
|
|
|
|
// <====== Group zone product ======>
|
|
const productGroupZone = computed(() =>
|
|
groupByField(productsResponse.value, {
|
|
keyField: "zone_type__code",
|
|
nameField: "zone_type__name",
|
|
})
|
|
);
|
|
|
|
// Options chart zone product
|
|
const chartProductZoneOptions = computed(() => {
|
|
return {
|
|
chart: {
|
|
type: "pie",
|
|
zooming: {
|
|
type: "xy",
|
|
},
|
|
panning: {
|
|
enabled: true,
|
|
type: "xy",
|
|
},
|
|
},
|
|
title: {
|
|
text: isVietnamese ? "Sản phẩm theo phân khu" : "Products by category",
|
|
},
|
|
subtitle: {
|
|
text: `Dữ liệu ngày: ${formatVniDate(infoReport.value["date-report"])} - ${productGroupZone.value.length} ${
|
|
isVietnamese ? "Phân khu" : "Category"
|
|
}`,
|
|
},
|
|
plotOptions: {
|
|
pie: {
|
|
cursor: "default",
|
|
dataLabels: [
|
|
{
|
|
enabled: true,
|
|
distance: 20,
|
|
format: "{point.name}: {point.y:,.0f}", // GIÁ TRỊ
|
|
},
|
|
{
|
|
enabled: true,
|
|
distance: -40,
|
|
format: "({point.percentage:.1f}%)", // GIÁ TRỊ
|
|
style: {
|
|
fontSize: "0.75em",
|
|
textOutline: "none",
|
|
opacity: 0.7,
|
|
},
|
|
filter: {
|
|
operator: ">",
|
|
property: "percentage",
|
|
value: 10,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
name: isVietnamese ? "Số lượng" : "Quantity",
|
|
colorByPoint: true,
|
|
data: productGroupZone.value.map((item) => {
|
|
return {
|
|
name: item.name,
|
|
y: item.count,
|
|
key: item.key,
|
|
color: item.color,
|
|
};
|
|
}),
|
|
},
|
|
],
|
|
};
|
|
});
|
|
|
|
// <====== End group zone product ======>
|
|
|
|
// <====== Group dealer product ======>
|
|
const productGroupDealer = computed(() =>
|
|
groupByField(productsResponse.value, {
|
|
keyField: "cart__dealer__code",
|
|
nameField: "cart__dealer__name",
|
|
})
|
|
);
|
|
// Options chart product dealer
|
|
const chartProductDealerOptions = computed(() => {
|
|
return {
|
|
chart: {
|
|
type: "pie",
|
|
zooming: {
|
|
type: "xy",
|
|
},
|
|
panning: {
|
|
enabled: true,
|
|
type: "xy",
|
|
},
|
|
},
|
|
title: {
|
|
text: isVietnamese ? "Sản phẩm theo đại lý" : "Products by distributor",
|
|
},
|
|
subtitle: {
|
|
text: `Dữ liệu ngày: ${formatVniDate(infoReport.value["date-report"])} - ${productGroupDealer.value?.length} ${
|
|
isVietnamese ? "Đại lý" : "Dealer"
|
|
}`,
|
|
},
|
|
plotOptions: {
|
|
pie: {
|
|
cursor: "default",
|
|
dataLabels: [
|
|
{
|
|
enabled: true,
|
|
distance: 20,
|
|
format: "{point.name}: {point.y:,.0f}", // GIÁ TRỊ
|
|
},
|
|
{
|
|
enabled: true,
|
|
distance: -40,
|
|
format: "({point.percentage:.1f}%)", // GIÁ TRỊ
|
|
style: {
|
|
fontSize: "0.75em",
|
|
textOutline: "none",
|
|
opacity: 0.7,
|
|
},
|
|
filter: {
|
|
operator: ">",
|
|
property: "percentage",
|
|
value: 10,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
name: isVietnamese ? "Số lượng" : "Quantity",
|
|
colorByPoint: true,
|
|
data:
|
|
productGroupDealer.value?.map((item) => {
|
|
return {
|
|
name: item.key,
|
|
y: item.count,
|
|
key: item.key,
|
|
color: item.color ?? null,
|
|
};
|
|
}) || [],
|
|
},
|
|
],
|
|
};
|
|
});
|
|
|
|
//Options chart status product dealer
|
|
const dealerProductSaleSummary = computed(() =>
|
|
(productGroupDealer.value ?? []).map((dealer) => {
|
|
const productSaleByStatus = groupByField(dealer.children ?? [], {
|
|
keyField: "status__sale_status__code",
|
|
nameField: "status__sale_status__name",
|
|
nameTransform: (name) => name?.split(". ")[1] ?? name,
|
|
colorField: "status__sale_status__color",
|
|
});
|
|
|
|
return {
|
|
...dealer,
|
|
productSaleByStatus,
|
|
};
|
|
})
|
|
);
|
|
|
|
const dataChartTransactionPolicy = computed(() => {
|
|
const listSaleStatusDealer = listSaleStatus.filter((status) => status.code !== "not-sold");
|
|
return buildChartSeriesByStatus(listSaleStatusDealer, dealerProductSaleSummary.value, "productSaleByStatus");
|
|
});
|
|
|
|
const createDealerStatusClickHandler = () => ({
|
|
click() {
|
|
const dealerCode = this.category?.split(" - ")?.[0] ?? null;
|
|
const saleStatusCode = this.options?.key ?? null;
|
|
|
|
if (!dealerCode || !saleStatusCode) return;
|
|
|
|
const dealerSummary = dealerProductSaleSummary.value?.find((dealer) => dealer.key === dealerCode);
|
|
|
|
const productsByStatus = dealerSummary?.productSaleByStatus?.find((status) => status.key === saleStatusCode);
|
|
|
|
updateProductStatusSummary(productsByStatus);
|
|
showModal.value = {
|
|
component: "report/TableReportProduct",
|
|
lock: true,
|
|
title: `Danh sách sản phẩm - Đại lý: ${dealerSummary.key || dealerSummary.code} - Trạng thái: ${
|
|
productsByStatus.name
|
|
} (${formatVniDate(infoReport["date-date-report"])})`,
|
|
width: "80%",
|
|
height: "80%",
|
|
vbind: { data: dataStatusProduct.value },
|
|
};
|
|
handlerOpenModal();
|
|
},
|
|
});
|
|
|
|
const chartStatusProductDealerOptions = computed(() => {
|
|
return {
|
|
chart: {
|
|
type: "column",
|
|
},
|
|
title: {
|
|
text: isVietnamese ? "Trạng thái sản phẩm theo từng đại lý" : "Product status by dealer",
|
|
},
|
|
subtitle: {
|
|
text: ` Dữ liệu ngày: ${formatVniDate(infoReport.value["date-report"])} `,
|
|
},
|
|
xAxis: {
|
|
categories: dealerProductSaleSummary.value?.map((dealer) => `${dealer.key} - ${dealer.count} SP`),
|
|
crosshair: true,
|
|
accessibility: {
|
|
description: "Countries",
|
|
},
|
|
},
|
|
yAxis: {
|
|
min: 0,
|
|
title: {
|
|
text: "Số lượng",
|
|
},
|
|
},
|
|
tooltip: {
|
|
headerFormat: "<b>{category}</b><br/>",
|
|
pointFormat: "{series.name}: {point.y} Sản phẩm",
|
|
},
|
|
plotOptions: {
|
|
column: {
|
|
cursor: "pointer",
|
|
pointPadding: 0.2,
|
|
borderWidth: 0,
|
|
dataLabels: {
|
|
enabled: true,
|
|
format: "{point.y}",
|
|
},
|
|
point: {
|
|
events: createDealerStatusClickHandler(),
|
|
},
|
|
},
|
|
},
|
|
series: dataChartTransactionPolicy.value,
|
|
};
|
|
});
|
|
|
|
// <====== End group dealer product ======>
|
|
|
|
const handleUnSelectChart = () => {
|
|
const chart = Highcharts.charts.find((c) => c);
|
|
if (chart) {
|
|
chart.series[0]?.points.forEach((p) => {
|
|
if (p.selected) p.select(false, false);
|
|
});
|
|
}
|
|
};
|
|
|
|
const handlerOpenModal = () => {
|
|
toggleBodyScroll(true);
|
|
};
|
|
|
|
const handlerCloseModal = () => {
|
|
toggleBodyScroll(false);
|
|
showModal.value = {
|
|
lock: false,
|
|
};
|
|
handleUnSelectChart();
|
|
};
|
|
|
|
// Reload data change
|
|
watch(
|
|
infoReport,
|
|
(newVal) => {
|
|
// foundCustomer.params.filter.create_time__lte = buildDateRange(infoReport.value["date-report"])?.end;
|
|
// foundCustomerTransaction.params.filter.txncust__create_time__lte = buildDateRange(
|
|
// infoReport.value["date-report"]
|
|
// )?.end;
|
|
// fetchCustomer();
|
|
},
|
|
{ deep: true }
|
|
);
|
|
</script>
|
|
|
|
<style>
|
|
body.popup-open {
|
|
position: fixed;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.report-daily-page .report-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
.report-daily-page .report-header .header-report-title {
|
|
font-size: 1.25rem;
|
|
font-weight: bolder;
|
|
}
|
|
|
|
.report-daily-page .report-header .select-date {
|
|
background-color: #204853;
|
|
padding: 10px;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.report-daily-page .report-header .select-date .label-date {
|
|
color: #fff;
|
|
margin-bottom: 10px;
|
|
display: inline-block;
|
|
}
|
|
.report-daily-page .cell.event {
|
|
padding: 10px;
|
|
border-radius: 16px;
|
|
cursor: pointer;
|
|
transition: box-shadow 0.2s ease, transform 0.2s ease;
|
|
}
|
|
|
|
.report-daily-page .cell.event:hover {
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.report-daily-page .cell .cell-label {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
}
|
|
.report-daily-page .cell .cell-value {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.report-daily-page .modal-card {
|
|
overflow-y: hidden;
|
|
}
|
|
|
|
@media only screen and (max-width: 767px) {
|
|
.report-daily-page .report-header {
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
}
|
|
</style>
|