Browse Source

初始化

黄琪勇 3 months ago
parent
commit
17647443b7
48 changed files with 1331 additions and 520 deletions
  1. 7 0
      package-lock.json
  2. 1 0
      package.json
  3. 3 81
      src/App.vue
  4. 9 0
      src/api/ApiInstance.ts
  5. 9 0
      src/api/user.ts
  6. 0 86
      src/assets/base.css
  7. BIN
      src/assets/font/DIN_Alternate_Bold.ttf
  8. 4 0
      src/assets/font/index.scss
  9. 12 0
      src/assets/index.scss
  10. 0 1
      src/assets/logo.svg
  11. 0 35
      src/assets/main.css
  12. 498 0
      src/assets/normalize.css
  13. 5 0
      src/assets/variables.scss
  14. 0 41
      src/components/HelloWorld.vue
  15. 0 94
      src/components/TheWelcome.vue
  16. 0 87
      src/components/WelcomeItem.vue
  17. 0 7
      src/components/icons/IconCommunity.vue
  18. 0 7
      src/components/icons/IconDocumentation.vue
  19. 0 7
      src/components/icons/IconEcosystem.vue
  20. 0 7
      src/components/icons/IconSupport.vue
  21. 0 19
      src/components/icons/IconTooling.vue
  22. 1 0
      src/config/index.ts
  23. 25 0
      src/libs/auth.ts
  24. 128 0
      src/libs/axios.ts
  25. 12 0
      src/libs/rem.ts
  26. 72 0
      src/libs/tools.ts
  27. 12 6
      src/main.ts
  28. 71 0
      src/plugins/httpAjax.ts
  29. 12 0
      src/plugins/loadingBar/index.ts
  30. 123 0
      src/plugins/loadingBar/loadingBar.vue
  31. 19 0
      src/plugins/nprogress/index.ts
  32. 18 0
      src/plugins/nprogress/nprogress.scss
  33. 14 0
      src/queryParams/index.ts
  34. 3 0
      src/registerComponents.ts
  35. 10 0
      src/registerPlugin.ts
  36. 68 18
      src/router/index.ts
  37. 20 0
      src/router/routers.ts
  38. 7 0
      src/store/index.ts
  39. 71 0
      src/store/modules/user.ts
  40. 0 15
      src/views/AboutView.vue
  41. 0 9
      src/views/HomeView.vue
  42. 0 0
      src/views/rhythm/index.ts
  43. 7 0
      src/views/rhythm/rhythm.vue
  44. 43 0
      src/viewsframe/errorPage/errorPage.vue
  45. BIN
      src/viewsframe/errorPage/imgs/404.png
  46. 2 0
      src/viewsframe/errorPage/index.ts
  47. 2 0
      src/viewsframe/login/index.ts
  48. 43 0
      src/viewsframe/login/login.vue

+ 7 - 0
package-lock.json

@@ -18,6 +18,7 @@
          "devDependencies": {
             "@tsconfig/node22": "^22.0.0",
             "@types/node": "^22.10.2",
+            "@types/nprogress": "^0.2.0",
             "@vitejs/plugin-vue": "^5.2.1",
             "@vitejs/plugin-vue-jsx": "^4.1.1",
             "@vue/eslint-config-prettier": "^10.1.0",
@@ -801,6 +802,12 @@
             "undici-types": "~6.20.0"
          }
       },
+      "node_modules/@types/nprogress": {
+         "version": "0.2.3",
+         "resolved": "https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.3.tgz",
+         "integrity": "sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==",
+         "dev": true
+      },
       "node_modules/@typescript-eslint/eslint-plugin": {
          "version": "8.19.0",
          "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz",

+ 1 - 0
package.json

@@ -22,6 +22,7 @@
       "vue-router": "^4.5.0"
    },
    "devDependencies": {
+      "@types/nprogress": "^0.2.0",
       "@tsconfig/node22": "^22.0.0",
       "@types/node": "^22.10.2",
       "@vitejs/plugin-vue": "^5.2.1",

+ 3 - 81
src/App.vue

@@ -1,85 +1,7 @@
-<script setup lang="ts">
-import { RouterLink, RouterView } from 'vue-router'
-import HelloWorld from './components/HelloWorld.vue'
-</script>
-
 <template>
-  <header>
-    <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
-
-    <div class="wrapper">
-      <HelloWorld msg="You did it!" />
-
-      <nav>
-        <RouterLink to="/">Home</RouterLink>
-        <RouterLink to="/about">About</RouterLink>
-      </nav>
-    </div>
-  </header>
-
-  <RouterView />
+   <router-view />
 </template>
 
-<style scoped>
-header {
-  line-height: 1.5;
-  max-height: 100vh;
-}
-
-.logo {
-  display: block;
-  margin: 0 auto 2rem;
-}
-
-nav {
-  width: 100%;
-  font-size: 12px;
-  text-align: center;
-  margin-top: 2rem;
-}
-
-nav a.router-link-exact-active {
-  color: var(--color-text);
-}
-
-nav a.router-link-exact-active:hover {
-  background-color: transparent;
-}
-
-nav a {
-  display: inline-block;
-  padding: 0 1rem;
-  border-left: 1px solid var(--color-border);
-}
-
-nav a:first-of-type {
-  border: 0;
-}
-
-@media (min-width: 1024px) {
-  header {
-    display: flex;
-    place-items: center;
-    padding-right: calc(var(--section-gap) / 2);
-  }
-
-  .logo {
-    margin: 0 2rem 0 0;
-  }
-
-  header .wrapper {
-    display: flex;
-    place-items: flex-start;
-    flex-wrap: wrap;
-  }
-
-  nav {
-    text-align: left;
-    margin-left: -1rem;
-    font-size: 1rem;
+<script lang="ts" setup></script>
 
-    padding: 1rem 0;
-    margin-top: 1rem;
-  }
-}
-</style>
+<style lang="scss"></style>

+ 9 - 0
src/api/ApiInstance.ts

@@ -0,0 +1,9 @@
+import Http from "@/libs/axios"
+import { getToken } from "@/libs/auth"
+import { URL_API } from "@/config"
+
+/** axios实例 */
+export const httpAxios = new Http(URL_API, {
+   tokenName: "Authorization",
+   getTokenFun: getToken
+})

+ 9 - 0
src/api/user.ts

@@ -0,0 +1,9 @@
+import { httpAxios } from "@/api/ApiInstance"
+
+//获取用户信息
+export const getUserInfoApi = () => {
+   return httpAxios.axioseRquest({
+      method: "get",
+      url: "/edu-app/user/getUserInfo"
+   })
+}

+ 0 - 86
src/assets/base.css

@@ -1,86 +0,0 @@
-/* color palette from <https://github.com/vuejs/theme> */
-:root {
-  --vt-c-white: #ffffff;
-  --vt-c-white-soft: #f8f8f8;
-  --vt-c-white-mute: #f2f2f2;
-
-  --vt-c-black: #181818;
-  --vt-c-black-soft: #222222;
-  --vt-c-black-mute: #282828;
-
-  --vt-c-indigo: #2c3e50;
-
-  --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
-  --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
-  --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
-  --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
-
-  --vt-c-text-light-1: var(--vt-c-indigo);
-  --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
-  --vt-c-text-dark-1: var(--vt-c-white);
-  --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
-}
-
-/* semantic color variables for this project */
-:root {
-  --color-background: var(--vt-c-white);
-  --color-background-soft: var(--vt-c-white-soft);
-  --color-background-mute: var(--vt-c-white-mute);
-
-  --color-border: var(--vt-c-divider-light-2);
-  --color-border-hover: var(--vt-c-divider-light-1);
-
-  --color-heading: var(--vt-c-text-light-1);
-  --color-text: var(--vt-c-text-light-1);
-
-  --section-gap: 160px;
-}
-
-@media (prefers-color-scheme: dark) {
-  :root {
-    --color-background: var(--vt-c-black);
-    --color-background-soft: var(--vt-c-black-soft);
-    --color-background-mute: var(--vt-c-black-mute);
-
-    --color-border: var(--vt-c-divider-dark-2);
-    --color-border-hover: var(--vt-c-divider-dark-1);
-
-    --color-heading: var(--vt-c-text-dark-1);
-    --color-text: var(--vt-c-text-dark-2);
-  }
-}
-
-*,
-*::before,
-*::after {
-  box-sizing: border-box;
-  margin: 0;
-  font-weight: normal;
-}
-
-body {
-  min-height: 100vh;
-  color: var(--color-text);
-  background: var(--color-background);
-  transition:
-    color 0.5s,
-    background-color 0.5s;
-  line-height: 1.6;
-  font-family:
-    Inter,
-    -apple-system,
-    BlinkMacSystemFont,
-    'Segoe UI',
-    Roboto,
-    Oxygen,
-    Ubuntu,
-    Cantarell,
-    'Fira Sans',
-    'Droid Sans',
-    'Helvetica Neue',
-    sans-serif;
-  font-size: 15px;
-  text-rendering: optimizeLegibility;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-}

BIN
src/assets/font/DIN_Alternate_Bold.ttf


+ 4 - 0
src/assets/font/index.scss

@@ -0,0 +1,4 @@
+// @font-face {
+//    font-family: "DINAlternate";
+//    src: url("./DIN_Alternate_Bold.ttf");
+// }

+ 12 - 0
src/assets/index.scss

@@ -0,0 +1,12 @@
+/*
+   全局样式
+ */
+html,
+body,
+#app {
+   width: 100%;
+   height: 100%;
+}
+body {
+   font-size: 16px; // 防止rem时候body上面的字体太小,如果没有设置字体大小,可以应用16px
+}

+ 0 - 1
src/assets/logo.svg

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

+ 0 - 35
src/assets/main.css

@@ -1,35 +0,0 @@
-@import './base.css';
-
-#app {
-  max-width: 1280px;
-  margin: 0 auto;
-  padding: 2rem;
-  font-weight: normal;
-}
-
-a,
-.green {
-  text-decoration: none;
-  color: hsla(160, 100%, 37%, 1);
-  transition: 0.4s;
-  padding: 3px;
-}
-
-@media (hover: hover) {
-  a:hover {
-    background-color: hsla(160, 100%, 37%, 0.2);
-  }
-}
-
-@media (min-width: 1024px) {
-  body {
-    display: flex;
-    place-items: center;
-  }
-
-  #app {
-    display: grid;
-    grid-template-columns: 1fr 1fr;
-    padding: 0 2rem;
-  }
-}

+ 498 - 0
src/assets/normalize.css

@@ -0,0 +1,498 @@
+/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+   ========================================================================== */
+
+/* add reset css */
+*,
+*:before,
+*:after {
+   box-sizing: border-box;
+}
+
+body {
+   -moz-osx-font-smoothing: grayscale;
+   -webkit-font-smoothing: antialiased;
+   text-rendering: optimizeLegibility;
+   font-family: PingFang SC, Helvetica Neue, Helvetica, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+}
+
+a:focus,
+a:active {
+   outline: none;
+}
+
+a,
+a:focus,
+a:hover {
+   cursor: pointer;
+   text-decoration: none;
+}
+
+ul {
+   list-style: none;
+   padding: 0;
+   margin: 0;
+}
+p {
+   padding: 0;
+   margin: 0;
+}
+
+img {
+   user-select: none;
+   -webkit-user-select: none;
+   -moz-user-select: none;
+   -ms-user-select: none;
+   -webkit-touch-callout: none;
+   -webkit-user-drag: none;
+   -moz-user-drag: none;
+   -ms-user-drag: none;
+   user-drag: none;
+}
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in
+ *    IE on Windows Phone and in iOS.
+ */
+
+html {
+   line-height: 1; /* 1 */
+   -ms-text-size-adjust: 100%; /* 2 */
+   -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+   ========================================================================== */
+
+/**
+ * Remove the margin in all browsers (opinionated).
+ */
+
+body {
+   margin: 0;
+}
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+article,
+aside,
+footer,
+header,
+nav,
+section {
+   display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+   font-size: 2em;
+   margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ * 1. Add the correct display in IE.
+ */
+
+figcaption,
+figure,
+main {
+   /* 1 */
+   display: block;
+}
+
+/**
+ * Add the correct margin in IE 8.
+ */
+
+figure {
+   margin: 1em 40px;
+}
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+   box-sizing: content-box; /* 1 */
+   height: 0; /* 1 */
+   overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+   font-family: monospace, monospace; /* 1 */
+   font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+
+/**
+ * 1. Remove the gray background on active links in IE 10.
+ * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
+ */
+
+a {
+   background-color: transparent; /* 1 */
+   -webkit-text-decoration-skip: objects; /* 2 */
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+   border-bottom: none; /* 1 */
+   text-decoration: underline; /* 2 */
+   text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
+ */
+
+b,
+strong {
+   font-weight: inherit;
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+   font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+   font-family: monospace, monospace; /* 1 */
+   font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font style in Android 4.3-.
+ */
+
+dfn {
+   font-style: italic;
+}
+
+/**
+ * Add the correct background and color in IE 9-.
+ */
+
+mark {
+   background-color: #ff0;
+   color: #000;
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+   font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+   font-size: 75%;
+   line-height: 0;
+   position: relative;
+   vertical-align: baseline;
+}
+
+sub {
+   bottom: -0.25em;
+}
+
+sup {
+   top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+audio,
+video {
+   display: inline-block;
+}
+
+/**
+ * Add the correct display in iOS 4-7.
+ */
+
+audio:not([controls]) {
+   display: none;
+   height: 0;
+}
+
+/**
+ * Remove the border on images inside links in IE 10-.
+ */
+
+img {
+   border-style: none;
+}
+
+/**
+ * Hide the overflow in IE.
+ */
+
+svg:not(:root) {
+   overflow: hidden;
+}
+
+/* Forms
+   ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers (opinionated).
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+   font-family: sans-serif; /* 1 */
+   font-size: 100%; /* 1 */
+   line-height: 1.15; /* 1 */
+   margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input {
+   /* 1 */
+   overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select {
+   /* 1 */
+   text-transform: none;
+}
+
+/**
+ * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+ *    controls in Android 4.
+ * 2. Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+html [type="button"], /* 1 */
+[type="reset"],
+[type="submit"] {
+   -webkit-appearance: button; /* 2 */
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+   border-style: none;
+   padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+   outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+   padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+
+legend {
+   box-sizing: border-box; /* 1 */
+   color: inherit; /* 2 */
+   display: table; /* 1 */
+   max-width: 100%; /* 1 */
+   padding: 0; /* 3 */
+   white-space: normal; /* 1 */
+}
+
+/**
+ * 1. Add the correct display in IE 9-.
+ * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+   display: inline-block; /* 1 */
+   vertical-align: baseline; /* 2 */
+}
+
+/**
+ * Remove the default vertical scrollbar in IE.
+ */
+
+textarea {
+   overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10-.
+ * 2. Remove the padding in IE 10-.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+   box-sizing: border-box; /* 1 */
+   padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+   height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+   -webkit-appearance: textfield; /* 1 */
+   outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-cancel-button,
+[type="search"]::-webkit-search-decoration {
+   -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+   -webkit-appearance: button; /* 1 */
+   font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+
+/*
+ * Add the correct display in IE 9-.
+ * 1. Add the correct display in Edge, IE, and Firefox.
+ */
+
+details, /* 1 */
+menu {
+   display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+   display: list-item;
+}
+
+/* Scripting
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+canvas {
+   display: inline-block;
+}
+
+/**
+ * Add the correct display in IE.
+ */
+
+template {
+   display: none;
+}
+
+/* Hidden
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 10-.
+ */
+
+[hidden] {
+   display: none;
+}

+ 5 - 0
src/assets/variables.scss

@@ -0,0 +1,5 @@
+/*
+   全局变量
+ */
+
+$themeColor: #1fa1ff;

+ 0 - 41
src/components/HelloWorld.vue

@@ -1,41 +0,0 @@
-<script setup lang="ts">
-defineProps<{
-  msg: string
-}>()
-</script>
-
-<template>
-  <div class="greetings">
-    <h1 class="green">{{ msg }}</h1>
-    <h3>
-      You’ve successfully created a project with
-      <a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
-      <a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
-    </h3>
-  </div>
-</template>
-
-<style scoped>
-h1 {
-  font-weight: 500;
-  font-size: 2.6rem;
-  position: relative;
-  top: -10px;
-}
-
-h3 {
-  font-size: 1.2rem;
-}
-
-.greetings h1,
-.greetings h3 {
-  text-align: center;
-}
-
-@media (min-width: 1024px) {
-  .greetings h1,
-  .greetings h3 {
-    text-align: left;
-  }
-}
-</style>

+ 0 - 94
src/components/TheWelcome.vue

@@ -1,94 +0,0 @@
-<script setup lang="ts">
-import WelcomeItem from './WelcomeItem.vue'
-import DocumentationIcon from './icons/IconDocumentation.vue'
-import ToolingIcon from './icons/IconTooling.vue'
-import EcosystemIcon from './icons/IconEcosystem.vue'
-import CommunityIcon from './icons/IconCommunity.vue'
-import SupportIcon from './icons/IconSupport.vue'
-
-const openReadmeInEditor = () => fetch('/__open-in-editor?file=README.md')
-</script>
-
-<template>
-  <WelcomeItem>
-    <template #icon>
-      <DocumentationIcon />
-    </template>
-    <template #heading>Documentation</template>
-
-    Vue’s
-    <a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
-    provides you with all information you need to get started.
-  </WelcomeItem>
-
-  <WelcomeItem>
-    <template #icon>
-      <ToolingIcon />
-    </template>
-    <template #heading>Tooling</template>
-
-    This project is served and bundled with
-    <a href="https://vite.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
-    recommended IDE setup is
-    <a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a>
-    +
-    <a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
-    you need to test your components and web pages, check out
-    <a href="https://vitest.dev/" target="_blank" rel="noopener">Vite</a>
-    and
-    <a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a>
-    /
-    <a href="https://playwright.dev/" target="_blank" rel="noopener">Playwright</a>.
-
-    <br />
-
-    More instructions are available in
-    <a href="javascript:void(0)" @click="openReadmeInEditor"><code>README.md</code></a
-    >.
-  </WelcomeItem>
-
-  <WelcomeItem>
-    <template #icon>
-      <EcosystemIcon />
-    </template>
-    <template #heading>Ecosystem</template>
-
-    Get official tools and libraries for your project:
-    <a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
-    <a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
-    <a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
-    <a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
-    you need more resources, we suggest paying
-    <a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
-    a visit.
-  </WelcomeItem>
-
-  <WelcomeItem>
-    <template #icon>
-      <CommunityIcon />
-    </template>
-    <template #heading>Community</template>
-
-    Got stuck? Ask your question on
-    <a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>
-    (our official Discord server), or
-    <a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
-      >StackOverflow</a
-    >. You should also follow the official
-    <a href="https://bsky.app/profile/vuejs.org" target="_blank" rel="noopener">@vuejs.org</a>
-    Bluesky account or the
-    <a href="https://x.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
-    X account for latest news in the Vue world.
-  </WelcomeItem>
-
-  <WelcomeItem>
-    <template #icon>
-      <SupportIcon />
-    </template>
-    <template #heading>Support Vue</template>
-
-    As an independent project, Vue relies on community backing for its sustainability. You can help
-    us by
-    <a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
-  </WelcomeItem>
-</template>

+ 0 - 87
src/components/WelcomeItem.vue

@@ -1,87 +0,0 @@
-<template>
-  <div class="item">
-    <i>
-      <slot name="icon"></slot>
-    </i>
-    <div class="details">
-      <h3>
-        <slot name="heading"></slot>
-      </h3>
-      <slot></slot>
-    </div>
-  </div>
-</template>
-
-<style scoped>
-.item {
-  margin-top: 2rem;
-  display: flex;
-  position: relative;
-}
-
-.details {
-  flex: 1;
-  margin-left: 1rem;
-}
-
-i {
-  display: flex;
-  place-items: center;
-  place-content: center;
-  width: 32px;
-  height: 32px;
-
-  color: var(--color-text);
-}
-
-h3 {
-  font-size: 1.2rem;
-  font-weight: 500;
-  margin-bottom: 0.4rem;
-  color: var(--color-heading);
-}
-
-@media (min-width: 1024px) {
-  .item {
-    margin-top: 0;
-    padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
-  }
-
-  i {
-    top: calc(50% - 25px);
-    left: -26px;
-    position: absolute;
-    border: 1px solid var(--color-border);
-    background: var(--color-background);
-    border-radius: 8px;
-    width: 50px;
-    height: 50px;
-  }
-
-  .item:before {
-    content: ' ';
-    border-left: 1px solid var(--color-border);
-    position: absolute;
-    left: 0;
-    bottom: calc(50% + 25px);
-    height: calc(50% - 25px);
-  }
-
-  .item:after {
-    content: ' ';
-    border-left: 1px solid var(--color-border);
-    position: absolute;
-    left: 0;
-    top: calc(50% + 25px);
-    height: calc(50% - 25px);
-  }
-
-  .item:first-of-type:before {
-    display: none;
-  }
-
-  .item:last-of-type:after {
-    display: none;
-  }
-}
-</style>

+ 0 - 7
src/components/icons/IconCommunity.vue

@@ -1,7 +0,0 @@
-<template>
-  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
-    <path
-      d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
-    />
-  </svg>
-</template>

+ 0 - 7
src/components/icons/IconDocumentation.vue

@@ -1,7 +0,0 @@
-<template>
-  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
-    <path
-      d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
-    />
-  </svg>
-</template>

+ 0 - 7
src/components/icons/IconEcosystem.vue

@@ -1,7 +0,0 @@
-<template>
-  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
-    <path
-      d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
-    />
-  </svg>
-</template>

+ 0 - 7
src/components/icons/IconSupport.vue

@@ -1,7 +0,0 @@
-<template>
-  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
-    <path
-      d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
-    />
-  </svg>
-</template>

+ 0 - 19
src/components/icons/IconTooling.vue

@@ -1,19 +0,0 @@
-<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
-<template>
-  <svg
-    xmlns="http://www.w3.org/2000/svg"
-    xmlns:xlink="http://www.w3.org/1999/xlink"
-    aria-hidden="true"
-    role="img"
-    class="iconify iconify--mdi"
-    width="24"
-    height="24"
-    preserveAspectRatio="xMidYMid meet"
-    viewBox="0 0 24 24"
-  >
-    <path
-      d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
-      fill="currentColor"
-    ></path>
-  </svg>
-</template>

+ 1 - 0
src/config/index.ts

@@ -0,0 +1 @@
+export const URL_API = import.meta.env.VITE_APP_URL as string

+ 25 - 0
src/libs/auth.ts

@@ -0,0 +1,25 @@
+/* code */
+// eslint-disable-next-line prefer-const
+export let CODE401 = 5000 // 没有权限
+export const CODE_ERR_CANCELED = "ERR_CANCELED" // 取消请求的code
+export function setCODE401(code: number) {
+   CODE401 = code
+}
+
+/** cookie */
+const TokenKey = "rhythm-Token"
+
+export let TokenInvalidFlag = true // 令牌是否有效
+export function getToken() {
+   return sessionStorage.getItem(TokenKey)
+}
+
+export function setToken(token: string) {
+   TokenInvalidFlag = true
+   return sessionStorage.setItem(TokenKey, token)
+}
+
+export function removeToken() {
+   TokenInvalidFlag = false
+   sessionStorage.removeItem(TokenKey)
+}

+ 128 - 0
src/libs/axios.ts

@@ -0,0 +1,128 @@
+/** http请求类 */
+import axios from "axios"
+import type { AxiosInstance, AxiosRequestConfig, Method, AxiosPromise } from "axios"
+import Nprogress from "@/plugins/nprogress"
+import { TokenInvalidFlag, CODE401, CODE_ERR_CANCELED } from "@/libs/auth"
+import userStore from "@/store/modules/user"
+
+// 重写 axios 传参
+interface AxiosConfigType extends AxiosRequestConfig {
+   method: Method // method 必须为Method 格式
+}
+
+// axiosObj 传值
+type axiosObjType = {
+   getTokenFun?: () => string | null // 获取token的函数
+   tokenName?: string // headers token的名字
+   nprogress?: boolean // 是否显示滚动条
+}
+
+/** api接口 */
+export interface axiosApiType {
+   (...param: any[]): AxiosPromise<any>
+}
+
+class HttpAsynAxios {
+   constructor(url: string, axiosObj?: axiosObjType) {
+      this.URL = url
+      this.axiosObj = axiosObj
+   }
+   private URL = ""
+   private axiosObj: axiosObjType | undefined = undefined
+   private httpInterceptor(instance: AxiosInstance) {
+      ;(this.axiosObj ? !(this.axiosObj.nprogress === false) : true) && Nprogress.start() // 开启
+      // http request 请求拦截器
+      instance.interceptors.request.use(
+         config => {
+            return config
+         },
+         error => {
+            console.log(error)
+            Nprogress.done()
+            const rejectData: apiResDataType = {
+               code: 500,
+               message: "系统运行异常,请联系管理员处理",
+               data: null
+            }
+            return Promise.reject(rejectData)
+         }
+      )
+      // http response 结果拦截器
+      instance.interceptors.response.use(
+         response => {
+            Nprogress.done()
+            // 如果返回401则跳转到登录页
+            if (response.data.code === CODE401) {
+               // 如果token登录状态 才提示和退出
+               if (TokenInvalidFlag) {
+                  // const responseData: apiResDataType = response.data
+                  // ElMessage.error(responseData.message)
+                  // 登出
+                  userStore().resetUser()
+               }
+            }
+            return response
+         },
+         error => {
+            console.log(error)
+            Nprogress.done()
+            // if (error.response) {
+            //     // 响应错误之后的操作
+            //     switch (error.response.status) {
+            //         case 401:
+            //     }
+            // }
+            const rejectData: apiResDataType =
+               error.code === CODE_ERR_CANCELED
+                  ? {
+                       code: error.code,
+                       message: "系统取消接口",
+                       data: null
+                    }
+                  : {
+                       code: 500,
+                       message: "系统运行异常,请联系管理员处理",
+                       data: null
+                    }
+            return Promise.reject(rejectData) // 返回接口返回的错误信息
+         }
+      )
+   }
+   // 创建实例
+   private createInstance() {
+      const axiosObj = this.axiosObj
+      return axios.create(
+         axiosObj?.tokenName
+            ? {
+                 baseURL: this.URL,
+                 headers: {
+                    [axiosObj.tokenName]: axiosObj.getTokenFun && axiosObj.getTokenFun()
+                 }
+              }
+            : {
+                 baseURL: this.URL
+              }
+      )
+   }
+   /**
+      axios请求方法
+      ```
+      {
+          data: data,     //get方法的时候为params:data
+          method: "post",
+          url: '/auth/loginIn'
+          headers:{
+                  'Content-Type':'application/json',
+          },
+          responseType: 'blob',
+      }
+      ```
+   */
+   axioseRquest(opt: AxiosConfigType) {
+      const instance = this.createInstance()
+      this.httpInterceptor(instance)
+      return instance(opt)
+   }
+}
+
+export default HttpAsynAxios

+ 12 - 0
src/libs/rem.ts

@@ -0,0 +1,12 @@
+export const baseWidth = 1920 // 设计图宽度
+export const baseSize = 18 //  rem 比例
+export let size = baseSize
+const minWidth = 680 //  最小缩放比例
+const doc = document.documentElement
+function refreshRem() {
+   const width = doc.getBoundingClientRect().width
+   size = baseSize * ((width > minWidth ? width : minWidth) / baseWidth)
+   doc.style.fontSize = size + "px"
+}
+refreshRem()
+window.addEventListener("resize", refreshRem)

+ 72 - 0
src/libs/tools.ts

@@ -0,0 +1,72 @@
+const classNameToArray = (cls = "") => cls.split(" ").filter(item => !!item.trim())
+/** Boolean 判断有没有这个class */
+export const hasClass = (el: HTMLElement | null | undefined, cls: string) => {
+   if (!el) return false
+   if (cls.includes(" ")) throw new Error("className should not contain space.")
+   return el.classList.contains(cls)
+}
+/**
+   添加class  cls:"a b c" or "a" or "a b"
+*/
+export const addClass = (el: HTMLElement | null | undefined, cls: string) => {
+   if (!el) return
+   el.classList.add(...classNameToArray(cls))
+}
+
+/** 删除class cls:"a b c" or "a" or "a b" */
+export const removeClass = (el: HTMLElement | null | undefined, cls: string) => {
+   if (!el) return
+   el.classList.remove(...classNameToArray(cls))
+}
+/**
+ * 获取el的style值
+ *
+ *  例:getStyle(document.querySelector("body"),"position") relative
+ */
+export const getStyle = (el: HTMLElement | null | undefined, styleName: keyof CSSStyleDeclaration): string => {
+   if (!el) return ""
+   if (styleName === "float") {
+      styleName = "cssFloat"
+   }
+   try {
+      const style = el.style[styleName]
+      if (style) return style as string
+      const computed = document.defaultView?.getComputedStyle(el, "")
+      return computed ? (computed[styleName] as string) : ""
+   } catch {
+      return el.style[styleName] as string
+   }
+}
+/**
+ * 给el修改 style
+ *
+      setStyle(document.querySelector("body"),{left:0,position:"relative"})
+      setStyle(document.querySelector("body"),"left","0"})
+*/
+export const setStyle = (el: HTMLElement | null | undefined, styleName: CSSStyleDeclaration | keyof CSSStyleDeclaration, value?: string) => {
+   if (!el) return
+   if (typeof styleName === "object") {
+      let prop: keyof CSSStyleDeclaration
+      for (prop in styleName) {
+         setStyle(el, prop, styleName[prop])
+      }
+   } else {
+      typeof value === "string" && (el.style[styleName as any] = value)
+   }
+}
+
+/** 删除style
+ *removeStyle(document.querySelector("body"),{left:"",position:""})
+ * removeStyle(document.querySelector("body"),"left")
+ */
+export const removeStyle = (el: HTMLElement | null | undefined, style: CSSStyleDeclaration | keyof CSSStyleDeclaration) => {
+   if (!el) return
+   if (typeof style === "object") {
+      let prop: keyof CSSStyleDeclaration
+      for (prop in style) {
+         setStyle(el, prop, "")
+      }
+   } else {
+      setStyle(el, style, "")
+   }
+}

+ 12 - 6
src/main.ts

@@ -1,11 +1,17 @@
-import './assets/main.css'
+import { createApp } from "vue"
+import App from "./App.vue"
+import "@/libs/rem"
 
-import { createApp } from 'vue'
-import App from './App.vue'
-import router from './router'
+import "./assets/normalize.css" //初始化css
+import "./assets/index.scss" //全局样式
+import "./assets/font/index.scss" //字体
 
 const app = createApp(App)
 
-app.use(router)
+import registerPlugin from "./registerPlugin" //注册插件
+registerPlugin(app)
 
-app.mount('#app')
+import registerComponents from "./registerComponents" //注册组件
+registerComponents(app)
+
+app.mount("#app")

+ 71 - 0
src/plugins/httpAjax.ts

@@ -0,0 +1,71 @@
+/**  http 方法封装 */
+import type { axiosApiType } from "@/libs/axios"
+import LoadingBar from "@/plugins/loadingBar"
+import { showToast } from "vant"
+
+/** ajax 请求 */
+export const httpAjax = function <T extends axiosApiType, P extends Parameters<T>>(axiosApi: T, ...nargs: P): Promise<apiResDataType> {
+   return new Promise(resolve => {
+      axiosApi(...nargs)
+         .then(res => {
+            resolve(res.data)
+         })
+         .catch(err => {
+            resolve(err)
+         })
+   })
+}
+/**错误会提示 ajax 请求 */
+export const httpAjaxErrMsg = function <T extends axiosApiType, P extends Parameters<T>>(axiosApi: T, ...nargs: P): Promise<apiResDataType> {
+   return new Promise(resolve => {
+      axiosApi(...nargs)
+         .then(res => {
+            const data = res.data as apiResDataType
+            if (data.code !== 200) {
+               showToast(data.message)
+            }
+            resolve(res.data)
+         })
+         .catch(err => {
+            showToast(err.message)
+            resolve(err)
+         })
+   })
+}
+/** 带loadingBar ajax 请求 */
+export const httpAjaxLoading = function <T extends axiosApiType, P extends Parameters<T>>(axiosApi: T, ...nargs: P): Promise<apiResDataType> {
+   return new Promise(resolve => {
+      LoadingBar.loading(true)
+      axiosApi(...nargs)
+         .then(res => {
+            LoadingBar.loading(false)
+            resolve(res.data)
+         })
+         .catch(err => {
+            LoadingBar.loading(false)
+            resolve(err)
+         })
+   })
+}
+/**
+ * 带loadingBar并且错误会提示 ajax 请求
+ */
+export const httpAjaxLoadingErrMsg = function <T extends axiosApiType, P extends Parameters<T>>(axiosApi: T, ...nargs: P): Promise<apiResDataType> {
+   return new Promise(resolve => {
+      LoadingBar.loading(true)
+      axiosApi(...nargs)
+         .then(res => {
+            LoadingBar.loading(false)
+            const data = res.data as apiResDataType
+            if (data.code !== 200) {
+               showToast(data.message)
+            }
+            resolve(res.data)
+         })
+         .catch(err => {
+            LoadingBar.loading(false)
+            showToast(err.message)
+            resolve(err)
+         })
+   })
+}

+ 12 - 0
src/plugins/loadingBar/index.ts

@@ -0,0 +1,12 @@
+import loadingBar from "./loadingBar.vue"
+import { h, render } from "vue"
+const vnode = h(loadingBar)
+render(vnode, document.body)
+const vm = vnode.component!.proxy as InstanceType<typeof loadingBar>
+/** 全局加载条
+   import LoadingBar from "@/plugin/loadingBar"
+
+   LoadingBar.loading(isshow,text)
+*/
+const LoadingBar = vm
+export default LoadingBar

+ 123 - 0
src/plugins/loadingBar/loadingBar.vue

@@ -0,0 +1,123 @@
+<!--
+* @FileDescription: 全局加载条
+* @Author: 黄琪勇
+* @Date:2022-05-23 15:33:04
+-->
+<template>
+   <div v-show="loadingBarData.show" class="h-loading-bar">
+      <div class="loading-bar-content">
+         <div class="loadingBox">
+            <div class="loadingItem"></div>
+            <div class="loadingItem"></div>
+            <div class="loadingItem"></div>
+            <div class="loadingItem"></div>
+         </div>
+         <div class="loadingTip">{{ loadingBarData.text }}</div>
+      </div>
+   </div>
+</template>
+
+<!-- <script setup lang="ts">
+import { shallowReactive } from "vue"
+import { addClass, removeClass } from "@/libs/tools"
+
+const loadingBarData = shallowReactive({
+   text: "",
+   show: false
+})
+const body = document.querySelector("body"),
+   name = "h-loadingBarBody",
+   defaultText = "正在拼命为您加载中..."
+/** 显示或关闭加载条 */
+const loading = (isShow: boolean, text?: string) => {
+   loadingBarData.text = text ?? defaultText
+   loadingBarData.show = isShow
+   loadingBarData.show ? addClass(body, name) : removeClass(body, name)
+}
+defineExpose({ loading })
+</script> -->
+
+<!-- 由于setup模式 编译方式好像改了 defineExpose出去的属性proxy上面访问不到  setup()访问的到 所以改为这种模式-->
+<script lang="ts">
+import { shallowReactive, defineComponent } from "vue"
+import { addClass, removeClass } from "@/libs/tools"
+export default defineComponent({
+   setup() {
+      const loadingBarData = shallowReactive({
+         text: "",
+         show: false
+      })
+      const body = document.querySelector("body"),
+         name = "h-loadingBarBody",
+         defaultText = "正在加载中..."
+      /** 显示或关闭加载条 */
+      const loading = (isShow: boolean, text?: string) => {
+         loadingBarData.text = text ?? defaultText
+         loadingBarData.show = isShow
+         loadingBarData.show ? addClass(body, name) : removeClass(body, name)
+      }
+      return {
+         loadingBarData,
+         loading
+      }
+   }
+})
+</script>
+<!-- 取消scoped 减少属性权重  外部添加class可以修改里面的样式 -->
+<style lang="scss">
+body.h-loadingBarBody {
+   overflow: hidden;
+}
+.h-loading-bar {
+   position: fixed;
+   top: 0;
+   left: 0;
+   z-index: 12345678;
+   cursor: wait;
+   width: 100%;
+   height: 100%;
+   background: rgba(255, 255, 255, 0.4);
+   .loading-bar-content {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      z-index: 1;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      .loadingBox {
+         width: 36px;
+         height: 36px;
+         display: flex;
+         justify-content: space-between;
+         flex-wrap: wrap;
+         align-content: space-between;
+         margin-bottom: 28px;
+         animation: rotate 1.5s linear infinite;
+         .loadingItem {
+            width: 16px;
+            height: 16px;
+            border-radius: 50%;
+            background: $themeColor;
+            opacity: 0.5;
+            &:nth-child(2) {
+               opacity: 1;
+            }
+         }
+      }
+      .loadingTip {
+         font-size: 20px;
+         color: $themeColor;
+      }
+      @keyframes rotate {
+         from {
+            transform: rotate(0deg);
+         }
+         to {
+            transform: rotate(360deg);
+         }
+      }
+   }
+}
+</style>

+ 19 - 0
src/plugins/nprogress/index.ts

@@ -0,0 +1,19 @@
+import NProgress from "nprogress"
+import "nprogress/nprogress.css"
+import "./nprogress.scss"
+
+NProgress.configure({
+   // 动画方式
+   easing: "ease",
+   // 递增进度条的速度
+   speed: 500,
+   // 是否显示加载ico
+   showSpinner: false,
+   // 自动递增间隔
+   trickleSpeed: 200,
+   // 初始化时的最小百分比
+   minimum: 0.3
+})
+
+/** 进度条 */
+export default NProgress

+ 18 - 0
src/plugins/nprogress/nprogress.scss

@@ -0,0 +1,18 @@
+/*
+   重置颜色
+ */
+
+#nprogress .bar {
+   background: $themeColor;
+}
+
+#nprogress .peg {
+   box-shadow:
+      0 0 10px $themeColor,
+      0 0 5px $themeColor;
+}
+
+#nprogress .spinner-icon {
+   border-top-color: $themeColor;
+   border-left-color: $themeColor;
+}

+ 14 - 0
src/queryParams/index.ts

@@ -0,0 +1,14 @@
+import { reactive } from "vue"
+import router from "@/router"
+
+/** 维护其他系统过来的特殊字段  这里用来记录,以免其他系统传过来的参数混乱 不好维护 */
+export type queryParamsType = {}
+const queryParams = reactive<queryParamsType>({})
+
+export function initQueryParams() {
+   const query = router.currentRoute.value.query
+   console.log(query)
+   console.log(queryParams, "携带参数")
+}
+
+export default queryParams

+ 3 - 0
src/registerComponents.ts

@@ -0,0 +1,3 @@
+import { type App } from "vue"
+
+export default (app: App<Element>) => {}

+ 10 - 0
src/registerPlugin.ts

@@ -0,0 +1,10 @@
+import type { App } from "vue"
+import router from "@/router"
+import setupPinia from "@/store"
+
+export default (app: App<Element>) => {
+   //路由
+   app.use(router)
+   //pinia
+   setupPinia(app)
+}

+ 68 - 18
src/router/index.ts

@@ -1,23 +1,73 @@
-import { createRouter, createWebHistory } from 'vue-router'
-import HomeView from '../views/HomeView.vue'
+import { createRouter, createWebHashHistory } from "vue-router"
+import { constRoutes } from "./routers"
+import NProgress from "@/plugins/nprogress"
+import { getToken, CODE401 } from "@/libs/auth"
+import userStore from "@/store/modules/user"
+
+const userStoreHook = userStore()
+
+userStoreHook.login()
 
 const router = createRouter({
-  history: createWebHistory(import.meta.env.BASE_URL),
-  routes: [
-    {
-      path: '/',
-      name: 'home',
-      component: HomeView,
-    },
-    {
-      path: '/about',
-      name: 'about',
-      // route level code-splitting
-      // this generates a separate chunk (About.[hash].js) for this route
-      // which is lazy-loaded when the route is visited.
-      component: () => import('../views/AboutView.vue'),
-    },
-  ],
+   history: createWebHashHistory(import.meta.env.BASE_URL),
+   routes: constRoutes,
+   scrollBehavior() {
+      return { left: 0, top: 0 }
+   }
+})
+function isRegWhite(regs: RegExp[], path: string) {
+   return (
+      regs.findIndex(reg => {
+         return reg.test(path)
+      }) > -1
+   )
+}
+const authWhiteList = ["/login"] // 不访问接口权限白名单
+const regWhiteList: RegExp[] = [] //正则白名单(用于系统不需要权限的分享页面)
+
+router.beforeEach((to, from, next) => {
+   NProgress.start()
+   const hasToken = getToken()
+   //如果有token且不是白名单页面则加载info
+   if (hasToken && !authWhiteList.includes(to.path)) {
+      //有roles时候
+      const hasRoles = userStoreHook.roles
+      if (hasRoles) {
+         next()
+      } else {
+         userStoreHook
+            .getUserInfo()
+            .then(() => {
+               next()
+            })
+            .catch((err: apiResDataType) => {
+               //退出 清空,当是登录权限问题时候 axios 里面会清空
+               if (err.code !== CODE401) {
+                  //ElMessage.error(err.message)
+                  userStoreHook.resetUser()
+               }
+            })
+      }
+   } else {
+      // 如果有token,并且是登录页 直接返回首页
+      if (hasToken && to.path === "/login") {
+         next({
+            path: "/"
+         })
+         return
+      }
+      if (authWhiteList.includes(to.path) || isRegWhite(regWhiteList, to.path)) {
+         next()
+      } else {
+         next({
+            path: "/login"
+         })
+      }
+   }
+})
+
+router.afterEach(() => {
+   NProgress.done()
 })
 
 export default router

+ 20 - 0
src/router/routers.ts

@@ -0,0 +1,20 @@
+import type { RouteRecordRaw } from "vue-router"
+
+/** 静态路由 */
+export const constRoutes: Array<RouteRecordRaw> = [
+   {
+      path: "/",
+      name: "rhythm",
+      component: () => import("@/views/rhythm")
+   },
+   {
+      path: "/login",
+      name: "login",
+      component: () => import("@/viewsframe/login")
+   },
+   {
+      path: "/:pathMatch(.*)*",
+      name: "errorPage",
+      component: () => import("@/viewsframe/errorPage")
+   }
+]

+ 7 - 0
src/store/index.ts

@@ -0,0 +1,7 @@
+import type { App } from "vue"
+import { createPinia } from "pinia"
+
+export const store = createPinia()
+export default (app: App<Element>) => {
+   app.use(store)
+}

+ 71 - 0
src/store/modules/user.ts

@@ -0,0 +1,71 @@
+import { defineStore } from "pinia"
+import { removeToken, setToken, setCODE401 } from "@/libs/auth"
+import router from "@/router"
+import { httpAjax } from "@/plugins/httpAjax"
+import { store } from "../index"
+import { getUserInfoApi } from "@/api/user"
+interface userType {
+   userInfo: {
+      nickname?: string
+   }
+   roles: ""
+}
+
+const useStore = defineStore("user", {
+   state: (): userType => {
+      return {
+         userInfo: {}, //用户信息
+         roles: "" //用户角色
+      }
+   },
+   actions: {
+      /** 登录 */
+      async login() {
+         function getAuthorizationFromUrl() {
+            const fullUrl = window.location.href
+            const queryIndex = fullUrl.indexOf("?")
+            if (queryIndex === -1) return undefined
+            const queryString = fullUrl.slice(queryIndex + 1)
+            const params = new URLSearchParams(queryString)
+            return params.get("Authorization") || undefined
+         }
+         const token = getAuthorizationFromUrl()
+         token && setToken(token)
+      },
+      /** 获取用户信息 */
+      async getUserInfo() {
+         const userInfoRes = await httpAjax(getUserInfoApi)
+         if (userInfoRes.code !== 200) {
+            return Promise.reject(userInfoRes)
+         }
+         const userInfo = userInfoRes.data || {}
+         if (!userInfo.id) {
+            return Promise.reject({
+               code: "500",
+               data: null,
+               message: "获取用户信息出错,请联系管理员!"
+            })
+         }
+         this.userInfo = userInfo
+         this.roles = userInfo.id
+         return Promise.resolve()
+      },
+      /** 退出登录 */
+      async loginOut() {
+         this.resetUser()
+         return Promise.resolve()
+      },
+      /** 清空所有登录信息,退出 */
+      resetUser() {
+         this.userInfo = {}
+         this.roles = ""
+         removeToken()
+         router.push({
+            path: "/login"
+         })
+      }
+   }
+})
+export default () => {
+   return useStore(store)
+}

+ 0 - 15
src/views/AboutView.vue

@@ -1,15 +0,0 @@
-<template>
-  <div class="about">
-    <h1>This is an about page</h1>
-  </div>
-</template>
-
-<style>
-@media (min-width: 1024px) {
-  .about {
-    min-height: 100vh;
-    display: flex;
-    align-items: center;
-  }
-}
-</style>

+ 0 - 9
src/views/HomeView.vue

@@ -1,9 +0,0 @@
-<script setup lang="ts">
-import TheWelcome from '../components/TheWelcome.vue'
-</script>
-
-<template>
-  <main>
-    <TheWelcome />
-  </main>
-</template>

+ 0 - 0
src/views/rhythm/index.ts


+ 7 - 0
src/views/rhythm/rhythm.vue

@@ -0,0 +1,7 @@
+<template>
+   <div class="">22111</div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped></style>

+ 43 - 0
src/viewsframe/errorPage/errorPage.vue

@@ -0,0 +1,43 @@
+<template>
+   <div class="errorPage">
+      <div class="error">
+         <img src="./imgs/404.png" class="img" alt="" />
+         <div class="tit">页面找不到了~</div>
+      </div>
+   </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped>
+.errorPage {
+   width: 100%;
+   height: 100%;
+   display: flex;
+   justify-content: center;
+   align-items: center;
+   .error {
+      background-color: #fff;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      margin-top: -100px;
+      .img {
+         width: 272px;
+         height: 227px;
+      }
+      .tit {
+         font-weight: 400;
+         font-size: 16px;
+         color: #999999;
+         margin-top: 16px;
+         margin-bottom: 20px;
+      }
+      .backBtn {
+         width: 88px;
+         height: 36px;
+      }
+   }
+}
+</style>

BIN
src/viewsframe/errorPage/imgs/404.png


+ 2 - 0
src/viewsframe/errorPage/index.ts

@@ -0,0 +1,2 @@
+import errorPage from "./errorPage.vue"
+export default errorPage

+ 2 - 0
src/viewsframe/login/index.ts

@@ -0,0 +1,2 @@
+import login from "./login.vue"
+export default login

+ 43 - 0
src/viewsframe/login/login.vue

@@ -0,0 +1,43 @@
+<template>
+   <div class="login">
+      <div class="error">
+         <img src="../errorPage/imgs/404.png" class="img" alt="" />
+         <div class="tit">登录已过期或服务器错误!</div>
+      </div>
+   </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped>
+.login {
+   width: 100%;
+   height: 100%;
+   display: flex;
+   justify-content: center;
+   align-items: center;
+   .error {
+      background-color: #fff;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      margin-top: -100px;
+      .img {
+         width: 272px;
+         height: 227px;
+      }
+      .tit {
+         font-weight: 400;
+         font-size: 16px;
+         color: #999999;
+         margin-top: 16px;
+         margin-bottom: 20px;
+      }
+      .backBtn {
+         width: 88px;
+         height: 36px;
+      }
+   }
+}
+</style>