一.BaseCounter
接收参数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
//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
单选
//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
//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
<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)"><</span>
<span class="show-year">{{now.getFullYear()}}</span>
<span class="btn-next" @click="yearClick(1)">></span>
</th>
<th colspan="3">
<span class="btn-prev" @click="monthClick(-1)"><</span>
<span class="show-month">{{months[now.getMonth()]}}</span>
<span class="btn-next" @click="monthClick(1)">></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>