|
@@ -2,7 +2,7 @@
|
|
|
<div class="dashboard">
|
|
|
<div class="dashboard-header">
|
|
|
<div class="community-name">
|
|
|
- <div class="fixedName"> 智慧大屏</div>
|
|
|
+ <div class="fixedName"> 智慧大屏演示版</div>
|
|
|
<div class="tenantName">{{ tenantName }}</div>
|
|
|
</div>
|
|
|
<div class="running-days"
|
|
@@ -21,26 +21,55 @@
|
|
|
:getPopupContainer="(trigger: HTMLElement) => trigger.parentNode"
|
|
|
@change="handleTenantChange"
|
|
|
/>
|
|
|
+ <div style="cursor: pointer; font-weight: 600" @click="goToHome">返回首页</div>
|
|
|
</div>
|
|
|
<div class="data-flow header-flow"></div>
|
|
|
</div>
|
|
|
<div class="dashboard-content">
|
|
|
<div class="block custom-scroll">
|
|
|
<div class="data-row">
|
|
|
+ <GuardObjectAgeCard :ageList="todayData.ageList" title="入住老人年龄分布" />
|
|
|
+ <GuardObjectTypeCard :guardList="todayData.guardList" title="检测对象分级" />
|
|
|
+ <MonitorPeopleCountCard :detectedCount="todayData.detectedCount" title="今日检测到人数" />
|
|
|
+ <ElderActivityCard :activity-rate="todayData.activeRate" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="editor-note">
|
|
|
+ <TechCard>
|
|
|
+ <div class="editor-note-hd">长者基础事件备忘录</div>
|
|
|
+ <div class="editor-note-bd">
|
|
|
+ <div>305房间,昨日发生滞留</div>
|
|
|
+ <div>802老人,昨日发生摔倒</div>
|
|
|
+ <div>601房间,昨日如厕异常</div>
|
|
|
+ <div>708房间,昨日发生摔倒</div>
|
|
|
+ <div>503房间,昨日发生滞留</div>
|
|
|
+ <div>915房间,昨日如厕异常</div>
|
|
|
+ <!-- 重复一遍内容以实现无缝滚动 -->
|
|
|
+ <div>305房间,昨日发生滞留</div>
|
|
|
+ <div>802老人,昨日发生摔倒</div>
|
|
|
+ <div>601房间,昨日如厕异常</div>
|
|
|
+ <div>708房间,昨日发生摔倒</div>
|
|
|
+ <div>503房间,昨日发生滞留</div>
|
|
|
+ <div>915房间,昨日如厕异常</div>
|
|
|
+ </div>
|
|
|
+ </TechCard>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- <div class="data-row">
|
|
|
<DeviceOnlineRateCard
|
|
|
:online-count="todayData.onlineCount"
|
|
|
:device-count="todayData.deviceCount"
|
|
|
></DeviceOnlineRateCard>
|
|
|
<MonitorPeopleCountCard :detectedCount="todayData.detectedCount"></MonitorPeopleCountCard>
|
|
|
- </div>
|
|
|
- <div class="data-row">
|
|
|
+ </div> -->
|
|
|
+ <!-- <div class="data-row">
|
|
|
<GuardObjectTypeCard :guardList="todayData.guardList"></GuardObjectTypeCard>
|
|
|
<AlertFallCompareCard
|
|
|
:fall-count="todayData.fallingCount"
|
|
|
:alert-count="todayData.alarmCount"
|
|
|
></AlertFallCompareCard>
|
|
|
- </div>
|
|
|
- <DeviceLocationCard :data="todayData.installPositionList"></DeviceLocationCard>
|
|
|
+ </div> -->
|
|
|
+ <!-- <DeviceLocationCard :data="todayData.installPositionList"></DeviceLocationCard> -->
|
|
|
</div>
|
|
|
<div class="block block-center custom-scroll">
|
|
|
<div class="map-container">
|
|
@@ -64,9 +93,22 @@
|
|
|
</div>
|
|
|
<div class="block custom-scroll" style="padding: 10px">
|
|
|
<div class="data-row">
|
|
|
+ <DeviceOnlineRateCard
|
|
|
+ :online-count="todayData.onlineCount"
|
|
|
+ :device-count="todayData.deviceCount"
|
|
|
+ />
|
|
|
+ <DeviceLocationCard :data="todayData.installPositionList" />
|
|
|
+ </div>
|
|
|
+ <AlertFallCompareCard
|
|
|
+ :fall-count="todayData.fallingCount"
|
|
|
+ :alert-count="todayData.alarmCount"
|
|
|
+ style="margin-bottom: 10px"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- <div class="data-row">
|
|
|
<GuardObjectAgeCard :ageList="todayData.ageList"></GuardObjectAgeCard>
|
|
|
<ElderActivityCard :activity-rate="todayData.activeRate"></ElderActivityCard>
|
|
|
- </div>
|
|
|
+ </div> -->
|
|
|
|
|
|
<div class="data-line">
|
|
|
<HistoryChartCard
|
|
@@ -122,13 +164,15 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="dashboard-footer">
|
|
|
- <Copyright
|
|
|
+ <!-- <Copyright
|
|
|
company="合肥雷能信息技术有限公司"
|
|
|
icp="皖ICP备2024060056号-3"
|
|
|
icp-link="https://beian.miit.gov.cn"
|
|
|
icp-text="皖ICP备2024060056号-3"
|
|
|
font-color="#4774a7"
|
|
|
- />
|
|
|
+ /> -->
|
|
|
+ <div>305房间19:00 发生跌倒事件,请及时处理;</div>
|
|
|
+ <div>802老人17:00 发生异常消失,已处理;</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -156,6 +200,8 @@ import type {
|
|
|
StatsHomeScreenFallHistory,
|
|
|
StatsHomeScreenQueryData,
|
|
|
} from '@/api/stats/types'
|
|
|
+import TechCard from './components/TechCard/index.vue'
|
|
|
+import { set } from 'lodash-es'
|
|
|
|
|
|
const userStore = useUserStore()
|
|
|
|
|
@@ -267,7 +313,7 @@ watch(
|
|
|
|
|
|
if (newTenantId) {
|
|
|
pollingInstance = useDashboardPolling({ tenantId: String(newTenantId) })
|
|
|
- pollingInstance.start()
|
|
|
+ // pollingInstance.start()
|
|
|
|
|
|
// 赋值响应式数据
|
|
|
watchEffect(() => {
|
|
@@ -278,11 +324,119 @@ watch(
|
|
|
|
|
|
updateFallQueryType = pollingInstance.updateFallQueryType
|
|
|
updateAlarmQueryType = pollingInstance.updateAlarmQueryType
|
|
|
+
|
|
|
+ todayData.value.systemGuardDay = Math.round(Math.random() * 100) // 守护天数
|
|
|
+
|
|
|
+ setInterval(() => {
|
|
|
+ console.log('🚀🚀🚀 mock 数据更新了')
|
|
|
+ localStorage.setItem('todayData', JSON.stringify(todayData.value))
|
|
|
+ mockData()
|
|
|
+ }, 2000)
|
|
|
+
|
|
|
+ setInterval(() => {
|
|
|
+ todayData.value.systemGuardDay = Math.round(Math.random() * 100) // 守护天数
|
|
|
+ }, 5000)
|
|
|
}
|
|
|
},
|
|
|
{ immediate: true }
|
|
|
)
|
|
|
|
|
|
+// mock 数据
|
|
|
+const mockData = () => {
|
|
|
+ // todayData.value.systemGuardDay = Math.round(Math.random() * 100) // 守护天数
|
|
|
+ // 年龄分布
|
|
|
+ todayData.value.ageList = [
|
|
|
+ { ageRange: '60-69岁', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { ageRange: '70-79岁', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { ageRange: '80-89岁', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { ageRange: '90-99岁', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { ageRange: '100岁以上', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ ]
|
|
|
+ // 检测对象分级
|
|
|
+ todayData.value.guardList = [
|
|
|
+ { guardType: 'parent', name: '重点对象', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { guardType: 'child', name: '一般对象', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { guardType: 'guardian', name: '普通对象', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ ]
|
|
|
+ // 检测人数
|
|
|
+ todayData.value.detectedCount = Math.floor(Math.random() * 500) + 100
|
|
|
+ // 长者活跃度
|
|
|
+ todayData.value.activeRate = Math.round(Math.random() * 100)
|
|
|
+ // 设备在线率
|
|
|
+ todayData.value.onlineCount = Math.round(Math.random() * 2500)
|
|
|
+ todayData.value.deviceCount = 2568
|
|
|
+ // 设备安装位置
|
|
|
+ todayData.value.installPositionList = [
|
|
|
+ { installPosition: 'Bedroom', name: '卧室', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { installPosition: 'LivingRoom', name: '客厅', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { installPosition: 'Restaurant', name: '餐厅', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ { installPosition: 'Toilet', name: '卫生间', count: Math.floor(Math.random() * 50) + 10 },
|
|
|
+ ]
|
|
|
+ // 跌倒次数
|
|
|
+ todayData.value.fallingCount = Math.floor(Math.random() * 500) + 100
|
|
|
+ // 报警次数
|
|
|
+ todayData.value.alarmCount = Math.floor(Math.random() * 500) + 100
|
|
|
+
|
|
|
+ const generateRecent7DaysData = () => {
|
|
|
+ const today = new Date()
|
|
|
+ const dayStatInfo = []
|
|
|
+
|
|
|
+ for (let i = 6; i >= 0; i--) {
|
|
|
+ const date = new Date(today)
|
|
|
+ date.setDate(today.getDate() - i)
|
|
|
+ const formattedDate = date.toISOString().split('T')[0]
|
|
|
+ dayStatInfo.push({
|
|
|
+ date: formattedDate,
|
|
|
+ fallingCount: Math.floor(Math.random() * 50) + 10,
|
|
|
+ alarmCount: Math.floor(Math.random() * 50) + 10,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return dayStatInfo
|
|
|
+ }
|
|
|
+
|
|
|
+ const generateRecent180DaysMonthlyData = () => {
|
|
|
+ const today = new Date()
|
|
|
+ const monthStatInfo = []
|
|
|
+
|
|
|
+ for (let i = 5; i >= 0; i--) {
|
|
|
+ const date = new Date(today)
|
|
|
+ date.setMonth(today.getMonth() - i)
|
|
|
+ const formattedMonth = date.toISOString().slice(0, 7)
|
|
|
+ monthStatInfo.push({
|
|
|
+ month: formattedMonth,
|
|
|
+ fallingCount: Math.floor(Math.random() * 200) + 50,
|
|
|
+ alarmCount: Math.floor(Math.random() * 200) + 50,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return monthStatInfo
|
|
|
+ }
|
|
|
+
|
|
|
+ fallHistoryData.value = {
|
|
|
+ dayStatInfo: generateRecent7DaysData().map((d) => ({
|
|
|
+ date: d.date,
|
|
|
+ fallingCount: d.fallingCount,
|
|
|
+ })),
|
|
|
+ monthStatInfo: generateRecent180DaysMonthlyData().map((m) => ({
|
|
|
+ month: m.month,
|
|
|
+ fallingCount: m.fallingCount,
|
|
|
+ })),
|
|
|
+ }
|
|
|
+
|
|
|
+ alarmHistoryData.value = {
|
|
|
+ dayStatInfo: generateRecent7DaysData().map((d) => ({
|
|
|
+ date: d.date,
|
|
|
+ alarmCount: d.alarmCount,
|
|
|
+ })),
|
|
|
+ monthStatInfo: generateRecent180DaysMonthlyData().map((m) => ({
|
|
|
+ month: m.month,
|
|
|
+ alarmCount: m.alarmCount,
|
|
|
+ })),
|
|
|
+ }
|
|
|
+}
|
|
|
+mockData()
|
|
|
+
|
|
|
const { fetchDict: fetchDictGuardianship, dictNameMap: guardTypeNameMap } =
|
|
|
useDict('guardianship_type')
|
|
|
|
|
@@ -299,10 +453,10 @@ watch(
|
|
|
todayData.value.detectedCount = val?.detectedCount ?? 0
|
|
|
todayData.value.fallingCount = val?.fallingCount ?? 0
|
|
|
todayData.value.alarmCount = val?.alarmCount ?? 0
|
|
|
- todayData.value.systemGuardDay = val?.systemGuardDay ?? 0
|
|
|
+ // todayData.value.systemGuardDay = val?.systemGuardDay ?? 0
|
|
|
todayData.value.onlineCount = val?.onlineCount ?? 0
|
|
|
todayData.value.deviceCount = val?.deviceCount ?? 0
|
|
|
- todayData.value.ageList = (val?.ageList && val?.ageList.filter((item) => item.count > 0)) ?? []
|
|
|
+ // todayData.value.ageList = (val?.ageList && val?.ageList.filter((item) => item.count > 0)) ?? []
|
|
|
todayData.value.installPositionList =
|
|
|
(val?.installPositionList &&
|
|
|
val?.installPositionList.map((item) => ({
|
|
@@ -314,15 +468,15 @@ watch(
|
|
|
names: installPositionNameMap.value,
|
|
|
}))) ??
|
|
|
[]
|
|
|
- todayData.value.guardList =
|
|
|
- (val?.guardList &&
|
|
|
- val?.guardList.map((item) => ({
|
|
|
- ...item,
|
|
|
- name:
|
|
|
- guardTypeNameMap.value[item.guardType as keyof typeof guardTypeNameMap.value] || '未知',
|
|
|
- names: guardTypeNameMap.value,
|
|
|
- }))) ??
|
|
|
- []
|
|
|
+ // todayData.value.guardList =
|
|
|
+ // (val?.guardList &&
|
|
|
+ // val?.guardList.map((item) => ({
|
|
|
+ // ...item,
|
|
|
+ // name:
|
|
|
+ // guardTypeNameMap.value[item.guardType as keyof typeof guardTypeNameMap.value] || '未知',
|
|
|
+ // names: guardTypeNameMap.value,
|
|
|
+ // }))) ??
|
|
|
+ // []
|
|
|
},
|
|
|
{ immediate: true, deep: true }
|
|
|
)
|
|
@@ -456,6 +610,10 @@ const handleResize = () => {
|
|
|
scale.value = 0.7
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+const goToHome = () => {
|
|
|
+ window.open('/', '_blank')
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="less">
|
|
@@ -517,6 +675,13 @@ const handleResize = () => {
|
|
|
.fixedName {
|
|
|
margin-right: 10px;
|
|
|
}
|
|
|
+
|
|
|
+ .tenantName {
|
|
|
+ max-width: 300px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.running-days {
|
|
@@ -590,7 +755,8 @@ const handleResize = () => {
|
|
|
}
|
|
|
|
|
|
&-content {
|
|
|
- padding: 24px;
|
|
|
+ // padding: 24px;
|
|
|
+ padding: 10px 20px 5px;
|
|
|
flex: 1;
|
|
|
border-radius: 8px;
|
|
|
display: flex;
|
|
@@ -723,21 +889,74 @@ const handleResize = () => {
|
|
|
margin-bottom: 12px;
|
|
|
}
|
|
|
|
|
|
+ // 长者基础事件备忘录
|
|
|
+ .editor-note {
|
|
|
+ color: @text-color;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ &-hd {
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ color: #6de4ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &-bd {
|
|
|
+ height: 130px;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ line-height: 1.5;
|
|
|
+ }
|
|
|
+
|
|
|
+ &-bd::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ pointer-events: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ &-bd > div {
|
|
|
+ animation: scrollUp 12s linear infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes scrollUp {
|
|
|
+ 0% {
|
|
|
+ transform: translateY(100%);
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ transform: translateY(-100%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.data-line {
|
|
|
display: grid;
|
|
|
- grid-template-columns: 1fr;
|
|
|
+ // grid-template-columns: 1fr;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
gap: 12px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
&-footer {
|
|
|
- height: 20px;
|
|
|
- color: @text-color;
|
|
|
- color: #6de4ff;
|
|
|
- text-align: center;
|
|
|
- line-height: 30px;
|
|
|
- font-size: 12px;
|
|
|
- margin-bottom: 10px;
|
|
|
+ // height: 20px;
|
|
|
+ // color: @text-color;
|
|
|
+ // color: #6de4ff;
|
|
|
+ // text-align: center;
|
|
|
+ // line-height: 30px;
|
|
|
+ // font-size: 12px;
|
|
|
+ // margin-bottom: 10px;
|
|
|
+
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ div {
|
|
|
+ flex-grow: 1;
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px;
|
|
|
+ border: 3px solid @border-color;
|
|
|
+ margin: 10px 24px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@media (max-width: 1600px) {
|