|
|
@@ -0,0 +1,1140 @@
|
|
|
+<template>
|
|
|
+ <div class="elderly-management-system large-screen">
|
|
|
+ <div class="cyber-bg"></div>
|
|
|
+ <div class="cyber-grid"></div>
|
|
|
+
|
|
|
+ <div class="my-container">
|
|
|
+ <!-- 顶部信息栏 -->
|
|
|
+ <TopInfoBar :tenant-name="getTenantName()" />
|
|
|
+
|
|
|
+ <!-- 统计信息卡片 -->
|
|
|
+ <StatsCard :stats="largeScreenStats" @stat-card-click="handleStatCardClick" />
|
|
|
+
|
|
|
+ <!-- 主内容区域 -->
|
|
|
+ <div v-if="!showAllDevices" class="main-content-large">
|
|
|
+ <!-- 左侧老人列表 -->
|
|
|
+ <ElderlyList
|
|
|
+ :elderly-list="elderlyList"
|
|
|
+ :selected-elderly-id="selectedElderly.id"
|
|
|
+ :warning-flags="warningFlags"
|
|
|
+ @select-elderly="selectElderly"
|
|
|
+ @add-device="openAddDeviceFromList"
|
|
|
+ @handle-warning="openHandleWarningDialog"
|
|
|
+ @add-elder="openAddElderDialog"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 右侧详情区域 -->
|
|
|
+ <DetailSection
|
|
|
+ :selected-elderly="selectedElderly.id > 0 ? selectedElderly : null"
|
|
|
+ :device-type-options="deviceTypeOptions"
|
|
|
+ @add-device="openAddDeviceDialog"
|
|
|
+ @show-device-detail="showDeviceDetail"
|
|
|
+ @remove-device="removeDevice"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 全部设备视图(组件) -->
|
|
|
+ <AllDevicesView
|
|
|
+ v-else
|
|
|
+ :devices="allDevices"
|
|
|
+ @refresh="refreshAllDevices"
|
|
|
+ @back="backToOverview"
|
|
|
+ @show-device-detail="showDeviceDetailByCode"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 底部状态栏 -->
|
|
|
+ <StatusBar
|
|
|
+ :system-status="largeScreenStatsData.systemStatus"
|
|
|
+ :last-time="largeScreenStatsData.lastTime"
|
|
|
+ :has-alerts="hasAlerts"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 对话框和抽屉 -->
|
|
|
+ <AddDeviceDialog
|
|
|
+ ref="addDeviceDialogRef"
|
|
|
+ v-model:visible="dialogVisible"
|
|
|
+ :current-elderly="currentElderly"
|
|
|
+ :device-type-options="deviceTypeOptions"
|
|
|
+ :tenant-name="getTenantName()"
|
|
|
+ :organization-id="organizationId"
|
|
|
+ @submit="addDevice"
|
|
|
+ />
|
|
|
+
|
|
|
+ <DeviceDetailDialog
|
|
|
+ v-model:visible="deviceDetailVisible"
|
|
|
+ :device="deviceDetail"
|
|
|
+ :device-type-options="deviceTypeOptions"
|
|
|
+ />
|
|
|
+
|
|
|
+ <WarningDrawer
|
|
|
+ v-model:visible="warningDrawerVisible"
|
|
|
+ v-model:page-num="pageNum"
|
|
|
+ v-model:page-size="pageSize"
|
|
|
+ :warning-data="warningData"
|
|
|
+ :total="total"
|
|
|
+ @page-change="handlePageChange"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @handle-warning="onHandleWarningFromDrawer"
|
|
|
+ />
|
|
|
+
|
|
|
+ <AddElderDialog v-model:visible="addElderDialogVisible" @submit="submitAddElder" />
|
|
|
+
|
|
|
+ <HandleWarningDialog
|
|
|
+ v-model:visible="handleWarningDialogVisible"
|
|
|
+ :current-elderly="currentWarningElderly"
|
|
|
+ :mode="handleWarningMode"
|
|
|
+ @submit="submitHandleWarning"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import {
|
|
|
+ ref,
|
|
|
+ reactive,
|
|
|
+ computed,
|
|
|
+ onMounted,
|
|
|
+ onUnmounted,
|
|
|
+ nextTick,
|
|
|
+ h,
|
|
|
+ defineAsyncComponent
|
|
|
+} from 'vue'
|
|
|
+import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
|
|
+import fetchHttp from '@/config/axios/fetchHttp'
|
|
|
+import { getAccessToken, getLoginForm } from '@/utils/auth'
|
|
|
+import { formatToDateTime } from '@/utils/dateUtil'
|
|
|
+import { useWebSocket } from './composables/useWebSocket'
|
|
|
+import { amapReverseGeocode } from '@/utils/amapService'
|
|
|
+
|
|
|
+// 导入组件
|
|
|
+const TopInfoBar = defineAsyncComponent(() => import('./components/TopInfoBar.vue'))
|
|
|
+const StatsCard = defineAsyncComponent(() => import('./components/StatsCard.vue'))
|
|
|
+const ElderlyList = defineAsyncComponent(() => import('./components/ElderlyList.vue'))
|
|
|
+const DetailSection = defineAsyncComponent(() => import('./components/DetailSection.vue'))
|
|
|
+const AddDeviceDialog = defineAsyncComponent(() => import('./components/AddDeviceDialog.vue'))
|
|
|
+const DeviceDetailDialog = defineAsyncComponent(() => import('./components/DeviceDetailDialog.vue'))
|
|
|
+const WarningDrawer = defineAsyncComponent(() => import('./components/WarningDrawer.vue'))
|
|
|
+const AddElderDialog = defineAsyncComponent(() => import('./components/AddElderDialog.vue'))
|
|
|
+const HandleWarningDialog = defineAsyncComponent(
|
|
|
+ () => import('./components/HandleWarningDialog.vue')
|
|
|
+)
|
|
|
+const StatusBar = defineAsyncComponent(() => import('./components/StatusBar.vue'))
|
|
|
+const AllDevicesView = defineAsyncComponent(() => import('./components/AllDevicesView.vue'))
|
|
|
+
|
|
|
+// 类型定义
|
|
|
+interface WarningHistory {
|
|
|
+ happensAt: string
|
|
|
+ content: string
|
|
|
+ elderId?: number
|
|
|
+ elderName?: string
|
|
|
+}
|
|
|
+
|
|
|
+interface StatisticsVO {
|
|
|
+ systemStatus: string
|
|
|
+ lastTime: string
|
|
|
+ isWarning: boolean
|
|
|
+}
|
|
|
+
|
|
|
+interface CommonVo {
|
|
|
+ name: string
|
|
|
+ value: string
|
|
|
+}
|
|
|
+
|
|
|
+interface HistoryInfoVo {
|
|
|
+ happensAt: string
|
|
|
+ content: string
|
|
|
+}
|
|
|
+
|
|
|
+interface Elderly {
|
|
|
+ id: number
|
|
|
+ avatar: string
|
|
|
+ name: string
|
|
|
+ age: number
|
|
|
+ gender: string
|
|
|
+ healthStatus: string
|
|
|
+ healthText: string
|
|
|
+ deviceNumber: number
|
|
|
+ elderPhone?: string
|
|
|
+ relativePhone?: string
|
|
|
+ _flashEffect?: boolean
|
|
|
+}
|
|
|
+
|
|
|
+interface HealthVO {
|
|
|
+ name: string
|
|
|
+ value: string
|
|
|
+ status: string
|
|
|
+ unit: string
|
|
|
+}
|
|
|
+
|
|
|
+interface DetailDevice {
|
|
|
+ deviceType: string
|
|
|
+ installPosition: string
|
|
|
+ status: string
|
|
|
+ indicatorText: string
|
|
|
+ deviceCode: string
|
|
|
+}
|
|
|
+
|
|
|
+interface SelectElderly {
|
|
|
+ id: number
|
|
|
+ healthList: HealthVO[]
|
|
|
+ name: string
|
|
|
+ deviceList: DetailDevice[]
|
|
|
+}
|
|
|
+
|
|
|
+interface Device {
|
|
|
+ deviceType: string
|
|
|
+ installPosition: string
|
|
|
+ status: string
|
|
|
+ indicatorInfo: CommonVo[]
|
|
|
+ historyInfo: HistoryInfoVo[]
|
|
|
+}
|
|
|
+
|
|
|
+interface LargeScreenStat {
|
|
|
+ icon: string
|
|
|
+ value: number | string
|
|
|
+ label: string
|
|
|
+ trend: 'up' | 'stable'
|
|
|
+ change: string
|
|
|
+ type?: 'warning'
|
|
|
+ clickable?: boolean
|
|
|
+ indicator: string
|
|
|
+}
|
|
|
+
|
|
|
+interface DeviceTypeVO {
|
|
|
+ deviceType: string
|
|
|
+ deviceTypeName: string
|
|
|
+ displayOrder: number
|
|
|
+}
|
|
|
+
|
|
|
+// 常量定义
|
|
|
+const WARNING_STORAGE_KEY = 'elder_warning_flags'
|
|
|
+const organizationId = localStorage.getItem('organizationId')
|
|
|
+
|
|
|
+// 响应式数据
|
|
|
+const total = ref(0)
|
|
|
+const pageNum = ref(1)
|
|
|
+const pageSize = ref(10)
|
|
|
+const elderlyList = ref<Elderly[]>([])
|
|
|
+const dialogVisible = ref(false)
|
|
|
+const addDeviceDialogRef = ref<any>(null)
|
|
|
+const deviceDetailVisible = ref(false)
|
|
|
+const currentElderly = ref<SelectElderly | null>(null)
|
|
|
+const selectedElderly = ref<SelectElderly>({
|
|
|
+ id: 0,
|
|
|
+ name: '',
|
|
|
+ healthList: [],
|
|
|
+ deviceList: []
|
|
|
+})
|
|
|
+
|
|
|
+// 全部设备视图
|
|
|
+const showAllDevices = ref(false)
|
|
|
+interface AllDevice {
|
|
|
+ deviceType: string
|
|
|
+ deviceCode: string
|
|
|
+ installPosition: string
|
|
|
+ elderName: string
|
|
|
+ historyInfo: HistoryInfoVo[]
|
|
|
+}
|
|
|
+const allDevices = ref<AllDevice[]>([])
|
|
|
+
|
|
|
+const deviceTypeOptions = ref<DeviceTypeVO[]>([])
|
|
|
+const warningDrawerVisible = ref(false)
|
|
|
+const warningData = ref<WarningHistory[]>([])
|
|
|
+const deviceDetail = ref<Device | null>(null)
|
|
|
+const largeScreenStatsData = ref<StatisticsVO>({
|
|
|
+ systemStatus: '正常',
|
|
|
+ lastTime: new Date().toLocaleString(),
|
|
|
+ isWarning: false
|
|
|
+})
|
|
|
+
|
|
|
+const addElderDialogVisible = ref(false)
|
|
|
+const handleWarningDialogVisible = ref(false)
|
|
|
+const currentWarningElderly = ref<Elderly | null>(null)
|
|
|
+const handleWarningMode = ref<'default' | 'reportOnly'>('default')
|
|
|
+const warningFlags = ref<number[]>([])
|
|
|
+
|
|
|
+// 告警标记管理
|
|
|
+const loadWarningFlags = () => {
|
|
|
+ try {
|
|
|
+ const raw = localStorage.getItem(WARNING_STORAGE_KEY)
|
|
|
+ warningFlags.value = raw ? JSON.parse(raw) : []
|
|
|
+ } catch (e) {
|
|
|
+ warningFlags.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const saveWarningFlags = () => {
|
|
|
+ localStorage.setItem(WARNING_STORAGE_KEY, JSON.stringify(warningFlags.value))
|
|
|
+}
|
|
|
+
|
|
|
+const addWarningFlag = (elderId: number) => {
|
|
|
+ if (!elderId) return
|
|
|
+ if (!warningFlags.value.includes(elderId)) {
|
|
|
+ warningFlags.value.push(elderId)
|
|
|
+ saveWarningFlags()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const clearWarningFlag = (elderId: number) => {
|
|
|
+ const idx = warningFlags.value.indexOf(elderId)
|
|
|
+ if (idx !== -1) {
|
|
|
+ warningFlags.value.splice(idx, 1)
|
|
|
+ saveWarningFlags()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 大屏统计信息
|
|
|
+const largeScreenStats = ref<LargeScreenStat[]>([
|
|
|
+ {
|
|
|
+ icon: 'mdi:account-group',
|
|
|
+ value: 0,
|
|
|
+ label: '老人数量',
|
|
|
+ trend: 'up',
|
|
|
+ change: '',
|
|
|
+ indicator: 'elderCount'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ icon: 'mdi:devices',
|
|
|
+ value: 0,
|
|
|
+ label: '设备总数',
|
|
|
+ trend: 'up',
|
|
|
+ change: '',
|
|
|
+ clickable: true,
|
|
|
+ indicator: 'deviceCount'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ icon: 'mdi:shield-check',
|
|
|
+ value: 0,
|
|
|
+ label: '在线设备',
|
|
|
+ trend: 'up',
|
|
|
+ change: '100%',
|
|
|
+ indicator: 'onlineCount'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ icon: 'mdi:alert-decagram',
|
|
|
+ value: 0,
|
|
|
+ label: '警告设备',
|
|
|
+ trend: 'up',
|
|
|
+ change: '需关注',
|
|
|
+ type: 'warning',
|
|
|
+ clickable: true,
|
|
|
+ indicator: 'warningCount'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+const hasAlerts = computed(() => largeScreenStatsData.value.isWarning)
|
|
|
+
|
|
|
+// 方法
|
|
|
+const getTenantName = () => {
|
|
|
+ return getLoginForm()?.tenantName || ''
|
|
|
+}
|
|
|
+
|
|
|
+const openAddDeviceDialog = (elderly: SelectElderly) => {
|
|
|
+ currentElderly.value = elderly
|
|
|
+ dialogVisible.value = true
|
|
|
+ nextTick(() => {
|
|
|
+ // 确保子组件已挂载并接收到 props 后再初始化表单
|
|
|
+ addDeviceDialogRef.value?.initForm?.()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const openAddDeviceFromList = (elderly: Elderly) => {
|
|
|
+ const temp: SelectElderly = {
|
|
|
+ id: elderly.id,
|
|
|
+ name: elderly.name,
|
|
|
+ healthList: [],
|
|
|
+ deviceList: []
|
|
|
+ }
|
|
|
+ openAddDeviceDialog(temp)
|
|
|
+}
|
|
|
+
|
|
|
+const addDevice = async (data: any) => {
|
|
|
+ try {
|
|
|
+ const res = await fetchHttp.post('/api/pc/admin/bindDevice', data, {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (res) {
|
|
|
+ ElMessage.success('设备添加成功!')
|
|
|
+ getElderDeviceMessage(selectedElderly.value.id)
|
|
|
+ } else {
|
|
|
+ ElMessage.error('设备添加失败!')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('添加设备失败:', error)
|
|
|
+ ElMessage.error('添加设备时出现错误')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const removeDevice = (elderly: any, device: DetailDevice) => {
|
|
|
+ console.log('elderly', elderly)
|
|
|
+ console.log('device', device)
|
|
|
+ ElMessageBox.confirm(`确定要删除设备吗?`, '删除确认', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+ .then(async () => {
|
|
|
+ const res = await fetchHttp.post(
|
|
|
+ '/api/pc/admin/unbindDevice',
|
|
|
+ {
|
|
|
+ deviceCode: device.deviceCode,
|
|
|
+ elderId: Number(elderly.id)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (res) {
|
|
|
+ ElMessage.success('设备删除成功!')
|
|
|
+ getElderDeviceMessage(selectedElderly.value.id)
|
|
|
+ } else {
|
|
|
+ ElMessage.error('设备删除失败!')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {})
|
|
|
+}
|
|
|
+
|
|
|
+const openAddElderDialog = () => {
|
|
|
+ addElderDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const submitAddElder = async (data: any) => {
|
|
|
+ try {
|
|
|
+ const res = await fetchHttp.post('/api/pc/admin/addElder', data, {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (res) {
|
|
|
+ ElMessage.success('长者添加成功!')
|
|
|
+ await getElderList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error('长者添加失败!')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('添加长者失败:', error)
|
|
|
+ ElMessage.error('添加长者时出现错误')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const openHandleWarningDialog = (elderly: Elderly) => {
|
|
|
+ currentWarningElderly.value = elderly
|
|
|
+ handleWarningMode.value = 'default' // 老人卡片进入:默认电话回访
|
|
|
+ handleWarningDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const submitHandleWarning = async (data: any) => {
|
|
|
+ if (data.handleType === 'report') {
|
|
|
+ data = {
|
|
|
+ ...data,
|
|
|
+ organizationId: organizationId,
|
|
|
+ organizationName: getTenantName()
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const res = await fetchHttp.post('/api/pc/admin/dealWith', data, {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (res) {
|
|
|
+ ElMessage.success('告警情况已上报!')
|
|
|
+ clearWarningFlag(data.elderId)
|
|
|
+ handleWarningDialogVisible.value = false
|
|
|
+ if (selectedElderly.value.id === data.elderId) {
|
|
|
+ await getElderDeviceMessage(selectedElderly.value.id)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ElMessage.error('处理失败,请重试')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('处理告警失败:', error)
|
|
|
+ ElMessage.error('处理告警时出现错误')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const selectElderly = (elderly: Elderly) => {
|
|
|
+ selectedElderly.value.id = elderly.id
|
|
|
+ selectedElderly.value.name = elderly.name
|
|
|
+ clearWarningFlag(elderly.id)
|
|
|
+ getElderDeviceMessage(selectedElderly.value.id)
|
|
|
+}
|
|
|
+
|
|
|
+const showDeviceDetail = async (device: DetailDevice) => {
|
|
|
+ await getDeviceDetail(device.deviceCode)
|
|
|
+ deviceDetailVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const handleStatCardClick = (stat: LargeScreenStat) => {
|
|
|
+ // 点击设备总数:切换到全部设备视图
|
|
|
+ if (stat.indicator === 'deviceCount') {
|
|
|
+ openAllDevicesView()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 点击警告设备:打开预警抽屉
|
|
|
+ if (stat.indicator === 'warningCount' && Number(stat.value) > 0) {
|
|
|
+ getAllWarning()
|
|
|
+ warningDrawerVisible.value = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handlePageChange = (newPageNum: number) => {
|
|
|
+ pageNum.value = newPageNum
|
|
|
+ getAllWarning()
|
|
|
+}
|
|
|
+
|
|
|
+const handleSizeChange = (newPageSize: number) => {
|
|
|
+ pageSize.value = newPageSize
|
|
|
+ pageNum.value = 1
|
|
|
+ getAllWarning()
|
|
|
+}
|
|
|
+
|
|
|
+const onHandleWarningFromDrawer = (record: WarningHistory) => {
|
|
|
+ let target: Elderly | null = null
|
|
|
+ if ((record as any).elderId) {
|
|
|
+ target = elderlyList.value.find((e) => e.id === (record as any).elderId) || null
|
|
|
+ }
|
|
|
+ if (!target && (record as any).elderName) {
|
|
|
+ target = elderlyList.value.find((e) => e.name === (record as any).elderName) || null
|
|
|
+ }
|
|
|
+ if (!target && record.content) {
|
|
|
+ // 尝试从内容中提取姓名(简单匹配)
|
|
|
+ const match = record.content.match(/(长者|老人|用户)[::]\s*([\u4e00-\u9fa5A-Za-z0-9_]+)/)
|
|
|
+ if (match && match[2]) {
|
|
|
+ target = elderlyList.value.find((e) => e.name === match[2]) || null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (target) {
|
|
|
+ handleWarningMode.value = 'reportOnly'
|
|
|
+ currentWarningElderly.value = target
|
|
|
+ handleWarningDialogVisible.value = true
|
|
|
+ } else {
|
|
|
+ handleWarningMode.value = 'reportOnly'
|
|
|
+ currentWarningElderly.value = null
|
|
|
+ handleWarningDialogVisible.value = true
|
|
|
+ // ElMessage.warning('未找到对应长者,将以上报方式处理')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// API 调用
|
|
|
+const getStatistics = async () => {
|
|
|
+ const res = await fetchHttp.get(
|
|
|
+ '/api/pc/admin/getStatistics?organizationId=' + organizationId,
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (res.list && res.list.length) {
|
|
|
+ res.list.forEach((v: any) => {
|
|
|
+ const index = largeScreenStats.value.findIndex((z) => z.indicator == v.indicator)
|
|
|
+ if (~index) {
|
|
|
+ largeScreenStats.value[index].value = v.total
|
|
|
+ largeScreenStats.value[index].change = v.change
|
|
|
+ largeScreenStats.value[index].trend = v.trend
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getElderList = async () => {
|
|
|
+ const res = await fetchHttp.get(
|
|
|
+ '/api/pc/admin/getElderList?organizationId=' + organizationId,
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (res?.length) {
|
|
|
+ elderlyList.value = res
|
|
|
+ if (elderlyList.value.length) {
|
|
|
+ selectedElderly.value.id = elderlyList.value[0].id
|
|
|
+ selectedElderly.value.name = elderlyList.value[0].name
|
|
|
+ getElderDeviceMessage(selectedElderly.value.id)
|
|
|
+ warningFlags.value = []
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getDeviceDetail = async (deviceCode: string) => {
|
|
|
+ const res = await fetchHttp.get(
|
|
|
+ '/api/pc/admin/getDeviceDetail?deviceCode=' + deviceCode,
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (res) {
|
|
|
+ deviceDetail.value = res
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getElderDeviceMessage = async (elderId: number) => {
|
|
|
+ const res = await fetchHttp.get(
|
|
|
+ '/api/pc/admin/getElderHealthAndDeviceInfo?elderId=' + elderId,
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (res) {
|
|
|
+ selectedElderly.value.healthList = res.healthList
|
|
|
+ selectedElderly.value.deviceList = res.deviceList
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getAllWarning = async () => {
|
|
|
+ const params = {
|
|
|
+ pageNum: pageNum.value,
|
|
|
+ pageSize: pageSize.value
|
|
|
+ }
|
|
|
+ const res = await fetchHttp.get('/api/pc/admin/getAllWarning', params, {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (res) {
|
|
|
+ total.value = res.total
|
|
|
+ warningData.value = res.list || []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getAllDevices = async () => {
|
|
|
+ const res = await fetchHttp.get(
|
|
|
+ '/api/pc/admin/getAllDeviceTypes',
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (res && res?.length) {
|
|
|
+ deviceTypeOptions.value = res
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// WebSocket 处理
|
|
|
+const addFlashingEffect = (elder: Elderly) => {
|
|
|
+ elder._flashEffect = true
|
|
|
+ setTimeout(() => {
|
|
|
+ elder._flashEffect = false
|
|
|
+ elderlyList.value = [...elderlyList.value]
|
|
|
+ }, 10000)
|
|
|
+}
|
|
|
+
|
|
|
+// 全部设备视图相关方法
|
|
|
+const refreshAllDevices = async () => {
|
|
|
+ try {
|
|
|
+ const res = await fetchHttp.get(
|
|
|
+ '/api/pc/admin/getAllDevice',
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${getAccessToken()}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (Array.isArray(res)) {
|
|
|
+ allDevices.value = res
|
|
|
+ } else if (res?.list && Array.isArray(res.list)) {
|
|
|
+ allDevices.value = res.list
|
|
|
+ } else {
|
|
|
+ allDevices.value = []
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('获取全部设备失败:', e)
|
|
|
+ allDevices.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const openAllDevicesView = async () => {
|
|
|
+ showAllDevices.value = true
|
|
|
+ await refreshAllDevices()
|
|
|
+}
|
|
|
+
|
|
|
+const backToOverview = () => {
|
|
|
+ showAllDevices.value = false
|
|
|
+}
|
|
|
+
|
|
|
+const showDeviceDetailByCode = async (deviceCode: string) => {
|
|
|
+ await getDeviceDetail(deviceCode)
|
|
|
+ deviceDetailVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const handleSOSAlert = async (alertData: any) => {
|
|
|
+ const alert = alertData.data || alertData
|
|
|
+ sendMessage({
|
|
|
+ type: 'SOS_ACK',
|
|
|
+ alertId: alertData.timestamp,
|
|
|
+ timestamp: Date.now()
|
|
|
+ })
|
|
|
+
|
|
|
+ // 获取地址信息
|
|
|
+ let addressInfo = '位置信息获取中...'
|
|
|
+ if (alert.longitude && alert.latitude) {
|
|
|
+ addressInfo = await amapReverseGeocode(alert.longitude, alert.latitude)
|
|
|
+ }
|
|
|
+
|
|
|
+ ElNotification({
|
|
|
+ title: '🚨🚨 SOS紧急预警 🚨🚨',
|
|
|
+ customClass: 'my-warning-notification',
|
|
|
+ message: h('div', {
|
|
|
+ style: { color: '#fff' },
|
|
|
+ innerHTML: `
|
|
|
+ <div>院区名称: ${alert.organizationName || '未知'}</div>
|
|
|
+ <div>长者姓名: ${alert.elderName || '未知'}</div>
|
|
|
+ <div>长者房间: ${alert.roomName || '未知'}</div>
|
|
|
+ <div>设备类型: ${alert.deviceType || '未知'}</div>
|
|
|
+ <div>设备电量: ${alert.batteryLevel || '0%'}</div>
|
|
|
+ <div>位置信息: ${addressInfo}</div>
|
|
|
+ <div>时间: ${new Date(alertData.timestamp).toLocaleString()}</div>
|
|
|
+ `
|
|
|
+ }),
|
|
|
+ type: 'warning',
|
|
|
+ duration: 10000
|
|
|
+ })
|
|
|
+
|
|
|
+ if (alert.elderId) addWarningFlag(alert.elderId)
|
|
|
+
|
|
|
+ if (alert.elderId && elderlyList.value.length > 0) {
|
|
|
+ const elderIndex = elderlyList.value.findIndex((elder) => elder.id === alert.elderId)
|
|
|
+ if (elderIndex !== -1) {
|
|
|
+ const reorderedList = [...elderlyList.value]
|
|
|
+ const [selectedElder] = reorderedList.splice(elderIndex, 1)
|
|
|
+ reorderedList.unshift(selectedElder)
|
|
|
+ elderlyList.value = reorderedList
|
|
|
+ addFlashingEffect(selectedElder)
|
|
|
+ getElderDeviceMessage(alert.elderId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ largeScreenStatsData.value = {
|
|
|
+ systemStatus: '告警',
|
|
|
+ lastTime: new Date(alertData.timestamp).toLocaleString(),
|
|
|
+ isWarning: true
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ largeScreenStatsData.value = {
|
|
|
+ systemStatus: '正常',
|
|
|
+ lastTime: new Date().toLocaleString(),
|
|
|
+ isWarning: false
|
|
|
+ }
|
|
|
+ }, 10000)
|
|
|
+}
|
|
|
+
|
|
|
+const handleHealthAlert = async (healthAlert: any) => {
|
|
|
+ const healthAlertData = healthAlert.data
|
|
|
+
|
|
|
+ if (healthAlertData.elderId) addWarningFlag(healthAlertData.elderId)
|
|
|
+
|
|
|
+ // 获取地址信息
|
|
|
+ let addressInfo = ''
|
|
|
+ if (healthAlertData.longitude && healthAlertData.latitude) {
|
|
|
+ addressInfo = await amapReverseGeocode(healthAlertData.longitude, healthAlertData.latitude)
|
|
|
+ }
|
|
|
+
|
|
|
+ ElNotification({
|
|
|
+ title: `🚨🚨 ${healthAlertData.eventType} 🚨🚨`,
|
|
|
+ customClass: 'my-warning-notification',
|
|
|
+ message: h('div', {
|
|
|
+ style: { color: '#fff' },
|
|
|
+ innerHTML: `
|
|
|
+ <div>长者姓名: ${healthAlertData.elderName || '未知'}</div>
|
|
|
+ <div>预警指标: ${healthAlertData.indicatorName || '未知'}</div>
|
|
|
+ <div>预警信息: ${healthAlertData.message || '未知'}</div>
|
|
|
+ <div>处理建议: ${healthAlertData.suggestion || '未知'}</div>
|
|
|
+ <div>预警程度: ${healthAlertData.alertLevel || ''}</div>
|
|
|
+ <div>位置信息: ${addressInfo}</div>
|
|
|
+ <div>时间: ${new Date(healthAlertData.timestamp).toLocaleString()}</div>
|
|
|
+ `
|
|
|
+ }),
|
|
|
+ type: 'warning',
|
|
|
+ duration: 10000
|
|
|
+ })
|
|
|
+
|
|
|
+ if (healthAlertData.elderId && elderlyList.value.length > 0) {
|
|
|
+ const elderIndex = elderlyList.value.findIndex((elder) => elder.id === healthAlertData.elderId)
|
|
|
+ if (elderIndex !== -1) {
|
|
|
+ elderlyList.value[elderIndex].healthText =
|
|
|
+ healthAlertData.message + healthAlertData.suggestion
|
|
|
+ const reorderedList = [...elderlyList.value]
|
|
|
+ const [selectedElder] = reorderedList.splice(elderIndex, 1)
|
|
|
+ reorderedList.unshift(selectedElder)
|
|
|
+ elderlyList.value = reorderedList
|
|
|
+ addFlashingEffect(selectedElder)
|
|
|
+ getElderDeviceMessage(healthAlertData.elderId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ largeScreenStatsData.value = {
|
|
|
+ systemStatus: '健康告警',
|
|
|
+ lastTime: new Date(healthAlertData.timestamp).toLocaleString(),
|
|
|
+ isWarning: true
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ largeScreenStatsData.value = {
|
|
|
+ systemStatus: '设备正常',
|
|
|
+ lastTime: new Date().toLocaleString(),
|
|
|
+ isWarning: false
|
|
|
+ }
|
|
|
+ }, 10000)
|
|
|
+}
|
|
|
+
|
|
|
+// WebSocket 连接
|
|
|
+const wsUrl = import.meta.env.VITE_API_WSS_URL
|
|
|
+const { connect, disconnect, sendMessage } = useWebSocket({
|
|
|
+ wsUrl,
|
|
|
+ onSOSAlert: handleSOSAlert,
|
|
|
+ onHealthAlert: handleHealthAlert
|
|
|
+})
|
|
|
+
|
|
|
+// 生命周期
|
|
|
+onMounted(() => {
|
|
|
+ loadWarningFlags()
|
|
|
+ getAllDevices()
|
|
|
+ getStatistics()
|
|
|
+ getElderList()
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ connect()
|
|
|
+ }, 1000)
|
|
|
+
|
|
|
+ document.addEventListener('visibilitychange', () => {
|
|
|
+ if (!document.hidden) {
|
|
|
+ // 页面回到前台
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ setInterval(() => {
|
|
|
+ // 定期健康检查
|
|
|
+ }, 30000)
|
|
|
+
|
|
|
+ window.addEventListener('online', () => {
|
|
|
+ // 网络恢复
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ disconnect()
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+$primary-color: #1a73e8;
|
|
|
+$secondary-color: #00c6ff;
|
|
|
+$accent-color: #7b61ff;
|
|
|
+$dark-bg: #0a0e14;
|
|
|
+$card-bg: #1a1f2e;
|
|
|
+$text-light: #fff;
|
|
|
+$text-gray: #8a8f98;
|
|
|
+$success-color: #26de81;
|
|
|
+$warning-color: #fd9644;
|
|
|
+$danger-color: #ff6b6b;
|
|
|
+$bg-gradient-start: #04060f;
|
|
|
+$bg-gradient-mid: #0c1631;
|
|
|
+$bg-gradient-end: #101b3f;
|
|
|
+$bg-accent-1: rgb(32 156 255 / 35%);
|
|
|
+$bg-accent-2: rgb(123 97 255 / 25%);
|
|
|
+
|
|
|
+@keyframes auroraShift {
|
|
|
+ 0% {
|
|
|
+ opacity: 0.6;
|
|
|
+ transform: translate(-10%, -10%) scale(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ 50% {
|
|
|
+ opacity: 0.9;
|
|
|
+ transform: translate(5%, 10%) scale(1.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ 100% {
|
|
|
+ opacity: 0.6;
|
|
|
+ transform: translate(15%, -5%) scale(1.05);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.large-screen {
|
|
|
+ position: relative;
|
|
|
+ padding: 20px;
|
|
|
+ overflow: hidden;
|
|
|
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
|
|
+ sans-serif;
|
|
|
+ color: $text-light;
|
|
|
+ background: radial-gradient(circle at 10% 20%, rgb(15 88 255 / 25%) 0%, transparent 35%),
|
|
|
+ radial-gradient(circle at 90% 10%, rgb(20 201 201 / 18%) 0%, transparent 30%),
|
|
|
+ linear-gradient(135deg, $bg-gradient-start 0%, $bg-gradient-mid 45%, $bg-gradient-end 100%);
|
|
|
+
|
|
|
+ &::before,
|
|
|
+ &::after {
|
|
|
+ position: absolute;
|
|
|
+ pointer-events: none;
|
|
|
+ background: radial-gradient(circle, $bg-accent-1 0%, transparent 60%);
|
|
|
+ content: '';
|
|
|
+ opacity: 0.7;
|
|
|
+ filter: blur(80px);
|
|
|
+ animation: auroraShift 18s ease-in-out infinite alternate;
|
|
|
+ inset: -30%;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ background: radial-gradient(circle, $bg-accent-2 0%, transparent 60%);
|
|
|
+ animation-delay: 4s;
|
|
|
+ animation-duration: 22s;
|
|
|
+ }
|
|
|
+
|
|
|
+ .my-container {
|
|
|
+ display: flex;
|
|
|
+ height: 100%;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-bg {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ z-index: -2;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: radial-gradient(circle at 20% 30%, rgb(34 123 255 / 18%) 0%, transparent 45%),
|
|
|
+ radial-gradient(circle at 80% 70%, rgb(123 97 255 / 15%) 0%, transparent 50%),
|
|
|
+ radial-gradient(circle at 50% 50%, rgb(0 255 240 / 8%) 0%, transparent 55%);
|
|
|
+ opacity: 0.5;
|
|
|
+ filter: blur(10px);
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-grid {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ z-index: -1;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background-image: linear-gradient(rgb(255 255 255 / 5%) 1px, transparent 1px),
|
|
|
+ linear-gradient(90deg, rgb(255 255 255 / 5%) 1px, transparent 1px);
|
|
|
+ background-position: center;
|
|
|
+ background-size: 40px 40px;
|
|
|
+ opacity: 0.35;
|
|
|
+ mix-blend-mode: screen;
|
|
|
+}
|
|
|
+
|
|
|
+.main-content-large {
|
|
|
+ display: grid;
|
|
|
+ min-height: 0;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ flex: 1;
|
|
|
+ grid-template-columns: 1fr 2fr;
|
|
|
+ gap: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-input__wrapper) {
|
|
|
+ padding: 15px 20px !important;
|
|
|
+ font-size: 16px !important;
|
|
|
+ background: rgb(255 255 255 / 10%) !important;
|
|
|
+ border-radius: 10px !important;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-input__inner) {
|
|
|
+ font-size: 16px !important;
|
|
|
+ color: $text-light !important;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-button) {
|
|
|
+ display: flex !important;
|
|
|
+ padding: 12px 24px !important;
|
|
|
+ font-size: 16px !important;
|
|
|
+ font-weight: 500 !important;
|
|
|
+ border-radius: 10px !important;
|
|
|
+ transition: all 0.3s !important;
|
|
|
+ align-items: center !important;
|
|
|
+ gap: 8px !important;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-button--primary) {
|
|
|
+ background: linear-gradient(90deg, $primary-color, $accent-color) !important;
|
|
|
+ border: none !important;
|
|
|
+ box-shadow: 0 4px 15px rgb(26 115 232 / 30%) !important;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-button--primary:hover) {
|
|
|
+ transform: translateY(-2px) !important;
|
|
|
+ box-shadow: 0 8px 25px rgb(26 115 232 / 40%) !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 全部设备视图样式 */
|
|
|
+.all-devices-view {
|
|
|
+ padding: 10px 0 20px;
|
|
|
+
|
|
|
+ .all-devices-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ h3 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-card {
|
|
|
+ background: rgb(255 255 255 / 6%);
|
|
|
+ border: 1px solid rgb(255 255 255 / 12%);
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 14px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+
|
|
|
+ .device-card-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ .device-type {
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ .device-code {
|
|
|
+ color: $text-gray;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-meta {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ gap: 4px;
|
|
|
+ color: $text-gray;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-history {
|
|
|
+ background: rgb(255 255 255 / 4%);
|
|
|
+ border: 1px solid rgb(255 255 255 / 10%);
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 8px;
|
|
|
+
|
|
|
+ .history-title {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #cfd3dc;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .history-list {
|
|
|
+ list-style: none;
|
|
|
+ padding: 0;
|
|
|
+ margin: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 6px;
|
|
|
+
|
|
|
+ li {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 150px 1fr;
|
|
|
+ gap: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+
|
|
|
+ .time {
|
|
|
+ color: #a5b1c2;
|
|
|
+ }
|
|
|
+ .content {
|
|
|
+ color: #ffffff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-actions {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+/* 滚动条优化 */
|
|
|
+.elderly-scroll-container::-webkit-scrollbar,
|
|
|
+.detail-section::-webkit-scrollbar,
|
|
|
+.history-list::-webkit-scrollbar {
|
|
|
+ width: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.elderly-scroll-container::-webkit-scrollbar-track,
|
|
|
+.detail-section::-webkit-scrollbar-track,
|
|
|
+.history-list::-webkit-scrollbar-track {
|
|
|
+ background: rgb(255 255 255 / 5%);
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.elderly-scroll-container::-webkit-scrollbar-thumb,
|
|
|
+.detail-section::-webkit-scrollbar-thumb,
|
|
|
+.history-list::-webkit-scrollbar-thumb {
|
|
|
+ background: rgb(26 115 232 / 50%);
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+.large-screen-dialog {
|
|
|
+ margin-top: 8vh !important;
|
|
|
+ background: #1a1f2e !important;
|
|
|
+ border: 1px solid rgb(255 255 255 / 10%) !important;
|
|
|
+ border-radius: 16px !important;
|
|
|
+ box-shadow: 0 20px 60px rgb(0 0 0 / 40%) !important;
|
|
|
+
|
|
|
+ .el-dialog__header {
|
|
|
+ padding: 25px !important;
|
|
|
+ margin-top: 20px;
|
|
|
+ color: white !important;
|
|
|
+ background: linear-gradient(90deg, var(--el-color-primary), #7b61ff) !important;
|
|
|
+ border-radius: 16px 16px 0 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__title {
|
|
|
+ font-size: 20px !important;
|
|
|
+ font-weight: 600 !important;
|
|
|
+ color: white !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 30px !important;
|
|
|
+ color: #fff !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+.my-warning-notification {
|
|
|
+ color: #fff !important;
|
|
|
+ background: linear-gradient(135deg, #1e4184 0%, #3469e3 100%) !important;
|
|
|
+ border: 1px solid rgb(42 157 143 / 30%) !important;
|
|
|
+ box-shadow: 0 4px 20px rgb(0 0 0 / 50%) !important;
|
|
|
+
|
|
|
+ .el-notification__group {
|
|
|
+ .el-notification__title {
|
|
|
+ color: #fff !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|