feat: rtsp 分頁串接 API (query-alarm-log)
This commit is contained in:
parent
d01d3e7581
commit
5ff030dc40
@ -24,4 +24,6 @@ export const GET_ALERT_SCHEDULE_LIST_API = `api/Alarm/GetAlarmSchedule`;
|
||||
export const POST_ALERT_SCHEDULE = `api/Alarm/SaveAlarmSchedule`;
|
||||
export const DELETE_ALERT_SCHEDULE = `api/Alarm/DeleteAlarmSchedule`;
|
||||
|
||||
export const POST_ALERT_MQTT_REFRESH = `api/Alarm/MQTTRefresh`;
|
||||
export const POST_ALERT_MQTT_REFRESH = `api/Alarm/MQTTRefresh`;
|
||||
|
||||
export const POST_QUERY_ALARM_LOG = `api/alarm/query-alarm-log`; // RTSP 分頁顯示告警 log
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
POST_ALERT_SCHEDULE,
|
||||
DELETE_ALERT_SCHEDULE,
|
||||
POST_ALERT_MQTT_REFRESH,
|
||||
POST_QUERY_ALARM_LOG,
|
||||
} from "./api";
|
||||
import instance from "@/util/request";
|
||||
import apihandler from "@/util/apihandler";
|
||||
@ -236,3 +237,24 @@ export const postMQTTRefresh = async () => {
|
||||
code: res.code,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查詢裝置告警紀錄(支援起訖日)
|
||||
* @param {{ id: number, start_date: string, end_date: string }} payload
|
||||
* - id: main_id(裝置/攝影機/樓層主鍵等)
|
||||
* - start_date: "YYYY-MM-DD"
|
||||
* - end_date: "YYYY-MM-DD"
|
||||
* @returns {Promise<any>} apihandler 標準回傳(成功時回 data 陣列)
|
||||
*/
|
||||
export const postQueryAlarmLog = async ({ id, start_date, end_date }) => {
|
||||
const res = await instance.post(POST_QUERY_ALARM_LOG, {
|
||||
id,
|
||||
start_date,
|
||||
end_date,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
code: res.code,
|
||||
});
|
||||
};
|
||||
|
@ -179,6 +179,7 @@
|
||||
"normal": "已复归",
|
||||
"unacked": "未确认",
|
||||
"acked": "已确认",
|
||||
"7days": "近7天",
|
||||
"30days": "近30天",
|
||||
"start_date": "起始日期",
|
||||
"end_date": "结束日期",
|
||||
@ -424,18 +425,9 @@
|
||||
},
|
||||
"rtsp": {
|
||||
"title": "影像串流",
|
||||
"start": "开始侦测",
|
||||
"stop": "结束侦测",
|
||||
|
||||
"selectPath": "选择存储位置",
|
||||
"selectDevice": "选择设备",
|
||||
"pleaseSelectDevice": "请先选择设备",
|
||||
"selectPathFirst": "请先选择存储文件夹",
|
||||
"startSuccess": "已开始侦测…",
|
||||
"startFail": "开始侦测失败,请稍后再试",
|
||||
"stopSuccess": "已请求结束侦测…",
|
||||
"stopFail": "结束侦测失败,请稍后再试",
|
||||
"noPermission": "未获得写入权限,请重新选择文件夹并授权",
|
||||
"selectFolderSuccess": "已选择文件夹:{name}",
|
||||
"selectFolderFail": "选择文件夹失败,请再试一次"
|
||||
"normalQuery":"已复归查询"
|
||||
}
|
||||
}
|
||||
|
@ -179,6 +179,7 @@
|
||||
"normal": "已復歸",
|
||||
"unacked": "未確認",
|
||||
"acked": "已確認",
|
||||
"7days": "近7天",
|
||||
"30days": "近30天",
|
||||
"start_date": "起始日期",
|
||||
"end_date": "結束日期",
|
||||
@ -424,18 +425,7 @@
|
||||
},
|
||||
"rtsp": {
|
||||
"title": "影像串流",
|
||||
"start": "開始偵測",
|
||||
"stop": "結束偵測",
|
||||
"selectPath": "選擇儲存位置",
|
||||
"selectDevice": "選擇設備",
|
||||
"pleaseSelectDevice": "請先選擇設備",
|
||||
"selectPathFirst": "請先選擇儲存資料夾",
|
||||
"startSuccess": "已開始偵測…",
|
||||
"startFail": "開始偵測失敗,請稍後再試",
|
||||
"stopSuccess": "結束偵測",
|
||||
"stopFail": "結束偵測失敗,請稍後再試",
|
||||
"noPermission": "沒有取得寫入權限,請重新選擇資料夾並允許",
|
||||
"selectFolderSuccess": "已選擇資料夾:{name}",
|
||||
"selectFolderFail": "選擇資料夾失敗,請再試一次"
|
||||
"normalQuery": "已復歸查詢"
|
||||
}
|
||||
}
|
||||
|
@ -179,6 +179,7 @@
|
||||
"normal": "Normal",
|
||||
"unacked": "Unacked",
|
||||
"acked": "Acked",
|
||||
"7days": "Last 7 Days",
|
||||
"30days": "Last 30 Days",
|
||||
"start_date": "Start Date",
|
||||
"end_date": "End Date",
|
||||
@ -424,18 +425,7 @@
|
||||
},
|
||||
"rtsp": {
|
||||
"title": "Video Stream",
|
||||
"start": "Start Detection",
|
||||
"stop": "Stop Detection",
|
||||
"selectPath": "Select Folder",
|
||||
"selectDevice": "Select Device",
|
||||
"pleaseSelectDevice": "Please select a device first",
|
||||
"selectPathFirst": "Please select a folder first",
|
||||
"startSuccess": "Detection started…",
|
||||
"startFail": "Failed to start detection, please try again later",
|
||||
"stopSuccess": "Detection stop requested…",
|
||||
"stopFail": "Failed to stop detection, please try again later",
|
||||
"noPermission": "No write permission. Please select a folder again and grant access",
|
||||
"selectFolderSuccess": "Folder selected: {name}",
|
||||
"selectFolderFail": "Failed to select folder, please try again"
|
||||
"normalQuery": "Query normal records"
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,55 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { ref, onMounted, watch, computed } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t, locale } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
|
||||
/**
|
||||
* 可調整 props:
|
||||
* - showQuickButton:是否顯示快捷按鈕(預設 true,可關掉)
|
||||
* - quickButtonType:快捷類型 '30d' | '7d'(預設 '30d')
|
||||
*/
|
||||
const props = defineProps({
|
||||
showQuickButton: { type: Boolean, default: true },
|
||||
quickButtonType: { type: String, default: "30d" }, // '30d' | '7d'
|
||||
});
|
||||
|
||||
// 統一設定區間的 helper
|
||||
const setRange = (start, end) => {
|
||||
const newRange = [
|
||||
{
|
||||
key: "start_at",
|
||||
value: dayjs(start),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: t("alert.start_date"),
|
||||
},
|
||||
{
|
||||
key: "end_at",
|
||||
value: dayjs(end),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: t("alert.end_date"),
|
||||
},
|
||||
];
|
||||
dateRange.value = newRange;
|
||||
|
||||
// 同步到共用查詢參數(字串格式)
|
||||
changeParams({
|
||||
...searchParams.value,
|
||||
Start_date: dayjs(start).format("YYYY-MM-DD"),
|
||||
End_date: dayjs(end).format("YYYY-MM-DD"),
|
||||
});
|
||||
};
|
||||
|
||||
// 初始 dateRange:若有既有搜尋值就吃既有;否則依 quickButtonType
|
||||
const dateRange = ref([
|
||||
{
|
||||
key: "start_at",
|
||||
value: searchParams.value.Start_date
|
||||
? dayjs(searchParams.value.Start_date)
|
||||
: props.quickButtonType === "7d"
|
||||
? dayjs().subtract(7, "day")
|
||||
: dayjs().subtract(30, "day"),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: t("alert.start_date"),
|
||||
@ -25,42 +64,28 @@ const dateRange = ref([
|
||||
},
|
||||
]);
|
||||
|
||||
// 快捷按鈕:依 quickButtonType 決定區間
|
||||
const changeTimeRange = () => {
|
||||
const newRange = [
|
||||
{
|
||||
key: "start_at",
|
||||
value: dayjs().subtract(30, "day"),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: t("alert.start_date"),
|
||||
},
|
||||
{
|
||||
key: "end_at",
|
||||
value: dayjs().endOf("day"),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: t("alert.end_date"),
|
||||
},
|
||||
];
|
||||
|
||||
dateRange.value = newRange;
|
||||
|
||||
changeParams({
|
||||
...searchParams.value,
|
||||
Start_date: newRange[0].value,
|
||||
End_date: newRange[1].value,
|
||||
});
|
||||
if (props.quickButtonType === "7d") {
|
||||
// 近7天:起始 = 今天往回 7 天,結束 = 今天
|
||||
setRange(dayjs().subtract(7, "day"), dayjs().endOf("day"));
|
||||
} else {
|
||||
// 近30天:起始 = 今天往回 30 天,結束 = 今天
|
||||
setRange(dayjs().subtract(30, "day"), dayjs().endOf("day"));
|
||||
}
|
||||
};
|
||||
|
||||
// 進頁面初始化:若沒有既有 Start/End,依 quickButtonType 設定
|
||||
onMounted(() => {
|
||||
// 初始化日期範圍
|
||||
if (
|
||||
!searchParams.value.Start_date ||
|
||||
!searchParams.value.End_date
|
||||
) {
|
||||
if (!searchParams.value.Start_date || !searchParams.value.End_date) {
|
||||
changeTimeRange();
|
||||
} else {
|
||||
// 若已存在既有值,把區間同步回 dateRange(避免格式落差)
|
||||
setRange(searchParams.value.Start_date, searchParams.value.End_date);
|
||||
}
|
||||
});
|
||||
|
||||
// 監聽搜尋變化
|
||||
// 使用者調整日期 → 同步到共用查詢參數
|
||||
watch(
|
||||
dateRange,
|
||||
() => {
|
||||
@ -70,24 +95,34 @@ watch(
|
||||
End_date: dayjs(dateRange.value[1].value).format("YYYY-MM-DD"),
|
||||
});
|
||||
},
|
||||
{ deep: true } // 確保在初始化立即觸發
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 語系切換時,更新 placeholder
|
||||
watch(locale, () => {
|
||||
dateRange.value[0].placeholder = t("alert.start_date");
|
||||
dateRange.value[1].placeholder = t("alert.end_date");
|
||||
});
|
||||
|
||||
// 快捷按鈕顯示文字
|
||||
const quickBtnLabel = computed(() =>
|
||||
props.quickButtonType === "7d" ? t("alert.7days") : t("alert.30days")
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-wrap items-center">
|
||||
<!-- 快捷按鈕:可關閉;可切換 30天 / 7天 -->
|
||||
<button
|
||||
v-if="showQuickButton"
|
||||
type="button"
|
||||
class="btn btn-outline-success mr-3"
|
||||
@click="changeTimeRange"
|
||||
>
|
||||
{{ $t("alert.30days") }}
|
||||
{{ quickBtnLabel }}
|
||||
</button>
|
||||
|
||||
<!-- 日期群組 -->
|
||||
<DateGroup :items="dateRange" :withLine="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,102 +1,29 @@
|
||||
<template>
|
||||
<section class="min-h-[600px] h-screen">
|
||||
<h1 class="text-2xl font-extrabold mb-2">{{ $t("rtsp.title") }}</h1>
|
||||
|
||||
<!-- Tabs:選擇攝影機 -->
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<h2 class="text-lg font-bold whitespace-nowrap">
|
||||
{{ $t("rtsp.selectDevice") }} :
|
||||
</h2>
|
||||
|
||||
<ButtonConnectedGroup
|
||||
:items="items"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveBtn(item);
|
||||
const found = rtspDevices.find((r) => r.main_id === item.key);
|
||||
if (found) selectDevice(found);
|
||||
}
|
||||
"
|
||||
:className="`flex flex-wrap`"
|
||||
size="sm"
|
||||
color="info"
|
||||
>
|
||||
<template #buttonContent="{ item }">
|
||||
<span class="text-base">{{ item.title }}</span>
|
||||
</template>
|
||||
</ButtonConnectedGroup>
|
||||
</div>
|
||||
|
||||
<div class="flex h-[70%] gap-4">
|
||||
<!-- 左側:即時監控 -->
|
||||
<div class="relative w-full flex-1 rounded border overflow-hidden">
|
||||
<iframe
|
||||
:src="monitorUrl"
|
||||
class="absolute inset-0 w-full h-full"
|
||||
allow="autoplay; fullscreen; picture-in-picture"
|
||||
referrerpolicy="no-referrer"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<!-- 右側:開始/結束偵測 -->
|
||||
<aside class="w-1/2 flex flex-col gap-6 p-4">
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
class="btn btn-add w-40"
|
||||
@click="startDetection"
|
||||
:disabled="
|
||||
isStarting || !selectedMainId || !currentDevice?.start_btn_enable
|
||||
"
|
||||
:title="!selectedMainId ? $t('rtsp.pleaseSelectDevice') : ''"
|
||||
>
|
||||
{{ $t("rtsp.start") }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn btn-error text-white w-40"
|
||||
@click="stopDetection"
|
||||
:disabled="
|
||||
isStopping || !selectedMainId || !currentDevice?.stop_btn_enable
|
||||
"
|
||||
:title="!selectedMainId ? $t('rtsp.pleaseSelectDevice') : ''"
|
||||
>
|
||||
{{ $t("rtsp.stop") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 顯示後端傳來的提示文字 -->
|
||||
<p v-if="currentDevice?.alarm_message" class="text-sm text-info">
|
||||
{{ currentDevice.alarm_message }}
|
||||
</p>
|
||||
|
||||
<p v-if="message" class="text-sm">{{ message }}</p>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getRtspDevices, setRtspEnable } from "@/apis/rtsp";
|
||||
import { getRtspDevices } from "@/apis/rtsp";
|
||||
import { postQueryAlarmLog } from "@/apis/alert";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import AlertSearchTimeRange from "@/views/alert/components/AlertQuery/AlertSearchTimeRange.vue";
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
|
||||
const DEFAULT_MONITOR_URL =
|
||||
"http://192.168.0.219:8026/?url=rtsp://admin02:mjmAdmin_99@192.168.0.200:554/stream1";
|
||||
|
||||
export default {
|
||||
name: "Rtsp",
|
||||
components: { Table, AlertSearchTimeRange },
|
||||
setup() {
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
return { items, changeActiveBtn, setItems, selectedBtn };
|
||||
const { searchParams } = useSearchParam();
|
||||
return { items, changeActiveBtn, setItems, selectedBtn, searchParams };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
monitorUrl: DEFAULT_MONITOR_URL,
|
||||
isStarting: false,
|
||||
isStopping: false,
|
||||
message: "",
|
||||
rtspDevices: [],
|
||||
selectedMainId: null, // 目前選中的設備 main_id
|
||||
pollTimer: null, // ← 用於記錄輪詢計時器
|
||||
selectedMainId: null,
|
||||
tableLoading: false,
|
||||
dataSource: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -106,13 +33,21 @@ export default {
|
||||
this.rtspDevices.find((d) => d.main_id === this.selectedMainId) || null
|
||||
);
|
||||
},
|
||||
columns() {
|
||||
return [
|
||||
{ key: "id", title: this.$t("alert.uuid") || "UUID" },
|
||||
{ key: "created_at", title: this.$t("alert.time") || "Time" },
|
||||
{ key: "reason", title: this.$t("alert.error_msg") || "Reason" },
|
||||
];
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadRtspDevices();
|
||||
},
|
||||
beforeUnmount() {
|
||||
// 離開頁面時確實清掉輪詢
|
||||
this.stopPolling();
|
||||
// ★ 設定近 7 天並立即查一次
|
||||
this.setLast7Days();
|
||||
if (this.selectedMainId) {
|
||||
await this.searchLogs();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedBtn: {
|
||||
@ -125,6 +60,22 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 工具:轉成 YYYY-MM-DD(考慮時區)
|
||||
toISO(d) {
|
||||
return new Date(d.getTime() - d.getTimezoneOffset() * 60000)
|
||||
.toISOString()
|
||||
.slice(0, 10);
|
||||
},
|
||||
// ★ 將查詢區間設為「近 7 天」
|
||||
setLast7Days() {
|
||||
const today = new Date();
|
||||
const start = new Date();
|
||||
// 與你在 AlertSearchTimeRange 的 7d 快捷一致:往回 7 天到今天
|
||||
start.setDate(start.getDate() - 7);
|
||||
this.searchParams.Start_date = this.toISO(start);
|
||||
this.searchParams.End_date = this.toISO(today);
|
||||
},
|
||||
|
||||
async loadRtspDevices() {
|
||||
try {
|
||||
const res = await getRtspDevices({});
|
||||
@ -166,82 +117,137 @@ export default {
|
||||
selectDevice(d) {
|
||||
this.selectedMainId = d.main_id;
|
||||
this.monitorUrl = d.rtsp_url || DEFAULT_MONITOR_URL;
|
||||
this.dataSource = [];
|
||||
},
|
||||
|
||||
// === 輪詢控制 ===
|
||||
startPolling() {
|
||||
// 若已在輪詢就不重複啟動
|
||||
if (this.pollTimer) return;
|
||||
// 查詢告警紀錄(讀共用的 searchParams)
|
||||
async searchLogs() {
|
||||
if (!this.selectedMainId) return;
|
||||
const start = this.searchParams?.Start_date;
|
||||
const end = this.searchParams?.End_date;
|
||||
if (!start || !end) return;
|
||||
|
||||
const tick = async () => {
|
||||
const keepId = this.selectedMainId;
|
||||
await this.loadRtspDevices();
|
||||
// 保持目前選中的設備不變(若仍存在)
|
||||
const found = this.rtspDevices.find((d) => d.main_id === keepId);
|
||||
if (found) this.selectDevice(found);
|
||||
};
|
||||
|
||||
// 立即跑一次,之後每 5 秒跑一次
|
||||
tick();
|
||||
this.pollTimer = setInterval(tick, 5000);
|
||||
},
|
||||
|
||||
stopPolling() {
|
||||
if (this.pollTimer) {
|
||||
clearInterval(this.pollTimer);
|
||||
this.pollTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
// 開始偵測
|
||||
async startDetection() {
|
||||
if (!this.selectedMainId) {
|
||||
this.message = this.$t("rtsp.pleaseSelectDevice");
|
||||
return;
|
||||
}
|
||||
this.isStarting = true;
|
||||
const keepId = this.selectedMainId;
|
||||
try {
|
||||
await setRtspEnable({ main_id: keepId, enable: true });
|
||||
await this.loadRtspDevices();
|
||||
const found = this.rtspDevices.find((d) => d.main_id === keepId);
|
||||
if (found) this.selectDevice(found);
|
||||
this.message = this.$t("rtsp.startSuccess");
|
||||
|
||||
// ★ 啟動後開始每 5 秒抓一次
|
||||
this.startPolling();
|
||||
this.tableLoading = true;
|
||||
const res = await postQueryAlarmLog({
|
||||
id: this.selectedMainId,
|
||||
start_date: start,
|
||||
end_date: end,
|
||||
});
|
||||
const rows = Array.isArray(res) ? res : res?.data || [];
|
||||
this.dataSource = rows.map((d) => ({
|
||||
...d,
|
||||
key: d.id,
|
||||
// 後端目前未回傳 formId,保留欄位供未來擴充
|
||||
formId: d.formId,
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.message = this.$t("rtsp.startFail");
|
||||
console.error("searchLogs() 失敗", e);
|
||||
this.dataSource = [];
|
||||
} finally {
|
||||
this.isStarting = false;
|
||||
this.tableLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 結束偵測
|
||||
async stopDetection() {
|
||||
if (!this.selectedMainId) {
|
||||
this.message = this.$t("rtsp.pleaseSelectDevice");
|
||||
return;
|
||||
}
|
||||
this.isStopping = true;
|
||||
const keepId = this.selectedMainId;
|
||||
try {
|
||||
await setRtspEnable({ main_id: keepId, enable: false });
|
||||
await this.loadRtspDevices();
|
||||
const found = this.rtspDevices.find((d) => d.main_id === keepId);
|
||||
if (found) this.selectDevice(found);
|
||||
this.message = this.$t("rtsp.stopSuccess");
|
||||
|
||||
// ★ 停止後結束輪詢
|
||||
this.stopPolling();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.message = this.$t("rtsp.stopFail");
|
||||
} finally {
|
||||
this.isStopping = false;
|
||||
}
|
||||
onRepairOrder(record) {
|
||||
console.log("repair order click:", record);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="min-h-[600px]">
|
||||
<h1 class="text-2xl font-extrabold mb-2">{{ $t("rtsp.title") }}</h1>
|
||||
|
||||
<!-- Tabs:選擇攝影機 -->
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<h2 class="text-lg font-bold whitespace-nowrap">
|
||||
{{ $t("rtsp.selectDevice") }} :
|
||||
</h2>
|
||||
|
||||
<ButtonConnectedGroup
|
||||
:items="items"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActiveBtn(item);
|
||||
const found = rtspDevices.find((r) => r.main_id === item.key);
|
||||
if (found) selectDevice(found);
|
||||
}
|
||||
"
|
||||
:className="`flex flex-wrap`"
|
||||
size="sm"
|
||||
color="info"
|
||||
>
|
||||
<template #buttonContent="{ item }">
|
||||
<span class="text-base">{{ item.title }}</span>
|
||||
</template>
|
||||
</ButtonConnectedGroup>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 items-start">
|
||||
<!-- 左側:即時監控 -->
|
||||
<div
|
||||
class="relative w-full flex-1 rounded border overflow-hidden h-[420px] md:h-[520px]"
|
||||
>
|
||||
<iframe
|
||||
:src="monitorUrl"
|
||||
class="absolute inset-0 w-full h-full"
|
||||
allow="autoplay; fullscreen; picture-in-picture"
|
||||
referrerpolicy="no-referrer"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<!-- 右側:查詢告警紀錄(日期 + 表格) -->
|
||||
<aside class="w-1/2 flex flex-col gap-4 px-4">
|
||||
<div
|
||||
class="w-full flex flex-wrap items-end gap-3 custom-border px-4 pt-3 pb-4"
|
||||
>
|
||||
<div class="w-full flex flex-wrap items-center justify-start gap-4">
|
||||
<h2 class="text-lg font-bold">{{ $t("rtsp.normalQuery") }} :</h2>
|
||||
|
||||
<!-- 選擇開始與結束時間 -->
|
||||
<AlertSearchTimeRange quickButtonType="7d" />
|
||||
<button
|
||||
class="btn btn-search"
|
||||
:disabled="
|
||||
!selectedMainId ||
|
||||
!searchParams?.Start_date ||
|
||||
!searchParams?.End_date ||
|
||||
tableLoading
|
||||
"
|
||||
:title="!selectedMainId ? $t('rtsp.pleaseSelectDevice') : ''"
|
||||
@click.stop.prevent="searchLogs"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'search']" class="text-lg" />
|
||||
{{ $t("button.search") || "Search" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格(交由 Table 內建分頁處理,給全量 dataSource) -->
|
||||
<Table
|
||||
:loading="tableLoading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
rowKey="key"
|
||||
>
|
||||
<template #bodyCell="{ record, column }">
|
||||
<template v-if="column.key === 'repairOrder'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white whitespace-nowrap"
|
||||
@click.stop.prevent="onRepairOrder(record)"
|
||||
>
|
||||
<span v-if="record.formId">{{ record.formId }}</span>
|
||||
<span v-else>
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />
|
||||
{{ $t("alert.repair_order_number") || "Repair Order" }}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user