vue2-client 1.9.80 → 1.9.82

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.
@@ -31,6 +31,7 @@ this.openDialog('xxx', 5, {}, {}, {})
31
31
  - refreshTable 重新查询
32
32
  - clearRowKeys 清除选中的行
33
33
  - getTableData() 获取全部数据
34
+ - setTableData(data) 设置表格内数据
34
35
  - Object.assign(this.fixedQueryForm, data) 给查询条件传值
35
36
  - 事件相关
36
37
  - afterSubmit( type(新增/修改/擦和讯), id: (所操作的主键id), form: (请求的表单内容)) 提交后触发
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-client",
3
- "version": "1.9.80",
3
+ "version": "1.9.82",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div>
3
- <button @click="startRecording" :disabled="isRecording">开始录音</button>
4
- <button @click="stopRecording" :disabled="!isRecording">停止录音</button>
3
+ <a-button type="primary" @click="startRecording" :disabled="isRecording">开始录音</a-button>
4
+ <a-button type="primary" @click="stopRecording" :disabled="!isRecording">停止录音</a-button>
5
5
  <!-- <audio v-if="audioUrl" :src="audioUrl" controls></audio>-->
6
6
  </div>
7
7
  </template>
@@ -20,8 +20,7 @@ export default {
20
20
  config: {}
21
21
  }
22
22
  },
23
- props: {
24
- },
23
+ props: {},
25
24
  watch: {},
26
25
  components: {},
27
26
  created () {
@@ -31,29 +30,30 @@ export default {
31
30
  },
32
31
  methods: {
33
32
  async startRecording () {
34
- this.inputData = ''
35
- const url = this.config.webSocketURL // WebSocket地址
36
- this.setupWebSocket(url)
33
+ this.inputData = ''
34
+ this.audioChunks = []
35
+ const url = this.config.webSocketURL ? this.config.webSocketURL : 'ws://192.168.50.67:31090/websocket/bash' // WebSocket地址
36
+ this.setupWebSocket(url)
37
37
 
38
- // 等待 WebSocket 连接成功
39
- await this.waitForWebSocketConnection()
38
+ // 等待 WebSocket 连接成功
39
+ await this.waitForWebSocketConnection()
40
40
 
41
- this.isRecording = true
41
+ this.isRecording = true
42
42
 
43
- // 初始化 AudioContext
44
- this.audioContext = new (window.AudioContext || window.webkitAudioContext)()
43
+ // 初始化 AudioContext
44
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)()
45
45
 
46
- if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
47
- throw new Error(
48
- '录音软件不支持您的浏览器!请使用现代浏览器或确保您正在使用 HTTPS。'
49
- )
50
- }
46
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
47
+ throw new Error(
48
+ '录音软件不支持您的浏览器!请使用现代浏览器或确保您正在使用 HTTPS。'
49
+ )
50
+ }
51
51
 
52
- // 获取麦克风输入流
53
- this.audioStream = await navigator.mediaDevices.getUserMedia({ audio: true })
52
+ // 获取麦克风输入流
53
+ this.audioStream = await navigator.mediaDevices.getUserMedia({ audio: true })
54
54
 
55
- // 设置音频处理流程
56
- this.setupAudioProcessing()
55
+ // 设置音频处理流程
56
+ this.setupAudioProcessing()
57
57
  },
58
58
  setupWebSocket (url) {
59
59
  this.ws = new WebSocket(url)
@@ -64,6 +64,10 @@ export default {
64
64
  if (data.result !== undefined) {
65
65
  this.inputData += data.result
66
66
  }
67
+ if (!this.isRecording) {
68
+ this.ws.close()
69
+ this.resRecordingData()
70
+ }
67
71
  }
68
72
 
69
73
  this.ws.onclose = () => console.log('WebSocket closed.')
@@ -93,9 +97,13 @@ export default {
93
97
 
94
98
  const inputData = e.inputBuffer.getChannelData(0)
95
99
 
100
+ // 收集音频数据块
101
+
96
102
  // Resample to 16000 Hz if needed
97
103
  const resampledData = this.resample(inputData, this.audioContext.sampleRate, 16000)
98
104
 
105
+ this.audioChunks.push(new Float32Array(resampledData))
106
+
99
107
  // Convert to 16-bit PCM and send via WebSocket
100
108
  const byteArray = this.convertTo16BitPCM(resampledData)
101
109
  this.ws.send(byteArray)
@@ -108,24 +116,97 @@ export default {
108
116
  throw error
109
117
  }
110
118
  },
119
+
111
120
  stopRecording () {
112
121
  this.isRecording = false
113
- if (this.ws) {
114
- // 可以添加一个事件处理器来处理WebSocket关闭
115
- this.ws.onclose = () => {
116
- console.log('WebSocket closed.')
117
- // 这里可以添加WebSocket关闭后的清理工作
118
- }
119
- // 关闭WebSocket连接
120
- this.ws.close()
121
- }
122
+
123
+ // 停止麦克风流
122
124
  if (this.audioStream) {
123
- this.audioStream.getTracks().forEach(track => track.stop())
125
+ this.audioStream.getTracks().forEach((track) => track.stop())
124
126
  }
127
+
128
+ // 合并音频数据块并保存为文件
129
+ // this.saveAudioFile()
130
+
131
+ // 关闭 WebSocket
132
+ // if (this.ws) {
133
+ // this.ws.close()
134
+ // }
135
+
125
136
  if (this.audioContext) {
126
137
  this.audioContext.close()
127
138
  }
128
139
  },
140
+
141
+ saveAudioFile () {
142
+ // 合并所有音频块
143
+ const audioBuffer = this.mergeAudioChunks(this.audioChunks)
144
+
145
+ // 转换为 WAV 格式
146
+ const wavBlob = this.encodeWAV(audioBuffer, 16000)
147
+
148
+ // 创建下载链接并触发下载
149
+ const url = URL.createObjectURL(wavBlob)
150
+ const a = document.createElement('a')
151
+ a.style.display = 'none'
152
+ a.href = url
153
+ a.download = 'recording.wav'
154
+ document.body.appendChild(a)
155
+ a.click()
156
+ window.URL.revokeObjectURL(url)
157
+ },
158
+
159
+ mergeAudioChunks (chunks) {
160
+ const length = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
161
+ const result = new Float32Array(length)
162
+ let offset = 0
163
+ for (const chunk of chunks) {
164
+ result.set(chunk, offset)
165
+ offset += chunk.length
166
+ }
167
+ return result
168
+ },
169
+
170
+ encodeWAV (samples, sampleRate) {
171
+ const buffer = new ArrayBuffer(44 + samples.length * 2) // 每个样本占2字节
172
+ const view = new DataView(buffer)
173
+
174
+ const writeString = (view, offset, string) => {
175
+ for (let i = 0; i < string.length; i++) {
176
+ view.setUint8(offset + i, string.charCodeAt(i))
177
+ }
178
+ }
179
+
180
+ // WAV文件头
181
+ writeString(view, 0, 'RIFF') // ChunkID
182
+ view.setUint32(4, 36 + samples.length * 2, true) // ChunkSize
183
+ writeString(view, 8, 'WAVE') // Format
184
+
185
+ // fmt子块
186
+ writeString(view, 12, 'fmt ') // Subchunk1ID
187
+ view.setUint32(16, 16, true) // Subchunk1Size (PCM格式固定为16)
188
+ view.setUint16(20, 1, true) // AudioFormat (PCM为1)
189
+ view.setUint16(22, 1, true) // NumChannels (单声道为1)
190
+ view.setUint32(24, sampleRate, true) // SampleRate
191
+ view.setUint32(28, sampleRate * 2, true) // ByteRate (SampleRate * NumChannels * BitsPerSample/8)
192
+ view.setUint16(32, 2, true) // BlockAlign (NumChannels * BitsPerSample/8)
193
+ view.setUint16(34, 16, true) // BitsPerSample
194
+
195
+ // data子块
196
+ writeString(view, 36, 'data') // Subchunk2ID
197
+ view.setUint32(40, samples.length * 2, true) // Subchunk2Size
198
+
199
+ // 写入PCM数据
200
+ let offset = 44
201
+ for (let i = 0; i < samples.length; i++) {
202
+ const s = Math.max(-1, Math.min(1, samples[i])) // 限制范围为[-1, 1]
203
+ view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true) // 转换为16位PCM
204
+ offset += 2
205
+ }
206
+
207
+ return new Blob([buffer], { type: 'audio/wav' })
208
+ },
209
+
129
210
  resample (data, inputSampleRate, outputSampleRate) {
130
211
  if (inputSampleRate === outputSampleRate) {
131
212
  return data
@@ -148,6 +229,9 @@ export default {
148
229
  },
149
230
  getRecordingData () {
150
231
  return this.inputData
232
+ },
233
+ resRecordingData () {
234
+ this.$emit('recordingData', this.inputData)
151
235
  }
152
236
  }
153
237
  }
@@ -1,3 +1,3 @@
1
- import Recoding from './Recoding.vue'
1
+ import Recording from './Recording.vue'
2
2
 
3
- export default Recoding
3
+ export default Recording
@@ -617,7 +617,7 @@
617
617
  :flex="attr.flex">
618
618
  <recording
619
619
  ref="recording"
620
- @stop="emitFunc('recording',attr)"
620
+ @recordingData="recordingData"
621
621
  >
622
622
  </recording>
623
623
  </x-form-col>
@@ -1151,7 +1151,11 @@ export default {
1151
1151
  // 获取 recording 转换后的数据
1152
1152
  getRecodingData () {
1153
1153
  return this.$refs.recording.getRecordingData()
1154
+ },
1155
+ recordingData (data) {
1156
+ this.emitFunc('recordingData', data)
1154
1157
  }
1158
+
1155
1159
  }
1156
1160
  }
1157
1161
  </script>
@@ -648,6 +648,12 @@ export default {
648
648
  getTableData () {
649
649
  return this.$refs.xTable.getTableData()
650
650
  },
651
+ /**
652
+ * 设置表格数据
653
+ */
654
+ setTableData (data) {
655
+ this.$refs.xTable.setTableData(data)
656
+ },
651
657
  /**
652
658
  * 表单本地提交
653
659
  */
@@ -160,7 +160,7 @@ export default {
160
160
  // 表格没有边距
161
161
  noPadding: {
162
162
  type: Boolean,
163
- default: false
163
+ default: true
164
164
  },
165
165
  // 表格没有上边框,与noPadding搭配可以实现连续表格
166
166
  noTopBorder: {
@@ -275,34 +275,80 @@ export default {
275
275
  console.log('内部注册完成', this.$refs)
276
276
  },
277
277
 
278
- transformArray (data) {
279
- const result = []
280
- const currentRow = []
281
- let tempGroup = [] // Temporary group for combining cells
278
+ transformArray (inputList) {
279
+ let operationIndex = 0
280
+ let operationList = []
281
+ const outputList = []
282
+ for (const lst of inputList) {
283
+ // 如果列表为空或只有一个元素,则所有元素相等。比较列表中每个元素是否与第一个元素相等
284
+ if (lst.length >= 1 && !lst.every(x => Array.isArray(x) || Array.isArray(lst[0]) || x.rowSpan === lst[0].rowSpan)) {
285
+ operationList = lst
286
+ break // 使用 break 退出整个循环
287
+ } else {
288
+ // 被操作的行
289
+ operationIndex += 1
290
+ }
291
+ }
282
292
 
283
- data.forEach(row => {
284
- row.forEach(cell => {
285
- // Check if the current cell should be part of the temporary group
286
- if (cell.rowSpan === 1) {
287
- tempGroup.push(cell) // Add to temporary group
288
- } else {
289
- // Push the temporary group if it has accumulated cells
290
- if (tempGroup.length > 0) {
291
- currentRow.push(tempGroup)
292
- tempGroup = []
293
- }
294
- currentRow.push(cell)
293
+ let maxMergeRow = 0
294
+
295
+ // 没有需要合并的行,直接返回
296
+ if (operationList.length === 0) {
297
+ return inputList
298
+ } else {
299
+ // 当前行的最大值
300
+ const maxRow = Math.max(...operationList.map(item => item.rowSpan))
301
+ let mergeIndexCol = 0
302
+ for (let index = 0; index < operationList.length; index++) {
303
+ const row = operationList[index]
304
+ let rowSpan = row.rowSpan
305
+ // 需要合并的行
306
+ let mergeIndexRow = operationIndex + 1
307
+ // 存放合并后的行
308
+ const rows = []
309
+ // 添加当前行
310
+ if (rowSpan < maxRow && mergeIndexRow < inputList.length) {
311
+ rows.push([row])
312
+ }
313
+ // 当前需要被操作并且操作行没有超出列表的高度
314
+ while (rowSpan < maxRow && mergeIndexRow < inputList.length) {
315
+ rowSpan += inputList[mergeIndexRow][mergeIndexCol].rowSpan
316
+ // 放一行到合并结果
317
+ rows.push([inputList[mergeIndexRow][mergeIndexCol]])
318
+ if (mergeIndexRow > maxMergeRow) {
319
+ // 记录最多操作到了哪行
320
+ maxMergeRow = mergeIndexRow
321
+ }
322
+ mergeIndexRow++
323
+ }
324
+ // operation_list赋值, 没有变化的,不处理
325
+ if (rows.length !== 0) {
326
+ operationList[index] = rows
327
+ }
328
+ if (row.rowSpan !== maxRow) {
329
+ mergeIndexCol++ // 操作列转为下一列
330
+ }
295
331
  }
296
- })
297
- })
332
+ }
298
333
 
299
- // Add remaining temporary group to the row
300
- if (tempGroup.length > 0) {
301
- currentRow.push(tempGroup)
334
+ // 组成outputlist, operation_list前部填入
335
+ let putindex = 0
336
+ while (operationIndex > 0) {
337
+ outputList.push(inputList[putindex])
338
+ putindex++
339
+ operationIndex -= 1
302
340
  }
303
341
 
304
- result.push(currentRow)
305
- return result
342
+ outputList.push(operationList)
343
+
344
+ // 组成outputlist, operation_list后部填入
345
+ while (maxMergeRow < inputList.length - 1) {
346
+ outputList.push(inputList[maxMergeRow + 1])
347
+ maxMergeRow += 1
348
+ }
349
+
350
+ console.log('转换完成了', outputList)
351
+ return this.transformArray(outputList)
306
352
  },
307
353
 
308
354
  // 根据名字从注册到组件中获取组件
@@ -887,14 +933,15 @@ export default {
887
933
  }
888
934
  })
889
935
  })
890
- this.$nextTick(() => {
891
- this.scanFinish = true
892
- })
893
936
 
894
937
  // 对配置进行转换
895
938
  console.log('转换前配置', this.config)
896
- const newColumns = this.transformArray(this.config.columns)
897
- console.log('转换后的列描述', newColumns)
939
+ this.originalConfig.columns = this.transformArray(this.config.columns)
940
+ console.log('转换后的列描述', this.originalConfig.columns)
941
+
942
+ this.$nextTick(() => {
943
+ this.scanFinish = true
944
+ })
898
945
  },
899
946
  // 初始化JSON配置
900
947
  jsonConfigInit () {
@@ -4,8 +4,8 @@
4
4
  <XReport
5
5
  ref="main"
6
6
  :use-oss-for-img="false"
7
- config-name="测试栅格配置"
8
- server-name="af-system"
7
+ config-name="pharStockCover"
8
+ server-name="af-his"
9
9
  :show-img-in-cell="true"
10
10
  :display-only="true"
11
11
  :edit-mode="false"
@@ -17,7 +17,7 @@
17
17
  <!-- 大标题 -->
18
18
  <h2 class="reportTitle" v-if="showTitle && activatedConfig.title" v-html="activatedConfig.title"></h2>
19
19
  <!-- 小标题 / 介乎于标题与表格之间的内容 -->
20
- <div class="subTitle" v-if="activatedConfig.subTitle">
20
+ <div class="subTitle" v-if="activatedConfig.subTitle && activatedConfig.subTitle.length">
21
21
  <div class="subTitleItems" v-for="(item, itemIndex) in activatedConfig.subTitle" :key="itemIndex">
22
22
  <template v-if="item.type === 'column'">
23
23
  <span>{{ item.text }}</span>
@@ -270,7 +270,7 @@ export default {
270
270
  // 表格没有边距
271
271
  noPadding: {
272
272
  type: Boolean,
273
- default: false
273
+ default: true
274
274
  },
275
275
  // 表格没有上边框,与noPadding搭配可以实现连续表格
276
276
  noTopBorder: {
@@ -504,7 +504,8 @@ export default {
504
504
  color: #000;
505
505
  // background-color: #fff;
506
506
  border-radius: 8px;
507
- height: 85vh;
507
+ height: auto;
508
+ min-height : 20vh;
508
509
  overflow-y: auto;
509
510
  overflow-x: hidden;
510
511
 
@@ -1,19 +1,37 @@
1
1
  <template>
2
- <a-row type="flex" :gutter="gutter" style="margin-bottom: 20px;">
2
+ <a-row type="flex" :gutter="gutter" style="margin-bottom: .5rem;">
3
3
  <!-- 预览页展示 -->
4
4
  <template v-if="display">
5
5
  <template v-if="!inputColumns">
6
6
  <a-col
7
7
  name="trGroup"
8
- style="flex: 1;"
9
8
  v-for="(cell, cellIndex) in columns"
10
- v-if="!cell.dontShowRow"
9
+ v-if="Array.isArray(cell) || !cell.dontShowRow"
11
10
  :key="cellIndex"
12
- :style="determineCellStyle(cell)"
13
- :span="cell.colSpan ? cell.colSpan * 2 : undefined">
11
+ :style="Array.isArray(cell) ? {} : determineCellStyle(cell)"
12
+ :span="Array.isArray(cell) ? cell[0][0].colSpan * 2 : (cell.colSpan ? cell.colSpan * 2 : undefined)">
14
13
  <a-card class="flexItem" :bordered="false" :body-style="flexItemBodyState">
15
14
  <!-- 插槽渲染 -->
16
- <template v-if="cell.type === 'slot'">
15
+ <template v-if="Array.isArray(cell)">
16
+ <!-- 处理 cell 是数组的情况 -->
17
+ <div v-for="(item, index) in cell" :key="index">
18
+ <x-report-tr-group
19
+ @updateImg="updateImg"
20
+ :show-img-in-cell="showImgInCell"
21
+ :img-prefix="imgPrefix"
22
+ :server-name="serverName"
23
+ :env="env"
24
+ :use-oss-for-img="useOssForImg"
25
+ :key="index"
26
+ :columns="recalculateItem(item)"
27
+ :no-top-border="noTopBorder"
28
+ :config-data="configData"
29
+ :config="config"
30
+ :display="true">
31
+ </x-report-tr-group>
32
+ </div>
33
+ </template>
34
+ <template v-else-if="cell.type === 'slot'">
17
35
  <template
18
36
  v-if="['x-form-table','x-add-native-form','x-tree-pro', 'x-his-editor', 'x-tab', 'x-form-group', 'x-report', 'x-buttons', 'x-conversation'].includes(cell.slotType)">
19
37
  <component
@@ -592,6 +610,19 @@ export default {
592
610
  }, this.env === 'dev')
593
611
  }
594
612
  },
613
+
614
+ // 对于嵌套情况,对colSpan重新分配
615
+ recalculateItem (item) {
616
+ const totalColSpan = item.reduce((sum, cell) => sum + (cell.colSpan || 1), 0) // 计算总的 colSpan
617
+ return item.map(cell => {
618
+ const newColSpan = Math.round((cell.colSpan || 1) / totalColSpan * 12) // 按比例重新分配 colSpan
619
+ return {
620
+ ...cell,
621
+ colSpan: newColSpan // 更新 colSpan
622
+ }
623
+ })
624
+ },
625
+
595
626
  getEventHandlers (cell) {
596
627
  const handlers = {}
597
628
  if (!cell?.events || cell?.events?.length === 0) {
@@ -1124,6 +1124,10 @@ export default {
1124
1124
  getTableData () {
1125
1125
  return this.$refs.table.localDataSource
1126
1126
  },
1127
+ // 设置表格内数据
1128
+ setTableData (data) {
1129
+ Object.assign(this.$refs.table, { localDataSource: data })
1130
+ },
1127
1131
  // 获取所有本地数据
1128
1132
  getLocalData () {
1129
1133
  return this.localEditModeDataSource
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div id="test" v-if="showReport">
3
- <XReport
3
+ <recording
4
4
  @updateImg="updateImg"
5
5
  ref="main"
6
6
  :use-oss-for-img="false"
@@ -22,7 +22,8 @@ import { exportHTMLNodeToPDF } from '@vue2-client/utils/htmlToPDFApi'
22
22
  export default {
23
23
  name: 'Example',
24
24
  components: {
25
- XReport
25
+ XReport,
26
+ Recording: () => import('@vue2-client/base-client/components/common/Recording')
26
27
  },
27
28
  mounted () {
28
29
  console.log(this.$route)