Ver Fonte

Refactor camera and user components for improved readability and consistency. Adjust template formatting and script structure in `channel.vue`, `index.vue`, and `user/index.vue`, ensuring consistent style and indentation throughout the files. Enhance user input handling and update query parameters for better data management.

yb há 3 semanas atrás
pai
commit
b10927f64b
3 ficheiros alterados com 556 adições e 277 exclusões
  1. 104 50
      src/views/camera/channel.vue
  2. 201 93
      src/views/camera/index.vue
  3. 251 134
      src/views/user/index.vue

+ 104 - 50
src/views/camera/channel.vue

@@ -10,16 +10,27 @@
     <div class="search-form">
       <el-form :model="queryParams" inline>
         <el-form-item label="通道名称">
-          <el-input v-model="queryParams.name" placeholder="请输入通道名称" clearable @keyup.enter="handleQuery" />
+          <el-input
+            v-model="queryParams.name"
+            placeholder="请输入通道名称"
+            clearable
+            @keyup.enter="handleQuery"
+          />
         </el-form-item>
         <el-form-item label="在线状态">
-          <el-select v-model="queryParams.status" placeholder="请选择" clearable>
+          <el-select
+            v-model="queryParams.status"
+            placeholder="请选择"
+            clearable
+          >
             <el-option label="在线" :value="true" />
             <el-option label="离线" :value="false" />
           </el-select>
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
+          <el-button type="primary" :icon="Search" @click="handleQuery"
+            >搜索</el-button
+          >
           <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
         </el-form-item>
       </el-form>
@@ -28,11 +39,36 @@
     <!-- 数据表格 -->
     <el-table v-loading="loading" :data="channelList" border>
       <el-table-column type="index" label="序号" width="60" align="center" />
-      <el-table-column prop="channelId" label="通道ID" min-width="180" show-overflow-tooltip />
-      <el-table-column prop="name" label="通道名称" min-width="150" show-overflow-tooltip />
-      <el-table-column prop="manufacturer" label="厂商" min-width="120" show-overflow-tooltip />
-      <el-table-column prop="address" label="地址" min-width="150" show-overflow-tooltip />
-      <el-table-column prop="ptzType" label="云台类型" width="100" align="center">
+      <el-table-column
+        prop="channelId"
+        label="通道ID"
+        min-width="180"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="name"
+        label="通道名称"
+        min-width="150"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="manufacturer"
+        label="厂商"
+        min-width="120"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="address"
+        label="地址"
+        min-width="150"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="ptzType"
+        label="云台类型"
+        width="100"
+        align="center"
+      >
         <template #default="{ row }">
           <span>{{ getPtzType(row.ptzType) }}</span>
         </template>
@@ -40,16 +76,28 @@
       <el-table-column prop="status" label="状态" width="80" align="center">
         <template #default="{ row }">
           <el-tag :type="row.status ? 'success' : 'danger'">
-            {{ row.status ? '在线' : '离线' }}
+            {{ row.status ? "在线" : "离线" }}
           </el-tag>
         </template>
       </el-table-column>
       <el-table-column label="操作" width="200" align="center" fixed="right">
         <template #default="{ row }">
-          <el-button type="primary" link :icon="VideoPlay" @click="handlePlay(row)" :disabled="!row.status">
+          <el-button
+            type="primary"
+            link
+            :icon="VideoPlay"
+            @click="handlePlay(row)"
+            :disabled="!row.status"
+          >
             实时播放
           </el-button>
-          <el-button type="success" link :icon="VideoCamera" @click="handlePlayback(row)" :disabled="!row.status">
+          <el-button
+            type="success"
+            link
+            :icon="VideoCamera"
+            @click="handlePlayback(row)"
+            :disabled="!row.status"
+          >
             录像回放
           </el-button>
         </template>
@@ -71,78 +119,84 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue'
-import { useRoute, useRouter } from 'vue-router'
-import { Search, Refresh, ArrowLeft, VideoPlay, VideoCamera } from '@element-plus/icons-vue'
-import { listChannel } from '@/api/camera'
-import type { CameraChannel } from '@/types'
-
-const route = useRoute()
-const router = useRouter()
-
-const deviceId = route.params.deviceId as string
-const loading = ref(false)
-const channelList = ref<CameraChannel[]>([])
-const total = ref(0)
+import { ref, reactive, onMounted } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import {
+  Search,
+  Refresh,
+  ArrowLeft,
+  VideoPlay,
+  VideoCamera,
+} from "@element-plus/icons-vue";
+import { listChannel } from "@/api/camera";
+import type { CameraChannel } from "@/types";
+
+const route = useRoute();
+const router = useRouter();
+
+const deviceId = route.params.deviceId as string;
+const loading = ref(false);
+const channelList = ref<CameraChannel[]>([]);
+const total = ref(0);
 
 const queryParams = reactive({
   pageNum: 1,
   pageSize: 10,
-  name: '',
-  status: undefined as boolean | undefined
-})
+  name: "",
+  status: undefined as boolean | undefined,
+});
 
 function getPtzType(type: number | undefined): string {
   const types: Record<number, string> = {
-    0: '未知',
-    1: '球机',
-    2: '半球',
-    3: '固定枪机',
-    4: '遥控枪机'
-  }
-  return types[type || 0] || '未知'
+    0: "未知",
+    1: "球机",
+    2: "半球",
+    3: "固定枪机",
+    4: "遥控枪机",
+  };
+  return types[type || 0] || "未知";
 }
 
 async function getList() {
-  loading.value = true
+  loading.value = true;
   try {
-    const res = await listChannel(deviceId, queryParams)
+    const res = await listChannel(deviceId, queryParams);
     if (res.code === 200) {
-      channelList.value = res.data.rows || res.rows || []
-      total.value = res.data.total || res.total || 0
+      channelList.value = res.data.rows;
+      total.value = res.data.total;
     }
   } finally {
-    loading.value = false
+    loading.value = false;
   }
 }
 
 function handleQuery() {
-  queryParams.pageNum = 1
-  getList()
+  queryParams.pageNum = 1;
+  getList();
 }
 
 function resetQuery() {
-  queryParams.pageNum = 1
-  queryParams.name = ''
-  queryParams.status = undefined
-  getList()
+  queryParams.pageNum = 1;
+  queryParams.name = "";
+  queryParams.status = undefined;
+  getList();
 }
 
 function goBack() {
-  router.push('/camera')
+  router.push("/camera");
 }
 
 function handlePlay(row: CameraChannel) {
-  router.push(`/camera/video/${deviceId}/${row.channelId}?mode=live`)
+  router.push(`/camera/video/${deviceId}/${row.channelId}?mode=live`);
 }
 
 function handlePlayback(row: CameraChannel) {
-  router.push(`/camera/video/${deviceId}/${row.channelId}?mode=playback`)
+  router.push(`/camera/video/${deviceId}/${row.channelId}?mode=playback`);
 }
 
 onMounted(() => {
-  getList()
-})
+  getList();
+});
 </script>
 
 <style lang="scss" scoped>

+ 201 - 93
src/views/camera/index.vue

@@ -4,19 +4,35 @@
     <div class="search-form">
       <el-form :model="queryParams" inline>
         <el-form-item label="设备ID">
-          <el-input v-model="queryParams.deviceId" placeholder="请输入设备ID" clearable @keyup.enter="handleQuery" />
+          <el-input
+            v-model="queryParams.deviceId"
+            placeholder="请输入设备ID"
+            clearable
+            @keyup.enter="handleQuery"
+          />
         </el-form-item>
         <el-form-item label="设备名称">
-          <el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" />
+          <el-input
+            v-model="queryParams.deviceName"
+            placeholder="请输入设备名称"
+            clearable
+            @keyup.enter="handleQuery"
+          />
         </el-form-item>
         <el-form-item label="在线状态">
-          <el-select v-model="queryParams.online" placeholder="请选择" clearable>
+          <el-select
+            v-model="queryParams.online"
+            placeholder="请选择"
+            clearable
+          >
             <el-option label="在线" :value="true" />
             <el-option label="离线" :value="false" />
           </el-select>
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
+          <el-button type="primary" :icon="Search" @click="handleQuery"
+            >搜索</el-button
+          >
           <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
         </el-form-item>
       </el-form>
@@ -24,32 +40,86 @@
 
     <!-- 操作按钮 -->
     <div class="table-actions">
-      <el-button type="primary" :icon="Plus" @click="handleAdd">新增设备</el-button>
-      <el-button type="success" :icon="Refresh" @click="getList">刷新列表</el-button>
+      <el-button type="primary" :icon="Plus" @click="handleAdd"
+        >新增设备</el-button
+      >
+      <el-button type="success" :icon="Refresh" @click="getList"
+        >刷新列表</el-button
+      >
     </div>
 
     <!-- 数据表格 -->
     <el-table v-loading="loading" :data="deviceList" border>
       <el-table-column type="index" label="序号" width="60" align="center" />
-      <el-table-column prop="deviceId" label="设备ID" min-width="180" show-overflow-tooltip />
-      <el-table-column prop="deviceName" label="设备名称" min-width="150" show-overflow-tooltip />
-      <el-table-column prop="manufacturer" label="厂商" min-width="120" show-overflow-tooltip />
-      <el-table-column prop="hostAddress" label="地址" min-width="150" show-overflow-tooltip />
-      <el-table-column prop="channelCount" label="通道数" width="80" align="center" />
+      <el-table-column
+        prop="deviceId"
+        label="设备ID"
+        min-width="180"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="deviceName"
+        label="设备名称"
+        min-width="150"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="manufacturer"
+        label="厂商"
+        min-width="120"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="hostAddress"
+        label="地址"
+        min-width="150"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        prop="channelCount"
+        label="通道数"
+        width="80"
+        align="center"
+      />
       <el-table-column prop="online" label="状态" width="80" align="center">
         <template #default="{ row }">
           <el-tag :type="row.online ? 'success' : 'danger'">
-            {{ row.online ? '在线' : '离线' }}
+            {{ row.online ? "在线" : "离线" }}
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="registerTime" label="注册时间" width="160" align="center" />
+      <el-table-column
+        prop="registerTime"
+        label="注册时间"
+        width="160"
+        align="center"
+      />
       <el-table-column label="操作" width="280" align="center" fixed="right">
         <template #default="{ row }">
-          <el-button type="primary" link :icon="View" @click="handleChannel(row)">通道</el-button>
-          <el-button type="primary" link :icon="Refresh" @click="handleSync(row)">同步</el-button>
-          <el-button type="primary" link :icon="Edit" @click="handleEdit(row)">编辑</el-button>
-          <el-button type="danger" link :icon="Delete" @click="handleDelete(row)">删除</el-button>
+          <el-button
+            type="primary"
+            link
+            :icon="View"
+            @click="handleChannel(row)"
+            >通道</el-button
+          >
+          <el-button
+            type="primary"
+            link
+            :icon="Refresh"
+            @click="handleSync(row)"
+            >同步</el-button
+          >
+          <el-button type="primary" link :icon="Edit" @click="handleEdit(row)"
+            >编辑</el-button
+          >
+          <el-button
+            type="danger"
+            link
+            :icon="Delete"
+            @click="handleDelete(row)"
+            >删除</el-button
+          >
         </template>
       </el-table-column>
     </el-table>
@@ -67,16 +137,30 @@
     />
 
     <!-- 新增/编辑弹窗 -->
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" destroy-on-close>
+    <el-dialog
+      v-model="dialogVisible"
+      :title="dialogTitle"
+      width="600px"
+      destroy-on-close
+    >
       <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
         <el-form-item label="设备ID" prop="deviceId">
-          <el-input v-model="form.deviceId" placeholder="请输入设备ID" :disabled="!!form.id" />
+          <el-input
+            v-model="form.deviceId"
+            placeholder="请输入设备ID"
+            :disabled="!!form.id"
+          />
         </el-form-item>
         <el-form-item label="设备名称" prop="deviceName">
           <el-input v-model="form.deviceName" placeholder="请输入设备名称" />
         </el-form-item>
         <el-form-item label="密码" prop="password">
-          <el-input v-model="form.password" type="password" placeholder="请输入密码" show-password />
+          <el-input
+            v-model="form.password"
+            type="password"
+            placeholder="请输入密码"
+            show-password
+          />
         </el-form-item>
         <el-form-item label="传输协议" prop="transport">
           <el-select v-model="form.transport" placeholder="请选择">
@@ -93,154 +177,178 @@
       </el-form>
       <template #footer>
         <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="handleSubmit"
+          >确定</el-button
+        >
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted, computed } from 'vue'
-import { useRouter } from 'vue-router'
-import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
-import { Search, Refresh, Plus, View, Edit, Delete } from '@element-plus/icons-vue'
-import { listDevice, addDevice, updateDevice, delDevice, syncChannel } from '@/api/camera'
-import type { CameraDevice } from '@/types'
+import { ref, reactive, onMounted, computed } from "vue";
+import { useRouter } from "vue-router";
+import {
+  ElMessage,
+  ElMessageBox,
+  type FormInstance,
+  type FormRules,
+} from "element-plus";
+import {
+  Search,
+  Refresh,
+  Plus,
+  View,
+  Edit,
+  Delete,
+} from "@element-plus/icons-vue";
+import {
+  listDevice,
+  addDevice,
+  updateDevice,
+  delDevice,
+  syncChannel,
+} from "@/api/camera";
+import type { CameraDevice } from "@/types";
 
-const router = useRouter()
+const router = useRouter();
 
-const loading = ref(false)
-const submitLoading = ref(false)
-const deviceList = ref<CameraDevice[]>([])
-const total = ref(0)
-const dialogVisible = ref(false)
-const formRef = ref<FormInstance>()
+const loading = ref(false);
+const submitLoading = ref(false);
+const deviceList = ref<CameraDevice[]>([]);
+const total = ref(0);
+const dialogVisible = ref(false);
+const formRef = ref<FormInstance>();
 
 const queryParams = reactive({
   pageNum: 1,
   pageSize: 10,
-  deviceId: '',
-  deviceName: '',
-  online: undefined as boolean | undefined
-})
+  deviceId: "",
+  deviceName: "",
+  online: undefined as boolean | undefined,
+});
 
 const form = reactive<Partial<CameraDevice> & { id?: string }>({
-  deviceId: '',
-  deviceName: '',
-  password: '',
-  transport: 'UDP',
-  charset: 'GB2312'
-})
+  deviceId: "",
+  deviceName: "",
+  password: "",
+  transport: "UDP",
+  charset: "GB2312",
+});
 
-const dialogTitle = computed(() => form.id ? '编辑设备' : '新增设备')
+const dialogTitle = computed(() => (form.id ? "编辑设备" : "新增设备"));
 
 const rules: FormRules = {
-  deviceId: [{ required: true, message: '请输入设备ID', trigger: 'blur' }],
-  deviceName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }]
-}
+  deviceId: [{ required: true, message: "请输入设备ID", trigger: "blur" }],
+  deviceName: [{ required: true, message: "请输入设备名称", trigger: "blur" }],
+};
 
 async function getList() {
-  loading.value = true
+  loading.value = true;
   try {
-    const res = await listDevice(queryParams)
+    const res = await listDevice(queryParams);
     if (res.code === 200) {
-      deviceList.value = res.data.rows || res.rows || []
-      total.value = res.data.total || res.total || 0
+      deviceList.value = res.data.rows;
+      total.value = res.data.total;
     }
   } finally {
-    loading.value = false
+    loading.value = false;
   }
 }
 
 function handleQuery() {
-  queryParams.pageNum = 1
-  getList()
+  queryParams.pageNum = 1;
+  getList();
 }
 
 function resetQuery() {
-  queryParams.pageNum = 1
-  queryParams.deviceId = ''
-  queryParams.deviceName = ''
-  queryParams.online = undefined
-  getList()
+  queryParams.pageNum = 1;
+  queryParams.deviceId = "";
+  queryParams.deviceName = "";
+  queryParams.online = undefined;
+  getList();
 }
 
 function handleAdd() {
   Object.assign(form, {
     id: undefined,
-    deviceId: '',
-    deviceName: '',
-    password: '',
-    transport: 'UDP',
-    charset: 'GB2312'
-  })
-  dialogVisible.value = true
+    deviceId: "",
+    deviceName: "",
+    password: "",
+    transport: "UDP",
+    charset: "GB2312",
+  });
+  dialogVisible.value = true;
 }
 
 function handleEdit(row: CameraDevice) {
   Object.assign(form, {
     id: row.deviceId,
-    ...row
-  })
-  dialogVisible.value = true
+    ...row,
+  });
+  dialogVisible.value = true;
 }
 
 function handleChannel(row: CameraDevice) {
-  router.push(`/camera/channel/${row.deviceId}`)
+  router.push(`/camera/channel/${row.deviceId}`);
 }
 
 async function handleSync(row: CameraDevice) {
   try {
-    const res = await syncChannel(row.deviceId)
+    const res = await syncChannel(row.deviceId);
     if (res.code === 200) {
-      ElMessage.success('同步成功')
-      getList()
+      ElMessage.success("同步成功");
+      getList();
     }
   } catch (error) {
-    console.error('同步失败', error)
+    console.error("同步失败", error);
   }
 }
 
 async function handleDelete(row: CameraDevice) {
   try {
-    await ElMessageBox.confirm(`确定要删除设备 "${row.deviceName}" 吗?`, '提示', {
-      type: 'warning'
-    })
-    const res = await delDevice(row.deviceId)
+    await ElMessageBox.confirm(
+      `确定要删除设备 "${row.deviceName}" 吗?`,
+      "提示",
+      {
+        type: "warning",
+      }
+    );
+    const res = await delDevice(row.deviceId);
     if (res.code === 200) {
-      ElMessage.success('删除成功')
-      getList()
+      ElMessage.success("删除成功");
+      getList();
     }
   } catch (error) {
-    if (error !== 'cancel') {
-      console.error('删除失败', error)
+    if (error !== "cancel") {
+      console.error("删除失败", error);
     }
   }
 }
 
 async function handleSubmit() {
-  if (!formRef.value) return
+  if (!formRef.value) return;
 
   await formRef.value.validate(async (valid) => {
     if (valid) {
-      submitLoading.value = true
+      submitLoading.value = true;
       try {
-        const res = form.id ? await updateDevice(form) : await addDevice(form)
+        const res = form.id ? await updateDevice(form) : await addDevice(form);
         if (res.code === 200) {
-          ElMessage.success(form.id ? '修改成功' : '新增成功')
-          dialogVisible.value = false
-          getList()
+          ElMessage.success(form.id ? "修改成功" : "新增成功");
+          dialogVisible.value = false;
+          getList();
         }
       } finally {
-        submitLoading.value = false
+        submitLoading.value = false;
       }
     }
-  })
+  });
 }
 
 onMounted(() => {
-  getList()
-})
+  getList();
+});
 </script>
 
 <style lang="scss" scoped>

+ 251 - 134
src/views/user/index.vue

@@ -4,17 +4,28 @@
     <div class="search-form">
       <el-form :model="queryParams" inline>
         <el-form-item label="搜索">
-          <el-input v-model="queryParams.search" placeholder="请输入用户名" clearable @keyup.enter="handleQuery" />
+          <el-input
+            v-model="queryParams.search"
+            placeholder="请输入用户名"
+            clearable
+            @keyup.enter="handleQuery"
+          />
         </el-form-item>
         <el-form-item label="角色">
-          <el-select v-model="queryParams.role" placeholder="请选择角色" clearable>
+          <el-select
+            v-model="queryParams.role"
+            placeholder="请选择角色"
+            clearable
+          >
             <el-option label="管理员" value="admin" />
             <el-option label="操作员" value="operator" />
             <el-option label="观察者" value="viewer" />
           </el-select>
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
+          <el-button type="primary" :icon="Search" @click="handleQuery"
+            >搜索</el-button
+          >
           <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
         </el-form-item>
       </el-form>
@@ -22,8 +33,12 @@
 
     <!-- 操作按钮 -->
     <div class="table-actions">
-      <el-button type="primary" :icon="Plus" @click="handleAdd">新增用户</el-button>
-      <el-button type="success" :icon="Refresh" @click="getList">刷新列表</el-button>
+      <el-button type="primary" :icon="Plus" @click="handleAdd"
+        >新增用户</el-button
+      >
+      <el-button type="success" :icon="Refresh" @click="getList"
+        >刷新列表</el-button
+      >
     </div>
 
     <!-- 数据表格 -->
@@ -32,24 +47,50 @@
       <el-table-column prop="username" label="用户名" min-width="150" />
       <el-table-column prop="role" label="角色" width="120" align="center">
         <template #default="{ row }">
-          <el-tag :type="getRoleTagType(row.role)">{{ getRoleLabel(row.role) }}</el-tag>
+          <el-tag :type="getRoleTagType(row.role)">{{
+            getRoleLabel(row.role)
+          }}</el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="createdAt" label="创建时间" width="180" align="center">
+      <el-table-column
+        prop="createdAt"
+        label="创建时间"
+        width="180"
+        align="center"
+      >
         <template #default="{ row }">
           {{ formatTime(row.createdAt) }}
         </template>
       </el-table-column>
-      <el-table-column prop="updatedAt" label="更新时间" width="180" align="center">
+      <el-table-column
+        prop="updatedAt"
+        label="更新时间"
+        width="180"
+        align="center"
+      >
         <template #default="{ row }">
           {{ formatTime(row.updatedAt) }}
         </template>
       </el-table-column>
       <el-table-column label="操作" width="220" align="center" fixed="right">
         <template #default="{ row }">
-          <el-button type="primary" link :icon="Edit" @click="handleEdit(row)">编辑</el-button>
-          <el-button type="warning" link :icon="Key" @click="handleResetPassword(row)">重置密码</el-button>
-          <el-button type="danger" link :icon="Delete" @click="handleDelete(row)">删除</el-button>
+          <el-button type="primary" link :icon="Edit" @click="handleEdit(row)"
+            >编辑</el-button
+          >
+          <el-button
+            type="warning"
+            link
+            :icon="Key"
+            @click="handleResetPassword(row)"
+            >重置密码</el-button
+          >
+          <el-button
+            type="danger"
+            link
+            :icon="Delete"
+            @click="handleDelete(row)"
+            >删除</el-button
+          >
         </template>
       </el-table-column>
     </el-table>
@@ -67,16 +108,34 @@
     />
 
     <!-- 新增/编辑弹窗 -->
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" destroy-on-close>
+    <el-dialog
+      v-model="dialogVisible"
+      :title="dialogTitle"
+      width="500px"
+      destroy-on-close
+    >
       <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="用户名" prop="username">
-          <el-input v-model="form.username" placeholder="请输入用户名" :disabled="isEdit" />
+          <el-input
+            v-model="form.username"
+            placeholder="请输入用户名"
+            :disabled="isEdit"
+          />
         </el-form-item>
         <el-form-item v-if="!isEdit" label="密码" prop="password">
-          <el-input v-model="form.password" type="password" placeholder="请输入密码" show-password />
+          <el-input
+            v-model="form.password"
+            type="password"
+            placeholder="请输入密码"
+            show-password
+          />
         </el-form-item>
         <el-form-item label="角色" prop="role">
-          <el-select v-model="form.role" placeholder="请选择角色" style="width: 100%">
+          <el-select
+            v-model="form.role"
+            placeholder="请选择角色"
+            style="width: 100%"
+          >
             <el-option label="管理员" value="admin" />
             <el-option label="操作员" value="operator" />
             <el-option label="观察者" value="viewer" />
@@ -85,245 +144,303 @@
       </el-form>
       <template #footer>
         <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="handleSubmit"
+          >确定</el-button
+        >
       </template>
     </el-dialog>
 
     <!-- 重置密码弹窗 -->
-    <el-dialog v-model="resetDialogVisible" title="重置密码" width="400px" destroy-on-close>
-      <el-form ref="resetFormRef" :model="resetForm" :rules="resetRules" label-width="80px">
+    <el-dialog
+      v-model="resetDialogVisible"
+      title="重置密码"
+      width="400px"
+      destroy-on-close
+    >
+      <el-form
+        ref="resetFormRef"
+        :model="resetForm"
+        :rules="resetRules"
+        label-width="80px"
+      >
         <el-form-item label="新密码" prop="password">
-          <el-input v-model="resetForm.password" type="password" placeholder="请输入新密码" show-password />
+          <el-input
+            v-model="resetForm.password"
+            type="password"
+            placeholder="请输入新密码"
+            show-password
+          />
         </el-form-item>
         <el-form-item label="确认密码" prop="confirmPassword">
-          <el-input v-model="resetForm.confirmPassword" type="password" placeholder="请再次输入密码" show-password />
+          <el-input
+            v-model="resetForm.confirmPassword"
+            type="password"
+            placeholder="请再次输入密码"
+            show-password
+          />
         </el-form-item>
       </el-form>
       <template #footer>
         <el-button @click="resetDialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="resetLoading" @click="handleResetSubmit">确定</el-button>
+        <el-button
+          type="primary"
+          :loading="resetLoading"
+          @click="handleResetSubmit"
+          >确定</el-button
+        >
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted } from 'vue'
-import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
-import { Search, Refresh, Plus, Edit, Delete, Key } from '@element-plus/icons-vue'
-import { listUsers, createUser, updateUser, deleteUser, resetPassword, type User, type UserForm } from '@/api/user'
-
-const loading = ref(false)
-const submitLoading = ref(false)
-const resetLoading = ref(false)
-const userList = ref<User[]>([])
-const total = ref(0)
-const dialogVisible = ref(false)
-const resetDialogVisible = ref(false)
-const formRef = ref<FormInstance>()
-const resetFormRef = ref<FormInstance>()
-const currentUserId = ref('')
+import { ref, reactive, computed, onMounted } from "vue";
+import {
+  ElMessage,
+  ElMessageBox,
+  type FormInstance,
+  type FormRules,
+} from "element-plus";
+import {
+  Search,
+  Refresh,
+  Plus,
+  Edit,
+  Delete,
+  Key,
+} from "@element-plus/icons-vue";
+import {
+  listUsers,
+  createUser,
+  updateUser,
+  deleteUser,
+  resetPassword,
+  type User,
+  type UserForm,
+} from "@/api/user";
+
+const loading = ref(false);
+const submitLoading = ref(false);
+const resetLoading = ref(false);
+const userList = ref<User[]>([]);
+const total = ref(0);
+const dialogVisible = ref(false);
+const resetDialogVisible = ref(false);
+const formRef = ref<FormInstance>();
+const resetFormRef = ref<FormInstance>();
+const currentUserId = ref("");
 
 const queryParams = reactive({
   page: 1,
   pageSize: 10,
-  search: '',
-  role: '' as '' | 'admin' | 'operator' | 'viewer'
-})
+  search: "",
+  role: "" as "" | "admin" | "operator" | "viewer",
+});
 
 const form = reactive<UserForm & { id?: string }>({
-  username: '',
-  password: '',
-  role: 'viewer'
-})
+  username: "",
+  password: "",
+  role: "viewer",
+});
 
 const resetForm = reactive({
-  password: '',
-  confirmPassword: ''
-})
+  password: "",
+  confirmPassword: "",
+});
 
-const isEdit = computed(() => !!form.id)
-const dialogTitle = computed(() => isEdit.value ? '编辑用户' : '新增用户')
+const isEdit = computed(() => !!form.id);
+const dialogTitle = computed(() => (isEdit.value ? "编辑用户" : "新增用户"));
 
 const rules: FormRules = {
   username: [
-    { required: true, message: '请输入用户名', trigger: 'blur' },
-    { min: 3, max: 20, message: '用户名长度为3-20个字符', trigger: 'blur' }
+    { required: true, message: "请输入用户名", trigger: "blur" },
+    { min: 3, max: 20, message: "用户名长度为3-20个字符", trigger: "blur" },
   ],
   password: [
-    { required: true, message: '请输入密码', trigger: 'blur' },
-    { min: 6, max: 20, message: '密码长度为6-20个字符', trigger: 'blur' }
+    { required: true, message: "请输入密码", trigger: "blur" },
+    { min: 6, max: 20, message: "密码长度为6-20个字符", trigger: "blur" },
   ],
-  role: [{ required: true, message: '请选择角色', trigger: 'change' }]
-}
-
-const validateConfirmPassword = (_rule: any, value: string, callback: Function) => {
+  role: [{ required: true, message: "请选择角色", trigger: "change" }],
+};
+
+const validateConfirmPassword = (
+  _rule: any,
+  value: string,
+  callback: Function
+) => {
   if (value !== resetForm.password) {
-    callback(new Error('两次输入的密码不一致'))
+    callback(new Error("两次输入的密码不一致"));
   } else {
-    callback()
+    callback();
   }
-}
+};
 
 const resetRules: FormRules = {
   password: [
-    { required: true, message: '请输入新密码', trigger: 'blur' },
-    { min: 6, max: 20, message: '密码长度为6-20个字符', trigger: 'blur' }
+    { required: true, message: "请输入新密码", trigger: "blur" },
+    { min: 6, max: 20, message: "密码长度为6-20个字符", trigger: "blur" },
   ],
   confirmPassword: [
-    { required: true, message: '请再次输入密码', trigger: 'blur' },
-    { validator: validateConfirmPassword, trigger: 'blur' }
-  ]
-}
+    { required: true, message: "请再次输入密码", trigger: "blur" },
+    { validator: validateConfirmPassword, trigger: "blur" },
+  ],
+};
 
 function getRoleLabel(role: string) {
   const map: Record<string, string> = {
-    admin: '管理员',
-    operator: '操作员',
-    viewer: '观察者'
-  }
-  return map[role] || role
+    admin: "管理员",
+    operator: "操作员",
+    viewer: "观察者",
+  };
+  return map[role] || role;
 }
 
 function getRoleTagType(role: string) {
-  const map: Record<string, '' | 'success' | 'warning' | 'info' | 'danger'> = {
-    admin: 'danger',
-    operator: 'warning',
-    viewer: 'info'
-  }
-  return map[role] || 'info'
+  const map: Record<string, "" | "success" | "warning" | "info" | "danger"> = {
+    admin: "danger",
+    operator: "warning",
+    viewer: "info",
+  };
+  return map[role] || "info";
 }
 
 function formatTime(time?: string) {
-  if (!time) return '-'
-  return new Date(time).toLocaleString('zh-CN')
+  if (!time) return "-";
+  return new Date(time).toLocaleString("zh-CN");
 }
 
 async function getList() {
-  loading.value = true
+  loading.value = true;
   try {
     // Filter out empty strings to avoid validation errors
     const params: any = {
       page: queryParams.page,
-      pageSize: queryParams.pageSize
-    }
-    if (queryParams.search) params.search = queryParams.search
-    if (queryParams.role) params.role = queryParams.role
+      pageSize: queryParams.pageSize,
+    };
+    if (queryParams.search) params.search = queryParams.search;
+    if (queryParams.role) params.role = queryParams.role;
 
-    const res = await listUsers(params)
+    const res = await listUsers(params);
     if (res.code === 200) {
-      userList.value = res.data?.rows || res.rows || []
-      total.value = res.data?.total || res.total || 0
+      userList.value = res.data?.rows;
+      total.value = res.data?.total;
     }
   } finally {
-    loading.value = false
+    loading.value = false;
   }
 }
 
 function handleQuery() {
-  queryParams.page = 1
-  getList()
+  queryParams.page = 1;
+  getList();
 }
 
 function resetQuery() {
-  queryParams.page = 1
-  queryParams.search = ''
-  queryParams.role = ''
-  getList()
+  queryParams.page = 1;
+  queryParams.search = "";
+  queryParams.role = "";
+  getList();
 }
 
 function handleAdd() {
   Object.assign(form, {
     id: undefined,
-    username: '',
-    password: '',
-    role: 'viewer'
-  })
-  dialogVisible.value = true
+    username: "",
+    password: "",
+    role: "viewer",
+  });
+  dialogVisible.value = true;
 }
 
 function handleEdit(row: User) {
   Object.assign(form, {
     id: row.id,
     username: row.username,
-    password: '',
-    role: row.role
-  })
-  dialogVisible.value = true
+    password: "",
+    role: row.role,
+  });
+  dialogVisible.value = true;
 }
 
 function handleResetPassword(row: User) {
-  currentUserId.value = row.id
-  resetForm.password = ''
-  resetForm.confirmPassword = ''
-  resetDialogVisible.value = true
+  currentUserId.value = row.id;
+  resetForm.password = "";
+  resetForm.confirmPassword = "";
+  resetDialogVisible.value = true;
 }
 
 async function handleDelete(row: User) {
   try {
-    await ElMessageBox.confirm(`确定要删除用户 "${row.username}" 吗?`, '提示', {
-      type: 'warning'
-    })
-    const res = await deleteUser(row.id)
+    await ElMessageBox.confirm(
+      `确定要删除用户 "${row.username}" 吗?`,
+      "提示",
+      {
+        type: "warning",
+      }
+    );
+    const res = await deleteUser(row.id);
     if (res.code === 200) {
-      ElMessage.success('删除成功')
-      getList()
+      ElMessage.success("删除成功");
+      getList();
     }
   } catch (error) {
-    if (error !== 'cancel') {
-      console.error('删除失败', error)
+    if (error !== "cancel") {
+      console.error("删除失败", error);
     }
   }
 }
 
 async function handleSubmit() {
-  if (!formRef.value) return
+  if (!formRef.value) return;
 
   await formRef.value.validate(async (valid) => {
     if (valid) {
-      submitLoading.value = true
+      submitLoading.value = true;
       try {
-        let res
+        let res;
         if (form.id) {
-          const { password, ...updateData } = form
-          res = await updateUser(form.id, updateData)
+          const { password, ...updateData } = form;
+          res = await updateUser(form.id, updateData);
         } else {
-          res = await createUser(form)
+          res = await createUser(form);
         }
         if (res.code === 200) {
-          ElMessage.success(form.id ? '修改成功' : '新增成功')
-          dialogVisible.value = false
-          getList()
+          ElMessage.success(form.id ? "修改成功" : "新增成功");
+          dialogVisible.value = false;
+          getList();
         }
       } finally {
-        submitLoading.value = false
+        submitLoading.value = false;
       }
     }
-  })
+  });
 }
 
 async function handleResetSubmit() {
-  if (!resetFormRef.value) return
+  if (!resetFormRef.value) return;
 
   await resetFormRef.value.validate(async (valid) => {
     if (valid) {
-      resetLoading.value = true
+      resetLoading.value = true;
       try {
-        const res = await resetPassword(currentUserId.value, resetForm.password)
+        const res = await resetPassword(
+          currentUserId.value,
+          resetForm.password
+        );
         if (res.code === 200) {
-          ElMessage.success('密码重置成功')
-          resetDialogVisible.value = false
+          ElMessage.success("密码重置成功");
+          resetDialogVisible.value = false;
         }
       } finally {
-        resetLoading.value = false
+        resetLoading.value = false;
       }
     }
-  })
+  });
 }
 
 onMounted(() => {
-  getList()
-})
+  getList();
+});
 </script>
 
 <style lang="scss" scoped>