LabPersonMatch.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <!-- 实验室与人员匹配 -->
  2. <template>
  3. <div class="lab-person-card">
  4. <div class="card-header">
  5. <span class="card-title">实验室与人员匹配</span>
  6. <div class="header-right">
  7. <el-select v-model="valueYear" @change="topChange()" size="small" style="width:220px">
  8. <el-option
  9. v-for="dict in optionList"
  10. :key="dict.value"
  11. :label="dict.label"
  12. :value="dict.value"
  13. />
  14. </el-select>
  15. <!-- 分组按钮 -->
  16. <div class="table-top-button-box">
  17. <div>
  18. <p @click="checkButton(2)" :class="checkIndex == 2?'check-table-button':''">研究生</p>
  19. <p @click="checkButton(3)" :class="checkIndex == 3?'check-table-button':''">本科生</p>
  20. <!--<p @click="checkButton(1)" :class="checkIndex == 1?'check-table-button':''">教职工</p>-->
  21. </div>
  22. </div>
  23. <p class="export-btn export-btn--blue" @click="exportButton1()">
  24. <i class="el-icon-download"></i> 导出各学院数据
  25. </p>
  26. <p class="export-btn export-btn--green" @click="exportButton2()">
  27. <i class="el-icon-download"></i> 导出实验室人员明细
  28. </p>
  29. </div>
  30. </div>
  31. <!-- 柱状图 -->
  32. <div class="chart-wrap">
  33. <p class="bar-chart" v-if="!chartData[0]" style="color:#999;font-size:14px;line-height:260px;text-align: center;">暂无数据</p>
  34. <div class="bar-chart" v-if="chartData[0]" id="bar-chart" ref="barChart"></div>
  35. </div>
  36. <!-- 筛选 -->
  37. <div class="filter-row">
  38. <!--<el-cascader-->
  39. <!--v-if="adminType == 1"-->
  40. <!--style="width: 180px"-->
  41. <!--clearable-->
  42. <!--v-model="queryParams.deptId"-->
  43. <!--:options="optionListA"-->
  44. <!--:props="{-->
  45. <!--emitPath:false,-->
  46. <!--checkStrictly: true,-->
  47. <!--value: 'deptId',-->
  48. <!--label: 'deptName',-->
  49. <!--children: 'child',-->
  50. <!--}"-->
  51. <!--:show-all-levels="false"-->
  52. <!--filterable-->
  53. <!--placeholder="筛选学院"-->
  54. <!--@change="examElStatisticsLabList"-->
  55. <!--&gt;</el-cascader>-->
  56. <el-select v-if="adminType == 1" v-model="queryParams.deptId" clearable @change="checkButtonFunction" placeholder="学院" style="width: 180px">
  57. <el-option
  58. v-for="dict in optionListA"
  59. :key="dict.deptId"
  60. :label="dict.deptName"
  61. :value="dict.deptId"
  62. />
  63. </el-select>
  64. <!--<el-select v-model="queryParams.levelId" clearable @change="checkButtonFunction" placeholder="实验室等级" style="width:200px;">-->
  65. <!--<el-option v-for="item in optionListB" :key="item.levelId" :label="item.titleName"-->
  66. <!--:value="item.levelId"/>-->
  67. <!--</el-select>-->
  68. </div>
  69. <!-- 表格 -->
  70. <div class="page-content-box">
  71. <el-table class="table-box" :data="tableData" border size="small">
  72. <el-table-column prop="deptName" label="学院" >
  73. <template slot-scope="scope">
  74. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.deptName}}</span>
  75. </template>
  76. </el-table-column>
  77. <el-table-column prop="level1Count" label="Ⅰ级人数" width="120" align="center" >
  78. <template slot-scope="scope">
  79. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.level1Count}}</span>
  80. </template>
  81. </el-table-column>
  82. <el-table-column prop="level2Count" label="Ⅱ级人数" width="120" align="center" >
  83. <template slot-scope="scope">
  84. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.level2Count}}</span>
  85. </template>
  86. </el-table-column>
  87. <el-table-column prop="level3Count" label="Ⅲ级人数" width="120" align="center" >
  88. <template slot-scope="scope">
  89. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.level3Count}}</span>
  90. </template>
  91. </el-table-column>
  92. <el-table-column prop="level4Count" label="Ⅳ级人数" width="120" align="center" >
  93. <template slot-scope="scope">
  94. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.level4Count}}</span>
  95. </template>
  96. </el-table-column>
  97. <el-table-column prop="noLabNeedCount" v-if="checkIndex == 2" label="无需进入实验室人数" width="160" align="center">
  98. <template slot-scope="scope">
  99. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.noLabNeedCount}}</span>
  100. </template>
  101. </el-table-column>
  102. <el-table-column prop="unboundCount" v-if="checkIndex == 2" label="未匹配人数" width="140" align="center">
  103. <template slot-scope="scope">
  104. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.unboundCount}}</span>
  105. </template>
  106. </el-table-column>
  107. <el-table-column prop="unboundCount" v-if="checkIndex == 3" label="未匹配人数" width="140" align="center">
  108. <template slot-scope="scope">
  109. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.unboundCount}}</span>
  110. </template>
  111. </el-table-column>
  112. <el-table-column prop="totalCount" label="总人数" width="140" align="center">
  113. <template slot-scope="scope">
  114. <span :style="scope.row.type == 2?'color:#184c93;font-weight:700;':''">{{scope.row.totalCount}}</span>
  115. </template>
  116. </el-table-column>
  117. <el-table-column label="操作" width="160" show-overflow-tooltip>
  118. <template slot-scope="scope">
  119. <div class="table-button-box">
  120. <p class="table-button-null"></p>
  121. <p class="table-button-p"
  122. v-if="adminType == 1"
  123. @click="tableButton(1,scope.row)"
  124. >{{scope.row.deptId?'人员明细':'全部明细'}}</p>
  125. <p class="table-button-null"></p>
  126. </div>
  127. </template>
  128. </el-table-column>
  129. </el-table>
  130. <pagination :page-sizes="[20, 30, 40, 50]"
  131. v-show="total>0"
  132. :total="total"
  133. :page.sync="queryParams.page"
  134. :limit.sync="queryParams.pageSize"
  135. @pagination="examElStatisticsLabList"
  136. />
  137. </div>
  138. <el-drawer
  139. size="100%"
  140. custom-class="labPersonMatch-index-drawer"
  141. :append-to-body="true"
  142. :title="drawerTitle"
  143. ref="drawer"
  144. :visible.sync="drawer"
  145. @close="drawerReturnButton"
  146. direction="rtl">
  147. <userListPage :drawerPropsData="drawerPropsData" v-if="drawerType == 1"></userListPage>
  148. </el-drawer>
  149. </div>
  150. </template>
  151. <script>
  152. import echarts from 'echarts'
  153. import {
  154. systemUserGetPost,
  155. } from "@/api/basicsModules/index";
  156. import {
  157. examElStatisticsLabChart,examElStatisticsLabList,examElStatisticsGetAcademicYear,
  158. laboratoryLevelConfigGetLevelTitleList,systemDeptCurrentDept,
  159. } from "@/api/safetyEducationExaminationNew/index";
  160. import userListPage from "./userListPage.vue";
  161. export default {
  162. name: 'LabPersonMatch',
  163. components: {
  164. userListPage,
  165. },
  166. data() {
  167. return {
  168. adminType:0,
  169. optionList:[],
  170. optionListA:[],
  171. optionListB:[],
  172. valueYear:'',
  173. checkIndex:2,
  174. queryParams:{
  175. page:1,
  176. pageSize:20,
  177. deptId:'',
  178. levelId:'',
  179. },
  180. chartData: [],
  181. tableData: [],
  182. total:0,
  183. //弹窗相关
  184. drawerTitle: '',
  185. drawerType: '',
  186. drawer: false,
  187. drawerPropsData:{},
  188. }
  189. },
  190. mounted() {
  191. this.systemUserGetPost();
  192. },
  193. beforeDestroy() {
  194. if (this._barChart) this._barChart.dispose()
  195. },
  196. methods: {
  197. //操作按钮
  198. tableButton(type,row){
  199. if(type == 1){
  200. this.$set(this, 'drawerTitle', '实验室匹配人员明细')
  201. let obj = {
  202. adminType:this.adminType,
  203. valueYear:this.valueYear,
  204. personType:this.checkIndex,
  205. deptId:row.deptId,
  206. }
  207. this.$set(this, 'drawerPropsData', obj)
  208. this.$set(this, 'drawerType', 1)
  209. this.$set(this, 'drawer', true)
  210. }
  211. },
  212. //弹窗返回
  213. drawerReturnButton(){
  214. this.$set(this, 'drawerType', 0)
  215. },
  216. //查询当前登录用户身份-校级/院级
  217. systemUserGetPost(){
  218. systemUserGetPost({}).then(response => {
  219. for(let i=0;i<response.data.length;i++){
  220. if(response.data[i].postCode == 'school_level_admin'){
  221. if(response.data[i].isPost){
  222. this.$set(this,'adminType','1');
  223. }
  224. }else if(response.data[i].postCode == 'college_admin'){
  225. if(response.data[i].isPost){
  226. this.$set(this,'adminType','2');
  227. }
  228. }
  229. }
  230. this.examElStatisticsGetAcademicYear();
  231. this.laboratoryLevelConfigGetLevelTitleList();
  232. this.systemDeptCurrentDept();
  233. });
  234. },
  235. //顶部学年切换
  236. topChange(){
  237. this.examElStatisticsLabChart();
  238. this.examElStatisticsLabList();
  239. },
  240. //身份切换
  241. checkButton(val){
  242. if(this.checkIndex!=val){
  243. this.$set(this,'checkIndex',val);
  244. this.examElStatisticsLabChart();
  245. this.examElStatisticsLabList();
  246. }
  247. },
  248. checkButtonFunction(){
  249. this.examElStatisticsLabChart();
  250. this.examElStatisticsLabList();
  251. },
  252. //导出
  253. exportButton1(){
  254. let self = this;
  255. self.$confirm(`确认导出各学院数据?`, "提示", {
  256. confirmButtonText: "确定",
  257. cancelButtonText: "取消",
  258. type: "warning"
  259. }).then(async () => {
  260. let obj = {
  261. academicYear:this.valueYear,
  262. personType:this.checkIndex,
  263. examTypeId:this.queryParams.examTypeId,
  264. };
  265. if(this.adminType == 1){
  266. obj.deptId = this.queryParams.deptId;
  267. }else{
  268. obj.deptId = localStorage.getItem('deptId');
  269. }
  270. self.download('/exam/elStatistics/lab/export', {...obj}, '实验室与人员匹配学院数据.xlsx')
  271. }).catch(() => {})
  272. },
  273. exportButton2(){
  274. let self = this;
  275. self.$confirm(`确认导出实验室人员明细?`, "提示", {
  276. confirmButtonText: "确定",
  277. cancelButtonText: "取消",
  278. type: "warning"
  279. }).then(async () => {
  280. let obj = {
  281. academicYear:this.valueYear,
  282. personType:this.checkIndex,
  283. examTypeId:this.queryParams.examTypeId,
  284. };
  285. if(this.adminType == 1){
  286. obj.deptId = this.queryParams.deptId;
  287. }else{
  288. obj.deptId = localStorage.getItem('deptId');
  289. }
  290. self.download('/exam/elStatistics/lab/detail-export', {...obj}, '实验室与人员匹配实验室人员明细.xlsx')
  291. }).catch(() => {})
  292. },
  293. initBarChart() {
  294. const chart = echarts.init(this.$refs.barChart)
  295. this._barChart = chart
  296. const data = this.chartData
  297. let num = 0;
  298. for(let i=0;i<data.length;i++){
  299. if(data[i].count>num){
  300. num = data[i].count
  301. }
  302. }
  303. chart.setOption({
  304. grid: { top: 30, bottom: 20, left: 50, right: 30, containLabel: false },
  305. xAxis: {
  306. type: 'category',
  307. data: data.map(d => d.levelName),
  308. axisLine: { lineStyle: { color: '#e0e0e0' } },
  309. axisTick: { show: false },
  310. axisLabel: { fontSize: 12, color: '#666' },
  311. },
  312. yAxis: {
  313. type: 'value',
  314. max: (value) => {
  315. // value.max 是三个 series 中的最大值,向上取整到 50 的倍数
  316. return Math.ceil(value.max / 50) * 50
  317. },
  318. splitNumber:5,
  319. splitLine: { lineStyle: { color: '#f0f0f0' } },
  320. axisLabel: { fontSize: 11, color: '#aaa' },
  321. axisLine: { show: false },
  322. axisTick: { show: false },
  323. },
  324. series: [
  325. {
  326. type: 'bar',
  327. barWidth: 60,
  328. data: data.map(d => ({
  329. value: d.count,
  330. itemStyle: { color: d.levelColor },
  331. })),
  332. label: {
  333. show: true,
  334. position: 'top',
  335. fontSize: 12,
  336. fontWeight: 700,
  337. color: '#555',
  338. formatter: '{c}',
  339. },
  340. },
  341. ],
  342. tooltip: {
  343. trigger: 'axis',
  344. axisPointer: { type: 'shadow' },
  345. formatter: (params) => `${params[0].name}: ${params[0].value}`,
  346. },
  347. })
  348. },
  349. //图表
  350. examElStatisticsLabChart() {
  351. let obj = {
  352. academicYear:this.valueYear,
  353. personType:this.checkIndex,
  354. };
  355. if(this.adminType == 1){
  356. obj.deptId = this.queryParams.deptId;
  357. }else{
  358. obj.deptId = localStorage.getItem('deptId');
  359. }
  360. examElStatisticsLabChart(obj).then(response => {
  361. this.$set(this, 'chartData', response.data)
  362. if(response.data[0]){
  363. this.$nextTick(() => {
  364. this.initBarChart()
  365. })
  366. }
  367. })
  368. },
  369. //列表
  370. examElStatisticsLabList() {
  371. let obj = JSON.parse(JSON.stringify(this.queryParams))
  372. obj.academicYear = this.valueYear;
  373. obj.personType = this.checkIndex;
  374. if(this.adminType == 1){
  375. obj.deptId = this.queryParams.deptId;
  376. }else{
  377. obj.deptId = localStorage.getItem('deptId');
  378. }
  379. examElStatisticsLabList(obj).then(response => {
  380. response.data.schoolSummary.type = 2
  381. let list = [];
  382. if(this.adminType == 1&&!this.queryParams.deptId){
  383. list = response.data.records.concat([response.data.schoolSummary]);
  384. }else{
  385. list = response.data.records;
  386. }
  387. this.$set(this, 'tableData', list)
  388. this.$set(this,'total',response.data.total);
  389. })
  390. },
  391. //查询风险等级
  392. laboratoryLevelConfigGetLevelTitleList() {
  393. laboratoryLevelConfigGetLevelTitleList({}).then(response => {
  394. this.$set(this, 'optionListB', response.data)
  395. })
  396. },
  397. //学院列表
  398. systemDeptCurrentDept(){
  399. systemDeptCurrentDept({}).then(response => {
  400. const list = response.data || [];
  401. this.formatTreeData(list);
  402. if(list[0]){
  403. this.$set(this,'optionListA',list[0].child);
  404. }else{
  405. this.$set(this,'optionListA',[]);
  406. }
  407. });
  408. },
  409. //学年列表
  410. examElStatisticsGetAcademicYear(){
  411. examElStatisticsGetAcademicYear({}).then(response => {
  412. this.$set(this,'optionList',response.data);
  413. this.$set(this,'valueYear',response.data[0].value);
  414. this.examElStatisticsLabChart();
  415. this.examElStatisticsLabList();
  416. });
  417. },
  418. }
  419. }
  420. </script>
  421. <style scoped lang="scss">
  422. .lab-person-card {
  423. background: #fff;
  424. /*border-radius: 6px;*/
  425. padding: 20px;
  426. margin-bottom: 12px;
  427. /*box-shadow: 0 1px 4px rgba(0,0,0,0.06);*/
  428. background: rgba(255, 255, 255, 0.95);
  429. border: 1px solid #e8edf5;
  430. border-radius: 16px;
  431. box-shadow: 0 4px 20px rgba(37, 50, 74, 0.06);
  432. .card-header {
  433. display: flex;
  434. justify-content: space-between;
  435. align-items: center;
  436. margin-bottom: 16px;
  437. .card-title { font-size: 16px; font-weight: 700; color: #222; }
  438. .header-right { display: flex; gap: 20px; }
  439. }
  440. /* 分组筛选按钮 */
  441. .table-top-button-box {
  442. display: inline-block;
  443. vertical-align: top;
  444. div {
  445. display: flex;
  446. p {
  447. border: 1px solid #DCDFE6;
  448. width: 80px;
  449. text-align: center;
  450. height: 40px;
  451. line-height: 40px;
  452. cursor: pointer;
  453. font-size: 14px;
  454. &:nth-child(1) { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
  455. /*&:nth-child(2) { border-left: none; border-right: none; }*/
  456. &:nth-child(2) { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
  457. &:nth-child(3) { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
  458. }
  459. .check-table-button{
  460. border: 1px solid #0183fa!important;
  461. background-color: #0183fa!important;;
  462. color:#fff!important;;
  463. }
  464. }
  465. }
  466. .export-btn {
  467. display: inline-block;
  468. padding: 0 16px;
  469. height: 40px;
  470. line-height: 40px;
  471. border-radius: 4px;
  472. font-size: 14px;
  473. cursor: pointer;
  474. color: #fff;
  475. &--blue { background-color: #0183fa; border: 1px solid #0183fa; }
  476. &--green { background-color: #00B176; border: 1px solid #00B176; }
  477. }
  478. .chart-wrap {
  479. border: 1px solid #eee;
  480. border-radius: 6px;
  481. padding: 20px 20px 30px;
  482. margin-bottom: 16px;
  483. .bar-chart {
  484. height: 260px;
  485. }
  486. }
  487. .filter-row {
  488. display: flex;
  489. gap: 20px;
  490. margin-bottom: 12px;
  491. }
  492. .page-content-box{
  493. height:500px;
  494. padding:0;
  495. }
  496. .total-link { color: #409eff; }
  497. }
  498. </style>
  499. <style lang="scss">
  500. .labPersonMatch-index-drawer{
  501. .el-drawer__body{
  502. padding:0;
  503. }
  504. .el-drawer__header{
  505. margin:0;
  506. }
  507. }
  508. </style>