TimeSelectorContent.vue 14 KB

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