integral_rank.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. <template>
  2. <div>
  3. <!-- B分排名 -->
  4. <van-nav-bar title="阶段排名(月、季度、年)" left-text="返回" @click-left="$route_back" left-arrow></van-nav-bar>
  5. <van-dropdown-menu>
  6. <van-dropdown-item :title="dateDropdownItemTitle" ref="dateDropdownItem">
  7. <div class="date-type__wrap">
  8. <div class="date-type__label" v-for="o in dateOption" :key="o.value" :class="{ active: dateType === o.value }" @click.stop="dateType = o.value">
  9. <span>{{ o.name }}</span>
  10. </div>
  11. </div>
  12. <van-picker v-if="dateType === 0" :columns="yearOption" @change="onYearChange" :default-index="10" item-height="40" />
  13. <van-datetime-picker v-show="dateType !== 0" v-model="date" type="year-month" :show-toolbar="false" item-height="40" :formatter="formatter" :filter="dateFilter" />
  14. <div class="date-picker__toolbar">
  15. <van-button class="reset-button button-border-none" block square color="rgba(38,162,255,0.2)" @click="onResetDate">重置</van-button>
  16. <van-button class="button-border-none" block square type="info" @click="onConfirmDate">确认</van-button>
  17. </div>
  18. </van-dropdown-item>
  19. <van-dropdown-item :title="deptDropdownItemTitle" ref="deptDropdownItem">
  20. <template slot="title">
  21. <Wxopendata type="departmentName" :openid="deptDropdownItemTitle"></Wxopendata>
  22. </template>
  23. <DeptSelectorDropdown @onConfirm="onConfirmDept" />
  24. </van-dropdown-item>
  25. <van-dropdown-item title="规则分类" ref="ruleDropdownItem"><RuleCategorySelDropdown @onConfirm="onConfirmRule" @onCancel="rule = null" /></van-dropdown-item>
  26. <van-dropdown-item :title="filterItemsTitle" ref="filterItems">
  27. <div class="filterItemsDiv">
  28. <div class="filterItemsTitle">人员</div>
  29. <div class="filterItemsContent">
  30. <div class="sub__item" v-for="(item, k) in position_arr" :key="k" @click="onselect(item, position_arr)" :class="{ selected: item.selected }">
  31. <span>{{ item.title }}</span>
  32. </div>
  33. </div>
  34. <div class="filterItemsTitle">排序</div>
  35. <div class="filterItemsContent">
  36. <div class="sub__item" v-for="(item, k) in sort_arr" :key="k" @click="onselect(item, sort_arr)" :class="{ selected: item.selected }">
  37. <span>{{ item.title }}</span>
  38. </div>
  39. </div>
  40. <div class="filterItemsTitle">积分类型</div>
  41. <div class="filterItemsContent">
  42. <div class="sub__item" v-for="(item, k) in types" :key="k" @click="onselect(item, types)" :class="{ selected: item.selected }">
  43. <span>{{ item.title }}</span>
  44. </div>
  45. </div>
  46. </div>
  47. <div class="date-picker__toolbar">
  48. <van-button class="reset-button button-border-none" block square color="rgba(38,162,255,0.2)" @click="onResetFilter">重置</van-button>
  49. <van-button class="button-border-none" block square type="info" @click="onConfirmFilter">确认</van-button>
  50. </div>
  51. </van-dropdown-item>
  52. </van-dropdown-menu>
  53. <div class="body_com has_header">
  54. <scroller ref="scroller" :on-refresh="onRefresh" :on-infinite="onInfinite">
  55. <van-cell v-for="(item, index) in data" :key="index" @click="openDetail(item)">
  56. <div slot="icon" class="rank-item__icon-wrap">
  57. <icon name="rank-first" v-if="item.rank == 1" class="rank-item__icon"></icon>
  58. <icon name="rank-second" v-if="item.rank == 2" class="rank-item__icon"></icon>
  59. <icon name="rank-third" v-if="item.rank == 3" class="rank-item__icon"></icon>
  60. <span v-if="item.rank > 3" class="rank-item__icon-label">{{ item.rank }}</span>
  61. </div>
  62. <div class="rank-item__name-wrap" slot="title">
  63. <userImage :img_url="item.employee_img_url" :user_name="item.employee_name" width="0.72rem" height="0.72rem" fontSize="0.2" style="margin-right: 0.1rem"></userImage>
  64. <span class="rank-item__name"><Wxopendata type="userName" :openid="item.employee_name"></Wxopendata></span>
  65. </div>
  66. <div class="rank-item__value" slot="right-icon">
  67. <span :class="{ negative: item.point < 0 }">{{ item.point }}</span>
  68. </div>
  69. </van-cell>
  70. <van-empty v-if="!hasData" description="暂没有排名数据..." />
  71. </scroller>
  72. </div>
  73. </div>
  74. </template>
  75. <script>
  76. import request from '@/utils/request'
  77. import DeptSelectorDropdown from '@/components/common/DeptSelectorDropdown' // 部门
  78. import RuleCategorySelDropdown from '@/components/common/RuleCategorySelDropdown' // 规则分类
  79. import userImage from '@/components/common/user_image'
  80. import Vue from 'vue'
  81. import { Empty, Picker, DropdownMenu, DropdownItem, DatetimePicker } from 'vant'
  82. import moment from 'moment'
  83. Vue.use(Empty).use(Picker).use(DropdownMenu).use(DropdownItem).use(DatetimePicker)
  84. export default {
  85. name: 'integral_rank',
  86. data () {
  87. const { month } = this.$route.query
  88. const dateNow = month ? moment(month).toDate() : new Date()
  89. const currYear = dateNow.getFullYear()
  90. return {
  91. page: 1,
  92. page_size: '20',
  93. sort: true,
  94. data: null,
  95. date: dateNow,
  96. dateType: 1,
  97. dateOption: [{ name: '年', value: 0 }, { name: '季度', value: 2 }, { name: '月', value: 1 }],
  98. departId: 0,
  99. year: currYear,
  100. loading: true,
  101. dateDropdownItemTitle: null,
  102. deptDropdownItemTitle: '部门',
  103. filterItemsTitle: '筛选',
  104. yearOption: this.getYearOption(currYear),
  105. mapDateDropdownItemTitle: { 0: '年', 1: '月', 2: '季度' },
  106. mapRankIconName: { 0: 'rank-first', 1: 'rank-second', 2: 'rank-third' },
  107. ruleId: null,
  108. position_arr: [{ title: '全部', code: 'all', selected: true }, { title: '管理者', code: 'manager', selected: false }, { title: '员工', code: 'employee', selected: false }],
  109. sort_arr: [{ title: '从高到低', code: 'DESC', selected: true }, { title: '从低到高', code: 'ASC', selected: false }],
  110. types: [{ title: 'B分', code: '3', selected: true }, { title: 'A分', code: '2', selected: false }],
  111. }
  112. },
  113. components: { DeptSelectorDropdown, userImage, RuleCategorySelDropdown },
  114. computed: {
  115. hasData () {
  116. // return this.loading || (Array.isArray(this.data) && this.data.length > 0)
  117. return (Array.isArray(this.data) && this.data.length > 0)
  118. }
  119. },
  120. watch: {
  121. dateType(val){
  122. let newData = new Date()
  123. if(val == 2){
  124. this.date = new Date(newData.getFullYear() + '-' + '01')
  125. }else if(val == 1){
  126. this.date = newData
  127. }
  128. },
  129. },
  130. methods: {
  131. // 打开明细列表
  132. openDetail (item) {
  133. console.log(item);
  134. var params = this.getParams()
  135. item.year = params.year// 年
  136. item.rule_id = params.rule// 分类
  137. item.quarter = params.quarter// 季度
  138. item.month = params.month
  139. item.pt_id=params.pt_id
  140. this.$router.push({ name: 'integralDetail', query: {item: JSON.stringify(item)}})
  141. },
  142. onConfirmRule (ruleIds) {
  143. ruleIds ? (this.ruleId = ruleIds) : (this.ruleId = null)
  144. this.$refs.ruleDropdownItem.toggle()
  145. this.$refs.scroller.triggerPullToRefresh()
  146. },
  147. sortNumber () {
  148. this.sort = !this.sort
  149. this.data.reverse()
  150. },
  151. getIconText (employeeName) {
  152. if (employeeName && employeeName.length > 2) {
  153. const len = employeeName.length
  154. return employeeName[len - 2] + employeeName[len - 1]
  155. }
  156. return employeeName
  157. },
  158. getYearOption (currYear) {
  159. const yearOption = []
  160. for (let i = currYear - 10; i <= currYear + 10; i++) {
  161. yearOption.push(i)
  162. }
  163. return yearOption
  164. },
  165. dateFilter (type, options) {
  166. if (type === 'month' && this.dateType === 2) {
  167. return options.filter(option => option < 5)
  168. }
  169. return options
  170. },
  171. formatter (type, val) {
  172. if (type === 'year') {
  173. return `${val}年`
  174. }
  175. return this.dateType === 2 ? `${val[1]}季度` : `${val}月`
  176. },
  177. getDateDropdownItemTitle () {
  178. if (this.dateType === 0) {
  179. return `${this.year}年`
  180. }
  181. return `${this.dateType === 1 ? this.date.getMonth() + 1 : this.date.getMonth() > 3 ? 1 : this.date.getMonth() + 1}${this.dateType === 1 ? '月' : '季度'}`
  182. },
  183. onYearChange (item, value) {
  184. this.year = value
  185. },
  186. onResetDate () {
  187. this.date = new Date()
  188. this.dateType = 1
  189. },
  190. onConfirmDate () {
  191. this.dateDropdownItemTitle = this.getDateDropdownItemTitle()
  192. this.$refs.dateDropdownItem.toggle()
  193. this.$refs.scroller.triggerPullToRefresh()
  194. },
  195. // 筛选项
  196. onResetFilter () {
  197. this.position_arr.forEach(element => {
  198. element.code == 'all' ? (element.selected = true) : (element.selected = false)
  199. })
  200. this.sort_arr.forEach(element => {
  201. element.selected = !element.selected
  202. })
  203. },
  204. onConfirmFilter () {
  205. this.$refs.filterItems.toggle()
  206. this.$refs.scroller.triggerPullToRefresh()
  207. },
  208. // 排序
  209. onselect (item, parent) {
  210. parent.forEach(element => {
  211. element.code == item.code ? (element.selected = true) : (element.selected = false)
  212. })
  213. },
  214. onConfirmDept (deptItem) {
  215. if (deptItem) {
  216. this.departId = deptItem.id
  217. this.deptDropdownItemTitle = deptItem.name
  218. } else {
  219. this.departId = 0
  220. this.deptDropdownItemTitle = '部门'
  221. }
  222. this.$refs.deptDropdownItem.toggle()
  223. this.$refs.scroller.triggerPullToRefresh()
  224. },
  225. getParams () {
  226. const pointTypeId = this.$store.getters.point_types.find(o => o.code === 'BF').id
  227. const params = { dept_id: this.departId, pt_id: pointTypeId, page: this.page, page_size: this.page_size }
  228. if (this.dateType === 0) {
  229. params.year = this.year
  230. } else if (this.dateType === 1) {
  231. params.month = moment(this.date).format('YYYY-MM')
  232. } else {
  233. params.quarter = this.date.getMonth() > 3 ? moment(this.date).format('YYYY') + '1' : moment(this.date).format('YYYYM')
  234. }
  235. this.ruleId ? (params.rule = this.ruleId) : (this.ruleId = null)
  236. this.position_arr.forEach(element => {
  237. if (element.selected) {
  238. params.position = element.code
  239. }
  240. })
  241. this.sort_arr.forEach(element => {
  242. if (element.selected) {
  243. params.sort = element.code
  244. }
  245. })
  246. this.types.forEach(element => {
  247. if (element.selected) {
  248. params.pt_id = element.code
  249. }
  250. })
  251. return params
  252. },
  253. // 加载
  254. showLoading () {
  255. this.$toast.loading({
  256. loadingType: 'spinner',
  257. message: '正在处理'
  258. })
  259. },
  260. get_list (done) {
  261. let self = this
  262. self.showLoading()
  263. const params = this.getParams()
  264. request('get', '/api/integral/statistics/ranking', params, 'v2').then(res => {
  265. done()
  266. if (res.data.code == 1) {
  267. self.$refs.scroller.finishInfinite(res.data.data.list.length != 20) // 停止上滚加载下一页,由于服务器端没有统一空数据和正常数据的格式,所以通过total字段来判定数据是否存在下一页
  268. const { list } = res.data.data
  269. if (self.page == 1) {
  270. self.data = list
  271. } else {
  272. self.data = self.data.concat(list)
  273. }
  274. } else {
  275. self.$refs.scroller.finishInfinite(true)
  276. }
  277. }).finally(() => {
  278. this.$toast.clear()
  279. })
  280. },
  281. onRefresh (done) {
  282. let self = this
  283. this.page = 1
  284. this.get_list(function () {
  285. self.list = []
  286. done()
  287. })
  288. },
  289. onInfinite (done) {
  290. this.page++
  291. this.get_list(done)
  292. }
  293. },
  294. mounted () {
  295. this.dateDropdownItemTitle = this.getDateDropdownItemTitle()
  296. this.$nextTick(() => {
  297. this.$refs.scroller.finishInfinite(false)
  298. })
  299. }
  300. }
  301. </script>
  302. <style scoped lang="less">
  303. .body_com {
  304. height: calc(100% - 1.92rem);
  305. position: relative;
  306. }
  307. /deep/ .body_com .van-hairline--top-bottom:after {
  308. border: none;
  309. }
  310. /deep/ .body_com .van-hairline-unset--top-bottom:after {
  311. border: none;
  312. }
  313. .date-type__wrap {
  314. display: flex;
  315. padding: 0.24rem 0.32rem;
  316. flex-direction: row;
  317. justify-content: center;
  318. & .date-type__label {
  319. display: flex;
  320. width: 1.4rem;
  321. height: 0.52rem;
  322. font-size: 0.3rem;
  323. color: #fff;
  324. background: #26a2ff;
  325. border-top: 0.02rem solid #26a2ff;
  326. border-bottom: 0.02rem solid #26a2ff;
  327. border-left: 0.02rem solid #fff;
  328. align-items: center;
  329. justify-content: center;
  330. transition: color, background-color 100ms;
  331. &.active {
  332. color: #26a2ff;
  333. background: #fff;
  334. &:first-child {
  335. border-left: 0.02rem solid #26a2ff;
  336. }
  337. }
  338. &:first-child {
  339. border-radius: 0.08rem 0 0 0.08rem;
  340. }
  341. &:last-child {
  342. border-right: 0.02rem solid #26a2ff;
  343. border-radius: 0 0.08rem 0.08rem 0;
  344. }
  345. }
  346. }
  347. .date-picker__toolbar {
  348. display: flex;
  349. flex-direction: row;
  350. & .reset-button {
  351. color: #26a2ff !important;
  352. }
  353. & .button-border-none {
  354. border: transparent !important;
  355. }
  356. }
  357. .dropdown-menu__item {
  358. position: relative;
  359. display: flex;
  360. flex: 1;
  361. min-width: 0;
  362. font-size: 0.3rem;
  363. color: #323233;
  364. align-items: center;
  365. justify-content: center;
  366. cursor: pointer;
  367. & > span {
  368. position: relative;
  369. box-sizing: border-box;
  370. max-width: 100%;
  371. padding: 0 0.16rem;
  372. color: #323233;
  373. font-size: 0.3rem;
  374. line-height: 0.36rem;
  375. &:after {
  376. position: absolute;
  377. top: 50%;
  378. right: -0.08rem;
  379. margin-top: -0.1rem;
  380. border: 0.06rem solid;
  381. border-color: transparent transparent currentColor currentColor;
  382. transform: rotate(-45deg);
  383. opacity: 0.8;
  384. content: '';
  385. }
  386. &.asc-order:after {
  387. margin-top: -0.02rem;
  388. transform: rotate(135deg);
  389. }
  390. }
  391. }
  392. .rank-item__icon-wrap {
  393. display: flex;
  394. padding-right: 0.32rem;
  395. width: 0.64rem;
  396. align-items: center;
  397. justify-content: center;
  398. & .rank-item__icon {
  399. width: 0.64rem;
  400. }
  401. & .rank-item__icon-label {
  402. position: relative;
  403. }
  404. }
  405. .rank-item__name-wrap {
  406. display: flex;
  407. width: 100%;
  408. height: 100%;
  409. align-items: center;
  410. & .rank-item__name-icon {
  411. position: relative;
  412. display: flex;
  413. width: 0.96rem;
  414. height: 0.96rem;
  415. font-size: 0.3rem;
  416. color: #fff;
  417. background: #26a2ff;
  418. align-items: center;
  419. justify-content: center;
  420. border-radius: 100%;
  421. }
  422. & .rank-item__name {
  423. padding-left: 0.24rem;
  424. }
  425. }
  426. & /deep/ .rank-item__value {
  427. display: flex;
  428. color: #26a2ff;
  429. align-items: center;
  430. text-align: right;
  431. & .negative {
  432. color: #ff2d55;
  433. }
  434. }
  435. .filterItemsDiv {
  436. & .filterItemsTitle {
  437. padding: 0 0.13rem;
  438. color: #333;
  439. font-size: 0.3rem;
  440. }
  441. & .filterItemsContent {
  442. display: flex;
  443. padding-bottom: 0.24rem;
  444. & .sub__item {
  445. display: flex;
  446. margin: 0.24rem 0.13rem 0 0.13rem;
  447. width: 1.52rem;
  448. height: 0.72rem;
  449. background: #f5f7fa;
  450. border-radius: 0.04rem;
  451. align-items: center;
  452. justify-content: center;
  453. touch-action: none;
  454. &.selected {
  455. color: #26a2ff;
  456. background: #26a2ff33;
  457. }
  458. & span {
  459. font-size: 0.28rem;
  460. }
  461. }
  462. }
  463. }
  464. </style>