index.taro.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <template>
  2. <nut-picker
  3. v-model="selectedValue"
  4. :visible="show"
  5. :okText="okText"
  6. :cancelText="cancelText"
  7. @close="closeHandler"
  8. :columns="columns"
  9. @change="changeHandler"
  10. :title="title"
  11. @confirm="confirm"
  12. :isWrapTeleport="isWrapTeleport"
  13. ></nut-picker>
  14. </template>
  15. <script lang="ts">
  16. import { toRefs, watch, computed, reactive, onBeforeMount } from 'vue';
  17. import type { PropType } from 'vue';
  18. import nutPicker from '../picker/index.taro.vue';
  19. import { popupProps } from '../popup/index.vue';
  20. import { PickerOption } from '../picker/types';
  21. import { createComponent } from '../../utils/create';
  22. import { padZero } from './utils';
  23. const { componentName, create } = createComponent('datepicker');
  24. type Formatter = (type: string, option: PickerOption) => PickerOption;
  25. type Filter = (columnType: string, options: PickerOption[]) => PickerOption[];
  26. const currentYear = new Date().getFullYear();
  27. function isDate(val: Date): val is Date {
  28. return Object.prototype.toString.call(val) === '[object Date]' && !isNaN(val.getTime());
  29. }
  30. const zhCNType = {
  31. day: '日',
  32. year: '年',
  33. month: '月',
  34. hour: '时',
  35. minute: '分',
  36. seconds: '秒'
  37. };
  38. export default create({
  39. components: {
  40. nutPicker
  41. },
  42. props: {
  43. ...popupProps,
  44. modelValue: null,
  45. title: {
  46. type: String,
  47. default: ''
  48. },
  49. okText: {
  50. type: String,
  51. default: ''
  52. },
  53. cancelText: {
  54. type: String,
  55. default: ''
  56. },
  57. type: {
  58. type: String,
  59. default: 'date'
  60. },
  61. isShowChinese: {
  62. type: Boolean,
  63. default: false
  64. },
  65. minuteStep: {
  66. type: Number,
  67. default: 1
  68. },
  69. minDate: {
  70. type: Date,
  71. default: () => new Date(currentYear - 10, 0, 1),
  72. validator: isDate
  73. },
  74. maxDate: {
  75. type: Date,
  76. default: () => new Date(currentYear + 10, 11, 31),
  77. validator: isDate
  78. },
  79. formatter: {
  80. type: Function as PropType<Formatter>,
  81. default: null
  82. },
  83. filter: Function as PropType<Filter>
  84. },
  85. emits: ['click', 'update:visible', 'change', 'confirm', 'update:moduleValue'],
  86. setup(props, { emit }) {
  87. const state = reactive({
  88. show: false,
  89. currentDate: new Date(),
  90. title: props.title,
  91. selectedValue: []
  92. });
  93. const formatValue = (value: Date) => {
  94. if (!isDate(value)) {
  95. value = props.minDate;
  96. }
  97. let timestmp = Math.max(value.getTime(), props.minDate.getTime());
  98. timestmp = Math.min(timestmp, props.maxDate.getTime());
  99. return new Date(timestmp);
  100. };
  101. function getMonthEndDay(year: number, month: number): number {
  102. return 32 - new Date(year, month - 1, 32).getDate();
  103. }
  104. const getBoundary = (type: string, value: Date) => {
  105. const boundary = props[`${type}Date`];
  106. const year = boundary.getFullYear();
  107. let month = 1;
  108. let date = 1;
  109. let hour = 0;
  110. let minute = 0;
  111. if (type === 'max') {
  112. month = 12;
  113. date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1);
  114. hour = 23;
  115. minute = 59;
  116. }
  117. const seconds = minute;
  118. if (value.getFullYear() === year) {
  119. month = boundary.getMonth() + 1;
  120. if (value.getMonth() + 1 === month) {
  121. date = boundary.getDate();
  122. if (value.getDate() === date) {
  123. hour = boundary.getHours();
  124. if (value.getHours() === hour) {
  125. minute = boundary.getMinutes();
  126. }
  127. }
  128. }
  129. }
  130. return {
  131. [`${type}Year`]: year,
  132. [`${type}Month`]: month,
  133. [`${type}Date`]: date,
  134. [`${type}Hour`]: hour,
  135. [`${type}Minute`]: minute,
  136. [`${type}Seconds`]: seconds
  137. };
  138. };
  139. const ranges = computed(() => {
  140. const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = getBoundary('max', state.currentDate);
  141. const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = getBoundary('min', state.currentDate);
  142. let result = [
  143. {
  144. type: 'year',
  145. range: [minYear, maxYear]
  146. },
  147. {
  148. type: 'month',
  149. range: [minMonth, maxMonth]
  150. },
  151. {
  152. type: 'day',
  153. range: [minDate, maxDate]
  154. },
  155. {
  156. type: 'hour',
  157. range: [minHour, maxHour]
  158. },
  159. {
  160. type: 'minute',
  161. range: [minMinute, maxMinute]
  162. },
  163. {
  164. type: 'seconds',
  165. range: [minSeconds, maxSeconds]
  166. }
  167. ];
  168. switch (props.type) {
  169. case 'date':
  170. result = result.slice(0, 3);
  171. break;
  172. case 'datetime':
  173. result = result.slice(0, 5);
  174. break;
  175. case 'time':
  176. result = result.slice(3, 6);
  177. break;
  178. case 'year-month':
  179. result = result.slice(0, 2);
  180. break;
  181. case 'month-day':
  182. result = result.slice(1, 3);
  183. break;
  184. case 'datehour':
  185. result = result.slice(0, 4);
  186. break;
  187. }
  188. return result;
  189. });
  190. const columns = computed(() => {
  191. // console.log(ranges.value);
  192. const val = ranges.value.map((res, columnIndex) => {
  193. return generateValue(res.range[0], res.range[1], getDateIndex(res.type), res.type, columnIndex);
  194. });
  195. return val;
  196. });
  197. const changeHandler = ({
  198. columnIndex,
  199. selectedValue,
  200. selectedOptions
  201. }: {
  202. columnIndex: number;
  203. selectedValue: (string | number)[];
  204. selectedOptions: PickerOption[];
  205. }) => {
  206. if (['date', 'datetime'].includes(props.type)) {
  207. let formatDate = [];
  208. formatDate = selectedValue;
  209. let date: Date;
  210. if (props.type === 'date') {
  211. state.currentDate = formatValue(
  212. new Date(
  213. formatDate[0],
  214. formatDate[1] - 1,
  215. Math.min(formatDate[2], getMonthEndDay(formatDate[0], formatDate[1]))
  216. )
  217. );
  218. } else if (props.type === 'datetime') {
  219. state.currentDate = formatValue(
  220. new Date(
  221. formatDate[0],
  222. formatDate[1] - 1,
  223. Math.min(formatDate[2], getMonthEndDay(formatDate[0], formatDate[1])),
  224. formatDate[3],
  225. formatDate[4]
  226. )
  227. );
  228. }
  229. }
  230. emit('change', { columnIndex, selectedValue, selectedOptions });
  231. };
  232. const formatterOption = (type, value) => {
  233. const { filter, formatter, isShowChinese } = props;
  234. let fOption = null;
  235. if (formatter) {
  236. fOption = formatter(type, { text: padZero(value, 2), value: padZero(value, 2) });
  237. } else {
  238. const padMin = padZero(value, 2);
  239. const fatter = isShowChinese ? zhCNType[type] : '';
  240. fOption = { text: padMin + fatter, value: padMin };
  241. }
  242. return fOption;
  243. };
  244. const generateValue = (min: number, max: number, val: number | string, type: string, columnIndex: number) => {
  245. // if (!(max > min)) return;
  246. const arr: Array<PickerOption> = [];
  247. let index = 0;
  248. while (min <= max) {
  249. arr.push(formatterOption(type, min));
  250. if (type === 'minute') {
  251. min += props.minuteStep;
  252. } else {
  253. min++;
  254. }
  255. if (min <= val) {
  256. index++;
  257. }
  258. }
  259. state.selectedValue[columnIndex] = arr[index].value;
  260. // return { values: arr, defaultIndex: index };
  261. return props.filter ? props.filter(type, arr) : arr;
  262. };
  263. const getDateIndex = (type: string) => {
  264. if (type === 'year') {
  265. return state.currentDate.getFullYear();
  266. } else if (type === 'month') {
  267. return state.currentDate.getMonth() + 1;
  268. } else if (type === 'day') {
  269. return state.currentDate.getDate();
  270. } else if (type === 'hour') {
  271. return state.currentDate.getHours();
  272. } else if (type === 'minute') {
  273. return state.currentDate.getMinutes();
  274. } else if (type === 'seconds') {
  275. return state.currentDate.getSeconds();
  276. }
  277. return 0;
  278. };
  279. const closeHandler = () => {
  280. emit('update:visible', false);
  281. };
  282. const confirm = (val: Event) => {
  283. emit('update:visible', false);
  284. emit('confirm', val);
  285. };
  286. onBeforeMount(() => {
  287. state.currentDate = formatValue(props.modelValue);
  288. });
  289. watch(
  290. () => props.modelValue,
  291. (value) => {
  292. state.currentDate = formatValue(value);
  293. }
  294. );
  295. watch(
  296. () => props.title,
  297. (val) => {
  298. state.title = val;
  299. }
  300. );
  301. watch(
  302. () => props.visible,
  303. (val) => {
  304. state.show = val;
  305. }
  306. );
  307. return {
  308. ...toRefs(state),
  309. changeHandler,
  310. closeHandler,
  311. confirm,
  312. columns
  313. };
  314. }
  315. });
  316. </script>