天天看点

公用的vue小组件

一.BaseCounter

公用的vue小组件

接收参数max、min,当number内容改变时返回父组件on-change事件

//counter.vue

<template>
  <div class="counter-component">
    <div class="counter-btn" @click="minus"> - </div>
    <div class="counter-show">
      <input type="text" v-model="number" @keyup="fixNumber">
    </div>
    <div class="counter-btn" @click="add"> + </div>
  </div>
</template>

<script>
  export default {
    props: {
      max: {
        type: Number,
        default: 5
      },
      min: {
        type: Number,
        default: 1
      }
    },
    data () {
      return {
        number: this.min
      }
    },
    watch: {
      number () {
        this.$emit('on-change', this.number)
      }
    },
    methods: {
      fixNumber () {
        let fix;
        if (typeof this.number === 'string') {
          fix = Number(this.number.replace(/\D/g, ''))
        }
        else {
          fix = this.number
        }
        if (fix > this.max || fix < this.min) {
          fix = this.min
        }
        this.number = fix
      },
      minus () {
        if (this.number <= this.min) {
          return
        }
        this.number --
      },
      add () {
        if (this.number >= this.max) {
          return
        }
        this.number ++
      }
    }
  }
</script>

<style scoped>
  .counter-component {
    position: relative;
    display: inline-block;
    overflow: hidden;
    vertical-align: middle;
  }
  .counter-show {
    float: left;
  }
  .counter-show input {
    border: none;
    border-top: 1px solid #e3e3e3;
    border-bottom: 1px solid #e3e3e3;
    height: 23px;
    line-height: 23px;
    width: 30px;
    outline: none;
    text-indent: 4px;
  }
  .counter-btn {
    border: 1px solid #e3e3e3;
    float: left;
    height: 25px;
    line-height: 25px;
    width: 25px;
    text-align: center;
    cursor: pointer;
  }
  .counter-btn:hover {
    border-color: #4fc08d;
    background: #4fc08d;
    color: #fff;
  }

</style>
      

//父组件

<v-counter @on-change="onParamChange('buyNum', $event)"></v-counter>      

二.BaseSelection

公用的vue小组件

//selection.vue

<template>
  <div class="selection-component">
    <div class="selection-show" @click="toggleDrop">
      <span>{{selections[nowIndex].label}}</span>
      <div class="arrow"></div>
    </div>
    <div class="selection-list" v-if="isDrop">
      <ul>
        <li v-for="(item, index) in selections" @click="chooseSelection(index)">{{item.label}}</li>
      </ul>
    </div>
  </div>
</template>

<script>
  export default {
    props: {
      selections: {
        type: Array,
        default: [{
          label: 'test',
          value: 0
        }]
      }
    },
    data () {
      return {
        isDrop: false,
        nowIndex: 0
      }
    },
    methods: {
      toggleDrop(){
        this.isDrop = !this.isDrop;
      },
      chooseSelection(index){
        this.nowIndex = index;
        this.isDrop = false;
        //通过on-change事件,把要购买的东西传回给父组件
        this.$emit('on-change', this.selections[this.nowIndex]);
      }
    }
  }
</script>

<style scoped>
  .selection-component {
    position: relative;
    display: inline-block;
  }
  .selection-show {
    border: 1px solid #e3e3e3;
    padding: 0 20px 0 10px;
    display: inline-block;
    position: relative;
    cursor: pointer;
    height: 25px;
    line-height: 25px;
    border-radius: 3px;
    background: #fff;
  }
  .selection-show .arrow {
    display: inline-block;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid #e3e3e3;
    width: 0;
    height: 0;
    margin-top: -1px;
    margin-left: 6px;
    margin-right: -14px;
    vertical-align: middle;
  }
  .selection-list {
    display: inline-block;
    position: absolute;
    left: 0;
    top: 25px;
    width: 100%;
    background: #fff;
    border-top: 1px solid #e3e3e3;
    border-bottom: 1px solid #e3e3e3;
    z-index: 5;
  }
  .selection-list li {
    padding: 5px 15px 5px 10px;
    border-left: 1px solid #e3e3e3;
    border-right: 1px solid #e3e3e3;
    cursor: pointer;
    background: #fff;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;

  }
  .selection-list li:hover {
    background: #e3e3e3;
  }
</style>
      

//父组件

<v-selection :selections="buyTypes" @on-change="onParamChange('buyType', $event)"></v-selection>      

三.BaseChooser

公用的vue小组件

单选

//chooser.vue

<template>
  <div class="chooser-component">
    <ul class="chooser-list">
      <li
        v-for="(item, index) in selections"
        @click="chosenSelection(index)"
        :title="item.label"
        :class="{active:index === nowIndex}"
      >{{ item.label }}</li>
    </ul>
  </div>
  </div>
</template>

<script>
  export default {
    props: {
      selections: {
        type: Array,
        default: [{
          label: 'test',
          value: 0
        }]
      }
    },
    data () {
      return {
        nowIndex: 0
      }
    },
    methods: {
      chosenSelection (index) {
        this.nowIndex = index;
        this.$emit('on-change', this.selections[index])
      }
    }
  }
</script>

<style scoped>
  .chooser-component {
    position: relative;
    display: inline-block;
  }
  .chooser-list li{
    display: inline-block;
    border: 1px solid #e3e3e3;
    height: 25px;
    line-height: 25px;
    padding: 0 8px;
    margin-right: 5px;
    border-radius: 3px;
    text-align: center;
    cursor: pointer;
  }
  .chooser-list li.active {
    border-color: #4fc08d;
    background: #4fc08d;
    color: #fff;
  }
</style>
      

//父组件

<v-chooser
  :selections="periodList"
  @on-change="onParamChange('period', $event)"></v-chooser>      

四.BaseMultiplyChooser

公用的vue小组件

//multiplyChooser.vue

<template>
  <div class="chooser-component">
    <ul class="chooser-list">
      <li v-for="(item, index) in selections" @click="toggleSelection(index)" :title="item.label" :class="{active: checkActive(index)}">
        {{ item.label }}
      </li>
    </ul>
  </div>
</template>

<script>
  import _ from 'lodash'
  export default{
    props: {
      selections: {
        type: Array,
        default: [{
          label: 'test',
          value: 0
        }]
      }
    },
    data(){
      return {
        nowIndexes: [0]
      }
    },
    methods:{
      toggleSelection(index){
        if(this.nowIndexes.indexOf(index) === -1){
          this.nowIndexes.push(index);
        }else {
          this.nowIndexes = _.remove(this.nowIndexes, (idx) => {
              return idx !== index
            })
        }
        let nowObjArray = _.map(this.nowIndexes, (idx) => {
            return this.selections[idx]
          })
        this.$emit('on-change', nowObjArray);
      },
      checkActive(index){
        return this.nowIndexes.indexOf(index) !== -1
      }
    }
  }
</script>

<style scoped>
  .chooser-component {
    position: relative;
    display: inline-block;
  }
  .chooser-list li{
    display: inline-block;
    border: 1px solid #e3e3e3;
    height: 25px;
    line-height: 25px;
    padding: 0 8px;
    margin-right: 5px;
    border-radius: 3px;
    text-align: center;
    cursor: pointer;
  }
  .chooser-list li.active {
    border-color: #4fc08d;
    background: #4fc08d;
    color: #fff;
  }
</style>
      

//父组件

<v-mul-chooser
  :selections="versionList"
  @on-change="onParamChange('versions', $event)"></v-mul-chooser>      

五.BaseDialog

http://blog.csdn.net/xidongdong1/article/details/78666709

六.datePicker

公用的vue小组件
<style scoped>
  .datetime-picker {
    position: relative;
    display: inline-block;
    font-family: "Segoe UI","Lucida Grande",Helvetica,Arial,"Microsoft YaHei";
    -webkit-font-smoothing: antialiased;
    color: #333;
  }

  .datetime-picker * {
    box-sizing: border-box;
  }

  .datetime-picker input {
    width: 100%;
    padding: 5px 10px;
    height: 30px;
    outline: 0 none;
    border: 1px solid #ccc;
    font-size: 13px;
  }

  .datetime-picker .picker-wrap {
    position: absolute;
    z-index: 1000;
    width: 238px;
    height: 280px;
    margin-top: 2px;
    background-color: #fff;
    box-shadow: 0 0 6px #ccc;
  }

  .datetime-picker table {
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
    text-align: center;
    font-size: 13px;
  }

  .datetime-picker tr {
    height: 34px;
    border: 0 none;
  }

  .datetime-picker th, .datetime-picker td {
    user-select: none;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 0 none;
    line-height: 34px;
    text-align: center;
  }

  .datetime-picker td {
    cursor: pointer;
  }

  .datetime-picker td:hover {
    background-color: #f0f0f0;
  }

  .datetime-picker td.date-pass, .datetime-picker td.date-future {
    color: #aaa;
  }

  .datetime-picker td.date-active {
    background-color: #ececec;
    color: #3bb4f2;
  }

  .datetime-picker .date-head {
    background-color: #3bb4f2;
    text-align: center;
    color: #fff;
    font-size: 14px;
  }

  .datetime-picker .date-days {
    color: #3bb4f2;
    font-size: 14px;
  }

  .datetime-picker .show-year {
    display: inline-block;
    min-width: 62px;
    vertical-align: middle;
  }

  .datetime-picker .show-month {
    display: inline-block;
    min-width: 28px;
    vertical-align: middle;
  }

  .datetime-picker .btn-prev,
  .datetime-picker .btn-next {
    cursor: pointer;
    display: inline-block;
    padding: 0 10px;
    vertical-align: middle;
  }

  .datetime-picker .btn-prev:hover,
  .datetime-picker .btn-next:hover {
    background: rgba(16, 160, 234, 0.5);
  }
</style>

<template>
  <div class="datetime-picker" :style="{ width: width }">
    <input
      type="text"
      :style="styleObj"
      :readonly="readonly"
      v-model="showValue"
      @click="show = !show">
    <div class="picker-wrap" v-show="show">
      <table class="date-picker">
        <thead>
        <tr class="date-head">
          <th colspan="4">
            <span class="btn-prev" @click="yearClick(-1)">&lt;</span>
            <span class="show-year">{{now.getFullYear()}}</span>
            <span class="btn-next" @click="yearClick(1)">&gt;</span>
          </th>
          <th colspan="3">
            <span class="btn-prev" @click="monthClick(-1)">&lt;</span>
            <span class="show-month">{{months[now.getMonth()]}}</span>
            <span class="btn-next" @click="monthClick(1)">&gt;</span>
          </th>
        </tr>
        <tr class="date-days">
          <th v-for="day in days">{{day}}</th>
        </tr>
        </thead>
        <tbody>
        <tr v-for="i in 6">
          <td v-for="j in 7"
              :class="date[i * 7 + j] && date[i * 7 + j].status"
              :date="date[i * 7 + j] && date[i * 7 + j].date"
              @click="pickDate(i * 7 + j)">{{date[i * 7 + j] && date[i * 7 + j].text}}</td>
        </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script>
  export default {
    props: {
      width: { type: String, default: '238px' },
      readonly: { type: Boolean, default: false },
      value: { type: String, default: '' },
      format: { type: String, default: 'YYYY-MM-DD' },
      styleObj: {type: Object, default: null}
    },
    data () {
      return {
        show: false,
        showValue: '',
        days: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
        months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        date: [],
        now: new Date()
      };
    },
    watch: {
      now () {
        this.update();
      },
      show () {
        this.update();
      }
    },
    methods: {
      close () {
        this.show = false;
      },
      update () {
        var arr = [];
        var time = new Date(this.now);
        time.setMonth(time.getMonth(), 1);           // the first day
        var curFirstDay = time.getDay();
        curFirstDay === 0 && (curFirstDay = 7);
        time.setDate(0);                             // the last day
        var lastDayCount = time.getDate();
        for (let i = curFirstDay; i > 0; i--) {
          arr.push({
            text: lastDayCount - i + 1,
            time: new Date(time.getFullYear(), time.getMonth(), lastDayCount - i + 1),
            status: 'date-pass'
          });
        }

        time.setMonth(time.getMonth() + 2, 0);       // the last day of this month
        var curDayCount = time.getDate();
        time.setDate(1);                             // fix bug when month change
        var value = this.value || this.stringify(new Date());
        for (let i = 0; i < curDayCount; i++) {
          let tmpTime = new Date(time.getFullYear(), time.getMonth(), i + 1);
          let status = '';
          this.stringify(tmpTime) === value && (status = 'date-active');
          arr.push({
            text: i + 1,
            time: tmpTime,
            status: status
          });
        }

        var j = 1;
        while (arr.length < 42) {
          arr.push({
            text: j,
            time: new Date(time.getFullYear(), time.getMonth() + 1, j),
            status: 'date-future'
          });
          j++;
        }
        this.date = arr;
      },
      yearClick (flag) {
        this.now.setFullYear(this.now.getFullYear() + flag);
        this.now = new Date(this.now);
      },
      monthClick (flag) {
        this.now.setMonth(this.now.getMonth() + flag);
        this.now = new Date(this.now);
      },
      pickDate (index) {
        this.show = false;
        this.now = new Date(this.date[index].time);
        this.showValue = this.stringify();
        this.$emit('on-change', this.showValue);
      },
      parse (str) {
        var time = new Date(str);
        return isNaN(time.getTime()) ? null : time;
      },
      stringify (time = this.now, format = this.format) {
        var year = time.getFullYear();
        var month = time.getMonth() + 1;
        var date = time.getDate();
        var monthName = this.months[time.getMonth()];

        var map = {
          YYYY: year,
          MMM: monthName,
          MM: ('0' + month).slice(-2),
          M: month,
          DD: ('0' + date).slice(-2),
          D: date
        };
        return format.replace(/Y+|M+|D+/g, function (str) {
          return map[str];
        });
      },
      leave (e) {
        if (!this.$el.contains(e.target)) {
          this.close();
        }
      }
    },
    mounted () {
      this.now = this.parse(this.value) || new Date();
      document.addEventListener('click', this.leave, false);
    },
    beforeDestroy () {
      document.removeEventListener('click', this.leave, false);
    }
  };
</script>
      

继续阅读