xiongxing 2 týždňov pred
rodič
commit
68178c8e5e

BIN
src/assets/imgs/index-banner.png


+ 16 - 2
src/layout/Layout.vue

@@ -46,6 +46,22 @@ const renderLayout = () => {
 export default defineComponent({
   name: 'Layout',
   setup() {
+    // return () => (
+    //   <section class={[prefixCls, `${prefixCls}__${layout.value}`, 'w-[100%] h-[100%] relative']}>
+    //     {mobile.value && !collapse.value ? (
+    //       <div
+    //         class="absolute left-0 top-0 z-99 h-full w-full bg-[var(--el-color-black)] opacity-30"
+    //         onClick={handleClickOutside}
+    //       ></div>
+    //     ) : undefined}
+
+    //     {renderLayout()}
+
+    //     <Backtop></Backtop>
+
+    //     <Setting></Setting>
+    //   </section>
+    // )
     return () => (
       <section class={[prefixCls, `${prefixCls}__${layout.value}`, 'w-[100%] h-[100%] relative']}>
         {mobile.value && !collapse.value ? (
@@ -58,8 +74,6 @@ export default defineComponent({
         {renderLayout()}
 
         <Backtop></Backtop>
-
-        <Setting></Setting>
       </section>
     )
   }

+ 3 - 2
src/layout/components/Setting/src/Setting.vue

@@ -200,9 +200,10 @@ const clear = () => {
 </script>
 
 <template>
+  <!-- class="fixed right-0 top-[45%] h-40px w-40px cursor-pointer bg-[var(--el-color-primary)] text-center leading-40px" -->
   <div
     :class="prefixCls"
-    class="fixed right-0 top-[45%] h-40px w-40px cursor-pointer bg-[var(--el-color-primary)] text-center leading-40px"
+    class="right-0 top-[45%] h-40px w-40px cursor-pointer bg-[var(--el-color-primary)] text-center leading-40px"
     @click="drawer = true"
   >
     <Icon color="#fff" icon="ep:setting" />
@@ -297,6 +298,6 @@ $prefix-cls: #{$namespace}-setting;
 
 .#{$prefix-cls} {
   border-radius: 6px 0 0 6px;
-  z-index: 1200;/*修正没有z-index会被表格层覆盖,值不要超过4000*/
+  z-index: 1200; /*修正没有z-index会被表格层覆盖,值不要超过4000*/
 }
 </style>

+ 1 - 1
src/views/Home/components/DetailSection.vue

@@ -5,7 +5,7 @@
         <h2>{{ selectedElderly.name }}的详细信息</h2>
       </div>
       <div class="header-actions">
-        <el-button type="info" size="small" @click="viewProfile" style="margin-right: 10px">
+        <el-button type="primary" size="small" @click="viewProfile" style="margin-right: 10px">
           长者档案
         </el-button>
         <el-button

+ 36 - 14
src/views/Home/components/ElderLocationMap.vue

@@ -474,13 +474,13 @@ const addRealtimePoint = async (pt: {
     const lat = Number(pt.latitude)
     if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
 
-    // 去重:与最后一点相同则忽略
-    const last = historyPoints[historyPoints.length - 1]
-    const lastKey = last
-      ? `${Number(last.longitude).toFixed(6)},${Number(last.latitude).toFixed(6)}`
+    // 去重:与最新一点相同则忽略(最新在第一位)
+    const first = historyPoints[0]
+    const firstKey = first
+      ? `${Number(first.longitude).toFixed(6)},${Number(first.latitude).toFixed(6)}`
       : ''
     const curKey = `${lng.toFixed(6)},${lat.toFixed(6)}`
-    if (lastKey && lastKey === curKey) return
+    if (firstKey && firstKey === curKey) return
 
     // 确保地图存在
     ensureMap(lng, lat)
@@ -494,19 +494,19 @@ const addRealtimePoint = async (pt: {
       }
     }
 
-    // 保存到历史(带地址)
+    // 保存到历史(带地址)——追加到第一位
     const rec: HistoryPoint = {
       longitude: lng,
       latitude: lat,
       locationTime: pt.locationTime,
       address: addressCache[curKey] || ''
     }
-    historyPoints.push(rec)
+    historyPoints.unshift(rec)
 
     const AMap = (window as any).AMap
     if (!AMap || !AMap.Marker) return
 
-    // 添加标记
+    // 添加标记(同步到第一位)
     const marker = new AMap.Marker({
       position: [lng, lat],
       title: rec.locationTime ? `时间:${rec.locationTime}` : `实时点位`
@@ -516,12 +516,35 @@ const addRealtimePoint = async (pt: {
     } else if (amap && typeof amap.addOverlays === 'function') {
       amap.addOverlays([marker])
     }
-    markers.push(marker)
 
-    // 限制最多100条,同时删除最早的覆盖物
+    // 绑定信息窗
+    try {
+      const infoContent = `<div style="padding:8px 10px;font-size:12px;color:#000;">
+          <div><strong>实时定位</strong></div>
+          ${rec.locationTime ? `<div>时间:${rec.locationTime}</div>` : ''}
+          ${rec.address ? `<div>地点:${rec.address}</div>` : ''}
+          <div>经度:${lng.toFixed(6)},纬度:${lat.toFixed(6)}</div>
+        </div>`
+      if (AMap.InfoWindow) {
+        const info = new AMap.InfoWindow({
+          content: infoContent,
+          offset: new AMap.Pixel(0, -30)
+        })
+        marker.on &&
+          marker.on('click', () => {
+            if (amap) info.open(amap, marker.getPosition())
+          })
+      }
+    } catch (err) {
+      console.warn('实时点信息窗创建失败(忽略):', err)
+    }
+
+    markers.unshift(marker)
+
+    // 限制最多100条,同时删除最晚的覆盖物(尾部)
     if (historyPoints.length > 100) {
-      historyPoints.shift()
-      const removed = markers.shift()
+      historyPoints.pop()
+      const removed = markers.pop()
       try {
         if (removed) {
           if (typeof amap.remove === 'function') amap.remove(removed)
@@ -533,9 +556,8 @@ const addRealtimePoint = async (pt: {
     // 更新轨迹与右侧列表
     updatePolyline()
     syncDisplayPoints()
-    scrollListToBottom()
 
-    // 适配视野或居中到最新点
+    // 将视野适配到轨迹或居中到最新点
     try {
       if (amap?.setFitView && polyline) {
         amap.setFitView([polyline, ...markers])

+ 8 - 24
src/views/Home/components/HandleWarningDialog.vue

@@ -54,28 +54,17 @@
           </div>
         </el-form-item>
 
-        <!-- <el-form-item
-          v-if="form.handleType === 'report' || isReportOnly"
-          label="上报类型"
-          prop="message"
-          :rules="[{ required: true, message: '请选择上报类型', trigger: 'blur' }]"
-        >
-          <el-select v-model="form.eventType" placeholder="请选择上报类型">
-            <el-option label="SOS_报警" value="SOS_报警" />
-            <el-option label="健康指标异常" value="健康指标异常" />
-          </el-select>
-        </el-form-item> -->
+        <!-- 上报信息 - 电话回访和上报模式都显示 -->
         <el-form-item
-          v-if="form.handleType === 'report' || isReportOnly"
           label="上报信息"
           prop="message"
-          :rules="[{ required: true, message: '请输入上报信息', trigger: 'blur' }]"
+          :rules="[{ required: form.handleType === 'report' || isReportOnly, message: '请输入上报信息', trigger: 'blur' }]"
         >
           <el-input
             v-model="form.message"
             type="textarea"
             :rows="4"
-            placeholder="请输入上报信息"
+            :placeholder="form.handleType === 'phone' ? '选填:补充上报信息(如有需要)' : '请输入上报信息'"
             maxlength="200"
             show-word-limit
           />
@@ -84,9 +73,7 @@
     </div>
     <template #footer>
       <el-button @click="closeDialog" size="large">取消</el-button>
-      <el-button v-if="form.handleType === 'report'" type="primary" @click="submit" size="large"
-        >确认处理</el-button
-      >
+      <el-button type="primary" @click="submit" size="large">确认处理</el-button>
     </template>
   </el-dialog>
 </template>
@@ -135,7 +122,7 @@ watch(
   (val) => {
     if (val) {
       // form.eventType = ''
-      form.message = ''
+      form.message = '已沟通,已解决'
       form.handleType = isReportOnly.value ? 'report' : 'phone'
       nextTick(() => {
         if (handleWarningFormRef.value) {
@@ -154,16 +141,13 @@ const submit = async () => {
   if (!handleWarningFormRef.value) return
 
   try {
-    // 如果是上报,需要验证消息
-    if (form.handleType === 'report' || isReportOnly.value) {
-      const valid = await handleWarningFormRef.value.validate()
-      if (!valid) return
-    }
+    // 验证表单
+    const valid = await handleWarningFormRef.value.validate()
+    if (!valid) return
 
     emit('submit', {
       elderId: props.currentElderly?.id,
       handleType: isReportOnly.value ? 'report' : form.handleType,
-      // eventType: form.eventType,
       message: form.message
     })
 

+ 6 - 4
src/views/Home/components/StatsCard.vue

@@ -56,7 +56,8 @@ $accent-color: #7b61ff;
 
 .stats-grid-large {
   display: grid;
-  grid-template-columns: repeat(4, 1fr);
+  // grid-template-columns: repeat(4, 1fr);
+  grid-template-columns: repeat(3, 1fr);
   gap: 15px;
   margin-bottom: 20px;
 }
@@ -69,6 +70,7 @@ $accent-color: #7b61ff;
   border-radius: 16px;
   transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
   align-items: center;
+  justify-content: center;
   gap: 20px;
 
   &:hover {
@@ -102,9 +104,9 @@ $accent-color: #7b61ff;
   }
 }
 
-.stat-content-large {
-  flex: 1;
-}
+// .stat-content-large {
+//   flex: 1;
+// }
 
 .stat-value-large {
   margin-bottom: 5px;

+ 4 - 7
src/views/Home/components/TopInfoBar.vue

@@ -7,9 +7,10 @@
     <div class="time-display">
       <div class="current-date">{{ currentDate }}</div>
       <div class="current-time">{{ currentTime }}</div>
-      <button class="fullscreen-btn" @click="toggleFullScreen">
+      <!-- <button class="fullscreen-btn" @click="toggleFullScreen">
         <Icon icon="ep:full-screen" />
-      </button>
+      </button> -->
+      <Setting />
     </div>
   </div>
 </template>
@@ -17,6 +18,7 @@
 <script lang="ts" setup>
 import { ref, onMounted, onUnmounted } from 'vue'
 import { ElMessage } from 'element-plus'
+import { Setting } from '@/layout/components/Setting'
 
 interface Props {
   tenantName: string
@@ -155,8 +157,3 @@ $text-gray: #8a8f98;
   }
 }
 </style>
-
-
-
-
-

+ 38 - 33
src/views/Home/home-refactored.vue

@@ -325,6 +325,7 @@ const largeScreenStats = ref<LargeScreenStat[]>([
     label: '老人数量',
     trend: 'up',
     change: '',
+    clickable: true,
     indicator: 'elderCount'
   },
   {
@@ -336,14 +337,14 @@ const largeScreenStats = ref<LargeScreenStat[]>([
     clickable: true,
     indicator: 'deviceCount'
   },
-  {
-    icon: 'mdi:shield-check',
-    value: 0,
-    label: '在线设备',
-    trend: 'up',
-    change: '100%',
-    indicator: 'onlineCount'
-  },
+  // {
+  //   icon: 'mdi:shield-check',
+  //   value: 0,
+  //   label: '在线设备',
+  //   trend: 'up',
+  //   change: '100%',
+  //   indicator: 'onlineCount'
+  // },
   {
     icon: 'mdi:alert-decagram',
     value: 0,
@@ -472,31 +473,30 @@ const openHandleWarningDialog = (elderly: Elderly) => {
 }
 
 const submitHandleWarning = async (data: any) => {
-  if (data.handleType === 'report') {
-    let params = {
-      elderId: data.elderId,
-      remarks: data.message
-    }
-    try {
-      const res = await fetchHttp.post('/api/pc/admin/processAlert', params, {
-        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('处理失败,请重试')
+  // 电话回访与上报告警均可确认处理,并上送 elderId 与上报信息(remarks)
+  const params = {
+    elderId: data.elderId,
+    remarks: data.message || ''
+  }
+  try {
+    const res = await fetchHttp.post('/api/pc/admin/processAlert', params, {
+      headers: {
+        Authorization: `Bearer ${getAccessToken()}`
+      }
+    })
+    if (res) {
+      ElMessage.success(data.handleType === 'report' ? '告警情况已上报!' : '电话回访已确认!')
+      clearWarningFlag(data.elderId)
+      handleWarningDialogVisible.value = false
+      if (selectedElderly.value.id === data.elderId) {
+        await getElderDeviceMessage(selectedElderly.value.id)
       }
-    } catch (error) {
-      console.error('处理告警失败:', error)
-      ElMessage.error('处理告警时出现错误')
+    } else {
+      ElMessage.error('处理失败,请重试')
     }
+  } catch (error) {
+    console.error('处理告警失败:', error)
+    ElMessage.error('处理告警时出现错误')
   }
 }
 
@@ -514,6 +514,11 @@ const showDeviceDetail = async (device: DetailDevice) => {
 }
 
 const handleStatCardClick = (stat: LargeScreenStat) => {
+  // 点击老人数量:回到总览视图
+  if (stat.indicator === 'elderCount') {
+    backToOverview()
+    return
+  }
   // 点击设备总数:切换到全部设备视图
   if (stat.indicator === 'deviceCount') {
     openAllDevicesView()
@@ -1210,7 +1215,7 @@ $bg-accent-2: rgb(123 97 255 / 25%);
   border: none;
   border-radius: 50%;
   color: #fff;
-  background: rgb(26 31 46 / 90%);
+  background: var(--el-color-warning);
   box-shadow: 0 6px 18px rgb(0 0 0 / 35%);
   backdrop-filter: blur(6px);
   z-index: 999;
@@ -1220,7 +1225,7 @@ $bg-accent-2: rgb(123 97 255 / 25%);
 .realtime-fab:hover {
   transform: translateY(-2px);
   box-shadow: 0 10px 26px rgb(0 0 0 / 45%);
-  background: rgb(26 115 232 / 30%);
+  background: #ffb74d;
 }
 
 .realtime-fab:active {

+ 4 - 4
src/views/Login/Login.vue

@@ -13,7 +13,7 @@
     >
     </div>
     <div class="flex items-center text-white mt-40px ml-50px" style="position: absolute">
-      <img alt="" class="mr-10px h-40px" src="@/assets/imgs/logo5.png" />
+      <!-- <img alt="" class="mr-10px h-40px" src="@/assets/imgs/logo5.png" /> -->
     </div>
 
     <div style="position: absolute; width: 100%; height: 100%; overflow: auto">
@@ -27,7 +27,7 @@
             <span
               class="text-40px font-bold"
               style="margin-left: 2vw; letter-spacing: 10px; color: #fff"
-              >欢迎来到颐年智慧医养数字平台</span
+              >欢迎来到智慧养老监控系统</span
             >
           </div>
           <div
@@ -36,7 +36,7 @@
             style="padding: 0 4vw; margin-top: 20vh; justify-content: center; align-items: center"
           >
             <span class="text-50px font-bold" style="letter-spacing: 10px; color: #333"
-              >欢迎来到颐年医养</span
+              >欢迎来到智慧养老监控系统</span
             >
             <div class="column">
               <span class="text-25px font-bold">智慧康养SaaS管理平台,以科技赋能养老</span>
@@ -286,7 +286,7 @@ onMounted(() => {
     heightScreen.value = heightScreen.value * 0.756
   }
 
-  titleWidth.value = getTextWidthWithLetterSpacingDOM('欢迎来到颐年医养', '61px Arial', 10)
+  titleWidth.value = getTextWidthWithLetterSpacingDOM('欢迎来到智慧养老监控系统', '61px Arial', 10)
   console.log('字体宽度', titleWidth.value)
 
   getLoginFormCache()