Lind 4 лет назад
Родитель
Сommit
9f27210a90

+ 3 - 3
config/config.ts

@@ -66,7 +66,7 @@ export default defineConfig({
     },
   ],
   nodeModulesTransform: { type: 'none' },
-  mfsu: {},
-  webpack5: {},
-  exportStatic: {},
+  // mfsu: {},
+  // webpack5: {},
+  // exportStatic: {},
 });

+ 5 - 2
config/defaultSettings.ts

@@ -6,13 +6,16 @@ const Settings: LayoutSettings & {
 } = {
   navTheme: 'light',
   // 拂晓蓝
-  primaryColor: '#1890ff',
+  // primaryColor: '#1890ff',
+  // 极光绿
+  primaryColor: '#52C41A',
   layout: 'mix',
   contentWidth: 'Fluid',
+  splitMenus: true,
   fixedHeader: false,
   fixSiderbar: true,
   colorWeak: false,
-  title: 'Ant Design Pro',
+  title: 'Jetlinks',
   pwa: false,
   logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
   iconfontUrl: '',

+ 82 - 53
src/app.tsx

@@ -1,11 +1,14 @@
 import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
 import { PageLoading } from '@ant-design/pro-layout';
-import type { RunTimeLayoutConfig } from 'umi';
+import { notification } from 'antd';
+import type { RequestConfig, RunTimeLayoutConfig } from 'umi';
 import { history, Link } from 'umi';
 import RightContent from '@/components/RightContent';
 import Footer from '@/components/Footer';
-import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
 import { BookOutlined, LinkOutlined } from '@ant-design/icons';
+import Service from '@/pages/user/Login/service';
+import Token from '@/utils/token';
+import type { RequestOptionsInit } from 'umi-request';
 
 const isDev = process.env.NODE_ENV === 'development';
 const loginPath = '/user/login';
@@ -20,13 +23,13 @@ export const initialStateConfig = {
  * */
 export async function getInitialState(): Promise<{
   settings?: Partial<LayoutSettings>;
-  currentUser?: API.CurrentUser;
-  fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
+  currentUser?: UserInfo;
+  fetchUserInfo?: () => Promise<UserInfo | undefined>;
 }> {
   const fetchUserInfo = async () => {
     try {
-      const msg = await queryCurrentUser();
-      return msg.data;
+      const user = await Service.queryCurrent();
+      return user.result;
     } catch (error) {
       history.push(loginPath);
     }
@@ -49,55 +52,81 @@ export async function getInitialState(): Promise<{
 
 /**
  * 异常处理程序
-    200: '服务器成功返回请求的数据。',
-    201: '新建或修改数据成功。',
-    202: '一个请求已经进入后台排队(异步任务)。',
-    204: '删除数据成功。',
-    400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
-    401: '用户没有权限(令牌、用户名、密码错误)。',
-    403: '用户得到授权,但是访问是被禁止的。',
-    404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
-    405: '请求方法不被允许。',
-    406: '请求的格式不可得。',
-    410: '请求的资源被永久删除,且不会再得到的。',
-    422: '当创建一个对象时,发生一个验证错误。',
-    500: '服务器发生错误,请检查服务器。',
-    502: '网关错误。',
-    503: '服务不可用,服务器暂时过载或维护。',
-    504: '网关超时。',
+ 200: '服务器成功返回请求的数据。',
+ 201: '新建或修改数据成功。',
+ 202: '一个请求已经进入后台排队(异步任务)。',
+ 204: '删除数据成功。',
+ 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
+ 401: '用户没有权限(令牌、用户名、密码错误)。',
+ 403: '用户得到授权,但是访问是被禁止的。',
+ 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
+ 405: '请求方法不被允许。',
+ 406: '请求的格式不可得。',
+ 410: '请求的资源被永久删除,且不会再得到的。',
+ 422: '当创建一个对象时,发生一个验证错误。',
+ 500: '服务器发生错误,请检查服务器。',
+ 502: '网关错误。',
+ 503: '服务不可用,服务器暂时过载或维护。',
+ 504: '网关超时。',
  //-----English
-    200: The server successfully returned the requested data. ',
-    201: New or modified data is successful. ',
-    202: A request has entered the background queue (asynchronous task). ',
-    204: Data deleted successfully. ',
-    400: 'There was an error in the request sent, and the server did not create or modify data. ',
-    401: The user does not have permission (token, username, password error). ',
-    403: The user is authorized, but access is forbidden. ',
-    404: The request sent was for a record that did not exist. ',
-    405: The request method is not allowed. ',
-    406: The requested format is not available. ',
-    410':
-        'The requested resource is permanently deleted and will no longer be available. ',
-    422: When creating an object, a validation error occurred. ',
-    500: An error occurred on the server, please check the server. ',
-    502: Gateway error. ',
-    503: The service is unavailable. ',
-    504: The gateway timed out. ',
- * @see https://pro.ant.design/docs/request/
+ 200: The server successfully returned the requested data. ',
+ 201: New or modified data is successful. ',
+ 202: A request has entered the background queue (asynchronous task). ',
+ 204: Data deleted successfully. ',
+ 400: 'There was an error in the request sent, and the server did not create or modify data. ',
+ 401: The user does not have permission (token, username, password error). ',
+ 403: The user is authorized, but access is forbidden. ',
+ 404: The request sent was for a record that did not exist. ',
+ 405: The request method is not allowed. ',
+ 406: The requested format is not available. ',
+ 410':
+ 'The requested resource is permanently deleted and will no longer be available. ',
+ 422: When creating an object, a validation error occurred. ',
+ 500: An error occurred on the server, please check the server. ',
+ 502: Gateway error. ',
+ 503: The service is unavailable. ',
+ 504: The gateway timed out. ',
+ * @see https://beta-pro.ant.design/docs/request-cn
  */
-// export const request: RequestConfig = {
-//   errorHandler: (error: any) => {
-//     const { response } = error;
 
-//     if (!response) {
-//       notification.error({
-//         description: '您的网络发生异常,无法连接服务器',
-//         message: '网络异常',
-//       });
-//     }
-//     throw error;
-//   },
-// };
+/**
+ * Token 拦截器
+ * @param url
+ * @param options
+ */
+const requestInterceptor = (url: string, options: RequestOptionsInit) => {
+  // const {params} = options;
+  const authHeader = { 'X-Access-Token': Token.get() || '' };
+  return {
+    url: `${url}`,
+    options: {
+      ...options,
+      // 格式化成后台需要的查询参数
+      // params: encodeQueryParam(params),
+      interceptors: true,
+      headers: authHeader,
+    },
+  };
+};
+
+export const request: RequestConfig = {
+  errorHandler: (error: any) => {
+    const { response } = error;
+
+    if (response.status === 401) {
+      history.push('/user/login');
+      return;
+    }
+    if (!response) {
+      notification.error({
+        description: '您的网络发生异常,无法连接服务器',
+        message: '网络异常',
+      });
+    }
+    throw error;
+  },
+  requestInterceptors: [requestInterceptor],
+};
 
 // ProLayout 支持的api https://procomponents.ant.design/components/layout
 export const layout: RunTimeLayoutConfig = ({ initialState }) => {
@@ -105,7 +134,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
     rightContentRender: () => <RightContent />,
     disableContentMargin: false,
     waterMarkProps: {
-      content: initialState?.currentUser?.name,
+      // content: initialState?.currentUser?.name,
     },
     footerRender: () => <Footer />,
     onPageChange: () => {

+ 104 - 0
src/components/BaseCrud/index.tsx

@@ -0,0 +1,104 @@
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { Button, Dropdown } from 'antd';
+import ProTable from '@ant-design/pro-table';
+import type { ProColumns, ActionType, RequestData } from '@ant-design/pro-table';
+
+import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons';
+import type BaseService from '@/utils/BaseService';
+import * as React from 'react';
+import Save from '@/components/BaseCrud/save';
+import type { ISchema } from '@formily/json-schema';
+import { CurdModel } from '@/components/BaseCrud/model';
+import type { ISchemaFieldProps } from '@formily/react/lib/types';
+import type { ModalProps } from 'antd/lib/modal/Modal';
+
+export type Option = {
+  model: 'edit' | 'preview' | 'add';
+  current: any;
+  visible: boolean;
+  add: () => void;
+  update: (current: any) => void;
+  close: () => void;
+};
+
+declare type OverlayFunc = () => React.ReactElement;
+export type Props<T> = {
+  columns: ProColumns<T>[];
+  service: BaseService<T>;
+  title: string;
+  menu?: React.ReactElement | OverlayFunc;
+  schema: ISchema;
+  schemaConfig?: ISchemaFieldProps;
+  defaultParams?: Record<string, any>;
+  actionRef: React.MutableRefObject<ActionType | undefined>;
+  modelConfig?: ModalProps;
+  request?: (params: any) => Promise<Partial<RequestData<T>>>;
+};
+
+const BaseCrud = <T extends Record<string, any>>(props: Props<T>) => {
+  const intl = useIntl();
+
+  const {
+    columns,
+    service,
+    title,
+    menu,
+    schema,
+    defaultParams,
+    actionRef,
+    schemaConfig,
+    modelConfig,
+    request,
+  } = props;
+
+  return (
+    <>
+      <ProTable<T>
+        columns={columns}
+        actionRef={actionRef}
+        options={{ fullScreen: true }}
+        request={request || (async (params = {}) => service.query(params))}
+        editable={{
+          type: 'multiple',
+        }}
+        rowKey="id"
+        search={{
+          labelWidth: 'auto',
+        }}
+        form={{
+          syncToUrl: false,
+        }}
+        pagination={{
+          pageSize: 10,
+        }}
+        dateFormatter="string"
+        headerTitle={title}
+        defaultParams={defaultParams}
+        toolBarRender={() => [
+          <Button onClick={CurdModel.add} key="button" icon={<PlusOutlined />} type="primary">
+            {intl.formatMessage({
+              id: 'pages.data.option.add',
+              defaultMessage: '新增',
+            })}
+          </Button>,
+          menu && (
+            <Dropdown key="menu" overlay={menu}>
+              <Button>
+                <EllipsisOutlined />
+              </Button>
+            </Dropdown>
+          ),
+        ]}
+      />
+      <Save
+        reload={() => actionRef.current?.reload()}
+        service={service}
+        schema={schema}
+        schemaConfig={schemaConfig}
+        modelConfig={modelConfig}
+      />
+    </>
+  );
+};
+
+export default BaseCrud;

+ 112 - 0
src/components/BaseCrud/save/index.tsx

@@ -0,0 +1,112 @@
+import React, { useEffect, useState } from 'react';
+import { message, Modal } from 'antd';
+import {
+  Form,
+  FormItem,
+  Input,
+  Password,
+  Upload,
+  PreviewText,
+  FormTab,
+  Select,
+  ArrayTable,
+  Switch,
+  FormGrid,
+} from '@formily/antd';
+import { createForm } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+import * as ICONS from '@ant-design/icons';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import type { ISchema } from '@formily/json-schema';
+import type BaseService from '@/utils/BaseService';
+import { Store } from 'jetlinks-store';
+import SystemConst from '@/utils/const';
+import { CurdModel } from '@/components/BaseCrud/model';
+import type { ISchemaFieldProps } from '@formily/react/lib/types';
+import type { ModalProps } from 'antd/lib/modal/Modal';
+
+interface Props<T> {
+  schema: ISchema;
+  service: BaseService<T>;
+  reload: () => void;
+  schemaConfig?: ISchemaFieldProps;
+  modelConfig?: ModalProps;
+}
+
+const Save = <T extends Record<string, any>>(props: Props<T>) => {
+  const intl = useIntl();
+  const { service, schema, reload, schemaConfig, modelConfig } = props;
+
+  const [visible, setVisible] = useState<boolean>(false);
+  const [current, setCurrent] = useState<T>();
+  const [model, setModel] = useState<'edit' | 'add' | 'preview'>('edit');
+
+  useEffect(() => {
+    const visibleSubscription = Store.subscribe(SystemConst.BASE_CURD_MODAL_VISIBLE, setVisible);
+    const dataSubscription = Store.subscribe(SystemConst.BASE_CURD_CURRENT, setCurrent);
+    const modelSubscription = Store.subscribe(SystemConst.BASE_CURD_MODEL, setModel);
+    return () => {
+      visibleSubscription.unsubscribe();
+      dataSubscription.unsubscribe();
+      modelSubscription.unsubscribe();
+    };
+  }, [current]);
+
+  const form = createForm({
+    validateFirst: true,
+    initialValues: current,
+  });
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      FormTab,
+      Input,
+      Password,
+      Upload,
+      Select,
+      ArrayTable,
+      Switch,
+      FormGrid,
+    },
+    scope: {
+      icon(name: any) {
+        return React.createElement(ICONS[name]);
+      },
+    },
+  });
+
+  const save = async () => {
+    const values: T = await form.submit();
+    await service.update(values);
+    message.success(
+      intl.formatMessage({
+        id: 'pages.data.option.success',
+        defaultMessage: '操作成功',
+      }),
+    );
+    CurdModel.close();
+    reload();
+  };
+
+  return (
+    <Modal
+      title={intl.formatMessage({
+        id: `pages.data.option.${model}`,
+        defaultMessage: '编辑',
+      })}
+      visible={visible}
+      onCancel={CurdModel.close}
+      onOk={save}
+      {...modelConfig}
+    >
+      <PreviewText.Placeholder value="-">
+        <Form form={form} labelCol={5} wrapperCol={16}>
+          <SchemaField schema={schema} {...schemaConfig} />
+        </Form>
+      </PreviewText.Placeholder>
+    </Modal>
+  );
+};
+
+export default Save;

+ 5 - 5
src/components/Footer/index.tsx

@@ -6,23 +6,23 @@ export default () => {
   const intl = useIntl();
   const defaultMessage = intl.formatMessage({
     id: 'app.copyright.produced',
-    defaultMessage: '蚂蚁集团体验技术部出品',
+    defaultMessage: 'Jetlinks Team',
   });
 
   return (
     <DefaultFooter
-      copyright={`2020 ${defaultMessage}`}
+      copyright={`2021 ${defaultMessage}`}
       links={[
         {
-          key: 'Ant Design Pro',
-          title: 'Ant Design Pro',
+          key: 'Jetlinks',
+          title: 'Jetlinks',
           href: 'https://pro.ant.design',
           blankTarget: true,
         },
         {
           key: 'github',
           title: <GithubOutlined />,
-          href: 'https://github.com/ant-design/ant-design-pro',
+          href: 'https://github.com/jetlinks',
           blankTarget: true,
         },
         {

+ 4 - 4
src/components/NoticeIcon/index.tsx

@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
 import { Tag, message } from 'antd';
 import { groupBy } from 'lodash';
 import moment from 'moment';
-import { useModel, useRequest } from 'umi';
+import { useRequest } from 'umi';
 import { getNotices } from '@/services/ant-design-pro/api';
 
 import NoticeIcon from './NoticeIcon';
@@ -71,8 +71,8 @@ const getUnreadData = (noticeData: Record<string, API.NoticeIconItem[]>) => {
 };
 
 const NoticeIconView = () => {
-  const { initialState } = useModel('@@initialState');
-  const { currentUser } = initialState || {};
+  // const { initialState } = useModel('@@initialState');
+  // const { currentUser } = initialState || {};
   const [notices, setNotices] = useState<API.NoticeIconItem[]>([]);
   const { data } = useRequest(getNotices);
 
@@ -111,7 +111,7 @@ const NoticeIconView = () => {
   return (
     <NoticeIcon
       className={styles.action}
-      count={currentUser && currentUser.unreadCount}
+      count={10}
       onItemClick={(item) => {
         changeReadState(item.id!);
       }}

+ 23 - 11
src/components/RightContent/AvatarDropdown.tsx

@@ -7,6 +7,7 @@ import HeaderDropdown from '../HeaderDropdown';
 import styles from './index.less';
 import { outLogin } from '@/services/ant-design-pro/api';
 import type { MenuInfo } from 'rc-menu/lib/interface';
+import { useIntl } from '@@/plugin-locale/localeExports';
 
 export type GlobalHeaderRightProps = {
   menu?: boolean;
@@ -33,17 +34,19 @@ const loginOut = async () => {
 const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
   const { initialState, setInitialState } = useModel('@@initialState');
 
+  const intl = useIntl();
+
   const onMenuClick = useCallback(
-    (event: MenuInfo) => {
+    async (event: MenuInfo) => {
       const { key } = event;
-      if (key === 'logout') {
-        setInitialState((s) => ({ ...s, currentUser: undefined }));
-        loginOut();
+      if (key === 'logout' && initialState) {
+        setInitialState({ ...initialState, currentUser: undefined });
+        await loginOut();
         return;
       }
       history.push(`/account/${key}`);
     },
-    [setInitialState],
+    [initialState, setInitialState],
   );
 
   const loading = (
@@ -64,7 +67,7 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
 
   const { currentUser } = initialState;
 
-  if (!currentUser || !currentUser.name) {
+  if (!currentUser || !currentUser.user.name) {
     return loading;
   }
 
@@ -73,28 +76,37 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
       {menu && (
         <Menu.Item key="center">
           <UserOutlined />
-          个人中心
+          {intl.formatMessage({
+            id: 'component.globalHeader.person.center',
+            defaultMessage: '个人中心',
+          })}
         </Menu.Item>
       )}
       {menu && (
         <Menu.Item key="settings">
           <SettingOutlined />
-          个人设置
+          {intl.formatMessage({
+            id: 'component.globalHeader.person.setting',
+            defaultMessage: '个人设置',
+          })}
         </Menu.Item>
       )}
       {menu && <Menu.Divider />}
 
       <Menu.Item key="logout">
         <LogoutOutlined />
-        退出登录
+        {intl.formatMessage({
+          id: 'component.globalHeader.logout',
+          defaultMessage: '退出登录',
+        })}
       </Menu.Item>
     </Menu>
   );
   return (
     <HeaderDropdown overlay={menuHeaderDropdown}>
       <span className={`${styles.action} ${styles.account}`}>
-        <Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
-        <span className={`${styles.name} anticon`}>{currentUser.name}</span>
+        <Avatar size="small" className={styles.avatar} src={currentUser.user.avatar} alt="avatar" />
+        <span className={`${styles.name} anticon`}>{currentUser.user.name}</span>
       </span>
     </HeaderDropdown>
   );

+ 2 - 2
src/components/RightContent/index.tsx

@@ -30,7 +30,7 @@ const GlobalHeaderRight: React.FC = () => {
         options={[
           { label: <a href="https://umijs.org/zh/guide/umi-ui.html">umi ui</a>, value: 'umi ui' },
           {
-            label: <a href="next.ant.design">Ant Design</a>,
+            label: <a href="https://ant.design/">Ant Design</a>,
             value: 'Ant Design',
           },
           {
@@ -54,7 +54,7 @@ const GlobalHeaderRight: React.FC = () => {
       >
         <QuestionCircleOutlined />
       </span>
-      <Avatar />
+      <Avatar menu={true} />
       <SelectLang className={styles.action} />
     </Space>
   );

+ 3 - 0
src/locales/en-US/globalHeader.ts

@@ -1,4 +1,7 @@
 export default {
+  'component.globalHeader.person.center': 'Personal Center',
+  'component.globalHeader.person.setting': 'Personal settings',
+  'component.globalHeader.logout': 'Log out',
   'component.globalHeader.search': 'Search',
   'component.globalHeader.search.example1': 'Search example 1',
   'component.globalHeader.search.example2': 'Search example 2',

+ 3 - 0
src/locales/zh-CN/globalHeader.ts

@@ -1,4 +1,7 @@
 export default {
+  'component.globalHeader.person.center': '个人中心',
+  'component.globalHeader.person.setting': '个人设置',
+  'component.globalHeader.logout': '退出登录',
   'component.globalHeader.search': '站内搜索',
   'component.globalHeader.search.example1': '搜索提示一',
   'component.globalHeader.search.example2': '搜索提示二',

+ 44 - 0
src/utils/BaseService.ts

@@ -0,0 +1,44 @@
+// import request from "@/utils/request";
+import Token from '@/utils/token';
+import SystemConst from '@/utils/const';
+import { request } from 'umi';
+
+interface IBaseService<T> {
+  query: (params: any) => Promise<any>;
+  save: (data: T) => Promise<any>;
+  remove: (id: string) => Promise<any>;
+}
+
+class BaseService<T> implements IBaseService<T> {
+  protected uri: string;
+
+  protected headers = {
+    'X-Access-Token': Token.get(),
+  };
+
+  constructor(uri: string) {
+    this.uri = `/${SystemConst.API_BASE}/${uri}`;
+  }
+
+  query(params: any): Promise<any> {
+    return request(`${this.uri}/_query/`, { params, method: 'GET' });
+  }
+
+  queryNoPaging(params: any): Promise<any> {
+    return request(`${this.uri}/_query/no-paging?paging=false`, { params, method: 'GET' });
+  }
+
+  remove(id: string): Promise<any> {
+    return request(`${this.uri}/${id}`, { method: 'DELETE' });
+  }
+
+  save(data: T): Promise<any> {
+    return request(this.uri, { data, method: 'POST' });
+  }
+
+  update(data: Partial<T>): Promise<any> {
+    return request(this.uri, { data, method: 'PATCH' });
+  }
+}
+
+export default BaseService;

+ 17 - 0
src/utils/const.ts

@@ -0,0 +1,17 @@
+class SystemConst {
+  static API_BASE = 'jetlinks';
+
+  static SYSTEM_NAME = 'Jetlinks';
+
+  static LOGIN = 'LOGIN-STATUS';
+
+  static DOC_URL = 'http://doc.jetlinks.cn';
+
+  static BASE_CURD_MODAL_VISIBLE = 'BASE_CURD_MODAL_VISIBLE';
+
+  static BASE_CURD_CURRENT = 'BASE_CURD_CURRENT';
+
+  static BASE_CURD_MODEL = 'BASE_CURD_MODEL';
+}
+
+export default SystemConst;

+ 64 - 0
src/utils/encodeQuery.ts

@@ -0,0 +1,64 @@
+export default function encodeQuery(params: any) {
+  if (!params) return {};
+  const queryParam = {
+    // pageIndex: 0,
+    current: params.current,
+  };
+  const { terms, sorts } = params;
+  Object.keys(params).forEach((key: string) => {
+    if (key === 'terms') {
+      let index = 0;
+      if (!terms) return;
+      Object.keys(terms).forEach((k: string) => {
+        if (
+          !(
+            terms[k] === '' ||
+            terms[k] === undefined ||
+            terms[k].length === 0 ||
+            terms[k] === {} ||
+            terms[k] === null
+          )
+        ) {
+          if (k.indexOf('$LIKE') > -1 && terms[k].toString().indexOf('%') === -1) {
+            terms[k] = `%${terms[k]}%`;
+          }
+          if (k.indexOf('$IN') > -1) {
+            terms[k] = terms[k].toString();
+          } else if (k.indexOf('$START') > -1) {
+            terms[k] = `%${terms[k]}`;
+          } else if (k.indexOf('$END') > -1) {
+            terms[k] = `${terms[k]}%`;
+          }
+          if (k.indexOf('@') > -1) {
+            const temp = k.split('@');
+            // eslint-disable-next-line prefer-destructuring
+            queryParam[`terms[${index}].column`] = temp[0];
+            // eslint-disable-next-line prefer-destructuring
+            queryParam[`terms[${index}].type`] = temp[1];
+          } else {
+            queryParam[`terms[${index}].column`] = k;
+          }
+          queryParam[`terms[${index}].value`] = terms[k];
+          index += 1;
+        }
+      });
+    } else if (key === 'sorts') {
+      // 当前Ant Design排序只支持单字段排序
+      if (!sorts) return;
+      Object.keys(sorts).forEach((s, index) => {
+        queryParam[`sorts[${index}].name`] = s;
+        queryParam[`sorts[${index}].order`] = sorts[s].replace('end', '');
+      });
+      // if (Object.keys(sorts).length > 0) {
+      //     queryParam[`sorts[0].name`] = sorts.field;
+      //     queryParam[`sorts[0].order`] = (sorts.order || '').replace('end', '');
+      // }
+    } else {
+      queryParam[key] = params[key];
+    }
+  });
+
+  // queryParam.pageIndex = current - 1;
+
+  return queryParam;
+}