hookehuyr

✨ feat: 新增表单预约时间控件

...@@ -7,11 +7,14 @@ export {} ...@@ -7,11 +7,14 @@ export {}
7 7
8 declare module '@vue/runtime-core' { 8 declare module '@vue/runtime-core' {
9 export interface GlobalComponents { 9 export interface GlobalComponents {
10 + Appointment: typeof import('./src/components/Appointment/index.vue')['default']
11 + AppointmentField: typeof import('./src/components/AppointmentField/index.vue')['default']
10 AreaPickerField: typeof import('./src/components/AreaPickerField/index.vue')['default'] 12 AreaPickerField: typeof import('./src/components/AreaPickerField/index.vue')['default']
11 ButtonField: typeof import('./src/components/ButtonField/index.vue')['default'] 13 ButtonField: typeof import('./src/components/ButtonField/index.vue')['default']
12 CalendarField: typeof import('./src/components/CalendarField/index.vue')['default'] 14 CalendarField: typeof import('./src/components/CalendarField/index.vue')['default']
13 CheckboxField: typeof import('./src/components/CheckboxField/index.vue')['default'] 15 CheckboxField: typeof import('./src/components/CheckboxField/index.vue')['default']
14 ContactField: typeof import('./src/components/ContactField/index.vue')['default'] 16 ContactField: typeof import('./src/components/ContactField/index.vue')['default']
17 + copy: typeof import('./src/components/DesField copy/index.vue')['default']
15 DatePickerField: typeof import('./src/components/DatePickerField/index.vue')['default'] 18 DatePickerField: typeof import('./src/components/DatePickerField/index.vue')['default']
16 DateTimePickerField: typeof import('./src/components/DateTimePickerField/index.vue')['default'] 19 DateTimePickerField: typeof import('./src/components/DateTimePickerField/index.vue')['default']
17 DesField: typeof import('./src/components/DesField/index.vue')['default'] 20 DesField: typeof import('./src/components/DesField/index.vue')['default']
...@@ -48,11 +51,13 @@ declare module '@vue/runtime-core' { ...@@ -48,11 +51,13 @@ declare module '@vue/runtime-core' {
48 VanCol: typeof import('vant/es')['Col'] 51 VanCol: typeof import('vant/es')['Col']
49 VanConfigProvider: typeof import('vant/es')['ConfigProvider'] 52 VanConfigProvider: typeof import('vant/es')['ConfigProvider']
50 VanDatePicker: typeof import('vant/es')['DatePicker'] 53 VanDatePicker: typeof import('vant/es')['DatePicker']
54 + VanDialog: typeof import('vant/es')['Dialog']
51 VanDivider: typeof import('vant/es')['Divider'] 55 VanDivider: typeof import('vant/es')['Divider']
52 VanField: typeof import('vant/es')['Field'] 56 VanField: typeof import('vant/es')['Field']
53 VanForm: typeof import('vant/es')['Form'] 57 VanForm: typeof import('vant/es')['Form']
54 VanIcon: typeof import('vant/es')['Icon'] 58 VanIcon: typeof import('vant/es')['Icon']
55 VanImage: typeof import('vant/es')['Image'] 59 VanImage: typeof import('vant/es')['Image']
60 + VanInput: typeof import('vant/es')['Input']
56 VanLoading: typeof import('vant/es')['Loading'] 61 VanLoading: typeof import('vant/es')['Loading']
57 VanNoticeBar: typeof import('vant/es')['NoticeBar'] 62 VanNoticeBar: typeof import('vant/es')['NoticeBar']
58 VanNumberKeyboard: typeof import('vant/es')['NumberKeyboard'] 63 VanNumberKeyboard: typeof import('vant/es')['NumberKeyboard']
......
1 +<!--
2 + * @Date: 2022-08-29 14:31:20
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2023-03-27 17:52:50
5 + * @FilePath: /data-table/src/components/AppointmentField/index.vue
6 + * @Description: 描述文本控件
7 +-->
8 +<template>
9 + <div class="Appointment-field-page">
10 + <div class="label">
11 + <span v-if="item.component_props.required">&nbsp;*</span>
12 + {{ item.component_props.label }}
13 + </div>
14 + <van-row v-for="(opt, index) in item.component_props.options" :key="index" style="padding: 0 1rem 0rem 1rem;">
15 + <van-col span="6">
16 + <div style="font-size: 1rem; line-height: 3;">{{ opt.title }}</div>
17 + </van-col>
18 + <van-col span="18">
19 + <van-field
20 + v-model="opt.value"
21 + is-link
22 + readonly
23 + :placeholder="opt.placeholder"
24 + @click="onClick(index, opt)"
25 + :border="false"
26 + style="width: 100%;"
27 + />
28 + </van-col>
29 + </van-row>
30 + <div
31 + v-if="show_empty"
32 + class="van-field__error-message"
33 + style="padding: 0 1rem 1rem 1rem"
34 + >
35 + 预约时间不能为空
36 + </div>
37 + </div>
38 + <van-popup v-model:show="showPicker" position="bottom">
39 + <van-picker
40 + :title="item.component_props.appointment_title"
41 + :columns="columns"
42 + @confirm="onConfirm"
43 + @cancel="showPicker = false"
44 + />
45 + </van-popup>
46 +</template>
47 +
48 +<script setup>
49 +import { styleColor } from "@/constant.js";
50 +import { showDialog } from 'vant';
51 +
52 +const props = defineProps({
53 + item: Object,
54 +});
55 +const emit = defineEmits(["active"]);
56 +const showPicker = ref(false);
57 +const picker_value = ref(props.item.component_props.default);
58 +const columns = ref([])
59 +const current_index = ref(0)
60 +const show_empty = ref(false);
61 +
62 +const onClick = (index, opt) => {
63 + if (opt.disabled) {
64 + showDialog({
65 + title: '温馨提示',
66 + message: '暂时不能预约!',
67 + theme: 'round-button',
68 + width: '320px',
69 + confirmButtonColor: styleColor.baseColor
70 + }).then(() => {
71 + // on close
72 + });
73 + return false;
74 + }
75 + current_index.value = index;
76 + columns.value = props.item.component_props.options[index]['columns'];
77 + showPicker.value = true
78 +}
79 +
80 +const onConfirm = ({ selectedOptions }) => {
81 + props.item.component_props.options.forEach(item => {
82 + item.value = ''
83 + })
84 + props.item.component_props.options[current_index.value]['value'] = selectedOptions[0]?.text;
85 + picker_value.value = selectedOptions[0]?.value;
86 + showPicker.value = false;
87 + // 触发点自定义监听事件,配合规则显示隐藏其他字段
88 + props.item.value = { key: props.item.key, value: picker_value.value, type: "appointment" };
89 + emit("active", props.item.value);
90 +};
91 +// 隐藏显示
92 +const HideShow = computed(() => {
93 + return !props.item.component_props.disabled
94 +})
95 +
96 +const validAppointment = () => {
97 + // 必填项
98 + if (props.item.component_props.required && !picker_value.value) {
99 + show_empty.value = true;
100 + } else {
101 + show_empty.value = false;
102 + }
103 + return !show_empty.value;
104 +};
105 +
106 +defineExpose({ validAppointment });
107 +</script>
108 +
109 +<style lang="less" scoped>
110 +.Appointment-field-page {
111 + .label {
112 + padding: 1rem 1rem 0 1rem;
113 + font-size: 0.9rem;
114 + font-weight: bold;
115 + span {
116 + color: red;
117 + }
118 + }
119 +}
120 +
121 +// :deep(.van-field__body) {
122 +// border: 1px solid #eaeaea;
123 +// border-radius: 0.25rem;
124 +// padding: 0.25rem 0.5rem;
125 +// }
126 +:deep(.van-cell--clickable) {
127 + border: 1px solid #eaeaea;
128 + border-radius: 0.25rem;
129 + padding: 0.25rem 0.5rem;
130 + margin-top: 0.5rem;
131 +}
132 +</style>
1 <!-- 1 <!--
2 * @Date: 2022-08-30 13:46:51 2 * @Date: 2022-08-30 13:46:51
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-03-03 21:11:24 4 + * @LastEditTime: 2023-03-27 16:55:35
5 * @FilePath: /data-table/src/components/PickerField/index.vue 5 * @FilePath: /data-table/src/components/PickerField/index.vue
6 * @Description: 单列选择器组件 6 * @Description: 单列选择器组件
7 --> 7 -->
......
1 <!-- 1 <!--
2 * @Date: 2022-09-08 15:47:54 2 * @Date: 2022-09-08 15:47:54
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-01-31 22:57:40 4 + * @LastEditTime: 2023-03-27 17:14:53
5 * @FilePath: /data-table/src/components/RatePickerField/index.vue 5 * @FilePath: /data-table/src/components/RatePickerField/index.vue
6 * @Description: 评分选择控件 6 * @Description: 评分选择控件
7 --> 7 -->
......
...@@ -29,6 +29,7 @@ import ButtonField from '@/components/ButtonField/index.vue' ...@@ -29,6 +29,7 @@ import ButtonField from '@/components/ButtonField/index.vue'
29 import NoteField from '@/components/NoteField/index.vue'; 29 import NoteField from '@/components/NoteField/index.vue';
30 import NameField from '@/components/NameField/index.vue'; 30 import NameField from '@/components/NameField/index.vue';
31 import GenderField from '@/components/GenderField/index.vue'; 31 import GenderField from '@/components/GenderField/index.vue';
32 +import AppointmentField from '@/components/AppointmentField/index.vue';
32 33
33 /** 34 /**
34 * 生成自定义组件类型 35 * 生成自定义组件类型
...@@ -58,6 +59,7 @@ import GenderField from '@/components/GenderField/index.vue'; ...@@ -58,6 +59,7 @@ import GenderField from '@/components/GenderField/index.vue';
58 * @type note 富文本控件 NoteField 59 * @type note 富文本控件 NoteField
59 * @type name 姓名控件 NameField 60 * @type name 姓名控件 NameField
60 * @type gender 性别控件 GenderField 61 * @type gender 性别控件 GenderField
62 + * @type appointment 预约控件 AppointmentField
61 */ 63 */
62 export function createComponentType(data) { 64 export function createComponentType(data) {
63 // 判断类型和使用组件 65 // 判断类型和使用组件
...@@ -178,5 +180,9 @@ export function createComponentType(data) { ...@@ -178,5 +180,9 @@ export function createComponentType(data) {
178 item.name = item.key; 180 item.name = item.key;
179 item.component = GenderField; 181 item.component = GenderField;
180 } 182 }
183 + if (item.component_props.tag === 'appointment') {
184 + item.name = item.key;
185 + item.component = AppointmentField;
186 + }
181 }) 187 })
182 } 188 }
......
1 <!-- 1 <!--
2 * @Date: 2022-07-18 10:22:22 2 * @Date: 2022-07-18 10:22:22
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-03-14 14:41:17 4 + * @LastEditTime: 2023-03-27 18:30:02
5 * @FilePath: /data-table/src/views/index.vue 5 * @FilePath: /data-table/src/views/index.vue
6 * @Description: 首页 6 * @Description: 首页
7 --> 7 -->
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
26 <van-cell-group :border="false"> 26 <van-cell-group :border="false">
27 <component v-for="(item, index) in formData" :id="item.key" :ref="(el) => setRefMap(el, item)" :key="index" 27 <component v-for="(item, index) in formData" :id="item.key" :ref="(el) => setRefMap(el, item)" :key="index"
28 :is="item.component" :item="item" @active="onActive" /> 28 :is="item.component" :item="item" @active="onActive" />
29 - </van-cell-group> 29 + </van-cell-group>
30 - <div v-if="formData.length && PCommit.visible" style="margin: 16px"> 30 + <div v-if="formData.length && PCommit.visible" style="margin: 16px">
31 - <van-button round block type="primary" native-type="submit"> 31 + <van-button round block type="primary" native-type="submit">
32 - {{ PCommit.text ? PCommit.text : '提交' }} 32 + {{ PCommit.text ? PCommit.text : '提交' }}
33 - </van-button> 33 + </van-button>
34 - </div> 34 + </div>
35 - <!-- <van-cell-group :border="false"> 35 + <!-- <van-cell-group :border="false">
36 <component 36 <component
37 v-for="(item, index) in mockData" 37 v-for="(item, index) in mockData"
38 :id="item.key" 38 :id="item.key"
...@@ -42,12 +42,12 @@ ...@@ -42,12 +42,12 @@
42 :item="item" 42 :item="item"
43 @active="onActive" 43 @active="onActive"
44 /> 44 />
45 - </van-cell-group> 45 + </van-cell-group>
46 - <div v-if="mockData.length && PCommit.visible" style="margin: 16px"> 46 + <div v-if="mockData.length && PCommit.visible" style="margin: 16px">
47 - <van-button round block type="primary" native-type="submit"> 47 + <van-button round block type="primary" native-type="submit">
48 - {{ PCommit.text ? PCommit.text : '提交' }} 48 + {{ PCommit.text ? PCommit.text : '提交' }}
49 - </van-button> 49 + </van-button>
50 - </div> --> 50 + </div> -->
51 </van-form> 51 </van-form>
52 </van-config-provider> 52 </van-config-provider>
53 </div> 53 </div>
...@@ -159,6 +159,7 @@ const image_uploader = ref([]); ...@@ -159,6 +159,7 @@ const image_uploader = ref([]);
159 const file_uploader = ref([]); 159 const file_uploader = ref([]);
160 const sign = ref([]); 160 const sign = ref([]);
161 const rate_picker = ref([]); 161 const rate_picker = ref([]);
162 +const appointment = ref([]);
162 // 动态绑定ref数据 163 // 动态绑定ref数据
163 const setRefMap = (el, item) => { 164 const setRefMap = (el, item) => {
164 if (el) { 165 if (el) {
...@@ -177,6 +178,9 @@ const setRefMap = (el, item) => { ...@@ -177,6 +178,9 @@ const setRefMap = (el, item) => {
177 if (item.component_props.tag === "rate_picker") { 178 if (item.component_props.tag === "rate_picker") {
178 rate_picker.value.push(el); 179 rate_picker.value.push(el);
179 } 180 }
181 + if (item.component_props.tag === "appointment") {
182 + appointment.value.push(el);
183 + }
180 } 184 }
181 }; 185 };
182 186
...@@ -248,26 +252,66 @@ onMounted(async () => { ...@@ -248,26 +252,66 @@ onMounted(async () => {
248 formData.value = formatData(page_form); 252 formData.value = formatData(page_form);
249 mockData.value = [ 253 mockData.value = [
250 { 254 {
251 - key: "111", 255 + key: "field_1",
252 - value: "",
253 - component: "",
254 - component_props: {
255 - name: "name",
256 - tag: "name",
257 - label: "姓名",
258 - required: true,
259 - },
260 - },
261 - {
262 - key: "222",
263 value: "", 256 value: "",
264 component: "", 257 component: "",
265 component_props: { 258 component_props: {
266 - name: "gender", 259 + name: "appointment",
267 - tag: "gender", 260 + tag: "appointment",
268 - label: "性别", 261 + label: "预约时间",
269 - default: '', 262 + default: '',
270 required: true, 263 required: true,
264 + placeholder: '',
265 + appointment_title: '选择入寺时间',
266 + options: [{
267 + title: '03月27日',
268 + placeholder: '剩余余量 9878',
269 + value: '',
270 + disabled: false,
271 + columns: [{
272 + checked : false,
273 + disabled : false,
274 + value: '1',
275 + text : "16:00-17:30 余1399",
276 + }, {
277 + checked : false,
278 + disabled : true,
279 + value: '2',
280 + text : "18:00-19:30 余1399",
281 + }]
282 + }, {
283 + title: '03月28日',
284 + placeholder: '剩余余量 9878',
285 + value: '',
286 + disabled: false,
287 + columns: [{
288 + checked : false,
289 + disabled : true,
290 + value: '3',
291 + text : "16:00-17:30 余1399",
292 + }, {
293 + checked : false,
294 + disabled : false,
295 + value: '4',
296 + text : "16:00-17:30 余1399",
297 + }]
298 + }, {
299 + title: '03月28日',
300 + placeholder: '等待预约(提前一天)',
301 + value: '',
302 + disabled: true,
303 + columns: [{
304 + checked : false,
305 + disabled : true,
306 + value: '5',
307 + text : "12:00-13:30 余1399",
308 + }, {
309 + checked : false,
310 + disabled : false,
311 + value: '6',
312 + text : "14:00-15:30 余1399",
313 + }]
314 + }]
271 }, 315 },
272 }, 316 },
273 ]; 317 ];
...@@ -404,6 +448,9 @@ const onActive = (item) => { ...@@ -404,6 +448,9 @@ const onActive = (item) => {
404 if (item.type === "picker") { // 下拉框控件 448 if (item.type === "picker") { // 下拉框控件
405 postData.value = _.assign(postData.value, { [item.key]: item.value }); 449 postData.value = _.assign(postData.value, { [item.key]: item.value });
406 } 450 }
451 + if (item.type === "appointment") { // 预约控件
452 + postData.value = _.assign(postData.value, { [item.key]: item.value });
453 + }
407 if (item.type === "radio") { // 单选控件 454 if (item.type === "radio") { // 单选控件
408 postData.value = _.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value }); 455 postData.value = _.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value });
409 } 456 }
...@@ -488,6 +535,18 @@ const validOther = () => { ...@@ -488,6 +535,18 @@ const validOther = () => {
488 } 535 }
489 }); 536 });
490 } 537 }
538 + if (appointment.value) {
539 + // 预约时间控件
540 + appointment.value.forEach((item, index) => {
541 + if (!appointment.value[index].validAppointment()) {
542 + valid = {
543 + status: appointment.value[index].validAppointment(),
544 + key: "appointment",
545 + };
546 + return false;
547 + }
548 + });
549 + }
491 return valid; 550 return valid;
492 }; 551 };
493 552
......