glib-web 4.11.2 → 4.11.4
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/actions/logics/set.js +2 -15
- package/app.vue +5 -4
- package/components/component.vue +1 -1
- package/components/composable/form.js +31 -16
- package/components/composable/parser.js +15 -8
- package/components/fields/_patternText.vue +4 -0
- package/components/fields/_select.vue +11 -8
- package/components/fields/check.vue +4 -0
- package/components/fields/checkGroup.vue +4 -0
- package/components/fields/chipGroup.vue +4 -1
- package/components/fields/date.vue +5 -1
- package/components/fields/datetime.vue +5 -1
- package/components/fields/file.vue +4 -0
- package/components/fields/hidden.vue +5 -0
- package/components/fields/multiUpload.vue +3 -1
- package/components/fields/otpField.vue +4 -1
- package/components/fields/radio.vue +4 -0
- package/components/fields/rating.vue +6 -1
- package/components/fields/richText.vue +4 -1
- package/components/fields/sign.vue +3 -0
- package/components/fields/text.vue +4 -0
- package/components/fields/textarea.vue +4 -0
- package/components/mixins/styles.js +1 -3
- package/components/panels/bulkEdit2.vue +59 -36
- package/components/panels/flow.vue +0 -1
- package/components/panels/form.vue +5 -4
- package/package.json +1 -1
- package/utils/http.js +2 -2
package/actions/logics/set.js
CHANGED
|
@@ -3,6 +3,7 @@ import merge from 'lodash.merge';
|
|
|
3
3
|
import { nextTick } from "vue";
|
|
4
4
|
import { sanitize } from "../../components/composable/date";
|
|
5
5
|
import { isPresent } from "../../utils/type";
|
|
6
|
+
import { getFormData as _getFormData } from "../../components/composable/form";
|
|
6
7
|
|
|
7
8
|
const sumDate = function (a, b) {
|
|
8
9
|
const date = new Date(a);
|
|
@@ -76,21 +77,7 @@ jsonLogic.add_operation("isPresent", isPresentOperator);
|
|
|
76
77
|
|
|
77
78
|
function getFormData() {
|
|
78
79
|
return Array.from(document.querySelectorAll('form')).reduce((prev, curr) => {
|
|
79
|
-
const
|
|
80
|
-
const obj = {};
|
|
81
|
-
|
|
82
|
-
formData.forEach((value, key) => {
|
|
83
|
-
// Reflect.has in favor of: object.hasOwnProperty(key)
|
|
84
|
-
if (!Reflect.has(obj, key)) {
|
|
85
|
-
obj[key] = value;
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (!Array.isArray(obj[key])) {
|
|
89
|
-
obj[key] = [obj[key]];
|
|
90
|
-
}
|
|
91
|
-
obj[key].push(value);
|
|
92
|
-
});
|
|
93
|
-
|
|
80
|
+
const obj = _getFormData(curr);
|
|
94
81
|
return Object.assign({}, prev, obj);
|
|
95
82
|
}, {});
|
|
96
83
|
}
|
package/app.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-app :class="page.styleClasses">
|
|
2
|
+
<v-app :class="page.styleClasses" ref="appRef">
|
|
3
3
|
<component :is="containerComponent" :spec="formSpec" :style="'height: 100%;'">
|
|
4
4
|
<nav-layout ref="navBar" @mounted="updateMainHeight()" :page="page" />
|
|
5
5
|
|
|
@@ -44,7 +44,7 @@ import FormPanel from "./components/panels/form.vue";
|
|
|
44
44
|
import { vueApp } from "./store";
|
|
45
45
|
import { useSocket } from "./components/composable/socket";
|
|
46
46
|
import { usePasteable } from "./components/composable/pasteable";
|
|
47
|
-
import { computed,
|
|
47
|
+
import { computed, onMounted, ref } from "vue";
|
|
48
48
|
import { TOOLTIP_ID } from "./constant";
|
|
49
49
|
import { watchGlibEvent } from "./store";
|
|
50
50
|
import { htmlElement } from "./components/helper";
|
|
@@ -56,16 +56,17 @@ export default {
|
|
|
56
56
|
const filePaster = computed(() => props.page.filePaster);
|
|
57
57
|
usePasteable(filePaster);
|
|
58
58
|
const tooltipId = TOOLTIP_ID;
|
|
59
|
+
const appRef = ref(null);
|
|
59
60
|
|
|
60
61
|
onMounted(() => {
|
|
61
|
-
const el = htmlElement(
|
|
62
|
+
const el = htmlElement(appRef.value);
|
|
62
63
|
el.addEventListener('dirtyupdate', (event) => {
|
|
63
64
|
const { dirty } = event.detail;
|
|
64
65
|
vueApp.isFormDirty = dirty;
|
|
65
66
|
});
|
|
66
67
|
});
|
|
67
68
|
|
|
68
|
-
return { vueApp, tooltipId };
|
|
69
|
+
return { vueApp, tooltipId, appRef };
|
|
69
70
|
},
|
|
70
71
|
components: {
|
|
71
72
|
"nav-layout": NavLayout,
|
package/components/component.vue
CHANGED
|
@@ -73,7 +73,7 @@ const RichTextField = defineAsyncComponent(() => import("./fields/richText.vue")
|
|
|
73
73
|
const FileField = defineAsyncComponent(() => import("./fields/file.vue"));
|
|
74
74
|
const MultiUploadField = defineAsyncComponent(() => import("./fields/multiUpload.vue"));
|
|
75
75
|
import SignField from "./fields/sign.vue";
|
|
76
|
-
import SelectField from "./fields/
|
|
76
|
+
import SelectField from "./fields/_select.vue";
|
|
77
77
|
import TimeZoneField from "./fields/timeZone.vue";
|
|
78
78
|
import DynamicSelectField from "./fields/dynamicSelect.vue";
|
|
79
79
|
import RadioGroupField from "./fields/radioGroup.vue";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inject, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from "vue";
|
|
2
2
|
import { htmlElement } from "../helper";
|
|
3
3
|
|
|
4
4
|
const setBusy = (htmlElement, value) => {
|
|
@@ -14,6 +14,7 @@ const triggerOnChange = (htmlElement) => {
|
|
|
14
14
|
const triggerOnInput = (htmlElement) => nextTick(() => htmlElement.dispatchEvent(new Event('input', { bubbles: true })));
|
|
15
15
|
|
|
16
16
|
const getFormData = (el, ignoredFields = new Set()) => {
|
|
17
|
+
if (!el) return {};
|
|
17
18
|
if (!(el instanceof HTMLFormElement) && el instanceof HTMLElement) {
|
|
18
19
|
el = el.querySelector('form');
|
|
19
20
|
}
|
|
@@ -37,37 +38,36 @@ const getFormData = (el, ignoredFields = new Set()) => {
|
|
|
37
38
|
return obj;
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
function useDirtyState() {
|
|
43
|
-
|
|
41
|
+
function useGlibForm({ formRef }) {
|
|
44
42
|
const initFormData = ref({});
|
|
45
43
|
const currentFormData = ref({});
|
|
46
44
|
const el = ref(null);
|
|
47
|
-
const
|
|
45
|
+
const registeredInputs = ref([]);
|
|
46
|
+
const ignoredDirtyCheckFields = ref(new Set());
|
|
48
47
|
|
|
49
48
|
const dispatchEv = (dirty = isFormDirty()) => {
|
|
49
|
+
if (!el.value) return;
|
|
50
|
+
|
|
50
51
|
const event = new CustomEvent('dirtyupdate', { bubbles: true, detail: { dirty: dirty } });
|
|
51
52
|
el.value.dispatchEvent(event);
|
|
52
53
|
};
|
|
53
54
|
|
|
54
|
-
|
|
55
55
|
onMounted(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
initFormData.value = getFormData(el.value, ignoredDirtyCheckFields.value);
|
|
59
|
-
currentFormData.value = initFormData.value;
|
|
56
|
+
el.value = htmlElement(formRef.value);
|
|
57
|
+
});
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
watch(registeredInputs, (value) => {
|
|
60
|
+
initFormData.value = getFormData(el.value, ignoredDirtyCheckFields.value);
|
|
61
|
+
currentFormData.value = initFormData.value;
|
|
63
62
|
|
|
63
|
+
dispatchEv();
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
onBeforeUnmount(() => {
|
|
67
67
|
dispatchEv(false);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
const
|
|
70
|
+
const updateDirtyState = (event) => {
|
|
71
71
|
currentFormData.value = getFormData(el.value, ignoredDirtyCheckFields.value);
|
|
72
72
|
dispatchEv();
|
|
73
73
|
};
|
|
@@ -76,7 +76,22 @@ function useDirtyState() {
|
|
|
76
76
|
return JSON.stringify(initFormData.value) !== JSON.stringify(currentFormData.value);
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
provide('registeredInputs', registeredInputs);
|
|
80
|
+
provide('ignoredDirtyCheckFields', ignoredDirtyCheckFields);
|
|
81
|
+
|
|
82
|
+
return { updateDirtyState };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function useGlibInput({ props }) {
|
|
86
|
+
const registeredInputs = inject('registeredInputs', null);
|
|
87
|
+
const ignoredDirtyCheckFields = inject('ignoredDirtyCheckFields', null);
|
|
88
|
+
|
|
89
|
+
if (!registeredInputs || !ignoredDirtyCheckFields) return;
|
|
90
|
+
|
|
91
|
+
onMounted(() => {
|
|
92
|
+
if (props.spec.disableDirtyCheck) ignoredDirtyCheckFields.value.add(props.spec.name);
|
|
93
|
+
registeredInputs.value = Array.from(new Set(registeredInputs.value).add(props.spec.name));
|
|
94
|
+
});
|
|
80
95
|
}
|
|
81
96
|
|
|
82
|
-
export { setBusy, triggerOnChange, triggerOnInput,
|
|
97
|
+
export { setBusy, triggerOnChange, triggerOnInput, useGlibForm, useGlibInput, getFormData };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export function parseCsv(csvString) {
|
|
1
|
+
export function parseCsv(csvString, options = { withHeader: true }) {
|
|
2
|
+
const { withHeader } = options;
|
|
3
|
+
|
|
2
4
|
// https://stackoverflow.com/a/41563966/9970813
|
|
3
5
|
let prevLetter = "",
|
|
4
6
|
row = [""],
|
|
@@ -19,7 +21,8 @@ export function parseCsv(csvString) {
|
|
|
19
21
|
letter = row[++columnIndex] = "";
|
|
20
22
|
} else if ("\n" === letter && canSplit) {
|
|
21
23
|
if ("\r" === prevLetter) {
|
|
22
|
-
|
|
24
|
+
const str = row[columnIndex].slice(0, -1);
|
|
25
|
+
row[columnIndex] = str.includes(',') ? parseCsv(str, { withHeader: false })[0] : str;
|
|
23
26
|
}
|
|
24
27
|
row = result[++rowIndex] = [(letter = "")];
|
|
25
28
|
columnIndex = 0;
|
|
@@ -37,10 +40,14 @@ export function parseCsv(csvString) {
|
|
|
37
40
|
// return r[0] !== "undefined"
|
|
38
41
|
// });
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
if (withHeader) {
|
|
44
|
+
const columns = result.shift();
|
|
45
|
+
return result.map(row => {
|
|
46
|
+
return columns.reduce((prev, curr, index) => {
|
|
47
|
+
return Object.assign({}, prev, { [curr]: row[index] });
|
|
48
|
+
}, {});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return result;
|
|
46
53
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
<script>
|
|
13
13
|
import { sanitize } from "../composable/date";
|
|
14
|
+
import { useGlibInput } from "../composable/form";
|
|
14
15
|
import inputVariant from '../mixins/inputVariant';
|
|
15
16
|
|
|
16
17
|
export default {
|
|
@@ -20,6 +21,9 @@ export default {
|
|
|
20
21
|
type: { type: String, required: true },
|
|
21
22
|
pattern: { type: String, required: true }
|
|
22
23
|
},
|
|
24
|
+
setup(props) {
|
|
25
|
+
useGlibInput({ props });
|
|
26
|
+
},
|
|
23
27
|
methods: {
|
|
24
28
|
_linkFieldModels(valueChanged) {
|
|
25
29
|
if (valueChanged) this.fieldModel = this.sanitizeValue(this.spec.value);
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<script>
|
|
39
39
|
import inputVariant from '../mixins/inputVariant';
|
|
40
40
|
import { determineDensity } from "../../utils/constant";
|
|
41
|
-
import { triggerOnChange, triggerOnInput } from "../composable/form";
|
|
41
|
+
import { triggerOnChange, triggerOnInput, useGlibInput } from "../composable/form";
|
|
42
42
|
|
|
43
43
|
export default {
|
|
44
44
|
mixins: [inputVariant],
|
|
@@ -46,6 +46,9 @@ export default {
|
|
|
46
46
|
spec: { type: Object, required: true },
|
|
47
47
|
defaultValue: { type: String, default: null }
|
|
48
48
|
},
|
|
49
|
+
setup(props) {
|
|
50
|
+
useGlibInput({ props });
|
|
51
|
+
},
|
|
49
52
|
data() {
|
|
50
53
|
return {
|
|
51
54
|
options: null,
|
|
@@ -96,6 +99,13 @@ export default {
|
|
|
96
99
|
}
|
|
97
100
|
},
|
|
98
101
|
methods: {
|
|
102
|
+
// _linkFieldModels(valueChanged) {
|
|
103
|
+
// if (this.spec.csvMode && valueChanged) {
|
|
104
|
+
// this.fieldModel = (this.spec.value || '').replace(/,\s+/g, ",").split(",").filter((v) => v != '');
|
|
105
|
+
// return;
|
|
106
|
+
// }
|
|
107
|
+
// this.fieldModel = this.spec.value;
|
|
108
|
+
// },
|
|
99
109
|
onChange() {
|
|
100
110
|
this.$executeOnChange();
|
|
101
111
|
const containerEl = this.$refs.container;
|
|
@@ -121,13 +131,6 @@ export default {
|
|
|
121
131
|
this.fieldModel = this.defaultValue;
|
|
122
132
|
}
|
|
123
133
|
},
|
|
124
|
-
// TODO
|
|
125
|
-
// sanitizeValue(value) {
|
|
126
|
-
// if (this.spec.csvMode) {
|
|
127
|
-
// return value.replace(/,\s+/g,",").split(",");
|
|
128
|
-
// }
|
|
129
|
-
// return value;
|
|
130
|
-
// },
|
|
131
134
|
$registryEnabled() {
|
|
132
135
|
return false;
|
|
133
136
|
}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
<script>
|
|
21
21
|
import ThumbnailCheck from "../fields/check/_thumbnail.vue";
|
|
22
22
|
import FeaturedCheck from "../fields/check/_featured.vue";
|
|
23
|
+
import { useGlibInput } from "../composable/form";
|
|
23
24
|
export default {
|
|
24
25
|
components: {
|
|
25
26
|
"thumbnail-check": ThumbnailCheck,
|
|
@@ -35,6 +36,9 @@ export default {
|
|
|
35
36
|
props: {
|
|
36
37
|
spec: { type: Object, required: true }
|
|
37
38
|
},
|
|
39
|
+
setup(props) {
|
|
40
|
+
useGlibInput({ props });
|
|
41
|
+
},
|
|
38
42
|
data() {
|
|
39
43
|
return {
|
|
40
44
|
uncheckValue: this.spec.uncheckValue
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<script>
|
|
15
15
|
import { defineComponent, ref, provide, reactive } from "vue";
|
|
16
16
|
import FieldCheck from './check.vue';
|
|
17
|
+
import { useGlibInput } from "../composable/form";
|
|
17
18
|
|
|
18
19
|
export default defineComponent({
|
|
19
20
|
components: {
|
|
@@ -22,6 +23,9 @@ export default defineComponent({
|
|
|
22
23
|
props: {
|
|
23
24
|
spec: { type: Object, required: true }
|
|
24
25
|
},
|
|
26
|
+
setup(props) {
|
|
27
|
+
useGlibInput({ props });
|
|
28
|
+
},
|
|
25
29
|
watch: {
|
|
26
30
|
childModels: function (val) {
|
|
27
31
|
// Ensure submitted params are in the same order as the checkboxes in display`
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
<script>
|
|
15
15
|
import { defineComponent } from "vue";
|
|
16
16
|
import inputVariant from "../mixins/inputVariant";
|
|
17
|
-
import { triggerOnChange, triggerOnInput } from "../composable/form";
|
|
17
|
+
import { triggerOnChange, triggerOnInput, useGlibInput } from "../composable/form";
|
|
18
18
|
|
|
19
19
|
export default defineComponent({
|
|
20
20
|
mixins: [inputVariant],
|
|
21
21
|
props: {
|
|
22
22
|
spec: Object
|
|
23
23
|
},
|
|
24
|
+
setup(props) {
|
|
25
|
+
useGlibInput({ props });
|
|
26
|
+
},
|
|
24
27
|
computed: {
|
|
25
28
|
values() {
|
|
26
29
|
if (this.fieldModel == null || this.fieldModel == undefined || this.fieldModel.length <= 0) return [null];
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
</template>
|
|
5
5
|
|
|
6
6
|
<script>
|
|
7
|
+
import { useGlibInput } from "../composable/form";
|
|
7
8
|
import PatternTextField from "./_patternText.vue";
|
|
8
9
|
|
|
9
10
|
export default {
|
|
@@ -13,7 +14,10 @@ export default {
|
|
|
13
14
|
},
|
|
14
15
|
props: {
|
|
15
16
|
spec: { type: Object, required: true },
|
|
16
|
-
}
|
|
17
|
+
},
|
|
18
|
+
setup(props) {
|
|
19
|
+
useGlibInput({ props });
|
|
20
|
+
},
|
|
17
21
|
};
|
|
18
22
|
</script>
|
|
19
23
|
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
</template>
|
|
19
19
|
|
|
20
20
|
<script>
|
|
21
|
+
import { useGlibInput } from "../composable/form";
|
|
21
22
|
import PatternTextField from "./_patternText.vue";
|
|
22
23
|
|
|
23
24
|
export default {
|
|
@@ -27,7 +28,10 @@ export default {
|
|
|
27
28
|
},
|
|
28
29
|
props: {
|
|
29
30
|
spec: { type: Object, required: true },
|
|
30
|
-
}
|
|
31
|
+
},
|
|
32
|
+
setup(props) {
|
|
33
|
+
useGlibInput({ props });
|
|
34
|
+
},
|
|
31
35
|
};
|
|
32
36
|
</script>
|
|
33
37
|
|
|
@@ -36,11 +36,15 @@
|
|
|
36
36
|
import Uploader from "../../utils/glibDirectUpload";
|
|
37
37
|
import { validateFile } from "../composable/file";
|
|
38
38
|
import { determineVariant } from "../../utils/constant";
|
|
39
|
+
import { useGlibInput } from "../composable/form";
|
|
39
40
|
|
|
40
41
|
export default {
|
|
41
42
|
props: {
|
|
42
43
|
spec: { type: Object, required: true },
|
|
43
44
|
},
|
|
45
|
+
setup(props) {
|
|
46
|
+
useGlibInput({ props });
|
|
47
|
+
},
|
|
44
48
|
data() {
|
|
45
49
|
return {
|
|
46
50
|
uploaded: false,
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script>
|
|
6
|
+
import { useGlibInput } from "../composable/form";
|
|
7
|
+
|
|
6
8
|
export default {
|
|
7
9
|
props: {
|
|
8
10
|
spec: { type: Object, required: true },
|
|
9
11
|
},
|
|
12
|
+
setup(props) {
|
|
13
|
+
useGlibInput({ props });
|
|
14
|
+
},
|
|
10
15
|
methods: {
|
|
11
16
|
$ready() {
|
|
12
17
|
// this.fieldModel = this.spec.value;
|
|
@@ -58,7 +58,7 @@ import { ref, computed, defineComponent, watch, getCurrentInstance } from 'vue';
|
|
|
58
58
|
import { VIcon } from 'vuetify/components';
|
|
59
59
|
import * as dropUploader from "../composable/upload";
|
|
60
60
|
import * as delegateUploader from "../composable/upload_delegator";
|
|
61
|
-
import { triggerOnChange } from "../composable/form";
|
|
61
|
+
import { triggerOnChange, useGlibInput } from "../composable/form";
|
|
62
62
|
import { nextTick } from "vue";
|
|
63
63
|
import { useFilesState, useFileUtils } from "../composable/file";
|
|
64
64
|
import doc from "./selectAsset/doc-1.png";
|
|
@@ -187,6 +187,8 @@ export default defineComponent({
|
|
|
187
187
|
fileSelect.value.click();
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
useGlibInput({ props });
|
|
191
|
+
|
|
190
192
|
return {
|
|
191
193
|
files,
|
|
192
194
|
fileSelect,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</template>
|
|
9
9
|
|
|
10
10
|
<script>
|
|
11
|
-
import { triggerOnChange } from "../composable/form";
|
|
11
|
+
import { triggerOnChange, useGlibInput } from "../composable/form";
|
|
12
12
|
import inputVariant from "../mixins/inputVariant";
|
|
13
13
|
|
|
14
14
|
export default {
|
|
@@ -16,6 +16,9 @@ export default {
|
|
|
16
16
|
props: {
|
|
17
17
|
spec: { type: Object, required: true },
|
|
18
18
|
},
|
|
19
|
+
setup(props) {
|
|
20
|
+
useGlibInput({ props });
|
|
21
|
+
},
|
|
19
22
|
watch: {
|
|
20
23
|
// there is a bug empty otp doesn't trigger form onchange
|
|
21
24
|
fieldModel(val, oldVal) {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import ThumbnailRadio from "../fields/radio/_thumbnail.vue";
|
|
21
21
|
import FeaturedRadio from "../fields/radio/_featured.vue";
|
|
22
|
+
import { useGlibInput } from "../composable/form";
|
|
22
23
|
|
|
23
24
|
export default {
|
|
24
25
|
components: {
|
|
@@ -28,6 +29,9 @@ export default {
|
|
|
28
29
|
props: {
|
|
29
30
|
spec: { type: Object, required: true }
|
|
30
31
|
},
|
|
32
|
+
setup(props) {
|
|
33
|
+
useGlibInput({ props });
|
|
34
|
+
},
|
|
31
35
|
inject: ['radioGroupCtx'],
|
|
32
36
|
computed: {
|
|
33
37
|
isActive() {
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
</template>
|
|
9
9
|
|
|
10
10
|
<script>
|
|
11
|
+
import { useGlibInput } from "../composable/form";
|
|
12
|
+
|
|
11
13
|
export default {
|
|
12
|
-
props: { spec: { type: Object, required: true } }
|
|
14
|
+
props: { spec: { type: Object, required: true } },
|
|
15
|
+
setup(props) {
|
|
16
|
+
useGlibInput({ props });
|
|
17
|
+
},
|
|
13
18
|
};
|
|
14
19
|
</script>
|
|
@@ -39,7 +39,7 @@ import QuillImageDropAndPaste from "quill-image-drop-and-paste";
|
|
|
39
39
|
import bus from "../../utils/eventBus";
|
|
40
40
|
|
|
41
41
|
import { vueApp } from "../../store";
|
|
42
|
-
import { triggerOnInput } from "../composable/form";
|
|
42
|
+
import { triggerOnInput, useGlibInput } from "../composable/form";
|
|
43
43
|
import { validateFile } from "../composable/file";
|
|
44
44
|
|
|
45
45
|
var ImageBlot = Quill.import("formats/image");
|
|
@@ -177,6 +177,9 @@ export default {
|
|
|
177
177
|
props: {
|
|
178
178
|
spec: { type: Object, required: true },
|
|
179
179
|
},
|
|
180
|
+
setup(props) {
|
|
181
|
+
useGlibInput({ props });
|
|
182
|
+
},
|
|
180
183
|
data: function () {
|
|
181
184
|
return {
|
|
182
185
|
customToolbar: [
|
|
@@ -47,6 +47,9 @@
|
|
|
47
47
|
import { ref, computed } from "vue";
|
|
48
48
|
import * as uploader from "../composable/upload";
|
|
49
49
|
import { useFileUtils } from "../composable/file";
|
|
50
|
+
import { useGlibInput } from "../composable/form";
|
|
51
|
+
|
|
52
|
+
useGlibInput({ props });
|
|
50
53
|
|
|
51
54
|
const { makeKey, Item } = useFileUtils();
|
|
52
55
|
const props = defineProps(['spec']);
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import eventFiltering from "../../utils/eventFiltering";
|
|
15
15
|
import inputVariant from "../mixins/inputVariant";
|
|
16
16
|
import { determineDensity } from "../../utils/constant";
|
|
17
|
+
import { useGlibInput } from "../composable/form";
|
|
17
18
|
|
|
18
19
|
export default {
|
|
19
20
|
mixins: [inputVariant],
|
|
@@ -21,6 +22,9 @@ export default {
|
|
|
21
22
|
spec: { type: Object, required: true },
|
|
22
23
|
type: { type: String, required: true },
|
|
23
24
|
},
|
|
25
|
+
setup(props) {
|
|
26
|
+
useGlibInput({ props });
|
|
27
|
+
},
|
|
24
28
|
data() {
|
|
25
29
|
return {
|
|
26
30
|
// config: {},
|
|
@@ -12,12 +12,16 @@
|
|
|
12
12
|
import eventFiltering from "../../utils/eventFiltering";
|
|
13
13
|
import inputVariant from "../mixins/inputVariant";
|
|
14
14
|
import { determineDensity } from "../../utils/constant";
|
|
15
|
+
import { useGlibInput } from "../composable/form";
|
|
15
16
|
|
|
16
17
|
export default {
|
|
17
18
|
mixins: [inputVariant],
|
|
18
19
|
props: {
|
|
19
20
|
spec: { type: Object, required: true },
|
|
20
21
|
},
|
|
22
|
+
setup(props) {
|
|
23
|
+
useGlibInput({ props });
|
|
24
|
+
},
|
|
21
25
|
data() {
|
|
22
26
|
return {
|
|
23
27
|
// styles: null,
|
|
@@ -2,7 +2,7 @@ import Hash from "../../utils/hash";
|
|
|
2
2
|
import { fieldModels, watchFieldModels } from "../composable/conditional";
|
|
3
3
|
import { determineColor } from "../../utils/constant";
|
|
4
4
|
import Action from "../../action";
|
|
5
|
-
import {
|
|
5
|
+
import { triggerOnChange } from "../composable/form";
|
|
6
6
|
import { realComponent } from "../helper";
|
|
7
7
|
import set from "lodash.set";
|
|
8
8
|
const NUMBER_PRECISION = 2;
|
|
@@ -284,8 +284,6 @@ export default {
|
|
|
284
284
|
if (valueChanged) {
|
|
285
285
|
this.fieldModel = this.latestSpec.value;
|
|
286
286
|
}
|
|
287
|
-
|
|
288
|
-
if (this.spec.disableDirtyCheck) ignoredDirtyCheckFields.value.add(this.spec.name);
|
|
289
287
|
}
|
|
290
288
|
},
|
|
291
289
|
action_resetValue() {
|
|
@@ -7,20 +7,21 @@
|
|
|
7
7
|
<table :class="rowLoaded ? 'loaded' : 'nonLoaded'" width="100%">
|
|
8
8
|
<thead v-if="props.spec.viewHeaders">
|
|
9
9
|
<tr>
|
|
10
|
-
<th class="cell-selection" v-if="rowLoaded"><v-checkbox v-model="checkbox" color="primary"
|
|
10
|
+
<th class="cell-selection" v-if="rowLoaded"><v-checkbox v-model="checkbox" density="compact" color="primary"
|
|
11
11
|
@change="checkAll"></v-checkbox></th>
|
|
12
|
-
<th :class="`cell-column${cellIndex}`" v-for="(
|
|
13
|
-
:style="{ minWidth: `${
|
|
14
|
-
<
|
|
12
|
+
<th :class="`cell-column${cellIndex}`" v-for="(headerSpec, cellIndex) in tableHeaders" :key="cellIndex"
|
|
13
|
+
:style="{ minWidth: `${props.spec.viewHeaders[cellIndex].minWidth || 100}px` }">
|
|
14
|
+
<glib-component :spec="headerSpec" />
|
|
15
15
|
</th>
|
|
16
16
|
</tr>
|
|
17
17
|
</thead>
|
|
18
18
|
|
|
19
19
|
<tbody>
|
|
20
20
|
<template v-if="rowLoaded">
|
|
21
|
-
<tr v-for="(row, rowIndex) in currentPageRows" :key="`row_${rowIndex}
|
|
21
|
+
<tr v-for="(row, rowIndex) in currentPageRows" :key="`row_${rowIndex}`"
|
|
22
|
+
:class="row.selected ? 'selected' : 'unselected'">
|
|
22
23
|
<td class="cell-selection">
|
|
23
|
-
<v-checkbox v-model="row.selected" color="primary"></v-checkbox>
|
|
24
|
+
<v-checkbox v-model="row.selected" density="compact" color="primary"></v-checkbox>
|
|
24
25
|
<glib-component :spec="row.icon"></glib-component>
|
|
25
26
|
</td>
|
|
26
27
|
<!-- <td class="cell-status"><v-icon :icon="row.icon.name" :color="row.icon.color"></v-icon></td> -->
|
|
@@ -52,8 +53,8 @@
|
|
|
52
53
|
</table>
|
|
53
54
|
|
|
54
55
|
</div>
|
|
55
|
-
<v-pagination density="compact" v-if="paginationLength > 1"
|
|
56
|
-
:length="paginationLength"></v-pagination>
|
|
56
|
+
<v-pagination :disabled="paginationConfig.disabled" density="compact" v-if="paginationLength > 1"
|
|
57
|
+
v-model="currPageIndex" :length="paginationLength"></v-pagination>
|
|
57
58
|
</div>
|
|
58
59
|
</template>
|
|
59
60
|
|
|
@@ -75,10 +76,14 @@ table thead th {
|
|
|
75
76
|
display: flex;
|
|
76
77
|
align-items: center;
|
|
77
78
|
}
|
|
79
|
+
|
|
80
|
+
tr.selected {
|
|
81
|
+
background-color: white;
|
|
82
|
+
}
|
|
78
83
|
</style>
|
|
79
84
|
|
|
80
85
|
<script setup>
|
|
81
|
-
import { computed, getCurrentInstance, onMounted, ref, watch } from "vue";
|
|
86
|
+
import { computed, getCurrentInstance, onMounted, reactive, ref, watch } from "vue";
|
|
82
87
|
import { parseCsv } from "../composable/parser";
|
|
83
88
|
import Action from "../../action";
|
|
84
89
|
import Hash from "../../utils/hash";
|
|
@@ -97,7 +102,7 @@ class Row {
|
|
|
97
102
|
pending: { name: 'pending', color: 'info' },
|
|
98
103
|
succeeded: { name: 'verified', color: 'success' },
|
|
99
104
|
failed: { name: 'cancel', color: 'error' }
|
|
100
|
-
}
|
|
105
|
+
};
|
|
101
106
|
|
|
102
107
|
// if (!this.status) return {};
|
|
103
108
|
const properties = new Hash(this.status ? obj[this.status] : { name: '' });
|
|
@@ -110,8 +115,9 @@ class Row {
|
|
|
110
115
|
} else {
|
|
111
116
|
return {
|
|
112
117
|
view: 'label',
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
styleClasses: ['status'],
|
|
119
|
+
text: `#${this.index + 1}`
|
|
120
|
+
};
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
123
|
}
|
|
@@ -132,8 +138,20 @@ const props = defineProps(['spec']);
|
|
|
132
138
|
const fileInput = ref(null);
|
|
133
139
|
const checkbox = ref(false);
|
|
134
140
|
const instance = getCurrentInstance();
|
|
141
|
+
const tableHeaders = props.spec.viewHeaders.map((header) => {
|
|
142
|
+
const spec = {
|
|
143
|
+
view: 'label',
|
|
144
|
+
text: header.text
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
if (header.tooltip) {
|
|
148
|
+
spec.tooltip = header.tooltip;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return spec;
|
|
152
|
+
});
|
|
135
153
|
const currPageIndex = ref(1);
|
|
136
|
-
const paginationConfig = { per: 25 };
|
|
154
|
+
const paginationConfig = reactive({ per: 25, disabled: false });
|
|
137
155
|
// const paginationLength = computed(() => Math.ceil(unsavedRows.value.length / paginationConfig.per));
|
|
138
156
|
const paginationLength = computed(() => getPageIndex(unsavedRows.value.length));
|
|
139
157
|
|
|
@@ -141,15 +159,15 @@ const rows = ref(makeRows(props.spec.dataRows));
|
|
|
141
159
|
const indexedRows = ref({});
|
|
142
160
|
let succeededRows = [];
|
|
143
161
|
// const succeededRows = computed(() => rows.value.filter((row) => row.status === 'succeeded'))
|
|
144
|
-
const unsavedRows = computed(() => rows.value.filter((row) => row.status !== 'succeeded'))
|
|
162
|
+
const unsavedRows = computed(() => rows.value.filter((row) => row.status !== 'succeeded'));
|
|
145
163
|
const currentPageRows = computed(() => {
|
|
146
164
|
// const firstItemIndex = currPageIndex.value <= 1 ? 0 : currPageIndex.value * paginationConfig.per;
|
|
147
165
|
|
|
148
166
|
const firstItemIndex = (currPageIndex.value - 1) * paginationConfig.per;
|
|
149
167
|
const lastItemIndex = firstItemIndex + paginationConfig.per;
|
|
150
168
|
|
|
151
|
-
return unsavedRows.value.slice(firstItemIndex, lastItemIndex)
|
|
152
|
-
})
|
|
169
|
+
return unsavedRows.value.slice(firstItemIndex, lastItemIndex);
|
|
170
|
+
});
|
|
153
171
|
const selectedRows = computed(() => {
|
|
154
172
|
const arr = [];
|
|
155
173
|
unsavedRows.value.filter((row) => row.selected).forEach((row) => arr.push(row));
|
|
@@ -168,7 +186,8 @@ onMounted(() => {
|
|
|
168
186
|
}
|
|
169
187
|
|
|
170
188
|
const clonedRows = JSON.parse(JSON.stringify(selectedRows.value));
|
|
171
|
-
Action.execute(props.spec.onSubmitStart, instance.ctx)
|
|
189
|
+
Action.execute(props.spec.onSubmitStart, instance.ctx);
|
|
190
|
+
paginationConfig.disabled = true;
|
|
172
191
|
|
|
173
192
|
importCanceled = false;
|
|
174
193
|
// Reset to first page before importing, so the progress is visible.
|
|
@@ -178,11 +197,11 @@ onMounted(() => {
|
|
|
178
197
|
});
|
|
179
198
|
|
|
180
199
|
|
|
181
|
-
watch(props, (value) => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
200
|
+
// watch(props, (value) => {
|
|
201
|
+
// if (value.spec.dataRows && value.spec.dataRows.length > 0) {
|
|
202
|
+
// rows.value = makeRows(value.spec.dataRows);
|
|
203
|
+
// }
|
|
204
|
+
// });
|
|
186
205
|
|
|
187
206
|
watch(selectedRows, (value) => {
|
|
188
207
|
const rows = value.map((row) => {
|
|
@@ -208,11 +227,11 @@ function handleDrop(e) {
|
|
|
208
227
|
}
|
|
209
228
|
|
|
210
229
|
function updateRows(newRows) {
|
|
211
|
-
rows.value = newRows
|
|
230
|
+
rows.value = newRows;
|
|
212
231
|
|
|
213
232
|
newRows.forEach((row) => {
|
|
214
|
-
indexedRows.value[row.id] = row
|
|
215
|
-
})
|
|
233
|
+
indexedRows.value[row.id] = row;
|
|
234
|
+
});
|
|
216
235
|
}
|
|
217
236
|
|
|
218
237
|
function makeRows(dataRows, selected = false) {
|
|
@@ -221,10 +240,10 @@ function makeRows(dataRows, selected = false) {
|
|
|
221
240
|
// const id = dataRow.rowId || Math.random().toString(36).slice(2, 7);
|
|
222
241
|
const id = dataRow.rowId || `row_${index}`;
|
|
223
242
|
const columns = props.spec.viewCells.map((viewCells, i) => {
|
|
224
|
-
const
|
|
243
|
+
const value = dataRow.columns[i].value;
|
|
244
|
+
const view = Object.assign({}, viewCells, dataRow.columns[i], { value: value });
|
|
225
245
|
const cellIndex = i;
|
|
226
246
|
const viewHeaders = props.spec.viewHeaders;
|
|
227
|
-
const value = dataRow.columns[i].value;
|
|
228
247
|
return new Cell({ rowId: id, view, cellIndex, viewHeaders, value });
|
|
229
248
|
});
|
|
230
249
|
return new Row({ selected, columns, id, index });
|
|
@@ -248,6 +267,7 @@ function handleCellChange(e, cell) {
|
|
|
248
267
|
}
|
|
249
268
|
|
|
250
269
|
cell.value = value;
|
|
270
|
+
cell.view.value = value;
|
|
251
271
|
|
|
252
272
|
Action.executeWithFormData(props.spec.onCellChange, instance.ctx, { rowId, columnId, value });
|
|
253
273
|
}
|
|
@@ -285,7 +305,7 @@ function loadFile(files) {
|
|
|
285
305
|
}
|
|
286
306
|
|
|
287
307
|
function getIndexAmongVisibleRows(row) {
|
|
288
|
-
let indexAmongVisibleRows = 0
|
|
308
|
+
let indexAmongVisibleRows = 0;
|
|
289
309
|
for (const currentRow of rows.value) {
|
|
290
310
|
if (currentRow.status !== 'succeeded') {
|
|
291
311
|
indexAmongVisibleRows++;
|
|
@@ -303,7 +323,7 @@ function getLatestIndexAmongVisibleRows() {
|
|
|
303
323
|
}
|
|
304
324
|
|
|
305
325
|
function getPageIndex(rowIndex) {
|
|
306
|
-
return Math.ceil(rowIndex / paginationConfig.per)
|
|
326
|
+
return Math.ceil(rowIndex / paginationConfig.per);
|
|
307
327
|
}
|
|
308
328
|
|
|
309
329
|
function submitRows(rows) {
|
|
@@ -311,7 +331,8 @@ function submitRows(rows) {
|
|
|
311
331
|
// Move back to the first page because the current page might not have any row anymore, e.g.
|
|
312
332
|
// all the rows on the current page has been successfully submitted.
|
|
313
333
|
currPageIndex.value = getPageIndex(getLatestIndexAmongVisibleRows());
|
|
314
|
-
Action.execute(props.spec.onSubmitEnd, instance.ctx)
|
|
334
|
+
Action.execute(props.spec.onSubmitEnd, instance.ctx);
|
|
335
|
+
paginationConfig.disabled = false;
|
|
315
336
|
return;
|
|
316
337
|
}
|
|
317
338
|
|
|
@@ -321,14 +342,14 @@ function submitRows(rows) {
|
|
|
321
342
|
currPageIndex.value = getPageIndex(getIndexAmongVisibleRows(row));
|
|
322
343
|
|
|
323
344
|
// change row status to pending
|
|
324
|
-
updateRow({ rowId: row.id, status: 'pending' })
|
|
345
|
+
updateRow({ rowId: row.id, status: 'pending' });
|
|
325
346
|
|
|
326
347
|
const formData = row.columns.reduce((prev, curr) => {
|
|
327
348
|
return Object.assign(prev, {
|
|
328
349
|
[curr.cellId]: curr.value,
|
|
329
|
-
[props.spec.paramNameForRowId || rowId]: row.id
|
|
330
|
-
})
|
|
331
|
-
}, {})
|
|
350
|
+
[props.spec.paramNameForRowId || 'rowId']: row.id
|
|
351
|
+
});
|
|
352
|
+
}, {});
|
|
332
353
|
const { submitUrl, paramName } = props.spec.import;
|
|
333
354
|
|
|
334
355
|
const data = {
|
|
@@ -341,14 +362,16 @@ function submitRows(rows) {
|
|
|
341
362
|
|
|
342
363
|
if (importCanceled) {
|
|
343
364
|
importCanceled = false;
|
|
344
|
-
Action.execute(props.spec.onSubmitEnd, instance.ctx)
|
|
365
|
+
Action.execute(props.spec.onSubmitEnd, instance.ctx);
|
|
366
|
+
paginationConfig.disabled = false;
|
|
345
367
|
} else {
|
|
346
368
|
submitRows(rows);
|
|
347
369
|
}
|
|
348
370
|
}, (error, response) => {
|
|
349
371
|
const message = error.toString();
|
|
350
372
|
Utils.launch.snackbar.error(message, instance.ctx);
|
|
351
|
-
Action.execute(props.spec.onSubmitEnd, instance.ctx)
|
|
373
|
+
Action.execute(props.spec.onSubmitEnd, instance.ctx);
|
|
374
|
+
paginationConfig.disabled = false;
|
|
352
375
|
});
|
|
353
376
|
}
|
|
354
377
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
<script>
|
|
16
16
|
import { provide, ref } from "vue";
|
|
17
|
-
import {
|
|
17
|
+
import { useGlibForm } from "../composable/form";
|
|
18
18
|
|
|
19
19
|
export default {
|
|
20
20
|
props: {
|
|
@@ -23,11 +23,12 @@ export default {
|
|
|
23
23
|
setup() {
|
|
24
24
|
const formCtx = ref({ form: null });
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const form = ref(null)
|
|
27
|
+
const glibForm = useGlibForm({ formRef: form });
|
|
27
28
|
|
|
28
29
|
provide('formCtx', formCtx);
|
|
29
30
|
|
|
30
|
-
return { formCtx,
|
|
31
|
+
return { formCtx, glibForm, form };
|
|
31
32
|
},
|
|
32
33
|
data: () => ({
|
|
33
34
|
url: null,
|
|
@@ -109,7 +110,7 @@ export default {
|
|
|
109
110
|
};
|
|
110
111
|
this.formElement.onchange = () => onChangeHandler();
|
|
111
112
|
|
|
112
|
-
this.formElement.oninput = (event) => this.
|
|
113
|
+
this.formElement.oninput = (event) => this.glibForm.updateDirtyState(event);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
// this.parentStyles = this.genericStyles({ width: this.spec.width });
|
package/package.json
CHANGED
package/utils/http.js
CHANGED
|
@@ -108,14 +108,14 @@ export default class {
|
|
|
108
108
|
const windowOrDialog = windowMode ? true : !topOfDialog;
|
|
109
109
|
|
|
110
110
|
this.execute(properties, "GET", component, (data, response) => {
|
|
111
|
+
const redirectUrl = Utils.url.htmlUrl(response.url);
|
|
111
112
|
// TODO: Check if it is okay to remove this `if` statement so we always push even if it's the same URL.
|
|
112
113
|
if (forcePushHistory || (windowOrDialog && !sameUrl && !properties.updateExisting)) {
|
|
113
|
-
const redirectUrl = Utils.url.htmlUrl(response.url);
|
|
114
114
|
Utils.history.pushPage(data, redirectUrl);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
if (!forcePushHistory && windowMode && properties.updateExisting) {
|
|
118
|
-
Utils.history.updatePage(data);
|
|
118
|
+
Utils.history.updatePage(data, redirectUrl);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
this.forceComponentUpdate(() => {
|