From 79250f5cc301b806266b088a902101d7710dd6c0 Mon Sep 17 00:00:00 2001 From: ElsaOOo Date: Fri, 26 Nov 2021 15:27:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20input=E7=BB=84=E4=BB=B6v-model:value?= =?UTF-8?q?=E6=9B=B4=E6=94=B9=E4=B8=BAv-model,=20=E5=8D=87=E7=BA=A7husky,?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E6=8F=90=E4=BA=A4=E6=97=B6lint=E6=97=A0?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/devui-vue/.eslintrc.js | 30 ++--- packages/devui-vue/.husky/commit-msg | 4 + packages/devui-vue/.husky/pre-commit | 4 + .../devui/back-top/src/hooks/index.ts | 2 +- ...eventListener.ts => use-event-listener.ts} | 0 .../comment/src/{getSlot.ts => get-slot.ts} | 0 .../devui/input/__tests__/input.spec.ts | 122 +++++++++--------- packages/devui-vue/devui/input/src/input.tsx | 91 +++++++------ .../devui-vue/devui/input/src/use-input.tsx | 16 +-- .../devui-vue/docs/components/input/index.md | 53 ++++---- packages/devui-vue/package.json | 11 +- 11 files changed, 172 insertions(+), 161 deletions(-) create mode 100755 packages/devui-vue/.husky/commit-msg create mode 100755 packages/devui-vue/.husky/pre-commit rename packages/devui-vue/devui/back-top/src/hooks/{use-eventListener.ts => use-event-listener.ts} (100%) rename packages/devui-vue/devui/comment/src/{getSlot.ts => get-slot.ts} (100%) diff --git a/packages/devui-vue/.eslintrc.js b/packages/devui-vue/.eslintrc.js index 34ce586d..89aa0110 100644 --- a/packages/devui-vue/.eslintrc.js +++ b/packages/devui-vue/.eslintrc.js @@ -1,33 +1,29 @@ module.exports = { - parser: 'vue-eslint-parser', + root: true, + parser: '@typescript-eslint/parser', parserOptions: { - parser: '@typescript-eslint/parser', sourceType: 'module', ecmaVersion: 6, ecmaFeatures: { jsx: true, - tsx: true, - }, + tsx: true + } }, env: { browser: true, node: true, jest: true, - es6: true, + es6: true }, plugins: ['@typescript-eslint'], extends: [ 'plugin:@typescript-eslint/recommended', 'plugin:vue/vue3-recommended', 'plugin:import/recommended', - 'plugin:import/typescript', + 'plugin:import/typescript' ], rules: { - quotes: [ - 'error', - 'single', - { avoidEscape: true, allowTemplateLiterals: true }, - ], + quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }], 'no-undef': 2, 'vue/max-attributes-per-line': 'off', 'vue/no-multiple-template-root': 'off', @@ -38,13 +34,15 @@ module.exports = { { multiline: { delimiter: 'none', - requireLast: false, + requireLast: false }, singleline: { delimiter: 'semi', - requireLast: true, - }, - }, + requireLast: true + } + } ], - }, + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['error'] + } } diff --git a/packages/devui-vue/.husky/commit-msg b/packages/devui-vue/.husky/commit-msg new file mode 100755 index 00000000..80e240c9 --- /dev/null +++ b/packages/devui-vue/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +cd ./packages/devui-vue && npx commitlint --edit $1 \ No newline at end of file diff --git a/packages/devui-vue/.husky/pre-commit b/packages/devui-vue/.husky/pre-commit new file mode 100755 index 00000000..9a45e99d --- /dev/null +++ b/packages/devui-vue/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +cd ./packages/devui-vue && npx @ls-lint/ls-lint && npx lint-staged diff --git a/packages/devui-vue/devui/back-top/src/hooks/index.ts b/packages/devui-vue/devui/back-top/src/hooks/index.ts index 4ae94650..b9fd2b4d 100644 --- a/packages/devui-vue/devui/back-top/src/hooks/index.ts +++ b/packages/devui-vue/devui/back-top/src/hooks/index.ts @@ -1,7 +1,7 @@ import usePosition from './use-position.ts' import useTarget from './use-target.ts' import useVisibility from './use-visibility.ts' -import useEventListener from './use-eventListener.ts' +import useEventListener from './use-event-listener.ts' import useThrottle from './use-throttle.ts' export { usePosition, useTarget, useVisibility, useEventListener, useThrottle } diff --git a/packages/devui-vue/devui/back-top/src/hooks/use-eventListener.ts b/packages/devui-vue/devui/back-top/src/hooks/use-event-listener.ts similarity index 100% rename from packages/devui-vue/devui/back-top/src/hooks/use-eventListener.ts rename to packages/devui-vue/devui/back-top/src/hooks/use-event-listener.ts diff --git a/packages/devui-vue/devui/comment/src/getSlot.ts b/packages/devui-vue/devui/comment/src/get-slot.ts similarity index 100% rename from packages/devui-vue/devui/comment/src/getSlot.ts rename to packages/devui-vue/devui/comment/src/get-slot.ts diff --git a/packages/devui-vue/devui/input/__tests__/input.spec.ts b/packages/devui-vue/devui/input/__tests__/input.spec.ts index 3256372c..3377eac5 100644 --- a/packages/devui-vue/devui/input/__tests__/input.spec.ts +++ b/packages/devui-vue/devui/input/__tests__/input.spec.ts @@ -1,38 +1,38 @@ -import { mount } from '@vue/test-utils'; -import { ref, nextTick } from 'vue'; -import DInput from '../src/input'; +import { mount } from '@vue/test-utils' +import { ref, nextTick } from 'vue' +import DInput from '../src/input' describe('d-input', () => { it('d-input render work', async () => { - const value = ref('abc'); + const value = ref('abc') const wrapper = mount({ components: { DInput }, template: ` - + `, - setup () { + setup() { return { value - }; + } } - }); - const input = wrapper.find('input'); - expect(input.attributes('dinput')).toBe('true'); - expect(input.element.value).toBe('abc'); + }) + const input = wrapper.find('input') + expect(input.attributes('dinput')).toBe('true') + expect(input.element.value).toBe('abc') - await input.setValue('def'); - expect(value.value).toBe('def'); + await input.setValue('def') + expect(value.value).toBe('def') - value.value = 'thx'; - await nextTick(); - expect(input.element.value).toBe('thx'); - }); + value.value = 'thx' + await nextTick() + expect(input.element.value).toBe('thx') + }) it('d-input bindEvents work', async () => { const onChange = jest.fn(), onFocus = jest.fn(), onBlur = jest.fn(), - onKeydown = jest.fn(); + onKeydown = jest.fn() const wrapper = mount({ components: { DInput }, template: ` @@ -42,88 +42,88 @@ describe('d-input', () => { @blur="onBlur" @keydown="onKeydown" /> `, - setup () { + setup() { return { onChange, onFocus, onBlur, onKeydown - }; + } } - }); - const input = wrapper.find('input'); + }) + const input = wrapper.find('input') - await input.trigger('change'); - expect(onChange).toBeCalledTimes(1); + await input.trigger('change') + expect(onChange).toBeCalledTimes(1) - await input.trigger('focus'); - expect(onFocus).toBeCalledTimes(1); + await input.trigger('focus') + expect(onFocus).toBeCalledTimes(1) - await input.trigger('blur'); - expect(onBlur).toBeCalledTimes(1); + await input.trigger('blur') + expect(onBlur).toBeCalledTimes(1) - await input.trigger('keydown'); - expect(onKeydown).toBeCalledTimes(1); - }); + await input.trigger('keydown') + expect(onKeydown).toBeCalledTimes(1) + }) it('d-input disabled work', async () => { const wrapper = mount(DInput, { props: { disabled: false } - }); - const input = wrapper.find('input'); - expect(input.attributes('disabled')).toBe(undefined); + }) + const input = wrapper.find('input') + expect(input.attributes('disabled')).toBe(undefined) await wrapper.setProps({ disabled: true - }); - expect(input.attributes('disabled')).toBe(''); - }); + }) + expect(input.attributes('disabled')).toBe('') + }) it('d-input error work', async () => { const wrapper = mount(DInput, { props: { error: false } - }); - const input = wrapper.find('input'); - expect(input.classes()).not.toContain('error'); + }) + const input = wrapper.find('input') + expect(input.classes()).not.toContain('error') await wrapper.setProps({ error: true - }); - expect(input.classes()).toContain('error'); - }); + }) + expect(input.classes()).toContain('error') + }) it('d-input size work', async () => { - const wrapper = mount(DInput); - const input = wrapper.find('input'); - expect(input.classes()).not.toContain('devui-input-sm'); - expect(input.classes()).not.toContain('devui-input-lg'); + const wrapper = mount(DInput) + const input = wrapper.find('input') + expect(input.classes()).not.toContain('devui-input-sm') + expect(input.classes()).not.toContain('devui-input-lg') await wrapper.setProps({ size: 'sm' - }); - expect(input.classes()).toContain('devui-input-sm'); - expect(input.classes()).not.toContain('devui-input-lg'); + }) + expect(input.classes()).toContain('devui-input-sm') + expect(input.classes()).not.toContain('devui-input-lg') await wrapper.setProps({ size: 'lg' - }); - expect(input.classes()).not.toContain('devui-input-sm'); - expect(input.classes()).toContain('devui-input-lg'); - }); + }) + expect(input.classes()).not.toContain('devui-input-sm') + expect(input.classes()).toContain('devui-input-lg') + }) it('d-input showPassword work', async () => { - const wrapper = mount(DInput); - const input = wrapper.find('input'); + const wrapper = mount(DInput) + const input = wrapper.find('input') - expect(input.attributes('type')).toBe('text'); + expect(input.attributes('type')).toBe('text') await wrapper.setProps({ showPassword: true - }); - expect(input.attributes('type')).toBe('password'); - }); -}); + }) + expect(input.attributes('type')).toBe('password') + }) +}) diff --git a/packages/devui-vue/devui/input/src/input.tsx b/packages/devui-vue/devui/input/src/input.tsx index 22822ede..50bcfa23 100644 --- a/packages/devui-vue/devui/input/src/input.tsx +++ b/packages/devui-vue/devui/input/src/input.tsx @@ -1,7 +1,7 @@ -import { defineComponent, computed, ref, watch, nextTick, onMounted, toRefs, inject } from 'vue'; -import { inputProps, InputType } from './use-input'; +import { defineComponent, computed, ref, watch, inject } from 'vue' +import { inputProps, InputType } from './use-input' import './input.scss' -import { dFormItemEvents, IFormItem, formItemInjectionKey } from '../../form/src/form-types'; +import { dFormItemEvents, IFormItem, formItemInjectionKey } from '../../form/src/form-types' export default defineComponent({ name: 'DInput', @@ -15,11 +15,11 @@ export default defineComponent({ } }, props: inputProps, - emits: ['update:value', 'focus', 'blur', 'change', 'keydown'], + emits: ['update:modelValue', 'focus', 'blur', 'change', 'keydown'], setup(props, ctx) { - const formItem = inject(formItemInjectionKey, {} as IFormItem); - const hasFormItem = Object.keys(formItem).length > 0; - const sizeCls = computed(() => `devui-input-${props.size}`); + const formItem = inject(formItemInjectionKey, {} as IFormItem) + const hasFormItem = Object.keys(formItem).length > 0 + const sizeCls = computed(() => `devui-input-${props.size}`) const showPwdIcon = ref(false) const inputType = ref('text') const inputCls = computed(() => { @@ -30,31 +30,40 @@ export default defineComponent({ } }) const showPreviewIcon = computed(() => inputType.value === 'password') - watch(() => props.showPassword, flg => { - inputType.value = flg ? 'password' : 'text' - }, { immediate: true }) + watch( + () => props.showPassword, + (flg) => { + inputType.value = flg ? 'password' : 'text' + }, + { immediate: true } + ) - watch(() => props.value, value => { - (value && value.length > 0 && showPreviewIcon.value) ? showPwdIcon.value = true : showPwdIcon.value = false - }) + watch( + () => props.modelValue, + (value) => { + value && value.length > 0 && showPreviewIcon.value + ? (showPwdIcon.value = true) + : (showPwdIcon.value = false) + } + ) const onInput = ($event: Event) => { - ctx.emit('update:value', ($event.target as HTMLInputElement).value); - hasFormItem && formItem.formItemMitt.emit(dFormItemEvents.input); - }, + ctx.emit('update:modelValue', ($event.target as HTMLInputElement).value) + hasFormItem && formItem.formItemMitt.emit(dFormItemEvents.input) + }, onFocus = () => { - ctx.emit('focus'); + ctx.emit('focus') }, onBlur = () => { - ctx.emit('blur'); - hasFormItem && formItem.formItemMitt.emit(dFormItemEvents.blur); + ctx.emit('blur') + hasFormItem && formItem.formItemMitt.emit(dFormItemEvents.blur) }, onChange = ($event: Event) => { - ctx.emit('change', ($event.target as HTMLInputElement).value); - hasFormItem && formItem.formItemMitt.emit(dFormItemEvents.change); + ctx.emit('change', ($event.target as HTMLInputElement).value) + hasFormItem && formItem.formItemMitt.emit(dFormItemEvents.change) }, onKeydown = ($event: KeyboardEvent) => { - ctx.emit('keydown', $event); + ctx.emit('keydown', $event) }, onChangeInputType = () => { inputType.value = inputType.value === 'password' ? 'text' : 'password' @@ -70,11 +79,11 @@ export default defineComponent({ onChange, onKeydown, onChangeInputType - }; + } }, - render () { + render() { const { - value, + modelValue, showPreviewIcon, showPwdIcon, inputCls, @@ -88,14 +97,14 @@ export default defineComponent({ onBlur, onChange, onKeydown, - onChangeInputType, - } = this; + onChangeInputType + } = this return ( -
+
- { - showPwdIcon &&
- { showPreviewIcon - ? - : - } -
} + {showPwdIcon && ( +
+ {showPreviewIcon ? ( + + ) : ( + + )} +
+ )}
- ); + ) } -}); +}) diff --git a/packages/devui-vue/devui/input/src/use-input.tsx b/packages/devui-vue/devui/input/src/use-input.tsx index 5706ad27..8a46ec40 100644 --- a/packages/devui-vue/devui/input/src/use-input.tsx +++ b/packages/devui-vue/devui/input/src/use-input.tsx @@ -1,4 +1,4 @@ -import { PropType } from 'vue'; +import { PropType } from 'vue' export const inputProps = { placeholder: { @@ -33,31 +33,31 @@ export const inputProps = { type: Boolean, default: false }, - value: { + modelValue: { type: String, default: '' }, - 'onUpdate:value': { + 'update:modelValue': { type: Function as PropType<(v: string) => void>, default: undefined }, - 'onChange': { + onChange: { type: Function as PropType<(v: string) => void>, default: undefined }, - 'onKeydown': { + onKeydown: { type: Function as PropType<(v: KeyboardEvent) => void>, default: undefined }, - 'onFocus': { + onFocus: { type: Function as PropType<() => void>, default: undefined }, - 'onBlur': { + onBlur: { type: Function as PropType<() => void>, default: undefined } -} as const; +} as const export type PreviewIconType = 'preview' | 'icon-preview-forbidden' export type InputType = 'password' | 'text' diff --git a/packages/devui-vue/docs/components/input/index.md b/packages/devui-vue/docs/components/input/index.md index f6fb8aa0..2bb81595 100644 --- a/packages/devui-vue/docs/components/input/index.md +++ b/packages/devui-vue/docs/components/input/index.md @@ -14,20 +14,20 @@ ``` @@ -39,30 +39,29 @@ ```vue ``` ::: - ### 密码框 :::demo ```vue ``` @@ -82,15 +81,15 @@ export default defineComponent({ ### API -| 参数 | 类型 | 默认 | 说明 | 跳转 Demo | -| :---------: | :------: | :-------: | :-----------------------: | :---------------------------------: | -| id | `string` | -- | 可选,文本框 id | [基本用法](#基本用法) | -| placeholder | `string` | -- | 可选,文本框 placeholder | [基本用法](#基本用法) | -| maxLength | `number` | Number.MAX_SAFE_INTEGER | 可选,输入框的 max-length | | -| disabled | `boolean` | false | 可选,文本框是否被禁用 | [基本用法](#基本用法) | -| error | `boolean` | false | 可选,文本框是否出现输入错误 | [基本用法](#基本用法) | -| size | `'sm'\|''\|'lg'` | '' | 可选,文本框尺寸,有三种选择`'lg'`,`''`,`'sm'` | [尺寸](#尺寸) | -| cssClass | `string` | '' | 可选,支持传入类名到输入框上 | | -| showPassword | `boolean` | false | 可选,密码输入框 | [密码框](#密码框) | -| autoFocus | `boolean` | false | 可选,输入框是否自动对焦 | [基本用法](#基本用法) | - +| 参数 | 类型 | 默认 | 说明 | 跳转 Demo | +| :-------------------: | :----------------: | :---------------------: | :--------------------------------------------: | :-------------------: | +| id | `string` | -- | 可选,文本框 id | [基本用法](#基本用法) | +| modelValue \/ v-model | `string \| number` | '' | 绑定值 | [密码框](#密码框) | +| placeholder | `string` | -- | 可选,文本框 placeholder | [基本用法](#基本用法) | +| maxLength | `number` | Number.MAX_SAFE_INTEGER | 可选,输入框的 max-length | | +| disabled | `boolean` | false | 可选,文本框是否被禁用 | [基本用法](#基本用法) | +| error | `boolean` | false | 可选,文本框是否出现输入错误 | [基本用法](#基本用法) | +| size | `'sm'\|''\|'lg'` | '' | 可选,文本框尺寸,有三种选择`'lg'`,`''`,`'sm'` | [尺寸](#尺寸) | +| cssClass | `string` | '' | 可选,支持传入类名到输入框上 | | +| showPassword | `boolean` | false | 可选,密码输入框 | [密码框](#密码框) | +| autoFocus | `boolean` | false | 可选,输入框是否自动对焦 | [基本用法](#基本用法) | diff --git a/packages/devui-vue/package.json b/packages/devui-vue/package.json index 6d4a5953..4a44e5b5 100644 --- a/packages/devui-vue/package.json +++ b/packages/devui-vue/package.json @@ -40,7 +40,8 @@ "clean:cli": "npm uninstall -g devui-cli & npm uninstall -g vue-devui", "cli:create": "node ./devui-cli/index.js create -t component", "predev": "node ./devui-cli/index.js create -t vue-devui --ignore-parse-error", - "prebuild": "node ./devui-cli/index.js create -t vue-devui --ignore-parse-error" + "prebuild": "node ./devui-cli/index.js create -t vue-devui --ignore-parse-error", + "prepare": "cd ../.. && husky install packages/devui-vue/.husky" }, "dependencies": { "@devui-design/icons": "^1.3.0", @@ -79,7 +80,7 @@ "eslint": "^7.28.0", "eslint-plugin-import": "^2.24.2", "eslint-plugin-vue": "^7.11.1", - "husky": "^4.3.7", + "husky": "^7.0.4", "inquirer": "^8.1.2", "jest": "^27.0.4", "lint-staged": "^11.0.0", @@ -100,12 +101,6 @@ "vue-tsc": "^0.2.2", "yarn": "^1.22.11" }, - "husky": { - "hooks": { - "pre-commit": "ls-lint && lint-staged", - "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" - } - }, "lint-staged": { "{src,devui}/**/*.{js,ts,jsx,tsx,vue}": "eslint --fix", "{src,devui}/**/*.{scss,css}": "stylelint --fix" -- Gitee