hookehuyr

✨ feat: 联调自定义规则显示字段功能

1 <!-- 1 <!--
2 * @Date: 2022-08-30 13:46:51 2 * @Date: 2022-08-30 13:46:51
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-02-10 15:34:39 4 + * @LastEditTime: 2023-02-27 14:24:03
5 * @FilePath: /data-table/src/components/PickerField/index.vue 5 * @FilePath: /data-table/src/components/PickerField/index.vue
6 * @Description: 单列选择器组件 6 * @Description: 单列选择器组件
7 --> 7 -->
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
37 const props = defineProps({ 37 const props = defineProps({
38 item: Object, 38 item: Object,
39 }); 39 });
40 +const emit = defineEmits(["active"]);
40 41
41 // TEST: 测试新功能:选择其他选项时,下方出现输入框,如果其他项被选中,输入框值为最终录入值。 42 // TEST: 测试新功能:选择其他选项时,下方出现输入框,如果其他项被选中,输入框值为最终录入值。
42 // 绑定值发生变化时回调,处理选项为其他时的输入项录入 43 // 绑定值发生变化时回调,处理选项为其他时的输入项录入
...@@ -61,6 +62,8 @@ const showPicker = ref(false); ...@@ -61,6 +62,8 @@ const showPicker = ref(false);
61 const onConfirm = ({ selectedOptions }) => { 62 const onConfirm = ({ selectedOptions }) => {
62 props.item.value = selectedOptions[0]?.value; 63 props.item.value = selectedOptions[0]?.value;
63 showPicker.value = false; 64 showPicker.value = false;
65 + // 触发点自定义监听事件,配合规则显示隐藏其他字段
66 + emit("active", { key: props.item.key, value: selectedOptions[0]?.value, type: "picker" });
64 // if (add_info_key.value === props.item.value) { 67 // if (add_info_key.value === props.item.value) {
65 // has_add_info.value = true; 68 // has_add_info.value = true;
66 // } 69 // }
......
1 <!-- 1 <!--
2 * @Date: 2022-07-18 10:22:22 2 * @Date: 2022-07-18 10:22:22
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-02-23 15:41:13 4 + * @LastEditTime: 2023-02-27 18:52:17
5 * @FilePath: /data-table/src/views/index.vue 5 * @FilePath: /data-table/src/views/index.vue
6 * @Description: 首页 6 * @Description: 首页
7 --> 7 -->
8 <template> 8 <template>
9 - <van-notice-bar 9 + <van-notice-bar v-if="formSetting.sjsj_is_count_down" left-icon="volume-o" :text="notice_text" scrollable
10 - v-if="formSetting.sjsj_is_count_down" 10 + mode="closeable" />
11 - left-icon="volume-o" 11 + <div class="table-box" :style="{ margin: is_pc ? '1rem 0' : '1rem', overflow: 'auto' }">
12 - :text="notice_text"
13 - scrollable
14 - mode="closeable"
15 - />
16 - <div class="table-box" :style="{ margin: is_pc ? '1rem 0' : '1rem', overflow: 'auto'}">
17 <template v-if="PHeader.visible"> 12 <template v-if="PHeader.visible">
18 - <van-image 13 + <van-image v-if="PHeader.type === 'image'" width="100%" :src="PHeader.cover" fit="cover" />
19 - v-if="PHeader.type === 'image'"
20 - width="100%"
21 - :src="PHeader.cover"
22 - fit="cover"
23 - />
24 <template v-if="PHeader.type === 'carousel'"> 14 <template v-if="PHeader.type === 'carousel'">
25 <van-swipe class="my-swipe" lazy-render indicator-color="white"> 15 <van-swipe class="my-swipe" lazy-render indicator-color="white">
26 - <van-swipe-item v-for="image in PHeader.cover" :key="index" 16 + <van-swipe-item v-for="image in PHeader.cover" :key="index"><img :src="image"
27 - ><img :src="image" style="height: 12rem; width: 100%; object-fit: cover" 17 + style="height: 12rem; width: 100%; object-fit: cover" /></van-swipe-item>
28 - /></van-swipe-item>
29 </van-swipe> 18 </van-swipe>
30 </template> 19 </template>
31 <div v-if="PHeader.type === 'text'" class="PHeader-Text" v-html="PHeader.banner" /> 20 <div v-if="PHeader.type === 'text'" class="PHeader-Text" v-html="PHeader.banner" />
...@@ -35,15 +24,8 @@ ...@@ -35,15 +24,8 @@
35 <van-config-provider :theme-vars="themeVars"> 24 <van-config-provider :theme-vars="themeVars">
36 <van-form @submit="onSubmit" scroll-to-error="true"> 25 <van-form @submit="onSubmit" scroll-to-error="true">
37 <van-cell-group :border="false"> 26 <van-cell-group :border="false">
38 - <component 27 + <component v-for="(item, index) in formData" :id="item.key" :ref="(el) => setRefMap(el, item)" :key="index"
39 - v-for="(item, index) in formData" 28 + :is="item.component" :item="item" @active="onActive" />
40 - :id="item.key"
41 - :ref="(el) => setRefMap(el, item)"
42 - :key="index"
43 - :is="item.component"
44 - :item="item"
45 - @active="onActive"
46 - />
47 </van-cell-group> 29 </van-cell-group>
48 <div v-if="formData.length && PCommit.visible" style="margin: 16px"> 30 <div v-if="formData.length && PCommit.visible" style="margin: 16px">
49 <van-button round block type="primary" native-type="submit"> 31 <van-button round block type="primary" native-type="submit">
...@@ -69,9 +51,7 @@ ...@@ -69,9 +51,7 @@
69 </van-form> 51 </van-form>
70 </van-config-provider> 52 </van-config-provider>
71 </div> 53 </div>
72 - <div 54 + <div style="text-align: center; color: #545454; font-size: 0.85rem; padding-bottom: 1rem">
73 - style="text-align: center; color: #545454; font-size: 0.85rem; padding-bottom: 1rem"
74 - >
75 提交即授权该表单收集你的填写信息 55 提交即授权该表单收集你的填写信息
76 </div> 56 </div>
77 <van-overlay :show="show"> 57 <van-overlay :show="show">
...@@ -89,7 +69,8 @@ ...@@ -89,7 +69,8 @@
89 <div style="text-align: center; margin-top: 0.5rem;">请输入密码填写表单</div> 69 <div style="text-align: center; margin-top: 0.5rem;">请输入密码填写表单</div>
90 <van-config-provider :theme-vars="themeVars"> 70 <van-config-provider :theme-vars="themeVars">
91 <van-form> 71 <van-form>
92 - <van-field v-model="mmtx_password" type="password" autocomplete label="密码" style="border: 1px solid #dfdfdf; margin: 1rem 0; border-radius: 5px;" /> 72 + <van-field v-model="mmtx_password" type="password" autocomplete label="密码"
73 + style="border: 1px solid #dfdfdf; margin: 1rem 0; border-radius: 5px;" />
93 <van-button @click="onSubmitPwd" type="primary" round block>验证并填写表单</van-button> 74 <van-button @click="onSubmitPwd" type="primary" round block>验证并填写表单</van-button>
94 </van-form> 75 </van-form>
95 </van-config-provider> 76 </van-config-provider>
...@@ -260,6 +241,81 @@ onMounted(async () => { ...@@ -260,6 +241,81 @@ onMounted(async () => {
260 }; 241 };
261 } 242 }
262 formData.value = formatData(page_form); 243 formData.value = formatData(page_form);
244 +
245 + // TODO: 等待真实数据结构 重构规则数据结构
246 + const rule_list = [
247 + {
248 + "mode": "SHOW",
249 + "field_names": [
250 + "man_inpu"
251 + ],
252 + "logical_op": "OR",
253 + "expr_list": [
254 + {
255 + "field_name": "sex",
256 + "op": "IN",
257 + "values": [
258 + "男"
259 + ]
260 + }
261 + ],
262 + "text": ""
263 + },
264 + {
265 + "mode": "SHOW",
266 + "field_names": [
267 + "woman_input"
268 + ],
269 + "logical_op": "OR",
270 + "expr_list": [
271 + {
272 + "field_name": "sex",
273 + "op": "IN",
274 + "values": [
275 + "女"
276 + ]
277 + }
278 + ],
279 + "text": ""
280 + },
281 + {
282 + "mode": "HIDE",
283 + "field_names": [
284 + "phone",
285 + "id_card"
286 + ],
287 + "logical_op": "AND",
288 + "expr_list": [
289 + {
290 + "field_name": "more",
291 + "op": "IN",
292 + "values": [
293 + "不想填了",
294 + ]
295 + },
296 + {
297 + "field_name": "field_9",
298 + "op": "IN",
299 + "values": [
300 + "说明1",
301 + ]
302 + }
303 + ],
304 + "text": ""
305 + }
306 + ];
307 + formData.value.forEach(item => {
308 + rule_list.forEach(rule => {
309 + if (rule.field_names?.includes(item.key)) {
310 + item.field_rules = {
311 + mode: rule.mode,
312 + logical_op: rule.logical_op,
313 + expr_list: rule.expr_list,
314 + }
315 + }
316 + })
317 + })
318 +
263 mockData.value = [ 319 mockData.value = [
264 { 320 {
265 key: "111", 321 key: "111",
...@@ -348,6 +404,44 @@ const checkUserPassword = async () => { ...@@ -348,6 +404,44 @@ const checkUserPassword = async () => {
348 } 404 }
349 } 405 }
350 406
407 +// 根据规则隐藏相应字段
408 +const checkRules = (field) => {
409 + formData.value.forEach(item => {
410 + // 只检查存在规则的字段
411 + if (item.field_rules) {
412 + let condition = '';
413 + // 多个规则的满足条件,为全且或者全或
414 + const op = item.field_rules?.logical_op === 'AND' ? '&&' : '||';
415 + item.field_rules?.expr_list.forEach(expr => {
416 + if (typeof postData.value[expr['field_name']] === 'string') { // 表单值为字符串(单选,下拉)
417 + const k = !!expr['values'].includes(postData.value[expr['field_name']])
418 + condition += `${k} ${op}`
419 + }
420 + if (typeof postData.value[expr['field_name']] === 'object') { // 表单值为数组(多选)
421 + const k = !!(_.intersection(expr['values'], postData.value[expr['field_name']])).length;
422 + condition += `${k} ${op}`
423 + }
424 + });
425 + // 把结果转换为布尔值
426 + if (item.field_rules?.logical_op === 'AND') {
427 + if (condition.indexOf('false') >= 0) {
428 + condition = false;
429 + } else {
430 + condition = true;
431 + }
432 + }
433 + if (item.field_rules?.logical_op === 'OR') {
434 + if (condition.indexOf('true') >= 0) {
435 + condition = true;
436 + } else {
437 + condition = false;
438 + }
439 + }
440 + item['component_props']['disabled'] = item.field_rules?.mode === 'SHOW' ? !condition : condition;
441 + }
442 + })
443 +}
444 +
351 // 操作绑定自定义字段回调 445 // 操作绑定自定义字段回调
352 const onActive = (item) => { 446 const onActive = (item) => {
353 if (item.key === "area_picker") { 447 if (item.key === "area_picker") {
...@@ -365,10 +459,13 @@ const onActive = (item) => { ...@@ -365,10 +459,13 @@ const onActive = (item) => {
365 if (item.type === "rate") { 459 if (item.type === "rate") {
366 postData.value = _.assign(postData.value, { [item.key]: item.value }); 460 postData.value = _.assign(postData.value, { [item.key]: item.value });
367 } 461 }
368 - if (item.type === "radio") { 462 + // if (item.type === "picker") { // 下拉框控件
463 + // // console.warn(item);
464 + // }
465 + if (item.type === "radio") { // 单选控件
369 postData.value = _.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value }); 466 postData.value = _.assign(postData.value, { [item.key]: item.affix ? item.affix : item.value });
370 } 467 }
371 - if (item.type === "checkbox") { 468 + if (item.type === "checkbox") { // 多选控件
372 const checkbox_value = _.cloneDeep(item.value) 469 const checkbox_value = _.cloneDeep(item.value)
373 checkbox_value.forEach((element, index) => { 470 checkbox_value.forEach((element, index) => {
374 for (const key in item.affix) { 471 for (const key in item.affix) {
...@@ -379,6 +476,9 @@ const onActive = (item) => { ...@@ -379,6 +476,9 @@ const onActive = (item) => {
379 }); 476 });
380 postData.value = _.assign(postData.value, { [item.key]: checkbox_value }); 477 postData.value = _.assign(postData.value, { [item.key]: checkbox_value });
381 } 478 }
479 +
480 + // 检查规则,会影响字段显示
481 + checkRules(item);
382 }; 482 };
383 483
384 // 检验没有绑定name的输入项 484 // 检验没有绑定name的输入项
...@@ -510,6 +610,7 @@ const onSubmit = async (values) => { ...@@ -510,6 +610,7 @@ const onSubmit = async (values) => {
510 text-align: center; 610 text-align: center;
511 white-space: pre-wrap; 611 white-space: pre-wrap;
512 } 612 }
613 +
513 .table-desc { 614 .table-desc {
514 padding: 0rem 1rem; 615 padding: 0rem 1rem;
515 color: #666; 616 color: #666;
...@@ -520,6 +621,7 @@ const onSubmit = async (values) => { ...@@ -520,6 +621,7 @@ const onSubmit = async (values) => {
520 width: 100%; 621 width: 100%;
521 } 622 }
522 } 623 }
624 +
523 .table-box { 625 .table-box {
524 background-color: #ffffff; 626 background-color: #ffffff;
525 padding-bottom: 1rem; 627 padding-bottom: 1rem;
...@@ -543,6 +645,7 @@ const onSubmit = async (values) => { ...@@ -543,6 +645,7 @@ const onSubmit = async (values) => {
543 align-items: center; 645 align-items: center;
544 justify-content: center; 646 justify-content: center;
545 height: 100%; 647 height: 100%;
648 +
546 .block { 649 .block {
547 width: 80vw; 650 width: 80vw;
548 background-color: #fff; 651 background-color: #fff;
...@@ -554,7 +657,7 @@ const onSubmit = async (values) => { ...@@ -554,7 +657,7 @@ const onSubmit = async (values) => {
554 .PHeader-Text { 657 .PHeader-Text {
555 padding: 1rem; 658 padding: 1rem;
556 font-weight: bold; 659 font-weight: bold;
557 - white-space:pre; 660 + white-space: pre;
558 } 661 }
559 662
560 // :deep(.van-icon) { // 处理正式服务器上箭头上下位移问题 663 // :deep(.van-icon) { // 处理正式服务器上箭头上下位移问题
......