补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
849 行
22 KiB
Plaintext
849 行
22 KiB
Plaintext
<template>
|
||
<view class="case-page" :class="pageRtlClass" :style="pageTheme">
|
||
<!-- 顶部受控局部极光微光晕 (面积 < 8%) -->
|
||
<view class="ambient-aurora-glow"></view>
|
||
|
||
<!-- 骨架屏加载态 -->
|
||
<view v-if="state.loading" class="case-skeleton">
|
||
<view class="skeleton-badge"></view>
|
||
<view class="skeleton-line line-title"></view>
|
||
<view class="skeleton-media"></view>
|
||
<view class="skeleton-grid">
|
||
<view class="skeleton-kpi-card"></view>
|
||
<view class="skeleton-kpi-card"></view>
|
||
<view class="skeleton-kpi-card"></view>
|
||
</view>
|
||
<view class="skeleton-card"></view>
|
||
</view>
|
||
|
||
<!-- 异常与未找到空状态 -->
|
||
<view v-else-if="state.empty" class="case-empty-wrap">
|
||
<view class="case-empty-card">
|
||
<view class="empty-badge">
|
||
<text class="empty-badge-icon">!</text>
|
||
</view>
|
||
<text class="case-empty-title">{{ t('empty') }}</text>
|
||
<text class="case-empty-desc">{{ t('emptyDesc') }}</text>
|
||
<button class="primary-cta-btn empty-back-btn" @click="handleBack">
|
||
<text class="primary-cta-btn-text">{{ t('back') }}</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 核心案例内容流 (Bento Grid 便当盒模组) -->
|
||
<scroll-view v-else class="case-scroll" :scroll-y="true">
|
||
<view class="case-content">
|
||
<!-- 1. 案例头部徽标与主标题展台 -->
|
||
<view class="case-header-card">
|
||
<view class="case-badge-pill">
|
||
<view class="case-badge-dot"></view>
|
||
<text class="case-badge-text">{{ headerBadgeText }}</text>
|
||
</view>
|
||
<text class="case-title">{{ state.title }}</text>
|
||
|
||
<!-- 案例元信息胶囊行 (若配置) -->
|
||
<view v-if="hasMetaTags" class="case-meta-row">
|
||
<view v-if="state.client && state.client !== ''" class="case-meta-pill">
|
||
<text class="case-meta-label">{{ t('clientLabel') }}:</text>
|
||
<text class="case-meta-val">{{ state.client }}</text>
|
||
</view>
|
||
<view v-if="state.industry && state.industry !== ''" class="case-meta-pill">
|
||
<text class="case-meta-label">{{ t('industryLabel') }}:</text>
|
||
<text class="case-meta-val">{{ state.industry }}</text>
|
||
</view>
|
||
<view v-if="state.region && state.region !== ''" class="case-meta-pill">
|
||
<text class="case-meta-label">{{ t('regionLabel') }}:</text>
|
||
<text class="case-meta-val">{{ state.region }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 2. 原生视频播放器展台 (若配置视频直链) -->
|
||
<view v-if="state.videoUrl !== ''" class="case-video-wrapper">
|
||
<video
|
||
class="case-video-player"
|
||
:src="state.videoUrl"
|
||
:poster="state.images.length > 0 ? state.images[0] : ''"
|
||
:controls="true"
|
||
preload="metadata"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 3. 多图画廊轮播 / 单图视觉展台 (无视频时或图集展示) -->
|
||
<view v-if="state.images.length > 0 && state.videoUrl === ''" class="gallery-wrapper">
|
||
<swiper
|
||
v-if="state.images.length > 1"
|
||
class="case-swiper"
|
||
:indicator-dots="true"
|
||
indicator-color="rgba(0, 136, 255, 0.2)"
|
||
indicator-active-color="#0088FF"
|
||
:autoplay="false"
|
||
circular
|
||
>
|
||
<swiper-item v-for="img in state.images" :key="img">
|
||
<image class="case-media" :src="img" mode="aspectFill" />
|
||
</swiper-item>
|
||
</swiper>
|
||
<image
|
||
v-else-if="state.images.length === 1"
|
||
class="case-media case-media-single"
|
||
:src="state.images[0]"
|
||
mode="aspectFill"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 3. 核心商业化成效指标卡 (Bento KPI Cards) -->
|
||
<view v-if="visibleKpis.length > 0" class="bento-card kpi-section">
|
||
<view class="bento-card-header">
|
||
<view class="bento-header-left">
|
||
<view class="bento-tag-bar tag-primary"></view>
|
||
<text class="bento-card-title">{{ t('kpis') }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="kpi-grid">
|
||
<view v-for="kpi in visibleKpis" :key="kpi.label" class="kpi-card">
|
||
<text class="kpi-value">{{ kpi.value }}</text>
|
||
<text class="kpi-label">{{ kpi.label }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 4. 客户挑战与背景卡 (Bento Challenge Card) -->
|
||
<view v-if="state.challenge && state.challenge !== ''" class="bento-card challenge-section">
|
||
<view class="bento-card-header">
|
||
<view class="bento-header-left">
|
||
<view class="bento-tag-bar tag-warning"></view>
|
||
<text class="bento-card-title">{{ t('challenge') }}</text>
|
||
</view>
|
||
</view>
|
||
<text class="bento-card-body">{{ state.challenge }}</text>
|
||
</view>
|
||
|
||
<!-- 5. 落地技术架构与解决方案卡 (Bento Solution / Overview Card) -->
|
||
<view v-if="state.solution && state.solution !== ''" class="bento-card solution-section">
|
||
<view class="bento-card-header">
|
||
<view class="bento-header-left">
|
||
<view class="bento-tag-bar tag-success"></view>
|
||
<text class="bento-card-title">{{ t('solution') }}</text>
|
||
</view>
|
||
</view>
|
||
<text class="bento-card-body">{{ state.solution }}</text>
|
||
</view>
|
||
|
||
<!-- 若无独立结构化拆分,但有正文内容,呈现为综合方案卡 -->
|
||
<view
|
||
v-else-if="state.content && state.content !== ''"
|
||
class="bento-card overview-section"
|
||
>
|
||
<view class="bento-card-header">
|
||
<view class="bento-header-left">
|
||
<view class="bento-tag-bar tag-primary"></view>
|
||
<text class="bento-card-title">{{ t('overview') }}</text>
|
||
</view>
|
||
</view>
|
||
<text class="bento-card-body">{{ state.content }}</text>
|
||
</view>
|
||
|
||
<!-- 6. 商务白皮书与演示视频数字资产卡 (Bento Resources) -->
|
||
<view v-if="hasResources" class="bento-card resource-section">
|
||
<view class="bento-card-header">
|
||
<view class="bento-header-left">
|
||
<view class="bento-tag-bar tag-ai"></view>
|
||
<text class="bento-card-title">{{ t('resources') }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="resource-list">
|
||
<!-- PDF 白皮书:有 url 才渲染可点行,不露裸 URL -->
|
||
<view
|
||
v-if="state.pdfUrl !== ''"
|
||
class="ext-link-row"
|
||
@click="handleOpenLink(state.pdfUrl)"
|
||
>
|
||
<view class="ext-link-main">
|
||
<view class="ext-badge ext-badge-pdf">
|
||
<text class="ext-badge-text">{{ t('pdf') }}</text>
|
||
</view>
|
||
<text class="ext-link-action">{{ t('pdfAction') }}</text>
|
||
</view>
|
||
<view class="ext-arrow-box">
|
||
<text class="ext-arrow-icon">↗</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- YouTube/Vimeo:有 url 才渲染可点行,不露裸 URL -->
|
||
<view
|
||
v-if="state.youtubeUrl !== ''"
|
||
class="ext-link-row"
|
||
@click="handleOpenLink(state.youtubeUrl)"
|
||
>
|
||
<view class="ext-link-main">
|
||
<view class="ext-badge ext-badge-video">
|
||
<text class="ext-badge-text">{{ t('youtube') }}</text>
|
||
</view>
|
||
<text class="ext-link-action">{{ t('videoAction') }}</text>
|
||
</view>
|
||
<view class="ext-arrow-box">
|
||
<text class="ext-arrow-icon">↗</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部安全垫高,避免被常驻主 CTA 按钮遮挡 -->
|
||
<view class="case-bottom-spacer"></view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 单屏唯一定位主 CTA (即时咨询方案主理人) -->
|
||
<view v-if="!state.loading && !state.empty" class="case-cta-bar">
|
||
<button class="primary-cta-btn" @click="handleConsult">
|
||
<text class="primary-cta-btn-text">{{ t('cta') }}</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { utsLightColors, utsRadius, utsSpacing, utsTypography } from '../../common/tokens.uts';
|
||
import { getCurrentLocale, isRtl } from '../../common/locale.uts';
|
||
import {
|
||
CaseDetailCopyKey,
|
||
CaseKpi,
|
||
createCaseDetailState,
|
||
applyCaseDetailOptions,
|
||
loadCaseDetail,
|
||
openCaseExchange,
|
||
goCaseBack,
|
||
openCaseExternalLink,
|
||
resolveCaseDetailText,
|
||
CaseDetailPageOptions,
|
||
} from './useCaseDetail.uts';
|
||
|
||
const pageTheme: Record<string, string> = {
|
||
'--oc-primary': utsLightColors.primary,
|
||
'--oc-primary-gradient': utsLightColors.primaryGradient,
|
||
'--oc-primary-soft': utsLightColors.primarySoft,
|
||
'--oc-aurora': utsLightColors.aurora,
|
||
'--oc-ai': '#7C5CFF',
|
||
'--oc-bg': utsLightColors.bg,
|
||
'--oc-surface': utsLightColors.surface,
|
||
'--oc-border': utsLightColors.border,
|
||
'--oc-text': utsLightColors.text,
|
||
'--oc-text-2': utsLightColors.text2,
|
||
'--oc-success': utsLightColors.success,
|
||
'--oc-warning': utsLightColors.warning,
|
||
'--oc-error': utsLightColors.error,
|
||
'--oc-radius-btn': utsRadius.button,
|
||
'--oc-radius-card': utsRadius.card,
|
||
'--oc-radius-modal': utsRadius.modal,
|
||
'--oc-radius-pill': utsRadius.pill,
|
||
'--oc-shadow-card': utsTypography.shadowCard,
|
||
'--oc-shadow-cta': utsTypography.shadowCta,
|
||
'--oc-shadow-float': utsTypography.shadowFloat,
|
||
'--oc-shadow': utsTypography.shadow,
|
||
'--oc-space-4': utsSpacing.space4,
|
||
'--oc-space-8': utsSpacing.space8,
|
||
'--oc-space-12': utsSpacing.space12,
|
||
'--oc-space-16': utsSpacing.space16,
|
||
'--oc-space-20': utsSpacing.space20,
|
||
'--oc-space-24': utsSpacing.space24,
|
||
'--oc-space-32': utsSpacing.space32,
|
||
};
|
||
|
||
const state = reactive(createCaseDetailState());
|
||
|
||
const pageRtlClass = computed((): string => {
|
||
return isRtl(getCurrentLocale()) ? 'case-rtl' : '';
|
||
});
|
||
|
||
const headerBadgeText = computed((): string => {
|
||
return state.blockType === 'video' ? t('videoBadge') : t('badge');
|
||
});
|
||
|
||
const hasMetaTags = computed((): boolean => {
|
||
return (
|
||
(state.client != null && state.client !== '') ||
|
||
(state.industry != null && state.industry !== '') ||
|
||
(state.region != null && state.region !== '')
|
||
);
|
||
});
|
||
|
||
const hasResources = computed((): boolean => {
|
||
return state.pdfUrl !== '' || state.youtubeUrl !== '';
|
||
});
|
||
|
||
const visibleKpis = computed((): CaseKpi[] => {
|
||
const list = state.kpis;
|
||
if (list.length <= 3) {
|
||
return list;
|
||
}
|
||
return list.slice(0, 3);
|
||
});
|
||
|
||
function t(key: CaseDetailCopyKey): string {
|
||
return resolveCaseDetailText(key);
|
||
}
|
||
|
||
function handleConsult(): void {
|
||
openCaseExchange(state.cardId);
|
||
}
|
||
|
||
function handleBack(): void {
|
||
goCaseBack();
|
||
}
|
||
|
||
function handleOpenLink(url: string): void {
|
||
openCaseExternalLink(url);
|
||
}
|
||
|
||
onLoad((options: CaseDetailPageOptions) => {
|
||
applyCaseDetailOptions(state, options);
|
||
loadCaseDetail(state);
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.case-page {
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
background-color: var(--oc-bg, #F5F8FC);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 顶部受控极光微光晕 (面积 < 8%) */
|
||
.ambient-aurora-glow {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 180px;
|
||
background: radial-gradient(circle at 50% -30%, rgba(0, 210, 255, 0.12) 0%, rgba(0, 136, 255, 0.06) 45%, rgba(99, 102, 241, 0) 100%);
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
|
||
/* RTL 镜像适配 */
|
||
.case-rtl {
|
||
direction: rtl;
|
||
text-align: right;
|
||
}
|
||
.case-rtl .case-header-card,
|
||
.case-rtl .bento-card {
|
||
text-align: right;
|
||
}
|
||
.case-rtl .bento-header-left,
|
||
.case-rtl .case-meta-row,
|
||
.case-rtl .ext-link-main {
|
||
flex-direction: row;
|
||
justify-content: flex-start;
|
||
}
|
||
.case-rtl .ext-arrow-icon {
|
||
display: inline-block;
|
||
transform: scaleX(-1);
|
||
}
|
||
|
||
.case-scroll {
|
||
position: relative;
|
||
flex: 1;
|
||
z-index: 1;
|
||
}
|
||
|
||
.case-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding-inline: var(--oc-space-16, 16px);
|
||
padding-block-start: var(--oc-space-16, 16px);
|
||
padding-block-end: var(--oc-space-16, 16px);
|
||
max-width: 720px;
|
||
width: 100%;
|
||
margin-inline: auto;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 1. 头部主卡片与徽标 */
|
||
.case-header-card {
|
||
background-color: var(--oc-surface, #FFFFFF);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
box-shadow: var(--oc-shadow-card, 0 8px 30px rgba(0, 0, 0, 0.04));
|
||
padding: var(--oc-space-20, 20px);
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-block-end: var(--oc-space-16, 16px);
|
||
}
|
||
|
||
.case-badge-pill {
|
||
align-self: flex-start;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
padding: 4px 10px;
|
||
background-color: var(--oc-primary-soft, #EBF5FF);
|
||
border-radius: var(--oc-radius-pill, 9999px);
|
||
margin-block-end: var(--oc-space-12, 12px);
|
||
}
|
||
|
||
.case-badge-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 3px;
|
||
background-color: var(--oc-primary, #0088FF);
|
||
margin-inline-end: 6px;
|
||
}
|
||
|
||
.case-badge-text {
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
color: var(--oc-primary, #0088FF);
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.case-title {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: var(--oc-text, #101828);
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.case-meta-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin-block-start: var(--oc-space-12, 12px);
|
||
}
|
||
|
||
.case-meta-pill {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
padding: 3px 8px;
|
||
background-color: var(--oc-bg, #F5F8FC);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.case-meta-label {
|
||
font-size: 11px;
|
||
color: var(--oc-text-2, #64748B);
|
||
margin-inline-end: 4px;
|
||
}
|
||
|
||
.case-meta-val {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: var(--oc-text, #101828);
|
||
}
|
||
|
||
/* 原生视频演示展台 */
|
||
.case-video-wrapper {
|
||
margin-block-end: var(--oc-space-16, 16px);
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
overflow: hidden;
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
box-shadow: var(--oc-shadow-card, 0 8px 30px rgba(0, 0, 0, 0.04));
|
||
background-color: #000000;
|
||
}
|
||
|
||
.case-video-player {
|
||
width: 100%;
|
||
height: 220px;
|
||
display: block;
|
||
background-color: #000000;
|
||
}
|
||
|
||
/* 2. 多图画廊展台 */
|
||
.gallery-wrapper {
|
||
margin-block-end: var(--oc-space-16, 16px);
|
||
}
|
||
|
||
.case-swiper {
|
||
height: 220px;
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
overflow: hidden;
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
box-shadow: var(--oc-shadow-card, 0 8px 30px rgba(0, 0, 0, 0.04));
|
||
background-color: var(--oc-surface, #FFFFFF);
|
||
}
|
||
|
||
.case-media {
|
||
width: 100%;
|
||
height: 220px;
|
||
background-color: var(--oc-bg, #F5F8FC);
|
||
}
|
||
|
||
.case-media-single {
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
box-shadow: var(--oc-shadow-card, 0 8px 30px rgba(0, 0, 0, 0.04));
|
||
}
|
||
|
||
/* Bento Grid 通用卡片规范 */
|
||
.bento-card {
|
||
background-color: var(--oc-surface, #FFFFFF);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
box-shadow: var(--oc-shadow-card, 0 8px 30px rgba(0, 0, 0, 0.04));
|
||
padding: var(--oc-space-20, 20px);
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-block-end: var(--oc-space-16, 16px);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.bento-card-header {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-block-end: var(--oc-space-12, 12px);
|
||
}
|
||
|
||
.bento-header-left {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.bento-tag-bar {
|
||
width: 3px;
|
||
height: 14px;
|
||
border-radius: 2px;
|
||
background-color: var(--oc-primary, #0088FF);
|
||
margin-inline-end: 8px;
|
||
}
|
||
|
||
.tag-primary {
|
||
background-color: var(--oc-primary, #0088FF);
|
||
}
|
||
|
||
.tag-warning {
|
||
background-color: var(--oc-warning, #F79009);
|
||
}
|
||
|
||
.tag-success {
|
||
background-color: var(--oc-success, #12B76A);
|
||
}
|
||
|
||
.tag-ai {
|
||
background-color: var(--oc-ai, #7C5CFF);
|
||
}
|
||
|
||
.bento-card-title {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: var(--oc-text, #101828);
|
||
letter-spacing: 0.2px;
|
||
}
|
||
|
||
.bento-card-body {
|
||
font-size: 15px;
|
||
color: var(--oc-text, #101828);
|
||
line-height: 1.65;
|
||
}
|
||
|
||
/* 3. 核心商业化成果数据卡 (KPI Grid) */
|
||
.kpi-grid {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.kpi-card {
|
||
flex: 1;
|
||
min-width: 90px;
|
||
background-color: var(--oc-bg, #F5F8FC);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: 14px;
|
||
padding: 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.kpi-value {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: var(--oc-primary, #0088FF);
|
||
line-height: 1.2;
|
||
margin-block-end: 4px;
|
||
}
|
||
|
||
.kpi-label {
|
||
font-size: 12px;
|
||
color: var(--oc-text-2, #64748B);
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* 6. 权威资料与演示资产卡 */
|
||
.resource-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.ext-link-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
min-height: 44px;
|
||
height: 48px;
|
||
background-color: var(--oc-bg, #F5F8FC);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: var(--oc-radius-btn, 14px);
|
||
padding: 10px 14px;
|
||
cursor: pointer;
|
||
box-sizing: border-box;
|
||
transition: opacity 150ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||
}
|
||
|
||
.ext-link-row:active {
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.ext-link-main {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.ext-badge {
|
||
padding: 4px 8px;
|
||
border-radius: 6px;
|
||
margin-inline-end: 10px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.ext-badge-pdf {
|
||
background-color: rgba(240, 68, 56, 0.1);
|
||
}
|
||
|
||
.ext-badge-pdf .ext-badge-text {
|
||
color: var(--oc-error, #F04438);
|
||
}
|
||
|
||
.ext-badge-video {
|
||
background-color: rgba(0, 136, 255, 0.1);
|
||
}
|
||
|
||
.ext-badge-video .ext-badge-text {
|
||
color: var(--oc-primary, #0088FF);
|
||
}
|
||
|
||
.ext-badge-text {
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.ext-link-action {
|
||
flex: 1;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--oc-primary, #0088FF);
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.ext-arrow-box {
|
||
width: 24px;
|
||
height: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-inline-start: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.ext-arrow-icon {
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
color: var(--oc-primary, #0088FF);
|
||
}
|
||
|
||
/* 底部垫高 */
|
||
.case-bottom-spacer {
|
||
height: calc(96px + var(--window-bottom, 0px) + env(safe-area-inset-bottom, 0px));
|
||
}
|
||
|
||
/* 骨架屏 */
|
||
.case-skeleton {
|
||
padding-block: var(--oc-space-24, 24px);
|
||
padding-inline: var(--oc-space-16, 16px);
|
||
display: flex;
|
||
flex-direction: column;
|
||
max-width: 720px;
|
||
width: 100%;
|
||
margin-inline: auto;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.skeleton-badge {
|
||
width: 80px;
|
||
height: 20px;
|
||
border-radius: 10px;
|
||
background-color: var(--oc-border, #EBF1F7);
|
||
margin-block-end: var(--oc-space-12, 12px);
|
||
}
|
||
|
||
.skeleton-line {
|
||
height: 16px;
|
||
background-color: var(--oc-border, #EBF1F7);
|
||
border-radius: 8px;
|
||
margin-block-end: var(--oc-space-12, 12px);
|
||
}
|
||
|
||
.line-title {
|
||
width: 75%;
|
||
height: 26px;
|
||
}
|
||
|
||
.skeleton-media {
|
||
height: 200px;
|
||
background-color: var(--oc-border, #EBF1F7);
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
margin-block-end: var(--oc-space-16, 16px);
|
||
}
|
||
|
||
.skeleton-grid {
|
||
display: flex;
|
||
flex-direction: row;
|
||
gap: 8px;
|
||
margin-block-end: var(--oc-space-16, 16px);
|
||
}
|
||
|
||
.skeleton-kpi-card {
|
||
flex: 1;
|
||
height: 72px;
|
||
background-color: var(--oc-surface, #FFFFFF);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: 14px;
|
||
}
|
||
|
||
.skeleton-card {
|
||
height: 120px;
|
||
background-color: var(--oc-surface, #FFFFFF);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
}
|
||
|
||
/* 异常与未找到空状态 */
|
||
.case-empty-wrap {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding-inline: var(--oc-space-16, 16px);
|
||
padding-block: var(--oc-space-24, 24px);
|
||
}
|
||
|
||
.case-empty-card {
|
||
max-width: 340px;
|
||
width: 100%;
|
||
padding: var(--oc-space-24, 24px);
|
||
background-color: var(--oc-surface, #FFFFFF);
|
||
border: 1px solid var(--oc-border, #EBF1F7);
|
||
border-radius: var(--oc-radius-card, 20px);
|
||
box-shadow: var(--oc-shadow-card, 0 8px 30px rgba(0, 0, 0, 0.04));
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.empty-badge {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 24px;
|
||
background-color: rgba(0, 136, 255, 0.08);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-block-end: var(--oc-space-16, 16px);
|
||
}
|
||
|
||
.empty-badge-icon {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: var(--oc-primary, #0088FF);
|
||
}
|
||
|
||
.case-empty-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--oc-text, #101828);
|
||
line-height: 1.5;
|
||
margin-block-end: var(--oc-space-8, 8px);
|
||
text-align: center;
|
||
}
|
||
|
||
.case-empty-desc {
|
||
font-size: 14px;
|
||
color: var(--oc-text-2, #64748B);
|
||
line-height: 1.5;
|
||
margin-block-end: var(--oc-space-20, 20px);
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-back-btn {
|
||
max-width: 220px;
|
||
}
|
||
|
||
/* 常驻底部唯一定位主 CTA 栏 */
|
||
.case-cta-bar {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: var(--window-bottom, 0px);
|
||
padding-block-start: 12px;
|
||
padding-inline: var(--oc-space-16, 16px);
|
||
padding-block-end: calc(16px + env(safe-area-inset-bottom, 0px));
|
||
background-color: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(12px);
|
||
border-top: 1px solid var(--oc-border, #EBF1F7);
|
||
box-sizing: border-box;
|
||
z-index: 100;
|
||
}
|
||
|
||
.primary-cta-btn {
|
||
height: 48px;
|
||
min-height: 44px;
|
||
background: var(--oc-primary-gradient, linear-gradient(135deg, #00A3FF 0%, #0B63E5 100%));
|
||
border-radius: var(--oc-radius-btn, 14px);
|
||
box-shadow: var(--oc-shadow-cta, 0 8px 20px rgba(0, 136, 255, 0.22));
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
border-width: 0;
|
||
cursor: pointer;
|
||
transition: opacity 150ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||
}
|
||
|
||
.primary-cta-btn:active {
|
||
opacity: 0.88;
|
||
}
|
||
|
||
.primary-cta-btn-text {
|
||
color: #FFFFFF;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
* {
|
||
animation: none !important;
|
||
transition: none !important;
|
||
}
|
||
}
|
||
</style>
|