更新:登录功能
This commit is contained in:
340
uni_modules/wot-design-uni/components/wd-textarea/index.scss
Normal file
340
uni_modules/wot-design-uni/components/wd-textarea/index.scss
Normal file
@@ -0,0 +1,340 @@
|
||||
@import "../common/abstracts/variable";
|
||||
@import "../common/abstracts/mixin";
|
||||
|
||||
.wot-theme-dark {
|
||||
@include b(textarea) {
|
||||
background: $-dark-background2;
|
||||
|
||||
&::after {
|
||||
background: $-dark-color-gray;
|
||||
}
|
||||
|
||||
@include when(not-empty) {
|
||||
&:not(.is-disabled) {
|
||||
&::after {
|
||||
background-color: $-dark-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include e(value) {
|
||||
background: $-dark-background2;
|
||||
}
|
||||
|
||||
@include e(inner) {
|
||||
color: $-dark-color;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: $-dark-color3;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(count) {
|
||||
color: $-dark-color3;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@include e(count-current) {
|
||||
color: $-dark-color;
|
||||
}
|
||||
|
||||
:deep(.wd-textarea__icon),
|
||||
:deep(.wd-textarea__clear) {
|
||||
color: $-dark-color;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@include when(cell) {
|
||||
background-color: $-dark-background2;
|
||||
|
||||
@include when(border) {
|
||||
@include halfPixelBorder("top", $-textarea-cell-padding, $-dark-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
@include when(disabled) {
|
||||
|
||||
.wd-textarea__inner {
|
||||
color: $-dark-color-gray;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@include e(label) {
|
||||
color: $-dark-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include b(textarea) {
|
||||
position: relative;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
text-align: left;
|
||||
background: $-textarea-bg;
|
||||
padding: $-textarea-cell-padding $-textarea-padding;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
display: none;
|
||||
content: "";
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: $-textarea-border-color;
|
||||
transform: scaleY(0.5);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@include e(label) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: $-input-cell-label-width;
|
||||
color: $-cell-title-color;
|
||||
margin-right: $-cell-padding;
|
||||
box-sizing: border-box;
|
||||
font-size: $-textarea-fs;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@include e(label-inner) {
|
||||
display: inline-block;
|
||||
line-height: $-cell-line-height;
|
||||
font-size: $-textarea-fs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@include e(required) {
|
||||
font-size: $-cell-required-size;
|
||||
color: $-cell-required-color;
|
||||
margin-left: $-cell-required-margin;
|
||||
|
||||
@include m(left) {
|
||||
margin-left: 0;
|
||||
margin-right: $-cell-required-margin;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(prefix) {
|
||||
margin-right: $-textarea-icon-margin;
|
||||
font-size: $-textarea-fs;
|
||||
line-height: initial;
|
||||
|
||||
:deep(.wd-textarea__icon) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(suffix) {
|
||||
flex-shrink: 0;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
@include e(value) {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
font-size: 0;
|
||||
background: $-textarea-bg;
|
||||
box-sizing: border-box;
|
||||
|
||||
@include when(show-limit) {
|
||||
padding-bottom: 36px;
|
||||
}
|
||||
|
||||
@include when(suffix) {
|
||||
padding-right: calc($-textarea-icon-size + 8px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@include e(inner) {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
font-size: $-textarea-fs;
|
||||
line-height: $-cell-line-height;
|
||||
color: $-textarea-color;
|
||||
outline: none;
|
||||
background: none;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
word-break: break-word;
|
||||
min-height: 24px;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: $-input-placeholder-color;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(suffix) {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
@include edeep(icon) {
|
||||
margin-left: $-textarea-icon-margin;
|
||||
font-size: $-textarea-icon-size;
|
||||
color: $-textarea-icon-color;
|
||||
background: $-textarea-bg;
|
||||
}
|
||||
|
||||
@include edeep(clear) {
|
||||
margin-left: $-textarea-icon-margin;
|
||||
font-size: $-textarea-icon-size;
|
||||
color: $-textarea-clear-color;
|
||||
vertical-align: middle;
|
||||
background: $-textarea-bg;
|
||||
line-height: $-cell-line-height;
|
||||
}
|
||||
|
||||
|
||||
@include e(count) {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 0;
|
||||
font-size: $-textarea-count-fs;
|
||||
color: $-textarea-count-color;
|
||||
background: $-textarea-bg;
|
||||
line-height: 20px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
@include e(count-current) {
|
||||
color: $-textarea-count-current-color;
|
||||
|
||||
@include when(error) {
|
||||
color: $-input-error-color;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(readonly-mask) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@include e(error-message) {
|
||||
color: $-form-item-error-message-color;
|
||||
font-size: $-form-item-error-message-font-size;
|
||||
line-height: $-form-item-error-message-line-height;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@include when(not-empty) {
|
||||
&:not(.is-disabled) {
|
||||
&::after {
|
||||
background-color: $-textarea-not-empty-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include when(disabled) {
|
||||
|
||||
.wd-textarea__inner {
|
||||
color: $-input-disabled-color;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(error) {
|
||||
|
||||
.wd-textarea__inner {
|
||||
color: $-input-error-color;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(auto-height) {
|
||||
&:not(.is-cell) {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(no-border) {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(cell) {
|
||||
display: flex;
|
||||
line-height: $-cell-line-height;
|
||||
|
||||
&.is-error::after {
|
||||
background: $-textarea-cell-border-color;
|
||||
}
|
||||
|
||||
.wd-textarea__value {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
:deep(.wd-textarea__icon) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: $-textarea-cell-height;
|
||||
line-height: $-textarea-cell-height;
|
||||
}
|
||||
|
||||
.wd-textarea__prefix {
|
||||
display: inline-block;
|
||||
margin-right: $-cell-icon-right;
|
||||
}
|
||||
|
||||
&.wd-textarea::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wd-textarea__suffix {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@include when(center) {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@include when(border) {
|
||||
@include halfPixelBorder("top", $-textarea-cell-padding);
|
||||
}
|
||||
}
|
||||
|
||||
@include when(large) {
|
||||
padding: $-textarea-cell-padding-large;
|
||||
|
||||
.wd-textarea__prefix {
|
||||
font-size: $-textarea-fs-large;
|
||||
}
|
||||
|
||||
.wd-textarea__label-inner {
|
||||
font-size: $-textarea-fs-large;
|
||||
}
|
||||
|
||||
.wd-textarea__inner {
|
||||
font-size: $-textarea-fs-large;
|
||||
}
|
||||
|
||||
.wd-textarea__count {
|
||||
font-size: $-textarea-count-fs-large;
|
||||
}
|
||||
|
||||
:deep(.wd-textarea__icon),
|
||||
:deep(.wd-textarea__clear) {
|
||||
font-size: $-textarea-icon-size-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
@import "../common/abstracts/variable";
|
||||
@import "../common/abstracts/mixin";
|
||||
|
||||
.wot-theme-dark {
|
||||
@include b(textarea) {
|
||||
@include e(placeholder) {
|
||||
color: $-dark-color3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include b(textarea) {
|
||||
@include e(placeholder) {
|
||||
color: $-input-placeholder-color;
|
||||
|
||||
&.is-error {
|
||||
color: $-input-error-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
288
uni_modules/wot-design-uni/components/wd-textarea/types.ts
Normal file
288
uni_modules/wot-design-uni/components/wd-textarea/types.ts
Normal file
@@ -0,0 +1,288 @@
|
||||
import type { ExtractPropTypes, PropType } from 'vue'
|
||||
import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeNumericProp, makeStringProp } from '../common/props'
|
||||
import type { FormItemRule } from '../wd-form/types'
|
||||
import type { InputClearTrigger, InputMode } from '../wd-input/types'
|
||||
|
||||
export type ConfirmType = 'send' | 'search' | 'next' | 'go' | 'done'
|
||||
|
||||
export const textareaProps = {
|
||||
...baseProps,
|
||||
/**
|
||||
* * 自定义文本域容器class名称。
|
||||
* 类型:string
|
||||
*/
|
||||
customTextareaContainerClass: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* * 自定义文本域class名称。
|
||||
* 类型:string
|
||||
*/
|
||||
customTextareaClass: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* * 自定义标签class名称。
|
||||
* 类型:string
|
||||
*/
|
||||
customLabelClass: makeStringProp(''),
|
||||
// 原生属性
|
||||
/**
|
||||
* * 绑定值。
|
||||
* 类型:string | number
|
||||
*/
|
||||
modelValue: makeNumericProp(''),
|
||||
|
||||
/**
|
||||
* * 占位文本。
|
||||
* 类型:string
|
||||
* 默认值:'请输入...'
|
||||
*/
|
||||
placeholder: String,
|
||||
|
||||
/**
|
||||
* 指定placeholder的样式。
|
||||
* 类型:string
|
||||
*/
|
||||
placeholderStyle: String,
|
||||
|
||||
/**
|
||||
* * 指定placeholder的样式类。
|
||||
* 类型:string
|
||||
* 默认值:空字符串
|
||||
*/
|
||||
placeholderClass: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* * 禁用输入框。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
disabled: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 最大输入长度,设置为-1表示不限制最大长度。
|
||||
* 类型:number
|
||||
* 默认值:-1
|
||||
*/
|
||||
maxlength: makeNumberProp(-1),
|
||||
|
||||
/**
|
||||
* * 自动聚焦并拉起键盘。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
autoFocus: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 获取焦点。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
focus: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 是否自动增高输入框高度,style.height属性在auto-height生效时不生效。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
autoHeight: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 如果textarea处于position:fixed区域,需要设置此属性为true。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
fixed: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 指定光标与键盘的距离,取textarea距离底部的距离和cursor-spacing指定的距离的最小值作为实际距离。
|
||||
* 类型:number
|
||||
* 默认值:0
|
||||
*/
|
||||
cursorSpacing: makeNumberProp(0),
|
||||
|
||||
/**
|
||||
* * 指定focus时的光标位置。
|
||||
* 类型:number
|
||||
* 默认值:-1
|
||||
*/
|
||||
cursor: makeNumberProp(-1),
|
||||
|
||||
/**
|
||||
* * 设置键盘右下角按钮的文字。
|
||||
* 类型:string
|
||||
* 默认值:'done'
|
||||
* 可选值有'done', 'go', 'next', 'search', 'send'
|
||||
*/
|
||||
confirmType: String as PropType<ConfirmType>,
|
||||
|
||||
/**
|
||||
* * 点击键盘右下角按钮时是否保持键盘不收起。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
confirmHold: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 是否显示键盘上方带有“完成”按钮那一栏。
|
||||
* 类型:boolean
|
||||
* 默认值:true
|
||||
*/
|
||||
showConfirmBar: makeBooleanProp(true),
|
||||
|
||||
/**
|
||||
* * 光标起始位置,自动聚集时有效,需与selection-end搭配使用。
|
||||
* 类型:number
|
||||
* 默认值:-1
|
||||
*/
|
||||
selectionStart: makeNumberProp(-1),
|
||||
|
||||
/**
|
||||
* * 光标结束位置,自动聚集时有效,需与selection-start搭配使用。
|
||||
* 类型:number
|
||||
* 默认值:-1
|
||||
*/
|
||||
selectionEnd: makeNumberProp(-1),
|
||||
|
||||
/**
|
||||
* * 键盘弹起时是否自动上推页面。
|
||||
* 类型:boolean
|
||||
* 默认值:true
|
||||
*/
|
||||
adjustPosition: makeBooleanProp(true),
|
||||
|
||||
/**
|
||||
* * 是否去掉iOS下的默认内边距。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
disableDefaultPadding: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * focus状态下点击页面时是否不收起键盘。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
holdKeyboard: makeBooleanProp(false),
|
||||
|
||||
// 非原生属性
|
||||
/**
|
||||
* * 显示为密码框。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
showPassword: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 是否显示清空按钮。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
clearable: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 输入框只读状态。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
readonly: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 前置图标,icon组件中的图标类名。
|
||||
* 类型:string
|
||||
*/
|
||||
prefixIcon: String,
|
||||
/**
|
||||
* * 是否显示字数限制,需要同时设置maxlength。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
showWordLimit: makeBooleanProp(false),
|
||||
/**
|
||||
* 设置左侧标题。
|
||||
* 类型:string
|
||||
*/
|
||||
label: String,
|
||||
|
||||
/**
|
||||
* 设置左侧标题宽度。
|
||||
* 类型:string
|
||||
*/
|
||||
labelWidth: makeStringProp(''),
|
||||
/**
|
||||
* * 设置输入框大小。
|
||||
* 类型:string
|
||||
*/
|
||||
size: String,
|
||||
|
||||
/**
|
||||
* * 设置输入框错误状态(红色)。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
error: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 当存在label属性时,设置标题和输入框垂直居中,默认为顶部居中。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
center: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 非cell类型下是否隐藏下划线。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
noBorder: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * cell类型下必填样式。
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
required: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* * 表单域model字段名,在使用表单校验功能的情况下,该属性是必填的。
|
||||
* 类型:string
|
||||
*/
|
||||
prop: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* * 表单验证规则。
|
||||
* 类型:FormItemRule[]
|
||||
* 默认值:[]
|
||||
*/
|
||||
rules: makeArrayProp<FormItemRule>(),
|
||||
/**
|
||||
* 显示清除图标的时机,always 表示输入框不为空时展示,focus 表示输入框聚焦且不为空时展示
|
||||
* 类型: "focus" | "always"
|
||||
* 默认值: "always"
|
||||
*/
|
||||
clearTrigger: makeStringProp<InputClearTrigger>('always'),
|
||||
/**
|
||||
* 是否在点击清除按钮时聚焦输入框
|
||||
* 类型: boolean
|
||||
* 默认值: true
|
||||
*/
|
||||
focusWhenClear: makeBooleanProp(true),
|
||||
/**
|
||||
* 是否忽略组件内对文本合成系统事件的处理。为 false 时将触发 compositionstart、compositionend、compositionupdate 事件,且在文本合成期间会触发 input 事件
|
||||
* 类型: boolean
|
||||
* 默认值: true
|
||||
*/
|
||||
ignoreCompositionEvent: makeBooleanProp(true),
|
||||
/**
|
||||
* 它提供了用户在编辑元素或其内容时可能输入的数据类型的提示。在符合条件的高版本webview里,uni-app的web和app-vue平台中可使用本属性。
|
||||
* 类型: InputMode
|
||||
* 可选值: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | "password"
|
||||
* 默认值: "text"
|
||||
*/
|
||||
inputmode: makeStringProp<InputMode>('text'),
|
||||
/**
|
||||
* 必填标记位置,可选值:before(标签前)、after(标签后)
|
||||
*/
|
||||
markerSide: makeStringProp<'before' | 'after'>('before')
|
||||
}
|
||||
|
||||
export type TextareaProps = ExtractPropTypes<typeof textareaProps>
|
||||
@@ -0,0 +1,296 @@
|
||||
<template>
|
||||
<view :class="rootClass" :style="customStyle">
|
||||
<view v-if="label || $slots.label" class="wd-textarea__label" :style="labelStyle">
|
||||
<text v-if="isRequired && markerSide === 'before'" class="wd-textarea__required wd-textarea__required--left">*</text>
|
||||
<view v-if="prefixIcon || $slots.prefix" class="wd-textarea__prefix">
|
||||
<wd-icon v-if="prefixIcon && !$slots.prefix" custom-class="wd-textarea__icon" :name="prefixIcon" @click="onClickPrefixIcon" />
|
||||
<slot v-else name="prefix"></slot>
|
||||
</view>
|
||||
<view class="wd-textarea__label-inner">
|
||||
<text v-if="label && !$slots.label">{{ label }}</text>
|
||||
<slot v-else-if="$slots.label" name="label"></slot>
|
||||
</view>
|
||||
<text v-if="isRequired && markerSide === 'after'" class="wd-textarea__required">*</text>
|
||||
</view>
|
||||
|
||||
<!-- 文本域 -->
|
||||
<view :class="`wd-textarea__value ${showClear ? 'is-suffix' : ''} ${customTextareaContainerClass} ${showWordCount ? 'is-show-limit' : ''}`">
|
||||
<textarea
|
||||
:class="`wd-textarea__inner ${customTextareaClass}`"
|
||||
v-model="inputValue"
|
||||
:show-count="false"
|
||||
:placeholder="placeholderValue"
|
||||
:disabled="disabled || readonly"
|
||||
:maxlength="maxlength"
|
||||
:focus="focused"
|
||||
:auto-focus="autoFocus"
|
||||
:placeholder-style="placeholderStyle"
|
||||
:placeholder-class="inputPlaceholderClass"
|
||||
:auto-height="autoHeight"
|
||||
:cursor-spacing="cursorSpacing"
|
||||
:fixed="fixed"
|
||||
:cursor="cursor"
|
||||
:show-confirm-bar="showConfirmBar"
|
||||
:selection-start="selectionStart"
|
||||
:selection-end="selectionEnd"
|
||||
:adjust-position="adjustPosition"
|
||||
:hold-keyboard="holdKeyboard"
|
||||
:confirm-type="confirmType"
|
||||
:confirm-hold="confirmHold"
|
||||
:disable-default-padding="disableDefaultPadding"
|
||||
:ignoreCompositionEvent="ignoreCompositionEvent"
|
||||
:inputmode="inputmode"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@confirm="handleConfirm"
|
||||
@linechange="handleLineChange"
|
||||
@keyboardheightchange="handleKeyboardheightchange"
|
||||
/>
|
||||
<view v-if="errorMessage" class="wd-textarea__error-message">{{ errorMessage }}</view>
|
||||
|
||||
<view v-if="props.readonly" class="wd-textarea__readonly-mask" />
|
||||
<view class="wd-textarea__suffix">
|
||||
<wd-icon v-if="showClear" custom-class="wd-textarea__clear" name="error-fill" @click="handleClear" />
|
||||
<view v-if="showWordCount" class="wd-textarea__count">
|
||||
<text :class="countClass">
|
||||
{{ currentLength }}
|
||||
</text>
|
||||
/{{ maxlength }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'wd-textarea',
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onBeforeMount, ref, watch, useSlots, type Slots } from 'vue'
|
||||
import wdIcon from '../wd-icon/wd-icon.vue'
|
||||
import { objToStyle, isDef, pause } from '../common/util'
|
||||
import { useCell } from '../composables/useCell'
|
||||
import { FORM_KEY, type FormItemRule } from '../wd-form/types'
|
||||
import { useParent } from '../composables/useParent'
|
||||
import { useTranslate } from '../composables/useTranslate'
|
||||
import { textareaProps } from './types'
|
||||
|
||||
interface TextareaSlots extends Slots {
|
||||
prefix?: () => any
|
||||
label?: () => any
|
||||
}
|
||||
|
||||
const { translate } = useTranslate('textarea')
|
||||
|
||||
const props = defineProps(textareaProps)
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'clear',
|
||||
'blur',
|
||||
'focus',
|
||||
'input',
|
||||
'keyboardheightchange',
|
||||
'confirm',
|
||||
'linechange',
|
||||
'clickprefixicon',
|
||||
'click'
|
||||
])
|
||||
const slots = useSlots() as TextareaSlots
|
||||
|
||||
const placeholderValue = computed(() => {
|
||||
return isDef(props.placeholder) ? props.placeholder : translate('placeholder')
|
||||
})
|
||||
|
||||
const clearing = ref<boolean>(false)
|
||||
const focused = ref<boolean>(false) // 控制聚焦
|
||||
const focusing = ref<boolean>(false) // 当前是否激活状态
|
||||
const inputValue = ref<string>('') // 输入框的值
|
||||
const cell = useCell()
|
||||
|
||||
watch(
|
||||
() => props.focus,
|
||||
(newValue) => {
|
||||
focused.value = newValue
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
inputValue.value = isDef(newValue) ? String(newValue) : ''
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
const { parent: form } = useParent(FORM_KEY)
|
||||
|
||||
/**
|
||||
* 展示清空按钮
|
||||
*/
|
||||
const showClear = computed(() => {
|
||||
const { disabled, readonly, clearable, clearTrigger } = props
|
||||
if (clearable && !readonly && !disabled && inputValue.value && (clearTrigger === 'always' || (props.clearTrigger === 'focus' && focusing.value))) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 展示字数统计
|
||||
*/
|
||||
const showWordCount = computed(() => {
|
||||
const { disabled, readonly, maxlength, showWordLimit } = props
|
||||
return Boolean(!disabled && !readonly && isDef(maxlength) && maxlength > -1 && showWordLimit)
|
||||
})
|
||||
|
||||
// 表单校验错误信息
|
||||
const errorMessage = computed(() => {
|
||||
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||
return form.errorMessages[props.prop]
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
// 是否展示必填
|
||||
const isRequired = computed(() => {
|
||||
let formRequired = false
|
||||
if (form && form.props.rules) {
|
||||
const rules = form.props.rules
|
||||
for (const key in rules) {
|
||||
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||
}
|
||||
}
|
||||
}
|
||||
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||
})
|
||||
|
||||
// 当前文本域文字长度
|
||||
const currentLength = computed(() => {
|
||||
/**
|
||||
* 使用Array.from处理多码元字符以获取正确的长度
|
||||
* @link https://github.com/Moonofweisheng/wot-design-uni/issues/933
|
||||
*/
|
||||
return Array.from(String(formatValue(props.modelValue))).length
|
||||
})
|
||||
|
||||
const rootClass = computed(() => {
|
||||
return `wd-textarea ${props.label || slots.label ? 'is-cell' : ''} ${props.center ? 'is-center' : ''} ${cell.border.value ? 'is-border' : ''} ${
|
||||
props.size ? 'is-' + props.size : ''
|
||||
} ${props.error ? 'is-error' : ''} ${props.disabled ? 'is-disabled' : ''} ${props.autoHeight ? 'is-auto-height' : ''} ${
|
||||
currentLength.value > 0 ? 'is-not-empty' : ''
|
||||
} ${props.noBorder ? 'is-no-border' : ''} ${props.customClass}`
|
||||
})
|
||||
|
||||
const labelClass = computed(() => {
|
||||
return `wd-textarea__label ${props.customLabelClass}`
|
||||
})
|
||||
|
||||
const inputPlaceholderClass = computed(() => {
|
||||
return `wd-textarea__placeholder ${props.placeholderClass}`
|
||||
})
|
||||
|
||||
const countClass = computed(() => {
|
||||
return `${currentLength.value > 0 ? 'wd-textarea__count-current' : ''} ${currentLength.value > props.maxlength ? 'is-error' : ''}`
|
||||
})
|
||||
|
||||
const labelStyle = computed(() => {
|
||||
return props.labelWidth
|
||||
? objToStyle({
|
||||
'min-width': props.labelWidth,
|
||||
'max-width': props.labelWidth
|
||||
})
|
||||
: ''
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
initState()
|
||||
})
|
||||
|
||||
// 状态初始化
|
||||
function initState() {
|
||||
inputValue.value = formatValue(inputValue.value)
|
||||
emit('update:modelValue', inputValue.value)
|
||||
}
|
||||
|
||||
function formatValue(value: string | number) {
|
||||
if (value === null || value === undefined) return ''
|
||||
const { maxlength, showWordLimit } = props
|
||||
if (showWordLimit && maxlength !== -1 && String(value).length > maxlength) {
|
||||
return value.toString().substring(0, maxlength)
|
||||
}
|
||||
return `${value}`
|
||||
}
|
||||
|
||||
async function handleClear() {
|
||||
focusing.value = false
|
||||
inputValue.value = ''
|
||||
if (props.focusWhenClear) {
|
||||
clearing.value = true
|
||||
focused.value = false
|
||||
}
|
||||
await pause()
|
||||
if (props.focusWhenClear) {
|
||||
focused.value = true
|
||||
focusing.value = true
|
||||
}
|
||||
emit('update:modelValue', inputValue.value)
|
||||
emit('clear')
|
||||
}
|
||||
async function handleBlur({ detail }: any) {
|
||||
// 等待150毫秒,clear执行完毕
|
||||
await pause(150)
|
||||
|
||||
if (clearing.value) {
|
||||
clearing.value = false
|
||||
return
|
||||
}
|
||||
|
||||
focusing.value = false
|
||||
emit('blur', {
|
||||
value: inputValue.value,
|
||||
cursor: detail.cursor ? detail.cursor : null
|
||||
})
|
||||
}
|
||||
function handleFocus({ detail }: any) {
|
||||
focusing.value = true
|
||||
emit('focus', detail)
|
||||
}
|
||||
function handleInput({ detail }: any) {
|
||||
inputValue.value = formatValue(inputValue.value as string)
|
||||
emit('update:modelValue', inputValue.value)
|
||||
emit('input', detail)
|
||||
}
|
||||
function handleKeyboardheightchange({ detail }: any) {
|
||||
emit('keyboardheightchange', detail)
|
||||
}
|
||||
function handleConfirm({ detail }: any) {
|
||||
emit('confirm', detail)
|
||||
}
|
||||
function handleLineChange({ detail }: any) {
|
||||
emit('linechange', detail)
|
||||
}
|
||||
function onClickPrefixIcon() {
|
||||
emit('clickprefixicon')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import './placeholder.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user