@antsoo-lib/core 2.0.0 → 2.0.3
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/CHANGELOG.md +12 -0
- package/dist/core.css +1 -0
- package/dist/index.cjs +57 -0
- package/dist/index.js +4154 -0
- package/dist/types/BaseSearch/index.d.ts +59 -0
- package/dist/types/BaseTable/index.d.ts +204 -0
- package/dist/types/Form/CoreForm.d.ts +82 -0
- package/dist/types/Form/types.d.ts +57 -0
- package/dist/types/SSelectPage/index.d.ts +102 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/render/AreaCascader.d.ts +5 -0
- package/dist/types/render/AutoComplete.d.ts +4 -0
- package/dist/types/render/Button.d.ts +4 -0
- package/dist/types/render/Cascader.d.ts +5 -0
- package/dist/types/render/Checkbox.d.ts +4 -0
- package/dist/types/render/CheckboxGroup.d.ts +4 -0
- package/dist/types/render/Custom.d.ts +8 -0
- package/dist/types/render/DatePicker.d.ts +4 -0
- package/dist/types/render/Input.d.ts +4 -0
- package/dist/types/render/InputGroup.d.ts +5 -0
- package/dist/types/render/InputNumber.d.ts +4 -0
- package/dist/types/render/InputPassword.d.ts +4 -0
- package/dist/types/render/InputRange.d.ts +9 -0
- package/dist/types/render/RadioGroup.d.ts +4 -0
- package/dist/types/render/Select.d.ts +4 -0
- package/dist/types/render/SselectPage.d.ts +4 -0
- package/dist/types/render/Switch.d.ts +4 -0
- package/dist/types/render/Tree.d.ts +9 -0
- package/dist/types/render/TreeSelect.d.ts +4 -0
- package/dist/types/render/Upload.d.ts +4 -0
- package/dist/types/render/helper.d.ts +10 -0
- package/dist/types/render/index.d.ts +43 -0
- package/dist/types/render/registry.d.ts +9 -0
- package/dist/types/render/state.d.ts +19 -0
- package/dist/types/render/types.d.ts +435 -0
- package/dist/types/utils/attrMapping.d.ts +26 -0
- package/package.json +10 -12
- package/src/BaseSearch/index.vue +371 -0
- package/src/BaseTable/index.vue +62 -36
- package/src/Form/CoreForm.vue +782 -0
- package/src/Form/types.ts +86 -0
- package/src/SSelectPage/index.vue +607 -0
- package/src/index.ts +15 -1
- package/src/{BaseTable/renderers → render}/AreaCascader.tsx +3 -3
- package/src/{BaseTable/renderers → render}/AutoComplete.tsx +3 -3
- package/src/{BaseTable/renderers → render}/Button.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Cascader.tsx +3 -3
- package/src/{BaseTable/renderers → render}/Checkbox.tsx +2 -2
- package/src/{BaseTable/renderers → render}/CheckboxGroup.tsx +2 -2
- package/src/render/Custom.tsx +19 -0
- package/src/{BaseTable/renderers → render}/DatePicker.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Input.tsx +3 -3
- package/src/{BaseTable/renderers → render}/InputGroup.tsx +3 -3
- package/src/{BaseTable/renderers → render}/InputNumber.tsx +3 -3
- package/src/{BaseTable/renderers → render}/InputPassword.tsx +3 -3
- package/src/render/InputRange.tsx +154 -0
- package/src/{BaseTable/renderers → render}/RadioGroup.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Select.tsx +2 -2
- package/src/{BaseTable/renderers → render}/SselectPage.tsx +3 -3
- package/src/{BaseTable/renderers → render}/Switch.tsx +2 -2
- package/src/render/Tree.tsx +136 -0
- package/src/{BaseTable/renderers → render}/TreeSelect.tsx +2 -2
- package/src/{BaseTable/renderers → render}/Upload.tsx +4 -5
- package/src/{BaseTable/utils.tsx → render/helper.tsx} +86 -9
- package/src/{BaseTable/renderers → render}/index.ts +45 -4
- package/src/{BaseTable → render}/types.ts +62 -2
- package/src/utils/attrMapping.ts +106 -0
- package/vite.config.ts +15 -2
- package/index.css +0 -2
- package/index.ts +0 -21
- package/src/BaseTable/helpers.tsx +0 -91
- /package/src/{BaseTable → render}/registry.ts +0 -0
- /package/src/{BaseTable → render}/state.ts +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { Button, Popover } from '@antsoo-lib/components'
|
|
3
|
+
import type { AnyObject } from '@antsoo-lib/shared'
|
|
4
|
+
|
|
5
|
+
import { computed, nextTick, ref } from 'vue'
|
|
6
|
+
import type { Component } from 'vue'
|
|
7
|
+
|
|
8
|
+
import CoreForm from '../Form/CoreForm.vue'
|
|
9
|
+
import type { FormField } from '../Form/types'
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
searchFields?: FormField[]
|
|
13
|
+
labelWidth?: number
|
|
14
|
+
actionsSpan?: number
|
|
15
|
+
gutter?: number
|
|
16
|
+
// 保持内边距
|
|
17
|
+
padding?: boolean
|
|
18
|
+
// 外部(主搜索区)BaseForm 组件
|
|
19
|
+
outerBaseForm?: Component
|
|
20
|
+
// 内部(更多筛选区)BaseForm 组件
|
|
21
|
+
innerBaseForm?: Component
|
|
22
|
+
// 更多筛选区表单的配置项(如 labelWidth, gutter 等),如果不传则使用默认值
|
|
23
|
+
innerBaseFormProps?: AnyObject
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
27
|
+
searchFields: () => [],
|
|
28
|
+
labelWidth: 112,
|
|
29
|
+
actionsSpan: 8,
|
|
30
|
+
gutter: 0,
|
|
31
|
+
padding: true,
|
|
32
|
+
outerBaseForm: () => CoreForm,
|
|
33
|
+
innerBaseForm: undefined,
|
|
34
|
+
innerBaseFormProps: () => ({}),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// 定义 emits
|
|
38
|
+
const emit = defineEmits<{
|
|
39
|
+
search: [values: AnyObject]
|
|
40
|
+
reset: [values: AnyObject]
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
// 使用 ref 而不是 reactive
|
|
44
|
+
const formData = ref<AnyObject>({})
|
|
45
|
+
// 弱类型定义,因为传入的 baseForm 可能不同,但必须实现 BaseForm 的接口
|
|
46
|
+
const baseFormRef = ref<any>()
|
|
47
|
+
const baseFormRef2 = ref<any>()
|
|
48
|
+
|
|
49
|
+
// 新增:表单重置 key,用于强制重新渲染
|
|
50
|
+
const formKey = ref(0)
|
|
51
|
+
|
|
52
|
+
// 更多筛选区域使用的 Form 组件:如果提供了 innerBaseForm 则使用它,否则回退到 outerBaseForm
|
|
53
|
+
const resolvedInnerBaseForm = computed(() => props.innerBaseForm || props.outerBaseForm)
|
|
54
|
+
|
|
55
|
+
const fields = computed(() => {
|
|
56
|
+
return props.searchFields.map((field) => {
|
|
57
|
+
return {
|
|
58
|
+
...field,
|
|
59
|
+
span: props.actionsSpan,
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// 计算首行是否能容纳按钮区域(24 栅格)
|
|
65
|
+
const canInlineActions = computed(() => {
|
|
66
|
+
let used = 0
|
|
67
|
+
for (const field of fields.value) {
|
|
68
|
+
const s = field.span ?? 24
|
|
69
|
+
if (used + s > 24) break
|
|
70
|
+
used += s
|
|
71
|
+
}
|
|
72
|
+
return used + props.actionsSpan <= 24
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// 新增:可见/隐藏筛选项拆分(24 栅格一行)
|
|
76
|
+
const visibleFields = computed<FormField[]>(() => {
|
|
77
|
+
const limit = 24 - (canInlineActions.value ? props.actionsSpan : 0)
|
|
78
|
+
const result: FormField[] = []
|
|
79
|
+
let used = 0
|
|
80
|
+
for (const f of fields.value) {
|
|
81
|
+
const s = f.span ?? 24
|
|
82
|
+
if (used + s > limit) break
|
|
83
|
+
result.push(f)
|
|
84
|
+
used += s
|
|
85
|
+
}
|
|
86
|
+
return result
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const hiddenFields = computed<FormField[]>(() => {
|
|
90
|
+
const visibleCount = visibleFields.value.length
|
|
91
|
+
return fields.value.slice(visibleCount).map((f) => ({ ...f, span: 12 }))
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const showMore = computed(() => hiddenFields.value.length > 0)
|
|
95
|
+
|
|
96
|
+
// 初始写入所有字段的默认值(无论 Popover 是否展开)
|
|
97
|
+
const initAllFieldDefaults = () => {
|
|
98
|
+
const initial: AnyObject = {}
|
|
99
|
+
props.searchFields.forEach((f) => {
|
|
100
|
+
let val = f.props?.value
|
|
101
|
+
|
|
102
|
+
// 尝试格式化 dayjs 对象
|
|
103
|
+
const format = f.props?.valueFormat
|
|
104
|
+
const formatVal = (v: any) =>
|
|
105
|
+
format && v && typeof v.format === 'function' ? v.format(format) : v
|
|
106
|
+
|
|
107
|
+
if (val !== undefined) {
|
|
108
|
+
if (Array.isArray(val)) {
|
|
109
|
+
val = val.map(formatVal).filter((v) => v !== null && v !== undefined && v !== '')
|
|
110
|
+
} else {
|
|
111
|
+
val = formatVal(val)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (f.key) {
|
|
116
|
+
if (val !== undefined && (Array.isArray(val) ? val.length > 0 : true)) {
|
|
117
|
+
initial[f.key] = val
|
|
118
|
+
} else if (
|
|
119
|
+
(f.type === 'select' || f.type === 'sselectPage') &&
|
|
120
|
+
(f.props?.mode === 'multiple' || f.props?.mode === 'tags')
|
|
121
|
+
) {
|
|
122
|
+
// 多选/标签模式默认初始化为 [],避免出现空标签
|
|
123
|
+
initial[f.key] = []
|
|
124
|
+
} else if (f.type === 'checkboxGroup' || f.type === 'upload') {
|
|
125
|
+
initial[f.key] = []
|
|
126
|
+
} else if (f.type === 'treeSelect' && (f.props?.multiple || f.props?.treeCheckable)) {
|
|
127
|
+
initial[f.key] = []
|
|
128
|
+
} else {
|
|
129
|
+
initial[f.key] = null
|
|
130
|
+
}
|
|
131
|
+
} else if (Array.isArray(f.attr)) {
|
|
132
|
+
// 处理 attr 映射(主要用于 datePicker range 模式)
|
|
133
|
+
f.attr.forEach((targetKey: string, idx: number) => {
|
|
134
|
+
if (!targetKey) return
|
|
135
|
+
initial[targetKey] = Array.isArray(val) ? val[idx] : val
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
// 直接替换整个对象以确保响应性
|
|
140
|
+
formData.value = { ...initial }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 重置处理
|
|
144
|
+
const handleReset = async () => {
|
|
145
|
+
// 重新初始化所有字段
|
|
146
|
+
initAllFieldDefaults()
|
|
147
|
+
|
|
148
|
+
// 通过改变 key 强制重新渲染表单组件
|
|
149
|
+
formKey.value += 1
|
|
150
|
+
|
|
151
|
+
// 等待 UI 同步
|
|
152
|
+
await nextTick()
|
|
153
|
+
|
|
154
|
+
// 清除校验状态
|
|
155
|
+
baseFormRef.value?.clearValidate()
|
|
156
|
+
baseFormRef2.value?.clearValidate?.()
|
|
157
|
+
|
|
158
|
+
emit('reset', { ...formData.value })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 组件加载时执行一次初始化
|
|
162
|
+
initAllFieldDefaults()
|
|
163
|
+
|
|
164
|
+
// 获取搜索参数 —— 统一返回 formData(包含可见与隐藏字段)
|
|
165
|
+
const getSearchParams = () => {
|
|
166
|
+
const params = { ...formData.value }
|
|
167
|
+
// 遍历参数,清理无效值
|
|
168
|
+
Object.keys(params).forEach((key) => {
|
|
169
|
+
const val = params[key]
|
|
170
|
+
if (val === '') {
|
|
171
|
+
params[key] = null
|
|
172
|
+
} else if (Array.isArray(val)) {
|
|
173
|
+
// 过滤数组中的空值(如 null, undefined, 空字符串)
|
|
174
|
+
const filtered = val.filter((v) => v !== null && v !== undefined && v !== '')
|
|
175
|
+
// 如果过滤后数组为空,则设为 null,避免传递 [] 给后端可能导致的问题
|
|
176
|
+
params[key] = filtered.length > 0 ? filtered : null
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
return params
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 搜索处理 —— 统一从 formData 取值
|
|
183
|
+
const handleSearch = async () => {
|
|
184
|
+
emit('search', getSearchParams())
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const setFormValue = (values: AnyObject, key?: string[]) => {
|
|
188
|
+
// 1. 同步更新内部数据源,确保 getSearchParams 获取到最新值
|
|
189
|
+
if (key && key.length > 0) {
|
|
190
|
+
key.forEach((k) => {
|
|
191
|
+
if (values[k] !== undefined) {
|
|
192
|
+
formData.value[k] = values[k]
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
} else {
|
|
196
|
+
// 如果没有指定 key,则合并所有 values
|
|
197
|
+
Object.assign(formData.value, values)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 2. 更新表单 UI
|
|
201
|
+
baseFormRef.value?.setFormValue(values, key)
|
|
202
|
+
baseFormRef2.value?.setFormValue(values, key)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 暴露方法给父组件
|
|
206
|
+
defineExpose({
|
|
207
|
+
getSearchParams,
|
|
208
|
+
initAllFieldDefaults,
|
|
209
|
+
setFormValue,
|
|
210
|
+
})
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<template>
|
|
214
|
+
<div class="base-search-container" :class="{ 'is-pd': padding }">
|
|
215
|
+
<component
|
|
216
|
+
:is="outerBaseForm"
|
|
217
|
+
ref="baseFormRef"
|
|
218
|
+
:key="`base-form-${formKey}`"
|
|
219
|
+
v-model:value="formData"
|
|
220
|
+
:fields="visibleFields"
|
|
221
|
+
label-align="right"
|
|
222
|
+
:label-width="labelWidth"
|
|
223
|
+
:inline-actions="canInlineActions"
|
|
224
|
+
:actions-span="actionsSpan"
|
|
225
|
+
:gutter="gutter"
|
|
226
|
+
>
|
|
227
|
+
<template v-if="canInlineActions" #search>
|
|
228
|
+
<div class="base-search-btn-container inline">
|
|
229
|
+
<div class="reset-btn" @click="handleReset">
|
|
230
|
+
<slot name="reset-icon">
|
|
231
|
+
<!-- 默认重置图标(使用 CSS 绘制或 SVG) -->
|
|
232
|
+
<span class="default-reset-icon">↺</span>
|
|
233
|
+
</slot>
|
|
234
|
+
</div>
|
|
235
|
+
<Button type="primary" @click="handleSearch"> 搜索 </Button>
|
|
236
|
+
</div>
|
|
237
|
+
</template>
|
|
238
|
+
</component>
|
|
239
|
+
|
|
240
|
+
<div v-if="!canInlineActions" class="base-search-btn-container">
|
|
241
|
+
<div v-show="showMore">
|
|
242
|
+
<Popover
|
|
243
|
+
v-if="showMore"
|
|
244
|
+
trigger="click"
|
|
245
|
+
placement="bottomLeft"
|
|
246
|
+
overlay-class-name="base-search-popover"
|
|
247
|
+
>
|
|
248
|
+
<template #content>
|
|
249
|
+
<div
|
|
250
|
+
class="popover-form-wrap"
|
|
251
|
+
:style="{ width: '550px', maxHeight: '60vh', overflowY: 'auto' }"
|
|
252
|
+
>
|
|
253
|
+
<component
|
|
254
|
+
:is="resolvedInnerBaseForm"
|
|
255
|
+
ref="baseFormRef2"
|
|
256
|
+
:key="`base-form-2-${formKey}`"
|
|
257
|
+
v-model:value="formData"
|
|
258
|
+
:fields="hiddenFields"
|
|
259
|
+
label-align="right"
|
|
260
|
+
label-position="vertical"
|
|
261
|
+
:gutter="10"
|
|
262
|
+
:label-width="200"
|
|
263
|
+
v-bind="innerBaseFormProps"
|
|
264
|
+
/>
|
|
265
|
+
</div>
|
|
266
|
+
</template>
|
|
267
|
+
<div class="more-btn">
|
|
268
|
+
<slot name="more-icon">
|
|
269
|
+
<!-- 默认更多图标 -->
|
|
270
|
+
<span class="default-more-icon">...</span>
|
|
271
|
+
</slot>
|
|
272
|
+
</div>
|
|
273
|
+
</Popover>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div class="reset-btn" @click="handleReset">
|
|
277
|
+
<slot name="reset-icon">
|
|
278
|
+
<span class="default-reset-icon">↺</span>
|
|
279
|
+
</slot>
|
|
280
|
+
</div>
|
|
281
|
+
<Button type="primary" @click="handleSearch"> 搜索 </Button>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</template>
|
|
285
|
+
|
|
286
|
+
<style lang="scss" scoped>
|
|
287
|
+
.base-search-container {
|
|
288
|
+
width: 100%;
|
|
289
|
+
border-radius: 8px;
|
|
290
|
+
background-color: #fff;
|
|
291
|
+
display: flex;
|
|
292
|
+
gap: 18px;
|
|
293
|
+
|
|
294
|
+
.base-search-btn-container {
|
|
295
|
+
display: flex;
|
|
296
|
+
gap: 12px;
|
|
297
|
+
flex-shrink: 0;
|
|
298
|
+
|
|
299
|
+
.reset-btn,
|
|
300
|
+
.more-btn {
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
justify-content: center;
|
|
304
|
+
width: 32px;
|
|
305
|
+
height: 32px;
|
|
306
|
+
border-radius: 4px;
|
|
307
|
+
border: 1px solid #dedee6;
|
|
308
|
+
box-sizing: border-box;
|
|
309
|
+
|
|
310
|
+
:deep(img),
|
|
311
|
+
:deep(svg) {
|
|
312
|
+
width: 20px;
|
|
313
|
+
height: 20px;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
&:hover {
|
|
317
|
+
cursor: pointer;
|
|
318
|
+
border-color: rgba(0, 179, 148, 1);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
&.inline {
|
|
323
|
+
margin-left: 12px;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.is-pd {
|
|
329
|
+
padding: 16px;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
:deep(.form-container) {
|
|
333
|
+
padding: 0;
|
|
334
|
+
border-radius: 0px;
|
|
335
|
+
|
|
336
|
+
.ant-form {
|
|
337
|
+
.ant-row {
|
|
338
|
+
row-gap: 10px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.ant-col {
|
|
342
|
+
.ant-form-item {
|
|
343
|
+
margin-bottom: 0px;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.popover-form-wrap {
|
|
350
|
+
max-width: 100%;
|
|
351
|
+
|
|
352
|
+
:deep(.form-container) {
|
|
353
|
+
.ant-form {
|
|
354
|
+
padding: 0 5px;
|
|
355
|
+
|
|
356
|
+
.ant-row {
|
|
357
|
+
.ant-form-item {
|
|
358
|
+
.ant-form-item-label {
|
|
359
|
+
line-height: normal;
|
|
360
|
+
padding: 0;
|
|
361
|
+
|
|
362
|
+
label {
|
|
363
|
+
line-height: normal;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
</style>
|
package/src/BaseTable/index.vue
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { Tooltip } from '@antsoo-lib/components'
|
|
2
|
+
import { Button as AButton, Pagination, Popover, Tooltip } from '@antsoo-lib/components'
|
|
3
3
|
import { QuestionCircleOutlined } from '@antsoo-lib/icons'
|
|
4
4
|
import type { AnyObject } from '@antsoo-lib/shared'
|
|
5
5
|
import { PRECISION } from '@antsoo-lib/shared'
|
|
6
|
-
import { env
|
|
6
|
+
import type { env as _env } from '@antsoo-lib/utils'
|
|
7
|
+
import { generateUniqueId, isNaN } from '@antsoo-lib/utils'
|
|
7
8
|
import { cloneDeep } from 'lodash-es'
|
|
8
9
|
import type { VxeComponentSizeType } from 'vxe-pc-ui'
|
|
9
10
|
import type { VxeGridPropTypes, VxeGridProps } from 'vxe-table'
|
|
10
11
|
|
|
11
12
|
import { computed, ref } from 'vue'
|
|
12
13
|
|
|
13
|
-
import { renderToolbar
|
|
14
|
-
import
|
|
14
|
+
import { renderToolbar } from '../render/helper'
|
|
15
|
+
import { useToolbarState } from '../render/state'
|
|
16
|
+
import type { ToolbarConfig } from '../render/types'
|
|
15
17
|
|
|
16
18
|
// Props默认值
|
|
17
19
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -29,6 +31,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
29
31
|
rToolBarCount: 4,
|
|
30
32
|
permissions: () => [],
|
|
31
33
|
getCommonCodeOptions: undefined,
|
|
34
|
+
hoverColor: '#E3FCF7',
|
|
32
35
|
})
|
|
33
36
|
// 抛出Emit方法(类型化)
|
|
34
37
|
const emit = defineEmits<{
|
|
@@ -42,7 +45,7 @@ const emit = defineEmits<{
|
|
|
42
45
|
(e: 'radioChangeEvent', params: { row: any; rowIndex: number; records: any }): void
|
|
43
46
|
(e: 'buttonClick', params: { button: Button; row: any; grid: any }): void
|
|
44
47
|
}>()
|
|
45
|
-
const { isDev, isTest } = env
|
|
48
|
+
const { isDev, isTest } = props.env || {}
|
|
46
49
|
const showHelpText = isDev || isTest
|
|
47
50
|
|
|
48
51
|
// Interface
|
|
@@ -127,6 +130,10 @@ interface Props {
|
|
|
127
130
|
permissions?: string[]
|
|
128
131
|
// Decoupled: Function to get common code options
|
|
129
132
|
getCommonCodeOptions?: (type: string) => Array<{ code: string; name: string }>
|
|
133
|
+
// 悬浮背景颜色
|
|
134
|
+
hoverColor?: string | null
|
|
135
|
+
// 环境变量
|
|
136
|
+
env?: ReturnType<typeof _env>
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
// table 实例
|
|
@@ -160,7 +167,6 @@ const actionColumn = computed(() => {
|
|
|
160
167
|
// 预过滤拥有权限的按钮,减少每行的计算量
|
|
161
168
|
const permittedButtons = computed(() => {
|
|
162
169
|
const all = actionColumn.value?.buttons || []
|
|
163
|
-
// Decoupled: Use props.permissions
|
|
164
170
|
return all.filter((b) => !b.permission || props.permissions.includes(b.permission))
|
|
165
171
|
})
|
|
166
172
|
|
|
@@ -339,7 +345,16 @@ function handleRowButtonClick(button: any, row: any) {
|
|
|
339
345
|
*/
|
|
340
346
|
function goToPage(page: number) {
|
|
341
347
|
if (paginationRef.value && page >= 1) {
|
|
342
|
-
onChange(page,
|
|
348
|
+
onChange(page, paginationPageSize.value)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 重置分页到第一页
|
|
354
|
+
*/
|
|
355
|
+
function resetPageNumber(): void {
|
|
356
|
+
if (paginationRef.value) {
|
|
357
|
+
onChange(1, paginationPageSize.value)
|
|
343
358
|
}
|
|
344
359
|
}
|
|
345
360
|
|
|
@@ -452,8 +467,15 @@ function handleGridColumns() {
|
|
|
452
467
|
})
|
|
453
468
|
}
|
|
454
469
|
// 递归处理列配置
|
|
455
|
-
const processColumn = (column: Column): any => {
|
|
470
|
+
const processColumn = (column: Column, index: number): any => {
|
|
456
471
|
const { buttons, ...col } = column
|
|
472
|
+
// 仅当缺少 field 且不是特殊类型列时,自动补充唯一 field
|
|
473
|
+
// 避免覆盖已有配置,且不仅限于“操作”列
|
|
474
|
+
if (!col.field && !col.type) {
|
|
475
|
+
// 如果是操作列,优先用 'action'(更具语义),否则用索引生成唯一ID
|
|
476
|
+
col.field = col.title === '操作' ? 'action' : `col_${index}`
|
|
477
|
+
}
|
|
478
|
+
|
|
457
479
|
// 选择器居中时,若启用了溢出选项,会引起3px的误差
|
|
458
480
|
if (col.type === 'checkbox' || col.type === 'radio') {
|
|
459
481
|
return {
|
|
@@ -573,7 +595,7 @@ function handleGridColumns() {
|
|
|
573
595
|
|
|
574
596
|
// 返回处理数据
|
|
575
597
|
return columns.map((column: Column, idx: number) => {
|
|
576
|
-
const baseColumn = processColumn(column)
|
|
598
|
+
const baseColumn = processColumn(column, idx)
|
|
577
599
|
|
|
578
600
|
if (idx === firstNonTypeIndex && props.tree) {
|
|
579
601
|
baseColumn.treeNode = true
|
|
@@ -643,6 +665,13 @@ function getSelectedRecords() {
|
|
|
643
665
|
return null
|
|
644
666
|
}
|
|
645
667
|
|
|
668
|
+
/**
|
|
669
|
+
* 获取表格实例
|
|
670
|
+
*/
|
|
671
|
+
function getGrid(): unknown {
|
|
672
|
+
return basetable.value
|
|
673
|
+
}
|
|
674
|
+
|
|
646
675
|
/**
|
|
647
676
|
* 递归查找列配置
|
|
648
677
|
* @param columns 列数组
|
|
@@ -689,6 +718,8 @@ defineExpose({
|
|
|
689
718
|
getLToolBarValues: () => lToolBarState.getAllValues(),
|
|
690
719
|
getRToolBarValues: () => rToolBarState.getAllValues(),
|
|
691
720
|
goToPage,
|
|
721
|
+
resetPageNumber,
|
|
722
|
+
getGrid,
|
|
692
723
|
lToolBarState,
|
|
693
724
|
rToolBarState,
|
|
694
725
|
getSelectedRecords,
|
|
@@ -701,9 +732,9 @@ defineExpose({
|
|
|
701
732
|
v-bind="options"
|
|
702
733
|
:footer-data="footerRenderData"
|
|
703
734
|
:style="{
|
|
704
|
-
'--vxe-ui-table-row-hover-background-color':
|
|
705
|
-
'--vxe-ui-table-column-hover-background-color':
|
|
706
|
-
'--vxe-ui-table-row-hover-striped-background-color':
|
|
735
|
+
'--vxe-ui-table-row-hover-background-color': hoverColor,
|
|
736
|
+
'--vxe-ui-table-column-hover-background-color': hoverColor,
|
|
737
|
+
'--vxe-ui-table-row-hover-striped-background-color': hoverColor,
|
|
707
738
|
}"
|
|
708
739
|
@cell-click="handleCellClick"
|
|
709
740
|
@checkbox-all="selectAllChangeEvent"
|
|
@@ -739,12 +770,12 @@ defineExpose({
|
|
|
739
770
|
:disabled="button.disabled?.(row, basetable)"
|
|
740
771
|
:loading="isButtonLoading(row, button)"
|
|
741
772
|
v-bind="button.props"
|
|
742
|
-
@click="handleRowButtonClick(button, row)"
|
|
773
|
+
@click.stop="handleRowButtonClick(button, row)"
|
|
743
774
|
>
|
|
744
775
|
{{ typeof button.name === 'function' ? button.name(row) : button.name }}
|
|
745
776
|
</AButton>
|
|
746
777
|
</template>
|
|
747
|
-
<
|
|
778
|
+
<Popover v-if="getVisibleButtons(row).length > 3" placement="bottomRight">
|
|
748
779
|
<template #content>
|
|
749
780
|
<template v-for="button in getVisibleButtons(row).slice(2)" :key="button.name">
|
|
750
781
|
<AButton
|
|
@@ -754,20 +785,20 @@ defineExpose({
|
|
|
754
785
|
:disabled="button.disabled?.(row, basetable)"
|
|
755
786
|
:loading="isButtonLoading(row, button)"
|
|
756
787
|
v-bind="button.props"
|
|
757
|
-
@click="handleRowButtonClick(button, row)"
|
|
788
|
+
@click.stop="handleRowButtonClick(button, row)"
|
|
758
789
|
>
|
|
759
790
|
{{ typeof button.name === 'function' ? button.name(row) : button.name }}
|
|
760
791
|
</AButton>
|
|
761
792
|
</template>
|
|
762
793
|
</template>
|
|
763
794
|
<AButton type="link" size="small"> 更多 </AButton>
|
|
764
|
-
</
|
|
795
|
+
</Popover>
|
|
765
796
|
</div>
|
|
766
797
|
</template>
|
|
767
798
|
<!-- 分页器 -->
|
|
768
799
|
<template v-if="pager" #pager>
|
|
769
800
|
<div class="base-table-pager">
|
|
770
|
-
<
|
|
801
|
+
<Pagination
|
|
771
802
|
ref="paginationRef"
|
|
772
803
|
size="small"
|
|
773
804
|
:total="total"
|
|
@@ -797,25 +828,25 @@ defineExpose({
|
|
|
797
828
|
cursor: help;
|
|
798
829
|
}
|
|
799
830
|
|
|
800
|
-
:
|
|
831
|
+
:global(.base-table-toolbar) {
|
|
801
832
|
padding-top: 0;
|
|
802
833
|
padding-bottom: 0;
|
|
834
|
+
}
|
|
803
835
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
836
|
+
:global(.base-table-toolbar .base-table-toolbar-container) {
|
|
837
|
+
display: flex;
|
|
838
|
+
align-items: center;
|
|
839
|
+
gap: 12px;
|
|
840
|
+
margin-bottom: 12px;
|
|
841
|
+
}
|
|
810
842
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
843
|
+
:global(.base-table-toolbar .vxe-tools--operate) {
|
|
844
|
+
margin-bottom: 12px;
|
|
845
|
+
}
|
|
814
846
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
}
|
|
847
|
+
:global(.base-table-toolbar .vxe-button--dropdown.size--mini),
|
|
848
|
+
:global(.base-table-toolbar .vxe-button.type--button.size--mini) {
|
|
849
|
+
margin: 0 0 0 12px;
|
|
819
850
|
}
|
|
820
851
|
|
|
821
852
|
.base-table-buttons-container {
|
|
@@ -873,9 +904,4 @@ defineExpose({
|
|
|
873
904
|
color: #1288fc;
|
|
874
905
|
cursor: pointer;
|
|
875
906
|
}
|
|
876
|
-
|
|
877
|
-
/* 斑马纹行悬浮背景颜色 */
|
|
878
|
-
:deep(.vxe-table--body .vxe-body--row.row--stripe.row--hover) {
|
|
879
|
-
background-color: red !important;
|
|
880
|
-
}
|
|
881
907
|
</style>
|