@scx-js/scx-ui 0.0.1
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/index.js +44 -0
- package/package.json +29 -0
- package/scx-context-menu/index.css +51 -0
- package/scx-context-menu/index.js +65 -0
- package/scx-context-menu/index.vue +86 -0
- package/scx-drag/index.js +30 -0
- package/scx-group/index.css +66 -0
- package/scx-group/index.vue +154 -0
- package/scx-icon/index.css +4 -0
- package/scx-icon/index.vue +20 -0
- package/scx-input/index.css +39 -0
- package/scx-input/index.vue +44 -0
- package/scx-panel/index.css +47 -0
- package/scx-panel/index.vue +28 -0
- package/scx-panel/scx-panel-item.css +36 -0
- package/scx-panel/scx-panel-item.vue +22 -0
- package/scx-progress/index.css +33 -0
- package/scx-progress/index.vue +29 -0
- package/scx-switch/index.css +109 -0
- package/scx-switch/index.vue +31 -0
- package/scx-upload/helper.js +177 -0
- package/scx-upload/index.css +204 -0
- package/scx-upload/index.vue +235 -0
- package/scx-upload-list/index.css +89 -0
- package/scx-upload-list/index.vue +228 -0
- package/style/changeTheme.js +13 -0
- package/style/dark.css +59 -0
- package/style/default.css +98 -0
- package/style/normalize.css +35 -0
@@ -0,0 +1,235 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="scx-upload">
|
3
|
+
|
4
|
+
<!-- 隐藏的 input 用于触发点击上传事件 -->
|
5
|
+
<input ref="hiddenInputRef" placeholder="file" style="display: none" type="file" @change="onHiddenInputChange"/>
|
6
|
+
|
7
|
+
<!-- 有文件时预览文件 -->
|
8
|
+
<div v-if="proxyModelValue" class="preview">
|
9
|
+
<!-- 有预览图时显示预览图 -->
|
10
|
+
<img v-if="uploadInfo.previewURL" :src="uploadInfo.previewURL" alt="img" class="preview-image">
|
11
|
+
<!-- 没有预览图但是有文件名时显示文件名 -->
|
12
|
+
<div v-else-if="uploadInfo.fileName" class="preview-text">
|
13
|
+
<div>{{ uploadInfo.fileName }}</div>
|
14
|
+
</div>
|
15
|
+
<!-- 都没有时显示文件 id -->
|
16
|
+
<div v-else class="preview-text">
|
17
|
+
<div>{{ proxyModelValue }}</div>
|
18
|
+
</div>
|
19
|
+
<!-- 操作项 -->
|
20
|
+
<div class="operation">
|
21
|
+
<a :href="uploadInfo.downloadURL" class="item-download">
|
22
|
+
下载
|
23
|
+
</a>
|
24
|
+
<div v-if="!disabled" class="item-replace" @click="selectFile">
|
25
|
+
替换
|
26
|
+
</div>
|
27
|
+
<div v-if="!disabled" class="item-delete" @click="deleteFile">
|
28
|
+
删除
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<!-- 没有文件且允许上传时显示 -->
|
34
|
+
<div v-else-if="!disabled" :class="dragover ?'dragover ':''" class="no-preview" @click="selectFile"
|
35
|
+
@dragleave="callDragleave"
|
36
|
+
@dragover="callDragover" @drop="callDrop">
|
37
|
+
<scx-icon icon="outlined-plus-circle"/>
|
38
|
+
<span>点击或拖拽</span>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<!-- 没有文件且不允许上传时 -->
|
42
|
+
<div v-else class="no-preview">
|
43
|
+
<scx-icon icon="outlined-question"/>
|
44
|
+
<span>无图片</span>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<img v-if="uploadInfo.progressVisible" :src="uploadInfo.previewURL" alt="img" class="progress-preview">
|
48
|
+
|
49
|
+
<div v-if="uploadInfo.progressVisible" class="progress">
|
50
|
+
<div class="temp-file-name">
|
51
|
+
<div>
|
52
|
+
{{ uploadInfo.fileName }}
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
<div class="progress-state">
|
56
|
+
<div class="progress-state-text">
|
57
|
+
{{ uploadInfo.progressState }}
|
58
|
+
</div>
|
59
|
+
<!-- 以下为进度条 -->
|
60
|
+
<scx-progress v-model="uploadInfo.progressValue"/>
|
61
|
+
</div>
|
62
|
+
|
63
|
+
</div>
|
64
|
+
|
65
|
+
</div>
|
66
|
+
</template>
|
67
|
+
|
68
|
+
<script>
|
69
|
+
import "./index.css";
|
70
|
+
import {computed, reactive, ref, watch} from "vue";
|
71
|
+
import {percentage} from "@scx-js/scx-common";
|
72
|
+
import {ScxFSSHelper, UploadInfo} from "./helper.js";
|
73
|
+
import {useScxFSS} from "@scx-js/scx-app-x";
|
74
|
+
import ScxProgress from "../scx-progress/index.vue";
|
75
|
+
import ScxIcon from "../scx-icon/index.vue";
|
76
|
+
|
77
|
+
export default {
|
78
|
+
name: "scx-upload",
|
79
|
+
components: {
|
80
|
+
ScxProgress,
|
81
|
+
ScxIcon
|
82
|
+
},
|
83
|
+
props: {
|
84
|
+
modelValue: {
|
85
|
+
type: String,
|
86
|
+
default: null
|
87
|
+
},
|
88
|
+
uploadHandler: {
|
89
|
+
type: Function,
|
90
|
+
default: null
|
91
|
+
},
|
92
|
+
fileInfoHandler: {
|
93
|
+
type: Function,
|
94
|
+
default: null
|
95
|
+
},
|
96
|
+
beforeUpload: {
|
97
|
+
type: Function,
|
98
|
+
default: null
|
99
|
+
},
|
100
|
+
onError: {
|
101
|
+
type: Function,
|
102
|
+
default: null
|
103
|
+
},
|
104
|
+
disabled: {
|
105
|
+
type: Boolean,
|
106
|
+
default: false
|
107
|
+
},
|
108
|
+
beforeDelete: {
|
109
|
+
type: Function,
|
110
|
+
default: null
|
111
|
+
}
|
112
|
+
},
|
113
|
+
setup(props, ctx) {
|
114
|
+
const scxFSS = useScxFSS();
|
115
|
+
const scxFSSHelper = new ScxFSSHelper(scxFSS);
|
116
|
+
|
117
|
+
function getFileInfoHandler() {
|
118
|
+
return props.fileInfoHandler ? props.fileInfoHandler : (fileID) => scxFSSHelper.fileInfoHandler(fileID);
|
119
|
+
}
|
120
|
+
|
121
|
+
function getUploadHandler() {
|
122
|
+
return props.uploadHandler ? props.uploadHandler : (needUploadFile, progress) => scxFSSHelper.uploadHandler(needUploadFile, progress);
|
123
|
+
}
|
124
|
+
|
125
|
+
function getOnError() {
|
126
|
+
return props.onError ? props.onError : (error, file) => console.error(error, file);
|
127
|
+
}
|
128
|
+
|
129
|
+
const hiddenInputRef = ref(null);
|
130
|
+
|
131
|
+
function selectFile() {
|
132
|
+
hiddenInputRef.value.click();
|
133
|
+
}
|
134
|
+
|
135
|
+
function onHiddenInputChange(e) {
|
136
|
+
const needUploadFile = e.target.files[0];
|
137
|
+
//重置 上传 input 的值 保证即使点击重复文件也可以上传
|
138
|
+
hiddenInputRef.value.value = null;
|
139
|
+
callUploadHandler(needUploadFile);
|
140
|
+
}
|
141
|
+
|
142
|
+
const proxyModelValue = computed({
|
143
|
+
get() {
|
144
|
+
return props.modelValue;
|
145
|
+
},
|
146
|
+
set(value) {
|
147
|
+
ctx.emit("update:modelValue", value);
|
148
|
+
}
|
149
|
+
});
|
150
|
+
|
151
|
+
async function deleteFile() {
|
152
|
+
if (props.beforeDelete) {
|
153
|
+
const result = await props.beforeDelete(uploadInfo.copy());
|
154
|
+
if (!result) {
|
155
|
+
return;
|
156
|
+
}
|
157
|
+
}
|
158
|
+
proxyModelValue.value = "";
|
159
|
+
}
|
160
|
+
|
161
|
+
const uploadInfo = reactive(new UploadInfo());
|
162
|
+
|
163
|
+
async function callUploadHandler(needUploadFile) {
|
164
|
+
if (props.beforeUpload) {
|
165
|
+
const result = await props.beforeUpload(needUploadFile);
|
166
|
+
if (!result) {
|
167
|
+
return;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
//设定初始值
|
171
|
+
uploadInfo.progressVisible = true;
|
172
|
+
uploadInfo.fileName = needUploadFile.name;
|
173
|
+
uploadInfo.previewURL = URL.createObjectURL(needUploadFile);
|
174
|
+
//上传回调函数
|
175
|
+
const progress = (v, s = "上传中") => {
|
176
|
+
//处理一下百分比的格式防止 33.33333333333339 这种情况出现
|
177
|
+
uploadInfo.progressState = s;
|
178
|
+
uploadInfo.progressValue = percentage(v, 100);
|
179
|
+
};
|
180
|
+
//开始上传
|
181
|
+
try {
|
182
|
+
proxyModelValue.value = await getUploadHandler()(needUploadFile, progress);
|
183
|
+
uploadInfo.progressState = "上传完毕";
|
184
|
+
uploadInfo.progressVisible = false;
|
185
|
+
uploadInfo.progressValue = 0;
|
186
|
+
} catch (e) {
|
187
|
+
getOnError()(e, needUploadFile);
|
188
|
+
uploadInfo.progressState = "上传失败";
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
function callFileInfoHandler(fileID) {
|
193
|
+
if (fileID) {
|
194
|
+
getFileInfoHandler()(fileID).then(item => uploadInfo.fill(item));
|
195
|
+
} else {
|
196
|
+
uploadInfo.reset();
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
//我们根据 proxyModelValue 实时更新 fileInfo
|
201
|
+
watch(proxyModelValue, (newVal) => callFileInfoHandler(newVal), {immediate: true});
|
202
|
+
const dragover = ref(false);
|
203
|
+
|
204
|
+
function callDrop(e) {
|
205
|
+
e.preventDefault();
|
206
|
+
dragover.value = false;
|
207
|
+
const needUploadFile = e.dataTransfer.files[0];
|
208
|
+
callUploadHandler(needUploadFile);
|
209
|
+
}
|
210
|
+
|
211
|
+
function callDragover(e) {
|
212
|
+
e.preventDefault();
|
213
|
+
dragover.value = true;
|
214
|
+
}
|
215
|
+
|
216
|
+
function callDragleave(e) {
|
217
|
+
e.preventDefault();
|
218
|
+
dragover.value = false;
|
219
|
+
}
|
220
|
+
|
221
|
+
return {
|
222
|
+
hiddenInputRef,
|
223
|
+
proxyModelValue,
|
224
|
+
uploadInfo,
|
225
|
+
dragover,
|
226
|
+
onHiddenInputChange,
|
227
|
+
selectFile,
|
228
|
+
deleteFile,
|
229
|
+
callDrop,
|
230
|
+
callDragover,
|
231
|
+
callDragleave
|
232
|
+
};
|
233
|
+
}
|
234
|
+
};
|
235
|
+
</script>
|
@@ -0,0 +1,89 @@
|
|
1
|
+
.scx-upload-list {
|
2
|
+
width: 100%;
|
3
|
+
}
|
4
|
+
|
5
|
+
.scx-upload-list > .scx-group {
|
6
|
+
padding: 0;
|
7
|
+
}
|
8
|
+
|
9
|
+
.scx-upload-list .scx-group-item {
|
10
|
+
border-radius: 4px;
|
11
|
+
overflow: hidden;
|
12
|
+
border: 1px solid var(--scx-theme);
|
13
|
+
padding: 5px;
|
14
|
+
display: flex;
|
15
|
+
column-gap: 10px;
|
16
|
+
}
|
17
|
+
|
18
|
+
.scx-upload-list .scx-group-item:hover {
|
19
|
+
background: var(--scx-theme-secondary);
|
20
|
+
}
|
21
|
+
|
22
|
+
.scx-upload-list .upload-button {
|
23
|
+
align-self: start;
|
24
|
+
}
|
25
|
+
|
26
|
+
.scx-upload-list .preview-image {
|
27
|
+
height: 60px;
|
28
|
+
width: 60px;
|
29
|
+
flex-shrink: 0;
|
30
|
+
border: 1px solid var(--scx-theme);
|
31
|
+
border-radius: 4px;
|
32
|
+
box-sizing: border-box;
|
33
|
+
}
|
34
|
+
|
35
|
+
.scx-upload-list .preview-text {
|
36
|
+
display: flex;
|
37
|
+
position: relative;
|
38
|
+
width: 100%;
|
39
|
+
flex-direction: column;
|
40
|
+
justify-content: space-between;
|
41
|
+
align-items: flex-start;
|
42
|
+
overflow: hidden;
|
43
|
+
line-height: normal;
|
44
|
+
}
|
45
|
+
|
46
|
+
.scx-upload-list .file-name {
|
47
|
+
max-width: calc(100% - 70px);
|
48
|
+
overflow: hidden;
|
49
|
+
text-overflow: ellipsis;
|
50
|
+
white-space: nowrap;
|
51
|
+
color: var(--scx-theme);
|
52
|
+
font-weight: 600;
|
53
|
+
font-size: 16px;
|
54
|
+
}
|
55
|
+
|
56
|
+
.scx-upload-list .preview-text > a {
|
57
|
+
text-decoration: unset;
|
58
|
+
}
|
59
|
+
|
60
|
+
.scx-upload-list .preview-text > a:hover {
|
61
|
+
text-decoration: underline;
|
62
|
+
}
|
63
|
+
|
64
|
+
.scx-upload-list .progress-state {
|
65
|
+
display: flex;
|
66
|
+
align-items: center;
|
67
|
+
column-gap: 10px;
|
68
|
+
width: 100%;
|
69
|
+
}
|
70
|
+
|
71
|
+
.scx-upload-list .progress-state > .progress-state-text {
|
72
|
+
flex-shrink: 0;
|
73
|
+
font-weight: 600;
|
74
|
+
font-size: 14px;
|
75
|
+
}
|
76
|
+
|
77
|
+
.scx-upload-list .progress-state > .scx-progress {
|
78
|
+
width: 100%;
|
79
|
+
}
|
80
|
+
|
81
|
+
.scx-upload-list .item-info {
|
82
|
+
display: flex;
|
83
|
+
font-weight: 600;
|
84
|
+
font-size: 14px;
|
85
|
+
max-width: 100%;
|
86
|
+
overflow-x: auto;
|
87
|
+
overflow-y: hidden;
|
88
|
+
white-space: nowrap;
|
89
|
+
}
|
@@ -0,0 +1,228 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="scx-upload-list">
|
3
|
+
|
4
|
+
<!-- 隐藏的 input 用于触发点击上传事件 -->
|
5
|
+
<input ref="hiddenInputRef" multiple placeholder="file" style="display: none" type="file"
|
6
|
+
@change="onHiddenInputChange">
|
7
|
+
|
8
|
+
<scx-group v-model="uploadInfoList"
|
9
|
+
:before-remove="beforeRemove"
|
10
|
+
:show-move-button="!disabled"
|
11
|
+
:show-remove-button="!disabled">
|
12
|
+
<template v-if="!disabled" #before>
|
13
|
+
<!-- 上传按钮 -->
|
14
|
+
<button class="upload-button" type="button" @click="selectFile">
|
15
|
+
点击上传, 当前共 {{ proxyModelValue.length }} 个文件
|
16
|
+
</button>
|
17
|
+
</template>
|
18
|
+
<template #default="{index,item}">
|
19
|
+
<slot :index="index" :item="item">
|
20
|
+
<img :src="item.previewURL" alt="img" class="preview-image">
|
21
|
+
<div class="preview-text">
|
22
|
+
<a v-if="item.downloadURL" :href="item.downloadURL" class="file-name">{{ item.fileName }}</a>
|
23
|
+
<span v-else class="file-name">{{ item.fileName }}</span>
|
24
|
+
<div v-if="item.progressVisible" class="progress-state">
|
25
|
+
<div class="progress-state-text">{{ item.progressState }}</div>
|
26
|
+
<scx-progress v-model="item.progressValue"/>
|
27
|
+
</div>
|
28
|
+
<div v-else class="item-info">
|
29
|
+
上传时间 : {{ item.uploadTime }} 文件大小 : {{ item.fileSizeDisplay }}
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</slot>
|
33
|
+
</template>
|
34
|
+
</scx-group>
|
35
|
+
|
36
|
+
</div>
|
37
|
+
</template>
|
38
|
+
|
39
|
+
<script>
|
40
|
+
import "./index.css";
|
41
|
+
import {computed, reactive, ref, watch} from "vue";
|
42
|
+
import {arrayEquals, percentage} from "@scx-js/scx-common";
|
43
|
+
import {ScxFSSHelper, UploadInfo} from "../scx-upload/helper.js";
|
44
|
+
import {useScxFSS} from "@scx-js/scx-app-x";
|
45
|
+
import ScxGroup from "../scx-group/index.vue";
|
46
|
+
import ScxProgress from "../scx-progress/index.vue";
|
47
|
+
|
48
|
+
export default {
|
49
|
+
name: "scx-upload-list",
|
50
|
+
components: {
|
51
|
+
ScxGroup,
|
52
|
+
ScxProgress
|
53
|
+
},
|
54
|
+
props: {
|
55
|
+
modelValue: {
|
56
|
+
type: Array,
|
57
|
+
default: null
|
58
|
+
},
|
59
|
+
uploadHandler: {
|
60
|
+
type: Function,
|
61
|
+
default: null
|
62
|
+
},
|
63
|
+
fileInfoHandler: {
|
64
|
+
type: Function,
|
65
|
+
default: null
|
66
|
+
},
|
67
|
+
beforeUpload: {
|
68
|
+
type: Function,
|
69
|
+
default: null
|
70
|
+
},
|
71
|
+
onError: {
|
72
|
+
type: Function,
|
73
|
+
default: null
|
74
|
+
},
|
75
|
+
disabled: { //若为 true 则只具有展示效果 不能上传删除和排序
|
76
|
+
type: Boolean,
|
77
|
+
default: false
|
78
|
+
},
|
79
|
+
beforeDelete: {
|
80
|
+
type: Function,
|
81
|
+
default: null
|
82
|
+
}
|
83
|
+
},
|
84
|
+
setup(props, ctx) {
|
85
|
+
const scxFSS = useScxFSS();
|
86
|
+
const scxFSSHelper = new ScxFSSHelper(scxFSS);
|
87
|
+
|
88
|
+
function getFileInfoHandler() {
|
89
|
+
return props.fileInfoHandler ? props.fileInfoHandler : (fileID) => scxFSSHelper.fileInfoHandler(fileID);
|
90
|
+
}
|
91
|
+
|
92
|
+
function getUploadHandler() {
|
93
|
+
return props.uploadHandler ? props.uploadHandler : (needUploadFile, progress) => scxFSSHelper.uploadHandler(needUploadFile, progress);
|
94
|
+
}
|
95
|
+
|
96
|
+
function getOnError() {
|
97
|
+
return props.onError ? props.onError : (error, file) => console.error(error, file);
|
98
|
+
}
|
99
|
+
|
100
|
+
const hiddenInputRef = ref(null);
|
101
|
+
|
102
|
+
function selectFile() {
|
103
|
+
hiddenInputRef.value.click();
|
104
|
+
}
|
105
|
+
|
106
|
+
function onHiddenInputChange(e) {
|
107
|
+
const needUploadFiles = Array.from(e.target.files);
|
108
|
+
//重置 上传 input 的值 保证即使点击重复文件也可以上传
|
109
|
+
hiddenInputRef.value.value = null;
|
110
|
+
callUploadHandler(needUploadFiles);
|
111
|
+
}
|
112
|
+
|
113
|
+
const proxyModelValue = computed({
|
114
|
+
get() {
|
115
|
+
//处理有时外部数据为 null 或 undefined 的情况
|
116
|
+
return props.modelValue ? props.modelValue : [];
|
117
|
+
},
|
118
|
+
set(value) {
|
119
|
+
ctx.emit("update:modelValue", value);
|
120
|
+
}
|
121
|
+
});
|
122
|
+
/**
|
123
|
+
* 已上传的信息列表
|
124
|
+
* @type {Ref<UnwrapRef<UploadInfo[]>>}
|
125
|
+
*/
|
126
|
+
const uploadInfoList = ref([]);
|
127
|
+
/**
|
128
|
+
* 当前是否有上传任务 防止多次点击上传文件按钮上传时产生多次上传任务
|
129
|
+
* @type {boolean}
|
130
|
+
*/
|
131
|
+
let hasUploadTask = false;
|
132
|
+
|
133
|
+
//上传文件
|
134
|
+
async function callUploadHandler(needUploadFiles) {
|
135
|
+
if (props.beforeUpload) {
|
136
|
+
const result = await props.beforeUpload(needUploadFiles);
|
137
|
+
if (!result) {
|
138
|
+
return;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
for (const needUploadFile of needUploadFiles) {
|
142
|
+
const i = new UploadInfo();
|
143
|
+
i.fileName = needUploadFile.name;
|
144
|
+
i.file = needUploadFile;
|
145
|
+
i.progressState = "等待中";
|
146
|
+
i.progressVisible = true;
|
147
|
+
i.progressValue = 0;
|
148
|
+
i.previewURL = URL.createObjectURL(needUploadFile);
|
149
|
+
uploadInfoList.value.push(i);
|
150
|
+
}
|
151
|
+
//如果当前没有上传任务 则进行递归上传
|
152
|
+
if (!hasUploadTask) {
|
153
|
+
await callUploadHandler0();
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
/**
|
158
|
+
* 这里我们为了减少服务器的压力并不采取批量上传 而是一条传完再传下一条
|
159
|
+
* @returns {Promise<void>}
|
160
|
+
*/
|
161
|
+
async function callUploadHandler0() {
|
162
|
+
hasUploadTask = true;
|
163
|
+
const nextNeedUpload = uploadInfoList.value.find(u => u.progressState === "等待中");
|
164
|
+
if (nextNeedUpload) {
|
165
|
+
const progress = (v, s = "上传中") => {
|
166
|
+
//处理一下百分比的格式防止 33.33333333333339 这种情况出现
|
167
|
+
nextNeedUpload.progressState = s;
|
168
|
+
nextNeedUpload.progressValue = percentage(v, 100);
|
169
|
+
};
|
170
|
+
try {
|
171
|
+
nextNeedUpload.fileID = await getUploadHandler()(nextNeedUpload.file, progress);
|
172
|
+
nextNeedUpload.progressVisible = false;
|
173
|
+
} catch (e) {
|
174
|
+
getOnError()(e, nextNeedUpload.file);
|
175
|
+
nextNeedUpload.progressState = "上传失败";
|
176
|
+
}
|
177
|
+
const item = await getFileInfoHandler()(nextNeedUpload.fileID);
|
178
|
+
nextNeedUpload.fill(item);
|
179
|
+
nextNeedUpload.file = null;
|
180
|
+
nextNeedUpload.progressValue = 0;
|
181
|
+
//进行下一次上传
|
182
|
+
await callUploadHandler0();
|
183
|
+
} else {
|
184
|
+
hasUploadTask = false;
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
function getFileIDs(l) {
|
189
|
+
return l.map(d => d.fileID).filter(d => d);
|
190
|
+
}
|
191
|
+
|
192
|
+
function callFileInfoHandler(fileIDs) {
|
193
|
+
if (!arrayEquals(fileIDs, getFileIDs(uploadInfoList.value))) {
|
194
|
+
console.log("外部发生变化 !!!");
|
195
|
+
uploadInfoList.value = fileIDs.map(fileID => {
|
196
|
+
const u = reactive(new UploadInfo());
|
197
|
+
u.fileID = fileID;
|
198
|
+
u.fileName = fileID;
|
199
|
+
getFileInfoHandler()(u.fileID).then(item => u.fill(item));
|
200
|
+
return u;
|
201
|
+
});
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
//我们根据 proxyModelValue 实时更新 fileInfo
|
206
|
+
watch(proxyModelValue, (newVal) => callFileInfoHandler(newVal), {immediate: true});
|
207
|
+
|
208
|
+
function callProxyModelHandler(list) {
|
209
|
+
const fileIDs = getFileIDs(list);
|
210
|
+
if (!arrayEquals(fileIDs, proxyModelValue.value)) {
|
211
|
+
console.log("内部发生变化 !!!");
|
212
|
+
proxyModelValue.value = fileIDs;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
watch(uploadInfoList, (newVal) => callProxyModelHandler(newVal), {deep: true});
|
217
|
+
const beforeRemove = props.beforeDelete ? (info) => props.beforeDelete(info.copy()) : null;
|
218
|
+
return {
|
219
|
+
hiddenInputRef,
|
220
|
+
uploadInfoList,
|
221
|
+
proxyModelValue,
|
222
|
+
beforeRemove,
|
223
|
+
onHiddenInputChange,
|
224
|
+
selectFile,
|
225
|
+
};
|
226
|
+
}
|
227
|
+
};
|
228
|
+
</script>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/**
|
2
|
+
* 修改主题
|
3
|
+
* @param value {boolean} 传入 true 或 false
|
4
|
+
*/
|
5
|
+
function changeTheme(value) {
|
6
|
+
if (value) {
|
7
|
+
document.documentElement.classList.add("dark");
|
8
|
+
} else {
|
9
|
+
document.documentElement.classList.remove("dark");
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
export {changeTheme};
|
package/style/dark.css
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
/* 此处重写项目中最基础的 css 变量 */
|
2
|
+
html.dark {
|
3
|
+
|
4
|
+
/* 主要强调色 */
|
5
|
+
--scx-theme: rgb(72, 147, 73);
|
6
|
+
/* 用于背景强调色 或 hover 背景色 */
|
7
|
+
--scx-theme-bg: rgb(0, 71, 25, 0.2);
|
8
|
+
/* 次要强调色 */
|
9
|
+
--scx-theme-secondary: rgba(72, 147, 73, 0.5);
|
10
|
+
/* 主要背景 (也可以为图片) */
|
11
|
+
--scx-bg: #2b2b2b;
|
12
|
+
/* 主要文字颜色 */
|
13
|
+
--scx-text-color: whitesmoke;
|
14
|
+
/* 次要文字颜色 */
|
15
|
+
--scx-text-secondary-color: #909399;
|
16
|
+
/* 占位文字颜色 */
|
17
|
+
--scx-text-placeholder-color: #a8abb2;
|
18
|
+
|
19
|
+
/* 滚动条背景 */
|
20
|
+
--scx-scrollbar-bg: rgb(20, 20, 20);
|
21
|
+
/* 滚动条拖动滑块背景 */
|
22
|
+
--scx-scrollbar-thumb-bg: rgba(72, 147, 73, 0.4);
|
23
|
+
/* 滚动条拖动滑块背景 (hover) */
|
24
|
+
--scx-scrollbar-thumb-hover-bg: rgba(72, 147, 73, 0.8);
|
25
|
+
|
26
|
+
/* 强调内容背景 */
|
27
|
+
--scx-overlay-bg: #3c3c3c;
|
28
|
+
|
29
|
+
/* 磨砂玻璃背景 */
|
30
|
+
--scx-glass-bg: rgba(50, 50, 50, 0.8);
|
31
|
+
/* 背景滤镜 */
|
32
|
+
--scx-glass-bg-filter: blur(8px);
|
33
|
+
|
34
|
+
/* 通用的阴影 分别是 [颜色, X 偏移量, Y 偏移量, 模糊, 扩张] */
|
35
|
+
--scx-box-shadow: rgba(0, 0, 0, 0.4) 1px 1px 3px 0px;
|
36
|
+
/* 没有偏移量的阴影 */
|
37
|
+
--scx-box-shadow-center: rgba(0, 0, 0, 0.4) 0 0 3px 1px;
|
38
|
+
/* 只需要上方有阴影 */
|
39
|
+
--scx-box-shadow-top: rgba(0, 0, 0, 0.4) 0 -1px 3px 0px;
|
40
|
+
/* 只需要左边有阴影 */
|
41
|
+
--scx-box-shadow-left: rgba(0, 0, 0, 0.4) -1px 0 3px 0px;
|
42
|
+
/* 只需要下方有阴影 */
|
43
|
+
--scx-box-shadow-bottom: rgba(0, 0, 0, 0.4) 0 1px 3px 0px;
|
44
|
+
/* 只需要右边有阴影 */
|
45
|
+
--scx-box-shadow-right: rgba(0, 0, 0, 0.4) 1px 0 3px 0px;
|
46
|
+
|
47
|
+
/* 输入框 */
|
48
|
+
--scx-input_bg: rgba(255, 255, 255, 0.2);
|
49
|
+
--scx-input_box-shadow: rgba(180, 180, 180, 0.6) 0 0 1px 1px;
|
50
|
+
--scx-input-hover_box-shadow: rgba(44, 170, 46, 0.6) 0 0 2px 2px;
|
51
|
+
|
52
|
+
/* scx-panel 组件 */
|
53
|
+
--scx-panel-item_bg: rgba(255, 255, 255, 0.2);
|
54
|
+
--scx-panel-item_border-color: transparent;
|
55
|
+
--scx-panel-item_hover-bg: rgb(72, 147, 73);
|
56
|
+
--scx-panel-item_hover-border-color: transparent;
|
57
|
+
--scx-panel-item_hover-color: whitesmoke;
|
58
|
+
|
59
|
+
}
|