新增報表管理頁面 | 首頁 : 小卡icon ui 優化、 冷藏室溫度更新,優化溫度-糖度 圖表顯示邏輯
This commit is contained in:
parent
a4e56a7e2d
commit
de40e0b326
24
src/App.vue
24
src/App.vue
@ -2,7 +2,7 @@
|
|||||||
import { RouterView } from "vue-router";
|
import { RouterView } from "vue-router";
|
||||||
import Navbar from "./components/navbar/Navbar.vue";
|
import Navbar from "./components/navbar/Navbar.vue";
|
||||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||||
import { ref, provide, onUnmounted, onMounted } from "vue";
|
import { ref, provide, onUnmounted, watch } from "vue";
|
||||||
import { getAlertLog } from "@/apis/alert";
|
import { getAlertLog } from "@/apis/alert";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
@ -47,13 +47,29 @@ const openToast = (status, content, to = "body", confirm = null) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
|
// 監聽登入狀態,登入時啟動 getAlert 與定時器,登出時清除定時器
|
||||||
|
watch(
|
||||||
|
() => store.user.token,
|
||||||
|
(token) => {
|
||||||
|
if (token) {
|
||||||
getAlert();
|
getAlert();
|
||||||
alertInterval = setInterval(getAlert, 30 * 1000);
|
alertInterval = setInterval(getAlert, 30 * 1000);
|
||||||
});
|
} else {
|
||||||
|
if (alertInterval) {
|
||||||
|
clearInterval(alertInterval);
|
||||||
|
alertInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (alertInterval) clearInterval(alertInterval);
|
if (alertInterval) {
|
||||||
|
clearInterval(alertInterval);
|
||||||
|
alertInterval = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
provide("app_toast", { openToast, cancelToastOpen });
|
provide("app_toast", { openToast, cancelToastOpen });
|
||||||
|
11
src/apis/report/api.js
Normal file
11
src/apis/report/api.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// 查詢
|
||||||
|
export const GET_ELECTRIC_METER_API = `/api/query/electric_meter`;
|
||||||
|
export const GET_REFRIGERATION_ROOM_TEMPERATURE_API = `/api/query/refrigeration_room_temperature`;
|
||||||
|
export const GET_SIP_API = `/api/query/sip`;
|
||||||
|
export const GET_CIP_API = `/api/query/cip`;
|
||||||
|
|
||||||
|
// 匯出
|
||||||
|
export const EXPORT_ELECTRIC_METER_API = `/api/report/electric_meter`;
|
||||||
|
export const EXPORT_REFRIGERATION_ROOM_TEMPERATURE_API = `/api/report/refrigeration_room_temperature`;
|
||||||
|
export const EXPORT_SIP_API = `/api/report/sip`;
|
||||||
|
export const EXPORT_CIP_API = `/api/report/cip`;
|
143
src/apis/report/index.js
Normal file
143
src/apis/report/index.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import {
|
||||||
|
GET_ELECTRIC_METER_API,
|
||||||
|
GET_REFRIGERATION_ROOM_TEMPERATURE_API,
|
||||||
|
GET_SIP_API,
|
||||||
|
GET_CIP_API,
|
||||||
|
EXPORT_ELECTRIC_METER_API,
|
||||||
|
EXPORT_REFRIGERATION_ROOM_TEMPERATURE_API,
|
||||||
|
EXPORT_SIP_API,
|
||||||
|
EXPORT_CIP_API,
|
||||||
|
} from "./api";
|
||||||
|
import instance, { fileInstance } from "@/util/request";
|
||||||
|
import apihandler from "@/util/apihandler";
|
||||||
|
import downloadExcel from "@/util/downloadExcel";
|
||||||
|
|
||||||
|
export const getElectricMeterLog = async ({ start_date }) => {
|
||||||
|
const res = await instance.get(GET_ELECTRIC_METER_API, {
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return apihandler(res.code, res.data, {
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
isSuccess: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRefrigerationRoomTemperatureLog = async ({ start_date }) => {
|
||||||
|
const res = await instance.get(GET_REFRIGERATION_ROOM_TEMPERATURE_API, {
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return apihandler(res.code, res.data, {
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
isSuccess: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSipLog = async ({ start_date }) => {
|
||||||
|
const res = await instance.get(GET_SIP_API, {
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return apihandler(res.code, res.data, {
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
isSuccess: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCipLog = async ({ start_date }) => {
|
||||||
|
const res = await instance.get(GET_CIP_API, {
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return apihandler(res.code, res.data, {
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
isSuccess: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const exportElectricMeterLog = async ({ start_date }) => {
|
||||||
|
const res = await fileInstance.get(EXPORT_ELECTRIC_METER_API, {
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
responseType: "blob",
|
||||||
|
});
|
||||||
|
|
||||||
|
return apihandler(
|
||||||
|
res.code,
|
||||||
|
res,
|
||||||
|
{
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
},
|
||||||
|
downloadExcel
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const exportRefrigerationRoomTemperatureLog = async ({ start_date }) => {
|
||||||
|
const res = await fileInstance.get(
|
||||||
|
EXPORT_REFRIGERATION_ROOM_TEMPERATURE_API,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
responseType: "blob",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return apihandler(
|
||||||
|
res.code,
|
||||||
|
res,
|
||||||
|
{
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
},
|
||||||
|
downloadExcel
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const exportSipLog = async ({ start_date }) => {
|
||||||
|
const res = await fileInstance.get(EXPORT_SIP_API, {
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
responseType: "blob",
|
||||||
|
});
|
||||||
|
return apihandler(
|
||||||
|
res.code,
|
||||||
|
res,
|
||||||
|
{
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
},
|
||||||
|
downloadExcel
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const exportCipLog = async ({ start_date }) => {
|
||||||
|
const res = await fileInstance.get(EXPORT_CIP_API, {
|
||||||
|
params: {
|
||||||
|
start_date,
|
||||||
|
},
|
||||||
|
responseType: "blob",
|
||||||
|
});
|
||||||
|
return apihandler(
|
||||||
|
res.code,
|
||||||
|
res,
|
||||||
|
{
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
},
|
||||||
|
downloadExcel
|
||||||
|
);
|
||||||
|
};
|
@ -12,11 +12,9 @@ import {
|
|||||||
} from "vue";
|
} from "vue";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import useSystemStatusByBaja from "@/hooks/baja/useSystemStatusByBaja";
|
import useSystemStatusByBaja from "@/hooks/baja/useSystemStatusByBaja";
|
||||||
import useSystemHeatmap from "@/hooks/baja/useSystemHeatmap";
|
|
||||||
import ForgeInfoModal from "./ForgeInfoModal.vue";
|
import ForgeInfoModal from "./ForgeInfoModal.vue";
|
||||||
import useSearchParams from "@/hooks/useSearchParam";
|
import useSearchParams from "@/hooks/useSearchParam";
|
||||||
const { searchParams } = useSearchParams();
|
const { searchParams } = useSearchParams();
|
||||||
const { updateTemp } = useSystemHeatmap();
|
|
||||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
const { forgeLock } = inject("app_toggle");
|
const { forgeLock } = inject("app_toggle");
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -40,6 +38,7 @@ const {
|
|||||||
updatePipeData,
|
updatePipeData,
|
||||||
subComponents,
|
subComponents,
|
||||||
clearSprites,
|
clearSprites,
|
||||||
|
updateTemp,
|
||||||
} = useSystemStatusByBaja();
|
} = useSystemStatusByBaja();
|
||||||
|
|
||||||
// 用於存儲 meter label 的 2D 位置
|
// 用於存儲 meter label 的 2D 位置
|
||||||
@ -130,13 +129,10 @@ const initForge = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 添加 camera 變化事件監聽,更新 meter 位置
|
// 添加 camera 變化事件監聽,更新 meter 位置
|
||||||
viewer.addEventListener(
|
viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, () => {
|
||||||
Autodesk.Viewing.CAMERA_CHANGE_EVENT,
|
|
||||||
() => {
|
|
||||||
console.log("CAMERA_CHANGE_EVENT");
|
console.log("CAMERA_CHANGE_EVENT");
|
||||||
updateMeterPositionsLocal();
|
updateMeterPositionsLocal();
|
||||||
}
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ export const AUTHPAGES = [
|
|||||||
{
|
{
|
||||||
authCode: "PF4",
|
authCode: "PF4",
|
||||||
icon: "chart-line",
|
icon: "chart-line",
|
||||||
navigate: "/historyData",
|
navigate: "/reportManagement",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
authCode: "PF5",
|
authCode: "PF5",
|
||||||
|
@ -29,7 +29,7 @@ export default function useSystemHeatmap() {
|
|||||||
roomDbId: d.room_dbid,
|
roomDbId: d.room_dbid,
|
||||||
position: JSON.parse(d.device_coordinate_3d),
|
position: JSON.parse(d.device_coordinate_3d),
|
||||||
sensorTypes: ["temperature"], // The types/properties this device exposes
|
sensorTypes: ["temperature"], // The types/properties this device exposes
|
||||||
temp: 25,
|
temp: 0,
|
||||||
dbId: d.forge_dbid,
|
dbId: d.forge_dbid,
|
||||||
subSys: "freezer",
|
subSys: "freezer",
|
||||||
}));
|
}));
|
||||||
@ -38,7 +38,7 @@ export default function useSystemHeatmap() {
|
|||||||
//create the heatmap
|
//create the heatmap
|
||||||
function getSensorValue(device, sensorType, pointData) {
|
function getSensorValue(device, sensorType, pointData) {
|
||||||
const dev = deviceList.value.find(
|
const dev = deviceList.value.find(
|
||||||
({ device_number }) => device_number === device.id
|
({ forge_dbid }) =>forge_dbid === device.id
|
||||||
);
|
);
|
||||||
console.log(9, device, dev);
|
console.log(9, device, dev);
|
||||||
// 假設溫度範圍 -20 ~ 40
|
// 假設溫度範圍 -20 ~ 40
|
||||||
@ -50,9 +50,9 @@ export default function useSystemHeatmap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update Specific Device Temperature
|
// Update Specific Device Temperature
|
||||||
const updateTemp = (device_number, temp) => {
|
const updateTemp = (forge_dbid, temp) => {
|
||||||
const subDevIndex = deviceList.value.findIndex(
|
const subDevIndex = deviceList.value.findIndex(
|
||||||
(d) => d.device_number === device_number
|
(d) => d.forge_dbid === forge_dbid
|
||||||
);
|
);
|
||||||
if (subDevIndex !== -1) {
|
if (subDevIndex !== -1) {
|
||||||
deviceList.value[subDevIndex] = {
|
deviceList.value[subDevIndex] = {
|
||||||
@ -131,11 +131,12 @@ export default function useSystemHeatmap() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Watcher for Device List Changes
|
watch(
|
||||||
watch(deviceList, (newValue) => {
|
() => searchParams.value.option,
|
||||||
console.log("熱圖更新", newValue, searchParams.value.option);
|
(newOption, oldOption) => {
|
||||||
switch (parseInt(searchParams.value.option)) {
|
console.log("熱圖更新 (option changed)", { newOption, oldOption });
|
||||||
case 1:
|
switch (parseInt(newOption)) {
|
||||||
|
case 4:
|
||||||
createHeatMap("frozen");
|
createHeatMap("frozen");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -150,7 +151,9 @@ export default function useSystemHeatmap() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
// Cleanup on Component Unmount
|
// Cleanup on Component Unmount
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -62,7 +62,7 @@ export default function useSystemStatusByBaja() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { updateHeatMapData, initHeatMap } = useSystemHeatmap();
|
const { updateHeatMapData, initHeatMap, updateTemp } = useSystemHeatmap();
|
||||||
|
|
||||||
const updateForgeViewer = async (viewer) => {
|
const updateForgeViewer = async (viewer) => {
|
||||||
if (!viewer) {
|
if (!viewer) {
|
||||||
@ -119,7 +119,7 @@ export default function useSystemStatusByBaja() {
|
|||||||
newPositions.set(meter.main_id, {
|
newPositions.set(meter.main_id, {
|
||||||
x: pos2d.x,
|
x: pos2d.x,
|
||||||
y: pos2d.y - 100, // 往上偏移 100px
|
y: pos2d.y - 100, // 往上偏移 100px
|
||||||
visible: viewer.isNodeVisible(meter.forge_dbid)
|
visible: viewer.isNodeVisible(meter.forge_dbid),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -496,5 +496,6 @@ export default function useSystemStatusByBaja() {
|
|||||||
createSprites,
|
createSprites,
|
||||||
clearSprites,
|
clearSprites,
|
||||||
clearSingleSprite,
|
clearSingleSprite,
|
||||||
|
updateTemp,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import Dashboard from "@/views/dashboard/Dashboard.vue";
|
|||||||
import History from "@/views/history/History.vue";
|
import History from "@/views/history/History.vue";
|
||||||
import Operation from "@/views/operation/Operation.vue";
|
import Operation from "@/views/operation/Operation.vue";
|
||||||
import GraphManagement from "@/views/graphManagement/GraphManagement.vue";
|
import GraphManagement from "@/views/graphManagement/GraphManagement.vue";
|
||||||
|
import ReportManagement from "@/views/reportManagement/ReportManagement.vue";
|
||||||
import AccountManagement from "@/views/accountManagement/AccountManagement.vue";
|
import AccountManagement from "@/views/accountManagement/AccountManagement.vue";
|
||||||
import AssetManagement from "@/views/AssetManagement/AssetManagement.vue";
|
import AssetManagement from "@/views/AssetManagement/AssetManagement.vue";
|
||||||
import AlertManagement from "@/views/alert/AlertManagement.vue";
|
import AlertManagement from "@/views/alert/AlertManagement.vue";
|
||||||
@ -33,6 +34,11 @@ const router = createRouter({
|
|||||||
name: "history",
|
name: "history",
|
||||||
component: History,
|
component: History,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/reportManagement",
|
||||||
|
name: "reportManagement",
|
||||||
|
component: ReportManagement,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/operation",
|
path: "/operation",
|
||||||
name: "operation",
|
name: "operation",
|
||||||
|
@ -63,12 +63,12 @@ watch(SaveCheckAuth, (newValue, oldValue) => {
|
|||||||
active: selectedBtn.value.authCode === "PF",
|
active: selectedBtn.value.authCode === "PF",
|
||||||
authCode: "PF",
|
authCode: "PF",
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: "生產設定權限",
|
// title: "生產設定權限",
|
||||||
key: "PS",
|
// key: "PS",
|
||||||
active: selectedBtn.value.authCode === "PS",
|
// active: selectedBtn.value.authCode === "PS",
|
||||||
authCode: "PS",
|
// authCode: "PS",
|
||||||
},
|
// },
|
||||||
]);
|
]);
|
||||||
} else if (!newValue.includes("PF10") && oldValue.includes("PF10")) {
|
} else if (!newValue.includes("PF10") && oldValue.includes("PF10")) {
|
||||||
console.log(newValue.filter((authCode) => authCode.startsWith("PS")));
|
console.log(newValue.filter((authCode) => authCode.startsWith("PS")));
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card rounded-md bg-neutral text-neutral-content shadow-sm shadow-gray-400">
|
<div class="card rounded-md bg-neutral text-neutral-content shadow-sm shadow-gray-400">
|
||||||
<div class="card-body text-xs px-1 py-2">
|
<div class="card-body text-xs px-1 py-2">
|
||||||
<p>安全存量: {{ safety_stock }} 頓</p>
|
<p>安全存量: {{ safety_stock }} 噸</p>
|
||||||
<p>目前存量: {{ current_stock }} 頓</p>
|
<p>目前存量: {{ current_stock }} 噸</p>
|
||||||
<p>{{ updateTime }}</p>
|
<p>{{ updateTime }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -111,10 +111,13 @@ watch(data, (newValue) => {
|
|||||||
// clearChart(other_real_temp_chart.value.chart);
|
// clearChart(other_real_temp_chart.value.chart);
|
||||||
if (newValue?.length > 0) {
|
if (newValue?.length > 0) {
|
||||||
other_real_temp_chart.value.chart.setOption({
|
other_real_temp_chart.value.chart.setOption({
|
||||||
|
...defaultChartOption.value,
|
||||||
legend: {
|
legend: {
|
||||||
data: newValue.map(({ full_name }) => full_name),
|
data: newValue.map(({ full_name }) => full_name),
|
||||||
|
...defaultChartOption.value.legend,
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
|
...defaultChartOption.value.xAxis,
|
||||||
data: newValue[0]?.data.map(({ time }) => dayjs(time).format("HH:mm")),
|
data: newValue[0]?.data.map(({ time }) => dayjs(time).format("HH:mm")),
|
||||||
},
|
},
|
||||||
series: newValue.map(({ full_name, data: seriesData }, index) => ({
|
series: newValue.map(({ full_name, data: seriesData }, index) => ({
|
||||||
@ -126,7 +129,7 @@ watch(data, (newValue) => {
|
|||||||
color: SECOND_CHART_COLOR[index],
|
color: SECOND_CHART_COLOR[index],
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
});
|
}, { notMerge: true });
|
||||||
}
|
}
|
||||||
// other_real_temp_chart.value.chart.hideLoading();
|
// other_real_temp_chart.value.chart.hideLoading();
|
||||||
});
|
});
|
||||||
|
@ -17,11 +17,11 @@ import { twMerge } from "tailwind-merge";
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-26 stat items-center p-2">
|
<div class="w-26 stat content-center p-2">
|
||||||
<div class="text-base font-bold text-slate-800">{{ item.name }}</div>
|
<div class="text-base font-bold text-slate-800">{{ item.name }}</div>
|
||||||
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-15 stat items-center text-center p-2">
|
<div class="w-15 stat content-center text-center gap-1 p-2">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
class="text-2xl text-slate-800 mx-auto"
|
class="text-2xl text-slate-800 mx-auto"
|
||||||
:icon="['fas', 'temperature-high']"
|
:icon="['fas', 'temperature-high']"
|
||||||
@ -30,7 +30,7 @@ import { twMerge } from "tailwind-merge";
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'w-15 stat items-center text-center p-2',
|
'w-15 stat content-center text-center gap-1 p-2',
|
||||||
item.sterilization == '殺菌完成'
|
item.sterilization == '殺菌完成'
|
||||||
? 'text-green-700'
|
? 'text-green-700'
|
||||||
: 'text-slate-800',
|
: 'text-slate-800',
|
||||||
@ -43,7 +43,7 @@ import { twMerge } from "tailwind-merge";
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'w-15 stat items-center text-center p-2',
|
'w-15 stat content-center text-center gap-1 p-2',
|
||||||
typeof item.sugar !== 'number' ? 'text-red-700' : 'text-slate-800',
|
typeof item.sugar !== 'number' ? 'text-red-700' : 'text-slate-800',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
@ -52,7 +52,7 @@ import { twMerge } from "tailwind-merge";
|
|||||||
{{ item.sugar }}
|
{{ item.sugar }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-15 stat items-center text-center p-2">
|
<div class="w-15 stat content-center text-center gap-1 p-2">
|
||||||
<div>
|
<div>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
:icon="['fas', 'circle']"
|
:icon="['fas', 'circle']"
|
||||||
|
@ -12,7 +12,7 @@ defineProps({ data: Array });
|
|||||||
<div class="text-sm font-bold text-slate-800">{{ item.name }}</div>
|
<div class="text-sm font-bold text-slate-800">{{ item.name }}</div>
|
||||||
<div class="text-sm text-slate-800"> </div>
|
<div class="text-sm text-slate-800"> </div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-15 stat items-center text-center p-2">
|
<div class="w-15 stat content-center text-center gap-1 p-2">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
:icon="['fas', 'temperature-high']"
|
:icon="['fas', 'temperature-high']"
|
||||||
class="text-2xl text-slate-800 mx-auto"
|
class="text-2xl text-slate-800 mx-auto"
|
||||||
|
@ -18,7 +18,7 @@ defineProps({ data: Array });
|
|||||||
<div class="text-base font-bold text-slate-800">{{ item.name }}</div>
|
<div class="text-base font-bold text-slate-800">{{ item.name }}</div>
|
||||||
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-15 stat items-center p-2">
|
<div class="w-15 stat content-center gap-1 p-2">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
:icon="['fas', 'temperature-high']"
|
:icon="['fas', 'temperature-high']"
|
||||||
class="text-2xl text-slate-800 mx-auto"
|
class="text-2xl text-slate-800 mx-auto"
|
||||||
|
@ -15,18 +15,18 @@ const { openToast } = inject("app_toast");
|
|||||||
|
|
||||||
const settingColumns = [
|
const settingColumns = [
|
||||||
{ title: "項目", key: "name" },
|
{ title: "項目", key: "name" },
|
||||||
{ title: "總庫存量", key: "total_capacity" },
|
{ title: "總庫存量(噸)", key: "total_capacity" },
|
||||||
{ title: "安全存量", key: "safety_stock" },
|
{ title: "安全存量(噸)", key: "safety_stock" },
|
||||||
{ title: "目前存量", key: "current_stock" },
|
{ title: "目前存量(噸)", key: "current_stock" },
|
||||||
{ title: "更新時間", key: "updated_at" },
|
{ title: "更新時間", key: "updated_at" },
|
||||||
{ title: "功能", key: "operation" },
|
{ title: "功能", key: "operation" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const recordColumns = [
|
const recordColumns = [
|
||||||
{ title: "項目", key: "item_name", filter: true },
|
{ title: "項目", key: "item_name", filter: true },
|
||||||
{ title: "總庫存量", key: "total_capacity" },
|
{ title: "總庫存量(噸)", key: "total_capacity" },
|
||||||
{ title: "安全存量", key: "safety_stock" },
|
{ title: "安全存量(噸)", key: "safety_stock" },
|
||||||
{ title: "目前存量", key: "current_stock" },
|
{ title: "目前存量(噸)", key: "current_stock" },
|
||||||
{ title: "操作類型", key: "type" },
|
{ title: "操作類型", key: "type" },
|
||||||
{ title: "最後修改者", key: "user_name", filter: true },
|
{ title: "最後修改者", key: "user_name", filter: true },
|
||||||
{ title: "日期", key: "time", sort: true },
|
{ title: "日期", key: "time", sort: true },
|
||||||
|
193
src/views/reportManagement/ReportManagement.vue
Normal file
193
src/views/reportManagement/ReportManagement.vue
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
<script setup>
|
||||||
|
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
||||||
|
import DateGroup from "@/components/customUI/DateGroup.vue";
|
||||||
|
import { computed, ref, onBeforeMount, watch } from "vue";
|
||||||
|
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||||
|
import {
|
||||||
|
getElectricMeterLog,
|
||||||
|
getRefrigerationRoomTemperatureLog,
|
||||||
|
getSipLog,
|
||||||
|
getCipLog,
|
||||||
|
exportElectricMeterLog,
|
||||||
|
exportRefrigerationRoomTemperatureLog,
|
||||||
|
exportSipLog,
|
||||||
|
exportCipLog,
|
||||||
|
} from "@/apis/report";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||||
|
|
||||||
|
const dateRange = ref([
|
||||||
|
{
|
||||||
|
key: "start_at",
|
||||||
|
value: dayjs(),
|
||||||
|
dateFormat: "yyyy-MM",
|
||||||
|
placeholder: "起始日期",
|
||||||
|
monthPicker: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const searchState = ref({
|
||||||
|
start_date: dayjs().format("YYYY-MM"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Table 相關變數
|
||||||
|
const tableLoading = ref(false);
|
||||||
|
|
||||||
|
const columns = ref([]);
|
||||||
|
const dataSource = ref([]);
|
||||||
|
|
||||||
|
const tableColumnsMap = {
|
||||||
|
elec: [
|
||||||
|
{ title: "日期 / 時間", dataIndex: "time", key: "time" },
|
||||||
|
{ title: "耗電度數", dataIndex: "cumulant", key: "cumulant" },
|
||||||
|
{ title: "電錶顯示值", dataIndex: "max_value", key: "max_value" },
|
||||||
|
],
|
||||||
|
fridge: [
|
||||||
|
{ title: "年", dataIndex: "year", key: "year" },
|
||||||
|
{ title: "月", dataIndex: "month", key: "month" },
|
||||||
|
{ title: "日", dataIndex: "day", key: "day" },
|
||||||
|
{ title: "星期", dataIndex: "weekday", key: "weekday" },
|
||||||
|
{ title: "上午溫度℃", dataIndex: "morning_temp", key: "morning_temp" },
|
||||||
|
{ title: "下午溫度℃", dataIndex: "afternoon_temp", key: "afternoon_temp" },
|
||||||
|
],
|
||||||
|
sip: [
|
||||||
|
{ title: "設備", dataIndex: "full_name", key: "full_name" },
|
||||||
|
{ title: "日期", dataIndex: "date", key: "date" },
|
||||||
|
{ title: "開始時間", dataIndex: "start_time", key: "start_time" },
|
||||||
|
{ title: "結束時間", dataIndex: "end_time", key: "end_time" },
|
||||||
|
],
|
||||||
|
cip: [
|
||||||
|
{ title: "設備", dataIndex: "full_name", key: "full_name" },
|
||||||
|
{ title: "日期", dataIndex: "date", key: "date" },
|
||||||
|
{ title: "開始時間", dataIndex: "start_time", key: "start_time" },
|
||||||
|
{ title: "結束時間", dataIndex: "end_time", key: "end_time" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiFetchMap = {
|
||||||
|
elec: getElectricMeterLog,
|
||||||
|
fridge: getRefrigerationRoomTemperatureLog,
|
||||||
|
sip: getSipLog,
|
||||||
|
cip: getCipLog,
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiExportMap = {
|
||||||
|
elec: exportElectricMeterLog,
|
||||||
|
fridge: exportRefrigerationRoomTemperatureLog,
|
||||||
|
sip: exportSipLog,
|
||||||
|
cip: exportCipLog,
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
selectedBtn,
|
||||||
|
(newItems) => {
|
||||||
|
columns.value = (newItems && tableColumnsMap[newItems.key]) || [];
|
||||||
|
dataSource.value = [];
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
dateRange,
|
||||||
|
(newRange) => {
|
||||||
|
let raw = newRange && newRange[0] && newRange[0].value;
|
||||||
|
let baseDayjs = null;
|
||||||
|
if (raw && typeof raw === "object" && "year" in raw && "month" in raw) {
|
||||||
|
baseDayjs = dayjs(new Date(raw.year, raw.month, 1));
|
||||||
|
} else {
|
||||||
|
baseDayjs = dayjs(raw);
|
||||||
|
}
|
||||||
|
if (!baseDayjs.isValid()) {
|
||||||
|
baseDayjs = dayjs();
|
||||||
|
}
|
||||||
|
searchState.value = {
|
||||||
|
start_date: baseDayjs.format("YYYY-MM"),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const submit = async (e, type = "") => {
|
||||||
|
e?.preventDefault?.();
|
||||||
|
e?.stopPropagation?.();
|
||||||
|
|
||||||
|
const key = selectedBtn.value?.key;
|
||||||
|
if (!key) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (type === "export") {
|
||||||
|
const exporter = apiExportMap[key];
|
||||||
|
if (exporter) {
|
||||||
|
await exporter(searchState.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tableLoading.value = true;
|
||||||
|
const fetcher = apiFetchMap[key];
|
||||||
|
if (fetcher) {
|
||||||
|
const res = await fetcher(searchState.value);
|
||||||
|
dataSource.value =
|
||||||
|
res?.data.map((d) => ({
|
||||||
|
...d,
|
||||||
|
time: d?.time ? dayjs(d?.time).format("YYYY-MM-DD HH:mm:ss") : "",
|
||||||
|
})) || [];
|
||||||
|
} else {
|
||||||
|
dataSource.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
dataSource.value = [];
|
||||||
|
} finally {
|
||||||
|
tableLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitBtns = computed(() => [
|
||||||
|
{
|
||||||
|
title: "查詢",
|
||||||
|
key: "submit",
|
||||||
|
active: false,
|
||||||
|
onClick: submit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "匯出",
|
||||||
|
key: "export",
|
||||||
|
active: false,
|
||||||
|
onClick: (e) => submit(e, "export"),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
setItems([
|
||||||
|
{ title: "電表", key: "elec", active: true },
|
||||||
|
{ title: "冷藏室", key: "fridge", active: false },
|
||||||
|
{ title: "SIP", key: "sip", active: false },
|
||||||
|
{ title: "CIP", key: "cip", active: false },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1 class="text-2xl font-extrabold mb-2">報表管理</h1>
|
||||||
|
<div class="w-full flex flex-wrap flex-col custom-border p-4 mb-4">
|
||||||
|
<ButtonGroup
|
||||||
|
:items="items"
|
||||||
|
:withLine="true"
|
||||||
|
class=""
|
||||||
|
:onclick="
|
||||||
|
(e, item) => {
|
||||||
|
changeActiveBtn(item);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<div class="flex flex-wrap items-center">
|
||||||
|
<DateGroup :items="dateRange" />
|
||||||
|
<ButtonGroup class="ml-5" :items="submitBtns" :withLine="false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table :loading="tableLoading" :columns="columns" :dataSource="dataSource">
|
||||||
|
</Table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
Loading…
Reference in New Issue
Block a user