Compare commits
No commits in common. "c60c0374247142cd2ca7e8ed549a700a91a6929b" and "17cb1efcd07f7f3ba3f072a9c5a8141fb269d1e7" have entirely different histories.
c60c037424
...
17cb1efcd0
10
index.html
@ -4,17 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="柚康 - 医疗行业患者全周期管理服务数智化升级服务" />
|
<meta name="description" content="柚康 - 医疗行业患者全周期管理服务数智化升级服务" />
|
||||||
<link rel="icon" type="image/png" href="/assets/logo.png" />
|
|
||||||
<link rel="apple-touch-icon" href="/assets/logo.png" />
|
|
||||||
<title>柚康|患者精细化管理服务平台</title>
|
<title>柚康|患者精细化管理服务平台</title>
|
||||||
<script>
|
|
||||||
if ('scrollRestoration' in history) {
|
|
||||||
history.scrollRestoration = 'manual'
|
|
||||||
}
|
|
||||||
if (!location.hash) {
|
|
||||||
scrollTo(0, 0)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 366 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 19 KiB |
@ -1,20 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { caseCategories, cases } from '../data/homepage'
|
import { caseCategories, cases } from '../data/homepage'
|
||||||
import LucideHospitalIcon from './LucideHospitalIcon.vue'
|
|
||||||
import SectionTitle from './SectionTitle.vue'
|
import SectionTitle from './SectionTitle.vue'
|
||||||
|
|
||||||
const active = ref(caseCategories[0])
|
const active = ref(caseCategories[0])
|
||||||
const filteredCases = computed(() => cases.filter(item => item.category === active.value))
|
const filteredCases = computed(() => cases.filter(item => item.category === active.value))
|
||||||
const activeIndex = computed(() => caseCategories.findIndex(category => category === active.value))
|
|
||||||
const categoryLineMap = {
|
|
||||||
国家医学中心: ['国家医学', '中心'],
|
|
||||||
公立医院: ['公立医院'],
|
|
||||||
妇幼保健客户: ['妇幼保健', '客户'],
|
|
||||||
医疗集团客户: ['医疗集团', '客户']
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCategoryLines = (category) => categoryLineMap[category] ?? [category]
|
|
||||||
|
|
||||||
const selectCategory = (category) => {
|
const selectCategory = (category) => {
|
||||||
active.value = category
|
active.value = category
|
||||||
@ -65,20 +55,12 @@ const handleCategoryKeydown = (event, index) => {
|
|||||||
@click="selectCategory(category)"
|
@click="selectCategory(category)"
|
||||||
@keydown="handleCategoryKeydown($event, index)"
|
@keydown="handleCategoryKeydown($event, index)"
|
||||||
>
|
>
|
||||||
<span class="case-filter-text">
|
{{ category }}
|
||||||
<span v-for="line in getCategoryLines(category)" :key="line">{{ line }}</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
<span class="case-tab-indicator" :style="{ transform: `translateX(${activeIndex * 100}%)` }"></span>
|
|
||||||
</aside>
|
</aside>
|
||||||
<div id="case-panel" class="case-grid" role="tabpanel" :aria-label="active">
|
<div id="case-panel" class="case-grid" role="tabpanel" :aria-label="active">
|
||||||
<article v-for="item in filteredCases" :key="`${active}-${item.name}`" class="case-card">
|
<article v-for="item in filteredCases" :key="`${active}-${item.name}`" class="case-card">
|
||||||
<div :class="['case-logo-slot', item.logoFit && `case-logo-slot--${item.logoFit}`, { 'case-logo-slot--placeholder': !item.logo }]">
|
<div :class="['case-logo', `case-logo--${item.tone}`]">{{ item.short }}</div>
|
||||||
<img v-if="item.logo" :src="item.logo" :alt="`${item.name.replace(/\n/g, '')} logo`" />
|
|
||||||
<span v-else class="case-logo-fallback">
|
|
||||||
<LucideHospitalIcon :size="30" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p>{{ item.name }}</p>
|
<p>{{ item.name }}</p>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import IconMark from './IconMark.vue'
|
|
||||||
import QrPlaceholder from './QrPlaceholder.vue'
|
import QrPlaceholder from './QrPlaceholder.vue'
|
||||||
import SectionTitle from './SectionTitle.vue'
|
import SectionTitle from './SectionTitle.vue'
|
||||||
</script>
|
</script>
|
||||||
@ -16,18 +15,24 @@ import SectionTitle from './SectionTitle.vue'
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
aria-label="在地图中打开办公地址"
|
aria-label="在地图中打开办公地址"
|
||||||
>
|
>
|
||||||
<span class="address-icon">
|
<span class="address-icon">📍</span>
|
||||||
<IconMark name="map-pin" :size="22" />
|
|
||||||
</span>
|
|
||||||
<strong>办公地址:</strong>
|
<strong>办公地址:</strong>
|
||||||
<span>杭州市西湖区翠苑街道塘苗路1号4号楼2楼</span>
|
<span>杭州市西湖区翠苑街道塘苗路1号4号楼2楼</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="contact-manager-grid">
|
<div class="contact-manager-grid">
|
||||||
<article class="manager-card">
|
<article class="manager-card">
|
||||||
<QrPlaceholder image="/assets/王经理二维码.jpg" label="微信长按扫码联系王经理" />
|
<div class="manager-copy">
|
||||||
|
<span class="manager-icon">👤</span>
|
||||||
|
<p><strong>客户成功经理:王经理</strong><br /></p>
|
||||||
|
</div>
|
||||||
|
<QrPlaceholder label="王经理个人码" small />
|
||||||
</article>
|
</article>
|
||||||
<article class="manager-card">
|
<article class="manager-card">
|
||||||
<QrPlaceholder image="/assets/罗经理二维码.png" label="微信长按扫码联系罗经理" />
|
<div class="manager-copy">
|
||||||
|
<span class="manager-icon">👤</span>
|
||||||
|
<p><strong>客户成功经理:罗经理</strong><br /></p>
|
||||||
|
</div>
|
||||||
|
<QrPlaceholder label="罗经理个人码" small />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,28 +2,13 @@
|
|||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
<div class="container footer-inner">
|
<div class="container footer-inner">
|
||||||
<div class="footer-brand">
|
<div class="footer-brand">
|
||||||
<img class="footer-logo" src="/assets/logo.png" alt="柚康科技 logo" />
|
<img src="/assets/logo.png" alt="柚康 logo" />
|
||||||
<span>
|
<span>
|
||||||
<strong>柚康科技</strong>
|
<strong>柚康</strong>
|
||||||
<i class="brand-separator" aria-hidden="true"></i>
|
<em>企业微信服务商标识</em>
|
||||||
<img class="brand-service" src="/assets/服务商.png" alt="企业微信服务商" />
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-legal" aria-label="网站备案信息">
|
<p>© 2026 杭州柚康科技有限公司 版权所有</p>
|
||||||
<span>© 2026 杭州柚康科技有限公司 版权所有</span>
|
|
||||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
|
|
||||||
浙ICP备2023032238号
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="police-record"
|
|
||||||
href="https://beian.mps.gov.cn/#/query/webSearch?code=33010602013275"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<img src="/assets/备案编号图标.png" alt="" aria-hidden="true" />
|
|
||||||
<span>浙公网安备33010602013275号</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { navItems } from '../data/homepage'
|
import { navItems } from '../data/homepage'
|
||||||
import IconMark from './IconMark.vue'
|
|
||||||
|
|
||||||
const activeTarget = ref(navItems[0]?.target ?? '')
|
const activeTarget = ref(navItems[0]?.target ?? '')
|
||||||
const isMenuOpen = ref(false)
|
const isMenuOpen = ref(false)
|
||||||
@ -85,12 +84,11 @@ onBeforeUnmount(() => {
|
|||||||
<template>
|
<template>
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
<div class="container header-inner">
|
<div class="container header-inner">
|
||||||
<a class="brand" href="#home" aria-label="柚康科技首页" @click.prevent="scrollToTarget('home')">
|
<a class="brand" href="#home" aria-label="柚康首页" @click.prevent="scrollToTarget('home')">
|
||||||
<img src="/assets/logo.png" alt="柚康科技 logo" class="brand-logo" />
|
<img src="/assets/logo.png" alt="柚康 logo" class="brand-logo" />
|
||||||
<span class="brand-copy">
|
<span class="brand-copy">
|
||||||
<strong>柚康科技</strong>
|
<strong>柚康</strong>
|
||||||
<i class="brand-separator" aria-hidden="true"></i>
|
<em>企业微信服务商标识</em>
|
||||||
<img class="brand-service" src="/assets/服务商.png" alt="企业微信服务商" />
|
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
@ -101,7 +99,9 @@ onBeforeUnmount(() => {
|
|||||||
:aria-label="isMenuOpen ? '收起主导航' : '展开主导航'"
|
:aria-label="isMenuOpen ? '收起主导航' : '展开主导航'"
|
||||||
@click="toggleMenu"
|
@click="toggleMenu"
|
||||||
>
|
>
|
||||||
<IconMark :name="isMenuOpen ? 'x' : 'menu'" :size="24" />
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
</button>
|
</button>
|
||||||
<nav
|
<nav
|
||||||
id="main-navigation"
|
id="main-navigation"
|
||||||
|
|||||||
@ -4,10 +4,8 @@
|
|||||||
<div class="hero-bg-orb hero-bg-orb--right"></div>
|
<div class="hero-bg-orb hero-bg-orb--right"></div>
|
||||||
<div class="container hero-grid">
|
<div class="container hero-grid">
|
||||||
<div class="hero-copy">
|
<div class="hero-copy">
|
||||||
<h1>
|
<p class="eyebrow">医疗服务数智化升级服务商</p>
|
||||||
<span class="hero-title-line hero-title-nowrap">患者全周期管理服务</span>
|
<h1>医疗行业患者全周期<br />管理服务数智化升级服务</h1>
|
||||||
<span class="hero-title-line">数智化升级服务商</span>
|
|
||||||
</h1>
|
|
||||||
<div class="hero-badge">专注于医疗服务提质提效增收</div>
|
<div class="hero-badge">专注于医疗服务提质提效增收</div>
|
||||||
<article id="company" class="intro-card glass-card">
|
<article id="company" class="intro-card glass-card">
|
||||||
<p>
|
<p>
|
||||||
@ -20,18 +18,8 @@
|
|||||||
|
|
||||||
<div class="hero-visual" aria-label="医疗服务场景图片占位">
|
<div class="hero-visual" aria-label="医疗服务场景图片占位">
|
||||||
<img src="/assets/medical.png" alt="医生与患者沟通场景占位图" />
|
<img src="/assets/medical.png" alt="医生与患者沟通场景占位图" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<a class="mobile-scroll-cue" href="#platform" aria-label="继续向下浏览">
|
|
||||||
<svg width="28" height="18" viewBox="0 0 28 18" fill="none" aria-hidden="true">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="scrollCueGradient" x1="4" y1="4" x2="24" y2="14" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#FF8A00" />
|
|
||||||
<stop offset="1" stop-color="#FFB84D" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<path d="M5 5L14 14L23 5" stroke="url(#scrollCueGradient)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -6,68 +6,27 @@ defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg
|
<svg class="icon-mark" :width="size" :height="size" viewBox="0 0 48 48" fill="none" aria-hidden="true">
|
||||||
class="icon-mark"
|
|
||||||
:width="size"
|
|
||||||
:height="size"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2.25"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<template v-if="name === 'user-flow'">
|
<template v-if="name === 'user-flow'">
|
||||||
<path d="M18 21a8 8 0 0 0-16 0" />
|
<circle cx="24" cy="16" r="7" stroke="currentColor" stroke-width="3" />
|
||||||
<circle cx="10" cy="8" r="5" />
|
<path d="M12 37c2.4-8.2 7-12 12-12s9.6 3.8 12 12" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
||||||
<path d="M22 20c0-3.37-2-6.5-4.5-8" />
|
<path d="M9 24h5m20 0h5" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
||||||
<path d="M17 3.13a5 5 0 0 1 0 9.74" />
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="name === 'clipboard'">
|
<template v-else-if="name === 'clipboard'">
|
||||||
<rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
|
<rect x="12" y="10" width="24" height="30" rx="5" stroke="currentColor" stroke-width="3" />
|
||||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
|
<path d="M18 8h12l2 5H16l2-5Z" fill="currentColor" />
|
||||||
<path d="M9 12h6" />
|
<path d="M18 23h12M18 31h8" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
||||||
<path d="M9 16h6" />
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="name === 'chart'">
|
<template v-else-if="name === 'chart'">
|
||||||
<path d="M3 3v18h18" />
|
<path d="M12 38h26" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
||||||
<path d="M18 17V9" />
|
<rect x="15" y="25" width="5" height="10" rx="2" fill="currentColor" />
|
||||||
<path d="M13 17V5" />
|
<rect x="24" y="17" width="5" height="18" rx="2" fill="currentColor" opacity="0.76" />
|
||||||
<path d="M8 17v-3" />
|
<rect x="33" y="10" width="5" height="25" rx="2" fill="currentColor" opacity="0.52" />
|
||||||
</template>
|
|
||||||
<template v-else-if="name === 'ai'">
|
|
||||||
<path d="M12 8V4H8" />
|
|
||||||
<rect width="16" height="12" x="4" y="8" rx="2" />
|
|
||||||
<path d="M2 14h2" />
|
|
||||||
<path d="M20 14h2" />
|
|
||||||
<path d="M15 13v2" />
|
|
||||||
<path d="M9 13v2" />
|
|
||||||
</template>
|
|
||||||
<template v-else-if="name === 'check'">
|
|
||||||
<path d="M20 6 9 17l-5-5" />
|
|
||||||
</template>
|
|
||||||
<template v-else-if="name === 'map-pin'">
|
|
||||||
<path d="M20 10c0 4.99-5.54 10.19-7.4 11.8a1 1 0 0 1-1.2 0C9.54 20.19 4 14.99 4 10a8 8 0 0 1 16 0" />
|
|
||||||
<circle cx="12" cy="10" r="3" />
|
|
||||||
</template>
|
|
||||||
<template v-else-if="name === 'user'">
|
|
||||||
<path d="M18 20a6 6 0 0 0-12 0" />
|
|
||||||
<circle cx="12" cy="10" r="4" />
|
|
||||||
</template>
|
|
||||||
<template v-else-if="name === 'menu'">
|
|
||||||
<path d="M4 6h16" />
|
|
||||||
<path d="M4 12h16" />
|
|
||||||
<path d="M4 18h16" />
|
|
||||||
</template>
|
|
||||||
<template v-else-if="name === 'x'">
|
|
||||||
<path d="M18 6 6 18" />
|
|
||||||
<path d="m6 6 12 12" />
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<circle cx="12" cy="12" r="10" />
|
<path d="M15 32c-4-3-6-8-4-13 2-7 8-11 15-10 8 1 13 7 13 15 0 6-3 11-8 14" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
||||||
<path d="M12 8v4" />
|
<path d="M19 25h4l3-8 3 16 3-8h4" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
<path d="M12 16h.01" />
|
<path d="M18 39h13" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
||||||
</template>
|
</template>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
defineProps({
|
|
||||||
size: { type: Number, default: 30 }
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<svg
|
|
||||||
class="icon-mark"
|
|
||||||
:width="size"
|
|
||||||
:height="size"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2.25"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path d="M12 6v4" />
|
|
||||||
<path d="M14 14h-4" />
|
|
||||||
<path d="M14 18h-4" />
|
|
||||||
<path d="M14 8h-4" />
|
|
||||||
<path d="M18 12h2a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
|
|
||||||
<path d="M18 22V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v18" />
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
@ -18,9 +18,7 @@ import SectionTitle from './SectionTitle.vue'
|
|||||||
</div>
|
</div>
|
||||||
<ul class="feature-list">
|
<ul class="feature-list">
|
||||||
<li v-for="item in feature.items" :key="item">
|
<li v-for="item in feature.items" :key="item">
|
||||||
<span class="check-dot">
|
<span class="check-dot">✓</span>
|
||||||
<IconMark name="check" :size="11" />
|
|
||||||
</span>
|
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
image: { type: String, default: '' },
|
|
||||||
label: { type: String, default: '二维码占位' },
|
label: { type: String, default: '二维码占位' },
|
||||||
small: { type: Boolean, default: false }
|
small: { type: Boolean, default: false }
|
||||||
})
|
})
|
||||||
@ -8,8 +7,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="qr-wrap" :class="{ 'qr-wrap--small': small }" role="img" :aria-label="props.label">
|
<div class="qr-wrap" :class="{ 'qr-wrap--small': small }" role="img" :aria-label="props.label">
|
||||||
<img v-if="image" class="qr-image" :src="image" :alt="props.label" />
|
<div class="qr-fake">
|
||||||
<div v-else class="qr-fake">
|
|
||||||
<span class="qr-corner tl"></span>
|
<span class="qr-corner tl"></span>
|
||||||
<span class="qr-corner tr"></span>
|
<span class="qr-corner tr"></span>
|
||||||
<span class="qr-corner bl"></span>
|
<span class="qr-corner bl"></span>
|
||||||
|
|||||||
@ -1,22 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { solutions } from '../data/homepage'
|
import { solutions } from '../data/homepage'
|
||||||
import QrPlaceholder from './QrPlaceholder.vue'
|
import QrPlaceholder from './QrPlaceholder.vue'
|
||||||
|
|
||||||
const activeKey = ref(solutions[0].key)
|
const activeKey = ref(solutions[0].key)
|
||||||
const isMobile = ref(false)
|
|
||||||
const activeIndex = computed(() => solutions.findIndex(item => item.key === activeKey.value))
|
const activeIndex = computed(() => solutions.findIndex(item => item.key === activeKey.value))
|
||||||
const activeSolution = computed(() => solutions[activeIndex.value] ?? solutions[0])
|
const activeSolution = computed(() => solutions[activeIndex.value] ?? solutions[0])
|
||||||
let mobileMediaQuery
|
|
||||||
|
|
||||||
const selectSolution = (index) => {
|
const selectSolution = (index) => {
|
||||||
if (!isMobile.value) return
|
|
||||||
activeKey.value = solutions[index]?.key ?? solutions[0].key
|
activeKey.value = solutions[index]?.key ?? solutions[0].key
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTabKeydown = (event, index) => {
|
const handleTabKeydown = (event, index) => {
|
||||||
if (!isMobile.value) return
|
|
||||||
|
|
||||||
const keyMap = {
|
const keyMap = {
|
||||||
ArrowRight: 1,
|
ArrowRight: 1,
|
||||||
ArrowDown: 1,
|
ArrowDown: 1,
|
||||||
@ -40,66 +35,31 @@ const handleTabKeydown = (event, index) => {
|
|||||||
selectSolution(nextIndex)
|
selectSolution(nextIndex)
|
||||||
document.getElementById(`solution-tab-${solutions[nextIndex].key}`)?.focus()
|
document.getElementById(`solution-tab-${solutions[nextIndex].key}`)?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateViewportMode = () => {
|
|
||||||
isMobile.value = mobileMediaQuery.matches
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
mobileMediaQuery = window.matchMedia('(max-width: 860px)')
|
|
||||||
updateViewportMode()
|
|
||||||
mobileMediaQuery.addEventListener('change', updateViewportMode)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
mobileMediaQuery?.removeEventListener('change', updateViewportMode)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="solution-section page-section page-section--compact">
|
<section class="solution-section page-section page-section--compact">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="solution-tabs solution-tabs--mobile" role="tablist" aria-label="版本切换">
|
<div class="solution-tabs" role="tablist" aria-label="版本切换">
|
||||||
<button
|
<button
|
||||||
v-for="(solution, index) in solutions"
|
v-for="(solution, index) in solutions"
|
||||||
:key="solution.key"
|
:key="solution.key"
|
||||||
:id="`solution-tab-${solution.key}`"
|
:id="`solution-tab-${solution.key}`"
|
||||||
type="button"
|
type="button"
|
||||||
:class="['solution-tab', { active: isMobile && solution.key === activeKey }]"
|
:class="['solution-tab', { active: solution.key === activeKey }]"
|
||||||
role="tab"
|
role="tab"
|
||||||
:aria-selected="isMobile ? solution.key === activeKey : undefined"
|
:aria-selected="solution.key === activeKey"
|
||||||
aria-controls="solution-panel"
|
aria-controls="solution-panel"
|
||||||
:tabindex="isMobile ? (solution.key === activeKey ? 0 : -1) : -1"
|
:tabindex="solution.key === activeKey ? 0 : -1"
|
||||||
@click="selectSolution(index)"
|
@click="selectSolution(index)"
|
||||||
@keydown="handleTabKeydown($event, index)"
|
@keydown="handleTabKeydown($event, index)"
|
||||||
>
|
>
|
||||||
{{ solution.tab }}
|
{{ solution.tab }}
|
||||||
</button>
|
</button>
|
||||||
<span v-if="isMobile" class="tab-indicator" :style="{ transform: `translateX(${activeIndex * 100}%)` }"></span>
|
<span class="tab-indicator" :style="{ transform: `translateX(${activeIndex * 100}%)` }"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="solution-grid solution-grid--desktop">
|
<div class="solution-grid">
|
||||||
<article
|
|
||||||
v-for="solution in solutions"
|
|
||||||
:key="solution.key"
|
|
||||||
:id="`solution-card-${solution.key}`"
|
|
||||||
class="solution-card"
|
|
||||||
>
|
|
||||||
<div class="solution-card-top">
|
|
||||||
<h3>{{ solution.title }}</h3>
|
|
||||||
</div>
|
|
||||||
<ol v-if="solution.key !== 'personal'" class="solution-points">
|
|
||||||
<li v-for="point in solution.points" :key="point">{{ point }}</li>
|
|
||||||
</ol>
|
|
||||||
<p v-else class="solution-personal-text">{{ solution.points[0] }}</p>
|
|
||||||
<div class="solution-qr-row">
|
|
||||||
<span v-if="solution.key !== 'personal'">微信长按扫码咨询</span>
|
|
||||||
<QrPlaceholder :image="solution.qrImage" :label="solution.qrLabel" :title="`${solution.tab}咨询二维码`" />
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="solution-panel solution-panel--mobile">
|
|
||||||
<article
|
<article
|
||||||
id="solution-panel"
|
id="solution-panel"
|
||||||
:key="activeSolution.key"
|
:key="activeSolution.key"
|
||||||
@ -109,6 +69,7 @@ onBeforeUnmount(() => {
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div class="solution-card-top">
|
<div class="solution-card-top">
|
||||||
|
<span class="solution-kicker">{{ activeSolution.tab }}</span>
|
||||||
<h3>{{ activeSolution.title }}</h3>
|
<h3>{{ activeSolution.title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<ol v-if="activeSolution.key !== 'personal'" class="solution-points">
|
<ol v-if="activeSolution.key !== 'personal'" class="solution-points">
|
||||||
@ -116,8 +77,8 @@ onBeforeUnmount(() => {
|
|||||||
</ol>
|
</ol>
|
||||||
<p v-else class="solution-personal-text">{{ activeSolution.points[0] }}</p>
|
<p v-else class="solution-personal-text">{{ activeSolution.points[0] }}</p>
|
||||||
<div class="solution-qr-row">
|
<div class="solution-qr-row">
|
||||||
<span v-if="activeSolution.key !== 'personal'">微信长按扫码咨询</span>
|
<span>具体咨询:</span>
|
||||||
<QrPlaceholder :image="activeSolution.qrImage" :label="activeSolution.qrLabel" :title="`${activeSolution.tab}咨询二维码`" />
|
<QrPlaceholder :label="activeSolution.qrLabel" :title="`${activeSolution.tab}咨询二维码`" />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,23 +9,23 @@ export const navItems = [
|
|||||||
export const features = [
|
export const features = [
|
||||||
{
|
{
|
||||||
icon: 'user-flow',
|
icon: 'user-flow',
|
||||||
title: '流量+数据获客',
|
title: '前端运营获客',
|
||||||
items: ['公众号/短视频引流', '个人码/活动码/团队码/台签', '快速认证建档&关联病历', '历史门诊住院数据挖掘', '科室靶点患者发现']
|
items: ['公众号/视频号互动引流', '个人号承接', '团队微信池', '活动裂变', '企业微信能力融合']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'clipboard',
|
icon: 'clipboard',
|
||||||
title: '需求双向精准对接',
|
title: '中端服务承接',
|
||||||
items: ['医护服务团队配置', '医患双向服务授权', '患者档案画像提取', '患者入组及管理路径', '患者档案共享/转移']
|
items: ['患者建档、管理画像', '管理服务流程', '日诊断评估', '日访持续执行', '宣教、随访、复诊']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'chart',
|
icon: 'chart',
|
||||||
title: '协同规范专业管理',
|
title: '后端跟进管理',
|
||||||
items: ['专科管理服务知识库', '管理路径模版/SOP引擎', '任务交办/执行/监管', '平台驾驶舱/主题分析', '员工服务积分/客户满意度']
|
items: ['复诊/随访流程分组', 'SOP任务引擎', '管理协作闭环', '任务分配下达', '运营管理支撑']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'ai',
|
icon: 'ai',
|
||||||
title: 'AI+人工专业服务',
|
title: '全端数智赋能',
|
||||||
items: ['患者全息档案', '平台知识库/知识图谱', 'Youcan Health模型', 'AI服务助手(八大场景)', '柚康服务经理']
|
items: ['体效营销体系及引擎', '话术智能客服训练', 'Youcan AI大模型', 'AI客服助手-小柚', '精细化客服多图层']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -35,51 +35,35 @@ export const solutions = [
|
|||||||
tab: '机构云服务版',
|
tab: '机构云服务版',
|
||||||
title: '版本优势',
|
title: '版本优势',
|
||||||
points: ['快速开通、快速培训、快速上线', '按年付费、费用低', '享受平台功能持续升级服务', '支持管理患者档案数据的导出'],
|
points: ['快速开通、快速培训、快速上线', '按年付费、费用低', '享受平台功能持续升级服务', '支持管理患者档案数据的导出'],
|
||||||
qrLabel: '柚康企业微信客服二维码',
|
qrLabel: '柚康企业微信客服二维码'
|
||||||
qrImage: '/assets/客服.png'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'local',
|
key: 'local',
|
||||||
tab: '机构部署版',
|
tab: '机构本地部署版',
|
||||||
title: '版本优势',
|
title: '版本优势',
|
||||||
points: ['支持本地服务器部署及内部业务系统对接', '数据本地化管理', '大规模长期使用,性价比高'],
|
points: ['支持本地服务器部署及内部业务系统对接', '数据本地化管理', '大规模长期使用,性价比高'],
|
||||||
qrLabel: '柚康企业微信客服二维码',
|
qrLabel: '柚康企业微信客服二维码'
|
||||||
qrImage: '/assets/客服.png'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'personal',
|
key: 'personal',
|
||||||
tab: '个人云服务版',
|
tab: '个人云服务版',
|
||||||
title: '医生/护士/助理免费开通',
|
title: '医生/护士/助理免费开通',
|
||||||
points: ['如果您是医生、护士、医生助理,现在即可免费开通使用:'],
|
points: ['如果您是医生、护士、医生助理,现在即可免费开通使用:'],
|
||||||
qrLabel: '柚助手小程序',
|
qrLabel: '柚助二维码'
|
||||||
qrImage: '/assets/小程序码.png'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export const caseCategories = ['国家医学中心', '公立医院', '妇幼保健客户', '医疗集团客户']
|
export const caseCategories = ['国家医学中心', '公立医院', '妇幼保健宫', '医疗集团客户']
|
||||||
|
|
||||||
export const cases = [
|
export const cases = [
|
||||||
{ logo: '/assets/国家骨科医学中心.webp ', logoFit: 'round', name: '国家骨科医学中心', category: '国家医学中心' },
|
{ short: '骨', name: '国家骨科医学中心\n上海市第六人民医院', tone: 'blue', category: '国家医学中心' },
|
||||||
{ logo: '/assets/国家呼吸医学中心.png', logoFit: 'wide', name: '国家呼吸医学中心', category: '国家医学中心' },
|
{ short: '呼', name: '国家呼吸医学中心\n广州医科大学附属第一医院', tone: 'orange', category: '国家医学中心' },
|
||||||
{ logo: '/assets/广附一.jpg', logoFit: 'round', name: '广州医科大学附属第一医院', category: '公立医院' },
|
{ short: '广', name: '广医口腔', tone: 'blue', category: '公立医院' },
|
||||||
{ logo: '/assets/上海六院.png', logoFit: 'round', name: '上海市第六人民医院', category: '公立医院' },
|
{ short: '广', name: '广医口腔', tone: 'blue', category: '公立医院' },
|
||||||
{ logo: '/assets/广医口腔logo.png', logoFit: 'round', name: '广州医科大学附属口腔医院', category: '公立医院' },
|
{ short: '丹', name: '丹红互联网医院', tone: 'red', category: '公立医院' },
|
||||||
{ logo: '/assets/复旦大学附属中山医院吴淞医院.png', logoFit: 'round', name: '复旦大学附属中山医院吴淞医院', category: '公立医院' },
|
{ short: '十', name: '圣德', tone: 'green', category: '妇幼保健宫' },
|
||||||
{ logo: '/assets/衡阳县中医医院.png', logoFit: 'round', name: '衡阳县中医医院', category: '公立医院' },
|
{ short: '中', name: '中信医疗', tone: 'red', category: '医疗集团客户' },
|
||||||
{ logo: '/assets/上海长宁区精神卫生中心.png', logoFit: 'round', name: '上海长宁区精神卫生中心', category: '公立医院' },
|
{ short: 'Z', name: '浙江震元', tone: 'cyan', category: '医疗集团客户' },
|
||||||
{ logo: '/assets/杭州整形医院.jpg', logoFit: 'round', name: '杭州整形医院', category: '公立医院' },
|
{ short: '合', name: '合禾', tone: 'green', category: '医疗集团客户' },
|
||||||
{ logo: '/assets/绍兴市震元堂中医院.jpg', logoFit: 'round', name: '绍兴市震元堂中医院', category: '公立医院' },
|
{ short: '…', name: '更多案例\n……', tone: 'gray', category: '医疗集团客户' }
|
||||||
{ logo: '/assets/杭州嘉誉护理院有限公司.jpg', logoFit: 'round', name: '杭州嘉誉护理院', category: '公立医院' },
|
|
||||||
{ logo: '/assets/杭州钱塘新区和禾康复医院.png', logoFit: 'wide', name: '杭州钱塘新区和禾康复医院', category: '公立医院' },
|
|
||||||
{ logo: '/assets/杭州市上城区九堡街道社区卫生服务中心.png', logoFit: 'round', name: '杭州市上城区九堡街道社区卫生服务中心', category: '公立医院' },
|
|
||||||
{ logo: '/assets/奉化妇保院.jpg', name: '宁波市奉化区妇幼保健院', category: '妇幼保健客户' },
|
|
||||||
{ logo: '/assets/邹平市妇保院.jpg', logoFit: 'round', name: '邹平市妇幼保健院', category: '妇幼保健客户' },
|
|
||||||
{ logo: '/assets/浦江妇幼保健医院.png', logoFit: 'round', name: '浦江妇幼保健医院', category: '妇幼保健客户' },
|
|
||||||
{ logo: '/assets/磐安县妇幼保健院.jpg', logoFit: 'round', name: '磐安县妇幼保健院', category: '妇幼保健客户' },
|
|
||||||
{ logo: '/assets/杭州仁德.png', name: '杭州仁德妇产医院', category: '妇幼保健客户' },
|
|
||||||
{ logo: '/assets/中信医疗logo.jpg', logoFit: 'wide', name: '中信医疗健康产业集团', category: '医疗集团客户' },
|
|
||||||
{ logo: '/assets/震元.png', logoFit: 'wide', name: '浙江震元医药连锁', category: '医疗集团客户' },
|
|
||||||
{ logo: '/assets/圣德logo.png', logoFit: 'wide', name: '上海圣德颐和医疗管理集团', category: '医疗集团客户' },
|
|
||||||
// { logo: '/assets/杭州钱塘新区和禾康复医院.png', logoFit: 'wide', name: '杭州钱塘新区和禾康复医院', category: '医疗集团客户' },
|
|
||||||
// { logo: '', name: '中丹红互联网医院', category: '医疗集团客户' },
|
|
||||||
]
|
]
|
||||||
|
|||||||
15
src/main.js
@ -2,19 +2,4 @@ import { createApp } from 'vue'
|
|||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import './styles/theme.css'
|
import './styles/theme.css'
|
||||||
|
|
||||||
if ('scrollRestoration' in history) {
|
|
||||||
history.scrollRestoration = 'manual'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!window.location.hash) {
|
|
||||||
const scrollToTop = () => window.scrollTo(0, 0)
|
|
||||||
|
|
||||||
scrollToTop()
|
|
||||||
requestAnimationFrame(scrollToTop)
|
|
||||||
window.addEventListener('load', scrollToTop, { once: true })
|
|
||||||
window.addEventListener('pageshow', scrollToTop, { once: true })
|
|
||||||
window.setTimeout(scrollToTop, 120)
|
|
||||||
window.setTimeout(scrollToTop, 360)
|
|
||||||
}
|
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
createApp(App).mount('#app')
|
||||||
|
|||||||