mirror of
https://github.com/keven1024/015.git
synced 2026-05-26 15:13:30 +00:00
feat: enhance About component with feature display and loading states, and refactor feature handling in share drawers for improved user experience
This commit is contained in:
@@ -4,12 +4,14 @@ import { Skeleton } from '@/components/ui/skeleton'
|
||||
import getFileSize from '~/lib/getFileSize'
|
||||
import SparkMD5 from 'spark-md5'
|
||||
import useMyAppConfig from '@/composables/useMyAppConfig'
|
||||
import { useFeatureMeta } from '@/composables/useFeatureMeta'
|
||||
import Progress from '~/components/ui/progress/Progress.vue'
|
||||
import renderI18n from '~/lib/renderI18n'
|
||||
import { I18nT } from 'vue-i18n'
|
||||
|
||||
const { locale } = useI18n()
|
||||
const appConfig = useMyAppConfig()
|
||||
const featureMeta = useFeatureMeta()
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['about'],
|
||||
queryFn: async () => {
|
||||
@@ -107,6 +109,29 @@ const genUserAvatar = (email: string) => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="isLoading">
|
||||
<Skeleton class="w-full h-24 rounded-xl" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="rounded-xl bg-white/50 flex flex-col p-3 gap-3">
|
||||
<div class="font-semibold">{{ t('page.about.enabledFeatures') }}</div>
|
||||
<div v-if="featureMeta.length" class="flex flex-row flex-wrap gap-2">
|
||||
<div
|
||||
v-for="feature in featureMeta"
|
||||
:key="feature.key"
|
||||
class="flex flex-row items-center gap-2 rounded-full bg-black/5 px-2 py-1 text-sm font-medium"
|
||||
>
|
||||
<div class="flex size-6 items-center justify-center rounded-full text-black/80" :style="feature.style">
|
||||
<component :is="feature.icon" class="size-3.5" />
|
||||
</div>
|
||||
<span>{{ feature.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-sm opacity-75">
|
||||
{{ t('page.about.enabledFeaturesEmpty') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="isLoading">
|
||||
<Skeleton class="w-full h-16 rounded-xl" />
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { cx } from 'class-variance-authority'
|
||||
import showDrawer from '@/lib/showDrawer'
|
||||
import FileShareHandle from '@/components/Preprocessing/FileShareHandle.vue'
|
||||
import ImageConvertHandle from '@/components/Preprocessing/ImageConvertHandle.vue'
|
||||
@@ -84,7 +83,7 @@ const actions = computed(() =>
|
||||
}
|
||||
"
|
||||
>
|
||||
<div :class="cx('size-14 flex justify-center items-center rounded-full mx-3', item?.className)">
|
||||
<div class="size-14 flex justify-center items-center rounded-full mx-3" :style="item?.style">
|
||||
<component :is="item?.icon" />
|
||||
</div>
|
||||
<div class="text-xs truncate w-full text-center">{{ item?.label }}</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { cx } from 'class-variance-authority'
|
||||
import showDrawer from '@/lib/showDrawer'
|
||||
import TextShareHandle from '@/components/Preprocessing/TextShareHandle.vue'
|
||||
import { useFeatureMeta, type FeatureKey } from '@/composables/useFeatureMeta'
|
||||
@@ -38,7 +37,8 @@ const actionHandlers: Partial<Record<FeatureKey, ActionHandler>> = {
|
||||
const actions = computed(() =>
|
||||
featureMeta.value
|
||||
.filter((meta) => {
|
||||
const handler = actionHandlers?.[meta.key]
|
||||
const { key } = meta || {}
|
||||
const handler = actionHandlers?.[key]
|
||||
return handler && (!handler.condition || handler.condition())
|
||||
})
|
||||
.map((meta) => ({ ...meta, onClick: actionHandlers[meta.key]!.onClick }))
|
||||
@@ -58,7 +58,7 @@ const actions = computed(() =>
|
||||
}
|
||||
"
|
||||
>
|
||||
<div :class="cx('size-14 flex justify-center items-center rounded-full mx-3', item?.className)">
|
||||
<div class="size-14 flex justify-center items-center rounded-full mx-3" :style="item?.style">
|
||||
<component :is="item?.icon" />
|
||||
</div>
|
||||
<div class="text-xs truncate w-full text-center">{{ item?.label }}</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LucideShare, LucideImageMinus, LucideArrowRightLeft } from 'lucide-vue-next'
|
||||
import useMyAppConfig from '@/composables/useMyAppConfig'
|
||||
import type { FileHandleKey, TextHandleKey } from '../components/Preprocessing/types'
|
||||
import generateRandomColors from '@/lib/generateRandomColors'
|
||||
|
||||
export type FeatureKey = FileHandleKey | TextHandleKey
|
||||
|
||||
@@ -8,7 +9,6 @@ export type FeatureMeta = {
|
||||
key: FeatureKey
|
||||
label: string
|
||||
icon: any
|
||||
className: string
|
||||
}
|
||||
|
||||
const allFeatureMeta = (t: (key: string) => string): FeatureMeta[] => [
|
||||
@@ -16,25 +16,21 @@ const allFeatureMeta = (t: (key: string) => string): FeatureMeta[] => [
|
||||
key: 'file-share',
|
||||
label: t('page.upload.file.handleType.file-share'),
|
||||
icon: LucideShare,
|
||||
className: 'bg-green-300',
|
||||
},
|
||||
{
|
||||
key: 'file-image-compress',
|
||||
label: t('page.upload.file.handleType.file-image-compress'),
|
||||
icon: LucideImageMinus,
|
||||
className: 'bg-red-300',
|
||||
},
|
||||
{
|
||||
key: 'file-image-convert',
|
||||
label: t('page.upload.file.handleType.file-image-convert'),
|
||||
icon: LucideArrowRightLeft,
|
||||
className: 'bg-purple-300',
|
||||
},
|
||||
{
|
||||
key: 'text-share',
|
||||
label: t('page.upload.text.handleType.text-share'),
|
||||
icon: LucideShare,
|
||||
className: 'bg-green-300',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -44,6 +40,8 @@ export function useFeatureMeta() {
|
||||
|
||||
return computed(() => {
|
||||
const enabledKeys = appConfig.value?.features ?? []
|
||||
return allFeatureMeta(t).filter((meta) => enabledKeys.includes(meta.key))
|
||||
const result = allFeatureMeta(t).filter((meta) => enabledKeys.includes(meta.key))
|
||||
const colors = generateRandomColors(result.length)
|
||||
return result.map((meta, index) => ({ ...meta, style: { backgroundColor: colors[index] } }))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -201,6 +201,8 @@
|
||||
"title": "About",
|
||||
"about": "About",
|
||||
"systemInfo": "System Info",
|
||||
"enabledFeatures": "Instance feature",
|
||||
"enabledFeaturesEmpty": "No extra features are enabled for this instance",
|
||||
"systemVersion": "System Version",
|
||||
"storage": "Storage",
|
||||
"analysis": "Analysis",
|
||||
|
||||
@@ -201,6 +201,8 @@
|
||||
"title": "关于",
|
||||
"about": "关于",
|
||||
"systemInfo": "系统信息",
|
||||
"enabledFeatures": "实例功能",
|
||||
"enabledFeaturesEmpty": "当前实例暂未启用额外功能",
|
||||
"systemVersion": "系统版本",
|
||||
"storage": "已托管的文件",
|
||||
"analysis": "分析",
|
||||
|
||||
17
front/lib/generateRandomColors.ts
Normal file
17
front/lib/generateRandomColors.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
function generateRandomColors(count: number, minHueDiff = 30) {
|
||||
const colors: { h: number; s: number; l: number }[] = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
let hue: number,
|
||||
attempts = 0
|
||||
const { h: previousHue } = colors?.[colors.length - 1] ?? {}
|
||||
do {
|
||||
hue = Math.random() * 360
|
||||
attempts++
|
||||
} while (attempts < 100 && previousHue !== undefined && Math.abs(previousHue - hue) < minHueDiff)
|
||||
|
||||
colors.push({ h: hue, s: 70, l: 75 })
|
||||
}
|
||||
return colors.map((c) => `hsl(${c.h}, ${c.s}%, ${c.l}%)`)
|
||||
}
|
||||
|
||||
export default generateRandomColors
|
||||
Reference in New Issue
Block a user