Files
dvcp_v2_webapp/project/pingchang/apps/AppCommunityMember/Statistics.vue
2022-10-20 10:07:37 +08:00

840 lines
21 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>
<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>