Jelajahi Sumber

feat:使用pinia替换vuex;新增markdown编辑器

lin-xin 3 tahun lalu
induk
melakukan
a072752609
14 mengubah file dengan 916 tambahan dan 1577 penghapusan
  1. 7 5
      README.md
  2. 0 700
      package-lock.json
  3. 4 3
      package.json
  4. 161 162
      src/components/Header.vue
  5. 156 154
      src/components/Sidebar.vue
  6. 174 183
      src/components/Tags.vue
  7. 2 3
      src/main.js
  8. 150 143
      src/router/index.js
  9. 0 56
      src/store/index.js
  10. 17 0
      src/store/sidebar.js
  11. 48 0
      src/store/tags.js
  12. 41 44
      src/views/Home.vue
  13. 124 124
      src/views/Login.vue
  14. 32 0
      src/views/Markdown.vue

+ 7 - 5
README.md

@@ -1,10 +1,10 @@
 # vue-manage-system
 
 <a href="https://github.com/vuejs/vue">
-    <img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue">
+    <img src="https://img.shields.io/badge/vue-3.1.2-brightgreen.svg" alt="vue">
   </a>
-  <a href="https://github.com/ElemeFE/element">
-    <img src="https://img.shields.io/badge/element--ui-2.8.2-brightgreen.svg" alt="element-ui">
+  <a href="https://github.com/vuejs/pinia">
+    <img src="https://img.shields.io/badge/pinia-2.0.14-brightgreen.svg" alt="pinia">
   </a>
   <a href="https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE">
     <img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
@@ -16,7 +16,7 @@
     <img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
   </a>
 
-基于 Vue3 + Element Plus 的后台管理系统解决方案。[线上地址](https://lin-xin.gitee.io/example/work/)
+基于 Vue3 + pinia + Element Plus 的后台管理系统解决方案。[线上地址](https://lin-xin.gitee.io/example/work/)
 
 > Vue2 版本请看 [tag-V4.2.0](https://github.com/lin-xin/vue-manage-system/tree/V4.2.0)
 
@@ -48,7 +48,7 @@
 
 ## 前言
 
-该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(Web Management System)开发。基于 Vue3,使用 vue-cli3 脚手架,引用 Element Plus 组件库,方便开发快速简洁好看的组件。分离颜色样式,支持手动切换主题色,而且很方便使用自定义主题色。
+该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(Web Management System)开发。基于 Vue3 + pinia,引用 Element Plus 组件库,方便开发快速简洁好看的组件。分离颜色样式,支持手动切换主题色,而且很方便使用自定义主题色。
 
 ## 功能
 
@@ -66,6 +66,8 @@
 -   [x] 三级菜单
 -   [x] 自定义图标
 -   [x] 国际化
+-   [x] vite
+-   [x] pinia
 
 ## 安装步骤
 

+ 0 - 700
package-lock.json

@@ -1,700 +0,0 @@
-{
-  "name": "vue-manage-system",
-  "version": "5.1.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "@babel/helper-validator-identifier": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
-      "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg=="
-    },
-    "@babel/parser": {
-      "version": "7.14.7",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.7.tgz",
-      "integrity": "sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA=="
-    },
-    "@babel/runtime": {
-      "version": "7.14.6",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz",
-      "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==",
-      "requires": {
-        "regenerator-runtime": "^0.13.4"
-      }
-    },
-    "@babel/runtime-corejs3": {
-      "version": "7.14.7",
-      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz",
-      "integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==",
-      "requires": {
-        "core-js-pure": "^3.15.0",
-        "regenerator-runtime": "^0.13.4"
-      }
-    },
-    "@babel/types": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz",
-      "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==",
-      "requires": {
-        "@babel/helper-validator-identifier": "^7.14.5",
-        "to-fast-properties": "^2.0.0"
-      }
-    },
-    "@intlify/core-base": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.6.tgz",
-      "integrity": "sha512-d5GDPpsQbqPkisSJA5b6nJFEkalY/IHAd7vOLNd/Sj4YaNRzXtInu2FoqKiOv8e/lQnXGTpurdCZg5Jxq1Gsxw==",
-      "requires": {
-        "@intlify/devtools-if": "9.1.6",
-        "@intlify/message-compiler": "9.1.6",
-        "@intlify/message-resolver": "9.1.6",
-        "@intlify/runtime": "9.1.6",
-        "@intlify/shared": "9.1.6",
-        "@intlify/vue-devtools": "9.1.6"
-      }
-    },
-    "@intlify/devtools-if": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.6.tgz",
-      "integrity": "sha512-m8Api+kh+BtFa2FZ/JjIdr1ibsGGqBjdKCzWo5BZecEUxBquIeOQZwpokPh/0K5j+/PZleFXkVAMC5mNt+9WdA==",
-      "requires": {
-        "@intlify/shared": "9.1.6"
-      }
-    },
-    "@intlify/message-compiler": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.6.tgz",
-      "integrity": "sha512-DR8645VOrVK6x/8tkaCpHnckMAIcoOgeNS5j0wB12RfZoXYQp7vAXMaOP511KMll2mXCREgIB0ojpajiof7yzQ==",
-      "requires": {
-        "@intlify/message-resolver": "9.1.6",
-        "@intlify/shared": "9.1.6",
-        "source-map": "0.6.1"
-      }
-    },
-    "@intlify/message-resolver": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.6.tgz",
-      "integrity": "sha512-UUnbawQa5U9sffd5wRIscqtyY1xWlwJbyfwCLPEWLvBhyAnCwPYlvaHGnnO0CSi0fzJTVwlV9DYzobh3agDeMA=="
-    },
-    "@intlify/runtime": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.6.tgz",
-      "integrity": "sha512-U1QZ+TPf3kQQvWo4BA2mj3cHAxMRHXNTBhu2u+deh6ubTqXdZ19XGBTMSasrXG6RE+zSio9oM+ndoLja7JGtPg==",
-      "requires": {
-        "@intlify/message-compiler": "9.1.6",
-        "@intlify/message-resolver": "9.1.6",
-        "@intlify/shared": "9.1.6"
-      }
-    },
-    "@intlify/shared": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.6.tgz",
-      "integrity": "sha512-6MtsKulyfZxdD7OuxjaODjj8QWoHCnLFAk4wkWiHqBCa6UCTC0qXjtEeZ1MxpQihvFmmJZauBUu25EvtngW5qQ=="
-    },
-    "@intlify/vue-devtools": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.6.tgz",
-      "integrity": "sha512-UdNovg4OML9rIr1sOGZzTfNr1nUy4UQpDf5ni4dNC93T6FIkVJz0n1Np7Vp7e6gDjcmufRYcV99tEwjQSN9+5A==",
-      "requires": {
-        "@intlify/message-resolver": "9.1.6",
-        "@intlify/runtime": "9.1.6",
-        "@intlify/shared": "9.1.6"
-      }
-    },
-    "@popperjs/core": {
-      "version": "2.9.2",
-      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz",
-      "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q=="
-    },
-    "@types/estree": {
-      "version": "0.0.48",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz",
-      "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==",
-      "dev": true
-    },
-    "@types/lodash": {
-      "version": "4.14.170",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz",
-      "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q=="
-    },
-    "@vitejs/plugin-vue": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.2.3.tgz",
-      "integrity": "sha512-LlnLpObkGKZ+b7dcpL4T24l13nPSHLjo+6Oc7MbZiKz5PMAUzADfNJ3EKfYIQ0l0969nxf2jp/9vsfnuJ7h6fw==",
-      "dev": true
-    },
-    "@vue/compiler-core": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.2.tgz",
-      "integrity": "sha512-nHmq7vLjq/XM2IMbZUcKWoH5sPXa2uR/nIKZtjbK5F3TcbnYE/zKsrSUR9WZJ03unlwotNBX1OyxVt9HbWD7/Q==",
-      "requires": {
-        "@babel/parser": "^7.12.0",
-        "@babel/types": "^7.12.0",
-        "@vue/shared": "3.1.2",
-        "estree-walker": "^2.0.1",
-        "source-map": "^0.6.1"
-      }
-    },
-    "@vue/compiler-dom": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.2.tgz",
-      "integrity": "sha512-k2+SWcWH0jL6WQAX7Or2ONqu5MbtTgTO0dJrvebQYzgqaKMXNI90RNeWeCxS4BnNFMDONpHBeFgbwbnDWIkmRg==",
-      "requires": {
-        "@vue/compiler-core": "3.1.2",
-        "@vue/shared": "3.1.2"
-      }
-    },
-    "@vue/compiler-sfc": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.2.tgz",
-      "integrity": "sha512-SeG/2+DvwejQ7oAiSx8BrDh5qOdqCYHGClPiTvVIHTfSIHiS2JjMbCANdDCjHkTOh/O7WZzo2JhdKm98bRBxTw==",
-      "dev": true,
-      "requires": {
-        "@babel/parser": "^7.13.9",
-        "@babel/types": "^7.13.0",
-        "@types/estree": "^0.0.48",
-        "@vue/compiler-core": "3.1.2",
-        "@vue/compiler-dom": "3.1.2",
-        "@vue/compiler-ssr": "3.1.2",
-        "@vue/shared": "3.1.2",
-        "consolidate": "^0.16.0",
-        "estree-walker": "^2.0.1",
-        "hash-sum": "^2.0.0",
-        "lru-cache": "^5.1.1",
-        "magic-string": "^0.25.7",
-        "merge-source-map": "^1.1.0",
-        "postcss": "^8.1.10",
-        "postcss-modules": "^4.0.0",
-        "postcss-selector-parser": "^6.0.4",
-        "source-map": "^0.6.1"
-      }
-    },
-    "@vue/compiler-ssr": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.2.tgz",
-      "integrity": "sha512-BwXo9LFk5OSWdMyZQ4bX1ELHX0Z/9F+ld/OaVnpUPzAZCHslBYLvyKUVDwv2C/lpLjRffpC2DOUEdl1+RP1aGg==",
-      "dev": true,
-      "requires": {
-        "@vue/compiler-dom": "3.1.2",
-        "@vue/shared": "3.1.2"
-      }
-    },
-    "@vue/devtools-api": {
-      "version": "6.0.0-beta.14",
-      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.14.tgz",
-      "integrity": "sha512-44fPrrN1cqcs6bFkT0C+yxTM6PZXLbR+ESh1U1j8UD22yO04gXvxH62HApMjLbS3WqJO/iCNC+CYT+evPQh2EQ=="
-    },
-    "@vue/reactivity": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.2.tgz",
-      "integrity": "sha512-glJzJoN2xE7I2lRvwKM5u1BHRPTd1yc8iaf//Lai/78/uYAvE5DXp5HzWRFOwMlbRvMGJHIQjOqoxj87cDAaag==",
-      "requires": {
-        "@vue/shared": "3.1.2"
-      }
-    },
-    "@vue/runtime-core": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.1.2.tgz",
-      "integrity": "sha512-gsPZG4dRIkixuuKmoj4P9IHgfT0yaFLcqWOM5F/bCk0nxQn1XtxH8oUehWuET726KhbukvDoJfe9G2CKviy80w==",
-      "requires": {
-        "@vue/reactivity": "3.1.2",
-        "@vue/shared": "3.1.2"
-      }
-    },
-    "@vue/runtime-dom": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.1.2.tgz",
-      "integrity": "sha512-QvINxjLucEZFzp5f0NVu7JqWYCv5TKQfkH2FDs/N6QNE4iKcYtKrWdT0HKfABnVXG28Znqv6rIH0dH4ZAOwxpA==",
-      "requires": {
-        "@vue/runtime-core": "3.1.2",
-        "@vue/shared": "3.1.2",
-        "csstype": "^2.6.8"
-      }
-    },
-    "@vue/shared": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.2.tgz",
-      "integrity": "sha512-EmH/poaDWBPJaPILXNI/1fvUbArJQmmTyVCwvvyDYDFnkPoTclAbHRAtyIvqfez7jybTDn077HTNILpxlsoWhg=="
-    },
-    "async-validator": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-3.5.2.tgz",
-      "integrity": "sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ=="
-    },
-    "axios": {
-      "version": "0.21.1",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
-      "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
-      "requires": {
-        "follow-redirects": "^1.10.0"
-      }
-    },
-    "big.js": {
-      "version": "5.2.2",
-      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
-      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
-      "dev": true
-    },
-    "bluebird": {
-      "version": "3.7.2",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
-      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
-      "dev": true
-    },
-    "colorette": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
-      "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
-      "dev": true
-    },
-    "consolidate": {
-      "version": "0.16.0",
-      "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz",
-      "integrity": "sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==",
-      "dev": true,
-      "requires": {
-        "bluebird": "^3.7.2"
-      }
-    },
-    "core-js-pure": {
-      "version": "3.15.1",
-      "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.1.tgz",
-      "integrity": "sha512-OZuWHDlYcIda8sJLY4Ec6nWq2hRjlyCqCZ+jCflyleMkVt3tPedDVErvHslyS2nbO+SlBFMSBJYvtLMwxnrzjA=="
-    },
-    "cropperjs": {
-      "version": "1.5.12",
-      "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.12.tgz",
-      "integrity": "sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw=="
-    },
-    "cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true
-    },
-    "csstype": {
-      "version": "2.6.17",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz",
-      "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A=="
-    },
-    "dayjs": {
-      "version": "1.10.5",
-      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.5.tgz",
-      "integrity": "sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g=="
-    },
-    "element-plus": {
-      "version": "1.0.2-beta.52",
-      "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-1.0.2-beta.52.tgz",
-      "integrity": "sha512-oAuJHwXyvM4dsuOz7HSDPIBVPqRJ1KEzFzGqYdqbBjQ/aw79uCJxvS9Q4q9/XrPMfPire09+bPTypiIaHkNBhA==",
-      "requires": {
-        "@popperjs/core": "^2.4.4",
-        "@types/lodash": "^4.14.161",
-        "async-validator": "^3.4.0",
-        "dayjs": "1.x",
-        "lodash": "^4.17.20",
-        "mitt": "^2.1.0",
-        "normalize-wheel": "^1.0.1",
-        "resize-observer-polyfill": "^1.5.1"
-      }
-    },
-    "emojis-list": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
-      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
-      "dev": true
-    },
-    "esbuild": {
-      "version": "0.12.9",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.9.tgz",
-      "integrity": "sha512-MWRhAbMOJ9RJygCrt778rz/qNYgA4ZVj6aXnNPxFjs7PmIpb0fuB9Gmg5uWrr6n++XKwwm/RmSz6RR5JL2Ocsw==",
-      "dev": true
-    },
-    "estree-walker": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
-    },
-    "follow-redirects": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
-      "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg=="
-    },
-    "fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "dev": true,
-      "optional": true
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "generic-names": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz",
-      "integrity": "sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==",
-      "dev": true,
-      "requires": {
-        "loader-utils": "^1.1.0"
-      }
-    },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "requires": {
-        "function-bind": "^1.1.1"
-      }
-    },
-    "hash-sum": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
-      "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
-      "dev": true
-    },
-    "icss-replace-symbols": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
-      "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
-      "dev": true
-    },
-    "icss-utils": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
-      "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
-      "dev": true
-    },
-    "is-core-module": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
-      "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.3"
-      }
-    },
-    "json5": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-      "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-      "dev": true,
-      "requires": {
-        "minimist": "^1.2.0"
-      }
-    },
-    "loader-utils": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
-      "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
-      "dev": true,
-      "requires": {
-        "big.js": "^5.2.2",
-        "emojis-list": "^3.0.0",
-        "json5": "^1.0.1"
-      }
-    },
-    "lodash": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
-    },
-    "lodash.camelcase": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
-      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
-      "dev": true
-    },
-    "lru-cache": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
-      "dev": true,
-      "requires": {
-        "yallist": "^3.0.2"
-      }
-    },
-    "magic-string": {
-      "version": "0.25.7",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
-      "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
-      "dev": true,
-      "requires": {
-        "sourcemap-codec": "^1.4.4"
-      }
-    },
-    "merge-source-map": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
-      "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
-      "dev": true,
-      "requires": {
-        "source-map": "^0.6.1"
-      }
-    },
-    "minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-      "dev": true
-    },
-    "mitt": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
-      "integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
-    },
-    "nanoid": {
-      "version": "3.1.23",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
-      "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==",
-      "dev": true
-    },
-    "normalize-wheel": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
-      "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU="
-    },
-    "path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true
-    },
-    "postcss": {
-      "version": "8.3.5",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz",
-      "integrity": "sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==",
-      "dev": true,
-      "requires": {
-        "colorette": "^1.2.2",
-        "nanoid": "^3.1.23",
-        "source-map-js": "^0.6.2"
-      }
-    },
-    "postcss-modules": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.1.3.tgz",
-      "integrity": "sha512-dBT39hrXe4OAVYJe/2ZuIZ9BzYhOe7t+IhedYeQ2OxKwDpAGlkEN/fR0fGnrbx4BvgbMReRX4hCubYK9cE/pJQ==",
-      "dev": true,
-      "requires": {
-        "generic-names": "^2.0.1",
-        "icss-replace-symbols": "^1.1.0",
-        "lodash.camelcase": "^4.3.0",
-        "postcss-modules-extract-imports": "^3.0.0",
-        "postcss-modules-local-by-default": "^4.0.0",
-        "postcss-modules-scope": "^3.0.0",
-        "postcss-modules-values": "^4.0.0",
-        "string-hash": "^1.1.1"
-      }
-    },
-    "postcss-modules-extract-imports": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
-      "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
-      "dev": true
-    },
-    "postcss-modules-local-by-default": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
-      "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
-      "dev": true,
-      "requires": {
-        "icss-utils": "^5.0.0",
-        "postcss-selector-parser": "^6.0.2",
-        "postcss-value-parser": "^4.1.0"
-      }
-    },
-    "postcss-modules-scope": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
-      "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
-      "dev": true,
-      "requires": {
-        "postcss-selector-parser": "^6.0.4"
-      }
-    },
-    "postcss-modules-values": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
-      "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
-      "dev": true,
-      "requires": {
-        "icss-utils": "^5.0.0"
-      }
-    },
-    "postcss-selector-parser": {
-      "version": "6.0.6",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz",
-      "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==",
-      "dev": true,
-      "requires": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      }
-    },
-    "postcss-value-parser": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
-      "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
-      "dev": true
-    },
-    "regenerator-runtime": {
-      "version": "0.13.7",
-      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
-      "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
-    },
-    "resize-observer-polyfill": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
-      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
-    },
-    "resolve": {
-      "version": "1.20.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
-      "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
-      "dev": true,
-      "requires": {
-        "is-core-module": "^2.2.0",
-        "path-parse": "^1.0.6"
-      }
-    },
-    "rollup": {
-      "version": "2.52.3",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz",
-      "integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==",
-      "dev": true,
-      "requires": {
-        "fsevents": "~2.3.2"
-      }
-    },
-    "schart.js": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/schart.js/-/schart.js-3.0.4.tgz",
-      "integrity": "sha512-uylb2u9rrHX1jyAuSAJUQON8XTfyDKI9kWj1J3fUlCQCkLVZ4HG4+IiV8qm//Z71dqvLI78QZ/fCBw0reB22Zw=="
-    },
-    "source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
-    },
-    "source-map-js": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz",
-      "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==",
-      "dev": true
-    },
-    "sourcemap-codec": {
-      "version": "1.4.8",
-      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
-      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
-      "dev": true
-    },
-    "string-hash": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz",
-      "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=",
-      "dev": true
-    },
-    "to-fast-properties": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
-    },
-    "tslib": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
-      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "dev": true
-    },
-    "vite": {
-      "version": "2.3.7",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-2.3.7.tgz",
-      "integrity": "sha512-Y0xRz11MPYu/EAvzN94+FsOZHbSvO6FUvHv127CyG7mV6oDoay2bw+g5y9wW3Blf8OY3chaz3nc/DcRe1IQ3Nw==",
-      "dev": true,
-      "requires": {
-        "esbuild": "^0.12.5",
-        "fsevents": "~2.3.1",
-        "postcss": "^8.3.0",
-        "resolve": "^1.19.0",
-        "rollup": "^2.38.5"
-      }
-    },
-    "vue": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.1.2.tgz",
-      "integrity": "sha512-q/rbKpb7aofax4ugqu2k/uj7BYuNPcd6Z5/qJtfkJQsE0NkwVoCyeSh7IZGH61hChwYn3CEkh4bHolvUPxlQ+w==",
-      "requires": {
-        "@vue/compiler-dom": "3.1.2",
-        "@vue/runtime-dom": "3.1.2",
-        "@vue/shared": "3.1.2"
-      }
-    },
-    "vue-cropperjs": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/vue-cropperjs/-/vue-cropperjs-5.0.0.tgz",
-      "integrity": "sha512-RhnC8O33uRZNkn74aiHZwNHnBJOXWlS4P6gsRI0lw4cZlWjKSCywZI9oSI9POlIPI6OYv30jvnHMXGch85tw7w==",
-      "requires": {
-        "cropperjs": "^1.5.6"
-      }
-    },
-    "vue-i18n": {
-      "version": "9.1.6",
-      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.6.tgz",
-      "integrity": "sha512-FEC4HZkTH6QRIu/A0wlo0VS/GH3w/fuCC6xfvoC8IyhhtbG9A+go9NfW+HZ1ZXdAcO4EWcVQi04M+iSwuxgixw==",
-      "requires": {
-        "@intlify/core-base": "9.1.6",
-        "@intlify/shared": "9.1.6",
-        "@intlify/vue-devtools": "9.1.6",
-        "@vue/devtools-api": "^6.0.0-beta.7"
-      }
-    },
-    "vue-router": {
-      "version": "4.0.10",
-      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.10.tgz",
-      "integrity": "sha512-YbPf6QnZpyyWfnk7CUt2Bme+vo7TLfg1nGZNkvYqKYh4vLaFw6Gn8bPGdmt5m4qrGnKoXLqc4htAsd3dIukICA==",
-      "requires": {
-        "@vue/devtools-api": "^6.0.0-beta.14"
-      }
-    },
-    "vue-schart": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/vue-schart/-/vue-schart-2.0.0.tgz",
-      "integrity": "sha512-qAu3e5wfMcq26wK1xeHExEWfGpnjfoN1R/9QXblNi+AsU/p52X7tTwhi+Fw7H/otfEufhEY2X7z7emaoF4QO+g==",
-      "requires": {
-        "schart.js": "^3.0.0"
-      }
-    },
-    "vuex": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
-      "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
-      "requires": {
-        "@vue/devtools-api": "^6.0.0-beta.11"
-      }
-    },
-    "wangeditor": {
-      "version": "4.7.4",
-      "resolved": "https://registry.npmjs.org/wangeditor/-/wangeditor-4.7.4.tgz",
-      "integrity": "sha512-MVWJyFZm3SOhIGFsxDwD6Q0ahIWQTYU0/otHxq85EnfdxtJBJ8c55iKDp3zIvBZaiBawBiYCpRmbn8HOedNj5w==",
-      "requires": {
-        "@babel/runtime": "^7.11.2",
-        "@babel/runtime-corejs3": "^7.11.2",
-        "tslib": "^2.1.0"
-      }
-    },
-    "yallist": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-      "dev": true
-    }
-  }
-}

+ 4 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "vue-manage-system",
-  "version": "5.1.0",
+  "version": "5.2.0",
   "private": true,
   "scripts": {
     "dev": "vite",
@@ -9,13 +9,14 @@
   },
   "dependencies": {
     "axios": "^0.21.1",
-    "element-plus": "1.0.2-beta.52",
+    "element-plus": "^1.0.2-beta.52",
+    "md-editor-v3": "^2.2.0",
+    "pinia": "^2.0.14",
     "vue": "^3.1.2",
     "vue-cropperjs": "^5.0.0",
     "vue-i18n": "^9.0.0",
     "vue-router": "^4.0.10",
     "vue-schart": "^2.0.0",
-    "vuex": "^4.0.2",
     "wangeditor": "^4.7.4"
   },
   "devDependencies": {

+ 161 - 162
src/components/Header.vue

@@ -1,162 +1,161 @@
-<template>
-    <div class="header">
-        <!-- 折叠按钮 -->
-        <div class="collapse-btn" @click="collapseChage">
-            <i v-if="!collapse" class="el-icon-s-fold"></i>
-            <i v-else class="el-icon-s-unfold"></i>
-        </div>
-        <div class="logo">后台管理系统</div>
-        <div class="header-right">
-            <div class="header-user-con">
-                <!-- 消息中心 -->
-                <div class="btn-bell">
-                    <el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom">
-                        <router-link to="/tabs">
-                            <i class="el-icon-bell"></i>
-                        </router-link>
-                    </el-tooltip>
-                    <span class="btn-bell-badge" v-if="message"></span>
-                </div>
-                <!-- 用户头像 -->
-                <div class="user-avator">
-                    <img src="../assets/img/img.jpg" />
-                </div>
-                <!-- 用户名下拉菜单 -->
-                <el-dropdown class="user-name" trigger="click" @command="handleCommand">
-                    <span class="el-dropdown-link">
-                        {{username}}
-                        <i class="el-icon-caret-bottom"></i>
-                    </span>
-                    <template #dropdown>
-                        <el-dropdown-menu>
-                            <a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
-                                <el-dropdown-item>项目仓库</el-dropdown-item>
-                            </a>
-                            <el-dropdown-item command="user">个人中心</el-dropdown-item>
-                            <el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
-                        </el-dropdown-menu>
-                    </template>
-                </el-dropdown>
-            </div>
-        </div>
-    </div>
-</template>
-<script>
-import { computed, onMounted } from "vue";
-import { useStore } from "vuex";
-import { useRouter } from "vue-router";
-export default {
-    setup() {
-        const username = localStorage.getItem("ms_username");
-        const message = 2;
-
-        const store = useStore();
-        const collapse = computed(() => store.state.collapse);
-        // 侧边栏折叠
-        const collapseChage = () => {
-            store.commit("handleCollapse", !collapse.value);
-        };
-
-        onMounted(() => {
-            if (document.body.clientWidth < 1500) {
-                collapseChage();
-            }
-        });
-
-        // 用户名下拉菜单选择事件
-        const router = useRouter();
-        const handleCommand = (command) => {
-            if (command == "loginout") {
-                localStorage.removeItem("ms_username");
-                router.push("/login");
-            } else if (command == "user") {
-                router.push("/user");
-            }
-        };
-
-        return {
-            username,
-            message,
-            collapse,
-            collapseChage,
-            handleCommand,
-        };
-    },
-};
-</script>
-<style scoped>
-.header {
-    position: relative;
-    box-sizing: border-box;
-    width: 100%;
-    height: 70px;
-    font-size: 22px;
-    color: #fff;
-}
-.collapse-btn {
-    float: left;
-    padding: 0 21px;
-    cursor: pointer;
-    line-height: 70px;
-}
-.header .logo {
-    float: left;
-    width: 250px;
-    line-height: 70px;
-}
-.header-right {
-    float: right;
-    padding-right: 50px;
-}
-.header-user-con {
-    display: flex;
-    height: 70px;
-    align-items: center;
-}
-.btn-fullscreen {
-    transform: rotate(45deg);
-    margin-right: 5px;
-    font-size: 24px;
-}
-.btn-bell,
-.btn-fullscreen {
-    position: relative;
-    width: 30px;
-    height: 30px;
-    text-align: center;
-    border-radius: 15px;
-    cursor: pointer;
-}
-.btn-bell-badge {
-    position: absolute;
-    right: 0;
-    top: -2px;
-    width: 8px;
-    height: 8px;
-    border-radius: 4px;
-    background: #f56c6c;
-    color: #fff;
-}
-.btn-bell .el-icon-bell {
-    color: #fff;
-}
-.user-name {
-    margin-left: 10px;
-}
-.user-avator {
-    margin-left: 20px;
-}
-.user-avator img {
-    display: block;
-    width: 40px;
-    height: 40px;
-    border-radius: 50%;
-}
-.el-dropdown-link {
-    color: #fff;
-    cursor: pointer;
-}
-.el-dropdown-menu__item {
-    text-align: center;
-}
-</style>
+<template>
+    <div class="header">
+        <!-- 折叠按钮 -->
+        <div class="collapse-btn" @click="collapseChage">
+            <i v-if="!sidebar.collapse" class="el-icon-s-fold"></i>
+            <i v-else class="el-icon-s-unfold"></i>
+        </div>
+        <div class="logo">后台管理系统</div>
+        <div class="header-right">
+            <div class="header-user-con">
+                <!-- 消息中心 -->
+                <div class="btn-bell">
+                    <el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom">
+                        <router-link to="/tabs">
+                            <i class="el-icon-bell"></i>
+                        </router-link>
+                    </el-tooltip>
+                    <span class="btn-bell-badge" v-if="message"></span>
+                </div>
+                <!-- 用户头像 -->
+                <div class="user-avator">
+                    <img src="../assets/img/img.jpg" />
+                </div>
+                <!-- 用户名下拉菜单 -->
+                <el-dropdown class="user-name" trigger="click" @command="handleCommand">
+                    <span class="el-dropdown-link">
+                        {{username}}
+                        <i class="el-icon-caret-bottom"></i>
+                    </span>
+                    <template #dropdown>
+                        <el-dropdown-menu>
+                            <a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
+                                <el-dropdown-item>项目仓库</el-dropdown-item>
+                            </a>
+                            <el-dropdown-item command="user">个人中心</el-dropdown-item>
+                            <el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
+                        </el-dropdown-menu>
+                    </template>
+                </el-dropdown>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import { computed, onMounted } from "vue";
+import { useSidebarStore } from '../store/sidebar'
+import { useRouter } from "vue-router";
+export default {
+    setup() {
+        const username = localStorage.getItem("ms_username");
+        const message = 2;
+
+        const sidebar = useSidebarStore();
+        // 侧边栏折叠
+        const collapseChage = () => {
+            sidebar.handleCollapse();
+        };
+
+        onMounted(() => {
+            if (document.body.clientWidth < 1500) {
+                collapseChage();
+            }
+        });
+
+        // 用户名下拉菜单选择事件
+        const router = useRouter();
+        const handleCommand = (command) => {
+            if (command == "loginout") {
+                localStorage.removeItem("ms_username");
+                router.push("/login");
+            } else if (command == "user") {
+                router.push("/user");
+            }
+        };
+
+        return {
+            sidebar,
+            username,
+            message,
+            collapseChage,
+            handleCommand,
+        };
+    },
+};
+</script>
+<style scoped>
+.header {
+    position: relative;
+    box-sizing: border-box;
+    width: 100%;
+    height: 70px;
+    font-size: 22px;
+    color: #fff;
+}
+.collapse-btn {
+    float: left;
+    padding: 0 21px;
+    cursor: pointer;
+    line-height: 70px;
+}
+.header .logo {
+    float: left;
+    width: 250px;
+    line-height: 70px;
+}
+.header-right {
+    float: right;
+    padding-right: 50px;
+}
+.header-user-con {
+    display: flex;
+    height: 70px;
+    align-items: center;
+}
+.btn-fullscreen {
+    transform: rotate(45deg);
+    margin-right: 5px;
+    font-size: 24px;
+}
+.btn-bell,
+.btn-fullscreen {
+    position: relative;
+    width: 30px;
+    height: 30px;
+    text-align: center;
+    border-radius: 15px;
+    cursor: pointer;
+}
+.btn-bell-badge {
+    position: absolute;
+    right: 0;
+    top: -2px;
+    width: 8px;
+    height: 8px;
+    border-radius: 4px;
+    background: #f56c6c;
+    color: #fff;
+}
+.btn-bell .el-icon-bell {
+    color: #fff;
+}
+.user-name {
+    margin-left: 10px;
+}
+.user-avator {
+    margin-left: 20px;
+}
+.user-avator img {
+    display: block;
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+}
+.el-dropdown-link {
+    color: #fff;
+    cursor: pointer;
+}
+.el-dropdown-menu__item {
+    text-align: center;
+}
+</style>

+ 156 - 154
src/components/Sidebar.vue

@@ -1,154 +1,156 @@
-<template>
-    <div class="sidebar">
-        <el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="collapse" background-color="#324157"
-            text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router>
-            <template v-for="item in items">
-                <template v-if="item.subs">
-                    <el-submenu :index="item.index" :key="item.index">
-                        <template #title>
-                            <i :class="item.icon"></i>
-                            <span>{{ item.title }}</span>
-                        </template>
-                        <template v-for="subItem in item.subs">
-                            <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
-                                <template #title>{{ subItem.title }}</template>
-                                <el-menu-item v-for="(threeItem, i) in subItem.subs" :key="i" :index="threeItem.index">
-                                    {{ threeItem.title }}</el-menu-item>
-                            </el-submenu>
-                            <el-menu-item v-else :index="subItem.index" :key="subItem.index">{{ subItem.title }}
-                            </el-menu-item>
-                        </template>
-                    </el-submenu>
-                </template>
-                <template v-else>
-                    <el-menu-item :index="item.index" :key="item.index">
-                        <i :class="item.icon"></i>
-                        <template #title>{{ item.title }}</template>
-                    </el-menu-item>
-                </template>
-            </template>
-        </el-menu>
-    </div>
-</template>
-
-<script>
-import { computed, watch } from "vue";
-import { useStore } from "vuex";
-import { useRoute } from "vue-router";
-export default {
-    setup() {
-        const items = [
-            {
-                icon: "el-icon-lx-home",
-                index: "/dashboard",
-                title: "系统首页",
-            },
-            {
-                icon: "el-icon-lx-cascades",
-                index: "/table",
-                title: "基础表格",
-            },
-            {
-                icon: "el-icon-lx-copy",
-                index: "/tabs",
-                title: "tab选项卡",
-            },
-            {
-                icon: "el-icon-lx-calendar",
-                index: "3",
-                title: "表单相关",
-                subs: [
-                    {
-                        index: "/form",
-                        title: "基本表单",
-                    },
-                    {
-                        index: "/upload",
-                        title: "文件上传",
-                    },
-                    {
-                        index: "4",
-                        title: "三级菜单",
-                        subs: [
-                            {
-                                index: "/editor",
-                                title: "富文本编辑器",
-                            },
-                        ],
-                    },
-                ],
-            },
-            {
-                icon: "el-icon-lx-emoji",
-                index: "/icon",
-                title: "自定义图标",
-            },
-            {
-                icon: "el-icon-pie-chart",
-                index: "/charts",
-                title: "schart图表",
-            },
-            {
-                icon: "el-icon-lx-global",
-                index: "/i18n",
-                title: "国际化功能",
-            },
-            {
-                icon: "el-icon-lx-warn",
-                index: "7",
-                title: "错误处理",
-                subs: [
-                    {
-                        index: "/permission",
-                        title: "权限测试",
-                    },
-                    {
-                        index: "/404",
-                        title: "404页面",
-                    },
-                ],
-            },
-            {
-                icon: "el-icon-lx-redpacket_fill",
-                index: "/donate",
-                title: "支持作者",
-            },
-        ];
-
-        const route = useRoute();
-
-        const onRoutes = computed(() => {
-            return route.path;
-        });
-
-        const store = useStore();
-        const collapse = computed(() => store.state.collapse);
-
-        return {
-            items,
-            onRoutes,
-            collapse,
-        };
-    },
-};
-</script>
-
-<style scoped>
-.sidebar {
-    display: block;
-    position: absolute;
-    left: 0;
-    top: 70px;
-    bottom: 0;
-    overflow-y: scroll;
-}
-.sidebar::-webkit-scrollbar {
-    width: 0;
-}
-.sidebar-el-menu:not(.el-menu--collapse) {
-    width: 250px;
-}
-.sidebar > ul {
-    height: 100%;
-}
-</style>
+<template>
+    <div class="sidebar">
+        <el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="sidebar.collapse" background-color="#324157"
+            text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router>
+            <template v-for="item in items">
+                <template v-if="item.subs">
+                    <el-submenu :index="item.index" :key="item.index">
+                        <template #title>
+                            <i :class="item.icon"></i>
+                            <span>{{ item.title }}</span>
+                        </template>
+                        <template v-for="subItem in item.subs">
+                            <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
+                                <template #title>{{ subItem.title }}</template>
+                                <el-menu-item v-for="(threeItem, i) in subItem.subs" :key="i" :index="threeItem.index">
+                                    {{ threeItem.title }}</el-menu-item>
+                            </el-submenu>
+                            <el-menu-item v-else :index="subItem.index" :key="subItem.index">{{ subItem.title }}
+                            </el-menu-item>
+                        </template>
+                    </el-submenu>
+                </template>
+                <template v-else>
+                    <el-menu-item :index="item.index" :key="item.index">
+                        <i :class="item.icon"></i>
+                        <template #title>{{ item.title }}</template>
+                    </el-menu-item>
+                </template>
+            </template>
+        </el-menu>
+    </div>
+</template>
+
+<script>
+import { computed } from "vue";
+import { useSidebarStore } from '../store/sidebar'
+import { useRoute } from "vue-router";
+export default {
+    setup() {
+        const items = [
+            {
+                icon: "el-icon-lx-home",
+                index: "/dashboard",
+                title: "系统首页",
+            },
+            {
+                icon: "el-icon-lx-cascades",
+                index: "/table",
+                title: "基础表格",
+            },
+            {
+                icon: "el-icon-lx-copy",
+                index: "/tabs",
+                title: "tab选项卡",
+            },
+            {
+                icon: "el-icon-lx-calendar",
+                index: "3",
+                title: "表单相关",
+                subs: [
+                    {
+                        index: "/form",
+                        title: "基本表单",
+                    },
+                    {
+                        index: "/upload",
+                        title: "文件上传",
+                    },
+                    {
+                        index: "4",
+                        title: "三级菜单",
+                        subs: [
+                            {
+                                index: "/editor",
+                                title: "富文本编辑器",
+                            },
+                            {
+                                index: "/markdown",
+                                title: "markdown编辑器",
+                            },
+                        ],
+                    },
+                ],
+            },
+            {
+                icon: "el-icon-lx-emoji",
+                index: "/icon",
+                title: "自定义图标",
+            },
+            {
+                icon: "el-icon-pie-chart",
+                index: "/charts",
+                title: "schart图表",
+            },
+            {
+                icon: "el-icon-lx-global",
+                index: "/i18n",
+                title: "国际化功能",
+            },
+            {
+                icon: "el-icon-lx-warn",
+                index: "7",
+                title: "错误处理",
+                subs: [
+                    {
+                        index: "/permission",
+                        title: "权限测试",
+                    },
+                    {
+                        index: "/404",
+                        title: "404页面",
+                    },
+                ],
+            },
+            {
+                icon: "el-icon-lx-redpacket_fill",
+                index: "/donate",
+                title: "支持作者",
+            },
+        ];
+
+        const route = useRoute();
+        const onRoutes = computed(() => {
+            return route.path;
+        });
+
+        const sidebar = useSidebarStore();
+
+        return {
+            items,
+            onRoutes,
+            sidebar,
+        };
+    },
+};
+</script>
+
+<style scoped>
+.sidebar {
+    display: block;
+    position: absolute;
+    left: 0;
+    top: 70px;
+    bottom: 0;
+    overflow-y: scroll;
+}
+.sidebar::-webkit-scrollbar {
+    width: 0;
+}
+.sidebar-el-menu:not(.el-menu--collapse) {
+    width: 250px;
+}
+.sidebar > ul {
+    height: 100%;
+}
+</style>

+ 174 - 183
src/components/Tags.vue

@@ -1,183 +1,174 @@
-<template>
-    <div class="tags" v-if="showTags">
-        <ul>
-            <li class="tags-li" v-for="(item,index) in tagsList" :class="{'active': isActive(item.path)}" :key="index">
-                <router-link :to="item.path" class="tags-li-title">{{item.title}}</router-link>
-                <span class="tags-li-icon" @click="closeTags(index)">
-                    <i class="el-icon-close"></i>
-                </span>
-            </li>
-        </ul>
-        <div class="tags-close-box">
-            <el-dropdown @command="handleTags">
-                <el-button size="mini" type="primary">
-                    标签选项
-                    <i class="el-icon-arrow-down el-icon--right"></i>
-                </el-button>
-                <template #dropdown>
-                    <el-dropdown-menu size="small">
-                        <el-dropdown-item command="other">关闭其他</el-dropdown-item>
-                        <el-dropdown-item command="all">关闭所有</el-dropdown-item>
-                    </el-dropdown-menu>
-                </template>
-            </el-dropdown>
-        </div>
-    </div>
-</template>
-
-<script>
-import { computed } from "vue";
-import { useStore } from "vuex";
-import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
-export default {
-    setup() {
-        const route = useRoute();
-        const router = useRouter();
-        const isActive = (path) => {
-            return path === route.fullPath;
-        };
-
-        const store = useStore();
-        const tagsList = computed(() => store.state.tagsList);
-        const showTags = computed(() => tagsList.value.length > 0);
-
-        // 关闭单个标签
-        const closeTags = (index) => {
-            const delItem = tagsList.value[index];
-            store.commit("delTagsItem", { index });
-            const item = tagsList.value[index]
-                ? tagsList.value[index]
-                : tagsList.value[index - 1];
-            if (item) {
-                delItem.path === route.fullPath && router.push(item.path);
-            } else {
-                router.push("/");
-            }
-        };
-
-        // 设置标签
-        const setTags = (route) => {
-            const isExist = tagsList.value.some((item) => {
-                return item.path === route.fullPath;
-            });
-            if (!isExist) {
-                if (tagsList.value.length >= 8) {
-                    store.commit("delTagsItem", { index: 0 });
-                }
-                store.commit("setTagsItem", {
-                    name: route.name,
-                    title: route.meta.title,
-                    path: route.fullPath,
-                });
-            }
-        };
-        setTags(route);
-        onBeforeRouteUpdate((to) => {
-            setTags(to);
-        });
-
-        // 关闭全部标签
-        const closeAll = () => {
-            store.commit("clearTags");
-            router.push("/");
-        };
-        // 关闭其他标签
-        const closeOther = () => {
-            const curItem = tagsList.value.filter((item) => {
-                return item.path === route.fullPath;
-            });
-            store.commit("closeTagsOther", curItem);
-        };
-        const handleTags = (command) => {
-            command === "other" ? closeOther() : closeAll();
-        };
-
-        // 关闭当前页面的标签页
-        // store.commit("closeCurrentTag", {
-        //     $router: router,
-        //     $route: route
-        // });
-
-        return {
-            isActive,
-            tagsList,
-            showTags,
-            closeTags,
-            handleTags,
-        };
-    },
-};
-</script>
-
-
-<style>
-.tags {
-    position: relative;
-    height: 30px;
-    overflow: hidden;
-    background: #fff;
-    padding-right: 120px;
-    box-shadow: 0 5px 10px #ddd;
-}
-
-.tags ul {
-    box-sizing: border-box;
-    width: 100%;
-    height: 100%;
-}
-
-.tags-li {
-    float: left;
-    margin: 3px 5px 2px 3px;
-    border-radius: 3px;
-    font-size: 12px;
-    overflow: hidden;
-    cursor: pointer;
-    height: 23px;
-    line-height: 23px;
-    border: 1px solid #e9eaec;
-    background: #fff;
-    padding: 0 5px 0 12px;
-    vertical-align: middle;
-    color: #666;
-    -webkit-transition: all 0.3s ease-in;
-    -moz-transition: all 0.3s ease-in;
-    transition: all 0.3s ease-in;
-}
-
-.tags-li:not(.active):hover {
-    background: #f8f8f8;
-}
-
-.tags-li.active {
-    color: #fff;
-}
-
-.tags-li-title {
-    float: left;
-    max-width: 80px;
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-    margin-right: 5px;
-    color: #666;
-}
-
-.tags-li.active .tags-li-title {
-    color: #fff;
-}
-
-.tags-close-box {
-    position: absolute;
-    right: 0;
-    top: 0;
-    box-sizing: border-box;
-    padding-top: 1px;
-    text-align: center;
-    width: 110px;
-    height: 30px;
-    background: #fff;
-    box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
-    z-index: 10;
-}
-</style>
+<template>
+    <div class="tags" v-if="tags.show">
+        <ul>
+            <li class="tags-li" v-for="(item,index) in tags.list" :class="{'active': isActive(item.path)}" :key="index">
+                <router-link :to="item.path" class="tags-li-title">{{item.title}}</router-link>
+                <span class="tags-li-icon" @click="closeTags(index)">
+                    <i class="el-icon-close"></i>
+                </span>
+            </li>
+        </ul>
+        <div class="tags-close-box">
+            <el-dropdown @command="handleTags">
+                <el-button size="mini" type="primary">
+                    标签选项
+                    <i class="el-icon-arrow-down el-icon--right"></i>
+                </el-button>
+                <template #dropdown>
+                    <el-dropdown-menu size="small">
+                        <el-dropdown-item command="other">关闭其他</el-dropdown-item>
+                        <el-dropdown-item command="all">关闭所有</el-dropdown-item>
+                    </el-dropdown-menu>
+                </template>
+            </el-dropdown>
+        </div>
+    </div>
+</template>
+
+<script>
+import { useTagsStore } from '../store/tags'
+import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
+export default {
+    setup() {
+        const route = useRoute();
+        const router = useRouter();
+        const isActive = (path) => {
+            return path === route.fullPath;
+        };
+
+        const tags = useTagsStore();
+        // 关闭单个标签
+        const closeTags = (index) => {
+            const delItem = tags.list[index];
+            tags.delTagsItem(index);
+            const item = tags.list[index] ? tags.list[index] : tags.list[index - 1];
+            if (item) {
+                delItem.path === route.fullPath && router.push(item.path);
+            } else {
+                router.push("/");
+            }
+        };
+
+        // 设置标签
+        const setTags = (route) => {
+            const isExist = tags.list.some((item) => {
+                return item.path === route.fullPath;
+            });
+            if (!isExist) {
+                if (tags.list.length >= 8) tags.delTagsItem(0);
+                tags.setTagsItem({
+                    name: route.name,
+                    title: route.meta.title,
+                    path: route.fullPath,
+                });
+            }
+        };
+        setTags(route);
+        onBeforeRouteUpdate((to) => {
+            setTags(to);
+        });
+
+        // 关闭全部标签
+        const closeAll = () => {
+            tags.clearTags();
+            router.push("/");
+        };
+        // 关闭其他标签
+        const closeOther = () => {
+            const curItem = tags.list.filter((item) => {
+                return item.path === route.fullPath;
+            });
+            tags.closeTagsOther(curItem);
+        };
+        const handleTags = (command) => {
+            command === "other" ? closeOther() : closeAll();
+        };
+
+        // 关闭当前页面的标签页
+        // tags.closeCurrentTag({
+        //     $router: router,
+        //     $route: route
+        // });
+
+        return {
+            isActive,
+            tags,
+            closeTags,
+            handleTags,
+        };
+    },
+};
+</script>
+
+
+<style>
+.tags {
+    position: relative;
+    height: 30px;
+    overflow: hidden;
+    background: #fff;
+    padding-right: 120px;
+    box-shadow: 0 5px 10px #ddd;
+}
+
+.tags ul {
+    box-sizing: border-box;
+    width: 100%;
+    height: 100%;
+}
+
+.tags-li {
+    float: left;
+    margin: 3px 5px 2px 3px;
+    border-radius: 3px;
+    font-size: 12px;
+    overflow: hidden;
+    cursor: pointer;
+    height: 23px;
+    line-height: 23px;
+    border: 1px solid #e9eaec;
+    background: #fff;
+    padding: 0 5px 0 12px;
+    vertical-align: middle;
+    color: #666;
+    -webkit-transition: all 0.3s ease-in;
+    -moz-transition: all 0.3s ease-in;
+    transition: all 0.3s ease-in;
+}
+
+.tags-li:not(.active):hover {
+    background: #f8f8f8;
+}
+
+.tags-li.active {
+    color: #fff;
+}
+
+.tags-li-title {
+    float: left;
+    max-width: 80px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    margin-right: 5px;
+    color: #666;
+}
+
+.tags-li.active .tags-li-title {
+    color: #fff;
+}
+
+.tags-close-box {
+    position: absolute;
+    right: 0;
+    top: 0;
+    box-sizing: border-box;
+    padding-top: 1px;
+    text-align: center;
+    width: 110px;
+    height: 30px;
+    background: #fff;
+    box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
+    z-index: 10;
+}
+</style>

+ 2 - 3
src/main.js

@@ -1,12 +1,11 @@
 import {createApp} from 'vue'
+import { createPinia } from 'pinia'
 import App from './App.vue'
 import router from './router'
-import store from './store'
 import installElementPlus from './plugins/element'
 import './assets/css/icon.css'
 const app = createApp(App)
 installElementPlus(app)
-app
-    .use(store)
+app.use(createPinia())
     .use(router)
     .mount('#app')

+ 150 - 143
src/router/index.js

@@ -1,144 +1,151 @@
-import {createRouter, createWebHashHistory} from "vue-router";
-import Home from "../views/Home.vue";
-
-const routes = [
-    {
-        path: '/',
-        redirect: '/dashboard'
-    }, {
-        path: "/",
-        name: "Home",
-        component: Home,
-        children: [
-            {
-                path: "/dashboard",
-                name: "dashboard",
-                meta: {
-                    title: '系统首页'
-                },
-                component: () => import ( /* webpackChunkName: "dashboard" */ "../views/Dashboard.vue")
-            }, {
-                path: "/table",
-                name: "basetable",
-                meta: {
-                    title: '表格'
-                },
-                component: () => import ( /* webpackChunkName: "table" */ "../views/BaseTable.vue")
-            }, {
-                path: "/charts",
-                name: "basecharts",
-                meta: {
-                    title: '图表'
-                },
-                component: () => import ( /* webpackChunkName: "charts" */ "../views/BaseCharts.vue")
-            }, {
-                path: "/form",
-                name: "baseform",
-                meta: {
-                    title: '表单'
-                },
-                component: () => import ( /* webpackChunkName: "form" */ "../views/BaseForm.vue")
-            }, {
-                path: "/tabs",
-                name: "tabs",
-                meta: {
-                    title: 'tab标签'
-                },
-                component: () => import ( /* webpackChunkName: "tabs" */ "../views/Tabs.vue")
-            }, {
-                path: "/donate",
-                name: "donate",
-                meta: {
-                    title: '鼓励作者'
-                },
-                component: () => import ( /* webpackChunkName: "donate" */ "../views/Donate.vue")
-            }, {
-                path: "/permission",
-                name: "permission",
-                meta: {
-                    title: '权限管理',
-                    permission: true
-                },
-                component: () => import ( /* webpackChunkName: "permission" */ "../views/Permission.vue")
-            }, {
-                path: "/i18n",
-                name: "i18n",
-                meta: {
-                    title: '国际化语言'
-                },
-                component: () => import ( /* webpackChunkName: "i18n" */ "../views/I18n.vue")
-            }, {
-                path: "/upload",
-                name: "upload",
-                meta: {
-                    title: '上传插件'
-                },
-                component: () => import ( /* webpackChunkName: "upload" */ "../views/Upload.vue")
-            }, {
-                path: "/icon",
-                name: "icon",
-                meta: {
-                    title: '自定义图标'
-                },
-                component: () => import ( /* webpackChunkName: "icon" */ "../views/Icon.vue")
-            }, {
-                path: '/404',
-                name: '404',
-                meta: {
-                    title: '找不到页面'
-                },
-                component: () => import (/* webpackChunkName: "404" */ '../views/404.vue')
-            }, {
-                path: '/403',
-                name: '403',
-                meta: {
-                    title: '没有权限'
-                },
-                component: () => import (/* webpackChunkName: "403" */ '../views/403.vue')
-            }, {
-                path: '/user',
-                name: 'user',
-                meta: {
-                    title: '个人中心'
-                },
-                component: () => import (/* webpackChunkName: "user" */ '../views/User.vue')
-            }, {
-                path: '/editor',
-                name: 'editor',
-                meta: {
-                    title: '富文本编辑器'
-                },
-                component: () => import (/* webpackChunkName: "editor" */ '../views/Editor.vue')
-            }
-        ]
-    }, {
-        path: "/login",
-        name: "Login",
-        meta: {
-            title: '登录'
-        },
-        component: () => import ( /* webpackChunkName: "login" */ "../views/Login.vue")
-    }
-];
-
-const router = createRouter({
-    history: createWebHashHistory(),
-    routes
-});
-
-router.beforeEach((to, from, next) => {
-    document.title = `${to.meta.title} | vue-manage-system`;
-    const role = localStorage.getItem('ms_username');
-    if (!role && to.path !== '/login') {
-        next('/login');
-    } else if (to.meta.permission) {
-        // 如果是管理员权限则可进入,这里只是简单的模拟管理员权限而已
-        role === 'admin'
-            ? next()
-            : next('/403');
-    } else {
-        next();
-    }
-});
-
+import {createRouter, createWebHashHistory} from "vue-router";
+import Home from "../views/Home.vue";
+
+const routes = [
+    {
+        path: '/',
+        redirect: '/dashboard'
+    }, {
+        path: "/",
+        name: "Home",
+        component: Home,
+        children: [
+            {
+                path: "/dashboard",
+                name: "dashboard",
+                meta: {
+                    title: '系统首页'
+                },
+                component: () => import ( /* webpackChunkName: "dashboard" */ "../views/Dashboard.vue")
+            }, {
+                path: "/table",
+                name: "basetable",
+                meta: {
+                    title: '表格'
+                },
+                component: () => import ( /* webpackChunkName: "table" */ "../views/BaseTable.vue")
+            }, {
+                path: "/charts",
+                name: "basecharts",
+                meta: {
+                    title: '图表'
+                },
+                component: () => import ( /* webpackChunkName: "charts" */ "../views/BaseCharts.vue")
+            }, {
+                path: "/form",
+                name: "baseform",
+                meta: {
+                    title: '表单'
+                },
+                component: () => import ( /* webpackChunkName: "form" */ "../views/BaseForm.vue")
+            }, {
+                path: "/tabs",
+                name: "tabs",
+                meta: {
+                    title: 'tab标签'
+                },
+                component: () => import ( /* webpackChunkName: "tabs" */ "../views/Tabs.vue")
+            }, {
+                path: "/donate",
+                name: "donate",
+                meta: {
+                    title: '鼓励作者'
+                },
+                component: () => import ( /* webpackChunkName: "donate" */ "../views/Donate.vue")
+            }, {
+                path: "/permission",
+                name: "permission",
+                meta: {
+                    title: '权限管理',
+                    permission: true
+                },
+                component: () => import ( /* webpackChunkName: "permission" */ "../views/Permission.vue")
+            }, {
+                path: "/i18n",
+                name: "i18n",
+                meta: {
+                    title: '国际化语言'
+                },
+                component: () => import ( /* webpackChunkName: "i18n" */ "../views/I18n.vue")
+            }, {
+                path: "/upload",
+                name: "upload",
+                meta: {
+                    title: '上传插件'
+                },
+                component: () => import ( /* webpackChunkName: "upload" */ "../views/Upload.vue")
+            }, {
+                path: "/icon",
+                name: "icon",
+                meta: {
+                    title: '自定义图标'
+                },
+                component: () => import ( /* webpackChunkName: "icon" */ "../views/Icon.vue")
+            }, {
+                path: '/404',
+                name: '404',
+                meta: {
+                    title: '找不到页面'
+                },
+                component: () => import (/* webpackChunkName: "404" */ '../views/404.vue')
+            }, {
+                path: '/403',
+                name: '403',
+                meta: {
+                    title: '没有权限'
+                },
+                component: () => import (/* webpackChunkName: "403" */ '../views/403.vue')
+            }, {
+                path: '/user',
+                name: 'user',
+                meta: {
+                    title: '个人中心'
+                },
+                component: () => import (/* webpackChunkName: "user" */ '../views/User.vue')
+            }, {
+                path: '/editor',
+                name: 'editor',
+                meta: {
+                    title: '富文本编辑器'
+                },
+                component: () => import (/* webpackChunkName: "editor" */ '../views/Editor.vue')
+            }, {
+                path: '/markdown',
+                name: 'markdown',
+                meta: {
+                    title: 'markdown编辑器'
+                },
+                component: () => import (/* webpackChunkName: "markdown" */ '../views/Markdown.vue')
+            }
+        ]
+    }, {
+        path: "/login",
+        name: "Login",
+        meta: {
+            title: '登录'
+        },
+        component: () => import ( /* webpackChunkName: "login" */ "../views/Login.vue")
+    }
+];
+
+const router = createRouter({
+    history: createWebHashHistory(),
+    routes
+});
+
+router.beforeEach((to, from, next) => {
+    document.title = `${to.meta.title} | vue-manage-system`;
+    const role = localStorage.getItem('ms_username');
+    if (!role && to.path !== '/login') {
+        next('/login');
+    } else if (to.meta.permission) {
+        // 如果是管理员权限则可进入,这里只是简单的模拟管理员权限而已
+        role === 'admin'
+            ? next()
+            : next('/403');
+    } else {
+        next();
+    }
+});
+
 export default router;

+ 0 - 56
src/store/index.js

@@ -1,56 +0,0 @@
-import {createStore} from 'vuex'
-
-export default createStore({
-    state: {
-        tagsList: [],
-        collapse: false
-    },
-    mutations: {
-        delTagsItem(state, data) {
-            state
-                .tagsList
-                .splice(data.index, 1);
-        },
-        setTagsItem(state, data) {
-            state
-                .tagsList
-                .push(data)
-        },
-        clearTags(state) {
-            state.tagsList = []
-        },
-        closeTagsOther(state, data) {
-            state.tagsList = data;
-        },
-        closeCurrentTag(state, data) {
-            for (let i = 0, len = state.tagsList.length; i < len; i++) {
-                const item = state.tagsList[i];
-                if (item.path === data.$route.fullPath) {
-                    if (i < len - 1) {
-                        data
-                            .$router
-                            .push(state.tagsList[i + 1].path);
-                    } else if (i > 0) {
-                        data
-                            .$router
-                            .push(state.tagsList[i - 1].path);
-                    } else {
-                        data
-                            .$router
-                            .push("/");
-                    }
-                    state
-                        .tagsList
-                        .splice(i, 1);
-                    break;
-                }
-            }
-        },
-        // 侧边栏折叠
-        handleCollapse(state, data) {
-            state.collapse = data;
-        }
-    },
-    actions: {},
-    modules: {}
-})

+ 17 - 0
src/store/sidebar.js

@@ -0,0 +1,17 @@
+import { defineStore } from 'pinia'
+
+export const useSidebarStore = defineStore('sidebar', {
+    state: () => {
+        return {
+            collapse: false
+        }
+    },
+    getters: {
+        
+    },
+    actions: {
+        handleCollapse() {
+            this.collapse = !this.collapse;
+        }
+    }
+})

+ 48 - 0
src/store/tags.js

@@ -0,0 +1,48 @@
+import { defineStore } from 'pinia'
+
+export const useTagsStore = defineStore('tags', {
+    state: () => {
+        return {
+            list: []
+        }
+    },
+    getters: {
+        show: (state) => {
+            return state.list.length > 0;
+        },
+        nameList: (state) => {
+            return state.list.map(item => item.name);
+        }
+    },
+    actions: {
+        delTagsItem(index) {
+            this.list.splice(index, 1);
+        },
+        setTagsItem(data) {
+            this.list.push(data)
+        },
+        clearTags() {
+            this.list = []
+        },
+        closeTagsOther(data) {
+            this.list = data;
+        },
+        closeCurrentTag(data) {
+            console.log(data)
+            for (let i = 0, len = this.list.length; i < len; i++) {
+                const item = this.list[i];
+                if (item.path === data.$route.fullPath) {
+                    if (i < len - 1) {
+                        data.$router.push(this.list[i + 1].path);
+                    } else if (i > 0) {
+                        data.$router.push(this.list[i - 1].path);
+                    } else {
+                        data.$router.push("/");
+                    }
+                    this.list.splice(i, 1);
+                    break;
+                }
+            }
+        },
+    }
+})

+ 41 - 44
src/views/Home.vue

@@ -1,44 +1,41 @@
-<template>
-    <div class="about">
-        <v-header />
-        <v-sidebar />
-        <div class="content-box" :class="{ 'content-collapse': collapse }">
-            <v-tags></v-tags>
-            <div class="content">
-                <router-view v-slot="{ Component }">
-                    <transition name="move" mode="out-in">
-                        <keep-alive :include="tagsList">
-                            <component :is="Component" />
-                        </keep-alive>
-                    </transition>
-                </router-view>
-                <!-- <el-backtop target=".content"></el-backtop> -->
-            </div>
-        </div>
-    </div>
-</template>
-<script>
-import { computed } from "vue";
-import { useStore } from "vuex";
-import vHeader from "../components/Header.vue";
-import vSidebar from "../components/Sidebar.vue";
-import vTags from "../components/Tags.vue";
-export default {
-    components: {
-        vHeader,
-        vSidebar,
-        vTags,
-    },
-    setup() {
-        const store = useStore();
-        const tagsList = computed(() =>
-            store.state.tagsList.map((item) => item.name)
-        );
-        const collapse = computed(() => store.state.collapse);
-        return {
-            tagsList,
-            collapse,
-        };
-    },
-};
-</script>
+<template>
+    <div class="about">
+        <v-header />
+        <v-sidebar />
+        <div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
+            <v-tags></v-tags>
+            <div class="content">
+                <router-view v-slot="{ Component }">
+                    <transition name="move" mode="out-in">
+                        <keep-alive :include="tags.nameList">
+                            <component :is="Component" />
+                        </keep-alive>
+                    </transition>
+                </router-view>
+                <!-- <el-backtop target=".content"></el-backtop> -->
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import { useSidebarStore } from '../store/sidebar'
+import { useTagsStore } from '../store/tags'
+import vHeader from "../components/Header.vue";
+import vSidebar from "../components/Sidebar.vue";
+import vTags from "../components/Tags.vue";
+export default {
+    components: {
+        vHeader,
+        vSidebar,
+        vTags,
+    },
+    setup() {
+        const sidebar = useSidebarStore();
+        const tags = useTagsStore();
+        return {
+            tags,
+            sidebar,
+        };
+    },
+};
+</script>

+ 124 - 124
src/views/Login.vue

@@ -1,125 +1,125 @@
-<template>
-    <div class="login-wrap">
-        <div class="ms-login">
-            <div class="ms-title">后台管理系统</div>
-            <el-form :model="param" :rules="rules" ref="login" label-width="0px" class="ms-content">
-                <el-form-item prop="username">
-                    <el-input v-model="param.username" placeholder="username">
-                        <template #prepend>
-                            <el-button icon="el-icon-user"></el-button>
-                        </template>
-                    </el-input>
-                </el-form-item>
-                <el-form-item prop="password">
-                    <el-input type="password" placeholder="password" v-model="param.password"
-                        @keyup.enter="submitForm()">
-                        <template #prepend>
-                            <el-button icon="el-icon-lock"></el-button>
-                        </template>
-                    </el-input>
-                </el-form-item>
-                <div class="login-btn">
-                    <el-button type="primary" @click="submitForm()">登录</el-button>
-                </div>
-                <p class="login-tips">Tips : 用户名和密码随便填。</p>
-            </el-form>
-        </div>
-    </div>
-</template>
-
-<script>
-import { ref, reactive } from "vue";
-import { useStore } from "vuex";
-import { useRouter } from "vue-router";
-import { ElMessage } from "element-plus";
-
-export default {
-    setup() {
-        const router = useRouter();
-        const param = reactive({
-            username: "admin",
-            password: "123123",
-        });
-
-        const rules = {
-            username: [
-                {
-                    required: true,
-                    message: "请输入用户名",
-                    trigger: "blur",
-                },
-            ],
-            password: [
-                { required: true, message: "请输入密码", trigger: "blur" },
-            ],
-        };
-        const login = ref(null);
-        const submitForm = () => {
-            login.value.validate((valid) => {
-                if (valid) {
-                    ElMessage.success("登录成功");
-                    localStorage.setItem("ms_username", param.username);
-                    router.push("/");
-                } else {
-                    ElMessage.error("登录成功");
-                    return false;
-                }
-            });
-        };
-
-        const store = useStore();
-        store.commit("clearTags");
-
-        return {
-            param,
-            rules,
-            login,
-            submitForm,
-        };
-    },
-};
-</script>
-
-<style scoped>
-.login-wrap {
-    position: relative;
-    width: 100%;
-    height: 100%;
-    background-image: url(../assets/img/login-bg.jpg);
-    background-size: 100%;
-}
-.ms-title {
-    width: 100%;
-    line-height: 50px;
-    text-align: center;
-    font-size: 20px;
-    color: #fff;
-    border-bottom: 1px solid #ddd;
-}
-.ms-login {
-    position: absolute;
-    left: 50%;
-    top: 50%;
-    width: 350px;
-    margin: -190px 0 0 -175px;
-    border-radius: 5px;
-    background: rgba(255, 255, 255, 0.3);
-    overflow: hidden;
-}
-.ms-content {
-    padding: 30px 30px;
-}
-.login-btn {
-    text-align: center;
-}
-.login-btn button {
-    width: 100%;
-    height: 36px;
-    margin-bottom: 10px;
-}
-.login-tips {
-    font-size: 12px;
-    line-height: 30px;
-    color: #fff;
-}
+<template>
+    <div class="login-wrap">
+        <div class="ms-login">
+            <div class="ms-title">后台管理系统</div>
+            <el-form :model="param" :rules="rules" ref="login" label-width="0px" class="ms-content">
+                <el-form-item prop="username">
+                    <el-input v-model="param.username" placeholder="username">
+                        <template #prepend>
+                            <el-button icon="el-icon-user"></el-button>
+                        </template>
+                    </el-input>
+                </el-form-item>
+                <el-form-item prop="password">
+                    <el-input type="password" placeholder="password" v-model="param.password"
+                        @keyup.enter="submitForm()">
+                        <template #prepend>
+                            <el-button icon="el-icon-lock"></el-button>
+                        </template>
+                    </el-input>
+                </el-form-item>
+                <div class="login-btn">
+                    <el-button type="primary" @click="submitForm()">登录</el-button>
+                </div>
+                <p class="login-tips">Tips : 用户名和密码随便填。</p>
+            </el-form>
+        </div>
+    </div>
+</template>
+
+<script>
+import { ref, reactive } from "vue";
+import { useTagsStore } from '../store/tags'
+import { useRouter } from "vue-router";
+import { ElMessage } from "element-plus";
+
+export default {
+    setup() {
+        const router = useRouter();
+        const param = reactive({
+            username: "admin",
+            password: "123123",
+        });
+
+        const rules = {
+            username: [
+                {
+                    required: true,
+                    message: "请输入用户名",
+                    trigger: "blur",
+                },
+            ],
+            password: [
+                { required: true, message: "请输入密码", trigger: "blur" },
+            ],
+        };
+        const login = ref(null);
+        const submitForm = () => {
+            login.value.validate((valid) => {
+                if (valid) {
+                    ElMessage.success("登录成功");
+                    localStorage.setItem("ms_username", param.username);
+                    router.push("/");
+                } else {
+                    ElMessage.error("登录成功");
+                    return false;
+                }
+            });
+        };
+
+        const tags = useTagsStore();
+        tags.clearTags();
+
+        return {
+            param,
+            rules,
+            login,
+            submitForm,
+        };
+    },
+};
+</script>
+
+<style scoped>
+.login-wrap {
+    position: relative;
+    width: 100%;
+    height: 100%;
+    background-image: url(../assets/img/login-bg.jpg);
+    background-size: 100%;
+}
+.ms-title {
+    width: 100%;
+    line-height: 50px;
+    text-align: center;
+    font-size: 20px;
+    color: #fff;
+    border-bottom: 1px solid #ddd;
+}
+.ms-login {
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    width: 350px;
+    margin: -190px 0 0 -175px;
+    border-radius: 5px;
+    background: rgba(255, 255, 255, 0.3);
+    overflow: hidden;
+}
+.ms-content {
+    padding: 30px 30px;
+}
+.login-btn {
+    text-align: center;
+}
+.login-btn button {
+    width: 100%;
+    height: 36px;
+    margin-bottom: 10px;
+}
+.login-tips {
+    font-size: 12px;
+    line-height: 30px;
+    color: #fff;
+}
 </style>

+ 32 - 0
src/views/Markdown.vue

@@ -0,0 +1,32 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item>
+                    <i class="el-icon-lx-calendar"></i> 表单
+                </el-breadcrumb-item>
+                <el-breadcrumb-item>markdown编辑器</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="plugins-tips">
+                md-editor-v3:vue3版本的 markdown 编辑器,配置丰富,请详看文档。
+                访问地址:
+                <a href="https://imzbf.github.io/md-editor-v3/index" target="_blank">md-editor-v3</a>
+            </div>
+            <md-editor class="mgb20" v-model="text" @on-upload-img="onUploadImg"/>
+            <el-button type="primary">提交</el-button>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref} from "vue";
+import MdEditor from 'md-editor-v3';
+import 'md-editor-v3/lib/style.css';
+
+const text = ref('Hello Editor!');
+const onUploadImg = (files)=>{
+    console.log(files)
+}
+</script>