vue2-client 1.12.64 → 1.12.66
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/XInput/XInput.vue +267 -0
- package/src/base-client/components/common/XInput/index.js +3 -0
- package/src/base-client/components/common/XReportGrid/XReportDesign.vue +3 -3
- package/src/base-client/components/common/XReportGrid/XReportTrGroup.vue +701 -700
- package/src/base-client/components/his/XList/XList.vue +1 -0
- package/src/router/async/router.map.js +2 -2
package/package.json
CHANGED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="x-input-wrapper" v-click-outside="handleClickOutside">
|
|
3
|
+
<!-- 输入框部分 -->
|
|
4
|
+
<a-input
|
|
5
|
+
v-model="innerValue"
|
|
6
|
+
v-bind="$attrs"
|
|
7
|
+
:placeholder="config?.placeholder"
|
|
8
|
+
:size="config?.size"
|
|
9
|
+
:maxLength="config?.maxLength"
|
|
10
|
+
:disabled="config?.disabled"
|
|
11
|
+
:allowClear="config?.allowClear"
|
|
12
|
+
@change="handleChange"
|
|
13
|
+
@pressEnter="handlePressEnter"
|
|
14
|
+
@focus="handleFocus"
|
|
15
|
+
>
|
|
16
|
+
<template v-if="config?.prefix" #prefix>
|
|
17
|
+
<a-icon :type="config.prefix" @click="handlePrefixClick" class="clickable-icon" />
|
|
18
|
+
</template>
|
|
19
|
+
<template v-if="config?.suffix" #suffix>
|
|
20
|
+
<a-icon :type="config.suffix" @click="handleSuffixClick" class="clickable-icon" />
|
|
21
|
+
</template>
|
|
22
|
+
</a-input>
|
|
23
|
+
|
|
24
|
+
<!-- 搜索结果列表 -->
|
|
25
|
+
<div v-if="isListVisible" class="search-list">
|
|
26
|
+
<a-list
|
|
27
|
+
v-if="searchList.length > 0"
|
|
28
|
+
size="small"
|
|
29
|
+
bordered
|
|
30
|
+
>
|
|
31
|
+
<a-list-item
|
|
32
|
+
v-for="item in searchList"
|
|
33
|
+
:key="item.id"
|
|
34
|
+
@click="handleSelect(item)"
|
|
35
|
+
>
|
|
36
|
+
<div class="item-content">
|
|
37
|
+
<span class="item-name">{{ item.name }}</span>
|
|
38
|
+
<span class="item-code">{{ item.code }}</span>
|
|
39
|
+
</div>
|
|
40
|
+
</a-list-item>
|
|
41
|
+
</a-list>
|
|
42
|
+
<div v-else class="empty-data">
|
|
43
|
+
<a-empty :image="simpleImage" :description="'暂无数据'" />
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script>
|
|
50
|
+
import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
|
|
51
|
+
import debounce from 'lodash/debounce'
|
|
52
|
+
import { Empty } from 'ant-design-vue'
|
|
53
|
+
|
|
54
|
+
// 自定义指令:点击外部关闭
|
|
55
|
+
const clickOutside = {
|
|
56
|
+
bind (el, binding) {
|
|
57
|
+
el.__vueClickOutside__ = function (event) {
|
|
58
|
+
if (!(el === event.target || el.contains(event.target))) {
|
|
59
|
+
binding.value(event)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
document.body.addEventListener('click', el.__vueClickOutside__)
|
|
63
|
+
},
|
|
64
|
+
unbind (el) {
|
|
65
|
+
document.body.removeEventListener('click', el.__vueClickOutside__)
|
|
66
|
+
delete el.__vueClickOutside__
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default {
|
|
71
|
+
name: 'XInput',
|
|
72
|
+
inheritAttrs: false,
|
|
73
|
+
directives: {
|
|
74
|
+
'click-outside': clickOutside
|
|
75
|
+
},
|
|
76
|
+
props: {
|
|
77
|
+
value: {
|
|
78
|
+
type: [String, Number],
|
|
79
|
+
default: ''
|
|
80
|
+
},
|
|
81
|
+
queryParamsName: {
|
|
82
|
+
type: String,
|
|
83
|
+
default: 'inputTestConfig'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
data () {
|
|
87
|
+
return {
|
|
88
|
+
innerValue: this.value,
|
|
89
|
+
config: null,
|
|
90
|
+
searchList: [],
|
|
91
|
+
isListVisible: false,
|
|
92
|
+
simpleImage: Empty.PRESENTED_IMAGE_SIMPLE
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
created () {
|
|
96
|
+
this.getData(this.queryParamsName)
|
|
97
|
+
this.debouncedSearch = debounce(this.handleSearch, 300)
|
|
98
|
+
},
|
|
99
|
+
methods: {
|
|
100
|
+
async getData (data) {
|
|
101
|
+
getConfigByName(data, 'af-his', res => {
|
|
102
|
+
console.warn(res)
|
|
103
|
+
this.config = res
|
|
104
|
+
if (res.defaultValue !== undefined) {
|
|
105
|
+
this.innerValue = res.defaultValue
|
|
106
|
+
// 当配置中有默认值时,向父组件同步默认值
|
|
107
|
+
this.$emit('input', res.defaultValue)
|
|
108
|
+
}
|
|
109
|
+
if (res.logicName) {
|
|
110
|
+
runLogic(res.logicName, res.parameter, 'af-his').then(result => {
|
|
111
|
+
if (result) {
|
|
112
|
+
this.handleLogicResult(result)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
handleChange (e) {
|
|
119
|
+
const value = e.target.value
|
|
120
|
+
// 双向绑定:将输入框的值同步给父组件
|
|
121
|
+
this.$emit('input', value)
|
|
122
|
+
// 发送change事件:当输入框值变化时触发,携带原生事件对象
|
|
123
|
+
this.$emit('change', e)
|
|
124
|
+
this.debouncedSearch(value)
|
|
125
|
+
},
|
|
126
|
+
handlePressEnter (e) {
|
|
127
|
+
// 发送回车事件:当用户按下回车键时触发
|
|
128
|
+
this.$emit('pressEnter', e)
|
|
129
|
+
this.handleSearch(e.target.value)
|
|
130
|
+
},
|
|
131
|
+
handleFocus () {
|
|
132
|
+
this.isListVisible = true
|
|
133
|
+
// 当获取焦点时,如果输入框为空,显示所有数据
|
|
134
|
+
if (!this.innerValue) {
|
|
135
|
+
this.handleSearch('')
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
handleClickOutside () {
|
|
139
|
+
this.isListVisible = false
|
|
140
|
+
},
|
|
141
|
+
async handleSearch (value) {
|
|
142
|
+
if (!this.config?.searchConfig?.logicName) return
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// 调用后端搜索接口
|
|
146
|
+
const result = await runLogic(
|
|
147
|
+
this.config.searchConfig.logicName,
|
|
148
|
+
{
|
|
149
|
+
...this.config.searchConfig.parameter,
|
|
150
|
+
searchValue: value
|
|
151
|
+
},
|
|
152
|
+
'af-his'
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if (result) {
|
|
156
|
+
// 更新搜索结果列表
|
|
157
|
+
this.searchList = result || []
|
|
158
|
+
console.warn(result)
|
|
159
|
+
// 发送搜索结果
|
|
160
|
+
this.$emit('search-result', result)
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('搜索失败:', error)
|
|
164
|
+
this.searchList = []
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
handlePrefixClick (e) {
|
|
168
|
+
e.stopPropagation()
|
|
169
|
+
// 发送前缀图标点击事件:当用户点击输入框前缀图标时触发
|
|
170
|
+
this.$emit('prefix-click', this.innerValue)
|
|
171
|
+
this.handleSearch(this.innerValue)
|
|
172
|
+
},
|
|
173
|
+
handleSuffixClick (e) {
|
|
174
|
+
e.stopPropagation()
|
|
175
|
+
// 发送后缀图标点击事件:当用户点击输入框后缀图标时触发
|
|
176
|
+
this.$emit('suffix-click', this.innerValue)
|
|
177
|
+
this.handleSearch(this.innerValue)
|
|
178
|
+
},
|
|
179
|
+
handleLogicResult (result) {
|
|
180
|
+
if (result.value !== undefined) {
|
|
181
|
+
this.innerValue = result.value
|
|
182
|
+
// 发送输入值:当逻辑处理返回新值时,同步给父组件
|
|
183
|
+
this.$emit('input', result.value)
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
handleSelect (item) {
|
|
187
|
+
this.innerValue = item.name
|
|
188
|
+
this.searchList = [] // 清空列表
|
|
189
|
+
this.isListVisible = false // 选择后关闭列表
|
|
190
|
+
// 发送选中项的名称:用于更新输入框的值
|
|
191
|
+
this.$emit('input', item.name)
|
|
192
|
+
// 发送选中事件:当用户从搜索结果列表中选择一项时触发,携带完整的选中项数据
|
|
193
|
+
this.$emit('select', item)
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
watch: {
|
|
197
|
+
value: {
|
|
198
|
+
handler (newValue) {
|
|
199
|
+
this.innerValue = newValue
|
|
200
|
+
},
|
|
201
|
+
immediate: true
|
|
202
|
+
},
|
|
203
|
+
queryParamsName: {
|
|
204
|
+
handler (newValue) {
|
|
205
|
+
this.getData(newValue)
|
|
206
|
+
},
|
|
207
|
+
deep: true
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
beforeDestroy () {
|
|
211
|
+
this.debouncedSearch.cancel()
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
</script>
|
|
215
|
+
|
|
216
|
+
<style scoped>
|
|
217
|
+
.x-input-wrapper {
|
|
218
|
+
position: relative;
|
|
219
|
+
display: inline-block;
|
|
220
|
+
width: 100%;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.search-list {
|
|
224
|
+
position: absolute;
|
|
225
|
+
top: 100%;
|
|
226
|
+
left: 0;
|
|
227
|
+
width: 100%;
|
|
228
|
+
max-height: 300px;
|
|
229
|
+
overflow-y: auto;
|
|
230
|
+
background: #fff;
|
|
231
|
+
z-index: 1000;
|
|
232
|
+
margin-top: 4px;
|
|
233
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.item-content {
|
|
237
|
+
display: flex;
|
|
238
|
+
justify-content: space-between;
|
|
239
|
+
width: 100%;
|
|
240
|
+
padding: 8px 12px;
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.item-content:hover {
|
|
245
|
+
background-color: #f5f5f5;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.item-code {
|
|
249
|
+
color: rgba(0, 0, 0, 0.45);
|
|
250
|
+
font-size: 12px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
:deep(.clickable-icon) {
|
|
254
|
+
cursor: pointer;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
:deep(.clickable-icon:hover) {
|
|
258
|
+
color: #1890ff;
|
|
259
|
+
}
|
|
260
|
+
.empty-data {
|
|
261
|
+
padding: 16px;
|
|
262
|
+
text-align: center;
|
|
263
|
+
background: #fff;
|
|
264
|
+
border: 1px solid #d9d9d9;
|
|
265
|
+
border-radius: 2px;
|
|
266
|
+
}
|
|
267
|
+
</style>
|