Compare commits

...
19 Commits
Author SHA1 Message Date
Hooray Hu 4883b7f492 chore: release v3.2.2 2023-08-26 12:20:14 +08:00
Hooray Hu bb0743f8f7 chore: 更新依赖 2023-08-26 12:19:19 +08:00
Hooray Hu 2bbf21d105 chore: 补充 process 引入 2023-08-26 12:18:40 +08:00
Hooray Hu ffd30c62b7 chore: release v3.2.1 2023-07-29 10:17:39 +08:00
Hooray Hu 86d0fc6d22 fix: plop 模板读取增加校验 2023-07-29 10:16:52 +08:00
Hooray Hu b7d212352e chore: 更新依赖,修改 ts 警告 2023-07-29 10:15:27 +08:00
Hooray Hu 24e922077f chore: 更新依赖 2023-07-04 15:28:52 +08:00
Hooray Hu d502b58649 chore: release v3.2.0 2023-06-22 17:26:02 +08:00
Hooray Hu eb64300242 chore: 更新依赖 2023-06-22 17:25:37 +08:00
Hooray Hu 1b7667ffc8 chore: 更新 readme 2023-06-14 16:14:12 +08:00
Hooray Hu 60cc6691cc chore: 更新依赖 2023-06-12 21:46:38 +08:00
Hooray Hu 94ce1ac6f9 Merge pull request #23 from lyric-zemin:chore-mitt
chore: eventBus类型完善
2023-06-05 20:22:07 +08:00
Hooray Hu 4172877fa5 chore: 调整为单行注释 2023-06-05 20:19:58 +08:00
wu.zeMin d736a08750 chore: eventBus类型完善 2023-06-05 14:17:35 +08:00
Hooray Hu 1a8529fd99 refactor!: 移除 el-icon 使用 2023-06-03 17:24:06 +08:00
Hooray Hu 0f347b6d72 refactor!: copyright 组件移动到 layouts 目录下 2023-06-02 00:27:40 +08:00
Hooray Hu 12d044ab99 chore: 优化 plop 启动脚本,自动读取模版目录 2023-06-01 23:46:44 +08:00
Hooray Hu 20c7c51527 chore: 代码微调 2023-06-01 23:35:06 +08:00
Hooray Hu 3dc4f750ad chore: 修改网址 2023-05-30 21:40:39 +08:00
51 changed files with 2666 additions and 2814 deletions
+2 -2
View File
@@ -18,6 +18,6 @@ jobs:
SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }}
with:
# 注意替换为你的 GitHub 源仓库地址
source-repo: git@github.com:hooray/fantastic-admin.git
source-repo: git@github.com:fantastic-admin/basic.git
# 注意替换为你的 Gitee 目标仓库地址
destination-repo: git@gitee.com:hooray/fantastic-admin.git
destination-repo: git@gitee.com:fantastic-admin/basic.git
-1
View File
@@ -1,7 +1,6 @@
{
"extends": [
"stylelint-stylistic/config",
"stylelint-config-standard",
"stylelint-config-standard-scss",
"stylelint-config-standard-vue/scss"
],
+26 -26
View File
@@ -1,5 +1,5 @@
<p align="center">
<img src="https://hooray.gitee.io/fantastic-admin/logo.png" width="200" height="200" />
<img src="https://fantastic-admin.gitee.io/logo.png" width="200" height="200" />
</p>
<h1 align="center">Fantastic-admin</h1>
@@ -7,21 +7,21 @@
<p align="center">一款<b>开箱即用</b>的 Vue 中后台管理系统框架</p>
<p align="center">
<a href="https://hooray.gitee.io/fantastic-admin" target="_blank">官网</a>
( <a href="https://hooray.github.io/fantastic-admin" target="_blank">备用</a> )
<a href="https://fantastic-admin.gitee.io" target="_blank">官网</a>
( <a href="https://fantastic-admin.github.io" target="_blank">备用</a> )
<span>&nbsp;|&nbsp;</span>
演示
( <a href="https://hooray.gitee.io/fantastic-admin-example" target="_blank">基础版</a>
<a href="https://hooray.gitee.io/fantastic-admin-pro-example" target="_blank">专业版</a> )
( <a href="https://fantastic-admin.gitee.io/basic-example" target="_blank">基础版</a>
<a href="https://fantastic-admin.gitee.io/pro-example" target="_blank">专业版</a> )
<span>&nbsp;|&nbsp;</span>
<a href="https://hooray.gitee.io/fantastic-admin/support.html" target="_blank">技术支持</a>
<a href="https://fantastic-admin.gitee.io/support.html" target="_blank">技术支持</a>
<span>&nbsp;|&nbsp;</span>
<a href="https://hooray.gitee.io/fantastic-admin/buy.html" target="_blank">购买</a>
<a href="https://fantastic-admin.gitee.io/buy.html" target="_blank">购买</a>
<p>
<p align="center">
<a href="###"><img src="https://img.shields.io/github/license/hooray/fantastic-admin?label=%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE&style=flat-square" /></a>
<a href="https://github.com/hooray/fantastic-admin/releases" target="_blank"><img src="https://img.shields.io/github/v/release/hooray/fantastic-admin?label=%E5%BD%93%E5%89%8D%E7%89%88%E6%9C%AC&style=flat-square" /></a>
<a href="###"><img src="https://img.shields.io/github/license/fantastic-admin/basic?label=%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE&style=flat-square" /></a>
<a href="https://github.com/fantastic-admin/basic/releases" target="_blank"><img src="https://img.shields.io/github/v/release/fantastic-admin/basic?label=%E5%BD%93%E5%89%8D%E7%89%88%E6%9C%AC&style=flat-square" /></a>
</p>
## 特点
@@ -38,12 +38,12 @@
## 分支说明
- `master` Vue3 版本框架源码分支,不含示例代码,可直接用于实际开发
- `main` Vue3 版本框架源码分支,不含示例代码,可直接用于实际开发
- `example` Vue3 版本演示源码分支,同线上演示站,包含大量示例,可用于参考学习
- `vue2` Vue2 版本框架源码分支,不含示例代码,可直接用于实际开发(停止维护)
- `vue2-example` Vue2 版本演示源码分支,包含大量示例,可用于参考学习(停止维护)
如果你更注重使用时的稳定性,推荐去 [Github Releases](https://github.com/hooray/fantastic-admin/releases) 页面下载最新稳定版本的压缩包。
如果你更注重使用时的稳定性,推荐去 [Github Releases](https://github.com/fantastic-admin/basic/releases) 页面下载最新稳定版本的压缩包。
## 预览
@@ -51,14 +51,14 @@
<table>
<tr>
<td><img src="https://hooray.gitee.io/fantastic-admin/preview1.png" /></td>
<td><img src="https://hooray.gitee.io/fantastic-admin/preview2.png" /></td>
<td><img src="https://hooray.gitee.io/fantastic-admin/preview3.png" /></td>
<td><img src="https://fantastic-admin.gitee.io/preview1.png" /></td>
<td><img src="https://fantastic-admin.gitee.io/preview2.png" /></td>
<td><img src="https://fantastic-admin.gitee.io/preview3.png" /></td>
</tr>
<tr>
<td><img src="https://hooray.gitee.io/fantastic-admin/preview4.png" /></td>
<td><img src="https://hooray.gitee.io/fantastic-admin/preview5.png" /></td>
<td><img src="https://hooray.gitee.io/fantastic-admin/preview6.png" /></td>
<td><img src="https://fantastic-admin.gitee.io/preview4.png" /></td>
<td><img src="https://fantastic-admin.gitee.io/preview5.png" /></td>
<td><img src="https://fantastic-admin.gitee.io/preview6.png" /></td>
</tr>
</table>
@@ -66,9 +66,9 @@
如果觉得 Fantastic-admin 这个框架不错,或者已经在使用了,希望你可以在 **Github** 或者 **Gitee(码云)** 帮我点个 ⭐ ,这将对我是极大的鼓励。
[![star](https://img.shields.io/github/stars/hooray/fantastic-admin?style=social)](https://github.com/hooray/fantastic-admin/stargazers)
[![star](https://img.shields.io/github/stars/fantastic-admin/basic?style=social)](https://github.com/fantastic-admin/basic)
[![star](https://gitee.com/hooray/fantastic-admin/badge/star.svg?theme=dark)](https://gitee.com/hooray/fantastic-admin/stargazers)
[![star](https://gitee.com/fantastic-admin/basic/badge/star.svg?theme=dark)](https://gitee.com/fantastic-admin/basic)
## 生态
@@ -88,7 +88,7 @@
<table>
<tr>
<th colspan="3" align="center">
<a href="https://hooray.gitee.io/one-step-admin/" target="_blank">One-step-admin</a>
<a href="https://one-step-admin.gitee.io" target="_blank">One-step-admin</a>
</th>
</tr>
<tr>
@@ -97,13 +97,13 @@
</th>
</tr>
<tr>
<td><img src="https://hooray.gitee.io/one-step-admin/preview1.png" /></td>
<td><img src="https://hooray.gitee.io/one-step-admin/preview2.png" /></td>
<td><img src="https://hooray.gitee.io/one-step-admin/preview3.png" /></td>
<td><img src="https://one-step-admin.gitee.io/preview1.png" /></td>
<td><img src="https://one-step-admin.gitee.io/preview2.png" /></td>
<td><img src="https://one-step-admin.gitee.io/preview3.png" /></td>
</tr>
<tr>
<td><img src="https://hooray.gitee.io/one-step-admin/preview4.png" /></td>
<td><img src="https://hooray.gitee.io/one-step-admin/preview5.png" /></td>
<td><img src="https://hooray.gitee.io/one-step-admin/preview6.png" /></td>
<td><img src="https://one-step-admin.gitee.io/preview4.png" /></td>
<td><img src="https://one-step-admin.gitee.io/preview5.png" /></td>
<td><img src="https://one-step-admin.gitee.io/preview6.png" /></td>
</tr>
</table>
+36 -37
View File
@@ -1,5 +1,5 @@
{
"version": "3.1.1",
"version": "3.2.2",
"scripts": {
"dev": "vite",
"build:test": "vue-tsc --noEmit && vite build --mode test",
@@ -18,69 +18,68 @@
"release": "bumpp"
},
"dependencies": {
"@vueuse/core": "^10.1.2",
"@vueuse/integrations": "^10.1.2",
"@vueuse/core": "^10.4.0",
"@vueuse/integrations": "^10.4.0",
"axios": "^1.4.0",
"dayjs": "^1.11.7",
"element-plus": "^2.3.5",
"eruda": "^3.0.0",
"hotkeys-js": "^3.10.2",
"dayjs": "^1.11.9",
"element-plus": "^2.3.10",
"eruda": "^3.0.1",
"hotkeys-js": "^3.12.0",
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
"mitt": "^3.0.1",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"path-to-regexp": "^6.2.1",
"pinia": "^2.1.3",
"pinia": "^2.1.6",
"qs": "^6.11.2",
"vconsole": "^3.15.0",
"vconsole": "^3.15.1",
"vue": "^3.3.4",
"vue-router": "^4.2.1"
"vue-router": "^4.2.4"
},
"devDependencies": {
"@antfu/eslint-config": "^0.39.3",
"@iconify-json/ep": "^1.1.10",
"@antfu/eslint-config": "^0.41.0",
"@iconify-json/ep": "^1.1.12",
"@iconify/vue": "^4.1.1",
"@types/nprogress": "^0.2.0",
"@types/path-browserify": "^1.0.0",
"@types/qs": "^6.9.7",
"@vitejs/plugin-legacy": "^4.0.3",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"autoprefixer": "^10.4.14",
"bumpp": "^9.1.0",
"cz-git": "^1.6.1",
"eslint": "^8.41.0",
"esno": "^0.16.3",
"@vitejs/plugin-legacy": "^4.1.1",
"@vitejs/plugin-vue": "^4.3.3",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"autoprefixer": "^10.4.15",
"bumpp": "^9.2.0",
"cz-git": "^1.7.1",
"eslint": "^8.48.0",
"esno": "^0.17.0",
"fs-extra": "^11.1.1",
"http-server": "^14.1.1",
"lint-staged": "^13.2.2",
"lint-staged": "^14.0.1",
"npm-run-all": "^4.1.5",
"plop": "^3.1.2",
"postcss-html": "^1.5.0",
"sass": "^1.62.1",
"simple-git-hooks": "^2.8.1",
"stylelint": "^15.6.2",
"stylelint-config-standard": "^33.0.0",
"stylelint-config-standard-scss": "^9.0.0",
"sass": "^1.66.1",
"simple-git-hooks": "^2.9.0",
"stylelint": "^15.10.3",
"stylelint-config-standard-scss": "^10.0.0",
"stylelint-config-standard-vue": "^1.0.0",
"stylelint-scss": "^5.0.0",
"stylelint-stylistic": "^0.4.2",
"stylelint-scss": "^5.1.0",
"stylelint-stylistic": "^0.4.3",
"svgo": "^3.0.2",
"terser": "^5.17.6",
"typescript": "^5.0.4",
"unplugin-auto-import": "^0.16.2",
"unplugin-vue-components": "^0.24.1",
"vite": "^4.3.8",
"terser": "^5.19.2",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.25.1",
"vite": "^4.4.9",
"vite-plugin-banner": "^0.7.0",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.8",
"vite-plugin-pages": "^0.30.1",
"vite-plugin-pages": "^0.31.0",
"vite-plugin-spritesmith": "^0.1.1",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-inspector": "^3.4.2",
"vite-plugin-vue-inspector": "^3.6.0",
"vite-plugin-vue-meta-layouts": "^0.2.2",
"vue-tsc": "^1.6.5"
"vue-tsc": "^1.8.8"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged",
+1 -1
View File
@@ -1,4 +1,4 @@
const fs = require('fs')
const fs = require('node:fs')
function getFolder(path) {
const components = []
+2 -2
View File
@@ -1,5 +1,5 @@
const path = require('path')
const fs = require('fs')
const path = require('node:path')
const fs = require('node:fs')
function getFolder(path) {
const components = []
+5 -3
View File
@@ -1,6 +1,8 @@
const fs = require('node:fs')
module.exports = function (plop) {
plop.setWelcomeMessage('请选择需要创建的模式:')
plop.setGenerator('page', require('./plop-templates/page/prompt'))
plop.setGenerator('component', require('./plop-templates/component/prompt'))
plop.setGenerator('store', require('./plop-templates/store/prompt'))
fs.readdirSync('./plop-templates').forEach((item) => {
fs.lstatSync(`./plop-templates/${item}`).isDirectory() && plop.setGenerator(item, require(`./plop-templates/${item}/prompt`))
})
}
+2341 -2404
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,4 +1,5 @@
import path from 'node:path'
import process from 'node:process'
import fs from 'fs-extra'
import { icons } from '@iconify-json/ep'
+2 -2
View File
@@ -15,7 +15,7 @@ const buttonConfig = ref({
// 侧边栏主导航当前实际宽度
const mainSidebarActualWidth = computed(() => {
let actualWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-main-sidebar-width'))
let actualWidth = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-main-sidebar-width'))
if (['head', 'single'].includes(settingsStore.settings.menu.menuMode)) {
actualWidth = 0
}
@@ -24,7 +24,7 @@ const mainSidebarActualWidth = computed(() => {
// 侧边栏次导航当前实际宽度
const subSidebarActualWidth = computed(() => {
let actualWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-width'))
let actualWidth = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-width'))
if (settingsStore.settings.menu.subMenuCollapse) {
actualWidth = 64
}
+4 -4
View File
@@ -1,12 +1,12 @@
<script setup lang="ts">
const props = defineProps<{
value: string | string[]
}>()
defineOptions({
name: 'Auth',
})
const props = defineProps<{
value: string | string[]
}>()
function check() {
return useAuth().auth(props.value)
}
+4 -4
View File
@@ -1,12 +1,12 @@
<script setup lang="ts">
const props = defineProps<{
value: string[]
}>()
defineOptions({
name: 'AuthAll',
})
const props = defineProps<{
value: string[]
}>()
function check() {
return useAuth().authAll(props.value)
}
+5 -7
View File
@@ -2,6 +2,10 @@
import type { UploadProps, UploadUserFile } from 'element-plus'
import { ElMessage } from 'element-plus'
defineOptions({
name: 'FileUpload',
})
const props = withDefaults(
defineProps<{
action: UploadProps['action']
@@ -32,10 +36,6 @@ const emits = defineEmits<{
]
}>()
defineOptions({
name: 'FileUpload',
})
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
const fileName = file.name.split('.')
const fileExt = fileName.at(-1) ?? ''
@@ -73,9 +73,7 @@ const onSuccess: UploadProps['onSuccess'] = (res, file, fileList) => {
drag
>
<div class="slot">
<el-icon class="el-icon--upload">
<svg-icon name="ep:upload-filled" />
</el-icon>
<svg-icon name="ep:upload-filled" class="el-icon--upload" />
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
</div>
+2 -2
View File
@@ -1,10 +1,10 @@
<script setup lang="ts">
const isBottom = ref(false)
defineOptions({
name: 'FixedActionBar',
})
const isBottom = ref(false)
onMounted(() => {
onScroll()
window.addEventListener('scroll', onScroll)
+5 -7
View File
@@ -1,4 +1,8 @@
<script setup lang="ts">
defineOptions({
name: 'ImagePreview',
})
const props = withDefaults(
defineProps<{
src: string
@@ -11,10 +15,6 @@ const props = withDefaults(
},
)
defineOptions({
name: 'ImagePreview',
})
const realWidth = computed(() => {
return typeof props.width === 'string' ? props.width : `${props.width}px`
})
@@ -28,9 +28,7 @@ const realHeight = computed(() => {
<el-image :src="src" fit="cover" :style="`width:${realWidth};height:${realHeight};`" :preview-src-list="[src]" preview-teleported>
<template #error>
<div class="image-slot">
<el-icon>
<svg-icon name="image-load-fail" />
</el-icon>
<svg-icon name="image-load-fail" />
</div>
</template>
</el-image>
+8 -14
View File
@@ -2,6 +2,10 @@
import type { UploadProps } from 'element-plus'
import { ElMessage } from 'element-plus'
defineOptions({
name: 'ImageUpload',
})
const props = withDefaults(
defineProps<{
action: UploadProps['action']
@@ -37,10 +41,6 @@ const emits = defineEmits<{
]
}>()
defineOptions({
name: 'ImageUpload',
})
const uploadData = ref({
imageViewerVisible: false,
progress: {
@@ -104,9 +104,7 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
<el-image v-if="url === ''" :src="url === '' ? placeholder : url" :style="`width:${width}px;height:${height}px;`" fit="fill">
<template #error>
<div class="image-slot" :style="`width:${width}px;height:${height}px;`">
<el-icon>
<svg-icon name="ep:plus" />
</el-icon>
<svg-icon name="ep:plus" />
</div>
</template>
</el-image>
@@ -115,14 +113,10 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
<div class="mask">
<div class="actions">
<span title="预览" @click.stop="preview">
<el-icon>
<svg-icon name="ep:zoom-in" />
</el-icon>
<svg-icon name="ep:zoom-in" />
</span>
<span title="移除" @click.stop="remove">
<el-icon>
<svg-icon name="ep:delete" />
</el-icon>
<svg-icon name="ep:delete" />
</span>
</div>
</div>
@@ -185,7 +179,7 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
transform: scale(1.5);
}
.el-icon {
.icon {
font-size: 24px;
}
}
+10 -20
View File
@@ -2,6 +2,10 @@
import type { UploadProps } from 'element-plus'
import { ElMessage } from 'element-plus'
defineOptions({
name: 'ImagesUpload',
})
const props = withDefaults(
defineProps<{
action: UploadProps['action']
@@ -39,10 +43,6 @@ const emits = defineEmits<{
]
}>()
defineOptions({
name: 'ImagesUpload',
})
const uploadData = ref({
dialogImageIndex: 0,
imageViewerVisible: false,
@@ -112,24 +112,16 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
<div class="mask">
<div class="actions">
<span title="预览" @click="preview(index)">
<el-icon>
<svg-icon name="ep:zoom-in" />
</el-icon>
<svg-icon name="ep:zoom-in" />
</span>
<span title="移除" @click="remove(index)">
<el-icon>
<svg-icon name="ep:delete" />
</el-icon>
<svg-icon name="ep:delete" />
</span>
<span v-show="url.length > 1" title="左移" :class="{ disabled: index === 0 }" @click="move(index, 'left')">
<el-icon>
<svg-icon name="ep:back" />
</el-icon>
<svg-icon name="ep:back" />
</span>
<span v-show="url.length > 1" title="右移" :class="{ disabled: index === url.length - 1 }" @click="move(index, 'right')">
<el-icon>
<svg-icon name="ep:right" />
</el-icon>
<svg-icon name="ep:right" />
</span>
</div>
</div>
@@ -148,9 +140,7 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
class="images-upload"
>
<div class="image-slot" :style="`width:${width}px;height:${height}px;`">
<el-icon>
<svg-icon name="ep:plus" />
</el-icon>
<svg-icon name="ep:plus" />
</div>
<div v-show="uploadData.progress.percent" class="progress" :style="`width:${width}px;height:${height}px;`">
<el-image :src="uploadData.progress.preview" :style="`width:${width}px;height:${height}px;`" fit="fill" />
@@ -218,7 +208,7 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
transform: scale(1.5);
}
.el-icon {
.icon {
font-size: 24px;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ defineOptions({
const router = useRouter()
const data = ref({
inter: NaN,
inter: Number.NaN,
countdown: 5,
})
+4 -4
View File
@@ -1,4 +1,8 @@
<script setup lang="ts">
defineOptions({
name: 'PageHeader',
})
withDefaults(
defineProps<{
title: string
@@ -9,10 +13,6 @@ withDefaults(
},
)
defineOptions({
name: 'PageHeader',
})
const slots = useSlots()
</script>
+5 -7
View File
@@ -1,4 +1,8 @@
<script setup lang="ts">
defineOptions({
name: 'PageMain',
})
const props = withDefaults(
defineProps<{
title?: string
@@ -12,10 +16,6 @@ const props = withDefaults(
},
)
defineOptions({
name: 'PageMain',
})
const titleSlot = !!useSlots().title
const collaspeData = ref(props.collaspe)
@@ -40,9 +40,7 @@ function unCollaspe() {
</div>
<slot />
<div v-if="collaspeData" class="collaspe" title="展开" @click="unCollaspe">
<el-icon>
<svg-icon name="ep:arrow-down" />
</el-icon>
<svg-icon name="ep:arrow-down" />
</div>
</div>
</template>
+4 -4
View File
@@ -2,6 +2,10 @@
// 行政区划数据来源于 https://github.com/modood/Administrative-divisions-of-China
import pcasRaw from './pcas-code.json'
defineOptions({
name: 'PcasCascader',
})
const props = withDefaults(
defineProps<{
modelValue: string[] | {
@@ -28,10 +32,6 @@ const emits = defineEmits<{
]
}>()
defineOptions({
name: 'PcasCascader',
})
interface pcasItem {
code: string
name: string
+5 -7
View File
@@ -1,4 +1,8 @@
<script setup lang="ts">
defineOptions({
name: 'SearchBar',
})
const props = withDefaults(
defineProps<{
fold?: boolean
@@ -21,10 +25,6 @@ const emits = defineEmits<{
]
}>()
defineOptions({
name: 'SearchBar',
})
const isFold = ref(props.fold)
watch(() => props.fold, (value) => {
@@ -44,9 +44,7 @@ function toggle() {
<div v-if="showToggle" class="toggle">
<el-button text size="small" @click="toggle">
<template #icon>
<el-icon>
<svg-icon :name="isFold ? 'ep:caret-bottom' : 'ep:caret-top' " />
</el-icon>
<svg-icon :name="isFold ? 'ep:caret-bottom' : 'ep:caret-top' " />
</template>
{{ isFold ? '展开' : '收起' }}
</el-button>
+46 -26
View File
@@ -1,48 +1,68 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue'
const props = withDefaults(
defineProps<{
name: string
flip?: 'horizontal' | 'vertical' | 'both' | ''
rotate?: number
}>(),
{
flip: '',
rotate: 0,
},
)
defineOptions({
name: 'SvgIcon',
})
const transformStyle = computed(() => {
const style = []
if (props.flip !== '') {
const props = defineProps<{
name: string
flip?: 'horizontal' | 'vertical' | 'both'
rotate?: number
color?: string
size?: string | number
}>()
const style = computed(() => {
const transform = []
if (props.flip) {
switch (props.flip) {
case 'horizontal':
style.push('rotateY(180deg)')
transform.push('rotateY(180deg)')
break
case 'vertical':
style.push('rotateX(180deg)')
transform.push('rotateX(180deg)')
break
case 'both':
style.push('rotateX(180deg)')
style.push('rotateY(180deg)')
transform.push('rotateX(180deg)')
transform.push('rotateY(180deg)')
break
}
}
if (props.rotate !== 0) {
style.push(`rotate(${props.rotate}deg)`)
if (props.rotate) {
transform.push(`rotate(${props.rotate % 360}deg)`)
}
return {
...(props.color && { color: props.color }),
...(props.size && { fontSize: typeof props.size === 'number' ? `${props.size}px` : props.size }),
...(transform.length && { transform: transform.join(' ') }),
}
return `transform: ${style.join(' ')};`
})
</script>
<template>
<Icon v-if="name.indexOf('ep:') === 0" :icon="name" :style="transformStyle" />
<svg v-else :style="transformStyle" aria-hidden="true">
<use :xlink:href="`#icon-${name}`" />
</svg>
<i class="icon" :style="style">
<Icon v-if="name.indexOf('ep:') === 0" :icon="name" />
<svg v-else aria-hidden="true">
<use :xlink:href="`#icon-${name}`" />
</svg>
</i>
</template>
<style lang="scss" scoped>
.icon {
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentcolor;
svg {
height: 1em;
width: 1em;
}
}
</style>
+7 -9
View File
@@ -1,4 +1,8 @@
<script setup lang="ts">
defineOptions({
name: 'Trend',
})
const props = withDefaults(
defineProps<{
value: string
@@ -15,10 +19,6 @@ const props = withDefaults(
},
)
defineOptions({
name: 'Trend',
})
const isUp = computed(() => {
let isUp = props.type === 'up'
if (props.reverse) {
@@ -33,9 +33,7 @@ const isUp = computed(() => {
<span v-if="prefix" class="prefix">{{ prefix }}</span>
<span class="text">{{ value }}</span>
<span v-if="suffix" class="suffix">{{ suffix }}</span>
<el-icon>
<svg-icon name="ep:caret-top" />
</el-icon>
<svg-icon name="ep:caret-top" />
</div>
</template>
@@ -52,12 +50,12 @@ const isUp = computed(() => {
&.down {
color: var(--el-color-danger);
.el-icon {
.icon {
transform: rotate(180deg);
}
}
.el-icon {
.icon {
margin-left: 5px;
transition: transform 0.3s;
}
+15 -45
View File
@@ -51,9 +51,7 @@ function handleCopy() {
<el-divider>颜色主题</el-divider>
<div class="color-scheme">
<div class="switch" :class="settingsStore.settings.app.colorScheme" @click="settingsStore.settings.app.colorScheme = settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark'">
<el-icon class="icon">
<svg-icon :name="settingsStore.settings.app.colorScheme === 'light' ? 'ep:sunny' : 'ep:moon'" />
</el-icon>
<svg-icon :name="settingsStore.settings.app.colorScheme === 'light' ? 'ep:sunny' : 'ep:moon'" />
</div>
</div>
<el-divider v-if="settingsStore.mode === 'pc'">
@@ -63,25 +61,19 @@ function handleCopy() {
<el-tooltip content="侧边栏模式(含主导航)" placement="top" :show-after="500">
<div class="mode mode-side" :class="{ active: settingsStore.settings.menu.menuMode === 'side' }" @click="settingsStore.settings.menu.menuMode = 'side'">
<div class="mode-container" />
<el-icon>
<svg-icon name="ep:check" />
</el-icon>
<svg-icon name="ep:check" />
</div>
</el-tooltip>
<el-tooltip content="顶部模式" placement="top" :show-after="500">
<div class="mode mode-head" :class="{ active: settingsStore.settings.menu.menuMode === 'head' }" @click="settingsStore.settings.menu.menuMode = 'head'">
<div class="mode-container" />
<el-icon>
<svg-icon name="ep:check" />
</el-icon>
<svg-icon name="ep:check" />
</div>
</el-tooltip>
<el-tooltip content="侧边栏模式(不含主导航)" placement="top" :show-after="500">
<div class="mode mode-single" :class="{ active: settingsStore.settings.menu.menuMode === 'single' }" @click="settingsStore.settings.menu.menuMode = 'single'">
<div class="mode-container" />
<el-icon>
<svg-icon name="ep:check" />
</el-icon>
<svg-icon name="ep:check" />
</div>
</el-tooltip>
</div>
@@ -90,9 +82,7 @@ function handleCopy() {
<div class="label">
主导航切换跳转
<el-tooltip content="开启该功能后,切换主导航时,页面自动跳转至该主导航下,次导航里第一个导航" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.menu.switchMainMenuAndPageJump" :disabled="['single'].includes(settingsStore.settings.menu.menuMode)" />
@@ -101,9 +91,7 @@ function handleCopy() {
<div class="label">
次导航保持展开一个
<el-tooltip content="开启该功能后,次导航只保持单个菜单的展开" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.menu.subMenuUniqueOpened" />
@@ -148,9 +136,7 @@ function handleCopy() {
<div class="label">
全屏
<el-tooltip content="该功能使用场景极少,用户习惯于通过窗口“最大化”功能来扩大显示区域,以显示更多内容,并且使用 F11 键也可以进入全屏效果" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.toolbar.enableFullscreen" />
@@ -159,9 +145,7 @@ function handleCopy() {
<div class="label">
页面刷新
<el-tooltip content="开启时会阻止原生 F5 键刷新功能,并采用框架提供的刷新模式进行页面刷新" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.toolbar.enablePageReload" />
@@ -170,9 +154,7 @@ function handleCopy() {
<div class="label">
颜色主题
<el-tooltip content="开启后可在明亮/暗黑模式中切换" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.toolbar.enableColorScheme" />
@@ -191,9 +173,7 @@ function handleCopy() {
<div class="label">
是否启用
<el-tooltip content="对导航进行快捷搜索" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.navSearch.enable" />
@@ -240,9 +220,7 @@ function handleCopy() {
<div class="label">
是否开启
<el-tooltip content="该功能开启时,登录成功默认进入主页,反之则默认进入导航栏里第一个导航页面" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.home.enable" />
@@ -258,9 +236,7 @@ function handleCopy() {
<div class="label">
组件尺寸
<el-tooltip content="全局设置 Element Plus 组件的默认尺寸大小" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-radio-group v-model="settingsStore.settings.app.elementSize" size="small">
@@ -285,9 +261,7 @@ function handleCopy() {
<div class="label">
载入进度条
<el-tooltip content="该功能开启时,跳转路由会看到页面顶部有进度条" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.app.enableProgress" />
@@ -296,9 +270,7 @@ function handleCopy() {
<div class="label">
动态标题
<el-tooltip content="该功能开启时,页面标题会显示当前路由标题,格式为“页面标题 - 网站名称”;关闭时则显示网站名称,网站名称在项目根目录下 .env.* 文件里配置" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
<svg-icon name="ep:question-filled" />
</el-tooltip>
</div>
<el-switch v-model="settingsStore.settings.app.enableDynamicTitle" />
@@ -306,9 +278,7 @@ function handleCopy() {
<template v-if="isSupported" #footer>
<el-button type="primary" @click="handleCopy">
<template #icon>
<el-icon>
<svg-icon name="ep:document-copy" />
</el-icon>
<svg-icon name="ep:document-copy" />
</template>
复制配置
</el-button>
+3 -5
View File
@@ -34,9 +34,7 @@ function handlerMouserScroll(event: WheelEvent) {
<template v-for="(item, index) in menuStore.allMenus" :key="index">
<div v-if="item.children && item.children.length !== 0" class="item-container" :class="{ active: index === menuStore.actived }">
<div class="item" @click="switchTo(index)">
<el-icon v-if="item.meta?.icon">
<svg-icon :name="item.meta.icon" />
</el-icon>
<svg-icon v-if="item.meta?.icon" :name="item.meta.icon" />
<span v-if="item.meta?.title">{{ item.meta.title }}</span>
</div>
</div>
@@ -147,7 +145,7 @@ header {
background-color: var(--g-header-menu-hover-bg);
}
.el-icon {
.icon {
font-size: 24px;
vertical-align: middle;
}
@@ -171,7 +169,7 @@ header {
:deep(.tools) {
padding: 0;
.buttons .item .el-icon {
.buttons .item .icon {
color: var(--g-header-color);
}
+4 -4
View File
@@ -2,6 +2,10 @@
import imgLogo from '@/assets/images/logo.png'
import useSettingsStore from '@/store/modules/settings'
defineOptions({
name: 'Logo',
})
withDefaults(
defineProps<{
showLogo?: boolean
@@ -13,10 +17,6 @@ withDefaults(
},
)
defineOptions({
name: 'Logo',
})
const settingsStore = useSettingsStore()
const title = ref(import.meta.env.VITE_APP_TITLE)
+2 -4
View File
@@ -25,9 +25,7 @@ const { switchTo } = useMenu()
active: index === menuStore.actived,
}" :title="item.meta?.title ?? '[ 无标题 ]'" @click="switchTo(index)"
>
<el-icon v-if="item.meta?.icon">
<svg-icon :name="item.meta.icon" />
</el-icon>
<svg-icon v-if="item.meta?.icon" :name="item.meta.icon" />
<span>{{ item.meta?.title ?? '[ 无标题 ]' }}</span>
</div>
</template>
@@ -89,7 +87,7 @@ const { switchTo } = useMenu()
background-color: var(--g-main-sidebar-menu-active-bg);
}
.el-icon {
.icon {
margin: 0 auto;
font-size: 24px;
}
+14 -26
View File
@@ -21,9 +21,9 @@ const menuStore = useMenuStore()
interface listTypes {
path: string
icon?: string
title?: string | Function
title?: string | (() => string)
link?: string
breadcrumb: (string | Function | undefined)[]
breadcrumb: any[]
}
const isShow = ref(false)
@@ -147,16 +147,16 @@ function hasChildren(item: RouteRecordRaw) {
}
return flag
}
function getSourceList(arr: RouteRecordRaw[], basePath?: string, icon?: string, breadcrumb?: (string | Function | undefined)[]) {
function getSourceList(arr: RouteRecordRaw[], basePath?: string, icon?: string, breadcrumb?: (string | (() => string))[]) {
arr.forEach((item) => {
if (item.meta?.sidebar !== false) {
const breadcrumbTemp = cloneDeep(breadcrumb) || []
if (item.children && hasChildren(item)) {
breadcrumbTemp.push(item.meta?.title)
item.meta?.title && breadcrumbTemp.push(item.meta?.title)
getSourceList(item.children, basePath ? [basePath, item.path].join('/') : item.path, item.meta?.icon ?? icon, breadcrumbTemp)
}
else {
breadcrumbTemp.push(item.meta?.title)
item.meta?.title && breadcrumbTemp.push(item.meta?.title)
sourceList.value.push({
path: basePath ? [basePath, item.path].join('/') : item.path,
icon: item.meta?.icon ?? icon,
@@ -168,15 +168,15 @@ function getSourceList(arr: RouteRecordRaw[], basePath?: string, icon?: string,
}
})
}
function getSourceListByMenus(arr: Menu.recordRaw[], icon?: string, breadcrumb?: (string | Function | undefined)[]) {
function getSourceListByMenus(arr: Menu.recordRaw[], icon?: string, breadcrumb?: (string | (() => string))[]) {
arr.forEach((item) => {
const breadcrumbTemp = cloneDeep(breadcrumb) || []
if (item.children && item.children.length > 0) {
breadcrumbTemp.push(item.meta?.title)
item.meta?.title && breadcrumbTemp.push(item.meta?.title)
getSourceListByMenus(item.children, item.meta?.icon ?? icon, breadcrumbTemp)
}
else {
breadcrumbTemp.push(item.meta?.title)
item.meta?.title && breadcrumbTemp.push(item.meta?.title)
sourceList.value.push({
icon: item.meta?.icon ?? icon,
title: item.meta?.title,
@@ -246,9 +246,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
<div class="search-box" @click.stop>
<el-input ref="searchInputRef" v-model="searchInput" placeholder="搜索页面,支持标题、URL模糊查询" clearable @keydown.esc="eventBus.emit('global-search-toggle')" @keydown.up.prevent="keyUp" @keydown.down.prevent="keyDown" @keydown.enter.prevent="keyEnter">
<template #prefix>
<el-icon>
<svg-icon name="ep:search" />
</el-icon>
<svg-icon name="ep:search" />
</template>
</el-input>
<div v-if="settingsStore.mode === 'pc'" class="tips">
@@ -262,14 +260,10 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
</div>
<div class="tip">
<el-tag type="info" size="large">
<el-icon>
<svg-icon name="search-up" />
</el-icon>
<svg-icon name="search-up" />
</el-tag>
<el-tag type="info" size="large">
<el-icon>
<svg-icon name="search-down" />
</el-icon>
<svg-icon name="search-down" />
</el-tag>
<el-tag type="info" size="large">
切换搜索结果
@@ -277,9 +271,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
</div>
<div class="tip">
<el-tag type="info" size="large">
<el-icon>
<svg-icon name="search-enter" />
</el-icon>
<svg-icon name="search-enter" />
</el-tag>
<el-tag type="info" size="large">
访问页面
@@ -297,9 +289,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
</div>
<div ref="searchResultRef" class="result">
<a v-for="(item, index) in resultList" :key="item.path" :ref="setSearchResultItemRef" class="item" :class="{ actived: index === actived }" @click="pageJump(item.path, item.link)" @mouseover="actived = index">
<el-icon class="icon">
<svg-icon v-if="item.icon" :name="item.icon" />
</el-icon>
<svg-icon v-if="item.icon" :name="item.icon" />
<div class="info">
<div class="title">
{{ item.title ?? '[ 无标题 ]' }}
@@ -307,9 +297,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
<div class="breadcrumb">
<span v-for="(bc, bcIndex) in item.breadcrumb" :key="bcIndex">
{{ bc ?? '[ 无标题 ]' }}
<el-icon>
<svg-icon name="ep:arrow-right" />
</el-icon>
<svg-icon name="ep:arrow-right" />
</span>
</div>
<div class="path">{{ item.path }}</div>
+6 -10
View File
@@ -3,6 +3,10 @@ import { resolveRoutePath } from '@/utils'
import useSettingsStore from '@/store/modules/settings'
import type { Menu } from '#/global'
defineOptions({
name: 'SidebarItem',
})
const props = withDefaults(
defineProps<{
item: Menu.recordRaw
@@ -13,10 +17,6 @@ const props = withDefaults(
},
)
defineOptions({
name: 'SidebarItem',
})
const settingsStore = useSettingsStore()
const hasChildren = computed(() => {
@@ -38,18 +38,14 @@ const hasChildren = computed(() => {
<router-link v-if="!hasChildren" v-slot="{ href, navigate, isActive, isExactActive }" custom :to="resolveRoutePath(basePath, item.path)">
<a :href="item.meta?.link ? item.meta.link : href" :class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']" :target="item.meta?.link ? '_blank' : '_self'" @click="navigate">
<el-menu-item :title="item.meta?.title ?? '[ 无标题 ]'" :index="resolveRoutePath(basePath, item.path || '')">
<el-icon v-if="item.meta?.icon" class="title-icon">
<svg-icon :name="item.meta.icon" />
</el-icon>
<svg-icon v-if="item.meta?.icon" :name="item.meta.icon" class="title-icon" />
<span class="title">{{ item.meta?.title ?? '[ 无标题 ]' }}</span>
</el-menu-item>
</a>
</router-link>
<el-sub-menu v-else :title="item.meta?.title ?? '[ 无标题 ]'" :index="settingsStore.settings.app.routeBaseOn !== 'filesystem' ? resolveRoutePath(basePath, item.path) : JSON.stringify(item)">
<template #title>
<el-icon v-if="item.meta?.icon" class="title-icon">
<svg-icon :name="item.meta.icon" />
</el-icon>
<svg-icon v-if="item.meta?.icon" :name="item.meta.icon" class="title-icon" />
<span class="title">{{ item.meta?.title ?? '[ 无标题 ]' }}</span>
</template>
<template v-for="route in (item.children as Menu.recordRaw[])">
+8 -20
View File
@@ -42,38 +42,26 @@ function userCommand(command: 'home' | 'setting' | 'hotkeys' | 'logout') {
<div class="tools">
<div class="buttons">
<span v-if="settingsStore.settings.navSearch.enable" class="item" @click="eventBus.emit('global-search-toggle')">
<el-icon>
<svg-icon name="ep:search" />
</el-icon>
<svg-icon name="ep:search" />
</span>
<span v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.enableFullscreen" class="item" @click="toggle">
<el-icon>
<svg-icon :name="isFullscreen ? 'fullscreen-exit' : 'fullscreen'" />
</el-icon>
<svg-icon :name="isFullscreen ? 'fullscreen-exit' : 'fullscreen'" />
</span>
<span v-if="settingsStore.settings.toolbar.enablePageReload" class="item" @click="mainPage.reload()">
<el-icon>
<svg-icon name="ep:refresh-right" />
</el-icon>
<svg-icon name="ep:refresh-right" />
</span>
<span v-if="settingsStore.settings.toolbar.enableColorScheme" class="item" @click="settingsStore.setColorScheme(settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark')">
<el-icon>
<svg-icon v-show="settingsStore.settings.app.colorScheme === 'light'" name="ep:sunny" />
<svg-icon v-show="settingsStore.settings.app.colorScheme === 'dark'" name="ep:moon" />
</el-icon>
<svg-icon v-show="settingsStore.settings.app.colorScheme === 'light'" name="ep:sunny" />
<svg-icon v-show="settingsStore.settings.app.colorScheme === 'dark'" name="ep:moon" />
</span>
</div>
<el-dropdown class="user-container" size="default" @command="userCommand">
<div class="user-wrapper">
<el-avatar size="small">
<el-icon>
<svg-icon name="ep:user-filled" />
</el-icon>
<svg-icon name="ep:user-filled" />
</el-avatar>
{{ userStore.account }}
<el-icon>
<svg-icon name="ep:caret-bottom" />
</el-icon>
<svg-icon name="ep:caret-bottom" />
</div>
<template #dropdown>
<el-dropdown-menu class="user-dropdown">
@@ -114,7 +102,7 @@ function userCommand(command: 'home' | 'setting' | 'hotkeys' | 'logout') {
cursor: pointer;
vertical-align: middle;
.el-icon {
.icon {
color: var(--el-text-color-primary);
transition: var(--el-transition-color);
}
+5 -7
View File
@@ -44,7 +44,7 @@ function onScroll() {
scrollTop.value = (document.documentElement || document.body).scrollTop
}
watch(scrollTop, (val, oldVal) => {
const topbarHeight = parseInt(getComputedStyle(document.documentElement || document.body).getPropertyValue('--g-topbar-height'))
const topbarHeight = Number.parseInt(getComputedStyle(document.documentElement || document.body).getPropertyValue('--g-topbar-height'))
scrollOnHide.value = settingsStore.settings.topbar.mode === 'sticky' && val > oldVal && val > topbarHeight
})
@@ -64,9 +64,7 @@ function pathCompile(path: string) {
>
<div class="left-box">
<div v-if="enableSubMenuCollapseButton" class="sidebar-collapse" :class="{ 'is-collapse': settingsStore.settings.menu.subMenuCollapse }" @click="settingsStore.toggleSidebarCollapse()">
<el-icon>
<svg-icon name="toolbar-collapse" />
</el-icon>
<svg-icon name="toolbar-collapse" />
</div>
<el-breadcrumb v-if="settingsStore.settings.breadcrumb.enable && settingsStore.mode === 'pc' && settingsStore.settings.app.routeBaseOn !== 'filesystem'">
<transition-group name="breadcrumb">
@@ -119,16 +117,16 @@ function pathCompile(path: string) {
height: 50px;
cursor: pointer;
.el-icon {
.icon {
color: var(--el-text-color-primary);
transition: var(--el-transition-color), var(--el-transition-md-fade);
}
&:hover .el-icon {
&:hover .icon {
color: var(--el-color-primary);
}
&.is-collapse .el-icon {
&.is-collapse .icon {
transform: rotateZ(-180deg);
}
+1 -3
View File
@@ -23,9 +23,7 @@ function open() {
</div>
<el-button type="primary" plain round @click="open">
<template #icon>
<el-icon>
<svg-icon name="ep:link" />
</el-icon>
<svg-icon name="ep:link" />
</template>
立即访问
</el-button>
+4 -5
View File
@@ -1,6 +1,5 @@
<script setup lang="ts">
import hotkeys from 'hotkeys-js'
import LinkView from './components/views/link.vue'
import Header from './components/Header/index.vue'
import MainSidebar from './components/MainSidebar/index.vue'
import SubSidebar from './components/SubSidebar/index.vue'
@@ -8,6 +7,8 @@ import Topbar from './components/Topbar/index.vue'
import Search from './components/Search/index.vue'
import HotkeysIntro from './components/HotkeysIntro/index.vue'
import AppSetting from './components/AppSetting/index.vue'
import LinkView from './components/views/link.vue'
import Copyright from './components/Copyright/index.vue'
import useSettingsStore from '@/store/modules/settings'
import useKeepAliveStore from '@/store/modules/keepAlive'
import useMenuStore from '@/store/modules/menu'
@@ -94,9 +95,7 @@ onUnmounted(() => {
<Search />
<HotkeysIntro />
<div v-if="settingsStore.settings.app.enableAppSetting">
<el-icon class="app-setting" @click="eventBus.emit('global-app-setting-toggle')">
<svg-icon name="ep:setting" />
</el-icon>
<svg-icon name="ep:setting" class="app-setting" @click="eventBus.emit('global-app-setting-toggle')" />
<AppSetting />
</div>
</div>
@@ -246,7 +245,7 @@ header:not(.header-leave-active) + .wrapper {
background-color: var(--el-color-primary);
cursor: pointer;
svg {
:deep(svg) {
animation: rotate 5s linear infinite;
}
+1 -1
View File
@@ -80,7 +80,7 @@ router.beforeEach(async (to, from, next) => {
}
// 注册并记录路由数据
// 记录的数据会在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
const removeRoutes: Function[] = []
const removeRoutes: (() => void)[] = []
routeStore.flatRoutes.forEach((route) => {
if (!/^(https?:|mailto:|tel:)/.test(route.path)) {
removeRoutes.push(router.addRoute(route as RouteRecordRaw))
+2 -2
View File
@@ -17,7 +17,7 @@ const useRouteStore = defineStore(
const isGenerate = ref(false)
const routesRaw = ref<Route.recordMainRaw[]>([])
const filesystemRoutesRaw = ref<RouteRecordRaw[]>([])
const currentRemoveRoutes = ref<Function[]>([])
const currentRemoveRoutes = ref<(() => void)[]>([])
// 将多层嵌套路由处理成两层,保留顶层和最子层路由,中间层级将被拍平
function flatAsyncRoutes<T extends RouteRecordRaw>(route: T): T {
@@ -216,7 +216,7 @@ const useRouteStore = defineStore(
isGenerate.value = true
}
// 记录 accessRoutes 路由,用于登出时删除路由
function setCurrentRemoveRoutes(routes: Function[]) {
function setCurrentRemoveRoutes(routes: (() => void)[]) {
currentRemoveRoutes.value = routes
}
// 清空动态路由
+1 -1
View File
@@ -17,7 +17,7 @@ const useUserStore = defineStore(
const isLogin = computed(() => {
let retn = false
if (token.value) {
if (new Date().getTime() < parseInt(failure_time.value) * 1000) {
if (new Date().getTime() < Number.parseInt(failure_time.value) * 1000) {
retn = true
}
}
+1
View File
@@ -1,6 +1,7 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
+1 -4
View File
@@ -3,15 +3,12 @@
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
declare module 'vue' {
export interface GlobalComponents {
Auth: typeof import('./../components/Auth/index.vue')['default']
AuthAll: typeof import('./../components/AuthAll/index.vue')['default']
Copyright: typeof import('./../components/Copyright/index.vue')['default']
FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default']
ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
+3 -3
View File
@@ -211,7 +211,7 @@ declare namespace Settings {
declare module 'vue-router' {
interface RouteMeta {
title?: string | Function
title?: string | (() => string)
i18n?: string
icon?: string
activeIcon?: string
@@ -229,7 +229,7 @@ declare module 'vue-router' {
declare namespace Route {
interface recordMainRaw {
meta?: {
title?: string | Function
title?: string | (() => string)
icon?: string
auth?: string | string[]
}
@@ -237,7 +237,7 @@ declare namespace Route {
}
interface breadcrumb {
path: string
title?: string | Function
title?: string | (() => string)
hide: boolean
}
}
+13 -20
View File
@@ -1,20 +1,13 @@
declare interface Window {
webkitDevicePixelRatio: any
mozDevicePixelRatio: any
}
declare module '*.vue' {
import { type DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare const __SYSTEM_INFO__: {
pkg: {
version: string
dependencies: Recordable<string>
devDependencies: Recordable<string>
}
lastBuildTime: string
}
declare interface Window {
webkitDevicePixelRatio: any
mozDevicePixelRatio: any
}
declare const __SYSTEM_INFO__: {
pkg: {
version: string
dependencies: Recordable<string>
devDependencies: Recordable<string>
}
lastBuildTime: string
}
+13 -1
View File
@@ -1,3 +1,15 @@
import mitt from 'mitt'
export default mitt()
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type Events = {
// 应用设置弹窗
'global-app-setting-toggle': undefined
// 系统信息弹窗
'global-system-info-toggle': undefined
// 快捷键弹窗
'global-hotkeys-intro-toggle': undefined
// 搜索框弹窗
'global-search-toggle': undefined
}
export default mitt<Events>()
+2 -2
View File
@@ -5,10 +5,10 @@ if (import.meta.env.PROD) {
const copyright_main_style = `${copyright_common_style} background: #e24329;`
const copyright_sub_style = `${copyright_common_style} background: #707070;`
if ((navigator.language).toLowerCase() === 'zh-cn') {
console.info('%c由%cFantastic-admin%c驱动', copyright_sub_style, copyright_main_style, copyright_sub_style, '\nhttps://gitee.com/hooray/fantastic-admin')
console.info('%c由%cFantastic-admin%c驱动', copyright_sub_style, copyright_main_style, copyright_sub_style, '\nhttps://fantastic-admin.gitee.io')
}
else {
console.info('%cPowered by%cFantastic-admin', copyright_sub_style, copyright_main_style, '\nhttps://github.com/hooray/fantastic-admin')
console.info('%cPowered by%cFantastic-admin', copyright_sub_style, copyright_main_style, '\nhttps://fantastic-admin.github.io')
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ meta:
const router = useRouter()
const data = ref({
inter: NaN,
inter: Number.NaN,
countdown: 5,
})
+22 -24
View File
@@ -26,12 +26,12 @@ const fantasticAdminInfo = ref({
imageVisible: false,
index: 0,
data: [
'https://hooray.gitee.io/fantastic-admin/preview1.png',
'https://hooray.gitee.io/fantastic-admin/preview2.png',
'https://hooray.gitee.io/fantastic-admin/preview3.png',
'https://hooray.gitee.io/fantastic-admin/preview4.png',
'https://hooray.gitee.io/fantastic-admin/preview5.png',
'https://hooray.gitee.io/fantastic-admin/preview6.png',
'https://fantastic-admin.gitee.io/preview1.png',
'https://fantastic-admin.gitee.io/preview2.png',
'https://fantastic-admin.gitee.io/preview3.png',
'https://fantastic-admin.gitee.io/preview4.png',
'https://fantastic-admin.gitee.io/preview5.png',
'https://fantastic-admin.gitee.io/preview6.png',
],
})
@@ -39,12 +39,12 @@ const oneStepAdminInfo = ref({
imageVisible: false,
index: 0,
data: [
'https://hooray.gitee.io/one-step-admin/preview1.png',
'https://hooray.gitee.io/one-step-admin/preview2.png',
'https://hooray.gitee.io/one-step-admin/preview3.png',
'https://hooray.gitee.io/one-step-admin/preview4.png',
'https://hooray.gitee.io/one-step-admin/preview5.png',
'https://hooray.gitee.io/one-step-admin/preview6.png',
'https://one-step-admin.gitee.io/preview1.png',
'https://one-step-admin.gitee.io/preview2.png',
'https://one-step-admin.gitee.io/preview3.png',
'https://one-step-admin.gitee.io/preview4.png',
'https://one-step-admin.gitee.io/preview5.png',
'https://one-step-admin.gitee.io/preview6.png',
],
})
</script>
@@ -61,22 +61,20 @@ const oneStepAdminInfo = ref({
</div>
</template>
<el-button-group>
<el-button type="success" size="large" plain @click="open('https://hooray.gitee.io/fantastic-admin/')">
<el-button type="success" size="large" plain @click="open('https://fantastic-admin.gitee.io')">
开发文档
</el-button>
<el-dropdown>
<el-button type="primary" size="large">
代码仓库
<el-icon class="el-icon--right">
<svg-icon name="ep:arrow-down" />
</el-icon>
<svg-icon name="ep:arrow-down" class="el-icon--right" />
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="open('https://gitee.com/hooray/fantastic-admin')">
<el-dropdown-item @click="open('https://gitee.com/fantastic-admin/basic')">
Gitee
</el-dropdown-item>
<el-dropdown-item @click="open('https://github.com/hooray/fantastic-admin')">
<el-dropdown-item @click="open('https://github.com/fantastic-admin/basic')">
Github
</el-dropdown-item>
</el-dropdown-menu>
@@ -117,17 +115,17 @@ const oneStepAdminInfo = ref({
<page-main class="ecology fa">
<template #title>
<div class="title-info">
<img src="https://hooray.gitee.io/fantastic-admin/logo.png">
<img src="https://fantastic-admin.gitee.io/logo.png">
<div>
<h1>Fantastic-admin</h1>
<h2>一款开箱即用的 Vue 中后台管理系统框架</h2>
</div>
</div>
<el-dropdown split-button type="primary" @click="open('https://hooray.gitee.io/fantastic-admin/')">
<el-dropdown split-button type="primary" @click="open('https://fantastic-admin.gitee.io')">
访问官网
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="open('https://hooray.github.io/fantastic-admin/')">
<el-dropdown-item @click="open('https://fantastic-admin.github.io')">
备用地址
</el-dropdown-item>
</el-dropdown-menu>
@@ -146,17 +144,17 @@ const oneStepAdminInfo = ref({
<page-main class="ecology osa">
<template #title>
<div class="title-info">
<img src="https://hooray.gitee.io/one-step-admin/logo.png">
<img src="https://one-step-admin.gitee.io/logo.png">
<div>
<h1>One-step-admin</h1>
<h2>一款干啥都快人一步的 Vue 中后台系统框架</h2>
</div>
</div>
<el-dropdown split-button type="primary" @click="open('https://hooray.gitee.io/one-step-admin/')">
<el-dropdown split-button type="primary" @click="open('https://one-step-admin.gitee.io')">
访问官网
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="open('https://hooray.github.io/one-step-admin/')">
<el-dropdown-item @click="open('https://one-step-admin.github.io')">
备用地址
</el-dropdown-item>
</el-dropdown-menu>
+10 -27
View File
@@ -8,6 +8,7 @@ meta:
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import Copyright from '@/layouts/components/Copyright/index.vue'
import useUserStore from '@/store/modules/user'
defineOptions({
@@ -164,18 +165,14 @@ function testAccount(account: string) {
<el-form-item prop="account">
<el-input v-model="loginForm.account" placeholder="用户名" text tabindex="1" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="ep:user" />
</el-icon>
<svg-icon name="ep:user" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" placeholder="密码" tabindex="2" autocomplete="on" show-password @keyup.enter="handleLogin">
<template #prefix>
<el-icon>
<svg-icon name="ep:lock" />
</el-icon>
<svg-icon name="ep:lock" />
</template>
</el-input>
</el-form-item>
@@ -217,18 +214,14 @@ function testAccount(account: string) {
<el-form-item prop="account">
<el-input v-model="registerForm.account" placeholder="用户名" tabindex="1" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="ep:user" />
</el-icon>
<svg-icon name="ep:user" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="captcha">
<el-input v-model="registerForm.captcha" placeholder="验证码" tabindex="2" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="ep:key" />
</el-icon>
<svg-icon name="ep:key" />
</template>
<template #append>
<el-button>发送验证码</el-button>
@@ -238,18 +231,14 @@ function testAccount(account: string) {
<el-form-item prop="password">
<el-input v-model="registerForm.password" type="password" placeholder="密码" tabindex="3" autocomplete="on" show-password>
<template #prefix>
<el-icon>
<svg-icon name="ep:lock" />
</el-icon>
<svg-icon name="ep:lock" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="checkPassword">
<el-input v-model="registerForm.checkPassword" type="password" placeholder="确认密码" tabindex="4" autocomplete="on" show-password>
<template #prefix>
<el-icon>
<svg-icon name="ep:lock" />
</el-icon>
<svg-icon name="ep:lock" />
</template>
</el-input>
</el-form-item>
@@ -274,18 +263,14 @@ function testAccount(account: string) {
<el-form-item prop="account">
<el-input v-model="resetForm.account" placeholder="用户名" tabindex="1" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="ep:user" />
</el-icon>
<svg-icon name="ep:user" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="captcha">
<el-input v-model="resetForm.captcha" placeholder="验证码" tabindex="2" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="ep:key" />
</el-icon>
<svg-icon name="ep:key" />
</template>
<template #append>
<el-button>发送验证码</el-button>
@@ -295,9 +280,7 @@ function testAccount(account: string) {
<el-form-item prop="newPassword">
<el-input v-model="resetForm.newPassword" type="password" placeholder="新密码" tabindex="3" autocomplete="on" show-password>
<template #prefix>
<el-icon>
<svg-icon name="ep:lock" />
</el-icon>
<svg-icon name="ep:lock" />
</template>
</el-input>
</el-form-item>
+1 -2
View File
@@ -6,7 +6,6 @@ meta:
</route>
<script setup lang="ts">
import type { UploadProps } from 'element-plus'
import { ElMessage } from 'element-plus'
defineOptions({
@@ -23,7 +22,7 @@ const form = ref({
wechat: '',
})
const handleSuccess: UploadProps['onSuccess'] = (res) => {
function handleSuccess(res: any) {
if (res.error === '') {
form.value.headimg = res.data.path
}
+4 -1
View File
@@ -1,5 +1,6 @@
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { defineConfig, loadEnv } from 'vite'
import dayjs from 'dayjs'
import pkg from './package.json'
@@ -11,7 +12,9 @@ export default ({ mode, command }) => {
// 全局 scss 资源
const scssResources = []
fs.readdirSync('src/assets/styles/resources').forEach((dirname) => {
if (fs.statSync(`src/assets/styles/resources/${dirname}`).isFile()) { scssResources.push(`@use "src/assets/styles/resources/${dirname}" as *;`) }
if (fs.statSync(`src/assets/styles/resources/${dirname}`).isFile()) {
scssResources.push(`@use "src/assets/styles/resources/${dirname}" as *;`)
}
})
// css 精灵图相关
fs.readdirSync('src/assets/sprites').forEach((dirname) => {
+2 -2
View File
@@ -5,8 +5,8 @@ export default function createBanner() {
/**
* 由 Fantastic-admin 提供技术支持
* Powered by Fantastic-admin
* Gitee https://gitee.com/hooray/fantastic-admin
* Github https://github.com/hooray/fantastic-admin
* Gitee https://fantastic-admin.gitee.io
* Github https://fantastic-admin.github.io
*/
`)
}
+1
View File
@@ -1,4 +1,5 @@
import path from 'node:path'
import process from 'node:process'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
export default function createSvgIcon(isBuild) {