hookehuyr

✨ feat(地址选择控件): 样式和功能调整

...@@ -26,11 +26,14 @@ declare module '@vue/runtime-core' { ...@@ -26,11 +26,14 @@ declare module '@vue/runtime-core' {
26 NameField: typeof import('./src/components/NameField/index.vue')['default'] 26 NameField: typeof import('./src/components/NameField/index.vue')['default']
27 NoteField: typeof import('./src/components/NoteField/index.vue')['default'] 27 NoteField: typeof import('./src/components/NoteField/index.vue')['default']
28 NumberField: typeof import('./src/components/NumberField/index.vue')['default'] 28 NumberField: typeof import('./src/components/NumberField/index.vue')['default']
29 + NutAddress: typeof import('@nutui/nutui-taro')['Address']
29 NutButton: typeof import('@nutui/nutui-taro')['Button'] 30 NutButton: typeof import('@nutui/nutui-taro')['Button']
31 + NutCell: typeof import('@nutui/nutui-taro')['Cell']
30 NutCheckbox: typeof import('@nutui/nutui-taro')['Checkbox'] 32 NutCheckbox: typeof import('@nutui/nutui-taro')['Checkbox']
31 NutCheckboxGroup: typeof import('@nutui/nutui-taro')['CheckboxGroup'] 33 NutCheckboxGroup: typeof import('@nutui/nutui-taro')['CheckboxGroup']
32 NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider'] 34 NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider']
33 NutDialog: typeof import('@nutui/nutui-taro')['Dialog'] 35 NutDialog: typeof import('@nutui/nutui-taro')['Dialog']
36 + NutDivider: typeof import('@nutui/nutui-taro')['Divider']
34 NutField: typeof import('@nutui/nutui-taro')['Field'] 37 NutField: typeof import('@nutui/nutui-taro')['Field']
35 NutForm: typeof import('@nutui/nutui-taro')['Form'] 38 NutForm: typeof import('@nutui/nutui-taro')['Form']
36 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] 39 NutFormItem: typeof import('@nutui/nutui-taro')['FormItem']
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
55 "@tarojs/runtime": "3.6.2", 55 "@tarojs/runtime": "3.6.2",
56 "@tarojs/shared": "3.6.2", 56 "@tarojs/shared": "3.6.2",
57 "@tarojs/taro": "3.6.2", 57 "@tarojs/taro": "3.6.2",
58 + "@vant/area-data": "^1.4.1",
58 "axios-miniprogram": "^2.0.0-rc-2", 59 "axios-miniprogram": "^2.0.0-rc-2",
59 "dayjs": "^1.11.7", 60 "dayjs": "^1.11.7",
60 "pinia": "2.0.10", 61 "pinia": "2.0.10",
......
This diff could not be displayed because it is too large.
1 <!-- 1 <!--
2 * @Date: 2022-08-30 14:32:11 2 * @Date: 2022-08-30 14:32:11
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-02-14 14:37:45 4 + * @LastEditTime: 2023-04-07 13:36:31
5 - * @FilePath: /data-table/src/components/AreaPickerField/index.vue 5 + * @FilePath: /custom_form/src/components/AreaPickerField/index.vue
6 * @Description: 省市区选择控件 6 * @Description: 省市区选择控件
7 --> 7 -->
8 <template> 8 <template>
9 <div v-if="HideShow" class="area-picker-field"> 9 <div v-if="HideShow" class="area-picker-field">
10 - <div class="label"><span v-if="item.component_props.required">&nbsp;*</span>{{ item.component_props.label }}</div> 10 + <div class="label">
11 - <van-field 11 + <text v-if="item.component_props.required">&nbsp;*</text>
12 - name="ignore" 12 + {{ item.component_props.label }}
13 - v-model="fieldValue" 13 + </div>
14 - is-link 14 + <nut-cell title="请选择省市区" :desc="text" is-link @click="showAddress"></nut-cell>
15 - readonly 15 + <nut-textarea v-if="show_address" v-model="address_info" rows="2" :autosize="{ maxHeight: 100, minHeight: 50 }" placeholder="请填写详细地址" @blur="onBlur" :border="false" />
16 - :required="item.component_props.required" 16 + <nut-divider :style="{ color: '#ebedf0' }" />
17 - placeholder="请选择省市区" 17 + <div
18 - :rules="item.rules" 18 + v-if="show_error"
19 - @click="showPicker = true" 19 + style="padding-left: 20px; color: red; font-size: 12px;"
20 - :border="show_address ? true : false"
21 - />
22 - <van-field
23 - v-if="show_address"
24 - name="ignore"
25 - v-model="address"
26 - placeholder="请填写详细地址"
27 - @blur="onBlur"
28 - :rules="item.rules"
29 - :border="false"
30 - />
31 - <!-- <div
32 - v-if="show_empty"
33 - class="van-field__error-message"
34 - style="padding: 0 1rem 1rem 1rem"
35 > 20 >
36 - 地址不能为空 21 + {{ error_msg }}
37 - </div> --> 22 + </div>
38 - <van-divider /> 23 + <nut-address v-model="address_value" v-model:visible="showPopup" :province="address.province" :city="address.city"
39 - 24 + :country="address.country" :town="address.town" @change="onChange" @close="close"
40 - <van-popup v-model:show="showPicker" position="bottom"> 25 + custom-address-title="请选择所在地区"></nut-address>
41 - <van-area
42 - v-model="item.city_code"
43 - title=""
44 - :area-list="areaList"
45 - @confirm="onConfirm"
46 - @cancel="showPicker = false"
47 - />
48 - </van-popup>
49 </div> 26 </div>
50 </template> 27 </template>
51 28
52 <script setup> 29 <script setup>
53 -import { areaList } from "@vant/area-data"; 30 +import { ref, computed, watch, onMounted, reactive } from "vue";
31 +import { useCascaderAreaData } from "./index";
54 32
55 const props = defineProps({ 33 const props = defineProps({
56 item: Object, 34 item: Object,
57 }); 35 });
36 +
37 +const areaList = useCascaderAreaData()
38 +
58 // 隐藏显示 39 // 隐藏显示
59 const HideShow = computed(() => { 40 const HideShow = computed(() => {
60 return !props.item.component_props.disabled 41 return !props.item.component_props.disabled
61 }) 42 })
43 +
62 const emit = defineEmits(["active"]); 44 const emit = defineEmits(["active"]);
63 -const show_empty = ref(false);
64 45
65 const show_address = ref(!props.item.component_props.no_street) 46 const show_address = ref(!props.item.component_props.no_street)
47 +const address_info = ref('')
66 48
67 -const address = ref(""); 49 +const showPopup = ref(false);
68 -const city_code = ref(""); 50 +const address = ref({
69 -const showPicker = ref(false); 51 + province: [],
70 -let fieldValue = ref(""); 52 + city: [],
53 + country: [],
54 + town: []
55 +})
71 56
72 -const onConfirm = ({ selectedOptions }) => { 57 +const customValue = (address_text, address_info, address_value) => {
73 - fieldValue.value = selectedOptions.map((option) => option.text).join(" "); 58 + let address = '';
74 - city_code.value = selectedOptions[2]?.value; 59 + if (address_text === '请选择地址') {
60 + address = address_info
61 + } else {
62 + address = address_text + ' ' + address_info
63 + }
64 + // 发送自定义数据
75 props.item.value = { 65 props.item.value = {
76 key: "area_picker", 66 key: "area_picker",
77 filed_name: props.item.key, 67 filed_name: props.item.key,
78 value: { 68 value: {
79 - address: fieldValue.value + ' ' + address.value, 69 + address,
80 - city_code: city_code.value 70 + city_code: JSON.stringify(address_value)
81 }, 71 },
82 }; 72 };
83 emit("active", props.item.value); 73 emit("active", props.item.value);
84 - showPicker.value = false; 74 +}
75 +
76 +onMounted(() => {
77 + // 初始化地址列表
78 + address.value['province'] = areaList;
79 + address.value['city'] = areaList[0]['children']
80 + address.value['country'] = areaList[0]['children'][0]['children'];
81 + customValue(text.value, address_info.value, address_value.value)
82 +})
83 +
84 +const text = ref('请选择地址')
85 +const address_value = ref([])
86 +
87 +const showAddress = () => {
88 + showPopup.value = !showPopup.value;
89 +};
90 +
91 +const onChange = (cal) => {
92 + if (cal.custom === 'province') { // 点击省列表时
93 + const id = cal.value['id']; // 当前选中省的ID
94 + const current_city = areaList.filter(item => item.id === id);
95 + address.value['city'] = current_city[0]['children']; // 更新市列表
96 + }
97 + if (cal.custom === 'city') { // 点击市列表时
98 + address.value['country'] = cal.value['children']; // 更新区列表
99 + }
100 +
101 + const name = address.value[cal.next]
102 + if (name.length < 1) {
103 + showPopup.value = false;
104 + }
105 +};
106 +const close = val => {
107 + const { province, city, country } = val.data;
108 + text.value = `${province.name} ${city.name} ${country.name}`;
109 + address_value.value = [province.id, city.id, country.id];
110 + // 发送自定义数据
111 + customValue(text.value, address_info.value, address_value.value)
85 }; 112 };
86 113
87 const onBlur = () => { 114 const onBlur = () => {
88 - props.item.value = { 115 + // 发送自定义数据
89 - key: "area_picker", 116 + customValue(text.value, address_info.value, address_value.value)
90 - filed_name: props.item.key,
91 - value: {
92 - address: fieldValue.value + ' ' + address.value,
93 - city_code: city_code.value
94 - },
95 - };
96 - emit("active", props.item.value);
97 } 117 }
98 118
119 +
120 +// 错误提示
121 +const show_error = ref(false);
122 +const error_msg = ref('');
123 +
124 +const required = props.item.component_props.required;
125 +const show_street = !props.item.component_props.no_street; // 显示详细地址
99 // 校验模块 126 // 校验模块
100 const validAreaPicker = () => { 127 const validAreaPicker = () => {
101 // 必填项 128 // 必填项
102 - if (props.item.component_props.required && !fieldValue.value) { 129 + if (required && show_street && (!address_value.value.length || !address_info.value)) {
103 - show_empty.value = true; 130 + show_error.value = true;
104 - } else if (props.item.component_props.required && !address.value) { 131 + error_msg.value = '必填项不能为空'
105 - show_empty.value = true; 132 + } else if (required && !show_street && !address_value.value.length) {
133 + show_error.value = true;
134 + error_msg.value = '必填项不能为空'
106 } else { 135 } else {
107 - show_empty.value = false; 136 + show_error.value = false;
137 + error_msg.value = ''
108 } 138 }
109 - return !show_empty.value; 139 + return !show_error.value;
110 }; 140 };
111 141
112 -defineExpose({ validAreaPicker }); 142 +defineExpose({ validAreaPicker, id: props.item.key });
113 </script> 143 </script>
114 144
115 -<style lang="less" scoped> 145 +<style lang="less">
116 .area-picker-field { 146 .area-picker-field {
117 .label { 147 .label {
118 - padding: 1rem 1rem 0 1rem; 148 + padding: 30px 30px 0 30px;
119 - font-size: 0.9rem; 149 + font-size: 26px;
120 font-weight: bold; 150 font-weight: bold;
121 151
122 - span { 152 + text {
123 color: red; 153 color: red;
124 } 154 }
155 +
156 + .note-wrapper {
157 + font-size: 24px;
158 + margin-left: 30px;
159 + color: gray;
160 + padding-bottom: 15px;
161 + padding-top: 7px;
162 + white-space: pre-wrap;
163 + }
125 } 164 }
126 -}
127 165
166 + .nut-textarea {
167 + padding: 10px 32px;
168 + }
169 +}
128 </style> 170 </style>
......
...@@ -4,7 +4,7 @@ import TextareaField from '@/components/TextareaField/index.vue' ...@@ -4,7 +4,7 @@ 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'
8 // import DatePickerField from '@/components/DatePickerField/index.vue' 8 // import DatePickerField from '@/components/DatePickerField/index.vue'
9 // import TimePickerField from '@/components/TimePickerField/index.vue' 9 // import TimePickerField from '@/components/TimePickerField/index.vue'
10 // import DateTimePickerField from '@/components/DateTimePickerField/index.vue' 10 // import DateTimePickerField from '@/components/DateTimePickerField/index.vue'
...@@ -93,9 +93,9 @@ export function createComponentType(data) { ...@@ -93,9 +93,9 @@ export function createComponentType(data) {
93 if (item.component_props.tag === 'select') { 93 if (item.component_props.tag === 'select') {
94 item.component = PickerField 94 item.component = PickerField
95 } 95 }
96 - // if (item.component_props.tag === 'address') { 96 + if (item.component_props.tag === 'address') {
97 - // item.component = AreaPickerField 97 + item.component = AreaPickerField
98 - // } 98 + }
99 // if (item.component_props.tag === 'date') { 99 // if (item.component_props.tag === 'date') {
100 // item.component = DatePickerField 100 // item.component = DatePickerField
101 // } 101 // }
......
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 17:14:16 4 + * @LastEditTime: 2023-04-07 13:07:06
5 * @FilePath: /custom_form/src/pages/table/index.vue 5 * @FilePath: /custom_form/src/pages/table/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -135,6 +135,7 @@ const picker = ref([]); ...@@ -135,6 +135,7 @@ const picker = ref([]);
135 const number = ref([]); 135 const number = ref([]);
136 const phone = ref([]); 136 const phone = ref([]);
137 const email = ref([]); 137 const email = ref([]);
138 +const address = ref([]);
138 const area_picker = ref([]); 139 const area_picker = ref([]);
139 const image_uploader = ref([]); 140 const image_uploader = ref([]);
140 const file_uploader = ref([]); 141 const file_uploader = ref([]);
...@@ -170,6 +171,9 @@ const setRefMap = (el, item) => { ...@@ -170,6 +171,9 @@ const setRefMap = (el, item) => {
170 if (item.component_props.tag === "email") { 171 if (item.component_props.tag === "email") {
171 email.value.push(el); 172 email.value.push(el);
172 } 173 }
174 + if (item.component_props.tag === "address") {
175 + address.value.push(el);
176 + }
173 if (item.component_props.tag === "area_picker") { 177 if (item.component_props.tag === "area_picker") {
174 area_picker.value.push(el); 178 area_picker.value.push(el);
175 } 179 }
...@@ -438,6 +442,9 @@ const onActive = (item) => { ...@@ -438,6 +442,9 @@ const onActive = (item) => {
438 if (item.key === "email") { 442 if (item.key === "email") {
439 postData.value[item.filed_name] = item.value; 443 postData.value[item.filed_name] = item.value;
440 } 444 }
445 + if (item.key === "address") {
446 + postData.value[item.filed_name] = item.value;
447 + }
441 if (item.key === "image_uploader") { 448 if (item.key === "image_uploader") {
442 postData.value[item.filed_name] = item.value; 449 postData.value[item.filed_name] = item.value;
443 } 450 }
...@@ -581,6 +588,19 @@ const validOther = () => { ...@@ -581,6 +588,19 @@ const validOther = () => {
581 } 588 }
582 }); 589 });
583 } 590 }
591 + if (address.value) {
592 + // 地址选择器
593 + address.value.forEach((item, index) => {
594 + if (!address.value[index].validAreaPicker()) {
595 + valid = {
596 + status: address.value[index].validAreaPicker(),
597 + key: "address",
598 + id: address.value[index]?.id
599 + };
600 + return false;
601 + }
602 + });
603 + }
584 if (area_picker.value) { 604 if (area_picker.value) {
585 // 省市区地址 605 // 省市区地址
586 area_picker.value.forEach((item, index) => { 606 area_picker.value.forEach((item, index) => {
......
...@@ -2278,6 +2278,11 @@ ...@@ -2278,6 +2278,11 @@
2278 "@typescript-eslint/types" "5.56.0" 2278 "@typescript-eslint/types" "5.56.0"
2279 eslint-visitor-keys "^3.3.0" 2279 eslint-visitor-keys "^3.3.0"
2280 2280
2281 +"@vant/area-data@^1.4.1":
2282 + version "1.4.1"
2283 + resolved "https://mirrors.cloud.tencent.com/npm/@vant/area-data/-/area-data-1.4.1.tgz#b4f1bce05dbb147dc08fd2ed9b2a0f63d3329b29"
2284 + integrity sha512-D8zI/rfxREhnIKGoYzsEJZ73fte4JARhFeFftLIH7ynu1sPrCBEgPkLEbwPyvw3VC4JdSIuzaK5uOhu+BcoPXw==
2285 +
2281 "@vue/babel-helper-vue-transform-on@^1.0.2": 2286 "@vue/babel-helper-vue-transform-on@^1.0.2":
2282 version "1.0.2" 2287 version "1.0.2"
2283 resolved "https://mirrors.cloud.tencent.com/npm/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz#9b9c691cd06fc855221a2475c3cc831d774bc7dc" 2288 resolved "https://mirrors.cloud.tencent.com/npm/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz#9b9c691cd06fc855221a2475c3cc831d774bc7dc"
......