Explorar o código

Merge branch 'master' of http://192.168.100.32:3000/fanfan/xt-front

fanfan %!s(int64=2) %!d(string=hai) anos
pai
achega
3ac1bd2781

+ 17 - 0
src/api/sys/sysTenantPackageApi.ts

@@ -7,6 +7,8 @@ enum Api {
   sysTenantPackageAdd = '/sys/tenantPackage/add',
   sysTenantPackageEdit = '/sys/tenantPackage/edit',
   sysTenantPackageRemove = '/sys/tenantPackage/removeByIds',
+  sysPackageAssign = '/sys/tenantPackage/assign',
+  sysPackageQueryMenuTree = '/sys/tenantPackage/menu/list',
 }
 
 /**
@@ -98,3 +100,18 @@ export const sysTenantPackageEdit = (params?: object) => {
 export const sysTenantPackageRemove = (params: Array<string | number>) => {
   return defHttp.post({ url: Api.sysTenantPackageRemove, params: params });
 };
+/**
+ * @description: 分配租户套餐,权限 - sys:tenantPackage:assign
+ * @method: POST
+ */
+export const sysPackageAssign = (params?: object) => {
+  return defHttp.post({ url: Api.sysPackageAssign, params: params });
+};
+
+/**
+ * @description: 查询角色信息列表,权限 - sys:role:query
+ * @method: POST
+ */
+export const sysPackageQueryMenuTree = id => {
+  return defHttp.get({ url: Api.sysPackageQueryMenuTree + '/' + id });
+};

+ 5 - 0
src/api/sys/sysUserApi.ts

@@ -11,6 +11,7 @@ enum Api {
   userLoginInfo = '/user/loginInfo',
   userListMenus = '/user/listMenus',
   userPermCode = '/user/getPermissionInfo',
+  validatorUsername = '/sys/user/username/valid',
 }
 
 /**
@@ -86,3 +87,7 @@ export const userListMenus = (params?: object) => {
 export const userPermCode = (params?: object) => {
   return defHttp.get({ url: Api.userPermCode, params: params });
 };
+
+export const validatorUsername = (params?: object) => {
+  return defHttp.post({ url: Api.validatorUsername, params: params });
+};

+ 19 - 10
src/utils/index.ts

@@ -5,7 +5,7 @@ import { unref } from 'vue';
 import { isObject } from '/@/utils/is';
 import { cloneDeep } from 'lodash-es';
 
-export const noop = () => { };
+export const noop = () => {};
 
 /**
  * @description:  Set ui mount node
@@ -74,10 +74,10 @@ export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormal
     ...opt,
     matched: (matched
       ? matched.map((item: any) => ({
-        meta: item.meta,
-        name: item.name,
-        path: item.path,
-      }))
+          meta: item.meta,
+          name: item.name,
+          path: item.path,
+        }))
       : undefined) as RouteRecordNormalized[],
   };
 }
@@ -123,26 +123,35 @@ export function getAllParentKeys(treeData: Array<any>, key: string) {
 
 // 获取字典值
 export function formatDictValue(options: Array<any>, value: string) {
-  if (!value || !options) {
+  if (!value) {
+    if (value != '0') {
+      return '';
+    }
+  }
+  if (!options) {
     return '';
   }
   let matchItem = '';
-
   if (typeof value == 'boolean') {
     matchItem = value ? '是' : '否';
     return matchItem;
   }
-  matchItem = options.find(item => item['value'] === value);
+  matchItem = options.find(item => (item['value'] as string) == value);
   return matchItem && matchItem['label'] ? matchItem['label'] : '';
 }
 
 // 获取字典颜色
 export function formatDictColor(options: Array<any>, value: string) {
-  if (!value || !options) {
+  if (!value) {
+    if (value != '0') {
+      return '';
+    }
+  }
+  if (!options) {
     return '';
   }
   let matchItem = '';
-  matchItem = options.find(item => item['value'] === value);
+  matchItem = options.find(item => item['value'] == value);
   return matchItem && matchItem['color'] ? matchItem['color'] : '';
 }
 

+ 104 - 0
src/views/sys/sysTenant/package/FormModalAssignMenu.vue

@@ -0,0 +1,104 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    title="分配菜单"
+    @ok="handleSubmit"
+  >
+    <BasicTree
+      :checkedKeys="checkedKeys"
+      :treeData="treeData"
+      checkable
+      @check="handleCheck"
+      :defaultExpandAll="true"
+      :multiple="true"
+      :fieldNames="{ key: 'id', title: 'name' }"
+    />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { BasicTree } from '/@/components/Tree';
+  import { sysPackageAssign, sysPackageQueryMenuTree } from '/@/api/sys/sysTenantPackageApi';
+
+  import { sysMenuQueryTree } from '/@/api/sys/sysMenuApi';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const treeData = ref([]) as any;
+  const packageId = ref();
+  const checkedKeys = ref([]);
+  const halfCheckedKeys = ref([]);
+  const parentIds = ref([]) as any;
+  onMounted(() => {
+    getTreeData();
+  });
+
+  const { createMessage, createConfirm } = useMessage();
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    setModalProps({ confirmLoading: false });
+    packageId.value = data.record.id;
+    // 获取已选中列表
+    const roleMenuList = (await sysPackageQueryMenuTree(packageId.value)) || [];
+    debugger;
+    // 找出菜单的所有父节点ID
+    treeData.value.map(item => {
+      if (item.children.length) {
+        item.children.map(ele => {
+          if (ele.children.length) {
+            parentIds.value.push(ele.id);
+          }
+        });
+      }
+      parentIds.value.push(item.id);
+    });
+    // 因为antd树插件勾选父节点会导致所有子节点选中,所以过滤所有父节点
+    halfCheckedKeys.value = Array.from(parentIds.value);
+    checkedKeys.value = roleMenuList
+      .filter(
+        item =>
+          (!parentIds.value.includes(item.id) && item.parentId != '0') || item.menuType == 'menu',
+      )
+      .map(item => item.id);
+  });
+
+  // 树节点选中事件
+  function handleCheck(keys, e) {
+    halfCheckedKeys.value = e.halfCheckedKeys;
+    checkedKeys.value = keys;
+  }
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    setModalProps({ confirmLoading: true });
+    try {
+      createConfirm({
+        iconType: 'warning',
+        title: '提示',
+        content: '确认分配权限?',
+        onOk: async () => {
+          await sysPackageAssign({
+            menuIds: checkedKeys.value.concat(halfCheckedKeys.value),
+            packageId: packageId.value,
+          });
+          createMessage.success('分配成功!');
+          emit('success', packageId.value);
+          closeModal();
+        },
+      });
+    } catch (error) {
+      // console.log('error', error);
+      createMessage.error((error as unknown as Error).message);
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+
+  // 查询树数据
+  async function getTreeData() {
+    treeData.value = await sysMenuQueryTree({});
+  }
+</script>

+ 3 - 76
src/views/sys/sysTenant/package/data.ts

@@ -1,4 +1,3 @@
-import { listDictModel } from '/@/api/common';
 import { DescItem } from '/@/components/Description';
 import { BasicColumn, FormSchema } from '/@/components/Table';
 
@@ -11,22 +10,10 @@ export const columns: BasicColumn[] = [
     title: '套餐名称',
     dataIndex: 'packageName',
   },
-  {
-    title: '套餐备注',
-    dataIndex: 'remark',
-  },
-  {
-    title: '排序',
-    dataIndex: 'sort',
-  },
   {
     title: '套餐类型',
     dataIndex: 'type',
   },
-  {
-    title: '启用',
-    dataIndex: 'disable',
-  },
   {
     title: '创建时间',
     dataIndex: 'createTime',
@@ -51,25 +38,6 @@ export const searchFormSchema: FormSchema[] = [
       placeholder: '请输入套餐名称',
     },
   },
-  {
-    label: '套餐类型',
-    field: 'type',
-    component: 'Input',
-    componentProps: {
-      placeholder: '请输入套餐类型',
-    },
-  },
-  {
-    label: '启用',
-    field: 'disable',
-    component: 'ApiSelect',
-    componentProps: {
-      api: listDictModel,
-      params: {
-        dictCode: 'sys_disable_type',
-      },
-    },
-  },
 ];
 // 表单新增编辑
 export const dataFormSchema: FormSchema[] = [
@@ -94,39 +62,14 @@ export const dataFormSchema: FormSchema[] = [
   {
     label: '套餐备注',
     field: 'remark',
-    required: true,
-    component: 'Input',
+    component: 'InputTextArea',
     componentProps: {
       placeholder: '请输入套餐备注',
     },
   },
-  {
-    label: '排序',
-    field: 'sort',
-    required: true,
-    component: 'Input',
-    componentProps: {
-      placeholder: '请输入排序',
-    },
-  },
-  {
-    label: '启用',
-    field: 'disable',
-    component: 'ApiSelect',
-    componentProps: {
-      api: listDictModel,
-      params: {
-        dictCode: 'sys_disable_type',
-      },
-    },
-  },
 ];
 // 表单详情查看
 export const viewSchema: DescItem[] = [
-  {
-    label: '套餐id',
-    field: 'id',
-  },
   {
     label: '套餐编码',
     field: 'packageCode',
@@ -135,28 +78,12 @@ export const viewSchema: DescItem[] = [
     label: '套餐名称',
     field: 'packageName',
   },
-  {
-    label: '套餐备注',
-    field: 'remark',
-  },
-  {
-    label: '排序',
-    field: 'sort',
-  },
   {
     label: '套餐类型',
     field: 'type',
   },
   {
-    label: '启用',
-    field: 'disable',
-  },
-  {
-    label: '创建时间',
-    field: 'createTime',
-  },
-  {
-    label: '更新时间',
-    field: 'updateTime',
+    label: '备注',
+    field: 'remark',
   },
 ];

+ 1 - 0
src/views/sys/sysTenant/package/formDrawer.vue

@@ -48,6 +48,7 @@
     if (unref(isUpdate)) {
       const resData = await sysTenantPackageDetail(data.record.id);
       rowId.value = resData.id;
+      resData.disable = String(resData.disable);
       await setFieldsValue({
         ...resData,
       });

+ 28 - 2
src/views/sys/sysTenant/package/index.vue

@@ -25,12 +25,22 @@
               {
                 auth: 'sys:tenantPackage:edit',
                 icon: 'icon-edit|iconfont',
+                ifShow: record.type != 'sys',
                 tooltip: '编辑',
                 label: '编辑',
                 onClick: handleEdit.bind(null, record),
               },
+              {
+                auth: 'sys:tenantPackage:assign',
+                icon: 'icon-setting|iconfont',
+                tooltip: '分配菜单',
+                label: '分配菜单',
+                onClick: handleAssignMenu.bind(null, record),
+                ifShow: record.type != 'sys',
+              },
               {
                 auth: 'sys:tenantPackage:remove',
+                ifShow: record.type != 'sys',
                 icon: 'icon-delete|iconfont',
                 tooltip: '删除',
                 label: '删除',
@@ -67,6 +77,7 @@
     </BasicTable>
     <FormDrawer @register="registerDrawer" @success="handleSuccess" />
     <ViewDrawer @register="registerDrawerView" @success="handleSuccess" />
+    <FormModalAssignMenu @register="registerModalAssignMenu" />
   </div>
 </template>
 <script lang="ts" setup>
@@ -82,6 +93,7 @@
   import ViewDrawer from './viewDrawer.vue';
   import { columns, searchFormSchema } from './data';
 
+  import FormModalAssignMenu from './FormModalAssignMenu.vue';
   import {
     sysTenantPackageQueryPage,
     sysTenantPackageRemove,
@@ -90,6 +102,7 @@
   import { formatDictColor, formatDictValue } from '/@/utils';
   import { useDrawer } from '/@/components/Drawer';
 
+  import { useModal } from '/@/components/Modal';
   const typeOptions = ref();
   const disableOptions = ref();
   onBeforeMount(async () => {
@@ -102,7 +115,13 @@
   const [registerDrawer, { openDrawer }] = useDrawer();
   const [registerDrawerView, { openDrawer: openDrawerView }] = useDrawer();
 
+  const [registerModalAssignMenu, { openModal: openModalAssignMenu }] = useModal();
+
   const tableSort = ref([
+    {
+      field: 'type',
+      direction: 'DESC',
+    },
     {
       field: 'create_time',
       direction: 'DESC',
@@ -110,7 +129,7 @@
   ]) as any;
 
   const [registerTable, { reload, getSelectRowKeys }] = useTable({
-    title: '门户列表 ',
+    title: ' ',
     api: sysTenantPackageQueryPage,
     rowKey: 'id',
     columns,
@@ -131,7 +150,7 @@
     useSearchForm: true,
     bordered: true,
     actionColumn: {
-      width: 200,
+      width: 300,
       title: '操作',
       dataIndex: 'action',
     },
@@ -180,6 +199,13 @@
       });
     }
   }
+  // 分配菜单按钮事件
+  function handleAssignMenu(record: Recordable) {
+    console.log(record);
+    openModalAssignMenu(true, {
+      record,
+    });
+  }
   // 表格点击字段排序
   function handleSortFn(sortInfo) {
     if (sortInfo?.order && sortInfo?.columnKey) {

+ 109 - 92
src/views/sys/sysTenant/page/data.ts

@@ -1,38 +1,36 @@
 import { listDictModel } from '/@/api/common';
+import { sysTenantPackageQueryPage } from '/@/api/sys/sysTenantPackageApi';
+import { validatorUsername } from '/@/api/sys/sysUserApi';
 import { DescItem } from '/@/components/Description';
 import { BasicColumn, FormSchema } from '/@/components/Table';
 
 export const columns: BasicColumn[] = [
   {
-    title: '租户名称',
+    title: '名称',
     dataIndex: 'name',
   },
   {
-    title: '租户联系人',
-    dataIndex: 'contractUser',
-  },
-  {
-    title: '租户类型',
+    title: '类型',
     dataIndex: 'type',
   },
   {
-    title: '租户联系人电话',
-    dataIndex: 'contactMobile',
+    title: '联系人',
+    dataIndex: 'contractUser',
   },
   {
-    title: '租户所属用户id',
-    dataIndex: 'tenantUserId',
+    title: '联系人电话',
+    dataIndex: 'contactMobile',
   },
   {
-    title: '租户套餐id',
-    dataIndex: 'packageId',
+    title: '管理账号',
+    dataIndex: 'username',
   },
   {
-    title: '租户备注',
-    dataIndex: 'remark',
+    title: '套餐名称',
+    dataIndex: 'packageName',
   },
   {
-    title: '租户状态',
+    title: '状态',
     dataIndex: 'disable',
   },
 ];
@@ -40,26 +38,26 @@ export const columns: BasicColumn[] = [
 // 表单列定义
 export const searchFormSchema: FormSchema[] = [
   {
-    label: '租户名称',
+    label: '名称',
     field: 'name',
     component: 'Input',
     componentProps: {
-      placeholder: '请输入租户名称',
-    },
-  },
-  {
-    label: '租户类型',
-    field: 'type',
-    component: 'ApiSelect',
-    componentProps: {
-      api: listDictModel,
-      params: {
-        dictCode: 'sys_create_type',
-      },
+      placeholder: '请输入名称',
     },
   },
-  {
-    label: '租户状态',
+  // {
+  //   label: '类型',
+  //   field: 'type',
+  //   component: 'ApiSelect',
+  //   componentProps: {
+  //     api: listDictModel,
+  //     params: {
+  //       dictCode: 'sys_create_type',
+  //     },
+  //   },
+  // },
+  {
+    label: '状态',
     field: 'disable',
     component: 'ApiSelect',
     componentProps: {
@@ -73,126 +71,145 @@ export const searchFormSchema: FormSchema[] = [
 // 表单新增编辑
 export const dataFormSchema: FormSchema[] = [
   {
-    label: '租户名称',
+    label: '名称',
     field: 'name',
     required: true,
     component: 'Input',
     componentProps: {
-      placeholder: '请输入租户名称',
+      placeholder: '请输入名称',
     },
   },
-  {
-    label: '租户联系人',
+  // 类型是否可以修改待定
+  // {
+  //   label: '类型',
+  //   field: 'type',
+  //   component: 'ApiRadioGroup',
+  //   componentProps: {
+  //     api: listDictModel,
+  //     params: {
+  //       dictCode: 'sys_create_type',
+  //     },
+  //   },
+  // },
+  {
+    label: '联系人',
     field: 'contractUser',
     required: true,
     component: 'Input',
     componentProps: {
-      placeholder: '请输入租户联系人',
-    },
-  },
-  {
-    label: '租户类型',
-    field: 'type',
-    component: 'ApiRadioGroup',
-    componentProps: {
-      api: listDictModel,
-      params: {
-        dictCode: 'sys_create_type',
-      },
+      placeholder: '请输入联系人',
     },
   },
   {
-    label: '租户联系人电话',
+    label: '联系人电话',
     field: 'contactMobile',
     required: true,
     component: 'Input',
     componentProps: {
-      placeholder: '请输入租户联系人电话',
+      placeholder: '请输入联系人电话',
     },
   },
   {
-    label: '租户所属用户id',
-    field: 'tenantUserId',
-    required: true,
+    label: '管理账号',
+    field: 'username',
     component: 'Input',
     componentProps: {
-      placeholder: '请输入租户所属用户id',
+      placeholder: '请输入管理账号',
+      disabled: false,
     },
-  },
-  {
-    label: '租户套餐id',
-    field: 'packageId',
-    required: true,
-    component: 'Input',
-    componentProps: {
-      placeholder: '请输入租户套餐id',
+    dynamicRules: () => {
+      return [
+        {
+          required: true,
+          validator: async (_, value) => {
+            if (!value) {
+              return Promise.reject('管理账号不能为空');
+            }
+            await validatorUsername({ username: value }).then(res => {
+              if (res != 1) {
+                return Promise.reject('管理账号不可用,请重新输入!');
+              }
+            });
+            return Promise.resolve();
+          },
+        },
+      ];
     },
   },
   {
-    label: '租户备注',
-    field: 'remark',
+    label: '套餐',
+    field: 'packageId',
     required: true,
-    component: 'Input',
-    componentProps: {
-      placeholder: '请输入租户备注',
+    component: 'ApiSelect',
+    componentProps: () => {
+      return {
+        placeholder: '请选择套餐',
+        api: sysTenantPackageQueryPage,
+        params: {
+          pageNum: 1,
+          pageSize: 999,
+          disable: '0',
+        },
+        mode: 'single',
+        labelField: 'packageName',
+        valueField: 'id',
+        resultField: 'data',
+      };
     },
   },
   {
-    label: '租户状态',
+    label: '状态',
     field: 'disable',
-    component: 'ApiSelect',
+    component: 'ApiRadioGroup',
     componentProps: {
       api: listDictModel,
       params: {
         dictCode: 'sys_disable_type',
       },
     },
+    defaultValue: '0',
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '请输入备注',
+    },
   },
 ];
 // 表单详情查看
 export const viewSchema: DescItem[] = [
   {
-    label: '租户id',
-    field: 'id',
-  },
-  {
-    label: '租户名称',
+    label: '名称',
     field: 'name',
   },
   {
-    label: '租户联系人',
-    field: 'contractUser',
-  },
-  {
-    label: '租户类型',
+    label: '类型',
     field: 'type',
   },
   {
-    label: '租户联系人电话',
-    field: 'contactMobile',
+    label: '联系人',
+    field: 'contractUser',
   },
   {
-    label: '租户所属用户id',
-    field: 'tenantUserId',
+    label: '联系人电话',
+    field: 'contactMobile',
   },
   {
-    label: '租户套餐id',
-    field: 'packageId',
+    label: '管理账号',
+    field: 'username',
   },
   {
-    label: '租户备注',
-    field: 'remark',
+    label: '套餐',
+    field: 'packageName',
   },
   {
-    label: '租户状态',
+    label: '状态',
     field: 'disable',
   },
   {
-    label: '创建时间',
-    field: 'createTime',
-  },
-  {
-    label: '更新时间',
-    field: 'updateTime',
+    label: '备注',
+    field: 'remark',
   },
 ];

+ 17 - 1
src/views/sys/sysTenant/page/formDrawer.vue

@@ -28,7 +28,7 @@
   const rowId = ref();
 
   const { createMessage } = useMessage();
-  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+  const [registerForm, { setFieldsValue, resetFields, validate, updateSchema }] = useForm({
     labelWidth: 100,
     schemas: dataFormSchema,
     showActionButtonGroup: false,
@@ -42,8 +42,24 @@
     isUpdate.value = !!data?.isUpdate;
 
     if (unref(isUpdate)) {
+      updateSchema([
+        {
+          field: 'username',
+          componentProps: {
+            disabled: true,
+          },
+          dynamicRules: () => {
+            return [
+              {
+                required: true,
+              },
+            ];
+          },
+        },
+      ]);
       const resData = await sysTenantDetail(data.record.id);
       rowId.value = resData.id;
+      resData.disable = String(resData.disable);
       await setFieldsValue({
         ...resData,
       });

+ 7 - 1
src/views/sys/sysTenant/page/index.vue

@@ -25,12 +25,14 @@
               {
                 auth: 'sys:tenant:edit',
                 icon: 'icon-edit|iconfont',
+                ifShow: record.type != 'sys',
                 tooltip: '编辑',
                 label: '编辑',
                 onClick: handleEdit.bind(null, record),
               },
               {
                 auth: 'sys:tenant:remove',
+                ifShow: record.type != 'sys',
                 icon: 'icon-delete|iconfont',
                 tooltip: '删除',
                 label: '删除',
@@ -100,6 +102,10 @@
   const [registerDrawerView, { openDrawer: openDrawerView }] = useDrawer();
 
   const tableSort = ref([
+    {
+      field: 'type',
+      direction: 'DESC',
+    },
     {
       field: 'create_time',
       direction: 'DESC',
@@ -107,7 +113,7 @@
   ]) as any;
 
   const [registerTable, { reload, getSelectRowKeys }] = useTable({
-    title: '门户列表 ',
+    title: '',
     api: sysTenantQueryPage,
     rowKey: 'id',
     columns,