初始化
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + Vue</title>
|
<title>萧康云医</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -55,13 +55,15 @@ const themeOptions = [
|
|||||||
const menuGroups = [
|
const menuGroups = [
|
||||||
{ key: 'home', label: '首页', to: '/', links: siteConfig.navigation.secondary.home },
|
{ key: 'home', label: '首页', to: '/', links: siteConfig.navigation.secondary.home },
|
||||||
{ key: 'product', label: '产品', to: '/product', links: siteConfig.navigation.secondary.product },
|
{ key: 'product', label: '产品', to: '/product', links: siteConfig.navigation.secondary.product },
|
||||||
{ key: 'video', label: '视频', to: '/video/user', activePath: '/video', links: siteConfig.navigation.secondary.video }
|
{ key: 'video', label: '视频', to: '/video/user', activePath: '/video', links: siteConfig.navigation.secondary.video },
|
||||||
|
{ key: 'about', label: '关于我们', to: '/about', links: siteConfig.navigation.secondary.about }
|
||||||
]
|
]
|
||||||
|
|
||||||
const activeTheme = computed(() => themeOptions.find((item) => item.value === themeMode.value) || themeOptions[0])
|
const activeTheme = computed(() => themeOptions.find((item) => item.value === themeMode.value) || themeOptions[0])
|
||||||
const isHome = computed(() => route.path === '/')
|
const isHome = computed(() => route.path === '/')
|
||||||
const isProduct = computed(() => route.path.startsWith('/product'))
|
const isProduct = computed(() => route.path.startsWith('/product'))
|
||||||
const isVideo = computed(() => route.path.startsWith('/video'))
|
const isVideo = computed(() => route.path.startsWith('/video'))
|
||||||
|
const isAbout = computed(() => route.path.startsWith('/about'))
|
||||||
const isNavSolid = computed(() => isPastFirstScreen.value)
|
const isNavSolid = computed(() => isPastFirstScreen.value)
|
||||||
const navTone = computed(() => {
|
const navTone = computed(() => {
|
||||||
if (isHome.value && !isNavSolid.value) return 'dark'
|
if (isHome.value && !isNavSolid.value) return 'dark'
|
||||||
@@ -92,6 +94,14 @@ const routeSearchItems = computed(() => [
|
|||||||
to: '/video/user',
|
to: '/video/user',
|
||||||
category: '页面',
|
category: '页面',
|
||||||
keywords: ['视频库', '演示视频', '用户端', '医生端', '店长端']
|
keywords: ['视频库', '演示视频', '用户端', '医生端', '店长端']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'route-about',
|
||||||
|
title: '关于我们',
|
||||||
|
description: siteConfig.about.visionTitle,
|
||||||
|
to: '/about',
|
||||||
|
category: '页面',
|
||||||
|
keywords: ['关于我们', '愿景', '位置', siteConfig.about.locationName, siteConfig.about.visionBody]
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -99,7 +109,8 @@ const navigationSearchItems = computed(() => {
|
|||||||
const groups = [
|
const groups = [
|
||||||
{ category: '首页标题', links: siteConfig.navigation.secondary.home },
|
{ category: '首页标题', links: siteConfig.navigation.secondary.home },
|
||||||
{ category: '产品标题', links: siteConfig.navigation.secondary.product },
|
{ category: '产品标题', links: siteConfig.navigation.secondary.product },
|
||||||
{ category: '视频分区', links: siteConfig.navigation.secondary.video }
|
{ category: '视频分区', links: siteConfig.navigation.secondary.video },
|
||||||
|
{ category: '关于我们', links: siteConfig.navigation.secondary.about }
|
||||||
]
|
]
|
||||||
|
|
||||||
return groups.flatMap((group) =>
|
return groups.flatMap((group) =>
|
||||||
@@ -197,7 +208,34 @@ const contentSearchItems = computed(() => {
|
|||||||
}))
|
}))
|
||||||
])
|
])
|
||||||
|
|
||||||
return [...homeItems, ...productItems, ...videoItems]
|
const aboutItems = [
|
||||||
|
{
|
||||||
|
id: 'about-vision',
|
||||||
|
title: '我们的愿景',
|
||||||
|
description: siteConfig.about.visionTitle,
|
||||||
|
to: '/about#vision',
|
||||||
|
category: '关于我们',
|
||||||
|
keywords: ['愿景', siteConfig.about.visionTitle, siteConfig.about.visionBody]
|
||||||
|
},
|
||||||
|
...siteConfig.about.principles.map((item) => ({
|
||||||
|
id: `about-principle-${item.title}`,
|
||||||
|
title: item.title,
|
||||||
|
description: item.body,
|
||||||
|
to: '/about#principles',
|
||||||
|
category: '发展理念',
|
||||||
|
keywords: [item.title, item.body]
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
id: 'about-location',
|
||||||
|
title: siteConfig.about.locationName,
|
||||||
|
description: `在高德地图中查看${siteConfig.about.locationName}。`,
|
||||||
|
to: '/about#location',
|
||||||
|
category: '公司位置',
|
||||||
|
keywords: ['高德地图', '地图', siteConfig.about.locationName, siteConfig.about.mapQuery]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return [...homeItems, ...productItems, ...videoItems, ...aboutItems]
|
||||||
})
|
})
|
||||||
|
|
||||||
const searchItems = computed(() => {
|
const searchItems = computed(() => {
|
||||||
@@ -425,7 +463,8 @@ onBeforeUnmount(() => {
|
|||||||
'site-header-home': isHome && !isNavSolid,
|
'site-header-home': isHome && !isNavSolid,
|
||||||
'site-header-solid': isNavSolid,
|
'site-header-solid': isNavSolid,
|
||||||
'site-header-product': isProduct,
|
'site-header-product': isProduct,
|
||||||
'site-header-video': isVideo
|
'site-header-video': isVideo,
|
||||||
|
'site-header-about': isAbout
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
@@ -437,7 +476,8 @@ onBeforeUnmount(() => {
|
|||||||
'site-nav-solid': isNavSolid,
|
'site-nav-solid': isNavSolid,
|
||||||
'site-nav-overlay': !isNavSolid,
|
'site-nav-overlay': !isNavSolid,
|
||||||
'site-nav-product': isProduct,
|
'site-nav-product': isProduct,
|
||||||
'site-nav-video': isVideo
|
'site-nav-video': isVideo,
|
||||||
|
'site-nav-about': isAbout
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
style="animation-delay: 0ms"
|
style="animation-delay: 0ms"
|
||||||
|
|||||||
129
src/components/vue-bits/BlurText.vue
Normal file
129
src/components/vue-bits/BlurText.vue
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
type TagName = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';
|
||||||
|
type AnimateBy = 'chars' | 'words';
|
||||||
|
type Direction = 'top' | 'bottom';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
text: string;
|
||||||
|
className?: string;
|
||||||
|
tag?: TagName;
|
||||||
|
animateBy?: AnimateBy;
|
||||||
|
direction?: Direction;
|
||||||
|
delay?: number;
|
||||||
|
duration?: number;
|
||||||
|
threshold?: number;
|
||||||
|
rootMargin?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
className: '',
|
||||||
|
tag: 'p',
|
||||||
|
animateBy: 'chars',
|
||||||
|
direction: 'bottom',
|
||||||
|
delay: 65,
|
||||||
|
duration: 820,
|
||||||
|
threshold: 0.1,
|
||||||
|
rootMargin: '0px'
|
||||||
|
});
|
||||||
|
|
||||||
|
const rootRef = ref<HTMLElement | null>(null);
|
||||||
|
const isVisible = ref(false);
|
||||||
|
let observer: IntersectionObserver | null = null;
|
||||||
|
|
||||||
|
const tokens = computed(() => {
|
||||||
|
if (props.animateBy === 'words') {
|
||||||
|
return props.text.split(/(\s+)/).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(props.text);
|
||||||
|
});
|
||||||
|
|
||||||
|
const rootClass = computed(() => ['blur-text', props.className].filter(Boolean).join(' '));
|
||||||
|
const yOffset = computed(() => (props.direction === 'top' ? '-0.72em' : '0.72em'));
|
||||||
|
|
||||||
|
function reveal() {
|
||||||
|
isVisible.value = true;
|
||||||
|
observer?.disconnect();
|
||||||
|
observer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
if (reduceMotion || !rootRef.value || !('IntersectionObserver' in window)) {
|
||||||
|
reveal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.isIntersecting) reveal();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: props.threshold,
|
||||||
|
rootMargin: props.rootMargin
|
||||||
|
}
|
||||||
|
);
|
||||||
|
observer.observe(rootRef.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
observer?.disconnect();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component :is="tag" ref="rootRef" :class="rootClass" :aria-label="text">
|
||||||
|
<span
|
||||||
|
v-for="(token, index) in tokens"
|
||||||
|
:key="`${token}-${index}`"
|
||||||
|
class="blur-text-token"
|
||||||
|
:class="{ 'blur-text-token-visible': isVisible }"
|
||||||
|
:style="{
|
||||||
|
'--blur-text-delay': `${index * delay}ms`,
|
||||||
|
'--blur-text-duration': `${duration}ms`,
|
||||||
|
'--blur-text-y': yOffset
|
||||||
|
}"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{{ token }}
|
||||||
|
</span>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.blur-text {
|
||||||
|
display: block;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blur-text-token {
|
||||||
|
display: inline-block;
|
||||||
|
filter: blur(12px);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(0, var(--blur-text-y), 0);
|
||||||
|
transition:
|
||||||
|
filter var(--blur-text-duration) cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
|
opacity var(--blur-text-duration) cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
|
transform var(--blur-text-duration) cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
transition-delay: var(--blur-text-delay);
|
||||||
|
will-change: filter, opacity, transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blur-text-token-visible {
|
||||||
|
filter: blur(0);
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.blur-text-token {
|
||||||
|
filter: none;
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -9,6 +9,7 @@ const route = useRoute()
|
|||||||
const backdropClass = computed(() => {
|
const backdropClass = computed(() => {
|
||||||
if (route.path.startsWith('/video')) return 'video-route-backdrop'
|
if (route.path.startsWith('/video')) return 'video-route-backdrop'
|
||||||
if (route.path.startsWith('/product')) return 'product-route-backdrop'
|
if (route.path.startsWith('/product')) return 'product-route-backdrop'
|
||||||
|
if (route.path.startsWith('/about')) return 'about-route-backdrop'
|
||||||
return 'home-route-backdrop'
|
return 'home-route-backdrop'
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -30,6 +31,7 @@ const backdropClass = computed(() => {
|
|||||||
<RouterLink to="/">首页</RouterLink>
|
<RouterLink to="/">首页</RouterLink>
|
||||||
<RouterLink to="/product">产品</RouterLink>
|
<RouterLink to="/product">产品</RouterLink>
|
||||||
<RouterLink to="/video/user">视频</RouterLink>
|
<RouterLink to="/video/user">视频</RouterLink>
|
||||||
|
<RouterLink to="/about">关于我们</RouterLink>
|
||||||
<RouterLink to="/#business">业务介绍</RouterLink>
|
<RouterLink to="/#business">业务介绍</RouterLink>
|
||||||
<RouterLink to="/product#pricing">定价计划</RouterLink>
|
<RouterLink to="/product#pricing">定价计划</RouterLink>
|
||||||
<RouterLink to="/product#contact">联系咨询</RouterLink>
|
<RouterLink to="/product#contact">联系咨询</RouterLink>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const routes = [
|
|||||||
{ path: 'product', name: 'Product', component: () => import('@/views/product/index.vue') },
|
{ path: 'product', name: 'Product', component: () => import('@/views/product/index.vue') },
|
||||||
{ path: 'video', redirect: '/video/user' },
|
{ path: 'video', redirect: '/video/user' },
|
||||||
{ path: 'video/:audience(user|doctor|manager)', name: 'VideoShowcase', component: () => import('@/views/video/index.vue') },
|
{ path: 'video/:audience(user|doctor|manager)', name: 'VideoShowcase', component: () => import('@/views/video/index.vue') },
|
||||||
|
{ path: 'about', name: 'About', component: () => import('@/views/about/index.vue') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ export const siteConfig = {
|
|||||||
primary: [
|
primary: [
|
||||||
{ label: '首页', to: '/' },
|
{ label: '首页', to: '/' },
|
||||||
{ label: '产品', to: '/product' },
|
{ label: '产品', to: '/product' },
|
||||||
{ label: '视频', to: '/video/user' }
|
{ label: '视频', to: '/video/user' },
|
||||||
|
{ label: '关于我们', to: '/about' }
|
||||||
],
|
],
|
||||||
secondary: {
|
secondary: {
|
||||||
home: [
|
home: [
|
||||||
@@ -28,6 +29,12 @@ export const siteConfig = {
|
|||||||
{ label: '用户端', to: '/video/user' },
|
{ label: '用户端', to: '/video/user' },
|
||||||
{ label: '医生端', to: '/video/doctor' },
|
{ label: '医生端', to: '/video/doctor' },
|
||||||
{ label: '小程序店长管理端', to: '/video/manager' }
|
{ label: '小程序店长管理端', to: '/video/manager' }
|
||||||
|
],
|
||||||
|
about: [
|
||||||
|
{ label: '我们的愿景', to: '/about#vision' },
|
||||||
|
{ label: '发展理念', to: '/about#principles' },
|
||||||
|
{ label: '公司位置', to: '/about#location' },
|
||||||
|
{ label: '联系咨询', to: '/product#contact' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -42,6 +49,32 @@ export const siteConfig = {
|
|||||||
phone: '400-820-9618',
|
phone: '400-820-9618',
|
||||||
email: 'hello@xiaokangcloudmed.cn',
|
email: 'hello@xiaokangcloudmed.cn',
|
||||||
address: '上海市浦东新区张江医疗创新园'
|
address: '上海市浦东新区张江医疗创新园'
|
||||||
|
},
|
||||||
|
about: {
|
||||||
|
visionTitle: '让基层医疗机构以更低门槛拥有稳定、合规、可持续的数字化能力。',
|
||||||
|
visionBody: '萧康云医希望把复杂的医疗协同、经营管理和线上服务能力,沉淀成机构每天都能用得顺手的云端工作台。我们关注的不只是系统上线,而是医生、运营团队和患者之间能否形成长期、可靠、可追踪的服务闭环。',
|
||||||
|
principles: [
|
||||||
|
{
|
||||||
|
title: '医疗协同',
|
||||||
|
body: '围绕患者、医生和机构角色建立统一任务流,让跨岗位协作更清晰。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '长期服务',
|
||||||
|
body: '支持随访、复诊、回访和转诊沉淀,帮助机构建立持续服务能力。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '合规安全',
|
||||||
|
body: '以权限、审计、备案协助和部署选择为基础,适配医疗行业的合规要求。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '经营增长',
|
||||||
|
body: '把挂号、处方、对账、推广和客户运营串联起来,帮助机构看清经营状态。'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
locationName: '大远科创园A栋',
|
||||||
|
mapQuery: '大远科创园A栋',
|
||||||
|
mapUrl: 'https://www.amap.com/search?query=%E5%A4%A7%E8%BF%9C%E7%A7%91%E5%88%9B%E5%9B%ADA%E6%A0%8B',
|
||||||
|
mapOpenUrl: 'https://www.amap.com/search?query=%E5%A4%A7%E8%BF%9C%E7%A7%91%E5%88%9B%E5%9B%ADA%E6%A0%8B'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
350
src/style.css
350
src/style.css
@@ -2573,7 +2573,7 @@ textarea {
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: clamp(3rem, 13vw, 9.5rem);
|
font-size: clamp(3rem, 13vw, 9.5rem);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: -0.04em;
|
letter-spacing: 0;
|
||||||
line-height: 0.92;
|
line-height: 0.92;
|
||||||
margin: 0 0 1.2rem;
|
margin: 0 0 1.2rem;
|
||||||
perspective: 900px;
|
perspective: 900px;
|
||||||
@@ -4219,6 +4219,351 @@ textarea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* About page */
|
||||||
|
.about-page {
|
||||||
|
--about-bg: #f8fbfc;
|
||||||
|
--about-bg-deep: #eef5f6;
|
||||||
|
--about-ink: #101416;
|
||||||
|
--about-muted: #5c6d74;
|
||||||
|
--about-panel: rgba(255, 255, 255, 0.92);
|
||||||
|
--about-panel-solid: #ffffff;
|
||||||
|
--about-line: rgba(15, 23, 42, 0.12);
|
||||||
|
--about-line-strong: rgba(13, 148, 136, 0.28);
|
||||||
|
--about-accent: #0f766e;
|
||||||
|
--about-accent-strong: #0f4f4a;
|
||||||
|
--about-sky: #0891b2;
|
||||||
|
--about-shadow: 0 24px 70px rgba(15, 23, 42, 0.12);
|
||||||
|
color: var(--about-ink);
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .about-route-backdrop,
|
||||||
|
:root[data-theme="dark"] .about-page {
|
||||||
|
--about-bg: #0c0f10;
|
||||||
|
--about-bg-deep: #050707;
|
||||||
|
--about-ink: #eef7fb;
|
||||||
|
--about-muted: rgba(215, 226, 234, 0.72);
|
||||||
|
--about-panel: rgba(13, 18, 20, 0.9);
|
||||||
|
--about-panel-solid: #0d1214;
|
||||||
|
--about-line: rgba(215, 226, 234, 0.14);
|
||||||
|
--about-line-strong: rgba(45, 212, 191, 0.32);
|
||||||
|
--about-accent: #2dd4bf;
|
||||||
|
--about-accent-strong: #99f6e4;
|
||||||
|
--about-sky: #22d3ee;
|
||||||
|
--about-shadow: 0 28px 82px rgba(0, 0, 0, 0.38);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-route-backdrop {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, var(--about-bg) 0%, var(--about-bg-deep) 48%, var(--about-bg) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-route-backdrop::before {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(245, 251, 252, 0.58) 22%, rgba(245, 251, 252, 0.62) 82%, rgba(255, 255, 255, 0.74));
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .about-route-backdrop::before {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(5, 9, 10, 0.8), rgba(5, 9, 10, 0.58) 24%, rgba(5, 9, 10, 0.66) 82%, rgba(5, 9, 10, 0.84));
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-route-backdrop .theme-backdrop-canvas {
|
||||||
|
opacity: 0.16;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .about-route-backdrop .theme-backdrop-canvas {
|
||||||
|
opacity: 0.28;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero {
|
||||||
|
min-height: 88vh;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero-inner,
|
||||||
|
.about-section-inner,
|
||||||
|
.about-location-shell {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 1180px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero-copy {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.25rem;
|
||||||
|
max-width: 880px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-eyebrow,
|
||||||
|
.about-section-heading span,
|
||||||
|
.about-vision-panel > span,
|
||||||
|
.about-map-meta span {
|
||||||
|
align-items: center;
|
||||||
|
color: var(--about-accent-strong);
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 900;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-eyebrow {
|
||||||
|
background: color-mix(in srgb, var(--about-accent) 10%, var(--about-panel-solid));
|
||||||
|
border: 1px solid var(--about-line-strong);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 0.5rem 0.78rem;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero-title {
|
||||||
|
color: var(--about-ink);
|
||||||
|
font-size: clamp(2.35rem, 6vw, 5.25rem);
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: 0;
|
||||||
|
line-height: 1.02;
|
||||||
|
margin: 0;
|
||||||
|
max-width: 960px;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero-subtitle,
|
||||||
|
.about-section-heading p,
|
||||||
|
.about-vision-panel p,
|
||||||
|
.about-principle-card p,
|
||||||
|
.about-map-meta small {
|
||||||
|
color: var(--about-muted);
|
||||||
|
line-height: 1.78;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero-subtitle {
|
||||||
|
font-size: clamp(1.05rem, 2vw, 1.45rem);
|
||||||
|
max-width: 720px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-primary-action,
|
||||||
|
.about-secondary-action,
|
||||||
|
.about-map-meta a {
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
display: inline-flex;
|
||||||
|
font-weight: 900;
|
||||||
|
gap: 0.45rem;
|
||||||
|
min-height: 46px;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: background 180ms ease-out, border-color 180ms ease-out, color 180ms ease-out, transform 180ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-primary-action,
|
||||||
|
.about-map-meta a {
|
||||||
|
background: var(--about-ink);
|
||||||
|
color: var(--about-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-primary-action:hover,
|
||||||
|
.about-map-meta a:hover {
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #c8fff2 48%, #ffffff 100%);
|
||||||
|
color: #041012;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-secondary-action {
|
||||||
|
border: 1px solid var(--about-line-strong);
|
||||||
|
color: var(--about-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-secondary-action:hover {
|
||||||
|
background: color-mix(in srgb, var(--about-accent) 10%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-section {
|
||||||
|
padding: 5.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-section-muted {
|
||||||
|
background:
|
||||||
|
linear-gradient(120deg, color-mix(in srgb, var(--about-accent) 8%, transparent), transparent 42%),
|
||||||
|
color-mix(in srgb, var(--about-panel-solid) 74%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .about-section-muted {
|
||||||
|
background:
|
||||||
|
linear-gradient(120deg, color-mix(in srgb, var(--about-accent) 10%, transparent), transparent 42%),
|
||||||
|
rgba(7, 20, 23, 0.34);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-vision-panel,
|
||||||
|
.about-principle-card,
|
||||||
|
.about-map-panel {
|
||||||
|
background: var(--about-panel);
|
||||||
|
border: 1px solid var(--about-line);
|
||||||
|
box-shadow: var(--about-shadow);
|
||||||
|
color: var(--about-ink);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-vision-panel {
|
||||||
|
border-radius: 30px;
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: clamp(1.5rem, 4vw, 2.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-vision-panel h2,
|
||||||
|
.about-section-heading h2 {
|
||||||
|
color: var(--about-ink);
|
||||||
|
font-size: clamp(2rem, 4vw, 3.6rem);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1.08;
|
||||||
|
margin: 0.4rem 0 0;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-vision-panel p {
|
||||||
|
font-size: 1.08rem;
|
||||||
|
max-width: 880px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-section-heading {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.6rem;
|
||||||
|
max-width: 760px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-principle-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-principle-reveal {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-principle-card {
|
||||||
|
border-radius: 24px;
|
||||||
|
display: grid;
|
||||||
|
gap: 0.7rem;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 240px;
|
||||||
|
padding: 1.2rem;
|
||||||
|
transition: border-color 180ms ease-out, box-shadow 180ms ease-out, transform 180ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-principle-card:hover {
|
||||||
|
border-color: var(--about-line-strong);
|
||||||
|
box-shadow: 0 28px 78px color-mix(in srgb, var(--about-accent) 12%, rgba(15, 23, 42, 0.12));
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-principle-card svg,
|
||||||
|
.about-map-meta > svg {
|
||||||
|
color: var(--about-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-principle-card h3 {
|
||||||
|
color: var(--about-ink);
|
||||||
|
font-size: 1.18rem;
|
||||||
|
font-weight: 900;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-location-shell {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.5rem;
|
||||||
|
max-width: 1480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-panel {
|
||||||
|
border-radius: 30px;
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: minmax(0, 1.45fr) minmax(300px, 0.36fr);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-frame {
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, color-mix(in srgb, var(--about-accent) 12%, var(--about-panel-solid)), var(--about-panel-solid));
|
||||||
|
border: 1px solid var(--about-line);
|
||||||
|
border-radius: 24px;
|
||||||
|
min-height: 560px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-frame iframe {
|
||||||
|
border: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-meta {
|
||||||
|
align-content: center;
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-meta strong {
|
||||||
|
color: var(--about-ink);
|
||||||
|
display: block;
|
||||||
|
font-size: 1.35rem;
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.about-principle-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-panel {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-frame {
|
||||||
|
min-height: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.about-hero {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-hero-actions,
|
||||||
|
.about-primary-action,
|
||||||
|
.about-secondary-action,
|
||||||
|
.about-map-meta a {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-primary-action,
|
||||||
|
.about-secondary-action,
|
||||||
|
.about-map-meta a {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-principle-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-map-frame {
|
||||||
|
aspect-ratio: 4 / 5;
|
||||||
|
min-height: 460px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Final nav contrast, backdrop interaction, and home marquee fixes */
|
/* Final nav contrast, backdrop interaction, and home marquee fixes */
|
||||||
.site-header {
|
.site-header {
|
||||||
--nav-ink: #f8ffff;
|
--nav-ink: #f8ffff;
|
||||||
@@ -4242,7 +4587,8 @@ textarea {
|
|||||||
|
|
||||||
.site-header-home,
|
.site-header-home,
|
||||||
:root[data-theme="dark"] .site-header-solid,
|
:root[data-theme="dark"] .site-header-solid,
|
||||||
:root[data-theme="dark"] .site-header-product {
|
:root[data-theme="dark"] .site-header-product,
|
||||||
|
:root[data-theme="dark"] .site-header-about {
|
||||||
--nav-ink: #f8ffff;
|
--nav-ink: #f8ffff;
|
||||||
--nav-muted: rgba(215, 226, 234, 0.78);
|
--nav-muted: rgba(215, 226, 234, 0.78);
|
||||||
--nav-panel: rgba(12, 12, 12, 0.78);
|
--nav-panel: rgba(12, 12, 12, 0.78);
|
||||||
|
|||||||
126
src/views/about/index.vue
Normal file
126
src/views/about/index.vue
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ArrowRight, Building2, CheckCircle2, MapPinned, ShieldCheck, Sparkles } from 'lucide-vue-next'
|
||||||
|
import AnimatedContent from '@/components/vue-bits/AnimatedContent.vue'
|
||||||
|
import DecryptedText from '@/components/vue-bits/DecryptedText.vue'
|
||||||
|
import SplitText from '@/components/vue-bits/SplitText.vue'
|
||||||
|
import { siteConfig } from '@/site.config'
|
||||||
|
|
||||||
|
const principleIcons = [Sparkles, CheckCircle2, ShieldCheck, Building2]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main class="about-page">
|
||||||
|
<section class="about-hero section-pad">
|
||||||
|
<div class="about-hero-inner px-5 pt-24 md:px-8">
|
||||||
|
<AnimatedContent :distance="54" :duration="0.9" class-name="about-hero-copy">
|
||||||
|
<div class="about-eyebrow">
|
||||||
|
<Building2 :size="16" aria-hidden="true" />
|
||||||
|
关于{{ siteConfig.brand.name }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SplitText
|
||||||
|
:text="siteConfig.about.visionTitle"
|
||||||
|
tag="h1"
|
||||||
|
class-name="about-hero-title"
|
||||||
|
split-type="lines"
|
||||||
|
:delay="58"
|
||||||
|
:duration="0.92"
|
||||||
|
ease="power4.out"
|
||||||
|
:from="{ opacity: 0, y: 38, filter: 'blur(12px)' }"
|
||||||
|
:to="{ opacity: 1, y: 0, filter: 'blur(0)' }"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="about-hero-subtitle">
|
||||||
|
<DecryptedText
|
||||||
|
:text="siteConfig.brand.description"
|
||||||
|
animate-on="view"
|
||||||
|
reveal-direction="center"
|
||||||
|
:speed="30"
|
||||||
|
:max-iterations="10"
|
||||||
|
encrypted-class-name="decrypted-ghost"
|
||||||
|
class-name="decrypted-final"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="about-hero-actions">
|
||||||
|
<a href="#vision" class="about-primary-action">
|
||||||
|
了解愿景
|
||||||
|
<ArrowRight :size="18" aria-hidden="true" />
|
||||||
|
</a>
|
||||||
|
<a :href="siteConfig.about.mapOpenUrl" target="_blank" rel="noreferrer" class="about-secondary-action">
|
||||||
|
查看公司位置
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</AnimatedContent>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="vision" class="about-section">
|
||||||
|
<div class="about-section-inner px-5 md:px-8">
|
||||||
|
<AnimatedContent :distance="44" :duration="0.8" class-name="about-vision-panel">
|
||||||
|
<span>我们的愿景</span>
|
||||||
|
<h2>{{ siteConfig.about.visionTitle }}</h2>
|
||||||
|
<p>{{ siteConfig.about.visionBody }}</p>
|
||||||
|
</AnimatedContent>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="principles" class="about-section about-section-muted">
|
||||||
|
<div class="about-section-inner px-5 md:px-8">
|
||||||
|
<AnimatedContent :distance="42" :duration="0.8" class-name="about-section-heading">
|
||||||
|
<span>发展理念</span>
|
||||||
|
<h2>把医疗服务、机构经营和合规能力放进同一个长期系统。</h2>
|
||||||
|
</AnimatedContent>
|
||||||
|
|
||||||
|
<div class="about-principle-grid">
|
||||||
|
<AnimatedContent
|
||||||
|
v-for="(item, index) in siteConfig.about.principles"
|
||||||
|
:key="item.title"
|
||||||
|
:delay="index * 0.08"
|
||||||
|
:distance="44"
|
||||||
|
class-name="about-principle-reveal"
|
||||||
|
>
|
||||||
|
<article class="about-principle-card">
|
||||||
|
<component :is="principleIcons[index % principleIcons.length]" :size="24" aria-hidden="true" />
|
||||||
|
<h3>{{ item.title }}</h3>
|
||||||
|
<p>{{ item.body }}</p>
|
||||||
|
</article>
|
||||||
|
</AnimatedContent>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="location" class="about-section">
|
||||||
|
<div class="about-location-shell px-5 md:px-8">
|
||||||
|
<AnimatedContent :distance="42" :duration="0.8" class-name="about-section-heading align-left">
|
||||||
|
<span>公司位置</span>
|
||||||
|
<h2>在高德地图中查看{{ siteConfig.about.locationName }}。</h2>
|
||||||
|
<p>地图使用高德官网公开搜索页嵌入,不接入开放平台 SDK,不需要 API Key。</p>
|
||||||
|
</AnimatedContent>
|
||||||
|
|
||||||
|
<AnimatedContent :distance="48" direction="horizontal" :reverse="true" :duration="0.86" class-name="about-map-panel">
|
||||||
|
<div class="about-map-frame">
|
||||||
|
<iframe
|
||||||
|
:src="siteConfig.about.mapUrl"
|
||||||
|
:title="`${siteConfig.about.locationName} 高德地图`"
|
||||||
|
loading="lazy"
|
||||||
|
referrerpolicy="no-referrer-when-downgrade"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="about-map-meta">
|
||||||
|
<MapPinned :size="24" aria-hidden="true" />
|
||||||
|
<div>
|
||||||
|
<span>搜索目标</span>
|
||||||
|
<strong>{{ siteConfig.about.locationName }}</strong>
|
||||||
|
<small>如果浏览器限制 iframe 加载,可使用下方按钮在高德地图中打开。</small>
|
||||||
|
</div>
|
||||||
|
<a :href="siteConfig.about.mapOpenUrl" target="_blank" rel="noreferrer">
|
||||||
|
在高德地图中打开
|
||||||
|
<ArrowRight :size="17" aria-hidden="true" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</AnimatedContent>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
@@ -5,6 +5,7 @@ import { ArrowLeft, ArrowRight, Calendar, CheckCircle2, Clock3, Play, ShieldChec
|
|||||||
import { homeCaseCards, homeMarqueeItems, homeServiceItems, metrics, siteConfig } from '@/site.config'
|
import { homeCaseCards, homeMarqueeItems, homeServiceItems, metrics, siteConfig } from '@/site.config'
|
||||||
import AnimatedContent from '@/components/vue-bits/AnimatedContent.vue'
|
import AnimatedContent from '@/components/vue-bits/AnimatedContent.vue'
|
||||||
import DecryptedText from '@/components/vue-bits/DecryptedText.vue'
|
import DecryptedText from '@/components/vue-bits/DecryptedText.vue'
|
||||||
|
import BlurText from '@/components/vue-bits/BlurText.vue'
|
||||||
import Magnet from '@/components/vue-bits/Magnet.vue'
|
import Magnet from '@/components/vue-bits/Magnet.vue'
|
||||||
import Silk from '@/components/vue-bits/Silk.vue'
|
import Silk from '@/components/vue-bits/Silk.vue'
|
||||||
import SplitText from '@/components/vue-bits/SplitText.vue'
|
import SplitText from '@/components/vue-bits/SplitText.vue'
|
||||||
@@ -99,19 +100,16 @@ onBeforeUnmount(() => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SplitText
|
<BlurText
|
||||||
:text="siteConfig.brand.name"
|
:text="siteConfig.brand.name"
|
||||||
tag="h1"
|
tag="h1"
|
||||||
class-name="cinematic-title"
|
class-name="cinematic-title"
|
||||||
split-type="chars"
|
animate-by="chars"
|
||||||
:delay="80"
|
direction="bottom"
|
||||||
:duration="1.15"
|
:delay="90"
|
||||||
ease="power4.out"
|
:duration="980"
|
||||||
:threshold="0.05"
|
:threshold="0.05"
|
||||||
root-margin="0px"
|
root-margin="0px"
|
||||||
:from="{ opacity: 0, y: 70, filter: 'blur(18px)', rotateX: -22 }"
|
|
||||||
:to="{ opacity: 1, y: 0, filter: 'blur(0px)', rotateX: 0 }"
|
|
||||||
text-align="left"
|
|
||||||
/>
|
/>
|
||||||
<p class="cinematic-subtitle animate-blur-fade-up" style="animation-delay: 500ms">
|
<p class="cinematic-subtitle animate-blur-fade-up" style="animation-delay: 500ms">
|
||||||
<DecryptedText
|
<DecryptedText
|
||||||
|
|||||||
Reference in New Issue
Block a user