瀏覽代碼

封装Login页面相关组件

xwlyy 5 年之前
父節點
當前提交
07e2de5ba3

+ 11 - 6
package-lock.json

@@ -1527,7 +1527,7 @@
                 },
                 "parse-json": {
                     "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+                    "resolved": "http://registry.npm.taobao.org/parse-json/download/parse-json-4.0.0.tgz",
                     "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
                     "dev": true,
                     "requires": {
@@ -3061,7 +3061,7 @@
                 },
                 "ms": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz",
+                    "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
                     "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 },
@@ -6189,7 +6189,7 @@
                 },
                 "ms": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
                     "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 },
@@ -6502,7 +6502,7 @@
                 },
                 "ms": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
                     "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 }
@@ -15337,6 +15337,11 @@
                 "tinycolor2": "^1.1.2"
             }
         },
+        "vue-create-context": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npm.taobao.org/vue-create-context/download/vue-create-context-1.1.0.tgz",
+            "integrity": "sha1-j486khRTSRvbXmWqb/dIvljCegE="
+        },
         "vue-eslint-parser": {
             "version": "2.0.3",
             "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",
@@ -15679,7 +15684,7 @@
             "dependencies": {
                 "ansi-regex": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+                    "resolved": "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz",
                     "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
                     "dev": true
                 },
@@ -15841,7 +15846,7 @@
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                    "resolved": "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz",
                     "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
                     "dev": true,
                     "requires": {

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
         "resize-detector": "^0.2.0",
         "vue": "^2.6.6",
         "vue-color": "^2.7.0",
+        "vue-create-context": "^1.1.0",
         "vue-highlightjs": "^1.3.3",
         "vue-i18n": "^8.11.1",
         "vue-router": "^3.0.1",

+ 6 - 0
src/components/Login/LoginContext.vue

@@ -0,0 +1,6 @@
+<script>
+import { createContext } from "vue-create-context";
+
+const LoginContext = createContext();
+export default LoginContext;
+</script>

+ 202 - 0
src/components/Login/LoginItem.vue

@@ -0,0 +1,202 @@
+<script>
+import { Form, Input, Button, Row, Col, Icon } from "ant-design-vue";
+import omit from "lodash/omit";
+import LoginContext from "./LoginContext";
+import ItemMap from "./map";
+
+// const FormItem = Form.Item;
+
+// FormItem中的decoratorOption方法有bug,已提交pr到ant-design-vue
+// 11行到27行为临时解决方案
+import cloneDeep from "lodash/cloneDeep";
+import warning from "ant-design-vue/es/_util/warning";
+const FormItem = cloneDeep(Form.Item);
+FormItem.methods.decoratorOption = vnode => {
+  if (vnode.data && vnode.data.directives) {
+    const directive = find(vnode.data.directives, ["name", "decorator"]);
+    warning(
+      !directive || (directive && Array.isArray(directive.value)),
+      `Invalid directive: type check failed for directive "decorator". Expected Array, got ${typeof (directive // directive可能为undefined
+        ? directive.value
+        : directive)}. At ${vnode.tag}.`
+    );
+    return directive ? directive.value : null;
+  } else {
+    return null;
+  }
+};
+
+const WrapFormItem = {
+  data() {
+    return {
+      count: 0
+    };
+  },
+  props: {
+    onChange: Function,
+    defaultValue: String,
+    rules: Array,
+    updateActive: Function,
+    form: Object,
+    customprops: Object,
+    name: String,
+    placeholder: String,
+    type: String,
+    getCaptchaButtonText: String,
+    getCaptchaSecondText: String,
+    onGetCaptcha: Function,
+    countDown: Number
+  },
+  mounted() {
+    const { updateActive, name } = this.$props;
+    if (updateActive) {
+      updateActive(name);
+    }
+  },
+  beforeDestroy() {
+    clearInterval(this.interval);
+  },
+  methods: {
+    getFormItemOptions({ onChange, defaultValue, customprops, rules }) {
+      const options = {
+        rules: rules || customprops.rules
+      };
+      if (onChange) {
+        options.onChange = onChange;
+      }
+      if (defaultValue) {
+        options.initialValue = defaultValue;
+      }
+      return options;
+    },
+    getCaptcha() {
+      const { onGetCaptcha } = this.$props;
+      const result = onGetCaptcha ? onGetCaptcha() : null;
+      if (result === false) {
+        return;
+      }
+      if (result instanceof Promise) {
+        result.then(this.runGetCaptchaCountDown);
+      } else {
+        this.runGetCaptchaCountDown();
+      }
+    },
+    runGetCaptchaCountDown() {
+      const { countDown } = this.$props;
+      this.count = countDown || 59;
+      this.interval = setInterval(() => {
+        this.count -= 1;
+        if (this.count === 0) {
+          clearInterval(this.interval);
+        }
+      }, 1000);
+    }
+  },
+  render() {
+    const { count } = this.$data;
+
+    // 这么写是为了防止restProps中 带入 onChange, defaultValue, rules, updateActive props
+    const {
+      // eslint-disable-next-line no-unused-vars
+      onChange,
+      // eslint-disable-next-line no-unused-vars
+      defaultValue,
+      // eslint-disable-next-line no-unused-vars
+      rules,
+      // eslint-disable-next-line no-unused-vars
+      updateActive,
+      form: { getFieldDecorator },
+      customprops,
+      name,
+      type,
+      getCaptchaButtonText,
+      getCaptchaSecondText,
+      ...restProps
+    } = this.$props;
+
+    customprops.prefix = (
+      <Icon type={customprops.prefixType} class="prefixIcon" />
+    );
+
+    // get getFieldDecorator props
+    const options = this.getFormItemOptions(this.$props);
+
+    const otherProps = {};
+    Object.keys(restProps).forEach(key => {
+      if (restProps[key]) otherProps[key] = restProps[key];
+    });
+
+    if (type === "Captcha") {
+      const inputProps = omit(otherProps, ["onGetCaptcha", "countDown"]);
+      return (
+        <FormItem>
+          <Row gutter={8}>
+            <Col span={16}>
+              {getFieldDecorator(name, options)(
+                <Input {...{ props: { ...customprops, ...inputProps } }} />
+              )}
+            </Col>
+            <Col span={8}>
+              <Button
+                disabled={!!count}
+                class="getCaptcha"
+                size="large"
+                onClick={this.getCaptcha}
+              >
+                {count
+                  ? `${count} ${getCaptchaSecondText}`
+                  : getCaptchaButtonText}
+              </Button>
+            </Col>
+          </Row>
+        </FormItem>
+      );
+    }
+
+    return (
+      <FormItem>
+        {getFieldDecorator(name, options)(
+          <Input {...{ props: { ...customprops, ...otherProps } }} />
+        )}
+      </FormItem>
+    );
+  }
+};
+
+const LoginItem = {};
+Object.keys(ItemMap).forEach(key => {
+  const item = ItemMap[key];
+  LoginItem[key] = {
+    props: {
+      onChange: Function,
+      defaultValue: String,
+      name: String,
+      placeholder: String,
+      rules: Array,
+      getCaptchaButtonText: String,
+      getCaptchaSecondText: String,
+      onGetCaptcha: Function,
+      countDown: Number
+    },
+    render() {
+      const props = { props: { ...this.$props } };
+      return (
+        <LoginContext.Consumer>
+          {context => (
+            <WrapFormItem
+              customprops={item.props}
+              rules={item.rules}
+              {...props}
+              type={key}
+              updateActive={context.updateActive}
+              form={context.form}
+            />
+          )}
+        </LoginContext.Consumer>
+      );
+    }
+  };
+});
+
+export default LoginItem;
+</script>

+ 34 - 0
src/components/Login/LoginSubmit.vue

@@ -0,0 +1,34 @@
+<script>
+import { Button, Form } from "ant-design-vue";
+
+const FormItem = Form.Item;
+
+export default {
+  props: {
+    className: String,
+    loading: Boolean
+  },
+  render() {
+    return (
+      <FormItem>
+        <Button
+          size="large"
+          class="submit"
+          type="primary"
+          htmlType="submit"
+          loading={this.loading}
+        >
+          {this.$slots.default}
+        </Button>
+      </FormItem>
+    );
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.submit {
+  width: 100%;
+  margin-top: 24px;
+}
+</style>

+ 43 - 0
src/components/Login/LoginTab.vue

@@ -0,0 +1,43 @@
+<script>
+import { Tabs } from "ant-design-vue";
+import LoginContext from "./LoginContext";
+
+const { TabPane } = Tabs;
+
+const generateId = (() => {
+  let i = 0;
+  return (prefix = "") => {
+    i += 1;
+    return `${prefix}${i}`;
+  };
+})();
+
+export default {
+  typeName: "LoginTab",
+  props: {
+    active: Boolean,
+    destroyInactiveTabPane: Boolean,
+    rootPrefixCls: String,
+    tab: String
+  },
+  data() {
+    return {
+      uniqueId: generateId("login-tab-")
+    };
+  },
+  mounted() {
+    this.tabUtil.addTab(this.uniqueId);
+  },
+  render() {
+    const props = { props: { ...this.$props } };
+    return (
+      <LoginContext.Consumer>
+        {value => {
+          this.tabUtil = value.tabUtil;
+          return <TabPane {...props}>{this.$slots.default}</TabPane>;
+        }}
+      </LoginContext.Consumer>
+    );
+  }
+};
+</script>

+ 124 - 0
src/components/Login/index.vue

@@ -0,0 +1,124 @@
+<script>
+import { Form, Tabs } from "ant-design-vue";
+import LoginSubmit from "./LoginSubmit";
+import LoginTab from "./LoginTab";
+import LoginItem from "./LoginItem";
+import LoginContext from "./LoginContext";
+
+const Login = {
+  props: {
+    defaultActiveKey: {
+      type: String,
+      default: ""
+    },
+    onTabChange: {
+      type: Function,
+      default: () => {}
+    },
+    onSubmit: {
+      type: Function,
+      default: () => {}
+    }
+  },
+  data() {
+    return {
+      type: this.$props.defaultActiveKey,
+      tabs: [],
+      active: {}
+    };
+  },
+  methods: {
+    handleSubmit(e) {
+      e.preventDefault();
+      const { type, form, onSubmit } = this;
+      const activeFileds = this.active[type];
+      form.validateFields(activeFileds, { force: true }, (err, values) => {
+        onSubmit(err, values);
+      });
+    },
+    onSwitch(type) {
+      this.type = type;
+      const { onTabChange } = this;
+      onTabChange(type);
+    },
+    getContext() {
+      const { tabs } = this.$data;
+      const { form } = this.$props;
+      return {
+        tabUtil: {
+          addTab: id => {
+            this.tabs = [...tabs, id];
+          },
+          removeTab: id => {
+            this.tabs = tabs.filter(currentId => currentId !== id);
+          }
+        },
+        form: {
+          ...form
+        },
+        updateActive: activeItem => {
+          const { type, active } = this;
+          if (active[type]) {
+            active[type].push(activeItem);
+          } else {
+            active[type] = [activeItem];
+          }
+          // this.active = active
+          console.log(this.active);
+        }
+      };
+    }
+  },
+  render() {
+    const { tabs, type } = this.$data;
+    const { form } = this.$props;
+    const children = this.$slots.default;
+    const TabChildren = [];
+    const otherChildren = [];
+
+    children.forEach(item => {
+      // console.log("item: ", item);
+      if (!item) {
+        return;
+      }
+
+      if (item.componentOptions && item.componentOptions.tag === "Tab") {
+        TabChildren.push(item);
+      } else {
+        otherChildren.push(item);
+      }
+    });
+    return (
+      <LoginContext.Provider value={this.getContext()}>
+        <div class="login">
+          <Form form={form} onSubmit={this.handleSubmit}>
+            {tabs.length > 0 ? (
+              <div>
+                <Tabs
+                  animated={false}
+                  class={"tabs"}
+                  activeKey={type}
+                  onChange={this.onSwitch}
+                >
+                  {TabChildren}
+                </Tabs>
+                {otherChildren}
+              </div>
+            ) : (
+              children
+            )}
+          </Form>
+        </div>
+      </LoginContext.Provider>
+    );
+  }
+};
+
+Login.Tab = LoginTab;
+Login.Submit = LoginSubmit;
+Object.keys(LoginItem).forEach(item => {
+  Login[item] = LoginItem[item];
+});
+
+export default Form.create()(Login);
+</script>

+ 61 - 0
src/components/Login/map.js

@@ -0,0 +1,61 @@
+export default {
+  UserName: {
+    props: {
+      size: "large",
+      id: "userName",
+      prefixType: "user",
+      placeholder: "admin"
+    },
+    rules: [
+      {
+        required: true,
+        message: "Please enter username!"
+      }
+    ]
+  },
+  Password: {
+    props: {
+      size: "large",
+      prefixType: "lock",
+      type: "password",
+      id: "password",
+      placeholder: "888888"
+    },
+    rules: [
+      {
+        required: true,
+        message: "Please enter password!"
+      }
+    ]
+  },
+  Mobile: {
+    props: {
+      size: "large",
+      prefixType: "mobile",
+      placeholder: "mobile number"
+    },
+    rules: [
+      {
+        required: true,
+        message: "Please enter mobile number!"
+      },
+      {
+        pattern: /^1\d{10}$/,
+        message: "Wrong mobile number format!"
+      }
+    ]
+  },
+  Captcha: {
+    props: {
+      size: "large",
+      prefixType: "mail",
+      placeholder: "captcha"
+    },
+    rules: [
+      {
+        required: true,
+        message: "Please enter Captcha!"
+      }
+    ]
+  }
+};

+ 2 - 0
src/main.js

@@ -39,6 +39,8 @@ import {
 import Authorized from "./components/Authorized";
 import Auth from "./directives/auth";
 import "highlight.js/styles/github.css";
+import ref from "vue-ref";
+Vue.use(ref, { name: "ant-ref" });
 
 Vue.config.productionTip = false;
 

+ 137 - 193
src/views/User/Login.vue

@@ -1,178 +1,131 @@
 <template>
   <div class="main">
-    <div class="login">
-      <a-form :form="form" @submit="handleSubmit">
-        <a-tabs
-          :animated="false"
-          class="tabs"
-          :activeKey="type"
-          @change="onSwitch"
-        >
-          <a-tab-pane key="account" tab="账户密码登录">
-            <VNodes :vnodes="renderMessage('account')" />
-            <a-form-item>
-              <a-input
-                id="userName"
-                type="UserName"
-                size="large"
-                placeholder="用户名:admin or user"
-                v-decorator="[
-                  'userName',
-                  {
-                    rules: [
-                      {
-                        required: true,
-                        message: '请输入用户名!'
-                      }
-                    ]
-                  }
-                ]"
-              >
-                <a-icon slot="prefix" type="user" class="prefixIcon" />
-              </a-input>
-            </a-form-item>
-            <a-form-item>
-              <a-input
-                id="password"
-                type="Password"
-                size="large"
-                placeholder="密码:ant.design"
-                v-decorator="[
-                  'password',
-                  {
-                    rules: [
-                      {
-                        required: true,
-                        message: '请输入密码!'
-                      }
-                    ]
-                  }
-                ]"
-                :onPressEnter="onPressEnter"
-              >
-                <a-icon slot="prefix" type="lock" class="prefixIcon" />
-              </a-input>
-            </a-form-item>
-          </a-tab-pane>
-          <a-tab-pane key="mobile" tab="手机号登录">
-            <VNodes :vnodes="renderMessage('mobile')" />
-            <a-form-item>
-              <a-input
-                size="large"
-                type="Mobile"
-                placeholder="手机号"
-                v-decorator="[
-                  'mobile',
-                  {
-                    rules: [
-                      {
-                        required: true,
-                        message: '请输入手机号!'
-                      },
-                      {
-                        pattern: /^1\d{10}$/,
-                        message: '手机号格式错误!'
-                      }
-                    ]
-                  }
-                ]"
-              >
-                <a-icon slot="prefix" type="mobile" class="prefixIcon" />
-              </a-input>
-            </a-form-item>
-            <a-form-item>
-              <a-row :gutter="8">
-                <a-col :span="16">
-                  <a-input
-                    size="large"
-                    type="Captcha"
-                    placeholder="验证码"
-                    v-decorator="[
-                      'captcha',
-                      {
-                        rules: [
-                          {
-                            required: true,
-                            message: '请输入验证码!'
-                          }
-                        ]
-                      }
-                    ]"
-                  >
-                    <a-icon slot="prefix" type="mail" class="prefixIcon" />
-                  </a-input>
-                </a-col>
-                <a-col :span="8">
-                  <a-button
-                    class="getCaptcha"
-                    size="large"
-                    @click="onGetCaptcha"
-                    :disabled="!!count"
-                    >{{ count ? `${count} 秒` : "获取验证码" }}</a-button
-                  >
-                </a-col>
-              </a-row>
-            </a-form-item>
-          </a-tab-pane>
-        </a-tabs>
-        <div>
-          <a-checkbox :checked="autoLogin" @change="changeAutoLogin">
-            自动登录
-          </a-checkbox>
-          <a style="float: right" href="">
-            忘记密码
-          </a>
-        </div>
-        <a-form-item>
-          <a-button
-            size="large"
-            class="submit"
-            type="primary"
-            htmlType="submit"
-            :loading="submitting"
-            >登录</a-button
-          >
-        </a-form-item>
-        <div class="other">
-          其他登录方式
-          <a-icon type="alipay-circle" class="icon" theme="outlined" />
-          <a-icon type="taobao-circle" class="icon" theme="outlined" />
-          <a-icon type="weibo-circle" class="icon" theme="outlined" />
-          <router-link class="register" to="/user/register">
-            注册账户
-          </router-link>
-        </div>
-      </a-form>
-    </div>
+    <Login
+      :defaultActiveKey="type"
+      :onTabChange="onTabChange"
+      :onSubmit="handleSubmit"
+      v-ant-ref="
+        form => {
+          this.loginForm = form;
+        }
+      "
+    >
+      <Tab key="account" tab="账户密码登录">
+        <VNodes :vnodes="renderMessage('account')" />
+        <UserName
+          name="userName"
+          placeholder="用户名:admin or user"
+          :rules="[
+            {
+              required: true,
+              message: '请输入用户名!'
+            }
+          ]"
+        />
+        <Password
+          name="password"
+          placeholder="密码:ant.design"
+          :rules="[{ required: true, message: '请输入密码!' }]"
+          :onPressEnter="
+            e => {
+              e.preventDefault();
+              this.form.validateFields(this.handleSubmit);
+            }
+          "
+        />
+      </Tab>
+      <Tab key="mobile" tab="手机号登录">
+        <VNodes :vnodes="renderMessage('mobile')" />
+        <Mobile
+          name="mobile"
+          placeholder="手机号"
+          :rules="[
+            {
+              required: true,
+              message: '请输入手机号!'
+            },
+            {
+              pattern: /^1\d{10}$/,
+              message: '手机号格式错误!'
+            }
+          ]"
+        />
+        <Captcha
+          name="captcha"
+          placeholder="验证码"
+          :countDown="120"
+          :onGetCaptcha="onGetCaptcha"
+          getCaptchaButtonText="获取验证码"
+          getCaptchaSecondText="秒"
+          :rules="[
+            {
+              required: true,
+              message: '请输入验证码!'
+            }
+          ]"
+        />
+      </Tab>
+      <div>
+        <a-checkbox :checked="autoLogin" @change="changeAutoLogin">
+          自动登录
+        </a-checkbox>
+        <a style="float: right" href="">
+          忘记密码
+        </a>
+      </div>
+      <Submit :loading="submitting">
+        登录
+      </Submit>
+      <div class="other">
+        其他登录方式
+        <a-icon type="alipay-circle" class="icon" theme="outlined" />
+        <a-icon type="taobao-circle" class="icon" theme="outlined" />
+        <a-icon type="weibo-circle" class="icon" theme="outlined" />
+        <router-link class="register" to="/user/register">
+          注册账户
+        </router-link>
+      </div>
+    </Login>
   </div>
 </template>
 
 <script>
 import { mapActions, mapState } from "vuex";
 import { Modal } from "ant-design-vue";
+import Login from "@/components/Login";
+
+const {
+  Tab,
+  UserName,
+  Password,
+  Mobile,
+  Captcha,
+  Submit
+} = Login.WrappedComponent;
+
 export default {
   components: {
     VNodes: {
       functional: true,
       render: (h, ctx) => ctx.props.vnodes
-    }
+    },
+    Login,
+    Tab,
+    UserName,
+    Password,
+    Mobile,
+    Captcha,
+    Submit
   },
   data() {
     return {
-      form: this.$form.createForm(this),
       type: "account",
       autoLogin: true,
       submitting: false,
       count: 0,
-      active: {
-        account: ["userName", "password"],
-        mobile: ["mobile", "captcha"]
-      }
+      tabs: []
     };
   },
-  beforeDestroy() {
-    clearInterval(this.interval);
-  },
   computed: {
     ...mapState("login", {
       status: state => state.status
@@ -180,54 +133,45 @@ export default {
   },
   methods: {
     ...mapActions("login", ["login", "getCaptcha"]),
-    handleSubmit(e) {
-      e.preventDefault();
-      const { type, form } = this;
-      const activeFileds = this.active[type];
-      form.validateFields(activeFileds, { force: true }, (err, values) => {
-        if (!err) {
-          this.submitting = true;
-          this.login({
-            ...values,
-            type
-          }).then(() => {
-            this.submitting = false;
-          });
-        }
-      });
+    handleSubmit(err, values) {
+      const { type } = this.$data;
+      if (!err) {
+        this.submitting = true;
+        this.login({
+          ...values,
+          type
+        }).then(() => {
+          this.submitting = false;
+        });
+      }
     },
-    onSwitch(key) {
-      this.type = key;
+    onTabChange(type) {
+      this.type = type;
     },
     changeAutoLogin(e) {
       this.autoLogin = e.target.checked;
     },
-    runGetCaptchaCountDown() {
-      this.count = 59;
-      this.interval = setInterval(() => {
-        this.count -= 1;
-        if (this.count === 0) {
-          clearInterval(this.interval);
-        }
-      }, 1000);
-    },
     onGetCaptcha() {
-      const { form } = this;
-      form.validateFields(["mobile"], { force: true }, (err, values) => {
-        if (!err) {
-          this.getCaptcha({ mobile: values.mobile });
-          this.runGetCaptchaCountDown();
-          Modal.info({
-            title:
-              "此项目为演示项目,并不会真的给您发送验证码。请切换到账户密码登录界面按提示登录。"
-          });
-        }
+      return new Promise((resolve, reject) => {
+        this.loginForm.validateFields(
+          ["mobile"],
+          { force: true },
+          (err, values) => {
+            if (err) {
+              reject(err);
+            } else {
+              this.getCaptcha({ mobile: values.mobile })
+                .then(resolve)
+                .catch(reject);
+              Modal.info({
+                title:
+                  "此项目为演示项目,并不会真的给您发送验证码。请切换到账户密码登录界面按提示登录。"
+              });
+            }
+          }
+        );
       });
     },
-    onPressEnter(e) {
-      e.preventDefault();
-      this.form.validateFields(this.handleSubmit);
-    },
     renderMessage(type) {
       if (this.status === "error" && this.type === type && !this.submitting) {
         return (