Browse Source

更新登录

lex 2 years ago
parent
commit
8f527d958b
40 changed files with 930 additions and 114 deletions
  1. 1 0
      package.json
  2. 20 0
      src/colexiu-project.code-workspace
  3. 11 19
      src/helpers/request.ts
  4. 17 3
      src/helpers/utils.ts
  5. 16 0
      src/router/routes-admin.ts
  6. 2 1
      src/state.ts
  7. 22 0
      src/views/App.tsx
  8. 88 6
      src/views/login/components/form/index.tsx
  9. 1 1
      src/views/login/components/img-code/index.tsx
  10. 3 0
      src/views/login/components/qr-login/index.module.less
  11. 200 0
      src/views/login/components/qr-login/index.tsx
  12. 0 0
      src/views/login/components/teacher-auth/index.module.less
  13. 88 0
      src/views/login/components/teacher-auth/index.tsx
  14. BIN
      src/views/login/images/teacher_1.png
  15. BIN
      src/views/login/images/teacher_2.png
  16. 90 54
      src/views/login/index.tsx
  17. 0 0
      src/views/login/loginState.ts
  18. BIN
      src/views/roleAuth/images/bg_bottom.png
  19. BIN
      src/views/roleAuth/images/bg_center.png
  20. BIN
      src/views/roleAuth/images/bg_left_bottom.png
  21. BIN
      src/views/roleAuth/images/bg_right_center.png
  22. BIN
      src/views/roleAuth/images/bg_top.png
  23. BIN
      src/views/roleAuth/images/midi_money.png
  24. BIN
      src/views/roleAuth/images/midi_upload.png
  25. BIN
      src/views/roleAuth/images/music_main.png
  26. BIN
      src/views/roleAuth/images/num1.png
  27. BIN
      src/views/roleAuth/images/num2.png
  28. BIN
      src/views/roleAuth/images/num3.png
  29. BIN
      src/views/roleAuth/images/num4.png
  30. BIN
      src/views/roleAuth/images/o1.png
  31. BIN
      src/views/roleAuth/images/o2.png
  32. BIN
      src/views/roleAuth/images/o3.png
  33. BIN
      src/views/roleAuth/images/o4.png
  34. BIN
      src/views/roleAuth/images/teacher_main.png
  35. 33 0
      src/views/roleAuth/musicAuth/index.module.less
  36. 86 0
      src/views/roleAuth/musicAuth/index.tsx
  37. 33 0
      src/views/roleAuth/teacherAuth/index.module.less
  38. 101 0
      src/views/roleAuth/teacherAuth/index.tsx
  39. 4 0
      vite.config.ts
  40. 114 30
      yarn.lock

+ 1 - 0
package.json

@@ -30,6 +30,7 @@
     "dayjs": "^1.10.7",
     "element-plus": "^2.2.5",
     "html-to-image": "^1.9.0",
+    "js-cookie": "^3.0.1",
     "loaders.css": "^0.1.2",
     "mitt": "^3.0.0",
     "normalize.css": "^8.0.1",

+ 20 - 0
src/colexiu-project.code-workspace

@@ -0,0 +1,20 @@
+{
+	"folders": [
+		{
+			"path": ".."
+		},
+		{
+			"path": "../../mdaya"
+		},
+		{
+			"path": "../../h5-colexiu"
+		},
+		{
+			"path": "../../colexiu-manage"
+		},
+		{
+			"path": "../../dy-admin-live"
+		}
+	],
+	"settings": {}
+}

+ 11 - 19
src/helpers/request.ts

@@ -1,7 +1,6 @@
 import { extend } from 'umi-request'
 import cleanDeep from 'clean-deep'
-import { browser } from '@/helpers/utils'
-import { setLogout, setLoginError } from '@/state'
+import { browser, getAuth } from '@/helpers/utils'
 import { postMessage } from './native-message'
 import { ElMessage } from 'element-plus'
 
@@ -33,7 +32,7 @@ let initRequest = false
 request.interceptors.request.use(
   (url, options: any) => {
     initRequest = options.initRequest || false
-    const Authorization = sessionStorage.getItem('Authorization') || ''
+    const Authorization = getAuth() || ''
     const authHeaders: any = {}
     if (
       Authorization &&
@@ -69,27 +68,20 @@ request.interceptors.response.use(
     }
     const data = await res.clone().json()
     if (data.code !== 200 && data.errCode !== 0) {
-      let msg = data.msg || data.message || '处理失败,请重试'
-      if (initRequest) {
-        if (data.code === 403 || data.code === 401) {
-          setLogout()
-        } else {
-          setLoginError()
-        }
+      const msg = data.msg || data.message || '处理失败,请重试'
+
+      if (data.code === 403 || data.code === 401) {
+        // window.location.href = location.origin
+        ElMessage.error(msg)
       }
+
       if (!(data.code === 403 || data.code === 401)) {
         ElMessage.error(msg)
       }
-      const browserInfo = browser()
+
       if (data.code === 403) {
-        msg += '403'
-        if (browserInfo.isApp) {
-          postMessage({
-            api: 'login'
-          })
-        } else {
-          setLogout()
-        }
+        // window.location.href = location.origin
+        // ElMessage.error(msg)
       }
       throw new Error(msg)
     }

+ 17 - 3
src/helpers/utils.ts

@@ -1,5 +1,6 @@
 import dayjs from 'dayjs'
 import numeral from 'numeral'
+import Cookies from 'js-cookie'
 import { state as helpState } from './helpState'
 
 export const browser = () => {
@@ -36,7 +37,7 @@ export const getRandomKey = () => {
  * 删除token
  */
 export const removeAuth = () => {
-  sessionStorage.removeItem('Authorization')
+  Cookies.remove('token')
 }
 
 /**
@@ -45,14 +46,27 @@ export const removeAuth = () => {
  * @returns {void}
  */
 export const setAuth = (token: any) => {
-  sessionStorage.setItem('Authorization', token)
+  // 7天过期
+  Cookies.set('token', token, { expires: 7 })
 }
 
 /**
  * 获取token
  */
 export const getAuth = () => {
-  sessionStorage.getItem('Authorization')
+  let token = Cookies.get('token')
+  token = token ? JSON.parse(token) : {}
+  return token.token || null
+}
+
+/**
+ * 获取登录用户类型
+ * @returns {string}
+ */
+export const getUserType = () => {
+  let token = Cookies.get('token')
+  token = token ? JSON.parse(token) : {}
+  return token.userType || null
 }
 
 export const getWeekCh = (week: number, type = 0) => {

+ 16 - 0
src/router/routes-admin.ts

@@ -18,6 +18,22 @@ export default [
     path: '/downLoad',
     name: 'downLoad',
     component: () => import('@/views/downLoad/index')
+  },
+  {
+    path: '/teacherAuth',
+    name: 'teacherAuth',
+    component: () => import('@/views/roleAuth/teacherAuth'),
+    meta: {
+      title: '老师认证'
+    }
+  },
+  {
+    path: '/musicAuth',
+    name: 'musicAuth',
+    component: () => import('@/views/roleAuth/musicAuth'),
+    meta: {
+      title: '音乐人认证'
+    }
   }
   // {
   //   path: '/',

+ 2 - 1
src/state.ts

@@ -6,5 +6,6 @@ export const state = reactive({
     status: 'init' as status,
     data: {} as any
   },
-  loginPopupStatus: false // 登录弹窗状态
+  loginPopupStatus: false, // 登录弹窗状态
+  loginPopupTimer: null as any // 登录弹窗定时器
 })

+ 22 - 0
src/views/App.tsx

@@ -6,9 +6,28 @@ import { RouterView } from 'vue-router'
 import Login from './login'
 import styles from './App.module.less'
 import { state } from '@/state'
+import { getAuth, getUserType } from '@/helpers/utils'
+import request from '@/helpers/request'
 
 export default defineComponent({
   name: 'App',
+  async mounted() {
+    const token = getAuth()
+    const userType = getUserType()
+    // 判断是否有token,token和userType 判断是否登录
+    if (!token) {
+      return
+    }
+
+    try {
+      const url =
+        userType === 'TEACHER'
+          ? '/api-website/teacher/queryUserInfo'
+          : '/api-website/student/queryUserInfo'
+      const res = await request.get(url)
+      console.log(res)
+    } catch {}
+  },
   render() {
     return (
       <>
@@ -20,9 +39,12 @@ export default defineComponent({
           <ElDialog
             modelValue={state.loginPopupStatus}
             onUpdate:modelValue={val => (state.loginPopupStatus = val)}
+            closeOnClickModal={false}
+            closeOnPressEscape={false}
           >
             <Login
               onClose={() => {
+                clearTimeout(state.loginPopupTimer)
                 state.loginPopupStatus = false
               }}
             />

+ 88 - 6
src/views/login/components/form/index.tsx

@@ -1,7 +1,17 @@
-import { ElButton, ElForm, ElFormItem, ElInput, ElLink } from 'element-plus'
+import request from '@/helpers/request'
+import {
+  ElButton,
+  ElForm,
+  ElFormItem,
+  ElInput,
+  ElLink,
+  ElMessage
+} from 'element-plus'
 import { defineComponent } from 'vue'
+import Cookies from 'js-cookie'
 import ImgCode from '../img-code'
 import styles from './index.module.less'
+import { setAuth } from '@/helpers/utils'
 
 const validatePassword = (
   rule: any,
@@ -26,10 +36,19 @@ export default defineComponent({
         | 'student-login'
         | 'teacher-register'
         | 'student-register'
+    },
+    onClose: {
+      type: Function,
+      default: () => {}
+    },
+    onChange: {
+      type: Function,
+      default: (type: string) => {}
     }
   },
   data() {
     return {
+      loading: false,
       codeDsiable: false,
       codeStatus: false, // 是否显示图形验证码
       codeTimer: 120, // 发短信时长
@@ -58,15 +77,76 @@ export default defineComponent({
     onSubmit() {
       ;(this as any).$refs.loginForm.validate(async (valid: boolean) => {
         if (valid) {
-          console.log(valid)
+          if (this.type === 'teacher-login') {
+            const params = {
+              isSurportRegister: false,
+              loginUserType: 'TEACHER'
+            }
+            await this.onLogin(params)
+          } else if (this.type === 'student-login') {
+            const params = {
+              isSurportRegister: false,
+              loginUserType: 'STUDENT'
+            }
+            await this.onLogin(params)
+          } else if (this.type === 'teacher-register') {
+            const params = {
+              isSurportRegister: true,
+              loginUserType: 'TEACHER'
+            }
+            await this.onLogin(params)
+          } else if (this.type === 'student-register') {
+            const params = {
+              isSurportRegister: true,
+              loginUserType: 'STUDENT'
+            }
+            await this.onLogin(params)
+          }
         }
       })
+    },
+    async onLogin(params: any) {
+      this.loading = true
+      try {
+        const form = this.form
+        const res = await request.post('/api-auth/smsLogin', {
+          requestType: 'form',
+          data: {
+            clientId: 'website',
+            clientSecret: 'website',
+            phone: form.username,
+            smsCode: form.code,
+            ...params
+          }
+        })
+        console.log(res, '登录')
+        const { authentication } = res.data
+        const token =
+          authentication.token_type + ' ' + authentication.access_token
+        setAuth(
+          JSON.stringify({
+            token,
+            loginUserType: params.loginUserType
+          })
+        )
+        if (this.type === 'teacher-login' || this.type === 'student-login') {
+          this.onClose()
+          window.location.reload()
+        } else if (
+          this.type === 'teacher-register' ||
+          this.type === 'student-register'
+        ) {
+          this.onChange('register-success')
+        }
+      } catch (e: any) {
+        console.log(e)
+      }
+      this.loading = false
+    },
+    onResetFields() {
+      ;(this as any).$refs.loginForm.resetFields()
     }
   },
-  unmounted() {
-    console.log('unmounted')
-    // ;(this as any).$refs.loginForm.resetFields()
-  },
   render() {
     return (
       <ElForm
@@ -129,6 +209,8 @@ export default defineComponent({
             type="primary"
             class={styles.btnStyles}
             onClick={this.onSubmit}
+            disabled={this.loading}
+            loading={this.loading}
           >
             {this.type === 'teacher-login' || this.type === 'student-login'
               ? '登 录'

+ 1 - 1
src/views/login/components/img-code/index.tsx

@@ -12,7 +12,7 @@ export default defineComponent({
   },
   data() {
     return {
-      identifyingCode: ''
+      identifyingCode: location.origin + '/api-admin/code/getImageCode'
     }
   },
   render() {

+ 3 - 0
src/views/login/components/qr-login/index.module.less

@@ -0,0 +1,3 @@
+.txt {
+  color: var(--el-color-primary);
+}

+ 200 - 0
src/views/login/components/qr-login/index.tsx

@@ -0,0 +1,200 @@
+import { ElButton, ElIcon, ElLink } from 'element-plus'
+import QrcodeVue from 'qrcode.vue'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import Cookies from 'js-cookie'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { setAuth } from '@/helpers/utils'
+
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `../../images/${fileName}`
+  const modules = import.meta.globEager('../../images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'qrCode',
+  props: {
+    onChange: {
+      type: Function,
+      default: (type: string) => {}
+    }
+  },
+  data() {
+    return {
+      qrCode: '',
+      isScan: false, // 是否扫码
+      scanCode: '',
+      codeTimerOut: false, // 是否超时或登录过期
+      // 状态 no_scan 未扫码 scanned 已扫码 succeed 登录成功 filed 登录失败
+      codeStatus: 'no_scan' as 'no_scan' | 'scanned' | 'succeed' | 'filed' // 扫码状态
+    }
+  },
+  async mounted() {
+    try {
+      //
+      const scanCode = sessionStorage.getItem('scanCode')
+      if (scanCode) {
+        this.scanCode = scanCode
+        sessionStorage.removeItem('scanCode')
+      } else {
+        await this.getCode()
+      }
+      this.qrCode = `https://dev.colexiu.com/student/#/scanLogin?code=${this.scanCode}`
+      state.loginPopupTimer = setInterval(async () => {
+        await this.getList()
+      }, 5000)
+    } catch {}
+  },
+  methods: {
+    async getCode() {
+      try {
+        const res = await request.get('/api-auth/getQRLoginCode', {
+          params: {
+            clientId: 'website',
+            clientSecret: 'website'
+          }
+        })
+        this.scanCode = res.data.code
+        this.codeStatus = res.data.codeStatus
+        sessionStorage.setItem('scanCode', res.data.code)
+      } catch {}
+    },
+    async getList() {
+      try {
+        console.log(this.scanCode)
+        const res = await request.get('/api-auth/pollingQRLoginCode', {
+          params: {
+            code: this.scanCode
+          }
+        })
+        console.log(res, 'getlist')
+        const result = res.data
+        // 判断是否有数据,如果没有数据则认定为超时
+        if (!result) {
+          this.codeTimerOut = true
+          this.removeTimer()
+          return
+        }
+        this.codeStatus = result.codeStatus
+        if (result.codeStatus === 'succeed') {
+          this.removeTimer()
+          const { authentication, userType } = result
+          const token =
+            authentication.token_type + ' ' + authentication.access_token
+          setAuth(
+            JSON.stringify({
+              token,
+              loginUserType: userType
+            })
+          )
+          // this.onClose()
+          // window.location.reload()
+        } else if (result.codeStatus === 'filed') {
+          this.removeTimer()
+        } else if (result.codeStatus === 'scanned') {
+          this.isScan = true
+        }
+      } catch {
+        console.log('error')
+        this.codeTimerOut = true
+        this.removeTimer()
+      }
+    },
+    removeTimer() {
+      // 取消定时器
+      this.codeStatus = 'no_scan'
+      this.isScan = false
+      clearInterval(state.loginPopupTimer)
+    }
+  },
+  render() {
+    return (
+      <div class={'text-center pt-4'}>
+        <div
+          class={'absolute top-2 right-2 z-10'}
+          onClick={() => {
+            this.removeTimer()
+            this.onChange('login')
+          }}
+        >
+          <img
+            src={getAssetsHomeFile('icon_pc_login.png')}
+            class="w-14 h-14 cursor-pointer"
+          />
+        </div>
+        {this.isScan ? (
+          <>
+            <ElIcon
+              class="mx-auto w-[138px] h-[138px] align-middle"
+              size={70}
+              color="var(--el-color-primary)"
+            >
+              <CircleCheck />
+            </ElIcon>
+            <p class="text-lg text-[#666] mt-6">扫描成功</p>
+            <p class="font-medium text-[#1A1A1A] text-[20px] pt-4">
+              请在手机上根据提示确认登录
+            </p>
+
+            <ElLink
+              type="primary"
+              underline={false}
+              class="m-auto mt-3"
+              onClick={async () => {
+                await this.getCode()
+                this.isScan = false
+              }}
+            >
+              返回扫描二维码
+            </ElLink>
+          </>
+        ) : (
+          <>
+            <div
+              class="mx-auto w-[138px] h-[138px] align-middle flex items-center justify-center relative overflow-hidden rounded"
+              style={{
+                boxShadow: '0px 0px 8px 0px rgba(0, 0, 0, 0.18)'
+              }}
+            >
+              <QrcodeVue value={this.qrCode} size={128} />
+              {/* 登录是否过期 */}
+              {this.codeTimerOut && (
+                <div class="absolute inset-0 bg-black bg-opacity-75 flex items-center justify-center flex-col">
+                  <p class="text-white text-sm pb-2">二维码已失效</p>
+                  <ElButton
+                    type="primary"
+                    size="small"
+                    onClick={async () => {
+                      this.codeTimerOut = false
+                      await this.getCode()
+                      state.loginPopupTimer = setInterval(async () => {
+                        await this.getList()
+                      }, 5000)
+                    }}
+                  >
+                    点击刷新
+                  </ElButton>
+                </div>
+              )}
+            </div>
+            <div class="flex items-center justify-center pt-8 font-normal">
+              <img
+                class="w-9 h-9 align-middle mr-4"
+                src={getAssetsHomeFile('icon_scan.png')}
+              />
+
+              <div class={['text-left leading-[22px]']}>
+                <p>
+                  打开<span class={styles.txt}>酷乐秀学生端APP</span>
+                </p>
+                <p>扫一扫登录</p>
+              </div>
+            </div>
+          </>
+        )}
+      </div>
+    )
+  }
+})

+ 0 - 0
src/views/login/components/teacher-auth/index.module.less


+ 88 - 0
src/views/login/components/teacher-auth/index.tsx

@@ -0,0 +1,88 @@
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import teacher1 from '../../images/teacher_1.png'
+import teacher2 from '../../images/teacher_2.png'
+import { ElButton, ElCol, ElRow } from 'element-plus'
+
+export default defineComponent({
+  name: 'teacher-auth',
+  props: {
+    onClose: {
+      type: Function,
+      default: () => {}
+    }
+  },
+  methods: {
+    onDetail(type = 'teacher' as 'teacher' | 'music') {
+      this.onClose()
+      if (type === 'teacher') {
+        this.$router.push('/teacherAuth')
+      } else if (type === 'music') {
+        this.$router.push('/musicAuth')
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        <div class="text-[#1a1a1a] font-medium text-xl text-center after:w-4 after:h-[3px] after:rounded-sm after:bg-[#2DC7AA] after:block after:m-auto after:mt-2">
+          酷乐秀认证
+        </div>
+
+        <div class="text-center text-gray-500 pt-4 pb-5">
+          完成酷乐秀认证能开启更多功能!
+        </div>
+
+        <ElRow gutter={10}>
+          <ElCol span={12} class="cursor-pointer">
+            <div
+              class="border-neutral-200 border-solid border rounded flex items-center p-4"
+              onClick={() => {
+                this.onDetail('teacher')
+              }}
+            >
+              <img src={teacher1} class="w-16 h-[84px]" />
+              <div class="pl-3">
+                <p class="text-slate-700 text-[16px] font-medium">老师认证</p>
+                <p class="text-gray-400 text-[14px] leading-5 pt-1">
+                  开启线上
+                  <br />
+                  教学之旅
+                </p>
+              </div>
+            </div>
+          </ElCol>
+          <ElCol span={12} class="cursor-pointer">
+            <div
+              class="border-neutral-200 border-solid border rounded flex items-center py-4 pl-4 pr-0"
+              onClick={() => {
+                this.onDetail('music')
+              }}
+            >
+              <img src={teacher2} class="w-16 h-[84px]" />
+              <div class="pl-3">
+                <p class="text-slate-700 text-[16px] font-medium">音乐人认证</p>
+                <p class="text-gray-400 text-[14px] leading-5 pt-1">
+                  上传曲谱
+                  <br />
+                  获取收益
+                </p>
+              </div>
+            </div>
+          </ElCol>
+        </ElRow>
+
+        <ElButton
+          type="primary"
+          class="w-full mt-4"
+          style={{ height: '40px' }}
+          onClick={() => {
+            this.onClose()
+          }}
+        >
+          下次再说
+        </ElButton>
+      </>
+    )
+  }
+})

BIN
src/views/login/images/teacher_1.png


BIN
src/views/login/images/teacher_2.png


+ 90 - 54
src/views/login/index.tsx

@@ -1,8 +1,10 @@
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
-import { ElButton, ElTabPane, ElTabs, ElTooltip } from 'element-plus'
+import { ElButton, ElIcon, ElTabPane, ElTabs, ElTooltip } from 'element-plus'
 import Form from './components/form'
 import QrcodeVue from 'qrcode.vue'
+import QrLogin from './components/qr-login'
+import TeacherAuth from './components/teacher-auth'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -20,18 +22,31 @@ export default defineComponent({
   },
   data() {
     return {
-      tipStatus: false,
-      qrCode: 'https://dev.colexiu.com',
       qrCodeDownLoad: 'http://dev.colexiu.com/student/#/download',
-      type: 'login' as 'login' | 'qr-login' | 'register' | 'register-success'
+      type: 'login' as
+        | 'login'
+        | 'qr-login'
+        | 'register'
+        | 'register-success'
+        | 'teacher-auth',
+      registerType: 'teacher' as 'teacher' | 'student',
+      loginType: 'teacher' as 'teacher' | 'student'
     }
   },
-  mounted() {
-    this.$nextTick(() => {
-      setTimeout(() => {
-        this.tipStatus = true
-      }, 300)
-    })
+  methods: {
+    onReset(type: 'login' | 'register') {
+      if (type === 'login') {
+        ;(this as any).$refs.teacherLogin &&
+          (this as any).$refs.teacherLogin.onResetFields()
+        ;(this as any).$refs.studentLogin &&
+          (this as any).$refs.studentLogin.onResetFields()
+      } else if (type === 'register') {
+        ;(this as any).$refs.teacherRegister &&
+          (this as any).$refs.teacherRegister.onResetFields()
+        ;(this as any).$refs.studentRegister &&
+          (this as any).$refs.studentRegister.onResetFields()
+      }
+    }
   },
   render() {
     return (
@@ -53,7 +68,8 @@ export default defineComponent({
           class={[
             styles.loginTabs,
             'px-9 pt-5 pb-12 bg-white relative',
-            this.type === 'qr-login' ? 'pb-4' : ''
+            this.type === 'qr-login' ? 'pb-4' : '',
+            this.type === 'teacher-auth' ? 'px-6 pb-8' : ''
           ]}
         >
           {this.type === 'login' && (
@@ -71,12 +87,24 @@ export default defineComponent({
                   }}
                 />
               </div>
-              <ElTabs>
-                <ElTabPane label="老师登录">
-                  <Form type="teacher-login" />
+              <ElTabs v-model={this.loginType}>
+                <ElTabPane label="老师登录" name="teacher">
+                  <Form
+                    type="teacher-login"
+                    ref="teacherLogin"
+                    onClose={() => {
+                      this.onClose()
+                    }}
+                  />
                 </ElTabPane>
-                <ElTabPane label="学员登录">
-                  <Form type="student-login" />
+                <ElTabPane label="学员登录" name="student">
+                  <Form
+                    type="student-login"
+                    ref="studentLogin"
+                    onClose={() => {
+                      this.onClose()
+                    }}
+                  />
                 </ElTabPane>
               </ElTabs>
               <div class={[styles.scanTxt]}>
@@ -84,6 +112,7 @@ export default defineComponent({
                 <span
                   class="cursor-pointer"
                   onClick={() => {
+                    this.onReset('login')
                     this.type = 'register'
                   }}
                 >
@@ -94,39 +123,13 @@ export default defineComponent({
           )}
 
           {this.type === 'qr-login' && (
-            <div class={'text-center pt-4'}>
-              <div
-                class={'absolute top-2 right-2 z-10'}
-                onClick={() => {
-                  this.type = 'login'
+            <>
+              <QrLogin
+                onChange={(type: any) => {
+                  this.type = type
                 }}
-              >
-                <img
-                  src={getAssetsHomeFile('icon_pc_login.png')}
-                  class="w-14 h-14 cursor-pointer"
-                />
-              </div>
-
-              <QrcodeVue
-                value={this.qrCode}
-                class="mx-auto shadow-lg w-[138px] h-[138px] align-middle"
               />
-
-              <div class="flex items-center justify-center pt-8">
-                <img
-                  class="w-9 h-9 align-middle mr-4"
-                  src={getAssetsHomeFile('icon_scan.png')}
-                />
-
-                <div class={[styles.scanTxt, 'text-left leading-[22px]']}>
-                  <p>
-                    打开<span>酷乐秀学生端APP</span>
-                  </p>
-                  <p>扫一扫登录</p>
-                </div>
-              </div>
-
-              <div class={[styles.scanTxt, 'pt-14']}>
+              <div class={[styles.scanTxt, 'pt-14 text-center']}>
                 没有账号,
                 <span
                   class="cursor-pointer"
@@ -137,17 +140,35 @@ export default defineComponent({
                   马上注册
                 </span>
               </div>
-            </div>
+            </>
           )}
 
           {this.type === 'register' && (
             <>
-              <ElTabs>
-                <ElTabPane label="老师注册">
-                  <Form type="teacher-register" />
+              <ElTabs v-model={this.registerType}>
+                <ElTabPane label="老师注册" name="teacher">
+                  <Form
+                    type="teacher-register"
+                    ref="teacherRegister"
+                    onClose={() => {
+                      this.onClose()
+                    }}
+                    onChange={(type: any) => {
+                      this.type = type
+                    }}
+                  />
                 </ElTabPane>
-                <ElTabPane label="学员注册">
-                  <Form type="student-register" />
+                <ElTabPane label="学员注册" name="student">
+                  <Form
+                    type="student-register"
+                    ref="studentRegister"
+                    onClose={() => {
+                      this.onClose()
+                    }}
+                    onChange={(type: any) => {
+                      this.type = type
+                    }}
+                  />
                 </ElTabPane>
               </ElTabs>
               <div class={[styles.scanTxt]}>
@@ -155,6 +176,7 @@ export default defineComponent({
                 <span
                   class="cursor-pointer"
                   onClick={() => {
+                    this.onReset('register')
                     this.type = 'login'
                   }}
                 >
@@ -184,13 +206,27 @@ export default defineComponent({
                 class="w-full mt-4"
                 style={{ height: '40px' }}
                 onClick={() => {
-                  this.onClose()
+                  if (this.registerType == 'teacher') {
+                    this.type = 'teacher-auth'
+                  } else {
+                    this.onClose()
+                  }
                 }}
               >
                 知道了
               </ElButton>
             </div>
           )}
+
+          {this.type === 'teacher-auth' && (
+            <TeacherAuth
+              onClose={() => {
+                this.type = 'login'
+                this.loginType = 'teacher'
+                this.onClose()
+              }}
+            />
+          )}
         </div>
       </div>
     )

+ 0 - 0
src/views/login/loginState.ts


BIN
src/views/roleAuth/images/bg_bottom.png


BIN
src/views/roleAuth/images/bg_center.png


BIN
src/views/roleAuth/images/bg_left_bottom.png


BIN
src/views/roleAuth/images/bg_right_center.png


BIN
src/views/roleAuth/images/bg_top.png


BIN
src/views/roleAuth/images/midi_money.png


BIN
src/views/roleAuth/images/midi_upload.png


BIN
src/views/roleAuth/images/music_main.png


BIN
src/views/roleAuth/images/num1.png


BIN
src/views/roleAuth/images/num2.png


BIN
src/views/roleAuth/images/num3.png


BIN
src/views/roleAuth/images/num4.png


BIN
src/views/roleAuth/images/o1.png


BIN
src/views/roleAuth/images/o2.png


BIN
src/views/roleAuth/images/o3.png


BIN
src/views/roleAuth/images/o4.png


BIN
src/views/roleAuth/images/teacher_main.png


+ 33 - 0
src/views/roleAuth/musicAuth/index.module.less

@@ -0,0 +1,33 @@
+.musicAuth {
+  background: url('../images/bg_top.png') no-repeat top left,
+    url('../images/bg_center.png') no-repeat left 80%,
+    url('../images/bg_bottom.png') no-repeat bottom right,
+    url('../images/music_main.png') no-repeat top right;
+  background-size: 212px 126px, 178px 223px, 171px 163px, 668px 406px;
+  background-color: #fff;
+  .txt {
+    position: relative;
+    z-index: 1;
+    @apply pb-5;
+    &::after {
+      content: '';
+      display: block;
+      width: 100%;
+      height: 13px;
+      background: #cbfdd5;
+      position: absolute;
+      bottom: 15px;
+      left: 0;
+      z-index: -1;
+      opacity: 0.63;
+      @apply rounded-xl;
+    }
+  }
+
+  .col {
+    background: #ffffff;
+    box-shadow: 0px 10px 36px 0px rgba(0, 0, 0, 0.13);
+    border-radius: 10px;
+    min-height: 197px;
+  }
+}

+ 86 - 0
src/views/roleAuth/musicAuth/index.tsx

@@ -0,0 +1,86 @@
+import { ElButton, ElCol, ElRow } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `../images/${fileName}`
+  const modules = import.meta.globEager('../images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'musicAuth',
+  render() {
+    return (
+      <div
+        class={[
+          styles.musicAuth,
+          'w-[1200px] mt-[72px] mb-[60px] bg-white min-h-full m-auto px-[138px] text-[#333]'
+        ]}
+      >
+        <div class="w-[388px] pt-24 pb-28">
+          <div class="text-[28px] font-semibold pb-5">音乐人认证是什么</div>
+          <p class={[styles.txt]}>
+            酷乐秀对有能力编曲的音乐人开通上传曲谱权限,开通
+          </p>
+          <p class={[styles.txt]}>
+            后您可上传自己编曲的乐谱MIDI让平台学员进行练习,
+          </p>
+          <p class={[styles.txt, 'inline-block']}>并获取收益。</p>
+        </div>
+        <div class="pb-20">
+          <h2 class="text-2xl pb-11 text-center font-semibold">认证权益</h2>
+
+          <ElRow class="mb-16">
+            <ElCol span={12} class="pr-3">
+              <div class={[styles.col, 'flex items-center px-10']}>
+                <img
+                  class="w-[97px] h-[73px]"
+                  src={getAssetsHomeFile('midi_upload.png')}
+                />
+                <div class="pl-10">
+                  <div class="flex items-center font-[18px] pb-3">
+                    <img
+                      class="w-[30px] h-[22px] mr-1"
+                      src={getAssetsHomeFile('num1.png')}
+                    />
+                    MP3文件上传
+                  </div>
+                  <p class="text-[14px] leading-6">
+                    您可上传自己编曲的乐谱MP3,并自定义乐谱价格,为平台学员提供智能陪练练习。
+                  </p>
+                </div>
+              </div>
+            </ElCol>
+            <ElCol span={12} class="pl-3">
+              <div class={[styles.col, 'flex items-center px-10']}>
+                <img
+                  class="w-[97px] h-[73px]"
+                  src={getAssetsHomeFile('midi_money.png')}
+                />
+                <div class="pl-10">
+                  <div class="flex items-center font-[18px] pb-3">
+                    <img
+                      class="w-[30px] h-[22px] mr-1"
+                      src={getAssetsHomeFile('num2.png')}
+                    />
+                    乐谱收益
+                  </div>
+                  <p class="text-[14px] leading-6">
+                    您可对上传的乐谱自定义价格,学员可自行挑选乐谱购买,购买后您将获取学员购买收入。
+                  </p>
+                </div>
+              </div>
+            </ElCol>
+          </ElRow>
+
+          <div class="text-center">
+            <ElButton type="primary" class="rounded w-40 !h-[38px]">
+              立即认证
+            </ElButton>
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 33 - 0
src/views/roleAuth/teacherAuth/index.module.less

@@ -0,0 +1,33 @@
+.musicAuth {
+  background: url('../images/bg_top.png') no-repeat top left,
+    url('../images/bg_left_bottom.png') no-repeat left bottom,
+    url('../images/bg_right_center.png') no-repeat right 70%,
+    url('../images/teacher_main.png') no-repeat top right;
+  background-size: 212px 126px, 178px 172px, 171px 230px, 682px 458px;
+  background-color: #fff;
+  .txt {
+    position: relative;
+    z-index: 1;
+    @apply pb-5;
+    &::after {
+      content: '';
+      display: block;
+      width: 100%;
+      height: 13px;
+      background: #cbfdd5;
+      position: absolute;
+      bottom: 15px;
+      left: 0;
+      z-index: -1;
+      opacity: 0.63;
+      @apply rounded-xl;
+    }
+  }
+
+  .col {
+    background: #ffffff;
+    box-shadow: 0px 10px 36px 0px rgba(0, 0, 0, 0.13);
+    border-radius: 10px;
+    min-height: 197px;
+  }
+}

+ 101 - 0
src/views/roleAuth/teacherAuth/index.tsx

@@ -0,0 +1,101 @@
+import { ElButton, ElCol, ElRow } from 'element-plus'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `../images/${fileName}`
+  const modules = import.meta.globEager('../images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'teacherAuth',
+  data() {
+    return {
+      list: [
+        {
+          logo: getAssetsHomeFile('o1.png'),
+          num: getAssetsHomeFile('num1.png'),
+          title: '线上授课',
+          desc: '认证成为酷乐秀老师后,可设置您的空闲时间为平台中的求学者进行1对1的线上课程指导。'
+        },
+        {
+          logo: getAssetsHomeFile('o2.png'),
+          num: getAssetsHomeFile('num2.png'),
+          title: '个人风采展示',
+          desc: '可发布自己的专业经历、获奖记录及音视频资料对求学者展示,让学员更加深入的了解您的专业技能,从而提高约课率。'
+        },
+        {
+          logo: getAssetsHomeFile('o3.png'),
+          num: getAssetsHomeFile('num3.png'),
+          title: '曲谱上传',
+          desc: '可上传您制作的MIDI乐谱为求学者提供学习曲目的途径,并从中获得收益。'
+        },
+        {
+          logo: getAssetsHomeFile('o4.png'),
+          num: getAssetsHomeFile('num4.png'),
+          title: '收益提现',
+          desc: '在您授课及上传曲谱销售后,经过平台核算,将您获得的收益发放至您的个人账户下,您可随时将自己获得的收益提现。'
+        }
+      ]
+    }
+  },
+  render() {
+    return (
+      <div
+        class={[
+          styles.musicAuth,
+          'w-[1200px] mt-[72px] mb-[60px] bg-white min-h-full m-auto px-[138px] text-[#333]'
+        ]}
+      >
+        <div class="w-[388px] pt-24 pb-28">
+          <div class="text-[28px] font-semibold pb-5">酷乐秀基本介绍</div>
+          <p class={[styles.txt]}>
+            酷乐秀是一款为器乐学习者提供智能陪练及线上授课撮
+          </p>
+          <p class={[styles.txt]}>
+            合的乐器教学平台,器乐老师可通过自身的专业知识为
+          </p>
+          <p class={[styles.txt, 'inline-block']}>
+            自己带来<span class="font-medium">授课及曲谱销售收益</span>。
+          </p>
+        </div>
+        <div class="pb-20">
+          <h2 class="text-2xl pb-11 text-center font-semibold">认证权益</h2>
+
+          <ElRow class="mb-24" gutter={28}>
+            {this.list.map((item: any) => (
+              <ElCol span={6} class="pr-3 !flex">
+                <div class={[styles.col, 'flex items-center flex-col p-4']}>
+                  <img class="w-[94px] h-[87px]" src={item.logo} />
+
+                  <div class="flex items-center font-[18px] font-medium pb-3 pt-5">
+                    <img class="w-[30px] h-[22px] mr-1" src={item.num} />
+                    {item.title}
+                  </div>
+                  <p class="text-[14px] text-[#666] leading-6 text-justify">
+                    {item.desc}
+                  </p>
+                </div>
+              </ElCol>
+            ))}
+          </ElRow>
+
+          <h2 class="text-2xl pb-10 text-center font-semibold">
+            酷乐秀欢迎您的加入
+          </h2>
+
+          <p class="text-lg text-center mb-10">
+            在艺术的殿堂中,为他人照亮前进的道路,用自己的经验和点拨,传播艺术的种子,获取硕果。
+          </p>
+
+          <div class="text-center">
+            <ElButton type="primary" class="rounded w-40 !h-[38px]">
+              立即认证
+            </ElButton>
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 4 - 0
vite.config.ts

@@ -49,6 +49,10 @@ export default defineConfig({
       '/api-auth': {
         target: proxyUrl,
         changeOrigin: true
+      },
+      '/api-website': {
+        target: proxyUrl,
+        changeOrigin: true
       }
     }
   },

+ 114 - 30
yarn.lock

@@ -43,7 +43,7 @@
   "resolved" "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.17.7.tgz"
   "version" "7.17.7"
 
-"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.16.5", "@babel/core@^7.4.0-0":
+"@babel/core@^7.16.5":
   "integrity" "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ=="
   "resolved" "https://registry.npmmirror.com/@babel/core/-/core-7.16.5.tgz"
   "version" "7.16.5"
@@ -415,7 +415,7 @@
     "chalk" "^2.0.0"
     "js-tokens" "^4.0.0"
 
-"@babel/parser@^7.15.8", "@babel/parser@^7.16.0", "@babel/parser@^7.16.4", "@babel/parser@^7.16.5", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
+"@babel/parser@^7.16.0", "@babel/parser@^7.16.4", "@babel/parser@^7.16.5", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
   "integrity" "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ=="
   "resolved" "https://registry.npmmirror.com/@babel/parser/-/parser-7.16.6.tgz"
   "version" "7.16.6"
@@ -1067,7 +1067,7 @@
     "@babel/parser" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.5":
+"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.5":
   "integrity" "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ=="
   "resolved" "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.16.5.tgz"
   "version" "7.16.5"
@@ -1258,7 +1258,7 @@
 
 "@popperjs/core@npm:@sxzz/popperjs-es@^2.11.7":
   "integrity" "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
-  "resolved" "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz"
+  "resolved" "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz"
   "version" "2.11.7"
 
 "@rollup/pluginutils@^4.1.1", "@rollup/pluginutils@^4.1.2", "@rollup/pluginutils@^4.2.1":
@@ -1295,7 +1295,7 @@
     "@types/fined" "*"
     "@types/node" "*"
 
-"@types/lodash-es@*", "@types/lodash-es@^4.17.6":
+"@types/lodash-es@^4.17.6":
   "integrity" "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg=="
   "resolved" "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.6.tgz"
   "version" "4.17.6"
@@ -1329,7 +1329,7 @@
   dependencies:
     "@types/node" "*"
 
-"@typescript-eslint/eslint-plugin@^5.0.0", "@typescript-eslint/eslint-plugin@^5.7.0":
+"@typescript-eslint/eslint-plugin@^5.7.0":
   "integrity" "sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg=="
   "resolved" "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz"
   "version" "5.7.0"
@@ -1355,7 +1355,7 @@
     "eslint-scope" "^5.1.1"
     "eslint-utils" "^3.0.0"
 
-"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.7.0":
+"@typescript-eslint/parser@^5.7.0":
   "integrity" "sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g=="
   "resolved" "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.7.0.tgz"
   "version" "5.7.0"
@@ -1645,7 +1645,7 @@
   "resolved" "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.31.tgz"
   "version" "3.2.31"
 
-"@vueuse/core@*", "@vueuse/core@^8.4.1", "@vueuse/core@^8.6.0":
+"@vueuse/core@^8.4.1", "@vueuse/core@^8.6.0":
   "integrity" "sha512-VirzExCm/N+QdrEWT7J4uSrvJ5hquKIAU9alQ37kUvIJk9XxCLxmfRnmekYc1kz2+6BnoyuKYXVmrMV351CB4w=="
   "resolved" "https://registry.npmmirror.com/@vueuse/core/-/core-8.6.0.tgz"
   "version" "8.6.0"
@@ -1685,11 +1685,6 @@
   "resolved" "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz"
   "version" "7.2.0"
 
-"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^8.6.0", "acorn@^8.7.1":
-  "integrity" "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
-  "resolved" "https://registry.npmmirror.com/acorn/-/acorn-8.7.1.tgz"
-  "version" "8.7.1"
-
 "acorn@^7.0.0":
   "integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
   "resolved" "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz"
@@ -1700,6 +1695,11 @@
   "resolved" "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz"
   "version" "7.4.1"
 
+"acorn@^8.6.0", "acorn@^8.7.1":
+  "integrity" "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
+  "resolved" "https://registry.npmmirror.com/acorn/-/acorn-8.7.1.tgz"
+  "version" "8.7.1"
+
 "aggregate-error@^3.0.0":
   "integrity" "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="
   "resolved" "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz"
@@ -2458,7 +2458,7 @@
   dependencies:
     "iconv-lite" "^0.6.2"
 
-"enquirer@^2.3.5", "enquirer@^2.3.6", "enquirer@>= 2.3.0 < 3":
+"enquirer@^2.3.5", "enquirer@^2.3.6":
   "integrity" "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg=="
   "resolved" "https://registry.npmmirror.com/enquirer/-/enquirer-2.3.6.tgz"
   "version" "2.3.6"
@@ -2487,12 +2487,92 @@
   "resolved" "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz"
   "version" "0.9.3"
 
+"esbuild-android-arm64@0.13.15":
+  "integrity" "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg=="
+  "resolved" "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz"
+  "version" "0.13.15"
+
 "esbuild-darwin-64@0.13.15":
   "integrity" "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ=="
   "resolved" "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz"
   "version" "0.13.15"
 
-"esbuild@^0.13.12", "esbuild@>=0.13":
+"esbuild-darwin-arm64@0.13.15":
+  "integrity" "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ=="
+  "resolved" "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-freebsd-64@0.13.15":
+  "integrity" "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA=="
+  "resolved" "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-freebsd-arm64@0.13.15":
+  "integrity" "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ=="
+  "resolved" "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-linux-32@0.13.15":
+  "integrity" "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g=="
+  "resolved" "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-linux-64@0.13.15":
+  "integrity" "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA=="
+  "resolved" "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-linux-arm@0.13.15":
+  "integrity" "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA=="
+  "resolved" "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-linux-arm64@0.13.15":
+  "integrity" "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA=="
+  "resolved" "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-linux-mips64le@0.13.15":
+  "integrity" "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg=="
+  "resolved" "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-linux-ppc64le@0.13.15":
+  "integrity" "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ=="
+  "resolved" "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-netbsd-64@0.13.15":
+  "integrity" "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w=="
+  "resolved" "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-openbsd-64@0.13.15":
+  "integrity" "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g=="
+  "resolved" "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-sunos-64@0.13.15":
+  "integrity" "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw=="
+  "resolved" "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-windows-32@0.13.15":
+  "integrity" "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw=="
+  "resolved" "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-windows-64@0.13.15":
+  "integrity" "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ=="
+  "resolved" "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild-windows-arm64@0.13.15":
+  "integrity" "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA=="
+  "resolved" "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz"
+  "version" "0.13.15"
+
+"esbuild@^0.13.12":
   "integrity" "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw=="
   "resolved" "https://registry.npmmirror.com/esbuild/-/esbuild-0.13.15.tgz"
   "version" "0.13.15"
@@ -2547,14 +2627,14 @@
   dependencies:
     "get-stdin" "^6.0.0"
 
-"eslint-plugin-prettier@^3.1.0", "eslint-plugin-prettier@^4.0.0":
+"eslint-plugin-prettier@^4.0.0":
   "integrity" "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ=="
   "resolved" "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz"
   "version" "4.0.0"
   dependencies:
     "prettier-linter-helpers" "^1.0.0"
 
-"eslint-plugin-vue@^8.0.1", "eslint-plugin-vue@^8.2.0":
+"eslint-plugin-vue@^8.2.0":
   "integrity" "sha512-cLIdTuOAMXyHeQ4drYKcZfoyzdwdBpH279X8/N0DgmotEI9yFKb5O/cAgoie/CkQZCH/MOmh0xw/KEfS90zY2A=="
   "resolved" "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-8.2.0.tgz"
   "version" "8.2.0"
@@ -2610,7 +2690,7 @@
   "resolved" "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz"
   "version" "3.1.0"
 
-"eslint@*", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0", "eslint@^8.4.1", "eslint@>= 5.0.0", "eslint@>=3.14.1", "eslint@>=5", "eslint@>=6.0.0", "eslint@>=7.28.0":
+"eslint@^8.4.1":
   "integrity" "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg=="
   "resolved" "https://registry.npmmirror.com/eslint/-/eslint-8.4.1.tgz"
   "version" "8.4.1"
@@ -3380,6 +3460,11 @@
     "node-fetch" "^1.0.1"
     "whatwg-fetch" ">=0.10.0"
 
+"js-cookie@^3.0.1":
+  "integrity" "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw=="
+  "resolved" "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.1.tgz"
+  "version" "3.0.1"
+
 "js-stringify@^1.0.2":
   "integrity" "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g=="
   "resolved" "https://registry.npmmirror.com/js-stringify/-/js-stringify-1.0.2.tgz"
@@ -3439,9 +3524,8 @@
   "resolved" "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz"
   "version" "6.1.0"
   dependencies:
-    "universalify" "^2.0.0"
-  optionalDependencies:
     "graceful-fs" "^4.1.6"
+    "universalify" "^2.0.0"
 
 "jstransformer@1.0.0":
   "integrity" "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A=="
@@ -3468,17 +3552,17 @@
   dependencies:
     "klona" "^2.0.4"
 
-"less@*", "less@^3.5.0 || ^4.0.0", "less@^4.1.2":
+"less@^4.1.2":
   "integrity" "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA=="
   "resolved" "https://registry.npmmirror.com/less/-/less-4.1.2.tgz"
   "version" "4.1.2"
   dependencies:
     "copy-anything" "^2.0.1"
+    "graceful-fs" "^4.1.2"
     "parse-node-version" "^1.0.1"
     "tslib" "^2.3.0"
   optionalDependencies:
     "errno" "^0.1.1"
-    "graceful-fs" "^4.1.2"
     "image-size" "~0.5.0"
     "make-dir" "^2.1.0"
     "mime" "^1.4.1"
@@ -3566,7 +3650,7 @@
   "resolved" "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.4.1.tgz"
   "version" "0.4.1"
 
-"lodash-es@*", "lodash-es@^4.17.21":
+"lodash-es@^4.17.21":
   "integrity" "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
   "resolved" "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz"
   "version" "4.17.21"
@@ -3606,7 +3690,7 @@
   "resolved" "https://registry.npmmirror.com/lodash.transform/-/lodash.transform-4.6.0.tgz"
   "version" "4.6.0"
 
-"lodash@*", "lodash@^4.17.21":
+"lodash@^4.17.21":
   "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
   "resolved" "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz"
   "version" "4.17.21"
@@ -4221,7 +4305,7 @@
   "resolved" "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
   "version" "4.2.0"
 
-"postcss@^8.0.0", "postcss@^8.0.9", "postcss@^8.1.0", "postcss@^8.1.10", "postcss@^8.2.14", "postcss@^8.3.11", "postcss@^8.3.3", "postcss@^8.4.14", "postcss@>=8.0.9":
+"postcss@^8.1.10", "postcss@^8.3.11", "postcss@^8.4.14":
   "integrity" "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig=="
   "resolved" "https://registry.npmmirror.com/postcss/-/postcss-8.4.14.tgz"
   "version" "8.4.14"
@@ -4242,7 +4326,7 @@
   dependencies:
     "fast-diff" "^1.1.2"
 
-"prettier@^2.5.1", "prettier@>= 1.13.0", "prettier@>=2.0.0":
+"prettier@^2.5.1":
   "integrity" "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg=="
   "resolved" "https://registry.npmmirror.com/prettier/-/prettier-2.5.1.tgz"
   "version" "2.5.1"
@@ -4562,7 +4646,7 @@
   dependencies:
     "glob" "^7.1.3"
 
-"rollup@^2.50.0", "rollup@^2.59.0":
+"rollup@^2.59.0":
   "integrity" "sha512-BbTXlEvB8d+XFbK/7E5doIcRtxWPRiqr0eb5vQ0+2paMM04Ye4PZY5nHOQef2ix24l/L0SpLd5hwcH15QHPdvA=="
   "resolved" "https://registry.npmmirror.com/rollup/-/rollup-2.61.1.tgz"
   "version" "2.61.1"
@@ -4995,7 +5079,7 @@
   "resolved" "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz"
   "version" "0.21.3"
 
-"typescript@*", "typescript@^4.5.2", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta":
+"typescript@^4.5.2":
   "integrity" "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw=="
   "resolved" "https://registry.npmmirror.com/typescript/-/typescript-4.5.2.tgz"
   "version" "4.5.2"
@@ -5167,7 +5251,7 @@
     "fs-extra" "^10.0.0"
     "magic-string" "^0.25.7"
 
-"vite@^2.3.0", "vite@^2.5.10", "vite@^2.7.3", "vite@^2.8.0", "vite@>=2.0.0":
+"vite@^2.7.3":
   "integrity" "sha512-GAY1P+9fLJOju1SRm8+hykVnEXog+E+KXuqqyMBQDriKCUIKzWnPn142yNNhSdf/ixYGYdUa5ce3A8WaEajzGw=="
   "resolved" "https://registry.npmmirror.com/vite/-/vite-2.7.3.tgz"
   "version" "2.7.3"
@@ -5366,7 +5450,7 @@
     "@volar/shared" "0.29.8"
     "vscode-vue-languageservice" "0.29.8"
 
-"vue@^2.6.0 || ^3.2.0", "vue@^3.0.0", "vue@^3.0.0-0 || ^2.6.0", "vue@^3.0.2", "vue@^3.2.0", "vue@^3.2.25", "vue@^3.2.26", "vue@2 || 3", "vue@3.2.26":
+"vue@^3.2.26":
   "integrity" "sha512-KD4lULmskL5cCsEkfhERVRIOEDrfEL9CwAsLYpzptOGjaGFNWo3BQ9g8MAb7RaIO71rmVOziZ/uEN/rHwcUIhg=="
   "resolved" "https://registry.npmmirror.com/vue/-/vue-3.2.26.tgz"
   "version" "3.2.26"