mirror of
https://github.com/keven1024/015.git
synced 2026-05-26 07:08:02 +00:00
feat: implement feature-based action handling in file and text share drawers, enhancing user interaction with dynamic feature support
This commit is contained in:
@@ -6,10 +6,17 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
func GetConfig(c *echo.Context) error {
|
||||
featureConfig := u.GetEnvMap("features")
|
||||
features := lo.FilterMap(lo.Entries(featureConfig), func(e lo.Entry[string, any], _ int) (string, bool) {
|
||||
node, ok := e.Value.(map[string]any)
|
||||
return e.Key, ok && cast.ToBool(node["enabled"])
|
||||
})
|
||||
|
||||
return utils.HTTPSuccessHandler(c, map[string]any{
|
||||
"site_title": u.GetEnvMap("site.title"),
|
||||
"site_desc": u.GetEnvMap("site.desc"),
|
||||
@@ -18,5 +25,6 @@ func GetConfig(c *echo.Context) error {
|
||||
"site_bg_url": u.GetEnvWithDefault("site.bg_url", "https://img.fudaoyuan.icu/api/1/random/?scale_min=1.5&webp=true&md=false&format=302"),
|
||||
"version": u.GetEnvWithDefault("VERSION", "dev"),
|
||||
"build_time": cast.ToInt(u.GetEnvWithDefault("BUILD_TIME", cast.ToString(time.Now().Unix()))),
|
||||
"features": features,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
LucideShare,
|
||||
LucideImage,
|
||||
LucideBot,
|
||||
LucideLanguages,
|
||||
LucideFileText,
|
||||
LucideImageMinus,
|
||||
LucideArrowRightLeft,
|
||||
LucideImagePlus,
|
||||
LucideAudioLines,
|
||||
LucideListMusic,
|
||||
} from 'lucide-vue-next'
|
||||
import { cx } from 'class-variance-authority'
|
||||
import { isObject } from 'lodash-es'
|
||||
import showDrawer from '@/lib/showDrawer'
|
||||
import FileShareHandle from '@/components/Preprocessing/FileShareHandle.vue'
|
||||
import ImageConvertHandle from '@/components/Preprocessing/ImageConvertHandle.vue'
|
||||
import { useFeatureMeta, type FeatureKey } from '@/composables/useFeatureMeta'
|
||||
import type { FileShareHandleProps } from '../Preprocessing/types'
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
hide: () => void
|
||||
file: File[]
|
||||
@@ -25,43 +13,25 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const isImage = computed(() => props.file.every((r) => r?.type?.startsWith('image/')))
|
||||
const isVideo = computed(() => props.file.every((r) => r?.type?.startsWith('video/')))
|
||||
const isAudio = computed(() => props.file.every((r) => r?.type?.startsWith('audio/')))
|
||||
const isMedia = computed(() => isImage.value || isVideo.value || isAudio.value)
|
||||
|
||||
const isPDF = computed(() => props.file.every((r) => r?.type?.startsWith('application/pdf')))
|
||||
const isDOC = computed(() => props.file.every((r) => r?.type?.startsWith('application/msword')))
|
||||
const isXLS = computed(() => props.file.every((r) => r?.type?.startsWith('application/vnd.ms-excel')))
|
||||
const isPPT = computed(() => props.file.every((r) => r?.type?.startsWith('application/vnd.ms-powerpoint')))
|
||||
const isDocument = computed(() => isPDF.value || isDOC.value || isXLS.value || isPPT.value)
|
||||
const actions = [
|
||||
{
|
||||
label: t('page.upload.file.handleType.file-share'),
|
||||
icon: LucideShare,
|
||||
className: 'bg-green-300',
|
||||
onClick: () => {
|
||||
showDrawer({
|
||||
render: ({ hide }) => h(FileShareHandle, { ...props, hide }),
|
||||
})
|
||||
const featureMeta = useFeatureMeta()
|
||||
|
||||
type ActionHandler = {
|
||||
condition?: () => boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const actionHandlers: Partial<Record<FeatureKey, ActionHandler>> = {
|
||||
'file-share': {
|
||||
onClick: () => showDrawer({ render: ({ hide }) => h(FileShareHandle, { ...props, hide }) }),
|
||||
},
|
||||
'file-image-compress': {
|
||||
condition: () => isImage.value,
|
||||
onClick: () => props.onFileHandle({ type: 'file-image-compress', config: {} }),
|
||||
},
|
||||
isImage.value && {
|
||||
label: t('page.upload.file.handleType.file-image-compress'),
|
||||
icon: LucideImageMinus,
|
||||
className: 'bg-red-300',
|
||||
onClick: () => {
|
||||
props.onFileHandle({ type: 'file-image-compress', config: {} })
|
||||
},
|
||||
},
|
||||
isImage.value && {
|
||||
label: t('page.upload.file.handleType.file-image-convert'),
|
||||
icon: LucideArrowRightLeft,
|
||||
className: 'bg-purple-300',
|
||||
onClick: () => {
|
||||
showDrawer({
|
||||
render: ({ hide }) => h(ImageConvertHandle, { ...props, hide }),
|
||||
})
|
||||
},
|
||||
'file-image-convert': {
|
||||
condition: () => isImage.value,
|
||||
onClick: () => showDrawer({ render: ({ hide }) => h(ImageConvertHandle, { ...props, hide }) }),
|
||||
},
|
||||
// isImage.value && {
|
||||
// label: '图片翻译', icon: LucideLanguages, className: 'bg-orange-300', onClick: () => {
|
||||
@@ -88,19 +58,24 @@ const actions = [
|
||||
// console.log('复制链接')
|
||||
// }
|
||||
// },
|
||||
]?.filter(isObject) as {
|
||||
label: string
|
||||
icon: any
|
||||
className: string
|
||||
onClick: () => void
|
||||
}[]
|
||||
}
|
||||
|
||||
const actions = computed(() =>
|
||||
featureMeta.value
|
||||
.filter((meta) => {
|
||||
const { key } = meta || {}
|
||||
const handler = actionHandlers?.[key]
|
||||
return handler && (!handler.condition || handler.condition())
|
||||
})
|
||||
.map((meta) => ({ ...meta, onClick: actionHandlers[meta.key]!.onClick }))
|
||||
)
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex flex-col gap-5 p-5">
|
||||
<div class="flex flex-col gap-5 p-5 overflow-x-auto">
|
||||
<div class="flex flex-row gap-2">
|
||||
<div
|
||||
v-for="item in actions"
|
||||
:key="item.label"
|
||||
:key="item.key"
|
||||
class="flex flex-col items-center gap-2 max-w-20"
|
||||
@click="
|
||||
() => {
|
||||
|
||||
@@ -1,49 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import { LucideShare, LucideImage, LucideBot, LucideLanguages } from 'lucide-vue-next'
|
||||
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'
|
||||
|
||||
const props = defineProps<{
|
||||
hide: () => void
|
||||
text: string
|
||||
onTextHandle: ({ type, config }: { type: string; config: any }) => void
|
||||
}>()
|
||||
const { t } = useI18n()
|
||||
const actions = [
|
||||
{
|
||||
label: t('page.upload.text.handleType.text-share'),
|
||||
icon: LucideShare,
|
||||
className: 'bg-green-300',
|
||||
onClick: () => {
|
||||
showDrawer({
|
||||
render: ({ hide }) => h(TextShareHandle, { ...props, hide }),
|
||||
|
||||
const featureMeta = useFeatureMeta()
|
||||
|
||||
type ActionHandler = {
|
||||
condition?: () => boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const actionHandlers: Partial<Record<FeatureKey, ActionHandler>> = {
|
||||
'text-share': {
|
||||
onClick: () => showDrawer({ render: ({ hide }) => h(TextShareHandle, { ...props, hide }) }),
|
||||
},
|
||||
// 'text-image-generate': {
|
||||
// label: '生成配图', icon: LucideImage, className: 'bg-red-300',
|
||||
// onClick: () => { console.log('复制链接') }
|
||||
// },
|
||||
// 'text-ai-ask': {
|
||||
// label: '问大模型', icon: LucideBot, className: 'bg-blue-300',
|
||||
// onClick: () => { console.log('复制链接') }
|
||||
// },
|
||||
// 'text-translate': {
|
||||
// label: '文本翻译', icon: LucideLanguages, className: 'bg-orange-300',
|
||||
// onClick: () => { console.log('复制链接') }
|
||||
// },
|
||||
}
|
||||
|
||||
const actions = computed(() =>
|
||||
featureMeta.value
|
||||
.filter((meta) => {
|
||||
const handler = actionHandlers?.[meta.key]
|
||||
return handler && (!handler.condition || handler.condition())
|
||||
})
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '生成配图', icon: LucideImage, className: 'bg-red-300', onClick: () => {
|
||||
// console.log('复制链接')
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// label: '问大模型', icon: LucideBot, className: 'bg-blue-300', onClick: () => {
|
||||
// console.log('复制链接')
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// label: '文本翻译', icon: LucideLanguages, className: 'bg-orange-300', onClick: () => {
|
||||
// console.log('复制链接')
|
||||
// }
|
||||
// },
|
||||
]
|
||||
.map((meta) => ({ ...meta, onClick: actionHandlers[meta.key]!.onClick }))
|
||||
)
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex flex-col gap-5 p-5">
|
||||
<div class="flex flex-col gap-5 p-5 overflow-x-auto">
|
||||
<div class="flex flex-row gap-2">
|
||||
<div
|
||||
v-for="item in actions"
|
||||
:key="item.label"
|
||||
:key="item.key"
|
||||
class="flex flex-col items-center gap-2 max-w-20"
|
||||
@click="
|
||||
() => {
|
||||
|
||||
@@ -28,7 +28,7 @@ const Children = () =>
|
||||
"
|
||||
>
|
||||
<DrawerContent>
|
||||
<div class="mx-auto w-full max-w-lg pb-10 px-3">
|
||||
<div class="mx-auto min-w-lg max-w-[80vw] pb-10 px-3">
|
||||
<Children />
|
||||
</div>
|
||||
</DrawerContent>
|
||||
|
||||
49
front/composables/useFeatureMeta.ts
Normal file
49
front/composables/useFeatureMeta.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { LucideShare, LucideImageMinus, LucideArrowRightLeft } from 'lucide-vue-next'
|
||||
import useMyAppConfig from '@/composables/useMyAppConfig'
|
||||
import type { FileHandleKey, TextHandleKey } from '../components/Preprocessing/types'
|
||||
|
||||
export type FeatureKey = FileHandleKey | TextHandleKey
|
||||
|
||||
export type FeatureMeta = {
|
||||
key: FeatureKey
|
||||
label: string
|
||||
icon: any
|
||||
className: string
|
||||
}
|
||||
|
||||
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',
|
||||
},
|
||||
]
|
||||
|
||||
export function useFeatureMeta() {
|
||||
const { t } = useI18n()
|
||||
const appConfig = useMyAppConfig()
|
||||
|
||||
return computed(() => {
|
||||
const enabledKeys = appConfig.value?.features ?? []
|
||||
return allFeatureMeta(t).filter((meta) => enabledKeys.includes(meta.key))
|
||||
})
|
||||
}
|
||||
@@ -8,6 +8,7 @@ const useMyAppConfig = () => {
|
||||
site_bg_url: string
|
||||
version: string
|
||||
build_time: number
|
||||
features: string[]
|
||||
}
|
||||
}>('/api/config')
|
||||
return computed(() => data?.value?.data)
|
||||
|
||||
Reference in New Issue
Block a user