提交 2e8e67fa 编写于 作者: F Frans

Merge branch 'v2'

## 2.1.1
`2019-4-22`
* :sparkles: feat: 新增文本框组件`TextInput`
* :sparkles: chore: 优化单元测试配置
* :sparkles: upd: `Stepper`组件新增支持小数
* :bug: fix: 修复`CountDown`组件在iOS上某些场景下的一个问题
* :bug: fix: 修复`Scroller`内部元素无法触发点击事件的问题
* :bug: fix: 修复国际化语言包一处错误
## 2.1.0
`2019-4-15`
......
const path = require('path');
const prodConf = require('./webpack.prod.conf.js');
const merge = require('webpack-merge');
......@@ -11,7 +12,8 @@ module.exports = merge(prodConf, {
use: {
loader: 'istanbul-instrumenter-loader',
options: { esModules: true }
}
},
include: path.resolve(__dirname, '../src/packages/')
},
{
test: /\.css$/,
......
{
"name": "@nutui/nutui",
"version": "2.1.0",
"version": "2.1.1",
"description": "一套轻量级移动端Vue组件库",
"typings": "dist/types/index.d.ts",
"main": "dist/nutui.js",
......
......@@ -442,6 +442,16 @@
"sort": "5",
"showDemo": true,
"author": "林如风"
},
{
"version": "1.0.0",
"name": "TextInput",
"chnName": "文本框",
"desc": "单行文本框",
"type": "component",
"sort": "1",
"showDemo": true,
"author": "Frans"
}
]
}
\ No newline at end of file
......@@ -85,6 +85,8 @@ import InfiniteLoading from './packages/infiniteloading/index.js';
import './packages/infiniteloading/infiniteloading.scss';
import Uploader from "./packages/uploader/index.js";
import "./packages/uploader/uploader.scss";
import TextInput from "./packages/textinput/index.js";
import "./packages/textinput/textinput.scss";
const packages = {
Cell,
......@@ -128,7 +130,8 @@ const packages = {
BackTop,
CountDown,
InfiniteLoading,
Uploader
Uploader,
TextInput
};
const components = {};
......
......@@ -3,7 +3,7 @@
<div class="nut-calendar" v-show="isVisible">
<div class="nut-calendar-control">
<span class="nut-calendar-confirm-btn" @click="confirm" v-if="(type == 'range' && currDate && currDate.length == 2) || type != 'range'">{{nutTranslate('lang.okBtnTxt')}}</span>
<span class="nut-calendar-cancel-btn" @click="closeActionSheet">{{nutTranslate('lang.cancelText')}}</span>
<span class="nut-calendar-cancel-btn" @click="closeActionSheet">{{nutTranslate('lang.cancelBtnTxt')}}</span>
<div class="nut-calendar-title">{{title || nutTranslate('lang.calendar.title')}}</div>
<div class="nut-calendar-week">
<span v-for="(item, index) of week" :key="index">{{item}}</span>
......
......@@ -85,11 +85,19 @@ const countdownTimer = {
startTime: {
// 可以是服务器当前时间
default: Date.now(),
type: [Number, String]
type: [Number, String],
validator(v) {
const dateStr = (new Date(v)).toString().toLowerCase();
return dateStr !== 'invalid date';
}
},
endTime: {
default: Date.now(),
type: [Number, String]
type: [Number, String],
validator(v) {
const dateStr = (new Date(v)).toString().toLowerCase();
return dateStr !== 'invalid date';
}
}
},
computed: {
......@@ -111,24 +119,32 @@ const countdownTimer = {
watch: {
paused(v, ov) {
if(!ov) {
this._curr = Date.now();
this._curr = this.getTimeStamp();
}else{
this.p += (Date.now() - this._curr);
this.p += (this.getTimeStamp() - this._curr);
}
}
},
methods: {
getTimeStamp(timeStr) {
if(!timeStr) return Date.now();
let t = timeStr;
t = t > 0? +t: t.toString().replace(/\-/g, '/');
return new Date(t).getTime();
}
},
created() {
if(this.interval > 0) {
let _start = 0;
const curr = Date.now();
const start = new Date(+this.startTime).getTime();
const end = new Date(+this.endTime).getTime();
const start = this.getTimeStamp(this.startTime);
const end = this.getTimeStamp(this.endTime);
const diffTime = curr - start;
this.restTime = end - (start + diffTime);
this.timer = setInterval(() => {
if(!this.paused) {
let restTime = end - (new Date().getTime() - this.p + diffTime);
let restTime = end - (this.getTimeStamp() - this.p + diffTime);
restTime -= 1000;
this.restTime = restTime;
if(restTime < 0) {
......
......@@ -48,8 +48,8 @@
export default {
data() {
return {
serverTime: 1551943874069,
end: 1559334689373,
serverTime: Date.now() - 30 * 1000,
end: Date.now() + 2 * 60 * 60 * 1000,
paused: false
};
},
......
......@@ -2,7 +2,7 @@
<nut-actionsheet :is-visible="isVisible" @close="closeActionSheet">
<div class="nut-picker" slot="custom" :class="customClassName ? customClassName : null">
<div class="nut-picker-control">
<span class="nut-picker-confirm-btn" @click="closeActionSheet">{{nutTranslate('lang.cancelText')}}</span>
<span class="nut-picker-confirm-btn" @click="closeActionSheet">{{nutTranslate('lang.cancelBtnTxt')}}</span>
<div class="nut-picker-title">{{title ? title : ''}}</div>
<span class="nut-picker-cancel-btn" @click="confirm">{{nutTranslate('lang.okBtnTxt')}}</span>
</div>
......
......@@ -77,7 +77,6 @@ export default {
mounted() {
let slot = [...this.$slots.default];
this.initCol(slot);
console.log(slot);
},
}
</script>
\ No newline at end of file
......@@ -89,7 +89,7 @@ export default {
},
touchStart(event) {
event.preventDefault();
// event.preventDefault();
let changedTouches = event.changedTouches[0];
this.touchParams.startX = changedTouches.pageX;
......
......@@ -158,7 +158,7 @@ export default {
},
touchStart(event) {
event.preventDefault();
// event.preventDefault();
let changedTouches = event.changedTouches[0];
this.touchParams.startY = changedTouches.pageY;
......@@ -167,6 +167,7 @@ export default {
},
touchMove(event) {
event.preventDefault();
let changedTouches = event.changedTouches[0];
this.touchParams.lastY = changedTouches.pageY;
......@@ -179,7 +180,7 @@ export default {
},
touchEnd(event) {
event.preventDefault();
// event.preventDefault();
let changedTouches = event.changedTouches[0];
this.touchParams.lastY = changedTouches.pageY;
......
......@@ -4,7 +4,7 @@
<div>
<nut-cell>
<span slot="title">
<nut-stepper @focus="focus" :blur="blur" :value.sync="val1" :max="12"></nut-stepper>
<nut-stepper @focus="focus" :blur="blur" :value.sync="val1" :max="12" :step="0.1" :decimalPlaces="1" @add="add"></nut-stepper>
</span>
<span slot="desc">
value: {{val1}} <button @click="reduce" class="demo-btn">-</button> <button @click="add" class="demo-btn">+</button>
......@@ -59,7 +59,7 @@
export default {
data() {
return {
val1: 0,
val1: 1,
val2: 12,
val3: 5,
val4: 0,
......@@ -76,7 +76,7 @@ export default {
},
add(v) {
console.log(v)
this.val1 = Number(this.val1) + 1;
// this.val1 = Number(this.val1) + 1;
},
reduce() {
this.val1 = Math.max(Number(this.val1) - 1, 0);
......
......@@ -65,6 +65,7 @@
| readonly | 是否只读 | Boolean | false
| transition | 是否需要过渡效果 | Boolean | true
| simple | 是否显示简单版 | Boolean | true
| decimalPlaces | 设置保留的小数位 | Number | 0
## Event
......
......@@ -80,7 +80,11 @@ export default {
value: {
type: [String, Number],
required: true
}
},
decimalPlaces: {
type: Number,
default: 0
}
},
data() {
return {
......@@ -105,11 +109,14 @@ export default {
}
},
watch: {
value(v, ov) {
if(v > this.max) v = this.max;
if(v < this.minNum) v = this.minNum;
this.num = v;
this.$emit('change', this.num);
value: {
handler(v, ov) {
if(v > this.max) v = this.max;
if(v < this.minNum) v = this.minNum;
this.num = v > 0? this.fixedDecimalPlaces(v): v;
this.$emit('change', this.num);
},
immediate: true
}
},
computed: {
......@@ -161,12 +168,16 @@ export default {
this.$emit('update:value', this.num);
this.$emit('change', this.num);
},
fixedDecimalPlaces(v) {
return Number(v).toFixed(this.decimalPlaces);
// .replace(/(\d+\.[^0]*)0+$/, '$1').replace(/\.$/, '')
},
add() {
this.num = Number(this.num);
if(this.num <= this.max - this.step && this.max > this.minNum) {
let [n1, n2] = this.num.toString().split('.');
let [n1, n2] = this.fixedDecimalPlaces(this.num + Number(this.step)).split('.');
let fixedLen = n2? n2.length: 0;
this.num = parseFloat((Number(n1) + Number(this.step)) + (n2? '.'+n2: '')).toFixed(fixedLen);
this.num = parseFloat(n1 + (n2? '.'+n2: '')).toFixed(fixedLen);
if(this.transition) {
this.showNum = false;
this.showAddAnim = true;
......@@ -192,9 +203,9 @@ export default {
},
reduce(){
if(this.num - this.step >= this.minNum) {
let [n1, n2] = this.num.toString().split('.');
let [n1, n2] = this.fixedDecimalPlaces(this.num - Number(this.step)).split('.');
let fixedLen = n2? n2.length: 0;
this.num = parseFloat((Number(n1) - this.step) + (n2? '.'+n2: '')).toFixed(fixedLen);
this.num = parseFloat(n1 + (n2? '.'+n2: '')).toFixed(fixedLen);
if(this.transition) {
this.showNum = false;
this.showAddAnim = false;
......
<template>
<div class="textinput-demo">
<h4>基本用法</h4>
<div>
<nut-cell>
<span slot="title">
<nut-textinput v-model="val" label="基本用法:" placeholder="请输入内容" />
</span>
</nut-cell>
<nut-cell>
<span slot="title">Value值: {{val}}</span>
</nut-cell>
</div>
<h4>配置</h4>
<div>
<nut-cell>
<span slot="title">
<nut-textinput :clearBtn="false" v-model="val2" label="无清空按钮:" placeholder="请输入内容" />
</span>
</nut-cell>
<nut-cell>
<span slot="title">
<nut-textinput :disabled="true" label="禁用状态:" placeholder="请输入内容" />
</span>
</nut-cell>
<nut-cell>
<span slot="title">
<nut-textinput placeholder="请输入密码" v-model="val3" label="密码框:" type="password" />
</span>
</nut-cell>
<nut-cell>
<span slot="title">
<nut-textinput placeholder="请输入内容" v-model="val4" label="无边框:" :hasBorder="false" />
</span>
</nut-cell>
</div>
<h4>使用input原生属性</h4>
<div>
<nut-cell>
<span slot="title">
<nut-textinput
placeholder="maxlength=5"
maxlength="5"
label="限制文字数:"
:outline="false"
:hasBorder="false"
/>
</span>
</nut-cell>
<nut-cell>
<span slot="title">
<nut-textinput placeholder="请输入内容" v-model="val5" readonly label="只读:" :outline="false" />
</span>
</nut-cell>
</div>
<h4>使用input原生事件</h4>
<div>
<nut-cell>
<span slot="title">
<nut-textinput
placeholder="请输入内容"
label="focus/blur事件:"
@focus="onFocus"
@blur="onBlur"
v-model="val6"
:hasBorder="false"
/>
</span>
</nut-cell>
<nut-cell>
<span slot="title">事件触发结果:{{result}}</span>
</nut-cell>
</div>
<h4>自定义Class</h4>
<div>
<nut-cell>
<span slot="title">
<nut-textinput class="my-input" v-model="val7" placeholder="请输入搜索内容" />
</span>
</nut-cell>
</div>
</div>
</template>
<script>
export default {
data() {
return {
val: "",
val2: "",
val3: "",
val4: "",
val5: "我使用了readonly原生属性",
val6: "",
val7: "",
result: "尚未触发"
};
},
methods: {
onFocus() {
this.result = "focus事件触发!";
},
onBlur() {
this.result = "blur事件触发!";
}
}
};
</script>
<style lang="scss">
.textinput-demo {
padding-left: 0;
padding-right: 0;
}
.textinput-demo h4 {
padding: 0 10px;
}
.nut-textinput {
&.my-input {
input {
height: 36px;
border-radius: 20px;
padding-left: 40px;
padding-right: 40px;
background: url("data:image/svg+xml, %3Csvg version='1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M15.8 14.3l-3.3-3.1c1-1.1 1.6-2.6 1.6-4.1 0-3.6-3.1-6.6-7-6.6s-7 3-7 6.6c0 3.6 3.1 6.6 7 6.6 1.7 0 3.2-.6 4.4-1.5l3.3 3.1c.1.1.3.2.5.2s.4-.1.5-.2c.3-.3.3-.7 0-1zM1.5 7.1c0-2.9 2.5-5.2 5.6-5.2 3.1 0 5.6 2.3 5.6 5.2 0 2.9-2.5 5.2-5.6 5.2-3.2 0-5.6-2.3-5.6-5.2z' fill-rule='evenodd' clip-rule='evenodd' fill='%23999999'/%3E%3C/svg%3E")
no-repeat 10px center;
background-size: 18px;
}
.nut-textinput-clear {
right: 15px;
}
}
}
</style>
\ No newline at end of file
# TextInput 文本框
单行文本框。
## 基本用法
```html
<nut-textinput
v-model="val"
label="无清空按钮:"
placeholder="请输入内容"
:clearBtn="true"
:disabled="false"
/>
```
## 高级用法
密码框
```html
<nut-textinput
placeholder="请输入密码"
v-model="val"
label="密码框:"
type="password"
/>
```
禁用
```html
<nut-textinput
label="禁用状态:"
:disabled="true"
placeholder="请输入内容"
/>
```
使用input原生属性
```html
<!-- maxlength -->
<nut-textinput
placeholder="maxlength=5"
maxlength="5"
label="限制文字数:"
/>
<!-- readonly -->
<nut-textinput
placeholder="请输入内容"
v-model="val"
readonly
label="只读:"
/>
```
使用input原生事件
```html
<nut-textinput
placeholder="请输入内容"
label="focus/blur事件:"
@focus="onFocus"
@blur="onBlur"
v-model="val"
/>
```
```javascript
export default {
data() {
return {
val: ""
};
},
methods: {
onFocus() {
console.log("focus事件触发!");
},
onBlur() {
console.log("blur事件触发!");
}
}
};
```
## Prop
| 字段 | 说明 | 类型 | 默认值
|----- | ----- | ----- | -----
| value | 当前input值,可使用 v-model 双向绑定数据 | String | ''
| label | 文本框前面的标签 | String | ''
| disabled | 是否禁用 | Boolean | false
| clearBtn | 是否需要情况按钮 | Boolean | true
| hasBorder | 是否需要边框 | Boolean | true
\ No newline at end of file
import TextInput from './textinput.vue';
import './textinput.scss';
TextInput.install = function(Vue) {
Vue.component(TextInput.name, TextInput);
};
export default TextInput
\ No newline at end of file
.nut-textinput {
display: flex;
align-items: center;
position: relative;
input {
flex: 1;
height: 40px;
appearance: none;
font-size: 14px;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid #DDDFE6;
background-color: '#FFFFFF';
padding: 0 30px 0 10px;
&::-webkit-input-placeholder {
color: #C1C4CB;
font-style: normal;
}
}
.nut-textinput-clear {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
height: 20px;
width: 20px;
svg {
fill: #999999;
}
}
}
.nut-textinput-disabled {
input {
background-color: #F5F7FA;
border-color: #E5E7ED;
color: #999999;
&::-webkit-input-placeholder {
color: #D1D3D9;
}
}
}
\ No newline at end of file
<template>
<label :class="['nut-textinput',{'nut-textinput-disabled':disabled}]">
<span class="nut-textinput-label" v-if="label">{{label}}</span>
<input
:type="type"
:value="value"
:disabled="disabled"
:style="{'borderWidth':hasBorder?'':0,'outline':outline?'':'none','padding-right':clearBtn?'':'10px'}"
v-bind="$attrs"
v-on="inputListeners"
>
<span class="nut-textinput-clear" v-if="clearBtn" v-show="clearBtnShow" @click="clear">
<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path
d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm2.8 9.7c.3.3.3.8 0 1.1s-.8.3-1.1 0L8 9.1l-1.7 1.7c-.3.3-.8.3-1.1 0-.3-.3-.3-.8 0-1.1L6.9 8 5.2 6.3c-.3-.3-.3-.8 0-1.1.3-.3.8-.3 1.1 0L8 6.9l1.7-1.7c.3-.3.8-.3 1.1 0 .3.3.3.8 0 1.1L9.1 8l1.7 1.7z"
></path>
</svg>
</span>
</label>
</template>
<script>
export default {
name: "nut-textinput",
props: {
value: {
type: String,
default: ""
},
type: {
type: String,
default: "text"
},
label: {
type: String,
default: ""
},
disabled: {
type: Boolean,
default: false
},
hasBorder: {
type: Boolean,
default: true
},
outline: {
type: Boolean,
default: false
},
clearBtn: {
type: Boolean,
default: true
}
},
computed: {},
data() {
return {
clearBtnShow: false
};
},
computed: {
inputListeners() {
let vm = this;
return Object.assign({}, this.$listeners, {
input: function(event) {
vm.clearBtnShow = !!event.target.value;
vm.$emit("input", event.target.value);
}
});
}
},
methods: {
clear() {
this.$emit("input", "");
this.clearBtnShow = false;
}
},
mounted() {
this.clearBtnShow = !!this.value;
}
};
</script>
\ No newline at end of file
......@@ -59,3 +59,4 @@ export declare class BackTop extends UIComponent {}
export declare class Scroller extends UIComponent {}
export declare class CountDown extends UIComponent {}
export declare class Uploader extends UIComponent {}
export declare class TextInput extends UIComponent {}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册