waibu-db 1.0.1 → 1.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/package.json +1 -1
- package/waibuBootstrap/theme/component/factory/btn-add.js +24 -0
- package/waibuBootstrap/theme/component/factory/btn-back.js +22 -0
- package/waibuBootstrap/theme/component/factory/btn-clone.js +39 -0
- package/waibuBootstrap/theme/component/factory/btn-columns.js +55 -0
- package/waibuBootstrap/theme/component/factory/btn-delete.js +52 -0
- package/waibuBootstrap/theme/component/factory/btn-details.js +41 -0
- package/waibuBootstrap/theme/component/factory/btn-edit.js +67 -0
- package/waibuBootstrap/theme/component/factory/btn-export.js +138 -0
- package/waibuBootstrap/theme/component/factory/echarts.js +67 -0
- package/waibuBootstrap/theme/component/factory/pagination.js +69 -0
- package/waibuBootstrap/theme/component/factory/query.js +177 -0
- package/waibuBootstrap/theme/component/factory/recs-info.js +58 -0
- package/waibuBootstrap/theme/component/factory/table.js +168 -0
- package/waibuBootstrap/theme/component/wdb-base.js +6 -0
- package/waibuBootstrap/theme/component/btn-add.js +0 -15
- package/waibuBootstrap/theme/component/btn-back.js +0 -13
- package/waibuBootstrap/theme/component/btn-clone.js +0 -30
- package/waibuBootstrap/theme/component/btn-columns.js +0 -45
- package/waibuBootstrap/theme/component/btn-delete.js +0 -43
- package/waibuBootstrap/theme/component/btn-details.js +0 -32
- package/waibuBootstrap/theme/component/btn-edit.js +0 -56
- package/waibuBootstrap/theme/component/btn-export.js +0 -129
- package/waibuBootstrap/theme/component/echarts.js +0 -58
- package/waibuBootstrap/theme/component/pagination.js +0 -60
- package/waibuBootstrap/theme/component/query.js +0 -137
- package/waibuBootstrap/theme/component/recs-info.js +0 -50
- package/waibuBootstrap/theme/component/table.js +0 -161
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import wdbBase from '../wdb-base.js'
|
|
2
|
+
|
|
3
|
+
async function query () {
|
|
4
|
+
const WdbBase = await wdbBase.call(this)
|
|
5
|
+
|
|
6
|
+
return class WdbQuery extends WdbBase {
|
|
7
|
+
async build () {
|
|
8
|
+
const { generateId } = this.plugin.app.bajo
|
|
9
|
+
const { jsonStringify } = this.plugin.app.waibuMpa
|
|
10
|
+
const { find, get, without, isEmpty, filter, upperFirst } = this.plugin.app.bajo.lib._
|
|
11
|
+
const qsKey = this.plugin.app.waibu.config.qsKey
|
|
12
|
+
const schema = get(this, 'component.locals.schema', {})
|
|
13
|
+
if (schema.view.disabled.includes('find')) {
|
|
14
|
+
this.params.html = ''
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
let fields = without(get(this, `component.locals._meta.query.${qsKey.fields}`, '').split(','), '')
|
|
18
|
+
if (isEmpty(fields)) fields = schema.view.fields
|
|
19
|
+
fields = filter(fields, f => schema.sortables.includes(f))
|
|
20
|
+
const id = generateId('alpha')
|
|
21
|
+
const columns = []
|
|
22
|
+
const models = []
|
|
23
|
+
const selects = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'contains', 'starts', 'ends', '!in', '!contains', '!starts', '!ends']
|
|
24
|
+
for (const f of schema.view.fields) {
|
|
25
|
+
if (!fields.includes(f)) continue
|
|
26
|
+
const prop = find(schema.properties, { name: f })
|
|
27
|
+
const ops = []
|
|
28
|
+
if (['float', 'double', 'integer', 'smallint'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
|
|
29
|
+
else if (['datetime', 'date', 'time'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
|
|
30
|
+
else if (['boolean'].includes(prop.type)) ops.push('eq', 'neq')
|
|
31
|
+
else ops.push(...selects)
|
|
32
|
+
if (ops.length === 0) continue
|
|
33
|
+
const sels = ops.map(o => `<c:option>${o}</c:option>`)
|
|
34
|
+
models.push(`${f}Op: 'eq'`, `${f}Val: ''`)
|
|
35
|
+
columns.push(`
|
|
36
|
+
<c:grid-col col="4-md" flex="align-items:center">
|
|
37
|
+
<c:form-check x-model="selected" t:label="field.${f}" value="${f}" />
|
|
38
|
+
</c:grid-col>
|
|
39
|
+
<c:grid-col col="3-md">
|
|
40
|
+
<c:form-select x-model="${f}Op">
|
|
41
|
+
${sels.join('\n')}
|
|
42
|
+
</c:form-select>
|
|
43
|
+
</c:grid-col>
|
|
44
|
+
<c:grid-col col="5-md">
|
|
45
|
+
<c:form-input x-model="${f}Val" />
|
|
46
|
+
</c:grid-col>
|
|
47
|
+
`)
|
|
48
|
+
}
|
|
49
|
+
this.params.noTag = true
|
|
50
|
+
const container = this.params.attr.modal ? 'modal' : 'drawer'
|
|
51
|
+
this.params.html = await this.component.buildSentence(`
|
|
52
|
+
<c:form-input type="search" t:placeholder="Query" id="${id}" x-data="{ query: '' }" x-init="
|
|
53
|
+
const url = new URL(window.location.href)
|
|
54
|
+
query = url.searchParams.get('${qsKey.query}') ?? ''
|
|
55
|
+
" x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
|
|
56
|
+
<c:form-input-addon>
|
|
57
|
+
<c:${container} launch-icon="${this.params.attr.icon ?? 'dotsThree'}" launch-on-end t:title="Query Builder" x-ref="query" x-data="{
|
|
58
|
+
fields: ${jsonStringify(fields, true)},
|
|
59
|
+
builder: '',
|
|
60
|
+
selected: [],
|
|
61
|
+
${models.join(',\n')},
|
|
62
|
+
ops: { eq: ':', neq: ':-', gt: ':>', gte: ':>=', lt: ':<', lte: ':<=' },
|
|
63
|
+
opsIn (v, neg) { return ':' + (neg ? '-' : '') + '[' + this.expandArray(v) + ']' },
|
|
64
|
+
opsExt (v, neg, ext) {
|
|
65
|
+
let prefix = (neg ? '-' : '') + '~'
|
|
66
|
+
if (ext) prefix += ext
|
|
67
|
+
return ':' + prefix + '\\'' + v + '\\''
|
|
68
|
+
},
|
|
69
|
+
initBuilder () {
|
|
70
|
+
this.builder = document.getElementById('${id}').value
|
|
71
|
+
if (_.isEmpty(this.builder)) return
|
|
72
|
+
const tokens = _.merge({}, this.ops, {
|
|
73
|
+
in: ':[',
|
|
74
|
+
contains: ':~',
|
|
75
|
+
starts: ':~^',
|
|
76
|
+
ends: ':~$$',
|
|
77
|
+
'!in': ':-[',
|
|
78
|
+
'!contains': ':-~',
|
|
79
|
+
'!starts': ':-~^',
|
|
80
|
+
'!ends': ':-~$$'
|
|
81
|
+
})
|
|
82
|
+
for (const part of this.builder.split('+')) {
|
|
83
|
+
let [f, opv] = part.split(':')
|
|
84
|
+
opv = ':' + opv
|
|
85
|
+
this.selected.push(f)
|
|
86
|
+
let op
|
|
87
|
+
let val
|
|
88
|
+
_.each(tokens, (v, k) => {
|
|
89
|
+
if (opv.slice(0, v.length) === v) {
|
|
90
|
+
op = k
|
|
91
|
+
val = opv.slice(v.length).replaceAll('[', '').replaceAll(']', '').replaceAll('\\'', '')
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
console.log(op, val)
|
|
95
|
+
if (_.isEmpty(op)) continue
|
|
96
|
+
this[f + 'Op'] = op
|
|
97
|
+
this[f + 'Val'] = val
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
expandArray (val = '') {
|
|
101
|
+
return _.map(val.split(','), item => {
|
|
102
|
+
item = _.trim(item)
|
|
103
|
+
if (Number(item)) return item
|
|
104
|
+
return '\\'' + item + '\\''
|
|
105
|
+
})
|
|
106
|
+
},
|
|
107
|
+
rebuild () {
|
|
108
|
+
const items = []
|
|
109
|
+
for (const sel of this.selected) {
|
|
110
|
+
const key = this[sel + 'Op']
|
|
111
|
+
let val = this[sel + 'Val']
|
|
112
|
+
if (_.isEmpty(val)) continue
|
|
113
|
+
let item
|
|
114
|
+
if (key === 'in') item = this.opsIn(val)
|
|
115
|
+
else if (key === '!in') item = this.opsIn(val, true)
|
|
116
|
+
else if (key === 'contains') item = this.opsExt(val)
|
|
117
|
+
else if (key === '!contains') item = this.opsExt(val, true)
|
|
118
|
+
else if (key === 'starts') item = this.opsExt(val, false, '^')
|
|
119
|
+
else if (key === '!starts') item = this.opsExt(val, true, '^')
|
|
120
|
+
else if (key === 'ends') item = this.opsExt(val, false, '$$')
|
|
121
|
+
else if (key === '!ends') item = this.opsExt(val, true, '$$')
|
|
122
|
+
else if (val.includes(' ')) item = this.ops[key] + '\\'' + val + '\\''
|
|
123
|
+
else item = this.ops[key] + val
|
|
124
|
+
items.push(sel + item)
|
|
125
|
+
}
|
|
126
|
+
this.builder = items.join('+')
|
|
127
|
+
},
|
|
128
|
+
submit (run) {
|
|
129
|
+
if (run) {
|
|
130
|
+
const url = new URL(window.location.href)
|
|
131
|
+
const params = new URLSearchParams(url.search)
|
|
132
|
+
params.set('${qsKey.page}', 1)
|
|
133
|
+
params.set('${qsKey.query}', this.builder ?? '')
|
|
134
|
+
window.location.href = '?' + params.toString()
|
|
135
|
+
} else $dispatch('on-query', this.builder)
|
|
136
|
+
const instance = wbs.getInstance('${upperFirst(container)}', $refs.query)
|
|
137
|
+
instance.hide()
|
|
138
|
+
}
|
|
139
|
+
}" x-init="
|
|
140
|
+
initBuilder()
|
|
141
|
+
const ops = _.map(fields, f => (f + 'Op'))
|
|
142
|
+
const vals = _.map(fields, f => (f + 'Val'))
|
|
143
|
+
const watcher = ['selected', ...ops, ...vals].join(',')
|
|
144
|
+
$watch(watcher, v => rebuild())
|
|
145
|
+
">
|
|
146
|
+
<c:grid-row gutter="2">
|
|
147
|
+
<c:grid-col col="12">
|
|
148
|
+
<c:form-textarea x-model="builder" readonly rows="4"/>
|
|
149
|
+
</c:grid-col>
|
|
150
|
+
${columns.join('\n')}
|
|
151
|
+
</c:grid-row>
|
|
152
|
+
<c:div flex="justify-content:end" margin="top-3">
|
|
153
|
+
<c:btn color="secondary" t:content="Close" dismiss="${container}" />
|
|
154
|
+
<c:btn color="primary" t:content="Apply" margin="start-2" @click="submit()" />
|
|
155
|
+
<c:btn color="primary" t:content="Submit Query" margin="start-2" @click="submit(true)" />
|
|
156
|
+
</c:div>
|
|
157
|
+
</c:${container}>
|
|
158
|
+
</c:form-input-addon>
|
|
159
|
+
<c:form-input-addon>
|
|
160
|
+
<c:btn t:content="Submit" x-data="{
|
|
161
|
+
submit () {
|
|
162
|
+
const val = document.getElementById('${id}').value ?? ''
|
|
163
|
+
const url = new URL(window.location.href)
|
|
164
|
+
const params = new URLSearchParams(url.search)
|
|
165
|
+
params.set('${qsKey.page}', 1)
|
|
166
|
+
params.set('${qsKey.query}', val)
|
|
167
|
+
window.location.href = '?' + params.toString()
|
|
168
|
+
}
|
|
169
|
+
}" @click="submit" @on-submit.window="submit()" />
|
|
170
|
+
</c:form-input-addon>
|
|
171
|
+
</c:form-input>
|
|
172
|
+
`)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default query
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getUrlOpts } from './pagination.js'
|
|
2
|
+
import wdbBase from '../wdb-base.js'
|
|
3
|
+
|
|
4
|
+
async function recsInfo () {
|
|
5
|
+
const WdbBase = await wdbBase.call(this)
|
|
6
|
+
|
|
7
|
+
return class WdbRecsInfo extends WdbBase {
|
|
8
|
+
async build () {
|
|
9
|
+
const { req } = this.component
|
|
10
|
+
const { attrToObject, groupAttrs, attrToArray } = this.plugin.app.waibuMpa
|
|
11
|
+
const { get, isEmpty, omit, merge } = this.plugin.app.bajo.lib._
|
|
12
|
+
const schema = get(this, 'component.locals.schema', {})
|
|
13
|
+
if (schema.view.disabled.includes('find')) {
|
|
14
|
+
this.params.html = ''
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
let { count, limit, page, pages } = attrToObject(this.params.attr.options)
|
|
18
|
+
count = count ?? get(this, 'component.locals.list.count', 0)
|
|
19
|
+
page = page ?? get(this, 'component.locals.list.page', 1)
|
|
20
|
+
limit = limit ?? get(this, 'component.locals.list.limit', 25)
|
|
21
|
+
pages = pages ?? get(this, 'component.locals.list.pages', 0)
|
|
22
|
+
|
|
23
|
+
this.params.tag = 'div'
|
|
24
|
+
this.params.attr.flex = 'justify-center:start align-items:center'
|
|
25
|
+
if (count === 0) {
|
|
26
|
+
this.params.html = req.t('No record found')
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
if (!this.params.attr.dropdown) this.params.attr.dropdown = true
|
|
30
|
+
const group = groupAttrs(this.params.attr, ['dropdown'])
|
|
31
|
+
const html = []
|
|
32
|
+
if (this.params.attr.count) html.push(req.t('%s record(s) found', req.format(count, 'integer')))
|
|
33
|
+
if (this.params.attr.pages) {
|
|
34
|
+
if (!isEmpty(html)) html[html.length - 1] += '.'
|
|
35
|
+
html.push(req.t('Page %s of %s pages', req.format(page, 'integer'), req.format(pages, 'integer')))
|
|
36
|
+
}
|
|
37
|
+
if (this.params.attr.recsPerPage) {
|
|
38
|
+
this.params.attr.recsPerPageValues = this.params.attr.recsPerPageValues ?? '10 25 50'
|
|
39
|
+
this.params.attr.recsPerPageValues = attrToArray(this.params.attr.recsPerPageValues)
|
|
40
|
+
if (!isEmpty(html)) html[html.length - 1] += ','
|
|
41
|
+
const items = []
|
|
42
|
+
for (const i of this.params.attr.recsPerPageValues) {
|
|
43
|
+
const attr = { href: this.component.buildUrl(merge(getUrlOpts.call(this), { params: { limit: i, page: 1 } })), disabled: i === limit }
|
|
44
|
+
items.push(await this.component.buildTag({ tag: 'dropdownItem', attr, html: i + '' }))
|
|
45
|
+
}
|
|
46
|
+
const attr = group.dropdown
|
|
47
|
+
attr.content = limit + ''
|
|
48
|
+
attr.color = attr.color ?? 'secondary-outline'
|
|
49
|
+
html.push(await this.component.buildTag({ tag: 'dropdown', attr, html: items.join('\n') }))
|
|
50
|
+
html.push(' ', req.t('recs per page'))
|
|
51
|
+
}
|
|
52
|
+
this.params.attr = omit(this.params.attr, ['count', 'pages', 'recsPerPage', 'dropdown', 'recsPerPageValues'])
|
|
53
|
+
this.params.html = html.map(h => `<div class="me-1">${h}</div>`).join('\n')
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default recsInfo
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import wdbBase from '../wdb-base.js'
|
|
2
|
+
|
|
3
|
+
async function table () {
|
|
4
|
+
const WdbBase = await wdbBase.call(this)
|
|
5
|
+
|
|
6
|
+
return class WdbTable extends WdbBase {
|
|
7
|
+
isRightAligned (type) {
|
|
8
|
+
return ['smallint', 'integer', 'float', 'double'].includes(type)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async build () {
|
|
12
|
+
const { req } = this.component
|
|
13
|
+
const { escape } = this.plugin.app.waibu
|
|
14
|
+
const { attrToArray, groupAttrs } = this.plugin.app.waibuMpa
|
|
15
|
+
const { get, omit, set, find, isEmpty, without } = this.plugin.app.bajo.lib._
|
|
16
|
+
const group = groupAttrs(this.params.attr, ['body', 'head', 'foot'])
|
|
17
|
+
this.params.attr = group._
|
|
18
|
+
|
|
19
|
+
const data = get(this, 'component.locals.list.data', [])
|
|
20
|
+
const schema = get(this, 'component.locals.schema', {})
|
|
21
|
+
if (schema.view.disabled.includes('find')) {
|
|
22
|
+
this.params.html = ''
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
const qsKey = this.plugin.app.waibu.config.qsKey
|
|
26
|
+
let fields = without(get(this, `component.locals._meta.query.${qsKey.fields}`, '').split(','), '')
|
|
27
|
+
if (isEmpty(fields)) fields = schema.view.fields
|
|
28
|
+
const sort = this.params.attr.sort ? attrToArray(this.params.attr.sort) : get(this, `component.locals._meta.query.${qsKey.sort}`, '')
|
|
29
|
+
|
|
30
|
+
let [sortCol, sortDir] = sort.split(':')
|
|
31
|
+
if (!['-1', '1'].includes(sortDir)) sortDir = '1'
|
|
32
|
+
|
|
33
|
+
let selection
|
|
34
|
+
const canDelete = !schema.view.disabled.includes('remove')
|
|
35
|
+
const canEdit = !schema.view.disabled.includes('update')
|
|
36
|
+
if (canEdit) selection = 'single'
|
|
37
|
+
if (canDelete) selection = 'multi'
|
|
38
|
+
if (selection) this.params.attr.hover = true
|
|
39
|
+
|
|
40
|
+
this.params.noTag = true
|
|
41
|
+
const html = []
|
|
42
|
+
let items = []
|
|
43
|
+
// head
|
|
44
|
+
for (const f of schema.view.fields) {
|
|
45
|
+
if (!fields.includes(f)) continue
|
|
46
|
+
const prop = find(schema.properties, { name: f })
|
|
47
|
+
let head = req.t(`field.${f}`)
|
|
48
|
+
if (!this.params.attr.noSort && (schema.sortables ?? []).includes(f)) {
|
|
49
|
+
let sortItem = `${f}:-1`
|
|
50
|
+
let icon = this.params.attr.sortUpIcon ?? 'caretUp'
|
|
51
|
+
if (f === sortCol) {
|
|
52
|
+
sortItem = `${f}:${sortDir === '1' ? '-1' : '1'}`
|
|
53
|
+
icon = sortDir === '1' ? (this.params.attr.sortUpIcon ?? 'caretUp') : (this.params.attr.sortDownIcon ?? 'caretDown')
|
|
54
|
+
}
|
|
55
|
+
const item = set({ page: 1 }, qsKey.sort, sortItem)
|
|
56
|
+
const href = this.component.buildUrl({ params: item })
|
|
57
|
+
const attr = this.isRightAligned(prop.type) ? { text: 'align:end' } : {}
|
|
58
|
+
const content = [
|
|
59
|
+
await this.component.buildTag({ tag: 'div', attr, html: req.t(`field.${f}`) }),
|
|
60
|
+
await this.component.buildTag({ tag: 'a', attr: { icon, href }, prepend: '<div class="ms-1">', append: '</div>' })
|
|
61
|
+
]
|
|
62
|
+
head = await this.component.buildTag({ tag: 'div', attr: { flex: 'justify-content:between align-items:end' }, html: content.join('\n') })
|
|
63
|
+
}
|
|
64
|
+
let text = this.params.attr.headerNowrap ? '' : 'nowrap'
|
|
65
|
+
if (this.isRightAligned(prop.type)) text += ' align:end'
|
|
66
|
+
const attr = { dataKey: f, dataType: prop.type, text }
|
|
67
|
+
items.push(await this.component.buildTag({ tag: 'th', attr, html: head }))
|
|
68
|
+
}
|
|
69
|
+
if (items.length > 0 && selection) {
|
|
70
|
+
let item = '<th></th>'
|
|
71
|
+
if (selection === 'multi') {
|
|
72
|
+
const attr = { 'x-model': 'toggleAll', name: '_rtm', noWrapper: true, noLabel: true }
|
|
73
|
+
item = await this.component.buildTag({ tag: 'formCheck', attr, prepend: '<th>', append: '</th>' })
|
|
74
|
+
} else {
|
|
75
|
+
const attr = { name: 'remove', '@click': 'selected = \'\'', style: { cursor: 'pointer' } }
|
|
76
|
+
item = await this.component.buildTag({ tag: 'icon', attr, prepend: '<th>', append: '</th>' })
|
|
77
|
+
}
|
|
78
|
+
items.unshift(item)
|
|
79
|
+
}
|
|
80
|
+
const header = await this.component.buildTag({ tag: 'tr', html: items.join('\n') })
|
|
81
|
+
html.push(await this.component.buildTag({ tag: 'thead', attr: group.head, html: header }))
|
|
82
|
+
// body
|
|
83
|
+
items = []
|
|
84
|
+
for (const d of data) {
|
|
85
|
+
const lines = []
|
|
86
|
+
if (selection) {
|
|
87
|
+
const tag = selection === 'single' ? 'formRadio' : 'formCheck'
|
|
88
|
+
const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
|
|
89
|
+
lines.push(await this.component.buildTag({ tag, attr, prepend: '<td>', append: '</td>' }))
|
|
90
|
+
}
|
|
91
|
+
for (const f of schema.view.fields) {
|
|
92
|
+
const prop = find(schema.properties, { name: f })
|
|
93
|
+
if (!fields.includes(f)) continue
|
|
94
|
+
const opts = {}
|
|
95
|
+
if (f === 'lng') opts.longitude = true
|
|
96
|
+
else if (f === 'lat') opts.latitude = true
|
|
97
|
+
let value = req.format(d[f], prop.type, opts)
|
|
98
|
+
if (prop.type === 'boolean') {
|
|
99
|
+
value = (await this.component.buildTag({ tag: 'icon', attr: { name: `circle${d[f] ? 'Check' : ''}` } })) +
|
|
100
|
+
' ' + (req.t(d[f] ? 'Yes' : 'No'))
|
|
101
|
+
} else value = escape(value)
|
|
102
|
+
let dataValue = d[f] ?? ''
|
|
103
|
+
if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
|
|
104
|
+
if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
|
|
105
|
+
const attr = { dataValue }
|
|
106
|
+
if (!['object', 'array'].includes(prop.type)) {
|
|
107
|
+
if (this.isRightAligned(prop.type)) attr.text = 'align:end nowrap'
|
|
108
|
+
else attr.text = 'nowrap'
|
|
109
|
+
}
|
|
110
|
+
const line = await this.component.buildTag({ tag: 'td', attr, html: value })
|
|
111
|
+
lines.push(line)
|
|
112
|
+
}
|
|
113
|
+
const attr = {}
|
|
114
|
+
if (!schema.view.disabled.includes('update') || !schema.view.disabled.includes('remove')) attr['@click'] = `toggle('${d.id}')`
|
|
115
|
+
if (!schema.view.disabled.includes('get')) attr['@dblclick'] = `goDetails('${d.id}')`
|
|
116
|
+
items.push(await this.component.buildTag({ tag: 'tr', attr, html: lines.join('\n') }))
|
|
117
|
+
}
|
|
118
|
+
html.push(await this.component.buildTag({ tag: 'tbody', attr: group.body, html: items.join('\n') }))
|
|
119
|
+
this.params.attr = omit(this.params.attr, ['sortUpIcon', 'sortDownIcon', 'noSort', 'selection', 'headerNowrap'])
|
|
120
|
+
const goDetails = `
|
|
121
|
+
goDetails (id) {
|
|
122
|
+
window.location.href = '${this.component.buildUrl({ base: 'details' })}&id=' + id
|
|
123
|
+
}
|
|
124
|
+
`
|
|
125
|
+
if (selection === 'multi') {
|
|
126
|
+
this.params.attr['x-data'] = `{
|
|
127
|
+
toggleAll: false,
|
|
128
|
+
selected: [],
|
|
129
|
+
toggle (id) {
|
|
130
|
+
if (this.selected.includes(id)) {
|
|
131
|
+
const idx = this.selected.indexOf(id)
|
|
132
|
+
this.selected.splice(idx, 1)
|
|
133
|
+
} else this.selected.push(id)
|
|
134
|
+
},
|
|
135
|
+
${goDetails}
|
|
136
|
+
}`
|
|
137
|
+
this.params.attr['x-init'] = `
|
|
138
|
+
$watch('toggleAll', val => {
|
|
139
|
+
if (val) {
|
|
140
|
+
const els = document.getElementsByName('_rt')
|
|
141
|
+
const items = Array.from(els)
|
|
142
|
+
selected = items.map(el => el.value)
|
|
143
|
+
} else selected = []
|
|
144
|
+
})
|
|
145
|
+
$watch('selected', val => $dispatch('on-selection', val))
|
|
146
|
+
`
|
|
147
|
+
} else if (selection === 'single') {
|
|
148
|
+
this.params.attr['x-data'] = `{
|
|
149
|
+
selected: '',
|
|
150
|
+
toggle (id) {
|
|
151
|
+
this.selected = id
|
|
152
|
+
},
|
|
153
|
+
${goDetails}
|
|
154
|
+
}`
|
|
155
|
+
this.params.attr['x-init'] = `
|
|
156
|
+
$watch('selected', val => $dispatch('on-selection', [val]))
|
|
157
|
+
`
|
|
158
|
+
} else {
|
|
159
|
+
this.params.attr['x-data'] = `{
|
|
160
|
+
${goDetails}
|
|
161
|
+
}`
|
|
162
|
+
}
|
|
163
|
+
this.params.html = await this.component.buildTag({ tag: 'table', attr: this.params.attr, html: html.join('\n') })
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default table
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
async function btnAdd (params = {}) {
|
|
2
|
-
const { isEmpty, get } = this.plugin.app.bajo.lib._
|
|
3
|
-
params.noTag = true
|
|
4
|
-
const schema = get(this, 'locals.schema', {})
|
|
5
|
-
if (schema.view.disabled.includes('create')) {
|
|
6
|
-
params.html = ''
|
|
7
|
-
return
|
|
8
|
-
}
|
|
9
|
-
if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Add')
|
|
10
|
-
params.attr.color = params.attr.color ?? 'secondary-outline'
|
|
11
|
-
if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'add' })
|
|
12
|
-
params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default btnAdd
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
async function btnBack (params = {}) {
|
|
2
|
-
const { isEmpty } = this.plugin.app.bajo.lib._
|
|
3
|
-
const { attrToArray } = this.plugin.app.waibuMpa
|
|
4
|
-
params.noTag = true
|
|
5
|
-
if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Back')
|
|
6
|
-
if (isEmpty(params.attr.icon)) params.attr.icon = 'arrowStart'
|
|
7
|
-
params.attr.color = params.attr.color ?? 'secondary-outline'
|
|
8
|
-
params.attr.excludeQs = ['mode', 'id', ...attrToArray(params.attr.excludeQs ?? '')]
|
|
9
|
-
if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'list', exclude: params.attr.excludeQs })
|
|
10
|
-
params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default btnBack
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
async function btnClone (params = {}) {
|
|
2
|
-
const { isEmpty, get } = this.plugin.app.bajo.lib._
|
|
3
|
-
params.noTag = true
|
|
4
|
-
const schema = get(this, 'locals.schema', {})
|
|
5
|
-
if (schema.view.disabled.includes('create')) {
|
|
6
|
-
params.html = ''
|
|
7
|
-
return
|
|
8
|
-
}
|
|
9
|
-
if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Clone')
|
|
10
|
-
params.attr.color = params.attr.color ?? 'secondary-outline'
|
|
11
|
-
if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'add', exclude: ['id'] }) + '&mode=clone'
|
|
12
|
-
if (params.attr.onList) {
|
|
13
|
-
params.attr['x-ref'] = 'clone'
|
|
14
|
-
params.attr.disabled = true
|
|
15
|
-
params.attr['x-data'] = `{
|
|
16
|
-
path: '${params.attr.href}'
|
|
17
|
-
}`
|
|
18
|
-
params.attr['@on-selection.window'] = `
|
|
19
|
-
const recId = $event.detail[0] ?? ''
|
|
20
|
-
if ($event.detail.length === 1) $refs.clone.classList.remove('disabled')
|
|
21
|
-
else $refs.clone.classList.add('disabled')
|
|
22
|
-
$refs.clone.href = path + '&id=' + recId
|
|
23
|
-
`
|
|
24
|
-
} else {
|
|
25
|
-
params.attr.href += '&id=' + this.req.query.id
|
|
26
|
-
}
|
|
27
|
-
params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default btnClone
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
async function btnColumns (params = {}) {
|
|
2
|
-
const { get, isEmpty, without } = this.plugin.app.bajo.lib._
|
|
3
|
-
const { jsonStringify } = this.plugin.app.waibuMpa
|
|
4
|
-
const qsKey = this.plugin.app.waibu.config.qsKey
|
|
5
|
-
const schema = get(this, 'locals.schema', {})
|
|
6
|
-
if (schema.view.disabled.includes('find')) {
|
|
7
|
-
params.html = ''
|
|
8
|
-
return
|
|
9
|
-
}
|
|
10
|
-
let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
|
|
11
|
-
if (isEmpty(fields)) fields = schema.view.fields
|
|
12
|
-
const items = []
|
|
13
|
-
params.attr.color = params.attr.color ?? 'secondary-outline'
|
|
14
|
-
if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Columns')
|
|
15
|
-
for (const f of schema.view.fields) {
|
|
16
|
-
if (f === 'id') {
|
|
17
|
-
items.push(await this.buildTag({ tag: 'formCheck', attr: { checked: true, label: this.req.t('ID'), value: f, disabled: true } }))
|
|
18
|
-
continue
|
|
19
|
-
}
|
|
20
|
-
const attr = { 'x-model': 'selected', label: this.req.t(`field.${f}`), value: f }
|
|
21
|
-
if (fields.includes(f)) attr.checked = true
|
|
22
|
-
items.push(await this.buildTag({ tag: 'formCheck', attr }))
|
|
23
|
-
}
|
|
24
|
-
const href = this._buildUrl({ exclude: [qsKey.fields] })
|
|
25
|
-
const html = ['<form class="mt-1 mb-2 mx-3" ']
|
|
26
|
-
html.push(`x-data="{
|
|
27
|
-
selected: ${jsonStringify(fields, true)},
|
|
28
|
-
all: ${jsonStringify(schema.view.fields, true)}
|
|
29
|
-
}"`)
|
|
30
|
-
html.push(`x-init="
|
|
31
|
-
$refs.apply.href = '${href}&${qsKey.fields}=' + selected.join(',')
|
|
32
|
-
$watch('selected', v => {
|
|
33
|
-
$refs.apply.href = '${href}&${qsKey.fields}=' + v.join(',')
|
|
34
|
-
})
|
|
35
|
-
">`)
|
|
36
|
-
html.push(...items)
|
|
37
|
-
const attr = { size: 'sm', 'x-ref': 'apply', margin: 'top-2', color: params.attr.applyColor ?? 'primary', icon: params.attr.applyIcon ?? 'arrowsStartEnd', href }
|
|
38
|
-
html.push(await this.buildTag({ tag: 'btn', attr, html: this.req.t('Apply') }))
|
|
39
|
-
html.push('</form>')
|
|
40
|
-
params.attr.autoClose = 'outside'
|
|
41
|
-
params.html = await this.buildTag({ tag: 'dropdown', attr: params.attr, html: html.join('\n') })
|
|
42
|
-
params.noTag = true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export default btnColumns
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
async function btnDelete (params = {}) {
|
|
2
|
-
const { generateId } = this.plugin.app.bajo
|
|
3
|
-
const { isEmpty, get } = this.plugin.app.bajo.lib._
|
|
4
|
-
params.noTag = true
|
|
5
|
-
const schema = get(this, 'locals.schema', {})
|
|
6
|
-
if (schema.view.disabled.includes('remove')) {
|
|
7
|
-
params.html = ''
|
|
8
|
-
return
|
|
9
|
-
}
|
|
10
|
-
if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Delete')
|
|
11
|
-
params.attr.color = params.attr.color ?? 'danger-outline'
|
|
12
|
-
params.attr.id = generateId('alpha')
|
|
13
|
-
if (params.attr.onList) {
|
|
14
|
-
params.attr.disabled = true
|
|
15
|
-
params.attr['x-data'] = `{
|
|
16
|
-
selected: [],
|
|
17
|
-
remove (ids) {
|
|
18
|
-
wmpa.postForm({ ids }, '${this._buildUrl({ base: 'delete' })}')
|
|
19
|
-
}
|
|
20
|
-
}`
|
|
21
|
-
params.attr['@on-selection.window'] = `
|
|
22
|
-
const el = document.getElementById('${params.attr.id}')
|
|
23
|
-
selected = $event.detail
|
|
24
|
-
if (selected.length > 0) el.classList.remove('disabled')
|
|
25
|
-
else el.classList.add('disabled')
|
|
26
|
-
`
|
|
27
|
-
} else {
|
|
28
|
-
params.attr['x-data'] = `{
|
|
29
|
-
selected: ['${this.req.query.id}'],
|
|
30
|
-
remove (modalId, ids) {
|
|
31
|
-
wmpa.postForm({ ids }, '${this._buildUrl({ base: 'delete', exclude: ['id', 'page'] })}')
|
|
32
|
-
}
|
|
33
|
-
}`
|
|
34
|
-
}
|
|
35
|
-
const msg = 'You\'re about to remove one or more records. Are you really sure to do this?'
|
|
36
|
-
params.attr['@click'] = `
|
|
37
|
-
const opts = selected.join(',')
|
|
38
|
-
const id = await wbs.confirmation(\`${this.req.t(msg)}\`, { ok: '${params.attr.id}:remove', close: 'y', opts })
|
|
39
|
-
`
|
|
40
|
-
params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default btnDelete
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
async function btnDetails (params = {}) {
|
|
2
|
-
const { generateId } = this.plugin.app.bajo
|
|
3
|
-
const { isEmpty, get } = this.plugin.app.bajo.lib._
|
|
4
|
-
params.noTag = true
|
|
5
|
-
const schema = get(this, 'locals.schema', {})
|
|
6
|
-
if (schema.view.disabled.includes('update')) {
|
|
7
|
-
params.html = ''
|
|
8
|
-
return
|
|
9
|
-
}
|
|
10
|
-
if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Details')
|
|
11
|
-
params.attr.color = params.attr.color ?? 'secondary-outline'
|
|
12
|
-
params.attr.id = generateId('alpha')
|
|
13
|
-
if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'details', exclude: ['id'] })
|
|
14
|
-
if (params.attr.onList) {
|
|
15
|
-
params.attr.disabled = true
|
|
16
|
-
params.attr['x-ref'] = 'details'
|
|
17
|
-
params.attr['x-data'] = `{
|
|
18
|
-
path: '${params.attr.href}'
|
|
19
|
-
}`
|
|
20
|
-
params.attr['@on-selection.window'] = `
|
|
21
|
-
const recId = $event.detail[0] ?? ''
|
|
22
|
-
if ($event.detail.length === 1) $refs.details.classList.remove('disabled')
|
|
23
|
-
else $refs.details.classList.add('disabled')
|
|
24
|
-
$refs.details.href = path + '&id=' + recId
|
|
25
|
-
`
|
|
26
|
-
} else {
|
|
27
|
-
params.attr.href += '&id=' + this.req.query.id
|
|
28
|
-
}
|
|
29
|
-
params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default btnDetails
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
async function btnEdit (params = {}) {
|
|
2
|
-
const { generateId } = this.plugin.app.bajo
|
|
3
|
-
const { isEmpty, get } = this.plugin.app.bajo.lib._
|
|
4
|
-
params.noTag = true
|
|
5
|
-
const schema = get(this, 'locals.schema', {})
|
|
6
|
-
if (schema.view.disabled.includes('update')) {
|
|
7
|
-
params.html = ''
|
|
8
|
-
return
|
|
9
|
-
}
|
|
10
|
-
if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Edit')
|
|
11
|
-
params.attr.color = params.attr.color ?? 'secondary-outline'
|
|
12
|
-
params.attr.id = generateId('alpha')
|
|
13
|
-
if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'edit', exclude: ['id'] })
|
|
14
|
-
if (params.attr.onList) {
|
|
15
|
-
params.attr.split = true
|
|
16
|
-
params.attr.disabled = true
|
|
17
|
-
params.attr['x-data'] = `{
|
|
18
|
-
path: '${params.attr.href}'
|
|
19
|
-
}`
|
|
20
|
-
if (params.attr.noClone) {
|
|
21
|
-
params.attr['@on-selection.window'] = `
|
|
22
|
-
const recId = $event.detail[0] ?? ''
|
|
23
|
-
const el = document.getElementById('${params.attr.id}')
|
|
24
|
-
if ($event.detail.length === 1) el.classList.remove('disabled')
|
|
25
|
-
else el.classList.add('disabled')
|
|
26
|
-
el.href = path + '&id=' + recId
|
|
27
|
-
`
|
|
28
|
-
params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
|
|
29
|
-
} else {
|
|
30
|
-
params.attr['@on-selection.window'] = `
|
|
31
|
-
const recId = $event.detail[0] ?? ''
|
|
32
|
-
const elId = '${params.attr.id}'
|
|
33
|
-
for (const id of [elId, elId + '-split']) {
|
|
34
|
-
const el = document.getElementById(id)
|
|
35
|
-
if ($event.detail.length === 1) el.classList.remove('disabled')
|
|
36
|
-
else el.classList.add('disabled')
|
|
37
|
-
const href = path + '&id=' + recId
|
|
38
|
-
if (id.slice(-6) === '-split') {
|
|
39
|
-
const selector = '#' + id.replace('-split', '-menu') + ' a.dropdown-item'
|
|
40
|
-
const item = document.querySelector(selector)
|
|
41
|
-
item.href = href.replace('/edit', '/add') + '&mode=clone'
|
|
42
|
-
} else el.href = href
|
|
43
|
-
}
|
|
44
|
-
`
|
|
45
|
-
const html = [
|
|
46
|
-
await this.buildTag({ tag: 'dropdownItem', attr: { content: this.req.t('Add as New Clone') } })
|
|
47
|
-
]
|
|
48
|
-
params.html = await this.buildTag({ tag: 'dropdown', attr: params.attr, html: html.join('\n') })
|
|
49
|
-
}
|
|
50
|
-
} else {
|
|
51
|
-
params.attr.href += '&id=' + this.req.query.id
|
|
52
|
-
params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default btnEdit
|