@fishawack/lab-velocity 2.0.0-beta.5 → 2.0.0-beta.50

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.
Files changed (111) hide show
  1. package/README.md +467 -36
  2. package/_Build/js/libs/build-id.js +14 -0
  3. package/_Build/js/libs/filters.js +36 -0
  4. package/_Build/js/libs/globals.js +7 -0
  5. package/_Build/js/libs/router.js +22 -0
  6. package/_Build/js/libs/routes.js +29 -0
  7. package/_Build/js/libs/store.js +21 -0
  8. package/_Build/js/libs/utility.js +161 -0
  9. package/_Build/vue/components/basic/Button.vue +1 -1
  10. package/_Build/vue/components/form/Avatar.vue +90 -0
  11. package/_Build/vue/components/form/Checkbox.vue +10 -0
  12. package/_Build/vue/components/form/InputNumber.vue +1 -1
  13. package/_Build/vue/components/form/Select.vue +223 -33
  14. package/_Build/vue/components/form/Spinner.vue +5 -0
  15. package/_Build/vue/components/layout/Alert.vue +5 -5
  16. package/_Build/vue/components/layout/Audit.vue +143 -0
  17. package/_Build/vue/{modules/AuthModule/components/VBreadcrumbs.vue → components/layout/Breadcrumbs.vue} +4 -4
  18. package/_Build/vue/{modules/AuthModule/components → components/layout}/Chips.vue +2 -2
  19. package/_Build/vue/components/layout/Footer.vue +11 -10
  20. package/_Build/vue/{modules/AuthModule/components/VFormFooter.vue → components/layout/FormFooter.vue} +13 -7
  21. package/_Build/vue/{modules/AuthModule/components → components/layout}/FormRole.vue +10 -8
  22. package/_Build/vue/components/layout/Layout.vue +94 -0
  23. package/_Build/vue/components/layout/Navigation.vue +77 -0
  24. package/_Build/vue/{modules/AuthModule/components/VPageHeader.vue → components/layout/PageHeader.vue} +14 -8
  25. package/_Build/vue/components/layout/SideBar.vue +26 -0
  26. package/_Build/vue/{modules/AuthModule/components/VTable.vue → components/layout/Table.vue} +37 -16
  27. package/_Build/vue/{modules/AuthModule/components/VTableSorter.vue → components/layout/TableSorter.vue} +108 -52
  28. package/_Build/vue/components/layout/TokenDisplay.vue +52 -0
  29. package/_Build/vue/components/layout/pageTitle.vue +1 -1
  30. package/_Build/vue/components/navigation/MenuItem.vue +7 -2
  31. package/_Build/vue/components/navigation/MenuItemGroup.vue +7 -2
  32. package/_Build/vue/modules/AuthModule/js/axios.js +21 -1
  33. package/_Build/vue/modules/AuthModule/js/guest-request.js +32 -0
  34. package/_Build/vue/modules/AuthModule/js/impersonation-banner.js +102 -0
  35. package/_Build/vue/modules/AuthModule/js/router.js +91 -114
  36. package/_Build/vue/modules/AuthModule/js/store.js +23 -6
  37. package/_Build/vue/modules/AuthModule/routes/PCompanies/columns.js +268 -0
  38. package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +213 -0
  39. package/_Build/vue/modules/AuthModule/routes/PIntegrations/columns.js +58 -0
  40. package/_Build/vue/modules/AuthModule/routes/PIntegrations/resource.js +79 -0
  41. package/_Build/vue/modules/AuthModule/routes/PTeams/columns.js +78 -0
  42. package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +251 -0
  43. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordAction.vue +51 -0
  44. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordDialog.vue +138 -0
  45. package/_Build/vue/modules/AuthModule/routes/PUsers/columns.js +349 -0
  46. package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +239 -0
  47. package/_Build/vue/modules/AuthModule/routes/account-exists.vue +2 -2
  48. package/_Build/vue/modules/AuthModule/routes/change-password.vue +28 -32
  49. package/_Build/vue/modules/AuthModule/routes/container.vue +2 -11
  50. package/_Build/vue/modules/AuthModule/routes/expired-reset.vue +4 -4
  51. package/_Build/vue/modules/AuthModule/routes/expired-verification.vue +10 -9
  52. package/_Build/vue/modules/AuthModule/routes/force-reset.vue +44 -58
  53. package/_Build/vue/modules/AuthModule/routes/forgot.vue +10 -5
  54. package/_Build/vue/modules/AuthModule/routes/login.vue +12 -19
  55. package/_Build/vue/modules/AuthModule/routes/logincallback.vue +1 -3
  56. package/_Build/vue/modules/AuthModule/routes/loginsso.vue +14 -10
  57. package/_Build/vue/modules/AuthModule/routes/logout.vue +17 -5
  58. package/_Build/vue/modules/AuthModule/routes/logoutheadless.vue +1 -3
  59. package/_Build/vue/modules/AuthModule/routes/register.vue +24 -28
  60. package/_Build/vue/modules/AuthModule/routes/reset.vue +20 -14
  61. package/_Build/vue/modules/AuthModule/routes/success-forgot.vue +14 -8
  62. package/_Build/vue/modules/AuthModule/routes/success-reset.vue +2 -2
  63. package/_Build/vue/modules/AuthModule/routes/success-verify.vue +1 -3
  64. package/_Build/vue/modules/AuthModule/routes/verify.vue +11 -14
  65. package/_Build/vue/modules/resource/Children/create.vue +81 -0
  66. package/_Build/vue/modules/resource/Children/edit.vue +106 -0
  67. package/_Build/vue/modules/resource/Children/index.vue +42 -0
  68. package/_Build/vue/modules/resource/Children/partials/form.vue +111 -0
  69. package/_Build/vue/modules/resource/Children/show.vue +166 -0
  70. package/_Build/vue/modules/resource/index.js +561 -0
  71. package/_Build/vue/modules/resource/parent.vue +63 -0
  72. package/_Build/vue/modules/resource/trashable.js +104 -0
  73. package/_base.scss +0 -1
  74. package/_defaults.scss +2 -13
  75. package/_variables.scss +9 -4
  76. package/{modules/_AuthModule.scss → components/_auth.scss} +19 -68
  77. package/components/_datepicker.scss +1 -0
  78. package/components/_descriptions.scss +2 -0
  79. package/components/_footer.scss +1 -0
  80. package/components/_form.scss +18 -0
  81. package/components/_header.scss +3 -27
  82. package/components/_layout.scss +56 -0
  83. package/components/_menu.scss +0 -5
  84. package/components/_sidebar.scss +12 -27
  85. package/components/_table.scss +3 -0
  86. package/components/_token-display.scss +41 -0
  87. package/general.scss +1 -0
  88. package/index.js +31 -1
  89. package/package.json +7 -4
  90. package/vendor.scss +0 -1
  91. package/_Build/vue/components/layout/sideBar.vue +0 -25
  92. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/Upload/upload.vue +0 -251
  93. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/create.vue +0 -62
  94. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/edit.vue +0 -98
  95. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/index.vue +0 -90
  96. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/partials/form.vue +0 -173
  97. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/show.vue +0 -262
  98. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/parent.vue +0 -36
  99. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/create.vue +0 -112
  100. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/edit.vue +0 -103
  101. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/index.vue +0 -112
  102. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/partials/form.vue +0 -169
  103. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/show.vue +0 -120
  104. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/parent.vue +0 -36
  105. package/components/_input.scss +0 -0
  106. package/modules/_AuthVariables.scss +0 -7
  107. /package/_Build/vue/{modules/AuthModule/components → components/layout}/AuthModal.vue +0 -0
  108. /package/_Build/vue/{modules/AuthModule/components → components/layout}/Chip.vue +0 -0
  109. /package/_Build/vue/{modules/AuthModule/components/VPasswordValidation.vue → components/layout/PasswordValidation.vue} +0 -0
  110. /package/_Build/vue/{modules/AuthModule/components/VRoleLegend.vue → components/layout/RoleLegend.vue} +0 -0
  111. /package/{modules → components}/_modal.scss +0 -0
@@ -0,0 +1,161 @@
1
+ export function eachNode(nodes, cb) {
2
+ nodes = document.querySelectorAll(nodes);
3
+
4
+ for (var i = 0, len = nodes.length; i < len; i++) {
5
+ cb(nodes[i], i);
6
+ }
7
+ }
8
+
9
+ export function parse_query_string(query) {
10
+ var vars = query.split("&");
11
+ var query_string = {};
12
+ for (var i = 0; i < vars.length; i++) {
13
+ var pair = vars[i].split("=");
14
+ // If first entry with this name
15
+ if (typeof query_string[pair[0]] === "undefined") {
16
+ query_string[pair[0]] = decodeURIComponent(pair[1]);
17
+ // If second entry with this name
18
+ } else if (typeof query_string[pair[0]] === "string") {
19
+ var arr = [query_string[pair[0]], decodeURIComponent(pair[1])];
20
+ query_string[pair[0]] = arr;
21
+ // If third or later entry with this name
22
+ } else {
23
+ query_string[pair[0]].push(decodeURIComponent(pair[1]));
24
+ }
25
+ }
26
+ return query_string;
27
+ }
28
+
29
+ export function load(path, mimetype, pages, index, arr) {
30
+ return new Promise((resolve) => {
31
+ index = index || 1;
32
+
33
+ var xobj = new XMLHttpRequest();
34
+ if (mimetype) {
35
+ xobj.overrideMimeType(mimetype);
36
+ }
37
+
38
+ xobj.open(
39
+ "GET",
40
+ pages ? `${path}?per_page=100&page=${index}` : path,
41
+ true,
42
+ );
43
+ xobj.onreadystatechange = function () {
44
+ if (
45
+ xobj.readyState === 4 &&
46
+ (+xobj.status === 200 || +xobj.status === 0)
47
+ ) {
48
+ var data = JSON.parse(xobj.responseText);
49
+
50
+ if (!pages) {
51
+ resolve(data);
52
+ } else {
53
+ var current = +xobj.getResponseHeader("x-wp-totalpages");
54
+
55
+ if (!arr) {
56
+ arr = data || [];
57
+ } else {
58
+ arr = arr.concat(data);
59
+ }
60
+
61
+ if (current && current !== index) {
62
+ load(path, mimetype, pages, ++index, arr).then((res) =>
63
+ resolve(res),
64
+ );
65
+ } else {
66
+ resolve(arr);
67
+ }
68
+ }
69
+ }
70
+ };
71
+ xobj.send(null);
72
+ });
73
+ }
74
+
75
+ export function classList(el) {
76
+ var list = el.classList;
77
+
78
+ return {
79
+ toggle: function (c) {
80
+ list.toggle(c);
81
+ return this;
82
+ },
83
+ add: function (c) {
84
+ list.add(c);
85
+ return this;
86
+ },
87
+ remove: function (c) {
88
+ list.remove(c);
89
+ return this;
90
+ },
91
+ };
92
+ }
93
+
94
+ var blueprints = {};
95
+ export function blueprint(selector, array, cb, root, node) {
96
+ var nodes;
97
+
98
+ if (!node) {
99
+ nodes = (root || document).querySelectorAll(selector);
100
+ } else {
101
+ nodes = [node];
102
+ }
103
+
104
+ var blueprint;
105
+
106
+ if (blueprints[selector]) {
107
+ blueprint = blueprints[selector];
108
+ } else {
109
+ blueprint = nodes[0].children[0];
110
+ blueprint.classList.remove("ut-hide");
111
+ blueprints[selector] = blueprint;
112
+ }
113
+
114
+ var docFrag = document.createDocumentFragment();
115
+
116
+ array.forEach(function (d, i, arr) {
117
+ var item = blueprint.cloneNode(true);
118
+
119
+ cb(item, d, i, arr);
120
+
121
+ docFrag.appendChild(item);
122
+ });
123
+
124
+ for (var i = nodes.length; i--; ) {
125
+ nodes[i].innerHTML = "";
126
+
127
+ nodes[i].appendChild(docFrag);
128
+ }
129
+ }
130
+
131
+ export function styleguide() {
132
+ let colors = [].slice
133
+ .call(document.styleSheets[0].cssRules)
134
+ .filter((d) => d.selectorText && d.selectorText.includes(".color-"))
135
+ .map((d) => d.selectorText);
136
+
137
+ let length = 0;
138
+ /* jshint ignore:start */
139
+ while (colors.find((d) => d === `.color-${length}`)) {
140
+ length++;
141
+ }
142
+ /* jshint ignore:end */
143
+ eachNode(".js-styleguide-dynamic", (node) => {
144
+ let name = node.dataset.styleguideDynamic;
145
+ blueprint(
146
+ name,
147
+ Array(length).fill(0),
148
+ (item, d, i) => {
149
+ let text = item.querySelector(".js-styleguide-index");
150
+ if (text) {
151
+ text.innerText = i;
152
+ }
153
+ classList(item.querySelector(`.${name}`) || item)
154
+ .remove(name)
155
+ .add(`${name}${i}`);
156
+ },
157
+ null,
158
+ node,
159
+ );
160
+ });
161
+ }
@@ -34,7 +34,7 @@ export default {
34
34
  },
35
35
  type: {
36
36
  type: String,
37
- default: null,
37
+ default: "default",
38
38
  },
39
39
  name: {
40
40
  type: String,
@@ -0,0 +1,90 @@
1
+ <template>
2
+ <XInput v-bind="$props">
3
+ <template #label>
4
+ <slot name="label" />
5
+ </template>
6
+
7
+ <el-upload
8
+ action="#"
9
+ :auto-upload="false"
10
+ :show-file-list="false"
11
+ accept="image/*"
12
+ :on-change="onFileChange"
13
+ >
14
+ <img
15
+ v-if="imgSrc"
16
+ :src="imgSrc"
17
+ style="
18
+ max-width: 150px;
19
+ max-height: 150px;
20
+ display: block;
21
+ cursor: pointer;
22
+ margin-bottom: 0.5rem;
23
+ "
24
+ />
25
+ <div v-else style="margin-bottom: 0.5rem">
26
+ <slot name="placeholder">
27
+ <el-button type="primary" plain> Upload Avatar </el-button>
28
+ </slot>
29
+ </div>
30
+ </el-upload>
31
+ <div v-if="content" style="margin-top: 0.5rem">
32
+ <el-button type="danger" size="small" @click="cancel">
33
+ Clear
34
+ </el-button>
35
+ </div>
36
+ </XInput>
37
+ </template>
38
+
39
+ <script>
40
+ import { ElUpload, ElButton } from "element-plus";
41
+ import input from "./input.js";
42
+ import XInput from "./input.vue";
43
+
44
+ export default {
45
+ mixins: [input],
46
+
47
+ components: { XInput, ElUpload, ElButton },
48
+
49
+ props: {
50
+ ...input.props,
51
+ baseClass: {
52
+ type: String,
53
+ default: "vel-basic",
54
+ },
55
+ preview: {
56
+ type: String,
57
+ default: null,
58
+ },
59
+ },
60
+
61
+ data() {
62
+ return {
63
+ imgSrc: null,
64
+ };
65
+ },
66
+
67
+ mounted() {
68
+ this.imgSrc = this.preview;
69
+ },
70
+
71
+ watch: {
72
+ preview(val) {
73
+ if (!this.content) this.imgSrc = val;
74
+ },
75
+ },
76
+
77
+ methods: {
78
+ onFileChange(uploadFile) {
79
+ this.content = uploadFile.raw;
80
+ this.imgSrc = URL.createObjectURL(uploadFile.raw);
81
+ this.handleInput();
82
+ },
83
+ cancel() {
84
+ this.content = null;
85
+ this.imgSrc = this.preview;
86
+ this.handleInput();
87
+ },
88
+ },
89
+ };
90
+ </script>
@@ -6,6 +6,8 @@
6
6
  :disabled="disabled"
7
7
  v-model="content"
8
8
  :required="required"
9
+ :true-label="trueValue"
10
+ :false-label="falseValue"
9
11
  @change="handleInput"
10
12
  >
11
13
  </el-checkbox>
@@ -29,6 +31,14 @@ export default {
29
31
  type: String,
30
32
  default: "vel-checkbox",
31
33
  },
34
+ trueValue: {
35
+ type: [String, Number, Boolean],
36
+ default: true,
37
+ },
38
+ falseValue: {
39
+ type: [String, Number, Boolean],
40
+ default: false,
41
+ },
32
42
  },
33
43
 
34
44
  components: {
@@ -20,7 +20,7 @@
20
20
  :placeholder="placeholder"
21
21
  v-model="content"
22
22
  :required="required"
23
- @input="handleInput"
23
+ @change="handleInput"
24
24
  >
25
25
  <template #prefix>
26
26
  <slot name="prefix" />
@@ -5,6 +5,7 @@
5
5
  </template>
6
6
 
7
7
  <el-select
8
+ ref="select"
8
9
  v-model="content"
9
10
  :class="baseClass"
10
11
  :multiple="multiple"
@@ -14,32 +15,35 @@
14
15
  :collapse-tags="collapseTags"
15
16
  :filterable="filterable"
16
17
  :value-on-clear="null"
17
- @change="handleInput"
18
- @clear="this.$emit('clear')"
19
- @blur="this.$emit('blur')"
18
+ remote-show-suffix
19
+ :remote="!!endpoint"
20
+ :remote-method="endpoint ? handleSearch : undefined"
21
+ :loading="initialLoading"
20
22
  :empty-values="[null, undefined]"
23
+ :value-key="effectiveValueKey"
24
+ @change="handleInput"
25
+ @clear="$emit('clear')"
26
+ @blur="$emit('blur')"
27
+ @visible-change="handleVisibleChange"
21
28
  >
22
29
  <template #default>
23
30
  <slot name="default">
24
31
  <el-option
25
- v-if="!options[0]?.label"
26
- v-for="(label, value) in options"
27
- :key="value"
28
- :label="label"
29
- :value="castValue(value)"
30
- >
31
- </el-option>
32
- <el-option
33
- v-else
34
- v-for="option in options"
35
- :key="option.value"
36
- :label="option.label"
32
+ v-for="option in currentOptions"
33
+ :key="option[effectiveValueKey]"
34
+ :label="option[labelKey]"
37
35
  :disabled="option.disabled"
38
- :value="castValue(option.value)"
36
+ :value="option"
39
37
  >
40
38
  </el-option>
41
39
  </slot>
42
40
  </template>
41
+ <template v-if="endpoint" #loading>
42
+ <div class="el-select-dropdown__loading">Loading...</div>
43
+ </template>
44
+ <template #label="{ value }">
45
+ {{ value[labelKey] || value }}
46
+ </template>
43
47
  </el-select>
44
48
  </XInput>
45
49
  </template>
@@ -47,17 +51,26 @@
47
51
  <script>
48
52
  import { ElSelect } from "element-plus";
49
53
  import { ElOption } from "element-plus";
54
+ import { ElNotification } from "element-plus";
50
55
  import input from "./input.js";
51
56
  import XInput from "./input.vue";
52
57
  import _ from "lodash";
58
+ import axios from "axios";
53
59
 
54
60
  export default {
61
+ components: {
62
+ XInput,
63
+ ElOption,
64
+ ElSelect,
65
+ },
66
+
55
67
  mixins: [input],
68
+
56
69
  props: {
57
70
  ...input.props,
58
71
  modelValue: {
59
- type: Array,
60
- default: [],
72
+ type: [Array, Number],
73
+ default: () => [],
61
74
  },
62
75
  baseClass: {
63
76
  type: String,
@@ -77,32 +90,209 @@ export default {
77
90
  },
78
91
  options: {
79
92
  type: Array,
80
- default: [],
93
+ default: () => [],
81
94
  },
82
95
  multiple: {
83
96
  type: Boolean,
84
97
  default: false,
85
98
  },
99
+ endpoint: {
100
+ type: String,
101
+ default: null,
102
+ },
103
+ params: {
104
+ type: Object,
105
+ default: () => ({}),
106
+ },
107
+ labelKey: {
108
+ type: String,
109
+ default: "label",
110
+ },
111
+ valueKey: {
112
+ type: String,
113
+ default: null,
114
+ },
115
+ searchParam: {
116
+ type: String,
117
+ default: "name",
118
+ },
119
+ perPage: {
120
+ type: Number,
121
+ default: 10,
122
+ },
86
123
  },
87
124
 
88
- components: {
89
- XInput,
90
- ElOption,
91
- ElSelect,
125
+ emits: ["change", "clear", "blur"],
126
+
127
+ data() {
128
+ return {
129
+ initialLoading: false,
130
+ loadingMore: false,
131
+ asyncOptions: [],
132
+ currentPage: 1,
133
+ hasMore: false,
134
+ searchQuery: "",
135
+ lastSearchQuery: "",
136
+ initialLoadDone: false,
137
+ scrollListener: null,
138
+ wrapScrollTop: 0,
139
+ wrapScrollLeft: 0,
140
+ };
141
+ },
142
+
143
+ computed: {
144
+ currentOptions() {
145
+ // If endpoint is provided, use async options, otherwise use static options
146
+ if (this.endpoint) {
147
+ return this.asyncOptions;
148
+ }
149
+ return this.options;
150
+ },
151
+ effectiveValueKey() {
152
+ // If valueKey was explicitly provided, use it
153
+ if (this.valueKey !== null) {
154
+ return this.valueKey;
155
+ }
156
+ // Otherwise, use "id" for endpoints, "value" for static options
157
+ return this.endpoint ? "id" : "value";
158
+ },
159
+ },
160
+
161
+ mounted() {
162
+ if (this.endpoint && this.$refs.select?.$refs?.scrollbarRef) {
163
+ const scrollbar = this.$refs.select.$refs.scrollbarRef;
164
+ const wrapRef = scrollbar.wrapRef;
165
+
166
+ this.scrollListener = () => {
167
+ if (!wrapRef) return;
168
+
169
+ const distance = 10;
170
+ const prevTop = this.wrapScrollTop;
171
+ const prevLeft = this.wrapScrollLeft;
172
+
173
+ this.wrapScrollTop = wrapRef.scrollTop;
174
+ this.wrapScrollLeft = wrapRef.scrollLeft;
175
+
176
+ const arrivedStates = {
177
+ bottom:
178
+ this.wrapScrollTop + wrapRef.clientHeight >=
179
+ wrapRef.scrollHeight - distance,
180
+ top: this.wrapScrollTop <= distance && prevTop !== 0,
181
+ right:
182
+ this.wrapScrollLeft + wrapRef.clientWidth >=
183
+ wrapRef.scrollWidth - distance &&
184
+ prevLeft !== this.wrapScrollLeft,
185
+ left: this.wrapScrollLeft <= distance && prevLeft !== 0,
186
+ };
187
+
188
+ let direction = null;
189
+ if (prevTop !== this.wrapScrollTop) {
190
+ direction = this.wrapScrollTop > prevTop ? "bottom" : "top";
191
+ }
192
+ if (prevLeft !== this.wrapScrollLeft) {
193
+ direction =
194
+ this.wrapScrollLeft > prevLeft ? "right" : "left";
195
+ }
196
+
197
+ if (direction && arrivedStates[direction]) {
198
+ this.handleEndReached(direction);
199
+ }
200
+ };
201
+
202
+ wrapRef.addEventListener("scroll", this.scrollListener);
203
+ }
204
+ },
205
+
206
+ beforeUnmount() {
207
+ if (
208
+ this.scrollListener &&
209
+ this.$refs.select?.$refs?.scrollbarRef?.wrapRef
210
+ ) {
211
+ this.$refs.select.$refs.scrollbarRef.wrapRef.removeEventListener(
212
+ "scroll",
213
+ this.scrollListener,
214
+ );
215
+ }
92
216
  },
93
217
 
94
- emits: ["change", "clear", "blur"],
95
218
  methods: {
96
- castValue(value) {
97
- if (
98
- !_.isNull(value) &&
99
- typeof value === "string" &&
100
- value !== "" &&
101
- !isNaN(Number(value))
102
- ) {
103
- return parseInt(value);
219
+ async handleVisibleChange(visible) {
220
+ // Load data when dropdown is opened for the first time
221
+ if (visible && this.endpoint && !this.initialLoadDone) {
222
+ await this.handleSearch("");
223
+ }
224
+ },
225
+
226
+ async handleSearch(query) {
227
+ if (!this.endpoint) return;
228
+
229
+ // For initial load, allow empty query even if it matches lastSearchQuery
230
+ if (!this.initialLoadDone || query !== this.lastSearchQuery) {
231
+ this.searchQuery = query;
232
+ this.lastSearchQuery = query;
233
+ this.currentPage = 1;
234
+ this.asyncOptions = [];
235
+ await this.fetchOptions(1, query);
236
+
237
+ if (!this.initialLoadDone) {
238
+ this.initialLoadDone = true;
239
+ }
240
+ }
241
+ },
242
+
243
+ handleEndReached(direction) {
244
+ if (!this.endpoint) return;
245
+ if (direction !== "bottom") return;
246
+ if (!this.hasMore || this.loadingMore) return;
247
+ this.fetchOptions(this.currentPage + 1, this.searchQuery);
248
+ },
249
+
250
+ async fetchOptions(page, searchQuery = "") {
251
+ const isInitialLoad = page === 1;
252
+ const loadingKey = isInitialLoad ? "initialLoading" : "loadingMore";
253
+
254
+ if (this[loadingKey]) return;
255
+
256
+ this[loadingKey] = true;
257
+
258
+ try {
259
+ const requestParams = {
260
+ ...this.params,
261
+ page,
262
+ per_page: this.perPage,
263
+ };
264
+
265
+ // Add search filter if query is provided
266
+ if (searchQuery) {
267
+ requestParams[`filter[${this.searchParam}]`] = searchQuery;
268
+ }
269
+
270
+ const response = await axios.get(this.endpoint, {
271
+ params: requestParams,
272
+ });
273
+
274
+ const data = response.data.data || [];
275
+ const meta = response.data.meta;
276
+
277
+ // If it's the first page, replace options, otherwise append
278
+ if (page === 1) {
279
+ this.asyncOptions = data;
280
+ } else {
281
+ this.asyncOptions = [...this.asyncOptions, ...data];
282
+ }
283
+
284
+ this.currentPage = page;
285
+ this.hasMore = meta && meta.current_page < meta.last_page;
286
+ } catch (error) {
287
+ console.error("Error fetching select options:", error);
288
+ ElNotification.error({
289
+ title: "Error",
290
+ message: "Failed to load options. Please try again.",
291
+ duration: 5000,
292
+ });
293
+ } finally {
294
+ this[loadingKey] = false;
104
295
  }
105
- return value;
106
296
  },
107
297
  },
108
298
  };
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <p>Loading...</p>
3
+ </template>
4
+
5
+ <script></script>
@@ -11,7 +11,11 @@
11
11
  <script>
12
12
  import { ElAlert } from "element-plus";
13
13
  export default {
14
- name: "VPageTitle",
14
+ name: "Alert",
15
+
16
+ components: {
17
+ ElAlert,
18
+ },
15
19
 
16
20
  props: {
17
21
  effect: {
@@ -30,9 +34,5 @@ export default {
30
34
  default: true,
31
35
  },
32
36
  },
33
-
34
- components: {
35
- ElAlert,
36
- },
37
37
  };
38
38
  </script>