hookehuyr

✨ feat(单选框): 调整组件样式和功能

...@@ -29,10 +29,14 @@ declare module '@vue/runtime-core' { ...@@ -29,10 +29,14 @@ declare module '@vue/runtime-core' {
29 NutButton: typeof import('@nutui/nutui-taro')['Button'] 29 NutButton: typeof import('@nutui/nutui-taro')['Button']
30 NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider'] 30 NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider']
31 NutDialog: typeof import('@nutui/nutui-taro')['Dialog'] 31 NutDialog: typeof import('@nutui/nutui-taro')['Dialog']
32 + NutField: typeof import('@nutui/nutui-taro')['Field']
32 NutForm: typeof import('@nutui/nutui-taro')['Form'] 33 NutForm: typeof import('@nutui/nutui-taro')['Form']
33 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] 34 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem']
34 NutInput: typeof import('@nutui/nutui-taro')['Input'] 35 NutInput: typeof import('@nutui/nutui-taro')['Input']
35 NutNoticebar: typeof import('@nutui/nutui-taro')['Noticebar'] 36 NutNoticebar: typeof import('@nutui/nutui-taro')['Noticebar']
37 + NutOverlay: typeof import('@nutui/nutui-taro')['Overlay']
38 + NutRadio: typeof import('@nutui/nutui-taro')['Radio']
39 + NutRadioGroup: typeof import('@nutui/nutui-taro')['RadioGroup']
36 NutSwiper: typeof import('@nutui/nutui-taro')['Swiper'] 40 NutSwiper: typeof import('@nutui/nutui-taro')['Swiper']
37 NutSwiperItem: typeof import('@nutui/nutui-taro')['SwiperItem'] 41 NutSwiperItem: typeof import('@nutui/nutui-taro')['SwiperItem']
38 NutTextarea: typeof import('@nutui/nutui-taro')['Textarea'] 42 NutTextarea: typeof import('@nutui/nutui-taro')['Textarea']
......
1 <!-- 1 <!--
2 * @Date: 2022-08-30 11:34:19 2 * @Date: 2022-08-30 11:34:19
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-02-22 11:02:05 4 + * @LastEditTime: 2023-04-03 17:13:20
5 - * @FilePath: /data-table/src/components/RadioField/index.vue 5 + * @FilePath: /custom_form/src/components/RadioField/index.vue
6 * @Description: 单项选择控件 6 * @Description: 单项选择控件
7 --> 7 -->
8 <template> 8 <template>
9 <div v-if="HideShow" class="radio-field-page"> 9 <div v-if="HideShow" class="radio-field-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.note" class="note" v-html="item.component_props.note" /> 14 <div v-if="item.component_props.note" class="note" v-html="item.component_props.note" />
15 - <van-field :rules="item.rules"> 15 + <nut-radio-group @change="onChange(item)" v-model="radio_value" :direction="item.component_props.direction"
16 - <template #input> 16 + style="width: 100%">
17 - <van-radio-group @change="onChange(item)" v-model="radio_value" :direction="item.component_props.direction" 17 + <div v-for="x in item.component_props.options" :key="x.value" class="radio-wrapper">
18 - style="width: 100%"> 18 + <nut-radio :label="x.value" icon-size="16" style="margin-bottom: 0.25rem">{{ x.title }}</nut-radio>
19 - <div v-for="x in item.component_props.options" :key="x.value" class="radio-wrapper"> 19 + <div v-if="x.desc_text" class="van-multi-ellipsis--l3 rule-desc-text">{{ x.desc_text }}</div>
20 - <van-radio :name="x.value" icon-size="1rem" :checked-color="themeVars.radioColor" 20 + <div v-if="x.desc_type === 'text'" class="rule-box" @click="showRule(x)">
21 - style="margin-bottom: 0.25rem">{{ x.title }}</van-radio> 21 + {{ x.desc_btn_name }}&nbsp;>>
22 - <div v-if="x.desc_text" class="van-multi-ellipsis--l3 rule-desc-text">{{ x.desc_text }}</div> 22 + </div>
23 - <div v-if="x.desc_type === 'text'" class="rule-box" @click="showRule(x)"> 23 + <div v-if="x.desc_type === 'url' && h5" class="rule-box" @click="showUrl(x)">
24 - {{ x.desc_btn_name }}&nbsp;>> 24 + {{ x.desc_btn_name }}&nbsp;<Link size="12" />
25 - </div> 25 + </div>
26 - <div v-if="x.desc_type === 'url'" class="rule-box" @click="showUrl(x)"> 26 + <nut-input v-if="radio_value === x.value && x.is_input" @blur="onBlur(x)" v-model="x.affix" label=" " label-width="5px"
27 - {{ x.desc_btn_name }}&nbsp;<van-icon name="link-o" /> 27 + :placeholder="x.input_placeholder" :required="x.input_required" :border="false" class="affix-input" />
28 - </div> 28 + <div
29 - <van-field v-if="radio_value === x.value && x.is_input" @blur="onBlur(x)" v-model="x.affix" label=" " label-width="5px" 29 + v-if="x.show_error"
30 - :placeholder="x.input_placeholder" :rules="x.input_required ? rules : ''" :required="x.input_required" 30 + style="padding: 5px 20px; color: red; font-size: 12px;"
31 - class="affix-input" /> 31 + >
32 + {{ x.error_msg }}
33 + </div>
34 + </div>
35 + </nut-radio-group>
36 + <nut-overlay v-model:visible="show" :lock-scroll="true">
37 + <div class="rule-wrapper">
38 + <div class="rule-block">
39 + <div style="height: 70vh; min-height: 70vh; overflow: scroll; white-space: pre-wrap; line-height: 1.5;" v-html="rule_content"></div>
40 + <div class="close-btn">
41 + <nut-button type="primary" block @click="closeRule"
42 + >关&nbsp;&nbsp;闭</nut-button
43 + >
32 </div> 44 </div>
33 - </van-radio-group> 45 + <div></div>
34 - </template>
35 - </van-field>
36 - </div>
37 -
38 - <van-overlay :show="show" :lock-scroll="false">
39 - <div class="rule-wrapper" @click.stop>
40 - <div class="rule-block">
41 - <div style="height: 70vh; min-height: 70vh; overflow: scroll; white-space: pre-wrap; line-height: 1.5;" v-html="rule_content"></div>
42 - <div class="close-btn">
43 - <van-button type="primary" block @click="closeRule"
44 - >关&nbsp;&nbsp;闭</van-button
45 - >
46 </div> 46 </div>
47 - <div></div>
48 </div> 47 </div>
49 - </div> 48 + </nut-overlay>
50 - </van-overlay> 49 + </div>
51 </template> 50 </template>
52 51
53 <script setup> 52 <script setup>
53 +import { ref, computed, watch, onMounted, reactive } from "vue";
54 import { styleColor } from "@/constant.js"; 54 import { styleColor } from "@/constant.js";
55 -import $ from "jquery"; 55 +import { $ } from '@tarojs/extend';
56 +import { Link } from '@nutui/icons-vue-taro';
56 57
57 const props = defineProps({ 58 const props = defineProps({
58 item: Object, 59 item: Object,
...@@ -67,23 +68,6 @@ const HideShow = computed(() => { ...@@ -67,23 +68,6 @@ const HideShow = computed(() => {
67 return !props.item.component_props.disabled 68 return !props.item.component_props.disabled
68 }) 69 })
69 70
70 -// TODO: 等待数据结构更新,看看怎么判断必填
71 -// 校验函数返回 true 表示校验通过,false 表示不通过
72 -const validator = (val) => {
73 - if (!val) {
74 - return false;
75 - } else {
76 - return true;
77 - }
78 -};
79 -// 错误提示文案
80 -const validatorMessage = (val, rule) => {
81 - if (!val) {
82 - return "补充信息不能为空";
83 - }
84 -};
85 -const rules = [{ validator, message: validatorMessage }];
86 -
87 const emit = defineEmits(["active"]); 71 const emit = defineEmits(["active"]);
88 const radio_value = ref(props.item.component_props.default); 72 const radio_value = ref(props.item.component_props.default);
89 const affix_value = ref(''); 73 const affix_value = ref('');
...@@ -101,16 +85,24 @@ const onBlur = (item) => { ...@@ -101,16 +85,24 @@ const onBlur = (item) => {
101 props.item.value = { key: props.item.key, value: radio_value.value, affix: affix_value.value, type: "radio" }; 85 props.item.value = { key: props.item.key, value: radio_value.value, affix: affix_value.value, type: "radio" };
102 emit("active", props.item.value); 86 emit("active", props.item.value);
103 } 87 }
88 +const options = props.item.component_props.options;
104 const clearAffix = () => { 89 const clearAffix = () => {
105 - const options = props.item.component_props.options;
106 // 为选中项目的补充清空 90 // 为选中项目的补充清空
107 options.forEach(element => { 91 options.forEach(element => {
108 if (element.value !== radio_value.value) { 92 if (element.value !== radio_value.value) {
109 element.affix = '' 93 element.affix = ''
110 } 94 }
95 + element.show_error = false;
96 + element.error_msg = '';
111 }); 97 });
112 } 98 }
113 onMounted(() => { 99 onMounted(() => {
100 + // 新增错误提示标识
101 + options.forEach(element => {
102 + element.show_error = false;
103 + element.error_msg = '';
104 + });
105 + //
114 radio_value.value = props.item.component_props.default ? props.item.component_props.default : ''; 106 radio_value.value = props.item.component_props.default ? props.item.component_props.default : '';
115 // 发送自定义数据结构 107 // 发送自定义数据结构
116 props.item.value = { key: props.item.key, value: radio_value.value, affix: affix_value.value, type: "radio" }; 108 props.item.value = { key: props.item.key, value: radio_value.value, affix: affix_value.value, type: "radio" };
...@@ -130,26 +122,56 @@ const closeRule = () => { ...@@ -130,26 +122,56 @@ const closeRule = () => {
130 show.value = false; 122 show.value = false;
131 rule_content.value = ""; 123 rule_content.value = "";
132 }; 124 };
133 -const showUrl = (rule) => { 125 +const h5 = computed(() => {
134 - location.href = rule.desc_url 126 + if (process.env.TARO_ENV === 'h5') {
127 + return true
128 + } else {
129 + return false;
130 + }
131 +})
132 +const showUrl = (rule) => { // 跳转设置链接
133 + if (process.env.TARO_ENV === 'h5') {
134 + location.href = rule.desc_url
135 + }
136 + // 小程序需要配置制定域名内网页,功能无法实现。
135 } 137 }
136 const rule_content = ref(""); 138 const rule_content = ref("");
139 +
140 +const show_error = ref(false);
141 +// 校验模块
142 +const validRadio = () => {
143 + // 必填项
144 + props.item.component_props.options.some(item => {
145 + if (radio_value.value === item.value && item.is_input && item.input_required && !item.affix) {
146 + show_error.value = true;
147 + item.show_error = true;
148 + item.error_msg = '补充信息不能为空';
149 + return true;
150 + } else {
151 + show_error.value = false;
152 + item.show_error = false;
153 + item.error_msg = '';
154 + }
155 + });
156 + return !show_error.value;
157 +};
158 +
159 +defineExpose({ validRadio });
137 </script> 160 </script>
138 161
139 -<style lang="less" scoped> 162 +<style lang="less">
140 .radio-field-page { 163 .radio-field-page {
141 .label { 164 .label {
142 - padding: 1rem 1rem 0 1rem; 165 + padding: 30px 30px 0 30px;
143 - font-size: 0.9rem; 166 + font-size: 26px;
144 font-weight: bold; 167 font-weight: bold;
145 - 168 + text {
146 - span {
147 color: red; 169 color: red;
148 } 170 }
149 } 171 }
150 172
151 .note { 173 .note {
152 - font-size: 0.9rem; 174 + font-size: 30px;
153 margin-left: 1rem; 175 margin-left: 1rem;
154 color: gray; 176 color: gray;
155 padding-bottom: 0.5rem; 177 padding-bottom: 0.5rem;
...@@ -159,8 +181,9 @@ const rule_content = ref(""); ...@@ -159,8 +181,9 @@ const rule_content = ref("");
159 .radio-wrapper { 181 .radio-wrapper {
160 border: 1px solid #eaeaea; 182 border: 1px solid #eaeaea;
161 border-radius: 0.25rem; 183 border-radius: 0.25rem;
162 - padding: 0.25rem 0.5rem; 184 + padding: 25px;
163 - margin-bottom: 0.25rem; 185 + margin: 25px;
186 + margin-top: 0;
164 } 187 }
165 .affix-input { 188 .affix-input {
166 border: 1px solid #eaeaea; 189 border: 1px solid #eaeaea;
...@@ -170,13 +193,13 @@ const rule_content = ref(""); ...@@ -170,13 +193,13 @@ const rule_content = ref("");
170 margin-bottom: 0.25rem; 193 margin-bottom: 0.25rem;
171 } 194 }
172 .rule-desc-text { 195 .rule-desc-text {
173 - margin: 0.35rem 0.5rem 0.5rem 1.75rem; 196 + margin: 0.35rem 0.5rem 0.5rem 60px;
174 - font-size: 0.8rem; 197 + font-size: 28px;
175 color: #808080; 198 color: #808080;
176 } 199 }
177 .rule-box { 200 .rule-box {
178 - font-size: 0.85rem; 201 + font-size: 28px;
179 - margin-left: 1.8rem; 202 + margin-left: 60px;
180 padding-bottom: 0.5rem; 203 padding-bottom: 0.5rem;
181 width: fit-content; 204 width: fit-content;
182 } 205 }
...@@ -184,18 +207,22 @@ const rule_content = ref(""); ...@@ -184,18 +207,22 @@ const rule_content = ref("");
184 207
185 .rule-wrapper { 208 .rule-wrapper {
186 display: flex; 209 display: flex;
210 + height: 100%;
187 align-items: center; 211 align-items: center;
188 justify-content: center; 212 justify-content: center;
189 - height: 100%;
190 } 213 }
191 214
192 .rule-block { 215 .rule-block {
193 - position: relative; 216 + // position: relative;
194 width: 80vw; 217 width: 80vw;
195 height: 80vh; 218 height: 80vh;
196 background-color: #fff; 219 background-color: #fff;
197 overflow: scroll; 220 overflow: scroll;
198 padding: 1rem; 221 padding: 1rem;
222 + display: flex;
223 + border-radius: 8px;
224 + align-items: center;
225 + justify-content: center;
199 .close-btn { 226 .close-btn {
200 position: absolute; 227 position: absolute;
201 bottom: 1rem; 228 bottom: 1rem;
...@@ -216,4 +243,8 @@ const rule_content = ref(""); ...@@ -216,4 +243,8 @@ const rule_content = ref("");
216 // border-radius: 0.25rem; 243 // border-radius: 0.25rem;
217 // padding: 0.25rem 0.5rem; 244 // padding: 0.25rem 0.5rem;
218 } 245 }
246 +
247 +.nut-radio__label {
248 + margin-left: 25px;
249 +}
219 </style> 250 </style>
......
1 import _ from '@/utils/lodash' 1 import _ from '@/utils/lodash'
2 import TextField from '@/components/TextField/index.vue' 2 import TextField from '@/components/TextField/index.vue'
3 import TextareaField from '@/components/TextareaField/index.vue' 3 import TextareaField from '@/components/TextareaField/index.vue'
4 -// import RadioField from '@/components/RadioField/index.vue' 4 +import RadioField from '@/components/RadioField/index.vue'
5 // import CheckboxField from '@/components/CheckboxField/index.vue' 5 // import CheckboxField from '@/components/CheckboxField/index.vue'
6 // import PickerField from '@/components/PickerField/index.vue' 6 // import PickerField from '@/components/PickerField/index.vue'
7 // import AreaPickerField from '@/components/AreaPickerField/index.vue' 7 // import AreaPickerField from '@/components/AreaPickerField/index.vue'
...@@ -84,9 +84,9 @@ export function createComponentType(data) { ...@@ -84,9 +84,9 @@ export function createComponentType(data) {
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 + }
90 // if (item.component_props.tag === 'checkbox') { 90 // if (item.component_props.tag === 'checkbox') {
91 // item.component = CheckboxField 91 // item.component = CheckboxField
92 // } 92 // }
......
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-03 13:32:11 4 + * @LastEditTime: 2023-04-03 16:52:30
5 * @FilePath: /custom_form/src/pages/table/index.vue 5 * @FilePath: /custom_form/src/pages/table/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -128,6 +128,7 @@ const formatData = (data) => { ...@@ -128,6 +128,7 @@ const formatData = (data) => {
128 // 省市区选择,图片上传,文件上传,电子签名,评分组件 128 // 省市区选择,图片上传,文件上传,电子签名,评分组件
129 const input = ref([]); 129 const input = ref([]);
130 const textarea = ref([]); 130 const textarea = ref([]);
131 +const radio = ref([]);
131 const area_picker = ref([]); 132 const area_picker = ref([]);
132 const image_uploader = ref([]); 133 const image_uploader = ref([]);
133 const file_uploader = ref([]); 134 const file_uploader = ref([]);
...@@ -142,6 +143,9 @@ const setRefMap = (el, item) => { ...@@ -142,6 +143,9 @@ const setRefMap = (el, item) => {
142 if (item.component_props.tag === "textarea") { 143 if (item.component_props.tag === "textarea") {
143 textarea.value.push(el); 144 textarea.value.push(el);
144 } 145 }
146 + if (item.component_props.tag === "radio") {
147 + radio.value.push(el);
148 + }
145 if (item.component_props.tag === "area_picker") { 149 if (item.component_props.tag === "area_picker") {
146 area_picker.value.push(el); 150 area_picker.value.push(el);
147 } 151 }
...@@ -371,6 +375,9 @@ const onActive = (item) => { ...@@ -371,6 +375,9 @@ const onActive = (item) => {
371 if (item.key === "textarea") { 375 if (item.key === "textarea") {
372 postData.value[item.filed_name] = item.value; 376 postData.value[item.filed_name] = item.value;
373 } 377 }
378 + if (item.type === "radio") { // 单选控件
379 + postData.value = Object.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value });
380 + }
374 if (item.key === "area_picker") { 381 if (item.key === "area_picker") {
375 postData.value[item.filed_name] = item.value; 382 postData.value[item.filed_name] = item.value;
376 } 383 }
...@@ -389,9 +396,6 @@ const onActive = (item) => { ...@@ -389,9 +396,6 @@ const onActive = (item) => {
389 if (item.type === "picker") { // 下拉框控件 396 if (item.type === "picker") { // 下拉框控件
390 postData.value = _.assign(postData.value, { [item.key]: item.value }); 397 postData.value = _.assign(postData.value, { [item.key]: item.value });
391 } 398 }
392 - if (item.type === "radio") { // 单选控件
393 - postData.value = _.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value });
394 - }
395 if (item.type === "checkbox") { // 多选控件 399 if (item.type === "checkbox") { // 多选控件
396 const checkbox_value = _.cloneDeep(item.value) 400 const checkbox_value = _.cloneDeep(item.value)
397 checkbox_value.forEach((element, index) => { 401 checkbox_value.forEach((element, index) => {
...@@ -437,6 +441,18 @@ const validOther = () => { ...@@ -437,6 +441,18 @@ const validOther = () => {
437 } 441 }
438 }); 442 });
439 } 443 }
444 + if (radio.value) {
445 + // 单选框
446 + radio.value.forEach((item, index) => {
447 + if (!radio.value[index].validRadio()) {
448 + valid = {
449 + status: radio.value[index].validRadio(),
450 + key: "radio",
451 + };
452 + return false;
453 + }
454 + });
455 + }
440 if (area_picker.value) { 456 if (area_picker.value) {
441 // 省市区地址 457 // 省市区地址
442 area_picker.value.forEach((item, index) => { 458 area_picker.value.forEach((item, index) => {
......