@tdesign/uniapp 0.8.1 → 0.9.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 (50) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/badge/README.en-US.md +1 -0
  3. package/dist/badge/README.md +1 -0
  4. package/dist/badge/badge.css +1 -1
  5. package/dist/button/button.vue +5 -0
  6. package/dist/calendar/calendar.vue +6 -2
  7. package/dist/common/style/theme/index-light.css +282 -0
  8. package/dist/common/style/theme/index-light.less +9 -0
  9. package/dist/common/style/theme/raw/_components-light.less +8 -0
  10. package/dist/common/style/theme/raw/_light-only.less +181 -0
  11. package/dist/dropdown-item/dropdown-item.vue +2 -0
  12. package/dist/dropdown-menu/dropdown-menu.vue +1 -1
  13. package/dist/fab/fab.vue +2 -2
  14. package/dist/fab/props.ts +1 -1
  15. package/dist/fab/type.ts +1 -1
  16. package/dist/form-item/form-item.css +2 -2
  17. package/dist/form-item/form-item.vue +28 -21
  18. package/dist/indexes/computed.js +6 -2
  19. package/dist/indexes/indexes.css +7 -2
  20. package/dist/indexes/indexes.vue +1 -1
  21. package/dist/indexes/props.ts +5 -0
  22. package/dist/indexes/type.ts +5 -0
  23. package/dist/input/input.vue +8 -6
  24. package/dist/search/search.css +5 -0
  25. package/dist/search/search.vue +7 -12
  26. package/dist/segmented/README.en-US.md +42 -0
  27. package/dist/segmented/README.md +75 -0
  28. package/dist/segmented/props.ts +31 -0
  29. package/dist/segmented/segmented.css +66 -0
  30. package/dist/segmented/segmented.vue +174 -0
  31. package/dist/segmented/type.ts +41 -0
  32. package/dist/tab-bar-item/tab-bar-item.vue +4 -6
  33. package/dist/table/README.en-US.md +72 -0
  34. package/dist/table/README.md +117 -0
  35. package/dist/table/base-table-props.ts +105 -0
  36. package/dist/table/props.ts +94 -0
  37. package/dist/table/table.css +251 -0
  38. package/dist/table/table.vue +551 -0
  39. package/dist/table/type.ts +180 -0
  40. package/dist/tabs/tabs.css +4 -0
  41. package/dist/theme-light.css +282 -0
  42. package/dist/theme-light.css.d.ts +2 -0
  43. package/dist/theme-light.less +1 -0
  44. package/dist/theme-light.less.d.ts +2 -0
  45. package/dist/types/index.d.ts +2 -0
  46. package/dist/types/segmented.d.ts +7 -0
  47. package/dist/types/table.d.ts +7 -0
  48. package/global.d.ts +2 -0
  49. package/package.json +33 -9
  50. package/{dist/script → script}/postinstall.js +18 -2
@@ -5,19 +5,24 @@
5
5
  >
6
6
  <view :class="formItemClass + '-wrap ' + formItemClass + '--' + dataLabelAlign + ' ' + tClassWrap">
7
7
  <!-- 标签区域 -->
8
- <view
9
- v-if="label"
10
- :class="labelClass + ' ' + labelClass + '--' + dataLabelAlign
11
- + (dataRequiredMark ? ' ' + labelClass + '--required' : '')
12
- + (dataRequiredMark && requiredMarkPosition === 'right' ? ' ' + labelClass + '--required-right' : '')
13
- + ' ' + tClassLabel"
14
- :style="'width: ' + dataLabelWidth"
15
- >
16
- <label :for="forId">{{ label }}</label>
17
- <template v-if="colon">
18
- {{ globalConfig.colonText }}
19
- </template>
20
- </view>
8
+ <slot name="label">
9
+ <view
10
+ v-if="label"
11
+ :class="labelClass + ' ' + labelClass + '--' + dataLabelAlign
12
+ + (dataRequiredMark ? ' ' + labelClass + '--required' : '')
13
+ + (dataRequiredMark && requiredMarkPosition === 'right' ? ' ' + labelClass + '--required-right' : '')
14
+ + ' ' + tClassLabel"
15
+ :style="'width: ' + dataLabelWidth"
16
+ >
17
+ <label
18
+ :class="labelClass + '-text'"
19
+ :for="forId"
20
+ >{{ label }}</label>
21
+ <template v-if="colon">
22
+ {{ globalConfig.colonText }}
23
+ </template>
24
+ </view>
25
+ </slot>
21
26
 
22
27
  <!-- 内容区域 -->
23
28
  <view :class="formClass + '__controls ' + errorClasses + ' ' + tClassControls">
@@ -28,12 +33,14 @@
28
33
  <slot />
29
34
  </view>
30
35
  <!-- 帮助信息 -->
31
- <view
32
- v-if="help"
33
- :class="formItemClass + '-help ' + formClass + '__controls--' + dataContentAlign + ' ' + tClassHelp"
34
- >
35
- {{ help }}
36
- </view>
36
+ <slot name="help">
37
+ <view
38
+ v-if="help"
39
+ :class="formItemClass + '-help ' + formClass + '__controls--' + dataContentAlign + ' ' + tClassHelp"
40
+ >
41
+ {{ help }}
42
+ </view>
43
+ </slot>
37
44
 
38
45
  <!-- 校验提示信息 -->
39
46
  <view
@@ -162,14 +169,14 @@ export default {
162
169
  const formRules = target.rules?.[this.name];
163
170
  const isRequired = formRules?.some(rule => rule.required);
164
171
 
165
- this.dataRules = formRules;
172
+ this.dataRules = formRules || [];
166
173
  this.colon = target.colon;
167
174
  this.dataLabelAlign = labelAlign || target.labelAlign;
168
175
  this.dataLabelWidth = normalizeLabelWidth(labelWidth || target.labelWidth);
169
176
  this.dataContentAlign = contentAlign || target.contentAlign;
170
177
  this.dataRequiredMark = requiredMark || target.requiredMark || globalConfig.requiredMark || isRequired;
171
178
  this.dataShowErrorMessage = typeof showErrorMessage === 'boolean' ? showErrorMessage : target.showErrorMessage;
172
- this.requiredMarkPosition = target.requiredMarkPosition || globalConfig.requiredMarkPosition;
179
+ this.requiredMarkPosition = target.requiredMarkPosition || globalConfig.requiredMarkPosition || 'left';
173
180
  },
174
181
  innerAfterUnlinked() {
175
182
  if (this.form) {
@@ -1,4 +1,8 @@
1
- export function getFirstCharacter(str) {
2
- return str.toString().substring(0, 1);
1
+ export function getFirstCharacter(str, showFullIndex) {
2
+ const res = str.toString();
3
+ if (showFullIndex) {
4
+ return res;
5
+ }
6
+ return res.substring(0, 1);
3
7
  }
4
8
 
@@ -5,17 +5,22 @@
5
5
  .t-indexes__sidebar {
6
6
  position: fixed;
7
7
  right: var(--td-indexes-sidebar-right, 16rpx);
8
- width: var(--td-indexes-sidebar-item-size, 40rpx);
9
8
  color: var(--td-indexes-sidebar-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))));
10
9
  font: var(--td-indexes-sidebar-font, var(--td-font-body-small, 24rpx / 40rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)));
11
10
  display: flex;
11
+ align-items: center;
12
+ justify-content: center;
12
13
  flex-flow: column nowrap;
13
14
  top: 50%;
14
15
  transform: translateY(-50%);
15
16
  z-index: 1;
16
17
  }
17
18
  .t-indexes__sidebar-item {
18
- border-radius: 50%;
19
+ min-width: var(--td-indexes-sidebar-item-size, 40rpx);
20
+ height: var(--td-indexes-sidebar-item-size, 40rpx);
21
+ padding: 0 calc(var(--td-spacer, 16rpx) / 2);
22
+ box-sizing: border-box;
23
+ border-radius: var(--td-indexes-sidebar-item-size, 40rpx);
19
24
  position: relative;
20
25
  text-align: center;
21
26
  }
@@ -22,7 +22,7 @@
22
22
  aria-role="button"
23
23
  :aria-label="dataCurrent === item ? '已选中' + item : ''"
24
24
  >
25
- {{ getFirstCharacter(item) }}
25
+ {{ getFirstCharacter(item, showFullIndex) }}
26
26
  </view>
27
27
 
28
28
  <view
@@ -17,6 +17,11 @@ export default {
17
17
  indexList: {
18
18
  type: Array,
19
19
  },
20
+ /** 索引是否完整展示 */
21
+ showFullIndex: {
22
+ type: Boolean,
23
+ default: false,
24
+ },
20
25
  /** 索引是否吸顶,默认为true */
21
26
  sticky: {
22
27
  type: Boolean,
@@ -17,6 +17,11 @@ export interface TdIndexesProps {
17
17
  * 索引字符列表。不传默认 `A-Z`
18
18
  */
19
19
  indexList?: Array<string | number>;
20
+ /**
21
+ * 索引是否完整展示
22
+ * @default false
23
+ */
24
+ showFullIndex?: Boolean;
20
25
  /**
21
26
  * 索引是否吸顶,默认为true
22
27
  * @default true
@@ -143,15 +143,16 @@
143
143
  </view>
144
144
  </template>
145
145
  <script>
146
- import TIcon from '../icon/icon';
147
- import { uniComponent } from '../common/src/index';
148
146
  import { prefix } from '../common/config';
149
- import props from './props';
147
+ import { RELATION_MAP } from '../common/relation/parent-map';
148
+ import { uniComponent } from '../common/src/index';
150
149
  import { getCharacterLength, calcIcon, coalesce, nextTick } from '../common/utils';
150
+ import tools from '../common/utils.wxs';
151
151
  import { isDef } from '../common/validator';
152
+ import TIcon from '../icon/icon';
153
+
152
154
  import { getInputClass } from './computed.js';
153
- import tools from '../common/utils.wxs';
154
- import { RELATION_MAP } from '../common/relation/parent-map';
155
+ import props from './props';
155
156
 
156
157
 
157
158
  const name = `${prefix}-input`;
@@ -344,8 +345,9 @@ export default {
344
345
  },
345
346
 
346
347
  clearInput(e) {
348
+ this.updateValue('');
349
+ this.emitChange({ value: this.dataValue });
347
350
  this.$emit('clear', e.detail);
348
- this.dataValue = '';
349
351
  },
350
352
 
351
353
  onKeyboardHeightChange(e) {
@@ -67,6 +67,11 @@
67
67
  :deep(.t-search__result-item--highLight) {
68
68
  color: var(--td-search-result-high-light-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9)));
69
69
  }
70
+ .t-search__result-list {
71
+ max-height: 560rpx;
72
+ overflow-y: auto;
73
+ -webkit-overflow-scrolling: touch;
74
+ }
70
75
  .t-search__result-list :deep(.t-search__result-item) {
71
76
  padding-left: 0;
72
77
  }
@@ -77,7 +77,7 @@
77
77
  />
78
78
  </view>
79
79
  <view
80
- v-if="isShowResultList && !isSelected"
80
+ v-if="isSearching && resultList.length > 0 && !isSelected"
81
81
  :class="classPrefix + '__result-list'"
82
82
  aria-role="listbox"
83
83
  >
@@ -142,8 +142,8 @@ export default {
142
142
  return {
143
143
  classPrefix: name,
144
144
  prefix,
145
- isShowResultList: false,
146
145
  isSelected: false,
146
+ isSearching: false,
147
147
  showClearIcon: false,
148
148
  tools,
149
149
 
@@ -156,16 +156,8 @@ export default {
156
156
  resultList: {
157
157
  handler(val) {
158
158
  const { isSelected } = this;
159
- if (val.length) {
160
- if (isSelected) {
161
- // 已选择
162
- this.isShowResultList = false;
163
- this.isSelected = false;
164
- } else {
165
- this.isShowResultList = true;
166
- }
167
- } else {
168
- this.isShowResultList = false;
159
+ if (val.length && isSelected) {
160
+ this.isSelected = false;
169
161
  }
170
162
  },
171
163
  immediate: true,
@@ -231,6 +223,7 @@ export default {
231
223
 
232
224
  onFocus(e) {
233
225
  const { value } = e.detail;
226
+ this.isSearching = true;
234
227
  this.updateClearIconVisible(true);
235
228
  this.$emit('focus', { value });
236
229
  },
@@ -243,6 +236,7 @@ export default {
243
236
 
244
237
  handleClear() {
245
238
  this.dataValue = '';
239
+ this.isSearching = false;
246
240
  this.$emit('clear', { value: '' });
247
241
  this.$emit('change', {
248
242
  value: '',
@@ -256,6 +250,7 @@ export default {
256
250
  },
257
251
 
258
252
  onActionClick() {
253
+ this.isSearching = false;
259
254
  this.$emit('action-click');
260
255
  },
261
256
 
@@ -0,0 +1,42 @@
1
+ :: BASE_DOC ::
2
+
3
+ ## API
4
+
5
+ ### Segmented Props
6
+
7
+ name | type | default | description | required
8
+ -- | -- | -- | -- | --
9
+ custom-style | Object | - | CSS(Cascading Style Sheets) | N
10
+ block | Boolean | false | \- | N
11
+ disabled | Boolean | - | \- | N
12
+ options | Object | [] | Typescript: `string[] \| number[] \| SegmentedItem[] ` `interface SegmentedItem { value: string \| number; label?: string; icon?: string \| object; disabled?: boolean }`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/segmented/type.ts) | N
13
+ value | String / Number | - | `v-model:value` is supported | N
14
+ default-value | String / Number | - | uncontrolled property | N
15
+
16
+ ### Segmented Events
17
+
18
+ name | params | description
19
+ -- | -- | --
20
+ change | `(context: { value: string \| number, selectedOption: SegmentedItem })` | \-
21
+
22
+ ### Segmented External Classes
23
+
24
+ className | Description
25
+ -- | --
26
+ t-class | \-
27
+ t-class-item | \-
28
+ t-class-thumb | \-
29
+
30
+ ### CSS Variables
31
+
32
+ The component provides the following CSS variables, which can be used to customize styles.
33
+ Name | Default Value | Description
34
+ -- | -- | --
35
+ --td-font-size-m | 32rpx | -
36
+ --td-segmented-bg-color | @bg-color-component-disabled | -
37
+ --td-segmented-item-active-bg | @bg-color-container | -
38
+ --td-segmented-item-active-color | @brand-color | -
39
+ --td-segmented-item-color | @text-color-primary | -
40
+ --td-segmented-item-disabled-color | @text-color-disabled | -
41
+ --td-segmented-item-label-font | @font-body-medium | -
42
+ --td-segmented-transition | all @anim-duration-base @anim-time-fn-easing | -
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: Segmented 分段控制器
3
+ description: 用于展示多个选项并允许用户选择其中单个选项。
4
+ spline: data
5
+ isComponent: true
6
+ ---
7
+
8
+
9
+ ## 引入
10
+
11
+ 推荐使用 easycom 模式引入组件,配置后无需手动 import 即可直接在模板中使用 `<t-segmented />`。详细配置请参考 [快速开始](../getting-started)。
12
+
13
+ 如需手动引入:
14
+
15
+ ```js
16
+ import TSegmented from '@tdesign/uniapp/segmented/segmented.vue';
17
+ ```
18
+
19
+ ### 组件类型
20
+
21
+ 基础
22
+
23
+ {{ base }}
24
+
25
+ 自适应宽度
26
+
27
+ {{ block }}
28
+
29
+ ### 组件状态
30
+
31
+ 控制器禁用态
32
+
33
+ {{ disabled }}
34
+
35
+
36
+ ## API
37
+
38
+ ### Segmented Props
39
+
40
+ 名称 | 类型 | 默认值 | 描述 | 必传
41
+ -- | -- | -- | -- | --
42
+ custom-style | Object | - | 自定义样式 | N
43
+ block | Boolean | false | 是否撑满父元素宽度 | N
44
+ disabled | Boolean | - | 是否禁用 | N
45
+ options | Object | [] | 数据化配置选项内容。TS 类型:`string[] \| number[] \| SegmentedItem[] ` `interface SegmentedItem { value: string \| number; label?: string; icon?: string \| object; disabled?: boolean }`。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/segmented/type.ts) | N
46
+ value | String / Number | - | 当前选中的值。支持语法糖 `v-model:value` | N
47
+ default-value | String / Number | - | 当前选中的值。非受控属性 | N
48
+
49
+ ### Segmented Events
50
+
51
+ 名称 | 参数 | 描述
52
+ -- | -- | --
53
+ change | `(context: { value: string \| number, selectedOption: SegmentedItem })` | 选项值发生变化时触发
54
+
55
+ ### Segmented External Classes
56
+
57
+ 类名 | 描述
58
+ -- | --
59
+ t-class | 根节点样式类
60
+ t-class-item | 列表子项样式类
61
+ t-class-thumb | 动画背景样式类
62
+
63
+ ### CSS Variables
64
+
65
+ 组件提供了下列 CSS 变量,可用于自定义样式。
66
+ 名称 | 默认值 | 描述
67
+ -- | -- | --
68
+ --td-font-size-m | 32rpx | -
69
+ --td-segmented-bg-color | @bg-color-component-disabled | -
70
+ --td-segmented-item-active-bg | @bg-color-container | -
71
+ --td-segmented-item-active-color | @brand-color | -
72
+ --td-segmented-item-color | @text-color-primary | -
73
+ --td-segmented-item-disabled-color | @text-color-disabled | -
74
+ --td-segmented-item-label-font | @font-body-medium | -
75
+ --td-segmented-transition | all @anim-duration-base @anim-time-fn-easing | -
@@ -0,0 +1,31 @@
1
+ /* eslint-disable */
2
+
3
+ /**
4
+ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
5
+ * */
6
+
7
+ import type { TdSegmentedProps } from './type';
8
+ export default {
9
+ /** 是否撑满父元素宽度 */
10
+ block: Boolean,
11
+ /** 是否禁用 */
12
+ disabled: Boolean,
13
+ /** 数据化配置选项内容 */
14
+ options: {
15
+ type: Object,
16
+ default: (): TdSegmentedProps['options'] => [],
17
+ },
18
+ /** 当前选中的值 */
19
+ value: {
20
+ type: [String, Number],
21
+ },
22
+ /** 当前选中的值,非受控属性 */
23
+ defaultValue: {
24
+ type: [String, Number],
25
+ },
26
+ /** 选项值发生变化时触发 */
27
+ onChange: {
28
+ type: Function,
29
+ default: () => ({}),
30
+ },
31
+ };
@@ -0,0 +1,66 @@
1
+ .t-segmented {
2
+ display: inline-block;
3
+ padding: calc(var(--td-spacer, 16rpx) / 4);
4
+ background-color: var(--td-segmented-bg-color, var(--td-bg-color-component-disabled, var(--td-gray-color-2, #eeeeee)));
5
+ border-radius: calc(var(--td-spacer, 16rpx) / 2);
6
+ }
7
+ .t-segmented--block {
8
+ display: flex;
9
+ width: 100%;
10
+ }
11
+ .t-segmented--block .t-segmented__item {
12
+ flex: 1;
13
+ min-width: 0;
14
+ }
15
+ .t-segmented--block .t-segmented__item-inner {
16
+ overflow: hidden;
17
+ text-overflow: ellipsis;
18
+ }
19
+ .t-segmented__group {
20
+ position: relative;
21
+ display: flex;
22
+ align-items: stretch;
23
+ justify-items: flex-start;
24
+ width: 100%;
25
+ }
26
+ .t-segmented__thumb {
27
+ position: absolute;
28
+ top: 0;
29
+ left: 0;
30
+ height: 100%;
31
+ background-color: var(--td-segmented-item-active-bg, var(--td-bg-color-container, var(--td-font-white-1, #ffffff)));
32
+ border-radius: calc(var(--td-spacer, 16rpx) / 2);
33
+ transition: var(--td-segmented-transition, all var(--td-anim-duration-base, 0.2s) var(--td-anim-time-fn-easing, cubic-bezier(0.38, 0, 0.24, 1)));
34
+ will-change: transform, width;
35
+ }
36
+ .t-segmented__item {
37
+ position: relative;
38
+ z-index: 1;
39
+ flex: none;
40
+ padding: calc(var(--td-spacer-1, 24rpx) / 4) var(--td-spacer-1, 24rpx);
41
+ text-align: center;
42
+ cursor: pointer;
43
+ font: var(--td-segmented-item-label-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)));
44
+ color: var(--td-segmented-item-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))));
45
+ }
46
+ .t-segmented__item-inner {
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ white-space: nowrap;
51
+ }
52
+ :deep(.t-segmented__item-icon:not(:empty)) {
53
+ margin-right: calc(var(--td-spacer, 16rpx) / 2);
54
+ }
55
+ .t-segmented__item-icon {
56
+ font-size: var(--td-font-size-m, 32rpx);
57
+ }
58
+ .t-segmented__item--active {
59
+ font-weight: 600;
60
+ color: var(--td-segmented-item-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9)));
61
+ }
62
+ .t-segmented__item--disabled {
63
+ cursor: not-allowed;
64
+ pointer-events: none;
65
+ color: var(--td-segmented-item-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26))));
66
+ }
@@ -0,0 +1,174 @@
1
+ <template>
2
+ <view
3
+ :class="'' + tools.cls(classPrefix, [['block', block]]) + ' ' + tClass"
4
+ :style="'' + tools._style([customStyle])"
5
+ >
6
+ <view :class="classPrefix + '__group'">
7
+ <!-- thumb 动画背景 -->
8
+ <view
9
+ :class="classPrefix + '__thumb ' + tClassThumb"
10
+ :style="thumbStyle"
11
+ />
12
+
13
+ <!-- 选项列表 -->
14
+ <view
15
+ v-for="(item, index) in segmentItems"
16
+ :key="index"
17
+ :class="'' + tools.cls(classPrefix + '__item', [
18
+ ['active', index === activeIndex],
19
+ ['disabled', disabled || item.disabled]
20
+ ]) + ' ' + classPrefix + '-item-' + index + ' ' + tClassItem"
21
+ @click="handleSelect(index)"
22
+ >
23
+ <view :class="classPrefix + '__item-inner'">
24
+ <block
25
+ v-if="item.icon"
26
+ name="icon"
27
+ >
28
+ <t-icon
29
+ :name="item.icon.name || item.icon"
30
+ :t-class="classPrefix + '__item-icon'"
31
+ />
32
+ </block>
33
+ <span
34
+ v-if="item.label"
35
+ :class="classPrefix + '__item-label'"
36
+ >{{ item.label }}</span>
37
+ </view>
38
+ </view>
39
+ </view>
40
+ </view>
41
+ </template>
42
+ <script>
43
+ import TIcon from '../icon/icon';
44
+ import { uniComponent } from '../common/src/index';
45
+ import { prefix } from '../common/config';
46
+ import props from './props';
47
+ import { calcIcon, getRect, coalesce } from '../common/utils';
48
+ import tools from '../common/utils.wxs';
49
+
50
+ const name = `${prefix}-segmented`;
51
+
52
+ export default {
53
+ components: {
54
+ TIcon,
55
+ },
56
+ ...uniComponent({
57
+ name,
58
+ options: {
59
+ styleIsolation: 'shared',
60
+ },
61
+ controlledProps: [
62
+ {
63
+ key: 'value',
64
+ event: 'change',
65
+ },
66
+ ],
67
+ externalClasses: [
68
+ `${prefix}-class`,
69
+ `${prefix}-class-thumb`,
70
+ `${prefix}-class-item`,
71
+ ],
72
+ props: {
73
+ ...props,
74
+ },
75
+ emits: ['change'],
76
+ data() {
77
+ return {
78
+ prefix,
79
+ classPrefix: name,
80
+ tools,
81
+ segmentItems: [],
82
+ activeIndex: -1,
83
+ thumbStyle: '',
84
+
85
+ dataValue: coalesce(this.value, this.defaultValue),
86
+ };
87
+ },
88
+ watch: {
89
+ options: {
90
+ handler(newOptions) {
91
+ this.updateOptions(newOptions);
92
+ },
93
+ immediate: true,
94
+ },
95
+ value: {
96
+ handler(val) {
97
+ this.dataValue = val;
98
+ },
99
+ },
100
+ dataValue: {
101
+ handler() {
102
+ this.updateActiveIndex();
103
+ },
104
+ immediate: true,
105
+ },
106
+ segmentItems() {
107
+ this.updateActiveIndex();
108
+ },
109
+ },
110
+ mounted() {
111
+ this.$nextTick(() => {
112
+ this.updateThumb();
113
+ });
114
+ },
115
+ methods: {
116
+ updateOptions(options) {
117
+ if (!options?.length) return;
118
+ const segmentItems = options.map((option) => {
119
+ if (typeof option === 'string' || typeof option === 'number') {
120
+ return { value: option, label: String(option) };
121
+ }
122
+ return {
123
+ ...option,
124
+ label: option.label ?? String(option.value),
125
+ icon: option.icon ? calcIcon(option.icon) : null,
126
+ };
127
+ });
128
+ this.segmentItems = segmentItems;
129
+ },
130
+
131
+ updateActiveIndex() {
132
+ const { dataValue, segmentItems } = this;
133
+ let activeIndex = -1;
134
+ if (dataValue != null) {
135
+ activeIndex = segmentItems.findIndex(item => item.value === dataValue);
136
+ }
137
+ if (activeIndex === this.activeIndex) return;
138
+ this.activeIndex = activeIndex;
139
+ this.$nextTick(() => {
140
+ this.updateThumb();
141
+ });
142
+ },
143
+
144
+ updateThumb() {
145
+ const { activeIndex, classPrefix } = this;
146
+ if (activeIndex < 0) return;
147
+ const groupClass = `.${classPrefix}__group`;
148
+ const itemClass = `.${classPrefix}-item-${activeIndex}`;
149
+ Promise.all([
150
+ getRect(this, itemClass),
151
+ getRect(this, groupClass),
152
+ ]).then(([itemRect, groupRect]) => {
153
+ if (itemRect && groupRect) {
154
+ const left = itemRect.left - groupRect.left;
155
+ this.thumbStyle = `width: ${itemRect.width}px; transform: translateX(${left}px);`;
156
+ }
157
+ });
158
+ },
159
+
160
+ handleSelect(index) {
161
+ const { segmentItems, activeIndex, disabled, options } = this;
162
+ const item = segmentItems[index];
163
+ if (disabled || !item || item.disabled) return;
164
+ if (index === activeIndex) return;
165
+ this._trigger('change', {
166
+ value: item.value,
167
+ selectedOption: options[index],
168
+ });
169
+ },
170
+ },
171
+ }),
172
+ };
173
+ </script>
174
+ <style scoped src="./segmented.css"></style>
@@ -0,0 +1,41 @@
1
+ /* eslint-disable */
2
+
3
+ /**
4
+ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
5
+ * */
6
+
7
+ export interface TdSegmentedProps {
8
+ /**
9
+ * 是否撑满父元素宽度
10
+ * @default false
11
+ */
12
+ block?: boolean;
13
+ /**
14
+ * 是否禁用
15
+ */
16
+ disabled?: boolean;
17
+ /**
18
+ * 数据化配置选项内容
19
+ * @default []
20
+ */
21
+ options?: string[] | number[] | SegmentedItem[];
22
+ /**
23
+ * 当前选中的值
24
+ */
25
+ value?: string | number;
26
+ /**
27
+ * 当前选中的值,非受控属性
28
+ */
29
+ defaultValue?: string | number;
30
+ /**
31
+ * 选项值发生变化时触发
32
+ */
33
+ onChange?: (context: { value: string | number; selectedOption: SegmentedItem }) => void;
34
+ }
35
+
36
+ export interface SegmentedItem {
37
+ value: string | number;
38
+ label?: string;
39
+ icon?: string | object;
40
+ disabled?: boolean;
41
+ }