hookehuyr

✨ feat(身份证控件): 样式和功能调整

...@@ -35,7 +35,6 @@ declare module '@vue/runtime-core' { ...@@ -35,7 +35,6 @@ declare module '@vue/runtime-core' {
35 NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker'] 35 NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker']
36 NutDialog: typeof import('@nutui/nutui-taro')['Dialog'] 36 NutDialog: typeof import('@nutui/nutui-taro')['Dialog']
37 NutDivider: typeof import('@nutui/nutui-taro')['Divider'] 37 NutDivider: typeof import('@nutui/nutui-taro')['Divider']
38 - NutField: typeof import('@nutui/nutui-taro')['Field']
39 NutForm: typeof import('@nutui/nutui-taro')['Form'] 38 NutForm: typeof import('@nutui/nutui-taro')['Form']
40 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] 39 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem']
41 NutInput: typeof import('@nutui/nutui-taro')['Input'] 40 NutInput: typeof import('@nutui/nutui-taro')['Input']
......
1 <!-- 1 <!--
2 * @Date: 2022-09-14 14:44:30 2 * @Date: 2022-09-14 14:44:30
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-04-07 09:30:49 4 + * @LastEditTime: 2023-04-10 15:09:13
5 * @FilePath: /custom_form/src/components/IdentityField/index.vue 5 * @FilePath: /custom_form/src/components/IdentityField/index.vue
6 * @Description: 身份证输入控件 6 * @Description: 身份证输入控件
7 --> 7 -->
8 <template> 8 <template>
9 <div v-if="HideShow" class="identity-page"> 9 <div v-if="HideShow" class="identity-page">
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 <!-- <div v-if="item.component_props.readonly" style="padding: 0.5rem 1rem;">{{ item.value }}</div> --> 14 <!-- <div v-if="item.component_props.readonly" style="padding: 0.5rem 1rem;">{{ item.value }}</div> -->
...@@ -27,20 +27,34 @@ ...@@ -27,20 +27,34 @@
27 :border="false" 27 :border="false"
28 > 28 >
29 </van-field> --> 29 </van-field> -->
30 - <van-field ref="fieldRef" v-model="item.value" :id="item.name" :name="item.name" 30 + <nut-form-item>
31 - :placeholder="item.component_props.placeholder" :rules="rules" :required="item.component_props.required" 31 + <nut-input
32 - :disabled="item.component_props.readonly" :border="false"> 32 + ref="fieldRef"
33 - </van-field> 33 + v-model="input_value"
34 + :placeholder="item.component_props.placeholder"
35 + :disabled="item.component_props.readonly"
36 + :border="false"
37 + @blur="onBlur"
38 + style="border: 1px solid #eaeaea; border-radius: 0.25rem; padding: 0.25rem 0.5rem;">
39 + </nut-input>
40 + <div
41 + v-if="show_error"
42 + style="padding: 5px; color: red; font-size: 12px;"
43 + >
44 + {{ error_msg }}
45 + </div>
46 + </nut-form-item>
34 <!-- <div v-if="gender" class="gender"><span>性别:</span>{{ gender }}</div> --> 47 <!-- <div v-if="gender" class="gender"><span>性别:</span>{{ gender }}</div> -->
35 - <van-number-keyboard v-model="item.value" :show="show" extra-key="X" close-button-text="完成" @blur="blurKeyboard()" 48 + <!-- <van-number-keyboard v-model="item.value" :show="show" extra-key="X" close-button-text="完成" @blur="blurKeyboard()"
36 - @input="onInput" @delete="onDelete" safe-area-inset-bottom /> 49 + @input="onInput" @delete="onDelete" safe-area-inset-bottom /> -->
37 </div> 50 </div>
38 </template> 51 </template>
39 52
40 <script setup> 53 <script setup>
41 -import $ from "jquery"; 54 +import { ref, computed, watch, onMounted, reactive } from "vue";
42 -import { storeToRefs, mainStore } from "@/utils/generatePackage"; 55 +// import $ from "jquery";
43 -import { showSuccessToast, showFailToast } from "vant"; 56 +// import { storeToRefs, mainStore } from "@/utils/generatePackage";
57 +// import { showSuccessToast, showFailToast } from "vant";
44 58
45 59
46 const props = defineProps({ 60 const props = defineProps({
...@@ -50,105 +64,119 @@ const props = defineProps({ ...@@ -50,105 +64,119 @@ const props = defineProps({
50 const HideShow = computed(() => { 64 const HideShow = computed(() => {
51 return !props.item.component_props.disabled 65 return !props.item.component_props.disabled
52 }) 66 })
67 +
68 +const emit = defineEmits(["active"]);
69 +const input_value = ref(props.item.component_props.default);
53 const show = ref(false); 70 const show = ref(false);
54 let content = ""; 71 let content = "";
55 72
56 -const store = mainStore(); 73 +// const store = mainStore();
57 -const { fieldName } = storeToRefs(store); 74 +// const { fieldName } = storeToRefs(store);
58 - 75 +
59 -// 监听字段变化 76 +// // 监听字段变化
60 -watch( 77 +// watch(
61 - () => fieldName.value, 78 +// () => fieldName.value,
62 - (v) => { 79 +// (v) => {
63 - // 如果不是点击本输入框 80 +// // 如果不是点击本输入框
64 - if (v !== props.item.name) { 81 +// if (v !== props.item.name) {
65 - // 还原border颜色 82 +// // 还原border颜色
66 - $(`#${props.item.name}`).parent().css("border-color", "#eaeaea"); 83 +// $(`#${props.item.name}`).parent().css("border-color", "#eaeaea");
67 - show.value = false; 84 +// show.value = false;
68 - document.getElementById("app").style.paddingBottom = "0"; 85 +// document.getElementById("app").style.paddingBottom = "0";
69 - } 86 +// }
70 - } 87 +// }
71 -); 88 +// );
72 const readonly = props.item.component_props.readonly; 89 const readonly = props.item.component_props.readonly;
73 const fieldRef = ref(null); 90 const fieldRef = ref(null);
74 const edit_mode = ref(false); 91 const edit_mode = ref(false);
75 -const clickRightIcon = () => { // 编辑模式 92 +// const clickRightIcon = () => { // 编辑模式
76 - edit_mode.value = true; 93 +// edit_mode.value = true;
77 - nextTick(() => { 94 +// nextTick(() => {
78 - fieldRef.value?.focus(); 95 +// fieldRef.value?.focus();
79 - }) 96 +// })
80 -} 97 +// }
81 -const openKeyboard = (e) => { 98 +// const openKeyboard = (e) => {
82 - if (readonly || e.target.className.indexOf('edit') > 0 || edit_mode.value) return false; // 如果为只读或者编辑模式,不能设置 99 +// if (readonly || e.target.className.indexOf('edit') > 0 || edit_mode.value) return false; // 如果为只读或者编辑模式,不能设置
83 - // // 键盘上移动 100 +// // // 键盘上移动
84 - // const target_to_view_height = window.innerHeight - e.target.getBoundingClientRect().y; // 元素到适口高度 101 +// // const target_to_view_height = window.innerHeight - e.target.getBoundingClientRect().y; // 元素到适口高度
85 - // const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度 102 +// // const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度
86 - // let scroll_height = ""; 103 +// // let scroll_height = "";
87 - // console.warn(target_top); 104 +// // console.warn(target_top);
88 - // if (target_top < 250) { 105 +// // if (target_top < 250) {
89 - // document.getElementById("app").style.paddingBottom = "250px"; 106 +// // document.getElementById("app").style.paddingBottom = "250px";
90 - // window.scrollTo(0, $("#app").height()); 107 +// // window.scrollTo(0, $("#app").height());
91 - // } else { 108 +// // } else {
92 - // // 向上滚动位置 109 +// // // 向上滚动位置
93 - // document.documentElement.scrollTop = (target_top > 250 ? 0 : target_top) + 250; 110 +// // document.documentElement.scrollTop = (target_top > 250 ? 0 : target_top) + 250;
94 - // } 111 +// // }
95 - // 键盘上移动 112 +// // 键盘上移动
96 - const target_to_view_height = 113 +// const target_to_view_height =
97 - window.innerHeight - e.target.getBoundingClientRect().bottom; // 元素到适口高度 114 +// window.innerHeight - e.target.getBoundingClientRect().bottom; // 元素到适口高度
98 - const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度 115 +// const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度
99 - let scroll_height = ""; 116 +// let scroll_height = "";
100 - if (target_to_view_height <= 250) { 117 +// if (target_to_view_height <= 250) {
101 - document.getElementById("app").style.paddingBottom = "250px"; 118 +// document.getElementById("app").style.paddingBottom = "250px";
102 - // 向上滚动位置 119 +// // 向上滚动位置
103 - document.documentElement.scrollTop = $(e.target).offset().top - 244; 120 +// document.documentElement.scrollTop = $(e.target).offset().top - 244;
104 - } 121 +// }
105 - // 选中添加border颜色 122 +// // 选中添加border颜色
106 - content = $(e.target).parent(); 123 +// content = $(e.target).parent();
107 - // TAG: 自定义主题颜色 124 +// // TAG: 自定义主题颜色
108 - content.css("border-color", "#c2915f"); 125 +// content.css("border-color", "#c2915f");
109 - setTimeout(() => { 126 +// setTimeout(() => {
110 - show.value = true; 127 +// show.value = true;
111 - }, 300); 128 +// }, 300);
112 - // 记录点击field名 129 +// // 记录点击field名
113 - store.changeFieldName(props.item.name); 130 +// store.changeFieldName(props.item.name);
114 -}; 131 +// };
115 -const blurKeyboard = () => { 132 +// const blurKeyboard = () => {
116 - show.value = false; 133 +// show.value = false;
117 - document.getElementById("app").style.paddingBottom = "0"; 134 +// document.getElementById("app").style.paddingBottom = "0";
118 - // 还原border颜色 135 +// // 还原border颜色
119 - content.css("border-color", "#eaeaea"); 136 +// content.css("border-color", "#eaeaea");
120 - // 键盘失焦检查输入和添加性别显示 137 +// // 键盘失焦检查输入和添加性别显示
121 - const input_val = props.item.value; 138 +// const input_val = props.item.value;
122 - if (required && !input_val) { 139 +// if (required && !input_val) {
123 - showFailToast("身份证号码不能为空"); 140 +// showFailToast("身份证号码不能为空");
124 - } else if (input_val && !/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(input_val)) { 141 +// } else if (input_val && !/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(input_val)) {
125 - showFailToast("请输入正确身份证号码"); 142 +// showFailToast("请输入正确身份证号码");
126 - } else { 143 +// } else {
127 - // gender.value = getGenderByIdNumber(input_val) 144 +// // gender.value = getGenderByIdNumber(input_val)
128 - } 145 +// }
129 -}; 146 +// };
147 +
148 +// 错误提示
149 +const show_error = ref(false);
150 +const error_msg = ref('');
130 151
131 -// 校验函数返回 true 表示校验通过,false 表示不通过 152 +// 校验模块
132 // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X 153 // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X
133 -const required = props.item.component_props.required; 154 +const validIdCard = () => {
134 -const validator = (val) => { 155 + // 必填项
135 - if (required && !val) { 156 + if (props.item.component_props.required && !input_value.value) {
136 - return false; 157 + show_error.value = true;
137 - } else if (val && !/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(val)) { 158 + error_msg.value = '必填项不能为空'
138 - return false; 159 + } else if (input_value.value && !/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(input_value.value)) {
160 + show_error.value = true;
161 + error_msg.value = '请输入正确身份证号码'
139 } else { 162 } else {
140 - return true; 163 + show_error.value = false;
141 - } 164 + error_msg.value = ''
142 -};
143 -// 错误提示文案
144 -const validatorMessage = (val, rule) => {
145 - if (required && !val) {
146 - return "身份证号码不能为空";
147 - } else if (val && !/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(val)) {
148 - return "请输入正确身份证号码";
149 } 165 }
166 + return !show_error.value;
150 }; 167 };
151 -const rules = [{ validator, message: validatorMessage }]; 168 +
169 +defineExpose({ validIdCard, id: props.item.key });
170 +
171 +const onBlur = () => {
172 + validIdCard()
173 + props.item.value = {
174 + key: "id_card",
175 + filed_name: props.item.key,
176 + value: input_value.value,
177 + };
178 + emit("active", props.item.value);
179 +}
152 180
153 const onInput = (value) => { }; 181 const onInput = (value) => { };
154 const onDelete = () => { }; 182 const onDelete = () => { };
...@@ -179,14 +207,14 @@ const getGenderByIdNumber = (idNumber) => { ...@@ -179,14 +207,14 @@ const getGenderByIdNumber = (idNumber) => {
179 } 207 }
180 </script> 208 </script>
181 209
182 -<style lang="less" scoped> 210 +<style lang="less">
183 .identity-page { 211 .identity-page {
184 .label { 212 .label {
185 - padding: 1rem 1rem 0 1rem; 213 + padding: 0 30px 0 45px;
186 - font-size: 0.9rem; 214 + font-size: 26px;
187 font-weight: bold; 215 font-weight: bold;
188 216
189 - span { 217 + text {
190 color: red; 218 color: red;
191 } 219 }
192 } 220 }
......
...@@ -15,7 +15,7 @@ import EmailField from '@/components/EmailField/index.vue' ...@@ -15,7 +15,7 @@ import EmailField from '@/components/EmailField/index.vue'
15 // import SignField from '@/components/SignField/index.vue' 15 // import SignField from '@/components/SignField/index.vue'
16 // import RatePickerField from '@/components/RatePickerField/index.vue' 16 // import RatePickerField from '@/components/RatePickerField/index.vue'
17 // import CalendarField from '@/components/CalendarField/index.vue' 17 // import CalendarField from '@/components/CalendarField/index.vue'
18 -// import IdentityField from '@/components/IdentityField/index.vue' 18 +import IdentityField from '@/components/IdentityField/index.vue'
19 import NumberField from '@/components/NumberField/index.vue' 19 import NumberField from '@/components/NumberField/index.vue'
20 // import DesField from '@/components/DesField/index.vue' 20 // import DesField from '@/components/DesField/index.vue'
21 // import DividerField from '@/components/DividerField/index.vue' 21 // import DividerField from '@/components/DividerField/index.vue'
...@@ -131,10 +131,10 @@ export function createComponentType(data) { ...@@ -131,10 +131,10 @@ export function createComponentType(data) {
131 // item.name = item.key 131 // item.name = item.key
132 // item.component = CalendarField 132 // item.component = CalendarField
133 // } 133 // }
134 - // if (item.component_props.tag === 'id_card') { 134 + if (item.component_props.tag === 'id_card') {
135 - // item.name = item.key 135 + item.name = item.key
136 - // item.component = IdentityField 136 + item.component = IdentityField
137 - // } 137 + }
138 // if (item.component_props.tag === 'desc') { 138 // if (item.component_props.tag === 'desc') {
139 // item.name = item.key 139 // item.name = item.key
140 // item.component = DesField 140 // item.component = DesField
......
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 17:26:07 4 + * @LastEditTime: 2023-04-10 14:38:57
5 * @FilePath: /custom_form/src/pages/table/index.vue 5 * @FilePath: /custom_form/src/pages/table/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -139,6 +139,7 @@ const address = ref([]); ...@@ -139,6 +139,7 @@ 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 datetime = ref([]);
142 +const id_card = ref([]);
142 const area_picker = ref([]); 143 const area_picker = ref([]);
143 const image_uploader = ref([]); 144 const image_uploader = ref([]);
144 const file_uploader = ref([]); 145 const file_uploader = ref([]);
...@@ -186,6 +187,9 @@ const setRefMap = (el, item) => { ...@@ -186,6 +187,9 @@ const setRefMap = (el, item) => {
186 if (item.component_props.tag === "datetime") { 187 if (item.component_props.tag === "datetime") {
187 datetime.value.push(el); 188 datetime.value.push(el);
188 } 189 }
190 + if (item.component_props.tag === "id_card") {
191 + id_card.value.push(el);
192 + }
189 if (item.component_props.tag === "area_picker") { 193 if (item.component_props.tag === "area_picker") {
190 area_picker.value.push(el); 194 area_picker.value.push(el);
191 } 195 }
...@@ -466,6 +470,9 @@ const onActive = (item) => { ...@@ -466,6 +470,9 @@ const onActive = (item) => {
466 if (item.key === "datetime") { 470 if (item.key === "datetime") {
467 postData.value[item.filed_name] = item.value; 471 postData.value[item.filed_name] = item.value;
468 } 472 }
473 + if (item.key === "id_card") {
474 + postData.value[item.filed_name] = item.value;
475 + }
469 if (item.key === "image_uploader") { 476 if (item.key === "image_uploader") {
470 postData.value[item.filed_name] = item.value; 477 postData.value[item.filed_name] = item.value;
471 } 478 }
...@@ -661,6 +668,19 @@ const validOther = () => { ...@@ -661,6 +668,19 @@ const validOther = () => {
661 } 668 }
662 }); 669 });
663 } 670 }
671 + if (id_card.value) {
672 + // 身份证控件
673 + id_card.value.forEach((item, index) => {
674 + if (!id_card.value[index].validIdCard()) {
675 + valid = {
676 + status: id_card.value[index].validIdCard(),
677 + key: "id_card",
678 + id: id_card.value[index]?.id
679 + };
680 + return false;
681 + }
682 + });
683 + }
664 if (area_picker.value) { 684 if (area_picker.value) {
665 // 省市区地址 685 // 省市区地址
666 area_picker.value.forEach((item, index) => { 686 area_picker.value.forEach((item, index) => {
......