index.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <template>
  2. <div :class="[n([''])]">
  3. <div
  4. :class="[n(['input']), isDatePanelShow && n(['input-active'])]"
  5. @click="setDatePanelDisplay(!isDatePanelShow)"
  6. >
  7. <slot name="start-icon" />
  8. <input
  9. v-model="startFormatDate"
  10. :placeholder="startPlaceholderVal"
  11. :class="[n(['input-start'])]"
  12. style="pointer-events: none"
  13. type="text"
  14. :readonly="true"
  15. :disabled="isUniFrameWork"
  16. autocomplete="false"
  17. >
  18. <span v-if="type !== 'single'">-</span>
  19. <input
  20. v-if="type !== 'single'"
  21. v-model="endFormatDate"
  22. :placeholder="endPlaceholderVal"
  23. :class="[n(['input-end'])]"
  24. style="pointer-events: none"
  25. type="text"
  26. :readonly="true"
  27. :disabled="isUniFrameWork"
  28. autocomplete="false"
  29. >
  30. <slot name="end-icon" />
  31. </div>
  32. <div
  33. v-if="isDatePanelShow"
  34. :class="[n(['dialog'])]"
  35. >
  36. <div
  37. :class="[
  38. n([
  39. 'dialog-container',
  40. 'dialog-container-' + rangeTableType,
  41. 'dialog-container-' + popupPosition,
  42. ]),
  43. ]"
  44. >
  45. <DatePickerPanel
  46. :type="props.type"
  47. rangeType="left"
  48. :date="dateValue"
  49. :startDate="startValue"
  50. :endDate="endValue"
  51. :currentOtherPanelValue="rightCurrentPanelValue"
  52. @pick="handlePick"
  53. @change="handleLeftPanelChange"
  54. />
  55. <DatePickerPanel
  56. v-if="props.type === 'range' && isPC && rangeTableType === 'two'"
  57. :type="props.type"
  58. rangeType="right"
  59. :date="dateValue"
  60. :startDate="startValue"
  61. :endDate="endValue"
  62. :currentOtherPanelValue="leftCurrentPanelValue"
  63. @pick="handlePick"
  64. @change="handleRightPanelChange"
  65. />
  66. </div>
  67. </div>
  68. </div>
  69. </template>
  70. <script setup lang="ts">
  71. import { ref, computed } from '../../../adapter-vue';
  72. import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
  73. // dayjs extension
  74. import dayjs, { Dayjs } from 'dayjs';
  75. import localeData from 'dayjs/plugin/localeData.js';
  76. import isSameOrAfter from 'dayjs/plugin/isSameOrAfter.js';
  77. import isSameOrBefore from 'dayjs/plugin/isSameOrBefore.js';
  78. import 'dayjs/locale/zh-cn';
  79. import DatePickerPanel from './date-picker-panel.vue';
  80. import { DateCell } from './date-picker';
  81. import { isPC, isUniFrameWork } from '../../../utils/env';
  82. dayjs.extend(localeData);
  83. dayjs.extend(isSameOrAfter);
  84. dayjs.extend(isSameOrBefore);
  85. dayjs.locale('zh-cn');
  86. const emit = defineEmits(['pick', 'change']);
  87. const props = defineProps({
  88. type: {
  89. type: String,
  90. default: 'range', // "single":单选, 暂不支持 / "range":区间选择
  91. },
  92. rangeTableType: {
  93. type: String,
  94. default: 'one', // "one": 在 一个 datePanel 之内选择区间(注意,移动端因屏幕较窄,仅支持在一个 datePanel 中进行选择) / "two": 在 两个 datePanel 之间选择区间
  95. },
  96. startPlaceholder: {
  97. type: String,
  98. default: () => TUITranslateService.t('开始时间'),
  99. },
  100. endPlaceholder: {
  101. type: String,
  102. default: () => TUITranslateService.t('开始时间'),
  103. },
  104. popupPosition: {
  105. type: String,
  106. default: 'bottom', // "top": 向上弹出 datePanel / "bottom": 向下弹出 datePanel
  107. },
  108. // 默认单选日期
  109. defaultSingleDate: {
  110. type: Dayjs,
  111. default: null,
  112. required: false,
  113. },
  114. });
  115. const isDatePanelShow = ref<boolean>(false);
  116. const dateValue = ref<typeof Dayjs>(props.type === 'single' ? props?.defaultSingleDate : null);
  117. const startValue = ref<typeof Dayjs>(props.type === 'single' ? props?.defaultSingleDate : null);
  118. const endValue = ref<typeof Dayjs>(props.type === 'single' ? props?.defaultSingleDate : null);
  119. const startFormatDate = computed(() => startValue?.value?.format('YYYY/MM/DD'));
  120. const endFormatDate = computed(() => endValue?.value?.format('YYYY/MM/DD'));
  121. const startPlaceholderVal = props.startPlaceholder;
  122. const endPlaceholderVal = props.endPlaceholder;
  123. const leftCurrentPanelValue = ref<typeof Dayjs>();
  124. const rightCurrentPanelValue = ref<typeof Dayjs>();
  125. const setDatePanelDisplay = (show: boolean) => {
  126. isDatePanelShow.value = show;
  127. };
  128. const n = (classNameList: Array<string>) => {
  129. const resultClassList: Array<string> = [];
  130. classNameList.forEach((className: string) => {
  131. if (className) {
  132. resultClassList.push('tui-date-picker-' + className);
  133. !isPC && resultClassList.push('tui-date-picker-h5-' + className);
  134. } else {
  135. resultClassList.push('tui-date-picker');
  136. !isPC && resultClassList.push('tui-date-picker-h5');
  137. }
  138. });
  139. return resultClassList;
  140. };
  141. const handlePick = (cell: DateCell) => {
  142. switch (props.type) {
  143. case 'single':
  144. startValue.value = cell.date;
  145. endValue.value = cell.date;
  146. dateValue.value = cell.date;
  147. emit('change', cell);
  148. emit('pick', dateValue.value);
  149. // 避免生硬直接关闭
  150. setTimeout(() => {
  151. setDatePanelDisplay(false);
  152. }, 300);
  153. break;
  154. case 'range':
  155. if (!startValue?.value) {
  156. startValue.value = cell.date;
  157. } else if (!endValue?.value) {
  158. if (startValue?.value?.isSameOrBefore(cell.date, 'day')) {
  159. endValue.value = cell.date;
  160. } else {
  161. endValue.value = startValue.value;
  162. startValue.value = cell.date;
  163. }
  164. emit('pick', {
  165. startDate: startValue?.value?.startOf('date'),
  166. endDate: endValue?.value?.endOf('date'),
  167. });
  168. // 避免生硬直接关闭
  169. setTimeout(() => {
  170. setDatePanelDisplay(false);
  171. }, 200);
  172. } else {
  173. startValue.value = cell.date;
  174. endValue.value = null;
  175. }
  176. emit('change', {
  177. startDate: startValue.value,
  178. endDate: endValue.value,
  179. leftCurrentPanel: leftCurrentPanelValue.value,
  180. rightCurrentPanel: leftCurrentPanelValue.value,
  181. });
  182. break;
  183. }
  184. };
  185. const handleLeftPanelChange = (value: typeof Dayjs) => {
  186. leftCurrentPanelValue.value = value;
  187. emit('change', {
  188. startDate: startValue.value,
  189. endDate: endValue.value,
  190. leftCurrentPanel: leftCurrentPanelValue.value,
  191. rightCurrentPanel: leftCurrentPanelValue.value,
  192. });
  193. };
  194. const handleRightPanelChange = (value: typeof Dayjs) => {
  195. rightCurrentPanelValue.value = value;
  196. emit('change', {
  197. startDate: startValue.value,
  198. endDate: endValue.value,
  199. leftCurrentPanel: leftCurrentPanelValue.value,
  200. rightCurrentPanel: leftCurrentPanelValue.value,
  201. });
  202. };
  203. </script>
  204. <style scoped lang="scss">
  205. .tui-date-picker {
  206. &-input {
  207. min-width: 160px;
  208. display: flex;
  209. flex-direction: row;
  210. color: #666;
  211. border-radius: 5px;
  212. font-size: 12px;
  213. &-start,
  214. &-end {
  215. flex: 1;
  216. color: #666;
  217. height: 17px;
  218. border: none;
  219. width: 67px;
  220. background-color: transparent;
  221. font-size: 12px;
  222. text-align: center;
  223. &:focus {
  224. border: none;
  225. outline: none;
  226. }
  227. &::placeholder {
  228. text-align: center;
  229. }
  230. }
  231. }
  232. &-dialog {
  233. position: relative;
  234. &-container {
  235. position: absolute;
  236. display: flex;
  237. flex-direction: row;
  238. padding: 10px;
  239. left: 5px;
  240. background-color: #fff;
  241. box-shadow: rgba(0, 0, 0, 0.16) 0 3px 6px, rgba(0, 0, 0, 0.23) 0 3px 6px;
  242. z-index: 1000;
  243. &-bottom {
  244. left: 5px;
  245. }
  246. &-top {
  247. bottom: 30px;
  248. }
  249. &-one {
  250. left: -5px;
  251. }
  252. }
  253. }
  254. }
  255. </style>