diff --git a/.env.example b/.env.example index 01824b2..59a11b7 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ # Tenant / headers # - TenantId header will be sent on every request. # - Authorization header is taken from client storage (AccessToken) and forwarded by the Nuxt proxy. -NUXT_PUBLIC_TENANT_ID=10584 +NUXT_PUBLIC_TENANT_ID=10588 # Upstream APIs (required) NUXT_PUBLIC_SERVER_API_BASE=https://server.websoft.top/api diff --git a/app/app.vue b/app/app.vue index 12685a0..1f83ab1 100644 --- a/app/app.vue +++ b/app/app.vue @@ -9,8 +9,13 @@ diff --git a/app/assets/css/tailwind.css b/app/assets/css/tailwind.css index b5c61c9..28c377c 100644 --- a/app/assets/css/tailwind.css +++ b/app/assets/css/tailwind.css @@ -1,3 +1,62 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer base { + :root { + --gov-blue: #1e6fb5; + --gov-teal: #2ba69a; + + --text-primary: #333333; + --text-secondary: #4b5563; + --text-tertiary: #6b7280; + + --bg-page: #f7f9fc; + --bg-surface: #ffffff; + --border-subtle: rgba(0, 0, 0, 0.08); + } + + html { + color: var(--text-primary); + background: var(--bg-page); + font-size: 16px; + line-height: 1.6; + text-size-adjust: 100%; + font-family: "Source Han Sans SC", "Noto Sans SC", "Microsoft YaHei", "PingFang SC", system-ui, + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, + "Apple Color Emoji", "Segoe UI Emoji"; + } + + body { + margin: 0; + color: var(--text-primary); + background: var(--bg-page); + } + + a { + color: var(--gov-blue); + text-decoration-thickness: 1px; + text-underline-offset: 3px; + } + + a:hover { + color: #185d96; + } + + :focus-visible { + outline: 2px solid var(--gov-teal); + outline-offset: 2px; + } + + #main-content { + scroll-margin-top: 120px; + } + + html[data-contrast='high'] { + --text-primary: #0f172a; + --text-secondary: #111827; + --text-tertiary: #111827; + --bg-page: #ffffff; + --border-subtle: rgba(0, 0, 0, 0.2); + } +} diff --git a/app/components/Breadcrumbs.vue b/app/components/Breadcrumbs.vue new file mode 100644 index 0000000..647177e --- /dev/null +++ b/app/components/Breadcrumbs.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/app/components/Carousel.vue b/app/components/Carousel.vue index 7a34254..873e665 100644 --- a/app/components/Carousel.vue +++ b/app/components/Carousel.vue @@ -24,6 +24,13 @@ aria-label="carousel-slide" > + + - + @@ -71,6 +94,9 @@ type CarouselItem = { src: string href?: string alt?: string + title?: string + desc?: string + ctaLabel?: string } const props = withDefaults( @@ -123,7 +149,9 @@ function prev() { background: #fff; position: relative; overflow: hidden; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04); + border-radius: 14px; + border: 1px solid var(--border-subtle); + box-shadow: 0 14px 30px rgba(15, 23, 42, 0.08); } .carousel { @@ -137,6 +165,7 @@ function prev() { .carousel-link { display: block; height: 100%; + position: relative; } .carousel-img { @@ -146,6 +175,45 @@ function prev() { display: block; } +.carousel-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: flex-end; + padding: 18px; + background: linear-gradient(180deg, rgba(15, 23, 42, 0.08), rgba(15, 23, 42, 0.74)); +} + +.carousel-overlay-inner { + max-width: 860px; +} + +.carousel-title { + color: rgba(255, 255, 255, 0.98); + font-size: 28px; + line-height: 1.25; + font-weight: 900; + letter-spacing: 0.02em; +} + +.carousel-desc { + margin-top: 10px; + color: rgba(255, 255, 255, 0.9); + font-size: 14px; + line-height: 1.8; +} + +.carousel-cta { + display: inline-flex; + margin-top: 12px; + padding: 8px 12px; + border-radius: 10px; + background: rgba(30, 111, 181, 0.95); + color: rgba(255, 255, 255, 0.98); + font-weight: 700; + font-size: 13px; +} + .carousel :deep(.slick-list), .carousel :deep(.slick-track), .carousel :deep(.slick-slide), diff --git a/app/components/SectionStub.vue b/app/components/SectionStub.vue new file mode 100644 index 0000000..8a48463 --- /dev/null +++ b/app/components/SectionStub.vue @@ -0,0 +1,46 @@ + + + + diff --git a/app/components/SiteFooter.vue b/app/components/SiteFooter.vue index 9cc608a..6ce477a 100644 --- a/app/components/SiteFooter.vue +++ b/app/components/SiteFooter.vue @@ -3,35 +3,54 @@
-
关注我们
-
- -
- 小程序 -
- 获取最新动态 -
+
联系我们
+
+
咨询服务:服务简介
+
建议反馈:建言献策
+
联系方式:联系我们
快速入口
- 经营范围 - 联系我们 + 政策要闻 + 数据服务 + 专家申请 + 资料下载 + 站内搜索 + 站点地图
备案信息
-
{{ icpText }}
+
+
{{ icpText }}
+
{{ policeText }}
+
{{ siteCodeText }}
+
+
+
友情链接
+ +
+
© {{ year }} {{ siteName }}. All rights reserved.
+
+ 隐私政策 + 使用条款 + 免责声明 +
Powered by String((siteInfo.value as any)?.data?.websiteName || '桂乐淘')) +const siteName = computed(() => String((siteInfo.value as any)?.data?.websiteName || '广西决策咨询网')) const { data: icpField } = useAsyncData('cms-website-field-icpNo', async () => { try { @@ -62,6 +81,22 @@ const { data: icpField } = useAsyncData('cms-website-field-icpNo', async () => { } }) +const { data: policeField } = useAsyncData('cms-website-field-policeNo', async () => { + try { + return await getCmsWebsiteFieldByCode('policeNo') + } catch { + return null + } +}) + +const { data: siteCodeField } = useAsyncData('cms-website-field-siteCode', async () => { + try { + return await getCmsWebsiteFieldByCode('siteCode') + } catch { + return null + } +}) + const icpNo = computed(() => { const v = icpField.value?.value ?? icpField.value?.defaultValue if (typeof v === 'string' && v.trim()) return v.trim() @@ -69,13 +104,36 @@ const icpNo = computed(() => { return typeof fallback === 'string' ? fallback.trim() : '' }) -const icpText = computed(() => (icpNo.value ? `备案号:${icpNo.value}` : '备案号:')) +const policeNo = computed(() => { + const v = policeField.value?.value ?? policeField.value?.defaultValue + if (typeof v === 'string' && v.trim()) return v.trim() + const fallback = (siteInfo.value as any)?.data?.policeNo + return typeof fallback === 'string' ? fallback.trim() : '' +}) + +const siteCode = computed(() => { + const v = siteCodeField.value?.value ?? siteCodeField.value?.defaultValue + if (typeof v === 'string' && v.trim()) return v.trim() + const fallback = (siteInfo.value as any)?.data?.siteCode + return typeof fallback === 'string' ? fallback.trim() : '' +}) + +const icpText = computed(() => (icpNo.value ? `桂 ICP 备:${icpNo.value}` : '桂 ICP 备:')) +const policeText = computed(() => (policeNo.value ? `桂公网安备:${policeNo.value}` : '桂公网安备:')) +const siteCodeText = computed(() => (siteCode.value ? `网站标识码:${siteCode.value}` : '网站标识码:')) const year = new Date().getFullYear() + +const friendLinks = [ + { name: '广西壮族自治区人民政府', url: 'https://www.gxzf.gov.cn' }, + { name: '国务院发展研究中心', url: 'https://www.drc.gov.cn' }, + { name: '中国社会科学院', url: 'https://www.cass.cn' }, + { name: '广西社科联', url: 'http://www.gxskl.gov.cn' } +] diff --git a/app/components/SiteHeader.vue b/app/components/SiteHeader.vue index c83f6bb..fb22dd1 100644 --- a/app/components/SiteHeader.vue +++ b/app/components/SiteHeader.vue @@ -1,50 +1,71 @@