@mythpe/quasar-ui-qui 0.1.19 → 0.1.21
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.
|
@@ -0,0 +1,945 @@
|
|
|
1
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
2
|
+
import { computed, nextTick, reactive, ref, toRef, toValue, useSlots } from 'vue'
|
|
3
|
+
import type {
|
|
4
|
+
ApiServiceParams,
|
|
5
|
+
FetchRowsArgs,
|
|
6
|
+
MDatatableFilterForm,
|
|
7
|
+
MDatatableMetaServer,
|
|
8
|
+
MDatatableOptions,
|
|
9
|
+
MDatatablePagination,
|
|
10
|
+
MDatatableProps, MDatatableScope,
|
|
11
|
+
MDtExportOptions,
|
|
12
|
+
MDtHeadersParameter,
|
|
13
|
+
MDtItem,
|
|
14
|
+
MDtItemIndex,
|
|
15
|
+
MDtMythApiServicesSchema
|
|
16
|
+
} from '../types'
|
|
17
|
+
import { useI18n } from 'vue-i18n'
|
|
18
|
+
import { useMyth } from './useMyth'
|
|
19
|
+
import { useQuasar } from 'quasar'
|
|
20
|
+
import lodash from 'lodash'
|
|
21
|
+
import { useRoute, useRouter } from 'vue-router'
|
|
22
|
+
import type { AxiosRequestConfig } from 'axios'
|
|
23
|
+
import { useResetForm, useSetFormValues } from 'vee-validate'
|
|
24
|
+
|
|
25
|
+
const initPaginationOptions: MDatatablePagination = {
|
|
26
|
+
sortBy: undefined,
|
|
27
|
+
descending: undefined,
|
|
28
|
+
page: 1,
|
|
29
|
+
rowsPerPage: 50,
|
|
30
|
+
rowsNumber: 0
|
|
31
|
+
}
|
|
32
|
+
export const initMetaServer: MDatatableMetaServer = {
|
|
33
|
+
current_page: null,
|
|
34
|
+
last_page: null,
|
|
35
|
+
total: null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const useDtHelpers = (options: MaybeRefOrGetter<MDatatableProps>) => {
|
|
39
|
+
const slots = useSlots()
|
|
40
|
+
const $q = useQuasar()
|
|
41
|
+
const router = useRouter()
|
|
42
|
+
const route = useRoute()
|
|
43
|
+
const { te } = useI18n({ useScope: 'global' })
|
|
44
|
+
const {
|
|
45
|
+
__,
|
|
46
|
+
props: pluginOptions,
|
|
47
|
+
parseHeaders,
|
|
48
|
+
pascalCase,
|
|
49
|
+
pluralize,
|
|
50
|
+
downloadFromResponse,
|
|
51
|
+
api,
|
|
52
|
+
alertError,
|
|
53
|
+
alertSuccess,
|
|
54
|
+
confirmMessage
|
|
55
|
+
} = useMyth()
|
|
56
|
+
const props = toValue(options)
|
|
57
|
+
const getRows = ref<MDtItem[]>([])
|
|
58
|
+
const defaultItem = computed(() => props.defaultItem)
|
|
59
|
+
const getTableTitle = computed(() => {
|
|
60
|
+
if (props.title) {
|
|
61
|
+
if (te(props.title)) {
|
|
62
|
+
return __(props.title)
|
|
63
|
+
}
|
|
64
|
+
return __('replace.index', { name: __(props.title) })
|
|
65
|
+
}
|
|
66
|
+
return undefined
|
|
67
|
+
})
|
|
68
|
+
const serviceName = computed(() => props.serviceName)
|
|
69
|
+
const exportToBlob = computed(() => {
|
|
70
|
+
if (props.exportUrl === undefined && pluginOptions.value.datatable?.exportUrl === undefined) {
|
|
71
|
+
return !0
|
|
72
|
+
}
|
|
73
|
+
const t = props.exportUrl === undefined ? pluginOptions.value.datatable?.exportUrl : props.exportUrl
|
|
74
|
+
if (t !== undefined) {
|
|
75
|
+
if (t.toString() === 'true' || t.toString() === '') {
|
|
76
|
+
return !1
|
|
77
|
+
}
|
|
78
|
+
if (t.toString() === 'pdf') {
|
|
79
|
+
return 'excel'
|
|
80
|
+
}
|
|
81
|
+
if (t.toString() === 'excel') {
|
|
82
|
+
return 'pdf'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return !0
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const filterDialogModel = ref(!1)
|
|
89
|
+
const showDialogModel = ref(!1)
|
|
90
|
+
const formDialogModel = ref(!1)
|
|
91
|
+
const isUpdateDialog = ref(!1)
|
|
92
|
+
const dialogItem = ref<MDtItem | undefined>(undefined)
|
|
93
|
+
const dialogItemIndex = ref<MDtItemIndex | undefined>()
|
|
94
|
+
const dialogErrors = ref<any>({})
|
|
95
|
+
const resetDialogs = () => {
|
|
96
|
+
filterDialogModel.value = !1
|
|
97
|
+
showDialogModel.value = !1
|
|
98
|
+
formDialogModel.value = !1
|
|
99
|
+
isUpdateDialog.value = !1
|
|
100
|
+
dialogItem.value = undefined
|
|
101
|
+
dialogItemIndex.value = undefined
|
|
102
|
+
dialogErrors.value = {}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const getHeaders = computed<any[]>(() => parseHeaders(props.headers as MDtHeadersParameter,
|
|
106
|
+
{ noSort: props.imageColumns }) || [])
|
|
107
|
+
const visibleHeaders = ref<string[]>([])
|
|
108
|
+
|
|
109
|
+
const contextmenu = ref(!1)
|
|
110
|
+
const selected = ref<MDtItem[]>([])
|
|
111
|
+
const onUpdateSelectedItems = () => {
|
|
112
|
+
if (contextmenu.value) {
|
|
113
|
+
contextmenu.value = !1
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const meta = ref<MDatatableMetaServer>({ ...initMetaServer })
|
|
118
|
+
const pagination = ref<MDatatablePagination>({ ...initPaginationOptions })
|
|
119
|
+
const search = ref<string | null>(null)
|
|
120
|
+
const searchColumnsRef = ref<string[]>([])
|
|
121
|
+
const searchPlaceholder = computed<string>(() => {
|
|
122
|
+
if (searchColumnsRef.value.length > 0) {
|
|
123
|
+
return __('myth.datatable.searchInputPlaceholder',
|
|
124
|
+
{
|
|
125
|
+
v: getHeaders.value.filter(e => e?.field !== props.controlKey && searchColumnsRef.value.indexOf(e.name) !== -1)
|
|
126
|
+
.map(e => e.label)
|
|
127
|
+
.join(', ')
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
return 'myth.datatable.searchInput'
|
|
131
|
+
})
|
|
132
|
+
const loading = ref<boolean>(!1)
|
|
133
|
+
const filterForm = ref<MDatatableFilterForm>({})
|
|
134
|
+
const tempFilterForm = ref<MDatatableFilterForm>({})
|
|
135
|
+
const hasAction = ref<boolean>(!1)
|
|
136
|
+
const fullscreen = ref(!1)
|
|
137
|
+
|
|
138
|
+
const hasAddBtn = computed<boolean>(() => {
|
|
139
|
+
if (props.hideAddBtn) {
|
|
140
|
+
return !1
|
|
141
|
+
}
|
|
142
|
+
return !!slots.form || !!props.storeRoute
|
|
143
|
+
})
|
|
144
|
+
const hasUpdateBtn = computed<boolean>(() => {
|
|
145
|
+
if (props.hideUpdateBtn) {
|
|
146
|
+
return !1
|
|
147
|
+
}
|
|
148
|
+
return (!!slots.form || !!props.updateRoute || props.updateQueryParams) as boolean
|
|
149
|
+
})
|
|
150
|
+
const hasCloneBtn = computed<boolean>(() => Boolean(props.cloneBtn))
|
|
151
|
+
const hasShowBtn = computed<boolean>(() => {
|
|
152
|
+
if (props.hideShowBtn) {
|
|
153
|
+
return !1
|
|
154
|
+
}
|
|
155
|
+
return (!!slots.show || !!props.showRoute || props.showQueryParams) as boolean
|
|
156
|
+
})
|
|
157
|
+
const hasDestroyBtn = computed<boolean>(() => !props.hideDestroyBtn)
|
|
158
|
+
const hasFilterDialog = computed<boolean>(() => !!slots.filter)
|
|
159
|
+
|
|
160
|
+
const isUpdateMode = ref<boolean>(!1)
|
|
161
|
+
const formMode = computed<'update' | 'store'>(() => isUpdateMode.value ? 'update' : 'store')
|
|
162
|
+
const isSingleSelectedItem = computed<boolean>(() => selected.value.length === 1)
|
|
163
|
+
const firstSelectedItem = computed<MDtItem>(() => selected.value[0] as MDtItem)
|
|
164
|
+
const hasSelectedItem = computed<boolean>(() => selected.value.length > 0)
|
|
165
|
+
const getShowTitle = computed(() => {
|
|
166
|
+
if (serviceName.value && typeof serviceName.value !== 'function') {
|
|
167
|
+
const c = pascalCase(pluralize(serviceName.value.split('/').pop()))
|
|
168
|
+
return __('replace.show_details', { name: __(`choice.${c}`, 1) })
|
|
169
|
+
}
|
|
170
|
+
return __('show_details')
|
|
171
|
+
})
|
|
172
|
+
const getFormTitle = computed(() => {
|
|
173
|
+
const name = serviceName.value && typeof serviceName.value !== 'function' ? __(`choice.${pascalCase(pluralize(
|
|
174
|
+
serviceName.value.split(
|
|
175
|
+
'/').pop()))}`, 1) : ''
|
|
176
|
+
return __(`replace.${formMode.value}`, { name })
|
|
177
|
+
})
|
|
178
|
+
const isGrid = computed(() => {
|
|
179
|
+
if (props.grid !== undefined) {
|
|
180
|
+
return props.grid
|
|
181
|
+
}
|
|
182
|
+
return $q.screen.lt.md
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const getMythApiServicesSchema = (): MDtMythApiServicesSchema => {
|
|
186
|
+
if (typeof serviceName.value === 'function') {
|
|
187
|
+
return serviceName.value() as MDtMythApiServicesSchema
|
|
188
|
+
}
|
|
189
|
+
const c = api.value.services[serviceName.value]
|
|
190
|
+
if (!c) {
|
|
191
|
+
throw Error(`No Service: ${serviceName.value}`)
|
|
192
|
+
}
|
|
193
|
+
return c
|
|
194
|
+
}
|
|
195
|
+
const updateSelectedItems = (items: MDtItem[]) => {
|
|
196
|
+
selected.value = items as any
|
|
197
|
+
}
|
|
198
|
+
const onScroll = ({ index, to }: any) => {
|
|
199
|
+
if (index && to && index === to) {
|
|
200
|
+
loadMore()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const loadMore = () => {
|
|
204
|
+
nextTick(() => {
|
|
205
|
+
fetchDatatableItems({
|
|
206
|
+
pagination: {
|
|
207
|
+
...pagination.value,
|
|
208
|
+
page: (pagination.value.page ?? 0) + 1
|
|
209
|
+
},
|
|
210
|
+
filter: search.value
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
const refreshNoUpdate = (done?: () => void) => {
|
|
215
|
+
if (contextmenu.value) {
|
|
216
|
+
contextmenu.value = !1
|
|
217
|
+
}
|
|
218
|
+
meta.value = { ...initMetaServer }
|
|
219
|
+
pagination.value.page = 1
|
|
220
|
+
pagination.value.rowsNumber = 0
|
|
221
|
+
getRows.value = []
|
|
222
|
+
nextTick(() => {
|
|
223
|
+
fetchDatatableItems({
|
|
224
|
+
pagination: pagination.value,
|
|
225
|
+
filter: search.value
|
|
226
|
+
})
|
|
227
|
+
if (done) {
|
|
228
|
+
done()
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
const refresh = (done?: () => void) => refreshNoUpdate(done)
|
|
233
|
+
const getRequestWith = (type: 'withIndex' | 'withShow' | 'withUpdate' | 'withStore'): string | null => {
|
|
234
|
+
let v: any = []
|
|
235
|
+
const params: { [k: string]: string } & string | (() => string | object) = props[type] as any
|
|
236
|
+
if (params) {
|
|
237
|
+
if (typeof params === 'string') {
|
|
238
|
+
v = params.split(',')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (lodash.isArray(params)) {
|
|
242
|
+
v = [...params]
|
|
243
|
+
} else if (lodash.isObject(params) && typeof params !== 'function') {
|
|
244
|
+
let e
|
|
245
|
+
for (const k in params) {
|
|
246
|
+
e = params[k]
|
|
247
|
+
v.push(`${k}=${e}`)
|
|
248
|
+
}
|
|
249
|
+
} else if (lodash.isFunction(params)) {
|
|
250
|
+
const f = params()
|
|
251
|
+
v = typeof f === 'string' ? f.split(',') : f
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return v.join(',') ?? null
|
|
255
|
+
}
|
|
256
|
+
const getDatatableParams = ({
|
|
257
|
+
pagination,
|
|
258
|
+
filter
|
|
259
|
+
}: FetchRowsArgs = {}, merge: Partial<ApiServiceParams> = {}): ApiServiceParams => {
|
|
260
|
+
const headerItems = getHeaders.value.map((e: any) => e.name).join(',')
|
|
261
|
+
// console.log(headerItems)
|
|
262
|
+
let params: ApiServiceParams = {
|
|
263
|
+
headerItems,
|
|
264
|
+
indexType: 'index',
|
|
265
|
+
fdt: 'i',
|
|
266
|
+
itemsPerPage: pagination?.rowsPerPage === 0 ? -1 : (pagination?.rowsPerPage !== undefined ? pagination?.rowsPerPage : 0),
|
|
267
|
+
page: pagination?.page !== undefined ? pagination.page : 0,
|
|
268
|
+
sortBy: pagination?.sortBy !== undefined ? pagination.sortBy : undefined,
|
|
269
|
+
sortDesc: !pagination?.sortBy ? undefined : (pagination?.descending === !0 ? 1 : (pagination?.descending === !1 ? 0 : undefined))
|
|
270
|
+
}
|
|
271
|
+
if (filter) {
|
|
272
|
+
params.search = filter
|
|
273
|
+
}
|
|
274
|
+
if (Object.keys(filterForm.value).length > 0) {
|
|
275
|
+
const TempFilter = { ...filterForm.value } as any
|
|
276
|
+
for (const fKey in TempFilter) {
|
|
277
|
+
if (lodash.isArray(TempFilter[fKey])) {
|
|
278
|
+
TempFilter[fKey] = TempFilter[fKey].map((elm: any) => {
|
|
279
|
+
if (elm.id) {
|
|
280
|
+
return elm.id
|
|
281
|
+
} else if (elm.value) {
|
|
282
|
+
return elm.value
|
|
283
|
+
}
|
|
284
|
+
return elm
|
|
285
|
+
})
|
|
286
|
+
} else if (lodash.isPlainObject(TempFilter[fKey])) {
|
|
287
|
+
if (TempFilter[fKey].id) {
|
|
288
|
+
TempFilter[fKey] = TempFilter[fKey].id
|
|
289
|
+
} else if (TempFilter[fKey].value) {
|
|
290
|
+
TempFilter[fKey] = TempFilter[fKey].value
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// console.log(JSON.stringify(filterForm.value))
|
|
295
|
+
// params.filter = filterForm.value
|
|
296
|
+
// console.log(TempFilter)
|
|
297
|
+
params.filter = TempFilter
|
|
298
|
+
}
|
|
299
|
+
if (searchColumnsRef.value.length > 0) {
|
|
300
|
+
params.searchColumns = searchColumnsRef.value.join(',')
|
|
301
|
+
}
|
|
302
|
+
if (props.requestParams) {
|
|
303
|
+
if (typeof props.requestParams === 'function') {
|
|
304
|
+
params = props.requestParams(params) as ApiServiceParams
|
|
305
|
+
} else {
|
|
306
|
+
params = {
|
|
307
|
+
...params,
|
|
308
|
+
...props.requestParams
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return { ...params, ...merge }
|
|
313
|
+
}
|
|
314
|
+
const fetchDatatableItems = (opts: FetchRowsArgs = {}) => {
|
|
315
|
+
if (props.endReach && meta.value.last_page && pagination.value.page >= meta.value.last_page) {
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
if (loading.value || !serviceName.value) return
|
|
319
|
+
loading.value = !0
|
|
320
|
+
selected.value = []
|
|
321
|
+
nextTick(() => {
|
|
322
|
+
const params = getDatatableParams(opts)
|
|
323
|
+
const requestWith = getRequestWith('withIndex')
|
|
324
|
+
if (requestWith) {
|
|
325
|
+
params.requestWith = requestWith
|
|
326
|
+
}
|
|
327
|
+
// console.log({ params })
|
|
328
|
+
getMythApiServicesSchema().index({ params })
|
|
329
|
+
.then((result: any) => {
|
|
330
|
+
const { _data, _meta } = result
|
|
331
|
+
pagination.value = {
|
|
332
|
+
page: parseInt((_meta?.current_page || 1).toString()) || 1,
|
|
333
|
+
rowsPerPage: parseInt(_meta?.per_page) || 0,
|
|
334
|
+
rowsNumber: parseInt((_meta?.total || 0).toString()) || 0,
|
|
335
|
+
sortBy: opts?.pagination?.sortBy || undefined,
|
|
336
|
+
descending: opts?.pagination?.descending || undefined
|
|
337
|
+
}
|
|
338
|
+
_meta && (meta.value = _meta)
|
|
339
|
+
if (props.endReach) {
|
|
340
|
+
getRows.value = [...getRows.value, ...(_data || [])]
|
|
341
|
+
} else {
|
|
342
|
+
getRows.value = _data || []
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
.catch((e) => {
|
|
346
|
+
// console.log(e)
|
|
347
|
+
if (e?.response?.status === 401) {
|
|
348
|
+
logoutDatatable()
|
|
349
|
+
return e
|
|
350
|
+
}
|
|
351
|
+
if (e?._message) {
|
|
352
|
+
alertError(e._message)
|
|
353
|
+
} else if (e?.message) {
|
|
354
|
+
alertError(e.message)
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
.finally(() => {
|
|
358
|
+
loading.value = !1
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
const exportData = (type: MDtExportOptions) => {
|
|
363
|
+
if (loading.value) {
|
|
364
|
+
return
|
|
365
|
+
}
|
|
366
|
+
const ex = async () => {
|
|
367
|
+
loading.value = !0
|
|
368
|
+
const toBLob = exportToBlob.value === !0 || exportToBlob.value === type
|
|
369
|
+
const headerItems = getHeaders.value.filter(e => e?.field !== props.controlKey && visibleHeaders.value.indexOf(e.name) !== -1)
|
|
370
|
+
const data = getDatatableParams({
|
|
371
|
+
pagination: pagination.value,
|
|
372
|
+
filter: search.value
|
|
373
|
+
}, {
|
|
374
|
+
indexType: type,
|
|
375
|
+
fdt: 'e',
|
|
376
|
+
toUrl: toBLob === !0 ? !1 : exportToBlob.value !== type,
|
|
377
|
+
headerItems
|
|
378
|
+
})
|
|
379
|
+
if (selected.value.length > 0) {
|
|
380
|
+
data.ids = selected.value.map((e: any) => e.id)
|
|
381
|
+
}
|
|
382
|
+
// console.log(3)
|
|
383
|
+
const config: AxiosRequestConfig = {}
|
|
384
|
+
if (toBLob) {
|
|
385
|
+
config.responseType = 'blob'
|
|
386
|
+
}
|
|
387
|
+
getMythApiServicesSchema().export(data, config)
|
|
388
|
+
.then(async (response) => {
|
|
389
|
+
const { _message } = response || {}
|
|
390
|
+
_message && (alertSuccess(_message))
|
|
391
|
+
try {
|
|
392
|
+
await downloadFromResponse(response)
|
|
393
|
+
} catch (e: any) {
|
|
394
|
+
if (response.status === 200 && response.headers['content-type'] === 'application/json') {
|
|
395
|
+
return response
|
|
396
|
+
}
|
|
397
|
+
if (e?.code) {
|
|
398
|
+
alertError(__(`messages.${e.code}`))
|
|
399
|
+
} else if (e?.message) {
|
|
400
|
+
alertError(e.message)
|
|
401
|
+
}
|
|
402
|
+
console.log(e)
|
|
403
|
+
}
|
|
404
|
+
return response
|
|
405
|
+
})
|
|
406
|
+
.catch((e) => {
|
|
407
|
+
alertError(e?._message || e?.message || 'Error')
|
|
408
|
+
})
|
|
409
|
+
.finally(() => {
|
|
410
|
+
loading.value = !1
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
if (!selected.value.length) {
|
|
414
|
+
confirmMessage(__('messages.export_all')).onOk(() => ex())
|
|
415
|
+
} else {
|
|
416
|
+
ex()
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const openDialogTimeout = 100
|
|
420
|
+
const setValues = useSetFormValues()
|
|
421
|
+
const resetDialogForm = useResetForm()
|
|
422
|
+
const resetVeeForm = async (attrs?: Record<string, any>) => {
|
|
423
|
+
const init: any = {}
|
|
424
|
+
for (const k in props.defaultItem) {
|
|
425
|
+
init[k] = attrs && k in attrs ? attrs[k] : props.defaultItem[k]
|
|
426
|
+
}
|
|
427
|
+
resetDialogForm({ values: init || {} }, { force: !0 })
|
|
428
|
+
attrs && setValues(attrs, !1)
|
|
429
|
+
}
|
|
430
|
+
const openFilterDialog = () => {
|
|
431
|
+
resetDialogForm({ values: {}, errors: {} }, { force: !0 })
|
|
432
|
+
nextTick(() => {
|
|
433
|
+
tempFilterForm.value = { ...filterForm.value }
|
|
434
|
+
filterDialogModel.value = !0
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
const saveFilterDialog = () => {
|
|
438
|
+
filterDialogModel.value = !1
|
|
439
|
+
nextTick(() => (filterForm.value = { ...tempFilterForm.value }))
|
|
440
|
+
}
|
|
441
|
+
const closeFilterDialog = () => {
|
|
442
|
+
filterDialogModel.value = !1
|
|
443
|
+
tempFilterForm.value = { ...filterForm.value }
|
|
444
|
+
}
|
|
445
|
+
const onRemoveFilter = (key: string | number) => {
|
|
446
|
+
const filter = filterForm.value
|
|
447
|
+
delete filter[key]
|
|
448
|
+
filterForm.value = { ...filter }
|
|
449
|
+
if (route.query[key]) {
|
|
450
|
+
const query = { ...route.query }
|
|
451
|
+
delete query[key]
|
|
452
|
+
router.push({ query })
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const updateFilterOptions = (data: Record<string, any>) => {
|
|
456
|
+
filterForm.value = {
|
|
457
|
+
...filterForm.value,
|
|
458
|
+
...data
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const openShowDialogNoIndex = async (i: MDtItem) => {
|
|
463
|
+
const item = toRef(i)
|
|
464
|
+
const index = getRows.value.findIndex(e => e.id === item.value.id)
|
|
465
|
+
return openShowDialog(item.value, index)
|
|
466
|
+
}
|
|
467
|
+
const openShowDialog = async (i: MDtItem, index: MDtItemIndex) => {
|
|
468
|
+
const fdt = 's'
|
|
469
|
+
const item = toRef(i)
|
|
470
|
+
if (props.showQueryParams) {
|
|
471
|
+
router.push({ query: { ...route.query, id: item.value.id, fdt } })
|
|
472
|
+
return
|
|
473
|
+
}
|
|
474
|
+
if (props.showRoute) {
|
|
475
|
+
if (typeof props.showRoute === 'string') {
|
|
476
|
+
router.push({ name: props.showRoute, params: { id: item.value.id }, query: route.query })
|
|
477
|
+
} else {
|
|
478
|
+
router.push(props.showRoute)
|
|
479
|
+
}
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
if (loading.value) {
|
|
483
|
+
return
|
|
484
|
+
}
|
|
485
|
+
loading.value = !0
|
|
486
|
+
const params: any = { fdt }
|
|
487
|
+
if (getRequestWith('withShow')) {
|
|
488
|
+
params.requestWith = getRequestWith('withShow')
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
getMythApiServicesSchema().show(item.value.id, { params })
|
|
492
|
+
.then(({ _data }) => {
|
|
493
|
+
dialogItem.value = _data
|
|
494
|
+
dialogItemIndex.value = index
|
|
495
|
+
getRows.value[index as any] = _data as any
|
|
496
|
+
nextTick(() => setTimeout(() => (selected.value = [getRows.value[index] as any]), openDialogTimeout))
|
|
497
|
+
setTimeout(() => (showDialogModel.value = !0), openDialogTimeout)
|
|
498
|
+
})
|
|
499
|
+
.catch((e: any) => {
|
|
500
|
+
const message = e?._message || e?.message
|
|
501
|
+
message && alertError(message)
|
|
502
|
+
})
|
|
503
|
+
.finally(() => (loading.value = !1))
|
|
504
|
+
}
|
|
505
|
+
const closeShowDialog = () => {
|
|
506
|
+
showDialogModel.value = !1
|
|
507
|
+
dialogItem.value = undefined
|
|
508
|
+
dialogItemIndex.value = undefined
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const updateRouteProp = computed(() => props.updateRoute)
|
|
512
|
+
const openUpdateDialogNoIndex = (i: MDtItem) => {
|
|
513
|
+
const item = toRef(i)
|
|
514
|
+
const index = getRows.value.findIndex(e => e.id === item.value.id)
|
|
515
|
+
return openUpdateDialog(item.value, index)
|
|
516
|
+
}
|
|
517
|
+
const openUpdateDialog = (i: MDtItem, index: MDtItemIndex) => {
|
|
518
|
+
const fdt = 'u'
|
|
519
|
+
const item = { ...toValue(i) }
|
|
520
|
+
if (props.updateQueryParams) {
|
|
521
|
+
router.push({ query: { ...route.query, id: item.id, fdt } })
|
|
522
|
+
return
|
|
523
|
+
}
|
|
524
|
+
if (updateRouteProp.value) {
|
|
525
|
+
if (typeof updateRouteProp.value === 'string') {
|
|
526
|
+
router.push({ name: updateRouteProp.value, params: { id: item.id }, query: route.query })
|
|
527
|
+
} else {
|
|
528
|
+
router.push(updateRouteProp.value)
|
|
529
|
+
}
|
|
530
|
+
return
|
|
531
|
+
}
|
|
532
|
+
if (loading.value) {
|
|
533
|
+
return
|
|
534
|
+
}
|
|
535
|
+
loading.value = !0
|
|
536
|
+
isUpdateMode.value = !0
|
|
537
|
+
const params: any = { fdt }
|
|
538
|
+
if (getRequestWith('withUpdate')) {
|
|
539
|
+
params.requestWith = getRequestWith('withUpdate')
|
|
540
|
+
}
|
|
541
|
+
getMythApiServicesSchema().show(item.id, { params })
|
|
542
|
+
.then(({ _data }) => {
|
|
543
|
+
dialogItem.value = _data
|
|
544
|
+
dialogItemIndex.value = index
|
|
545
|
+
if (_data) {
|
|
546
|
+
getRows.value[index] = { ..._data } as any
|
|
547
|
+
nextTick(() => setTimeout(() => (selected.value = [getRows.value[index] as any]), openDialogTimeout))
|
|
548
|
+
}
|
|
549
|
+
setTimeout(() => {
|
|
550
|
+
resetVeeForm(_data)
|
|
551
|
+
nextTick(() => {
|
|
552
|
+
formDialogModel.value = !0
|
|
553
|
+
})
|
|
554
|
+
}, openDialogTimeout)
|
|
555
|
+
})
|
|
556
|
+
.catch((e: any) => {
|
|
557
|
+
const message = e?._message || e?.message
|
|
558
|
+
message && alertError(message)
|
|
559
|
+
})
|
|
560
|
+
.finally(() => (loading.value = !1))
|
|
561
|
+
}
|
|
562
|
+
const openCreateDialog = (dtItem?: MDtItem) => {
|
|
563
|
+
const fdt = 'c'
|
|
564
|
+
if (props.storeQueryParams) {
|
|
565
|
+
router.push({ query: { ...route.query, id: undefined, fdt } })
|
|
566
|
+
return
|
|
567
|
+
}
|
|
568
|
+
if (props.storeRoute) {
|
|
569
|
+
if (typeof props.storeRoute === 'string') {
|
|
570
|
+
router.push({ name: props.storeRoute, query: route.query })
|
|
571
|
+
} else {
|
|
572
|
+
router.push(props.storeRoute)
|
|
573
|
+
}
|
|
574
|
+
return
|
|
575
|
+
}
|
|
576
|
+
isUpdateMode.value = !1
|
|
577
|
+
dialogItem.value = { ...defaultItem.value, ...dtItem } as MDtItem
|
|
578
|
+
dialogItemIndex.value = undefined
|
|
579
|
+
setTimeout(() => {
|
|
580
|
+
resetVeeForm(dtItem)
|
|
581
|
+
formDialogModel.value = !0
|
|
582
|
+
}, openDialogTimeout)
|
|
583
|
+
}
|
|
584
|
+
const closeFormDialog = () => {
|
|
585
|
+
formDialogModel.value = !1
|
|
586
|
+
isUpdateMode.value = !1
|
|
587
|
+
dialogItem.value = undefined
|
|
588
|
+
dialogItemIndex.value = undefined
|
|
589
|
+
setTimeout(() => resetVeeForm(), openDialogTimeout)
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const updateDatatableItem = (i: MDtItem, index?: MDtItemIndex) => {
|
|
593
|
+
const item = { ...toValue(i) }
|
|
594
|
+
if (item) {
|
|
595
|
+
if (index !== undefined) {
|
|
596
|
+
getRows.value[index] = item
|
|
597
|
+
} else {
|
|
598
|
+
const findIndex = getRows.value.findIndex(e => parseInt(e.id?.toString?.() ?? '') === parseInt(item?.id?.toString?.() ?? ''))
|
|
599
|
+
if (findIndex >= 0) {
|
|
600
|
+
getRows.value[findIndex] = item
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
selected.value.length === 1 && updateSelectedItems([item])
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const removeDtItem = (i: MDtItem | number) => {
|
|
607
|
+
const item = toRef<MDtItem | number>(i)
|
|
608
|
+
const id: string | number = typeof item.value !== 'object' ? item.value : item.value.id as string
|
|
609
|
+
if (typeof item.value !== 'object') {
|
|
610
|
+
getRows.value = getRows.value.filter((e, i) => i !== id)
|
|
611
|
+
} else {
|
|
612
|
+
getRows.value = getRows.value.filter((e) => parseInt(e.id?.toString() ?? '') !== parseInt(id.toString()))
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
const ignoreKeysProps = computed(() => props.ignoreKeys)
|
|
616
|
+
|
|
617
|
+
const hideAutoMessage = computed(() => props.hideAutoMessage)
|
|
618
|
+
const onDeleteItem = (i: MDtItem, index: number) => {
|
|
619
|
+
const item = toRef(i)
|
|
620
|
+
if (loading.value || !item.value?.id) {
|
|
621
|
+
return
|
|
622
|
+
}
|
|
623
|
+
hasAction.value = !0
|
|
624
|
+
confirmMessage(__('messages.confirm_delete')).onOk(async () => {
|
|
625
|
+
loading.value = !0
|
|
626
|
+
try {
|
|
627
|
+
const { _message, _success } = await getMythApiServicesSchema().destroy(item.value.id)
|
|
628
|
+
if (!hideAutoMessage.value && _success && _message) {
|
|
629
|
+
_message && alertSuccess(_message)
|
|
630
|
+
}
|
|
631
|
+
if (_success) {
|
|
632
|
+
if (pagination.value.rowsNumber !== undefined) {
|
|
633
|
+
--pagination.value.rowsNumber
|
|
634
|
+
selected.value = []
|
|
635
|
+
}
|
|
636
|
+
removeDtItem(index)
|
|
637
|
+
}
|
|
638
|
+
} catch (e: any) {
|
|
639
|
+
e?._message && alertError(e._message)
|
|
640
|
+
} finally {
|
|
641
|
+
loading.value = !1
|
|
642
|
+
}
|
|
643
|
+
}).onDismiss(() => {
|
|
644
|
+
hasAction.value = !1
|
|
645
|
+
})
|
|
646
|
+
}
|
|
647
|
+
const deleteSelectionItem = () => {
|
|
648
|
+
if (!selected.value.length) return
|
|
649
|
+
if (loading.value || !selected.value.length) {
|
|
650
|
+
return
|
|
651
|
+
}
|
|
652
|
+
if (selected.value.length === 1) {
|
|
653
|
+
const dtItem = selected.value[0] as MDtItem
|
|
654
|
+
const index = getRows.value.findIndex((e: any) => parseInt(e.id) === parseInt(dtItem?.id?.toString() || ''))
|
|
655
|
+
return onDeleteItem(dtItem, index)
|
|
656
|
+
}
|
|
657
|
+
if (!props.multiDestroy) {
|
|
658
|
+
return
|
|
659
|
+
}
|
|
660
|
+
hasAction.value = !0
|
|
661
|
+
confirmMessage(__('messages.confirm_delete')).onOk(async () => {
|
|
662
|
+
loading.value = !0
|
|
663
|
+
try {
|
|
664
|
+
const {
|
|
665
|
+
_message,
|
|
666
|
+
_success
|
|
667
|
+
} = await getMythApiServicesSchema().destroyAll(selected.value.map((e: MDtItem) => e.id))
|
|
668
|
+
if (!hideAutoMessage.value && _success && _message) {
|
|
669
|
+
_message && alertSuccess(_message)
|
|
670
|
+
}
|
|
671
|
+
if (_success) {
|
|
672
|
+
refresh()
|
|
673
|
+
}
|
|
674
|
+
} catch (e: any) {
|
|
675
|
+
e?._message && alertError(e._message)
|
|
676
|
+
} finally {
|
|
677
|
+
loading.value = !1
|
|
678
|
+
nextTick()
|
|
679
|
+
selected.value = []
|
|
680
|
+
}
|
|
681
|
+
}).onDismiss(() => {
|
|
682
|
+
hasAction.value = !1
|
|
683
|
+
})
|
|
684
|
+
}
|
|
685
|
+
const logoutDatatable = () => {
|
|
686
|
+
//
|
|
687
|
+
}
|
|
688
|
+
const onRowContextmenu = (e: MouseEvent | Event, row: MDtItem, index: number | undefined) => {
|
|
689
|
+
e.preventDefault?.()
|
|
690
|
+
row = toValue(row)
|
|
691
|
+
if (index === dialogItemIndex.value) {
|
|
692
|
+
selected.value = []
|
|
693
|
+
dialogItem.value = undefined
|
|
694
|
+
dialogItemIndex.value = undefined
|
|
695
|
+
if (contextmenu.value) {
|
|
696
|
+
contextmenu.value = !1
|
|
697
|
+
}
|
|
698
|
+
return
|
|
699
|
+
}
|
|
700
|
+
selected.value = [row]
|
|
701
|
+
dialogItem.value = row
|
|
702
|
+
dialogItemIndex.value = index
|
|
703
|
+
if (isGrid.value) {
|
|
704
|
+
contextmenu.value = !0
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
const contextmenuItems = computed<any>(() => ([
|
|
708
|
+
{
|
|
709
|
+
name: 'show',
|
|
710
|
+
label: pluginOptions.value?.dt?.contextmenu?.btnStyle?.showLabel ? 'labels.show' : undefined,
|
|
711
|
+
click: (item: MDtItem, index: MDtItemIndex) => {
|
|
712
|
+
openShowDialog(item, index)
|
|
713
|
+
},
|
|
714
|
+
showIf: hasShowBtn.value,
|
|
715
|
+
order: 100
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
name: 'clone',
|
|
719
|
+
label: pluginOptions.value?.dt?.contextmenu?.btnStyle?.showLabel ? 'labels.clone' : undefined,
|
|
720
|
+
click: onCloneItem,
|
|
721
|
+
showIf: hasCloneBtn.value,
|
|
722
|
+
order: 200
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
name: 'update',
|
|
726
|
+
label: pluginOptions.value?.dt?.contextmenu?.btnStyle?.showLabel ? 'labels.update' : undefined,
|
|
727
|
+
click: (item: MDtItem, index: MDtItemIndex) => {
|
|
728
|
+
openUpdateDialog(item, index)
|
|
729
|
+
},
|
|
730
|
+
showIf: hasUpdateBtn.value,
|
|
731
|
+
order: 300
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
name: 'destroy',
|
|
735
|
+
label: pluginOptions.value?.dt?.contextmenu?.btnStyle?.showLabel ? 'labels.destroy' : undefined,
|
|
736
|
+
click: (item: MDtItem, index: MDtItemIndex) => {
|
|
737
|
+
selected.value = [item]
|
|
738
|
+
onDeleteItem(item, index)
|
|
739
|
+
},
|
|
740
|
+
showIf: hasDestroyBtn.value,
|
|
741
|
+
attr: {
|
|
742
|
+
color: 'negative'
|
|
743
|
+
},
|
|
744
|
+
order: 400
|
|
745
|
+
},
|
|
746
|
+
...(props.contextItems || [])
|
|
747
|
+
].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))))
|
|
748
|
+
const onCloneItem = (item: MDtItem) => {
|
|
749
|
+
item = toValue(item)
|
|
750
|
+
confirmMessage()
|
|
751
|
+
.onOk(() => {
|
|
752
|
+
$q.loading.show()
|
|
753
|
+
getMythApiServicesSchema().clone(item.id)
|
|
754
|
+
.then(({ _message }) => {
|
|
755
|
+
_message && alertSuccess(_message)
|
|
756
|
+
refreshNoUpdate()
|
|
757
|
+
})
|
|
758
|
+
.catch(({ _message }) => {
|
|
759
|
+
_message && alertError(_message)
|
|
760
|
+
})
|
|
761
|
+
.finally(() => {
|
|
762
|
+
$q.loading.hide()
|
|
763
|
+
})
|
|
764
|
+
})
|
|
765
|
+
}
|
|
766
|
+
const endReach = computed<boolean>(() => Boolean(props.endReach))
|
|
767
|
+
const rowsPerPageOptions = computed(() => props.rowsPerPageOptions)
|
|
768
|
+
const getRowsPerPageOptions = computed<any[]>(() => endReach.value ? [0] : (rowsPerPageOptions.value || [0]))
|
|
769
|
+
|
|
770
|
+
const imageDialog = reactive<MDatatableScope['imageDialog']>({
|
|
771
|
+
value: !1,
|
|
772
|
+
src: undefined,
|
|
773
|
+
asAttachment: undefined
|
|
774
|
+
})
|
|
775
|
+
const openImageDialog = (src: string, opts?: { asAttachment?: boolean }) => {
|
|
776
|
+
imageDialog.src = src
|
|
777
|
+
imageDialog.asAttachment = opts?.asAttachment
|
|
778
|
+
nextTick(() => {
|
|
779
|
+
imageDialog.value = !0
|
|
780
|
+
})
|
|
781
|
+
}
|
|
782
|
+
const closeImageDialog = () => {
|
|
783
|
+
imageDialog.value = !1
|
|
784
|
+
nextTick(() => {
|
|
785
|
+
imageDialog.src = undefined
|
|
786
|
+
imageDialog.asAttachment = undefined
|
|
787
|
+
})
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const tableOptions = reactive<MDatatableOptions>({
|
|
791
|
+
search,
|
|
792
|
+
loading,
|
|
793
|
+
pagination,
|
|
794
|
+
meta,
|
|
795
|
+
filter: filterForm,
|
|
796
|
+
tempFilter: tempFilterForm,
|
|
797
|
+
selected,
|
|
798
|
+
hasAction,
|
|
799
|
+
fullscreen,
|
|
800
|
+
getHeaders,
|
|
801
|
+
visibleHeaders
|
|
802
|
+
})
|
|
803
|
+
const datatableItemsScope = reactive({
|
|
804
|
+
openShowDialog,
|
|
805
|
+
openShowDialogNoIndex,
|
|
806
|
+
closeShowDialog,
|
|
807
|
+
openUpdateDialog,
|
|
808
|
+
openUpdateDialogNoIndex,
|
|
809
|
+
openCreateDialog,
|
|
810
|
+
closeFormDialog,
|
|
811
|
+
onDeleteItem,
|
|
812
|
+
refresh,
|
|
813
|
+
refreshNoUpdate,
|
|
814
|
+
tableOptions,
|
|
815
|
+
isSingleSelectedItem,
|
|
816
|
+
firstSelectedItem,
|
|
817
|
+
updateDatatableItem,
|
|
818
|
+
updateSelectedItems,
|
|
819
|
+
imageDialog,
|
|
820
|
+
openImageDialog,
|
|
821
|
+
closeImageDialog
|
|
822
|
+
})
|
|
823
|
+
const getShowSelection = computed<boolean | undefined>(() => {
|
|
824
|
+
if (props.hideSelection) {
|
|
825
|
+
return !1
|
|
826
|
+
}
|
|
827
|
+
return props.showSelection
|
|
828
|
+
})
|
|
829
|
+
const defaultTopBtnProps: any = {
|
|
830
|
+
dense: !0,
|
|
831
|
+
flat: !0
|
|
832
|
+
}
|
|
833
|
+
const getProp = computed<any>(() => (k: any) => {
|
|
834
|
+
if (props[k] !== undefined) {
|
|
835
|
+
return props[k]
|
|
836
|
+
}
|
|
837
|
+
if (pluginOptions.value.datatable?.[k as any] !== undefined) {
|
|
838
|
+
return pluginOptions.value.datatable?.[k as any] as any
|
|
839
|
+
}
|
|
840
|
+
return props[k]
|
|
841
|
+
})
|
|
842
|
+
const skipSlots = ['default', 'top', 'title']
|
|
843
|
+
const getSlots = computed(() => {
|
|
844
|
+
const keys = Object.keys(slots || {})
|
|
845
|
+
return keys.filter(e => !skipSlots.includes(e))
|
|
846
|
+
})
|
|
847
|
+
const onClickTopMenu = (item: any) => {
|
|
848
|
+
if (item.multiClick && selected.value.length > 1) {
|
|
849
|
+
item.multiClick(selected.value)
|
|
850
|
+
return
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (item.click) {
|
|
854
|
+
item.click(selected.value[0], 0)
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
return {
|
|
859
|
+
imageDialog,
|
|
860
|
+
onRowContextmenu,
|
|
861
|
+
getRowsPerPageOptions,
|
|
862
|
+
getShowSelection,
|
|
863
|
+
defaultTopBtnProps,
|
|
864
|
+
getProp,
|
|
865
|
+
getSlots,
|
|
866
|
+
onClickTopMenu,
|
|
867
|
+
getRows,
|
|
868
|
+
defaultItem,
|
|
869
|
+
getTableTitle,
|
|
870
|
+
serviceName,
|
|
871
|
+
exportToBlob,
|
|
872
|
+
filterDialogModel,
|
|
873
|
+
showDialogModel,
|
|
874
|
+
formDialogModel,
|
|
875
|
+
isUpdateDialog,
|
|
876
|
+
dialogItem,
|
|
877
|
+
dialogItemIndex,
|
|
878
|
+
dialogErrors,
|
|
879
|
+
resetDialogs,
|
|
880
|
+
getHeaders,
|
|
881
|
+
visibleHeaders,
|
|
882
|
+
contextmenu,
|
|
883
|
+
selected,
|
|
884
|
+
onUpdateSelectedItems,
|
|
885
|
+
meta,
|
|
886
|
+
pagination,
|
|
887
|
+
search,
|
|
888
|
+
searchColumnsRef,
|
|
889
|
+
searchPlaceholder,
|
|
890
|
+
loading,
|
|
891
|
+
filterForm,
|
|
892
|
+
tempFilterForm,
|
|
893
|
+
hasAction,
|
|
894
|
+
fullscreen,
|
|
895
|
+
hasAddBtn,
|
|
896
|
+
hasUpdateBtn,
|
|
897
|
+
hasCloneBtn,
|
|
898
|
+
hasShowBtn,
|
|
899
|
+
hasDestroyBtn,
|
|
900
|
+
hasFilterDialog,
|
|
901
|
+
isUpdateMode,
|
|
902
|
+
formMode,
|
|
903
|
+
isSingleSelectedItem,
|
|
904
|
+
firstSelectedItem,
|
|
905
|
+
hasSelectedItem,
|
|
906
|
+
getShowTitle,
|
|
907
|
+
getFormTitle,
|
|
908
|
+
isGrid,
|
|
909
|
+
getMythApiServicesSchema,
|
|
910
|
+
updateSelectedItems,
|
|
911
|
+
onScroll,
|
|
912
|
+
loadMore,
|
|
913
|
+
refreshNoUpdate,
|
|
914
|
+
refresh,
|
|
915
|
+
getRequestWith,
|
|
916
|
+
getDatatableParams,
|
|
917
|
+
fetchDatatableItems,
|
|
918
|
+
exportData,
|
|
919
|
+
openDialogTimeout,
|
|
920
|
+
openFilterDialog,
|
|
921
|
+
saveFilterDialog,
|
|
922
|
+
closeFilterDialog,
|
|
923
|
+
onRemoveFilter,
|
|
924
|
+
updateFilterOptions,
|
|
925
|
+
openShowDialogNoIndex,
|
|
926
|
+
openShowDialog,
|
|
927
|
+
closeShowDialog,
|
|
928
|
+
openUpdateDialogNoIndex,
|
|
929
|
+
openUpdateDialog,
|
|
930
|
+
openCreateDialog,
|
|
931
|
+
closeFormDialog,
|
|
932
|
+
ignoreKeysProps,
|
|
933
|
+
updateDatatableItem,
|
|
934
|
+
tableOptions,
|
|
935
|
+
removeDtItem,
|
|
936
|
+
onDeleteItem,
|
|
937
|
+
deleteSelectionItem,
|
|
938
|
+
logoutDatatable,
|
|
939
|
+
contextmenuItems,
|
|
940
|
+
datatableItemsScope,
|
|
941
|
+
onCloneItem,
|
|
942
|
+
openImageDialog,
|
|
943
|
+
closeImageDialog
|
|
944
|
+
}
|
|
945
|
+
}
|