lex 1 рік тому
батько
коміт
e4159fdb0d

+ 1 - 1
.npmrc

@@ -1,3 +1,3 @@
 phantomjs_cdnurl=http://cnpmjs.org/downloads
 sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
-registry=https://registry.npm.taobao.org
+registry=https://registry.npmmirror.com/

+ 5 - 8
README-zh.md

@@ -5,10 +5,10 @@
 
 ## 公用方法调用
+
 1. 导出文档或下载模板
    - utils/downLoadFile
 
-
 ## Extra
 
 如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
@@ -42,13 +42,12 @@ cd vue-admin-template
 npm install
 
 # 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
-npm install --registry=https://registry.npm.taobao.org
+npm install --registry=https://registry.npmmirror.com
 
 # 启动服务
 npm run dev
 ```
 
-
 ## 发布
 
 ```bash
@@ -71,6 +70,7 @@ npm run preview -- --report
 ```
 
 ## 组件
+
 组件使用的文件统一放到:src/components 目录下,首字母大写
 
 Upload -- 目前只实现了图片上传
@@ -78,12 +78,11 @@ QrCode -- 生成二维码弹窗
 Editor -- 富文本编辑器(如果要用到上传视屏,则需要在处理一下)
 Tooltip --
 
-
 ## 注意事项
 
 ```bash
 
-新建页面  
+新建页面
 1.需要在router中添加页面引用的路径 以及组件名称
 2.需要在/silder/silder配置权限 文件路径要与步骤1中相同
 3.需要适用admin账号设置相应权限
@@ -95,6 +94,4 @@ js-cookie 封装cookie的相关方法
 vue-qr 根据链接生成二维码
 numeral 数字格式化工具
 vue-amap 高德地图相关API
-
-
-
+```

+ 10 - 8
package-lock.json

@@ -5392,11 +5392,6 @@
         "randomfill": "^1.0.3"
       }
     },
-    "crypto-js": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
-      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
-    },
     "css": {
       "version": "2.2.4",
       "resolved": "https://registry.npmmirror.com/css/-/css-2.2.4.tgz",
@@ -6908,9 +6903,9 @@
       "dev": true
     },
     "eventemitter3": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
-      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz",
+      "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
     },
     "events": {
       "version": "3.3.0",
@@ -8556,6 +8551,13 @@
         "eventemitter3": "^4.0.0",
         "follow-redirects": "^1.0.0",
         "requires-port": "^1.0.0"
+      },
+      "dependencies": {
+        "eventemitter3": {
+          "version": "4.0.7",
+          "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
+          "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
+        }
       }
     },
     "http-proxy-middleware": {

+ 1 - 0
package.json

@@ -33,6 +33,7 @@
     "dayjs": "^1.8.35",
     "echarts": "^4.8.0",
     "element-ui": "^2.13.2",
+    "eventemitter3": "^5.0.1",
     "html-to-image": "^1.9.0",
     "http-server": "^0.12.3",
     "i": "^0.3.6",

+ 34 - 24
src/api/user.js

@@ -1,46 +1,56 @@
-import request from '@/utils/request'
-import qs from 'qs'
+import request from "@/utils/request";
+import request2 from "@/utils/request2";
+import qs from "qs";
 
-export function login (data) {
+export function mutualTLSQuery(data) {
+  return request2({
+    url: `/api-web/open/mutualTLS/query`,
+    method: "GET",
+    params: data,
+    data: {}
+  });
+}
+
+export function login(data) {
   return request({
-    url: '/api-auth/usernameLogin',
+    url: "/api-auth/usernameLogin",
     // url: '/user/login',
-    method: 'post',
+    method: "post",
     data: qs.stringify(data)
-  })
+  });
 }
 
-export function getInfo () {
+export function getInfo() {
   return request({
-    url: '/api-web/employee/queryUserInfo',
-    method: 'get'
-  })
+    url: "/api-web/employee/queryUserInfo",
+    method: "get"
+  });
 }
 // 登出
-export function logout () {
+export function logout() {
   return request({
-    url: '/api-auth/exit',
-    method: 'post',
+    url: "/api-auth/exit",
+    method: "post",
     headers: {
-      'content-type': 'application/x-www-form-urlencoded'
+      "content-type": "application/x-www-form-urlencoded"
     },
-    data: {},
-  })
+    data: {}
+  });
 }
 
 // 获取首页数据
-export function getIndex (data) {
+export function getIndex(data) {
   return request({
-    url: '/api-web/index',
-    method: 'get',
+    url: "/api-web/index",
+    method: "get",
     params: data
-  })
+  });
 }
 // 刷新token
-export function getRefreshToken (data) {
+export function getRefreshToken(data) {
   return request({
-    url: '/api-auth/refreshToken',
-    method: 'post',
+    url: "/api-auth/refreshToken",
+    method: "post",
     data: qs.stringify(data)
-  })
+  });
 }

BIN
src/components/TheAuth/images/auth-title-bg.png


BIN
src/components/TheAuth/images/btn-done.png


BIN
src/components/TheAuth/images/btn-down.png


BIN
src/components/TheAuth/images/btn-up.png


BIN
src/components/TheAuth/images/downlowdCert.png


BIN
src/components/TheAuth/images/mac/2.png


+ 47 - 42
src/components/TheAuth/index.vue

@@ -4,7 +4,7 @@
     <!-- {/* <i class={styles.iconClose} onClick={() => emit('close')}></i> */} -->
     <div class="authContent">
       <div class="steps">
-        <div ref="scrollbarRef">
+        <div class="scrollbarRef">
           <div v-if="brower.ios">
             <div v-if="step === 1">
               <div class="step">
@@ -27,7 +27,7 @@
                     双击<span>《安全证书.p12》</span>
                     ,输入钥匙串密码 ,点击
                     <span>【修改钥匙串】</span>
-                    <span styl="font-weight: 400; color: #777">
+                    <span style=" color: #777">
                       (若无此步骤则忽略)
                     </span>
                   </p>
@@ -45,7 +45,7 @@
                 <p class="txt">
                   输入证书密码:
                   <span class="red" style="text-decoration: underline">
-                    colexiu.com
+                    dayaedu.com
                   </span>
                   ,点击
                   <span>【好】</span>
@@ -63,7 +63,7 @@
                 <p class="txt">
                   <span class="red">重启浏览器</span>
                   (在电脑屏幕左上方选择当前浏览器并点击
-                  <span>【退出】</span>),再重新打开音乐数字课堂网址
+                  <span>【退出】</span>),再重新打开管乐迷后台网址
                 </p>
                 <div class="imgs">
                   <img src="./images/mac/3.png" class="m3" />
@@ -89,7 +89,7 @@
                 <div class="stepContent">
                   <p class="txt">
                     输入您的电脑密码,点击<span>【始终允许】</span>
-                    <span style="font-weight: 400; color: #777">
+                    <span style="color: #777;">
                       (若无此步骤则忽略)
                     </span>
                   </p>
@@ -101,7 +101,7 @@
               <div class="step">
                 <div class="stepNum">07</div>
                 <div class="stepContent">
-                  <p class="txt">证书安装完成,开始使用音乐数字课堂吧!</p>
+                  <p class="txt">证书安装完成,开始使用管乐迷后台吧!</p>
                 </div>
               </div>
             </div>
@@ -191,7 +191,7 @@
                 <div class="stepContent">
                   <p class="txt">
                     <span class="red">重启浏览器</span>
-                    ,打开音乐数字课堂网址
+                    ,打开管乐迷后台网址
                   </p>
                 </div>
               </div>
@@ -201,7 +201,7 @@
                 <div class="stepContent">
                   <p class="txt">
                     在【选择证书】弹窗中点击<span>【确定】</span>
-                    按钮,证书安装完成,开始使用音乐数字课堂吧!
+                    按钮,证书安装完成,开始使用管乐迷后台吧!
                   </p>
                   <img src="./images/window/7.png" class="w7" />
                 </div>
@@ -215,26 +215,11 @@
         <div
           class="btn btnUp"
           :style="{ cursor: step <= 1 ? 'not-allowed' : 'pointer' }"
-          @click="
-            () => {
-              if (step <= 1) return;
-              step -= 1;
-              // scrollbarRef.value.scrollTo({ top: 0, left: 0 })
-            }
-          "
+          @click="changeUpDown('up')"
         ></div>
         <div
           :class="['btn', 'btnDown', step === maxStep && 'btnDone']"
-          @click="
-            () => {
-              if (step >= maxStep) {
-                emit('close');
-              } else {
-                step += 1;
-              }
-              // scrollbarRef.value.scrollTo({ top: 0, left: 0 })
-            }
-          "
+          @click="changeUpDown('down')"
         ></div>
       </div>
     </div>
@@ -242,21 +227,36 @@
 </template>
 
 <script>
-import { browser } from "@/common/common";
+import { browser } from "@/utils";
 export default {
-  data: {
-    browser: browser(),
-    step: 1,
-    maxStep: browser().ios ? 5 : 7
+  data() {
+    return {
+      brower: browser() || {},
+      step: 1,
+      maxStep: browser().ios ? 5 : 7
+    };
   },
   methods: {
+    changeUpDown(type) {
+      if (type === "up") {
+        if (this.step <= 1) return;
+        this.step -= 1;
+      } else if (type === "down") {
+        if (this.step >= this.maxStep) {
+          this.$listeners.close();
+        } else {
+          this.step += 1;
+        }
+      }
+      document.querySelector(".scrollbarRef").scrollTo(0, 0);
+    },
     onDownload() {
       const isMac = (function() {
         return /macintosh|mac os x/i.test(navigator.userAgent);
       })();
       if (isMac) {
         window.open(
-          `https://oss.dayaedu.com/https-ssl/安全证书.p12?v=${new Date().getTime()}`
+          `https://oss.dayaedu.com/https-ssl/gym/安全证书.p12?v=${new Date().getTime()}`
         );
       } else {
         window.open("https://oss.dayaedu.com/https-ssl/安全证书.pfx");
@@ -298,7 +298,8 @@ export default {
 }
 
 .authContent {
-  background: linear-gradient(180deg, #e1f8ff 0%, #bbe7fd 100%);
+  // background: linear-gradient(180deg, #e1f8ff 0%, #bbe7fd 100%);
+  background: linear-gradient(180deg, #d3ede8 0%, #cff5fa 100%);
   border-radius: 0 0 23px 23px;
 }
 
@@ -310,17 +311,21 @@ export default {
     180deg,
     rgba(255, 255, 255, 0.7) 0%,
     rgba(255, 255, 255, 0.7) 88%,
-    #cdf0ff 100%
+    #c0e7eb 100%
   );
-  border-radius: 23px;
+  border-radius: 18px;
   border: 1px solid #ffffff;
 
-  :global {
-    .n-scrollbar {
-      margin: 0 0 25px;
-      padding: 14px 25px 25px;
-      min-height: calc(55vh - 28px);
-      max-height: calc(55vh - 28px);
+  .scrollbarRef {
+    margin: 0 0 25px;
+    padding: 14px 25px 25px;
+    min-height: calc(55vh - 28px);
+    max-height: calc(55vh - 28px);
+    overflow-x: hidden;
+    overflow-y: auto;
+
+    &::-webkit-scrollbar {
+      display: none;
     }
   }
 }
@@ -337,7 +342,7 @@ export default {
     align-items: center;
     justify-content: center;
     text-align: center;
-    background: #b9eaff;
+    background: #c1ece8;
     border: 1px solid #ffffff;
     font-size: 14px;
     font-weight: 600;
@@ -356,7 +361,7 @@ export default {
       line-height: 33px;
 
       span {
-        color: #1f6dec;
+        color: #14928a;
       }
 
       .red {

+ 40 - 1
src/layout/components/Navbar.vue

@@ -465,12 +465,25 @@
     <!-- <portal-target name="AppMain" ref="target">
       <instructions ref="instructions" @checkShow="checkShow" />
     </portal-target> -->
+
+    <el-dialog
+      :visible.sync="authVisible"
+      append-to-body
+      width="699px"
+      :show-close="false"
+      :destroy-on-close="true"
+      :close-on-press-escape="false"
+      class="theAuthDialog"
+    >
+      <TheAuth @close="authVisible = false" />
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import qs from "qs";
 import Logo from "./Sidebar/Logo";
+import TheAuth from "@/components/TheAuth/index.vue";
 import { mapGetters } from "vuex";
 import { resetPassword } from "@/api/buildTeam";
 import AppLink from "./Sidebar/Link";
@@ -480,10 +493,12 @@ import { tenantInfoQueryPage } from "@/views/organManager/api";
 import { sendSmsCode } from "./api";
 import axios from "axios";
 import userModel from "./modal/userModal";
+import { eventGlobal } from "@/utils";
 export default {
   components: {
     AppLink,
-    userModel
+    userModel,
+    TheAuth
     // instructions,
     // Breadcrumb,
     // Hamburger
@@ -492,6 +507,7 @@ export default {
     let tenantConfig = sessionStorage.getItem("tenantConfig");
     tenantConfig = tenantConfig ? JSON.parse(tenantConfig) : {};
     return {
+      authVisible: false,
       accountStatus: false,
       organName: this.$store.getters.organName,
       organNameList: [],
@@ -570,6 +586,11 @@ export default {
     this.$bus.$on("getShowNums", obj => {
       this.noReadNum = obj;
     });
+
+    // 监听证书提示
+    eventGlobal.on("auth-not-installed", () => {
+      this.authVisible = true;
+    });
     // 手动加入
     this.toggleSideBar();
     this.organNameList = this.organName ? this.organName.split(",") : [];
@@ -1101,4 +1122,22 @@ export default {
   color: #198cfe;
   line-height: 17px;
 }
+
+.theAuthDialog {
+  /deep/ .el-dialog {
+    background-color: transparent;
+    box-shadow: none;
+    border-radius: 0;
+    margin: auto !important;
+    height: 100%;
+    display: flex;
+    align-items: center;
+  }
+  /deep/ .el-dialog__header {
+    display: none;
+  }
+  /deep/ .el-dialog__body {
+    padding: 0;
+  }
+}
 </style>

+ 29 - 0
src/utils/index.js

@@ -1,3 +1,7 @@
+import EventEmitter from "eventemitter3";
+
+export const eventGlobal = new EventEmitter();
+
 /**
  * Created by PanJiaChen on 16/11/18.
  */
@@ -190,3 +194,28 @@ export const getTimeFormat = (times, keys = [], format = "YYYY-MM-DD") => {
   }
   return {};
 };
+
+export const browser = () => {
+  const u = navigator.userAgent;
+  return {
+    trident: u.indexOf("Trident") > -1, //IE内核
+    presto: u.indexOf("Presto") > -1, //opera内核
+    webKit: u.indexOf("AppleWebKit") > -1, //苹果、谷歌内核
+    gecko: u.indexOf("Gecko") > -1 && u.indexOf("KHTML") == -1, //火狐内核
+    mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
+    ios: !!u.match(/Mac OS X/), //ios终端
+    // ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
+    android: u.indexOf("DAYAAPPA") > -1 || u.indexOf("Adr") > -1, //android终端
+    iPhone: u.indexOf("DAYAAPPI") > -1, //是否为iPhone或者QQHD浏览器
+    isApp:
+      u.indexOf("DAYAAPPI") > -1 ||
+      u.indexOf("DAYAAPPA") > -1 ||
+      u.indexOf("Adr") > -1,
+    iPad: u.indexOf("iPad") > -1, //是否iPad
+    webApp: u.indexOf("Safari") == -1, //是否web应该程序,没有头部与底部
+    weixin: u.indexOf("MicroMessenger") > -1, //是否微信 (2015-01-22新增)
+    alipay: u.indexOf("AlipayClient") > -1, //是否支付宝
+    huawei: !!u.match(/huawei/i) || !!u.match(/honor/i),
+    xiaomi: !!u.match(/mi\s/i) || !!u.match(/redmi/i) || !!u.match(/mix/i)
+  };
+};

+ 23 - 3
src/utils/request2.js

@@ -3,7 +3,8 @@ import axios from "axios";
 import { Message } from "element-ui";
 import store from "@/store";
 import qs from "querystring";
-import { getToken, removeToken,setToken } from "@/utils/auth";
+
+import { getToken, removeToken, setToken } from "@/utils/auth";
 import cleanDeep from "clean-deep";
 // import { Loading } from 'element-ui'
 import {
@@ -12,6 +13,7 @@ import {
 } from "./request-loading";
 import router from "@/router/index";
 import Vue from "vue";
+import { eventGlobal } from "@/utils";
 const showMessage = Symbol("showMessage");
 class DonMessage {
   success(options, single = true) {
@@ -131,7 +133,7 @@ service.interceptors.response.use(
         removeToken();
         await store.dispatch("user/resetToken").then(() => {
           setTimeout(async () => {
-          location.reload();
+            location.reload();
           }, 1000);
         });
 
@@ -165,8 +167,26 @@ service.interceptors.response.use(
   async error => {
     if (error.message == "Network Error") {
       vue.$message.error("网络异常,请检查网络连接");
+    }
+    // 对响应错误做点什么
+    if (error.message.indexOf("timeout") != -1) {
+      // ;(window as any)['$message'].error('网络超时')
+      vue.$message.error("网络超时");
+    } else if (error.message == "Network Error") {
+      // ;(window as any)['$message'].error('网络连接错误')
+      vue.$message.error("网络异常,请检查网络连接");
     } else {
-      vue.$message.error(error.message);
+      if (error) {
+        if (error.response.status === 511) {
+          eventGlobal.emit("auth-not-installed");
+        } else {
+          vue.$message.error("请求错误");
+        }
+      } else {
+        vue.$message.error("接口路径找不到");
+      }
+      //  else {
+      //   vue.$message.error(error.message);
     }
     await tryHideFullScreenLoading(store);
     return Promise.reject(error);

+ 68 - 15
src/views/login/index.vue

@@ -65,7 +65,9 @@
             @keyup.enter.native="handleLogin"
           />
           <span class="show-pwd" @click="showPwd">
-            <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
+            <svg-icon
+              :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
+            />
           </span>
         </el-form-item>
         <div class="remberBox" @click="isSaveUserInfo = !isSaveUserInfo">
@@ -82,14 +84,28 @@
         </div>
       </el-form>
     </div>
+
+    <el-dialog
+      :visible.sync="authVisible"
+      width="699px"
+      :show-close="false"
+      :destroy-on-close="true"
+      :close-on-press-escape="false"
+      class="theAuthDialog"
+    >
+      <TheAuth @close="authVisible = false" />
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import { validUsername } from "@/utils/validate";
 import { Searchs } from "@/helpers";
+import TheAuth from "@/components/TheAuth/index.vue";
+import { mutualTLSQuery } from "@/api/user";
 export default {
   name: "Login",
+  components: { TheAuth },
   data() {
     const validateUsername = (rule, value, callback) => {
       // validUsername
@@ -107,32 +123,49 @@ export default {
       }
     };
     return {
+      authVisible: false,
       loginForm: {
         username: "",
-        password: "",
+        password: ""
       },
       loginRules: {
-        username: [{ required: true, trigger: "blur", validator: validateUsername }],
-        password: [{ required: true, trigger: "blur", validator: validatePassword }],
+        username: [
+          { required: true, trigger: "blur", validator: validateUsername }
+        ],
+        password: [
+          { required: true, trigger: "blur", validator: validatePassword }
+        ]
       },
       passwordType: "password",
       redirect: undefined,
-      isSaveUserInfo: true,
+      isSaveUserInfo: true
     };
   },
   mounted() {
     this.loginForm.username = localStorage.getItem("username");
     this.loginForm.password = localStorage.getItem("password");
+
+    this.onAuthError();
   },
   watch: {
     $route: {
-      handler: function (route) {
+      handler: function(route) {
         this.redirect = route.query && route.query.redirect;
       },
-      immediate: true,
-    },
+      immediate: true
+    }
   },
   methods: {
+    async onAuthError() {
+      try {
+        await mutualTLSQuery({});
+      } catch (err) {
+        console.log(err);
+        if (err.response.status === 511) {
+          this.authVisible = true;
+        }
+      }
+    },
     showPwd() {
       if (this.passwordType === "password") {
         this.passwordType = "";
@@ -156,12 +189,12 @@ export default {
         localStorage.setItem("username", "");
         localStorage.setItem("password", "");
       }
-      this.$refs.loginForm.validate((valid) => {
+      this.$refs.loginForm.validate(valid => {
         if (valid) {
           this.$store
             .dispatch("user/login", this.loginForm)
-            .then((res) => {
-              this.$nextTick((res) => {
+            .then(res => {
+              this.$nextTick(res => {
                 //  这里清空 tab
                 this.$store.dispatch("delAllViews");
                 this.$router.push({ path: "/workbench" });
@@ -175,8 +208,8 @@ export default {
     },
     saveUserInfo() {
       this.isSaveUserInfo = !this.isSaveUserInfo;
-    },
-  },
+    }
+  }
 };
 </script>
 
@@ -244,7 +277,8 @@ body {
   .dotWrap {
     width: 21px;
     height: 21px;
-    background: url("../../assets/images/icon_checkbox_default.png") no-repeat center;
+    background: url("../../assets/images/icon_checkbox_default.png") no-repeat
+      center;
     background-size: contain;
     margin-right: 8px;
     position: relative;
@@ -321,7 +355,8 @@ $light_gray: #eee;
   display: flex;
   align-items: center;
   justify-content: space-between;
-  background: url("../../assets/images/base/login-bg.png") no-repeat center left #fff;
+  background: url("../../assets/images/base/login-bg.png") no-repeat center left
+    #fff;
   background-size: cover;
 
   .login-form {
@@ -492,4 +527,22 @@ $light_gray: #eee;
   //   margin-left: 20px;
   // }
 }
+
+.theAuthDialog {
+  /deep/ .el-dialog {
+    background-color: transparent;
+    box-shadow: none;
+    border-radius: 0;
+    margin: auto !important;
+    height: 100%;
+    display: flex;
+    align-items: center;
+  }
+  /deep/ .el-dialog__header {
+    display: none;
+  }
+  /deep/ .el-dialog__body {
+    padding: 0;
+  }
+}
 </style>

+ 1 - 1
vue.config.js

@@ -19,7 +19,7 @@ const name = defaultSettings.title || "管乐迷后台管理系统"; // page tit
 // let target = 'http://192.168.3.20:8000' //邹璇
 // let target = "http://192.168.0.127:8000"; //勇哥
 // let target = "http://192.168.3.14:8005"; // 原谅
-let target = "https://test.dayaedu.com"; //测试环境
+let target = "https://dev.dayaedu.com"; //测试环境
 // All configuration item explanations can be find in https://cli.vuejs.org/config/
 module.exports = {
   /**