wangfumin 1 vecka sedan
förälder
incheckning
1a31371124
2 ändrade filer med 240 tillägg och 86 borttagningar
  1. 194 80
      src/views/pc/information/components/MemberTable.vue
  2. 46 6
      src/views/pc/information/index.vue

+ 194 - 80
src/views/pc/information/components/MemberTable.vue

@@ -9,7 +9,7 @@
         
         <div class="search-wrapper">
           <el-input
-            v-model="form.snCode"
+            v-model="keyword"
             placeholder="搜索相机SN码"
             class="search-input"
           >
@@ -26,20 +26,20 @@
       
       <div class="table-content">
         <el-table
-          :data="tableData"
+          :data="list"
           style="width: 100%"
           stripe
           border
           v-loading="loading"
         >
           <el-table-column
-            prop="packageName"
+            prop="id"
             label="会员权益ID"
             width="180"
             align="center"
           >
             <template #default="{ row }">
-              <span>{{ row.packageName || '--' }}</span>
+              <span>{{ row.id || '--' }}</span>
             </template>
           </el-table-column>
           
@@ -50,31 +50,66 @@
             align="center"
           >
             <template #default="{ row }">
-              <span>{{ row.snCode || '未绑定' }}</span>
+              <span>{{ row.snCode || '-' }}</span>
             </template>
           </el-table-column>
           
           <el-table-column
-            prop="buyDate"
+            prop="incrementStartStr"
             label="购买日期"
             width="150"
             align="center"
           >
             <template #default="{ row }">
-              <span>{{ row.buyDate || '--' }}</span>
+              <span>{{ row.incrementStartStr || '--' }}</span>
             </template>
           </el-table-column>
           
           <el-table-column
-            prop="expiredDate"
+            prop="incrementEndStr"
             label="到期日期"
             width="150"
             align="center"
           >
             <template #default="{ row }">
-              <span :class="{ 'is-expired': isExpired(row.expiredDate) }">
-                {{ row.expiredDate || '--' }}
-              </span>
+              <div v-if="row.isExpire" class="is-expired">
+                {{ $t("manage.information.memberTable.isExpired") }}
+              </div>
+              <div v-else class="expired-icon-l">
+                {{row.incrementEndStr}}
+                <h-icon
+                  type="register_agreement"
+                  class="expired-icon"
+                  v-if="isWillExpired(row)"
+                  @click.stop="setTimeoutShowCtrls(true, row)"
+                ></h-icon>
+                <div
+                  v-show="showCtrls === row.id"
+                  class="expired-ctrls-w"
+                  @click.stop
+                >
+                  <p v-if="!expired(row).expiredTime">{{$t('manage.member.memberjtdq')}}</p>
+                  <p v-else-if="expired(row).trueExpired">
+                    {{ $t("manage.deviceAdmin.memberExpired") }}
+                  </p>
+                  <p
+                    v-else
+                    v-html="
+                      expired(row).expiredTime ? $t('manage.deviceAdmin.memberWillExpired', {
+                        expired: expired(row).expiredText,
+                      }) : $t('manage.member.memberjtdq')
+                    "
+                  ></p>
+                  <div class="ctrls-w">
+                    <div class="btn cancel-btn" @click="showCtrls = null">
+                      {{ $t("common.cancle") }}
+                    </div>
+                    <div class="btn submit-btn" @click="renew(row)">
+                      {{$t('manage.member.xf')}}
+                    </div>
+                  </div>
+                </div>
+              </div>
             </template>
           </el-table-column>
           
@@ -95,7 +130,7 @@
                 <el-button
                   type="primary"
                   link
-                  @click="handleRenew(row)"
+                  @click="renew(row)"
                 >
                   续费
                 </el-button>
@@ -119,7 +154,7 @@
         <Paging
             v-if="total"
             :total="total"
-            :current="form.pageNum"
+            :current="pageNum"
             @clickHandle="pageChange"
           />
         
@@ -162,78 +197,85 @@
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from 'vue'
+import { ref, watch, reactive, onMounted, onUnmounted } from 'vue'
 import { ElMessage } from 'element-plus'
-// import { Search } from '@element-plus/icons-vue'
+import Paging from '@/components/pc/Paging/index.vue'
+const emit = defineEmits(['search', 'page-change'])
+const props = defineProps({
+  list: {
+    type: Array,
+    default: () => []
+  },
+  total: {
+    type: Number,
+    default: 0
+  },
+  pageNum: {
+    type: Number,
+    default: 1
+  },
+  loading: {
+    type: Boolean,
+    default: false
+  },
+  snCode: {
+    type: String,
+    default: ''
+  }
+})
 
-// 响应式数据
-const tableData = ref([])
-const total = ref(0)
-const loading = ref(false)
+// UI 状态
 const showBindDialog = ref(false)
 const selectedItem = ref({})
 const isSearching = ref(false)
+const keyword = ref(props.snCode || '')
+const showCtrls = ref(null)
+const timer = ref(null)
 
-const form = reactive({
-  pageNum: 1,
-  pageSize: 10,
-  snCode: ''
+watch(() => props.snCode, (val) => {
+  if (val !== keyword.value) keyword.value = val || ''
 })
 
 const bindForm = reactive({
   snCode: ''
 })
-
-// 方法
-const getTableList = () => {
-  loading.value = true
-  // 模拟API调用
-  setTimeout(() => {
-    // 模拟数据
-    const mockData = [
-      {
-        id: 1,
-        packageName: '基础套餐',
-        snCode: 'SN123456789',
-        buyDate: '2024-01-15',
-        expiredDate: '2024-12-15'
-      },
-      {
-        id: 2,
-        packageName: '高级套餐',
-        snCode: '',
-        buyDate: '2024-02-20',
-        expiredDate: '2024-11-20'
-      },
-      {
-        id: 3,
-        packageName: '专业套餐',
-        snCode: 'SN987654321',
-        buyDate: '2023-12-10',
-        expiredDate: '2024-01-10'
-      }
-    ]
-    
-    tableData.value = mockData
-    total.value = mockData.length
-    loading.value = false
-  }, 1000)
+const isWillExpired = (item) => {
+  return new Date(item.incrementEndStr) - new Date() <= 86400000 * 30
 }
 
-const handleKeywordSearch = () => {
-  form.pageNum = 1
-  isSearching.value = !!form.snCode
-  getTableList()
+const expired = (item) => {
+  if (!item.incrementEndStr) {
+    return {}
+  }
+  let expired =
+    Math.floor((new Date(item.incrementEndStr) - new Date()) / 86400000) + 1
+
+  return {
+    expiredTime: expired,
+    expiredText: `<span class="expired" style="color: #ff3e3e">${expired}</span>`,
+    isExpired: expired <= 30,
+    trueExpired: expired < 0
+  }
 }
 
-const handleSizeChange = (size) => {
-  form.pageSize = size
-  getTableList()
+const setTimeoutShowCtrls = (show, item) => {
+  if (timer.value) {
+    clearTimeout(timer.value)
+  }
+  timer.value = setTimeout(() => {
+    showCtrls.value = show ? item.id : null
+  }, 200)
 }
 
-const handleCurrentChange = (page) => {
-  form.pageNum = page
-  getTableList()
+const renew = (item) => {
+  selectedItem.value = item
+  ElMessage.info('跳转到续费页面')
+  // 这里可以添加路由跳转逻辑
+}
+// 方法
+const handleKeywordSearch = () => {
+  isSearching.value = !!keyword.value
+  emit('search', keyword.value.trim())
 }
 
 const handleBind = (item) => {
@@ -252,28 +294,36 @@ const handleBindConfirm = () => {
   setTimeout(() => {
     ElMessage.success('绑定成功')
     showBindDialog.value = false
-    getTableList()
+    emit('search', keyword.value.trim())
   }, 1000)
 }
 
-const handleRenew = (item) => {
-  selectedItem.value = item
-  ElMessage.info('跳转到续费页面')
-  // 这里可以添加路由跳转逻辑
-}
 
 const goToBuy = () => {
   ElMessage.info('跳转到购买页面')
   // 这里可以添加路由跳转逻辑
 }
 
-const isExpired = (date) => {
-  if (!date) return false
-  return new Date(date) < new Date()
+// removed unused isExpired
+
+const pageChange = (newPage) => {
+  emit('page-change', newPage)
+}
+
+// 全局点击事件处理
+const handleGlobalClick = () => {
+  setTimeoutShowCtrls(false, {})
 }
 
 onMounted(() => {
-  getTableList()
+  document.documentElement.addEventListener('click', handleGlobalClick)
+})
+
+onUnmounted(() => {
+  document.documentElement.removeEventListener('click', handleGlobalClick)
+  if (timer.value) {
+    clearTimeout(timer.value)
+  }
 })
 </script>
 
@@ -378,9 +428,73 @@ onMounted(() => {
       justify-content: center;
       gap: 16px;
     }
-    
     .is-expired {
-      color: #f56c6c;
+      color: #ff0000;
+    }
+    .expired-icon {
+      margin-left: 6px;
+      color: #ff0000;
+      font-size: 14px;
+      cursor: pointer;
+    }
+    
+    .expired-icon-l {
+      position: relative;
+      
+      .expired-ctrls-w {
+        position: absolute;
+        right: 0px;
+        background: #fff;
+        z-index: 1000;
+        min-width: 172px;
+        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;
+
+        p {
+          white-space: nowrap;
+          margin: 0;
+        }
+        
+        &::before {
+          content: "";
+          display: block;
+          border: 8px solid transparent;
+          border-bottom-color: #fff;
+          position: absolute;
+          top: -15px;
+          left: 90px;
+          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;
+          font-size: 12px;
+        }
+        
+        .submit-btn {
+          background: #15bec8;
+          margin-left: 8px;
+          border-radius: 2px;
+          color: #fff;
+        }
+      }
     }
     
     .pagination {

+ 46 - 6
src/views/pc/information/index.vue

@@ -58,11 +58,20 @@
     </div>
     
     <!-- 会员表格 -->
-    <MemberTable />
+    <MemberTable
+      :list="list"
+      :total="total"
+      :pageNum="pageNum"
+      :loading="loading"
+      :snCode="snCode"
+      @search="onSearch"
+      @page-change="onPageChange"
+    />
   </div>
 </template>
 
 <script setup>
+defineOptions({ name: 'PcInformationIndex' })
 import { ref, onMounted, computed } from 'vue'
 import { useUserStore } from '@/stores/user'
 import { findSceneNumber, findIncrementList } from '@/api/information/index'
@@ -87,17 +96,48 @@ const getTotalScene = async () => {
   totalScene.value = SS.sceneNum + kJ.sceneNum + kk.sceneNum + SG.sceneNum + sx.sceneNum
 }
 
-const getfindIncrementList = async () => {
-  const res = await findIncrementList({})
-  const {cameraCount} = res
-  totalDevice.value = cameraCount
+const list = ref([])
+const pageNum = ref(1)
+const pageSize = ref(10)
+const total = ref(0)
+const snCode = ref('')
+const loading = ref(false)
+
+const fetchList = async () => {
+  loading.value = true
+  try {
+    const res = await findIncrementList({
+      pageNum: pageNum.value,
+      pageSize: pageSize.value,
+      snCode: snCode.value?.trim() || ''
+    })
+    // 兼容返回结构:res 或 res.data
+    const data = res?.data || res
+    list.value = data?.list || []
+    pageNum.value = data?.pageNum || pageNum.value
+    pageSize.value = data?.pageSize || pageSize.value
+    total.value = data?.total || 0
+  } finally {
+    loading.value = false
+  }
+}
+
+const onSearch = (kw) => {
+  snCode.value = kw || ''
+  pageNum.value = 1
+  fetchList()
+}
+
+const onPageChange = (newPage) => {
+  pageNum.value = newPage || 1
+  fetchList()
 }
 
 onMounted(() => {
   // 页面初始化逻辑
   info.value = userStore.info
   getTotalScene()
-  getfindIncrementList()
+  fetchList()
 })
 </script>