zhangjie %!s(int64=4) %!d(string=hai) anos
pai
achega
091cba10fc

+ 2 - 2
src/assets/styles/card-preview.scss

@@ -216,7 +216,7 @@
     margin-left: -12px;
   }
   &:last-child {
-    right: 30px;
+    right: 96px;
   }
   li {
     position: absolute;
@@ -264,7 +264,7 @@
     }
   }
   &-text {
-    right: 152px;
+    right: 25%;
   }
   &-text-cont {
     height: 16px;

+ 11 - 0
src/assets/styles/home.scss

@@ -387,6 +387,17 @@
   margin-right: 320px;
 }
 
+.vlcode {
+  height: 36px;
+}
+.vlcode-left {
+  margin-right: 135px;
+}
+.vlcode-right {
+  float: right;
+  width: 120px;
+}
+
 // other
 .tips-info {
   font-size: 14px;

+ 6 - 0
src/modules/base/components/ResetPwd.vue

@@ -66,6 +66,11 @@ const initModalForm = {
 };
 export default {
   name: "reset-pwd",
+  props: {
+    userPassword: {
+      type: String
+    }
+  },
   data() {
     const equalToPswd = (rule, value, callback) => {
       if (value !== this.resetModel.newPassword) {
@@ -94,6 +99,7 @@ export default {
   methods: {
     initData(val) {
       this.resetModel = { ...initModalForm };
+      this.resetModel.password = this.userPassword || "";
       this.resetModel.id = this.$ls.get("user", { id: "" }).id;
     },
     visibleChange() {

+ 11 - 0
src/modules/base/views/UserEdit.vue

@@ -22,6 +22,14 @@
           clearable
         ></el-input>
       </el-form-item>
+      <el-form-item prop="phone" label="手机号:">
+        <el-input
+          style="width:282px;"
+          v-model.trim="modalForm.phone"
+          placeholder="请输入手机号"
+          clearable
+        ></el-input>
+      </el-form-item>
       <el-form-item prop="roleCode" label="角色:">
         <el-select
           style="width:282px;"
@@ -74,6 +82,7 @@
 <script>
 import { updateUser, userDetail, roleList, courseList } from "../api";
 import { logout } from "../../login/api";
+import { phone } from "@/plugins/formRules";
 
 export default {
   name: "user-edit",
@@ -90,11 +99,13 @@ export default {
       modalForm: {
         id: "",
         name: "",
+        phone: "",
         loginName: "",
         roleCode: "",
         courseId: ""
       },
       rules: {
+        phone,
         name: [
           {
             required: true,

+ 1 - 0
src/modules/base/views/UserManage.vue

@@ -77,6 +77,7 @@
         <el-table-column prop="loginName" label="用户名"></el-table-column>
         <el-table-column prop="name" label="姓名"></el-table-column>
         <el-table-column prop="roleName" label="角色"></el-table-column>
+        <el-table-column prop="phone" label="手机号"></el-table-column>
         <el-table-column
           prop="courseNameCode"
           label="科目名称(编码)"

+ 6 - 3
src/modules/card/components/RightClickMenu.vue

@@ -84,7 +84,10 @@ export default {
         left: e.x - 50 + "px"
       });
       // 作文题非拆分题
-      if (this.curElement.type === "COMPOSITION") {
+      if (
+        this.curElement.type === "COMPOSITION" ||
+        this.curElement["container"]
+      ) {
         this.showDeleteChildBtn = false;
       } else {
         const positionInfos = fetchSameExplainNumberExplainChildernPositionInfo(
@@ -92,8 +95,9 @@ export default {
           this.pages
         );
         this.showDeleteChildBtn = positionInfos.length >= 2;
-        this.IS_EXPLAIN_CHILDREN = this.curElement.type === "EXPLAIN_CHILDREN";
       }
+      this.IS_EXPLAIN_CHILDREN = this.curElement.type === "EXPLAIN_CHILDREN";
+
       // 直接用了setTimeout,解决弹出框跟随延迟的问题,似乎粗暴了点。
       setTimeout(() => {
         this.visible = true;
@@ -144,7 +148,6 @@ export default {
       });
     },
     toDeleteChildren() {
-      // TODO:校验当前元件是否可以删除
       this.visible = false;
       this.deleteExplainChildren(this.curElement);
       this.$nextTick(() => {

+ 2 - 2
src/modules/card/components/elementEdit/Composition.vue

@@ -11,8 +11,8 @@
     >
       <div class="elem-composition-elements">
         <composition-element
-          v-for="(element, eindex) in data.elements"
-          :key="eindex"
+          v-for="element in data.elements"
+          :key="element.id"
           :data="element"
           @change="elementChange"
         ></composition-element>

+ 2 - 2
src/modules/card/components/elementEdit/ExplainChildren.vue

@@ -13,8 +13,8 @@
         @dragleave.prevent
       >
         <explain-children-element
-          v-for="(element, eindex) in data.elements"
-          :key="eindex"
+          v-for="element in data.elements"
+          :key="element.id"
           :data="element"
           @change="elementChange"
         ></explain-children-element>

+ 2 - 2
src/modules/card/components/elementPreview/Composition.vue

@@ -6,8 +6,8 @@
     <div class="elem-body">
       <div class="elem-composition-elements">
         <composition-element
-          v-for="(element, eindex) in data.elements"
-          :key="eindex"
+          v-for="element in data.elements"
+          :key="element.id"
           :data="element"
         ></composition-element>
       </div>

+ 2 - 2
src/modules/card/components/elementPreview/ExplainChildren.vue

@@ -8,8 +8,8 @@
       <!-- 解答题子元件区域 -->
       <div class="elem-explain-elements">
         <explain-children-element
-          v-for="(element, eindex) in data.elements"
-          :key="eindex"
+          v-for="element in data.elements"
+          :key="element.id"
           :data="element"
         ></explain-children-element>
       </div>

+ 2 - 2
src/modules/card/previewTemp.js

@@ -287,7 +287,7 @@ const css =
     margin-left: -12px;\
   }\
   .page-locator-group:last-child {\
-    right: 30px;\
+    right: 96px;\
   }\
   .page-locator-group li {\
     position: absolute;\
@@ -331,7 +331,7 @@ const css =
     border-bottom: 16px solid #000;\
   }\
   .page-number-text {\
-    right: 152px;\
+    right: 25%;\
   }\
   .page-number-text-cont {\
     height: 16px;\

+ 2 - 2
src/modules/card/views/CardDesign.vue

@@ -230,8 +230,8 @@
                     <div class="page-column-body">
                       <topic-element-edit
                         class="page-column-element"
-                        v-for="(element, eindex) in column.elements"
-                        :key="eindex"
+                        v-for="element in column.elements"
+                        :key="element.id"
                         :data="element"
                       ></topic-element-edit>
                     </div>

+ 3 - 0
src/modules/login/api.js

@@ -3,6 +3,9 @@ import { $post, $get } from "@/plugins/axios";
 export const login = datas => {
   return $post("/api/print/basic/user/login", datas);
 };
+export const getSmsCode = datas => {
+  return $post("/api/print/basic/user/getVerifyCode", datas);
+};
 export const logout = userId => {
   return $get("/api/print/basic/sys/logout", { userId });
 };

+ 149 - 5
src/modules/login/views/Login.vue

@@ -25,6 +25,26 @@
               <i class="icon icon-lock" slot="prefix"></i
             ></el-input>
           </el-form-item>
+          <el-form-item prop="code">
+            <div class="vlcode">
+              <div class="vlcode-right">
+                <el-button
+                  style="width:100%;"
+                  :type="isFetchingCode ? 'default' : 'primary'"
+                  @click="fetchSmsCode"
+                  :disabled="isFetchingCode"
+                  >{{ codeContent }}</el-button
+                >
+              </div>
+              <div class="vlcode-left">
+                <el-input
+                  v-model.trim="loginModel.code"
+                  placeholder="请输入手机验证码"
+                  clearable
+                ></el-input>
+              </div>
+            </div>
+          </el-form-item>
           <el-form-item>
             <el-button
               style="width:116px;"
@@ -39,23 +59,54 @@
     </div>
     <div class="login-shadow-top"></div>
     <div class="login-shadow-bottom"></div>
+
+    <!-- 修改密码 -->
+    <reset-pwd
+      :user-password="loginModel.password"
+      @modified="logoutAction"
+      ref="ResetPwd"
+    ></reset-pwd>
   </div>
 </template>
 
 <script>
-import { password } from "@/plugins/formRules";
-import { login } from "../api";
+import { password, smscode } from "@/plugins/formRules";
+import { login, getSmsCode } from "../api";
 import { AES } from "@/plugins/crypto";
 
+import ResetPwd from "@/modules/base/components/ResetPwd";
+
+const wstorage = {
+  set(key, value, expire = null) {
+    window.localStorage.setItem(key, JSON.stringify({ value, expire }));
+  },
+  get(key, defaultVal = null) {
+    const lsvalue = JSON.parse(window.localStorage.getItem(key));
+    return lsvalue && (!lsvalue.expire || lsvalue.expire > new Date().getTime())
+      ? lsvalue.value
+      : defaultVal;
+  },
+  remove(key) {
+    window.localStorage.removeItem(key);
+  }
+};
+
+const codeWaitingTime = 60;
+const nameWaitTime = "login";
+
 export default {
   name: "login",
+  components: { ResetPwd },
   data() {
     return {
+      nameWaitTime,
       loginModel: {
         loginName: "",
+        code: "",
         password: ""
       },
       loginRules: {
+        code: smscode,
         loginName: [
           {
             required: true,
@@ -66,11 +117,17 @@ export default {
         password
       },
       roles: [],
-      isSubmit: false
+      isSubmit: false,
+      // code
+      isFetchingCode: false,
+      codeContent: "获取验证码",
+      codeWaitingTime,
+      time: codeWaitingTime
     };
   },
   mounted() {
     this.$ls.clear();
+    this.setWaitingTime();
   },
   methods: {
     async submit(name) {
@@ -81,20 +138,107 @@ export default {
       this.isSubmit = true;
       const data = await login({
         loginName: this.loginModel.loginName,
-        password: AES(this.loginModel.password)
+        password: AES(this.loginModel.password),
+        code: this.loginModel.code
       }).catch(() => {});
       this.isSubmit = false;
       if (!data) return;
 
       data.account.roleCode = data.roles.map(item => item.roleCode).join();
 
-      this.$ls.set("token", data.token, this.GLOBAL.authTimeout);
       this.$ls.set("schoolId", data.account.schoolId, this.GLOBAL.authTimeout);
       this.$ls.set("user", data.account, this.GLOBAL.authTimeout);
+
+      if (
+        data.account.roleCode.includes("QUESTION_TEACHER") &&
+        !data.account.pwChangedCount
+      ) {
+        this.$refs.ResetPwd.open();
+        return;
+      }
+
+      this.$ls.set("token", data.token, this.GLOBAL.authTimeout);
       this.$store.commit("setUser", data.account);
       this.$router.push({
         name: "Home"
       });
+    },
+    // code valid
+    setWaitingTime() {
+      let codetime = wstorage.get(this.nameWaitTime);
+      if (codetime) {
+        let num = Math.floor((codetime.expire - new Date().getTime()) / 1000);
+        if (num > 0) {
+          this.time = num;
+          this.isFetchingCode = true;
+          this.changeContent();
+        }
+      }
+    },
+    checkField(field) {
+      return new Promise((resolve, reject) => {
+        this.$refs.loginForm.validateField(field, unvalid => {
+          if (unvalid) {
+            reject();
+          } else {
+            resolve();
+          }
+        });
+      });
+    },
+    async fetchSmsCode() {
+      const validAll = [
+        this.checkField("loginName"),
+        this.checkField("password")
+      ];
+
+      let result = true;
+      await Promise.all(validAll).catch(() => {
+        result = false;
+      });
+
+      if (!result) return;
+
+      this.isFetchingCode = true;
+      const data = await getSmsCode({
+        loginName: this.loginModel.loginName,
+        password: this.loginModel.password
+      }).catch(() => {
+        this.isFetchingCode = false;
+      });
+      if (!data) return;
+      this.$message.success(
+        `已向手机号【${data.phone}】成功发送短信,请在2分钟内进行验证!`
+      );
+      this.changeContent();
+    },
+    changeContent() {
+      if (!this.isFetchingCode) return;
+      this.codeContent = "倒计时" + this.time + "s";
+      const circleTime = time => {
+        let t = setInterval(() => {
+          if (time > 1) {
+            time--;
+            let expire = new Date().getTime() + time * 1000;
+            wstorage.set(
+              this.nameWaitTime,
+              {
+                time,
+                expire
+              },
+              expire
+            );
+            this.codeContent = "倒计时" + time + "s";
+          } else {
+            this.time = this.codeWaitingTime;
+            wstorage.remove(this.nameWaitTime);
+            this.codeContent = "获取验证码";
+            this.isFetchingCode = false;
+            clearInterval(t);
+          }
+        }, 1e3);
+      };
+      circleTime(this.time);
     }
   }
 };