vue2-client 1.20.30 → 1.20.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/base-client/components/common/XMarkdownSectionExtractor/DemoXMarkdownSectionExtractor.vue +27 -13
- package/src/base-client/components/common/XMarkdownSectionExtractor/XMarkdownSectionExtractor.vue +65 -20
- package/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer.vue +1 -1
- package/src/base-client/components/his/XSidebar/XSidebar.vue +25 -25
package/package.json
CHANGED
|
@@ -1,44 +1,54 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="demo-x-markdown-section-extractor">
|
|
3
|
-
<h2>Demo: XMarkdownSectionExtractor
|
|
3
|
+
<h2>Demo: XMarkdownSectionExtractor</h2>
|
|
4
4
|
|
|
5
5
|
<XMarkdownSectionExtractor
|
|
6
6
|
ref="extractor"
|
|
7
|
-
:source="bingliMd"
|
|
8
7
|
:showUploadButton="false"
|
|
9
|
-
@
|
|
8
|
+
@sections-rendered="onSectionsRendered"
|
|
10
9
|
@selection-change="onSelectionChange"
|
|
11
10
|
/>
|
|
12
11
|
|
|
13
12
|
<div class="demo-actions">
|
|
13
|
+
<button @click="loadAndExtract">加载内容并提取</button>
|
|
14
14
|
<button @click="getSelected">获取选中数据</button>
|
|
15
|
+
<button @click="clearSections">清空</button>
|
|
15
16
|
</div>
|
|
16
17
|
</div>
|
|
17
18
|
</template>
|
|
18
19
|
|
|
19
20
|
<script>
|
|
20
21
|
import XMarkdownSectionExtractor from './XMarkdownSectionExtractor.vue'
|
|
21
|
-
|
|
22
|
+
|
|
23
|
+
// 示例 markdown 内容(实际使用时可以从接口获取)
|
|
24
|
+
const sampleMarkdown = `### 还需提问
|
|
25
|
+
1. 患者近期是否有发热症状?
|
|
26
|
+
2. 是否有药物过敏史?
|
|
27
|
+
3. 既往病史情况如何?
|
|
28
|
+
|
|
29
|
+
### 下一步建议
|
|
30
|
+
1. 建议进行血常规检查
|
|
31
|
+
2. 建议完善胸部影像学检查
|
|
32
|
+
3. 根据检查结果调整治疗方案
|
|
33
|
+
`
|
|
22
34
|
|
|
23
35
|
export default {
|
|
24
36
|
name: 'DemoXMarkdownSectionExtractor',
|
|
25
37
|
components: { XMarkdownSectionExtractor },
|
|
26
38
|
data () {
|
|
27
39
|
return {
|
|
28
|
-
bingliMd,
|
|
29
40
|
selectedItems: []
|
|
30
41
|
}
|
|
31
42
|
},
|
|
32
|
-
mounted () {
|
|
33
|
-
const keywords = ['还需提问']
|
|
34
|
-
this.$refs.extractor.extractAs(keywords, 'title-content', true)
|
|
35
|
-
const keywords2 = ['下一步建议']
|
|
36
|
-
this.$refs.extractor.extractAs(keywords2, 'title-content', true)
|
|
37
|
-
},
|
|
38
43
|
methods: {
|
|
39
|
-
|
|
44
|
+
async loadAndExtract () {
|
|
45
|
+
// 方式1:先设置 source,再调用 extractAs
|
|
46
|
+
this.$refs.extractor.setSource(sampleMarkdown)
|
|
47
|
+
await this.$refs.extractor.extractAs(['还需提问', '下一步建议'])
|
|
48
|
+
},
|
|
49
|
+
onSectionsRendered (sections) {
|
|
40
50
|
// eslint-disable-next-line no-console
|
|
41
|
-
console.log('
|
|
51
|
+
console.log('sections rendered:', sections)
|
|
42
52
|
},
|
|
43
53
|
onSelectionChange (selected) {
|
|
44
54
|
this.selectedItems = selected
|
|
@@ -48,6 +58,9 @@ export default {
|
|
|
48
58
|
// eslint-disable-next-line no-console
|
|
49
59
|
console.log('selected items:', selected)
|
|
50
60
|
alert(`已选中 ${selected.length} 条数据,已输出到控制台`)
|
|
61
|
+
},
|
|
62
|
+
clearSections () {
|
|
63
|
+
this.$refs.extractor.clearRenderedSections()
|
|
51
64
|
}
|
|
52
65
|
}
|
|
53
66
|
}
|
|
@@ -65,6 +78,7 @@ export default {
|
|
|
65
78
|
border-radius: 4px;
|
|
66
79
|
cursor: pointer;
|
|
67
80
|
font-size: 14px;
|
|
81
|
+
margin-right: 8px;
|
|
68
82
|
|
|
69
83
|
&:hover {
|
|
70
84
|
background: #40a9ff;
|
package/src/base-client/components/common/XMarkdownSectionExtractor/XMarkdownSectionExtractor.vue
CHANGED
|
@@ -153,6 +153,18 @@ export default {
|
|
|
153
153
|
|
|
154
154
|
event.target.value = ''
|
|
155
155
|
},
|
|
156
|
+
/**
|
|
157
|
+
* 设置 markdown 内容(传入字符串)
|
|
158
|
+
* @param {string} markdownText - markdown 格式的字符串
|
|
159
|
+
*/
|
|
160
|
+
setSource (markdownText) {
|
|
161
|
+
if (typeof markdownText !== 'string') {
|
|
162
|
+
console.warn('[XMarkdownSectionExtractor] setSource requires a string')
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
this.currentSource = markdownText
|
|
166
|
+
this.$emit('source-changed', markdownText)
|
|
167
|
+
},
|
|
156
168
|
async extract () {
|
|
157
169
|
if (!this.currentSource) {
|
|
158
170
|
return
|
|
@@ -191,35 +203,53 @@ export default {
|
|
|
191
203
|
this.loading = false
|
|
192
204
|
}
|
|
193
205
|
},
|
|
194
|
-
async extractAs (keywords = [], parseType = 'title-content'
|
|
206
|
+
async extractAs (keywords = [], parseType = 'title-content') {
|
|
195
207
|
if (!Array.isArray(keywords) || keywords.length === 0) {
|
|
196
208
|
return []
|
|
197
209
|
}
|
|
210
|
+
if (!this.currentSource) {
|
|
211
|
+
console.warn('[XMarkdownSectionExtractor] currentSource is not set, call setSource() first')
|
|
212
|
+
return []
|
|
213
|
+
}
|
|
198
214
|
const config = keywords.map((label, idx) => ({
|
|
199
215
|
key: `section_${idx}`,
|
|
200
216
|
label,
|
|
201
217
|
headingLevel: 3
|
|
202
218
|
}))
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
try {
|
|
220
|
+
// 读取 markdown 内容(支持字符串、文件、URL 等)
|
|
221
|
+
const text = await readMarkdownAsText(this.currentSource)
|
|
222
|
+
if (!text) {
|
|
223
|
+
console.warn('[XMarkdownSectionExtractor] No text content from source')
|
|
224
|
+
return []
|
|
225
|
+
}
|
|
226
|
+
const sections = extractSectionsFromMarkdown(text, config)
|
|
227
|
+
if (parseType === 'flat') {
|
|
228
|
+
return flattenSectionsToItemsArray(sections)
|
|
229
|
+
}
|
|
230
|
+
const result = sections.map(sec => ({
|
|
231
|
+
sectionTitle: sec.label || '',
|
|
232
|
+
items: Array.isArray(sec.hierarchicalItems)
|
|
233
|
+
? sec.hierarchicalItems.map(h => ({
|
|
234
|
+
title: h.title || '',
|
|
235
|
+
content: Array.isArray(h.items) ? h.items : []
|
|
236
|
+
}))
|
|
237
|
+
: []
|
|
238
|
+
}))
|
|
239
|
+
// 创建新数组引用以确保 Vue 响应式更新
|
|
240
|
+
const newSections = [...this.renderedSections, ...result]
|
|
241
|
+
this.renderedSections = newSections
|
|
242
|
+
// 强制更新视图
|
|
243
|
+
this.$forceUpdate()
|
|
244
|
+
// 等待 DOM 更新后发送事件
|
|
245
|
+
this.$nextTick(() => {
|
|
246
|
+
this.$emit('sections-rendered', this.renderedSections)
|
|
247
|
+
})
|
|
248
|
+
return result
|
|
249
|
+
} catch (e) {
|
|
250
|
+
console.error('[XMarkdownSectionExtractor] extractAs error:', e)
|
|
251
|
+
return []
|
|
221
252
|
}
|
|
222
|
-
return result
|
|
223
253
|
},
|
|
224
254
|
toggleItem (sectionIdx, itemIdx, contentIdx) {
|
|
225
255
|
const key = `${sectionIdx}-${itemIdx}-${contentIdx}`
|
|
@@ -263,6 +293,17 @@ export default {
|
|
|
263
293
|
clearSelection () {
|
|
264
294
|
this.selectedItems = []
|
|
265
295
|
this.$emit('selection-change', [])
|
|
296
|
+
},
|
|
297
|
+
clearRenderedSections () {
|
|
298
|
+
this.renderedSections = []
|
|
299
|
+
this.selectedItems = []
|
|
300
|
+
this.$emit('sections-cleared')
|
|
301
|
+
},
|
|
302
|
+
forceRender () {
|
|
303
|
+
this.$forceUpdate()
|
|
304
|
+
this.$nextTick(() => {
|
|
305
|
+
this.$emit('sections-rendered', this.renderedSections)
|
|
306
|
+
})
|
|
266
307
|
}
|
|
267
308
|
}
|
|
268
309
|
}
|
|
@@ -271,6 +312,10 @@ export default {
|
|
|
271
312
|
<style lang="less" scoped>
|
|
272
313
|
.x-markdown-section-extractor {
|
|
273
314
|
display: block;
|
|
315
|
+
width: 100%;
|
|
316
|
+
max-height: 599px;
|
|
317
|
+
overflow-y: auto;
|
|
318
|
+
overflow-x: hidden;
|
|
274
319
|
}
|
|
275
320
|
|
|
276
321
|
.extractor-header {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<div
|
|
3
|
+
ref="drawerRoot"
|
|
4
|
+
class="drawer"
|
|
5
|
+
:class="[{ 'drawer-collapsed': !isOpen }, wrapperClassObject()]"
|
|
6
|
+
:style="drawerStyle">
|
|
7
7
|
<div
|
|
8
8
|
v-show="!isFrameConfig || showToggleInFrame"
|
|
9
9
|
class="drawer-toggle"
|
|
@@ -106,11 +106,11 @@ export default {
|
|
|
106
106
|
mainWithData: [{ max: 80, min: 50 }],
|
|
107
107
|
rowResizeObserver: null,
|
|
108
108
|
drawerResizeObserver: null,
|
|
109
|
-
|
|
109
|
+
syncDrawerMinHeightRafId: null,
|
|
110
110
|
// 记录所在 a-row 有史以来出现过的最大高度,只增不减,展开/收起基准统一
|
|
111
|
-
|
|
111
|
+
maxRowHeight: null,
|
|
112
112
|
// 记录抽屉自身高度,用于按钮居中
|
|
113
|
-
|
|
113
|
+
drawerHeight: null
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
116
|
mounted () {
|
|
@@ -128,9 +128,9 @@ export default {
|
|
|
128
128
|
})
|
|
129
129
|
},
|
|
130
130
|
beforeDestroy () {
|
|
131
|
-
if (this.
|
|
132
|
-
cancelAnimationFrame(this.
|
|
133
|
-
this.
|
|
131
|
+
if (this.syncDrawerMinHeightRafId) {
|
|
132
|
+
cancelAnimationFrame(this.syncDrawerMinHeightRafId)
|
|
133
|
+
this.syncDrawerMinHeightRafId = null
|
|
134
134
|
}
|
|
135
135
|
this.unbindRowResizeObserver()
|
|
136
136
|
this.unbindDrawerResizeObserver()
|
|
@@ -144,7 +144,7 @@ export default {
|
|
|
144
144
|
(!currentCol.className ||
|
|
145
145
|
!currentCol.className.includes ||
|
|
146
146
|
!currentCol.className.includes('ant-col'))
|
|
147
|
-
|
|
147
|
+
) {
|
|
148
148
|
currentCol = currentCol.parentNode
|
|
149
149
|
}
|
|
150
150
|
if (!currentCol || !currentCol.parentNode) return null
|
|
@@ -155,13 +155,13 @@ export default {
|
|
|
155
155
|
// 展开/收起共用同一套参照:用 CSS 变量记录行高,箭头始终相对于该值居中,
|
|
156
156
|
// 不走 CSS transition,彻底避免 min-height 动画化导致的"缓慢下移"问题
|
|
157
157
|
scheduleSyncDrawerMinHeight () {
|
|
158
|
-
if (this.
|
|
159
|
-
this.
|
|
160
|
-
this.
|
|
158
|
+
if (this.syncDrawerMinHeightRafId) return
|
|
159
|
+
this.syncDrawerMinHeightRafId = requestAnimationFrame(() => {
|
|
160
|
+
this.syncDrawerMinHeightRafId = null
|
|
161
161
|
this.syncDrawerMinHeightToParentRow()
|
|
162
162
|
})
|
|
163
163
|
},
|
|
164
|
-
// 同步读取行高并更新
|
|
164
|
+
// 同步读取行高并更新 maxRowHeight,供 mounted 在渲染前调用
|
|
165
165
|
computeMaxRowHeight () {
|
|
166
166
|
const root = this.$refs.drawerRoot
|
|
167
167
|
if (!root) return
|
|
@@ -169,8 +169,8 @@ export default {
|
|
|
169
169
|
if (!row) return
|
|
170
170
|
const raw = row.offsetHeight || row.getBoundingClientRect().height
|
|
171
171
|
const h = Math.round(raw)
|
|
172
|
-
if (h > 0 && (this.
|
|
173
|
-
this.
|
|
172
|
+
if (h > 0 && (this.maxRowHeight === null || h > this.maxRowHeight)) {
|
|
173
|
+
this.maxRowHeight = h
|
|
174
174
|
}
|
|
175
175
|
},
|
|
176
176
|
syncDrawerMinHeightToParentRow () {
|
|
@@ -183,8 +183,8 @@ export default {
|
|
|
183
183
|
const h = Math.round(raw)
|
|
184
184
|
if (h <= 0) return
|
|
185
185
|
// 只增不减:记录有史以来出现过的最大行高,作为收起后的定位基准
|
|
186
|
-
if (this.
|
|
187
|
-
this.
|
|
186
|
+
if (this.maxRowHeight === null || h > this.maxRowHeight) {
|
|
187
|
+
this.maxRowHeight = h
|
|
188
188
|
}
|
|
189
189
|
if (!this.rowResizeObserver && typeof ResizeObserver !== 'undefined') {
|
|
190
190
|
this.rowResizeObserver = new ResizeObserver(() => {
|
|
@@ -231,7 +231,7 @@ export default {
|
|
|
231
231
|
if (!root) return
|
|
232
232
|
const h = root.offsetHeight || root.getBoundingClientRect().height
|
|
233
233
|
if (h > 0) {
|
|
234
|
-
this.
|
|
234
|
+
this.drawerHeight = Math.round(h)
|
|
235
235
|
}
|
|
236
236
|
},
|
|
237
237
|
// 通用的样式保护方法
|
|
@@ -281,8 +281,8 @@ export default {
|
|
|
281
281
|
if (row) {
|
|
282
282
|
const h = Math.round(row.offsetHeight || row.getBoundingClientRect().height)
|
|
283
283
|
if (h > 0) {
|
|
284
|
-
if (this.
|
|
285
|
-
this.
|
|
284
|
+
if (this.maxRowHeight === null || h > this.maxRowHeight) {
|
|
285
|
+
this.maxRowHeight = h
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
}
|
|
@@ -568,7 +568,7 @@ export default {
|
|
|
568
568
|
const isPx = this.widthMode === 'px'
|
|
569
569
|
const width = isPx ? `${this.collapsedWidth}px` : `${this.collapsedWidthPercent}%`
|
|
570
570
|
// 优先用外部传入的 rowHeight,否则用内部计算的行高
|
|
571
|
-
const rowH = this.rowHeight || this.
|
|
571
|
+
const rowH = this.rowHeight || this.maxRowHeight
|
|
572
572
|
const top = rowH ? `${rowH / 2 - 24}px` : '50%'
|
|
573
573
|
return { width, top }
|
|
574
574
|
}
|
|
@@ -586,7 +586,7 @@ export default {
|
|
|
586
586
|
border-left: solid rgba(240, 242, 245) 2px;
|
|
587
587
|
/* 明确列出需要过渡的属性,min-height 和 height 永远不走过渡,避免箭头"慢慢下移" */
|
|
588
588
|
transition: width 0.3s ease, background-color 0.3s ease, border-color 0.3s ease,
|
|
589
|
-
|
|
589
|
+
box-shadow 0.3s ease, border-radius 0.3s ease;
|
|
590
590
|
border-radius: 10px;
|
|
591
591
|
}
|
|
592
592
|
/* 折叠时保持高度 */
|