feat:修复移动端适配的部分问题

This commit is contained in:
liulong 2026-05-26 18:01:25 +08:00
parent 34c7cc4393
commit b7af8a9ce7
21 changed files with 527 additions and 136 deletions

View File

@ -1,4 +1,5 @@
<template> <template>
<div class="portal-footer-shell">
<footer class="site-footer"> <footer class="site-footer">
<div class="footer-main"> <div class="footer-main">
<div class="footer-main-inner"> <div class="footer-main-inner">
@ -74,6 +75,7 @@
</div> </div>
</div> </div>
</footer> </footer>
</div>
</template> </template>
<script> <script>
@ -88,6 +90,13 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
/* 门户统一页脚外壳(由 Main 布局固定在滚动区底部) */
.portal-footer-shell {
flex-shrink: 0;
width: 100%;
background: var(--page-footer-bg, #f0f7f2);
}
/* Figma 底部信息块:主区 #f0f7f2 padding 40/20/20版权条 #e2ede5 高 64px */ /* Figma 底部信息块:主区 #f0f7f2 padding 40/20/20版权条 #e2ede5 高 64px */
.site-footer { .site-footer {
display: block; display: block;

View File

@ -1,6 +1,10 @@
<template> <template>
<!-- 顶部菜单栏 --> <!-- 顶部菜单栏 -->
<div v-show="!isIframeEmbedded" class="nav-box"> <div
v-show="!isIframeEmbedded"
class="nav-box"
:class="{ 'nav-box--mobile-menu-open': mobileMenuOpen }"
>
<div class="nav-inner page-nav-inner"> <div class="nav-inner page-nav-inner">
<div class="logo-box" @click="goHome"> <div class="logo-box" @click="goHome">
<span class="logo-mark"> <span class="logo-mark">
@ -125,7 +129,6 @@
<div class="mobile-auth-btn logout" @click="logoutHandler">退出登录</div> <div class="mobile-auth-btn logout" @click="logoutHandler">退出登录</div>
</div> </div>
<div v-else class="mobile-auth-btns"> <div v-else class="mobile-auth-btns">
<div class="mobile-auth-btn activate">激活</div>
<div class="mobile-auth-btn login" @click="gologin">登录</div> <div class="mobile-auth-btn login" @click="gologin">登录</div>
</div> </div>
</div> </div>
@ -744,6 +747,12 @@ export default {
cursor: not-allowed !important; cursor: not-allowed !important;
} }
/* 抽屉已有关闭钮时,隐藏顶部汉堡(避免与 header × 叠成两个叉) */
.nav-box--mobile-menu-open .hamburger-btn {
visibility: hidden;
pointer-events: none;
}
/* 汉堡菜单按钮 */ /* 汉堡菜单按钮 */
.hamburger-btn { .hamburger-btn {
display: none; display: none;
@ -984,11 +993,6 @@ export default {
flex: 1; flex: 1;
} }
.mobile-auth-btn.activate {
color: #333;
background: #f0f0f0;
}
.mobile-auth-btn.logout { .mobile-auth-btn.logout {
color: #666; color: #666;
background: #fff; background: #fff;

View File

@ -29,6 +29,10 @@
box-sizing: border-box; box-sizing: border-box;
} }
.content-wrap--portal {
background: #f6f7fa;
}
.page-content-wrap, .page-content-wrap,
.page-nav-inner { .page-nav-inner {
box-sizing: border-box; box-sizing: border-box;
@ -344,7 +348,9 @@
.fwsc-page .banner-section, .fwsc-page .banner-section,
.fwsc-page .fwsc-main, .fwsc-page .fwsc-main,
.fwsc-container .fwsc-main, .fwsc-container .fwsc-main,
.fwsc-container .secondary-nav-content { .fwsc-container .secondary-nav-content,
.xqsc-container .xqsc-main,
.xqsc-container .secondary-nav-content {
max-width: 100%; max-width: 100%;
overflow-x: hidden; overflow-x: hidden;
box-sizing: border-box; box-sizing: border-box;
@ -365,7 +371,9 @@
.fwsc-page .card-actions span, .fwsc-page .card-actions span,
.fwsc-container .nav-tab, .fwsc-container .nav-tab,
.fwsc-container .publish-btn, .fwsc-container .publish-btn,
.fwsc-container .card-actions span { .fwsc-container .card-actions span,
.xqsc-container .nav-tab,
.xqsc-container .publish-btn {
min-height: 48px; min-height: 48px;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;

View File

@ -138,3 +138,56 @@
margin-left: calc(50% - 50vw); margin-left: calc(50% - 50vw);
margin-right: calc(50% - 50vw); margin-right: calc(50% - 50vw);
} }
// =============================================================================
// 门户内页:唯一滚动容器为 .content-wrap页脚为滚动内容最后一项
// =============================================================================
@media screen and (min-width: 768px) {
html,
body {
height: 100%;
overflow: hidden;
}
#app,
.portal-app-root {
height: 100%;
overflow: hidden;
}
}
.content-wrap--portal {
background: #f6f7fa;
}
/* 短页:栈高度至少铺满滚动区,页脚贴底;长页:随内容增高,不在页脚后留白 */
.portal-page-stack {
display: flex;
flex-direction: column;
box-sizing: border-box;
width: 100%;
min-height: 100%;
}
.portal-page-stack .portal-route-outlet {
flex: 1 1 auto;
width: 100%;
min-height: auto;
}
.portal-page-stack .portal-layout-footer {
flex-shrink: 0;
width: 100%;
}
.portal-route-outlet-wrap {
width: 100%;
min-height: 100%;
}
.portal-page-shell {
display: block;
box-sizing: border-box;
width: 100%;
}

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="fwsc-container fwsc-page"> <div class="fwsc-container fwsc-page portal-page-shell">
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
<!-- <BreadcrumbNav currentPage="碳服务市场" /> --> <!-- <BreadcrumbNav currentPage="碳服务市场" /> -->
@ -139,8 +139,6 @@
</div> </div>
</main> </main>
<Footer />
<!-- 发布服务抽屉 --> <!-- 发布服务抽屉 -->
<FwscPublish :visible.sync="publishVisible" @success="onPublishSuccess" /> <FwscPublish :visible.sync="publishVisible" @success="onPublishSuccess" />
@ -175,7 +173,6 @@
<script> <script>
import { LocationIcon, SearchIcon } from 'tdesign-icons-vue'; import { LocationIcon, SearchIcon } from 'tdesign-icons-vue';
import NewNav from '@/pages/index/components/new-nav/index.vue'; import NewNav from '@/pages/index/components/new-nav/index.vue';
import Footer from '@/pages/index/components/footer/index.vue';
import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue'; import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue';
import FwscPublish from './components/FwscPublish.vue'; import FwscPublish from './components/FwscPublish.vue';
import api from '@/pages/index/api/fwsc/index.js'; import api from '@/pages/index/api/fwsc/index.js';
@ -186,7 +183,6 @@ export default {
name: 'FwscPage', name: 'FwscPage',
components: { components: {
NewNav, NewNav,
Footer,
BreadcrumbNav, BreadcrumbNav,
FwscPublish, FwscPublish,
LocationIcon, LocationIcon,
@ -491,7 +487,6 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.fwsc-container { .fwsc-container {
min-height: 100vh;
background: #f5f5f5; background: #f5f5f5;
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="fwsc-page"> <div class="fwsc-page portal-page-shell">
<div class="main-content"> <div class="main-content">
<!-- 当前位置导航 --> <!-- 当前位置导航 -->
<!-- <div class="breadcrumb-box"> <!-- <div class="breadcrumb-box">
@ -89,19 +89,16 @@
</div> </div>
</div> </div>
<Footer />
</div> </div>
</template> </template>
<script> <script>
import NewNav from '@/pages/index/components/new-nav/index.vue'; import NewNav from '@/pages/index/components/new-nav/index.vue';
import Footer from '@/pages/index/components/footer/index.vue';
export default { export default {
name: 'FwscIndex', name: 'FwscIndex',
components: { components: {
NewNav, NewNav,
Footer
}, },
methods: { methods: {
goHome() { goHome() {
@ -120,7 +117,6 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.fwsc-page { .fwsc-page {
min-height: 100vh;
background: #f5f7fa; background: #f5f7fa;
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="jrsc-page"> <div class="jrsc-page portal-page-shell">
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
<!-- <BreadcrumbNav currentPage="碳金融市场" /> --> <!-- <BreadcrumbNav currentPage="碳金融市场" /> -->
@ -193,7 +193,6 @@
</div> </div>
</main> </main>
<Footer />
<!-- 产品详情弹窗 --> <!-- 产品详情弹窗 -->
<t-dialog <t-dialog
@ -326,14 +325,12 @@
<script> <script>
import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue'; import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue';
import Footer from '@/pages/index/components/footer/index.vue';
import api from '@/pages/index/api/fwsc/index.js'; import api from '@/pages/index/api/fwsc/index.js';
export default { export default {
name: 'JrscPage', name: 'JrscPage',
components: { components: {
BreadcrumbNav, BreadcrumbNav,
Footer,
}, },
data() { data() {
return { return {
@ -617,7 +614,6 @@ export default {
@bg-white: #fff; @bg-white: #fff;
.jrsc-page { .jrsc-page {
min-height: 100vh;
background: @bg-gray; background: @bg-gray;
animation: fadeIn 0.3s ease; animation: fadeIn 0.3s ease;
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="sjlbc-page"> <div class="sjlbc-page portal-page-shell">
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
<div class="breadcrumb-box"> <div class="breadcrumb-box">
<span class="breadcrumb-link" @click="$router.push('/view/mhzc/home')">首页</span> <span class="breadcrumb-link" @click="$router.push('/view/mhzc/home')">首页</span>
@ -31,11 +31,10 @@
<div class="data-list"> <div class="data-list">
<t-table <t-table
:data="sortedDataList" :data="sortedDataList"
:pagination="paginationConfig" :pagination="null"
row-key="uuid" row-key="uuid"
hover hover
:columns="tableColumns" :columns="tableColumns"
@page-change="handlePageChange"
> >
<template #td-type="{ row }"> <template #td-type="{ row }">
<span class="type-tag" :class="row.type === '公共数据' ? 'tag-green' : 'tag-blue'"> <span class="type-tag" :class="row.type === '公共数据' ? 'tag-green' : 'tag-blue'">
@ -58,6 +57,20 @@
<t-button size="small" theme="primary" variant="outline" @click="showContactDialog(row)">获取联系方式</t-button> <t-button size="small" theme="primary" variant="outline" @click="showContactDialog(row)">获取联系方式</t-button>
</template> </template>
</t-table> </t-table>
<div v-if="pagination.total > 0" class="sjlbc-pagination">
<div class="sjlbc-pagination-total"> {{ pagination.total }} 条数据</div>
<t-pagination
v-model="pagination.pageNo"
:total="pagination.total"
:page-size.sync="pagination.pageSize"
:total-content="false"
:show-jumper="!isMobile"
:show-page-size="!isMobile"
:page-size-options="[10, 20, 30, 50]"
align="center"
@change="handlePaginationChange"
/>
</div>
</div> </div>
</div> </div>
@ -88,22 +101,19 @@
</div> </div>
</t-dialog> </t-dialog>
<!-- 底部 -->
<Footer />
</div> </div>
</template> </template>
<script> <script>
import NewNav from '@/pages/index/components/new-nav/index.vue'; import NewNav from '@/pages/index/components/new-nav/index.vue';
import Footer from '@/pages/index/components/footer/index.vue';
import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue'; import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue';
import gxzxApi from '@/pages/index/api/gxzx/index.js'; import gxzxApi from '@/pages/index/api/gxzx/index.js';
import { isMobileViewport, watchDeviceClass } from '@/pages/index/utils/breakpoint';
export default { export default {
name: 'SjlbcPage', name: 'SjlbcPage',
components: { components: {
NewNav, NewNav,
Footer,
BreadcrumbNav, BreadcrumbNav,
}, },
data() { data() {
@ -155,19 +165,25 @@ export default {
title: '联系方式', title: '联系方式',
}, },
], ],
paginationConfig: { isMobile: false,
current: 1, unwatchDevice: null,
pageSize: 10,
total: 0,
showJumper: true,
},
}; };
}, },
mounted() { mounted() {
this.isMobile = isMobileViewport();
this.unwatchDevice = watchDeviceClass(() => {
this.isMobile = isMobileViewport();
});
this.sjscUuid = this.$route.query.id || ''; this.sjscUuid = this.$route.query.id || '';
this.loadSjscInfo(); this.loadSjscInfo();
this.loadData(); this.loadData();
}, },
beforeDestroy() {
if (this.unwatchDevice) {
this.unwatchDevice();
this.unwatchDevice = null;
}
},
computed: { computed: {
totalPages() { totalPages() {
return Math.ceil(this.pagination.total / this.pagination.pageSize) || 1; return Math.ceil(this.pagination.total / this.pagination.pageSize) || 1;
@ -244,9 +260,6 @@ export default {
jgDw: item.jgDw jgDw: item.jgDw
})); }));
this.pagination.total = Number(res.data.total); this.pagination.total = Number(res.data.total);
this.paginationConfig.total = Number(res.data.total);
this.paginationConfig.current = this.pagination.pageNo;
this.paginationConfig.pageSize = this.pagination.pageSize;
} }
}); });
}, },
@ -280,7 +293,7 @@ export default {
this.contactDialogVisible = true; this.contactDialogVisible = true;
}); });
}, },
handlePageChange(pageInfo) { handlePaginationChange(pageInfo) {
this.pagination.pageNo = pageInfo.current; this.pagination.pageNo = pageInfo.current;
this.pagination.pageSize = pageInfo.pageSize; this.pagination.pageSize = pageInfo.pageSize;
this.loadData(); this.loadData();
@ -291,7 +304,6 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.sjlbc-page { .sjlbc-page {
min-height: 100vh;
background: #f5f5f5; background: #f5f5f5;
} }
@ -487,4 +499,119 @@ export default {
overflow-x: auto; overflow-x: auto;
} }
} }
// hydt / xqsc
.sjlbc-pagination {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 20px 16px 24px;
border-top: 1px solid #f0f0f0;
}
.sjlbc-pagination-total {
flex: 0 0 auto;
font-size: 14px;
line-height: 22px;
color: #666;
white-space: nowrap;
}
.sjlbc-pagination ::v-deep .t-pagination {
flex: 1 1 auto;
justify-content: flex-end;
}
@media (max-width: 768px) {
.sjlbc-pagination {
flex-direction: column;
align-items: stretch;
gap: 12px;
padding: 16px 12px 28px;
}
.sjlbc-pagination-total {
width: 100%;
text-align: center;
font-size: 14px;
}
.sjlbc-pagination ::v-deep .t-pagination {
display: flex !important;
flex-wrap: wrap !important;
align-items: center;
justify-content: center !important;
width: 100% !important;
max-width: 100%;
row-gap: 12px;
column-gap: 8px;
}
.sjlbc-pagination ::v-deep .t-pagination__total {
display: none;
}
.sjlbc-pagination ::v-deep .t-pagination__btn-prev {
order: 1;
}
.sjlbc-pagination ::v-deep .t-pagination__pager {
order: 2;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 6px;
max-width: 100%;
}
.sjlbc-pagination ::v-deep .t-pagination__btn-next {
order: 3;
}
.sjlbc-pagination ::v-deep .t-pagination__jump {
display: none !important;
}
@sjlbc-mob-pagination-h: 36px;
.sjlbc-pagination ::v-deep .t-pagination__number {
min-width: @sjlbc-mob-pagination-h;
height: @sjlbc-mob-pagination-h;
line-height: @sjlbc-mob-pagination-h;
margin: 0;
box-sizing: border-box;
}
.sjlbc-pagination ::v-deep .t-pagination__btn {
min-width: @sjlbc-mob-pagination-h;
width: @sjlbc-mob-pagination-h;
height: @sjlbc-mob-pagination-h;
line-height: @sjlbc-mob-pagination-h;
}
}
@media (max-width: 480px) {
.sjlbc-pagination {
padding-bottom: 32px;
}
@sjlbc-mob-pagination-h-sm: 32px;
.sjlbc-pagination ::v-deep .t-pagination__number {
min-width: @sjlbc-mob-pagination-h-sm;
height: @sjlbc-mob-pagination-h-sm;
line-height: @sjlbc-mob-pagination-h-sm;
font-size: 14px;
}
.sjlbc-pagination ::v-deep .t-pagination__btn {
min-width: @sjlbc-mob-pagination-h-sm;
width: @sjlbc-mob-pagination-h-sm;
height: @sjlbc-mob-pagination-h-sm;
line-height: @sjlbc-mob-pagination-h-sm;
}
}
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="sjsc-page"> <div class="sjsc-page portal-page-shell">
<div> <div>
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
<!-- <BreadcrumbNav currentPage="碳数据市场" /> --> <!-- <BreadcrumbNav currentPage="碳数据市场" /> -->
@ -85,13 +85,11 @@
</div> </div>
</div> </div>
<Footer />
</div> </div>
</template> </template>
<script> <script>
import NewNav from '@/pages/index/components/new-nav/index.vue'; import NewNav from '@/pages/index/components/new-nav/index.vue';
import Footer from '@/pages/index/components/footer/index.vue';
import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue'; import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue';
import gxzxApi from '@/pages/index/api/gxzx/index.js'; import gxzxApi from '@/pages/index/api/gxzx/index.js';
@ -99,7 +97,6 @@ export default {
name: 'SjscPage', name: 'SjscPage',
components: { components: {
NewNav, NewNav,
Footer,
BreadcrumbNav, BreadcrumbNav,
}, },
data() { data() {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="xqsc-container"> <div class="xqsc-container portal-page-shell">
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
<!-- <BreadcrumbNav currentPage="碳需求市场" /> --> <!-- <BreadcrumbNav currentPage="碳需求市场" /> -->
@ -143,8 +143,6 @@
</div> </div>
</main> </main>
<Footer />
<!-- 发布需求抽屉 --> <!-- 发布需求抽屉 -->
<XqscPublish <XqscPublish
:visible.sync="publishVisible" :visible.sync="publishVisible"
@ -205,7 +203,6 @@
<script> <script>
import { SearchIcon } from 'tdesign-icons-vue'; import { SearchIcon } from 'tdesign-icons-vue';
import NewNav from '@/pages/index/components/new-nav/index.vue'; import NewNav from '@/pages/index/components/new-nav/index.vue';
import Footer from '@/pages/index/components/footer/index.vue';
import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue'; import BreadcrumbNav from '@/pages/index/components/breadcrumb/index.vue';
import XqscPublish from './components/XqscPublish.vue'; import XqscPublish from './components/XqscPublish.vue';
import api from '@/pages/index/api/fwsc/index.js'; import api from '@/pages/index/api/fwsc/index.js';
@ -215,7 +212,6 @@ export default {
name: 'XqscPage', name: 'XqscPage',
components: { components: {
NewNav, NewNav,
Footer,
BreadcrumbNav, BreadcrumbNav,
XqscPublish, XqscPublish,
SearchIcon, SearchIcon,
@ -452,7 +448,6 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.xqsc-container { .xqsc-container {
min-height: 100vh;
background: #f5f5f5; background: #f5f5f5;
} }
@ -960,32 +955,83 @@ export default {
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.xqsc-container {
overflow-x: hidden;
}
.xqsc-main { .xqsc-main {
padding: 12px; padding: 12px;
box-sizing: border-box;
} }
.secondary-nav-content { .secondary-nav-content {
flex-direction: column; flex-direction: column;
align-items: stretch;
padding: 12px 16px; padding: 12px 16px;
gap: 12px; gap: 12px;
box-sizing: border-box;
} }
.nav-tabs { .nav-tabs {
display: flex;
flex-wrap: nowrap;
align-items: center;
width: 100%; width: 100%;
max-width: 100%;
height: auto;
min-height: 42px;
padding-bottom: 4px; padding-bottom: 4px;
overflow-x: auto; overflow-x: auto;
gap: 4px; overflow-y: hidden;
gap: 8px;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
} }
.nav-tab { .nav-tab {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
height: auto;
min-height: 40px;
padding: 10px 12px; padding: 10px 12px;
font-size: 13px; font-size: 13px;
line-height: 1.2;
white-space: nowrap;
box-sizing: border-box;
}
.nav-right {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 8px;
width: 100%;
}
.list-count {
width: 100%;
font-size: 14px;
line-height: 22px;
text-align: center;
white-space: nowrap; white-space: nowrap;
} }
.publish-btn { .publish-btn {
width: 100%; width: 100%;
padding: 10px; height: auto;
min-height: 44px;
padding: 10px 16px;
font-size: 15px;
line-height: 1.3;
box-sizing: border-box;
} }
.content-area { .content-area {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="gxnlpt-page portal-page"> <div class="gxnlpt-page portal-page portal-page-shell">
<div <div
class="gxnlpt-shell page-content-wrap" class="gxnlpt-shell page-content-wrap"
:class="{ 'gxnlpt-shell--stacked': stackedNavLayout }" :class="{ 'gxnlpt-shell--stacked': stackedNavLayout }"
@ -230,12 +230,10 @@
</main> </main>
</div> </div>
</div> </div>
<Footer />
</div> </div>
</template> </template>
<script> <script>
import Footer from '@/pages/index/components/footer/index.vue';
import GxnlptCardTags from '@/pages/index/views/gxnlpt/components/GxnlptCardTags.vue'; import GxnlptCardTags from '@/pages/index/views/gxnlpt/components/GxnlptCardTags.vue';
import api from '@/pages/index/api/fwsc/index.js'; import api from '@/pages/index/api/fwsc/index.js';
import { import {
@ -433,7 +431,7 @@ const SUBMIT_FORM_EMPTY = () => ({
export default { export default {
name: 'GxnlptIndex', name: 'GxnlptIndex',
components: { Footer, GxnlptCardTags }, components: { GxnlptCardTags },
data() { data() {
return { return {
starOutline, starOutline,

View File

@ -36,10 +36,14 @@
搜索 搜索
</div> </div>
</div> </div>
<div class="top-search-hot"> <div class="top-search-hot" v-if="hotSearchTags.length > 0">
<div class="hot-label">热门搜索: </div> <div class="hot-label">热门搜索: </div>
<div class="hot-tag" v-for="(tag, index) in hotSearchTags" :key="index" @click="handleHotSearch(tag)">{{ <div
tag }}</div> class="hot-tag"
v-for="(tag, index) in hotSearchTags"
:key="index"
@click="handleHotSearch(tag)"
>{{ tag }}</div>
</div> </div>
</div> </div>
@ -332,6 +336,7 @@
import Footer from '@/pages/index/components/footer/index.vue'; import Footer from '@/pages/index/components/footer/index.vue';
import hydtApi from '@/pages/index/api/hydt'; import hydtApi from '@/pages/index/api/hydt';
import gxzxApi from '@/pages/index/api/gxzx/index.js'; import gxzxApi from '@/pages/index/api/gxzx/index.js';
import searchApi from '@/pages/index/api/search.js';
import { bindSectionWheelScroll } from '@/pages/index/utils/portal-scroll-mode'; import { bindSectionWheelScroll } from '@/pages/index/utils/portal-scroll-mode';
export default { export default {
@ -399,7 +404,7 @@ export default {
iconHeight: 183 iconHeight: 183
} }
], ],
hotSearchTags: ['电厂配额', '林业碳汇开发', 'CBAM 报告', '2025年度碳报告', '光伏发展'], hotSearchTags: [],
// //
overseas2List: [ overseas2List: [
{ {
@ -492,6 +497,7 @@ export default {
this.syncNewsTabFromRoute(this.$route.query.type); this.syncNewsTabFromRoute(this.$route.query.type);
this.syncBannerHeight(); this.syncBannerHeight();
window.addEventListener('resize', this.syncBannerHeight); window.addEventListener('resize', this.syncBannerHeight);
this.loadHotSearch();
this.fetchNewsData(); this.fetchNewsData();
this.getTfwzxUrl(); this.getTfwzxUrl();
this.$nextTick(() => { this.$nextTick(() => {
@ -720,9 +726,19 @@ export default {
this.$router.push({ path: '/search', query: { keyword: this.inputValue } }); this.$router.push({ path: '/search', query: { keyword: this.inputValue } });
} }
}, },
async loadHotSearch() {
try {
const res = await searchApi.getHotSearch();
if (res && res.data) {
this.hotSearchTags = res.data;
}
} catch (error) {
console.error('加载热门搜索失败', error);
}
},
// //
handleHotSearch(keyword) { handleHotSearch(keyword) {
this.$router.push({ path: '/search', query: { keyword: keyword } }); this.$router.push({ path: '/search', query: { keyword } });
}, },
// //
handleOverseasClick(item) { handleOverseasClick(item) {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="hydt-page portal-page"> <div class="hydt-page portal-page portal-page-shell">
<!-- Figma banner-bg 150622:19115背景 imageRef ec2e3dd --> <!-- Figma banner-bg 150622:19115背景 imageRef ec2e3dd -->
<section class="hydt-banner" aria-label="行业动态"> <section class="hydt-banner" aria-label="行业动态">
<div class="hydt-banner-bg" aria-hidden="true"></div> <div class="hydt-banner-bg" aria-hidden="true"></div>
@ -81,12 +81,10 @@
</div> </div>
</section> </section>
<Footer />
</div> </div>
</template> </template>
<script> <script>
import Footer from '@/pages/index/components/footer/index.vue';
import hydtApi from '@/pages/index/api/hydt/index.js'; import hydtApi from '@/pages/index/api/hydt/index.js';
const NEWS_TABS = [ const NEWS_TABS = [
@ -97,7 +95,7 @@ const NEWS_TABS = [
export default { export default {
name: 'HydtIndex', name: 'HydtIndex',
components: { Footer }, components: {},
data() { data() {
return { return {
newsTabs: NEWS_TABS, newsTabs: NEWS_TABS,

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="hyzt-wrap"> <div class="hyzt-wrap portal-page-shell">
<!-- Figma 150581:4101 banner-bg / 150679:365 hyzt_banner 1 --> <!-- Figma 150581:4101 banner-bg / 150679:365 hyzt_banner 1 -->
<section class="banner-section" aria-label="行业专题"> <section class="banner-section" aria-label="行业专题">
<div class="banner-bg" aria-hidden="true"></div> <div class="banner-bg" aria-hidden="true"></div>
@ -34,19 +34,15 @@
</div> </div>
</div> </div>
<Footer />
</div> </div>
</template> </template>
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import Footer from '@/pages/index/components/footer/index.vue';
export default { export default {
name: 'hyzt', name: 'hyzt',
components: { components: {},
Footer,
},
data() { data() {
return { return {
headerClassList: ['blue-header', 'green-header', 'purple-header'], headerClassList: ['blue-header', 'green-header', 'purple-header'],
@ -74,7 +70,6 @@ export default {
@import '../../styles/home-figma-variables.less'; @import '../../styles/home-figma-variables.less';
.hyzt-wrap { .hyzt-wrap {
min-height: 100%;
background: #f6f7fa; background: #f6f7fa;
} }

View File

@ -203,4 +203,14 @@
.copyright { .copyright {
display: none; display: none;
} }
}
@media screen and (max-width: 768px) {
.item-container {
width: 100%;
max-width: 100%;
margin-right: 0;
margin-left: 0;
box-sizing: border-box;
}
} }

View File

@ -27,10 +27,10 @@
<div class="login-content"> <div class="login-content">
<t-tabs class="loginTabs" v-model="loginTab" size="large"> <t-tabs class="loginTabs" v-model="loginTab" size="large">
<t-tab-panel value="account" label="账号登录" :destroyOnHide="false"> <t-tab-panel value="account" label="账号登录" :destroyOnHide="false">
<login v-if="type === 'login'" /> <login />
<zhuce v-if="type === 'created'" @zhucecg="typechange" />
</t-tab-panel> </t-tab-panel>
<t-tab-panel v-if="MH_REGISTRATION_ENABLED" value="register" label="注册" :destroyOnHide="false"> <t-tab-panel v-if="MH_REGISTRATION_ENABLED" value="register" label="注册" :destroyOnHide="false">
<zhuce @zhucecg="onRegisterSuccess" />
</t-tab-panel> </t-tab-panel>
<t-tab-panel value="qrcode" label="扫码登录" :destroyOnHide="false"> <t-tab-panel value="qrcode" label="扫码登录" :destroyOnHide="false">
<div class="qrcode-login"> <div class="qrcode-login">
@ -112,7 +112,10 @@ export default {
}, },
countDown: 0, countDown: 0,
captchaVerification:'', captchaVerification:'',
intervalTimer: null, intervalTimer: null,
pollTimer: null,
pollStopped: false,
pollToken: 0,
}; };
}, },
computed: { computed: {
@ -125,10 +128,37 @@ export default {
mounted() { mounted() {
this.Getqrcode(); this.Getqrcode();
}, },
beforeDestroy() { deactivated() {
this.stopPollLogin();
},
beforeDestroy() {
this.stopPollLogin();
clearInterval(this.intervalTimer); clearInterval(this.intervalTimer);
this.intervalTimer = null;
},
beforeRouteLeave(to, from, next) {
this.stopPollLogin();
next();
}, },
methods: { methods: {
stopPollLogin() {
this.pollStopped = true;
this.pollToken += 1;
if (this.pollTimer) {
clearTimeout(this.pollTimer);
this.pollTimer = null;
}
},
schedulePollLogin(delay = 2000) {
if (this.pollStopped) return;
if (this.pollTimer) {
clearTimeout(this.pollTimer);
}
this.pollTimer = setTimeout(() => {
this.pollTimer = null;
this.pollLoginResult();
}, delay);
},
clearQRCode() { clearQRCode() {
// PC // PC
if (this.$refs.qrcodePC) { if (this.$refs.qrcodePC) {
@ -193,6 +223,7 @@ export default {
this.delVisible=false; this.delVisible=false;
this.form.sjhm=''; this.form.sjhm='';
this.form.sms=''; this.form.sms='';
this.pollStopped = false;
this.pollLoginResult(); this.pollLoginResult();
}, },
async didBindPhone() { async didBindPhone() {
@ -234,48 +265,52 @@ export default {
console.log('res', data); console.log('res', data);
} catch (error) { } catch (error) {
console.error('数据加载失败', error); console.error('数据加载失败', error);
}finally{ } finally {
this.pollLoginResult(); if (!this.pollStopped) {
this.pollLoginResult();
}
} }
}, },
async pollLoginResult() { async pollLoginResult() {
try { if (this.pollStopped) return;
const { data } = await backresultlogin(this.reqId); const token = this.pollToken;
try {
if (data.status == 3) { const { data } = await backresultlogin(this.reqId);
// if (this.pollStopped || token !== this.pollToken) return;
// return
window.sessionStorage.setItem('sfdl', true); if (data.status == 3) {
window.location.href = `/view/mhzc/home`; this.stopPollLogin();
return; // window.sessionStorage.setItem('sfdl', true);
} else if (data.status == 4) { window.location.href = `/view/mhzc/home`;
// return;
console.log('登录状态为4停止轮询'); }
// if (data.status == 4) {
getVerify().then((res) => { this.stopPollLogin();
this.captchaVerification = res.data; getVerify().then((res) => {
this.delVisible=true; if (this.pollStopped) return;
}); this.captchaVerification = res.data;
return; // this.delVisible = true;
} else { });
// return;
setTimeout(() => { }
this.pollLoginResult(); this.schedulePollLogin(2000);
}, 2000); // 2 } catch (error) {
console.error('数据加载失败', error);
if (token === this.pollToken && !this.pollStopped) {
this.schedulePollLogin(1500);
}
} }
} catch (error) { },
console.error('数据加载失败', error);
//
//
setTimeout(() => {
this.pollLoginResult();
}, 1500);
}
},
typechange(value) { typechange(value) {
if (value) { if (value) {
this.type = 'login'; this.type = 'login';
} }
},
onRegisterSuccess(value) {
if (value) {
this.type = 'login';
this.loginTab = 'account';
}
}, },
generateQRCode() { generateQRCode() {
try { try {
@ -484,7 +519,86 @@ export default {
} }
.login-content { .login-content {
width: 100%;
max-width: 100%; max-width: 100%;
box-sizing: border-box;
}
.mobile-layout ::v-deep .loginTabs {
width: 100%;
.t-tabs__nav-wrap {
width: 100%;
}
.t-tabs__nav-item {
padding: 0 10px;
font-size: 14px;
}
.t-tabs__content {
width: 100%;
padding: 12px 0 0;
box-sizing: border-box;
}
}
.mobile-layout ::v-deep .field {
width: 100%;
margin-top: 16px;
box-sizing: border-box;
}
.mobile-layout ::v-deep .item-container,
.mobile-layout ::v-deep form.login-box {
width: 100% !important;
max-width: 100%;
margin: 0 !important;
transform: none !important;
box-sizing: border-box;
}
.mobile-layout ::v-deep .t-form,
.mobile-layout ::v-deep .t-form__item,
.mobile-layout ::v-deep .t-form__controls,
.mobile-layout ::v-deep .t-form__controls-content {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.mobile-layout ::v-deep .t-input__wrap,
.mobile-layout ::v-deep .t-input {
width: 100% !important;
max-width: 100%;
box-sizing: border-box;
}
.mobile-layout ::v-deep .captcha-wrapper {
display: flex;
align-items: center;
gap: 8px;
width: 100% !important;
max-width: 100%;
height: auto;
box-sizing: border-box;
}
.mobile-layout ::v-deep .captcha-wrapper .t-input__wrap {
flex: 1;
min-width: 0;
width: auto !important;
}
.mobile-layout ::v-deep .captcha-img {
flex-shrink: 0;
width: 100px;
height: 40px;
}
.mobile-layout ::v-deep .btn-container {
width: 100%;
margin-top: 16px;
} }
.qrcode-login { .qrcode-login {

View File

@ -2,17 +2,21 @@
<div> <div>
<Nav @gotoIfreamPage="gotoIfreamPage" @gotoPage="gotoPage" /> <Nav @gotoIfreamPage="gotoIfreamPage" @gotoPage="gotoPage" />
<div ref="contentWrap" class="content-wrap" :class="contentWrapClass" :style="contentWrapStyle"> <div ref="contentWrap" class="content-wrap" :class="contentWrapClass" :style="contentWrapStyle">
<!-- <iframe v-if="iframeUrl" :src="iframeUrl" width="100%" height="100%" frameborder="0" scrolling="yes"> <div :class="showPortalFooter ? 'portal-page-stack' : 'portal-route-outlet-wrap'">
</iframe> --> <div class="portal-route-outlet">
<keep-alive> <keep-alive :exclude="keepAliveExclude">
<router-view @gotoIfreamPage="gotoIfreamPage" @gotoPage="gotoPage" /> <router-view @gotoIfreamPage="gotoIfreamPage" @gotoPage="gotoPage" />
</keep-alive> </keep-alive>
</div>
<Footer v-if="showPortalFooter" class="portal-layout-footer" />
</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import Nav from "@/pages/index/components/nav/index2.vue"; import Nav from "@/pages/index/components/nav/index2.vue";
import Footer from "@/pages/index/components/footer/index.vue";
import { hasLogin } from "@/pages/index/api/login"; import { hasLogin } from "@/pages/index/api/login";
import { isMobileViewport } from "@/pages/index/utils/breakpoint"; import { isMobileViewport } from "@/pages/index/utils/breakpoint";
@ -23,10 +27,13 @@ export default {
iframeUrl: '', iframeUrl: '',
documentClientHeight: 100, documentClientHeight: 100,
isMobileViewport: false, isMobileViewport: false,
//
keepAliveExclude: ['LoginIndex'],
}; };
}, },
components: { components: {
Nav, Nav,
Footer,
}, },
watch: { watch: {
$route() { $route() {
@ -38,10 +45,17 @@ export default {
isHomePage() { isHomePage() {
return (this.$route.path === '/home' || this.$route.path === '/') && !this.iframeUrl; return (this.$route.path === '/home' || this.$route.path === '/') && !this.iframeUrl;
}, },
isLoginPage() {
return this.$route.path === '/login' || this.$route.name === 'login';
},
showPortalFooter() {
return !this.isHomePage && !this.isLoginPage;
},
contentWrapClass() { contentWrapClass() {
return { return {
'content-wrap--mobile': this.isMobileViewport, 'content-wrap--mobile': this.isMobileViewport,
'content-wrap--home': this.isHomePage, 'content-wrap--home': this.isHomePage,
'content-wrap--portal': !this.isHomePage && !this.isLoginPage,
}; };
}, },
contentWrapStyle() { contentWrapStyle() {
@ -177,12 +191,17 @@ export default {
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
box-sizing: border-box; box-sizing: border-box;
overscroll-behavior-y: contain;
}
.content-wrap--portal {
overscroll-behavior-y: contain;
} }
.content-wrap--mobile { .content-wrap--mobile {
position: relative; position: relative;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
overscroll-behavior-y: auto; overscroll-behavior-y: contain;
} }
.content-wrap::-webkit-scrollbar { .content-wrap::-webkit-scrollbar {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="compliance-portal"> <div class="compliance-portal portal-page-shell">
<!-- 首屏标题区 + 三大专题入口 --> <!-- 首屏标题区 + 三大专题入口 -->
<div id="section-landing" class="qych-snap-section qych-landing"> <div id="section-landing" class="qych-snap-section qych-landing">
<div class="banner-section"> <div class="banner-section">
@ -264,21 +264,16 @@
</div> </div>
</div> </div>
</main> </main>
<Footer />
</div> </div>
</template> </template>
<script> <script>
import Footer from '@/pages/index/components/footer/index.vue'
import serviceCardIcon1 from '../../assets/qych/service-card-icon-1.png' import serviceCardIcon1 from '../../assets/qych/service-card-icon-1.png'
import serviceCardIcon2 from '../../assets/qych/service-card-icon-2.png' import serviceCardIcon2 from '../../assets/qych/service-card-icon-2.png'
export default { export default {
name: 'CompliancePortal', name: 'CompliancePortal',
components: { components: {},
Footer,
},
data() { data() {
return { return {
serviceCardIcon1, serviceCardIcon1,
@ -310,7 +305,7 @@ export default {
{ {
cardTitle: '航运燃料', cardTitle: '航运燃料',
cardDesc: 'LNG、绿甲醇、绿氨等多元低碳燃料转型政策与系统入口。', cardDesc: 'LNG、绿甲醇、绿氨等多元低碳燃料转型政策与系统入口。',
btnText: '进入交易大厅', btnText: '进入专题',
headerClass: 'purple-header', headerClass: 'purple-header',
sectionId: 'section2', sectionId: 'section2',
index: 2, index: 2,
@ -443,6 +438,21 @@ export default {
if (!scrollRoot) return; if (!scrollRoot) return;
const direction = e.deltaY > 0 ? 1 : -1; const direction = e.deltaY > 0 ? 1 : -1;
const maxScrollTop = scrollRoot.scrollHeight - scrollRoot.clientHeight;
const atBottom = maxScrollTop <= 0 || scrollRoot.scrollTop >= maxScrollTop - 12;
const atTop = scrollRoot.scrollTop <= 12;
//
if ((direction > 0 && atBottom) || (direction < 0 && atTop)) {
return;
}
const lastSectionEl = document.getElementById('section2');
if (lastSectionEl) {
const lastBottom = this.getSectionScrollTop(lastSectionEl, scrollRoot) + lastSectionEl.offsetHeight;
if (scrollRoot.scrollTop + scrollRoot.clientHeight * 0.35 > lastBottom) {
return;
}
}
const currentIndex = this.getCurrentSectionIndex(scrollRoot); const currentIndex = this.getCurrentSectionIndex(scrollRoot);
const currentEl = document.getElementById(this.sectionIds[currentIndex]); const currentEl = document.getElementById(this.sectionIds[currentIndex]);

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="search-page"> <div class="search-page portal-page-shell">
<!-- 搜索区域背景 --> <!-- 搜索区域背景 -->
<div class="search-hero"> <div class="search-hero">
<div class="hero-bg-overlay"></div> <div class="hero-bg-overlay"></div>
@ -157,13 +157,10 @@
</div> </div>
</div> </div>
<!-- 底部版权 -->
<Footer />
</div> </div>
</template> </template>
<script> <script>
import Footer from '@/pages/index/components/footer/index.vue';
import searchApi from '@/pages/index/api/search.js'; import searchApi from '@/pages/index/api/search.js';
export default { export default {
@ -373,7 +370,6 @@ export default {
<style scoped lang="less"> <style scoped lang="less">
.search-page { .search-page {
min-height: 100vh;
background-color: #f5f7fa; background-color: #f5f7fa;
} }

View File

@ -14,18 +14,16 @@
<div class="text-box">{{zxData.context}}</div> <div class="text-box">{{zxData.context}}</div>
</div> </div>
</div> </div>
<Footer />
</div> </div>
</template> </template>
<script> <script>
import Nav from '@/pages/index/components/nav/index.vue'; import Nav from '@/pages/index/components/nav/index.vue';
import Footer from '@/pages/index/components/footer/index.vue';
import api from '@/pages/index/api/gxzx/index.js'; import api from '@/pages/index/api/gxzx/index.js';
export default { export default {
name: 'HomePage', name: 'HomePage',
components: { Nav, Footer }, components: { Nav },
data() { data() {
return { return {
type: '国家政策', type: '国家政策',

View File

@ -107,7 +107,13 @@ public class SearchServiceImpl implements SearchService {
@Override @Override
public List<String> getHotSearchKeywords() { public List<String> getHotSearchKeywords() {
return Arrays.asList("碳达峰", "碳核查", "ESG", "碳资产管理", "ISO 14067"); return Arrays.asList(
"电厂配额",
"林业碳汇开发",
"CBAM 报告",
"2025年度碳报告",
"光伏发展"
);
} }
@Override @Override