renusify 2.5.1 → 3.0.0

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 (212) hide show
  1. package/components/app/index.vue +74 -22
  2. package/components/app/toast/index.vue +76 -71
  3. package/components/app/toast/toast.vue +62 -44
  4. package/components/avatar/index.vue +208 -84
  5. package/components/button/buttonConfirm.vue +53 -26
  6. package/components/button/buttonGroup.js +0 -2
  7. package/components/button/buttonGroup.vue +310 -62
  8. package/components/button/index.vue +584 -100
  9. package/components/calendar/index.js +0 -2
  10. package/components/calendar/index.vue +326 -262
  11. package/components/calendar/month.vue +64 -55
  12. package/components/calendar/year.vue +30 -25
  13. package/components/card/index.vue +139 -59
  14. package/components/codeEditor/highlightCss.vue +38 -39
  15. package/components/codeEditor/highlightHtml.vue +64 -64
  16. package/components/codeEditor/highlightJs.vue +37 -38
  17. package/components/codeEditor/index.vue +129 -79
  18. package/components/codeEditor/run.vue +225 -39
  19. package/components/codeEditor/useCodeFormatter.js +150 -0
  20. package/components/confirm/index.vue +140 -81
  21. package/components/container/col.vue +5 -4
  22. package/components/container/divider.vue +28 -19
  23. package/components/container/index.vue +34 -15
  24. package/components/container/row.vue +26 -9
  25. package/components/container/spacer.vue +2 -4
  26. package/components/container/style.scss +3 -0
  27. package/components/content/index.vue +49 -32
  28. package/components/cropper/index.vue +401 -244
  29. package/components/float/index.vue +542 -415
  30. package/components/form/addressInput/index.vue +184 -109
  31. package/components/form/camInput/index.vue +370 -244
  32. package/components/form/checkInput/index.vue +138 -71
  33. package/components/form/checkboxInput/index.vue +87 -47
  34. package/components/form/colorInput/Alpha.vue +81 -83
  35. package/components/form/colorInput/Hue.vue +91 -68
  36. package/components/form/colorInput/Preview.vue +43 -47
  37. package/components/form/colorInput/Saturation.vue +101 -86
  38. package/components/form/colorInput/index.vue +72 -40
  39. package/components/form/colorInput/picker.vue +111 -106
  40. package/components/form/colorInput/useColor.js +153 -0
  41. package/components/form/dateInput/index.vue +691 -356
  42. package/components/form/dateInput/month.vue +63 -54
  43. package/components/form/dateInput/year.vue +35 -25
  44. package/components/form/fileInput/index.js +0 -1
  45. package/components/form/fileInput/index.vue +263 -106
  46. package/components/form/fileInput/single.vue +323 -164
  47. package/components/form/groupInput/index.vue +199 -101
  48. package/components/form/index.vue +189 -83
  49. package/components/form/input/index.vue +416 -377
  50. package/components/form/jsonInput/JsonView.vue +54 -56
  51. package/components/form/jsonInput/index.vue +247 -165
  52. package/components/form/maskInput/index.vue +252 -132
  53. package/components/form/numberInput/index.js +0 -1
  54. package/components/form/numberInput/index.vue +226 -117
  55. package/components/form/passwordInput/index.js +2 -1
  56. package/components/form/passwordInput/index.vue +269 -102
  57. package/components/form/radioInput/index.vue +143 -72
  58. package/components/form/rangeInput/index.vue +280 -167
  59. package/components/form/ratingInput/index.vue +57 -57
  60. package/components/form/selectInput/index.js +1 -3
  61. package/components/form/selectInput/index.vue +584 -296
  62. package/components/form/switchInput/index.vue +73 -59
  63. package/components/form/telInput/index.js +0 -1
  64. package/components/form/telInput/index.vue +238 -135
  65. package/components/form/textArea/index.vue +72 -35
  66. package/components/form/textEditor/index.vue +739 -0
  67. package/components/form/{text-editor → textEditor}/style.scss +8 -16
  68. package/components/form/textInput/index.vue +54 -32
  69. package/components/form/timeInput/index.vue +83 -56
  70. package/components/form/timeInput/range.vue +116 -95
  71. package/components/form/timeInput/timepicker.vue +382 -449
  72. package/components/form/uniqueInput/index.vue +105 -48
  73. package/components/form/unitInput/index.vue +139 -84
  74. package/components/formCreator/index.js +0 -1
  75. package/components/formCreator/index.vue +314 -148
  76. package/components/highlight/index.vue +41 -25
  77. package/components/highlight/style.scss +2 -2
  78. package/components/highlight/{mixin.js → useHighlight.js} +181 -160
  79. package/components/icon/index.vue +79 -33
  80. package/components/img/index.vue +249 -147
  81. package/components/img/preview.vue +180 -198
  82. package/components/img/svgImg.vue +42 -39
  83. package/components/index.js +5 -20
  84. package/components/infinite/index.js +1 -2
  85. package/components/infinite/index.vue +248 -66
  86. package/components/map/index.vue +428 -261
  87. package/components/map/route.vue +794 -487
  88. package/components/map/select.vue +118 -58
  89. package/components/menu/index.vue +201 -91
  90. package/components/meta/meta.js +26 -3
  91. package/components/modal/index.vue +383 -158
  92. package/components/notify/index.vue +204 -86
  93. package/components/notify/notification.vue +38 -55
  94. package/components/progress/circle.vue +189 -70
  95. package/components/progress/line.vue +266 -46
  96. package/components/searchBox/index.js +1 -3
  97. package/components/searchBox/index.vue +194 -101
  98. package/components/skeleton/index.vue +45 -20
  99. package/components/slider/index.vue +318 -156
  100. package/components/swiper/index.vue +254 -106
  101. package/components/table/crud/footer.vue +77 -53
  102. package/components/table/crud/header.vue +71 -72
  103. package/components/table/crud/index.vue +631 -401
  104. package/components/table/index.vue +721 -278
  105. package/components/timeAgo/index.vue +145 -96
  106. package/components/tour/index.vue +338 -235
  107. package/components/tree/index.vue +235 -89
  108. package/components/tree/tree-element.vue +107 -106
  109. package/directive/animate/index.js +77 -0
  110. package/directive/clickOutSide/index.js +98 -0
  111. package/directive/drag/index.js +153 -0
  112. package/directive/index.js +11 -13
  113. package/directive/intersect/index.js +263 -0
  114. package/directive/mask/index.js +67 -0
  115. package/directive/parallax/index.js +78 -0
  116. package/directive/ripple/index.js +14 -0
  117. package/directive/scroll/index.js +244 -0
  118. package/directive/sortable/index.js +274 -0
  119. package/directive/title/index.js +75 -0
  120. package/directive/touch/index.js +268 -0
  121. package/index.js +10 -8
  122. package/package.json +5 -2
  123. package/plugins/validation/Validate.js +88 -79
  124. package/scripts/generate-docs.mjs +226 -0
  125. package/scripts/menu.mjs +240 -0
  126. package/scripts/parser.mjs +1086 -0
  127. package/style/_index.scss +7 -0
  128. package/style/app.scss +13 -65
  129. package/style/colors.scss +5 -22
  130. package/style/functions/index.scss +8 -0
  131. package/style/mixins/index.scss +17 -5
  132. package/style/variables/base.scss +154 -175
  133. package/style/variables/color.scss +0 -12
  134. package/style/variables/utilities.scss +0 -180
  135. package/tools/helper.js +0 -8
  136. package/tools/icons.js +6 -1
  137. package/tools/root.js +71 -0
  138. package/components/app/style.scss +0 -41
  139. package/components/app/toast/style.scss +0 -20
  140. package/components/avatar/style.scss +0 -32
  141. package/components/bar/bottomNav.js +0 -1
  142. package/components/bar/bottomNav.vue +0 -28
  143. package/components/bar/bottomNavigationCircle.js +0 -2
  144. package/components/bar/bottomNavigationCircle.vue +0 -99
  145. package/components/bar/scss/bottomNav.scss +0 -67
  146. package/components/bar/scss/toolbar.scss +0 -174
  147. package/components/bar/toolbar/index.js +0 -8
  148. package/components/bar/toolbar/index.vue +0 -35
  149. package/components/bar/toolbar/laptop.vue +0 -33
  150. package/components/bar/toolbar/menuChilds.vue +0 -41
  151. package/components/bar/toolbar/menuLaptop.vue +0 -41
  152. package/components/bar/toolbar/menuMob.vue +0 -39
  153. package/components/bar/toolbar/mixin.js +0 -43
  154. package/components/bar/toolbar/mobile.vue +0 -34
  155. package/components/breadcrumb/bredcrumbItem.vue +0 -39
  156. package/components/breadcrumb/index.js +0 -3
  157. package/components/breadcrumb/index.vue +0 -71
  158. package/components/breadcrumb/style.scss +0 -51
  159. package/components/button/style.scss +0 -411
  160. package/components/card/style.scss +0 -86
  161. package/components/chart/chart.js +0 -1
  162. package/components/chart/chart.vue +0 -69
  163. package/components/chart/worldMap.js +0 -2
  164. package/components/chart/worldMap.vue +0 -1112
  165. package/components/chat/MessageList.vue +0 -163
  166. package/components/chat/chatInput.vue +0 -150
  167. package/components/chat/chatMsg.vue +0 -276
  168. package/components/chat/index.js +0 -11
  169. package/components/chat/index.vue +0 -113
  170. package/components/chip/index.js +0 -3
  171. package/components/chip/index.vue +0 -77
  172. package/components/chip/style.scss +0 -199
  173. package/components/codeEditor/mixin.js +0 -145
  174. package/components/countdown/index.js +0 -1
  175. package/components/countdown/index.vue +0 -105
  176. package/components/form/colorInput/mixin.js +0 -132
  177. package/components/form/fileInput/file.js +0 -148
  178. package/components/form/telInput/assets/flags.png +0 -0
  179. package/components/form/telInput/assets/flags@2x.png +0 -0
  180. package/components/form/text-editor/index.vue +0 -710
  181. package/components/icon/style.scss +0 -17
  182. package/components/infinite/div.js +0 -6
  183. package/components/infinite/div.vue +0 -193
  184. package/components/infinite/page.js +0 -3
  185. package/components/infinite/page.vue +0 -105
  186. package/components/list/index.js +0 -3
  187. package/components/list/index.vue +0 -122
  188. package/components/list/style.scss +0 -66
  189. package/components/message/index.js +0 -4
  190. package/components/message/index.vue +0 -40
  191. package/components/modal/style.scss +0 -146
  192. package/components/nestable/NestableItem.vue +0 -307
  193. package/components/nestable/editable.js +0 -44
  194. package/components/nestable/index.js +0 -1
  195. package/components/nestable/index.vue +0 -226
  196. package/components/nestable/methods.js +0 -416
  197. package/components/progress/style.scss +0 -229
  198. package/components/table/style.scss +0 -338
  199. package/components/tabs/index.js +0 -3
  200. package/components/tabs/index.vue +0 -151
  201. package/components/timeline/index.js +0 -6
  202. package/components/timeline/index.vue +0 -76
  203. package/directive/resize/index.js +0 -30
  204. package/directive/skeleton/index.js +0 -27
  205. package/directive/skeleton/style.scss +0 -37
  206. package/plugins/request/Request.js +0 -68
  207. package/style/animation.scss +0 -94
  208. package/style/style.scss +0 -8
  209. package/tools/rootable.js +0 -75
  210. /package/components/form/{text-editor → textEditor}/index.js +0 -0
  211. /package/components/form/{text-editor → textEditor}/preview.js +0 -0
  212. /package/components/form/{text-editor → textEditor}/preview.vue +0 -0
@@ -1,6 +1,14 @@
1
1
  <template>
2
2
  <div :class="`${$r.prefix}table-manage`">
3
- <r-modal v-model="showForm" bottom full-width>
3
+ <r-modal v-model="showForm" full-width position="bottom">
4
+ <!-- @slot Slot for custom form rendering
5
+ @binding {Boolean} autoSend - Whether to auto-send form
6
+ @binding {String} method - HTTP method (post/put)
7
+ @binding {Object} modelValue - Edited item data
8
+ @binding {Function} ok - Function to call on successful form submission
9
+ @binding {Object} options - Form field options from table headers
10
+ @binding {String} title - Form title
11
+ @binding {String} url - Form submission URL -->
4
12
  <slot :autoSend="autoSend"
5
13
  :method="method"
6
14
  :modelValue="editedItem"
@@ -19,7 +27,7 @@
19
27
  ></r-form-creator>
20
28
  </slot>
21
29
  </r-modal>
22
- <r-modal v-model="showCopy" bottom full-width>
30
+ <r-modal v-model="showCopy" full-width position="bottom">
23
31
  <div class="pa-3">
24
32
  <div v-for="(item,key) in table.option" :key="key"
25
33
  class="mb-5">
@@ -60,67 +68,78 @@
60
68
  @edit="copyHandle('edit')"
61
69
  @update:modelValue="searching()"
62
70
  @a-search="(a_search=$event),(searching())"></manage-header>
63
- <div v-if="card" class="d-flex overflow-x-auto">
64
- <div class="d-flex">
65
- <r-card v-for="(item,i) in table.data" :key="i" :class="{'br-lg':!$r.inputs.tile}"
66
- class="pa-3 me-3"
67
- style="width: 300px">
68
- <div v-for="(h,j) in headerTable" :key="i+'-'+j"
69
- class="d-flex text-no-wrap overflow-x-auto">
70
- <slot :editItem="editItem" :item="item" :value="h" :name="'td-'+h.value">
71
- <slot :editItem="editItem" :item="item" :value="h" name="card">
72
- <div v-if="h['option']['type']==='r-date-input' && item[h['value']]!==undefined"
73
- class="py-2">
74
- {{ h['text'] }}:
75
- {{ $d(new Date(item[h['value']]), h['option']['format'] || 'short') }}
71
+ <template v-if="card">
72
+ <!-- @slot Slot for custom cards view rendering
73
+ @binding {Function} deleteItem - Function to delete an item
74
+ @binding {Function} editItem - Function to edit an item
75
+ @binding {Array} header - Table headers
76
+ @binding {Array} items - Table data items -->
77
+ <slot :deleteItem="deleteItem" :editItem="editItem" :header="headerTable" :items="table.data" name="cards">
78
+ <div class="d-flex overflow-x-auto">
79
+ <div class="d-flex">
80
+ <r-card v-for="(item,i) in table.data" :key="i"
81
+ class="pa-3 me-3"
82
+ style="width: 300px">
83
+ <div v-for="(h,j) in headerTable" :key="i+'-'+j"
84
+ class="d-flex text-no-wrap overflow-x-auto">
85
+ <!-- @slot Dynamic slot for card content, named 'td-[h.value]'
86
+ @binding {Function} editItem - Function to edit an item
87
+ @binding {Object} item - Card data item
88
+ @binding {Object} value - Header configuration -->
89
+ <slot :editItem="editItem" :item="item" :name="'td-'+h.value" :value="h">
90
+ <div v-if="h['option']['type']==='r-date-input' && item[h['value']]!==undefined"
91
+ class="py-2">
92
+ {{ h['text'] }}:
93
+ {{ $d(new Date(item[h['value']]), h['option']['format'] || 'short') }}
94
+ </div>
95
+ <div
96
+ v-else-if="h['option']['type']==='r-time-ago' && item[h['value']]!==undefined"
97
+ class="py-2">
98
+ {{ h['text'] }}:
99
+ <r-time-ago :time="item[h['value']]"></r-time-ago>
100
+ </div>
101
+ <div v-else-if="h['option']['type']==='r-switch-input'" class="d-flex py-2">
102
+ {{ h['text'] }}:
103
+ <r-switch-input
104
+ :modelValue="item[h['value']]"
105
+ :readonly="h['option']['formInput']===false"
106
+ class="mt-0"
107
+ @update:modelValue="h['option']['formInput']!==false?editItem(item,true,h['value']):''"
108
+ ></r-switch-input>
109
+ </div>
110
+ <div v-else-if="h['option']['type'] === 'r-number-input'" class="py-2">
111
+ {{ h['text'] }}: {{ $n(item[h['value']]) }}
112
+ </div>
113
+ <div v-else-if="h['option']['type']!=='action'" class="py-2">
114
+ {{ h['text'] }}: {{
115
+ h['value'] in cast ?
116
+ $helper.ifHas(item, '', h['value'], cast[h['value']])
117
+ : item[h['value']]
118
+ }}
119
+ </div>
120
+ </slot>
121
+ <div v-if="h['option']['type']==='action'" class="w-full text-end">
122
+ <r-divider class="mt-3"></r-divider>
123
+ <r-btn v-if="!disableUpdate" class="mx-0 color-success-text"
124
+ icon text @click.prevent="editItem(item)">
125
+ <r-icon exact v-html="$r.icons.edit"></r-icon>
126
+ </r-btn>
127
+ <r-btn v-if="!disableDelete" class="mx-0 color-error-text"
128
+ icon text @click.prevent="deleteItem(item)">
129
+ <r-icon v-html="$r.icons.delete"></r-icon>
130
+ </r-btn>
131
+ <r-btn v-for="(val,index) in actions" :key="index" :class="`color-${val.color}-text`"
132
+ class="mx-0" icon
133
+ text @click.prevent="$emit(val.name,item)">
134
+ <r-icon exact v-html="val.icon"></r-icon>
135
+ </r-btn>
76
136
  </div>
77
- <div
78
- v-else-if="h['option']['type']==='r-time-ago' && item[h['value']]!==undefined"
79
- class="py-2">
80
- {{ h['text'] }}:
81
- <r-time-ago :time="item[h['value']]"></r-time-ago>
82
- </div>
83
- <div v-else-if="h['option']['type']==='r-switch-input'" class="d-flex py-2">
84
- {{ h['text'] }}:
85
- <r-switch-input
86
- :modelValue="item[h['value']]"
87
- :readonly="h['option']['formInput']===false"
88
- class="mt-0"
89
- @update:modelValue="h['option']['formInput']!==false?editItem(item,true,h['value']):''"
90
- ></r-switch-input>
91
- </div>
92
- <div v-else-if="h['option']['type'] === 'r-number-input'" class="py-2">
93
- {{ h['text'] }}: {{ $n(item[h['value']]) }}
94
- </div>
95
- <div v-else-if="h['option']['type']!=='action'" class="py-2">
96
- {{ h['text'] }}: {{
97
- h['value'] in cast ?
98
- $helper.ifHas(item, '', h['value'], cast[h['value']])
99
- : item[h['value']]
100
- }}
101
- </div>
102
- </slot>
103
- </slot>
104
- <div v-if="h['option']['type']==='action'" class="w-full text-end">
105
- <r-divider class="mt-3"></r-divider>
106
- <r-btn v-if="!disableUpdate" class="mx-0 color-success-text"
107
- icon text @click.prevent="editItem(item)">
108
- <r-icon exact v-html="$r.icons.edit"></r-icon>
109
- </r-btn>
110
- <r-btn v-if="!disableDelete" class="mx-0 color-error-text"
111
- icon text @click.prevent="deleteItem(item)">
112
- <r-icon v-html="$r.icons.delete"></r-icon>
113
- </r-btn>
114
- <r-btn v-for="(val,index) in actions" :key="index" :class="`color-${val.color}-text`"
115
- class="mx-0" icon
116
- text @click.prevent="$emit(val.name,item)">
117
- <r-icon exact v-html="val.icon"></r-icon>
118
- </r-btn>
119
- </div>
137
+ </div>
138
+ </r-card>
120
139
  </div>
121
- </r-card>
122
- </div>
123
- </div>
140
+ </div>
141
+ </slot>
142
+ </template>
124
143
  <r-table v-else :headers="headerTable" :items="table.data" :key-item="itemId"
125
144
  :responsive="responsive"
126
145
  transition="table-row">
@@ -144,9 +163,9 @@
144
163
  }" class="w-10 icon-holder"
145
164
  >
146
165
  <r-icon v-if="(sortType === 2 && sortBy === item.value)"
147
- v-html="this.$r.icons.arrow_down"></r-icon>
166
+ v-html="$r.icons.arrow_down"></r-icon>
148
167
  <r-icon v-if="(sortType !== 2 || sortBy !== item.value)"
149
- v-html="this.$r.icons.arrow_up"></r-icon>
168
+ v-html="$r.icons.arrow_up"></r-icon>
150
169
 
151
170
  </div>
152
171
 
@@ -155,6 +174,10 @@
155
174
  </template>
156
175
 
157
176
  <template v-slot:row="props">
177
+ <!-- @slot Slot for custom row rendering in table view
178
+ @binding {Function} deleteItem - Function to delete an item
179
+ @binding {Function} editItem - Function to edit an item
180
+ @binding {Object} table - Table props object -->
158
181
  <slot :deleteItem="deleteItem" :editItem="editItem" :table="props" name="row">
159
182
  <td v-for="(value,key2) in props.th"
160
183
  :key="`td-${key2}`">
@@ -168,37 +191,39 @@
168
191
  icon text @click.prevent="props.open(props.key)">
169
192
  <r-icon v-html="props.opened!==props.key?$r.icons.plus:$r.icons.minus"></r-icon>
170
193
  </r-btn>
194
+ <!-- @slot Dynamic slot for table cell content, named 'td-[value.value]'
195
+ @binding {Function} editItem - Function to edit an item
196
+ @binding {Object} item - Row data item
197
+ @binding {Object} value - Header configuration -->
171
198
  <slot :editItem="editItem" :item="props.item" :value="value" :name="'td-'+value.value">
172
- <slot :editItem="editItem" :item="props.item" :value="value" name="cell">
173
- <div
174
- v-if="value['option']['type']==='r-date-input' && props.item[value['value']]!==undefined">
175
- {{
199
+ <div
200
+ v-if="value['option']['type']==='r-date-input' && props.item[value['value']]!==undefined">
201
+ {{
176
202
  $d(new Date(props.item[value['value']]), value['option']['format'] || 'short')
177
- }}
178
- </div>
179
- <div
180
- v-else-if="value['option']['type']==='r-time-ago' && props.item[value['value']]!==undefined">
181
- <r-time-ago :time="props.item[value['value']]"></r-time-ago>
182
- </div>
183
- <div v-else-if="value['option']['type']==='r-switch-input'">
184
- <r-switch-input
185
- :modelValue="props.item[value['value']]"
186
- :readonly="value['option']['formInput']===false"
187
- class="mt-0"
188
- @update:modelValue="value['option']['formInput']!==false?editItem(props.item,true,value['value']):''"
189
- ></r-switch-input>
190
- </div>
191
- <div v-else-if="value['option']['type'] === 'r-number-input'">
192
- {{ $n(props.item[value['value']]) }}
193
- </div>
194
- <div v-else-if="value['option']['type']!=='action'">
195
- {{
203
+ }}
204
+ </div>
205
+ <div
206
+ v-else-if="value['option']['type']==='r-time-ago' && props.item[value['value']]!==undefined">
207
+ <r-time-ago :time="props.item[value['value']]"></r-time-ago>
208
+ </div>
209
+ <div v-else-if="value['option']['type']==='r-switch-input'">
210
+ <r-switch-input
211
+ :modelValue="props.item[value['value']]"
212
+ :readonly="value['option']['formInput']===false"
213
+ class="mt-0"
214
+ @update:modelValue="value['option']['formInput']!==false?editItem(props.item,true,value['value']):''"
215
+ ></r-switch-input>
216
+ </div>
217
+ <div v-else-if="value['option']['type'] === 'r-number-input'">
218
+ {{ $n(props.item[value['value']]) }}
219
+ </div>
220
+ <div v-else-if="value['option']['type']!=='action'">
221
+ {{
196
222
  value['value'] in cast ?
197
- $helper.ifHas(props.item, '', value['value'], cast[value['value']])
198
- : props.item[value['value']]
199
- }}
200
- </div>
201
- </slot>
223
+ $helper.ifHas(props.item, '', value['value'], cast[value['value']])
224
+ : props.item[value['value']]
225
+ }}
226
+ </div>
202
227
  </slot>
203
228
  <div v-if="value['option']['type']==='action'">
204
229
  <r-btn v-if="!disableUpdate" class="mx-0 color-success-text"
@@ -222,6 +247,7 @@
222
247
  </r-table>
223
248
  <manage-footer v-model:page="page" v-model:per-page="itemsPerPage"
224
249
  :total="table.total"></manage-footer>
250
+
225
251
  <r-confirm
226
252
  v-model="showConfirm"
227
253
  hard
@@ -231,346 +257,550 @@
231
257
  </div>
232
258
  </template>
233
259
 
234
- <script>
235
- import {defineAsyncComponent} from 'vue'
236
-
237
- export default {
238
- name: 'r-table-crud',
239
- components: {
240
- ManageHeader: defineAsyncComponent(() =>
241
- import('./header.vue')
242
- ), ManageFooter: defineAsyncComponent(() =>
243
- import('./footer.vue')
244
- )
260
+ <script setup>
261
+ import {ref, computed, watch, onMounted, inject} from 'vue'
262
+ import ManageFooter from "./footer.vue";
263
+ import ManageHeader from "./header.vue";
264
+
265
+
266
+ const props = defineProps({
267
+ /**
268
+ * API endpoint URL for data operations
269
+ * @type {String}
270
+ * @required
271
+ */
272
+ link: {
273
+ required: true,
274
+ type: String
245
275
  },
246
- props: {
247
- link: {
248
- required: true,
249
- type: String
250
- },
251
- actions: {
252
- default: function () {
253
- return []
254
- },
255
- type: Array
256
- },
257
- cast: {
258
- default: function () {
259
- return {}
260
- },
261
- type: Object
262
- },
263
- perPage: {
264
- type: Object, default: () => {
265
- return {name: '10', value: 10}
266
- }
267
- },
268
- query: Object,
269
- responsive: {
270
- type: Boolean,
271
- default: true
272
- },
273
-
274
- card: Boolean,
275
- disableAdd: Boolean,
276
- advanceSearch: {type: Boolean, default: true},
277
- disableDelete: Boolean,
278
- disableUpdate: Boolean,
279
- mcud: String,
280
- itemId: {type: String, default: '_id'},
281
- headers: Object
276
+ /**
277
+ * Array of custom action buttons configuration
278
+ * @type {Array}
279
+ * @default () => []
280
+ */
281
+ actions: {
282
+ default: () => [],
283
+ type: Array
282
284
  },
283
- emits: ['actions'],
284
- data() {
285
- return {
286
- time_out_id: null,
287
- loading: false,
288
- showForm: false,
289
- showCopy: false,
290
- showConfirm: false,
291
- search: '',
292
- a_search: {},
293
- copyItem: {'d': {}, 'c': 1},
294
- editedItem: {},
295
- deleted: '',
296
- url: '',
297
- method: 'post',
298
- title: '',
299
- autoSend: false,
300
- itemsPerPage: this.perPage,
301
- page: 1,
302
- sortBy: null,
303
- sortType: 0,
304
- table: {
305
- headers: [],
306
- option: {},
307
- data: [],
308
- startTime: false,
309
- total: 0
310
- },
311
- check_all: false,
312
- checked: {}
313
- }
285
+ /**
286
+ * Object mapping field names to nested property paths for display
287
+ * @type {Object}
288
+ * @default () => ({})
289
+ */
290
+ cast: {
291
+ default: () => ({}),
292
+ type: Object
314
293
  },
315
- created() {
316
- this.refresh()
294
+ /**
295
+ * Default items per page configuration
296
+ * @type {Object}
297
+ * @default () => ({name: '10', value: 10})
298
+ */
299
+ perPage: {
300
+ type: Object,
301
+ default: () => ({name: '10', value: 10})
317
302
  },
318
- watch: {
319
- page: function (n, o) {
320
- if (n > 0) {
321
- this.refresh()
322
- }
323
- },
324
- itemsPerPage: function () {
325
- this.refresh()
303
+ /**
304
+ * Additional query parameters for API requests
305
+ * @type {Object}
306
+ */
307
+ query: Object,
308
+ /**
309
+ * Enables responsive table behavior
310
+ * @type {Boolean}
311
+ */
312
+ responsive: Boolean,
313
+ /**
314
+ * Displays data in cards view instead of table
315
+ * @type {Boolean}
316
+ */
317
+ card: Boolean,
318
+ /**
319
+ * Disables "Add New" functionality
320
+ * @type {Boolean}
321
+ */
322
+ disableAdd: Boolean,
323
+ /**
324
+ * Enables advanced search functionality
325
+ * @type {Boolean}
326
+ * @default true
327
+ */
328
+ advanceSearch: {type: Boolean, default: true},
329
+ /**
330
+ * Disables delete functionality
331
+ * @type {Boolean}
332
+ */
333
+ disableDelete: Boolean,
334
+ /**
335
+ * Disables update/edit functionality
336
+ * @type {Boolean}
337
+ */
338
+ disableUpdate: Boolean,
339
+ /**
340
+ * Mass CRUD operations endpoint URL
341
+ * @type {String}
342
+ */
343
+ mcud: String,
344
+ /**
345
+ * Property name that serves as unique item identifier
346
+ * @type {String}
347
+ * @default '_id'
348
+ */
349
+ itemId: {type: String, default: '_id'},
350
+ /**
351
+ * Additional HTTP headers for API requests
352
+ * @type {Object}
353
+ */
354
+ headers: Object
355
+ })
356
+
357
+ const {$helper, $t} = inject('renusify')
358
+ const $axios = inject('axios')
359
+
360
+ const time_out_id = ref(null)
361
+ const loading = ref(false)
362
+ const showForm = ref(false)
363
+ const showCopy = ref(false)
364
+ const showConfirm = ref(false)
365
+ const search = ref('')
366
+ const a_search = ref({})
367
+ const copyItem = ref({d: {}, c: 1})
368
+ const editedItem = ref({})
369
+ const deleted = ref('')
370
+ const url = ref('')
371
+ const method = ref('post')
372
+ const title = ref('')
373
+ const autoSend = ref(false)
374
+ const itemsPerPage = ref(props.perPage)
375
+ const page = ref(1)
376
+ const sortBy = ref(null)
377
+ const sortType = ref(0)
378
+ const table = ref({
379
+ headers: [],
380
+ option: {},
381
+ data: [],
382
+ startTime: false,
383
+ total: 0
384
+ })
385
+ const check_all = ref(false)
386
+ const checked = ref({})
387
+
388
+
389
+ const showMCUD = computed(() => {
390
+ if (check_all.value) return true
391
+
392
+ for (let k in checked.value) {
393
+ if (checked.value[k] === true) {
394
+ return true
326
395
  }
327
- },
328
- computed: {
329
- showMCUD() {
330
- if (this.check_all) {
331
- return true
332
- }
333
- for (let k in this.checked) {
334
- if (this.checked[k] === true) {
335
- return true
336
- }
337
- }
338
- return false
339
- },
340
- headerTable() {
341
- const headers = this.table.headers
342
- const res = []
343
- const lng = headers.length
344
- for (let i = 0; i < lng; i++) {
345
- if (this.$helper.ifHas(headers[i], true, 'option', 'tableShow') !== false) {
346
- res.push(headers[i])
347
- }
348
- }
349
- return res
396
+ }
397
+ return false
398
+ })
399
+
400
+ const headerTable = computed(() => {
401
+ const headers = table.value.headers
402
+ const res = []
403
+ const lng = headers.length
404
+
405
+ for (let i = 0; i < lng; i++) {
406
+ if ($helper.ifHas(headers[i], true, 'option', 'tableShow') !== false) {
407
+ res.push(headers[i])
350
408
  }
351
- },
352
- methods: {
353
- getAttr(data, typ) {
354
- let res = {}
355
- let ls = ['formInput', 'sortable', 'type', 'tableShow', 'priority']
356
- if (typ === 'edit') {
357
- ls.push('rules')
358
- }
359
- for (let i in data) {
360
- if (this.$helper.hasKey(data, i) &&
361
- !ls.includes(i)) {
362
- res[i] = data[i]
363
- }
364
- }
365
- return res
366
- },
367
- copyAll() {
368
- this.loading = true
369
- this.$axios[this.copyItem['t'] === 'copy' ? 'post' : 'put'](this.mcud, this.copyItem, {headers: this.headers}).then(() => {
370
- this.loading = false
371
- this.showCopy = false
372
- this.refresh()
373
- }, () => {
374
- this.loading = false
409
+ }
410
+ return res
411
+ })
412
+
413
+
414
+ const getAttr = (data, typ) => {
415
+ const res = {}
416
+ const ls = ['formInput', 'sortable', 'type', 'tableShow', 'priority']
417
+
418
+ if (typ === 'edit') {
419
+ ls.push('rules')
420
+ }
421
+
422
+ for (let i in data) {
423
+ if ($helper.hasKey(data, i) && !ls.includes(i)) {
424
+ res[i] = data[i]
425
+ }
426
+ }
427
+ return res
428
+ }
429
+
430
+ const copyAll = () => {
431
+ loading.value = true
432
+
433
+ const axiosMethod = copyItem.value.t === 'copy' ? 'post' : 'put'
434
+ $axios[axiosMethod](props.mcud, copyItem.value, {headers: props.headers})
435
+ .then(() => {
436
+ loading.value = false
437
+ showCopy.value = false
438
+ refresh()
375
439
  })
376
- },
377
- copyHandle(typ = 'copy') {
378
- this.copyItem = {'d': {}, 'c': 1, 't': typ}
379
- if (typ === 'edit') {
380
- this.copyItem['ids'] = this.get_ids()
381
- if (this.copyItem['ids'].length === 0) {
382
- return
383
- }
384
- }
385
- if (typ === 'copy') {
386
- if (this.check_all) {
387
- this.copyItem['d'] = this.table.data[0]
388
- this.copyItem['c'] = 1
389
- }
390
- for (let k in this.checked) {
391
- if (this.checked[k] === true) {
392
- this.table.data.forEach((item) => {
393
- if (item[this.itemId] === k) {
394
- this.copyItem['d'] = item
395
- this.copyItem['c'] = 1
396
- }
397
- })
398
- break
440
+ .catch(() => {
441
+ loading.value = false
442
+ })
443
+ }
444
+
445
+ const copyHandle = (typ = 'copy') => {
446
+ copyItem.value = {d: {}, c: 1, t: typ}
447
+
448
+ if (typ === 'edit') {
449
+ copyItem.value.ids = get_ids()
450
+ if (copyItem.value.ids.length === 0) {
451
+ return
452
+ }
453
+ }
454
+
455
+ if (typ === 'copy') {
456
+ if (check_all.value) {
457
+ copyItem.value.d = table.value.data[0]
458
+ copyItem.value.c = 1
459
+ }
460
+
461
+ for (let k in checked.value) {
462
+ if (checked.value[k] === true) {
463
+ table.value.data.forEach((item) => {
464
+ if (item[props.itemId] === k) {
465
+ copyItem.value.d = item
466
+ copyItem.value.c = 1
399
467
  }
400
- }
401
- }
402
- this.showCopy = true
403
- },
404
- get_ids() {
405
- let res = []
406
- if (this.check_all) {
407
- this.table.data.forEach((item) => {
408
- res.push(item[this.itemId])
409
468
  })
469
+ break
410
470
  }
411
- for (let k in this.checked) {
412
- if (this.checked[k] === true) {
413
- res.push(k)
414
- }
415
- }
416
- return res
417
- },
418
- deleteAll() {
419
- let res = this.get_ids()
420
- if (res.length > 0) {
421
- this.$axios.delete(this.mcud, {
422
- data: {
423
- ids: res
424
- },
425
- headers: this.headers
471
+ }
472
+ }
473
+
474
+ showCopy.value = true
475
+ }
476
+
477
+ const get_ids = () => {
478
+ const res = []
479
+
480
+ if (check_all.value) {
481
+ table.value.data.forEach((item) => {
482
+ res.push(item[props.itemId])
483
+ })
484
+ }
485
+
486
+ for (let k in checked.value) {
487
+ if (checked.value[k] === true) {
488
+ res.push(k)
489
+ }
490
+ }
491
+ return res
492
+ }
493
+
494
+ const deleteAll = () => {
495
+ const res = get_ids()
496
+ if (res.length > 0) {
497
+ $axios.delete(props.mcud, {
498
+ data: {ids: res},
499
+ headers: props.headers
500
+ })
501
+ .then(() => {
502
+ refresh()
426
503
  })
427
- .then(() => {
428
- this.refresh()
429
- })
430
- }
431
- },
432
- sortSetup(item) {
433
- if (!this.$helper.ifHas(item, true, 'option', 'sortable')) {
434
- return
435
- }
436
- if (this.sortBy !== item.value) {
437
- this.sortType = 0
438
- }
439
- this.sortBy = item.value
440
- if (this.sortType >= 2) {
441
- this.sortType = 0
504
+ .catch(error => {
505
+ console.error('Delete error:', error)
506
+ })
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Sets up sorting for a column
512
+ * @param {Object} item - Header configuration object
513
+ */
514
+ const sortSetup = (item) => {
515
+ if (!$helper.ifHas(item, true, 'option', 'sortable')) {
516
+ return
517
+ }
518
+
519
+ if (sortBy.value !== item.value) {
520
+ sortType.value = 0
521
+ }
522
+
523
+ sortBy.value = item.value
524
+
525
+ if (sortType.value >= 2) {
526
+ sortType.value = 0
527
+ } else {
528
+ sortType.value += 1
529
+ }
530
+
531
+ refresh()
532
+ }
533
+
534
+ /**
535
+ * Handles successful form submission
536
+ */
537
+ const ok = () => {
538
+ table.value.startTime = false
539
+ page.value = 1
540
+ sortBy.value = null
541
+ sortType.value = 0
542
+ autoSend.value = false
543
+ showForm.value = false
544
+ refresh()
545
+ }
546
+
547
+ /**
548
+ * Refreshes table data
549
+ * @param {Object} e - Event object with perPage value
550
+ */
551
+ const refresh = (e) => {
552
+ loading.value = true
553
+ check_all.value = false
554
+ checked.value = {}
555
+
556
+ const perPage = e !== undefined ? e.value : itemsPerPage.value.value
557
+ const params = {...props.query}
558
+
559
+ params.page = page.value
560
+ params.per_page = perPage
561
+
562
+ if (table.value.startTime !== false) {
563
+ params.t = table.value.startTime
564
+ }
565
+
566
+ if (sortType.value !== 0) {
567
+ const sort = (sortType.value === 2) ? 'desc' : 'asc'
568
+ params[sort] = sortBy.value
569
+ }
570
+
571
+ if (search.value.length >= 1) {
572
+ params.search = search.value
573
+ }
574
+
575
+ if (Object.keys(a_search.value).length > 0) {
576
+ params.a_search = JSON.stringify(a_search.value)
577
+ }
578
+
579
+ setup(props.link, params)
580
+ }
581
+
582
+ /**
583
+ * Performs search with debouncing
584
+ */
585
+ const searching = () => {
586
+ clearTimeout(time_out_id.value)
587
+ loading.value = true
588
+
589
+ time_out_id.value = setTimeout(() => {
590
+ page.value = 1
591
+ refresh()
592
+ }, 1000)
593
+ }
594
+
595
+ /**
596
+ * Opens form for creating a new item
597
+ */
598
+ const newItem = () => {
599
+ title.value = $t('new', 'renusify')
600
+ url.value = props.link
601
+
602
+ const items = {}
603
+ table.value.headers.forEach((item) => {
604
+ if (item.option.formInput !== false) {
605
+ if (item.option.type === 'boolean') {
606
+ items[item.value] = false
442
607
  } else {
443
- this.sortType += 1
444
- }
445
- this.refresh()
446
- },
447
- ok() {
448
- this.table.startTime = false
449
- this.page = 1;
450
- this.sortBy = null;
451
- this.sortType = 0;
452
- this.autoSend = false
453
- this.showForm = false
454
- this.refresh()
455
- },
456
- refresh(e) {
457
- this.loading = true
458
- this.check_all = false
459
- this.checked = {}
460
- const perPage = e !== undefined ? e.value : this.itemsPerPage.value
461
- let params = this.query || {}
462
- params.page = this.page
463
- if (this.table.startTime !== false) {
464
- params.t = this.table.startTime
465
- }
466
- if (this.sortType !== 0) {
467
- const sort = (this.sortType === 2) ? 'desc' : 'asc'
468
- params[sort] = this.sortBy
469
- }
470
- params.per_page = perPage
471
- if (this.search.length >= 1) {
472
- params.search = this.search
608
+ items[item.value] = null
473
609
  }
474
- if (this.$helper.size(this.a_search) > 0) {
475
- params.a_search = JSON.stringify(this.a_search)
610
+ }
611
+ })
612
+
613
+ editedItem.value = items
614
+ method.value = 'post'
615
+ autoSend.value = false
616
+ showForm.value = true
617
+ }
618
+
619
+ /**
620
+ * Opens form for editing an existing item
621
+ * @param {Object} item - Item to edit
622
+ * @param {Boolean} autoSendFlag - Whether to auto-save changes
623
+ * @param {String|null} key - Specific field key to edit
624
+ */
625
+ const editItem = (item, autoSendFlag = false, key = null) => {
626
+ let sw
627
+ title.value = $t('edit', 'renusify')
628
+ url.value = `${props.link}/${item[props.itemId]}`
629
+
630
+ if (key) {
631
+ sw = !item[key]
632
+ }
633
+
634
+ const items = {}
635
+ table.value.headers.forEach((header) => {
636
+ if (header.option.formInput !== false) {
637
+ if (header.option.type === 'boolean') {
638
+ items[header.value] = item[header.value] !== undefined ? item[header.value] : false
639
+ } else {
640
+ items[header.value] = item[header.value] !== undefined ? item[header.value] : null
476
641
  }
642
+ }
643
+ })
477
644
 
478
- this.setup(this.link, params)
479
- },
480
- searching() {
481
- clearTimeout(this.time_out_id)
482
- this.loading = true
483
- this.time_out_id = setTimeout(() => {
484
- this.page = 1
485
- this.refresh()
486
- }, 1000)
487
-
488
- },
489
- newItem() {
490
- this.title = this.$t('new', 'renusify')
491
- this.url = this.link
492
- const items = {}
493
- this.table.headers.map((item) => {
494
- if (item.option.formInput !== false) {
495
- if (item.option.type === 'boolean') {
496
- items[item.value] = false
497
- } else {
498
- items[item.value] = null
499
- }
500
- }
645
+ editedItem.value = {...items}
646
+
647
+ if (key) {
648
+ editedItem.value[key] = sw
649
+ }
650
+
651
+ method.value = 'put'
652
+ autoSend.value = autoSendFlag
653
+ showForm.value = true
654
+ }
655
+
656
+ /**
657
+ * Opens confirmation dialog for deleting an item
658
+ * @param {Object} item - Item to delete
659
+ */
660
+ const deleteItem = (item) => {
661
+ showConfirm.value = true
662
+ deleted.value = item[props.itemId]
663
+ }
664
+
665
+ /**
666
+ * Deletes an item by ID
667
+ * @param {String} _id - Item ID to delete
668
+ */
669
+ const deleteItemById = (_id) => {
670
+ $axios.delete(`${props.link}/${_id}`, {headers: props.headers})
671
+ .then(() => {
672
+ refresh()
501
673
  })
502
- this.editedItem = items
503
- this.method = 'post'
504
- this.autoSend = false
505
- this.showForm = true
506
- },
507
- editItem(item, autoSend = false, key = null) {
508
- let sw
509
- this.title = this.$t('edit', 'renusify')
510
- this.url = this.link + '/' + item[this.itemId]
511
- if (key) {
512
- sw = !item[key]
513
- }
514
- const items = {}
515
- this.table.headers.map((header) => {
516
- if (header.option.formInput !== false) {
517
- if (header.option.type === 'boolean') {
518
- items[header.value] = item[header.value] !== undefined ? item[header.value] : false
519
- } else {
520
- items[header.value] = item[header.value] !== undefined ? item[header.value] : null
521
- }
522
- }
674
+ .catch(error => {
675
+ console.error('Delete item error:', error)
523
676
  })
524
- this.editedItem = Object.assign({}, items)
677
+ }
525
678
 
526
- if (key) {
527
- this.editedItem[key] = sw
528
- }
529
- this.method = 'put'
530
- this.autoSend = autoSend
531
- this.showForm = true
532
- },
533
- deleteItem(item) {
534
- this.showConfirm = true
535
- this.deleted = item[this.itemId]
536
- },
537
- delete(_id) {
538
- this.$axios.delete(this.link + '/' + _id, {headers: this.headers})
539
- .then((res) => {
540
- this.refresh()
541
- })
542
- },
543
- accept() {
544
- this.showConfirm = false
545
- this.delete(this.deleted)
546
- this.deleted = ''
547
- this.refresh()
548
- },
549
- setup(url, params = null) {
550
- this.loading = true
551
-
552
- this.$axios.get(url, {params: params, headers: this.headers}).then(({data}) => {
679
+ /**
680
+ * Handles confirmation dialog acceptance
681
+ */
682
+ const accept = () => {
683
+ showConfirm.value = false
684
+ deleteItemById(deleted.value)
685
+ deleted.value = ''
686
+ refresh()
687
+ }
688
+
689
+ /**
690
+ * Fetches table data from API
691
+ * @param {String} url - API endpoint URL
692
+ * @param {Object|null} params - Query parameters
693
+ */
694
+ const setup = (url, params = null) => {
695
+ loading.value = true
696
+
697
+ $axios.get(url, {params: params, headers: props.headers})
698
+ .then(({data}) => {
553
699
  let all = []
554
- if (this.mcud) {
700
+
701
+ if (props.mcud) {
555
702
  all.push({
556
703
  option: {type: 'mcud', sortable: false, formInput: false, priority: 10},
557
704
  text: 'action',
558
705
  value: 'action'
559
706
  })
560
707
  }
708
+
561
709
  all = all.concat(data.headers)
562
710
 
563
- this.table.headers = all.map((item) => {
564
- this.table.option[item.value] = item.option
565
- item.text = this.$t(item.value)
711
+ table.value.headers = all.map((item) => {
712
+ table.value.option[item.value] = item.option
713
+ item.text = $t(item.value)
566
714
  return item
567
715
  })
568
- this.table.data = data.data
569
- this.table.total = data.total
570
- this.table.startTime = this.$helper.ifHas(data, false, 't')
571
- this.loading = false
716
+
717
+ table.value.data = data.data
718
+ table.value.total = data.total
719
+ table.value.startTime = $helper.ifHas(data, false, 't')
720
+ loading.value = false
721
+ })
722
+ .catch(error => {
723
+ console.error('Setup error:', error)
724
+ loading.value = false
572
725
  })
726
+ }
727
+
728
+ onMounted(() => {
729
+ refresh()
730
+ })
731
+
732
+ watch(page, (newValue) => {
733
+ if (newValue > 0) {
734
+ refresh()
735
+ }
736
+ })
737
+
738
+ watch(itemsPerPage, () => {
739
+ refresh()
740
+ })
741
+ </script>
742
+ <style lang="scss">
743
+ @use "../../../style" as *;
744
+
745
+
746
+ .#{$prefix}table-manage {
747
+ white-space: nowrap;
748
+
749
+ .overflow-x-auto {
750
+ overflow-x: auto;
751
+ }
752
+
753
+ .table-checkbox {
754
+ width: 17px;
755
+ height: 17px;
756
+ }
757
+
758
+ .manage-footer {
759
+ .btn-page {
760
+ color: var(--color-on-sheet);
761
+ border: solid 1px var(--color-sheet-low);
762
+ background-color: var(--color-sheet-container);
763
+ }
764
+ }
765
+
766
+ th {
767
+ position: relative;
768
+ transition: 0.2s;
769
+
770
+ .icon-hidden {
771
+ visibility: hidden;
772
+ }
773
+
774
+ &.header-sortable:hover {
775
+ cursor: pointer;
776
+
777
+ .icon-hidden {
778
+ visibility: visible;
779
+ }
780
+ }
781
+ }
782
+
783
+ td {
784
+ max-width: 130px !important;
785
+ overflow: auto;
786
+ }
787
+
788
+ .manage-footer {
789
+ .btn-page {
790
+ border-radius: 30px;
791
+ padding: 2px;
792
+ }
793
+
794
+ .per-page {
795
+ width: 80px;
796
+ }
797
+
798
+ .input-page {
799
+ outline: none;
800
+ width: 20px;
801
+
573
802
  }
574
803
  }
575
804
  }
576
- </script>
805
+
806
+ </style>