Procházet zdrojové kódy

Merge branch 'master' of lizhiqi/yongxu-web into master

lizhiqi před 2 roky
rodič
revize
185ee99144

+ 1 - 1
config/defaultSettings.ts

@@ -14,7 +14,7 @@ const Settings: LayoutSettings & {
   colorWeak: false,
   title: '永续绿建web管理平台',
   pwa: false,
-  logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
+  logo: '/logo.png',
   iconfontUrl: '',
   menu: {
     locale: false, // 关闭菜单国际化

+ 5 - 0
config/proxy.ts

@@ -16,6 +16,11 @@ export default {
       // 依赖 origin 的功能可能需要这个,比如 cookie
       changeOrigin: true,
     },
+    '/web/': {
+      // 要代理的地址
+      target: 'http://yongxu.yehaoji.cn:8198',
+      changeOrigin: true,
+    },
   },
   test: {
     '/api/': {

+ 1 - 18
config/routes.ts

@@ -15,27 +15,10 @@
   },
   {
     path: '/welcome',
-    name: 'welcome',
+    name: '首页',
     icon: 'smile',
     component: './Welcome',
   },
-  {
-    path: '/admin',
-    name: 'admin',
-    icon: 'crown',
-    access: 'canAdmin',
-    routes: [
-      {
-        path: '/admin/sub-page',
-        name: 'sub-page',
-        icon: 'smile',
-        component: './Welcome',
-      },
-      {
-        component: './404',
-      },
-    ],
-  },
   {
     name: 'list.table-list',
     icon: 'table',

+ 2 - 0
package.json

@@ -54,6 +54,7 @@
     "@umijs/route-utils": "^2.0.0",
     "antd": "^4.23.3",
     "classnames": "^2.3.0",
+    "js-md5": "^0.7.3",
     "lodash": "^4.17.0",
     "moment": "^2.29.0",
     "omit.js": "^2.0.2",
@@ -72,6 +73,7 @@
     "@types/express": "^4.17.0",
     "@types/history": "^4.7.0",
     "@types/jest": "^26.0.0",
+    "@types/js-md5": "^0.4.3",
     "@types/lodash": "^4.14.0",
     "@types/react": "^17.0.0",
     "@types/react-dom": "^17.0.0",

binární
public/assets/login-left.png


binární
public/assets/logo.png


+ 41 - 2
src/app.tsx

@@ -3,6 +3,7 @@ import RightContent from '@/components/RightContent';
 import { BookOutlined, LinkOutlined } from '@ant-design/icons';
 import type { Settings as LayoutSettings } from '@ant-design/pro-components';
 import { PageLoading, SettingDrawer } from '@ant-design/pro-components';
+// import { message, notification } from 'antd';
 import type { RunTimeLayoutConfig } from 'umi';
 import { history, Link } from 'umi';
 import defaultSettings from '../config/defaultSettings';
@@ -27,8 +28,10 @@ export async function getInitialState(): Promise<{
 }> {
   const fetchUserInfo = async () => {
     try {
-      const msg = await queryCurrentUser();
-      return msg.data;
+      const msg = await queryCurrentUser(localStorage.getItem('token'));
+      return {
+        name: msg.user_name,
+      };
     } catch (error) {
       history.push(loginPath);
     }
@@ -105,3 +108,39 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
     ...initialState?.settings,
   };
 };
+// const addToken = async (url: any, options: any) => {
+//   // 此处为拦截器,每次发送请求之前判断能否取到token
+//   if (localStorage.getItem('token')) {
+//     const headers = {
+//       Authorization: `${localStorage.getItem('token')}`,
+//     };
+//     return {
+//       url,
+//       options: { ...options, headers },
+//     };
+//   }
+// };
+//
+// const MainResponseInterceptors = (response: { status: number }, opts: any) => {
+//   if (response.status === 401) {
+//     message.warn('帐号登录过期,请重新登录');
+//     history.push(loginPath);
+//   }
+//   return response;
+// };
+// export const request = {
+//   errorHandler: (error: { response: any }) => {
+//     const { response } = error;
+//
+//     if (!response) {
+//       notification.error({
+//         description: '您的网络发生异常,无法连接服务器',
+//         message: '网络异常',
+//       });
+//     }
+//
+//     throw error;
+//   },
+//   requestInterceptors: [addToken],
+//   responseInterceptors: [MainResponseInterceptors],
+// };

+ 60 - 36
src/pages/user/Login/index.less

@@ -1,28 +1,63 @@
 @import (reference) '~antd/es/style/themes/index';
 
 .container {
-  display: flex;
-  flex-direction: column;
-  height: 100vh;
-  overflow: auto;
-  background: @layout-body-background;
-}
+  height: 100%;
+  .loginBox {
+    .login_bg {
+      position: absolute;
+      top: 0;
+      right: 0;
+    }
+    .main {
+      position: absolute;
+      top: 20%;
+      left: 12%;
+      width: 500px;
+      padding: 60px 50px;
+      overflow: hidden;
+      background: #f3f7ff;
+      border-radius: 10px;
+      .title {
+        margin-bottom: 20px;
+        text-align: center;
+        .logoStyle {
+          width: 170px;
+        }
+      }
 
-.lang {
-  width: 100%;
-  height: 40px;
-  line-height: 44px;
-  text-align: right;
-  :global(.ant-dropdown-trigger) {
-    margin-right: 24px;
-  }
-}
+      .formItem {
+        display: block;
+        height: 100px;
+        margin-bottom: 0;
+      }
 
-.content {
-  flex: 1;
-  padding: 32px 0;
-}
+      .formItemInput {
+        height: 46px;
+        border-radius: 5px;
+      }
 
+      .captcha {
+        position: absolute;
+        top: 0;
+        right: 0;
+        height: 44px;
+      }
+      .submit {
+        display: block;
+        width: 100%;
+        height: 50px;
+        margin-top: 10px;
+        color: #fff;
+        font-size: 20px;
+        background: #4e94fe;
+        border: 0 none;
+        border-radius: 25px;
+        box-shadow: 0 4px 11px 0 rgba(204, 204, 204, 0.46);
+        cursor: pointer;
+      }
+    }
+  }
+}
 @media (min-width: @screen-md-min) {
   .container {
     background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
@@ -30,21 +65,10 @@
     background-position: center 110px;
     background-size: 100%;
   }
-
-  .content {
-    padding: 32px 0 24px;
-  }
 }
-
-// .icon {
-//   margin-left: 8px;
-//   color: rgba(0, 0, 0, 0.2);
-//   font-size: 24px;
-//   vertical-align: middle;
-//   cursor: pointer;
-//   transition: color 0.3s;
-
-//   &:hover {
-//     color: @primary-color;
-//   }
-// }
+.footer {
+  position: absolute;
+  bottom: 0;
+  left: 45%;
+  background: transparent;
+}

+ 119 - 252
src/pages/user/Login/index.tsx

@@ -1,286 +1,153 @@
-import Footer from '@/components/Footer';
 import { login } from '@/services/ant-design-pro/api';
-// import { getFakeCaptcha } from '@/services/ant-design-pro/login';
-import {
-  AlipayCircleOutlined,
-  LockOutlined,
-  // MobileOutlined,
-  TaobaoCircleOutlined,
-  UserOutlined,
-  WeiboCircleOutlined,
-} from '@ant-design/icons';
-import {
-  LoginForm,
-  // ProFormCaptcha,
-  ProFormCheckbox,
-  ProFormText,
-} from '@ant-design/pro-components';
-import { Alert, message } from 'antd';
-import React, { useState } from 'react';
-import { FormattedMessage, history, SelectLang, useIntl, useModel } from 'umi';
+import { getCaptchaId } from '@/services/ant-design-pro/login';
+import { DefaultFooter } from '@ant-design/pro-components';
+import { Button, Form, Input, message } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { history, useModel } from 'umi';
 import styles from './index.less';
+import md5 from 'js-md5';
 
-const LoginMessage: React.FC<{
-  content: string;
-}> = ({ content }) => (
-  <Alert
-    style={{
-      marginBottom: 24,
-    }}
-    message={content}
-    type="error"
-    showIcon
-  />
-);
+const FormItem = Form.Item;
+
+/**
+ *
+ * @returns footer
+ */
+const Footer: React.FC = () => {
+  const currentYear = new Date().getFullYear();
+  const defaultMessage = '永续绿建web管理平台';
+  return <DefaultFooter className={styles.footer} copyright={`${currentYear} ${defaultMessage}`} />;
+};
 
 const Login: React.FC = () => {
-  const [userLoginState, setUserLoginState] = useState<API.LoginResult>({});
-  const [type] = useState<string>('account');
   const { initialState, setInitialState } = useModel('@@initialState');
+  const [captcha_id, setCaptcha_id] = useState('');
+
+  /**
+   * 获取验证码id
+   */
+  const fetchCaptcha = () => {
+    getCaptchaId().then(async (res) => {
+      setCaptcha_id(res.captcha_id);
+    });
+  };
 
-  const intl = useIntl();
+  /**
+   * 初始化
+   */
+  useEffect(() => {
+    fetchCaptcha();
+  }, []);
 
+  /**
+   * 获取账号用户信息
+   */
   const fetchUserInfo = async () => {
     const userInfo = await initialState?.fetchUserInfo?.();
     if (userInfo) {
-      await setInitialState((s) => ({
-        ...s,
-        currentUser: userInfo,
-      }));
+      await setInitialState((s: any) => ({ ...s, currentUser: userInfo }));
     }
   };
 
+  /**
+   * 登录
+   * @param values
+   * @returns
+   */
   const handleSubmit = async (values: API.LoginParams) => {
     try {
-      // 登录
-      const msg = await login({ ...values, type });
-      if (msg.status === 'ok') {
-        const defaultLoginSuccessMessage = intl.formatMessage({
-          id: 'pages.login.success',
-          defaultMessage: '登录成功!',
-        });
-        message.success(defaultLoginSuccessMessage);
+      const body = {
+        ...values,
+        password: md5(values.password),
+        captcha_id,
+      };
+      const msg = await login(body);
+      if (!msg?.error) {
+        if (localStorage.getItem('token') == null || localStorage.getItem('token') == undefined) {
+          localStorage.setItem('token', '');
+        }
+        localStorage.setItem('token', `${msg.token_type} ${msg.access_token}`);
+        message.success(`欢迎登录系统`);
         await fetchUserInfo();
-        /** 此方法会跳转到 redirect 参数所在的位置 */
+        history.push('/');
         if (!history) return;
         const { query } = history.location;
         const { redirect } = query as { redirect: string };
         history.push(redirect || '/');
-        return;
+      } else {
+        message.error(msg?.error?.message || '登录失败');
       }
-      console.log(msg);
-      // 如果失败去设置用户错误信息
-      setUserLoginState(msg);
     } catch (error) {
-      const defaultLoginFailureMessage = intl.formatMessage({
-        id: 'pages.login.failure',
-        defaultMessage: '登录失败,请重试!',
-      });
-      message.error(defaultLoginFailureMessage);
+      // const _ = fetchCaptcha();
+      message.success(`登录失败`);
     }
   };
-  const { status, type: loginType } = userLoginState;
 
   return (
     <div className={styles.container}>
-      <div className={styles.lang} data-lang>
-        {SelectLang && <SelectLang />}
-      </div>
-      <div className={styles.content}>
-        <LoginForm
-          logo={<img alt="logo" src="/logo.svg" />}
-          title="永续绿建后台管理系统"
-          subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
-          initialValues={{
-            autoLogin: true,
-          }}
-          actions={[
-            <FormattedMessage
-              key="loginWith"
-              id="pages.login.loginWith"
-              defaultMessage="其他登录方式"
-            />,
-            <AlipayCircleOutlined key="AlipayCircleOutlined" className={styles.icon} />,
-            <TaobaoCircleOutlined key="TaobaoCircleOutlined" className={styles.icon} />,
-            <WeiboCircleOutlined key="WeiboCircleOutlined" className={styles.icon} />,
-          ]}
-          onFinish={async (values) => {
-            await 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: '手机号: XXX',
-              })}
-              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="手机号格式错误!"
-                      />
-                    ),
-                  },
-                ]}
+      <div className={styles.loginBox}>
+        <img alt="请登录" src="/assets/login-left.png" className={styles.login_bg} />
+        <div className={styles.main}>
+          <div className={styles.title}>
+            <img src="/assets/logo.png" alt="logo" className={styles.logoStyle} />
+          </div>
+          <Form layout="vertical" onFinish={handleSubmit}>
+            <FormItem
+              rules={[{ required: true, message: '请输入用户名!' }]}
+              className={styles.formItem}
+              name="user_name"
+              label="用户名"
+            >
+              <Input
+                className={styles.formItemInput}
+                size="large"
+                placeholder="请输入用户名"
+                allowClear
               />
-              <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');
-                }}
+            </FormItem>
+            <FormItem
+              className={styles.formItem}
+              name="password"
+              label="密码"
+              rules={[{ required: true, message: '请输入密码!' }]}
+            >
+              <Input
+                className={styles.formItemInput}
+                size="large"
+                type="password"
+                placeholder="请输入密码"
+                allowClear
               />
-            </>
-          )} */}
-          <div
-            style={{
-              marginBottom: 24,
-            }}
-          >
-            <ProFormCheckbox noStyle name="autoLogin">
-              <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
-            </ProFormCheckbox>
-            <a
-              style={{
-                float: 'right',
-              }}
+            </FormItem>
+            <FormItem
+              className={styles.formItem}
+              name="captcha_code"
+              label="验证码"
+              rules={[{ required: true, message: '请输入验证码!' }]}
             >
-              <FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />
-            </a>
-          </div>
-        </LoginForm>
+              <Input
+                className={styles.formItemInput}
+                size="large"
+                suffix={
+                  <img
+                    className={styles.captcha}
+                    onClick={() => {
+                      fetchCaptcha();
+                    }}
+                    src={`/web/v1/login/captcha?id=${captcha_id}`}
+                    alt="验证码"
+                  />
+                }
+                placeholder="请输入验证码"
+              />
+            </FormItem>
+            <div>
+              <Button className={styles.submit} htmlType="submit">
+                登录
+              </Button>
+            </div>
+          </Form>
+        </div>
       </div>
       <Footer />
     </div>

+ 8 - 7
src/services/ant-design-pro/api.ts

@@ -2,17 +2,18 @@
 /* eslint-disable */
 import { request } from 'umi';
 
-/** 获取当前的用户 GET /api/currentUser */
-export async function currentUser(options?: { [key: string]: any }) {
+/** 获取当前的用户信息 */
+export async function currentUser(param: any) {
   return request<{
+    user_name: any;
     data: API.CurrentUser;
-  }>('/api/currentUser', {
+  }>('/web/v1/users/current', {
     method: 'GET',
-    ...(options || {}),
+    headers: { Authorization: param },
   });
 }
 
-/** 退出登录接口 POST /api/login/outLogin */
+/** 退出登录接口 POST */
 export async function outLogin(options?: { [key: string]: any }) {
   return request<Record<string, any>>('/api/login/outLogin', {
     method: 'POST',
@@ -20,9 +21,9 @@ export async function outLogin(options?: { [key: string]: any }) {
   });
 }
 
-/** 登录接口 POST /api/login/account */
+/** 登录接口 POST */
 export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
-  return request<API.LoginResult>('/api/login/account', {
+  return request<API.LoginResult>('/web/v1/login', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json',

+ 17 - 0
src/services/ant-design-pro/login.ts

@@ -19,3 +19,20 @@ export async function getFakeCaptcha(
     ...(options || {}),
   });
 }
+
+/**
+ * 获取验证码id
+ * @returns
+ */
+export async function getCaptchaId() {
+  return request(`/web/v1/login/captchaid`);
+}
+
+/**
+ * 获取验证码图片
+ * @param params
+ * @returns
+ */
+export async function getCaptcha(params: any) {
+  return request(`/web/v1/login/captcha?id=${params}`);
+}

+ 5 - 1
src/services/ant-design-pro/typings.d.ts

@@ -24,6 +24,9 @@ declare namespace API {
   };
 
   type LoginResult = {
+    token_type: string;
+    error: any;
+    access_token: LoginResult;
     status?: string;
     type?: string;
     currentAuthority?: string;
@@ -63,7 +66,8 @@ declare namespace API {
 
   type LoginParams = {
     username?: string;
-    password?: string;
+    password?: any;
+    captcha_id?: string;
     autoLogin?: boolean;
     type?: string;
   };