原版 :https://market.sencha.com/extensions/sencha-touch-2-rating-star-field
效果:
我的改造版(只是类名变了):
1 Ext.define('ux.field.Rating', { 2 xtype: 'fieldRating', 3 extend: 'Ext.field.Field', 4 config: { 5 baseCls: Ext.baseCSSPrefix + 'field x-rating', 6 7 /** 8 * @cfg {Number} value 9 * 默认值 0-5 10 */ 11 value: 0, 12 13 /** 14 * @cfg {Boolean} editable 15 * 是否只读 16 */ 17 editable: true, 18 19 /** 20 * @cfg 21 * @inheritdoc 22 */ 23 name: 'rating', 24 25 /** 26 * @cfg 27 * @private 28 *是否显示清除按钮 29 */ 30 clearIcon: false, 31 32 /** 33 * @cfg 34 * @private 35 */ 36 component: { 37 hidden: true, 38 xtype: 'input', 39 type: 'text', 40 useMask: true 41 }, 42 /** 43 * @cfg {Boolean} labelMaskTap 44 * @private 45 */ 46 }, 47 //布局模版 48 getElementConfig: function () { 49 var prefix = Ext.baseCSSPrefix; 50 return { 51 reference: 'element', 52 className: 'x-container', 53 children: [{ 54 reference: 'label', 55 cls: prefix + 'form-label', 56 children: [{ 57 reference: 'labelspan', 58 tag: 'span' 59 }] 60 }, { 61 reference: 'innerElement', 62 cls: prefix + 'component-outer', 63 html: ' ' + 64 '<div class="starContainer">' + 65 '<div class="left"></div>' + 66 '<div class="x-button x-button-action star star1"></div>' + 67 '<div class="center"></div>' + 68 '<div class="x-button x-button-action star star2"></div>' + 69 '<div class="center"></div>' + 70 '<div class="x-button x-button-action star star3"></div>' + 71 '<div class="center"></div>' + 72 '<div class="x-button x-button-action star star4"></div>' + 73 '<div class="center"></div>' + 74 '<div class="x-button x-button-action star star5"></div>' + 75 '<div class="right"></div>' + 76 '</div>' + 77 '<div class="starFakeContainer"></div>', 78 }] 79 }; 80 }, 81 initialize: function () { 82 var me = this; 83 me.callParent(); 84 me.on('painted', function () { 85 me.starFakeContainer = me.innerElement.down('.starFakeContainer'); 86 me.star1 = me.innerElement.down('.star1'); 87 me.star2 = me.innerElement.down('.star2'); 88 me.star3 = me.innerElement.down('.star3'); 89 me.star4 = me.innerElement.down('.star4'); 90 me.star5 = me.innerElement.down('.star5'); 91 var value = me.getValue(); 92 me.orignalValue = value; 93 me.activateStars(value); 94 95 me.setEditable(me.getEditable()); 96 }); 97 }, 98 applyEditable: function (editable) { 99 var me = this; 100 if (!me.starFakeContainer) { 101 return editable; 102 } 103 if (editable) { 104 me.starFakeContainer.on({ 105 touchstart: me.onTouchStartMove, 106 touchmove: me.onTouchStartMove, 107 touchend: me.onTouchEnd, 108 scope: me 109 }); 110 } else { 111 me.starFakeContainer.un({ 112 touchstart: me.onTouchStartMove, 113 touchmove: me.onTouchStartMove, 114 touchend: me.onTouchEnd, 115 scope: me 116 }); 117 } 118 return editable; 119 }, 120 applyValue: function (value) { 121 var me = this; 122 if (value < 0) { 123 value = 0; 124 } else if (value > 5) { 125 value = 5; 126 } 127 me.activateStars(value); 128 return value; 129 }, 130 activateStars: function (rating) { 131 if (!this['star1']) { 132 return; 133 } 134 for (var i = 1; i <= 5; i++) { 135 this['star' + i].removeCls('active'); 136 } 137 for (var i = 1; i <= rating; i++) { 138 this['star' + i].addCls('active'); 139 } 140 }, 141 buildRating: function (delta) { 142 var width = this.starFakeContainer.getWidth(); 143 144 if (delta >= width) { 145 delta = width; 146 } else if (delta <= 0) { 147 delta = 0; 148 } 149 delta = delta / width * 100; 150 var onePart = ((width / 12) / width * 100); 151 var rating = 0; 152 if (delta >= (onePart * 9)) { 153 rating = 5; 154 } else if (delta >= (onePart * 7)) { 155 rating = 4; 156 } else if (delta >= (onePart * 5)) { 157 rating = 3; 158 } else if (delta >= (onePart * 3)) { 159 rating = 2; 160 } else if (delta >= onePart) { 161 rating = 1; 162 } 163 this.setValue(rating); 164 return delta; 165 }, 166 onTouchStartMove: function (a) { 167 var me = this; 168 var offsetLeft = me.innerElement.dom.offsetLeft; 169 if (offsetLeft) { 170 me.buildRating(a.pageX - offsetLeft); 171 } 172 }, 173 onTouchEnd: function (a) { 174 var me = this; 175 var offsetLeft = me.innerElement.dom.offsetLeft; 176 if (offsetLeft) { 177 me.buildRating(a.pageX - offsetLeft); 178 } 179 var value = me.getValue(); 180 if (me.orignalValue != value) { 181 me.orignalValue = value; 182 me.fireEvent('change', this, value, me.orignalValue); 183 } 184 } 185 });
所需css:
1 /*#region 评分*/ 2 .x-rating { 3 min-height:25px; 4 } 5 .x-rating .x-component-outer { 6 position:relative; 7 padding:0.6em; 8 min-height:40px; 9 } 10 .x-rating .starContainer,.x-rating .starFakeContainer { 11 position:absolute; 12 top:0px; 13 left:0px; 14 width:100%; 15 height:100%; 16 } 17 .x-rating .starFakeContainer { 18 z-index:10; 19 } 20 .x-rating .starContainer { 21 display:-webkit-box; 22 -webkit-box-align:center; 23 -webkit-box-pack:center; 24 } 25 .x-rating .starContainer .center,.x-rating .starContainer .left,.x-rating .starContainer .right { 26 -webkit-box-flex:1 27 } 28 .x-rating .star { 29 border:0px !important; 30 padding:0px !important; 31 margin:0px 0px 0px -12.5px !important; 32 position:absolute; 33 left:16.66%; 34 top:5px; 35 -webkit-mask:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAMAUExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALMw9IgAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAB7BAAAewQHDaVRTAAAAGHRFWHRTb2Z0d2FyZQBQYWludC5ORVQgdjMuMzUw7rOfAAACdElEQVR4Xu2SSZaDMAwFc/9LpwkUBGN5kDy181yLDtbwVYt+vQczvcCrOIBfK6MFXht8Gplc4HO/0GBugeN+mcHUApzfoGBhZgGO71AyMLEAp4Ginp8RMOfYFx9QVmNd5OwNGlpmFeCoAy0lkwpw8gFNHXMKcNCDtorfErCEmXaCMKChroAhzbISgREFlQX0cYaNKAzlU1tAnadfSMBYNtUFtIHq+SQM5jKbAEeiMJrJZAKcSMBwHi0EVJmKYdKzYCWDrFFS1bAeJT5EUCGEyQS6rNaFbJdnldmGcOjketPuBmc3AQqD+A8CQw2O/wEeA9j/BwYa7Lc/fwYZHKf3v0MMuHz8DDA4D/Pb3YCzX4HOBhy9C3Q14OTG7bOjAQc/3L+7GXBux3l0MuDYgfvqYsApeDw7GHDo5PlubsCZC6/Q2IAjX/xKUwNO3BBKDQ04cEeqNTMg3kEsNjIg3EWuNjEg+kGg3MCA4CehenUDYj2CjcoGhPqEO1UNiBSItCoaECgR61UzIE4k2qxkQJhMvFtFgaQAKYFyA3JCNBcgJsgSSA0QUwBBIZbAEmgvkLiQaleAqABLYAl0EIifSHSrQJjMElgC0S4BxRAnsgRmF8ifClEkwFj2nESBAEM7lEIwJRHtxWDmgrIMMxJGASYcaEkwIWESoO9B24e+hEWAtgQTT+hKqNPoBWHMhZ5EpMeyC70IDLrQE9AJ0EnA8B06AhoB6hmw8IW6QL4A1UxYOqEqkCtATQGLB9QE8gSoKGF5h5JPpHNBwQABGxR80gI8jRBSIMDLTiImIcB3GXYBvooxCdQ7/yGcpm/Y6HVHzxIYLPB+/wEfB7NVrMbQ3wAAAABJRU5ErkJggg==') /*../icons/star.png*/ left center no-repeat; 36 -webkit-mask-size:25px 25px; 37 background-color:#ff0000; 38 width:25px; 39 height:25px; 40 opacity:.2; 41 -webkit-transform:scale(1,1); 42 } 43 .x-rating .star2 { 44 left:33.33%; 45 } 46 .x-rating .star3 { 47 left:49.99%; 48 } 49 .x-rating .star4 { 50 left:66.66%; 51 } 52 .x-rating .star5 { 53 left:83.33%; 54 } 55 .x-rating .star.active { 56 -webkit-transform:scale(1,1); 57 opacity:1; 58 } 59 /*#endregion*/
使用(请自行调整样式):
1 Ext.define('app.view.user.orders.Info', { 2 extend: 'Ext.form.Panel', 3 xtype: 'ordersInfo', 4 alternateClassName: 'ordersInfo', 5 requires: ['ux.field.Rating'], 6 config: { 7 title: '订单详情', 8 cls: 'info', 9 scrollable: { 10 direction: 'vertical', 11 indicators: false 12 }, 13 items: [ 14 { 15 margin: 5, 16 cls: 'border', 17 items: [{ 18 label: '描述相符', 19 xtype: 'fieldRating', 20 name:'miaoShu' 21 }, 22 { 23 label: '服务态度', 24 xtype: 'fieldRating', 25 name: 'fuwu' 26 }, 27 { 28 label: '发货速度', 29 xtype: 'fieldRating', 30 name: 'fahuo' 31 }] 32 }, 33 { 34 xtype: 'button', 35 margin: '10 20 10 20', 36 action: 'save', 37 text: '评分', 38 ui: 'orange' 39 }] 40 }, 41 initialize: function () { 42 var me = this, 43 button = me.down('button'); 44 button.on({ 45 tap: 'onSave', 46 scope: me 47 }); 48 me.callParent(arguments); 49 }, 50 updateData: function (data) { 51 var me = this, 52 info = me.down('#info'); 53 info.setData(data); 54 }, 55 //保存数据 56 onSave: function () { 57 var me = this; 58 if (util.valid(Ext.create('app.model.Rating'), me)) { 59 me.fireEvent('save', me, me.getValues()); 60 } 61 } 62 });
附送验证模型:
1 //评分 2 Ext.define('app.model.Rating', { 3 extend: 'Ext.data.Model', 4 config: { 5 fields: [{ 6 name: 'miaoShu', 7 type: 'int' 8 }, 9 { 10 name: 'fuwu', 11 type: 'string' 12 }, 13 { 14 name: 'fahuo', 15 type: 'string' 16 }], 17 validations: [{ 18 field: 'miaoShu', 19 type: 'format', 20 matcher: /[1-5]/, 21 message: '请对描述相符进行评分!' 22 }, { 23 field: 'fuwu', 24 type: 'format', 25 matcher: /[1-5]/, 26 message: '请对服务态度进行评分!' 27 }, { 28 field: 'fahuo', 29 type: 'format', 30 matcher: /[1-5]/, 31 message: '请对发货速度进行评分!' 32 }] 33 } 34 });