首頁、登入頁介面修改 | 新增檢重機設備頁面
This commit is contained in:
parent
3a98726d1b
commit
33dab54820
Binary file not shown.
Before Width: | Height: | Size: 337 KiB |
BIN
public/background.png
Normal file
BIN
public/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 MiB |
@ -97,7 +97,7 @@ const pageInput = computed(() => {
|
||||
:key="`page${page}`"
|
||||
:class="
|
||||
twMerge(
|
||||
'w-10 h-10 mx-1 border-2 border-sub-success rounded-full flex items-center justify-center',
|
||||
'w-10 h-10 mx-1 border-2 border-sub-success rounded-full flex items-center justify-center cursor-pointer',
|
||||
currentPage === page ? 'bg-sub-success' : 'bg-transparent'
|
||||
)
|
||||
"
|
||||
|
@ -231,6 +231,7 @@ watch(
|
||||
v-if="filterColumn[column.key]"
|
||||
>
|
||||
<div class="card min-w-max bg-body shadow-xl px-10 py-5">
|
||||
<div class="max-h-80 overflow-auto">
|
||||
<Checkbox
|
||||
v-for="item in filterItems[column.key]"
|
||||
:title="item.name"
|
||||
@ -242,6 +243,7 @@ watch(
|
||||
"
|
||||
className="justify-start"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-actions mt-4 justify-end">
|
||||
<input
|
||||
type="reset"
|
||||
|
@ -114,7 +114,11 @@ const initForge = () => {
|
||||
|
||||
const tree = viewer.model.getData().instanceTree;
|
||||
hideAllObjects(tree, visibleDbid.value);
|
||||
|
||||
visibleDbid.value.forEach((dbid) => {
|
||||
if (dbid === 58) {
|
||||
viewer.setThemingColor(dbid, new THREE.Vector4(1, 0, 0, 1));
|
||||
}
|
||||
});
|
||||
// 印出被點選物件的 dbid
|
||||
// viewer.addEventListener(
|
||||
// Autodesk.Viewing.SELECTION_CHANGED_EVENT,
|
||||
|
@ -18,7 +18,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="navbar bg-dark text-success py-2 mb-3 w-full relative z-50">
|
||||
<header class="navbar bg-dark text-success py-2 w-full relative z-50">
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
||||
|
@ -46,7 +46,8 @@ import {
|
||||
faEyeSlash,
|
||||
faTemperatureHigh,
|
||||
faTint,
|
||||
faCircle
|
||||
faCircle,
|
||||
faSyncAlt
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import { faClock } from "@fortawesome/free-regular-svg-icons";
|
||||
@ -97,7 +98,8 @@ library.add(
|
||||
faTemperatureHigh,
|
||||
faClock,
|
||||
faTint,
|
||||
faCircle
|
||||
faCircle,
|
||||
faSyncAlt
|
||||
);
|
||||
|
||||
export default library;
|
||||
|
@ -75,7 +75,7 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) {
|
||||
d.device_error_point_name,
|
||||
d.points || []
|
||||
),
|
||||
subSys:d.subSys,
|
||||
subSys: d.subSys,
|
||||
is_show: true,
|
||||
currentColor: d.device_normal_color,
|
||||
},
|
||||
@ -148,10 +148,12 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) {
|
||||
// 當點位是 "light",且目前顏色不是錯誤顏色時,從 lightColorMap 取顏色
|
||||
if (
|
||||
point.toLowerCase() === "light" &&
|
||||
subscribeData.value[device_number].currentColor !== subscribeData.value[device_number].device_error_color
|
||||
subscribeData.value[device_number].currentColor !==
|
||||
subscribeData.value[device_number].device_error_color
|
||||
) {
|
||||
subscribeData.value[device_number].currentColor =
|
||||
lightColorMap[Number(value)] || subscribeData.value[device_number].device_normal_color;
|
||||
lightColorMap[Number(value)] ||
|
||||
subscribeData.value[device_number].device_normal_color;
|
||||
}
|
||||
|
||||
// 當點位是錯誤點時,判斷值是否等於錯誤值,決定是否使用錯誤顏色
|
||||
@ -372,10 +374,24 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) {
|
||||
|
||||
const reloadModal = () => {};
|
||||
|
||||
watch(visibleDbid, (newValue) => {
|
||||
forgeViewer.value &&
|
||||
watch(
|
||||
visibleDbid,
|
||||
(newValue) => {
|
||||
if (!forgeViewer.value) return;
|
||||
hideAllObjects(forgeViewer.value.model.getData().instanceTree, newValue);
|
||||
|
||||
newValue.forEach((dbid) => {
|
||||
// 根據 dbid 設置主題色
|
||||
if (dbid === 58) {
|
||||
// 紅色 (RGB: 1,0,0)
|
||||
forgeViewer.value.setThemingColor(
|
||||
dbid,
|
||||
new THREE.Vector4(1, 0, 0, 1)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
watch(initialData, (newValue) => {
|
||||
if (newValue) {
|
||||
|
@ -5,7 +5,7 @@ import AssetTable from "./components/AssetTable.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">資產管理</h1>
|
||||
<h1 class="text-2xl font-extrabold mb-2">設備管理</h1>
|
||||
<!-- <AssetMainList /> -->
|
||||
<AssetSubList />
|
||||
<AssetTable />
|
||||
|
@ -25,12 +25,6 @@ onBeforeMount(() => {
|
||||
active: false,
|
||||
component: Role,
|
||||
},
|
||||
// {
|
||||
// title: "角色權限",
|
||||
// key: "auth",
|
||||
// active: false,
|
||||
// component: Auth,
|
||||
// },
|
||||
]);
|
||||
});
|
||||
|
||||
@ -44,7 +38,7 @@ const activeTab = computed(() => {
|
||||
<ButtonGroup
|
||||
:items="items"
|
||||
:withLine="true"
|
||||
class="mt-8 mb-6"
|
||||
class="mb-6"
|
||||
:onclick="changeComponent"
|
||||
/>
|
||||
<component :is="activeTab.component"></component>
|
||||
|
@ -34,10 +34,11 @@ const activeTab = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">即時告警</h1>
|
||||
<ButtonGroup
|
||||
:items="items"
|
||||
:withLine="true"
|
||||
class="mt-8 mb-6"
|
||||
class="mb-6"
|
||||
:onclick="changeComponent"
|
||||
/>
|
||||
<component :is="activeTab.component"></component>
|
||||
|
@ -162,7 +162,6 @@ provide("alert_table", { openModal, updateEditRecord, dataSource, search, tableL
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">告警紀錄查詢</h1>
|
||||
<AlertTableModal :editRecord="editRecord" />
|
||||
<div>
|
||||
<AlertSearch />
|
||||
|
@ -12,7 +12,7 @@ const timesListData = async () => {
|
||||
const res = await getAlarmScheduleList();
|
||||
timesList.value = res.data.map((items) => ({
|
||||
...items,
|
||||
key:items.id,
|
||||
key: items.id,
|
||||
schedule_array: JSON.parse(items.schedule_json).map((time, index) => ({
|
||||
day: index + 1,
|
||||
time,
|
||||
@ -34,13 +34,10 @@ provide("notify_table", { timesList, noticeList, timesListData });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">告警設定</h1>
|
||||
<div>
|
||||
<AlertSubList />
|
||||
<AlertOutliersTable />
|
||||
<AlertTimeTable />
|
||||
<AlertNotifyTable />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -6,7 +6,6 @@ import DashboardImmediateTemp from "./components/DashboardImmediateTemp.vue";
|
||||
import DashboardFrozenTemp from "./components/DashboardFrozenTemp.vue";
|
||||
import DashboardElectricity from "./components/DashboardElectricity.vue";
|
||||
import DashboardAlert from "./components/DashboardAlert.vue";
|
||||
import DashboardMoreModal from "./components/DashboardMoreModal.vue";
|
||||
import DashboardForgeOptionButton from "./components/DashboardForgeOptionButton.vue";
|
||||
import DashboardForgeOptionCard from "./components/DashboardForgeOptionCard.vue";
|
||||
import { getDashboardInit } from "@/apis/dashboard";
|
||||
@ -46,8 +45,6 @@ provide("dashboard_items", {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <Forge /> -->
|
||||
<!-- <DashboardMoreModal /> -->
|
||||
<div class="flex justify-between">
|
||||
<div class="w-1/4 h-full flex flex-col justify-start border-dashboard z-20">
|
||||
<div class=""><DashboardProduct /></div>
|
||||
|
@ -1,16 +1,47 @@
|
||||
<script setup>
|
||||
import useAlarmStore from "@/stores/useAlarmStore";
|
||||
import dayjs from "dayjs";
|
||||
import { computed } from "vue";
|
||||
import { ref,computed } from "vue";
|
||||
|
||||
const store = useAlarmStore();
|
||||
const alarms = computed(() =>
|
||||
store.alarmData.slice(0, 5).map((d) => ({
|
||||
...d,
|
||||
timestamp_date: dayjs(d.timestamp_date).format("YYYY.MM.DD"),
|
||||
timestamp_time: d.timestamp_time.split(":").slice(0, 2).join(":"),
|
||||
}))
|
||||
);
|
||||
// const alarms = computed(() =>
|
||||
// store.alarmData.slice(0, 5).map((d) => ({
|
||||
// ...d,
|
||||
// timestamp_date: dayjs(d.timestamp_date).format("YYYY.MM.DD"),
|
||||
// timestamp_time: d.timestamp_time.split(":").slice(0, 2).join(":"),
|
||||
// }))
|
||||
// );
|
||||
|
||||
const alarms = ref([
|
||||
{
|
||||
uuid: 1,
|
||||
timestamp_date: "2020-01-02",
|
||||
timestamp_time: "11:02",
|
||||
msg: "xx異常",
|
||||
status: "處理中",
|
||||
},
|
||||
{
|
||||
uuid: 2,
|
||||
timestamp_date: "2020-01-03",
|
||||
timestamp_time: "12:02",
|
||||
msg: "xx異常",
|
||||
status: "已處理",
|
||||
},
|
||||
{
|
||||
uuid: 3,
|
||||
timestamp_date: "2020-01-04",
|
||||
timestamp_time: "13:02",
|
||||
msg: "xx異常",
|
||||
status: "已處理",
|
||||
},
|
||||
{
|
||||
uuid: 4,
|
||||
timestamp_date: "2020-01-05",
|
||||
timestamp_time: "14:02",
|
||||
msg: "xx異常",
|
||||
status: "處理中",
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -23,8 +54,8 @@ const alarms = computed(() =>
|
||||
<tr class="">
|
||||
<th>日期</th>
|
||||
<th>時間</th>
|
||||
<th>設備名稱</th>
|
||||
<th>備註</th>
|
||||
<th>狀態</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-base">
|
||||
@ -36,8 +67,8 @@ const alarms = computed(() =>
|
||||
<tr v-else v-for="alarm in alarms" :key="alarm.uuid">
|
||||
<td>{{ alarm.timestamp_date }}</td>
|
||||
<td>{{ alarm.timestamp_time }}</td>
|
||||
<td>{{ alarm.full_name }}</td>
|
||||
<td>{{ alarm.msg }}</td>
|
||||
<td>{{ alarm.status }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -1,32 +0,0 @@
|
||||
<script setup>
|
||||
import DashboardElectricity from "./DashboardElectricity.vue";
|
||||
import { getDashboardEnergy } from "@/apis/dashboard";
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const data = ref({
|
||||
electric: [],
|
||||
oil: [],
|
||||
});
|
||||
const getEnergyData = async () => {
|
||||
const res = await getDashboardEnergy();
|
||||
console.log(res.data);
|
||||
data.value = res.data;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getEnergyData();
|
||||
|
||||
// 每10分鐘
|
||||
setInterval(() => {
|
||||
getEnergyData();
|
||||
}, 600000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-10">
|
||||
<DashboardElectricity :data="data?.electric" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -8,39 +8,43 @@ const initialData = ref({
|
||||
options: [
|
||||
{
|
||||
option: 1,
|
||||
text: "全覽",
|
||||
text: "生產資訊",
|
||||
visible: true,
|
||||
camera_position:
|
||||
'{\r\n "x": -50.037341934259175,\r\n "y": 4.193144220651064,\r\n "z": 48.82382986271525\r\n}',
|
||||
target_position:
|
||||
'{\r\n "x": -48.60645671639422,\r\n "y": -243.44871185400785,\r\n "z": -209.89087363853187\r\n}',
|
||||
top: "40%",
|
||||
},
|
||||
{
|
||||
option: 2,
|
||||
text: "第一調理室",
|
||||
visible: true,
|
||||
camera_position:
|
||||
'{\r\n "x": -28.46493477534664,\r\n "y": -36.0939757799516,\r\n "z": 23.34233364953707\r\n}',
|
||||
'{\r\n "x": -21.45136358561956,\r\n "y": -36.616360736377224,\r\n "z": 24.06657791386018\r\n}',
|
||||
target_position:
|
||||
'{\r\n "x": -20.45671929333666,\r\n "y": -285.17467268716234,\r\n "z": -233.86674284924965\r\n}',
|
||||
'{\r\n "x": -13.443148103609595,\r\n "y": -285.69705764358827,\r\n "z": -233.14249858492653\r\n}',
|
||||
top: "40%",
|
||||
},
|
||||
{
|
||||
option: 3,
|
||||
text: "第二調理室",
|
||||
visible: true,
|
||||
camera_position:
|
||||
'{\r\n "x": -60.447812508764216,\r\n "y": -19.3527808477629,\r\n "z": 18.437219317201254\r\n}',
|
||||
'{\r\n "x": -46.43016370491481,\r\n "y": -30.764006433151266,\r\n "z": 29.75302179892112\r\n}',
|
||||
target_position:
|
||||
'{\r\n "x": -67.50499285142918,\r\n "y": -319.4347359953091,\r\n "z": -176.9159237872396\r\n}',
|
||||
'{\r\n "x": -48.66787230158924,\r\n "y": -253.50633028228958,\r\n "z": -200.14573739171746\r\n}',
|
||||
top: "40%",
|
||||
},
|
||||
{
|
||||
option: 4,
|
||||
text: "冷藏室",
|
||||
visible: true,
|
||||
camera_position:
|
||||
'{\r\n "x": -133.72122968732023,\r\n "y": 17.16800177856409,\r\n "z": 96.3791288407085\r\n}',
|
||||
'{\r\n "x": -159.1333742651323,\r\n "y": 0.20725923658232803,\r\n "z": 57.03929922462568\r\n}',
|
||||
target_position:
|
||||
'{\r\n "x": -135.5987317399465,\r\n "y": -141.97927871150915,\r\n "z": -224.4486321448447\r\n}',
|
||||
'{\r\n "x": -153.80612704653015,\r\n "y": -182.43894674945912,\r\n "z": -176.99952023763058\r\n}',
|
||||
top: "40%",
|
||||
},
|
||||
{
|
||||
option: 5,
|
||||
@ -50,6 +54,17 @@ const initialData = ref({
|
||||
'{\r\n "x": -50.037341934259175,\r\n "y": 4.193144220651064,\r\n "z": 48.82382986271525\r\n}',
|
||||
target_position:
|
||||
'{\r\n "x": -48.60645671639422,\r\n "y": -243.44871185400785,\r\n "z": -209.89087363853187\r\n}',
|
||||
top: "40%",
|
||||
},
|
||||
{
|
||||
option: 6,
|
||||
text: "威鈦閥狀態",
|
||||
visible: true,
|
||||
camera_position:
|
||||
'{\r\n "x": -50.037341934259175,\r\n "y": 4.193144220651064,\r\n "z": 48.82382986271525\r\n}',
|
||||
target_position:
|
||||
'{\r\n "x": -48.60645671639422,\r\n "y": -243.44871185400785,\r\n "z": -209.89087363853187\r\n}',
|
||||
top: "25%",
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -60,8 +75,8 @@ watch(
|
||||
() => initialData.value,
|
||||
(newValue) => {
|
||||
if (newValue?.options[0]) {
|
||||
const { option, camera_position, target_position } = newValue.options[0];
|
||||
changeParams({ option, camera_position, target_position });
|
||||
const { option, camera_position, target_position, top } = newValue.options[0];
|
||||
changeParams({ option, camera_position, target_position, top });
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
@ -92,6 +107,7 @@ watch(
|
||||
option: option.option,
|
||||
camera_position: option.camera_position,
|
||||
target_position: option.target_position,
|
||||
top: option.top
|
||||
})
|
||||
"
|
||||
>
|
||||
|
@ -2,6 +2,15 @@
|
||||
import { ref, defineProps, inject, watch } from "vue";
|
||||
import useSearchParams from "@/hooks/useSearchParam";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import ProductionCard from "./dashboardForgeCards/ProductionCard.vue";
|
||||
import CookingCard from "./dashboardForgeCards/CookingCard.vue";
|
||||
import HeaterCard from "./dashboardForgeCards/HeaterCard.vue";
|
||||
import VesselCard from "./dashboardForgeCards/VesselCard.vue";
|
||||
import CookingPotCard from "./dashboardForgeCards/CookingPotCard.vue";
|
||||
import HeaterPotCard from "./dashboardForgeCards/HeaterPotCard.vue";
|
||||
import RefrigerationCard from "./dashboardForgeCards/RefrigerationCard.vue";
|
||||
import MeterCard from "./dashboardForgeCards/MeterCard.vue";
|
||||
import ValveCard from "./dashboardForgeCards/ValveCard.vue";
|
||||
|
||||
const { searchParams, changeParams } = useSearchParams();
|
||||
|
||||
@ -30,7 +39,6 @@ const productionData = [
|
||||
temperature: "60°C",
|
||||
},
|
||||
];
|
||||
|
||||
const cookingData = [
|
||||
{
|
||||
batch: "3",
|
||||
@ -40,9 +48,9 @@ const cookingData = [
|
||||
temperature: "70°C",
|
||||
sterilization: "殺菌OK",
|
||||
sugar: "6%",
|
||||
material: "補料",
|
||||
material: "品管",
|
||||
sugarStatus: "",
|
||||
materialColor: "text-green-700",
|
||||
materialColor: "text-gray-200",
|
||||
},
|
||||
{
|
||||
batch: "4",
|
||||
@ -52,9 +60,9 @@ const cookingData = [
|
||||
temperature: "70°C",
|
||||
sterilization: "殺菌OK",
|
||||
sugar: "10%",
|
||||
material: "補料",
|
||||
material: "品管",
|
||||
sugarStatus: "warning",
|
||||
materialColor: "text-yellow-500",
|
||||
materialColor: "text-yellow-400",
|
||||
},
|
||||
{
|
||||
batch: "5",
|
||||
@ -64,9 +72,9 @@ const cookingData = [
|
||||
temperature: "60°C",
|
||||
sterilization: "殺菌NG",
|
||||
sugar: "6%",
|
||||
material: "補料",
|
||||
material: "品管",
|
||||
sugarStatus: "",
|
||||
materialColor: "text-green-700",
|
||||
materialColor: "text-green-500",
|
||||
},
|
||||
{
|
||||
batch: "6",
|
||||
@ -76,12 +84,11 @@ const cookingData = [
|
||||
temperature: "80°C",
|
||||
sterilization: "殺菌OK",
|
||||
sugar: "6%",
|
||||
material: "補料",
|
||||
material: "品管",
|
||||
sugarStatus: "",
|
||||
materialColor: "text-red-700",
|
||||
materialColor: "text-red-500",
|
||||
},
|
||||
];
|
||||
|
||||
const heaterData = [
|
||||
{
|
||||
equipment: "加熱器 板式",
|
||||
@ -218,6 +225,8 @@ const cookingPotData = [
|
||||
activeTab2: ref("生產資訊"),
|
||||
},
|
||||
];
|
||||
|
||||
// 加熱器資料
|
||||
const heaterPotData = [
|
||||
{ name: "加熱器 板式", temperature: "3.1°C" },
|
||||
{ name: "加熱器 管式", temperature: "3.1°C" },
|
||||
@ -267,354 +276,186 @@ const meterData = [
|
||||
energyConsumption: "3429.4kWh",
|
||||
},
|
||||
];
|
||||
|
||||
// 威鈦閥異常訊號資料
|
||||
const valveData = [
|
||||
{
|
||||
title: "二重釜800L",
|
||||
groups: [
|
||||
{ code: "SV3", status: "ON" },
|
||||
{ code: "SV4", status: "ON" },
|
||||
{ code: "SV5", status: "ON" },
|
||||
{ code: "SV6", status: "ON" },
|
||||
{ code: "SV7", status: "ON" },
|
||||
{ code: "SV8", status: "ON" },
|
||||
{ code: "SV9", status: "ON" },
|
||||
{ code: "SV10", status: "ON" },
|
||||
{ code: "SV11", status: "ON" },
|
||||
{ code: "SV20", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "二重釜400L",
|
||||
groups: [
|
||||
{ code: "SV12", status: "ON" },
|
||||
{ code: "SV13", status: "ON" },
|
||||
{ code: "SV14", status: "ON" },
|
||||
{ code: "SV15", status: "ON" },
|
||||
{ code: "SV16", status: "ON" },
|
||||
{ code: "SV17", status: "ON" },
|
||||
{ code: "SV18", status: "ON" },
|
||||
{ code: "SV19", status: "ON" },
|
||||
{ code: "SV21", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "補料",
|
||||
groups: [
|
||||
{ code: "SV1", status: "ON" },
|
||||
{ code: "SV2", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "調配槽1",
|
||||
groups: [
|
||||
{ code: "BV1", status: "ON" },
|
||||
{ code: "BV2", status: "ON" },
|
||||
{ code: "BV3", status: "ON" },
|
||||
{ code: "BV4", status: "ON" },
|
||||
{ code: "BV5", status: "ON" },
|
||||
{ code: "BV6", status: "ON" },
|
||||
{ code: "BV7", status: "ON" },
|
||||
{ code: "BV8", status: "ON" },
|
||||
{ code: "BV46", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "調配槽2",
|
||||
groups: [
|
||||
{ code: "BV9", status: "ON" },
|
||||
{ code: "BV10", status: "ON" },
|
||||
{ code: "BV11", status: "ON" },
|
||||
{ code: "BV12", status: "ON" },
|
||||
{ code: "BV13", status: "ON" },
|
||||
{ code: "BV14", status: "ON" },
|
||||
{ code: "BV15", status: "ON" },
|
||||
{ code: "BV16", status: "ON" },
|
||||
{ code: "BV47", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "調配槽3",
|
||||
groups: [
|
||||
{ code: "BV17", status: "ON" },
|
||||
{ code: "BV18", status: "ON" },
|
||||
{ code: "BV19", status: "ON" },
|
||||
{ code: "BV20", status: "ON" },
|
||||
{ code: "BV21", status: "ON" },
|
||||
{ code: "BV22", status: "ON" },
|
||||
{ code: "BV23", status: "ON" },
|
||||
{ code: "BV24", status: "ON" },
|
||||
{ code: "BV48", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "調配槽4",
|
||||
groups: [
|
||||
{ code: "BV25", status: "ON" },
|
||||
{ code: "BV26", status: "ON" },
|
||||
{ code: "BV27", status: "ON" },
|
||||
{ code: "BV28", status: "ON" },
|
||||
{ code: "BV29", status: "ON" },
|
||||
{ code: "BV30", status: "ON" },
|
||||
{ code: "BV31", status: "ON" },
|
||||
{ code: "BV32", status: "ON" },
|
||||
{ code: "BV49", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "熱水",
|
||||
groups: [
|
||||
{ code: "HV1", status: "ON" },
|
||||
{ code: "HV2", status: "ON" },
|
||||
{ code: "HV3", status: "ON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "其他",
|
||||
groups: [
|
||||
{ code: "BV33", status: "ON" },
|
||||
{ code: "BV34", status: "ON" },
|
||||
{ code: "BV35", status: "ON" },
|
||||
{ code: "BV36", status: "ON" },
|
||||
{ code: "BV37", status: "ON" },
|
||||
{ code: "BV38", status: "ON" },
|
||||
{ code: "BV39", status: "ON" },
|
||||
{ code: "BV40", status: "ON" },
|
||||
{ code: "BV41", status: "ON" },
|
||||
{ code: "BV42", status: "ON" },
|
||||
{ code: "BV43", status: "ON" },
|
||||
{ code: "BV44", status: "ON" },
|
||||
{ code: "BV45", status: "ON" },
|
||||
],
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="absolute top-[40%] left-1/2 -translate-x-1/2 z-50">
|
||||
<div class="absolute left-1/2 -translate-x-1/2 z-50"
|
||||
:style="{ top: searchParams?.top || '40%' }"
|
||||
>
|
||||
<template v-if="searchParams?.option == '1'">
|
||||
<div class="flex gap-3">
|
||||
<div class="flex flex-col gap-3">
|
||||
<!-- 二重釜 -->
|
||||
<div
|
||||
v-for="(item, index) in productionData"
|
||||
:key="index"
|
||||
class="stats shadow bg-slate-200"
|
||||
>
|
||||
<div class="stat p-2">
|
||||
<div
|
||||
class="bg-green-600 rounded-lg p-2 shadow-lg shadow-slate-400"
|
||||
>
|
||||
<div class="text-sm text-slate-100">第{{ item.batch }}鍋</div>
|
||||
<div class="text-sm font-bold text-slate-100">
|
||||
{{ item.product }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">
|
||||
{{ item.equipment }}
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
||||
</div>
|
||||
<div class="stat p-2">
|
||||
<div class="text-2xl text-slate-800">
|
||||
<FontAwesomeIcon :icon="['fas', 'temperature-high']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.temperature }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<!-- 調理鍋 -->
|
||||
<div
|
||||
v-for="(item, index) in cookingData"
|
||||
:key="index"
|
||||
class="stats shadow bg-slate-200"
|
||||
>
|
||||
<div class="stat p-2">
|
||||
<div
|
||||
class="bg-green-600 rounded-lg p-2 shadow-lg shadow-slate-400"
|
||||
>
|
||||
<div class="text-sm text-slate-100">第{{ item.batch }}鍋</div>
|
||||
<div class="text-sm font-bold text-slate-100">
|
||||
{{ item.product }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">
|
||||
{{ item.equipment }}
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div class="text-2xl text-slate-800">
|
||||
<FontAwesomeIcon :icon="['fas', 'temperature-high']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.temperature }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div class="text-2xl text-slate-800">
|
||||
<FontAwesomeIcon :icon="['far', 'clock']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.sterilization }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div
|
||||
:class="
|
||||
twMerge(
|
||||
'text-2xl',
|
||||
item.sugarStatus == 'warning'
|
||||
? 'text-red-700'
|
||||
: 'text-slate-800'
|
||||
)
|
||||
"
|
||||
>
|
||||
<FontAwesomeIcon :icon="['fas', 'tint']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">糖{{ item.sugar }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div :class="`text-2xl ${item.materialColor}`">
|
||||
<FontAwesomeIcon :icon="['fas', 'circle']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.material }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<!-- 加熱器 -->
|
||||
<div
|
||||
v-for="(item, index) in heaterData"
|
||||
:key="index"
|
||||
class="stats shadow bg-slate-200"
|
||||
>
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">
|
||||
{{ item.equipment }}
|
||||
</div>
|
||||
<div class="text-sm text-slate-800"></div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div class="text-sm text-slate-800">
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'temperature-high']"
|
||||
class="fa-2x"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.temperature }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ProductionCard :data="productionData" />
|
||||
<CookingCard :data="cookingData" />
|
||||
<HeaterCard :data="heaterData" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="searchParams?.option == '2'">
|
||||
<div class="grid grid-flow-col grid-rows-1 gap-4">
|
||||
<div
|
||||
<VesselCard
|
||||
v-for="(vessel, index) in vesselsData"
|
||||
:key="index"
|
||||
class="card bg-slate-200 text-accent-content rounded-md w-[22rem]"
|
||||
>
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ vessel.name }}</h2>
|
||||
<div
|
||||
role="tablist"
|
||||
class="tabs tabs-boxed tabs-sm bg-opacity-50 shadow-inner shadow-slate-600"
|
||||
>
|
||||
<a
|
||||
v-for="tab in tabs"
|
||||
:key="tab.label"
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{
|
||||
'tab-active !bg-green-500 !text-white shadow-sm shadow-slate-800':
|
||||
vessel.activeTab.value === tab.label,
|
||||
}"
|
||||
@click.prevent="vessel.activeTab.value = tab.label"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-0">
|
||||
<div v-if="vessel.activeTab.value === '生產資訊'">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>品名:</b> {{ vessel.product }}</li>
|
||||
<li><b>鍋次:</b> 第{{ vessel.batch }}鍋</li>
|
||||
<li><b>狀態:</b> {{ vessel.status }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="vessel.activeTab.value === '投料進度'">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr class="border-0 bg-slate-300 text-slate-800">
|
||||
<th>順序</th>
|
||||
<th>動作</th>
|
||||
<th>原料</th>
|
||||
<th>狀態</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(feed, index) in vessel.feedProgress"
|
||||
:key="index"
|
||||
class="border-0"
|
||||
>
|
||||
<th>{{ feed.order }}</th>
|
||||
<td>{{ feed.action }}</td>
|
||||
<td>{{ feed.material }}</td>
|
||||
<td>{{ feed.state }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="vessel.activeTab.value === '流量計'">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr class="border-0 bg-slate-300 text-slate-800">
|
||||
<th></th>
|
||||
<th>當前使用量</th>
|
||||
<th>今日累積量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(flow, index) in vessel.flowMeterData"
|
||||
:key="index"
|
||||
class="border-0"
|
||||
>
|
||||
<th>{{ flow.material }}</th>
|
||||
<td>{{ flow.current }}</td>
|
||||
<td>{{ flow.total }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
:vessel="vessel"
|
||||
:tabs="tabs"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="searchParams?.option == '3'">
|
||||
<div class="grid grid-flow-col grid-rows-2 gap-4">
|
||||
<!-- 調理鍋 -->
|
||||
<div
|
||||
v-for="(pot, index) in cookingPotData"
|
||||
:key="index"
|
||||
class="card bg-slate-200 text-accent-content rounded-md w-60"
|
||||
>
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ pot.name }}</h2>
|
||||
<div
|
||||
role="tablist"
|
||||
class="tabs tabs-boxed tabs-sm bg-opacity-50 shadow-inner shadow-slate-600"
|
||||
>
|
||||
<a
|
||||
v-for="tab in tabs2"
|
||||
:key="tab.label"
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{
|
||||
'tab-active !bg-green-500 !text-white shadow-sm shadow-slate-800':
|
||||
pot.activeTab2.value === tab.label,
|
||||
}"
|
||||
@click.prevent="pot.activeTab2.value = tab.label"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-0">
|
||||
<div v-if="pot.activeTab2.value === '生產資訊'">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>品名:</b> {{ pot.product }}</li>
|
||||
<li><b>鍋次:</b> 第{{ pot.batch }}鍋</li>
|
||||
<li><b>溫度:</b> {{ pot.temperature }}</li>
|
||||
<li><b>糖度:</b> {{ pot.sugar }}</li>
|
||||
<li><b>狀態:</b> {{ pot.status }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="pot.activeTab2.value === '品檢'">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>品檢時間:</b> {{ pot.inspectionTime }}</li>
|
||||
<li><b>風味:</b> {{ pot.flavor }}</li>
|
||||
<li><b>鹽度:</b> {{ pot.salinity }}</li>
|
||||
<li><b>酸度:</b> {{ pot.acidity }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="pot.activeTab2.value === '流量計'">
|
||||
<div class="">
|
||||
<table class="table table-sm whitespace-nowrap">
|
||||
<thead>
|
||||
<tr class="border-0 bg-slate-300 text-slate-800">
|
||||
<th></th>
|
||||
<th>當前使用量</th>
|
||||
<th>今日累積量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="border-0">
|
||||
<th class="">補水</th>
|
||||
<td>{{ pot.waterConsumption }}</td>
|
||||
<td>100L</td>
|
||||
</tr>
|
||||
<tr class="border-0">
|
||||
<th>補醋</th>
|
||||
<td>{{ pot.aceticAcidConsumption }}</td>
|
||||
<td>100L</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加熱器 -->
|
||||
<div
|
||||
<HeaterPotCard
|
||||
v-for="(heat, index) in heaterPotData"
|
||||
:key="index"
|
||||
class="card bg-slate-200 text-accent-content rounded-md w-60"
|
||||
>
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ heat.name }}</h2>
|
||||
<div class="p-0">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>溫度:</b> {{ heat.temperature }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
:heat="heat"
|
||||
/>
|
||||
<CookingPotCard
|
||||
v-for="(pot, index) in cookingPotData"
|
||||
:key="index"
|
||||
:pot="pot"
|
||||
:tabs2="tabs2"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="searchParams?.option == '4'">
|
||||
<div class="flex gap-3">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="stats shadow bg-slate-200">
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">
|
||||
{{ refrigerationData.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div class="text-2xl text-slate-800">
|
||||
<FontAwesomeIcon :icon="['fas', 'temperature-high']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">
|
||||
{{ refrigerationData.temperature }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RefrigerationCard :data="refrigerationData" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="searchParams?.option == '5'">
|
||||
<div class="grid grid-flow-col grid-rows-2 gap-4">
|
||||
<div
|
||||
<MeterCard
|
||||
v-for="(meter, index) in meterData"
|
||||
:key="index"
|
||||
class="card bg-slate-200 text-accent-content rounded-md w-60"
|
||||
>
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ meter.name }}</h2>
|
||||
<div class="shadow-inner shadow-slate-400 rounded-md p-2">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>電流:</b> {{ meter.current }}</li>
|
||||
<li><b>電壓:</b> {{ meter.voltage }}</li>
|
||||
<li><b>功率:</b> {{ meter.power }}</li>
|
||||
<li><b>用電量:</b> {{ meter.energyConsumption }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-actions justify-end">
|
||||
<button class="btn btn-xs btn-success">詳細資料</button>
|
||||
</div>
|
||||
</div>
|
||||
:meter="meter"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="searchParams?.option == '6'">
|
||||
<div class="flex flex-col gap-3">
|
||||
<ValveCard v-for="(card, idx) in valveData" :key="idx" :card="card" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -1,212 +0,0 @@
|
||||
<script setup>
|
||||
import LineChart from "@/components/chart/LineChart.vue";
|
||||
import { SECOND_CHART_COLOR } from "@/constant";
|
||||
import dayjs from "dayjs";
|
||||
import { ref, inject, watch, onMounted } from "vue";
|
||||
import { getDashboardFormulaRoom } from "@/apis/dashboard";
|
||||
import useSearchParams from "@/hooks/useSearchParam";
|
||||
import clearChart from "@/util/clearChart";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import showChartLoading from "@/util/showChartLoading";
|
||||
|
||||
const { searchParams } = useSearchParams();
|
||||
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
|
||||
const { openModal, intervalOption } = inject("dashboard_items");
|
||||
const intervalType = "formula";
|
||||
|
||||
const defaultChartOption = ref({
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
},
|
||||
legend: {
|
||||
data: [],
|
||||
textStyle: {
|
||||
color: "#ffffff",
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: "30%",
|
||||
left: "0%",
|
||||
right: "0%",
|
||||
bottom: "0%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
// type: "time",
|
||||
type: "category",
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
formatter: (value) => {
|
||||
return dayjs(value).format("HH:mm");
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
boundaryGap: [0, "100%"],
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
},
|
||||
},
|
||||
series: [],
|
||||
});
|
||||
|
||||
const formula_temp_chart = ref(null);
|
||||
|
||||
const data = ref([]);
|
||||
const getData = async (timeInterval, typeOption = 1) => {
|
||||
// showChartLoading(formula_temp_chart.value.chart);
|
||||
const res = await getDashboardFormulaRoom({
|
||||
timeInterval,
|
||||
typeOption,
|
||||
});
|
||||
data.value = res.data;
|
||||
};
|
||||
|
||||
const timeoutTimer = ref("");
|
||||
onMounted(() => {
|
||||
setItems([
|
||||
{
|
||||
title: "溫度",
|
||||
key: "temp",
|
||||
active: true,
|
||||
typeOption: 1,
|
||||
},
|
||||
{
|
||||
title: "濕度",
|
||||
key: "humidity",
|
||||
active: false,
|
||||
typeOption: 2,
|
||||
},
|
||||
]);
|
||||
getData(1);
|
||||
// 每10分鐘
|
||||
timeoutTimer.value = setInterval(() => {
|
||||
getData(1, selectedBtn.value.typeOption);
|
||||
}, 600000);
|
||||
});
|
||||
|
||||
watch(
|
||||
selectedBtn,
|
||||
(newValue) => {
|
||||
// window.clearInterval(timeoutTimer.value);
|
||||
getData(
|
||||
parseInt(searchParams.value[intervalType] || 1),
|
||||
newValue.typeOption
|
||||
);
|
||||
timeoutTimer.value = setInterval(() => {
|
||||
getData(
|
||||
parseInt(searchParams.value[intervalType] || 1),
|
||||
newValue.typeOption
|
||||
);
|
||||
}, 600000);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
searchParams,
|
||||
(newValue, oldValue) => {
|
||||
if (
|
||||
newValue[intervalType] &&
|
||||
newValue[intervalType] !== oldValue[intervalType]
|
||||
) {
|
||||
window.clearInterval(timeoutTimer.value);
|
||||
getData(parseInt(newValue[intervalType]), selectedBtn.value.typeOption);
|
||||
timeoutTimer.value = setInterval(() => {
|
||||
getData(parseInt(newValue[intervalType]), selectedBtn.value.typeOption);
|
||||
}, 600000);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(data, (newValue) => {
|
||||
clearChart(formula_temp_chart.value.chart);
|
||||
|
||||
if (newValue.length > 0) {
|
||||
const allValues = newValue.flatMap(({ data }) =>
|
||||
data.map(({ value }) => value).filter((value) => value !== null && value !== undefined)
|
||||
);
|
||||
const minValue = Math.floor(Math.min(...allValues));
|
||||
const maxValue = Math.ceil(Math.max(...allValues));
|
||||
formula_temp_chart.value.chart.setOption({
|
||||
legend: {
|
||||
data: newValue.map(({ full_name }) => full_name),
|
||||
// 初始化為 multiple 模式
|
||||
selectedMode: "multiple",
|
||||
},
|
||||
xAxis: {
|
||||
data: newValue[0].data.map(({ time }) => time),
|
||||
},
|
||||
yAxis: {
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
},
|
||||
series: newValue.map(({ full_name, data }, index) => ({
|
||||
name: full_name,
|
||||
type: "line",
|
||||
data: data.map(({ value }) => value),
|
||||
itemStyle: {
|
||||
color: SECOND_CHART_COLOR[index],
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
// 監聽圖例選擇變更事件
|
||||
formula_temp_chart.value.chart.on("legendselectchanged", function (params) {
|
||||
formula_temp_chart.value.chart.setOption({
|
||||
legend: {
|
||||
selectedMode: "single", // 點擊圖例後切換為 single 模式
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col justify-center mb-3">
|
||||
<h3 class="text-info font-bold text-xl text-center">廠房溫濕度</h3>
|
||||
<div className="mt-2 w-full flex justify-center relative">
|
||||
<ButtonConnectedGroup
|
||||
:items="items"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-xs btn-link px-2 py-1 text-base absolute right-0 btn-text-without-border"
|
||||
@click.stop="() => openModal(intervalType)"
|
||||
>
|
||||
More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LineChart
|
||||
id="dashboard_formula_temp"
|
||||
class="min-h-[350px] max-h-fit"
|
||||
:option="defaultChartOption"
|
||||
ref="formula_temp_chart"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,115 +0,0 @@
|
||||
<script setup>
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { inject, onMounted, watch } from "vue";
|
||||
import useSearchParams from "@/hooks/useSearchParam";
|
||||
const { changeParams, searchParams } = useSearchParams();
|
||||
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
|
||||
const { currentIntervalType } = inject("dashboard_items");
|
||||
|
||||
onMounted(() => {
|
||||
setItems([
|
||||
{
|
||||
title: "1小時",
|
||||
key: "1hour",
|
||||
active:
|
||||
!searchParams.value?.[currentIntervalType.value] ||
|
||||
searchParams.value?.[currentIntervalType.value] === 1,
|
||||
timeInterval: 1,
|
||||
},
|
||||
{
|
||||
title: "4小時",
|
||||
key: "4hour",
|
||||
active: searchParams.value?.[currentIntervalType.value] === 4,
|
||||
timeInterval: 4,
|
||||
},
|
||||
{
|
||||
title: "8小時",
|
||||
key: "8hour",
|
||||
active: searchParams.value?.[currentIntervalType.value] === 8,
|
||||
timeInterval: 8,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
watch(currentIntervalType, (newValue) => {
|
||||
console.log(
|
||||
"currentIntervalType",
|
||||
newValue,
|
||||
searchParams.value,
|
||||
searchParams.value?.[newValue]
|
||||
);
|
||||
setItems([
|
||||
{
|
||||
title: "1小時",
|
||||
key: "1hour",
|
||||
active:
|
||||
!searchParams.value?.[newValue] ||
|
||||
parseInt(searchParams.value?.[newValue]) === 1,
|
||||
timeInterval: 1,
|
||||
},
|
||||
{
|
||||
title: "4小時",
|
||||
key: "4hour",
|
||||
active: parseInt(searchParams.value?.[newValue]) === 4,
|
||||
timeInterval: 4,
|
||||
},
|
||||
{
|
||||
title: "8小時",
|
||||
key: "8hour",
|
||||
active: parseInt(searchParams.value?.[newValue]) === 8,
|
||||
timeInterval: 8,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const onCancel = () => {
|
||||
dashboard_more.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
// decideIntervalOption(selectedBtn.value.timeInterval);
|
||||
console.log(searchParams.value);
|
||||
changeParams({
|
||||
...searchParams.value,
|
||||
[currentIntervalType.value]: selectedBtn.value.timeInterval,
|
||||
});
|
||||
onCancel();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal id="dashboard_more" title="顯示設定" :onCancel="onCancel" width="500">
|
||||
<template #modalContent>
|
||||
<ButtonGroup
|
||||
:items="items"
|
||||
:withLine="true"
|
||||
class="mt-8 mb-6"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -56,7 +56,7 @@ onMounted(() => {
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-info font-bold text-xl text-center">生產量</h3>
|
||||
<h3 class="text-info font-bold text-xl text-center">原醋即時庫存量</h3>
|
||||
<button type="button" class="btn-xs btn btn-info" @click.stop="openModal">
|
||||
{{ isExpanded ? "關閉" : "開啟" }}
|
||||
</button>
|
||||
|
@ -1,71 +0,0 @@
|
||||
<script setup>
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { ref, inject, watch } from "vue";
|
||||
import DashboardProductCompleteModal from "./DashboardProductCompleteModal.vue";
|
||||
import useDashboardOption from "@/hooks/useDashboardOption";
|
||||
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
const options = useDashboardOption(setItems)
|
||||
|
||||
const { progress_data } = inject("dashboard_product_complete");
|
||||
|
||||
const modal_open = ref(false);
|
||||
const onCancel = () => {
|
||||
modal_open.value = false;
|
||||
dashboard_product.close();
|
||||
};
|
||||
const openModal = () => {
|
||||
modal_open.value = true;
|
||||
dashboard_product.showModal();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 生產完成率 -->
|
||||
<DashboardProductCompleteModal :onCancel="onCancel" :open="modal_open" />
|
||||
<div class="flex flex-col justify-center mb-3">
|
||||
<h3 class="text-info font-bold text-xl text-center">今日生產完成率 %</h3>
|
||||
<div className="mt-2 w-full flex justify-center relative">
|
||||
<ButtonConnectedGroup
|
||||
:items="items"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-xs btn-link px-2 py-1 text-base absolute right-0 btn-text-without-border"
|
||||
@click.stop="openModal"
|
||||
>
|
||||
More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="progress_data?.[selectedBtn?.typeOption - 1]">
|
||||
<div
|
||||
class="grid grid-cols-6 gap-3 justify-items-end items-center my-2"
|
||||
v-for="{ pac, value, target, percentage } in progress_data[
|
||||
selectedBtn.typeOption - 1
|
||||
].data"
|
||||
:key="pac"
|
||||
>
|
||||
<span class="col-span-1 text-xl font-bold">{{ pac }}</span>
|
||||
<progress
|
||||
v-if="target !== 0"
|
||||
class="progress progress-success col-span-4"
|
||||
:value="value"
|
||||
:max="target"
|
||||
></progress>
|
||||
<span class="col-span-1 text-xl font-bold justify-self-start"
|
||||
>{{ percentage }} %</span
|
||||
>
|
||||
</div></template
|
||||
>
|
||||
<template v-else>
|
||||
<p class="text-center mt-8 text-lg">尚未設定目標值</p>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
@ -1,99 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, inject, onBeforeMount, defineProps, watch, computed } from "vue";
|
||||
import DashboardProductCompleteModalTarget from "./DashboardProductCompleteModalTarget.vue";
|
||||
import DashboardProductCompleteModalRecord from "./DashboardProductCompleteModalRecord.vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
postDashboardProductTarget,
|
||||
} from "@/apis/dashboard";
|
||||
import useDashboardOption from "@/hooks/useDashboardOption";
|
||||
|
||||
const props = defineProps({
|
||||
onCancel: Function,
|
||||
open: Boolean,
|
||||
});
|
||||
|
||||
const { getCompletion } = inject("dashboard_product_complete");
|
||||
const { items, changeActiveBtn, setItems } = useActiveBtn();
|
||||
|
||||
const options = ref([]);
|
||||
const setOptions = (btnGroup) => {
|
||||
options.value = btnGroup;
|
||||
};
|
||||
useDashboardOption(setOptions);
|
||||
|
||||
const date = ref([]);
|
||||
const formState = ref({});
|
||||
const selectedBtn = ref({});
|
||||
|
||||
const updateData = (data) => {
|
||||
date.value = data.date;
|
||||
formState.value = data.formState;
|
||||
selectedBtn.value = data.selectedBtn;
|
||||
};
|
||||
|
||||
const changeComponent = (e, item) => {
|
||||
changeActiveBtn(item);
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const res = await postDashboardProductTarget({
|
||||
date: dayjs(date.value[0].value).format("YYYY-MM-DD"),
|
||||
type: selectedBtn.value?.typeOption || 1,
|
||||
data: formState.value,
|
||||
});
|
||||
if (res.isSuccess) {
|
||||
getCompletion();
|
||||
props.onCancel();
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
setItems([
|
||||
{
|
||||
title: "目標設定",
|
||||
key: "Target",
|
||||
active: true,
|
||||
component: DashboardProductCompleteModalTarget,
|
||||
},
|
||||
{
|
||||
title: "執行紀錄",
|
||||
key: "Record",
|
||||
active: false,
|
||||
component: DashboardProductCompleteModalRecord,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const activeTab = computed(() => {
|
||||
return items.value.find(({ active }) => active);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal id="dashboard_product" title="顯示設定" :onCancel="onCancel" width="1000" modalClass="bg-body">
|
||||
<template #modalContent>
|
||||
<ButtonGroup :items="items" :withLine="true" class="mt-8 mb-6" :onclick="changeComponent" />
|
||||
<component :is="activeTab.component" :activeKey="activeTab.key" :open="props.open" :options="options" @updateData="updateData"></component>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<div v-if="activeTab.key === 'Target'">
|
||||
<button type="reset" class="btn btn-outline-success mr-2" @click.prevent="onCancel">
|
||||
取消
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-success" @click.stop.prevent="onOk">
|
||||
確定
|
||||
</button>
|
||||
</div>
|
||||
<div v-else="activeTab.key === 'Record'">
|
||||
<button type="reset" class="btn btn-outline-success mr-2" @click.prevent="onCancel">
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,125 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import {
|
||||
getDashboardProductRecord,
|
||||
} from "@/apis/dashboard";
|
||||
|
||||
const tableLoading = ref(false);
|
||||
const dataSource = ref([]);
|
||||
|
||||
const {
|
||||
items: dateRange,
|
||||
setItems: setDateItems,
|
||||
} = useActiveBtn();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: "date",
|
||||
title: "日期",
|
||||
},
|
||||
{
|
||||
key: "full_name",
|
||||
title: "項目",
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
key: "device_value",
|
||||
title: "實際生產量(L)",
|
||||
},
|
||||
{
|
||||
key: "target_value",
|
||||
title: "目標生產量(L)",
|
||||
},
|
||||
{
|
||||
key: "f2_value",
|
||||
title: "二廠實際包裝數",
|
||||
},
|
||||
{
|
||||
key: "f3_value",
|
||||
title: "三廠實際包裝數",
|
||||
},
|
||||
{
|
||||
key: "target",
|
||||
title: "目標包裝數",
|
||||
},
|
||||
];
|
||||
|
||||
const submitBtns = computed(() => [
|
||||
{
|
||||
title: "查詢",
|
||||
key: "search",
|
||||
active: true,
|
||||
onClick: getRecord,
|
||||
disabled: tableLoading.value,
|
||||
},
|
||||
]);
|
||||
|
||||
const getRecord = async () => {
|
||||
tableLoading.value = true;
|
||||
try {
|
||||
const res = await getDashboardProductRecord({
|
||||
start_time: dayjs(dateRange.value[0].value).format("YYYY-MM-DD"),
|
||||
end_time: dayjs(dateRange.value[1].value).format("YYYY-MM-DD"),
|
||||
});
|
||||
dataSource.value = res.data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
} finally {
|
||||
tableLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
setDateItems([
|
||||
{
|
||||
key: "start_time",
|
||||
value: dayjs(),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "起始日期",
|
||||
},
|
||||
{
|
||||
key: "end_time",
|
||||
value: dayjs(),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "結束時間",
|
||||
},
|
||||
]);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-row items-end">
|
||||
<DateGroup :items="dateRange" :withLine="true" width="500">
|
||||
<template #topLeft>日期</template>
|
||||
</DateGroup>
|
||||
<ButtonGroup :items="submitBtns" :withLine="false" class="ml-8 mr-8 mb-4" />
|
||||
</div>
|
||||
|
||||
<Table :loading="tableLoading" :columns="columns" :dataSource="dataSource">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'f2_value'">
|
||||
<div v-for="(item, index) in record.data" :key="index">
|
||||
<p class="text-start">{{ item.pac }} : {{ item.f2_value }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'f3_value'">
|
||||
<div v-for="(item, index) in record.data" :key="index">
|
||||
<p class="text-start">{{ item.pac }} : {{ item.f3_value }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'target'">
|
||||
<div v-for="(item, index) in record.data" :key="index">
|
||||
<p class="text-start">{{ item.pac }} : {{ item.target }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -1,111 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, inject, onMounted, defineProps, watch, watchEffect } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import {
|
||||
getDashboardProductTarget,
|
||||
} from "@/apis/dashboard";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
open: Boolean,
|
||||
options: Array,
|
||||
activeKey: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["updateData"]);
|
||||
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
setItems(props.options);
|
||||
|
||||
const formState = ref({
|
||||
pot: 0,
|
||||
"130ml": 0,
|
||||
"150ml": 0,
|
||||
"1L": 0,
|
||||
"2L": 0,
|
||||
});
|
||||
|
||||
const date = ref([
|
||||
{
|
||||
key: "date",
|
||||
value: dayjs(),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "日期設定",
|
||||
},
|
||||
]);
|
||||
|
||||
const { initialData } = inject("dashboard_items");
|
||||
|
||||
const getSettings = async () => {
|
||||
const res = await getDashboardProductTarget({
|
||||
date: dayjs(date.value[0].value).format("YYYY-MM-DD"),
|
||||
type: selectedBtn.value?.typeOption,
|
||||
});
|
||||
|
||||
formState.value = Object.fromEntries(
|
||||
res.data.data.map(({ name, value }) => [name, value])
|
||||
);
|
||||
|
||||
emit("updateData", { date: date.value, formState: formState.value, selectedBtn: selectedBtn.value });
|
||||
};
|
||||
|
||||
watch(
|
||||
[selectedBtn, date],
|
||||
() => {
|
||||
getSettings();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
getSettings();
|
||||
setItems(props.options);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.activeKey == 'Target') {
|
||||
getSettings();
|
||||
setItems(props.options);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DateGroup :isBottomLabelExist="false" :items="date" :withLine="false" class="mt-5">
|
||||
<template #topLeft>日期</template>
|
||||
</DateGroup>
|
||||
<ButtonConnectedGroup :items="items" :className="`w-full mt-8 mb-4`" size="md" color="info" :onclick="(e, item) => {
|
||||
changeActiveBtn(item);
|
||||
}" />
|
||||
<div class="flex flex-col items-start">
|
||||
<p>鍋型</p>
|
||||
<div class="flex justify-between">
|
||||
<InputNumber :value="formState" width="150" class="mr-4" name="pot">
|
||||
<template #topLeft>總數</template>
|
||||
</InputNumber>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-start">
|
||||
<p>包裝類型</p>
|
||||
<div class="flex justify-between">
|
||||
<template v-if="initialData">
|
||||
<InputNumber v-for="size in initialData?.points?.[0].values" :key="size.text" :value="formState" width="150"
|
||||
class="mr-4" :name="size.text">
|
||||
<template #topLeft>{{ size.text }}</template>
|
||||
</InputNumber>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -11,53 +11,37 @@ const getCompletion = async () => {
|
||||
production_data.value = [
|
||||
{
|
||||
id: 1,
|
||||
productName: "產品 A",
|
||||
status: "生產中",
|
||||
completionRate: 0.65, // 65%
|
||||
productName: "蘋果醋",
|
||||
status: "調理中",
|
||||
completionRate: 0.7, // 70%
|
||||
details: [
|
||||
{ pot: 1, location: "A1", status: "待處理" },
|
||||
{ pot: 2, location: "A2", status: "生產中" },
|
||||
{ pot: "第1鍋", location: "包裝室", status: "生產中" },
|
||||
{ pot: "第2鍋", location: "充填室", status: "生產中" },
|
||||
{ pot: "第3鍋", location: "調理桶甲", status: "生產中" },
|
||||
{ pot: "第4鍋", location: "調理桶乙", status: "生產中" },
|
||||
{ pot: "第5鍋", location: "調理桶丙", status: "生產中" },
|
||||
{ pot: "第6鍋", location: "調理桶丁", status: "生產中" },
|
||||
{
|
||||
pot: "第7鍋",
|
||||
location: "二重釜大-梅精醋\n二重釜小-加熱",
|
||||
status: "生產中",
|
||||
},
|
||||
{ pot: "第8鍋", location: "", status: "備料" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
productName: "產品 B",
|
||||
status: "已完成",
|
||||
completionRate: 1.0, // 100%
|
||||
details: [
|
||||
{ pot: 1, location: "B1", status: "已完成" },
|
||||
{ pot: 2, location: "B2", status: "已完成" },
|
||||
],
|
||||
productName: "梅子醋",
|
||||
status: "尚未生產",
|
||||
completionRate: 0,
|
||||
details: [],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
productName: "產品 C",
|
||||
status: "待生產",
|
||||
completionRate: 0.0, // 0%
|
||||
details: [
|
||||
{ pot: 1, location: "C1", status: "待處理" },
|
||||
{ pot: 2, location: "C2", status: "待處理" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
productName: "產品 D",
|
||||
status: "生產中",
|
||||
completionRate: 0.3, // 30%
|
||||
details: [
|
||||
{ pot: 1, location: "D1", status: "待處理" },
|
||||
{ pot: 2, location: "D2", status: "生產中" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
productName: "產品 E",
|
||||
status: "異常",
|
||||
completionRate: 0.1, // 10%
|
||||
details: [
|
||||
{ pot: 1, location: "E1", status: "異常" },
|
||||
{ pot: 2, location: "E2", status: "待處理" },
|
||||
],
|
||||
productName: "桑葚醋",
|
||||
status: "尚未生產",
|
||||
completionRate: 0,
|
||||
details: [],
|
||||
},
|
||||
];
|
||||
};
|
||||
@ -81,7 +65,11 @@ onMounted(() => {
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="text-info font-bold text-xl text-center">今日生產目標</h3>
|
||||
<button v-if="selectedProduct" class="btn-xs btn btn-info" @click="goBack">
|
||||
<button
|
||||
v-if="selectedProduct"
|
||||
class="btn-xs btn btn-info"
|
||||
@click="goBack"
|
||||
>
|
||||
返回
|
||||
</button>
|
||||
</div>
|
||||
|
@ -0,0 +1,58 @@
|
||||
<script setup>
|
||||
defineProps({ data: Array });
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
v-for="(item, index) in data"
|
||||
:key="index"
|
||||
class="stats shadow bg-slate-200"
|
||||
>
|
||||
<div class="stat p-2">
|
||||
<div class="bg-green-600 rounded-lg p-2 shadow-lg shadow-slate-400">
|
||||
<div class="text-sm text-slate-100">第{{ item.batch }}鍋</div>
|
||||
<div class="text-sm font-bold text-slate-100">{{ item.product }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">{{ item.equipment }}</div>
|
||||
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div class="text-2xl text-slate-800">
|
||||
<FontAwesomeIcon :icon="['fas', 'temperature-high']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.temperature }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div :class="['text-2xl',
|
||||
item.sterilization == '殺菌NG' ? 'text-red-700' : 'text-slate-800',
|
||||
]">
|
||||
<FontAwesomeIcon :icon="['far', 'clock']" />
|
||||
</div>
|
||||
<div :class="['text-sm',
|
||||
item.sterilization == '殺菌NG' ? 'text-red-700' : 'text-slate-800',
|
||||
]">{{ item.sterilization }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div
|
||||
:class="[
|
||||
'text-2xl',
|
||||
item.sugarStatus == 'warning' ? 'text-red-700' : 'text-slate-800',
|
||||
]"
|
||||
>
|
||||
<FontAwesomeIcon :icon="['fas', 'tint']" />
|
||||
</div>
|
||||
<div :class="['text-sm',
|
||||
item.sugarStatus == 'warning' ? 'text-red-700' : 'text-slate-800',
|
||||
]">糖{{ item.sugar }}</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div :class="`text-2xl ${item.materialColor}`">
|
||||
<FontAwesomeIcon :icon="['fas', 'circle']" class="border-2 border-slate-400 rounded-full"/>
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.material }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,75 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
pot: Object,
|
||||
tabs2: Array
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="card bg-slate-200 text-accent-content rounded-md w-60">
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ pot.name }}</h2>
|
||||
<div
|
||||
role="tablist"
|
||||
class="tabs tabs-boxed tabs-sm bg-opacity-50 shadow-inner shadow-slate-600"
|
||||
>
|
||||
<a
|
||||
v-for="tab in tabs2"
|
||||
:key="tab.label"
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{
|
||||
'tab-active !bg-green-500 !text-white shadow-sm shadow-slate-800':
|
||||
pot.activeTab2.value === tab.label,
|
||||
}"
|
||||
@click.prevent="pot.activeTab2.value = tab.label"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-0">
|
||||
<div v-if="pot.activeTab2.value === '生產資訊'">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>品名:</b> {{ pot.product }}</li>
|
||||
<li><b>鍋次:</b> 第{{ pot.batch }}鍋</li>
|
||||
<li><b>溫度:</b> {{ pot.temperature }}</li>
|
||||
<li><b>糖度:</b> {{ pot.sugar }}</li>
|
||||
<li><b>狀態:</b> {{ pot.status }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="pot.activeTab2.value === '品檢'">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>品檢時間:</b> {{ pot.inspectionTime }}</li>
|
||||
<li><b>風味:</b> {{ pot.flavor }}</li>
|
||||
<li><b>鹽度:</b> {{ pot.salinity }}</li>
|
||||
<li><b>酸度:</b> {{ pot.acidity }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="pot.activeTab2.value === '流量計'">
|
||||
<div class="">
|
||||
<table class="table table-sm whitespace-nowrap">
|
||||
<thead>
|
||||
<tr class="border-0 bg-slate-300 text-slate-800">
|
||||
<th></th>
|
||||
<th>當前使用量</th>
|
||||
<th>今日累積量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="border-0">
|
||||
<th class="">補水</th>
|
||||
<td>{{ pot.waterConsumption }}</td>
|
||||
<td>100L</td>
|
||||
</tr>
|
||||
<tr class="border-0">
|
||||
<th>補醋</th>
|
||||
<td>{{ pot.aceticAcidConsumption }}</td>
|
||||
<td>100L</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,23 @@
|
||||
<script setup>
|
||||
defineProps({ data: Array })
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
v-for="(item, index) in data"
|
||||
:key="index"
|
||||
class="stats shadow bg-slate-200"
|
||||
>
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">{{ item.equipment }}</div>
|
||||
<div class="text-sm text-slate-800"></div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div class="text-sm text-slate-800">
|
||||
<FontAwesomeIcon :icon="['fas', 'temperature-high']" class="fa-2x" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.temperature }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,17 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
heat: Object
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="card bg-slate-200 text-accent-content rounded-md w-60">
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ heat.name }}</h2>
|
||||
<div class="p-0">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>溫度:</b> {{ heat.temperature }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
defineProps({ meter: Object })
|
||||
</script>
|
||||
<template>
|
||||
<div class="card bg-slate-200 text-accent-content rounded-md w-60">
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ meter.name }}</h2>
|
||||
<div class="shadow-inner shadow-slate-400 rounded-md p-2">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>電流:</b> {{ meter.current }}</li>
|
||||
<li><b>電壓:</b> {{ meter.voltage }}</li>
|
||||
<li><b>功率:</b> {{ meter.power }}</li>
|
||||
<li><b>用電量:</b> {{ meter.energyConsumption }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-actions justify-end">
|
||||
<button class="btn btn-xs btn-success">詳細資料</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,29 @@
|
||||
<script setup>
|
||||
defineProps({ data: Array });
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
v-for="(item, index) in data"
|
||||
:key="index"
|
||||
class="stats shadow bg-slate-200"
|
||||
>
|
||||
<div class="stat p-2">
|
||||
<div class="bg-green-600 rounded-lg p-2 shadow-lg shadow-slate-400">
|
||||
<div class="text-sm text-slate-100">第{{ item.batch }}鍋</div>
|
||||
<div class="text-sm font-bold text-slate-100">{{ item.product }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">{{ item.equipment }}</div>
|
||||
<div class="text-sm text-slate-800">{{ item.status }}</div>
|
||||
</div>
|
||||
<div class="stat p-2">
|
||||
<div class="text-2xl text-slate-800">
|
||||
<FontAwesomeIcon :icon="['fas', 'temperature-high']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">{{ item.temperature }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
defineProps({ data: Object });
|
||||
</script>
|
||||
<template>
|
||||
<div class="stats shadow bg-slate-200">
|
||||
<div class="stat p-2">
|
||||
<div class="text-sm font-bold text-slate-800">
|
||||
{{ data.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat text-center p-2">
|
||||
<div class="text-2xl text-slate-800">
|
||||
<FontAwesomeIcon :icon="['fas', 'temperature-high']" />
|
||||
</div>
|
||||
<div class="text-sm text-slate-800">
|
||||
{{ data.temperature }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,27 @@
|
||||
<script setup>
|
||||
defineProps({ card: Object });
|
||||
import { twMerge } from "tailwind-merge";
|
||||
</script>
|
||||
<template>
|
||||
<div class="stats justify-start shadow bg-slate-200">
|
||||
<div class="stat w-24 p-2">
|
||||
<div class="bg-gray-600 rounded-lg p-2 shadow-lg shadow-slate-400">
|
||||
<div class="text-sm text-slate-100">{{ card.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="item in card.groups" :key="item.code" class="stat p-2">
|
||||
<div class="text-2xl">
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'circle']"
|
||||
:class="
|
||||
twMerge(
|
||||
'border-2 border-slate-400 rounded-full',
|
||||
item.status == 'ON' ? 'text-green-500' : 'text-gray-400'
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<span class="text-sm text-slate-800">{{ item.code }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,90 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
vessel: Object,
|
||||
tabs: Array,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="card bg-slate-200 text-accent-content rounded-md w-[22rem]">
|
||||
<div class="card-body p-3">
|
||||
<h2 class="card-title">{{ vessel.name }}</h2>
|
||||
<div
|
||||
role="tablist"
|
||||
class="tabs tabs-boxed tabs-sm bg-opacity-50 shadow-inner shadow-slate-600"
|
||||
>
|
||||
<a
|
||||
v-for="tab in tabs"
|
||||
:key="tab.label"
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{
|
||||
'tab-active !bg-green-500 !text-white shadow-sm shadow-slate-800':
|
||||
vessel.activeTab.value === tab.label,
|
||||
}"
|
||||
@click.prevent="vessel.activeTab.value = tab.label"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-0">
|
||||
<div v-if="vessel.activeTab.value === '生產資訊'">
|
||||
<ul class="leading-7 tracking-wider text-slate-700 px-2">
|
||||
<li><b>品名:</b> {{ vessel.product }}</li>
|
||||
<li><b>鍋次:</b> 第{{ vessel.batch }}鍋</li>
|
||||
<li><b>狀態:</b> {{ vessel.status }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="vessel.activeTab.value === '投料進度'">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr class="border-0 bg-slate-300 text-slate-800">
|
||||
<th>順序</th>
|
||||
<th>動作</th>
|
||||
<th>原料</th>
|
||||
<th>狀態</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(feed, index) in vessel.feedProgress"
|
||||
:key="index"
|
||||
class="border-0"
|
||||
>
|
||||
<th>{{ feed.order }}</th>
|
||||
<td>{{ feed.action }}</td>
|
||||
<td>{{ feed.material }}</td>
|
||||
<td>{{ feed.state }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="vessel.activeTab.value === '流量計'">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr class="border-0 bg-slate-300 text-slate-800">
|
||||
<th></th>
|
||||
<th>當前使用量</th>
|
||||
<th>今日累積量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(flow, index) in vessel.flowMeterData"
|
||||
:key="index"
|
||||
class="border-0"
|
||||
>
|
||||
<th>{{ flow.material }}</th>
|
||||
<td>{{ flow.current }}</td>
|
||||
<td>{{ flow.total }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -112,7 +112,7 @@ const getPoint = async (Device_list, Cumulant) => {
|
||||
setPoints(
|
||||
res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.points,
|
||||
title: d.item_name,
|
||||
key: d.points,
|
||||
active: index === 0,
|
||||
}))
|
||||
|
@ -11,7 +11,9 @@ const store = useUserInfoStore();
|
||||
const router = useRouter();
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const imageSrc =
|
||||
import.meta.env.MODE === "production" ? "../dist/background.jpg" : "./background.jpg";
|
||||
import.meta.env.MODE === "production"
|
||||
? "../dist/background.png"
|
||||
: "./background.png";
|
||||
let schema = yup.object({
|
||||
account: yup.string().required("必填"),
|
||||
password: yup.string().required("必填"),
|
||||
@ -41,33 +43,23 @@ const doLogin = async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-white dark:bg-gray-900">
|
||||
<div class="flex justify-center h-screen">
|
||||
<div
|
||||
class="hidden bg-cover lg:block lg:w-2/3"
|
||||
class="absolute top-0 left-0 w-screen h-screen bg-cover bg-left bg-no-repeat z-0"
|
||||
:style="{ backgroundImage: `url(${imageSrc})` }"
|
||||
></div>
|
||||
|
||||
<div class="flex items-center w-full max-w-md px-6 mx-auto lg:w-2/6">
|
||||
<div class="flex-1">
|
||||
<div class="text-center">
|
||||
<div class="flex justify-center mx-auto">
|
||||
<img class="w-20" src="/logo.png" alt="logo" />
|
||||
</div>
|
||||
<p class="mt-3 text-2xl text-gray-500 dark:text-gray-300">
|
||||
台灣百家珍釀造食品股份有限公司
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<div>
|
||||
<div
|
||||
class="absolute sm:bottom-10 sm:left-52 z-20 w-full max-w-md p-6 rounded-lg bg-white dark:bg-gray-800 "
|
||||
>
|
||||
<div class="mt-4">
|
||||
<div class="flex items-center">
|
||||
<label
|
||||
class="block mb-2 text-lg text-gray-600 dark:text-gray-200"
|
||||
class="w-16 text-lg text-gray-600 dark:text-gray-200"
|
||||
for="account"
|
||||
>帳號</label
|
||||
>
|
||||
<input
|
||||
name="account"
|
||||
class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-lg dark:placeholder-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 focus:border-rose-400 dark:focus:border-rose-400 focus:ring-rose-400 focus:outline-none focus:ring focus:ring-opacity-40"
|
||||
class="block w-full px-4 py-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-lg dark:placeholder-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 focus:border-rose-400 dark:focus:border-rose-400 focus:ring-rose-400 focus:outline-none focus:ring focus:ring-opacity-40"
|
||||
v-model="formState.account"
|
||||
autocomplete="none"
|
||||
/>
|
||||
@ -75,16 +67,14 @@ const doLogin = async () => {
|
||||
{{ formErrorMsg.account }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<label
|
||||
class="text-lg text-gray-600 dark:text-gray-200"
|
||||
for="password"
|
||||
<div class="flex items-center mt-3">
|
||||
<label class=" w-16 text-lg text-gray-600 dark:text-gray-200" for="password"
|
||||
>密碼</label
|
||||
>
|
||||
<div class="relative">
|
||||
<div class="relative w-full">
|
||||
<input
|
||||
name="password"
|
||||
class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-lg dark:placeholder-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 focus:border-rose-400 dark:focus:border-rose-400 focus:ring-rose-400 focus:outline-none focus:ring focus:ring-opacity-40"
|
||||
class="block w-full px-4 py-2 text-gray-700 placeholder-gray-400 bg-white border border-gray-200 rounded-lg dark:placeholder-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 focus:border-rose-400 dark:focus:border-rose-400 focus:ring-rose-400 focus:outline-none focus:ring focus:ring-opacity-40"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
v-model="formState.password"
|
||||
autocomplete="off"
|
||||
@ -99,7 +89,7 @@ const doLogin = async () => {
|
||||
{{ formErrorMsg.password }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<div class="mt-6">
|
||||
<button
|
||||
class="text-lg w-full px-4 py-2 tracking-wide text-white transition-colors duration-300 transform bg-rose-500 rounded-lg hover:bg-rose-400 focus:outline-none focus:bg-rose-400 focus:ring focus:ring-rose-300 focus:ring-opacity-50"
|
||||
@click.stop.prevent="doLogin"
|
||||
@ -109,9 +99,6 @@ const doLogin = async () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -1,46 +1,46 @@
|
||||
<script setup>
|
||||
import Forge from "@/components/forge/Forge.vue";
|
||||
import DashboardForgeOptionButton from "@/views/dashboard/components/DashboardForgeOptionButton.vue";
|
||||
import { getDashboardInit } from "@/apis/dashboard";
|
||||
import { onMounted, ref, provide, watch } from "vue";
|
||||
import ProductSettingBtn from "./components/ProducSettingBtn.vue";
|
||||
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
||||
import WeightLossMachineTable from "./components/WeightLossMachineSettingTable.vue";
|
||||
import { computed, onMounted, ref, provide, onBeforeMount } from "vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
|
||||
const initialData = ref(null);
|
||||
// const forgeData = ref([]);
|
||||
const init = async () => {
|
||||
const res = await getDashboardInit("PS");
|
||||
initialData.value = res.data;
|
||||
const changeComponent = (e, item) => {
|
||||
changeActiveBtn(item);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
const { items, changeActiveBtn, setItems } = useActiveBtn();
|
||||
|
||||
onBeforeMount(() => {
|
||||
setItems([
|
||||
{
|
||||
title: "檢重機設定",
|
||||
key: "WeightLossMachineTable",
|
||||
active: true,
|
||||
component: WeightLossMachineTable,
|
||||
},
|
||||
// {
|
||||
// title: "B 設備管理",
|
||||
// key: "ProducSettingTable2",
|
||||
// active: false,
|
||||
// component: ProducSettingTable,
|
||||
// }
|
||||
]);
|
||||
});
|
||||
|
||||
// const openModal = (type) => {
|
||||
// production_setting_modal.showModal();
|
||||
// };
|
||||
|
||||
provide("productSetting_items", {
|
||||
initialData,
|
||||
// forgeData,
|
||||
// openModal,
|
||||
const activeTab = computed(() => {
|
||||
return items.value.find(({ active }) => active);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-between my-10">
|
||||
<ProductSettingBtn />
|
||||
<DashboardForgeOptionButton :initialData="initialData" />
|
||||
<Forge
|
||||
:fullScreen="true"
|
||||
:cubeStyle="{ right: 5, top: 5 }"
|
||||
:initialData="initialData"
|
||||
<h1 class="text-2xl font-extrabold mb-2">生產設定</h1>
|
||||
<ButtonGroup
|
||||
:items="items"
|
||||
:withLine="true"
|
||||
class="mb-6"
|
||||
:onclick="changeComponent"
|
||||
/>
|
||||
</div>
|
||||
<component :is="activeTab.component"></component>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.border-dashboard {
|
||||
@apply border border-light-info bg-dark-info bg-opacity-70 p-3 first:mt-0 last:mb-0;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -1,54 +0,0 @@
|
||||
<script setup>
|
||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||
import ProductSettingPointModal from "./ProductSettingPointModal.vue";
|
||||
import ProductSettingTypeModal from "./ProductSettingTypeModal.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const store = useUserInfoStore();
|
||||
|
||||
const btns = computed(() =>
|
||||
store.auth_page.filter((d) => d.authCode === "PS3" || d.authCode === "PS4")
|
||||
);
|
||||
|
||||
const openModal = (code) => {
|
||||
if (code === "PS3") {
|
||||
production_setting_PS3_modal.showModal();
|
||||
} else if (code === "PS4") {
|
||||
production_setting_PS4_modal.showModal();
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = (code) => {
|
||||
if (code === "PS3") {
|
||||
production_setting_PS3_modal.close();
|
||||
} else if (code === "PS4") {
|
||||
production_setting_PS4_modal.close();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex absolute right-10 top-0 z-40">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success mx-2"
|
||||
v-for="btn in btns"
|
||||
:key="btn.authCode"
|
||||
@click="() => openModal(btn.authCode)"
|
||||
>
|
||||
{{ btn.subName }}
|
||||
</button>
|
||||
</div>
|
||||
<ProductSettingPointModal
|
||||
v-if="btns.find(({ authCode }) => authCode === 'PS4')?.subName"
|
||||
:title="btns.find(({ authCode }) => authCode === 'PS4')?.subName"
|
||||
:onCancel="() => onCancel('PS4')"
|
||||
/>
|
||||
<ProductSettingTypeModal
|
||||
v-if="btns.find(({ authCode }) => authCode === 'PS3')?.subName"
|
||||
:title="btns.find(({ authCode }) => authCode === 'PS3')?.subName"
|
||||
:onCancel="() => onCancel('PS3')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,162 +0,0 @@
|
||||
<script setup>
|
||||
import { defineProps, inject, watch } from "vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { getDashboardDevice } from "@/apis/dashboard";
|
||||
import { postProductSettingPoint } from "@/apis/productSetting";
|
||||
import useDashboardOption from "@/hooks/useDashboardOption";
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
onCancel: Function,
|
||||
});
|
||||
|
||||
const {
|
||||
items: types,
|
||||
changeActiveBtn: changeActiveTypeBtn,
|
||||
setItems: setTypeItems,
|
||||
selectedBtn: selectedTypeBtn,
|
||||
} = useActiveBtn(); // Type
|
||||
const options = useDashboardOption(setTypeItems, "productSetting_items");
|
||||
|
||||
const {
|
||||
items: builings,
|
||||
changeActiveBtn: changeActiveBuildingBtn,
|
||||
setItems: setBuildingItems,
|
||||
selectedBtn: selectedBuildingBtn,
|
||||
} = useActiveBtn(); // building
|
||||
|
||||
const { initialData } = inject("productSetting_items");
|
||||
|
||||
watch(
|
||||
initialData,
|
||||
(newValue) => {
|
||||
console.log("initialData", newValue);
|
||||
newValue &&
|
||||
setBuildingItems(
|
||||
newValue.options.map(({ option, text }, index) => ({
|
||||
title: text,
|
||||
key: `option_${option}`,
|
||||
active: index === 0,
|
||||
value: option,
|
||||
}))
|
||||
);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const {
|
||||
items: devices,
|
||||
changeActiveBtn: changeActiveDeviceBtn,
|
||||
setItems: setDeviceItems,
|
||||
selectedBtn: selectedDeviceBtn,
|
||||
} = useActiveBtn("multiple"); // Device
|
||||
|
||||
const getDeviceList = async (option) => {
|
||||
const res = await getDashboardDevice({ option });
|
||||
let data = [];
|
||||
res.data
|
||||
.filter(({ is_batch }) => is_batch === 1)
|
||||
.forEach(({ device }) => {
|
||||
data = [
|
||||
...data,
|
||||
...device.map(({ device_number, full_name }) => ({
|
||||
title: full_name,
|
||||
key: device_number,
|
||||
active: false,
|
||||
device_number,
|
||||
})),
|
||||
];
|
||||
});
|
||||
setDeviceItems(data);
|
||||
};
|
||||
|
||||
watch(
|
||||
selectedBuildingBtn,
|
||||
(newValue) => {
|
||||
newValue && getDeviceList(newValue.value);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const onOk = async () => {
|
||||
const res = await postProductSettingPoint(
|
||||
selectedTypeBtn.value,
|
||||
selectedDeviceBtn.value
|
||||
);
|
||||
props.onCancel();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
id="production_setting_PS4_modal"
|
||||
:title="title"
|
||||
:onCancel="onCancel"
|
||||
width="700"
|
||||
>
|
||||
<template #modalContent>
|
||||
<div class="mt-7">
|
||||
<p class="text-lg">煮食種類</p>
|
||||
<ButtonGroup
|
||||
:items="types"
|
||||
:withLine="true"
|
||||
class="my-2"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveTypeBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-7">
|
||||
<p class="text-lg">廠區</p>
|
||||
<ButtonGroup
|
||||
:items="builings"
|
||||
:withLine="true"
|
||||
class="my-2"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveBuildingBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-7">
|
||||
<p class="text-lg">設備(複選)</p>
|
||||
<ButtonGroup
|
||||
:items="devices"
|
||||
:withLine="false"
|
||||
class="my-2"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveDeviceBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,141 +0,0 @@
|
||||
<script setup>
|
||||
import { defineProps, ref, watch, inject } from "vue";
|
||||
import * as yup from "yup";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { postProductSettingType } from "@/apis/productSetting";
|
||||
|
||||
const props = defineProps({
|
||||
editRecord: Object,
|
||||
getData: Function,
|
||||
updateEditRecord: Function,
|
||||
});
|
||||
const { openToast } = inject("app_toast");
|
||||
const formScheme = yup.object({
|
||||
product_name: yup.string().required("必填"),
|
||||
operate_text: yup.string(),
|
||||
disable: yup.number().required("必填"), // 0:啟用;1:停用
|
||||
});
|
||||
const formState = ref({
|
||||
product_name: "",
|
||||
operate_text: "",
|
||||
disable: 0,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.editRecord,
|
||||
(newValue) => {
|
||||
console.log(newValue);
|
||||
if (newValue) {
|
||||
formState.value = newValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
||||
useFormErrorMessage(formScheme);
|
||||
const form = ref(null);
|
||||
|
||||
const reset = () => {
|
||||
let resetForm = {};
|
||||
for (let key in formState.value) {
|
||||
resetForm[key] = typeof formState.value[key] == "number" ? 0 : "";
|
||||
}
|
||||
formState.value = resetForm;
|
||||
};
|
||||
const onCancel = () => {
|
||||
reset();
|
||||
handleErrorReset();
|
||||
props.updateEditRecord(null);
|
||||
production_setting_PS3_Add_modal.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const values = await handleSubmit(formScheme, formState.value);
|
||||
console.log(values);
|
||||
const params = props.editRecord ? { ...props.editRecord, ...values } : values;
|
||||
const res = await postProductSettingType(params);
|
||||
if (res.isSuccess) {
|
||||
props.getData();
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#production_setting_PS3_Add_modal");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
id="production_setting_PS3_Add_modal"
|
||||
:title="editRecord ? '編輯' : '新增'"
|
||||
:onCancel="onCancel"
|
||||
width="500"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form">
|
||||
<Input
|
||||
v-if="!editRecord"
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
name="product_name"
|
||||
>
|
||||
<template #topLeft>名稱</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.product_name }}
|
||||
</span></template
|
||||
>
|
||||
</Input>
|
||||
<Input :value="formState" class="my-2" name="operate_text">
|
||||
<template #topLeft>顯示名稱</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.operate_text }}
|
||||
</span></template
|
||||
>
|
||||
</Input>
|
||||
<RadioGroup
|
||||
class="my-2"
|
||||
name="disable"
|
||||
:value="formState"
|
||||
:items="[
|
||||
{
|
||||
key: 0,
|
||||
value: 0,
|
||||
title: '啟用',
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
value: 1,
|
||||
title: '停用',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #topLeft>狀態</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.disable }}
|
||||
</span></template
|
||||
>
|
||||
</RadioGroup>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,86 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, defineProps } from "vue";
|
||||
import { getProductSettingType } from "@/apis/productSetting";
|
||||
import ProductSettingTypeAddModal from "./ProductSettingTypeAddModal.vue";
|
||||
|
||||
const props = defineProps({
|
||||
onCancel: Function,
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{ title: "名稱", key: "product_name" },
|
||||
{ title: "顯示名稱", key: "operate_text" },
|
||||
{ title: "狀態", key: "disable" },
|
||||
{ title: "操作", key: "operation" },
|
||||
];
|
||||
|
||||
const data = ref([]);
|
||||
const getData = async () => {
|
||||
const res = await getProductSettingType();
|
||||
data.value = res.data.map((d) => ({ ...d, key: d.product_id }));
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
const editRecord = ref(null);
|
||||
const edit = (record) => {
|
||||
editRecord.value = record;
|
||||
record && openModal();
|
||||
};
|
||||
const openModal = () => {
|
||||
production_setting_PS3_Add_modal.showModal();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProductSettingTypeAddModal
|
||||
:editRecord="editRecord"
|
||||
:getData="getData"
|
||||
:updateEditRecord="edit"
|
||||
/>
|
||||
<Modal
|
||||
id="production_setting_PS3_modal"
|
||||
title="產品設定"
|
||||
:onCancel="onCancel"
|
||||
width="1000"
|
||||
>
|
||||
<template #modalContent>
|
||||
<Table :columns="columns" :dataSource="data" :withStyle="false">
|
||||
<template #beforeTable>
|
||||
<button
|
||||
class="btn btn-sm btn-success my-4"
|
||||
@click.stop.prevent="openModal"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
</button>
|
||||
</template>
|
||||
<template #bodyCell="{ record, column }">
|
||||
<template v-if="column.key === 'disable'">
|
||||
{{ record.disable === 0 ? "啟用" : "停用" }}
|
||||
</template>
|
||||
<template v-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => edit(record)"
|
||||
>
|
||||
修改
|
||||
</button></template
|
||||
>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
關閉
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,102 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, defineProps, inject, watch } from "vue";
|
||||
import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
// import { postBuildings } from "@/apis/building";
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
formState: Object,
|
||||
getData: Function,
|
||||
});
|
||||
|
||||
const buildScheme = yup.object({
|
||||
group: yup.string().required("必填"),
|
||||
product_code: yup.string().required("必填"),
|
||||
product_name: yup.string().required("必填"),
|
||||
upper_limit: yup.string().required("必填"),
|
||||
lower_limit: yup.string().required("必填"),
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
useFormErrorMessage(buildScheme);
|
||||
|
||||
const onCancel = () => {
|
||||
handleErrorReset();
|
||||
weight_loss_machine_modal.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const value = await handleSubmit(buildScheme, props.formState);
|
||||
// const res = await postBuildings(value);
|
||||
// if (res.isSuccess) {
|
||||
// props.getData();
|
||||
onCancel();
|
||||
// } else {
|
||||
// openToast("error", res.msg, "#weight_loss_machine_modal");
|
||||
// }
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
id="weight_loss_machine_modal"
|
||||
:title="props.formState?.product_code ? '修改檢重機設備' : '新增檢重機設備'"
|
||||
:onCancel="onCancel"
|
||||
:width="710"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="group">
|
||||
<template #topLeft>組別</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">{{ formErrorMsg.group }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
<Input :value="formState" class="my-2" name="product_code">
|
||||
<template #topLeft>成品代號</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">{{ formErrorMsg.product_code }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
<Input :value="formState" class="my-2" name="product_name">
|
||||
<template #topLeft>品名</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">{{ formErrorMsg.product_name }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
<Input :value="formState" class="my-2" name="upper_limit">
|
||||
<template #topLeft>上限</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">{{ formErrorMsg.upper_limit }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
<Input :value="formState" class="my-2" name="lower_limit">
|
||||
<template #topLeft>下限</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">{{ formErrorMsg.lower_limit }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,239 @@
|
||||
<script setup>
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
import WeightLossMachineSettingAddModal from "./WeightLossMachineSettingAddModal.vue";
|
||||
import { ref, onMounted } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "組別",
|
||||
key: "group",
|
||||
},
|
||||
{
|
||||
title: "成品代號",
|
||||
key: "product_code",
|
||||
},
|
||||
{
|
||||
title: "品名",
|
||||
key: "product_name",
|
||||
},
|
||||
{
|
||||
title: "上限",
|
||||
key: "upper_limit",
|
||||
},
|
||||
{
|
||||
title: "下限",
|
||||
key: "lower_limit",
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "operation",
|
||||
},
|
||||
];
|
||||
|
||||
const dataSource = ref([]);
|
||||
const updateTime = ref("");
|
||||
|
||||
const fake_data = [
|
||||
{
|
||||
group: 1,
|
||||
product_code: "113807",
|
||||
product_name: "58藜麥高粱醋590mL",
|
||||
upper_limit: 13.52,
|
||||
lower_limit: 12.61,
|
||||
},
|
||||
{
|
||||
group: 2,
|
||||
product_code: "111018",
|
||||
product_name: "陳年醋600mL(1*12)",
|
||||
upper_limit: 11.752,
|
||||
lower_limit: 10.961,
|
||||
},
|
||||
{
|
||||
group: 3,
|
||||
product_code: "121062N",
|
||||
product_name: "梅子醋600mL",
|
||||
upper_limit: 13.104,
|
||||
lower_limit: 12.222,
|
||||
},
|
||||
{
|
||||
group: 4,
|
||||
product_code: "111629B",
|
||||
product_name: "蘋果醋600mL",
|
||||
upper_limit: 13.104,
|
||||
lower_limit: 12.222,
|
||||
},
|
||||
{
|
||||
group: 5,
|
||||
product_code: "112404N",
|
||||
product_name: "一斤果醋-青蘋果醋600mL",
|
||||
upper_limit: 13.104,
|
||||
lower_limit: 12.222,
|
||||
},
|
||||
{
|
||||
group: 6,
|
||||
product_code: "112503N",
|
||||
product_name: "一斤果醋-蜂蜜蘋果醋600mL",
|
||||
upper_limit: 13.104,
|
||||
lower_limit: 12.222,
|
||||
},
|
||||
{
|
||||
group: 7,
|
||||
product_code: "113579",
|
||||
product_name: "蜂蜜蘋果醋600mL",
|
||||
upper_limit: 13.312,
|
||||
lower_limit: 12.416,
|
||||
},
|
||||
{
|
||||
group: 8,
|
||||
product_code: "113784",
|
||||
product_name: "紅石榴醋600毫升",
|
||||
upper_limit: 13.104,
|
||||
lower_limit: 12.222,
|
||||
},
|
||||
{
|
||||
group: 9,
|
||||
product_code: "211046-5",
|
||||
product_name: "一斤壽司醋280mL(1*12)",
|
||||
upper_limit: 6.76,
|
||||
lower_limit: 6.305,
|
||||
},
|
||||
{
|
||||
group: 10,
|
||||
product_code: "211046N",
|
||||
product_name: "百家珍壽司醋270ml",
|
||||
upper_limit: 6.24,
|
||||
lower_limit: 5.82,
|
||||
},
|
||||
{
|
||||
group: 11,
|
||||
product_code: "112268N",
|
||||
product_name: "一斤果醋-無糖蘋果醋600mL",
|
||||
upper_limit: 11.752,
|
||||
lower_limit: 10.961,
|
||||
},
|
||||
{
|
||||
group: 12,
|
||||
product_code: "112282N",
|
||||
product_name: "一斤果醋-低糖蘋果醋600mL",
|
||||
upper_limit: 11.96,
|
||||
lower_limit: 11.155,
|
||||
},
|
||||
{
|
||||
group: 13,
|
||||
product_code: "112442N",
|
||||
product_name: "一斤果醋-水蜜桃醋600mL",
|
||||
upper_limit: 13.104,
|
||||
lower_limit: 12.222,
|
||||
},
|
||||
{
|
||||
group: 14,
|
||||
product_code: "113722",
|
||||
product_name: "清心福全-蘋果醋600ml*6瓶",
|
||||
upper_limit: 6.552,
|
||||
lower_limit: 6.111,
|
||||
},
|
||||
{
|
||||
group: 15,
|
||||
product_code: "111933N",
|
||||
product_name: "蘆薈醋600ml",
|
||||
upper_limit: 13.104,
|
||||
lower_limit: 12.222,
|
||||
},
|
||||
];
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const getDataSource = async () => {
|
||||
loading.value = true;
|
||||
dataSource.value = fake_data;
|
||||
updateTime.value = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDataSource();
|
||||
});
|
||||
|
||||
const formState = ref({
|
||||
group: "",
|
||||
product_code: "",
|
||||
product_name: "",
|
||||
upper_limit: "",
|
||||
lower_limit: "",
|
||||
});
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record) {
|
||||
formState.value = record;
|
||||
} else {
|
||||
resetModalForm();
|
||||
}
|
||||
weight_loss_machine_modal.showModal();
|
||||
};
|
||||
|
||||
const resetModalForm = () => {
|
||||
formState.value = {
|
||||
group: "",
|
||||
product_code: "",
|
||||
product_name: "",
|
||||
upper_limit: "",
|
||||
lower_limit: "",
|
||||
};
|
||||
};
|
||||
|
||||
const removeAccount = async (id) => {
|
||||
// openToast("warning", "是否確認刪除該項目?", "body", async () => {
|
||||
// await cancelToastOpen();
|
||||
// const res = await delAccount(id);
|
||||
// if (res.isSuccess) {
|
||||
// getDataSource();
|
||||
// openToast("success", "刪除成功");
|
||||
// } else {
|
||||
// openToast("error", res.msg);
|
||||
// }
|
||||
// });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mb-3">
|
||||
<h3 class="text-xl mr-5">檢重機設備列表</h3>
|
||||
<WeightLossMachineSettingAddModal :getData="getDataSource" :formState="formState" />
|
||||
<button class="btn btn-sm btn-success mr-3" @click.stop.prevent="openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-info mr-3"
|
||||
@click.stop.prevent="getDataSource()"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'sync-alt']" />更新
|
||||
</button>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="dataSource" :loading="loading">
|
||||
<template #beforeTable>
|
||||
<p class="text-info text-xl pb-5">更新時間 : {{ updateTime }}</p>
|
||||
</template>
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
修改
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => removeAccount(record)"
|
||||
>
|
||||
刪除
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
Loading…
Reference in New Issue
Block a user