flyzto 3 ماه پیش
والد
کامیت
f8865afc74

+ 46 - 0
apps/web-antd/src/api/user/user_role.ts

@@ -0,0 +1,46 @@
+import { requestClient } from "#/api/request";
+
+interface ApiResultData {
+  data: Object;
+  state: number;
+  message: string;
+}
+
+/**
+ * 获取角色列表
+ */
+export async function getRoleList() {
+  return requestClient.get<ApiResultData>('/user_role/list');
+}
+
+/**
+ * 获取角色详情
+ * @param id
+ */
+export async function getRoleInfo(id: string | number) {
+  return requestClient.get<ApiResultData>('/user_role/detail?id=' + id);
+}
+
+/**
+ * 更新角色信息
+ * @param data
+ */
+export async function updateRoleInfo(data: any) {
+  return requestClient.post<ApiResultData>('/user_role/update', data);
+}
+
+/**
+ * 创建角色
+ * @param data
+ */
+export async function createRole(data: any) {
+  return requestClient.post<ApiResultData>('/user_role/create', data);
+}
+
+/**
+ * 删除角色
+ * @param id
+ */
+export async function deleteRole(data: any) {
+  return requestClient.post<ApiResultData>('/user_role/delete', data);
+}

+ 1 - 1
apps/web-antd/src/locales/langs/en-US/user.json

@@ -1,5 +1,5 @@
 {
-  "title": "User Management",
+  "title": "Permission",
   "user_list": "User List",
   "search": {
     "user_name": "Username",

+ 2 - 1
apps/web-antd/src/locales/langs/zh-CN/user.json

@@ -1,6 +1,7 @@
 {
-  "title": "账号管理",
+  "title": "权限管理",
   "user_list": "账号列表",
+  "role_list": "角色列表",
   "search": {
     "user_name": "用户名",
     "nick_name": "昵称",

+ 11 - 2
apps/web-antd/src/router/routes/modules/user_list.ts

@@ -5,14 +5,23 @@ import { $t } from '#/locales';
 const routes: RouteRecordRaw[] = [
   {
     meta: {
-      icon: 'solar:user-outline',
+      icon: 'solar:verified-check-outline',
       keepAlive: true,
       order: 4,
       title: $t('user.title'),
     },
-    name: 'UserManagement',
+    name: 'Permission',
     path: '/user',
     children: [
+      {
+        meta: {
+          title: $t('user.role_list'),
+          icon: 'solar:shield-user-outline',
+        },
+        name: 'RoleList',
+        path: '/user/role-list',
+        component: () => import('#/views/user/role_list/index.vue'),
+      },
       {
         meta: {
           title: $t('user.user_list'),

+ 164 - 0
apps/web-antd/src/views/user/role_list/index.vue

@@ -0,0 +1,164 @@
+<script setup>
+import dayjs from 'dayjs';
+import { ref } from 'vue';
+import { Page, useVbenModal } from '@vben/common-ui';
+import { Button, Card, Tag, Space, Popconfirm, message } from "ant-design-vue";
+import { useVbenForm } from '#/adapter/form';
+import { $t } from "@vben/locales";
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getRoleList, deleteRole, getRoleInfo, updateRoleInfo, createRole } from "#/api/user/user_role";
+
+import RoleModal from './role_info.vue';
+
+const gridOptions = {
+  border: true,
+  stripe: true,
+  checkboxConfig: {
+    highlight: true,
+  },
+  columns: [
+    { fixed: 'left', title: '序号', type: 'seq', width: 60 },
+    { field: 'id', title: '角色ID', width: 80 },
+    { field: 'role_name', title: '角色名称', width: 150 },
+    { field: 'privileges', title: '权限', slots: { default: 'privileges' } },
+    { field: 'create_time', title: '创建时间', width: 180 },
+    { field: 'update_time', title: '更新时间', width: 180 },
+    { fixed: 'right', title: '操作', width: 120, slots: { default: 'action' } },
+  ],
+  exportConfig: {},
+  keepSource: true,
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }) => {
+        try {
+          const res = await getRoleList();
+          return {
+            total: res.total,
+            items: res.list
+          }
+        } catch (error) {
+          console.error('获取角色列表失败:', error);
+          return {
+            total: 0,
+            items: []
+          };
+        }
+      },
+    },
+  },
+  rowConfig: {
+    isHover: true,
+  },
+  pagerConfig: {
+    enabled: false,
+  },
+  toolbarConfig: {
+    custom: true,
+    export: true,
+    refresh: true,
+    zoom: true,
+  },
+};
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  gridOptions,
+});
+
+const [Modal, modalApi] = useVbenModal({
+  connectedComponent: RoleModal,
+  onBeforeClose() {
+    const { formData } = modalApi.getData();
+    if (!formData) {
+      return true;
+    }
+    if (formData.id) {
+      updateRoleInfo(formData)
+      .then(res => {
+        message.success('编辑角色成功');
+        gridApi.reload();
+      })
+      .catch(err => {
+        message.error('编辑角色失败');
+        console.error('编辑角色失败:', err);
+      });
+    }
+    else {
+      createRole(formData)
+      .then(res => {
+        message.success('新增角色成功');
+        gridApi.reload();
+      })
+      .catch(err => {
+        message.error('新增角色失败');
+        console.error('新增角色失败:', err);
+      });
+    }
+  },
+});
+
+// 新增角色
+const addRole = () => {
+  modalApi.setState({ title: '新增角色' })
+    .open();
+};
+
+// 编辑角色
+const editRole = (row) => {
+  modalApi.setData({ formData: row })
+    .setState({ title: '编辑角色' })
+    .open();
+};
+
+// 处理删除角色
+const handleDelete = async (row) => {
+  try {
+    await deleteRole({ id: row.id });
+    message.success('删除成功');
+    gridApi.reload();
+  } catch (error) {
+    message.error('删除失败');
+  }
+};
+
+</script>
+
+<template>
+  <Page>
+    <Card>
+      <div class="vp-raw w-full">
+        <Grid>
+          <template #toolbar-actions>
+            <Button type="primary" @click="addRole">新增角色</Button>
+          </template>
+          <template #privileges="{ row }">
+            <div class="flex flex-wrap gap-1">
+              <Tag v-for="privilege in row.privileges" :key="privilege" color="blue" size="small">
+                {{ privilege }}
+              </Tag>
+            </div>
+          </template>
+          <template #action="{ row }">
+            <Space>
+              <Button type="link" size="small" @click="editRole(row)">编辑</Button>
+              <Popconfirm
+                title="确定要删除这个角色吗?"
+                @confirm="handleDelete(row)"
+                ok-text="确定"
+                cancel-text="取消"
+              >
+                <Button type="link" size="small" danger>删除</Button>
+              </Popconfirm>
+            </Space>
+          </template>
+        </Grid>
+      </div>
+    </Card>
+    <Modal />
+  </Page>
+</template>
+
+<style scoped>
+.ant-card {
+  margin-bottom: 16px;
+}
+</style>

+ 135 - 0
apps/web-antd/src/views/user/role_list/role_info.vue

@@ -0,0 +1,135 @@
+<script setup>
+import { useVbenModal } from '@vben/common-ui';
+import { Form, Input, Select, Button, message } from "ant-design-vue";
+import { ref, reactive, computed, watch } from 'vue';
+
+const formProps = ['id', 'role_name', 'privileges'];
+
+const formRef = ref();
+
+const addState = reactive({
+  role_name: '',
+  privileges: [],
+});
+
+const editState = reactive({});
+const editCache = {};
+
+// 权限选项从mockData导入
+
+const rulesAdd = {
+  role_name: [
+    { required: true, message: '请输入角色名称', trigger: 'blur' },
+    { min: 2, max: 20, message: '角色名称长度为2-20位', trigger: 'blur' }
+  ],
+  privileges: [
+    { required: true, message: '请选择权限', trigger: 'change' }
+  ],
+};
+
+const rulesEdit = {
+  role_name: [
+    { min: 2, max: 20, message: '角色名称长度为2-20位', trigger: 'blur' }
+  ],
+  privileges: [
+    { required: true, message: '请选择权限', trigger: 'change' }
+  ],
+};
+
+const rules = computed(() => {
+  return editState.id ? rulesEdit : rulesAdd;
+});
+
+const formState = computed(() => {
+  return isEdit.value ? editState : addState;
+});
+
+const isEdit = computed(() => {
+  return !!editState.id;
+});
+
+const resetForm = () => {
+  formRef.value.resetFields();
+  Object.keys(editCache).forEach(key => {
+    delete editCache[key];
+  });
+  delete editState.id;
+};
+
+const [Modal, modalApi] = useVbenModal({
+  title: 'Role Info',
+  onOpened() {
+    const { formData } = modalApi.getData();
+    if (formData) {
+      Object.keys(formData).forEach(key => {
+        if (formProps.includes(key)) {
+          editState[key] = formData[key];
+          editCache[key] = formData[key];
+        }
+      });
+    }
+    modalApi.setData({ formData: null });
+  },
+  onCancel() {
+    modalApi.setData({ formData: null });
+    modalApi.close();
+  },
+  onConfirm() {
+    formRef.value.validate().then(() => {
+      if (isEdit.value) {
+        let needEdit = false;
+        const editData = {};
+        Object.keys(editState).forEach(key => {
+          if (editState[key] !== editCache[key]) {
+            editData[key] = editState[key];
+            needEdit = true;
+          }
+        });
+        if (needEdit) {
+          editData.id = editState.id;
+          modalApi.setData({ formData: { ...editData } });
+        }
+      }
+      else {
+        modalApi.setData({ formData: { ...addState } });
+      }
+      modalApi.close();
+    })
+    .catch(err => {
+      console.log('请检查输入内容', err);
+    });
+  },
+  onClosed() {
+    resetForm();
+  }
+});
+
+</script>
+
+<template>
+  <Modal>
+    <Form style="padding: 0.75rem" ref="formRef" :model="formState" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }" :rules="rules">
+      <Form.Item label="角色名称" name="role_name">
+        <Input v-model:value="formState.role_name" placeholder="请输入角色名称" />
+      </Form.Item>
+      <Form.Item label="权限" name="privileges">
+        <Select
+          v-model:value="formState.privileges"
+          mode="multiple"
+          placeholder="请选择权限"
+          style="width: 100%">
+          <Select.Option value="user_list">用户列表</Select.Option>
+          <Select.Option value="user_create">用户添加</Select.Option>
+          <Select.Option value="user_delete">用户删除</Select.Option>
+          <Select.Option value="user_edit">用户编辑</Select.Option>
+        </Select>
+      </Form.Item>
+    </Form>
+  </Modal>
+</template>
+
+<style scoped>
+:deep(.ant-form-horizontal) {
+  padding: 0.75rem !important;
+}
+</style>

+ 32 - 7
apps/web-antd/src/views/user/user_list/index.vue

@@ -1,12 +1,14 @@
 <script setup>
+import dayjs from 'dayjs';
+import { ref } from 'vue';
 import { Page, useVbenModal } from '@vben/common-ui';
 import { Button, Card, Tag, Avatar, Switch, Space, Popconfirm, message } from "ant-design-vue";
 import { useVbenForm } from '#/adapter/form';
 import { $t } from "@vben/locales";
 import { useVbenVxeGrid } from '#/adapter/vxe-table';
+
+import { getRoleList } from "#/api/user/user_role";
 import { getAccountList, deleteAccount, getAccountInfo, updateAccountInfo, createAccount } from "#/api/user/user_list";
-import { ref } from 'vue';
-import dayjs from 'dayjs';
 
 import ExtraModal from './user_info.vue';
 
@@ -206,17 +208,40 @@ const [Modal, modalApi] = useVbenModal({
   },
 });
 
+const roleListUpdate = async () => {
+  return getRoleList()
+  .then(res => {
+    return res.list.map(item => {
+      const { id, role_name } = item;
+      return {
+        label: role_name,
+        value: id,
+      }
+    });
+  })
+}
+
+// 打开弹窗
+const openModal = (state=null, data=null) => {
+  roleListUpdate()
+  .then(res => {
+    modalApi.setData({ formData: data, roleList: res })
+      .setState({ title: state })
+      .open();
+  })
+  .catch(err => {
+    console.error('获取角色列表失败:', err);
+  });
+}
+
 // 新增账号
 const addAccount = () => {
-  modalApi.setState({ title: '新增账号' })
-    .open();
+  openModal('新增账号');
 };
 
 // 编辑账号
 const editAccount = (row) => {
-  modalApi.setData({ formData: row })
-    .setState({ title: '编辑账号' })
-    .open();
+  openModal('编辑账号', row);
 };
 
 // 处理删除账号

+ 12 - 5
apps/web-antd/src/views/user/user_list/user_info.vue

@@ -6,6 +6,7 @@ import { ref, reactive, computed, watch } from 'vue';
 const formProps = ['user_id', 'user_name', 'nick_name', 'password', 'confirm_password', 'phone', 'user_role', 'white_list_ip'];
 
 const formRef = ref();
+const selectRoleList = ref([]);
 
 const addState = reactive({
   white_list_ip: '',
@@ -71,7 +72,7 @@ const resetForm = () => {
 const [Modal, modalApi] = useVbenModal({
   title: 'Account Info',
   onOpened() {
-    const { formData } = modalApi.getData();
+    const { formData, roleList } = modalApi.getData();
     if (formData) {
       Object.keys(formData).forEach(key => {
         if (formProps.includes(key)) {
@@ -80,6 +81,7 @@ const [Modal, modalApi] = useVbenModal({
         }
       });
     }
+    selectRoleList.value = roleList;
     modalApi.setData({ formData: null });
   },
   onCancel() {
@@ -121,7 +123,7 @@ const [Modal, modalApi] = useVbenModal({
 </script>
 <template>
   <Modal>
-    <Form ref="formRef" :model="formState" :label-col="{ span: 6 }" :wrapper-col="{ span: 12 }" :rules="rules">
+    <Form style="padding: 0.75rem" ref="formRef" :model="formState" :label-col="{ span: 6 }" :wrapper-col="{ span: 12 }" :rules="rules">
       <Form.Item label="用户名" name="user_name">
         <Input v-model:value="formState.user_name" :disabled="isEdit" />
       </Form.Item>
@@ -139,8 +141,7 @@ const [Modal, modalApi] = useVbenModal({
       </Form.Item>
       <Form.Item label="角色" name="user_role">
         <Select v-model:value="formState.user_role">
-          <Select.Option :value="1">超级管理员</Select.Option>
-          <Select.Option :value="100">业务管理员</Select.Option>
+          <Select.Option v-for="item in selectRoleList" :key="item.value" :value="item.value">{{ item.label }}</Select.Option>
         </Select>
       </Form.Item>
       <Form.Item label="IP白名单" name="white_list_ip">
@@ -148,4 +149,10 @@ const [Modal, modalApi] = useVbenModal({
       </Form.Item>
     </Form>
   </Modal>
-</template>
+</template>
+
+<style scoped>
+:deep(.ant-form-item:last-child) {
+  margin-bottom: 0;
+}
+</style>