diff --git a/example/app.json b/example/app.json index 86326f11e..07c9872da 100644 --- a/example/app.json +++ b/example/app.json @@ -13,6 +13,7 @@ "pages/rate/rate", "pages/progress/progress", "pages/calendar/calendar", + "pages/cascader/cascader", "pages/cell/cell", "pages/cell-group/cell-group", "pages/collapse/collapse", diff --git a/example/pages/home/data/form.ts b/example/pages/home/data/form.ts index f5f0182a7..5711e4a6b 100644 --- a/example/pages/home/data/form.ts +++ b/example/pages/home/data/form.ts @@ -6,6 +6,10 @@ const form = { name: 'Calendar', label: '日历', }, + { + name: 'Cascader', + label: '级联选择器', + }, { name: 'Checkbox', label: '多选框', diff --git a/site/site.config.mjs b/site/site.config.mjs index 4ef818fd5..586717f69 100644 --- a/site/site.config.mjs +++ b/site/site.config.mjs @@ -168,6 +168,13 @@ export default { path: '/miniprogram/components/calendar', component: () => import('@/calendar/README.md'), }, + { + title: 'Cascader 级联选择器', + name: 'cascader', + meta: { docType: 'form' }, + path: '/miniprogram/components/cascader', + component: () => import('@/cascader/README.md'), + }, { title: 'CheckBox 复选框', name: 'checkbox', diff --git a/src/cascader/README.en-US.md b/src/cascader/README.en-US.md new file mode 100644 index 000000000..924e97e8e --- /dev/null +++ b/src/cascader/README.en-US.md @@ -0,0 +1,13 @@ +:: BASE_DOC :: + +## API +### Cascader Props + +name | type | default | description | required +-- | -- | -- | -- | -- +close-btn | Boolean / Slot | true | \- | N +options | Array | [] | Typescript:`Array` | N +title | String / Slot | - | \- | N +value | String / Number | - | \- | N +default-value | String / Number | undefined | uncontrolled property | N +visible | Boolean | false | \- | N diff --git a/src/cascader/README.md b/src/cascader/README.md new file mode 100644 index 000000000..45d32b49d --- /dev/null +++ b/src/cascader/README.md @@ -0,0 +1,34 @@ +--- +title: Cascader 级联选择器 +description: 级联选择器适用于有清晰层级结构的数据集合,用户可以通过逐级查看并选择。 +spline: form +isComponent: true +--- + +## 引入 + +全局引入,在 miniprogram 根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。 + +```json +"usingComponents": { + "t-calendar": "tdesign-miniprogram/cascader/cascader" +} +``` + +## 代码演示 + +### 基础用法 + +{{ base }} + +## API +### Cascader Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +close-btn | Boolean / Slot | true | 关闭按钮 | N +options | Array | [] | 可选项数据源。TS 类型:`Array` | N +title | String / Slot | - | 标题 | N +value | String / Number | - | 选项值 | N +default-value | String / Number | undefined | 选项值。非受控属性 | N +visible | Boolean | false | 是否展示 | N diff --git a/src/cascader/_example/base/data.js b/src/cascader/_example/base/data.js new file mode 100644 index 000000000..26c1d1ee4 --- /dev/null +++ b/src/cascader/_example/base/data.js @@ -0,0 +1,60 @@ +export default { + areaList: [ + { + label: '北京市', + value: '110000', + children: [ + { + value: '110100', + label: '北京市', + children: [ + { value: '110101', label: '东城区' }, + { value: '110102', label: '西城区' }, + { value: '110105', label: '朝阳区' }, + { value: '110106', label: '丰台区' }, + { value: '110107', label: '石景山区' }, + { value: '110108', label: '海淀区' }, + { value: '110109', label: '门头沟区' }, + { value: '110111', label: '房山区' }, + { value: '110112', label: '通州区' }, + { value: '110113', label: '顺义区' }, + { value: '110114', label: '昌平区' }, + { value: '110115', label: '大兴区' }, + { value: '110116', label: '怀柔区' }, + { value: '110117', label: '平谷区' }, + { value: '110118', label: '密云区' }, + { value: '110119', label: '延庆区' }, + ], + }, + ], + }, + { + label: '天津市', + value: '120000', + children: [ + { + value: '120100', + label: '天津市', + children: [ + { value: '120101', label: '和平区' }, + { value: '120102', label: '河东区' }, + { value: '120103', label: '河西区' }, + { value: '120104', label: '南开区' }, + { value: '120105', label: '河北区' }, + { value: '120106', label: '红桥区' }, + { value: '120110', label: '东丽区' }, + { value: '120111', label: '西青区' }, + { value: '120112', label: '津南区' }, + { value: '120113', label: '北辰区' }, + { value: '120114', label: '武清区' }, + { value: '120115', label: '宝坻区' }, + { value: '120116', label: '滨海新区' }, + { value: '120117', label: '宁河区' }, + { value: '120118', label: '静海区' }, + { value: '120119', label: '蓟州区' }, + ], + }, + ], + }, + ], +}; diff --git a/src/cascader/_example/base/index.js b/src/cascader/_example/base/index.js new file mode 100644 index 000000000..d4ee8ebc0 --- /dev/null +++ b/src/cascader/_example/base/index.js @@ -0,0 +1,21 @@ +import data from './data'; + +Component({ + data: { + options: data.areaList, + note: '请选择地址', + visible: false, + }, + methods: { + showCascader() { + this.setData({ visible: true }); + }, + onChange(e) { + const { selectedOptions } = e.detail; + + this.setData({ + note: selectedOptions.map((item) => item.label).join('/'), + }); + }, + }, +}); diff --git a/src/cascader/_example/base/index.json b/src/cascader/_example/base/index.json new file mode 100644 index 000000000..64b003122 --- /dev/null +++ b/src/cascader/_example/base/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cascader": "tdesign-miniprogram/cascader/cascader" + } +} diff --git a/src/cascader/_example/base/index.wxml b/src/cascader/_example/base/index.wxml new file mode 100644 index 000000000..3380c00c6 --- /dev/null +++ b/src/cascader/_example/base/index.wxml @@ -0,0 +1,3 @@ + + + diff --git a/src/cascader/_example/base/index.wxss b/src/cascader/_example/base/index.wxss new file mode 100644 index 000000000..e69de29bb diff --git a/src/cascader/_example/cascader.js b/src/cascader/_example/cascader.js new file mode 100644 index 000000000..b79c5124b --- /dev/null +++ b/src/cascader/_example/cascader.js @@ -0,0 +1 @@ +Component({}); diff --git a/src/cascader/_example/cascader.json b/src/cascader/_example/cascader.json new file mode 100644 index 000000000..b51c15b0d --- /dev/null +++ b/src/cascader/_example/cascader.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "base": "./base" + } +} diff --git a/src/cascader/_example/cascader.wxml b/src/cascader/_example/cascader.wxml new file mode 100644 index 000000000..854216109 --- /dev/null +++ b/src/cascader/_example/cascader.wxml @@ -0,0 +1,8 @@ + + Cascader 级联选择器 + 级联选择器适用于有清晰层级结构的数据集合,用户可以通过逐级查看并选择。 + + + + + diff --git a/src/cascader/_example/cascader.wxss b/src/cascader/_example/cascader.wxss new file mode 100644 index 000000000..e69de29bb diff --git a/src/cascader/cascader.json b/src/cascader/cascader.json new file mode 100644 index 000000000..b4d79de06 --- /dev/null +++ b/src/cascader/cascader.json @@ -0,0 +1,11 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popup": "tdesign-miniprogram/popup/popup", + "t-radio": "tdesign-miniprogram/radio/radio", + "t-radio-group": "tdesign-miniprogram/radio-group/radio-group", + "t-swiper": "tdesign-miniprogram/swiper/swiper", + "t-swiper-item": "tdesign-miniprogram/swiper/swiper-item" + } +} diff --git a/src/cascader/cascader.less b/src/cascader/cascader.less new file mode 100644 index 000000000..61204c497 --- /dev/null +++ b/src/cascader/cascader.less @@ -0,0 +1,163 @@ +@import '../common/style/index.less'; + +@cascader-active-color: @primary-color; +@cascader-disabled-color: @text-disabled-color; +@cascader-title-color: @text-level-1-color; +@cascader-options-title-color: @text-level-3-color; +@cascader-border-color: @border-level-1-color; + +@cascader-options-height: 320px; + +// steps +@cascader-step-height: 44px; +@cascader-step-active-color: @primary-color; +@cascader-step-dot-size: 8px; +@cascader-step-dot-color: @text-level-4-color; +@cascader-step-line-color: @text-level-4-color; + +.@{prefix}-cascader { + display: flex; + flex-direction: column; + background-color: #fff; + color: @cascader-title-color; + + &__close-btn { + right: 16px; + top: 12px; + position: absolute; + } + + &__title { + position: relative; + font-weight: 700; + text-align: center; + line-height: 48px; + font-size: 16px; + } + + &__content { + width: 100%; + flex: 1; + display: flex; + flex-direction: column; + } + + &__options { + height: @cascader-options-height; + + &-title { + color: @cascader-options-title-color; + font-size: 14px; + line-height: 40px; + padding-left: 16px; + } + + &-content { + flex: 1; + height: 100%; + overflow: auto; + padding-left: 16px; + } + + &-container { + display: flex; + transition: all ease 0.3s; + } + } + + &__step { + display: flex; + align-items: center; + height: @cascader-step-height; + + &s { + padding: 0 16px; + border-bottom: 1px solid @cascader-border-color; + } + + &-dot { + position: relative; + width: @cascader-step-dot-size; + height: @cascader-step-dot-size; + border-radius: 50%; + border: 1px solid @cascader-step-dot-color; + box-sizing: border-box; + + &:not(.t-cascader__step-dot--last)::after { + content: ''; + display: block; + position: absolute; + left: 50%; + top: @cascader-step-dot-size; + height: calc(@cascader-step-height - @cascader-step-dot-size); + width: 0; + border-left: 1px dashed @cascader-step-line-color; + transform: translateX(-50%); + } + + &--active { + background: @cascader-step-active-color; + border-color: @cascader-step-active-color; + } + } + + &-label { + padding-left: 16px; + font-size: 16px; + + &--active { + color: @cascader-step-active-color; + } + } + } + + &__swiper { + height: @cascader-options-height; + } +} + +.@{prefix}-cascader-item { + font-size: 16px; + font-weight: 400; + text-align: left; + cursor: pointer; + display: flex; + flex-wrap: nowrap; + flex-direction: row; + align-items: center; + width: 100%; + justify-content: space-between; + line-height: 48px; + + &-content { + flex-grow: 1; + } + + &.active { + color: @cascader-active-color; + } + + &.disabled { + color: @cascader-disabled-color; + cursor: not-allowed; + } +} + +.slide-enter-active, +.slide-leave-active { + transition: all 0.2s ease-in-out; + position: absolute; +} + +.slide-leave-to { + transform: translateX(-100%); +} + +.slide-enter-from { + transform: translateX(100%); +} + +.slide-enter-from, +.slide-leave-to { + opacity: 0; +} diff --git a/src/cascader/cascader.ts b/src/cascader/cascader.ts new file mode 100644 index 000000000..cea9e0a88 --- /dev/null +++ b/src/cascader/cascader.ts @@ -0,0 +1,97 @@ +import { SuperComponent, wxComponent } from '../common/src/index'; +import config from '../common/config'; +import props from './props'; +import { TdCascaderProps } from './type'; + +const { prefix } = config; +const name = `${prefix}-cascader`; + +export interface CascaderProps extends TdCascaderProps {} + +@wxComponent() +export default class Cascader extends SuperComponent { + externalClasses = [`${prefix}-class`]; + + properties = props; + + data = { + prefix, + name, + stepIndex: 0, + selectedIndexes: [], + selectedValue: [], + steps: ['选择选项'], + }; + + observers = { + options() { + this.setData({ + items: [this.data.options], + }); + }, + selectedIndexes() { + const { items, selectedIndexes } = this.data; + + this.setData({ + selectedValue: items.map((item, index) => item[selectedIndexes[index]]?.value), + }); + }, + }; + + lifetimes = { + ready() {}, + }; + + methods = { + hide() { + this.setData({ visible: false }); + }, + onStepClick(e) { + const { index } = e.currentTarget.dataset; + + this.setData({ stepIndex: index }); + }, + onSwiperChange(e) { + const { current } = e.detail; + + this.setData({ + stepIndex: current, + }); + }, + handleSelect(e) { + const { level } = e.target.dataset; + const { value } = e.detail; + const { selectedIndexes, steps, items, stepIndex } = this.data; + const index = items[level].findIndex((item) => item.value === value); + const item = items[level][index]; + + if (item.disabled) { + return; + } + selectedIndexes[level] = index; + selectedIndexes.length = level + 1; + steps[level] = item.label; + + this.triggerEvent('pick', item.value, index); + + if (item?.children?.length) { + items[level + 1] = item.children; + items.length = level + 2; + steps[level + 1] = '选择选项'; + steps.length = level + 2; + + this.setData({ steps, items, selectedIndexes }, () => { + this.setData({ stepIndex: stepIndex + 1 }); + }); + } else { + // setCascaderValue(item.value); + this.setData({ steps, selectedIndexes }); + this.hide(); + this.triggerEvent('change', { + value: item.value, + selectedOptions: items.map((item, index) => item[selectedIndexes[index]]), + }); + } + }, + }; +} diff --git a/src/cascader/cascader.wxml b/src/cascader/cascader.wxml new file mode 100644 index 000000000..17c90e606 --- /dev/null +++ b/src/cascader/cascader.wxml @@ -0,0 +1,60 @@ + + + + + {{title}} + + + + + + + + + + + + {{ item }} + + + + + + + + + + + + + + + + diff --git a/src/cascader/props.ts b/src/cascader/props.ts new file mode 100644 index 000000000..dbd14345e --- /dev/null +++ b/src/cascader/props.ts @@ -0,0 +1,41 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdCascaderProps } from './type'; +const props: TdCascaderProps = { + /** 关闭按钮 */ + closeBtn: { + type: Boolean, + value: true, + }, + /** 可选项数据源 */ + options: { + type: Array, + value: [], + }, + /** 标题 */ + title: { + type: String, + }, + /** 选项值 */ + value: { + type: String, + optionalTypes: [Number], + value: null, + }, + /** 选项值,非受控属性 */ + defaultValue: { + type: String, + optionalTypes: [Number], + }, + /** 是否展示 */ + visible: { + type: Boolean, + value: false, + }, +}; + +export default props; diff --git a/src/cascader/type.ts b/src/cascader/type.ts new file mode 100644 index 000000000..51561d931 --- /dev/null +++ b/src/cascader/type.ts @@ -0,0 +1,57 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TreeOptionData } from '../common/common'; + +export interface TdCascaderProps { + /** + * 关闭按钮 + * @default true + */ + closeBtn?: { + type: BooleanConstructor; + value?: boolean; + }; + /** + * 可选项数据源 + * @default [] + */ + options?: { + type: ArrayConstructor; + value?: Array; + }; + /** + * 标题 + */ + title?: { + type: StringConstructor; + value?: string; + }; + /** + * 选项值 + */ + value?: { + type: StringConstructor; + optionalTypes: Array; + value?: string | number; + }; + /** + * 选项值,非受控属性 + */ + defaultValue?: { + type: StringConstructor; + optionalTypes: Array; + value?: string | number; + }; + /** + * 是否展示 + * @default false + */ + visible?: { + type: BooleanConstructor; + value?: boolean; + }; +} diff --git a/src/radio/radio-group-props.ts b/src/radio-group/props.ts similarity index 61% rename from src/radio/radio-group-props.ts rename to src/radio-group/props.ts index 079d28a4e..4b29f8c5e 100644 --- a/src/radio/radio-group-props.ts +++ b/src/radio-group/props.ts @@ -6,11 +6,21 @@ import { TdRadioGroupProps } from './type'; const props: TdRadioGroupProps = { + /** 复选框和内容相对位置;仅在使用 options 时生效 */ + align: { + type: String, + value: 'left', + }, /** 是否禁用全部子单选框 */ disabled: { type: Boolean, value: undefined, }, + /** 自定义选中图标和非选中图标。示例:[选中态图标,非选中态图标]。值为 fill-circle 表示图标为填充型图标,值为 stroke-line 表示图标为描边型图标;仅在使用 options 时生效 */ + icon: { + type: null, + value: 'fill-circle', + }, /** HTML 元素原生属性 */ name: { type: String, @@ -22,15 +32,12 @@ const props: TdRadioGroupProps = { }, /** 选中的值 */ value: { - type: String, - optionalTypes: [Number, Boolean], + type: null, value: null, }, /** 选中的值,非受控属性 */ defaultValue: { - type: String, - optionalTypes: [Number, Boolean], - value: false, + type: null, }, }; diff --git a/src/radio-group/radio-group.ts b/src/radio-group/radio-group.ts index e980ad8a6..271fbb4d3 100644 --- a/src/radio-group/radio-group.ts +++ b/src/radio-group/radio-group.ts @@ -1,6 +1,6 @@ import config from '../common/config'; import { SuperComponent, wxComponent, RelationsOptions } from '../common/src/index'; -import Props from '../radio/radio-group-props'; +import Props from './props'; const { prefix } = config; const name = `${prefix}-radio-group`; @@ -30,6 +30,10 @@ export default class RadioGroup extends SuperComponent { properties = { ...Props, + borderless: { + type: Boolean, + value: false, + }, }; controlledProps = [ @@ -39,12 +43,6 @@ export default class RadioGroup extends SuperComponent { }, ]; - lifetimes = { - attached() { - this.initWithOptions(); - }, - }; - observers = { value() { this.getChilds().forEach((item) => { @@ -53,6 +51,9 @@ export default class RadioGroup extends SuperComponent { }); }); }, + options() { + this.initWithOptions(); + }, }; methods = { @@ -69,9 +70,9 @@ export default class RadioGroup extends SuperComponent { }, handleRadioChange(e) { - const { value } = e.target.dataset; + const { value, index } = e.target.dataset; - this.updateValue(value); + this._trigger('change', { value, index }); }, // 支持自定义options diff --git a/src/radio-group/radio-group.wxml b/src/radio-group/radio-group.wxml index 3df23bda3..749a04195 100644 --- a/src/radio-group/radio-group.wxml +++ b/src/radio-group/radio-group.wxml @@ -5,8 +5,12 @@ class="{{prefix}}-radio-option" label="{{item.label}}" value="{{item.value}}" + data-index="{{index}}" data-value="{{item.value}}" disabled="{{item.disabled}}" + align="{{align}}" + icon="{{icon}}" + borderless="{{borderless}}" bind:change="handleRadioChange" /> diff --git a/src/radio-group/type.ts b/src/radio-group/type.ts new file mode 100644 index 000000000..21b1ec707 --- /dev/null +++ b/src/radio-group/type.ts @@ -0,0 +1,69 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ +import { RadioValue } from '../radio/type'; + +export interface TdRadioGroupProps { + /** + * 复选框和内容相对位置;仅在使用 options 时生效 + * @default left + */ + align?: { + type: StringConstructor; + value?: 'left' | 'right'; + }; + /** + * 是否禁用全部子单选框 + */ + disabled?: { + type: BooleanConstructor; + value?: boolean; + }; + /** + * 自定义选中图标和非选中图标。示例:[选中态图标,非选中态图标]。值为 fill-circle 表示图标为填充型图标,值为 stroke-line 表示图标为描边型图标;仅在使用 options 时生效 + * @default 'fill-circle' + */ + icon?: { + type: null; + value?: 'fill-circle' | 'stroke-line' | Array; + }; + /** + * HTML 元素原生属性 + * @default '' + */ + name?: { + type: StringConstructor; + value?: string; + }; + /** + * 单选组件按钮形式。RadioOption 数据类型为 string 或 number 时,表示 label 和 value 值相同 + */ + options?: { + type: ArrayConstructor; + value?: Array; + }; + /** + * 选中的值 + */ + value?: { + type: null; + value?: T; + }; + /** + * 选中的值,非受控属性 + */ + defaultValue?: { + type: null; + value?: T; + }; +} + +export type RadioOption = string | number | RadioOptionObj; + +export interface RadioOptionObj { + label?: string; + value?: string | number; + disabled?: boolean; +} diff --git a/src/radio/README.en-US.md b/src/radio/README.en-US.md new file mode 100644 index 000000000..a5f19eb72 --- /dev/null +++ b/src/radio/README.en-US.md @@ -0,0 +1,46 @@ +:: BASE_DOC :: + +## API +### Radio Props + +name | type | default | description | required +-- | -- | -- | -- | -- +align | String | left | options:left/right | N +allow-uncheck | Boolean | false | \- | N +checked | Boolean | false | \- | N +default-checked | Boolean | undefined | uncontrolled property | N +color | String | #0052d9 | \- | N +content | String / Slot | - | \- | N +content-disabled | Boolean | - | \- | N +disabled | Boolean | undefined | \- | N +external-classes | Array | - | `['t-class', 't-class-icon', 't-class-label', 't-class-content', 't-class-border']` | N +icon | String / Array | 'fill-circle' | Typescript:`'fill-circle' | 'stroke-line' | Array` | N +label | String / Slot | - | \- | N +max-content-row | Number | 5 | \- | N +max-label-row | Number | 3 | \- | N +name | String | - | \- | N +value | String / Number / Boolean | false | Typescript:`T` | N + +### Radio Events + +name | params | description +-- | -- | -- +change | `(checked: boolean)` | \- + +### RadioGroup Props + +name | type | default | description | required +-- | -- | -- | -- | -- +align | String | left | options:left/right | N +disabled | Boolean | undefined | \- | N +icon | String / Array | 'fill-circle' | Typescript:`'fill-circle' | 'stroke-line' | Array` | N +name | String | - | \- | N +options | Array | - | Typescript:`Array` `type RadioOption = string | number | RadioOptionObj` `interface RadioOptionObj { label?: string; value?: string | number; disabled?: boolean }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/radio-group/type.ts) | N +value | String / Number / Boolean | - | Typescript:`T` | N +default-value | String / Number / Boolean | undefined | uncontrolled property。Typescript:`T` | N + +### RadioGroup Events + +name | params | description +-- | -- | -- +change | `(value: T)` | \- diff --git a/src/radio/README.md b/src/radio/README.md index 62d1cd128..4045bed4f 100644 --- a/src/radio/README.md +++ b/src/radio/README.md @@ -95,7 +95,9 @@ change | `(checked: boolean)` | 值变化时触发 名称 | 类型 | 默认值 | 说明 | 必传 -- | -- | -- | -- | -- +align | String | left | 复选框和内容相对位置;仅在使用 options 时生效。可选项:left/right | N disabled | Boolean | undefined | 是否禁用全部子单选框 | N +icon | String / Array | 'fill-circle' | 自定义选中图标和非选中图标。示例:[选中态图标,非选中态图标]。值为 fill-circle 表示图标为填充型图标,值为 stroke-line 表示图标为描边型图标;仅在使用 options 时生效。TS 类型:`'fill-circle' | 'stroke-line' | Array` | N name | String | - | HTML 元素原生属性 | N options | Array | - | 单选组件按钮形式。RadioOption 数据类型为 string 或 number 时,表示 label 和 value 值相同。TS 类型:`Array` `type RadioOption = string | number | RadioOptionObj` `interface RadioOptionObj { label?: string; value?: string | number; disabled?: boolean }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/radio/type.ts) | N value | String / Number / Boolean | false | 选中的值。TS 类型:`RadioValue` | N diff --git a/src/radio/props.ts b/src/radio/props.ts index 59805fefb..1ce14b12e 100644 --- a/src/radio/props.ts +++ b/src/radio/props.ts @@ -51,8 +51,7 @@ const props: TdRadioProps = { }, /** 自定义选中图标和非选中图标。示例:[选中态图标,非选中态图标]。值为 fill-circle 表示图标为填充型图标,值为 stroke-line 表示图标为描边型图标 */ icon: { - type: String, - optionalTypes: [Array], + type: null, value: 'fill-circle', }, /** 主文案 */ @@ -76,8 +75,7 @@ const props: TdRadioProps = { }, /** 单选按钮的值 */ value: { - type: String, - optionalTypes: [Number, Boolean], + type: null, value: false, }, }; diff --git a/src/radio/radio.ts b/src/radio/radio.ts index f2c06a47b..26e7f2ab9 100644 --- a/src/radio/radio.ts +++ b/src/radio/radio.ts @@ -21,9 +21,17 @@ export default class Radio extends SuperComponent { behaviors = ['wx://form-field']; + parent = null; + relations: RelationsOptions = { '../radio-group/radio-group': { type: 'ancestor', + linked(parent) { + this.parent = parent; + if (parent.align) { + this.setData({ align: parent.align }); + } + }, }, }; @@ -91,7 +99,7 @@ export default class Radio extends SuperComponent { }, initStatus() { const { icon } = this.data; - const isIdArr = Array.isArray(icon); + const isIdArr = Array.isArray(this.parent?.icon || icon); this.setData({ customIcon: isIdArr, diff --git a/src/radio/type.ts b/src/radio/type.ts index f2732ea68..1e48cf87c 100644 --- a/src/radio/type.ts +++ b/src/radio/type.ts @@ -4,7 +4,7 @@ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC * */ -export interface TdRadioProps { +export interface TdRadioProps { /** * 复选框和内容相对位置 * @default left @@ -79,8 +79,7 @@ export interface TdRadioProps { * @default 'fill-circle' */ icon?: { - type: StringConstructor; - optionalTypes: Array; + type: null; value?: 'fill-circle' | 'stroke-line' | Array; }; /** @@ -119,61 +118,9 @@ export interface TdRadioProps { * @default false */ value?: { - type: StringConstructor; - optionalTypes: Array; - value?: RadioValue; - }; -} - -export interface TdRadioGroupProps { - /** - * 是否禁用全部子单选框 - */ - disabled?: { - type: BooleanConstructor; - value?: boolean; - }; - /** - * HTML 元素原生属性 - * @default '' - */ - name?: { - type: StringConstructor; - value?: string; - }; - /** - * 单选组件按钮形式。RadioOption 数据类型为 string 或 number 时,表示 label 和 value 值相同 - */ - options?: { - type: ArrayConstructor; - value?: Array; - }; - /** - * 选中的值 - * @default false - */ - value?: { - type: StringConstructor; - optionalTypes: Array; - value?: RadioValue; - }; - /** - * 选中的值,非受控属性 - * @default false - */ - defaultValue?: { - type: StringConstructor; - optionalTypes: Array; - value?: RadioValue; + type: null; + value?: T; }; } export type RadioValue = string | number | boolean; - -export type RadioOption = string | number | RadioOptionObj; - -export interface RadioOptionObj { - label?: string; - value?: string | number; - disabled?: boolean; -} diff --git a/src/swiper/swiper.ts b/src/swiper/swiper.ts index ef27347f6..981541543 100644 --- a/src/swiper/swiper.ts +++ b/src/swiper/swiper.ts @@ -149,20 +149,29 @@ export default class Swiper extends SuperComponent { } ready() { - this.createSelectorQuery() - .select('#swiper') - .boundingClientRect((rect) => { - this.setData({ - _width: rect.width, - _height: rect.height, - }); - this.initItem(); - this.initNav(); - this.initCurrent(); - }) - .exec(); + this.init(); } + methods = { + init() { + if (this.hasInited) return; + this.createSelectorQuery() + .select('#swiper') + .boundingClientRect((rect) => { + if (rect.width === 0) return; // 在 wx:if == false 时也会执行 ready,导致无法获取 size + this.hasInited = true; + this.setData({ + _width: rect.width, + _height: rect.height, + }); + this.initItem(); + this.initNav(); + this.initCurrent(); + }) + .exec(); + }, + }; + /** * 初始化 swiper-item */