正在显示
10 个修改的文件
包含
298 行增加
和
14 行删除
不能预览此文件类型
| @@ -10,6 +10,7 @@ | @@ -10,6 +10,7 @@ | ||
| 10 | height="30" | 10 | height="30" |
| 11 | src="../../assets/Annie-logo.jpg" | 11 | src="../../assets/Annie-logo.jpg" |
| 12 | alt="Annie-logo" | 12 | alt="Annie-logo" |
| 13 | + fetchpriority="high" | ||
| 13 | /> | 14 | /> |
| 14 | <h1 style="font-size: 0px"> | 15 | <h1 style="font-size: 0px"> |
| 15 | {{ webSite.webname }} | 16 | {{ webSite.webname }} |
| @@ -52,6 +52,7 @@ export default defineNuxtConfig({ | @@ -52,6 +52,7 @@ export default defineNuxtConfig({ | ||
| 52 | app: { | 52 | app: { |
| 53 | head: { | 53 | head: { |
| 54 | title: 'Annie网站 - 优质 AI 工具集合、AI 资源网站、AI 导航平台', | 54 | title: 'Annie网站 - 优质 AI 工具集合、AI 资源网站、AI 导航平台', |
| 55 | + titleTemplate: '%s | Annie网站', | ||
| 55 | htmlAttrs: { | 56 | htmlAttrs: { |
| 56 | lang: 'zh-CN' | 57 | lang: 'zh-CN' |
| 57 | }, | 58 | }, |
| @@ -61,12 +62,22 @@ export default defineNuxtConfig({ | @@ -61,12 +62,22 @@ export default defineNuxtConfig({ | ||
| 61 | { name: "force-rendering", content: "webkit"}, | 62 | { name: "force-rendering", content: "webkit"}, |
| 62 | { "http-equiv":"X-UA-Compatible", content:"IE=edge, chrome=1" }, | 63 | { "http-equiv":"X-UA-Compatible", content:"IE=edge, chrome=1" }, |
| 63 | { name: 'viewport', content:"width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" }, | 64 | { name: 'viewport', content:"width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" }, |
| 64 | - { name:'robots', content:'max-image-preview:large'} | 65 | + { name: 'description', content: 'Annie网站是专业的AI工具导航平台,汇集全网优质AI工具,包含AI写作、AI绘画、AI视频、AI对话、AI代码、AI设计、AI办公等各类AI资源,让你快速找到好用的AI工具。' }, |
| 66 | + { name: 'keywords', content: 'AI工具,AI导航,AI资源,AI写作,AI绘画,AI视频,AI对话,ChatGPT,AI代码,AI设计,人工智能工具' }, | ||
| 67 | + { name: 'author', content: 'Annie网站' }, | ||
| 68 | + { name:'robots', content:'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1'}, | ||
| 69 | + { name: 'googlebot', content: 'index, follow' }, | ||
| 70 | + { name: 'format-detection', content: 'telephone=no' }, | ||
| 71 | + { name: 'referrer', content: 'origin-when-cross-origin' }, | ||
| 72 | + { property: 'og:type', content: 'website' }, | ||
| 73 | + { property: 'og:site_name', content: 'Annie网站' }, | ||
| 74 | + { property: 'og:locale', content: 'zh_CN' }, | ||
| 75 | + { property: 'twitter:card', content: 'summary_large_image' }, | ||
| 76 | + { property: 'twitter:site', content: '@Annie网站' } | ||
| 65 | ], | 77 | ], |
| 66 | link: [ | 78 | link: [ |
| 67 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, | 79 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, |
| 68 | { rel: 'canonical', href: 'https://aiboxgo.com/' } | 80 | { rel: 'canonical', href: 'https://aiboxgo.com/' } |
| 69 | - | ||
| 70 | ], | 81 | ], |
| 71 | script: [{ src: "/js/translate.js"}] | 82 | script: [{ src: "/js/translate.js"}] |
| 72 | }, | 83 | }, |
| @@ -24,9 +24,12 @@ | @@ -24,9 +24,12 @@ | ||
| 24 | import type { appType } from "~/api/types/app"; | 24 | import type { appType } from "~/api/types/app"; |
| 25 | import { getAppList } from "~/api/app"; | 25 | import { getAppList } from "~/api/app"; |
| 26 | import type { classifyType } from "~/api/types/classify"; | 26 | import type { classifyType } from "~/api/types/classify"; |
| 27 | +import type { webSiteType } from "~/api/types/webSite"; | ||
| 27 | const sortList = useState<classifyType[]>("sortTree"); | 28 | const sortList = useState<classifyType[]>("sortTree"); |
| 29 | +const webSite = useState<webSiteType>("webSite"); | ||
| 28 | const route = useRoute(); | 30 | const route = useRoute(); |
| 29 | const router = useRouter(); | 31 | const router = useRouter(); |
| 32 | +const config = useRuntimeConfig(); | ||
| 30 | const { name } = route.params; | 33 | const { name } = route.params; |
| 31 | const list = ref<appType[]>([]); | 34 | const list = ref<appType[]>([]); |
| 32 | const total = ref<number>(0); | 35 | const total = ref<number>(0); |
| @@ -36,7 +39,6 @@ const params = ref<any>({ | @@ -36,7 +39,6 @@ const params = ref<any>({ | ||
| 36 | typeAlias: name as string, | 39 | typeAlias: name as string, |
| 37 | }); | 40 | }); |
| 38 | 41 | ||
| 39 | -// 返回分类名称 | ||
| 40 | function findLabelByAlias< | 42 | function findLabelByAlias< |
| 41 | T extends { alias?: string; label?: string; children?: T[] } | 43 | T extends { alias?: string; label?: string; children?: T[] } |
| 42 | >(alias: string, data: T[], childrenKey: string = "children"): string { | 44 | >(alias: string, data: T[], childrenKey: string = "children"): string { |
| @@ -44,14 +46,12 @@ function findLabelByAlias< | @@ -44,14 +46,12 @@ function findLabelByAlias< | ||
| 44 | return ""; | 46 | return ""; |
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | - // 1. 首先在当前层级查找 | ||
| 48 | for (const item of data) { | 49 | for (const item of data) { |
| 49 | if (item.alias === alias) { | 50 | if (item.alias === alias) { |
| 50 | - return item.label || ""; // 返回 label 或空字符串 | 51 | + return item.label || ""; |
| 51 | } | 52 | } |
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | - // 2. 如果当前层级没找到,递归查找子节点 | ||
| 55 | for (const item of data) { | 55 | for (const item of data) { |
| 56 | const children = (item as any)[childrenKey] as T[]; | 56 | const children = (item as any)[childrenKey] as T[]; |
| 57 | if (children && children.length > 0) { | 57 | if (children && children.length > 0) { |
| @@ -65,6 +65,8 @@ function findLabelByAlias< | @@ -65,6 +65,8 @@ function findLabelByAlias< | ||
| 65 | return ""; | 65 | return ""; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | +const categoryLabel = findLabelByAlias(name as string, sortList.value); | ||
| 69 | + | ||
| 68 | function onPageChange(pageNum: number) { | 70 | function onPageChange(pageNum: number) { |
| 69 | router.push({ | 71 | router.push({ |
| 70 | path: route.path + "/page/" + pageNum, | 72 | path: route.path + "/page/" + pageNum, |
| @@ -74,4 +76,72 @@ function onPageChange(pageNum: number) { | @@ -74,4 +76,72 @@ function onPageChange(pageNum: number) { | ||
| 74 | const res = await getAppList(params.value); | 76 | const res = await getAppList(params.value); |
| 75 | list.value = res.rows; | 77 | list.value = res.rows; |
| 76 | total.value = res.total; | 78 | total.value = res.total; |
| 79 | + | ||
| 80 | +useHead({ | ||
| 81 | + title: `${categoryLabel} - ${webSite.value.webname}`, | ||
| 82 | + meta: [ | ||
| 83 | + { | ||
| 84 | + name: "description", | ||
| 85 | + content: `${categoryLabel}分类下的AI工具推荐,精选优质${categoryLabel}相关AI工具,助您高效完成工作。`, | ||
| 86 | + }, | ||
| 87 | + { | ||
| 88 | + name: "keywords", | ||
| 89 | + content: `${categoryLabel},AI工具,${categoryLabel}AI,人工智能,${webSite.value.webkeywords}`, | ||
| 90 | + }, | ||
| 91 | + { name: "robots", content: "index, follow" }, | ||
| 92 | + { | ||
| 93 | + property: "og:title", | ||
| 94 | + content: `${categoryLabel} - ${webSite.value.webname}`, | ||
| 95 | + }, | ||
| 96 | + { | ||
| 97 | + property: "og:description", | ||
| 98 | + content: `${categoryLabel}分类下的AI工具推荐,精选优质${categoryLabel}相关AI工具。`, | ||
| 99 | + }, | ||
| 100 | + { property: "og:type", content: "website" }, | ||
| 101 | + { property: "og:url", content: config.public.baseUrl + route.fullPath }, | ||
| 102 | + { property: "og:site_name", content: webSite.value.webname }, | ||
| 103 | + ], | ||
| 104 | + link: [{ rel: "canonical", href: config.public.baseUrl + route.fullPath }], | ||
| 105 | + script: [ | ||
| 106 | + { | ||
| 107 | + type: "application/ld+json", | ||
| 108 | + children: JSON.stringify({ | ||
| 109 | + "@context": "https://schema.org", | ||
| 110 | + "@graph": [ | ||
| 111 | + { | ||
| 112 | + "@type": "CollectionPage", | ||
| 113 | + "@id": config.public.baseUrl + route.fullPath + "#collectionpage", | ||
| 114 | + url: config.public.baseUrl + route.fullPath, | ||
| 115 | + name: `${categoryLabel} - ${webSite.value.webname}`, | ||
| 116 | + description: `${categoryLabel}分类下的AI工具推荐`, | ||
| 117 | + isPartOf: { | ||
| 118 | + "@id": "https://aiboxgo.com/#website", | ||
| 119 | + }, | ||
| 120 | + about: { | ||
| 121 | + "@type": "Thing", | ||
| 122 | + name: categoryLabel, | ||
| 123 | + }, | ||
| 124 | + }, | ||
| 125 | + { | ||
| 126 | + "@type": "BreadcrumbList", | ||
| 127 | + "@id": config.public.baseUrl + route.fullPath + "#breadcrumb", | ||
| 128 | + itemListElement: [ | ||
| 129 | + { | ||
| 130 | + "@type": "ListItem", | ||
| 131 | + position: 1, | ||
| 132 | + name: "首页", | ||
| 133 | + item: "https://aiboxgo.com/", | ||
| 134 | + }, | ||
| 135 | + { | ||
| 136 | + "@type": "ListItem", | ||
| 137 | + position: 2, | ||
| 138 | + name: categoryLabel, | ||
| 139 | + }, | ||
| 140 | + ], | ||
| 141 | + }, | ||
| 142 | + ], | ||
| 143 | + }), | ||
| 144 | + }, | ||
| 145 | + ], | ||
| 146 | +}); | ||
| 77 | </script> | 147 | </script> |
| @@ -23,9 +23,12 @@ | @@ -23,9 +23,12 @@ | ||
| 23 | import type { appType } from "~/api/types/app"; | 23 | import type { appType } from "~/api/types/app"; |
| 24 | import { getAppList } from "~/api/app"; | 24 | import { getAppList } from "~/api/app"; |
| 25 | import type { classifyType } from "~/api/types/classify"; | 25 | import type { classifyType } from "~/api/types/classify"; |
| 26 | +import type { webSiteType } from "~/api/types/webSite"; | ||
| 26 | const sortList = useState<classifyType[]>("sortTree"); | 27 | const sortList = useState<classifyType[]>("sortTree"); |
| 28 | +const webSite = useState<webSiteType>("webSite"); | ||
| 27 | const route = useRoute(); | 29 | const route = useRoute(); |
| 28 | const router = useRouter(); | 30 | const router = useRouter(); |
| 31 | +const config = useRuntimeConfig(); | ||
| 29 | const { pageNum, name } = route.params; | 32 | const { pageNum, name } = route.params; |
| 30 | const list = ref<appType[]>([]); | 33 | const list = ref<appType[]>([]); |
| 31 | const total = ref<number>(0); | 34 | const total = ref<number>(0); |
| @@ -35,7 +38,6 @@ const params = ref<any>({ | @@ -35,7 +38,6 @@ const params = ref<any>({ | ||
| 35 | typeAlias: name as string, | 38 | typeAlias: name as string, |
| 36 | }); | 39 | }); |
| 37 | 40 | ||
| 38 | -// 返回分类名称 | ||
| 39 | function findLabelByAlias< | 41 | function findLabelByAlias< |
| 40 | T extends { alias?: string; label?: string; children?: T[] } | 42 | T extends { alias?: string; label?: string; children?: T[] } |
| 41 | >(alias: string, data: T[], childrenKey: string = "children"): string { | 43 | >(alias: string, data: T[], childrenKey: string = "children"): string { |
| @@ -43,14 +45,12 @@ function findLabelByAlias< | @@ -43,14 +45,12 @@ function findLabelByAlias< | ||
| 43 | return ""; | 45 | return ""; |
| 44 | } | 46 | } |
| 45 | 47 | ||
| 46 | - // 1. 首先在当前层级查找 | ||
| 47 | for (const item of data) { | 48 | for (const item of data) { |
| 48 | if (item.alias === alias) { | 49 | if (item.alias === alias) { |
| 49 | - return item.label || ""; // 返回 label 或空字符串 | 50 | + return item.label || ""; |
| 50 | } | 51 | } |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | - // 2. 如果当前层级没找到,递归查找子节点 | ||
| 54 | for (const item of data) { | 54 | for (const item of data) { |
| 55 | const children = (item as any)[childrenKey] as T[]; | 55 | const children = (item as any)[childrenKey] as T[]; |
| 56 | if (children && children.length > 0) { | 56 | if (children && children.length > 0) { |
| @@ -64,8 +64,9 @@ function findLabelByAlias< | @@ -64,8 +64,9 @@ function findLabelByAlias< | ||
| 64 | return ""; | 64 | return ""; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | +const categoryLabel = findLabelByAlias(name as string, sortList.value); | ||
| 68 | + | ||
| 67 | function onPageChange(pageNum: number) { | 69 | function onPageChange(pageNum: number) { |
| 68 | - console.log(route); | ||
| 69 | if (pageNum === 1) { | 70 | if (pageNum === 1) { |
| 70 | router.push({ | 71 | router.push({ |
| 71 | path: "/category/" + name, | 72 | path: "/category/" + name, |
| @@ -80,4 +81,31 @@ function onPageChange(pageNum: number) { | @@ -80,4 +81,31 @@ function onPageChange(pageNum: number) { | ||
| 80 | const res = await getAppList(params.value); | 81 | const res = await getAppList(params.value); |
| 81 | list.value = res.rows; | 82 | list.value = res.rows; |
| 82 | total.value = res.total; | 83 | total.value = res.total; |
| 84 | + | ||
| 85 | +useHead({ | ||
| 86 | + title: `${categoryLabel} - 第${pageNum}页 - ${webSite.value.webname}`, | ||
| 87 | + meta: [ | ||
| 88 | + { | ||
| 89 | + name: "description", | ||
| 90 | + content: `${categoryLabel}分类下的AI工具推荐第${pageNum}页,精选优质${categoryLabel}相关AI工具。`, | ||
| 91 | + }, | ||
| 92 | + { | ||
| 93 | + name: "keywords", | ||
| 94 | + content: `${categoryLabel},AI工具,${categoryLabel}AI,人工智能,第${pageNum}页`, | ||
| 95 | + }, | ||
| 96 | + { name: "robots", content: "index, follow" }, | ||
| 97 | + { | ||
| 98 | + property: "og:title", | ||
| 99 | + content: `${categoryLabel} - 第${pageNum}页 - ${webSite.value.webname}`, | ||
| 100 | + }, | ||
| 101 | + { | ||
| 102 | + property: "og:description", | ||
| 103 | + content: `${categoryLabel}分类下的AI工具推荐第${pageNum}页。`, | ||
| 104 | + }, | ||
| 105 | + { property: "og:type", content: "website" }, | ||
| 106 | + { property: "og:url", content: config.public.baseUrl + route.fullPath }, | ||
| 107 | + { property: "og:site_name", content: webSite.value.webname }, | ||
| 108 | + ], | ||
| 109 | + link: [{ rel: "canonical", href: config.public.baseUrl + route.fullPath }], | ||
| 110 | +}); | ||
| 83 | </script> | 111 | </script> |
| @@ -27,7 +27,6 @@ useHead({ | @@ -27,7 +27,6 @@ useHead({ | ||
| 27 | meta: [ | 27 | meta: [ |
| 28 | { name: "description", content: webSite.value.webdescription }, | 28 | { name: "description", content: webSite.value.webdescription }, |
| 29 | { name: "keywords", content: webSite.value.webkeywords }, | 29 | { name: "keywords", content: webSite.value.webkeywords }, |
| 30 | - | ||
| 31 | { name: "format-detection", content: "telephone=no" }, | 30 | { name: "format-detection", content: "telephone=no" }, |
| 32 | { name: "referrer", content: "origin-when-cross-origin" }, | 31 | { name: "referrer", content: "origin-when-cross-origin" }, |
| 33 | { | 32 | { |
| @@ -45,7 +44,6 @@ useHead({ | @@ -45,7 +44,6 @@ useHead({ | ||
| 45 | content: | 44 | content: |
| 46 | "专业 AI 工具导航网站,汇集全网优质 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 资源,让你快速找到好用的 AI 工具。", | 45 | "专业 AI 工具导航网站,汇集全网优质 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 资源,让你快速找到好用的 AI 工具。", |
| 47 | }, | 46 | }, |
| 48 | - // { property: 'og:image', content: 'https://aiboxgo.com/images/logo.png'}, | ||
| 49 | { property: "og:url", content: "https://aiboxgo.com/" }, | 47 | { property: "og:url", content: "https://aiboxgo.com/" }, |
| 50 | { property: "og:site_name", content: "Annie网站" }, | 48 | { property: "og:site_name", content: "Annie网站" }, |
| 51 | { property: "og:type", content: "website" }, | 49 | { property: "og:type", content: "website" }, |
| @@ -60,6 +58,56 @@ useHead({ | @@ -60,6 +58,56 @@ useHead({ | ||
| 60 | "专业 AI 工具导航网站,汇集全网优质 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 资源,让你快速找到好用的 AI 工具。", | 58 | "专业 AI 工具导航网站,汇集全网优质 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 资源,让你快速找到好用的 AI 工具。", |
| 61 | }, | 59 | }, |
| 62 | ], | 60 | ], |
| 61 | + script: [ | ||
| 62 | + { | ||
| 63 | + type: "application/ld+json", | ||
| 64 | + children: JSON.stringify({ | ||
| 65 | + "@context": "https://schema.org", | ||
| 66 | + "@graph": [ | ||
| 67 | + { | ||
| 68 | + "@type": "WebSite", | ||
| 69 | + "@id": "https://aiboxgo.com/#website", | ||
| 70 | + "url": "https://aiboxgo.com/", | ||
| 71 | + "name": webSite.value.webname, | ||
| 72 | + "description": webSite.value.webdescription, | ||
| 73 | + "inLanguage": "zh-CN", | ||
| 74 | + "potentialAction": { | ||
| 75 | + "@type": "SearchAction", | ||
| 76 | + "target": { | ||
| 77 | + "@type": "EntryPoint", | ||
| 78 | + "urlTemplate": "https://aiboxgo.com/search?keyword={search_term_string}" | ||
| 79 | + }, | ||
| 80 | + "query-input": "required name=search_term_string" | ||
| 81 | + } | ||
| 82 | + }, | ||
| 83 | + { | ||
| 84 | + "@type": "Organization", | ||
| 85 | + "@id": "https://aiboxgo.com/#organization", | ||
| 86 | + "name": webSite.value.webname, | ||
| 87 | + "url": "https://aiboxgo.com/", | ||
| 88 | + "logo": { | ||
| 89 | + "@type": "ImageObject", | ||
| 90 | + "url": "https://aiboxgo.com/favicon.ico" | ||
| 91 | + }, | ||
| 92 | + "sameAs": [] | ||
| 93 | + }, | ||
| 94 | + { | ||
| 95 | + "@type": "CollectionPage", | ||
| 96 | + "@id": "https://aiboxgo.com/", | ||
| 97 | + "url": "https://aiboxgo.com/", | ||
| 98 | + "name": webSite.value.webname, | ||
| 99 | + "description": webSite.value.webdescription, | ||
| 100 | + "isPartOf": { | ||
| 101 | + "@id": "https://aiboxgo.com/#website" | ||
| 102 | + }, | ||
| 103 | + "about": { | ||
| 104 | + "@id": "https://aiboxgo.com/#organization" | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + ] | ||
| 108 | + }) | ||
| 109 | + } | ||
| 110 | + ] | ||
| 63 | }); | 111 | }); |
| 64 | </script> | 112 | </script> |
| 65 | 113 |
| @@ -19,9 +19,12 @@ | @@ -19,9 +19,12 @@ | ||
| 19 | <script lang="ts" setup> | 19 | <script lang="ts" setup> |
| 20 | import type { appType } from "~/api/types/app"; | 20 | import type { appType } from "~/api/types/app"; |
| 21 | import { getAppList } from "~/api/app"; | 21 | import { getAppList } from "~/api/app"; |
| 22 | +import type { webSiteType } from "~/api/types/webSite"; | ||
| 22 | 23 | ||
| 23 | const route = useRoute(); | 24 | const route = useRoute(); |
| 24 | const router = useRouter(); | 25 | const router = useRouter(); |
| 26 | +const config = useRuntimeConfig(); | ||
| 27 | +const webSite = useState<webSiteType>("webSite"); | ||
| 25 | 28 | ||
| 26 | const { keyword } = route.query as { keyword: string }; | 29 | const { keyword } = route.query as { keyword: string }; |
| 27 | const list = ref<appType[]>([]); | 30 | const list = ref<appType[]>([]); |
| @@ -44,4 +47,21 @@ function onPageChange(pageNum: number) { | @@ -44,4 +47,21 @@ function onPageChange(pageNum: number) { | ||
| 44 | const res = await getAppList(params.value); | 47 | const res = await getAppList(params.value); |
| 45 | list.value = res.rows; | 48 | list.value = res.rows; |
| 46 | total.value = res.total; | 49 | total.value = res.total; |
| 50 | + | ||
| 51 | +useHead({ | ||
| 52 | + title: `"${keyword}" 搜索结果 - ${webSite.value.webname}`, | ||
| 53 | + meta: [ | ||
| 54 | + { name: "description", content: `搜索"${keyword}"相关的AI工具,找到${total.value}个结果。${webSite.value.webdescription}` }, | ||
| 55 | + { name: "keywords", content: `${keyword},AI工具搜索,${webSite.value.webkeywords}` }, | ||
| 56 | + { name: "robots", content: "noindex, follow" }, | ||
| 57 | + { property: "og:title", content: `"${keyword}" 搜索结果 - ${webSite.value.webname}` }, | ||
| 58 | + { property: "og:description", content: `搜索"${keyword}"相关的AI工具结果页面。` }, | ||
| 59 | + { property: "og:type", content: "website" }, | ||
| 60 | + { property: "og:url", content: config.public.baseUrl + route.fullPath }, | ||
| 61 | + { property: "og:site_name", content: webSite.value.webname }, | ||
| 62 | + ], | ||
| 63 | + link: [ | ||
| 64 | + { rel: "canonical", href: config.public.baseUrl + route.fullPath } | ||
| 65 | + ] | ||
| 66 | +}); | ||
| 47 | </script> | 67 | </script> |
| @@ -22,8 +22,11 @@ | @@ -22,8 +22,11 @@ | ||
| 22 | <script lang="ts" setup> | 22 | <script lang="ts" setup> |
| 23 | import type { appType } from "~/api/types/app"; | 23 | import type { appType } from "~/api/types/app"; |
| 24 | import { getAppList } from "~/api/app"; | 24 | import { getAppList } from "~/api/app"; |
| 25 | +import type { webSiteType } from "~/api/types/webSite"; | ||
| 25 | const route = useRoute(); | 26 | const route = useRoute(); |
| 26 | const router = useRouter(); | 27 | const router = useRouter(); |
| 28 | +const config = useRuntimeConfig(); | ||
| 29 | +const webSite = useState<webSiteType>("webSite"); | ||
| 27 | const { pageNum } = route.params; | 30 | const { pageNum } = route.params; |
| 28 | const { keyword } = route.query as { keyword: string }; | 31 | const { keyword } = route.query as { keyword: string }; |
| 29 | const list = ref<appType[]>([]); | 32 | const list = ref<appType[]>([]); |
| @@ -54,4 +57,21 @@ function onPageChange(pageNum: number) { | @@ -54,4 +57,21 @@ function onPageChange(pageNum: number) { | ||
| 54 | const res = await getAppList(params.value); | 57 | const res = await getAppList(params.value); |
| 55 | list.value = res.rows; | 58 | list.value = res.rows; |
| 56 | total.value = res.total; | 59 | total.value = res.total; |
| 60 | + | ||
| 61 | +useHead({ | ||
| 62 | + title: `"${keyword}" 搜索结果 - 第${pageNum}页 - ${webSite.value.webname}`, | ||
| 63 | + meta: [ | ||
| 64 | + { name: "description", content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。` }, | ||
| 65 | + { name: "keywords", content: `${keyword},AI工具搜索,第${pageNum}页` }, | ||
| 66 | + { name: "robots", content: "noindex, follow" }, | ||
| 67 | + { property: "og:title", content: `"${keyword}" 搜索结果 - 第${pageNum}页 - ${webSite.value.webname}` }, | ||
| 68 | + { property: "og:description", content: `搜索"${keyword}"相关的AI工具第${pageNum}页结果。` }, | ||
| 69 | + { property: "og:type", content: "website" }, | ||
| 70 | + { property: "og:url", content: config.public.baseUrl + route.fullPath }, | ||
| 71 | + { property: "og:site_name", content: webSite.value.webname }, | ||
| 72 | + ], | ||
| 73 | + link: [ | ||
| 74 | + { rel: "canonical", href: config.public.baseUrl + route.fullPath } | ||
| 75 | + ] | ||
| 76 | +}); | ||
| 57 | </script> | 77 | </script> |
| @@ -58,6 +58,13 @@ useHead({ | @@ -58,6 +58,13 @@ useHead({ | ||
| 58 | meta: [ | 58 | meta: [ |
| 59 | { name: "description", content: DetailData.value.description }, | 59 | { name: "description", content: DetailData.value.description }, |
| 60 | { | 60 | { |
| 61 | + name: "keywords", | ||
| 62 | + content: `${DetailData.value.title},AI工具,${ | ||
| 63 | + DetailData.value.types?.map((t: any) => t.label).join(",") || "" | ||
| 64 | + }`, | ||
| 65 | + }, | ||
| 66 | + { name: "robots", content: "index, follow" }, | ||
| 67 | + { | ||
| 61 | property: "og:title", | 68 | property: "og:title", |
| 62 | content: `${DetailData.value.title}${ | 69 | content: `${DetailData.value.title}${ |
| 63 | DetailData.value.popupContent != null | 70 | DetailData.value.popupContent != null |
| @@ -81,6 +88,13 @@ useHead({ | @@ -81,6 +88,13 @@ useHead({ | ||
| 81 | property: "article:published_time", | 88 | property: "article:published_time", |
| 82 | content: DetailData.value.updateTime, | 89 | content: DetailData.value.updateTime, |
| 83 | }, | 90 | }, |
| 91 | + { property: "twitter:card", content: "summary_large_image" }, | ||
| 92 | + { property: "twitter:title", content: DetailData.value.title }, | ||
| 93 | + { property: "twitter:description", content: DetailData.value.description }, | ||
| 94 | + { | ||
| 95 | + property: "twitter:image", | ||
| 96 | + content: config.public.baseUrl + DetailData.value.image, | ||
| 97 | + }, | ||
| 84 | ], | 98 | ], |
| 85 | link: [ | 99 | link: [ |
| 86 | { | 100 | { |
| @@ -88,6 +102,77 @@ useHead({ | @@ -88,6 +102,77 @@ useHead({ | ||
| 88 | href: config.public.baseUrl + route.fullPath, | 102 | href: config.public.baseUrl + route.fullPath, |
| 89 | }, | 103 | }, |
| 90 | ], | 104 | ], |
| 105 | + script: [ | ||
| 106 | + { | ||
| 107 | + type: "application/ld+json", | ||
| 108 | + children: JSON.stringify({ | ||
| 109 | + "@context": "https://schema.org", | ||
| 110 | + "@graph": [ | ||
| 111 | + { | ||
| 112 | + "@type": "SoftwareApplication", | ||
| 113 | + "@id": config.public.baseUrl + route.fullPath + "#software", | ||
| 114 | + name: DetailData.value.title, | ||
| 115 | + description: DetailData.value.description, | ||
| 116 | + url: DetailData.value.link, | ||
| 117 | + image: config.public.baseUrl + DetailData.value.image, | ||
| 118 | + applicationCategory: "UtilitiesApplication", | ||
| 119 | + operatingSystem: "Web Browser", | ||
| 120 | + offers: { | ||
| 121 | + "@type": "Offer", | ||
| 122 | + price: "0", | ||
| 123 | + priceCurrency: "CNY", | ||
| 124 | + }, | ||
| 125 | + }, | ||
| 126 | + { | ||
| 127 | + "@type": "Article", | ||
| 128 | + "@id": config.public.baseUrl + route.fullPath + "#article", | ||
| 129 | + headline: DetailData.value.title, | ||
| 130 | + description: DetailData.value.description, | ||
| 131 | + image: config.public.baseUrl + DetailData.value.image, | ||
| 132 | + datePublished: DetailData.value.updateTime, | ||
| 133 | + dateModified: DetailData.value.updateTime, | ||
| 134 | + author: { | ||
| 135 | + "@id": "https://aiboxgo.com/#organization", | ||
| 136 | + }, | ||
| 137 | + publisher: { | ||
| 138 | + "@id": "https://aiboxgo.com/#organization", | ||
| 139 | + }, | ||
| 140 | + mainEntityOfPage: { | ||
| 141 | + "@type": "WebPage", | ||
| 142 | + "@id": config.public.baseUrl + route.fullPath, | ||
| 143 | + }, | ||
| 144 | + }, | ||
| 145 | + { | ||
| 146 | + "@type": "BreadcrumbList", | ||
| 147 | + "@id": config.public.baseUrl + route.fullPath + "#breadcrumb", | ||
| 148 | + itemListElement: [ | ||
| 149 | + { | ||
| 150 | + "@type": "ListItem", | ||
| 151 | + position: 1, | ||
| 152 | + name: "首页", | ||
| 153 | + item: "https://aiboxgo.com/", | ||
| 154 | + }, | ||
| 155 | + ...(DetailData.value.types?.[0] | ||
| 156 | + ? [ | ||
| 157 | + { | ||
| 158 | + "@type": "ListItem", | ||
| 159 | + position: 2, | ||
| 160 | + name: DetailData.value.types[0].label, | ||
| 161 | + item: `https://aiboxgo.com/category/${DetailData.value.types[0].alias}`, | ||
| 162 | + }, | ||
| 163 | + ] | ||
| 164 | + : []), | ||
| 165 | + { | ||
| 166 | + "@type": "ListItem", | ||
| 167 | + position: DetailData.value.types?.length ? 3 : 2, | ||
| 168 | + name: DetailData.value.title, | ||
| 169 | + }, | ||
| 170 | + ], | ||
| 171 | + }, | ||
| 172 | + ], | ||
| 173 | + }), | ||
| 174 | + }, | ||
| 175 | + ], | ||
| 91 | }); | 176 | }); |
| 92 | </script> | 177 | </script> |
| 93 | 178 | ||
| @@ -104,6 +189,7 @@ useHead({ | @@ -104,6 +189,7 @@ useHead({ | ||
| 104 | :src="config.public.apiUrl + DetailData.image" | 189 | :src="config.public.apiUrl + DetailData.image" |
| 105 | :alt="DetailData.title" | 190 | :alt="DetailData.title" |
| 106 | class="w-16 h-16 object-contain" | 191 | class="w-16 h-16 object-contain" |
| 192 | + loading="lazy" | ||
| 107 | /> | 193 | /> |
| 108 | <div> | 194 | <div> |
| 109 | <h1 class="text-2xl font-bold text-[#5961f9]"> | 195 | <h1 class="text-2xl font-bold text-[#5961f9]"> |
-
请 注册 或 登录 后发表评论