feat(Breadcrumbe): Add Breadcrumb component
style: change function to arrow function
This commit is contained in:
parent
2fe9543b84
commit
4612e5544b
|
@ -50,5 +50,17 @@ export default [
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 退出接口
|
||||
{
|
||||
url: '/user/loginOut',
|
||||
method: 'get',
|
||||
timeout,
|
||||
response: () => {
|
||||
return {
|
||||
code: result_code,
|
||||
data: null
|
||||
}
|
||||
}
|
||||
}
|
||||
] as MockMethod[]
|
||||
|
|
|
@ -33,6 +33,7 @@ specifiers:
|
|||
lodash-es: ^4.17.21
|
||||
mockjs: ^1.1.0
|
||||
nprogress: ^0.2.0
|
||||
path-to-regexp: ^6.2.0
|
||||
pinia: ^2.0.9
|
||||
postcss: ^8.4.5
|
||||
postcss-html: ^1.3.0
|
||||
|
@ -41,7 +42,6 @@ specifiers:
|
|||
pretty-quick: ^3.1.3
|
||||
qs: ^6.10.3
|
||||
rimraf: ^3.0.2
|
||||
screenfull: ^6.0.0
|
||||
stylelint: ^14.2.0
|
||||
stylelint-config-html: ^1.0.0
|
||||
stylelint-config-prettier: ^9.0.3
|
||||
|
@ -74,9 +74,9 @@ dependencies:
|
|||
lodash-es: registry.nlark.com/lodash-es/4.17.21
|
||||
mockjs: registry.npmmirror.com/mockjs/1.1.0
|
||||
nprogress: registry.npmmirror.com/nprogress/0.2.0
|
||||
path-to-regexp: registry.npmmirror.com/path-to-regexp/6.2.0
|
||||
pinia: registry.npmmirror.com/pinia/2.0.9_typescript@4.5.4+vue@3.2.26
|
||||
qs: registry.npmmirror.com/qs/6.10.3
|
||||
screenfull: registry.npmmirror.com/screenfull/6.0.0
|
||||
vue: registry.npmmirror.com/vue/3.2.26
|
||||
vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.26
|
||||
vue-router: registry.npmmirror.com/vue-router/4.0.12_vue@3.2.26
|
||||
|
@ -1732,7 +1732,7 @@ packages:
|
|||
{
|
||||
integrity: sha1-0t5eA0JOcH3BDHQGjd7a5wh0Gyc=,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.nlark.com/eslint-utils/download/eslint-utils-2.1.0.tgz
|
||||
tarball: https://registry.nlark.com/eslint-utils/download/eslint-utils-2.1.0.tgz?cache=0&sync_timestamp=1631600361784&other_urls=https%3A%2F%2Fregistry.nlark.com%2Feslint-utils%2Fdownload%2Feslint-utils-2.1.0.tgz
|
||||
}
|
||||
name: eslint-utils
|
||||
version: 2.1.0
|
||||
|
@ -4246,17 +4246,6 @@ packages:
|
|||
version: 1.0.7
|
||||
dev: true
|
||||
|
||||
registry.nlark.com/path-to-regexp/6.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha1-97OAMzYQTDRoia3s5hRmkjBkXzg=,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.nlark.com/path-to-regexp/download/path-to-regexp-6.2.0.tgz
|
||||
}
|
||||
name: path-to-regexp
|
||||
version: 6.2.0
|
||||
dev: true
|
||||
|
||||
registry.nlark.com/path-type/4.0.0:
|
||||
resolution:
|
||||
{
|
||||
|
@ -4959,7 +4948,7 @@ packages:
|
|||
{
|
||||
integrity: sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.nlark.com/semver/download/semver-5.7.1.tgz?cache=0&sync_timestamp=1631500167672&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-5.7.1.tgz
|
||||
tarball: https://registry.nlark.com/semver/download/semver-5.7.1.tgz
|
||||
}
|
||||
name: semver
|
||||
version: 5.7.1
|
||||
|
@ -4971,7 +4960,7 @@ packages:
|
|||
{
|
||||
integrity: sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.nlark.com/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1631500167672&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz
|
||||
tarball: https://registry.nlark.com/semver/download/semver-6.3.0.tgz
|
||||
}
|
||||
name: semver
|
||||
version: 6.3.0
|
||||
|
@ -7066,8 +7055,8 @@ packages:
|
|||
vue-i18n:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@intlify/message-compiler': registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.27
|
||||
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.27
|
||||
'@intlify/message-compiler': registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.28
|
||||
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.28
|
||||
jsonc-eslint-parser: registry.npmmirror.com/jsonc-eslint-parser/1.4.1
|
||||
source-map: registry.nlark.com/source-map/0.6.1
|
||||
vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.26
|
||||
|
@ -7123,18 +7112,18 @@ packages:
|
|||
source-map: registry.nlark.com/source-map/0.6.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.27:
|
||||
registry.npmmirror.com/@intlify/message-compiler/9.2.0-beta.28:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-T3mBTm0559VX6l+lh8p5gDJ9/IS1XbVXeeMNJ2zTzxrf4lXg8OuotNjaxG3ZsuauQ5OqqlArkMYryXGyZnHolA==,
|
||||
integrity: sha512-NBH9fZyitN2cijGt8bmU1W7ZPdhKbgW01L1RxJKFJW0cRaCmknJq63Aif1Q6xcxKt9ZhPbvIKHgPGzg1nWMfeA==,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.npmmirror.com/@intlify/message-compiler/download/@intlify/message-compiler-9.2.0-beta.27.tgz
|
||||
tarball: https://registry.npmmirror.com/@intlify/message-compiler/download/@intlify/message-compiler-9.2.0-beta.28.tgz
|
||||
}
|
||||
name: '@intlify/message-compiler'
|
||||
version: 9.2.0-beta.27
|
||||
version: 9.2.0-beta.28
|
||||
engines: { node: '>= 12' }
|
||||
dependencies:
|
||||
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.27
|
||||
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.28
|
||||
source-map: registry.nlark.com/source-map/0.6.1
|
||||
dev: true
|
||||
|
||||
|
@ -7178,15 +7167,15 @@ packages:
|
|||
engines: { node: '>= 10' }
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/@intlify/shared/9.2.0-beta.27:
|
||||
registry.npmmirror.com/@intlify/shared/9.2.0-beta.28:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+Av77mIHy0qFkAq96mMAQGYcColMGN7e5+rUsyn3XxBw6oC3AGqYn/cQ6U/T3qOrzcHgcA+etAaLN3IxFqkJDw==,
|
||||
integrity: sha512-JBMcoj1D4kSAma7Vb0+d8z6lPLIn7hIdZJPxbU8bgeMMniwKLoIS/jGlEfrZihsB5+otckPeQp203z8skwVS0w==,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.npmmirror.com/@intlify/shared/download/@intlify/shared-9.2.0-beta.27.tgz
|
||||
tarball: https://registry.npmmirror.com/@intlify/shared/download/@intlify/shared-9.2.0-beta.28.tgz
|
||||
}
|
||||
name: '@intlify/shared'
|
||||
version: 9.2.0-beta.27
|
||||
version: 9.2.0-beta.28
|
||||
engines: { node: '>= 12' }
|
||||
dev: true
|
||||
|
||||
|
@ -7212,7 +7201,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@intlify/bundle-utils': registry.npmmirror.com/@intlify/bundle-utils/2.2.0_vue-i18n@9.1.9
|
||||
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.27
|
||||
'@intlify/shared': registry.npmmirror.com/@intlify/shared/9.2.0-beta.28
|
||||
'@rollup/pluginutils': registry.npmmirror.com/@rollup/pluginutils/4.1.2
|
||||
debug: registry.npmmirror.com/debug/4.3.3
|
||||
fast-glob: registry.nlark.com/fast-glob/3.2.7
|
||||
|
@ -9689,7 +9678,7 @@ packages:
|
|||
{
|
||||
integrity: sha1-MOvR73wv3/AcOk8VEESvJfqwUj4=,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-1.3.0.tgz
|
||||
tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-1.3.0.tgz?cache=0&sync_timestamp=1636378650851&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-1.3.0.tgz
|
||||
}
|
||||
name: eslint-visitor-keys
|
||||
version: 1.3.0
|
||||
|
@ -9701,7 +9690,7 @@ packages:
|
|||
{
|
||||
integrity: sha1-9lMoJZMFknOSyTjtROsKXJsr0wM=,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz
|
||||
tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz?cache=0&sync_timestamp=1636378650851&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.1.0.tgz
|
||||
}
|
||||
name: eslint-visitor-keys
|
||||
version: 2.1.0
|
||||
|
@ -11134,6 +11123,16 @@ packages:
|
|||
engines: { node: '>=8' }
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/path-to-regexp/6.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.npmmirror.com/path-to-regexp/download/path-to-regexp-6.2.0.tgz
|
||||
}
|
||||
name: path-to-regexp
|
||||
version: 6.2.0
|
||||
|
||||
registry.npmmirror.com/picocolors/1.0.0:
|
||||
resolution:
|
||||
{
|
||||
|
@ -11609,18 +11608,6 @@ packages:
|
|||
tslib: registry.npmmirror.com/tslib/2.3.1
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/screenfull/6.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-LGY0nhNQkC4FX4DT4pZdJ5cZH5EOz9Gfh9KcVMl779pS677k4IV1Wv7sY/CwC9VKFT21fYgCh7zkTVVefi5XKA==,
|
||||
registry: https://registry.npm.taobao.org/,
|
||||
tarball: https://registry.npmmirror.com/screenfull/download/screenfull-6.0.0.tgz
|
||||
}
|
||||
name: screenfull
|
||||
version: 6.0.0
|
||||
engines: { node: ^14.13.1 || >=16.0.0 }
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/shebang-regex/3.0.0:
|
||||
resolution:
|
||||
{
|
||||
|
@ -12307,7 +12294,7 @@ packages:
|
|||
esbuild: registry.npmmirror.com/esbuild/0.11.3
|
||||
fast-glob: registry.nlark.com/fast-glob/3.2.7
|
||||
mockjs: registry.npmmirror.com/mockjs/1.1.0
|
||||
path-to-regexp: registry.nlark.com/path-to-regexp/6.2.0
|
||||
path-to-regexp: registry.npmmirror.com/path-to-regexp/6.2.0
|
||||
vite: registry.npmmirror.com/vite/2.7.10_less@4.1.2
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
|
|
@ -8,7 +8,7 @@ const appStore = useAppStore()
|
|||
|
||||
const size = computed(() => appStore.size)
|
||||
|
||||
function initDark() {
|
||||
const initDark = () => {
|
||||
const isDarkTheme = isDark()
|
||||
appStore.setIsDark(isDarkTheme)
|
||||
}
|
||||
|
|
|
@ -9,3 +9,7 @@ export const loginApi = (data: UserLoginType) => {
|
|||
UserLoginType
|
||||
>)
|
||||
}
|
||||
|
||||
export const loginOutApi = () => {
|
||||
return request({ url: '/user/loginOut', method: 'get' })
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<script lang="tsx">
|
||||
import { ElBreadcrumb, ElBreadcrumbItem } from 'element-plus'
|
||||
import { ref, watch, computed, unref, defineComponent, TransitionGroup } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
// import { compile } from 'path-to-regexp'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { filterBreadcrumb } from './helper'
|
||||
import { filter, treeToList } from '@/utils/tree'
|
||||
import type { RouteLocationNormalizedLoaded, RouteMeta } from 'vue-router'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Icon } from '@/components/Icon'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Breadcrumb',
|
||||
setup() {
|
||||
const { currentRoute } = useRouter()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const levelList = ref<AppRouteRecordRaw[]>([])
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const menuRouters = computed(() => {
|
||||
const routers = permissionStore.getRouters
|
||||
return filterBreadcrumb(routers)
|
||||
})
|
||||
|
||||
const getBreadcrumb = () => {
|
||||
const currentPath = currentRoute.value.path
|
||||
|
||||
levelList.value = filter<AppRouteRecordRaw>(unref(menuRouters), (node: AppRouteRecordRaw) => {
|
||||
return node.path === currentPath
|
||||
})
|
||||
}
|
||||
|
||||
const renderBreadcrumb = () => {
|
||||
const breadcrumbList = treeToList<AppRouteRecordRaw[]>(unref(levelList))
|
||||
return breadcrumbList.map((v) => {
|
||||
const disabled = v.redirect === 'noredirect'
|
||||
const meta = v.meta as RouteMeta
|
||||
return (
|
||||
<ElBreadcrumbItem to={{ path: disabled ? '' : v.path }} key={v.name}>
|
||||
{meta?.icon ? (
|
||||
<>
|
||||
<Icon icon={meta.icon} class="mr-[5px]"></Icon> {t(v?.meta?.title)}
|
||||
</>
|
||||
) : (
|
||||
t(v?.meta?.title)
|
||||
)}
|
||||
</ElBreadcrumbItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => currentRoute.value,
|
||||
(route: RouteLocationNormalizedLoaded) => {
|
||||
if (route.path.startsWith('/redirect/')) {
|
||||
return
|
||||
}
|
||||
getBreadcrumb()
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
return () => (
|
||||
<ElBreadcrumb separator="/" class="flex items-center h-full ml-[10px]">
|
||||
<TransitionGroup appear enter-active-class="animate__animated animate__fadeInRight">
|
||||
{renderBreadcrumb()}
|
||||
</TransitionGroup>
|
||||
</ElBreadcrumb>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.el-breadcrumb__item) {
|
||||
display: flex;
|
||||
|
||||
.el-breadcrumb__inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,31 @@
|
|||
import { pathResolve } from '@/utils/routerHelper'
|
||||
import type { RouteMeta } from 'vue-router'
|
||||
|
||||
export const filterBreadcrumb = (
|
||||
routes: AppRouteRecordRaw[],
|
||||
parentPath = ''
|
||||
): AppRouteRecordRaw[] => {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
const meta = route?.meta as RouteMeta
|
||||
if (meta.hidden && !meta.showMainRoute) {
|
||||
continue
|
||||
}
|
||||
|
||||
const data: AppRouteRecordRaw =
|
||||
!meta.alwaysShow && route.children?.length === 1
|
||||
? { ...route.children[0], path: pathResolve(route.path, route.children[0].path) }
|
||||
: { ...route }
|
||||
|
||||
data.path = pathResolve(parentPath, data.path)
|
||||
|
||||
if (data.children) {
|
||||
data.children = filterBreadcrumb(data.children, data.path)
|
||||
}
|
||||
if (data) {
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, unref } from 'vue'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const collapse = computed(() => appStore.getCollapse)
|
||||
|
||||
function toggleCollapse() {
|
||||
const toggleCollapse = () => {
|
||||
const collapsed = unref(collapse)
|
||||
appStore.setCollapse(!collapsed)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,33 @@
|
|||
<script setup lang="ts">
|
||||
import { provide, computed } from 'vue'
|
||||
import { provide, computed, watch } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
import { useLocaleStore } from '@/store/modules/locale'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { setCssVar } from '@/utils'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
watch(
|
||||
() => width.value,
|
||||
(width: number) => {
|
||||
if (width < 768) {
|
||||
!appStore.getMobile ? appStore.setMobile(true) : undefined
|
||||
setCssVar('--left-menu-min-width', '0')
|
||||
appStore.setCollapse(true)
|
||||
appStore.getLayout !== 'classic' ? appStore.setLayout('classic') : undefined
|
||||
} else {
|
||||
appStore.getMobile ? appStore.setMobile(false) : undefined
|
||||
setCssVar('--left-menu-min-width', '64px')
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
const localeStore = useLocaleStore()
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ export default defineComponent({
|
|||
})
|
||||
|
||||
// 对表单赋值
|
||||
function setValues(data: FormSetValuesType[]) {
|
||||
const setValues = (data: FormSetValuesType[]) => {
|
||||
if (!data.length) return
|
||||
const formData: Recordable = {}
|
||||
for (const v of data) {
|
||||
|
@ -89,7 +89,7 @@ export default defineComponent({
|
|||
)
|
||||
|
||||
// 渲染包裹标签,是否使用栅格布局
|
||||
function renderWrap() {
|
||||
const renderWrap = () => {
|
||||
const content = isCol ? (
|
||||
<ElRow gutter={20}>{renderFormItemWrap()}</ElRow>
|
||||
) : (
|
||||
|
@ -99,7 +99,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// 是否要渲染el-col
|
||||
function renderFormItemWrap() {
|
||||
const renderFormItemWrap = () => {
|
||||
// hidden属性表示隐藏,不做渲染
|
||||
return schema
|
||||
.filter((v) => !v.hidden)
|
||||
|
@ -119,7 +119,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// 渲染formItem
|
||||
function renderFormItem(item: FormSchema) {
|
||||
const renderFormItem = (item: FormSchema) => {
|
||||
// 单独给只有options属性的组件做判断
|
||||
const notRenderOptions = ['SelectV2', 'Cascader', 'Transfer']
|
||||
const slotsMap: Recordable = {
|
||||
|
@ -162,7 +162,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// 渲染options
|
||||
function renderOptions(item: FormSchema) {
|
||||
const renderOptions = (item: FormSchema) => {
|
||||
switch (item.component) {
|
||||
case 'Select':
|
||||
const { renderSelectOptions } = useRenderSelect(slots)
|
||||
|
@ -181,7 +181,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// 过滤传入Form组件的属性
|
||||
function getFormBindValue() {
|
||||
const getFormBindValue = () => {
|
||||
// 避免在标签上出现多余的属性
|
||||
const delKeys = ['schema', 'isCol', 'autoSetPlaceholder', 'isCustom', 'model']
|
||||
const props = { ...unref(getProps) }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { ElCheckbox, ElCheckboxButton } from 'element-plus'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export function useRenderChcekbox() {
|
||||
function renderChcekboxOptions(item: FormSchema) {
|
||||
export const useRenderChcekbox = () => {
|
||||
const renderChcekboxOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { ElRadio, ElRadioButton } from 'element-plus'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export function useRenderRadio() {
|
||||
function renderRadioOptions(item: FormSchema) {
|
||||
export const useRenderRadio = () => {
|
||||
const renderRadioOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
||||
|
|
|
@ -2,9 +2,9 @@ import { ElOption, ElOptionGroup } from 'element-plus'
|
|||
import { getSlot } from '@/utils/tsxHelper'
|
||||
import { Slots } from 'vue'
|
||||
|
||||
export function useRenderSelect(slots: Slots) {
|
||||
export const useRenderSelect = (slots: Slots) => {
|
||||
// 渲染 select options
|
||||
function renderSelectOptions(item: FormSchema) {
|
||||
const renderSelectOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
return item?.componentProps?.options?.map((option) => {
|
||||
|
@ -25,7 +25,7 @@ export function useRenderSelect(slots: Slots) {
|
|||
}
|
||||
|
||||
// 渲染 select option item
|
||||
function renderSelectOptionItem(item: FormSchema, option: ComponentOptions) {
|
||||
const renderSelectOptionItem = (item: FormSchema, option: ComponentOptions) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
||||
|
|
|
@ -17,7 +17,7 @@ interface PlaceholderMoel {
|
|||
* @returns 返回提示信息对象
|
||||
* @description 用于自动设置placeholder
|
||||
*/
|
||||
export function setTextPlaceholder(schema: FormSchema): PlaceholderMoel {
|
||||
export const setTextPlaceholder = (schema: FormSchema): PlaceholderMoel => {
|
||||
const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword']
|
||||
const selectMap = ['Select', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect']
|
||||
if (textMap.includes(schema?.component as string)) {
|
||||
|
@ -53,7 +53,7 @@ export function setTextPlaceholder(schema: FormSchema): PlaceholderMoel {
|
|||
* @returns 返回栅格属性
|
||||
* @description 合并传入进来的栅格属性
|
||||
*/
|
||||
export function setGridProp(col: ColProps = {}): ColProps {
|
||||
export const setGridProp = (col: ColProps = {}): ColProps => {
|
||||
const colProps: ColProps = {
|
||||
// 如果有span,代表用户优先级更高,所以不需要默认栅格
|
||||
...(col.span
|
||||
|
@ -75,7 +75,7 @@ export function setGridProp(col: ColProps = {}): ColProps {
|
|||
* @param item 传入的组件属性
|
||||
* @returns 默认添加 clearable 属性
|
||||
*/
|
||||
export function setComponentProps(item: FormSchema): Recordable {
|
||||
export const setComponentProps = (item: FormSchema): Recordable => {
|
||||
const notNeedClearable = ['ColorPicker']
|
||||
const componentProps: Recordable = notNeedClearable.includes(item.component as string)
|
||||
? { ...item.componentProps }
|
||||
|
@ -94,11 +94,11 @@ export function setComponentProps(item: FormSchema): Recordable {
|
|||
* @param slotsProps 插槽属性
|
||||
* @param field 字段名
|
||||
*/
|
||||
export function setItemComponentSlots(
|
||||
export const setItemComponentSlots = (
|
||||
slots: Slots,
|
||||
slotsProps: Recordable = {},
|
||||
field: string
|
||||
): Recordable {
|
||||
): Recordable => {
|
||||
const slotObj: Recordable = {}
|
||||
for (const key in slotsProps) {
|
||||
if (slotsProps[key]) {
|
||||
|
@ -118,7 +118,7 @@ export function setItemComponentSlots(
|
|||
* @returns FormMoel
|
||||
* @description 生成对应的formModel
|
||||
*/
|
||||
export function initModel(schema: FormSchema[], formModel: Recordable) {
|
||||
export const initModel = (schema: FormSchema[], formModel: Recordable) => {
|
||||
const model: Recordable = { ...formModel }
|
||||
schema.map((v) => {
|
||||
// 如果是hidden,就删除对应的值
|
||||
|
@ -138,7 +138,7 @@ export function initModel(schema: FormSchema[], formModel: Recordable) {
|
|||
* @param field 字段名
|
||||
* @returns 返回FormIiem插槽
|
||||
*/
|
||||
export function setFormItemSlots(slots: Slots, field: string): Recordable {
|
||||
export const setFormItemSlots = (slots: Slots, field: string): Recordable => {
|
||||
const slotObj: Recordable = {}
|
||||
if (slots[`${field}-error`]) {
|
||||
slotObj['error'] = (data: Recordable) => {
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, unref, ref, watch, nextTick } from 'vue'
|
||||
import { ElIcon } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import Iconify from '@purge-icons/generated'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('icon')
|
||||
|
||||
const props = defineProps({
|
||||
// icon name
|
||||
icon: propTypes.string,
|
||||
|
@ -34,7 +29,7 @@ const getIconifyStyle = computed(() => {
|
|||
}
|
||||
})
|
||||
|
||||
async function updateIcon(icon: string) {
|
||||
const updateIcon = async (icon: string) => {
|
||||
if (unref(isLocal)) return
|
||||
|
||||
const el = unref(elRef)
|
||||
|
@ -66,7 +61,7 @@ watch(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<ElIcon :class="prefixCls" :size="size" :color="color">
|
||||
<ElIcon class="v-icon" :size="size" :color="color">
|
||||
<svg v-if="isLocal" aria-hidden="true">
|
||||
<use :xlink:href="symbolId" />
|
||||
</svg>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import { ref, unref, computed, watch } from 'vue'
|
||||
import { ElInput } from 'element-plus'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useConfigGlobal } from '@/hooks/web/useConfigGlobal'
|
||||
import { zxcvbn } from '@zxcvbn-ts/core'
|
||||
import type { ZxcvbnResult } from '@zxcvbn-ts/core'
|
||||
|
@ -25,15 +24,10 @@ const { configGlobal } = useConfigGlobal()
|
|||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
// 生成class前缀
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = ref(getPrefixCls('input-password'))
|
||||
|
||||
// 设置input的type属性
|
||||
const textType = ref<'password' | 'text'>('password')
|
||||
|
||||
function changeTextType() {
|
||||
const changeTextType = () => {
|
||||
textType.value = unref(textType) === 'text' ? 'password' : 'text'
|
||||
}
|
||||
|
||||
|
@ -61,7 +55,7 @@ const getIconName = computed(() =>
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="[prefixCls, `${prefixCls}--${configGlobal?.size}`]">
|
||||
<div :class="['v-input-password', `v-input-password--${configGlobal?.size}`]">
|
||||
<ElInput v-bind="$attrs" v-model="valueRef" :type="textType">
|
||||
<template #suffix>
|
||||
<Icon class="el-input__icon cursor-pointer" :icon="getIconName" @click="changeTextType" />
|
||||
|
@ -69,10 +63,9 @@ const getIconName = computed(() =>
|
|||
</ElInput>
|
||||
<div
|
||||
v-if="strength"
|
||||
:class="`${prefixCls}__bar`"
|
||||
class="relative h-6px mt-10px mb-6px mr-auto ml-auto"
|
||||
class="v-input-password__bar relative h-6px mt-10px mb-6px mr-auto ml-auto"
|
||||
>
|
||||
<div :class="`${prefixCls}__bar--fill`" :data-score="getPasswordStrength"></div>
|
||||
<div class="v-input-password__bar--fill" :data-score="getPasswordStrength"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -10,7 +10,7 @@ const langMap = computed(() => localeStore.getLocaleMap)
|
|||
|
||||
const currentLang = computed(() => localeStore.getLocale)
|
||||
|
||||
function setLang(lang: LocaleType) {
|
||||
const setLang = (lang: LocaleType) => {
|
||||
if (lang === unref(currentLang).lang) return
|
||||
// 需要重新加载页面让整个语言多初始化
|
||||
window.location.reload()
|
||||
|
@ -24,13 +24,7 @@ function setLang(lang: LocaleType) {
|
|||
|
||||
<template>
|
||||
<ElDropdown trigger="click" @command="setLang">
|
||||
<Icon
|
||||
:size="18"
|
||||
icon="ion:language-sharp"
|
||||
color="var(--el-text-color-primary)"
|
||||
class="cursor-pointer"
|
||||
:class="$attrs.class"
|
||||
/>
|
||||
<Icon :size="18" icon="ion:language-sharp" class="cursor-pointer" :class="$attrs.class" />
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<ElDropdownItem v-for="item in langMap" :key="item.lang" :command="item.lang">
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="tsx">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { ElMenu, ElScrollbar } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import type { LayoutType } from '@/config/app'
|
||||
|
@ -18,10 +17,6 @@ export default defineComponent({
|
|||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const preFixCls = getPrefixCls('menu')
|
||||
|
||||
const menuMode = computed((): 'vertical' | 'horizontal' => {
|
||||
// 竖
|
||||
const vertical: LayoutType[] = ['classic']
|
||||
|
@ -46,7 +41,7 @@ export default defineComponent({
|
|||
return path
|
||||
})
|
||||
|
||||
function menuSelect(index: string) {
|
||||
const menuSelect = (index: string) => {
|
||||
if (isUrl(index)) {
|
||||
window.open(index)
|
||||
} else {
|
||||
|
@ -57,8 +52,8 @@ export default defineComponent({
|
|||
return () => (
|
||||
<div
|
||||
class={[
|
||||
preFixCls,
|
||||
'h-[100%] overflow-hidden',
|
||||
'v-menu',
|
||||
'h-[100%] overflow-hidden z-100',
|
||||
appStore.getCollapse
|
||||
? 'w-[var(--left-menu-min-width)]'
|
||||
: 'w-[var(--left-menu-max-width)]',
|
||||
|
@ -147,7 +142,7 @@ export default defineComponent({
|
|||
|
||||
// 折叠动画的时候,就需要把文字给隐藏掉
|
||||
:deep(.horizontal-collapse-transition) {
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out !important;
|
||||
// transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out !important;
|
||||
.@{prefix-cls}__title {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ import { useRenderMenuTitle } from './useRenderMenuTitle'
|
|||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { pathResolve } from '@/utils/routerHelper'
|
||||
|
||||
export function useRenderMenuItem(
|
||||
export const useRenderMenuItem = (
|
||||
allRouters: AppRouteRecordRaw[] = [],
|
||||
menuMode: 'vertical' | 'horizontal'
|
||||
) {
|
||||
function renderMenuItem(routers?: AppRouteRecordRaw[]) {
|
||||
) => {
|
||||
const renderMenuItem = (routers?: AppRouteRecordRaw[]) => {
|
||||
return (routers || allRouters).map((v) => {
|
||||
const meta = (v.meta ?? {}) as RouteMeta
|
||||
if (!meta.hidden) {
|
||||
|
|
|
@ -2,18 +2,18 @@ import type { RouteMeta } from 'vue-router'
|
|||
import { Icon } from '@/components/Icon'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
export function useRenderMenuTitle() {
|
||||
function renderMenuTitle(meta: RouteMeta) {
|
||||
export const useRenderMenuTitle = () => {
|
||||
const renderMenuTitle = (meta: RouteMeta) => {
|
||||
const { t } = useI18n()
|
||||
const { title = 'Please set title', icon } = meta
|
||||
|
||||
return icon ? (
|
||||
<>
|
||||
<Icon icon={meta.icon}></Icon>
|
||||
<span>{t(title as string)}</span>
|
||||
<span class="v-menu__title">{t(title as string)}</span>
|
||||
</>
|
||||
) : (
|
||||
<span>{t(title as string)}</span>
|
||||
<span class="v-menu__title">{t(title as string)}</span>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import type { RouteMeta } from 'vue-router'
|
||||
import { ref, unref } from 'vue'
|
||||
|
||||
interface TreeConfig {
|
||||
id: string
|
||||
children: string
|
||||
pid: string
|
||||
}
|
||||
import { findPath } from '@/utils/tree'
|
||||
|
||||
type OnlyOneChildType = AppRouteRecordRaw & { noShowingChildren?: boolean }
|
||||
|
||||
|
@ -14,50 +9,15 @@ interface HasOneShowingChild {
|
|||
onlyOneChild?: OnlyOneChildType
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: TreeConfig = {
|
||||
id: 'id',
|
||||
children: 'children',
|
||||
pid: 'pid'
|
||||
}
|
||||
|
||||
const getConfig = (config: Partial<TreeConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
|
||||
|
||||
export function getAllParentPath<T = Recordable>(treeData: T[], path: string) {
|
||||
export const getAllParentPath = <T = Recordable>(treeData: T[], path: string) => {
|
||||
const menuList = findPath(treeData, (n) => n.path === path) as AppRouteRecordRaw[]
|
||||
return (menuList || []).map((item) => item.path)
|
||||
}
|
||||
|
||||
export function findPath<T = any>(
|
||||
tree: any,
|
||||
func: Fn,
|
||||
config: Partial<TreeConfig> = {}
|
||||
): T | T[] | null {
|
||||
config = getConfig(config)
|
||||
const path: T[] = []
|
||||
const list = [...tree]
|
||||
const visitedSet = new Set()
|
||||
const { children } = config
|
||||
while (list.length) {
|
||||
const node = list[0]
|
||||
if (visitedSet.has(node)) {
|
||||
path.pop()
|
||||
list.shift()
|
||||
} else {
|
||||
visitedSet.add(node)
|
||||
node[children!] && list.unshift(...node[children!])
|
||||
path.push(node)
|
||||
if (func(node)) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function hasOneShowingChild(
|
||||
export const hasOneShowingChild = (
|
||||
children: AppRouteRecordRaw[] = [],
|
||||
parent: AppRouteRecordRaw
|
||||
): HasOneShowingChild {
|
||||
): HasOneShowingChild => {
|
||||
const onlyOneChild = ref<OnlyOneChildType>()
|
||||
|
||||
const showingChildren = children.filter((v) => {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useFullscreen } from '@vueuse/core'
|
|||
|
||||
const { toggle, isFullscreen } = useFullscreen()
|
||||
|
||||
function toggleFullscreen() {
|
||||
const toggleFullscreen = () => {
|
||||
toggle()
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -9,7 +9,7 @@ const appStore = useAppStore()
|
|||
|
||||
const sizeMap = computed(() => appStore.sizeMap)
|
||||
|
||||
function setSize(size: ElememtPlusSzie) {
|
||||
const setSize = (size: ElememtPlusSzie) => {
|
||||
appStore.setSize(size)
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div>tagsView</div>
|
||||
<div class="h-[var(--tags-view-height)]">tagsView</div>
|
||||
</template>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import { ref } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { ElSwitch } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useIcon } from '@/hooks/web/useIcon'
|
||||
|
||||
const Sun = useIcon({ icon: 'emojione-monotone:sun', color: '#fde047' })
|
||||
|
@ -14,21 +13,17 @@ const appStore = useAppStore()
|
|||
// 初始化获取是否是暗黑主题
|
||||
const isDark = ref(appStore.getIsDark)
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('theme-switch')
|
||||
|
||||
// 设置switch的背景颜色
|
||||
const blackColor = 'var(--el-color-black)'
|
||||
|
||||
function themeChange(val: boolean) {
|
||||
const themeChange = (val: boolean) => {
|
||||
appStore.setIsDark(val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElSwitch
|
||||
:class="prefixCls"
|
||||
class="v-theme-switch"
|
||||
v-model="isDark"
|
||||
inline-prompt
|
||||
:border-color="blackColor"
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useI18n } from '@/hooks/web/useI18n'
|
|||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { resetRouter } from '@/router'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { loginOutApi } from '@/api/login'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
@ -11,16 +12,19 @@ const { wsCache } = useCache()
|
|||
|
||||
const { replace } = useRouter()
|
||||
|
||||
function loginOut() {
|
||||
const loginOut = () => {
|
||||
ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), {
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then(() => {
|
||||
wsCache.clear()
|
||||
resetRouter() // 重置静态路由表
|
||||
replace('/login')
|
||||
.then(async () => {
|
||||
const res = await loginOutApi().catch(() => {})
|
||||
if (res) {
|
||||
wsCache.clear()
|
||||
resetRouter() // 重置静态路由表
|
||||
replace('/login')
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { App } from 'vue'
|
||||
import { Icon } from './Icon'
|
||||
|
||||
export function setupGlobCom(app: App<Element>): void {
|
||||
export const setupGlobCom = (app: App<Element>): void => {
|
||||
app.component('Icon', Icon)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ export interface AppState {
|
|||
isDark: boolean
|
||||
size: ElememtPlusSzie
|
||||
sizeMap: ElememtPlusSzie[]
|
||||
mobile: boolean
|
||||
}
|
||||
|
||||
export const appModules: AppState = {
|
||||
|
@ -45,5 +46,6 @@ export const appModules: AppState = {
|
|||
showMenuTab: false, // 是否固定一级菜单
|
||||
isDark: wsCache.get('isDark') || false, // 是否是暗黑模式
|
||||
size: wsCache.get('default') || 'default', // 组件尺寸
|
||||
sizeMap: ['default', 'large', 'small']
|
||||
sizeMap: ['default', 'large', 'small'],
|
||||
mobile: false // 是否是移动端
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import { config } from '@/config/axios/config'
|
|||
|
||||
const { default_headers } = config
|
||||
|
||||
export function useAxios() {
|
||||
function request(option: AxiosConfig): AxiosPromise {
|
||||
export const useAxios = () => {
|
||||
const request = (option: AxiosConfig): AxiosPromise => {
|
||||
const { url, method, params, data, headersType, responseType } = option
|
||||
return service({
|
||||
url: url,
|
||||
|
|
|
@ -6,7 +6,7 @@ import WebStorageCache from 'web-storage-cache'
|
|||
|
||||
type CacheType = 'sessionStorage' | 'localStorage'
|
||||
|
||||
export function useCache(type: CacheType = 'sessionStorage') {
|
||||
export const useCache = (type: CacheType = 'sessionStorage') => {
|
||||
const wsCache: WebStorageCache = new WebStorageCache({
|
||||
storage: type
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { inject } from 'vue'
|
||||
|
||||
export function useConfigGlobal() {
|
||||
export const useConfigGlobal = () => {
|
||||
const configGlobal = inject('configGlobal', {}) as ConfigGlobalTypes
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import variables from '@/styles/variables.module.less'
|
||||
|
||||
export function useDesign() {
|
||||
export const useDesign = () => {
|
||||
const lessVariables = variables
|
||||
|
||||
/**
|
||||
* @param scope 类名
|
||||
* @returns 返回空间名-类名
|
||||
*/
|
||||
function getPrefixCls(scope: string) {
|
||||
const getPrefixCls = (scope: string) => {
|
||||
return `${lessVariables.namespace}-${scope}`
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { Form, FormExpose } from '@/components/Form'
|
|||
import type { ElForm } from 'element-plus'
|
||||
import { ref, unref, nextTick } from 'vue'
|
||||
|
||||
export function useForm() {
|
||||
export const useForm = () => {
|
||||
// From实例
|
||||
const formRef = ref<typeof Form & FormExpose>()
|
||||
|
||||
|
@ -13,12 +13,12 @@ export function useForm() {
|
|||
* @param ref Form实例
|
||||
* @param elRef ElForm实例
|
||||
*/
|
||||
function register(ref: typeof Form & FormExpose, elRef: ComponentRef<typeof ElForm>) {
|
||||
const register = (ref: typeof Form & FormExpose, elRef: ComponentRef<typeof ElForm>) => {
|
||||
formRef.value = ref
|
||||
elFormRef.value = elRef
|
||||
}
|
||||
|
||||
async function getForm() {
|
||||
const getForm = async () => {
|
||||
const form = unref(formRef)
|
||||
if (!form) {
|
||||
console.error('The form is not registered. Please use the register method to register')
|
||||
|
|
|
@ -11,7 +11,7 @@ type I18nGlobalTranslation = {
|
|||
|
||||
type I18nTranslationRestParameters = [string, any]
|
||||
|
||||
function getKey(namespace: string | undefined, key: string) {
|
||||
const getKey = (namespace: string | undefined, key: string) => {
|
||||
if (!namespace) {
|
||||
return key
|
||||
}
|
||||
|
@ -21,9 +21,11 @@ function getKey(namespace: string | undefined, key: string) {
|
|||
return `${namespace}.${key}`
|
||||
}
|
||||
|
||||
export function useI18n(namespace?: string): {
|
||||
export const useI18n = (
|
||||
namespace?: string
|
||||
): {
|
||||
t: I18nGlobalTranslation
|
||||
} {
|
||||
} => {
|
||||
const normalFn = {
|
||||
t: (key: string) => {
|
||||
return getKey(namespace, key)
|
||||
|
|
|
@ -2,6 +2,6 @@ import { h } from 'vue'
|
|||
import type { VNode } from 'vue'
|
||||
import { Icon } from '@/components/Icon'
|
||||
|
||||
export function useIcon(props: IconTypes): VNode {
|
||||
export const useIcon = (props: IconTypes): VNode => {
|
||||
return h(Icon, props)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { i18n } from '@/plugins/vueI18n'
|
|||
import { useLocaleStoreWithOut } from '@/store/modules/locale'
|
||||
import { setHtmlPageLang } from '@/plugins/vueI18n/helper'
|
||||
|
||||
function setI18nLanguage(locale: LocaleType) {
|
||||
const setI18nLanguage = (locale: LocaleType) => {
|
||||
const localeStore = useLocaleStoreWithOut()
|
||||
|
||||
if (i18n.mode === 'legacy') {
|
||||
|
@ -16,10 +16,10 @@ function setI18nLanguage(locale: LocaleType) {
|
|||
setHtmlPageLang(locale)
|
||||
}
|
||||
|
||||
export function useLocale() {
|
||||
export const useLocale = () => {
|
||||
// Switching the language will change the locale of useI18n
|
||||
// And submit to configuration modification
|
||||
async function changeLocale(locale: LocaleType) {
|
||||
const changeLocale = async (locale: LocaleType) => {
|
||||
const globalI18n = i18n.global
|
||||
|
||||
const langModule = await import(`../../locales/${locale}.ts`)
|
||||
|
|
|
@ -6,11 +6,10 @@ import { useCssVar } from '@vueuse/core'
|
|||
|
||||
const primaryColor = useCssVar('--el-color-primary', document.documentElement)
|
||||
|
||||
export function useNProgress() {
|
||||
export const useNProgress = () => {
|
||||
NProgress.configure({ showSpinner: false } as NProgressOptions)
|
||||
initColor()
|
||||
|
||||
async function initColor() {
|
||||
const initColor = async () => {
|
||||
await nextTick()
|
||||
const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef
|
||||
if (bar) {
|
||||
|
@ -18,11 +17,13 @@ export function useNProgress() {
|
|||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
initColor()
|
||||
|
||||
const start = () => {
|
||||
NProgress.start()
|
||||
}
|
||||
|
||||
function done() {
|
||||
const done = () => {
|
||||
NProgress.done()
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useI18n } from '@/hooks/web/useI18n'
|
|||
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
export function useTitle(newTitle?: string) {
|
||||
export const useTitle = (newTitle?: string) => {
|
||||
const { t } = useI18n()
|
||||
const title = ref(
|
||||
newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle
|
||||
|
|
|
@ -3,13 +3,13 @@ import { computed, defineComponent, KeepAlive } from 'vue'
|
|||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { Menu } from '@/components/Menu'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { Collapse } from '@/components/Collapse'
|
||||
import { LocaleDropdown } from '@/components/LocaleDropdown'
|
||||
import { SizeDropdown } from '@/components/SizeDropdown'
|
||||
import { UserInfo } from '@/components/UserInfo'
|
||||
import { Screenfull } from '@/components/Screenfull'
|
||||
// import { TagsView } from '@/components/TagsView'
|
||||
import { Breadcrumb } from '@/components/Breadcrumb'
|
||||
import { TagsView } from '@/components/TagsView'
|
||||
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
|
||||
|
@ -19,40 +19,50 @@ const getCaches = computed((): string[] => {
|
|||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const mobile = computed(() => appStore.getMobile)
|
||||
|
||||
const collapse = computed(() => appStore.getCollapse)
|
||||
|
||||
const classSuffix = computed(() => appStore.getLayout)
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const perFixCls = getPrefixCls('app')
|
||||
const handleClickOutside = () => {
|
||||
appStore.setCollapse(true)
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Layout',
|
||||
setup() {
|
||||
return () => (
|
||||
<section
|
||||
class={[perFixCls, `${perFixCls}__${classSuffix.value}`, 'w-[100%] h-[100%] relative']}
|
||||
>
|
||||
<section class={['v-app', `v-app__${classSuffix.value}`, 'w-[100%] h-[100%] relative']}>
|
||||
{mobile.value && !collapse.value ? (
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full opacity-30 z-99 bg-[var(--el-color-black)]"
|
||||
onClick={handleClickOutside}
|
||||
></div>
|
||||
) : undefined}
|
||||
<Menu class="absolute top-0 left-0"></Menu>
|
||||
<div
|
||||
class={[
|
||||
`${perFixCls}-right`,
|
||||
'v-app-right',
|
||||
'absolute top-0 h-[100%]',
|
||||
appStore.getCollapse
|
||||
collapse.value
|
||||
? 'w-[calc(100%-var(--left-menu-min-width))]'
|
||||
: 'w-[calc(100%-var(--left-menu-max-width))]',
|
||||
appStore.getCollapse
|
||||
collapse.value
|
||||
? 'left-[var(--left-menu-min-width)]'
|
||||
: 'left-[var(--left-menu-max-width)]'
|
||||
: 'left-[var(--left-menu-max-width)]',
|
||||
'<md:(!left-0 !w-[100%])'
|
||||
]}
|
||||
>
|
||||
<div
|
||||
class={[
|
||||
`${perFixCls}-right__tool`,
|
||||
'v-app-right__tool',
|
||||
'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between'
|
||||
]}
|
||||
>
|
||||
<div class="h-full flex items-center">
|
||||
<Collapse class="header__tigger"></Collapse>
|
||||
<Breadcrumb class="<md:hidden"></Breadcrumb>
|
||||
</div>
|
||||
<div class="h-full flex items-center">
|
||||
<Screenfull class="header__tigger"></Screenfull>
|
||||
|
@ -61,6 +71,9 @@ export default defineComponent({
|
|||
<UserInfo class="header__tigger"></UserInfo>
|
||||
</div>
|
||||
</div>
|
||||
<div class="v-app-right__tags relative">
|
||||
<TagsView></TagsView>
|
||||
</div>
|
||||
<router-view>
|
||||
{{
|
||||
default: ({ Component, route }) => (
|
||||
|
@ -97,7 +110,8 @@ export default defineComponent({
|
|||
&-right {
|
||||
transition: left var(--transition-time-02);
|
||||
|
||||
&__tool {
|
||||
&__tool,
|
||||
&__tags {
|
||||
&::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
|
|
@ -31,7 +31,7 @@ import App from './App.vue'
|
|||
|
||||
import './permission'
|
||||
|
||||
async function setupAll() {
|
||||
const setupAll = async () => {
|
||||
const app = createApp(App)
|
||||
|
||||
await setupI18n(app)
|
||||
|
|
|
@ -7,7 +7,7 @@ const plugins = [ElLoading]
|
|||
|
||||
const components = [ElScrollbar]
|
||||
|
||||
export function setupElementPlus(app: App) {
|
||||
export const setupElementPlus = (app: App) => {
|
||||
plugins.forEach((plugin) => {
|
||||
app.use(plugin)
|
||||
})
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export function setHtmlPageLang(locale: LocaleType) {
|
||||
export const setHtmlPageLang = (locale: LocaleType) => {
|
||||
document.querySelector('html')?.setAttribute('lang', locale)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { setHtmlPageLang } from './helper'
|
|||
|
||||
export let i18n: ReturnType<typeof createI18n>
|
||||
|
||||
async function createI18nOptions(): Promise<I18nOptions> {
|
||||
const createI18nOptions = async (): Promise<I18nOptions> => {
|
||||
const localeStore = useLocaleStoreWithOut()
|
||||
const locale = localeStore.getLocale
|
||||
const localeMap = localeStore.getLocaleMap
|
||||
|
@ -35,7 +35,7 @@ async function createI18nOptions(): Promise<I18nOptions> {
|
|||
}
|
||||
}
|
||||
|
||||
export async function setupI18n(app: App) {
|
||||
export const setupI18n = async (app: App) => {
|
||||
const options = await createI18nOptions()
|
||||
i18n = createI18n(options) as I18n
|
||||
app.use(i18n)
|
||||
|
|
|
@ -121,7 +121,7 @@ const router = createRouter({
|
|||
scrollBehavior: () => ({ left: 0, top: 0 })
|
||||
})
|
||||
|
||||
export function resetRouter(): void {
|
||||
export const resetRouter = (): void => {
|
||||
const resetWhiteNameList = ['RedirectRoot', 'Redirect', 'Login', 'Root', 'Dashboard', 'Page404']
|
||||
router.getRoutes().forEach((route) => {
|
||||
const { name } = route
|
||||
|
@ -131,7 +131,7 @@ export function resetRouter(): void {
|
|||
})
|
||||
}
|
||||
|
||||
export function setupRouter(app: App<Element>) {
|
||||
export const setupRouter = (app: App<Element>) => {
|
||||
app.use(router)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { createPinia } from 'pinia'
|
|||
|
||||
const store = createPinia()
|
||||
|
||||
export function setupStore(app: App<Element>) {
|
||||
export const setupStore = (app: App<Element>) => {
|
||||
app.use(store)
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,9 @@ export const useAppStore = defineStore({
|
|||
},
|
||||
getSizeMap(): ElememtPlusSzie[] {
|
||||
return this.sizeMap
|
||||
},
|
||||
getMobile(): boolean {
|
||||
return this.mobile
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -128,10 +131,13 @@ export const useAppStore = defineStore({
|
|||
setSize(size: ElememtPlusSzie) {
|
||||
this.size = size
|
||||
wsCache.set('size', this.size)
|
||||
},
|
||||
setMobile(mobile: boolean) {
|
||||
this.mobile = mobile
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export function useAppStoreWithOut() {
|
||||
export const useAppStoreWithOut = () => {
|
||||
return useAppStore(store)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,6 @@ export const useLocaleStore = defineStore({
|
|||
}
|
||||
})
|
||||
|
||||
export function useLocaleStoreWithOut() {
|
||||
export const useLocaleStoreWithOut = () => {
|
||||
return useLocaleStore(store)
|
||||
}
|
||||
|
|
|
@ -91,6 +91,6 @@ export const usePermissionStore = defineStore({
|
|||
}
|
||||
})
|
||||
|
||||
export function usePermissionStoreWithOut() {
|
||||
export const usePermissionStoreWithOut = () => {
|
||||
return usePermissionStore(store)
|
||||
}
|
||||
|
|
|
@ -170,6 +170,6 @@ export const useTagsViewStore = defineStore({
|
|||
}
|
||||
})
|
||||
|
||||
export function useTagsViewStoreWithOut() {
|
||||
export const useTagsViewStoreWithOut = () => {
|
||||
return useTagsViewStore(store)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* @param String color 十六进制颜色值
|
||||
* @return Boolean
|
||||
*/
|
||||
export function isHexColor(color: string) {
|
||||
export const isHexColor = (color: string) => {
|
||||
const reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-f]{6})$/
|
||||
return reg.test(color)
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export function isHexColor(color: string) {
|
|||
* @param g
|
||||
* @param b
|
||||
*/
|
||||
export function rgbToHex(r: number, g: number, b: number) {
|
||||
export const rgbToHex = (r: number, g: number, b: number) => {
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
const hex = ((r << 16) | (g << 8) | b).toString(16)
|
||||
return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex
|
||||
|
@ -30,7 +30,7 @@ export function rgbToHex(r: number, g: number, b: number) {
|
|||
* @param {string} hex The color to transform
|
||||
* @returns The RGB representation of the passed color
|
||||
*/
|
||||
export function hexToRGB(hex: string) {
|
||||
export const hexToRGB = (hex: string) => {
|
||||
let sHex = hex.toLowerCase()
|
||||
if (isHexColor(hex)) {
|
||||
if (sHex.length === 4) {
|
||||
|
@ -49,7 +49,7 @@ export function hexToRGB(hex: string) {
|
|||
return sHex
|
||||
}
|
||||
|
||||
export function colorIsDark(color: string) {
|
||||
export const colorIsDark = (color: string) => {
|
||||
if (!isHexColor(color)) return
|
||||
const [r, g, b] = hexToRGB(color)
|
||||
.replace(/(?:\(|\)|rgb|RGB)*/g, '')
|
||||
|
@ -64,7 +64,7 @@ export function colorIsDark(color: string) {
|
|||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The HEX representation of the processed color
|
||||
*/
|
||||
export function darken(color: string, amount: number) {
|
||||
export const darken = (color: string, amount: number) => {
|
||||
color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color
|
||||
amount = Math.trunc((255 * amount) / 100)
|
||||
return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight(
|
||||
|
@ -79,7 +79,7 @@ export function darken(color: string, amount: number) {
|
|||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The processed color represented as HEX
|
||||
*/
|
||||
export function lighten(color: string, amount: number) {
|
||||
export const lighten = (color: string, amount: number) => {
|
||||
color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color
|
||||
amount = Math.trunc((255 * amount) / 100)
|
||||
return `#${addLight(color.substring(0, 2), amount)}${addLight(
|
||||
|
@ -95,7 +95,7 @@ export function lighten(color: string, amount: number) {
|
|||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The processed part of the color
|
||||
*/
|
||||
function addLight(color: string, amount: number) {
|
||||
const addLight = (color: string, amount: number) => {
|
||||
const cc = parseInt(color, 16) + amount
|
||||
const c = cc > 255 ? 255 : cc
|
||||
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
|
||||
|
@ -107,7 +107,7 @@ function addLight(color: string, amount: number) {
|
|||
* @param {number} g green
|
||||
* @param {number} b blue
|
||||
*/
|
||||
function luminanace(r: number, g: number, b: number) {
|
||||
const luminanace = (r: number, g: number, b: number) => {
|
||||
const a = [r, g, b].map((v) => {
|
||||
v /= 255
|
||||
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)
|
||||
|
@ -120,7 +120,7 @@ function luminanace(r: number, g: number, b: number) {
|
|||
* @param {string} rgb1 rgb color 1
|
||||
* @param {string} rgb2 rgb color 2
|
||||
*/
|
||||
function contrast(rgb1: string[], rgb2: number[]) {
|
||||
const contrast = (rgb1: string[], rgb2: number[]) => {
|
||||
return (
|
||||
(luminanace(~~rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) /
|
||||
(luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05)
|
||||
|
@ -131,7 +131,7 @@ function contrast(rgb1: string[], rgb2: number[]) {
|
|||
* Determines what the best text color is (black or white) based con the contrast with the background
|
||||
* @param hexColor - Last selected color by the user
|
||||
*/
|
||||
export function calculateBestTextColor(hexColor: string) {
|
||||
export const calculateBestTextColor = (hexColor: string) => {
|
||||
const rgbColor = hexToRGB(hexColor.substring(1))
|
||||
const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0])
|
||||
|
||||
|
@ -144,7 +144,7 @@ export function calculateBestTextColor(hexColor: string) {
|
|||
* @param {number} amount The amount to change the color by
|
||||
* @returns {string} The processed part of the color
|
||||
*/
|
||||
function subtractLight(color: string, amount: number) {
|
||||
const subtractLight = (color: string, amount: number) => {
|
||||
const cc = parseInt(color, 16) - amount
|
||||
const c = cc < 0 ? 0 : cc
|
||||
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
|
||||
|
|
|
@ -21,7 +21,7 @@ export const withInstall = <T>(component: T, alias?: string) => {
|
|||
* @param str 需要转下划线的驼峰字符串
|
||||
* @returns 字符串下划线
|
||||
*/
|
||||
export function humpToUnderline(str: string): string {
|
||||
export const humpToUnderline = (str: string): string => {
|
||||
return str.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||
}
|
||||
|
||||
|
@ -29,12 +29,12 @@ export function humpToUnderline(str: string): string {
|
|||
* @param str 需要转驼峰的下划线字符串
|
||||
* @returns 字符串驼峰
|
||||
*/
|
||||
export function underlineToHump(str: string): string {
|
||||
return str.replace(/\-(\w)/g, function (_, letter: string) {
|
||||
export const underlineToHump = (str: string): string => {
|
||||
return str.replace(/\-(\w)/g, (_, letter: string) => {
|
||||
return letter.toUpperCase()
|
||||
})
|
||||
}
|
||||
|
||||
export function setCssVar(prop: string, val: any, dom = document.documentElement) {
|
||||
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
|
||||
dom.style.setProperty(prop, val)
|
||||
}
|
||||
|
|
|
@ -2,23 +2,23 @@
|
|||
|
||||
const toString = Object.prototype.toString
|
||||
|
||||
export function is(val: unknown, type: string) {
|
||||
export const is = (val: unknown, type: string) => {
|
||||
return toString.call(val) === `[object ${type}]`
|
||||
}
|
||||
|
||||
export function isDef<T = unknown>(val?: T): val is T {
|
||||
export const isDef = <T = unknown>(val?: T): val is T => {
|
||||
return typeof val !== 'undefined'
|
||||
}
|
||||
|
||||
export function isUnDef<T = unknown>(val?: T): val is T {
|
||||
export const isUnDef = <T = unknown>(val?: T): val is T => {
|
||||
return !isDef(val)
|
||||
}
|
||||
|
||||
export function isObject(val: any): val is Record<any, any> {
|
||||
export const isObject = (val: any): val is Record<any, any> => {
|
||||
return val !== null && is(val, 'Object')
|
||||
}
|
||||
|
||||
export function isEmpty<T = unknown>(val: T): val is T {
|
||||
export const isEmpty = <T = unknown>(val: T): val is T => {
|
||||
if (isArray(val) || isString(val)) {
|
||||
return val.length === 0
|
||||
}
|
||||
|
@ -34,59 +34,59 @@ export function isEmpty<T = unknown>(val: T): val is T {
|
|||
return false
|
||||
}
|
||||
|
||||
export function isDate(val: unknown): val is Date {
|
||||
export const isDate = (val: unknown): val is Date => {
|
||||
return is(val, 'Date')
|
||||
}
|
||||
|
||||
export function isNull(val: unknown): val is null {
|
||||
export const isNull = (val: unknown): val is null => {
|
||||
return val === null
|
||||
}
|
||||
|
||||
export function isNullAndUnDef(val: unknown): val is null | undefined {
|
||||
export const isNullAndUnDef = (val: unknown): val is null | undefined => {
|
||||
return isUnDef(val) && isNull(val)
|
||||
}
|
||||
|
||||
export function isNullOrUnDef(val: unknown): val is null | undefined {
|
||||
export const isNullOrUnDef = (val: unknown): val is null | undefined => {
|
||||
return isUnDef(val) || isNull(val)
|
||||
}
|
||||
|
||||
export function isNumber(val: unknown): val is number {
|
||||
export const isNumber = (val: unknown): val is number => {
|
||||
return is(val, 'Number')
|
||||
}
|
||||
|
||||
export function isPromise<T = any>(val: unknown): val is Promise<T> {
|
||||
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
|
||||
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
|
||||
}
|
||||
|
||||
export function isString(val: unknown): val is string {
|
||||
export const isString = (val: unknown): val is string => {
|
||||
return is(val, 'String')
|
||||
}
|
||||
|
||||
export function isFunction(val: unknown): val is Function {
|
||||
export const isFunction = (val: unknown): val is Function => {
|
||||
return typeof val === 'function'
|
||||
}
|
||||
|
||||
export function isBoolean(val: unknown): val is boolean {
|
||||
export const isBoolean = (val: unknown): val is boolean => {
|
||||
return is(val, 'Boolean')
|
||||
}
|
||||
|
||||
export function isRegExp(val: unknown): val is RegExp {
|
||||
export const isRegExp = (val: unknown): val is RegExp => {
|
||||
return is(val, 'RegExp')
|
||||
}
|
||||
|
||||
export function isArray(val: any): val is Array<any> {
|
||||
export const isArray = (val: any): val is Array<any> => {
|
||||
return val && Array.isArray(val)
|
||||
}
|
||||
|
||||
export function isWindow(val: any): val is Window {
|
||||
export const isWindow = (val: any): val is Window => {
|
||||
return typeof window !== 'undefined' && is(val, 'Window')
|
||||
}
|
||||
|
||||
export function isElement(val: unknown): val is Element {
|
||||
export const isElement = (val: unknown): val is Element => {
|
||||
return isObject(val) && !!val.tagName
|
||||
}
|
||||
|
||||
export function isMap(val: unknown): val is Map<any, any> {
|
||||
export const isMap = (val: unknown): val is Map<any, any> => {
|
||||
return is(val, 'Map')
|
||||
}
|
||||
|
||||
|
@ -94,12 +94,12 @@ export const isServer = typeof window === 'undefined'
|
|||
|
||||
export const isClient = !isServer
|
||||
|
||||
export function isUrl(path: string): boolean {
|
||||
export const isUrl = (path: string): boolean => {
|
||||
const reg =
|
||||
/(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
|
||||
return reg.test(path)
|
||||
}
|
||||
|
||||
export function isDark(): boolean {
|
||||
export const isDark = (): boolean => {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
|
||||
import type { Router, RouteLocationNormalized, RouteRecordNormalized, RouteMeta } from 'vue-router'
|
||||
import { isUrl } from '@/utils/is'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
|
@ -23,7 +23,7 @@ export const getParentLayout = () => {
|
|||
})
|
||||
}
|
||||
|
||||
export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormalized {
|
||||
export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormalized => {
|
||||
if (!route) return route
|
||||
const { matched, ...opt } = route
|
||||
return {
|
||||
|
@ -39,15 +39,16 @@ export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormal
|
|||
}
|
||||
|
||||
// 前端控制路由生成
|
||||
export function generateRoutesFn1(
|
||||
export const generateRoutesFn1 = (
|
||||
routes: AppRouteRecordRaw[],
|
||||
basePath = '/'
|
||||
): AppRouteRecordRaw[] {
|
||||
): AppRouteRecordRaw[] => {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
const meta = route.meta as RouteMeta
|
||||
// skip some route
|
||||
if (route.meta && route.meta.hidden && !route.meta.showMainRoute) {
|
||||
if (meta.hidden && !meta.showMainRoute) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -55,7 +56,7 @@ export function generateRoutesFn1(
|
|||
|
||||
let onlyOneChild: Nullable<string> = null
|
||||
|
||||
if (route.children && route.children.length === 1 && !route.meta.alwaysShow) {
|
||||
if (route.children && route.children.length === 1 && !meta.alwaysShow) {
|
||||
onlyOneChild = (
|
||||
isUrl(route.children[0].path)
|
||||
? route.children[0].path
|
||||
|
@ -72,7 +73,7 @@ export function generateRoutesFn1(
|
|||
data = Object.assign({}, route)
|
||||
} else {
|
||||
const routePath = pathResolve(basePath, onlyOneChild || route.path)
|
||||
if (routePath === item.path || (route.meta && route.meta.followRoute === item.path)) {
|
||||
if (routePath === item.path || meta.followRoute === item.path) {
|
||||
data = Object.assign({}, route)
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +91,7 @@ export function generateRoutesFn1(
|
|||
}
|
||||
|
||||
// 后端控制路由生成
|
||||
export function generateRoutesFn2(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
|
||||
export const generateRoutesFn2 = (routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] => {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
|
@ -121,13 +122,13 @@ export function generateRoutesFn2(routes: AppRouteRecordRaw[]): AppRouteRecordRa
|
|||
return res
|
||||
}
|
||||
|
||||
export function pathResolve(parentPath: string, path: string) {
|
||||
export const pathResolve = (parentPath: string, path: string) => {
|
||||
const childPath = path.startsWith('/') || !path ? path : `/${path}`
|
||||
return `${parentPath}${childPath}`
|
||||
}
|
||||
|
||||
// 路由降级
|
||||
export function flatMultiLevelRoutes(routes: AppRouteRecordRaw[]) {
|
||||
export const flatMultiLevelRoutes = (routes: AppRouteRecordRaw[]) => {
|
||||
const modules: AppRouteRecordRaw[] = cloneDeep(routes)
|
||||
for (let index = 0; index < modules.length; index++) {
|
||||
const route = modules[index]
|
||||
|
@ -140,7 +141,7 @@ export function flatMultiLevelRoutes(routes: AppRouteRecordRaw[]) {
|
|||
}
|
||||
|
||||
// 层级是否大于2
|
||||
function isMultipleRoute(route: AppRouteRecordRaw) {
|
||||
const isMultipleRoute = (route: AppRouteRecordRaw) => {
|
||||
if (!route || !Reflect.has(route, 'children') || !route.children?.length) {
|
||||
return false
|
||||
}
|
||||
|
@ -159,7 +160,7 @@ function isMultipleRoute(route: AppRouteRecordRaw) {
|
|||
}
|
||||
|
||||
// 生成二级路由
|
||||
function promoteRouteLevel(route: AppRouteRecordRaw) {
|
||||
const promoteRouteLevel = (route: AppRouteRecordRaw) => {
|
||||
let router: Router | null = createRouter({
|
||||
routes: [route as unknown as RouteRecordNormalized],
|
||||
history: createWebHashHistory()
|
||||
|
@ -173,11 +174,11 @@ function promoteRouteLevel(route: AppRouteRecordRaw) {
|
|||
}
|
||||
|
||||
// 添加所有子菜单
|
||||
function addToChildren(
|
||||
const addToChildren = (
|
||||
routes: RouteRecordNormalized[],
|
||||
children: AppRouteRecordRaw[],
|
||||
routeModule: AppRouteRecordRaw
|
||||
) {
|
||||
) => {
|
||||
for (let index = 0; index < children.length; index++) {
|
||||
const child = children[index]
|
||||
const route = routes.find((item) => item.name === child.name)
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
interface TreeHelperConfig {
|
||||
id: string
|
||||
children: string
|
||||
pid: string
|
||||
}
|
||||
const DEFAULT_CONFIG: TreeHelperConfig = {
|
||||
id: 'id',
|
||||
children: 'children',
|
||||
pid: 'pid'
|
||||
}
|
||||
|
||||
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
|
||||
|
||||
// tree from list
|
||||
export const listToTree = <T = any>(list: any[], config: Partial<TreeHelperConfig> = {}): T[] => {
|
||||
const conf = getConfig(config) as TreeHelperConfig
|
||||
const nodeMap = new Map()
|
||||
const result: T[] = []
|
||||
const { id, children, pid } = conf
|
||||
|
||||
for (const node of list) {
|
||||
node[children] = node[children] || []
|
||||
nodeMap.set(node[id], node)
|
||||
}
|
||||
for (const node of list) {
|
||||
const parent = nodeMap.get(node[pid])
|
||||
;(parent ? parent.children : result).push(node)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const treeToList = <T = any>(tree: any, config: Partial<TreeHelperConfig> = {}): T => {
|
||||
config = getConfig(config)
|
||||
const { children } = config
|
||||
const result: any = [...tree]
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
if (!result[i][children!]) continue
|
||||
result.splice(i + 1, 0, ...result[i][children!])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const findNode = <T = any>(
|
||||
tree: any,
|
||||
func: Fn,
|
||||
config: Partial<TreeHelperConfig> = {}
|
||||
): T | null => {
|
||||
config = getConfig(config)
|
||||
const { children } = config
|
||||
const list = [...tree]
|
||||
for (const node of list) {
|
||||
if (func(node)) return node
|
||||
node[children!] && list.push(...node[children!])
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const findNodeAll = <T = any>(
|
||||
tree: any,
|
||||
func: Fn,
|
||||
config: Partial<TreeHelperConfig> = {}
|
||||
): T[] => {
|
||||
config = getConfig(config)
|
||||
const { children } = config
|
||||
const list = [...tree]
|
||||
const result: T[] = []
|
||||
for (const node of list) {
|
||||
func(node) && result.push(node)
|
||||
node[children!] && list.push(...node[children!])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const findPath = <T = any>(
|
||||
tree: any,
|
||||
func: Fn,
|
||||
config: Partial<TreeHelperConfig> = {}
|
||||
): T | T[] | null => {
|
||||
config = getConfig(config)
|
||||
const path: T[] = []
|
||||
const list = [...tree]
|
||||
const visitedSet = new Set()
|
||||
const { children } = config
|
||||
while (list.length) {
|
||||
const node = list[0]
|
||||
if (visitedSet.has(node)) {
|
||||
path.pop()
|
||||
list.shift()
|
||||
} else {
|
||||
visitedSet.add(node)
|
||||
node[children!] && list.unshift(...node[children!])
|
||||
path.push(node)
|
||||
if (func(node)) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const findPathAll = (tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}) => {
|
||||
config = getConfig(config)
|
||||
const path: any[] = []
|
||||
const list = [...tree]
|
||||
const result: any[] = []
|
||||
const visitedSet = new Set(),
|
||||
{ children } = config
|
||||
while (list.length) {
|
||||
const node = list[0]
|
||||
if (visitedSet.has(node)) {
|
||||
path.pop()
|
||||
list.shift()
|
||||
} else {
|
||||
visitedSet.add(node)
|
||||
node[children!] && list.unshift(...node[children!])
|
||||
path.push(node)
|
||||
func(node) && result.push([...path])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const filter = <T = any>(
|
||||
tree: T[],
|
||||
func: (n: T) => boolean,
|
||||
config: Partial<TreeHelperConfig> = {}
|
||||
): T[] => {
|
||||
config = getConfig(config)
|
||||
const children = config.children as string
|
||||
function listFilter(list: T[]) {
|
||||
return list
|
||||
.map((node: any) => ({ ...node }))
|
||||
.filter((node) => {
|
||||
node[children] = node[children] && listFilter(node[children])
|
||||
return func(node) || (node[children] && node[children].length)
|
||||
})
|
||||
}
|
||||
return listFilter(tree)
|
||||
}
|
||||
|
||||
export const forEach = <T = any>(
|
||||
tree: T[],
|
||||
func: (n: T) => any,
|
||||
config: Partial<TreeHelperConfig> = {}
|
||||
): void => {
|
||||
config = getConfig(config)
|
||||
const list: any[] = [...tree]
|
||||
const { children } = config
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
//func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿
|
||||
if (func(list[i])) {
|
||||
return
|
||||
}
|
||||
children && list[i][children] && list.splice(i + 1, 0, ...list[i][children])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Extract tree specified structure
|
||||
*/
|
||||
export const treeMap = <T = any>(
|
||||
treeData: T[],
|
||||
opt: { children?: string; conversion: Fn }
|
||||
): T[] => {
|
||||
return treeData.map((item) => treeMapEach(item, opt))
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Extract tree specified structure
|
||||
*/
|
||||
export const treeMapEach = (
|
||||
data: any,
|
||||
{ children = 'children', conversion }: { children?: string; conversion: Fn }
|
||||
) => {
|
||||
const haveChildren = Array.isArray(data[children]) && data[children].length > 0
|
||||
const conversionData = conversion(data) || {}
|
||||
if (haveChildren) {
|
||||
return {
|
||||
...conversionData,
|
||||
[children]: data[children].map((i: number) =>
|
||||
treeMapEach(i, {
|
||||
children,
|
||||
conversion
|
||||
})
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...conversionData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归遍历树结构
|
||||
* @param treeDatas 树
|
||||
* @param callBack 回调
|
||||
* @param parentNode 父节点
|
||||
*/
|
||||
export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => {
|
||||
treeDatas.forEach((element) => {
|
||||
const newNode = callBack(element, parentNode) || element
|
||||
if (element.children) {
|
||||
eachTree(element.children, callBack, newNode)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { Slots } from 'vue'
|
||||
import { isFunction } from '@/utils/is'
|
||||
|
||||
export function getSlot(slots: Slots, slot = 'default', data?: Recordable) {
|
||||
export const getSlot = (slots: Slots, slot = 'default', data?: Recordable) => {
|
||||
// Reflect.has 判断一个对象是否存在某个属性
|
||||
if (!slots || !Reflect.has(slots, slot)) {
|
||||
return null
|
||||
|
|
|
@ -111,7 +111,7 @@ watch(
|
|||
)
|
||||
|
||||
// 登录
|
||||
async function signIn() {
|
||||
const signIn = async () => {
|
||||
const formRef = unref(elFormRef)
|
||||
const validate = await formRef?.validate()?.catch(() => {})
|
||||
if (validate) {
|
||||
|
|
Loading…
Reference in New Issue