|
|
|
<script setup>
|
|
|
|
<script lang="ts" setup>
|
|
|
|
import { getAppDetail } from "~/api/app";
|
|
|
|
import type { appDetail, Types } from "~/api/types/app";
|
|
|
|
import type { webSiteType } from "~/api/types/webSite";
|
|
|
|
const route = useRoute();
|
|
|
|
const config = useRuntimeConfig();
|
|
|
|
const showAd = ref(true);
|
|
|
|
const appDetail = ref({
|
|
|
|
const DetailData = ref<appDetail>({
|
|
|
|
id: 0,
|
|
|
|
title: "",
|
|
|
|
content: "",
|
|
|
|
image: "",
|
|
|
|
description: "",
|
|
|
|
link: "",
|
|
|
|
types: [],
|
|
|
|
});
|
|
|
|
const webSite = useState("webSite");
|
|
|
|
const detailAd = ref({
|
|
|
|
width: 300,
|
|
|
|
height: 177,
|
|
|
|
frontAdVos: [],
|
|
|
|
});
|
|
|
|
function mergeDuplicates(data) {
|
|
|
|
const webSite = useState<webSiteType>("webSite");
|
|
|
|
function mergeDuplicates(data: Types[]) {
|
|
|
|
const map = new Map();
|
|
|
|
|
|
|
|
data.forEach((item) => {
|
|
...
|
...
|
@@ -27,7 +30,7 @@ function mergeDuplicates(data) { |
|
|
|
const existing = map.get(item.id);
|
|
|
|
// 避免重复的子项(基于子项id)
|
|
|
|
const existingChildIds = new Set(
|
|
|
|
existing.children.map((child) => child.id)
|
|
|
|
existing.children.map((child: any) => child.id)
|
|
|
|
);
|
|
|
|
item.children.forEach((child) => {
|
|
|
|
if (!existingChildIds.has(child.id)) {
|
|
...
|
...
|
@@ -39,34 +42,26 @@ function mergeDuplicates(data) { |
|
|
|
|
|
|
|
return Array.from(map.values());
|
|
|
|
}
|
|
|
|
const { data: detailData } = await useFetch(
|
|
|
|
`http://aitoolht.crgx.net/dh/app/${route.params.id}`
|
|
|
|
);
|
|
|
|
const { data: adData } = await useFetch(
|
|
|
|
"http://aitoolht.crgx.net/dh/ad/listFrontAd",
|
|
|
|
{
|
|
|
|
method: "get",
|
|
|
|
params: { pageSize: 10, pageNum: 1, code: "top" },
|
|
|
|
}
|
|
|
|
);
|
|
|
|
detailAd.value = adData.value.rows[0];
|
|
|
|
appDetail.value = detailData.value.data;
|
|
|
|
appDetail.value.types = mergeDuplicates(detailData.value.data.types);
|
|
|
|
|
|
|
|
// 获取详情数据
|
|
|
|
const detailRes = await getAppDetail(Number(route.params.id));
|
|
|
|
DetailData.value = detailRes.data;
|
|
|
|
DetailData.value.types = mergeDuplicates(detailRes.data.types);
|
|
|
|
|
|
|
|
useHead({
|
|
|
|
title: appDetail.value.popupContent
|
|
|
|
? `${appDetail.value.title} - ${appDetail.value.popupContent}`
|
|
|
|
: appDetail.value.title,
|
|
|
|
title: DetailData.value.popupContent
|
|
|
|
? `${DetailData.value.title} - ${DetailData.value.popupContent}`
|
|
|
|
: DetailData.value.title,
|
|
|
|
meta: [
|
|
|
|
{ name: "description", content: appDetail.value.description },
|
|
|
|
{ name: "description", content: DetailData.value.description },
|
|
|
|
{
|
|
|
|
name: "og:title",
|
|
|
|
content: `${appDetail.value.title}-${appDetail.value.popupContent}`,
|
|
|
|
content: `${DetailData.value.title}-${DetailData.value.popupContent}`,
|
|
|
|
},
|
|
|
|
{ name: "og:description", content: appDetail.value.description },
|
|
|
|
{ name: "og:description", content: DetailData.value.description },
|
|
|
|
{
|
|
|
|
name: "og:image",
|
|
|
|
content: config.public.baseUrl + appDetail.value.image,
|
|
|
|
content: config.public.baseUrl + DetailData.value.image,
|
|
|
|
},
|
|
|
|
{ name: "og:url", content: route.fullPath },
|
|
|
|
{ name: "og:site_name", content: webSite.value.webname },
|
|
...
|
...
|
@@ -79,25 +74,25 @@ useHead({ |
|
|
|
<main class="flex-grow md:p-6 bg-white p-1">
|
|
|
|
<!-- Top Application Info Bar -->
|
|
|
|
<header
|
|
|
|
v-show="appDetail.types.length > 0"
|
|
|
|
v-show="DetailData.types.length > 0"
|
|
|
|
class="bg-white shadow-sm md:py-4 md:px-8 py-2 px-4 flex md:items-center md:justify-between flex-col md:flex-row"
|
|
|
|
>
|
|
|
|
<div class="flex items-center space-x-4">
|
|
|
|
<img
|
|
|
|
:src="config.public.baseUrl + appDetail.image"
|
|
|
|
:src="config.public.baseUrl + DetailData.image"
|
|
|
|
alt="App Icon"
|
|
|
|
class="w-16 h-16 object-contain"
|
|
|
|
/>
|
|
|
|
<div>
|
|
|
|
<h1 class="text-2xl font-bold text-[#5961f9]">
|
|
|
|
{{ appDetail.title }}
|
|
|
|
{{ DetailData.title }}
|
|
|
|
</h1>
|
|
|
|
<p class="text-sm text-gray-600 mt-1">
|
|
|
|
{{ appDetail.description }}
|
|
|
|
{{ DetailData.description }}
|
|
|
|
</p>
|
|
|
|
<div class="mt-2 flex items-center space-x-2">
|
|
|
|
<div
|
|
|
|
v-for="tag in appDetail.types"
|
|
|
|
v-for="tag in DetailData.types"
|
|
|
|
class="flex items-center space-x-2"
|
|
|
|
>
|
|
|
|
<template v-if="tag.children.length > 0">
|
|
...
|
...
|
@@ -123,7 +118,7 @@ useHead({ |
|
|
|
</div>
|
|
|
|
<div class="flex md:space-x-3 md:mt-0 mt-4">
|
|
|
|
<a
|
|
|
|
:href="appDetail.link"
|
|
|
|
:href="DetailData.link"
|
|
|
|
target="_blank"
|
|
|
|
class="!rounded-button whitespace-nowrap px-4 py-2 bg-[#5961f9] max-[768px]:text-xs text-white hover:bg-blue-600 transition-colors"
|
|
|
|
>
|
|
...
|
...
|
@@ -134,7 +129,7 @@ useHead({ |
|
|
|
|
|
|
|
<main class="relative w-full">
|
|
|
|
<!-- 悬浮广告弹窗 -->
|
|
|
|
<div
|
|
|
|
<!-- <div
|
|
|
|
class="md:absolute top-0 right-0 md:m-4 z-50 relative max-[768px]:m-auto"
|
|
|
|
v-show="showAd"
|
|
|
|
:style="{
|
|
...
|
...
|
@@ -158,9 +153,9 @@ useHead({ |
|
|
|
X
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div> -->
|
|
|
|
<div class="md:max-w-5xl mx-auto md:p-8 p-2 w-full">
|
|
|
|
<div v-html="appDetail.content"></div>
|
|
|
|
<div v-html="DetailData.content"></div>
|
|
|
|
</div>
|
|
|
|
</main>
|
|
|
|
</main>
|
...
|
...
|
|