mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-06 20:03:19 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce810aa633 | ||
|
|
eabb43ecab | ||
|
|
8bfa0f0c27 | ||
|
|
05261b37bb | ||
|
|
b974139eeb | ||
|
|
867852e501 | ||
|
|
2cadef4ad1 | ||
|
|
3b889f611f | ||
|
|
9f37438669 | ||
|
|
b7c627c556 | ||
|
|
7d2279f38d | ||
|
|
eb306b18bc | ||
|
|
8d4e226c17 | ||
|
|
1edf486160 | ||
|
|
e2d058abdd | ||
|
|
9b5003fad8 | ||
|
|
cbef1bb52e |
+38
-38
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "module",
|
||||
"version": "5.3.0",
|
||||
"version": "5.4.0",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
@@ -25,87 +25,87 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vee-validate/zod": "^4.15.0",
|
||||
"@vueuse/components": "^13.1.0",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"@vueuse/integrations": "^13.1.0",
|
||||
"@vueuse/components": "^13.3.0",
|
||||
"@vueuse/core": "^13.3.0",
|
||||
"@vueuse/integrations": "^13.3.0",
|
||||
"axios": "^1.9.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"defu": "^6.1.4",
|
||||
"disable-devtool": "^0.3.8",
|
||||
"element-plus": "^2.9.9",
|
||||
"element-plus": "^2.9.11",
|
||||
"eruda": "^3.4.1",
|
||||
"es-toolkit": "^1.36.0",
|
||||
"hotkeys-js": "^3.13.9",
|
||||
"lucide-vue-next": "^0.503.0",
|
||||
"es-toolkit": "^1.38.0",
|
||||
"hotkeys-js": "^3.13.10",
|
||||
"lucide-vue-next": "^0.511.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^8.2.0",
|
||||
"pinia": "^3.0.2",
|
||||
"qs": "^6.14.0",
|
||||
"reka-ui": "^2.2.0",
|
||||
"reka-ui": "^2.3.0",
|
||||
"scule": "^1.3.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"ua-parser-js": "^2.0.3",
|
||||
"vconsole": "^3.15.1",
|
||||
"vee-validate": "^4.15.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue": "^3.5.16",
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-sonner": "^1.3.2",
|
||||
"zod": "^3.24.3"
|
||||
"vue-sonner": "^2.0.0",
|
||||
"zod": "^3.25.41"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "4.3.0",
|
||||
"@faker-js/faker": "^9.7.0",
|
||||
"@iconify/json": "^2.2.333",
|
||||
"@iconify/vue": "^4.3.0",
|
||||
"@antfu/eslint-config": "^4.13.2",
|
||||
"@faker-js/faker": "^9.8.0",
|
||||
"@iconify/json": "^2.2.342",
|
||||
"@iconify/vue": "^5.0.0",
|
||||
"@stylistic/stylelint-config": "^2.0.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/path-browserify": "^1.0.3",
|
||||
"@types/qs": "^6.9.18",
|
||||
"@unocss/eslint-plugin": "^66.0.0",
|
||||
"@unocss/preset-legacy-compat": "^66.0.0",
|
||||
"@types/qs": "^6.14.0",
|
||||
"@unocss/eslint-plugin": "^66.1.2",
|
||||
"@unocss/preset-legacy-compat": "^66.1.2",
|
||||
"@vitejs/plugin-legacy": "^6.1.1",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"@vitejs/plugin-vue-jsx": "^4.2.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"boxen": "^8.0.1",
|
||||
"bumpp": "^10.1.0",
|
||||
"bumpp": "^10.1.1",
|
||||
"cz-git": "^1.11.1",
|
||||
"eslint": "^9.25.1",
|
||||
"eslint": "^9.27.0",
|
||||
"esno": "^4.8.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"http-server": "^14.1.1",
|
||||
"inquirer": "^12.6.0",
|
||||
"lint-staged": "^15.5.1",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"inquirer": "^12.6.3",
|
||||
"lint-staged": "^16.1.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"picocolors": "^1.1.1",
|
||||
"plop": "^4.0.1",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss": "^8.5.4",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"sass-embedded": "^1.87.0",
|
||||
"sass-embedded": "^1.89.0",
|
||||
"simple-git-hooks": "^2.13.0",
|
||||
"stylelint": "^16.19.1",
|
||||
"stylelint": "^16.20.0",
|
||||
"stylelint-config-recess-order": "^6.0.0",
|
||||
"stylelint-config-standard-scss": "^14.0.0",
|
||||
"stylelint-config-standard-scss": "^15.0.1",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-scss": "^6.11.1",
|
||||
"stylelint-scss": "^6.12.0",
|
||||
"svgo": "^3.3.2",
|
||||
"taze": "^19.0.4",
|
||||
"taze": "^19.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"unocss": "^66.0.0",
|
||||
"unocss": "^66.1.2",
|
||||
"unocss-preset-animations": "^1.2.1",
|
||||
"unplugin-auto-import": "^19.1.2",
|
||||
"unplugin-auto-import": "^19.3.0",
|
||||
"unplugin-turbo-console": "^2.1.3",
|
||||
"unplugin-vue-components": "^28.5.0",
|
||||
"vite": "^6.2.7",
|
||||
"unplugin-vue-components": "^28.7.0",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-app-loading": "^0.3.1",
|
||||
"vite-plugin-archiver": "^0.1.2",
|
||||
"vite-plugin-banner": "^0.8.1",
|
||||
"vite-plugin-compression2": "^1.3.3",
|
||||
"vite-plugin-compression2": "^1.4.0",
|
||||
"vite-plugin-env-parse": "^1.0.15",
|
||||
"vite-plugin-fake-server": "^2.2.0",
|
||||
"vite-plugin-pages": "^0.33.0",
|
||||
|
||||
Generated
+1810
-1325
File diff suppressed because it is too large
Load Diff
+58
-18
@@ -1,7 +1,19 @@
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import axios from 'axios'
|
||||
// import qs from 'qs'
|
||||
import { toast } from 'vue-sonner'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
// 请求重试配置
|
||||
const MAX_RETRY_COUNT = 3 // 最大重试次数
|
||||
const RETRY_DELAY = 1000 // 重试延迟时间(毫秒)
|
||||
|
||||
// 扩展 AxiosRequestConfig 类型
|
||||
declare module 'axios' {
|
||||
export interface AxiosRequestConfig {
|
||||
retry?: boolean
|
||||
retryCount?: number
|
||||
}
|
||||
}
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: (import.meta.env.DEV && import.meta.env.VITE_OPEN_PROXY) ? '/proxy/' : import.meta.env.VITE_APP_API_BASEURL,
|
||||
@@ -29,6 +41,28 @@ api.interceptors.request.use(
|
||||
},
|
||||
)
|
||||
|
||||
// 处理错误信息的函数
|
||||
function handleError(error: any) {
|
||||
if (error.status === 401) {
|
||||
useUserStore().requestLogout()
|
||||
throw error
|
||||
}
|
||||
let message = error.message
|
||||
if (message === 'Network Error') {
|
||||
message = '后端网络故障'
|
||||
}
|
||||
else if (message.includes('timeout')) {
|
||||
message = '接口请求超时'
|
||||
}
|
||||
else if (message.includes('Request failed with status code')) {
|
||||
message = `接口${message.substr(message.length - 3)}异常`
|
||||
}
|
||||
toast.error('Error', {
|
||||
description: message,
|
||||
})
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
api.interceptors.response.use(
|
||||
(response) => {
|
||||
/**
|
||||
@@ -50,25 +84,31 @@ api.interceptors.response.use(
|
||||
}
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(error) => {
|
||||
if (error.status === 401) {
|
||||
useUserStore().requestLogout()
|
||||
throw error
|
||||
async (error) => {
|
||||
// 获取请求配置
|
||||
const config = error.config
|
||||
|
||||
// 如果配置不存在或未启用重试,则直接处理错误
|
||||
if (!config || !config.retry) {
|
||||
return handleError(error)
|
||||
}
|
||||
let message = error.message
|
||||
if (message === 'Network Error') {
|
||||
message = '后端网络故障'
|
||||
|
||||
// 设置重试次数
|
||||
config.retryCount = config.retryCount || 0
|
||||
|
||||
// 判断是否超过重试次数
|
||||
if (config.retryCount >= MAX_RETRY_COUNT) {
|
||||
return handleError(error)
|
||||
}
|
||||
else if (message.includes('timeout')) {
|
||||
message = '接口请求超时'
|
||||
}
|
||||
else if (message.includes('Request failed with status code')) {
|
||||
message = `接口${message.substr(message.length - 3)}异常`
|
||||
}
|
||||
toast.error('Error', {
|
||||
description: message,
|
||||
})
|
||||
return Promise.reject(error)
|
||||
|
||||
// 重试次数自增
|
||||
config.retryCount += 1
|
||||
|
||||
// 延迟重试
|
||||
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY))
|
||||
|
||||
// 重新发起请求
|
||||
return api(config)
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ const isProfileShow = ref(false)
|
||||
],
|
||||
]" class="flex-center"
|
||||
>
|
||||
<template #label>
|
||||
<template #header>
|
||||
<div class="space-y-2">
|
||||
<div class="text-xs text-secondary-foreground/50 font-light">
|
||||
当前登录账号
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toast } from 'vue-sonner'
|
||||
import * as z from 'zod'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
|
||||
defineOptions({
|
||||
name: 'EditPasswordForm',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import * as z from 'zod'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
|
||||
defineOptions({
|
||||
name: 'LoginForm',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import * as z from 'zod'
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
|
||||
defineOptions({
|
||||
name: 'RegisterForm',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import * as z from 'zod'
|
||||
import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
|
||||
defineOptions({
|
||||
name: 'ResetPasswordForm',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { UploadProps, UploadUserFile } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
defineOptions({
|
||||
name: 'FileUpload',
|
||||
@@ -47,16 +47,16 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传文件只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
toast.error(`上传文件只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传文件大小不能超过 ${props.size}MB!`)
|
||||
toast.error(`上传文件大小不能超过 ${props.size}MB!`)
|
||||
}
|
||||
return isTypeOk && isSizeOk
|
||||
}
|
||||
|
||||
const onExceed: UploadProps['onExceed'] = () => {
|
||||
ElMessage.warning('文件上传超过限制')
|
||||
toast.warning('文件上传超过限制')
|
||||
}
|
||||
|
||||
const onSuccess: UploadProps['onSuccess'] = (res, file, fileList) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { UploadProps } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
defineOptions({
|
||||
name: 'ImageUpload',
|
||||
@@ -67,10 +67,10 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
toast.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
toast.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
}
|
||||
if (isTypeOk && isSizeOk) {
|
||||
uploadData.value.progress.preview = URL.createObjectURL(file)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { UploadProps } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
defineOptions({
|
||||
name: 'ImagesUpload',
|
||||
@@ -81,10 +81,10 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
toast.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
toast.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
}
|
||||
if (isTypeOk && isSizeOk) {
|
||||
uploadData.value.progress.preview = URL.createObjectURL(file)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { toast } from 'vue-sonner'
|
||||
import settingsDefault from '@/settings.default'
|
||||
import useMenuStore from '@/store/modules/menu'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import eventBus from '@/utils/eventBus'
|
||||
import { diffTwoObj } from '@/utils/object'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
defineOptions({
|
||||
name: 'AppSetting',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import { useSlots } from '@/slots'
|
||||
import useMenuStore from '@/store/modules/menu'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import Logo from '../Logo/index.vue'
|
||||
|
||||
defineOptions({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { SubMenuProps } from './types'
|
||||
import { cn } from '@/utils'
|
||||
import { useTimeoutFn } from '@vueuse/core'
|
||||
import { cn } from '@/utils'
|
||||
import Item from './item.vue'
|
||||
import { rootMenuInjectionKey } from './types'
|
||||
|
||||
@@ -112,6 +112,9 @@ function handleClick() {
|
||||
if (rootMenu.isMenuPopup && hasChildren.value) {
|
||||
return
|
||||
}
|
||||
if (props.menu.meta?.link) {
|
||||
return
|
||||
}
|
||||
if (hasChildren.value) {
|
||||
rootMenu.handleSubMenuClick(index, props.uniqueKey)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { Tabbar } from '#/global'
|
||||
import { useSlots } from '@/slots'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import useTabbarStore from '@/store/modules/tabbar'
|
||||
import { useMagicKeys } from '@vueuse/core'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { useSlots } from '@/slots'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import useTabbarStore from '@/store/modules/tabbar'
|
||||
|
||||
defineOptions({
|
||||
name: 'Tabbar',
|
||||
@@ -30,10 +30,20 @@ const tabRef = useTemplateRef<HTMLElement[]>('tabRef')
|
||||
|
||||
watch(() => route, (val) => {
|
||||
if (settingsStore.settings.tabbar.enable) {
|
||||
tabbarStore.add(val).then(() => {
|
||||
tabbarStore.add(val)
|
||||
nextTick(() => {
|
||||
const index = tabbarStore.list.findIndex(item => item.tabId === activedTabId.value)
|
||||
if (index !== -1) {
|
||||
tabRef.value && tabsRef.value?.scrollTo(tabRef.value[index].offsetLeft - tabsRef.value.ref?.$el.clientWidth * 0.4)
|
||||
const tabEl = tabRef.value?.find(item => Number.parseInt(item.dataset.index!) === index)
|
||||
const containerEl = tabsRef.value?.ref?.$el
|
||||
if (tabEl && containerEl) {
|
||||
const tabLeft = tabEl.offsetLeft
|
||||
const tabWidth = tabEl.offsetWidth
|
||||
const containerWidth = containerEl.clientWidth
|
||||
// 计算滚动位置,使标签页居中
|
||||
const scrollLeft = tabLeft - (containerWidth - tabWidth) / 2
|
||||
tabsRef.value?.scrollTo(scrollLeft)
|
||||
}
|
||||
tabbarScrollTip()
|
||||
}
|
||||
})
|
||||
@@ -147,7 +157,7 @@ onUnmounted(() => {
|
||||
<div class="tabbar">
|
||||
<component :is="useSlots('tabbar-start')" />
|
||||
<div class="tabbar-container">
|
||||
<FaScrollArea ref="tabsRef" horizontal :scrollbar="false" mask gradient-color="var(--g-tabbar-bg)" class="tabs">
|
||||
<FaScrollArea ref="tabsRef" :scrollbar="false" mask horizontal gradient-color="var(--g-tabbar-bg)" class="tabs">
|
||||
<TransitionGroup ref="tabContainerRef" name="tabbar" tag="div" class="tab-container">
|
||||
<div
|
||||
v-for="(element, index) in tabbarStore.list" :key="element.tabId"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { compile } from 'path-to-regexp'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import Breadcrumb from '../../../Breadcrumb/index.vue'
|
||||
import BreadcrumbItem from '../../../Breadcrumb/item.vue'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
defineOptions({
|
||||
name: 'Fullscreen',
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import type { Menu } from '@/types/global'
|
||||
import { useFocus } from '@vueuse/core'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import Breadcrumb from '@/layouts/components/Breadcrumb/index.vue'
|
||||
import BreadcrumbItem from '@/layouts/components/Breadcrumb/item.vue'
|
||||
import useMenuStore from '@/store/modules/menu'
|
||||
import useRouteStore from '@/store/modules/route'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { resolveRoutePath } from '@/utils'
|
||||
import { useFocus } from '@vueuse/core'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
|
||||
defineOptions({
|
||||
name: 'Search',
|
||||
name: 'NavSearchModal',
|
||||
})
|
||||
|
||||
const isShow = defineModel<boolean>({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
defineOptions({
|
||||
name: 'PageReload',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import Tabbar from './Tabbar/index.vue'
|
||||
import Toolbar from './Toolbar/index.vue'
|
||||
@@ -20,11 +21,10 @@ const enableToolbar = computed(() => {
|
||||
|
||||
const scrollTop = ref(0)
|
||||
const scrollOnHide = ref(false)
|
||||
const topbarHeight = computed(() => {
|
||||
const tabbarHeight = settingsStore.settings.tabbar.enable ? Number.parseInt(getComputedStyle(document.documentElement || document.body).getPropertyValue('--g-tabbar-height')) : 0
|
||||
const toolbarHeight = enableToolbar.value ? Number.parseInt(getComputedStyle(document.documentElement || document.body).getPropertyValue('--g-toolbar-height')) : 0
|
||||
return tabbarHeight + toolbarHeight
|
||||
})
|
||||
|
||||
const topbarRef = useTemplateRef('topbarRef')
|
||||
const { height: topbarHeight } = useElementSize(topbarRef)
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', onScroll)
|
||||
})
|
||||
@@ -42,7 +42,7 @@ watch(scrollTop, (val, oldVal) => {
|
||||
<template>
|
||||
<FaSmartFixedBlock position="top" :class="{ 'absolute!': settingsStore.settings.topbar.mode === 'static' }">
|
||||
<div
|
||||
class="topbar-container transition-[transform,box-shadow]-300" :class="{
|
||||
ref="topbarRef" class="topbar-container transition-[transform,box-shadow]-300" :class="{
|
||||
[`topbar-${settingsStore.settings.topbar.mode}`]: true,
|
||||
mask: scrollTop,
|
||||
hide: scrollOnHide,
|
||||
|
||||
+30
-44
@@ -22,51 +22,36 @@ const settingsStore = useSettingsStore()
|
||||
const keepAliveStore = useKeepAliveStore()
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
// 头部当前实际高度
|
||||
const headerActualHeight = computed(() => {
|
||||
let actualHeight = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-header-height'))
|
||||
if (['single', 'side'].includes(settingsStore.settings.menu.mode) || settingsStore.mode === 'mobile') {
|
||||
actualHeight = 0
|
||||
}
|
||||
return actualHeight
|
||||
// 头部是否隐藏
|
||||
const isHeaderHide = computed(() => {
|
||||
return ['single', 'side'].includes(settingsStore.settings.menu.mode) || settingsStore.mode === 'mobile'
|
||||
})
|
||||
|
||||
// 侧边栏主导航当前实际宽度
|
||||
const mainSidebarActualWidth = computed(() => {
|
||||
let actualWidth = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-main-sidebar-width'))
|
||||
if (settingsStore.settings.menu.mode === 'single' || (settingsStore.settings.menu.mode === 'head' && settingsStore.mode !== 'mobile')) {
|
||||
actualWidth = 0
|
||||
}
|
||||
return actualWidth
|
||||
// 侧边栏主导航是否隐藏
|
||||
const isMainSidebarHide = computed(() => {
|
||||
return settingsStore.settings.menu.mode === 'single'
|
||||
|| (settingsStore.settings.menu.mode === 'head' && settingsStore.mode !== 'mobile')
|
||||
})
|
||||
|
||||
// 侧边栏次导航当前实际宽度
|
||||
const subSidebarActualWidth = computed(() => {
|
||||
let actualWidth = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-width'))
|
||||
if (settingsStore.settings.menu.subMenuCollapse && settingsStore.mode !== 'mobile') {
|
||||
actualWidth = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-collapse-width'))
|
||||
}
|
||||
if (menuStore.sidebarMenus.every(item => item.meta?.menu === false)) {
|
||||
actualWidth = 0
|
||||
}
|
||||
return actualWidth
|
||||
// 侧边栏次导航是否隐藏
|
||||
const isSubSidebarHide = computed(() => {
|
||||
return menuStore.sidebarMenus.every(item => item.meta?.menu === false)
|
||||
})
|
||||
|
||||
// 顶栏当前实际高度
|
||||
const topbarActualHeight = computed(() => {
|
||||
let actualHeight = 0
|
||||
if (settingsStore.settings.tabbar.enable) {
|
||||
actualHeight += Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-tabbar-height'))
|
||||
}
|
||||
if (settingsStore.settings.toolbar.enable && Object.keys(settingsStore.settings.toolbar).some((key) => {
|
||||
if (settingsStore.settings.app.routeBaseOn === 'filesystem' && key === 'breadcrumb') {
|
||||
return false
|
||||
}
|
||||
return settingsStore.settings.toolbar[key as keyof typeof settingsStore.settings.toolbar]
|
||||
})) {
|
||||
actualHeight += Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-toolbar-height'))
|
||||
}
|
||||
return actualHeight
|
||||
// 标签栏是否隐藏
|
||||
const isTabbarHide = computed(() => {
|
||||
return !settingsStore.settings.tabbar.enable
|
||||
})
|
||||
|
||||
// 工具栏是否隐藏
|
||||
const isToolbarHide = computed(() => {
|
||||
return !settingsStore.settings.toolbar.enable
|
||||
|| !Object.keys(settingsStore.settings.toolbar).some((key) => {
|
||||
if (settingsStore.settings.app.routeBaseOn === 'filesystem' && key === 'breadcrumb') {
|
||||
return false
|
||||
}
|
||||
return settingsStore.settings.toolbar[key as keyof typeof settingsStore.settings.toolbar]
|
||||
})
|
||||
})
|
||||
|
||||
const isLink = computed(() => !!routeInfo.meta.link)
|
||||
@@ -96,10 +81,11 @@ const enableAppSetting = import.meta.env.VITE_APP_SETTING
|
||||
<template>
|
||||
<div
|
||||
class="layout" :style="{
|
||||
'--g-header-actual-height': `${headerActualHeight}px`,
|
||||
'--g-main-sidebar-actual-width': `${mainSidebarActualWidth}px`,
|
||||
'--g-sub-sidebar-actual-width': `${subSidebarActualWidth}px`,
|
||||
'--g-topbar-actual-height': `${topbarActualHeight}px`,
|
||||
'--g-header-actual-height': isHeaderHide ? '0px' : 'var(--g-header-height)',
|
||||
'--g-main-sidebar-actual-width': isMainSidebarHide ? '0px' : 'var(--g-main-sidebar-width)',
|
||||
'--g-sub-sidebar-actual-width': isSubSidebarHide ? '0px' : (settingsStore.settings.menu.subMenuCollapse && settingsStore.mode !== 'mobile' ? 'var(--g-sub-sidebar-collapse-width)' : 'var(--g-sub-sidebar-width)'),
|
||||
'--g-tabbar-actual-height': isTabbarHide ? '0px' : 'var(--g-tabbar-height)',
|
||||
'--g-toolbar-actual-height': isToolbarHide ? '0px' : 'var(--g-toolbar-height)',
|
||||
}"
|
||||
>
|
||||
<div id="app-main">
|
||||
@@ -214,7 +200,7 @@ const enableAppSetting = import.meta.env.VITE_APP_SETTING
|
||||
position: relative;
|
||||
flex: auto;
|
||||
height: 100%;
|
||||
margin: var(--g-topbar-actual-height) 0 0;
|
||||
margin: calc(var(--g-tabbar-actual-height) + var(--g-toolbar-actual-height)) 0 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// 加载 iconify 图标
|
||||
import { downloadAndInstall } from '@/iconify'
|
||||
|
||||
import icons from '@/iconify/index.json'
|
||||
// 自定义指令
|
||||
import directive from '@/utils/directive'
|
||||
|
||||
import App from './App.vue'
|
||||
|
||||
import router from './router'
|
||||
import pinia from './store'
|
||||
import uiProvider from './ui/provider'
|
||||
|
||||
@@ -18,7 +18,6 @@ export default defineFakeRoute([
|
||||
{
|
||||
path: '/multilevel_menu_example',
|
||||
component: 'Layout',
|
||||
redirect: '/multilevel_menu_example/page',
|
||||
name: 'multilevelMenuExample',
|
||||
meta: {
|
||||
title: '多级导航',
|
||||
@@ -36,7 +35,6 @@ export default defineFakeRoute([
|
||||
{
|
||||
path: 'level2',
|
||||
name: 'multilevelMenuExample2',
|
||||
redirect: '/multilevel_menu_example/level2/page',
|
||||
meta: {
|
||||
title: '导航2',
|
||||
},
|
||||
@@ -52,7 +50,6 @@ export default defineFakeRoute([
|
||||
{
|
||||
path: 'level3',
|
||||
name: 'multilevelMenuExample2-2',
|
||||
redirect: '/multilevel_menu_example/level2/level3/page1',
|
||||
meta: {
|
||||
title: '导航2-2',
|
||||
},
|
||||
|
||||
+22
-1
@@ -1,10 +1,10 @@
|
||||
import type { Router, RouteRecordRaw } from 'vue-router'
|
||||
import { useNProgress } from '@vueuse/integrations/useNProgress'
|
||||
import useKeepAliveStore from '@/store/modules/keepAlive'
|
||||
import useMenuStore from '@/store/modules/menu'
|
||||
import useRouteStore from '@/store/modules/route'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { useNProgress } from '@vueuse/integrations/useNProgress'
|
||||
import { asyncRoutes, asyncRoutesByFilesystem } from './routes'
|
||||
import '@/assets/styles/nprogress.css'
|
||||
|
||||
@@ -112,6 +112,26 @@ function setupRoutes(router: Router) {
|
||||
})
|
||||
}
|
||||
|
||||
// 当父级路由未配置重定向时,自动重定向到有访问权限的子路由
|
||||
function setupRedirectAuthChildrenRoute(router: Router) {
|
||||
router.beforeEach((to, _from, next) => {
|
||||
const { auth } = useAuth()
|
||||
const currentRoute = router.getRoutes().find(route => route.path === (to.matched.at(-1)?.path ?? ''))
|
||||
if (!currentRoute?.redirect) {
|
||||
const findAuthRoute = currentRoute?.children?.find(route => route.meta?.menu !== false && auth(route.meta?.auth ?? ''))
|
||||
if (findAuthRoute) {
|
||||
next(findAuthRoute)
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 进度条
|
||||
function setupProgress(router: Router) {
|
||||
const { isLoading } = useNProgress()
|
||||
@@ -208,6 +228,7 @@ function setupOther(router: Router) {
|
||||
|
||||
export default function setupGuards(router: Router) {
|
||||
setupRoutes(router)
|
||||
setupRedirectAuthChildrenRoute(router)
|
||||
setupProgress(router)
|
||||
setupTitle(router)
|
||||
setupKeepAlive(router)
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import pinia from '@/store'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { loadingFadeOut } from 'virtual:app-loading'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import pinia from '@/store'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import setupExtensions from './extensions'
|
||||
import setupGuards from './guards'
|
||||
// 路由相关数据
|
||||
|
||||
@@ -7,7 +7,6 @@ function Layout() {
|
||||
const routes: RouteRecordRaw = {
|
||||
path: '/multilevel_menu_example',
|
||||
component: Layout,
|
||||
redirect: '/multilevel_menu_example/page',
|
||||
name: 'multilevelMenuExample',
|
||||
meta: {
|
||||
title: '多级导航',
|
||||
@@ -25,7 +24,6 @@ const routes: RouteRecordRaw = {
|
||||
{
|
||||
path: 'level2',
|
||||
name: 'multilevelMenuExample2',
|
||||
redirect: '/multilevel_menu_example/level2/page',
|
||||
meta: {
|
||||
title: '导航2',
|
||||
},
|
||||
@@ -41,7 +39,6 @@ const routes: RouteRecordRaw = {
|
||||
{
|
||||
path: 'level3',
|
||||
name: 'multilevelMenuExample2-2',
|
||||
redirect: '/multilevel_menu_example/level2/level3/page1',
|
||||
meta: {
|
||||
title: '导航2-2',
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Route } from '#/global'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import generatedRoutes from 'virtual:generated-pages'
|
||||
import { setupLayouts } from 'virtual:meta-layouts'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import MultilevelMenuExample from './modules/multilevel.menu.example'
|
||||
|
||||
// 固定路由(默认路由)
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import type { RecursiveRequired, Settings } from '#/global'
|
||||
import { cloneDeep } from 'es-toolkit'
|
||||
import settingsDefault from '@/settings.default'
|
||||
import { merge } from '@/utils/object'
|
||||
import { cloneDeep } from 'es-toolkit'
|
||||
|
||||
const globalSettings: Settings.all = {
|
||||
// 请在此处编写或粘贴配置代码
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Menu, Route } from '#/global'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { cloneDeep } from 'es-toolkit'
|
||||
import apiApp from '@/api/modules/app'
|
||||
import menu from '@/menu'
|
||||
import { resolveRoutePath } from '@/utils'
|
||||
import { cloneDeep } from 'es-toolkit'
|
||||
import useRouteStore from './route'
|
||||
import useSettingsStore from './settings'
|
||||
import useUserStore from './user'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Route } from '#/global'
|
||||
import type { RouteRecordRaw, RouterMatcher } from 'vue-router'
|
||||
import apiApp from '@/api/modules/app'
|
||||
import { systemRoutes as systemRoutesRaw } from '@/router/routes'
|
||||
import { cloneDeep } from 'es-toolkit'
|
||||
import { createRouterMatcher } from 'vue-router'
|
||||
import apiApp from '@/api/modules/app'
|
||||
import { systemRoutes as systemRoutesRaw } from '@/router/routes'
|
||||
import useSettingsStore from './settings'
|
||||
|
||||
const useRouteStore = defineStore(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Settings } from '#/global'
|
||||
import type { RouteMeta } from 'vue-router'
|
||||
import { cloneDeep } from 'es-toolkit'
|
||||
import settingsDefault from '@/settings'
|
||||
import { merge } from '@/utils/object'
|
||||
import { cloneDeep } from 'es-toolkit'
|
||||
|
||||
const useSettingsStore = defineStore(
|
||||
// 唯一ID
|
||||
|
||||
@@ -12,7 +12,7 @@ const useTabbarStore = defineStore(
|
||||
const leaveIndex = ref(-1)
|
||||
|
||||
// 添加标签页
|
||||
async function add(route: RouteLocationNormalized) {
|
||||
function add(route: RouteLocationNormalized) {
|
||||
const names: string[] = []
|
||||
route.matched.forEach((v, i) => {
|
||||
if (i > 0) {
|
||||
|
||||
Vendored
+1
-1
@@ -89,6 +89,6 @@ declare global {
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -21,6 +21,7 @@ declare module 'vue' {
|
||||
FaDrawer: typeof import('./../ui/components/FaDrawer/index.vue')['default']
|
||||
FaDropdown: typeof import('./../ui/components/FaDropdown/index.vue')['default']
|
||||
FaFixedActionBar: typeof import('./../ui/components/FaFixedActionBar/index.vue')['default']
|
||||
FaHoverCard: typeof import('./../ui/components/FaHoverCard/index.vue')['default']
|
||||
FaIcon: typeof import('./../ui/components/FaIcon/index.vue')['default']
|
||||
FaInput: typeof import('./../ui/components/FaInput/index.vue')['default']
|
||||
FaKbd: typeof import('./../ui/components/FaKbd/index.vue')['default']
|
||||
@@ -32,6 +33,7 @@ declare module 'vue' {
|
||||
FaPasswordStrength: typeof import('./../ui/components/FaPasswordStrength/index.vue')['default']
|
||||
FaPinInput: typeof import('./../ui/components/FaPinInput/index.vue')['default']
|
||||
FaPopover: typeof import('./../ui/components/FaPopover/index.vue')['default']
|
||||
FaProgress: typeof import('./../ui/components/FaProgress/index.vue')['default']
|
||||
FaScrollArea: typeof import('./../ui/components/FaScrollArea/index.vue')['default']
|
||||
FaSearchBar: typeof import('./../ui/components/FaSearchBar/index.vue')['default']
|
||||
FaSelect: typeof import('./../ui/components/FaSelect/index.vue')['default']
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import type { AvatarVariants } from '.'
|
||||
import { cn } from '@/utils'
|
||||
import { AvatarRoot } from 'reka-ui'
|
||||
import { cn } from '@/utils'
|
||||
import { avatarVariant } from '.'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import type { PrimitiveProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import type { ButtonVariants } from '.'
|
||||
import { cn } from '@/utils'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { cn } from '@/utils'
|
||||
import { buttonVariants } from '.'
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { CheckboxRootEmits, CheckboxRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Check } from 'lucide-vue-next'
|
||||
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<CheckboxRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<CheckboxRootEmits>()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuCheckboxItemEmits, ContextMenuCheckboxItemProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Check } from 'lucide-vue-next'
|
||||
import {
|
||||
ContextMenuCheckboxItem,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ContextMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<ContextMenuCheckboxItemEmits>()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuContentEmits, ContextMenuContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
ContextMenuContent,
|
||||
ContextMenuPortal,
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ContextMenuContentProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<ContextMenuContentEmits>()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuItemEmits, ContextMenuItemProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
ContextMenuItem,
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ContextMenuItemProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
||||
const emits = defineEmits<ContextMenuItemEmits>()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuLabelProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ContextMenuLabel } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuRadioItemEmits, ContextMenuRadioItemProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Circle } from 'lucide-vue-next'
|
||||
import {
|
||||
ContextMenuItemIndicator,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ContextMenuRadioItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<ContextMenuRadioItemEmits>()
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuSeparatorProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
ContextMenuSeparator,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ContextMenuSeparatorProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuSubContentEmits, DropdownMenuSubContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
ContextMenuSubContent,
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<DropdownMenuSubContentEmits>()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuSubTriggerProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ChevronRight } from 'lucide-vue-next'
|
||||
import {
|
||||
ContextMenuSubTrigger,
|
||||
useForwardProps,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import type { DialogContentEmits, DialogContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import type { SheetVariants } from '.'
|
||||
import { cn } from '@/utils'
|
||||
import { useScrollLock } from '@vueuse/core'
|
||||
import { X } from 'lucide-vue-next'
|
||||
import {
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { sheetVariants } from '.'
|
||||
|
||||
interface SheetContentProps extends DialogContentProps {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { DialogDescriptionProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { DialogDescription } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { DialogTitleProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { DialogTitle } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuCheckboxItemEmits, DropdownMenuCheckboxItemProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Check } from 'lucide-vue-next'
|
||||
import {
|
||||
DropdownMenuCheckboxItem,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<DropdownMenuCheckboxItemEmits>()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuContentEmits, DropdownMenuContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuPortal,
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<DropdownMenuContentProps & { class?: HTMLAttributes['class'] }>(),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuItemProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { DropdownMenuItem, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuItemProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuLabelProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { DropdownMenuLabel, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuRadioItemEmits, DropdownMenuRadioItemProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Circle } from 'lucide-vue-next'
|
||||
import {
|
||||
DropdownMenuItemIndicator,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuRadioItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuSeparatorProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
DropdownMenuSeparator,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuSeparatorProps & {
|
||||
class?: HTMLAttributes['class']
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuSubContentEmits, DropdownMenuSubContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
DropdownMenuSubContent,
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<DropdownMenuSubContentEmits>()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuSubTriggerProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ChevronRight } from 'lucide-vue-next'
|
||||
import {
|
||||
DropdownMenuSubTrigger,
|
||||
useForwardProps,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DropdownMenuSubTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ const props = defineProps<{
|
||||
|
||||
const slots = defineSlots<{
|
||||
default?: () => VNode
|
||||
label?: () => VNode
|
||||
header?: () => VNode
|
||||
footer?: () => VNode
|
||||
}>()
|
||||
|
||||
const myItems = computed(() => {
|
||||
@@ -55,9 +56,9 @@ function handleItemClick(item: { handle?: () => void }) {
|
||||
<slot />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent :align :align-offset :side :side-offset :collision-padding>
|
||||
<template v-if="!!slots.label">
|
||||
<template v-if="!!slots.header">
|
||||
<DropdownMenuLabel>
|
||||
<slot name="label" />
|
||||
<slot name="header" />
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
</template>
|
||||
@@ -72,6 +73,12 @@ function handleItemClick(item: { handle?: () => void }) {
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator v-if="index !== myItems.length - 1" />
|
||||
</template>
|
||||
<template v-if="!!slots.footer">
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel>
|
||||
<slot name="footer" />
|
||||
</DropdownMenuLabel>
|
||||
</template>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { HoverCardRootEmits, HoverCardRootProps } from 'reka-ui'
|
||||
import { HoverCardRoot, useForwardPropsEmits } from 'reka-ui'
|
||||
|
||||
const props = defineProps<HoverCardRootProps>()
|
||||
const emits = defineEmits<HoverCardRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HoverCardRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</HoverCardRoot>
|
||||
</template>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import type { HoverCardContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import {
|
||||
HoverCardContent,
|
||||
HoverCardPortal,
|
||||
useForwardProps,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<HoverCardContentProps & { class?: HTMLAttributes['class'] }>(),
|
||||
{
|
||||
sideOffset: 4,
|
||||
},
|
||||
)
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'z-2000 min-w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
</template>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { HoverCardTriggerProps } from 'reka-ui'
|
||||
import { HoverCardTrigger } from 'reka-ui'
|
||||
|
||||
const props = defineProps<HoverCardTriggerProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HoverCardTrigger v-bind="props">
|
||||
<slot />
|
||||
</HoverCardTrigger>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as HoverCard } from './HoverCard.vue'
|
||||
export { default as HoverCardContent } from './HoverCardContent.vue'
|
||||
export { default as HoverCardTrigger } from './HoverCardTrigger.vue'
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { HoverCardContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from './hover-card'
|
||||
|
||||
defineOptions({
|
||||
name: 'FaHoverCard',
|
||||
})
|
||||
|
||||
const props = defineProps<{
|
||||
align?: HoverCardContentProps['align']
|
||||
alignOffset?: HoverCardContentProps['alignOffset']
|
||||
side?: HoverCardContentProps['side']
|
||||
sideOffset?: HoverCardContentProps['sideOffset']
|
||||
collisionPadding?: HoverCardContentProps['collisionPadding']
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HoverCard>
|
||||
<HoverCardTrigger as-child>
|
||||
<slot />
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent :align :align-offset :side :side-offset :collision-padding :class="props.class">
|
||||
<slot name="card" />
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</template>
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { UseImage } from '@vueuse/components'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
defineOptions({
|
||||
name: 'FaIcon',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
defaultValue?: string | number
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { DialogContentEmits, DialogContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { useScrollLock } from '@vueuse/core'
|
||||
import { Maximize, Minimize, X } from 'lucide-vue-next'
|
||||
import {
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DialogContentProps & {
|
||||
class?: HTMLAttributes['class']
|
||||
@@ -91,7 +91,7 @@ const id = inject('ModalId')
|
||||
>
|
||||
<slot />
|
||||
<div class="absolute inset-e-4 top-4 flex-center gap-2">
|
||||
<button v-if="props.maximizable" class="rounded-sm bg-transparent opacity-70 ring-offset-background transition-opacity hidden disabled:pointer-events-none sm:inline-block data-[state=open]:bg-accent data-[state=open]:text-muted-foreground hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-ring" @click="handleMaximize">
|
||||
<button v-if="props.maximizable" class="hidden rounded-sm bg-transparent opacity-70 ring-offset-background transition-opacity disabled:pointer-events-none sm:inline-block data-[state=open]:bg-accent data-[state=open]:text-muted-foreground hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-ring" @click="handleMaximize">
|
||||
<Maximize v-if="!props.maximize" class="h-4 w-4" />
|
||||
<Minimize v-else class="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { DialogDescriptionProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { DialogDescription, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { DialogContentEmits, DialogContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { X } from 'lucide-vue-next'
|
||||
import {
|
||||
DialogClose,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DialogContentProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<DialogContentEmits>()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { DialogTitleProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { DialogTitle, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModalEmits, ModalProps } from '.'
|
||||
import { cn } from '@/utils'
|
||||
import { VisuallyHidden } from 'reka-ui'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ToastRootEmits } from 'reka-ui'
|
||||
import type { ToastProps } from '.'
|
||||
import { cn } from '@/utils'
|
||||
import { ToastRoot, useForwardPropsEmits } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { toastVariants } from '.'
|
||||
|
||||
const props = defineProps<ToastProps>()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ToastActionProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ToastAction } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ToastActionProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { ToastCloseProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { X } from 'lucide-vue-next'
|
||||
import { ToastClose } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ToastCloseProps & {
|
||||
class?: HTMLAttributes['class']
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ToastDescriptionProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ToastDescription } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ToastDescriptionProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ToastTitleProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ToastTitle } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ToastTitleProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ToastViewportProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ToastViewport } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<ToastViewportProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ function handleCollaspe() {
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
<FaButton v-if="collaspe" variant="link" size="icon" class="absolute inset-b-0 inset-s-1/2 opacity-0 transition-all -translate-x-1/2 group-hover/pagemain-opacity-100" :class="{ 'rotate-x-180': !isCollaspe }" @click="handleCollaspe">
|
||||
<FaButton v-if="collaspe" variant="link" size="icon" class="group-hover/pagemain-opacity-100 absolute inset-b-0 inset-s-1/2 opacity-0 transition-all -translate-x-1/2" :class="{ 'rotate-x-180': !isCollaspe }" @click="handleCollaspe">
|
||||
<FaIcon name="i-ep:arrow-down" class="text-xl" />
|
||||
</FaButton>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ const id = useId()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PinInput :id :model-value="value" placeholder="○" otp type="number" @complete="handleComplete" @update:model-value="arrStr => value = arrStr.filter(Boolean)">
|
||||
<PinInput :id :model-value="value" placeholder="○" otp @complete="handleComplete" @update:model-value="arrStr => value = arrStr.filter(Boolean)">
|
||||
<PinInputGroup>
|
||||
<PinInputInput v-for="(item, index) in length" :key="item" :index="index" />
|
||||
</PinInputGroup>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { PinInputRootEmits, PinInputRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { PinInputRoot, useForwardPropsEmits } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = withDefaults(defineProps<PinInputRootProps & { class?: HTMLAttributes['class'] }>(), {
|
||||
modelValue: () => [],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { PrimitiveProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Primitive, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<PrimitiveProps & { class?: HTMLAttributes['class'] }>()
|
||||
const delegatedProps = computed(() => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { PinInputInputProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { PinInputInput, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<PinInputInputProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopoverContentEmits, PopoverContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
PopoverContent,
|
||||
PopoverPortal,
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { Progress } from './progress'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
|
||||
const modelValue = defineModel<number>({
|
||||
default: 0,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Progress v-bind="props" :model-value="modelValue" />
|
||||
</template>
|
||||
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import type { ProgressRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import {
|
||||
ProgressIndicator,
|
||||
ProgressRoot,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<ProgressRootProps & { class?: HTMLAttributes['class'] }>(),
|
||||
{
|
||||
modelValue: 0,
|
||||
},
|
||||
)
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProgressRoot
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative h-4 w-full overflow-hidden rounded-full bg-secondary',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<ProgressIndicator
|
||||
class="h-full w-full flex-1 bg-primary transition-all"
|
||||
:style="`transform: translateX(-${100 - (props.modelValue ?? 0)}%);`"
|
||||
/>
|
||||
</ProgressRoot>
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Progress } from './Progress.vue'
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { useElementSize, useScroll } from '@vueuse/core'
|
||||
import { cn } from '@/utils'
|
||||
import { ScrollArea, ScrollBar } from './scroll-area'
|
||||
|
||||
defineOptions({
|
||||
@@ -25,6 +25,10 @@ const props = withDefaults(
|
||||
},
|
||||
)
|
||||
|
||||
const emits = defineEmits<{
|
||||
onScroll: [Event]
|
||||
}>()
|
||||
|
||||
const scrollAreaRef = useTemplateRef('scrollAreaRef')
|
||||
|
||||
const arrivedState = ref<{
|
||||
@@ -46,6 +50,10 @@ const showMaskEnd = computed(() => {
|
||||
return !arrivedState.value?.bottom
|
||||
})
|
||||
|
||||
function onScroll(event: Event) {
|
||||
emits('onScroll', event)
|
||||
}
|
||||
|
||||
function onWheel(event: WheelEvent) {
|
||||
if (props.horizontal) {
|
||||
scrollAreaRef.value?.el?.viewportElement?.scrollBy({
|
||||
@@ -108,7 +116,7 @@ defineExpose({
|
||||
'--mask-scroll-container-gradient-color': props.gradientColor,
|
||||
} : {}"
|
||||
>
|
||||
<ScrollArea ref="scrollAreaRef" :class="cn('relative z-0 flex-1', props.contentClass)" :scrollbar="props.scrollbar" :on-wheel="onWheel">
|
||||
<ScrollArea ref="scrollAreaRef" :class="cn('relative z-0 flex-1', props.contentClass)" :scrollbar="props.scrollbar" :on-scroll="onScroll" :on-wheel="onWheel">
|
||||
<slot />
|
||||
<ScrollBar v-if="props.horizontal" orientation="horizontal" :class="{ 'opacity-0 pointer-events-none': !props.scrollbar }" />
|
||||
</ScrollArea>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import type { ScrollAreaRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
ScrollAreaCorner,
|
||||
ScrollAreaRoot,
|
||||
ScrollAreaViewport,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import ScrollBar from './ScrollBar.vue'
|
||||
|
||||
const props = defineProps<ScrollAreaRootProps & {
|
||||
class?: HTMLAttributes['class']
|
||||
scrollbar?: boolean
|
||||
onScroll?: (event: Event) => void
|
||||
onWheel?: (event: WheelEvent) => void
|
||||
}>()
|
||||
|
||||
@@ -31,7 +32,7 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<ScrollAreaRoot v-bind="delegatedProps" :class="cn('relative overflow-hidden', props.class)">
|
||||
<ScrollAreaViewport ref="viewportRef" class="scroll-area-viewport h-full w-full rounded-[inherit]" @wheel.prevent="onWheel">
|
||||
<ScrollAreaViewport ref="viewportRef" class="scroll-area-viewport h-full w-full overscroll-contain rounded-[inherit]" @scroll="onScroll" @wheel="onWheel">
|
||||
<slot />
|
||||
</ScrollAreaViewport>
|
||||
<ScrollBar :class="{ 'opacity-0 pointer-events-none': !props.scrollbar }" />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ScrollAreaScrollbarProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ScrollAreaScrollbar, ScrollAreaThumb } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = withDefaults(defineProps<ScrollAreaScrollbarProps & { class?: HTMLAttributes['class'] }>(), {
|
||||
orientation: 'vertical',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectContentEmits, SelectContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
SelectContent,
|
||||
SelectPortal,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { SelectScrollDownButton, SelectScrollUpButton } from '.'
|
||||
|
||||
defineOptions({
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectGroupProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { SelectGroup } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SelectGroupProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItemProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { Check } from 'lucide-vue-next'
|
||||
import {
|
||||
SelectItem,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
useForwardProps,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SelectItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectLabelProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { SelectLabel } from 'reka-ui'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SelectLabelProps & { class?: HTMLAttributes['class'] }>()
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectScrollDownButtonProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { SelectScrollDownButton, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SelectScrollDownButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectScrollUpButtonProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ChevronUp } from 'lucide-vue-next'
|
||||
import { SelectScrollUpButton, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SelectScrollUpButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectSeparatorProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { SelectSeparator } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SelectSeparatorProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectTriggerProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { SelectIcon, SelectTrigger, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SelectTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { SliderRootEmits, SliderRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { SliderRange, SliderRoot, SliderThumb, SliderTrack, useForwardPropsEmits } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SliderRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<SliderRootEmits>()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { SwitchRootEmits, SwitchRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import {
|
||||
SwitchRoot,
|
||||
SwitchThumb,
|
||||
useForwardPropsEmits,
|
||||
} from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<SwitchRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { TabsContentProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { TabsContent } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<TabsContentProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { TabsListProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { TabsList } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<TabsListProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { TabsTriggerProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
import { TabsTrigger, useForwardProps } from 'reka-ui'
|
||||
import { computed } from 'vue'
|
||||
import { cn } from '@/utils'
|
||||
|
||||
const props = defineProps<TabsTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user