|
|
@@ -0,0 +1,507 @@
|
|
|
+<template>
|
|
|
+ <AidopDemoShell title="数据地图" subtitle="外部系统、DB/API 接入、数据处理层级和服务出口总览" card-body-padding="0">
|
|
|
+ <template #bar-right>
|
|
|
+ <el-button icon="ele-Operation" @click="go('/aidop/data-platform/sync-tasks')">数据任务</el-button>
|
|
|
+ <el-button type="primary" icon="ele-AlarmClock" @click="go('/platform/job')">任务调度</el-button>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <div class="data-map">
|
|
|
+ <section class="map-hero">
|
|
|
+ <div>
|
|
|
+ <div class="eyebrow">Ai-DOP Data Platform</div>
|
|
|
+ <h3>企业数据流转总览</h3>
|
|
|
+ <p>统一呈现数据库同步、API 入站、API 拉取与 API 出站服务,帮助演示数据从来源到业务应用的完整路径。</p>
|
|
|
+ </div>
|
|
|
+ <div class="legend">
|
|
|
+ <span v-for="item in legends" :key="item.label"><i :class="item.className" />{{ item.label }}</span>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section class="map-canvas">
|
|
|
+ <div class="flow-steps">
|
|
|
+ <div v-for="(step, index) in flowSteps" :key="step" class="flow-step">
|
|
|
+ <span>{{ step }}</span>
|
|
|
+ <i v-if="index < flowSteps.length - 1">→</i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="lane lane-source">
|
|
|
+ <div class="lane__title">外部系统 / 来源</div>
|
|
|
+ <div v-for="node in sourceNodes" :key="node.title" class="node-card is-clickable" @click="openDetail(node.detailKey)">
|
|
|
+ <div class="node-card__top">
|
|
|
+ <span class="node-card__title">{{ node.title }}</span>
|
|
|
+ <el-tag size="small" effect="plain" :type="node.statusType">{{ node.status }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="node-card__desc">{{ node.desc }}</div>
|
|
|
+ <div class="node-card__meta">{{ node.meta }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="lane lane-access">
|
|
|
+ <div class="lane__title">接入层</div>
|
|
|
+ <div v-for="node in accessNodes" :key="node.title" class="process-card is-clickable" :class="node.kind" @click="openDetail(node.detailKey)">
|
|
|
+ <div class="process-card__icon">{{ node.icon }}</div>
|
|
|
+ <div>
|
|
|
+ <div class="process-card__title">{{ node.title }}</div>
|
|
|
+ <div class="process-card__desc">{{ node.desc }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="hub">
|
|
|
+ <div class="hub__ring">
|
|
|
+ <div class="hub__core">
|
|
|
+ <div class="hub__title">数据中台</div>
|
|
|
+ <div class="hub__desc">接入 · 治理 · 标准化 · 服务</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="pipeline">
|
|
|
+ <div v-for="step in pipelineSteps" :key="step.title" class="pipeline-step is-clickable" @click="openDetail(step.detailKey)">
|
|
|
+ <div class="pipeline-step__index">{{ step.index }}</div>
|
|
|
+ <div>
|
|
|
+ <div class="pipeline-step__title">{{ step.title }}</div>
|
|
|
+ <div class="pipeline-step__desc">{{ step.desc }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="lane lane-service">
|
|
|
+ <div class="lane__title">服务出口</div>
|
|
|
+ <div v-for="node in serviceNodes" :key="node.title" class="process-card is-clickable" :class="node.kind" @click="openDetail(node.detailKey)">
|
|
|
+ <div class="process-card__icon">{{ node.icon }}</div>
|
|
|
+ <div>
|
|
|
+ <div class="process-card__title">{{ node.title }}</div>
|
|
|
+ <div class="process-card__desc">{{ node.desc }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="lane lane-consumer">
|
|
|
+ <div class="lane__title">业务消费 / 回写</div>
|
|
|
+ <div v-for="node in consumerNodes" :key="node.title" class="node-card node-card--consumer is-clickable" @click="openDetail(node.detailKey)">
|
|
|
+ <div class="node-card__top">
|
|
|
+ <span class="node-card__title">{{ node.title }}</span>
|
|
|
+ <el-tag size="small" effect="plain">{{ node.type }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="node-card__desc">{{ node.desc }}</div>
|
|
|
+ <div class="node-card__meta">{{ node.meta }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <el-drawer v-model="detailVisible" :title="currentDetail?.title || '节点详情'" size="420px">
|
|
|
+ <div v-if="currentDetail" class="detail-drawer">
|
|
|
+ <el-tag effect="plain" :type="currentDetail.tagType">{{ currentDetail.category }}</el-tag>
|
|
|
+ <p class="detail-summary">{{ currentDetail.summary }}</p>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="detail-section__title">关联对象</div>
|
|
|
+ <div class="detail-object" v-for="item in currentDetail.objects" :key="item.label">
|
|
|
+ <span>{{ item.label }}</span>
|
|
|
+ <b>{{ item.value }}</b>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="detail-section__title">操作入口</div>
|
|
|
+ <div class="detail-actions">
|
|
|
+ <el-button v-for="action in currentDetail.actions" :key="action.label" type="primary" plain @click="go(action.path)">
|
|
|
+ {{ action.label }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+ </div>
|
|
|
+ </AidopDemoShell>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="aidopDataPlatformDataMap">
|
|
|
+import { computed, ref } from 'vue';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import AidopDemoShell from '../components/AidopDemoShell.vue';
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
+
|
|
|
+const legends = [
|
|
|
+ { label: 'DB 同步', className: 'legend-db' },
|
|
|
+ { label: 'API 入站', className: 'legend-api-in' },
|
|
|
+ { label: 'API 拉取', className: 'legend-api-pull' },
|
|
|
+ { label: 'API 出站', className: 'legend-api-out' },
|
|
|
+];
|
|
|
+
|
|
|
+const flowSteps = ['外部系统 / 来源', '接入层', '数据中台', '服务出口', '业务消费 / 回写'];
|
|
|
+
|
|
|
+const sourceNodes = [
|
|
|
+ { title: 'ERP / SAP', desc: '物料、供应商、采购与财务主数据', meta: 'DB / API PULL', status: '正常', statusType: 'success', detailKey: 'source-erp' },
|
|
|
+ { title: 'MES 生产系统', desc: '工单执行、过站、报工与设备数据', meta: 'DB / API IN', status: '规划中', statusType: 'info', detailKey: 'source-mes' },
|
|
|
+ { title: 'WMS 仓储系统', desc: '库存快照、入出库事件与库位状态', meta: 'API IN / PULL', status: '延迟', statusType: 'warning', detailKey: 'source-wms' },
|
|
|
+];
|
|
|
+
|
|
|
+const accessNodes = [
|
|
|
+ { title: 'DB 同步接入', desc: '通过任务调度周期读取表 / 视图 / 增量字段', icon: 'DB', kind: 'kind-db', detailKey: 'access-db' },
|
|
|
+ { title: 'API 入站接收', desc: 'Webhook / REST 推送', icon: 'IN', kind: 'kind-api-in', detailKey: 'access-api-in' },
|
|
|
+ { title: 'API 定时拉取', desc: '分页 / 游标 / 时间窗', icon: 'GET', kind: 'kind-api-pull', detailKey: 'access-api-pull' },
|
|
|
+];
|
|
|
+
|
|
|
+const pipelineSteps = [
|
|
|
+ { index: '01', title: '原始接入区', desc: '先按来源原样落地,保留批次和追溯信息', detailKey: 'stage-raw' },
|
|
|
+ { index: '02', title: '清洗校验', desc: '必填、字典、格式和异常样本', detailKey: 'stage-clean' },
|
|
|
+ { index: '03', title: '字段映射', desc: '源字段到标准字段转换', detailKey: 'stage-mapping' },
|
|
|
+ { index: '04', title: '标准模型', desc: '主数据、业务事实、主题模型', detailKey: 'stage-standard' },
|
|
|
+ { index: '05', title: '指标与服务', desc: '面向页面、报表和外部 API', detailKey: 'stage-service' },
|
|
|
+];
|
|
|
+
|
|
|
+const serviceNodes = [
|
|
|
+ { title: 'API 出站服务', desc: '对外提供标准数据接口', icon: 'OUT', kind: 'kind-api-out', detailKey: 'service-api-out' },
|
|
|
+ { title: '业务查询服务', desc: 'S3 / S4 / S8 页面消费', icon: 'APP', kind: 'kind-db', detailKey: 'service-query' },
|
|
|
+ { title: '同步日志追踪', desc: '批次、行数、异常和重试', icon: 'LOG', kind: 'kind-job', detailKey: 'service-log' },
|
|
|
+];
|
|
|
+
|
|
|
+const consumerNodes = [
|
|
|
+ { title: '业务作业', desc: '采购、生产、交付等业务页面直接消费', meta: '业务操作', type: '业务', detailKey: 'consumer-work' },
|
|
|
+ { title: '异常监控', desc: '规则识别、异常跟踪、任务流转', meta: '监控闭环', type: '监控', detailKey: 'consumer-exception' },
|
|
|
+ { title: '指标看板', desc: '经营指标、模块看板、BI 报表', meta: '数据分析', type: '看板', detailKey: 'consumer-dashboard' },
|
|
|
+ { title: '外部系统', desc: '通过标准 API 回写或提供数据服务', meta: 'API OUT', type: '服务', detailKey: 'consumer-external' },
|
|
|
+];
|
|
|
+
|
|
|
+const detailVisible = ref(false);
|
|
|
+const activeDetailKey = ref('access-db');
|
|
|
+const detailMap: Record<string, any> = {
|
|
|
+ 'source-erp': { title: 'ERP / SAP', category: '外部系统', tagType: 'success', summary: '提供物料、供应商、采购、财务等主数据,可通过数据库同步或 API 拉取进入数据中台。', objects: [{ label: '接入方式', value: 'DB 同步 / API 拉取' }, { label: '关联任务', value: '物料主数据 API 拉取' }, { label: '健康状态', value: '正常' }], actions: [{ label: '去数据源管理', path: '/aidop/data-platform/sources' }, { label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }] },
|
|
|
+ 'source-mes': { title: 'MES 生产系统', category: '外部系统', tagType: 'info', summary: '提供工单执行、过站、报工和设备数据,后续用于生产执行、异常监控和指标看板。', objects: [{ label: '接入方式', value: 'DB / API 入站' }, { label: '状态', value: '规划中' }, { label: '消费方', value: '业务作业、异常监控' }], actions: [{ label: '去数据源管理', path: '/aidop/data-platform/sources' }] },
|
|
|
+ 'source-wms': { title: 'WMS 仓储系统', category: '外部系统', tagType: 'warning', summary: '提供库存快照、入出库事件和库位状态,支持 API 入站推送或数据中台定时拉取。', objects: [{ label: '接入方式', value: 'API 入站 / API 拉取' }, { label: '最近状态', value: '延迟' }, { label: '异常查看', value: '数据任务日志' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }, { label: '去任务日志', path: '/aidop/data-platform/sync-logs' }] },
|
|
|
+ 'access-db': { title: 'DB 同步接入', category: '接入层', tagType: 'primary', summary: '通过任务调度周期读取外部系统数据库表、视图或增量字段,适合批量同步和可追溯入库。', objects: [{ label: '典型来源', value: 'ERP / SAP、MES' }, { label: '关联 Job', value: 'job_s3_mdp_sync_transform' }, { label: '目标表', value: 'mdp_stg_* / mdp_std_*' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }, { label: '去任务调度', path: '/platform/job' }, { label: '去任务日志', path: '/aidop/data-platform/sync-logs' }] },
|
|
|
+ 'access-api-in': { title: 'API 入站接收', category: '接入层', tagType: 'success', summary: '外部系统主动调用数据中台入站 API,适合事件推送、状态回写和实时性要求较高的场景。', objects: [{ label: '协议', value: 'REST / Webhook' }, { label: '认证方式', value: 'Token / API Key' }, { label: '日志', value: '请求批次与异常样本' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }, { label: '去任务日志', path: '/aidop/data-platform/sync-logs' }] },
|
|
|
+ 'access-api-pull': { title: 'API 定时拉取', category: '接入层', tagType: 'warning', summary: '数据中台按分页、游标或时间窗定时调用外部 API,适合外部系统不支持主动推送的场景。', objects: [{ label: '策略', value: '分页 / 游标 / 时间窗' }, { label: '调度', value: '复用任务调度' }, { label: '失败处理', value: '重试与日志追踪' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }, { label: '去任务调度', path: '/platform/job' }] },
|
|
|
+ 'stage-raw': { title: '原始接入区', category: '数据中台', tagType: 'info', summary: '先把来源数据原样落地,保留批次号、来源系统和时间戳,便于追溯和重放。', objects: [{ label: '对应表', value: 'mdp_stg_*' }, { label: '保留信息', value: '来源、批次、原始字段' }], actions: [{ label: '去任务日志', path: '/aidop/data-platform/sync-logs' }] },
|
|
|
+ 'stage-clean': { title: '清洗校验', category: '数据中台', tagType: 'warning', summary: '对必填字段、字典值、格式和重复数据进行校验,异常样本进入日志追踪。', objects: [{ label: '校验内容', value: '必填、字典、格式、重复' }, { label: '异常出口', value: '数据任务日志' }], actions: [{ label: '去任务日志', path: '/aidop/data-platform/sync-logs' }] },
|
|
|
+ 'stage-mapping': { title: '字段映射', category: '数据中台', tagType: 'primary', summary: '把外部系统字段转换为新系统统一字段,屏蔽各系统编码和字段命名差异。', objects: [{ label: '配置表', value: 'mdp_field_mapping' }, { label: '映射方式', value: '直连、常量、脚本、字典' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }] },
|
|
|
+ 'stage-standard': { title: '标准模型', category: '数据中台', tagType: 'success', summary: '形成主数据、业务事实和主题模型,供业务页面、异常监控和指标看板复用。', objects: [{ label: '模型类型', value: '主数据 / 事实 / 主题' }, { label: '示例', value: '物料、供应商、采购订单' }], actions: [{ label: '去数据地图', path: '/aidop/data-platform/data-map' }] },
|
|
|
+ 'stage-service': { title: '指标与服务', category: '数据中台', tagType: 'success', summary: '把标准模型封装为页面查询、指标计算和对外 API 服务。', objects: [{ label: '服务对象', value: '业务作业、指标看板、外部系统' }, { label: '出口', value: 'API 出站 / 业务查询' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }] },
|
|
|
+ 'service-api-out': { title: 'API 出站服务', category: '服务出口', tagType: 'success', summary: '把数据中台标准数据以 API 形式提供给外部系统、BI 或移动端使用。', objects: [{ label: '协议', value: 'REST' }, { label: '认证', value: 'Token / API Key' }, { label: '状态', value: '规划中' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }] },
|
|
|
+ 'service-query': { title: '业务查询服务', category: '服务出口', tagType: 'primary', summary: '为 S3、S4、S8 等业务页面提供统一查询能力,减少页面直接依赖外部系统。', objects: [{ label: '消费方', value: '业务作业、异常监控' }, { label: '服务类型', value: '页面查询 / 聚合查询' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }] },
|
|
|
+ 'service-log': { title: '同步日志追踪', category: '服务出口', tagType: 'warning', summary: '记录每次数据任务的读取、写入、异常和耗时,用于演示和排障。', objects: [{ label: '日志表', value: 'mdp_sync_log' }, { label: '内容', value: '批次、行数、异常样本' }], actions: [{ label: '去任务日志', path: '/aidop/data-platform/sync-logs' }] },
|
|
|
+ 'consumer-work': { title: '业务作业', category: '业务消费', tagType: 'primary', summary: '采购、生产、交付等业务页面直接消费数据中台标准模型。', objects: [{ label: '示例', value: 'S3、S4 业务页面' }, { label: '数据来源', value: '标准模型 / 业务查询服务' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }] },
|
|
|
+ 'consumer-exception': { title: '异常监控', category: '业务消费', tagType: 'warning', summary: '规则识别、异常跟踪和任务流转基于数据中台统一数据口径。', objects: [{ label: '示例', value: 'S8 异常监控' }, { label: '数据来源', value: '规则模型 / 标准模型' }], actions: [{ label: '去任务日志', path: '/aidop/data-platform/sync-logs' }] },
|
|
|
+ 'consumer-dashboard': { title: '指标看板', category: '业务消费', tagType: 'success', summary: '经营指标、模块看板和 BI 报表使用中台模型沉淀后的指标口径。', objects: [{ label: '示例', value: '智慧运营看板 / BI' }, { label: '出口', value: '指标与服务' }], actions: [{ label: '去数据地图', path: '/aidop/data-platform/data-map' }] },
|
|
|
+ 'consumer-external': { title: '外部系统', category: '业务消费', tagType: 'info', summary: '外部系统可以通过标准 API 获取或回写数据,形成数据闭环。', objects: [{ label: '方向', value: 'API 出站 / 回写' }, { label: '认证', value: 'Token / API Key' }], actions: [{ label: '去数据任务', path: '/aidop/data-platform/sync-tasks' }] },
|
|
|
+};
|
|
|
+
|
|
|
+const currentDetail = computed(() => detailMap[activeDetailKey.value]);
|
|
|
+
|
|
|
+function openDetail(key: string) {
|
|
|
+ activeDetailKey.value = key;
|
|
|
+ detailVisible.value = true;
|
|
|
+}
|
|
|
+
|
|
|
+function go(path: string) {
|
|
|
+ router.push(path);
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.data-map {
|
|
|
+ background: linear-gradient(180deg, #f7f9fc 0%, #eef3f8 100%);
|
|
|
+ padding: 18px;
|
|
|
+}
|
|
|
+.map-hero {
|
|
|
+ align-items: flex-start;
|
|
|
+ background: linear-gradient(135deg, #10223d 0%, #173c67 60%, #1f6f8b 100%);
|
|
|
+ border-radius: 14px;
|
|
|
+ color: #fff;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 14px;
|
|
|
+ padding: 18px 20px;
|
|
|
+}
|
|
|
+.eyebrow {
|
|
|
+ color: rgba(255, 255, 255, 0.68);
|
|
|
+ font-size: 12px;
|
|
|
+ letter-spacing: 0.12em;
|
|
|
+ text-transform: uppercase;
|
|
|
+}
|
|
|
+.map-hero h3 {
|
|
|
+ font-size: 24px;
|
|
|
+ margin: 6px 0;
|
|
|
+}
|
|
|
+.map-hero p {
|
|
|
+ color: rgba(255, 255, 255, 0.78);
|
|
|
+ margin: 0;
|
|
|
+ max-width: 720px;
|
|
|
+}
|
|
|
+.legend {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ justify-content: flex-end;
|
|
|
+ max-width: 360px;
|
|
|
+}
|
|
|
+.legend span {
|
|
|
+ align-items: center;
|
|
|
+ background: rgba(255, 255, 255, 0.12);
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.18);
|
|
|
+ border-radius: 999px;
|
|
|
+ display: inline-flex;
|
|
|
+ font-size: 12px;
|
|
|
+ gap: 6px;
|
|
|
+ padding: 6px 10px;
|
|
|
+}
|
|
|
+.legend i {
|
|
|
+ border-radius: 999px;
|
|
|
+ display: inline-block;
|
|
|
+ height: 8px;
|
|
|
+ width: 8px;
|
|
|
+}
|
|
|
+.map-canvas {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #dbe4ef;
|
|
|
+ border-radius: 14px;
|
|
|
+ display: grid;
|
|
|
+ gap: 16px;
|
|
|
+ grid-template-columns: 1.1fr 1fr 1.45fr 1fr 1.1fr;
|
|
|
+ min-height: 560px;
|
|
|
+ overflow: hidden;
|
|
|
+ padding: 18px;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+.flow-steps {
|
|
|
+ align-items: center;
|
|
|
+ background: linear-gradient(90deg, rgba(47, 128, 237, 0.1), rgba(25, 169, 116, 0.1));
|
|
|
+ border: 1px solid #d8e7f5;
|
|
|
+ border-radius: 12px;
|
|
|
+ display: grid;
|
|
|
+ grid-column: 1 / -1;
|
|
|
+ grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
|
+ margin-bottom: 2px;
|
|
|
+ padding: 10px 12px;
|
|
|
+ position: relative;
|
|
|
+ z-index: 3;
|
|
|
+}
|
|
|
+.flow-step {
|
|
|
+ align-items: center;
|
|
|
+ color: #17324d;
|
|
|
+ display: flex;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 700;
|
|
|
+ justify-content: center;
|
|
|
+ min-height: 30px;
|
|
|
+ position: relative;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+.flow-step span {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #cfe0ef;
|
|
|
+ border-radius: 999px;
|
|
|
+ box-shadow: 0 6px 14px rgba(20, 43, 77, 0.08);
|
|
|
+ padding: 7px 12px;
|
|
|
+}
|
|
|
+.flow-step i {
|
|
|
+ color: #2f80ed;
|
|
|
+ font-size: 26px;
|
|
|
+ font-style: normal;
|
|
|
+ font-weight: 800;
|
|
|
+ position: absolute;
|
|
|
+ right: -14px;
|
|
|
+}
|
|
|
+.lane,
|
|
|
+.hub {
|
|
|
+ position: relative;
|
|
|
+ z-index: 2;
|
|
|
+}
|
|
|
+.lane__title {
|
|
|
+ color: #56657a;
|
|
|
+ display: none;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 700;
|
|
|
+ letter-spacing: 0.05em;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+.node-card,
|
|
|
+.process-card,
|
|
|
+.pipeline-step {
|
|
|
+ background: rgba(255, 255, 255, 0.96);
|
|
|
+ border: 1px solid #dce5ef;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 8px 22px rgba(20, 43, 77, 0.06);
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding: 12px;
|
|
|
+}
|
|
|
+.is-clickable {
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ transition: border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
|
|
|
+}
|
|
|
+.is-clickable:hover {
|
|
|
+ border-color: #2f80ed;
|
|
|
+ box-shadow: 0 10px 26px rgba(47, 128, 237, 0.14);
|
|
|
+ transform: translateY(-1px);
|
|
|
+}
|
|
|
+.is-clickable:hover::after {
|
|
|
+ color: #2f80ed;
|
|
|
+ content: '点击查看';
|
|
|
+ font-size: 12px;
|
|
|
+ position: absolute;
|
|
|
+ right: 10px;
|
|
|
+ top: -18px;
|
|
|
+}
|
|
|
+.node-card__top {
|
|
|
+ align-items: center;
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+.node-card__title,
|
|
|
+.process-card__title,
|
|
|
+.pipeline-step__title {
|
|
|
+ color: #172033;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+.node-card__desc,
|
|
|
+.process-card__desc,
|
|
|
+.pipeline-step__desc,
|
|
|
+.node-card__meta {
|
|
|
+ color: #65738a;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1.6;
|
|
|
+ margin-top: 4px;
|
|
|
+}
|
|
|
+.node-card__meta {
|
|
|
+ color: #2f6f9f;
|
|
|
+ font-family: Consolas, Monaco, monospace;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+.process-card {
|
|
|
+ align-items: center;
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+.process-card__icon {
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 10px;
|
|
|
+ color: #fff;
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 40px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 700;
|
|
|
+ height: 40px;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+.kind-db .process-card__icon {
|
|
|
+ background: #2f80ed;
|
|
|
+}
|
|
|
+.kind-api-in .process-card__icon {
|
|
|
+ background: #7b61ff;
|
|
|
+}
|
|
|
+.kind-api-pull .process-card__icon {
|
|
|
+ background: #00a7b5;
|
|
|
+}
|
|
|
+.kind-api-out .process-card__icon {
|
|
|
+ background: #19a974;
|
|
|
+}
|
|
|
+.kind-job .process-card__icon {
|
|
|
+ background: #f39c12;
|
|
|
+}
|
|
|
+.hub {
|
|
|
+ align-items: center;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+.hub__ring {
|
|
|
+ align-items: center;
|
|
|
+ background: radial-gradient(circle, rgba(47, 128, 237, 0.18), rgba(47, 128, 237, 0.04) 68%);
|
|
|
+ border: 1px solid rgba(47, 128, 237, 0.2);
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ height: 190px;
|
|
|
+ justify-content: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ width: 190px;
|
|
|
+}
|
|
|
+.hub__core {
|
|
|
+ align-items: center;
|
|
|
+ background: linear-gradient(135deg, #163a63, #1d6f92);
|
|
|
+ border-radius: 50%;
|
|
|
+ box-shadow: 0 18px 36px rgba(22, 58, 99, 0.24);
|
|
|
+ color: #fff;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 132px;
|
|
|
+ justify-content: center;
|
|
|
+ text-align: center;
|
|
|
+ width: 132px;
|
|
|
+}
|
|
|
+.hub__title {
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+.hub__desc {
|
|
|
+ color: rgba(255, 255, 255, 0.75);
|
|
|
+ font-size: 12px;
|
|
|
+ margin-top: 6px;
|
|
|
+}
|
|
|
+.pipeline {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.pipeline-step {
|
|
|
+ align-items: center;
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ padding: 10px;
|
|
|
+}
|
|
|
+.pipeline-step__index {
|
|
|
+ color: #2f80ed;
|
|
|
+ font-weight: 800;
|
|
|
+}
|
|
|
+.legend-db,
|
|
|
+.channel-dot.legend-db {
|
|
|
+ background: #2f80ed;
|
|
|
+}
|
|
|
+.legend-api-in,
|
|
|
+.channel-dot.legend-api-in {
|
|
|
+ background: #7b61ff;
|
|
|
+}
|
|
|
+.legend-api-pull,
|
|
|
+.channel-dot.legend-api-pull {
|
|
|
+ background: #00a7b5;
|
|
|
+}
|
|
|
+.legend-api-out,
|
|
|
+.legend-api-out {
|
|
|
+ background: #19a974;
|
|
|
+}
|
|
|
+.detail-summary {
|
|
|
+ color: #3f4d60;
|
|
|
+ line-height: 1.8;
|
|
|
+ margin: 14px 0 18px;
|
|
|
+}
|
|
|
+.detail-section {
|
|
|
+ border-top: 1px solid #edf1f6;
|
|
|
+ padding: 14px 0;
|
|
|
+}
|
|
|
+.detail-section__title {
|
|
|
+ color: #172033;
|
|
|
+ font-weight: 700;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+.detail-object {
|
|
|
+ display: grid;
|
|
|
+ gap: 10px;
|
|
|
+ grid-template-columns: 92px 1fr;
|
|
|
+ line-height: 28px;
|
|
|
+}
|
|
|
+.detail-object span {
|
|
|
+ color: #65738a;
|
|
|
+}
|
|
|
+.detail-object b {
|
|
|
+ color: #172033;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+.detail-actions {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+</style>
|