From 993e5fd593398926af72af660cb5ed6aba8e4e2b Mon Sep 17 00:00:00 2001 From: 13404089107 <puwei@sinata.cn> Date: 星期二, 20 五月 2025 16:43:04 +0800 Subject: [PATCH] 对接接口 --- culture/src/views/pedigree-chart/add.vue | 808 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 727 insertions(+), 81 deletions(-) diff --git a/culture/src/views/pedigree-chart/add.vue b/culture/src/views/pedigree-chart/add.vue index 6f3d77f..65a1944 100644 --- a/culture/src/views/pedigree-chart/add.vue +++ b/culture/src/views/pedigree-chart/add.vue @@ -1,93 +1,117 @@ <template> - <el-form - :model="form" - :rules="rules" - ref="pedigreeForm" - label-position="top" - class="strain-form" - > - <div class="card"> - <div class="form-items-row"> - <el-form-item label="菌种源" prop="strainSource" required> - <div class="flex-row"> - <div class="input-wrapper"> - <el-input - v-model="form.strainSource" - placeholder="请输入" - class="fixed-width-input" - ></el-input> + <div> + <el-form :model="form" :rules="rules" ref="pedigreeForm" label-position="top" class="strain-form"> + <div class="card"> + <div class="form-items-row"> + <el-form-item label="菌种源" required> + <div class="flex-row"> + <div class="input-wrapper"> + <el-form-item prop="strainSourceStart" style="margin-bottom: 0;"> + <el-input v-model="form.strainSourceStart" placeholder="请输入" class="fixed-width-input"></el-input> + </el-form-item> + </div> + <span class="form-text">代—</span> + <div class="input-wrapper"> + <el-form-item prop="strainSourceEnd" style="margin-bottom: 0;"> + <el-input v-model="form.strainSourceEnd" placeholder="请输入" class="fixed-width-input"></el-input> + </el-form-item> + </div> + <span class="form-text">细胞库</span> </div> - <span class="form-text">代—</span> - <div class="input-wrapper"> - <el-input - v-model="form.generation" - placeholder="请输入" - class="fixed-width-input" - ></el-input> - </div> - <span class="form-text">细胞库</span> - </div> - </el-form-item> - <el-form-item label="传代菌种编号" prop="strainNo" required> - <el-input - v-model="form.strainNo" - placeholder="请输入" - class="fixed-width-input" - ></el-input> - </el-form-item> - <el-form-item label="传代菌种名称" prop="strainName" required> - <el-input - v-model="form.strainName" - placeholder="请输入" - class="fixed-width-input" - ></el-input> - </el-form-item> - </div> - </div> - <div class="chart"> - <div class="header"> - <div class="title">菌种传代生产谱系图</div> - <div class="option-btn"> - <el-button type="primary" class="el-icon-plus"> 新增</el-button> - <el-button type="primary">设置传代计划数</el-button> - <el-button type="primary">详情</el-button> + </el-form-item> + <el-form-item label="传代菌种编号" prop="strainNo" required> + <el-input v-model="form.strainNo" placeholder="请输入" class="fixed-width-input"></el-input> + </el-form-item> + <el-form-item label="传代菌种名称" prop="strainName" required> + <el-input v-model="form.strainName" placeholder="请输入" class="fixed-width-input"></el-input> + </el-form-item> </div> </div> - </div> - <div class="end-btn"> - <el-button type="primary" @click="handleSubmit">提交</el-button> - <el-button @click="handleDraft">存草稿</el-button> - <el-button @click="handleCancel">取消</el-button> - </div> - <!-- 签字确认组件 --> - <SignatureCanvas - :visible.sync="signatureVisible" - @confirm="handleSignatureConfirm" - /> - </el-form> + <div class="card" style="margin-top: 30px;"> + <Table :height="null" :total="0" :tableData="tableData"> + <el-table-column label="接种操作人" prop="strainSource" /> + <el-table-column label="接种操作时间" prop="strainNo" /> + <el-table-column label="传代菌种编号" prop="strainName" /> + <el-table-column label="传代菌种名称" prop="strainName" /> + <el-table-column label="接种菌种编号" prop="strainName" /> + <el-table-column label="接种菌种名称" prop="strainName" /> + <el-table-column label="入库总数" prop="strainName" /> + <el-table-column label="保存/废弃" prop="strainName" /> + <el-table-column label="入库时间" prop="strainName" /> + <el-table-column label="操作"> + <template #default="{ row }"> + <el-button type="text">确认入库</el-button> + </template> + </el-table-column> + </Table> + </div> + <div class="chart"> + <div class="header"> + <div class="title">菌种传代生产谱系图</div> + <div class="option-btn"> + <el-button type="primary" class="el-icon-plus" @click="addNode"> 新增</el-button> + <el-button type="primary" @click="setGenerationPlan">设置传代计划数</el-button> + <el-button type="primary" @click="showDetail">详情</el-button> + </div> + </div> + + <div class="strain-flow-chart"> + <div id="mountNode"></div> + </div> + <el-button type="primary" @click="handleSubmit" style="width: 150px;">保存</el-button> + + </div> + <div class="end-btn"> + <!-- <el-button @click="handleDraft">存草稿</el-button> + <el-button @click="handleCancel">取消</el-button> --> + </div> + </el-form> + + <ParentForm ref="parentForm" @addNodeSign="addNodeSign" /> + <PlanForm ref="planForm" @addNodeSign="addNodeSign" /> + <AddSublevelForm ref="addSublevelForm" @addNodeSign="addNodeSign" /> + <AddSublevelPlan ref="addSublevelPlan" @addNodeSign="addNodeSign" /> + <ConfirmStorageDialog name="接种操作人签字" :visible.sync="confirmStorageDialogVisible" + @confirm="handleSignatureConfirm" /> + <!-- 菌种工程师 --> + <ConfirmStorageDialog name="菌种保藏人签字" text="是否确认该项菌种信息入库" :visible.sync="storageVisible" + @confirm="handleSignatureConfirm" /> + </div> </template> <script> -import SignatureCanvas from "@/components/SignatureCanvas.vue"; +import G6 from '@antv/g6'; +import ParentForm from "./components/ParentForm.vue"; +import PlanForm from "./components/PlanForm.vue"; +import AddSublevelForm from "./components/AddSublevelForm.vue"; +import AddSublevelPlan from "./components/AddSublevelPlan.vue"; +import ConfirmStorageDialog from "@/components/confirm-storage-dialog"; export default { name: "AddPedigree", components: { - SignatureCanvas, + ParentForm, + PlanForm, + AddSublevelForm, + AddSublevelPlan, + ConfirmStorageDialog }, data() { return { signatureVisible: false, form: { - strainSource: "", - generation: "", + strainSourceStart: "", + strainSourceEnd: "", cellBank: "", strainNo: "", strainName: "", remarks: "", }, rules: { - strainSource: [ + strainSourceStart: [ + { required: true, message: "请输入菌种源", trigger: "blur" }, + ], + strainSourceEnd: [ { required: true, message: "请输入菌种源", trigger: "blur" }, ], strainNo: [ @@ -97,13 +121,58 @@ { required: true, message: "请输入传代菌种名称", trigger: "blur" }, ], }, + graph: null, + nodeCount: 0, + selectedNode: null, + graphData: { + nodes: [], + edges: [] + }, + // 弹窗相关数据 + dialogVisible: false, + dialogTitle: '', + formLabel: '', + inputType: 'text', + showDiscarded: false, + isAddingNode: false, + nodeData: {}, + nodeType: '',//1母代 2计划数 3子孙代 + tableData: [], + confirmStorageDialogVisible: false, + storageVisible: false }; }, + computed: { + canAddNode() { + // 如果没有节点,可以新增母代 + if (this.graphData.nodes.length === 0) { + return true; + } + // 如果选中了传代计划数节点,可以新增下一代 + if (this.selectedNode && this.selectedNode.label === '传代计划数') { + return true; + } + return false; + } + }, + mounted() { + this.initGraph(); + this.initEvents(); + }, + beforeDestroy() { + this.graph?.destroy(); + window.removeEventListener('resize', this.handleResize); + }, methods: { + addNodeSign(value, type) { + this.nodeData = value + this.nodeType = type + this.confirmStorageDialogVisible = true; + }, handleSubmit() { this.$refs.pedigreeForm.validate((valid) => { if (valid) { - this.signatureVisible = true; + this.confirmStorageDialogVisible = true; } }); }, @@ -115,11 +184,559 @@ this.$router.back(); }, handleSignatureConfirm(signatureImage) { - this.signatureVisible = false; + this.confirmStorageDialogVisible = false; + if (this.nodeType === 1) { + this.handleAddParent({ ...this.nodeData, signature: signatureImage.signature }) + } else if (this.nodeType === 2) { + this.handleAddPlan(this.nodeData) + } else if (this.nodeType === 3) { + this.handleAddSublevel(this.nodeData) + } // 处理提交逻辑 - console.log("submit form with signature:", this.form, signatureImage); - this.$router.back(); }, + initGraph() { + const container = document.getElementById('mountNode'); + const width = container.scrollWidth; + const height = container.scrollHeight || 600; + + // 自定义节点 + G6.registerNode('custom-node', { + draw(cfg, group) { + const width = 120; + const titleHeight = 30; + const contentHeight = 40; + const gap = 4; + const totalHeight = titleHeight + gap + contentHeight; + + // 根据节点状态设置颜色 + const isDiscarded = !cfg.isDiscarded; + const titleFill = isDiscarded ? 'rgba(245, 248, 250, 1)' : (cfg.selected ? 'l(0) 0:#0ACBCA 1:#049C9A' : 'l(0) 0:#0ACBCA 1:#049C9A'); + const contentFill = isDiscarded ? 'rgba(245, 248, 250, 1)' : (cfg.selected ? 'rgba(4,156,154,0.2)' : 'rgba(4,156,154,0.1)'); + const textFill = isDiscarded ? 'rgba(144, 147, 153, 1)' : '#049C9A'; + const stroke = isDiscarded ? '#DCDFE6' : (cfg.selected ? '#049C9A' : 'transparent'); + + // 创建渐变 + const gradient = group.addShape('rect', { + attrs: { + x: -width / 2, + y: -totalHeight / 2, + width: width, + height: titleHeight, + radius: 20, + fill: titleFill, + cursor: 'move', + stroke: stroke, + lineWidth: isDiscarded ? 1 : (cfg.selected ? 2 : 0), + }, + name: 'title-box', + }); + + // 下部分 - 内容背景 + const contentBox = group.addShape('rect', { + attrs: { + x: -width / 2, + y: -totalHeight / 2 + titleHeight + gap, + width: width, + height: contentHeight, + fill: contentFill, + radius: 15, + cursor: 'move', + stroke: stroke, + lineWidth: isDiscarded ? 1 : (cfg.selected ? 2 : 0), + }, + name: 'content-box', + }); + + // 标题文本 + if (cfg.label) { + group.addShape('text', { + attrs: { + text: cfg.label, + x: 0, + y: -totalHeight / 2 + titleHeight / 2, + fill: isDiscarded ? 'rgba(144, 147, 153, 1)' : '#fff', + fontSize: 12, + textAlign: 'center', + textBaseline: 'middle', + fontWeight: 'bold', + cursor: 'move', + }, + name: 'title-text', + }); + } + + // 内容文本 + let content = ''; + if (cfg.label === '传代计划数') { + content = `${cfg.planCount || 0}`; + } else if (cfg.number) { + content = cfg.label === '母代' ? `代传菌种编号:${cfg.number}` : `接种菌种编号:${cfg.number}`; + } + + if (content) { + group.addShape('text', { + attrs: { + text: content, + x: 0, + y: -totalHeight / 2 + titleHeight + gap + contentHeight / 2, + fill: textFill, + fontSize: 10, + textAlign: 'center', + textBaseline: 'middle', + cursor: 'move', + }, + name: 'content-text', + }); + } + + return gradient; + }, + getAnchorPoints() { + return [ + [0.5, 0], // 上 + [1, 0.5], // 右 + [0.5, 1], // 下 + [0, 0.5], // 左 + ]; + }, + setState(name, value, item) { + // 移除悬浮效果,保持节点样式始终一致 + }, + }); + + this.graph = new G6.Graph({ + container: 'mountNode', + width, + height, + fitView: true, + fitViewPadding: 30, + animate: false, + enabledStack: false, + renderer: 'canvas', + minZoom: 0.3, + maxZoom: 2, + defaultZoom: 1, + layout: { + type: 'dagre', + rankdir: 'LR', + align: 'UL', + nodesep: 30, // 减小节点间距 + ranksep: 50, // 减小层级间距 + controlPoints: true, + }, + modes: { + default: [ + { + type: 'drag-canvas', + enableOptimize: true, + direction: 'both', + scalableRange: 0.1, + dragTimesOfScale: 0.1, + onlyChangeComputeZoom: true, + }, + { + type: 'zoom-canvas', + sensitivity: 1.5, + enableOptimize: true, + }, + { + type: 'drag-node', + enableDelegate: true, + delegateStyle: { + fill: '#f3f3f3', + stroke: '#ccc', + opacity: 0.5, + }, + updateEdge: false, + enableOptimize: true, + optimizeZoom: 0.7, + damping: 0.1, + } + ] + }, + defaultNode: { + type: 'custom-node', + style: { + fill: 'l(0) 0:#0ACBCA 1:#049C9A', + }, + }, + defaultEdge: { + type: 'cubic-horizontal', + style: { + stroke: 'rgba(4, 156, 154, 1)', + lineWidth: 1, + opacity: 0.5, + endArrow: { + path: G6.Arrow.triangle(6, 6), + fill: 'rgba(4, 156, 154, 1)', + stroke: 'rgba(4, 156, 154, 1)', + }, + }, + }, + optimizeEdge: true, + optimizeLayoutAnimation: true, + }); + + const canvas = this.graph.get('canvas'); + canvas.set('localRefresh', false); + canvas.set('autoDraw', true); + canvas.set('animating', false); + + let throttleTimer = null; + const throttleInterval = 16; + + this.graph.on('node:dragstart', () => { + canvas.set('localRefresh', false); + this.graph.get('canvas').draw(); + }); + + this.graph.on('node:drag', (e) => { + if (throttleTimer) return; + throttleTimer = setTimeout(() => { + const model = e.item.get('model'); + const edges = this.graph.getEdges().filter(edge => { + const source = edge.getSource(); + const target = edge.getTarget(); + return source.get('id') === model.id || target.get('id') === model.id; + }); + edges.forEach(edge => { + this.graph.refreshItem(edge); + }); + throttleTimer = null; + }, throttleInterval); + }); + + this.graph.on('node:dragend', (e) => { + if (throttleTimer) { + clearTimeout(throttleTimer); + throttleTimer = null; + } + const model = e.item.get('model'); + const edges = this.graph.getEdges().filter(edge => { + const source = edge.getSource(); + const target = edge.getTarget(); + return source.get('id') === model.id || target.get('id') === model.id; + }); + edges.forEach(edge => { + this.graph.refreshItem(edge); + }); + canvas.set('localRefresh', true); + this.graph.get('canvas').draw(); + }); + + this.graph.data(this.graphData); + this.graph.render(); + + let debounceTimer = null; + this.graph.on('afterchange', () => { + if (debounceTimer) clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + if (!canvas.get('destroyed')) { + canvas.draw(); + } + }, 16); + }); + }, + initEvents() { + // 监听窗口大小变化 + window.addEventListener('resize', this.handleResize);// 添加触摸事件处理 + const handleNodeClick = (evt) => { + evt.preventDefault(); // 阻止默认触摸行为 + const node = evt.item; + const nodeModel = node.getModel(); + // 如果节点已废弃,不允许任何操作 + if (!nodeModel.isDiscarded) { + this.$message.warning('该节点已废弃,不能进行操作'); + return; + } + // 更新选中节点 + this.selectedNode = nodeModel; + // 更新节点选中状态 + this.graphData.nodes.forEach(n => { + n.selected = n.id === nodeModel.id; + }); + this.graph.changeData(this.graphData); + }; + this.graph.on('node:click', handleNodeClick); + this.graph.on('node:touchstart', handleNodeClick); + + // 画布点击事件,取消选中节点(添加触摸支持) + const handleCanvasClick = (evt) => { + evt.preventDefault(); + this.selectedNode = null; + this.graphData.nodes.forEach(n => { + n.selected = false; + }); + this.graph.changeData(this.graphData); + }; + + this.graph.on('canvas:click', handleCanvasClick); + this.graph.on('canvas:touchstart', handleCanvasClick); + }, + handleResize() { + if (this.graph) { + const container = document.getElementById('mountNode'); + const width = container.scrollWidth; + const height = container.scrollHeight || 600; + this.graph.changeSize(width, height); + } + }, + addNode() { + // 如果没有节点,新增母代 + if (this.graphData.nodes.length === 0) { + this.$refs.pedigreeForm.validate((valid) => { + if (valid) { + this.$refs.parentForm.openInitData({ + strainName: this.form.strainName, + strainNo: this.form.strainNo, + strainSourceStart: this.form.strainSourceStart, + strainSourceEnd: this.form.strainSourceEnd, + }); + } + }) + return + } + + // 如果选中了传代计划数节点,新增下一代 + if (this.selectedNode && this.selectedNode.label === '传代计划数') { + const nodeModel = this.selectedNode; + + // 检查是否已达到计划数 + if (nodeModel.currentCount >= nodeModel.planCount) { + this.$message.warning('已达到计划数,不能再添加'); + return; + } + + // 获取父节点 + const parentEdge = this.graphData.edges.find(e => e.target === nodeModel.id); + const parentNode = this.graphData.nodes.find(n => n.id === parentEdge.source); + + // 如果父节点是孙代,不允许添加 + if (parentNode.label === '孙代') { + this.$message.warning('孙代节点不能再生成下一代'); + return; + } + + const isParent = parentNode.label === '母代'; + const nextLevel = isParent ? '子代' : '孙代'; + + this.showDiscarded = true; + this.isAddingNode = true; + this.$refs.addSublevelForm.openInitData({ + title: `新增${nextLevel}`, + form: { + strainName: this.form.strainName, + strainNo: this.form.strainNo, + isDiscarded: true + } + }) + } else { + this.$message.warning('请选择传代计划数节点'); + } + }, + handleAddParent(value) { + console.log(value); + + const parentId = `parent-${++this.nodeCount}`; + this.graphData.nodes.push({ + id: parentId, + label: '母代', + number: value.strainNo.trim(), + data: value, + isDiscarded: true, + x: 200, + y: 200, + style: { + fill: '#00B5AA', + }, + }); + this.graph.changeData(this.graphData); + this.$message.success('母代节点添加成功'); + this.$refs.parentForm.closeDialog(); + }, + handleDialogClose() { + this.$refs.form.resetFields(); + }, + handleAddSublevel(value) { + if (this.isAddingNode) { + // 新增节点的处理逻辑 + const nodeModel = this.selectedNode; + const parentEdge = this.graphData.edges.find(e => e.target === nodeModel.id); + const parentNode = this.graphData.nodes.find(n => n.id === parentEdge.source); + const isParent = parentNode.label === '母代'; + const nextLevel = isParent ? '子代' : '孙代'; + + const childId = `child-${++this.nodeCount}`; + this.graphData.nodes.push({ + id: childId, + label: nextLevel, + number: value.inoculateNo.trim(), + isDiscarded: value.isDiscarded, + data: value, + style: { + fill: value.isDiscarded ? '#999' : '#00B5AA', + opacity: value.isDiscarded ? 0.3 : (isParent ? 0.6 : 0.4), + }, + }); + + this.graphData.edges.push({ + source: nodeModel.id, + target: childId, + style: { + stroke: 'rgba(4, 156, 154, 1)', + lineWidth: 1, + }, + }); + + const nodeIndex = this.graphData.nodes.findIndex(n => n.id === nodeModel.id); + this.graphData.nodes[nodeIndex].currentCount++; + + this.graph.changeData(this.graphData); + this.$message.success(`${nextLevel}添加成功`); + this.$refs.addSublevelForm.closeDialog(); + + this.isAddingNode = false; + } else { + // 编辑节点的处理逻辑 + const nodeModel = this.selectedNode; + const nodeIndex = this.graphData.nodes.findIndex(n => n.id === nodeModel.id); + + if (nodeIndex > -1) { + if (nodeModel.label === '传代计划数') { + this.graphData.nodes[nodeIndex].planCount = parseInt(value.value); + } else { + this.graphData.nodes[nodeIndex].number = value.value.trim(); + if (this.showDiscarded) { + this.graphData.nodes[nodeIndex].isDiscarded = value.isDiscarded; + // 如果设置为废弃状态,同时废弃所有子节点 + if (value.isDiscarded) { + const discardChildren = (parentId) => { + const childEdges = this.graphData.edges.filter(e => e.source === parentId); + childEdges.forEach(edge => { + const childNode = this.graphData.nodes.find(n => n.id === edge.target); + if (childNode) { + const childIndex = this.graphData.nodes.findIndex(n => n.id === childNode.id); + if (childIndex > -1) { + this.graphData.nodes[childIndex].isDiscarded = true; + this.graphData.nodes[childIndex].style.fill = '#999'; + this.graphData.nodes[childIndex].style.opacity = 0.3; + // 递归处理子节点的子节点 + discardChildren(childNode.id); + } + } + }); + }; + discardChildren(nodeModel.id); + } + } + } + this.graph.changeData(this.graphData); + this.$message.success('修改成功'); + this.dialogVisible = false; + } + } + }, + setGenerationPlan() { + if (!this.selectedNode) { + this.$message.warning('请先选择节点'); + return; + } + + const nodeModel = this.selectedNode; + + if (nodeModel.label === '孙代') { + this.$message.warning('孙代节点不能再生成传代计划数'); + return; + } + + if (nodeModel.label === '传代计划数') { + this.$message.warning('传代计划数节点不能再设置计划数'); + return; + } + + const hasGenerationNode = this.graphData.edges.some(e => + e.source === nodeModel.id && + this.graphData.nodes.some(n => n.id === e.target && n.label === '传代计划数') + ); + + if (hasGenerationNode) { + this.$message.warning('该节点已经存在传代计划数节点'); + return; + } + if (nodeModel.label === '子代') { + this.$refs.addSublevelPlan.openInitData({ + ...nodeModel.data, + label: nodeModel.label, + strainName: this.form.strainName, + strainNo: this.form.strainNo, + strainSourceStart: this.form.strainSourceStart, + strainSourceEnd: this.form.strainSourceEnd, + }) + } else { + this.$refs.planForm.openInitData({ + ...nodeModel.data, + label: nodeModel.label, + strainName: this.form.strainName, + strainNo: this.form.strainNo, + strainSourceStart: this.form.strainSourceStart, + strainSourceEnd: this.form.strainSourceEnd, + }) + } + }, + handleAddPlan(value) { + const nodeModel = this.selectedNode; + const generationId = `generation-${++this.nodeCount}`; + + this.graphData.nodes.push({ + id: generationId, + label: '传代计划数', + planCount: value.count, + data: value, + isDiscarded: true, + currentCount: 0, + style: { + fill: '#00B5AA', + }, + }); + + this.graphData.edges.push({ + source: nodeModel.id, + target: generationId, + style: { + stroke: 'rgba(4, 156, 154, 1)', + lineWidth: 1, + }, + }); + + this.graph.changeData(this.graphData); + this.$message.success('传代计划数设置成功'); + this.$refs.planForm.closeDialog() + }, + showDetail() { + if (!this.selectedNode) { + this.$message.warning('请先选择节点'); + return; + } + + const nodeModel = this.selectedNode; + + if (nodeModel.label === '母代') { + this.$refs.parentForm.openInitData({ ...nodeModel.data, status: 'detail' }); + } else if (nodeModel.label === '子代' || nodeModel.label === '孙代') { + this.dialogTitle = `${nodeModel.label}详情`; + this.$refs.addSublevelForm.openInitData({ + title: `${nodeModel.label}详情`, + form: { ...nodeModel.data } + }) + } else if (nodeModel.label === '传代计划数') { + const nodeIndex = this.graphData.nodes.findIndex(n => n.id === nodeModel.id); + if (this.graphData.nodes[nodeIndex - 1].label === '子代') { + this.$refs.addSublevelPlan.openInitData({ ...nodeModel.data, status: 'detail' }); + } else { + this.$refs.planForm.openInitData({ ...nodeModel.data, status: 'detail' }); + } + } + } }, }; </script> @@ -135,26 +752,31 @@ display: flex; align-items: center; } + .chart { padding: 20px 38px; - background: rgba(255,255,255,0.8); - box-shadow: 0px 10px 19px 0px rgba(0,0,0,0.06); + background: rgba(255, 255, 255, 0.8); + box-shadow: 0px 10px 19px 0px rgba(0, 0, 0, 0.06); border-radius: 16px; border: 4px solid #FFFFFF; margin-top: 30px; + .header { display: flex; justify-content: space-between; align-items: center; + .title { - font-size: 18px; + font-size: 18px; } + .option-btn { - display: flex; - gap: 10px; - .el-button { - margin-left: 0; - } + display: flex; + gap: 10px; + + .el-button { + margin-left: 0; + } } } } @@ -253,4 +875,28 @@ flex-wrap: wrap; gap: 10px; } -</style> \ No newline at end of file + +.strain-flow-chart { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + + .toolbar { + margin-bottom: 16px; + display: flex; + gap: 12px; + + .el-button { + margin-right: 0; + } + } + + #mountNode { + flex: 1; + width: 100%; + min-height: 500px; + border-radius: 4px; + } +} +</style> \ No newline at end of file -- Gitblit v1.7.1