diff --git a/devui/input-number/index.ts b/devui/input-number/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e429b5437e8819d98611f82f4e8048a9c2cb4846 --- /dev/null +++ b/devui/input-number/index.ts @@ -0,0 +1,15 @@ +import type { App } from 'vue' +import InputNumber from './src/input-number' + +InputNumber.install = function(app: App) { + app.component(InputNumber.name, InputNumber) +} +export { InputNumber } + +export default { + title: 'InputNumber 数字输入框', + category: '导航', + install(app: App):void { + app.use(InputNumber as any) + } +} diff --git a/devui/input-number/src/input-number-types.ts b/devui/input-number/src/input-number-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..9dc1b0022333e2a33f10b97b62fefd4653e1df12 --- /dev/null +++ b/devui/input-number/src/input-number-types.ts @@ -0,0 +1,54 @@ +import type { PropType, ExtractPropTypes } from 'vue' + +export const inputNumberProps = { + placeholder: { + type: String, + default: undefined + }, + disabled: { + type: Boolean, + default: false + }, + step:{ + type: Number, + default: 0 + }, + max: { + type: Number, + default: Infinity + }, + min: { + type: Number, + default: -Infinity + }, + size: { + type: String, + default: '' + }, + modelValue: { + type: Number, + default: 0 + }, + 'onUpdate:modelValue': { + type: Function as PropType<(v: string) => void>, + default: undefined + }, + 'onChange': { + type: Function as PropType<(v: string) => void>, + default: undefined + }, + 'onKeydown': { + type: Function as PropType<(v: KeyboardEvent) => void>, + default: undefined + }, + 'onFocus': { + type: Function as PropType<() => void>, + default: undefined + }, + 'onBlur': { + type: Function as PropType<() => void>, + default: undefined + } +} as const + +export type InputNumberProps = ExtractPropTypes diff --git a/devui/input-number/src/input-number.scss b/devui/input-number/src/input-number.scss new file mode 100644 index 0000000000000000000000000000000000000000..ce20f121a9dd5d022e9d5d9cbb28a1c93ea89e3e --- /dev/null +++ b/devui/input-number/src/input-number.scss @@ -0,0 +1,119 @@ +@import '../../style/mixins/index'; +@import '../../style/theme/color'; +@import '../../style/theme/font'; +@import '../../style/theme/shadow'; +@import '../../style/theme/corner'; + +.devui-input-number { + position: relative; + display: block; + width: 180px; + line-height: 38px; + + &.devui-input-disabled { + .devui-subtract, + .devui-add, + .devui-input-style { + cursor: not-allowed; + border-color: #e4e7ed; + color: #e4e7ed; + } + + .devui-input-style { + background-color: #f5f7fa; + color: #c0c4cc; + } + } + + &.devui-input-number-sm { + .devui-subtract, + .devui-add { + width: 40px; + height: 34px; + line-height: 34px; + } + + .devui-input-style { + width: 70px; + height: 34px; + line-height: 34px; + } + } + + &.devui-input-number-lg { + .devui-subtract, + .devui-add { + width: 60px; + } + + .devui-input-style { + width: 90px; + } + } + + .devui-input-item { + display: flex; + display: -webkit-box; + width: 200px; + line-height: 38px; + } + + .devui-input-style::-webkit-outer-spin-button, + .devui-input-style::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + + .devui-add, + .devui-subtract { + display: inline-block; + width: 50px; + height: 38px; + line-height: 38px; + text-align: center; + font-size: 16px; + color: #333333; + background: #f5f7fa; + cursor: pointer; + border: 1px solid #dcdfe6; + } + + .devui-add { + float: right; + margin-left: -1px; + border-radius: 0 4px 4px 0; + } + + .devui-subtract { + float: left; + margin-right: -1px; + border-radius: 4px 0 0 4px; + } + + .devui-input-style { + display: block; + -moz-appearance: textfield; + } + + .devui-input-style { + flex-grow: 1; + outline: none; + background-color: #ffffff; + border: 1px solid #dcdfe6; + border-radius: 2px; + padding: 5px 10px; + line-height: 38px; + font-size: 12px; + color: #252b3a; + width: 80px; + display: block; + cursor: text; + height: 38px; + text-align: center; + transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + } + + .disabled { + cursor: not-allowed; + } +} diff --git a/devui/input-number/src/input-number.tsx b/devui/input-number/src/input-number.tsx new file mode 100644 index 0000000000000000000000000000000000000000..01ec9a86ddac5f1e1dc46e6cb8841717ed6405a9 --- /dev/null +++ b/devui/input-number/src/input-number.tsx @@ -0,0 +1,107 @@ +import { defineComponent, ref, computed } from 'vue'; +import { inputNumberProps, InputNumberProps } from './input-number-types'; +import './input-number.scss'; + +export default defineComponent({ + name: 'DInputNumber', + props: inputNumberProps, + emits: ['update:modelValue', 'change', 'input', 'focus', 'blur', 'keydown'], + setup(props: InputNumberProps, ctx) { + const inputVal = ref(props.modelValue); + + // 大小 + const isSize = computed(() => { + return `devui-input-number-${props.size}`; + }); + + // 判断是否禁用 + const isDisabled = computed(() => { + return props.disabled ? 'devui-input-disabled' : ''; + }); + + //新增 + const add = () => { + if (props.disabled) return; + if (inputVal.value >= props.max) return; + inputVal.value += props.step != 0 ? props.step : 1; + ctx.emit('change', inputVal.value); + ctx.emit('update:modelValue', inputVal.value); + }; + // 减少 + const subtract = () => { + if (props.disabled) return; + if (inputVal.value <= props.min) return; + inputVal.value -= props.step != 0 ? props.step : 1; + ctx.emit('change', inputVal.value); + ctx.emit('update:modelValue', inputVal.value); + }; + const onInput = (val) => { + inputVal.value = parseInt(val.data); + ctx.emit('input', val.data); + ctx.emit('update:modelValue', val.data); + }; + const onFocus = ($event: Event) => { + ctx.emit('focus', $event); + }; + const onBlur = ($event: Event) => { + ctx.emit('blur', $event); + }; + const onChange = ($event: Event) => { + ctx.emit('change', ($event.target as HTMLInputElement).value); + }; + const onKeydown = ($event: KeyboardEvent) => { + ctx.emit('keydown', $event); + }; + return { + inputVal, + isDisabled, + isSize, + add, + subtract, + onInput, + onChange, + onKeydown, + onBlur, + onFocus, + }; + }, + render() { + const { + placeholder, + add, + inputVal, + isDisabled, + isSize, + subtract, + onInput, + onChange, + onKeydown, + onBlur, + onFocus, + } = this; + const dInputNum = ['devui-input-number', isDisabled, isSize]; + return ( +
+
+ + - + + + + + + +
+
+ ); + }, +}); diff --git a/docs/components/input-number/index.md b/docs/components/input-number/index.md new file mode 100644 index 0000000000000000000000000000000000000000..60b0f36679f64cb49445f800b22a402eaa863cfe --- /dev/null +++ b/docs/components/input-number/index.md @@ -0,0 +1,134 @@ +# InputNum 数字输入框 + +数字输入框仅允许输入标准的数字值,可定义范围。 + + +### 基本用法 + +:::demo 使用它,只需要在d-input-number元素中使用v-model绑定变量即可,变量的初始值即为默认值。 + +```vue + + +``` +::: + +### 禁用状态 + +:::demo disabled属性接受一个Boolean,设置为true即可禁用整个组件,如果你只需要控制数值在某一范围内,可以设置min属性和max属性,不设置min和max时,最小值为 0。 + +```vue + + +``` +::: + +### 步数 + +:::demo 设置step属性可以控制步长,接受一个Number。 + +```vue + + +``` +::: + +### 尺寸 + +:::demo 额外提供了 lg、normal、sm 三种尺寸的数字输入框。 + +```vue + + +``` +::: +### API + +| 参数 | 类型 | 默认 | 说明 | 跳转 Demo | +| :---------: | :------: | :-------: | :-----------------------: | :---------------------------------: | +| step | `number` | 0 | 可选,步数 | [步数](#步数) | +| placeholder | `string` | -- | 可选,文本框 placeholder | [基本用法](#基本用法) | +| max | `number` | -- | 可选,输入框的最大值max | [基本用法](#基本用法) | +| min | `number` | -- | 可选,输入框的最小值min | [基本用法](#基本用法) | +| disabled | `boolean` | false | 可选,文本框是否被禁用 | [禁用状态](#禁用状态) | +| value | `number` | 0 | 可选,文本框默认值 | [基本用法](#基本用法) | +| size | `'lg'\|'normal'\|'sm'` | '' | 可选,文本框尺寸,有三种选择`'lg'`,`'narmal'`,`'sm'` | [尺寸](#尺寸) | + +### Events + +| 事件名称 | 说明 | 回调参数 | +| :---------: | :------: | :-------: | +| change | 绑定值被改变时触发 | (currentValue) | +| blur | 在组件 Input 失去焦点时触发 | (event: Event) | +| focus | 在组件 Input 获得焦点时触发 | (event: Event) | +| input | 在组件 Input 获得输入时触发 | (currentValue) |