mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2026-01-22 23:50:22 +08:00
Compare commits
32 Commits
v2.0.0
...
fd9480e92f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd9480e92f | ||
|
|
0520cd015a | ||
|
|
f5a26c32e9 | ||
|
|
886ef9e11c | ||
|
|
7d0a17b2b5 | ||
|
|
fa4967efc3 | ||
|
|
207150623e | ||
|
|
6981692c54 | ||
|
|
b64e1c7595 | ||
|
|
efcbe3bea4 | ||
|
|
2c808c6d8b | ||
|
|
447db11c52 | ||
|
|
a3d0e863cc | ||
|
|
a2827e4c0d | ||
|
|
f888c2fbfd | ||
|
|
1c37a38b92 | ||
|
|
f2eb40357d | ||
|
|
58de3c1ad6 | ||
|
|
567e306a5c | ||
|
|
065c6b50c6 | ||
|
|
95a1ef654c | ||
|
|
346da8772a | ||
|
|
a6773cbfec | ||
|
|
4c337d3aa8 | ||
|
|
2c9604a829 | ||
|
|
c4fd0459ab | ||
|
|
aa92455dbb | ||
|
|
857381471e | ||
|
|
b780113f18 | ||
|
|
fe4bbded53 | ||
|
|
d801cf28cc | ||
|
|
334174d442 |
2
.npmrc
2
.npmrc
@@ -1,2 +1,2 @@
|
|||||||
registry=https://registry.npm.taobao.org
|
registry=https://registry.npmmirror.com
|
||||||
shamefully-hoist=true
|
shamefully-hoist=true
|
||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -18,5 +18,8 @@
|
|||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"eslint.validate": ["javascript", "typescript", "javascriptreact", "typescriptreact", "vue"]
|
"eslint.validate": ["javascript", "typescript", "javascriptreact", "typescriptreact", "vue"],
|
||||||
|
"eslint.options": {
|
||||||
|
"overrideConfigFile": "package.json"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Vue Naive Admin 是一款极简风格的后台管理模板,包含前后端解
|
|||||||
|
|
||||||
## 设计理念
|
## 设计理念
|
||||||
|
|
||||||
Vue Naive Admin 2022年2月开始开源,从 1.0 到现在的 2.0,一直秉持着`简单即正义`的理念,旨在帮助中小企业、在校大学生及个人开发者快速上手开发后台管理项目,为了降低使用者的学习成本,没有使用看似主流的 TypeScript(前端),这也使得 Vue Naive Admin 成为了市面上少有的 `使用 JavaScript 的 Vue3 后台管理模板`,而且还算优秀,得到了大量朋友的认可和喜爱,截至 2023-11-17, github `1.1k+` star,gitee `260+` star。
|
Vue Naive Admin 2022年2月开始开源,从 1.0 到现在的 2.0,一直秉持着`简单即正义`的理念,旨在帮助中小企业、在校大学生及个人开发者快速上手开发后台管理项目,为了降低使用者的学习成本,没有使用看似主流的 TypeScript(前端),这也使得 Vue Naive Admin 成为了市面上少有的 `使用 JavaScript 的 Vue3 后台管理模板`,而且还算优秀,得到了大量朋友的认可和喜爱。
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
|
|
||||||
@@ -52,6 +52,7 @@ Vue Naive Admin 提供一套后端代码,技术栈使用 Nestjs + TypeOrm + My
|
|||||||
|
|
||||||
- 源码-github: [isme-nest-serve | github](https://github.com/zclzone/isme-nest-serve)
|
- 源码-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)
|
- 源码-gitee: [isme-nest-serve | gitee](https://gitee.com/isme-admin/isme-nest-serve)
|
||||||
|
- 接口文档: [apidoc | isme-nest-serve](https://apifox.com/apidoc/shared-ff4a4d32-c0d1-4caf-b0ee-6abc130f734a)
|
||||||
|
|
||||||
## 版权说明
|
## 版权说明
|
||||||
|
|
||||||
@@ -64,4 +65,5 @@ Vue Naive Admin 提供一套后端代码,技术栈使用 Nestjs + TypeOrm + My
|
|||||||
## 其他已对接本项目的后端项目
|
## 其他已对接本项目的后端项目
|
||||||
|
|
||||||
- [isme-java-serve](https://github.com/DHBin/isme-java-serve): 一个轻量级的Java后端服务,基于SpringBoot、MybatisPlus、SaToken、MapStruct等实现,已对接 Vue Naive Admin 2.0。
|
- [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。
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<div id="app">
|
<div id="app">
|
||||||
<!-- 白屏时的loading效果 -->
|
<!-- 白屏时的loading效果 -->
|
||||||
<div class="loading-container">
|
<div class="loading-container">
|
||||||
<img src="/resource/logo.png" alt="logo" height="128" />
|
<img src="/resource/logo.png" alt="logo" />
|
||||||
<div class="loading-spin__container">
|
<div class="loading-spin__container">
|
||||||
<div class="loading-spin">
|
<div class="loading-spin">
|
||||||
<div class="left-0 top-0 loading-spin-item"></div>
|
<div class="left-0 top-0 loading-spin-item"></div>
|
||||||
|
|||||||
56
package.json
56
package.json
@@ -10,37 +10,36 @@
|
|||||||
"lint:fix": "eslint --fix --ext .js,.vue ."
|
"lint:fix": "eslint --fix --ext .js,.vue ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/json": "^2.2.129",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@iconify/utils": "^2.1.11",
|
"axios": "^1.6.7",
|
||||||
"@unocss/eslint-config": "^0.58.0",
|
|
||||||
"@unocss/preset-rem-to-px": "^0.58.0",
|
|
||||||
"@vueuse/core": "^10.5.0",
|
|
||||||
"axios": "^1.5.1",
|
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.5.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"naive-ui": "^2.35.0",
|
"naive-ui": "^2.38.1",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^3.2.0",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"sass": "^1.69.3",
|
"vue": "^3.4.21",
|
||||||
"unocss": "^0.58.0",
|
"vue-echarts": "^6.6.9",
|
||||||
"vue": "^3.3.11",
|
"vue-router": "^4.3.0",
|
||||||
"vue-echarts": "^6.6.1",
|
|
||||||
"vue-router": "^4.2.5",
|
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.5.2",
|
"@iconify/json": "^2.2.188",
|
||||||
|
"@iconify/utils": "^2.1.22",
|
||||||
|
"@unocss/eslint-config": "^0.58.5",
|
||||||
|
"@unocss/preset-rem-to-px": "^0.58.5",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@zclzone/eslint-config": "^0.0.5",
|
"@zclzone/eslint-config": "^0.0.5",
|
||||||
"esno": "^0.17.0",
|
"esno": "^4.0.0",
|
||||||
"fs-extra": "^11.1.1",
|
"fs-extra": "^11.2.0",
|
||||||
"glob": "^10.3.10",
|
"glob": "^10.3.10",
|
||||||
"rollup-plugin-visualizer": "^5.9.2",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"unplugin-auto-import": "^0.16.6",
|
"sass": "^1.71.1",
|
||||||
"unplugin-vue-components": "^0.25.2",
|
"unocss": "^0.58.5",
|
||||||
"vite": "^5.0.7",
|
"unplugin-auto-import": "^0.17.5",
|
||||||
"vite-plugin-simple-html": "^0.1.1",
|
"unplugin-vue-components": "^0.26.0",
|
||||||
"vite-plugin-vue-devtools": "1.0.0-rc.7"
|
"vite": "^5.1.4",
|
||||||
|
"vite-plugin-simple-html": "^0.1.2"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
@@ -48,13 +47,10 @@
|
|||||||
"@unocss",
|
"@unocss",
|
||||||
".eslint-global-variables.json"
|
".eslint-global-variables.json"
|
||||||
],
|
],
|
||||||
"rules": {
|
"parserOptions": {
|
||||||
"no-unused-vars": [
|
"ecmaFeatures": {
|
||||||
"error",
|
"jsx": true
|
||||||
{
|
}
|
||||||
"varsIgnorePattern": "^_"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6396
pnpm-lock.yaml
generated
6396
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
|||||||
<svg
|
|
||||||
t="1702480351321"
|
|
||||||
class="icon"
|
|
||||||
viewBox="0 0 1024 1024"
|
|
||||||
version="1.1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
p-id="11122"
|
|
||||||
width="200"
|
|
||||||
height="200"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M509.9008 519.8336m-450.816 0a450.816 450.816 0 1 0 901.632 0 450.816 450.816 0 1 0-901.632 0Z"
|
|
||||||
fill="#C65EDB"
|
|
||||||
p-id="11123"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
d="M798.1568 512.512l-113.3056-78.3872a47.4112 47.4112 0 0 1-20.4288-39.0656l0.3584-137.7792c0.1024-39.2704-44.9024-61.5936-76.0832-37.7856l-109.568 83.5072a47.2832 47.2832 0 0 1-43.4688 7.3216l-130.9184-42.9056c-37.3248-12.2368-72.448 23.6544-59.4432 60.7232l45.568 129.9968A47.3088 47.3088 0 0 1 284.416 501.76l-81.2544 111.2576c-23.1424 31.6928 0.1024 76.2368 39.3728 75.3152l137.728-3.1744a47.3344 47.3344 0 0 1 39.4752 19.6096l80.6912 111.6672c22.9888 31.8464 72.4992 23.4496 83.7632-14.1824l37.9392-126.6176 126.5664 118.272a27.648 27.648 0 0 0 17.7664 7.4752c7.8848 0.3584 15.872-2.6112 21.7088-8.8064a27.91936 27.91936 0 0 0-1.3312-39.4752l-124.8768-116.6848 123.8016-39.8848c37.376-11.9808 44.6976-61.6448 12.3904-84.0192z m-389.6832-6.5024l-42.8032 77.824a27.86816 27.86816 0 0 1-37.9392 11.008 27.93984 27.93984 0 0 1-11.008-37.9392l40.0384-72.7552-19.0464-63.6928c-4.4032-14.7968 3.9936-30.3616 18.7392-34.7648 14.7456-4.4032 30.3616 3.9936 34.7648 18.7392l20.6848 69.2224c3.2256 10.752 1.9968 22.528-3.4304 32.3584z"
|
|
||||||
fill="#FFFFFF"
|
|
||||||
p-id="11124"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
src/assets/images/isme.png
Normal file
BIN
src/assets/images/isme.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
12
src/components/common/TheLogo.vue
Normal file
12
src/components/common/TheLogo.vue
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!--------------------------------
|
||||||
|
- @Author: Ronnie Zhang
|
||||||
|
- @LastEditor: Ronnie Zhang
|
||||||
|
- @LastEditTime: 2024/03/04 16:09:47
|
||||||
|
- @Email: zclzone@outlook.com
|
||||||
|
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||||
|
--------------------------------->
|
||||||
|
<template>
|
||||||
|
<div class="h-40 w-40 rounded-full bg-primary p-1/100">
|
||||||
|
<img src="@/assets/images/isme.png" alt="Logo" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -7,25 +7,22 @@
|
|||||||
--------------------------------->
|
--------------------------------->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AppCard
|
<AppCard v-if="$slots.default" bordered bg="#fafafc dark:black" class="mb-30 min-h-60 rounded-4">
|
||||||
v-if="$slots.default"
|
<form class="flex justify-between p-16" @submit.prevent="handleSearch()">
|
||||||
bordered
|
<n-space wrap :size="[32, 16]">
|
||||||
bg="#fafafc dark:black"
|
<slot />
|
||||||
class="mb-30 min-h-60 flex justify-between rounded-4 p-16"
|
</n-space>
|
||||||
>
|
<div class="flex-shrink-0">
|
||||||
<n-space wrap :size="[32, 16]">
|
<n-button ghost type="primary" @click="handleReset">
|
||||||
<slot />
|
<i class="i-fe:rotate-ccw mr-4" />
|
||||||
</n-space>
|
重置
|
||||||
<div class="flex-shrink-0">
|
</n-button>
|
||||||
<n-button ghost type="primary" @click="handleReset">
|
<n-button attr-type="submit" class="ml-20" type="primary">
|
||||||
<i class="i-fe:rotate-ccw mr-4" />
|
<i class="i-fe:search mr-4" />
|
||||||
重置
|
搜索
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button class="ml-20" type="primary" @click="handleSearch">
|
</div>
|
||||||
<i class="i-fe:search mr-4" />
|
</form>
|
||||||
搜索
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</AppCard>
|
</AppCard>
|
||||||
|
|
||||||
<n-data-table
|
<n-data-table
|
||||||
@@ -129,7 +126,7 @@ function handleSearch() {
|
|||||||
async function handleReset() {
|
async function handleReset() {
|
||||||
const queryItems = { ...props.queryItems }
|
const queryItems = { ...props.queryItems }
|
||||||
for (const key in queryItems) {
|
for (const key in queryItems) {
|
||||||
queryItems[key] = ''
|
queryItems[key] = null
|
||||||
}
|
}
|
||||||
emit('update:queryItems', { ...queryItems, ...initQuery })
|
emit('update:queryItems', { ...queryItems, ...initQuery })
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!--------------------------------
|
<!--------------------------------
|
||||||
- @Author: Ronnie Zhang
|
- @Author: Ronnie Zhang
|
||||||
- @LastEditor: Ronnie Zhang
|
- @LastEditor: Ronnie Zhang
|
||||||
- @LastEditTime: 2023/12/16 18:50:02
|
- @LastEditTime: 2024/01/13 17:41:38
|
||||||
- @Email: zclzone@outlook.com
|
- @Email: zclzone@outlook.com
|
||||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||||
--------------------------------->
|
--------------------------------->
|
||||||
@@ -9,17 +9,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-modal
|
<n-modal
|
||||||
v-model:show="show"
|
v-model:show="show"
|
||||||
|
class="modal-box"
|
||||||
:style="{ width: modalOptions.width, ...modalOptions.modalStyle }"
|
:style="{ width: modalOptions.width, ...modalOptions.modalStyle }"
|
||||||
:preset="undefined"
|
:preset="undefined"
|
||||||
size="huge"
|
size="huge"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
|
@after-leave="onAfterLeave"
|
||||||
>
|
>
|
||||||
<n-card
|
<n-card :style="modalOptions.contentStyle" :closable="modalOptions.closable" @close="close()">
|
||||||
:title="modalOptions.title"
|
<template #header>
|
||||||
:style="modalOptions.contentStyle"
|
<header class="modal-header">{{ modalOptions.title }}</header>
|
||||||
:closable="modalOptions.closable"
|
</template>
|
||||||
@close="close()"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
|
||||||
<!-- 底部按钮 -->
|
<!-- 底部按钮 -->
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { initDrag } from './utils'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
width: {
|
width: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -101,12 +102,17 @@ const show = ref(false)
|
|||||||
const modalOptions = ref({})
|
const modalOptions = ref({})
|
||||||
|
|
||||||
// 打开模态框
|
// 打开模态框
|
||||||
function open(options = {}) {
|
async function open(options = {}) {
|
||||||
// 将props和options合并赋值给modalOptions
|
// 将props和options合并赋值给modalOptions
|
||||||
modalOptions.value = { ...props, ...options }
|
modalOptions.value = { ...props, ...options }
|
||||||
|
|
||||||
// 将show的值设置为true
|
// 将show的值设置为true
|
||||||
show.value = true
|
show.value = true
|
||||||
|
await nextTick()
|
||||||
|
initDrag(
|
||||||
|
Array.prototype.at.call(document.querySelectorAll('.modal-header'), -1),
|
||||||
|
Array.prototype.at.call(document.querySelectorAll('.modal-box'), -1)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义一个close函数,用于关闭模态框
|
// 定义一个close函数,用于关闭模态框
|
||||||
@@ -149,6 +155,14 @@ async function handleCancel(data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onAfterLeave() {
|
||||||
|
await nextTick()
|
||||||
|
initDrag(
|
||||||
|
Array.prototype.at.call(document.querySelectorAll('.modal-header'), -1),
|
||||||
|
Array.prototype.at.call(document.querySelectorAll('.modal-box'), -1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const okLoading = computed({
|
const okLoading = computed({
|
||||||
get() {
|
get() {
|
||||||
return !!modalOptions.value?.okLoading
|
return !!modalOptions.value?.okLoading
|
||||||
|
|||||||
94
src/components/me/modal/utils.js
Normal file
94
src/components/me/modal/utils.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/**********************************
|
||||||
|
* @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]
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
currentX: 0,
|
||||||
|
currentY: 0,
|
||||||
|
flag: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化拖拽
|
||||||
|
export function initDrag(bar, box) {
|
||||||
|
if (!bar || !box) return
|
||||||
|
const screenWidth = document.body.clientWidth // 页面宽度
|
||||||
|
const screenHeight = document.documentElement.clientHeight // 页面可见区域高度
|
||||||
|
|
||||||
|
const dragDomWidth = box.offsetWidth // 盒子宽度
|
||||||
|
const dragDomHeight = box.offsetHeight // 盒子高度
|
||||||
|
|
||||||
|
const minDomLeft = box.offsetLeft // 盒子相对于父元素的左偏移量
|
||||||
|
const minDomTop = box.offsetTop // 盒子相对于父元素的上偏移量
|
||||||
|
|
||||||
|
const maxDragDomLeft = screenWidth - minDomLeft - dragDomWidth // 盒子在水平方向上可拖拽的最大距离
|
||||||
|
const maxDragDomTop = screenHeight - minDomTop - dragDomHeight // 盒子在垂直方向上可拖拽的最大距离
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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距离
|
||||||
|
|
||||||
|
let left = parseInt(params.left) + disX // 盒子元素的新left值
|
||||||
|
let top = parseInt(params.top) + disY // 盒子元素的新top值
|
||||||
|
|
||||||
|
// 拖出屏幕边缘
|
||||||
|
if (-left > minDomLeft) {
|
||||||
|
left = -minDomLeft
|
||||||
|
} else if (left > maxDragDomLeft) {
|
||||||
|
left = maxDragDomLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-top > minDomTop) {
|
||||||
|
top = -minDomTop
|
||||||
|
} else if (top > maxDragDomTop) {
|
||||||
|
top = maxDragDomTop
|
||||||
|
}
|
||||||
|
|
||||||
|
box.style.left = left + 'px'
|
||||||
|
box.style.top = top + 'px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,8 @@ function open(options) {
|
|||||||
async function setCurrentRole() {
|
async function setCurrentRole() {
|
||||||
try {
|
try {
|
||||||
okLoading.value = true
|
okLoading.value = true
|
||||||
await userStore.switchCurrentRole(roleCode.value)
|
const { data } = await api.switchCurrentRole(roleCode.value)
|
||||||
|
await authStore.switchCurrentRole(data)
|
||||||
okLoading.value = false
|
okLoading.value = false
|
||||||
$message.success('切换成功')
|
$message.success('切换成功')
|
||||||
modalRef.value?.handleOk()
|
modalRef.value?.handleOk()
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<router-link class="h-60 f-c-c" to="/">
|
<router-link class="h-60 f-c-c" to="/">
|
||||||
<img src="@/assets/images/logo.png" class="h-40" />
|
<!-- <img src="@/assets/images/logo.png" class="h-40" /> -->
|
||||||
|
<TheLogo class="rounded-8!" />
|
||||||
<h2
|
<h2
|
||||||
v-show="!appStore.collapsed"
|
v-show="!appStore.collapsed"
|
||||||
class="ml-10 max-w-140 flex-shrink-0 text-16 color-primary font-bold"
|
class="ml-10 max-w-140 flex-shrink-0 text-16 color-primary font-bold"
|
||||||
|
|||||||
40
src/router/basic-routes.js
Normal file
40
src/router/basic-routes.js
Normal file
@@ -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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
import { useAuthStore } from '@/store'
|
import { useAuthStore } from '@/store'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
|
||||||
const WHITE_LIST = ['/login', '/404', '/role-select']
|
const WHITE_LIST = ['/login', '/404']
|
||||||
export function createPermissionGuard(router) {
|
export function createPermissionGuard(router) {
|
||||||
router.beforeEach(async (to) => {
|
router.beforeEach(async (to) => {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import { useTabStore } from '@/store'
|
import { useTabStore } from '@/store'
|
||||||
|
|
||||||
export const EXCLUDE_TAB = ['/404', '/403', '/login', '/role-select']
|
export const EXCLUDE_TAB = ['/404', '/403', '/login']
|
||||||
|
|
||||||
export function createTabGuard(router) {
|
export function createTabGuard(router) {
|
||||||
router.afterEach((to) => {
|
router.afterEach((to) => {
|
||||||
|
|||||||
@@ -9,51 +9,14 @@
|
|||||||
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
|
||||||
import { setupRouterGuards } from './guards'
|
import { setupRouterGuards } from './guards'
|
||||||
import { useAuthStore, usePermissionStore, useUserStore } from '@/store'
|
import { useAuthStore, usePermissionStore, useUserStore } from '@/store'
|
||||||
|
import { getPermissions, getUserInfo } from '@/store/helper'
|
||||||
export const basicRoutes = [
|
import { basicRoutes } from './basic-routes'
|
||||||
{
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history:
|
history:
|
||||||
import.meta.env.VITE_USE_HASH === 'true' ? createWebHashHistory('/') : createWebHistory('/'),
|
import.meta.env.VITE_USE_HASH === 'true'
|
||||||
|
? createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH || '/')
|
||||||
|
: createWebHistory(import.meta.env.VITE_PUBLIC_PATH || '/'),
|
||||||
routes: basicRoutes,
|
routes: basicRoutes,
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||||
})
|
})
|
||||||
@@ -74,32 +37,21 @@ export async function initUserAndPermissions() {
|
|||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
if (!authStore.accessToken) {
|
if (!authStore.accessToken) {
|
||||||
authStore.toLogin()
|
const route = unref(router.currentRoute)
|
||||||
|
if (route.path !== '/login') {
|
||||||
|
router.replace({
|
||||||
|
path: '/login',
|
||||||
|
query: route.query,
|
||||||
|
})
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await Promise.all([userStore.getUserInfo(), permissionStore.initPermissions()])
|
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) => {
|
permissionStore.accessRoutes.forEach((route) => {
|
||||||
|
route.component = routeComponents[route.component] || undefined
|
||||||
!router.hasRoute(route.name) && router.addRoute(route)
|
!router.hasRoute(route.name) && router.addRoute(route)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resetRouter() {
|
|
||||||
const basicRouteNames = getRouteNames(basicRoutes)
|
|
||||||
router.getRoutes().forEach((route) => {
|
|
||||||
const name = route.name
|
|
||||||
if (!basicRouteNames.includes(name)) {
|
|
||||||
router.removeRoute(name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRouteNames(routes) {
|
|
||||||
const names = []
|
|
||||||
for (const route of routes) {
|
|
||||||
names.push(route.name)
|
|
||||||
if (route.children?.length) {
|
|
||||||
names.push(...getRouteNames(route.children))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|||||||
29
src/store/helper.js
Normal file
29
src/store/helper.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -33,6 +33,6 @@ export const useAppStore = defineStore('app', {
|
|||||||
},
|
},
|
||||||
persist: {
|
persist: {
|
||||||
paths: ['collapsed', 'naiveThemeOverrides'],
|
paths: ['collapsed', 'naiveThemeOverrides'],
|
||||||
storage: localStorage,
|
storage: sessionStorage,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
**********************************/
|
**********************************/
|
||||||
|
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useUserStore, usePermissionStore, useTabStore } from '@/store'
|
import { useUserStore, usePermissionStore, useTabStore, useRouterStore } from '@/store'
|
||||||
import { resetRouter, router } from '@/router'
|
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', {
|
export const useAuthStore = defineStore('auth', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -22,24 +21,30 @@ export const useAuthStore = defineStore('auth', {
|
|||||||
this.$reset()
|
this.$reset()
|
||||||
},
|
},
|
||||||
toLogin() {
|
toLogin() {
|
||||||
const currentRoute = unref(router.currentRoute)
|
const { router, route } = useRouterStore()
|
||||||
router.replace({
|
router.replace({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
query: currentRoute.query,
|
query: route.query,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
async switchCurrentRole(data) {
|
||||||
|
this.resetLoginState()
|
||||||
|
await nextTick()
|
||||||
|
this.setToken(data)
|
||||||
|
},
|
||||||
resetLoginState() {
|
resetLoginState() {
|
||||||
const { resetUser } = useUserStore()
|
const { resetUser } = useUserStore()
|
||||||
const { resetPermission } = usePermissionStore()
|
const { resetRouter } = useRouterStore()
|
||||||
|
const { resetPermission, accessRoutes } = usePermissionStore()
|
||||||
const { resetTabs } = useTabStore()
|
const { resetTabs } = useTabStore()
|
||||||
|
// 重置路由
|
||||||
|
resetRouter(accessRoutes)
|
||||||
// 重置用户
|
// 重置用户
|
||||||
resetUser()
|
resetUser()
|
||||||
// 重置权限
|
// 重置权限
|
||||||
resetPermission()
|
resetPermission()
|
||||||
// 重置Tabs
|
// 重置Tabs
|
||||||
resetTabs()
|
resetTabs()
|
||||||
// 重置路由
|
|
||||||
resetRouter()
|
|
||||||
// 重置token
|
// 重置token
|
||||||
this.resetToken()
|
this.resetToken()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ export * from './auth'
|
|||||||
export * from './permission'
|
export * from './permission'
|
||||||
export * from './tab'
|
export * from './tab'
|
||||||
export * from './user'
|
export * from './user'
|
||||||
|
export * from './router'
|
||||||
|
|||||||
@@ -7,27 +7,16 @@
|
|||||||
**********************************/
|
**********************************/
|
||||||
|
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { isExternal } from '@/utils'
|
|
||||||
import { basePermissions } from '@/settings'
|
|
||||||
import api from '@/api'
|
|
||||||
|
|
||||||
const routeComponents = import.meta.glob('@/views/**/*.vue')
|
|
||||||
|
|
||||||
export const usePermissionStore = defineStore('permission', {
|
export const usePermissionStore = defineStore('permission', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
menus: [],
|
|
||||||
accessRoutes: [],
|
accessRoutes: [],
|
||||||
asyncPermissions: [],
|
permissions: [],
|
||||||
|
menus: [],
|
||||||
}),
|
}),
|
||||||
getters: {
|
|
||||||
permissions() {
|
|
||||||
return basePermissions.concat(this.asyncPermissions)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
actions: {
|
actions: {
|
||||||
async initPermissions() {
|
setPermissions(permissions) {
|
||||||
const { data } = (await api.getRolePermissions()) || []
|
this.permissions = permissions
|
||||||
this.asyncPermissions = data
|
|
||||||
this.menus = this.permissions
|
this.menus = this.permissions
|
||||||
.filter((item) => item.type === 'MENU')
|
.filter((item) => item.type === 'MENU')
|
||||||
.map((item) => this.getMenuItem(item))
|
.map((item) => this.getMenuItem(item))
|
||||||
@@ -36,7 +25,7 @@ export const usePermissionStore = defineStore('permission', {
|
|||||||
},
|
},
|
||||||
getMenuItem(item, parent) {
|
getMenuItem(item, parent) {
|
||||||
const route = this.generateRoute(item, item.show ? null : parent?.key)
|
const route = this.generateRoute(item, item.show ? null : parent?.key)
|
||||||
if (item.enable && route.path && !isExternal(route.path)) this.accessRoutes.push(route)
|
if (item.enable && route.path && !route.path.startsWith('http')) this.accessRoutes.push(route)
|
||||||
if (!item.show) return null
|
if (!item.show) return null
|
||||||
const menuItem = {
|
const menuItem = {
|
||||||
label: route.meta.title,
|
label: route.meta.title,
|
||||||
@@ -60,7 +49,7 @@ export const usePermissionStore = defineStore('permission', {
|
|||||||
name: item.code,
|
name: item.code,
|
||||||
path: item.path,
|
path: item.path,
|
||||||
redirect: item.redirect,
|
redirect: item.redirect,
|
||||||
component: routeComponents[item.component] || undefined,
|
component: item.component,
|
||||||
meta: {
|
meta: {
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
title: item.name,
|
title: item.name,
|
||||||
|
|||||||
26
src/store/modules/router.js
Normal file
26
src/store/modules/router.js
Normal file
@@ -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,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
**********************************/
|
**********************************/
|
||||||
|
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { router } from '@/router'
|
import { useRouterStore } from './router'
|
||||||
|
|
||||||
export const useTabStore = defineStore('tab', {
|
export const useTabStore = defineStore('tab', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -55,13 +55,13 @@ export const useTabStore = defineStore('tab', {
|
|||||||
async removeTab(path) {
|
async removeTab(path) {
|
||||||
this.setTabs(this.tabs.filter((tab) => tab.path !== path))
|
this.setTabs(this.tabs.filter((tab) => tab.path !== path))
|
||||||
if (path === this.activeTab) {
|
if (path === this.activeTab) {
|
||||||
router.push(this.tabs[this.tabs.length - 1].path)
|
useRouterStore().router?.push(this.tabs[this.tabs.length - 1].path)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeOther(curPath = this.activeTab) {
|
removeOther(curPath = this.activeTab) {
|
||||||
this.setTabs(this.tabs.filter((tab) => tab.path === curPath))
|
this.setTabs(this.tabs.filter((tab) => tab.path === curPath))
|
||||||
if (curPath !== this.activeTab) {
|
if (curPath !== this.activeTab) {
|
||||||
router.push(this.tabs[this.tabs.length - 1].path)
|
useRouterStore().router?.push(this.tabs[this.tabs.length - 1].path)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeLeft(curPath) {
|
removeLeft(curPath) {
|
||||||
@@ -69,7 +69,7 @@ export const useTabStore = defineStore('tab', {
|
|||||||
const filterTabs = this.tabs.filter((item, index) => index >= curIndex)
|
const filterTabs = this.tabs.filter((item, index) => index >= curIndex)
|
||||||
this.setTabs(filterTabs)
|
this.setTabs(filterTabs)
|
||||||
if (!filterTabs.find((item) => item.path === this.activeTab)) {
|
if (!filterTabs.find((item) => item.path === this.activeTab)) {
|
||||||
router.push(filterTabs[filterTabs.length - 1].path)
|
useRouterStore().router?.push(filterTabs[filterTabs.length - 1].path)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeRight(curPath) {
|
removeRight(curPath) {
|
||||||
@@ -77,7 +77,7 @@ export const useTabStore = defineStore('tab', {
|
|||||||
const filterTabs = this.tabs.filter((item, index) => index <= curIndex)
|
const filterTabs = this.tabs.filter((item, index) => index <= curIndex)
|
||||||
this.setTabs(filterTabs)
|
this.setTabs(filterTabs)
|
||||||
if (!filterTabs.find((item) => item.path === this.activeTab.value)) {
|
if (!filterTabs.find((item) => item.path === this.activeTab.value)) {
|
||||||
router.push(filterTabs[filterTabs.length - 1].path)
|
useRouterStore().router?.push(filterTabs[filterTabs.length - 1].path)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetTabs() {
|
resetTabs() {
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
**********************************/
|
**********************************/
|
||||||
|
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import api from '@/api'
|
|
||||||
import { useAuthStore } from '@/store'
|
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', {
|
export const useUserStore = defineStore('user', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -35,32 +33,8 @@ export const useUserStore = defineStore('user', {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async getUserInfo() {
|
setUser(user) {
|
||||||
try {
|
this.userInfo = user
|
||||||
const res = await api.getUser()
|
|
||||||
const { id, username, profile, roles, currentRole } = res.data || {}
|
|
||||||
this.userInfo = {
|
|
||||||
id,
|
|
||||||
username,
|
|
||||||
avatar: profile?.avatar,
|
|
||||||
nickName: profile?.nickName,
|
|
||||||
gender: profile?.gender,
|
|
||||||
address: profile?.address,
|
|
||||||
email: profile?.email,
|
|
||||||
roles,
|
|
||||||
currentRole,
|
|
||||||
}
|
|
||||||
return Promise.resolve(res.data)
|
|
||||||
} catch (error) {
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async switchCurrentRole(roleCode) {
|
|
||||||
const { data } = await api.switchCurrentRole(roleCode)
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
authStore.resetLoginState()
|
|
||||||
await nextTick()
|
|
||||||
authStore.setToken(data)
|
|
||||||
},
|
},
|
||||||
resetUser() {
|
resetUser() {
|
||||||
this.$reset()
|
this.$reset()
|
||||||
|
|||||||
@@ -41,3 +41,9 @@ textarea {
|
|||||||
border: none;
|
border: none;
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
video {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function setupInterceptors(axiosInstance) {
|
|||||||
const message = resolveResError(code, data?.message ?? statusText)
|
const message = resolveResError(code, data?.message ?? statusText)
|
||||||
|
|
||||||
//需要错误提醒
|
//需要错误提醒
|
||||||
!config.noNeedTip && window.$message?.error(message)
|
!config?.noNeedTip && message && window.$message?.error(message)
|
||||||
return Promise.reject({ code, message, error: data ?? response })
|
return Promise.reject({ code, message, error: data ?? response })
|
||||||
}
|
}
|
||||||
return Promise.resolve(data ?? response)
|
return Promise.resolve(data ?? response)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import * as NaiveUI from 'naive-ui'
|
import * as NaiveUI from 'naive-ui'
|
||||||
import { isNullOrUndef } from '@/utils'
|
import { isNullOrUndef } from '@/utils'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store'
|
||||||
|
|
||||||
export function setupMessage(NMessage) {
|
export function setupMessage(NMessage) {
|
||||||
class Message {
|
class Message {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!--------------------------------
|
<!--------------------------------
|
||||||
- @Author: Ronnie Zhang
|
- @Author: Ronnie Zhang
|
||||||
- @LastEditor: Ronnie Zhang
|
- @LastEditor: Ronnie Zhang
|
||||||
- @LastEditTime: 2023/12/16 18:51:56
|
- @LastEditTime: 2024/01/13 17:41:47
|
||||||
- @Email: zclzone@outlook.com
|
- @Email: zclzone@outlook.com
|
||||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||||
--------------------------------->
|
--------------------------------->
|
||||||
@@ -55,7 +55,7 @@ function openModal2() {
|
|||||||
$modal2.value?.open({
|
$modal2.value?.open({
|
||||||
cancelText: '关闭当前',
|
cancelText: '关闭当前',
|
||||||
okText: '关闭所有弹窗',
|
okText: '关闭所有弹窗',
|
||||||
modalStyle: { width: '320px', padding: '12px', top: '100px' },
|
width: '400px',
|
||||||
async onOk() {
|
async onOk() {
|
||||||
okLoading2.value = true
|
okLoading2.value = true
|
||||||
$message.loading('正在关闭...', { key: 'modal2' })
|
$message.loading('正在关闭...', { key: 'modal2' })
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="w-320 flex-col px-20 py-32">
|
<div class="w-320 flex-col px-20 py-32">
|
||||||
<h2 class="f-c-c text-24 text-#6a6a6a font-normal">
|
<h2 class="f-c-c text-24 text-#6a6a6a font-normal">
|
||||||
<img src="@/assets/images/logo.png" height="50" class="mr-12" />
|
<img src="@/assets/images/logo.png" class="mr-12 h-50" />
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</h2>
|
</h2>
|
||||||
<n-input
|
<n-input
|
||||||
@@ -104,42 +104,30 @@
|
|||||||
import { throttle, lStorage } from '@/utils'
|
import { throttle, lStorage } from '@/utils'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import api from './api'
|
import api from './api'
|
||||||
import { useUserStore, useAuthStore } from '@/store'
|
import { useAuthStore } from '@/store'
|
||||||
import { initUserAndPermissions } from '@/router'
|
import { initUserAndPermissions } from '@/router'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const title = import.meta.env.VITE_TITLE
|
const title = import.meta.env.VITE_TITLE
|
||||||
|
|
||||||
const isLogined = computed(() => {
|
|
||||||
return authStore.accessToken && userStore.roles
|
|
||||||
})
|
|
||||||
|
|
||||||
const loginInfo = ref({
|
const loginInfo = ref({
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
})
|
})
|
||||||
function initLoginInfo() {
|
|
||||||
const localLoginInfo = lStorage.get('loginInfo')
|
|
||||||
if (localLoginInfo) {
|
|
||||||
loginInfo.value.username = localLoginInfo.username || ''
|
|
||||||
loginInfo.value.password = localLoginInfo.password || ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const captchaUrl = ref('')
|
const captchaUrl = ref('')
|
||||||
const initCaptcha = throttle(() => {
|
const initCaptcha = throttle(() => {
|
||||||
captchaUrl.value = '/api/auth/captcha?' + Date.now()
|
captchaUrl.value = '/api/auth/captcha?' + Date.now()
|
||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
if (isLogined.value) {
|
const localLoginInfo = lStorage.get('loginInfo')
|
||||||
router.push({ path: '/role-select', query: route.query })
|
if (localLoginInfo) {
|
||||||
} else {
|
loginInfo.value.username = localLoginInfo.username || ''
|
||||||
initLoginInfo()
|
loginInfo.value.password = localLoginInfo.password || ''
|
||||||
initCaptcha()
|
|
||||||
}
|
}
|
||||||
|
initCaptcha()
|
||||||
|
|
||||||
function quickLogin() {
|
function quickLogin() {
|
||||||
loginInfo.value.username = 'admin'
|
loginInfo.value.username = 'admin'
|
||||||
|
|||||||
@@ -106,10 +106,14 @@ function handleDelete(item) {
|
|||||||
$dialog.confirm({
|
$dialog.confirm({
|
||||||
content: `确认删除【${item.name}】?`,
|
content: `确认删除【${item.name}】?`,
|
||||||
async confirm() {
|
async confirm() {
|
||||||
$message.loading('正在删除', { key: 'deleteMenu' })
|
try {
|
||||||
await api.deletePermission(item.id)
|
$message.loading('正在删除', { key: 'deleteMenu' })
|
||||||
$message.success('删除成功', { key: 'deleteMenu' })
|
await api.deletePermission(item.id)
|
||||||
emit('refresh')
|
$message.success('删除成功', { key: 'deleteMenu' })
|
||||||
|
emit('refresh')
|
||||||
|
} catch (error) {
|
||||||
|
$message.destroy('deleteMenu')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,21 @@
|
|||||||
</template>
|
</template>
|
||||||
<n-input v-model:value="modalForm.code" />
|
<n-input v-model:value="modalForm.code" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="12" path="path">
|
<n-form-item-gi
|
||||||
|
:span="12"
|
||||||
|
path="path"
|
||||||
|
:rule="{
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
type: 'string',
|
||||||
|
message: '必须是/、http、https开头',
|
||||||
|
validator(rule, value) {
|
||||||
|
if (value) {
|
||||||
|
return /\/|http|https/.test(value)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<QuestionLabel label="路由地址" content="父级菜单可不填" />
|
<QuestionLabel label="路由地址" content="父级菜单可不填" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -23,13 +23,7 @@
|
|||||||
:get-data="api.read"
|
:get-data="api.read"
|
||||||
>
|
>
|
||||||
<MeQueryItem label="角色名" :label-width="50">
|
<MeQueryItem label="角色名" :label-width="50">
|
||||||
<n-input
|
<n-input v-model:value="queryItems.name" type="text" placeholder="请输入角色名" clearable />
|
||||||
v-model:value="queryItems.name"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入角色名"
|
|
||||||
clearable
|
|
||||||
@keydown.enter="() => $table?.handleSearch()"
|
|
||||||
/>
|
|
||||||
</MeQueryItem>
|
</MeQueryItem>
|
||||||
<MeQueryItem label="状态" :label-width="50">
|
<MeQueryItem label="状态" :label-width="50">
|
||||||
<n-select
|
<n-select
|
||||||
|
|||||||
@@ -43,7 +43,6 @@
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名"
|
||||||
clearable
|
clearable
|
||||||
@keydown.enter="() => $table?.handleSearch"
|
|
||||||
/>
|
/>
|
||||||
</MeQueryItem>
|
</MeQueryItem>
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名"
|
||||||
clearable
|
clearable
|
||||||
@keydown.enter="() => $table?.handleSearch"
|
|
||||||
/>
|
/>
|
||||||
</MeQueryItem>
|
</MeQueryItem>
|
||||||
|
|
||||||
|
|||||||
@@ -100,9 +100,10 @@
|
|||||||
import { MeModal } from '@/components'
|
import { MeModal } from '@/components'
|
||||||
import { useForm, useModal } from '@/composables'
|
import { useForm, useModal } from '@/composables'
|
||||||
import { useUserStore } from '@/store'
|
import { useUserStore } from '@/store'
|
||||||
|
import { getUserInfo } from '@/store/helper'
|
||||||
import api from './api'
|
import api from './api'
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
const required = {
|
const required = {
|
||||||
required: true,
|
required: true,
|
||||||
message: '此为必填项',
|
message: '此为必填项',
|
||||||
@@ -116,7 +117,7 @@ async function handlePwdSave() {
|
|||||||
await pwdValidation()
|
await pwdValidation()
|
||||||
await api.changePassword(pwdForm.value)
|
await api.changePassword(pwdForm.value)
|
||||||
$message.success('密码修改成功')
|
$message.success('密码修改成功')
|
||||||
userStore.getUserInfo()
|
refreshUserInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
const newAvatar = ref(userStore.avatar)
|
const newAvatar = ref(userStore.avatar)
|
||||||
@@ -128,7 +129,7 @@ async function handleAvatarSave() {
|
|||||||
}
|
}
|
||||||
await api.updateProfile({ id: userStore.userId, avatar: newAvatar.value })
|
await api.updateProfile({ id: userStore.userId, avatar: newAvatar.value })
|
||||||
$message.success('头像修改成功')
|
$message.success('头像修改成功')
|
||||||
userStore.getUserInfo()
|
refreshUserInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
const genders = [
|
const genders = [
|
||||||
@@ -148,6 +149,11 @@ async function handleProfileSave() {
|
|||||||
await profileValidation()
|
await profileValidation()
|
||||||
await api.updateProfile(profileForm.value)
|
await api.updateProfile(profileForm.value)
|
||||||
$message.success('资料修改成功')
|
$message.success('资料修改成功')
|
||||||
userStore.getUserInfo()
|
refreshUserInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshUserInfo() {
|
||||||
|
const user = await getUserInfo()
|
||||||
|
userStore.setUser(user)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import AutoImport from 'unplugin-auto-import/vite'
|
|||||||
import Components from 'unplugin-vue-components/vite'
|
import Components from 'unplugin-vue-components/vite'
|
||||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
||||||
import simpleHtmlPlugin from 'vite-plugin-simple-html'
|
import simpleHtmlPlugin from 'vite-plugin-simple-html'
|
||||||
import VueDevTools from 'vite-plugin-vue-devtools'
|
|
||||||
import { pluginPagePathes, pluginIcons } from './build/plugin-isme'
|
import { pluginPagePathes, pluginIcons } from './build/plugin-isme'
|
||||||
|
|
||||||
export default defineConfig(({ command, mode }) => {
|
export default defineConfig(({ command, mode }) => {
|
||||||
@@ -25,7 +24,6 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
return {
|
return {
|
||||||
base: VITE_PUBLIC_PATH || '/',
|
base: VITE_PUBLIC_PATH || '/',
|
||||||
plugins: [
|
plugins: [
|
||||||
VueDevTools(),
|
|
||||||
Vue(),
|
Vue(),
|
||||||
Unocss(),
|
Unocss(),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
@@ -64,6 +62,7 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
target: VITE_PROXY_TARGET,
|
target: VITE_PROXY_TARGET,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(new RegExp('^/api'), ''),
|
rewrite: (path) => path.replace(new RegExp('^/api'), ''),
|
||||||
|
secure: false,
|
||||||
configure: (proxy, options) => {
|
configure: (proxy, options) => {
|
||||||
// 配置此项可在响应头中看到请求的真实地址
|
// 配置此项可在响应头中看到请求的真实地址
|
||||||
proxy.on('proxyRes', (proxyRes, req) => {
|
proxy.on('proxyRes', (proxyRes, req) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user