7.31更新

This commit is contained in:
hrx 2026-07-31 16:09:44 +08:00
parent c1bacfd965
commit 08cc6e012b
4 changed files with 66 additions and 77 deletions

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="contract-case-page" :class="{ 'has-fixed-tabs': isTabsFixed }"> <div class="contract-case-page">
<div class="decor decor--blue"></div> <div class="decor decor--blue"></div>
<div class="decor decor--purple"></div> <div class="decor decor--purple"></div>
@ -14,7 +14,7 @@
</div> </div>
</section> </section>
<section ref="caseTabs" class="case-tabs" :class="{ 'is-fixed': isTabsFixed }"> <section class="case-tabs">
<div class="case-container"> <div class="case-container">
<button <button
v-for="tab in tabs" v-for="tab in tabs"
@ -237,7 +237,6 @@
</section> </section>
</main> </main>
</div> </div>
</template> </template>
@ -247,8 +246,6 @@ export default {
data() { data() {
return { return {
activeTab: 'scenarios', activeTab: 'scenarios',
isTabsFixed: false,
tabsOriginTop: 0,
bannerImg: require('@/assets/image/bgimg.png'), bannerImg: require('@/assets/image/bgimg.png'),
tabs: [ tabs: [
{ id: 'scenarios', label: '应用场景' }, { id: 'scenarios', label: '应用场景' },
@ -338,7 +335,6 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
const scroller = this.getScrollContainer() const scroller = this.getScrollContainer()
this.resetScrollTop(scroller) this.resetScrollTop(scroller)
this.refreshTabsOrigin()
scroller.addEventListener('scroll', this.handlePageScroll, { passive: true }) scroller.addEventListener('scroll', this.handlePageScroll, { passive: true })
this.handlePageScroll() this.handlePageScroll()
}) })
@ -356,15 +352,6 @@ export default {
} else { } else {
scroller.scrollTop = 0 scroller.scrollTop = 0
} }
this.isTabsFixed = false
},
refreshTabsOrigin() {
const scroller = this.getScrollContainer()
this.tabsOriginTop = this.getElementTopInScroller(this.$refs.caseTabs, scroller)
},
getScrollOffset() {
const tabs = this.$el.querySelector('.case-tabs')
return (tabs ? tabs.offsetHeight : 0) + 12
}, },
getElementTopInScroller(element, scroller) { getElementTopInScroller(element, scroller) {
if (scroller === window) { if (scroller === window) {
@ -380,14 +367,13 @@ export default {
const element = this.$el.querySelector(`#${sectionName}`) const element = this.$el.querySelector(`#${sectionName}`)
if (!element) return if (!element) return
const scroller = this.getScrollContainer() const scroller = this.getScrollContainer()
const top = this.getElementTopInScroller(element, scroller) - this.getScrollOffset() const top = this.getElementTopInScroller(element, scroller)
scroller.scrollTo({ top, behavior: 'smooth' }) scroller.scrollTo({ top, behavior: 'smooth' })
}, },
handlePageScroll() { handlePageScroll() {
const scroller = this.getScrollContainer() const scroller = this.getScrollContainer()
const scrollerTop = this.getScrollerTop(scroller) const scrollerTop = this.getScrollerTop(scroller)
this.isTabsFixed = scrollerTop >= this.tabsOriginTop const offsetTop = scrollerTop + 24
const offsetTop = scrollerTop + this.getScrollOffset() + 24
const currentTab = this.tabs const currentTab = this.tabs
.map(tab => { .map(tab => {
const element = this.$el.querySelector(`#${tab.id}`) const element = this.$el.querySelector(`#${tab.id}`)
@ -493,27 +479,12 @@ export default {
} }
.case-tabs { .case-tabs {
position: sticky;
top: 0;
z-index: 80;
height: 60px; height: 60px;
background: #fff; background: #fff;
border-bottom: 1px solid #e5e7eb; border-bottom: 1px solid #e5e7eb;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06); box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
} }
.case-tabs.is-fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
}
.contract-case-page.has-fixed-tabs .case-main {
padding-top: 60px;
}
.case-tabs .case-container { .case-tabs .case-container {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -1,12 +1,12 @@
<template> <template>
<div class="decision-case-page" :class="{ 'has-fixed-tabs': isTabsFixed }"> <div class="decision-case-page">
<div class="decor decor--blue"></div> <div class="decor decor--blue"></div>
<div class="decor decor--purple"></div> <div class="decor decor--purple"></div>
<div class="decor decor--deep-blue"></div> <div class="decor decor--deep-blue"></div>
<div class="decor decor--deep-purple"></div> <div class="decor decor--deep-purple"></div>
<section class="case-hero"> <section class="case-hero">
<img class="case-hero__bg" :src="bannerImg" alt="" @load="refreshTabsOrigin"> <img class="case-hero__bg" :src="bannerImg" alt="">
<div class="case-container"> <div class="case-container">
<div class="case-hero__content"> <div class="case-hero__content">
<h1>投策智能体</h1> <h1>投策智能体</h1>
@ -17,7 +17,7 @@
</div> </div>
</section> </section>
<section ref="caseTabs" class="case-tabs" :class="{ 'is-fixed': isTabsFixed }"> <section class="case-tabs">
<div class="case-container"> <div class="case-container">
<button <button
v-for="tab in tabs" v-for="tab in tabs"
@ -156,8 +156,6 @@ export default {
data() { data() {
return { return {
activeTab: 'scenarios', activeTab: 'scenarios',
isTabsFixed: false,
tabsOriginTop: 0,
tabs: [ tabs: [
{ id: 'scenarios', label: '应用场景' }, { id: 'scenarios', label: '应用场景' },
{ id: 'solution', label: '解决方案' }, { id: 'solution', label: '解决方案' },
@ -238,7 +236,6 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
const scroller = this.getScrollContainer() const scroller = this.getScrollContainer()
this.resetScrollTop(scroller) this.resetScrollTop(scroller)
this.refreshTabsOrigin()
scroller.addEventListener('scroll', this.handlePageScroll, { passive: true }) scroller.addEventListener('scroll', this.handlePageScroll, { passive: true })
this.handlePageScroll() this.handlePageScroll()
}) })
@ -256,16 +253,6 @@ export default {
} else { } else {
scroller.scrollTop = 0 scroller.scrollTop = 0
} }
this.isTabsFixed = false
},
refreshTabsOrigin() {
const scroller = this.getScrollContainer()
this.tabsOriginTop = this.getElementTopInScroller(this.$refs.caseTabs, scroller)
},
getScrollOffset() {
const tabs = this.$el.querySelector('.case-tabs')
const tabsHeight = tabs ? tabs.offsetHeight : 0
return tabsHeight + 12
}, },
getElementTopInScroller(element, scroller) { getElementTopInScroller(element, scroller) {
if (scroller === window) { if (scroller === window) {
@ -281,16 +268,14 @@ export default {
const element = this.$el.querySelector(`#${sectionName}`) const element = this.$el.querySelector(`#${sectionName}`)
if (element) { if (element) {
const scroller = this.getScrollContainer() const scroller = this.getScrollContainer()
const offset = this.getScrollOffset() const top = this.getElementTopInScroller(element, scroller)
const top = this.getElementTopInScroller(element, scroller) - offset
scroller.scrollTo({ top, behavior: 'smooth' }) scroller.scrollTo({ top, behavior: 'smooth' })
} }
}, },
handlePageScroll() { handlePageScroll() {
const scroller = this.getScrollContainer() const scroller = this.getScrollContainer()
const scrollerTop = this.getScrollerTop(scroller) const scrollerTop = this.getScrollerTop(scroller)
this.isTabsFixed = scrollerTop >= this.tabsOriginTop const offsetTop = scrollerTop + 24
const offsetTop = scrollerTop + this.getScrollOffset() + 24
const currentTab = this.tabs const currentTab = this.tabs
.map(tab => { .map(tab => {
const element = this.$el.querySelector(`#${tab.id}`) const element = this.$el.querySelector(`#${tab.id}`)
@ -441,27 +426,12 @@ export default {
} }
.case-tabs { .case-tabs {
position: sticky;
top: 0;
z-index: 80;
background: #fff; background: #fff;
height: 60px; height: 60px;
border-bottom: 1px solid #e5e7eb; border-bottom: 1px solid #e5e7eb;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06); box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
} }
.case-tabs.is-fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
}
.decision-case-page.has-fixed-tabs .case-main {
padding-top: 60px;
}
.case-tabs .case-container { .case-tabs .case-container {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -8,7 +8,7 @@
<section class="home-hero"> <section class="home-hero">
<div class="home-hero__inner animate-in"> <div class="home-hero__inner animate-in">
<div class="hero-title-row"> <div class="hero-title-row">
<h1>{{ $t('home.heroTitle') }}</h1> <h1 :class="{ 'home-hero__title--english': isEnglish }">{{ $t('home.heroTitle') }}</h1>
</div> </div>
<h2 v-if="$t('home.heroSubtitle')">{{ $t('home.heroSubtitle') }}</h2> <h2 v-if="$t('home.heroSubtitle')">{{ $t('home.heroSubtitle') }}</h2>
<!-- <p>{{ $t('home.heroSlogan') }}</p> --> <!-- <p>{{ $t('home.heroSlogan') }}</p> -->
@ -339,6 +339,9 @@ export default {
showTalkVisible() { showTalkVisible() {
return this.$store.state.product.showTalk return this.$store.state.product.showTalk
}, },
isEnglish() {
return this.$i18n && this.$i18n.locale === 'en-US'
},
localizedSolutionCards() { localizedSolutionCards() {
const keyMap = { const keyMap = {
'群智协作': 'collaboration', '群智协作': 'collaboration',
@ -659,6 +662,12 @@ body.dark-theme .bg-orb {
letter-spacing: 0.02em; letter-spacing: 0.02em;
} }
.home-hero__title--english {
font-size: clamp(40px, 5vw, 58px);
letter-spacing: 0.01em;
white-space: nowrap;
}
h2 { h2 {
margin: 20px 0 0; margin: 20px 0 0;
font-size: 72px; font-size: 72px;
@ -690,6 +699,13 @@ body.dark-theme .bg-orb {
flex-wrap: wrap; flex-wrap: wrap;
} }
@media (max-width: 768px) {
.home-hero__inner .home-hero__title--english {
font-size: clamp(32px, 9vw, 48px);
white-space: normal;
}
}
.hero-actions { .hero-actions {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -123,7 +123,7 @@
<el-input v-model.trim="english.title" placeholder="Enter article title" /> <el-input v-model.trim="english.title" placeholder="Enter article title" />
</div> </div>
<div class="language-field language-field--wide"> <div class="language-field language-field--wide">
<label>English Summary</label> <label>Intro</label>
<el-input <el-input
v-model.trim="english.summary" v-model.trim="english.summary"
type="textarea" type="textarea"
@ -172,7 +172,7 @@
<div class="language-richtext"> <div class="language-richtext">
<div class="language-richtext__head"> <div class="language-richtext__head">
<span>English Content</span> <span>English Content</span>
<small>Rich text editor</small>
</div> </div>
<div v-if="dialogVisible" class="wang-editor-wrap wang-editor-wrap--english"> <div v-if="dialogVisible" class="wang-editor-wrap wang-editor-wrap--english">
<Toolbar <Toolbar
@ -220,7 +220,7 @@
<div slot="footer" class="article-editor-footer"> <div slot="footer" class="article-editor-footer">
<span class="editor-status">{{ dirty ? '未保存' : '已同步' }}</span> <span class="editor-status">{{ dirty ? '未保存' : '已同步' }}</span>
<div> <div class="editor-footer-buttons">
<el-button size="small" @click="requestClose">取消</el-button> <el-button size="small" @click="requestClose">取消</el-button>
<el-button size="small" @click="handleSave('0')">保存草稿</el-button> <el-button size="small" @click="handleSave('0')">保存草稿</el-button>
<el-button type="primary" size="small" @click="handlePublishConfirm">保存并发布</el-button> <el-button type="primary" size="small" @click="handlePublishConfirm">保存并发布</el-button>
@ -282,7 +282,7 @@ export default {
} }
}, },
englishEditorConfig: { englishEditorConfig: {
placeholder: 'Start writing the English version of your article...', placeholder: 'Please enter article content…',
MENU_CONF: { MENU_CONF: {
uploadImage: { uploadImage: {
customUpload: async(file, insertFn) => { customUpload: async(file, insertFn) => {
@ -705,13 +705,18 @@ export default {
margin-right: 4px; margin-right: 4px;
} }
::v-deep .article-editor-dialog { ::v-deep .article-editor-dialog {
--article-editor-radius: 8px;
margin: 0 !important; margin: 0 !important;
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-shrink: 0;
padding:0 24px!important;
padding-top: 16px;
border-bottom: 1px solid #edf0f5;
} }
::v-deep .article-editor-dialog .el-dialog__header { ::v-deep .el-dialog__header {
flex-shrink: 0; flex-shrink: 0;
padding: 16px 24px; padding: 16px 24px;
border-bottom: 1px solid #edf0f5; border-bottom: 1px solid #edf0f5;
@ -735,7 +740,7 @@ export default {
font-size: 13px; font-size: 13px;
background: #fff; background: #fff;
border: 1px solid #d8e0ec; border: 1px solid #d8e0ec;
border-radius: 999px; border-radius: var(--article-editor-radius);
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
} }
@ -754,11 +759,20 @@ export default {
} }
::v-deep .article-editor-dialog .el-dialog__footer { ::v-deep .article-editor-dialog .el-dialog__footer {
width: 100%;
height: 60px;
min-height: 60px;
flex-shrink: 0; flex-shrink: 0;
padding: 14px 24px; padding: 0 24px !important;
display: flex;
align-items: center;
box-sizing: border-box;
border-top: 1px solid #edf0f5; border-top: 1px solid #edf0f5;
} }
::v-deep .article-editor-dialog .el-button {
border-radius: var(--article-editor-radius);
}
.article-editor { .article-editor {
height: 100%; height: 100%;
display: flex; display: flex;
@ -1032,7 +1046,7 @@ export default {
color: #94a3b8; color: #94a3b8;
font-size: 12px; font-size: 12px;
background: #fff; background: #fff;
border: 1px solid #dcdfe6; /* border: 1px solid #dcdfe6; */
border-radius: 4px; border-radius: 4px;
} }
@ -1249,13 +1263,31 @@ export default {
} }
.article-editor-footer { .article-editor-footer {
width: 100%;
height: 100%;
min-height: 0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
box-sizing: border-box;
} }
.editor-status { .editor-status {
color: #9ca3af; color: #9ca3af;
font-size: 12px; font-size: 12px;
} }
.editor-footer-buttons {
display: flex;
align-items: center;
padding: 0;
gap: 10px;
.el-button {
min-height: 32px;
margin-left: 0 !important;
border-radius: var(--article-editor-radius);
}
}
</style> </style>