Field.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <template>
  2. <FlexView
  3. :touchable="touchable || childOnClickListener !== undefined"
  4. :pressedColor="themeContext.resolveThemeColor('FieldPressedColor', 'pressed.white')"
  5. :innerStyle="{
  6. ...themeStyles.field.value,
  7. ...(labelPosition === 'top' ? themeStyles.fieldVertical.value : {}),
  8. ...fieldStyle,
  9. ...(focused ? activeFieldStyle : {}),
  10. ...(error || finalErrorMessage ? errorFieldStyle : {})
  11. }"
  12. :direction="labelPosition === 'top' ? 'column' : 'row'"
  13. :center="labelPosition === 'top' ? false : true"
  14. @click="onClick"
  15. >
  16. <!-- 左边的标签区域 -->
  17. <FlexRow
  18. v-if="showLabel !== false && label"
  19. align="center"
  20. :flex="labelFlex"
  21. >
  22. <text v-if="requiredShow && showRequiredBadge" :style="themeStyles.requiredMark.value">*</text>
  23. <slot name="leftIcon" />
  24. <view v-if="!label" :style="labelStyle" />
  25. <text v-else :style="{
  26. ...themeStyles.labelText.value,
  27. width: labelWidth,
  28. textAlign: labelAlign,
  29. color: themeContext.resolveThemeColor(disabled ? labelDisableColor : labelColor),
  30. ...labelStyle,
  31. }">
  32. {{ label + (colon ? ': ' : '') }}
  33. </text>
  34. </FlexRow>
  35. <!-- 输入框区域 -->
  36. <FlexCol :flex="inputFlex">
  37. <FlexRow
  38. justify="space-between"
  39. align="center"
  40. :innerStyle="themeStyles.inputWapper2.value"
  41. >
  42. <slot name="prefix" />
  43. <slot name="leftButton" />
  44. <slot name="control" />
  45. <slot>
  46. <textarea
  47. v-if="multiline"
  48. ref="inputRef"
  49. :style="{
  50. ...themeStyles.input.value,
  51. ...inputStyle,
  52. ...(focused ? activeInputStyle : {}),
  53. color: themeContext.resolveThemeColor(disabled ? inputDisableColor : (error ? errorTextColor : inputColor)),
  54. textAlign: inputAlign,
  55. }"
  56. :autoHeight="autoHeight"
  57. :value="inputValue"
  58. :password="type==='password'"
  59. :placeholder="placeholder"
  60. :placeholder-style="`color: ${themeContext.resolveThemeColor(error ? errorTextColor : placeholderTextColor)}`"
  61. confirm-type="done"
  62. :maxlength="maxLength"
  63. :disabled="disabled"
  64. :readonly="readonly"
  65. @input="onInput"
  66. @focus="onFocus"
  67. @blur="onBlur"
  68. @confirm="onBlur"
  69. @click="onClick"
  70. />
  71. <input
  72. v-else
  73. ref="inputRef"
  74. :style="{
  75. ...themeStyles.input.value,
  76. ...inputStyle,
  77. ...(focused ? activeInputStyle : {}),
  78. color: themeContext.resolveThemeColor(disabled ? inputDisableColor : (error ? errorTextColor : inputColor)),
  79. textAlign: inputAlign,
  80. }"
  81. :value="inputValue"
  82. :password="type==='password'"
  83. :placeholder="placeholder"
  84. :placeholder-style="`color: ${themeContext.resolveThemeColor(error ? errorTextColor : placeholderTextColor)}`"
  85. confirm-type="done"
  86. :type="selectStyleType(type, 'text', {
  87. text: 'text',
  88. password: 'safe-password',
  89. number: 'number',
  90. decimal: 'digit',
  91. tel: 'tel',
  92. email: 'text',
  93. }) || 'text'"
  94. :maxlength="maxLength"
  95. :disabled="disabled"
  96. :readonly="readonly"
  97. @input="onInput"
  98. @focus="onFocus"
  99. @blur="onBlur"
  100. @confirm="onBlur"
  101. @click="onClick"
  102. />
  103. </slot>
  104. <slot name="suffix" />
  105. <slot name="rightButton" />
  106. <Icon v-if="showRightArrow" icon="arrow-right" :size="themeContext.resolveThemeSize('FieldRightArrowSize', 30)" v-bind="rightArrowProps" />
  107. </FlexRow>
  108. <FlexRow
  109. v-if="finalErrorMessage"
  110. :gap="10"
  111. :innerStyle="themeStyles.errorMessage.value"
  112. align="center"
  113. >
  114. <Icon
  115. :icon="errorIcon"
  116. :size="themeContext.resolveThemeSize('FieldErrorIconSize', 40)"
  117. v-bind="errorIconProps"
  118. :color="themeContext.resolveThemeColor('FieldErrorMessageColor', 'danger')"
  119. />
  120. <text :style="themeStyles.errorMessageText.value">{{finalErrorMessage}}</text>
  121. </FlexRow>
  122. <text v-if="showWordLimit" :style="themeStyles.wordLimitText.value">{{wordLimitText}}</text>
  123. </FlexCol>
  124. <!-- 清除按钮 -->
  125. <IconButton
  126. v-if="(clearButton && (
  127. clearButtonMode === 'always'
  128. || (clearButtonMode === 'while-editing' && focused))
  129. || (clearButtonMode === 'unless-editing' && modelValue !== '')
  130. )"
  131. icon="delete-filling"
  132. :color="themeContext.resolveThemeColor('FieldClearIconColor', 'grey')"
  133. v-bind="clearButtonProps"
  134. @click="onClear"
  135. />
  136. </FlexView>
  137. </template>
  138. <script setup lang="ts">
  139. import { computed, inject, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue';
  140. import { propGetThemeVar, useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
  141. import { FormItemContextContextKey, propGetFormContext, type FormContext, type FormItemContext, type FormItemInternalContext } from './FormContext';
  142. import { DynamicColor, DynamicSize, DynamicVar, selectStyleType } from '../theme/ThemeTools';
  143. import type { IconProps } from '../basic/Icon.vue';
  144. import Icon from '../basic/Icon.vue';
  145. import IconButton from '../basic/IconButton.vue';
  146. import FlexCol from '../layout/FlexCol.vue';
  147. import FlexRow from '../layout/FlexRow.vue';
  148. import FlexView from '../layout/FlexView.vue';
  149. export interface FieldInstance {
  150. /**
  151. * 获取输入框焦点
  152. */
  153. focus: () => void;
  154. /**
  155. * 取消输入框焦点
  156. */
  157. blur: () => void;
  158. /**
  159. * 清空输入框
  160. */
  161. clear: () => void;
  162. /**
  163. * 返回值表明当前输入框是否获得了焦点。
  164. */
  165. isFocused: () => boolean;
  166. /**
  167. * 清除当前条目的校验状态
  168. */
  169. clearValidate: () => void;
  170. }
  171. export interface FieldProps {
  172. modelValue?: any,
  173. /**
  174. * 输入框左侧文本
  175. */
  176. label?: string;
  177. /**
  178. * 名称,作为提交表单时的标识符
  179. */
  180. name?: string;
  181. /**
  182. * 是否可点击.可以作为一个纯点击条目
  183. */
  184. touchable?: boolean;
  185. /**
  186. * 输入框类型
  187. * @default 'text'
  188. */
  189. type?: 'text'|'tel'|'number'|'password'|'number'|'email'|'decimal';
  190. /**
  191. * 输入的最大字符数
  192. */
  193. maxLength?: number;
  194. /**
  195. * 输入框占位提示文字
  196. */
  197. placeholder?: string;
  198. /**
  199. * 是否禁用输入框
  200. * @default false
  201. */
  202. disabled?: boolean;
  203. /**
  204. * 是否只读
  205. * @default false
  206. */
  207. readonly?: boolean;
  208. /**
  209. * 是否内容垂直居中
  210. * @default false
  211. */
  212. center?: boolean;
  213. /**
  214. * 是否在 label 后面添加冒号
  215. * @default true
  216. */
  217. colon?: boolean;
  218. /**
  219. * 是否必填
  220. * @default false
  221. */
  222. required?: boolean;
  223. /**
  224. * 多行文字
  225. * @default false
  226. */
  227. multiline?: boolean;
  228. /**
  229. * 多行文字下是否自动调整高度
  230. * @default true
  231. */
  232. autoHeight?: boolean;
  233. /**
  234. * 是否显示表单必填星号
  235. * @default true
  236. */
  237. showRequiredBadge?: boolean;
  238. /**
  239. * 是否启用清除图标,点击清除图标后会清空输入框
  240. * @default false
  241. */
  242. clearButton?: boolean;
  243. /**
  244. * 清除图标的自定义属性
  245. */
  246. clearButtonProps?: IconProps;
  247. /**
  248. * 清除图标的显示模式
  249. */
  250. clearButtonMode?: 'always'|'while-editing'|'unless-editing';
  251. /**
  252. * 左侧文本的宽度
  253. */
  254. labelWidth?: string|number;
  255. /**
  256. * 左侧文本对齐
  257. * @default 'left'
  258. */
  259. labelAlign?: 'left'|'center'|'right';
  260. /**
  261. * 左侧文本的位置
  262. * @default 'left'
  263. */
  264. labelPosition?: 'top'|'left';
  265. /**
  266. * 左侧文本的flex占比
  267. * @default undefined
  268. */
  269. labelFlex?: number;
  270. /**
  271. * 输入框的flex占比
  272. * @default 5
  273. */
  274. inputFlex?: number;
  275. /**
  276. * 左侧文本的样式
  277. */
  278. labelStyle?: TextStyle;
  279. /**
  280. * 左侧文本的颜色
  281. */
  282. labelColor?: string;
  283. /**
  284. * 左侧文本的禁用颜色
  285. */
  286. labelDisableColor?: string;
  287. /**
  288. * 输入框样式
  289. */
  290. inputStyle?: TextStyle;
  291. /**
  292. * 激活时的外壳样式
  293. */
  294. activeInputStyle?: TextStyle;
  295. /**
  296. * 输入框颜色
  297. */
  298. inputColor?: string;
  299. /**
  300. * 输入框文本对齐。
  301. * @default 'left'
  302. */
  303. inputAlign?: 'left'|'center'|'right';
  304. /**
  305. * 输入框禁用颜色
  306. */
  307. inputDisableColor?: string;
  308. /**
  309. * 外壳样式
  310. */
  311. fieldStyle?: ViewStyle;
  312. /**
  313. * 激活时的外壳样式
  314. */
  315. activeFieldStyle?: ViewStyle;
  316. /**
  317. * 错误时的外壳样式
  318. */
  319. errorFieldStyle?: ViewStyle;
  320. /**
  321. * 错误时的文字颜色
  322. * @default Color.danger
  323. */
  324. errorTextColor?: string;
  325. /**
  326. * 文本框水印文字颜色
  327. * @default Color.grey
  328. */
  329. placeholderTextColor?: string;
  330. /**
  331. * 输入内容格式化函数
  332. */
  333. formatter?: (text: string) => string;
  334. /**
  335. * 格式化函数触发的时机,可选值为 blur
  336. * @default 'input'
  337. */
  338. formatTrigger?: 'blur'|'input';
  339. /**
  340. * 设置字段校验的时机
  341. * * blur 文本框失去焦点时校验
  342. * * change 数值更改时校验
  343. * * submit 提交时校验(默认)
  344. * @default 'submit'
  345. */
  346. validateTrigger?: 'blur'|'change'|'submit';
  347. /**
  348. * 是否将输入内容标红。
  349. */
  350. error?: boolean;
  351. /**
  352. * 错误提示的图标
  353. * @default 'prompt'
  354. */
  355. errorIcon?: string;
  356. /**
  357. * 错误提示图标的自定义属性
  358. */
  359. errorIconProps?: IconProps;
  360. /**
  361. * 底部错误提示文案,为空时不展示
  362. */
  363. errorMessage?: string;
  364. /**
  365. * 点击输入框是否重置错误状态
  366. * @default true
  367. */
  368. resetErrorOnClick?: boolean;
  369. /**
  370. * 是否显示字数统计
  371. * @default false
  372. */
  373. showWordLimit?: boolean;
  374. /**
  375. * 是否显左边标题
  376. * @default true
  377. */
  378. showLabel?: boolean;
  379. /**
  380. * 是否显右边箭头
  381. * @default false
  382. */
  383. showRightArrow?: boolean;
  384. /**
  385. * 右边箭头的自定义属性
  386. */
  387. rightArrowProps?: IconProps;
  388. }
  389. defineOptions({
  390. options: {
  391. styleIsolation: "shared",
  392. virtualHost: true,
  393. }
  394. })
  395. const emit = defineEmits([ 'update:modelValue', 'click', 'blur', 'focus', 'clear' ])
  396. const props = withDefaults(defineProps<FieldProps>(), {
  397. label: '',
  398. labelColor: () => propGetThemeVar('FieldLabelColor', propGetFormContext()?.fieldProps.value?.labelColor ?? 'text'),
  399. labelDisableColor: () => propGetThemeVar('FieldLabelDisableColor', propGetFormContext()?.fieldProps.value?.labelDisableColor ?? 'grey'),
  400. labelFlex: () => propGetThemeVar('FieldLabelFlex', propGetFormContext()?.labelFlex.value)!,
  401. inputDisableColor: () => propGetThemeVar('FieldInputDisableColor', propGetFormContext()?.fieldProps.value?.inputDisableColor ?? 'grey'),
  402. inputColor: () => propGetThemeVar('FieldInputColor', propGetFormContext()?.fieldProps.value?.inputColor ?? 'text'),
  403. inputFlex: () => propGetThemeVar('FieldInputFlex', propGetFormContext()?.inputFlex.value ?? 5),
  404. placeholderTextColor: () => propGetThemeVar('FieldPlaceholderTextColor', propGetFormContext()?.fieldProps.value?.placeholderTextColor ?? 'text.second'),
  405. errorTextColor: () => propGetThemeVar('FieldErrorTextColor', propGetFormContext()?.fieldProps.value?.errorTextColor ?? 'danger'),
  406. resetErrorOnClick: () => propGetThemeVar('FieldResetErrorOnClick', propGetFormContext()?.fieldProps.value?.resetErrorOnClick ?? true),
  407. colon: () => propGetFormContext()?.colon.value ?? true,
  408. fieldStyle: () => propGetFormContext()?.fieldProps.value?.fieldStyle ?? propGetThemeVar('FieldFieldStyle', {}),
  409. activeFieldStyle: () => propGetFormContext()?.fieldProps.value?.activeFieldStyle ?? propGetThemeVar('FieldActiveFieldStyle', {}),
  410. errorFieldStyle: () => propGetFormContext()?.fieldProps.value?.errorFieldStyle ?? propGetThemeVar('FieldErrorFieldStyle', {}),
  411. labelStyle: () => propGetThemeVar('FieldLabelStyle', propGetFormContext()?.fieldProps.value?.labelStyle ?? {}),
  412. inputStyle: () => propGetThemeVar('FieldInputStyle', propGetFormContext()?.fieldProps.value?.inputStyle ?? {}),
  413. activeInputStyle: () => propGetThemeVar('FieldActiveInputStyle', propGetFormContext()?.fieldProps.value?.activeInputStyle ?? {}),
  414. required: false,
  415. center: () => propGetFormContext()?.fieldProps.value?.center ?? true,
  416. showWordLimit: () => propGetFormContext()?.fieldProps.value?.showWordLimit ?? false,
  417. clearButton: () => propGetFormContext()?.fieldProps.value?.clearButton ?? false,
  418. clearButtonMode: () => propGetFormContext()?.fieldProps.value?.clearButtonMode ?? 'always',
  419. labelWidth: () => propGetFormContext()?.labelWidth.value ?? "left",
  420. labelAlign: () => propGetFormContext()?.labelAlign.value ?? "left",
  421. labelPosition: () => propGetFormContext()?.labelPosition.value ?? 'left',
  422. inputAlign: () => propGetFormContext()?.fieldProps.value?.inputAlign ?? "left",
  423. type: () => propGetFormContext()?.fieldProps.value?.type ?? "text",
  424. formatTrigger: () => propGetFormContext()?.fieldProps.value?.formatTrigger ?? 'input',
  425. showLabel: () => propGetFormContext()?.showLabel.value ?? true,
  426. showRequiredBadge: () => propGetFormContext()?.fieldProps.value?.showRequiredBadge ?? true,
  427. showRightArrow: () => propGetFormContext()?.fieldProps.value?.showRightArrow ?? false,
  428. disabled: false,
  429. readonly: false,
  430. autoHeight: true,
  431. maxLength: 100,
  432. modelValue: undefined,
  433. errorIcon: () => propGetThemeVar('FieldErrorIcon', 'prompt'),
  434. errorIconProps: () => propGetThemeVar('FieldErrorIconProps', propGetFormContext()?.fieldProps.value?.errorIconProps ?? {}),
  435. });
  436. //#region Context
  437. const formContextProps = inject<FormContext>('formContext', null as any);
  438. const error = ref<string|null>(null);
  439. let childRef : any = null;
  440. const childOnClickListener = ref<(() => void)|undefined>(undefined);
  441. //Context for parent
  442. const formItemInternalContext : FormItemInternalContext = {
  443. getValidateTrigger: () => props.validateTrigger || formContextProps?.validateTrigger.value || 'submit',
  444. getFieldName: () => props.name ?? '',
  445. setErrorState(errorMessage) { error.value = errorMessage; },
  446. getUniqueId() {
  447. return uniqueId;
  448. },
  449. setBlurState() {
  450. inputRef.value?.blur();
  451. childRef.value?.blur();
  452. },
  453. };
  454. //Context for custom children
  455. const formItemContext : FormItemContext = {
  456. getFieldName: (ref: any) => {
  457. if (ref)
  458. childRef = ref;
  459. return props.name ?? uniqueId;
  460. },
  461. onFieldFocus: () => {
  462. formContextProps?.onFieldFocus(formItemInternalContext);
  463. emit('focus');
  464. },
  465. onFieldBlur: () => {
  466. formContextProps?.onFieldBlur(formItemInternalContext);
  467. emit('blur');
  468. },
  469. getFormModelValue: () => formContextProps?.getItemValue(formItemInternalContext),
  470. onFieldChange: (newValue: unknown) => { formContextProps?.onFieldChange(formItemInternalContext, newValue); },
  471. clearValidate: () => { formContextProps?.clearValidate(formItemInternalContext); },
  472. setOnClickListener(listener: (() => void)|undefined) {
  473. childOnClickListener.value = listener;
  474. },
  475. }
  476. provide(FormItemContextContextKey, formItemContext);
  477. //Add ref in form
  478. const addNumber = formContextProps?.addFormItemField(formItemInternalContext);
  479. const uniqueId = (formContextProps?.name || 'form') + 'Item' + (props.name || `unknowProperity${addNumber}`);
  480. onBeforeUnmount(() => {
  481. formContextProps?.removeFormItemField(formItemInternalContext);
  482. })
  483. //#endregion
  484. const themeContext = useTheme();
  485. const themeStyles = themeContext.useThemeStyles({
  486. field: {
  487. backgroundColor: DynamicColor('FieldBackgroundColor', 'background.cell'),
  488. paddingVertical: DynamicSize('FieldPaddingVertical', 16),
  489. paddingHorizontal: DynamicSize('FieldPaddingHorizontal', 20),
  490. borderBottomWidth: DynamicSize('FieldBorderBottomWidth', '1px'),
  491. borderBottomColor: DynamicColor('FieldBorderBottomColor', 'border.cell'),
  492. borderBottomStyle: 'solid',
  493. },
  494. fieldVertical: {
  495. gap: DynamicSize('FieldVerticalGap', 20),
  496. },
  497. requiredMark: {
  498. fontSize: DynamicSize('FieldRequiredMark', 28),
  499. alignSelf: 'flex-start',
  500. color: DynamicColor('FieldRequiredMark', 'danger'),
  501. paddingVertical: DynamicSize('FieldRequiredMarkPaddingVertical', 8),
  502. marginHorizontal: DynamicSize('FieldRequiredMarkMarginHorizontal', 8),
  503. },
  504. labelText: {
  505. marginRight: DynamicSize('FieldLabelMarginRight', 20),
  506. fontSize: DynamicSize('FieldLabelFontSize', 28),
  507. alignSelf: 'flex-start',
  508. paddingVertical: DynamicSize('FieldLabelPaddingVertical', 8),
  509. },
  510. inputWapper2: {
  511. align: 'center',
  512. alignSelf: 'center',
  513. width: '100%',
  514. },
  515. input: {
  516. flex: 1,
  517. width: 'auto',
  518. minWidth: '100rpx',
  519. paddingVertical: DynamicSize('FieldInputPaddingVertical', 0),
  520. paddingHorizontal: DynamicSize('FieldInputPaddingHorizontal', 0),
  521. },
  522. errorMessageText: {
  523. fontSize: DynamicSize('FieldErrorMessageFontSize', 24),
  524. color: DynamicColor('FieldErrorMessageColor', 'danger'),
  525. },
  526. errorMessage: {
  527. marginTop: DynamicSize('FieldErrorMessageMarginTop', 12),
  528. },
  529. wordLimitText: {
  530. fontSize: DynamicSize('FieldWordLimitTextFontSize', 24),
  531. color: DynamicColor('FieldWordLimitTextColor', 'text.second'),
  532. width: DynamicSize('FieldWordLimitTextWidth', '100%'),
  533. textAlign: DynamicVar('FieldWordLimitTextTextAlign', 'right'),
  534. },
  535. clearIcon: {
  536. width: DynamicSize('FieldClearIconWidth', 60),
  537. justifyContent: 'center',
  538. alignItems: 'center',
  539. },
  540. });
  541. const requiredShow = computed(() => {
  542. return props.required == true || formContextProps?.getItemRequieed(formItemInternalContext);
  543. });
  544. const wordLimitText = computed(() => {
  545. let wordString = props.modelValue ? props.modelValue.length : '0';
  546. if (props.maxLength)
  547. wordString += `/${props.maxLength}`;
  548. else
  549. wordString += `字`;
  550. return wordString
  551. })
  552. const finalErrorMessage = computed(() => {
  553. return props.errorMessage || error.value || '';
  554. })
  555. const inputValue = ref();
  556. const focused = ref(false);
  557. const inputRef = ref();
  558. watch(() => props.modelValue, (newValue) => {
  559. inputValue.value = newValue;
  560. });
  561. onMounted(() => {
  562. inputValue.value = props.modelValue ?? formItemContext.getFormModelValue();
  563. });
  564. function emitChangeText(text: string) {
  565. emit('update:modelValue', text);
  566. inputValue.value = text;
  567. formItemContext.onFieldChange(text);
  568. }
  569. function doFormatter(text: string) {
  570. switch (props.type) {
  571. case 'decimal':
  572. text = text.replace(/[^\d.]/g, "");
  573. text = text.replace(/^\./g, ""); //必须保证第一个为数字而不是.
  574. text = text.replace(/\.{2,}/g, "."); //保证只有出现一个.而没有多个.
  575. text = text.replace(".","$#$").replace(/\./g, "").replace("$#$", ".");
  576. break;
  577. case 'number':
  578. text = text.replace(/[^\d]/g, '');
  579. break;
  580. case 'tel':
  581. text = text.replace(/[^(\d|\-|*|#)]/g, '');
  582. break;
  583. }
  584. if (props.formatter)
  585. text = props.formatter(text);
  586. return text;
  587. }
  588. function onInput(e: any) {
  589. focused.value = true;
  590. let text = e.detail.value;
  591. if (props.formatTrigger !== 'blur') //格式化字符串
  592. text = doFormatter(text);
  593. emitChangeText(text);
  594. }
  595. function onFocus() {
  596. focused.value = true;
  597. formItemContext.onFieldFocus();
  598. }
  599. function onBlur() {
  600. focused.value = false;
  601. if (props.formatTrigger === 'blur'){//格式化字符串
  602. const text = doFormatter(props.modelValue ?? '');
  603. emitChangeText(text);
  604. }
  605. formItemContext.onFieldBlur();
  606. }
  607. function onClear() {
  608. //清空按钮
  609. emitChangeText('');
  610. emit('clear');
  611. }
  612. function onClick() {
  613. childOnClickListener.value?.();
  614. emit('click');
  615. if (props.resetErrorOnClick)
  616. fieldInstance.clearValidate();
  617. }
  618. const fieldInstance : FieldInstance = {
  619. focus() {
  620. inputRef.value?.focus();
  621. childRef.value?.focus();
  622. },
  623. blur() {
  624. inputRef.value?.blur();
  625. childRef.value?.blur();
  626. },
  627. clear() {
  628. inputRef.value?.clear();
  629. childRef.value?.clear();
  630. },
  631. isFocused() :boolean {
  632. return focused.value;
  633. },
  634. clearValidate() {
  635. formContextProps?.clearValidate(formItemInternalContext);
  636. }
  637. }
  638. defineExpose<FieldInstance>(fieldInstance);
  639. </script>