@neatui/vue 2.7.4 → 2.8.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neatui/vue",
3
- "version": "2.7.4",
3
+ "version": "2.8.5",
4
4
  "description": "basic component ui",
5
5
  "main": "./src/index.ts",
6
6
  "license": "MIT",
@@ -1,177 +1,181 @@
1
1
  <template>
2
2
  <div ui-flex="row lm :wrap" class="neatui-upload mb-sm-sub mr-sm-sub" v-bind="{ ...attrs }">
3
- <div v-for="(file, idx) in state.files" :key="idx" ui-flex="col cm" class="flex-fixed bg-fore n-sl w-ms h-ms b-solid bk-case b-xs r-sm pr" v-bind="itemAttrs">
4
- <!-- 删除按钮 -->
5
- <div class="pa ot-ss or-ss bg-fore co-risk lh-xs r-xl parent-hover:show" @click="remove(idx)">
6
- <svg class="block" viewBox="0 0 1024 1024" style="width: 1em; height: 1em">
7
- <path
8
- d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66 0.3L512 563.4l-99.3 118.4-66.1-0.3c-4.4 0-8-3.5-8-8 0-1.9 0.7-3.7 1.9-5.2l130.1-155L340.5 359c-1.2-1.5-1.9-3.3-1.9-5.2 0-4.4 3.6-8 8-8l66.1 0.3L512 464.6l99.3-118.4 66-0.3c4.4 0 8 3.5 8 8 0 1.9-0.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
9
- fill="currentColor"
10
- />
11
- </svg>
12
- </div>
3
+ <div v-for="(file, idx) in state.files" :key="idx" ui-flex="col cm" class="flex-fixed pr" :style="`width: ${isNumber(width) ? width + 'em' : width}`" v-bind="itemAttrs">
4
+ <div class="w-full pr" :style="`box-sizing: content-box; padding-top: ${ratio}%`">
5
+ <div class="pa full ot-no ol-no bg-fore n-sm b-solid bk-case b-xs r-sm" ui-flex="col cm">
6
+ <!-- 删除按钮 -->
7
+ <div class="pa ot-ss or-ss bg-fore co-risk lh-xs r-xl parent-hover:show z-ls" @click="remove(idx)">
8
+ <svg class="block" viewBox="0 0 1024 1024" style="width: 1em; height: 1em">
9
+ <path
10
+ d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66 0.3L512 563.4l-99.3 118.4-66.1-0.3c-4.4 0-8-3.5-8-8 0-1.9 0.7-3.7 1.9-5.2l130.1-155L340.5 359c-1.2-1.5-1.9-3.3-1.9-5.2 0-4.4 3.6-8 8-8l66.1 0.3L512 464.6l99.3-118.4 66-0.3c4.4 0 8 3.5 8 8 0 1.9-0.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
11
+ fill="currentColor"
12
+ />
13
+ </svg>
14
+ </div>
13
15
 
14
- <!-- 文件预览 -->
15
- <img v-if="file.type === 'image'" :src="getImageUrl(file)" alt="" />
16
- <template v-else-if="file.type === 'pdf'">
17
- <svg t="1705645350800" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1913">
18
- <path
19
- d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
20
- fill="#B7B7BD"
21
- p-id="1914"
22
- ></path>
23
- <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="1915"></path>
24
- <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1916"></path>
25
- <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1917"></path>
26
- <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1918"></path>
27
- <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1919"></path>
28
- <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1920"></path>
29
- <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#FF4867" p-id="1921"></path>
30
- <path
31
- d="M314.229 459.289c-21.407 0-40.606-36.765-50.708-60.673-16.991-7.098-35.722-13.728-53.918-18.014-15.92 10.514-43.014 26.251-63.818 26.251-12.911 0-22.21-6.496-25.622-17.813-2.609-9.309-0.4-15.738 2.409-19.22 5.485-7.5 16.79-11.317 33.715-11.317 13.714 0 31.107 2.41 50.507 7.098 12.51-8.907 25.22-19.22 36.525-30.135-5.017-23.84-10.503-62.48 3.412-80.294 6.89-8.505 17.393-11.318 30.103-7.5 13.914 4.017 19.199 12.522 20.804 19.22 5.887 23.237-20.804 54.578-38.8 72.994 4.015 15.938 9.3 32.747 15.721 48.15 25.822 11.518 56.527 28.728 60.006 47.48 1.405 6.495-0.602 12.522-5.887 17.813-4.549 3.75-9.365 5.96-14.45 5.96z m-31.647-52.419c12.785 26.402 24.975 38.862 31.4 38.862 0.995 0 2.386-0.404 4.373-2.02 2.385-2.425 2.385-4.041 1.988-5.523-1.325-6.937-12.124-18.32-37.761-31.319z m-126.377-35.247c-16.73 0-21.33 4.093-22.73 6.003-0.399 0.614-1.599 2.455-0.399 7.23 1 4.092 3.8 8.458 12.464 8.458 10.865 0 26.595-6.207 44.857-17.325-13.063-2.933-24.594-4.366-34.192-4.366z m67.632-1.765c10.845 2.983 22.09 6.827 32.535 10.803-3.792-9.809-6.853-20.015-9.448-29.824-7.651 6.561-15.436 12.99-23.087 19.021zM265.9 259.556c-3.827 0-6.513 1.409-8.93 4.024-7.118 8.917-7.924 31.38-2.418 60.144 20.884-22.26 32.232-42.711 29.411-53.64-0.402-1.61-1.611-6.504-11.348-9.32-2.686-0.805-4.7-1.208-6.715-1.208z"
32
- fill="#FFFFFF"
33
- p-id="1922"
34
- ></path>
35
- </svg>
36
- </template>
37
- <template v-else-if="file.type === 'xlsx'">
38
- <svg t="1705645380770" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2060">
39
- <path
40
- d="M205.799024 64.936585H664.850732l231.773658 231.773659v638.376585c0 13.79353-11.18208 24.97561-24.97561 24.97561H205.799024c-13.79353 0-24.97561-11.18208-24.975609-24.97561V89.912195c0-13.79353 11.18208-24.97561 24.975609-24.97561z m448.70681 24.97561H205.799024v845.174634H871.64878V307.055141L654.505834 89.912195z"
41
- fill="#B7B7BD"
42
- p-id="2061"
43
- ></path>
44
- <path
45
- d="M664.850732 64.936585l-10.989269 23.477074v196.807804c0 13.79353 11.18208 24.97561 24.97561 24.97561h194.310244L896.62439 296.710244 664.850732 64.936585z m13.986341 49.306849L849.815102 285.221463H678.837073V114.243434z"
46
- fill="#B7B7BD"
47
- p-id="2062"
48
- ></path>
49
- <path
50
- d="M255.250732 571.441951m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
51
- fill="#B7B7BD"
52
- p-id="2063"
53
- ></path>
54
- <path
55
- d="M255.250732 707.309268m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
56
- fill="#B7B7BD"
57
- p-id="2064"
58
- ></path>
59
- <path
60
- d="M255.250732 639.37561m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
61
- fill="#B7B7BD"
62
- p-id="2065"
63
- ></path>
64
- <path
65
- d="M255.250732 774.243902m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
66
- fill="#B7B7BD"
67
- p-id="2066"
68
- ></path>
69
- <path
70
- d="M255.250732 842.177561m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
71
- fill="#B7B7BD"
72
- p-id="2067"
73
- ></path>
74
- <path
75
- d="M67.434146 193.810732m15.984391 0l286.72 0q15.98439 0 15.98439 15.98439l0 286.72q0 15.98439-15.98439 15.98439l-286.72 0q-15.98439 0-15.984391-15.98439l0-286.72q0-15.98439 15.984391-15.98439Z"
76
- fill="#00C090"
77
- p-id="2068"
78
- ></path>
79
- <path
80
- d="M242.569116 353.23904l89.224866 84.502478c4.337764 4.107988 4.523582 10.954302 0.415595 15.291067-4.107988 4.337764-10.954302 4.523582-15.291067 0.415595l-89.224866-84.502479-84.502478 89.224867c-4.107988 4.337764-10.954302 4.523582-15.291067 0.415594-4.337764-4.107988-4.523582-10.954302-0.415595-15.291067l84.502478-89.224867-89.224866-84.502478c-4.337764-4.107988-4.523582-10.954302-0.415594-15.291067 4.107988-4.337764 10.954302-4.523582 15.291067-0.415594l89.224867 84.502478 84.502478-89.224867c4.107988-4.337764 10.954302-4.523582 15.291067-0.415594 4.337764 4.107988 4.523582 10.954302 0.415594 15.291068l-84.502478 89.224866z"
81
- fill="#FFFFFF"
82
- p-id="2069"
83
- ></path>
84
- </svg>
85
- </template>
86
- <template v-else-if="file.type === 'docx'">
87
- <svg t="1705645401993" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2207">
88
- <path
89
- d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
90
- fill="#B7B7BD"
91
- p-id="2208"
92
- ></path>
93
- <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="2209"></path>
94
- <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#4297FC" p-id="2210"></path>
95
- <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2211"></path>
96
- <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2212"></path>
97
- <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2213"></path>
98
- <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2214"></path>
99
- <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2215"></path>
100
- <path
101
- d="M315.269 451.314c7.015 7.61 19.731 2.651 19.731-7.693V271h-22.737v143.524l-76.9-83.418c-4.503-4.885-12.223-4.885-16.726 0l-76.9 83.418V271H119v172.62c0 10.345 12.716 15.303 19.731 7.694L227 355.564l88.269 95.75z"
102
- fill="#FFFFFF"
103
- p-id="2216"
104
- ></path>
105
- </svg>
106
- </template>
107
- <template v-else-if="file.type === 'pptx'">
108
- <svg t="1705645414803" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2354">
109
- <path
110
- d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
111
- fill="#B7B7BD"
112
- p-id="2355"
113
- ></path>
114
- <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="2356"></path>
115
- <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2357"></path>
116
- <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2358"></path>
117
- <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2359"></path>
118
- <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2360"></path>
119
- <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2361"></path>
120
- <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#FF7A64" p-id="2362"></path>
121
- <path
122
- d="M314 312.055c0 16.701-8.014 25.218-26.96 27.787H129c-5.523 0-10 4.477-10 10V451.5c0 5.523 4.477 10 10 10s10-4.477 10-10v-91.658h148.694a10 10 0 0 0 1.264-0.08C317.98 356.065 334 339.393 334 312.055c0-26.83-15.298-44.972-43.677-52.703a10 10 0 0 0-2.629-0.352H129c-5.523 0-10 4.477-10 10s4.477 10 10 10h157.317C305.466 284.553 314 295.032 314 312.055z"
123
- fill="#FFFFFF"
124
- p-id="2363"
125
- ></path>
126
- </svg>
127
- </template>
128
- <template v-else>
129
- <svg t="1705645436666" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2501">
130
- <path
131
- d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
132
- fill="#B7B7BD"
133
- p-id="2502"
134
- ></path>
135
- <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="2503"></path>
136
- <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2504"></path>
137
- <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2505"></path>
138
- <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2506"></path>
139
- <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2507"></path>
140
- <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2508"></path>
141
- <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#CCCCCC" p-id="2509"></path>
142
- <path
143
- d="M226.274 240c14.845 0 29.79 4.862 41.18 13.738C280.444 263.86 288 278.458 288 295.57c0 22.53-15.93 43.423-46.252 63.48l-0.31 0.203V387c0 6.525-5.208 11.834-11.695 11.996l-0.305 0.004c-6.627 0-12-5.373-12-12v-34.314a12 12 0 0 1 5.673-10.196C250.988 325.189 264 309.227 264 295.57c0-9.498-3.99-17.207-11.298-22.902-7.059-5.5-16.796-8.668-26.428-8.668-9.598 0-19.202 3.147-26.147 8.625-7.064 5.571-10.985 13.116-11.123 22.435l-0.004 0.51c0 6.628-5.373 12-12 12s-12-5.372-12-12c0-33.201 28.361-55.57 61.274-55.57zM230 453c9.389 0 17-7.611 17-17s-7.611-17-17-17-17 7.611-17 17 7.611 17 17 17z"
144
- fill="#FFFFFF"
145
- p-id="2510"
146
- ></path>
147
- </svg>
148
- </template>
149
-
150
- <!-- 上传状态 -->
151
- <div v-if="file.status === 'uploading'" class="upload-status">
152
- <div class="loading-spinner"></div>
153
- <span class="status-text">上传中...</span>
154
- </div>
155
- <div v-else-if="file.status === 'error'" class="upload-status error">
156
- <span class="status-text">上传失败</span>
16
+ <!-- 文件预览 -->
17
+ <img v-if="file.type === 'image'" class="r-ss" :src="getImageUrl(file)" alt="" />
18
+ <template v-else-if="file.type === 'pdf'">
19
+ <svg t="1705645350800" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1913">
20
+ <path
21
+ d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
22
+ fill="#B7B7BD"
23
+ p-id="1914"
24
+ ></path>
25
+ <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="1915"></path>
26
+ <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1916"></path>
27
+ <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1917"></path>
28
+ <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1918"></path>
29
+ <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1919"></path>
30
+ <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="1920"></path>
31
+ <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#FF4867" p-id="1921"></path>
32
+ <path
33
+ d="M314.229 459.289c-21.407 0-40.606-36.765-50.708-60.673-16.991-7.098-35.722-13.728-53.918-18.014-15.92 10.514-43.014 26.251-63.818 26.251-12.911 0-22.21-6.496-25.622-17.813-2.609-9.309-0.4-15.738 2.409-19.22 5.485-7.5 16.79-11.317 33.715-11.317 13.714 0 31.107 2.41 50.507 7.098 12.51-8.907 25.22-19.22 36.525-30.135-5.017-23.84-10.503-62.48 3.412-80.294 6.89-8.505 17.393-11.318 30.103-7.5 13.914 4.017 19.199 12.522 20.804 19.22 5.887 23.237-20.804 54.578-38.8 72.994 4.015 15.938 9.3 32.747 15.721 48.15 25.822 11.518 56.527 28.728 60.006 47.48 1.405 6.495-0.602 12.522-5.887 17.813-4.549 3.75-9.365 5.96-14.45 5.96z m-31.647-52.419c12.785 26.402 24.975 38.862 31.4 38.862 0.995 0 2.386-0.404 4.373-2.02 2.385-2.425 2.385-4.041 1.988-5.523-1.325-6.937-12.124-18.32-37.761-31.319z m-126.377-35.247c-16.73 0-21.33 4.093-22.73 6.003-0.399 0.614-1.599 2.455-0.399 7.23 1 4.092 3.8 8.458 12.464 8.458 10.865 0 26.595-6.207 44.857-17.325-13.063-2.933-24.594-4.366-34.192-4.366z m67.632-1.765c10.845 2.983 22.09 6.827 32.535 10.803-3.792-9.809-6.853-20.015-9.448-29.824-7.651 6.561-15.436 12.99-23.087 19.021zM265.9 259.556c-3.827 0-6.513 1.409-8.93 4.024-7.118 8.917-7.924 31.38-2.418 60.144 20.884-22.26 32.232-42.711 29.411-53.64-0.402-1.61-1.611-6.504-11.348-9.32-2.686-0.805-4.7-1.208-6.715-1.208z"
34
+ fill="#FFFFFF"
35
+ p-id="1922"
36
+ ></path>
37
+ </svg>
38
+ </template>
39
+ <template v-else-if="file.type === 'xlsx'">
40
+ <svg t="1705645380770" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2060">
41
+ <path
42
+ d="M205.799024 64.936585H664.850732l231.773658 231.773659v638.376585c0 13.79353-11.18208 24.97561-24.97561 24.97561H205.799024c-13.79353 0-24.97561-11.18208-24.975609-24.97561V89.912195c0-13.79353 11.18208-24.97561 24.975609-24.97561z m448.70681 24.97561H205.799024v845.174634H871.64878V307.055141L654.505834 89.912195z"
43
+ fill="#B7B7BD"
44
+ p-id="2061"
45
+ ></path>
46
+ <path
47
+ d="M664.850732 64.936585l-10.989269 23.477074v196.807804c0 13.79353 11.18208 24.97561 24.97561 24.97561h194.310244L896.62439 296.710244 664.850732 64.936585z m13.986341 49.306849L849.815102 285.221463H678.837073V114.243434z"
48
+ fill="#B7B7BD"
49
+ p-id="2062"
50
+ ></path>
51
+ <path
52
+ d="M255.250732 571.441951m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
53
+ fill="#B7B7BD"
54
+ p-id="2063"
55
+ ></path>
56
+ <path
57
+ d="M255.250732 707.309268m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
58
+ fill="#B7B7BD"
59
+ p-id="2064"
60
+ ></path>
61
+ <path
62
+ d="M255.250732 639.37561m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
63
+ fill="#B7B7BD"
64
+ p-id="2065"
65
+ ></path>
66
+ <path
67
+ d="M255.250732 774.243902m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
68
+ fill="#B7B7BD"
69
+ p-id="2066"
70
+ ></path>
71
+ <path
72
+ d="M255.250732 842.177561m9.990244 0l555.457561 0q9.990244 0 9.990243 9.990244l0 0q0 9.990244-9.990243 9.990244l-555.457561 0q-9.990244 0-9.990244-9.990244l0 0q0-9.990244 9.990244-9.990244Z"
73
+ fill="#B7B7BD"
74
+ p-id="2067"
75
+ ></path>
76
+ <path
77
+ d="M67.434146 193.810732m15.984391 0l286.72 0q15.98439 0 15.98439 15.98439l0 286.72q0 15.98439-15.98439 15.98439l-286.72 0q-15.98439 0-15.984391-15.98439l0-286.72q0-15.98439 15.984391-15.98439Z"
78
+ fill="#00C090"
79
+ p-id="2068"
80
+ ></path>
81
+ <path
82
+ d="M242.569116 353.23904l89.224866 84.502478c4.337764 4.107988 4.523582 10.954302 0.415595 15.291067-4.107988 4.337764-10.954302 4.523582-15.291067 0.415595l-89.224866-84.502479-84.502478 89.224867c-4.107988 4.337764-10.954302 4.523582-15.291067 0.415594-4.337764-4.107988-4.523582-10.954302-0.415595-15.291067l84.502478-89.224867-89.224866-84.502478c-4.337764-4.107988-4.523582-10.954302-0.415594-15.291067 4.107988-4.337764 10.954302-4.523582 15.291067-0.415594l89.224867 84.502478 84.502478-89.224867c4.107988-4.337764 10.954302-4.523582 15.291067-0.415594 4.337764 4.107988 4.523582 10.954302 0.415594 15.291068l-84.502478 89.224866z"
83
+ fill="#FFFFFF"
84
+ p-id="2069"
85
+ ></path>
86
+ </svg>
87
+ </template>
88
+ <template v-else-if="file.type === 'docx'">
89
+ <svg t="1705645401993" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2207">
90
+ <path
91
+ d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
92
+ fill="#B7B7BD"
93
+ p-id="2208"
94
+ ></path>
95
+ <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="2209"></path>
96
+ <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#4297FC" p-id="2210"></path>
97
+ <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2211"></path>
98
+ <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2212"></path>
99
+ <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2213"></path>
100
+ <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2214"></path>
101
+ <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2215"></path>
102
+ <path
103
+ d="M315.269 451.314c7.015 7.61 19.731 2.651 19.731-7.693V271h-22.737v143.524l-76.9-83.418c-4.503-4.885-12.223-4.885-16.726 0l-76.9 83.418V271H119v172.62c0 10.345 12.716 15.303 19.731 7.694L227 355.564l88.269 95.75z"
104
+ fill="#FFFFFF"
105
+ p-id="2216"
106
+ ></path>
107
+ </svg>
108
+ </template>
109
+ <template v-else-if="file.type === 'pptx'">
110
+ <svg t="1705645414803" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2354">
111
+ <path
112
+ d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
113
+ fill="#B7B7BD"
114
+ p-id="2355"
115
+ ></path>
116
+ <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="2356"></path>
117
+ <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2357"></path>
118
+ <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2358"></path>
119
+ <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2359"></path>
120
+ <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2360"></path>
121
+ <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2361"></path>
122
+ <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#FF7A64" p-id="2362"></path>
123
+ <path
124
+ d="M314 312.055c0 16.701-8.014 25.218-26.96 27.787H129c-5.523 0-10 4.477-10 10V451.5c0 5.523 4.477 10 10 10s10-4.477 10-10v-91.658h148.694a10 10 0 0 0 1.264-0.08C317.98 356.065 334 339.393 334 312.055c0-26.83-15.298-44.972-43.677-52.703a10 10 0 0 0-2.629-0.352H129c-5.523 0-10 4.477-10 10s4.477 10 10 10h157.317C305.466 284.553 314 295.032 314 312.055z"
125
+ fill="#FFFFFF"
126
+ p-id="2363"
127
+ ></path>
128
+ </svg>
129
+ </template>
130
+ <template v-else>
131
+ <svg t="1705645436666" class="w-ss h-ss" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2501">
132
+ <path
133
+ d="M205.5 64H665l232 232v639c0 13.807-11.193 25-25 25H205.5c-13.807 0-25-11.193-25-25V89c0-13.807 11.193-25 25-25z m449.145 25H205.5v846H872V306.355L654.645 89z"
134
+ fill="#B7B7BD"
135
+ p-id="2502"
136
+ ></path>
137
+ <path d="M665 64l-11 23.5v197c0 13.807 11.193 25 25 25h194.5L897 296 665 64z m14 49.355L850.145 284.5H679V113.355z" fill="#B7B7BD" p-id="2503"></path>
138
+ <path d="M255 571m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2504"></path>
139
+ <path d="M255 707m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2505"></path>
140
+ <path d="M255 639m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2506"></path>
141
+ <path d="M255 774m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2507"></path>
142
+ <path d="M255 842m10 0l556 0q10 0 10 10l0 0q0 10-10 10l-556 0q-10 0-10-10l0 0q0-10 10-10Z" fill="#B7B7BD" p-id="2508"></path>
143
+ <path d="M67 193m16 0l287 0q16 0 16 16l0 287q0 16-16 16l-287 0q-16 0-16-16l0-287q0-16 16-16Z" fill="#CCCCCC" p-id="2509"></path>
144
+ <path
145
+ d="M226.274 240c14.845 0 29.79 4.862 41.18 13.738C280.444 263.86 288 278.458 288 295.57c0 22.53-15.93 43.423-46.252 63.48l-0.31 0.203V387c0 6.525-5.208 11.834-11.695 11.996l-0.305 0.004c-6.627 0-12-5.373-12-12v-34.314a12 12 0 0 1 5.673-10.196C250.988 325.189 264 309.227 264 295.57c0-9.498-3.99-17.207-11.298-22.902-7.059-5.5-16.796-8.668-26.428-8.668-9.598 0-19.202 3.147-26.147 8.625-7.064 5.571-10.985 13.116-11.123 22.435l-0.004 0.51c0 6.628-5.373 12-12 12s-12-5.372-12-12c0-33.201 28.361-55.57 61.274-55.57zM230 453c9.389 0 17-7.611 17-17s-7.611-17-17-17-17 7.611-17 17 7.611 17 17 17z"
146
+ fill="#FFFFFF"
147
+ p-id="2510"
148
+ ></path>
149
+ </svg>
150
+ </template>
151
+
152
+ <!-- 上传状态 -->
153
+ <div class="upload-status" :data-status="file.status">
154
+ <div class="neatui-upload-icon"><i></i></div>
155
+ </div>
156
+ </div>
157
157
  </div>
158
158
  </div>
159
159
 
160
160
  <!-- 上传按钮 -->
161
- <div v-if="showUploadButton">
161
+ <div v-if="showUploadButton" ui-flex="col cm">
162
162
  <slot name="btn">
163
- <label ui-flex="row cm" ui-form="@a type:upload tips:hover" :class="`upload w-ms h-ms n-sl bg-fore b-solid bk-case b-xs r-sm ${uploadClass}`" :style="uploadStyle">
164
- <div v-if="tips" ui-form-tips>
165
- {{ tips }}
166
- </div>
167
- <input style="display: none" type="file" class="pa input" :multiple="!max || max > 1" :accept="accept" @change="change" />
168
- <div class="co-note ac input-icon">
169
- <svg t="1705642180833" style="width: 1em; height: 1em" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4215">
170
- <path
171
- d="M906.212134 565.732986 565.732986 565.732986 565.732986 906.212134C565.732986 926.013685 541.666486 959.972 511.97312 959.972 482.297674 959.972 458.213254 926.013685 458.213254 906.212134L458.213254 565.732986 117.734106 565.732986C97.950475 565.732986 63.97424 541.666486 63.97424 511.97312 63.97424 482.279754 97.950475 458.213254 117.734106 458.213254L458.213254 458.213254 458.213254 117.734106C458.213254 97.950475 482.297674 63.97424 511.97312 63.97424 541.666486 63.97424 565.732986 97.950475 565.732986 117.734106L565.732986 458.213254 906.212134 458.213254C925.995765 458.213254 959.972 482.279754 959.972 511.97312 959.972 541.666486 925.995765 565.732986 906.212134 565.732986Z"
172
- p-id="4216"
173
- ></path>
174
- </svg>
163
+ <label class="pr" ui-form="@a type:upload tips:hover" :style="`width: ${isNumber(width) ? width + 'em' : width}`">
164
+ <div class="w-full pr" :style="`box-sizing: content-box; padding-top: ${ratio}%`">
165
+ <div ui-flex="row cm" :class="`pa ot-no ol-no full upload n-sl bg-fore b-dashed bk-note-lm b-xs r-sm ${uploadClass}`" :style="uploadStyle">
166
+ <div v-if="tips" ui-form-tips>
167
+ {{ tips }}
168
+ </div>
169
+ <input style="display: none" type="file" class="pa input" :multiple="!max || max > 1" :accept="accept" @change="change" />
170
+ <div class="co-note ac input-icon">
171
+ <svg t="1705642180833" style="width: 1em; height: 1em" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4215">
172
+ <path
173
+ d="M906.212134 565.732986 565.732986 565.732986 565.732986 906.212134C565.732986 926.013685 541.666486 959.972 511.97312 959.972 482.297674 959.972 458.213254 926.013685 458.213254 906.212134L458.213254 565.732986 117.734106 565.732986C97.950475 565.732986 63.97424 541.666486 63.97424 511.97312 63.97424 482.279754 97.950475 458.213254 117.734106 458.213254L458.213254 458.213254 458.213254 117.734106C458.213254 97.950475 482.297674 63.97424 511.97312 63.97424 541.666486 63.97424 565.732986 97.950475 565.732986 117.734106L565.732986 458.213254 906.212134 458.213254C925.995765 458.213254 959.972 482.279754 959.972 511.97312 959.972 541.666486 925.995765 565.732986 906.212134 565.732986Z"
174
+ p-id="4216"
175
+ ></path>
176
+ </svg>
177
+ </div>
178
+ </div>
175
179
  </div>
176
180
  </label>
177
181
  </slot>
@@ -181,6 +185,7 @@
181
185
  <script setup lang="ts">
182
186
  import { reactive, watch, onUnmounted, computed } from 'vue';
183
187
  import Compressor from 'compressorjs';
188
+ import { isNumber } from '@fekit/utils';
184
189
 
185
190
  interface Props {
186
191
  modelValue?: string | any[];
@@ -195,6 +200,8 @@
195
200
  maxSize?: number;
196
201
  compress?: boolean;
197
202
  disabled?: boolean;
203
+ width?: number | string;
204
+ ratio?: number;
198
205
  }
199
206
 
200
207
  const props = withDefaults(defineProps<Props>(), {
@@ -210,16 +217,19 @@
210
217
  compress: true,
211
218
  max: 0,
212
219
  disabled: false,
220
+ width: 10,
221
+ ratio: 100,
213
222
  });
214
223
 
215
224
  interface UploadFile {
216
225
  name: string;
217
226
  type: string;
218
227
  path: string;
219
- status: 'uploading' | 'success' | 'error';
228
+ status: 'ing' | 'yes' | 'err' | 'end';
220
229
  size?: number;
221
230
  progress?: number;
222
231
  result?: any; // 直接保存上传函数的返回值
232
+ timer?: number; // 用于存储定时器ID
223
233
  }
224
234
 
225
235
  const state = reactive<{
@@ -234,7 +244,7 @@
234
244
 
235
245
  // 获取图片URL
236
246
  const getImageUrl = (file: UploadFile): string => {
237
- if (file.status === 'success' && file.result) {
247
+ if ((file.status === 'yes' || file.status === 'end') && file.result) {
238
248
  // 如果上传成功,优先使用上传返回的URL
239
249
  if (typeof file.result === 'string') {
240
250
  return file.result;
@@ -320,18 +330,60 @@
320
330
  });
321
331
  };
322
332
 
333
+ // 清除文件定时器
334
+ const clearFileTimer = (file: UploadFile) => {
335
+ if (file.timer) {
336
+ clearTimeout(file.timer);
337
+ file.timer = undefined;
338
+ }
339
+ };
340
+
341
+ // 设置状态转换定时器
342
+ const setStatusTransitionTimer = (file: UploadFile, delay: number = 2000) => {
343
+ clearFileTimer(file);
344
+ file.timer = window.setTimeout(() => {
345
+ const fileIndex = state.files.findIndex((f) => f.path === file.path);
346
+ if (fileIndex !== -1) {
347
+ if (file.status === 'yes') {
348
+ // yes 状态 3 秒后变为 end
349
+ state.files[fileIndex].status = 'end';
350
+ clearFileTimer(state.files[fileIndex]);
351
+ } else if (file.status === 'err') {
352
+ // err 状态 3 秒后清除占位(删除文件)
353
+ removeFile(fileIndex, true);
354
+ }
355
+ }
356
+ }, delay);
357
+ };
358
+
323
359
  // 删除文件函数
324
360
  const remove = (index: number) => {
361
+ removeFile(index, false);
362
+ };
363
+
364
+ // 内部删除文件函数
365
+ const removeFile = (index: number, isAutoRemove: boolean = false) => {
366
+ const file = state.files[index];
367
+ if (!file) return;
368
+
369
+ // 清除定时器
370
+ clearFileTimer(file);
371
+
325
372
  // 释放对象URL,防止内存泄漏
326
- if (state.files[index] && state.files[index].path) {
327
- URL.revokeObjectURL(state.files[index].path);
373
+ if (file.path && file.path.startsWith('blob:')) {
374
+ URL.revokeObjectURL(file.path);
328
375
  }
329
376
 
330
377
  // 从数组中移除文件
331
378
  state.files.splice(index, 1);
332
379
 
333
- // 发射更新事件
334
- emitUpdateEvents();
380
+ // 如果不是自动移除,发射更新事件
381
+ if (!isAutoRemove) {
382
+ emitUpdateEvents();
383
+ } else {
384
+ // 自动移除时也需要更新外部数据源
385
+ emitUpdateEvents();
386
+ }
335
387
  };
336
388
 
337
389
  // 发射更新事件
@@ -339,7 +391,7 @@
339
391
  // 设置内部更新标志
340
392
  state.isInternalUpdate = true;
341
393
 
342
- const successFiles = state.files.filter((file) => file.status === 'success');
394
+ const successFiles = state.files.filter((file) => file.status === 'yes' || file.status === 'end');
343
395
 
344
396
  // 如果没有成功文件,返回空值
345
397
  if (successFiles.length === 0) {
@@ -395,7 +447,7 @@
395
447
  name: file.name,
396
448
  type: getFileType(file),
397
449
  path: previewUrl,
398
- status: 'uploading',
450
+ status: 'ing',
399
451
  size: file.size,
400
452
  };
401
453
  state.files.push(uploadFile);
@@ -439,20 +491,29 @@
439
491
  if (result) {
440
492
  // 直接保存上传函数的返回值
441
493
  state.files[fileIndex].result = result;
442
- state.files[fileIndex].status = 'success';
494
+ state.files[fileIndex].status = 'yes';
495
+ // 设置3秒后转换为end状态的定时器
496
+ setStatusTransitionTimer(state.files[fileIndex]);
497
+ // 每个文件完成后更新外部数据源
498
+ emitUpdateEvents();
443
499
  } else {
444
- state.files[fileIndex].status = 'error';
500
+ // 上传失败时先显示错误状态,3秒后移除文件
501
+ state.files[fileIndex].status = 'err';
502
+ // 设置3秒后删除文件的定时器
503
+ setStatusTransitionTimer(state.files[fileIndex]);
504
+ // 发射错误事件
505
+ emit('error', `文件 ${file.name} 上传失败: 上传函数返回空值`);
445
506
  }
446
- // 每个文件完成后更新外部数据源
447
- emitUpdateEvents();
448
507
  }
449
508
  } catch (error) {
450
509
  console.error('文件处理失败:', error);
451
510
 
452
- // 更新失败文件状态
511
+ // 上传失败时先显示错误状态,3秒后移除文件
453
512
  const fileIndex = state.files.findIndex((f) => f.path === uploadFile.path);
454
513
  if (fileIndex !== -1) {
455
- state.files[fileIndex].status = 'error';
514
+ state.files[fileIndex].status = 'err';
515
+ // 设置3秒后删除文件的定时器
516
+ setStatusTransitionTimer(state.files[fileIndex]);
456
517
  }
457
518
 
458
519
  emit('error', `文件 ${file.name} 上传失败: ${error}`);
@@ -475,7 +536,7 @@
475
536
  name: 'file',
476
537
  type: 'image',
477
538
  path: item,
478
- status: 'success' as const,
539
+ status: 'end' as const,
479
540
  result: item,
480
541
  };
481
542
  } else {
@@ -484,7 +545,7 @@
484
545
  name: item.name || 'file',
485
546
  type: item.type || 'file',
486
547
  path: item.url || item.path || '',
487
- status: 'success' as const,
548
+ status: 'end' as const,
488
549
  result: item,
489
550
  };
490
551
  }
@@ -495,7 +556,7 @@
495
556
  name: 'file',
496
557
  type: 'image',
497
558
  path: newValue,
498
- status: 'success',
559
+ status: 'end',
499
560
  result: newValue,
500
561
  },
501
562
  ];
@@ -506,7 +567,7 @@
506
567
  name: objValue.name || 'file',
507
568
  type: objValue.type || 'file',
508
569
  path: objValue.url || objValue.path || '',
509
- status: 'success',
570
+ status: 'end',
510
571
  result: newValue,
511
572
  },
512
573
  ];
@@ -520,6 +581,9 @@
520
581
  // 组件销毁时清理资源
521
582
  onUnmounted(() => {
522
583
  state.files.forEach((file) => {
584
+ // 清除定时器
585
+ clearFileTimer(file);
586
+ // 释放对象URL
523
587
  if (file.path && file.path.startsWith('blob:')) {
524
588
  URL.revokeObjectURL(file.path);
525
589
  }
@@ -536,17 +600,22 @@
536
600
  left: 0;
537
601
  right: 0;
538
602
  bottom: 0;
539
- background: rgba(0, 0, 0, 0.6);
603
+ background: rgba(255, 255, 255, 0.8);
604
+ backdrop-filter: blur(0.5em);
605
+ -webkit-backdrop-filter: blur(0.5em);
540
606
  display: flex;
541
607
  flex-direction: column;
542
608
  align-items: center;
543
609
  justify-content: center;
544
- color: white;
610
+ color: #666;
545
611
  font-size: 0.75em;
546
612
  border-radius: 0.25em;
613
+ transition: opacity 0.3s ease;
547
614
 
548
- &.error {
549
- background: rgba(255, 0, 0, 0.6);
615
+ // 完成
616
+ &[data-status='end'] {
617
+ opacity: 0;
618
+ pointer-events: none;
550
619
  }
551
620
 
552
621
  .status-text {
@@ -572,5 +641,172 @@
572
641
  transform: rotate(360deg);
573
642
  }
574
643
  }
644
+
645
+ // 上传动画
646
+ @keyframes am-ing {
647
+ 0% {
648
+ transform: translateY(120%);
649
+ }
650
+ 50% {
651
+ transform: translateY(0%);
652
+ }
653
+ 60% {
654
+ transform: translateY(2%);
655
+ }
656
+ 99% {
657
+ transform: translateY(-80%);
658
+ }
659
+ 100% {
660
+ transform: translateY(0%);
661
+ }
662
+ }
663
+ // 动画定义
664
+ @keyframes am-yes-a {
665
+ 0% {
666
+ transform: rotate(43deg) scale3d(0, 1, 1);
667
+ }
668
+ 40%,
669
+ 100% {
670
+ transform: rotate(43deg) scale3d(1, 1, 1);
671
+ }
672
+ }
673
+
674
+ @keyframes am-yes-b {
675
+ 0%,
676
+ 40% {
677
+ transform: rotate(-53deg) scale3d(0, 1, 1);
678
+ }
679
+ 100% {
680
+ transform: rotate(-53deg) scale3d(1, 1, 1);
681
+ }
682
+ }
683
+
684
+ @keyframes am-err-a {
685
+ 0%,
686
+ 50% {
687
+ transform: rotate(45deg) scale3d(0, 1, 1);
688
+ }
689
+ 100% {
690
+ transform: rotate(45deg) scale3d(1, 1, 1);
691
+ }
692
+ }
693
+ @keyframes am-err-b {
694
+ 0% {
695
+ transform: rotate(-45deg) scale3d(0, 1, 1);
696
+ }
697
+ 50%,
698
+ 100% {
699
+ transform: rotate(-45deg) scale3d(1, 1, 1);
700
+ }
701
+ }
702
+
703
+ .neatui-upload-icon {
704
+ position: relative;
705
+ width: 2em;
706
+ height: 2em;
707
+ border-radius: 50%;
708
+ border: 1px solid currentColor;
709
+ overflow: hidden;
710
+ }
711
+
712
+ // 上传
713
+ [data-status='ing'] .neatui-upload-icon {
714
+ color: var(--co-warn);
715
+
716
+ & > * {
717
+ position: absolute;
718
+ display: block;
719
+ width: 100%;
720
+ height: 100%;
721
+ animation: am-ing 0.8s infinite both;
722
+
723
+ &::before,
724
+ &::after {
725
+ content: '';
726
+ position: absolute;
727
+ top: 24%;
728
+ }
729
+
730
+ &::before {
731
+ border-left: 2px solid currentColor;
732
+ border-top: 2px solid currentColor;
733
+ width: 0.7em;
734
+ height: 0.7em;
735
+ left: 30.5%;
736
+ transform: rotate(45deg);
737
+ }
738
+ &::after {
739
+ width: 2px;
740
+ height: 1em;
741
+ left: 44%;
742
+ background-color: currentColor;
743
+ }
744
+ }
745
+ }
746
+
747
+ // 成功
748
+ [data-status='yes'] .neatui-upload-icon {
749
+ color: var(--co-well);
750
+
751
+ &:before,
752
+ &:after {
753
+ position: absolute;
754
+ content: '';
755
+ display: block;
756
+ background-color: currentColor;
757
+ border-radius: 10rem;
758
+ }
759
+
760
+ &::before {
761
+ width: 35%;
762
+ height: 10%;
763
+ left: 18%;
764
+ top: 41%;
765
+ transform: rotate(43deg);
766
+ transform-origin: 0% 100%;
767
+ animation: am-yes-a ease-out 0.5s both;
768
+ }
769
+
770
+ &::after {
771
+ width: 61%;
772
+ height: 10%;
773
+ left: 46.5%;
774
+ bottom: 24.5%;
775
+ transform-origin: 0% 100%;
776
+ transform: rotate(-53deg);
777
+ animation: am-yes-b ease-out 0.5s both;
778
+ }
779
+ }
780
+
781
+ // 失败
782
+ [data-status='err'] .neatui-upload-icon {
783
+ color: var(--co-risk);
784
+
785
+ &:before,
786
+ &:after {
787
+ position: absolute;
788
+ content: '';
789
+ display: block;
790
+ background-color: currentColor;
791
+ border-radius: 10rem;
792
+ width: 70%;
793
+ height: 10%;
794
+ top: 17.25%;
795
+ }
796
+
797
+ &::before {
798
+ left: 23%;
799
+ transform: rotate(45deg);
800
+ transform-origin: 0% 100%;
801
+ animation: am-err-a ease 0.5s both;
802
+ }
803
+
804
+ &::after {
805
+ right: 23%;
806
+ transform-origin: 100% 100%;
807
+ transform: rotate(45deg);
808
+ animation: am-err-b ease 0.5s both;
809
+ }
810
+ }
575
811
  }
576
812
  </style>
@@ -5,20 +5,20 @@
5
5
  <thead class="table-head-fixed">
6
6
  <tr>
7
7
  <th v-if="selected" min-width col-fixed="1">
8
- <label class="mb-ss" ui-form="@a type:checkbox"><input type="checkbox" :checked="state.isAllSelect" @change="fSelectAll" /><span></span></label>
8
+ <label class="mb-ss" ui-form="@a type:checkbox"><input type="checkbox" v-model="all" /><span></span></label>
9
9
  </th>
10
10
  <template v-for="(col, idx) in state.table" :key="idx">
11
11
  <th v-if="!col.__close" :min-width="col.fixed" :col-fixed="col.fixed" :col-fixed-side="col.fixed || 'l'" :data-field="col.field">
12
12
  <slot :name="`thead-${col.field}`" :col="col" :idx="idx">
13
13
  <div v-bind="col.thead">
14
- <span class="nl-xs ux-none" v-if="col.order === 0 || col.order === 1 || col.order === 2" @click="fSetOrder(col)">
14
+ <span class="ux-none" ui-flex="row rm" v-if="col.order === 0 || col.order === 1 || col.order === 2" @click="fSetOrder(col)">
15
15
  <span>{{ col.label }}</span>
16
- <span ui-btn="@a none s :square" :ui-order="col.__order"></span>
16
+ <span ui-btn="@a none xs :square" :ui-order="col.__order"></span>
17
17
  </span>
18
- <span class="nl-xs" v-else>{{ col.label }}</span>
18
+ <span v-else>{{ col.label }}</span>
19
19
  <!-- 设置 -->
20
- <IFollowView v-if="!slots.operate && idx === state._last" tipBoxClass="n-no">
21
- <button :ui-btn="`@a none s :square`">
20
+ <IFollowView v-if="!slots.operate && idx === state.__last" tipBoxClass="n-no">
21
+ <button :ui-btn="`@a none xs :square`">
22
22
  <Icon name="tableloader-setting" class="co-read"></Icon>
23
23
  </button>
24
24
  <template #tips>
@@ -30,7 +30,7 @@
30
30
  <input type="checkbox" :checked="!element.__close" @change="element.__close = element.__close ? 0 : 1" />
31
31
  <span>
32
32
  {{ element.label }}
33
- <i v-if="element.fixed" class="icon icon-fixed fs-xs"></i>
33
+ <i v-if="element.fixed" class="icon icon-fixed"></i>
34
34
  </span>
35
35
  </label>
36
36
  </div>
@@ -43,12 +43,12 @@
43
43
  </slot>
44
44
  </th>
45
45
  </template>
46
- <th v-if="slots.operate" col-fixed="r" class="fs-xs nowrap ar">
46
+ <th v-if="slots.operate" col-fixed="r" class="fs-ss nowrap ar">
47
47
  <div ui-flex="row rm">
48
- <span class="mr-sm">{{ words['operate'] || '操作' }}</span>
49
- <IFollowView v-if="tools" tipBoxClass="n-no">
50
- <button :ui-btn="`@a none s :square`">
51
- <Icon name="tableloader-setting" class="co-auto"></Icon>
48
+ <span>{{ words['operate'] || '操作' }}</span>
49
+ <IFollowView v-if="tools" class="lh-xs" tipBoxClass="n-no">
50
+ <button :ui-btn="`@a none xs :square`">
51
+ <Icon name="tableloader-setting" class="co-auto o-ls fs-xs"></Icon>
52
52
  </button>
53
53
  <template #tips>
54
54
  <div class="w-mm h-ml+ n-sl" ui-scroll="x:hidden :y">
@@ -59,7 +59,7 @@
59
59
  <input type="checkbox" :checked="!element.__close" @change="element.__close = element.__close ? 0 : 1" />
60
60
  <span>
61
61
  {{ element.label }}
62
- <i v-if="element.fixed" class="icon icon-fixed fs-xs"></i>
62
+ <i v-if="element.fixed" class="icon icon-fixed"></i>
63
63
  </span>
64
64
  </label>
65
65
  </div>
@@ -81,13 +81,13 @@
81
81
  <template v-for="(col, cid) in state.table" :key="cid">
82
82
  <td
83
83
  v-if="!col.__close"
84
- :class="`${!slots.operate && cid === state._last ? 'ar' : ''}`"
84
+ :class="`${!slots.operate && cid === state.__last ? 'ar' : ''}`"
85
85
  :min-width="col.fixed"
86
86
  :col-fixed="col.fixed"
87
87
  :col-fixed-side="col.fixed || 'l'"
88
88
  :data-field="col.field"
89
89
  >
90
- <slot :name="col.field" :col="col" :item="item" :idx="idx"><TableColView :col="col" :data="item" /></slot>
90
+ <slot :name="col.field" :col="col" :item="item" :idx="idx"><TableRender :col="col" :data="item" /></slot>
91
91
  </td>
92
92
  </template>
93
93
  <td v-if="slots.operate" col-fixed="r" class="ar"><slot name="operate" :item="item" :idx="idx"></slot></td>
@@ -100,7 +100,7 @@
100
100
  <ul>
101
101
  <li class="bg-fore r-sm my-sl nx-ms ny-sl" ui-flex="row xm">
102
102
  <div>
103
- <label class="mb-ss" ui-form="@a type:checkbox"><input type="checkbox" :checked="state.isAllSelect" @change="fSelectAll" /><span>全选</span></label>
103
+ <label class="mb-ss" ui-form="@a type:checkbox"><input type="checkbox" v-model="all" /><span>全选</span></label>
104
104
  </div>
105
105
  <div>
106
106
  <IFollowView>
@@ -127,7 +127,7 @@
127
127
  <tr v-if="!col.__close">
128
128
  <td class="nowrap vt n-ss">{{ col.label }}:</td>
129
129
  <td class="n-ss">
130
- <TableColView :col="col" :data="item" />
130
+ <TableRender :col="col" :data="item" />
131
131
  </td>
132
132
  </tr>
133
133
  </template>
@@ -151,14 +151,14 @@
151
151
  import IDraggable from '../../basic/IDraggable.vue';
152
152
  import { IFollowView, Icon } from '../../basic';
153
153
  import { itable } from '@fekit/itable';
154
- import TableColView from './TableColView.vue';
154
+ import TableRender from './TableRender.vue';
155
155
  import { isFunction, isObject, hasOwnProp, deepcopy } from '@fekit/utils';
156
156
  const route = useRoute();
157
157
  const router = useRouter();
158
158
  // 外部插槽
159
159
  const slots: any = useSlots();
160
160
  // 外部方法
161
- const emits = defineEmits(['update:table', 'order', 'refresh']);
161
+ const emits = defineEmits(['update:allSelected', 'order', 'refresh']);
162
162
 
163
163
  // 类型定义
164
164
  interface JSONRules {
@@ -178,6 +178,17 @@
178
178
  clear?: boolean | (() => boolean);
179
179
  order?: 0 | 1 | 2 | number | (() => 0 | 1 | 2 | number);
180
180
  }
181
+ // 表格列(在 JSONRules 基础上的运行时扩展字段)
182
+ type TableColumn = JSONRules & {
183
+ __close?: number | boolean;
184
+ __order?: 0 | 1 | 2 | number;
185
+ __sort?: number;
186
+ close?: boolean | number | 'always';
187
+ fixed?: string | number;
188
+ thead?: any;
189
+ field: any;
190
+ label: any;
191
+ };
181
192
  interface Props {
182
193
  words?: { [key: string]: any };
183
194
  table: Array<JSONRules>;
@@ -189,6 +200,7 @@
189
200
  attrs?: { [key: string]: any };
190
201
  event?: { [key: string]: any };
191
202
  };
203
+ allSelected?: boolean;
192
204
  }
193
205
  // 外部入参
194
206
  const props: any = withDefaults(defineProps<Props>(), {
@@ -199,72 +211,71 @@
199
211
  tools: true,
200
212
  rows: () => ({ attrs: {}, event: {} }),
201
213
  theme: '@b sz:l',
214
+ allSelected: false,
202
215
  });
203
216
 
204
217
  // 内部数据
205
- const state: any = reactive({
206
- name: computed(() => 'confs' + route.path?.replace(/\//g, '_')),
218
+ const state = reactive<{
219
+ load: number;
220
+ tool: number;
221
+ table: TableColumn[];
222
+ param: Record<string, any>;
223
+ __last: number;
224
+ }>({
207
225
  load: 0,
208
226
  tool: 1,
209
- mode: 0,
210
- id: 0,
211
- limit: computed(() => {
212
- const _cache = JSON.parse(localStorage.getItem(`${state.name}_limit`) || '{}');
213
- return (props.limit || [])?.map((item: any) => {
214
- if (_cache[item.field] && hasOwnProp(_cache[item.field], '__close')) {
215
- item.__close = _cache[item.field]?.__close;
216
- } else {
217
- if (item.close === true || item.close === 1 || item.close === 'always') {
218
- item.__close = 1;
219
- }
220
- }
221
- return item;
222
- });
223
- }),
224
- table: [],
227
+ table: [] as TableColumn[],
225
228
  param: {},
226
- // 列表是否全选
227
- isAllSelect: computed(() => {
228
- return (
229
- props.lists.length &&
230
- props.lists.every((item: any) => {
231
- return item.__selected;
232
- })
233
- );
234
- }),
235
- // 列表是否有在选行
236
- isHasSelect: computed(() => {
237
- return props.lists.filter((item: any) => {
238
- return item.__selected;
239
- });
240
- }),
241
- bulk: computed(() => {
242
- return props.isHasSelect.map((item: any) => {
243
- return item.customerId;
229
+ __last: 0,
230
+ });
231
+
232
+ const name = computed(() => 'confs' + route.path?.replace(/\//g, '_')) || '';
233
+
234
+ // 全选状态
235
+ const all = computed({
236
+ get() {
237
+ return props.lists.every((item: any) => item.__selected === true);
238
+ },
239
+ set(value: boolean) {
240
+ props.lists.forEach((item: any) => {
241
+ item.__selected = value;
244
242
  });
245
- }),
246
- // 最后一列可见列
247
- last: 0,
248
- lw: 0,
243
+ },
249
244
  });
250
245
 
246
+ // 向内同步
247
+ watch(
248
+ () => props.allSelected,
249
+ (v) => {
250
+ all.value = v;
251
+ },
252
+ { immediate: true },
253
+ );
254
+ // 向外同步
255
+ watch(
256
+ () => all.value,
257
+ (v) => {
258
+ emits('update:allSelected', v);
259
+ },
260
+ );
261
+
251
262
  // 前置处理
252
263
  watch(
253
264
  () => props.table,
254
265
  () => {
255
- const _cache = JSON.parse(localStorage.getItem(`${state.name}_table`) || '{}');
256
- state._last = 0;
266
+ const _cache = JSON.parse(localStorage.getItem(`${name}_table`) || '{}');
267
+ state.__last = 0;
257
268
  state.table = deepcopy(props.table || [])
258
- ?.map((col: any, idx: number) => {
269
+ ?.map((col: TableColumn, idx: number) => {
259
270
  // 表头属性处理
260
271
  col.thead ??= {};
261
272
  col.thead.class = (col.thead.class || '').split(/\s+/);
262
273
  if (!col.thead.class.some((c: any) => c === 'wrap' || c === 'nowrap')) {
263
274
  col.thead.class.push('nowrap');
264
275
  }
265
- // 如果没有以 fs- 开头的类,就加 fs-xs
276
+ // 如果没有以 fs- 开头的类,就加 fs-ss
266
277
  if (!col.thead.class.some((c: string) => /^fs-/.test(c))) {
267
- col.thead.class.push('fs-xs');
278
+ col.thead.class.push('fs-ss');
268
279
  }
269
280
 
270
281
  // 缓存的显示信息
@@ -276,8 +287,8 @@
276
287
  }
277
288
  }
278
289
  // 获取缓存的表格列顺序(注意不是数据记录的排序)
279
- if (_cache[col.field] && hasOwnProp(_cache[col.field], '_sort')) {
280
- col._sort = _cache[col.field]?._sort;
290
+ if (_cache[col.field] && hasOwnProp(_cache[col.field], '__sort')) {
291
+ col.__sort = _cache[col.field]?.__sort;
281
292
  }
282
293
 
283
294
  // 获取本地存储的字段升降序(注意不是表格列的顺序)
@@ -287,11 +298,11 @@
287
298
 
288
299
  // 最后一列可见列
289
300
  if (!col.__close) {
290
- state._last = idx;
301
+ state.__last = idx;
291
302
  }
292
303
  return col;
293
304
  })
294
- .sort((a: any, b: any) => a._sort - b._sort)
305
+ .sort((a: any, b: any) => a.__sort - b.__sort)
295
306
  .sort((a: any, b: any) => {
296
307
  const fixedOrder: any = { 1: -1, l: -1, r: 1 };
297
308
  const aFixed = a.fixed || '';
@@ -310,9 +321,9 @@
310
321
  [() => state.table],
311
322
  () => {
312
323
  const _table: any = {};
313
- state._last = 0;
324
+ state.__last = 0;
314
325
  state.table.forEach((col: any, idx: number) => {
315
- _table[col.field] = { _sort: idx };
326
+ _table[col.field] = { __sort: idx };
316
327
  if (hasOwnProp(col, '__close')) {
317
328
  _table[col.field].__close = col.__close ? 1 : 0;
318
329
  }
@@ -320,20 +331,14 @@
320
331
  _table[col.field].__order = col.__order;
321
332
  }
322
333
  if (!col.__close) {
323
- state._last = idx;
334
+ state.__last = idx;
324
335
  }
325
336
  });
326
- localStorage.setItem(`${state.name}_table`, JSON.stringify(_table));
337
+ localStorage.setItem(`${name}_table`, JSON.stringify(_table));
327
338
  },
328
339
  { deep: true },
329
340
  );
330
341
 
331
- // 全选
332
- const fSelectAll = ({ target = {} }: any = {}) => {
333
- props.lists.map((col: any = {}) => {
334
- col.__selected = target.checked;
335
- });
336
- };
337
342
  const tableRef: any = ref(null);
338
343
  onMounted(() => {
339
344
  nextTick(() => {
@@ -349,9 +354,7 @@
349
354
  itable.listen(tableRef.value);
350
355
  });
351
356
  },
352
- {
353
- deep: true,
354
- },
357
+ { deep: true },
355
358
  );
356
359
  onUnmounted(() => {
357
360
  itable.remove();
@@ -1,14 +1,14 @@
1
1
  <template>
2
2
  <template v-if="isArray(json.child)">
3
3
  <div :class="json?.attrs?.class?.includes('wrap') ? 'wrap' : 'nowrap'" v-bind="json.attrs" v-on="event(json, data)">
4
- <TableColView v-for="(sub, idx) in col.child" :key="idx" :col="sub" :data="data" />
4
+ <TableRender v-for="(sub, idx) in col.child" :key="idx" :col="sub" :data="data" />
5
5
  </div>
6
6
  </template>
7
7
  <template v-else-if="json.popup">
8
8
  <IFollowView>
9
- <TableColView :col="{ ...col, popup: null }" :data="data" />
9
+ <TableRender :col="{ ...col, popup: null }" :data="data" />
10
10
  <template #tips>
11
- <TableColView :col="isFunction(col.popup) ? col.popup(data) : col.popup" :data="data" />
11
+ <TableRender :col="isFunction(col.popup) ? col.popup(data) : col.popup" :data="data" />
12
12
  </template>
13
13
  </IFollowView>
14
14
  </template>
@@ -23,7 +23,7 @@
23
23
  import { computed } from 'vue';
24
24
  import { isFunction, isArray, isObject, deepcopy } from '@fekit/utils';
25
25
  import { useRoute, useRouter } from 'vue-router';
26
- import TableColView from './TableColView.vue';
26
+ import TableRender from './TableRender.vue';
27
27
  import { IFollowView } from '../../basic';
28
28
 
29
29
  const DefaultImage =