@utogether/udp-core 2.0.0-beta.26 → 2.0.0-beta.28

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 (183) hide show
  1. package/dist/403-CyoOD-Ik.js +65 -0
  2. package/dist/404-Ch_OHTw0.js +65 -0
  3. package/dist/500-Cf9am7HK.js +67 -0
  4. package/dist/AuthorityInfo-BJ427cqG.js +4 -0
  5. package/dist/AuthorityInfo.vue_vue_type_style_index_0_lang-D9y7AwbB.js +99 -0
  6. package/dist/AuthorityPanel-lVnZ5bce.js +4 -0
  7. package/dist/AuthorityPanel.vue_vue_type_style_index_0_lang-nsfhDlvV.js +113 -0
  8. package/dist/Company-qo-lKPkv.js +25 -0
  9. package/dist/CompanyPanel-DkKWAKvY.js +204 -0
  10. package/dist/DataSet-iBHzs75s.js +147 -0
  11. package/dist/Department-CHUQByKX.js +25 -0
  12. package/dist/DepartmentPanel-C742dfOO.js +248 -0
  13. package/dist/DesignPanel-D9oE5R7m.js +4 -0
  14. package/dist/DesignPanel.vue_vue_type_style_index_0_lang-hYldVEbo.js +1097 -0
  15. package/dist/DictView-CrFXprP9.js +110 -0
  16. package/dist/InvOrganization-Cz8SzIgs.js +73 -0
  17. package/dist/Org-Bzt6yKox.js +38 -0
  18. package/dist/Preview-jonigfMQ.js +48 -0
  19. package/dist/ReportDefine-BpiM5Ovq.js +10 -0
  20. package/dist/ReportDesign-fp09cXJ7.js +164 -0
  21. package/dist/ReportQuery-52ZP1PYP.js +74 -0
  22. package/dist/ReportQueryFrom-DSYavJb1.js +4 -0
  23. package/dist/ReportQueryFrom.vue_vue_type_style_index_0_lang-DBGYEp0u.js +177 -0
  24. package/dist/ReportTemplate-CA_UW0eD.js +161 -0
  25. package/dist/Role-DqOt8Bk0.js +25 -0
  26. package/dist/RoleAssign-BdxhefhQ.js +26 -0
  27. package/dist/RolePanel-2gZ3H0ET.js +4 -0
  28. package/dist/RolePanel-BsNd0svJ.js +4 -0
  29. package/dist/RolePanel.vue_vue_type_script_setup_true_lang-DLOwgF99.js +152 -0
  30. package/dist/RolePanel.vue_vue_type_script_setup_true_lang-DmzD9kab.js +131 -0
  31. package/dist/ScrollPanel.vue_vue_type_style_index_0_lang-B9ptUMeI.js +99 -0
  32. package/dist/Staff-D3L8fEoY.js +26 -0
  33. package/dist/StaffInfo-DYRw6ZYJ.js +4 -0
  34. package/dist/StaffInfo.vue_vue_type_script_setup_true_lang-xqEOQnMs.js +107 -0
  35. package/dist/StaffPanel-DkrNH6Px.js +4 -0
  36. package/dist/StaffPanel.vue_vue_type_script_setup_true_lang-BPBMnliy.js +152 -0
  37. package/dist/SysUser-Bl1-cIpJ.js +15 -0
  38. package/dist/SysUserPanel-yxNLaBwt.js +4 -0
  39. package/dist/SysUserPanel.vue_vue_type_script_setup_true_lang-BDcsgSl5.js +352 -0
  40. package/dist/SystemMenu-C4_Arn12.js +155 -0
  41. package/dist/UserInfo-QcNwJCUR.js +4 -0
  42. package/dist/UserInfo.vue_vue_type_style_index_0_lang-DLIeUphX.js +159 -0
  43. package/dist/childView-B23DEO2u.js +4 -0
  44. package/dist/childView-saDZQv9x.js +4 -0
  45. package/dist/childView.vue_vue_type_style_index_0_lang-RsKR6CAC.js +182 -0
  46. package/dist/childView.vue_vue_type_style_index_0_lang-ZFEfavUY.js +142 -0
  47. package/dist/code-rule-2_Ihx_by.js +147 -0
  48. package/dist/core.es.js +26 -6
  49. package/dist/cron-task-BFOPJQIk.js +134 -0
  50. package/dist/flow-task-PoDnE_kE.js +10 -0
  51. package/dist/frameView-f7Ya0Knj.js +43 -0
  52. package/dist/index-BpdTDaTk.js +3213 -0
  53. package/dist/layout-home-v3DPJ7rB.js +231 -0
  54. package/dist/layoutView-DZHoQDH1.js +3190 -0
  55. package/dist/log-in-BPAMJTwH.js +115 -0
  56. package/dist/log-out-DZACF0jg.js +129 -0
  57. package/dist/login-B8cwozax.js +216 -0
  58. package/dist/login-log-CVvRgZSu.js +70 -0
  59. package/dist/lov-view-Ba7Y7xy0.js +98 -0
  60. package/dist/menuInfo-BpRKFYK8.js +4 -0
  61. package/dist/menuInfo.vue_vue_type_style_index_0_lang-DJ5sUC6m.js +359 -0
  62. package/dist/pda-app-C0jJecrL.js +706 -0
  63. package/dist/redirect-DTz8GSnv.js +15 -0
  64. package/dist/resource-BG22Cw4P.js +97 -0
  65. package/dist/su-welcome-DaxhbrC6.js +879 -0
  66. package/dist/sys-config-CG2fMtak.js +384 -0
  67. package/dist/udp-core.css +1 -2
  68. package/dist/useNav-_u2UAQWk.js +84 -0
  69. package/dist/utogether-MlnyYtNS.js +4 -0
  70. package/dist/wecom-push-B75FLee5.js +70 -0
  71. package/index.ts +54 -50
  72. package/package.json +21 -21
  73. package/src/App.vue +79 -78
  74. package/src/api/http.ts +10 -4
  75. package/src/api/index.ts +51 -51
  76. package/src/api/user.ts +64 -64
  77. package/src/components/SuQrcode/src/index.tsx +231 -236
  78. package/src/components/udp/content/index.vue +93 -93
  79. package/src/components/udp/form-upload/form-upload.vue +496 -496
  80. package/src/components/udp/grid/index.vue +643 -544
  81. package/src/components/udp/index.ts +2 -1
  82. package/src/components/udp/modal-form/index.vue +326 -0
  83. package/src/components/udp/utils.ts +420 -420
  84. package/src/layout/components/lay-navbar/index.vue +240 -240
  85. package/src/layout/components/lay-panel/index.vue +2 -6
  86. package/src/layout/components/lay-search/components/SearchModal.vue +185 -181
  87. package/src/layout/components/lay-sidebar/mixNav.vue +261 -261
  88. package/src/layout/components/lay-sidebar/sidebarItem.vue +199 -195
  89. package/src/layout/components/lay-sidebar/vertical.vue +92 -92
  90. package/src/layout/layoutView.vue +1 -0
  91. package/src/main.ts +111 -109
  92. package/src/plugins/i18n/zh.ts +5 -1
  93. package/src/plugins/vxe-table/index.ts +116 -116
  94. package/src/router/index.ts +5 -34
  95. package/src/router/utils.ts +434 -430
  96. package/src/store/modules/permission.ts +1 -1
  97. package/src/store/modules/system.ts +193 -186
  98. package/src/style/print-lock.css +380 -375
  99. package/src/style/suprint.css +990 -985
  100. package/src/style/vxetable.scss +372 -364
  101. package/src/utils/authority/index.ts +19 -2
  102. package/src/utils/dataFormat/index.ts +309 -222
  103. package/src/utils/udp/http/index.ts +126 -67
  104. package/src/utils/udp/http/types.d.ts +15 -14
  105. package/src/views/login/login-view.vue +4 -21
  106. package/src/views/organization/department/DepartmentPanel.vue +3 -4
  107. package/src/views/system/sys/sys-config.vue +344 -344
  108. package/src/views/uhome/components/home-todo.vue +191 -178
  109. package/src/views/uhome/dynamic-card.vue +31 -31
  110. package/src/views/uhome/su-welcome.vue +310 -310
  111. package/src/views/ulogin/login.vue +292 -296
  112. package/src/views/urpt/design/DesignPanel.vue +538 -507
  113. package/src/views/urpt/design/print-data.ts +39 -59
  114. package/tsconfig.json +2 -3
  115. package/types/global.d.ts +234 -234
  116. package/vite.config.ts +2 -1
  117. package/dist/403-D6Wb3JJh.js +0 -72
  118. package/dist/404-B_HNbPNV.js +0 -72
  119. package/dist/500-BMemTTy1.js +0 -77
  120. package/dist/AuthorityInfo-6wHxwiFM.js +0 -91
  121. package/dist/AuthorityPanel-OqF_DvXA.js +0 -98
  122. package/dist/Company-BLXSfow0.js +0 -21
  123. package/dist/CompanyPanel-B4kRZ5Cg.js +0 -199
  124. package/dist/DataSet-CuGZcKC-.js +0 -141
  125. package/dist/Department-oFhsa7cd.js +0 -21
  126. package/dist/DepartmentPanel-B1UGpu-r.js +0 -242
  127. package/dist/DepartmentPanel-BL58AdoT.js +0 -2
  128. package/dist/DesignPanel-DmkFVw1N.js +0 -1196
  129. package/dist/DictView-CC5yMzOq.js +0 -100
  130. package/dist/File-WJXjGYxr.js +0 -89
  131. package/dist/InvOrganization-BwMmD8Ua.js +0 -76
  132. package/dist/Org-HgnOWubi.js +0 -76
  133. package/dist/Preview-CAzWxLmP.js +0 -45
  134. package/dist/ReIcon-DecitXmg.js +0 -413
  135. package/dist/ReportDefine-BgLM-Rjk.js +0 -10
  136. package/dist/ReportDesign-DwchGG2P.js +0 -158
  137. package/dist/ReportQuery-D5jaRJEn.js +0 -101
  138. package/dist/ReportQueryFrom-CNlmtYey.js +0 -198
  139. package/dist/ReportTemplate-CNcTmccs.js +0 -162
  140. package/dist/Role-Be8WfdCA.js +0 -21
  141. package/dist/RoleAssign-CtehGAUf.js +0 -22
  142. package/dist/RolePanel-CAwITg_q.js +0 -131
  143. package/dist/RolePanel-SN0FGFZ7.js +0 -176
  144. package/dist/ScrollPanel-DW30pGz1.js +0 -88
  145. package/dist/Staff-V2YsNKcj.js +0 -22
  146. package/dist/StaffInfo-CKRPW0r4.js +0 -149
  147. package/dist/StaffPanel-BxcgMg7u.js +0 -143
  148. package/dist/SysUser-Dxyj5w5m.js +0 -12
  149. package/dist/SysUserPanel-DI6CWcZo.js +0 -365
  150. package/dist/SystemMenu-BBHREM8T.js +0 -154
  151. package/dist/UserInfo-BMVQ0xwE.js +0 -194
  152. package/dist/_plugin-vue_export-helper-C6QCLu9I.js +0 -8
  153. package/dist/api-BFNxcxSN.js +0 -30
  154. package/dist/authority-BJFEUdrh.js +0 -20
  155. package/dist/await-to-js.es5-DkWYmPXW.js +0 -10
  156. package/dist/childView-BBjPEraB.js +0 -184
  157. package/dist/childView-DNqG2UVm.js +0 -181
  158. package/dist/code-rule-QFrhqFrW.js +0 -151
  159. package/dist/contant-Q77dU_V3.js +0 -4
  160. package/dist/cron-task-BpUDKEAU.js +0 -131
  161. package/dist/flow-task-DAc2NrDU.js +0 -11
  162. package/dist/frameView-Di0zJZUR.js +0 -39
  163. package/dist/layout-home-BVp7vYLR.js +0 -239
  164. package/dist/layoutView-B6Mx2ERA.js +0 -2662
  165. package/dist/log-in-BrNB0mLz.js +0 -117
  166. package/dist/log-out-dYfqvzy6.js +0 -111
  167. package/dist/login-D_Hjy3Jd.js +0 -231
  168. package/dist/login-log-CJak-ANA.js +0 -79
  169. package/dist/lov-view-DM5Nm5tW.js +0 -95
  170. package/dist/menuInfo-BlOY1KZW.js +0 -346
  171. package/dist/mitt-CQgI7DYU.js +0 -27
  172. package/dist/pda-app-CA7YI37u.js +0 -589
  173. package/dist/redirect-N0k4tImU.js +0 -16
  174. package/dist/resource-CfQTHFcs.js +0 -94
  175. package/dist/su-welcome-DVD3S_du.js +0 -799
  176. package/dist/sys-config-Ddb3vBpK.js +0 -316
  177. package/dist/system-Cm3lyBas.js +0 -1528
  178. package/dist/udp-core-DaQd5CF1.js +0 -2028
  179. package/dist/useDataThemeChange-DJ4K3VGp.js +0 -226
  180. package/dist/useNav-BhOJIdrJ.js +0 -106
  181. package/dist/utogether-l7JXs4Lv.js +0 -4
  182. package/dist/water-mark-WDQZ9YqB.js +0 -119
  183. package/dist/wecom-push-kWkAVRCj.js +0 -77
@@ -1,496 +1,496 @@
1
- <!--
2
- * @Author: wei.li
3
- * @Date: 2022-11-04 14:53:36
4
- * @LastEditors: levi7754 levi7754@163.com
5
- * @LastEditTime: 2026-01-14 10:19:50
6
- * @Description: form upload
7
- -->
8
- <template>
9
- <div class="flex w-full flex-wrap">
10
- <div class="ut-upload-list flex flex-wrap items-center">
11
- <div v-for="(item, idx) in fileList" :key="item" :class="'ut-upload-item--' + size">
12
- <div class="ut-upload--image flex items-center justify-center">
13
- <img
14
- v-if="getItemType(item) === 'img'"
15
- :src="item.url"
16
- class="ut-image select-none"
17
- @click="onPreview(item)"
18
- />
19
- <div v-else-if="renderMode === 'item' || dataStatus !== 'detail'" class="ut-image-hover">
20
- <div class="ut-image-file--wrapper">
21
- <i :class="getItemType(item)" class="ut-image-file ut-image" style="line-height: 1" />
22
- <div class="px-1 ut-image" :style="{ fontSize: size === 'small' ? '11px' : '10px' }">
23
- {{ maskFilenameFromPath(item) }}
24
- </div>
25
- </div>
26
- <i
27
- class="vxe-icon-download ut-download-icon"
28
- :style="{ fontSize: renderMode === 'item' ? '60px' : '48px' }"
29
- @click="onDownload(item)"
30
- />
31
- </div>
32
- <vxe-tooltip v-else :content="item.name" class="cursor-pointer">
33
- <div class="ut-image-hover">
34
- <div class="ut-image-file--wrapper">
35
- <i :class="getItemType(item)" class="ut-image-file ut-image" style="font-size: 32px" />
36
- <div class="px-1 ut-image" :style="{ fontSize: size === 'small' ? '11px' : '10px' }">
37
- {{ maskFilenameFromPath(item) }}
38
- </div>
39
- </div>
40
- <i
41
- class="vxe-icon-download ut-download-icon"
42
- :style="{ fontSize: renderMode === 'item' ? '60px' : '48px' }"
43
- @click="onDownload(item)"
44
- />
45
- </div>
46
- </vxe-tooltip>
47
- </div>
48
- <div v-if="dataStatus !== 'detail'" class="ut-upload--image-item-btn-wrapper">
49
- <div class="ut-upload-remove--btn">
50
- <i class="vxe-icon-close" @click="onRemove(item, idx)" />
51
- </div>
52
- </div>
53
- </div>
54
- <div
55
- v-if="dataStatus !== 'detail'"
56
- :class="'ut-upload-item--' + size"
57
- class="ut-upload--btn"
58
- style="border: 1px dashed var(--vxe-ui-input-border-color)"
59
- @click="onStartUpload"
60
- >
61
- <div class="flex items-center justify-center h-full flex-col cursor-pointer ut-upload--btn">
62
- <span v-if="!loading" class="vxe-icon-add" style="font-size: 18px" />
63
- <div v-if="!loading" style="padding: 0 1px; font-size: 11px; text-align: center">点击上传</div>
64
- <vxe-icon v-if="loading" name="spinner" status="primary" roll style="font-size: 24px" />
65
- <div v-if="loading" style="padding: 0 1px; font-size: 11px; text-align: center">
66
- {{ progressText }}
67
- </div>
68
- </div>
69
- </div>
70
- </div>
71
- <vxe-upload
72
- ref="xupload"
73
- v-model="list"
74
- style="display: none"
75
- v-bind="$attrs"
76
- :limit-count="limitCount"
77
- :limit-size="limitSize"
78
- :size="size"
79
- :mode="mode"
80
- :single-mode="singleMode"
81
- :image-types="imageTypes"
82
- :auto-hidden-button="autoHiddenButton"
83
- :show-error-status="showErrorStatus"
84
- :remove-method="handleRemove"
85
- :upload-method="httpRequest"
86
- :multiple="multiple"
87
- :show-download-button="showDownloadButton"
88
- :show-list="false"
89
- show-progress
90
- @upload-success="onSuccess"
91
- />
92
- </div>
93
- </template>
94
-
95
- <script lang="ts">
96
- export default { name: 'FormUpload' };
97
- </script>
98
-
99
- <script setup lang="ts">
100
- import { ref, computed, watch, onBeforeMount, getCurrentInstance } from 'vue';
101
- import { cookies } from '@utogether/utils';
102
- import compressorjs from 'compressorjs';
103
- import { isString, isArray } from 'xe-utils';
104
- import { VxeUI, VxeUploadPropTypes } from 'vxe-pc-ui';
105
- import axios from 'axios';
106
-
107
- export interface IProps {
108
- record: object;
109
- imageTypes?: Array<string>;
110
- field: string;
111
- dataStatus?: string;
112
- renderMode?: string;
113
- size?: VxeUploadPropTypes.Size;
114
- mode?: VxeUploadPropTypes.Mode;
115
- limitCount?: number;
116
- limitSize?: number;
117
- compassorSize?: number;
118
- quality?: number;
119
- singleMode?: boolean;
120
- autoHiddenButton?: boolean;
121
- showErrorStatus?: boolean;
122
- showDownloadButton?: boolean;
123
- multiple?: boolean;
124
- success?: Function;
125
- remove?: Function;
126
- }
127
- const props = withDefaults(defineProps<IProps>(), {
128
- record: () => {
129
- return {};
130
- },
131
- imageTypes: () => {
132
- return ['jpg', 'jpeg', 'png', 'gif'];
133
- },
134
- field: '',
135
- renderMode: 'item',
136
- dataStatus: '',
137
- size: 'mini',
138
- mode: '',
139
- singleMode: false,
140
- autoHiddenButton: false,
141
- showDownloadButton: false,
142
- multiple: false,
143
- limitSize: 30,
144
- limitCount: 9,
145
- quality: 0.6,
146
- compassorSize: 2
147
- });
148
-
149
- watch(
150
- () => props.record[props.field],
151
- val => {
152
- initData();
153
- }
154
- );
155
-
156
- const instance = getCurrentInstance()!;
157
- const baseUrl = instance.appContext.config.globalProperties.$url;
158
- const serviceApi = instance.appContext.config.globalProperties.$serviceApi;
159
- const progressText = ref('');
160
- const loading = ref(false);
161
-
162
- const imgTypes = ['image/gif', 'image/jpeg', 'image/png'];
163
- const xupload = ref(null);
164
-
165
- const headers = computed(() => {
166
- const kTOKENKEY = 'authorized-token';
167
- const token = cookies.get(kTOKENKEY)!;
168
- const data = JSON.parse(token);
169
- const headers = {
170
- Authorization: 'Bearer ' + data.accessToken
171
- };
172
- return headers;
173
- });
174
- const list = ref([]);
175
- const fileList = ref<any>([]);
176
-
177
- const httpRequest = options => {
178
- if (imgTypes.includes(options.file.type) && options.file.size > 1024 * 1024 * props.compassorSize) {
179
- return new Promise((resolve, reject) => {
180
- new compressorjs(options.file, {
181
- quality: props.quality,
182
- mimeType: options.file.type,
183
- success(result: File) {
184
- const file = new File([result], result.name, { type: result.type });
185
- uploadProcess(resolve, reject, file);
186
- console.log({ result, file });
187
- }
188
- });
189
- });
190
- } else {
191
- return new Promise((resolve, reject) => uploadProcess(resolve, reject, options.file));
192
- }
193
- };
194
-
195
- const uploadProcess = (resolve, reject, file) => {
196
- const formBody = new FormData();
197
- formBody.append('file', file);
198
- const url = baseUrl + '/ufil/file/uploadMultipart';
199
- loading.value = true;
200
- return axios
201
- .post(url, formBody, {
202
- headers: Object.assign({ 'Content-Type': 'multipart/form-data' }, headers.value),
203
- onUploadProgress(progressEvent) {
204
- const progress = Math.round((progressEvent.loaded * 100) / (progressEvent.total || 0));
205
- progressText.value = `进度${progress}%`;
206
- }
207
- })
208
- .then(async response => {
209
- progressText.value = `处理中...`;
210
- file.path = response.data.data;
211
- file.url = `${baseUrl}/ufil${file.path}`;
212
- fileList.value.push(file);
213
- console.log(file);
214
- loading.value = false;
215
- resolve(file);
216
- })
217
- .catch(() => {
218
- loading.value = false;
219
- reject(null);
220
- });
221
- };
222
-
223
- const onStartUpload = () => {
224
- xupload.value.choose();
225
- };
226
-
227
- const onSuccess = ({ data }) => {
228
- if (props.success) {
229
- return props.success(data);
230
- } else {
231
- const field = props.field;
232
- Object.assign(props.record, { [field]: data.path });
233
- }
234
- console.log(props.record, fileList);
235
- };
236
-
237
- const handleRemove: VxeUploadPropTypes.RemoveMethod = ({ option }) => {
238
- if (props.remove) {
239
- return props.remove(option, fileList);
240
- }
241
- emit('remove', option, fileList);
242
- };
243
-
244
- const onRemove = async (file, idx) => {
245
- console.log(file, idx, fileList.value);
246
- if (file.id) {
247
- await serviceApi.delete('/ufil/v1/attachment', [file]);
248
- }
249
- fileList.value.splice(idx, 1);
250
- emit('remove', file, fileList);
251
- };
252
-
253
- const onPreview = item => {
254
- // console.log(item.url);
255
- VxeUI.previewImage({ activeIndex: 0, urlList: [item.url] });
256
- };
257
-
258
- const getItemType = item => {
259
- const type = item.type || item.url.split('.').pop();
260
- let icon = '';
261
- switch (type) {
262
- case 'jpg':
263
- case 'png':
264
- case 'jpeg':
265
- case 'webp':
266
- case 'image/jpeg':
267
- case 'image/png':
268
- case 'image/gif':
269
- icon = 'img';
270
- break;
271
- case 'doc':
272
- case 'docx':
273
- icon = 'vxe-icon-file-word';
274
- break;
275
- case 'xlsx':
276
- case 'xls':
277
- icon = 'vxe-icon-file-excel';
278
- break;
279
- case 'pdf':
280
- icon = 'vxe-icon-file-pdf';
281
- break;
282
- case 'txt':
283
- icon = 'vxe-icon-file-txt';
284
- break;
285
- case 'ppt':
286
- icon = 'vxe-icon-file-ppt';
287
- break;
288
- case 'markdown':
289
- icon = 'vxe-icon-file-markdown';
290
- break;
291
- case 'apk':
292
- icon = 'ri-android-fill';
293
- break;
294
- case 'ipa':
295
- icon = 'ri-apple-line';
296
- break;
297
-
298
- default:
299
- icon = 'vxe-icon-file';
300
- break;
301
- }
302
- return icon;
303
- };
304
-
305
- const maskFilenameFromPath = file => {
306
- let total = props.size === 'small' ? 10 : 9;
307
- // 提取纯文件名(含扩展名)
308
- const extractFilename = file => {
309
- if (file.name) return file.name;
310
- const path = file.url;
311
- const separator = path.includes('\\') ? '\\' : '/';
312
- return path.split(separator).pop();
313
- };
314
-
315
- // 处理文件名脱敏
316
- const maskFilename = filename => {
317
- const dotPos = filename.lastIndexOf('.');
318
- const name = dotPos > 0 ? filename.substring(0, dotPos) : filename;
319
- const ext = dotPos > 0 ? filename.substring(dotPos) : '';
320
-
321
- if (name.length <= total) return filename;
322
-
323
- const prefix = props.renderMode === 'item' ? name.substring(0, total - 5) : '';
324
- const suffix = name.substring(name.length - (total - 6));
325
- return `${prefix}...${suffix}${ext}`;
326
- };
327
-
328
- return maskFilename(extractFilename(file));
329
- };
330
-
331
- const onDownload = file => {
332
- console.log(file);
333
- };
334
- const emit = defineEmits<{
335
- (e: 'remove', file: any, fileList: any): void;
336
- }>();
337
-
338
- const initData = () => {
339
- const fileUrl = props.record[props.field];
340
- fileList.value = [];
341
- if (fileUrl) {
342
- if (fileUrl && isString(fileUrl)) {
343
- const name = fileUrl.match(/[^\/\\]+$/)[0];
344
- const file = { name, url: `${baseUrl}/ufil${fileUrl}` };
345
- if (props.singleMode) {
346
- fileList.value = file;
347
- } else {
348
- fileList.value.push(file);
349
- }
350
- } else if (isArray(fileUrl)) {
351
- fileUrl.forEach(file => {
352
- if (isString(file)) {
353
- const name = file.match(/[^\/\\]+$/)[0];
354
- fileList.value.push({ name, url: `${baseUrl}/ufil${file}` });
355
- } else {
356
- fileList.value.push({
357
- name: file.name,
358
- id: file.id,
359
- url: `${baseUrl}/ufil${file.realpath}`
360
- });
361
- }
362
- });
363
- }
364
- }
365
- };
366
-
367
- onBeforeMount(() => {
368
- initData();
369
- });
370
- </script>
371
-
372
- <style lang="scss">
373
- .ut-upload-item--small {
374
- position: relative;
375
- width: var(--vxe-ui-upload-image-wh-small);
376
- height: var(--vxe-ui-upload-image-wh-small);
377
- margin: var(--vxe-ui-layout-padding-half);
378
- border: 1px solid var(--vxe-ui-input-border-color);
379
- border-radius: var(--vxe-ui-base-border-radius);
380
-
381
- .ut-upload--image {
382
- position: relative;
383
- width: var(--vxe-ui-upload-image-wh-small);
384
- height: var(--vxe-ui-upload-image-wh-small);
385
- border-radius: var(--vxe-ui-base-border-radius);
386
-
387
- .ut-image-file--wrapper {
388
- display: flex;
389
- flex-direction: column;
390
- align-items: center;
391
-
392
- .ut-image-file {
393
- font-size: 48px;
394
- }
395
- }
396
- }
397
- }
398
-
399
- .ut-upload-item--mini {
400
- position: relative;
401
- width: var(--vxe-ui-upload-image-wh-mini);
402
- height: var(--vxe-ui-upload-image-wh-mini);
403
- margin: var(--vxe-ui-layout-padding-half);
404
- border: 1px solid var(--vxe-ui-input-border-color);
405
- border-radius: var(--vxe-ui-base-border-radius);
406
-
407
- .ut-upload--image {
408
- position: relative;
409
- width: var(--vxe-ui-upload-image-wh-mini);
410
- height: var(--vxe-ui-upload-image-wh-mini);
411
- border-radius: var(--vxe-ui-base-border-radius);
412
-
413
- .ut-image-file--wrapper {
414
- display: flex;
415
- flex-direction: column;
416
- align-items: center;
417
-
418
- .ut-image-file {
419
- font-size: 42px;
420
- }
421
- }
422
- }
423
- }
424
-
425
- .ut-upload-item--xs {
426
- position: relative;
427
- width: 48px;
428
- height: 48px;
429
- margin: var(--vxe-ui-layout-padding-half);
430
- border: 1px solid var(--vxe-ui-input-border-color);
431
- border-radius: var(--vxe-ui-base-border-radius);
432
-
433
- .ut-upload--image {
434
- position: relative;
435
- width: 48px;
436
- height: 48px;
437
- border-radius: var(--vxe-ui-base-border-radius);
438
-
439
- .ut-image-file--wrapper {
440
- display: flex;
441
- flex-direction: column;
442
- align-items: center;
443
-
444
- .ut-image-file {
445
- font-size: 32px;
446
- }
447
- }
448
- }
449
- }
450
-
451
- .ut-upload--btn:hover {
452
- color: var(--udp-theme-vxeColor);
453
- border: 1px dashed var(--udp-theme-vxeColor);
454
- }
455
-
456
- .ut-upload--image-item-btn-wrapper {
457
- position: absolute;
458
- top: calc(var(--vxe-ui-layout-padding-half) * -1);
459
- right: calc(var(--vxe-ui-layout-padding-half) * -1);
460
- display: inline-flex;
461
- justify-content: center;
462
-
463
- .ut-upload-remove--btn {
464
- z-index: 3;
465
- display: flex;
466
- flex-direction: row;
467
- align-items: center;
468
- justify-content: center;
469
- width: 1.8em;
470
- height: 1.8em;
471
- font-size: 0.8em;
472
- color: #fff;
473
- cursor: pointer;
474
- background-color: var(--vxe-ui-status-error-color);
475
- border-radius: 50%;
476
- }
477
- }
478
-
479
- .ut-image {
480
- height: 100%;
481
- border-radius: var(--vxe-ui-base-border-radius);
482
- }
483
-
484
- .ut-image-hover:hover > .ut-image-file--wrapper {
485
- display: none;
486
- }
487
-
488
- .ut-image-hover:hover > .ut-download-icon {
489
- display: block;
490
- }
491
-
492
- .ut-download-icon {
493
- display: none;
494
- cursor: pointer;
495
- }
496
- </style>
1
+ <!--
2
+ * @Author: wei.li
3
+ * @Date: 2022-11-04 14:53:36
4
+ * @LastEditors: levi7754 levi7754@163.com
5
+ * @LastEditTime: 2026-01-14 10:19:50
6
+ * @Description: form upload
7
+ -->
8
+ <template>
9
+ <div class="flex w-full flex-wrap">
10
+ <div class="ut-upload-list flex flex-wrap items-center">
11
+ <div v-for="(item, idx) in fileList" :key="item" :class="'ut-upload-item--' + size">
12
+ <div class="ut-upload--image flex items-center justify-center">
13
+ <img
14
+ v-if="getItemType(item) === 'img'"
15
+ :src="item.url"
16
+ class="ut-image select-none"
17
+ @click="onPreview(item)"
18
+ />
19
+ <div v-else-if="renderMode === 'item' || dataStatus !== 'detail'" class="ut-image-hover">
20
+ <div class="ut-image-file--wrapper">
21
+ <i :class="getItemType(item)" class="ut-image-file ut-image" style="line-height: 1" />
22
+ <div class="px-1 ut-image" :style="{ fontSize: size === 'small' ? '11px' : '10px' }">
23
+ {{ maskFilenameFromPath(item) }}
24
+ </div>
25
+ </div>
26
+ <i
27
+ class="vxe-icon-download ut-download-icon"
28
+ :style="{ fontSize: renderMode === 'item' ? '60px' : '48px' }"
29
+ @click="onDownload(item)"
30
+ />
31
+ </div>
32
+ <vxe-tooltip v-else :content="item.name" class="cursor-pointer">
33
+ <div class="ut-image-hover">
34
+ <div class="ut-image-file--wrapper">
35
+ <i :class="getItemType(item)" class="ut-image-file ut-image" style="font-size: 32px" />
36
+ <div class="px-1 ut-image" :style="{ fontSize: size === 'small' ? '11px' : '10px' }">
37
+ {{ maskFilenameFromPath(item) }}
38
+ </div>
39
+ </div>
40
+ <i
41
+ class="vxe-icon-download ut-download-icon"
42
+ :style="{ fontSize: renderMode === 'item' ? '60px' : '48px' }"
43
+ @click="onDownload(item)"
44
+ />
45
+ </div>
46
+ </vxe-tooltip>
47
+ </div>
48
+ <div v-if="dataStatus !== 'detail'" class="ut-upload--image-item-btn-wrapper">
49
+ <div class="ut-upload-remove--btn">
50
+ <i class="vxe-icon-close" @click="onRemove(item, idx)" />
51
+ </div>
52
+ </div>
53
+ </div>
54
+ <div
55
+ v-if="dataStatus !== 'detail'"
56
+ :class="'ut-upload-item--' + size"
57
+ class="ut-upload--btn"
58
+ style="border: 1px dashed var(--vxe-ui-input-border-color)"
59
+ @click="onStartUpload"
60
+ >
61
+ <div class="flex items-center justify-center h-full flex-col cursor-pointer ut-upload--btn">
62
+ <span v-if="!loading" class="vxe-icon-add" style="font-size: 18px" />
63
+ <div v-if="!loading" style="padding: 0 1px; font-size: 11px; text-align: center">点击上传</div>
64
+ <vxe-icon v-if="loading" name="spinner" status="primary" roll style="font-size: 24px" />
65
+ <div v-if="loading" style="padding: 0 1px; font-size: 11px; text-align: center">
66
+ {{ progressText }}
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <vxe-upload
72
+ ref="xupload"
73
+ v-model="list"
74
+ style="display: none"
75
+ v-bind="$attrs"
76
+ :limit-count="limitCount"
77
+ :limit-size="limitSize"
78
+ :size="size"
79
+ :mode="mode"
80
+ :single-mode="singleMode"
81
+ :image-types="imageTypes"
82
+ :auto-hidden-button="autoHiddenButton"
83
+ :show-error-status="showErrorStatus"
84
+ :remove-method="handleRemove"
85
+ :upload-method="httpRequest"
86
+ :multiple="multiple"
87
+ :show-download-button="showDownloadButton"
88
+ :show-list="false"
89
+ show-progress
90
+ @upload-success="onSuccess"
91
+ />
92
+ </div>
93
+ </template>
94
+
95
+ <script lang="ts">
96
+ export default { name: 'FormUpload' };
97
+ </script>
98
+
99
+ <script setup lang="ts">
100
+ import { ref, computed, watch, onBeforeMount, getCurrentInstance } from 'vue';
101
+ import { cookies } from '@utogether/utils';
102
+ import compressorjs from 'compressorjs';
103
+ import { isString, isArray } from 'xe-utils';
104
+ import { VxeUI, VxeUploadPropTypes } from 'vxe-pc-ui';
105
+ import axios from 'axios';
106
+
107
+ export interface IProps {
108
+ record: object;
109
+ imageTypes?: Array<string>;
110
+ field: string;
111
+ dataStatus?: string;
112
+ renderMode?: string;
113
+ size?: VxeUploadPropTypes.Size;
114
+ mode?: VxeUploadPropTypes.Mode;
115
+ limitCount?: number;
116
+ limitSize?: number;
117
+ compassorSize?: number;
118
+ quality?: number;
119
+ singleMode?: boolean;
120
+ autoHiddenButton?: boolean;
121
+ showErrorStatus?: boolean;
122
+ showDownloadButton?: boolean;
123
+ multiple?: boolean;
124
+ success?: Function;
125
+ remove?: Function;
126
+ }
127
+ const props = withDefaults(defineProps<IProps>(), {
128
+ record: () => {
129
+ return {};
130
+ },
131
+ imageTypes: () => {
132
+ return ['jpg', 'jpeg', 'png', 'gif'];
133
+ },
134
+ field: '',
135
+ renderMode: 'item',
136
+ dataStatus: '',
137
+ size: 'mini',
138
+ mode: '',
139
+ singleMode: false,
140
+ autoHiddenButton: false,
141
+ showDownloadButton: false,
142
+ multiple: false,
143
+ limitSize: 30,
144
+ limitCount: 9,
145
+ quality: 0.6,
146
+ compassorSize: 2
147
+ });
148
+
149
+ watch(
150
+ () => props.record[props.field],
151
+ val => {
152
+ initData();
153
+ }
154
+ );
155
+
156
+ const instance = getCurrentInstance()!;
157
+ const baseUrl = instance.appContext.config.globalProperties.$url;
158
+ const serviceApi = instance.appContext.config.globalProperties.$serviceApi;
159
+ const progressText = ref('');
160
+ const loading = ref(false);
161
+
162
+ const imgTypes = ['image/gif', 'image/jpeg', 'image/png'];
163
+ const xupload = ref(null);
164
+
165
+ const headers = computed(() => {
166
+ const kTOKENKEY = 'authorized-token';
167
+ const token = cookies.get(kTOKENKEY)!;
168
+ const data = JSON.parse(token);
169
+ const headers = {
170
+ Authorization: 'Bearer ' + data.accessToken
171
+ };
172
+ return headers;
173
+ });
174
+ const list = ref([]);
175
+ const fileList = ref<any>([]);
176
+
177
+ const httpRequest = options => {
178
+ if (imgTypes.includes(options.file.type) && options.file.size > 1024 * 1024 * props.compassorSize) {
179
+ return new Promise((resolve, reject) => {
180
+ new compressorjs(options.file, {
181
+ quality: props.quality,
182
+ mimeType: options.file.type,
183
+ success(result: File) {
184
+ const file = new File([result], result.name, { type: result.type });
185
+ uploadProcess(resolve, reject, file);
186
+ console.log({ result, file });
187
+ }
188
+ });
189
+ });
190
+ } else {
191
+ return new Promise((resolve, reject) => uploadProcess(resolve, reject, options.file));
192
+ }
193
+ };
194
+
195
+ const uploadProcess = (resolve, reject, file) => {
196
+ const formBody = new FormData();
197
+ formBody.append('file', file);
198
+ const url = baseUrl + '/ufil/file/uploadMultipart';
199
+ loading.value = true;
200
+ return axios
201
+ .post(url, formBody, {
202
+ headers: Object.assign({ 'Content-Type': 'multipart/form-data' }, headers.value),
203
+ onUploadProgress(progressEvent) {
204
+ const progress = Math.round((progressEvent.loaded * 100) / (progressEvent.total || 0));
205
+ progressText.value = `进度${progress}%`;
206
+ }
207
+ })
208
+ .then(async response => {
209
+ progressText.value = `处理中...`;
210
+ file.path = response.data.data;
211
+ file.url = `${baseUrl}/ufil${file.path}`;
212
+ fileList.value.push(file);
213
+ console.log(file);
214
+ loading.value = false;
215
+ resolve(file);
216
+ })
217
+ .catch(() => {
218
+ loading.value = false;
219
+ reject(null);
220
+ });
221
+ };
222
+
223
+ const onStartUpload = () => {
224
+ xupload.value.choose();
225
+ };
226
+
227
+ const onSuccess = ({ data }) => {
228
+ if (props.success) {
229
+ return props.success(data);
230
+ } else {
231
+ const field = props.field;
232
+ Object.assign(props.record, { [field]: data.path });
233
+ }
234
+ console.log(props.record, fileList);
235
+ };
236
+
237
+ const handleRemove: VxeUploadPropTypes.RemoveMethod = ({ option }) => {
238
+ if (props.remove) {
239
+ return props.remove(option, fileList);
240
+ }
241
+ emit('remove', option, fileList);
242
+ };
243
+
244
+ const onRemove = async (file, idx) => {
245
+ console.log(file, idx, fileList.value);
246
+ if (file.id) {
247
+ await serviceApi.delete('/ufil/v1/attachment', [file]);
248
+ }
249
+ fileList.value.splice(idx, 1);
250
+ emit('remove', file, fileList);
251
+ };
252
+
253
+ const onPreview = item => {
254
+ // console.log(item.url);
255
+ VxeUI.previewImage({ activeIndex: 0, urlList: [item.url] });
256
+ };
257
+
258
+ const getItemType = item => {
259
+ const type = item.type || item.url.split('.').pop();
260
+ let icon = '';
261
+ switch (type) {
262
+ case 'jpg':
263
+ case 'png':
264
+ case 'jpeg':
265
+ case 'webp':
266
+ case 'image/jpeg':
267
+ case 'image/png':
268
+ case 'image/gif':
269
+ icon = 'img';
270
+ break;
271
+ case 'doc':
272
+ case 'docx':
273
+ icon = 'vxe-icon-file-word';
274
+ break;
275
+ case 'xlsx':
276
+ case 'xls':
277
+ icon = 'vxe-icon-file-excel';
278
+ break;
279
+ case 'pdf':
280
+ icon = 'vxe-icon-file-pdf';
281
+ break;
282
+ case 'txt':
283
+ icon = 'vxe-icon-file-txt';
284
+ break;
285
+ case 'ppt':
286
+ icon = 'vxe-icon-file-ppt';
287
+ break;
288
+ case 'markdown':
289
+ icon = 'vxe-icon-file-markdown';
290
+ break;
291
+ case 'apk':
292
+ icon = 'ri-android-fill';
293
+ break;
294
+ case 'ipa':
295
+ icon = 'ri-apple-line';
296
+ break;
297
+
298
+ default:
299
+ icon = 'vxe-icon-file';
300
+ break;
301
+ }
302
+ return icon;
303
+ };
304
+
305
+ const maskFilenameFromPath = file => {
306
+ let total = props.size === 'small' ? 10 : 9;
307
+ // 提取纯文件名(含扩展名)
308
+ const extractFilename = file => {
309
+ if (file.name) return file.name;
310
+ const path = file.url;
311
+ const separator = path.includes('\\') ? '\\' : '/';
312
+ return path.split(separator).pop();
313
+ };
314
+
315
+ // 处理文件名脱敏
316
+ const maskFilename = filename => {
317
+ const dotPos = filename.lastIndexOf('.');
318
+ const name = dotPos > 0 ? filename.substring(0, dotPos) : filename;
319
+ const ext = dotPos > 0 ? filename.substring(dotPos) : '';
320
+
321
+ if (name.length <= total) return filename;
322
+
323
+ const prefix = props.renderMode === 'item' ? name.substring(0, total - 5) : '';
324
+ const suffix = name.substring(name.length - (total - 6));
325
+ return `${prefix}...${suffix}${ext}`;
326
+ };
327
+
328
+ return maskFilename(extractFilename(file));
329
+ };
330
+
331
+ const onDownload = file => {
332
+ console.log(file);
333
+ };
334
+ const emit = defineEmits<{
335
+ (e: 'remove', file: any, fileList: any): void;
336
+ }>();
337
+
338
+ const initData = () => {
339
+ const fileUrl = props.record[props.field];
340
+ fileList.value = [];
341
+ if (fileUrl) {
342
+ if (fileUrl && isString(fileUrl)) {
343
+ const name = fileUrl.match(/[^\/\\]+$/)[0];
344
+ const file = { name, url: `${baseUrl}/ufil${fileUrl}` };
345
+ if (props.singleMode) {
346
+ fileList.value = file;
347
+ } else {
348
+ fileList.value.push(file);
349
+ }
350
+ } else if (isArray(fileUrl)) {
351
+ fileUrl.forEach(file => {
352
+ if (isString(file)) {
353
+ const name = file.match(/[^\/\\]+$/)[0];
354
+ fileList.value.push({ name, url: `${baseUrl}/ufil${file}` });
355
+ } else {
356
+ fileList.value.push({
357
+ name: file.name,
358
+ id: file.id,
359
+ url: `${baseUrl}/ufil${file.realpath}`
360
+ });
361
+ }
362
+ });
363
+ }
364
+ }
365
+ };
366
+
367
+ onBeforeMount(() => {
368
+ initData();
369
+ });
370
+ </script>
371
+
372
+ <style lang="scss">
373
+ .ut-upload-item--small {
374
+ position: relative;
375
+ width: var(--vxe-ui-upload-image-wh-small);
376
+ height: var(--vxe-ui-upload-image-wh-small);
377
+ margin: var(--vxe-ui-layout-padding-half);
378
+ border: 1px solid var(--vxe-ui-input-border-color);
379
+ border-radius: var(--vxe-ui-base-border-radius);
380
+
381
+ .ut-upload--image {
382
+ position: relative;
383
+ width: var(--vxe-ui-upload-image-wh-small);
384
+ height: var(--vxe-ui-upload-image-wh-small);
385
+ border-radius: var(--vxe-ui-base-border-radius);
386
+
387
+ .ut-image-file--wrapper {
388
+ display: flex;
389
+ flex-direction: column;
390
+ align-items: center;
391
+
392
+ .ut-image-file {
393
+ font-size: 48px;
394
+ }
395
+ }
396
+ }
397
+ }
398
+
399
+ .ut-upload-item--mini {
400
+ position: relative;
401
+ width: var(--vxe-ui-upload-image-wh-mini);
402
+ height: var(--vxe-ui-upload-image-wh-mini);
403
+ margin: var(--vxe-ui-layout-padding-half);
404
+ border: 1px solid var(--vxe-ui-input-border-color);
405
+ border-radius: var(--vxe-ui-base-border-radius);
406
+
407
+ .ut-upload--image {
408
+ position: relative;
409
+ width: var(--vxe-ui-upload-image-wh-mini);
410
+ height: var(--vxe-ui-upload-image-wh-mini);
411
+ border-radius: var(--vxe-ui-base-border-radius);
412
+
413
+ .ut-image-file--wrapper {
414
+ display: flex;
415
+ flex-direction: column;
416
+ align-items: center;
417
+
418
+ .ut-image-file {
419
+ font-size: 42px;
420
+ }
421
+ }
422
+ }
423
+ }
424
+
425
+ .ut-upload-item--xs {
426
+ position: relative;
427
+ width: 48px;
428
+ height: 48px;
429
+ margin: var(--vxe-ui-layout-padding-half);
430
+ border: 1px solid var(--vxe-ui-input-border-color);
431
+ border-radius: var(--vxe-ui-base-border-radius);
432
+
433
+ .ut-upload--image {
434
+ position: relative;
435
+ width: 48px;
436
+ height: 48px;
437
+ border-radius: var(--vxe-ui-base-border-radius);
438
+
439
+ .ut-image-file--wrapper {
440
+ display: flex;
441
+ flex-direction: column;
442
+ align-items: center;
443
+
444
+ .ut-image-file {
445
+ font-size: 32px;
446
+ }
447
+ }
448
+ }
449
+ }
450
+
451
+ .ut-upload--btn:hover {
452
+ color: var(--udp-theme-vxeColor);
453
+ border: 1px dashed var(--udp-theme-vxeColor);
454
+ }
455
+
456
+ .ut-upload--image-item-btn-wrapper {
457
+ position: absolute;
458
+ top: calc(var(--vxe-ui-layout-padding-half) * -1);
459
+ right: calc(var(--vxe-ui-layout-padding-half) * -1);
460
+ display: inline-flex;
461
+ justify-content: center;
462
+
463
+ .ut-upload-remove--btn {
464
+ z-index: 3;
465
+ display: flex;
466
+ flex-direction: row;
467
+ align-items: center;
468
+ justify-content: center;
469
+ width: 1.8em;
470
+ height: 1.8em;
471
+ font-size: 0.8em;
472
+ color: #fff;
473
+ cursor: pointer;
474
+ background-color: var(--vxe-ui-status-error-color);
475
+ border-radius: 50%;
476
+ }
477
+ }
478
+
479
+ .ut-image {
480
+ height: 100%;
481
+ border-radius: var(--vxe-ui-base-border-radius);
482
+ }
483
+
484
+ .ut-image-hover:hover > .ut-image-file--wrapper {
485
+ display: none;
486
+ }
487
+
488
+ .ut-image-hover:hover > .ut-download-icon {
489
+ display: block;
490
+ }
491
+
492
+ .ut-download-icon {
493
+ display: none;
494
+ cursor: pointer;
495
+ }
496
+ </style>