diff --git a/RuoYi-Vue/easycode-web/src/views/DiagramCenterView.vue b/RuoYi-Vue/easycode-web/src/views/DiagramCenterView.vue
index 6ea6dd6..79ac249 100644
--- a/RuoYi-Vue/easycode-web/src/views/DiagramCenterView.vue
+++ b/RuoYi-Vue/easycode-web/src/views/DiagramCenterView.vue
@@ -15,12 +15,13 @@
+
+
+
+
+
+
+
+
+ {{ warning }}
+
+
+
+
+
+
+
+
+
route.params.projectId)
+const DIAGRAM_QUERY_ALIASES = {
+ er: 'er',
+ module: 'module',
+ 'module-diagram': 'module',
+ architecture: 'architecture',
+ flowchart: 'flowchart',
+ use_case: 'use_case',
+ usecase: 'use_case',
+ sequence: 'sequence',
+ three_line_table: 'three_line_table',
+ ai: 'ai'
+}
+
+function normalizeInitialDiagram(value) {
+ const key = Array.isArray(value) ? value[0] : value
+ return DIAGRAM_QUERY_ALIASES[String(key || '')] || 'three_line_table'
+}
+
const loading = ref(false)
const savingDiagram = ref(false)
const generatingAiDiagram = ref(false)
-const activeDiagram = ref('er')
+const activeDiagram = ref(normalizeInitialDiagram(route.query.diagram))
const project = reactive({})
const database = ref({ tables: [], erDiagram: {} })
const erDraft = ref({ positions: {}, relations: [], deletedEdgeIds: [] })
@@ -506,6 +816,8 @@ const architectureSvgRef = ref(null)
const architectureDsl = ref('')
const architectureBlackWhite = ref(false)
const flowchartDsl = ref('')
+const useCaseDsl = ref('')
+const sequenceDsl = ref('')
const savedDiagrams = ref([])
const moduleDiagramSourceMode = ref('blueprint')
const moduleDiagramOutlineText = ref('')
@@ -543,14 +855,27 @@ const flowchartStatsText = computed(() => {
return `${stats.nodeCount} 节点 · ${stats.edgeCount} 连线`
})
const flowchartLabeledEdges = computed(() => flowchartDiagram.value.edges.filter((edge) => edge.label))
+const parsedUseCase = computed(() => parseUseCaseDsl(useCaseDsl.value))
+const useCaseDiagram = computed(() => buildUseCaseDiagram(parsedUseCase.value, {
+ title: `${project.projectName || '项目'}用例图`
+}))
+const useCaseStatsText = computed(() => {
+ const stats = useCaseDiagram.value.stats
+ return `${stats.actorCount} 参与者 · ${stats.useCaseCount} 用例`
+})
+const parsedSequence = computed(() => parseSequenceDsl(sequenceDsl.value))
+const sequenceDiagram = computed(() => buildSequenceDiagram(parsedSequence.value, {
+ title: `${project.projectName || '项目'}时序图`
+}))
+const sequenceStatsText = computed(() => {
+ const stats = sequenceDiagram.value.stats
+ return `${stats.participantCount} 参与者 · ${stats.messageCount} 消息`
+})
const currentGraph = computed(() => {
if (activeDiagram.value === 'ai') {
return normalizeGraph(aiDiagram.value, 'AI 图表')
}
- if (activeDiagram.value === 'overview') {
- return buildOverviewGraph()
- }
return buildArchitectureGraph()
})
@@ -618,6 +943,31 @@ function flowchartTextY(node, lineIndex) {
return node.y + node.height / 2 - ((lines.length - 1) * 8) + lineIndex * 17
}
+function sequenceFrameTagPath(frame) {
+ const width = frame.type === 'loop' ? 46 : 42
+ return `M ${frame.x} ${frame.y} H ${frame.x + width} L ${frame.x + width - 12} ${frame.y + 24} H ${frame.x} Z`
+}
+
+function sequenceFrameLabel(frame) {
+ return `[${frame.label || (frame.type === 'loop' ? '循环' : '条件分支')}]`
+}
+
+function sequenceActorPath(participant) {
+ const x = participant.x
+ return `M ${x} 87 L ${x} 113 M ${x - 16} 96 L ${x + 16} 96 M ${x} 113 L ${x - 15} 132 M ${x} 113 L ${x + 15} 132`
+}
+
+function useCaseRelationPath(relation) {
+ return relation.path || ''
+}
+
+function useCaseActorPath(actor) {
+ const cx = actor.x + actor.width / 2
+ const headBottom = actor.y + 23
+ const bodyBottom = actor.y + 56
+ return `M ${cx} ${headBottom} L ${cx} ${bodyBottom} M ${cx - 24} ${actor.y + 36} L ${cx + 24} ${actor.y + 36} M ${cx} ${bodyBottom} L ${cx - 22} ${actor.y + 82} M ${cx} ${bodyBottom} L ${cx + 22} ${actor.y + 82}`
+}
+
function normalizeGraph(source, fallbackTitle) {
const nodes = Array.isArray(source?.nodes) ? source.nodes : []
const positionedNodes = nodes.map((node, index) => ({
@@ -669,33 +1019,6 @@ function buildArchitectureGraph() {
}, '系统架构图')
}
-function buildOverviewGraph() {
- const tableNodes = database.value.tables.slice(0, 8).map((table) => ({
- id: `table_${table.tableName}`,
- label: table.tableComment || table.tableName,
- lane: table.tableName
- }))
- return normalizeGraph({
- title: '项目总览图',
- description: '汇总项目、模块、数据库和预览生成链路。',
- nodes: [
- { id: 'project', label: project.projectName || '项目草稿', lane: '需求' },
- { id: 'blueprint', label: '系统模块设计', lane: '蓝图' },
- { id: 'loop', label: '业务闭环设计', lane: '流程' },
- { id: 'database', label: '数据库设计', lane: `${database.value.tables.length || 0} 张表` },
- { id: 'preview', label: '源码预览/下载', lane: '交付' },
- ...tableNodes
- ],
- edges: [
- { source: 'project', target: 'blueprint', label: '规划' },
- { source: 'blueprint', target: 'loop', label: '闭环' },
- { source: 'loop', target: 'database', label: '落表' },
- { source: 'database', target: 'preview', label: '生成' },
- ...tableNodes.map((node) => ({ source: 'database', target: node.id, label: '包含' }))
- ]
- }, '项目总览图')
-}
-
async function loadData() {
if (!projectId.value) return
loading.value = true
@@ -707,7 +1030,9 @@ async function loadData() {
])
Object.assign(project, projectResult || {})
database.value = normalizeDatabase(databaseResult)
- savedDiagrams.value = Array.isArray(diagramsResult) ? diagramsResult : []
+ savedDiagrams.value = Array.isArray(diagramsResult)
+ ? diagramsResult.filter((diagram) => diagram.diagramType !== 'overview')
+ : []
applySavedDrafts()
aiForm.title = `${project.projectName || '项目'}业务流程图`
} catch (error) {
@@ -782,7 +1107,19 @@ function applySavedDrafts() {
} else {
flowchartDsl.value = buildDefaultFlowchartDsl(project)
}
- const savedAi = savedDiagrams.value.find((item) => isAiDiagramType(item.diagramType))
+ const savedUseCase = savedDiagrams.value.find((item) => item.diagramType === 'use_case')
+ if (savedUseCase) {
+ applySavedUseCaseDiagram(savedUseCase)
+ } else {
+ useCaseDsl.value = buildDefaultUseCaseDsl(project)
+ }
+ const savedSequence = savedDiagrams.value.find((item) => item.diagramType === 'sequence')
+ if (savedSequence) {
+ applySavedSequenceDiagram(savedSequence)
+ } else {
+ sequenceDsl.value = buildDefaultSequenceDsl(project)
+ }
+ const savedAi = savedDiagrams.value.find((item) => item.diagramType !== 'use_case' && isAiDiagramType(item.diagramType))
if (savedAi) {
applySavedAiDiagram(savedAi)
}
@@ -807,6 +1144,16 @@ function loadSavedDiagram(diagram) {
applySavedFlowchartDiagram(diagram)
return
}
+ if (diagram.diagramType === 'use_case') {
+ activeDiagram.value = 'use_case'
+ applySavedUseCaseDiagram(diagram)
+ return
+ }
+ if (diagram.diagramType === 'sequence') {
+ activeDiagram.value = 'sequence'
+ applySavedSequenceDiagram(diagram)
+ return
+ }
if (diagram.diagramType === 'module') {
activeDiagram.value = 'module'
applySavedModuleDiagram(diagram)
@@ -844,6 +1191,20 @@ function applySavedFlowchartDiagram(diagram) {
: buildDefaultFlowchartDsl(project)
}
+function applySavedUseCaseDiagram(diagram) {
+ const payload = parseJson(diagram.diagramJson, {})
+ useCaseDsl.value = typeof payload.dsl === 'string'
+ ? payload.dsl
+ : buildDefaultUseCaseDsl(project)
+}
+
+function applySavedSequenceDiagram(diagram) {
+ const payload = parseJson(diagram.diagramJson, {})
+ sequenceDsl.value = typeof payload.dsl === 'string'
+ ? payload.dsl
+ : buildDefaultSequenceDsl(project)
+}
+
function applySavedAiDiagram(diagram) {
const payload = parseJson(diagram.diagramJson, {})
aiDiagram.value = {
@@ -960,6 +1321,32 @@ function currentDiagramPayload() {
})
}
}
+ if (activeDiagram.value === 'use_case') {
+ const existing = savedDiagrams.value.find((item) => item.diagramType === 'use_case')
+ return {
+ diagramId: existing?.diagramId,
+ diagramType: 'use_case',
+ title: '用例图',
+ description: 'DSL 驱动的 UML 用例图草稿',
+ diagramJson: JSON.stringify({
+ dsl: useCaseDsl.value,
+ graph: useCaseDiagram.value
+ })
+ }
+ }
+ if (activeDiagram.value === 'sequence') {
+ const existing = savedDiagrams.value.find((item) => item.diagramType === 'sequence')
+ return {
+ diagramId: existing?.diagramId,
+ diagramType: 'sequence',
+ title: '时序图',
+ description: 'DSL 驱动的系统交互时序图草稿',
+ diagramJson: JSON.stringify({
+ dsl: sequenceDsl.value,
+ graph: sequenceDiagram.value
+ })
+ }
+ }
if (activeDiagram.value === 'three_line_table') {
const existing = savedDiagrams.value.find((item) => item.diagramType === 'three_line_table')
return {
@@ -1063,6 +1450,54 @@ function refreshFlowchartPreview() {
ElMessage.success('预览已更新')
}
+function clearUseCaseDsl() {
+ useCaseDsl.value = ''
+}
+
+function loadUseCaseExample() {
+ useCaseDsl.value = USE_CASE_EXAMPLE_DSL
+ ElMessage.success('示例 DSL 已加载')
+}
+
+function regenerateUseCaseDsl() {
+ useCaseDsl.value = buildDefaultUseCaseDsl(project)
+ ElMessage.success('已根据当前项目重新生成用例图 DSL')
+}
+
+function refreshUseCasePreview() {
+ if (!useCaseDiagram.value.useCases.length) {
+ ElMessage.warning('请先在 DSL 中添加用例')
+ return
+ }
+ ElMessage.success('预览已更新')
+}
+
+function clearSequenceDsl() {
+ sequenceDsl.value = ''
+}
+
+function loadSequenceExample() {
+ sequenceDsl.value = SEQUENCE_EXAMPLE_DSL
+ ElMessage.success('示例 DSL 已加载')
+}
+
+function regenerateSequenceDsl() {
+ sequenceDsl.value = buildDefaultSequenceDsl(project)
+ ElMessage.success('已根据当前项目重新生成时序图 DSL')
+}
+
+function refreshSequencePreview() {
+ if (!sequenceDiagram.value.messages.length) {
+ ElMessage.warning('请先在 DSL 中添加时序消息')
+ return
+ }
+ if (parsedSequence.value.errors.length) {
+ ElMessage.warning('DSL 还有格式提示,请修正后再使用')
+ return
+ }
+ ElMessage.success('预览已更新')
+}
+
function graphSvgText() {
const svg = graphSvgRef.value
if (!svg) return ''
@@ -1091,6 +1526,26 @@ function downloadFlowchartSvg() {
saveBlob(new Blob([text], { type: 'image/svg+xml;charset=utf-8' }), `${flowchartDiagram.value.title || '流程图'}.svg`)
}
+function useCaseSvgText() {
+ return buildUseCaseExportSvg(useCaseDiagram.value)
+}
+
+function downloadUseCaseSvg() {
+ const text = useCaseSvgText()
+ if (!text) return
+ saveBlob(new Blob([text], { type: 'image/svg+xml;charset=utf-8' }), `${useCaseDiagram.value.title || '用例图'}.svg`)
+}
+
+function sequenceSvgText() {
+ return buildSequenceExportSvg(sequenceDiagram.value)
+}
+
+function downloadSequenceSvg() {
+ const text = sequenceSvgText()
+ if (!text) return
+ saveBlob(new Blob([text], { type: 'image/svg+xml;charset=utf-8' }), `${sequenceDiagram.value.title || '时序图'}.svg`)
+}
+
async function downloadArchitecturePng() {
const text = architectureSvgText()
if (!text) return
@@ -1149,6 +1604,64 @@ async function downloadFlowchartPng() {
}
}
+async function downloadUseCasePng() {
+ const text = useCaseSvgText()
+ if (!text) return
+ const blob = new Blob([text], { type: 'image/svg+xml;charset=utf-8' })
+ const url = URL.createObjectURL(blob)
+ const image = new Image()
+ image.decoding = 'async'
+ try {
+ await new Promise((resolve, reject) => {
+ image.onload = resolve
+ image.onerror = reject
+ image.src = url
+ })
+ const canvas = document.createElement('canvas')
+ canvas.width = useCaseDiagram.value.canvas.width
+ canvas.height = useCaseDiagram.value.canvas.height
+ const context = canvas.getContext('2d')
+ context.fillStyle = '#ffffff'
+ context.fillRect(0, 0, canvas.width, canvas.height)
+ context.drawImage(image, 0, 0)
+ const pngBlob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'))
+ if (pngBlob) {
+ saveBlob(pngBlob, `${useCaseDiagram.value.title || '用例图'}.png`)
+ }
+ } finally {
+ URL.revokeObjectURL(url)
+ }
+}
+
+async function downloadSequencePng() {
+ const text = sequenceSvgText()
+ if (!text) return
+ const blob = new Blob([text], { type: 'image/svg+xml;charset=utf-8' })
+ const url = URL.createObjectURL(blob)
+ const image = new Image()
+ image.decoding = 'async'
+ try {
+ await new Promise((resolve, reject) => {
+ image.onload = resolve
+ image.onerror = reject
+ image.src = url
+ })
+ const canvas = document.createElement('canvas')
+ canvas.width = sequenceDiagram.value.canvas.width
+ canvas.height = sequenceDiagram.value.canvas.height
+ const context = canvas.getContext('2d')
+ context.fillStyle = '#ffffff'
+ context.fillRect(0, 0, canvas.width, canvas.height)
+ context.drawImage(image, 0, 0)
+ const pngBlob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'))
+ if (pngBlob) {
+ saveBlob(pngBlob, `${sequenceDiagram.value.title || '时序图'}.png`)
+ }
+ } finally {
+ URL.revokeObjectURL(url)
+ }
+}
+
function downloadCurrentSvg() {
const text = graphSvgText()
if (!text) return
@@ -1191,6 +1704,8 @@ onMounted(loadData)
.diagram-center-page {
display: grid;
gap: 18px;
+ width: calc(100% - 40px);
+ max-width: none;
}
.diagram-layout {
@@ -1658,6 +2173,435 @@ onMounted(loadData)
dominant-baseline: middle;
}
+.use-case-editor {
+ display: grid;
+ min-height: 660px;
+ background: #ffffff;
+}
+
+.use-case-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ min-height: 68px;
+ padding: 14px 18px;
+ border-bottom: 1px solid #e7ecf3;
+
+ strong,
+ span {
+ display: block;
+ }
+
+ strong {
+ color: #172033;
+ font-size: 16px;
+ }
+
+ span {
+ margin-top: 4px;
+ color: #667085;
+ font-size: 13px;
+ }
+}
+
+.use-case-workbench {
+ display: grid;
+ grid-template-columns: minmax(340px, 38%) minmax(0, 1fr);
+ min-height: 592px;
+}
+
+.use-case-dsl-panel {
+ display: flex;
+ min-width: 0;
+ flex-direction: column;
+ gap: 12px;
+ padding: 14px;
+ border-right: 1px solid #e7ecf3;
+ background: #fbfdff;
+}
+
+.use-case-dsl-header {
+ display: grid;
+ gap: 10px;
+
+ strong,
+ span {
+ display: block;
+ }
+
+ strong {
+ color: #172033;
+ font-size: 15px;
+ }
+
+ span {
+ margin-top: 4px;
+ color: #667085;
+ font-size: 12px;
+ line-height: 1.5;
+ }
+}
+
+.use-case-dsl-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+
+ :deep(.el-button) {
+ margin-left: 0;
+ }
+}
+
+.use-case-grammar {
+ padding: 9px 10px;
+ border: 1px solid #dbeafe;
+ border-radius: 6px;
+ background: #eff6ff;
+ color: #475569;
+ font-size: 12px;
+ line-height: 1.6;
+}
+
+.use-case-dsl-input {
+ flex: 1;
+
+ :deep(.el-textarea__inner) {
+ min-height: 460px !important;
+ border-color: #d9e0ea;
+ border-radius: 6px;
+ color: #172033;
+ font-family: Consolas, "Courier New", "Microsoft YaHei", monospace;
+ font-size: 14px;
+ line-height: 1.65;
+ }
+}
+
+.use-case-dsl-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+
+ span {
+ color: #667085;
+ font-size: 12px;
+ }
+}
+
+.use-case-preview-panel {
+ display: grid;
+ min-width: 0;
+ align-content: start;
+ background: #f8fafc;
+}
+
+.use-case-warnings {
+ display: grid;
+ gap: 4px;
+ padding: 10px 18px;
+ border-bottom: 1px solid #fde68a;
+ background: #fffbeb;
+ color: #92400e;
+ font-size: 12px;
+}
+
+.use-case-preview-scroll {
+ min-height: 592px;
+ overflow: auto;
+ padding: 24px;
+}
+
+.use-case-svg {
+ display: block;
+ background: #ffffff;
+ border: 1px solid #d9e0ea;
+ border-radius: 6px;
+ font-family: "Microsoft YaHei", Arial, sans-serif;
+}
+
+.use-case-svg-title {
+ fill: #111111;
+ font-size: 18px;
+ font-weight: 700;
+ text-anchor: middle;
+}
+
+.use-case-relation {
+ fill: none;
+ stroke: #222222;
+ stroke-width: 1.3;
+
+ &.is-dependency {
+ stroke-dasharray: 5 4;
+ }
+}
+
+.use-case-relation-label {
+ fill: #111111;
+ font-size: 11px;
+ paint-order: stroke;
+ stroke: #ffffff;
+ stroke-width: 4px;
+ text-anchor: middle;
+}
+
+.use-case-actor circle,
+.use-case-actor path,
+.use-case-node ellipse {
+ fill: #ffffff;
+ stroke: #222222;
+ stroke-width: 1.4;
+}
+
+.use-case-actor path {
+ fill: none;
+}
+
+.use-case-actor text,
+.use-case-node text {
+ fill: #111111;
+ font-size: 13px;
+ text-anchor: middle;
+ dominant-baseline: middle;
+}
+
+.sequence-editor {
+ display: grid;
+ min-height: 680px;
+ background: #ffffff;
+}
+
+.sequence-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ min-height: 68px;
+ padding: 14px 18px;
+ border-bottom: 1px solid #e7ecf3;
+
+ strong,
+ span {
+ display: block;
+ }
+
+ strong {
+ color: #172033;
+ font-size: 16px;
+ }
+
+ span {
+ margin-top: 4px;
+ color: #667085;
+ font-size: 13px;
+ }
+}
+
+.sequence-workbench {
+ display: grid;
+ grid-template-columns: minmax(340px, 38%) minmax(0, 1fr);
+ min-height: 612px;
+}
+
+.sequence-dsl-panel {
+ display: flex;
+ min-width: 0;
+ flex-direction: column;
+ gap: 12px;
+ padding: 14px;
+ border-right: 1px solid #e7ecf3;
+ background: #fbfdff;
+}
+
+.sequence-dsl-header {
+ display: grid;
+ gap: 10px;
+
+ strong,
+ span {
+ display: block;
+ }
+
+ strong {
+ color: #172033;
+ font-size: 15px;
+ }
+
+ span {
+ margin-top: 4px;
+ color: #667085;
+ font-size: 12px;
+ line-height: 1.5;
+ }
+}
+
+.sequence-dsl-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+
+ :deep(.el-button) {
+ margin-left: 0;
+ }
+}
+
+.sequence-grammar {
+ padding: 9px 10px;
+ border: 1px solid #dbeafe;
+ border-radius: 6px;
+ background: #eff6ff;
+ color: #475569;
+ font-size: 12px;
+ line-height: 1.6;
+}
+
+.sequence-dsl-input {
+ flex: 1;
+
+ :deep(.el-textarea__inner) {
+ min-height: 500px !important;
+ border-color: #d9e0ea;
+ border-radius: 6px;
+ color: #172033;
+ font-family: Consolas, "Courier New", "Microsoft YaHei", monospace;
+ font-size: 14px;
+ line-height: 1.65;
+ }
+}
+
+.sequence-dsl-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+
+ span {
+ color: #667085;
+ font-size: 12px;
+ }
+}
+
+.sequence-preview-panel {
+ display: grid;
+ min-width: 0;
+ align-content: start;
+ background: #f8fafc;
+}
+
+.sequence-errors {
+ display: grid;
+ gap: 4px;
+ padding: 10px 18px;
+ border-bottom: 1px solid #fee2e2;
+ background: #fff7f7;
+ color: #b42318;
+ font-size: 12px;
+}
+
+.sequence-preview-scroll {
+ min-height: 612px;
+ overflow: auto;
+ padding: 24px;
+}
+
+.sequence-svg {
+ display: block;
+ background: #ffffff;
+ border: 1px solid #d9e0ea;
+ border-radius: 6px;
+ font-family: "Microsoft YaHei", Arial, sans-serif;
+}
+
+.sequence-svg-title {
+ fill: #0f172a;
+ font-size: 18px;
+ font-weight: 700;
+ text-anchor: middle;
+}
+
+.sequence-lifeline {
+ fill: none;
+ stroke: #94a3b8;
+ stroke-dasharray: 7 6;
+ stroke-width: 1.1;
+}
+
+.sequence-frame-box {
+ fill: #f8fbff;
+ fill-opacity: 0.72;
+ stroke: #64748b;
+ stroke-width: 1.2;
+}
+
+.sequence-frame-tag {
+ fill: #e8f0fb;
+ stroke: #64748b;
+ stroke-width: 1.2;
+}
+
+.sequence-frame-kind {
+ fill: #1f2937;
+ font-size: 12px;
+ font-weight: 700;
+}
+
+.sequence-frame-note {
+ fill: #475569;
+ font-size: 11px;
+}
+
+.sequence-frame-else-line {
+ fill: none;
+ stroke: #94a3b8;
+ stroke-dasharray: 6 5;
+}
+
+.sequence-activation {
+ fill: #ffffff;
+ stroke: #1f2937;
+ stroke-width: 1.2;
+}
+
+.sequence-message-path {
+ fill: none;
+ stroke: #1f2937;
+ stroke-width: 1.5;
+
+ &.is-return {
+ stroke-dasharray: 7 5;
+ }
+}
+
+.sequence-message-text {
+ fill: #1f2937;
+ font-size: 13px;
+ paint-order: stroke;
+ stroke: #ffffff;
+ stroke-width: 4px;
+ text-anchor: middle;
+}
+
+.sequence-actor-head,
+.sequence-object-box {
+ fill: #ffffff;
+ stroke: #1f2937;
+ stroke-width: 1.4;
+}
+
+.sequence-actor-body {
+ fill: none;
+ stroke: #1f2937;
+ stroke-linecap: round;
+ stroke-width: 1.5;
+}
+
+.sequence-participant-label {
+ fill: #1f2937;
+ font-size: 13px;
+ text-anchor: middle;
+}
+
.custom-diagram-panel {
display: grid;
gap: 0;
@@ -1759,20 +2703,32 @@ onMounted(loadData)
}
.architecture-workbench,
- .flowchart-workbench {
+ .flowchart-workbench,
+ .use-case-workbench,
+ .sequence-workbench {
grid-template-columns: 1fr;
}
.architecture-dsl-panel,
- .flowchart-dsl-panel {
+ .flowchart-dsl-panel,
+ .use-case-dsl-panel,
+ .sequence-dsl-panel {
border-right: 0;
border-bottom: 1px solid #e7ecf3;
}
.architecture-toolbar,
- .flowchart-toolbar {
+ .flowchart-toolbar,
+ .use-case-toolbar,
+ .sequence-toolbar {
align-items: flex-start;
flex-direction: column;
}
}
+
+@media (max-width: 720px) {
+ .diagram-center-page {
+ width: calc(100% - 24px);
+ }
+}
diff --git a/RuoYi-Vue/easycode-web/src/views/diagramCenterView.test.mjs b/RuoYi-Vue/easycode-web/src/views/diagramCenterView.test.mjs
index 335e1cd..ee10462 100644
--- a/RuoYi-Vue/easycode-web/src/views/diagramCenterView.test.mjs
+++ b/RuoYi-Vue/easycode-web/src/views/diagramCenterView.test.mjs
@@ -60,7 +60,7 @@ test('diagram center promotes the three-line table tab and removes overview', ()
const menu = source.slice(menuStart, menuEnd)
const labels = [...menu.matchAll(/ match[1])
- assert.deepEqual(labels, ['three_line_table', 'er', 'module', 'architecture', 'flowchart', 'sequence', 'ai'])
+ assert.deepEqual(labels, ['three_line_table', 'er', 'module', 'architecture', 'flowchart', 'use_case', 'sequence', 'ai'])
assert.doesNotMatch(source, /overview:\s*'overview'/)
assert.doesNotMatch(source, /activeDiagram\.value === 'overview'/)
assert.doesNotMatch(source, /function buildOverviewGraph/)