首頁:小卡串資料、今日生產目標串api | 即時告警:預設全選並搜尋

This commit is contained in:
koko 2025-08-08 14:32:29 +08:00
parent 49560b3dd5
commit f4d26271a4
14 changed files with 336 additions and 246 deletions

View File

@ -7,4 +7,5 @@ export const GET_DASHBOARD_ROOM_TEMP_API = `/SituationRoom/GetFormulaRoomStatusD
export const GET_DASHBOARD_ENERGY_API = `/SituationRoom/GetEnergeData`; export const GET_DASHBOARD_ENERGY_API = `/SituationRoom/GetEnergeData`;
export const POST_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/SetTargetSetting`; export const POST_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/SetTargetSetting`;
export const GET_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/GetTargetSetting` export const GET_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/GetTargetSetting`
export const GET_DASHBOARD_PRODUCT_HISTORY_API = `/SituationRoom/GetProductionHistory` export const GET_DASHBOARD_PRODUCT_HISTORY_API = `/SituationRoom/GetProductionHistory`;
export const GET_DASHBOARD_PRODUCT_TASK_API = `/SituationRoom/GetProductionTask`;

View File

@ -9,6 +9,7 @@ import {
POST_DASHBOARD_PRODUCT_TARGET_SETTING_API, POST_DASHBOARD_PRODUCT_TARGET_SETTING_API,
GET_DASHBOARD_PRODUCT_TARGET_SETTING_API, GET_DASHBOARD_PRODUCT_TARGET_SETTING_API,
GET_DASHBOARD_PRODUCT_HISTORY_API, GET_DASHBOARD_PRODUCT_HISTORY_API,
GET_DASHBOARD_PRODUCT_TASK_API
} from "./api"; } from "./api";
import instance from "@/util/request"; import instance from "@/util/request";
import apihandler from "@/util/apihandler"; import apihandler from "@/util/apihandler";
@ -147,3 +148,12 @@ export const getDashboardProductRecord = async ({ start_time, end_time }) => {
}); });
}; };
export const getDashboardProductTask = async () => {
const res = await instance.get(GET_DASHBOARD_PRODUCT_TASK_API);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
}

View File

@ -151,11 +151,28 @@ const openModal = async (record) => {
} }
}; };
watch(searchParams, (newValue) => { watch(
if (newValue.system_tag) { () => ({
isAck: searchParams.value.isAck,
isRecover: searchParams.value.isRecover,
Start_date: searchParams.value.start_created_at,
End_date: searchParams.value.end_created_at,
system_tag: searchParams.value.system_tag,
}),
(val) => {
//
if (
val.isAck !== undefined &&
val.isRecover !== undefined &&
val.Start_date &&
val.End_date &&
val.system_tag
) {
search(); search();
} }
}); },
{ immediate: true, deep: true }
);
provide("alert_modal", { model_data, search, updateEditRecord }); provide("alert_modal", { model_data, search, updateEditRecord });
provide("alert_table", { openModal, updateEditRecord, dataSource, search, tableLoading }); provide("alert_table", { openModal, updateEditRecord, dataSource, search, tableLoading });

View File

@ -11,7 +11,7 @@ const changeCheckedItem = () => {
if (checkedItem.value.length === store.subSys.length) { if (checkedItem.value.length === store.subSys.length) {
changeParams({ changeParams({
...searchParams.value, ...searchParams.value,
system_tag: [], system_tag: [store.subSys[0]?.main_system_tag+`_`+store.subSys[0]?.sub_system_tag],
}); });
} else { } else {
changeParams({ changeParams({
@ -53,7 +53,7 @@ watch(searchParams, (newValue) => {
if (!newValue.system_tag) { if (!newValue.system_tag) {
changeParams({ changeParams({
...newValue, ...newValue,
system_tag: [store.subSys[0]?.main_system_tag+`_`+store.subSys[0]?.sub_system_tag], system_tag: store.subSys.map(({ main_system_tag, sub_system_tag }) => main_system_tag+`_`+sub_system_tag),
}); });
} }
}); });

View File

@ -53,18 +53,6 @@ const vesselsData = computed(() => {
return data.map((vessel) => ({ return data.map((vessel) => ({
...vessel, ...vessel,
activeTab: getDeviceActiveTab(vessel.name), activeTab: getDeviceActiveTab(vessel.name),
SIP: [
{
count: "1",
startTime: "08:00",
endTime: "08:30",
},
{
count: "2",
startTime: "14:00",
endTime: "14:30",
},
],
})); }));
}); });
@ -84,12 +72,12 @@ const cookingPotData = computed(() => {
// //
const refrigerationData = computed(() => { const refrigerationData = computed(() => {
return props.realTimeData?.refrigerationData[0] || {}; return props.realTimeData?.refrigerationData || [];
}); });
// //
const meterData = computed(() => { const meterData = computed(() => {
const data = props.realTimeData?.refrigerationData || []; const data = props.realTimeData?.meterData || [];
return data.sort((a, b) => a.name.localeCompare(b.name)); return data.sort((a, b) => a.name.localeCompare(b.name));
}); });
@ -117,7 +105,7 @@ const valveData = computed(() => {
v-for="(vessel, index) in vesselsData" v-for="(vessel, index) in vesselsData"
:key="index" :key="index"
:vessel="vessel" :vessel="vessel"
:tabs="tabs" :tabs="tabs.filter(tab => tab.label !== '品檢')"
/> />
</div> </div>
</template> </template>

View File

@ -1,92 +1,35 @@
<script setup> <script setup>
import { ref, onMounted, computed } from "vue"; import { ref, onMounted, onUnmounted, computed } from "vue";
import { getDashboardProductTask } from "@/apis/dashboard";
import useActiveBtn from "@/hooks/useActiveBtn"; import useActiveBtn from "@/hooks/useActiveBtn";
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn(); const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
// const formulaData = ref([]);
const recipe_data = ref([]); const productData = ref([]);
recipe_data.value = [ const loading = ref(false);
{
id: 1,
productName: "蘋果醋",
status: "調理中",
completionRate: 0.7, // 70%
details: [
{ 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: "梅子醋",
status: "尚未生產",
completionRate: 0,
details: [],
},
{
id: 3,
productName: "桑葚醋",
status: "尚未生產",
completionRate: 0,
details: [],
},
];
// let timer = null;
const finished_data = ref([]);
finished_data.value = [ const fetchProductTask = async () => {
{ loading.value = true;
id: 1, try {
productName: "111018", const res = await getDashboardProductTask();
status: "已完成", formulaData.value = res?.data?.formula || [];
completionRate: 1, // 100% productData.value = res?.data?.product || [];
details: [ } catch (e) {
{ pot: "第1鍋", location: "包裝室", status: "生產中" }, formulaData.value = [];
{ pot: "第2鍋", location: "充填室", status: "生產中" }, productData.value = [];
// { pot: "3", location: "調", status: "" }, } finally {
// { pot: "4", location: "調", status: "" }, loading.value = false;
// { pot: "5", location: "調", status: "" }, }
// { pot: "6", location: "調", status: "" }, };
// {
// pot: "7",
// location: "-\n-",
// status: "",
// },
// { pot: "8", location: "", status: "" },
],
},
{
id: 2,
productName: "121062N",
status: "尚未生產",
completionRate: 0,
details: [],
},
{
id: 3,
productName: "113579",
status: "尚未生產",
completionRate: 0,
details: [],
},
];
const production_data = computed(() => { const production_data = computed(() => {
if (selectedBtn.value?.key === 1) { if (selectedBtn.value?.key === 1) {
return recipe_data.value; // return formulaData.value;
} else if (selectedBtn.value?.key === 2) { } else if (selectedBtn.value?.key === 2) {
return finished_data.value; // return productData.value;
} }
return []; return [];
}); });
@ -116,6 +59,17 @@ onMounted(() => {
active: false, active: false,
}, },
]); ]);
fetchProductTask();
timer = setInterval(() => {
fetchProductTask();
}, 60 * 1000);
});
onUnmounted(() => {
if (timer) {
clearInterval(timer);
timer = null;
}
}); });
</script> </script>
@ -144,7 +98,7 @@ onMounted(() => {
/> />
</template> </template>
<template v-else> <template v-else>
<span class="text-base ">{{ selectedProduct.productName }}</span> <p class="text-base my-4">{{ selectedProduct.name }}</p>
</template> </template>
</div> </div>
<div className="h-60 overflow-x-auto"> <div className="h-60 overflow-x-auto">
@ -154,27 +108,35 @@ onMounted(() => {
<th>品名</th> <th>品名</th>
<th>狀態</th> <th>狀態</th>
<th>生產進度</th> <th>生產進度</th>
<th>功能</th> <th v-if="selectedBtn?.key === 1">功能</th>
</tr> </tr>
</thead> </thead>
<thead v-else> <thead v-else>
<tr> <tr>
<th>鍋次</th> <th
<th>位置</th> v-if="
selectedProduct.details &&
selectedProduct.details.length &&
selectedBtn?.key === 1
"
>
鍋次
</th>
<th>狀態</th> <th>狀態</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- 產品列表 --> <!-- 產品列表 -->
<template v-if="!selectedProduct"> <template v-if="!selectedProduct">
<tr v-for="item in production_data" :key="item.id"> <tr v-for="item in production_data" :key="item.name">
<td>{{ item.productName }}</td> <td>{{ item.name }}</td>
<td>{{ item.status }}</td> <td>{{ item.status }}</td>
<td>{{ (item.completionRate * 100).toFixed(0) }}%</td> <td>{{ item.progress }}</td>
<td> <td v-if="item.details && item.details.length">
<button <button
class="btn btn-xs btn-outline-info" class="btn btn-xs btn-outline-info"
@click="showDetails(item)" @click="showDetails(item)"
:disabled="!item.details || item.details.length === 0"
> >
詳細 詳細
</button> </button>
@ -183,14 +145,21 @@ onMounted(() => {
</template> </template>
<!-- 詳細資料 --> <!-- 詳細資料 -->
<template v-else> <template v-else>
<tr v-for="(detail, index) in selectedProduct.details" :key="index"> <tr
<td>{{ detail.pot }}</td> v-if="selectedProduct.details && selectedProduct.details.length"
<td>{{ detail.location }}</td> v-for="(detail, index) in selectedProduct.details"
:key="index"
>
<td v-if="selectedBtn?.key === 1">{{ detail.batch }}</td>
<td>{{ detail.status }}</td> <td>{{ detail.status }}</td>
</tr> </tr>
<tr v-else>
<td colspan="3" class="text-center">無詳細資料</td>
</tr>
</template> </template>
</tbody> </tbody>
</table> </table>
<div v-if="loading" class="text-center mt-2">載入中...</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,5 +1,6 @@
<script setup> <script setup>
defineProps({ data: Array }); defineProps({ data: Array });
import { twMerge } from "tailwind-merge";
</script> </script>
<template> <template>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
@ -8,50 +9,62 @@ defineProps({ data: Array });
:key="index" :key="index"
class="stats shadow bg-slate-200" class="stats shadow bg-slate-200"
> >
<div class="stat p-2"> <div class="w-24 stat p-2">
<div class="bg-green-600 rounded-lg p-2 shadow-lg shadow-slate-400"> <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 text-slate-100">{{ item.batch || "__" }}</div>
<div class="text-sm font-bold text-slate-100">{{ item.product }}</div> <div class="text-sm font-bold text-slate-100">
{{ item.product || "&nbsp;" }}
</div> </div>
</div> </div>
<div class="stat p-2"> </div>
<div class="text-sm font-bold text-slate-800">{{ item.equipment }}</div> <div class="w-26 stat items-center p-2">
<div class="text-base font-bold text-slate-800">{{ item.name }}</div>
<div class="text-sm text-slate-800">{{ item.status }}</div> <div class="text-sm text-slate-800">{{ item.status }}</div>
</div> </div>
<div class="stat text-center p-2"> <div class="w-15 stat items-center text-center p-2">
<div class="text-2xl text-slate-800"> <FontAwesomeIcon
<FontAwesomeIcon :icon="['fas', 'temperature-high']" /> class="text-2xl text-slate-800 mx-auto"
</div> :icon="['fas', 'temperature-high']"
/>
<div class="text-sm text-slate-800">{{ item.temperature }}</div> <div class="text-sm text-slate-800">{{ item.temperature }}</div>
</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 <div
:class="[ :class="[
'text-2xl', 'w-15 stat items-center text-center p-2',
item.sugarStatus == 'warning' ? 'text-red-700' : 'text-slate-800', item.sterilization == '殺菌完成'
? 'text-green-700'
: 'text-slate-800',
]" ]"
> >
<FontAwesomeIcon :icon="['fas', 'tint']" /> <FontAwesomeIcon :icon="['far', 'clock']" class="text-2xl mx-auto" />
<div class="text-sm">
{{ item.sterilization }}
</div> </div>
<div :class="['text-sm',
item.sugarStatus == 'warning' ? 'text-red-700' : 'text-slate-800',
]">{{ item.sugar }}</div>
</div> </div>
<div class="stat text-center p-2"> <div
<div :class="`text-2xl ${item.materialColor}`"> :class="[
<FontAwesomeIcon :icon="['fas', 'circle']" class="border-2 border-slate-400 rounded-full"/> 'w-15 stat items-center text-center p-2',
typeof item.sugar !== 'number' ? 'text-red-700' : 'text-slate-800',
]"
>
<FontAwesomeIcon class="text-2xl mx-auto" :icon="['fas', 'tint']" />
<div class="text-sm">
{{ item.sugar }}
</div> </div>
<div class="text-sm text-slate-800">{{ item.material }}</div> </div>
<div class="w-15 stat items-center text-center p-2">
<div>
<FontAwesomeIcon
:icon="['fas', 'circle']"
:class="
twMerge(
'text-2xl border-2 border-slate-400 rounded-full',
item.qcResult ? item.qcResult.trim() : 'text-slate-400'
)
"
/>
</div>
<div class="text-sm text-slate-800">{{ item.qcName }}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
const props = defineProps({ const props = defineProps({
pot: Object, pot: Object,
tabs: Array tabs: Array,
}); });
</script> </script>
<template> <template>
@ -29,41 +29,105 @@ const props = defineProps({
<div class="p-0"> <div class="p-0">
<div v-if="pot.activeTab2.value === '生產資訊'"> <div v-if="pot.activeTab2.value === '生產資訊'">
<ul class="leading-7 tracking-wider text-slate-700 px-2"> <ul class="leading-7 tracking-wider text-slate-700 px-2">
<li><b>品名:</b> {{ pot.product }}</li> <li><b>品名:</b> {{ pot.productInfo?.product }}</li>
<li><b>鍋次:</b> {{ pot.batch }}</li> <li><b>鍋次:</b> {{ pot.productInfo?.batch || "__" }}</li>
<li><b>溫度:</b> {{ pot.temperature }}</li> <li><b>溫度:</b> {{ pot.productInfo?.temperature }}</li>
<li><b>糖度:</b> {{ pot.sugar }}</li> <li><b>糖度:</b> {{ pot.productInfo?.sugar }}</li>
<li><b>狀態:</b> {{ pot.status }}</li> <li><b>狀態:</b> {{ pot.productInfo?.status }}</li>
</ul> </ul>
</div> </div>
<div v-else-if="pot.activeTab2.value === '投料進度'">
<div class="h-40 overflow-x-auto">
<table class="table table-sm table-pin-rows">
<thead>
<tr class="border-0 bg-slate-300 text-slate-800">
<th class="w-10">順序</th>
<th>動作</th>
<th>原料</th>
<th class="w-20">狀態</th>
</tr>
</thead>
<tbody>
<tr
v-for="(feed, index) in pot.feedProgress"
:key="index"
class="border-0"
>
<th>{{ feed.order }}</th>
<td>{{ feed.action }}</td>
<td>{{ feed.material }}</td>
<td>{{ feed.state }}</td>
</tr>
<tr
v-if="!pot.feedProgress || !pot.feedProgress.length"
class="h-28 border-0"
>
<td colspan="4" class="text-center">無投料進度資料</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-else-if="pot.activeTab2.value === '品檢'"> <div v-else-if="pot.activeTab2.value === '品檢'">
<ul class="leading-7 tracking-wider text-slate-700 px-2"> <div class="h-40 overflow-x-auto">
<li><b>品檢時間:</b> {{ pot.inspectionTime }}</li> <table class="table table-sm table-pin-rows whitespace-nowrap">
<li><b>風味:</b> {{ pot.flavor }}</li> <thead>
<li><b>鹽度:</b> {{ pot.salinity }}</li> <tr class="border-0 bg-slate-300 text-slate-800">
<li><b>酸度:</b> {{ pot.acidity }}</li> <th>狀態</th>
</ul> <th>品檢時間</th>
<th>風味</th>
<th>色澤</th>
<th>雜異物</th>
<th>鹽度</th>
<th>酸度</th>
<th>糖度</th>
</tr>
</thead>
<tbody>
<tr
v-for="(flow, index) in pot.qcResult"
:key="index"
class="border-0"
>
<th>{{ flow.state }}</th>
<td>{{ flow.time }}</td>
<td>{{ flow.flavor }}</td>
<td>{{ flow.color }}</td>
<td>{{ flow.impurities }}</td>
<td>{{ flow.actual_Salinity }}</td>
<td>{{ flow.actual_PH }}</td>
<td>{{ flow.actual_Brix }}</td>
</tr>
<tr v-if="!pot.qcResult || !pot.qcResult.length" class="h-28 border-0">
<td colspan="8" class="text-center">無品檢資料</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
<div v-else-if="pot.activeTab2.value === '流量計'"> <div v-else-if="pot.activeTab2.value === '流量計'">
<div class=""> <div class="h-40 overflow-x-auto">
<table class="table table-sm whitespace-nowrap"> <table class="table table-sm">
<thead> <thead>
<tr class="border-0 bg-slate-300 text-slate-800"> <tr class="border-0 bg-slate-300 text-slate-800">
<th></th> <th></th>
<th>當前使用量</th>
<th>今日累積量</th> <th>今日累積量</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr class="border-0"> <tr
<th class="">補水</th> v-for="(flow, index) in pot.flowMeterData"
<td>{{ pot.waterConsumption }}</td> :key="index"
<td>100L</td> class="border-0"
>
<th class="">{{ flow.material }}</th>
<td>{{ flow.total }}</td>
</tr> </tr>
<tr class="border-0"> <tr
<th>補醋</th> v-if="!pot.flowMeterData || !pot.flowMeterData.length"
<td>{{ pot.aceticAcidConsumption }}</td> class="h-28 border-0"
<td>100L</td> >
<td colspan="2" class="text-center">無流量計資料</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -80,26 +144,19 @@ const props = defineProps({
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr class="border-0"> <tr
<th> v-for="(value, index) in pot.sip"
第一次 :key="index"
</th> class="border-0">
<td>2025-06-01T08:00:00</td> <th>{{ value.count }}</th>
<td>2025-06-01T08:30:00</td> <td>{{ value.startTime }}</td>
<td>{{ value.endTime }}</td>
</tr> </tr>
<tr class="border-0"> <tr
<th> v-if="!pot.sip || !pot.sip.length"
第二次 class="h-28 border-0"
</th> >
<td>2025-06-01T09:00:00</td> <td colspan="3" class="text-center">無SIP資料</td>
<td>2025-06-01T09:30:00</td>
</tr>
<tr class="border-0">
<th>
第二次
</th>
<td>2025-06-01T10:00:00</td>
<td>2025-06-01T10:30:00</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
defineProps({ data: Array }) defineProps({ data: Array });
</script> </script>
<template> <template>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
@ -8,14 +8,15 @@ defineProps({ data: Array })
:key="index" :key="index"
class="stats shadow bg-slate-200" class="stats shadow bg-slate-200"
> >
<div class="stat p-2"> <div class="w-26 stat items-center p-2">
<div class="text-sm font-bold text-slate-800">{{ item.equipment }}</div> <div class="text-sm font-bold text-slate-800">{{ item.name }}</div>
<div class="text-sm text-slate-800"></div> <div class="text-sm text-slate-800">&nbsp;</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>
<div class="w-15 stat items-center text-center p-2">
<FontAwesomeIcon
:icon="['fas', 'temperature-high']"
class="text-2xl text-slate-800 mx-auto"
/>
<div class="text-sm text-slate-800">{{ item.temperature }}</div> <div class="text-sm text-slate-800">{{ item.temperature }}</div>
</div> </div>
</div> </div>

View File

@ -8,20 +8,21 @@ defineProps({ data: Array });
:key="index" :key="index"
class="stats shadow bg-slate-200" class="stats shadow bg-slate-200"
> >
<div class="stat p-2"> <div class="w-24 stat p-2">
<div class="bg-green-600 rounded-lg p-2 shadow-lg shadow-slate-400"> <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 text-slate-100">{{ item.batch }}</div>
<div class="text-sm font-bold text-slate-100">{{ item.product }}</div> <div class="text-sm font-bold text-slate-100">{{ item.product }}</div>
</div> </div>
</div> </div>
<div class="stat p-2"> <div class="w-26 stat items-center p-2">
<div class="text-sm font-bold text-slate-800">{{ item.equipment }}</div> <div class="text-base font-bold text-slate-800">{{ item.name }}</div>
<div class="text-sm text-slate-800">{{ item.status }}</div> <div class="text-sm text-slate-800">{{ item.status }}</div>
</div> </div>
<div class="stat p-2"> <div class="w-15 stat items-center p-2">
<div class="text-2xl text-slate-800"> <FontAwesomeIcon
<FontAwesomeIcon :icon="['fas', 'temperature-high']" /> :icon="['fas', 'temperature-high']"
</div> class="text-2xl text-slate-800 mx-auto"
/>
<div class="text-sm text-slate-800">{{ item.temperature }}</div> <div class="text-sm text-slate-800">{{ item.temperature }}</div>
</div> </div>
</div> </div>

View File

@ -1,11 +1,17 @@
<script setup> <script setup>
defineProps({ data: Object }); const props = defineProps({ data: { type: Array, default: () => [] } });
</script> </script>
<template> <template>
<div class="stats shadow bg-slate-200"> <div class="flex flex-col gap-3">
<template v-if="props.data.length">
<div
v-for="(item, index) in props.data"
:key="index"
class="stats shadow bg-slate-200"
>
<div class="stat p-2"> <div class="stat p-2">
<div class="text-sm font-bold text-slate-800"> <div class="text-sm font-bold text-slate-800">
{{ data.name }} {{ item.name }}
</div> </div>
</div> </div>
<div class="stat text-center p-2"> <div class="stat text-center p-2">
@ -13,8 +19,13 @@ defineProps({ data: Object });
<FontAwesomeIcon :icon="['fas', 'temperature-high']" /> <FontAwesomeIcon :icon="['fas', 'temperature-high']" />
</div> </div>
<div class="text-sm text-slate-800"> <div class="text-sm text-slate-800">
{{ data.temperature }} {{ item.temperature }}
</div> </div>
</div> </div>
</div> </div>
</template>
<template v-else>
<div class="text-center text-slate-400 py-4">無冷藏室資料</div>
</template>
</div>
</template> </template>

View File

@ -10,17 +10,15 @@ import { twMerge } from "tailwind-merge";
</div> </div>
</div> </div>
<div v-for="item in card.groups" :key="item.code" class="stat p-2"> <div v-for="item in card.groups" :key="item.code" class="stat p-2">
<div class="text-2xl">
<FontAwesomeIcon <FontAwesomeIcon
:icon="['fas', 'circle']" :icon="['fas', 'circle']"
:class=" :class="
twMerge( twMerge(
'border-2 border-slate-400 rounded-full', 'text-2xl border-2 border-slate-400 rounded-full',
item.status == 'ON' ? 'text-green-500' : 'text-gray-400' item.status ? item.status.trim() : 'text-slate-400'
) )
" "
/> />
</div>
<span class="text-sm text-slate-800">{{ item.code }}</span> <span class="text-sm text-slate-800">{{ item.code }}</span>
</div> </div>
</div> </div>

View File

@ -7,7 +7,7 @@ const props = defineProps({
<template> <template>
<div class="card bg-slate-200 text-accent-content rounded-md w-[25rem]"> <div class="card bg-slate-200 text-accent-content rounded-md w-[25rem]">
<div class="card-body p-3"> <div class="card-body p-3">
<h2 class="card-title">{{ vessel.name }}</h2> <h2 class="card-title">{{ vessel?.name }}</h2>
<div <div
role="tablist" role="tablist"
class="tabs tabs-boxed tabs-sm bg-opacity-50 shadow-inner shadow-slate-600" class="tabs tabs-boxed tabs-sm bg-opacity-50 shadow-inner shadow-slate-600"
@ -29,20 +29,21 @@ const props = defineProps({
<div class="p-0"> <div class="p-0">
<div v-if="vessel.activeTab.value === '生產資訊'"> <div v-if="vessel.activeTab.value === '生產資訊'">
<ul class="leading-7 tracking-wider text-slate-700 px-2"> <ul class="leading-7 tracking-wider text-slate-700 px-2">
<li><b>品名:</b> {{ vessel.product }}</li> <li><b>品名:</b> {{ vessel.productInfo?.product }}</li>
<li><b>鍋次:</b> {{ vessel.batch }}</li> <li><b>鍋次:</b> {{ vessel.productInfo?.batch || "__" }}</li>
<li><b>狀態:</b> {{ vessel.status }}</li> <li><b>溫度:</b> {{ vessel.productInfo?.temperature }}</li>
<li><b>狀態:</b> {{ vessel.productInfo?.status }}</li>
</ul> </ul>
</div> </div>
<div v-else-if="vessel.activeTab.value === '投料進度'"> <div v-else-if="vessel.activeTab.value === '投料進度'">
<div class="overflow-x-auto"> <div class="h-40 overflow-x-auto">
<table class="table table-sm"> <table class="table table-sm table-pin-rows">
<thead> <thead>
<tr class="border-0 bg-slate-300 text-slate-800"> <tr class="border-0 bg-slate-300 text-slate-800">
<th>順序</th> <th class="w-10">順序</th>
<th>動作</th> <th>動作</th>
<th>原料</th> <th>原料</th>
<th>狀態</th> <th class="w-20">狀態</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -56,17 +57,22 @@ const props = defineProps({
<td>{{ feed.material }}</td> <td>{{ feed.material }}</td>
<td>{{ feed.state }}</td> <td>{{ feed.state }}</td>
</tr> </tr>
<tr
v-if="!vessel.feedProgress || !vessel.feedProgress.length"
class="h-28 border-0"
>
<td colspan="4" class="text-center">無投料進度資料</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div v-else-if="vessel.activeTab.value === '流量計'"> <div v-else-if="vessel.activeTab.value === '流量計'">
<div class="overflow-x-auto"> <div class="h-40 overflow-x-auto">
<table class="table table-sm"> <table class="table table-sm table-pin-rows ">
<thead> <thead>
<tr class="border-0 bg-slate-300 text-slate-800"> <tr class="border-0 bg-slate-300 text-slate-800">
<th></th> <th></th>
<th>當前使用量</th>
<th>今日累積量</th> <th>今日累積量</th>
</tr> </tr>
</thead> </thead>
@ -76,17 +82,22 @@ const props = defineProps({
:key="index" :key="index"
class="border-0" class="border-0"
> >
<th>{{ flow.material }}</th> <th class="w-26">{{ flow.material }}</th>
<td>{{ flow.current }}</td>
<td>{{ flow.total }}</td> <td>{{ flow.total }}</td>
</tr> </tr>
<tr
v-if="!vessel.flowMeterData || !vessel.flowMeterData.length"
class="h-28 border-0"
>
<td colspan="2" class="text-center">無流量計資料</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div v-else-if="vessel.activeTab.value === 'SIP'"> <div v-else-if="vessel.activeTab.value === 'SIP'">
<div class="overflow-x-auto"> <div class="h-40 overflow-x-auto">
<table class="table table-sm"> <table class="table table-sm table-pin-rows">
<thead> <thead>
<tr class="border-0 bg-slate-300 text-slate-800"> <tr class="border-0 bg-slate-300 text-slate-800">
<th>次數</th> <th>次數</th>
@ -96,14 +107,20 @@ const props = defineProps({
</thead> </thead>
<tbody> <tbody>
<tr <tr
v-for="(value, index) in vessel.SIP" v-for="(value, index) in vessel.sip"
:key="index" :key="index"
class="border-0" class="border-0"
> >
<th >{{ value.count }}</th> <th>{{ value.count }}</th>
<td>{{ value.startTime }}</td> <td>{{ value.startTime }}</td>
<td>{{ value.endTime }}</td> <td>{{ value.endTime }}</td>
</tr> </tr>
<tr
v-if="!vessel.sip || !vessel.sip.length"
class="h-28 border-0"
>
<td colspan="3" class="text-center">無SIP資料</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -4,6 +4,13 @@ module.exports = {
"./node_modules/vue-tailwind-datepicker/**/*.js", "./node_modules/vue-tailwind-datepicker/**/*.js",
], ],
purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
safelist: [
'text-white',
'text-green-600',
'text-yellow-300',
'text-red-700',
'text-slate-400'
],
darkMode: false, // or 'media' or 'class' darkMode: false, // or 'media' or 'class'
theme: { theme: {
extend: { extend: {