mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-24 10:20:19 +08:00
feat(Cascader): add field-names prop (#7932)
This commit is contained in:
parent
d005d71eea
commit
4d843df5d4
@ -134,6 +134,44 @@ export default {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Custom Field Names
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-cascader
|
||||||
|
v-model="cascaderValue"
|
||||||
|
title="Select Area"
|
||||||
|
:options="options"
|
||||||
|
:field-names="fieldNames"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cascaderValue: '',
|
||||||
|
fieldNames: {
|
||||||
|
text: 'name',
|
||||||
|
value: 'code',
|
||||||
|
children: 'items',
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Zhejiang',
|
||||||
|
code: '330000',
|
||||||
|
items: [{ name: 'Hangzhou', code: '330100' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Jiangsu',
|
||||||
|
code: '320000',
|
||||||
|
items: [{ name: 'Nanjing', code: '320100' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
@ -146,6 +184,7 @@ export default {
|
|||||||
| placeholder | Placeholder of unselected tab | _string_ | `Select` |
|
| placeholder | Placeholder of unselected tab | _string_ | `Select` |
|
||||||
| active-color | Active color | _string_ | `#ee0a24` |
|
| active-color | Active color | _string_ | `#ee0a24` |
|
||||||
| closeable | Whether to show close icon | _boolean_ | `true` |
|
| closeable | Whether to show close icon | _boolean_ | `true` |
|
||||||
|
| field-names `v2.12.4` | Custom the fields of options | _object_ | `{ text: 'text', value: 'value', children: 'children' }` |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
|
@ -146,18 +146,59 @@ export default {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 自定义字段名
|
||||||
|
|
||||||
|
通过 `field-names` 属性可以自定义 `options` 里的字段名称。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-cascader
|
||||||
|
v-model="cascaderValue"
|
||||||
|
title="请选择所在地区"
|
||||||
|
:options="options"
|
||||||
|
:field-names="fieldNames"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cascaderValue: '',
|
||||||
|
fieldNames: {
|
||||||
|
text: 'name',
|
||||||
|
value: 'code',
|
||||||
|
children: 'items',
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: '浙江省',
|
||||||
|
code: '330000',
|
||||||
|
items: [{ name: '杭州市', code: '330100' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '江苏省',
|
||||||
|
code: '320000',
|
||||||
|
items: [{ name: '南京市', code: '320100' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
| ------------ | ------------------ | ------------------ | --------- |
|
| --- | --- | --- | --- |
|
||||||
| title | 顶部标题 | _string_ | - |
|
| title | 顶部标题 | _string_ | - |
|
||||||
| value | 选中项的值 | _string \| number_ | - |
|
| value | 选中项的值 | _string \| number_ | - |
|
||||||
| options | 可选项数据源 | _Option[]_ | `[]` |
|
| options | 可选项数据源 | _Option[]_ | `[]` |
|
||||||
| placeholder | 未选中时的提示文案 | _string_ | `请选择` |
|
| placeholder | 未选中时的提示文案 | _string_ | `请选择` |
|
||||||
| active-color | 选中状态的高亮颜色 | _string_ | `#ee0a24` |
|
| active-color | 选中状态的高亮颜色 | _string_ | `#ee0a24` |
|
||||||
| closeable | 是否显示关闭图标 | _boolean_ | `true` |
|
| closeable | 是否显示关闭图标 | _boolean_ | `true` |
|
||||||
|
| field-names `v2.12.4` | 自定义 `options` 结构中的字段 | _object_ | `{ text: 'text', value: 'value', children: 'children' }` |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
|
@ -79,12 +79,40 @@
|
|||||||
/>
|
/>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block card :title="t('customFieldNames')">
|
||||||
|
<van-field
|
||||||
|
v-model="customFieldNames.result"
|
||||||
|
is-link
|
||||||
|
readonly
|
||||||
|
:label="t('area')"
|
||||||
|
:placeholder="t('selectArea')"
|
||||||
|
@click="customFieldNames.show = true"
|
||||||
|
/>
|
||||||
|
<van-popup
|
||||||
|
v-model="customFieldNames.show"
|
||||||
|
round
|
||||||
|
position="bottom"
|
||||||
|
get-container="body"
|
||||||
|
safe-area-inset-bottom
|
||||||
|
>
|
||||||
|
<van-cascader
|
||||||
|
v-model="customFieldNames.value"
|
||||||
|
:title="t('selectArea')"
|
||||||
|
:options="customFieldOptions"
|
||||||
|
:field-names="fieldNames"
|
||||||
|
@close="customFieldNames.show = false"
|
||||||
|
@finish="onFinish('customFieldNames', $event)"
|
||||||
|
/>
|
||||||
|
</van-popup>
|
||||||
|
</demo-block>
|
||||||
</demo-section>
|
</demo-section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import zhCNOptions from './area-zh-CN';
|
import zhCNOptions from './area-zh-CN';
|
||||||
import enUSOptions from './area-en-US';
|
import enUSOptions from './area-en-US';
|
||||||
|
import { deepClone } from '../../utils/deep-clone';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
i18n: {
|
i18n: {
|
||||||
@ -105,6 +133,7 @@ export default {
|
|||||||
{ text: '杭州市', value: '330100' },
|
{ text: '杭州市', value: '330100' },
|
||||||
{ text: '宁波市', value: '330200' },
|
{ text: '宁波市', value: '330200' },
|
||||||
],
|
],
|
||||||
|
customFieldNames: '自定义字段名',
|
||||||
},
|
},
|
||||||
'en-US': {
|
'en-US': {
|
||||||
area: 'Area',
|
area: 'Area',
|
||||||
@ -123,6 +152,7 @@ export default {
|
|||||||
{ text: 'Hangzhou', value: '330100' },
|
{ text: 'Hangzhou', value: '330100' },
|
||||||
{ text: 'Ningbo', value: '330200' },
|
{ text: 'Ningbo', value: '330200' },
|
||||||
],
|
],
|
||||||
|
customFieldNames: 'Custom Field Names',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -144,9 +174,43 @@ export default {
|
|||||||
result: '',
|
result: '',
|
||||||
options: [],
|
options: [],
|
||||||
},
|
},
|
||||||
|
customFieldNames: {
|
||||||
|
show: false,
|
||||||
|
value: null,
|
||||||
|
result: '',
|
||||||
|
},
|
||||||
|
fieldNames: {
|
||||||
|
text: 'name',
|
||||||
|
value: 'code',
|
||||||
|
children: 'items',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
customFieldOptions() {
|
||||||
|
const options = deepClone(this.t('options'));
|
||||||
|
const adjustFieldName = (item) => {
|
||||||
|
if ('text' in item) {
|
||||||
|
item.name = item.text;
|
||||||
|
delete item.text;
|
||||||
|
}
|
||||||
|
if ('value' in item) {
|
||||||
|
item.code = item.value;
|
||||||
|
delete item.value;
|
||||||
|
}
|
||||||
|
if ('children' in item) {
|
||||||
|
item.items = item.children;
|
||||||
|
delete item.children;
|
||||||
|
item.items.forEach(adjustFieldName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
options.forEach(adjustFieldName);
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.async.options = this.t('asyncOptions1');
|
this.async.options = this.t('asyncOptions1');
|
||||||
},
|
},
|
||||||
@ -161,7 +225,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onFinish(type, { value, selectedOptions }) {
|
onFinish(type, { value, selectedOptions }) {
|
||||||
const result = selectedOptions.map((option) => option.text).join('/');
|
const result = selectedOptions
|
||||||
|
.map((option) => option.text || option.name)
|
||||||
|
.join('/');
|
||||||
|
|
||||||
this[type] = {
|
this[type] = {
|
||||||
...this[type],
|
...this[type],
|
||||||
show: false,
|
show: false,
|
||||||
|
@ -9,6 +9,7 @@ export default createComponent({
|
|||||||
props: {
|
props: {
|
||||||
title: String,
|
title: String,
|
||||||
value: [Number, String],
|
value: [Number, String],
|
||||||
|
fieldNames: Object,
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
activeColor: String,
|
activeColor: String,
|
||||||
options: {
|
options: {
|
||||||
@ -28,6 +29,18 @@ export default createComponent({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
textKey() {
|
||||||
|
return this.fieldNames?.text || 'text';
|
||||||
|
},
|
||||||
|
valueKey() {
|
||||||
|
return this.fieldNames?.value || 'value';
|
||||||
|
},
|
||||||
|
childrenKey() {
|
||||||
|
return this.fieldNames?.children || 'children';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
options: {
|
options: {
|
||||||
deep: true,
|
deep: true,
|
||||||
@ -36,7 +49,9 @@ export default createComponent({
|
|||||||
|
|
||||||
value(value) {
|
value(value) {
|
||||||
if (value || value === 0) {
|
if (value || value === 0) {
|
||||||
const values = this.tabs.map((tab) => tab.selectedOption?.value);
|
const values = this.tabs.map(
|
||||||
|
(tab) => tab.selectedOption?.[this.valueKey]
|
||||||
|
);
|
||||||
if (values.indexOf(value) !== -1) {
|
if (values.indexOf(value) !== -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -54,13 +69,13 @@ export default createComponent({
|
|||||||
for (let i = 0; i < options.length; i++) {
|
for (let i = 0; i < options.length; i++) {
|
||||||
const option = options[i];
|
const option = options[i];
|
||||||
|
|
||||||
if (option.value === value) {
|
if (option[this.valueKey] === value) {
|
||||||
return [option];
|
return [option];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option.children) {
|
if (option[this.childrenKey]) {
|
||||||
const selectedOptions = this.getSelectedOptionsByValue(
|
const selectedOptions = this.getSelectedOptionsByValue(
|
||||||
option.children,
|
option[this.childrenKey],
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
if (selectedOptions) {
|
if (selectedOptions) {
|
||||||
@ -87,10 +102,10 @@ export default createComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const next = optionsCursor.filter(
|
const next = optionsCursor.filter(
|
||||||
(item) => item.value === option.value
|
(item) => item[this.valueKey] === option[this.valueKey]
|
||||||
);
|
);
|
||||||
if (next.length) {
|
if (next.length) {
|
||||||
optionsCursor = next[0].children;
|
optionsCursor = next[0][this.childrenKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
return tab;
|
return tab;
|
||||||
@ -126,9 +141,9 @@ export default createComponent({
|
|||||||
this.tabs = this.tabs.slice(0, tabIndex + 1);
|
this.tabs = this.tabs.slice(0, tabIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option.children) {
|
if (option[this.childrenKey]) {
|
||||||
const nextTab = {
|
const nextTab = {
|
||||||
options: option.children,
|
options: option[this.childrenKey],
|
||||||
selectedOption: null,
|
selectedOption: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,14 +163,14 @@ export default createComponent({
|
|||||||
.filter((item) => !!item);
|
.filter((item) => !!item);
|
||||||
|
|
||||||
const eventParams = {
|
const eventParams = {
|
||||||
value: option.value,
|
value: option[this.valueKey],
|
||||||
tabIndex,
|
tabIndex,
|
||||||
selectedOptions,
|
selectedOptions,
|
||||||
};
|
};
|
||||||
this.$emit('input', option.value);
|
this.$emit('input', option[this.valueKey]);
|
||||||
this.$emit('change', eventParams);
|
this.$emit('change', eventParams);
|
||||||
|
|
||||||
if (!option.children) {
|
if (!option[this.childrenKey]) {
|
||||||
this.$emit('finish', eventParams);
|
this.$emit('finish', eventParams);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -182,7 +197,8 @@ export default createComponent({
|
|||||||
renderOptions(options, selectedOption, tabIndex) {
|
renderOptions(options, selectedOption, tabIndex) {
|
||||||
const renderOption = (option) => {
|
const renderOption = (option) => {
|
||||||
const isSelected =
|
const isSelected =
|
||||||
selectedOption && option.value === selectedOption.value;
|
selectedOption &&
|
||||||
|
option[this.valueKey] === selectedOption[this.valueKey];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
@ -192,7 +208,7 @@ export default createComponent({
|
|||||||
this.onSelect(option, tabIndex);
|
this.onSelect(option, tabIndex);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>{option.text}</span>
|
<span>{option[this.textKey]}</span>
|
||||||
{isSelected ? (
|
{isSelected ? (
|
||||||
<Icon name="success" class={bem('selected-icon')} />
|
<Icon name="success" class={bem('selected-icon')} />
|
||||||
) : null}
|
) : null}
|
||||||
@ -206,7 +222,7 @@ export default createComponent({
|
|||||||
renderTab(item, tabIndex) {
|
renderTab(item, tabIndex) {
|
||||||
const { options, selectedOption } = item;
|
const { options, selectedOption } = item;
|
||||||
const title = selectedOption
|
const title = selectedOption
|
||||||
? selectedOption.text
|
? selectedOption[this.textKey]
|
||||||
: this.placeholder || t('select');
|
: this.placeholder || t('select');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,5 +29,14 @@ exports[`renders demo correctly 1`] = `
|
|||||||
<!----></i>
|
<!----></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
|
||||||
|
<div class="van-cell__title van-field__label"><span>地区</span></div>
|
||||||
|
<div class="van-cell__value van-field__value">
|
||||||
|
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="请选择所在地区" class="van-field__control"></div>
|
||||||
|
</div><i class="van-icon van-icon-arrow van-cell__right-icon">
|
||||||
|
<!----></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user