@netang/quasar 0.1.67 → 0.1.68

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 (247) 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/dialog/img-viewer/index.vue +657 -657
  7. package/components/dialog/index.vue +372 -372
  8. package/components/drawer/index.vue +303 -303
  9. package/components/editor-code/index.vue +325 -325
  10. package/components/empty/index.vue +80 -80
  11. package/components/field-date/index.vue +850 -850
  12. package/components/field-table/index.vue +1277 -1277
  13. package/components/field-tree/index.vue +754 -754
  14. package/components/img/index.vue +211 -211
  15. package/components/mixed-table/index.vue +532 -532
  16. package/components/mixed-table-splitter/index.vue +377 -377
  17. package/components/power-page/index.vue +94 -94
  18. package/components/private/components/move-to-tree/index.vue +154 -154
  19. package/components/private/edit-power-data/index.vue +846 -846
  20. package/components/private/table-visible-columns-button/index.vue +114 -114
  21. package/components/render/index.vue +123 -123
  22. package/components/search/index.vue +231 -231
  23. package/components/search-item/index.vue +212 -212
  24. package/components/select/index.vue +177 -177
  25. package/components/splitter/index.vue +422 -422
  26. package/components/table/index.vue +513 -513
  27. package/components/table-column-fixed/index.vue +110 -110
  28. package/components/table-summary/index.vue +107 -107
  29. package/components/toolbar/index.vue +146 -146
  30. package/components/tree/index.vue +1728 -1728
  31. package/components/uploader/index.vue +188 -188
  32. package/components/uploader-query/index.vue +883 -840
  33. package/docs/404.html +33 -33
  34. package/docs/assets/404.html-60b35caa.js +1 -1
  35. package/docs/assets/404.html-d1e63d77.js +1 -1
  36. package/docs/assets/alert.html-b2a2a72f.js +5 -5
  37. package/docs/assets/alert.html-ba46d137.js +1 -1
  38. package/docs/assets/app-9f30aa4b.js +6 -6
  39. package/docs/assets/area.html-01b9b58d.js +42 -42
  40. package/docs/assets/area.html-9a4fce6a.js +1 -1
  41. package/docs/assets/arr.html-145d27e7.js +1 -1
  42. package/docs/assets/arr.html-674e65ab.js +11 -11
  43. package/docs/assets/auth.html-579fa830.js +1 -1
  44. package/docs/assets/auth.html-8544ed95.js +8 -8
  45. package/docs/assets/bus.html-c71254aa.js +1 -1
  46. package/docs/assets/bus.html-dc7d3d19.js +6 -6
  47. package/docs/assets/column-title.html-c735cb5a.js +3 -3
  48. package/docs/assets/column-title.html-e9316762.js +1 -1
  49. package/docs/assets/confirm.html-ddfdc27f.js +10 -10
  50. package/docs/assets/confirm.html-ef3e2bef.js +1 -1
  51. package/docs/assets/copy.html-d20345b6.js +1 -1
  52. package/docs/assets/copy.html-ef8c8571.js +13 -13
  53. package/docs/assets/data.html-6432175d.js +30 -30
  54. package/docs/assets/data.html-a3b05d5b.js +1 -1
  55. package/docs/assets/dialog.html-1f698e5a.js +1 -1
  56. package/docs/assets/dialog.html-62902b83.js +68 -68
  57. package/docs/assets/dialog.html-baea77c9.js +1 -1
  58. package/docs/assets/dialog.html-bb082fc4.js +1 -1
  59. package/docs/assets/dict.html-1311da3d.js +23 -23
  60. package/docs/assets/dict.html-b96fbf0c.js +1 -1
  61. package/docs/assets/dictOptions.html-7c4f40a5.js +1 -1
  62. package/docs/assets/dictOptions.html-fb99d175.js +5 -5
  63. package/docs/assets/dragger.html-668d3efa.js +1 -1
  64. package/docs/assets/dragger.html-749d585a.js +1 -1
  65. package/docs/assets/editor-code.html-6ab26ea9.js +1 -1
  66. package/docs/assets/editor-code.html-d196205d.js +1 -1
  67. package/docs/assets/empty.html-1c139131.js +1 -1
  68. package/docs/assets/empty.html-1e9c441d.js +1 -1
  69. package/docs/assets/field-date.html-069fdb13.js +1 -1
  70. package/docs/assets/field-date.html-ad204aa9.js +1 -1
  71. package/docs/assets/field-table.html-ce480f03.js +1 -1
  72. package/docs/assets/field-table.html-d9236160.js +1 -1
  73. package/docs/assets/field-text.html-7277c62f.js +1 -1
  74. package/docs/assets/field-text.html-ccb4cecf.js +1 -1
  75. package/docs/assets/field-tree.html-519bfb45.js +1 -1
  76. package/docs/assets/field-tree.html-fdc748d6.js +1 -1
  77. package/docs/assets/form.html-2b562c37.js +2 -2
  78. package/docs/assets/form.html-75104cd5.js +1 -1
  79. package/docs/assets/framework-204010b2.js +5 -5
  80. package/docs/assets/getData.html-990e3787.js +1 -1
  81. package/docs/assets/getData.html-bb72025f.js +34 -34
  82. package/docs/assets/getFile.html-42368004.js +1 -1
  83. package/docs/assets/getFile.html-99abd054.js +3 -3
  84. package/docs/assets/getImage.html-3429c5a1.js +1 -1
  85. package/docs/assets/getImage.html-4d886d83.js +3 -3
  86. package/docs/assets/getTime.html-7435f922.js +1 -1
  87. package/docs/assets/getTime.html-b37f49eb.js +20 -20
  88. package/docs/assets/img.html-7d1da657.js +1 -1
  89. package/docs/assets/img.html-fbea1105.js +1 -1
  90. package/docs/assets/index.html-1695dd7c.js +1 -1
  91. package/docs/assets/index.html-65a4aa67.js +1 -1
  92. package/docs/assets/index.html-7b98d5bd.js +1 -1
  93. package/docs/assets/index.html-c01f2648.js +1 -1
  94. package/docs/assets/input-number.html-0b250d2a.js +1 -1
  95. package/docs/assets/input-number.html-a8eb0378.js +1 -1
  96. package/docs/assets/list-menu-item.html-7f1b4611.js +1 -1
  97. package/docs/assets/list-menu-item.html-84ed5ab8.js +1 -1
  98. package/docs/assets/list-menu.html-28b4163f.js +1 -1
  99. package/docs/assets/list-menu.html-cb6ba95b.js +1 -1
  100. package/docs/assets/loading.html-dae9e39d.js +6 -6
  101. package/docs/assets/loading.html-dc74c9e6.js +1 -1
  102. package/docs/assets/notify.html-e6c4c514.js +1 -1
  103. package/docs/assets/notify.html-f2c4d914.js +8 -8
  104. package/docs/assets/power-page.html-32e02f82.js +1 -1
  105. package/docs/assets/power-page.html-485e77da.js +1 -1
  106. package/docs/assets/power.html-d258cc19.js +93 -93
  107. package/docs/assets/power.html-e490bd32.js +1 -1
  108. package/docs/assets/previewImage.html-6a6b4245.js +1 -1
  109. package/docs/assets/previewImage.html-c5b7e945.js +2 -2
  110. package/docs/assets/price.html-1882c548.js +19 -19
  111. package/docs/assets/price.html-94d3f5be.js +1 -1
  112. package/docs/assets/price.html-d213df0f.js +1 -1
  113. package/docs/assets/price.html-deaf880f.js +1 -1
  114. package/docs/assets/render.html-8efcbdd4.js +1 -1
  115. package/docs/assets/render.html-df228e38.js +1 -1
  116. package/docs/assets/rule.html-2cd57fc2.js +13 -13
  117. package/docs/assets/rule.html-61662001.js +1 -1
  118. package/docs/assets/ruleValid.html-04fe2552.js +1 -1
  119. package/docs/assets/ruleValid.html-e0a776af.js +14 -14
  120. package/docs/assets/search-0782d0d1.svg +1 -1
  121. package/docs/assets/search-item.html-3f75394c.js +1 -1
  122. package/docs/assets/search-item.html-4e942ecd.js +1 -1
  123. package/docs/assets/search.html-2807043e.js +1 -1
  124. package/docs/assets/search.html-c24f8806.js +1 -1
  125. package/docs/assets/select.html-00d0607c.js +1 -1
  126. package/docs/assets/select.html-de7731f5.js +1 -1
  127. package/docs/assets/splitter.html-56f51a70.js +1 -1
  128. package/docs/assets/splitter.html-f5c836d7.js +1 -1
  129. package/docs/assets/style-161e43ab.css +1 -1
  130. package/docs/assets/symbols.html-a6aea4bf.js +1 -1
  131. package/docs/assets/symbols.html-b1f65bad.js +21 -21
  132. package/docs/assets/table-column-fixed.html-3a69e7b2.js +1 -1
  133. package/docs/assets/table-column-fixed.html-e763c38b.js +1 -1
  134. package/docs/assets/table-pagination.html-236934d3.js +1 -1
  135. package/docs/assets/table-pagination.html-c37ee2ac.js +1 -1
  136. package/docs/assets/table-splitter.html-07eab15c.js +1 -1
  137. package/docs/assets/table-splitter.html-7670ee65.js +1 -1
  138. package/docs/assets/table-summary.html-04db434f.js +1 -1
  139. package/docs/assets/table-summary.html-943c65a0.js +1 -1
  140. package/docs/assets/table.html-36253ad7.js +1 -1
  141. package/docs/assets/table.html-7f9c5d1b.js +38 -38
  142. package/docs/assets/table.html-93d53dc8.js +1 -1
  143. package/docs/assets/table.html-ac99b9cb.js +1 -1
  144. package/docs/assets/thumbnail.html-bab1976b.js +1 -1
  145. package/docs/assets/thumbnail.html-eb64e5e8.js +1 -1
  146. package/docs/assets/timestamp.html-4e54f79b.js +13 -13
  147. package/docs/assets/timestamp.html-d0e1b88a.js +1 -1
  148. package/docs/assets/toast.html-58ecbe21.js +1 -1
  149. package/docs/assets/toast.html-c9b9d36b.js +6 -6
  150. package/docs/assets/toolbar.html-83d9f97c.js +1 -1
  151. package/docs/assets/toolbar.html-ff7b8c92.js +1 -1
  152. package/docs/assets/tree.html-d07cbe79.js +23 -23
  153. package/docs/assets/tree.html-ea04193e.js +1 -1
  154. package/docs/assets/uploader-query.html-05590718.js +1 -1
  155. package/docs/assets/uploader-query.html-3175bac5.js +1 -1
  156. package/docs/assets/uploader.html-36da4394.js +2 -2
  157. package/docs/assets/uploader.html-6b5f3079.js +1 -1
  158. package/docs/assets/uploader.html-b9340b57.js +1 -1
  159. package/docs/assets/uploader.html-bc1c22e3.js +1 -1
  160. package/docs/assets/value-format.html-8ae3d47d.js +1 -1
  161. package/docs/assets/value-format.html-afa99b3d.js +1 -1
  162. package/docs/components/column-title.html +35 -35
  163. package/docs/components/data.html +62 -62
  164. package/docs/components/dialog.html +33 -33
  165. package/docs/components/dragger.html +33 -33
  166. package/docs/components/editor-code.html +33 -33
  167. package/docs/components/empty.html +33 -33
  168. package/docs/components/field-date.html +33 -33
  169. package/docs/components/field-table.html +33 -33
  170. package/docs/components/field-text.html +33 -33
  171. package/docs/components/field-tree.html +33 -33
  172. package/docs/components/img.html +33 -33
  173. package/docs/components/input-number.html +33 -33
  174. package/docs/components/list-menu-item.html +33 -33
  175. package/docs/components/list-menu.html +33 -33
  176. package/docs/components/power-page.html +33 -33
  177. package/docs/components/price.html +33 -33
  178. package/docs/components/render.html +33 -33
  179. package/docs/components/search-item.html +33 -33
  180. package/docs/components/search.html +33 -33
  181. package/docs/components/select.html +33 -33
  182. package/docs/components/splitter.html +33 -33
  183. package/docs/components/table-column-fixed.html +33 -33
  184. package/docs/components/table-pagination.html +33 -33
  185. package/docs/components/table-splitter.html +33 -33
  186. package/docs/components/table-summary.html +33 -33
  187. package/docs/components/table.html +33 -33
  188. package/docs/components/thumbnail.html +33 -33
  189. package/docs/components/toolbar.html +33 -33
  190. package/docs/components/uploader-query.html +33 -33
  191. package/docs/components/uploader.html +33 -33
  192. package/docs/components/value-format.html +33 -33
  193. package/docs/index.html +33 -33
  194. package/docs/utils/alert.html +37 -37
  195. package/docs/utils/area.html +74 -74
  196. package/docs/utils/arr.html +43 -43
  197. package/docs/utils/auth.html +40 -40
  198. package/docs/utils/bus.html +38 -38
  199. package/docs/utils/confirm.html +42 -42
  200. package/docs/utils/copy.html +45 -45
  201. package/docs/utils/dialog.html +100 -100
  202. package/docs/utils/dict.html +55 -55
  203. package/docs/utils/dictOptions.html +37 -37
  204. package/docs/utils/form.html +34 -34
  205. package/docs/utils/getData.html +66 -66
  206. package/docs/utils/getFile.html +35 -35
  207. package/docs/utils/getImage.html +35 -35
  208. package/docs/utils/getTime.html +52 -52
  209. package/docs/utils/index.html +33 -33
  210. package/docs/utils/loading.html +38 -38
  211. package/docs/utils/notify.html +40 -40
  212. package/docs/utils/power.html +125 -125
  213. package/docs/utils/previewImage.html +34 -34
  214. package/docs/utils/price.html +51 -51
  215. package/docs/utils/rule.html +45 -45
  216. package/docs/utils/ruleValid.html +46 -46
  217. package/docs/utils/symbols.html +53 -53
  218. package/docs/utils/table.html +70 -70
  219. package/docs/utils/timestamp.html +45 -45
  220. package/docs/utils/toast.html +38 -38
  221. package/docs/utils/tree.html +55 -55
  222. package/docs/utils/uploader.html +34 -34
  223. package/package.json +24 -24
  224. package/sass/common.scss +179 -179
  225. package/sass/index.scss +13 -13
  226. package/sass/quasar/field.scss +250 -250
  227. package/sass/quasar/table.scss +168 -168
  228. package/sass/variables.scss +140 -140
  229. package/utils/$form.js +72 -72
  230. package/utils/$power.js +1411 -1411
  231. package/utils/$render.js +75 -75
  232. package/utils/$search.js +416 -416
  233. package/utils/$table.js +1193 -1193
  234. package/utils/$tree.js +664 -664
  235. package/utils/config.js +57 -55
  236. package/utils/dialog.js +36 -36
  237. package/utils/dict.js +21 -21
  238. package/utils/getFile.js +41 -41
  239. package/utils/getImage.js +176 -176
  240. package/utils/getTime.js +113 -113
  241. package/utils/index.js +65 -65
  242. package/utils/previewImage.js +14 -14
  243. package/utils/timestamp.js +18 -18
  244. package/utils/uploader/qiniu.js +320 -320
  245. package/utils/uploader.js +1625 -1488
  246. package/utils/useFileUrl.js +25 -25
  247. package/utils/useSearch.js +499 -499
@@ -1,840 +1,883 @@
1
- <template>
2
- <div class="n-uploader-query">
3
-
4
- <!-- 上传按钮 -->
5
- <slot
6
- name="button"
7
- :disable="disable || readonly"
8
- :size="currentSize"
9
- v-if="$slots.button"
10
- />
11
- <div
12
- class="n-uploader-query__button--button"
13
- v-else-if="! noButton && currentButtonType === 'button'"
14
- >
15
- <q-btn
16
- class="n-button-icon"
17
- :label="buttonText || '上传'"
18
- @click="uploader.chooseUpload"
19
- color="primary"
20
- outline
21
- :disable="disable || readonly"
22
- unelevated
23
- v-bind="buttonProps"
24
- />
25
- </div>
26
-
27
- <!-- 拖拽器 -->
28
- <n-dragger
29
- class="n-uploader-query__query row q-gutter-sm"
30
- v-model="query"
31
- :drag="currentDrag"
32
- @update:model-value="uploader.updateValue"
33
- v-slot="{ mousedown, fromIndex, dragStart, dragEnter, dragEnd }"
34
- >
35
- <!-- 上传图片队列 -->
36
- <template v-if="type === 'image'">
37
-
38
- <!-- 左边方块按钮 -->
39
- <template v-if="! disable && ! readonly && ! rightSquareButton">
40
- <slot
41
- name="square-button"
42
- :size="currentSize"
43
- :show="showSquareButton"
44
- v-if="$slots['square-button']"
45
- />
46
- <div
47
- class="n-uploader-query__button--square cursor-pointer"
48
- :style="{
49
- width: toPx(currentSize),
50
- height: toPx(currentSize),
51
- }"
52
- @click="uploader.chooseUpload"
53
- v-show="showSquareButton"
54
- v-else-if="! noButton && currentButtonType === 'square'"
55
- >
56
- <q-icon
57
- name="add"
58
- :size="toPx(currentSize / 2)"
59
- />
60
- <div class="n-uploader-query__button--square__text" v-if="buttonText">{{buttonText}}</div>
61
- </div>
62
- </template>
63
-
64
- <!-- 单个图片 -->
65
- <div
66
- v-for="(fileItem, fileItemIndex) in query"
67
- :key="`query-item-${fileItem.id}`"
68
- class="n-uploader-query__item n-uploader-query__item--image"
69
- :class="{
70
- ghost: fileItemIndex === fromIndex,
71
- }"
72
- :draggable="currentDrag"
73
- @mousedown.self="mousedown($event, fileItemIndex)"
74
- @mouseup="dragEnd"
75
- @dragstart="dragStart($event, fileItemIndex)"
76
- @dragenter="dragEnter($event, fileItemIndex)"
77
- @dragend="dragEnd"
78
- >
79
- <n-img
80
- :src="currentQuery[fileItemIndex].src"
81
- :spinner-size="toPx(currentSize / 2)"
82
- :width="toPx(currentSize)"
83
- :height="toPx(currentSize)"
84
- fit="fill"
85
- >
86
- <!-- 如果是外链 -->
87
- <span class="n-uploader-query__item__net" v-if="fileItem.isNet && ! fileItem.isNetUploaded">链接</span>
88
-
89
- <!-- 内容 -->
90
- <div
91
- class="n-uploader-query__item__inner absolute-full flex flex-center no-padding transparent"
92
- :class="{
93
- 'transparent': fileItem.status < UPLOAD_STATUS.success,
94
- }"
95
- v-if="fileItem.status !== UPLOAD_STATUS.success"
96
- >
97
- <!-- 如果上传失败 -->
98
- <div
99
- class="n-uploader-query__item__inner__msg n-uploader-query__item__inner__msg--error"
100
- v-if="fileItem.status === UPLOAD_STATUS.fail"
101
- >{{fileItem.msg}}</div>
102
-
103
- <!-- 上传中前 -->
104
- <q-circular-progress
105
- indeterminate
106
- rounded
107
- :size="toPx(currentSize / 1.5)"
108
- :thickness="0.14"
109
- color="white"
110
- v-if="fileItem.status < UPLOAD_STATUS.uploading"
111
- />
112
-
113
- <!-- 上传中 -->
114
- <q-circular-progress
115
- :value="fileItem.progress"
116
- :size="toPx(currentSize / 1.5)"
117
- :thickness="0.14"
118
- color="white"
119
- track-color="grey-5"
120
- show-value
121
- v-else-if="fileItem.status === UPLOAD_STATUS.uploading"
122
- >
123
- <q-icon
124
- class="cursor-pointer"
125
- name="pause"
126
- :size="toPx(currentSize / 3)"
127
- @click="uploader.deleteFileItem(fileItem)"
128
- />
129
- </q-circular-progress>
130
- </div>
131
-
132
- <!-- 操作 -->
133
- <div
134
- class="n-uploader-query__item__settings transparent no-padding"
135
- v-if="fileItem.status !== UPLOAD_STATUS.uploading"
136
- >
137
- <!-- 操作插槽-->
138
- <slot
139
- name="settings"
140
- :file="fileItem"
141
- />
142
-
143
- <!-- 预览 -->
144
- <q-icon
145
- class="n-uploader-query__item__settings__icon cursor-pointer"
146
- name="search"
147
- :size="settingsIconSize"
148
- title="预览"
149
- @click="previewImage(fileItemIndex)"
150
- v-bind="settingsIconProps"
151
- v-if="! noPreview && currentQuery[fileItemIndex].preview_src"
152
- />
153
-
154
- <!-- 删除 -->
155
- <q-icon
156
- class="n-uploader-query__item__settings__icon cursor-pointer"
157
- name="close"
158
- :size="settingsIconSize"
159
- title="删除"
160
- @click="uploader.deleteFileItem(fileItem)"
161
- v-bind="settingsIconProps"
162
- v-if="! noDelete && ! disable && ! readonly"
163
- />
164
-
165
- </div>
166
-
167
- </n-img>
168
- </div>
169
-
170
- <!-- 右边方块按钮 -->
171
- <template v-if="! disable && ! readonly && rightSquareButton">
172
- <slot
173
- name="square-button"
174
- :size="currentSize"
175
- :show="showSquareButton"
176
- v-if="$slots['square-button']"
177
- />
178
- <div
179
- class="n-uploader-query__button--square cursor-pointer"
180
- :style="{
181
- width: toPx(currentSize),
182
- height: toPx(currentSize),
183
- }"
184
- @click="uploader.chooseUpload"
185
- v-show="showSquareButton"
186
- v-else-if="! noButton && currentButtonType === 'square'"
187
- >
188
- <q-icon
189
- name="add"
190
- :size="toPx(currentSize / 2)"
191
- />
192
- <div class="n-uploader-query__button--square__text" v-if="buttonText">{{buttonText}}</div>
193
- </div>
194
- </template>
195
- </template>
196
-
197
- <!-- 上传文件队列 -->
198
- <template v-else>
199
-
200
- <!-- 单个文件 -->
201
- <div
202
- v-for="(fileItem, fileItemIndex) in query"
203
- :key="`query-item-${fileItem.id}`"
204
- class="n-uploader-query__item n-uploader-query__item--file"
205
- :class="{
206
- ghost: fileItemIndex === fromIndex,
207
- }"
208
- :style="{
209
- height: toPx(currentSize),
210
- }"
211
- :draggable="currentDrag"
212
- @mousedown.self="mousedown($event, fileItemIndex)"
213
- @mouseup="dragEnd"
214
- @dragstart="dragStart($event, fileItemIndex)"
215
- @dragenter="dragEnter($event, fileItemIndex)"
216
- @dragend="dragEnd"
217
- >
218
- <!-- 如果是外链 -->
219
- <span class="n-uploader-query__item__net" v-if="fileItem.isNet && ! fileItem.isNetUploaded">链接</span>
220
-
221
- <!-- 图标 -->
222
- <div
223
- class="n-uploader-query__item__icon"
224
- :style="{
225
- width: toPx(currentSize),
226
- height: toPx(currentSize),
227
- }"
228
- >
229
- <!-- 上传中前 -->
230
- <q-circular-progress
231
- class="n-uploader-query__item__icon__icon"
232
- indeterminate
233
- rounded
234
- :size="toPx(currentSize / 1.8)"
235
- :thickness="0.18"
236
- v-if="fileItem.status < UPLOAD_STATUS.uploading"
237
- />
238
-
239
- <!-- 上传中 -->
240
- <q-circular-progress
241
- class="n-uploader-query__item__icon__icon"
242
- :value="fileItem.progress"
243
- :size="toPx(currentSize / 1.8)"
244
- :thickness="0.18"
245
- show-value
246
- v-else-if="fileItem.status === UPLOAD_STATUS.uploading"
247
- >
248
- <q-icon
249
- class="cursor-pointer"
250
- name="pause"
251
- :size="toPx(currentSize / 3)"
252
- @click="uploader.deleteFileItem(fileItem)"
253
- />
254
- </q-circular-progress>
255
-
256
- <!-- 文件图标 -->
257
- <q-icon
258
- class="n-uploader-query__item__icon__icon"
259
- name="description"
260
- :size="toPx(currentSize / 1.5)"
261
- v-else-if="type === 'file'"
262
- />
263
-
264
- <!-- 播放图标 -->
265
- <q-icon
266
- class="n-uploader-query__item__icon__icon cursor-pointer"
267
- name="play_circle"
268
- title="播放"
269
- :size="toPx(currentSize / 1.5)"
270
- @click="uploader.play(fileItem)"
271
- v-else
272
- />
273
- </div>
274
-
275
- <!-- 信息 -->
276
- <div class="n-uploader-query__item__info">
277
- <!-- 标题 -->
278
- <div class="n-uploader-query__item__info__title ellipsis">{{getFileName(fileItem)}}</div>
279
- <!-- 错误提示 -->
280
- <div class="n-uploader-query__item__info__msg--error" v-if="fileItem.status === UPLOAD_STATUS.fail">{{fileItem.msg}}</div>
281
- </div>
282
-
283
- <!-- 操作 -->
284
- <div class="n-uploader-query__item__settings">
285
-
286
- <!-- 操作插槽-->
287
- <slot
288
- name="settings"
289
- :file="fileItem"
290
- />
291
-
292
- <template v-if="fileItem.status === UPLOAD_STATUS.success">
293
-
294
- <!-- 复制地址 -->
295
- <q-icon
296
- class="n-uploader-query__item__settings__icon cursor-pointer"
297
- name="content_copy"
298
- color="white"
299
- :size="settingsIconSize"
300
- title="复制地址"
301
- @click="uploader.copyUrl(fileItem)"
302
- v-bind="settingsIconProps"
303
- />
304
-
305
- <!-- 修改 -->
306
- <q-icon
307
- class="n-uploader-query__item__settings__icon cursor-pointer"
308
- name="edit"
309
- color="white"
310
- :size="settingsIconSize"
311
- title="修改"
312
- v-bind="settingsIconProps"
313
- v-if="! noEdit && ! disable && ! readonly && ! fileItem.isNet"
314
- >
315
- <q-popup-edit
316
- :model-value="fileItem.title"
317
- buttons
318
- label-set="保存"
319
- @save="uploader.editFileTitle($event, fileItem)"
320
- v-slot="scope"
321
- >
322
- <q-input
323
- v-model="scope.value"
324
- dense
325
- autofocus
326
- counter
327
- :maxlength="50"
328
- @keyup.enter="scope.set"
329
- >
330
- <template v-slot:append>
331
- <span class="text-subtitle2 text-weight-bold">.{{fileItem.ext}}</span>
332
- </template>
333
- </q-input>
334
- </q-popup-edit>
335
- </q-icon>
336
- </template>
337
-
338
- <!-- 删除 -->
339
- <q-icon
340
- class="n-uploader-query__item__settings__icon cursor-pointer"
341
- name="close"
342
- color="white"
343
- :size="settingsIconSize"
344
- title="删除"
345
- @click="uploader.deleteFileItem(fileItem)"
346
- v-bind="settingsIconProps"
347
- v-if="! noDelete && ! disable && ! readonly"
348
- />
349
- </div>
350
- </div>
351
- </template>
352
- </n-dragger>
353
- </div>
354
- </template>
355
-
356
- <script>
357
- import { computed, inject } from 'vue'
358
- import { useQuasar } from 'quasar'
359
-
360
- import $n_has from 'lodash/has'
361
- import $n_get from 'lodash/get'
362
-
363
- import $n_px from '@netang/utils/px'
364
- import $n_forEach from '@netang/utils/forEach'
365
- import $n_isValidArray from '@netang/utils/isValidArray'
366
- import $n_isValidString from '@netang/utils/isValidString'
367
-
368
- import $n_getImage from '../../utils/getImage'
369
- import $n_previewImage from '../../utils/previewImage'
370
-
371
- import NDragger from '../dragger'
372
-
373
- import { NUploaderKey } from '../../utils/symbols'
374
-
375
- import {
376
- // 上传状态
377
- UPLOAD_STATUS,
378
- } from '../../utils/useUploader'
379
-
380
- export default {
381
-
382
- /**
383
- * 标识
384
- */
385
- name: 'NUploaderQuery',
386
-
387
- /**
388
- * 组件
389
- */
390
- components: {
391
- NDragger,
392
- },
393
-
394
- /**
395
- * 声明属性
396
- */
397
- props: {
398
- // 按钮类型, 可选值 square button
399
- buttonType: {
400
- type: String,
401
- validator: v => [ 'square', 'button' ].includes(v),
402
- },
403
- // 按钮文字
404
- buttonText: String,
405
- // 按钮声明属性
406
- buttonProps: Object,
407
- // 图片/按钮/文件 尺寸
408
- size: Number,
409
- // 是否开启拖拽
410
- drag: {
411
- type: Boolean,
412
- default: true,
413
- },
414
- // 是否禁用
415
- disable: Boolean,
416
- // 是否只读
417
- readonly: Boolean,
418
- // 是否隐藏按钮
419
- noButton: Boolean,
420
- // 是否隐藏预览按钮
421
- noPreview: Boolean,
422
- // 是否隐藏修改按钮
423
- noEdit: Boolean,
424
- // 是否隐藏删除按钮
425
- noDelete: Boolean,
426
- // 自动显示方块按钮
427
- autoShowSquareButton: Boolean,
428
- // 方块按钮在右边显示
429
- rightSquareButton: Boolean,
430
- // 设置图标尺寸
431
- settingsIconSize: {
432
- type: String,
433
- default: 'xs',
434
- },
435
- // 设置图标传参
436
- settingsIconProps: Object,
437
- },
438
-
439
- /**
440
- * 声明事件
441
- */
442
- emits: [
443
- 'update:modelValue',
444
- ],
445
-
446
- /**
447
- * 组合式
448
- */
449
- setup(props) {
450
-
451
- // ==========【数据】============================================================================================
452
-
453
- // quasar 对象
454
- const $q = useQuasar()
455
-
456
- // 获取上传器注入数据
457
- const {
458
- // 声明属性
459
- props: uploaderProps,
460
- // 上传器
461
- uploader,
462
- // 文件队列
463
- query,
464
- } = inject(NUploaderKey)
465
-
466
- // ==========【计算属性】=========================================================================================
467
-
468
- /**
469
- * 当前上传文件队列
470
- */
471
- const currentQuery = computed(function () {
472
-
473
- // 如果不是图片
474
- if (uploaderProps.type !== 'image') {
475
- if ($n_isValidArray(query.value)) {
476
- return query.value
477
- }
478
- return []
479
- }
480
-
481
- const lists = []
482
-
483
- $n_forEach(query.value, function (fileItem) {
484
- const newItem = Object.assign({}, fileItem)
485
-
486
- let src = ''
487
- let preview_src = ''
488
-
489
- if ($n_has(fileItem, '__img')) {
490
- src = fileItem.__img
491
- preview_src = src
492
- } else if ($n_isValidString(fileItem.hash)) {
493
- src = $n_getImage(fileItem.hash, { w: $q.platform.is.mobile ? currentSize.value * 2 : currentSize.value })
494
- if (src) {
495
- // 预览地址
496
- preview_src = fileItem.hash
497
- }
498
- }
499
-
500
- lists.push(Object.assign(newItem, {
501
- // 图片地址
502
- src,
503
- // 预览地址
504
- preview_src,
505
- }))
506
- })
507
-
508
- return lists
509
- })
510
-
511
-
512
- /**
513
- * 当前是否开启拖拽
514
- */
515
- const currentDrag = computed(function() {
516
- return props.drag
517
- && query.value.length > 1
518
- && ! props.readonly
519
- && ! props.disable
520
- })
521
-
522
- /**
523
- * 当前按钮类型
524
- */
525
- const currentButtonType = computed(function () {
526
- if (props.buttonType) {
527
- return props.buttonType
528
- }
529
- return uploaderProps.type === 'image' ? 'square' : 'button'
530
- })
531
-
532
- /**
533
- * 当前尺寸
534
- */
535
- const currentSize = computed(function () {
536
- if (props.size) {
537
- return props.size
538
- }
539
- return uploaderProps.type === 'image' ? 70 : 50
540
- })
541
-
542
- /**
543
- * 是否显示方块按钮
544
- */
545
- const showSquareButton = computed(function () {
546
- // 自动显示方块按钮 && 有上传文件限制数量
547
- return props.autoShowSquareButton && uploaderProps.count > 0 ?
548
- // 如果 当前上传文件队列数量 < 上传文件限制数量
549
- currentQuery.value.length < uploaderProps.count
550
- // 始终显示
551
- : true
552
- })
553
-
554
- // ==========【方法】=============================================================================================
555
-
556
- /**
557
- * 预览图片
558
- */
559
- function previewImage(startPosition) {
560
- $n_previewImage({
561
- // 需要预览的图片 URL 数组
562
- images: currentQuery.value.map(e => e.preview_src),
563
- // 图片预览起始位置索引
564
- startPosition,
565
- })
566
- }
567
-
568
- /**
569
- * 获取文件名称
570
- */
571
- function getFileName(fileItem) {
572
- return fileItem.title + ($n_get(fileItem, 'ext') ? '.' + fileItem.ext : '')
573
- }
574
-
575
-
576
- // ==========【返回】=============================================================================================
577
-
578
- return {
579
- // 上传状态
580
- UPLOAD_STATUS,
581
- // 上传文件类型, 可选值 file image video audio
582
- type: uploaderProps.type,
583
- // 上传文件数量(0:不限)
584
- count: uploaderProps.count,
585
- // 文件队列
586
- query,
587
- // 当前上传文件队列
588
- currentQuery,
589
-
590
- // 当前是否开启拖拽
591
- currentDrag,
592
- // 当前按钮类型
593
- currentButtonType,
594
- // 当前尺寸
595
- currentSize,
596
- // 是否显示方块按钮
597
- showSquareButton,
598
-
599
- // 上传器
600
- uploader,
601
-
602
- // 预览图片
603
- previewImage,
604
- // 获取文件名称
605
- getFileName,
606
-
607
- toPx: $n_px,
608
- }
609
- },
610
- }
611
- </script>
612
-
613
- <style lang="scss">
614
- @import "@/assets/sass/variables.scss";
615
-
616
- // 上传器队列
617
- .n-uploader-query {
618
-
619
- // 上传按钮
620
- &__button {
621
-
622
- // 方块
623
- &--square {
624
- display: inline-flex;
625
- vertical-align: middle;
626
- overflow: hidden;
627
- border: 1px dashed rgba(var(--n-reverse-color-rgb), 0.2);
628
- flex-direction: column;
629
- justify-content: center;
630
- align-items: center;
631
- color: rgba(var(--n-reverse-color-rgb), 0.4);
632
- border-radius: 4px;
633
-
634
- &:hover {
635
- border-color: $primary;
636
- }
637
-
638
- // 文字
639
- &__text {
640
- font-size: 12px;
641
- }
642
- }
643
-
644
- // 按钮
645
- &--button {
646
- + .n-uploader-query__query {
647
- margin-top: 0;
648
- }
649
- }
650
- }
651
-
652
- // 上传单个文件
653
- &__item {
654
- position: relative;
655
-
656
- // 开启拖拽
657
- &[draggable="true"] {
658
- cursor: move;
659
- }
660
-
661
- // 当前拖拽占位元素
662
- &.ghost {
663
- &:after {
664
- content: "";
665
- position: absolute;
666
- top: 0;
667
- left: 0;
668
- right: 0;
669
- bottom: 0;
670
- border: 2px dashed mix(#ffffff, $primary, 40%);
671
- border-radius: 4px;
672
- background-color: rgba(255, 255, 255, 0.75);
673
- }
674
-
675
- .n-uploader-query__item__inner,
676
- .n-uploader-query__item__settings {
677
- display: none;
678
- }
679
- }
680
-
681
- &:hover {
682
- .n-uploader-query__item__settings {
683
- visibility: visible;
684
- }
685
- }
686
-
687
- // 单个图片
688
- &--image {
689
- border-radius: 4px;
690
- background-color: rgba(0,0,0,0.1);
691
- overflow: hidden;
692
- }
693
-
694
- // 单个文件
695
- &--file {
696
- position: relative;
697
- width: 300px;
698
- display: flex;
699
- flex-direction: row;
700
- align-items: center;
701
- border-radius: 4px;
702
- color: rgba(var(--n-reverse-color-rgb), 0.8);
703
- background-color: rgba(var(--n-reverse-color-rgb), 0.05);
704
-
705
- // 图标
706
- .n-uploader-query__item__icon {
707
- position: relative;
708
- display: flex;
709
- align-items: center;
710
- justify-content: center;
711
- z-index: 1;
712
-
713
- &__icon {
714
- color: rgba(var(--n-reverse-color-rgb), 0.2);
715
- }
716
- }
717
-
718
- // 信息
719
- .n-uploader-query__item__info {
720
- display: flex;
721
- flex-direction: column;
722
- line-height: 18px;
723
-
724
- &__title {
725
- max-width: 150px;
726
- }
727
-
728
- &__msg--error {
729
- color: $negative;
730
- }
731
- }
732
- }
733
-
734
- // 外链
735
- &__net {
736
- position: absolute;
737
- bottom: -1px;
738
- right: -1px;
739
- color: #ffffff;
740
- padding: 1px 3px;
741
- border-radius: 3px;
742
- background-color: var(--q-primary);
743
- transform: scale(0.7);
744
- pointer-events: none;
745
- }
746
-
747
- //操作
748
- &__settings {
749
- position: absolute;
750
- top: 5px;
751
- right: 5px;
752
- visibility: hidden;
753
-
754
- &__icon {
755
- background-color: rgba(0,0,0,0.5) !important;
756
- border-radius: 50%;
757
- padding: 5px;
758
-
759
- + .n-uploader-query__item__settings__icon {
760
- margin-left: 4px;
761
- }
762
-
763
- &:hover {
764
- background-color: rgba(0,0,0,0.8);
765
- }
766
- }
767
- }
768
-
769
- // 内容
770
- &__inner {
771
-
772
- &__msg {
773
- margin: 3px;
774
- padding: 2px 3px;
775
- line-height: 18px;
776
- font-size: 12px;
777
- background-color: rgba(0,0,0,0.6);
778
- border-radius: 6px;
779
-
780
- &--error {
781
- background-color: $warning;
782
- }
783
- }
784
- }
785
- }
786
- }
787
-
788
- @media (max-width: $breakpoint-xs-max){
789
- // 上传器队列
790
- .n-uploader-query {
791
- // 上传单个文件
792
- &__item {
793
- // 单个文件
794
- &--file {
795
- width: 100%;
796
- }
797
- }
798
-
799
- // 信息
800
- .n-uploader-query__item__info {
801
- &__title {
802
- max-width: 200px;
803
- }
804
- }
805
- }
806
- }
807
-
808
- /**
809
- * 手机版
810
- */
811
- body.mobile {
812
- // 上传器队列
813
- .n-uploader-query {
814
- // 上传单个文件
815
- &__item {
816
- &__settings {
817
- visibility: visible;
818
- }
819
- }
820
- }
821
- }
822
-
823
- /**
824
- * 暗黑
825
- */
826
- .body--dark {
827
- .n-uploader-query__item--file {
828
- .n-uploader-query__item__settings {
829
- // 图标
830
- &__icon {
831
- background-color: rgba(255,255,255, 0.1);
832
-
833
- &:hover {
834
- background-color: rgba(255,255,255, 0.2);
835
- }
836
- }
837
- }
838
- }
839
- }
840
- </style>
1
+ <template>
2
+ <div class="n-uploader-query">
3
+
4
+ <!-- 上传按钮 -->
5
+ <slot
6
+ name="button"
7
+ :disable="disable || readonly"
8
+ :size="currentSize"
9
+ v-if="$slots.button"
10
+ />
11
+ <div
12
+ class="n-uploader-query__button--button"
13
+ v-else-if="! noButton && currentButtonType === 'button'"
14
+ >
15
+ <!-- 按钮组-->
16
+ <q-btn-group outline>
17
+ <!-- 上传本地图片 -->
18
+ <q-btn
19
+ class="n-button-icon"
20
+ :label="buttonText || '上传'"
21
+ @click="uploader.chooseUpload"
22
+ color="primary"
23
+ outline
24
+ :disable="disable || readonly"
25
+ unelevated
26
+ v-bind="buttonProps"
27
+ />
28
+ <!-- 上传网络图片 -->
29
+ <q-btn
30
+ class="n-button-icon q-px-sm"
31
+ icon="cloud_upload"
32
+ @click="uploader.chooseUploadNet"
33
+ color="primary"
34
+ outline
35
+ :disable="disable || readonly"
36
+ unelevated
37
+ v-bind="buttonProps"
38
+ v-if="showUploadNetButton"
39
+ />
40
+ </q-btn-group>
41
+ </div>
42
+
43
+ <!-- 拖拽器 -->
44
+ <n-dragger
45
+ class="n-uploader-query__query row q-gutter-sm"
46
+ v-model="query"
47
+ :drag="currentDrag"
48
+ @update:model-value="uploader.updateValue"
49
+ v-slot="{ mousedown, fromIndex, dragStart, dragEnter, dragEnd }"
50
+ >
51
+ <!-- 上传图片队列 -->
52
+ <template v-if="type === 'image'">
53
+
54
+ <!-- 左边方块按钮 -->
55
+ <template v-if="! disable && ! readonly && ! rightSquareButton">
56
+ <slot
57
+ name="square-button"
58
+ :size="currentSize"
59
+ :show="showSquareButton"
60
+ v-if="$slots['square-button']"
61
+ />
62
+ <div
63
+ class="n-uploader-query__button--square"
64
+ :style="{
65
+ width: toPx(currentSize),
66
+ height: toPx(currentSize),
67
+ }"
68
+ v-show="showSquareButton"
69
+ v-else-if="! noButton && currentButtonType === 'square'"
70
+ >
71
+ <!-- 上传本地图片 -->
72
+ <div
73
+ class="n-uploader-query__button--square-button cursor-pointer"
74
+ @click="uploader.chooseUpload"
75
+ >
76
+ <q-icon
77
+ name="add"
78
+ :size="toPx(currentSize / 2)"
79
+ />
80
+ <div class="n-uploader-query__button--square-button__text" v-if="buttonText">{{buttonText}}</div>
81
+ </div>
82
+
83
+ <!-- 上传网络图片 -->
84
+ <div
85
+ class="n-uploader-query__button--square-button q-mt-xs cursor-pointer"
86
+ @click="uploader.chooseUploadNet"
87
+ v-if="showUploadNetButton"
88
+ >
89
+ <q-icon
90
+ name="cloud_upload"
91
+ :size="toPx(currentSize / 3)"
92
+ />
93
+ <div class="n-uploader-query__button--square-button__text" v-if="buttonText">{{buttonText}}</div>
94
+ </div>
95
+ </div>
96
+ </template>
97
+
98
+ <!-- 单个图片 -->
99
+ <div
100
+ v-for="(fileItem, fileItemIndex) in query"
101
+ :key="`query-item-${fileItem.id}`"
102
+ class="n-uploader-query__item n-uploader-query__item--image"
103
+ :class="{
104
+ ghost: fileItemIndex === fromIndex,
105
+ }"
106
+ :draggable="currentDrag"
107
+ @mousedown.self="mousedown($event, fileItemIndex)"
108
+ @mouseup="dragEnd"
109
+ @dragstart="dragStart($event, fileItemIndex)"
110
+ @dragenter="dragEnter($event, fileItemIndex)"
111
+ @dragend="dragEnd"
112
+ >
113
+ <n-img
114
+ :src="currentQuery[fileItemIndex].src"
115
+ :spinner-size="toPx(currentSize / 2)"
116
+ :width="toPx(currentSize)"
117
+ :height="toPx(currentSize)"
118
+ fit="fill"
119
+ >
120
+ <!-- 如果是外链 -->
121
+ <span class="n-uploader-query__item__net" v-if="fileItem.isNet && ! fileItem.isNetUploaded">链接</span>
122
+
123
+ <!-- 内容 -->
124
+ <div
125
+ class="n-uploader-query__item__inner absolute-full flex flex-center no-padding transparent"
126
+ :class="{
127
+ 'transparent': fileItem.status < UPLOAD_STATUS.success,
128
+ }"
129
+ v-if="fileItem.status !== UPLOAD_STATUS.success"
130
+ >
131
+ <!-- 如果上传失败 -->
132
+ <div
133
+ class="n-uploader-query__item__inner__msg n-uploader-query__item__inner__msg--error"
134
+ v-if="fileItem.status === UPLOAD_STATUS.fail"
135
+ >{{fileItem.msg}}</div>
136
+
137
+ <!-- 上传中前 -->
138
+ <q-circular-progress
139
+ indeterminate
140
+ rounded
141
+ :size="toPx(currentSize / 1.5)"
142
+ :thickness="0.14"
143
+ color="white"
144
+ v-if="fileItem.status < UPLOAD_STATUS.uploading"
145
+ />
146
+
147
+ <!-- 上传中 -->
148
+ <q-circular-progress
149
+ :value="fileItem.progress"
150
+ :size="toPx(currentSize / 1.5)"
151
+ :thickness="0.14"
152
+ color="white"
153
+ track-color="grey-5"
154
+ show-value
155
+ v-else-if="fileItem.status === UPLOAD_STATUS.uploading"
156
+ >
157
+ <q-icon
158
+ class="cursor-pointer"
159
+ name="pause"
160
+ :size="toPx(currentSize / 3)"
161
+ @click="uploader.deleteFileItem(fileItem)"
162
+ />
163
+ </q-circular-progress>
164
+ </div>
165
+
166
+ <!-- 操作 -->
167
+ <div
168
+ class="n-uploader-query__item__settings transparent no-padding"
169
+ v-if="fileItem.status !== UPLOAD_STATUS.uploading"
170
+ >
171
+ <!-- 操作插槽-->
172
+ <slot
173
+ name="settings"
174
+ :file="fileItem"
175
+ />
176
+
177
+ <!-- 预览 -->
178
+ <q-icon
179
+ class="n-uploader-query__item__settings__icon cursor-pointer"
180
+ name="search"
181
+ :size="settingsIconSize"
182
+ title="预览"
183
+ @click="previewImage(fileItemIndex)"
184
+ v-bind="settingsIconProps"
185
+ v-if="! noPreview && currentQuery[fileItemIndex].preview_src"
186
+ />
187
+
188
+ <!-- 删除 -->
189
+ <q-icon
190
+ class="n-uploader-query__item__settings__icon cursor-pointer"
191
+ name="close"
192
+ :size="settingsIconSize"
193
+ title="删除"
194
+ @click="uploader.deleteFileItem(fileItem)"
195
+ v-bind="settingsIconProps"
196
+ v-if="! noDelete && ! disable && ! readonly"
197
+ />
198
+
199
+ </div>
200
+
201
+ </n-img>
202
+ </div>
203
+
204
+ <!-- 右边方块按钮 -->
205
+ <template v-if="! disable && ! readonly && rightSquareButton">
206
+ <slot
207
+ name="square-button"
208
+ :size="currentSize"
209
+ :show="showSquareButton"
210
+ v-if="$slots['square-button']"
211
+ />
212
+ <div
213
+ class="n-uploader-query__button--square cursor-pointer"
214
+ :style="{
215
+ width: toPx(currentSize),
216
+ height: toPx(currentSize),
217
+ }"
218
+ @click="uploader.chooseUpload"
219
+ v-show="showSquareButton"
220
+ v-else-if="! noButton && currentButtonType === 'square'"
221
+ >
222
+ <q-icon
223
+ name="add"
224
+ :size="toPx(currentSize / 2)"
225
+ />
226
+ <div class="n-uploader-query__button--square__text" v-if="buttonText">{{buttonText}}</div>
227
+ </div>
228
+ </template>
229
+ </template>
230
+
231
+ <!-- 上传文件队列 -->
232
+ <template v-else>
233
+
234
+ <!-- 单个文件 -->
235
+ <div
236
+ v-for="(fileItem, fileItemIndex) in query"
237
+ :key="`query-item-${fileItem.id}`"
238
+ class="n-uploader-query__item n-uploader-query__item--file"
239
+ :class="{
240
+ ghost: fileItemIndex === fromIndex,
241
+ }"
242
+ :style="{
243
+ height: toPx(currentSize),
244
+ }"
245
+ :draggable="currentDrag"
246
+ @mousedown.self="mousedown($event, fileItemIndex)"
247
+ @mouseup="dragEnd"
248
+ @dragstart="dragStart($event, fileItemIndex)"
249
+ @dragenter="dragEnter($event, fileItemIndex)"
250
+ @dragend="dragEnd"
251
+ >
252
+ <!-- 如果是外链 -->
253
+ <span class="n-uploader-query__item__net" v-if="fileItem.isNet && ! fileItem.isNetUploaded">链接</span>
254
+
255
+ <!-- 图标 -->
256
+ <div
257
+ class="n-uploader-query__item__icon"
258
+ :style="{
259
+ width: toPx(currentSize),
260
+ height: toPx(currentSize),
261
+ }"
262
+ >
263
+ <!-- 上传中前 -->
264
+ <q-circular-progress
265
+ class="n-uploader-query__item__icon__icon"
266
+ indeterminate
267
+ rounded
268
+ :size="toPx(currentSize / 1.8)"
269
+ :thickness="0.18"
270
+ v-if="fileItem.status < UPLOAD_STATUS.uploading"
271
+ />
272
+
273
+ <!-- 上传中 -->
274
+ <q-circular-progress
275
+ class="n-uploader-query__item__icon__icon"
276
+ :value="fileItem.progress"
277
+ :size="toPx(currentSize / 1.8)"
278
+ :thickness="0.18"
279
+ show-value
280
+ v-else-if="fileItem.status === UPLOAD_STATUS.uploading"
281
+ >
282
+ <q-icon
283
+ class="cursor-pointer"
284
+ name="pause"
285
+ :size="toPx(currentSize / 3)"
286
+ @click="uploader.deleteFileItem(fileItem)"
287
+ />
288
+ </q-circular-progress>
289
+
290
+ <!-- 文件图标 -->
291
+ <q-icon
292
+ class="n-uploader-query__item__icon__icon"
293
+ name="description"
294
+ :size="toPx(currentSize / 1.5)"
295
+ v-else-if="type === 'file'"
296
+ />
297
+
298
+ <!-- 播放图标 -->
299
+ <q-icon
300
+ class="n-uploader-query__item__icon__icon cursor-pointer"
301
+ name="play_circle"
302
+ title="播放"
303
+ :size="toPx(currentSize / 1.5)"
304
+ @click="uploader.play(fileItem)"
305
+ v-else
306
+ />
307
+ </div>
308
+
309
+ <!-- 信息 -->
310
+ <div class="n-uploader-query__item__info">
311
+ <!-- 标题 -->
312
+ <div class="n-uploader-query__item__info__title ellipsis">{{getFileName(fileItem)}}</div>
313
+ <!-- 错误提示 -->
314
+ <div class="n-uploader-query__item__info__msg--error" v-if="fileItem.status === UPLOAD_STATUS.fail">{{fileItem.msg}}</div>
315
+ </div>
316
+
317
+ <!-- 操作 -->
318
+ <div class="n-uploader-query__item__settings">
319
+
320
+ <!-- 操作插槽-->
321
+ <slot
322
+ name="settings"
323
+ :file="fileItem"
324
+ />
325
+
326
+ <template v-if="fileItem.status === UPLOAD_STATUS.success">
327
+
328
+ <!-- 复制地址 -->
329
+ <q-icon
330
+ class="n-uploader-query__item__settings__icon cursor-pointer"
331
+ name="content_copy"
332
+ color="white"
333
+ :size="settingsIconSize"
334
+ title="复制地址"
335
+ @click="uploader.copyUrl(fileItem)"
336
+ v-bind="settingsIconProps"
337
+ />
338
+
339
+ <!-- 修改 -->
340
+ <q-icon
341
+ class="n-uploader-query__item__settings__icon cursor-pointer"
342
+ name="edit"
343
+ color="white"
344
+ :size="settingsIconSize"
345
+ title="修改"
346
+ v-bind="settingsIconProps"
347
+ v-if="! noEdit && ! disable && ! readonly && ! fileItem.isNet"
348
+ >
349
+ <q-popup-edit
350
+ :model-value="fileItem.title"
351
+ buttons
352
+ label-set="保存"
353
+ @save="uploader.editFileTitle($event, fileItem)"
354
+ v-slot="scope"
355
+ >
356
+ <q-input
357
+ v-model="scope.value"
358
+ dense
359
+ autofocus
360
+ counter
361
+ :maxlength="50"
362
+ @keyup.enter="scope.set"
363
+ >
364
+ <template v-slot:append>
365
+ <span class="text-subtitle2 text-weight-bold">.{{fileItem.ext}}</span>
366
+ </template>
367
+ </q-input>
368
+ </q-popup-edit>
369
+ </q-icon>
370
+ </template>
371
+
372
+ <!-- 删除 -->
373
+ <q-icon
374
+ class="n-uploader-query__item__settings__icon cursor-pointer"
375
+ name="close"
376
+ color="white"
377
+ :size="settingsIconSize"
378
+ title="删除"
379
+ @click="uploader.deleteFileItem(fileItem)"
380
+ v-bind="settingsIconProps"
381
+ v-if="! noDelete && ! disable && ! readonly"
382
+ />
383
+ </div>
384
+ </div>
385
+ </template>
386
+ </n-dragger>
387
+ </div>
388
+ </template>
389
+
390
+ <script>
391
+ import { computed, inject } from 'vue'
392
+ import { useQuasar } from 'quasar'
393
+
394
+ import $n_has from 'lodash/has'
395
+ import $n_get from 'lodash/get'
396
+
397
+ import $n_px from '@netang/utils/px'
398
+ import $n_forEach from '@netang/utils/forEach'
399
+ import $n_isValidArray from '@netang/utils/isValidArray'
400
+ import $n_isValidString from '@netang/utils/isValidString'
401
+
402
+ import $n_getImage from '../../utils/getImage'
403
+ import $n_previewImage from '../../utils/previewImage'
404
+
405
+ import NDragger from '../dragger'
406
+
407
+ import { NUploaderKey } from '../../utils/symbols'
408
+
409
+ import {
410
+ // 上传状态
411
+ UPLOAD_STATUS,
412
+ } from '../../utils/useUploader'
413
+
414
+ export default {
415
+
416
+ /**
417
+ * 标识
418
+ */
419
+ name: 'NUploaderQuery',
420
+
421
+ /**
422
+ * 组件
423
+ */
424
+ components: {
425
+ NDragger,
426
+ },
427
+
428
+ /**
429
+ * 声明属性
430
+ */
431
+ props: {
432
+ // 按钮类型, 可选值 square button
433
+ buttonType: {
434
+ type: String,
435
+ validator: v => [ 'square', 'button' ].includes(v),
436
+ },
437
+ // 按钮文字
438
+ buttonText: String,
439
+ // 按钮声明属性
440
+ buttonProps: Object,
441
+ // 图片/按钮/文件 尺寸
442
+ size: Number,
443
+ // 是否开启拖拽
444
+ drag: {
445
+ type: Boolean,
446
+ default: true,
447
+ },
448
+ // 是否禁用
449
+ disable: Boolean,
450
+ // 是否只读
451
+ readonly: Boolean,
452
+ // 是否隐藏按钮
453
+ noButton: Boolean,
454
+ // 是否隐藏预览按钮
455
+ noPreview: Boolean,
456
+ // 是否隐藏修改按钮
457
+ noEdit: Boolean,
458
+ // 是否隐藏删除按钮
459
+ noDelete: Boolean,
460
+ // 自动显示方块按钮
461
+ autoShowSquareButton: Boolean,
462
+ // 方块按钮在右边显示
463
+ rightSquareButton: Boolean,
464
+ // 设置图标尺寸
465
+ settingsIconSize: {
466
+ type: String,
467
+ default: 'xs',
468
+ },
469
+ // 设置图标传参
470
+ settingsIconProps: Object,
471
+ // 是否显示上传网络外链按钮
472
+ showUploadNetButton: Boolean,
473
+ },
474
+
475
+ /**
476
+ * 声明事件
477
+ */
478
+ emits: [
479
+ 'update:modelValue',
480
+ ],
481
+
482
+ /**
483
+ * 组合式
484
+ */
485
+ setup(props) {
486
+
487
+ // ==========【数据】============================================================================================
488
+
489
+ // quasar 对象
490
+ const $q = useQuasar()
491
+
492
+ // 获取上传器注入数据
493
+ const {
494
+ // 声明属性
495
+ props: uploaderProps,
496
+ // 上传器
497
+ uploader,
498
+ // 文件队列
499
+ query,
500
+ } = inject(NUploaderKey)
501
+
502
+ // ==========【计算属性】=========================================================================================
503
+
504
+ /**
505
+ * 当前上传文件队列
506
+ */
507
+ const currentQuery = computed(function () {
508
+
509
+ // 如果不是图片
510
+ if (uploaderProps.type !== 'image') {
511
+ if ($n_isValidArray(query.value)) {
512
+ return query.value
513
+ }
514
+ return []
515
+ }
516
+
517
+ const lists = []
518
+
519
+ $n_forEach(query.value, function (fileItem) {
520
+ const newItem = Object.assign({}, fileItem)
521
+
522
+ let src = ''
523
+ let preview_src = ''
524
+
525
+ if ($n_has(fileItem, '__img')) {
526
+ src = fileItem.__img
527
+ preview_src = src
528
+ } else if ($n_isValidString(fileItem.hash)) {
529
+ src = $n_getImage(fileItem.hash, { w: $q.platform.is.mobile ? currentSize.value * 2 : currentSize.value })
530
+ if (src) {
531
+ // 预览地址
532
+ preview_src = fileItem.hash
533
+ }
534
+ }
535
+
536
+ lists.push(Object.assign(newItem, {
537
+ // 图片地址
538
+ src,
539
+ // 预览地址
540
+ preview_src,
541
+ }))
542
+ })
543
+
544
+ return lists
545
+ })
546
+
547
+
548
+ /**
549
+ * 当前是否开启拖拽
550
+ */
551
+ const currentDrag = computed(function() {
552
+ return props.drag
553
+ && query.value.length > 1
554
+ && ! props.readonly
555
+ && ! props.disable
556
+ })
557
+
558
+ /**
559
+ * 当前按钮类型
560
+ */
561
+ const currentButtonType = computed(function () {
562
+ if (props.buttonType) {
563
+ return props.buttonType
564
+ }
565
+ return uploaderProps.type === 'image' ? 'square' : 'button'
566
+ })
567
+
568
+ /**
569
+ * 当前尺寸
570
+ */
571
+ const currentSize = computed(function () {
572
+ if (props.size) {
573
+ return props.size
574
+ }
575
+ return uploaderProps.type === 'image' ? 70 : 50
576
+ })
577
+
578
+ /**
579
+ * 是否显示方块按钮
580
+ */
581
+ const showSquareButton = computed(function () {
582
+ // 自动显示方块按钮 && 有上传文件限制数量
583
+ return props.autoShowSquareButton && uploaderProps.count > 0 ?
584
+ // 如果 当前上传文件队列数量 < 上传文件限制数量
585
+ currentQuery.value.length < uploaderProps.count
586
+ // 始终显示
587
+ : true
588
+ })
589
+
590
+ // ==========【方法】=============================================================================================
591
+
592
+ /**
593
+ * 预览图片
594
+ */
595
+ function previewImage(startPosition) {
596
+ $n_previewImage({
597
+ // 需要预览的图片 URL 数组
598
+ images: currentQuery.value.map(e => e.preview_src),
599
+ // 图片预览起始位置索引
600
+ startPosition,
601
+ })
602
+ }
603
+
604
+ /**
605
+ * 获取文件名称
606
+ */
607
+ function getFileName(fileItem) {
608
+ return fileItem.title + ($n_get(fileItem, 'ext') ? '.' + fileItem.ext : '')
609
+ }
610
+
611
+
612
+ // ==========【返回】=============================================================================================
613
+
614
+ return {
615
+ // 上传状态
616
+ UPLOAD_STATUS,
617
+ // 上传文件类型, 可选值 file image video audio
618
+ type: uploaderProps.type,
619
+ // 上传文件数量(0:不限)
620
+ count: uploaderProps.count,
621
+ // 文件队列
622
+ query,
623
+ // 当前上传文件队列
624
+ currentQuery,
625
+
626
+ // 当前是否开启拖拽
627
+ currentDrag,
628
+ // 当前按钮类型
629
+ currentButtonType,
630
+ // 当前尺寸
631
+ currentSize,
632
+ // 是否显示方块按钮
633
+ showSquareButton,
634
+
635
+ // 上传器
636
+ uploader,
637
+
638
+ // 预览图片
639
+ previewImage,
640
+ // 获取文件名称
641
+ getFileName,
642
+
643
+ toPx: $n_px,
644
+ }
645
+ },
646
+ }
647
+ </script>
648
+
649
+ <style lang="scss">
650
+ @import "@/assets/sass/variables.scss";
651
+
652
+ // 上传器队列
653
+ .n-uploader-query {
654
+
655
+ // 上传按钮
656
+ &__button {
657
+
658
+ // 方块
659
+ &--square {
660
+ display: inline-flex;
661
+ overflow: hidden;
662
+ flex-direction: column;
663
+
664
+ &-button {
665
+ flex: 1;
666
+ display: flex;
667
+ vertical-align: middle;
668
+ border: 1px dashed rgba(var(--n-reverse-color-rgb), 0.2);
669
+ flex-direction: column;
670
+ justify-content: center;
671
+ align-items: center;
672
+ color: rgba(var(--n-reverse-color-rgb), 0.4);
673
+ border-radius: 4px;
674
+ overflow: hidden;
675
+
676
+ &:hover {
677
+ border-color: $primary;
678
+ }
679
+
680
+ // 文字
681
+ &__text {
682
+ font-size: 12px;
683
+ }
684
+ }
685
+ }
686
+
687
+ // 按钮
688
+ &--button {
689
+ + .n-uploader-query__query {
690
+ margin-top: 0;
691
+ }
692
+ }
693
+ }
694
+
695
+ // 上传单个文件
696
+ &__item {
697
+ position: relative;
698
+
699
+ // 开启拖拽
700
+ &[draggable="true"] {
701
+ cursor: move;
702
+ }
703
+
704
+ // 当前拖拽占位元素
705
+ &.ghost {
706
+ &:after {
707
+ content: "";
708
+ position: absolute;
709
+ top: 0;
710
+ left: 0;
711
+ right: 0;
712
+ bottom: 0;
713
+ border: 2px dashed mix(#ffffff, $primary, 40%);
714
+ border-radius: 4px;
715
+ background-color: rgba(255, 255, 255, 0.75);
716
+ }
717
+
718
+ .n-uploader-query__item__inner,
719
+ .n-uploader-query__item__settings {
720
+ display: none;
721
+ }
722
+ }
723
+
724
+ &:hover {
725
+ .n-uploader-query__item__settings {
726
+ visibility: visible;
727
+ }
728
+ }
729
+
730
+ // 单个图片
731
+ &--image {
732
+ border-radius: 4px;
733
+ background-color: rgba(0,0,0,0.1);
734
+ overflow: hidden;
735
+ }
736
+
737
+ // 单个文件
738
+ &--file {
739
+ position: relative;
740
+ width: 300px;
741
+ display: flex;
742
+ flex-direction: row;
743
+ align-items: center;
744
+ border-radius: 4px;
745
+ color: rgba(var(--n-reverse-color-rgb), 0.8);
746
+ background-color: rgba(var(--n-reverse-color-rgb), 0.05);
747
+
748
+ // 图标
749
+ .n-uploader-query__item__icon {
750
+ position: relative;
751
+ display: flex;
752
+ align-items: center;
753
+ justify-content: center;
754
+ z-index: 1;
755
+
756
+ &__icon {
757
+ color: rgba(var(--n-reverse-color-rgb), 0.2);
758
+ }
759
+ }
760
+
761
+ // 信息
762
+ .n-uploader-query__item__info {
763
+ display: flex;
764
+ flex-direction: column;
765
+ line-height: 18px;
766
+
767
+ &__title {
768
+ max-width: 150px;
769
+ }
770
+
771
+ &__msg--error {
772
+ color: $negative;
773
+ }
774
+ }
775
+ }
776
+
777
+ // 外链
778
+ &__net {
779
+ position: absolute;
780
+ bottom: -1px;
781
+ right: -1px;
782
+ color: #ffffff;
783
+ padding: 1px 3px;
784
+ border-radius: 3px;
785
+ background-color: var(--q-primary);
786
+ transform: scale(0.7);
787
+ pointer-events: none;
788
+ }
789
+
790
+ //操作
791
+ &__settings {
792
+ position: absolute;
793
+ top: 5px;
794
+ right: 5px;
795
+ visibility: hidden;
796
+
797
+ &__icon {
798
+ background-color: rgba(0,0,0,0.5) !important;
799
+ border-radius: 50%;
800
+ padding: 5px;
801
+
802
+ + .n-uploader-query__item__settings__icon {
803
+ margin-left: 4px;
804
+ }
805
+
806
+ &:hover {
807
+ background-color: rgba(0,0,0,0.8);
808
+ }
809
+ }
810
+ }
811
+
812
+ // 内容
813
+ &__inner {
814
+
815
+ &__msg {
816
+ margin: 3px;
817
+ padding: 2px 3px;
818
+ line-height: 18px;
819
+ font-size: 12px;
820
+ background-color: rgba(0,0,0,0.6);
821
+ border-radius: 6px;
822
+
823
+ &--error {
824
+ background-color: $warning;
825
+ }
826
+ }
827
+ }
828
+ }
829
+ }
830
+
831
+ @media (max-width: $breakpoint-xs-max){
832
+ // 上传器队列
833
+ .n-uploader-query {
834
+ // 上传单个文件
835
+ &__item {
836
+ // 单个文件
837
+ &--file {
838
+ width: 100%;
839
+ }
840
+ }
841
+
842
+ // 信息
843
+ .n-uploader-query__item__info {
844
+ &__title {
845
+ max-width: 200px;
846
+ }
847
+ }
848
+ }
849
+ }
850
+
851
+ /**
852
+ * 手机版
853
+ */
854
+ body.mobile {
855
+ // 上传器队列
856
+ .n-uploader-query {
857
+ // 上传单个文件
858
+ &__item {
859
+ &__settings {
860
+ visibility: visible;
861
+ }
862
+ }
863
+ }
864
+ }
865
+
866
+ /**
867
+ * 暗黑
868
+ */
869
+ .body--dark {
870
+ .n-uploader-query__item--file {
871
+ .n-uploader-query__item__settings {
872
+ // 图标
873
+ &__icon {
874
+ background-color: rgba(255,255,255, 0.1);
875
+
876
+ &:hover {
877
+ background-color: rgba(255,255,255, 0.2);
878
+ }
879
+ }
880
+ }
881
+ }
882
+ }
883
+ </style>