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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-client",
3
- "version": "1.20.30",
3
+ "version": "1.20.32",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
@@ -1,44 +1,54 @@
1
1
  <template>
2
2
  <div class="demo-x-markdown-section-extractor">
3
- <h2>Demo: XMarkdownSectionExtractor + bingli.md</h2>
3
+ <h2>Demo: XMarkdownSectionExtractor</h2>
4
4
 
5
5
  <XMarkdownSectionExtractor
6
6
  ref="extractor"
7
- :source="bingliMd"
8
7
  :showUploadButton="false"
9
- @extracted="onExtracted"
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
- import bingliMd from './bingli.md'
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
- onExtracted (sections) {
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('extracted sections:', sections)
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;
@@ -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', asComponent = false) {
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
- const sections = extractSectionsFromMarkdown(
204
- await readMarkdownAsText(this.currentSource),
205
- config
206
- )
207
- if (parseType === 'flat') {
208
- return flattenSectionsToItemsArray(sections)
209
- }
210
- const result = sections.map(sec => ({
211
- sectionTitle: sec.label || '',
212
- items: Array.isArray(sec.hierarchicalItems)
213
- ? sec.hierarchicalItems.map(h => ({
214
- title: h.title || '',
215
- content: Array.isArray(h.items) ? h.items : []
216
- }))
217
- : []
218
- }))
219
- if (asComponent) {
220
- this.renderedSections = [...this.renderedSections, ...result]
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 {
@@ -214,7 +214,7 @@ export default {
214
214
  <style lang="less" scoped>
215
215
  .x-markdown-viewer {
216
216
  width: 100%;
217
- max-height: 500px;
217
+ max-height: 499px;
218
218
  overflow-y: auto;
219
219
  overflow-x: hidden;
220
220
  }
@@ -1,9 +1,9 @@
1
1
  <template>
2
- <div
3
- ref="drawerRoot"
4
- class="drawer"
5
- :class="[{ 'drawer-collapsed': !isOpen }, wrapperClassObject()]"
6
- :style="drawerStyle">
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
- _syncDrawerMinHeightRafId: null,
109
+ syncDrawerMinHeightRafId: null,
110
110
  // 记录所在 a-row 有史以来出现过的最大高度,只增不减,展开/收起基准统一
111
- _maxRowHeight: null,
111
+ maxRowHeight: null,
112
112
  // 记录抽屉自身高度,用于按钮居中
113
- _drawerHeight: null
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._syncDrawerMinHeightRafId) {
132
- cancelAnimationFrame(this._syncDrawerMinHeightRafId)
133
- this._syncDrawerMinHeightRafId = null
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._syncDrawerMinHeightRafId) return
159
- this._syncDrawerMinHeightRafId = requestAnimationFrame(() => {
160
- this._syncDrawerMinHeightRafId = null
158
+ if (this.syncDrawerMinHeightRafId) return
159
+ this.syncDrawerMinHeightRafId = requestAnimationFrame(() => {
160
+ this.syncDrawerMinHeightRafId = null
161
161
  this.syncDrawerMinHeightToParentRow()
162
162
  })
163
163
  },
164
- // 同步读取行高并更新 _maxRowHeight,供 mounted 在渲染前调用
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._maxRowHeight === null || h > this._maxRowHeight)) {
173
- this._maxRowHeight = h
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._maxRowHeight === null || h > this._maxRowHeight) {
187
- this._maxRowHeight = h
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._drawerHeight = Math.round(h)
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._maxRowHeight === null || h > this._maxRowHeight) {
285
- this._maxRowHeight = h
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._maxRowHeight
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
- box-shadow 0.3s ease, border-radius 0.3s ease;
589
+ box-shadow 0.3s ease, border-radius 0.3s ease;
590
590
  border-radius: 10px;
591
591
  }
592
592
  /* 折叠时保持高度 */