From f26f29d84e0a68831a6af14dab3eec5500496d2e Mon Sep 17 00:00:00 2001 From: spring <2396852758@qq.com> Date: 星期三, 28 五月 2025 16:48:52 +0800 Subject: [PATCH] 初始化项目 --- uview-ui/components/u-form-item/u-form-item.vue | 431 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 431 insertions(+), 0 deletions(-) diff --git a/uview-ui/components/u-form-item/u-form-item.vue b/uview-ui/components/u-form-item/u-form-item.vue new file mode 100644 index 0000000..d6b8c8b --- /dev/null +++ b/uview-ui/components/u-form-item/u-form-item.vue @@ -0,0 +1,431 @@ +<template> + <view class="u-form-item" :class="{'u-border-bottom': elBorderBottom, 'u-form-item__border-bottom--error': validateState === 'error' && showError('border-bottom')}"> + <view class="u-form-item__body" :style="{ + flexDirection: elLabelPosition == 'left' ? 'row' : 'column' + }"> + <!-- 寰俊灏忕▼搴忎腑锛屽皢涓�涓弬鏁拌缃┖瀛楃涓诧紝缁撴灉浼氬彉鎴愬瓧绗︿覆"true" --> + <view class="u-form-item--left" :style="{ + width: uLabelWidth, + flex: `0 0 ${uLabelWidth}`, + marginBottom: elLabelPosition == 'left' ? 0 : '10rpx', + }"> + <!-- 涓轰簡鍧楀榻� --> + <view class="u-form-item--left__content" v-if="required || leftIcon || label"> + <!-- nvue涓嶆敮鎸佷吉鍏冪礌before --> + <text v-if="required" class="u-form-item--left__content--required">*</text> + <view class="u-form-item--left__content__icon" v-if="leftIcon"> + <u-icon :name="leftIcon" :custom-style="leftIconStyle"></u-icon> + </view> + <view class="u-form-item--left__content__label" :style="[elLabelStyle, { + 'justify-content': elLabelAlign == 'left' ? 'flex-start' : elLabelAlign == 'center' ? 'center' : 'flex-end' + }]"> + {{label}} + </view> + </view> + </view> + <view class="u-form-item--right u-flex"> + <view class="u-form-item--right__content"> + <view class="u-form-item--right__content__slot "> + <slot /> + </view> + <view class="u-form-item--right__content__icon u-flex" v-if="$slots.right || rightIcon"> + <u-icon :custom-style="rightIconStyle" v-if="rightIcon" :name="rightIcon"></u-icon> + <slot name="right" /> + </view> + </view> + </view> + </view> + <view class="u-form-item__message" v-if="validateState === 'error' && showError('message')" :style="{ + paddingLeft: elLabelPosition == 'left' ? $u.addUnit(elLabelWidth) : '0', + }">{{validateMessage}}</view> + </view> +</template> + +<script> + import Emitter from '../../libs/util/emitter.js'; + import schema from '../../libs/util/async-validator'; + // 鍘婚櫎璀﹀憡淇℃伅 + schema.warning = function() {}; + + /** + * form-item 琛ㄥ崟item + * @description 姝ょ粍浠朵竴鑸敤浜庤〃鍗曞満鏅紝鍙互閰嶇疆Input杈撳叆妗嗭紝Select寮瑰嚭妗嗭紝杩涜琛ㄥ崟楠岃瘉绛夈�� + * @tutorial http://uviewui.com/components/form.html + * @property {String} label 宸︿晶鎻愮ず鏂囧瓧 + * @property {Object} prop 琛ㄥ崟鍩焟odel瀵硅薄鐨勫睘鎬у悕锛屽湪浣跨敤 validate銆乺esetFields 鏂规硶鐨勬儏鍐典笅锛岃灞炴�ф槸蹇呭~鐨� + * @property {Boolean} border-bottom 鏄惁鏄剧ず琛ㄥ崟鍩熺殑涓嬪垝绾胯竟妗� + * @property {String} label-position 琛ㄥ崟鍩熸彁绀烘枃瀛楃殑浣嶇疆锛宭eft-宸︿晶锛宼op-涓婃柟 + * @property {String Number} label-width 鎻愮ず鏂囧瓧鐨勫搴︼紝鍗曚綅rpx锛堥粯璁�90锛� + * @property {Object} label-style lable鐨勬牱寮忥紝瀵硅薄褰㈠紡 + * @property {String} label-align lable鐨勫榻愭柟寮� + * @property {String} right-icon 鍙充晶鑷畾涔夊瓧浣撳浘鏍�(闄恥View鍐呯疆鍥炬爣)鎴栧浘鐗囧湴鍧� + * @property {String} left-icon 宸︿晶鑷畾涔夊瓧浣撳浘鏍�(闄恥View鍐呯疆鍥炬爣)鎴栧浘鐗囧湴鍧� + * @property {Object} left-icon-style 宸︿晶鍥炬爣鐨勬牱寮忥紝瀵硅薄褰㈠紡 + * @property {Object} right-icon-style 鍙充晶鍥炬爣鐨勬牱寮忥紝瀵硅薄褰㈠紡 + * @property {Boolean} required 鏄惁鏄剧ず宸﹁竟鐨�"*"鍙凤紝杩欓噷浠呰捣灞曠ず浣滅敤锛屽闇�鏍¢獙蹇呭~锛岃閫氳繃rules閰嶇疆蹇呭~瑙勫垯(榛樿false) + * @example <u-form-item label="濮撳悕"><u-input v-model="form.name" /></u-form-item> + */ + + export default { + name: 'u-form-item', + mixins: [Emitter], + inject: { + uForm: { + default () { + return null + } + } + }, + props: { + // input鐨刲abel鎻愮ず璇� + label: { + type: String, + default: '' + }, + // 缁戝畾鐨勫�� + prop: { + type: String, + default: '' + }, + // 鏄惁鏄剧ず琛ㄥ崟鍩熺殑涓嬪垝绾胯竟妗� + borderBottom: { + type: [String, Boolean], + default: '' + }, + // label鐨勪綅缃紝left-宸﹁竟锛宼op-涓婅竟 + labelPosition: { + type: String, + default: '' + }, + // label鐨勫搴︼紝鍗曚綅rpx + labelWidth: { + type: [String, Number], + default: '' + }, + // lable鐨勬牱寮忥紝瀵硅薄褰㈠紡 + labelStyle: { + type: Object, + default () { + return {} + } + }, + // lable瀛椾綋鐨勫榻愭柟寮� + labelAlign: { + type: String, + default: '' + }, + // 鍙充晶鍥炬爣 + rightIcon: { + type: String, + default: '' + }, + // 宸︿晶鍥炬爣 + leftIcon: { + type: String, + default: '' + }, + // 宸︿晶鍥炬爣鐨勬牱寮� + leftIconStyle: { + type: Object, + default () { + return {} + } + }, + // 宸︿晶鍥炬爣鐨勬牱寮� + rightIconStyle: { + type: Object, + default () { + return {} + } + }, + // 鏄惁鏄剧ず宸﹁竟鐨勫繀濉槦鍙凤紝鍙綔鏄剧ず鐢紝鍏蜂綋鏍¢獙蹇呭~鐨勯�昏緫锛岃鍦╮ules涓厤缃� + required: { + type: Boolean, + default: false + } + }, + data() { + return { + initialValue: '', // 瀛樺偍鐨勯粯璁ゅ�� + // isRequired: false, // 鏄惁蹇呭~锛岀敱浜庝汉鎬у寲鑰冭檻锛屽繀濉�"*"鍙烽�氳繃props鐨剅equired閰嶇疆锛屼笉鍐嶉�氳繃rules鐨勮鍒欒嚜鍔ㄧ敓鎴� + validateState: '', // 鏄惁鏍¢獙鎴愬姛 + validateMessage: '', // 鏍¢獙澶辫触鐨勬彁绀鸿 + // 鏈夐敊璇椂鐨勬彁绀烘柟寮忥紝message-鎻愮ず淇℃伅锛宐order-濡傛灉input璁剧疆浜嗚竟妗嗭紝鍙樻垚鍛堢孩鑹诧紝 + errorType: ['message'], + fieldValue: '', // 鑾峰彇褰撳墠瀛愮粍浠秈nput鐨勮緭鍏ョ殑鍊� + // 鐖剁粍浠剁殑鍙傛暟锛屽湪computed璁$畻涓紝鏃犳硶寰楃煡this.parent鍙戠敓鍙樺寲锛屾晠灏嗙埗缁勪欢鐨勫弬鏁板�硷紝鏀惧埌data涓� + parentData: { + borderBottom: true, + labelWidth: 90, + labelPosition: 'left', + labelStyle: {}, + labelAlign: 'left', + } + }; + }, + watch: { + validateState(val) { + this.broadcastInputError(); + }, + // 鐩戝惉u-form缁勪欢鐨別rrorType鐨勫彉鍖� + "uForm.errorType"(val) { + this.errorType = val; + this.broadcastInputError(); + }, + }, + computed: { + // 璁$畻鍚庣殑label瀹藉害锛岀敱浜庨渶瑕佸涓垽鏂紝鏁呮斁鍒癱omputed涓� + uLabelWidth() { + // 濡傛灉鐢ㄦ埛璁剧疆label涓虹┖瀛楃涓�(寰俊灏忕▼搴忕┖瀛楃涓叉渶缁堜細鍙樻垚瀛楃涓茬殑'true')锛屾剰鍛崇潃瑕佸皢label鐨勪綅缃搴﹁缃负auto + return this.elLabelPosition == 'left' ? (this.label === 'true' || this.label === '' ? 'auto' : this.$u.addUnit(this + .elLabelWidth)) : '100%'; + }, + showError() { + return type => { + // 濡傛灉errorType鏁扮粍涓惈鏈塶one锛屾垨鑰卼oast鎻愮ず绫诲瀷 + if (this.errorType.indexOf('none') >= 0) return false; + else if (this.errorType.indexOf(type) >= 0) return true; + else return false; + } + }, + // label鐨勫搴� + elLabelWidth() { + // label榛樿瀹藉害涓�90锛屼紭鍏堜娇鐢ㄦ湰缁勪欢鐨勫�硷紝濡傛灉娌℃湁(濡傛灉璁剧疆涓�0锛屼篃绠楁槸閰嶇疆浜嗗�硷紝渚濈劧璧锋晥)锛屽垯鐢╱-form鐨勫�� + return (this.labelWidth != 0 || this.labelWidth != '') ? this.labelWidth : (this.parentData.labelWidth ? this.parentData + .labelWidth : + 90); + }, + // label鐨勬牱寮� + elLabelStyle() { + return Object.keys(this.labelStyle).length ? this.labelStyle : (this.parentData.labelStyle ? this.parentData.labelStyle : + {}); + }, + // label鐨勪綅缃紝宸︿晶鎴栬�呬笂鏂� + elLabelPosition() { + return this.labelPosition ? this.labelPosition : (this.parentData.labelPosition ? this.parentData.labelPosition : + 'left'); + }, + // label鐨勫榻愭柟寮� + elLabelAlign() { + return this.labelAlign ? this.labelAlign : (this.parentData.labelAlign ? this.parentData.labelAlign : 'left'); + }, + // label鐨勪笅鍒掔嚎 + elBorderBottom() { + // 瀛愮粍浠剁殑borderBottom榛樿涓虹┖瀛楃涓诧紝濡傛灉涓嶇瓑浜庣┖瀛楃涓诧紝鎰忓懗鐫�瀛愮粍浠惰缃簡鍊硷紝浼樺厛浣跨敤瀛愮粍浠剁殑鍊� + return this.borderBottom !== '' ? this.borderBottom : this.parentData.borderBottom ? this.parentData.borderBottom : + true; + } + }, + methods: { + broadcastInputError() { + // 瀛愮粍浠跺彂鍑轰簨浠讹紝绗笁涓弬鏁颁负true鎴栬�協alse锛宼rue浠h〃鏈夐敊璇� + this.broadcast('u-input', 'on-form-item-error', this.validateState === 'error' && this.showError('border')); + }, + // 鍒ゆ柇鏄惁闇�瑕乺equired鏍¢獙 + setRules() { + let that = this; + // 鐢变簬浜烘�у寲鑰冭檻锛屽繀濉�"*"鍙烽�氳繃props鐨剅equired閰嶇疆锛屼笉鍐嶉�氳繃rules鐨勮鍒欒嚜鍔ㄧ敓鎴� + // 浠庣埗缁勪欢u-form鎷垮埌褰撳墠u-form-item闇�瑕侀獙璇� 鐨勮鍒� + // let rules = this.getRules(); + // if (rules.length) { + // this.isRequired = rules.some(rule => { + // // 濡傛灉鏈夊繀濉」锛屽氨杩斿洖锛屾病鏈夌殑璇濓紝灏辨槸undefined + // return rule.required; + // }); + // } + + // blur浜嬩欢 + this.$on('on-form-blur', that.onFieldBlur); + // change浜嬩欢 + this.$on('on-form-change', that.onFieldChange); + }, + + // 浠巙-form鐨剅ules灞炴�т腑锛屽彇鍑哄綋鍓島-form-item鐨勬牎楠岃鍒� + getRules() { + // 鐖剁粍浠剁殑鎵�鏈夎鍒� + let rules = this.parent.rules; + rules = rules ? rules[this.prop] : []; + // 淇濊瘉杩斿洖鐨勬槸涓�涓暟缁勫舰寮� + return [].concat(rules || []); + }, + + // blur浜嬩欢鏃惰繘琛岃〃鍗曟牎楠� + onFieldBlur() { + this.validation('blur'); + }, + + // change浜嬩欢杩涜琛ㄥ崟鏍¢獙 + onFieldChange() { + this.validation('change'); + }, + + // 杩囨护鍑虹鍚堣姹傜殑rule瑙勫垯 + getFilteredRule(triggerType = '') { + let rules = this.getRules(); + // 鏁翠綋楠岃瘉琛ㄥ崟鏃讹紝triggerType涓虹┖瀛楃涓诧紝姝ゆ椂杩斿洖鎵�鏈夎鍒欒繘琛岄獙璇� + if (!triggerType) return rules; + // 鍘嗛亶鍒ゆ柇瑙勫垯鏄惁鏈夊搴旂殑浜嬩欢锛屾瘮濡俠lur锛宑hange瑙﹀彂绛夌殑浜嬩欢 + // 浣跨敤indexOf鍒ゆ柇锛屾槸鍥犱负鏌愪簺鏃跺�欒缃殑楠岃瘉瑙勫垯鐨則rigger灞炴�у彲鑳戒负澶氫釜锛屾瘮濡俒'blur','change'] + // 鏌愪簺鍦烘櫙鍙兘鐨勫垽鏂鍒欙紝鍙兘涓嶅瓨鍦╰rigger灞炴�э紝鏁呭厛鍒ゆ柇鏄惁瀛樺湪姝ゅ睘鎬� + return rules.filter(res => res.trigger && res.trigger.indexOf(triggerType) !== -1); + }, + + // 鏍¢獙鏁版嵁 + validation(trigger, callback = () => {}) { + // 妫�楠屼箣闂达紝鍏堣幏鍙栭渶瑕佹牎楠岀殑鍊� + this.fieldValue = this.parent.model[this.prop]; + // blur鍜宑hange鏄惁鏈夊綋鍓嶆柟寮忕殑鏍¢獙瑙勫垯 + let rules = this.getFilteredRule(trigger); + // 鍒ゆ柇鏄惁鏈夐獙璇佽鍒欙紝濡傛灉娌℃湁瑙勫垯锛屼篃璋冪敤鍥炶皟鏂规硶锛屽惁鍒欑埗缁勪欢u-form浼氬洜涓� + // 瀵筩ount鍙橀噺鐨勭粺璁¢敊璇�屾棤娉曡繘鍏ヤ笂涓�灞傜殑鍥炶皟 + if (!rules || rules.length === 0) { + return callback(''); + } + // 璁剧疆褰撳墠鐨勮濉紝鏍囪瘑涓烘牎楠屼腑 + this.validateState = 'validating'; + // 璋冪敤async-validator鐨勬柟娉� + let validator = new schema({ + [this.prop]: rules + }); + validator.validate({ + [this.prop]: this.fieldValue + }, { + firstFields: true + }, (errors, fields) => { + // 璁板綍鐘舵�佸拰鎶ラ敊淇℃伅 + this.validateState = !errors ? 'success' : 'error'; + this.validateMessage = errors ? errors[0].message : ''; + // 璋冪敤鍥炶皟鏂规硶 + callback(this.validateMessage); + }); + }, + + // 娓呯┖褰撳墠鐨剈-form-item + resetField() { + this.parent.model[this.prop] = this.initialValue; + // 璁剧疆涓篳success`鐘舵�侊紝鍙槸涓轰簡娓呯┖閿欒鏍囪 + this.validateState = 'success'; + } + }, + + // 缁勪欢鍒涘缓瀹屾垚鏃讹紝灏嗗綋鍓嶅疄渚嬩繚瀛樺埌u-form涓� + mounted() { + // 鏀粯瀹濄�佸ご鏉″皬绋嬪簭涓嶆敮鎸乸rovide/inject锛屾墍浠ヤ娇鐢ㄨ繖涓柟娉曡幏鍙栨暣涓埗缁勪欢锛屽湪created瀹氫箟锛岄伩鍏嶅惊鐜簲鐢� + this.parent = this.$u.$parent.call(this, 'u-form'); + if (this.parent) { + // 鍘嗛亶parentData涓殑灞炴�э紝灏唒arent涓殑鍚屽悕灞炴�ц祴鍊肩粰parentData + Object.keys(this.parentData).map(key => { + this.parentData[key] = this.parent[key]; + }); + // 濡傛灉娌℃湁浼犲叆prop锛屾垨鑰卽Form涓虹┖(濡傛灉u-form-input鍗曠嫭浣跨敤锛屽氨涓嶄細鏈塽Form娉ㄥ叆)锛屽氨涓嶈繘琛屾牎楠� + if (this.prop) { + // 灏嗘湰瀹炰緥娣诲姞鍒扮埗缁勪欢涓� + this.parent.fields.push(this); + this.errorType = this.parent.errorType; + // 璁剧疆鍒濆鍊� + this.initialValue = this.fieldValue; + // 娣诲姞琛ㄥ崟鏍¢獙锛岃繖閲屽繀椤昏鍐欏湪$nextTick涓紝鍥犱负u-form鐨剅ules鏄�氳繃ref鎵嬪姩浼犲叆鐨� + // 涓嶅湪$nextTick涓殑璇濓紝鍙兘浼氶�犳垚鎵ц姝ゅ浠g爜鏃讹紝鐖剁粍浠惰繕娌¢�氳繃ref鎶婅鍒欑粰u-form锛屽鑷磋鍒欎负绌� + this.$nextTick(() => { + this.setRules(); + }) + } + } + }, + + // 缁勪欢閿�姣佸墠锛屽皢瀹炰緥浠巙-form鐨勭紦瀛樹腑绉婚櫎 + beforeDestroy() { + // 濡傛灉褰撳墠娌℃湁prop鐨勮瘽琛ㄧず褰撳墠涓嶈杩涜鍒犻櫎锛堝洜涓烘病鏈夋敞鍏ワ級 + if (this.parent && this.prop) { + this.parent.fields.map((item, index) => { + if (item === this) this.parent.fields.splice(index, 1); + }) + } + }, + }; +</script> + +<style lang="scss" scoped> + @import "../../libs/css/style.components.scss"; + + .u-form-item { + @include vue-flex; + // align-items: flex-start; + padding: 20rpx 0; + font-size: 28rpx; + color: $u-main-color; + box-sizing: border-box; + line-height: $u-form-item-height; + flex-direction: column; + + &__border-bottom--error:after { + border-color: $u-type-error; + } + + &__body { + @include vue-flex; + } + + &--left { + @include vue-flex; + align-items: center; + + &__content { + position: relative; + @include vue-flex; + align-items: center; + padding-right: 10rpx; + flex: 1; + + &__icon { + margin-right: 8rpx; + } + + &--required { + position: absolute; + left: -16rpx; + vertical-align: middle; + color: $u-type-error; + padding-top: 6rpx; + } + + &__label { + @include vue-flex; + align-items: center; + flex: 1; + } + } + } + + &--right { + flex: 1; + + &__content { + @include vue-flex; + align-items: center; + flex: 1; + + &__slot { + flex: 1; + /* #ifndef MP */ + @include vue-flex; + align-items: center; + /* #endif */ + } + + &__icon { + margin-left: 10rpx; + color: $u-light-color; + font-size: 30rpx; + } + } + } + + &__message { + font-size: 24rpx; + line-height: 24rpx; + color: $u-type-error; + margin-top: 12rpx; + } + } +</style> -- Gitblit v1.9.3