Bladeren bron

Merge branch 'iteration-20240410-cert' into 11/24SAAS

lex 1 jaar geleden
bovenliggende
commit
7499339076

+ 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-auth/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/icon-close.png


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


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


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


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


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


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


BIN
src/components/TheAuth/images/window/1.png


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


BIN
src/components/TheAuth/images/window/3.png


BIN
src/components/TheAuth/images/window/4.png


BIN
src/components/TheAuth/images/window/5.png


BIN
src/components/TheAuth/images/window/6.png


BIN
src/components/TheAuth/images/window/7.png


BIN
src/components/TheAuth/images/window/8.png


+ 353 - 0
src/components/TheAuth/index.tsx1

@@ -0,0 +1,353 @@
+import { defineComponent, reactive, ref } from "vue";
+import styles from "./index.module.less";
+import { NButton, NScrollbar } from "naive-ui";
+import w1 from "./images/window/1.png";
+import w2 from "./images/window/2.png";
+import w3 from "./images/window/3.png";
+import w4 from "./images/window/4.png";
+import w5 from "./images/window/5.png";
+import w6 from "./images/window/6.png";
+import w7 from "./images/window/7.png";
+import w8 from "./images/window/8.png";
+
+import m1 from "./images/mac/1.png";
+import m2 from "./images/mac/2.png";
+import m3 from "./images/mac/3.png";
+import m4 from "./images/mac/4.png";
+import m5 from "./images/mac/5.png";
+import m6 from "./images/mac/6.png";
+import { browser } from "@/utils";
+
+export default defineComponent({
+  name: "the-auth",
+  emits: ["close"],
+  setup(props, { emit }) {
+    const scrollbarRef = ref();
+    const state = reactive({
+      step: 1,
+      maxStep: browser().ios ? 5 : 7
+    });
+    // 下载证书
+    const onDownload = () => {
+      // const agent = navigator.userAgent.toLowerCase();
+      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()}`
+        );
+      } else {
+        window.open("https://oss.dayaedu.com/https-ssl/安全证书.pfx");
+      }
+      // emit('close');
+    };
+    return () => (
+      <div class={styles.theAuth}>
+        <div class={styles.theTitle} />
+        {/* <i class={styles.iconClose} onClick={() => emit('close')}></i> */}
+        <div class={styles.authContent}>
+          <div class={styles.steps}>
+            <NScrollbar ref={scrollbarRef}>
+              {browser().ios ? (
+                <>
+                  {state.step === 1 && (
+                    <>
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>01</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            点击下方【下载证书】按钮,下载数据安全证书安装包
+                          </p>
+
+                          <div>
+                            <div
+                              class={styles.downloadCert}
+                              onClick={onDownload}
+                            />
+                          </div>
+                        </div>
+                      </div>
+
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>02</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            双击<span>《安全证书.p12》</span>
+                            ,输入钥匙串密码 ,点击
+                            <span>【修改钥匙串】</span>
+                            <span
+                              style={{ "font-weight": 400, color: "#777777" }}
+                            >
+                              (若无此步骤则忽略)
+                            </span>
+                          </p>
+                          <div class={[styles.imgs, styles.imgs2]}>
+                            <img src={m1} class={styles.m1} />
+                            <img src={m6} class={styles.m6} />
+                          </div>
+                        </div>
+                      </div>
+                    </>
+                  )}
+
+                  {state.step === 2 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>03</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          输入证书密码:
+                          <span
+                            class={styles.red}
+                            style={{ "text-decoration": "underline" }}
+                          >
+                            colexiu.com
+                          </span>
+                          ,点击
+                          <span>【好】</span>
+                        </p>
+
+                        <div class={styles.imgs}>
+                          <img src={m2} class={styles.m2} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+
+                  {state.step === 3 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>04</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          <span class={styles.red}>重启浏览器</span>
+                          (在电脑屏幕左上方选择当前浏览器并点击
+                          <span>【退出】</span>),再重新打开音乐数字课堂网址
+                        </p>
+                        <div class={styles.imgs}>
+                          <img src={m3} class={styles.m3} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+
+                  {state.step === 4 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>05</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          在【选择证书】弹窗中点击<span>【确定】</span>按钮
+                        </p>
+                        <div class={styles.imgs}>
+                          <img src={m4} class={styles.m4} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+
+                  {state.step === 5 && (
+                    <>
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>06</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            输入您的电脑密码,点击<span>【始终允许】</span>
+                            <span
+                              style={{ "font-weight": 400, color: "#777777" }}
+                            >
+                              (若无此步骤则忽略)
+                            </span>
+                          </p>
+                          <div class={styles.imgs}>
+                            <img src={m5} class={styles.m5} />
+                          </div>
+                        </div>
+                      </div>
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>07</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            证书安装完成,开始使用音乐数字课堂吧!
+                          </p>
+                        </div>
+                      </div>
+                    </>
+                  )}
+                </>
+              ) : (
+                <>
+                  {state.step === 1 && (
+                    <>
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>01</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            点击下方【下载证书】按钮,下载数据安全证书安装包
+                          </p>
+                          <div>
+                            <div
+                              class={styles.downloadCert}
+                              onClick={onDownload}
+                            />
+                          </div>
+                        </div>
+                      </div>
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>02</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            双击<span>《安全证书.pfx》</span>,出现弹窗后点击
+                            <span>【下一步】</span>
+                          </p>
+                          <div class={[styles.imgs, styles.imgs2]}>
+                            <img src={w1} class={styles.w1} />
+                            <img src={w2} class={styles.w2} />
+                          </div>
+                        </div>
+                      </div>
+                    </>
+                  )}
+
+                  {state.step === 2 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>03</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          点击<span>【下一步】</span>
+                        </p>
+                        <div class={styles.imgs}>
+                          <img src={w3} class={styles.w3} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+
+                  {state.step === 3 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>04</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          点击<span>【下一步】</span>
+                        </p>
+                        <div class={styles.imgs}>
+                          <img src={w4} class={styles.w4} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+                  {state.step === 4 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>05</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          点击<span>【下一步】</span>
+                        </p>
+                        <div class={styles.imgs}>
+                          <img src={w8} class={styles.w8} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+                  {state.step === 5 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>06</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          点击<span>【完成】</span>
+                        </p>
+                        <div class={styles.imgs}>
+                          <img src={w5} class={styles.w5} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+                  {state.step === 6 && (
+                    <div class={styles.step}>
+                      <div class={styles.stepNum}>07</div>
+                      <div class={styles.stepContent}>
+                        <p class={styles.txt}>
+                          点击<span>【确定】</span>
+                        </p>
+                        <div class={styles.imgs}>
+                          <img src={w6} class={styles.w6} />
+                        </div>
+                      </div>
+                    </div>
+                  )}
+                  {state.step === 7 && (
+                    <>
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>08</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            <span class={styles.red}>重启浏览器</span>
+                            ,打开音乐数字课堂网址
+                          </p>
+                        </div>
+                      </div>
+
+                      <div class={styles.step}>
+                        <div class={styles.stepNum}>09</div>
+                        <div class={styles.stepContent}>
+                          <p class={styles.txt}>
+                            在【选择证书】弹窗中点击<span>【确定】</span>
+                            按钮,证书安装完成,开始使用音乐数字课堂吧!
+                            {/* (若浏览器没有弹出该弹窗请
+                        <span class={styles.red}>重启电脑</span>
+                        后重试) */}
+                          </p>
+                          <img src={w7} class={styles.w7} />
+                        </div>
+                      </div>
+                    </>
+                  )}
+
+                  {/* <div class={styles.step}>
+                    <div class={styles.stepNum}>05</div>
+                    <div class={styles.stepContent}>
+                      <p class={styles.txt}>
+                        证书安装完成,开始使用音乐数字课堂吧。
+                      </p>
+                    </div>
+                  </div> */}
+                </>
+              )}
+            </NScrollbar>
+          </div>
+
+          <div class={styles.btnGroup}>
+            {/* <NButton round type="primary" onClick={onDownload}>
+              下载证书
+            </NButton> */}
+            <div
+              class={[styles.btn, styles.btnUp]}
+              style={{
+                cursor: state.step <= 1 ? "not-allowed" : "pointer"
+              }}
+              onClick={() => {
+                if (state.step <= 1) return;
+                state.step -= 1;
+                scrollbarRef.value.scrollTo({ top: 0, left: 0 });
+              }}
+            />
+            <div
+              class={[
+                styles.btn,
+                styles.btnDown,
+                state.step === state.maxStep && styles.btnDone
+              ]}
+              onClick={() => {
+                if (state.step >= state.maxStep) {
+                  emit("close");
+                } else {
+                  state.step += 1;
+                }
+
+                scrollbarRef.value.scrollTo({ top: 0, left: 0 });
+              }}
+            />
+          </div>
+        </div>
+      </div>
+    );
+  }
+});

+ 525 - 0
src/components/TheAuth/index.vue

@@ -0,0 +1,525 @@
+<template>
+  <div class="theAuth">
+    <div class="theTitle"></div>
+    <!-- {/* <i class={styles.iconClose} onClick={() => emit('close')}></i> */} -->
+    <div class="authContent">
+      <div class="steps">
+        <div class="scrollbarRef">
+          <div v-if="brower.ios">
+            <div v-if="step === 1">
+              <div class="step">
+                <div class="stepNum">01</div>
+                <div class="stepContent">
+                  <p class="txt">
+                    点击下方【下载证书】按钮,下载数据安全证书安装包
+                  </p>
+
+                  <div>
+                    <div class="downloadCert" @click="onDownload"></div>
+                  </div>
+                </div>
+              </div>
+
+              <div class="step">
+                <div class="stepNum">02</div>
+                <div class="stepContent">
+                  <p class="txt">
+                    双击<span>《安全证书.p12》</span>
+                    ,输入钥匙串密码 ,点击
+                    <span>【修改钥匙串】</span>
+                    <span style=" color: #777">
+                      (若无此步骤则忽略)
+                    </span>
+                  </p>
+                  <div class="imgs imgs2">
+                    <img src="./images/mac/1.png" class="m1" />
+                    <img src="./images/mac/6.png" class="m6" />
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 2">
+              <div class="stepNum">03</div>
+              <div class="stepContent">
+                <p class="txt">
+                  输入证书密码:
+                  <span class="red" style="text-decoration: underline">
+                    dayaedu.com
+                  </span>
+                  ,点击
+                  <span>【好】</span>
+                </p>
+
+                <div class="imgs">
+                  <img src="./images/mac/2.png" class="m2" />
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 3">
+              <div class="stepNum">04</div>
+              <div class="stepContent">
+                <p class="txt">
+                  <span class="red">重启浏览器</span>
+                  (在电脑屏幕左上方选择当前浏览器并点击
+                  <span>【退出】</span>),再重新打开管乐迷后台网址
+                </p>
+                <div class="imgs">
+                  <img src="./images/mac/3.png" class="m3" />
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 4">
+              <div class="stepNum">05</div>
+              <div class="stepContent">
+                <p class="txt">
+                  在【选择证书】弹窗中点击<span>【确定】</span>按钮
+                </p>
+                <div class="imgs">
+                  <img src="./images/mac/4.png" class="m4" />
+                </div>
+              </div>
+            </div>
+
+            <div v-if="step === 5">
+              <div class="step">
+                <div class="stepNum">06</div>
+                <div class="stepContent">
+                  <p class="txt">
+                    输入您的电脑密码,点击<span>【始终允许】</span>
+                    <span style="color: #777;">
+                      (若无此步骤则忽略)
+                    </span>
+                  </p>
+                  <div class="imgs">
+                    <img src="./images/mac/5.png" class="m5" />
+                  </div>
+                </div>
+              </div>
+              <div class="step">
+                <div class="stepNum">07</div>
+                <div class="stepContent">
+                  <p class="txt">证书安装完成,开始使用管乐迷后台吧!</p>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div v-else>
+            <div v-if="step === 1">
+              <div class="step">
+                <div class="stepNum">01</div>
+                <div class="stepContent">
+                  <p class="txt">
+                    点击下方【下载证书】按钮,下载数据安全证书安装包
+                  </p>
+                  <div>
+                    <div class="downloadCert" @click="onDownload"></div>
+                  </div>
+                </div>
+              </div>
+              <div class="step">
+                <div class="stepNum">02</div>
+                <div class="stepContent">
+                  <p class="txt">
+                    双击<span>《安全证书.pfx》</span>,出现弹窗后点击
+                    <span>【下一步】</span>
+                  </p>
+                  <div class="imgs imgs2">
+                    <img src="./images/window/1.png" class="w1" />
+                    <img src="./images/window/2.png" class="w2" />
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 2">
+              <div class="stepNum">03</div>
+              <div class="stepContent">
+                <p class="txt">点击<span>【下一步】</span></p>
+                <div class="imgs">
+                  <img src="./images/window/3.png" class="w3" />
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 3">
+              <div class="stepNum">04</div>
+              <div class="stepContent">
+                <p class="txt">点击<span>【下一步】</span></p>
+                <div class="imgs">
+                  <img src="./images/window/4.png" class="w4" />
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 4">
+              <div class="stepNum">05</div>
+              <div class="stepContent">
+                <p class="txt">点击<span>【下一步】</span></p>
+                <div class="imgs">
+                  <img src="./images/window/8.png" class="w8" />
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 5">
+              <div class="stepNum">06</div>
+              <div class="stepContent">
+                <p class="txt">点击<span>【完成】</span></p>
+                <div class="imgs">
+                  <img src="./images/window/5.png" class="w5" />
+                </div>
+              </div>
+            </div>
+
+            <div class="step" v-if="step === 6">
+              <div class="stepNum">07</div>
+              <div class="stepContent">
+                <p class="txt">点击<span>【确定】</span></p>
+                <div class="imgs">
+                  <img src="./images/window/6.png" class="w6" />
+                </div>
+              </div>
+            </div>
+
+            <div v-if="step === 7">
+              <div class="step">
+                <div class="stepNum">08</div>
+                <div class="stepContent">
+                  <p class="txt">
+                    <span class="red">重启浏览器</span>
+                    ,打开管乐迷后台网址
+                  </p>
+                </div>
+              </div>
+
+              <div class="step">
+                <div class="stepNum">09</div>
+                <div class="stepContent">
+                  <p class="txt">
+                    在【选择证书】弹窗中点击<span>【确定】</span>
+                    按钮,证书安装完成,开始使用管乐迷后台吧!
+                  </p>
+                  <img src="./images/window/7.png" class="w7" />
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="btnGroup">
+        <div
+          v-if="step > 1"
+          class="btn btnUp"
+          :style="{ cursor: step <= 1 ? 'not-allowed' : 'pointer' }"
+          @click="changeUpDown('up')"
+        ></div>
+        <div
+          :class="['btn', 'btnDown', step === maxStep && 'btnDone']"
+          @click="changeUpDown('down')"
+        ></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { browser } from "@/utils";
+export default {
+  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/gym/安全证书.p12?v=${new Date().getTime()}`
+        );
+      } else {
+        window.open("https://oss.dayaedu.com/https-ssl/安全证书.pfx");
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.theAuth {
+  // background-color: #fff;
+  width: 724px;
+  padding-top: 130px;
+  box-shadow: none;
+  position: relative;
+  // overflow: hidden;
+
+  .iconClose {
+    position: absolute;
+    right: 38px;
+    top: 118px;
+    width: 16px;
+    height: 16px;
+    background: url("./images/icon-close.png") no-repeat center;
+    background-size: contain;
+    cursor: pointer;
+  }
+}
+
+.theTitle {
+  position: absolute;
+  top: 0;
+  left: 0;
+  background: url("./images/auth-title-bg.png") top center no-repeat;
+  background-size: 100%;
+  width: 100%;
+  height: 142px;
+}
+
+.authContent {
+  // background: linear-gradient(180deg, #e1f8ff 0%, #bbe7fd 100%);
+  background: linear-gradient(180deg, #d3ede8 0%, #cff5fa 100%);
+  border-radius: 0 0 23px 23px;
+  padding-top: 8px;
+}
+
+.steps {
+  margin: 11px 27px 0;
+  padding-top: 12px;
+
+  background: linear-gradient(
+    180deg,
+    rgba(255, 255, 255, 0.7) 0%,
+    rgba(255, 255, 255, 0.7) 88%,
+    #c0e7eb 100%
+  );
+  border-radius: 18px;
+  border: 1px solid #ffffff;
+
+  .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;
+    }
+  }
+}
+
+.step {
+  display: flex;
+  align-items: flex-start;
+  padding-bottom: 35px;
+
+  .stepNum {
+    width: 33px;
+    height: 33px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: center;
+    background: #c1ece8;
+    border: 1px solid #ffffff;
+    font-size: 14px;
+    font-weight: 600;
+    color: #131415;
+    border-radius: 50%;
+    margin-right: 14px;
+    flex-shrink: 0;
+  }
+
+  .stepContent {
+    .txt {
+      font-size: 14px;
+      font-family: PingFangSC, PingFang SC;
+      font-weight: 600;
+      color: #000000;
+      line-height: 33px;
+
+      span {
+        color: #14928a;
+      }
+
+      .red {
+        color: #e80000;
+      }
+    }
+  }
+
+  .downloadCert {
+    margin-top: 14px;
+    width: 165px;
+    height: 39px;
+    background: url("./images/downlowdCert.png") no-repeat center;
+    background-size: contain;
+    border-radius: 50px;
+    cursor: pointer;
+  }
+
+  .imgs2 {
+    display: flex;
+    align-items: flex-start;
+    padding-top: 12px;
+
+    .m6,
+    .w2 {
+      margin-left: 38px;
+    }
+  }
+
+  img {
+    border-radius: 2px;
+  }
+
+  .w1 {
+    width: 90px;
+    height: 98px;
+  }
+
+  .w2 {
+    width: 250px;
+    height: 258px;
+  }
+
+  .w3,
+  .w4,
+  .w5,
+  .w8 {
+    margin-top: 12px;
+    width: 313px;
+    height: 322px;
+  }
+
+  .w6 {
+    margin-top: 12px;
+    width: 196px;
+    height: 207px;
+  }
+
+  .w7 {
+    margin-top: 8px;
+    width: 96%;
+  }
+
+  .m1 {
+    width: 73px;
+    height: 80px;
+  }
+
+  .m6 {
+    width: 263px;
+  }
+
+  .m3 {
+    margin-top: 8px;
+    width: 358px;
+  }
+
+  .m2,
+  .m4,
+  .m5 {
+    margin-top: 8px;
+    width: 100%;
+  }
+
+  .m2 {
+    width: 90%;
+  }
+
+  .moreImg {
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+
+    img {
+      width: 47%;
+      margin-bottom: 32px;
+
+      &:nth-child(2n + 2) {
+        margin-left: 6%;
+      }
+    }
+  }
+}
+
+.btnGroup {
+  padding: 25px 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  // :global {
+  //   .n-button {
+  //     height: 37px !important;
+  //     width: 223px !important;
+  //     --n-border: none !important;
+  //     --n-border-hover: none !important;
+  //     --n-border-pressed: none !important;
+  //     --n-border-focus: none !important;
+  //     background: linear-gradient(305deg, #15BBFF 0%, #1784FF 100%);
+  //     border-radius: 22px;
+  //     font-size: 15Px;
+  //     font-family: AlimamaShuHeiTi, AlimamaShuHeiTi;
+  //     font-weight: bold;
+  //     color: #FFFFFF;
+  //     line-height: 22px;
+  //     letter-spacing: 1px;
+  //   }
+  // }
+  .btn {
+    width: 165px;
+    height: 39px;
+    cursor: pointer;
+    transition: opacity 0.2s ease;
+    &:hover {
+      opacity: 0.9;
+      transition: opacity 0.2s ease;
+    }
+
+    + .btn {
+      margin-left: 16px;
+    }
+  }
+
+  .btnUp {
+    background: url("./images/btn-up.png") no-repeat center center;
+    background-size: contain;
+  }
+
+  .btnDown {
+    background: url("./images/btn-down.png") no-repeat center center;
+    background-size: contain;
+  }
+
+  .btnDone {
+    background: url("./images/btn-done.png") no-repeat center center;
+    background-size: contain;
+  }
+}
+</style>

+ 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>

+ 43 - 31
src/views/resetTeaming/modals/payMember.vue

@@ -1,7 +1,13 @@
 <template>
   <div class="teamCourseList">
-    <p class="coreTitle">云教练
-      <el-form-item label="是否展示" prop="isShowMemberForPay" v-if="allCourseCurrentPrice==0"  class="titleFormItem">
+    <p class="coreTitle">
+      云教练
+      <el-form-item
+        label="是否展示"
+        prop="isShowMemberForPay"
+        v-if="allCourseCurrentPrice == 0"
+        class="titleFormItem"
+      >
         <el-select
           style="width: 100% !important"
           v-model="form.isShowMemberForPay"
@@ -15,7 +21,7 @@
     </p>
 
     <el-table
-    class="courseTable"
+      class="courseTable"
       :data="form.memberList"
       style="width: 100% !important; background: #f9f9f9"
       :header-cell-style="{ background: '#F9F9F9', color: '#444' }"
@@ -32,7 +38,7 @@
             :rules="{
               required: true,
               message: '请选择会员类型',
-              trigger: 'change',
+              trigger: 'change'
             }"
           >
             <el-select
@@ -62,7 +68,7 @@
             :rules="{
               required: true,
               message: '请选择是否必选',
-              trigger: 'change',
+              trigger: 'change'
             }"
           >
             <el-select
@@ -82,7 +88,7 @@
           </el-form-item>
         </template>
       </el-table-column>
-            <el-table-column
+      <el-table-column
         label="自动激活"
         prop="autoActivationFlag"
         key="autoActivationFlag"
@@ -95,7 +101,7 @@
             :rules="{
               required: true,
               message: '请选择是否自动激活',
-              trigger: 'change',
+              trigger: 'change'
             }"
           >
             <el-select
@@ -123,8 +129,8 @@
               {
                 required: true,
                 message: '请选择会员周期',
-                trigger: 'blur',
-              },
+                trigger: 'blur'
+              }
             ]"
           >
             <el-input-number
@@ -133,7 +139,7 @@
               v-model="form.memberList[scope.$index].num"
               :controls="false"
               :precision="0"
-              :min="0"
+              :min="1"
               @change="
                 changeMemberperiodEnum(form.memberList[scope.$index].period)
               "
@@ -156,8 +162,8 @@
               {
                 required: true,
                 message: '请选择周期单位',
-                trigger: 'blur',
-              },
+                trigger: 'blur'
+              }
             ]"
           >
             <el-select
@@ -181,7 +187,7 @@
         prop="actualAmount"
         key="actualAmount"
         width="170px"
-        v-if="form.calenderFeeType !='TEACHER'"
+        v-if="form.calenderFeeType != 'TEACHER'"
       >
         <template slot="header">
           <p style="position: relative">
@@ -190,7 +196,7 @@
               <div slot="content">学生实际缴费金额</div>
               <i
                 class="el-icon-question"
-                 style="font-size: 18px; color: #f56c6c;top:2px;position: relative"
+                style="font-size: 18px; color: #f56c6c;top:2px;position: relative"
               ></i>
             </el-tooltip>
           </p>
@@ -202,9 +208,9 @@
               {
                 required: true,
                 message: '请输入售价',
-                trigger: 'blur',
+                trigger: 'blur'
               },
-              { validator: validateMember, trigger: 'blur' },
+              { validator: validateMember, trigger: 'blur' }
             ]"
           >
             <!-- $listeners.moneyChange -->
@@ -218,7 +224,7 @@
               @change="change"
               :disabled="
                 !form.memberList[scope.$index].period ||
-                !form.memberList[scope.$index].num
+                  !form.memberList[scope.$index].num
               "
               placeholder="请输入售价"
             />
@@ -237,7 +243,7 @@
             :rules="{
               required: true,
               message: '请输入原价',
-              trigger: 'blur',
+              trigger: 'blur'
             }"
           >
             <!-- $listeners.moneyChange -->
@@ -279,7 +285,13 @@ import { objectToOptions } from "@/utils";
 import { memberEnumList } from "@/utils/searchArray";
 import { getMemberFee } from "../api";
 export default {
-  props: ["form", "memberRankList", "organId", "rulesForm",'showAutoActivationFlag'],
+  props: [
+    "form",
+    "memberRankList",
+    "organId",
+    "rulesForm",
+    "showAutoActivationFlag"
+  ],
   data() {
     return {
       charges: [],
@@ -289,7 +301,7 @@ export default {
       boolOptionsOptions: objectToOptions(boolOptions),
       memberEnumList,
       memberPrice: {},
-      getMemberFeeFlag: true,
+      getMemberFeeFlag: true
     };
   },
   mounted() {},
@@ -367,7 +379,7 @@ export default {
           try {
             const res = await getMemberFee({
               rankId: val,
-              organId: this.organId,
+              organId: this.organId
             });
             this.memberPrice = res.data;
             this.getMemberFeeFlag = true;
@@ -428,7 +440,7 @@ export default {
         return callback();
       }
       return callback(new Error(`定价应在${min}-${max}之间`));
-    },
+    }
   },
   watch: {
     form: {
@@ -437,19 +449,19 @@ export default {
         if (val.memberList[0].memberRankSettingId && !this.memberPrice.id) {
           this.changeMember(val.memberList[0].memberRankSettingId);
         }
-      },
-    },
+      }
+    }
   },
-  computed:{
-    allCourseCurrentPrice(){
-      let price = 0
+  computed: {
+    allCourseCurrentPrice() {
+      let price = 0;
       this.form.memberList.forEach(element => {
-        price+=element.actualAmount
+        price += element.actualAmount;
       });
-      if(price >0){
-        this.form.isShowMemberForPay = true
+      if (price > 0) {
+        this.form.isShowMemberForPay = true;
       }
-      return price
+      return price;
     }
   }
 };