hookehuyr

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

......@@ -29,10 +29,14 @@ declare module '@vue/runtime-core' {
NutButton: typeof import('@nutui/nutui-taro')['Button']
NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider']
NutDialog: typeof import('@nutui/nutui-taro')['Dialog']
NutField: typeof import('@nutui/nutui-taro')['Field']
NutForm: typeof import('@nutui/nutui-taro')['Form']
NutFormItem: typeof import('@nutui/nutui-taro')['FormItem']
NutInput: typeof import('@nutui/nutui-taro')['Input']
NutNoticebar: typeof import('@nutui/nutui-taro')['Noticebar']
NutOverlay: typeof import('@nutui/nutui-taro')['Overlay']
NutRadio: typeof import('@nutui/nutui-taro')['Radio']
NutRadioGroup: typeof import('@nutui/nutui-taro')['RadioGroup']
NutSwiper: typeof import('@nutui/nutui-taro')['Swiper']
NutSwiperItem: typeof import('@nutui/nutui-taro')['SwiperItem']
NutTextarea: typeof import('@nutui/nutui-taro')['Textarea']
......
<!--
* @Date: 2022-08-30 11:34:19
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-02-22 11:02:05
* @FilePath: /data-table/src/components/RadioField/index.vue
* @LastEditTime: 2023-04-03 17:13:20
* @FilePath: /custom_form/src/components/RadioField/index.vue
* @Description: 单项选择控件
-->
<template>
<div v-if="HideShow" class="radio-field-page">
<div class="label">
<span v-if="item.component_props.required">&nbsp;*</span>
<text v-if="item.component_props.required">&nbsp;*</text>
{{ item.component_props.label }}
</div>
<div v-if="item.component_props.note" class="note" v-html="item.component_props.note" />
<van-field :rules="item.rules">
<template #input>
<van-radio-group @change="onChange(item)" v-model="radio_value" :direction="item.component_props.direction"
style="width: 100%">
<div v-for="x in item.component_props.options" :key="x.value" class="radio-wrapper">
<van-radio :name="x.value" icon-size="1rem" :checked-color="themeVars.radioColor"
style="margin-bottom: 0.25rem">{{ x.title }}</van-radio>
<div v-if="x.desc_text" class="van-multi-ellipsis--l3 rule-desc-text">{{ x.desc_text }}</div>
<div v-if="x.desc_type === 'text'" class="rule-box" @click="showRule(x)">
{{ x.desc_btn_name }}&nbsp;>>
</div>
<div v-if="x.desc_type === 'url'" class="rule-box" @click="showUrl(x)">
{{ x.desc_btn_name }}&nbsp;<van-icon name="link-o" />
</div>
<van-field v-if="radio_value === x.value && x.is_input" @blur="onBlur(x)" v-model="x.affix" label=" " label-width="5px"
:placeholder="x.input_placeholder" :rules="x.input_required ? rules : ''" :required="x.input_required"
class="affix-input" />
<nut-radio-group @change="onChange(item)" v-model="radio_value" :direction="item.component_props.direction"
style="width: 100%">
<div v-for="x in item.component_props.options" :key="x.value" class="radio-wrapper">
<nut-radio :label="x.value" icon-size="16" style="margin-bottom: 0.25rem">{{ x.title }}</nut-radio>
<div v-if="x.desc_text" class="van-multi-ellipsis--l3 rule-desc-text">{{ x.desc_text }}</div>
<div v-if="x.desc_type === 'text'" class="rule-box" @click="showRule(x)">
{{ x.desc_btn_name }}&nbsp;>>
</div>
<div v-if="x.desc_type === 'url' && h5" class="rule-box" @click="showUrl(x)">
{{ x.desc_btn_name }}&nbsp;<Link size="12" />
</div>
<nut-input v-if="radio_value === x.value && x.is_input" @blur="onBlur(x)" v-model="x.affix" label=" " label-width="5px"
:placeholder="x.input_placeholder" :required="x.input_required" :border="false" class="affix-input" />
<div
v-if="x.show_error"
style="padding: 5px 20px; color: red; font-size: 12px;"
>
{{ x.error_msg }}
</div>
</div>
</nut-radio-group>
<nut-overlay v-model:visible="show" :lock-scroll="true">
<div class="rule-wrapper">
<div class="rule-block">
<div style="height: 70vh; min-height: 70vh; overflow: scroll; white-space: pre-wrap; line-height: 1.5;" v-html="rule_content"></div>
<div class="close-btn">
<nut-button type="primary" block @click="closeRule"
>关&nbsp;&nbsp;闭</nut-button
>
</div>
</van-radio-group>
</template>
</van-field>
</div>
<van-overlay :show="show" :lock-scroll="false">
<div class="rule-wrapper" @click.stop>
<div class="rule-block">
<div style="height: 70vh; min-height: 70vh; overflow: scroll; white-space: pre-wrap; line-height: 1.5;" v-html="rule_content"></div>
<div class="close-btn">
<van-button type="primary" block @click="closeRule"
>关&nbsp;&nbsp;闭</van-button
>
<div></div>
</div>
<div></div>
</div>
</div>
</van-overlay>
</nut-overlay>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted, reactive } from "vue";
import { styleColor } from "@/constant.js";
import $ from "jquery";
import { $ } from '@tarojs/extend';
import { Link } from '@nutui/icons-vue-taro';
const props = defineProps({
item: Object,
......@@ -67,23 +68,6 @@ const HideShow = computed(() => {
return !props.item.component_props.disabled
})
// TODO: 等待数据结构更新,看看怎么判断必填
// 校验函数返回 true 表示校验通过,false 表示不通过
const validator = (val) => {
if (!val) {
return false;
} else {
return true;
}
};
// 错误提示文案
const validatorMessage = (val, rule) => {
if (!val) {
return "补充信息不能为空";
}
};
const rules = [{ validator, message: validatorMessage }];
const emit = defineEmits(["active"]);
const radio_value = ref(props.item.component_props.default);
const affix_value = ref('');
......@@ -101,16 +85,24 @@ const onBlur = (item) => {
props.item.value = { key: props.item.key, value: radio_value.value, affix: affix_value.value, type: "radio" };
emit("active", props.item.value);
}
const options = props.item.component_props.options;
const clearAffix = () => {
const options = props.item.component_props.options;
// 为选中项目的补充清空
options.forEach(element => {
if (element.value !== radio_value.value) {
element.affix = ''
}
element.show_error = false;
element.error_msg = '';
});
}
onMounted(() => {
// 新增错误提示标识
options.forEach(element => {
element.show_error = false;
element.error_msg = '';
});
//
radio_value.value = props.item.component_props.default ? props.item.component_props.default : '';
// 发送自定义数据结构
props.item.value = { key: props.item.key, value: radio_value.value, affix: affix_value.value, type: "radio" };
......@@ -130,26 +122,56 @@ const closeRule = () => {
show.value = false;
rule_content.value = "";
};
const showUrl = (rule) => {
location.href = rule.desc_url
const h5 = computed(() => {
if (process.env.TARO_ENV === 'h5') {
return true
} else {
return false;
}
})
const showUrl = (rule) => { // 跳转设置链接
if (process.env.TARO_ENV === 'h5') {
location.href = rule.desc_url
}
// 小程序需要配置制定域名内网页,功能无法实现。
}
const rule_content = ref("");
const show_error = ref(false);
// 校验模块
const validRadio = () => {
// 必填项
props.item.component_props.options.some(item => {
if (radio_value.value === item.value && item.is_input && item.input_required && !item.affix) {
show_error.value = true;
item.show_error = true;
item.error_msg = '补充信息不能为空';
return true;
} else {
show_error.value = false;
item.show_error = false;
item.error_msg = '';
}
});
return !show_error.value;
};
defineExpose({ validRadio });
</script>
<style lang="less" scoped>
<style lang="less">
.radio-field-page {
.label {
padding: 1rem 1rem 0 1rem;
font-size: 0.9rem;
padding: 30px 30px 0 30px;
font-size: 26px;
font-weight: bold;
span {
text {
color: red;
}
}
.note {
font-size: 0.9rem;
font-size: 30px;
margin-left: 1rem;
color: gray;
padding-bottom: 0.5rem;
......@@ -159,8 +181,9 @@ const rule_content = ref("");
.radio-wrapper {
border: 1px solid #eaeaea;
border-radius: 0.25rem;
padding: 0.25rem 0.5rem;
margin-bottom: 0.25rem;
padding: 25px;
margin: 25px;
margin-top: 0;
}
.affix-input {
border: 1px solid #eaeaea;
......@@ -170,13 +193,13 @@ const rule_content = ref("");
margin-bottom: 0.25rem;
}
.rule-desc-text {
margin: 0.35rem 0.5rem 0.5rem 1.75rem;
font-size: 0.8rem;
margin: 0.35rem 0.5rem 0.5rem 60px;
font-size: 28px;
color: #808080;
}
.rule-box {
font-size: 0.85rem;
margin-left: 1.8rem;
font-size: 28px;
margin-left: 60px;
padding-bottom: 0.5rem;
width: fit-content;
}
......@@ -184,18 +207,22 @@ const rule_content = ref("");
.rule-wrapper {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
height: 100%;
}
.rule-block {
position: relative;
// position: relative;
width: 80vw;
height: 80vh;
background-color: #fff;
overflow: scroll;
padding: 1rem;
display: flex;
border-radius: 8px;
align-items: center;
justify-content: center;
.close-btn {
position: absolute;
bottom: 1rem;
......@@ -216,4 +243,8 @@ const rule_content = ref("");
// border-radius: 0.25rem;
// padding: 0.25rem 0.5rem;
}
.nut-radio__label {
margin-left: 25px;
}
</style>
......
import _ from '@/utils/lodash'
import TextField from '@/components/TextField/index.vue'
import TextareaField from '@/components/TextareaField/index.vue'
// import RadioField from '@/components/RadioField/index.vue'
import RadioField from '@/components/RadioField/index.vue'
// import CheckboxField from '@/components/CheckboxField/index.vue'
// import PickerField from '@/components/PickerField/index.vue'
// import AreaPickerField from '@/components/AreaPickerField/index.vue'
......@@ -84,9 +84,9 @@ export function createComponentType(data) {
// item.name = item.key
// item.component = NumberField
// }
// if (item.component_props.tag === 'radio') {
// item.component = RadioField
// }
if (item.component_props.tag === 'radio') {
item.component = RadioField
}
// if (item.component_props.tag === 'checkbox') {
// item.component = CheckboxField
// }
......
<!--
* @Date: 2023-03-24 09:19:27
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-04-03 13:32:11
* @LastEditTime: 2023-04-03 16:52:30
* @FilePath: /custom_form/src/pages/table/index.vue
* @Description: 文件描述
-->
......@@ -128,6 +128,7 @@ const formatData = (data) => {
// 省市区选择,图片上传,文件上传,电子签名,评分组件
const input = ref([]);
const textarea = ref([]);
const radio = ref([]);
const area_picker = ref([]);
const image_uploader = ref([]);
const file_uploader = ref([]);
......@@ -142,6 +143,9 @@ const setRefMap = (el, item) => {
if (item.component_props.tag === "textarea") {
textarea.value.push(el);
}
if (item.component_props.tag === "radio") {
radio.value.push(el);
}
if (item.component_props.tag === "area_picker") {
area_picker.value.push(el);
}
......@@ -371,6 +375,9 @@ const onActive = (item) => {
if (item.key === "textarea") {
postData.value[item.filed_name] = item.value;
}
if (item.type === "radio") { // 单选控件
postData.value = Object.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value });
}
if (item.key === "area_picker") {
postData.value[item.filed_name] = item.value;
}
......@@ -389,9 +396,6 @@ const onActive = (item) => {
if (item.type === "picker") { // 下拉框控件
postData.value = _.assign(postData.value, { [item.key]: item.value });
}
if (item.type === "radio") { // 单选控件
postData.value = _.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value });
}
if (item.type === "checkbox") { // 多选控件
const checkbox_value = _.cloneDeep(item.value)
checkbox_value.forEach((element, index) => {
......@@ -437,6 +441,18 @@ const validOther = () => {
}
});
}
if (radio.value) {
// 单选框
radio.value.forEach((item, index) => {
if (!radio.value[index].validRadio()) {
valid = {
status: radio.value[index].validRadio(),
key: "radio",
};
return false;
}
});
}
if (area_picker.value) {
// 省市区地址
area_picker.value.forEach((item, index) => {
......