840 lines
21 KiB
Vue
840 lines
21 KiB
Vue
<template>
|
||
<section class="Statistics">
|
||
<div class="left">
|
||
<header>应报到单位名单</header>
|
||
<div class="left_tree">
|
||
<el-input
|
||
size="small"
|
||
v-model="filterText"
|
||
placeholder="请输入单位..."
|
||
suffix-icon="iconfont iconSearch"
|
||
clearable
|
||
></el-input>
|
||
<div class="left_cont">
|
||
<el-tree
|
||
:data="treeData"
|
||
:props="defaultProps"
|
||
ref="tree"
|
||
node-key="partyOrgId"
|
||
empty-text="搜索不到相关内容"
|
||
@node-click="handleNodeClick"
|
||
:default-expanded-keys="[user.info.organizationId]"
|
||
:filter-node-method="filterNode"
|
||
:highlight-current="true"
|
||
:current-node-key="defaultArr"
|
||
v-if="treeData.length > 0"
|
||
>
|
||
</el-tree>
|
||
<div v-else class="no-data" style="height: 70%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="right">
|
||
<div class="top">
|
||
<header>近12个月学员活动参与情况统计</header>
|
||
<div class="month" id="month"></div>
|
||
</div>
|
||
<div class="middle">
|
||
<header>
|
||
<el-date-picker
|
||
v-model="selectMonth"
|
||
type="month"
|
||
@change="typeScale()"
|
||
size="mini"
|
||
:clearable="false"
|
||
value-format="yyyy-MM-dd"
|
||
placeholder="选择年月"
|
||
>
|
||
</el-date-picker>
|
||
</header>
|
||
<div class="content">
|
||
<div class="middle-left">
|
||
<ul>
|
||
<li>
|
||
<p>应报到学员人数</p>
|
||
<div class="num">
|
||
<span>{{ numObj.party_num }}</span>
|
||
<span>人</span>
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<p>本月报名活动人数</p>
|
||
<div class="num">
|
||
<span>{{ numObj.registered_num }}</span>
|
||
<span>人</span>
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<p>实际参与活动人数</p>
|
||
<div class="num">
|
||
<span>{{ numObj.actual_num }}</span>
|
||
<span>人</span>
|
||
</div>
|
||
</li>
|
||
<li>
|
||
<p>活动实际参与率</p>
|
||
<div class="num">
|
||
<span>{{ numObj.proportion }}</span>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="middle-right" id="middle-right"></div>
|
||
</div>
|
||
</div>
|
||
<div class="bottom">
|
||
<header>
|
||
<ul>
|
||
<li
|
||
v-for="(e, index) in table"
|
||
:key="index"
|
||
@click="handleClick(e, index)"
|
||
:class="{ activeNav: e.id == activeId }"
|
||
>
|
||
{{ e.label }}
|
||
</li>
|
||
<span ref="border"></span>
|
||
</ul>
|
||
</header>
|
||
<el-row
|
||
type="flex"
|
||
justify="space-between"
|
||
align="middle"
|
||
style="padding: 8px 16px"
|
||
>
|
||
<el-col>
|
||
<el-date-picker
|
||
v-model="searchObj.ymd"
|
||
type="month"
|
||
@change="(page.current = 1), searchList()"
|
||
size="small"
|
||
:clearable="false"
|
||
value-format="yyyy-MM-01"
|
||
placeholder="选择年月"
|
||
>
|
||
</el-date-picker>
|
||
<el-button
|
||
icon="iconfont iconExported"
|
||
size="mini"
|
||
style="margin-left: 8px"
|
||
@click="outPut()"
|
||
:disabled="!Boolean(tableData.length)"
|
||
>导出
|
||
</el-button>
|
||
</el-col>
|
||
<el-col style="display: flex; justify-content: space-between">
|
||
<el-input
|
||
size="small"
|
||
v-model="searchObj.name"
|
||
placeholder="姓名"
|
||
prefix-icon="iconfont iconSearch"
|
||
clearable
|
||
style="width: 220px; margin-right: 4px"
|
||
@keyup.enter.native="searchList()"
|
||
/>
|
||
<el-button
|
||
type="primary"
|
||
icon="iconfont iconSearch"
|
||
size="small"
|
||
@click="searchList()"
|
||
>查询</el-button
|
||
>
|
||
<el-button
|
||
icon="el-icon-refresh-right"
|
||
size="small"
|
||
@click="(searchObj.name = ''), searchList()"
|
||
>重置</el-button
|
||
>
|
||
</el-col>
|
||
</el-row>
|
||
<div style="padding: 0 16px; box-sizing: border-box">
|
||
<el-table
|
||
:data="tableData"
|
||
header-cell-class-name="table-header"
|
||
tooltip-effect="light"
|
||
row-class-name="table-row"
|
||
cell-class-name="table-cell"
|
||
height="260"
|
||
>
|
||
<el-table-column
|
||
prop="name"
|
||
label="姓名"
|
||
show-tooltip-when-overflow
|
||
>
|
||
<span slot-scope="{ row }">{{ row.name || "-" }}</span>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="sex"
|
||
label="性别"
|
||
show-tooltip-when-overflow
|
||
align="center"
|
||
>
|
||
<span slot-scope="{ row }">
|
||
{{ dict.getLabel("sex", row.sex) || "-" }}
|
||
</span>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="partyOrg"
|
||
label="所属组织"
|
||
show-tooltip-when-overflow
|
||
>
|
||
<span slot-scope="{ row }">{{ row.partyOrg || "-" }}</span>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="signupCount"
|
||
label="报名次数"
|
||
show-tooltip-when-overflow
|
||
align="center"
|
||
>
|
||
<span slot-scope="{ row }">{{ row.signupCount }}</span>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="partakeCount"
|
||
label="参与次数"
|
||
show-tooltip-when-overflow
|
||
align="center"
|
||
>
|
||
<span slot-scope="{ row }">{{ row.partakeCount }}</span>
|
||
</el-table-column>
|
||
<div slot="empty" class="no-data" style="height: 160px"></div>
|
||
</el-table>
|
||
</div>
|
||
<div class="pagination">
|
||
<el-pagination
|
||
background
|
||
@current-change="
|
||
(v) => {
|
||
page.current = v;
|
||
searchList();
|
||
}
|
||
"
|
||
@size-change="
|
||
(v) => {
|
||
page.size = v;
|
||
page.current = 1;
|
||
searchList();
|
||
}
|
||
"
|
||
:current-page.sync="page.current"
|
||
:total="page.total"
|
||
layout="total,prev, pager, next,sizes, jumper"
|
||
:page-size="page.size"
|
||
:page-sizes="[5, 10, 20, 50, 100]"
|
||
>
|
||
</el-pagination>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<script>
|
||
import { mapState } from "vuex"
|
||
import * as echarts from 'echarts'
|
||
|
||
export default {
|
||
name: "Statistics",
|
||
props: {
|
||
instance: Function,
|
||
dict: Object,
|
||
permissions: Function,
|
||
},
|
||
data() {
|
||
return {
|
||
treeData: [],
|
||
filterText: "",
|
||
defaultArr: "",
|
||
partyOrgId: "", //当前选中的id
|
||
data12: {},
|
||
selectMonth: "",
|
||
activeId: "0",
|
||
numObj: {},
|
||
searchObj: {
|
||
name: "",
|
||
listType: "0",
|
||
ymd: "",
|
||
},
|
||
tableData: [],
|
||
page: {
|
||
size: 5,
|
||
current: 1,
|
||
total: 0,
|
||
},
|
||
};
|
||
},
|
||
watch: {
|
||
filterText(val) {
|
||
this.$refs.tree.filter(val);
|
||
},
|
||
},
|
||
computed: {
|
||
...mapState(["user"]),
|
||
table() {
|
||
return [
|
||
{ label: "学员活动记录", id: "0" },
|
||
{ label: "未参与活动名单", id: "1" },
|
||
];
|
||
},
|
||
defaultProps() {
|
||
return {
|
||
children: "children",
|
||
label: "label",
|
||
};
|
||
},
|
||
},
|
||
methods: {
|
||
handleNodeClick(data) {
|
||
this.partyOrgId = data.partyOrgId;
|
||
this.activeId = "0";
|
||
this.searchObj.name = "";
|
||
this.searchObj.listType = "0";
|
||
this.$nextTick(() => {
|
||
this.month12();
|
||
this.typeScale();
|
||
this.searchList();
|
||
});
|
||
},
|
||
filterNode(value, data) {
|
||
if (!value) return true;
|
||
return data.label.indexOf(value) !== -1;
|
||
},
|
||
// 根据登录用户单位查 树形结构
|
||
searchSysAll() {
|
||
this.treeData = [];
|
||
this.instance
|
||
.post("/app/apppartyreportconfig/party-list", null, {
|
||
params: { id: this.user.info.organizationId },
|
||
})
|
||
.then((res) => {
|
||
if (res && res.code == 0) {
|
||
res.data = res.data.map((a) => {
|
||
if (a.partyMemberCount) {
|
||
return {
|
||
...a,
|
||
label: `${a.partyOrgName}(${a.partyMemberCount})`,
|
||
};
|
||
} else {
|
||
return { ...a, label: a.partyOrgName };
|
||
}
|
||
// ${a.partyOrgName}(${a.partyMemberCount})
|
||
});
|
||
this.treeData = res.data.filter(
|
||
(e) => e.partyOrgId == this.user.info.organizationId
|
||
);
|
||
this.handleNodeClick(this.treeData[0]);
|
||
this.$nextTick(() => {
|
||
this.$refs.tree.setCurrentKey(this.treeData[0].partyOrgId);
|
||
});
|
||
this.defaultArr = this.treeData[0].partyOrgId;
|
||
this.treeData.map((t) => this.addChildParty(t, res.data));
|
||
}
|
||
});
|
||
},
|
||
month12() {
|
||
this.instance
|
||
.post("/app/apppartyreport/trend", null, {
|
||
params: {
|
||
partyOrgId: this.partyOrgId,
|
||
},
|
||
})
|
||
.then((res) => {
|
||
if (res.code == 0) {
|
||
this.data12 = res.data;
|
||
this.showMonth12(res.data);
|
||
}
|
||
});
|
||
},
|
||
typeScale() {
|
||
this.instance
|
||
.post("/app/apppartyreport/type-scale", null, {
|
||
params: {
|
||
partyOrgId: this.partyOrgId,
|
||
ymd: this.selectMonth,
|
||
},
|
||
})
|
||
.then((res) => {
|
||
if (res.code == 0) {
|
||
this.numObj = res.data;
|
||
this.showPartyScale();
|
||
}
|
||
});
|
||
},
|
||
handleClick(e, index) {
|
||
this.activeId = e.id;
|
||
this.searchObj.listType = e.id;
|
||
this.searchObj.name = "";
|
||
this.page.current = 1;
|
||
this.page.size = 5;
|
||
this.searchList();
|
||
this.$refs.border.style.left = index * 160 + "px";
|
||
},
|
||
searchList() {
|
||
this.instance
|
||
.post(`/app/apppartyreport/list-signup`, null, {
|
||
params: {
|
||
...this.searchObj,
|
||
partyOrgId: this.partyOrgId,
|
||
...this.page,
|
||
},
|
||
})
|
||
.then((res) => {
|
||
if (res?.data) {
|
||
this.tableData = res.data.records;
|
||
this.page.total = res.data.total;
|
||
}
|
||
});
|
||
},
|
||
outPut() {
|
||
this.instance
|
||
.post("/app/apppartyreport/export-signup", null, {
|
||
responseType: "blob",
|
||
params: {
|
||
partyOrgId: this.partyOrgId,
|
||
...this.searchObj,
|
||
},
|
||
})
|
||
.then((res) => {
|
||
const link = document.createElement("a");
|
||
let blob = new Blob([res], { type: "application/vnd.ms-excel" });
|
||
link.style.display = "none";
|
||
link.href = URL.createObjectURL(blob);
|
||
let fileName = "";
|
||
this.activeId == 0
|
||
? (fileName = "学员活动记录")
|
||
: (fileName = "未参与活动名单");
|
||
link.setAttribute("download", fileName + ".xls");
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
});
|
||
},
|
||
showMonth12(data) {
|
||
let myChart2 = echarts.init(document.getElementById("month"));
|
||
let months = [];
|
||
let arr1 = [];
|
||
let arr2 = [];
|
||
let arr3 = [];
|
||
data.forEach((e) => {
|
||
months.push(e.cycle);
|
||
arr1.push(e.partyNum);
|
||
arr2.push(e.registeredNumrc);
|
||
arr3.push(e.actualNumrc);
|
||
});
|
||
let option = {
|
||
tooltip: {
|
||
trigger: "axis",
|
||
axisPointer: {
|
||
type: "cross",
|
||
label: {
|
||
backgroundColor: "#6a7985",
|
||
},
|
||
},
|
||
},
|
||
dataset: {
|
||
source: [
|
||
["product", ...months],
|
||
["应报到学员人次", ...arr1],
|
||
["报名活动人次", ...arr2],
|
||
["实际参与活动人次", ...arr3],
|
||
],
|
||
},
|
||
legend: {
|
||
data: ["应报到学员人次", "报名活动人次", "实际参与活动人次"],
|
||
},
|
||
toolbox: {
|
||
feature: {
|
||
saveAsImage: {},
|
||
},
|
||
},
|
||
grid: {
|
||
left: "3%",
|
||
right: "4%",
|
||
bottom: "3%",
|
||
containLabel: true,
|
||
},
|
||
xAxis: {
|
||
boundaryGap: false,
|
||
type: "category",
|
||
},
|
||
yAxis: { gridIndex: 0 },
|
||
|
||
series: [
|
||
{ type: "line", smooth: true, seriesLayoutBy: "row" },
|
||
{ type: "line", smooth: true, seriesLayoutBy: "row" },
|
||
{ type: "line", smooth: true, seriesLayoutBy: "row" },
|
||
],
|
||
};
|
||
|
||
myChart2.setOption(option);
|
||
|
||
window.onresize = function () {
|
||
myChart2.resize();
|
||
};
|
||
},
|
||
showPartyScale() {
|
||
let myChart = echarts.init(document.getElementById("middle-right"));
|
||
let legendData = [];
|
||
let seriesData = [];
|
||
if (JSON.stringify(this.numObj.reportTypeMap) != "[]") {
|
||
this.numObj.reportTypeMap.forEach((e) => {
|
||
legendData.push(
|
||
this.dict.getLabel("partyReportSignupReportType", e.report_type)
|
||
);
|
||
seriesData.push({
|
||
value: e.count,
|
||
name: this.dict.getLabel(
|
||
"partyReportSignupReportType",
|
||
e.report_type
|
||
),
|
||
});
|
||
});
|
||
} else {
|
||
legendData = [
|
||
"居住地社区报到服务",
|
||
"单位联系社区报到服务",
|
||
"其他社区报到服务",
|
||
];
|
||
seriesData = [
|
||
{ value: 0, name: "居住地社区报到服务" },
|
||
{ value: 0, name: "单位联系社区报到服务" },
|
||
{ value: 0, name: "其他社区报到服务" },
|
||
];
|
||
}
|
||
let option = {
|
||
backgroundColor: "rgba(249,249,249,1)",
|
||
|
||
title: {
|
||
text: "学员报到类型比例",
|
||
x: "left",
|
||
},
|
||
|
||
tooltip: {
|
||
trigger: "item",
|
||
formatter: "{a} <br/>{b} : {c} ({d}%)",
|
||
},
|
||
legend: {
|
||
orient: "vertical",
|
||
right: 50,
|
||
top: 70,
|
||
bottom: 20,
|
||
width: "auto",
|
||
data: legendData,
|
||
},
|
||
series: [
|
||
{
|
||
name: "学员报到类型比例",
|
||
type: "pie",
|
||
radius: ["45%", "75%"],
|
||
center: ["30%", "55%"],
|
||
avoidLabelOverlap: false,
|
||
label: {
|
||
position: "inside",
|
||
formatter: "{d}%",
|
||
},
|
||
|
||
data: seriesData,
|
||
itemStyle: {
|
||
normal: {
|
||
color: function (params) {
|
||
//自定义颜色
|
||
var colorList = [
|
||
"#4B87FE",
|
||
"#FFAA44",
|
||
"#FF4466",
|
||
"#FFAA44",
|
||
"#2EA222",
|
||
];
|
||
return colorList[params.dataIndex];
|
||
},
|
||
},
|
||
},
|
||
},
|
||
],
|
||
};
|
||
myChart.setOption(option);
|
||
window.onresize = function () {
|
||
myChart.resize();
|
||
};
|
||
},
|
||
getNowDate() {
|
||
let mydate = new Date();
|
||
let mymonth = mydate.getMonth() + 1;
|
||
mymonth < 10 ? (mymonth = `0${mymonth}`) : mymonth;
|
||
let year = mydate.getFullYear();
|
||
return `${year}-${mymonth}-01`;
|
||
},
|
||
},
|
||
mounted() {
|
||
this.searchSysAll();
|
||
this.partyOrgId = this.user.info.organizationId;
|
||
this.dict.load("partyReportSignupReportType", "sex");
|
||
this.selectMonth = this.getNowDate();
|
||
this.searchObj.ymd = this.getNowDate();
|
||
this.$nextTick(() => {
|
||
this.month12();
|
||
this.typeScale();
|
||
this.searchList();
|
||
});
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.Statistics {
|
||
width: 100%;
|
||
height: 100%;
|
||
padding: 16px;
|
||
box-sizing: border-box;
|
||
overflow: auto;
|
||
.left {
|
||
width: 264px;
|
||
background-color: #fff;
|
||
border-radius: 4px;
|
||
height: 1104px;
|
||
border: 1px solid rgba(216, 220, 227, 1);
|
||
float: left;
|
||
|
||
header {
|
||
padding: 0 16px;
|
||
color: #222;
|
||
height: 40px;
|
||
line-height: 40px;
|
||
background: #eeeff1;
|
||
border-bottom: 1px solid #e5e5e5;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
border-radius: 4px 4px 0 0;
|
||
}
|
||
|
||
.left_tree {
|
||
padding: 8px;
|
||
width: 100%;
|
||
height: 100%;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.left_cont {
|
||
width: 100%;
|
||
margin-top: 8px;
|
||
overflow-y: auto;
|
||
height: calc(100% - 60px);
|
||
|
||
::v-deep .el-tree {
|
||
background-color: #fff;
|
||
width: 600px;
|
||
}
|
||
|
||
.right_btn {
|
||
width: 96px;
|
||
background: #fff;
|
||
border-radius: 2px;
|
||
font-size: 12px;
|
||
padding: 4px 0;
|
||
position: fixed;
|
||
z-index: 999;
|
||
|
||
li {
|
||
height: 28px;
|
||
line-height: 28px;
|
||
cursor: pointer;
|
||
text-indent: 12px;
|
||
}
|
||
|
||
li:hover {
|
||
background-color: #eff6ff;
|
||
color: #5088ff;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
::v-deep .el-input__inner {
|
||
border-radius: 0px;
|
||
}
|
||
|
||
::v-deep .el-tree-node__children {
|
||
width: 240px;
|
||
overflow-x: scroll;
|
||
}
|
||
}
|
||
|
||
.right {
|
||
width: calc(100% - 280px);
|
||
float: right;
|
||
height: 1104px;
|
||
|
||
.top {
|
||
width: 100%;
|
||
height: 288px;
|
||
background-color: #fff;
|
||
border-radius: 4px;
|
||
|
||
header {
|
||
color: #333333;
|
||
padding: 0 16px;
|
||
box-sizing: border-box;
|
||
height: 47px;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
line-height: 47px;
|
||
background: rgba(255, 255, 255, 1);
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.month {
|
||
width: 100%;
|
||
padding: 16px;
|
||
box-sizing: border-box;
|
||
height: calc(100% - 47px);
|
||
}
|
||
}
|
||
|
||
.middle {
|
||
width: 100%;
|
||
height: 320px;
|
||
margin-top: 16px;
|
||
background-color: #fff;
|
||
border-radius: 4px;
|
||
|
||
header {
|
||
padding: 0 16px;
|
||
box-sizing: border-box;
|
||
height: 48px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.content {
|
||
width: 100%;
|
||
padding: 16px;
|
||
box-sizing: border-box;
|
||
height: calc(100% - 48px);
|
||
|
||
.middle-left {
|
||
width: 50%;
|
||
height: 100%;
|
||
float: left;
|
||
|
||
ul {
|
||
overflow: hidden;
|
||
width: 95%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
|
||
li {
|
||
width: 48%;
|
||
height: 112px;
|
||
padding: 8px;
|
||
box-sizing: border-box;
|
||
background: rgba(249, 249, 249, 1);
|
||
border-radius: 2px;
|
||
margin-bottom: 16px;
|
||
margin-left: 4%;
|
||
|
||
&:nth-of-type(2n - 1) {
|
||
margin-left: 0;
|
||
}
|
||
|
||
p {
|
||
color: #333333;
|
||
font-size: 16px;
|
||
height: 32px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.num {
|
||
height: 80px;
|
||
line-height: 60px;
|
||
|
||
span:nth-of-type(1) {
|
||
color: #2266ff;
|
||
font-size: 32px;
|
||
}
|
||
|
||
span:nth-of-type(2) {
|
||
font-size: 16px;
|
||
color: rgba(153, 153, 153, 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.middle-right {
|
||
width: 50%;
|
||
height: 100%;
|
||
float: left;
|
||
padding: 4px;
|
||
box-sizing: border-box;
|
||
background: rgba(249, 249, 249, 1);
|
||
border-radius: 2px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.bottom {
|
||
width: 100%;
|
||
height: 464px;
|
||
margin-top: 16px;
|
||
background-color: #fff;
|
||
border-radius: 4px;
|
||
|
||
header {
|
||
width: 100%;
|
||
height: 56px;
|
||
padding: 0 16px;
|
||
box-sizing: border-box;
|
||
border-bottom: 1px solid rgb(241, 237, 237);
|
||
|
||
ul {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
|
||
li {
|
||
width: 160px;
|
||
height: 56px;
|
||
line-height: 56px;
|
||
float: left;
|
||
text-align: center;
|
||
font-size: 16px;
|
||
color: #333333;
|
||
position: relative;
|
||
transition: opacity 1s;
|
||
cursor: pointer;
|
||
opacity: 0.7;
|
||
|
||
&:hover {
|
||
color: rgba(34, 102, 255, 1);
|
||
}
|
||
}
|
||
|
||
.activeNav {
|
||
opacity: 1;
|
||
font-weight: bold;
|
||
color: rgba(34, 102, 255, 1);
|
||
}
|
||
|
||
span {
|
||
width: 160px;
|
||
height: 3px;
|
||
display: block;
|
||
background-color: rgba(34, 102, 255, 1);
|
||
transition: left 0.5s;
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
::v-deep .el-col-24 {
|
||
width: auto;
|
||
}
|
||
}
|
||
</style>
|