waibu-db 1.0.1 → 1.0.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/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 +68 -0
- package/waibuBootstrap/theme/component/factory/pagination.js +69 -0
- package/waibuBootstrap/theme/component/factory/query.js +145 -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
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
async function btnExport (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('find')) {
|
|
6
|
-
params.html = ''
|
|
7
|
-
return
|
|
8
|
-
}
|
|
9
|
-
if (isEmpty(params.attr.launch)) params.attr.launch = this.req.t('Export')
|
|
10
|
-
params.attr.launchColor = params.attr.launchColor ?? 'secondary-outline'
|
|
11
|
-
params.attr.title = this.req.t('Data Export')
|
|
12
|
-
const html = await this.buildSentence(`
|
|
13
|
-
<c:div x-data="{
|
|
14
|
-
delivery: 'clipboard',
|
|
15
|
-
options: [],
|
|
16
|
-
ftype: 'json',
|
|
17
|
-
toggle (val) {
|
|
18
|
-
if (val === 'clipboard') {
|
|
19
|
-
$refs.zip.setAttribute('disabled', '')
|
|
20
|
-
$refs.xlsx.setAttribute('disabled', '')
|
|
21
|
-
$refs.xml.setAttribute('disabled', '')
|
|
22
|
-
_.pull(this.options, 'zip')
|
|
23
|
-
if (!['json', 'csv'].includes(this.ftype)) this.ftype = 'json'
|
|
24
|
-
} else {
|
|
25
|
-
$refs.zip.removeAttribute('disabled')
|
|
26
|
-
$refs.xlsx.removeAttribute('disabled')
|
|
27
|
-
$refs.xml.removeAttribute('disabled')
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
extractForm (selector) {
|
|
31
|
-
let item = {}
|
|
32
|
-
const els = document.querySelectorAll(selector + ' [data-value]')
|
|
33
|
-
for (const el of els) {
|
|
34
|
-
const value = this.options.includes('fvalue') ? el.getAttribute('value') : wmpa.parseValue(el.dataset.value, el.dataset.type)
|
|
35
|
-
let key = el.getAttribute('name')
|
|
36
|
-
if (this.options.includes('fkey')) {
|
|
37
|
-
try {
|
|
38
|
-
const elLabel = document.querySelector('label[for=' + el.getAttribute('id') + ']')
|
|
39
|
-
key = elLabel.innerText
|
|
40
|
-
} catch (err) {}
|
|
41
|
-
}
|
|
42
|
-
item[key] = value
|
|
43
|
-
}
|
|
44
|
-
return this.ftype === 'csv' ? CSVJSON.json2csv(item) : JSON.stringify(item)
|
|
45
|
-
},
|
|
46
|
-
extractTable (selector) {
|
|
47
|
-
let items = []
|
|
48
|
-
let checker = false
|
|
49
|
-
const keys = []
|
|
50
|
-
const types = []
|
|
51
|
-
let els = document.querySelectorAll(selector + ' thead th')
|
|
52
|
-
for (const el of els) {
|
|
53
|
-
keys.push(this.options.includes('fkey') ? el.innerText : el.dataset.key)
|
|
54
|
-
types.push(el.dataset.type)
|
|
55
|
-
}
|
|
56
|
-
if (_.isEmpty(keys[0])) {
|
|
57
|
-
checker = true
|
|
58
|
-
keys.shift()
|
|
59
|
-
types.shift()
|
|
60
|
-
}
|
|
61
|
-
els = document.querySelectorAll(selector + ' tbody tr')
|
|
62
|
-
for (const el of els) {
|
|
63
|
-
let data = []
|
|
64
|
-
_.each(el.children, (v, i) => {
|
|
65
|
-
if ((i + '') === '0' && checker) return undefined
|
|
66
|
-
data.push(this.options.includes('fvalue') ? v.innerText : wmpa.parseValue(v.dataset.value, types[parseInt(i - 1)]))
|
|
67
|
-
})
|
|
68
|
-
const item = {}
|
|
69
|
-
for (const i in keys) {
|
|
70
|
-
item[keys[i]] = data[i]
|
|
71
|
-
}
|
|
72
|
-
items.push(item)
|
|
73
|
-
}
|
|
74
|
-
return this.ftype === 'csv' ? CSVJSON.json2csv(items) : JSON.stringify(items)
|
|
75
|
-
},
|
|
76
|
-
async submit () {
|
|
77
|
-
const instance = wbs.getInstance('Modal', $refs.export)
|
|
78
|
-
const handler = '${params.attr.handler ?? ''}'
|
|
79
|
-
if (this.delivery === 'clipboard') {
|
|
80
|
-
const selector = '${params.attr.selector}'
|
|
81
|
-
if (_.isEmpty(selector)) {
|
|
82
|
-
await wbs.notify('Cant get data selector', { type: 'danger' })
|
|
83
|
-
} else {
|
|
84
|
-
const item = handler === 'list' ? this.extractTable(selector) : this.extractForm(selector)
|
|
85
|
-
await wbs.copyToClipboard(item)
|
|
86
|
-
}
|
|
87
|
-
instance.hide()
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
wmpa.postForm({ options: this.options.join(','), ftype: this.ftype, handler }, '${this._buildUrl({ base: 'export' })}')
|
|
91
|
-
instance.hide()
|
|
92
|
-
}
|
|
93
|
-
}" x-init="
|
|
94
|
-
toggle(delivery)
|
|
95
|
-
$watch('delivery', val => toggle(val))
|
|
96
|
-
">
|
|
97
|
-
<c:grid-row gutter="2">
|
|
98
|
-
<c:grid-col col="6-md">
|
|
99
|
-
<c:fieldset t:legend="Delivery" legend-type="6">
|
|
100
|
-
<c:form-radio x-model="delivery" value="file" t:label="Save as File" />
|
|
101
|
-
<c:form-radio x-model="delivery" value="clipboard" t:label="Copy to Clipboard" />
|
|
102
|
-
</c:fieldset>
|
|
103
|
-
<c:fieldset t:legend="Options" legend-type="6" margin="top-2">
|
|
104
|
-
<c:form-check x-ref="fkey" x-model="options" value="fkey" t:label="Formatted Field" />
|
|
105
|
-
<c:form-check x-ref="fvalue" x-model="options" value="fvalue" t:label="Formatted Value" />
|
|
106
|
-
<c:form-check x-ref="zip" x-model="options" value="zip" t:label="Zipped" />
|
|
107
|
-
</c:fieldset>
|
|
108
|
-
</c:grid-col>
|
|
109
|
-
<c:grid-col col="6-md">
|
|
110
|
-
<c:fieldset t:legend="File Type" legend-type="6">
|
|
111
|
-
<c:form-radio x-ref="xlsx" x-model="ftype" value="xlsx" t:label="Excel XLSX" />
|
|
112
|
-
<c:form-radio x-ref="csv" x-model="ftype" value="csv" t:label="CSV" />
|
|
113
|
-
<c:form-radio x-ref="xml" x-model="ftype" value="xml" t:label="XML" />
|
|
114
|
-
<c:form-radio x-ref="json" x-model="ftype" value="json" t:label="JSON" />
|
|
115
|
-
</c:fieldset />
|
|
116
|
-
</c:grid-col>
|
|
117
|
-
</c:grid-row>
|
|
118
|
-
<c:div flex="justify-content:end" margin="top-3">
|
|
119
|
-
<c:btn color="secondary" t:content="Close" dismiss />
|
|
120
|
-
<c:btn color="primary" t:content="Submit" margin="start-2" @click="await submit()" />
|
|
121
|
-
</c:div>
|
|
122
|
-
</c:div>
|
|
123
|
-
`)
|
|
124
|
-
params.attr['x-data'] = true
|
|
125
|
-
params.attr['x-ref'] = 'export'
|
|
126
|
-
params.html = await this.buildTag({ tag: 'modal', attr: params.attr, html })
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export default btnExport
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
const defOption = {
|
|
2
|
-
grid: {
|
|
3
|
-
top: 8,
|
|
4
|
-
bottom: 20,
|
|
5
|
-
left: 25,
|
|
6
|
-
right: 0
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const chart = {
|
|
11
|
-
scripts: [
|
|
12
|
-
'^waibuDb.virtual:/echarts/echarts.min.js'
|
|
13
|
-
],
|
|
14
|
-
handler: async function (params = {}) {
|
|
15
|
-
const { defaultsDeep, generateId } = this.plugin.app.bajo
|
|
16
|
-
const { base64JsonDecode, jsonStringify } = this.plugin.app.waibuMpa
|
|
17
|
-
const { cloneDeep } = this.plugin.app.bajo.lib._
|
|
18
|
-
this._normalizeAttr(params, { tag: 'div' })
|
|
19
|
-
params.attr.dim = params.attr.dim ?? 'width:100 height:100'
|
|
20
|
-
params.attr.id = generateId('alpha')
|
|
21
|
-
params.attr['x-data'] = `chart${params.attr.id}`
|
|
22
|
-
params.attr['@resize.window.debounce.500ms'] = `
|
|
23
|
-
if (chart) {
|
|
24
|
-
chart.resize()
|
|
25
|
-
}
|
|
26
|
-
`
|
|
27
|
-
let option = cloneDeep(defOption)
|
|
28
|
-
if (params.attr.option === true) params.attr.option = 'e30='
|
|
29
|
-
if (params.attr.option) option = defaultsDeep(base64JsonDecode(params.attr.option), defOption)
|
|
30
|
-
params.attr['x-init'] = `
|
|
31
|
-
$watch('option', val => {
|
|
32
|
-
if (chart) chart.setOption(val)
|
|
33
|
-
})
|
|
34
|
-
`
|
|
35
|
-
params.append = `
|
|
36
|
-
<script>
|
|
37
|
-
document.addEventListener('alpine:init', () => {
|
|
38
|
-
Alpine.data('chart${params.attr.id}', () => {
|
|
39
|
-
let chart
|
|
40
|
-
return {
|
|
41
|
-
init () {
|
|
42
|
-
const el = document.getElementById('${params.attr.id}')
|
|
43
|
-
chart = echarts.init(el, null, { renderer: 'canvas' })
|
|
44
|
-
chart.setOption(this.option)
|
|
45
|
-
},
|
|
46
|
-
get chart () {
|
|
47
|
-
return chart
|
|
48
|
-
},
|
|
49
|
-
option: ${jsonStringify(option, true)}
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
</script>
|
|
54
|
-
`
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export default chart
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export function getUrlOpts (params = {}) {
|
|
2
|
-
const { get } = this.plugin.app.bajo.lib._
|
|
3
|
-
return {
|
|
4
|
-
params,
|
|
5
|
-
excludes: [
|
|
6
|
-
get(this, 'plugin.app.waibu.config.qsKey.lang', 'lang'),
|
|
7
|
-
get(this, 'plugin.app.waibuMpa.config.darkMode.qsKey', 'dark-mode')
|
|
8
|
-
]
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async function pagination (params = {}) {
|
|
13
|
-
const { attrToObject, paginationLayout, groupAttrs } = this.plugin.app.waibuMpa
|
|
14
|
-
const { get, isNumber } = this.plugin.app.bajo.lib._
|
|
15
|
-
const schema = get(this, 'locals.schema', {})
|
|
16
|
-
if (schema.view.disabled.includes('find')) {
|
|
17
|
-
params.html = ''
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
let { count, limit, page } = attrToObject(params.attr.options)
|
|
21
|
-
count = count ?? get(this, 'locals.list.count', 0)
|
|
22
|
-
limit = limit ?? get(this, 'locals.list.limit', 25)
|
|
23
|
-
page = page ?? get(this, 'locals.list.page', 1)
|
|
24
|
-
const pages = paginationLayout(count, limit, page) ?? []
|
|
25
|
-
params.noTag = true
|
|
26
|
-
const group = groupAttrs(params.attr, ['pagination'])
|
|
27
|
-
const html = []
|
|
28
|
-
let icon
|
|
29
|
-
let attr
|
|
30
|
-
if (params.attr.first) {
|
|
31
|
-
icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.firstIcon ?? 'playSkipStart' } })
|
|
32
|
-
attr = { disabled: page <= pages[0], href: this._buildUrl(getUrlOpts.call(this, { page: 1 })) }
|
|
33
|
-
html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
|
|
34
|
-
}
|
|
35
|
-
if (params.attr.prev) {
|
|
36
|
-
icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.prevIcon ?? 'playFastBackward' } })
|
|
37
|
-
attr = { disabled: page <= pages[0], href: this._buildUrl(getUrlOpts.call(this, { page: page - 1 })) }
|
|
38
|
-
html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
|
|
39
|
-
}
|
|
40
|
-
if (!params.attr.noPages) {
|
|
41
|
-
for (const p of pages) {
|
|
42
|
-
attr = { disabled: p === '...', href: this._buildUrl(getUrlOpts.call(this, { page: p })), active: p === page }
|
|
43
|
-
html.push(await this.buildTag({ tag: 'paginationItem', attr, html: isNumber(p) ? this.req.format(p, 'integer') : p }))
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (params.attr.next) {
|
|
47
|
-
icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.nextIcon ?? 'playFastForward' } })
|
|
48
|
-
attr = { disabled: page >= pages[pages.length - 1], href: this._buildUrl(getUrlOpts.call(this, { page: page + 1 })) }
|
|
49
|
-
html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
|
|
50
|
-
}
|
|
51
|
-
if (params.attr.last) {
|
|
52
|
-
icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.lastIcon ?? 'playSkipEnd' } })
|
|
53
|
-
attr = { disabled: page >= pages[pages.length - 1], href: this._buildUrl(getUrlOpts.call(this, { page: pages[pages.length - 1] })) }
|
|
54
|
-
html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
|
|
55
|
-
}
|
|
56
|
-
params.attr = group.pagination
|
|
57
|
-
params.html = await this.buildTag({ tag: 'pagination', attr: params.attr, html: html.join('\n') })
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export default pagination
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
async function query (params = {}) {
|
|
2
|
-
const { generateId } = this.plugin.app.bajo
|
|
3
|
-
const { jsonStringify } = this.plugin.app.waibuMpa
|
|
4
|
-
const { find, get, without, isEmpty, filter, upperFirst } = this.plugin.app.bajo.lib._
|
|
5
|
-
const qsKey = this.plugin.app.waibu.config.qsKey
|
|
6
|
-
const schema = get(this, 'locals.schema', {})
|
|
7
|
-
if (schema.view.disabled.includes('find')) {
|
|
8
|
-
params.html = ''
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
|
|
12
|
-
if (isEmpty(fields)) fields = schema.view.fields
|
|
13
|
-
fields = filter(fields, f => schema.sortables.includes(f))
|
|
14
|
-
const id = generateId('alpha')
|
|
15
|
-
const columns = []
|
|
16
|
-
const models = []
|
|
17
|
-
const selects = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'contains', 'starts', 'ends', '!in', '!contains', '!starts', '!ends']
|
|
18
|
-
for (const f of schema.view.fields) {
|
|
19
|
-
if (!fields.includes(f)) continue
|
|
20
|
-
const prop = find(schema.properties, { name: f })
|
|
21
|
-
const ops = []
|
|
22
|
-
if (['float', 'double', 'integer', 'smallint'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
|
|
23
|
-
else if (['datetime', 'date', 'time'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
|
|
24
|
-
else if (['boolean'].includes(prop.type)) ops.push('eq', 'neq')
|
|
25
|
-
else ops.push(...selects)
|
|
26
|
-
if (ops.length === 0) continue
|
|
27
|
-
const sels = ops.map(o => `<c:option>${o}</c:option>`)
|
|
28
|
-
models.push(`${f}Op: 'eq'`, `${f}Val: ''`)
|
|
29
|
-
columns.push(`
|
|
30
|
-
<c:grid-col col="4-md" flex="align-items:center">
|
|
31
|
-
<c:form-check x-model="selected" t:label="field.${f}" value="${f}" />
|
|
32
|
-
</c:grid-col>
|
|
33
|
-
<c:grid-col col="3-md">
|
|
34
|
-
<c:form-select x-model="${f}Op">
|
|
35
|
-
${sels.join('\n')}
|
|
36
|
-
</c:form-select>
|
|
37
|
-
</c:grid-col>
|
|
38
|
-
<c:grid-col col="5-md">
|
|
39
|
-
<c:form-input x-model="${f}Val" />
|
|
40
|
-
</c:grid-col>
|
|
41
|
-
`)
|
|
42
|
-
}
|
|
43
|
-
params.noTag = true
|
|
44
|
-
const container = params.attr.modal ? 'modal' : 'drawer'
|
|
45
|
-
params.html = await this.buildSentence(`
|
|
46
|
-
<c:form-input type="search" t:placeholder="Query" id="${id}" x-data="{ query: '' }" x-init="
|
|
47
|
-
const url = new URL(window.location.href)
|
|
48
|
-
query = url.searchParams.get('${qsKey.query}') ?? ''
|
|
49
|
-
" x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
|
|
50
|
-
<c:form-input-addon>
|
|
51
|
-
<c:${container} launch-icon="${params.attr.icon ?? 'dotsThree'}" launch-on-end t:title="Query Builder" x-ref="query" x-data="{
|
|
52
|
-
fields: ${jsonStringify(fields, true)},
|
|
53
|
-
builder: '',
|
|
54
|
-
selected: [],
|
|
55
|
-
${models.join(',\n')},
|
|
56
|
-
ops: { eq: ':', neq: ':-', gt: ':>', gte: ':>=', lt: ':<', lte: ':<=' },
|
|
57
|
-
opsIn (v, neg) { return ':' + (neg ? '-' : '') + '[' + this.expandArray(v) + ']' },
|
|
58
|
-
opsExt (v, neg, ext) {
|
|
59
|
-
let prefix = (neg ? '-' : '') + '~'
|
|
60
|
-
if (ext) prefix += ext
|
|
61
|
-
return ':' + prefix + '\\'' + v + '\\''
|
|
62
|
-
},
|
|
63
|
-
expandArray (val = '') {
|
|
64
|
-
return _.map(val.split(','), item => {
|
|
65
|
-
item = _.trim(item)
|
|
66
|
-
if (Number(item)) return item
|
|
67
|
-
return '\\'' + item + '\\''
|
|
68
|
-
})
|
|
69
|
-
},
|
|
70
|
-
rebuild () {
|
|
71
|
-
const items = []
|
|
72
|
-
for (const sel of this.selected) {
|
|
73
|
-
const key = this[sel + 'Op']
|
|
74
|
-
let val = this[sel + 'Val']
|
|
75
|
-
if (_.isEmpty(val)) continue
|
|
76
|
-
let item
|
|
77
|
-
if (key === 'in') item = this.opsIn(val)
|
|
78
|
-
else if (key === '!in') item = this.opsIn(val, true)
|
|
79
|
-
else if (key === 'contains') item = this.opsExt(val)
|
|
80
|
-
else if (key === '!contains') item = this.opsExt(val, true)
|
|
81
|
-
else if (key === 'starts') item = this.opsExt(val, false, '^')
|
|
82
|
-
else if (key === '!starts') item = this.opsExt(val, true, '^')
|
|
83
|
-
else if (key === 'ends') item = this.opsExt(val, false, '$$')
|
|
84
|
-
else if (key === '!ends') item = this.opsExt(val, true, '$$')
|
|
85
|
-
else if (val.includes(' ')) item = this.ops[key] + '\\'' + val + '\\''
|
|
86
|
-
else item = this.ops[key] + val
|
|
87
|
-
items.push(sel + item)
|
|
88
|
-
}
|
|
89
|
-
this.builder = items.join('+')
|
|
90
|
-
},
|
|
91
|
-
submit (run) {
|
|
92
|
-
if (run) {
|
|
93
|
-
const url = new URL(window.location.href)
|
|
94
|
-
const params = new URLSearchParams(url.search)
|
|
95
|
-
params.set('${qsKey.page}', 1)
|
|
96
|
-
params.set('${qsKey.query}', this.builder ?? '')
|
|
97
|
-
window.location.href = '?' + params.toString()
|
|
98
|
-
} else $dispatch('on-query', this.builder)
|
|
99
|
-
const instance = wbs.getInstance('${upperFirst(container)}', $refs.query)
|
|
100
|
-
instance.hide()
|
|
101
|
-
}
|
|
102
|
-
}" x-init="
|
|
103
|
-
const ops = _.map(fields, f => (f + 'Op'))
|
|
104
|
-
const vals = _.map(fields, f => (f + 'Val'))
|
|
105
|
-
const watcher = ['selected', ...ops, ...vals].join(',')
|
|
106
|
-
$watch(watcher, v => rebuild())
|
|
107
|
-
">
|
|
108
|
-
<c:grid-row gutter="2">
|
|
109
|
-
<c:grid-col col="12">
|
|
110
|
-
<c:form-textarea x-model="builder" readonly rows="4"/>
|
|
111
|
-
</c:grid-col>
|
|
112
|
-
${columns.join('\n')}
|
|
113
|
-
</c:grid-row>
|
|
114
|
-
<c:div flex="justify-content:end" margin="top-3">
|
|
115
|
-
<c:btn color="secondary" t:content="Close" dismiss="${container}" />
|
|
116
|
-
<c:btn color="primary" t:content="Apply" margin="start-2" @click="submit()" />
|
|
117
|
-
<c:btn color="primary" t:content="Submit Query" margin="start-2" @click="submit(true)" />
|
|
118
|
-
</c:div>
|
|
119
|
-
</c:${container}>
|
|
120
|
-
</c:form-input-addon>
|
|
121
|
-
<c:form-input-addon>
|
|
122
|
-
<c:btn t:content="Submit" x-data="{
|
|
123
|
-
submit () {
|
|
124
|
-
const val = document.getElementById('${id}').value ?? ''
|
|
125
|
-
const url = new URL(window.location.href)
|
|
126
|
-
const params = new URLSearchParams(url.search)
|
|
127
|
-
params.set('${qsKey.page}', 1)
|
|
128
|
-
params.set('${qsKey.query}', val)
|
|
129
|
-
window.location.href = '?' + params.toString()
|
|
130
|
-
}
|
|
131
|
-
}" @click="submit" @on-submit.window="submit()" />
|
|
132
|
-
</c:form-input-addon>
|
|
133
|
-
</c:form-input>
|
|
134
|
-
`)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export default query
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { getUrlOpts } from './pagination.js'
|
|
2
|
-
|
|
3
|
-
async function recordsInfo (params = {}) {
|
|
4
|
-
const { attrToObject, groupAttrs, attrToArray } = this.plugin.app.waibuMpa
|
|
5
|
-
const { get, isEmpty, omit, merge } = this.plugin.app.bajo.lib._
|
|
6
|
-
const schema = get(this, 'locals.schema', {})
|
|
7
|
-
if (schema.view.disabled.includes('find')) {
|
|
8
|
-
params.html = ''
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
let { count, limit, page, pages } = attrToObject(params.attr.options)
|
|
12
|
-
count = count ?? get(this, 'locals.list.count', 0)
|
|
13
|
-
page = page ?? get(this, 'locals.list.page', 1)
|
|
14
|
-
limit = limit ?? get(this, 'locals.list.limit', 25)
|
|
15
|
-
pages = pages ?? get(this, 'locals.list.pages', 0)
|
|
16
|
-
|
|
17
|
-
params.tag = 'div'
|
|
18
|
-
params.attr.flex = 'justify-center:start align-items:center'
|
|
19
|
-
if (count === 0) {
|
|
20
|
-
params.html = this.req.t('No record found')
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
if (!params.attr.dropdown) params.attr.dropdown = true
|
|
24
|
-
const group = groupAttrs(params.attr, ['dropdown'])
|
|
25
|
-
const html = []
|
|
26
|
-
if (params.attr.count) html.push(this.req.t('%s record(s) found', this.req.format(count, 'integer')))
|
|
27
|
-
if (params.attr.pages) {
|
|
28
|
-
if (!isEmpty(html)) html[html.length - 1] += '.'
|
|
29
|
-
html.push(this.req.t('Page %s of %s pages', this.req.format(page, 'integer'), this.req.format(pages, 'integer')))
|
|
30
|
-
}
|
|
31
|
-
if (params.attr.recsPerPage) {
|
|
32
|
-
params.attr.recsPerPageValues = params.attr.recsPerPageValues ?? '10 25 50'
|
|
33
|
-
params.attr.recsPerPageValues = attrToArray(params.attr.recsPerPageValues)
|
|
34
|
-
if (!isEmpty(html)) html[html.length - 1] += ','
|
|
35
|
-
const items = []
|
|
36
|
-
for (const i of params.attr.recsPerPageValues) {
|
|
37
|
-
const attr = { href: this._buildUrl(merge(getUrlOpts.call(this), { params: { limit: i, page: 1 } })), disabled: i === limit }
|
|
38
|
-
items.push(await this.buildTag({ tag: 'dropdownItem', attr, html: i + '' }))
|
|
39
|
-
}
|
|
40
|
-
const attr = group.dropdown
|
|
41
|
-
attr.content = limit + ''
|
|
42
|
-
attr.color = attr.color ?? 'secondary-outline'
|
|
43
|
-
html.push(await this.buildTag({ tag: 'dropdown', attr, html: items.join('\n') }))
|
|
44
|
-
html.push(' ', this.req.t('recs per page'))
|
|
45
|
-
}
|
|
46
|
-
params.attr = omit(params.attr, ['count', 'pages', 'recsPerPage', 'dropdown', 'recsPerPageValues'])
|
|
47
|
-
params.html = html.map(h => `<div class="me-1">${h}</div>`).join('\n')
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export default recordsInfo
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
function isRightAligned (type) {
|
|
2
|
-
return ['smallint', 'integer', 'float', 'double'].includes(type)
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
const table = {
|
|
6
|
-
handler: async function (params = {}) {
|
|
7
|
-
const { escape } = this.plugin.app.waibu
|
|
8
|
-
const { attrToArray, groupAttrs } = this.plugin.app.waibuMpa
|
|
9
|
-
const { get, omit, set, find, isEmpty, without } = this.plugin.app.bajo.lib._
|
|
10
|
-
const group = groupAttrs(params.attr, ['body', 'head', 'foot'])
|
|
11
|
-
params.attr = group._
|
|
12
|
-
|
|
13
|
-
const data = get(this, 'locals.list.data', [])
|
|
14
|
-
const schema = get(this, 'locals.schema', {})
|
|
15
|
-
if (schema.view.disabled.includes('find')) {
|
|
16
|
-
params.html = ''
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
const qsKey = this.plugin.app.waibu.config.qsKey
|
|
20
|
-
let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
|
|
21
|
-
if (isEmpty(fields)) fields = schema.view.fields
|
|
22
|
-
const sort = params.attr.sort ? attrToArray(params.attr.sort) : get(this, `locals._meta.query.${qsKey.sort}`, '')
|
|
23
|
-
|
|
24
|
-
let [sortCol, sortDir] = sort.split(':')
|
|
25
|
-
if (!['-1', '1'].includes(sortDir)) sortDir = '1'
|
|
26
|
-
|
|
27
|
-
let selection
|
|
28
|
-
const canDelete = !schema.view.disabled.includes('remove')
|
|
29
|
-
const canEdit = !schema.view.disabled.includes('update')
|
|
30
|
-
if (canEdit) selection = 'single'
|
|
31
|
-
if (canDelete) selection = 'multi'
|
|
32
|
-
if (selection) params.attr.hover = true
|
|
33
|
-
|
|
34
|
-
params.noTag = true
|
|
35
|
-
const html = []
|
|
36
|
-
let items = []
|
|
37
|
-
// head
|
|
38
|
-
for (const f of schema.view.fields) {
|
|
39
|
-
if (!fields.includes(f)) continue
|
|
40
|
-
const prop = find(schema.properties, { name: f })
|
|
41
|
-
let head = this.req.t(`field.${f}`)
|
|
42
|
-
if (!params.attr.noSort && (schema.sortables ?? []).includes(f)) {
|
|
43
|
-
let sortItem = `${f}:-1`
|
|
44
|
-
let icon = params.attr.sortUpIcon ?? 'caretUp'
|
|
45
|
-
if (f === sortCol) {
|
|
46
|
-
sortItem = `${f}:${sortDir === '1' ? '-1' : '1'}`
|
|
47
|
-
icon = sortDir === '1' ? (params.attr.sortUpIcon ?? 'caretUp') : (params.attr.sortDownIcon ?? 'caretDown')
|
|
48
|
-
}
|
|
49
|
-
const item = set({ page: 1 }, qsKey.sort, sortItem)
|
|
50
|
-
const href = this._buildUrl({ params: item })
|
|
51
|
-
const attr = isRightAligned(prop.type) ? { text: 'align:end' } : {}
|
|
52
|
-
const content = [
|
|
53
|
-
await this.buildTag({ tag: 'div', attr, html: this.req.t(`field.${f}`) }),
|
|
54
|
-
await this.buildTag({ tag: 'a', attr: { icon, href }, prepend: '<div class="ms-1">', append: '</div>' })
|
|
55
|
-
]
|
|
56
|
-
head = await this.buildTag({ tag: 'div', attr: { flex: 'justify-content:between align-items:end' }, html: content.join('\n') })
|
|
57
|
-
}
|
|
58
|
-
let text = params.attr.headerNowrap ? '' : 'nowrap'
|
|
59
|
-
if (isRightAligned(prop.type)) text += ' align:end'
|
|
60
|
-
const attr = { dataKey: f, dataType: prop.type, text }
|
|
61
|
-
items.push(await this.buildTag({ tag: 'th', attr, html: head }))
|
|
62
|
-
}
|
|
63
|
-
if (items.length > 0 && selection) {
|
|
64
|
-
let item = '<th></th>'
|
|
65
|
-
if (selection === 'multi') {
|
|
66
|
-
const attr = { 'x-model': 'toggleAll', name: '_rtm', noWrapper: true, noLabel: true }
|
|
67
|
-
item = await this.buildTag({ tag: 'formCheck', attr, prepend: '<th>', append: '</th>' })
|
|
68
|
-
} else {
|
|
69
|
-
const attr = { name: 'remove', '@click': 'selected = \'\'', style: { cursor: 'pointer' } }
|
|
70
|
-
item = await this.buildTag({ tag: 'icon', attr, prepend: '<th>', append: '</th>' })
|
|
71
|
-
}
|
|
72
|
-
items.unshift(item)
|
|
73
|
-
}
|
|
74
|
-
const header = await this.buildTag({ tag: 'tr', html: items.join('\n') })
|
|
75
|
-
html.push(await this.buildTag({ tag: 'thead', attr: group.head, html: header }))
|
|
76
|
-
// body
|
|
77
|
-
items = []
|
|
78
|
-
for (const d of data) {
|
|
79
|
-
const lines = []
|
|
80
|
-
if (selection) {
|
|
81
|
-
const tag = selection === 'single' ? 'formRadio' : 'formCheck'
|
|
82
|
-
const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
|
|
83
|
-
lines.push(await this.buildTag({ tag, attr, prepend: '<td>', append: '</td>' }))
|
|
84
|
-
}
|
|
85
|
-
for (const f of schema.view.fields) {
|
|
86
|
-
const prop = find(schema.properties, { name: f })
|
|
87
|
-
if (!fields.includes(f)) continue
|
|
88
|
-
const opts = {}
|
|
89
|
-
if (f === 'lng') opts.longitude = true
|
|
90
|
-
else if (f === 'lat') opts.latitude = true
|
|
91
|
-
let value = this.req.format(d[f], prop.type, opts)
|
|
92
|
-
if (prop.type === 'boolean') {
|
|
93
|
-
value = (await this.buildTag({ tag: 'icon', attr: { name: `circle${d[f] ? 'Check' : ''}` } })) +
|
|
94
|
-
' ' + (this.req.t(d[f] ? 'Yes' : 'No'))
|
|
95
|
-
} else value = escape(value)
|
|
96
|
-
let dataValue = d[f] ?? ''
|
|
97
|
-
if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
|
|
98
|
-
if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
|
|
99
|
-
const attr = { dataValue }
|
|
100
|
-
if (!['object', 'array'].includes(prop.type)) {
|
|
101
|
-
if (isRightAligned(prop.type)) attr.text = 'align:end nowrap'
|
|
102
|
-
else attr.text = 'nowrap'
|
|
103
|
-
}
|
|
104
|
-
const line = await this.buildTag({ tag: 'td', attr, html: value })
|
|
105
|
-
lines.push(line)
|
|
106
|
-
}
|
|
107
|
-
const attr = {}
|
|
108
|
-
if (!schema.view.disabled.includes('update') || !schema.view.disabled.includes('remove')) attr['@click'] = `toggle('${d.id}')`
|
|
109
|
-
if (!schema.view.disabled.includes('get')) attr['@dblclick'] = `goDetails('${d.id}')`
|
|
110
|
-
items.push(await this.buildTag({ tag: 'tr', attr, html: lines.join('\n') }))
|
|
111
|
-
}
|
|
112
|
-
html.push(await this.buildTag({ tag: 'tbody', attr: group.body, html: items.join('\n') }))
|
|
113
|
-
params.attr = omit(params.attr, ['sortUpIcon', 'sortDownIcon', 'noSort', 'selection', 'headerNowrap'])
|
|
114
|
-
const goDetails = `
|
|
115
|
-
goDetails (id) {
|
|
116
|
-
window.location.href = '${this._buildUrl({ base: 'details' })}&id=' + id
|
|
117
|
-
}
|
|
118
|
-
`
|
|
119
|
-
if (selection === 'multi') {
|
|
120
|
-
params.attr['x-data'] = `{
|
|
121
|
-
toggleAll: false,
|
|
122
|
-
selected: [],
|
|
123
|
-
toggle (id) {
|
|
124
|
-
if (this.selected.includes(id)) {
|
|
125
|
-
const idx = this.selected.indexOf(id)
|
|
126
|
-
this.selected.splice(idx, 1)
|
|
127
|
-
} else this.selected.push(id)
|
|
128
|
-
},
|
|
129
|
-
${goDetails}
|
|
130
|
-
}`
|
|
131
|
-
params.attr['x-init'] = `
|
|
132
|
-
$watch('toggleAll', val => {
|
|
133
|
-
if (val) {
|
|
134
|
-
const els = document.getElementsByName('_rt')
|
|
135
|
-
const items = Array.from(els)
|
|
136
|
-
selected = items.map(el => el.value)
|
|
137
|
-
} else selected = []
|
|
138
|
-
})
|
|
139
|
-
$watch('selected', val => $dispatch('on-selection', val))
|
|
140
|
-
`
|
|
141
|
-
} else if (selection === 'single') {
|
|
142
|
-
params.attr['x-data'] = `{
|
|
143
|
-
selected: '',
|
|
144
|
-
toggle (id) {
|
|
145
|
-
this.selected = id
|
|
146
|
-
},
|
|
147
|
-
${goDetails}
|
|
148
|
-
}`
|
|
149
|
-
params.attr['x-init'] = `
|
|
150
|
-
$watch('selected', val => $dispatch('on-selection', [val]))
|
|
151
|
-
`
|
|
152
|
-
} else {
|
|
153
|
-
params.attr['x-data'] = `{
|
|
154
|
-
${goDetails}
|
|
155
|
-
}`
|
|
156
|
-
}
|
|
157
|
-
params.html = await this.buildTag({ tag: 'table', attr: params.attr, html: html.join('\n') })
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export default table
|