mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-06 21:23:28 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25458835e9 | ||
|
|
abc0ac8e4c | ||
|
|
0e222b986d | ||
|
|
925ee00287 | ||
|
|
d9291175b9 | ||
|
|
c2e9b16917 | ||
|
|
c0881951d3 | ||
|
|
f0c6ddd221 | ||
|
|
07b9e31b22 | ||
|
|
a0708253af | ||
|
|
a1e1f2e4db | ||
|
|
b93c86aa44 | ||
|
|
49e78d5c20 | ||
|
|
33ea08fa4b | ||
|
|
0e89630b54 | ||
|
|
d6d67d357d | ||
|
|
848e107388 |
@@ -1,2 +0,0 @@
|
||||
dist/
|
||||
node_modules/
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": "@antfu"
|
||||
"extends": "@antfu",
|
||||
"rules": {
|
||||
"curly": ["error", "all"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ dist-ssr
|
||||
*.local
|
||||
src/assets/sprites/*.*
|
||||
!src/assets/sprites/.gitkeep
|
||||
public/icons
|
||||
|
||||
+4
-7
@@ -1,14 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-standard-scss",
|
||||
"stylelint-config-standard-vue/scss"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.scss"],
|
||||
"customSyntax": "postcss-scss"
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
@@ -18,6 +13,8 @@
|
||||
"property-no-unknown": null,
|
||||
"font-family-no-missing-generic-family-keyword": null,
|
||||
"selector-class-pattern": null,
|
||||
"max-line-length": null
|
||||
"max-line-length": null,
|
||||
"scss/double-slash-comment-empty-line-before": null,
|
||||
"scss/no-global-function-names": null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
( <a href="https://hooray.github.io/fantastic-admin" target="_blank">备用</a> )
|
||||
<span> | </span>
|
||||
演示
|
||||
( <a href="https://hooray.gitee.io/fantastic-admin/vue3/basic" target="_blank">基础版</a>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/vue3/pro" target="_blank">专业版</a> )
|
||||
( <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> )
|
||||
<span> | </span>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/support.html" target="_blank">技术支持</a>
|
||||
<span> | </span>
|
||||
|
||||
+14
-15
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:test": "vue-tsc --noEmit && vite build --mode test",
|
||||
@@ -18,11 +18,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tinymce/tinymce-vue": "^5.0.0",
|
||||
"@vueuse/core": "^9.2.0",
|
||||
"@vueuse/integrations": "^9.2.0",
|
||||
"@vueuse/core": "^9.3.0",
|
||||
"@vueuse/integrations": "^9.3.0",
|
||||
"axios": "^0.27.2",
|
||||
"dayjs": "^1.11.5",
|
||||
"element-plus": "^2.2.17",
|
||||
"element-plus": "^2.2.18",
|
||||
"hotkeys-js": "^3.10.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
@@ -30,25 +30,25 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^6.2.1",
|
||||
"pinia": "^2.0.22",
|
||||
"pinia": "^2.0.23",
|
||||
"qs": "^6.11.0",
|
||||
"tinymce": "^6.2.0",
|
||||
"vue": "^3.2.39",
|
||||
"vue": "^3.2.41",
|
||||
"vue-router": "^4.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^0.27.0",
|
||||
"@iconify-json/ep": "^1.1.7",
|
||||
"@iconify-json/ep": "^1.1.8",
|
||||
"@iconify/vue": "^4.0.0",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/path-browserify": "^1.0.0",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@vitejs/plugin-vue": "^3.1.0",
|
||||
"@vitejs/plugin-vue": "^3.1.2",
|
||||
"@vitejs/plugin-vue-jsx": "^2.0.1",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"bumpp": "^8.2.1",
|
||||
"cz-git": "^1.3.11",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint": "^8.25.0",
|
||||
"esno": "^0.16.3",
|
||||
"fs-extra": "^10.1.0",
|
||||
"http-server": "^14.1.1",
|
||||
@@ -56,18 +56,17 @@
|
||||
"lint-staged": "^13.0.3",
|
||||
"plop": "^3.1.1",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-scss": "^4.0.5",
|
||||
"sass": "^1.55.0",
|
||||
"stylelint": "^14.12.1",
|
||||
"stylelint": "^14.13.0",
|
||||
"stylelint-config-standard": "^28.0.0",
|
||||
"stylelint-config-standard-scss": "^5.0.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-scss": "^4.3.0",
|
||||
"svgo": "^2.8.0",
|
||||
"typescript": "^4.8.3",
|
||||
"typescript": "^4.8.4",
|
||||
"unplugin-auto-import": "^0.11.2",
|
||||
"unplugin-vue-components": "^0.22.7",
|
||||
"vite": "^3.1.3",
|
||||
"unplugin-vue-components": "^0.22.8",
|
||||
"vite": "^3.1.8",
|
||||
"vite-plugin-banner": "^0.5.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
@@ -78,7 +77,7 @@
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-layouts": "^0.6.0",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vue-tsc": "^0.40.13"
|
||||
"vue-tsc": "^1.0.7"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
@@ -36,22 +36,23 @@ module.exports = {
|
||||
name: 'name',
|
||||
message: '请输入组件名称',
|
||||
validate: (v) => {
|
||||
if (!v || v.trim === '')
|
||||
if (!v || v.trim === '') {
|
||||
return '组件名称不能为空'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
actions: (data) => {
|
||||
let path = ''
|
||||
if (data.isGlobal)
|
||||
if (data.isGlobal) {
|
||||
path = 'src/components/{{properCase name}}/index.vue'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
path = `${data.path}/components/{{properCase name}}/index.vue`
|
||||
|
||||
}
|
||||
const actions = [
|
||||
{
|
||||
type: 'add',
|
||||
|
||||
@@ -28,11 +28,12 @@ module.exports = {
|
||||
name: 'name',
|
||||
message: '请输入文件名',
|
||||
validate: (v) => {
|
||||
if (!v || v.trim === '')
|
||||
if (!v || v.trim === '') {
|
||||
return '文件名不能为空'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -4,8 +4,8 @@ const use{{ properCase name }}Store = defineStore(
|
||||
{
|
||||
state: () => ({}),
|
||||
getters: {},
|
||||
actions: {}
|
||||
}
|
||||
actions: {},
|
||||
},
|
||||
)
|
||||
|
||||
export default use{{ properCase name }}Store
|
||||
|
||||
@@ -6,11 +6,12 @@ module.exports = {
|
||||
name: 'name',
|
||||
message: '请输入模块名称',
|
||||
validate: (v) => {
|
||||
if (!v || v.trim === '')
|
||||
if (!v || v.trim === '') {
|
||||
return '模块名称不能为空'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -18,7 +19,7 @@ module.exports = {
|
||||
const actions = [
|
||||
{
|
||||
type: 'add',
|
||||
path: 'src/store/modules/{{camelCase name}}.js',
|
||||
path: 'src/store/modules/{{camelCase name}}.ts',
|
||||
templateFile: 'plop-templates/store/index.hbs',
|
||||
},
|
||||
]
|
||||
|
||||
Generated
+487
-487
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+8
-7
@@ -14,27 +14,28 @@ const buttonConfig = ref({
|
||||
// 侧边栏主导航当前实际宽度
|
||||
const mainSidebarActualWidth = computed(() => {
|
||||
let actualWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-main-sidebar-width'))
|
||||
if (['head', 'single'].includes(settingsStore.menu.menuMode))
|
||||
if (['head', 'single'].includes(settingsStore.menu.menuMode)) {
|
||||
actualWidth = 0
|
||||
|
||||
}
|
||||
return `${actualWidth}px`
|
||||
})
|
||||
|
||||
// 侧边栏次导航当前实际宽度
|
||||
const subSidebarActualWidth = computed(() => {
|
||||
let actualWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-width'))
|
||||
if (settingsStore.menu.subMenuCollapse)
|
||||
if (settingsStore.menu.subMenuCollapse) {
|
||||
actualWidth = 64
|
||||
|
||||
}
|
||||
return `${actualWidth}px`
|
||||
})
|
||||
|
||||
watch(() => settingsStore.app.colorScheme, () => {
|
||||
if (settingsStore.app.colorScheme === 'dark')
|
||||
if (settingsStore.app.colorScheme === 'dark') {
|
||||
document.documentElement.classList.add('dark')
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
+8
-8
@@ -25,9 +25,9 @@ api.interceptors.request.use(
|
||||
* 全局拦截请求发送前提交的参数
|
||||
* 以下代码为示例,在请求头里带上 token 信息
|
||||
*/
|
||||
if (userStore.isLogin && request.headers)
|
||||
if (userStore.isLogin && request.headers) {
|
||||
request.headers.Token = userStore.token
|
||||
|
||||
}
|
||||
// 是否将 POST 请求参数进行字符串化处理
|
||||
if (request.method === 'post') {
|
||||
// request.data = qs.stringify(request.data, {
|
||||
@@ -63,15 +63,15 @@ api.interceptors.response.use(
|
||||
},
|
||||
(error) => {
|
||||
let message = error.message
|
||||
if (message === 'Network Error')
|
||||
if (message === 'Network Error') {
|
||||
message = '后端网络故障'
|
||||
|
||||
else if (message.includes('timeout'))
|
||||
}
|
||||
else if (message.includes('timeout')) {
|
||||
message = '接口请求超时'
|
||||
|
||||
else if (message.includes('Request failed with status code'))
|
||||
}
|
||||
else if (message.includes('Request failed with status code')) {
|
||||
message = `接口${message.substr(message.length - 3)}异常`
|
||||
|
||||
}
|
||||
ElMessage({
|
||||
message,
|
||||
type: 'error',
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
"overlay": #1d1e1f,
|
||||
)
|
||||
);
|
||||
|
||||
@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
|
||||
|
||||
@@ -5,6 +5,5 @@
|
||||
// )
|
||||
// )
|
||||
// );
|
||||
|
||||
@use "element-plus/theme-chalk/src/index.scss" as *;
|
||||
@use "./dark.scss";
|
||||
|
||||
@@ -82,3 +82,7 @@ textarea {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-loading-mask {
|
||||
z-index: 900 !important;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// 文字超出隐藏,默认为单行超出隐藏,可设置多行
|
||||
@mixin text-overflow($line: 1, $fixed-width: true) {
|
||||
@if ($line == 1 and $fixed-width == true) {
|
||||
@if $line == 1 and $fixed-width == true {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@else {
|
||||
} @else {
|
||||
/* stylelint-disable-next-line value-no-vendor-prefix */
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
@@ -19,17 +17,17 @@
|
||||
@mixin position-center($type: x) {
|
||||
position: absolute;
|
||||
|
||||
@if ($type == x) {
|
||||
@if $type == x {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@if ($type == y) {
|
||||
@if $type == y {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@if ($type == xy) {
|
||||
@if $type == xy {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
|
||||
@@ -15,9 +15,9 @@ const emit = defineEmits(['checkAll', 'checkNull'])
|
||||
const checkAll = computed({
|
||||
get() {
|
||||
let flag = false
|
||||
if (props.data.length !== 0 && props.data.length === props.selectionData.length)
|
||||
if (props.data.length !== 0 && props.data.length === props.selectionData.length) {
|
||||
flag = true
|
||||
|
||||
}
|
||||
return flag
|
||||
},
|
||||
set(newVal) {
|
||||
@@ -27,9 +27,9 @@ const checkAll = computed({
|
||||
|
||||
const isIndeterminate = computed(() => {
|
||||
let flag = false
|
||||
if (props.selectionData.length > 0 && props.selectionData.length < props.data.length)
|
||||
if (props.selectionData.length > 0 && props.selectionData.length < props.data.length) {
|
||||
flag = true
|
||||
|
||||
}
|
||||
return flag
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -48,12 +48,12 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const fileExt = fileName.at(-1)
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk)
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传文件只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
|
||||
if (!isSizeOk)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传文件大小不能超过 ${props.size}MB!`)
|
||||
|
||||
}
|
||||
return isTypeOk && isSizeOk
|
||||
}
|
||||
|
||||
|
||||
@@ -75,15 +75,15 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const fileExt = fileName.at(-1)
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk)
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
|
||||
if (!isSizeOk)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
|
||||
if (isTypeOk && isSizeOk)
|
||||
}
|
||||
if (isTypeOk && isSizeOk) {
|
||||
uploadData.value.progress.preview = URL.createObjectURL(file)
|
||||
|
||||
}
|
||||
return isTypeOk && isSizeOk
|
||||
}
|
||||
const onProgress: UploadProps['onProgress'] = (file) => {
|
||||
|
||||
@@ -81,12 +81,12 @@ function remove(index: number) {
|
||||
// 移动
|
||||
function move(index: number, type: 'left' | 'right') {
|
||||
const url = props.url
|
||||
if (type === 'left' && index !== 0)
|
||||
if (type === 'left' && index !== 0) {
|
||||
url[index] = url.splice(index - 1, 1, url[index])[0]
|
||||
|
||||
if (type === 'right' && index !== url.length - 1)
|
||||
}
|
||||
if (type === 'right' && index !== url.length - 1) {
|
||||
url[index] = url.splice(index + 1, 1, url[index])[0]
|
||||
|
||||
}
|
||||
emit('update:url', url)
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const fileExt = fileName.at(-1)
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk)
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
|
||||
if (!isSizeOk)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
|
||||
if (isTypeOk && isSizeOk)
|
||||
}
|
||||
if (isTypeOk && isSizeOk) {
|
||||
uploadData.value.progress.preview = URL.createObjectURL(file)
|
||||
|
||||
}
|
||||
return isTypeOk && isSizeOk
|
||||
}
|
||||
const onProgress: UploadProps['onProgress'] = (file) => {
|
||||
|
||||
@@ -91,18 +91,20 @@ function anyToCode(value: any[], dictionarie: any[] = pcasData.value) {
|
||||
const input: string[] = []
|
||||
if (value.length > 0) {
|
||||
const findItem = dictionarie.find((item) => {
|
||||
if (props.format === 'code')
|
||||
if (props.format === 'code') {
|
||||
return item.code === value[0]
|
||||
|
||||
else if (props.format === 'name')
|
||||
}
|
||||
else if (props.format === 'name') {
|
||||
return item.name === value[0]
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return item.name === value[0].name && item.code === value[0].code
|
||||
}
|
||||
})
|
||||
input.push(findItem.code)
|
||||
if (findItem.children)
|
||||
if (findItem.children) {
|
||||
input.push(...anyToCode(value.slice(1 - value.length), findItem.children))
|
||||
}
|
||||
}
|
||||
return input
|
||||
}
|
||||
@@ -125,8 +127,9 @@ function codeToAny(codes: string[], dictionarie: any[] = pcasData.value): any {
|
||||
})
|
||||
}
|
||||
const newCodes = codes.slice(1 - codes.length)
|
||||
if (newCodes.length > 0 && findItem.children)
|
||||
if (newCodes.length > 0 && findItem.children) {
|
||||
output.push(...codeToAny(newCodes, findItem.children))
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ const transformStyle = computed(() => {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (props.rotate !== 0)
|
||||
if (props.rotate !== 0) {
|
||||
style.push(`rotate(${props.rotate}deg)`)
|
||||
|
||||
}
|
||||
return `transform: ${style.join(' ')};`
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -24,9 +24,9 @@ const props = defineProps({
|
||||
|
||||
const isUp = computed(() => {
|
||||
let isUp = props.type === 'up'
|
||||
if (props.reverse)
|
||||
if (props.reverse) {
|
||||
isUp = !isUp
|
||||
|
||||
}
|
||||
return isUp
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -31,8 +31,9 @@ onMounted(() => {
|
||||
const { copy, copied, isSupported } = useClipboard()
|
||||
|
||||
watch(copied, (val) => {
|
||||
if (val)
|
||||
if (val) {
|
||||
ElMessage.success('复制成功,请粘贴到 src/settings.ts 文件中!')
|
||||
}
|
||||
})
|
||||
|
||||
function handleCopy() {
|
||||
|
||||
@@ -22,9 +22,9 @@ const to = computed(() => {
|
||||
const rtn: {
|
||||
name?: string
|
||||
} = {}
|
||||
if (settingsStore.dashboard.enable)
|
||||
if (settingsStore.dashboard.enable) {
|
||||
rtn.name = 'dashboard'
|
||||
|
||||
}
|
||||
return rtn
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -38,15 +38,15 @@ const resultList = computed(() => {
|
||||
let result = []
|
||||
result = sourceList.value.filter((item) => {
|
||||
let flag = false
|
||||
if (item.title.includes(searchInput.value))
|
||||
if (item.title.includes(searchInput.value)) {
|
||||
flag = true
|
||||
|
||||
if (item.path.includes(searchInput.value))
|
||||
}
|
||||
if (item.path.includes(searchInput.value)) {
|
||||
flag = true
|
||||
|
||||
if (item.breadcrumb.some(b => b.includes(searchInput.value)))
|
||||
}
|
||||
if (item.breadcrumb.some(b => b.includes(searchInput.value))) {
|
||||
flag = true
|
||||
|
||||
}
|
||||
return flag
|
||||
})
|
||||
return result
|
||||
@@ -111,9 +111,9 @@ onMounted(() => {
|
||||
|
||||
function hasChildren(item: Route.recordRaw) {
|
||||
let flag = true
|
||||
if (item.children && item.children.every(i => i.meta.sidebar === false))
|
||||
if (item.children && item.children.every(i => i.meta.sidebar === false)) {
|
||||
flag = false
|
||||
|
||||
}
|
||||
return flag
|
||||
}
|
||||
function getSourceList(arr: Route.recordRaw[], basePath?: string, icon?: string, breadcrumb?: string[]) {
|
||||
@@ -127,12 +127,12 @@ function getSourceList(arr: Route.recordRaw[], basePath?: string, icon?: string,
|
||||
else {
|
||||
breadcrumbTemp.push(typeof item.meta.title === 'function' ? item.meta.title() : item.meta.title)
|
||||
let path = ''
|
||||
if (isExternalLink(item.path))
|
||||
if (isExternalLink(item.path)) {
|
||||
path = item.path
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
path = basePath ? [basePath, item.path].join('/') : item.path
|
||||
|
||||
}
|
||||
sourceList.value.push({
|
||||
icon: item.meta.icon || icon,
|
||||
title: typeof item.meta.title === 'function' ? item.meta.title() : item.meta.title,
|
||||
@@ -165,24 +165,25 @@ function getSourceListByMenus(arr: Menu.recordRaw[], icon?: string, breadcrumb?:
|
||||
function keyUp() {
|
||||
if (resultList.value.length) {
|
||||
actived.value -= 1
|
||||
if (actived.value < 0)
|
||||
if (actived.value < 0) {
|
||||
actived.value = resultList.value.length - 1
|
||||
|
||||
}
|
||||
handleScroll()
|
||||
}
|
||||
}
|
||||
function keyDown() {
|
||||
if (resultList.value.length) {
|
||||
actived.value += 1
|
||||
if (actived.value > resultList.value.length - 1)
|
||||
if (actived.value > resultList.value.length - 1) {
|
||||
actived.value = 0
|
||||
|
||||
}
|
||||
handleScroll()
|
||||
}
|
||||
}
|
||||
function keyEnter() {
|
||||
if (actived.value !== -1)
|
||||
if (actived.value !== -1) {
|
||||
searchResultItemRef.value[actived.value].click()
|
||||
}
|
||||
}
|
||||
function handleScroll() {
|
||||
let scrollTo = 0
|
||||
@@ -192,11 +193,12 @@ function handleScroll() {
|
||||
const activedClientHeight = searchResultItemRef.value[actived.value].clientHeight
|
||||
const searchScrollTop = searchResultRef.value.scrollTop
|
||||
const searchClientHeight = searchResultRef.value.clientHeight
|
||||
if (activedOffsetTop + activedClientHeight > searchScrollTop + searchClientHeight)
|
||||
if (activedOffsetTop + activedClientHeight > searchScrollTop + searchClientHeight) {
|
||||
scrollTo = activedOffsetTop + activedClientHeight - searchClientHeight
|
||||
|
||||
else if (activedOffsetTop <= searchScrollTop)
|
||||
}
|
||||
else if (activedOffsetTop <= searchScrollTop) {
|
||||
scrollTo = activedOffsetTop
|
||||
}
|
||||
}
|
||||
searchResultRef.value.scrollTo({
|
||||
top: scrollTo,
|
||||
@@ -204,11 +206,12 @@ function handleScroll() {
|
||||
}
|
||||
|
||||
function pageJump(url: string) {
|
||||
if (isExternalLink(url))
|
||||
if (isExternalLink(url)) {
|
||||
window.open(url, 'top')
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
router.push(url)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@ const props = defineProps({
|
||||
const hasChildren = computed(() => {
|
||||
let flag = true
|
||||
if (props.item.children) {
|
||||
if (props.item.children.every(item => item.meta.sidebar === false))
|
||||
if (props.item.children.every(item => item.meta.sidebar === false)) {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
else {
|
||||
flag = false
|
||||
|
||||
@@ -22,9 +22,9 @@ const breadcrumbList = computed(() => {
|
||||
title: settingsStore.dashboard.title,
|
||||
})
|
||||
}
|
||||
if (route.meta.breadcrumbNeste)
|
||||
if (route.meta.breadcrumbNeste) {
|
||||
breadcrumbList.push(...route.meta.breadcrumbNeste.filter(item => item.hide === false))
|
||||
|
||||
}
|
||||
return breadcrumbList
|
||||
})
|
||||
|
||||
|
||||
@@ -19,11 +19,12 @@ const menuStore = useMenuStore()
|
||||
|
||||
watch(() => settingsStore.menu.subMenuCollapse, (val) => {
|
||||
if (settingsStore.mode === 'mobile') {
|
||||
if (!val)
|
||||
if (!val) {
|
||||
document.body.classList.add('hidden')
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
document.body.classList.remove('hidden')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
+2
-1
@@ -13,8 +13,9 @@ import 'virtual:svg-icons-register'
|
||||
import '@/assets/styles/globals.scss'
|
||||
// 加载 iconify 图标(element plus)
|
||||
import { downloadAndInstall } from '@/iconify-ep'
|
||||
if (useSettingsStore().app.iconifyOfflineUse)
|
||||
if (useSettingsStore().app.iconifyOfflineUse) {
|
||||
downloadAndInstall()
|
||||
}
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(ElementPlus)
|
||||
|
||||
+35
-40
@@ -26,47 +26,37 @@ router.beforeEach(async (to, from, next) => {
|
||||
settingsStore.app.enableProgress && (isLoading.value = true)
|
||||
// 是否已登录
|
||||
if (userStore.isLogin) {
|
||||
// 是否已根据权限动态生成并挂载路由
|
||||
// 是否已根据权限动态生成并注册路由
|
||||
if (routeStore.isGenerate) {
|
||||
// 导航栏如果不是 single 模式,则需要根据 path 定位主导航的选中状态
|
||||
settingsStore.menu.menuMode !== 'single' && menuStore.setActived(to.path)
|
||||
if (to.name) {
|
||||
if (to.matched.length !== 0) {
|
||||
// 如果已登录状态下,进入登录页会强制跳转到控制台页面
|
||||
if (to.name === 'login') {
|
||||
next({
|
||||
name: 'dashboard',
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
else if (!settingsStore.dashboard.enable && to.name === 'dashboard') {
|
||||
// 如果未开启控制台页面,则默认进入侧边栏导航第一个模块
|
||||
if (menuStore.sidebarMenus.length > 0) {
|
||||
next({
|
||||
path: menuStore.sidebarMenusFirstDeepestPath,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 如果是通过 name 跳转,并且 name 对应的路由没有权限时,需要做这步处理,手动指向到 404 页面
|
||||
// 如果已登录状态下,进入登录页会强制跳转到控制台页面
|
||||
if (to.name === 'login') {
|
||||
next({
|
||||
name: 'dashboard',
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
// 如果未开启控制台,但进入的是控制台页面,则会进入侧边栏导航第一个模块
|
||||
else if (!settingsStore.dashboard.enable && to.name === 'dashboard') {
|
||||
if (menuStore.sidebarMenus.length > 0) {
|
||||
next({
|
||||
path: '/404',
|
||||
path: menuStore.sidebarMenusFirstDeepestPath,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
// 如果侧边栏导航第一个模块无法命中,则还是进入控制台页面
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
// 正常访问页面
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 生成动态路由
|
||||
switch (settingsStore.app.routeBaseOn) {
|
||||
case 'frontend':
|
||||
await routeStore.generateRoutesAtFront(asyncRoutes)
|
||||
@@ -76,6 +66,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
break
|
||||
case 'filesystem':
|
||||
await routeStore.generateRoutesAtFilesystem(asyncRoutesByFilesystem)
|
||||
// 文件系统生成的路由,需要手动生成导航数据
|
||||
switch (settingsStore.menu.baseOn) {
|
||||
case 'frontend':
|
||||
await menuStore.generateMenusAtFront()
|
||||
@@ -86,18 +77,21 @@ router.beforeEach(async (to, from, next) => {
|
||||
}
|
||||
break
|
||||
}
|
||||
// 注册并记录路由数据
|
||||
// 记录的数据会在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
|
||||
const removeRoutes: Function[] = []
|
||||
routeStore.flatRoutes.forEach((route) => {
|
||||
if (!/^(https?:|mailto:|tel:)/.test(route.path))
|
||||
if (!/^(https?:|mailto:|tel:)/.test(route.path)) {
|
||||
removeRoutes.push(router.addRoute(route as RouteRecordRaw))
|
||||
}
|
||||
})
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
routeStore.flatSystemRoutes.forEach((route) => {
|
||||
removeRoutes.push(router.addRoute(route as RouteRecordRaw))
|
||||
})
|
||||
}
|
||||
// 记录路由数据,在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
|
||||
routeStore.setCurrentRemoveRoutes(removeRoutes)
|
||||
// 动态路由生成并注册后,重新进入当前路由
|
||||
next({
|
||||
path: to.path,
|
||||
query: to.query,
|
||||
@@ -129,11 +123,12 @@ router.afterEach((to, from) => {
|
||||
// 判断当前页面是否开启缓存,如果开启,则将当前页面的 name 信息存入 keep-alive 全局状态
|
||||
if (to.meta.cache) {
|
||||
const componentName = to.matched.at(-1)?.components?.default.name
|
||||
if (componentName)
|
||||
if (componentName) {
|
||||
keepAliveStore.add(componentName)
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
console.warn('该页面组件未设置组件名,会导致缓存失效,请检查')
|
||||
}
|
||||
}
|
||||
// 判断离开页面是否开启缓存,如果开启,则根据缓存规则判断是否需要清空 keep-alive 全局状态里离开页面的 name 信息
|
||||
if (from.meta.cache) {
|
||||
@@ -142,22 +137,22 @@ router.afterEach((to, from) => {
|
||||
// 通过 meta.cache 判断针对哪些页面进行缓存
|
||||
switch (typeof from.meta.cache) {
|
||||
case 'string':
|
||||
if (from.meta.cache !== to.name)
|
||||
if (from.meta.cache !== to.name) {
|
||||
keepAliveStore.remove(componentName)
|
||||
|
||||
}
|
||||
break
|
||||
case 'object':
|
||||
if (!from.meta.cache.includes(to.name as string))
|
||||
if (!from.meta.cache.includes(to.name as string)) {
|
||||
keepAliveStore.remove(componentName)
|
||||
|
||||
}
|
||||
break
|
||||
}
|
||||
// 如果进入的是 reload 页面,则也将离开页面的缓存清空
|
||||
if (to.name === 'reload')
|
||||
if (to.name === 'reload') {
|
||||
keepAliveStore.remove(componentName)
|
||||
}
|
||||
}
|
||||
}
|
||||
document.body.scrollTop = 0
|
||||
document.documentElement.scrollTop = 0
|
||||
})
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import menu from '@/menu'
|
||||
|
||||
function getDeepestPath(menu: Menu.recordRaw, rootPath = '') {
|
||||
let retnPath = ''
|
||||
if (menu.path) {
|
||||
if (menu.path !== undefined) {
|
||||
if (menu.children) {
|
||||
if (
|
||||
menu.children.some((item) => {
|
||||
@@ -75,8 +75,9 @@ function getDefaultOpenedPaths(menus: Menu.recordRaw[], rootPath = '') {
|
||||
if (item.path && item.meta.defaultOpened && item.children) {
|
||||
defaultOpenedPaths.push(resolveRoutePath(rootPath, item.path))
|
||||
const childrenDefaultOpenedPaths = getDefaultOpenedPaths(item.children, resolveRoutePath(rootPath, item.path))
|
||||
if (childrenDefaultOpenedPaths.length > 0)
|
||||
if (childrenDefaultOpenedPaths.length > 0) {
|
||||
defaultOpenedPaths.push(...childrenDefaultOpenedPaths)
|
||||
}
|
||||
}
|
||||
})
|
||||
return defaultOpenedPaths
|
||||
@@ -133,9 +134,9 @@ const useMenuStore = defineStore(
|
||||
defaultOpenedPaths() {
|
||||
const settingsStore = useSettingsStore()
|
||||
let defaultOpenedPaths: string[] = []
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem')
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
defaultOpenedPaths = getDefaultOpenedPaths(this.sidebarMenus)
|
||||
|
||||
}
|
||||
return defaultOpenedPaths
|
||||
},
|
||||
},
|
||||
@@ -189,14 +190,10 @@ const useMenuStore = defineStore(
|
||||
}
|
||||
else {
|
||||
// 如果是 string 类型,则认为是路由,需要查找对应的主导航索引
|
||||
this.allMenus.forEach((item, index) => {
|
||||
if (
|
||||
item.children.some((r) => {
|
||||
return data.indexOf(`${r.path}/`) === 0 || data === r.path
|
||||
})
|
||||
)
|
||||
this.actived = index
|
||||
})
|
||||
const findIndex = this.allMenus.findIndex(item => item.children.some(r => data.indexOf(`${r.path}/`) === 0 || data === r.path))
|
||||
if (findIndex >= 0) {
|
||||
this.actived = findIndex
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -47,12 +47,13 @@ function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/
|
||||
route.component = () => import('@/layouts/index.vue')
|
||||
break
|
||||
default:
|
||||
if (route.component)
|
||||
if (route.component) {
|
||||
route.component = views[`../../views/${route.component}`]
|
||||
}
|
||||
}
|
||||
if (route.children)
|
||||
if (route.children) {
|
||||
route.children = formatBackRoutes(route.children, views)
|
||||
|
||||
}
|
||||
return route
|
||||
})
|
||||
}
|
||||
@@ -91,8 +92,9 @@ function flatAsyncRoutesRecursive(routes: Route.recordRaw[], breadcrumb: Route.b
|
||||
// 如果 path 一样则覆盖,因为子路由的 path 可能设置为空,导致和父路由一样,直接注册会提示路由重复
|
||||
if (res.some(v => v.path === item.path)) {
|
||||
res.forEach((v, i) => {
|
||||
if (v.path === item.path)
|
||||
if (v.path === item.path) {
|
||||
res[i] = item
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -40,11 +40,7 @@ const useSettingsStore = defineStore(
|
||||
}
|
||||
else {
|
||||
// 如果为桌面设备,再根据页面宽度判断是否需要切换为移动端展示
|
||||
if (width < 992)
|
||||
this.mode = 'mobile'
|
||||
|
||||
else
|
||||
this.mode = 'pc'
|
||||
this.mode = width < 992 ? 'mobile' : 'pc'
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -54,8 +50,9 @@ const useSettingsStore = defineStore(
|
||||
// 切换侧边栏导航展开/收起
|
||||
toggleSidebarCollapse() {
|
||||
this.menu.subMenuCollapse = !this.menu.subMenuCollapse
|
||||
if (this.mode === 'pc')
|
||||
if (this.mode === 'pc') {
|
||||
this.subMenuCollapseLastStatus = !this.subMenuCollapseLastStatus
|
||||
}
|
||||
},
|
||||
// 设置主题颜色模式
|
||||
setColorScheme(color: Required<Settings.app>['colorScheme']) {
|
||||
|
||||
@@ -16,8 +16,9 @@ const useUserStore = defineStore(
|
||||
isLogin: (state) => {
|
||||
let retn = false
|
||||
if (state.token) {
|
||||
if (new Date().getTime() < parseInt(state.failure_time) * 1000)
|
||||
if (new Date().getTime() < parseInt(state.failure_time) * 1000) {
|
||||
retn = true
|
||||
}
|
||||
}
|
||||
return retn
|
||||
},
|
||||
|
||||
@@ -5,11 +5,12 @@ export default function useAuth() {
|
||||
function hasPermission(permission: string) {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
if (settingsStore.app.enablePermission)
|
||||
if (settingsStore.app.enablePermission) {
|
||||
return userStore.permissions.includes(permission)
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function auth(value: string | string[]) {
|
||||
|
||||
@@ -10,11 +10,12 @@ export default function useMenu() {
|
||||
function switchTo(index: number | string) {
|
||||
menuStore.setActived(index)
|
||||
if (settingsStore.menu.switchMainMenuAndPageJump) {
|
||||
if (isExternalLink(menuStore.sidebarMenusFirstDeepestPath))
|
||||
if (isExternalLink(menuStore.sidebarMenusFirstDeepestPath)) {
|
||||
window.open(menuStore.sidebarMenusFirstDeepestPath, '_blank')
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
router.push(menuStore.sidebarMenusFirstDeepestPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,16 @@ export default function directive(app: App) {
|
||||
// 注册 v-auth 和 v-auth-all 指令
|
||||
app.directive('auth', {
|
||||
mounted: (el, binding) => {
|
||||
if (!useAuth().auth(binding.value))
|
||||
if (!useAuth().auth(binding.value)) {
|
||||
el.remove()
|
||||
}
|
||||
},
|
||||
})
|
||||
app.directive('auth-all', {
|
||||
mounted: (el, binding) => {
|
||||
if (!useAuth().authAll(binding.value))
|
||||
if (!useAuth().authAll(binding.value)) {
|
||||
el.remove()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
+4
-4
@@ -5,11 +5,11 @@ export function isExternalLink(path: string) {
|
||||
}
|
||||
|
||||
export function resolveRoutePath(basePath: string, routePath: string) {
|
||||
if (isExternalLink(routePath))
|
||||
if (isExternalLink(routePath)) {
|
||||
return routePath
|
||||
|
||||
if (isExternalLink(basePath))
|
||||
}
|
||||
if (isExternalLink(basePath)) {
|
||||
return basePath
|
||||
|
||||
}
|
||||
return basePath ? path.resolve(basePath, routePath) : routePath
|
||||
}
|
||||
|
||||
+8
-7
@@ -46,12 +46,12 @@ function handleLogin() {
|
||||
loading.value = true
|
||||
userStore.login(loginForm.value).then(() => {
|
||||
loading.value = false
|
||||
if (loginForm.value.remember)
|
||||
if (loginForm.value.remember) {
|
||||
localStorage.setItem('login_account', loginForm.value.account)
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
localStorage.removeItem('login_account')
|
||||
|
||||
}
|
||||
router.push(redirect.value)
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
@@ -85,11 +85,12 @@ const registerRules = ref<FormRules>({
|
||||
{ required: true, trigger: 'blur', message: '请再次输入密码' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value !== registerForm.value.password)
|
||||
if (value !== registerForm.value.password) {
|
||||
callback(new Error('两次输入的密码不一致'))
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -32,11 +32,12 @@ const rules = ref<FormRules>({
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (value !== form.value.newpassword)
|
||||
if (value !== form.value.newpassword) {
|
||||
callback(new Error('请确认新密码'))
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -19,11 +19,12 @@ const form = ref({
|
||||
})
|
||||
|
||||
const handleSuccess: UploadProps['onSuccess'] = (res) => {
|
||||
if (res.error === '')
|
||||
if (res.error === '') {
|
||||
form.value.headimg = res.data.path
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
ElMessage.warning(res.error)
|
||||
}
|
||||
}
|
||||
const editPassword = () => {
|
||||
router.push({
|
||||
|
||||
@@ -4,8 +4,9 @@ import ViteRestart from 'vite-plugin-restart'
|
||||
export default function createRestart() {
|
||||
const pluginsFile: string[] = []
|
||||
fs.readdirSync('vite/plugins').forEach((dirname) => {
|
||||
if (fs.statSync(`vite/plugins/${dirname}`).isFile())
|
||||
if (fs.statSync(`vite/plugins/${dirname}`).isFile()) {
|
||||
pluginsFile.push(`vite/plugins/${dirname}`)
|
||||
}
|
||||
})
|
||||
return ViteRestart({
|
||||
restart: pluginsFile,
|
||||
|
||||
@@ -4,8 +4,9 @@ import spritesmith from 'vite-plugin-spritesmith'
|
||||
export default function createSpritesmith(isBuild) {
|
||||
const spriteDirnames: string[] = []
|
||||
fs.readdirSync('src/assets/sprites').forEach((dirname) => {
|
||||
if (fs.statSync(`src/assets/sprites/${dirname}`).isDirectory())
|
||||
if (fs.statSync(`src/assets/sprites/${dirname}`).isDirectory()) {
|
||||
spriteDirnames.push(dirname)
|
||||
}
|
||||
})
|
||||
const plugin: any[] = []
|
||||
spriteDirnames.forEach((item) => {
|
||||
|
||||
Reference in New Issue
Block a user