vue2-client 1.9.83 → 1.9.85

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,13 +1,13 @@
1
1
  {
2
2
  "name": "vue2-client",
3
- "version": "1.9.83",
3
+ "version": "1.9.85",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
7
7
  "serve:gaslink": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode gaslink",
8
8
  "serve:revenue": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode revenue",
9
9
  "serve:his": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint --mode his",
10
- "mac-serve": "vue-cli-service serve --no-eslint --mode mac",
10
+ "mac-serve": "vue-cli-service serve --no-eslint --mode his",
11
11
  "build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
12
12
  "test:unit": "vue-cli-service test:unit",
13
13
  "lint": "vue-cli-service lint",
@@ -217,6 +217,12 @@ export default {
217
217
  },
218
218
  ...mapState('account', { currUser: 'user' })
219
219
  },
220
+ provide () {
221
+ return {
222
+ getComponentByName: this.getComponentByName,
223
+ registerComponent: this.registerComponent
224
+ }
225
+ },
220
226
  methods: {
221
227
  runLogic,
222
228
  getConfigByNameAsync,
@@ -298,6 +304,17 @@ export default {
298
304
  }
299
305
  this.loaded = true
300
306
  },
307
+ registerComponent (componentName, component) {
308
+ console.log('内部注册', this.$options.name, componentName)
309
+ this.$refs[componentName] = component
310
+ console.log('内部注册完成', this.$refs)
311
+ },
312
+ // 根据名字从注册到组件中获取组件
313
+ getComponentByName (componentName) {
314
+ console.log('内部取组件', this.$options.name, componentName)
315
+ console.log('内部组件内容', this.$refs)
316
+ return this.$refs[componentName]
317
+ },
301
318
  // 兼容需要省略 传递 [formItems: res.formJson ] 可以使用 ...res 展开运算符 直接转递
302
319
  getFromItem (formItems, formJson) {
303
320
  const _formItems = formItems || formJson
@@ -747,6 +764,7 @@ export default {
747
764
  }).catch(e => {
748
765
  this.$message.error(this.businessType + '失败:' + e)
749
766
  }).finally(() => {
767
+ this.loading = false
750
768
  if (callback) {
751
769
  callback()
752
770
  }
@@ -30,6 +30,7 @@
30
30
  <script>
31
31
  import { runLogic } from '@vue2-client/services/api/common'
32
32
  export default {
33
+ inject: ['getSelectedId', 'getSelectedData', 'getMixinData', 'getOutEnv', 'currUser'],
33
34
  data () {
34
35
  return {
35
36
  serviceName: undefined,
@@ -52,10 +53,10 @@ export default {
52
53
  const {
53
54
  serviceName,
54
55
  // 配置内容
55
- value
56
+ logicName
56
57
  } = params
57
- console.log('=========')
58
- this.renderConfig = value
58
+ console.log('调用会话框init方法', params)
59
+ this.renderConfig = { logicName: logicName }
59
60
  this.loading = true
60
61
  this.serviceName = serviceName
61
62
  },
@@ -77,6 +78,9 @@ export default {
77
78
  this.messages.push({ type: 'bot', text: response.value })
78
79
  },
79
80
  },
81
+ mounted () {
82
+ this.setAddtionalInfo(this.getMixinData())
83
+ },
80
84
  }
81
85
  </script>
82
86
 
@@ -29,12 +29,19 @@
29
29
  :placeholder="attr.placeholder ? attr.placeholder : '请输入'+attr.name.replace(/\s*/g, '')"
30
30
  :ref="`${attr.model}input`"/>
31
31
  <a-button
32
- v-if="attr.inputOnAfterName && attr.inputOnAfterFunc"
32
+ v-if="attr.inputOnAfterName && attr.inputOnAfterFunc && !attr.inputOnAfterName.includes('|')"
33
33
  style="flex: 1; width: auto; min-width: 4rem;max-width: 6rem"
34
34
  type="primary"
35
35
  @click="emitFunc(attr.inputOnAfterFunc,attr)">
36
36
  {{ attr.inputOnAfterName }}
37
37
  </a-button>
38
+ <!-- 状态按钮 -->
39
+ <x-status-button
40
+ v-else
41
+ :states="parseStates(attr.inputOnAfterName, attr.inputOnAfterFunc)"
42
+ v-on="generateDynamicEvents(attr.inputOnAfterFunc, attr)"
43
+ style="flex: 1; width: auto; min-width: 4rem; max-width: 6rem"
44
+ />
38
45
  <!-- 仅可以配置 一个按钮 以及 一个图标插槽 -->
39
46
  <a-button
40
47
  style="width: 2rem; flex-shrink: 0;"
@@ -621,6 +628,26 @@
621
628
  >
622
629
  </recording>
623
630
  </x-form-col>
631
+ <!-- 表格录入 -->
632
+ <x-form-col
633
+ v-else-if="attr.type === 'rowEdit' && show"
634
+ :flex="attr.flex">
635
+ <a-form-model-item
636
+ :ref="attr.model"
637
+ :label="showLabel?attr.name:undefined"
638
+ :prop="attr.prop ? attr.prop : attr.model">
639
+ <x-form-table
640
+ :key="'childTable_' + attr.model"
641
+ :title="attr.name"
642
+ :queryParamsName="attr.crud"
643
+ :localEditMode="true"
644
+ :fixed-query-form="childTableFixedQueryForm(attr)"
645
+ :service-name="serviceName"
646
+ @hook:mounted="(h)=>onComponentMounted(h, attr)"
647
+ :ref="'childXFormTable_' + attr.model">
648
+ </x-form-table>
649
+ </a-form-model-item>
650
+ </x-form-col>
624
651
  </template>
625
652
  <script>
626
653
  import { debounce } from 'ant-design-vue/lib/vc-table/src/utils'
@@ -636,8 +663,9 @@ import util from '@vue2-client/utils/util'
636
663
  import XTreeSelect from '@vue2-client/base-client/components/common/XForm/XTreeSelect'
637
664
  import { searchToListOption, searchToOption } from '@vue2-client/services/v3Api'
638
665
  import { mapState } from 'vuex'
639
- import { executeStrFunction } from '@vue2-client/utils/runEvalFunction'
666
+ import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
640
667
  import XLicensePlate from '@vue2-client/base-client/components/common/XLicensePlate/XLicensePlate.vue'
668
+ import XStatusButton from './XStatusButton.vue'
641
669
 
642
670
  export default {
643
671
  name: 'XFormItem',
@@ -651,7 +679,8 @@ export default {
651
679
  CitySelect,
652
680
  PersonSetting,
653
681
  AddressSearchCombobox,
654
- Upload
682
+ Upload,
683
+ XStatusButton
655
684
  },
656
685
  data () {
657
686
  // 检索去抖
@@ -849,7 +878,46 @@ export default {
849
878
  deep: true
850
879
  }
851
880
  },
881
+ inject: ['registerComponent', 'getComponentByName'],
852
882
  methods: {
883
+ // 把内部的crud表单录入放到表单中,以便外部可以调用
884
+ onComponentMounted (h, attr) {
885
+ console.log('crud表单', h)
886
+ if (cell.slotRef) {
887
+ this.registerComponent(attr.key, this.$refs["'childXFormTable_' + attr.model"])
888
+ }
889
+ },
890
+ childTableFixedQueryForm (item) {
891
+ console.log('传递的form', this.form)
892
+ if (this.modifyModelData?.primaryKeyData) {
893
+ const fixedForm = {}
894
+ fixedForm[item.childTableForeignKeyName] = Object.values(this.modifyModelData.primaryKeyData)[0]
895
+ return fixedForm
896
+ }
897
+ return null
898
+ },
899
+ // 动态生成事件绑定对象
900
+ generateDynamicEvents (inputOnAfterFunc, attr) {
901
+ const events = {}
902
+ const states = this.parseStates(attr.inputOnAfterName, inputOnAfterFunc)
903
+
904
+ states.forEach((state) => {
905
+ // 动态绑定事件名到 emitFunc
906
+ events[state.event] = () => {
907
+ console.info('事件名', state.event)
908
+ this.emitFunc(state.event, attr)
909
+ }
910
+ })
911
+
912
+ return events // 返回 { state1Event: handler, state2Event: handler, ... }
913
+ },
914
+ parseStates (input, events) {
915
+ const eventNames = events.split('|')
916
+ return input.split('|').map((label, index) => ({
917
+ label,
918
+ event: eventNames[index] // 如果没有提供事件名称,则使用默认值
919
+ }))
920
+ },
853
921
  focusInput () {
854
922
  if (this.attr.defaultFocus) {
855
923
  this.$nextTick(h => {
@@ -904,17 +972,17 @@ export default {
904
972
  // js 函数作为数据源
905
973
  async updateOptions () {
906
974
  if (this.attr.keyName && (this.attr.keyName.indexOf('async ') !== -1 || this.attr.keyName.indexOf('function ') !== -1)) {
907
- this.optionForFunc = await executeStrFunction(this.attr.keyName, [this.form, runLogic, this.mode, getConfigByNameAsync])
975
+ this.optionForFunc = await executeStrFunctionByContext(this, this.attr.keyName, [this.form, runLogic, this.mode, getConfigByNameAsync])
908
976
  }
909
977
  },
910
978
  async dataChangeFunc () {
911
979
  if (this.attr.dataChangeFunc) {
912
- await executeStrFunction(this.attr.dataChangeFunc, [this.form, this.setForm, this.attr, util, this.mode, runLogic, getConfigByNameAsync])
980
+ await executeStrFunctionByContext(this, this.attr.dataChangeFunc, [this.form, this.setForm, this.attr, util, this.mode, runLogic, getConfigByNameAsync])
913
981
  }
914
982
  },
915
983
  async showFormItemFunc () {
916
984
  if (this.attr.showFormItemFunc) {
917
- const obj = executeStrFunction(this.attr.showFormItemFunc, [this.form, this.setForm, this.attr, util, this.mode])
985
+ const obj = executeStrFunctionByContext(this, this.attr.showFormItemFunc, [this.form, this.setForm, this.attr, util, this.mode])
918
986
  // 判断是 bool 还是 obj 兼容
919
987
  if (typeof obj === 'boolean') {
920
988
  this.show = obj
@@ -929,7 +997,7 @@ export default {
929
997
  },
930
998
  async showQueryFormItemFunc () {
931
999
  if (this.attr.showQueryFormItemFunc) {
932
- const obj = executeStrFunction(this.attr.showQueryFormItemFunc, [this.form, this.setForm, this.attr, util, this.mode])
1000
+ const obj = executeStrFunctionByContext(this, this.attr.showQueryFormItemFunc, [this.form, this.setForm, this.attr, util, this.mode])
933
1001
  // 判断是 bool 还是 obj 兼容
934
1002
  if (typeof obj === 'boolean') {
935
1003
  this.show = obj
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div>
3
+ <a-button type="primary" @click="handleClick">{{ currentLabel }}</a-button>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'StateButton',
10
+ props: {
11
+ // 定义状态和对应的显示文本与事件
12
+ states: {
13
+ type: Array,
14
+ required: true,
15
+ default: () => [
16
+ { label: '状态1', event: 'state1Event' },
17
+ { label: '状态2', event: 'state2Event' },
18
+ { label: '状态3', event: 'state3Event' },
19
+ ],
20
+ },
21
+ initialIndex: {
22
+ type: Number,
23
+ default: 0,
24
+ },
25
+ },
26
+ data () {
27
+ return {
28
+ currentIndex: this.initialIndex,
29
+ }
30
+ },
31
+ computed: {
32
+ // 当前显示的按钮文本
33
+ currentLabel () {
34
+ return this.states[this.currentIndex]?.label || '按钮'
35
+ },
36
+ },
37
+ methods: {
38
+ handleClick () {
39
+ // 触发当前状态的事件
40
+ const currentState = this.states[this.currentIndex]
41
+ if (currentState && currentState.event) {
42
+ this.$emit(currentState.event)
43
+ }
44
+
45
+ // 切换到下一个状态
46
+ this.currentIndex = (this.currentIndex + 1) % this.states.length
47
+ },
48
+ },
49
+ }
50
+ </script>
51
+
52
+ <style scoped>
53
+ /* 添加样式以适应项目需求 */
54
+ </style>
@@ -4,7 +4,7 @@
4
4
  <XReport
5
5
  ref="main"
6
6
  :use-oss-for-img="false"
7
- config-name="pharStockCover"
7
+ config-name="medicalRecordCover"
8
8
  server-name="af-his"
9
9
  :show-img-in-cell="true"
10
10
  :display-only="true"
@@ -770,6 +770,8 @@ export default {
770
770
  this.form = form
771
771
  if (this.createdQuery) {
772
772
  this.refresh(true)
773
+ } else {
774
+ this.createdQuery = true
773
775
  }
774
776
  },
775
777
  /**
@@ -0,0 +1,210 @@
1
+ // recordingPlugin.js
2
+ export default {
3
+ install (Vue) {
4
+ Vue.prototype.$recording = {
5
+ isRecording: false, // 标识是否正在录音
6
+ mediaRecorder: null, // MediaRecorder 实例
7
+ audioChunks: [], // 存储录音数据块
8
+ audioUrl: null, // 录音生成的 URL
9
+ inputData: '',
10
+ audioContext: null,
11
+ audioStream: null,
12
+ ws: null,
13
+
14
+ async startRecording () {
15
+ this.inputData = ''
16
+ this.audioChunks = []
17
+
18
+ const url = 'ws://192.168.50.67:31090/websocket/bash' // WebSocket地址
19
+ this.setupWebSocket(url)
20
+
21
+ // 等待 WebSocket 连接成功
22
+ await this.waitForWebSocketConnection()
23
+
24
+ this.isRecording = true
25
+
26
+ // 初始化 AudioContext
27
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)()
28
+
29
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
30
+ throw new Error(
31
+ '录音软件不支持您的浏览器!请使用现代浏览器或确保您正在使用 HTTPS。'
32
+ )
33
+ }
34
+
35
+ // 获取麦克风输入流
36
+ this.audioStream = await navigator.mediaDevices.getUserMedia({ audio: true })
37
+
38
+ // 设置音频处理流程
39
+ this.setupAudioProcessing()
40
+ },
41
+
42
+ setupWebSocket (url) {
43
+ this.ws = new WebSocket(url)
44
+
45
+ this.ws.onmessage = (event) => {
46
+ const data = JSON.parse(event.data)
47
+ console.log('Result:', data.result)
48
+ if (data.result !== undefined) {
49
+ this.inputData += data.result
50
+ }
51
+ if (!this.isRecording) {
52
+ this.ws.close()
53
+ this.resRecordingData()
54
+ }
55
+ }
56
+
57
+ this.ws.onclose = () => console.log('WebSocket closed.')
58
+ this.ws.onerror = (error) => console.error('WebSocket error:', error)
59
+ },
60
+
61
+ waitForWebSocketConnection () {
62
+ return new Promise((resolve, reject) => {
63
+ this.ws.onopen = () => {
64
+ console.log('WebSocket 连接成功')
65
+ resolve()
66
+ }
67
+
68
+ this.ws.onerror = (error) => {
69
+ reject(new Error('WebSocket 连接失败: ' + error.message))
70
+ }
71
+ })
72
+ },
73
+
74
+ setupAudioProcessing () {
75
+ try {
76
+ const source = this.audioContext.createMediaStreamSource(this.audioStream)
77
+ const processor = this.audioContext.createScriptProcessor(4096, 1, 1)
78
+
79
+ processor.onaudioprocess = (e) => {
80
+ if (!this.isRecording) return
81
+
82
+ const inputData = e.inputBuffer.getChannelData(0)
83
+
84
+ // Resample to 16000 Hz if needed
85
+ const resampledData = this.resample(inputData, this.audioContext.sampleRate, 16000)
86
+
87
+ this.audioChunks.push(new Float32Array(resampledData))
88
+
89
+ // Convert to 16-bit PCM and send via WebSocket
90
+ const byteArray = this.convertTo16BitPCM(resampledData)
91
+ this.ws.send(byteArray)
92
+ }
93
+
94
+ source.connect(processor)
95
+ processor.connect(this.audioContext.destination)
96
+ } catch (error) {
97
+ console.error('Error setting up audio processing:', error)
98
+ throw error
99
+ }
100
+ },
101
+
102
+ stopRecording () {
103
+ this.isRecording = false
104
+
105
+ // 停止麦克风流
106
+ if (this.audioStream) {
107
+ this.audioStream.getTracks().forEach((track) => track.stop())
108
+ }
109
+
110
+ if (this.audioContext) {
111
+ this.audioContext.close()
112
+ }
113
+ },
114
+
115
+ saveAudioFile () {
116
+ const audioBuffer = this.mergeAudioChunks(this.audioChunks)
117
+
118
+ // 转换为 WAV 格式
119
+ const wavBlob = this.encodeWAV(audioBuffer, 16000)
120
+
121
+ // 创建下载链接并触发下载
122
+ const url = URL.createObjectURL(wavBlob)
123
+ const a = document.createElement('a')
124
+ a.style.display = 'none'
125
+ a.href = url
126
+ a.download = 'recording.wav'
127
+ document.body.appendChild(a)
128
+ a.click()
129
+ window.URL.revokeObjectURL(url)
130
+ },
131
+
132
+ mergeAudioChunks (chunks) {
133
+ const length = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
134
+ const result = new Float32Array(length)
135
+ let offset = 0
136
+ for (const chunk of chunks) {
137
+ result.set(chunk, offset)
138
+ offset += chunk.length
139
+ }
140
+ return result
141
+ },
142
+
143
+ encodeWAV (samples, sampleRate) {
144
+ const buffer = new ArrayBuffer(44 + samples.length * 2)
145
+ const view = new DataView(buffer)
146
+
147
+ const writeString = (view, offset, string) => {
148
+ for (let i = 0; i < string.length; i++) {
149
+ view.setUint8(offset + i, string.charCodeAt(i))
150
+ }
151
+ }
152
+
153
+ writeString(view, 0, 'RIFF')
154
+ view.setUint32(4, 36 + samples.length * 2, true)
155
+ writeString(view, 8, 'WAVE')
156
+
157
+ writeString(view, 12, 'fmt ')
158
+ view.setUint32(16, 16, true)
159
+ view.setUint16(20, 1, true)
160
+ view.setUint16(22, 1, true)
161
+ view.setUint32(24, sampleRate, true)
162
+ view.setUint32(28, sampleRate * 2, true)
163
+ view.setUint16(32, 2, true)
164
+ view.setUint16(34, 16, true)
165
+
166
+ writeString(view, 36, 'data')
167
+ view.setUint32(40, samples.length * 2, true)
168
+
169
+ let offset = 44
170
+ for (let i = 0; i < samples.length; i++) {
171
+ const s = Math.max(-1, Math.min(1, samples[i]))
172
+ view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true)
173
+ offset += 2
174
+ }
175
+
176
+ return new Blob([buffer], { type: 'audio/wav' })
177
+ },
178
+
179
+ resample (data, inputSampleRate, outputSampleRate) {
180
+ if (inputSampleRate === outputSampleRate) {
181
+ return data
182
+ }
183
+ const sampleRateRatio = inputSampleRate / outputSampleRate
184
+ const newLength = Math.round(data.length / sampleRateRatio)
185
+ const resampledData = new Float32Array(newLength)
186
+ for (let i = 0; i < newLength; i++) {
187
+ const index = Math.round(i * sampleRateRatio)
188
+ resampledData[i] = data[index]
189
+ }
190
+ return resampledData
191
+ },
192
+
193
+ convertTo16BitPCM (data) {
194
+ const output = new Int16Array(data.length)
195
+ for (let i = 0; i < data.length; i++) {
196
+ output[i] = Math.max(-1, Math.min(1, data[i])) * 0x7fff
197
+ }
198
+ return new Uint8Array(output.buffer)
199
+ },
200
+
201
+ getRecordingData () {
202
+ return this.inputData
203
+ },
204
+
205
+ resRecordingData () {
206
+ // Custom logic for emitting recording data
207
+ }
208
+ }
209
+ }
210
+ }
@@ -1,21 +1,23 @@
1
- import GetLoginInfoService from '@vue2-client/base-client/plugins/GetLoginInfoService'
2
- import GetAppDataService from '@vue2-client/base-client/plugins/AppData'
3
- import getConfig from '@vue2-client/base-client/plugins/Config'
4
- import moment from '@vue2-client/base-client/plugins/moment'
5
-
6
- import VueI18nPlugin from './i18n-extend'
7
- import AuthorityPlugin from './authority-plugin'
8
- import TabsPagePlugin from './tabs-page-plugin'
9
-
10
- const Plugins = {
11
- install: function (Vue) {
12
- Vue.use(GetLoginInfoService)
13
- Vue.use(GetAppDataService)
14
- Vue.use(getConfig)
15
- Vue.use(moment)
16
- Vue.use(VueI18nPlugin)
17
- Vue.use(AuthorityPlugin)
18
- Vue.use(TabsPagePlugin)
19
- }
20
- }
21
- export default Plugins
1
+ import GetLoginInfoService from '@vue2-client/base-client/plugins/GetLoginInfoService'
2
+ import GetAppDataService from '@vue2-client/base-client/plugins/AppData'
3
+ import getConfig from '@vue2-client/base-client/plugins/Config'
4
+ import moment from '@vue2-client/base-client/plugins/moment'
5
+ import recording from '@vue2-client/base-client/plugins/Recording'
6
+
7
+ import VueI18nPlugin from './i18n-extend'
8
+ import AuthorityPlugin from './authority-plugin'
9
+ import TabsPagePlugin from './tabs-page-plugin'
10
+
11
+ const Plugins = {
12
+ install: function (Vue) {
13
+ Vue.use(GetLoginInfoService)
14
+ Vue.use(GetAppDataService)
15
+ Vue.use(getConfig)
16
+ Vue.use(moment)
17
+ Vue.use(VueI18nPlugin)
18
+ Vue.use(AuthorityPlugin)
19
+ Vue.use(TabsPagePlugin)
20
+ Vue.use(recording)
21
+ }
22
+ }
23
+ export default Plugins
@@ -87,9 +87,9 @@ routerResource.example = {
87
87
  // component: () => import('@vue2-client/base-client/components/common/XFormGroup/demo.vue'),
88
88
  // component: () => import('@vue2-client/base-client/components/common/XFormGroup/demo.vue'),
89
89
  // component: () => import('@vue2-client/base-client/components/common/XReport/XReportDemo.vue'),
90
- // component: () => import('@vue2-client/base-client/components/common/XFormTable/demo.vue'),
90
+ component: () => import('@vue2-client/base-client/components/common/XFormTable/demo.vue'),
91
91
  // component: () => import('@vue2-client/base-client/components/common/XTab/XTabDemo.vue'),
92
- component: () => import('@vue2-client/base-client/components/common/XReportGrid/XReportDemo.vue'),
92
+ // component: () => import('@vue2-client/base-client/components/common/XReportGrid/XReportDemo.vue'),
93
93
  // component: () => import('@vue2-client/pages/WorkflowDetail/WorkFlowDemo.vue'),
94
94
  // component: () => import('@vue2-client/base-client/components/common/XConversation/XConversationDemo.vue'),
95
95
  // component: () => import('@vue2-client/base-client/components/common/XButtons/XButtonDemo.vue'),