@utogether/udp-core 1.0.1-beta.1 → 1.0.1-beta.11

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 (147) hide show
  1. package/build/plugins.ts +39 -32
  2. package/dist/{403-Dp617CWX.js → 403-Dsgsr2M-.js} +1 -1
  3. package/dist/{404-Cz_Axb6Y.js → 404-DmPHEfqM.js} +1 -1
  4. package/dist/{500-BGCtRNse.js → 500-OYEgHR2a.js} +1 -1
  5. package/dist/{AuthorityInfo-DGGfm7IS.js → AuthorityInfo-B1H1txG-.js} +1 -1
  6. package/dist/AuthorityInfo.vue_vue_type_style_index_0_lang-BUlmQtjQ.js +100 -0
  7. package/dist/{Company-IV3GTnzY.js → Company-C8ojgx93.js} +3 -3
  8. package/dist/{CompanyPanel-qV-_VtoL.js → CompanyPanel-Dageer1t.js} +16 -16
  9. package/dist/{Department-B3W-OxW8.js → Department-Dr94_85I.js} +3 -3
  10. package/dist/{DepartmentPanel-Cw3OWxE7.js → DepartmentPanel-Bc5r1HpT.js} +1 -1
  11. package/dist/{DesignPanel-BFxR2fHJ.js → DesignPanel-CcWt1Myp.js} +1 -1
  12. package/dist/{DesignPanel.vue_vue_type_style_index_0_lang-DljbeFba.js → DesignPanel.vue_vue_type_style_index_0_lang-CKA6nx1l.js} +24 -26
  13. package/dist/DictView-BHj6wexC.js +110 -0
  14. package/dist/InvOrganization-DP3Le9g-.js +72 -0
  15. package/dist/Org-BRGHTwsw.js +39 -0
  16. package/dist/{Preview-BlDMmpdR.js → Preview-CqMaUD_3.js} +1 -1
  17. package/dist/{ReportDefine-Cub_85LA.js → ReportDefine-PEQdTCOY.js} +1 -1
  18. package/dist/{ReportDesign-hFhq5UVE.js → ReportDesign-CB4bv8ba.js} +43 -43
  19. package/dist/{ReportQuery-ChkWEyxT.js → ReportQuery-B7t4lMK0.js} +1 -1
  20. package/dist/{ReportQueryFrom-KVyD_8Dj.js → ReportQueryFrom-VBxUIMRJ.js} +1 -1
  21. package/dist/{ReportQueryFrom.vue_vue_type_style_index_0_lang-CLNODquq.js → ReportQueryFrom.vue_vue_type_style_index_0_lang-HeLA4Qs5.js} +1 -1
  22. package/dist/{ReportTemplate-ag9NDvh2.js → ReportTemplate-DNkv7F5p.js} +28 -28
  23. package/dist/{Role-_q3lQ8CZ.js → Role-BGfTpZK4.js} +6 -6
  24. package/dist/{RoleAssign-DZb9IRsm.js → RoleAssign-DrKFWZzA.js} +8 -8
  25. package/dist/{RolePanel-CsLsz-Ds.js → RolePanel-3lvRh7aS.js} +1 -1
  26. package/dist/{RolePanel-BQb1LlhD.js → RolePanel-BRI5QYe4.js} +1 -1
  27. package/dist/RolePanel.vue_vue_type_script_setup_true_lang-BXKZb7yt.js +132 -0
  28. package/dist/{RolePanel.vue_vue_type_script_setup_true_lang-BrnRgHEk.js → RolePanel.vue_vue_type_script_setup_true_lang-Cci_Ysty.js} +44 -38
  29. package/dist/{ScrollPanel.vue_vue_type_style_index_0_lang-CaFKRwXu.js → ScrollPanel.vue_vue_type_style_index_0_lang-BmId6zzA.js} +21 -21
  30. package/dist/{Staff-BSf9Ypbk.js → Staff-Dr0dq3fW.js} +3 -3
  31. package/dist/{StaffInfo-BNKasyMF.js → StaffInfo-_N9g2hPa.js} +1 -1
  32. package/dist/{StaffInfo.vue_vue_type_script_setup_true_lang-DDZ7ukd0.js → StaffInfo.vue_vue_type_script_setup_true_lang-DiTJY2ug.js} +11 -11
  33. package/dist/{StaffPanel-Bpq0WVlH.js → StaffPanel-mxJQiW3G.js} +1 -1
  34. package/dist/{StaffPanel.vue_vue_type_script_setup_true_lang-BhiJ0Q-Q.js → StaffPanel.vue_vue_type_script_setup_true_lang-GRGn-5t6.js} +2 -2
  35. package/dist/{SysUser-FAABuNti.js → SysUser-DgA19hZE.js} +2 -2
  36. package/dist/{SysUserPanel-PxJeOgHm.js → SysUserPanel-Bc85z5_K.js} +1 -1
  37. package/dist/SysUserPanel.vue_vue_type_script_setup_true_lang-BHDrP1Pk.js +294 -0
  38. package/dist/{SystemMenu-C-7NAGon.js → SystemMenu-Be-gFIie.js} +26 -26
  39. package/dist/{UserInfo-ClXKtyGo.js → UserInfo-BCYuwFmr.js} +1 -1
  40. package/dist/{UserInfo.vue_vue_type_style_index_0_lang-8N7P4Hl7.js → UserInfo.vue_vue_type_style_index_0_lang-B1lgokO6.js} +39 -37
  41. package/dist/{childView-C_HmDQNd.js → childView-DhcBr1UA.js} +1 -1
  42. package/dist/{childView-uUlBcTza.js → childView-Dk4zZoXd.js} +1 -1
  43. package/dist/{childView.vue_vue_type_style_index_0_lang-Ckjmw6wJ.js → childView.vue_vue_type_style_index_0_lang-CVnLnl03.js} +40 -40
  44. package/dist/{childView.vue_vue_type_style_index_0_lang-y0sDvYx5.js → childView.vue_vue_type_style_index_0_lang-D4GYVhOc.js} +1 -1
  45. package/dist/{code-rule-AgCVDKFy.js → code-rule-BSwPwuAL.js} +9 -10
  46. package/dist/core.es.js +14 -11
  47. package/dist/{cron-task-C6FgQxTi.js → cron-task-T1ztyra7.js} +1 -1
  48. package/dist/{frameView-BDgISK7N.js → frameView-BDgngiBq.js} +1 -1
  49. package/dist/img/l_img.svg +1 -1
  50. package/dist/img/minicolors.png +0 -0
  51. package/dist/img/v_img.svg +1 -1
  52. package/dist/index-BSj2AtVL.js +4468 -0
  53. package/dist/{layoutView-yb3DV2DQ.js → layoutView-i7dLLBzJ.js} +1181 -1176
  54. package/dist/{login-RRpljbkm.js → login-K5Ludp8x.js} +105 -113
  55. package/dist/{login-log-CvVnyGi3.js → login-log-kqKzKTto.js} +1 -1
  56. package/dist/{lov-view-C9-rjzZR.js → lov-view-lAexiucu.js} +2 -2
  57. package/dist/{menuInfo-CzPQyFhp.js → menuInfo-BaN5YZPp.js} +1 -1
  58. package/dist/{menuInfo.vue_vue_type_style_index_0_lang-BumXunCg.js → menuInfo.vue_vue_type_style_index_0_lang-yI4QOYxG.js} +100 -97
  59. package/dist/{pda-app-Dvy3U-b6.js → pda-app-DEtDXFvM.js} +211 -197
  60. package/dist/{resource-Fy0lFkSV.js → resource-Bztmz7pn.js} +15 -15
  61. package/dist/{su-welcome-DYvSCUST.js → su-welcome-Bx8nBPi9.js} +109 -110
  62. package/dist/sys-config-nvrtYGgi.js +290 -0
  63. package/dist/udp-core.css +2 -2
  64. package/dist/{utogether-CjmJiHoE.js → utogether-yPnwDAIH.js} +1 -1
  65. package/index.ts +40 -36
  66. package/package.json +18 -18
  67. package/src/App.vue +65 -70
  68. package/src/api/index.ts +31 -31
  69. package/src/components/SuCharts/src/UserInfo.vue +78 -78
  70. package/src/components/SuScrollTree/ScrollPanel.vue +3 -3
  71. package/src/components/udp/count-down.vue +536 -0
  72. package/src/components/udp/flip-down/FlipCard/flip-card.vue +251 -0
  73. package/src/components/udp/flip-down/FlipCard/interfaces.ts +4 -0
  74. package/src/components/udp/flip-down/FlipClock/flip-clock.vue +113 -0
  75. package/src/components/udp/form-upload.vue +414 -64
  76. package/src/components/udp/form.vue +112 -0
  77. package/src/components/udp/grid.vue +495 -0
  78. package/src/components/udp/index.ts +10 -4
  79. package/src/components/udp/lov.vue +388 -0
  80. package/src/components/udp/modal-form.vue +13 -3
  81. package/src/components/udp/modal-grid.vue +298 -0
  82. package/src/components/udp/upload.vue +423 -0
  83. package/src/components/udp/utils.ts +447 -40
  84. package/src/directives/permission/index.ts +1 -1
  85. package/src/layout/components/lay-navbar/index.vue +239 -237
  86. package/src/layout/components/lay-panel/index.vue +150 -150
  87. package/src/layout/components/lay-search/index.vue +25 -25
  88. package/src/layout/components/lay-select-org/index.vue +64 -69
  89. package/src/layout/components/lay-sidebar/breadCrumb.vue +1 -1
  90. package/src/layout/components/lay-sidebar/horizontal.vue +190 -191
  91. package/src/layout/components/lay-sidebar/mixNav.vue +260 -258
  92. package/src/layout/components/lay-sidebar/sidebar-logo.vue +92 -98
  93. package/src/layout/components/lay-tag/index.vue +625 -625
  94. package/src/layout/hooks/useNav.ts +173 -173
  95. package/src/layout/layoutView.vue +215 -215
  96. package/src/main.ts +119 -109
  97. package/src/plugins/i18n/en.ts +302 -289
  98. package/src/plugins/i18n/zh.ts +349 -337
  99. package/src/plugins/vxe-table/index.ts +53 -46
  100. package/src/plugins/vxe-table/render.tsx +956 -817
  101. package/src/router/index.ts +177 -183
  102. package/src/router/modules/remaining.ts +58 -83
  103. package/src/store/modules/permission.ts +1 -4
  104. package/src/style/button.scss +85 -78
  105. package/src/style/tailwind.css +1 -68
  106. package/src/style/vxetable.scss +44 -11
  107. package/src/utils/authority/index.ts +1 -1
  108. package/src/utils/{http → udp/http}/index.ts +283 -297
  109. package/src/utils/{http → udp/http}/types.d.ts +8 -5
  110. package/src/utils/udp/useRender.ts +17 -6
  111. package/src/views/login/login-view.vue +300 -314
  112. package/src/views/organization/company/CompanyPanel.vue +259 -259
  113. package/src/views/organization/inv-org/InvOrganization.vue +68 -54
  114. package/src/views/organization/org/Org.vue +9 -5
  115. package/src/views/system/menu/SystemMenu.vue +183 -197
  116. package/src/views/system/menu/menuInfo.vue +363 -371
  117. package/src/views/system/role/AuthorityInfo.vue +19 -15
  118. package/src/views/system/role/Role.vue +1 -5
  119. package/src/views/system/role/RolePanel.vue +11 -2
  120. package/src/views/system/role/UserInfo.vue +195 -193
  121. package/src/views/system/role-assign/RoleAssign.vue +57 -57
  122. package/src/views/system/role-assign/RolePanel.vue +139 -136
  123. package/src/views/system/sys/sys-config.vue +287 -291
  124. package/src/views/system/sysUser/SysUserPanel.vue +278 -278
  125. package/src/views/uapp/pda/pda-app.vue +208 -176
  126. package/src/views/udev/dict/DictView.vue +118 -106
  127. package/src/views/udev/dict/childView.vue +7 -7
  128. package/src/views/udev/lov/lov-view.vue +91 -91
  129. package/src/views/ufile/aggregation/File.vue +5 -5
  130. package/src/views/ufile/file/water-mark.vue +14 -14
  131. package/src/views/uhome/su-welcome.vue +3 -3
  132. package/src/views/ulogin/login.vue +12 -8
  133. package/src/views/upms/user/login-log.vue +1 -1
  134. package/src/views/urpt/design/DesignPanel.vue +507 -526
  135. package/src/views/urpt/design/ReportDesign.vue +15 -19
  136. package/src/views/urpt/static-resource/resource.vue +3 -3
  137. package/src/views/urpt/template/ReportTemplate.vue +7 -7
  138. package/types/global.d.ts +2 -1
  139. package/vite.config.ts +6 -2
  140. package/dist/AuthorityInfo.vue_vue_type_style_index_0_lang-BqccGW7v.js +0 -102
  141. package/dist/DictView-C-i7e4hZ.js +0 -95
  142. package/dist/InvOrganization-cfT6riGU.js +0 -260
  143. package/dist/Org-CA7vTDIF.js +0 -35
  144. package/dist/RolePanel.vue_vue_type_script_setup_true_lang-cmW7zBLu.js +0 -126
  145. package/dist/SysUserPanel.vue_vue_type_script_setup_true_lang-njefUln5.js +0 -288
  146. package/dist/index-DzOzUkf6.js +0 -3388
  147. package/dist/sys-config-DJ1vNQTy.js +0 -277
@@ -2,25 +2,89 @@
2
2
  * @Author: wei.li
3
3
  * @Date: 2022-11-04 14:53:36
4
4
  * @LastEditors: levi7754 levi7754@163.com
5
- * @LastEditTime: 2025-07-14 18:32:47
5
+ * @LastEditTime: 2025-08-18 11:17:57
6
6
  * @Description: form upload
7
7
  -->
8
8
  <template>
9
- <div v-spinning="loading" class="ut-upload-wrapper">
10
- <el-upload
11
- v-model:file-list="fileList"
12
- :action="baseUrl + '/ufil/file/uploadMultipart'"
13
- list-type="picture-card"
14
- :headers="headers"
15
- :before-upload="beforeUpload"
16
- :on-success="onSuccess"
17
- :on-preview="onPreview"
18
- :on-remove="handleRemove"
19
- >
20
- <template #default>
21
- <i class="ri-add-line" />
22
- </template>
23
- </el-upload>
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"><i class="vxe-icon-close" @click="onRemove(item, idx)" /></div>
50
+ </div>
51
+ </div>
52
+ <div
53
+ v-if="dataStatus !== 'detail'"
54
+ :class="'ut-upload-item--' + size"
55
+ class="ut-upload--btn"
56
+ style="border: 1px dashed var(--vxe-ui-input-border-color)"
57
+ @click="onStartUpload"
58
+ >
59
+ <div class="flex items-center justify-center h-full flex-col cursor-pointer ut-upload--btn">
60
+ <span v-if="!loading" class="vxe-icon-add" style="font-size: 18px" />
61
+ <div v-if="!loading" style="font-size: 11px; padding: 0 1px; text-align: center">点击上传</div>
62
+ <vxe-icon v-if="loading" name="spinner" status="primary" roll style="font-size: 24px" />
63
+ <div v-if="loading" style="font-size: 11px; padding: 0 1px; text-align: center">{{ progressText }}</div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ <vxe-upload
68
+ ref="xupload"
69
+ v-model="list"
70
+ style="display: none"
71
+ v-bind="$attrs"
72
+ :limit-count="limitCount"
73
+ :limit-size="limitSize"
74
+ :size="size"
75
+ :mode="mode"
76
+ :single-mode="singleMode"
77
+ :image-types="imageTypes"
78
+ :auto-hidden-button="autoHiddenButton"
79
+ :show-error-status="showErrorStatus"
80
+ :remove-method="handleRemove"
81
+ :upload-method="httpRequest"
82
+ :multiple="multiple"
83
+ :show-download-button="showDownloadButton"
84
+ :show-list="false"
85
+ show-progress
86
+ @upload-success="onSuccess"
87
+ />
24
88
  </div>
25
89
  </template>
26
90
 
@@ -29,31 +93,63 @@ export default { name: 'FormUpload' };
29
93
  </script>
30
94
 
31
95
  <script setup lang="ts">
32
- import { ref, computed, getCurrentInstance } from 'vue';
96
+ import { ref, computed, onBeforeMount, getCurrentInstance } from 'vue';
33
97
  import { cookies } from '@utogether/utils';
34
- // import type { UploadProps } from "element-plus";
35
- // import { loadEnv } from "@build/index";
36
-
37
- // const { VITE_BASE_URL } = loadEnv();
98
+ import compressorjs from 'compressorjs';
99
+ import { isString, isArray } from 'xe-utils';
100
+ import { VxeUI, VxeUploadPropTypes } from 'vxe-pc-ui';
101
+ import axios from 'axios';
38
102
 
39
103
  export interface IProps {
40
104
  record: object;
105
+ imageTypes?: Array<string>;
41
106
  field: string;
42
- limit?: number;
43
- beforeUpload?: Function;
44
- onSuccess?: Function;
45
- onRemove?: Function;
107
+ dataStatus?: string;
108
+ renderMode?: string;
109
+ size?: VxeUploadPropTypes.Size;
110
+ mode?: VxeUploadPropTypes.Mode;
111
+ limitCount?: number;
112
+ limitSize?: number;
113
+ compassorSize?: number;
114
+ quality?: number;
115
+ singleMode?: boolean;
116
+ autoHiddenButton?: boolean;
117
+ showErrorStatus?: boolean;
118
+ showDownloadButton?: boolean;
119
+ multiple?: boolean;
120
+ success?: Function;
121
+ remove?: Function;
46
122
  }
47
123
  const props = withDefaults(defineProps<IProps>(), {
48
124
  record: () => {
49
125
  return {};
50
126
  },
127
+ imageTypes: () => {
128
+ return ['jpg', 'jpeg', 'png', 'gif'];
129
+ },
51
130
  field: '',
52
- limit: 1
131
+ renderMode: 'item',
132
+ dataStatus: '',
133
+ size: 'mini',
134
+ mode: '',
135
+ singleMode: false,
136
+ autoHiddenButton: false,
137
+ showDownloadButton: false,
138
+ multiple: false,
139
+ limitSize: 30,
140
+ limitCount: 9,
141
+ quality: 0.6,
142
+ compassorSize: 2
53
143
  });
54
144
 
55
145
  const instance = getCurrentInstance()!;
56
146
  const baseUrl = instance.appContext.config.globalProperties.$url;
147
+ const serviceApi = instance.appContext.config.globalProperties.$serviceApi;
148
+ const progressText = ref('');
149
+ const loading = ref(false);
150
+
151
+ const imgTypes = ['image/gif', 'image/jpeg', 'image/png'];
152
+ const xupload = ref(null);
57
153
 
58
154
  const headers = computed(() => {
59
155
  const kTOKENKEY = 'authorized-token';
@@ -64,69 +160,323 @@ const headers = computed(() => {
64
160
  };
65
161
  return headers;
66
162
  });
163
+ const list = [];
164
+ const fileList = ref<any>([]);
67
165
 
68
- const fileList = ref([]);
69
- const loading = ref(false);
70
- const display = ref('flex');
71
-
72
- const beforeUpload = file => {
73
- if (props.beforeUpload) {
74
- return props.beforeUpload(file);
166
+ const httpRequest = options => {
167
+ if (imgTypes.includes(options.file.type) && options.file.size > 1024 * 1024 * props.compassorSize) {
168
+ return new Promise((resolve, reject) => {
169
+ new compressorjs(options.file, {
170
+ quality: props.quality,
171
+ mimeType: options.file.type,
172
+ success(result: File) {
173
+ const file = new File([result], result.name, { type: result.type });
174
+ uploadProcess(resolve, reject, file);
175
+ console.log({ result, file });
176
+ }
177
+ });
178
+ });
179
+ } else {
180
+ return new Promise((resolve, reject) => uploadProcess(resolve, reject, options.file));
75
181
  }
76
- console.log(file);
182
+ };
183
+
184
+ const uploadProcess = (resolve, reject, file) => {
185
+ const formBody = new FormData();
186
+ formBody.append('file', file);
187
+ const url = baseUrl + '/ufil/file/uploadMultipart';
77
188
  loading.value = true;
78
- return true;
189
+ return axios
190
+ .post(url, formBody, {
191
+ headers: Object.assign({ 'Content-Type': 'multipart/form-data' }, headers.value),
192
+ onUploadProgress(progressEvent) {
193
+ const progress = Math.round((progressEvent.loaded * 100) / (progressEvent.total || 0));
194
+ progressText.value = `进度${progress}%`;
195
+ }
196
+ })
197
+ .then(async response => {
198
+ progressText.value = `处理中...`;
199
+ file.path = response.data.data;
200
+ file.url = `${baseUrl}/ufil${file.path}`;
201
+ fileList.value.push(file);
202
+ console.log(file);
203
+ loading.value = false;
204
+ resolve(file);
205
+ })
206
+ .catch(() => {
207
+ loading.value = false;
208
+ reject(null);
209
+ });
210
+
211
+ return serviceApi
212
+ .post('/ufil/file/uploadMultipart', formBody, { headers })
213
+ .then(async data => {
214
+ file.path = data;
215
+ file.url = `${baseUrl}/ufil${file.path}`;
216
+ fileList.value.push(file);
217
+ console.log(file);
218
+ resolve(file);
219
+ })
220
+ .catch(() => {
221
+ reject(null);
222
+ });
223
+ };
224
+
225
+ const onStartUpload = () => {
226
+ xupload.value.choose();
79
227
  };
80
228
 
81
- const onSuccess = response => {
82
- loading.value = false;
83
- if (props.onSuccess) {
84
- return props.onSuccess(response);
229
+ const onSuccess = ({ data }) => {
230
+ if (props.success) {
231
+ return props.success(data);
85
232
  } else {
86
233
  const field = props.field;
87
- Object.assign(props.record, { [field]: response.data });
88
- display.value = props.limit > fileList.value.length ? 'flex' : 'none';
234
+ Object.assign(props.record, { [field]: data.path });
89
235
  }
236
+ console.log(props.record, fileList);
90
237
  };
91
238
 
92
- const handleRemove = (uploadFile, uploadFiles) => {
93
- if (props.onRemove) {
94
- return props.onRemove(uploadFile, fileList);
239
+ const handleRemove: VxeUploadPropTypes.RemoveMethod = ({ option }) => {
240
+ if (props.remove) {
241
+ return props.remove(option, fileList);
95
242
  }
96
- loading.value = false;
97
- console.log(uploadFile, uploadFiles);
98
- display.value = 'flex';
99
- emit('remove', uploadFile, fileList);
243
+ emit('remove', option, fileList);
244
+ };
245
+
246
+ const onRemove = (file, idx) => {
247
+ fileList.value.splice(idx, 1);
248
+ emit('remove', file, fileList);
249
+ };
250
+
251
+ const onPreview = item => {
252
+ // console.log(item.url);
253
+ VxeUI.previewImage({ activeIndex: 0, urlList: [item.url] });
254
+ };
255
+
256
+ const getItemType = item => {
257
+ const type = item.type || item.url.split('.').pop();
258
+ let icon = '';
259
+ switch (type) {
260
+ case 'jpg':
261
+ case 'png':
262
+ case 'jpeg':
263
+ case 'webp':
264
+ case 'image/jpeg':
265
+ case 'image/png':
266
+ case 'image/gif':
267
+ icon = 'img';
268
+ break;
269
+ case 'doc':
270
+ case 'docx':
271
+ icon = 'vxe-icon-file-word';
272
+ break;
273
+ case 'xlsx':
274
+ case 'xls':
275
+ icon = 'vxe-icon-file-excel';
276
+ break;
277
+ case 'pdf':
278
+ icon = 'vxe-icon-file-pdf';
279
+ break;
280
+ case 'txt':
281
+ icon = 'vxe-icon-file-txt';
282
+ break;
283
+ case 'ppt':
284
+ icon = 'vxe-icon-file-ppt';
285
+ break;
286
+ case 'markdown':
287
+ icon = 'vxe-icon-file-markdown';
288
+ break;
289
+ case 'apk':
290
+ icon = 'ri-android-fill';
291
+ break;
292
+ case 'ipa':
293
+ icon = 'ri-apple-line';
294
+ break;
295
+
296
+ default:
297
+ icon = 'vxe-icon-file';
298
+ break;
299
+ }
300
+ return icon;
301
+ };
302
+
303
+ const maskFilenameFromPath = file => {
304
+ let total = props.size === 'small' ? 10 : 9;
305
+ // 提取纯文件名(含扩展名)
306
+ const extractFilename = file => {
307
+ if (file.name) return file.name;
308
+ const path = file.url;
309
+ const separator = path.includes('\\') ? '\\' : '/';
310
+ return path.split(separator).pop();
311
+ };
312
+
313
+ // 处理文件名脱敏
314
+ const maskFilename = filename => {
315
+ const dotPos = filename.lastIndexOf('.');
316
+ const name = dotPos > 0 ? filename.substring(0, dotPos) : filename;
317
+ const ext = dotPos > 0 ? filename.substring(dotPos) : '';
318
+
319
+ if (name.length <= total) return filename;
320
+
321
+ const prefix = props.renderMode === 'item' ? name.substring(0, total - 5) : '';
322
+ const suffix = name.substring(name.length - (total - 6));
323
+ return `${prefix}...${suffix}${ext}`;
324
+ };
325
+
326
+ return maskFilename(extractFilename(file));
100
327
  };
101
328
 
102
- const onPreview = () => {
103
- console.log('....');
329
+ const onDownload = file => {
330
+ console.log(file);
104
331
  };
105
332
  const emit = defineEmits<{
106
- (e: 'remove', file: File, fileList: any): void;
333
+ (e: 'remove', file: any, fileList: any): void;
107
334
  }>();
335
+
336
+ onBeforeMount(() => {
337
+ const fileUrl = props.record[props.field];
338
+ fileList.value = [
339
+ // { name: '深圳友聚信息技术开发文档.doc', url: `xxx.pdf`, _X_KEY: '1111e' },
340
+ // { name: '深圳友聚信息技术开发文档.doc', url: `xxx.xlsx`, _X_KEY: '111122' },
341
+ // {
342
+ // name: '15806d21e3f445dcae0e2e0825c76a79.apk',
343
+ // url: `${baseUrl}/ufil/resource/image/20250817/15806d21e3f445dcae0e2e0825c76a79.apk`
344
+ // }
345
+ ];
346
+ if (fileUrl) {
347
+ if (fileUrl && isString(fileUrl)) {
348
+ const name = fileUrl.match(/[^\/\\]+$/)[0];
349
+ const file = { name, url: `${baseUrl}/ufil${fileUrl}` };
350
+ if (props.singleMode) {
351
+ fileList.value = file;
352
+ } else {
353
+ fileList.value.push(file);
354
+ }
355
+ } else if (isArray(fileUrl)) {
356
+ fileUrl.forEach(url => {
357
+ const name = url.match(/[^\/\\]+$/)[0];
358
+ fileList.value.push({ name, url: `${baseUrl}/ufil${url}` });
359
+ });
360
+ }
361
+ }
362
+ console.log('fileList');
363
+ });
108
364
  </script>
109
365
 
110
366
  <style lang="scss">
111
- .ut-upload-wrapper {
112
- .el-upload--picture-card {
113
- display: v-bind(display);
114
- width: 60px !important;
115
- height: 60px !important;
367
+ .ut-upload-item--small {
368
+ position: relative;
369
+ width: var(--vxe-ui-upload-image-wh-small);
370
+ height: var(--vxe-ui-upload-image-wh-small);
371
+
372
+ margin: var(--vxe-ui-layout-padding-half);
373
+ border: 1px solid var(--vxe-ui-input-border-color);
374
+ border-radius: var(--vxe-ui-base-border-radius);
375
+ .ut-upload--image {
376
+ position: relative;
377
+ border-radius: var(--vxe-ui-base-border-radius);
378
+ width: var(--vxe-ui-upload-image-wh-small);
379
+ height: var(--vxe-ui-upload-image-wh-small);
380
+ .ut-image-file--wrapper {
381
+ display: flex;
382
+ flex-direction: column;
383
+ align-items: center;
384
+ .ut-image-file {
385
+ font-size: 48px;
386
+ }
387
+ }
116
388
  }
389
+ }
390
+
391
+ .ut-upload-item--mini {
392
+ position: relative;
393
+ width: var(--vxe-ui-upload-image-wh-mini);
394
+ height: var(--vxe-ui-upload-image-wh-mini);
117
395
 
118
- .el-upload-list--picture-card .el-upload-list__item {
119
- width: 60px !important;
120
- height: 60px !important;
396
+ margin: var(--vxe-ui-layout-padding-half);
397
+ border: 1px solid var(--vxe-ui-input-border-color);
398
+ border-radius: var(--vxe-ui-base-border-radius);
399
+ .ut-upload--image {
400
+ position: relative;
401
+ border-radius: var(--vxe-ui-base-border-radius);
402
+ width: var(--vxe-ui-upload-image-wh-mini);
403
+ height: var(--vxe-ui-upload-image-wh-mini);
404
+ .ut-image-file--wrapper {
405
+ display: flex;
406
+ flex-direction: column;
407
+ align-items: center;
408
+ .ut-image-file {
409
+ font-size: 42px;
410
+ }
411
+ }
121
412
  }
413
+ }
122
414
 
123
- .el-upload-list--picture-card .el-progress {
124
- width: 50px !important;
415
+ .ut-upload-item--xs {
416
+ position: relative;
417
+ width: 48px;
418
+ height: 48px;
125
419
 
126
- .el-progress-circle {
127
- width: 50px !important;
128
- height: 50px !important;
420
+ margin: var(--vxe-ui-layout-padding-half);
421
+ border: 1px solid var(--vxe-ui-input-border-color);
422
+ border-radius: var(--vxe-ui-base-border-radius);
423
+ .ut-upload--image {
424
+ position: relative;
425
+ border-radius: var(--vxe-ui-base-border-radius);
426
+ width: 48px;
427
+ height: 48px;
428
+ .ut-image-file--wrapper {
429
+ display: flex;
430
+ flex-direction: column;
431
+ align-items: center;
432
+ .ut-image-file {
433
+ font-size: 32px;
434
+ }
129
435
  }
130
436
  }
131
437
  }
438
+
439
+ .ut-upload--btn:hover {
440
+ border: 1px dashed var(--udp-theme-vxeColor);
441
+ color: var(--udp-theme-vxeColor);
442
+ }
443
+
444
+ .ut-upload--image-item-btn-wrapper {
445
+ display: inline-flex;
446
+ justify-content: center;
447
+ position: absolute;
448
+ top: calc(var(--vxe-ui-layout-padding-half) * -1);
449
+ right: calc(var(--vxe-ui-layout-padding-half) * -1);
450
+ .ut-upload-remove--btn {
451
+ display: flex;
452
+ flex-direction: row;
453
+ justify-content: center;
454
+ align-items: center;
455
+ width: 1.8em;
456
+ height: 1.8em;
457
+ font-size: 0.8em;
458
+ border-radius: 50%;
459
+ color: #fff;
460
+ background-color: var(--vxe-ui-status-error-color);
461
+ z-index: 3;
462
+ cursor: pointer;
463
+ }
464
+ }
465
+
466
+ .ut-image {
467
+ height: 100%;
468
+ border-radius: var(--vxe-ui-base-border-radius);
469
+ }
470
+
471
+ .ut-image-hover:hover > .ut-image-file--wrapper {
472
+ display: none;
473
+ }
474
+
475
+ .ut-image-hover:hover > .ut-download-icon {
476
+ display: block;
477
+ }
478
+ .ut-download-icon {
479
+ display: none;
480
+ cursor: pointer;
481
+ }
132
482
  </style>
@@ -0,0 +1,112 @@
1
+ <!--
2
+ * @Author: wei.li
3
+ * @Date: 2022-03-02 17:07:59
4
+ * @LastEditors: levi7754 levi7754@163.com
5
+ * @LastEditTime: 2025-08-19 10:19:16
6
+ * @Description: SuForm
7
+ -->
8
+
9
+ <template>
10
+ <div class="width: 100%">
11
+ <vxe-form
12
+ ref="xForm"
13
+ :data="record"
14
+ :items="formItems"
15
+ :rules="rules"
16
+ :loading="loading"
17
+ :vertical="vertical"
18
+ :title-width="titleWidth"
19
+ :disabled="dataStatus === 'detail'"
20
+ title-align="right"
21
+ title-bold
22
+ v-bind="$attrs"
23
+ >
24
+ <template #collapseNode>
25
+ <div class="flex justify-start">
26
+ <vxe-checkbox
27
+ v-model="collapseStatus"
28
+ content="查看更多"
29
+ :checked-value="false"
30
+ :unchecked-value="true"
31
+ class="pl-10"
32
+ @change="onChange"
33
+ />
34
+ </div>
35
+ </template>
36
+ </vxe-form>
37
+ </div>
38
+ </template>
39
+
40
+ <script lang="ts">
41
+ export default {
42
+ name: 'UtForm'
43
+ };
44
+ </script>
45
+ <script setup lang="ts">
46
+ import { useI18n } from 'vue-i18n';
47
+ import { ref, computed } from 'vue';
48
+ // import { formatRules } from '@utogether/utils';
49
+ import { formatItems, formatRules } from './utils';
50
+ import { clone } from 'xe-utils';
51
+ // import type { IRecord, IFormItemProps } from '../../types';
52
+
53
+ export interface IProps {
54
+ record: IRecord; // 数据对象
55
+ items: Array<IFormItemProps>; // form item
56
+ loading?: boolean;
57
+ vertical?: boolean;
58
+ titleWidth?: number;
59
+ span?: number;
60
+ dataStatus?: string;
61
+ }
62
+
63
+ const props = withDefaults(defineProps<IProps>(), {
64
+ record: (): IRecord => {
65
+ return {};
66
+ },
67
+ items: () => [],
68
+ loading: false,
69
+ vertical: false,
70
+ titleWidth: 100,
71
+ span: 6,
72
+ dataStatus: 'detail'
73
+ });
74
+ const xForm = ref(undefined);
75
+ const { t } = useI18n();
76
+ const collapseStatus = ref(true);
77
+ /**
78
+ * @description: 根据items 获取必填选
79
+ */
80
+ const rules = computed(() => {
81
+ if (!props.items.length) return {};
82
+ return formatRules(props.items, t);
83
+ });
84
+
85
+ const formItems = computed(() => {
86
+ if (!props.items.length) return props.items;
87
+ return formatItems(clone(props.items, true), 'edit', 6);
88
+ });
89
+
90
+ const onChange = () => {
91
+ xForm.value.toggleCollapse();
92
+ };
93
+
94
+ /**
95
+ * @deprecated
96
+ * @description 使用validate替换
97
+ * @param cb
98
+ */
99
+ const validateForm = (cb: Function) => {
100
+ (xForm as any).value.validate(cb);
101
+ };
102
+ const validate = (cb: Function) => {
103
+ (xForm as any).value.validate(cb);
104
+ setTimeout(() => {
105
+ xForm.value.clearValidate();
106
+ }, 1600);
107
+ };
108
+ defineExpose({
109
+ validateForm,
110
+ validate
111
+ });
112
+ </script>