fix(manifest): 新增首页的动态组件
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
data?: any
|
||||
srcLabel?: string
|
||||
avatarLabel?: string
|
||||
usernameLabel?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
srcLabel: 'src',
|
||||
avatarLabel: 'avatar_url',
|
||||
usernameLabel: 'username'
|
||||
})
|
||||
|
||||
const isVideo = (src: string) => {
|
||||
return typeof src === 'string' && src.includes('.mp4')
|
||||
}
|
||||
|
||||
const default_avatar = 'https://oss.gptpro.ink/temps/image/1720368128128.png'
|
||||
const default_nickname = ''
|
||||
|
||||
const linkUrl = computed(() => `/appshows/${props.data['name']}/${props.data['_id']}`)
|
||||
|
||||
const handleAccessUrl = () => {
|
||||
if (props.data?.customAccessUrl) {
|
||||
navigateTo(props.data?.customAccessUrl, { external: true })
|
||||
} else {
|
||||
navigateTo(`/draw/${props.data['name']}/${props.data['_id']}`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<NuxtLinkLocale :to="linkUrl">
|
||||
<div
|
||||
hoverable
|
||||
class="relative overflow-hidden cursor-pointer group !p-0"
|
||||
data-component-id="app-card"
|
||||
data-component-name="应用"
|
||||
>
|
||||
<!-- 视频/图片封面 -->
|
||||
<div class="overflow-hidden rounded-lg">
|
||||
<video
|
||||
v-if="isVideo(data[srcLabel])"
|
||||
autoplay
|
||||
loop
|
||||
muted
|
||||
playsinline
|
||||
class="w-full object-cover aspect-[3/4] transition-transform duration-300 group-hover:scale-105"
|
||||
:src="data[srcLabel]"
|
||||
type="video/mp4"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
class="w-full object-cover aspect-[3/4] transition-transform duration-300 group-hover:scale-105"
|
||||
:src="data[srcLabel]"
|
||||
:alt="data['title']"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- hover 信息层 -->
|
||||
<div
|
||||
class="absolute bottom-0 left-0 w-full h-2/5 rounded-lg invisible group-hover:visible bg-black/50 backdrop-blur-md overflow-hidden transition-all duration-300 flex flex-col justify-start items-start text-white px-3 py-2"
|
||||
data-component-id="app-card-info"
|
||||
data-component-name="应用信息"
|
||||
>
|
||||
<!-- 标题和描述 -->
|
||||
<span
|
||||
class="font-medium truncate w-full"
|
||||
data-component-id="app-card-info-title"
|
||||
>
|
||||
{{ data["title"] }}
|
||||
</span>
|
||||
<p
|
||||
class="text-xs opacity-80 truncate w-full"
|
||||
data-component-id="app-card-info-description"
|
||||
>
|
||||
{{ data["description"] }}
|
||||
</p>
|
||||
|
||||
<!-- 作者信息 -->
|
||||
<div class="flex items-center mt-auto w-full">
|
||||
<a-avatar
|
||||
:src="data['user']?.[avatarLabel] || default_avatar"
|
||||
size="small"
|
||||
class="mr-2"
|
||||
/>
|
||||
<span class="text-xs opacity-80">
|
||||
{{ data["user"]?.["nickname"] || default_nickname }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="w-full flex justify-center mt-2">
|
||||
<button
|
||||
class="w-4/5 rounded-full px-4 py-0.5 border-gray-300 bg-blue-500"
|
||||
@click.stop.prevent="handleAccessUrl"
|
||||
>
|
||||
一键使用
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLinkLocale>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,93 @@
|
||||
<script setup lang="ts">
|
||||
import { GlobalInjectKeyConst, type GlobalInjectWorkflowAppStatus } from '~/types'
|
||||
import AppCard from "~/components/AllApps/AppCard.vue";
|
||||
|
||||
const { workflows: apps } = inject<GlobalInjectWorkflowAppStatus>(GlobalInjectKeyConst.AllWorkFlowApps, {})
|
||||
|
||||
const fitter = ref<string>('全部')
|
||||
|
||||
const tags = computed(() => {
|
||||
const tags = [] as {
|
||||
text: string
|
||||
count: number
|
||||
}[]
|
||||
for (const workflow of apps.value) {
|
||||
if (!workflow.tags) continue
|
||||
for (const tag of workflow.tags) {
|
||||
const tagItem = tags.find(t => t.text === tag)
|
||||
if (tagItem) {
|
||||
tagItem.count++
|
||||
} else {
|
||||
tags.push({ text: tag, count: 1 })
|
||||
}
|
||||
}
|
||||
}
|
||||
// 按照出现次数降序排列
|
||||
return ["全部"].concat(
|
||||
tags
|
||||
.filter(v => v.text)
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.map(item => item.text)
|
||||
)
|
||||
})
|
||||
|
||||
/** 过滤后的应用列表 */
|
||||
const showApps = computed(() => {
|
||||
if (fitter.value === '全部') {
|
||||
return apps.value
|
||||
}
|
||||
return apps.value.filter(item => item.tags?.includes(fitter.value))
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="px-2 flex flex-col gap-2">
|
||||
<div class="text-lg font-bold py-2">
|
||||
<span>应用中心</span>
|
||||
</div>
|
||||
<div>
|
||||
<!-- 标签选择 -->
|
||||
<div class="mb-2 flex flex-nowrap overflow-x-auto gap-2 scrollbar-hide">
|
||||
<a-checkable-tag
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
:checked="fitter === tag"
|
||||
@change="() => fitter = tag"
|
||||
class="px-3 py-1.5 text-sm whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
{{ tag }}
|
||||
</a-checkable-tag>
|
||||
</div>
|
||||
|
||||
<!-- 应用展示 -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-6 3xl:grid-cols-8 gap-3">
|
||||
<div
|
||||
v-for="item in showApps"
|
||||
:key="item.id"
|
||||
class="w-full"
|
||||
>
|
||||
<AppCard
|
||||
:data="item"
|
||||
src-label="cover"
|
||||
avatar-label="avatar_url"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none; /* IE & Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari */
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user