first commit
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
:: BASE_DOC ::
|
||||
|
||||
## API
|
||||
|
||||
### FormItem Props
|
||||
|
||||
name | type | default | description | required
|
||||
-- | -- | -- | -- | --
|
||||
custom-style | Object | - | CSS(Cascading Style Sheets) | N
|
||||
arrow | Boolean | false | \- | N
|
||||
content-align | String | - | options: left/right | N
|
||||
for | String | - | \- | N
|
||||
help | String | - | \- | N
|
||||
label | String | '' | \- | N
|
||||
label-align | String | - | options: left/right/top | N
|
||||
label-width | String / Number | - | \- | N
|
||||
name | String | - | \- | N
|
||||
required-mark | Boolean | undefined | \- | N
|
||||
rules | Array | - | Typescript: `Array<FormRule>` | N
|
||||
show-error-message | Boolean | undefined | \- | N
|
||||
|
||||
### FormItem Slots
|
||||
|
||||
name | Description
|
||||
-- | --
|
||||
help | \-
|
||||
label | \-
|
||||
|
||||
### CSS Variables
|
||||
|
||||
The component provides the following CSS variables, which can be used to customize styles.
|
||||
Name | Default Value | Description
|
||||
-- | -- | --
|
||||
--td-form-item-horizontal-padding | 32rpx | -
|
||||
--td-form-item-justify-content | space-between | -
|
||||
--td-form-item-label-width | 160rpx | -
|
||||
--td-form-item-vertical-padding | 32rpx | -
|
||||
37
uni_modules/tdesign-uniapp/components/form-item/README.md
Normal file
37
uni_modules/tdesign-uniapp/components/form-item/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
:: BASE_DOC ::
|
||||
|
||||
## API
|
||||
|
||||
### FormItem Props
|
||||
|
||||
名称 | 类型 | 默认值 | 描述 | 必传
|
||||
-- | -- | -- | -- | --
|
||||
custom-style | Object | - | 自定义样式 | N
|
||||
arrow | Boolean | false | 是否显示右侧箭头 | N
|
||||
content-align | String | - | 表单内容对齐方式,优先级高于 Form.contentAlign。可选项:left/right | N
|
||||
for | String | - | label 原生属性 | N
|
||||
help | String | - | 表单项说明内容 | N
|
||||
label | String | '' | 字段标签名称 | N
|
||||
label-align | String | - | 表单字段标签对齐方式:左对齐、右对齐、顶部对齐。默认使用 Form 的对齐方式,优先级高于 Form.labelAlign。可选项:left/right/top | N
|
||||
label-width | String / Number | - | 可以整体设置标签宽度,优先级高于 Form.labelWidth | N
|
||||
name | String | - | 表单字段名称 | N
|
||||
required-mark | Boolean | undefined | 是否显示必填符号(*),优先级高于 Form.requiredMark | N
|
||||
rules | Array | - | 表单字段校验规则。TS 类型:`Array<FormRule>` | N
|
||||
show-error-message | Boolean | undefined | 校验不通过时,是否显示错误提示信息,优先级高于 `Form.showErrorMessage` | N
|
||||
|
||||
### FormItem Slots
|
||||
|
||||
名称 | 描述
|
||||
-- | --
|
||||
help | 自定义 `help` 显示内容
|
||||
label | 自定义 `label` 显示内容
|
||||
|
||||
### CSS Variables
|
||||
|
||||
组件提供了下列 CSS 变量,可用于自定义样式。
|
||||
名称 | 默认值 | 描述
|
||||
-- | -- | --
|
||||
--td-form-item-horizontal-padding | 32rpx | -
|
||||
--td-form-item-justify-content | space-between | -
|
||||
--td-form-item-label-width | 160rpx | -
|
||||
--td-form-item-vertical-padding | 32rpx | -
|
||||
120
uni_modules/tdesign-uniapp/components/form-item/form-item.css
Normal file
120
uni_modules/tdesign-uniapp/components/form-item/form-item.css
Normal file
@@ -0,0 +1,120 @@
|
||||
/* Less 支持测试 - 监听功能验证 */
|
||||
.t-form-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: var(--td-form-item-justify-content, space-between);
|
||||
width: 100%;
|
||||
padding: var(--td-form-item-horizontal-padding, 32rpx) var(--td-form-item-vertical-padding, 32rpx);
|
||||
background-color: #ffffff;
|
||||
--td-input-vertical-padding: 0rpx;
|
||||
--td-textarea-vertical-padding: 0rpx;
|
||||
--td-textarea-horizontal-padding: 0rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.t-form-item--top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.t-form-item--bordered {
|
||||
border-bottom: 1rpx solid var(--td-border-color, var(--td-gray-color-3, #e7e7e7));
|
||||
}
|
||||
.t-form-item--error .t-form-item__error {
|
||||
color: var(--td-error-color, var(--td-error-color-6, #d54941));
|
||||
}
|
||||
.t-form-item--success .t-form-item__success {
|
||||
color: var(--td-success-color, var(--td-success-color-5, #2ba471));
|
||||
}
|
||||
.t-form-item__label {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: var(--td-form-item-label-width, 160rpx);
|
||||
margin-right: 16rpx;
|
||||
font-size: var(--td-font-size-m, 32rpx);
|
||||
color: var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
|
||||
line-height: 1.5;
|
||||
}
|
||||
.t-form-item__label--required {
|
||||
color: var(--td-error-color, var(--td-error-color-6, #d54941));
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
.t-form-item__label--colon {
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
.t-form-item__label--left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.t-form-item__label--right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.t-form-item__label--top {
|
||||
position: relative;
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.t-form-item__controls {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.t-form-item__controls--left {
|
||||
text-align: left;
|
||||
}
|
||||
.t-form-item__controls--right {
|
||||
text-align: right;
|
||||
}
|
||||
.t-form-item__controls-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.t-form-item__help {
|
||||
font-size: var(--td-font-size-m, 32rpx);
|
||||
color: var(--td-font-gray-1, rgba(0, 0, 0, 0.9));
|
||||
line-height: 1.4;
|
||||
}
|
||||
.t-form-item__desc-link {
|
||||
margin-top: 8rpx;
|
||||
color: var(--td-primary-color-7, #0052d9);
|
||||
font-size: var(--td-font-size-s, 24rpx);
|
||||
line-height: 1.4;
|
||||
}
|
||||
.t-form-item__error {
|
||||
margin-top: 4rpx;
|
||||
font-size: var(--td-font-size-s, 24rpx);
|
||||
line-height: 1.4;
|
||||
}
|
||||
.t-form-item__error--error {
|
||||
color: var(--td-error-color, var(--td-error-color-6, #d54941));
|
||||
}
|
||||
.t-form-item__error--warning {
|
||||
color: var(--td-warning-color, var(--td-warning-color-5, #e37318));
|
||||
}
|
||||
.t-form-item__success {
|
||||
margin-top: 4rpx;
|
||||
font-size: var(--td-font-size-s, 24rpx);
|
||||
color: var(--td-success-color, var(--td-success-color-5, #2ba471));
|
||||
line-height: 1.4;
|
||||
}
|
||||
.t-form-item__arrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 8rpx;
|
||||
color: var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)));
|
||||
}
|
||||
.t-form-item__extra {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
.desc-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
height: 50rpx;
|
||||
--td-button-font-weight: 400;
|
||||
}
|
||||
396
uni_modules/tdesign-uniapp/components/form-item/form-item.vue
Normal file
396
uni_modules/tdesign-uniapp/components/form-item/form-item.vue
Normal file
@@ -0,0 +1,396 @@
|
||||
<template>
|
||||
<view
|
||||
:class="
|
||||
classPrefix +
|
||||
' ' + classPrefix + '--' + dataLabelAlign +
|
||||
' ' + classPrefix + '--bordered' +
|
||||
' ' + classPrefix + '--' + (verifyStatus === 2 ? 'error' : verifyStatus === 1 ? 'success' : '') +
|
||||
' ' + tClass
|
||||
"
|
||||
:style="tools._style([customStyle])"
|
||||
>
|
||||
<view
|
||||
v-if="label"
|
||||
:class="classPrefix + '__label ' + classPrefix + '__label--' + dataLabelAlign + ' ' + tClassLabel"
|
||||
:style="'width: ' + dataLabelWidth"
|
||||
>
|
||||
<text
|
||||
v-if="dataRequiredMark && requiredMarkPosition === 'left'"
|
||||
:class="classPrefix + '__label--required'"
|
||||
>
|
||||
*
|
||||
</text>
|
||||
<slot
|
||||
name="label"
|
||||
>
|
||||
<text>
|
||||
{{ label }}
|
||||
</text>
|
||||
</slot>
|
||||
<text
|
||||
v-if="dataRequiredMark && requiredMarkPosition === 'right'"
|
||||
:class="classPrefix + '__label--required'"
|
||||
>
|
||||
*
|
||||
</text>
|
||||
<text
|
||||
v-if="colon"
|
||||
:class="classPrefix + '__label--colon'"
|
||||
>
|
||||
:
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
:class="classPrefix + '__controls ' + tClassControls"
|
||||
:style="contentStyle"
|
||||
>
|
||||
<view :class="classPrefix + '__controls-content ' + classPrefix + '__controls-content--' + dataContentAlign">
|
||||
<slot />
|
||||
<view
|
||||
v-if="arrow"
|
||||
:class="classPrefix + '__arrow'"
|
||||
>
|
||||
<t-icon
|
||||
name="chevron-right"
|
||||
size="16"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
:class="classPrefix + '__help ' + classPrefix + '__help--' + dataContentAlign + tClassHelp"
|
||||
:style="errorPosition"
|
||||
>
|
||||
<slot name="help">
|
||||
{{ help }}
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="errorList.length > 0 && dataShowErrorMessage"
|
||||
:class="classPrefix + '__error ' + classPrefix + '__error--' + (errorList[0].type || 'error')"
|
||||
>
|
||||
{{ errorList[0].message }}
|
||||
</view>
|
||||
<view
|
||||
v-if="successList.length > 0"
|
||||
:class="classPrefix + '__success'"
|
||||
>
|
||||
{{ successList[0].message }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view :class="classPrefix + '__extra ' + tClassExtra">
|
||||
<slot name="extra" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { uniComponent } from '../common/src/index';
|
||||
import { prefix } from '../common/config';
|
||||
import props from './props';
|
||||
import { validate, ValidateStatus } from './form-model';
|
||||
import TIcon from '../icon/icon.vue';
|
||||
import TButton from '../button/button.vue';
|
||||
import { ChildrenMixin, RELATION_MAP } from '../common/relation';
|
||||
import tools from '../common/utils.wxs';
|
||||
import { isNumber } from '../common/validator';
|
||||
|
||||
const name = `${prefix}-form-item`;
|
||||
|
||||
export default uniComponent({
|
||||
name,
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
externalClasses: [
|
||||
`${prefix}-class`,
|
||||
`${prefix}-class-label`,
|
||||
`${prefix}-class-controls`,
|
||||
`${prefix}-class-help`,
|
||||
`${prefix}-class-extra`,
|
||||
],
|
||||
provide() {
|
||||
return {
|
||||
[RELATION_MAP.FormKey]: this,
|
||||
};
|
||||
},
|
||||
mixins: [ChildrenMixin(RELATION_MAP.FormItem)],
|
||||
components: {
|
||||
TIcon,
|
||||
TButton,
|
||||
},
|
||||
props: {
|
||||
...props,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
prefix,
|
||||
classPrefix: name,
|
||||
errorList: [],
|
||||
successList: [],
|
||||
verifyStatus: ValidateStatus.TO_BE_VALIDATED,
|
||||
needResetField: false,
|
||||
resetValidating: false,
|
||||
// rules: [],
|
||||
form: {},
|
||||
colon: false,
|
||||
// showErrorMessage: true,
|
||||
|
||||
tools,
|
||||
dataRules: this.rules,
|
||||
dataLabelAlign: this.labelAlign,
|
||||
dataLabelWidth: this.labelWidth,
|
||||
dataRequiredMark: this.requiredMark,
|
||||
dataShowErrorMessage: this.showErrorMessage,
|
||||
dataContentAlign: this.contentAlign,
|
||||
|
||||
errorPosition: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
contentStyle() {
|
||||
const { labelWidth, labelAlign } = this;
|
||||
let contentStyle = {};
|
||||
if (labelWidth && labelAlign !== 'top') {
|
||||
if (isNumber(labelWidth)) {
|
||||
contentStyle = { marginLeft: `${labelWidth}px` };
|
||||
} else {
|
||||
contentStyle = { marginLeft: labelWidth };
|
||||
}
|
||||
}
|
||||
|
||||
return tools._style(contentStyle);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
created() {
|
||||
// this.initFormItem();
|
||||
},
|
||||
onBeforeUnmount() {
|
||||
if (this.form) {
|
||||
this.form.unregisterChild(this.name);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
innerAfterLinked() {
|
||||
const target = this[RELATION_MAP.FormItem];
|
||||
target.registerChild(this);
|
||||
this.form = target;
|
||||
this.initFormItem();
|
||||
|
||||
const { requiredMark, labelAlign, labelWidth, showErrorMessage, contentAlign } = this;
|
||||
const isRequired = target.rules[this.name]?.some(rule => rule.required);
|
||||
|
||||
this.dataRules = target.rules[this.name];
|
||||
this.colon = target.colon;
|
||||
this.dataLabelAlign = labelAlign || target.labelAlign || 'right';
|
||||
this.dataLabelWidth = labelWidth || target.labelWidth;
|
||||
this.dataContentAlign = contentAlign || target.contentAlign,
|
||||
this.dataRequiredMark = (() => {
|
||||
if (!isRequired) {
|
||||
return false;
|
||||
}
|
||||
if (typeof requiredMark === 'boolean') {
|
||||
return requiredMark;
|
||||
}
|
||||
return target.requiredMark || false;
|
||||
})();
|
||||
this.dataShowErrorMessage = typeof showErrorMessage === 'boolean' ? showErrorMessage : target.showErrorMessage;
|
||||
this.requiredMarkPosition = target.requiredMarkPosition;
|
||||
|
||||
setTimeout(() => {
|
||||
this.errorPosition = this.dataLabelAlign !== 'top' && `text-align: ${this.dataContentAlign}`;
|
||||
}, 33);
|
||||
},
|
||||
innerAfterUnlinked() {
|
||||
if (this.form) {
|
||||
this.form.unregisterChild(this.name);
|
||||
}
|
||||
},
|
||||
// 处理描述信息链接点击事件
|
||||
handlePreviewImage(e) {
|
||||
const { url } = e.currentTarget.dataset;
|
||||
const urls = url.map(item => item.url) || [];
|
||||
if (url) {
|
||||
uni.previewImage({
|
||||
urls,
|
||||
current: urls[0],
|
||||
});
|
||||
}
|
||||
},
|
||||
// 初始化表单项
|
||||
initFormItem() {
|
||||
this.setInitialValue();
|
||||
},
|
||||
|
||||
// 设置初始值
|
||||
setInitialValue() {
|
||||
const { name } = this;
|
||||
if (name && this.form) {
|
||||
const { formData } = this.form;
|
||||
this.initialValue = formData[name];
|
||||
}
|
||||
},
|
||||
|
||||
// 获取表单数据
|
||||
getFormData() {
|
||||
if (this.form) {
|
||||
return this.form.formData;
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
// 获取当前值
|
||||
getValue() {
|
||||
const { name } = this;
|
||||
if (name && this.form) {
|
||||
const { formData } = this.form;
|
||||
return formData[name];
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
// 获取验证规则
|
||||
getRules() {
|
||||
const { rules } = this;
|
||||
|
||||
// 优先使用组件自身的规则
|
||||
if (rules && rules.length > 0) {
|
||||
return rules;
|
||||
}
|
||||
|
||||
// 使用表单的规则
|
||||
return this.dataRules || [];
|
||||
},
|
||||
|
||||
// 验证表单项
|
||||
async validate(data, trigger, showErrorMessage) {
|
||||
const rules = this.getRules();
|
||||
if (rules.length === 0) {
|
||||
return { [this.name]: true };
|
||||
}
|
||||
|
||||
// 根据触发方式过滤规则
|
||||
const filteredRules = trigger === 'all'
|
||||
? rules
|
||||
: rules.filter(rule => (rule.trigger || 'change') === trigger);
|
||||
|
||||
if (filteredRules.length === 0) {
|
||||
return { [this.name]: true };
|
||||
}
|
||||
|
||||
const value = data[this.name];
|
||||
const results = await validate(value, filteredRules);
|
||||
|
||||
// 分析验证结果
|
||||
const analysis = this.analysisValidateResult(results);
|
||||
|
||||
// 更新状态
|
||||
this.updateValidateStatus(analysis, showErrorMessage);
|
||||
|
||||
// 返回验证结果
|
||||
const result = {};
|
||||
result[this.name] = analysis.errorList.length > 0 ? analysis.errorList : true;
|
||||
return result;
|
||||
},
|
||||
|
||||
// 纯净验证(不显示错误信息)
|
||||
async validateOnly(trigger) {
|
||||
return this.validate(this.getFormData(), trigger, false);
|
||||
},
|
||||
|
||||
// 分析验证结果
|
||||
analysisValidateResult(results) {
|
||||
const errorList = results.filter(item => item.result !== true);
|
||||
const successList = results.filter(item => item.result === true && item.message && item.type === 'success');
|
||||
|
||||
return {
|
||||
errorList,
|
||||
successList,
|
||||
resultList: results,
|
||||
};
|
||||
},
|
||||
|
||||
// 更新验证状态
|
||||
updateValidateStatus(analysis) {
|
||||
const { errorList, successList } = analysis;
|
||||
|
||||
this.errorList = errorList;
|
||||
this.successList = successList;
|
||||
this.verifyStatus = errorList.length > 0 ? ValidateStatus.FAIL : ValidateStatus.SUCCESS;
|
||||
},
|
||||
|
||||
// 清空验证结果
|
||||
clearValidate() {
|
||||
this.errorList = [];
|
||||
this.successList = [];
|
||||
this.verifyStatus = ValidateStatus.TO_BE_VALIDATED;
|
||||
},
|
||||
|
||||
// 重置字段
|
||||
resetField() {
|
||||
const { name } = this;
|
||||
if (name && this.form) {
|
||||
const { resetType } = this.form;
|
||||
|
||||
if (resetType === 'initial') {
|
||||
this.form.updateFormData(name, this.initialValue);
|
||||
} else {
|
||||
this.form.updateFormData(name, this.getEmptyValue());
|
||||
}
|
||||
}
|
||||
|
||||
this.clearValidate();
|
||||
},
|
||||
|
||||
// 设置验证信息
|
||||
setValidateMessage(validateMessage) {
|
||||
this.errorList = validateMessage.filter(item => item.type === 'error');
|
||||
this.successList = validateMessage.filter(item => item.type === 'warning');
|
||||
this.verifyStatus = validateMessage.length > 0 ? ValidateStatus.FAIL : ValidateStatus.SUCCESS;
|
||||
},
|
||||
|
||||
// 获取空值
|
||||
getEmptyValue() {
|
||||
const value = this.getValue();
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return [];
|
||||
}
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return {};
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return 0;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
// 处理值变化
|
||||
onValueChange(value) {
|
||||
const { name } = this;
|
||||
if (name && this.form) {
|
||||
this.form.updateFormData(name, value);
|
||||
|
||||
// 触发change验证
|
||||
this.validate(this.getFormData(), 'change', true);
|
||||
}
|
||||
},
|
||||
|
||||
// 处理失焦事件
|
||||
onBlur() {
|
||||
// 触发blur验证
|
||||
this.validate(this.getFormData(), 'blur', true);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
@import './form-item.css';
|
||||
</style>
|
||||
198
uni_modules/tdesign-uniapp/components/form-item/form-model.ts
Normal file
198
uni_modules/tdesign-uniapp/components/form-item/form-model.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
// 验证结果接口
|
||||
interface ValidateResult {
|
||||
result: boolean;
|
||||
message?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
// 工具函数:安全获取rule属性的实际值
|
||||
function getRuleValue(field: any, key?: string): any {
|
||||
if (field && typeof field === 'object' && 'type' in field) {
|
||||
// 针对type字段,过滤掉'error'和'warning'
|
||||
if (key === 'type') {
|
||||
const { value } = field;
|
||||
if (value === 'error' || value === 'warning') {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
// type字段只允许string
|
||||
if (key === 'type') {
|
||||
if (field === 'error' || field === 'warning') {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
// 验证状态枚举
|
||||
export const ValidateStatus = {
|
||||
TO_BE_VALIDATED: 0,
|
||||
SUCCESS: 1,
|
||||
FAIL: 2,
|
||||
};
|
||||
|
||||
// 执行单个验证规则
|
||||
async function executeRule(value: any, rule: any): Promise<ValidateResult> {
|
||||
const result: ValidateResult = {
|
||||
result: true,
|
||||
};
|
||||
|
||||
// 必填验证
|
||||
const required = getRuleValue(rule.required);
|
||||
if (required && (value === undefined || value === null || value === '' || value.length === 0)) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || '此字段为必填项';
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
|
||||
// 如果值为空且不是必填,则跳过其他验证
|
||||
if (value === undefined || value === null || value === '' || value.length === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 列表最小值验证
|
||||
const min = getRuleValue(rule.min);
|
||||
if (min !== undefined) {
|
||||
const val = Array.isArray(value) ? value.length : Number(value);
|
||||
if (val < Number(min)) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `不能小于 ${min}`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 列表最大值验证
|
||||
const max = getRuleValue(rule.max);
|
||||
if (max !== undefined) {
|
||||
const val = Array.isArray(value) ? value.length : Number(value);
|
||||
if (val > Number(max)) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `不能大于 ${max}`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// 字符串最大长度验证
|
||||
const maxLength = getRuleValue(rule.maxLength);
|
||||
if (maxLength !== undefined) {
|
||||
const len = String(value).length;
|
||||
if (len > Number(maxLength)) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `长度不能超过 ${maxLength}`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 字符串最小长度验证
|
||||
const minLength = getRuleValue(rule.minLength);
|
||||
if (minLength !== undefined) {
|
||||
const len = String(value).length;
|
||||
if (len < Number(minLength)) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `长度不能少于 ${minLength}`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 固定长度验证
|
||||
const len = getRuleValue(rule.len);
|
||||
if (len !== undefined) {
|
||||
const strValue = String(value);
|
||||
if (strValue.length !== Number(len)) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `长度必须为 ${len}`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 正则验证
|
||||
const pattern = getRuleValue(rule.pattern);
|
||||
if (pattern) {
|
||||
let regex;
|
||||
if (pattern instanceof RegExp) {
|
||||
regex = pattern;
|
||||
} else if (typeof pattern === 'string') {
|
||||
regex = new RegExp(pattern);
|
||||
} else if (pattern && typeof pattern === 'object' && 'test' in pattern) {
|
||||
regex = pattern;
|
||||
} else {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || '格式不正确';
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
if (!regex.test(String(value))) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || '格式不正确';
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 类型验证
|
||||
const type = getRuleValue(rule.type, 'type');
|
||||
if (typeof type === 'string') {
|
||||
let isValid = true;
|
||||
if (type === 'email') {
|
||||
isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value));
|
||||
if (!isValid) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `${type} 格式不正确`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
} else if (type === 'url') {
|
||||
try {
|
||||
const url = new URL(String(value));
|
||||
isValid = !!url;
|
||||
} catch (err) {
|
||||
isValid = false;
|
||||
}
|
||||
if (!isValid) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `${type} 格式不正确`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
} else if (type === 'number') {
|
||||
isValid = !Number.isNaN(Number(value));
|
||||
if (!isValid) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || `${type} 格式不正确`;
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 自定义验证
|
||||
const validator = getRuleValue(rule.validator);
|
||||
if (validator) {
|
||||
const validateResult = await validator(value);
|
||||
if (!validateResult) {
|
||||
result.result = false;
|
||||
result.message = getRuleValue(rule.message) || '验证失败';
|
||||
result.type = 'error';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 验证函数
|
||||
export async function validate(value: any, rules: any[]): Promise<ValidateResult[]> {
|
||||
const results: ValidateResult[] = [];
|
||||
|
||||
const promises = rules.map(rule => executeRule(value, rule));
|
||||
const ruleResults = await Promise.all(promises);
|
||||
results.push(...ruleResults);
|
||||
|
||||
return results;
|
||||
}
|
||||
64
uni_modules/tdesign-uniapp/components/form-item/props.ts
Normal file
64
uni_modules/tdesign-uniapp/components/form-item/props.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
|
||||
* */
|
||||
|
||||
import type { TdFormItemProps } from './type';
|
||||
export default {
|
||||
/** 是否显示右侧箭头 */
|
||||
arrow: Boolean,
|
||||
/** 表单内容对齐方式,优先级高于 Form.contentAlign */
|
||||
contentAlign: {
|
||||
type: String,
|
||||
validator(val: TdFormItemProps['contentAlign']): boolean {
|
||||
if (!val) return true;
|
||||
return ['left', 'right'].includes(val);
|
||||
},
|
||||
},
|
||||
/** label 原生属性 */
|
||||
for: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
/** 表单项说明内容 */
|
||||
help: {
|
||||
type: String,
|
||||
},
|
||||
/** 字段标签名称 */
|
||||
label: {
|
||||
type: String,
|
||||
default: '' as TdFormItemProps['label'],
|
||||
},
|
||||
/** 表单字段标签对齐方式:左对齐、右对齐、顶部对齐。默认使用 Form 的对齐方式,优先级高于 Form.labelAlign */
|
||||
labelAlign: {
|
||||
type: String,
|
||||
validator(val: TdFormItemProps['labelAlign']): boolean {
|
||||
if (!val) return true;
|
||||
return ['left', 'right', 'top'].includes(val);
|
||||
},
|
||||
},
|
||||
/** 可以整体设置标签宽度,优先级高于 Form.labelWidth */
|
||||
labelWidth: {
|
||||
type: [String, Number],
|
||||
},
|
||||
/** 表单字段名称 */
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
/** 是否显示必填符号(*),优先级高于 Form.requiredMark */
|
||||
requiredMark: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
/** 表单字段校验规则 */
|
||||
rules: {
|
||||
type: Array,
|
||||
},
|
||||
/** 校验不通过时,是否显示错误提示信息,优先级高于 `Form.showErrorMessage` */
|
||||
showErrorMessage: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
};
|
||||
7
uni_modules/tdesign-uniapp/components/form-item/type.ts
Normal file
7
uni_modules/tdesign-uniapp/components/form-item/type.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
|
||||
* */
|
||||
|
||||
export type { TdFormItemProps} from '../form/type';
|
||||
Reference in New Issue
Block a user