From 3471290659516cf21db3211a9053daff5f283e03 Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期五, 20 三月 2026 15:50:18 +0800
Subject: [PATCH] feat: 基础数据仪器管理、判定依据、判定依据明细
---
ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 282 insertions(+), 0 deletions(-)
diff --git a/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue
new file mode 100755
index 0000000..4021450
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue
@@ -0,0 +1,282 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { useEcharts } from '@/hooks/common/echarts';
+
+defineOptions({
+ name: 'StoreSilkSankey'
+});
+
+interface StoreSilkDetailVo {
+ fsNum?: string;
+ siloNum?: string;
+ equNo?: string;
+ output?: number | null;
+}
+
+interface Props {
+ rollerRecordList?: StoreSilkDetailVo[];
+ packerRecordList?: StoreSilkDetailVo[];
+ rows?: any[];
+}
+
+const props = withDefaults(defineProps<Props>(), {
+ rollerRecordList: () => [],
+ packerRecordList: () => [],
+ rows: () => []
+});
+
+type SankeyType = 'roller' | 'packer';
+
+const sankeyType = ref<SankeyType>('roller');
+
+function normalizeNumber(val: unknown) {
+ const v = Number(val);
+ if (!Number.isFinite(v)) return null;
+ return v;
+}
+
+function calcRollerBox(val: unknown) {
+ if (val === null || val === undefined) return null;
+ const v = Number(val) / 50;
+ if (!Number.isFinite(v)) return null;
+ return v;
+}
+
+function calcPackerBox(val: unknown) {
+ if (val === null || val === undefined) return null;
+ const v = Number(val) / 10 / 250;
+ if (!Number.isFinite(v)) return null;
+ return v;
+}
+
+function formatTime(val: unknown) {
+ if (!val) return '';
+ const s = String(val);
+ return s.length > 19 ? s.slice(0, 19) : s;
+}
+
+function buildChartData(list: StoreSilkDetailVo[], type: SankeyType) {
+ const nodeSet = new Set<string>();
+ const linkMap = new Map<string, { source: string; target: string; value: number }>();
+ const nodeInValue = new Map<string, number>();
+ const nodeOutValue = new Map<string, number>();
+ const nodeExtra = new Map<string, any>();
+ const rows = Array.isArray(props.rows) ? props.rows : [];
+
+ function addLink(source: string, target: string, value: number) {
+ if (value <= 0) return;
+ nodeSet.add(source);
+ nodeSet.add(target);
+ nodeInValue.set(target, (nodeInValue.get(target) ?? 0) + value);
+ nodeOutValue.set(source, (nodeOutValue.get(source) ?? 0) + value);
+ const key = `${source}__->__${target}`;
+ const existed = linkMap.get(key);
+ if (existed) {
+ existed.value += value;
+ return;
+ }
+ linkMap.set(key, { source, target, value });
+ }
+
+ for (const item of list) {
+ const val = normalizeNumber(item.output);
+ if (val === null) continue;
+ if (val <= 0) continue;
+
+ const fsBase = `鍠備笣鏈� ${item.fsNum || '-'}`;
+ const siloBase = `鍌ㄤ笣鏌� ${item.siloNum || '-'}`;
+ const equ = item.equNo ? `${item.equNo}#${type === 'roller' ? '鍗锋帴鏈�' : '鍖呰鏈�'}` : type === 'roller' ? '鍗锋帴鏈�' : '鍖呰鏈�';
+ const target = equ;
+ const fs = fsBase;
+ const silo = siloBase;
+
+ addLink(fs, silo, val);
+ addLink(silo, target, val);
+
+ const parentRow = rows.find((r: any) => {
+ const dList = type === 'roller' ? r?.rollerDetailList : r?.packerDetailList;
+ if (!Array.isArray(dList)) return false;
+ return dList.some((d: any) => d?.fsNum === item.fsNum && d?.siloNum === item.siloNum && d?.equNo === item.equNo);
+ });
+
+ if (parentRow) {
+ const materialname = String(parentRow.materialname ?? '');
+ const batchcode = String(parentRow.batchcode ?? parentRow.batchCode ?? '');
+ const jobinput = Number(parentRow.jobinput);
+ const distimebegin = formatTime(parentRow.distimebegin);
+ const distimeend = formatTime(parentRow.distimeend);
+
+ nodeExtra.set(fs, {
+ kind: 'fs',
+ displayName: fsBase,
+ meta: { materialname, batchcode, jobinput }
+ });
+ nodeExtra.set(silo, {
+ kind: 'silo',
+ displayName: siloBase,
+ meta: { distimebegin, distimeend }
+ });
+ }
+ }
+
+ const nodes = Array.from(nodeSet).map(name => {
+ const inV = nodeInValue.get(name) ?? 0;
+ const outV = nodeOutValue.get(name) ?? 0;
+ const value = inV > 0 ? inV : outV;
+ return { name, value, ...(nodeExtra.get(name) ?? {}) };
+ });
+ const links = Array.from(linkMap.values());
+
+ return { nodes, links };
+}
+
+const activeList = computed(() => {
+ return sankeyType.value === 'roller' ? props.rollerRecordList : props.packerRecordList;
+});
+
+watch(
+ () => [props.rollerRecordList, props.packerRecordList],
+ () => {
+ const rollerLen = Array.isArray(props.rollerRecordList) ? props.rollerRecordList.length : 0;
+ const packerLen = Array.isArray(props.packerRecordList) ? props.packerRecordList.length : 0;
+
+ if (sankeyType.value === 'roller' && rollerLen === 0 && packerLen > 0) {
+ sankeyType.value = 'packer';
+ return;
+ }
+
+ if (sankeyType.value === 'packer' && packerLen === 0 && rollerLen > 0) {
+ sankeyType.value = 'roller';
+ }
+ },
+ { deep: true, immediate: true }
+);
+
+const hasData = computed(() => {
+ return Boolean(activeList.value && activeList.value.length > 0);
+});
+
+const { domRef, updateOptions } = useEcharts(() => {
+ const { nodes, links } = buildChartData(activeList.value, sankeyType.value);
+
+ return {
+ tooltip: {
+ trigger: 'item'
+ },
+ series: [
+ {
+ type: 'sankey',
+ data: nodes,
+ links,
+ nodeAlign: 'justify',
+ layoutIterations: 32,
+ emphasis: {
+ focus: 'adjacency'
+ },
+ lineStyle: {
+ color: 'source',
+ curveness: 0.5,
+ opacity: 0.35
+ },
+ label: {
+ position: 'right',
+ formatter: (params: any) => {
+ const name = String(params?.data?.name ?? '');
+ const value = params?.data?.value;
+ const kind = params?.data?.kind;
+ const displayName = String(params?.data?.displayName ?? name);
+ const meta = params?.data?.meta;
+
+ if (kind === 'fs') {
+ const materialLine = meta?.materialname ? `鐗屽彿锛�${meta.materialname}` : '';
+ const batchLine = meta?.batchcode ? `鎵规锛�${meta.batchcode}` : '';
+ const jobLine =
+ Number.isFinite(Number(meta?.jobinput)) ? `鎶曟枡閲嶉噺锛�${Number(meta.jobinput).toFixed(2)}kg` : '';
+ const lines: string[] = [displayName];
+ lines.push('');
+ if (materialLine) lines.push(materialLine);
+ if (batchLine) lines.push(batchLine);
+ if (jobLine) {
+ lines.push('');
+ lines.push(jobLine);
+ }
+ return lines.join('\n');
+ }
+
+ if (kind === 'silo') {
+ const beginLine = meta?.distimebegin ? `寮�濮嬶細${meta.distimebegin}` : '';
+ const endLine = meta?.distimeend ? `缁撴潫锛�${meta.distimeend}` : '';
+ const valLine =
+ typeof value === 'number' && Number.isFinite(value) ? `浜ч噺锛�${value.toFixed(2)}绠盽 : '';
+ const lines: string[] = [displayName];
+ lines.push('');
+ if (beginLine) lines.push(beginLine);
+ if (endLine) lines.push(endLine);
+ if (valLine) {
+ lines.push('');
+ lines.push(valLine);
+ }
+ return lines.join('\n');
+ }
+ const showValue =
+ name.startsWith('鍌ㄤ笣鏌� ') ||
+ name.endsWith('#鍗锋帴鏈�') ||
+ name.endsWith('#鍖呰鏈�') ||
+ name === '鍗锋帴鏈�' ||
+ name === '鍖呰鏈�';
+
+ if (showValue && typeof value === 'number' && Number.isFinite(value)) {
+ return `${displayName}\n${value.toFixed(2)}绠盽;
+ }
+ return displayName;
+ }
+ }
+ },
+ ]
+ };
+});
+
+watch(
+ () => [props.rollerRecordList, props.packerRecordList, sankeyType.value],
+ () => {
+ updateOptions(opts => {
+ const { nodes, links } = buildChartData(activeList.value, sankeyType.value);
+ opts.series[0].data = nodes as any;
+ (opts.series[0] as any).links = links;
+ return opts;
+ });
+ },
+ { deep: true, immediate: true }
+);
+</script>
+
+<template>
+ <div class="h-full flex-col-stretch">
+ <div class="pb-8px">
+ <NSpace :size="8">
+ <NButton
+ size="small"
+ ghost
+ :type="sankeyType === 'roller' ? 'primary' : 'default'"
+ @click="sankeyType = 'roller'"
+ >
+ 鍗锋帴
+ </NButton>
+ <NButton
+ size="small"
+ ghost
+ :type="sankeyType === 'packer' ? 'primary' : 'default'"
+ @click="sankeyType = 'packer'"
+ >
+ 鍖呰
+ </NButton>
+ </NSpace>
+ </div>
+ <div v-if="!hasData" class="flex-center flex-1 text-gray-400">
+ 鏆傛棤{{ sankeyType === 'roller' ? '鍗锋帴' : '鍖呰' }}妗戝熀鍥炬暟鎹�
+ </div>
+ <div v-else ref="domRef" class="flex-1 min-h-360px overflow-hidden"></div>
+ </div>
+</template>
+
+<style scoped></style>
--
Gitblit v1.9.3