Compare commits

..

10 Commits

44 changed files with 861 additions and 178 deletions

View File

@ -4,7 +4,17 @@
<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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
public/assets/客服.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
public/assets/广附一.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
public/assets/服务商.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
public/assets/震元.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,10 +1,20 @@
<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
@ -55,12 +65,20 @@ const handleCategoryKeydown = (event, index) => {
@click="selectCategory(category)" @click="selectCategory(category)"
@keydown="handleCategoryKeydown($event, index)" @keydown="handleCategoryKeydown($event, index)"
> >
{{ category }} <span class="case-filter-text">
<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', `case-logo--${item.tone}`]">{{ item.short }}</div> <div :class="['case-logo-slot', item.logoFit && `case-logo-slot--${item.logoFit}`, { 'case-logo-slot--placeholder': !item.logo }]">
<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>

View File

@ -1,4 +1,5 @@
<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>
@ -15,24 +16,18 @@ import SectionTitle from './SectionTitle.vue'
rel="noopener noreferrer" rel="noopener noreferrer"
aria-label="在地图中打开办公地址" aria-label="在地图中打开办公地址"
> >
<span class="address-icon">📍</span> <span class="address-icon">
<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">
<div class="manager-copy"> <QrPlaceholder image="/assets/王经理二维码.jpg" label="微信长按扫码联系王经理" />
<span class="manager-icon">👤</span>
<p><strong>客户成功经理王经理</strong><br /></p>
</div>
<QrPlaceholder label="王经理个人码" small />
</article> </article>
<article class="manager-card"> <article class="manager-card">
<div class="manager-copy"> <QrPlaceholder image="/assets/罗经理二维码.png" label="微信长按扫码联系罗经理" />
<span class="manager-icon">👤</span>
<p><strong>客户成功经理罗经理</strong><br /></p>
</div>
<QrPlaceholder label="罗经理个人码" small />
</article> </article>
</div> </div>
</div> </div>

View File

@ -2,13 +2,28 @@
<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 src="/assets/logo.png" alt="柚康 logo" /> <img class="footer-logo" src="/assets/logo.png" alt="柚康科技 logo" />
<span> <span>
<strong>柚康</strong> <strong>柚康科技</strong>
<em>企业微信服务商标识</em> <i class="brand-separator" aria-hidden="true"></i>
<img class="brand-service" src="/assets/服务商.png" alt="企业微信服务商" />
</span> </span>
</div> </div>
<p>© 2026 杭州柚康科技有限公司 版权所有</p> <div class="footer-legal" aria-label="网站备案信息">
<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>

View File

@ -1,6 +1,7 @@
<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)
@ -84,11 +85,12 @@ 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>
<em>企业微信服务商标识</em> <i class="brand-separator" aria-hidden="true"></i>
<img class="brand-service" src="/assets/服务商.png" alt="企业微信服务商" />
</span> </span>
</a> </a>
<button <button
@ -99,9 +101,7 @@ onBeforeUnmount(() => {
:aria-label="isMenuOpen ? '收起主导航' : '展开主导航'" :aria-label="isMenuOpen ? '收起主导航' : '展开主导航'"
@click="toggleMenu" @click="toggleMenu"
> >
<span></span> <IconMark :name="isMenuOpen ? 'x' : 'menu'" :size="24" />
<span></span>
<span></span>
</button> </button>
<nav <nav
id="main-navigation" id="main-navigation"

View File

@ -4,8 +4,10 @@
<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">
<p class="eyebrow">医疗服务数智化升级服务商</p> <h1>
<h1>医疗行业患者全周期<br />管理服务数智化升级服务</h1> <span class="hero-title-line hero-title-nowrap">患者全周期管理服务</span>
<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>
@ -18,8 +20,18 @@
<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>

View File

@ -6,27 +6,68 @@ defineProps({
</script> </script>
<template> <template>
<svg class="icon-mark" :width="size" :height="size" viewBox="0 0 48 48" fill="none" aria-hidden="true"> <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"
>
<template v-if="name === 'user-flow'"> <template v-if="name === 'user-flow'">
<circle cx="24" cy="16" r="7" stroke="currentColor" stroke-width="3" /> <path d="M18 21a8 8 0 0 0-16 0" />
<path d="M12 37c2.4-8.2 7-12 12-12s9.6 3.8 12 12" stroke="currentColor" stroke-width="3" stroke-linecap="round" /> <circle cx="10" cy="8" r="5" />
<path d="M9 24h5m20 0h5" stroke="currentColor" stroke-width="3" stroke-linecap="round" /> <path d="M22 20c0-3.37-2-6.5-4.5-8" />
<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 x="12" y="10" width="24" height="30" rx="5" stroke="currentColor" stroke-width="3" /> <rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
<path d="M18 8h12l2 5H16l2-5Z" fill="currentColor" /> <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 23h12M18 31h8" stroke="currentColor" stroke-width="3" stroke-linecap="round" /> <path d="M9 12h6" />
<path d="M9 16h6" />
</template> </template>
<template v-else-if="name === 'chart'"> <template v-else-if="name === 'chart'">
<path d="M12 38h26" stroke="currentColor" stroke-width="3" stroke-linecap="round" /> <path d="M3 3v18h18" />
<rect x="15" y="25" width="5" height="10" rx="2" fill="currentColor" /> <path d="M18 17V9" />
<rect x="24" y="17" width="5" height="18" rx="2" fill="currentColor" opacity="0.76" /> <path d="M13 17V5" />
<rect x="33" y="10" width="5" height="25" rx="2" fill="currentColor" opacity="0.52" /> <path d="M8 17v-3" />
</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>
<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" /> <circle cx="12" cy="12" r="10" />
<path d="M19 25h4l3-8 3 16 3-8h4" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" /> <path d="M12 8v4" />
<path d="M18 39h13" stroke="currentColor" stroke-width="3" stroke-linecap="round" /> <path d="M12 16h.01" />
</template> </template>
</svg> </svg>
</template> </template>

View File

@ -0,0 +1,27 @@
<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>

View File

@ -18,7 +18,9 @@ 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> <span class="check-dot">
<IconMark name="check" :size="11" />
</span>
{{ item }} {{ item }}
</li> </li>
</ul> </ul>

View File

@ -1,5 +1,6 @@
<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 }
}) })
@ -7,7 +8,8 @@ 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">
<div class="qr-fake"> <img v-if="image" class="qr-image" :src="image" :alt="props.label" />
<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>

View File

@ -1,17 +1,22 @@
<script setup> <script setup>
import { computed, ref } from 'vue' import { computed, onBeforeUnmount, onMounted, 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,
@ -35,31 +40,66 @@ 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" role="tablist" aria-label="版本切换"> <div class="solution-tabs solution-tabs--mobile" 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: solution.key === activeKey }]" :class="['solution-tab', { active: isMobile && solution.key === activeKey }]"
role="tab" role="tab"
:aria-selected="solution.key === activeKey" :aria-selected="isMobile ? solution.key === activeKey : undefined"
aria-controls="solution-panel" aria-controls="solution-panel"
:tabindex="solution.key === activeKey ? 0 : -1" :tabindex="isMobile ? (solution.key === activeKey ? 0 : -1) : -1"
@click="selectSolution(index)" @click="selectSolution(index)"
@keydown="handleTabKeydown($event, index)" @keydown="handleTabKeydown($event, index)"
> >
{{ solution.tab }} {{ solution.tab }}
</button> </button>
<span class="tab-indicator" :style="{ transform: `translateX(${activeIndex * 100}%)` }"></span> <span v-if="isMobile" class="tab-indicator" :style="{ transform: `translateX(${activeIndex * 100}%)` }"></span>
</div> </div>
<div class="solution-grid"> <div class="solution-grid solution-grid--desktop">
<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"
@ -69,7 +109,6 @@ const handleTabKeydown = (event, index) => {
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">
@ -77,8 +116,8 @@ const handleTabKeydown = (event, index) => {
</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>具体咨询</span> <span v-if="activeSolution.key !== 'personal'">微信长按扫码咨询</span>
<QrPlaceholder :label="activeSolution.qrLabel" :title="`${activeSolution.tab}咨询二维码`" /> <QrPlaceholder :image="activeSolution.qrImage" :label="activeSolution.qrLabel" :title="`${activeSolution.tab}咨询二维码`" />
</div> </div>
</article> </article>
</div> </div>

View File

@ -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: '全端数智赋能', title: 'AI+人工专业服务',
items: ['体效营销体系及引擎', '话术智能客服训练', 'Youcan AI大模型', 'AI客服助手-小柚', '精细化客服多图层'] items: ['患者全息档案', '平台知识库/知识图谱', 'Youcan Health模型', 'AI服务助手(八大场景)', '柚康服务经理']
} }
] ]
@ -35,35 +35,51 @@ 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 = [
{ short: '骨', name: '国家骨科医学中心\n上海市第六人民医院', tone: 'blue', category: '国家医学中心' }, { logo: '/assets/国家骨科医学中心.webp ', logoFit: 'round', name: '国家骨科医学中心', category: '国家医学中心' },
{ short: '呼', name: '国家呼吸医学中心\n广州医科大学附属第一医院', tone: 'orange', category: '国家医学中心' }, { logo: '/assets/国家呼吸医学中心.png', logoFit: 'wide', name: '国家呼吸医学中心', category: '国家医学中心' },
{ short: '广', name: '广医口腔', tone: 'blue', category: '公立医院' }, { logo: '/assets/广附一.jpg', logoFit: 'round', name: '广州医科大学附属第一医院', category: '公立医院' },
{ short: '广', name: '广医口腔', tone: 'blue', category: '公立医院' }, { logo: '/assets/上海六院.png', logoFit: 'round', name: '上海市第六人民医院', category: '公立医院' },
{ short: '丹', name: '丹红互联网医院', tone: 'red', category: '公立医院' }, { logo: '/assets/广医口腔logo.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/上海长宁区精神卫生中心.png', 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/杭州嘉誉护理院有限公司.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: '医疗集团客户' },
] ]

View File

@ -2,4 +2,19 @@ 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')

File diff suppressed because it is too large Load Diff