From d7c6c6429a00a480a5b163e0afe7aae217d8a1f6 Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期二, 10 三月 2026 17:30:36 +0800
Subject: [PATCH] 2026.3.10

---
 src/views/system/SystemManage.vue |  627 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 625 insertions(+), 2 deletions(-)

diff --git a/src/views/system/SystemManage.vue b/src/views/system/SystemManage.vue
index 0f7f348..c429e89 100644
--- a/src/views/system/SystemManage.vue
+++ b/src/views/system/SystemManage.vue
@@ -1,2 +1,625 @@
-<template>绯荤粺绠$悊</template>
-<script setup lang="ts"></script>
+<template>
+  <div class="system-manage">
+    <el-card class="system-card">
+      <template #header>
+        <div class="card-header">
+          <span>绯荤粺绠$悊</span>
+        </div>
+      </template>
+
+      <!-- 瀵艰埅鏍囩椤� -->
+      <el-tabs v-model="activeTab" class="system-tabs">
+        <!-- 鐢ㄦ埛绠$悊 -->
+        <el-tab-pane label="鐢ㄦ埛绠$悊" name="users">
+          <div class="tab-content">
+            <!-- 鎼滅储鍜屾坊鍔犳寜閽� -->
+            <div class="search-add-bar">
+              <el-input
+                v-model="userSearchQuery"
+                placeholder="鎼滅储鐢ㄦ埛"
+                style="width: 200px"
+                prefix-icon="el-icon-search"
+              />
+              <el-button type="primary" @click="openUserDialog">
+                <el-icon><Plus /></el-icon> 娣诲姞鐢ㄦ埛
+              </el-button>
+            </div>
+
+            <!-- 鐢ㄦ埛琛ㄦ牸 -->
+            <el-table :data="filteredUsers" style="width: 100%">
+              <el-table-column prop="id" label="ID" width="80" />
+              <el-table-column prop="username" label="鐢ㄦ埛鍚�" />
+              <el-table-column prop="name" label="濮撳悕" />
+              <el-table-column prop="role" label="瑙掕壊" />
+              <el-table-column prop="status" label="鐘舵��">
+                <template #default="scope">
+                  <el-tag :type="scope.row.status === 'active' ? 'success' : 'danger'">
+                    {{ scope.row.status === 'active' ? '娲昏穬' : '绂佺敤' }}
+                  </el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column label="鎿嶄綔" width="200">
+                <template #default="scope">
+                  <el-button size="small" @click="editUser(scope.row)"> 缂栬緫 </el-button>
+                  <el-button size="small" type="danger" @click="deleteUser(scope.row.id)">
+                    鍒犻櫎
+                  </el-button>
+                  <el-button size="small" @click="setPermissions(scope.row)"> 鏉冮檺 </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+
+            <!-- 鍒嗛〉 -->
+            <el-pagination
+              v-model:current-page="userCurrentPage"
+              v-model:page-size="userPageSize"
+              :page-sizes="[10, 20, 50]"
+              layout="total, sizes, prev, pager, next, jumper"
+              :total="users.length"
+              style="margin-top: 20px"
+            />
+          </div>
+        </el-tab-pane>
+
+        <!-- 椁愰ギ搴楅摵绠$悊 -->
+        <el-tab-pane label="椁愰ギ搴楅摵绠$悊" name="restaurants">
+          <div class="tab-content">
+            <!-- 鎼滅储鍜屾坊鍔犳寜閽� -->
+            <div class="search-add-bar">
+              <el-input
+                v-model="restaurantSearchQuery"
+                placeholder="鎼滅储搴楅摵"
+                style="width: 200px"
+                prefix-icon="el-icon-search"
+              />
+              <el-button type="primary" @click="openRestaurantDialog">
+                <el-icon><Plus /></el-icon> 娣诲姞搴楅摵
+              </el-button>
+            </div>
+
+            <!-- 搴楅摵琛ㄦ牸 -->
+            <el-table :data="filteredRestaurants" style="width: 100%">
+              <el-table-column prop="id" label="ID" width="80" />
+              <el-table-column prop="name" label="搴楅摵鍚嶇О" />
+              <el-table-column prop="address" label="鍦板潃" />
+              <el-table-column prop="contact" label="鑱旂郴浜�" />
+              <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" />
+              <el-table-column label="鎿嶄綔" width="250">
+                <template #default="scope">
+                  <el-button size="small" @click="editRestaurant(scope.row)"> 缂栬緫 </el-button>
+                  <el-button size="small" type="danger" @click="deleteRestaurant(scope.row.id)">
+                    鍒犻櫎
+                  </el-button>
+                  <el-button size="small" @click="manageDevices(scope.row)"> 璁惧绠$悊 </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+
+            <!-- 鍒嗛〉 -->
+            <el-pagination
+              v-model:current-page="restaurantCurrentPage"
+              v-model:page-size="restaurantPageSize"
+              :page-sizes="[10, 20, 50]"
+              layout="total, sizes, prev, pager, next, jumper"
+              :total="restaurants.length"
+              style="margin-top: 20px"
+            />
+          </div>
+        </el-tab-pane>
+
+        <!-- 璁惧绠$悊 -->
+        <el-tab-pane label="璁惧绠$悊" name="devices">
+          <div class="tab-content" v-if="selectedRestaurant">
+            <h3>{{ selectedRestaurant.name }} - 璁惧鍒楄〃</h3>
+
+            <!-- 鎼滅储鍜屾坊鍔犳寜閽� -->
+            <div class="search-add-bar">
+              <el-input
+                v-model="deviceSearchQuery"
+                placeholder="鎼滅储璁惧"
+                style="width: 200px"
+                prefix-icon="el-icon-search"
+              />
+              <el-button type="primary" @click="openDeviceDialog">
+                <el-icon><Plus /></el-icon> 娣诲姞璁惧
+              </el-button>
+            </div>
+
+            <!-- 璁惧琛ㄦ牸 -->
+            <el-table :data="filteredDevices" style="width: 100%">
+              <el-table-column prop="id" label="ID" width="80" />
+              <el-table-column prop="deviceId" label="璁惧缂栧彿" />
+              <el-table-column prop="type" label="璁惧绫诲瀷" />
+              <el-table-column prop="status" label="鐘舵��">
+                <template #default="scope">
+                  <el-tag :type="scope.row.status === 'online' ? 'success' : 'danger'">
+                    {{ scope.row.status === 'online' ? '鍦ㄧ嚎' : '绂荤嚎' }}
+                  </el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column prop="installDate" label="瀹夎鏃ユ湡" />
+              <el-table-column label="鎿嶄綔" width="200">
+                <template #default="scope">
+                  <el-button size="small" @click="editDevice(scope.row)"> 缂栬緫 </el-button>
+                  <el-button size="small" type="danger" @click="deleteDevice(scope.row.id)">
+                    鍒犻櫎
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+
+            <!-- 鍒嗛〉 -->
+            <el-pagination
+              v-model:current-page="deviceCurrentPage"
+              v-model:page-size="devicePageSize"
+              :page-sizes="[10, 20, 50]"
+              layout="total, sizes, prev, pager, next, jumper"
+              :total="devices.length"
+              style="margin-top: 20px"
+            />
+          </div>
+          <div class="tab-content" v-else>
+            <el-empty description="璇峰厛閫夋嫨涓�涓楗簵閾�" />
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+    </el-card>
+
+    <!-- 鐢ㄦ埛瀵硅瘽妗� -->
+    <el-dialog
+      v-model="userDialogVisible"
+      :title="isEditUser ? '缂栬緫鐢ㄦ埛' : '娣诲姞鐢ㄦ埛'"
+      width="500px"
+    >
+      <el-form :model="userForm" label-width="80px">
+        <el-form-item label="鐢ㄦ埛鍚�">
+          <el-input v-model="userForm.username" />
+        </el-form-item>
+        <el-form-item label="濮撳悕">
+          <el-input v-model="userForm.name" />
+        </el-form-item>
+        <el-form-item label="瀵嗙爜" v-if="!isEditUser">
+          <el-input type="password" v-model="userForm.password" />
+        </el-form-item>
+        <el-form-item label="瑙掕壊">
+          <el-select v-model="userForm.role">
+            <el-option label="绠$悊鍛�" value="admin" />
+            <el-option label="鏅�氱敤鎴�" value="user" />
+            <el-option label="搴楅摵绠$悊鍛�" value="restaurant_admin" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鐘舵��">
+          <el-switch v-model="userForm.status" active-value="active" inactive-value="inactive" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="userDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="saveUser">淇濆瓨</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 鏉冮檺璁剧疆瀵硅瘽妗� -->
+    <el-dialog v-model="permissionDialogVisible" title="鏉冮檺璁剧疆" width="500px">
+      <el-form label-width="80px">
+        <el-form-item label="鐢ㄦ埛">
+          <el-input :value="selectedUser?.name" disabled />
+        </el-form-item>
+        <el-form-item label="鏉冮檺">
+          <el-checkbox-group v-model="userPermissions">
+            <el-checkbox label="user_management">鐢ㄦ埛绠$悊</el-checkbox>
+            <el-checkbox label="restaurant_management">搴楅摵绠$悊</el-checkbox>
+            <el-checkbox label="device_management">璁惧绠$悊</el-checkbox>
+            <el-checkbox label="data_analysis">鏁版嵁鍒嗘瀽</el-checkbox>
+            <el-checkbox label="system_settings">绯荤粺璁剧疆</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="permissionDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="savePermissions">淇濆瓨</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 搴楅摵瀵硅瘽妗� -->
+    <el-dialog
+      v-model="restaurantDialogVisible"
+      :title="isEditRestaurant ? '缂栬緫搴楅摵' : '娣诲姞搴楅摵'"
+      width="500px"
+    >
+      <el-form :model="restaurantForm" label-width="80px">
+        <el-form-item label="搴楅摵鍚嶇О">
+          <el-input v-model="restaurantForm.name" />
+        </el-form-item>
+        <el-form-item label="鍦板潃">
+          <el-input v-model="restaurantForm.address" />
+        </el-form-item>
+        <el-form-item label="鑱旂郴浜�">
+          <el-input v-model="restaurantForm.contact" />
+        </el-form-item>
+        <el-form-item label="鑱旂郴鐢佃瘽">
+          <el-input v-model="restaurantForm.phone" />
+        </el-form-item>
+        <el-form-item label="钀ヤ笟鎵х収">
+          <el-input v-model="restaurantForm.license" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="restaurantDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="saveRestaurant">淇濆瓨</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 璁惧瀵硅瘽妗� -->
+    <el-dialog
+      v-model="deviceDialogVisible"
+      :title="isEditDevice ? '缂栬緫璁惧' : '娣诲姞璁惧'"
+      width="500px"
+    >
+      <el-form :model="deviceForm" label-width="80px">
+        <el-form-item label="璁惧缂栧彿">
+          <el-input v-model="deviceForm.deviceId" />
+        </el-form-item>
+        <el-form-item label="璁惧绫诲瀷">
+          <el-select v-model="deviceForm.type">
+            <el-option label="娌圭儫鐩戞祴浠�" value="fume_monitor" />
+            <el-option label="棰楃矑鐗╃洃娴嬩华" value="particle_monitor" />
+            <el-option label="鍣0鐩戞祴浠�" value="noise_monitor" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="瀹夎浣嶇疆">
+          <el-input v-model="deviceForm.location" />
+        </el-form-item>
+        <el-form-item label="瀹夎鏃ユ湡">
+          <el-date-picker v-model="deviceForm.installDate" type="date" />
+        </el-form-item>
+        <el-form-item label="鐘舵��">
+          <el-select v-model="deviceForm.status">
+            <el-option label="鍦ㄧ嚎" value="online" />
+            <el-option label="绂荤嚎" value="offline" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="deviceDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="saveDevice">淇濆瓨</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed } from 'vue'
+import { Plus } from '@element-plus/icons-vue'
+
+// 鏍囩椤电姸鎬�
+const activeTab = ref('users')
+
+// 鐢ㄦ埛绠$悊鐩稿叧
+const users = ref([
+  { id: 1, username: 'admin', name: '绠$悊鍛�', role: 'admin', status: 'active' },
+  { id: 2, username: 'user1', name: '鐢ㄦ埛1', role: 'user', status: 'active' },
+  {
+    id: 3,
+    username: 'restaurant1',
+    name: '搴楅摵绠$悊鍛�1',
+    role: 'restaurant_admin',
+    status: 'active',
+  },
+])
+const userSearchQuery = ref('')
+const userCurrentPage = ref(1)
+const userPageSize = ref(10)
+const userDialogVisible = ref(false)
+const isEditUser = ref(false)
+const userForm = ref({
+  id: 0,
+  username: '',
+  name: '',
+  password: '',
+  role: 'user',
+  status: 'active',
+})
+
+// 鏉冮檺绠$悊鐩稿叧
+const permissionDialogVisible = ref(false)
+const selectedUser = ref<any>(null)
+const userPermissions = ref<string[]>([])
+
+// 椁愰ギ搴楅摵绠$悊鐩稿叧
+const restaurants = ref([
+  {
+    id: 1,
+    name: '娴嬭瘯椁愬巺1',
+    address: '鍖椾含甯傛湞闃冲尯',
+    contact: '寮犱笁',
+    phone: '13800138001',
+    license: '123456789',
+  },
+  {
+    id: 2,
+    name: '娴嬭瘯椁愬巺2',
+    address: '鍖椾含甯傛捣娣�鍖�',
+    contact: '鏉庡洓',
+    phone: '13800138002',
+    license: '987654321',
+  },
+])
+const restaurantSearchQuery = ref('')
+const restaurantCurrentPage = ref(1)
+const restaurantPageSize = ref(10)
+const restaurantDialogVisible = ref(false)
+const isEditRestaurant = ref(false)
+const restaurantForm = ref({
+  id: 0,
+  name: '',
+  address: '',
+  contact: '',
+  phone: '',
+  license: '',
+})
+
+// 璁惧绠$悊鐩稿叧
+const devices = ref([
+  {
+    id: 1,
+    restaurantId: 1,
+    deviceId: 'FUM-001',
+    type: 'fume_monitor',
+    location: '鍘ㄦ埧',
+    status: 'online',
+    installDate: '2024-01-01',
+  },
+  {
+    id: 2,
+    restaurantId: 1,
+    deviceId: 'FUM-002',
+    type: 'particle_monitor',
+    location: '鎺掔儫鍙�',
+    status: 'online',
+    installDate: '2024-01-02',
+  },
+  {
+    id: 3,
+    restaurantId: 2,
+    deviceId: 'FUM-003',
+    type: 'fume_monitor',
+    location: '鍘ㄦ埧',
+    status: 'offline',
+    installDate: '2024-01-03',
+  },
+])
+const deviceSearchQuery = ref('')
+const deviceCurrentPage = ref(1)
+const devicePageSize = ref(10)
+const deviceDialogVisible = ref(false)
+const isEditDevice = ref(false)
+const deviceForm = ref({
+  id: 0,
+  restaurantId: 0,
+  deviceId: '',
+  type: 'fume_monitor',
+  location: '',
+  status: 'online',
+  installDate: '',
+})
+const selectedRestaurant = ref<any>(null)
+
+// 璁$畻灞炴��
+const filteredUsers = computed(() => {
+  if (!userSearchQuery.value) return users.value
+  return users.value.filter(
+    (user) =>
+      user.username.includes(userSearchQuery.value) || user.name.includes(userSearchQuery.value),
+  )
+})
+
+const filteredRestaurants = computed(() => {
+  if (!restaurantSearchQuery.value) return restaurants.value
+  return restaurants.value.filter(
+    (restaurant) =>
+      restaurant.name.includes(restaurantSearchQuery.value) ||
+      restaurant.address.includes(restaurantSearchQuery.value),
+  )
+})
+
+const filteredDevices = computed(() => {
+  if (!selectedRestaurant.value) return []
+  let result = devices.value.filter((device) => device.restaurantId === selectedRestaurant.value.id)
+  if (deviceSearchQuery.value) {
+    result = result.filter(
+      (device) =>
+        device.deviceId.includes(deviceSearchQuery.value) ||
+        device.type.includes(deviceSearchQuery.value),
+    )
+  }
+  return result
+})
+
+// 鏂规硶
+// 鐢ㄦ埛绠$悊鏂规硶
+const openUserDialog = () => {
+  isEditUser.value = false
+  userForm.value = {
+    id: 0,
+    username: '',
+    name: '',
+    password: '',
+    role: 'user',
+    status: 'active',
+  }
+  userDialogVisible.value = true
+}
+
+const editUser = (user: any) => {
+  isEditUser.value = true
+  userForm.value = { ...user }
+  userDialogVisible.value = true
+}
+
+const saveUser = () => {
+  if (isEditUser.value) {
+    const index = users.value.findIndex((u) => u.id === userForm.value.id)
+    if (index !== -1) {
+      users.value[index] = { ...userForm.value }
+    }
+  } else {
+    const newUser = {
+      ...userForm.value,
+      id: users.value.length + 1,
+    }
+    users.value.push(newUser)
+  }
+  userDialogVisible.value = false
+}
+
+const deleteUser = (id: number) => {
+  users.value = users.value.filter((user) => user.id !== id)
+}
+
+const setPermissions = (user: any) => {
+  selectedUser.value = user
+  // 妯℃嫙鏉冮檺鏁版嵁
+  userPermissions.value = ['user_management', 'restaurant_management']
+  permissionDialogVisible.value = true
+}
+
+const savePermissions = () => {
+  // 淇濆瓨鏉冮檺閫昏緫
+  permissionDialogVisible.value = false
+}
+
+// 搴楅摵绠$悊鏂规硶
+const openRestaurantDialog = () => {
+  isEditRestaurant.value = false
+  restaurantForm.value = {
+    id: 0,
+    name: '',
+    address: '',
+    contact: '',
+    phone: '',
+    license: '',
+  }
+  restaurantDialogVisible.value = true
+}
+
+const editRestaurant = (restaurant: any) => {
+  isEditRestaurant.value = true
+  restaurantForm.value = { ...restaurant }
+  restaurantDialogVisible.value = true
+}
+
+const saveRestaurant = () => {
+  if (isEditRestaurant.value) {
+    const index = restaurants.value.findIndex((r) => r.id === restaurantForm.value.id)
+    if (index !== -1) {
+      restaurants.value[index] = { ...restaurantForm.value }
+    }
+  } else {
+    const newRestaurant = {
+      ...restaurantForm.value,
+      id: restaurants.value.length + 1,
+    }
+    restaurants.value.push(newRestaurant)
+  }
+  restaurantDialogVisible.value = false
+}
+
+const deleteRestaurant = (id: number) => {
+  restaurants.value = restaurants.value.filter((restaurant) => restaurant.id !== id)
+  // 鍚屾椂鍒犻櫎鍏宠仈鐨勮澶�
+  devices.value = devices.value.filter((device) => device.restaurantId !== id)
+  if (selectedRestaurant.value && selectedRestaurant.value.id === id) {
+    selectedRestaurant.value = null
+  }
+}
+
+const manageDevices = (restaurant: any) => {
+  selectedRestaurant.value = restaurant
+  activeTab.value = 'devices'
+}
+
+// 璁惧绠$悊鏂规硶
+const openDeviceDialog = () => {
+  isEditDevice.value = false
+  deviceForm.value = {
+    id: 0,
+    restaurantId: selectedRestaurant.value.id,
+    deviceId: '',
+    type: 'fume_monitor',
+    location: '',
+    status: 'online',
+    installDate: '',
+  }
+  deviceDialogVisible.value = true
+}
+
+const editDevice = (device: any) => {
+  isEditDevice.value = true
+  deviceForm.value = { ...device }
+  deviceDialogVisible.value = true
+}
+
+const saveDevice = () => {
+  if (isEditDevice.value) {
+    const index = devices.value.findIndex((d) => d.id === deviceForm.value.id)
+    if (index !== -1) {
+      devices.value[index] = { ...deviceForm.value }
+    }
+  } else {
+    const newDevice = {
+      ...deviceForm.value,
+      id: devices.value.length + 1,
+    }
+    devices.value.push(newDevice)
+  }
+  deviceDialogVisible.value = false
+}
+
+const deleteDevice = (id: number) => {
+  devices.value = devices.value.filter((device) => device.id !== id)
+}
+</script>
+
+<style scoped>
+.system-manage {
+  padding: 20px;
+}
+
+.system-card {
+  margin-bottom: 20px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.system-tabs {
+  margin-top: 20px;
+}
+
+.tab-content {
+  padding: 20px 0;
+}
+
+.search-add-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+}
+</style>

--
Gitblit v1.9.3