add PasswordInput component

This commit is contained in:
陈嘉涵 2017-09-11 11:31:03 +08:00
parent 4c45b5eb39
commit eb724555cb
7 changed files with 254 additions and 0 deletions

View File

@ -0,0 +1,88 @@
<script>
export default {
data() {
return {
value: '',
showKeyboard: true
}
},
methods: {
onInput(key) {
this.value = (this.value + key).slice(0, 6);
},
onDelete() {
this.value = this.value.slice(0, this.value.length - 1);
}
}
}
</script>
## PasswordInput 密码输入框
密码输入框组件通常与 [数字键盘](/zanui/vue/component/number-keyboard) 组件配合使用
### 使用指南
``` javascript
import { PasswordInput, NumberKeyBoard } from 'vant';
Vue.component(PasswordInput.name, PasswordInput);
Vue.component(NumberKeyBoard.name, NumberKeyBoard);
```
### 代码演示
#### 基础用法
:::demo 基础用法
```html
<!-- 密码输入框 -->
<van-password-input
:value="value"
info="密码为 6 位数字"
@focus="showKeyboard = true"
></van-password-input>
<!-- 数字键盘 -->
<van-number-keyboard
:show="showKeyboard"
@input="onInput"
@delete="onDelete"
@blur="showKeyboard = false"
></van-number-keyboard>
```
```javascript
export default {
data() {
return {
value: '',
showKeyboard: true
}
},
methods: {
onInput(key) {
this.value = (this.value + key).slice(0, 6);
},
onDelete() {
this.value = this.value.slice(0, this.value.length - 1);
}
}
}
```
:::
### API
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| value | 密码值 | `String` | `''` | - |
| length | 密码长度 | `Number` | `6` | - |
| info | 输入框下方提示 | `String` | - | - |
| errorInfo | 输入框下方错误提示 | `String` | - | - |
### Event
| 事件名 | 说明 | 参数 |
|-----------|-----------|-----------|
| focus | 输入框聚焦时触发 | - |

View File

@ -147,6 +147,10 @@ module.exports = {
"path": "/number-keyboard",
"title": "NumberKeyboard 数字键盘"
},
{
"path": "/password-input",
"title": "PasswordInput 密码输入框"
},
{
"path": "/radio",
"title": "Radio 单选框"

View File

@ -26,6 +26,7 @@ import NavBar from './nav-bar';
import NoticeBar from './notice-bar';
import NumberKeyboard from './number-keyboard';
import Panel from './panel';
import PasswordInput from './password-input';
import Picker from './picker';
import Popup from './popup';
import Progress from './progress';
@ -78,6 +79,7 @@ const components = [
NoticeBar,
NumberKeyboard,
Panel,
PasswordInput,
Picker,
Popup,
Progress,
@ -146,6 +148,7 @@ export {
NoticeBar,
NumberKeyboard,
Panel,
PasswordInput,
Picker,
Popup,
Progress,

View File

@ -0,0 +1,43 @@
<template>
<div class="van-password-input">
<ul class="van-password-input__security van-hairline--surround" @touchstart.stop="$emit('focus')">
<li v-for="visibility in points" class="van-hairline">
<i :style="`visibility: ${visibility}`" />
</li>
</ul>
<div
v-if="errorInfo || info"
v-text="errorInfo || info"
:class="errorInfo ? 'van-password-input__error-info' : 'van-password-input__info'"
/>
</div>
</template>
<script>
export default {
name: 'van-password-input',
props: {
info: String,
errorInfo: String,
value: {
type: String,
default: ''
},
length: {
type: Number,
default: 6
}
},
computed: {
points() {
const arr = [];
for (let i = 0; i < this.length; i++) {
arr[i] = this.value[i] ? 'visible' : 'hidden';
}
return arr;
}
}
};
</script>

View File

@ -34,6 +34,7 @@
@import './radio.css';
@import './switch.css';
@import './uploader.css';
@import './password-input.css';
@import './number-keyboard.css';
/* action components */

View File

@ -0,0 +1,59 @@
@import "./common/var.css";
.van-password-input {
margin: 0 15px;
user-select: none;
position: relative;
&:focus {
outline: none;
}
&__info,
&__error-info {
font-size: 14px;
margin-top: 15px;
text-align: center;
}
&__info {
color: $gray-dark;
}
&__error-info {
color: $red;
}
&__security {
width: 100%;
height: 50px;
display: flex;
background-color: $white;
&::after {
border-radius: 6px;
}
li {
flex: 1;
height: 100%;
position: relative;
&:not(:first-of-type)::after {
border-left-width: 1px;
}
}
i {
position: absolute;
left: 50%;
top: 50%;
width: 10px;
height: 10px;
margin: -5px 0 0 -5px;
visibility: hidden;
border-radius: 100%;
background-color: $black;
}
}
}

View File

@ -0,0 +1,56 @@
import PasswordInput from 'packages/password-input';
import { mount } from 'avoriaz';
describe('PasswordInput', () => {
let wrapper;
afterEach(() => {
wrapper && wrapper.destroy();
});
it('create a PasswordInput', () => {
wrapper = mount(PasswordInput, {});
expect(wrapper.find('.van-password-input').length).to.equal(1);
});
it('create a PasswordInput with value && info', (done) => {
wrapper = mount(PasswordInput, {
propsData: {
value: '000',
info: '测试info'
}
});
expect(wrapper.find('.van-password-input i')[2].hasStyle('visibility', 'visible')).to.be.true;
expect(wrapper.find('.van-password-input i')[3].hasStyle('visibility', 'visible')).to.be.false;
expect(wrapper.find('.van-password-input__info')[0].text()).to.equal('测试info');
wrapper.vm.value = '0000';
wrapper.vm.errorInfo = '测试errorInfo';
wrapper.vm.$nextTick(() => {
expect(wrapper.find('.van-password-input i')[3].hasStyle('visibility', 'visible')).to.be.true;
expect(wrapper.find('.van-password-input__info').length).to.equal(0);
expect(wrapper.find('.van-password-input__error-info')[0].text()).to.equal('测试errorInfo');
done();
});
});
it('listen to focus event', () => {
wrapper = mount(PasswordInput, {});
const focus = sinon.spy();
wrapper.vm.$on('focus', focus);
wrapper.find('.van-password-input__security')[0].trigger('touchstart');
expect(focus.calledOnce).to.be.true;
});
it('change password length', () => {
wrapper = mount(PasswordInput, {
propsData: {
length: 2
}
});
expect(wrapper.find('.van-password-input i').length).to.equal(2);
});
});