hookehuyr

✨ feat(数字录入控件): 样式和功能调整

...@@ -35,6 +35,7 @@ declare module '@vue/runtime-core' { ...@@ -35,6 +35,7 @@ declare module '@vue/runtime-core' {
35 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] 35 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem']
36 NutInput: typeof import('@nutui/nutui-taro')['Input'] 36 NutInput: typeof import('@nutui/nutui-taro')['Input']
37 NutNoticebar: typeof import('@nutui/nutui-taro')['Noticebar'] 37 NutNoticebar: typeof import('@nutui/nutui-taro')['Noticebar']
38 + NutNumberKeyboard: typeof import('@nutui/nutui-taro')['NumberKeyboard']
38 NutOverlay: typeof import('@nutui/nutui-taro')['Overlay'] 39 NutOverlay: typeof import('@nutui/nutui-taro')['Overlay']
39 NutPicker: typeof import('@nutui/nutui-taro')['Picker'] 40 NutPicker: typeof import('@nutui/nutui-taro')['Picker']
40 NutPopup: typeof import('@nutui/nutui-taro')['Popup'] 41 NutPopup: typeof import('@nutui/nutui-taro')['Popup']
......
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-02-09 15:54:02 4 + * @LastEditTime: 2023-04-06 15:51:54
5 - * @FilePath: /data-table/src/components/NumberField/index.vue 5 + * @FilePath: /custom_form/src/components/NumberField/index.vue
6 * @Description: 数字输入框 6 * @Description: 数字输入框
7 --> 7 -->
8 <template> 8 <template>
9 <div v-if="HideShow" class="number-page"> 9 <div v-if="HideShow" class="number-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 14 <div
...@@ -16,44 +16,52 @@ ...@@ -16,44 +16,52 @@
16 v-html="item.component_props.note" 16 v-html="item.component_props.note"
17 style="font-size: 0.9rem; margin-left: 1rem; color: gray; padding-bottom: 0.5rem; padding-top: 0.25rem; white-space: pre-wrap;" 17 style="font-size: 0.9rem; margin-left: 1rem; color: gray; padding-bottom: 0.5rem; padding-top: 0.25rem; white-space: pre-wrap;"
18 /> 18 />
19 - <van-field 19 + <nut-input
20 - v-model="item.value" 20 + ref="inputRef"
21 - :id="item.name" 21 + v-model="input_value"
22 - :name="item.name" 22 + :id="item.name + '_input'"
23 + :label="item.name"
23 :placeholder="item.component_props.placeholder" 24 :placeholder="item.component_props.placeholder"
24 - :rules="rules"
25 - :required="item.component_props.required"
26 :disabled="item.component_props.readonly" 25 :disabled="item.component_props.readonly"
27 readonly 26 readonly
28 - @touchstart.stop="showKeyboard($event)" 27 + @click-input="showKeyboard($event)"
29 :border="false" 28 :border="false"
29 + style="border: 1px solid #eaeaea; border-radius: 0.25rem; padding: 0.25rem 0.5rem;"
30 > 30 >
31 - </van-field> 31 + </nut-input>
32 - <van-number-keyboard 32 + <div
33 - v-model="item.value" 33 + v-if="show_error"
34 - :show="showInteger" 34 + style="padding-left: 20px; color: red; font-size: 12px;"
35 - @blur="blurKeyboard()" 35 + >
36 + {{ error_msg }}
37 + </div>
38 + <nut-number-keyboard
39 + v-model="input_value"
40 + v-model:visible="showInteger"
41 + :overlay="true"
42 + @close="blurKeyboard"
36 @input="onInput" 43 @input="onInput"
37 @delete="onDelete" 44 @delete="onDelete"
38 - safe-area-inset-bottom
39 /> 45 />
40 - <van-number-keyboard 46 + <nut-number-keyboard
41 - v-model="item.value" 47 + v-model="input_value"
42 - :show="showDecimal" 48 + v-model:visible="showDecimal"
43 - theme="custom" 49 + type="rightColumn"
44 - extra-key="." 50 + :custom-key="customKey"
45 close-button-text="完成" 51 close-button-text="完成"
46 - @blur="blurKeyboard()" 52 + @close="blurKeyboard"
47 @input="onInput" 53 @input="onInput"
48 @delete="onDelete" 54 @delete="onDelete"
49 - safe-area-inset-bottom
50 /> 55 />
51 </div> 56 </div>
52 </template> 57 </template>
53 58
54 <script setup> 59 <script setup>
55 -import $ from "jquery"; 60 +import Taro, { useLoad } from '@tarojs/taro'
56 -import { storeToRefs, mainStore } from "@/utils/generatePackage"; 61 +import { ref, computed, watch, onMounted, nextTick } from "vue";
62 +import { $ } from '@tarojs/extend';
63 +import { storeToRefs } from "pinia";
64 +import { mainStore } from '@/stores'
57 65
58 const props = defineProps({ 66 const props = defineProps({
59 item: Object, 67 item: Object,
...@@ -63,9 +71,13 @@ const HideShow = computed(() => { ...@@ -63,9 +71,13 @@ const HideShow = computed(() => {
63 return !props.item.component_props.disabled 71 return !props.item.component_props.disabled
64 }) 72 })
65 let content = ""; 73 let content = "";
74 +const customKey = ref(['.']);
75 +const input_value = ref('');
76 +const inputRef = ref(null)
66 77
67 const store = mainStore(); 78 const store = mainStore();
68 const { fieldName } = storeToRefs(store); 79 const { fieldName } = storeToRefs(store);
80 +const emit = defineEmits(["active"]);
69 81
70 // 监听字段变化 82 // 监听字段变化
71 watch( 83 watch(
...@@ -75,14 +87,18 @@ watch( ...@@ -75,14 +87,18 @@ watch(
75 if (v !== props.item.name) { 87 if (v !== props.item.name) {
76 // 还原border颜色 88 // 还原border颜色
77 $(`#${props.item.name}`).parent().css("border-color", "#eaeaea"); 89 $(`#${props.item.name}`).parent().css("border-color", "#eaeaea");
78 - if (props.item.component_props.max_fraction_count === 0) { 90 + if (props.item.component_props.max_fraction_count > 0) {
91 + // 显示小数键盘
92 + showDecimal.value = false;
93 + } else {
79 // 显示整数键盘 94 // 显示整数键盘
80 showInteger.value = false; 95 showInteger.value = false;
96 + }
97 + if (process.env.TARO_ENV === 'h5') {
98 + $('#app').css('padding-bottom', '0')
81 } else { 99 } else {
82 - // 显示小数键盘 100 + $('.table-page').css('padding-bottom', '0')
83 - showDecimal.value = false;
84 } 101 }
85 - document.getElementById("app").style.paddingBottom = "0";
86 } 102 }
87 } 103 }
88 ); 104 );
...@@ -90,15 +106,27 @@ const readonly = props.item.component_props.readonly; ...@@ -90,15 +106,27 @@ const readonly = props.item.component_props.readonly;
90 const showKeyboard = (e) => { 106 const showKeyboard = (e) => {
91 if (readonly) return false; // 如果为只读,不能设置 107 if (readonly) return false; // 如果为只读,不能设置
92 // 键盘上移动 108 // 键盘上移动
93 - const target_to_view_height = 109 + const windowHeight = Taro.getSystemInfoSync().windowHeight; // 可使用窗口高度
94 - window.innerHeight - e.target.getBoundingClientRect().bottom; // 元素到适口高度 110 + Taro.createSelectorQuery().select(`#${props.item.name}`).boundingClientRect(async (res) => {
95 - const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度 111 + const target_to_view_height = windowHeight - res.bottom; // 元素到视口高度
96 - let scroll_height = ""; 112 + // const target_top = document.body.scrollHeight - $(e.target).offset().top; // 元素到正文高度
97 - if (target_to_view_height <= 250) { 113 + // let scroll_height = "";
98 - document.getElementById("app").style.paddingBottom = "250px"; 114 + if (target_to_view_height <= 250) {
99 - // 向上滚动位置 115 + if (process.env.TARO_ENV === 'h5') {
100 - document.documentElement.scrollTop = $(e.target).offset().top - 244; 116 + $('#app').css('padding-bottom', '250px')
101 - } 117 + } else {
118 + $('.table-page').css('padding-bottom', '250px')
119 + }
120 + // 向上滚动位置
121 + setTimeout(() => {
122 + Taro.pageScrollTo({
123 + scrollTop: res.top,
124 + duration: 300
125 + })
126 + }, 500);
127 + }
128 + }).exec()
129 +
102 // if (target_top < 250) { 130 // if (target_top < 250) {
103 // window.scrollTo(0, $("#app").height()); 131 // window.scrollTo(0, $("#app").height());
104 // } else { 132 // } else {
...@@ -106,32 +134,44 @@ const showKeyboard = (e) => { ...@@ -106,32 +134,44 @@ const showKeyboard = (e) => {
106 // document.documentElement.scrollTop = (target_top > 500 ? 0 : target_top) + 250; 134 // document.documentElement.scrollTop = (target_top > 500 ? 0 : target_top) + 250;
107 // } 135 // }
108 // 选中添加border颜色 136 // 选中添加border颜色
109 - content = $(e.target).parent(); 137 + content = $(`#${props.item.name}_input`);
110 // TAG: 自定义主题颜色 138 // TAG: 自定义主题颜色
111 content.css("border-color", "#c2915f"); 139 content.css("border-color", "#c2915f");
112 setTimeout(() => { 140 setTimeout(() => {
113 - if (props.item.component_props.max_fraction_count === 0) { 141 + if (props.item.component_props.max_fraction_count > 0) {
114 - // 显示整数键盘
115 - showInteger.value = true;
116 - } else {
117 // 显示小数键盘 142 // 显示小数键盘
118 showDecimal.value = true; 143 showDecimal.value = true;
144 + } else {
145 + // 显示整数键盘
146 + showInteger.value = true;
119 } 147 }
120 }, 300); 148 }, 300);
121 // 记录点击field名 149 // 记录点击field名
122 store.changeFieldName(props.item.name); 150 store.changeFieldName(props.item.name);
123 }; 151 };
124 const blurKeyboard = () => { 152 const blurKeyboard = () => {
125 - if (props.item.component_props.max_fraction_count === 0) { 153 + if (props.item.component_props.max_fraction_count > 0) {
154 + // 显示小数键盘
155 + showDecimal.value = false;
156 + } else {
126 // 显示整数键盘 157 // 显示整数键盘
127 showInteger.value = false; 158 showInteger.value = false;
159 + }
160 + if (process.env.TARO_ENV === 'h5') {
161 + $('#app').css('padding-bottom', '0')
128 } else { 162 } else {
129 - // 显示小数键盘 163 + $('.table-page').css('padding-bottom', '0')
130 - showDecimal.value = false;
131 } 164 }
132 - document.getElementById("app").style.paddingBottom = "0";
133 // 还原border颜色 165 // 还原border颜色
134 content.css("border-color", "#eaeaea"); 166 content.css("border-color", "#eaeaea");
167 + // 发送自定义回调数字
168 + props.item.value = {
169 + key: "number",
170 + filed_name: props.item.key,
171 + value: input_value.value,
172 + };
173 + emit("active", props.item.value);
174 + validNumber()
135 }; 175 };
136 176
137 const showDecimal = ref(false); 177 const showDecimal = ref(false);
...@@ -143,59 +183,64 @@ const min = props.item.component_props.min; ...@@ -143,59 +183,64 @@ const min = props.item.component_props.min;
143 const max = props.item.component_props.max; 183 const max = props.item.component_props.max;
144 const max_count = props.item.component_props.max_fraction_count; // 保留小数个数 null=不限,0=没有小数,大于0=最多只能输入的小数个数 184 const max_count = props.item.component_props.max_fraction_count; // 保留小数个数 null=不限,0=没有小数,大于0=最多只能输入的小数个数
145 const reg = new RegExp("^([0-9]+)(\\.(\\d){" + max_count +"," + max_count +"})$", "g"); 185 const reg = new RegExp("^([0-9]+)(\\.(\\d){" + max_count +"," + max_count +"})$", "g");
146 -const validator = (val) => { 186 +
147 - if (required && !val) { // 必填 187 +const show_error = ref(false);
148 - return false; 188 +const error_msg = ref('');
149 - } else if (val && min && (val < min)) { // 小于最小值 189 +// 校验模块
150 - return false; 190 +const validNumber = () => {
151 - } else if (val && max && (val > max)) { // 大于最大值 191 + // 必填项
152 - return false; 192 + if (required && !input_value.value) { // 必填
153 - } else if (val && max_count && !reg.test(val)) { // 不符合保留小数个数 193 + show_error.value = true;
154 - return false; 194 + error_msg.value = '必填项不能为空';
195 + } else if (input_value.value && min && (input_value.value < min)) { // 小于最小值
196 + show_error.value = true;
197 + error_msg.value = `最小值为 ${min}`;
198 + } else if (input_value.value && max && (input_value.value > max)) { // 大于最大值
199 + show_error.value = true;
200 + error_msg.value = `最大值为 ${max}`;
201 + } else if (input_value.value && max_count && !reg.test(input_value.value)) { // 不符合保留小数个数
202 + show_error.value = true;
203 + error_msg.value = `保留小数点后 ${max_count} 位`;
155 } else { 204 } else {
156 - return true; 205 + show_error.value = false;
157 - } 206 + error_msg.value = '';
158 -};
159 -// 错误提示文案
160 -const validatorMessage = (val, rule) => {
161 - if (required && !val) {
162 - return "必填项不能为空";
163 - } else if (val && min && (val < min)) { // 小于最小值
164 - return "最小值为" + min;
165 - } else if (val && max && (val > max)) { // 大于最大值
166 - return "最大值为" + max;
167 - } else if (val && max_count && !reg.test(val)) { // 不符合保留小数个数
168 - return "保留小数点后" + max_count + "位";
169 } 207 }
208 + return !show_error.value;
170 }; 209 };
171 -const rules = [{ validator, message: validatorMessage }];
172 210
173 -const onInput = (value) => {}; 211 +defineExpose({ validNumber, id: props.item.key });
212 +
213 +const onInput = (value) => {
214 +};
174 const onDelete = () => {}; 215 const onDelete = () => {};
175 </script> 216 </script>
176 217
177 -<style lang="less" scoped> 218 +<style lang="less">
178 .number-page { 219 .number-page {
179 .label { 220 .label {
180 - padding: 1rem 1rem 0 1rem; 221 + padding: 30px 30px 0 30px;
181 - font-size: 0.9rem; 222 + font-size: 26px;
182 font-weight: bold; 223 font-weight: bold;
183 - span { 224 + text {
184 color: red; 225 color: red;
185 } 226 }
186 } 227 }
187 -}
188 228
189 -:deep(.van-field__body) { 229 + .nut-input {
190 - border: 1px solid #eaeaea; 230 + padding: 20px 25px;
191 - border-radius: 0.25rem;
192 - padding: 0.25rem 0.5rem;
193 - input {
194 - color: #323233;
195 } 231 }
196 } 232 }
197 233
198 -:deep(.van-number-keyboard__title) { 234 +// :deep(.van-field__body) {
199 - font-size: 1.05rem; 235 +// border: 1px solid #eaeaea;
200 -} 236 +// border-radius: 0.25rem;
237 +// padding: 0.25rem 0.5rem;
238 +// input {
239 +// color: #323233;
240 +// }
241 +// }
242 +
243 +// :deep(.van-number-keyboard__title) {
244 +// font-size: 1.05rem;
245 +// }
201 </style> 246 </style>
......
...@@ -16,7 +16,7 @@ import PickerField from '@/components/PickerField/index.vue' ...@@ -16,7 +16,7 @@ import PickerField from '@/components/PickerField/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'
22 // import VideoField from '@/components/VideoField/index.vue' 22 // import VideoField from '@/components/VideoField/index.vue'
...@@ -80,10 +80,10 @@ export function createComponentType(data) { ...@@ -80,10 +80,10 @@ export function createComponentType(data) {
80 item.name = item.key 80 item.name = item.key
81 item.component = TextareaField 81 item.component = TextareaField
82 } 82 }
83 - // if (item.component_props.tag === 'number') { 83 + if (item.component_props.tag === 'number') {
84 - // item.name = item.key 84 + item.name = item.key
85 - // item.component = NumberField 85 + item.component = NumberField
86 - // } 86 + }
87 if (item.component_props.tag === 'radio') { 87 if (item.component_props.tag === 'radio') {
88 item.component = RadioField 88 item.component = RadioField
89 } 89 }
......
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-06 13:37:04 4 + * @LastEditTime: 2023-04-06 14:35:26
5 * @FilePath: /custom_form/src/pages/table/index.vue 5 * @FilePath: /custom_form/src/pages/table/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -132,6 +132,7 @@ const radio = ref([]); ...@@ -132,6 +132,7 @@ const radio = ref([]);
132 const checkbox = ref([]); 132 const checkbox = ref([]);
133 const multi_rule = ref([]); 133 const multi_rule = ref([]);
134 const picker = ref([]); 134 const picker = ref([]);
135 +const number = ref([]);
135 const area_picker = ref([]); 136 const area_picker = ref([]);
136 const image_uploader = ref([]); 137 const image_uploader = ref([]);
137 const file_uploader = ref([]); 138 const file_uploader = ref([]);
...@@ -158,6 +159,9 @@ const setRefMap = (el, item) => { ...@@ -158,6 +159,9 @@ const setRefMap = (el, item) => {
158 if (item.component_props.tag === "select") { 159 if (item.component_props.tag === "select") {
159 picker.value.push(el); 160 picker.value.push(el);
160 } 161 }
162 + if (item.component_props.tag === "number") {
163 + number.value.push(el);
164 + }
161 if (item.component_props.tag === "area_picker") { 165 if (item.component_props.tag === "area_picker") {
162 area_picker.value.push(el); 166 area_picker.value.push(el);
163 } 167 }
...@@ -417,6 +421,9 @@ const onActive = (item) => { ...@@ -417,6 +421,9 @@ const onActive = (item) => {
417 if (item.key === "area_picker") { 421 if (item.key === "area_picker") {
418 postData.value[item.filed_name] = item.value; 422 postData.value[item.filed_name] = item.value;
419 } 423 }
424 + if (item.key === "number") {
425 + postData.value[item.filed_name] = item.value;
426 + }
420 if (item.key === "image_uploader") { 427 if (item.key === "image_uploader") {
421 postData.value[item.filed_name] = item.value; 428 postData.value[item.filed_name] = item.value;
422 } 429 }
...@@ -521,6 +528,19 @@ const validOther = () => { ...@@ -521,6 +528,19 @@ const validOther = () => {
521 } 528 }
522 }); 529 });
523 } 530 }
531 + if (number.value) {
532 + // 下拉框
533 + number.value.forEach((item, index) => {
534 + if (!number.value[index].validNumber()) {
535 + valid = {
536 + status: number.value[index].validNumber(),
537 + key: "number",
538 + id: number.value[index]?.id
539 + };
540 + return false;
541 + }
542 + });
543 + }
524 if (area_picker.value) { 544 if (area_picker.value) {
525 // 省市区地址 545 // 省市区地址
526 area_picker.value.forEach((item, index) => { 546 area_picker.value.forEach((item, index) => {
......