feat: 车站状态页面的交互权限
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Station, StationAlarms, StationDevices } from '@/apis';
|
import type { Station, StationAlarms, StationDevices } from '@/apis';
|
||||||
import { DEVICE_TYPE_LITERALS } from '@/enums';
|
import { usePermission } from '@/composables';
|
||||||
|
import { DEVICE_TYPE_LITERALS, PERMISSION_TYPE_LITERALS } from '@/enums';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { isFunction } from 'es-toolkit';
|
import { isFunction } from 'es-toolkit';
|
||||||
@@ -24,6 +25,8 @@ const emit = defineEmits<{
|
|||||||
clickConfig: [station: Station];
|
clickConfig: [station: Station];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
|
||||||
const { station, devices, alarms, selectable } = toRefs(props);
|
const { station, devices, alarms, selectable } = toRefs(props);
|
||||||
|
|
||||||
const onlineDeviceCount = computed(() => {
|
const onlineDeviceCount = computed(() => {
|
||||||
@@ -71,7 +74,7 @@ const openDeviceConfigModal = () => {
|
|||||||
emit('clickConfig', station.value);
|
emit('clickConfig', station.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dropdownOptions: DropdownOption[] = [
|
const dropdownOptions = computed<DropdownOption[]>(() => [
|
||||||
{
|
{
|
||||||
label: '视频平台',
|
label: '视频平台',
|
||||||
key: 'video-platform',
|
key: 'video-platform',
|
||||||
@@ -80,9 +83,10 @@ const dropdownOptions: DropdownOption[] = [
|
|||||||
{
|
{
|
||||||
label: '设备配置',
|
label: '设备配置',
|
||||||
key: 'device-config',
|
key: 'device-config',
|
||||||
|
show: hasPermission(station.value.code, PERMISSION_TYPE_LITERALS.OPERATION),
|
||||||
onSelect: openDeviceConfigModal,
|
onSelect: openDeviceConfigModal,
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
const onSelectDropdownOption = (key: string, option: DropdownOption) => {
|
const onSelectDropdownOption = (key: string, option: DropdownOption) => {
|
||||||
const onSelect = option['onSelect'];
|
const onSelect = option['onSelect'];
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export * from './alarm';
|
|||||||
export * from './device';
|
export * from './device';
|
||||||
export * from './permission';
|
export * from './permission';
|
||||||
export * from './query';
|
export * from './query';
|
||||||
|
export * from './station';
|
||||||
export * from './stomp';
|
export * from './stomp';
|
||||||
|
|||||||
1
src/composables/station/index.ts
Normal file
1
src/composables/station/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './use-batch-actions';
|
||||||
120
src/composables/station/use-batch-actions.ts
Normal file
120
src/composables/station/use-batch-actions.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { usePermission } from '../permission';
|
||||||
|
import { type Station } from '@/apis';
|
||||||
|
import { PERMISSION_TYPE_LITERALS, type PermissionType } from '@/enums';
|
||||||
|
import { computed, ref, watch, type Ref } from 'vue';
|
||||||
|
|
||||||
|
type BatchActionKey = 'export-icmp' | 'export-record' | 'sync-camera' | 'sync-nvr';
|
||||||
|
|
||||||
|
type BatchAction = {
|
||||||
|
label: string;
|
||||||
|
key: BatchActionKey;
|
||||||
|
permission: PermissionType;
|
||||||
|
active: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBatchActions = (stations: Ref<Station[]>, abortController?: Ref<AbortController | undefined>) => {
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
|
||||||
|
const batchActions = ref<BatchAction[]>([
|
||||||
|
{
|
||||||
|
label: '导出设备状态',
|
||||||
|
key: 'export-icmp',
|
||||||
|
permission: PERMISSION_TYPE_LITERALS.VIEW,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '导出录像诊断',
|
||||||
|
key: 'export-record',
|
||||||
|
permission: PERMISSION_TYPE_LITERALS.VIEW,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '同步摄像机',
|
||||||
|
key: 'sync-camera',
|
||||||
|
permission: PERMISSION_TYPE_LITERALS.OPERATION,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '同步录像机通道',
|
||||||
|
key: 'sync-nvr',
|
||||||
|
permission: PERMISSION_TYPE_LITERALS.OPERATION,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const selectedAction = computed(() => {
|
||||||
|
return batchActions.value.find((action) => action.active);
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectableStations = computed(() => {
|
||||||
|
if (!selectedAction.value) return [];
|
||||||
|
const result: Station[] = [];
|
||||||
|
if (selectedAction.value.permission === PERMISSION_TYPE_LITERALS.VIEW) {
|
||||||
|
result.push(...stations.value.filter((station) => hasPermission(station.code, PERMISSION_TYPE_LITERALS.VIEW)));
|
||||||
|
}
|
||||||
|
if (selectedAction.value.permission === PERMISSION_TYPE_LITERALS.OPERATION) {
|
||||||
|
result.push(...stations.value.filter((station) => hasPermission(station.code, PERMISSION_TYPE_LITERALS.OPERATION)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
const stationSelection = ref<Record<Station['code'], boolean>>({});
|
||||||
|
|
||||||
|
const toggleSelectAction = (action: BatchAction) => {
|
||||||
|
batchActions.value.forEach((batchAction) => {
|
||||||
|
if (batchAction.key === action.key) {
|
||||||
|
if (batchAction.active) stationSelection.value = {};
|
||||||
|
batchAction.active = !batchAction.active;
|
||||||
|
} else {
|
||||||
|
batchAction.active = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelectAllStations = (checked: boolean) => {
|
||||||
|
if (!checked) {
|
||||||
|
stationSelection.value = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectableStations.value.forEach((station) => {
|
||||||
|
stationSelection.value[station.code] = checked;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(selectedAction, () => {
|
||||||
|
toggleSelectAllStations(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmAction = (callbacks: Record<BatchActionKey, () => void>) => {
|
||||||
|
const { key } = selectedAction.value ?? {};
|
||||||
|
if (!key) return;
|
||||||
|
const noStationSeleted = !Object.values(stationSelection.value).some((selected) => selected);
|
||||||
|
if (noStationSeleted) {
|
||||||
|
window.$message.warning('请选择车站');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callbacks[key]();
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelAction = () => {
|
||||||
|
abortController?.value?.abort();
|
||||||
|
stationSelection.value = {};
|
||||||
|
batchActions.value.forEach((batchAction) => {
|
||||||
|
batchAction.active = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
batchActions,
|
||||||
|
selectedAction,
|
||||||
|
|
||||||
|
selectableStations,
|
||||||
|
stationSelection,
|
||||||
|
|
||||||
|
toggleSelectAction,
|
||||||
|
toggleSelectAllStations,
|
||||||
|
|
||||||
|
confirmAction,
|
||||||
|
cancelAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,177 +1,145 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { initStationAlarms, initStationDevices, syncCameraApi, syncNvrChannelsApi, type Station } from '@/apis';
|
import { initStationAlarms, initStationDevices, syncCameraApi, syncNvrChannelsApi, type Station } from '@/apis';
|
||||||
import { AlarmDetailModal, DeviceDetailModal, DeviceParamConfigModal, IcmpExportModal, RecordCheckExportModal, StationCard, type StationCardProps } from '@/components';
|
import { AlarmDetailModal, DeviceDetailModal, DeviceParamConfigModal, IcmpExportModal, RecordCheckExportModal, StationCard, type StationCardProps } from '@/components';
|
||||||
import { useLineDevicesQuery } from '@/composables';
|
import { useBatchActions, useLineDevicesQuery } from '@/composables';
|
||||||
import { useAlarmStore, useDeviceStore, useSettingStore, useStationStore } from '@/stores';
|
import { useAlarmStore, useDeviceStore, usePermissionStore, useSettingStore } from '@/stores';
|
||||||
import { parseErrorFeedback } from '@/utils';
|
|
||||||
import { useMutation } from '@tanstack/vue-query';
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
|
import { isCancel } from 'axios';
|
||||||
import { NButton, NButtonGroup, NCheckbox, NFlex, NGrid, NGridItem, NScrollbar } from 'naive-ui';
|
import { NButton, NButtonGroup, NCheckbox, NFlex, NGrid, NGridItem, NScrollbar } from 'naive-ui';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const { stationGridCols: stationGridColumns } = storeToRefs(settingStore);
|
const { stationGridCols: stationGridColumns } = storeToRefs(settingStore);
|
||||||
const stationStore = useStationStore();
|
|
||||||
const { stations } = storeToRefs(stationStore);
|
|
||||||
const deviceStore = useDeviceStore();
|
const deviceStore = useDeviceStore();
|
||||||
const { lineDevices } = storeToRefs(deviceStore);
|
const { lineDevices } = storeToRefs(deviceStore);
|
||||||
const alarmStore = useAlarmStore();
|
const alarmStore = useAlarmStore();
|
||||||
const { lineAlarms } = storeToRefs(alarmStore);
|
const { lineAlarms } = storeToRefs(alarmStore);
|
||||||
|
const permissionStore = usePermissionStore();
|
||||||
|
|
||||||
const { refetch: refetchLineDevicesQuery } = useLineDevicesQuery();
|
const stations = computed(() => permissionStore.stations.VIEW ?? []);
|
||||||
|
|
||||||
// 操作栏
|
|
||||||
// 当点击操作栏中的一个按钮时,其他按钮会被禁用
|
|
||||||
type Action = 'export-icmp' | 'export-record' | 'sync-camera' | 'sync-nvr' | null;
|
|
||||||
const selectedAction = ref<Action>(null);
|
|
||||||
const showOperation = ref(false);
|
|
||||||
const stationSelectable = ref(false);
|
|
||||||
const stationSelection = ref<Record<Station['code'], boolean>>({});
|
|
||||||
const showIcmpExportModal = ref(false);
|
const showIcmpExportModal = ref(false);
|
||||||
const showRecordCheckExportModal = ref(false);
|
const showRecordCheckExportModal = ref(false);
|
||||||
|
|
||||||
const onToggleSelectAll = (checked: boolean) => {
|
const abortController = ref(new AbortController());
|
||||||
if (!checked) {
|
|
||||||
stationSelection.value = {};
|
|
||||||
} else {
|
|
||||||
stationSelection.value = Object.fromEntries(stations.value.map((station) => [station.code, true]));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onAction = (action: Action) => {
|
const { batchActions, selectedAction, selectableStations, stationSelection, toggleSelectAction, toggleSelectAllStations, confirmAction, cancelAction } = useBatchActions(stations, abortController);
|
||||||
selectedAction.value = action;
|
|
||||||
if (action === null) return;
|
|
||||||
showOperation.value = true;
|
|
||||||
stationSelectable.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
const { refetch: refetchLineDevicesQuery } = useLineDevicesQuery();
|
||||||
selectedAction.value = null;
|
|
||||||
showOperation.value = false;
|
|
||||||
stationSelectable.value = false;
|
|
||||||
stationSelection.value = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFinish = onCancel;
|
|
||||||
|
|
||||||
const { mutate: syncCamera, isPending: cameraSyncing } = useMutation({
|
const { mutate: syncCamera, isPending: cameraSyncing } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
|
abortController.value.abort();
|
||||||
|
abortController.value = new AbortController();
|
||||||
|
|
||||||
|
const signal = abortController.value.signal;
|
||||||
|
|
||||||
const stationCodes = Object.entries(stationSelection.value)
|
const stationCodes = Object.entries(stationSelection.value)
|
||||||
.filter(([, selected]) => selected)
|
.filter(([, selected]) => selected)
|
||||||
.map(([code]) => code);
|
.map(([code]) => code);
|
||||||
const results = await Promise.allSettled(stationCodes.map((stationCode) => syncCameraApi({ stationCode })));
|
const requests = await Promise.allSettled(stationCodes.map((stationCode) => syncCameraApi({ stationCode, signal })));
|
||||||
return results.map((result, index) => ({ ...result, stationCode: stationCodes[index] }));
|
return requests.map((result, index) => ({ ...result, stationCode: stationCodes[index] }));
|
||||||
},
|
},
|
||||||
onSuccess: (results) => {
|
onSuccess: (requests) => {
|
||||||
const successCount = results.filter((result) => result.status === 'fulfilled').length;
|
type PromiseRequest = (typeof requests)[number];
|
||||||
const failures = results.filter((result) => result.status === 'rejected');
|
const successRequests: PromiseRequest[] = [];
|
||||||
const failureCount = failures.length;
|
const failedRequests: PromiseRequest[] = [];
|
||||||
if (failureCount > 0) {
|
const canceledRequests: PromiseRequest[] = [];
|
||||||
const failedStations = failures.map((f) => stations.value.find((s) => s.code === f.stationCode)?.name).join('、');
|
for (const request of requests) {
|
||||||
if (successCount === 0) {
|
if (request.status === 'fulfilled') {
|
||||||
window.$message.error('摄像机同步全部失败');
|
successRequests.push(request);
|
||||||
window.$message.error(`${failedStations}`);
|
} else if (isCancel(request.reason)) {
|
||||||
|
canceledRequests.push(request);
|
||||||
} else {
|
} else {
|
||||||
window.$message.warning(`摄像机同步完成:成功${successCount}个车站,失败${failureCount}个车站`);
|
failedRequests.push(request);
|
||||||
window.$message.warning(`${failedStations}`);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
window.$message.success('摄像机同步成功');
|
|
||||||
}
|
}
|
||||||
if (successCount > 0) {
|
const notices: string[] = [`成功 ${successRequests.length} 个车站`, `失败 ${failedRequests.length} 个车站`];
|
||||||
// 摄像机同步后,需要重新查询一次设备,待测试
|
if (canceledRequests.length > 0) notices.push(`取消 ${canceledRequests.length} 个车站`);
|
||||||
|
window.$notification.info({
|
||||||
|
title: '摄像机同步结果',
|
||||||
|
content: notices.join(','),
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
if (successRequests.length > 0) {
|
||||||
|
// 摄像机同步后,需要重新查询一次设备
|
||||||
refetchLineDevicesQuery();
|
refetchLineDevicesQuery();
|
||||||
}
|
}
|
||||||
onFinish();
|
cancelAction();
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.error(error);
|
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
|
||||||
window.$message.error(errorFeedback);
|
|
||||||
onCancel();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutate: syncNvrChannels, isPending: nvrChannelsSyncing } = useMutation({
|
const { mutate: syncNvrChannels, isPending: nvrChannelsSyncing } = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
|
abortController.value.abort();
|
||||||
|
abortController.value = new AbortController();
|
||||||
|
|
||||||
|
const signal = abortController.value.signal;
|
||||||
|
|
||||||
const stationCodes = Object.entries(stationSelection.value)
|
const stationCodes = Object.entries(stationSelection.value)
|
||||||
.filter(([, selected]) => selected)
|
.filter(([, selected]) => selected)
|
||||||
.map(([code]) => code);
|
.map(([code]) => code);
|
||||||
const results = await Promise.allSettled(stationCodes.map((stationCode) => syncNvrChannelsApi({ stationCode })));
|
const requests = await Promise.allSettled(stationCodes.map((stationCode) => syncNvrChannelsApi({ stationCode, signal })));
|
||||||
return results.map((result, index) => ({ ...result, stationCode: stationCodes[index] }));
|
return requests.map((result, index) => ({ ...result, stationCode: stationCodes[index] }));
|
||||||
},
|
},
|
||||||
onSuccess: (results) => {
|
onSuccess: (requests) => {
|
||||||
const successCount = results.filter((result) => result.status === 'fulfilled').length;
|
type PromiseRequest = (typeof requests)[number];
|
||||||
const failures = results.filter((result) => result.status === 'rejected');
|
const successRequests: PromiseRequest[] = [];
|
||||||
const failureCount = failures.length;
|
const failedRequests: PromiseRequest[] = [];
|
||||||
if (failureCount > 0) {
|
const canceledRequests: PromiseRequest[] = [];
|
||||||
const failedStations = failures.map((failure) => stations.value.find((station) => station.code === failure.stationCode)?.name).join('、');
|
for (const request of requests) {
|
||||||
if (successCount === 0) {
|
if (request.status === 'fulfilled') {
|
||||||
window.$message.error('录像机通道同步全部失败');
|
successRequests.push(request);
|
||||||
window.$message.error(`${failedStations}`);
|
} else if (isCancel(request.reason)) {
|
||||||
|
canceledRequests.push(request);
|
||||||
} else {
|
} else {
|
||||||
window.$message.warning(`录像机通道同步完成:成功${successCount}个车站,失败${failureCount}个车站`);
|
failedRequests.push(request);
|
||||||
window.$message.warning(`${failedStations}`);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
window.$message.success('录像机通道同步成功');
|
|
||||||
}
|
}
|
||||||
onFinish();
|
const notices: string[] = [`成功 ${successRequests.length} 个车站`, `失败 ${failedRequests.length} 个车站`];
|
||||||
},
|
if (canceledRequests.length > 0) notices.push(`取消 ${canceledRequests.length} 个车站`);
|
||||||
onError: (error) => {
|
window.$notification.info({
|
||||||
console.error(error);
|
title: '录像机通道同步结果',
|
||||||
const errorFeedback = parseErrorFeedback(error);
|
content: notices.join(','),
|
||||||
window.$message.error(errorFeedback);
|
duration: 3000,
|
||||||
onCancel();
|
});
|
||||||
|
cancelAction();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const confirming = computed(() => cameraSyncing.value || nvrChannelsSyncing.value);
|
const confirming = computed(() => cameraSyncing.value || nvrChannelsSyncing.value);
|
||||||
|
|
||||||
const onConfirm = async () => {
|
const onClickConfirmAction = () => {
|
||||||
const noStationSelected = !Object.values(stationSelection.value).some((selected) => selected);
|
confirmAction({
|
||||||
if (selectedAction.value === 'export-icmp') {
|
'export-icmp': () => {
|
||||||
if (noStationSelected) {
|
|
||||||
window.$message.warning('请选择要导出设备状态的车站');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showIcmpExportModal.value = true;
|
showIcmpExportModal.value = true;
|
||||||
} else if (selectedAction.value === 'export-record') {
|
},
|
||||||
if (noStationSelected) {
|
'export-record': () => {
|
||||||
window.$message.warning('请选择要导出录像诊断的车站');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showRecordCheckExportModal.value = true;
|
showRecordCheckExportModal.value = true;
|
||||||
} else if (selectedAction.value === 'sync-camera') {
|
},
|
||||||
if (noStationSelected) {
|
'sync-camera': () => {
|
||||||
window.$message.warning('请选择要同步摄像机的车站');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
syncCamera();
|
syncCamera();
|
||||||
} else if (selectedAction.value === 'sync-nvr') {
|
},
|
||||||
if (noStationSelected) {
|
'sync-nvr': () => {
|
||||||
window.$message.warning('请选择要同步录像机通道的车站');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
syncNvrChannels();
|
syncNvrChannels();
|
||||||
} else {
|
},
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 车站卡片的事件
|
// 车站卡片的事件
|
||||||
const selectedStation = ref<Station>();
|
const modalStation = ref<Station>();
|
||||||
const showDeviceParamConfigModal = ref(false);
|
const showDeviceParamConfigModal = ref(false);
|
||||||
const showDeviceDetailModal = ref(false);
|
const showDeviceDetailModal = ref(false);
|
||||||
const showAlarmDetailModal = ref(false);
|
const showAlarmDetailModal = ref(false);
|
||||||
|
|
||||||
const onClickConfig: StationCardProps['onClickConfig'] = (station) => {
|
const onClickConfig: StationCardProps['onClickConfig'] = (station) => {
|
||||||
selectedStation.value = station;
|
modalStation.value = station;
|
||||||
showDeviceParamConfigModal.value = true;
|
showDeviceParamConfigModal.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickDetail: StationCardProps['onClickDetail'] = (type, station) => {
|
const onClickDetail: StationCardProps['onClickDetail'] = (type, station) => {
|
||||||
selectedStation.value = station;
|
modalStation.value = station;
|
||||||
if (type === 'device') {
|
if (type === 'device') {
|
||||||
showDeviceDetailModal.value = true;
|
showDeviceDetailModal.value = true;
|
||||||
}
|
}
|
||||||
@@ -186,15 +154,14 @@ const onClickDetail: StationCardProps['onClickDetail'] = (type, station) => {
|
|||||||
<!-- 工具栏 -->
|
<!-- 工具栏 -->
|
||||||
<NFlex align="center" style="padding: 8px 8px 0 8px">
|
<NFlex align="center" style="padding: 8px 8px 0 8px">
|
||||||
<NButtonGroup>
|
<NButtonGroup>
|
||||||
<NButton secondary :focusable="false" :disabled="!!selectedAction && selectedAction !== 'export-icmp'" @click="() => onAction('export-icmp')">导出设备状态</NButton>
|
<template v-for="batchAction in batchActions" :key="batchAction.key">
|
||||||
<NButton secondary :focusable="false" :disabled="!!selectedAction && selectedAction !== 'export-record'" @click="() => onAction('export-record')">导出录像诊断</NButton>
|
<NButton :secondary="!batchAction.active" :focusable="false" @click="() => toggleSelectAction(batchAction)">{{ batchAction.label }}</NButton>
|
||||||
<NButton secondary :focusable="false" :disabled="!!selectedAction && selectedAction !== 'sync-camera'" @click="() => onAction('sync-camera')">同步摄像机</NButton>
|
</template>
|
||||||
<NButton secondary :focusable="false" :disabled="!!selectedAction && selectedAction !== 'sync-nvr'" @click="() => onAction('sync-nvr')">同步录像机通道</NButton>
|
|
||||||
</NButtonGroup>
|
</NButtonGroup>
|
||||||
<template v-if="showOperation">
|
<template v-if="selectedAction">
|
||||||
<NCheckbox label="全选" @update:checked="onToggleSelectAll" />
|
<NCheckbox label="全选" :checked="selectableStations.length === Object.keys(stationSelection).length" @update:checked="toggleSelectAllStations" />
|
||||||
<NButton tertiary size="small" type="primary" :focusable="false" :loading="confirming" @click="onConfirm">确定</NButton>
|
<NButton tertiary size="small" type="primary" :focusable="false" :loading="confirming" @click="onClickConfirmAction">确定</NButton>
|
||||||
<NButton tertiary size="small" type="tertiary" :focusable="false" :disabled="confirming" @click="onCancel">取消</NButton>
|
<NButton tertiary size="small" type="tertiary" :focusable="false" @click="cancelAction">取消</NButton>
|
||||||
</template>
|
</template>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
|
|
||||||
@@ -205,7 +172,7 @@ const onClickDetail: StationCardProps['onClickDetail'] = (type, station) => {
|
|||||||
:station="station"
|
:station="station"
|
||||||
:devices="lineDevices[station.code] ?? initStationDevices()"
|
:devices="lineDevices[station.code] ?? initStationDevices()"
|
||||||
:alarms="lineAlarms[station.code] ?? initStationAlarms()"
|
:alarms="lineAlarms[station.code] ?? initStationAlarms()"
|
||||||
:selectable="stationSelectable"
|
:selectable="!!selectableStations.find((selectable) => selectable.code === station.code)"
|
||||||
v-model:selected="stationSelection[station.code]"
|
v-model:selected="stationSelection[station.code]"
|
||||||
@click-detail="onClickDetail"
|
@click-detail="onClickDetail"
|
||||||
@click-config="onClickConfig"
|
@click-config="onClickConfig"
|
||||||
@@ -214,12 +181,12 @@ const onClickDetail: StationCardProps['onClickDetail'] = (type, station) => {
|
|||||||
</NGrid>
|
</NGrid>
|
||||||
</NScrollbar>
|
</NScrollbar>
|
||||||
|
|
||||||
<IcmpExportModal v-model:show="showIcmpExportModal" :stations="stations.filter((station) => stationSelection[station.code])" @after-leave="onFinish" />
|
<IcmpExportModal v-model:show="showIcmpExportModal" :stations="stations.filter((station) => stationSelection[station.code])" @after-leave="cancelAction" />
|
||||||
<RecordCheckExportModal v-model:show="showRecordCheckExportModal" :stations="stations.filter((station) => stationSelection[station.code])" @after-leave="onFinish" />
|
<RecordCheckExportModal v-model:show="showRecordCheckExportModal" :stations="stations.filter((station) => stationSelection[station.code])" @after-leave="cancelAction" />
|
||||||
|
|
||||||
<DeviceParamConfigModal v-model:show="showDeviceParamConfigModal" :station="selectedStation" />
|
<DeviceParamConfigModal v-model:show="showDeviceParamConfigModal" :station="modalStation" />
|
||||||
<DeviceDetailModal v-model:show="showDeviceDetailModal" :station="selectedStation" />
|
<DeviceDetailModal v-model:show="showDeviceDetailModal" :station="modalStation" />
|
||||||
<AlarmDetailModal v-model:show="showAlarmDetailModal" :station="selectedStation" />
|
<AlarmDetailModal v-model:show="showAlarmDetailModal" :station="modalStation" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|||||||
Reference in New Issue
Block a user