1
0

3 کامیت‌ها 35b1eee483 ... ca508be9d4

نویسنده SHA1 پیام تاریخ
  wangfumin ca508be9d4 1 1 هفته پیش
  wangfumin 7dfeadc77d Merge branch 'master' of http://192.168.0.115:3000/tangning/personalhubs 1 هفته پیش
  wangfumin cebfbc4b3d 新增table 1 هفته پیش
4فایلهای تغییر یافته به همراه206 افزوده شده و 762 حذف شده
  1. 1 7
      components.d.ts
  2. 0 470
      src/components/CameraItem.vue
  3. 63 116
      src/components/tableList/index.vue
  4. 142 169
      src/views/pc/device/index.vue

+ 1 - 7
components.d.ts

@@ -9,7 +9,7 @@ export {}
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
-    CameraItem: typeof import('./src/components/CameraItem.vue')['default']
+    CameraItem: typeof import('./src/components/tableList/CameraItem.vue')['default']
     Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
@@ -17,8 +17,6 @@ declare module '@vue/runtime-core' {
     ElCol: typeof import('element-plus/es')['ElCol']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElEmpty: typeof import('element-plus/es')['ElEmpty']
-    ElForm: typeof import('element-plus/es')['ElForm']
-    ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
@@ -28,7 +26,6 @@ declare module '@vue/runtime-core' {
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
-    ElTooltip: typeof import('element-plus/es')['ElTooltip']
     Footer: typeof import('./src/components/mobile/footer.vue')['default']
     Header: typeof import('./src/components/mobile/header.vue')['default']
     HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
@@ -48,7 +45,4 @@ declare module '@vue/runtime-core' {
     Toast: typeof import('./src/components/Toast/Toast.vue')['default']
     WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
   }
-  export interface ComponentCustomProperties {
-    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
-  }
 }

+ 0 - 470
src/components/CameraItem.vue

@@ -1,470 +0,0 @@
-<template>
-  <div class="col-content">
-    <div class="i-left">
-      <img
-        @click="gotoScene(item)"
-        :src="getDeviceImage(item.cameraType)"
-        alt=""
-      />
-    </div>
-    <div class="i-right">
-      <template v-if="item.goodsId !== 1 && item.goodsId !== 8">
-        <p
-          class="d-id"
-          :title="item.snCode || '--'"
-        >
-          <span class="vip-icon" v-if="isMember"></span>
-          <span class="vip-icon vip-expired-icon" v-else-if="isExpiredMember"></span>
-          <span class="sncode">
-            设备编号: {{ item.snCode || "--" }}
-          </span>
-        </p>
-        <p
-          class="p-sub"
-          style="padding-left: 26px"
-          :title="`${item.usedSpaceStr} / ${
-            isMember ? '无限容量' : item.totalSpaceStr
-          }`"
-        >
-          <!-- <img src="/images/icon-cloud.png" alt="" /> -->
-          <span v-if="item.usedSpaceStr && item.totalSpaceStr && item.totalSpaceStr != '0B'">
-            {{ item.usedSpaceStr }}
-            {{ (isMember || item.cameraType == 10 || item.cameraType == 11 || item.cameraType == 12) ? '' : `/${item.totalSpaceStr}` }}
-          </span>
-          <span v-else>--</span>
-        </p>
-        <div class="capacity">
-          <div class="c-line">
-            <div
-              class="active"
-              v-show="!isMember"
-              :style="{
-                width: (item.cameraType == 10 || item.cameraType == 11 || item.cameraType == 12) ? '1%' : getBar(item.usedSpace, item.totalSpace),
-                background: (item.cameraType == 10 || item.cameraType == 11 || item.cameraType == 12) ? '#15BEC8' : getColor(item.usedSpace, item.totalSpace),
-              }"
-            ></div>
-          </div>
-        </div>
-        <p class="p-sub p-expired">
-          到期时间:{{ item.spaceEndStr || "--" }}
-          <span class="expired-icon-w">
-            <span
-              v-if="validExpired(item.spaceEndStr).isExpired"
-              class="expired-icon"
-              @mouseenter="setTimeoutShowCtrls(true)"
-              @mouseleave="setTimeoutShowCtrls(false)"
-            >
-              ⚠️
-            </span>
-            <div 
-              v-show="showCtrls" 
-              class="expired-ctrls-w" 
-              @mouseenter="setTimeoutShowCtrls(true)"
-              @mouseleave="setTimeoutShowCtrls(false)"
-            >
-              <p v-if="!validExpired(item.spaceEndStr).expiredTime">会员即将到期</p>
-              <p v-else-if="validExpired(item.spaceEndStr).trueExpired">会员已过期</p>
-              <p v-else>
-                会员将在 <span class="expired">{{ validExpired(item.spaceEndStr).expiredTime }}</span> 天后过期
-              </p>
-              <div class="ctrls-w">
-                <div class="btn cancel-btn" @click="showCtrls = false">取消</div>
-                <div class="btn submit-btn" @click="handleRepay">续费</div>
-              </div>
-            </div>
-          </span>
-        </p>
-        <p class="p-sub" :title="item.cooperationUserName">
-          协作用户:{{ item.cooperationUserName || '-' }}
-        </p>
-        <div class="oper-con">
-          <div class="oper">
-            <div>
-              <span class="spot"></span>
-            </div>
-            <ul>
-              <li @click="unbind(item)">
-                解绑
-              </li>
-              <li v-if="item.status !== 0" @click="handleCooperation(item)">
-                {{ item.cooperationUserName ? '取消分配' : '分配' }}
-              </li>
-            </ul>
-          </div>
-        </div>
-      </template>
-      <template v-else>
-        <p class="d-id">ID: <span class="sncode">{{ item.childName }}</span></p>
-        <p class="p-sub">余额:{{ item.balance }}</p>
-        <div class="d-edit" :class="{ 'dtow-edit': item.cameraType != 4 }">
-          <div class="primary">
-            <span>充值</span>
-          </div>
-          <div>
-            <span @click="unbind(item)">解绑</span>
-          </div>
-        </div>
-      </template>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { ref, computed } from 'vue'
-
-interface Props {
-  item: any
-}
-
-const props = defineProps<Props>()
-
-const emit = defineEmits(['handleCooperation', 'unbind', 'renew'])
-
-const showCtrls = ref(false)
-const timer = ref<NodeJS.Timeout>()
-
-// 计算属性
-const isMember = computed(() => {
-  return props.item.userIncrementId && !expired.value.trueExpired
-})
-
-const isExpiredMember = computed(() => {
-  return props.item.userIncrementId && expired.value.trueExpired
-})
-
-const expired = computed(() => {
-  if (!props.item.spaceEndTime) {
-    return {}
-  }
-  const expiredDays = Math.floor((new Date(props.item.spaceEndTime).getTime() - new Date().getTime()) / 86400000) + 1
-  return {
-    expiredTime: expiredDays,
-    expiredText: `<span class="expired">${expiredDays}</span>`,
-    isExpired: expiredDays <= 30,
-    trueExpired: expiredDays < 0
-  }
-})
-
-// 方法
-const getDeviceImage = (cameraType: number) => {
-  switch (cameraType) {
-    case 4:
-      return '/images/banner_pro.png'
-    case 9:
-      return '/src/assets/images/pic_lite.png'
-    case 10:
-      return '/src/assets/images/pci_Laser.png'
-    case 11:
-      return '/src/assets/images/pic_shenguan.png'
-    case 12:
-      return '/src/assets/images/pic_Mova.png'
-    default:
-      return '/images/t_product.png'
-  }
-}
-
-const gotoScene = (item: any) => {
-  // 这里可以添加路由跳转逻辑
-  console.log('跳转到场景页面', item)
-}
-
-const getBar = (a: number, b: number) => {
-  if (a === 0) {
-    return 0
-  }
-  const temp = (a / b) * 100
-  if (temp < 1) {
-    return "1%"
-  }
-  return (temp > 100 ? 100 : Math.round(temp)) + "%"
-}
-
-const getColor = (a: number, b: number) => {
-  const temp = (a / b) * 100
-  const point = 80
-  return temp < point ? "#15BEC8" : "#ff0000"
-}
-
-const validExpired = (time: string) => {
-  if (!time) {
-    return {}
-  }
-  const expired = Math.floor((new Date(time).getTime() - new Date().getTime()) / 86400000) + 1
-
-  return {
-    expiredText: `<span class="expired">${expired}</span>`,
-    expiredTime: expired,
-    isExpired: expired <= 30,
-    trueExpired: expired < 0
-  }
-}
-
-const handleCooperation = () => {
-  emit("handleCooperation", props.item)
-}
-
-const unbind = () => {
-  emit("unbind", props.item)
-}
-
-const setTimeoutShowCtrls = (bool: boolean) => {
-  if (timer.value) {
-    clearTimeout(timer.value)
-  }
-  timer.value = setTimeout(() => {
-    showCtrls.value = bool
-  }, 200)
-}
-
-const handleRepay = () => {
-  emit('renew', props.item)
-}
-</script>
-
-<style lang="less" scoped>
-.col-content {
-  display: flex;
-  gap: 15px;
-  height: 100%;
-  
-  .i-left {
-    flex-shrink: 0;
-    
-    img {
-      width: 80px;
-      height: 80px;
-      object-fit: cover;
-      border-radius: 4px;
-      cursor: pointer;
-    }
-  }
-  
-  .i-right {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    
-    .d-id {
-      display: flex;
-      align-items: center;
-      font-weight: 600;
-      color: #323233;
-      margin-bottom: 8px;
-      
-      .vip-icon {
-        width: 16px;
-        height: 16px;
-        background: url('@/assets/images/vip_true.svg') no-repeat center center;
-        background-size: cover;
-        margin-right: 5px;
-        
-        &.vip-expired-icon {
-          opacity: 0.5;
-        }
-      }
-      
-      .sncode {
-        flex: 1;
-        word-break: break-all;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: nowrap;
-      }
-    }
-    
-    .p-sub {
-      display: flex;
-      align-items: center;
-      color: #666;
-      font-size: 14px;
-      margin-bottom: 5px;
-      
-      img {
-        width: 16px;
-        height: 16px;
-        margin-right: 5px;
-      }
-      
-      &.p-expired {
-        overflow: visible !important;
-      }
-    }
-    
-    .capacity {
-      margin: 8px 0;
-      
-      .c-line {
-        width: 100%;
-        height: 6px;
-        background: #e5e5e5;
-        border-radius: 3px;
-        overflow: hidden;
-        
-        .active {
-          height: 100%;
-          border-radius: 3px;
-          transition: all 0.3s;
-        }
-      }
-    }
-    
-    .oper-con {
-      margin-top: 10px;
-      
-      .oper {
-        position: relative;
-        
-        .spot {
-          display: inline-block;
-          width: 4px;
-          height: 4px;
-          background: #666;
-          border-radius: 50%;
-          cursor: pointer;
-          
-          &::before,
-          &::after {
-            content: '';
-            position: absolute;
-            width: 4px;
-            height: 4px;
-            background: #666;
-            border-radius: 50%;
-          }
-          
-          &::before {
-            top: -6px;
-          }
-          
-          &::after {
-            top: 6px;
-          }
-        }
-        
-        ul {
-          display: none;
-          position: absolute;
-          top: 100%;
-          right: 0;
-          background: #fff;
-          border: 1px solid #e5e5e5;
-          border-radius: 4px;
-          box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
-          z-index: 10;
-          min-width: 109px;
-          
-          li {
-            padding: 8px 16px;
-            cursor: pointer;
-            font-size: 14px;
-            color: #323233;
-            
-            &:hover {
-              background: #f5f5f5;
-            }
-            
-            &:not(:last-child) {
-              border-bottom: 1px solid #f0f0f0;
-            }
-          }
-        }
-        
-        &:hover ul {
-          display: block;
-        }
-      }
-    }
-    
-    .d-edit {
-      display: flex;
-      gap: 10px;
-      margin-top: 10px;
-      
-      .primary {
-        background: #15BEC8;
-        color: #fff;
-        padding: 4px 12px;
-        border-radius: 4px;
-        cursor: pointer;
-        font-size: 14px;
-        
-        &:hover {
-          background: #13a8b1;
-        }
-      }
-      
-      div:not(.primary) {
-        color: #666;
-        cursor: pointer;
-        
-        &:hover {
-          color: #15BEC8;
-        }
-      }
-    }
-  }
-}
-
-.expired-icon {
-  cursor: pointer;
-}
-
-.expired-icon-w {
-  position: relative;
-  
-  .expired-ctrls-w {
-    position: absolute;
-    left: 0px;
-    background: #fff;
-    z-index: 1;
-    min-width: 172px;
-    height: 85px;
-    padding: 12px 16px;
-    box-shadow: 0px 2px 12px 0px rgba(50, 50, 51, 0.12);
-    bottom: -10px;
-    transform: translateY(100%);
-    border-radius: 4px;
-    font-size: 14px;
-    color: #323233;
-    line-height: 22px;
-    
-    &::before {
-      content: "";
-      display: block;
-      border: 8px solid transparent;
-      border-bottom-color: #fff;
-      position: absolute;
-      top: -15px;
-      left: 14px;
-      z-index: 1;
-    }
-    
-    .ctrls-w {
-      display: flex;
-      justify-content: flex-end;
-      margin-top: 13px;
-    }
-    
-    .btn {
-      padding: 0 8px;
-      line-height: 24px;
-      border-radius: 2px;
-      background: #ebebeb;
-      display: inline-block;
-      cursor: pointer;
-    }
-    
-    .submit-btn {
-      background: #15BEC8;
-      margin-left: 8px;
-      color: #fff;
-    }
-    
-    .expired {
-      color: #ff4757;
-      font-weight: 600;
-    }
-  }
-}
-</style>

+ 63 - 116
src/components/tableList/index.vue

@@ -2,57 +2,47 @@
   <div class="table-list-wrapper">
     <!-- 列表视图 -->
     <div class="table-layout" v-show="!showCardView">
-      <ul class="t-header" :class="{ line: showLine }">
-        <li v-if="selection" class="check-cls">
-          <el-checkbox 
-            v-model="selectAll" 
-            @change="handleSelectAll"
-            :disabled="!data.length"
-          />
-        </li>
-        <li
-          v-for="(item, i) in header"
-          :key="i"
-          :style="{
-            width: item.width ? item.width + 'px' : 100 / header.length + '%',
-          }"
-        >
-          {{ item.name }}
-        </li>
-      </ul>
-      <div class="t-con">
-        <ul
-          class="t-item"
-          :class="{ line: showLine }"
-          v-for="(item, i) in data"
-          :key="i"
+      <el-table
+        ref="tableRef"
+        :data="data"
+        style="width: 100%"
+        @selection-change="handleSelectionChange"
+        :border="showLine"
+        stripe
+      >
+        <!-- 选择列 -->
+        <el-table-column
+          v-if="selection"
+          type="selection"
+          width="55"
+          align="center"
+        />
+        
+        <!-- 数据列 -->
+        <el-table-column
+          v-for="(item, index) in header"
+          :key="index"
+          :prop="item.key"
+          :label="item.name"
+          :width="item.width"
+          :min-width="item.minWidth || 100"
+          :align="item.align || 'left'"
+          :show-overflow-tooltip="true"
         >
-          <li v-if="selection" class="check-cls">
-            <el-checkbox 
-              v-model="item.selected" 
-              @change="handleItemSelect(item)"
-            />
-          </li>
-          <li
-            v-for="(sub, j) in header"
-            :key="j"
-            :style="{
-              width: sub.width ? sub.width + 'px' : 100 / header.length + '%',
-            }"
-          >
+          <template #default="scope">
             <slot
-              :data="sub.staticName || item[sub.key]"
-              :subKey="sub.key"
-              :type="sub.type"
-              :item="item"
-              :canclick="sub.canclick"
+              :data="item.staticName || scope.row[item.key]"
+              :subKey="item.key"
+              :type="item.type"
+              :item="scope.row"
+              :canclick="item.canclick"
               name="item"
             >
-              {{ sub.staticName || item[sub.key] || '-' }}
+              {{ item.staticName || scope.row[item.key] || '-' }}
             </slot>
-          </li>
-        </ul>
-      </div>
+          </template>
+        </el-table-column>
+      </el-table>
     </div>
 
     <!-- 卡片视图 -->
@@ -68,7 +58,7 @@
             <el-checkbox 
               v-if="selection"
               v-model="item.selected" 
-              @change="handleItemSelect(item)"
+              @change="handleItemSelect"
               class="item-checkbox"
             />
             <slot name="card" :item="item" :index="index">
@@ -87,9 +77,13 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, watch } from 'vue'
+import { ref } from 'vue'
 import CameraItem from '@/components/tableList/CameraItem.vue'
 
+defineOptions({
+  name: 'TableList'
+})
+
 interface Props {
   data: any[]
   header: any[]
@@ -106,31 +100,23 @@ const props = withDefaults(defineProps<Props>(), {
 
 const emit = defineEmits(['selection-change', 'cooperation', 'unbind', 'renew'])
 
-const selectAll = ref(false)
-
-const handleSelectAll = (val: boolean) => {
-  props.data.forEach(item => {
-    item.selected = val
-  })
-  handleSelectionChange()
-}
+const tableRef = ref()
 
-const handleItemSelect = (item: any) => {
-  handleSelectionChange()
-  
-  // 更新全选状态
-  selectAll.value = props.data.every(item => item.selected)
+// el-table的选择变化处理(用于列表视图)
+const handleSelectionChange = (selection: any[]) => {
+  emit('selection-change', selection)
 }
 
-const handleSelectionChange = () => {
+// 卡片视图的单项选择处理
+const handleItemSelect = () => {
   const selectedItems = props.data.filter(item => item.selected)
   emit('selection-change', selectedItems)
 }
 
-// 监听数据变化,重置选择状态
-watch(() => props.data, () => {
-  selectAll.value = false
-}, { deep: true })
+// 暴露tableRef给父组件使用
+defineExpose({
+  tableRef
+})
 </script>
 
 <style lang="less" scoped>
@@ -141,58 +127,19 @@ watch(() => props.data, () => {
 .table-layout {
   width: 100%;
   
-  .t-header {
-    display: flex;
-    background: #f7f7f7;
-    border-radius: 4px;
-    padding: 0 20px;
-    height: 50px;
-    align-items: center;
-    font-weight: 600;
-    color: #323233;
-    margin-bottom: 10px;
-    
-    &.line {
-      border-bottom: 1px solid #e5e5e5;
-    }
-    
-    li {
-      display: flex;
-      align-items: center;
-      padding: 0 10px;
-      
-      &.check-cls {
-        width: 60px;
-        justify-content: center;
+  :deep(.el-table) {
+    .el-table__header-wrapper {
+      th {
+        background-color: #f7f7f7;
+        font-weight: 600;
+        color: #323233;
       }
     }
-  }
-  
-  .t-con {
-    .t-item {
-      display: flex;
-      padding: 0 20px;
-      height: 60px;
-      align-items: center;
-      border-bottom: 1px solid #f0f0f0;
-      
-      &:hover {
-        background: #f9f9f9;
-      }
-      
-      &.line {
-        border-bottom: 1px solid #e5e5e5;
-      }
-      
-      li {
-        display: flex;
-        align-items: center;
-        padding: 0 10px;
-        color: #666;
-        
-        &.check-cls {
-          width: 60px;
-          justify-content: center;
+    
+    .el-table__body-wrapper {
+      .el-table__row {
+        &:hover {
+          background-color: #f9f9f9;
         }
       }
     }

+ 142 - 169
src/views/pc/device/index.vue

@@ -15,63 +15,50 @@
       <!-- 操作栏 -->
       <div class="main-list">
         <div class="btns">
-          <!-- 全选 -->
-          <div v-show="isImgType" class="all-select" :class="{disable: !cameraList.length}">
-            <el-checkbox 
-              v-model="selectAll" 
-              @change="handleSelectAll"
-              :disabled="!cameraList.length"
-            >
-              全选
-            </el-checkbox>
-          </div>
-          
-          <!-- 协作按钮 -->
-          <el-button 
+          <!-- 批量操作提示 -->
+          <!-- <div v-show="!isImgType && selectedArr.length > 0" class="selection-info">
+            已选择 {{ selectedArr.length }} 项
+          </div> -->
+
+          <i
             v-if="tabActive !== 0 && selectedArr.length > 0"
-            type="primary" 
-            size="small"
             @click="multCop"
-          >
-            协作
-          </el-button>
+            class="iconfont icon-menu_cooperation1"
+            :title="$t('manage.myDevices.pictle')"
+            :class="{ active: isImgType }"
+          ></i>
           
           <!-- 解绑按钮 -->
-          <el-button 
+          <i 
             v-if="tabActive !== 0 && selectedArr.length > 0"
-            type="danger" 
-            size="small"
             @click="multDel"
-          >
-            解绑
-          </el-button>
+            class="iconfont icon-unbind"
+            :title="$t('manage.myDevices.unbind')"
+            :class="{ active: isImgType }"
+          ></i>
         </div>
 
         <div class="rig-con">
           <!-- 添加设备 -->
-          <el-button type="primary" size="small" @click="addDevice">
-            添加设备
-          </el-button>
+          <h-icon :title="$t('manage.deviceAdmin.collaborative')"  @click="multCop" type="icon-new_camera" class="icon main-list-left-icon icon-new_camera" />
           
           <!-- 视图切换 -->
-          <template v-if="tabActive !== 0">
-            <el-button-group class="view-toggle">
-              <el-button 
-                :type="isImgType ? 'primary' : 'default'"
-                size="small"
-                @click="changeType(true)"
-              >
-                卡片
-              </el-button>
-              <el-button 
-                :type="!isImgType ? 'primary' : 'default'"
-                size="small"
-                @click="changeType(false)"
-              >
-                列表
-              </el-button>
-            </el-button-group>
-          </template>
+          <div v-if="tabActive !== 0">
+            <i
+              @click="changeType(true)"
+              v-if="!isImgType"
+              class="iconfont icon-main_grid"
+              :title="$t('manage.myDevices.pictle')"
+              :class="{ active: isImgType }"
+            ></i>
+            <i
+              @click="changeType(false)"
+              v-else
+              class="iconfont icon-view_list"
+              :title="$t('manage.myDevices.listtle')"
+              :class="{ active: !isImgType }"
+            ></i>
+          </div>
           
           <!-- 搜索框 -->
           <el-input
@@ -91,10 +78,11 @@
                 />
               </el-select>
             </template>
-            <template #append>
-              <el-button @click="handleSearch">
-                搜索
-              </el-button>
+            <template #suffix>
+              <h-icon
+                class="search-btn"
+                type="search"
+              />
             </template>
           </el-input>
         </div>
@@ -102,105 +90,73 @@
     </div>
 
     <!-- 内容区域 -->
-    <template>
-      <!-- 协作设备特殊显示 -->
-      <el-row :gutter="20" class="camera-cards" v-show="tabActive === 0">
-        <template v-if="!loading">
-          <el-col
-            :span="8"
-            v-for="(item, index) in cameraList"
-            :key="index"
-            class="camera-item"
-          >
-            <div class="card-wrapper">
-              <el-checkbox 
-                v-model="item.selected" 
-                @change="handleItemSelect(item)"
-                class="item-checkbox"
-              />
-              <camera-item
-                :item="item"
-                @handleCooperation="handleCooperation"
-                @unbind="unbind"
-                @renew="handleRenew"
-              />
-            </div>
-          </el-col>
-        </template>
-      </el-row>
-
-      <!-- TableList组件 - 支持列表和卡片视图 -->
+    <div class="content-wrapper">
       <table-list
-        v-show="tabActive !== 0"
-        ref="tableRef"
+        :data="cameraList"
+        :header="tabHeader"
+        :selection="tabActive !== 0"
+        :show-card-view="isImgType"
         @selection-change="selectHandle"
-        @unbind="unbind"
         @cooperation="handleCooperation"
+        @unbind="unbind"
         @renew="handleRenew"
-        :header="tabHeader"
-        :selection="cameraList.length > 0"
-        :data="cameraList"
-        :show-card-view="isImgType"
-        :show-line="true"
-        class="table-list"
+        ref="tableRef"
       >
-        <template #item="{ data, type, canclick, item }">
-          <template v-if="canclick">
-            <span
+        <!-- 自定义插槽内容 -->
+        <template #item="{ data, subKey, type, item, canclick }">
+          <div v-if="type === 'image'" class="flex-avatar">
+            <div v-if="isMember(item)" class="vip-icon"></div>
+            <div v-else-if="isExpiredMember(item)" class="vip-icon vip-expired-icon"></div>
+            <span>{{ data }}</span>
+          </div>
+          
+          <div v-else-if="type === 'qingkuang'">
+            <span>{{ item.usedSpaceStr || '0B' }}/{{ item.totalSpaceStr || '0B' }}</span>
+          </div>
+          
+          <div v-else-if="type === 'spaceEndStr'">
+            <span :class="{ 'text-danger': isExpired(item) }">
+              {{ data || '-' }}
+            </span>
+          </div>
+          
+          <div v-else-if="canclick && subKey === 'operation'" class="operation-buttons">
+            <span 
+              v-if="tabActive === 0"
               class="table-btn"
               @click="handleCooperation(item)"
-              v-if="item.status !== 0"
             >
-              协作列表
+              协作
             </span>
-
-            <span class="info-wrapper">
-              <span class="table-btn" @mouseover="showInfo = item.id" @mouseout="showInfo = null">
-                详细信息
+            <template v-else>
+              <span 
+                class="table-btn"
+                @click="handleCooperation(item)"
+                style="margin-right: 10px;"
+              >
+                协作
               </span>
-              <div
-                v-show="showInfo === item.id"
-                class="info-tooltip"
+              <span 
+                class="table-btn"
+                @click="unbind(item)"
+                style="margin-right: 10px;"
               >
-                <div class="info-content">
-                  <span class="th">场景数量</span><span class="th">最后时间</span>
-                  <span class="td">{{ item.sceneNum || '-' }}</span><span class="td">{{ item.lastTime || '-' }}</span>
-                </div>
-              </div>
-            </span>
-          </template>
-          
-          <span v-else-if="type === 'image'" class="flex-avatar">
-            <span v-if="isMember(item)" class="vip-icon"></span>
-            <span v-else-if="isExpiredMember(item)" class="vip-icon vip-expired-icon"></span>
-            {{ item.snCode }}
-          </span>
-          
-          <span v-else-if="type === 'qingkuang'">
-            <span v-if="item.usedSpaceStr && item.totalSpaceStr && item.totalSpaceStr != '0B'">
-              {{ item.usedSpaceStr }}{{ isMember(item) ? "" : ` / ${item.totalSpaceStr}` }}
-            </span>
-            <span v-else>--</span>
-          </span>
-          
-          <span v-else-if="type === 'spaceEndStr'">
-            {{ item.spaceEndStr || "--" }}
-            <span v-if="isExpired(item)" class="expired-icon" @mouseover="showCtrls = item.id" @mouseout="showCtrls = null">
-              ⚠️
-              <div v-show="showCtrls === item.id" class="expired-tooltip">
-                <p>{{ isExpired(item) ? '会员已过期' : '会员即将过期' }}</p>
-                <div class="ctrls-w">
-                  <el-button size="small" @click="showCtrls = null">取消</el-button>
-                  <el-button size="small" type="primary" @click="handleRenew(item)">续费</el-button>
-                </div>
-              </div>
-            </span>
-          </span>
+                解绑
+              </span>
+              <span 
+                v-if="item.userIncrementId"
+                class="table-btn"
+                @click="handleRenew(item)"
+              >
+                续费
+              </span>
+            </template>
+          </div>
           
-          <span v-else>{{ data || "-" }}</span>
+          <span v-else>{{ data || '-' }}</span>
         </template>
       </table-list>
-    </template>
+    </div>
 
     <!-- 空状态 -->
     <div class="empty-state" v-if="!loading && !total">
@@ -248,7 +204,6 @@
 import { ref, computed, onMounted, watch } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import TableList from '@/components/tableList/index.vue'
-import CameraItem from '@/components/CameraItem.vue'
 
 defineOptions({
   name: 'DeviceIndex'
@@ -270,11 +225,11 @@ const searchTypeList = ref([
 
 // 表格头部配置
 const tabHeader = ref([
-  { key: 'snCode', name: 'S/N码', type: 'image', width: 200 },
-  { key: 'status', name: '云容量', width: 100 },
-  { key: 'spaceEndStr', name: '到期时间', type: 'spaceEndStr', width: 150 },
-  { key: 'qingkuang', name: '协作者', type: 'qingkuang', width: 150 },
-  { key: 'operation', name: '操作', canclick: true, width: 200 }
+  { key: 'snCode', name: 'S/N码', type: 'image' },
+  { key: 'status', name: '状态' },
+  { key: 'qingkuang', name: '云容量', type: 'qingkuang' },
+  { key: 'spaceEndStr', name: '到期时间', type: 'spaceEndStr'},
+  { key: 'operation', name: '操作', canclick: true }
 ])
 
 // 静态设备数据
@@ -420,7 +375,6 @@ const isImgType = ref(localStorage.getItem("isImgTypeForDevice") !== "false")
 const searchKey = ref("")
 const loading = ref(false)
 const selectedArr = ref<any[]>([])
-const selectAll = ref(false)
 const showBinding = ref(false)
 const showRenew = ref(false)
 const reNewItem = ref<any>({})
@@ -433,14 +387,16 @@ const totalObj = ref<Record<number, number>>({
   0: 0,
   4: 0, 
   10: 0,
-  11: 0
+  11: 0,
+  12: 0
 })
 
 const oldTotalObj = ref<Record<number, number>>({
   0: 0,
   4: 0,
   10: 0, 
-  11: 0
+  11: 0,
+  12: 0
 })
 
 const filteredData = computed(() => {
@@ -449,9 +405,10 @@ const filteredData = computed(() => {
   // 根据当前标签页过滤
   data = mockCameraData.value.filter(item => {
     if (tabActive.value === 0) return item.status === 0 // 协作设备
-    if (tabActive.value === 4) return item.goodsId === 0 // 我的设备(排除充值设备)
+    if (tabActive.value === 4) return item.goodsId === 0 && item.status !== 0 // 我的设备(排除充值设备,排除协作设备)
     if (tabActive.value === 10) return item.cooperationUserName && item.goodsId === 0 // 共享设备(有协作用户)
     if (tabActive.value === 11) return item.goodsId === 1 || item.goodsId === 8 // 租赁设备(充值设备)
+    if (tabActive.value === 12) return item.cameraType === 12 // 四维深巡
     return true
   })
   
@@ -472,7 +429,6 @@ const filteredData = computed(() => {
 
 const cameraList = computed(() => {
   const data = filteredData.value
-  console.log(data, 888)
   // 分页
   const start = (currentPage.value - 1) * pageSize.value
   const end = start + pageSize.value
@@ -498,9 +454,10 @@ const handleTabClick = (tab: any) => {
 const updateTotalCount = () => {
   // 更新各个标签的数量
   totalObj.value[0] = mockCameraData.value.filter(item => item.status === 0).length // 协作设备
-  totalObj.value[4] = mockCameraData.value.filter(item => item.goodsId === 0).length // 我的设备(排除充值设备)
+  totalObj.value[4] = mockCameraData.value.filter(item => item.goodsId === 0 && item.status !== 0).length // 我的设备(排除充值设备,排除协作设备)
   totalObj.value[10] = mockCameraData.value.filter(item => item.cooperationUserName && item.goodsId === 0).length // 共享设备(有协作用户)
   totalObj.value[11] = mockCameraData.value.filter(item => item.goodsId === 1 || item.goodsId === 8).length // 租赁设备(充值设备)
+  totalObj.value[12] = mockCameraData.value.filter(item => item.cameraType === 12).length // 四维深巡
   
   oldTotalObj.value = { ...totalObj.value }
 }
@@ -514,18 +471,9 @@ const handleSearch = () => {
   currentPage.value = 1
 }
 
-const handleSelectAll = (val: boolean) => {
-  cameraList.value.forEach(item => {
-    item.selected = val
-  })
-  updateSelectedArr()
-}
 
-const handleItemSelect = (_item: any) => {
+const handleItemSelect = () => {
   updateSelectedArr()
-  
-  // 更新全选状态
-  selectAll.value = cameraList.value.every(item => item.selected)
 }
 
 const updateSelectedArr = () => {
@@ -632,22 +580,31 @@ onMounted(() => {
 // 监听器
 watch(tabActive, () => {
   selectedArr.value = []
-  selectAll.value = false
 })
 </script>
 
 <style lang="less" scoped>
 .camera-page {
-  padding: 30px;
-  background: #fff;
-  min-height: calc(100vh - 60px);
+  padding: 20px 30px!important;
 }
 
 .camera-header {
-  margin-bottom: 20px;
   
   :deep(.el-tabs__header) {
-    margin-bottom: 20px;
+    margin-bottom: 10px;
+    .el-tabs__item{
+      font-size: 16px;
+    }
+    .el-tabs__item.is-active, .el-tabs__item:hover{
+      color: #15bec8;
+    }
+    .el-tabs__active-bar{
+      background-color: #15bec8;
+    }
+  }
+
+  :deep(.el-tabs__nav-wrap:after) {
+    height: 0;
   }
   
   .main-list {
@@ -660,20 +617,19 @@ watch(tabActive, () => {
     .btns {
       display: flex;
       align-items: center;
-      gap: 10px;
+      gap: 20px;
       
-      .all-select {
-        &.disable {
-          opacity: 0.5;
-          pointer-events: none;
-        }
+      .selection-info {
+        color: #15bec8;
+        font-size: 14px;
+        font-weight: 500;
       }
     }
     
     .rig-con {
       display: flex;
       align-items: center;
-      gap: 10px;
+      gap: 20px;
       
       .view-toggle {
         margin: 0 10px;
@@ -694,6 +650,9 @@ watch(tabActive, () => {
           }
         }
       }
+      :deep(.el-input-group__prepend .el-select){
+        margin: 0!important;
+      }
     }
   }
 }
@@ -798,4 +757,18 @@ watch(tabActive, () => {
     }
   }
 }
+
+.content-wrapper {
+  margin: 20px 0;
+}
+
+.operation-buttons {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.text-danger {
+  color: #f56c6c;
+}
 </style>