vdesign-ui 0.1.24 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/components/activityviews/index.vue +14 -12
  2. package/dist/components/button/index.vue +1 -1
  3. package/dist/components/checkbox/checkbox-group/index.vue +2 -1
  4. package/dist/components/checkbox/index.vue +19 -20
  5. package/dist/components/dialog/index.vue +15 -11
  6. package/dist/components/dropdown/index.vue +43 -31
  7. package/dist/components/empty/404-dark.png +0 -0
  8. package/dist/components/empty/404.png +0 -0
  9. package/dist/components/empty/index.vue +30 -2
  10. package/dist/components/empty/network-dark.png +0 -0
  11. package/dist/components/empty/network.png +0 -0
  12. package/dist/components/empty/nocoupons-dark.png +0 -0
  13. package/dist/components/empty/nocoupons.png +0 -0
  14. package/dist/components/empty/nodata-dark.png +0 -0
  15. package/dist/components/empty/nodata.png +0 -0
  16. package/dist/components/empty/nomargin-dark.png +0 -0
  17. package/dist/components/empty/nomargin.png +0 -0
  18. package/dist/components/empty/nonotice-dark.png +0 -0
  19. package/dist/components/empty/nonotice.png +0 -0
  20. package/dist/components/empty/noocomments-dark.png +0 -0
  21. package/dist/components/empty/noocomments.png +0 -0
  22. package/dist/components/empty/noorders-dark.png +0 -0
  23. package/dist/components/empty/noorders.png +0 -0
  24. package/dist/components/empty/noposition-dark.png +0 -0
  25. package/dist/components/empty/noposition.png +0 -0
  26. package/dist/components/empty/nosearch-dark.png +0 -0
  27. package/dist/components/empty/nosearch.png +0 -0
  28. package/dist/components/empty/style.less +4 -0
  29. package/dist/components/footer/index.vue +5 -1
  30. package/dist/components/footnav/footnav-item/index.vue +11 -42
  31. package/dist/components/footnav/index.vue +29 -33
  32. package/dist/components/headnav/index.vue +55 -37
  33. package/dist/components/headnav/style.less +0 -9
  34. package/dist/components/icon/font/iconfont.css +62 -952
  35. package/dist/components/icon/font/iconfont.js +5 -1
  36. package/dist/components/icon/index.vue +26 -18
  37. package/dist/components/input/calcTextareaHeight.js +36 -140
  38. package/dist/components/input/index.vue +107 -109
  39. package/dist/components/input/search/index.vue +18 -22
  40. package/dist/components/input/stepper/index.vue +32 -26
  41. package/dist/components/input/style.less +20 -33
  42. package/dist/components/list/index.vue +57 -69
  43. package/dist/components/list/style.less +20 -92
  44. package/dist/components/loading/img_status_loading_white_ani.svg +155 -0
  45. package/dist/components/loading/img_status_refresh_ani.svg +158 -0
  46. package/dist/components/loading/index.vue +28 -11
  47. package/dist/components/loading/style.less +1 -1
  48. package/dist/components/mixins/clickoutside.js +81 -81
  49. package/dist/components/mixins/outlineConfigPlugin.js +11 -6
  50. package/dist/components/noticebar/index.vue +9 -4
  51. package/dist/components/pagebreak/index.vue +21 -14
  52. package/dist/components/radio/index.vue +164 -135
  53. package/dist/components/radio/radio-group/index.vue +40 -52
  54. package/dist/components/result/index.vue +1 -2
  55. package/dist/components/selector/index.vue +49 -31
  56. package/dist/components/selector/style.less +14 -0
  57. package/dist/components/step-item/index.vue +2 -2
  58. package/dist/components/tabs/index.vue +55 -32
  59. package/dist/components/tabs/tab/index.vue +13 -16
  60. package/dist/components/tag/index.vue +18 -4
  61. package/dist/components/tag/style.less +2 -2
  62. package/dist/components/title/index.vue +11 -8
  63. package/dist/components/title/style.less +6 -0
  64. package/dist/img/img_status_loading_white_ani.b56fcfae.svg +155 -0
  65. package/dist/img/img_status_refresh_ani.d0e59f12.svg +158 -0
  66. package/dist/token.css +8 -0
  67. package/dist/vdesign-ui.common.js +1481 -1354
  68. package/dist/vdesign-ui.css +1 -1
  69. package/dist/vdesign-ui.umd.js +1481 -1354
  70. package/dist/vdesign-ui.umd.min.js +3 -3
  71. package/package.json +1 -1
  72. package/dist/components/loading/loading.png +0 -0
  73. package/dist/components/loading/refresh.png +0 -0
  74. package/dist/img/404-dark.775df5bb.png +0 -0
  75. package/dist/img/network-dark.11a147bb.png +0 -0
  76. package/dist/img/nodata-dark.b0ea0e39.png +0 -0
@@ -1,18 +1,23 @@
1
1
  <template>
2
- <svg v-if="svg" class="vd-iconfont vd-svg-icon" :class="[`vd-icon-${name}`, size ? `vd-icon-${size}` : '']"
3
- :style="{ fill: color }" @click="$emit('click', $event)">
4
- <use :xlink:href="`#vd-icon-${name}`" />
5
- </svg>
6
- <i v-else-if="name" class="vd-iconfont"
7
- :class="[isImage(name) ? '' : `vd-icon-${name}`, size ? `vd-icon-${size}` : '']" :style="{ color }"
8
- @click="$emit('click', $event)">
9
- <img class="vd-img-icon" v-if="isImage(name)" :src="name" />
10
- </i>
2
+ <span>
3
+ <template v-if="name">
4
+ <svg v-if="isSvg" class="vd-iconfont vd-svg-icon" :class="[`vd-icon-${name}`, sizeClass]" :style="{ fill: color }"
5
+ @click="$emit('click', $event)">
6
+ <use :xlink:href="`#vd-icon-${name}`" />
7
+ </svg>
8
+ <i v-else class="vd-iconfont" :class="[isImage ? '' : `vd-icon-${name}`, sizeClass]" :style="{ color }"
9
+ @click="$emit('click', $event)">
10
+ <img class="vd-img-icon" v-if="isImage" :src="name" />
11
+ </i>
12
+ </template>
13
+ <slot v-else></slot>
14
+ </span>
11
15
  </template>
12
16
 
13
17
  <script>
14
- import './font/iconfont.js'
15
18
  import "./font/iconfont.css";
19
+ import "./font/iconfont.js";
20
+
16
21
  export default {
17
22
  name: 'vd-icon',
18
23
  props: {
@@ -28,16 +33,19 @@ export default {
28
33
  type: String,
29
34
  default: '',
30
35
  },
31
- svg: {
32
- type: Boolean,
33
- default: false,
34
- }
35
36
  },
36
- methods: {
37
- isImage(name) {
38
- return name ? name.indexOf('/') !== -1 : false;
37
+ computed: {
38
+ isImage() {
39
+ return this.name.includes('/');
40
+ },
41
+ isSvg() {
42
+ // 判断是否为 SVG 图标,依据 name 是否以 '.svg' 结尾
43
+ return this.name.endsWith('_svg');
44
+ },
45
+ sizeClass() {
46
+ return this.size ? `vd-icon-${this.size}` : '';
39
47
  }
40
- },
48
+ }
41
49
  }
42
50
  </script>
43
51
 
@@ -1,117 +1,5 @@
1
- // Thanks to
2
- // https://github.com/andreypopp/react-textarea-autosize/
3
-
4
- // let hiddenTextarea;
5
- //
6
- // const HIDDEN_STYLE = `
7
- // height:0 !important;
8
- // min-height:0 !important;
9
- // max-height:none !important;
10
- // visibility:hidden !important;
11
- // overflow:hidden !important;
12
- // position:absolute !important;
13
- // z-index:-1000 !important;
14
- // top:0 !important;
15
- // right:0 !important
16
- // `;
17
- //
18
- // const CONTEXT_STYLE = [
19
- // 'letter-spacing',
20
- // 'line-height',
21
- // 'padding-top',
22
- // 'padding-bottom',
23
- // 'font-family',
24
- // 'font-weight',
25
- // 'font-size',
26
- // 'text-rendering',
27
- // 'text-transform',
28
- // 'width',
29
- // 'text-indent',
30
- // 'padding-left',
31
- // 'padding-right',
32
- // 'border-width',
33
- // 'box-sizing'
34
- // ];
35
- //
36
- // function calculateNodeStyling(node) {
37
- // const style = window.getComputedStyle(node);
38
- //
39
- // const boxSizing = style.getPropertyValue('box-sizing');
40
- //
41
- // const paddingSize = (
42
- // parseFloat(style.getPropertyValue('padding-bottom')) +
43
- // parseFloat(style.getPropertyValue('padding-top'))
44
- // );
45
- //
46
- // const borderSize = (
47
- // parseFloat(style.getPropertyValue('border-bottom-width')) +
48
- // parseFloat(style.getPropertyValue('border-top-width'))
49
- // );
50
- //
51
- // const contextStyle = CONTEXT_STYLE
52
- // .map(name => `${name}:${style.getPropertyValue(name)}`)
53
- // .join(';');
54
- //
55
- // return {contextStyle, paddingSize, borderSize, boxSizing};
56
- // }
57
- //
58
- // export default function calcTextareaHeight(targetNode, minRows = null, maxRows = null) {
59
- // if (!hiddenTextarea) {
60
- // hiddenTextarea = document.createElement('textarea');
61
- // document.body.appendChild(hiddenTextarea);
62
- // }
63
- //
64
- // let {
65
- // paddingSize,
66
- // borderSize,
67
- // boxSizing,
68
- // contextStyle
69
- // } = calculateNodeStyling(targetNode);
70
- //
71
- // hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
72
- // hiddenTextarea.value = targetNode.value || targetNode.placeholder || '';
73
- //
74
- // let height = hiddenTextarea.scrollHeight;
75
- // let minHeight = -Infinity;
76
- // let maxHeight = Infinity;
77
- // let overflowY;
78
- //
79
- // if (boxSizing === 'border-box') {
80
- // height = height + borderSize;
81
- // } else if (boxSizing === 'content-box') {
82
- // height = height - paddingSize;
83
- // }
84
- //
85
- // hiddenTextarea.value = '';
86
- // let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
87
- //
88
- // if (minRows !== null) {
89
- // minHeight = singleRowHeight * minRows;
90
- // if (boxSizing === 'border-box') {
91
- // minHeight = minHeight + paddingSize + borderSize;
92
- // }
93
- // height = Math.max(minHeight, height);
94
- // }
95
- // if (maxRows !== null) {
96
- // maxHeight = singleRowHeight * maxRows;
97
- // if (boxSizing === 'border-box') {
98
- // maxHeight = maxHeight + paddingSize + borderSize;
99
- // }
100
- // overflowY = height > maxHeight ? '' : 'hidden';
101
- // height = Math.min(maxHeight, height);
102
- // }
103
- //
104
- // if (!maxRows) {
105
- // overflowY = 'hidden';
106
- // }
107
- //
108
- // return {
109
- // height: `${height}px`,
110
- // minHeight: `${minHeight}px`,
111
- // maxHeight: `${maxHeight}px`,
112
- // overflowY
113
- // };
114
- // }
1
+ // 定义全局的环境检查
2
+ const inBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
115
3
 
116
4
  const HIDDEN_TEXTAREA_STYLE = `
117
5
  min-height:0 !important;
@@ -122,7 +10,7 @@ const HIDDEN_TEXTAREA_STYLE = `
122
10
  position:absolute !important;
123
11
  z-index:-1000 !important;
124
12
  top:0 !important;
125
- right:0 !important
13
+ right:0 !important;
126
14
  `;
127
15
 
128
16
  const SIZING_STYLE = [
@@ -144,13 +32,25 @@ const SIZING_STYLE = [
144
32
  ];
145
33
 
146
34
  let computedStyleCache = {};
147
- let hiddenTextarea;
35
+ let hiddenTextarea = null;
148
36
 
37
+ // 计算节点的样式信息
149
38
  function calculateNodeStyling(node, useCache = false) {
39
+ if (!inBrowser || !node) {
40
+ // 如果不在浏览器环境中,返回默认的样式对象,防止报错
41
+ return {
42
+ sizingStyle: '',
43
+ paddingSize: 0,
44
+ borderSize: 0,
45
+ boxSizing: 'content-box',
46
+ };
47
+ }
48
+
150
49
  const nodeRef = (
151
- node.getAttribute('id') ||
152
- node.getAttribute('data-reactid') ||
153
- node.getAttribute('name'));
50
+ node.getAttribute('id') ||
51
+ node.getAttribute('data-reactid') ||
52
+ node.getAttribute('name')
53
+ );
154
54
 
155
55
  if (useCache && computedStyleCache[nodeRef]) {
156
56
  return computedStyleCache[nodeRef];
@@ -192,30 +92,27 @@ function calculateNodeStyling(node, useCache = false) {
192
92
  return nodeInfo;
193
93
  }
194
94
 
95
+ // 计算 textarea 的高度
195
96
  export default function calcTextareaHeight(uiTextNode, minRows = null, maxRows = null, useCache = false) {
97
+ if (!inBrowser || !uiTextNode) {
98
+ // 如果不在浏览器环境中,返回默认的高度,避免 SSR 中出错
99
+ return { height: '0px', minHeight: '0px', maxHeight: '0px', overflowY: 'hidden' };
100
+ }
101
+
196
102
  if (!hiddenTextarea) {
103
+ // 创建一个隐藏的 textarea 元素来测量高度
197
104
  hiddenTextarea = document.createElement('textarea');
198
105
  document.body.appendChild(hiddenTextarea);
199
106
  }
200
107
 
201
- // Fix wrap="off" issue
202
- // https://github.com/ant-design/ant-design/issues/6577
203
108
  if (uiTextNode.getAttribute('wrap')) {
204
109
  hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap'));
205
110
  } else {
206
111
  hiddenTextarea.removeAttribute('wrap');
207
112
  }
208
113
 
209
- // Copy all CSS properties that have an impact on the height of the content in
210
- // the textbox
211
- let {
212
- paddingSize, borderSize,
213
- boxSizing, sizingStyle,
214
- } = calculateNodeStyling(uiTextNode, useCache);
114
+ const { paddingSize, borderSize, boxSizing, sizingStyle } = calculateNodeStyling(uiTextNode, useCache);
215
115
 
216
- // Need to have the overflow attribute to hide the scrollbar otherwise
217
- // text-lines will not calculated properly as the shadow will technically be
218
- // narrower for content
219
116
  hiddenTextarea.setAttribute('style', `${sizingStyle};${HIDDEN_TEXTAREA_STYLE}`);
220
117
  hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || '';
221
118
 
@@ -225,34 +122,33 @@ export default function calcTextareaHeight(uiTextNode, minRows = null, maxRows =
225
122
  let overflowY;
226
123
 
227
124
  if (boxSizing === 'border-box') {
228
- // border-box: add border, since height = content + padding + border
229
- height = height + borderSize;
125
+ height += borderSize;
230
126
  } else if (boxSizing === 'content-box') {
231
- // remove padding, since height = content
232
- height = height - paddingSize;
127
+ height -= paddingSize;
233
128
  }
234
129
 
235
130
  if (minRows !== null || maxRows !== null) {
236
- // measure height of a textarea with a single row
237
131
  hiddenTextarea.value = ' ';
238
- let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
132
+ const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
133
+
239
134
  if (minRows !== null) {
240
135
  minHeight = singleRowHeight * minRows;
241
136
  if (boxSizing === 'border-box') {
242
- minHeight = minHeight + paddingSize + borderSize;
137
+ minHeight += paddingSize + borderSize;
243
138
  }
244
139
  height = Math.max(minHeight, height);
245
140
  }
141
+
246
142
  if (maxRows !== null) {
247
143
  maxHeight = singleRowHeight * maxRows;
248
144
  if (boxSizing === 'border-box') {
249
- maxHeight = maxHeight + paddingSize + borderSize;
145
+ maxHeight += paddingSize + borderSize;
250
146
  }
251
147
  overflowY = height > maxHeight ? '' : 'hidden';
252
148
  height = Math.min(maxHeight, height);
253
149
  }
254
150
  }
255
- // Remove scroll bar flash when autosize without maxRows
151
+
256
152
  if (!maxRows) {
257
153
  overflowY = 'hidden';
258
154
  }
@@ -261,6 +157,6 @@ export default function calcTextareaHeight(uiTextNode, minRows = null, maxRows =
261
157
  height: `${height}px`,
262
158
  minHeight: `${minHeight}px`,
263
159
  maxHeight: `${maxHeight}px`,
264
- overflowY
160
+ overflowY,
265
161
  };
266
162
  }
@@ -1,34 +1,17 @@
1
1
  <template>
2
2
  <div :class="wrapClasses" class="vd-input">
3
- <template v-if="inputTextArea || type !== 'textarea'">
4
- <span v-if="prefix || $slots.prefix" class="vd-input-form__prefix" :class="borderRightClasses">
3
+ <!-- 前缀,还有前缀右边框 -->
4
+ <span v-if="hasPrefix" class="vd-input-form__prefix" :class="borderRightClasses">
5
5
  <slot name="prefix">{{ prefix }}</slot>
6
6
  </span>
7
- <input
8
- v-if="!inputTextArea"
9
- ref="input"
10
- v-bind="$attrs"
11
- :type="passwordIcon ? (passwordVisible ? 'text' : 'password') : type"
12
- :class="inputClasses"
13
- :disabled="disabled"
14
- :placeholder="placeholder"
15
- :value="currentValue"
16
- @input="handleInput"
17
- @focus="handleFocus"
18
- @blur="handleBlur"
19
- />
20
-
21
7
  <textarea
22
- v-else
8
+ v-if="type === 'textarea'"
23
9
  ref="textarea"
24
10
  v-bind="$attrs"
25
- :id="elementId"
26
- :wrap="wrap"
27
- :type="type"
28
11
  :rows="rows"
29
12
  :class="inputClasses"
30
13
  :disabled="disabled"
31
- :placeholder="placeholder"
14
+ :readonly="readonly"
32
15
  :value="currentValue"
33
16
  @focus="handleFocus"
34
17
  @blur="handleBlur"
@@ -36,71 +19,58 @@
36
19
  :style="textareaStyles"
37
20
  >
38
21
  </textarea>
22
+ <input
23
+ v-else
24
+ ref="input"
25
+ v-bind="$attrs"
26
+ :type="passwordIcon ? (passwordVisible ? 'text' : 'password') : type"
27
+ :class="inputClasses"
28
+ :disabled="disabled"
29
+ :readonly="readonly"
30
+ :value="currentValue"
31
+ @input="handleInput"
32
+ @focus="handleFocus"
33
+ @blur="handleBlur"
34
+ />
35
+
36
+ <!-- 右侧功能区域 -->
37
+
39
38
  <div
40
- v-if="suffix || $slots.suffix || append || $slots.append || isWordLimitVisible"
39
+ v-if="showRightSection"
41
40
  class="vd-input-form__right"
42
41
  >
43
- <span v-if="suffix || $slots.suffix" class="vd-input-form__suffix">
44
- <slot name="suffix">
45
- <vd-icon name="icon_btn_clean" @click="handleClear"></vd-icon>
46
- </slot>
42
+ <!-- 清除按钮 -->
43
+ <span v-if="showClear" class="vd-input-form__suffix">
44
+ <vd-icon name="icon_btn_clean" @mousedown.prevent.native="handleClear"></vd-icon>
47
45
  </span>
48
- <span v-if="append || $slots.append" class="vd-input-form__append">
46
+ <!-- (大数字的时候)设计要求的自定义文字 -->
47
+ <span v-if="hasAppend" class="vd-input-form__append">
49
48
  <slot name="append">{{ append }}</slot>
50
49
  </span>
50
+ <!-- 字数限制 -->
51
51
  <div
52
52
  v-if="isWordLimitVisible"
53
- :class="[
54
- 'vd-input__word-limit',
55
- { 'vd-input__word-limit--error': isOverLimit },
56
- ]"
53
+ :class="wordLimitClasses"
57
54
  >
58
55
  <span>{{ textLength }}</span
59
56
  >/<span>{{ upperLimit }}</span>
60
57
  </div>
61
58
  </div>
59
+ <!-- 密码的时候,单独的清空按钮 -->
62
60
  <span
63
- v-if="passwordClose || passwordIcon"
61
+ v-if="showPasswordControls"
64
62
  class="vd-input-form__password"
65
63
  >
66
- <span v-if="passwordClose" class="vd-input-form__password--close">
67
- <vd-icon name="icon_btn_close" @click="handleClear"></vd-icon>
64
+ <span v-if="showPasswordClear" class="vd-input-form__password--close">
65
+ <vd-icon name="icon_btn_close" @mousedown.prevent.native="handleClear"></vd-icon>
68
66
  </span>
69
- <span v-if="passwordIcon" class="vd-input-form__password--password">
67
+ <span v-if="showPasswordIcon" class="vd-input-form__password--password">
70
68
  <vd-icon
71
69
  :name="passwordVisible ? 'icon_btn_eye' : 'icon_btn_eye_close'"
72
70
  @click="handlePasswordVisible"
73
71
  ></vd-icon>
74
72
  </span>
75
73
  </span>
76
- </template>
77
- <template v-else>
78
- <textarea
79
- ref="textarea"
80
- :type="type"
81
- v-bind="$attrs"
82
- :wrap="wrap"
83
- :class="inputClasses"
84
- :disabled="disabled"
85
- :placeholder="placeholder"
86
- :value="currentValue"
87
- :rows="rows"
88
- :name="name"
89
- @input="handleInput"
90
- :style="textareaStyles"
91
- >
92
- </textarea>
93
- <div
94
- v-if="isWordLimitVisible"
95
- :class="[
96
- 'vd-input__word-limit',
97
- { 'vd-input__word-limit--error': isOverLimit },
98
- ]"
99
- >
100
- <span>{{ textLength }}</span
101
- >/<span>{{ upperLimit }}</span>
102
- </div>
103
- </template>
104
74
  </div>
105
75
  </template>
106
76
 
@@ -111,26 +81,26 @@ const prefixCls = "vd-input";
111
81
  export default {
112
82
  name: "vd-input",
113
83
  props: {
84
+ value: {
85
+ type: [String, Number],
86
+ default: "",
87
+ },
114
88
  type: {
115
89
  type: String,
116
90
  default: "text",
117
91
  },
118
92
  prefix: {
119
93
  type: [Boolean, String],
94
+ default: ''
120
95
  },
121
96
  borderRight: {
122
97
  type: Boolean,
123
98
  default: true
124
99
  },
125
- suffix: {
126
- type: Boolean,
127
- },
128
100
  // 设计如此,单独的文字
129
101
  append: {
130
- type: Boolean,
131
- },
132
- placeholder: {
133
- type: String,
102
+ type: [Boolean, String],
103
+ default: ''
134
104
  },
135
105
  error: {
136
106
  type: Boolean,
@@ -144,27 +114,17 @@ export default {
144
114
  passwordClose: {
145
115
  type: Boolean,
146
116
  },
147
- passwordType: {
148
- type: String,
149
- default: "icon_btn_eye_close",
150
- },
117
+ // 输入框类型 表单类型,常规输入库,搜索框
151
118
  form: {
152
119
  type: String,
153
120
  },
154
121
  disabled: {
155
122
  type: Boolean,
156
123
  },
157
- value: {
158
- type: [String, Number],
159
- default: "",
160
- },
161
124
  rows: {
162
125
  type: Number,
163
126
  default: 2,
164
127
  },
165
- name: {
166
- type: String,
167
- },
168
128
  // 设计如此多行textarea,跟input属性一样
169
129
  inputTextArea: {
170
130
  type: Boolean,
@@ -180,29 +140,66 @@ export default {
180
140
  type: [Boolean, Object],
181
141
  default: false,
182
142
  },
183
- elementId: {
184
- type: String,
185
- },
186
- wrap: {
187
- type: String,
188
- default: "soft",
189
- },
190
143
  // 新增0118
191
144
  showWordLimit: {
192
145
  type: Boolean,
193
146
  default: false,
194
147
  },
148
+ clearable:Boolean,
149
+ readonly:Boolean
195
150
  },
196
151
  data() {
197
152
  return {
198
153
  focusClass: false,
199
154
  currentValue: this.value,
200
- isOnComposition: false,
201
155
  textareaStyles: {},
202
156
  passwordVisible: false,
157
+ focused: false,
203
158
  };
204
159
  },
205
160
  computed: {
161
+ hasPrefix() {
162
+ return (this.prefix !== undefined && this.prefix !== '') || this.$slots.prefix;
163
+ },
164
+ // 是否显示右侧区域
165
+ showRightSection() {
166
+ return this.showClear || this.hasAppend || this.isWordLimitVisible;
167
+ },
168
+ // 是否有附加内容
169
+ hasAppend() {
170
+ return (this.append !== undefined && this.append !== '') || this.$slots.append;
171
+ },
172
+ // 是否显示清除按钮(假设已经在 computed 中定义)
173
+ showClear() {
174
+ return (
175
+ this.clearable &&
176
+ this.focused &&
177
+ this.currentValue !== '' &&
178
+ !this.disabled &&
179
+ !this.readonly
180
+ );
181
+ },
182
+ wordLimitClasses() {
183
+ return [
184
+ 'vd-input__word-limit',
185
+ { 'vd-input__word-limit--error': this.isOverLimit },
186
+ ];
187
+ },
188
+ showPasswordClear() {
189
+ return (
190
+ this.passwordClose &&
191
+ this.focused &&
192
+ this.currentValue !== '' &&
193
+ !this.disabled &&
194
+ !this.readonly
195
+ );
196
+ },
197
+ showPasswordIcon() {
198
+ return this.passwordIcon && this.type === 'password';
199
+ },
200
+ showPasswordControls() {
201
+ return this.showPasswordClear || this.showPasswordIcon;
202
+ },
206
203
  wrapClasses() {
207
204
  return [
208
205
  {
@@ -212,7 +209,7 @@ export default {
212
209
  [`${prefixCls}-${this.type}--warning`]: this.warning,
213
210
  [`${prefixCls}--bigsize`]: this.bigSize,
214
211
  [`${prefixCls}--error`]: this.error,
215
- [`${prefixCls}--active`]: this.focusClass,
212
+ [`${prefixCls}--active`]: this.focused,
216
213
  [`${prefixCls}-inputarea`]: this.inputTextArea,
217
214
  "vd-hairline--bottom": this.hairline,
218
215
  "vd-textarea-auto": this.inputTextArea,
@@ -235,6 +232,7 @@ export default {
235
232
  },
236
233
  ];
237
234
  },
235
+ // 当前输入的文本长度
238
236
  textLength() {
239
237
  if (typeof this.value === "number") {
240
238
  return String(this.value).length;
@@ -257,37 +255,34 @@ export default {
257
255
  },
258
256
  methods: {
259
257
  handleFocus(event) {
260
- if (this.form) {
261
- this.focusClass = true;
262
- }
258
+ this.focused = true;
263
259
  this.$emit("focus", event);
260
+
261
+ if (this.readonly) {
262
+ this.blur();
263
+ }
264
264
  },
265
265
  handleBlur(event) {
266
- if (this.form) {
267
- this.focusClass = false;
268
- }
266
+ this.focused = false;
269
267
  this.$emit("blur", event);
270
268
  },
271
269
  focus() {
272
270
  if (this.type === "textarea") {
273
- this.$refs.textarea.focus();
271
+ this.$refs.textarea && this.$refs.textarea.focus();
274
272
  } else {
275
- this.$refs.input.focus();
273
+ this.$refs.input && this.$refs.input.focus();
276
274
  }
277
275
  },
278
276
  blur() {
279
277
  if (this.type === "textarea") {
280
- this.$refs.textarea.blur();
278
+ this.$refs.textarea && this.$refs.textarea.blur();
281
279
  } else {
282
- this.$refs.input.blur();
280
+ this.$refs.input && this.$refs.input.blur();
283
281
  }
284
282
  },
285
283
 
286
284
  handleInput(event) {
287
- if (this.isOnComposition) return;
288
285
  let value = event.target.value;
289
- if (this.number && value !== "")
290
- value = Number.isNaN(Number(value)) ? value : Number(value);
291
286
  this.$emit("input", value);
292
287
  this.setCurrentValue(value);
293
288
  this.$emit("change", event);
@@ -295,18 +290,16 @@ export default {
295
290
 
296
291
  setCurrentValue(value) {
297
292
  if (value === this.currentValue) return;
293
+ this.currentValue = value;
298
294
  this.$nextTick(() => {
299
295
  this.resizeTextarea();
300
296
  });
301
- this.currentValue = value;
302
297
  },
303
298
  resizeTextarea() {
304
- const autosize = this.autosize;
305
- if (!autosize || this.type !== "textarea") {
306
- return false;
299
+ if (!this.autosize || this.type !== "textarea") {
300
+ return;
307
301
  }
308
- const minRows = autosize.minRows;
309
- const maxRows = autosize.maxRows;
302
+ const { minRows, maxRows } = this.autosize;
310
303
  this.textareaStyles = calcTextareaHeight(
311
304
  this.$refs.textarea,
312
305
  minRows,
@@ -319,7 +312,12 @@ export default {
319
312
  this.setCurrentValue("");
320
313
  this.$emit("change", e);
321
314
  this.$emit("clear");
315
+ this.$nextTick(() => {
316
+ this.focus();
317
+ });
322
318
  },
319
+
320
+ // 切换密码显示状态
323
321
  handlePasswordVisible() {
324
322
  this.passwordVisible = !this.passwordVisible;
325
323
  this.$nextTick(() => {