mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +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
|
||||
|
||||
### Props
|
||||
@ -146,6 +184,7 @@ export default {
|
||||
| placeholder | Placeholder of unselected tab | _string_ | `Select` |
|
||||
| active-color | Active color | _string_ | `#ee0a24` |
|
||||
| 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
|
||||
|
||||
|
@ -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
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ------------ | ------------------ | ------------------ | --------- |
|
||||
| title | 顶部标题 | _string_ | - |
|
||||
| value | 选中项的值 | _string \| number_ | - |
|
||||
| options | 可选项数据源 | _Option[]_ | `[]` |
|
||||
| placeholder | 未选中时的提示文案 | _string_ | `请选择` |
|
||||
| active-color | 选中状态的高亮颜色 | _string_ | `#ee0a24` |
|
||||
| closeable | 是否显示关闭图标 | _boolean_ | `true` |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| title | 顶部标题 | _string_ | - |
|
||||
| value | 选中项的值 | _string \| number_ | - |
|
||||
| options | 可选项数据源 | _Option[]_ | `[]` |
|
||||
| placeholder | 未选中时的提示文案 | _string_ | `请选择` |
|
||||
| active-color | 选中状态的高亮颜色 | _string_ | `#ee0a24` |
|
||||
| closeable | 是否显示关闭图标 | _boolean_ | `true` |
|
||||
| field-names `v2.12.4` | 自定义 `options` 结构中的字段 | _object_ | `{ text: 'text', value: 'value', children: 'children' }` |
|
||||
|
||||
### Events
|
||||
|
||||
|
@ -79,12 +79,40 @@
|
||||
/>
|
||||
</van-popup>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import zhCNOptions from './area-zh-CN';
|
||||
import enUSOptions from './area-en-US';
|
||||
import { deepClone } from '../../utils/deep-clone';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
@ -105,6 +133,7 @@ export default {
|
||||
{ text: '杭州市', value: '330100' },
|
||||
{ text: '宁波市', value: '330200' },
|
||||
],
|
||||
customFieldNames: '自定义字段名',
|
||||
},
|
||||
'en-US': {
|
||||
area: 'Area',
|
||||
@ -123,6 +152,7 @@ export default {
|
||||
{ text: 'Hangzhou', value: '330100' },
|
||||
{ text: 'Ningbo', value: '330200' },
|
||||
],
|
||||
customFieldNames: 'Custom Field Names',
|
||||
},
|
||||
},
|
||||
|
||||
@ -144,9 +174,43 @@ export default {
|
||||
result: '',
|
||||
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() {
|
||||
this.async.options = this.t('asyncOptions1');
|
||||
},
|
||||
@ -161,7 +225,10 @@ export default {
|
||||
},
|
||||
|
||||
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],
|
||||
show: false,
|
||||
|
@ -9,6 +9,7 @@ export default createComponent({
|
||||
props: {
|
||||
title: String,
|
||||
value: [Number, String],
|
||||
fieldNames: Object,
|
||||
placeholder: String,
|
||||
activeColor: String,
|
||||
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: {
|
||||
options: {
|
||||
deep: true,
|
||||
@ -36,7 +49,9 @@ export default createComponent({
|
||||
|
||||
value(value) {
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@ -54,13 +69,13 @@ export default createComponent({
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const option = options[i];
|
||||
|
||||
if (option.value === value) {
|
||||
if (option[this.valueKey] === value) {
|
||||
return [option];
|
||||
}
|
||||
|
||||
if (option.children) {
|
||||
if (option[this.childrenKey]) {
|
||||
const selectedOptions = this.getSelectedOptionsByValue(
|
||||
option.children,
|
||||
option[this.childrenKey],
|
||||
value
|
||||
);
|
||||
if (selectedOptions) {
|
||||
@ -87,10 +102,10 @@ export default createComponent({
|
||||
};
|
||||
|
||||
const next = optionsCursor.filter(
|
||||
(item) => item.value === option.value
|
||||
(item) => item[this.valueKey] === option[this.valueKey]
|
||||
);
|
||||
if (next.length) {
|
||||
optionsCursor = next[0].children;
|
||||
optionsCursor = next[0][this.childrenKey];
|
||||
}
|
||||
|
||||
return tab;
|
||||
@ -126,9 +141,9 @@ export default createComponent({
|
||||
this.tabs = this.tabs.slice(0, tabIndex + 1);
|
||||
}
|
||||
|
||||
if (option.children) {
|
||||
if (option[this.childrenKey]) {
|
||||
const nextTab = {
|
||||
options: option.children,
|
||||
options: option[this.childrenKey],
|
||||
selectedOption: null,
|
||||
};
|
||||
|
||||
@ -148,14 +163,14 @@ export default createComponent({
|
||||
.filter((item) => !!item);
|
||||
|
||||
const eventParams = {
|
||||
value: option.value,
|
||||
value: option[this.valueKey],
|
||||
tabIndex,
|
||||
selectedOptions,
|
||||
};
|
||||
this.$emit('input', option.value);
|
||||
this.$emit('input', option[this.valueKey]);
|
||||
this.$emit('change', eventParams);
|
||||
|
||||
if (!option.children) {
|
||||
if (!option[this.childrenKey]) {
|
||||
this.$emit('finish', eventParams);
|
||||
}
|
||||
},
|
||||
@ -182,7 +197,8 @@ export default createComponent({
|
||||
renderOptions(options, selectedOption, tabIndex) {
|
||||
const renderOption = (option) => {
|
||||
const isSelected =
|
||||
selectedOption && option.value === selectedOption.value;
|
||||
selectedOption &&
|
||||
option[this.valueKey] === selectedOption[this.valueKey];
|
||||
|
||||
return (
|
||||
<li
|
||||
@ -192,7 +208,7 @@ export default createComponent({
|
||||
this.onSelect(option, tabIndex);
|
||||
}}
|
||||
>
|
||||
<span>{option.text}</span>
|
||||
<span>{option[this.textKey]}</span>
|
||||
{isSelected ? (
|
||||
<Icon name="success" class={bem('selected-icon')} />
|
||||
) : null}
|
||||
@ -206,7 +222,7 @@ export default createComponent({
|
||||
renderTab(item, tabIndex) {
|
||||
const { options, selectedOption } = item;
|
||||
const title = selectedOption
|
||||
? selectedOption.text
|
||||
? selectedOption[this.textKey]
|
||||
: this.placeholder || t('select');
|
||||
|
||||
return (
|
||||
|
@ -29,5 +29,14 @@ exports[`renders demo correctly 1`] = `
|
||||
<!----></i>
|
||||
</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>
|
||||
`;
|
||||
|
Loading…
x
Reference in New Issue
Block a user