hookehuyr

✨ feat(日期时间控件): 样式和功能调整

1 <!-- 1 <!--
2 * @Date: 2022-09-08 15:02:45 2 * @Date: 2022-09-08 15:02:45
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-02-07 16:46:30 4 + * @LastEditTime: 2023-04-07 17:31:50
5 - * @FilePath: /data-table/src/components/DateTimePickerField/index.vue 5 + * @FilePath: /custom_form/src/components/DateTimePickerField/index.vue
6 * @Description: 日期时间选择器 6 * @Description: 日期时间选择器
7 --> 7 -->
8 <template> 8 <template>
9 <div v-if="HideShow" class="datetime-picker"> 9 <div v-if="HideShow" class="datetime-picker">
10 <div class="label"> 10 <div class="label">
11 - <span v-if="item.component_props.required">&nbsp;*</span> 11 + <text v-if="item.component_props.required">&nbsp;*</text>
12 {{ item.component_props.label }} 12 {{ item.component_props.label }}
13 </div> 13 </div>
14 - <van-field 14 + <nut-cell desc-text-align="left" :desc="popupDesc" @click="onTap" is-link style="border: 1px solid #eaeaea; border-radius: 0.25rem; padding: 0.25rem 0.5rem;"></nut-cell>
15 - v-model="item.value" 15 + <div
16 - is-link 16 + v-if="show_error"
17 - readonly 17 + style="padding: 5px; color: red; font-size: 12px;"
18 - :name="item.key" 18 + >
19 - :required="item.component_props.required" 19 + {{ error_msg }}
20 - :disabled="item.component_props.readonly" 20 + </div>
21 - :placeholder="item.component_props.placeholder ? item.component_props.placeholder : '请选择日期时间'" 21 + <nut-popup position="bottom" v-model:visible="showPicker">
22 - :rules="rules" 22 + <nut-date-picker
23 - @click="onTap" 23 + v-model="currentDate"
24 - :border="false" 24 + title="日期时间选择"
25 - /> 25 + :type="columns_type"
26 - <van-popup v-model:show="showPicker" position="bottom"> 26 + :min-date="minDate"
27 - <van-picker-group 27 + :max-date="maxDate"
28 - title="请选择日期时间"
29 - :tabs="['选择日期', '选择时间']"
30 @confirm="onConfirm" 28 @confirm="onConfirm"
31 - @cancel="onCancel" 29 + @cancel="showPicker = false"
30 + :is-show-chinese="true"
32 > 31 >
33 - <van-date-picker v-model="currentDate" :min-date="minDate" :max-date="maxDate" :columns-type="columns_date_type" /> 32 + </nut-date-picker>
34 - <van-time-picker v-model="currentTime" :columns-type="columns_time_type" /> 33 + </nut-popup>
35 - </van-picker-group>
36 - </van-popup>
37 </div> 34 </div>
38 </template> 35 </template>
39 36
40 <script setup> 37 <script setup>
41 -import { showToast } from "vant"; 38 +import { ref, computed, watch, onMounted, reactive } from "vue";
42 -import dayjs from "dayjs";
43 39
44 const props = defineProps({ 40 const props = defineProps({
45 item: Object, 41 item: Object,
46 }); 42 });
43 +const emit = defineEmits(["active"]);
44 +
47 // 隐藏显示 45 // 隐藏显示
48 const HideShow = computed(() => { 46 const HideShow = computed(() => {
49 return !props.item.component_props.disabled 47 return !props.item.component_props.disabled
50 }) 48 })
49 +
51 const showPicker = ref(false); 50 const showPicker = ref(false);
51 +const popupDesc = ref('');
52 +let minDate = new Date(2020, 0, 1);
53 +let maxDate = new Date(2035, 10, 1);
54 +const currentDate = ref('');
52 const readonly = props.item.component_props.readonly; 55 const readonly = props.item.component_props.readonly;
53 56
54 const onTap = () => { 57 const onTap = () => {
55 if (readonly) return false; // 如果为只读,不能设置 58 if (readonly) return false; // 如果为只读,不能设置
56 showPicker.value = true 59 showPicker.value = true
57 } 60 }
58 -const currentDate = ref([]);
59 -const currentTime = ref([]);
60 61
61 -const onConfirm = () => { 62 +const onConfirm = ({ selectedValue, selectedOptions }) => {
62 - props.item.value = `${currentDate.value.join("-")} ${currentTime.value.join(":")}`; 63 + if (columns_type.value === 'datetime') {
64 + const date = selectedValue.slice(0, 3).join('-');
65 + const time = selectedValue.slice(3).join(':');
66 + popupDesc.value = date + ' ' + time;
67 + props.item.value = {
68 + key: "datetime",
69 + filed_name: props.item.key,
70 + value: date + ' ' + time,
71 + };
72 + } else {
73 + popupDesc.value = selectedOptions.map((val) => val.value).join('-');
74 + props.item.value = {
75 + key: "date",
76 + filed_name: props.item.key,
77 + value: selectedOptions.map((val) => val.value).join('-'),
78 + };
79 + }
80 + emit("active", props.item.value);
63 showPicker.value = false; 81 showPicker.value = false;
82 + validDateTime()
64 }; 83 };
65 const onCancel = () => { 84 const onCancel = () => {
66 showPicker.value = false; 85 showPicker.value = false;
67 }; 86 };
68 87
69 -const columns_date_type = ref([]); 88 +const columns_type = ref('datetime');
70 -const columns_time_type = ref([]);
71 const date_format = props.item.component_props.data_dateformat; 89 const date_format = props.item.component_props.data_dateformat;
72 -// 数字前面补位 90 +// // 数字前面补位
73 -const formatZero = (num, len) => { 91 +// const formatZero = (num, len) => {
74 - if (String(num).length > len) { 92 +// if (String(num).length > len) {
75 - return num; 93 +// return num;
76 - } 94 +// }
77 - return (Array(len).join(0) + num).slice(-len) 95 +// return (Array(len).join(0) + num).slice(-len)
78 -} 96 +// }
79 -
80 -const minDate = ref()
81 -const maxDate = ref()
82 97
83 onMounted(() => { 98 onMounted(() => {
84 // 根据默认值时间调整显示 99 // 根据默认值时间调整显示
85 - const datetime = props.item.component_props.default ? props.item.component_props.default.split(" ") : props.item.value.split(" "); 100 + popupDesc.value = props.item.component_props.default ? props.item.component_props.default : '请选择';
86 - currentDate.value = datetime[0]?.split("-"); 101 + currentDate.value = new Date(props.item.component_props.default);
87 - currentTime.value = datetime[1]?.split(":");
88 - // YYYY=年,YYYY-MM=年月,YYYY-MM-DD=年月日,YYYY-MM-DD HH=年月日时,YYYY-MM-DD HH:mm=年月日时分,YYYY-MM-DD HH:mm:ss=年月日时分秒
89 - let Year = '';
90 - let Month = '';
91 - let Day = '';
92 - if (!props.item.component_props.default) {
93 - Year = String(dayjs().year());
94 - Month = formatZero(dayjs().month(), 2);
95 - Day = formatZero(dayjs().date(), 2);
96 - } else {
97 - Year = currentDate.value[0];
98 - Month = formatZero(currentDate.value[1], 2);
99 - Day = formatZero(currentDate.value[2], 2);
100 - }
101 - let Hour = ''
102 - let Minute = ''
103 - let Second = ''
104 - if (!props.item.component_props.default) {
105 - Hour = String(dayjs().hour());
106 - Minute = String(dayjs().minute());
107 - Second = String(dayjs().second());
108 - } else {
109 - Hour = currentTime.value[0];
110 - Minute = currentTime.value[1];
111 - Second = currentTime.value[2];
112 - }
113 switch (date_format) { 102 switch (date_format) {
114 - case "YYYY": 103 + // case "YYYY":
115 - columns_date_type.value = ['year'] 104 + // columns_date_type.value = ['year']
116 - // 设置默认值 105 + // // 设置默认值
117 - currentDate.value = [Year]; 106 + // currentDate.value = [Year];
118 - break; 107 + // break;
119 case "YYYY-MM": 108 case "YYYY-MM":
120 - columns_date_type.value = ['year', 'month'] 109 + columns_type.value = 'year-month'
121 - // 设置默认值
122 - currentDate.value = [Year, Month];
123 break; 110 break;
124 case "YYYY-MM-DD": 111 case "YYYY-MM-DD":
125 - columns_date_type.value = ['year', 'month', 'day'] 112 + columns_type.value = 'date'
126 - // 设置默认值
127 - currentDate.value = [Year, Month, Day];
128 - break;
129 - case "YYYY-MM-DD HH":
130 - columns_date_type.value = ['year', 'month', 'day']
131 - columns_time_type.value = ['hour']
132 - // 设置默认值
133 - currentDate.value = [Year, Month, Day];
134 - currentTime.value = [Hour];
135 - break;
136 - case "YYYY-MM-DD HH:mm":
137 - columns_date_type.value = ['year', 'month', 'day']
138 - columns_time_type.value = ['hour', 'minute']
139 - // 设置默认值
140 - currentDate.value = [Year, Month, Day];
141 - currentTime.value = [Hour, Minute];
142 break; 113 break;
114 + // case "YYYY-MM-DD HH":
115 + // columns_date_type.value = ['year', 'month', 'day']
116 + // columns_time_type.value = ['hour']
117 + // // 设置默认值
118 + // currentDate.value = [Year, Month, Day];
119 + // currentTime.value = [Hour];
120 + // break;
121 + // case "YYYY-MM-DD HH:mm":
122 + // columns_date_type.value = ['year', 'month', 'day']
123 + // columns_time_type.value = ['hour', 'minute']
124 + // // 设置默认值
125 + // currentDate.value = [Year, Month, Day];
126 + // currentTime.value = [Hour, Minute];
127 + // break;
143 case "YYYY-MM-DD HH:mm:ss": 128 case "YYYY-MM-DD HH:mm:ss":
144 - columns_date_type.value = ['year', 'month', 'day'] 129 + columns_type.value = 'datetime'
145 - columns_time_type.value = ['hour', 'minute', 'second']
146 - // 设置默认值
147 - currentDate.value = [Year, Month, Day];
148 - currentTime.value = [Hour, Minute, Second];
149 break; 130 break;
150 } 131 }
151 // 设置默认最大最小日期 132 // 设置默认最大最小日期
152 - if (data_minvalue.split(" ")[0].length) { 133 + if (data_minvalue.split(" ")[0]) {
153 const min = data_minvalue.split(" ")[0].split("-") 134 const min = data_minvalue.split(" ")[0].split("-")
154 - minDate.value = new Date(+min[0], +min[1] - 1, +min[2]) 135 + minDate = new Date(+min[0], +min[1] - 1, +min[2])
155 } 136 }
156 - if (data_maxvalue.split(" ")[0].length) { 137 + if (data_maxvalue.split(" ")[0]) {
157 const max = data_maxvalue.split(" ")[0].split("-") 138 const max = data_maxvalue.split(" ")[0].split("-")
158 - maxDate.value = new Date(+max[0], +max[1] - 1, +max[2]) 139 + maxDate = new Date(+max[0], +max[1] - 1, +max[2])
159 } 140 }
160 }); 141 });
161 142
162 const required = props.item.component_props.required; 143 const required = props.item.component_props.required;
163 const data_minvalue = props.item.component_props.data_minvalue; 144 const data_minvalue = props.item.component_props.data_minvalue;
164 const data_maxvalue = props.item.component_props.data_maxvalue; 145 const data_maxvalue = props.item.component_props.data_maxvalue;
165 -const validator = (val) => { 146 +
166 - if (required && !val) { 147 +// 错误提示
167 - return false; 148 +const show_error = ref(false);
168 - } else if (val && data_minvalue && val < data_minvalue) { 149 +const error_msg = ref('');
169 - return false; 150 +// 校验模块
170 - } else if (val && data_maxvalue && val > data_maxvalue) { 151 +const validDateTime = () => {
171 - return false; 152 + // 必填项
153 + if (required && popupDesc.value === '请选择') {
154 + show_error.value = true;
155 + error_msg.value = '必填项不能为空'
156 + } else if (required && popupDesc.value && data_minvalue && popupDesc.value < data_minvalue) {
157 + show_error.value = true;
158 + error_msg.value = "最小可选:" + data_minvalue;
159 + } else if (required && popupDesc.value && data_maxvalue && popupDesc.value > data_maxvalue) {
160 + show_error.value = true;
161 + error_msg.value = "最大可选:" + data_maxvalue;
172 } else { 162 } else {
173 - return true; 163 + show_error.value = false;
164 + error_msg.value = ''
174 } 165 }
166 + return !show_error.value;
175 }; 167 };
176 -// 错误提示文案 168 +
177 -const validatorMessage = (val, rule) => { 169 +defineExpose({ validDateTime, id: props.item.key });
178 - if (required && !val) {
179 - return "必填项不能为空";
180 - } else if (val && data_minvalue && val < data_minvalue) {
181 - return "最小可选:" + data_minvalue;
182 - } else if (val && data_maxvalue && val > data_maxvalue) {
183 - return "最大可选:" + data_maxvalue;
184 - }
185 -};
186 -const rules = [{ validator, message: validatorMessage }];
187 </script> 170 </script>
188 171
189 <style lang="less" scoped> 172 <style lang="less" scoped>
190 .datetime-picker { 173 .datetime-picker {
191 margin: 1rem; 174 margin: 1rem;
192 .label { 175 .label {
193 - // padding: 1rem 1rem 0 1rem; 176 + padding-bottom: 20px;
194 - font-size: 0.9rem; 177 + font-size: 26px;
195 font-weight: bold; 178 font-weight: bold;
196 179
197 - span { 180 + text {
198 color: red; 181 color: red;
199 } 182 }
200 } 183 }
201 - :deep(.van-icon) { // 处理正式服务器上箭头上下位移问题 184 + // :deep(.van-icon) { // 处理正式服务器上箭头上下位移问题
202 - font-size: var(--van-cell-icon-size); 185 + // font-size: var(--van-cell-icon-size);
203 - line-height: var(--van-cell-line-height); 186 + // line-height: var(--van-cell-line-height);
204 - } 187 + // }
205 } 188 }
206 189
207 -:deep(.van-cell--clickable) { 190 +// :deep(.van-cell--clickable) {
208 - border: 1px solid #eaeaea; 191 +// border: 1px solid #eaeaea;
209 - border-radius: 0.25rem; 192 +// border-radius: 0.25rem;
210 - padding: 0.25rem 0.5rem; 193 +// padding: 0.25rem 0.5rem;
211 - margin-top: 0.5rem; 194 +// margin-top: 0.5rem;
212 - input { 195 +// input {
213 - color: #323233; 196 +// color: #323233;
214 - } 197 +// }
215 -} 198 +// }
216 </style> 199 </style>
......
...@@ -7,7 +7,7 @@ import PickerField from '@/components/PickerField/index.vue' ...@@ -7,7 +7,7 @@ import PickerField from '@/components/PickerField/index.vue'
7 import AreaPickerField from '@/components/AreaPickerField/index.vue' 7 import AreaPickerField from '@/components/AreaPickerField/index.vue'
8 import DatePickerField from '@/components/DatePickerField/index.vue' 8 import DatePickerField from '@/components/DatePickerField/index.vue'
9 import TimePickerField from '@/components/TimePickerField/index.vue' 9 import TimePickerField from '@/components/TimePickerField/index.vue'
10 -// import DateTimePickerField from '@/components/DateTimePickerField/index.vue' 10 +import DateTimePickerField from '@/components/DateTimePickerField/index.vue'
11 // import ImageUploaderField from '@/components/ImageUploaderField/index.vue' 11 // import ImageUploaderField from '@/components/ImageUploaderField/index.vue'
12 // import FileUploaderField from '@/components/FileUploaderField/index.vue' 12 // import FileUploaderField from '@/components/FileUploaderField/index.vue'
13 import PhoneField from '@/components/PhoneField/index.vue' 13 import PhoneField from '@/components/PhoneField/index.vue'
...@@ -102,9 +102,9 @@ export function createComponentType(data) { ...@@ -102,9 +102,9 @@ export function createComponentType(data) {
102 if (item.component_props.tag === 'time') { 102 if (item.component_props.tag === 'time') {
103 item.component = TimePickerField 103 item.component = TimePickerField
104 } 104 }
105 - // if (item.component_props.tag === 'datetime') { 105 + if (item.component_props.tag === 'datetime') {
106 - // item.component = DateTimePickerField 106 + item.component = DateTimePickerField
107 - // } 107 + }
108 // if (item.component_props.tag === 'image_uploader') { 108 // if (item.component_props.tag === 'image_uploader') {
109 // item.component = ImageUploaderField 109 // item.component = ImageUploaderField
110 // } 110 // }
......
1 <!-- 1 <!--
2 * @Date: 2023-03-24 09:19:27 2 * @Date: 2023-03-24 09:19:27
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-04-07 16:12:27 4 + * @LastEditTime: 2023-04-07 17:26:07
5 * @FilePath: /custom_form/src/pages/table/index.vue 5 * @FilePath: /custom_form/src/pages/table/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -138,6 +138,7 @@ const email = ref([]); ...@@ -138,6 +138,7 @@ const email = ref([]);
138 const address = ref([]); 138 const address = ref([]);
139 const date = ref([]); 139 const date = ref([]);
140 const time = ref([]); 140 const time = ref([]);
141 +const datetime = ref([]);
141 const area_picker = ref([]); 142 const area_picker = ref([]);
142 const image_uploader = ref([]); 143 const image_uploader = ref([]);
143 const file_uploader = ref([]); 144 const file_uploader = ref([]);
...@@ -182,6 +183,9 @@ const setRefMap = (el, item) => { ...@@ -182,6 +183,9 @@ const setRefMap = (el, item) => {
182 if (item.component_props.tag === "time") { 183 if (item.component_props.tag === "time") {
183 time.value.push(el); 184 time.value.push(el);
184 } 185 }
186 + if (item.component_props.tag === "datetime") {
187 + datetime.value.push(el);
188 + }
185 if (item.component_props.tag === "area_picker") { 189 if (item.component_props.tag === "area_picker") {
186 area_picker.value.push(el); 190 area_picker.value.push(el);
187 } 191 }
...@@ -459,6 +463,9 @@ const onActive = (item) => { ...@@ -459,6 +463,9 @@ const onActive = (item) => {
459 if (item.key === "time") { 463 if (item.key === "time") {
460 postData.value[item.filed_name] = item.value; 464 postData.value[item.filed_name] = item.value;
461 } 465 }
466 + if (item.key === "datetime") {
467 + postData.value[item.filed_name] = item.value;
468 + }
462 if (item.key === "image_uploader") { 469 if (item.key === "image_uploader") {
463 postData.value[item.filed_name] = item.value; 470 postData.value[item.filed_name] = item.value;
464 } 471 }
...@@ -641,6 +648,19 @@ const validOther = () => { ...@@ -641,6 +648,19 @@ const validOther = () => {
641 } 648 }
642 }); 649 });
643 } 650 }
651 + if (datetime.value) {
652 + // 日期时间选择器
653 + datetime.value.forEach((item, index) => {
654 + if (!datetime.value[index].validDateTime()) {
655 + valid = {
656 + status: datetime.value[index].validDateTime(),
657 + key: "datetime",
658 + id: datetime.value[index]?.id
659 + };
660 + return false;
661 + }
662 + });
663 + }
644 if (area_picker.value) { 664 if (area_picker.value) {
645 // 省市区地址 665 // 省市区地址
646 area_picker.value.forEach((item, index) => { 666 area_picker.value.forEach((item, index) => {
......