RankEntire.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. <template>
  2. <div style=" background-color: #fff; padding:15px;" @click="show3=false;show4=false">
  3. <div style="margin-top:2px;" class="inline-block-btn-list">
  4. <el-date-picker
  5. class="first-element-btn"
  6. v-model="filter.time_range"
  7. type="daterange"
  8. value-format="timestamp"
  9. range-separator="至"
  10. start-placeholder="开始日期"
  11. end-placeholder="结束日期"
  12. :picker-options="instantPickerOptions"
  13. @change="onFilterChanged"
  14. ></el-date-picker>
  15. <el-cascader
  16. v-if="false"
  17. v-show="profile.manager_type > 0"
  18. clearable
  19. :options="deptTree"
  20. change-on-select
  21. v-model="filter.dept_id"
  22. @change="onFilterChanged"
  23. placeholder="选择部门进行筛选"
  24. class="gap-right-8"
  25. style="width:160px;"
  26. ></el-cascader>
  27. <!-- 部门 tree -->
  28. <div style="position:relative; display:inline-block;" @click.stop="show3=!show3">
  29. <el-button style="color:rgb(192 196 204); padding-right:25px;">
  30. <span
  31. style="max-width: 150px; display: inline-block; overflow: hidden;"
  32. :class="{'treeActice':textTip != '选择部门进行筛选'}"
  33. >{{textTip}}</span>
  34. <span class="el-input__suffix">
  35. <span class="el-input__suffix-inner">
  36. <i :class="{'arrRotation': show3}" class="el-input__icon el-icon-arrow-down"></i>
  37. </span>
  38. </span>
  39. </el-button>
  40. <el-collapse-transition>
  41. <div
  42. v-show="show3"
  43. style="margin-top:10px;
  44. padding:6px 0;
  45. max-height:250px;
  46. min-width: 180px;
  47. overflow: auto;
  48. position:absolute;
  49. z-index:99;
  50. border: 1px solid #e4e7ed;
  51. border-radius: 2px;
  52. background:#fff;
  53. box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);"
  54. >
  55. <el-tree
  56. node-key="value"
  57. ref="tree"
  58. :data="deptTree"
  59. :accordion="true"
  60. :highlight-current="true"
  61. :props="defaultProps"
  62. show-checkbox
  63. @check="handleCheck"
  64. ></el-tree>
  65. </div>
  66. </el-collapse-transition>
  67. </div>
  68. <el-button
  69. @click="filtersAdvShow = true"
  70. v-show="filtersAdvShow==false"
  71. type="primary"
  72. plain
  73. >高级</el-button>
  74. <el-button @click="filtersAdvShow = false" v-show="filtersAdvShow" type="primary" plain>取消</el-button>
  75. <el-select
  76. style="width:70px;"
  77. v-show="settings.point_set.indexOf('a') >= 0"
  78. v-model="filter.point_type"
  79. @change="onFilterChanged"
  80. >
  81. <el-option
  82. v-for="item in pointTypes"
  83. :key="item.value"
  84. :label="item.label"
  85. :value="item.value"
  86. ></el-option>
  87. </el-select>
  88. </div>
  89. <div v-show="filtersAdvShow" class="inline-block-btn-list inline-block-btn-list-adv">
  90. <!-- 初始分 -->
  91. <el-checkbox
  92. class="first-element-btn"
  93. style="padding:9px 20px;"
  94. border
  95. v-model="filter.include_init_point"
  96. @change="onFilterChanged"
  97. >包含初始积分</el-checkbox>
  98. <!-- 工龄分 -->
  99. <el-checkbox
  100. class="first-element-btn"
  101. style="padding:9px 20px;"
  102. border
  103. v-model="filter.include_seniority_point"
  104. @change="onFilterChanged"
  105. >包含工龄分</el-checkbox>
  106. <el-checkbox
  107. class="first-element-btn"
  108. style="padding:9px 20px;"
  109. border
  110. v-model="filter.include_manager"
  111. @change="updateEmployeeRange"
  112. >包含管理者进行排名</el-checkbox>
  113. <el-cascader
  114. clearable
  115. v-show="false"
  116. :options="categoryTree"
  117. change-on-select
  118. v-model="filter.category_id"
  119. @change="onFilterChanged"
  120. placeholder="选择积分分类进行筛选"
  121. class="gap-right-8"
  122. ></el-cascader>
  123. <!-- <el-collapse-transition> -->
  124. <div style="position:relative; display:inline-block;" @click.stop="show4=true">
  125. <el-button @click="show4 = !show4" style="color:rgb(192 196 204); padding-right:25px;">
  126. <span
  127. style="max-width: 150px; display: inline-block; overflow: hidden;"
  128. :class="{'treeActice':textTip1 != '选择分类进行筛选'}"
  129. >{{textTip1}}</span>
  130. <span class="el-input__suffix">
  131. <span class="el-input__suffix-inner">
  132. <i :class="{'arrRotation': show4}" class="el-input__icon el-icon-arrow-down"></i>
  133. </span>
  134. </span>
  135. </el-button>
  136. <el-collapse-transition>
  137. <div
  138. v-show="show4"
  139. style="margin-top:10px;
  140. padding:6px 0;
  141. max-height:250px;
  142. min-width: 180px;
  143. overflow: auto;
  144. position:absolute;
  145. z-index:99;
  146. border: 1px solid #e4e7ed;
  147. border-radius: 2px;
  148. box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);"
  149. >
  150. <el-tree
  151. node-key="value"
  152. ref="tree4"
  153. :data="categoryTree"
  154. :accordion="true"
  155. :highlight-current="true"
  156. :props="defaultProps"
  157. show-checkbox
  158. @check="handleCheck4"
  159. ></el-tree>
  160. </div>
  161. </el-collapse-transition>
  162. </div>
  163. <!-- </el-collapse-transition> -->
  164. <!-- <el-select
  165. v-show="true"
  166. v-model="filter.point_type"
  167. @change="onFilterChanged">
  168. <el-option
  169. v-for="item in pointTypes"
  170. :key="item.value"
  171. :label="item.label"
  172. :value="item.value">
  173. </el-option>
  174. </el-select>-->
  175. <el-select
  176. v-show="false"
  177. v-model="filter.target_id_set"
  178. filterable
  179. multiple
  180. :multiple-limit="15"
  181. placeholder="选择员工进行筛选(最多15个)"
  182. @change="onFilterChanged"
  183. style="width:200px;"
  184. >
  185. <el-option
  186. v-for="item in employeeOptions"
  187. :key="item.id"
  188. :label="item.name"
  189. :value="item.id"
  190. ></el-option>
  191. </el-select>
  192. <el-button type="primary" plain @click="get_excel_token">导出Excel</el-button>
  193. </div>
  194. <el-table
  195. v-loading="loading"
  196. ref="rankTable"
  197. :data="rankList"
  198. tooltip-effect="dark"
  199. style="width: 100%"
  200. :row-class-name="tableRowClassName"
  201. :header-cell-style="{ textAlign : 'left' }"
  202. >
  203. <el-table-column label="姓名">
  204. <template slot-scope="scope">
  205. <div class="rank_head">
  206. <img
  207. v-if="scope.row.target_img != null"
  208. v-bind:src="scope.row.target_img"
  209. width="42px"
  210. height="42px"
  211. >
  212. <img
  213. v-if="scope.row.target_img == null"
  214. src="static/images/head_default.png"
  215. width="42px"
  216. height="42px"
  217. >
  218. {{scope.row.target_name}}
  219. </div>
  220. </template>
  221. </el-table-column>
  222. <el-table-column label="部门" prop="dept_name" class-name="text-auxiliary"></el-table-column>
  223. <el-table-column label="排行">
  224. <template slot-scope="scope">
  225. <div
  226. style="float:left"
  227. class="table-font-number"
  228. v-if="(currentPage - 1) * 10 + scope.row.index <= 3"
  229. >
  230. <img src="static/images/rank_01.png" width="20" v-if="scope.row.index == 1">
  231. <img src="static/images/rank_02.png" width="20" v-if="scope.row.index == 2">
  232. <img src="static/images/rank_03.png" width="20" v-if="scope.row.index == 3">
  233. </div>
  234. <div
  235. style="float:left"
  236. class="table-font-number"
  237. v-if="(currentPage - 1) * 10 + scope.row.index > 3"
  238. >NO.{{ (currentPage - 1) * 10 + scope.row.index }}</div>
  239. <div style="float:left" v-if="scope.row.status > 0">
  240. <img
  241. style="width:9px; height:17px; margin:3px 5px 0 8px;"
  242. src="static/images/up_small.png"
  243. alt
  244. >
  245. <span
  246. style="display:block; float:right; color:rgb(245,108,108)"
  247. >{{scope.row.rank_change}}</span>
  248. </div>
  249. <div style="float:left" v-if="scope.row.status < 0">
  250. <img
  251. style="width:9px; height:17px; margin:3px 5px 0 8px;"
  252. src="static/images/down_small.png"
  253. alt
  254. >
  255. <span
  256. style="display:block; float:right; color:rgb(103,194,58)"
  257. >{{scope.row.rank_change}}</span>
  258. </div>
  259. <!-- <div style="float:left; color:rgb(204,204,204);" v-if="scope.row.status === 0"><img style="width:9px; height:3px; margin:0 5px 3px 8px;" src="static/images/equal.png" alt=""></div> -->
  260. </template>
  261. </el-table-column>
  262. <el-table-column label="总分">
  263. <template slot-scope="scope">
  264. <div
  265. style="color:#409EFF; font-size: 18px;"
  266. v-bind:class="'table-font-xl table-font-number ' + (scope.row.p_sum < 0 ? 'point-negative' : 'point-positive')"
  267. >{{ scope.row.p_sum }}</div>
  268. </template>
  269. </el-table-column>
  270. </el-table>
  271. <el-pagination
  272. background
  273. layout="prev, pager, next"
  274. :page-size="10"
  275. :page-sizes="[10, 20, 50, 100]"
  276. :total="totalCount"
  277. :current-page.sync="currentPage"
  278. @current-change="changePage"
  279. ></el-pagination>
  280. </div>
  281. </template>
  282. <script>
  283. import qs from 'qs'
  284. export default {
  285. data() {
  286. return {
  287. initPointTypes: [{
  288. label: '不包含初始分', value: 0
  289. }, {
  290. label: '包含初始分', value: 1
  291. }],
  292. seniorityPointTypes: [{
  293. label: '不包含工龄分', value: 0
  294. }, {
  295. label: '包含工龄分', value: 1
  296. }],
  297. show3: false,
  298. show4: false,
  299. textTip: '选择部门进行筛选',
  300. textTip1: '选择分类进行筛选',
  301. settings: this.$store.getters.system_setting,
  302. defaultProps: {
  303. children: 'children',
  304. label: 'label'
  305. },
  306. profile: this.$store.getters.user_info,
  307. filtersAdvShow: false,
  308. filter: {
  309. keywords: '',
  310. target_id_set: [],
  311. category_id: [],
  312. dept_id: [],
  313. point_type: 0,
  314. time_range: '',
  315. include_manager: false,
  316. include_init_point: false,
  317. include_seniority_point: false
  318. },
  319. totalCount: 0,
  320. currentPage: 1,
  321. rankList: null,
  322. categoryTree: [],
  323. deptTree: [],
  324. employeeOptions: [],
  325. pointTypes: [],
  326. loading: false,
  327. instantPickerOptions: {
  328. shortcuts: [
  329. {
  330. text: '本周',
  331. onClick(picker) {
  332. const now = new Date(new Date().toLocaleDateString())
  333. const start =
  334. now.getTime() - (now.getDay() - 1) * 24 * 60 * 60 * 1000
  335. const end = start + 7 * 24 * 60 * 60 * 1000 - 1000
  336. picker.$emit('pick', [start, end])
  337. }
  338. },
  339. {
  340. text: '上周',
  341. onClick(picker) {
  342. const now = new Date(new Date().toLocaleDateString())
  343. const start =
  344. now.getTime() - (now.getDay() + 6) * 24 * 60 * 60 * 1000
  345. const end = start + 7 * 24 * 60 * 60 * 1000 - 1000
  346. picker.$emit('pick', [start, end])
  347. }
  348. },
  349. {
  350. text: '本月',
  351. onClick(picker) {
  352. const now = new Date()
  353. const startDate = new Date(now.getFullYear(), now.getMonth(), 1)
  354. const endDate = new Date(now.getFullYear(), now.getMonth() + 1, 0)
  355. picker.$emit('pick', [startDate.getTime(), endDate.getTime()])
  356. }
  357. },
  358. {
  359. text: '上月',
  360. onClick(picker) {
  361. const now = new Date()
  362. const startDate = new Date(
  363. now.getFullYear() - (now.getMonth() > 0 ? 0 : 1),
  364. (now.getMonth() + 11) % 12,
  365. 1
  366. )
  367. const endDate = new Date(now.getFullYear(), now.getMonth(), 0)
  368. picker.$emit('pick', [startDate.getTime(), endDate.getTime()])
  369. }
  370. },
  371. {
  372. text: '本年',
  373. onClick(picker) {
  374. const now = new Date()
  375. const startDate = new Date(now.getFullYear(), 0, 1)
  376. const endDate = new Date(now.getFullYear() + 1, 0, 0)
  377. picker.$emit('pick', [startDate.getTime(), endDate.getTime()])
  378. }
  379. }
  380. ]
  381. }
  382. }
  383. },
  384. methods: {
  385. handleCheck(target, param) {
  386. this.textTip = ''
  387. const textTip = []
  388. this.$refs.tree.getCheckedNodes().forEach(item => {
  389. textTip.push(item.label)
  390. })
  391. this.textTip = textTip.join('、')
  392. if (param.checkedKeys.length == 0) {
  393. this.textTip = '选择部门进行筛选'
  394. }
  395. this.filter.dept_id = param.checkedKeys
  396. this.onFilterChanged()
  397. // this.show3 = false;
  398. },
  399. handleCheck4(target, param) {
  400. this.textTip1 = ''
  401. const textTip1 = []
  402. this.$refs.tree4.getCheckedNodes().forEach(item => {
  403. textTip1.push(item.label)
  404. })
  405. this.textTip1 = textTip1.join('、')
  406. if (param.checkedKeys.length == 0) {
  407. this.textTip1 = '选择分类进行筛选'
  408. }
  409. this.filter.category_id = param.checkedKeys
  410. this.onFilterChanged()
  411. // this.show4 = false;
  412. },
  413. onFilterChanged: function() {
  414. this.currentPage = 1
  415. this.loadRankList()
  416. },
  417. changePage: function(current) {
  418. if (isNaN(current) || current < 1) {
  419. return false
  420. }
  421. this.loadRankList()
  422. },
  423. updateEmployeeRange: function(currentVal) {
  424. if (currentVal) {
  425. this.loadTargetList()
  426. this.onFilterChanged()
  427. } else {
  428. var self = this
  429. this.loadTargetList(() => {
  430. const idArr = self.employeeOptions.map(item => {
  431. return item.id
  432. })
  433. self.filter.target_id_set = self.filter.target_id_set.filter(
  434. item => idArr.indexOf(item) > -1
  435. )
  436. self.onFilterChanged()
  437. })
  438. }
  439. },
  440. // 获取控件所需数据
  441. loadWidgetData: function() {
  442. var self = this
  443. this.$http('get','/integral.php/ajax_request_common/prepare_integral_options',{
  444. id: this.profile.id,
  445. category_tree: 1,
  446. dept_tree: 1
  447. })
  448. .then(function(response) {
  449. if (response.status == 200) {
  450. var jsonData = response.data
  451. try {
  452. self.categoryTree = jsonData.category_tree
  453. self.deptTree = jsonData.dept_tree
  454. } catch (err) {
  455. console.log(err)
  456. }
  457. }
  458. })
  459. .catch(function(error) {
  460. console.log(error)
  461. })
  462. },
  463. // 获取可排名的员工列表
  464. loadTargetList: function(callback) {
  465. var self = this
  466. this.$http('get','/integral.php/point_data/load_target_list',{
  467. site_id: this.profile.site_id,
  468. exclude_manager: this.filter.include_manager ? 0 : 1
  469. })
  470. .then(function(response) {
  471. if (response.status == 200) {
  472. var jsonData = response.data
  473. try {
  474. self.employeeOptions = jsonData
  475. if (callback) {
  476. callback.call()
  477. }
  478. } catch (err) {
  479. console.log(err)
  480. }
  481. }
  482. })
  483. .catch(function(error) {
  484. console.log(error)
  485. })
  486. },
  487. get_excel_token: function() {
  488. var self = this
  489. const params = {
  490. site_id: this.profile.site_id,
  491. }
  492. for (const item in this.filter) {
  493. const value = this.filter[item]
  494. if (item == 'target_id_set') {
  495. if (!value || value.length == 0) {
  496. continue
  497. }
  498. params[item] = value.join(',')
  499. } else if (item == 'category_id' || item == 'dept_id') {
  500. if (!value || value.length == 0) {
  501. continue
  502. }
  503. params[item] = value
  504. // params[item] = value[value.length - 1];
  505. } else if (item == 'time_range') {
  506. if (!value || value.length < 2) {
  507. continue
  508. }
  509. const timeRangeArr = value.map(x => parseInt(x / 1000))
  510. timeRangeArr[1] += 60 * 60 * 24 - 1
  511. params[item] = timeRangeArr.join(',')
  512. } else if (item == 'include_manager') {
  513. params['exclude_manager'] = value ? 0 : 1
  514. } else if (item == 'include_init_point') {
  515. params['include_init_point'] = value ? 1 : 0
  516. } else if (item == 'include_seniority_point') {
  517. params['include_seniority_point'] = value ? 1 : 0
  518. } else {
  519. params[item] = value
  520. }
  521. }
  522. this.$http('get','/integral.php/token_create/generateEffective')
  523. .then(function(response) {
  524. if (response.status == 200) {
  525. var jsonData = response.data
  526. params['token'] = jsonData.data.token
  527. params['employee_id'] = jsonData.data.employee_id
  528. let url = self.serverdomain+'/integral.php/integral_excel/rank_by_employee?'+qs.stringify(params)
  529. window.open(url, '_blank')
  530. }
  531. })
  532. .catch(function(error) {
  533. console.log(error)
  534. })
  535. },
  536. loadPointType: function() {
  537. var self = this
  538. this.$http('get','/integral.php/ajax_request_common/get_point_types')
  539. .then(function(response) {
  540. if (response.status == 200) {
  541. const jsonData = response.data
  542. try {
  543. self.pointTypes = jsonData
  544. } catch (err) {
  545. console.log(err)
  546. }
  547. }
  548. })
  549. .catch(function(error) {
  550. console.log(error)
  551. })
  552. },
  553. loadRankList: function() {
  554. const params = {
  555. site_id: this.profile.site_id,
  556. page: this.currentPage
  557. }
  558. for (const item in this.filter) {
  559. const value = this.filter[item]
  560. if (item == 'target_id_set') {
  561. if (!value || value.length == 0) {
  562. continue
  563. }
  564. params[item] = value.join(',')
  565. } else if (item == 'category_id' || item == 'dept_id') {
  566. if (!value || value.length == 0) {
  567. continue
  568. }
  569. params[item] = value
  570. // params[item] = value[value.length - 1];
  571. } else if (item == 'time_range') {
  572. if (!value || value.length < 2) {
  573. continue
  574. }
  575. const timeRangeArr = value.map(x => parseInt(x / 1000))
  576. timeRangeArr[1] += 60 * 60 * 24 - 1
  577. params[item] = timeRangeArr.join(',')
  578. } else if (item == 'include_manager') {
  579. params['exclude_manager'] = value ? 0 : 1
  580. } else if (item == 'include_init_point') {
  581. params['include_init_point'] = value ? 1 : 0
  582. } else if (item == 'include_seniority_point') {
  583. params['include_seniority_point'] = value ? 1 : 0
  584. } else {
  585. params[item] = value
  586. }
  587. }
  588. var self = this
  589. self.loading = true
  590. this.$http('get','/integral.php/point_data/rank_by_employee',params)
  591. .then(function(response) {
  592. if (response.status == 200) {
  593. self.loading = false
  594. const jsonData = response.data
  595. try {
  596. self.totalCount = jsonData.total_count
  597. self.rankList = jsonData.list_data
  598. } catch (err) {
  599. console.log(err)
  600. }
  601. }
  602. })
  603. .catch(function(error) {
  604. console.log(error)
  605. })
  606. },
  607. tableRowClassName({ row, rowIndex }) {
  608. if (row.target_id == this.$store.getters.user_info.id) {
  609. return 'rank-mine'
  610. }
  611. return ''
  612. }
  613. },
  614. created() {
  615. this.loadWidgetData()
  616. this.loadTargetList()
  617. this.loadPointType()
  618. this.loadRankList()
  619. this.$nextTick(() => { // 点击body关闭筛选组件
  620. document.querySelector('#app').addEventListener('click', e => {
  621. if (this.show3) {
  622. this.show3 = false
  623. }
  624. })
  625. })
  626. }
  627. }
  628. </script>
  629. <style>
  630. tbody .image-container div.cell {
  631. height: 42px;
  632. }
  633. .el-pagination {
  634. text-align: center;
  635. margin-top: 15px;
  636. }
  637. .el-tabs__header {
  638. margin: 0;
  639. }
  640. .rank_head img {
  641. width: 30px;
  642. height: 30px;
  643. -webkit-border-radius: 20px;
  644. -moz-border-radius: 20px;
  645. border-radius: 20px;
  646. margin-right: 5px;
  647. }
  648. .rank_head * {
  649. vertical-align: middle;
  650. }
  651. .arrRotation {
  652. transform: rotate(180deg);
  653. }
  654. .treeActice {
  655. color: #606266;
  656. }
  657. .el-tree-node__content {
  658. height: 34px;
  659. }
  660. </style>