form-grid.js

 

/*

This file is part of Ext JS 4

Copyright (c) 2011 Sencha Inc

Contact: http://www.sencha.com/contact

GNU General Public License Usage

This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.

If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.

*/

Ext.require([

'Ext.form.*',

'Ext.data.*',

'Ext.grid.Panel',

'Ext.layout.container.Column'

]);

 

Ext.onReady(function(){

Ext.QuickTips.init();

var bd = Ext.getBody();

//F.A. == Menu Bar begins

var menu = Ext.create('Ext.menu.Menu', {

id: 'mainMenu',

style: {

overflow: 'visible' // For the Combo popup

},

items: [

{

text: 'Radio Options',

menu: { // <-- submenu by nested config object

items: [

// stick any markup in a menu

//'<b class="menu-title">Choose a Theme</b>',

{

text: 'Aero Glass',

checked: true,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Vista Black',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Gray Theme',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Default Theme',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}

]

}

},{

text: 'Radio Options2',

menu: { // <-- submenu by nested config object

items: [

// stick any markup in a menu

//'<b class="menu-title">Choose a Theme</b>',

{

text: 'Aero Glass',

checked: true,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Vista Black',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Gray Theme',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Default Theme',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}

]

}

}

]

});

var menu2 = Ext.create('Ext.menu.Menu', {

id: 'mainMenu1',

style: {

overflow: 'visible' // For the Combo popup

},

items: [

{

text: 'Radio Options',

menu: { // <-- submenu by nested config object

items: [

// stick any markup in a menu

//'<b class="menu-title">Choose a Theme</b>',

{

text: 'Aero Glass',

checked: true,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Vista Black',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Gray Theme',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Default Theme',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}

]

}

},{

text: 'Radio Options2',

menu: { // <-- submenu by nested config object

items: [

// stick any markup in a menu

//'<b class="menu-title">Choose a Theme</b>',

{

text: 'Aero Glass',

checked: true,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Vista Black',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: 'Gray Theme',

checked: false,

group: 'theme',

checkHandler: onItemCheck

}, {

text: '1111Default Theme',

checked: false,

group: 'theme',

checkHandler: onItemClick

}

]

}

}

]

});

var tb = Ext.create('Ext.toolbar.Toolbar');

tb.suspendLayout = true;

tb.render('toolbar');

tb.add({

text:'Button w/ Menu',

iconCls: 'bmenu', // <-- icon

menu: menu // assign menu by instance

}, {

text:'Button w/ Menu',

iconCls: 'bmenu', // <-- icon

menu: menu2 // assign menu by instance

});

menu.add(' ');

// Menus have a rich api for

// adding and removing elements dynamically

var item = menu.add({

text: 'Dynamically added Item'

});

// items support full Observable API

item.on('click', onItemClick);

// items can easily be looked up

menu.add({

text: 'Disabled Item',

id: 'disableMe' // <-- Items can also have an id for easy lookup

// disabled: true <-- allowed but for sake of example we use long way below

});

// access items by id or index

menu.items.get('disableMe').disable();

// They can also be referenced by id in or components

tb.add('-', {

icon: 'list-items.gif', // icons can also be specified inline

cls: 'x-btn-icon',

tooltip: '<b>Quick Tips</b><br/>Icon only button with tooltip',

handler: function(){

//Ext.example.msg('Button Click','You clicked the "icon only" button.');

}

});

// functions to display feedback

function onButtonClick(btn){

//Ext.example.msg('Button Click','You clicked the "{0}" button.', btn.text);

//Ext.Msg.alert('Status', 'Button Click:');

}

function onItemCheck(item, checked){

//Ext.example.msg('Item Check', 'You {1} the "{0}" menu item.', item.text, checked ? 'checked' : 'unchecked');

//Ext.Msg.alert('Status', 'Item Check');

}

function onItemToggle(item, pressed){

//Ext.example.msg('Button Toggled', 'Button "{0}" was toggled to {1}.', item.text, pressed);

//Ext.Msg.alert('Status', 'Button Toggled');

}

//F.A. == menu Bar ends

// sample static data for the store

var myData = [

['3m Co', 71.72, 0.02, 0.03, '9/1 12:00am'],

['Alcoa Inc', 29.01, 0.42, 1.47, '9/1 12:00am'],

['Altria Group Inc', 83.81, 0.28, 0.34, '9/1 12:00am'],

['American Express Company', 52.55, 0.01, 0.02, '9/1 12:00am'],

['American International Group, Inc.', 64.13, 0.31, 0.49, '9/1 12:00am'],

['AT&T Inc.', 31.61, -0.48, -1.54, '9/1 12:00am'],

['Boeing Co.', 75.43, 0.53, 0.71, '9/1 12:00am'],

['Caterpillar Inc.', 67.27, 0.92, 1.39, '9/1 12:00am'],

['Citigroup, Inc.', 49.37, 0.02, 0.04, '9/1 12:00am'],

['E.I. du Pont de Nemours and Company', 40.48, 0.51, 1.28, '9/1 12:00am'],

['Exxon Mobil Corp', 68.1, -0.43, -0.64, '9/1 12:00am'],

['General Electric Company', 34.14, -0.08, -0.23, '9/1 12:00am'],

['General Motors Corporation', 30.27, 1.09, 3.74, '9/1 12:00am'],

['Hewlett-Packard Co.', 36.53, -0.03, -0.08, '9/1 12:00am'],

['Honeywell Intl Inc', 38.77, 0.05, 0.13, '9/1 12:00am'],

['Intel Corporation', 19.88, 0.31, 1.58, '9/1 12:00am'],

['International Business Machines', 81.41, 0.44, 0.54, '9/1 12:00am'],

['Johnson & Johnson', 64.72, 0.06, 0.09, '9/1 12:00am'],

['JP Morgan & Chase & Co', 45.73, 0.07, 0.15, '9/1 12:00am'],

['McDonald\'s Corporation', 36.76, 0.86, 2.40, '9/1 12:00am'],

['Merck & Co., Inc.', 40.96, 0.41, 1.01, '9/1 12:00am'],

['Microsoft Corporation', 25.84, 0.14, 0.54, '9/1 12:00am'],

['Pfizer Inc', 27.96, 0.4, 1.45, '9/1 12:00am'],

['The Coca-Cola Company', 45.07, 0.26, 0.58, '9/1 12:00am'],

['The Home Depot, Inc.', 34.64, 0.35, 1.02, '9/1 12:00am'],

['The Procter & Gamble Company', 61.91, 0.01, 0.02, '9/1 12:00am'],

['United Technologies Corporation', 63.26, 0.55, 0.88, '9/1 12:00am'],

['3m Co', 71.72, 0.02, 0.03, '9/1 12:00am'],

['Alcoa Inc', 29.01, 0.42, 1.47, '9/1 12:00am'],

['Altria Group Inc', 83.81, 0.28, 0.34, '9/1 12:00am'],

['American Express Company', 52.55, 0.01, 0.02, '9/1 12:00am'],

['American International Group, Inc.', 64.13, 0.31, 0.49, '9/1 12:00am'],

['AT&T Inc.', 31.61, -0.48, -1.54, '9/1 12:00am'],

['Boeing Co.', 75.43, 0.53, 0.71, '9/1 12:00am'],

['Caterpillar Inc.', 67.27, 0.92, 1.39, '9/1 12:00am'],

['Citigroup, Inc.', 49.37, 0.02, 0.04, '9/1 12:00am'],

['E.I. du Pont de Nemours and Company', 40.48, 0.51, 1.28, '9/1 12:00am'],

['Exxon Mobil Corp', 68.1, -0.43, -0.64, '9/1 12:00am'],

['General Electric Company', 34.14, -0.08, -0.23, '9/1 12:00am'],

['General Motors Corporation', 30.27, 1.09, 3.74, '9/1 12:00am'],

['Hewlett-Packard Co.', 36.53, -0.03, -0.08, '9/1 12:00am'],

['Honeywell Intl Inc', 38.77, 0.05, 0.13, '9/1 12:00am'],

['Intel Corporation', 19.88, 0.31, 1.58, '9/1 12:00am'],

['International Business Machines', 81.41, 0.44, 0.54, '9/1 12:00am'],

['Johnson & Johnson', 64.72, 0.06, 0.09, '9/1 12:00am'],

['JP Morgan & Chase & Co', 45.73, 0.07, 0.15, '9/1 12:00am'],

['McDonald\'s Corporation', 36.76, 0.86, 2.40, '9/1 12:00am'],

['Merck & Co., Inc.', 40.96, 0.41, 1.01, '9/1 12:00am'],

['Microsoft Corporation', 25.84, 0.14, 0.54, '9/1 12:00am'],

['Pfizer Inc', 27.96, 0.4, 1.45, '9/1 12:00am'],

['The Coca-Cola Company', 45.07, 0.26, 0.58, '9/1 12:00am'],

['The Home Depot, Inc.', 34.64, 0.35, 1.02, '9/1 12:00am'],

['The Procter & Gamble Company', 61.91, 0.01, 0.02, '9/1 12:00am'],

['United Technologies Corporation', 63.26, 0.55, 0.88, '9/1 12:00am'],

['Verizon Communications', 35.57, 0.39, 1.11, '9/1 12:00am'],

['Wal-Mart Stores, Inc.', 45.45, 0.73, 1.63, '9/1 12:00am']

];

var myData2 = [

['223m Co', 71.72, 0.02, 0.03, '9/1 12:00am'],

['22Alcoa Inc', 29.01, 0.42, 1.47, '9/1 12:00am'],

['22Altria Group Inc', 83.81, 0.28, 0.34, '9/1 12:00am'],

['22Verizon Communications', 35.57, 0.39, 1.11, '9/1 12:00am'],

['22Wal-Mart Stores, Inc.', 45.45, 0.73, 1.63, '9/1 12:00am']

];

var ds = Ext.create('Ext.data.ArrayStore', {

fields: [

{name: 'company'},

{name: 'price', type: 'float'},

{name: 'change', type: 'float'},

{name: 'pctChange', type: 'float'},

{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'},

// Rating dependent upon performance 0 = best, 2 = worst

{name: 'rating', type: 'int', convert: function(value, record) {

var pct = record.get('pctChange');

if (pct < 0) return 2;

if (pct < 1) return 1;

return 0;

}}

],

data: myData

});

//F.A. define JSON Reader and store begins

Ext.define('Good', {

extend: 'Ext.data.Model',

fields: [

{name: 'company'},

{name: 'price', type: 'float'},

{name: 'change', type: 'float'},

{name: 'pctChange', type: 'float'},

{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'},

// Rating dependent upon performance 0 = best, 2 = worst

{name: 'rating', type: 'int', convert: function(value, record) {

var pct = record.get('pctChange');

if (pct < 0) return 2;

if (pct < 1) return 1;

return 0;

}}

]

});

var mystore = new Ext.data.Store({

model: 'Good',

proxy: {

type: 'ajax',

url : '/servlet/tigerservlet',

reader: {

type: 'json'

}

}

});

//F.A. define JSON Reader and store begins

// example of custom renderer function

function change(val){

if(val > 0){

return '<span style="color:green;">' + val + '</span>';

}else if(val < 0){

return '<span style="color:red;">' + val + '</span>';

}

return val;

}

// example of custom renderer function

function pctChange(val){

if(val > 0){

return '<span style="color:green;">' + val + '%</span>';

}else if(val < 0){

return '<span style="color:red;">' + val + '%</span>';

}

return val;

}

// render rating as "A", "B" or "C" depending upon numeric value.

function rating(v) {

if (v == 0) return "A";

if (v == 1) return "B";

if (v == 2) return "C";

}

 

//bd.createChild({tag: 'h2', html: 'Using a Grid with a Form'});

/*

* Here is where we create the Form

*/

var gridForm = Ext.create('Ext.form.Panel', {

id: 'company-form',

frame: true,

//title: 'Company data',

bodyPadding: 5,

//width: 750,

layout: 'column', // Specifies that the items will now be arranged in columns

dockedItems: [{

//itemId: 'toolbar',

xtype: 'toolbar',

dock: 'top',

//items: [tb]

}],

fieldDefaults: {

labelAlign: 'left',

msgTarget: 'side'

},

items: [{

columnWidth: 0.75,

xtype: 'gridpanel',

store: mystore,

height: 700,

title:'Company Data',

columns: [

{

id :'company',

text : 'Company',

flex: 1,

sortable : true,

dataIndex: 'company'

},

{

text : 'Price',

width : 75,

sortable : true,

dataIndex: 'price'

},

{

text : 'Change',

width : 75,

sortable : true,

renderer : change,

dataIndex: 'change'

},

{

text : '% Change',

width : 75,

sortable : true,

renderer : pctChange,

dataIndex: 'pctChange'

},

{

text : 'Last Updated',

width : 85,

sortable : true,

renderer : Ext.util.Format.dateRenderer('m/d/Y'),

dataIndex: 'lastChange'

},

{

text: 'Rating',

width: 30,

sortable: true,

renderer: rating,

dataIndex: 'rating'

}

],

listeners: {

selectionchange: function(model, records) {

if (records[0]) {

this.up('form').getForm().loadRecord(records[0]);

}

}

}

}, {

columnWidth: 0.25,

margin: '0 0 0 10',

xtype: 'fieldset',

title:'Company details',

defaults: {

width: 240,

labelWidth: 90

},

defaultType: 'textfield',

items: [{

fieldLabel: 'Name',

name: 'company'

},{

fieldLabel: 'Price',

name: 'price'

},{

fieldLabel: '% Change',

name: 'pctChange'

},{

xtype: 'datefield',

fieldLabel: 'Last Updated',

name: 'lastChange'

}, {

xtype: 'radiogroup',

fieldLabel: 'Rating',

columns: 3,

defaults: {

name: 'rating' //Each radio has the same name so the browser will make sure only one is checked at once

},

items: [{

inputValue: '0',

boxLabel: 'A'

}, {

inputValue: '1',

boxLabel: 'B'

}, {

inputValue: '2',

boxLabel: 'C'

}]

}]

}],

renderTo: bd

});

function onItemClick(item){

//Ext.example.msg('Menu Click', 'You clicked the "{0}" menu item.', item.text);

//Ext.Msg.alert('Status', 'Menu Click:'+gridForm.child('gridpanel').id);

ds.loadData(myData2,false);

}

gridForm.child('gridpanel').getSelectionModel().select(0);

});

<think>我们正在讨论Ant Design Vue (a-form)中的表单验证问题,特别是使用v-decorator.trim时验证规则的配置,以及输入框位置偏移的问题。 首先,回顾一下v-decorator.trim的作用:它用于自动去除输入值的前后空白字符。这在用户输入时特别有用,可以避免用户无意中输入的空格导致验证失败。 关于验证规则: 在Ant Design Vue中,我们可以通过rules属性为每个表单项定义验证规则。这些规则是一个数组,每个规则对象可以包含多个属性,如required(是否必填)、message(验证失败时的提示信息)、validator(自定义验证函数)等。 当使用v-decorator.trim时,由于它会自动去除输入值的前后空格,因此我们在验证规则中不需要再写trim操作。但是,需要注意的是,trim操作是在表单提交前自动完成的,所以在自定义验证函数中,我们得到的值已经是去除前后空格后的值。 配置验证规则示例: 假设我们有一个用户名输入框,要求必填且最小长度为2,最大长度为10。我们可以这样配置: ```vue <template> <a-form :form="form"> <a-form-item label="用户名"> <a-input v-decorator.trim="[ 'username', { rules: [ { required: true, message: '请输入用户名' }, { min: 2, message: '用户名至少2个字符' }, { max: 10, message: '用户名不能超过10个字符' } ] } ]" /> </a-form-item> </a-form> </template> <script> export default { beforeCreate() { this.form = this.$form.createForm(this); } }; </script> ``` 注意:v-decorator.trim的写法,trim修饰符直接加在v-decorator后面。 关于输入框位置偏移问题: 这个问题通常是由于表单布局或样式引起的。可能的原因包括: 1. 表单项(a-form-item)的布局设置(layout)与内部元素不匹配。 2. 自定义样式覆盖了默认样式,导致输入框位置偏移。 解决方案: 1. 检查表单的layout属性。Ant Design Vue的表单支持三种布局:horizontal(水平)、vertical(垂直)、inline(行内)。根据布局调整样式。 2. 如果使用了自定义样式,检查是否有影响输入框位置的样式,例如padding、margin等。 3. 使用浏览器开发者工具检查元素,查看具体是哪个样式导致了偏移,然后覆盖该样式。 例如,在水平布局中,标签和输入框是并排的,标签有固定的宽度。如果标签内容过长,可能会导致输入框位置偏移。我们可以通过设置标签的宽度来调整: ```vue <a-form-item label="用户名" :label-col="{ span: 4 }" :wrapper-col="{ span: 14 }"> <!-- 输入框 --> </a-form-item> ``` 或者,我们可以通过全局样式调整: ```css .ant-form-item-label { width: 100px; /* 自定义宽度 */ } ``` 4. 如果偏移是由于输入框本身的高度不一致(比如有错误信息时)导致的,可以尝试给表单项设置固定的高度,或者使用min-height。 5. 另外,当使用v-decorator.trim时,并不会直接导致位置偏移,所以偏移问题可能是其他原因造成的。 如果以上方法不能解决问题,请提供具体的代码和偏移现象的截图,以便更准确地定位问题。 最后,关于验证规则的一个常见问题:如果我们希望禁止输入空格(而不仅仅是去除前后空格),我们可以使用自定义验证规则,在验证函数中检查输入值是否包含空格。例如,禁止用户名中间有空格: ```javascript rules: [ { required: true, message: '请输入用户名' }, { min: 2, message: '用户名至少2个字符' }, { max: 10, message: '用户名不能超过10个字符' }, { pattern: /^\S*$/, message: '用户名不能包含空格' } ] ``` 这里使用了正则表达式,确保输入的用户名中不包含任何空格。 总结: 1. v-decorator.trim可以方便地去除输入值的前后空格,在验证规则中不需要再处理空格。 2. 输入框位置偏移问题通常与布局和样式有关,需要根据具体情况调整样式。 3. 使用rules数组配置验证规则,可以包含内置规则和自定义规则。 希望这些信息能帮助你解决问题。如果还有其他疑问,请提供更多细节。</think>### 使用 `a-form-item` 和 `v-decorator.trim` 的验证规则与位置偏移解决方案 #### 1. `v-decorator.trim` 验证规则配置 `v-decorator.trim` 会自动去除输入值的前后空格,验证规则需注意以下要点: **语法示例**: ```vue <a-form-item label="用户名"> <a-input v-decorator.trim="[ 'username', { rules: [ { required: true, message: '请输入用户名' }, { pattern: /^[\u4e00-\u9fa5a-zA-Z0-9]+$/, message: '不能包含空格' }, { validator: customValidator } ] } ]" /> </a-form-item> ``` **关键配置**: 1. **空格处理** `trim` 会在验证前自动去除首尾空格,但需在规则中明确禁止中间空格: ```js { pattern: /^\S*$/, message: '不能包含空格' } ``` 2. **自定义验证器** 在验证器中直接使用处理后的值: ```js const customValidator = (rule, value, callback) => { // value 已自动 trim if (value.length < 4) callback('至少4个字符') else callback() } ``` 3. **混合规则** 内置规则 + 自定义规则的组合: ```js rules: [ { required: true, message: '必填项' }, { min: 3, message: '最少3字符' }, { validator: checkSpecialChars } ] ``` #### 2. 输入框位置偏移解决方案 常见原因及修复方法: **1. 布局冲突** **问题**:表单标签宽度不一致导致错位 **修复**:固定标签宽度 ```vue <a-form :labelCol="{ span: 6 }" :wrapperCol="{ span: 14 }"> <!-- 表单项 --> </a-form> ``` **2. 校验信息撑开高度** **问题**:错误提示导致表单项高度变化 **修复**:预置错误消息高度 ```css .ant-form-item { min-height: 62px; /* 根据实际高度调整 */ } ``` **3. 表单嵌套冲突** **问题**:多层表单容器导致定位异常 **修复**:避免非常规嵌套 ```vue <!-- 错误示例 --> <div v-for="item in list"> <a-form>...</a-form> <!-- 避免循环内创建表单 --> </div> <!-- 正确做法 --> <a-form> <div v-for="item in list"> <a-form-item>...</a-form-item> </div> </a-form> ``` **4. CSS 权重问题** **问题**:自定义样式覆盖默认样式 **修复**:增强样式权重 ```css /* 添加父级选择器提高权重 */ .ant-form .ant-form-item { margin-bottom: 24px !important; } ``` **5. 响应式断点问题** **问题**:特定屏幕尺寸下错位 **修复**:添加响应式样式 ```css @media (max-width: 768px) { .ant-form-item-label { text-align: left !important; } } ``` #### 最佳实践建议 1. **表单统一配置** 在顶层统一设置布局参数: ```vue <a-form :form="form" :labelCol="{ span: 4 }" :wrapperCol="{ span: 18 }" @submit="handleSubmit" > ``` 2. **动态验证规则** 使用 `rules` 的动态绑定: ```js computed: { dynamicRules() { return [ { required: this.isRequired, message: '动态必填' }, { pattern: /^[A-Z]+$/, message: '需全大写' } ] } } ``` 3. **调试工具** 使用浏览器开发者工具检查元素盒模型: - 检查 `margin/padding` 异常值 - 查看 `position: absolute` 的定位元素 - 检测 `display: flex` 容器的对齐方式 > 注:若问题仍存在,请提供具体代码片段和界面截图以便精准定位[^1]。 --- ### 相关问题 1. 如何在 Ant Design Vue 中实现动态表单验证规则? 2. 表单提交时如何获取所有已 `trim` 处理的值? 3. 响应式布局下表单错位的系统化调试方法有哪些? 4. 如何自定义 Ant Design 表单错误提示的样式和位置? [^1]: 表单布局问题通常源于容器尺寸计算或样式覆盖,建议优先检查父元素的布局模式(Flex/Grid)和表单项的盒模型参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值