analyse.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. <template>
  2. <div v-loading="tableDataLoad" class="all">
  3. <template v-if="headValue.length > 0">
  4. <header class="flex-box flex-d-center">
  5. <div class="flex-box">
  6. <el-cascader v-model="headValue" :options="options" :props="props" :show-all-levels="false"></el-cascader>
  7. <!-- :props="{
  8. expandTrigger: 'hover',
  9. label: 'name',
  10. value: 'id',
  11. children: 'list'
  12. }" -->
  13. </div>
  14. <div>
  15. <el-button-group v-if="this.$isAuthoritys_jx(this.$11)">
  16. <el-button type="primary" icon="el-icon-document" @click="handleCommand('C')">导出明细报表</el-button>
  17. <el-button type="primary" icon="el-icon-document" @click="handleCommand('A')">导出结果报表</el-button>
  18. </el-button-group>
  19. <!-- <el-button type="primary">
  20. 导出报表
  21. </el-button> -->
  22. <!--
  23. <el-dropdown>
  24. <el-dropdown-menu slot="dropdown">
  25. <el-dropdown-item command="A">考核结果报表</el-dropdown-item>
  26. <el-dropdown-item command="B">多次考核结果对比表</el-dropdown-item>
  27. <el-dropdown-item command="C">单次考核对比表</el-dropdown-item>
  28. <el-dropdown-item command="D">自定义导出</el-dropdown-item>
  29. </el-dropdown-menu>
  30. </el-dropdown>
  31. -->
  32. </div>
  33. </header>
  34. <div class="main">
  35. <div class="main-herader flex-box-ce">
  36. <div class="flex-1 fontColorB item" @click="amfTabLckick(0)">
  37. <div>{{ flow.all }}</div>
  38. <div>参与人数</div>
  39. <div class="bian"></div>
  40. </div>
  41. <div class="flex-1 fontColorB item" @click="amfTabLckick(1)">
  42. <div>{{ flow.target }}</div>
  43. <div>目标制定</div>
  44. </div>
  45. <div class="flex-1 fontColorB item" @click="amfTabLckick(2)">
  46. <div>{{ flow.confirm }}</div>
  47. <div>目标确认</div>
  48. </div>
  49. <div class="flex-1 fontColorB item" @click="amfTabLckick(3)">
  50. <div>{{ flow.execution }}</div>
  51. <div>执行中</div>
  52. </div>
  53. <div class="flex-1 fontColorB item" @click="amfTabLckick(4)">
  54. <div>{{ flow.result_value }}</div>
  55. <div>结果值录入</div>
  56. </div>
  57. <div class="flex-1 fontColorB item" @click="amfTabLckick(5)">
  58. <div>{{ flow.score }}</div>
  59. <div>评分</div>
  60. </div>
  61. <div class="flex-1 fontColorB item" @click="amfTabLckick(9)">
  62. <div>{{ flow.review }}</div>
  63. <div>审批</div>
  64. </div>
  65. <div class="flex-1 fontColorB item" @click="amfTabLckick(11)">
  66. <div>{{ flow.done }}</div>
  67. <div>考核结束</div>
  68. </div>
  69. </div>
  70. <div class="main-main">
  71. <div class="main-title">
  72. 结果分析
  73. <el-tooltip effect="dark" placement="top-start">
  74. <div slot="content">
  75. <div style="margin-bottom: 10px;">“-”代表什么?</div>
  76. <div style="padding-left: 20px;">
  77. 1、员工的绩效考评未结束时,显示“-”
  78. <br />
  79. 2、绩效考评已结束,但员工的考核结果还没有公布时,也会显示“-
  80. </div>
  81. </div>
  82. <i class="el-icon-warning fontColorC"></i>
  83. </el-tooltip>
  84. </div>
  85. <div class="flex-box">
  86. <div class="flex-3" style="border-right: 1px solid #ebebeb;">
  87. <div class="main-title2">等级分布</div>
  88. <template v-if="headValue.length > 0">
  89. <div ref="echarts2" class="echarts"></div>
  90. </template>
  91. <NoData v-else></NoData>
  92. </div>
  93. <div class="flex-3" style="overflow-x:hidden; padding-left: 20px;">
  94. <el-table :data="tableData" :header-cell-style="{ background: '#ECF5FF' }">
  95. <el-table-column prop="name" label="绩效等级" align="center">
  96. <template slot="header" slot-scope="scope">
  97. <el-tooltip effect="dark" placement="top-start">
  98. <div slot="content">
  99. <div style="margin-bottom: 10px;">“-”代表什么?</div>
  100. <div style="padding-left: 20px;">
  101. 1、员工的绩效考评未结束时,显示“-”
  102. <br />
  103. 2、绩效考评已结束,但员工的考核结果还没有公布时,也会显示“-
  104. </div>
  105. </div>
  106. <span>
  107. <span>绩效等级</span>
  108. <i class="el-icon-warning fontColorC"></i>
  109. </span>
  110. </el-tooltip>
  111. </template>
  112. </el-table-column>
  113. <el-table-column prop="value" label="实际分布" align="center"></el-table-column>
  114. </el-table>
  115. </div>
  116. </div>
  117. </div>
  118. <div class="main-footer">
  119. <div class="main-title">绩效排名</div>
  120. <div class="flex-box-ce search">
  121. <div class="flex-box-ce">
  122. <div class="label">部门</div>
  123. <el-select v-model="deptName" clearable multiple placeholder="请选择部门" style="width: 300px;">
  124. <el-option v-for="(item, index) in deptList" :key="index" :label="item.label"
  125. :value="item.value"></el-option>
  126. </el-select>
  127. </div>
  128. <div class="flex-box-ce">
  129. <div class="label">等级</div>
  130. <el-select v-model="rank" clearable placeholder="请选择等级">
  131. <el-option v-for="(item, index) in tableData" :key="index" :label="item.name"
  132. :value="item.name"></el-option>
  133. </el-select>
  134. </div>
  135. </div>
  136. <div class="table-box" style="padding: 20px;">
  137. <el-table :data="getEmployees()" :header-cell-style="{ background: '#ECF5FF' }">
  138. <el-table-column type="index" label="序号" align="center"></el-table-column>
  139. <el-table-column prop="name" label="姓名" align="center"></el-table-column>
  140. <el-table-column prop="dept_name" label="部门" align="center">
  141. <template slot-scope="scope">
  142. <span v-if="scope.row.departments&&scope.row.departments.length>0">
  143. <span v-for="(item,key) in scope.row.departments">{{item.dept_name}},</span>
  144. </span>
  145. <span v-else>{{scope.row.dept_name}}</span>
  146. </template>
  147. </el-table-column>
  148. <el-table-column prop="final_point" label="考核结果" align="center"></el-table-column>
  149. <el-table-column prop="final_level" label="绩效等级" align="center">
  150. <template slot="header" slot-scope="scope">
  151. <el-tooltip effect="dark" placement="top-start">
  152. <div slot="content">
  153. <div style="margin-bottom: 10px;">“-”代表什么?</div>
  154. <div style="padding-left: 20px;">
  155. 1、员工的绩效考评未结束时,显示“-”
  156. <br />
  157. 2、绩效考评已结束,但员工的考核结果还没有公布时,也会显示“-
  158. </div>
  159. </div>
  160. <span>
  161. <span>绩效等级</span>
  162. <i class="el-icon-warning fontColorC"></i>
  163. </span>
  164. </el-tooltip>
  165. </template>
  166. </el-table-column>
  167. <el-table-column prop="rank" label="绩效排名" align="center"></el-table-column>
  168. <el-table-column label="操作" width="100" align="center">
  169. <template slot-scope="scope">
  170. <el-button type="text" @click="openDetail(scope.row)">查看详情</el-button>
  171. </template>
  172. </el-table-column>
  173. </el-table>
  174. </div>
  175. </div>
  176. </div>
  177. </template>
  178. <div class="flex-box-v flex-center-center" v-else
  179. style="padding: 26px 35px;background-color: #fff;border: 1px solid #e8e8e8;border-radius: 4px;height: 400px;">
  180. <img src="static/images/invite_new_company.png" style="width: 200px;height: 200px;margin-bottom: 10px;" />
  181. <div class="fontColorB">您没有参与任何考核</div>
  182. </div>
  183. </div>
  184. </template>
  185. <script>
  186. import ECharts from 'echarts';
  187. let that;
  188. export default {
  189. data() {
  190. return {
  191. tableDataLoad: false, //列表load
  192. options: [], //
  193. props: {
  194. lazy: true,
  195. label: 'name',
  196. value: 'id',
  197. children: 'list',
  198. lazyLoad (node, resolve) {
  199. const { level } = node;
  200. that.getAssessTree(node,resolve);
  201. // setTimeout(() => {
  202. // const nodes = Array.from({ length: level + 1 }).map(item => ({
  203. // value: ++id,
  204. // label: `选项${id}`,
  205. // leaf: level >= 2
  206. // }));
  207. // // 通过调用resolve将子节点数据返回,通知组件数据加载完成
  208. // resolve(nodes);
  209. // }, 1000);
  210. }
  211. },
  212. headValue: [],
  213. amhlList: [
  214. { value: 0, label: '全部' },
  215. { value: 2, label: '月度' },
  216. { value: 3, label: '季度' },
  217. { value: 4, label: '半年度' },
  218. { value: 5, label: '年度' },
  219. { value: 1, label: '日' },
  220. { value: 6, label: '自定义' }
  221. ],
  222. flow: {},
  223. tableData: [],
  224. package_id: '',
  225. employees: [],
  226. deptList: [],
  227. deptName: [], //部门名称
  228. rank: '' ,//级别
  229. cycle_type:0,
  230. };
  231. },
  232. components: {},
  233. watch: {
  234. deptName(val) {
  235. console.log(val)
  236. // this.getEmployees();
  237. },
  238. rank(val) {
  239. // this.getEmployees();
  240. },
  241. headValue(val) {
  242. this.tableData = [];
  243. this.deptList = [];
  244. this.cycle_type = val[0];
  245. this.package_id = val[val.length - 1];
  246. this.getData();
  247. }
  248. },
  249. created() {
  250. that=this;
  251. },
  252. mounted() {
  253. this.assessTree();
  254. },
  255. methods: {
  256. //点击表格某一行
  257. amfTabLckick(index) {
  258. let data={
  259. navcli:index,
  260. cycle_type:this.cycle_type,
  261. id:this.package_id,
  262. }
  263. this.$router.push({ path: '/assessDetails', query: { amfTabLckick: JSON.stringify(data) } });
  264. },
  265. getEmployees() {
  266. var employees = this.employees;
  267. var arr = [];
  268. if (this.deptName.length > 0) {
  269. employees.forEach(item => {
  270. if(item.departments){
  271. item.departments.some(dep=>{
  272. let is=false;
  273. this.deptName.some(name=>{
  274. if(dep.dept_name==name){
  275. arr.push(item)
  276. is=true
  277. return true
  278. }
  279. })
  280. if(is){
  281. return true
  282. }
  283. })
  284. }else if(item.dept_name==this.deptName){
  285. arr.push(item)
  286. }
  287. });
  288. } else {
  289. arr = employees;
  290. }
  291. if (this.rank) {
  292. arr = arr.filter(item => {
  293. return item.final_level == this.rank;
  294. });
  295. }
  296. return arr;
  297. },
  298. getData() {
  299. if(!this.package_id){
  300. return false;
  301. }
  302. this.tableDataLoad = true;
  303. this.$axiosUser('get', '/api/pro/per/statistics', { package_id: this.package_id })
  304. .then(res => {
  305. this.flow = res.data.data.flow;
  306. this.setTableData(res.data.data.employees);
  307. this.employees = res.data.data.employees;
  308. this.getEmployees();
  309. })
  310. .finally(() => {
  311. this.tableDataLoad = false;
  312. });
  313. },
  314. setTableData(employees) {
  315. var obj = {};
  316. let deptList = new Set();
  317. this.deptList = [];
  318. employees.forEach(item => {
  319. var count = 0;
  320. if(item.departments){
  321. item.departments.forEach(dep=>{
  322. deptList.add(dep.dept_name)
  323. })
  324. }else if(item.dept_name){
  325. deptList.add(item.dept_name)
  326. }
  327. employees.forEach(item2 => {
  328. if (item.final_level == item2.final_level) {
  329. count++;
  330. }
  331. });
  332. obj[item.final_level] = count;
  333. });
  334. let deptArr=[...deptList]
  335. this.getResult(Object.keys(obj), Object.values(obj));
  336. let tableData = [];
  337. for (let key in obj) {
  338. tableData.push({ name: key, value: obj[key] });
  339. }
  340. deptArr.forEach(item=>{
  341. this.deptList.push({ label: item, value:item });
  342. })
  343. this.tableData = tableData;
  344. },
  345. //结果分析
  346. getResult(keys, values) {
  347. let option = {
  348. tooltip: {
  349. trigger: 'axis',
  350. axisPointer: {
  351. // 坐标轴指示器,坐标轴触发有效
  352. type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
  353. }
  354. },
  355. xAxis: {
  356. type: 'category',
  357. data: keys
  358. },
  359. yAxis: {
  360. type: 'value',
  361. minInterval: 1
  362. },
  363. series: [
  364. {
  365. data: values,
  366. type: 'bar',
  367. itemStyle: {
  368. normal: {
  369. //这里是重点
  370. color: function(params) {
  371. //注意,如果颜色太少的话,后面颜色不会自动循环,最好多定义几个颜色
  372. var colorList = ['#409EFF', '#4ECB73', '#36CBCB', '#F2637B', '#FBD437', '#749f83', '#ca8622'];
  373. var index;
  374. //给大于颜色数量的柱体添加循环颜色的判断
  375. if (params.dataIndex >= colorList.length) {
  376. index = params.dataIndex - colorList.length;
  377. return colorList[index];
  378. }
  379. return colorList[params.dataIndex];
  380. }
  381. }
  382. },
  383. barMaxWidth: 30
  384. }
  385. ]
  386. };
  387. var myChart = ECharts.init(this.$refs.echarts2);
  388. option && myChart.setOption(option);
  389. },
  390. //考核人数
  391. getAssessNum() {
  392. let option = {
  393. tooltip: {
  394. trigger: 'axis',
  395. axisPointer: {
  396. // 坐标轴指示器,坐标轴触发有效
  397. type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
  398. }
  399. },
  400. xAxis: {
  401. type: 'category',
  402. data: ['Mon', 'Tue']
  403. },
  404. yAxis: {
  405. type: 'value'
  406. },
  407. series: [
  408. {
  409. data: [
  410. 120,
  411. {
  412. value: 200
  413. // itemStyle: {
  414. // color: '#409EFF'
  415. // }
  416. }
  417. ],
  418. type: 'bar',
  419. itemStyle: {
  420. normal: {
  421. //这里是重点
  422. color: function(params) {
  423. //注意,如果颜色太少的话,后面颜色不会自动循环,最好多定义几个颜色
  424. var colorList = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622'];
  425. var index;
  426. //给大于颜色数量的柱体添加循环颜色的判断
  427. if (params.dataIndex >= colorList.length) {
  428. index = params.dataIndex - colorList.length;
  429. return colorList[index];
  430. }
  431. return colorList[params.dataIndex];
  432. }
  433. }
  434. },
  435. barMaxWidth: 30
  436. }
  437. ]
  438. };
  439. var myChart = ECharts.init(this.$refs.echarts1);
  440. option && myChart.setOption(option);
  441. },
  442. getAssessTree(node,resolve){
  443. if(this.options.length==0){
  444. return
  445. }
  446. this.tableDataLoad = true;
  447. this.$axiosUser('get', 'api/pro/per/package/list', { page: 0, is_manage_scope: 1,cycle_type:node.value}).then(res => {
  448. let data = res.data.data.list;
  449. data.forEach(item=>{
  450. item.leaf=true;
  451. })
  452. resolve(data)
  453. }).finally(() => {
  454. this.tableDataLoad = false;
  455. });
  456. },
  457. //请求绩效树
  458. assessTree() {
  459. this.tableDataLoad = true;
  460. this.$axiosUser('get', 'api/pro/per/package/list', { page: 0, is_manage_scope: 1 }).then(res => {
  461. if (res.data.code == 1) {
  462. let data = res.data.data.list;
  463. data.forEach(item=>{
  464. item.leaf=true;
  465. })
  466. let arr = [
  467. { name: '月度', id: 2, list: [] },
  468. { name: '日', id: 1, list: [] },
  469. { name: '季度', id: 3, list: [] },
  470. { name: '半年度', id: 4, list: [] },
  471. { name: '年度', id: 5, list: [] },
  472. { name: '自定义', id: 6, list: [] }
  473. ];
  474. let arr2 = [
  475. { name: '月度', id: 2, list: [] },
  476. { name: '日', id: 1, list: [] },
  477. { name: '季度', id: 3, list: [] },
  478. { name: '半年度', id: 4, list: [] },
  479. { name: '年度', id: 5, list: [] },
  480. { name: '自定义', id: 6, list: [] }
  481. ];
  482. data.forEach(item => {
  483. arr.forEach(item2 => {
  484. if (item2.id == item.cycle_type) {
  485. item2.list.push(item);
  486. }
  487. });
  488. });
  489. let headValue = [];
  490. arr.some((item,index) => {
  491. if (item.list.length > 0) {
  492. arr2[index].list=item.list;
  493. headValue.push(item.id);
  494. headValue.push(item.list[0].id);
  495. return true;
  496. }
  497. });
  498. this.options=arr2;
  499. if (headValue.length > 0) {
  500. this.$nextTick(()=>{
  501. this.headValue = headValue;
  502. window.addEventListener('resize', this.selfAdaption);
  503. })
  504. }
  505. }
  506. }).finally(() => {
  507. this.tableDataLoad = false;
  508. });
  509. },
  510. //echarts自适应
  511. openDetail(item) {
  512. if (item.per_id) {
  513. this.$router.push({
  514. path: '/staffAssDet',
  515. query: { assID: this.package_id, employeeID: item.per_id, employeeIDs: item.id }
  516. });
  517. }
  518. },
  519. selfAdaption() {
  520. // var echarts1 = ECharts.init(this.$refs.echarts1);
  521. var echarts2 = ECharts.init(this.$refs.echarts2);
  522. // echarts1.resize();
  523. echarts2.resize();
  524. },
  525. handleCommand(val) {
  526. switch (val) {
  527. case 'A': //导出结果
  528. this.$axiosUser('get', '/api/pro/per/package/export/result', { package_id: this.package_id }).then(res => {
  529. this.$alert('系统正在处理中,处理完将在首页以“消息”形式通知您,请留意消息通知', '提示', {
  530. confirmButtonText: '我知道了',
  531. callback: action => {}
  532. });
  533. });
  534. break;
  535. case 'C': //导出明细
  536. this.$axiosUser('get', '/api/pro/per/package/export/detail', { package_id: this.package_id }).then(res => {
  537. this.$alert('系统正在处理中,处理完将在工作台以“消息”形式通知您,请留意消息通知', '提示', {
  538. confirmButtonText: '我知道了',
  539. callback: action => { }
  540. });
  541. });
  542. break;
  543. case 'g': //管理
  544. this.isManagement = true;
  545. break;
  546. }
  547. },
  548. filtrationStr(str) {
  549. let name = '';
  550. switch (str) {
  551. case 'all':
  552. name = '参与人数';
  553. break;
  554. case 'done':
  555. name = '流程完成节点人数';
  556. break;
  557. case 'review':
  558. name = '审批节点节点人数';
  559. break;
  560. case 'target':
  561. name = '目标制定节点人数';
  562. break;
  563. case 'confirm':
  564. name = '目标确认节点人数';
  565. break;
  566. case 'execution':
  567. name = '执行中节点人数';
  568. break;
  569. case 'score_self':
  570. name = '自评节点人数';
  571. break;
  572. case 'result_value':
  573. name = '结果值录入节点人数';
  574. break;
  575. case 'score_mutual':
  576. name = '互评节点人数';
  577. break;
  578. case 'special_scorer':
  579. name = '指定评分人节点人数';
  580. break;
  581. case 'score_supervisor':
  582. name = '上级评分节点人数';
  583. break;
  584. case 'score':
  585. name = '所有评分节点人数';
  586. break;
  587. }
  588. return name;
  589. }
  590. },
  591. beforeDestroy() {
  592. //离开路由
  593. window.removeEventListener('resize', this.selfAdaption); //取消echarts自适应
  594. }
  595. };
  596. </script>
  597. <style scoped="scoped" lang="scss">
  598. .all {
  599. background-color: #fff;
  600. padding: 20px;
  601. min-height: calc(100vh - 210px);
  602. }
  603. header {
  604. height: 42px;
  605. }
  606. .main {
  607. background-color: #fff;
  608. }
  609. .main-herader {
  610. margin: 10px 0;
  611. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  612. // box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.05);
  613. }
  614. .item {
  615. text-align: center;
  616. padding: 20px;
  617. position: relative;
  618. cursor: pointer;
  619. }
  620. .item div:nth-child(1) {
  621. font-size: 20px;
  622. font-weight: 600;
  623. margin-bottom: 10px;
  624. color: #409EFF;
  625. }
  626. .bian {
  627. position: absolute;
  628. width: 1px;
  629. height: 30px;
  630. background-color: #e8e8e8;
  631. right: 0;
  632. top: 50%;
  633. margin-top: -15px;
  634. }
  635. .echarts {
  636. height: 350px;
  637. width: 100%;
  638. }
  639. .main-title {
  640. font-weight: 600;
  641. // padding: 20px 0px;
  642. font-size: 16px;
  643. position: relative;
  644. margin: 20px 0 20px 20px;
  645. &::after {
  646. content: "";
  647. position: absolute;
  648. width: 4px;
  649. height: 18px;
  650. background-color: #409EFF;
  651. left: -10px;
  652. top: 2px;
  653. }
  654. }
  655. .main-title2 {
  656. padding-left: 40px;
  657. font-size: 14px;
  658. font-weight: 600;
  659. }
  660. .departments {
  661. width: 46%;
  662. margin-bottom: 10px;
  663. }
  664. .departments span {
  665. background-color: #e6a23c;
  666. color: #fff;
  667. width: 18px;
  668. height: 18px;
  669. font-size: 12px;
  670. text-align: center;
  671. line-height: 18px;
  672. display: inline-block;
  673. }
  674. .department-name {
  675. padding: 0 10px;
  676. }
  677. .label {
  678. width: 50px;
  679. text-align: right;
  680. margin-right: 10px;
  681. }
  682. .search {
  683. padding: 20px;
  684. padding-bottom: 0;
  685. }
  686. </style>