Files
hospital-rescue-app-v2/uni_modules/tmx-ui/components/x-form-item/x-form-item.uvue
T
2026-09-24 16:25:22 +08:00

509 lines
14 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
import { PropType } from "vue"
import { getUid } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { FORM_RULE, FORM_SUBMIT_OBJECT} from "../../interface.uts"
import { FORM_ITEM } from "../x-form/interface.uts"
import { formVaild} from "../x-form/util.uts"
/**
* @name 表单子组件 xFormItem
* @description 从1.1.2开始允许非xform直接子节点了,也就是在xform可以嵌套view进行form-item布局了,但建议不要嵌套太深,影响性能.
* 1.1.17开始支持嵌套对象字段,key必须/分割如'user/id'sdk不支持以.分割符。
* @page /pages/index/form
* @category 表单组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
export default {
name:"xFormItem",
data() {
return {
id: ("xFormItem-" + getUid()) as string,
errorText: "请正确填写",
isError: false,
first: true,
tid: 0
}
},
props: {
/**
* 表单名称
*/
label: {
type: String,
default: ""
},
/**
* 是否显示标题
*/
showLabel: {
type: [Boolean, null] as PropType<boolean | null>,
default: null
},
/**
* 校验的字段名称
* 字段名称一定要存在于form表单数据中。否则报错。
*/
field: {
type: String,
default: ""
},
/**
* 是否是必填项.
* 设置了此值,才会执行校验。
*/
required: {
type: Boolean,
default: false
},
/**
* 是否显示校验必填项前面的红*符号
*/
showRequired: {
type: Boolean,
default: true
},
/**
* 校验规则对象,uniappx暂不支持对象中嵌套泛型函数
* 从1.1.10开始rule对象中增加了trigger:change,blur校验时机,blur主要是用在input组件上的
* 如果你内部没有input而使用blur,会造成不校验的问题,请认真阅读文档.
*/
rule: {
type: Array as PropType<FORM_RULE[]>,
default: () : FORM_RULE[] => [] as FORM_RULE[]
},
/**
* 标签宽(横向表单时有效)
* 默认空值取父form统一的设置。
*/
labelWidth: {
type: String,
default: ""
},
/**
* 默认空值取父form统一的设置。
* vertical|horizontal
*/
labelDirection: {
type: String,
default: ""
},
/**
* 默认空值取父form统一的设置。
* 标签的文本颜色
*/
labelFontColor: {
type: String,
default: ""
},
/**
* 默认空值取父form统一的设置。
* 标签的文本颜色
*/
labelFontSize: {
type: String,
default: ""
},
/**
* 标签标题对齐方式
* left,right,center
*/
labelAlign:{
type:String,
default:"left"
},
/**
* 是否显示底部边框
*/
showBottomBorder: {
type: Boolean,
default: true
},
/**
* 排版布局上和下的间隙。
* 数组必须是2长度
* 第一个是上间隙,第二个是下间隙,
* 如果showBottomBorder为false时下间隙第二个参数会被取消。
* 如果label为horizontal横向时,第一个参数的上间隙生效,如果是上下排列不生效。
*/
cellPadding: {
type: Array as PropType<string[]>,
default: () : string[] => ['10', '10'] as string[]
},
/**
* 排版布局上和下的间隙。
* 数组必须是2长度
* 第一个是上间隙,第二个是下间隙,
* 如果label为horizontal横向时,些参数失效,只有竖向时才生效。
*/
labelPadding: {
type: Array as PropType<string[]>,
default: () : string[] => ['12', '12'] as string[]
},
/**
* 校验时,是否显示下边的出错信息
*/
showError:{
type:Boolean,
default:true
},
/**
* 默认区域内的自定样式
*/
contentStyle:{
type:String,
default:""
}
},
inject: {
XFORMITEM_TOP: { type: Number, default: 0 },
XFORMITEM_LABEL_WIDTH: { type: String, default: '100' },
XFORMITEM_LABEL_Direction: { type: String, default: 'horizontal' },
XFORMITEM_LABEL_FontColor: { type: String, default: '#333333' },
XFORMITEM_LABEL_SCROLL: { type: Boolean, default: true },
XFORMITEM_LABEL_FontSize: { type: String, default: '16' },
XFORMITEM_SHOWLABEL: { type: Boolean, default: true },
XFORMITEM_ERROR_ALIGN: { type: String, default: 'right' },
},
mounted() {
this.pushDataToParent();
},
beforeUnmount() {
this.removeSelf();
clearTimeout(this.tid);
},
computed: {
_contentStyle():string{
return this.contentStyle
},
_showError():boolean{
return this.showError
},
_cellPadding() : string[] {
let mb = checkIsCssUnit(this.cellPadding[0], xConfig.unit)
let pb = checkIsCssUnit(this.cellPadding[1], xConfig.unit)
return [mb, pb]
},
_labelPadding() : string[] {
let mb = checkIsCssUnit(this.labelPadding[0], xConfig.unit)
let pb = checkIsCssUnit(this.labelPadding[1], xConfig.unit)
return [mb, pb]
},
_parentTop() : number {
return this.XFORMITEM_TOP
},
_labelWidth() : string {
if (this.labelWidth != "") return checkIsCssUnit(this.labelWidth, xConfig.unit)
return checkIsCssUnit(this.XFORMITEM_LABEL_WIDTH, xConfig.unit)
},
_labelFontSize() : string {
let labelsize = checkIsCssUnit(this.XFORMITEM_LABEL_FontSize, xConfig.unit)
if (this.labelFontSize != "") {
labelsize = checkIsCssUnit(this.labelFontSize, xConfig.unit)
}
return labelsize
},
_labelDirection() : string {
if (this.labelDirection != "") return this.labelDirection
return this.XFORMITEM_LABEL_Direction
},
_labelFontColor() : string {
if (this.labelFontColor != "") return getDefaultColor(this.labelFontColor)
return getDefaultColor(this.XFORMITEM_LABEL_FontColor)
},
_label() : string {
return this.label
},
_showLabel() : boolean {
let show = this.XFORMITEM_SHOWLABEL;
if (this.showLabel != null) {
show = this.showLabel! as boolean;
}
return show
},
_showRequired() : boolean {
return this.showRequired
},
_rule() : FORM_RULE[] {
return this.rule;
},
_showBottomBoder() : boolean {
return this.showBottomBorder
},
_required() : boolean {
return this.required
},
_errorFontsize() : string {
return (xConfig.fontScale * 14).toString() + 'px'
},
_bordrBottomSolid() : string {
if (!this.showBottomBorder) return "border-bottom:none";
let lightSolid = "border-bottom: 1px solid #f5f5f5";
let darkSolid = `border-bottom: 1px solid ${xConfig.borderDarkColor}`
return xConfig.dark == 'dark' ? darkSolid : lightSolid
},
_labelAlign():string{
let dq = 'flex-start'
if(this.labelAlign=='right'){
dq = 'flex-end'
}else if(this.labelAlign=='center'){
dq = 'center'
}
return dq;
}
},
watch: {
field() {
this.pushDataToParent();
},
},
methods: {
pushDataToParent() {
let pelement = this.findParent(this);
if (pelement == null) return;
// @ts-ignore
let parent : XFormComponentPublicInstance = pelement as XFormComponentPublicInstance;
// 预先传递,怕子组件内的内容复杂导致查询延误,导致顺序错乱。
parent.pushAdd({
id: this.id,
ele: this,
top: 0,
name: this.field,
} as FORM_ITEM)
let t = this;
uni.createSelectorQuery().in(t)
.select(".xFormItem")
.boundingClientRect().exec((ret) => {
let nodeinfo = ret[0] as NodeInfo
parent.pushAdd({
id: t.id,
ele: t,
top: nodeinfo.top!,
name: t.field,
} as FORM_ITEM)
})
},
removeSelf() {
let pelement = this.findParent(this);
if (pelement == null) return;
// @ts-ignore
let parent : XFormComponentPublicInstance = pelement as XFormComponentPublicInstance;
parent.delItem(this.id)
},
getParentRules():FORM_RULE[] {
let pelement = this.findParent(this);
if (pelement == null) return [] as FORM_RULE[];
// @ts-ignore
let parent : XFormComponentPublicInstance = pelement as XFormComponentPublicInstance;
return parent.getRules(this.field) as FORM_RULE[]
},
vaildCompele(val : any|null,isSkipBlur:boolean|null = true) : FORM_SUBMIT_OBJECT {
this.first = false;
if (!this._required) {
return {
valid: true,
key: this.field,
value: val,
errorMessage: ""
} as FORM_SUBMIT_OBJECT;
}
const rulesList = [...this._rule,...this.getParentRules()] as FORM_RULE[]
if (rulesList.length == 0 && this._required) {
let isSuccess = formVaild(val,{} as FORM_RULE)
this.isError = !isSuccess
return {
valid: isSuccess,
key: this.field,
value: val,
errorMessage: isSuccess ? "" : '请正确填写/选择'
} as FORM_SUBMIT_OBJECT;
}
let isSuccess = true;
for (let i = 0; i < rulesList.length; i++) {
let item = rulesList[i]
if(item.trigger == 'blur'&&isSkipBlur==true) continue;
isSuccess = formVaild(val,item)
this.isError = !isSuccess
if (!isSuccess) {
if (item.errorMessage != "" && typeof item.errorMessage == 'string') {
this.errorText = item.errorMessage! as string
} else {
this.errorText = '请正确填写/选择'
}
break;
}
}
return {
valid: isSuccess,
key: this.field,
value: val,
errorMessage: isSuccess ? "" : this.errorText
} as FORM_SUBMIT_OBJECT;
},
clearValid(){
this.isError=false;
this.first = true;
},
/**
* 实时校验结果。form中要开启,如果没有这种需求可以不开启,会损耗性能。
*/
getVaildStatus(val : any|null) : FORM_SUBMIT_OBJECT {
if (!this._required) {
return {
valid: true,
key: this.field,
value: val,
errorMessage: ""
} as FORM_SUBMIT_OBJECT;
}
const rulesList = [...this._rule,...this.getParentRules()] as FORM_RULE[]
if (rulesList.length == 0 && this._required) {
let isSuccess = formVaild(val,{} as FORM_RULE)
return {
valid: isSuccess,
key: this.field,
value: val,
errorMessage: isSuccess ? "" : '请正确填写/选择'
} as FORM_SUBMIT_OBJECT;
}
let isSuccess = true;
let errotips = ''
for (let i = 0; i < rulesList.length; i++) {
let item = rulesList[i]
isSuccess = formVaild(val,item)
if (!isSuccess) {
if (item.errorMessage != "" && typeof item.errorMessage == 'string') {
errotips = item.errorMessage! as string
} else {
errotips = '请正确填写/选择'
}
break;
}
}
return {
valid: isSuccess,
key: this.field,
value: val,
errorMessage: isSuccess ? "" : errotips
} as FORM_SUBMIT_OBJECT;
},
/**
* 为Input定制校验函数.
*/
validByblur(value:any){
this.vaildCompele(value,false)
},
findParent(parent:VueComponent|null):VueComponent|null{
if(parent == null) return null;
// #ifdef WEB||APP-IOS|| MP-WEIXIN
if(parent.$parent?.id?.indexOf('xForm')>-1) return parent.$parent;
// #endif
// #ifdef APP-HARMONY
if(parent.$parent?.$options?.name?.indexOf('xForm')>-1) return parent.$parent;
// #endif
// #ifdef APP-ANDROID
if(parent.$parent instanceof XFormComponentPublicInstance) return parent.$parent;
// #endif
let parents = this.findParent(parent.$parent)
// #ifdef WEB||APP-IOS || MP-WEIXIN
if(parents?.id?.indexOf('xForm')>-1) return parents;
// #endif
// #ifdef APP-HARMONY
if(parents?.$options?.name?.indexOf('xForm')>-1) return parents;
// #endif
// #ifdef APP-ANDROID
if(parents instanceof XFormComponentPublicInstance) return parents;
// #endif
return null;
}
}
}
</script>
<template>
<view class="xFormItem" ref="xFormItem" :style="[
_bordrBottomSolid,
{
paddingTop: _cellPadding[0],
paddingBottom: _cellPadding[1]
}
]">
<view class="xFormIitemWrap" :style="{flexDirection:_labelDirection=='horizontal'?'row':'column'}">
<view v-if="_showLabel" class="xFormIteLabel" :style="{
width:_labelDirection=='horizontal'?_labelWidth:'auto',
paddingBottom:_labelDirection=='horizontal'?'0':_labelPadding[1],
paddingTop:_labelDirection=='horizontal'?'0':_labelPadding[0],
'justify-content': _labelAlign
}">
<!--
@slot 标题
-->
<slot name="label">
<text v-if="_showRequired&&_required"
:style="{fontSize:_labelFontSize,color:'red',paddingRight:'8rpx'}">*</text>
<x-text v-if="_label" :font-size="_labelFontSize" :color="_labelFontColor">
{{_label}}
</x-text>
</slot>
</view>
<view class="xFormIteContent" :style="[{flex:_labelDirection=='horizontal'?'1':'auto'},_contentStyle]">
<!--
@slot 默认内容区域
-->
<slot></slot>
</view>
</view>
<view v-if="isError&&!first&&_required&&_showError" class="xFormItemError">
<!--
@slot 出错提示的插槽
-->
<slot name="error">
<text class="xFormItemErrorText" :style="{fontSize:_errorFontsize,textAlign:XFORMITEM_ERROR_ALIGN}">{{errorText}}</text>
</slot>
</view>
</view>
</template>
<style scoped>
.xFormIteLabel {
display: flex;
flex-direction: row;
align-items: center;
}
.xFormItem {
display: flex;
flex-direction: column;
}
.xFormItemErrorText {
color: red;
padding-top: 5px;
display: flex;
}
.xFormIitemWrap{
display: flex;
}
</style>