liujintao 1 月之前
父节点
当前提交
8bb0ef77ca
共有 27 个文件被更改,包括 764 次插入432 次删除
  1. 2 1
      components.d.ts
  2. 10 0
      src/api/remote.ts
  3. 183 136
      src/api/setting.ts
  4. 8 0
      src/api/types/setting.ts
  5. 4 0
      src/utils/request.ts
  6. 1 1
      src/views/login/index.vue
  7. 31 12
      src/views/remoteViewing/index.vue
  8. 34 4
      src/views/settings/alarmSettings/components/humanDetection/index.vue
  9. 37 4
      src/views/settings/alarmSettings/components/intrusionDetection/index.vue
  10. 34 4
      src/views/settings/alarmSettings/components/lineCrossingDetection/index.vue
  11. 34 5
      src/views/settings/alarmSettings/components/motionDetection/index.vue
  12. 13 13
      src/views/settings/audioVideo/components/nightVisionIlluminator/index.vue
  13. 1 1
      src/views/settings/audioVideo/components/video/index.vue
  14. 9 5
      src/views/settings/imageDisplay/components/image/index.vue
  15. 8 4
      src/views/settings/imageDisplay/components/osd/index.vue
  16. 28 5
      src/views/settings/imageDisplay/components/privacyMasking/index.vue
  17. 3 3
      src/views/settings/imageDisplay/index.vue
  18. 2 3
      src/views/settings/netSettings/components/networkDiagnostics/index.vue
  19. 97 69
      src/views/settings/systemSettings/components/cameraInfo/index.vue
  20. 29 29
      src/views/settings/systemSettings/components/passwordManagement/index.vue
  21. 30 29
      src/views/settings/systemSettings/components/systemMaintenance/index.vue
  22. 68 17
      src/views/settings/systemSettings/components/systemUpgrade/index.vue
  23. 49 45
      src/views/settings/systemSettings/components/timeSettings/index.vue
  24. 4 4
      src/views/settings/systemSettings/index.vue
  25. 22 23
      src/views/standardProtocol/components/onvif/index.vue
  26. 21 13
      src/views/standardProtocol/components/rtsp/index.vue
  27. 2 2
      src/views/standardProtocol/index.vue

+ 2 - 1
components.d.ts

@@ -8,10 +8,11 @@ export {}
 declare module 'vue' {
 declare module 'vue' {
   export interface GlobalComponents {
   export interface GlobalComponents {
     DefenseTimeSchedule: typeof import('./src/components/DefenseTimeSchedule/index.vue')['default']
     DefenseTimeSchedule: typeof import('./src/components/DefenseTimeSchedule/index.vue')['default']
-    ElAlert: typeof import('element-plus/es')['ElAlert']
     ElAside: typeof import('element-plus/es')['ElAside']
     ElAside: typeof import('element-plus/es')['ElAside']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
+    ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElForm: typeof import('element-plus/es')['ElForm']

+ 10 - 0
src/api/remote.ts

@@ -0,0 +1,10 @@
+import { request } from '@/utils/request'
+
+
+// 远程查看
+export function getRemoteView() {
+    return request<any>({
+        url: `/API/V1.0/Device/RemoteView`,
+        method: 'get'
+    })
+}

+ 183 - 136
src/api/setting.ts

@@ -1,218 +1,265 @@
-import { request } from '@/utils/request'
+import {request} from '@/utils/request'
 import type * as Setting from './types/setting'
 import type * as Setting from './types/setting'
-import type { TimeParaData } from './types/setting'
+import type {TimeParaData} from './types/setting'
 
 
 export function getUserSettingApi(NIC: number) {
 export function getUserSettingApi(NIC: number) {
-  return request<Setting.GetSettingResponseData>({
-    url: `API/V1.0/Network/NetworkV4Para?NIC=${NIC}`,
-    method: 'get'
-  })
+    return request({
+        url: `API/V1.0/Network/NetworkV4Para?NIC=${NIC}`,
+        method: 'get'
+    })
 }
 }
 
 
 export function putUserSettingApi(
 export function putUserSettingApi(
-  NIC: number,
-  data: Setting.UpdateSettingRequestData
+    NIC: number,
+    data: Setting.UpdateSettingRequestData
 ) {
 ) {
-  return request({
-    url: `API/V1.0/Network/NetworkV4Para?NIC=${NIC}`,
-    method: 'put',
-    data
-  })
+    return request({
+        url: `API/V1.0/Network/NetworkV4Para?NIC=${NIC}`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 //查询/设置  系统时间
 //查询/设置  系统时间
 export function GetTimePara() {
 export function GetTimePara() {
-  return request<{ data: Setting.TimeParaResponse }>({
-    url: `API/V1.0/System/TimePara`,
-    method: 'get'
-  })
+    return request<{ data: Setting.TimeParaResponse }>({
+        url: `API/V1.0/System/TimePara`,
+        method: 'get'
+    })
 }
 }
 
 
 
 
 export function PutTimePara(data: TimeParaData) {
 export function PutTimePara(data: TimeParaData) {
-  return request<{ data: string }>({
-    url: `API/V1.0/System/TimePara`,
-    method: 'put',
-    data
-  })
+    return request<{ data: string }>({
+        url: `API/V1.0/System/TimePara`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 
 
 export function cameraReset(data: any) {
 export function cameraReset(data: any) {
-  return request({
-    url: `/API/V1.0/Device/ResetReboot`,
-    method: 'PUT',
-    data
-  })
+    return request({
+        url: `/API/V1.0/Device/ResetReboot`,
+        method: 'PUT',
+        data
+    })
 }
 }
 
 
 export function cameraResetGet() {
 export function cameraResetGet() {
-  return request({
-    url: `/API/V1.0/Device/ResetReboot`,
-    method: 'get',
-  })
+    return request({
+        url: `/API/V1.0/Device/ResetReboot`,
+        method: 'get',
+    })
 }
 }
 
 
 export function getCameraVolume() {
 export function getCameraVolume() {
-  return request({
-    url: `/API/V1.0/Audio/AudioPara`,
-    method: 'get'
-  })
+    return request({
+        url: `/API/V1.0/Audio/AudioPara`,
+        method: 'get'
+    })
 }
 }
 
 
 
 
 export function cameraVolume(data: any) {
 export function cameraVolume(data: any) {
-  return request({
-    url: `/API/V1.0/Audio/AudioPara`,
-    method: 'put',
-    data
-  })
-}
-
-
-export function getCameraAlarm() {
-  return request({
-    url: `/API/V1.0/Alarm/AlarmPara`,
-    method: 'get'
-  })
+    return request({
+        url: `/API/V1.0/Audio/AudioPara`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 
 
 export function cameraAlarm(data: any) {
 export function cameraAlarm(data: any) {
-  return request({
-    url: `/API/V1.0/Alarm/AlarmPara`,
-    method: 'put',
-    data
-  })
-}
-
-
-export function getCameraNightMode() {
-  return request({
-    url: `/API/V1.0/Video/NightPara`,
-    method: 'get'
-  })
-}
-
-export function cameraNightMode(data: any) {
-  return request({
-    url: `/API/V1.0/Video/NightPara`,
-    method: 'put',
-    data
-  })
+    return request({
+        url: `/API/V1.0/Alarm/AlarmPara`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 
 
 export function getCameraDeviceInfo() {
 export function getCameraDeviceInfo() {
-  return request({
-    url: `/API/V1.0/DeviceInfo`,
-    method: 'get'
-  })
+    return request<{ data: Setting.CameraDeviceInfo }>({
+        url: `/API/V1.0/DeviceInfo`,
+        method: 'get'
+    })
 }
 }
 
 
-// export function cameraDeviceInfo(data: any) {
-//   return request({
-//     url: `/API/V1.0/DeviceInfo`,
-//     method: 'put',
-//     data
-//   })
-// }
-
 
 
 export function cameraResetPassword(data: any) {
 export function cameraResetPassword(data: any) {
-  return request({
-    url: `/API/V1.0/Device/ResetPassword`,
-    method: 'put',
-    data
-  })
+    return request({
+        url: `/API/V1.0/Device/ResetPassword`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 // 图像参数
 // 图像参数
 export function getImagePara() {
 export function getImagePara() {
-  return request<any>({
-    url: `/API/V1.0/Video/ImagePara`,
-    method: 'get'
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/Image`,
+        method: 'get'
+    })
 }
 }
 
 
 export function putImagePara(data: any) {
 export function putImagePara(data: any) {
-  return request<any>({
-    url: `/API/V1.0/Video/ImagePara`,
-    method: 'put',
-    data
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/Image`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 // 隐私遮挡
 // 隐私遮挡
 export function getPrivacyMask() {
 export function getPrivacyMask() {
-  return request<any>({
-    url: `/API/V1.0/Video/PrivacyMask`,
-    method: 'get'
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/PrivacyMask`,
+        method: 'get'
+    })
 }
 }
 
 
 export function putPrivacyMask(data: any) {
 export function putPrivacyMask(data: any) {
-  return request<any>({
-    url: `/API/V1.0/Video/PrivacyMask`,
-    method: 'put',
-    data
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/PrivacyMask`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 // OSD设置
 // OSD设置
 export function getOsdPara() {
 export function getOsdPara() {
-  return request<any>({
-    url: `/API/V1.0/Video/OsdPara`,
-    method: 'get'
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/OsdPosiTion`,
+        method: 'get'
+    })
 }
 }
 
 
 export function putOsdPara(data: any) {
 export function putOsdPara(data: any) {
-  return request<any>({
-    url: `/API/V1.0/Video/OsdPara`,
-    method: 'put',
-    data
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/OsdPosiTion`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 // 视频编码参数
 // 视频编码参数
 export function getVideoEncodePara() {
 export function getVideoEncodePara() {
-  return request<any>({
-    url: `/API/V1.0/Video/EncodePara`,
-    method: 'get'
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/BitStream`,
+        method: 'get'
+    })
 }
 }
 
 
 export function putVideoEncodePara(data: any) {
 export function putVideoEncodePara(data: any) {
-  return request<any>({
-    url: `/API/V1.0/Video/EncodePara`,
-    method: 'put',
-    data
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/BitStream`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 // 夜视补光参数
 // 夜视补光参数
 export function getNightVisionIlluminator() {
 export function getNightVisionIlluminator() {
-  return request<any>({
-    url: `/API/V1.0/Video/NightVisionPara`,
-    method: 'get'
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/NightPara`,
+        method: 'get'
+    })
 }
 }
 
 
 export function putNightVisionIlluminator(data: any) {
 export function putNightVisionIlluminator(data: any) {
-  return request<any>({
-    url: `/API/V1.0/Video/NightVisionPara`,
-    method: 'put',
-    data
-  })
+    return request<any>({
+        url: `/API/V1.0/Video/NightPara`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
 // 网络诊断(Ping)
 // 网络诊断(Ping)
 export function networkDiagnostics(data: { DomainName: string }) {
 export function networkDiagnostics(data: { DomainName: string }) {
-  return request<any>({
-    url: `/API/V1.0/Network/NetworkDiag`,
-    method: 'put',
-    data
-  })
+    return request<any>({
+        url: `/API/V1.0/Network/NetworkDiag`,
+        method: 'put',
+        data
+    })
+}
+
+// 系统升级
+export function systemUpgrade(data:any) {
+    return request<any>({
+        url: `/API/V1.0/Device/UpDate`,
+        method: 'post',
+        data: data,
+        timeout: 300000 // 5分钟超时,固件上传需要更长时间
+    })
+}
+
+
+// 移动检测
+export function getMotionDetection() {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/MotionDet`,
+        method: 'get'
+    })
+}
+
+export function putMotionDetection(data:any) {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/MotionDet`,
+        method: 'put',
+        data
+    })
 }
 }
 
 
+// 人形侦测
+export function getHumanDetection() {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/HumanDet`,
+        method: 'get'
+    })
+}
+
+export function putHumanDetection(data:any) {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/HumanDet`,
+        method: 'put',
+        data
+    })
+}
+
+//区域侦测
+export function getIntrusionDetection() {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/RegionDet`,
+        method: 'get'
+    })
+}
+
+
+export function putIntrusionDetection(data:any) {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/RegionDet`,
+        method: 'put',
+        data
+    })
+}
+
+//越界侦测
+export function getLineCrossingDetection() {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/CrossLineDet`,
+        method: 'get'
+    })
+}
+
+export function putLineCrossingDetection(data:any) {
+    return request<any>({
+        url: `/API/V1.0/Aidetect/CrossLineDet`,
+        method: 'put',
+        data
+    })
+}
+
+
+
+

+ 8 - 0
src/api/types/setting.ts

@@ -1,3 +1,11 @@
+/** 摄像头设备信息 */
+export interface CameraDeviceInfo {
+  DeviceName: string
+  SoftwareVer: string
+  HardwareVer: string
+  MacAddr: string
+}
+
 export interface UpdateSettingRequestData {
 export interface UpdateSettingRequestData {
   NIC: number
   NIC: number
   enableDHCP?: number
   enableDHCP?: number

+ 4 - 0
src/utils/request.ts

@@ -34,6 +34,10 @@ function createRequest(service: AxiosInstance) {
     }
     }
     // 将默认配置 defaultConfig 和传入的自定义配置 config 进行合并成为 mergeConfig
     // 将默认配置 defaultConfig 和传入的自定义配置 config 进行合并成为 mergeConfig
     const mergeConfig = merge(defaultConfig, config)
     const mergeConfig = merge(defaultConfig, config)
+    // 当 data 为 FormData 时,删除 Content-Type,让浏览器自动设置(含 boundary)
+    if (mergeConfig.data instanceof FormData && mergeConfig.headers) {
+      (mergeConfig.headers as Record<string, any>)['Content-Type'] = undefined
+    }
     return service(mergeConfig)
     return service(mergeConfig)
   }
   }
 }
 }

+ 1 - 1
src/views/login/index.vue

@@ -222,7 +222,7 @@ const handleLogin = () => {
   }
   }
 
 
   .login-card {
   .login-card {
-    width: 480px;
+    width: 500px;
     border-radius: 12px;
     border-radius: 12px;
     box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
     box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
     background-color: #fff;
     background-color: #fff;

+ 31 - 12
src/views/remoteViewing/index.vue

@@ -12,7 +12,7 @@
         <div class="status-content">
         <div class="status-content">
           <label>Status</label>
           <label>Status</label>
           <span class="status-badge" :class="{ 'online': isOnline, 'offline': !isOnline }">
           <span class="status-badge" :class="{ 'online': isOnline, 'offline': !isOnline }">
-            {{ deviceStatus }}
+            {{ OnlineStatus ? 'Online' : 'Offline' }}
           </span>
           </span>
         </div>
         </div>
         <button
         <button
@@ -29,7 +29,7 @@
       <div class="info-item">
       <div class="info-item">
         <label>Device Serial Number</label>
         <label>Device Serial Number</label>
         <div class="code-display">
         <div class="code-display">
-          <span>{{ deviceCode }}</span>
+          <span>{{ DeviceSN }}</span>
           <button class="copy-btn" @click="copyDeviceSN">Copy</button>
           <button class="copy-btn" @click="copyDeviceSN">Copy</button>
         </div>
         </div>
       </div>
       </div>
@@ -38,7 +38,7 @@
       <div class="info-item">
       <div class="info-item">
         <label>QR Code</label>
         <label>QR Code</label>
         <div class="qrcode-box">
         <div class="qrcode-box">
-          <QrcodeVue :value="qrCodeValue" :size="150" />
+          <QrcodeVue :value="DeviceSN" :size="150" />
         </div>
         </div>
       </div>
       </div>
 
 
@@ -65,21 +65,40 @@
 import { ref, onMounted } from 'vue'
 import { ref, onMounted } from 'vue'
 import { Refresh } from '@element-plus/icons-vue'
 import { Refresh } from '@element-plus/icons-vue'
 import QrcodeVue from 'qrcode.vue'
 import QrcodeVue from 'qrcode.vue'
+import {getRemoteView} from '@/api/remote'
+import {ElMessage} from 'element-plus'
+
+const OnlineStatus = ref(false) // true:在线 false:不在线
+const isOnline = ref(false)
+const DeviceSN = ref('')
 
 
-const deviceStatus = ref('Online')
-const isOnline = ref(true)
 const isRefreshing = ref(false)
 const isRefreshing = ref(false)
 const showNotification = ref(false)
 const showNotification = ref(false)
 const notificationMessage = ref('')
 const notificationMessage = ref('')
-const deviceCode = ref('0314153000122700030900')
-const qrCodeValue = ref('0314153000122700030900')
+
+
+function getRemoteViewInfo() {
+  getRemoteView().then(resp => {
+    if (resp.data) {
+      OnlineStatus.value = resp.data.OnlineStatus
+      isOnline.value = resp.data.OnlineStatus
+      DeviceSN.value = resp.data.DeviceSN
+    } else {
+      ElMessage.error('Failed to get remote view info')
+    }
+  })
+}
 
 
 const refreshStatus = async () => {
 const refreshStatus = async () => {
   isRefreshing.value = true
   isRefreshing.value = true
   try {
   try {
-    await new Promise(resolve => setTimeout(resolve, 1500))
-    isOnline.value = true
-    deviceStatus.value = 'Online'
+    // 使用 Promise 包装 setTimeout 以确保正确的异步处理
+    await new Promise(resolve => {
+      setTimeout(async () => {
+        await getRemoteViewInfo()
+        resolve()
+      }, 1000)
+    })
     notificationMessage.value = 'Device status refreshed'
     notificationMessage.value = 'Device status refreshed'
     showNotification.value = true
     showNotification.value = true
     setTimeout(() => {
     setTimeout(() => {
@@ -98,7 +117,7 @@ const refreshStatus = async () => {
 
 
 const copyDeviceSN = async () => {
 const copyDeviceSN = async () => {
   try {
   try {
-    await navigator.clipboard.writeText(deviceCode.value)
+    await navigator.clipboard.writeText(DeviceSN.value)
     notificationMessage.value = 'Copied to clipboard'
     notificationMessage.value = 'Copied to clipboard'
     showNotification.value = true
     showNotification.value = true
     setTimeout(() => {
     setTimeout(() => {
@@ -114,7 +133,7 @@ const copyDeviceSN = async () => {
 }
 }
 
 
 onMounted(() => {
 onMounted(() => {
-  // API call location
+  getRemoteViewInfo()
 })
 })
 </script>
 </script>
 
 

+ 34 - 4
src/views/settings/alarmSettings/components/humanDetection/index.vue

@@ -23,7 +23,7 @@
       <el-checkbox v-model="form.AudioAlarm">Sound Alarm</el-checkbox>
       <el-checkbox v-model="form.AudioAlarm">Sound Alarm</el-checkbox>
     </div>
     </div>
 
 
-    <el-button type="primary" round @click="handleSave">Save</el-button>
+    <el-button type="primary" :loading="loading" round @click="handleSave">Save</el-button>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -32,6 +32,9 @@ import {reactive, computed} from 'vue'
 import {ElMessage} from 'element-plus'
 import {ElMessage} from 'element-plus'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
+import {getHumanDetection, getMotionDetection, putHumanDetection} from '@/api/setting'
+
+const loading = ref(false)
 
 
 const form = reactive<{
 const form = reactive<{
   Enable: boolean
   Enable: boolean
@@ -48,6 +51,20 @@ const form = reactive<{
   AudioAlarm: false
   AudioAlarm: false
 })
 })
 
 
+function getHumanDetectionInfo() {
+  getHumanDetection().then(resp => {
+    form.Enable = resp.data.Enable
+    form.Sensitivity = resp.data.Sensitivity
+    form.schedule = resp.data.DailyArmMasks
+    form.AudioAlarm = resp.data.AudioAlarm
+  })
+}
+
+
+onMounted(() => {
+  getHumanDetectionInfo()
+})
+
 // 灵敏度文字映射
 // 灵敏度文字映射
 const sensitivityLabel = computed(() => {
 const sensitivityLabel = computed(() => {
   const val = form.Sensitivity
   const val = form.Sensitivity
@@ -57,15 +74,28 @@ const sensitivityLabel = computed(() => {
 })
 })
 
 
 function handleSave() {
 function handleSave() {
-  // TODO: 调用接口保存配置
   const payload = {
   const payload = {
     Enable: form.Enable,
     Enable: form.Enable,
     Sensitivity: form.Sensitivity,
     Sensitivity: form.Sensitivity,
     DailyArmMasks: {...form.schedule},
     DailyArmMasks: {...form.schedule},
     AudioAlarm: form.AudioAlarm
     AudioAlarm: form.AudioAlarm
   }
   }
-  console.log('Save motion detection config:', payload)
-  ElMessage.success('Saved successfully')
+  try {
+    loading.value = true
+    putHumanDetection(payload).then(resp => {
+      if (resp.data === 'ok\n') {
+        ElMessage.success('Saved successfully')
+        getHumanDetectionInfo()
+      } else {
+        ElMessage.error("Save failed")
+      }
+    })
+  } catch (e) {
+    console.error(e)
+    ElMessage.error("Save failed")
+  } finally {
+    loading.value = false
+  }
 }
 }
 </script>
 </script>
 
 

+ 37 - 4
src/views/settings/alarmSettings/components/intrusionDetection/index.vue

@@ -38,7 +38,7 @@
       <el-checkbox v-model="form.AudioAlarmLeave">Sound Alarm on Leave</el-checkbox>
       <el-checkbox v-model="form.AudioAlarmLeave">Sound Alarm on Leave</el-checkbox>
     </div>
     </div>
 
 
-    <el-button type="primary" round @click="handleSave">Save</el-button>
+    <el-button type="primary" :loading="loading" round @click="handleSave">Save</el-button>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -47,6 +47,9 @@ import {reactive, computed} from 'vue'
 import {ElMessage} from 'element-plus'
 import {ElMessage} from 'element-plus'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
+import {getIntrusionDetection, getMotionDetection, putIntrusionDetection} from '@/api/setting'
+
+const loading = ref(false)
 
 
 const form = reactive<{
 const form = reactive<{
   Enable: boolean
   Enable: boolean
@@ -83,8 +86,24 @@ const sensitivityLabel = computed(() => {
   return 'High'
   return 'High'
 })
 })
 
 
+function getIntrusionDetectionInfo() {
+  getIntrusionDetection().then(resp => {
+    form.Enable = resp.data.Enable
+    form.Sensitivity = resp.data.Sensitivity
+    form.TriggerMode = resp.data.TriggerMode
+    form.LightFlashEn = resp.data.LightFlashEn
+    form.schedule = resp.data.DailyArmMasks
+    form.AudioAlarmEnter = resp.data.AudioAlarmEnter
+    form.AudioAlarmLeave = resp.data.AudioAlarmLeave
+  })
+}
+
+
+onMounted(() => {
+  getIntrusionDetectionInfo()
+})
+
 function handleSave() {
 function handleSave() {
-  // TODO: 调用接口保存配置
   const payload = {
   const payload = {
     Enable: form.Enable,
     Enable: form.Enable,
     Sensitivity: form.Sensitivity,
     Sensitivity: form.Sensitivity,
@@ -94,8 +113,22 @@ function handleSave() {
     LightFlashEn: form.LightFlashEn,
     LightFlashEn: form.LightFlashEn,
     TriggerMode: form.TriggerMode,
     TriggerMode: form.TriggerMode,
   }
   }
-  console.log('Save motion detection config:', payload)
-  ElMessage.success('Saved successfully')
+  try {
+    loading.value = true
+    putIntrusionDetection(payload).then(resp => {
+      if (resp.data === 'ok\n') {
+        ElMessage.success('Saved successfully')
+        getIntrusionDetectionInfo()
+      } else {
+        ElMessage.error("Save failed")
+      }
+    })
+  } catch (e) {
+    console.error(e)
+    ElMessage.error("Save failed")
+  } finally {
+    loading.value = false
+  }
 }
 }
 </script>
 </script>
 
 

+ 34 - 4
src/views/settings/alarmSettings/components/lineCrossingDetection/index.vue

@@ -28,7 +28,7 @@
       <el-checkbox v-model="form.AudioAlarmBA">Sound Alarm on B→A</el-checkbox>
       <el-checkbox v-model="form.AudioAlarmBA">Sound Alarm on B→A</el-checkbox>
     </div>
     </div>
 
 
-    <el-button type="primary" round @click="handleSave">Save</el-button>
+    <el-button type="primary" :loading="loading" round @click="handleSave">Save</el-button>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -37,6 +37,9 @@ import {reactive, computed} from 'vue'
 import {ElMessage} from 'element-plus'
 import {ElMessage} from 'element-plus'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
+import {getLineCrossingDetection, putLineCrossingDetection} from '@/api/setting'
+
+const loading = ref(false)
 
 
 const form = reactive<{
 const form = reactive<{
   Enable: boolean
   Enable: boolean
@@ -57,6 +60,22 @@ const form = reactive<{
   AudioAlarmBA: false,
   AudioAlarmBA: false,
 })
 })
 
 
+function getLineCrossingDetectionInfo() {
+  getLineCrossingDetection().then(resp => {
+    form.Enable = resp.data.Enable
+    form.Sensitivity = resp.data.Sensitivity
+    form.LightFlashEn = resp.data.LightFlashEn
+    form.schedule = resp.data.DailyArmMasks
+    form.AudioAlarmAB = resp.data.AudioAlarmAB
+    form.AudioAlarmBA = resp.data.AudioAlarmBA
+  })
+}
+
+
+onMounted(() => {
+  getLineCrossingDetectionInfo()
+})
+
 // 灵敏度文字映射
 // 灵敏度文字映射
 const sensitivityLabel = computed(() => {
 const sensitivityLabel = computed(() => {
   const val = form.Sensitivity
   const val = form.Sensitivity
@@ -66,7 +85,6 @@ const sensitivityLabel = computed(() => {
 })
 })
 
 
 function handleSave() {
 function handleSave() {
-  // TODO: 调用接口保存配置
   const payload = {
   const payload = {
     Enable: form.Enable,
     Enable: form.Enable,
     Sensitivity: form.Sensitivity,
     Sensitivity: form.Sensitivity,
@@ -75,8 +93,20 @@ function handleSave() {
     AudioAlarmBA: form.AudioAlarmBA,
     AudioAlarmBA: form.AudioAlarmBA,
     LightFlashEn: form.LightFlashEn,
     LightFlashEn: form.LightFlashEn,
   }
   }
-  console.log('Save motion detection config:', payload)
-  ElMessage.success('Saved successfully')
+  loading.value = true
+  putLineCrossingDetection(payload).then(resp => {
+    if (resp.data === 'ok\n') {
+      ElMessage.success('Saved successfully')
+      getLineCrossingDetectionInfo()
+    } else {
+      ElMessage.error("Save failed")
+    }
+  }).catch(e => {
+    console.error(e)
+    ElMessage.error("Save failed")
+  }).finally(() => {
+    loading.value = false
+  })
 }
 }
 </script>
 </script>
 
 

+ 34 - 5
src/views/settings/alarmSettings/components/motionDetection/index.vue

@@ -23,7 +23,7 @@
       <el-checkbox v-model="form.AudioAlarm">Sound Alarm</el-checkbox>
       <el-checkbox v-model="form.AudioAlarm">Sound Alarm</el-checkbox>
     </div>
     </div>
 
 
-    <el-button type="primary" round @click="handleSave">Save</el-button>
+    <el-button type="primary" :loading="loading" round @click="handleSave">Save</el-button>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -32,6 +32,9 @@ import {reactive, computed} from 'vue'
 import {ElMessage} from 'element-plus'
 import {ElMessage} from 'element-plus'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import DefenseTimeSchedule from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
 import type {DailyArmMasks} from '@/components/DefenseTimeSchedule/index.vue'
+import {getMotionDetection, putMotionDetection} from '@/api/setting'
+
+const loading = ref(false)
 
 
 const form = reactive<{
 const form = reactive<{
   Enable: boolean
   Enable: boolean
@@ -48,6 +51,20 @@ const form = reactive<{
   AudioAlarm: false
   AudioAlarm: false
 })
 })
 
 
+function getMotionDetectionInfo() {
+  getMotionDetection().then(resp => {
+    form.Enable = resp.data.Enable
+    form.Sensitivity = resp.data.Sensitivity
+    form.schedule = resp.data.DailyArmMasks
+    form.AudioAlarm = resp.data.AudioAlarm
+  })
+}
+
+
+onMounted(() => {
+  getMotionDetectionInfo()
+})
+
 // 灵敏度文字映射
 // 灵敏度文字映射
 const sensitivityLabel = computed(() => {
 const sensitivityLabel = computed(() => {
   const val = form.Sensitivity
   const val = form.Sensitivity
@@ -56,16 +73,28 @@ const sensitivityLabel = computed(() => {
   return 'High'
   return 'High'
 })
 })
 
 
-function handleSave() {
-  // TODO: 调用接口保存配置
+async function handleSave() {
   const payload = {
   const payload = {
     Enable: form.Enable,
     Enable: form.Enable,
     Sensitivity: form.Sensitivity,
     Sensitivity: form.Sensitivity,
     DailyArmMasks: {...form.schedule},
     DailyArmMasks: {...form.schedule},
     AudioAlarm: form.AudioAlarm
     AudioAlarm: form.AudioAlarm
   }
   }
-  console.log('Save motion detection config:', payload)
-  ElMessage.success('Saved successfully')
+  try {
+    loading.value = true
+    const resp = await putMotionDetection(payload)
+    if (resp.data === 'ok\n') {
+      ElMessage.success('Saved successfully')
+      getMotionDetectionInfo()
+    } else {
+      ElMessage.error("Save failed")
+    }
+  } catch (e) {
+    console.error(e)
+    ElMessage.error("Save failed")
+  } finally {
+    loading.value = false
+  }
 }
 }
 </script>
 </script>
 
 

+ 13 - 13
src/views/settings/audioVideo/components/nightVisionIlluminator/index.vue

@@ -50,12 +50,12 @@
               <div class="form-item__control slider-control">
               <div class="form-item__control slider-control">
                 <!-- <span class="slider-min">0</span> -->
                 <!-- <span class="slider-min">0</span> -->
                 <el-slider
                 <el-slider
-                  v-model="form.FlOnSens"
+                  v-model="form.FIOnSens"
                   :min="0"
                   :min="0"
                   :max="100"
                   :max="100"
                   :show-tooltip="false"
                   :show-tooltip="false"
                 />
                 />
-                <span class="slider-value">{{ form.FlOnSens }}</span>
+                <span class="slider-value">{{ form.FIOnSens }}</span>
                 <!-- <span class="slider-max">100</span> -->
                 <!-- <span class="slider-max">100</span> -->
               </div>
               </div>
             </div>
             </div>
@@ -66,12 +66,12 @@
               <div class="form-item__control slider-control">
               <div class="form-item__control slider-control">
                 <!-- <span class="slider-min">0</span> -->
                 <!-- <span class="slider-min">0</span> -->
                 <el-slider
                 <el-slider
-                  v-model="form.FlOffSens "
+                  v-model="form.FIOffSens "
                   :min="0"
                   :min="0"
                   :max="100"
                   :max="100"
                   :show-tooltip="false"
                   :show-tooltip="false"
                 />
                 />
-                <span class="slider-value">{{ form.FlOffSens  }}</span>
+                <span class="slider-value">{{ form.FIOffSens  }}</span>
                 <!-- <span class="slider-max">100</span> -->
                 <!-- <span class="slider-max">100</span> -->
               </div>
               </div>
             </div>
             </div>
@@ -140,8 +140,8 @@ const form = reactive({
   NightMode: 0,        // 补光方式: 0-全彩夜视 1-黑白夜视 2-智能夜视
   NightMode: 0,        // 补光方式: 0-全彩夜视 1-黑白夜视 2-智能夜视
   AutoLightEn: false,   // 自动调节亮度
   AutoLightEn: false,   // 自动调节亮度
   PwmLight: 50,     // 灯光亮度
   PwmLight: 50,     // 灯光亮度
-  FlOnSens: 50,  // 开灯灵敏度
-  FlOffSens : 50, // 关灯灵敏度
+  FIOnSens: 50,  // 开灯灵敏度
+  FIOffSens : 50, // 关灯灵敏度
   ColorfulMode: 0,      // 夜视模式
   ColorfulMode: 0,      // 夜视模式
   lightOnHour: 0,       // 开灯时间-时
   lightOnHour: 0,       // 开灯时间-时
   lightOnMinute: 0,     // 开灯时间-分
   lightOnMinute: 0,     // 开灯时间-分
@@ -165,8 +165,8 @@ async function resetDefaults() {
   form.NightMode = 0
   form.NightMode = 0
   form.AutoLightEn = false
   form.AutoLightEn = false
   form.PwmLight = 50
   form.PwmLight = 50
-  form.FlOnSens = 50
-  form.FlOffSens  = 50
+  form.FIOnSens = 50
+  form.FIOffSens  = 50
   form.ColorfulMode = 0
   form.ColorfulMode = 0
   form.lightOnHour = 0
   form.lightOnHour = 0
   form.lightOnMinute = 0
   form.lightOnMinute = 0
@@ -184,8 +184,8 @@ async function fetchParams() {
       if (d.NightMode !== undefined) form.NightMode = Number(d.NightMode)
       if (d.NightMode !== undefined) form.NightMode = Number(d.NightMode)
       if (d.AutoLightEn !== undefined) form.AutoLightEn = !!d.AutoLightEn
       if (d.AutoLightEn !== undefined) form.AutoLightEn = !!d.AutoLightEn
       if (d.PwmLight !== undefined) form.PwmLight = Number(d.PwmLight)
       if (d.PwmLight !== undefined) form.PwmLight = Number(d.PwmLight)
-      if (d.FlOnSens !== undefined) form.FlOnSens = Number(d.FlOnSens)
-      if (d.FlOffSens  !== undefined) form.FlOffSens  = Number(d.FlOffSens )
+      if (d.FIOnSens !== undefined) form.FIOnSens = Number(d.FIOnSens)
+      if (d.FIOffSens  !== undefined) form.FIOffSens  = Number(d.FIOffSens )
       if (d.ColorfulMode !== undefined) form.ColorfulMode = Number(d.ColorfulMode)
       if (d.ColorfulMode !== undefined) form.ColorfulMode = Number(d.ColorfulMode)
       // 解析 LightOnTime / LightOffTime 嵌套格式
       // 解析 LightOnTime / LightOffTime 嵌套格式
       if (d.LightOnTime) {
       if (d.LightOnTime) {
@@ -207,10 +207,10 @@ async function saveParams() {
   try {
   try {
     const data = {
     const data = {
       NightMode: form.NightMode,
       NightMode: form.NightMode,
-      AutoLightEn: form.AutoLightEn ? 1 : 0,
+      AutoLightEn: form.AutoLightEn,
       PwmLight: form.PwmLight,
       PwmLight: form.PwmLight,
-      FlOnSens: form.FlOnSens,
-      FlOffSens : form.FlOffSens ,
+      FIOnSens: form.FIOnSens,
+      FIOffSens : form.FIOffSens ,
       ColorfulMode: form.ColorfulMode,
       ColorfulMode: form.ColorfulMode,
       LightOnTime: { tm_hour: form.lightOnHour, tm_min: form.lightOnMinute },
       LightOnTime: { tm_hour: form.lightOnHour, tm_min: form.lightOnMinute },
       LightOffTime: { tm_hour: form.lightOffHour, tm_min: form.lightOffMinute }
       LightOffTime: { tm_hour: form.lightOffHour, tm_min: form.lightOffMinute }

+ 1 - 1
src/views/settings/audioVideo/components/video/index.vue

@@ -297,7 +297,7 @@ onMounted(() => {
 .form-item {
 .form-item {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  margin-bottom: 16px;
+  margin-bottom: 10px;
 
 
   &__label {
   &__label {
     flex: 0 0 120px;
     flex: 0 0 120px;

+ 9 - 5
src/views/settings/imageDisplay/components/image/index.vue

@@ -1,9 +1,9 @@
 <template>
 <template>
   <div class="image-settings">
   <div class="image-settings">
     <!-- 左侧视频预览 -->
     <!-- 左侧视频预览 -->
-    <div class="image-settings__video">
-      <MyVideo :drag-flag="false" />
-    </div>
+  <div class="image-settings__video">
+    <MyVideo ref="myVideoRef" :drag-flag="false" />
+  </div>
 
 
     <!-- 右侧设置面板 -->
     <!-- 右侧设置面板 -->
     <div class="image-settings__panel">
     <div class="image-settings__panel">
@@ -104,6 +104,9 @@ import { ElMessage } from 'element-plus'
 import MyVideo from '@/components/myVideo.vue'
 import MyVideo from '@/components/myVideo.vue'
 import { getImagePara, putImagePara } from '@/api/setting'
 import { getImagePara, putImagePara } from '@/api/setting'
 
 
+// 视频组件引用
+const myVideoRef = ref<InstanceType<typeof MyVideo> | null>(null)
+
 // 基本参数
 // 基本参数
 const basicParams = reactive({
 const basicParams = reactive({
   CscLuma: 50,        // 亮度 
   CscLuma: 50,        // 亮度 
@@ -186,9 +189,10 @@ async function saveParams() {
     }
     }
     const res = await putImagePara(data)
     const res = await putImagePara(data)
     if (res.data === 'ok\n') {
     if (res.data === 'ok\n') {
-      ElMessage.success('设置成功')
+      // ElMessage.success('设置成功')
     }
     }
   } catch (e) {
   } catch (e) {
+    console.log(e)
     ElMessage.warning('设置失败')
     ElMessage.warning('设置失败')
   }
   }
 }
 }
@@ -327,4 +331,4 @@ onMounted(() => {
     }
     }
   }
   }
 }
 }
-</style>
+</style>

+ 8 - 4
src/views/settings/imageDisplay/components/osd/index.vue

@@ -3,7 +3,7 @@
     <!-- 左侧:视频预览 + OSD拖拽 -->
     <!-- 左侧:视频预览 + OSD拖拽 -->
     <div class="osd-settings__left">
     <div class="osd-settings__left">
       <div class="osd-settings__video" ref="videoWrapRef">
       <div class="osd-settings__video" ref="videoWrapRef">
-        <MyVideo :drag-flag="false" />
+        <MyVideo ref="myVideoRef" :drag-flag="false" />
         <!-- OSD叠加层:用于拖拽定位 -->
         <!-- OSD叠加层:用于拖拽定位 -->
         <div class="osd-overlay" ref="overlayRef">
         <div class="osd-overlay" ref="overlayRef">
           <!-- 时间OSD -->
           <!-- 时间OSD -->
@@ -128,6 +128,8 @@ import { Edit } from '@element-plus/icons-vue'
 import MyVideo from '@/components/myVideo.vue'
 import MyVideo from '@/components/myVideo.vue'
 import { getOsdPara, putOsdPara } from '@/api/setting'
 import { getOsdPara, putOsdPara } from '@/api/setting'
 
 
+const myVideoRef = ref<InstanceType<typeof MyVideo> | null>(null)
+
 // 表单数据
 // 表单数据
 const form = reactive({
 const form = reactive({
   EnName: true,
   EnName: true,
@@ -269,6 +271,7 @@ async function saveOsd() {
     }
     }
     const res = await putOsdPara(data)
     const res = await putOsdPara(data)
     if (res.data === 'ok\n') {
     if (res.data === 'ok\n') {
+      myVideoRef.value?.refreshWebSocket()
       ElMessage.success('Saved successfully')
       ElMessage.success('Saved successfully')
     }
     }
   } catch {
   } catch {
@@ -349,8 +352,9 @@ onUnmounted(() => {
   pointer-events: auto;
   pointer-events: auto;
   cursor: move;
   cursor: move;
   padding: 2px 6px;
   padding: 2px 6px;
-  font-size: 12px;
-  color: #fff;
+  font-size: 16px;
+  font-weight: 500;
+  color: #ed0c0c;
   white-space: nowrap;
   white-space: nowrap;
   user-select: none;
   user-select: none;
   border: 1px solid transparent;
   border: 1px solid transparent;
@@ -449,4 +453,4 @@ onUnmounted(() => {
     min-width: 80px;
     min-width: 80px;
   }
   }
 }
 }
-</style>
+</style>

+ 28 - 5
src/views/settings/imageDisplay/components/privacyMasking/index.vue

@@ -3,7 +3,7 @@
     <!-- 左侧:视频 + 底部提示 -->
     <!-- 左侧:视频 + 底部提示 -->
     <div class="privacy-masking__left">
     <div class="privacy-masking__left">
       <div class="privacy-masking__video">
       <div class="privacy-masking__video">
-        <MyVideo :drag-flag="false" />
+        <MyVideo ref="myVideoRef" :drag-flag="false" />
         <canvas
         <canvas
           ref="canvasRef"
           ref="canvasRef"
           class="mask-canvas"
           class="mask-canvas"
@@ -62,10 +62,12 @@
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
 import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import MyVideo from '@/components/myVideo.vue'
 import MyVideo from '@/components/myVideo.vue'
 import { getPrivacyMask, putPrivacyMask } from '@/api/setting'
 import { getPrivacyMask, putPrivacyMask } from '@/api/setting'
 
 
+const myVideoRef = ref<InstanceType<typeof MyVideo> | null>(null)
+
 // 遮挡矩形(canvas 像素坐标)
 // 遮挡矩形(canvas 像素坐标)
 interface MaskRect {
 interface MaskRect {
   x: number
   x: number
@@ -93,12 +95,26 @@ let dragOffsetY = 0
 let tempRect: MaskRect | null = null
 let tempRect: MaskRect | null = null
 
 
 // 颜色映射:颜色名 -> 接口十六进制值
 // 颜色映射:颜色名 -> 接口十六进制值
-const COLOR_MAP: Record<string, string> = {
+const COLOR_MAP = {
   black: 0x000000,
   black: 0x000000,
   red: 0xFF0000
   red: 0xFF0000
 }
 }
 
 
-function setColor(color: 'black' | 'red') {
+async function setColor(color: 'black' | 'red') {
+  // 如果遮罩已满且切换了颜色,询问用户是否清空重新绘制
+  if (masks.length >= MAX_MASKS && color !== maskColor.value) {
+    try {
+      await ElMessageBox.confirm(
+        'All 5 mask regions are in use. Switching color will clear all existing masks so you can redraw. Continue?',
+        'Confirm',
+        { confirmButtonText: 'OK', cancelButtonText: 'Cancel', type: 'warning' }
+      )
+      masks.splice(0, masks.length)
+    } catch {
+      // 用户取消,不做任何操作
+      return
+    }
+  }
   maskColor.value = color
   maskColor.value = color
   drawAll()
   drawAll()
 }
 }
@@ -186,8 +202,14 @@ function onMouseUp() {
 }
 }
 
 
 function clearAllMasks() {
 function clearAllMasks() {
+  const count = masks.length
+  if (count === 0) {
+    ElMessage.info('No mask regions to clear')
+    return
+  }
   masks.splice(0, masks.length)
   masks.splice(0, masks.length)
   drawAll()
   drawAll()
+  ElMessage.success(`Cleared ${count} mask region${count > 1 ? 's' : ''}`)
 }
 }
 
 
 function drawAll(extra?: MaskRect) {
 function drawAll(extra?: MaskRect) {
@@ -270,6 +292,7 @@ async function saveMasks() {
     }
     }
     const res = await putPrivacyMask(data)
     const res = await putPrivacyMask(data)
     if (res.data === 'ok\n') {
     if (res.data === 'ok\n') {
+      // myVideoRef.value?.refreshWebSocket()
       ElMessage.success('Saved successfully')
       ElMessage.success('Saved successfully')
     }
     }
   } catch {
   } catch {
@@ -282,7 +305,7 @@ async function fetchMasks() {
     const res = await getPrivacyMask()
     const res = await getPrivacyMask()
     if (res.data) {
     if (res.data) {
       const d = res.data
       const d = res.data
-      enableMask.value = d.CoverEnable === 1
+      enableMask.value = !! d.CoverEnable
       if (Array.isArray(d.CoverList) && d.CoverList.length > 0) {
       if (Array.isArray(d.CoverList) && d.CoverList.length > 0) {
         // 从第一个区域读取颜色
         // 从第一个区域读取颜色
         maskColor.value = colorFromHex(d.CoverList[0].CoverColor)
         maskColor.value = colorFromHex(d.CoverList[0].CoverColor)

+ 3 - 3
src/views/settings/imageDisplay/index.vue

@@ -2,13 +2,13 @@
   <div class="settings-container">
   <div class="settings-container">
     <el-tabs v-model="activeName" class="tabs">
     <el-tabs v-model="activeName" class="tabs">
       <el-tab-pane label="Image" name="first">
       <el-tab-pane label="Image" name="first">
-        <Image />
+        <Image v-if="activeName === 'first'" />
       </el-tab-pane>
       </el-tab-pane>
       <el-tab-pane label="OSD Settings" name="second">
       <el-tab-pane label="OSD Settings" name="second">
-        <OSD />
+        <OSD v-if="activeName === 'second'" />
       </el-tab-pane>
       </el-tab-pane>
       <el-tab-pane label="Privacy Masking " name="third">
       <el-tab-pane label="Privacy Masking " name="third">
-        <PrivacyMasking />
+        <PrivacyMasking v-if="activeName === 'third'" />
       </el-tab-pane>
       </el-tab-pane>
     </el-tabs>
     </el-tabs>
   </div>
   </div>

+ 2 - 3
src/views/settings/netSettings/components/networkDiagnostics/index.vue

@@ -75,9 +75,8 @@ const handlePing = async () => {
   loading.value = true
   loading.value = true
   result.value = ''
   result.value = ''
   try {
   try {
-    // const res = await networkDiagnostics({ DomainName: host })
-    // result.value = res.data?.NetworkStatus || ''
-    result.value = "PINGwww.baidu.com(183.240.99.224):56databytes\n64bytesfrom183.240.99.224:seq=0ttl=53time=12.822ms\n64bytesfrom183.240.99.224:seq=1ttl=53time=13.056ms\n64bytesfrom183.240.99.224:seq=2ttl=53time=12.501ms\n64bytesfrom183.240.99.224:seq=3ttl=53time=12.116ms\n64bytesfrom183.240.99.224:seq=4ttl=53time=12.488ms\n\n---www.baidu.compingstatistics---\n5packetstransmitted,5packetsreceived,00x62eacketloss\nround-tripmin/avg/max=12.116/12.596/13.056ms\n"
+    const res = await networkDiagnostics({ DomainName: host })
+    result.value = res.data?.NetworkStatus || ''
   } catch {
   } catch {
     ElMessage.error('Diagnosis failed. Please check your network connection.')
     ElMessage.error('Diagnosis failed. Please check your network connection.')
   } finally {
   } finally {

+ 97 - 69
src/views/settings/systemSettings/components/cameraInfo/index.vue

@@ -1,89 +1,117 @@
 <template>
 <template>
-  <div class="setting-container">
-    <div class="content">
-      <el-form
-        ref="formRef"
-        v-loading="loading"
-        label-position="left"
-        label-width="130px"
-        :model="formData"
-      >
-        <el-form-item label="Device Name" prop="DeviceName">
-          <el-input
-            v-model="formData.DeviceName"
-            class="el-input"
-            :disabled="true"
-            type="text"
-          />
-        </el-form-item>
-        <el-form-item label="Software Version" prop="SoftwareVer">
-          <el-input
-            v-model="formData.SoftwareVer"
-            :disabled="true"
-            type="text"
-          />
-        </el-form-item>
-        <el-form-item label="Hardware Version" prop="HardwareVer">
-          <el-input
-            v-model="formData.HardwareVer"
-            :disabled="true"
-            type="text"
-          />
-        </el-form-item>
-        <el-form-item label="MAC Address" prop="MacAddr">
-          <el-input
-            v-model="formData.MacAddr"
-            :disabled="true"
-            type="text"
-          />
-        </el-form-item>
-
-      </el-form>
+  <div class="camera-info">
+    <div class="section" v-loading="loading">
+      <div class="section-header">
+        <h3>Device Information</h3>
+      </div>
+      <div class="section-content">
+        <div class="form-item">
+          <label class="form-label">Device Name</label>
+          <span class="form-value">{{ formData.DeviceName || '-' }}</span>
+        </div>
+        <div class="form-item">
+          <label class="form-label">Software Version</label>
+          <span class="form-value">{{ formData.SoftwareVer || '-' }}</span>
+        </div>
+        <div class="form-item">
+          <label class="form-label">Hardware Version</label>
+          <span class="form-value">{{ formData.HardwareVer || '-' }}</span>
+        </div>
+        <div class="form-item">
+          <label class="form-label">MAC Address</label>
+          <span class="form-value mono">{{ formData.MacAddr || '-' }}</span>
+        </div>
+      </div>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
-import { type FormInstance } from 'element-plus'
 import { getCameraDeviceInfo } from '@/api/setting'
 import { getCameraDeviceInfo } from '@/api/setting'
+import type { CameraDeviceInfo } from '@/api/types/setting'
 
 
-/** 表单元素的引用 */
-const formRef = ref<FormInstance | null>(null)
-
-onMounted(() => {
-  deviceInfo()
+const formData = ref<CameraDeviceInfo>({
+  DeviceName: '',
+  SoftwareVer: '',
+  HardwareVer: '',
+  MacAddr: ''
 })
 })
-
-/** 相机设备信息 */
-export interface CameraDeviceInfo {
-  DeviceName: string
-  SoftwareVer: string
-  HardwareVer: string
-  MacAddr: string
-}
-
-const formData = ref<CameraDeviceInfo>({})
 const loading = ref(false)
 const loading = ref(false)
 
 
-const deviceInfo = () => {
-  getCameraDeviceInfo().then((device) => {
-    formData.value = device.data
-  })
+const fetchDeviceInfo = async () => {
+  loading.value = true
+  try {
+    const res = await getCameraDeviceInfo()
+    formData.value = res.data
+  } catch {
+    ElMessage.error('Failed to get device info')
+  } finally {
+    loading.value = false
+  }
 }
 }
 
 
+onMounted(() => {
+  fetchDeviceInfo()
+})
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-.setting-container {
-  padding: 20px;
-}
+$text-primary: #1f2d3d;
+$text-secondary: #333;
+$divider-color: #f0f0f0;
 
 
-.el-input {
-  width: 210px;
-}
+.camera-info {
+  max-width: 700px;
+  padding: 0;
+
+  .section {
+    padding: 20px;
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
+  }
+
+  .section-header {
+    padding-bottom: 16px;
+    margin-bottom: 16px;
+    border-bottom: 2px solid $divider-color;
+
+    h3 {
+      font-size: 15px;
+      font-weight: 600;
+      color: $text-primary;
+      margin: 0;
+    }
+  }
+
+  .section-content {
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+  }
+
+  .form-item {
+    display: flex;
+    align-items: center;
+    gap: 16px;
+
+    .form-label {
+      min-width: 140px;
+      font-size: 14px;
+      font-weight: 500;
+      color: $text-secondary;
+      flex-shrink: 0;
+    }
+
+    .form-value {
+      font-size: 14px;
+      color: #606266;
 
 
-/* 加深表单label颜色 */
-:deep(.el-form-item__label) {
-  color: #1a1a1a; /* 更深的颜色值 */
+      &.mono {
+        font-family: 'Monaco', 'Courier New', monospace;
+        letter-spacing: 0.5px;
+      }
+    }
+  }
 }
 }
 </style>
 </style>

+ 29 - 29
src/views/settings/systemSettings/components/user/index.vue → src/views/settings/systemSettings/components/passwordManagement/index.vue

@@ -116,7 +116,7 @@
 
 
         <div class="form-actions">
         <div class="form-actions">
 <!--          <el-button  @click="resetForm"> Reset </el-button>-->
 <!--          <el-button  @click="resetForm"> Reset </el-button>-->
-          <el-button class="btn-save" round @click.prevent="handleConfirm">Save</el-button>
+          <el-button class="btn-save" type="primary" round @click.prevent="handleConfirm">Save</el-button>
         </div>
         </div>
       </el-form>
       </el-form>
     </div>
     </div>
@@ -401,34 +401,34 @@ $primary-hover: #2563eb;
     // justify-content: center;
     // justify-content: center;
     justify-content: flex-start;
     justify-content: flex-start;
 
 
-    .btn-save {
-      background-color: $primary-color !important;
-      color: #fff !important;
-      border: none !important;
-      min-width: 80px;
-      height: 36px;
-      font-weight: 500;
-      font-size: 14px;
-      box-shadow: 0 2px 4px rgba($primary-color, 0.2);
-      transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-
-      &:hover {
-        background-color: $primary-hover !important;
-        box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
-      }
-
-      &:active {
-        box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
-        transform: translateY(1px);
-      }
-
-      &:disabled,
-      &.is-disabled {
-        background-color: #c0c4cc !important;
-        cursor: not-allowed;
-        box-shadow: none;
-      }
-    }
+    //.btn-save {
+    //  background-color: $primary-color !important;
+    //  color: #fff !important;
+    //  border: none !important;
+    //  min-width: 80px;
+    //  height: 36px;
+    //  font-weight: 500;
+    //  font-size: 14px;
+    //  box-shadow: 0 2px 4px rgba($primary-color, 0.2);
+    //  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+    //
+    //  &:hover {
+    //    background-color: $primary-hover !important;
+    //    box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
+    //  }
+    //
+    //  &:active {
+    //    box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
+    //    transform: translateY(1px);
+    //  }
+    //
+    //  &:disabled,
+    //  &.is-disabled {
+    //    background-color: #c0c4cc !important;
+    //    cursor: not-allowed;
+    //    box-shadow: none;
+    //  }
+    //}
 
 
     //.el-button {
     //.el-button {
     //  min-width: 100px;
     //  min-width: 100px;

+ 30 - 29
src/views/settings/systemSettings/components/systemMaintenance/index.vue

@@ -69,6 +69,7 @@
 
 
     <el-button
     <el-button
         class="btn-save"
         class="btn-save"
+        type="primary"
         round
         round
         @click="Save"
         @click="Save"
         :loading="loading"
         :loading="loading"
@@ -253,7 +254,7 @@ const Save = async () => {
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
 // 颜色和尺寸变量定义
 // 颜色和尺寸变量定义
-$primary-color: #3b82f6;
+$primary-color: #409EFF;
 $primary-hover: #2563eb;
 $primary-hover: #2563eb;
 $warning-color: #f59e0b;
 $warning-color: #f59e0b;
 $warning-light: #fef3c7;
 $warning-light: #fef3c7;
@@ -387,34 +388,34 @@ $radius-md: 6px;
 // 按钮样式 - 通过覆盖 Element Plus 默认样式
 // 按钮样式 - 通过覆盖 Element Plus 默认样式
 
 
 /* Save 按钮 - 主操作 */
 /* Save 按钮 - 主操作 */
-.btn-save {
-  background-color: $primary-color !important;
-  color: #fff !important;
-  border: none !important;
-  min-width: 80px;
-  height: 36px;
-  font-weight: 500;
-  font-size: 14px;
-  box-shadow: 0 2px 4px rgba($primary-color, 0.2);
-  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-
-  &:hover {
-    background-color: $primary-hover !important;
-    box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
-  }
-
-  &:active {
-    box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
-    transform: translateY(1px);
-  }
-
-  &:disabled,
-  &.is-disabled {
-    background-color: #c0c4cc !important;
-    cursor: not-allowed;
-    box-shadow: none;
-  }
-}
+//.btn-save {
+//  background-color: $primary-color !important;
+//  color: #fff !important;
+//  border: none !important;
+//  min-width: 80px;
+//  height: 36px;
+//  font-weight: 500;
+//  font-size: 14px;
+//  box-shadow: 0 2px 4px rgba($primary-color, 0.2);
+//  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+//
+//  &:hover {
+//    background-color: $primary-hover !important;
+//    box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
+//  }
+//
+//  &:active {
+//    box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
+//    transform: translateY(1px);
+//  }
+//
+//  &:disabled,
+//  &.is-disabled {
+//    background-color: #c0c4cc !important;
+//    cursor: not-allowed;
+//    box-shadow: none;
+//  }
+//}
 
 
 /* Restart Now - 中等风险操作,琥珀色 */
 /* Restart Now - 中等风险操作,琥珀色 */
 .btn-restart {
 .btn-restart {

+ 68 - 17
src/views/settings/systemSettings/components/systemUpgrade/index.vue

@@ -5,7 +5,7 @@
       <p class="subtitle">Select and upload the upgrade file to update your device</p>
       <p class="subtitle">Select and upload the upgrade file to update your device</p>
     </div>
     </div>
 
 
-    <el-form ref="formRef" :model="formData" class="upgrade-form">
+    <el-form class="upgrade-form">
       <div class="upload-section">
       <div class="upload-section">
         <el-upload
         <el-upload
           class="file-upload"
           class="file-upload"
@@ -19,7 +19,7 @@
             <div class="upload-icon">📦</div>
             <div class="upload-icon">📦</div>
             <div class="upload-text">
             <div class="upload-text">
               <p class="text-primary">Click or drag file here</p>
               <p class="text-primary">Click or drag file here</p>
-              <p class="text-secondary">Supported file format: .bin, .img, .zip</p>
+              <p class="text-secondary">Supported file format: .img</p>
             </div>
             </div>
           </div>
           </div>
         </el-upload>
         </el-upload>
@@ -69,20 +69,18 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { ref, reactive } from 'vue'
+import { ref } from 'vue'
 import {
 import {
   ElLoading,
   ElLoading,
   ElMessage,
   ElMessage,
-  ElMessageBox,
-  type FormInstance
+  ElMessageBox
 } from 'element-plus'
 } from 'element-plus'
 import { useUserStore } from '@/stores/modules/user'
 import { useUserStore } from '@/stores/modules/user'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
+import { systemUpgrade } from '@/api/setting'
 
 
 const router = useRouter()
 const router = useRouter()
 const loading = ref(false)
 const loading = ref(false)
-const formRef = ref<FormInstance | null>(null)
-const formData = reactive({})
 const selectedFile = ref<File | null>(null)
 const selectedFile = ref<File | null>(null)
 
 
 const formatFileSize = (bytes: number): string => {
 const formatFileSize = (bytes: number): string => {
@@ -94,7 +92,39 @@ const formatFileSize = (bytes: number): string => {
 }
 }
 
 
 const handleFileChange = (file: any) => {
 const handleFileChange = (file: any) => {
-  selectedFile.value = file.raw
+  const rawFile = file.raw
+  
+  // 校验文件大小(不超过6M)
+  const maxSize = 6 * 1024 * 1024 // 6MB
+  if (rawFile.size > maxSize) {
+    ElMessage.error('File size cannot exceed 6MB')
+    return
+  }
+  
+  // 校验文件后缀(必须为.img)
+  const fileName = rawFile.name
+  const fileExtension = fileName.split('.').pop()?.toLowerCase()
+  if (fileExtension !== 'img') {
+    ElMessage.error('File must have .img extension')
+    return
+  }
+  
+  // 校验文件名称格式(包含MD5值)
+  const fileNamePattern = /^V[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[A-Z0-9]+_[0-9a-f]{32}\.img$/i
+  if (!fileNamePattern.test(fileName)) {
+    ElMessage.error('Invalid file name format. Please use the correct upgrade file.')
+    return
+  }
+  
+  // 提取并验证MD5值
+  const md5Match = fileName.match(/_(\w{32})\.img$/i)
+  if (md5Match) {
+    const md5Value = md5Match[1]
+    console.log('MD5 value found:', md5Value)
+    // 这里可以添加MD5值的进一步验证逻辑
+  }
+  
+  selectedFile.value = rawFile
 }
 }
 
 
 const handleClearFile = () => {
 const handleClearFile = () => {
@@ -107,6 +137,8 @@ const handleUpgrade = async () => {
     return
     return
   }
   }
 
 
+  let loadingInstance: any = null
+  
   try {
   try {
     await ElMessageBox.confirm(
     await ElMessageBox.confirm(
       'The upgrade operation will restart the device and may take several minutes. Are you sure you want to continue?',
       'The upgrade operation will restart the device and may take several minutes. Are you sure you want to continue?',
@@ -120,18 +152,37 @@ const handleUpgrade = async () => {
     )
     )
 
 
     loading.value = true
     loading.value = true
-    // Here should call the actual upgrade API
-    // Simulate upgrade process
-    setTimeout(() => {
-      loading.value = false
-      ElMessage.success(
-        'Upgrade successful! The device is restarting, please wait...'
-      )
+    loadingInstance = ElLoading.service({
+      lock: true,
+      text: 'System upgrade in progress. Do not close this window or power off your device.',
+      background: 'rgba(0, 0, 0, 0.7)'
+    })
+    
+    const uploadData = new FormData()
+    uploadData.append('UpgradeFile', selectedFile.value)
+    const response = await systemUpgrade(uploadData)
+
+    // 先关闭 loading,再处理结果(避免路由跳转后组件卸载导致无法关闭)
+    loadingInstance.close()
+    loading.value = false
+
+    if(response.data.UpgradeCode === 0){
+      ElMessage.success('Upgrade successful! The device is restarting, please wait...')
       useUserStore().logout()
       useUserStore().logout()
-      router.push('/login')
-    }, 3000)
+      await router.push('/login')
+    }else if(response.data.UpgradeCode === 1){
+      ElMessage.error('Upgrade failed! MD5 verification failed,please check the file integrity.')
+    }else if(response.data.UpgradeCode === 2){
+      ElMessage.error('Upgrade failed! The version numbers do not match.')
+    } else {
+      ElMessage.error('Upgrade failed! An unexpected error has occurred.')
+    }
   } catch (error) {
   } catch (error) {
     console.log(error)
     console.log(error)
+    loading.value = false
+    if (loadingInstance) {
+      loadingInstance.close()
+    }
     if (error !== 'cancel') {
     if (error !== 'cancel') {
       ElMessage.error('Upgrade failed')
       ElMessage.error('Upgrade failed')
     }
     }

+ 49 - 45
src/views/settings/systemSettings/components/time/index.vue → src/views/settings/systemSettings/components/timeSettings/index.vue

@@ -99,6 +99,7 @@
       <div class="button-group">
       <div class="button-group">
         <el-button
         <el-button
             round
             round
+            type="primary"
             @click="saveSettings"
             @click="saveSettings"
             :loading="saveLoading"
             :loading="saveLoading"
             class="save-btn"
             class="save-btn"
@@ -259,7 +260,8 @@ async function saveSettings() {
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
 // 颜色和尺寸变量定义
 // 颜色和尺寸变量定义
-$primary-color: #3b82f6;
+$primary-color: #409EFF;
+//$primary-color: #3b82f6;
 $primary-hover: #2563eb;
 $primary-hover: #2563eb;
 $text-primary: #1f2d3d;
 $text-primary: #1f2d3d;
 $text-secondary: #333;
 $text-secondary: #333;
@@ -386,25 +388,27 @@ $transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
     }
     }
 
 
     .sync-btn {
     .sync-btn {
-      background-color: $primary-color !important;
-      color: #fff !important;
-      border: none !important;
-      height: 30px;
-      min-width: 80px;
+      //background-color: $primary-color !important;
+      //color: #fff !important;
+      //border: none !important;
+      border-radius: 8px;
+
+      height: 26px;
+      width: 75px;
       font-weight: 500;
       font-weight: 500;
       font-size: 12px;
       font-size: 12px;
       box-shadow: 0 2px 4px rgba($primary-color, 0.2);
       box-shadow: 0 2px 4px rgba($primary-color, 0.2);
       transition: all 0.2s $transition-easing;
       transition: all 0.2s $transition-easing;
 
 
-      &:hover {
-        background-color: $primary-hover !important;
-        box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
-      }
-
-      &:active {
-        box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
-        transform: translateY(1px);
-      }
+      //&:hover {
+      //  background-color: $primary-hover !important;
+      //  box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
+      //}
+      //
+      //&:active {
+      //  box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
+      //  transform: translateY(1px);
+      //}
     }
     }
   }
   }
 
 
@@ -454,34 +458,34 @@ $transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
     font-weight: 500;
     font-weight: 500;
   }
   }
 
 
-  .save-btn {
-    background-color: $primary-color !important;
-    color: #fff !important;
-    border: none !important;
-    min-width: 80px;
-    height: 36px;
-    font-weight: 500;
-    font-size: 14px;
-    box-shadow: 0 2px 4px rgba($primary-color, 0.2);
-    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-
-    &:hover {
-      background-color: $primary-hover !important;
-      box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
-    }
-
-    &:active {
-      box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
-      transform: translateY(1px);
-    }
-
-    &:disabled,
-    &.is-disabled {
-      background-color: #c0c4cc !important;
-      cursor: not-allowed;
-      box-shadow: none;
-    }
-  }
+  //.save-btn {
+  //  background-color: $primary-color !important;
+  //  color: #fff !important;
+  //  border: none !important;
+  //  min-width: 80px;
+  //  height: 36px;
+  //  font-weight: 500;
+  //  font-size: 14px;
+  //  box-shadow: 0 2px 4px rgba($primary-color, 0.2);
+  //  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+  //
+  //  &:hover {
+  //    background-color: $primary-hover !important;
+  //    box-shadow: 0 4px 8px rgba($primary-hover, 0.3);
+  //  }
+  //
+  //  &:active {
+  //    box-shadow: 0 1px 2px rgba($primary-hover, 0.2);
+  //    transform: translateY(1px);
+  //  }
+  //
+  //  &:disabled,
+  //  &.is-disabled {
+  //    background-color: #c0c4cc !important;
+  //    cursor: not-allowed;
+  //    box-shadow: none;
+  //  }
+  //}
 }
 }
 
 
 // Element Plus 组件样式覆盖
 // Element Plus 组件样式覆盖
@@ -540,11 +544,11 @@ $transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
 // Switch 样式
 // Switch 样式
 :deep(.el-switch) {
 :deep(.el-switch) {
   --el-switch-on-color: $primary-color;
   --el-switch-on-color: $primary-color;
-  --el-switch-off-color: #909399; // 使用更深的灰色以提高对比度
+  --el-switch-off-color: #d1d1d3; // 使用更深的灰色以提高对比度
   height: 22px;
   height: 22px;
 
 
   .el-switch__core {
   .el-switch__core {
-    border-color: #909399; // 设置边框颜色与关闭状态一致
+    border-color: #d1d1d3; // 设置边框颜色与关闭状态一致
   }
   }
 
 
   &.is-checked .el-switch__core {
   &.is-checked .el-switch__core {

+ 4 - 4
src/views/settings/systemSettings/index.vue

@@ -10,11 +10,11 @@
       </el-tab-pane>
       </el-tab-pane>
 
 
       <el-tab-pane label="Time Settings" name="fourth">
       <el-tab-pane label="Time Settings" name="fourth">
-        <TimeSetting />
+        <TimeSettings />
       </el-tab-pane>
       </el-tab-pane>
 
 
       <el-tab-pane label="Password Management" name="fifth">
       <el-tab-pane label="Password Management" name="fifth">
-        <UserSecurity />
+        <PasswordManagement />
       </el-tab-pane>
       </el-tab-pane>
 
 
       <el-tab-pane label="System Update" name="third">
       <el-tab-pane label="System Update" name="third">
@@ -27,8 +27,8 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import { ref } from 'vue'
 import { ref } from 'vue'
 import CameraInfo from './components/cameraInfo/index.vue'
 import CameraInfo from './components/cameraInfo/index.vue'
-import UserSecurity from './components/user/index.vue'
-import TimeSetting from './components/time/index.vue'
+import PasswordManagement from './components/passwordManagement/index.vue'
+import TimeSettings from './components/timeSettings/index.vue'
 import SystemMaintenance from './components/systemMaintenance/index.vue'
 import SystemMaintenance from './components/systemMaintenance/index.vue'
 import SystemUpgrade from './components/systemUpgrade/index.vue'
 import SystemUpgrade from './components/systemUpgrade/index.vue'
 
 

+ 22 - 23
src/views/standardProtocol/components/onvif/index.vue

@@ -10,27 +10,27 @@
       <el-form :model="onvifConfig" label-width="125px" class="onvif-form">
       <el-form :model="onvifConfig" label-width="125px" class="onvif-form">
         <!-- Enable ONVIF -->
         <!-- Enable ONVIF -->
         <el-form-item label="Enable ONVIF">
         <el-form-item label="Enable ONVIF">
-          <el-switch v-model="onvifConfig.OnvifEnable" />
+          <el-switch v-model="onvifConfig.OnvifEnable"/>
         </el-form-item>
         </el-form-item>
 
 
         <!-- IP Address -->
         <!-- IP Address -->
         <el-form-item label="IP Address">
         <el-form-item label="IP Address">
-          <el-input v-model="onvifConfig.IpAddr" placeholder="Enter IP address" />
+          <el-input v-model="onvifConfig.IpAddr" placeholder="Enter IP address"/>
         </el-form-item>
         </el-form-item>
 
 
         <!-- Port -->
         <!-- Port -->
         <el-form-item label="Port">
         <el-form-item label="Port">
-          <el-input v-model="onvifConfig.Port" placeholder="Enter port number" />
+          <el-input v-model="onvifConfig.Port" placeholder="Enter port number"/>
         </el-form-item>
         </el-form-item>
 
 
         <!-- ONVIF Username -->
         <!-- ONVIF Username -->
         <el-form-item label="ONVIF Username">
         <el-form-item label="ONVIF Username">
-          <el-input v-model="onvifConfig.OnvifName" placeholder="Enter ONVIF username" />
+          <el-input v-model="onvifConfig.OnvifName" placeholder="Enter ONVIF username"/>
         </el-form-item>
         </el-form-item>
 
 
         <!-- ONVIF Password -->
         <!-- ONVIF Password -->
         <el-form-item label="ONVIF Password">
         <el-form-item label="ONVIF Password">
-          <el-input v-model="onvifConfig.Password" type="password" placeholder="Enter ONVIF password" show-password />
+          <el-input v-model="onvifConfig.Password" type="password" placeholder="Enter ONVIF password" show-password/>
         </el-form-item>
         </el-form-item>
 
 
         <!-- Save Button -->
         <!-- Save Button -->
@@ -40,21 +40,21 @@
       </el-form>
       </el-form>
 
 
       <!-- Access Example -->
       <!-- Access Example -->
-<!--      <div class="examples-section" v-if="onvifConfig.OnvifEnable">-->
-<!--        <h4>ONVIF Device Manager Access Example</h4>-->
-<!--        <div class="example-box">-->
-<!--          <div class="example-title">Using ONVIF Device Manager</div>-->
-<!--          <div class="code-block">-->
-<!--            <code>{{ onvifAccessUrl }}</code>-->
-<!--            <el-button type="primary" @click="copyToClipboard(onvifAccessUrl)">-->
-<!--              Copy-->
-<!--            </el-button>-->
-<!--          </div>-->
-<!--          <div class="tip-text">-->
-<!--            Use the above URL with your ONVIF Device Manager software to connect to the camera.-->
-<!--          </div>-->
-<!--        </div>-->
-<!--      </div>-->
+      <!--      <div class="examples-section" v-if="onvifConfig.OnvifEnable">-->
+      <!--        <h4>ONVIF Device Manager Access Example</h4>-->
+      <!--        <div class="example-box">-->
+      <!--          <div class="example-title">Using ONVIF Device Manager</div>-->
+      <!--          <div class="code-block">-->
+      <!--            <code>{{ onvifAccessUrl }}</code>-->
+      <!--            <el-button type="primary" @click="copyToClipboard(onvifAccessUrl)">-->
+      <!--              Copy-->
+      <!--            </el-button>-->
+      <!--          </div>-->
+      <!--          <div class="tip-text">-->
+      <!--            Use the above URL with your ONVIF Device Manager software to connect to the camera.-->
+      <!--          </div>-->
+      <!--        </div>-->
+      <!--      </div>-->
     </div>
     </div>
 
 
     <!-- Save Success Toast -->
     <!-- Save Success Toast -->
@@ -74,11 +74,10 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { ref, computed, onMounted } from 'vue'
-import { getOnvifProtocolApi, OnvifProtocolApi } from '@/api/protocol'
+import {ref, onMounted} from 'vue'
+import {getOnvifProtocolApi, OnvifProtocolApi} from '@/api/protocol'
 import {ElMessage} from 'element-plus'
 import {ElMessage} from 'element-plus'
 
 
-
 const onvifConfig = ref({
 const onvifConfig = ref({
   OnvifEnable: false,
   OnvifEnable: false,
   IpAddr: '',
   IpAddr: '',

+ 21 - 13
src/views/standardProtocol/components/rtsp/index.vue

@@ -71,23 +71,32 @@
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import { ref, onMounted } from 'vue'
 import { ref, onMounted } from 'vue'
+import {getUserSettingApi} from '@/api/setting'
+import {getRtspPasswordApi} from '@/api/userManagement'
 
 
 const passwordVerificationEnabled = ref(false)
 const passwordVerificationEnabled = ref(false)
-const cameraIp = ref('192.168.0.105')
+const cameraIp = ref('192.168.1.100')
 const showCopyToast = ref(false)
 const showCopyToast = ref(false)
+const NIC = 1
 
 
-// Fetch configuration
-onMounted(async () => {
-  try {
-    const response = await fetch('/api/camera/config')
-    if (response.ok) {
-      const data = await response.json()
-      passwordVerificationEnabled.value = data.passwordEnabled === true
-      cameraIp.value = data.cameraIp || '192.168.0.105'
-    }
-  } catch (error) {
-    console.error('Failed to fetch configuration:', error)
+// 获取本机的IP地址
+async function getIpAddress() {
+  const response = await getUserSettingApi(NIC)
+  if (response.data) {
+    cameraIp.value = response.data.ipAddress
   }
   }
+}
+// 获取rtsp password校验的状态
+async function getRtspPasswordStatus() {
+  const response = await getRtspPasswordApi()
+  if (response.data) {
+    passwordVerificationEnabled.value = !!response.data.RtspPwdEn
+  }
+}
+
+onMounted(async () => {
+  await getIpAddress()
+  await getRtspPasswordStatus()
 })
 })
 
 
 // Copy to clipboard
 // Copy to clipboard
@@ -100,7 +109,6 @@ const copyToClipboard = async (text: string) => {
   }
   }
 }
 }
 
 
-// Copy example
 const copyExample = () => {
 const copyExample = () => {
   const exampleText = `rtsp://username:password@${cameraIp.value}:554/video1`
   const exampleText = `rtsp://username:password@${cameraIp.value}:554/video1`
   copyToClipboard(exampleText)
   copyToClipboard(exampleText)

+ 2 - 2
src/views/standardProtocol/index.vue

@@ -2,11 +2,11 @@
   <div class="settings-container">
   <div class="settings-container">
     <el-tabs v-model="activeName" class="tabs">
     <el-tabs v-model="activeName" class="tabs">
       <el-tab-pane label="RTSP" name="first">
       <el-tab-pane label="RTSP" name="first">
-        <RTSP/>
+        <RTSP v-if="activeName === 'first'" />
       </el-tab-pane>
       </el-tab-pane>
 
 
       <el-tab-pane label="ONVIF" name="second">
       <el-tab-pane label="ONVIF" name="second">
-        <Onvif/>
+        <Onvif v-if="activeName === 'second'" />
       </el-tab-pane>
       </el-tab-pane>
     </el-tabs>
     </el-tabs>
   </div>
   </div>