作者 xiaoqiu

完成繁体页面

不能预览此文件类型
... ... @@ -28,7 +28,7 @@
</header>
</template>
<script setup>
<script setup lang="ts">
const webSite = useState("webSite");
</script>
... ...
... ... @@ -77,7 +77,6 @@ const route = useRoute();
const router = useRouter();
const config = useRuntimeConfig();
const { setActiveSubCategory } = useActiveSubCategory();
const toggleExpand = (index: number) => {
if (activeCategory.value === index) {
activeCategory.value = null;
... ... @@ -97,11 +96,10 @@ const scrollToElement = (elementId: string) => {
}
return false;
};
const handleCategoryClick = (
event: Event,
id: number,
SubCategory: classifyType
SubCategory: classifyType,
) => {
event.preventDefault();
if (SubCategory.children && SubCategory.children.length > 0) {
... ... @@ -111,9 +109,11 @@ const handleCategoryClick = (
setActiveSubCategory(null);
}
const targetId = `term-${id}`;
if (route.path === "/") {
// 如果在首页,滚动到目标元素或者以hktw首页
if (route.path === "/" || route.path === "/hktw") {
scrollToElement(targetId);
} else if (route.path.startsWith("/hktw/")) {
router.push(`/hktw#${targetId}`);
} else {
router.push(`/#${targetId}`);
}
... ... @@ -122,7 +122,7 @@ const handleCategoryClick = (
const handleSubCategoryClick = (
event: Event,
parentId: number,
subId: number
subId: number,
) => {
event.preventDefault();
const targetId = `term-${parentId}-${subId}`;
... ...
... ... @@ -17,8 +17,8 @@
@confirm="onConfirm(item.id)"
>
<template #reference>
<a
:href="config.public.baseUrl + '/site-details/' + item.id"
<nuxt-link
:to="routePath + '/site-details/' + item.id"
target="_blank"
@click.stop="onNuxtLink"
>
... ... @@ -44,12 +44,12 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</template>
</el-popconfirm>
<a
<nuxt-link
v-else
:href="config.public.baseUrl + '/site-details/' + item.id"
:to="routePath + '/site-details/' + item.id"
target="_blank"
>
<div class="group p-3">
... ... @@ -74,15 +74,22 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</div>
</template>
<script lang="ts" setup>
import { Promotion } from "@element-plus/icons-vue";
defineProps<{
cardList: any[];
}>();
// 设置默认值
const props = withDefaults(
defineProps<{
cardList: any[];
routePath?: string; // 必须加 ? 表示可选
}>(),
{
routePath: "", // 默认值
},
);
const config = useRuntimeConfig();
// 阻止默认行为
... ... @@ -91,6 +98,6 @@ function onNuxtLink(event: any) {
}
// 点击确认跳转
function onConfirm(id: number) {
window.open(`/site-details/${id}`);
window.open(props.routePath + "/site-details/" + id);
}
</script>
... ...
... ... @@ -49,15 +49,15 @@
</ul>
</div>
<div class="flex-auto"></div>
<a
<nuxt-link
class="hidden md:block text-xs ml-2 text-[#282a2d] hover:text-[#5961f9]"
:href="`${config.public.baseUrl}/category/${childAlias}`"
>查看更多 &gt;&gt;</a
:to="routePath + `/category/${childAlias}`"
>查看更多 &gt;&gt;</nuxt-link
>
<a
<nuxt-link
class="md:hidden text-xs ml-2 text-[#282a2d] hover:text-[#5961f9]"
:href="`${config.public.baseUrl}/category/${childAlias}`"
>&gt;&gt;</a
:to="routePath + `/category/${childAlias}`"
>&gt;&gt;</nuxt-link
>
</div>
</div>
... ... @@ -90,8 +90,8 @@
@confirm="onConfirm(appItem.id)"
>
<template #reference>
<a
:href="config.public.baseUrl + '/site-details/' + appItem.id"
<nuxt-link
:to="routePath + '/site-details/' + appItem.id"
target="_blank"
@click.stop="onNuxtLink"
>
... ... @@ -117,12 +117,12 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</template>
</el-popconfirm>
<a
<nuxt-link
v-else
:href="config.public.baseUrl + '/site-details/' + appItem.id"
:to="routePath + '/site-details/' + appItem.id"
target="_blank"
>
<div class="group p-3">
... ... @@ -147,22 +147,27 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</div>
</div>
</template>
<script setup lang="ts">
import { Promotion } from "@element-plus/icons-vue";
const props = defineProps<{
childData: any;
}>();
// 设置默认值
const props = withDefaults(
defineProps<{
childData: any;
routePath?: string; // 必须加 ? 表示可选
}>(),
{
routePath: "", // 默认值
},
);
const route = useRoute();
const childAlias = ref(props.childData.children[0].alias);
const config = useRuntimeConfig();
const { activeSubCategoryId, setActiveSubCategory } = useActiveSubCategory();
// 监听activeSubCategoryId的变化,当变化是把currentFilter更新为-1
watch(activeSubCategoryId, (newValue, oldValue) => {
if (newValue !== oldValue) {
... ... @@ -175,7 +180,7 @@ function onNuxtLink(event: any) {
}
function onConfirm(id: number) {
window.open(`/site-details/${id}`);
window.open(props.routePath + "/site-details/" + id);
}
const currentFilter = ref(0);
... ... @@ -199,7 +204,7 @@ const scrollToSubCategory = () => {
if (parentId === String(props.childData.id)) {
const index = props.childData.children.findIndex(
(child: any) => String(child.id) === childId
(child: any) => String(child.id) === childId,
);
if (index !== -1) {
currentFilter.value = index;
... ...
... ... @@ -11,15 +11,15 @@
{{ childData.label }}
</h4>
<div class="flex-auto"></div>
<a
<nuxt-link
class="hidden md:block text-xs ml-2 text-[#282a2d] hover:text-[#5961f9] dark:text-[#989da1]"
:href="`${config.public.baseUrl}/category/${childData.alias}`"
>查看更多 &gt;&gt;</a
:to="`${routePath}/category/${childData.alias}`"
>查看更多 &gt;&gt;</nuxt-link
>
<a
<nuxt-link
class="md:hidden text-xs ml-2 text-[#282a2d] hover:text-[#5961f9]"
:href="`${config.public.baseUrl}/category/${childData.alias}`"
>&gt;&gt;</a
:to="`${routePath}/category/${childData.alias}`"
>&gt;&gt;</nuxt-link
>
</div>
</div>
... ... @@ -45,8 +45,8 @@
@confirm="onConfirm(item.id)"
>
<template #reference>
<a
:href="'/site-details/' + item.id"
<nuxt-link
:to="routePath + '/site-details/' + item.id"
target="_blank"
@click.stop="onNuxtLink"
>
... ... @@ -72,10 +72,14 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</template>
</el-popconfirm>
<a v-else :href="'/site-details/' + item.id" target="_blank">
<nuxt-link
v-else
:to="routePath + '/site-details/' + item.id"
target="_blank"
>
<div class="group p-3">
<div class="flex items-start space-x-4">
<img
... ... @@ -98,23 +102,31 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</div>
</div>
</template>
<script setup lang="ts">
import { Promotion } from "@element-plus/icons-vue";
defineProps<{
childData: any;
}>();
// 设置默认值
const props = withDefaults(
defineProps<{
childData: any;
routePath?: string; // 必须加 ? 表示可选
}>(),
{
routePath: "", // 默认值
},
);
const config = useRuntimeConfig();
const route = useRoute();
// 阻止默认行为
function onNuxtLink(event: any) {
event.preventDefault();
}
// 点击确认跳转
function onConfirm(id: number) {
window.open(`/site-details/${id}`);
window.open(props.routePath + "/site-details/" + id);
}
</script>
... ...
<template>
<LazyHomeContentHasChildren v-if="appData.children" :childData="appData" />
<LazyHomeContentNoChildren v-else :childData="appData" />
<LazyHomeContentHasChildren
v-if="appData.children"
:childData="appData"
:routePath="routePath"
/>
<LazyHomeContentNoChildren
v-else
:childData="appData"
:routePath="routePath"
/>
</template>
<script lang="ts" setup>
defineProps<{
appData: any;
}>();
withDefaults(
defineProps<{
appData: any;
routePath?: string; // 必须加 ? 表示可选
}>(),
{
routePath: "", // 默认值
},
);
</script>
... ...
... ... @@ -51,8 +51,8 @@
@confirm="onConfirm(item.id)"
>
<template #reference>
<a
:href="config.public.baseUrl + '/site-details/' + item.id"
<nuxt-link
:to="routePath + '/site-details/' + item.id"
target="_blank"
@click.stop="onNuxtLink"
>
... ... @@ -78,12 +78,12 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</template>
</el-popconfirm>
<a
<nuxt-link
v-else
:href="config.public.baseUrl + '/site-details/' + item.id"
:to="routePath + '/site-details/' + item.id"
target="_blank"
>
<div class="group p-3">
... ... @@ -108,7 +108,7 @@
</div>
</div>
</div>
</a>
</nuxt-link>
</div>
</div>
</div>
... ... @@ -116,12 +116,20 @@
<script lang="ts" setup>
import { Promotion } from "@element-plus/icons-vue";
defineProps<{
recommendList: any[];
navTitle: string;
navIcon: string;
}>();
// 设置默认值
withDefaults(
defineProps<{
recommendList: any[];
navTitle: string;
navIcon: string;
routePath?: string; // 必须加 ? 表示可选
}>(),
{
routePath: "", // 默认值
},
);
const config = useRuntimeConfig();
const route = useRoute();
// 阻止默认行为
function onNuxtLink(event: any) {
event.preventDefault();
... ...
... ... @@ -23,6 +23,13 @@
<el-dropdown-item>
<a
class="ignore"
href="javascript:translate.changeLanguage('chinese_traditional');"
>繁体中文</a
>
</el-dropdown-item>
<el-dropdown-item>
<a
class="ignore"
href="javascript:translate.changeLanguage('vietnamese');"
>Tiếng Việt</a
>
... ...
... ... @@ -36,11 +36,14 @@
</div>
</template>
<script setup>
<script setup lang="ts">
const keyWord = ref("");
const isExpanded = ref(false);
const searchInput = ref(null);
const route = useRoute();
const routePath = computed(() => {
return route.path.startsWith("/hktw") ? "/hktw" : "";
});
function toggleSearch() {
isExpanded.value = !isExpanded.value;
if (isExpanded.value && searchInput.value) {
... ... @@ -58,7 +61,7 @@ function handleBlur() {
function onSearch() {
if (keyWord.value) {
window.location.href = "/search?keyword=" + keyWord.value;
window.location.href = routePath.value + "/search?keyword=" + keyWord.value;
}
}
</script>
... ...
... ... @@ -15,7 +15,6 @@
<script lang="ts" setup>
const { initTheme } = useDarkMode();
onMounted(() => {
initTheme();
});
... ...
... ... @@ -40,8 +40,8 @@ export default defineNuxtConfig({
compressPublicAssets: false,
devProxy: {
'/dev-api': {
// target: 'http://192.168.2.15:35273/',
target: 'http://htai.aiboxgo.com/',
target: 'http://192.168.2.15:35273/',
// target: 'https://htai.aiboxgo.com/',
changeOrigin: true,
}
... ... @@ -49,8 +49,8 @@ export default defineNuxtConfig({
// 该配置用于服务端请求转发
routeRules: {
'/dev-api/**': {
// proxy: 'http://192.168.2.15:35273/**'
proxy: 'http://htai.aiboxgo.com/**'
proxy: 'http://192.168.2.15:35273/**'
// proxy: 'http://htai.aiboxgo.com/**'
},
},
prerender: {
... ...
... ... @@ -40,7 +40,7 @@ const params = ref<any>({
});
function findLabelByAlias<
T extends { alias?: string; label?: string; children?: T[] }
T extends { alias?: string; label?: string; children?: T[] },
>(alias: string, data: T[], childrenKey: string = "children"): string {
if (!data || data.length === 0) {
return "";
... ... @@ -98,10 +98,10 @@ useHead({
content: `${categoryLabel}分类下的AI工具推荐,精选优质${categoryLabel}相关AI工具。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.fullPath },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [{ rel: "canonical", href: config.public.baseUrl + route.fullPath }],
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
script: [
{
type: "application/ld+json",
... ... @@ -110,8 +110,8 @@ useHead({
"@graph": [
{
"@type": "CollectionPage",
"@id": config.public.baseUrl + route.fullPath + "#collectionpage",
url: config.public.baseUrl + route.fullPath,
"@id": config.public.baseUrl + route.path + "#collectionpage",
url: config.public.baseUrl + route.path,
name: `${categoryLabel} - ${webSite.value.webname}`,
description: `${categoryLabel}分类下的AI工具推荐`,
isPartOf: {
... ... @@ -124,7 +124,7 @@ useHead({
},
{
"@type": "BreadcrumbList",
"@id": config.public.baseUrl + route.fullPath + "#breadcrumb",
"@id": config.public.baseUrl + route.path + "#breadcrumb",
itemListElement: [
{
"@type": "ListItem",
... ...
... ... @@ -39,7 +39,7 @@ const params = ref<any>({
});
function findLabelByAlias<
T extends { alias?: string; label?: string; children?: T[] }
T extends { alias?: string; label?: string; children?: T[] },
>(alias: string, data: T[], childrenKey: string = "children"): string {
if (!data || data.length === 0) {
return "";
... ... @@ -103,9 +103,9 @@ useHead({
content: `${categoryLabel}分类下的AI工具推荐第${pageNum}页。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.fullPath },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [{ rel: "canonical", href: config.public.baseUrl + route.fullPath }],
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
});
</script>
... ...
<template>
<div class="md:p-10 p-4 pt-0" style="min-height: calc(100vh - 320px)">
<a
id="toogle-chinese-traditional"
href="javascript:translate.changeLanguage('chinese_traditional');"
class="ignore"
></a>
<h1 style="font-size: 0; margin: 0">
{{ findLabelByAlias(name as string, sortList) }}
</h1>
<HomeRecommend
:recommendList="list"
:navTitle="findLabelByAlias(name as string, sortList)"
navIcon="tag"
routePath="/hktw"
/>
<el-pagination
background
layout="prev, pager, next"
:hide-on-single-page="true"
v-model:page-size="params.pageSize"
v-model:current-page="params.pageNum"
:total="total"
@current-change="onPageChange"
/>
</div>
</template>
<script lang="ts" setup>
import type { appType } from "~/api/types/app";
import { getAppList } from "~/api/app";
import type { classifyType } from "~/api/types/classify";
import type { webSiteType } from "~/api/types/webSite";
const sortList = useState<classifyType[]>("sortTree");
const webSite = useState<webSiteType>("webSite");
const route = useRoute();
const router = useRouter();
const config = useRuntimeConfig();
const { name } = route.params;
const list = ref<appType[]>([]);
const total = ref<number>(0);
const params = ref<any>({
pageNum: 1,
pageSize: 30,
typeAlias: name as string,
});
function findLabelByAlias<
T extends { alias?: string; label?: string; children?: T[] },
>(alias: string, data: T[], childrenKey: string = "children"): string {
if (!data || data.length === 0) {
return "";
}
for (const item of data) {
if (item.alias === alias) {
return item.label || "";
}
}
for (const item of data) {
const children = (item as any)[childrenKey] as T[];
if (children && children.length > 0) {
const foundLabel = findLabelByAlias(alias, children, childrenKey);
if (foundLabel !== "") {
return foundLabel;
}
}
}
return "";
}
const categoryLabel = findLabelByAlias(name as string, sortList.value);
function onPageChange(pageNum: number) {
router.push({
path: route.path + "/page/" + pageNum,
});
}
const res = await getAppList(params.value);
list.value = res.rows;
total.value = res.total;
onMounted(() => {
let toggleChineseTraditional = document.getElementById(
"toogle-chinese-traditional",
);
if (toggleChineseTraditional) {
// 自动点击
toggleChineseTraditional.click();
}
});
useHead({
title: `${categoryLabel} - ${webSite.value.webname}`,
meta: [
{
name: "description",
content: `${categoryLabel}分类下的AI工具推荐,精选优质${categoryLabel}相关AI工具,助您高效完成工作。`,
},
{
name: "keywords",
content: `${categoryLabel},AI工具,${categoryLabel}AI,人工智能,${webSite.value.webkeywords}`,
},
{ name: "robots", content: "index, follow" },
{
property: "og:title",
content: `${categoryLabel} - ${webSite.value.webname}`,
},
{
property: "og:description",
content: `${categoryLabel}分类下的AI工具推荐,精选优质${categoryLabel}相关AI工具。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
script: [
{
type: "application/ld+json",
children: JSON.stringify({
"@context": "https://schema.org",
"@graph": [
{
"@type": "CollectionPage",
"@id": config.public.baseUrl + route.path + "#collectionpage",
url: config.public.baseUrl + route.path,
name: `${categoryLabel} - ${webSite.value.webname}`,
description: `${categoryLabel}分类下的AI工具推荐`,
isPartOf: {
"@id": "https://aiboxgo.com/#website",
},
about: {
"@type": "Thing",
name: categoryLabel,
},
},
{
"@type": "BreadcrumbList",
"@id": config.public.baseUrl + route.path + "#breadcrumb",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "首页",
item: "https://aiboxgo.com/",
},
{
"@type": "ListItem",
position: 2,
name: categoryLabel,
},
],
},
],
}),
},
],
});
</script>
... ...
<template>
<div class="p-10">
<a
id="toogle-chinese-traditional"
href="javascript:translate.changeLanguage('chinese_traditional');"
class="ignore"
></a>
<!-- 推荐工具区 -->
<HomeRecommend
:recommendList="list"
:navTitle="findLabelByAlias(name as string, sortList)"
navIcon="tag"
:navTitleWidth="120.5"
routePath="/hktw"
/>
<el-pagination
background
layout="prev, pager, next"
:hide-on-single-page="true"
v-model:page-size="params.pageSize"
v-model:current-page="params.pageNum"
:total="total"
@current-change="onPageChange"
/>
</div>
</template>
<script lang="ts" setup>
import type { appType } from "~/api/types/app";
import { getAppList } from "~/api/app";
import type { classifyType } from "~/api/types/classify";
import type { webSiteType } from "~/api/types/webSite";
const sortList = useState<classifyType[]>("sortTree");
const webSite = useState<webSiteType>("webSite");
const route = useRoute();
const router = useRouter();
const config = useRuntimeConfig();
const { pageNum, name } = route.params;
const list = ref<appType[]>([]);
const total = ref<number>(0);
const params = ref<any>({
pageNum: Number(pageNum),
pageSize: 30,
typeAlias: name as string,
});
function findLabelByAlias<
T extends { alias?: string; label?: string; children?: T[] },
>(alias: string, data: T[], childrenKey: string = "children"): string {
if (!data || data.length === 0) {
return "";
}
for (const item of data) {
if (item.alias === alias) {
return item.label || "";
}
}
for (const item of data) {
const children = (item as any)[childrenKey] as T[];
if (children && children.length > 0) {
const foundLabel = findLabelByAlias(alias, children, childrenKey);
if (foundLabel !== "") {
return foundLabel;
}
}
}
return "";
}
const categoryLabel = findLabelByAlias(name as string, sortList.value);
function onPageChange(pageNum: number) {
if (pageNum === 1) {
router.push({
path: "/hktw/category/" + name,
});
} else if (pageNum > 1) {
router.push({
path: `/hktw/category/${name}/page/${pageNum}`,
});
}
}
const res = await getAppList(params.value);
list.value = res.rows;
total.value = res.total;
onMounted(() => {
let toggleChineseTraditional = document.getElementById(
"toogle-chinese-traditional",
);
if (toggleChineseTraditional) {
// 自动点击
toggleChineseTraditional.click();
}
});
useHead({
title: `${categoryLabel} - 第${pageNum}页 - ${webSite.value.webname}`,
meta: [
{
name: "description",
content: `${categoryLabel}分类下的AI工具推荐第${pageNum}页,精选优质${categoryLabel}相关AI工具。`,
},
{
name: "keywords",
content: `${categoryLabel},AI工具,${categoryLabel}AI,人工智能,第${pageNum}页`,
},
{ name: "robots", content: "index, follow" },
{
property: "og:title",
content: `${categoryLabel} - 第${pageNum}页 - ${webSite.value.webname}`,
},
{
property: "og:description",
content: `${categoryLabel}分类下的AI工具推荐第${pageNum}页。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
});
</script>
... ...
<script lang="ts" setup>
import { getAppList, getAllApp } from "~/api/app";
import type { webSiteType } from "~/api/types/webSite";
import { getAdList } from "~/api/ad";
const webSite = useState<webSiteType>("webSite");
const route = useRoute();
console.log(route);
const { data: adList } = await useAsyncData(
"adList",
async () => {
const res = await getAdList();
return res[0] || null;
},
{
server: true,
lazy: false,
},
);
const { data: recommendList } = await useAsyncData(
"recommendList",
async () => {
const res = await getAppList({
pageSize: 10,
pageNum: 1,
isRecommend: "1",
});
return res.rows || [];
},
{
server: true,
lazy: false,
},
);
const { data: appList } = await useAsyncData(
"appList",
async () => {
const res = await getAllApp();
return res.data || [];
},
{
server: true,
lazy: false,
},
);
onMounted(() => {
let toggleChineseTraditional = document.getElementById(
"toogle-chinese-traditional",
);
if (toggleChineseTraditional) {
// 自动点击
toggleChineseTraditional.click();
}
});
useHead({
title: webSite.value.webname,
meta: [
{ name: "description", content: webSite.value.webdescription },
{ name: "keywords", content: webSite.value.webkeywords },
{ name: "format-detection", content: "telephone=no" },
{ name: "referrer", content: "origin-when-cross-origin" },
{
name: "robots",
content:
"follow, index, max-snippet:-1, max-video-preview:-1, max-image-preview:large",
},
{ property: "og:locale", content: "zh_CN" },
{
property: "og:title",
content: "AI工具盒 - 優質 AI 工具集合、AI 資源網站、AI 導航平台",
},
{
property: "og:description",
content:
"專業 AI 工具導全网優質全网優質 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 資源,快速找到好用的 AI 工具。",
},
{ property: "og:url", content: "https://aiboxgo.com/" },
{ property: "og:site_name", content: "AI工具盒" },
{ property: "og:type", content: "website" },
{ property: "twitter:card", content: "summary_large_image" },
{
property: "twitter:title",
content: "AI工具盒 - 優質 AI 工具集合、AI 資源網站、AI 導航平台",
},
{
property: "twitter:description",
content:
"專業 AI 工具導全网優質全网優質 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 資源,快速找到好用的 AI 工具。",
},
],
script: [
{
type: "application/ld+json",
children: JSON.stringify({
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebSite",
"@id": "https://aiboxgo.com/#website",
url: "https://aiboxgo.com/",
name: webSite.value.webname,
description: webSite.value.webdescription,
inLanguage: "zh-CN",
potentialAction: {
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate:
"https://aiboxgo.com/search?keyword={search_term_string}",
},
"query-input": "required name=search_term_string",
},
},
{
"@type": "Organization",
"@id": "https://aiboxgo.com/#organization",
name: webSite.value.webname,
url: "https://aiboxgo.com/",
logo: {
"@type": "ImageObject",
url: "https://aiboxgo.com/favicon.ico",
},
sameAs: [],
},
{
"@type": "CollectionPage",
"@id": "https://aiboxgo.com/",
url: "https://aiboxgo.com/",
name: webSite.value.webname,
description: webSite.value.webdescription,
isPartOf: {
"@id": "https://aiboxgo.com/#website",
},
about: {
"@id": "https://aiboxgo.com/#organization",
},
},
],
}),
},
],
});
</script>
<template>
<div
class="flex flex-col min-h-screen bg-white dark:bg-[#1a1b1d] transition-colors duration-300"
>
<a
id="toogle-chinese-traditional"
href="javascript:translate.changeLanguage('chinese_traditional');"
class="ignore"
></a>
<main
class="flex-grow md:p-6 p-2 bg-white dark:bg-[#1a1b1d] transition-colors duration-300"
>
<ADSwiperCarousel v-if="adList" :adSwiperList="adList" />
<HomeBanner />
<section class="md:mb-12 mb-6">
<HomeRecommend
v-if="recommendList && recommendList.length > 0"
:recommendList="recommendList"
navTitle="推薦工具"
navIcon="star"
routePath="/hktw"
/>
<div
v-for="appItem in appList"
:key="appItem?.id"
class="md:mb-12 mb-6"
>
<HomeContent :appData="appItem" routePath="/hktw" />
</div>
</section>
</main>
</div>
</template>
... ...
<template>
<div class="p-10" style="min-height: calc(100vh - 320px)">
<a
id="toogle-chinese-traditional"
href="javascript:translate.changeLanguage('chinese_traditional');"
class="ignore"
></a>
<HomeRecommend
:recommendList="list"
:navTitle="keyword"
navIcon="tag"
routePath="/hktw"
/>
<el-empty v-show="!list.length" description="未搜索到相关内容" />
<el-pagination
background
layout="prev, pager, next"
:hide-on-single-page="true"
v-model:page-size="params.pageSize"
v-model:current-page="params.pageNum"
:total="total"
@current-change="onPageChange"
/>
</div>
</template>
<script lang="ts" setup>
import type { appType } from "~/api/types/app";
import { getAppList } from "~/api/app";
import type { webSiteType } from "~/api/types/webSite";
const route = useRoute();
const router = useRouter();
const config = useRuntimeConfig();
const webSite = useState<webSiteType>("webSite");
const { keyword } = route.query as { keyword: string };
const list = ref<appType[]>([]);
const total = ref<number>(0);
const params = ref<any>({
pageNum: 1,
pageSize: 10,
title: keyword,
});
function onPageChange(pageNum: number) {
router.push({
path: route.path + "/page/" + pageNum,
query: {
keyword: keyword,
},
});
}
const res = await getAppList(params.value);
list.value = res.rows;
total.value = res.total;
onMounted(() => {
let toggleChineseTraditional = document.getElementById(
"toogle-chinese-traditional",
);
if (toggleChineseTraditional) {
// 自动点击
toggleChineseTraditional.click();
}
});
useHead({
title: `"${keyword}" 搜索结果 - ${webSite.value.webname}`,
meta: [
{
name: "description",
content: `搜索"${keyword}"相关的AI工具,找到${total.value}个结果。${webSite.value.webdescription}`,
},
{
name: "keywords",
content: `${keyword},AI工具搜索,${webSite.value.webkeywords}`,
},
{ name: "robots", content: "noindex, follow" },
{
property: "og:title",
content: `"${keyword}" 搜索结果 - ${webSite.value.webname}`,
},
{
property: "og:description",
content: `搜索"${keyword}"相关的AI工具结果页面。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
});
</script>
... ...
<template>
<div class="p-10">
<a
id="toogle-chinese-traditional"
href="javascript:translate.changeLanguage('chinese_traditional');"
class="ignore"
></a>
<!-- 推荐工具区 -->
<HomeRecommend
:recommendList="list"
:navTitle="keyword"
navIcon="tag"
:navTitleWidth="120.5"
routePath="/hktw"
/>
<el-pagination
background
layout="prev, pager, next"
:hide-on-single-page="true"
v-model:page-size="params.pageSize"
v-model:current-page="params.pageNum"
:total="total"
@current-change="onPageChange"
/>
</div>
</template>
<script lang="ts" setup>
import type { appType } from "~/api/types/app";
import { getAppList } from "~/api/app";
import type { webSiteType } from "~/api/types/webSite";
const route = useRoute();
const router = useRouter();
const config = useRuntimeConfig();
const webSite = useState<webSiteType>("webSite");
const { pageNum } = route.params;
const { keyword } = route.query as { keyword: string };
const list = ref<appType[]>([]);
const total = ref<number>(0);
const params = ref<any>({
pageNum: Number(pageNum),
pageSize: 10,
title: keyword,
});
function onPageChange(pageNum: number) {
if (pageNum === 1) {
router.push({
path: "/hktw/search",
query: {
keyword: keyword,
},
});
} else if (pageNum > 1) {
router.push({
path: `/hktw/search/page/${pageNum}`,
query: {
keyword: keyword,
},
});
}
}
const res = await getAppList(params.value);
list.value = res.rows;
total.value = res.total;
onMounted(() => {
let toggleChineseTraditional = document.getElementById(
"toogle-chinese-traditional",
);
if (toggleChineseTraditional) {
// 自动点击
toggleChineseTraditional.click();
}
});
useHead({
title: `"${keyword}" 搜索结果 - 第${pageNum}页 - ${webSite.value.webname}`,
meta: [
{
name: "description",
content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。`,
},
{ name: "keywords", content: `${keyword},AI工具搜索,第${pageNum}页` },
{ name: "robots", content: "noindex, follow" },
{
property: "og:title",
content: `"${keyword}" 搜索结果 - 第${pageNum}页 - ${webSite.value.webname}`,
},
{
property: "og:description",
content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
});
</script>
... ...
<script lang="ts" setup>
import { getAppDetail, getAppList } from "~/api/app";
import type { appDetail, Types, appType } from "~/api/types/app";
import type { webSiteType } from "~/api/types/webSite";
const route = useRoute();
const config = useRuntimeConfig();
const DetailData = ref<appDetail>({
id: 0,
title: "",
content: "",
image: "",
description: "",
link: "",
types: [],
});
const webSite = useState<webSiteType>("webSite");
const relatedApps = ref<appType[]>([]);
function mergeDuplicates(data: Types[]) {
const map = new Map();
data.forEach((item) => {
if (!map.has(item.id)) {
map.set(item.id, {
id: item.id,
label: item.label,
alias: item.alias,
children: [...(item.children || [])],
});
} else {
const existing = map.get(item.id);
const existingChildIds = new Set(
existing.children.map((child: any) => child.id),
);
item.children.forEach((child) => {
if (!existingChildIds.has(child.id)) {
existing.children.push(child);
}
});
}
});
return Array.from(map.values());
}
const detailRes = await getAppDetail(Number(route.params.id));
DetailData.value = detailRes.data;
DetailData.value.types = mergeDuplicates(detailRes.data.types);
if (DetailData.value.types?.length > 0) {
const firstType = DetailData.value.types[0];
const typeAlias = firstType.alias || firstType.children?.[0]?.alias;
if (typeAlias) {
const relatedRes = await getAppList({
pageNum: 1,
pageSize: 8,
typeAlias: typeAlias,
});
relatedApps.value = relatedRes.rows
.filter((app: appType) => app.id !== DetailData.value.id)
.slice(0, 6);
}
}
onMounted(() => {
let toggleChineseTraditional = document.getElementById(
"toogle-chinese-traditional",
);
if (toggleChineseTraditional) {
// 自动点击
toggleChineseTraditional.click();
}
});
useHead({
title: DetailData.value.description
? `${DetailData.value.title} - ${DetailData.value.description}`
: DetailData.value.title,
meta: [
{ name: "description", content: DetailData.value.description },
{
name: "keywords",
content: `${DetailData.value.title},AI工具,${
DetailData.value.types?.map((t: any) => t.label).join(",") || ""
}`,
},
{ name: "robots", content: "index, follow" },
{
property: "og:title",
content: `${DetailData.value.title}${
DetailData.value.popupContent != null
? ` - ${DetailData.value.popupContent}`
: `- ${DetailData.value.description} | ${webSite.value.webname.slice(
0,
7,
)}`
}`,
},
{ property: "og:description", content: DetailData.value.description },
{ property: "og:type", content: "article" },
{
property: "og:image",
content: config.public.baseUrl + DetailData.value.image,
},
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname.slice(0, 7) },
{ property: "og:updated_time", content: DetailData.value.updateTime },
{
property: "article:published_time",
content: DetailData.value.updateTime,
},
{ property: "twitter:card", content: "summary_large_image" },
{ property: "twitter:title", content: DetailData.value.title },
{ property: "twitter:description", content: DetailData.value.description },
{
property: "twitter:image",
content: config.public.baseUrl + DetailData.value.image,
},
],
link: [
{
rel: "canonical",
href: config.public.baseUrl + route.path,
},
],
script: [
{
type: "application/ld+json",
children: JSON.stringify({
"@context": "https://schema.org",
"@graph": [
{
"@type": "SoftwareApplication",
"@id": config.public.baseUrl + route.path + "#software",
name: DetailData.value.title,
description: DetailData.value.description,
url: DetailData.value.link,
image: config.public.baseUrl + DetailData.value.image,
applicationCategory: "UtilitiesApplication",
operatingSystem: "Web Browser",
offers: {
"@type": "Offer",
price: "0",
priceCurrency: "CNY",
},
},
{
"@type": "Article",
"@id": config.public.baseUrl + route.path + "#article",
headline: DetailData.value.title,
description: DetailData.value.description,
image: config.public.baseUrl + DetailData.value.image,
datePublished: DetailData.value.updateTime,
dateModified: DetailData.value.updateTime,
author: {
"@id": "https://aiboxgo.com/#organization",
},
publisher: {
"@id": "https://aiboxgo.com/#organization",
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": config.public.baseUrl + route.path,
},
},
{
"@type": "BreadcrumbList",
"@id": config.public.baseUrl + route.path + "#breadcrumb",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "首页",
item: "https://aiboxgo.com/",
},
...(DetailData.value.types?.[0]
? [
{
"@type": "ListItem",
position: 2,
name: DetailData.value.types[0].label,
item: `https://aiboxgo.com/category/${DetailData.value.types[0].alias}`,
},
]
: []),
{
"@type": "ListItem",
position: DetailData.value.types?.length ? 3 : 2,
name: DetailData.value.title,
},
],
},
],
}),
},
],
});
</script>
<template>
<div
class="flex flex-col min-h-screen bg-white dark:bg-[#1a1b1d] transition-colors duration-300"
>
<a
id="toogle-chinese-traditional"
href="javascript:translate.changeLanguage('chinese_traditional');"
class="ignore"
></a>
<main class="flex-grow bg-white dark:bg-[#1a1b1d]">
<div class="relative overflow-hidden">
<CommonParticleBackground
:particleCount="60"
particleColor="#5961f9"
lineColor="#7c3aed"
:particleSize="2"
:lineDistance="100"
:speed="0.3"
/>
<div
class="absolute inset-0 bg-gradient-to-r from-[#5961f9]/10 via-[#7c3aed]/10 to-[#a855f7]/10 dark:from-[#5961f9]/5 dark:via-[#7c3aed]/5 dark:to-[#a855f7]/5"
></div>
<div class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start">
<div class="lg:col-span-2 flex justify-center lg:justify-end">
<div class="relative group">
<div
class="absolute -inset-1 bg-gradient-to-r from-[#5961f9] to-[#a855f7] rounded-2xl blur opacity-30 group-hover:opacity-50 transition duration-300"
></div>
<img
:src="config.public.apiUrl + DetailData.image"
:alt="DetailData.title"
class="relative w-24 h-24 md:w-32 md:h-32 object-contain bg-white dark:bg-gray-800 rounded-2xl shadow-xl p-3"
loading="lazy"
/>
</div>
</div>
<div class="lg:col-span-7 space-y-4">
<div class="flex items-center gap-3 flex-wrap">
<h1
class="text-2xl md:text-3xl font-bold text-[#282a2d] dark:text-[#c6c9cf]"
>
{{ DetailData.title }}
</h1>
<span
v-if="DetailData.popupContent"
class="px-3 py-1 bg-gradient-to-r from-[#5961f9] to-[#a855f7] text-white text-xs rounded-full"
>
{{ DetailData.popupContent }}
</span>
</div>
<div class="flex flex-wrap gap-2">
<template v-for="tag in DetailData.types" :key="tag.id">
<template v-if="tag.children && tag.children.length > 0">
<NuxtLink
v-for="child in tag.children"
:key="child.id"
:to="'/category/' + child.alias"
class="px-3 py-1.5 bg-white/80 dark:bg-gray-800/80 text-[#5961f9] dark:text-[#8b92f9] rounded-full text-sm hover:bg-[#5961f9] hover:text-white dark:hover:bg-[#5961f9] transition-all duration-300 shadow-sm"
>
{{ child.label }}
</NuxtLink>
</template>
<template v-else>
<NuxtLink
:to="'/category/' + tag.alias"
class="px-3 py-1.5 bg-white/80 dark:bg-gray-800/80 text-[#5961f9] dark:text-[#8b92f9] rounded-full text-sm hover:bg-[#5961f9] hover:text-white dark:hover:bg-[#5961f9] transition-all duration-300 shadow-sm"
>
{{ tag.label }}
</NuxtLink>
</template>
</template>
</div>
<p
class="text-gray-600 dark:text-gray-300 text-base leading-relaxed line-clamp-3"
>
{{ DetailData.description }}
</p>
<div class="flex flex-wrap gap-3 pt-2">
<a
:href="DetailData.link"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-[#5961f9] to-[#7c3aed] hover:from-[#4751e8] hover:to-[#6d28d9] text-white font-medium rounded-xl shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-300"
>
<i class="iconfont icon-guide text-lg"></i>
<span>立即访问</span>
</a>
</div>
</div>
<div class="lg:col-span-3">
<div
class="bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-2xl shadow-lg p-4 border border-gray-100 dark:border-gray-700"
>
<div
class="text-center text-gray-400 dark:text-gray-500 text-sm"
>
<a
no_cache=""
href="https://www.coze.cn/?utm_medium=daohang&amp;utm_source=aikit&amp;utm_content=&amp;utm_id=&amp;utm_campaign=&amp;utm_term=hw_coze_aikit&amp;utm_source_platform="
rel="external nofollow"
target="_blank"
><img
src="https://ai-kit.cn/wp-content/uploads/2026/01/kouzi_gd.jpg"
alt="扣子"
/></a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<article class="prose prose-lg dark:prose-invert max-w-none p-6 md:p-8">
<div v-html="DetailData.content" class="detail-content"></div>
</article>
</div>
<div
v-if="relatedApps.length > 0"
class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"
>
<div class="rounded-2xl p-6">
<h2
class="text-xl font-bold text-[#555] dark:text-[#888] mb-6 flex items-center gap-2"
>
<i class="iconfont icon-tag" style="font-size: 1.2rem"></i>
相关推荐
</h2>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4">
<CommonCard :cardList="relatedApps" routePath="/hktw" />
</div>
</div>
</div>
</main>
</div>
</template>
<style scoped lang="less">
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.detail-content {
@apply text-[#282a2d] dark:text-[#c6c9cf];
}
.detail-content :deep(h1),
.detail-content :deep(h2),
.detail-content :deep(h3),
.detail-content :deep(h4) {
@apply text-[#282a2d] my-5 py-1 pl-5 border-l-4 border-[#5961f9] dark:text-[#c6c9cf];
}
.detail-content :deep(h1) {
@apply text-2xl;
}
.detail-content :deep(h2) {
@apply text-xl;
}
.detail-content :deep(h3) {
@apply text-2xl;
}
.detail-content :deep(p) {
@apply mb-4 leading-relaxed;
}
.detail-content :deep(img) {
@apply rounded-lg max-w-full h-auto my-4;
}
.detail-content :deep(a) {
@apply text-[#5961f9] hover:underline;
}
.detail-content :deep(ul),
.detail-content :deep(ol) {
@apply pl-6 mb-4 text-sm list-disc;
}
.detail-content :deep(li) {
@apply mb-2;
}
.detail-content :deep(blockquote) {
@apply border-l-4 border-[#5961f9] pl-4 italic my-4;
}
.detail-content :deep(code) {
@apply bg-gray-100 dark:bg-gray-700 px-1 py-0.5 rounded text-sm;
}
.detail-content :deep(pre) {
@apply bg-gray-100 dark:bg-gray-700 p-4 rounded-lg overflow-x-auto my-4;
}
.detail-content :deep(table) {
@apply w-full border-collapse my-4;
}
.detail-content :deep(th),
.detail-content :deep(td) {
@apply border border-gray-200 dark:border-gray-600 px-4 py-2;
}
.detail-content :deep(th) {
@apply bg-gray-100 dark:bg-gray-700 font-semibold;
}
</style>
... ...
... ... @@ -5,7 +5,6 @@ import { getAdList } from "~/api/ad";
const webSite = useState<webSiteType>("webSite");
const route = useRoute();
console.log(route);
const { data: adList } = await useAsyncData(
"adList",
async () => {
... ... @@ -15,7 +14,7 @@ const { data: adList } = await useAsyncData(
{
server: true,
lazy: false,
}
},
);
const { data: recommendList } = await useAsyncData(
... ... @@ -31,7 +30,7 @@ const { data: recommendList } = await useAsyncData(
{
server: true,
lazy: false,
}
},
);
const { data: appList } = await useAsyncData(
... ... @@ -43,7 +42,7 @@ const { data: appList } = await useAsyncData(
{
server: true,
lazy: false,
}
},
);
useHead({
... ...
... ... @@ -51,17 +51,27 @@ total.value = res.total;
useHead({
title: `"${keyword}" 搜索结果 - ${webSite.value.webname}`,
meta: [
{ name: "description", content: `搜索"${keyword}"相关的AI工具,找到${total.value}个结果。${webSite.value.webdescription}` },
{ name: "keywords", content: `${keyword},AI工具搜索,${webSite.value.webkeywords}` },
{
name: "description",
content: `搜索"${keyword}"相关的AI工具,找到${total.value}个结果。${webSite.value.webdescription}`,
},
{
name: "keywords",
content: `${keyword},AI工具搜索,${webSite.value.webkeywords}`,
},
{ name: "robots", content: "noindex, follow" },
{ property: "og:title", content: `"${keyword}" 搜索结果 - ${webSite.value.webname}` },
{ property: "og:description", content: `搜索"${keyword}"相关的AI工具结果页面。` },
{
property: "og:title",
content: `"${keyword}" 搜索结果 - ${webSite.value.webname}`,
},
{
property: "og:description",
content: `搜索"${keyword}"相关的AI工具结果页面。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.fullPath },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [
{ rel: "canonical", href: config.public.baseUrl + route.fullPath }
]
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
});
</script>
... ...
... ... @@ -61,17 +61,24 @@ total.value = res.total;
useHead({
title: `"${keyword}" 搜索结果 - 第${pageNum}页 - ${webSite.value.webname}`,
meta: [
{ name: "description", content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。` },
{
name: "description",
content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。`,
},
{ name: "keywords", content: `${keyword},AI工具搜索,第${pageNum}页` },
{ name: "robots", content: "noindex, follow" },
{ property: "og:title", content: `"${keyword}" 搜索结果 - 第${pageNum}页 - ${webSite.value.webname}` },
{ property: "og:description", content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。` },
{
property: "og:title",
content: `"${keyword}" 搜索结果 - 第${pageNum}页 - ${webSite.value.webname}`,
},
{
property: "og:description",
content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。`,
},
{ property: "og:type", content: "website" },
{ property: "og:url", content: config.public.baseUrl + route.fullPath },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname },
],
link: [
{ rel: "canonical", href: config.public.baseUrl + route.fullPath }
]
link: [{ rel: "canonical", href: config.public.baseUrl + route.path }],
});
</script>
... ...
... ... @@ -44,7 +44,6 @@ function mergeDuplicates(data: Types[]) {
const detailRes = await getAppDetail(Number(route.params.id));
DetailData.value = detailRes.data;
DetailData.value.types = mergeDuplicates(detailRes.data.types);
console.log(DetailData.value);
if (DetailData.value.types?.length > 0) {
const firstType = DetailData.value.types[0];
const typeAlias = firstType.alias || firstType.children?.[0]?.alias;
... ... @@ -90,7 +89,7 @@ useHead({
property: "og:image",
content: config.public.baseUrl + DetailData.value.image,
},
{ property: "og:url", content: config.public.baseUrl + route.fullPath },
{ property: "og:url", content: config.public.baseUrl + route.path },
{ property: "og:site_name", content: webSite.value.webname.slice(0, 7) },
{ property: "og:updated_time", content: DetailData.value.updateTime },
{
... ... @@ -108,7 +107,7 @@ useHead({
link: [
{
rel: "canonical",
href: config.public.baseUrl + route.fullPath,
href: config.public.baseUrl + route.path,
},
],
script: [
... ... @@ -119,7 +118,7 @@ useHead({
"@graph": [
{
"@type": "SoftwareApplication",
"@id": config.public.baseUrl + route.fullPath + "#software",
"@id": config.public.baseUrl + route.path + "#software",
name: DetailData.value.title,
description: DetailData.value.description,
url: DetailData.value.link,
... ... @@ -134,7 +133,7 @@ useHead({
},
{
"@type": "Article",
"@id": config.public.baseUrl + route.fullPath + "#article",
"@id": config.public.baseUrl + route.path + "#article",
headline: DetailData.value.title,
description: DetailData.value.description,
image: config.public.baseUrl + DetailData.value.image,
... ... @@ -148,12 +147,12 @@ useHead({
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": config.public.baseUrl + route.fullPath,
"@id": config.public.baseUrl + route.path,
},
},
{
"@type": "BreadcrumbList",
"@id": config.public.baseUrl + route.fullPath + "#breadcrumb",
"@id": config.public.baseUrl + route.path + "#breadcrumb",
itemListElement: [
{
"@type": "ListItem",
... ... @@ -317,7 +316,7 @@ useHead({
相关推荐
</h2>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4">
<CommonCard :cardList="relatedApps" />
<CommonCard :cardList="relatedApps" :routePath="''" />
</div>
</div>
</div>
... ...
此 diff 太大无法显示。
export default defineEventHandler(async (event) => {
const res = await $fetch('http://htai.toolsai.cc/sitemap.xml', {
method: 'get'
})
console.log("后台数据", res)
event.node.res.setHeader('content-type', 'text/xml')
return res
})
\ No newline at end of file