diff --git a/web b/web
deleted file mode 160000
index d0a99dc..0000000
--- a/web
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d0a99dcf123cefe5aed5b263ccde7acd4949da2a
diff --git a/web/.editorconfig b/web/.editorconfig
new file mode 100644
index 0000000..68cc827
--- /dev/null
+++ b/web/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = unset
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/web/.env.development b/web/.env.development
new file mode 100644
index 0000000..318c4cb
--- /dev/null
+++ b/web/.env.development
@@ -0,0 +1,12 @@
+# 是否使用Hash路由
+VITE_USE_HASH = 'true'
+
+# 资源公共路径,需要以 /开头和结尾
+VITE_PUBLIC_PATH = '/'
+
+# Axios 基础路径
+# VITE_AXIOS_BASE_URL = '/api' # 用于代理
+VITE_AXIOS_BASE_URL = 'https://mock.apipark.cn/m1/3776410-0-default' # apifox云端mock
+
+# 代理配置-target
+VITE_PROXY_TARGET = 'http://localhost:8085'
diff --git a/web/.env.production b/web/.env.production
new file mode 100644
index 0000000..1b309e4
--- /dev/null
+++ b/web/.env.production
@@ -0,0 +1,10 @@
+# 是否使用Hash路由
+VITE_USE_HASH = 'false'
+
+# 资源公共路径,需要以 /开头和结尾
+VITE_PUBLIC_PATH = '/'
+
+VITE_AXIOS_BASE_URL = '/api' # 用于代理
+
+# 代理配置-target
+VITE_PROXY_TARGET = 'http://localhost:8085'
diff --git a/web/.eslint-global-variables.json b/web/.eslint-global-variables.json
new file mode 100644
index 0000000..4f9c397
--- /dev/null
+++ b/web/.eslint-global-variables.json
@@ -0,0 +1,63 @@
+{
+ "globals": {
+ "$loadingBar": true,
+ "$message": true,
+ "$dialog": true,
+ "$notification": true,
+ "$modal": true,
+ "defineOptions": true,
+ "EffectScope": true,
+ "computed": true,
+ "createApp": true,
+ "customRef": true,
+ "defineAsyncComponent": true,
+ "defineComponent": true,
+ "effectScope": true,
+ "getCurrentInstance": true,
+ "getCurrentScope": true,
+ "h": true,
+ "inject": true,
+ "isProxy": true,
+ "isReactive": true,
+ "isReadonly": true,
+ "isRef": true,
+ "markRaw": true,
+ "nextTick": true,
+ "onActivated": true,
+ "onBeforeMount": true,
+ "onBeforeUnmount": true,
+ "onBeforeUpdate": true,
+ "onDeactivated": true,
+ "onErrorCaptured": true,
+ "onMounted": true,
+ "onRenderTracked": true,
+ "onRenderTriggered": true,
+ "onScopeDispose": true,
+ "onServerPrefetch": true,
+ "onUnmounted": true,
+ "onUpdated": true,
+ "provide": true,
+ "reactive": true,
+ "readonly": true,
+ "ref": true,
+ "resolveComponent": true,
+ "shallowReactive": true,
+ "shallowReadonly": true,
+ "shallowRef": true,
+ "toRaw": true,
+ "toRef": true,
+ "toRefs": true,
+ "triggerRef": true,
+ "unref": true,
+ "useAttrs": true,
+ "useCssModule": true,
+ "useCssVars": true,
+ "useRoute": true,
+ "useRouter": true,
+ "useSlots": true,
+ "watch": true,
+ "watchEffect": true,
+ "watchPostEffect": true,
+ "watchSyncEffect": true
+ }
+}
diff --git a/web/.npmrc b/web/.npmrc
new file mode 100644
index 0000000..5cee373
--- /dev/null
+++ b/web/.npmrc
@@ -0,0 +1,3 @@
+registry=https://registry.npmmirror.com
+shamefully-hoist=true
+strict-peer-dependencies=false
diff --git a/web/LICENSE b/web/LICENSE
new file mode 100644
index 0000000..888e80f
--- /dev/null
+++ b/web/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Ronnie Zhang(大脸怪)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/web/README.md b/web/README.md
new file mode 100644
index 0000000..53c2ff5
--- /dev/null
+++ b/web/README.md
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+## 简介
+
+Vue Naive Admin 是一款极简风格的后台管理模板,包含前后端解决方案,前端使用 Vite + Vue3 + Pinia + Unocss,后端使用 Nestjs + TypeOrm + MySql,简单易用,赏心悦目,历经十几次重构和细节打磨,诚意满满!!
+
+## 设计理念
+
+Vue Naive Admin 2022年2月开始开源,从 1.0 到现在的 2.0,一直秉持着`简单即正义`的理念,旨在帮助中小企业、在校大学生及个人开发者快速上手开发后台管理项目,为了降低使用者的学习成本,没有使用看似主流的 TypeScript(前端),这也使得 Vue Naive Admin 成为了市面上少有的 `使用 JavaScript 的 Vue3 后台管理模板`,而且还算优秀,得到了大量朋友的认可和喜爱。
+
+## 特性
+
+- 🆒 使用 **Vue3** 主流最新技术栈: `Vite + Vue3 + Pinia`
+- 🍇 使用 **原子CSS**框架: `Unocss`,优雅、轻量、易用
+- 🍍 集成 `Pinia` 状态管理,支持状态持久化
+- 🤹 使用主流的 `iconify + unocss` 图标方案,支持自定义图标,支持动态渲染
+- 🎨 使用 Naive UI,`极致简洁的代码风格和清爽的页面设计`,审美在线,主题轻松定制
+- 👏 先进且易于理解的文件结构设计,多个模块之间**零耦合**,单个业务模块删除不影响其他模块
+- 🚀 `扁平化路由`设计,每一个组件都可以是一个页面,告别多级路由 `KeepAlive` 难实现问题
+- 🍒 `基于权限动态生成路由`,无需额外定义路由,`403和404页面可区分`,而不是无权限也跳404
+- 🔐 基于Redis集成 `无感刷新`,用户登录态可控,安全与体验缺一不可
+- ✨ 基于 Naive UI 封装 `message` 全局工具方法,支持批量提醒,支持跨页面单例模式
+- ⚡️ 基于 Naive UI 封装常用的业务组件,包含`Page` 组件、`CRUD` 表格组件及 `Modal`组件等,简单易用,减少大量重复性工作
+
+## 极致的性能
+
+![](https://docs.isme.top/Public/Uploads/2023-11-18/6558568b2b476.png)
+![](https://docs.isme.top/Public/Uploads/2023-11-18/655853caa9ce8.png)
+
+## 2.0 和 1.0 区别
+
+- 2.0 是基于 1.0 风格从 0 到 1 重新设计的,所以 2.0 看似跟 1.0 很像,但其实代码机构差别还挺大的。
+- 1.0 只提供前端,后端使用 Mock 模拟的,而 2.0 是全栈版,提供真实的后端接口。
+- 2.0 虽然版本高于 1.0,但复杂度却远低于 1.0,虽然 1.0 也很简单。
+- 2.0 的灵活度远高于 1.0,只要你愿意,你可以为每个页面单独定制一个 layout
+
+[体验1.0 | template.isme.top](https://template.isme.top)
+
+[体验2.0 | admin.isme.top](https://admin.isme.top)
+
+## Nestjs 后端
+
+Vue Naive Admin 提供一套后端代码,技术栈使用 Nestjs + TypeOrm + MySql,内置 JWT、RABC及模板所需的一些基础接口。
+
+- 源码-github: [isme-nest-serve | github](https://github.com/zclzone/isme-nest-serve)
+- 源码-gitee: [isme-nest-serve | gitee](https://gitee.com/isme-admin/isme-nest-serve)
+
+## 文档
+
+- 项目文档: [docs | vue-naive-admin](https://docs.isme.top/web/#/624306705/188522224)
+- 接口文档: [apidoc | isme-nest-serve](https://apifox.com/apidoc/shared-ff4a4d32-c0d1-4caf-b0ee-6abc130f734a)
+
+> 注:有个比较常见的问题,就是如何添加菜单和修改菜单,由于项目是由后端控制菜单资源的,所以需要对接后端后在资源管理功能对菜单进行增删改,然后在角色管理功能给对应角色进行授权。具体如何对接后端,请参考 [项目文档](https://docs.isme.top/web/#/624306705/188522224)。当然,可能有些菜单你不想通过权限控制,那么你可以在 `/src/settings.js` 文件添加 basePermissions,只需对齐菜单资源的结构即可,结构可以参照 [接口文档](https://apifox.com/apidoc/shared-ff4a4d32-c0d1-4caf-b0ee-6abc130f734a/api-134536978)。
+
+## 使用这个模板开始你的项目
+
+[使用这个模板创建Github仓库](https://github.com/zclzone/vue-naive-admin/generate).
+
+或者使用 `degit` 克隆此仓库,这样将没有任何历史提交记录:
+
+```cmd
+npx degit zclzone/vue-naive-admin
+```
+
+## 版权说明
+
+本项目使用 `MIT协议`,默认授权给任何人,被授权人可免费地无限制的使用、复制、修改、合并、发布、发行、再许可、售卖本软件拷贝、并有权向被供应人授予同等的权利,但必须满足以下条件:
+
+- 复制、修改和发行本项目代码需包含原作者的版权及许可信息,包括但不限于文件头注释、协议等
+
+简单来说,作者只想保留版权,没有任何其他限制。
+
+## 其他已对接本项目的后端项目
+
+- [isme-java-serve](https://github.com/DHBin/isme-java-serve): 一个轻量级的Java后端服务,基于SpringBoot、MybatisPlus、SaToken、MapStruct等实现,已对接 Vue Naive Admin 2.0。
+- [naive-admin-go](https://github.com/ituserxxx/naive-admin-go): 一个 Go 后端服务,基于 gin、gorm、mysql、jwt和session,已对接 Vue Naive Admin 2.0。
+
+## 入群交流
+
+添加微信,拉你进群
+
+![](https://static.isme.top/image/coder_wx.jpg)
diff --git a/web/build/index.js b/web/build/index.js
new file mode 100644
index 0000000..985ced8
--- /dev/null
+++ b/web/build/index.js
@@ -0,0 +1,39 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:48:02
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import path from 'node:path'
+import { globSync } from 'glob'
+import dynamicIcons from '../src/assets/icons/dynamic-icons'
+
+/**
+ * @usage 生成icons, 用于 unocss safelist,以支持页面动态渲染自定义图标
+ */
+export function getIcons() {
+ const feFiles = globSync('src/assets/icons/feather/*.svg', { nodir: true, strict: true })
+ const meFiles = globSync('src/assets/icons/isme/*.svg', { nodir: true, strict: true })
+ const feIcons = feFiles.map((filePath) => {
+ const fileName = path.basename(filePath) // 获取文件名,包括后缀
+ const fileNameWithoutExt = path.parse(fileName).name // 获取去除后缀的文件名
+ return `i-fe:${fileNameWithoutExt}`
+ })
+ const meIcons = meFiles.map((filePath) => {
+ const fileName = path.basename(filePath) // 获取文件名,包括后缀
+ const fileNameWithoutExt = path.parse(fileName).name // 获取去除后缀的文件名
+ return `i-me:${fileNameWithoutExt}`
+ })
+
+ return [...dynamicIcons, ...feIcons, ...meIcons]
+}
+
+/**
+ * @usage 生成.vue文件路径列表,用于添加菜单时可下拉选择对应的.vue文件路径,防止手动输入报错
+ */
+export function getPagePathes() {
+ const files = globSync('src/views/**/*.vue')
+ return files.map(item => `/${path.normalize(item).replace(/\\/g, '/')}`)
+}
diff --git a/web/build/plugin-isme/icons.js b/web/build/plugin-isme/icons.js
new file mode 100644
index 0000000..4da9779
--- /dev/null
+++ b/web/build/plugin-isme/icons.js
@@ -0,0 +1,25 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:48:11
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { getIcons } from '..'
+
+const PLUGIN_ICONS_ID = 'isme:icons'
+export function pluginIcons() {
+ return {
+ name: 'isme:icons',
+ resolveId(id) {
+ if (id === PLUGIN_ICONS_ID)
+ return `\0${PLUGIN_ICONS_ID}`
+ },
+ load(id) {
+ if (id === `\0${PLUGIN_ICONS_ID}`) {
+ return `export default ${JSON.stringify(getIcons())}`
+ }
+ },
+ }
+}
diff --git a/web/build/plugin-isme/index.js b/web/build/plugin-isme/index.js
new file mode 100644
index 0000000..326b960
--- /dev/null
+++ b/web/build/plugin-isme/index.js
@@ -0,0 +1,10 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:48:17
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+export { pluginPagePathes } from './page-pathes'
+export { pluginIcons } from './icons'
diff --git a/web/build/plugin-isme/page-pathes.js b/web/build/plugin-isme/page-pathes.js
new file mode 100644
index 0000000..a29d8e2
--- /dev/null
+++ b/web/build/plugin-isme/page-pathes.js
@@ -0,0 +1,25 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:37:43
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { getPagePathes } from '..'
+
+const PLUGIN_PAGE_PATHES_ID = 'isme:page-pathes'
+export function pluginPagePathes() {
+ return {
+ name: 'isme:page-pathes',
+ resolveId(id) {
+ if (id === PLUGIN_PAGE_PATHES_ID)
+ return `\0${PLUGIN_PAGE_PATHES_ID}`
+ },
+ load(id) {
+ if (id === `\0${PLUGIN_PAGE_PATHES_ID}`) {
+ return `export default ${JSON.stringify(getPagePathes())}`
+ }
+ },
+ }
+}
diff --git a/web/eslint.config.js b/web/eslint.config.js
new file mode 100644
index 0000000..b15d4e5
--- /dev/null
+++ b/web/eslint.config.js
@@ -0,0 +1,34 @@
+import antfu from '@antfu/eslint-config'
+
+export default antfu({
+ unocss: true,
+ formatters: true,
+ stylistic: true,
+ rules: {
+ 'n/prefer-global/process': 'off',
+ 'no-undef': 'error',
+ 'no-fallthrough': 'off',
+ 'vue/block-order': 'off',
+ '@typescript-eslint/no-this-alias': 'off',
+ 'prefer-promise-reject-errors': 'off',
+ },
+ languageOptions: {
+ globals: {
+ h: 'readonly',
+ unref: 'readonly',
+ provide: 'readonly',
+ inject: 'readonly',
+ markRaw: 'readonly',
+ defineAsyncComponent: 'readonly',
+ nextTick: 'readonly',
+ useRoute: 'readonly',
+ useRouter: 'readonly',
+ Message: 'readonly',
+ $loadingBar: 'readonly',
+ $message: 'readonly',
+ $dialog: 'readonly',
+ $notification: 'readonly',
+ $modal: 'readonly',
+ },
+ },
+})
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..ce02b2d
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+ %VITE_TITLE%
+
+
+
+
+
+
+
diff --git a/web/jsconfig.json b/web/jsconfig.json
new file mode 100644
index 0000000..270b986
--- /dev/null
+++ b/web/jsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "baseUrl": "./",
+ "moduleResolution": "node",
+ "paths": {
+ "@/*": ["src/*"],
+ "~/*": ["./*"]
+ },
+ "jsx": "preserve",
+ "allowJs": true
+ },
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/web/package.json b/web/package.json
new file mode 100644
index 0000000..fd9d2dc
--- /dev/null
+++ b/web/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "vue-naive-admin",
+ "type": "module",
+ "version": "2.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview",
+ "lint:fix": "eslint --fix",
+ "postinstall": "npx simple-git-hooks",
+ "up": "taze major -I"
+ },
+ "dependencies": {
+ "@arco-design/color": "^0.4.0",
+ "@vueuse/core": "^10.11.0",
+ "axios": "^1.7.2",
+ "dayjs": "^1.11.11",
+ "echarts": "^5.5.1",
+ "lodash-es": "^4.17.21",
+ "naive-ui": "^2.38.2",
+ "pinia": "^2.1.7",
+ "pinia-plugin-persistedstate": "^3.2.1",
+ "vue": "^3.4.31",
+ "vue-echarts": "^6.7.3",
+ "vue-router": "^4.4.0",
+ "xlsx": "^0.18.5"
+ },
+ "devDependencies": {
+ "@antfu/eslint-config": "^2.21.2",
+ "@iconify/json": "^2.2.223",
+ "@iconify/utils": "^2.1.25",
+ "@unocss/eslint-config": "^0.61.0",
+ "@unocss/eslint-plugin": "^0.61.0",
+ "@unocss/preset-rem-to-px": "^0.61.0",
+ "@vitejs/plugin-vue": "^5.0.5",
+ "eslint": "^9.6.0",
+ "eslint-plugin-format": "^0.1.2",
+ "esno": "^4.7.0",
+ "fs-extra": "^11.2.0",
+ "glob": "^10.4.2",
+ "lint-staged": "^15.2.7",
+ "rollup-plugin-visualizer": "^5.12.0",
+ "sass": "^1.77.6",
+ "simple-git-hooks": "^2.11.1",
+ "taze": "^0.13.9",
+ "unocss": "^0.61.0",
+ "unplugin-auto-import": "^0.17.6",
+ "unplugin-vue-components": "^0.27.2",
+ "vite": "^5.3.2",
+ "vite-plugin-router-warn": "^1.0.0",
+ "vite-plugin-vue-devtools": "^7.3.5"
+ },
+ "simple-git-hooks": {
+ "pre-commit": "pnpm lint-staged"
+ },
+ "lint-staged": {
+ "*": "eslint --fix"
+ }
+}
diff --git a/web/public/favicon.png b/web/public/favicon.png
new file mode 100644
index 0000000..929c58a
Binary files /dev/null and b/web/public/favicon.png differ
diff --git a/web/src/App.vue b/web/src/App.vue
new file mode 100644
index 0000000..ccad917
--- /dev/null
+++ b/web/src/App.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/api/index.js b/web/src/api/index.js
new file mode 100644
index 0000000..23cbe93
--- /dev/null
+++ b/web/src/api/index.js
@@ -0,0 +1,24 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:50:38
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { request } from '@/utils'
+
+export default {
+ // 获取用户信息
+ getUser: () => request.get('/user/detail'),
+ // 刷新token
+ refreshToken: () => request.get('/auth/refresh/token'),
+ // 登出
+ logout: () => request.post('/auth/logout', {}, { needTip: false }),
+ // 切换当前角色
+ switchCurrentRole: role => request.post(`/auth/current-role/switch/${role}`),
+ // 获取角色权限
+ getRolePermissions: () => request.get('/role/permissions/tree'),
+ // 验证菜单路径
+ validateMenuPath: path => request.get(`/permission/menu/validate?path=${path}`),
+}
diff --git a/web/src/assets/icons/dynamic-icons.js b/web/src/assets/icons/dynamic-icons.js
new file mode 100644
index 0000000..5326f66
--- /dev/null
+++ b/web/src/assets/icons/dynamic-icons.js
@@ -0,0 +1,10 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:50:49
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+// 需要动态渲染的iconify图标,以i-开头
+export default ['i-simple-icons:juejin']
diff --git a/web/src/assets/icons/feather/activity.svg b/web/src/assets/icons/feather/activity.svg
new file mode 100644
index 0000000..669a57a
--- /dev/null
+++ b/web/src/assets/icons/feather/activity.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/airplay.svg b/web/src/assets/icons/feather/airplay.svg
new file mode 100644
index 0000000..7ce7302
--- /dev/null
+++ b/web/src/assets/icons/feather/airplay.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/alert-circle.svg b/web/src/assets/icons/feather/alert-circle.svg
new file mode 100644
index 0000000..8d02b7d
--- /dev/null
+++ b/web/src/assets/icons/feather/alert-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/alert-octagon.svg b/web/src/assets/icons/feather/alert-octagon.svg
new file mode 100644
index 0000000..de9b03f
--- /dev/null
+++ b/web/src/assets/icons/feather/alert-octagon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/alert-triangle.svg b/web/src/assets/icons/feather/alert-triangle.svg
new file mode 100644
index 0000000..6dcb096
--- /dev/null
+++ b/web/src/assets/icons/feather/alert-triangle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/align-center.svg b/web/src/assets/icons/feather/align-center.svg
new file mode 100644
index 0000000..5b8842e
--- /dev/null
+++ b/web/src/assets/icons/feather/align-center.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/align-justify.svg b/web/src/assets/icons/feather/align-justify.svg
new file mode 100644
index 0000000..0539876
--- /dev/null
+++ b/web/src/assets/icons/feather/align-justify.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/align-left.svg b/web/src/assets/icons/feather/align-left.svg
new file mode 100644
index 0000000..9ac852a
--- /dev/null
+++ b/web/src/assets/icons/feather/align-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/align-right.svg b/web/src/assets/icons/feather/align-right.svg
new file mode 100644
index 0000000..ef139ff
--- /dev/null
+++ b/web/src/assets/icons/feather/align-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/anchor.svg b/web/src/assets/icons/feather/anchor.svg
new file mode 100644
index 0000000..e01627a
--- /dev/null
+++ b/web/src/assets/icons/feather/anchor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/aperture.svg b/web/src/assets/icons/feather/aperture.svg
new file mode 100644
index 0000000..9936e86
--- /dev/null
+++ b/web/src/assets/icons/feather/aperture.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/archive.svg b/web/src/assets/icons/feather/archive.svg
new file mode 100644
index 0000000..428882c
--- /dev/null
+++ b/web/src/assets/icons/feather/archive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-down-circle.svg b/web/src/assets/icons/feather/arrow-down-circle.svg
new file mode 100644
index 0000000..3238091
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-down-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-down-left.svg b/web/src/assets/icons/feather/arrow-down-left.svg
new file mode 100644
index 0000000..7248358
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-down-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-down-right.svg b/web/src/assets/icons/feather/arrow-down-right.svg
new file mode 100644
index 0000000..81d9822
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-down-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-down.svg b/web/src/assets/icons/feather/arrow-down.svg
new file mode 100644
index 0000000..4f84f62
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-left-circle.svg b/web/src/assets/icons/feather/arrow-left-circle.svg
new file mode 100644
index 0000000..3b19ff8
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-left-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-left.svg b/web/src/assets/icons/feather/arrow-left.svg
new file mode 100644
index 0000000..a5058fc
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-right-circle.svg b/web/src/assets/icons/feather/arrow-right-circle.svg
new file mode 100644
index 0000000..ff01dd5
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-right-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-right.svg b/web/src/assets/icons/feather/arrow-right.svg
new file mode 100644
index 0000000..939b57c
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-up-circle.svg b/web/src/assets/icons/feather/arrow-up-circle.svg
new file mode 100644
index 0000000..044a75d
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-up-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-up-left.svg b/web/src/assets/icons/feather/arrow-up-left.svg
new file mode 100644
index 0000000..cea55e8
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-up-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-up-right.svg b/web/src/assets/icons/feather/arrow-up-right.svg
new file mode 100644
index 0000000..95678e0
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-up-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/arrow-up.svg b/web/src/assets/icons/feather/arrow-up.svg
new file mode 100644
index 0000000..16b13ab
--- /dev/null
+++ b/web/src/assets/icons/feather/arrow-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/at-sign.svg b/web/src/assets/icons/feather/at-sign.svg
new file mode 100644
index 0000000..5a5e5d0
--- /dev/null
+++ b/web/src/assets/icons/feather/at-sign.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/award.svg b/web/src/assets/icons/feather/award.svg
new file mode 100644
index 0000000..be70d5a
--- /dev/null
+++ b/web/src/assets/icons/feather/award.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/bar-chart-2.svg b/web/src/assets/icons/feather/bar-chart-2.svg
new file mode 100644
index 0000000..864167a
--- /dev/null
+++ b/web/src/assets/icons/feather/bar-chart-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/bar-chart.svg b/web/src/assets/icons/feather/bar-chart.svg
new file mode 100644
index 0000000..074d7c1
--- /dev/null
+++ b/web/src/assets/icons/feather/bar-chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/battery-charging.svg b/web/src/assets/icons/feather/battery-charging.svg
new file mode 100644
index 0000000..644cb59
--- /dev/null
+++ b/web/src/assets/icons/feather/battery-charging.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/battery.svg b/web/src/assets/icons/feather/battery.svg
new file mode 100644
index 0000000..7fe8771
--- /dev/null
+++ b/web/src/assets/icons/feather/battery.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/bell-off.svg b/web/src/assets/icons/feather/bell-off.svg
new file mode 100644
index 0000000..4b07c84
--- /dev/null
+++ b/web/src/assets/icons/feather/bell-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/bell.svg b/web/src/assets/icons/feather/bell.svg
new file mode 100644
index 0000000..bba561c
--- /dev/null
+++ b/web/src/assets/icons/feather/bell.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/bluetooth.svg b/web/src/assets/icons/feather/bluetooth.svg
new file mode 100644
index 0000000..cebed7b
--- /dev/null
+++ b/web/src/assets/icons/feather/bluetooth.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/bold.svg b/web/src/assets/icons/feather/bold.svg
new file mode 100644
index 0000000..d1a4efd
--- /dev/null
+++ b/web/src/assets/icons/feather/bold.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/book-open.svg b/web/src/assets/icons/feather/book-open.svg
new file mode 100644
index 0000000..5e0ca0a
--- /dev/null
+++ b/web/src/assets/icons/feather/book-open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/book.svg b/web/src/assets/icons/feather/book.svg
new file mode 100644
index 0000000..12ffcbc
--- /dev/null
+++ b/web/src/assets/icons/feather/book.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/bookmark.svg b/web/src/assets/icons/feather/bookmark.svg
new file mode 100644
index 0000000..2239cc5
--- /dev/null
+++ b/web/src/assets/icons/feather/bookmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/box.svg b/web/src/assets/icons/feather/box.svg
new file mode 100644
index 0000000..d89be30
--- /dev/null
+++ b/web/src/assets/icons/feather/box.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/briefcase.svg b/web/src/assets/icons/feather/briefcase.svg
new file mode 100644
index 0000000..e3af050
--- /dev/null
+++ b/web/src/assets/icons/feather/briefcase.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/calendar.svg b/web/src/assets/icons/feather/calendar.svg
new file mode 100644
index 0000000..6c7fd87
--- /dev/null
+++ b/web/src/assets/icons/feather/calendar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/camera-off.svg b/web/src/assets/icons/feather/camera-off.svg
new file mode 100644
index 0000000..daa3e25
--- /dev/null
+++ b/web/src/assets/icons/feather/camera-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/camera.svg b/web/src/assets/icons/feather/camera.svg
new file mode 100644
index 0000000..0e7f060
--- /dev/null
+++ b/web/src/assets/icons/feather/camera.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cast.svg b/web/src/assets/icons/feather/cast.svg
new file mode 100644
index 0000000..63c954d
--- /dev/null
+++ b/web/src/assets/icons/feather/cast.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/check-circle.svg b/web/src/assets/icons/feather/check-circle.svg
new file mode 100644
index 0000000..f2f4fd1
--- /dev/null
+++ b/web/src/assets/icons/feather/check-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/check-square.svg b/web/src/assets/icons/feather/check-square.svg
new file mode 100644
index 0000000..72ab7a8
--- /dev/null
+++ b/web/src/assets/icons/feather/check-square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/check.svg b/web/src/assets/icons/feather/check.svg
new file mode 100644
index 0000000..1c20989
--- /dev/null
+++ b/web/src/assets/icons/feather/check.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevron-down.svg b/web/src/assets/icons/feather/chevron-down.svg
new file mode 100644
index 0000000..278c6a3
--- /dev/null
+++ b/web/src/assets/icons/feather/chevron-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevron-left.svg b/web/src/assets/icons/feather/chevron-left.svg
new file mode 100644
index 0000000..747d46d
--- /dev/null
+++ b/web/src/assets/icons/feather/chevron-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevron-right.svg b/web/src/assets/icons/feather/chevron-right.svg
new file mode 100644
index 0000000..258de41
--- /dev/null
+++ b/web/src/assets/icons/feather/chevron-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevron-up.svg b/web/src/assets/icons/feather/chevron-up.svg
new file mode 100644
index 0000000..4eb5ecc
--- /dev/null
+++ b/web/src/assets/icons/feather/chevron-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevrons-down.svg b/web/src/assets/icons/feather/chevrons-down.svg
new file mode 100644
index 0000000..e67ef2f
--- /dev/null
+++ b/web/src/assets/icons/feather/chevrons-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevrons-left.svg b/web/src/assets/icons/feather/chevrons-left.svg
new file mode 100644
index 0000000..c32e398
--- /dev/null
+++ b/web/src/assets/icons/feather/chevrons-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevrons-right.svg b/web/src/assets/icons/feather/chevrons-right.svg
new file mode 100644
index 0000000..f506814
--- /dev/null
+++ b/web/src/assets/icons/feather/chevrons-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chevrons-up.svg b/web/src/assets/icons/feather/chevrons-up.svg
new file mode 100644
index 0000000..0eaf518
--- /dev/null
+++ b/web/src/assets/icons/feather/chevrons-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/chrome.svg b/web/src/assets/icons/feather/chrome.svg
new file mode 100644
index 0000000..9189815
--- /dev/null
+++ b/web/src/assets/icons/feather/chrome.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/circle.svg b/web/src/assets/icons/feather/circle.svg
new file mode 100644
index 0000000..b009088
--- /dev/null
+++ b/web/src/assets/icons/feather/circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/clipboard.svg b/web/src/assets/icons/feather/clipboard.svg
new file mode 100644
index 0000000..ccee454
--- /dev/null
+++ b/web/src/assets/icons/feather/clipboard.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/clock.svg b/web/src/assets/icons/feather/clock.svg
new file mode 100644
index 0000000..ea3f5e5
--- /dev/null
+++ b/web/src/assets/icons/feather/clock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cloud-drizzle.svg b/web/src/assets/icons/feather/cloud-drizzle.svg
new file mode 100644
index 0000000..13af6bb
--- /dev/null
+++ b/web/src/assets/icons/feather/cloud-drizzle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cloud-lightning.svg b/web/src/assets/icons/feather/cloud-lightning.svg
new file mode 100644
index 0000000..32d154c
--- /dev/null
+++ b/web/src/assets/icons/feather/cloud-lightning.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cloud-off.svg b/web/src/assets/icons/feather/cloud-off.svg
new file mode 100644
index 0000000..1e1e7d6
--- /dev/null
+++ b/web/src/assets/icons/feather/cloud-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cloud-rain.svg b/web/src/assets/icons/feather/cloud-rain.svg
new file mode 100644
index 0000000..3e0b85b
--- /dev/null
+++ b/web/src/assets/icons/feather/cloud-rain.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cloud-snow.svg b/web/src/assets/icons/feather/cloud-snow.svg
new file mode 100644
index 0000000..e4eb820
--- /dev/null
+++ b/web/src/assets/icons/feather/cloud-snow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cloud.svg b/web/src/assets/icons/feather/cloud.svg
new file mode 100644
index 0000000..0ee0c63
--- /dev/null
+++ b/web/src/assets/icons/feather/cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/code.svg b/web/src/assets/icons/feather/code.svg
new file mode 100644
index 0000000..c4954b5
--- /dev/null
+++ b/web/src/assets/icons/feather/code.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/codepen.svg b/web/src/assets/icons/feather/codepen.svg
new file mode 100644
index 0000000..ab2a815
--- /dev/null
+++ b/web/src/assets/icons/feather/codepen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/codesandbox.svg b/web/src/assets/icons/feather/codesandbox.svg
new file mode 100644
index 0000000..49848f5
--- /dev/null
+++ b/web/src/assets/icons/feather/codesandbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/coffee.svg b/web/src/assets/icons/feather/coffee.svg
new file mode 100644
index 0000000..32905e5
--- /dev/null
+++ b/web/src/assets/icons/feather/coffee.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/columns.svg b/web/src/assets/icons/feather/columns.svg
new file mode 100644
index 0000000..d264b55
--- /dev/null
+++ b/web/src/assets/icons/feather/columns.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/command.svg b/web/src/assets/icons/feather/command.svg
new file mode 100644
index 0000000..93f554c
--- /dev/null
+++ b/web/src/assets/icons/feather/command.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/compass.svg b/web/src/assets/icons/feather/compass.svg
new file mode 100644
index 0000000..3296260
--- /dev/null
+++ b/web/src/assets/icons/feather/compass.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/copy.svg b/web/src/assets/icons/feather/copy.svg
new file mode 100644
index 0000000..4e0b09f
--- /dev/null
+++ b/web/src/assets/icons/feather/copy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-down-left.svg b/web/src/assets/icons/feather/corner-down-left.svg
new file mode 100644
index 0000000..9fffb3e
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-down-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-down-right.svg b/web/src/assets/icons/feather/corner-down-right.svg
new file mode 100644
index 0000000..b27d408
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-down-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-left-down.svg b/web/src/assets/icons/feather/corner-left-down.svg
new file mode 100644
index 0000000..24b8375
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-left-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-left-up.svg b/web/src/assets/icons/feather/corner-left-up.svg
new file mode 100644
index 0000000..e54527c
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-left-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-right-down.svg b/web/src/assets/icons/feather/corner-right-down.svg
new file mode 100644
index 0000000..a49e6d6
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-right-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-right-up.svg b/web/src/assets/icons/feather/corner-right-up.svg
new file mode 100644
index 0000000..a5c5dce
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-right-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-up-left.svg b/web/src/assets/icons/feather/corner-up-left.svg
new file mode 100644
index 0000000..0a1ffd6
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-up-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/corner-up-right.svg b/web/src/assets/icons/feather/corner-up-right.svg
new file mode 100644
index 0000000..0b8f961
--- /dev/null
+++ b/web/src/assets/icons/feather/corner-up-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/cpu.svg b/web/src/assets/icons/feather/cpu.svg
new file mode 100644
index 0000000..2ed16ef
--- /dev/null
+++ b/web/src/assets/icons/feather/cpu.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/credit-card.svg b/web/src/assets/icons/feather/credit-card.svg
new file mode 100644
index 0000000..1b7fd02
--- /dev/null
+++ b/web/src/assets/icons/feather/credit-card.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/crop.svg b/web/src/assets/icons/feather/crop.svg
new file mode 100644
index 0000000..ffbfd04
--- /dev/null
+++ b/web/src/assets/icons/feather/crop.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/crosshair.svg b/web/src/assets/icons/feather/crosshair.svg
new file mode 100644
index 0000000..ba39401
--- /dev/null
+++ b/web/src/assets/icons/feather/crosshair.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/database.svg b/web/src/assets/icons/feather/database.svg
new file mode 100644
index 0000000..c296fbc
--- /dev/null
+++ b/web/src/assets/icons/feather/database.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/delete.svg b/web/src/assets/icons/feather/delete.svg
new file mode 100644
index 0000000..8c6074b
--- /dev/null
+++ b/web/src/assets/icons/feather/delete.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/disc.svg b/web/src/assets/icons/feather/disc.svg
new file mode 100644
index 0000000..2595b44
--- /dev/null
+++ b/web/src/assets/icons/feather/disc.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/divide.svg b/web/src/assets/icons/feather/divide.svg
new file mode 100644
index 0000000..3cbff3a
--- /dev/null
+++ b/web/src/assets/icons/feather/divide.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/dollar-sign.svg b/web/src/assets/icons/feather/dollar-sign.svg
new file mode 100644
index 0000000..1a124d2
--- /dev/null
+++ b/web/src/assets/icons/feather/dollar-sign.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/download-cloud.svg b/web/src/assets/icons/feather/download-cloud.svg
new file mode 100644
index 0000000..f3126fc
--- /dev/null
+++ b/web/src/assets/icons/feather/download-cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/download.svg b/web/src/assets/icons/feather/download.svg
new file mode 100644
index 0000000..76767a9
--- /dev/null
+++ b/web/src/assets/icons/feather/download.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/dribbble.svg b/web/src/assets/icons/feather/dribbble.svg
new file mode 100644
index 0000000..bb8577d
--- /dev/null
+++ b/web/src/assets/icons/feather/dribbble.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/droplet.svg b/web/src/assets/icons/feather/droplet.svg
new file mode 100644
index 0000000..ca09301
--- /dev/null
+++ b/web/src/assets/icons/feather/droplet.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/edit-2.svg b/web/src/assets/icons/feather/edit-2.svg
new file mode 100644
index 0000000..06830c9
--- /dev/null
+++ b/web/src/assets/icons/feather/edit-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/edit-3.svg b/web/src/assets/icons/feather/edit-3.svg
new file mode 100644
index 0000000..d728efc
--- /dev/null
+++ b/web/src/assets/icons/feather/edit-3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/edit.svg b/web/src/assets/icons/feather/edit.svg
new file mode 100644
index 0000000..ec7b4ca
--- /dev/null
+++ b/web/src/assets/icons/feather/edit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/external-link.svg b/web/src/assets/icons/feather/external-link.svg
new file mode 100644
index 0000000..6236df3
--- /dev/null
+++ b/web/src/assets/icons/feather/external-link.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/eye-off.svg b/web/src/assets/icons/feather/eye-off.svg
new file mode 100644
index 0000000..77c54cb
--- /dev/null
+++ b/web/src/assets/icons/feather/eye-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/eye.svg b/web/src/assets/icons/feather/eye.svg
new file mode 100644
index 0000000..9cde243
--- /dev/null
+++ b/web/src/assets/icons/feather/eye.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/facebook.svg b/web/src/assets/icons/feather/facebook.svg
new file mode 100644
index 0000000..2570f56
--- /dev/null
+++ b/web/src/assets/icons/feather/facebook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/fast-forward.svg b/web/src/assets/icons/feather/fast-forward.svg
new file mode 100644
index 0000000..fa39877
--- /dev/null
+++ b/web/src/assets/icons/feather/fast-forward.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/feather.svg b/web/src/assets/icons/feather/feather.svg
new file mode 100644
index 0000000..ac3b868
--- /dev/null
+++ b/web/src/assets/icons/feather/feather.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/figma.svg b/web/src/assets/icons/feather/figma.svg
new file mode 100644
index 0000000..66fd217
--- /dev/null
+++ b/web/src/assets/icons/feather/figma.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/file-minus.svg b/web/src/assets/icons/feather/file-minus.svg
new file mode 100644
index 0000000..345756e
--- /dev/null
+++ b/web/src/assets/icons/feather/file-minus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/file-plus.svg b/web/src/assets/icons/feather/file-plus.svg
new file mode 100644
index 0000000..eed1200
--- /dev/null
+++ b/web/src/assets/icons/feather/file-plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/file-text.svg b/web/src/assets/icons/feather/file-text.svg
new file mode 100644
index 0000000..4197ddd
--- /dev/null
+++ b/web/src/assets/icons/feather/file-text.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/file.svg b/web/src/assets/icons/feather/file.svg
new file mode 100644
index 0000000..378519a
--- /dev/null
+++ b/web/src/assets/icons/feather/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/film.svg b/web/src/assets/icons/feather/film.svg
new file mode 100644
index 0000000..ac46360
--- /dev/null
+++ b/web/src/assets/icons/feather/film.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/filter.svg b/web/src/assets/icons/feather/filter.svg
new file mode 100644
index 0000000..38a47e0
--- /dev/null
+++ b/web/src/assets/icons/feather/filter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/flag.svg b/web/src/assets/icons/feather/flag.svg
new file mode 100644
index 0000000..037737c
--- /dev/null
+++ b/web/src/assets/icons/feather/flag.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/folder-minus.svg b/web/src/assets/icons/feather/folder-minus.svg
new file mode 100644
index 0000000..d5b7af6
--- /dev/null
+++ b/web/src/assets/icons/feather/folder-minus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/folder-plus.svg b/web/src/assets/icons/feather/folder-plus.svg
new file mode 100644
index 0000000..898f2fc
--- /dev/null
+++ b/web/src/assets/icons/feather/folder-plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/folder.svg b/web/src/assets/icons/feather/folder.svg
new file mode 100644
index 0000000..134458b
--- /dev/null
+++ b/web/src/assets/icons/feather/folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/framer.svg b/web/src/assets/icons/feather/framer.svg
new file mode 100644
index 0000000..3e66347
--- /dev/null
+++ b/web/src/assets/icons/feather/framer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/frown.svg b/web/src/assets/icons/feather/frown.svg
new file mode 100644
index 0000000..f312254
--- /dev/null
+++ b/web/src/assets/icons/feather/frown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/gift.svg b/web/src/assets/icons/feather/gift.svg
new file mode 100644
index 0000000..d2c14bd
--- /dev/null
+++ b/web/src/assets/icons/feather/gift.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/git-branch.svg b/web/src/assets/icons/feather/git-branch.svg
new file mode 100644
index 0000000..4400372
--- /dev/null
+++ b/web/src/assets/icons/feather/git-branch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/git-commit.svg b/web/src/assets/icons/feather/git-commit.svg
new file mode 100644
index 0000000..e959d72
--- /dev/null
+++ b/web/src/assets/icons/feather/git-commit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/git-merge.svg b/web/src/assets/icons/feather/git-merge.svg
new file mode 100644
index 0000000..c65fffd
--- /dev/null
+++ b/web/src/assets/icons/feather/git-merge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/git-pull-request.svg b/web/src/assets/icons/feather/git-pull-request.svg
new file mode 100644
index 0000000..fc80bdf
--- /dev/null
+++ b/web/src/assets/icons/feather/git-pull-request.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/github.svg b/web/src/assets/icons/feather/github.svg
new file mode 100644
index 0000000..ff0af48
--- /dev/null
+++ b/web/src/assets/icons/feather/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/gitlab.svg b/web/src/assets/icons/feather/gitlab.svg
new file mode 100644
index 0000000..85d54a1
--- /dev/null
+++ b/web/src/assets/icons/feather/gitlab.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/globe.svg b/web/src/assets/icons/feather/globe.svg
new file mode 100644
index 0000000..0a0586d
--- /dev/null
+++ b/web/src/assets/icons/feather/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/grid.svg b/web/src/assets/icons/feather/grid.svg
new file mode 100644
index 0000000..8ef2e9d
--- /dev/null
+++ b/web/src/assets/icons/feather/grid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/hard-drive.svg b/web/src/assets/icons/feather/hard-drive.svg
new file mode 100644
index 0000000..8e90fa1
--- /dev/null
+++ b/web/src/assets/icons/feather/hard-drive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/hash.svg b/web/src/assets/icons/feather/hash.svg
new file mode 100644
index 0000000..c9c8d41
--- /dev/null
+++ b/web/src/assets/icons/feather/hash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/headphones.svg b/web/src/assets/icons/feather/headphones.svg
new file mode 100644
index 0000000..fd8915b
--- /dev/null
+++ b/web/src/assets/icons/feather/headphones.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/heart.svg b/web/src/assets/icons/feather/heart.svg
new file mode 100644
index 0000000..a083b7e
--- /dev/null
+++ b/web/src/assets/icons/feather/heart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/help-circle.svg b/web/src/assets/icons/feather/help-circle.svg
new file mode 100644
index 0000000..51fddd8
--- /dev/null
+++ b/web/src/assets/icons/feather/help-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/hexagon.svg b/web/src/assets/icons/feather/hexagon.svg
new file mode 100644
index 0000000..eae7f25
--- /dev/null
+++ b/web/src/assets/icons/feather/hexagon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/home.svg b/web/src/assets/icons/feather/home.svg
new file mode 100644
index 0000000..7bb31b2
--- /dev/null
+++ b/web/src/assets/icons/feather/home.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/image.svg b/web/src/assets/icons/feather/image.svg
new file mode 100644
index 0000000..a7d84b9
--- /dev/null
+++ b/web/src/assets/icons/feather/image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/inbox.svg b/web/src/assets/icons/feather/inbox.svg
new file mode 100644
index 0000000..03a13b4
--- /dev/null
+++ b/web/src/assets/icons/feather/inbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/info.svg b/web/src/assets/icons/feather/info.svg
new file mode 100644
index 0000000..a09fa5f
--- /dev/null
+++ b/web/src/assets/icons/feather/info.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/instagram.svg b/web/src/assets/icons/feather/instagram.svg
new file mode 100644
index 0000000..9fdb8e3
--- /dev/null
+++ b/web/src/assets/icons/feather/instagram.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/italic.svg b/web/src/assets/icons/feather/italic.svg
new file mode 100644
index 0000000..a123d37
--- /dev/null
+++ b/web/src/assets/icons/feather/italic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/key.svg b/web/src/assets/icons/feather/key.svg
new file mode 100644
index 0000000..e778e74
--- /dev/null
+++ b/web/src/assets/icons/feather/key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/layers.svg b/web/src/assets/icons/feather/layers.svg
new file mode 100644
index 0000000..ea788c2
--- /dev/null
+++ b/web/src/assets/icons/feather/layers.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/layout.svg b/web/src/assets/icons/feather/layout.svg
new file mode 100644
index 0000000..28743d9
--- /dev/null
+++ b/web/src/assets/icons/feather/layout.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/life-buoy.svg b/web/src/assets/icons/feather/life-buoy.svg
new file mode 100644
index 0000000..54c2bd7
--- /dev/null
+++ b/web/src/assets/icons/feather/life-buoy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/link-2.svg b/web/src/assets/icons/feather/link-2.svg
new file mode 100644
index 0000000..8cc7f6d
--- /dev/null
+++ b/web/src/assets/icons/feather/link-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/link.svg b/web/src/assets/icons/feather/link.svg
new file mode 100644
index 0000000..c89dd41
--- /dev/null
+++ b/web/src/assets/icons/feather/link.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/linkedin.svg b/web/src/assets/icons/feather/linkedin.svg
new file mode 100644
index 0000000..3953109
--- /dev/null
+++ b/web/src/assets/icons/feather/linkedin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/list.svg b/web/src/assets/icons/feather/list.svg
new file mode 100644
index 0000000..5ce38ea
--- /dev/null
+++ b/web/src/assets/icons/feather/list.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/loader.svg b/web/src/assets/icons/feather/loader.svg
new file mode 100644
index 0000000..e1a70c1
--- /dev/null
+++ b/web/src/assets/icons/feather/loader.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/lock.svg b/web/src/assets/icons/feather/lock.svg
new file mode 100644
index 0000000..de09d9d
--- /dev/null
+++ b/web/src/assets/icons/feather/lock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/log-in.svg b/web/src/assets/icons/feather/log-in.svg
new file mode 100644
index 0000000..ba0da59
--- /dev/null
+++ b/web/src/assets/icons/feather/log-in.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/log-out.svg b/web/src/assets/icons/feather/log-out.svg
new file mode 100644
index 0000000..c9002c9
--- /dev/null
+++ b/web/src/assets/icons/feather/log-out.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/mail.svg b/web/src/assets/icons/feather/mail.svg
new file mode 100644
index 0000000..2af169e
--- /dev/null
+++ b/web/src/assets/icons/feather/mail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/map-pin.svg b/web/src/assets/icons/feather/map-pin.svg
new file mode 100644
index 0000000..d5548e9
--- /dev/null
+++ b/web/src/assets/icons/feather/map-pin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/map.svg b/web/src/assets/icons/feather/map.svg
new file mode 100644
index 0000000..ecebd7b
--- /dev/null
+++ b/web/src/assets/icons/feather/map.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/maximize-2.svg b/web/src/assets/icons/feather/maximize-2.svg
new file mode 100644
index 0000000..e41fc0b
--- /dev/null
+++ b/web/src/assets/icons/feather/maximize-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/maximize.svg b/web/src/assets/icons/feather/maximize.svg
new file mode 100644
index 0000000..fc30518
--- /dev/null
+++ b/web/src/assets/icons/feather/maximize.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/meh.svg b/web/src/assets/icons/feather/meh.svg
new file mode 100644
index 0000000..6f57fff
--- /dev/null
+++ b/web/src/assets/icons/feather/meh.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/menu.svg b/web/src/assets/icons/feather/menu.svg
new file mode 100644
index 0000000..e8a84a9
--- /dev/null
+++ b/web/src/assets/icons/feather/menu.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/message-circle.svg b/web/src/assets/icons/feather/message-circle.svg
new file mode 100644
index 0000000..4b21b32
--- /dev/null
+++ b/web/src/assets/icons/feather/message-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/message-square.svg b/web/src/assets/icons/feather/message-square.svg
new file mode 100644
index 0000000..6a2e4e5
--- /dev/null
+++ b/web/src/assets/icons/feather/message-square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/mic-off.svg b/web/src/assets/icons/feather/mic-off.svg
new file mode 100644
index 0000000..0786219
--- /dev/null
+++ b/web/src/assets/icons/feather/mic-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/mic.svg b/web/src/assets/icons/feather/mic.svg
new file mode 100644
index 0000000..dc5f780
--- /dev/null
+++ b/web/src/assets/icons/feather/mic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/minimize-2.svg b/web/src/assets/icons/feather/minimize-2.svg
new file mode 100644
index 0000000..a720fa6
--- /dev/null
+++ b/web/src/assets/icons/feather/minimize-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/minimize.svg b/web/src/assets/icons/feather/minimize.svg
new file mode 100644
index 0000000..46d6119
--- /dev/null
+++ b/web/src/assets/icons/feather/minimize.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/minus-circle.svg b/web/src/assets/icons/feather/minus-circle.svg
new file mode 100644
index 0000000..80c0de1
--- /dev/null
+++ b/web/src/assets/icons/feather/minus-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/minus-square.svg b/web/src/assets/icons/feather/minus-square.svg
new file mode 100644
index 0000000..4862832
--- /dev/null
+++ b/web/src/assets/icons/feather/minus-square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/minus.svg b/web/src/assets/icons/feather/minus.svg
new file mode 100644
index 0000000..93cc734
--- /dev/null
+++ b/web/src/assets/icons/feather/minus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/monitor.svg b/web/src/assets/icons/feather/monitor.svg
new file mode 100644
index 0000000..6c3556d
--- /dev/null
+++ b/web/src/assets/icons/feather/monitor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/moon.svg b/web/src/assets/icons/feather/moon.svg
new file mode 100644
index 0000000..dbf7c6c
--- /dev/null
+++ b/web/src/assets/icons/feather/moon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/more-horizontal.svg b/web/src/assets/icons/feather/more-horizontal.svg
new file mode 100644
index 0000000..dc6a855
--- /dev/null
+++ b/web/src/assets/icons/feather/more-horizontal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/more-vertical.svg b/web/src/assets/icons/feather/more-vertical.svg
new file mode 100644
index 0000000..cba6958
--- /dev/null
+++ b/web/src/assets/icons/feather/more-vertical.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/mouse-pointer.svg b/web/src/assets/icons/feather/mouse-pointer.svg
new file mode 100644
index 0000000..f5af559
--- /dev/null
+++ b/web/src/assets/icons/feather/mouse-pointer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/move.svg b/web/src/assets/icons/feather/move.svg
new file mode 100644
index 0000000..4e251b5
--- /dev/null
+++ b/web/src/assets/icons/feather/move.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/music.svg b/web/src/assets/icons/feather/music.svg
new file mode 100644
index 0000000..7bee2f7
--- /dev/null
+++ b/web/src/assets/icons/feather/music.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/navigation-2.svg b/web/src/assets/icons/feather/navigation-2.svg
new file mode 100644
index 0000000..ae31db9
--- /dev/null
+++ b/web/src/assets/icons/feather/navigation-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/navigation.svg b/web/src/assets/icons/feather/navigation.svg
new file mode 100644
index 0000000..f600a41
--- /dev/null
+++ b/web/src/assets/icons/feather/navigation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/octagon.svg b/web/src/assets/icons/feather/octagon.svg
new file mode 100644
index 0000000..124c548
--- /dev/null
+++ b/web/src/assets/icons/feather/octagon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/package.svg b/web/src/assets/icons/feather/package.svg
new file mode 100644
index 0000000..f1e09ee
--- /dev/null
+++ b/web/src/assets/icons/feather/package.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/paperclip.svg b/web/src/assets/icons/feather/paperclip.svg
new file mode 100644
index 0000000..b1f69b7
--- /dev/null
+++ b/web/src/assets/icons/feather/paperclip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/pause-circle.svg b/web/src/assets/icons/feather/pause-circle.svg
new file mode 100644
index 0000000..f6b1a8d
--- /dev/null
+++ b/web/src/assets/icons/feather/pause-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/pause.svg b/web/src/assets/icons/feather/pause.svg
new file mode 100644
index 0000000..4e78038
--- /dev/null
+++ b/web/src/assets/icons/feather/pause.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/pen-tool.svg b/web/src/assets/icons/feather/pen-tool.svg
new file mode 100644
index 0000000..0d26fa1
--- /dev/null
+++ b/web/src/assets/icons/feather/pen-tool.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/percent.svg b/web/src/assets/icons/feather/percent.svg
new file mode 100644
index 0000000..2cb9719
--- /dev/null
+++ b/web/src/assets/icons/feather/percent.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/phone-call.svg b/web/src/assets/icons/feather/phone-call.svg
new file mode 100644
index 0000000..8b86660
--- /dev/null
+++ b/web/src/assets/icons/feather/phone-call.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/phone-forwarded.svg b/web/src/assets/icons/feather/phone-forwarded.svg
new file mode 100644
index 0000000..aa21bef
--- /dev/null
+++ b/web/src/assets/icons/feather/phone-forwarded.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/phone-incoming.svg b/web/src/assets/icons/feather/phone-incoming.svg
new file mode 100644
index 0000000..b2d523a
--- /dev/null
+++ b/web/src/assets/icons/feather/phone-incoming.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/phone-missed.svg b/web/src/assets/icons/feather/phone-missed.svg
new file mode 100644
index 0000000..4950f09
--- /dev/null
+++ b/web/src/assets/icons/feather/phone-missed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/phone-off.svg b/web/src/assets/icons/feather/phone-off.svg
new file mode 100644
index 0000000..4d00fb3
--- /dev/null
+++ b/web/src/assets/icons/feather/phone-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/phone-outgoing.svg b/web/src/assets/icons/feather/phone-outgoing.svg
new file mode 100644
index 0000000..fea27a3
--- /dev/null
+++ b/web/src/assets/icons/feather/phone-outgoing.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/phone.svg b/web/src/assets/icons/feather/phone.svg
new file mode 100644
index 0000000..2a35154
--- /dev/null
+++ b/web/src/assets/icons/feather/phone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/pie-chart.svg b/web/src/assets/icons/feather/pie-chart.svg
new file mode 100644
index 0000000..b5bbe67
--- /dev/null
+++ b/web/src/assets/icons/feather/pie-chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/play-circle.svg b/web/src/assets/icons/feather/play-circle.svg
new file mode 100644
index 0000000..8766dc7
--- /dev/null
+++ b/web/src/assets/icons/feather/play-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/play.svg b/web/src/assets/icons/feather/play.svg
new file mode 100644
index 0000000..fd76e30
--- /dev/null
+++ b/web/src/assets/icons/feather/play.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/plus-circle.svg b/web/src/assets/icons/feather/plus-circle.svg
new file mode 100644
index 0000000..4291ff0
--- /dev/null
+++ b/web/src/assets/icons/feather/plus-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/plus-square.svg b/web/src/assets/icons/feather/plus-square.svg
new file mode 100644
index 0000000..c380e24
--- /dev/null
+++ b/web/src/assets/icons/feather/plus-square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/plus.svg b/web/src/assets/icons/feather/plus.svg
new file mode 100644
index 0000000..703c5b7
--- /dev/null
+++ b/web/src/assets/icons/feather/plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/pocket.svg b/web/src/assets/icons/feather/pocket.svg
new file mode 100644
index 0000000..a3b2561
--- /dev/null
+++ b/web/src/assets/icons/feather/pocket.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/power.svg b/web/src/assets/icons/feather/power.svg
new file mode 100644
index 0000000..598308f
--- /dev/null
+++ b/web/src/assets/icons/feather/power.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/printer.svg b/web/src/assets/icons/feather/printer.svg
new file mode 100644
index 0000000..8a9a7ac
--- /dev/null
+++ b/web/src/assets/icons/feather/printer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/radio.svg b/web/src/assets/icons/feather/radio.svg
new file mode 100644
index 0000000..5abfcd1
--- /dev/null
+++ b/web/src/assets/icons/feather/radio.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/refresh-ccw.svg b/web/src/assets/icons/feather/refresh-ccw.svg
new file mode 100644
index 0000000..10cff0e
--- /dev/null
+++ b/web/src/assets/icons/feather/refresh-ccw.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/refresh-cw.svg b/web/src/assets/icons/feather/refresh-cw.svg
new file mode 100644
index 0000000..06c358d
--- /dev/null
+++ b/web/src/assets/icons/feather/refresh-cw.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/repeat.svg b/web/src/assets/icons/feather/repeat.svg
new file mode 100644
index 0000000..c7657b0
--- /dev/null
+++ b/web/src/assets/icons/feather/repeat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/rewind.svg b/web/src/assets/icons/feather/rewind.svg
new file mode 100644
index 0000000..7b0fa3d
--- /dev/null
+++ b/web/src/assets/icons/feather/rewind.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/rotate-ccw.svg b/web/src/assets/icons/feather/rotate-ccw.svg
new file mode 100644
index 0000000..ade5dc4
--- /dev/null
+++ b/web/src/assets/icons/feather/rotate-ccw.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/rotate-cw.svg b/web/src/assets/icons/feather/rotate-cw.svg
new file mode 100644
index 0000000..83dca35
--- /dev/null
+++ b/web/src/assets/icons/feather/rotate-cw.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/rss.svg b/web/src/assets/icons/feather/rss.svg
new file mode 100644
index 0000000..c9a1368
--- /dev/null
+++ b/web/src/assets/icons/feather/rss.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/save.svg b/web/src/assets/icons/feather/save.svg
new file mode 100644
index 0000000..46c7299
--- /dev/null
+++ b/web/src/assets/icons/feather/save.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/scissors.svg b/web/src/assets/icons/feather/scissors.svg
new file mode 100644
index 0000000..fd0647f
--- /dev/null
+++ b/web/src/assets/icons/feather/scissors.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/search.svg b/web/src/assets/icons/feather/search.svg
new file mode 100644
index 0000000..8710306
--- /dev/null
+++ b/web/src/assets/icons/feather/search.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/send.svg b/web/src/assets/icons/feather/send.svg
new file mode 100644
index 0000000..42ef2a2
--- /dev/null
+++ b/web/src/assets/icons/feather/send.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/server.svg b/web/src/assets/icons/feather/server.svg
new file mode 100644
index 0000000..54ce094
--- /dev/null
+++ b/web/src/assets/icons/feather/server.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/settings.svg b/web/src/assets/icons/feather/settings.svg
new file mode 100644
index 0000000..19c2726
--- /dev/null
+++ b/web/src/assets/icons/feather/settings.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/share-2.svg b/web/src/assets/icons/feather/share-2.svg
new file mode 100644
index 0000000..09b1c7b
--- /dev/null
+++ b/web/src/assets/icons/feather/share-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/share.svg b/web/src/assets/icons/feather/share.svg
new file mode 100644
index 0000000..df38c14
--- /dev/null
+++ b/web/src/assets/icons/feather/share.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/shield-off.svg b/web/src/assets/icons/feather/shield-off.svg
new file mode 100644
index 0000000..18692dd
--- /dev/null
+++ b/web/src/assets/icons/feather/shield-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/shield.svg b/web/src/assets/icons/feather/shield.svg
new file mode 100644
index 0000000..c7c4841
--- /dev/null
+++ b/web/src/assets/icons/feather/shield.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/shopping-bag.svg b/web/src/assets/icons/feather/shopping-bag.svg
new file mode 100644
index 0000000..eaa39e8
--- /dev/null
+++ b/web/src/assets/icons/feather/shopping-bag.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/shopping-cart.svg b/web/src/assets/icons/feather/shopping-cart.svg
new file mode 100644
index 0000000..17a40bf
--- /dev/null
+++ b/web/src/assets/icons/feather/shopping-cart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/shuffle.svg b/web/src/assets/icons/feather/shuffle.svg
new file mode 100644
index 0000000..8cfb5db
--- /dev/null
+++ b/web/src/assets/icons/feather/shuffle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/sidebar.svg b/web/src/assets/icons/feather/sidebar.svg
new file mode 100644
index 0000000..8ba817e
--- /dev/null
+++ b/web/src/assets/icons/feather/sidebar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/skip-back.svg b/web/src/assets/icons/feather/skip-back.svg
new file mode 100644
index 0000000..88d024e
--- /dev/null
+++ b/web/src/assets/icons/feather/skip-back.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/skip-forward.svg b/web/src/assets/icons/feather/skip-forward.svg
new file mode 100644
index 0000000..f3fdac3
--- /dev/null
+++ b/web/src/assets/icons/feather/skip-forward.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/slack.svg b/web/src/assets/icons/feather/slack.svg
new file mode 100644
index 0000000..5d97346
--- /dev/null
+++ b/web/src/assets/icons/feather/slack.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/slash.svg b/web/src/assets/icons/feather/slash.svg
new file mode 100644
index 0000000..f4131b8
--- /dev/null
+++ b/web/src/assets/icons/feather/slash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/sliders.svg b/web/src/assets/icons/feather/sliders.svg
new file mode 100644
index 0000000..19c9385
--- /dev/null
+++ b/web/src/assets/icons/feather/sliders.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/smartphone.svg b/web/src/assets/icons/feather/smartphone.svg
new file mode 100644
index 0000000..0171a95
--- /dev/null
+++ b/web/src/assets/icons/feather/smartphone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/smile.svg b/web/src/assets/icons/feather/smile.svg
new file mode 100644
index 0000000..24dc8a2
--- /dev/null
+++ b/web/src/assets/icons/feather/smile.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/speaker.svg b/web/src/assets/icons/feather/speaker.svg
new file mode 100644
index 0000000..75d5ff9
--- /dev/null
+++ b/web/src/assets/icons/feather/speaker.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/square.svg b/web/src/assets/icons/feather/square.svg
new file mode 100644
index 0000000..6eabc77
--- /dev/null
+++ b/web/src/assets/icons/feather/square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/star.svg b/web/src/assets/icons/feather/star.svg
new file mode 100644
index 0000000..bcdc31a
--- /dev/null
+++ b/web/src/assets/icons/feather/star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/stop-circle.svg b/web/src/assets/icons/feather/stop-circle.svg
new file mode 100644
index 0000000..c10d9d4
--- /dev/null
+++ b/web/src/assets/icons/feather/stop-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/sun.svg b/web/src/assets/icons/feather/sun.svg
new file mode 100644
index 0000000..7f51b94
--- /dev/null
+++ b/web/src/assets/icons/feather/sun.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/sunrise.svg b/web/src/assets/icons/feather/sunrise.svg
new file mode 100644
index 0000000..eff4b1e
--- /dev/null
+++ b/web/src/assets/icons/feather/sunrise.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/sunset.svg b/web/src/assets/icons/feather/sunset.svg
new file mode 100644
index 0000000..a5a2221
--- /dev/null
+++ b/web/src/assets/icons/feather/sunset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/table.svg b/web/src/assets/icons/feather/table.svg
new file mode 100644
index 0000000..679bd57
--- /dev/null
+++ b/web/src/assets/icons/feather/table.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/tablet.svg b/web/src/assets/icons/feather/tablet.svg
new file mode 100644
index 0000000..9c80b40
--- /dev/null
+++ b/web/src/assets/icons/feather/tablet.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/tag.svg b/web/src/assets/icons/feather/tag.svg
new file mode 100644
index 0000000..7219b15
--- /dev/null
+++ b/web/src/assets/icons/feather/tag.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/target.svg b/web/src/assets/icons/feather/target.svg
new file mode 100644
index 0000000..be84b17
--- /dev/null
+++ b/web/src/assets/icons/feather/target.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/terminal.svg b/web/src/assets/icons/feather/terminal.svg
new file mode 100644
index 0000000..af459c0
--- /dev/null
+++ b/web/src/assets/icons/feather/terminal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/thermometer.svg b/web/src/assets/icons/feather/thermometer.svg
new file mode 100644
index 0000000..33142cc
--- /dev/null
+++ b/web/src/assets/icons/feather/thermometer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/thumbs-down.svg b/web/src/assets/icons/feather/thumbs-down.svg
new file mode 100644
index 0000000..3e7bcd6
--- /dev/null
+++ b/web/src/assets/icons/feather/thumbs-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/thumbs-up.svg b/web/src/assets/icons/feather/thumbs-up.svg
new file mode 100644
index 0000000..226c44d
--- /dev/null
+++ b/web/src/assets/icons/feather/thumbs-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/toggle-left.svg b/web/src/assets/icons/feather/toggle-left.svg
new file mode 100644
index 0000000..240be29
--- /dev/null
+++ b/web/src/assets/icons/feather/toggle-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/toggle-right.svg b/web/src/assets/icons/feather/toggle-right.svg
new file mode 100644
index 0000000..fc6e81c
--- /dev/null
+++ b/web/src/assets/icons/feather/toggle-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/tool.svg b/web/src/assets/icons/feather/tool.svg
new file mode 100644
index 0000000..f3cbf3d
--- /dev/null
+++ b/web/src/assets/icons/feather/tool.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/trash-2.svg b/web/src/assets/icons/feather/trash-2.svg
new file mode 100644
index 0000000..f24d55b
--- /dev/null
+++ b/web/src/assets/icons/feather/trash-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/trash.svg b/web/src/assets/icons/feather/trash.svg
new file mode 100644
index 0000000..55650bd
--- /dev/null
+++ b/web/src/assets/icons/feather/trash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/trello.svg b/web/src/assets/icons/feather/trello.svg
new file mode 100644
index 0000000..b2f599b
--- /dev/null
+++ b/web/src/assets/icons/feather/trello.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/trending-down.svg b/web/src/assets/icons/feather/trending-down.svg
new file mode 100644
index 0000000..a9d4cfa
--- /dev/null
+++ b/web/src/assets/icons/feather/trending-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/trending-up.svg b/web/src/assets/icons/feather/trending-up.svg
new file mode 100644
index 0000000..52026a4
--- /dev/null
+++ b/web/src/assets/icons/feather/trending-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/triangle.svg b/web/src/assets/icons/feather/triangle.svg
new file mode 100644
index 0000000..274b652
--- /dev/null
+++ b/web/src/assets/icons/feather/triangle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/truck.svg b/web/src/assets/icons/feather/truck.svg
new file mode 100644
index 0000000..3389837
--- /dev/null
+++ b/web/src/assets/icons/feather/truck.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/tv.svg b/web/src/assets/icons/feather/tv.svg
new file mode 100644
index 0000000..955bbff
--- /dev/null
+++ b/web/src/assets/icons/feather/tv.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/twitch.svg b/web/src/assets/icons/feather/twitch.svg
new file mode 100644
index 0000000..1706249
--- /dev/null
+++ b/web/src/assets/icons/feather/twitch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/twitter.svg b/web/src/assets/icons/feather/twitter.svg
new file mode 100644
index 0000000..f8886ec
--- /dev/null
+++ b/web/src/assets/icons/feather/twitter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/type.svg b/web/src/assets/icons/feather/type.svg
new file mode 100644
index 0000000..c6b2de3
--- /dev/null
+++ b/web/src/assets/icons/feather/type.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/umbrella.svg b/web/src/assets/icons/feather/umbrella.svg
new file mode 100644
index 0000000..dc77c0c
--- /dev/null
+++ b/web/src/assets/icons/feather/umbrella.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/underline.svg b/web/src/assets/icons/feather/underline.svg
new file mode 100644
index 0000000..044945d
--- /dev/null
+++ b/web/src/assets/icons/feather/underline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/unlock.svg b/web/src/assets/icons/feather/unlock.svg
new file mode 100644
index 0000000..01dc359
--- /dev/null
+++ b/web/src/assets/icons/feather/unlock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/upload-cloud.svg b/web/src/assets/icons/feather/upload-cloud.svg
new file mode 100644
index 0000000..a1db297
--- /dev/null
+++ b/web/src/assets/icons/feather/upload-cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/upload.svg b/web/src/assets/icons/feather/upload.svg
new file mode 100644
index 0000000..91eaff7
--- /dev/null
+++ b/web/src/assets/icons/feather/upload.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/user-check.svg b/web/src/assets/icons/feather/user-check.svg
new file mode 100644
index 0000000..42f91b2
--- /dev/null
+++ b/web/src/assets/icons/feather/user-check.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/user-minus.svg b/web/src/assets/icons/feather/user-minus.svg
new file mode 100644
index 0000000..44b75f5
--- /dev/null
+++ b/web/src/assets/icons/feather/user-minus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/user-plus.svg b/web/src/assets/icons/feather/user-plus.svg
new file mode 100644
index 0000000..21460f6
--- /dev/null
+++ b/web/src/assets/icons/feather/user-plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/user-x.svg b/web/src/assets/icons/feather/user-x.svg
new file mode 100644
index 0000000..0c41a48
--- /dev/null
+++ b/web/src/assets/icons/feather/user-x.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/user.svg b/web/src/assets/icons/feather/user.svg
new file mode 100644
index 0000000..7bb5f29
--- /dev/null
+++ b/web/src/assets/icons/feather/user.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/users.svg b/web/src/assets/icons/feather/users.svg
new file mode 100644
index 0000000..aacf6b0
--- /dev/null
+++ b/web/src/assets/icons/feather/users.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/video-off.svg b/web/src/assets/icons/feather/video-off.svg
new file mode 100644
index 0000000..08ec697
--- /dev/null
+++ b/web/src/assets/icons/feather/video-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/video.svg b/web/src/assets/icons/feather/video.svg
new file mode 100644
index 0000000..8ff156a
--- /dev/null
+++ b/web/src/assets/icons/feather/video.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/voicemail.svg b/web/src/assets/icons/feather/voicemail.svg
new file mode 100644
index 0000000..5d78a8e
--- /dev/null
+++ b/web/src/assets/icons/feather/voicemail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/volume-1.svg b/web/src/assets/icons/feather/volume-1.svg
new file mode 100644
index 0000000..150e875
--- /dev/null
+++ b/web/src/assets/icons/feather/volume-1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/volume-2.svg b/web/src/assets/icons/feather/volume-2.svg
new file mode 100644
index 0000000..03d521c
--- /dev/null
+++ b/web/src/assets/icons/feather/volume-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/volume-x.svg b/web/src/assets/icons/feather/volume-x.svg
new file mode 100644
index 0000000..be44240
--- /dev/null
+++ b/web/src/assets/icons/feather/volume-x.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/volume.svg b/web/src/assets/icons/feather/volume.svg
new file mode 100644
index 0000000..53bfe15
--- /dev/null
+++ b/web/src/assets/icons/feather/volume.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/watch.svg b/web/src/assets/icons/feather/watch.svg
new file mode 100644
index 0000000..a1099da
--- /dev/null
+++ b/web/src/assets/icons/feather/watch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/wifi-off.svg b/web/src/assets/icons/feather/wifi-off.svg
new file mode 100644
index 0000000..35eae43
--- /dev/null
+++ b/web/src/assets/icons/feather/wifi-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/wifi.svg b/web/src/assets/icons/feather/wifi.svg
new file mode 100644
index 0000000..748c285
--- /dev/null
+++ b/web/src/assets/icons/feather/wifi.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/wind.svg b/web/src/assets/icons/feather/wind.svg
new file mode 100644
index 0000000..82b3646
--- /dev/null
+++ b/web/src/assets/icons/feather/wind.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/x-circle.svg b/web/src/assets/icons/feather/x-circle.svg
new file mode 100644
index 0000000..94aad5e
--- /dev/null
+++ b/web/src/assets/icons/feather/x-circle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/x-octagon.svg b/web/src/assets/icons/feather/x-octagon.svg
new file mode 100644
index 0000000..8543198
--- /dev/null
+++ b/web/src/assets/icons/feather/x-octagon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/x-square.svg b/web/src/assets/icons/feather/x-square.svg
new file mode 100644
index 0000000..7677c38
--- /dev/null
+++ b/web/src/assets/icons/feather/x-square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/x.svg b/web/src/assets/icons/feather/x.svg
new file mode 100644
index 0000000..7d5875c
--- /dev/null
+++ b/web/src/assets/icons/feather/x.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/youtube.svg b/web/src/assets/icons/feather/youtube.svg
new file mode 100644
index 0000000..c482438
--- /dev/null
+++ b/web/src/assets/icons/feather/youtube.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/zap-off.svg b/web/src/assets/icons/feather/zap-off.svg
new file mode 100644
index 0000000..c636f8b
--- /dev/null
+++ b/web/src/assets/icons/feather/zap-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/zap.svg b/web/src/assets/icons/feather/zap.svg
new file mode 100644
index 0000000..8fdafa9
--- /dev/null
+++ b/web/src/assets/icons/feather/zap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/zoom-in.svg b/web/src/assets/icons/feather/zoom-in.svg
new file mode 100644
index 0000000..da4572d
--- /dev/null
+++ b/web/src/assets/icons/feather/zoom-in.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/feather/zoom-out.svg b/web/src/assets/icons/feather/zoom-out.svg
new file mode 100644
index 0000000..fd678d7
--- /dev/null
+++ b/web/src/assets/icons/feather/zoom-out.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/icons/isme/apifox.svg b/web/src/assets/icons/isme/apifox.svg
new file mode 100644
index 0000000..009cdf5
--- /dev/null
+++ b/web/src/assets/icons/isme/apifox.svg
@@ -0,0 +1,16 @@
+
diff --git a/web/src/assets/icons/isme/awesome.svg b/web/src/assets/icons/isme/awesome.svg
new file mode 100644
index 0000000..cfaab8b
--- /dev/null
+++ b/web/src/assets/icons/isme/awesome.svg
@@ -0,0 +1 @@
+
diff --git a/web/src/assets/icons/isme/dialog.svg b/web/src/assets/icons/isme/dialog.svg
new file mode 100644
index 0000000..809330d
--- /dev/null
+++ b/web/src/assets/icons/isme/dialog.svg
@@ -0,0 +1 @@
+
diff --git a/web/src/assets/icons/isme/docs.svg b/web/src/assets/icons/isme/docs.svg
new file mode 100644
index 0000000..907ac11
--- /dev/null
+++ b/web/src/assets/icons/isme/docs.svg
@@ -0,0 +1,15 @@
+
diff --git a/web/src/assets/icons/isme/gitee.svg b/web/src/assets/icons/isme/gitee.svg
new file mode 100644
index 0000000..5896c65
--- /dev/null
+++ b/web/src/assets/icons/isme/gitee.svg
@@ -0,0 +1 @@
+
diff --git a/web/src/assets/icons/isme/naiveui.svg b/web/src/assets/icons/isme/naiveui.svg
new file mode 100644
index 0000000..c236f7f
--- /dev/null
+++ b/web/src/assets/icons/isme/naiveui.svg
@@ -0,0 +1,37 @@
+
diff --git a/web/src/assets/images/404.webp b/web/src/assets/images/404.webp
new file mode 100644
index 0000000..3482bf9
Binary files /dev/null and b/web/src/assets/images/404.webp differ
diff --git a/web/src/assets/images/isme.png b/web/src/assets/images/isme.png
new file mode 100644
index 0000000..6a2acca
Binary files /dev/null and b/web/src/assets/images/isme.png differ
diff --git a/web/src/assets/images/login_banner.webp b/web/src/assets/images/login_banner.webp
new file mode 100644
index 0000000..2bc7cad
Binary files /dev/null and b/web/src/assets/images/login_banner.webp differ
diff --git a/web/src/assets/images/login_bg.webp b/web/src/assets/images/login_bg.webp
new file mode 100644
index 0000000..2a9df19
Binary files /dev/null and b/web/src/assets/images/login_bg.webp differ
diff --git a/web/src/assets/images/logo.png b/web/src/assets/images/logo.png
new file mode 100644
index 0000000..187ff0b
Binary files /dev/null and b/web/src/assets/images/logo.png differ
diff --git a/web/src/components/common/AppCard.vue b/web/src/components/common/AppCard.vue
new file mode 100644
index 0000000..358cf4c
--- /dev/null
+++ b/web/src/components/common/AppCard.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/components/common/AppPage.vue b/web/src/components/common/AppPage.vue
new file mode 100644
index 0000000..31881b8
--- /dev/null
+++ b/web/src/components/common/AppPage.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/components/common/CommonPage.vue b/web/src/components/common/CommonPage.vue
new file mode 100644
index 0000000..1e3c423
--- /dev/null
+++ b/web/src/components/common/CommonPage.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 返回
+
+
+
+
+ {{ title ?? route.meta?.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/components/common/LayoutSetting.vue b/web/src/components/common/LayoutSetting.vue
new file mode 100644
index 0000000..137493b
--- /dev/null
+++ b/web/src/components/common/LayoutSetting.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+ 布局设置
+
+
+
+
+
+
+
+
+
+
+
+ 注: 此设置仅对未设置layout或者设置成跟随系统的页面有效,菜单设置的layout优先级最高
+
+
+
+
+
+
diff --git a/web/src/components/common/TheFooter.vue b/web/src/components/common/TheFooter.vue
new file mode 100644
index 0000000..e2cf27e
--- /dev/null
+++ b/web/src/components/common/TheFooter.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/web/src/components/common/TheLogo.vue b/web/src/components/common/TheLogo.vue
new file mode 100644
index 0000000..e5a8420
--- /dev/null
+++ b/web/src/components/common/TheLogo.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/web/src/components/common/ThemeSetting.vue b/web/src/components/common/ThemeSetting.vue
new file mode 100644
index 0000000..55c5cd1
--- /dev/null
+++ b/web/src/components/common/ThemeSetting.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+ 设置主题色
+
+
+
+
diff --git a/web/src/components/common/ToggleTheme.vue b/web/src/components/common/ToggleTheme.vue
new file mode 100644
index 0000000..ca97e19
--- /dev/null
+++ b/web/src/components/common/ToggleTheme.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
diff --git a/web/src/components/common/index.js b/web/src/components/common/index.js
new file mode 100644
index 0000000..14e66b2
--- /dev/null
+++ b/web/src/components/common/index.js
@@ -0,0 +1,6 @@
+export { default as AppCard } from './AppCard.vue'
+export { default as TheFooter } from './TheFooter.vue'
+export { default as AppPage } from './AppPage.vue'
+export { default as CommonPage } from './CommonPage.vue'
+export { default as LayoutSetting } from './LayoutSetting.vue'
+export { default as ToggleTheme } from './ToggleTheme.vue'
diff --git a/web/src/components/index.js b/web/src/components/index.js
new file mode 100644
index 0000000..8621d75
--- /dev/null
+++ b/web/src/components/index.js
@@ -0,0 +1,2 @@
+export * from './common'
+export * from './me'
diff --git a/web/src/components/me/crud/QueryItem.vue b/web/src/components/me/crud/QueryItem.vue
new file mode 100644
index 0000000..01ba98f
--- /dev/null
+++ b/web/src/components/me/crud/QueryItem.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/components/me/crud/index.vue b/web/src/components/me/crud/index.vue
new file mode 100644
index 0000000..ae2e60a
--- /dev/null
+++ b/web/src/components/me/crud/index.vue
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/components/me/index.js b/web/src/components/me/index.js
new file mode 100644
index 0000000..795b613
--- /dev/null
+++ b/web/src/components/me/index.js
@@ -0,0 +1,3 @@
+export { default as MeModal } from './modal/index.vue'
+export { default as MeCrud } from './crud/index.vue'
+export { default as MeQueryItem } from './crud/QueryItem.vue'
diff --git a/web/src/components/me/modal/index.vue b/web/src/components/me/modal/index.vue
new file mode 100644
index 0000000..40b2585
--- /dev/null
+++ b/web/src/components/me/modal/index.vue
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/components/me/modal/utils.js b/web/src/components/me/modal/utils.js
new file mode 100644
index 0000000..dcba865
--- /dev/null
+++ b/web/src/components/me/modal/utils.js
@@ -0,0 +1,72 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2024/01/13 17:41:26
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+// 获取元素的CSS样式
+function getCss(element, key) {
+ return element.currentStyle
+ ? element.currentStyle[key]
+ : window.getComputedStyle(element, null)[key]
+}
+
+// 初始化拖拽
+export function initDrag(bar, box) {
+ if (!bar || !box)
+ return
+ const params = {
+ left: 0,
+ top: 0,
+ currentX: 0,
+ currentY: 0,
+ flag: false,
+ }
+
+ if (getCss(box, 'left') !== 'auto') {
+ params.left = getCss(box, 'left')
+ }
+ if (getCss(box, 'top') !== 'auto') {
+ params.top = getCss(box, 'top')
+ }
+
+ // 设置触发拖动元素的鼠标样式为移动图标
+ bar.style.cursor = 'move'
+ // 鼠标按下事件处理函数
+ bar.onmousedown = function (e) {
+ params.flag = true // 设置拖拽标志为true
+ e.preventDefault() // 阻止默认事件
+ params.currentX = e.clientX // 鼠标当前位置的X坐标
+ params.currentY = e.clientY // 鼠标当前位置的Y坐标
+ }
+ document.onmouseup = function () {
+ params.flag = false // 设置拖拽标志为false
+ if (getCss(box, 'left') !== 'auto') {
+ params.left = getCss(box, 'left')
+ }
+ if (getCss(box, 'top') !== 'auto') {
+ params.top = getCss(box, 'top')
+ }
+ }
+ document.onmousemove = function (e) {
+ if (e.target !== bar && !params.flag)
+ return
+
+ e.preventDefault() // 阻止默认事件
+ // 如果拖拽标志为true
+ if (params.flag) {
+ const nowX = e.clientX // 鼠标当前位置的X坐标
+ const nowY = e.clientY // 鼠标当前位置的Y坐标
+ const disX = nowX - params.currentX // 鼠标移动的X距离
+ const disY = nowY - params.currentY // 鼠标移动的Y距离
+
+ const left = Number.parseInt(params.left) + disX // 盒子元素的新left值
+ const top = Number.parseInt(params.top) + disY // 盒子元素的新top值
+
+ box.style.left = `${left}px`
+ box.style.top = `${top}px`
+ }
+ }
+}
diff --git a/web/src/composables/index.js b/web/src/composables/index.js
new file mode 100644
index 0000000..2e9b5a1
--- /dev/null
+++ b/web/src/composables/index.js
@@ -0,0 +1,4 @@
+export * from './useCrud'
+export * from './useForm'
+export * from './useModal'
+export * from './useAliveData'
diff --git a/web/src/composables/useAliveData.js b/web/src/composables/useAliveData.js
new file mode 100644
index 0000000..bcc2f06
--- /dev/null
+++ b/web/src/composables/useAliveData.js
@@ -0,0 +1,30 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:22:28
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+const lastDataMap = new Map()
+export function useAliveData(initData = {}, key) {
+ key = key ?? useRoute().name
+ const lastData = lastDataMap.get(key)
+ const aliveData = ref(lastData || { ...initData })
+
+ watch(
+ aliveData,
+ (v) => {
+ lastDataMap.set(key, v)
+ },
+ { deep: true },
+ )
+
+ return {
+ aliveData,
+ reset() {
+ aliveData.value = { ...initData }
+ lastDataMap.delete(key)
+ },
+ }
+}
diff --git a/web/src/composables/useCrud.js b/web/src/composables/useCrud.js
new file mode 100644
index 0000000..d3c4425
--- /dev/null
+++ b/web/src/composables/useCrud.js
@@ -0,0 +1,127 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/12 09:03:00
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { useForm, useModal } from '.'
+
+const ACTIONS = {
+ view: '查看',
+ edit: '编辑',
+ add: '新增',
+}
+
+export function useCrud({ name, initForm = {}, doCreate, doDelete, doUpdate, refresh }) {
+ const modalAction = ref('')
+ const [modalRef, okLoading] = useModal()
+ const [modalFormRef, modalForm, validation] = useForm(initForm)
+
+ /** 新增 */
+ function handleAdd(row = {}, title) {
+ handleOpen({ action: 'add', title, row: { ...initForm, ...row } })
+ }
+
+ /** 修改 */
+ function handleEdit(row, title) {
+ handleOpen({ action: 'edit', title, row })
+ }
+
+ /** 查看 */
+ function handleView(row, title) {
+ handleOpen({ action: 'view', title, row })
+ }
+
+ /** 打开modal */
+ function handleOpen(options = {}) {
+ const { action, row, title, onOk } = options
+ modalAction.value = action
+ modalForm.value = { ...row }
+ modalRef.value?.open({
+ ...options,
+ async onOk() {
+ if (typeof onOk === 'function') {
+ return await onOk()
+ }
+ else {
+ return await handleSave()
+ }
+ },
+ title: title ?? (ACTIONS[modalAction.value] || '') + name,
+ })
+ }
+
+ /** 保存 */
+ async function handleSave(action) {
+ if (!action && !['edit', 'add'].includes(modalAction.value)) {
+ return false
+ }
+ await validation()
+ const actions = {
+ add: {
+ api: () => doCreate(modalForm.value),
+ cb: () => $message.success('新增成功'),
+ },
+ edit: {
+ api: () => doUpdate(modalForm.value),
+ cb: () => $message.success('保存成功'),
+ },
+ }
+
+ action = action || actions[modalAction.value]
+
+ try {
+ okLoading.value = true
+ const data = await action.api()
+ action.cb()
+ okLoading.value = false
+ data && refresh(data)
+ }
+ catch (error) {
+ okLoading.value = false
+ return false
+ }
+ }
+
+ /** 删除 */
+ function handleDelete(id, confirmOptions) {
+ if (!id && id !== 0)
+ return
+ const d = $dialog.warning({
+ content: '确定删除?',
+ title: '提示',
+ positiveText: '确定',
+ negativeText: '取消',
+ async onPositiveClick() {
+ try {
+ d.loading = true
+ const data = await doDelete(id)
+ $message.success('删除成功')
+ d.loading = false
+ refresh(data, true)
+ }
+ catch (error) {
+ d.loading = false
+ }
+ },
+ ...confirmOptions,
+ })
+ }
+
+ return {
+ modalRef,
+ modalFormRef,
+ modalAction,
+ modalForm,
+ okLoading,
+ validation,
+ handleAdd,
+ handleDelete,
+ handleEdit,
+ handleView,
+ handleOpen,
+ handleSave,
+ }
+}
diff --git a/web/src/composables/useForm.js b/web/src/composables/useForm.js
new file mode 100644
index 0000000..69f9d50
--- /dev/null
+++ b/web/src/composables/useForm.js
@@ -0,0 +1,23 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:22:43
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+export function useForm(initFormData = {}) {
+ const formRef = ref(null)
+ const formModel = ref({ ...initFormData })
+ const rules = {
+ required: {
+ required: true,
+ message: '此为必填项',
+ trigger: ['blur', 'change'],
+ },
+ }
+ const validation = () => {
+ return formRef.value?.validate()
+ }
+ return [formRef, formModel, validation, rules]
+}
diff --git a/web/src/composables/useModal.js b/web/src/composables/useModal.js
new file mode 100644
index 0000000..dc08216
--- /dev/null
+++ b/web/src/composables/useModal.js
@@ -0,0 +1,20 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:22:49
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+export function useModal() {
+ const modalRef = ref(null)
+ const okLoading = computed({
+ get() {
+ return modalRef.value?.okLoading
+ },
+ set(v) {
+ modalRef.value.okLoading = v
+ },
+ })
+ return [modalRef, okLoading]
+}
diff --git a/web/src/directives/index.js b/web/src/directives/index.js
new file mode 100644
index 0000000..f27a036
--- /dev/null
+++ b/web/src/directives/index.js
@@ -0,0 +1,23 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:23:01
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { router } from '@/router'
+
+const permission = {
+ mounted(el, binding) {
+ const currentRoute = unref(router.currentRoute)
+ const btns = currentRoute.meta?.btns?.map(item => item.code) || []
+ if (!btns.includes(binding.value)) {
+ el.remove()
+ }
+ },
+}
+
+export function setupDirectives(app) {
+ app.directive('permission', permission)
+}
diff --git a/web/src/layouts/components/BreadCrumb.vue b/web/src/layouts/components/BreadCrumb.vue
new file mode 100644
index 0000000..dadc9ad
--- /dev/null
+++ b/web/src/layouts/components/BreadCrumb.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+ {{ route.meta.title }}
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
diff --git a/web/src/layouts/components/Fullscreen.vue b/web/src/layouts/components/Fullscreen.vue
new file mode 100644
index 0000000..7ccc3d4
--- /dev/null
+++ b/web/src/layouts/components/Fullscreen.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/web/src/layouts/components/MenuCollapse.vue b/web/src/layouts/components/MenuCollapse.vue
new file mode 100644
index 0000000..1ec6d30
--- /dev/null
+++ b/web/src/layouts/components/MenuCollapse.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/components/RoleSelect.vue b/web/src/layouts/components/RoleSelect.vue
new file mode 100644
index 0000000..b839df4
--- /dev/null
+++ b/web/src/layouts/components/RoleSelect.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+ {{ role.name }}
+
+
+
+
+
+
+
+ 退出登录
+
+
+ 确认
+
+
+
+
+
+
+
diff --git a/web/src/layouts/components/SideLogo.vue b/web/src/layouts/components/SideLogo.vue
new file mode 100644
index 0000000..a9cfba0
--- /dev/null
+++ b/web/src/layouts/components/SideLogo.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
diff --git a/web/src/layouts/components/SideMenu.vue b/web/src/layouts/components/SideMenu.vue
new file mode 100644
index 0000000..a369658
--- /dev/null
+++ b/web/src/layouts/components/SideMenu.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/components/UserAvatar.vue b/web/src/layouts/components/UserAvatar.vue
new file mode 100644
index 0000000..84bf105
--- /dev/null
+++ b/web/src/layouts/components/UserAvatar.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+ {{ userStore.nickName ?? userStore.username }}
+ [{{ userStore.currentRole?.name }}]
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/components/index.js b/web/src/layouts/components/index.js
new file mode 100644
index 0000000..a9d0280
--- /dev/null
+++ b/web/src/layouts/components/index.js
@@ -0,0 +1,8 @@
+export { default as RoleSelect } from './RoleSelect.vue'
+export { default as UserAvatar } from './UserAvatar.vue'
+export { default as MenuCollapse } from './MenuCollapse.vue'
+export { default as BreadCrumb } from './BreadCrumb.vue'
+export { default as AppTab } from './tab/index.vue'
+export { default as SideLogo } from './SideLogo.vue'
+export { default as SideMenu } from './SideMenu.vue'
+export { default as Fullscreen } from './Fullscreen.vue'
diff --git a/web/src/layouts/components/tab/ContextMenu.vue b/web/src/layouts/components/tab/ContextMenu.vue
new file mode 100644
index 0000000..ceaaae9
--- /dev/null
+++ b/web/src/layouts/components/tab/ContextMenu.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
diff --git a/web/src/layouts/components/tab/index.vue b/web/src/layouts/components/tab/index.vue
new file mode 100644
index 0000000..0936732
--- /dev/null
+++ b/web/src/layouts/components/tab/index.vue
@@ -0,0 +1,99 @@
+
+
+
+
+ tabStore.removeTab(path)"
+ >
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/empty/index.vue b/web/src/layouts/empty/index.vue
new file mode 100644
index 0000000..31d7e98
--- /dev/null
+++ b/web/src/layouts/empty/index.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/web/src/layouts/full/header/index.vue b/web/src/layouts/full/header/index.vue
new file mode 100644
index 0000000..45850d7
--- /dev/null
+++ b/web/src/layouts/full/header/index.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/full/index.vue b/web/src/layouts/full/index.vue
new file mode 100644
index 0000000..24d8d86
--- /dev/null
+++ b/web/src/layouts/full/index.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/full/sidebar/index.vue b/web/src/layouts/full/sidebar/index.vue
new file mode 100644
index 0000000..030938e
--- /dev/null
+++ b/web/src/layouts/full/sidebar/index.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/normal/header/index.vue b/web/src/layouts/normal/header/index.vue
new file mode 100644
index 0000000..9590e8f
--- /dev/null
+++ b/web/src/layouts/normal/header/index.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/normal/index.vue b/web/src/layouts/normal/index.vue
new file mode 100644
index 0000000..12fb6de
--- /dev/null
+++ b/web/src/layouts/normal/index.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/normal/sidebar/index.vue b/web/src/layouts/normal/sidebar/index.vue
new file mode 100644
index 0000000..f6b71fb
--- /dev/null
+++ b/web/src/layouts/normal/sidebar/index.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/simple/index.vue b/web/src/layouts/simple/index.vue
new file mode 100644
index 0000000..ef192dd
--- /dev/null
+++ b/web/src/layouts/simple/index.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/layouts/simple/sidebar/index.vue b/web/src/layouts/simple/sidebar/index.vue
new file mode 100644
index 0000000..27a7e6f
--- /dev/null
+++ b/web/src/layouts/simple/sidebar/index.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/main.js b/web/src/main.js
new file mode 100644
index 0000000..ce2e92f
--- /dev/null
+++ b/web/src/main.js
@@ -0,0 +1,31 @@
+/**********************************
+ * @Description: 入口文件
+ * @FilePath: main.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:41:32
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import '@/styles/reset.css'
+import '@/styles/global.scss'
+import 'uno.css'
+
+import { createApp } from 'vue'
+import App from './App.vue'
+import { setupRouter } from './router'
+import { setupStore } from './store'
+import { setupNaiveDiscreteApi } from './utils'
+import { setupDirectives } from './directives'
+
+async function bootstrap() {
+ const app = createApp(App)
+ setupStore(app)
+ setupDirectives(app)
+ await setupRouter(app)
+ app.mount('#app')
+ setupNaiveDiscreteApi()
+}
+
+bootstrap()
diff --git a/web/src/router/basic-routes.js b/web/src/router/basic-routes.js
new file mode 100644
index 0000000..e8321ab
--- /dev/null
+++ b/web/src/router/basic-routes.js
@@ -0,0 +1,40 @@
+export const basicRoutes = [
+ {
+ name: 'Login',
+ path: '/login',
+ component: () => import('@/views/login/index.vue'),
+ meta: {
+ title: '登录页',
+ layout: 'empty',
+ },
+ },
+
+ {
+ name: 'Home',
+ path: '/',
+ component: () => import('@/views/home/index.vue'),
+ meta: {
+ title: '首页',
+ },
+ },
+
+ {
+ name: '404',
+ path: '/404',
+ component: () => import('@/views/error-page/404.vue'),
+ meta: {
+ title: '页面飞走了',
+ layout: 'empty',
+ },
+ },
+
+ {
+ name: '403',
+ path: '/403',
+ component: () => import('@/views/error-page/403.vue'),
+ meta: {
+ title: '没有权限',
+ layout: 'empty',
+ },
+ },
+]
diff --git a/web/src/router/guards/index.js b/web/src/router/guards/index.js
new file mode 100644
index 0000000..35f274b
--- /dev/null
+++ b/web/src/router/guards/index.js
@@ -0,0 +1,19 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:24:46
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { createPageLoadingGuard } from './page-loading-guard'
+import { createPageTitleGuard } from './page-title-guard'
+import { createPermissionGuard } from './permission-guard'
+import { createTabGuard } from './tab-guard'
+
+export function setupRouterGuards(router) {
+ createPageLoadingGuard(router)
+ createPermissionGuard(router)
+ createPageTitleGuard(router)
+ createTabGuard(router)
+}
diff --git a/web/src/router/guards/page-loading-guard.js b/web/src/router/guards/page-loading-guard.js
new file mode 100644
index 0000000..ba145fa
--- /dev/null
+++ b/web/src/router/guards/page-loading-guard.js
@@ -0,0 +1,23 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:24:53
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+export function createPageLoadingGuard(router) {
+ router.beforeEach(() => {
+ $loadingBar.start()
+ })
+
+ router.afterEach(() => {
+ setTimeout(() => {
+ $loadingBar.finish()
+ }, 200)
+ })
+
+ router.onError(() => {
+ $loadingBar.error()
+ })
+}
diff --git a/web/src/router/guards/page-title-guard.js b/web/src/router/guards/page-title-guard.js
new file mode 100644
index 0000000..96042f6
--- /dev/null
+++ b/web/src/router/guards/page-title-guard.js
@@ -0,0 +1,21 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:00
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+const baseTitle = import.meta.env.VITE_TITLE
+
+export function createPageTitleGuard(router) {
+ router.afterEach((to) => {
+ const pageTitle = to.meta?.title
+ if (pageTitle) {
+ document.title = `${pageTitle} | ${baseTitle}`
+ }
+ else {
+ document.title = baseTitle
+ }
+ })
+}
diff --git a/web/src/router/guards/permission-guard.js b/web/src/router/guards/permission-guard.js
new file mode 100644
index 0000000..b317a08
--- /dev/null
+++ b/web/src/router/guards/permission-guard.js
@@ -0,0 +1,56 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:07
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { useAuthStore, usePermissionStore, useUserStore } from '@/store'
+import api from '@/api'
+import { getPermissions, getUserInfo } from '@/store/helper'
+
+const WHITE_LIST = ['/login', '/404']
+export function createPermissionGuard(router) {
+ router.beforeEach(async (to) => {
+ const authStore = useAuthStore()
+ const token = authStore.accessToken
+
+ /** 没有token */
+ if (!token) {
+ if (WHITE_LIST.includes(to.path))
+ return true
+ return { path: 'login', query: { ...to.query, redirect: to.path } }
+ }
+
+ // 有token的情况
+ if (to.path === '/login')
+ return { path: '/' }
+ if (WHITE_LIST.includes(to.path))
+ return true
+
+ const userStore = useUserStore()
+ const permissionStore = usePermissionStore()
+ if (!userStore.userInfo) {
+ const [user, permissions] = await Promise.all([getUserInfo(), getPermissions()])
+ userStore.setUser(user)
+ permissionStore.setPermissions(permissions)
+ const routeComponents = import.meta.glob('@/views/**/*.vue')
+ permissionStore.accessRoutes.forEach((route) => {
+ route.component = routeComponents[route.component] || undefined
+ !router.hasRoute(route.name) && router.addRoute(route)
+ })
+ return { ...to, replace: true }
+ }
+
+ const routes = router.getRoutes()
+ if (routes.find(route => route.name === to.name))
+ return true
+
+ // 判断是无权限还是404
+ const { data: hasMenu } = await api.validateMenuPath(to.path)
+ return hasMenu
+ ? { name: '403', query: { path: to.fullPath }, state: { from: 'permission-guard' } }
+ : { name: '404', query: { path: to.fullPath } }
+ })
+}
diff --git a/web/src/router/guards/tab-guard.js b/web/src/router/guards/tab-guard.js
new file mode 100644
index 0000000..e74b652
--- /dev/null
+++ b/web/src/router/guards/tab-guard.js
@@ -0,0 +1,24 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:17
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { useTabStore } from '@/store'
+
+export const EXCLUDE_TAB = ['/404', '/403', '/login']
+
+export function createTabGuard(router) {
+ router.afterEach((to) => {
+ if (EXCLUDE_TAB.includes(to.path))
+ return
+ const tabStore = useTabStore()
+ const { name, fullPath: path } = to
+ const title = to.meta?.title
+ const icon = to.meta?.icon
+ const keepAlive = to.meta?.keepAlive
+ tabStore.addTab({ name, path, title, icon, keepAlive })
+ })
+}
diff --git a/web/src/router/index.js b/web/src/router/index.js
new file mode 100644
index 0000000..994157b
--- /dev/null
+++ b/web/src/router/index.js
@@ -0,0 +1,25 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:23
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
+import { setupRouterGuards } from './guards'
+import { basicRoutes } from './basic-routes'
+
+export const router = createRouter({
+ history:
+ import.meta.env.VITE_USE_HASH === 'true'
+ ? createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH || '/')
+ : createWebHistory(import.meta.env.VITE_PUBLIC_PATH || '/'),
+ routes: basicRoutes,
+ scrollBehavior: () => ({ left: 0, top: 0 }),
+})
+
+export async function setupRouter(app) {
+ app.use(router)
+ setupRouterGuards(router)
+}
diff --git a/web/src/settings.js b/web/src/settings.js
new file mode 100644
index 0000000..b337449
--- /dev/null
+++ b/web/src/settings.js
@@ -0,0 +1,77 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/13 20:54:36
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+export const defaultLayout = 'normal'
+
+export const defaultPrimaryColor = '#316C72'
+
+// 控制 LayoutSetting 组件是否可见
+export const layoutSettingVisible = true
+
+export const naiveThemeOverrides = {
+ common: {
+ primaryColor: '#316C72FF',
+ primaryColorHover: '#316C72E3',
+ primaryColorPressed: '#2B4C59FF',
+ primaryColorSuppl: '#316C72E3',
+ },
+}
+
+export const basePermissions = [
+ {
+ code: 'ExternalLink',
+ name: '外链(可内嵌打开)',
+ type: 'MENU',
+ icon: 'i-fe:external-link',
+ order: 98,
+ enable: true,
+ show: true,
+ children: [
+ {
+ code: 'ShowDocs',
+ name: '项目文档',
+ type: 'MENU',
+ path: 'https://docs.isme.top/web/#/624306705/188522224',
+ icon: 'i-me:docs',
+ order: 1,
+ enable: true,
+ show: true,
+ },
+ {
+ code: 'ApiFoxDocs',
+ name: '接口文档',
+ type: 'MENU',
+ path: 'https://apifox.com/apidoc/shared-ff4a4d32-c0d1-4caf-b0ee-6abc130f734a',
+ icon: 'i-me:apifox',
+ order: 2,
+ enable: true,
+ show: true,
+ },
+ {
+ code: 'NaiveUI',
+ name: 'Naive UI',
+ type: 'MENU',
+ path: 'https://www.naiveui.com/zh-CN/os-theme',
+ icon: 'i-me:naiveui',
+ order: 3,
+ enable: true,
+ show: true,
+ },
+ {
+ code: 'MyBlog',
+ name: '博客-掘金',
+ type: 'MENU',
+ path: 'https://juejin.cn/user/1961184475483255/posts',
+ icon: 'i-simple-icons:juejin',
+ order: 4,
+ enable: true,
+ show: true,
+ },
+ ],
+ },
+]
diff --git a/web/src/store/helper.js b/web/src/store/helper.js
new file mode 100644
index 0000000..dd8fd26
--- /dev/null
+++ b/web/src/store/helper.js
@@ -0,0 +1,30 @@
+import { basePermissions } from '@/settings'
+import api from '@/api'
+
+export async function getUserInfo() {
+ const res = await api.getUser()
+ const { id, username, profile, roles, currentRole } = res.data || {}
+ return {
+ id,
+ username,
+ avatar: profile?.avatar,
+ nickName: profile?.nickName,
+ gender: profile?.gender,
+ address: profile?.address,
+ email: profile?.email,
+ roles,
+ currentRole,
+ }
+}
+
+export async function getPermissions() {
+ let asyncPermissions = []
+ try {
+ const res = await api.getRolePermissions()
+ asyncPermissions = res?.data || []
+ }
+ catch (error) {
+ console.error(error)
+ }
+ return basePermissions.concat(asyncPermissions)
+}
diff --git a/web/src/store/index.js b/web/src/store/index.js
new file mode 100644
index 0000000..a193337
--- /dev/null
+++ b/web/src/store/index.js
@@ -0,0 +1,18 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:26:15
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+
+export function setupStore(app) {
+ const pinia = createPinia()
+ pinia.use(piniaPluginPersistedstate)
+ app.use(pinia)
+}
+
+export * from './modules'
diff --git a/web/src/store/modules/app.js b/web/src/store/modules/app.js
new file mode 100644
index 0000000..d16aca3
--- /dev/null
+++ b/web/src/store/modules/app.js
@@ -0,0 +1,56 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:31
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { defineStore } from 'pinia'
+import { useDark } from '@vueuse/core'
+import { generate, getRgbStr } from '@arco-design/color'
+import { defaultLayout, defaultPrimaryColor, naiveThemeOverrides } from '@/settings'
+
+export const useAppStore = defineStore('app', {
+ state: () => ({
+ collapsed: false,
+ isDark: useDark(),
+ layout: defaultLayout,
+ primaryColor: defaultPrimaryColor,
+ naiveThemeOverrides,
+ }),
+ actions: {
+ switchCollapsed() {
+ this.collapsed = !this.collapsed
+ },
+ setCollapsed(b) {
+ this.collapsed = b
+ },
+ toggleDark() {
+ this.isDark = !this.isDark
+ },
+ setLayout(v) {
+ this.layout = v
+ },
+ setPrimaryColor(color) {
+ this.primaryColor = color
+ },
+ setThemeColor(color = this.primaryColor, isDark = this.isDark) {
+ const colors = generate(color, {
+ list: true,
+ dark: isDark,
+ })
+ document.body.style.setProperty('--primary-color', getRgbStr(colors[5]))
+ this.naiveThemeOverrides.common = Object.assign(this.naiveThemeOverrides.common || {}, {
+ primaryColor: colors[5],
+ primaryColorHover: colors[4],
+ primaryColorSuppl: colors[4],
+ primaryColorPressed: colors[6],
+ })
+ },
+ },
+ persist: {
+ paths: ['collapsed', 'layout', 'primaryColor', 'naiveThemeOverrides'],
+ storage: sessionStorage,
+ },
+})
diff --git a/web/src/store/modules/auth.js b/web/src/store/modules/auth.js
new file mode 100644
index 0000000..aea8f0e
--- /dev/null
+++ b/web/src/store/modules/auth.js
@@ -0,0 +1,59 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:39
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { defineStore } from 'pinia'
+import { usePermissionStore, useRouterStore, useTabStore, useUserStore } from '@/store'
+
+export const useAuthStore = defineStore('auth', {
+ state: () => ({
+ accessToken: undefined,
+ }),
+ actions: {
+ setToken({ accessToken }) {
+ this.accessToken = accessToken
+ },
+ resetToken() {
+ this.$reset()
+ },
+ toLogin() {
+ const { router, route } = useRouterStore()
+ router.replace({
+ path: '/login',
+ query: route.query,
+ })
+ },
+ async switchCurrentRole(data) {
+ this.resetLoginState()
+ await nextTick()
+ this.setToken(data)
+ },
+ resetLoginState() {
+ const { resetUser } = useUserStore()
+ const { resetRouter } = useRouterStore()
+ const { resetPermission, accessRoutes } = usePermissionStore()
+ const { resetTabs } = useTabStore()
+ // 重置路由
+ resetRouter(accessRoutes)
+ // 重置用户
+ resetUser()
+ // 重置权限
+ resetPermission()
+ // 重置Tabs
+ resetTabs()
+ // 重置token
+ this.resetToken()
+ },
+ async logout() {
+ this.resetLoginState()
+ this.toLogin()
+ },
+ },
+ persist: {
+ key: 'vue-naivue-admin_auth',
+ },
+})
diff --git a/web/src/store/modules/index.js b/web/src/store/modules/index.js
new file mode 100644
index 0000000..9672b79
--- /dev/null
+++ b/web/src/store/modules/index.js
@@ -0,0 +1,6 @@
+export * from './app'
+export * from './auth'
+export * from './permission'
+export * from './tab'
+export * from './user'
+export * from './router'
diff --git a/web/src/store/modules/permission.js b/web/src/store/modules/permission.js
new file mode 100644
index 0000000..b61a039
--- /dev/null
+++ b/web/src/store/modules/permission.js
@@ -0,0 +1,82 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:47
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { hyphenate } from '@vueuse/core'
+import { defineStore } from 'pinia'
+import { isExternal } from '@/utils'
+
+export const usePermissionStore = defineStore('permission', {
+ state: () => ({
+ accessRoutes: [],
+ permissions: [],
+ menus: [],
+ }),
+ actions: {
+ setPermissions(permissions) {
+ this.permissions = permissions
+ this.menus = this.permissions
+ .filter(item => item.type === 'MENU')
+ .map(item => this.getMenuItem(item))
+ .filter(item => !!item)
+ .sort((a, b) => a.order - b.order)
+ },
+ getMenuItem(item, parent) {
+ const route = this.generateRoute(item, item.show ? null : parent?.key)
+ if (item.enable && route.path && !route.path.startsWith('http'))
+ this.accessRoutes.push(route)
+ if (!item.show)
+ return null
+ const menuItem = {
+ label: route.meta.title,
+ key: route.name,
+ path: route.path,
+ originPath: route.meta.originPath,
+ icon: () => h('i', { class: `${route.meta.icon} text-16` }),
+ order: item.order ?? 0,
+ }
+ const children = item.children?.filter(item => item.type === 'MENU') || []
+ if (children.length) {
+ menuItem.children = children
+ .map(child => this.getMenuItem(child, menuItem))
+ .filter(item => !!item)
+ .sort((a, b) => a.order - b.order)
+ if (!menuItem.children.length)
+ delete menuItem.children
+ }
+ return menuItem
+ },
+ generateRoute(item, parentKey) {
+ let originPath
+ if (isExternal(item.path)) {
+ originPath = item.path
+ item.component = '/src/views/iframe/index.vue'
+ item.path = `/iframe/${hyphenate(item.code)}`
+ }
+ return {
+ name: item.code,
+ path: item.path,
+ redirect: item.redirect,
+ component: item.component,
+ meta: {
+ originPath,
+ icon: `${item.icon}?mask`,
+ title: item.name,
+ layout: item.layout,
+ keepAlive: !!item.keepAlive,
+ parentKey,
+ btns: item.children
+ ?.filter(item => item.type === 'BUTTON')
+ .map(item => ({ code: item.code, name: item.name })),
+ },
+ }
+ },
+ resetPermission() {
+ this.$reset()
+ },
+ },
+})
diff --git a/web/src/store/modules/router.js b/web/src/store/modules/router.js
new file mode 100644
index 0000000..51a5b59
--- /dev/null
+++ b/web/src/store/modules/router.js
@@ -0,0 +1,26 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2024/01/06 17:18:40
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { defineStore } from 'pinia'
+
+export const useRouterStore = defineStore('router', () => {
+ const router = useRouter()
+ const route = useRoute()
+
+ function resetRouter(accessRoutes) {
+ accessRoutes.forEach((item) => {
+ router.hasRoute(item.name) && router.removeRoute(item.name)
+ })
+ }
+
+ return {
+ router,
+ route,
+ resetRouter,
+ }
+})
diff --git a/web/src/store/modules/tab.js b/web/src/store/modules/tab.js
new file mode 100644
index 0000000..12c5e46
--- /dev/null
+++ b/web/src/store/modules/tab.js
@@ -0,0 +1,94 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:52
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { defineStore } from 'pinia'
+import { useRouterStore } from './router'
+
+export const useTabStore = defineStore('tab', {
+ state: () => ({
+ tabs: [],
+ activeTab: '',
+ reloading: false,
+ }),
+ getters: {
+ activeIndex() {
+ return this.tabs.findIndex(item => item.path === this.activeTab)
+ },
+ },
+ actions: {
+ async setActiveTab(path) {
+ await nextTick() // tab栏dom更新完再设置激活,让tab栏定位到新增的tab上生效
+ this.activeTab = path
+ },
+ setTabs(tabs) {
+ this.tabs = tabs
+ },
+ addTab(tab = {}) {
+ const findIndex = this.tabs.findIndex(item => item.path === tab.path)
+ if (findIndex !== -1) {
+ this.tabs.splice(findIndex, 1, tab)
+ }
+ else {
+ this.setTabs([...this.tabs, tab])
+ }
+ this.setActiveTab(tab.path)
+ },
+ async reloadTab(path, keepAlive) {
+ const findItem = this.tabs.find(item => item.path === path)
+ if (!findItem)
+ return
+ // 更新key可让keepAlive失效
+ if (keepAlive)
+ findItem.keepAlive = false
+ $loadingBar.start()
+ this.reloading = true
+ await nextTick()
+ this.reloading = false
+ findItem.keepAlive = !!keepAlive
+ setTimeout(() => {
+ document.documentElement.scrollTo({ left: 0, top: 0 })
+ $loadingBar.finish()
+ }, 100)
+ },
+ async removeTab(path) {
+ this.setTabs(this.tabs.filter(tab => tab.path !== path))
+ if (path === this.activeTab) {
+ useRouterStore().router?.push(this.tabs[this.tabs.length - 1].path)
+ }
+ },
+ removeOther(curPath = this.activeTab) {
+ this.setTabs(this.tabs.filter(tab => tab.path === curPath))
+ if (curPath !== this.activeTab) {
+ useRouterStore().router?.push(this.tabs[this.tabs.length - 1].path)
+ }
+ },
+ removeLeft(curPath) {
+ const curIndex = this.tabs.findIndex(item => item.path === curPath)
+ const filterTabs = this.tabs.filter((item, index) => index >= curIndex)
+ this.setTabs(filterTabs)
+ if (!filterTabs.find(item => item.path === this.activeTab)) {
+ useRouterStore().router?.push(filterTabs[filterTabs.length - 1].path)
+ }
+ },
+ removeRight(curPath) {
+ const curIndex = this.tabs.findIndex(item => item.path === curPath)
+ const filterTabs = this.tabs.filter((item, index) => index <= curIndex)
+ this.setTabs(filterTabs)
+ if (!filterTabs.find(item => item.path === this.activeTab.value)) {
+ useRouterStore().router?.push(filterTabs[filterTabs.length - 1].path)
+ }
+ },
+ resetTabs() {
+ this.$reset()
+ },
+ },
+ persist: {
+ paths: ['tabs'],
+ storage: sessionStorage,
+ },
+})
diff --git a/web/src/store/modules/user.js b/web/src/store/modules/user.js
new file mode 100644
index 0000000..c434737
--- /dev/null
+++ b/web/src/store/modules/user.js
@@ -0,0 +1,43 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:25:59
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { defineStore } from 'pinia'
+
+export const useUserStore = defineStore('user', {
+ state: () => ({
+ userInfo: null,
+ }),
+ getters: {
+ userId() {
+ return this.userInfo?.id
+ },
+ username() {
+ return this.userInfo?.username
+ },
+ nickName() {
+ return this.userInfo?.nickName
+ },
+ avatar() {
+ return this.userInfo?.avatar
+ },
+ currentRole() {
+ return this.userInfo?.currentRole || {}
+ },
+ roles() {
+ return this.userInfo?.roles || []
+ },
+ },
+ actions: {
+ setUser(user) {
+ this.userInfo = user
+ },
+ resetUser() {
+ this.$reset()
+ },
+ },
+})
diff --git a/web/src/styles/global.scss b/web/src/styles/global.scss
new file mode 100644
index 0000000..857043c
--- /dev/null
+++ b/web/src/styles/global.scss
@@ -0,0 +1,91 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:26:28
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+html,
+body {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+#app {
+ width: 100%;
+ height: 100%;
+}
+
+/* transition fade-slide */
+.fade-slide-leave-active,
+.fade-slide-enter-active {
+ transition: all 0.3s;
+}
+
+.fade-slide-enter-from {
+ opacity: 0;
+ transform: translateX(-30px);
+}
+
+.fade-slide-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
+
+/* 自定义滚动条样式 */
+.cus-scroll {
+ overflow: auto;
+ &::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+}
+.cus-scroll-x {
+ overflow-x: auto;
+ &::-webkit-scrollbar {
+ width: 0;
+ height: 8px;
+ }
+}
+.cus-scroll-y {
+ overflow-y: auto;
+ &::-webkit-scrollbar {
+ width: 8px;
+ height: 0;
+ }
+}
+.cus-scroll,
+.cus-scroll-x,
+.cus-scroll-y {
+ &::-webkit-scrollbar-thumb {
+ background-color: transparent;
+ border-radius: 4px;
+ }
+ &:hover {
+ &::-webkit-scrollbar-thumb {
+ background: #bfbfbf;
+ }
+ &::-webkit-scrollbar-thumb:hover {
+ background: rgb(var(--primary-color));
+ }
+ }
+}
+
+/* 切换主题的动画效果 */
+::view-transition-old(root),
+::view-transition-new(root) {
+ animation: none;
+ mix-blend-mode: normal;
+}
+
+::view-transition-old(root),
+.dark::view-transition-new(root) {
+ z-index: 1;
+}
+
+::view-transition-new(root),
+.dark::view-transition-old(root) {
+ z-index: 9999;
+}
diff --git a/web/src/styles/reset.css b/web/src/styles/reset.css
new file mode 100644
index 0000000..71447a2
--- /dev/null
+++ b/web/src/styles/reset.css
@@ -0,0 +1,49 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:26:38
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+html {
+ box-sizing: border-box;
+}
+
+*,
+::before,
+::after {
+ margin: 0;
+ padding: 0;
+ box-sizing: inherit;
+}
+
+a {
+ text-decoration: none;
+ color: inherit;
+}
+
+a:hover,
+a:link,
+a:visited,
+a:active {
+ text-decoration: none;
+}
+
+ol,
+ul {
+ list-style: none;
+}
+
+input,
+textarea {
+ outline: none;
+ border: none;
+ resize: none;
+}
+
+img,
+video {
+ max-width: 100%;
+ height: auto;
+}
diff --git a/web/src/utils/common.js b/web/src/utils/common.js
new file mode 100644
index 0000000..eccf9e9
--- /dev/null
+++ b/web/src/utils/common.js
@@ -0,0 +1,106 @@
+/**********************************
+ * @FilePath: common.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:45:46
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import dayjs from 'dayjs'
+
+/**
+ * @param {(object | string | number)} time
+ * @param {string} format
+ * @returns {string | null} 格式化后的时间字符串
+ *
+ */
+export function formatDateTime(time = undefined, format = 'YYYY-MM-DD HH:mm:ss') {
+ return dayjs(time).format(format)
+}
+
+export function formatDate(date = undefined, format = 'YYYY-MM-DD') {
+ return formatDateTime(date, format)
+}
+
+/**
+ * @param {Function} fn
+ * @param {number} wait
+ * @returns {Function} 节流函数
+ *
+ */
+export function throttle(fn, wait) {
+ let context, args
+ let previous = 0
+
+ return function (...argArr) {
+ const now = +new Date()
+ context = this
+ args = argArr
+ if (now - previous > wait) {
+ fn.apply(context, args)
+ previous = now
+ }
+ }
+}
+
+/**
+ * @param {Function} method
+ * @param {number} wait
+ * @param {boolean} immediate
+ * @return {*} 防抖函数
+ */
+export function debounce(method, wait, immediate) {
+ let timeout
+ return function (...args) {
+ const context = this
+ if (timeout) {
+ clearTimeout(timeout)
+ }
+ // 立即执行需要两个条件,一是immediate为true,二是timeout未被赋值或被置为null
+ if (immediate) {
+ /**
+ * 如果定时器不存在,则立即执行,并设置一个定时器,wait毫秒后将定时器置为null
+ * 这样确保立即执行后wait毫秒内不会被再次触发
+ */
+ const callNow = !timeout
+ timeout = setTimeout(() => {
+ timeout = null
+ }, wait)
+ if (callNow) {
+ method.apply(context, args)
+ }
+ }
+ else {
+ // 如果immediate为false,则函数wait毫秒后执行
+ timeout = setTimeout(() => {
+ /**
+ * args是一个类数组对象,所以使用fn.apply
+ * 也可写作method.call(context, ...args)
+ */
+ method.apply(context, args)
+ }, wait)
+ }
+ }
+}
+
+/**
+ * @param {number} time 毫秒数
+ * @returns 睡一会儿,让子弹暂停一下
+ */
+export function sleep(time) {
+ return new Promise(resolve => setTimeout(resolve, time))
+}
+
+/**
+ * @param {HTMLElement} el
+ * @param {Function} cb
+ * @return {ResizeObserver}
+ */
+export function useResize(el, cb) {
+ const observer = new ResizeObserver((entries) => {
+ cb(entries[0].contentRect)
+ })
+ observer.observe(el)
+ return observer
+}
diff --git a/web/src/utils/http/helpers.js b/web/src/utils/http/helpers.js
new file mode 100644
index 0000000..b7de8bb
--- /dev/null
+++ b/web/src/utils/http/helpers.js
@@ -0,0 +1,67 @@
+/**********************************
+ * @FilePath: helpers.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:46:22
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { useAuthStore } from '@/store'
+
+let isConfirming = false
+export function resolveResError(code, message, needTip = true) {
+ switch (code) {
+ case 401:
+ if (isConfirming || !needTip)
+ return
+ isConfirming = true
+ $dialog.confirm({
+ title: '提示',
+ type: 'info',
+ content: '登录已过期,是否重新登录?',
+ confirm() {
+ useAuthStore().logout()
+ window.$message?.success('已退出登录')
+ isConfirming = false
+ },
+ cancel() {
+ isConfirming = false
+ },
+ })
+ return false
+ case 11007:
+ case 11008:
+ if (isConfirming || !needTip)
+ return
+ isConfirming = true
+ $dialog.confirm({
+ title: '提示',
+ type: 'info',
+ content: `${message},是否重新登录?`,
+ confirm() {
+ useAuthStore().logout()
+ window.$message?.success('已退出登录')
+ isConfirming = false
+ },
+ cancel() {
+ isConfirming = false
+ },
+ })
+ return false
+ case 403:
+ message = '请求被拒绝'
+ break
+ case 404:
+ message = '请求资源或接口不存在'
+ break
+ case 500:
+ message = '服务器发生异常'
+ break
+ default:
+ message = message ?? `【${code}】: 未知异常!`
+ break
+ }
+ needTip && window.$message?.error(message)
+ return message
+}
diff --git a/web/src/utils/http/index.js b/web/src/utils/http/index.js
new file mode 100644
index 0000000..b0d5ca0
--- /dev/null
+++ b/web/src/utils/http/index.js
@@ -0,0 +1,30 @@
+/**********************************
+ * @FilePath: index.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:46:28
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import axios from 'axios'
+import { setupInterceptors } from './interceptors'
+
+export function createAxios(options = {}) {
+ const defaultOptions = {
+ baseURL: import.meta.env.VITE_AXIOS_BASE_URL,
+ timeout: 12000,
+ }
+ const service = axios.create({
+ ...defaultOptions,
+ ...options,
+ })
+ setupInterceptors(service)
+ return service
+}
+
+export const request = createAxios()
+
+export const mockRequest = createAxios({
+ baseURL: '/mock-api',
+})
diff --git a/web/src/utils/http/interceptors.js b/web/src/utils/http/interceptors.js
new file mode 100644
index 0000000..d048d3d
--- /dev/null
+++ b/web/src/utils/http/interceptors.js
@@ -0,0 +1,70 @@
+/**********************************
+ * @FilePath: interceptors.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:46:40
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { resolveResError } from './helpers'
+import { useAuthStore } from '@/store'
+
+export function setupInterceptors(axiosInstance) {
+ function reqResolve(config) {
+ // 处理不需要token的请求
+ if (config.needToken === false) {
+ return config
+ }
+
+ const { accessToken } = useAuthStore()
+ if (accessToken) {
+ // token: Bearer + xxx
+ config.headers.Authorization = `Bearer ${accessToken}`
+ }
+
+ return config
+ }
+
+ function reqReject(error) {
+ return Promise.reject(error)
+ }
+
+ const SUCCESS_CODES = [0, 200]
+ function resResolve(response) {
+ const { data, status, config, statusText, headers } = response
+ if (headers['content-type']?.includes('json')) {
+ if (SUCCESS_CODES.includes(data?.code)) {
+ return Promise.resolve(data)
+ }
+ const code = data?.code ?? status
+
+ const needTip = config?.needTip !== false
+
+ // 根据code处理对应的操作,并返回处理后的message
+ const message = resolveResError(code, data?.message ?? statusText, needTip)
+
+ return Promise.reject({ code, message, error: data ?? response })
+ }
+ return Promise.resolve(data ?? response)
+ }
+
+ async function resReject(error) {
+ if (!error || !error.response) {
+ const code = error?.code
+ /** 根据code处理对应的操作,并返回处理后的message */
+ const message = resolveResError(code, error.message)
+ return Promise.reject({ code, message, error })
+ }
+
+ const { data, status, config } = error.response
+ const code = data?.code ?? status
+
+ const needTip = config?.needTip !== false
+ const message = resolveResError(code, data?.message ?? error.message, needTip)
+ return Promise.reject({ code, message, error: error.response?.data || error.response })
+ }
+
+ axiosInstance.interceptors.request.use(reqResolve, reqReject)
+ axiosInstance.interceptors.response.use(resResolve, resReject)
+}
diff --git a/web/src/utils/index.js b/web/src/utils/index.js
new file mode 100644
index 0000000..a18642a
--- /dev/null
+++ b/web/src/utils/index.js
@@ -0,0 +1,14 @@
+/**********************************
+ * @FilePath: index.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:45:53
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+export * from './common'
+export * from './http'
+export * from './is'
+export * from './naiveTools'
+export * from './storage'
diff --git a/web/src/utils/is.js b/web/src/utils/is.js
new file mode 100644
index 0000000..6b9a814
--- /dev/null
+++ b/web/src/utils/is.js
@@ -0,0 +1,127 @@
+/**********************************
+ * @FilePath: is.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:45:32
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+const toString = Object.prototype.toString
+
+export function is(val, type) {
+ return toString.call(val) === `[object ${type}]`
+}
+
+export function isDef(val) {
+ return typeof val !== 'undefined'
+}
+
+export function isUndef(val) {
+ return typeof val === 'undefined'
+}
+
+export function isNull(val) {
+ return val === null
+}
+
+export function isWhitespace(val) {
+ return val === ''
+}
+
+export function isObject(val) {
+ return !isNull(val) && is(val, 'Object')
+}
+
+export function isArray(val) {
+ return val && Array.isArray(val)
+}
+
+export function isString(val) {
+ return is(val, 'String')
+}
+
+export function isNumber(val) {
+ return is(val, 'Number')
+}
+
+export function isBoolean(val) {
+ return is(val, 'Boolean')
+}
+
+export function isDate(val) {
+ return is(val, 'Date')
+}
+
+export function isRegExp(val) {
+ return is(val, 'RegExp')
+}
+
+export function isFunction(val) {
+ return typeof val === 'function'
+}
+
+export function isPromise(val) {
+ return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
+}
+
+export function isElement(val) {
+ return isObject(val) && !!val.tagName
+}
+
+export function isWindow(val) {
+ return typeof window !== 'undefined' && isDef(window) && is(val, 'Window')
+}
+
+export function isNullOrUndef(val) {
+ return isNull(val) || isUndef(val)
+}
+
+export function isNullOrWhitespace(val) {
+ return isNullOrUndef(val) || isWhitespace(val)
+}
+
+/** 空数组 | 空字符串 | 空对象 | 空Map | 空Set */
+export function isEmpty(val) {
+ if (isArray(val) || isString(val)) {
+ return val.length === 0
+ }
+
+ if (val instanceof Map || val instanceof Set) {
+ return val.size === 0
+ }
+
+ if (isObject(val)) {
+ return Object.keys(val).length === 0
+ }
+
+ return false
+}
+
+/**
+ * 类似mysql的IFNULL函数
+ *
+ * @param {number | boolean | string} val
+ * @param {number | boolean | string} def
+ * @returns 第一个参数为null | undefined | '' 则返回第二个参数作为备用值,否则返回第一个参数
+ */
+export function ifNull(val, def = '') {
+ return isNullOrWhitespace(val) ? def : val
+}
+
+export function isUrl(path) {
+ const reg = /^https?:\/\/[-\w+&@#/%?=~|!:,.;]+[-\w+&@#/%=~|]$/
+ return reg.test(path)
+}
+
+/**
+ * @param {string} path
+ * @returns {boolean} 是否是外部链接
+ */
+export function isExternal(path) {
+ return /^https?:|mailto:|tel:/.test(path)
+}
+
+export const isServer = typeof window === 'undefined'
+
+export const isClient = !isServer
diff --git a/web/src/utils/naiveTools.js b/web/src/utils/naiveTools.js
new file mode 100644
index 0000000..20efe5e
--- /dev/null
+++ b/web/src/utils/naiveTools.js
@@ -0,0 +1,121 @@
+/**********************************
+ * @FilePath: naiveTools.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:45:20
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import * as NaiveUI from 'naive-ui'
+import { isNullOrUndef } from '@/utils'
+import { useAppStore } from '@/store'
+
+export function setupMessage(NMessage) {
+ class Message {
+ static instance
+ constructor() {
+ // 单例模式
+ if (Message.instance)
+ return Message.instance
+ Message.instance = this
+ this.message = {}
+ this.removeTimer = {}
+ }
+
+ removeMessage(key, duration = 5000) {
+ this.removeTimer[key] && clearTimeout(this.removeTimer[key])
+ this.removeTimer[key] = setTimeout(() => {
+ this.message[key]?.destroy()
+ }, duration)
+ }
+
+ destroy(key, duration = 200) {
+ setTimeout(() => {
+ this.message[key]?.destroy()
+ }, duration)
+ }
+
+ showMessage(type, content, option = {}) {
+ if (Array.isArray(content)) {
+ return content.forEach(msg => NMessage[type](msg, option))
+ }
+
+ if (!option.key) {
+ return NMessage[type](content, option)
+ }
+
+ const currentMessage = this.message[option.key]
+ if (currentMessage) {
+ currentMessage.type = type
+ currentMessage.content = content
+ }
+ else {
+ this.message[option.key] = NMessage[type](content, {
+ ...option,
+ duration: 0,
+ onAfterLeave: () => {
+ delete this.message[option.key]
+ },
+ })
+ }
+ this.removeMessage(option.key, option.duration)
+ }
+
+ loading(content, option) {
+ this.showMessage('loading', content, option)
+ }
+
+ success(content, option) {
+ this.showMessage('success', content, option)
+ }
+
+ error(content, option) {
+ this.showMessage('error', content, option)
+ }
+
+ info(content, option) {
+ this.showMessage('info', content, option)
+ }
+
+ warning(content, option) {
+ this.showMessage('warning', content, option)
+ }
+ }
+
+ return new Message()
+}
+
+export function setupDialog(NDialog) {
+ NDialog.confirm = function (option = {}) {
+ const showIcon = !isNullOrUndef(option.title)
+ return NDialog[option.type || 'warning']({
+ showIcon,
+ positiveText: '确定',
+ negativeText: '取消',
+ onPositiveClick: option.confirm,
+ onNegativeClick: option.cancel,
+ onMaskClick: option.cancel,
+ ...option,
+ })
+ }
+
+ return NDialog
+}
+
+export function setupNaiveDiscreteApi() {
+ const appStore = useAppStore()
+ const configProviderProps = computed(() => ({
+ theme: appStore.isDark ? NaiveUI.darkTheme : undefined,
+ themeOverrides: useAppStore().naiveThemeOverrides,
+ }))
+ const { message, dialog, notification, loadingBar } = NaiveUI.createDiscreteApi(
+ ['message', 'dialog', 'notification', 'loadingBar'],
+ { configProviderProps },
+ )
+
+ window.$loadingBar = loadingBar
+ window.$notification = notification
+ window.$message = setupMessage(message)
+ window.$dialog = setupDialog(dialog)
+}
diff --git a/web/src/utils/storage/index.js b/web/src/utils/storage/index.js
new file mode 100644
index 0000000..f173867
--- /dev/null
+++ b/web/src/utils/storage/index.js
@@ -0,0 +1,30 @@
+/**********************************
+ * @FilePath: index.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:46:07
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { createStorage } from './storage'
+
+const prefixKey = 'vue-naive-admin_'
+
+export const createLocalStorage = function (option = {}) {
+ return createStorage({
+ prefixKey: option.prefixKey || '',
+ storage: localStorage,
+ })
+}
+
+export const createSessionStorage = function (option = {}) {
+ return createStorage({
+ prefixKey: option.prefixKey || '',
+ storage: sessionStorage,
+ })
+}
+
+export const lStorage = createLocalStorage({ prefixKey })
+
+export const sStorage = createSessionStorage({ prefixKey })
diff --git a/web/src/utils/storage/storage.js b/web/src/utils/storage/storage.js
new file mode 100644
index 0000000..6741e07
--- /dev/null
+++ b/web/src/utils/storage/storage.js
@@ -0,0 +1,66 @@
+/**********************************
+ * @FilePath: storage.js
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/04 22:46:13
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { isNullOrUndef } from '@/utils'
+
+class Storage {
+ constructor(option) {
+ this.storage = option.storage
+ this.prefixKey = option.prefixKey
+ }
+
+ getKey(key) {
+ return `${this.prefixKey}${key}`.toLowerCase()
+ }
+
+ set(key, value, expire) {
+ const stringData = JSON.stringify({
+ value,
+ time: Date.now(),
+ expire: !isNullOrUndef(expire) ? new Date().getTime() + expire * 1000 : null,
+ })
+ this.storage.setItem(this.getKey(key), stringData)
+ }
+
+ get(key) {
+ const { value } = this.getItem(key, {})
+ return value
+ }
+
+ getItem(key, def = null) {
+ const val = this.storage.getItem(this.getKey(key))
+ if (!val)
+ return def
+ try {
+ const data = JSON.parse(val)
+ const { value, time, expire } = data
+ if (isNullOrUndef(expire) || expire > new Date().getTime()) {
+ return { value, time }
+ }
+ this.remove(key)
+ return def
+ }
+ catch (error) {
+ this.remove(key)
+ return def
+ }
+ }
+
+ remove(key) {
+ this.storage.removeItem(this.getKey(key))
+ }
+
+ clear() {
+ this.storage.clear()
+ }
+}
+
+export function createStorage({ prefixKey = '', storage = sessionStorage }) {
+ return new Storage({ prefixKey, storage })
+}
diff --git a/web/src/views/base/index.vue b/web/src/views/base/index.vue
new file mode 100644
index 0000000..d768f38
--- /dev/null
+++ b/web/src/views/base/index.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+ Default
+
+ Tertiary
+
+
+ Primary
+
+
+ Info
+
+
+ Success
+
+
+ Warning
+
+
+ Error
+
+
+
+
+
+
+
+
+ 新增
+
+
+
+ 删除
+
+
+
+ 编辑
+
+
+
+ 查看
+
+
+
+
+
+
+
+
+
+ 信息
+
+
+ 成功
+
+
+ 警告
+
+
+ 错误
+
+
+
+
+
+
+
+ 删除
+
+
+
+
+
+
+
+ 登录
+
+
+ 多个错误提醒
+
+
+
+
+
+
+
+
diff --git a/web/src/views/base/keep-alive.vue b/web/src/views/base/keep-alive.vue
new file mode 100644
index 0000000..b268b7b
--- /dev/null
+++ b/web/src/views/base/keep-alive.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+ 注:右击标签重新加载可重置keep-alive
+
+
+
+
+
+
diff --git a/web/src/views/base/test-modal.vue b/web/src/views/base/test-modal.vue
new file mode 100644
index 0000000..2013550
--- /dev/null
+++ b/web/src/views/base/test-modal.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+ 打开第一个弹个窗
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
diff --git a/web/src/views/base/unocss-icon.vue b/web/src/views/base/unocss-icon.vue
new file mode 100644
index 0000000..8960d8d
--- /dev/null
+++ b/web/src/views/base/unocss-icon.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+ feather图标集 + isme自定义图标
+
+
+
+
+
+
+
diff --git a/web/src/views/base/unocss.vue b/web/src/views/base/unocss.vue
new file mode 100644
index 0000000..980d2f3
--- /dev/null
+++ b/web/src/views/base/unocss.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+ 文档:
+
+ https://uno.antfu.me/
+
+
+
+ playground:
+
+ https://unocss.antfu.me/play/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/demo/upload/index.vue b/web/src/views/demo/upload/index.vue
new file mode 100644
index 0000000..9145dd7
--- /dev/null
+++ b/web/src/views/demo/upload/index.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+ 点击或者拖动文件到该区域来上传
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ url
+
+
+ MD
+
+
+ img
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/error-page/403.vue b/web/src/views/error-page/403.vue
new file mode 100644
index 0000000..880c3de
--- /dev/null
+++ b/web/src/views/error-page/403.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+ 返回上一页
+
+
+ 返回首页
+
+
+
+
+
+
+
+
diff --git a/web/src/views/error-page/404.vue b/web/src/views/error-page/404.vue
new file mode 100644
index 0000000..322aeeb
--- /dev/null
+++ b/web/src/views/error-page/404.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+ 返回首页
+
+
+
+
+
+
+
+
diff --git a/web/src/views/home/index.vue b/web/src/views/home/index.vue
new file mode 100644
index 0000000..3b5211d
--- /dev/null
+++ b/web/src/views/home/index.vue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+
+ Hello, {{ userStore.nickName ?? userStore.username }}
+
+ 当前角色:{{ userStore.currentRole?.name }}
+
+
+
+
+ 一个人几乎可以在任何他怀有无限热忱的事情上成功。
+
+
+ —— 查尔斯·史考伯
+
+
+
+
+
+ isme.top
+
+
+
+
+ 这是一款极简风格的后台管理模板,包含前后端解决方案,前端使用 Vite + Vue3 + Pinia +
+ Unocss,后端使用 Nestjs + TypeOrm +
+ MySql,简单易用,赏心悦目,历经十几次重构和细节打磨,诚意满满!!
+
+
+
+
+
+
+
+ 👏 历经十几次重构和细节打磨
+
+
+
+ -
+ 🆒 使用
+ Vue3
+ 主流技术栈:
+ Vite + Vue3 + Pinia
+
+ -
+ 🍇 使用
+ 原子CSS
+ 框架:
+ Unocss
+ ,优雅、轻量、易用
+
+ -
+ 🤹 使用主流的
+ iconify + unocss
+ 图标方案,支持自定义图标,支持动态渲染
+
+ -
+ 🎨 使用 Naive UI,
+ 极致简洁的代码风格和清爽的页面设计
+ ,审美在线,主题轻松定制
+
+ -
+ 👏 先进且易于理解的文件结构设计,多个模块之间
+ 零耦合
+ ,单个业务模块删除不影响其他模块
+
+ -
+ 🚀
+ 扁平化路由
+ 设计,每一个组件都可以是一个页面,告别多级路由 KeepAlive 难实现问题
+
+
+ -
+ 🍒
+ 基于权限动态生成路由
+ ,无需额外定义路由,
+ 403和404可区分
+ ,而不是无权限也跳404
+
+ -
+ 🔐 基于Redis集成
+ 无感刷新
+ ,用户登录态可控,安全与体验缺一不可
+
+ -
+ ✨ 基于 Naive UI 封装
+ message
+ 全局工具方法,支持批量提醒,支持跨页面共享实例
+
+ -
+ ⚡️ 基于 Naive UI 封装常用的业务组件,包含
+ Page
+ 组件、
+ CRUD
+ 表格组件及
+ Modal
+ 组件,减少大量重复性工作
+
+
+
+
+
+ 👉点击
+
+ 更多
+
+ 查看更多实用功能,持续开发中...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/iframe/index.vue b/web/src/views/iframe/index.vue
new file mode 100644
index 0000000..8768865
--- /dev/null
+++ b/web/src/views/iframe/index.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/web/src/views/login/api.js b/web/src/views/login/api.js
new file mode 100644
index 0000000..9c4f939
--- /dev/null
+++ b/web/src/views/login/api.js
@@ -0,0 +1,15 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:28:30
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { request } from '@/utils'
+
+export default {
+ toggleRole: data => request.post('/auth/role/toggle', data),
+ login: data => request.post('/auth/login', data, { needToken: false }),
+ getUser: () => request.get('/user/detail'),
+}
diff --git a/web/src/views/login/index.vue b/web/src/views/login/index.vue
new file mode 100644
index 0000000..b1ef364
--- /dev/null
+++ b/web/src/views/login/index.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 一键体验
+
+
+
+ 登录
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/pms/resource/api.js b/web/src/views/pms/resource/api.js
new file mode 100644
index 0000000..4b60272
--- /dev/null
+++ b/web/src/views/pms/resource/api.js
@@ -0,0 +1,19 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2024/04/01 15:52:04
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import axios from 'axios'
+import { request } from '@/utils'
+
+export default {
+ getMenuTree: () => request.get('/permission/menu/tree'),
+ getButtons: ({ parentId }) => request.get(`/permission/button/${parentId}`),
+ getComponents: () => axios.get(`${import.meta.env.VITE_PUBLIC_PATH}components.json`),
+ addPermission: data => request.post('/permission', data),
+ savePermission: (id, data) => request.patch(`/permission/${id}`, data),
+ deletePermission: id => request.delete(`permission/${id}`),
+}
diff --git a/web/src/views/pms/resource/components/MenuTree.vue b/web/src/views/pms/resource/components/MenuTree.vue
new file mode 100644
index 0000000..1a2f74f
--- /dev/null
+++ b/web/src/views/pms/resource/components/MenuTree.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
+ 菜单
+
+
+
+
+ 新增
+
+
+
+
+
+
+
emit('refresh', data)" />
+
+
+
+
diff --git a/web/src/views/pms/resource/components/QuestionLabel.vue b/web/src/views/pms/resource/components/QuestionLabel.vue
new file mode 100644
index 0000000..cc2285d
--- /dev/null
+++ b/web/src/views/pms/resource/components/QuestionLabel.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+ {{ content }}
+
+ {{ label }}
+
+
+
+
diff --git a/web/src/views/pms/resource/components/ResAddOrEdit.vue b/web/src/views/pms/resource/components/ResAddOrEdit.vue
new file mode 100644
index 0000000..937de7e
--- /dev/null
+++ b/web/src/views/pms/resource/components/ResAddOrEdit.vue
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 显示
+
+
+ 隐藏
+
+
+
+
+
+
+
+
+
+ 启用
+
+
+ 禁用
+
+
+
+
+
+
+
+
+
+ 是
+
+
+ 否
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/pms/resource/index.vue b/web/src/views/pms/resource/index.vue
new file mode 100644
index 0000000..084b6ff
--- /dev/null
+++ b/web/src/views/pms/resource/index.vue
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ currentMenu.name }}
+
+
+
+ 编辑
+
+
+
+
+ {{ currentMenu.code }}
+
+
+ {{ currentMenu.name }}
+
+
+ {{ currentMenu.path ?? '--' }}
+
+
+ {{ currentMenu.component ?? '--' }}
+
+
+
+
+ {{ currentMenu.icon }}
+
+ 无
+
+
+ {{ currentMenu.layout || '跟随系统' }}
+
+
+ {{ currentMenu.show ? '是' : '否' }}
+
+
+ {{ currentMenu.enable ? '是' : '否' }}
+
+
+ {{ currentMenu.keepAlive ? '是' : '否' }}
+
+
+ {{ currentMenu.order ?? '--' }}
+
+
+
+
+
+ 按钮
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/pms/role/api.js b/web/src/views/pms/role/api.js
new file mode 100644
index 0000000..d18d97c
--- /dev/null
+++ b/web/src/views/pms/role/api.js
@@ -0,0 +1,21 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:29:27
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { request } from '@/utils'
+
+export default {
+ create: data => request.post('/role', data),
+ read: (params = {}) => request.get('/role/page', { params }),
+ update: data => request.patch(`/role/${data.id}`, data),
+ delete: id => request.delete(`/role/${id}`),
+
+ getAllPermissionTree: () => request.get('/permission/tree'),
+ getAllUsers: (params = {}) => request.get('/user', { params }),
+ addRoleUsers: (roleId, data) => request.patch(`/role/users/add/${roleId}`, data),
+ removeRoleUsers: (roleId, data) => request.patch(`/role/users/remove/${roleId}`, data),
+}
diff --git a/web/src/views/pms/role/index.vue b/web/src/views/pms/role/index.vue
new file mode 100644
index 0000000..36b85b1
--- /dev/null
+++ b/web/src/views/pms/role/index.vue
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+ 新增角色
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 启用
+
+
+ 停用
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/pms/role/role-user.vue b/web/src/views/pms/role/role-user.vue
new file mode 100644
index 0000000..1773b5d
--- /dev/null
+++ b/web/src/views/pms/role/role-user.vue
@@ -0,0 +1,228 @@
+
+
+
+
+
+
+ {{ route.query.roleName }}
+
+
+
+
+
+
+ 批量取消授权
+
+
+
+ 批量授权
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/pms/user/api.js b/web/src/views/pms/user/api.js
new file mode 100644
index 0000000..70a0630
--- /dev/null
+++ b/web/src/views/pms/user/api.js
@@ -0,0 +1,19 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:29:51
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { request } from '@/utils'
+
+export default {
+ create: data => request.post('/user', data),
+ read: (params = {}) => request.get('/user', { params }),
+ update: data => request.patch(`/user/${data.id}`, data),
+ delete: id => request.delete(`/user/${id}`),
+ resetPwd: (id, data) => request.patch(`/user/password/reset/${id}`, data),
+
+ getAllRoles: () => request.get('/role?enable=1'),
+}
diff --git a/web/src/views/pms/user/index.vue b/web/src/views/pms/user/index.vue
new file mode 100644
index 0000000..d09e82b
--- /dev/null
+++ b/web/src/views/pms/user/index.vue
@@ -0,0 +1,311 @@
+
+
+
+
+
+
+
+ 创建新用户
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 启用
+
+
+ 停用
+
+
+
+
+
+ 详细信息需由用户本人补充修改
+
+
+
+
+
+
diff --git a/web/src/views/profile/api.js b/web/src/views/profile/api.js
new file mode 100644
index 0000000..f091b8e
--- /dev/null
+++ b/web/src/views/profile/api.js
@@ -0,0 +1,14 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:30:03
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { request } from '@/utils'
+
+export default {
+ changePassword: data => request.post('/auth/password', data),
+ updateProfile: data => request.patch(`/user/profile/${data.id}`, data),
+}
diff --git a/web/src/views/profile/index.vue b/web/src/views/profile/index.vue
new file mode 100644
index 0000000..23d6b2a
--- /dev/null
+++ b/web/src/views/profile/index.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+ 用户名:
+ {{ userStore.username }}
+
+
+ 修改密码
+
+
+
+
+ 更改头像
+
+
+ 修改头像只支持在线链接,不提供上传图片功能,如有需要可自行对接!
+
+
+
+
+
+
+
+
+
+
+ 修改资料
+
+
+
+
+
+ {{ userStore.nickName }}
+
+
+ {{ genders.find((item) => item.value === userStore.userInfo?.gender)?.label ?? '未知' }}
+
+
+ {{ userStore.userInfo?.address }}
+
+
+ {{ userStore.userInfo?.email }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/uno.config.js b/web/uno.config.js
new file mode 100644
index 0000000..177883b
--- /dev/null
+++ b/web/uno.config.js
@@ -0,0 +1,57 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:30:57
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import { defineConfig, presetAttributify, presetIcons, presetUno } from 'unocss'
+import presetRemToPx from '@unocss/preset-rem-to-px'
+import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders'
+import { getIcons } from './build'
+
+const icons = getIcons()
+export default defineConfig({
+ presets: [
+ presetUno(),
+ presetAttributify(),
+ presetIcons({
+ warn: true,
+ prefix: ['i-'],
+ extraProperties: {
+ display: 'inline-block',
+ },
+ collections: {
+ me: FileSystemIconLoader('./src/assets/icons/isme'),
+ fe: FileSystemIconLoader('./src/assets/icons/feather'),
+ },
+ }),
+ presetRemToPx({ baseFontSize: 4 }),
+ ],
+ safelist: icons.map(icon => `${icon} ${icon}?mask`.split(' ')).flat(),
+ shortcuts: [
+ ['wh-full', 'w-full h-full'],
+ ['f-c-c', 'flex justify-center items-center'],
+ ['flex-col', 'flex flex-col'],
+ ['card-border', 'border border-solid border-light_border dark:border-dark_border'],
+ ['auto-bg', 'bg-white dark:bg-dark'],
+ ['auto-bg-hover', 'hover:bg-#eaf0f1 hover:dark:bg-#1b2429'],
+ ['auto-bg-highlight', 'bg-#eaf0f1 dark:bg-#1b2429'],
+ ['text-highlight', 'rounded-4 px-8 py-2 auto-bg-highlight'],
+ ],
+ rules: [
+ [
+ 'card-shadow',
+ { 'box-shadow': '0 1px 2px -2px #00000029, 0 3px 6px #0000001f, 0 5px 12px 4px #00000017' },
+ ],
+ ],
+ theme: {
+ colors: {
+ primary: 'rgba(var(--primary-color))',
+ dark: '#18181c',
+ light_border: '#efeff5',
+ dark_border: '#2d2d30',
+ },
+ },
+})
diff --git a/web/vite.config.js b/web/vite.config.js
new file mode 100644
index 0000000..d847e83
--- /dev/null
+++ b/web/vite.config.js
@@ -0,0 +1,74 @@
+/**********************************
+ * @Author: Ronnie Zhang
+ * @LastEditor: Ronnie Zhang
+ * @LastEditTime: 2023/12/05 21:31:02
+ * @Email: zclzone@outlook.com
+ * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
+ **********************************/
+
+import path from 'node:path'
+import { defineConfig, loadEnv } from 'vite'
+import Vue from '@vitejs/plugin-vue'
+import VueDevTools from 'vite-plugin-vue-devtools'
+import Unocss from 'unocss/vite'
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
+import removeNoMatch from 'vite-plugin-router-warn'
+import { pluginIcons, pluginPagePathes } from './build/plugin-isme'
+
+export default defineConfig(({ mode }) => {
+ const viteEnv = loadEnv(mode, process.cwd())
+ const { VITE_PUBLIC_PATH, VITE_PROXY_TARGET } = viteEnv
+
+ return {
+ base: VITE_PUBLIC_PATH || '/',
+ plugins: [
+ Vue(),
+ VueDevTools(),
+ Unocss(),
+ AutoImport({
+ imports: ['vue', 'vue-router'],
+ dts: false,
+ }),
+ Components({
+ resolvers: [NaiveUiResolver()],
+ dts: false,
+ }),
+ // 自定义插件,用于生成页面文件的path,并添加到虚拟模块
+ pluginPagePathes(),
+ // 自定义插件,用于生成自定义icon,并添加到虚拟模块
+ pluginIcons(),
+ // 移除非必要的vue-router动态路由警告: No match found for location with path
+ removeNoMatch(),
+ ],
+ resolve: {
+ alias: {
+ '@': path.resolve(process.cwd(), 'src'),
+ '~': path.resolve(process.cwd()),
+ },
+ },
+ server: {
+ host: '0.0.0.0',
+ port: 3200,
+ open: false,
+ proxy: {
+ '/api': {
+ target: VITE_PROXY_TARGET,
+ changeOrigin: true,
+ rewrite: path => path.replace(/^\/api/, ''),
+ secure: false,
+ configure: (proxy, options) => {
+ // 配置此项可在响应头中看到请求的真实地址
+ proxy.on('proxyRes', (proxyRes, req) => {
+ proxyRes.headers['x-real-url'] = new URL(req.url || '', options.target)?.href || ''
+ })
+ },
+ },
+ },
+ },
+ build: {
+ chunkSizeWarningLimit: 1024, // chunk 大小警告的限制(单位kb)
+ },
+ }
+})