|
|
@@ -1,124 +1,361 @@
|
|
|
<script setup>
|
|
|
-import { computed, ref } from "vue";
|
|
|
+import { Icon } from "@iconify/vue";
|
|
|
+import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
|
|
import PageHero from "../components/PageHero.vue";
|
|
|
+import Reveal from "../components/motion/Reveal.vue";
|
|
|
import bannerUrl from "../assets/images/banner.jpg?url";
|
|
|
import bgUrl from "../assets/images/bg.jpg?url";
|
|
|
-import Reveal from "../components/motion/Reveal.vue";
|
|
|
|
|
|
-const industries = ["全部", "汽车", "化工", "装备", "电子", "能源", "园区"];
|
|
|
-const active = ref("全部");
|
|
|
+const activeTab = ref("equipment");
|
|
|
+const expanded = ref({
|
|
|
+ equipment: false,
|
|
|
+ qhse: false,
|
|
|
+ injection: false,
|
|
|
+});
|
|
|
|
|
|
-const allCases = [
|
|
|
- {
|
|
|
- industry: "汽车",
|
|
|
- title: "过程质量追溯与异常闭环",
|
|
|
- desc: "打通工序数据与质量规则,实现异常定位与闭环处理。",
|
|
|
- cover: bannerUrl,
|
|
|
- metrics: ["良率 +3.2%", "追溯时间 -80%", "异常闭环 T+1"],
|
|
|
- },
|
|
|
- {
|
|
|
- industry: "化工",
|
|
|
- title: "能效监测与对标优化",
|
|
|
- desc: "构建能耗指标体系与异常分析模型,实现持续能效提升。",
|
|
|
- cover: bgUrl,
|
|
|
- metrics: ["能耗 -6.8%", "对标覆盖 12 装置", "异常预警 7 类"],
|
|
|
- },
|
|
|
+const prefersReducedMotion = () =>
|
|
|
+ typeof window !== "undefined" &&
|
|
|
+ window.matchMedia &&
|
|
|
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
|
+
|
|
|
+const categories = [
|
|
|
{
|
|
|
- industry: "装备",
|
|
|
- title: "预测性维护与停机治理",
|
|
|
- desc: "多源数据融合评估健康度,提前预警故障风险。",
|
|
|
+ key: "equipment",
|
|
|
+ title: "设备管理",
|
|
|
+ lead:
|
|
|
+ "覆盖台账、巡检、维保、预测性维护到告警处置的全生命周期闭环,让设备运行更稳定、维护更可控。",
|
|
|
+ icon: "lucide:cpu",
|
|
|
+ accent: "rgba(37, 99, 235, 0.9)",
|
|
|
cover: bannerUrl,
|
|
|
- metrics: ["停机 -20%", "维修成本 -12%", "告警准确率 +15%"],
|
|
|
+ cases: [
|
|
|
+ {
|
|
|
+ title: "设备台账与参数标准化",
|
|
|
+ desc: "统一资产主数据、参数口径与编码规则,沉淀可复用的设备数据底座。",
|
|
|
+ tags: ["台账治理", "主数据", "标准化"],
|
|
|
+ metrics: ["资产一致率 ≥99%", "新增设备上线 T+1", "编码规则统一"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "巡检计划与移动端执行闭环",
|
|
|
+ desc: "计划自动排程、移动端签到拍照、异常一键上报,形成巡检闭环追踪。",
|
|
|
+ tags: ["巡检", "移动端", "闭环"],
|
|
|
+ metrics: ["漏检率≈0", "巡检效率 +30%", "异常处置可追溯"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "智能维保与工单联动",
|
|
|
+ desc: "维保策略与备件、人员、工时联动,工单流转可视化,降低运维成本。",
|
|
|
+ tags: ["维保", "工单", "备件"],
|
|
|
+ metrics: ["工单闭环率 ≥98%", "平均修复时长 -20%", "备件周转 +15%"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "预测性维护与健康度评估",
|
|
|
+ desc: "多源数据融合评估健康度,提前预警故障风险,避免非计划停机。",
|
|
|
+ tags: ["预测性维护", "健康度", "预警"],
|
|
|
+ metrics: ["停机 -20%", "维修成本 -12%", "告警准确率 +15%"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "设备接入与状态监测(SCADA/IoT)",
|
|
|
+ desc: "统一采集与边缘接入,实时监测关键参数,支撑告警与趋势分析。",
|
|
|
+ tags: ["接入", "监测", "边缘"],
|
|
|
+ metrics: ["采集点位 6,000+", "秒级刷新", "告警规则可配置"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "异常告警与处置SOP",
|
|
|
+ desc: "告警分级、自动派单与SOP引导,确保异常快速定位与闭环处置。",
|
|
|
+ tags: ["告警", "SOP", "处置"],
|
|
|
+ metrics: ["响应时长 -35%", "误报率下降", "处置闭环可审计"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "润滑与点检管理",
|
|
|
+ desc: "点检路线、标准作业与耗材管理协同,减少人为遗漏与重复劳动。",
|
|
|
+ tags: ["点检", "润滑", "耗材"],
|
|
|
+ metrics: ["点检准时率 ≥99%", "重复工时 -18%", "耗材可追溯"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "关键设备KPI看板与对标",
|
|
|
+ desc: "以停机、故障率、MTBF/MTTR为核心指标,对标分析并推动持续改进。",
|
|
|
+ tags: ["KPI", "看板", "对标"],
|
|
|
+ metrics: ["指标口径统一", "月度复盘固化", "改进项可跟踪"],
|
|
|
+ },
|
|
|
+ ],
|
|
|
},
|
|
|
{
|
|
|
- industry: "电子",
|
|
|
- title: "设备接入与 OEE 提升",
|
|
|
- desc: "统一采集与运行监控,驱动 OEE 分析与产线瓶颈改善。",
|
|
|
+ key: "qhse",
|
|
|
+ title: "QHSE",
|
|
|
+ lead:
|
|
|
+ "将质量、健康、安全、环境融入业务流程:从风险识别到整改闭环,让合规与效率同时提升。",
|
|
|
+ icon: "lucide:shield-check",
|
|
|
+ accent: "rgba(14, 116, 144, 0.92)",
|
|
|
cover: bgUrl,
|
|
|
- metrics: ["OEE +5.5%", "采集点位 6,000+", "停机原因 100% 标准化"],
|
|
|
+ cases: [
|
|
|
+ {
|
|
|
+ title: "隐患排查治理与整改闭环",
|
|
|
+ desc: "隐患登记、分级分责、整改验收一体化,形成闭环追踪与审计留痕。",
|
|
|
+ tags: ["隐患", "整改", "闭环"],
|
|
|
+ metrics: ["闭环率 ≥98%", "复发率下降", "审计留痕完整"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "作业票与高风险作业管控",
|
|
|
+ desc: "动火、受限空间等高风险作业票线上化,联动审批、交底与现场验证。",
|
|
|
+ tags: ["作业票", "审批", "现场验证"],
|
|
|
+ metrics: ["审批时长 -40%", "票证合规率提升", "现场校验可追溯"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "风险分级管控与双重预防",
|
|
|
+ desc: "风险辨识、分级与管控措施落地,叠加隐患治理形成双重预防机制。",
|
|
|
+ tags: ["风险分级", "双重预防", "措施"],
|
|
|
+ metrics: ["风险台账统一", "措施执行可核查", "复核机制固化"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "应急预案与演练管理",
|
|
|
+ desc: "预案库、演练计划、过程记录与复盘改进,提升应急协同与响应效率。",
|
|
|
+ tags: ["应急", "演练", "复盘"],
|
|
|
+ metrics: ["演练覆盖率提升", "响应流程标准化", "复盘问题闭环"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "质量追溯与异常闭环",
|
|
|
+ desc: "打通过程数据与质量规则,异常自动触发处置流程,提升产品/工程质量。",
|
|
|
+ tags: ["质量", "追溯", "异常"],
|
|
|
+ metrics: ["不良率下降", "追溯时间 -80%", "异常闭环 T+1"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "环保监测与排放合规",
|
|
|
+ desc: "在线监测、阈值预警与报表归档,支撑排放合规与监管报送。",
|
|
|
+ tags: ["环保", "监测", "合规"],
|
|
|
+ metrics: ["预警及时", "报表自动生成", "监管口径一致"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "承包商HSE准入与评价",
|
|
|
+ desc: "资质准入、培训考试、现场评价与黑白名单管理,降低外协风险。",
|
|
|
+ tags: ["承包商", "准入", "评价"],
|
|
|
+ metrics: ["准入效率提升", "违规率下降", "评价结果可追溯"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "培训考试与证书到期提醒",
|
|
|
+ desc: "培训计划、在线考试、证书管理与到期提醒,保障人员资质合规。",
|
|
|
+ tags: ["培训", "考试", "证书"],
|
|
|
+ metrics: ["到期漏管≈0", "培训覆盖率提升", "记录可审计"],
|
|
|
+ },
|
|
|
+ ],
|
|
|
},
|
|
|
{
|
|
|
- industry: "能源",
|
|
|
- title: "设备巡检与工单联动",
|
|
|
- desc: "巡检计划、移动端执行与工单闭环,形成运维知识沉淀。",
|
|
|
+ key: "injection",
|
|
|
+ title: "智能注气",
|
|
|
+ lead:
|
|
|
+ "围绕注气作业计划、过程监控与效果评估,联动工艺参数与产量数据,实现可控、可追溯、可优化。",
|
|
|
+ icon: "lucide:wind",
|
|
|
+ accent: "rgba(2, 132, 199, 0.92)",
|
|
|
cover: bannerUrl,
|
|
|
- metrics: ["工单闭环率 98%", "巡检漏检 0", "知识库 300+"],
|
|
|
- },
|
|
|
- {
|
|
|
- industry: "园区",
|
|
|
- title: "园区运营可视化",
|
|
|
- desc: "统一态势大屏与多系统联动,实现事件快速响应。",
|
|
|
- cover: bgUrl,
|
|
|
- metrics: ["事件响应 -35%", "系统对接 10+", "指标 200+"],
|
|
|
+ cases: [
|
|
|
+ {
|
|
|
+ title: "注气计划编排与批量下发",
|
|
|
+ desc: "按井/站点编排注气计划,批量下发到现场执行端,确保节奏一致。",
|
|
|
+ tags: ["计划", "下发", "协同"],
|
|
|
+ metrics: ["计划变更可追溯", "下发效率提升", "执行偏差可视化"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "注气过程监控与异常预警",
|
|
|
+ desc: "实时监控压力、流量、温度等关键参数,异常自动预警并联动处置。",
|
|
|
+ tags: ["监控", "预警", "处置"],
|
|
|
+ metrics: ["秒级监控", "异常定位更快", "告警规则可配置"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "注气工艺参数优化建议",
|
|
|
+ desc: "基于历史数据与工况特征,给出参数窗口建议,提升作业稳定性。",
|
|
|
+ tags: ["优化", "参数窗口", "建议"],
|
|
|
+ metrics: ["波动降低", "能耗可控", "经验可沉淀"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "注气效果评估与对比分析",
|
|
|
+ desc: "联动产量、压力与含水等指标,对比评估不同策略效果,支撑决策。",
|
|
|
+ tags: ["评估", "对比", "决策"],
|
|
|
+ metrics: ["评估口径统一", "策略对比直观", "月度复盘固化"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "井站联动与作业记录归档",
|
|
|
+ desc: "井-站-管网联动视图,作业记录自动归档,满足追溯与审计需求。",
|
|
|
+ tags: ["联动", "归档", "追溯"],
|
|
|
+ metrics: ["记录完整", "审计便捷", "事件链条清晰"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "设备运行状态与注气策略联动",
|
|
|
+ desc: "联动压缩机/阀组状态,自动约束策略边界,降低设备风险与误操作。",
|
|
|
+ tags: ["联动", "约束", "安全"],
|
|
|
+ metrics: ["误操作降低", "风险边界清晰", "联动规则可配置"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "能耗与成本核算(注气侧)",
|
|
|
+ desc: "按作业、井、班组维度核算能耗与成本,为优化与考核提供依据。",
|
|
|
+ tags: ["能耗", "成本", "核算"],
|
|
|
+ metrics: ["核算自动化", "成本结构清晰", "对标可用"],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "注气数据治理与口径统一",
|
|
|
+ desc: "统一注气数据口径、指标体系与采集规范,支撑长期运营与模型迭代。",
|
|
|
+ tags: ["数据治理", "指标体系", "规范"],
|
|
|
+ metrics: ["口径统一", "数据可用性提升", "资产沉淀"],
|
|
|
+ },
|
|
|
+ ],
|
|
|
},
|
|
|
];
|
|
|
|
|
|
-const filtered = computed(() => {
|
|
|
- if (active.value === "全部") return allCases;
|
|
|
- return allCases.filter((c) => c.industry === active.value);
|
|
|
+const tabItems = computed(() =>
|
|
|
+ categories.map((c) => ({
|
|
|
+ ...c,
|
|
|
+ count: c.cases.length,
|
|
|
+ })),
|
|
|
+);
|
|
|
+
|
|
|
+const maxPreview = 6;
|
|
|
+const visibleCases = (cat) =>
|
|
|
+ expanded.value[cat.key] ? cat.cases : cat.cases.slice(0, maxPreview);
|
|
|
+
|
|
|
+const toggleExpand = (key) => {
|
|
|
+ expanded.value = { ...expanded.value, [key]: !expanded.value[key] };
|
|
|
+};
|
|
|
+
|
|
|
+const scrollToCategory = (key) => {
|
|
|
+ const el = document.getElementById(`case-${key}`);
|
|
|
+ if (!el) return;
|
|
|
+ activeTab.value = key;
|
|
|
+ el.scrollIntoView({
|
|
|
+ behavior: prefersReducedMotion() ? "auto" : "smooth",
|
|
|
+ block: "start",
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+let io = null;
|
|
|
+onMounted(() => {
|
|
|
+ if (typeof window === "undefined") return;
|
|
|
+ if (!("IntersectionObserver" in window)) return;
|
|
|
+
|
|
|
+ const headerHRaw = getComputedStyle(document.documentElement)
|
|
|
+ .getPropertyValue("--header-h")
|
|
|
+ .trim();
|
|
|
+ const headerH = Number.parseInt(headerHRaw, 10);
|
|
|
+ const topOffset = Number.isFinite(headerH) ? headerH : 72;
|
|
|
+
|
|
|
+ const sections = categories
|
|
|
+ .map((c) => document.getElementById(`case-${c.key}`))
|
|
|
+ .filter(Boolean);
|
|
|
+
|
|
|
+ io = new IntersectionObserver(
|
|
|
+ (entries) => {
|
|
|
+ const visible = entries
|
|
|
+ .filter((e) => e.isIntersecting)
|
|
|
+ .sort(
|
|
|
+ (a, b) =>
|
|
|
+ (a.boundingClientRect.top ?? 0) - (b.boundingClientRect.top ?? 0),
|
|
|
+ )[0];
|
|
|
+ if (!visible) return;
|
|
|
+ const id = visible.target.getAttribute("id") || "";
|
|
|
+ const key = id.replace("case-", "");
|
|
|
+ if (key) activeTab.value = key;
|
|
|
+ },
|
|
|
+ {
|
|
|
+ threshold: 0.22,
|
|
|
+ rootMargin: `-${topOffset + 10}px 0px -55% 0px`,
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ for (const s of sections) io.observe(s);
|
|
|
+});
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ if (io) io.disconnect();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <div>
|
|
|
+ <div class="casesView">
|
|
|
<PageHero
|
|
|
kicker="客户案例"
|
|
|
- title="以指标呈现价值"
|
|
|
- subtitle="从设备接入、数据治理到应用运营,沉淀可复用的行业实践。"
|
|
|
+ title="案例分为三大类:设备管理、QHSE、智能注气"
|
|
|
+ subtitle="每个分类沉淀了多项可复用的落地案例与最佳实践,覆盖从方案设计到运营闭环的关键环节。"
|
|
|
>
|
|
|
<template #actions>
|
|
|
- <RouterLink class="btn btn-primary" to="/contact"
|
|
|
- >获取案例集</RouterLink
|
|
|
+ <RouterLink class="btn btn-primary" style="border-radius: 0" to="/contact"
|
|
|
+ >留言咨询</RouterLink
|
|
|
>
|
|
|
- <RouterLink class="btn btn-ghost" to="/solutions">匹配方案</RouterLink>
|
|
|
</template>
|
|
|
</PageHero>
|
|
|
|
|
|
+ <nav class="tabBar" aria-label="案例分类导航">
|
|
|
+ <button
|
|
|
+ v-for="t in tabItems"
|
|
|
+ :key="t.key"
|
|
|
+ type="button"
|
|
|
+ class="tabBtn"
|
|
|
+ :class="{ 'is-active': activeTab === t.key }"
|
|
|
+ @click="scrollToCategory(t.key)"
|
|
|
+ >
|
|
|
+ <Icon :icon="t.icon" class="tabIcon" />
|
|
|
+ <span class="tabText">{{ t.title }}</span>
|
|
|
+ <span class="tabCount">{{ t.count }}</span>
|
|
|
+ </button>
|
|
|
+ </nav>
|
|
|
+
|
|
|
<section class="section">
|
|
|
<div class="container">
|
|
|
- <Reveal as="div" class="filters" :delay="0">
|
|
|
- <button
|
|
|
- v-for="i in industries"
|
|
|
- :key="i"
|
|
|
- type="button"
|
|
|
- class="filter"
|
|
|
- :data-active="active === i ? 'true' : 'false'"
|
|
|
- @click="active = i"
|
|
|
+ <div class="stack">
|
|
|
+ <section
|
|
|
+ v-for="(cat, catIdx) in categories"
|
|
|
+ :key="cat.key"
|
|
|
+ class="caseSection"
|
|
|
+ :id="`case-${cat.key}`"
|
|
|
+ :style="{ '--accent': cat.accent }"
|
|
|
>
|
|
|
- {{ i }}
|
|
|
- </button>
|
|
|
- </Reveal>
|
|
|
-
|
|
|
- <div class="grid grid-3">
|
|
|
- <Reveal
|
|
|
- v-for="(c, idx) in filtered"
|
|
|
- :key="c.title"
|
|
|
- as="div"
|
|
|
- class="card caseCard hover-lift"
|
|
|
- :delay="60 + idx * 70"
|
|
|
- >
|
|
|
- <img
|
|
|
- class="caseCover"
|
|
|
- :src="c.cover"
|
|
|
- :alt="c.title"
|
|
|
- loading="lazy"
|
|
|
- />
|
|
|
- <div class="casePad">
|
|
|
- <div class="caseTop">
|
|
|
- <span class="pill">{{ c.industry }}</span>
|
|
|
- <RouterLink class="btn btn-link" to="/contact"
|
|
|
- >对接同类项目 →</RouterLink
|
|
|
- >
|
|
|
+ <Reveal as="header" class="caseHead" :delay="40 + catIdx * 80">
|
|
|
+ <div class="caseHeadTop">
|
|
|
+ <span class="caseBadge">分类</span>
|
|
|
+ <div class="caseHeadTitle">
|
|
|
+ <Icon :icon="cat.icon" class="caseHeadIcon" />
|
|
|
+ <h2 class="h2">{{ cat.title }}</h2>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="caseTitle">{{ c.title }}</div>
|
|
|
- <div class="muted">{{ c.desc }}</div>
|
|
|
- <div class="caseMetrics">
|
|
|
- <span v-for="m in c.metrics" :key="m" class="metric">{{
|
|
|
- m
|
|
|
- }}</span>
|
|
|
+ <p class="muted caseLead">{{ cat.lead }}</p>
|
|
|
+ <div class="caseHeadActions">
|
|
|
+ <RouterLink class="btn btn-ghost" style="border-radius: 0" to="/contact"
|
|
|
+ >对接同类项目</RouterLink
|
|
|
+ >
|
|
|
</div>
|
|
|
+ </Reveal>
|
|
|
+
|
|
|
+ <div class="caseGrid">
|
|
|
+ <Reveal
|
|
|
+ v-for="(c, idx) in visibleCases(cat)"
|
|
|
+ :key="c.title"
|
|
|
+ as="article"
|
|
|
+ class="card caseCard hover-lift"
|
|
|
+ :delay="60 + idx * 60"
|
|
|
+ >
|
|
|
+ <div class="caseCardTop">
|
|
|
+ <div class="caseCardLine" aria-hidden="true"></div>
|
|
|
+ <div class="caseCardTitle">{{ c.title }}</div>
|
|
|
+ <div class="muted caseCardDesc">{{ c.desc }}</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="caseMeta">
|
|
|
+ <div class="caseTags" aria-label="标签">
|
|
|
+ <span v-for="t in c.tags" :key="t" class="tag">{{ t }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="caseMetrics" aria-label="指标">
|
|
|
+ <span v-for="m in c.metrics" :key="m" class="metric">{{ m }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Reveal>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="caseMore">
|
|
|
+ <button
|
|
|
+ v-if="cat.cases.length > maxPreview"
|
|
|
+ type="button"
|
|
|
+ class="btn btn-link moreBtn"
|
|
|
+ @click="toggleExpand(cat.key)"
|
|
|
+ >
|
|
|
+ {{ expanded[cat.key] ? "收起" : `展开更多(${cat.cases.length - maxPreview})` }}
|
|
|
+ </button>
|
|
|
</div>
|
|
|
- </Reveal>
|
|
|
+ </section>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
@@ -126,63 +363,200 @@ const filtered = computed(() => {
|
|
|
</template>
|
|
|
|
|
|
<style scoped>
|
|
|
-.filters {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 10px;
|
|
|
- margin-bottom: 16px;
|
|
|
+.casesView {
|
|
|
+ --tab-bg: rgba(255, 255, 255, 0.92);
|
|
|
}
|
|
|
|
|
|
-.filter {
|
|
|
- height: 38px;
|
|
|
- padding: 0 14px;
|
|
|
- /* border-radius: 999px; */
|
|
|
- border: 1px solid var(--border);
|
|
|
- background: #fff;
|
|
|
- cursor: pointer;
|
|
|
- font-weight: 700;
|
|
|
- color: var(--slate-700);
|
|
|
- transition: 160ms ease;
|
|
|
+.tabBar {
|
|
|
+ position: sticky;
|
|
|
+ top: var(--header-h);
|
|
|
+ z-index: 20;
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+ background: #eef5f8;
|
|
|
+ height: 76px;
|
|
|
+ margin: 10px 0 18px;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 0;
|
|
|
}
|
|
|
|
|
|
-.filter:hover {
|
|
|
- background: rgba(15, 23, 42, 0.02);
|
|
|
+.tabBtn {
|
|
|
+ height: 76px;
|
|
|
+ border: 0;
|
|
|
+ background: #eef5f8;
|
|
|
+ padding: 0 18px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 10px;
|
|
|
+ color: var(--slate-700);
|
|
|
+ cursor: pointer;
|
|
|
+ transition:
|
|
|
+ transform 160ms ease,
|
|
|
+ box-shadow 160ms ease,
|
|
|
+ color 160ms ease,
|
|
|
+ background-color 160ms ease;
|
|
|
}
|
|
|
|
|
|
-.filter[data-active="true"] {
|
|
|
- border-color: rgba(37, 99, 235, 0.35);
|
|
|
- background: rgba(37, 99, 235, 0.08);
|
|
|
+.tabBtn.is-active {
|
|
|
color: var(--brand-700);
|
|
|
+ background-color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.tabBtn:focus-visible {
|
|
|
+ outline: none;
|
|
|
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.22);
|
|
|
+}
|
|
|
+
|
|
|
+.tabIcon {
|
|
|
+ width: 18px;
|
|
|
+ height: 18px;
|
|
|
+}
|
|
|
+
|
|
|
+.tabText {
|
|
|
+ display: inline-block;
|
|
|
+ white-space: nowrap;
|
|
|
+ font-weight: 900;
|
|
|
+ letter-spacing: -0.01em;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ background-image: linear-gradient(currentColor, currentColor);
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-position: 0 100%;
|
|
|
+ background-size: 0 2px;
|
|
|
+ transition: background-size 160ms ease;
|
|
|
+}
|
|
|
+
|
|
|
+.tabBtn.is-active .tabText {
|
|
|
+ background-size: 100% 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.tabCount {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 900;
|
|
|
+ padding: 4px 8px;
|
|
|
+ border-radius: 999px;
|
|
|
+ border: 1px solid rgba(15, 23, 42, 0.12);
|
|
|
+ background: rgba(255, 255, 255, 0.72);
|
|
|
+ color: rgba(2, 6, 23, 0.72);
|
|
|
}
|
|
|
|
|
|
-.caseTop {
|
|
|
+.stack {
|
|
|
+ display: grid;
|
|
|
+ gap: 26px;
|
|
|
+}
|
|
|
+
|
|
|
+.caseSection {
|
|
|
+ scroll-margin-top: calc(var(--header-h) + 88px);
|
|
|
+ padding: 18px 0 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.caseHead {
|
|
|
+ padding: 18px 4px 14px;
|
|
|
+ display: grid;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.caseHeadTop {
|
|
|
display: flex;
|
|
|
+ align-items: center;
|
|
|
justify-content: space-between;
|
|
|
+ gap: 14px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+
|
|
|
+.caseBadge {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 900;
|
|
|
+ letter-spacing: 0.12em;
|
|
|
+ color: rgba(2, 6, 23, 0.62);
|
|
|
+ background: rgba(15, 23, 42, 0.04);
|
|
|
+ border: 1px solid rgba(15, 23, 42, 0.08);
|
|
|
+ padding: 6px 10px;
|
|
|
+ border-radius: 999px;
|
|
|
+ flex: 0 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.caseHeadTitle {
|
|
|
+ display: inline-flex;
|
|
|
align-items: center;
|
|
|
- gap: 12px;
|
|
|
- margin-bottom: 12px;
|
|
|
+ gap: 10px;
|
|
|
}
|
|
|
|
|
|
-.caseCover {
|
|
|
- width: 100%;
|
|
|
- height: 140px;
|
|
|
- object-fit: cover;
|
|
|
- display: block;
|
|
|
- border-bottom: 1px solid var(--border);
|
|
|
+.caseHeadIcon {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ color: var(--accent);
|
|
|
+}
|
|
|
+
|
|
|
+.caseLead {
|
|
|
+ max-width: 92ch;
|
|
|
+}
|
|
|
+
|
|
|
+.caseHeadActions {
|
|
|
+ margin-top: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.caseGrid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+ gap: 18px;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.caseCard {
|
|
|
+ border: 1px solid var(--border);
|
|
|
+ border-top: 0;
|
|
|
+ box-shadow: 0 16px 42px rgba(2, 6, 23, 0.08);
|
|
|
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), #fff);
|
|
|
+}
|
|
|
+
|
|
|
+.caseCardTop {
|
|
|
+ padding: 18px 18px 10px;
|
|
|
}
|
|
|
|
|
|
-.casePad {
|
|
|
- padding: 18px;
|
|
|
+.caseCardLine {
|
|
|
+ height: 2px;
|
|
|
+ width: 100%;
|
|
|
+ background: linear-gradient(90deg, var(--accent), rgba(2, 6, 23, 0));
|
|
|
+ margin-bottom: 12px;
|
|
|
}
|
|
|
|
|
|
-.caseTitle {
|
|
|
+.caseCardTitle {
|
|
|
font-weight: 900;
|
|
|
letter-spacing: -0.02em;
|
|
|
- margin-bottom: 6px;
|
|
|
+ line-height: 1.25;
|
|
|
+}
|
|
|
+
|
|
|
+.caseCardDesc {
|
|
|
+ margin-top: 8px;
|
|
|
+ line-height: 1.75;
|
|
|
+}
|
|
|
+
|
|
|
+.caseMeta {
|
|
|
+ padding: 8px 18px 18px;
|
|
|
+ display: grid;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.caseTags {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.tag {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 6px 10px;
|
|
|
+ border-radius: 999px;
|
|
|
+ background: rgba(15, 23, 42, 0.03);
|
|
|
+ border: 1px solid var(--border);
|
|
|
+ font-weight: 700;
|
|
|
+ color: var(--slate-700);
|
|
|
+ font-size: 12px;
|
|
|
}
|
|
|
|
|
|
.caseMetrics {
|
|
|
- margin-top: 12px;
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 10px;
|
|
|
@@ -192,11 +566,64 @@ const filtered = computed(() => {
|
|
|
display: inline-flex;
|
|
|
align-items: center;
|
|
|
padding: 6px 10px;
|
|
|
- /* border-radius: 12px; */
|
|
|
background: rgba(15, 23, 42, 0.03);
|
|
|
border: 1px solid var(--border);
|
|
|
- font-weight: 700;
|
|
|
- color: var(--slate-700);
|
|
|
+ border-left: 2px solid var(--accent);
|
|
|
+ font-weight: 800;
|
|
|
+ color: rgba(2, 6, 23, 0.78);
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
+
|
|
|
+.caseMore {
|
|
|
+ margin-top: 10px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.moreBtn {
|
|
|
+ font-weight: 900;
|
|
|
+ letter-spacing: -0.01em;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.pageHero) {
|
|
|
+ background-image: url("../assets/images/bg5.jpg");
|
|
|
+ background-size: cover, cover;
|
|
|
+ background-position: center, center;
|
|
|
+ background-repeat: no-repeat, no-repeat;
|
|
|
+ background-blend-mode: overlay;
|
|
|
+ color: #111;
|
|
|
+ border-bottom: none;
|
|
|
+ padding: 80px 0;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.pageHero__subtitle) {
|
|
|
+ color: #4f5055;
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 960px) {
|
|
|
+ .caseGrid {
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .tabBar {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tabBtn {
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 0 16px;
|
|
|
+ height: 64px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tabText {
|
|
|
+ padding-bottom: 6px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 560px) {
|
|
|
+ .caseGrid {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|