Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Hooke
/
custom_form
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Authored by
hookehuyr
2023-03-31 18:04:13 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
199058c40bd4e13b84e3dc53e2eb9864affac092
199058c4
1 parent
853f138e
✨ feat(单行输入控件): 改造单行输入控件校验和多端二维码识别功能
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
230 additions
and
46 deletions
components.d.ts
package.json
src/components/TextField/index.vue
src/pages/table/index.vue
yarn.lock
components.d.ts
View file @
199058c
...
...
@@ -26,10 +26,15 @@ declare module '@vue/runtime-core' {
NameField
:
typeof
import
(
'./src/components/NameField/index.vue'
)[
'default'
]
NoteField
:
typeof
import
(
'./src/components/NoteField/index.vue'
)[
'default'
]
NumberField
:
typeof
import
(
'./src/components/NumberField/index.vue'
)[
'default'
]
NutButton
:
typeof
import
(
'@nutui/nutui-taro'
)[
'Button'
]
NutConfigProvider
:
typeof
import
(
'@nutui/nutui-taro'
)[
'ConfigProvider'
]
NutDialog
:
typeof
import
(
'@nutui/nutui-taro'
)[
'Dialog'
]
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'
]
NutSwiper
:
typeof
import
(
'@nutui/nutui-taro'
)[
'Swiper'
]
NutSwiperItem
:
typeof
import
(
'@nutui/nutui-taro'
)[
'SwiperItem'
]
PhoneField
:
typeof
import
(
'./src/components/PhoneField/index.vue'
)[
'default'
]
PickerField
:
typeof
import
(
'./src/components/PickerField/index.vue'
)[
'default'
]
RadioField
:
typeof
import
(
'./src/components/RadioField/index.vue'
)[
'default'
]
...
...
package.json
View file @
199058c
...
...
@@ -38,7 +38,7 @@
"dependencies"
:
{
"@babel/runtime"
:
"^7.7.7"
,
"@nutui/icons-vue-taro"
:
"^0.0.9"
,
"@nutui/nutui-taro"
:
"^4.0.
0
"
,
"@nutui/nutui-taro"
:
"^4.0.
4
"
,
"@tarojs/components"
:
"3.6.2"
,
"@tarojs/extend"
:
"^3.6.2"
,
"@tarojs/helper"
:
"3.6.2"
,
...
...
src/components/TextField/index.vue
View file @
199058c
<!--
* @Date: 2022-08-29 14:31:20
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-03-
24 15:04:18
* @LastEditTime: 2023-03-
31 17:57:13
* @FilePath: /custom_form/src/components/TextField/index.vue
* @Description: 单行文本输入框(微信扫描功能)
-->
...
...
@@ -11,19 +11,37 @@
<span v-if="item.component_props.required"> *</span>
{{ item.component_props.label }}
</div>
<div class="note-wrapper" v-if="item.component_props.note" v-html="item.component_props.note" />
<nut-input v-model="item.value" :name="item.name" :type="item.type"
:placeholder="item.component_props.placeholder ? item.component_props.placeholder : '请输入'" :rules="item.rules"
:required="item.required"
<!-- <div class="note-wrapper" v-if="item.component_props.note" v-html="item.component_props.note" /> -->
<nut-form-item :required="item.required">
<nut-input
v-model="input_value"
:type="item.type"
:border="false"
:placeholder="item.component_props.placeholder ? item.component_props.placeholder : '请输入'"
:readonly="item.component_props.readonly || (item.component_props.is_camera_scan && !item.component_props.is_edit_camera_scan_result)"
:disabled="item.component_props.disabled" :input-align="item.component_props.align"
:right-icon="item.component_props.is_camera_scan ? 'scan' : ''" @click-right-icon="clickRightIcon" />
:disabled="item.component_props.disabled"
:input-align="item.component_props.align"
style="border: 1px solid #eaeaea; border-radius: 0.25rem; padding: 0.25rem 0.5rem;"
>
<template #right v-if="item.component_props.is_camera_scan"><Scan @click="clickRightIcon()"></Scan></template>
</nut-input>
<div
v-if="show_error"
style="padding: 5px; color: red; font-size: 12px;"
>
{{ error_msg }}
</div>
</nut-form-item>
</div>
</template>
<script setup>
import { getUrlParams } from "@/utils/tools";
import Taro from '@tarojs/taro'
import { ref, computed, watch, onMounted, reactive } from "vue";
import { Scan } from '@nutui/icons-vue-taro';
// 初始化WX环境
import wx from 'weixin-js-sdk'
const props = defineProps({
item: Object,
...
...
@@ -33,6 +51,13 @@ const HideShow = computed(() => {
return !props.item.component_props.disabled
})
const emit = defineEmits(["active"]);
const input_value = ref('');
// 错误提示
const show_error = ref(false);
const error_msg = ref('');
// 默认识别类型
const scan_type_code = ref('ALL_CODE');
// 微信二维码扫描功能判断
...
...
@@ -49,11 +74,12 @@ const scanType = (scan_type_code) => {
// 预览模式
const model = getUrlParams(location.href) ? getUrlParams(location.href).model : '';
const clickRightIcon =
async
() => {
const clickRightIcon = () => {
// 预览模式屏蔽微信功能
if (model === 'preview') return false;
Taro.ready(() => {
Taro.scanQRCode({
if (process.env.TARO_ENV === 'h5') {
wx.ready(() => {
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: scanType(props.item.camera_scan_type), // 可以指定扫二维码还是一维码,默认二者都有
success: function (res) {
...
...
@@ -83,14 +109,86 @@ const clickRightIcon = async () => {
},
});
});
}
if (process.env.TARO_ENV === 'weapp') {
Taro.scanCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: scanType(props.item.camera_scan_type), // 可以指定扫二维码还是一维码,默认二者都有
success: function (res) {
if (res.errMsg === 'scanCode:ok') {
let code = res.result;
input_value.value = code;
Taro.showToast({
title: '扫描成功',
icon: 'success',
duration: 2000
})
} else {
console.warn(res);
Taro.showToast({
title: '扫描失败',
icon: 'error',
duration: 2000
})
}
},
error: function (res) {
if (res.errMsg.indexOf('function_not_exist') > 0) {
alert('版本过低请升级')
}
alert(res.errMsg);
},
});
}
}
// watch(
// () => input_value.value,
// (newValue, oldValue) => {
// console.warn(newValue);
// // props.item.value = {
// // key: "input",
// // filed_name: props.item.key,
// // value: newValue,
// // };
// // emit("active", props.item.value);
// },
// { immediate: true }
// );
onMounted(() => {
})
const changeInput = (val) => {
// props.item.value = {
// key: "input",
// filed_name: props.item.key,
// value: val,
// };
// emit("active", props.item.value);
// validInput();
}
// 校验模块
const validInput = () => {
// 必填项
if (props.item.component_props.required && !input_value.value) {
show_error.value = true;
error_msg.value = '必填项不能为空'
} else {
show_error.value = false;
error_msg.value = ''
}
return !show_error.value;
};
defineExpose({ validInput });
</script>
<style lang="less"
scoped
>
<style lang="less">
.text-field-page {
.label {
padding:
1rem 1rem 0 1rem
;
font-size:
0.9rem
;
padding:
30px 30px 0 30px
;
font-size:
26px
;
font-weight: bold;
span {
...
...
@@ -98,19 +196,30 @@ const clickRightIcon = async () => {
}
.note-wrapper {
font-size:
0.9rem
;
margin-left:
1rem
;
font-size:
24px
;
margin-left:
30px
;
color: gray;
padding-bottom:
0.5rem
;
padding-top:
0.25rem
;
padding-bottom:
15px
;
padding-top:
7px
;
white-space: pre-wrap;
}
}
}
:deep(.van-field__body) {
border: 1px solid #eaeaea;
border-radius: 0.25rem;
padding: 0.25rem 0.5rem;
// :deep(.nut-input) {
// border: 1px solid #eaeaea;
// border-radius: 0.25rem;
// padding: 0.25rem 0.5rem;
// }
// .nut-input--border {
// border: 1px solid #eaeaea;
// border-radius: 7px;
// padding: 7px 14px !important;
// }
input {
color: #000 !important;
}
</style>
...
...
src/pages/table/index.vue
View file @
199058c
<!--
* @Date: 2023-03-24 09:19:27
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-03-
24 15:55:18
* @FilePath: /custom_form/src/pages/
index
/index.vue
* @LastEditTime: 2023-03-
31 18:02:51
* @FilePath: /custom_form/src/pages/
table
/index.vue
* @Description: 文件描述
-->
<template>
<div class="table-page">
<nut-noticebar v-if="formSetting.sjsj_is_count_down" :text="notice_text" :scrollable="true"
:closeMode="true" right-icon="circle-close" />{{ formSetting }}
:closeMode="true" right-icon="circle-close" />
<div class="table-box" :style="{ margin: is_pc ? '1rem 0' : '1rem', overflow: 'auto' }">
<template v-if="PHeader.visible">
<image v-if="PHeader.type === 'image'" :src="PHeader.cover" mode="widthFix" style="width: 100%;" />
<template v-if="PHeader.type === 'carousel'">
<nut-swiper class="my-swipe" pagination-color="#fff" auto-play="3000">
<nut-swiper-item v-for="(image, index) in PHeader.cover" :key="index">
<img :src="image" style="height: 12rem; width: 100%; object-fit: cover" />
</nut-swiper-item>
</nut-swiper>
</template>
<div v-if="PHeader.type === 'text'" class="PHeader-Text taro_html" v-html="PHeader.banner" />
</template>
<view v-if="PHeader.label" class="table-title taro_html" v-html="PHeader.label" />
<div v-if="PHeader.description" class="table-desc taro_html" v-html="PHeader.description" />
<nut-config-provider :theme-vars="themeVars">
<nut-form ref="ruleForm">
<component v-for="(item, index) in formData"
:id="item.key"
:ref="(el) => setRefMap(el, item)"
:key="index"
:is="{...item.component}"
:item="item"
@active="onActive" />
<!-- <template v-for="(item, index) in formData" :id="item.key">
<nut-form-item v-if="index === 0" :prop="item.name" :required="item.required" :rules="item.rules">
<nut-input v-model="item.value" :type="item.type"
:placeholder="item.component_props.placeholder ? item.component_props.placeholder : '请输入'"
:readonly="item.component_props.readonly || (item.component_props.is_camera_scan && !item.component_props.is_edit_camera_scan_result)"
:disabled="item.component_props.disabled" :input-align="item.component_props.align">
</nut-input>
</nut-form-item>
</template> -->
<div v-if="formData.length && PCommit.visible" style="margin: 16px">
<!-- <nut-button round block type="primary" onclick="onSubmit"> -->
<nut-button round block type="primary" @click="onSubmit">
{{ PCommit.text ? PCommit.text : '提交' }}
</nut-button>
</div>
</nut-form>
</nut-config-provider>
</div>
</div>
</template>
...
...
@@ -16,7 +57,7 @@
import Taro, { useLoad } from '@tarojs/taro'
import { $ } from '@tarojs/extend'
import { createComponentType } from "@/hooks/useComponentType";
import { ref, computed, watchEffect, onMounted } from "vue";
import { ref, computed, watchEffect, onMounted
, reactive
} from "vue";
import _ from "@/utils/lodash";
import { storeToRefs } from 'pinia'
import { mainStore } from '@/stores'
...
...
@@ -25,6 +66,10 @@ import { addFormDataAPI } from "@/api/data.js";
import { wxInfo, getUrlParams } from "@/utils/tools";
import { styleColor } from "@/constant.js";
import { sharePage } from '@/composables/useShare.js'
// 初始化WX环境
import wx from 'weixin-js-sdk'
const ruleForm = ref(null);
// // 获取表单设置
const store = mainStore();
...
...
@@ -42,8 +87,9 @@ const PHeaderHeight = computed(() => {
});
// TAG: 自定义主题颜色
const themeVars = {
buttonPrimaryBackground: styleColor.baseColor,
buttonPrimaryBorderColor: styleColor.baseColor,
// buttonPrimaryBackground: styleColor.baseColor,
// buttonPrimaryBorderColor: styleColor.baseColor,
primaryColor: styleColor.baseColor,
};
const PHeader = ref({});
...
...
@@ -80,6 +126,7 @@ const formatData = (data) => {
// 处理没有绑定值的组件的赋值
// 省市区选择,图片上传,文件上传,电子签名,评分组件
const input = ref([]);
const area_picker = ref([]);
const image_uploader = ref([]);
const file_uploader = ref([]);
...
...
@@ -88,6 +135,9 @@ const rate_picker = ref([]);
// 动态绑定ref数据
const setRefMap = (el, item) => {
if (el) {
if (item.component_props.tag === "input") {
input.value.push(el);
}
if (item.component_props.tag === "area_picker") {
area_picker.value.push(el);
}
...
...
@@ -123,13 +173,13 @@ const onSubmitPwd = async () => {
onMounted(async () => {
// TAG: 全局背景色
$('body').css('background-color', styleColor.backgroundColor)
//
$('body').css('background-color', styleColor.backgroundColor)
const { data } = await queryFormAPI({ form_code });
const form_data = data;
// 动态修改标题
Taro.setNavigationBarTitle({
title: form_data.name
title: form_data.name
? form_data.name : ''
});
form_name.value = form_data.name;
// 重构数据结构
...
...
@@ -212,7 +262,7 @@ onMounted(async () => {
checkUserPassword();
// 启用分享功能,非预览模式
if (formSetting.value.wxzq_is_share && model !== 'preview') {
Taro
.ready(() => {
wx
.ready(() => {
/**
* 微信分享卡片标题模式
* form_name=使用表单名称作为分享标题,customize=自定义分享标题
...
...
@@ -311,6 +361,9 @@ const checkRules = () => {
// 操作绑定自定义字段回调
const onActive = (item) => {
if (item.key === "input") {
postData.value[item.filed_name] = item.value;
}
if (item.key === "area_picker") {
postData.value[item.filed_name] = item.value;
}
...
...
@@ -347,12 +400,24 @@ const onActive = (item) => {
checkRules();
};
// 检验
没有绑定name的输入项
// 检验
输入项控件的Rules
const validOther = () => {
let valid = {
status: true,
key: "",
};
if (input.value) {
// 单行文本
input.value.forEach((item, index) => {
if (!input.value[index].validInput()) {
valid = {
status: input.value[index].validInput(),
key: "input",
};
return false;
}
});
}
if (area_picker.value) {
// 省市区地址
area_picker.value.forEach((item, index) => {
...
...
@@ -426,7 +491,7 @@ const preValidData = (values) => {
const onSubmit = async (values) => {
// 表单数据处理
postData.value = preValidData(values);
//
postData.value = preValidData(values);
// 合并扩展字段
postData.value = { ...postData.value, x_field_1, x_cycle };
// 检查非表单输入项
...
...
@@ -469,18 +534,23 @@ const onSubmit = async (values) => {
};
</script>
<style lang="less" scoped>
<style lang="less">
.table-page {
background-color: #FAF9DC;
min-height: calc(100vh);
position: relative;
}
.table-title {
padding:
1rem
;
font-size:
1.15rem
;
padding:
30px
;
font-size:
30px
;
text-align: center;
white-space: pre-wrap;
}
.table-desc {
padding: 0rem
1rem
;
padding: 0rem
30px
;
color: #666;
font-size:
0.9rem
;
font-size:
26px
;
white-space: pre-wrap;
img {
...
...
@@ -490,7 +560,7 @@ const onSubmit = async (values) => {
.table-box {
background-color: #ffffff;
padding-bottom:
1rem
;
padding-bottom:
30px
;
}
.wrapper {
...
...
@@ -501,7 +571,7 @@ const onSubmit = async (values) => {
}
.block {
width:
10rem
;
width:
300px
;
// height: 10rem;
background-color: #fff;
}
...
...
@@ -515,13 +585,13 @@ const onSubmit = async (values) => {
.block {
width: 80vw;
background-color: #fff;
padding:
1rem
;
padding:
30px
;
border-radius: 5px;
}
}
.PHeader-Text {
padding:
1rem
;
padding:
30px
;
font-weight: bold;
white-space: pre;
}
...
...
yarn.lock
View file @
199058c
...
...
@@ -1359,10 +1359,10 @@
resolved "https://mirrors.cloud.tencent.com/npm/@nutui/icons-vue-taro/-/icons-vue-taro-0.0.9.tgz#b5223eb01e2b987fdbe460e5d0439a66481e54f1"
integrity sha512-10VYAtFC+o1X0anGs+y2PgF1NWMeLFz2JVMRw4BWLg6wbtVbYy9wukLxyGhZC6Yf6t39DcwaGVda8paV7K6/Ew==
"@nutui/nutui-taro@^4.0.
0
":
version "4.0.
3
"
resolved "https://mirrors.cloud.tencent.com/npm/@nutui/nutui-taro/-/nutui-taro-4.0.
3.tgz#a77f644832b4c98f1e4a3cf93b9498057f7ccb5f
"
integrity sha512-
/+xCfUBRE8DcBQpQirgU3GgsaaRmnGu46olvwhjNm5WpJ+SmhloTYrG7Y0aqwnmI3Z9HRVsW8mEUW/ypzPviH
Q==
"@nutui/nutui-taro@^4.0.
4
":
version "4.0.
4
"
resolved "https://mirrors.cloud.tencent.com/npm/@nutui/nutui-taro/-/nutui-taro-4.0.
4.tgz#c5b65431ece527e3e531bc7923ad0a7b499daeeb
"
integrity sha512-
v9XyXidgiRgZTH5JofgjhJTGGvQDXhOqkLb/uRjlR2c9eQBSU653rlTO82VjUWOpCterYDxtqXswUZIf+PRbf
Q==
dependencies:
"@nutui/icons-vue-taro" "^0.0.9"
sass "^1.50.0"
...
...
Please
register
or
login
to post a comment