feat: 添加数据导出功能及相关样式

- 在管理页面中新增数据导出功能,支持导出任务、超期任务、审批数据和签到数据
- 更新应用页面,添加导出管理的弹出菜单
- 新增导出相关的 SVG 图标
- 优化样式以提升用户体验
This commit is contained in:
kuaifan 2025-09-23 16:41:07 +08:00
parent 65d30b7a30
commit 631fa0db4e
5 changed files with 131 additions and 5 deletions

View File

@ -526,6 +526,7 @@ export default {
emitter.on('approveDetails', this.openApproveDetails); emitter.on('approveDetails', this.openApproveDetails);
emitter.on('openReport', this.openReport); emitter.on('openReport', this.openReport);
emitter.on('openFavorite', this.openFavorite); emitter.on('openFavorite', this.openFavorite);
emitter.on('openManageExport', this.openManageExport);
// //
document.addEventListener('keydown', this.shortcutEvent); document.addEventListener('keydown', this.shortcutEvent);
}, },
@ -544,6 +545,7 @@ export default {
emitter.off('approveDetails', this.openApproveDetails); emitter.off('approveDetails', this.openApproveDetails);
emitter.off('openReport', this.openReport); emitter.off('openReport', this.openReport);
emitter.off('openFavorite', this.openFavorite); emitter.off('openFavorite', this.openFavorite);
emitter.off('openManageExport', this.openManageExport);
// //
document.removeEventListener('keydown', this.shortcutEvent); document.removeEventListener('keydown', this.shortcutEvent);
}, },
@ -1317,6 +1319,23 @@ export default {
this.favoriteShow = true; this.favoriteShow = true;
}, },
openManageExport(type) {
switch (type) {
case 'task':
this.exportTaskShow = true;
break;
case 'overdue':
this.exportOverdueTask();
break;
case 'approve':
this.exportApproveShow = true;
break;
case 'checkin':
this.exportCheckinShow = true;
break;
}
},
handleLongpress(event) { handleLongpress(event) {
const {type, data, element} = this.longpressData; const {type, data, element} = this.longpressData;
this.$store.commit("longpress/clear") this.$store.commit("longpress/clear")

View File

@ -23,7 +23,7 @@
:xl="{ span: 6 }" :xl="{ span: 6 }"
:xxl="{ span: 3 }"> :xxl="{ span: 3 }">
<div class="apply-col"> <div class="apply-col">
<div @click="applyClick({value: 'microApp'}, item)"> <div class="apply-item" @click="applyClick({value: 'microApp'}, item)">
<div class="logo"> <div class="logo">
<div class="apply-icon no-dark-content" :style="{backgroundImage: `url(${item.icon})`}"></div> <div class="apply-icon no-dark-content" :style="{backgroundImage: `url(${item.icon})`}"></div>
</div> </div>
@ -41,7 +41,28 @@
:xl="{ span: 6 }" :xl="{ span: 6 }"
:xxl="{ span: 3 }"> :xxl="{ span: 3 }">
<div class="apply-col"> <div class="apply-col">
<div @click="applyClick(item)"> <template v-if="item.value === 'exportManage'">
<EPopover
v-model="exportPopoverShow"
trigger="click"
placement="bottom"
popperClass="apply-export-popover"
:transfer="true">
<div slot="reference" class="apply-item">
<div class="logo">
<div class="apply-icon no-dark-content" :class="getLogoClass(item.value)"></div>
</div>
<p>{{ $L(item.label) }}</p>
</div>
<ul class="apply-export-menu">
<li @click="handleExport('task')">{{ $L('导出任务统计') }}</li>
<li @click="handleExport('overdue')">{{ $L('导出超期任务') }}</li>
<li @click="handleExport('approve')">{{ $L('导出审批数据') }}</li>
<li @click="handleExport('checkin')">{{ $L('导出签到数据') }}</li>
</ul>
</EPopover>
</template>
<div v-else class="apply-item" @click="applyClick(item)">
<div class="logo"> <div class="logo">
<div class="apply-icon no-dark-content" :class="getLogoClass(item.value)"></div> <div class="apply-icon no-dark-content" :class="getLogoClass(item.value)"></div>
<div @click.stop="applyClick(item, 'badge')" class="apply-box-top-report"> <div @click.stop="applyClick(item, 'badge')" class="apply-box-top-report">
@ -361,6 +382,8 @@ export default {
// //
appPushShow: false, appPushShow: false,
// //
exportPopoverShow: false,
//
scanLoginShow: false, scanLoginShow: false,
scanLoginLoad: false, scanLoginLoad: false,
scanLoginCode: '', scanLoginCode: '',
@ -392,6 +415,7 @@ export default {
applyList() { applyList() {
const list = [ const list = [
{value: "approve", label: "审批中心", sort: 30, show: this.microAppsIds.includes('approve')}, {value: "approve", label: "审批中心", sort: 30, show: this.microAppsIds.includes('approve')},
{value: "favorite", label: "我的收藏", sort: 45},
{value: "report", label: "工作报告", sort: 50}, {value: "report", label: "工作报告", sort: 50},
{value: "mybot", label: "我的机器人", sort: 55}, {value: "mybot", label: "我的机器人", sort: 55},
{value: "robot", label: "AI 机器人", sort: 60, show: this.microAppsIds.includes('ai')}, {value: "robot", label: "AI 机器人", sort: 60, show: this.microAppsIds.includes('ai')},
@ -419,6 +443,7 @@ export default {
{type: 'admin', value: "mail", label: "邮件通知", sort: 170}, {type: 'admin', value: "mail", label: "邮件通知", sort: 170},
{type: 'admin', value: "appPush", label: "APP 推送", sort: 180}, {type: 'admin', value: "appPush", label: "APP 推送", sort: 180},
{type: 'admin', value: "complaint", label: "举报管理", sort: 190}, {type: 'admin', value: "complaint", label: "举报管理", sort: 190},
{type: 'admin', value: "exportManage", label: "数据导出", sort: 195},
{type: 'admin', value: "allUser", label: "团队管理", sort: 200}, {type: 'admin', value: "allUser", label: "团队管理", sort: 200},
]) ])
} }
@ -457,6 +482,9 @@ export default {
case 'report': case 'report':
emitter.emit('openReport', params == 'badge' ? 'receive' : 'my'); emitter.emit('openReport', params == 'badge' ? 'receive' : 'my');
break; break;
case 'favorite':
emitter.emit('openFavorite');
break;
case 'mybot': case 'mybot':
this.getMybot(); this.getMybot();
this.mybotShow = true; this.mybotShow = true;
@ -506,6 +534,10 @@ export default {
} }
this.$emit("on-click", item.value, params); this.$emit("on-click", item.value, params);
}, },
handleExport(type) {
this.exportPopoverShow = false;
emitter.emit('openManageExport', type);
},
// //
getMybot() { getMybot() {
this.mybotLoad++ this.mybotLoad++

View File

@ -49,7 +49,7 @@
.apply-col { .apply-col {
margin-bottom: 16px; margin-bottom: 16px;
> div { .apply-item {
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
background: #FFFFFF; background: #FFFFFF;
@ -63,7 +63,7 @@
position: relative; position: relative;
border: 1px solid #f1f1f1; border: 1px solid #f1f1f1;
> .logo { .logo {
width: 40px; width: 40px;
height: 40px; height: 40px;
display: inline-block; display: inline-block;
@ -102,7 +102,7 @@
.apply-col { .apply-col {
margin-bottom: 6px; margin-bottom: 6px;
> div { .apply-item {
display: block; display: block;
text-align: center; text-align: center;
padding: 12px 0; padding: 12px 0;
@ -204,6 +204,14 @@
background-image: url("../images/application/mybot.svg"); background-image: url("../images/application/mybot.svg");
} }
&.favorite {
background-image: url("../images/application/favorite.svg");
}
&.export-manage {
background-image: url("../images/application/export.svg");
}
&.robot { &.robot {
background-image: url("../images/application/robot.svg"); background-image: url("../images/application/robot.svg");
} }
@ -233,6 +241,29 @@
} }
} }
.apply-export-popover {
padding: 4px 0 !important;
.apply-export-menu {
list-style: none;
padding: 0;
margin: 0;
min-width: 160px;
> li {
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
color: #333;
white-space: nowrap;
&:hover {
background: #f5f5f5;
}
}
}
}
.ivu-modal-wrap-apply { .ivu-modal-wrap-apply {
position: relative; position: relative;
overflow: auto; overflow: auto;

View File

@ -0,0 +1,40 @@
<svg
t="1758615909894"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="46324"
width="1024"
height="1024"
>
<path
d="M0 0m256 0l512 0q256 0 256 256l0 512q0 256-256 256l-512 0q-256 0-256-256l0-512q0-256 256-256Z"
fill="#2F80ED"
p-id="46325"
data-spm-anchor-id="a313x.search_index.0.i9.662f3a81WMCCXl"
/>
<path
d="M797.866667 0l12.8 29.866667A605.866667 605.866667 0 0 1 853.333333 256a605.866667 605.866667 0 0 1-42.666666 226.133333 588.8 588.8 0 0 1-132.266667 196.266667 588.8 588.8 0 0 1-196.266667 132.266667A605.866667 605.866667 0 0 1 256 853.333333a605.866667 605.866667 0 0 1-226.133333-42.666666l-29.866667-12.8V256a256 256 0 0 1 256-256h541.866667z"
fill="#2F80ED"
p-id="46326"
data-spm-anchor-id="a313x.search_index.0.i6.662f3a81WMCCXl"
/>
<path
d="M1024 388.266667V768a256 256 0 0 1-256 256H341.333333v-42.666667a605.866667 605.866667 0 0 1 42.666667-226.133333 618.666667 618.666667 0 0 1 328.533333-328.533333A605.866667 605.866667 0 0 1 938.666667 384z"
fill="#2F80ED"
p-id="46327"
data-spm-anchor-id="a313x.search_index.0.i10.662f3a81WMCCXl"
/>
<path
d="M358.4 844.8a708.266667 708.266667 0 0 1 25.6-89.6 618.666667 618.666667 0 0 1 328.533333-328.533333l123.733334-34.133334a708.266667 708.266667 0 0 1-25.6 89.6 588.8 588.8 0 0 1-132.266667 196.266667 588.8 588.8 0 0 1-196.266667 132.266667z"
fill="#2F80ED"
p-id="46328"
data-spm-anchor-id="a313x.search_index.0.i12.662f3a81WMCCXl"
/>
<path
d="M290.133333 738.133333h443.733334a38.4 38.4 0 0 1 34.133333 38.4 38.4 38.4 0 0 1-34.133333 34.133334H290.133333a38.4 38.4 0 0 1-34.133333-34.133334 38.4 38.4 0 0 1 34.133333-38.4z m149.333334-110.933333v-170.666667H256L512 213.333333l256 243.2h-183.466667v170.666667a42.666667 42.666667 0 0 1-42.666666 42.666667h-59.733334a42.666667 42.666667 0 0 1-42.666666-42.666667z"
fill="#FFFFFF"
p-id="46329"
/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 48 48">
<rect width="48" height="48" rx="12" fill="#F6C04C"/>
<path fill="#FFFFFF" d="M24 13.6c.6 0 1.15.33 1.43.86l3.04 5.7 6.21.93c.6.09 1.09.5 1.27 1.06.18.56.03 1.19-.39 1.61l-4.5 4.47 1.07 6.3c.1.6-.14 1.2-.63 1.56-.49.37-1.14.42-1.69.13L24 32.17l-5.71 3c-.55.29-1.2.24-1.69-.13-.49-.36-.73-.96-.63-1.56l1.07-6.3-4.5-4.47c-.42-.42-.57-1.05-.39-1.61.18-.56.67-.97 1.27-1.06l6.21-.93 3.04-5.7c.28-.53.83-.86 1.43-.86Z"/>
</svg>

After

Width:  |  Height:  |  Size: 500 B