| | |
| | | <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-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="噪声监测仪" 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: 'fuxiaojie', |
| | | name: '付小姐在成都', |
| | | role: 'restaurant_admin', |
| | | status: 'active', |
| | | }, |
| | | { id: 2, username: 'jike', name: '吉刻联盟', role: 'restaurant_admin', status: 'active' }, |
| | | { id: 3, username: 'jiazaitala', name: '家在塔啦', role: 'restaurant_admin', status: 'active' }, |
| | | { id: 4, username: 'langlailiao', name: '狼来了', role: 'restaurant_admin', status: 'active' }, |
| | | { id: 5, username: 'lekaisai', name: '乐凯撒星游店', role: 'restaurant_admin', status: 'active' }, |
| | | { |
| | | id: 6, |
| | | username: 'xinyuan', |
| | | name: '馨远美食小镇(哈尼美食广场)', |
| | | role: 'restaurant_admin', |
| | | status: 'active', |
| | | }, |
| | | { id: 7, username: 'bangyuehan', name: '棒约翰', role: 'restaurant_admin', status: 'active' }, |
| | | { id: 8, username: 'nangtang', name: '弄堂咪道', role: 'restaurant_admin', status: 'active' }, |
| | | { |
| | | id: 9, |
| | | username: 'yangji', |
| | | name: '杨记齐齐哈尔烤肉', |
| | | role: 'restaurant_admin', |
| | | status: 'active', |
| | | }, |
| | | { |
| | | id: 10, |
| | | username: 'rensheng', |
| | | name: '上海稔传餐饮管理有限公司(人生一串)', |
| | | role: 'restaurant_admin', |
| | | status: 'active', |
| | | }, |
| | | { id: 11, username: 'yuanjia', name: '缘家', role: 'restaurant_admin', status: 'active' }, |
| | | { |
| | | id: 12, |
| | | username: 'quansheng', |
| | | name: '泉盛餐饮(上海)有限公司(食其家)', |
| | | role: 'restaurant_admin', |
| | | status: 'active', |
| | | }, |
| | | { id: 13, username: 'fengmao', name: '丰茂烤串', role: 'restaurant_admin', status: 'active' }, |
| | | { |
| | | id: 14, |
| | | username: 'taihuang', |
| | | name: '上海泰煌餐饮管理有限公司(泰煌鸡)', |
| | | role: 'restaurant_admin', |
| | | status: 'active', |
| | | }, |
| | | { |
| | | id: 15, |
| | | username: 'chenxi', |
| | | name: '徐汇区辰熙餐馆(小铁君串烧居酒屋)', |
| | | 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> |