| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- <script setup>
- 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";
- const activeTab = ref("equipment");
- const expanded = ref({
- equipment: false,
- qhse: false,
- injection: false,
- });
- const prefersReducedMotion = () =>
- typeof window !== "undefined" &&
- window.matchMedia &&
- window.matchMedia("(prefers-reduced-motion: reduce)").matches;
- const categories = [
- {
- key: "equipment",
- title: "设备管理",
- lead:
- "覆盖台账、巡检、维保、预测性维护到告警处置的全生命周期闭环,让设备运行更稳定、维护更可控。",
- icon: "lucide:cpu",
- accent: "rgba(37, 99, 235, 0.9)",
- cover: bannerUrl,
- 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: ["指标口径统一", "月度复盘固化", "改进项可跟踪"],
- },
- ],
- },
- {
- key: "qhse",
- title: "QHSE",
- lead:
- "将质量、健康、安全、环境融入业务流程:从风险识别到整改闭环,让合规与效率同时提升。",
- icon: "lucide:shield-check",
- accent: "rgba(14, 116, 144, 0.92)",
- cover: bgUrl,
- 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", "培训覆盖率提升", "记录可审计"],
- },
- ],
- },
- {
- key: "injection",
- title: "智能注气",
- lead:
- "围绕注气作业计划、过程监控与效果评估,联动工艺参数与产量数据,实现可控、可追溯、可优化。",
- icon: "lucide:wind",
- accent: "rgba(2, 132, 199, 0.92)",
- cover: bannerUrl,
- 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 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 class="casesView">
- <PageHero
- kicker="客户案例"
- title="案例分为三大类:设备管理、QHSE、智能注气"
- subtitle="每个分类沉淀了多项可复用的落地案例与最佳实践,覆盖从方案设计到运营闭环的关键环节。"
- >
- <template #actions>
- <RouterLink class="btn btn-primary" style="border-radius: 0" to="/contact"
- >留言咨询</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">
- <div class="stack">
- <section
- v-for="(cat, catIdx) in categories"
- :key="cat.key"
- class="caseSection"
- :id="`case-${cat.key}`"
- :style="{ '--accent': cat.accent }"
- >
- <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>
- <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>
- </section>
- </div>
- </div>
- </section>
- </div>
- </template>
- <style scoped>
- .casesView {
- --tab-bg: rgba(255, 255, 255, 0.92);
- }
- .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;
- }
- .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;
- }
- .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);
- }
- .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: 10px;
- }
- .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;
- }
- .caseCardLine {
- height: 2px;
- width: 100%;
- background: linear-gradient(90deg, var(--accent), rgba(2, 6, 23, 0));
- margin-bottom: 12px;
- }
- .caseCardTitle {
- font-weight: 900;
- letter-spacing: -0.02em;
- 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 {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- }
- .metric {
- display: inline-flex;
- align-items: center;
- padding: 6px 10px;
- background: rgba(15, 23, 42, 0.03);
- border: 1px solid var(--border);
- 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>
|