TimeSelectorContent.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. <template>
  2. <div class="selector-content" v-if="selectorType === 'card'" key="s-content">
  3. 暂不提供时间选择的card组件
  4. </div>
  5. <div
  6. class="selector-content"
  7. v-else-if="selectorType === 'line'"
  8. key="s-content"
  9. >
  10. <div class="select-group-container">
  11. <div
  12. v-for="(item, index) in timeSelectList"
  13. :key="index"
  14. class="j-button-item bgc"
  15. :class="{
  16. active: item.selected
  17. }"
  18. @click="clickTimeButton(item)"
  19. >
  20. {{ item.name }}
  21. </div>
  22. </div>
  23. <div
  24. v-if="showExactConf"
  25. class="date-time-container"
  26. :class="{ active: state.exact === 'exact' }"
  27. >
  28. <el-date-picker
  29. v-model="dateTimePickerState.start"
  30. class="date-time-item left"
  31. :type="dateTimePickerConf.type"
  32. :editable="dateTimePickerConf.editable"
  33. :align="dateTimePickerConf.align"
  34. :prefix-icon="dateTimePickerConf.prefixIcon"
  35. :format="dateTimePickerConf.format"
  36. :placeholder="dateTimePickerState.startPlaceHolder"
  37. :picker-options="startPickerOptions"
  38. :append-to-body="false"
  39. @change="startDatePickerChange"
  40. >
  41. </el-date-picker>
  42. <el-date-picker
  43. v-model="dateTimePickerState.end"
  44. class="date-time-item right"
  45. :type="dateTimePickerConf.type"
  46. :editable="dateTimePickerConf.editable"
  47. :align="dateTimePickerConf.align"
  48. :prefix-icon="dateTimePickerConf.prefixIcon"
  49. :format="dateTimePickerConf.format"
  50. :placeholder="dateTimePickerState.endPlaceHolder"
  51. :picker-options="endPickerOptions"
  52. :append-to-body="false"
  53. @change="endDatePickerChange"
  54. >
  55. </el-date-picker>
  56. </div>
  57. </div>
  58. </template>
  59. <script>
  60. import { DatePicker } from 'element-ui'
  61. import { dateFormatter } from '@/utils/'
  62. import moment from 'moment'
  63. import 'moment/locale/zh-cn'
  64. import { uniqWith } from 'lodash'
  65. moment.locale('zh-cn')
  66. const timeSelectMap = {
  67. default: [
  68. {
  69. name: '全部',
  70. value: 'all',
  71. selected: true
  72. },
  73. {
  74. name: '最近7天',
  75. value: 'lately7',
  76. selected: false
  77. },
  78. {
  79. name: '最近30天',
  80. value: 'lately30',
  81. selected: false
  82. },
  83. {
  84. name: '去年',
  85. value: 'lastYear',
  86. selected: false
  87. }
  88. ],
  89. sub: [
  90. {
  91. name: '全部',
  92. value: 'all',
  93. selected: true
  94. },
  95. {
  96. name: '今天',
  97. value: 'today',
  98. selected: false
  99. },
  100. {
  101. name: '昨天',
  102. value: 'yesterday',
  103. selected: false
  104. },
  105. {
  106. name: '最近一周',
  107. value: 'lately7',
  108. selected: false
  109. },
  110. {
  111. name: '最近一月',
  112. value: 'lately30',
  113. selected: false
  114. }
  115. ],
  116. more: [
  117. {
  118. name: '近3个月',
  119. value: 'lately90',
  120. selected: true
  121. },
  122. {
  123. name: '近半年',
  124. value: 'lately180',
  125. selected: false
  126. },
  127. {
  128. name: '今年全年',
  129. value: 'thisYear',
  130. selected: false
  131. },
  132. {
  133. name: '去年至今',
  134. value: 'sinceLastYear',
  135. selected: false
  136. },
  137. {
  138. name: '前年至今',
  139. value: 'sinceYearBeforeLast',
  140. selected: false
  141. }
  142. ],
  143. allAndlately90: [
  144. {
  145. name: '全部',
  146. value: 'all',
  147. selected: true
  148. },
  149. {
  150. name: '最近3个月',
  151. value: 'lately90',
  152. selected: false
  153. }
  154. ],
  155. bidSearch: [
  156. {
  157. name: '最近7天',
  158. value: 'lately7',
  159. selected: false
  160. },
  161. {
  162. name: '最近30天',
  163. value: 'lately30',
  164. selected: false
  165. },
  166. {
  167. name: '最近1年',
  168. value: 'sinceLastYear',
  169. selected: true
  170. },
  171. {
  172. name: '最近3年',
  173. value: 'sinceLastThreeYear',
  174. selected: false
  175. },
  176. {
  177. name: '最近5年',
  178. value: 'sinceLastFiveYear',
  179. selected: false
  180. }
  181. ],
  182. supplySearch: [
  183. {
  184. name: '全部',
  185. value: 'all',
  186. selected: true
  187. },
  188. {
  189. name: '今天',
  190. value: 'today',
  191. selected: false
  192. },
  193. {
  194. name: '最近7天',
  195. value: 'lately7',
  196. selected: false
  197. },
  198. {
  199. name: '最近30天',
  200. value: 'lately30',
  201. selected: false
  202. },
  203. {
  204. name: '近3个月',
  205. value: 'lately90',
  206. selected: false
  207. }
  208. ]
  209. }
  210. function getTimeSelectList(map) {
  211. let arr = []
  212. for (const key in map) {
  213. const item = map[key]
  214. if (Array.isArray(item)) {
  215. arr = arr.concat(item)
  216. }
  217. }
  218. // 数组去重
  219. const uniqed = uniqWith(arr, function (a, b) {
  220. return a.value === b.value
  221. })
  222. return uniqed
  223. }
  224. // const timeSelectListAll = getTimeSelectList(timeSelectMap)
  225. export default {
  226. name: 'time-selector-content',
  227. components: {
  228. [DatePicker.name]: DatePicker
  229. },
  230. props: {
  231. selectorType: {
  232. type: String,
  233. default: 'card' // card/line
  234. },
  235. selectorTime: {
  236. type: String,
  237. default: 'default' // default/sub/more/bidSearch
  238. },
  239. options: {
  240. type: Array,
  241. default: () => []
  242. },
  243. defaultSelectedKey: {
  244. type: String,
  245. default: 'all' // all/lately30/lately90...
  246. },
  247. showExact: {
  248. type: Boolean,
  249. default: true
  250. }
  251. },
  252. data() {
  253. return {
  254. debug: false,
  255. timeSelectList: [],
  256. dateTimePickerConf: {
  257. type: 'date',
  258. editable: false,
  259. align: 'center',
  260. prefixIcon: 'clear-icon',
  261. format: 'yyyy-MM-dd'
  262. },
  263. dateTimePickerState: {
  264. start: '',
  265. end: '',
  266. startPlaceHolder: '', // 开始日期
  267. endPlaceHolder: '' // 结束日期
  268. },
  269. startPickerOptions: {
  270. disabledDate: (time) => {
  271. const { end } = this.dateTimePickerState
  272. if (end) {
  273. return time.getTime() > +new Date(end)
  274. }
  275. }
  276. },
  277. endPickerOptions: {
  278. disabledDate: (time) => {
  279. const { start } = this.dateTimePickerState
  280. if (start) {
  281. return time.getTime() < +new Date(start)
  282. }
  283. }
  284. }
  285. }
  286. },
  287. computed: {
  288. showExactConf() {
  289. // 如果传了options,则默认不展示exact
  290. if (this.options.length) {
  291. return this.options.includes('exact')
  292. } else {
  293. return this.showExact
  294. }
  295. },
  296. state() {
  297. return this.getState()
  298. }
  299. },
  300. created() {
  301. this.timeSelectList = this.calcTimeSelectList()
  302. this.calcLastTime()
  303. },
  304. methods: {
  305. dateFormatter,
  306. calcTimeSelectList() {
  307. let timeSelectList = []
  308. const propTimeSelectList = timeSelectMap[this.selectorTime]
  309. if (this.options.length) {
  310. timeSelectList = this.options
  311. .map((item) => {
  312. const conf = propTimeSelectList.find((t) => {
  313. return t.value === item
  314. })
  315. return conf
  316. })
  317. .filter((v) => !!v)
  318. } else {
  319. timeSelectList = propTimeSelectList
  320. }
  321. return timeSelectList
  322. },
  323. calcLastTime() {
  324. if (this.selectorTime === 'more') {
  325. const renameList = [
  326. 'thisYear', // 今年全年
  327. 'sinceLastYear', // 去年至今
  328. 'sinceYearBeforeLast' // 前年至今
  329. ]
  330. const thisYear = new Date().getFullYear()
  331. this.timeSelectList.forEach((item) => {
  332. if (renameList.indexOf(item.value) !== -1) {
  333. if (item.value === renameList[0]) {
  334. item.name = `${thisYear}年全年`
  335. } else if (item.value === renameList[1]) {
  336. item.name = `${thisYear - 1}年至今`
  337. } else if (item.value === renameList[2]) {
  338. item.name = `${thisYear - 2}年至今`
  339. }
  340. }
  341. })
  342. }
  343. },
  344. setState(data) {
  345. // {
  346. // start: 1620230400000, // 2021-5-6
  347. // end: 1620489600000, // 2021-5-9
  348. // exact: 'all'
  349. // }
  350. if (!data || Object.keys(data).length === 0) {
  351. this.setTimeSelectListState(this.defaultSelectedKey)
  352. } else {
  353. switch (data.exact) {
  354. case 'all':
  355. case 'today':
  356. case 'yesterday':
  357. case 'lately7':
  358. case 'lately30':
  359. case 'lately90':
  360. case 'lately180':
  361. case 'thisYear':
  362. case 'sinceLastYear':
  363. case 'sinceLastThreeYear':
  364. case 'sinceLastFiveYear':
  365. case 'sinceYearBeforeLast':
  366. case 'lastYear': {
  367. this.setTimeSelectListState(data.exact)
  368. this.clearDateTimePicker()
  369. break
  370. }
  371. case 'exact': {
  372. if (!data.start || !data.end) break
  373. if (data.start < data.end) {
  374. this.timeSelectList.forEach((v) => (v.selected = false))
  375. this.dateTimePickerState.start = new Date(data.start)
  376. this.dateTimePickerState.end = new Date(data.end)
  377. }
  378. break
  379. }
  380. default: {
  381. console.warn('exact值为空')
  382. }
  383. }
  384. }
  385. },
  386. getState() {
  387. // timeState.exact: all/lately7/lately30/lastYear/exact5种状态
  388. // 为true时候表示精确筛选,从时间选择器中获取值(其他为根据当前时间计算出的值)
  389. const timeState = {
  390. start: 0,
  391. end: 0,
  392. exact: 'exact'
  393. }
  394. const durations = {
  395. hour1: 60 * 60 * 1000,
  396. day1: 60 * 60 * 1000 * 24 * 1,
  397. day7: 60 * 60 * 1000 * 24 * 7,
  398. day30: 60 * 60 * 1000 * 24 * 30
  399. }
  400. const selectButton = this.timeSelectList.find((item) => item.selected)
  401. if (selectButton) {
  402. timeState.exact = selectButton.value
  403. Object.assign(timeState, this.calcNotExactTime(timeState.exact))
  404. } else {
  405. timeState.exact = 'exact'
  406. if (this.dateTimePickerState.start) {
  407. timeState.start = this.dateTimePickerState.start.getTime()
  408. }
  409. if (this.dateTimePickerState.end) {
  410. // 结束时间为当天23:59:59
  411. timeState.end =
  412. this.dateTimePickerState.end.getTime() + (durations.day1 - 1000)
  413. }
  414. // 什么都未选择的情况
  415. if (!timeState.start && !timeState.end) {
  416. timeState.exact = ''
  417. }
  418. }
  419. return timeState
  420. },
  421. onChange() {
  422. const state = this.getState()
  423. if (this.debug) {
  424. state.startFormatted = this.dateFormatter(state.start)
  425. state.endFormatted = this.dateFormatter(state.end)
  426. console.table({
  427. start: state.startFormatted,
  428. end: state.endFormatted
  429. })
  430. }
  431. this.$emit('onChange', state)
  432. },
  433. setTimeSelectListState(value, callback) {
  434. this.timeSelectList.forEach((item) => {
  435. item.selected = item.value === value
  436. callback && callback(item)
  437. })
  438. },
  439. clearDateTimePicker() {
  440. this.dateTimePickerState.start = ''
  441. this.dateTimePickerState.end = ''
  442. },
  443. // 计算lately7/lately30/lastYear等的开始和结束时间
  444. // endTime传入一个时间戳
  445. calcNotExactTime(exact = 'lately7', endTime = Date.now()) {
  446. const t = {
  447. start: 0,
  448. end: +new Date(endTime)
  449. }
  450. const durations = {
  451. hour1: 60 * 60 * 1000,
  452. day1: 60 * 60 * 1000 * 24 * 1,
  453. day7: 60 * 60 * 1000 * 24 * 7,
  454. day30: 60 * 60 * 1000 * 24 * 30
  455. }
  456. switch (exact) {
  457. case 'today': {
  458. t.start = moment().startOf('day').format('x')
  459. t.end = moment().endOf('day').format('x')
  460. break
  461. }
  462. case 'yesterday': {
  463. t.start = moment().startOf('day').format('x') - durations.day1
  464. t.end = moment().endOf('day').format('x') - durations.day1
  465. break
  466. }
  467. case 'lately7': {
  468. t.start = t.end - durations.day7
  469. break
  470. }
  471. case 'lately30': {
  472. t.start = t.end - durations.day30
  473. break
  474. }
  475. case 'lately90': {
  476. // 近90天
  477. t.start = t.end - durations.day30 * 3
  478. break
  479. }
  480. case 'lately180': {
  481. // 180天
  482. t.start = t.end - durations.day30 * 6
  483. break
  484. }
  485. case 'thisYear': {
  486. // 今年全年
  487. const year = new Date(t.end).getFullYear()
  488. t.start = +new Date(`${year}`)
  489. t.end = +new Date(`${year + 1}`) - durations.hour1 * 8 - 1
  490. break
  491. }
  492. case 'lastYear': {
  493. // 去年全年
  494. const year = new Date(t.end).getFullYear()
  495. const lastYear = year - 1
  496. t.start = +new Date(`${lastYear}`)
  497. t.end = +new Date(`${year}`) - durations.hour1 * 8 - 1
  498. break
  499. }
  500. case 'sinceLastYear': {
  501. // 去年至今
  502. const year = new Date(t.end).getFullYear()
  503. const lastYear = year - 1
  504. t.start = +new Date(`${lastYear}`)
  505. break
  506. }
  507. case 'sinceLastThreeYear': {
  508. // 去年至今
  509. const year = new Date(t.end).getFullYear()
  510. const lastYear = year - 3
  511. t.start = +new Date(`${lastYear}`)
  512. break
  513. }
  514. case 'sinceLastFiveYear': {
  515. // 去年至今
  516. const year = new Date(t.end).getFullYear()
  517. const lastYear = year - 5
  518. t.start = +new Date(`${lastYear}`)
  519. break
  520. }
  521. case 'sinceYearBeforeLast': {
  522. // 前年至今
  523. const year = new Date(t.end).getFullYear()
  524. t.start = +new Date(`${year - 2}`)
  525. break
  526. }
  527. default: {
  528. t.start = 0
  529. t.end = 0
  530. break
  531. }
  532. }
  533. return t
  534. },
  535. clickTimeButton(item) {
  536. if (item.selected) return
  537. this.timeSelectList.forEach((v) => (v.selected = false))
  538. item.selected = true
  539. this.onChange()
  540. },
  541. startDatePickerChange(start) {
  542. const { end } = this.dateTimePickerState
  543. if (start && end) {
  544. // start和end都有值
  545. this.setTimeSelectListState()
  546. this.onChange()
  547. } else if (!start && !end) {
  548. // start和end都没值
  549. this.setTimeSelectListState(this.defaultSelectedKey)
  550. this.onChange()
  551. }
  552. },
  553. endDatePickerChange(end) {
  554. const { start } = this.dateTimePickerState
  555. if (start && end) {
  556. // start和end都有值
  557. this.setTimeSelectListState()
  558. this.onChange()
  559. } else if (!start && !end) {
  560. // start和end都没值
  561. this.setTimeSelectListState(this.defaultSelectedKey)
  562. this.onChange()
  563. }
  564. }
  565. }
  566. }
  567. </script>
  568. <style lang="scss" scoped>
  569. .selector-content,
  570. .select-group-container {
  571. display: flex;
  572. align-items: center;
  573. }
  574. .selector-content {
  575. flex-wrap: wrap;
  576. }
  577. .select-group-container {
  578. margin-right: 24px;
  579. }
  580. .date-time-container {
  581. display: flex;
  582. padding: 6px;
  583. background-color: #f4f5f7;
  584. border-radius: 4px;
  585. &.active {
  586. background-color: #2cb7ca;
  587. .date-time-item {
  588. &.left::after {
  589. background-color: #e0e0e0;
  590. }
  591. }
  592. }
  593. .clear-icon {
  594. display: none;
  595. }
  596. .date-time-item {
  597. position: relative;
  598. width: 160px;
  599. height: 28px;
  600. line-height: 28px;
  601. text-align: center;
  602. &.left {
  603. margin-right: 30px;
  604. &::after {
  605. content: '';
  606. position: absolute;
  607. width: 12px;
  608. height: 2px;
  609. top: 50%;
  610. right: -10px;
  611. transform: translate(100%, -50%);
  612. background-color: #1d1d1d;
  613. }
  614. }
  615. ::v-deep {
  616. .el-input__inner {
  617. padding: 0;
  618. height: 100%;
  619. text-align: center;
  620. cursor: pointer;
  621. }
  622. .el-input__suffix {
  623. display: flex;
  624. align-items: center;
  625. }
  626. }
  627. }
  628. }
  629. </style>