@netang/quasar 0.2.19 → 0.2.21

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 (284) hide show
  1. package/.editorconfig +12 -12
  2. package/_docs/docs/components/field-table.md +58 -58
  3. package/_docs/docs/components/field-tree.md +21 -21
  4. package/_docs/docs/components/table.md +24 -24
  5. package/_docs/docs/utils/table.md +196 -196
  6. package/components/column-title/index.vue +38 -38
  7. package/components/data/index.vue +20 -20
  8. package/components/dialog/img-viewer/index.vue +697 -697
  9. package/components/dragger/index.vue +203 -203
  10. package/components/drawer/index.vue +303 -303
  11. package/components/editor-code/index.vue +325 -325
  12. package/components/empty/index.vue +82 -82
  13. package/components/field-date/index.vue +850 -850
  14. package/components/field-date/methods.js +100 -100
  15. package/components/field-table/index.vue +1482 -1482
  16. package/components/field-text/index.vue +165 -165
  17. package/components/field-tree/index.vue +754 -754
  18. package/components/img/index.vue +279 -279
  19. package/components/list-menu/index.vue +149 -149
  20. package/components/list-menu-item/index.vue +79 -79
  21. package/components/mixed-table/index.vue +532 -532
  22. package/components/mixed-table-splitter/index.vue +377 -377
  23. package/components/power-page/index.vue +96 -96
  24. package/components/price/index.vue +188 -188
  25. package/components/private/components/index.js +11 -11
  26. package/components/private/components/move-to-tree/index.vue +154 -154
  27. package/components/private/edit-power-data/index.vue +846 -846
  28. package/components/private/table-visible-columns-button/index.vue +114 -114
  29. package/components/render/index.vue +123 -123
  30. package/components/search/index.vue +231 -231
  31. package/components/search-item/index.vue +210 -210
  32. package/components/select/index.vue +177 -177
  33. package/components/splitter/index.vue +422 -422
  34. package/components/table/index.vue +513 -513
  35. package/components/table-column-fixed/index.vue +110 -110
  36. package/components/table-pagination/index.vue +192 -192
  37. package/components/table-summary/index.vue +107 -107
  38. package/components/thumbnail/index.vue +72 -72
  39. package/components/toolbar/index.vue +146 -146
  40. package/components/tree/index.vue +1728 -1728
  41. package/components/uploader/index.vue +195 -195
  42. package/components/uploader-query/index.vue +945 -945
  43. package/components/value-format/index.vue +274 -274
  44. package/configs/area3.js +1 -1
  45. package/docs/404.html +33 -33
  46. package/docs/assets/404.html-60b35caa.js +1 -1
  47. package/docs/assets/404.html-d1e63d77.js +1 -1
  48. package/docs/assets/alert.html-b2a2a72f.js +5 -5
  49. package/docs/assets/alert.html-ba46d137.js +1 -1
  50. package/docs/assets/app-9f30aa4b.js +6 -6
  51. package/docs/assets/area.html-01b9b58d.js +42 -42
  52. package/docs/assets/area.html-9a4fce6a.js +1 -1
  53. package/docs/assets/arr.html-145d27e7.js +1 -1
  54. package/docs/assets/arr.html-674e65ab.js +11 -11
  55. package/docs/assets/auth.html-579fa830.js +1 -1
  56. package/docs/assets/auth.html-8544ed95.js +8 -8
  57. package/docs/assets/bus.html-c71254aa.js +1 -1
  58. package/docs/assets/bus.html-dc7d3d19.js +6 -6
  59. package/docs/assets/column-title.html-c735cb5a.js +3 -3
  60. package/docs/assets/column-title.html-e9316762.js +1 -1
  61. package/docs/assets/confirm.html-ddfdc27f.js +10 -10
  62. package/docs/assets/confirm.html-ef3e2bef.js +1 -1
  63. package/docs/assets/copy.html-d20345b6.js +1 -1
  64. package/docs/assets/copy.html-ef8c8571.js +13 -13
  65. package/docs/assets/data.html-6432175d.js +30 -30
  66. package/docs/assets/data.html-a3b05d5b.js +1 -1
  67. package/docs/assets/dialog.html-1f698e5a.js +1 -1
  68. package/docs/assets/dialog.html-62902b83.js +68 -68
  69. package/docs/assets/dialog.html-baea77c9.js +1 -1
  70. package/docs/assets/dialog.html-bb082fc4.js +1 -1
  71. package/docs/assets/dict.html-1311da3d.js +23 -23
  72. package/docs/assets/dict.html-b96fbf0c.js +1 -1
  73. package/docs/assets/dictOptions.html-7c4f40a5.js +1 -1
  74. package/docs/assets/dictOptions.html-fb99d175.js +5 -5
  75. package/docs/assets/dragger.html-668d3efa.js +1 -1
  76. package/docs/assets/dragger.html-749d585a.js +1 -1
  77. package/docs/assets/editor-code.html-6ab26ea9.js +1 -1
  78. package/docs/assets/editor-code.html-d196205d.js +1 -1
  79. package/docs/assets/empty.html-1c139131.js +1 -1
  80. package/docs/assets/empty.html-1e9c441d.js +1 -1
  81. package/docs/assets/field-date.html-069fdb13.js +1 -1
  82. package/docs/assets/field-date.html-ad204aa9.js +1 -1
  83. package/docs/assets/field-table.html-ce480f03.js +1 -1
  84. package/docs/assets/field-table.html-d9236160.js +1 -1
  85. package/docs/assets/field-text.html-7277c62f.js +1 -1
  86. package/docs/assets/field-text.html-ccb4cecf.js +1 -1
  87. package/docs/assets/field-tree.html-519bfb45.js +1 -1
  88. package/docs/assets/field-tree.html-fdc748d6.js +1 -1
  89. package/docs/assets/form.html-2b562c37.js +2 -2
  90. package/docs/assets/form.html-75104cd5.js +1 -1
  91. package/docs/assets/framework-204010b2.js +5 -5
  92. package/docs/assets/getData.html-990e3787.js +1 -1
  93. package/docs/assets/getData.html-bb72025f.js +34 -34
  94. package/docs/assets/getFile.html-42368004.js +1 -1
  95. package/docs/assets/getFile.html-99abd054.js +3 -3
  96. package/docs/assets/getImage.html-3429c5a1.js +1 -1
  97. package/docs/assets/getImage.html-4d886d83.js +3 -3
  98. package/docs/assets/getTime.html-7435f922.js +1 -1
  99. package/docs/assets/getTime.html-b37f49eb.js +20 -20
  100. package/docs/assets/img.html-7d1da657.js +1 -1
  101. package/docs/assets/img.html-fbea1105.js +1 -1
  102. package/docs/assets/index.html-1695dd7c.js +1 -1
  103. package/docs/assets/index.html-65a4aa67.js +1 -1
  104. package/docs/assets/index.html-7b98d5bd.js +1 -1
  105. package/docs/assets/index.html-c01f2648.js +1 -1
  106. package/docs/assets/input-number.html-0b250d2a.js +1 -1
  107. package/docs/assets/input-number.html-a8eb0378.js +1 -1
  108. package/docs/assets/list-menu-item.html-7f1b4611.js +1 -1
  109. package/docs/assets/list-menu-item.html-84ed5ab8.js +1 -1
  110. package/docs/assets/list-menu.html-28b4163f.js +1 -1
  111. package/docs/assets/list-menu.html-cb6ba95b.js +1 -1
  112. package/docs/assets/loading.html-dae9e39d.js +6 -6
  113. package/docs/assets/loading.html-dc74c9e6.js +1 -1
  114. package/docs/assets/notify.html-e6c4c514.js +1 -1
  115. package/docs/assets/notify.html-f2c4d914.js +8 -8
  116. package/docs/assets/power-page.html-32e02f82.js +1 -1
  117. package/docs/assets/power-page.html-485e77da.js +1 -1
  118. package/docs/assets/power.html-d258cc19.js +93 -93
  119. package/docs/assets/power.html-e490bd32.js +1 -1
  120. package/docs/assets/previewImage.html-6a6b4245.js +1 -1
  121. package/docs/assets/previewImage.html-c5b7e945.js +2 -2
  122. package/docs/assets/price.html-1882c548.js +19 -19
  123. package/docs/assets/price.html-94d3f5be.js +1 -1
  124. package/docs/assets/price.html-d213df0f.js +1 -1
  125. package/docs/assets/price.html-deaf880f.js +1 -1
  126. package/docs/assets/render.html-8efcbdd4.js +1 -1
  127. package/docs/assets/render.html-df228e38.js +1 -1
  128. package/docs/assets/rule.html-2cd57fc2.js +13 -13
  129. package/docs/assets/rule.html-61662001.js +1 -1
  130. package/docs/assets/ruleValid.html-04fe2552.js +1 -1
  131. package/docs/assets/ruleValid.html-e0a776af.js +14 -14
  132. package/docs/assets/search-0782d0d1.svg +1 -1
  133. package/docs/assets/search-item.html-3f75394c.js +1 -1
  134. package/docs/assets/search-item.html-4e942ecd.js +1 -1
  135. package/docs/assets/search.html-2807043e.js +1 -1
  136. package/docs/assets/search.html-c24f8806.js +1 -1
  137. package/docs/assets/select.html-00d0607c.js +1 -1
  138. package/docs/assets/select.html-de7731f5.js +1 -1
  139. package/docs/assets/splitter.html-56f51a70.js +1 -1
  140. package/docs/assets/splitter.html-f5c836d7.js +1 -1
  141. package/docs/assets/style-161e43ab.css +1 -1
  142. package/docs/assets/symbols.html-a6aea4bf.js +1 -1
  143. package/docs/assets/symbols.html-b1f65bad.js +21 -21
  144. package/docs/assets/table-column-fixed.html-3a69e7b2.js +1 -1
  145. package/docs/assets/table-column-fixed.html-e763c38b.js +1 -1
  146. package/docs/assets/table-pagination.html-236934d3.js +1 -1
  147. package/docs/assets/table-pagination.html-c37ee2ac.js +1 -1
  148. package/docs/assets/table-splitter.html-07eab15c.js +1 -1
  149. package/docs/assets/table-splitter.html-7670ee65.js +1 -1
  150. package/docs/assets/table-summary.html-04db434f.js +1 -1
  151. package/docs/assets/table-summary.html-943c65a0.js +1 -1
  152. package/docs/assets/table.html-36253ad7.js +1 -1
  153. package/docs/assets/table.html-7f9c5d1b.js +38 -38
  154. package/docs/assets/table.html-93d53dc8.js +1 -1
  155. package/docs/assets/table.html-ac99b9cb.js +1 -1
  156. package/docs/assets/thumbnail.html-bab1976b.js +1 -1
  157. package/docs/assets/thumbnail.html-eb64e5e8.js +1 -1
  158. package/docs/assets/timestamp.html-4e54f79b.js +13 -13
  159. package/docs/assets/timestamp.html-d0e1b88a.js +1 -1
  160. package/docs/assets/toast.html-58ecbe21.js +1 -1
  161. package/docs/assets/toast.html-c9b9d36b.js +6 -6
  162. package/docs/assets/toolbar.html-83d9f97c.js +1 -1
  163. package/docs/assets/toolbar.html-ff7b8c92.js +1 -1
  164. package/docs/assets/tree.html-d07cbe79.js +23 -23
  165. package/docs/assets/tree.html-ea04193e.js +1 -1
  166. package/docs/assets/uploader-query.html-05590718.js +1 -1
  167. package/docs/assets/uploader-query.html-3175bac5.js +1 -1
  168. package/docs/assets/uploader.html-36da4394.js +2 -2
  169. package/docs/assets/uploader.html-6b5f3079.js +1 -1
  170. package/docs/assets/uploader.html-b9340b57.js +1 -1
  171. package/docs/assets/uploader.html-bc1c22e3.js +1 -1
  172. package/docs/assets/value-format.html-8ae3d47d.js +1 -1
  173. package/docs/assets/value-format.html-afa99b3d.js +1 -1
  174. package/docs/components/column-title.html +35 -35
  175. package/docs/components/data.html +62 -62
  176. package/docs/components/dialog.html +33 -33
  177. package/docs/components/dragger.html +33 -33
  178. package/docs/components/editor-code.html +33 -33
  179. package/docs/components/empty.html +33 -33
  180. package/docs/components/field-date.html +33 -33
  181. package/docs/components/field-table.html +33 -33
  182. package/docs/components/field-text.html +33 -33
  183. package/docs/components/field-tree.html +33 -33
  184. package/docs/components/img.html +33 -33
  185. package/docs/components/input-number.html +33 -33
  186. package/docs/components/list-menu-item.html +33 -33
  187. package/docs/components/list-menu.html +33 -33
  188. package/docs/components/power-page.html +33 -33
  189. package/docs/components/price.html +33 -33
  190. package/docs/components/render.html +33 -33
  191. package/docs/components/search-item.html +33 -33
  192. package/docs/components/search.html +33 -33
  193. package/docs/components/select.html +33 -33
  194. package/docs/components/splitter.html +33 -33
  195. package/docs/components/table-column-fixed.html +33 -33
  196. package/docs/components/table-pagination.html +33 -33
  197. package/docs/components/table-splitter.html +33 -33
  198. package/docs/components/table-summary.html +33 -33
  199. package/docs/components/table.html +33 -33
  200. package/docs/components/thumbnail.html +33 -33
  201. package/docs/components/toolbar.html +33 -33
  202. package/docs/components/uploader-query.html +33 -33
  203. package/docs/components/uploader.html +33 -33
  204. package/docs/components/value-format.html +33 -33
  205. package/docs/css/index.css +3 -3
  206. package/docs/index.html +33 -33
  207. package/docs/utils/alert.html +37 -37
  208. package/docs/utils/area.html +74 -74
  209. package/docs/utils/arr.html +43 -43
  210. package/docs/utils/auth.html +40 -40
  211. package/docs/utils/bus.html +38 -38
  212. package/docs/utils/confirm.html +42 -42
  213. package/docs/utils/copy.html +45 -45
  214. package/docs/utils/dialog.html +100 -100
  215. package/docs/utils/dict.html +55 -55
  216. package/docs/utils/dictOptions.html +37 -37
  217. package/docs/utils/form.html +34 -34
  218. package/docs/utils/getData.html +66 -66
  219. package/docs/utils/getFile.html +35 -35
  220. package/docs/utils/getImage.html +35 -35
  221. package/docs/utils/getTime.html +52 -52
  222. package/docs/utils/index.html +33 -33
  223. package/docs/utils/loading.html +38 -38
  224. package/docs/utils/notify.html +40 -40
  225. package/docs/utils/power.html +125 -125
  226. package/docs/utils/previewImage.html +34 -34
  227. package/docs/utils/price.html +51 -51
  228. package/docs/utils/rule.html +45 -45
  229. package/docs/utils/ruleValid.html +46 -46
  230. package/docs/utils/symbols.html +53 -53
  231. package/docs/utils/table.html +70 -70
  232. package/docs/utils/timestamp.html +45 -45
  233. package/docs/utils/toast.html +38 -38
  234. package/docs/utils/tree.html +55 -55
  235. package/docs/utils/uploader.html +34 -34
  236. package/package.json +25 -25
  237. package/sass/common.scss +184 -184
  238. package/sass/index.scss +12 -12
  239. package/sass/line.scss +39 -39
  240. package/sass/quasar/btn.scss +46 -46
  241. package/sass/quasar/common.scss +3 -3
  242. package/sass/quasar/drawer.scss +6 -6
  243. package/sass/quasar/field.scss +259 -259
  244. package/sass/quasar/loading.scss +6 -6
  245. package/sass/quasar/table.scss +168 -168
  246. package/sass/quasar/toolbar.scss +22 -22
  247. package/sass/variables.scss +140 -140
  248. package/store/index.js +29 -29
  249. package/utils/$auth.js +127 -127
  250. package/utils/$form.js +72 -72
  251. package/utils/$power.js +1486 -1486
  252. package/utils/$render.js +75 -75
  253. package/utils/$rule.js +13 -13
  254. package/utils/$ruleValid.js +10 -10
  255. package/utils/$search.js +416 -416
  256. package/utils/$table.js +1275 -1275
  257. package/utils/alert.js +12 -12
  258. package/utils/area.js +400 -400
  259. package/utils/arr.js +51 -51
  260. package/utils/bus.js +6 -6
  261. package/utils/config.js +64 -62
  262. package/utils/confirm.js +11 -11
  263. package/utils/copy.js +30 -30
  264. package/utils/dialog.js +36 -36
  265. package/utils/dict.js +21 -21
  266. package/utils/dictOptions.js +28 -28
  267. package/utils/getData.js +88 -88
  268. package/utils/getFile.js +67 -67
  269. package/utils/getImage.js +276 -236
  270. package/utils/getTime.js +113 -113
  271. package/utils/index.js +67 -67
  272. package/utils/loading.js +15 -15
  273. package/utils/notify.js +13 -13
  274. package/utils/play.js +40 -40
  275. package/utils/previewImage.js +14 -14
  276. package/utils/price.js +18 -18
  277. package/utils/symbols.js +18 -18
  278. package/utils/timestamp.js +18 -18
  279. package/utils/toast.js +13 -13
  280. package/utils/uploader.js +2114 -2099
  281. package/utils/useAuth.js +30 -30
  282. package/utils/useFileUrl.js +26 -26
  283. package/utils/useRouter.js +47 -47
  284. package/utils/useSearch.js +499 -499
@@ -1,1482 +1,1482 @@
1
- <template>
2
-
3
- <!-- 如果有默认插槽 -->
4
- <template v-if="$slots.default">
5
- <slot
6
- :showValue="showValue"
7
- :selected="selected"
8
- :onRemove="onRemoveSelected"
9
- :onShowDialog="onShowDialog"
10
- :onClear="onFieldClear"
11
- />
12
- </template>
13
-
14
- <!--:class="fieldFocused ? 'q-field&#45;&#45;float q-field&#45;&#45;focused q-field&#45;&#45;highlighted' : ''"-->
15
- <!--:clearable="clearable && (! multiple || collapseTags)"-->
16
- <q-field
17
- class="n-field-table"
18
- :model-value="showValue"
19
- :disable="disable"
20
- :readonly="readonly"
21
- :clearable="clearable"
22
- @focus="onFieldFocus"
23
- @blur="onFieldBlur"
24
- @clear="onFieldClear"
25
- v-bind="$attrs"
26
- v-else
27
- >
28
- <template v-slot:control>
29
-
30
- <template v-if="multiple">
31
- <template v-if="selected.length">
32
-
33
- <!-- 多选插槽 -->
34
- <slot
35
- name="selected"
36
- :selected="selected"
37
- :remove="onRemoveSelected"
38
- v-if="$slots.selected"
39
- />
40
-
41
- <!-- 显示折叠的值数量 -->
42
- <q-chip
43
- dense
44
- :label="`+${selected.length}`"
45
- v-else-if="collapseTags"
46
- />
47
-
48
- <!-- 多选标签 -->
49
- <template v-else>
50
- <q-chip
51
- v-for="(item, index) in selected"
52
- :key="`options-${index}`"
53
- :label="currentFormatLabel(item)"
54
- dense
55
- :removable="! readonly && ! disable"
56
- @remove="onRemoveSelected(index)"
57
- >
58
- <q-tooltip>{{currentFormatLabel(item)}}</q-tooltip>
59
- </q-chip>
60
- </template>
61
- </template>
62
-
63
- <!-- 占位符-->
64
- <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
65
- </template>
66
-
67
- <!-- 显示文字 -->
68
- <span v-else-if="showValue">{{showValue}}</span>
69
-
70
- <!-- 占位符-->
71
- <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
72
-
73
- <!-- 筛选输入框 -->
74
- <input
75
- ref="inputRef"
76
- class="q-field__input q-placeholder col q-field__input--padding"
77
- v-model="inputValue"
78
- v-if="filter && ! readonly && ! disable"
79
- />
80
-
81
- </template>
82
-
83
- <!-- 弹出对话框图标 -->
84
- <template v-slot:append v-if="! noDialog && ! readonly && ! disable">
85
- <q-icon
86
- class="cursor-pointer"
87
- name="search"
88
- @click.prevent.stop="onShowDialog"
89
- />
90
- </template>
91
-
92
- <!-- 默认插槽 -->
93
- <template
94
- v-for="slotName in slotNames.normal"
95
- v-slot:[slotName]
96
- >
97
- <slot :name="slotName" />
98
- </template>
99
-
100
- <!-- 弹出层代理 -->
101
- <q-popup-proxy
102
- ref="popupRef"
103
- no-refocus
104
- no-focus
105
- fit
106
- @focus="onFieldBlur"
107
- @show="onPopupShow"
108
- @before-hide="showPopup = false"
109
- v-if="! readonly && ! disable"
110
- >
111
- <!-- 快捷表格 -->
112
- <n-table
113
- class="n-table n-field-table__popup-table"
114
- v-model:pagination="tablePagination"
115
- :selected="selected"
116
- @update:selected="emitModelValue"
117
- :row-key="tableRowKey"
118
- :rows="tableRows"
119
- :columns="columns"
120
- :selection="multiple ? 'multiple' : 'none'"
121
- :loading="tableLoading"
122
- :rows-per-page-options="tableRowsPerPageOptions"
123
- @row-click="quickTableRowClick"
124
- @request="tableRequest"
125
- flat
126
- virtual-scroll
127
- dense
128
- v-bind="tableProps"
129
- >
130
- <!-- 图片 -->
131
- <template
132
- v-for="imgItem in tableImgs"
133
- v-slot:[`body-cell-${imgItem.name}`]="props"
134
- >
135
- <n-data
136
- :data="formatImg(props.row[imgItem.name], imgItem)"
137
- v-slot="{ data }"
138
- >
139
- <!-- 缩略图 -->
140
- <n-thumbnail
141
- v-for="(item, index) in data"
142
- :key="`thumbnail-item-${item}`"
143
- class="n-table__thumbnail"
144
- :src="item"
145
- preview
146
- :preview-props="{
147
- startPosition: index,
148
- images: data,
149
- }"
150
- />
151
- </n-data>
152
- </template>
153
-
154
- <!-- 表格插槽 -->
155
- <template
156
- v-for="slotName in slotNames.table"
157
- v-slot:[slotName]="props"
158
- >
159
- <q-td :props="props">
160
- <slot
161
- :name="slotName"
162
- v-bind="props"
163
- />
164
- </q-td>
165
- </template>
166
-
167
- <!-- 翻页 -->
168
- <template v-slot:pagination="props">
169
- <n-table-pagination
170
- :props="props"
171
- no-power
172
- dense
173
- />
174
- </template>
175
- </n-table>
176
- </q-popup-proxy>
177
- </q-field>
178
-
179
- <!-- 弹出对话框 -->
180
- <n-dialog
181
- v-model="showDialog"
182
- width="80%"
183
- :on-confirm="onDialogConfirm"
184
- @before-show="onDialogBeforeShow"
185
- @show="onDialogShow"
186
- @hide="onDialogHide"
187
- cancel
188
- v-bind="dialogProps"
189
- >
190
- <q-page>
191
- <n-mixed-table />
192
- </q-page>
193
- </n-dialog>
194
- </template>
195
-
196
- <script>
197
- import { ref, computed, watch, onUpdated } from 'vue'
198
-
199
- import $n_has from 'lodash/has'
200
- import $n_uniq from 'lodash/uniq'
201
- import $n_cloneDeep from 'lodash/cloneDeep'
202
- import $n_isFunction from 'lodash/isFunction'
203
- import $n_findIndex from 'lodash/findIndex'
204
- import $n_get from 'lodash/get'
205
- import $n_find from 'lodash/find'
206
-
207
- import $n_indexOf from '@netang/utils/indexOf'
208
- import $n_forEach from '@netang/utils/forEach'
209
- import $n_isValidArray from '@netang/utils/isValidArray'
210
- import $n_join from '@netang/utils/join'
211
- import $n_split from '@netang/utils/split'
212
- import $n_isValidObject from '@netang/utils/isValidObject'
213
- import $n_isValidValue from '@netang/utils/isValidValue'
214
- import $n_isValidString from '@netang/utils/isValidString'
215
- import $n_numberDeep from '@netang/utils/numberDeep'
216
- import $n_sleep from '@netang/utils/sleep'
217
- import $n_http from '@netang/utils/http'
218
- import $n_runAsync from '@netang/utils/runAsync'
219
-
220
- import $n_$power from '../../utils/$power'
221
- import $n_$table from '../../utils/$table'
222
-
223
- import { configs } from '../../utils/config'
224
-
225
- import $n_getImage from '../../utils/getImage'
226
-
227
- const {
228
- // 字典常量
229
- dicts,
230
- } = configs
231
-
232
- export default {
233
-
234
- /**
235
- * 标识
236
- */
237
- name: 'NFieldTable',
238
-
239
- /**
240
- * 关闭组件 attribute 透传行为
241
- */
242
- inheritAttrs: false,
243
-
244
- /**
245
- * 声明属性
246
- */
247
- props: {
248
- // 值 v-model
249
- modelValue: {
250
- required: true,
251
- },
252
- // 值字段(必填)
253
- valueKey: {
254
- type: String,
255
- required: true,
256
- },
257
- // 标签字段
258
- labelKey: String,
259
- // 值类型
260
- // string: 字符串或数字
261
- // stringArray: 普通数组(包含字符串或数字的一维数组)
262
- // objectArray: 对象数组(包含对象的一维数组)
263
- valueType: {
264
- type: String,
265
- default: 'objectArray'
266
- },
267
- // 值分隔符(值类型为 string 有效)
268
- valueSeparator: {
269
- type: String,
270
- default: ',',
271
- },
272
-
273
- // 请求路由路径
274
- path: String,
275
- // 请求地址(默认为 path)
276
- url: String,
277
- // 请求参数
278
- query: Object,
279
- // 附加请求数据
280
- data: Object,
281
- // 加载已选数据数组
282
- // 如果有数组数据, 则初始化时从数组中选取已有的数据
283
- loadSelected: [Array, Function],
284
- // 初始是否不加载已选数据
285
- // true, 则初始时不加载数据(同时 loadSelected 无效)
286
- noDefaultLoadSelected: Boolean,
287
- // 更新值时不加载已选数据
288
- noUpdateLoadSelected: Boolean,
289
- // 格式化显示标签
290
- formatLabel: Function,
291
- // 下拉表格显示的字段数组(空为:[值字段, 标签字段])
292
- showKeys: Array,
293
- // 隐藏搜索字段数组
294
- hideSearchKeys: Array,
295
- // 默认筛选字段(空为:标签字段)
296
- filterKey: String,
297
- // 是否开启筛选
298
- filter: Boolean,
299
- // 表格声明属性
300
- tableProps: Object,
301
- // 对话框声明属性
302
- dialogProps: Object,
303
-
304
- // 关闭对话框
305
- noDialog: Boolean,
306
-
307
- // 表格列数据
308
- columns: Array,
309
- // 行数据
310
- rows: Array,
311
- // 是否多选
312
- multiple: Boolean,
313
- // 多选模式下是否折叠标签
314
- collapseTags: Boolean,
315
- // 占位符
316
- placeholder: String,
317
- // 是否可清除
318
- clearable: Boolean,
319
- // 是否禁用
320
- disable: Boolean,
321
- // 是否只读
322
- readonly: Boolean,
323
- // 输入防抖(毫秒)
324
- inputDebounce: {
325
- type: [ Number, String ],
326
- default: 500
327
- },
328
- // 自定义请求方法
329
- request: Function,
330
- // 每次对话框显示都请求
331
- requestEveryDialogShow: Boolean,
332
- },
333
-
334
- /**
335
- * 声明事件
336
- */
337
- emits: [
338
- 'loaded',
339
- 'update:modelValue',
340
- 'update:selected',
341
- ],
342
-
343
- /**
344
- * 组合式
345
- */
346
- setup(props, { emit, slots }) {
347
-
348
- // ==========【计算属性】=========================================================================================
349
-
350
- /**
351
- * 插槽标识
352
- */
353
- const slotNames = computed(function() {
354
-
355
- const table = []
356
- const normal = []
357
-
358
- // 如果有插槽
359
- if ($n_isValidObject(slots)) {
360
- for (const key in slots) {
361
- if (key !== 'append' && key !== 'control') {
362
- if (key.startsWith('table-')) {
363
- table.push(key.replace('table-', ''))
364
- } else {
365
- normal.push(key)
366
- }
367
- }
368
- }
369
- }
370
-
371
- return {
372
- table,
373
- normal,
374
- }
375
- })
376
-
377
- /**
378
- * 当前标签字段
379
- */
380
- const currentlabelKey = computed(function() {
381
- return props.labelKey || props.valueKey
382
- })
383
-
384
- /**
385
- * 当前显示字段
386
- */
387
- const currentShowKeys = computed(function() {
388
- return $n_uniq($n_isValidArray(props.showKeys)
389
- ? props.showKeys
390
- : [ props.valueKey, currentlabelKey.value ])
391
- })
392
-
393
- /**
394
- * 当前搜索字段
395
- */
396
- const currentFilterKey = computed(function() {
397
- return props.filterKey || currentlabelKey.value
398
- })
399
-
400
- /**
401
- * 显示值
402
- */
403
- const showValue = computed(function () {
404
-
405
- // 如果有已选数据
406
- return $n_isValidArray(selected.value)
407
- // 取已选数据第一条
408
- ? currentFormatLabel(selected.value[0])
409
- : ''
410
- })
411
-
412
- // ==========【数据】============================================================================================
413
-
414
- // 创建权限实例
415
- const $power = $n_$power.create({
416
- // 路由路径
417
- path: $n_isValidString(props.path) ? props.path : false,
418
- // 路由参数
419
- query: props.query,
420
- // 关闭权限页面
421
- power: false,
422
- // 禁止对话框注入
423
- $dialog: null,
424
- })
425
-
426
- const {
427
- // 当前路由路径
428
- routePath,
429
- } = $power
430
-
431
- // 创建表格实例
432
- const $table = $n_$table.create({
433
- // 权限实例
434
- $power,
435
- // 请求地址
436
- url: props.url,
437
- // 附加请求数据
438
- data: props.data,
439
- // 获取表格列数据
440
- columns: getTableColumns(),
441
- // 表格行唯一键值
442
- rowKey: props.valueKey,
443
- // 行数据
444
- rows: props.rows,
445
- // 选择类型, 可选值 single multiple none
446
- selection: props.multiple ? 'multiple' : 'single',
447
- // 已选数据
448
- selected: [],
449
- // http 设置
450
- httpSettings: {
451
- // 头部请求
452
- headers: {
453
- // 添加头部查看请求
454
- Pview: 1,
455
- },
456
- },
457
- // 刷新后清空已选数据
458
- refreshResetSelected: false,
459
- // 自定义请求方法
460
- async request({ httpOptions, props: httpProps }) {
461
- return $n_isFunction(props.request) ?
462
- // 如果有自定义请求方法
463
- await $n_runAsync(props.request)({
464
- // http 请求参数
465
- httpOptions,
466
- // 对话框是否已显示
467
- showDialog: $n_get(httpProps, 'showDialog') === 1 ? true : showDialog.value,
468
- }) :
469
- // 否则请求数据
470
- await $n_http(httpOptions)
471
- },
472
- })
473
-
474
- // 创建睡眠实例
475
- const sleep = $n_sleep()
476
-
477
- // 输入框节点
478
- const inputRef = ref(null)
479
-
480
- // 输入框值
481
- const inputValue = ref('')
482
-
483
- // 弹出层节点
484
- const popupRef = ref(null)
485
-
486
- // 是否显示对话框
487
- const showDialog = ref(false)
488
-
489
- // 是否显示弹出层
490
- const showPopup = ref(false)
491
-
492
- // 当前表格列数据
493
- const columns = getQuickTableColumns()
494
-
495
- // 停止观察值
496
- let stopValueWatcher = false
497
-
498
- // 临时已选数据
499
- let tempSelected = []
500
-
501
- // 初始化已选数据
502
- const selected = ref(valueToSelected(props.modelValue, true, true))
503
-
504
- // 加载已选数据
505
- if (
506
- ! props.noDefaultLoadSelected
507
- && props.loadSelected === void 0
508
- ) {
509
- loadSelected()
510
- .finally()
511
- } else {
512
- // 初始化加载成功
513
- emit('loaded', selected.value)
514
- }
515
-
516
- // ==========【监听数据】=========================================================================================
517
-
518
- /**
519
- * 监听声明值
520
- */
521
- watch(() => props.modelValue, async function(val) {
522
-
523
- // 如果停止观察值
524
- if (stopValueWatcher === true) {
525
- // 取消停止观察值
526
- stopValueWatcher = false
527
- return
528
- }
529
-
530
- // 值转已选数据
531
- let newSelected = valueToSelected(val, false, false)
532
-
533
- // 如果值类型是数组对象
534
- if (props.valueType === 'objectArray') {
535
-
536
- // 设置已选数据
537
- setSelected(newSelected)
538
-
539
- // 否则值类型是字符串或数组
540
- } else {
541
-
542
- // 初始已选数据
543
- const _selected = []
544
-
545
- // 如果值转已选数据是有效数组
546
- if (newSelected.length) {
547
-
548
- const newSelectedOld = newSelected
549
- const __selected = []
550
-
551
- // 如果有已选数据
552
- const currentSelected = tempSelected.length ? tempSelected : selected.value
553
- if (currentSelected.length) {
554
-
555
- const _newSelected = []
556
-
557
- // 遍历新已选数据
558
- for (const newItem of newSelected) {
559
- // 已选中的数据
560
- const hasItem = $n_find(currentSelected, e => e[props.valueKey] === newItem)
561
- if (hasItem) {
562
- __selected.push(hasItem)
563
- // 需增加的值
564
- } else {
565
- _newSelected.push(newItem)
566
- }
567
- }
568
-
569
- // 设置新的需要增加的值
570
- newSelected = _newSelected
571
- }
572
-
573
- // 需增加的值
574
- if (newSelected.length) {
575
- // 如果更新值时不加载已选数据
576
- if (props.noUpdateLoadSelected) {
577
- // 请求选择数据
578
- __selected.push(...newSelected.map(e => setSelectedItem(e)))
579
- } else {
580
- // 请求选择数据
581
- __selected.push(...await onRequestSelected(newSelected))
582
- }
583
- }
584
-
585
- // 重新筛选和排序已选数据
586
- for (const item of newSelectedOld) {
587
- // 已选中的数据
588
- const hasItem = $n_find(__selected, e => e[props.valueKey] === item)
589
- if (hasItem) {
590
- _selected.push(hasItem)
591
- }
592
- }
593
- }
594
-
595
- // 设置已选数据
596
- setSelected(_selected)
597
-
598
- // 清空临时已选数据
599
- tempSelected = []
600
- }
601
-
602
- // 将已选数据转为值
603
- const _value = selectedToValue(selected.value)
604
-
605
- // 如果声明值发生变化
606
- if (_value !== props.modelValue) {
607
- // 停止观察值
608
- stopValueWatcher = true
609
- // 触发更新已选数据
610
- emit('update:modelValue', _value)
611
- }
612
-
613
- // 设置输入框焦点
614
- setInputFocus()
615
-
616
- // 设置输入框文字选中
617
- setInputSelection()
618
-
619
- }, {
620
- // 深度监听
621
- deep: true,
622
- })
623
-
624
- /**
625
- * 监听输入框值
626
- */
627
- watch(inputValue, async function (val) {
628
-
629
- // 延迟执行
630
- await sleep(props.inputDebounce)
631
-
632
- // 是否有值
633
- const hasValue = $n_isValidValue(val)
634
-
635
- const n_search = {}
636
- n_search[currentFilterKey.value] = [
637
- {
638
- // 比较类型
639
- compare: dicts.SEARCH_COMPARE_TYPE__LIKE,
640
- // 值
641
- value: hasValue ? val : '',
642
- }
643
- ]
644
-
645
- // 设置表格传参
646
- $table.setQuery({
647
- n_search,
648
- })
649
-
650
- if (
651
- // 如果弹出层是隐藏的
652
- ! showPopup.value
653
- // 如果输入框有值
654
- && hasValue
655
- ) {
656
- // 显示弹出层节点
657
- showPopupRef()
658
- }
659
-
660
- // 表格重新加载
661
- await $table.tableReload()
662
- })
663
-
664
- /**
665
- * 监听其他值
666
- */
667
- // watch([
668
- // ()=>props.path,
669
- // ()=>props.url,
670
- // ()=>props.query,
671
- // ()=>props.data,
672
- // ()=>props.showKeys,
673
- // ()=>props.hideSearchKeys,
674
- // ], function () {
675
- // _dialogShowed = false
676
- // _popupShowed = false
677
- //
678
- // }, {
679
- // deep: true
680
- // })
681
-
682
- // ==========【方法】=============================================================================================
683
-
684
- /**
685
- * 加载已选数据
686
- */
687
- async function loadSelected() {
688
- if (
689
- // 如果值类型不是数组对象
690
- props.valueType !== 'objectArray'
691
- // 如果初始加载已选数据
692
- && ! props.noDefaultLoadSelected
693
- // 如果有请求路由路径
694
- && routePath
695
- ) {
696
- // 获取值数组
697
- const values = valueToSelected(props.modelValue, false, false)
698
- if (values.length) {
699
- // 初始的已选数据
700
- const _selected = await onRequestSelected(values)
701
- const _value = selectedToValue(_selected)
702
-
703
- // 如果声明值未发生变化
704
- if (_value === props.modelValue) {
705
-
706
- // 设置已选数据
707
- setSelected(_selected)
708
-
709
- } else {
710
- // 设置临时已选数据
711
- tempSelected = _selected
712
- // 触发更新值
713
- emit('update:modelValue', _value)
714
- }
715
- // 初始化加载成功
716
- emit('loaded', _selected)
717
- return
718
- }
719
- }
720
-
721
- // 触发更新已选数据
722
- emit('update:selected', selected.value)
723
- // 初始化加载成功
724
- emit('loaded', selected.value)
725
- }
726
-
727
- /**
728
- * 触发更新值
729
- */
730
- function emitModelValue(val) {
731
-
732
- // 设置临时已选数据
733
- tempSelected = val
734
-
735
- // 触发更新值
736
- emit('update:modelValue', selectedToValue(val))
737
- }
738
-
739
- /**
740
- * 设置已选数据
741
- */
742
- function setSelected(val) {
743
-
744
- // 设置已选数据
745
- selected.value = val
746
-
747
- // 触发更新已选数据
748
- emit('update:selected', val)
749
- }
750
-
751
- /**
752
- * 当前格式化显示标签
753
- */
754
- function currentFormatLabel(item) {
755
-
756
- // 如果有格式化显示标签方法
757
- if ($n_isFunction(props.formatLabel)) {
758
- // 执行格式化显示标签方法
759
- return props.formatLabel(item)
760
- }
761
-
762
- // 否则显示该值的标签字段
763
- const val = item[currentlabelKey.value]
764
- return $n_isValidValue(val) ? val : item[props.valueKey]
765
- }
766
-
767
- /**
768
- * 设置已选数据的单个元素
769
- */
770
- function setSelectedItem(val) {
771
- const obj = {}
772
- obj[props.valueKey] = val
773
- obj[currentlabelKey.value] = val
774
- return obj
775
- }
776
-
777
- /**
778
- * 值转已选数据
779
- */
780
- function valueToSelected(val, isFirst, toSelected) {
781
-
782
- // 如果值类型是数组对象
783
- if (props.valueType === 'objectArray') {
784
-
785
- // 如果是有效数组
786
- if ($n_isValidArray(val)) {
787
- for (const item of val) {
788
- if (
789
- // 如果元素不是有效对象
790
- ! $n_isValidObject(item)
791
- // 如果元素没有值字段
792
- || ! $n_has(item, props.valueKey)
793
- ) {
794
- return []
795
- }
796
- }
797
- }
798
-
799
- // 否则直接返回
800
- return val
801
- }
802
-
803
- if (
804
- // 如果初始化
805
- isFirst
806
- // 如果初始加载已选数据方法
807
- && ! props.noDefaultLoadSelected
808
- // 如果有初始加载已选数据
809
- && props.loadSelected !== void 0
810
- ) {
811
- // 将值转为数组
812
- val = props.valueType === 'string' ? $n_split(val, props.valueSeparator) : val
813
-
814
- // 如果是有效数组
815
- if ($n_isValidArray(val)) {
816
- val = val.filter(e => $n_isValidValue(e))
817
- if (val.length) {
818
- return onLoadSelected(val, isFirst)
819
- }
820
- }
821
- return []
822
- }
823
-
824
- if (
825
- // 非初始化
826
- ! isFirst
827
- // 或初始不加载已选数据
828
- || props.noDefaultLoadSelected
829
- // 或没有路由路径
830
- || ! routePath
831
- ) {
832
- // 将值转为数组
833
- val = props.valueType === 'string' ? $n_split(val, props.valueSeparator) : val
834
-
835
- // 如果是有效数组
836
- if ($n_isValidArray(val)) {
837
- val = val.filter(e => $n_isValidValue(e))
838
- return toSelected ? val.map(e => setSelectedItem(e)) : val
839
- }
840
- }
841
-
842
- return []
843
- }
844
-
845
- /**
846
- * 已选数据转值
847
- */
848
- function selectedToValue(val) {
849
-
850
- // 如果值类型是数组对象
851
- if (props.valueType === 'objectArray') {
852
-
853
- // 则直接返回
854
- return val
855
- }
856
-
857
- // 值数组
858
- const values = val.length
859
- // 如果有已选数据
860
- ? (
861
- props.multiple
862
- // 如果是多选
863
- ? val.map(e => e[props.valueKey])
864
- // 否则是单选
865
- : [ val[0][props.valueKey] ]
866
- )
867
- // 否则为空
868
- : []
869
-
870
- // 如果值类型是数组
871
- if (props.valueType === 'stringArray') {
872
-
873
- // 直接返回数组
874
- return values
875
- }
876
-
877
- // 返回转为分隔符隔开的字符串
878
- return $n_numberDeep($n_join(values, props.valueSeparator))
879
- }
880
-
881
- /**
882
- * 加载已选数据
883
- */
884
- function onLoadSelected(values, isFirst) {
885
-
886
- function next(lists) {
887
- const _selected = []
888
- $n_forEach(lists, function (item) {
889
- if (
890
- $n_has(item, props.valueKey)
891
- && $n_indexOf(values, item[props.valueKey]) > -1
892
- && $n_findIndex(_selected, e => e[props.valueKey] === item[props.valueKey]) === -1
893
- ) {
894
- _selected.push($n_cloneDeep(item))
895
- }
896
- })
897
- return _selected
898
- }
899
-
900
- // 如果是加载已选数据方法
901
- if ($n_isFunction(props.loadSelected)) {
902
- const res = props.loadSelected(values, next, isFirst)
903
- if ($n_isValidArray(res)) {
904
- return res
905
- }
906
- return []
907
- }
908
-
909
- return next(props.loadSelected)
910
- }
911
-
912
- /**
913
- * 请求选择数据
914
- */
915
- async function onRequestSelected(value) {
916
-
917
- let requestValues = value
918
-
919
- const all = {}
920
- let hasAll = false
921
-
922
- // 如果有初始加载已选数据数组
923
- if (props.loadSelected !== void 0) {
924
- const rows = onLoadSelected(value, false)
925
- if ($n_isValidArray(rows)) {
926
-
927
- requestValues = []
928
-
929
- for (const item of rows) {
930
- all[item[props.valueKey]] = item
931
- }
932
- for (const val of value) {
933
- if (! $n_has(all, val)) {
934
- requestValues.push(val)
935
- }
936
- }
937
- if (! requestValues.length) {
938
- return rows
939
- }
940
-
941
- hasAll = true
942
- }
943
- }
944
-
945
- // 请求参数
946
- const httpOptions = {
947
- url: $table.routePath,
948
- data: Object.assign(
949
- // 获取表格请求数据
950
- $table.getTableRequestData({
951
- // filter,
952
- pagination: {
953
- // 页码
954
- page: 1,
955
- // 每页的数据条数
956
- rowsPerPage: value.length,
957
- // 排序字段
958
- sortBy: null,
959
- // 是否降序排列
960
- descending: true,
961
- }
962
- }, false),
963
- {
964
- // 查看字段
965
- n_view: {
966
- // 查看字段
967
- field: props.valueKey,
968
- // 查看值
969
- value: requestValues,
970
- },
971
- }
972
- ),
973
- // 是否开启防抖(防止重复请求)
974
- debounce: false,
975
- }
976
-
977
- // 请求数据
978
- const { status, data } = $n_isFunction(props.request) ?
979
- // 如果有自定义请求方法
980
- await $n_runAsync(props.request)({
981
- // http 请求参数
982
- httpOptions,
983
- // 对话框是否已显示
984
- showDialog: showDialog.value,
985
- }) :
986
- // 否则请求数据
987
- await $n_http(httpOptions)
988
-
989
- if (status) {
990
- if ($n_isValidArray($n_get(data, 'rows'))) {
991
- if (! hasAll) {
992
- return data.rows
993
- }
994
- for (const item of data.rows) {
995
- all[item[props.valueKey]] = item
996
- }
997
- }
998
- }
999
-
1000
- const newRows = []
1001
- for (const val of value) {
1002
- if ($n_has(all, val)) {
1003
- newRows.push(all[val])
1004
- }
1005
- }
1006
- return newRows
1007
- }
1008
-
1009
- /**
1010
- * 获取表格列数据
1011
- */
1012
- function getTableColumns() {
1013
-
1014
- let columns
1015
-
1016
- // 如果有声明路由表格列数据
1017
- if ($n_isValidArray(props.columns)) {
1018
- columns = $n_cloneDeep(props.columns)
1019
-
1020
- // 如果有路由路径
1021
- } else if (routePath) {
1022
- // 否则如果有路由表格列数据
1023
- const rawTableColumns = $n_$table.config(routePath, 'columns')
1024
- if ($n_isValidArray(rawTableColumns)) {
1025
- columns = $n_cloneDeep(rawTableColumns)
1026
- }
1027
- }
1028
-
1029
- if ($n_isValidArray(columns)) {
1030
- if ($n_isValidArray(props.hideSearchKeys)) {
1031
- for (const item of columns) {
1032
- if (
1033
- props.hideSearchKeys.indexOf(item.name) > -1
1034
- && $n_has(item, 'search')
1035
- ) {
1036
- item.search.hide = true
1037
- }
1038
- }
1039
- }
1040
- return columns
1041
- }
1042
-
1043
- return []
1044
- }
1045
-
1046
- /**
1047
- * 获取快捷表格列数据
1048
- */
1049
- function getQuickTableColumns() {
1050
-
1051
- const columns = []
1052
-
1053
- // 如果有原始表格列数据
1054
- if ($n_isValidArray($table.tableColumns.value)) {
1055
-
1056
- // 克隆原始表格列数据
1057
- const rawTableColumns = $n_cloneDeep($table.tableColumns.value)
1058
-
1059
- // 快捷表格显示的属性名称数组
1060
- $n_forEach(currentShowKeys.value, function (key) {
1061
- for (const item of rawTableColumns) {
1062
- if (item.name === key) {
1063
- // 删除搜索字段
1064
- if ($n_has(item, 'search')) {
1065
- delete item.search
1066
- }
1067
- // 删除可见字段
1068
- if ($n_has(item, 'visible')) {
1069
- delete item.visible
1070
- }
1071
- columns.push(item)
1072
- }
1073
- }
1074
- })
1075
- }
1076
-
1077
- return columns
1078
- }
1079
-
1080
- /**
1081
- * 移除已选数据
1082
- */
1083
- function onRemoveSelected(index) {
1084
-
1085
- const _selected = [...selected.value]
1086
- _selected.splice(index, 1)
1087
-
1088
- // 触发更新值
1089
- emitModelValue(_selected)
1090
- }
1091
-
1092
- /**
1093
- * 字段获取焦点触发
1094
- */
1095
- function onFieldFocus(e) {
1096
-
1097
- // 停止冒泡
1098
- e.stopPropagation()
1099
-
1100
- // 设置输入框焦点
1101
- setInputFocus()
1102
-
1103
- // window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
1104
- }
1105
-
1106
- /**
1107
- * 字段失去焦点触发
1108
- */
1109
- function onFieldBlur(e) {
1110
-
1111
- // 停止冒泡
1112
- e.stopPropagation()
1113
-
1114
- if (
1115
- // 如果开启筛选
1116
- props.filter
1117
- // 如果没有显示弹出层
1118
- && ! showPopup.value
1119
- ) {
1120
- // 清空输入框值
1121
- inputValue.value = ''
1122
- }
1123
- }
1124
-
1125
- /**
1126
- * 字段清空触发
1127
- */
1128
- function onFieldClear() {
1129
-
1130
- // 触发更新值
1131
- // 清空快捷表格已选数据
1132
- emitModelValue([])
1133
-
1134
- // 隐藏弹出层节点
1135
- hidePopupRef()
1136
- }
1137
-
1138
- /**
1139
- * 显示弹出层节点
1140
- */
1141
- function showPopupRef() {
1142
-
1143
- // 如果有弹出层节点
1144
- if (popupRef.value) {
1145
- // 显示弹出层
1146
- popupRef.value.show()
1147
- }
1148
- }
1149
-
1150
- /**
1151
- * 隐藏弹出层节点
1152
- */
1153
- function hidePopupRef() {
1154
-
1155
- // 如果有弹出层节点
1156
- if (popupRef.value) {
1157
- // 隐藏弹出层
1158
- popupRef.value.hide()
1159
- }
1160
- }
1161
-
1162
- /**
1163
- * 弹出层显示回调
1164
- */
1165
- let _popupShowed = false
1166
- function onPopupShow() {
1167
-
1168
- // 显示弹出层
1169
- showPopup.value = true
1170
-
1171
- // 设置输入框焦点
1172
- setInputFocus()
1173
-
1174
- // 如果每次对话框显示都请求
1175
- if (props.requestEveryDialogShow) {
1176
- // 表格重新加载
1177
- $table.tableReload()
1178
- .finally()
1179
- return
1180
- }
1181
-
1182
- if (_popupShowed) {
1183
- return
1184
- }
1185
- _popupShowed = true
1186
-
1187
- // 表格重新加载
1188
- $table.tableReload()
1189
- .finally()
1190
- }
1191
-
1192
- /**
1193
- * 显示对话框
1194
- */
1195
- function onShowDialog() {
1196
- // 显示对话框
1197
- showDialog.value = true
1198
- }
1199
-
1200
- /**
1201
- * 对话框显示前回调
1202
- */
1203
- function onDialogBeforeShow() {
1204
-
1205
- // 设置当前已选数据
1206
- $table.tableSelected.value = [...selected.value]
1207
-
1208
- // 隐藏弹出层节点
1209
- hidePopupRef()
1210
- }
1211
-
1212
- /**
1213
- * 对话框显示回调
1214
- */
1215
- let _dialogShowed = false
1216
- function onDialogShow() {
1217
-
1218
- // 如果每次对话框显示都请求
1219
- if (props.requestEveryDialogShow) {
1220
- // 表格重新加载
1221
- $table.tableReload()
1222
- .finally()
1223
- return
1224
- }
1225
-
1226
- if (_dialogShowed) {
1227
- return
1228
- }
1229
- _dialogShowed = true
1230
-
1231
- // 表格重新加载
1232
- $table.tableReload()
1233
- .finally()
1234
- }
1235
-
1236
- /**
1237
- * 对话框隐藏后回调
1238
- */
1239
- function onDialogHide() {
1240
-
1241
- let isReload = true
1242
-
1243
- // 清空输入框值
1244
- if (
1245
- // 如果开启筛选
1246
- props.filter
1247
- // 如果有输入框值
1248
- && inputValue.value
1249
- ) {
1250
- // 此时清空输入框后, 会自动刷新表格
1251
- inputValue.value = ''
1252
-
1253
- // 所以只需要重置搜索值即可, 不需要再重置后刷新表格
1254
- isReload = false
1255
- }
1256
-
1257
- // 获取表格搜索值
1258
- let searchValue = $table.getTableSearchValue()
1259
- if (searchValue.length) {
1260
-
1261
- // 如果有隐藏搜索字段数组
1262
- if ($n_isValidArray(props.hideSearchKeys)) {
1263
- // 从搜索值数组中去除隐藏搜索字段的数组
1264
- searchValue = searchValue.filter(e => $n_indexOf(e.field, props.hideSearchKeys) === -1)
1265
- }
1266
-
1267
- // 表格搜索重置
1268
- $table.tableSearchReset(isReload && searchValue.length, {
1269
- showDialog: 1,
1270
- })
1271
- }
1272
- }
1273
-
1274
- /**
1275
- * 对话框点击确认回调
1276
- */
1277
- function onDialogConfirm(data) {
1278
-
1279
- // 触发更新值
1280
- emitModelValue([...data])
1281
- }
1282
-
1283
- /**
1284
- * 单击快捷表格行
1285
- */
1286
- function quickTableRowClick(e, row) {
1287
-
1288
- // 如果为多选
1289
- if (props.multiple) {
1290
-
1291
- // 克隆已选数据
1292
- const _selected = [...selected.value]
1293
-
1294
- const opt = {}
1295
- opt[props.valueKey] = row[props.valueKey]
1296
-
1297
- // 获取当前数据索引
1298
- const itemIndex = $n_findIndex(_selected, opt)
1299
-
1300
- // 如果不存在
1301
- if (itemIndex === -1) {
1302
- // 则添加
1303
- _selected.push(row)
1304
-
1305
- // 否则
1306
- } else {
1307
- // 删除
1308
- _selected.splice(itemIndex, 1)
1309
- }
1310
-
1311
- // 触发更新值
1312
- emitModelValue(_selected)
1313
-
1314
- // 否则为单选
1315
- } else {
1316
-
1317
- // 触发更新值
1318
- emitModelValue([ row ])
1319
-
1320
- // 隐藏弹出层节点
1321
- hidePopupRef()
1322
- }
1323
- }
1324
-
1325
- /**
1326
- * 设置输入框文字选中
1327
- */
1328
- function setInputSelection() {
1329
- if (
1330
- // 如果开启筛选
1331
- props.filter
1332
- // 如果有输入框节点
1333
- && inputRef.value
1334
- // 如果输入框有值
1335
- && inputValue.value.length
1336
- ) {
1337
- // 全选文字
1338
- inputRef.value.select()
1339
- // inputRef.value.setSelectionRange(0, inputValue.value.length)
1340
- }
1341
- }
1342
-
1343
- /**
1344
- * 设置输入框焦点
1345
- */
1346
- function setInputFocus() {
1347
- if (
1348
- // 如果开启筛选
1349
- props.filter
1350
- // 如果有输入框节点
1351
- && inputRef.value
1352
- ) {
1353
- inputRef.value.focus()
1354
- }
1355
- }
1356
-
1357
- /**
1358
- * 格式化图片
1359
- */
1360
- function formatImg(img, { count }) {
1361
-
1362
- // 图片数组
1363
- const imgs = []
1364
-
1365
- // 转为图片数组
1366
- const arr = $n_split(img, ',')
1367
- for (const item of arr) {
1368
- const src = $n_getImage(item)
1369
- if (src) {
1370
- imgs.push(item)
1371
- if (
1372
- count > 0
1373
- && imgs.length === count
1374
- ) {
1375
- break
1376
- }
1377
- }
1378
- }
1379
-
1380
- return imgs
1381
- }
1382
-
1383
- // ==========【生命周期】=========================================================================================
1384
-
1385
- /**
1386
- * 在组件因为响应式状态变更而更新其 DOM 树之后调用
1387
- */
1388
- onUpdated(function () {
1389
- if (
1390
- popupRef.value
1391
- && $n_has(popupRef.value, 'currentComponent.ref.updatePosition')
1392
- ) {
1393
- popupRef.value.currentComponent.ref.updatePosition()
1394
- }
1395
- })
1396
-
1397
- // ==========【返回】=============================================================================================
1398
-
1399
- return {
1400
- // 解构表格实例
1401
- ...$table,
1402
-
1403
- // 插槽标识
1404
- slotNames,
1405
- // 当前标签字段
1406
- currentlabelKey,
1407
- // 显示值
1408
- showValue,
1409
-
1410
- // 输入框节点
1411
- inputRef,
1412
- // 输入框值
1413
- inputValue,
1414
- // 弹出层节点
1415
- popupRef,
1416
- // 是否显示对话框
1417
- showDialog,
1418
- // 是否显示弹出层
1419
- showPopup,
1420
- // 当前已选数据
1421
- selected,
1422
- // 当前表格列数据
1423
- columns,
1424
-
1425
- // 当前格式化显示标签
1426
- currentFormatLabel,
1427
- // 移除已选数据
1428
- onRemoveSelected,
1429
-
1430
- // 字段获取焦点触发
1431
- onFieldFocus,
1432
- // 字段失去焦点触发
1433
- onFieldBlur,
1434
- // 字段清空触发
1435
- onFieldClear,
1436
-
1437
- // 弹出层显示回调
1438
- onPopupShow,
1439
-
1440
- // 显示对话框
1441
- onShowDialog,
1442
- // 对话框显示前回调
1443
- onDialogBeforeShow,
1444
- // 对话框显示回调
1445
- onDialogShow,
1446
- // 对话框隐藏后回调
1447
- onDialogHide,
1448
- // 对话框点击确认回调
1449
- onDialogConfirm,
1450
-
1451
- // 单击快捷表格行
1452
- quickTableRowClick,
1453
-
1454
- // 触发更新值
1455
- emitModelValue,
1456
- // 格式化图片
1457
- formatImg,
1458
- }
1459
- },
1460
- }
1461
- </script>
1462
-
1463
- <style lang="scss">
1464
- .n-field-table {
1465
- .q-field__input--padding {
1466
- padding-left: 4px;
1467
- min-width: 50px !important;
1468
- cursor: text;
1469
- }
1470
- }
1471
-
1472
- /**
1473
- * 桌面
1474
- */
1475
- body.desktop {
1476
- .n-field-table {
1477
- &__popup-table {
1478
- height: 300px;
1479
- }
1480
- }
1481
- }
1482
- </style>
1
+ <template>
2
+
3
+ <!-- 如果有默认插槽 -->
4
+ <template v-if="$slots.default">
5
+ <slot
6
+ :showValue="showValue"
7
+ :selected="selected"
8
+ :onRemove="onRemoveSelected"
9
+ :onShowDialog="onShowDialog"
10
+ :onClear="onFieldClear"
11
+ />
12
+ </template>
13
+
14
+ <!--:class="fieldFocused ? 'q-field&#45;&#45;float q-field&#45;&#45;focused q-field&#45;&#45;highlighted' : ''"-->
15
+ <!--:clearable="clearable && (! multiple || collapseTags)"-->
16
+ <q-field
17
+ class="n-field-table"
18
+ :model-value="showValue"
19
+ :disable="disable"
20
+ :readonly="readonly"
21
+ :clearable="clearable"
22
+ @focus="onFieldFocus"
23
+ @blur="onFieldBlur"
24
+ @clear="onFieldClear"
25
+ v-bind="$attrs"
26
+ v-else
27
+ >
28
+ <template v-slot:control>
29
+
30
+ <template v-if="multiple">
31
+ <template v-if="selected.length">
32
+
33
+ <!-- 多选插槽 -->
34
+ <slot
35
+ name="selected"
36
+ :selected="selected"
37
+ :remove="onRemoveSelected"
38
+ v-if="$slots.selected"
39
+ />
40
+
41
+ <!-- 显示折叠的值数量 -->
42
+ <q-chip
43
+ dense
44
+ :label="`+${selected.length}`"
45
+ v-else-if="collapseTags"
46
+ />
47
+
48
+ <!-- 多选标签 -->
49
+ <template v-else>
50
+ <q-chip
51
+ v-for="(item, index) in selected"
52
+ :key="`options-${index}`"
53
+ :label="currentFormatLabel(item)"
54
+ dense
55
+ :removable="! readonly && ! disable"
56
+ @remove="onRemoveSelected(index)"
57
+ >
58
+ <q-tooltip>{{currentFormatLabel(item)}}</q-tooltip>
59
+ </q-chip>
60
+ </template>
61
+ </template>
62
+
63
+ <!-- 占位符-->
64
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
65
+ </template>
66
+
67
+ <!-- 显示文字 -->
68
+ <span v-else-if="showValue">{{showValue}}</span>
69
+
70
+ <!-- 占位符-->
71
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
72
+
73
+ <!-- 筛选输入框 -->
74
+ <input
75
+ ref="inputRef"
76
+ class="q-field__input q-placeholder col q-field__input--padding"
77
+ v-model="inputValue"
78
+ v-if="filter && ! readonly && ! disable"
79
+ />
80
+
81
+ </template>
82
+
83
+ <!-- 弹出对话框图标 -->
84
+ <template v-slot:append v-if="! noDialog && ! readonly && ! disable">
85
+ <q-icon
86
+ class="cursor-pointer"
87
+ name="search"
88
+ @click.prevent.stop="onShowDialog"
89
+ />
90
+ </template>
91
+
92
+ <!-- 默认插槽 -->
93
+ <template
94
+ v-for="slotName in slotNames.normal"
95
+ v-slot:[slotName]
96
+ >
97
+ <slot :name="slotName" />
98
+ </template>
99
+
100
+ <!-- 弹出层代理 -->
101
+ <q-popup-proxy
102
+ ref="popupRef"
103
+ no-refocus
104
+ no-focus
105
+ fit
106
+ @focus="onFieldBlur"
107
+ @show="onPopupShow"
108
+ @before-hide="showPopup = false"
109
+ v-if="! readonly && ! disable"
110
+ >
111
+ <!-- 快捷表格 -->
112
+ <n-table
113
+ class="n-table n-field-table__popup-table"
114
+ v-model:pagination="tablePagination"
115
+ :selected="selected"
116
+ @update:selected="emitModelValue"
117
+ :row-key="tableRowKey"
118
+ :rows="tableRows"
119
+ :columns="columns"
120
+ :selection="multiple ? 'multiple' : 'none'"
121
+ :loading="tableLoading"
122
+ :rows-per-page-options="tableRowsPerPageOptions"
123
+ @row-click="quickTableRowClick"
124
+ @request="tableRequest"
125
+ flat
126
+ virtual-scroll
127
+ dense
128
+ v-bind="tableProps"
129
+ >
130
+ <!-- 图片 -->
131
+ <template
132
+ v-for="imgItem in tableImgs"
133
+ v-slot:[`body-cell-${imgItem.name}`]="props"
134
+ >
135
+ <n-data
136
+ :data="formatImg(props.row[imgItem.name], imgItem)"
137
+ v-slot="{ data }"
138
+ >
139
+ <!-- 缩略图 -->
140
+ <n-thumbnail
141
+ v-for="(item, index) in data"
142
+ :key="`thumbnail-item-${item}`"
143
+ class="n-table__thumbnail"
144
+ :src="item"
145
+ preview
146
+ :preview-props="{
147
+ startPosition: index,
148
+ images: data,
149
+ }"
150
+ />
151
+ </n-data>
152
+ </template>
153
+
154
+ <!-- 表格插槽 -->
155
+ <template
156
+ v-for="slotName in slotNames.table"
157
+ v-slot:[slotName]="props"
158
+ >
159
+ <q-td :props="props">
160
+ <slot
161
+ :name="slotName"
162
+ v-bind="props"
163
+ />
164
+ </q-td>
165
+ </template>
166
+
167
+ <!-- 翻页 -->
168
+ <template v-slot:pagination="props">
169
+ <n-table-pagination
170
+ :props="props"
171
+ no-power
172
+ dense
173
+ />
174
+ </template>
175
+ </n-table>
176
+ </q-popup-proxy>
177
+ </q-field>
178
+
179
+ <!-- 弹出对话框 -->
180
+ <n-dialog
181
+ v-model="showDialog"
182
+ width="80%"
183
+ :on-confirm="onDialogConfirm"
184
+ @before-show="onDialogBeforeShow"
185
+ @show="onDialogShow"
186
+ @hide="onDialogHide"
187
+ cancel
188
+ v-bind="dialogProps"
189
+ >
190
+ <q-page>
191
+ <n-mixed-table />
192
+ </q-page>
193
+ </n-dialog>
194
+ </template>
195
+
196
+ <script>
197
+ import { ref, computed, watch, onUpdated } from 'vue'
198
+
199
+ import $n_has from 'lodash/has'
200
+ import $n_uniq from 'lodash/uniq'
201
+ import $n_cloneDeep from 'lodash/cloneDeep'
202
+ import $n_isFunction from 'lodash/isFunction'
203
+ import $n_findIndex from 'lodash/findIndex'
204
+ import $n_get from 'lodash/get'
205
+ import $n_find from 'lodash/find'
206
+
207
+ import $n_indexOf from '@netang/utils/indexOf'
208
+ import $n_forEach from '@netang/utils/forEach'
209
+ import $n_isValidArray from '@netang/utils/isValidArray'
210
+ import $n_join from '@netang/utils/join'
211
+ import $n_split from '@netang/utils/split'
212
+ import $n_isValidObject from '@netang/utils/isValidObject'
213
+ import $n_isValidValue from '@netang/utils/isValidValue'
214
+ import $n_isValidString from '@netang/utils/isValidString'
215
+ import $n_numberDeep from '@netang/utils/numberDeep'
216
+ import $n_sleep from '@netang/utils/sleep'
217
+ import $n_http from '@netang/utils/http'
218
+ import $n_runAsync from '@netang/utils/runAsync'
219
+
220
+ import $n_$power from '../../utils/$power'
221
+ import $n_$table from '../../utils/$table'
222
+
223
+ import { configs } from '../../utils/config'
224
+
225
+ import $n_getImage from '../../utils/getImage'
226
+
227
+ const {
228
+ // 字典常量
229
+ dicts,
230
+ } = configs
231
+
232
+ export default {
233
+
234
+ /**
235
+ * 标识
236
+ */
237
+ name: 'NFieldTable',
238
+
239
+ /**
240
+ * 关闭组件 attribute 透传行为
241
+ */
242
+ inheritAttrs: false,
243
+
244
+ /**
245
+ * 声明属性
246
+ */
247
+ props: {
248
+ // 值 v-model
249
+ modelValue: {
250
+ required: true,
251
+ },
252
+ // 值字段(必填)
253
+ valueKey: {
254
+ type: String,
255
+ required: true,
256
+ },
257
+ // 标签字段
258
+ labelKey: String,
259
+ // 值类型
260
+ // string: 字符串或数字
261
+ // stringArray: 普通数组(包含字符串或数字的一维数组)
262
+ // objectArray: 对象数组(包含对象的一维数组)
263
+ valueType: {
264
+ type: String,
265
+ default: 'objectArray'
266
+ },
267
+ // 值分隔符(值类型为 string 有效)
268
+ valueSeparator: {
269
+ type: String,
270
+ default: ',',
271
+ },
272
+
273
+ // 请求路由路径
274
+ path: String,
275
+ // 请求地址(默认为 path)
276
+ url: String,
277
+ // 请求参数
278
+ query: Object,
279
+ // 附加请求数据
280
+ data: Object,
281
+ // 加载已选数据数组
282
+ // 如果有数组数据, 则初始化时从数组中选取已有的数据
283
+ loadSelected: [Array, Function],
284
+ // 初始是否不加载已选数据
285
+ // true, 则初始时不加载数据(同时 loadSelected 无效)
286
+ noDefaultLoadSelected: Boolean,
287
+ // 更新值时不加载已选数据
288
+ noUpdateLoadSelected: Boolean,
289
+ // 格式化显示标签
290
+ formatLabel: Function,
291
+ // 下拉表格显示的字段数组(空为:[值字段, 标签字段])
292
+ showKeys: Array,
293
+ // 隐藏搜索字段数组
294
+ hideSearchKeys: Array,
295
+ // 默认筛选字段(空为:标签字段)
296
+ filterKey: String,
297
+ // 是否开启筛选
298
+ filter: Boolean,
299
+ // 表格声明属性
300
+ tableProps: Object,
301
+ // 对话框声明属性
302
+ dialogProps: Object,
303
+
304
+ // 关闭对话框
305
+ noDialog: Boolean,
306
+
307
+ // 表格列数据
308
+ columns: Array,
309
+ // 行数据
310
+ rows: Array,
311
+ // 是否多选
312
+ multiple: Boolean,
313
+ // 多选模式下是否折叠标签
314
+ collapseTags: Boolean,
315
+ // 占位符
316
+ placeholder: String,
317
+ // 是否可清除
318
+ clearable: Boolean,
319
+ // 是否禁用
320
+ disable: Boolean,
321
+ // 是否只读
322
+ readonly: Boolean,
323
+ // 输入防抖(毫秒)
324
+ inputDebounce: {
325
+ type: [ Number, String ],
326
+ default: 500
327
+ },
328
+ // 自定义请求方法
329
+ request: Function,
330
+ // 每次对话框显示都请求
331
+ requestEveryDialogShow: Boolean,
332
+ },
333
+
334
+ /**
335
+ * 声明事件
336
+ */
337
+ emits: [
338
+ 'loaded',
339
+ 'update:modelValue',
340
+ 'update:selected',
341
+ ],
342
+
343
+ /**
344
+ * 组合式
345
+ */
346
+ setup(props, { emit, slots }) {
347
+
348
+ // ==========【计算属性】=========================================================================================
349
+
350
+ /**
351
+ * 插槽标识
352
+ */
353
+ const slotNames = computed(function() {
354
+
355
+ const table = []
356
+ const normal = []
357
+
358
+ // 如果有插槽
359
+ if ($n_isValidObject(slots)) {
360
+ for (const key in slots) {
361
+ if (key !== 'append' && key !== 'control') {
362
+ if (key.startsWith('table-')) {
363
+ table.push(key.replace('table-', ''))
364
+ } else {
365
+ normal.push(key)
366
+ }
367
+ }
368
+ }
369
+ }
370
+
371
+ return {
372
+ table,
373
+ normal,
374
+ }
375
+ })
376
+
377
+ /**
378
+ * 当前标签字段
379
+ */
380
+ const currentlabelKey = computed(function() {
381
+ return props.labelKey || props.valueKey
382
+ })
383
+
384
+ /**
385
+ * 当前显示字段
386
+ */
387
+ const currentShowKeys = computed(function() {
388
+ return $n_uniq($n_isValidArray(props.showKeys)
389
+ ? props.showKeys
390
+ : [ props.valueKey, currentlabelKey.value ])
391
+ })
392
+
393
+ /**
394
+ * 当前搜索字段
395
+ */
396
+ const currentFilterKey = computed(function() {
397
+ return props.filterKey || currentlabelKey.value
398
+ })
399
+
400
+ /**
401
+ * 显示值
402
+ */
403
+ const showValue = computed(function () {
404
+
405
+ // 如果有已选数据
406
+ return $n_isValidArray(selected.value)
407
+ // 取已选数据第一条
408
+ ? currentFormatLabel(selected.value[0])
409
+ : ''
410
+ })
411
+
412
+ // ==========【数据】============================================================================================
413
+
414
+ // 创建权限实例
415
+ const $power = $n_$power.create({
416
+ // 路由路径
417
+ path: $n_isValidString(props.path) ? props.path : false,
418
+ // 路由参数
419
+ query: props.query,
420
+ // 关闭权限页面
421
+ power: false,
422
+ // 禁止对话框注入
423
+ $dialog: null,
424
+ })
425
+
426
+ const {
427
+ // 当前路由路径
428
+ routePath,
429
+ } = $power
430
+
431
+ // 创建表格实例
432
+ const $table = $n_$table.create({
433
+ // 权限实例
434
+ $power,
435
+ // 请求地址
436
+ url: props.url,
437
+ // 附加请求数据
438
+ data: props.data,
439
+ // 获取表格列数据
440
+ columns: getTableColumns(),
441
+ // 表格行唯一键值
442
+ rowKey: props.valueKey,
443
+ // 行数据
444
+ rows: props.rows,
445
+ // 选择类型, 可选值 single multiple none
446
+ selection: props.multiple ? 'multiple' : 'single',
447
+ // 已选数据
448
+ selected: [],
449
+ // http 设置
450
+ httpSettings: {
451
+ // 头部请求
452
+ headers: {
453
+ // 添加头部查看请求
454
+ Pview: 1,
455
+ },
456
+ },
457
+ // 刷新后清空已选数据
458
+ refreshResetSelected: false,
459
+ // 自定义请求方法
460
+ async request({ httpOptions, props: httpProps }) {
461
+ return $n_isFunction(props.request) ?
462
+ // 如果有自定义请求方法
463
+ await $n_runAsync(props.request)({
464
+ // http 请求参数
465
+ httpOptions,
466
+ // 对话框是否已显示
467
+ showDialog: $n_get(httpProps, 'showDialog') === 1 ? true : showDialog.value,
468
+ }) :
469
+ // 否则请求数据
470
+ await $n_http(httpOptions)
471
+ },
472
+ })
473
+
474
+ // 创建睡眠实例
475
+ const sleep = $n_sleep()
476
+
477
+ // 输入框节点
478
+ const inputRef = ref(null)
479
+
480
+ // 输入框值
481
+ const inputValue = ref('')
482
+
483
+ // 弹出层节点
484
+ const popupRef = ref(null)
485
+
486
+ // 是否显示对话框
487
+ const showDialog = ref(false)
488
+
489
+ // 是否显示弹出层
490
+ const showPopup = ref(false)
491
+
492
+ // 当前表格列数据
493
+ const columns = getQuickTableColumns()
494
+
495
+ // 停止观察值
496
+ let stopValueWatcher = false
497
+
498
+ // 临时已选数据
499
+ let tempSelected = []
500
+
501
+ // 初始化已选数据
502
+ const selected = ref(valueToSelected(props.modelValue, true, true))
503
+
504
+ // 加载已选数据
505
+ if (
506
+ ! props.noDefaultLoadSelected
507
+ && props.loadSelected === void 0
508
+ ) {
509
+ loadSelected()
510
+ .finally()
511
+ } else {
512
+ // 初始化加载成功
513
+ emit('loaded', selected.value)
514
+ }
515
+
516
+ // ==========【监听数据】=========================================================================================
517
+
518
+ /**
519
+ * 监听声明值
520
+ */
521
+ watch(() => props.modelValue, async function(val) {
522
+
523
+ // 如果停止观察值
524
+ if (stopValueWatcher === true) {
525
+ // 取消停止观察值
526
+ stopValueWatcher = false
527
+ return
528
+ }
529
+
530
+ // 值转已选数据
531
+ let newSelected = valueToSelected(val, false, false)
532
+
533
+ // 如果值类型是数组对象
534
+ if (props.valueType === 'objectArray') {
535
+
536
+ // 设置已选数据
537
+ setSelected(newSelected)
538
+
539
+ // 否则值类型是字符串或数组
540
+ } else {
541
+
542
+ // 初始已选数据
543
+ const _selected = []
544
+
545
+ // 如果值转已选数据是有效数组
546
+ if (newSelected.length) {
547
+
548
+ const newSelectedOld = newSelected
549
+ const __selected = []
550
+
551
+ // 如果有已选数据
552
+ const currentSelected = tempSelected.length ? tempSelected : selected.value
553
+ if (currentSelected.length) {
554
+
555
+ const _newSelected = []
556
+
557
+ // 遍历新已选数据
558
+ for (const newItem of newSelected) {
559
+ // 已选中的数据
560
+ const hasItem = $n_find(currentSelected, e => e[props.valueKey] === newItem)
561
+ if (hasItem) {
562
+ __selected.push(hasItem)
563
+ // 需增加的值
564
+ } else {
565
+ _newSelected.push(newItem)
566
+ }
567
+ }
568
+
569
+ // 设置新的需要增加的值
570
+ newSelected = _newSelected
571
+ }
572
+
573
+ // 需增加的值
574
+ if (newSelected.length) {
575
+ // 如果更新值时不加载已选数据
576
+ if (props.noUpdateLoadSelected) {
577
+ // 请求选择数据
578
+ __selected.push(...newSelected.map(e => setSelectedItem(e)))
579
+ } else {
580
+ // 请求选择数据
581
+ __selected.push(...await onRequestSelected(newSelected))
582
+ }
583
+ }
584
+
585
+ // 重新筛选和排序已选数据
586
+ for (const item of newSelectedOld) {
587
+ // 已选中的数据
588
+ const hasItem = $n_find(__selected, e => e[props.valueKey] === item)
589
+ if (hasItem) {
590
+ _selected.push(hasItem)
591
+ }
592
+ }
593
+ }
594
+
595
+ // 设置已选数据
596
+ setSelected(_selected)
597
+
598
+ // 清空临时已选数据
599
+ tempSelected = []
600
+ }
601
+
602
+ // 将已选数据转为值
603
+ const _value = selectedToValue(selected.value)
604
+
605
+ // 如果声明值发生变化
606
+ if (_value !== props.modelValue) {
607
+ // 停止观察值
608
+ stopValueWatcher = true
609
+ // 触发更新已选数据
610
+ emit('update:modelValue', _value)
611
+ }
612
+
613
+ // 设置输入框焦点
614
+ setInputFocus()
615
+
616
+ // 设置输入框文字选中
617
+ setInputSelection()
618
+
619
+ }, {
620
+ // 深度监听
621
+ deep: true,
622
+ })
623
+
624
+ /**
625
+ * 监听输入框值
626
+ */
627
+ watch(inputValue, async function (val) {
628
+
629
+ // 延迟执行
630
+ await sleep(props.inputDebounce)
631
+
632
+ // 是否有值
633
+ const hasValue = $n_isValidValue(val)
634
+
635
+ const n_search = {}
636
+ n_search[currentFilterKey.value] = [
637
+ {
638
+ // 比较类型
639
+ compare: dicts.SEARCH_COMPARE_TYPE__LIKE,
640
+ // 值
641
+ value: hasValue ? val : '',
642
+ }
643
+ ]
644
+
645
+ // 设置表格传参
646
+ $table.setQuery({
647
+ n_search,
648
+ })
649
+
650
+ if (
651
+ // 如果弹出层是隐藏的
652
+ ! showPopup.value
653
+ // 如果输入框有值
654
+ && hasValue
655
+ ) {
656
+ // 显示弹出层节点
657
+ showPopupRef()
658
+ }
659
+
660
+ // 表格重新加载
661
+ await $table.tableReload()
662
+ })
663
+
664
+ /**
665
+ * 监听其他值
666
+ */
667
+ // watch([
668
+ // ()=>props.path,
669
+ // ()=>props.url,
670
+ // ()=>props.query,
671
+ // ()=>props.data,
672
+ // ()=>props.showKeys,
673
+ // ()=>props.hideSearchKeys,
674
+ // ], function () {
675
+ // _dialogShowed = false
676
+ // _popupShowed = false
677
+ //
678
+ // }, {
679
+ // deep: true
680
+ // })
681
+
682
+ // ==========【方法】=============================================================================================
683
+
684
+ /**
685
+ * 加载已选数据
686
+ */
687
+ async function loadSelected() {
688
+ if (
689
+ // 如果值类型不是数组对象
690
+ props.valueType !== 'objectArray'
691
+ // 如果初始加载已选数据
692
+ && ! props.noDefaultLoadSelected
693
+ // 如果有请求路由路径
694
+ && routePath
695
+ ) {
696
+ // 获取值数组
697
+ const values = valueToSelected(props.modelValue, false, false)
698
+ if (values.length) {
699
+ // 初始的已选数据
700
+ const _selected = await onRequestSelected(values)
701
+ const _value = selectedToValue(_selected)
702
+
703
+ // 如果声明值未发生变化
704
+ if (_value === props.modelValue) {
705
+
706
+ // 设置已选数据
707
+ setSelected(_selected)
708
+
709
+ } else {
710
+ // 设置临时已选数据
711
+ tempSelected = _selected
712
+ // 触发更新值
713
+ emit('update:modelValue', _value)
714
+ }
715
+ // 初始化加载成功
716
+ emit('loaded', _selected)
717
+ return
718
+ }
719
+ }
720
+
721
+ // 触发更新已选数据
722
+ emit('update:selected', selected.value)
723
+ // 初始化加载成功
724
+ emit('loaded', selected.value)
725
+ }
726
+
727
+ /**
728
+ * 触发更新值
729
+ */
730
+ function emitModelValue(val) {
731
+
732
+ // 设置临时已选数据
733
+ tempSelected = val
734
+
735
+ // 触发更新值
736
+ emit('update:modelValue', selectedToValue(val))
737
+ }
738
+
739
+ /**
740
+ * 设置已选数据
741
+ */
742
+ function setSelected(val) {
743
+
744
+ // 设置已选数据
745
+ selected.value = val
746
+
747
+ // 触发更新已选数据
748
+ emit('update:selected', val)
749
+ }
750
+
751
+ /**
752
+ * 当前格式化显示标签
753
+ */
754
+ function currentFormatLabel(item) {
755
+
756
+ // 如果有格式化显示标签方法
757
+ if ($n_isFunction(props.formatLabel)) {
758
+ // 执行格式化显示标签方法
759
+ return props.formatLabel(item)
760
+ }
761
+
762
+ // 否则显示该值的标签字段
763
+ const val = item[currentlabelKey.value]
764
+ return $n_isValidValue(val) ? val : item[props.valueKey]
765
+ }
766
+
767
+ /**
768
+ * 设置已选数据的单个元素
769
+ */
770
+ function setSelectedItem(val) {
771
+ const obj = {}
772
+ obj[props.valueKey] = val
773
+ obj[currentlabelKey.value] = val
774
+ return obj
775
+ }
776
+
777
+ /**
778
+ * 值转已选数据
779
+ */
780
+ function valueToSelected(val, isFirst, toSelected) {
781
+
782
+ // 如果值类型是数组对象
783
+ if (props.valueType === 'objectArray') {
784
+
785
+ // 如果是有效数组
786
+ if ($n_isValidArray(val)) {
787
+ for (const item of val) {
788
+ if (
789
+ // 如果元素不是有效对象
790
+ ! $n_isValidObject(item)
791
+ // 如果元素没有值字段
792
+ || ! $n_has(item, props.valueKey)
793
+ ) {
794
+ return []
795
+ }
796
+ }
797
+ }
798
+
799
+ // 否则直接返回
800
+ return val
801
+ }
802
+
803
+ if (
804
+ // 如果初始化
805
+ isFirst
806
+ // 如果初始加载已选数据方法
807
+ && ! props.noDefaultLoadSelected
808
+ // 如果有初始加载已选数据
809
+ && props.loadSelected !== void 0
810
+ ) {
811
+ // 将值转为数组
812
+ val = props.valueType === 'string' ? $n_split(val, props.valueSeparator) : val
813
+
814
+ // 如果是有效数组
815
+ if ($n_isValidArray(val)) {
816
+ val = val.filter(e => $n_isValidValue(e))
817
+ if (val.length) {
818
+ return onLoadSelected(val, isFirst)
819
+ }
820
+ }
821
+ return []
822
+ }
823
+
824
+ if (
825
+ // 非初始化
826
+ ! isFirst
827
+ // 或初始不加载已选数据
828
+ || props.noDefaultLoadSelected
829
+ // 或没有路由路径
830
+ || ! routePath
831
+ ) {
832
+ // 将值转为数组
833
+ val = props.valueType === 'string' ? $n_split(val, props.valueSeparator) : val
834
+
835
+ // 如果是有效数组
836
+ if ($n_isValidArray(val)) {
837
+ val = val.filter(e => $n_isValidValue(e))
838
+ return toSelected ? val.map(e => setSelectedItem(e)) : val
839
+ }
840
+ }
841
+
842
+ return []
843
+ }
844
+
845
+ /**
846
+ * 已选数据转值
847
+ */
848
+ function selectedToValue(val) {
849
+
850
+ // 如果值类型是数组对象
851
+ if (props.valueType === 'objectArray') {
852
+
853
+ // 则直接返回
854
+ return val
855
+ }
856
+
857
+ // 值数组
858
+ const values = val.length
859
+ // 如果有已选数据
860
+ ? (
861
+ props.multiple
862
+ // 如果是多选
863
+ ? val.map(e => e[props.valueKey])
864
+ // 否则是单选
865
+ : [ val[0][props.valueKey] ]
866
+ )
867
+ // 否则为空
868
+ : []
869
+
870
+ // 如果值类型是数组
871
+ if (props.valueType === 'stringArray') {
872
+
873
+ // 直接返回数组
874
+ return values
875
+ }
876
+
877
+ // 返回转为分隔符隔开的字符串
878
+ return $n_numberDeep($n_join(values, props.valueSeparator))
879
+ }
880
+
881
+ /**
882
+ * 加载已选数据
883
+ */
884
+ function onLoadSelected(values, isFirst) {
885
+
886
+ function next(lists) {
887
+ const _selected = []
888
+ $n_forEach(lists, function (item) {
889
+ if (
890
+ $n_has(item, props.valueKey)
891
+ && $n_indexOf(values, item[props.valueKey]) > -1
892
+ && $n_findIndex(_selected, e => e[props.valueKey] === item[props.valueKey]) === -1
893
+ ) {
894
+ _selected.push($n_cloneDeep(item))
895
+ }
896
+ })
897
+ return _selected
898
+ }
899
+
900
+ // 如果是加载已选数据方法
901
+ if ($n_isFunction(props.loadSelected)) {
902
+ const res = props.loadSelected(values, next, isFirst)
903
+ if ($n_isValidArray(res)) {
904
+ return res
905
+ }
906
+ return []
907
+ }
908
+
909
+ return next(props.loadSelected)
910
+ }
911
+
912
+ /**
913
+ * 请求选择数据
914
+ */
915
+ async function onRequestSelected(value) {
916
+
917
+ let requestValues = value
918
+
919
+ const all = {}
920
+ let hasAll = false
921
+
922
+ // 如果有初始加载已选数据数组
923
+ if (props.loadSelected !== void 0) {
924
+ const rows = onLoadSelected(value, false)
925
+ if ($n_isValidArray(rows)) {
926
+
927
+ requestValues = []
928
+
929
+ for (const item of rows) {
930
+ all[item[props.valueKey]] = item
931
+ }
932
+ for (const val of value) {
933
+ if (! $n_has(all, val)) {
934
+ requestValues.push(val)
935
+ }
936
+ }
937
+ if (! requestValues.length) {
938
+ return rows
939
+ }
940
+
941
+ hasAll = true
942
+ }
943
+ }
944
+
945
+ // 请求参数
946
+ const httpOptions = {
947
+ url: $table.routePath,
948
+ data: Object.assign(
949
+ // 获取表格请求数据
950
+ $table.getTableRequestData({
951
+ // filter,
952
+ pagination: {
953
+ // 页码
954
+ page: 1,
955
+ // 每页的数据条数
956
+ rowsPerPage: value.length,
957
+ // 排序字段
958
+ sortBy: null,
959
+ // 是否降序排列
960
+ descending: true,
961
+ }
962
+ }, false),
963
+ {
964
+ // 查看字段
965
+ n_view: {
966
+ // 查看字段
967
+ field: props.valueKey,
968
+ // 查看值
969
+ value: requestValues,
970
+ },
971
+ }
972
+ ),
973
+ // 是否开启防抖(防止重复请求)
974
+ debounce: false,
975
+ }
976
+
977
+ // 请求数据
978
+ const { status, data } = $n_isFunction(props.request) ?
979
+ // 如果有自定义请求方法
980
+ await $n_runAsync(props.request)({
981
+ // http 请求参数
982
+ httpOptions,
983
+ // 对话框是否已显示
984
+ showDialog: showDialog.value,
985
+ }) :
986
+ // 否则请求数据
987
+ await $n_http(httpOptions)
988
+
989
+ if (status) {
990
+ if ($n_isValidArray($n_get(data, 'rows'))) {
991
+ if (! hasAll) {
992
+ return data.rows
993
+ }
994
+ for (const item of data.rows) {
995
+ all[item[props.valueKey]] = item
996
+ }
997
+ }
998
+ }
999
+
1000
+ const newRows = []
1001
+ for (const val of value) {
1002
+ if ($n_has(all, val)) {
1003
+ newRows.push(all[val])
1004
+ }
1005
+ }
1006
+ return newRows
1007
+ }
1008
+
1009
+ /**
1010
+ * 获取表格列数据
1011
+ */
1012
+ function getTableColumns() {
1013
+
1014
+ let columns
1015
+
1016
+ // 如果有声明路由表格列数据
1017
+ if ($n_isValidArray(props.columns)) {
1018
+ columns = $n_cloneDeep(props.columns)
1019
+
1020
+ // 如果有路由路径
1021
+ } else if (routePath) {
1022
+ // 否则如果有路由表格列数据
1023
+ const rawTableColumns = $n_$table.config(routePath, 'columns')
1024
+ if ($n_isValidArray(rawTableColumns)) {
1025
+ columns = $n_cloneDeep(rawTableColumns)
1026
+ }
1027
+ }
1028
+
1029
+ if ($n_isValidArray(columns)) {
1030
+ if ($n_isValidArray(props.hideSearchKeys)) {
1031
+ for (const item of columns) {
1032
+ if (
1033
+ props.hideSearchKeys.indexOf(item.name) > -1
1034
+ && $n_has(item, 'search')
1035
+ ) {
1036
+ item.search.hide = true
1037
+ }
1038
+ }
1039
+ }
1040
+ return columns
1041
+ }
1042
+
1043
+ return []
1044
+ }
1045
+
1046
+ /**
1047
+ * 获取快捷表格列数据
1048
+ */
1049
+ function getQuickTableColumns() {
1050
+
1051
+ const columns = []
1052
+
1053
+ // 如果有原始表格列数据
1054
+ if ($n_isValidArray($table.tableColumns.value)) {
1055
+
1056
+ // 克隆原始表格列数据
1057
+ const rawTableColumns = $n_cloneDeep($table.tableColumns.value)
1058
+
1059
+ // 快捷表格显示的属性名称数组
1060
+ $n_forEach(currentShowKeys.value, function (key) {
1061
+ for (const item of rawTableColumns) {
1062
+ if (item.name === key) {
1063
+ // 删除搜索字段
1064
+ if ($n_has(item, 'search')) {
1065
+ delete item.search
1066
+ }
1067
+ // 删除可见字段
1068
+ if ($n_has(item, 'visible')) {
1069
+ delete item.visible
1070
+ }
1071
+ columns.push(item)
1072
+ }
1073
+ }
1074
+ })
1075
+ }
1076
+
1077
+ return columns
1078
+ }
1079
+
1080
+ /**
1081
+ * 移除已选数据
1082
+ */
1083
+ function onRemoveSelected(index) {
1084
+
1085
+ const _selected = [...selected.value]
1086
+ _selected.splice(index, 1)
1087
+
1088
+ // 触发更新值
1089
+ emitModelValue(_selected)
1090
+ }
1091
+
1092
+ /**
1093
+ * 字段获取焦点触发
1094
+ */
1095
+ function onFieldFocus(e) {
1096
+
1097
+ // 停止冒泡
1098
+ e.stopPropagation()
1099
+
1100
+ // 设置输入框焦点
1101
+ setInputFocus()
1102
+
1103
+ // window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
1104
+ }
1105
+
1106
+ /**
1107
+ * 字段失去焦点触发
1108
+ */
1109
+ function onFieldBlur(e) {
1110
+
1111
+ // 停止冒泡
1112
+ e.stopPropagation()
1113
+
1114
+ if (
1115
+ // 如果开启筛选
1116
+ props.filter
1117
+ // 如果没有显示弹出层
1118
+ && ! showPopup.value
1119
+ ) {
1120
+ // 清空输入框值
1121
+ inputValue.value = ''
1122
+ }
1123
+ }
1124
+
1125
+ /**
1126
+ * 字段清空触发
1127
+ */
1128
+ function onFieldClear() {
1129
+
1130
+ // 触发更新值
1131
+ // 清空快捷表格已选数据
1132
+ emitModelValue([])
1133
+
1134
+ // 隐藏弹出层节点
1135
+ hidePopupRef()
1136
+ }
1137
+
1138
+ /**
1139
+ * 显示弹出层节点
1140
+ */
1141
+ function showPopupRef() {
1142
+
1143
+ // 如果有弹出层节点
1144
+ if (popupRef.value) {
1145
+ // 显示弹出层
1146
+ popupRef.value.show()
1147
+ }
1148
+ }
1149
+
1150
+ /**
1151
+ * 隐藏弹出层节点
1152
+ */
1153
+ function hidePopupRef() {
1154
+
1155
+ // 如果有弹出层节点
1156
+ if (popupRef.value) {
1157
+ // 隐藏弹出层
1158
+ popupRef.value.hide()
1159
+ }
1160
+ }
1161
+
1162
+ /**
1163
+ * 弹出层显示回调
1164
+ */
1165
+ let _popupShowed = false
1166
+ function onPopupShow() {
1167
+
1168
+ // 显示弹出层
1169
+ showPopup.value = true
1170
+
1171
+ // 设置输入框焦点
1172
+ setInputFocus()
1173
+
1174
+ // 如果每次对话框显示都请求
1175
+ if (props.requestEveryDialogShow) {
1176
+ // 表格重新加载
1177
+ $table.tableReload()
1178
+ .finally()
1179
+ return
1180
+ }
1181
+
1182
+ if (_popupShowed) {
1183
+ return
1184
+ }
1185
+ _popupShowed = true
1186
+
1187
+ // 表格重新加载
1188
+ $table.tableReload()
1189
+ .finally()
1190
+ }
1191
+
1192
+ /**
1193
+ * 显示对话框
1194
+ */
1195
+ function onShowDialog() {
1196
+ // 显示对话框
1197
+ showDialog.value = true
1198
+ }
1199
+
1200
+ /**
1201
+ * 对话框显示前回调
1202
+ */
1203
+ function onDialogBeforeShow() {
1204
+
1205
+ // 设置当前已选数据
1206
+ $table.tableSelected.value = [...selected.value]
1207
+
1208
+ // 隐藏弹出层节点
1209
+ hidePopupRef()
1210
+ }
1211
+
1212
+ /**
1213
+ * 对话框显示回调
1214
+ */
1215
+ let _dialogShowed = false
1216
+ function onDialogShow() {
1217
+
1218
+ // 如果每次对话框显示都请求
1219
+ if (props.requestEveryDialogShow) {
1220
+ // 表格重新加载
1221
+ $table.tableReload()
1222
+ .finally()
1223
+ return
1224
+ }
1225
+
1226
+ if (_dialogShowed) {
1227
+ return
1228
+ }
1229
+ _dialogShowed = true
1230
+
1231
+ // 表格重新加载
1232
+ $table.tableReload()
1233
+ .finally()
1234
+ }
1235
+
1236
+ /**
1237
+ * 对话框隐藏后回调
1238
+ */
1239
+ function onDialogHide() {
1240
+
1241
+ let isReload = true
1242
+
1243
+ // 清空输入框值
1244
+ if (
1245
+ // 如果开启筛选
1246
+ props.filter
1247
+ // 如果有输入框值
1248
+ && inputValue.value
1249
+ ) {
1250
+ // 此时清空输入框后, 会自动刷新表格
1251
+ inputValue.value = ''
1252
+
1253
+ // 所以只需要重置搜索值即可, 不需要再重置后刷新表格
1254
+ isReload = false
1255
+ }
1256
+
1257
+ // 获取表格搜索值
1258
+ let searchValue = $table.getTableSearchValue()
1259
+ if (searchValue.length) {
1260
+
1261
+ // 如果有隐藏搜索字段数组
1262
+ if ($n_isValidArray(props.hideSearchKeys)) {
1263
+ // 从搜索值数组中去除隐藏搜索字段的数组
1264
+ searchValue = searchValue.filter(e => $n_indexOf(e.field, props.hideSearchKeys) === -1)
1265
+ }
1266
+
1267
+ // 表格搜索重置
1268
+ $table.tableSearchReset(isReload && searchValue.length, {
1269
+ showDialog: 1,
1270
+ })
1271
+ }
1272
+ }
1273
+
1274
+ /**
1275
+ * 对话框点击确认回调
1276
+ */
1277
+ function onDialogConfirm(data) {
1278
+
1279
+ // 触发更新值
1280
+ emitModelValue([...data])
1281
+ }
1282
+
1283
+ /**
1284
+ * 单击快捷表格行
1285
+ */
1286
+ function quickTableRowClick(e, row) {
1287
+
1288
+ // 如果为多选
1289
+ if (props.multiple) {
1290
+
1291
+ // 克隆已选数据
1292
+ const _selected = [...selected.value]
1293
+
1294
+ const opt = {}
1295
+ opt[props.valueKey] = row[props.valueKey]
1296
+
1297
+ // 获取当前数据索引
1298
+ const itemIndex = $n_findIndex(_selected, opt)
1299
+
1300
+ // 如果不存在
1301
+ if (itemIndex === -1) {
1302
+ // 则添加
1303
+ _selected.push(row)
1304
+
1305
+ // 否则
1306
+ } else {
1307
+ // 删除
1308
+ _selected.splice(itemIndex, 1)
1309
+ }
1310
+
1311
+ // 触发更新值
1312
+ emitModelValue(_selected)
1313
+
1314
+ // 否则为单选
1315
+ } else {
1316
+
1317
+ // 触发更新值
1318
+ emitModelValue([ row ])
1319
+
1320
+ // 隐藏弹出层节点
1321
+ hidePopupRef()
1322
+ }
1323
+ }
1324
+
1325
+ /**
1326
+ * 设置输入框文字选中
1327
+ */
1328
+ function setInputSelection() {
1329
+ if (
1330
+ // 如果开启筛选
1331
+ props.filter
1332
+ // 如果有输入框节点
1333
+ && inputRef.value
1334
+ // 如果输入框有值
1335
+ && inputValue.value.length
1336
+ ) {
1337
+ // 全选文字
1338
+ inputRef.value.select()
1339
+ // inputRef.value.setSelectionRange(0, inputValue.value.length)
1340
+ }
1341
+ }
1342
+
1343
+ /**
1344
+ * 设置输入框焦点
1345
+ */
1346
+ function setInputFocus() {
1347
+ if (
1348
+ // 如果开启筛选
1349
+ props.filter
1350
+ // 如果有输入框节点
1351
+ && inputRef.value
1352
+ ) {
1353
+ inputRef.value.focus()
1354
+ }
1355
+ }
1356
+
1357
+ /**
1358
+ * 格式化图片
1359
+ */
1360
+ function formatImg(img, { count }) {
1361
+
1362
+ // 图片数组
1363
+ const imgs = []
1364
+
1365
+ // 转为图片数组
1366
+ const arr = $n_split(img, ',')
1367
+ for (const item of arr) {
1368
+ const src = $n_getImage(item)
1369
+ if (src) {
1370
+ imgs.push(item)
1371
+ if (
1372
+ count > 0
1373
+ && imgs.length === count
1374
+ ) {
1375
+ break
1376
+ }
1377
+ }
1378
+ }
1379
+
1380
+ return imgs
1381
+ }
1382
+
1383
+ // ==========【生命周期】=========================================================================================
1384
+
1385
+ /**
1386
+ * 在组件因为响应式状态变更而更新其 DOM 树之后调用
1387
+ */
1388
+ onUpdated(function () {
1389
+ if (
1390
+ popupRef.value
1391
+ && $n_has(popupRef.value, 'currentComponent.ref.updatePosition')
1392
+ ) {
1393
+ popupRef.value.currentComponent.ref.updatePosition()
1394
+ }
1395
+ })
1396
+
1397
+ // ==========【返回】=============================================================================================
1398
+
1399
+ return {
1400
+ // 解构表格实例
1401
+ ...$table,
1402
+
1403
+ // 插槽标识
1404
+ slotNames,
1405
+ // 当前标签字段
1406
+ currentlabelKey,
1407
+ // 显示值
1408
+ showValue,
1409
+
1410
+ // 输入框节点
1411
+ inputRef,
1412
+ // 输入框值
1413
+ inputValue,
1414
+ // 弹出层节点
1415
+ popupRef,
1416
+ // 是否显示对话框
1417
+ showDialog,
1418
+ // 是否显示弹出层
1419
+ showPopup,
1420
+ // 当前已选数据
1421
+ selected,
1422
+ // 当前表格列数据
1423
+ columns,
1424
+
1425
+ // 当前格式化显示标签
1426
+ currentFormatLabel,
1427
+ // 移除已选数据
1428
+ onRemoveSelected,
1429
+
1430
+ // 字段获取焦点触发
1431
+ onFieldFocus,
1432
+ // 字段失去焦点触发
1433
+ onFieldBlur,
1434
+ // 字段清空触发
1435
+ onFieldClear,
1436
+
1437
+ // 弹出层显示回调
1438
+ onPopupShow,
1439
+
1440
+ // 显示对话框
1441
+ onShowDialog,
1442
+ // 对话框显示前回调
1443
+ onDialogBeforeShow,
1444
+ // 对话框显示回调
1445
+ onDialogShow,
1446
+ // 对话框隐藏后回调
1447
+ onDialogHide,
1448
+ // 对话框点击确认回调
1449
+ onDialogConfirm,
1450
+
1451
+ // 单击快捷表格行
1452
+ quickTableRowClick,
1453
+
1454
+ // 触发更新值
1455
+ emitModelValue,
1456
+ // 格式化图片
1457
+ formatImg,
1458
+ }
1459
+ },
1460
+ }
1461
+ </script>
1462
+
1463
+ <style lang="scss">
1464
+ .n-field-table {
1465
+ .q-field__input--padding {
1466
+ padding-left: 4px;
1467
+ min-width: 50px !important;
1468
+ cursor: text;
1469
+ }
1470
+ }
1471
+
1472
+ /**
1473
+ * 桌面
1474
+ */
1475
+ body.desktop {
1476
+ .n-field-table {
1477
+ &__popup-table {
1478
+ height: 300px;
1479
+ }
1480
+ }
1481
+ }
1482
+ </style>