mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2025-12-28 12:10:20 +08:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40d5106c6b | ||
|
|
094a9dcb3b | ||
|
|
db5089d92e | ||
|
|
a41ccad2d0 | ||
|
|
8973e39566 | ||
|
|
8c1191ece2 | ||
|
|
b3aa8147b1 | ||
|
|
0d240f083a | ||
|
|
c180cf54a8 | ||
|
|
2541706ac3 | ||
|
|
44b935e8f6 | ||
|
|
ea1ce0601a | ||
|
|
2989ecf126 | ||
|
|
ce94bf38d1 | ||
|
|
13bc185926 | ||
|
|
51cfd3e2eb | ||
|
|
c22cb3b35c | ||
|
|
621a2304e7 | ||
|
|
729337cdc5 | ||
|
|
4ef58b612f | ||
|
|
ba5d32244f | ||
|
|
efc2a194a3 | ||
|
|
6160c2e664 | ||
|
|
fbd1e9a38a | ||
|
|
17928cbc57 | ||
|
|
e7fc403c77 |
16
.env.github
Normal file
16
.env.github
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 自定义域名CNAME
|
||||||
|
# VITE_APP_GLOB_CNAME = 'template.qszone.com'
|
||||||
|
|
||||||
|
# 资源公共路径,需要以 /开头和结尾
|
||||||
|
VITE_PUBLIC_PATH = '/vue-naive-admin/'
|
||||||
|
|
||||||
|
VITE_APP_USE_HASH = true
|
||||||
|
|
||||||
|
# 是否启用MOCK
|
||||||
|
VITE_APP_USE_MOCK = true
|
||||||
|
|
||||||
|
# base api
|
||||||
|
VITE_APP_GLOB_BASE_API = '/api'
|
||||||
|
|
||||||
|
# test base api
|
||||||
|
VITE_APP_GLOB_BASE_API_TEST = '/api-test'
|
||||||
11
.env.staging
11
.env.staging
@@ -1,11 +0,0 @@
|
|||||||
# 资源公共路径,需要以 /开头和结尾
|
|
||||||
VITE_PUBLIC_PATH = '/'
|
|
||||||
|
|
||||||
# 是否启用MOCK
|
|
||||||
VITE_APP_USE_MOCK = false
|
|
||||||
|
|
||||||
# base api
|
|
||||||
VITE_APP_GLOB_BASE_API = 'http://localhost:8080/api'
|
|
||||||
|
|
||||||
# test base api
|
|
||||||
VITE_APP_GLOB_BASE_API_TEST = 'http://localhost:8080/api-test'
|
|
||||||
38
.github/workflows/deploy.yml
vendored
Normal file
38
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: use Node.js 16
|
||||||
|
uses: actions/setup-node@v2.1.2
|
||||||
|
with:
|
||||||
|
node-version: '16.x'
|
||||||
|
|
||||||
|
- name: use pnpm 6.32.2
|
||||||
|
uses: pnpm/action-setup@v2.2.1
|
||||||
|
with:
|
||||||
|
version: 6.32.2
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
pnpm install
|
||||||
|
pnpm run build:github
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
with:
|
||||||
|
publish_dir: ./dist
|
||||||
|
github_token: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
||||||
|
user_name: ${{ secrets.USER_NAME }}
|
||||||
|
user_email: ${{ secrets.USER_EMAIL }}
|
||||||
|
force_orphan: true
|
||||||
|
commit_message: deploy gh-pages
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 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.
|
||||||
39
README.md
39
README.md
@@ -1,4 +1,16 @@
|
|||||||
## VUE NAIVE ADMIN
|
<p align="center">
|
||||||
|
<a href="https://github.com/zclzone/vue-naive-admin">
|
||||||
|
<img alt="Vue Naive Admin Logo" width="200" src="https://assets.qszone.com/images/logo_qs.svg">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/zclzone/vue-naive-admin/actions"><img allt="checks" src="https://badgen.net/github/checks/zclzone/vue-naive-admin"/></a>
|
||||||
|
<a href="https://github.com/zclzone/vue-naive-admin"><img allt="stars" src="https://badgen.net/github/stars/zclzone/vue-naive-admin"/></a>
|
||||||
|
<a href="https://github.com/zclzone/vue-naive-admin"><img allt="forks" src="https://badgen.net/github/forks/zclzone/vue-naive-admin"/></a>
|
||||||
|
<a href="https://github.com/zclzone/vue-naive-admin/releases"><img allt="releases" src="https://badgen.net/github/releases/zclzone/vue-naive-admin"/></a>
|
||||||
|
<a href="./LICENSE"><img allt="MIT License" src="https://badgen.net/github/license/zclzone/vue-naive-admin"/></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
### 简介
|
### 简介
|
||||||
|
|
||||||
@@ -13,26 +25,30 @@
|
|||||||
|
|
||||||
- 🍒 集成 Naive UI,尤大推荐的 UI 组件库,[https://www.naiveui.com](https://www.naiveui.com)
|
- 🍒 集成 Naive UI,尤大推荐的 UI 组件库,[https://www.naiveui.com](https://www.naiveui.com)
|
||||||
- 🍑 集成登陆、注销及权限验证
|
- 🍑 集成登陆、注销及权限验证
|
||||||
- 🍐 集成多环境配置,dev、测试、预发布和生产
|
- 🍐 集成多环境配置,dev、测试、生产和github pages环境
|
||||||
- 🍎 集成 Eslint + Prettier,代码约束和格式化统一
|
- 🍎 集成 Eslint + Prettier,代码约束和格式化统一
|
||||||
- 🍉 集成 Mock 接口服务,dev 环境和发布环境都支持,可动态配置是否启用 mock 服务,不启用时不会加载 mock 包,减少打包体积
|
- 🍉 集成 Mock 接口服务,dev 环境和发布环境都支持,可动态配置是否启用 mock 服务,不启用时不会加载 mock 包,减少打包体积
|
||||||
- 🍇 集成 unocss,antfu 大神开源的原子化 css 解决方案,非常轻量,目前我是自己写 scss 样式搭配着 unocss 使用的
|
- 🍇 集成 unocss,antfu 大神开源的原子化 css 解决方案,非常轻量,目前我是自己写 scss 样式搭配着 unocss 使用的
|
||||||
- 🍍 集成 Pinia,Vuex 的替代方案,轻量、简单、易用(尤大已表示不会有Vuex5,或者说pinia就是Vuex5)
|
- 🍍 集成 Pinia,Vuex 的替代方案,轻量、简单、易用(尤大已表示不会有Vuex5,或者说pinia就是Vuex5)
|
||||||
- 📦 集成 Vite 自动导入插件unplugin-vue-components,解放双手,开发效率直接起飞
|
- 📦 集成 Vite 自动导入插件unplugin-vue-components,解放双手,开发效率直接起飞
|
||||||
|
- 🤹 集成 unplugin-icons插件,优雅使用iconify图标
|
||||||
- 🍏 二次封装 Axios,支持多 axios 实例,支持线上环境免重新打包修改 baseURL
|
- 🍏 二次封装 Axios,支持多 axios 实例,支持线上环境免重新打包修改 baseURL
|
||||||
- 🍌 二次封装全局 Dialog、Message、LoadingBar 组件
|
- 🍌 二次封装全局 Dialog、Message、LoadingBar 组件
|
||||||
- 🍋 二次封装 localStorage 和 sessionStorage,支持设置过期时间
|
- 🍋 二次封装 localStorage 和 sessionStorage,支持设置过期时间
|
||||||
|
|
||||||
## 预览
|
### 预览
|
||||||
|
|
||||||
[template.qszone.com](https://template.qszone.com)
|
[template.qszone.com](https://template.qszone.com)
|
||||||
|
|
||||||
## 文档
|
[github pages](https://zclzone.github.io/vue-naive-admin)
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
|
||||||
[Vue Naive Admin Docs](https://zclzone.github.io/vue-naive-admin-docs)
|
[Vue Naive Admin Docs](https://zclzone.github.io/vue-naive-admin-docs)
|
||||||
|
|
||||||
[羽雀文档:Vue Naive Admin](https://www.yuque.com/qszone/vue-naive-admin)
|
[羽雀文档:Vue Naive Admin](https://www.yuque.com/qszone/vue-naive-admin)
|
||||||
|
|
||||||
## 构建步骤
|
### 构建
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# 推荐配置git autocrlf 为 false(本项目规范使用lf换行符,此配置是为防止git自动将源文件转换为crlf)
|
# 推荐配置git autocrlf 为 false(本项目规范使用lf换行符,此配置是为防止git自动将源文件转换为crlf)
|
||||||
@@ -52,20 +68,20 @@ pnpm i # 或者 npm i
|
|||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## 发布
|
### 发布
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# 构建测试环境
|
# 构建测试环境
|
||||||
npm run build:test
|
npm run build:test
|
||||||
|
|
||||||
# 构建预发布环境
|
# 构建github pages环境
|
||||||
npm run build:staging
|
npm run build:github
|
||||||
|
|
||||||
# 构建生产环境
|
# 构建生产环境
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
## 其他指令
|
### 其他指令
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# eslint代码格式检查
|
# eslint代码格式检查
|
||||||
@@ -78,7 +94,9 @@ npm run lint:fix
|
|||||||
npm run preview
|
npm run preview
|
||||||
```
|
```
|
||||||
|
|
||||||
## Git 提交规范
|
### 规范
|
||||||
|
|
||||||
|
#### git commit 规范
|
||||||
|
|
||||||
- `feat` 增加新功能
|
- `feat` 增加新功能
|
||||||
- `fix` 修复问题/BUG
|
- `fix` 修复问题/BUG
|
||||||
@@ -94,3 +112,4 @@ npm run preview
|
|||||||
- `types` 类型定义文件更改
|
- `types` 类型定义文件更改
|
||||||
- `wip` 开发中
|
- `wip` 开发中
|
||||||
- `mod` 不确定分类的修改
|
- `mod` 不确定分类的修改
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
}
|
},
|
||||||
|
"jsx": "preserve"
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-naive-admin",
|
"name": "vue-naive-admin",
|
||||||
"version": "0.0.1",
|
"version": "0.3.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"lint": "eslint --ext .js,.vue .",
|
"lint": "eslint --ext .js,.vue .",
|
||||||
"lint:fix": "eslint --fix --ext .js,.vue .",
|
"lint:fix": "eslint --fix --ext .js,.vue .",
|
||||||
"build": "vite build && esno ./build/script",
|
"build": "vite build && esno ./build/script",
|
||||||
"build:test": "vite build --mode test && esno ./build/script",
|
"build:test": "vite build --mode test && esno ./build/script",
|
||||||
"build:staging": "vite build --mode staging && esno ./build/script",
|
"build:github": "vite build --mode github && esno ./build/script",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-config-provider :theme-overrides="appStore.themeOverrides">
|
<n-config-provider :theme-overrides="useTheme.naiveThemeOverrides">
|
||||||
<n-loading-bar-provider>
|
<n-loading-bar-provider>
|
||||||
<LoadingBar />
|
<LoadingBar />
|
||||||
<n-dialog-provider>
|
<n-dialog-provider>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
import MessageContent from './MessageContent.vue'
|
import MessageContent from './MessageContent.vue'
|
||||||
import DialogContent from './DialogContent.vue'
|
import DialogContent from './DialogContent.vue'
|
||||||
import LoadingBar from './LoadingBar.vue'
|
import LoadingBar from './LoadingBar.vue'
|
||||||
|
import { useThemeStore } from '@/store/modules/theme'
|
||||||
|
|
||||||
import { useAppStore } from '@/store/modules/app'
|
const useTheme = useThemeStore()
|
||||||
const appStore = useAppStore()
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
87
src/layout/components/tags/index.vue
Normal file
87
src/layout/components/tags/index.vue
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tags-wrapper" :style="{ height: useTheme.tags.height + 'px' }">
|
||||||
|
<n-space>
|
||||||
|
<n-tag
|
||||||
|
v-for="tag in useTags.tags"
|
||||||
|
:key="tag.path"
|
||||||
|
:type="useTags.activeTag === tag.path ? 'primary' : 'default'"
|
||||||
|
:closable="useTags.tags.length > 1"
|
||||||
|
@click="handleTagClick(tag.path)"
|
||||||
|
@close.stop="handleClose(tag.path)"
|
||||||
|
>
|
||||||
|
{{ tag.title }}
|
||||||
|
</n-tag>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="Tags">
|
||||||
|
import { watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { useTagsStore } from '@/store/modules/tags'
|
||||||
|
import { useThemeStore } from '@/store/modules/theme'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const useTags = useTagsStore()
|
||||||
|
const useTheme = useThemeStore()
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.path,
|
||||||
|
() => {
|
||||||
|
const { name, path } = route
|
||||||
|
const title = route.meta?.title
|
||||||
|
useTags.addTag({ name, path, title })
|
||||||
|
useTags.setActiveTag(path)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleTagClick = (path) => {
|
||||||
|
useTags.setActiveTag(path)
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = (path) => {
|
||||||
|
if (path === useTags.activeTag) {
|
||||||
|
const activeIndex = useTags.tags.findIndex((item) => item.path === path)
|
||||||
|
if (activeIndex > 0) {
|
||||||
|
router.push(useTags.tags[activeIndex - 1].path)
|
||||||
|
} else {
|
||||||
|
router.push(useTags.tags[activeIndex + 1].path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useTags.removeTag(path)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tags-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 0 10px;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 9;
|
||||||
|
.n-tag {
|
||||||
|
padding: 0 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
.n-tag__close {
|
||||||
|
margin-left: 5px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.7s;
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
background-color: $primaryColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: $primaryColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,20 +1,21 @@
|
|||||||
<script setup>
|
|
||||||
import AppHeader from './components/header/index.vue'
|
|
||||||
import SideMenu from './components/sidebar/index.vue'
|
|
||||||
import AppMain from './components/AppMain.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<n-layout has-sider position="absolute">
|
<n-layout has-sider position="absolute">
|
||||||
<n-layout-sider :width="200" :collapsed-width="0" :native-scrollbar="false">
|
<n-layout-sider bordered :width="200" :collapsed-width="0" :native-scrollbar="false">
|
||||||
<SideMenu />
|
<SideBar />
|
||||||
</n-layout-sider>
|
</n-layout-sider>
|
||||||
<n-layout>
|
<n-layout>
|
||||||
<n-layout-header>
|
<n-layout-header :style="{ height: useTheme.header.height + 'px' }" style="border-left: none">
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
</n-layout-header>
|
</n-layout-header>
|
||||||
<n-layout position="absolute" style="top: 60px; background-color: #f5f6fb" :native-scrollbar="false">
|
|
||||||
|
<n-layout
|
||||||
|
position="absolute"
|
||||||
|
style="background-color: #f5f6fb"
|
||||||
|
:style="{ top: useTheme.header.height + 'px' }"
|
||||||
|
:native-scrollbar="false"
|
||||||
|
>
|
||||||
|
<AppTags v-if="useTheme.tags.visible" />
|
||||||
<AppMain />
|
<AppMain />
|
||||||
</n-layout>
|
</n-layout>
|
||||||
</n-layout>
|
</n-layout>
|
||||||
@@ -22,6 +23,16 @@ import AppMain from './components/AppMain.vue'
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import AppHeader from './components/header/index.vue'
|
||||||
|
import SideBar from './components/sidebar/index.vue'
|
||||||
|
import AppMain from './components/AppMain.vue'
|
||||||
|
import AppTags from './components/tags/index.vue'
|
||||||
|
import { useThemeStore } from '@/store/modules/theme'
|
||||||
|
|
||||||
|
const useTheme = useThemeStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.n-layout-header {
|
.n-layout-header {
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
|
||||||
import { setupRouterGuard } from './guard'
|
import { setupRouterGuard } from './guard'
|
||||||
import { basicRoutes } from './routes'
|
import { basicRoutes } from './routes'
|
||||||
|
|
||||||
|
const isHash = !!import.meta.env.VITE_APP_USE_HASH
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history: createWebHistory('/'),
|
history: isHash ? createWebHashHistory('/') : createWebHistory('/'),
|
||||||
routes: basicRoutes,
|
routes: basicRoutes,
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||||
})
|
})
|
||||||
|
|||||||
1
src/settings/index.js
Normal file
1
src/settings/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as themeSettings } from './theme.json'
|
||||||
21
src/settings/theme.json
Normal file
21
src/settings/theme.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"tags": {
|
||||||
|
"visible": true,
|
||||||
|
"height": 50
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"height": 60
|
||||||
|
},
|
||||||
|
"naiveThemeOverrides": {
|
||||||
|
"common": {
|
||||||
|
"primaryColor": "#316C72FF",
|
||||||
|
"primaryColorHover": "#316C72E3",
|
||||||
|
"primaryColorPressed": "#2B4C59FF",
|
||||||
|
"primaryColorSuppl": "#316C7263",
|
||||||
|
"successColor": "#316C72FF",
|
||||||
|
"successColorHover": "#316C72E3",
|
||||||
|
"successColorPressed": "#2B4C59FF",
|
||||||
|
"successColorSuppl": "#316C7263"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useAppStore = defineStore('app', {
|
|
||||||
state() {
|
|
||||||
return {
|
|
||||||
themeOverrides: {
|
|
||||||
common: {
|
|
||||||
primaryColor: '#316c72',
|
|
||||||
primaryColorSuppl: '#316c72',
|
|
||||||
primaryColorHover: '#316c72',
|
|
||||||
successColorHover: '#316c72',
|
|
||||||
successColorSuppl: '#316c72',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
7
src/store/modules/tags/helpers.js
Normal file
7
src/store/modules/tags/helpers.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createSessionStorage } from '@/utils/cache'
|
||||||
|
|
||||||
|
export const tagsSS = createSessionStorage({ prefixKey: 'tag_' })
|
||||||
|
export const activeTag = tagsSS.get('activeTag')
|
||||||
|
export const tags = tagsSS.get('tags')
|
||||||
|
|
||||||
|
export const WITHOUT_TAG_PATHS = ['/404', '/login', '/redirect']
|
||||||
26
src/store/modules/tags/index.js
Normal file
26
src/store/modules/tags/index.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { tagsSS, activeTag, tags, WITHOUT_TAG_PATHS } from './helpers'
|
||||||
|
|
||||||
|
export const useTagsStore = defineStore('tag', {
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
tags: tags || [],
|
||||||
|
activeTag: activeTag || '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setActiveTag(path) {
|
||||||
|
this.activeTag = path
|
||||||
|
tagsSS.set('activeTag', path)
|
||||||
|
},
|
||||||
|
addTag(tag = {}) {
|
||||||
|
if (WITHOUT_TAG_PATHS.includes(tag.path) || this.tags.some((item) => item.path === tag.path)) return
|
||||||
|
this.tags.push(tag)
|
||||||
|
tagsSS.set('tags', this.tags)
|
||||||
|
},
|
||||||
|
removeTag(path) {
|
||||||
|
this.tags = this.tags.filter((tag) => tag.path !== path)
|
||||||
|
tagsSS.set('tags', this.tags)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
13
src/store/modules/theme.js
Normal file
13
src/store/modules/theme.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { themeSettings } from '@/settings'
|
||||||
|
export const useThemeStore = defineStore('theme', {
|
||||||
|
state() {
|
||||||
|
return themeSettings
|
||||||
|
},
|
||||||
|
getters: {},
|
||||||
|
actions: {
|
||||||
|
setTabVisible(visible) {
|
||||||
|
this.tags.visible = visible
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { router } from '@/router'
|
import { router } from '@/router'
|
||||||
import { getToken, removeToken } from '@/utils/token'
|
import { getToken, removeToken } from '@/utils/token'
|
||||||
import { isWithoutToken } from './help'
|
import { isWithoutToken } from './helpers'
|
||||||
|
|
||||||
export function setupInterceptor(service) {
|
export function setupInterceptor(service) {
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="page-404">
|
<div class="page-404">
|
||||||
<n-result status="404" description="抱歉,您访问的页面不存在。">
|
<n-result status="404" description="抱歉,您访问的页面不存在。">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<img src="@/assets/images/404.png" width="500" />
|
<img src="@/assets/images/404.png" width="300" />
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<n-button strong secondary type="primary" @click="replace('/')">返回首页</n-button>
|
<n-button strong secondary type="primary" @click="replace('/')">返回首页</n-button>
|
||||||
@@ -18,10 +18,14 @@ const { replace } = useRouter()
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-404 {
|
.page-404 {
|
||||||
height: 100%;
|
|
||||||
min-height: calc(100vh - 60px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
bottom: 50px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
:columns="columns"
|
:columns="columns"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:row-key="(row) => row.id"
|
:row-key="(row) => row.id"
|
||||||
max-height="calc(100vh - 250px)"
|
|
||||||
@update:checked-row-keys="handleCheck"
|
@update:checked-row-keys="handleCheck"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div pb-20>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<input v-model="post.title" type="text" placeholder="输入文章标题..." class="title" />
|
<input v-model="post.title" type="text" placeholder="输入文章标题..." class="title" />
|
||||||
<n-button type="primary" style="width: 80px" :loading="btnLoading" @click="handleSavePost">保存</n-button>
|
<n-button type="primary" style="width: 80px" :loading="btnLoading" @click="handleSavePost">保存</n-button>
|
||||||
</div>
|
</div>
|
||||||
<MdEditor v-model="post.content" style="height: calc(100vh - 140px)" />
|
<MdEditor v-model="post.content" style="height: calc(100vh - 200px)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -34,31 +34,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="form-wrapper">
|
|
||||||
<h2 class="title">{{ title }}</h2>
|
|
||||||
<div class="form-item" mt-20>
|
|
||||||
<input
|
|
||||||
v-model="loginInfo.name"
|
|
||||||
autofocus
|
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
placeholder="username"
|
|
||||||
@keydown.enter="handleLogin"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="form-item" mt-20>
|
|
||||||
<input
|
|
||||||
v-model="loginInfo.password"
|
|
||||||
type="password"
|
|
||||||
class="input"
|
|
||||||
placeholder="password"
|
|
||||||
@keydown.enter="handleLogin"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="form-item" mt-20>
|
|
||||||
<button class="submit-btn" @click="handleLogin">登录</button>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -13,3 +13,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user