Lind 4 лет назад
Родитель
Сommit
0725a8cf84

+ 26 - 0
src/components/BaseCrud/model.ts

@@ -0,0 +1,26 @@
+import type { Option } from '@/components/BaseCrud/index';
+import { Store } from 'jetlinks-store';
+import SystemConst from '@/utils/const';
+
+export const CurdModel = <Option>{
+  model: 'edit',
+  current: undefined,
+  visible: false,
+
+  add() {
+    Store.set(SystemConst.BASE_CURD_MODAL_VISIBLE, true);
+    Store.set(SystemConst.BASE_CURD_CURRENT, {});
+    Store.set(SystemConst.BASE_CURD_MODEL, 'add');
+  },
+
+  update(current: any) {
+    Store.set(SystemConst.BASE_CURD_MODEL, 'edit');
+    Store.set(SystemConst.BASE_CURD_MODAL_VISIBLE, true);
+    Store.set(SystemConst.BASE_CURD_CURRENT, current);
+  },
+
+  close() {
+    Store.set(SystemConst.BASE_CURD_MODAL_VISIBLE, false);
+    Store.set(SystemConst.BASE_CURD_CURRENT, {});
+  },
+};

+ 276 - 0
src/pages/system/User/index.tsx

@@ -0,0 +1,276 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import React, { useRef } from 'react';
+import {
+  EditOutlined,
+  KeyOutlined,
+  CloseCircleOutlined,
+  PlayCircleOutlined,
+} from '@ant-design/icons';
+import { Menu, Tooltip, Popconfirm, message } from 'antd';
+import type { ProColumns, ActionType } from '@ant-design/pro-table';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import BaseCrud from '@/components/BaseCrud';
+import { CurdModel } from '@/components/BaseCrud/model';
+import BaseService from '@/utils/BaseService';
+
+const menu = (
+  <Menu>
+    <Menu.Item key="1">1st item</Menu.Item>
+    <Menu.Item key="2">2nd item</Menu.Item>
+    <Menu.Item key="3">3rd item</Menu.Item>
+  </Menu>
+);
+
+export const service = new BaseService<UserItem>('user');
+const User: React.FC = () => {
+  const intl = useIntl();
+  const actionRef = useRef<ActionType>();
+
+  const columns: ProColumns<UserItem>[] = [
+    {
+      dataIndex: 'index',
+      valueType: 'indexBorder',
+      width: 48,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.system.user.name',
+        defaultMessage: '姓名',
+      }),
+      dataIndex: 'name',
+      copyable: true,
+      ellipsis: true,
+      align: 'center',
+      tip: '姓名过长会自动收缩',
+      sorter: true,
+      defaultSortOrder: 'ascend',
+      formItemProps: {
+        rules: [
+          {
+            required: true,
+            message: '此项为必填项',
+          },
+        ],
+      },
+      search: {
+        transform: (value) => ({ name$LIKE: value }),
+      },
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.system.user.username',
+        defaultMessage: '用户名',
+      }),
+      dataIndex: 'username',
+      copyable: true,
+      ellipsis: true,
+      align: 'center',
+      tip: '用户名过长会自动收缩',
+      formItemProps: {
+        rules: [
+          {
+            required: true,
+            message: '此项为必填项',
+          },
+        ],
+      },
+      search: {
+        transform: (value) => ({ username$LIKE: value }),
+      },
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.system.user.status',
+        defaultMessage: '状态',
+      }),
+      dataIndex: 'status',
+      filters: true,
+      align: 'center',
+      onFilter: true,
+      valueType: 'select',
+      valueEnum: {
+        all: { text: '全部', status: 'Default' },
+        1: {
+          text: '正常',
+          status: 1,
+        },
+        0: {
+          text: '禁用',
+          status: 0,
+        },
+      },
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.data.option',
+        defaultMessage: '操作',
+      }),
+      valueType: 'option',
+      align: 'center',
+      width: 200,
+      render: (text, record) => [
+        <a key="editable" onClick={() => CurdModel.update(record)}>
+          <Tooltip
+            title={intl.formatMessage({
+              id: 'pages.data.option.edit',
+              defaultMessage: '编辑',
+            })}
+          >
+            <EditOutlined />
+          </Tooltip>
+        </a>,
+        <a onClick={() => console.log('授权')}>
+          <Tooltip
+            title={intl.formatMessage({
+              id: 'pages.data.option.authorize',
+              defaultMessage: '授权',
+            })}
+          >
+            <KeyOutlined />
+          </Tooltip>
+        </a>,
+        <a href={record.id} target="_blank" rel="noopener noreferrer" key="view">
+          <Popconfirm
+            title={intl.formatMessage({
+              id: 'pages.data.option.disable.tips',
+              defaultMessage: '确认禁用?',
+            })}
+            onConfirm={async () => {
+              await service.update({
+                id: record.id,
+                status: record.status ? 0 : 1,
+              });
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
+            }}
+          >
+            <Tooltip
+              title={intl.formatMessage({
+                id: `pages.data.option.${record.status ? 'disable' : 'enable'}`,
+                defaultMessage: record.status ? '禁用' : '启用',
+              })}
+            >
+              {record.status ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
+            </Tooltip>
+          </Popconfirm>
+        </a>,
+      ],
+    },
+  ];
+
+  const schema = {
+    type: 'object',
+    properties: {
+      name: {
+        title: intl.formatMessage({
+          id: 'pages.system.user.name',
+          defaultMessage: '姓名',
+        }),
+        type: 'string',
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-component-props': {},
+        'x-decorator-props': {},
+        name: 'name',
+        required: true,
+        _designableId: '1jq1ln7nzji',
+        'x-index': 0,
+      },
+      username: {
+        title: intl.formatMessage({
+          id: 'pages.system.user.username',
+          defaultMessage: '用户名',
+        }),
+        type: 'string',
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-component-props': {},
+        'x-decorator-props': {},
+        name: 'username',
+        required: true,
+        _designableId: '9vf50ad9n1h',
+        'x-index': 1,
+      },
+      password: {
+        type: 'string',
+        title: intl.formatMessage({
+          id: 'pages.system.user.password',
+          defaultMessage: '密码',
+        }),
+        'x-decorator': 'FormItem',
+        'x-component': 'Password',
+        'x-component-props': {
+          checkStrength: true,
+        },
+        'x-reactions': [
+          {
+            dependencies: ['.confirmPassword'],
+            fulfill: {
+              state: {
+                errors:
+                  '{{$deps[0] && $self.value && $self.value !==$deps[0] ? "确认密码不匹配" : ""}}',
+              },
+            },
+          },
+        ],
+        'x-decorator-props': {},
+        name: 'password',
+        required: false,
+        _designableId: 'weg6kt6izlt',
+        'x-index': 2,
+      },
+      confirmPassword: {
+        type: 'string',
+        title: intl.formatMessage({
+          id: 'pages.system.user.confirmPassword',
+          defaultMessage: '确认密码?',
+        }),
+        'x-decorator': 'FormItem',
+        'x-component': 'Password',
+        'x-component-props': {
+          checkStrength: true,
+        },
+        'x-reactions': [
+          {
+            dependencies: ['.password'],
+            fulfill: {
+              state: {
+                errors:
+                  '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "确认密码不匹配" : ""}}',
+              },
+            },
+          },
+        ],
+        'x-decorator-props': {},
+        name: 'confirmPassword',
+        required: false,
+        _designableId: 'mhsm2fk573e',
+        'x-index': 3,
+      },
+    },
+    _designableId: 'zd740kqp5hf',
+  };
+
+  return (
+    <PageContainer>
+      <BaseCrud<UserItem>
+        actionRef={actionRef}
+        columns={columns}
+        service={service}
+        title={intl.formatMessage({
+          id: 'pages.system.user',
+          defaultMessage: '用户管理',
+        })}
+        menu={menu}
+        schema={schema}
+      />
+    </PageContainer>
+  );
+};
+
+export default User;

+ 11 - 0
src/pages/system/User/typings.d.ts

@@ -0,0 +1,11 @@
+type UserItem = {
+  id: string;
+  name: string;
+  status: number;
+  username: string;
+  createTime: number;
+  email?: string;
+  telephone?: string;
+  avatar?: string;
+  description?: string;
+};

+ 149 - 270
src/pages/user/Login/index.tsx

@@ -1,88 +1,158 @@
-import {
-  AlipayCircleOutlined,
-  LockOutlined,
-  MobileOutlined,
-  TaobaoCircleOutlined,
-  UserOutlined,
-  WeiboCircleOutlined,
-} from '@ant-design/icons';
-import { Alert, Space, message, Tabs } from 'antd';
-import React, { useState } from 'react';
-import ProForm, { ProFormCaptcha, ProFormCheckbox, ProFormText } from '@ant-design/pro-form';
-import { useIntl, Link, history, FormattedMessage, SelectLang, useModel } from 'umi';
+import { message } from 'antd';
+import React, { useEffect, useRef, useState } from 'react';
+import { Link, history } from 'umi';
 import Footer from '@/components/Footer';
-import { login } from '@/services/ant-design-pro/api';
-import { getFakeCaptcha } from '@/services/ant-design-pro/login';
-
 import styles from './index.less';
+import Token from '@/utils/token';
+import Service from '@/pages/user/Login/service';
+import { createForm } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+import { Form, Submit, Input, Password, FormItem } from '@formily/antd';
+import { filter, mergeMap } from 'rxjs/operators';
+import * as ICONS from '@ant-design/icons';
+import { useModel } from '@@/plugin-model/useModel';
+import SystemConst from '@/utils/const';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { SelectLang } from '@@/plugin-locale/SelectLang';
 
-const LoginMessage: React.FC<{
-  content: string;
-}> = ({ content }) => (
-  <Alert
-    style={{
-      marginBottom: 24,
-    }}
-    message={content}
-    type="error"
-    showIcon
-  />
-);
+/** 此方法会跳转到 redirect 参数所在的位置 */
+const goto = () => {
+  if (!history) return;
+  setTimeout(() => {
+    const { query } = history.location;
+    const { redirect } = query as {
+      redirect: string;
+    };
+    history.push(redirect || '/');
+  }, 10);
+};
 
 const Login: React.FC = () => {
-  const [submitting, setSubmitting] = useState(false);
-  const [userLoginState, setUserLoginState] = useState<API.LoginResult>({});
-  const [type, setType] = useState<string>('account');
+  const [captcha, setCaptcha] = useState<{ key?: string; base64?: string }>({});
+
   const { initialState, setInitialState } = useModel('@@initialState');
 
   const intl = useIntl();
 
   const fetchUserInfo = async () => {
-    const userInfo = await initialState?.fetchUserInfo?.();
+    const userInfo = (await initialState?.fetchUserInfo?.()) as UserInfo;
     if (userInfo) {
-      await setInitialState((s) => ({
-        ...s,
+      setInitialState({
+        ...initialState,
         currentUser: userInfo,
-      }));
+      });
     }
   };
 
-  const handleSubmit = async (values: API.LoginParams) => {
-    setSubmitting(true);
-    try {
-      // 登录
-      const msg = await login({ ...values, type });
-      if (msg.status === 'ok') {
-        const defaultLoginSuccessMessage = intl.formatMessage({
-          id: 'pages.login.success',
-          defaultMessage: '登录成功!',
-        });
-        message.success(defaultLoginSuccessMessage);
-        await fetchUserInfo();
-        /** 此方法会跳转到 redirect 参数所在的位置 */
-        if (!history) return;
-        const { query } = history.location;
-        const { redirect } = query as { redirect: string };
-        history.push(redirect || '/');
-        return;
-      }
-      // 如果失败去设置用户错误信息
-      setUserLoginState(msg);
-    } catch (error) {
-      const defaultLoginFailureMessage = intl.formatMessage({
-        id: 'pages.login.failure',
-        defaultMessage: '登录失败,请重试!',
-      });
+  const loginRef = useRef<Partial<LoginParam>>({});
+  const loginForm = createForm({
+    validateFirst: true,
+    initialValues: loginRef.current,
+  });
 
-      message.error(defaultLoginFailureMessage);
-    }
-    setSubmitting(false);
+  const getCode = () => {
+    delete loginForm.values?.verifyCode;
+    loginRef.current = loginForm.values;
+    Service.captchaConfig()
+      .pipe(
+        filter((r) => r.enabled),
+        mergeMap(Service.getCaptcha),
+      )
+      .subscribe(setCaptcha);
   };
-  const { status, type: loginType } = userLoginState;
 
+  useEffect(() => {
+    getCode();
+  }, []);
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Input,
+      Password,
+    },
+    scope: {
+      icon(name: any) {
+        return React.createElement(ICONS[name]);
+      },
+    },
+  });
+
+  const schema = {
+    type: 'object',
+    properties: {
+      username: {
+        type: 'string',
+        // title: '用户名',
+        'x-decorator': 'FormItem',
+        'x-validator': { required: true, message: '请输入用户名!' },
+        'x-component': 'Input',
+        'x-component-props': {
+          placeholder: intl.formatMessage({
+            id: 'pages.login.username.placeholder',
+            defaultMessage: '用户名',
+          }),
+          prefix: "{{icon('UserOutlined')}}",
+        },
+      },
+      password: {
+        type: 'string',
+        // title: '密码',
+        'x-validator': { required: true, message: '请输入用户名!' },
+        'x-decorator': 'FormItem',
+        'x-component': 'Password',
+        'x-component-props': {
+          prefix: "{{icon('LockOutlined')}}",
+          placeholder: intl.formatMessage({
+            id: 'pages.login.password.placeholder',
+            defaultMessage: '密码',
+          }),
+        },
+      },
+      verifyCode: {
+        type: 'string',
+        // title: '验证码',
+        'x-visible': !!captcha.key,
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-component-props': {
+          addonAfter: <img src={captcha.base64} alt="验证码" onClick={getCode} />,
+          placeholder: intl.formatMessage({
+            id: 'pages.login.captcha.placeholder',
+            defaultMessage: '请输入验证码',
+          }),
+        },
+      },
+    },
+  };
+
+  const doLogin = async (data: LoginParam) =>
+    Service.login({ verifyKey: captcha.key, ...data }).subscribe({
+      next: async (userInfo: UserInfo) => {
+        message.success(
+          intl.formatMessage({
+            id: 'pages.login.success',
+            defaultMessage: '登录成功!',
+          }),
+        );
+        Token.set(userInfo.token);
+        await fetchUserInfo();
+        goto();
+      },
+      error: () =>
+        message.error(
+          intl.formatMessage({
+            id: 'pages.login.failure',
+            defaultMessage: '登录失败,请重试!',
+          }),
+        ),
+      complete: () => {
+        getCode();
+      },
+    });
   return (
     <div className={styles.container}>
-      <div className={styles.lang} data-lang>
+      <div className={styles.lang} data-lang="">
         {SelectLang && <SelectLang />}
       </div>
       <div className={styles.content}>
@@ -90,218 +160,27 @@ const Login: React.FC = () => {
           <div className={styles.header}>
             <Link to="/">
               <img alt="logo" className={styles.logo} src="/logo.svg" />
-              <span className={styles.title}>Ant Design</span>
+              <span className={styles.title}>{SystemConst.SYSTEM_NAME}</span>
             </Link>
           </div>
           <div className={styles.desc}>
-            {intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
+            {intl.formatMessage({
+              id: 'pages.layouts.userLayout.title',
+              defaultMessage: 'Jetlinks',
+            })}
           </div>
         </div>
 
         <div className={styles.main}>
-          <ProForm
-            initialValues={{
-              autoLogin: true,
-            }}
-            submitter={{
-              searchConfig: {
-                submitText: intl.formatMessage({
-                  id: 'pages.login.submit',
-                  defaultMessage: '登录',
-                }),
-              },
-              render: (_, dom) => dom.pop(),
-              submitButtonProps: {
-                loading: submitting,
-                size: 'large',
-                style: {
-                  width: '100%',
-                },
-              },
-            }}
-            onFinish={async (values) => {
-              handleSubmit(values as API.LoginParams);
-            }}
-          >
-            <Tabs activeKey={type} onChange={setType}>
-              <Tabs.TabPane
-                key="account"
-                tab={intl.formatMessage({
-                  id: 'pages.login.accountLogin.tab',
-                  defaultMessage: '账户密码登录',
-                })}
-              />
-              <Tabs.TabPane
-                key="mobile"
-                tab={intl.formatMessage({
-                  id: 'pages.login.phoneLogin.tab',
-                  defaultMessage: '手机号登录',
-                })}
-              />
-            </Tabs>
-
-            {status === 'error' && loginType === 'account' && (
-              <LoginMessage
-                content={intl.formatMessage({
-                  id: 'pages.login.accountLogin.errorMessage',
-                  defaultMessage: '账户或密码错误(admin/ant.design)',
-                })}
-              />
-            )}
-            {type === 'account' && (
-              <>
-                <ProFormText
-                  name="username"
-                  fieldProps={{
-                    size: 'large',
-                    prefix: <UserOutlined className={styles.prefixIcon} />,
-                  }}
-                  placeholder={intl.formatMessage({
-                    id: 'pages.login.username.placeholder',
-                    defaultMessage: '用户名: admin or user',
-                  })}
-                  rules={[
-                    {
-                      required: true,
-                      message: (
-                        <FormattedMessage
-                          id="pages.login.username.required"
-                          defaultMessage="请输入用户名!"
-                        />
-                      ),
-                    },
-                  ]}
-                />
-                <ProFormText.Password
-                  name="password"
-                  fieldProps={{
-                    size: 'large',
-                    prefix: <LockOutlined className={styles.prefixIcon} />,
-                  }}
-                  placeholder={intl.formatMessage({
-                    id: 'pages.login.password.placeholder',
-                    defaultMessage: '密码: ant.design',
-                  })}
-                  rules={[
-                    {
-                      required: true,
-                      message: (
-                        <FormattedMessage
-                          id="pages.login.password.required"
-                          defaultMessage="请输入密码!"
-                        />
-                      ),
-                    },
-                  ]}
-                />
-              </>
-            )}
-
-            {status === 'error' && loginType === 'mobile' && <LoginMessage content="验证码错误" />}
-            {type === 'mobile' && (
-              <>
-                <ProFormText
-                  fieldProps={{
-                    size: 'large',
-                    prefix: <MobileOutlined className={styles.prefixIcon} />,
-                  }}
-                  name="mobile"
-                  placeholder={intl.formatMessage({
-                    id: 'pages.login.phoneNumber.placeholder',
-                    defaultMessage: '手机号',
-                  })}
-                  rules={[
-                    {
-                      required: true,
-                      message: (
-                        <FormattedMessage
-                          id="pages.login.phoneNumber.required"
-                          defaultMessage="请输入手机号!"
-                        />
-                      ),
-                    },
-                    {
-                      pattern: /^1\d{10}$/,
-                      message: (
-                        <FormattedMessage
-                          id="pages.login.phoneNumber.invalid"
-                          defaultMessage="手机号格式错误!"
-                        />
-                      ),
-                    },
-                  ]}
-                />
-                <ProFormCaptcha
-                  fieldProps={{
-                    size: 'large',
-                    prefix: <LockOutlined className={styles.prefixIcon} />,
-                  }}
-                  captchaProps={{
-                    size: 'large',
-                  }}
-                  placeholder={intl.formatMessage({
-                    id: 'pages.login.captcha.placeholder',
-                    defaultMessage: '请输入验证码',
-                  })}
-                  captchaTextRender={(timing, count) => {
-                    if (timing) {
-                      return `${count} ${intl.formatMessage({
-                        id: 'pages.getCaptchaSecondText',
-                        defaultMessage: '获取验证码',
-                      })}`;
-                    }
-                    return intl.formatMessage({
-                      id: 'pages.login.phoneLogin.getVerificationCode',
-                      defaultMessage: '获取验证码',
-                    });
-                  }}
-                  name="captcha"
-                  rules={[
-                    {
-                      required: true,
-                      message: (
-                        <FormattedMessage
-                          id="pages.login.captcha.required"
-                          defaultMessage="请输入验证码!"
-                        />
-                      ),
-                    },
-                  ]}
-                  onGetCaptcha={async (phone) => {
-                    const result = await getFakeCaptcha({
-                      phone,
-                    });
-                    if (result === false) {
-                      return;
-                    }
-                    message.success('获取验证码成功!验证码为:1234');
-                  }}
-                />
-              </>
-            )}
-            <div
-              style={{
-                marginBottom: 24,
-              }}
-            >
-              <ProFormCheckbox noStyle name="autoLogin">
-                <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
-              </ProFormCheckbox>
-              <a
-                style={{
-                  float: 'right',
-                }}
-              >
-                <FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />
-              </a>
-            </div>
-          </ProForm>
-          <Space className={styles.other}>
-            <FormattedMessage id="pages.login.loginWith" defaultMessage="其他登录方式" />
-            <AlipayCircleOutlined className={styles.icon} />
-            <TaobaoCircleOutlined className={styles.icon} />
-            <WeiboCircleOutlined className={styles.icon} />
-          </Space>
+          <Form form={loginForm} layout="vertical" size="large" onAutoSubmit={doLogin}>
+            <SchemaField schema={schema} />
+            <Submit block size="large">
+              {intl.formatMessage({
+                id: 'pages.login.submit',
+                defaultMessage: '登录',
+              })}
+            </Submit>
+          </Form>
         </div>
       </div>
       <Footer />

+ 47 - 0
src/pages/user/Login/service.ts

@@ -0,0 +1,47 @@
+import { defer, from } from 'rxjs';
+import { filter, map } from 'rxjs/operators';
+import SystemConst from '@/utils/const';
+import { request } from 'umi';
+
+const Service = {
+  captchaConfig: () =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/authorize/captcha/config`, {
+          method: 'GET',
+          errorHandler: () => {
+            // 未开启验证码 不显示验证码
+          },
+        }),
+      ).pipe(map((resp) => resp?.result)),
+    ),
+
+  getCaptcha: () =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/authorize/captcha/image?width=130&height=30`, {
+          method: 'GET',
+        }),
+      ).pipe(map((resp) => resp.result)),
+    ),
+
+  login: (data: LoginParam) =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/authorize/login`, {
+          method: 'POST',
+          data,
+        }),
+      ).pipe(
+        filter((r) => r.status === 200),
+        map((resp) => resp.result),
+      ),
+    ),
+
+  queryCurrent: () =>
+    request(`/${SystemConst.API_BASE}/authorize/me`, {
+      method: 'GET',
+    }),
+};
+
+export default Service;

+ 43 - 0
src/pages/user/Login/user.d.ts

@@ -0,0 +1,43 @@
+type LoginParam = {
+  username: string;
+  password: string;
+  expires?: number;
+  verifyCode?: string;
+  verifyKey?: string;
+};
+
+type UserBase = {
+  avatar: string;
+  createTime: number;
+  description: string;
+  email: string;
+  id: string;
+  name: string;
+  telephone: string;
+  tenantDisabled: boolean;
+  tenants: any[];
+};
+
+type Role = {
+  id: string;
+  name: string;
+  type: string;
+};
+
+type Permission = {
+  id: string;
+  name: string;
+  actions: string[];
+  dataAccesses: string[];
+  options: any;
+};
+
+type UserInfo = {
+  userId: string;
+  user: Partial<UserBase>;
+  token: string;
+  roles: Role[];
+  permissions: Partial<Permission>[];
+  expires: number;
+  currentAuthority: string[];
+};