ソースを参照

refactor: rate

richard1015 4 年 前
コミット
01cb77fef9
4 ファイル変更125 行追加160 行削除
  1. 27 41
      src/packages/rate/demo.vue
  2. 49 61
      src/packages/rate/doc.md
  3. 12 20
      src/packages/rate/index.scss
  4. 37 38
      src/packages/rate/index.vue

+ 27 - 41
src/packages/rate/demo.vue

@@ -1,58 +1,49 @@
 <template>
   <div class="demo">
     <h2>基本用法</h2>
-    <div>
-      <nut-rate v-model:value="state.val"></nut-rate>
-    </div>
+    <nut-rate v-model="state.val" />
+
+    <h2>半星</h2>
+    <nut-rate allow-half v-model="state.val1"></nut-rate>
 
     <h2>自定义 icon </h2>
-    <div>
-      <nut-rate
-        checked-icon="heart-fill1"
-        unchecked-icon="heart"
-        v-model:value="state.val3"
-      ></nut-rate>
-    </div>
+    <nut-rate
+      checked-icon="heart-fill1"
+      unchecked-icon="heart"
+      v-model="state.val2"
+    ></nut-rate>
 
     <h2>自定义数量</h2>
-    <div>
-      <nut-rate :total="6" v-model:value="state.val4"></nut-rate>
-    </div>
+    <nut-rate :count="6" v-model="state.val3"></nut-rate>
 
     <h2>自定义颜色</h2>
-    <div>
-      <nut-rate active-color="#FFC800" v-model:value="state.val5"></nut-rate>
-    </div>
+    <nut-rate active-color="#FFC800" v-model="state.val4"></nut-rate>
 
     <h2>禁用状态</h2>
-    <div>
-      <nut-rate :disabled="true" v-model:value="state.val6"></nut-rate>
-    </div>
+    <nut-rate disabled v-model="state.val5"></nut-rate>
 
     <h2>只读状态</h2>
-    <div>
-      <nut-rate :value="2" :read-only="true"></nut-rate>
-    </div>
+    <nut-rate v-model="state.val6" readonly></nut-rate>
 
     <h2>绑定事件,{{ state.result }}</h2>
-    <div>
-      <nut-rate @click="onClick"></nut-rate>
-    </div>
-    <h2>自定义尺寸</h2>
-    <div>
-      <nut-rate :size="35"></nut-rate>
-    </div>
+    <nut-rate @change="onChange"></nut-rate>
+    <h2>自定义尺寸 35px</h2>
+    <nut-rate v-model="state.val7" icon-size="35"></nut-rate>
   </div>
 </template>
 
 <script>
-import { reactive } from 'vue';
+import { reactive, getCurrentInstance } from 'vue';
 import { createComponent } from '@/utils/create';
 const { createDemo } = createComponent('rate');
 export default createDemo({
   setup() {
+    let { ctx } = getCurrentInstance();
+
     const state = reactive({
-      val: 4,
+      val: 3,
+      val1: 3.5,
+      val2: 3,
       val3: 3,
       val4: 3,
       val5: 3,
@@ -60,21 +51,16 @@ export default createDemo({
       val7: 3,
       result: ''
     });
-    const onClick = idx => {
-      state.result = '您点击了第' + idx + '个!';
+    const onChange = val => {
+      state.result = '您点击了第' + val + '个!';
+      ctx.$toast.text(state.result);
     };
     return {
       state,
-      onClick
+      onChange
     };
   }
 });
 </script>
 
-<style lang="scss" scoped>
-.demo {
-}
-h4 {
-  padding: 0 10px;
-}
-</style>
+<style lang="scss" scoped></style>

+ 49 - 61
src/packages/rate/doc.md

@@ -18,98 +18,86 @@ app.use(Rate);
 
 ### 基础用法
 
-```html
-    <nut-rate 
-        v-model:value="state.val"
-    >
-    </nut-rate>
+``` html
+<nut-rate v-model="value" />
+```
+
+``` javascript
+import { ref } from 'vue';
+setup() {
+    const value = ref(3);
+    return { value }
+}
 ```
 
 ### 自定义icon
 
-```html
-    <nut-rate
-        checked-icon="heart-fill1"
-        unchecked-icon="heart"
-        v-model:value="state.val3"
-    >
-    </nut-rate>
+``` html
+<nut-rate checked-icon="heart-fill1" unchecked-icon="heart" v-model="value" />
 ```
 
 ### 自定义数量
 
-```html
-    <nut-rate 
-        :total="6" 
-        v-model:value="state.val4"
-    >
-    </nut-rate>
+``` html
+<nut-rate :total="6" v-model="value" />
 ```
 
 ### 自定义颜色
 
-```html
-    <nut-rate 
-        active-color="#FFC800" 
-        v-model:value="state.val5"
-    >
-    </nut-rate>
+``` html
+<nut-rate active-color="#FFC800" v-model="value" />
 ```
 
 ### 禁用状态
 
-```html
-    <nut-rate 
-        :disabled="true" 
-        v-model:value="state.val6"
-    >
-    </nut-rate>
+``` html
+<nut-rate disabled v-model="value" />
 ```
 
 ### 只读
 
-```html
-<nut-rate 
-    v-model:value="val"
-    :readOnly="true"
->
-</nut-rate>
+``` html
+<nut-rate v-model="value" readOnly />
 ```
-
-
 ### 绑定事件
 
-```html
-<nut-rate 
-    @click="onClick"
->
-</nut-rate>
+``` html
+<nut-rate v-model="value" @change="onChange" />
+```
+``` javascript
+import { ref } from 'vue';
+setup() {
+    const value = ref(3);
+    const onChange = (val)=>{
+        Toast.text(val);
+    }
+    return { value }
+}
 ```
-
 ### 自定义尺寸
 
-```html
-<nut-rate 
-    :size="35"
->
-</nut-rate>
+``` html
+<nut-rate v-model="value" icon-size="35" />
 ```
 
 
 
 ## Prop
 
-| 字段 | 说明 | 类型 | 默认值
-| ----- | ----- | ----- | -----
-| total | star 总数 | Number | 5
-| value | 当前 star 数,可使用 v-model 双向绑定数据 | Number | 0
-| size | star 大小 | Number | 25
-| spacing | 两个star的间距 | Number | 20
-| read-only | 是否只读 | Boolean | false
-| unchecked-icon | 使用图标(未选中) | String | -
-| checked-icon | 使用图标(选中) | String | -
+| 字段           | 说明                                      | 类型    | 默认值      |
+|----------------|-------------------------------------------|---------|-------------|
+| count          | star 总数                                 | Number  | 5           |
+| v-model        | 当前 star 数,可使用 v-model 双向绑定数据 | Number  | -           |
+| icon-size      | star 大小                                 | Number  | 18          |
+| active-color   | 自定义图标颜色                            | String  | #FA200C     |
+| unchecked-icon | 未选中[图标名称](#/Icon)或者图片链接      | String  | star-n      |
+| checked-icon   | 选中[图标名称](#/Icon)或者图片链接        | String  | star-fill-n |
+| allow-half     | 是否半星                                  | Boolean | false       |
+| readonly       | 是否只读                                  | Boolean | false       |
+| disabled       | 是否禁用                                  | Boolean | false       |
+| spacing        | 间距                                      | Number  | 20          |
 
 ## Event
-| 字段 | 说明 | 回调参数 
-|----- | ----- | ----- 
-| click | 点击star时触发 | star的index
+| 字段   | 说明                       | 回调参数 |
+|--------|----------------------------|----------|
+| change | 当前分值修改时时触发的事件 | 当前值   |

+ 12 - 20
src/packages/rate/index.scss

@@ -1,27 +1,19 @@
-@function toRGB($color) {
-  @return 'rgb(' + red($color) + ', ' + green($color) + ', ' + blue($color) +
-    ')';
-}
-
-@mixin nut-rate-bg($color) {
-  $svgColor: toRGB($color);
-  background: url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='16' height='16' viewBox='0 0 16 16'%3E %3Cpath fill='#{$svgColor}' d='M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899zM8 11.773l-3.492 1.836 0.667-3.888-2.825-2.753 3.904-0.567 1.746-3.537 1.746 3.537 3.904 0.567-2.825 2.753 0.667 3.888-3.492-1.836z'%3E%3C/path%3E %3C/svg%3E")
-    center no-repeat;
-}
-
-@mixin nut-rate-active-bg($color) {
-  $svgColor: toRGB($color);
-  background-image: url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='16' height='16' viewBox='0 0 16 16'%3E %3Cpath fill='#{$svgColor}' d='M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899z'%3E%3C/path%3E %3C/svg%3E");
-}
-
 .nut-rate {
-  .nut-rate-item {
-    display: inline-block;
-    vertical-align: bottom;
-    margin-right: 15px;
+  display: flex;
+  &-item {
+    flex-shrink: 0;
+    position: relative;
     background-size: cover;
     &:last-child {
       margin-right: 0;
     }
+
+    &__icon-half {
+      position: absolute;
+      width: 50%;
+      left: 0;
+      top: 0;
+      overflow: hidden;
+    }
   }
 }

+ 37 - 38
src/packages/rate/index.vue

@@ -2,40 +2,43 @@
   <view :class="classes">
     <view
       class="nut-rate-item"
-      :class="{ 'nut-rate-active': n <= state.current }"
-      v-for="n in total"
+      v-for="n in count"
       :key="n"
       @click="onClick($event, n)"
-      :style="{
-        marginRight: spacing + 'px'
-      }"
+      :style="{ marginRight: spacing + 'px' }"
     >
       <nut-icon
-        :size="size + 'px'"
-        :color="n <= state.current ? (disabled ? '#ccc' : activeColor) : '#ccc'"
-        :name="n <= state.current ? checkedIcon : uncheckedIcon"
-      >
-      </nut-icon>
+        :size="iconSize"
+        :color="n <= modelValue ? (disabled ? '#ccc' : activeColor) : '#ccc'"
+        :name="n <= modelValue ? checkedIcon : uncheckedIcon"
+      />
+      <nut-icon
+        v-if="allowHalf && modelValue + 1 > n"
+        :class="{ 'nut-rate-item__icon-half': allowHalf }"
+        :size="iconSize"
+        :color="activeColor"
+        :name="checkedIcon"
+      />
     </view>
   </view>
 </template>
 <script lang="ts">
-import { watch, reactive, computed } from 'vue';
+import { computed } from 'vue';
 import { createComponent } from '@/utils/create';
 const { componentName, create } = createComponent('rate');
 export default create({
   props: {
-    total: {
+    count: {
       type: [String, Number],
       default: 5
     },
-    value: {
+    modelValue: {
       type: [String, Number],
       default: 0
     },
-    size: {
+    iconSize: {
       type: [String, Number],
-      default: 25
+      default: 18
     },
     activeColor: {
       type: String,
@@ -49,7 +52,7 @@ export default create({
       type: String,
       default: 'star-fill-n'
     },
-    readOnly: {
+    readonly: {
       type: Boolean,
       default: false
     },
@@ -57,17 +60,17 @@ export default create({
       type: Boolean,
       default: false
     },
+    allowHalf: {
+      type: Boolean,
+      default: false
+    },
     spacing: {
       type: [String, Number],
-      default: 20
+      default: 14
     }
   },
-  emits: ['update:value', 'click'],
+  emits: ['update:modelValue', 'change'],
   setup(props, { emit }) {
-    const state = reactive({
-      current: props.value
-    });
-
     const classes = computed(() => {
       const prefixCls = componentName;
       return {
@@ -75,29 +78,25 @@ export default create({
       };
     });
 
-    const onClick = (e: Event, idx: number) => {
+    const onClick = (e: Event, index: number) => {
       e.preventDefault();
       e.stopPropagation();
-      if (props.disabled || props.readOnly) return;
-
-      if (idx === 1 && state.current === idx) {
-        state.current = 0;
+      if (props.disabled || props.readonly) return;
+      let value = 0;
+      if (index === 1 && props.modelValue === index) {
       } else {
-        state.current = idx;
+        value = index;
+        if (props.allowHalf) {
+          if ((e?.target as Element).className.includes('__icon-half')) {
+            value -= 0.5;
+          }
+        }
       }
-      emit('update:value', state.current);
-      emit('click', state.current);
+      emit('update:modelValue', value);
+      emit('change', value);
     };
 
-    watch(
-      () => props.value,
-      newVal => {
-        state.current = newVal;
-      }
-    );
-
     return {
-      state,
       classes,
       onClick
     };