@afeefa/vue-app 0.0.43 → 0.0.47
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/.afeefa/package/release/version.txt +1 -1
- package/package.json +1 -1
- package/src/api-resources/ApiActions.js +4 -55
- package/src/components/APagination.vue +0 -4
- package/src/components/ARow.vue +1 -3
- package/src/components/ASearchSelect.vue +2 -2
- package/src/components/ATableHeader.vue +1 -1
- package/src/components/list/FilterSourceType.js +10 -0
- package/src/components/list/ListViewMixin.js +16 -17
- package/src/components/list/ListViewRequest.js +124 -0
- package/src/components/list/{RouteParamsFilterSource.js → RouteQueryFilterSource.js} +1 -1
- package/src/plugins/api-resources/ApiResourcesPlugin.js +13 -0
- package/src-admin/bootstrap.js +3 -1
- package/src-admin/components/{Index.vue → Start.vue} +0 -0
- package/src-admin/components/detail/DetailProperty.vue +5 -5
- package/src-admin/components/index.js +2 -2
- package/src-admin/components/list/ListView.vue +35 -49
- package/src-admin/components/pages/CreatePage.vue +6 -9
- package/src-admin/components/pages/DetailPage.vue +2 -2
- package/src-admin/components/pages/EditPage.vue +7 -10
- package/src-admin/components/pages/ListPage.vue +7 -122
- package/src-admin/components/routes/DetailRoute.vue +1 -7
- package/src-admin/components/routes/EditRoute.vue +1 -7
- package/src-admin/components/routes/ListRoute.vue +9 -10
- package/src-admin/fonts/ibm-plex-sans-v9-latin-700.woff +0 -0
- package/src-admin/fonts/ibm-plex-sans-v9-latin-700.woff2 +0 -0
- package/src-admin/fonts/ibm-plex-sans-v9-latin-700italic.woff +0 -0
- package/src-admin/fonts/ibm-plex-sans-v9-latin-700italic.woff2 +0 -0
- package/src-admin/fonts/ibm-plex-sans-v9-latin-italic.woff +0 -0
- package/src-admin/fonts/ibm-plex-sans-v9-latin-italic.woff2 +0 -0
- package/src-admin/fonts/ibm-plex-sans-v9-latin-regular.woff +0 -0
- package/src-admin/fonts/ibm-plex-sans-v9-latin-regular.woff2 +0 -0
- package/src-admin/models/Model.js +10 -7
- package/src-admin/styles/ibm-plex-sans.scss +43 -0
- package/src/components/list/QuerySourceType.js +0 -4
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.47
|
package/package.json
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
import { RouteParamsFilterSource } from '@a-vue/components/list/RouteParamsFilterSource'
|
2
1
|
import { AlertEvent, DialogEvent, LoadingEvent, SaveEvent } from '@a-vue/events'
|
3
2
|
import { eventBus } from '@a-vue/plugins/event-bus/EventBus'
|
4
3
|
import { sleep } from '@a-vue/utils/timeout'
|
5
|
-
import { RequestFilters } from '@afeefa/api-resources-client'
|
6
4
|
|
7
5
|
export class GetAction {
|
8
6
|
action = null
|
@@ -35,7 +33,7 @@ export class GetAction {
|
|
35
33
|
eventBus.dispatch(new LoadingEvent(LoadingEvent.START_LOADING))
|
36
34
|
}
|
37
35
|
|
38
|
-
const result = await this.action.
|
36
|
+
const result = await this.action.createRequest()
|
39
37
|
.params({
|
40
38
|
id: this.id
|
41
39
|
})
|
@@ -111,7 +109,7 @@ export class SaveAction {
|
|
111
109
|
|
112
110
|
const startTime = Date.now()
|
113
111
|
|
114
|
-
const result = await this.action.
|
112
|
+
const result = await this.action.createRequest()
|
115
113
|
.params({
|
116
114
|
id: this.id || undefined
|
117
115
|
})
|
@@ -195,7 +193,7 @@ export class RemoveAction {
|
|
195
193
|
|
196
194
|
const startTime = Date.now()
|
197
195
|
|
198
|
-
const result = await this.action.
|
196
|
+
const result = await this.action.createRequest()
|
199
197
|
.params({
|
200
198
|
id: this.id
|
201
199
|
})
|
@@ -230,11 +228,6 @@ export class RemoveAction {
|
|
230
228
|
|
231
229
|
export class ListAction {
|
232
230
|
request = null
|
233
|
-
action = null
|
234
|
-
fields = null
|
235
|
-
scopes = {}
|
236
|
-
filters = {}
|
237
|
-
route = null
|
238
231
|
events = true
|
239
232
|
|
240
233
|
setRequest (request) {
|
@@ -242,31 +235,6 @@ export class ListAction {
|
|
242
235
|
return this
|
243
236
|
}
|
244
237
|
|
245
|
-
setAction (action) {
|
246
|
-
this.action = action
|
247
|
-
return this
|
248
|
-
}
|
249
|
-
|
250
|
-
setFiltersForRoute (route) {
|
251
|
-
this.route = route
|
252
|
-
return this
|
253
|
-
}
|
254
|
-
|
255
|
-
setFields (fields) {
|
256
|
-
this.fields = fields
|
257
|
-
return this
|
258
|
-
}
|
259
|
-
|
260
|
-
setScopes (scopes) {
|
261
|
-
this.scopes = scopes
|
262
|
-
return this
|
263
|
-
}
|
264
|
-
|
265
|
-
setFilters (filters) {
|
266
|
-
this.filters = filters
|
267
|
-
return this
|
268
|
-
}
|
269
|
-
|
270
238
|
noEvents (noEvents) {
|
271
239
|
this.events = noEvents === undefined ? false : !noEvents
|
272
240
|
return this
|
@@ -277,26 +245,7 @@ export class ListAction {
|
|
277
245
|
eventBus.dispatch(new LoadingEvent(LoadingEvent.START_LOADING))
|
278
246
|
}
|
279
247
|
|
280
|
-
|
281
|
-
|
282
|
-
if (this.route) {
|
283
|
-
const querySource = new RouteParamsFilterSource(this.route)
|
284
|
-
const requestFilters = this.action.createRequestFilters(null, querySource)
|
285
|
-
const storedFilters = RequestFilters.fromHistory(this.route.path)
|
286
|
-
if (storedFilters) {
|
287
|
-
requestFilters.initFromUsed(storedFilters.serialize(), 1)
|
288
|
-
}
|
289
|
-
filters = requestFilters.serialize()
|
290
|
-
}
|
291
|
-
|
292
|
-
const request = this.request ||
|
293
|
-
this.action
|
294
|
-
.request()
|
295
|
-
.scopes(this.scopes)
|
296
|
-
.filters(filters)
|
297
|
-
.fields(this.fields)
|
298
|
-
|
299
|
-
const result = await request.send()
|
248
|
+
const result = await this.request.send()
|
300
249
|
|
301
250
|
if (result.error) {
|
302
251
|
if (this.events) {
|
package/src/components/ARow.vue
CHANGED
@@ -93,7 +93,7 @@ import { Component, Watch, Mixins } from 'vue-property-decorator'
|
|
93
93
|
import { UsesPositionServiceMixin } from '../services/position/UsesPositionServiceMixin'
|
94
94
|
import { PositionConfig } from '../services/PositionService'
|
95
95
|
import { randomCssClass } from '../utils/random'
|
96
|
-
import {
|
96
|
+
import { FilterSourceType } from '@a-vue/components/list/FilterSourceType'
|
97
97
|
import SearchSelectFilters from './search-select/SearchSelectFilters'
|
98
98
|
import SearchSelectList from './search-select/SearchSelectList'
|
99
99
|
import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
|
@@ -109,7 +109,7 @@ export default class ASearchSelect extends Mixins(UsesPositionServiceMixin, Canc
|
|
109
109
|
selectId = randomCssClass(10)
|
110
110
|
isOpen = false
|
111
111
|
items_ = []
|
112
|
-
filterSource =
|
112
|
+
filterSource = FilterSourceType.OBJECT
|
113
113
|
|
114
114
|
isLoading = false
|
115
115
|
filters = []
|
@@ -0,0 +1,10 @@
|
|
1
|
+
export class FilterSourceType {
|
2
|
+
// filters are synchronized with url's query string
|
3
|
+
static QUERY_STRING = 'query_string'
|
4
|
+
|
5
|
+
// filters are initialized from given route params (no synchronisation allowed)
|
6
|
+
static ROUTE_PARAMS = 'route_params'
|
7
|
+
|
8
|
+
// filters are synchronized with plain history object
|
9
|
+
static OBJECT = 'object'
|
10
|
+
}
|
@@ -1,14 +1,15 @@
|
|
1
1
|
import { ListAction } from '@a-vue/api-resources/ApiActions'
|
2
2
|
import { Component, Vue, Watch } from 'vue-property-decorator'
|
3
3
|
|
4
|
-
import {
|
4
|
+
import { FilterSourceType } from './FilterSourceType'
|
5
5
|
import { RouteFilterSource } from './RouteFilterSource'
|
6
6
|
|
7
7
|
@Component({
|
8
8
|
props: [
|
9
|
-
'models', 'meta',
|
10
|
-
'
|
11
|
-
'noEvents', 'noHistory',
|
9
|
+
'models', 'meta', // if already loaded
|
10
|
+
'listViewRequest',
|
11
|
+
'noEvents', 'noHistory',
|
12
|
+
'filterHistoryKey', 'filterSource',
|
12
13
|
'loadOnlyIfKeyword'
|
13
14
|
]
|
14
15
|
})
|
@@ -35,19 +36,16 @@ export class ListViewMixin extends Vue {
|
|
35
36
|
}
|
36
37
|
|
37
38
|
if (this.requestFilters) {
|
39
|
+
// this can happen only on HMR-reload
|
38
40
|
this.requestFilters.off('change', this.filtersChanged)
|
39
41
|
}
|
40
42
|
|
41
43
|
const historyKey = this.noHistory
|
42
44
|
? undefined
|
43
45
|
: [this.$route.path, this.filterHistoryKey].filter(i => i).join('.')
|
44
|
-
const
|
45
|
-
|
46
|
-
|
47
|
-
console.log('Initial Filters: ', this.initialFilters)
|
48
|
-
if (this.initialFilters) {
|
49
|
-
this.requestFilters.initFromUsed(this.initialFilters)
|
50
|
-
}
|
46
|
+
const filterSource = this.filterSource === FilterSourceType.OBJECT ? undefined : new RouteFilterSource(this.$router)
|
47
|
+
const action = this.listViewRequest.getAction()
|
48
|
+
this.requestFilters = action.createRequestFilters(historyKey, filterSource)
|
51
49
|
|
52
50
|
this.requestFilters.on('change', this.filtersChanged)
|
53
51
|
|
@@ -64,8 +62,8 @@ export class ListViewMixin extends Vue {
|
|
64
62
|
|
65
63
|
@Watch('$route.query')
|
66
64
|
routeQueryChanged () {
|
67
|
-
if (this.filterSource !==
|
68
|
-
this.requestFilters.
|
65
|
+
if (this.filterSource !== FilterSourceType.QUERY_STRING) {
|
66
|
+
this.requestFilters.filterSourceChanged()
|
69
67
|
}
|
70
68
|
}
|
71
69
|
|
@@ -109,11 +107,12 @@ export class ListViewMixin extends Vue {
|
|
109
107
|
this.isLoading = true
|
110
108
|
this.$emit('update:isLoading', this.isLoading)
|
111
109
|
|
110
|
+
const request = this.listViewRequest
|
111
|
+
.filters(this.requestFilters.serialize())
|
112
|
+
.toApiRequest()
|
113
|
+
|
112
114
|
const {models, meta} = await new ListAction()
|
113
|
-
.
|
114
|
-
.setFields(this.fields)
|
115
|
-
.setScopes(this.scopes)
|
116
|
-
.setFilters(this.requestFilters.serialize())
|
115
|
+
.setRequest(request)
|
117
116
|
.noEvents(this.noEvents)
|
118
117
|
.load()
|
119
118
|
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import { RequestFilters, apiResources } from '@afeefa/api-resources-client'
|
2
|
+
|
3
|
+
export class ListViewRequest {
|
4
|
+
_api = {}
|
5
|
+
_resource = {}
|
6
|
+
_action = {}
|
7
|
+
|
8
|
+
_params = {}
|
9
|
+
_filters = {}
|
10
|
+
_fields = {}
|
11
|
+
|
12
|
+
_filterSource = null
|
13
|
+
_historyKey = null
|
14
|
+
|
15
|
+
action ({api, resource, action}) {
|
16
|
+
this._api = api
|
17
|
+
this._resource = resource
|
18
|
+
this._action = action
|
19
|
+
return this
|
20
|
+
}
|
21
|
+
|
22
|
+
getAction () {
|
23
|
+
return apiResources.getAction({
|
24
|
+
api: this._api,
|
25
|
+
resource: this._resource,
|
26
|
+
action: this._action
|
27
|
+
})
|
28
|
+
}
|
29
|
+
|
30
|
+
params (params) {
|
31
|
+
this._params = params
|
32
|
+
return this
|
33
|
+
}
|
34
|
+
|
35
|
+
getParams () {
|
36
|
+
return this._params
|
37
|
+
}
|
38
|
+
|
39
|
+
filters (filters) {
|
40
|
+
this._filters = filters
|
41
|
+
return this
|
42
|
+
}
|
43
|
+
|
44
|
+
getFilters () {
|
45
|
+
return this._filters
|
46
|
+
}
|
47
|
+
|
48
|
+
fields (fields) {
|
49
|
+
this._fields = fields
|
50
|
+
return this
|
51
|
+
}
|
52
|
+
|
53
|
+
getFields () {
|
54
|
+
return this._fields
|
55
|
+
}
|
56
|
+
|
57
|
+
initFromFilterSource (filterSource) {
|
58
|
+
this._filterSource = filterSource
|
59
|
+
return this
|
60
|
+
}
|
61
|
+
|
62
|
+
recreateFromHistory (historyKey) {
|
63
|
+
this._historyKey = historyKey
|
64
|
+
return this
|
65
|
+
}
|
66
|
+
|
67
|
+
clone () {
|
68
|
+
const request = new ListViewRequest()
|
69
|
+
request._api = this._api
|
70
|
+
request._resource = this._resource
|
71
|
+
request._action = this._action
|
72
|
+
request._params = this._params
|
73
|
+
request._filters = this._filters
|
74
|
+
request._fields = this._fields
|
75
|
+
return request
|
76
|
+
}
|
77
|
+
|
78
|
+
toApiRequest () {
|
79
|
+
const action = this.getAction({
|
80
|
+
api: this._api,
|
81
|
+
resource: this._resource,
|
82
|
+
action: this._action
|
83
|
+
})
|
84
|
+
|
85
|
+
const request = action.createRequest()
|
86
|
+
.params(this._params)
|
87
|
+
.fields(this._fields)
|
88
|
+
|
89
|
+
// filters set on the request will be skipped if filterSource or historyKey are given
|
90
|
+
// and produce results
|
91
|
+
let filtersToUse = this._filters
|
92
|
+
let useHistory = true
|
93
|
+
|
94
|
+
// create and init request filters based on the current filter source state
|
95
|
+
if (this._filterSource) {
|
96
|
+
const requestFilters = action.createRequestFilters(null, this._filterSource)
|
97
|
+
const requestFiltersToUse = requestFilters.serialize()
|
98
|
+
// do not use history if any filter given by the given filter source
|
99
|
+
if (Object.keys(requestFiltersToUse).length) {
|
100
|
+
useHistory = false
|
101
|
+
}
|
102
|
+
|
103
|
+
filtersToUse = {
|
104
|
+
...filtersToUse,
|
105
|
+
...requestFiltersToUse
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
// if any filters is set by current filter source
|
110
|
+
// skip history and give that filter precedence
|
111
|
+
// otherwise: set stored filters
|
112
|
+
if (useHistory && this._historyKey) {
|
113
|
+
// check any already stored filters from a previous request
|
114
|
+
const storedFilters = RequestFilters.fromHistory(this._historyKey)
|
115
|
+
if (storedFilters) {
|
116
|
+
filtersToUse = storedFilters.serialize()
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
request.filters(filtersToUse)
|
121
|
+
|
122
|
+
return request
|
123
|
+
}
|
124
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { apiResources } from '@afeefa/api-resources-client'
|
2
|
+
|
3
|
+
class ApiResourcesPlugin {
|
4
|
+
install (Vue) {
|
5
|
+
Object.defineProperty(Vue.prototype, '$apiResources', {
|
6
|
+
get () {
|
7
|
+
return apiResources
|
8
|
+
}
|
9
|
+
})
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
export const apiResourcesPlugin = new ApiResourcesPlugin()
|
package/src-admin/bootstrap.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import './config/event-bus'
|
2
2
|
import './config/components'
|
3
3
|
|
4
|
+
import { apiResourcesPlugin } from '@a-vue/plugins/api-resources/ApiResourcesPlugin'
|
4
5
|
import { hasOptionsPlugin } from '@a-vue/plugins/has-options/HasOptionsPlugin'
|
5
6
|
import { timeout } from '@a-vue/utils/timeout'
|
6
7
|
import { apiResources } from '@afeefa/api-resources-client'
|
@@ -10,6 +11,7 @@ import { appConfig } from './config/AppConfig'
|
|
10
11
|
import routeConfigPlugin from './config/routing'
|
11
12
|
import vuetify from './config/vuetify'
|
12
13
|
|
14
|
+
Vue.use(apiResourcesPlugin)
|
13
15
|
Vue.use(hasOptionsPlugin)
|
14
16
|
|
15
17
|
Vue.config.productionTip = false
|
@@ -48,7 +50,7 @@ export async function bootstrap ({ apis, models, routing, authService, app, comp
|
|
48
50
|
vuetify,
|
49
51
|
router,
|
50
52
|
el: '#app',
|
51
|
-
template: '<
|
53
|
+
template: '<start :splash="splash" />',
|
52
54
|
data: {
|
53
55
|
splash
|
54
56
|
}
|
File without changes
|
@@ -8,10 +8,10 @@
|
|
8
8
|
>
|
9
9
|
{{ _icon.icon }}
|
10
10
|
</v-icon>
|
11
|
-
<label :class="['label', {'label--withIcon':
|
11
|
+
<label :class="['label', {'label--withIcon': _icon != null}]">{{ label }}</label>
|
12
12
|
</div>
|
13
13
|
|
14
|
-
<div :class="['content', {'content--withIcon':
|
14
|
+
<div :class="['content', {'content--withIcon': _icon != null}]">
|
15
15
|
<a-row
|
16
16
|
vertical
|
17
17
|
gap="6"
|
@@ -37,8 +37,8 @@ export default class DetailProperty extends Vue {
|
|
37
37
|
}
|
38
38
|
|
39
39
|
if (this.iconModelType) {
|
40
|
-
const
|
41
|
-
return
|
40
|
+
const ModelClass = apiResources.getModelClass(this.iconModelType)
|
41
|
+
return ModelClass.icon
|
42
42
|
}
|
43
43
|
}
|
44
44
|
}
|
@@ -64,7 +64,7 @@ export default class DetailProperty extends Vue {
|
|
64
64
|
@media (max-width: 900px), (orientation : portrait) {
|
65
65
|
padding-left: 55px;
|
66
66
|
&--withIcon {
|
67
|
-
padding-left: 0;
|
67
|
+
padding-left: 0;
|
68
68
|
}
|
69
69
|
}
|
70
70
|
}
|
@@ -7,7 +7,7 @@ import DetailContent from './detail/DetailContent'
|
|
7
7
|
import DetailMeta from './detail/DetailMeta'
|
8
8
|
import DetailProperty from './detail/DetailProperty'
|
9
9
|
import DetailTitle from './detail/DetailTitle'
|
10
|
-
import
|
10
|
+
import Start from './Start.vue'
|
11
11
|
import ListCard from './list/ListCard'
|
12
12
|
import ListColumnHeader from './list/ListColumnHeader'
|
13
13
|
import ListContent from './list/ListContent'
|
@@ -46,4 +46,4 @@ Vue.component('DetailColumn', DetailColumn)
|
|
46
46
|
Vue.component('AppBarButton', AppBarButton)
|
47
47
|
Vue.component('AppBarTitle', AppBarTitle)
|
48
48
|
|
49
|
-
Vue.component('
|
49
|
+
Vue.component('Start', Start)
|
@@ -9,23 +9,32 @@
|
|
9
9
|
</div>
|
10
10
|
|
11
11
|
<template v-if="models_.length">
|
12
|
-
<template v-if="
|
13
|
-
<
|
14
|
-
<
|
12
|
+
<template v-if="_table">
|
13
|
+
<a-table>
|
14
|
+
<a-table-header>
|
15
|
+
<div v-if="$has.icon" />
|
16
|
+
|
15
17
|
<slot name="header-table" />
|
16
|
-
</
|
18
|
+
</a-table-header>
|
17
19
|
|
18
|
-
<
|
20
|
+
<a-table-row
|
19
21
|
v-for="model in models_"
|
20
22
|
:key="model.id"
|
21
|
-
class="row"
|
22
23
|
>
|
24
|
+
<v-icon
|
25
|
+
v-if="$has.icon"
|
26
|
+
:color="model.getIcon().color"
|
27
|
+
size="1.2rem"
|
28
|
+
v-text="model.getIcon().icon"
|
29
|
+
/>
|
30
|
+
|
23
31
|
<slot
|
24
32
|
name="model-table"
|
25
33
|
:model="model"
|
34
|
+
:setFilter="setFilter"
|
26
35
|
/>
|
27
|
-
</
|
28
|
-
</
|
36
|
+
</a-table-row>
|
37
|
+
</a-table>
|
29
38
|
</template>
|
30
39
|
|
31
40
|
<template v-else>
|
@@ -60,6 +69,8 @@ import { LoadingEvent } from '@a-vue/events'
|
|
60
69
|
props: ['table']
|
61
70
|
})
|
62
71
|
export default class ListView extends Mixins(ListViewMixin) {
|
72
|
+
$hasOptions = ['icon']
|
73
|
+
|
63
74
|
@Watch('isLoading')
|
64
75
|
isLoadingChanged () {
|
65
76
|
if (this.noEvents === undefined || !this.noEvents) {
|
@@ -71,56 +82,31 @@ export default class ListView extends Mixins(ListViewMixin) {
|
|
71
82
|
}
|
72
83
|
this.$emit('update:isLoading', this.isLoading)
|
73
84
|
}
|
85
|
+
|
86
|
+
get _table () {
|
87
|
+
return this.table !== false
|
88
|
+
}
|
89
|
+
|
90
|
+
setFilter (name, value) {
|
91
|
+
this.filters[name].value = value
|
92
|
+
}
|
74
93
|
}
|
75
94
|
</script>
|
76
95
|
|
77
96
|
|
78
97
|
<style lang="scss" scoped>
|
98
|
+
.listView {
|
99
|
+
max-width: 100%;
|
100
|
+
overflow-x: auto;
|
101
|
+
overflow-y: hidden;
|
102
|
+
}
|
103
|
+
|
79
104
|
.filters {
|
105
|
+
margin-left: 4px;
|
80
106
|
margin-bottom: 2rem;
|
81
107
|
}
|
82
108
|
|
83
|
-
.table {
|
84
|
-
display: table;
|
85
|
-
border-collapse: collapse;
|
109
|
+
.a-table-row > :last-child {
|
86
110
|
width: 100%;
|
87
|
-
|
88
|
-
.header, .row {
|
89
|
-
display: table-row;
|
90
|
-
> * {
|
91
|
-
display: table-cell;
|
92
|
-
padding: .4rem;
|
93
|
-
padding-right: 1.5rem;
|
94
|
-
white-space: nowrap;
|
95
|
-
vertical-align: middle;
|
96
|
-
|
97
|
-
&.info {
|
98
|
-
background: none !important;
|
99
|
-
color: #888888;
|
100
|
-
}
|
101
|
-
}
|
102
|
-
}
|
103
|
-
|
104
|
-
.header {
|
105
|
-
text-transform: uppercase;
|
106
|
-
letter-spacing: 1px;
|
107
|
-
color: #999999;
|
108
|
-
font-size: .9rem;
|
109
|
-
> * {
|
110
|
-
padding-right: 1rem;
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
.row {
|
115
|
-
border-bottom: 1px solid #DDDDDD;
|
116
|
-
|
117
|
-
&:hover {
|
118
|
-
background: #F4F4F4;
|
119
|
-
}
|
120
|
-
|
121
|
-
&:last-child {
|
122
|
-
border: none;
|
123
|
-
}
|
124
|
-
}
|
125
111
|
}
|
126
112
|
</style>
|
@@ -69,7 +69,11 @@ export default class CreatePage extends Mixins(EditPageMixin) {
|
|
69
69
|
}
|
70
70
|
|
71
71
|
get modelUpateAction () {
|
72
|
-
return this.ModelClass.getAction(
|
72
|
+
return this.ModelClass.getAction('create')
|
73
|
+
}
|
74
|
+
|
75
|
+
get _icon () {
|
76
|
+
return this.icon || this.modelToEdit.getIcon()
|
73
77
|
}
|
74
78
|
|
75
79
|
get _title () {
|
@@ -89,7 +93,7 @@ export default class CreatePage extends Mixins(EditPageMixin) {
|
|
89
93
|
get _listLink () {
|
90
94
|
if (this.listLink) {
|
91
95
|
if (typeof this.listLink === 'function') {
|
92
|
-
return this.listLink(
|
96
|
+
return this.listLink()
|
93
97
|
} else {
|
94
98
|
return this.listLink
|
95
99
|
}
|
@@ -97,13 +101,6 @@ export default class CreatePage extends Mixins(EditPageMixin) {
|
|
97
101
|
return this.modelToEdit.getLink('list')
|
98
102
|
}
|
99
103
|
|
100
|
-
get _icon () {
|
101
|
-
if (this.icon) {
|
102
|
-
return this.icon
|
103
|
-
}
|
104
|
-
return this.modelToEdit.getIcon()
|
105
|
-
}
|
106
|
-
|
107
104
|
createModelToEdit () {
|
108
105
|
if (this.createModel) {
|
109
106
|
return this.createModel(this.fields)
|
@@ -94,7 +94,7 @@ export default class DetailPage extends Vue {
|
|
94
94
|
get _listLink () {
|
95
95
|
if (this.listLink) {
|
96
96
|
if (typeof this.listLink === 'function') {
|
97
|
-
return this.listLink(
|
97
|
+
return this.listLink()
|
98
98
|
} else {
|
99
99
|
return this.listLink
|
100
100
|
}
|
@@ -113,7 +113,7 @@ export default class DetailPage extends Vue {
|
|
113
113
|
if (this.removeAction) {
|
114
114
|
return this.removeAction
|
115
115
|
}
|
116
|
-
return this.ModelClass.getAction(
|
116
|
+
return this.ModelClass.getAction('delete')
|
117
117
|
}
|
118
118
|
|
119
119
|
async remove () {
|
@@ -99,14 +99,18 @@ export default class EditPage extends Mixins(EditPageMixin) {
|
|
99
99
|
}
|
100
100
|
|
101
101
|
get modelUpateAction () {
|
102
|
-
return this.ModelClass.getAction(
|
102
|
+
return this.ModelClass.getAction('update')
|
103
103
|
}
|
104
104
|
|
105
105
|
get _getAction () {
|
106
106
|
if (this.getAction) {
|
107
107
|
return this.getAction
|
108
108
|
}
|
109
|
-
return this.ModelClass.getAction(
|
109
|
+
return this.ModelClass.getAction('get')
|
110
|
+
}
|
111
|
+
|
112
|
+
get _icon () {
|
113
|
+
return this.icon || this.model.getIcon()
|
110
114
|
}
|
111
115
|
|
112
116
|
get _title () {
|
@@ -126,7 +130,7 @@ export default class EditPage extends Mixins(EditPageMixin) {
|
|
126
130
|
get _listLink () {
|
127
131
|
if (this.listLink) {
|
128
132
|
if (typeof this.listLink === 'function') {
|
129
|
-
return this.listLink(
|
133
|
+
return this.listLink()
|
130
134
|
} else {
|
131
135
|
return this.listLink
|
132
136
|
}
|
@@ -134,13 +138,6 @@ export default class EditPage extends Mixins(EditPageMixin) {
|
|
134
138
|
return this.model.getLink('list')
|
135
139
|
}
|
136
140
|
|
137
|
-
get _icon () {
|
138
|
-
if (this.icon) {
|
139
|
-
return this.icon
|
140
|
-
}
|
141
|
-
return this.model.getIcon()
|
142
|
-
}
|
143
|
-
|
144
141
|
createModelToEdit () {
|
145
142
|
return this.model_.cloneForEdit(this.fields)
|
146
143
|
}
|
@@ -15,72 +15,7 @@
|
|
15
15
|
/>
|
16
16
|
</app-bar-button>
|
17
17
|
|
18
|
-
<
|
19
|
-
v-bind="$attrs"
|
20
|
-
:models="models"
|
21
|
-
:meta="meta"
|
22
|
-
:action="action"
|
23
|
-
:scopes="scopes"
|
24
|
-
:fields="fields"
|
25
|
-
:table="table"
|
26
|
-
:initialFilters="initialFilters"
|
27
|
-
v-on="$listeners"
|
28
|
-
>
|
29
|
-
<template #filters="{filters, count}">
|
30
|
-
<div class="pl-2">
|
31
|
-
<slot
|
32
|
-
name="filters"
|
33
|
-
:filters="filters"
|
34
|
-
:count="count"
|
35
|
-
/>
|
36
|
-
</div>
|
37
|
-
</template>
|
38
|
-
|
39
|
-
<template
|
40
|
-
v-if="table"
|
41
|
-
#header-table
|
42
|
-
>
|
43
|
-
<div />
|
44
|
-
|
45
|
-
<slot name="header" />
|
46
|
-
</template>
|
47
|
-
|
48
|
-
<template
|
49
|
-
v-if="table"
|
50
|
-
#model-table="{ model }"
|
51
|
-
>
|
52
|
-
<v-icon
|
53
|
-
:color="model.getIcon().color"
|
54
|
-
size="1.5rem"
|
55
|
-
v-text="model.getIcon().icon"
|
56
|
-
/>
|
57
|
-
|
58
|
-
<slot
|
59
|
-
name="model"
|
60
|
-
:model="model"
|
61
|
-
/>
|
62
|
-
|
63
|
-
<div class="lastColumn" />
|
64
|
-
</template>
|
65
|
-
|
66
|
-
<template
|
67
|
-
v-if="!table"
|
68
|
-
#model="{ model }"
|
69
|
-
>
|
70
|
-
<a-row gap="4">
|
71
|
-
<v-icon
|
72
|
-
:color="model.getIcon().color"
|
73
|
-
size="2rem"
|
74
|
-
v-text="model.getIcon().icon"
|
75
|
-
/>
|
76
|
-
|
77
|
-
<slot
|
78
|
-
name="model"
|
79
|
-
:model="model"
|
80
|
-
/>
|
81
|
-
</a-row>
|
82
|
-
</template>
|
83
|
-
</list-view>
|
18
|
+
<slot />
|
84
19
|
</div>
|
85
20
|
</template>
|
86
21
|
|
@@ -89,42 +24,13 @@ import { Component, Vue } from 'vue-property-decorator'
|
|
89
24
|
import { apiResources } from '@afeefa/api-resources-client'
|
90
25
|
|
91
26
|
@Component({
|
92
|
-
props: ['
|
27
|
+
props: ['icon', 'title', 'newTitle', 'newLink', 'Model']
|
93
28
|
})
|
94
29
|
export default class ListPage extends Vue {
|
95
30
|
$hasOptions = ['add']
|
96
31
|
|
97
|
-
|
98
|
-
|
99
|
-
console.warn('<list-view> owner must provide a static getListConfig method.')
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
get listConfig () {
|
104
|
-
return this.$parent.constructor.getListConfig(this.$route)
|
105
|
-
}
|
106
|
-
|
107
|
-
get ModelClass () {
|
108
|
-
return this.listConfig.ModelClass
|
109
|
-
}
|
110
|
-
|
111
|
-
get action () {
|
112
|
-
if (this.listConfig.action) {
|
113
|
-
return this.listConfig.action
|
114
|
-
}
|
115
|
-
return this.ModelClass.getAction(this.$routeDefinition, 'list')
|
116
|
-
}
|
117
|
-
|
118
|
-
get scopes () {
|
119
|
-
return this.listConfig.scopes
|
120
|
-
}
|
121
|
-
|
122
|
-
get initialFilters () {
|
123
|
-
return this.listConfig.initialFilters
|
124
|
-
}
|
125
|
-
|
126
|
-
get fields () {
|
127
|
-
return this.listConfig.fields
|
32
|
+
get _icon () {
|
33
|
+
return this.icon || this.Model.icon
|
128
34
|
}
|
129
35
|
|
130
36
|
get _title () {
|
@@ -132,7 +38,7 @@ export default class ListPage extends Vue {
|
|
132
38
|
return this.title
|
133
39
|
}
|
134
40
|
|
135
|
-
const type = apiResources.getType(this.
|
41
|
+
const type = apiResources.getType(this.Model.type)
|
136
42
|
return type.t('TITLE_PLURAL')
|
137
43
|
}
|
138
44
|
|
@@ -141,33 +47,12 @@ export default class ListPage extends Vue {
|
|
141
47
|
return this.newTitle
|
142
48
|
}
|
143
49
|
|
144
|
-
const type = apiResources.getType(this.
|
50
|
+
const type = apiResources.getType(this.Model.type)
|
145
51
|
return type.t('TITLE_SINGULAR')
|
146
52
|
}
|
147
53
|
|
148
|
-
get _icon () {
|
149
|
-
if (this.icon) {
|
150
|
-
return this.icon
|
151
|
-
}
|
152
|
-
return this.ModelClass.icon
|
153
|
-
}
|
154
|
-
|
155
54
|
get _newLink () {
|
156
|
-
|
157
|
-
if (typeof this.newLink === 'function') {
|
158
|
-
return this.newLink(this.$route.params)
|
159
|
-
} else {
|
160
|
-
return this.newLink
|
161
|
-
}
|
162
|
-
}
|
163
|
-
return this.ModelClass.getLink('new')
|
55
|
+
return this.newLink || this.Model.getLink('new')
|
164
56
|
}
|
165
57
|
}
|
166
58
|
</script>
|
167
|
-
|
168
|
-
|
169
|
-
<style lang="scss" scoped>
|
170
|
-
.lastColumn {
|
171
|
-
width: 100%;
|
172
|
-
}
|
173
|
-
</style>
|
@@ -19,13 +19,7 @@ function load (route) {
|
|
19
19
|
const routeDefinition = route.meta.routeDefinition
|
20
20
|
const Component = routeDefinition.config.detail
|
21
21
|
const detailConfig = Component.getDetailConfig(route)
|
22
|
-
|
23
|
-
let action = null
|
24
|
-
if (detailConfig.ModelClass) {
|
25
|
-
action = detailConfig.ModelClass.getAction(routeDefinition, 'get')
|
26
|
-
} else {
|
27
|
-
action = detailConfig.action
|
28
|
-
}
|
22
|
+
const action = detailConfig.action || detailConfig.ModelClass.getAction('get')
|
29
23
|
|
30
24
|
return new GetAction()
|
31
25
|
.setAction(action)
|
@@ -20,13 +20,7 @@ function load (route) {
|
|
20
20
|
const routeDefinition = route.meta.routeDefinition
|
21
21
|
const Component = routeDefinition.config.edit
|
22
22
|
const editConfig = Component.getEditConfig(route)
|
23
|
-
|
24
|
-
let action = null
|
25
|
-
if (editConfig.ModelClass) {
|
26
|
-
action = editConfig.ModelClass.getAction(routeDefinition, 'get')
|
27
|
-
} else {
|
28
|
-
action = editConfig.getAction
|
29
|
-
}
|
23
|
+
const action = editConfig.getAction || editConfig.ModelClass.getAction('get')
|
30
24
|
|
31
25
|
return new GetAction()
|
32
26
|
.setAction(action)
|
@@ -10,6 +10,7 @@
|
|
10
10
|
<script>
|
11
11
|
import { Component, Vue, Watch } from 'vue-property-decorator'
|
12
12
|
import { ListAction } from '@a-vue/api-resources/ApiActions'
|
13
|
+
import { RouteQueryFilterSource } from '@a-vue/components/list/RouteQueryFilterSource'
|
13
14
|
|
14
15
|
Component.registerHooks([
|
15
16
|
'beforeRouteEnter',
|
@@ -28,20 +29,18 @@ let lastVm = null
|
|
28
29
|
function load (route) {
|
29
30
|
const routeDefinition = route.meta.routeDefinition
|
30
31
|
const Component = routeDefinition.config.list
|
31
|
-
const listConfig = Component.getListConfig(route)
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
action = listConfig.ModelClass.getAction(routeDefinition, 'list')
|
36
|
-
} else {
|
37
|
-
action = listConfig.action
|
33
|
+
if (!Component.listViewRequest) {
|
34
|
+
console.warn('A list route component must implement a static listViewRequest property.')
|
38
35
|
}
|
39
36
|
|
37
|
+
const request = Component.listViewRequest.clone()
|
38
|
+
.initFromFilterSource(new RouteQueryFilterSource(route))
|
39
|
+
.recreateFromHistory(route.path)
|
40
|
+
.toApiRequest()
|
41
|
+
|
40
42
|
return new ListAction()
|
41
|
-
.
|
42
|
-
.setFields(listConfig.fields)
|
43
|
-
.setScopes(listConfig.scopes)
|
44
|
-
.setFiltersForRoute(route)
|
43
|
+
.setRequest(request)
|
45
44
|
.load()
|
46
45
|
}
|
47
46
|
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,5 +1,6 @@
|
|
1
|
-
import { Model as ApiResourcesModel } from '@afeefa/api-resources-client'
|
1
|
+
import { Model as ApiResourcesModel, apiResources } from '@afeefa/api-resources-client'
|
2
2
|
import { mdiAlphaMCircle } from '@mdi/js'
|
3
|
+
|
3
4
|
export class Model extends ApiResourcesModel {
|
4
5
|
static resourceType = null
|
5
6
|
static routeName = null
|
@@ -9,12 +10,14 @@ export class Model extends ApiResourcesModel {
|
|
9
10
|
return (new this()).getLink(action)
|
10
11
|
}
|
11
12
|
|
12
|
-
static getAction (
|
13
|
+
static getAction (action) {
|
13
14
|
if (this.resourceType) {
|
14
|
-
|
15
|
-
|
15
|
+
return apiResources.getAction({
|
16
|
+
resource: this.resourceType,
|
17
|
+
action
|
18
|
+
})
|
16
19
|
}
|
17
|
-
console.warn('You can\'t get an action of a model without resourceType:', this.type)
|
20
|
+
console.warn('You can\'t get an action out of a model without resourceType:', this.type)
|
18
21
|
return null
|
19
22
|
}
|
20
23
|
|
@@ -32,8 +35,8 @@ export class Model extends ApiResourcesModel {
|
|
32
35
|
}
|
33
36
|
}
|
34
37
|
|
35
|
-
getAction (
|
36
|
-
return this.constructor.getAction(
|
38
|
+
getAction (action) {
|
39
|
+
return this.constructor.getAction(action)
|
37
40
|
}
|
38
41
|
|
39
42
|
getIcon () {
|
@@ -0,0 +1,43 @@
|
|
1
|
+
$fontUrl: '';
|
2
|
+
|
3
|
+
@if variable-exists(ibmPlexSansFontUrl) {
|
4
|
+
$fontUrl: $ibmPlexSansFontUrl;
|
5
|
+
}
|
6
|
+
|
7
|
+
/* ibm-plex-sans-regular - latin */
|
8
|
+
@font-face {
|
9
|
+
font-family: 'IBM Plex Sans';
|
10
|
+
font-style: normal;
|
11
|
+
font-weight: 400;
|
12
|
+
src: local(''),
|
13
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
14
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
15
|
+
}
|
16
|
+
|
17
|
+
/* ibm-plex-sans-italic - latin */
|
18
|
+
@font-face {
|
19
|
+
font-family: 'IBM Plex Sans';
|
20
|
+
font-style: italic;
|
21
|
+
font-weight: 400;
|
22
|
+
src: local(''),
|
23
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
24
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
25
|
+
}
|
26
|
+
/* ibm-plex-sans-700 - latin */
|
27
|
+
@font-face {
|
28
|
+
font-family: 'IBM Plex Sans';
|
29
|
+
font-style: normal;
|
30
|
+
font-weight: 700;
|
31
|
+
src: local(''),
|
32
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
33
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
34
|
+
}
|
35
|
+
/* ibm-plex-sans-700italic - latin */
|
36
|
+
@font-face {
|
37
|
+
font-family: 'IBM Plex Sans';
|
38
|
+
font-style: italic;
|
39
|
+
font-weight: 700;
|
40
|
+
src: local(''),
|
41
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
42
|
+
url('#{$fontUrl}/ibm-plex-sans-v9-latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
43
|
+
}
|