jobsys-newbie 2.2.0 → 2.2.2
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/components/pagination/NewbiePagination.jsx +272 -0
- package/components/pagination/index.js +5 -0
- package/components/pagination/index.less +0 -0
- package/components/provider/NewbieProvider.jsx +1 -0
- package/components/search/NewbieSearch.jsx +12 -4
- package/components/search/components/SearchItem.jsx +33 -18
- package/components/table/NewbieTable.jsx +12 -3
- package/dist/jobsys-newbie.cjs +18 -18
- package/dist/jobsys-newbie.cjs.map +1 -1
- package/dist/jobsys-newbie.js +2217 -2165
- package/dist/jobsys-newbie.js.map +1 -1
- package/package.json +1 -1
- package/playground/search/TestSearch.vue +26 -4
- package/playground/table/TestTable.vue +6 -3
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { defineComponent, inject, reactive, ref } from "vue"
|
|
2
|
+
import { isBoolean, isUndefined } from "lodash-es"
|
|
3
|
+
import { useCache, useFetch, useProcessStatusSuccess, useSm3 } from "../../hooks"
|
|
4
|
+
|
|
5
|
+
import "./index.less"
|
|
6
|
+
import NewbieSearch from "../search/NewbieSearch.jsx"
|
|
7
|
+
import { Divider } from "ant-design-vue"
|
|
8
|
+
import { NEWBIE_PAGINATION } from "../provider/NewbieProvider.jsx"
|
|
9
|
+
/**
|
|
10
|
+
* 分页组件
|
|
11
|
+
*
|
|
12
|
+
*
|
|
13
|
+
* @version 2.1.0
|
|
14
|
+
*/
|
|
15
|
+
export default defineComponent({
|
|
16
|
+
name: "NewbiePagination",
|
|
17
|
+
props: {
|
|
18
|
+
/**
|
|
19
|
+
* 是否使用分页,为 Object 时时使用自定义分页
|
|
20
|
+
*/
|
|
21
|
+
pagination: { type: [Boolean, Object], default: true },
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 原生翻页事件
|
|
25
|
+
*/
|
|
26
|
+
pageEvents: { type: Object, default: () => ({}) },
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 是否使用表单搜索
|
|
30
|
+
*/
|
|
31
|
+
filterable: { type: Boolean, default: true },
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 请求数据URL
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
url: { type: String, default: "" },
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 请求方式,默认为 GET
|
|
41
|
+
*
|
|
42
|
+
* @values get, post
|
|
43
|
+
*/
|
|
44
|
+
method: { type: String, default: "get" },
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 请求数据时额外提交的参数
|
|
48
|
+
*/
|
|
49
|
+
extraData: { type: Object, default: () => ({}) },
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 加载后数据的回调函数
|
|
53
|
+
*/
|
|
54
|
+
afterFetched: { type: Function, default: null },
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 数据为空文案
|
|
58
|
+
*/
|
|
59
|
+
emptyText: { type: String, default: "暂无内容" },
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 加载错误文案
|
|
63
|
+
*/
|
|
64
|
+
errorText: { type: String, default: "加载失败,点击重新加载" },
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 持久化,传入 localStorage 的 key,如果为 true, 将会以 URL Hash 为 key
|
|
68
|
+
*/
|
|
69
|
+
persistence: { type: [Boolean, String], default: false },
|
|
70
|
+
},
|
|
71
|
+
emits: [
|
|
72
|
+
/**
|
|
73
|
+
* 未传入 `url` 时的手动请求方法
|
|
74
|
+
*/
|
|
75
|
+
"fetch",
|
|
76
|
+
],
|
|
77
|
+
|
|
78
|
+
setup(props, { slots, emit, expose }) {
|
|
79
|
+
const searchRef = ref()
|
|
80
|
+
const footerElemRef = ref()
|
|
81
|
+
|
|
82
|
+
const paginationProvider = inject(NEWBIE_PAGINATION, () => {})
|
|
83
|
+
|
|
84
|
+
const genPersistenceKey = (prefix) => {
|
|
85
|
+
if (!props.persistence) {
|
|
86
|
+
return null
|
|
87
|
+
}
|
|
88
|
+
prefix = prefix || ""
|
|
89
|
+
if (isBoolean(props.persistence)) {
|
|
90
|
+
return `newbiePagination_${prefix}` + useSm3(location.href)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return `newbiePagination_${prefix}` + useSm3(location.pathname + "_" + props.persistence)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let persistencePagination = props.persistence ? useCache(genPersistenceKey()).get({}) : {}
|
|
97
|
+
|
|
98
|
+
const state = reactive({
|
|
99
|
+
pagination: {
|
|
100
|
+
// 翻页数据
|
|
101
|
+
totalSize: 0,
|
|
102
|
+
currentPage: persistencePagination.currentPage || 1,
|
|
103
|
+
pageSize: persistencePagination.pageSize || props.pagination?.pageSize || 10,
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
isLoading: { loading: false }, // 翻页Loading
|
|
107
|
+
|
|
108
|
+
items: [], // 页面数据
|
|
109
|
+
searchFormData: {}, // 搜索表单数据
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 持久化翻页与滚动
|
|
114
|
+
*/
|
|
115
|
+
const onPersistence = () => {
|
|
116
|
+
if (!props.persistence) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const data = {
|
|
121
|
+
...state.pagination,
|
|
122
|
+
}
|
|
123
|
+
useCache(genPersistenceKey()).set(data)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const getQueryData = () => {
|
|
127
|
+
let params = { ...state.searchFormData, ...props.extraData }
|
|
128
|
+
if (props.pagination) {
|
|
129
|
+
if (state.pagination.pageSize) {
|
|
130
|
+
params[NEWBIE_PAGINATION.pageSizeKey] = state.pagination.pageSize
|
|
131
|
+
}
|
|
132
|
+
params[NEWBIE_PAGINATION.pageKey] = state.pagination.currentPage
|
|
133
|
+
}
|
|
134
|
+
return params
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const setQueryData = (fields) => {
|
|
138
|
+
if (searchRef.value) {
|
|
139
|
+
searchRef.value?.setQueryForm(fields)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const fetchItems = async () => {
|
|
144
|
+
let data = {},
|
|
145
|
+
params = getQueryData()
|
|
146
|
+
|
|
147
|
+
const method = props.method
|
|
148
|
+
|
|
149
|
+
if (method === "get") {
|
|
150
|
+
data = { params }
|
|
151
|
+
} else if (method === "post") {
|
|
152
|
+
data = { ...params }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const res = await useFetch(state.isLoading)[method](props.url, data)
|
|
156
|
+
useProcessStatusSuccess(res, () => {
|
|
157
|
+
const fetched = props.afterFetched || paginationProvider.afterFetched
|
|
158
|
+
const result = fetched(res)
|
|
159
|
+
if (props.pagination) {
|
|
160
|
+
state.pagination.totalSize = result.totalSize
|
|
161
|
+
}
|
|
162
|
+
state.items = result.items
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 执行获取数据
|
|
168
|
+
* @param {boolean} refresh 是否刷新
|
|
169
|
+
* @return {*}
|
|
170
|
+
*/
|
|
171
|
+
const doFetch = async (refresh) => {
|
|
172
|
+
if (refresh === true) {
|
|
173
|
+
state.pagination.currentPage = 1
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (props.url) {
|
|
177
|
+
await fetchItems()
|
|
178
|
+
} else {
|
|
179
|
+
emit("fetch")
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
onPersistence()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const onSearch = (searchData) => {
|
|
186
|
+
state.searchFormData = searchData
|
|
187
|
+
doFetch(!searchData.persistence)
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 获取当前页的数据
|
|
191
|
+
* @returns {*[]}
|
|
192
|
+
*/
|
|
193
|
+
const getData = () => {
|
|
194
|
+
return [].concat(state.items)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 设置数据
|
|
199
|
+
* @param items
|
|
200
|
+
*/
|
|
201
|
+
const setData = (items) => {
|
|
202
|
+
state.items = items
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 设置翻页数据
|
|
207
|
+
* @param {int} total
|
|
208
|
+
* @param {int} currentPage
|
|
209
|
+
* @param {int} pageSize
|
|
210
|
+
*/
|
|
211
|
+
const setPagination = (total, currentPage, pageSize) => {
|
|
212
|
+
if (!isUndefined(total)) {
|
|
213
|
+
state.pagination.totalSize = total
|
|
214
|
+
}
|
|
215
|
+
if (!isUndefined(currentPage)) {
|
|
216
|
+
state.pagination.currentPage = currentPage
|
|
217
|
+
}
|
|
218
|
+
if (!isUndefined(pageSize)) {
|
|
219
|
+
state.pagination.pageSize = pageSize
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 获取翻页数据
|
|
225
|
+
* @returns {*}
|
|
226
|
+
*/
|
|
227
|
+
const getPagination = () => state.pagination
|
|
228
|
+
|
|
229
|
+
expose({
|
|
230
|
+
getData,
|
|
231
|
+
setData,
|
|
232
|
+
setPagination,
|
|
233
|
+
getPagination,
|
|
234
|
+
getQueryData,
|
|
235
|
+
setQueryData,
|
|
236
|
+
doFetch,
|
|
237
|
+
})
|
|
238
|
+
/********** render **********/
|
|
239
|
+
|
|
240
|
+
const prependElem = () => (slots.prepend ? <div class={"newbie-pagination-prepend-wrapper"}>{slots.prepend()}</div> : null)
|
|
241
|
+
|
|
242
|
+
const filterElem = () =>
|
|
243
|
+
props.filterable
|
|
244
|
+
? [
|
|
245
|
+
<NewbieSearch
|
|
246
|
+
ref={searchRef}
|
|
247
|
+
persistence={props.persistence}
|
|
248
|
+
filterableColumns={state.filterableColumns}
|
|
249
|
+
sortableColumns={state.sortableColumns}
|
|
250
|
+
onSearch={onSearch}
|
|
251
|
+
>
|
|
252
|
+
{{
|
|
253
|
+
...props.searchSlots,
|
|
254
|
+
}}
|
|
255
|
+
</NewbieSearch>,
|
|
256
|
+
<Divider></Divider>,
|
|
257
|
+
]
|
|
258
|
+
: null
|
|
259
|
+
|
|
260
|
+
const footerElem = () =>
|
|
261
|
+
slots.append || props.pagination ? (
|
|
262
|
+
<div ref={footerElemRef} class={`newbie-pagination-footer`}>
|
|
263
|
+
<div class={"newbie-pagination-append-wrapper"}>{slots.append ? slots.append() : null}</div>
|
|
264
|
+
<div class={"newbie-pagination-pagination-wrapper"}></div>
|
|
265
|
+
</div>
|
|
266
|
+
) : null
|
|
267
|
+
|
|
268
|
+
const contentElem = () => <div class={"newbie-pagination-content-wrapper"}>{slots.default?.()}</div>
|
|
269
|
+
|
|
270
|
+
return () => <div class={"newbie-pagination"}>{[prependElem(), filterElem(), contentElem(), footerElem()]}</div>
|
|
271
|
+
},
|
|
272
|
+
})
|
|
File without changes
|
|
@@ -7,6 +7,7 @@ export const NEWBIE_TABLE = Symbol("NEWBIE_TABLE")
|
|
|
7
7
|
export const NEWBIE_UPLOADER = Symbol("NEWBIE_UPLOADER")
|
|
8
8
|
export const NEWBIE_FORM = Symbol("NEWBIE_FORM")
|
|
9
9
|
export const NEWBIE_SEARCH = Symbol("NEWBIE_SEARCH")
|
|
10
|
+
export const NEWBIE_PAGINATION = Symbol("NEWBIE_PAGINATION")
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Newbie 配置组件
|
|
@@ -40,7 +40,8 @@ export default defineComponent({
|
|
|
40
40
|
* @property {Array} [conditions] 搜索项的条件,可选项为不同类型的内置条件
|
|
41
41
|
* @property {*} [defaultValue] 默认搜索值
|
|
42
42
|
* @property {*} [defaultCondition] 默认搜索条件
|
|
43
|
-
* @property {Function} [
|
|
43
|
+
* @property {Function} [customRender] 自定义渲染搜索项, 接收 { item, queryForm, context } 作为参数,返回一个对象,包含 conditions, Component, displayValue, onComponentOpen 四个属性
|
|
44
|
+
* @property {Function} [collectItem] 收集搜索项的内容]
|
|
44
45
|
* */
|
|
45
46
|
|
|
46
47
|
/**
|
|
@@ -80,7 +81,7 @@ export default defineComponent({
|
|
|
80
81
|
*/
|
|
81
82
|
"search",
|
|
82
83
|
],
|
|
83
|
-
setup(props, { expose, emit }) {
|
|
84
|
+
setup(props, { expose, emit, slots }) {
|
|
84
85
|
const searchProvider = inject(NEWBIE_SEARCH, () => {})
|
|
85
86
|
|
|
86
87
|
const state = reactive({
|
|
@@ -333,7 +334,7 @@ export default defineComponent({
|
|
|
333
334
|
state.searchLabels = []
|
|
334
335
|
|
|
335
336
|
concat(state.fieldColumns, state.expandColumns).forEach((item) => {
|
|
336
|
-
let { value, searchLabel } = item.collectItem ? item.collectItem() : {}
|
|
337
|
+
let { value, searchLabel } = item.collectItem ? item.collectItem({ queryForm: state.queryForm, item }) : {}
|
|
337
338
|
const { condition, type } = state.queryForm[item.key]
|
|
338
339
|
|
|
339
340
|
if (
|
|
@@ -369,7 +370,13 @@ export default defineComponent({
|
|
|
369
370
|
*/
|
|
370
371
|
const setQueryForm = (fields) => {
|
|
371
372
|
Object.keys(fields).forEach((key) => {
|
|
372
|
-
state.queryForm[key]
|
|
373
|
+
if (state.queryForm[key]) {
|
|
374
|
+
state.queryForm[key].value = fields[key]
|
|
375
|
+
} else {
|
|
376
|
+
state.queryForm[key] = {
|
|
377
|
+
value: fields[key],
|
|
378
|
+
}
|
|
379
|
+
}
|
|
373
380
|
})
|
|
374
381
|
}
|
|
375
382
|
|
|
@@ -398,6 +405,7 @@ export default defineComponent({
|
|
|
398
405
|
return state.fieldColumns.map((item) =>
|
|
399
406
|
createSearchItem(item, state.queryForm, searchState[item.key], {
|
|
400
407
|
props,
|
|
408
|
+
slots,
|
|
401
409
|
searchProvider,
|
|
402
410
|
}),
|
|
403
411
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { find } from "lodash-es"
|
|
1
|
+
import { find, isFunction } from "lodash-es"
|
|
2
2
|
import { computed, nextTick, ref, Transition, withModifiers } from "vue"
|
|
3
3
|
import { Dropdown, Menu, MenuItem, TypographyText } from "ant-design-vue"
|
|
4
4
|
import { DownOutlined } from "@ant-design/icons-vue"
|
|
@@ -16,29 +16,44 @@ import { useT } from "../../../hooks/index.js"
|
|
|
16
16
|
* @return {JSX.Element}
|
|
17
17
|
*/
|
|
18
18
|
const render = (item, queryForm, itemState, context) => {
|
|
19
|
-
const { searchProvider, props } = context
|
|
19
|
+
const { searchProvider, props, slots } = context
|
|
20
20
|
|
|
21
21
|
const panelRef = ref(null)
|
|
22
22
|
|
|
23
23
|
let field = {}
|
|
24
24
|
|
|
25
|
-
if (
|
|
26
|
-
field =
|
|
27
|
-
} else
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
if (item.customRender) {
|
|
26
|
+
field = item.customRender({ item, queryForm, context })
|
|
27
|
+
} else {
|
|
28
|
+
switch (item.type) {
|
|
29
|
+
case "number":
|
|
30
|
+
field = Fields.createNumber(item, queryForm, context)
|
|
31
|
+
break
|
|
32
|
+
case "select":
|
|
33
|
+
field = Fields.createSelect(item, queryForm, context)
|
|
34
|
+
break
|
|
35
|
+
case "textarea":
|
|
36
|
+
field = Fields.createTextarea(item, queryForm, context)
|
|
37
|
+
break
|
|
38
|
+
case "date":
|
|
39
|
+
field = Fields.createDate(item, queryForm, context)
|
|
40
|
+
break
|
|
41
|
+
case "cascade":
|
|
42
|
+
field = Fields.createCascader(item, queryForm, context)
|
|
43
|
+
break
|
|
44
|
+
case "switch":
|
|
45
|
+
field = Fields.createSwitch(item, queryForm, context)
|
|
46
|
+
break
|
|
47
|
+
default:
|
|
48
|
+
field = Fields.createInput(item, queryForm, context)
|
|
49
|
+
break
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (slots[item.key]) {
|
|
53
|
+
field.Component = slots[item.key]({ item, queryForm, context })
|
|
39
54
|
}
|
|
40
55
|
|
|
41
|
-
const { conditions, Component, displayValue, onComponentOpen } = field
|
|
56
|
+
const { conditions = [], Component, displayValue = "", onComponentOpen = null } = field
|
|
42
57
|
|
|
43
58
|
/**
|
|
44
59
|
* 选择条件
|
|
@@ -144,7 +159,7 @@ const render = (item, queryForm, itemState, context) => {
|
|
|
144
159
|
</Dropdown>
|
|
145
160
|
)}
|
|
146
161
|
</div>
|
|
147
|
-
<div class={"newbie-search-input-container"}>{Component}</div>
|
|
162
|
+
<div class={"newbie-search-input-container"}>{isFunction(Component) ? Component() : Component}</div>
|
|
148
163
|
</div>
|
|
149
164
|
) : null}
|
|
150
165
|
</Transition>
|
|
@@ -71,10 +71,15 @@ export default defineComponent({
|
|
|
71
71
|
tableEvents: { type: Object, default: () => ({}) },
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* 原生
|
|
74
|
+
* 原生 slots
|
|
75
75
|
*/
|
|
76
76
|
tableSlots: { type: Object, default: () => ({}) },
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* NewbieSearch slots
|
|
80
|
+
*/
|
|
81
|
+
searchSlots: { type: Object, default: () => ({}) },
|
|
82
|
+
|
|
78
83
|
/**
|
|
79
84
|
* 是否使用分页,为 Object 时时使用自定义分页
|
|
80
85
|
*/
|
|
@@ -682,7 +687,11 @@ export default defineComponent({
|
|
|
682
687
|
filterableColumns={state.filterableColumns}
|
|
683
688
|
sortableColumns={state.sortableColumns}
|
|
684
689
|
onSearch={onSearch}
|
|
685
|
-
|
|
690
|
+
>
|
|
691
|
+
{{
|
|
692
|
+
...props.searchSlots,
|
|
693
|
+
}}
|
|
694
|
+
</NewbieSearch>,
|
|
686
695
|
<Divider></Divider>,
|
|
687
696
|
]
|
|
688
697
|
: null
|
|
@@ -738,7 +747,7 @@ export default defineComponent({
|
|
|
738
747
|
showQuickJumper: true,
|
|
739
748
|
showSizeChanger: true,
|
|
740
749
|
showTotal: (total) => useT("table.total", { total }),
|
|
741
|
-
pageSizeOptions: ["10", "30", "50", "100"],
|
|
750
|
+
pageSizeOptions: ["10", "30", "50", "100", "300", "500"],
|
|
742
751
|
total: state.pagination.totalSize,
|
|
743
752
|
pageSize: state.pagination.pageSize,
|
|
744
753
|
current: state.pagination.currentPage,
|