2024-08-31 18:02:48 +08:00

1000 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div :class="$options.name" :show="show" :size="size" :style="style">
<div class="upload-list-tray">
<!-- 托盘头部 -->
<div class="header" ref="header" @dblclick.stop.prevent="dblclickHeader">
<div class="left">
<div class="title">
<span class="upload-count" slot="reference">上传队列</span>
<div class="upload-info" v-if="liveSpeed && liveSpeed > 0">
<el-divider :direction="`vertical`" />
<div class="info-item live-speed">
<label>速度</label>
<span>{{ $g.getSize(liveSpeed) }}/s</span>
</div>
<div class="info-item taken-time" v-if="takenTime && takenTime > 0">
<label>已耗时</label>
<span>{{
$g.date.toHourMinuteSecondByMillisecond(takenTime * 1000, {
zh: true,
hideMilliSecond: true,
hideZero: true,
})
}}</span>
</div>
<div class="info-item remain-time" v-if="remainTime && remainTime > 0">
<label>剩余</label
><span>{{
$g.date.toHourMinuteSecondByMillisecond(remainTime * 1000, {
zh: true,
hideMilliSecond: true,
hideZero: true,
})
}}</span>
</div>
</div>
</div>
</div>
<div class="right" @mousedown.stop>
<!-- 控制文件的图标按钮 -->
<div class="file-btns" v-if="showDelSuccessIconBtn || showErrorIconBtn">
<div
class="icon-btn"
v-if="showDelSuccessIconBtn"
@click.stop="clearAllSuccessFile"
title="清除所有已经成功的上传记录"
>
<i class="el-icon-delete" style="color: #67c23a"></i>
</div>
<div
class="icon-btn"
v-if="showErrorIconBtn"
@click.stop="clearAllErrorFile"
title="清除所有失败的上传记录"
>
<i class="el-icon-delete-solid" style="color: #f56c6c"></i>
</div>
<div
class="icon-btn"
v-if="showErrorIconBtn"
@click.stop="uploadAllErrorFile"
title="重新上传所有失败的文件"
>
<i class="el-icon-upload2" style="color: #409eff"></i>
</div>
<template v-if="uploadList.length > maxShowUploadFileCount">
<div
class="icon-btn"
v-if="expandAllUploadList"
@click.stop="expandAllUploadList = false"
title="折叠只显示前10条上传记录"
>
<i class="el-icon-folder" style="color: #409eff"></i>
</div>
<el-tooltip
v-else
:content="`请谨慎展开列表,这将导致您的网页很卡!`"
:effect="`dark`"
:enterable="false"
:placement="`top-start`"
:popper-class="`sg-el-tooltip`"
:transition="`none`"
:disabled="uploadList.length < 200"
>
<div
class="icon-btn"
@click.stop="expandAllUploadList = true"
title="展开所有上传记录"
>
<i class="el-icon-folder-opened" style="color: #409eff"></i>
</div>
</el-tooltip>
</template>
</div>
<!-- 控制托盘的图标按钮 -->
<div class="tray-btns">
<div
class="icon-btn"
v-if="size !== 'lg' && showRightBottomBtn"
@click.stop="toRightBottomPosition"
title="回到原来的位置"
>
<i class="el-icon-bottom-right"></i>
</div>
<div
class="icon-btn"
v-if="size !== 'mn'"
@click.stop="size = 'mn'"
title="最小化"
>
<i class="el-icon-minus"></i>
</div>
<div
class="icon-btn"
v-if="size !== 'md'"
@click.stop="size = 'md'"
title="还原"
>
<i :class="size === 'lg' ? 'el-icon-copy-document' : 'el-icon-d-caret'"></i>
</div>
<div
class="icon-btn"
v-if="size !== 'lg'"
@click.stop="size = 'lg'"
title="全屏"
>
<i class="el-icon-full-screen"></i>
</div>
<div class="icon-btn" @click.stop="close">
<i class="el-icon-close"></i>
</div>
</div>
</div>
</div>
<!-- 上传中的文件列表 -->
<div class="upload-file-list">
<ul>
<li
v-for="(a, i) in expandAllUploadList ? uploadList : uploadList.slice(0, 10)"
:key="i"
:title="
a.size > 1024 * 1024 * 500
? `超大文件上传中,请耐心等待,切勿关闭或刷新浏览器!`
: ''
"
>
<div class="left">
<div class="icon-btns">
<el-button
title="移出上传队列"
:show="a.status === 'error'"
class="remove-icon-btn icon-btn"
type="danger"
icon="el-icon-delete-solid"
size="mini"
plain
circle
@click.stop="removeUploadFile(a)"
></el-button>
</div>
<!-- 动画加载旋转 -->
<div
class="fileLoading"
v-loading="a.percent < 100"
v-if="a.percent < 100 && a.status !== 'error'"
></div>
<!-- 上传成功icon -->
<div class="loadingSuccessIcon" v-if="a.percent === 100">
<i class="el-icon-success" style="color: #67c23a"></i>
</div>
<!-- 上传失败icon -->
<div class="loadingEorrorIcon" v-if="a.status === 'error'">
<i class="el-icon-error" style="color: #f56c6c"></i>
</div>
<span class="name" :title="a.filePath || a.name">
{{ a.filePath || a.name }}
<!-- {{ a.filePath && a.filePath.includes(`/`) ? `[路径:${a.filePath}]` : "" }} -->
</span>
<el-tag class="size" size="mini"
>{{ $g.getSize(a.size * (a.percent / 100)) }}/{{
$g.getSize(a.size)
}}</el-tag
>
<!-- <el-progress class="progress" :percentage="a.percent"></el-progress> -->
<el-progress
class="progress"
style="width: 100%"
type="line"
:percentage="parseInt(a.percent)"
:show-text="true"
:stroke-width="10"
:text-inside="false"
:color="'#409EFF'"
:define-back-color="'#eee'"
/>
</div>
<div class="right">
<span class="tip" :color="a.color">{{ a.tip }}</span>
<div class="icon-btns">
<el-button
:show="a.status === 'error'"
title="重新上传"
class="upload-icon-btn icon-btn"
type="primary"
icon="el-icon-upload2"
size="mini"
plain
circle
@click.stop="startUploadFile(a)"
></el-button>
</div>
</div>
</li>
</ul>
</div>
<!-- 折叠按钮 -->
<sgCollapseBtn
style="z-index: 1"
:collapseLabel="`上传记录`"
v-model="expandAllUploadList"
v-if="uploadList.length > maxShowUploadFileCount"
/>
<div class="footer">
<div class="text" v-html="popoverContent"></div>
<div class="progress" v-if="uploadList.length > 1 && totalPercentage < 100">
<label>总进度</label>
<el-progress
style="width: 100%"
type="line"
:percentage="parseInt(totalPercentage)"
:show-text="true"
:stroke-width="10"
:text-inside="false"
:color="'#409EFF'"
:define-back-color="'#eee'"
/>
</div>
</div>
</div>
<!-- 拖拽移动窗体 -->
<sgDragMove
:data="dragMoveDoms"
:cursor="{
grab: 'default',
grabbing: 'default',
}"
nearPadding="10"
:disabled="size === 'lg' && disabledDragMove"
@dragStart="$emit(`dragStart`, dragMoveDoms)"
@dragging="
showRightBottomBtn = true;
$emit(`dragging`, dragMoveDoms);
"
@dragEnd="$emit(`dragEnd`, dragMoveDoms)"
mousemoveNearSide
/>
<!-- 拖拽改变窗体尺寸 -->
<sgDragSize
v-if="resizeable_"
:disabled="size === 'lg'"
@dragStart="disabledDragMove = true"
@dragging="draggingSize"
@dragEnd="disabledDragMove = false"
:minWidth="minWidth"
:minHeight="minHeight"
/>
</div>
</template>
<script>
import sgCollapseBtn from "./sgCollapseBtn";
import sgDragMove from "./sgDragMove";
import sgDragSize from "./sgDragSize";
export default {
name: "sgUploadTray",
components: {
sgCollapseBtn,
sgDragMove,
sgDragSize,
},
data() {
return {
maxShowUploadFileCount: 10, //默认展示上传数量
expandAllUploadList: false, //默认折叠
minWidth: 800,
minHeight: 40,
style_bk: null,
style: {},
resizeable_: true,
disabledDragMove: false, //屏蔽移动
show: false,
showRightBottomBtn: false,
size: "md", //lg全屏、md普通、mn最小
uploadList: [],
dragMoveDoms: [
/* {
canDragDom: elementDOM,//可以拖拽的位置元素
moveDom: elementDOM,//拖拽同步移动的元素
} */
], //可以拖拽移动的物体
lastUploadedTotalSize: 0, //记录上次已经下载完成的总大小
liveSpeed: 0, //瞬时下载速度(单位B)
takenTime: 0, //已耗时
remainTime: 0, //剩余下载时长
interval: null,
second: 1, //轮训间隔秒钟
successFileList: [], //成功文件列表
errorFileList: [], //失败文件列表
remainFileList: [], //剩余文件列表
};
},
props: ["data", "value", "resizeable", "position"],
watch: {
value: {
handler(d) {
this.show = d;
},
deep: true,
immediate: true,
},
show: {
handler(d) {
this.$emit(`input`, d);
},
deep: true,
immediate: true,
},
data: {
handler(d) {
this.uploadList = d || [];
},
deep: true,
immediate: true,
},
uploadList: {
handler(newValue, oldValue) {
if (newValue && Object.keys(newValue).length) {
this.interval || this.startUploadCalcLiveSpeed();
this.successFileList = newValue.filter((v) => v.percent == 100);
this.errorFileList = newValue.filter((v) => v.status === "error");
this.remainFileList = newValue.filter(
(v) => v.status !== "error" && v.status !== "success"
);
} else {
this.successFileList = [];
this.errorFileList = [];
this.remainFileList = [];
}
},
deep: true, //深度监听
immediate: true, //立即执行
},
resizeable: {
handler(newValue, oldValue) {
this.resizeable_ = newValue === "" || newValue;
},
deep: true, //深度监听
immediate: true, //立即执行
},
size: {
handler(newValue, oldValue) {
switch (newValue) {
case "lg":
case "mn":
this.style_bk = JSON.parse(JSON.stringify(this.style));
delete this.style.width, delete this.style.height;
break;
case "md":
this.style_bk && (this.style = JSON.parse(JSON.stringify(this.style_bk)));
break;
}
},
deep: true, //深度监听
immediate: true, //立即执行
},
},
computed: {
showDelSuccessIconBtn(d) {
return this.uploadList.some((v) => v.percent == 100);
},
showErrorIconBtn(d) {
return this.uploadList.some((v) => v.status == "error");
},
popoverContent(d) {
let r = [];
this.successFileList.length &&
r.push(
`已上传成功<span style="color: #67C23A;">${this.successFileList.length}</span>个`
);
this.errorFileList.length &&
r.push(`失败<span style="color: #F56C6C;">${this.errorFileList.length}</span>个`);
this.remainFileList.length &&
r.push(
`剩余<span style="color: #409EFF;">${this.remainFileList.length}</span>个`
);
if (this.uploadList.length) {
return `共计${this.uploadList.length}个文件,${
r.length ? `${r.join("")}文件` : ``
}`;
} else {
return `暂无待上传文件`;
}
},
// 总体进度
totalPercentage() {
if (this.uploadList.length) {
return parseFloat(
((this.successFileList.length / this.uploadList.length) * 100).toFixed(2)
);
} else {
return 0;
}
},
// 真正上传中的文件对象数组
uploadingFiles(d) {
let uploadingFiles_ = (this.uploadList || []).filter(
(v) => v.percent < 100 && v.status !== "error" && v.status !== "success"
);
if (uploadingFiles_.length) {
this.$emit(`changeUploadingListClose`, {
path: this.position,
close: this.close,
});
} else {
this.$emit(`changeUploadingListClose`, null);
}
return uploadingFiles_;
},
},
mounted() {
this.$el.style.setProperty("--minWidth", `${this.minWidth}px`); //js往css传递局部参数
this.$el.style.setProperty("--minHeight", `${this.minHeight}px`); //js往css传递局部参数
this.dragMoveDoms = [
{
canDragDom: this.$refs.header, //托盘的头部可以拖拽
moveDom: this.$el, //拖拽的时候,整个上传列表一起跟随移动
},
];
},
destroyed() {
clearInterval(this.interval);
},
methods: {
// 开始计算瞬时下载速度
startUploadCalcLiveSpeed() {
clearInterval(this.interval);
this.interval = setInterval(() => {
this.calcLiveSpeed();
}, 1000 * this.second);
},
// 结束计算瞬时下载速度
endUploadCalcLiveSpeed(d) {
clearInterval(this.interval);
this.interval = null;
this.liveSpeed = 0;
this.takenTime = 0;
this.remainTime = 0;
},
// 没有上传进程文件才结束计算速度
ifNoUploadingFile_EndCalcLiveSpeed(d) {
this.uploadingFiles.length || this.endUploadCalcLiveSpeed();
},
// 计算瞬时下载速度
calcLiveSpeed(d) {
this.takenTime++;
let uploadList = this.uploadList;
if (uploadList.length) {
let totalSize = uploadList.reduce(
(prevResult, current) => prevResult + current.size,
0
); //求和需要上传的文件总大小
let uploadedTotalSize = uploadList.reduce(
(prevResult, current) => prevResult + current.size * (0.01 * current.percent),
0
); //求和已经上传的文件总大小
let remainTotalSize = totalSize - uploadedTotalSize; //剩余需要上传的文件
if (this.lastUploadedTotalSize) {
this.liveSpeed = (uploadedTotalSize - this.lastUploadedTotalSize) / this.second; //瞬时速度
this.remainTime = remainTotalSize / this.liveSpeed; //瞬时剩余时长
} else {
this.liveSpeed = 0;
}
this.lastUploadedTotalSize = uploadedTotalSize; //记录本次已经上传的总大小
} else {
this.endUploadCalcLiveSpeed();
}
},
clearAllSuccessFile() {
let successFileList = this.uploadList.filter((v) => v.percent == 100);
if (successFileList.length === 0)
return this.$message(`暂无可以移除的成功记录,请稍后再试!`);
this.$emit(`clearAllSuccessFile`, successFileList);
this.$nextTick(() => {
successFileList.forEach((file) => {
file.removeFile(); //移除原始队列
});
this.uploadList = this.uploadList.filter((v) => v.percent < 100);
});
},
clearAllErrorFile() {
let errorFileList = this.uploadList.filter((v) => v.status == "error");
if (errorFileList.length === 0)
return this.$message(`暂无可以移除的失败记录,请稍后再试!`);
this.$emit(`clearAllErrorFile`, errorFileList);
this.$nextTick(() => {
errorFileList.forEach((file) => {
file.removeFile(); //移除原始队列
});
this.uploadList = this.uploadList.filter((v) => v.status !== "error");
});
},
uploadAllErrorFile(d) {
let errorFileList = this.uploadList.filter((v) => v.status == "error");
errorFileList.forEach((fileData) => this.startUploadFile(fileData));
if (errorFileList.length === 0) return this.$message(`暂无失败记录,请稍后再试!`);
this.$emit(`uploadAllErrorFile`, errorFileList);
},
draggingSize({ style }) {
this.disabledDragMove = true;
this.style = style;
},
toRightBottomPosition(d) {
this.showRightBottomBtn = false;
let rect = this.$el.getBoundingClientRect();
this.$el.style.left = `${innerWidth - rect.width}px`;
this.$el.style.top = `${innerHeight - rect.height}px`;
// 用下面的写法会清除掉setProperty属性
/* this.$el.style = {
left: innerWidth - rect.width + "px",
top: innerHeight - rect.height + "px",
}; */
},
dblclickHeader(d) {
switch (this.size) {
case "lg":
this.size = "md";
break;
case "md":
this.size = "mn";
break;
case "mn":
this.size = "md";
break;
default:
}
},
removeAllFilesFromList() {
this.uploadList.forEach((file) => {
file.removeFile(); //移除原始队列
});
this.uploadList = []; //移除托盘队列
},
removeFileFromList(d) {
let file = this.uploadList.find((v) => v.uid == d.uid);
file.removeFile(); //移除原始队列
this.uploadList.splice(
this.uploadList.findIndex((v) => v.uid == d.uid),
1
); //移除托盘队列
},
// 重新上传失败的记录
startUploadFile(fileData) {
fileData.startUpload({ handleTrigger: true });
},
// 移出某一个队列文件
removeUploadFile(d) {
if (d.status === "error" || d.status === "success") {
this.removeFileFromList(d);
} else if (d.percent < 100) {
this.$confirm(`${d.name}正在上传中,确定要取消吗?`, `提示`, {
dangerouslyUseHTMLString: true,
confirmButtonText: `确定`,
cancelButtonText: `取消`,
type: "warning",
})
.then(() => {
this.$emit(`stopUpload`, [d]);
this.$nextTick(() => {
this.removeFileFromList(d);
this.ifNoUploadingFile_EndCalcLiveSpeed();
});
})
.catch(() => {});
} else {
this.removeFileFromList(d);
}
},
// 关闭托盘
close({ cb } = {}) {
let stopUploadList = this.uploadingFiles;
if (stopUploadList.length) {
this.$confirm(`您还有正在上传中的文件,确定要取消吗?`, `提示`, {
dangerouslyUseHTMLString: true,
confirmButtonText: `确定`,
cancelButtonText: `取消`,
type: "warning",
})
.then(() => {
this.show = false;
this.$emit(`stopUpload`, stopUploadList);
this.$nextTick(() => {
this.endUploadCalcLiveSpeed();
this.removeAllFilesFromList();
cb && cb(stopUploadList); //完成后回调
});
})
.catch(() => {});
} else {
this.show = false;
}
},
},
};
</script>
<style lang="scss" scoped>
.sgUploadTray {
$headerHeight: 40px; //头部高度
$footerHeight: 40px; //底部统计上传进度高度
$collapseBtnHeight: 30px; //折叠按钮高度
$minWidth: var(--minWidth); //托盘最小宽度
$minHeight: var(--minHeight); //托盘最小高度
$loadingWidth: 30px; //加载动画宽度(旋转全全)
$rightWidth: 200px; //右侧宽度
$sizeWidth: 200px; //文件大小宽度宽度
$progressWidth: 100px; //进度条宽度
$tipWidth: 100px; //提示文本宽度
// ----------------------------------------
z-index: 2001; //根据情况自己拿捏(太大了会遮住element的其他弹窗组件)v-loading默认是2000的z-index
user-select: none;
position: fixed;
right: 10px;
bottom: 10px;
width: $minWidth;
background-color: white;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
overflow: hidden;
border: 1px solid #eee;
font-size: 14px;
display: none;
&[show] {
display: block;
}
&[size="lg"] {
left: 0 !important;
top: 0 !important;
width: 100vw;
height: 100vh;
transition: none;
.upload-file-list {
max-height: calc(100vh - 60px) !important;
}
}
&[size="md"] {
width: $minWidth;
height: revert;
}
&[size="mn"] {
width: $minWidth;
height: $minHeight;
}
.upload-list-tray {
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-bottom: 20px;
width: 100%;
height: 100%;
position: relative;
.header {
flex-shrink: 0;
font-size: 16px;
font-weight: bold;
width: 100%;
height: $headerHeight;
box-sizing: border-box;
padding: 10px 20px;
/*从上往下线性渐变背景*/
background: linear-gradient(#409eff11, white);
color: #409eff;
display: flex;
justify-content: space-between;
align-items: center;
.left {
display: flex;
align-items: center;
flex-grow: 1;
.title {
display: flex;
align-items: center;
flex-wrap: nowrap;
.upload-info {
display: flex;
align-items: center;
color: black;
flex-shrink: 0;
align-items: center;
font-weight: normal;
.info-item {
margin-right: 5px;
&:last-of-type {
margin-right: 0;
}
span {
font-family: DIN-Light;
color: #409eff;
}
&.live-speed {
span {
font-family: DIN-Black;
}
}
}
}
}
.icon-btns {
display: flex;
align-items: center;
flex-wrap: nowrap;
.icon-btn {
cursor: pointer;
margin-right: 5px;
&:last-of-type {
margin-right: 0;
}
i {
pointer-events: none;
}
&:hover {
opacity: 0.618;
}
}
}
}
.right {
display: flex;
align-items: center;
justify-content: flex-end;
flex-shrink: 0;
pointer-events: auto;
.icon-btn {
margin-left: 10px;
cursor: pointer;
i {
pointer-events: none;
}
&:hover {
opacity: 0.618;
}
&:first-of-type {
margin-left: 0;
}
}
.file-btns {
margin-left: 10px;
display: flex;
flex-wrap: nowrap;
justify-content: flex-end;
box-sizing: border-box;
padding: 0 10px;
border-right: 1px solid #eee;
}
.tray-btns {
margin-left: 10px;
display: flex;
flex-wrap: nowrap;
justify-content: flex-end;
}
}
}
.upload-file-list {
width: 100%;
flex-grow: 1;
overflow-y: auto;
height: max-content;
max-height: calc(
100vh - #{$headerHeight} - #{$footerHeight} - #{$collapseBtnHeight} - 40px
);
box-sizing: border-box;
padding: 0 20px;
ul {
width: 100%;
li {
line-height: 1.6;
box-sizing: border-box;
padding: 10px;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.left {
width: calc(100% - #{$rightWidth});
display: flex;
align-items: center;
flex-grow: 1;
flex-shrink: 0;
// 上传队列记录左侧侧操作按钮
.icon-btns {
display: flex;
flex-wrap: nowrap;
.icon-btn {
display: none;
&[show] {
margin-right: 15px;
display: block;
}
}
}
.fileLoading {
flex-shrink: 0;
width: 30px;
margin-right: 5px;
height: 0;
transform: scale(0.5);
}
.loadingSuccessIcon,
.loadingEorrorIcon {
margin-right: 5px;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
.name {
text-align: left;
margin-right: 10px;
width: calc(
100% - #{$loadingWidth} - #{$sizeWidth} - #{$progressWidth} - #{$rightWidth} -
20px
);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
flex-shrink: 0;
flex-grow: 1;
}
.size {
margin-right: 10px;
max-width: $sizeWidth;
/*单行省略号*/
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
flex-shrink: 0;
}
.progress {
max-width: $progressWidth;
display: flex;
align-items: center;
flex-wrap: nowrap;
flex-shrink: 0;
}
}
.right {
display: flex;
align-items: center;
justify-content: flex-end;
width: $rightWidth;
.tip {
width: $tipWidth;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
flex-shrink: 0;
text-align: right;
&[color="red"] {
color: #f56c6c;
}
&[color="green"] {
color: #67c23a;
}
&[color="blue"] {
color: #409eff;
}
}
// 上传队列记录右侧操作按钮
.icon-btns {
display: flex;
flex-wrap: nowrap;
.icon-btn {
display: none;
&[show] {
margin-left: 15px;
display: block;
}
}
}
}
&:hover {
background-color: #409eff11;
color: #409eff;
.left {
// 移入上传队列记录左侧操作按钮
.icon-btns {
.icon-btn {
display: block;
&:last-of-type {
margin-right: 10px;
}
}
}
}
.right {
.tip {
margin-right: 15px;
}
// 移入上传队列记录右侧操作按钮
.icon-btns {
.icon-btn {
display: block;
&:first-of-type {
margin-left: 0;
}
}
}
}
}
}
}
}
.footer {
font-weight: normal;
flex-shrink: 0;
font-size: 14px;
font-weight: bold;
width: 100%;
height: $footerHeight;
box-sizing: border-box;
padding: 10px 20px;
margin-bottom: -20px;
background: linear-gradient(white, #eff2f7);
color: #909399;
display: flex;
align-items: center;
flex-wrap: nowrap;
white-space: nowrap;
* {
font-weight: normal;
}
.text {
white-space: nowrap;
}
.progress {
max-width: 200px;
flex-grow: 1;
white-space: nowrap;
display: flex;
align-items: center;
flex-wrap: nowrap;
label {
white-space: nowrap;
flex-shrink: 0;
margin-right: 5px;
}
>>> .el-progress {
white-space: nowrap;
.el-progress__text {
font-weight: normal;
font-size: 14px !important;
}
}
}
}
}
}
</style>