diff --git a/docs/examples-docs/password-input.md b/docs/examples-docs/password-input.md new file mode 100644 index 000000000..440e96475 --- /dev/null +++ b/docs/examples-docs/password-input.md @@ -0,0 +1,88 @@ + + +## PasswordInput 密码输入框 +密码输入框组件通常与 [数字键盘](/zanui/vue/component/number-keyboard) 组件配合使用 + +### 使用指南 +``` javascript +import { PasswordInput, NumberKeyBoard } from 'vant'; + +Vue.component(PasswordInput.name, PasswordInput); +Vue.component(NumberKeyBoard.name, NumberKeyBoard); +``` + +### 代码演示 + +#### 基础用法 + +:::demo 基础用法 +```html + + + + + +``` + +```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 | 输入框聚焦时触发 | - | diff --git a/docs/src/doc.config.js b/docs/src/doc.config.js index 2451493c9..0e42554c6 100644 --- a/docs/src/doc.config.js +++ b/docs/src/doc.config.js @@ -147,6 +147,10 @@ module.exports = { "path": "/number-keyboard", "title": "NumberKeyboard 数字键盘" }, + { + "path": "/password-input", + "title": "PasswordInput 密码输入框" + }, { "path": "/radio", "title": "Radio 单选框" diff --git a/packages/index.js b/packages/index.js index 831cd0d82..f617e8870 100644 --- a/packages/index.js +++ b/packages/index.js @@ -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, diff --git a/packages/password-input/index.vue b/packages/password-input/index.vue new file mode 100644 index 000000000..2f16aed3a --- /dev/null +++ b/packages/password-input/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/vant-css/src/index.css b/packages/vant-css/src/index.css index 9585bd21a..dc819043f 100644 --- a/packages/vant-css/src/index.css +++ b/packages/vant-css/src/index.css @@ -34,6 +34,7 @@ @import './radio.css'; @import './switch.css'; @import './uploader.css'; +@import './password-input.css'; @import './number-keyboard.css'; /* action components */ diff --git a/packages/vant-css/src/password-input.css b/packages/vant-css/src/password-input.css new file mode 100644 index 000000000..eb6a64982 --- /dev/null +++ b/packages/vant-css/src/password-input.css @@ -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; + } + } +} diff --git a/test/unit/specs/password-input.spec.js b/test/unit/specs/password-input.spec.js new file mode 100644 index 000000000..e5ce480d5 --- /dev/null +++ b/test/unit/specs/password-input.spec.js @@ -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); + }); +});