refactor(Picker): data driven

This commit is contained in:
chenjiahan 2022-01-18 21:02:41 +08:00 committed by neverland
parent bcb7eabff3
commit 89b029aa42
8 changed files with 308 additions and 373 deletions

View File

@ -9,6 +9,7 @@ import {
// Utils
import {
isDef,
extend,
unitToPx,
truthProp,
@ -32,12 +33,11 @@ import Column, { PICKER_KEY } from './PickerColumn';
// Types
import type {
PickerColumn,
PickerOption,
PickerExpose,
PickerFieldNames,
PickerObjectColumn,
PickerToolbarPosition,
} from './types';
import { PickerOption } from '.';
const [name, bem, t] = createNamespace('picker');
@ -55,7 +55,8 @@ export const pickerSharedProps = {
};
const pickerProps = extend({}, pickerSharedProps, {
columns: makeArrayProp<PickerOption | PickerColumn>(),
columns: makeArrayProp<PickerColumn | PickerColumn[]>(),
modelValue: makeArrayProp<number | string>(),
defaultIndex: makeNumericProp(0),
toolbarPosition: makeStringProp<PickerToolbarPosition>('top'),
columnsFieldNames: Object as PropType<PickerFieldNames>,
@ -68,20 +69,21 @@ export default defineComponent({
props: pickerProps,
emits: ['confirm', 'cancel', 'change'],
emits: ['confirm', 'cancel', 'change', 'update:modelValue'],
setup(props, { emit, slots }) {
const hasOptions = ref(false);
const formattedColumns = ref<PickerObjectColumn[]>([]);
const selectedValues = ref(props.modelValue);
const currentColumns = ref<PickerColumn[]>([]);
const {
text: textKey,
values: valuesKey,
value: valueKey,
children: childrenKey,
} = extend(
{
text: 'text',
values: 'values',
value: 'value',
children: 'children',
},
props.columnsFieldNames
@ -95,186 +97,78 @@ export default defineComponent({
const dataType = computed(() => {
const firstColumn = props.columns[0];
if (typeof firstColumn === 'object') {
if (childrenKey in firstColumn) {
return 'cascade';
}
if (valuesKey in firstColumn) {
return 'object';
}
if (Array.isArray(firstColumn)) {
return 'multiple';
}
return 'plain';
if (childrenKey in firstColumn) {
return 'cascade';
}
return 'default';
});
const findOption = (options: PickerOption[], value: number | string) =>
options.find((option) => option[valueKey] === value);
const formatCascade = () => {
const formatted: PickerObjectColumn[] = [];
const formatted: PickerColumn[] = [];
let cursor: PickerObjectColumn = {
let cursor: PickerOption | undefined = {
[childrenKey]: props.columns,
};
let columnIndex = 0;
while (cursor && cursor[childrenKey]) {
const children = cursor[childrenKey];
let defaultIndex = cursor.defaultIndex ?? +props.defaultIndex;
const options: PickerOption[] = cursor[childrenKey];
const value = selectedValues.value[columnIndex];
while (children[defaultIndex] && children[defaultIndex].disabled) {
if (defaultIndex < children.length - 1) {
defaultIndex++;
} else {
defaultIndex = 0;
break;
}
cursor = isDef(value) ? findOption(options, value) : undefined;
if (!cursor && options.length) {
const firstValue = options[0][valueKey];
selectedValues.value[columnIndex] = firstValue;
cursor = findOption(options, firstValue);
}
formatted.push({
[valuesKey]: cursor[childrenKey],
className: cursor.className,
defaultIndex,
});
cursor = children[defaultIndex];
}
formattedColumns.value = formatted;
};
const format = () => {
const { columns } = props;
if (dataType.value === 'plain') {
formattedColumns.value = [{ [valuesKey]: columns }];
} else if (dataType.value === 'cascade') {
formatCascade();
} else {
formattedColumns.value = columns as PickerObjectColumn[];
}
hasOptions.value = formattedColumns.value.some(
(item) => item[valuesKey] && item[valuesKey].length !== 0
);
};
// get indexes of all columns
const getIndexes = () => children.map((child) => child.state.index);
// set options of column by index
const setColumnValues = (index: number, options: PickerOption[]) => {
const column = children[index];
if (column) {
column.setOptions(options);
hasOptions.value = true;
}
};
const onCascadeChange = (columnIndex: number) => {
let cursor: PickerObjectColumn = {
[childrenKey]: props.columns,
};
const indexes = getIndexes();
for (let i = 0; i <= columnIndex; i++) {
cursor = cursor[childrenKey][indexes[i]];
}
while (cursor && cursor[childrenKey]) {
columnIndex++;
setColumnValues(columnIndex, cursor[childrenKey]);
cursor = cursor[childrenKey][cursor.defaultIndex || 0];
formatted.push(options);
}
return formatted;
};
// get column instance by index
const getChild = (index: number) => children[index];
const selectedOptions = computed(() =>
currentColumns.value.map((options, index) =>
findOption(options, selectedValues.value[index])
)
);
// get column value by index
const getColumnValue = (index: number) => {
const column = getChild(index);
if (column) {
return column.getValue();
}
};
const onChange = (value: number | string, columnIndex: number) => {
selectedValues.value[columnIndex] = value;
// set column value by index
const setColumnValue = (index: number, value: string) => {
const column = getChild(index);
if (column) {
column.setValue(value);
if (dataType.value === 'cascade') {
onCascadeChange(index);
}
}
};
// get column option index by column index
const getColumnIndex = (index: number) => {
const column = getChild(index);
if (column) {
return column.state.index;
}
};
// set column option index by column index
const setColumnIndex = (columnIndex: number, optionIndex: number) => {
const column = getChild(columnIndex);
if (column) {
column.setIndex(optionIndex);
if (dataType.value === 'cascade') {
onCascadeChange(columnIndex);
}
}
};
// get options of column by index
const getColumnValues = (index: number) => {
const column = getChild(index);
if (column) {
return column.state.options;
}
};
// get values of all columns
const getValues = () => children.map((child) => child.getValue());
// set values of all columns
const setValues = (values: string[]) => {
values.forEach((value, index) => {
setColumnValue(index, value);
});
};
// set indexes of all columns
const setIndexes = (indexes: number[]) => {
indexes.forEach((optionIndex, columnIndex) => {
setColumnIndex(columnIndex, optionIndex);
});
};
const emitAction = (event: 'confirm' | 'cancel') => {
if (dataType.value === 'plain') {
emit(event, getColumnValue(0), getColumnIndex(0));
} else {
emit(event, getValues(), getIndexes());
}
};
const onChange = (columnIndex: number) => {
if (dataType.value === 'cascade') {
onCascadeChange(columnIndex);
currentColumns.value = formatCascade();
}
if (dataType.value === 'plain') {
emit('change', getColumnValue(0), getColumnIndex(0));
} else {
emit('change', getValues(), columnIndex);
}
emit('change', {
columnIndex,
selectedValues: selectedValues.value,
selectedOptions: selectedOptions.value,
});
};
const confirm = () => {
children.forEach((child) => child.stopMomentum());
emitAction('confirm');
emit('confirm', {
selectedValues: selectedValues.value,
selectedOptions: selectedOptions.value,
});
};
const cancel = () => emitAction('cancel');
const cancel = () =>
emit('cancel', {
selectedValues: selectedValues.value,
selectedOptions: selectedOptions.value,
});
const renderTitle = () => {
if (slots.title) {
@ -324,19 +218,19 @@ export default defineComponent({
};
const renderColumnItems = () =>
formattedColumns.value.map((item, columnIndex) => (
currentColumns.value.map((options, columnIndex) => (
<Column
v-slots={{ option: slots.option }}
value={selectedValues.value[columnIndex]}
textKey={textKey}
options={options}
readonly={props.readonly}
valueKey={valueKey}
allowHtml={props.allowHtml}
className={item.className}
itemHeight={itemHeight.value}
defaultIndex={item.defaultIndex ?? +props.defaultIndex}
swipeDuration={props.swipeDuration}
initialOptions={item[valuesKey]}
visibleItemCount={props.visibleItemCount}
onChange={() => onChange(columnIndex)}
onChange={(value: number | string) => onChange(value, columnIndex)}
/>
));
@ -371,22 +265,51 @@ export default defineComponent({
);
};
watch(() => props.columns, format, { immediate: true });
watch(
() => props.columns,
() => {
const { columns } = props;
useExpose<PickerExpose>({
confirm,
getValues,
setValues,
getIndexes,
setIndexes,
getColumnIndex,
setColumnIndex,
getColumnValue,
setColumnValue,
getColumnValues,
setColumnValues,
switch (dataType.value) {
case 'multiple':
currentColumns.value = columns;
break;
case 'cascade':
currentColumns.value = formatCascade();
break;
default:
currentColumns.value = [columns];
break;
}
currentColumns.value.forEach((options, index) => {
if (selectedValues.value[index] === undefined && options.length) {
selectedValues.value[index] = options[0][valueKey];
}
});
hasOptions.value = currentColumns.value.some(
(options) => !!options.length
);
},
{ immediate: true }
);
watch(
() => props.modelValue,
(value) => {
selectedValues.value = value;
}
);
watch(selectedValues, () => {
if (selectedValues.value !== props.modelValue) {
emit('update:modelValue', selectedValues.value);
}
});
useExpose<PickerExpose>({ confirm });
return () => (
<div class={bem()}>
{props.toolbarPosition === 'top' ? renderToolbar() : null}

View File

@ -1,14 +1,10 @@
import { ref, watch, reactive, defineComponent, type InjectionKey } from 'vue';
import { ref, reactive, defineComponent, type InjectionKey, watch } from 'vue';
// Utils
import { deepClone } from '../utils/deep-clone';
import {
clamp,
isObject,
unknownProp,
numericProp,
makeArrayProp,
makeNumberProp,
preventDefault,
createNamespace,
makeRequiredProp,
@ -40,21 +36,18 @@ function getElementTranslateY(element: Element) {
export const PICKER_KEY: InjectionKey<PickerColumnProvide> = Symbol(name);
const isOptionDisabled = (option: PickerOption) =>
isObject(option) && option.disabled;
export default defineComponent({
name,
props: {
value: numericProp,
textKey: makeRequiredProp(String),
options: makeArrayProp<PickerOption>(),
readonly: Boolean,
valueKey: makeRequiredProp(String),
allowHtml: Boolean,
className: unknownProp,
itemHeight: makeRequiredProp(Number),
defaultIndex: makeNumberProp(0),
swipeDuration: makeRequiredProp(numericProp),
initialOptions: makeArrayProp<PickerOption>(),
visibleItemCount: makeRequiredProp(numericProp),
},
@ -70,15 +63,13 @@ export default defineComponent({
const wrapper = ref<HTMLElement>();
const state = reactive({
index: props.defaultIndex,
offset: 0,
duration: 0,
options: deepClone(props.initialOptions),
});
const touch = useTouch();
const count = () => state.options.length;
const count = () => props.options.length;
const baseOffset = () =>
(props.itemHeight * (+props.visibleItemCount - 1)) / 2;
@ -87,24 +78,22 @@ export default defineComponent({
index = clamp(index, 0, count());
for (let i = index; i < count(); i++) {
if (!isOptionDisabled(state.options[i])) return i;
if (!props.options[i].disabled) return i;
}
for (let i = index - 1; i >= 0; i--) {
if (!isOptionDisabled(state.options[i])) return i;
if (!props.options[i].disabled) return i;
}
return 0;
};
const setIndex = (index: number, emitChange?: boolean) => {
index = adjustIndex(index) || 0;
const updateValueByIndex = (index: number) => {
index = adjustIndex(index);
const offset = -index * props.itemHeight;
const trigger = () => {
if (index !== state.index) {
state.index = index;
if (emitChange) {
emit('change', index);
}
const { value } = props.options[index];
if (value !== props.value) {
emit('change', value);
}
};
@ -118,13 +107,6 @@ export default defineComponent({
state.offset = offset;
};
const setOptions = (options: PickerOption[]) => {
if (JSON.stringify(options) !== JSON.stringify(state.options)) {
state.options = deepClone(options);
setIndex(props.defaultIndex);
}
};
const onClickItem = (index: number) => {
if (moving || props.readonly) {
return;
@ -132,14 +114,7 @@ export default defineComponent({
transitionEndTrigger = null;
state.duration = DEFAULT_DURATION;
setIndex(index, true);
};
const getOptionText = (option: PickerOption) => {
if (isObject(option) && props.textKey in option) {
return option[props.textKey];
}
return option;
updateValueByIndex(index);
};
const getIndexByOffset = (offset: number) =>
@ -153,7 +128,7 @@ export default defineComponent({
const index = getIndexByOffset(distance);
state.duration = +props.swipeDuration;
setIndex(index, true);
updateValueByIndex(index);
};
const stopMomentum = () => {
@ -230,10 +205,10 @@ export default defineComponent({
const index = getIndexByOffset(state.offset);
state.duration = DEFAULT_DURATION;
setIndex(index, true);
updateValueByIndex(index);
// compatible with desktop scenario
// use setTimeout to skip the click event Emitted after touchstart
// use setTimeout to skip the click event emitted after touchstart
setTimeout(() => {
moving = false;
}, 0);
@ -244,17 +219,17 @@ export default defineComponent({
height: `${props.itemHeight}px`,
};
return state.options.map((option, index: number) => {
const text = getOptionText(option);
const disabled = isOptionDisabled(option);
return props.options.map((option, index) => {
const text = option[props.textKey];
const { disabled } = option;
const value: string | number = option[props.valueKey];
const data = {
role: 'button',
style: optionStyle,
tabindex: disabled ? -1 : 0,
class: bem('item', {
disabled,
selected: index === state.index,
selected: value === props.value,
}),
onClick: () => onClickItem(index),
};
@ -272,39 +247,23 @@ export default defineComponent({
});
};
const setValue = (value: string) => {
const { options } = state;
for (let i = 0; i < options.length; i++) {
if (getOptionText(options[i]) === value) {
return setIndex(i);
}
}
};
const getValue = (): PickerOption => state.options[state.index];
setIndex(state.index);
useParent(PICKER_KEY);
useExpose({
state,
setIndex,
getValue,
setValue,
setOptions,
stopMomentum,
});
watch(() => props.initialOptions, setOptions);
useExpose({ stopMomentum });
watch(
() => props.defaultIndex,
(value) => setIndex(value)
() => props.value,
(value) => {
const index = props.options.findIndex(
(option) => option[props.valueKey] === value
);
const offset = -adjustIndex(index) * props.itemHeight;
state.offset = offset;
}
);
return () => (
<div
class={[bem(), props.className]}
class={bem()}
onTouchstart={onTouchStart}
onTouchmove={onTouchMove}
onTouchend={onTouchEnd}

View File

@ -35,13 +35,18 @@ import { Toast } from 'vant';
export default {
setup() {
const columns = ['Delaware', 'Florida', 'Georqia', 'Indiana', 'Maine'];
const onConfirm = (value, index) => {
Toast(`Value: ${value}, Index: ${index}`);
const columns = [
{ text: 'Delaware', value: 'Delaware' },
{ text: 'Florida', value: 'Florida' },
{ text: 'Georqia', value: 'Georqia' },
{ text: 'Indiana', value: 'Indiana' },
{ text: 'Maine', value: 'Maine' },
];
const onConfirm = (option, index) => {
Toast(`Value: ${option.value}, Index: ${index}`);
};
const onChange = (value, index) => {
Toast(`Value: ${value}, Index: ${index}`);
const onChange = (option, index) => {
Toast(`Value: ${option.value}, Index: ${index}`);
};
const onCancel = () => Toast('Cancel');
@ -55,12 +60,6 @@ export default {
};
```
### Default Index
```html
<van-picker title="Title" :columns="columns" :default-index="2" />
```
### Multiple Columns
```html
@ -71,14 +70,18 @@ export default {
export default {
setup() {
const columns = [
{
values: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
defaultIndex: 2,
},
{
values: ['Morning', 'Afternoon', 'Evening'],
defaultIndex: 1,
},
[
{ text: 'Monday', value: 'Monday' },
{ text: 'Tuesday', value: 'Tuesday' },
{ text: 'Wednesday', value: 'Wednesday' },
{ text: 'Thursday', value: 'Thursday' },
{ text: 'Friday', value: 'Friday' },
],
[
{ text: 'Morning', value: 'Morning' },
{ text: 'Afternoon', value: 'Afternoon' },
{ text: 'Evening', value: 'Evening' },
],
];
return { columns };

View File

@ -43,13 +43,21 @@ import { Toast } from 'vant';
export default {
setup() {
const columns = ['杭州', '宁波', '温州', '绍兴', '湖州', '嘉兴', '金华'];
const onConfirm = (value, index) => {
Toast(`当前值: ${value}, 当前索引: ${index}`);
const columns = [
{ text: '杭州', value: 'Hangzhou' },
{ text: '宁波', value: 'Ningbo' },
{ text: '温州', value: 'Wenzhou' },
{ text: '绍兴', value: 'Shaoxing' },
{ text: '湖州', value: 'Huzhou' },
{ text: '嘉兴', value: 'Jiaxing' },
{ text: '金华', value: 'Jinhua' },
{ text: '衢州', value: 'Quzhou' },
];
const onConfirm = (option, index) => {
Toast(`当前值: ${option.value}, 当前索引: ${index}`);
};
const onChange = (value, index) => {
Toast(`当前值: ${value}, 当前索引: ${index}`);
const onChange = (option, index) => {
Toast(`当前值: ${option.value}, 当前索引: ${index}`);
};
const onCancel = () => Toast('取消');
@ -63,17 +71,9 @@ export default {
};
```
### 默认选中项
单列选择时,可以通过 `default-index` 属性设置初始选中项的索引。
```html
<van-picker title="标题" :columns="columns" :default-index="2" />
```
### 多列选择
`columns` 属性可以通过对象数组的形式配置多列选择,对象中可以配置选项数据、初始选中项等,详细格式见[下方表格](#/zh-CN/picker#column-shu-ju-jie-gou)
`columns` 属性可以通过二维数组的形式配置多列选择。
```html
<van-picker title="标题" :columns="columns" />
@ -84,15 +84,19 @@ export default {
setup() {
const columns = [
// 第一列
{
values: ['周一', '周二', '周三', '周四', '周五'],
defaultIndex: 2,
},
[
{ text: '周一', value: 'Monday' },
{ text: '周二', value: 'Tuesday' },
{ text: '周三', value: 'Wednesday' },
{ text: '周四', value: 'Thursday' },
{ text: '周五', value: 'Friday' },
],
// 第二列
{
values: ['上午', '下午', '晚上'],
defaultIndex: 1,
},
[
{ text: '上午', value: 'Morning' },
{ text: '下午', value: 'Afternoon' },
{ text: '晚上', value: 'Evening' },
],
];
return { columns };

View File

@ -1,23 +1,51 @@
export const dateColumns = {
export const basicColumns = {
'zh-CN': [
{
values: ['周一', '周二', '周三', '周四', '周五'],
defaultIndex: 2,
},
{
values: ['上午', '下午', '晚上'],
defaultIndex: 1,
},
{ text: '杭州', value: 'Hangzhou' },
{ text: '宁波', value: 'Ningbo' },
{ text: '温州', value: 'Wenzhou' },
{ text: '绍兴', value: 'Shaoxing' },
{ text: '湖州', value: 'Huzhou' },
{ text: '嘉兴', value: 'Jiaxing' },
{ text: '金华', value: 'Jinhua' },
{ text: '衢州', value: 'Quzhou' },
],
'en-US': [
{
values: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
defaultIndex: 2,
},
{
values: ['Morning', 'Afternoon', 'Evening'],
defaultIndex: 1,
},
{ text: 'Delaware', value: 'Delaware' },
{ text: 'Florida', value: 'Florida' },
{ text: 'Georqia', value: 'Georqia' },
{ text: 'Indiana', value: 'Indiana' },
{ text: 'Maine', value: 'Maine' },
],
};
export const dateColumns = {
'zh-CN': [
[
{ text: '周一', value: 'Monday' },
{ text: '周二', value: 'Tuesday' },
{ text: '周三', value: 'Wednesday' },
{ text: '周四', value: 'Thursday' },
{ text: '周五', value: 'Friday' },
],
[
{ text: '上午', value: 'Morning' },
{ text: '下午', value: 'Afternoon' },
{ text: '晚上', value: 'Evening' },
],
],
'en-US': [
[
{ text: 'Monday', value: 'Monday' },
{ text: 'Tuesday', value: 'Tuesday' },
{ text: 'Wednesday', value: 'Wednesday' },
{ text: 'Thursday', value: 'Thursday' },
{ text: 'Friday', value: 'Friday' },
],
[
{ text: 'Morning', value: 'Morning' },
{ text: 'Afternoon', value: 'Afternoon' },
{ text: 'Evening', value: 'Evening' },
],
],
};
@ -25,27 +53,45 @@ export const cascadeColumns = {
'zh-CN': [
{
text: '浙江',
value: 'Zhejiang',
children: [
{
text: '杭州',
children: [{ text: '西湖区' }, { text: '余杭区' }],
value: 'Hangzhou',
children: [
{ text: '西湖区', value: 'Xihu' },
{ text: '余杭区', value: 'Yuhang' },
],
},
{
text: '温州',
children: [{ text: '鹿城区' }, { text: '瓯海区' }],
value: 'Wenzhou',
children: [
{ text: '鹿城区', value: 'Lucheng' },
{ text: '瓯海区', value: 'Ouhai' },
],
},
],
},
{
text: '福建',
value: 'Fujian',
children: [
{
text: '福州',
children: [{ text: '鼓楼区' }, { text: '台江区' }],
value: 'Fuzhou',
children: [
{ text: '鼓楼区', value: 'Gulou' },
{ text: '台江区', value: 'Taijiang' },
],
},
{
text: '厦门',
children: [{ text: '思明区' }, { text: '海沧区' }],
value: 'Xiamen',
children: [
{ text: '思明区', value: 'Siming' },
{ text: '海沧区', value: 'Haicang' },
],
},
],
},
@ -53,27 +99,45 @@ export const cascadeColumns = {
'en-US': [
{
text: 'Zhejiang',
value: 'Zhejiang',
children: [
{
text: 'Hangzhou',
children: [{ text: 'Xihu' }, { text: 'Yuhang' }],
value: 'Hangzhou',
children: [
{ text: 'Xihu', value: 'Xihu' },
{ text: 'Yuhang', value: 'Yuhang' },
],
},
{
text: 'Wenzhou',
children: [{ text: 'Lucheng' }, { text: 'Ouhai' }],
value: 'Wenzhou',
children: [
{ text: 'Lucheng', value: 'Lucheng' },
{ text: 'Ouhai', value: 'Ouhai' },
],
},
],
},
{
text: 'Fujian',
value: 'Fujian',
children: [
{
text: 'Fuzhou',
children: [{ text: 'Gulou' }, { text: 'Taijiang' }],
value: 'Fuzhou',
children: [
{ text: 'Gulou', value: 'Gulou' },
{ text: 'Taijiang', value: 'Taijiang' },
],
},
{
text: 'Xiamen',
children: [{ text: 'Siming' }, { text: 'Haicang' }],
value: 'Xiamen',
children: [
{ text: 'Siming', value: 'Siming' },
{ text: 'Haicang', value: 'Haicang' },
],
},
],
},

View File

@ -1,11 +1,17 @@
<script setup lang="ts">
import VanPicker from '..';
import VanPicker, { PickerOption, PickerChangeEventParams } from '..';
import VanField from '../../field';
import VanPopup from '../../popup';
import { ref, computed } from 'vue';
import { dateColumns, cascadeColumns, cascadeColumnsCustomKey } from './data';
import {
dateColumns,
cascadeColumns,
cascadeColumnsCustomKey,
basicColumns,
} from './data';
import { useTranslate } from '../../../docs/site/use-translate';
import { Toast } from '../../toast';
import { PickerConfirmEventParams } from '../types';
const t = useTranslate({
'zh-CN': {
@ -15,6 +21,7 @@ const t = useTranslate({
chooseCity: '选择城市',
showToolbar: '展示顶部栏',
dateColumns: dateColumns['zh-CN'],
basicColumns: basicColumns['zh-CN'],
defaultIndex: '默认选中项',
disableOption: '禁用选项',
cascadeColumns: cascadeColumns['zh-CN'],
@ -22,16 +29,6 @@ const t = useTranslate({
setColumnValues: '动态设置选项',
customChildrenKey: '自定义 Columns 结构',
customChildrenColumns: cascadeColumnsCustomKey['zh-CN'],
textColumns: [
'杭州',
'宁波',
'温州',
'绍兴',
'湖州',
'嘉兴',
'金华',
'衢州',
],
disabledColumns: [
{ text: '杭州', disabled: true },
{ text: '宁波' },
@ -41,8 +38,7 @@ const t = useTranslate({
浙江: ['杭州', '宁波', '温州', '嘉兴', '湖州'],
福建: ['福州', '厦门', '莆田', '三明', '泉州'],
},
toastContent: (value: string, index: number) =>
`当前值:${value}, 当前索引:${index}`,
toastContent: (value: string) => `当前值:${value}`,
},
'en-US': {
city: 'City',
@ -51,6 +47,7 @@ const t = useTranslate({
chooseCity: 'Choose City',
showToolbar: 'Show Toolbar',
dateColumns: dateColumns['en-US'],
basicColumns: basicColumns['en-US'],
defaultIndex: 'Default Index',
disableOption: 'Disable Option',
cascadeColumns: cascadeColumns['en-US'],
@ -58,7 +55,6 @@ const t = useTranslate({
setColumnValues: 'Set Column Values',
customChildrenKey: 'Custom Columns Fields',
customChildrenColumns: cascadeColumnsCustomKey['en-US'],
textColumns: ['Delaware', 'Florida', 'Georqia', 'Indiana', 'Maine'],
disabledColumns: [
{ text: 'Delaware', disabled: true },
{ text: 'Florida' },
@ -96,16 +92,16 @@ const columns = computed(() => {
];
});
const onChange1 = (value: string, index: number) => {
Toast(t('toastContent', value, index));
const onChange1 = ({ selectedValues }: PickerChangeEventParams) => {
Toast(t('toastContent', selectedValues.join(',')));
};
const onChange2 = (values: string[]) => {
picker.value.setColumnValues(1, t('column3')[values[0]]);
};
const onConfirm = (value: string, index: number) => {
Toast(t('toastContent', value, index));
const onConfirm = ({ selectedValues }: PickerConfirmEventParams) => {
Toast(t('toastContent', selectedValues.join(',')));
};
const onCancel = () => Toast(t('cancel'));
@ -128,17 +124,9 @@ const onConfirm2 = (value: string) => {
<demo-block card :title="t('basicUsage')">
<van-picker
:title="t('title')"
:columns="t('textColumns')"
@change="onChange1"
/>
</demo-block>
<demo-block card :title="t('defaultIndex')">
<van-picker
:title="t('title')"
:columns="t('textColumns')"
:default-index="2"
:columns="t('basicColumns')"
@change="onChange1"
@confirm="onConfirm"
/>
</demo-block>
@ -155,6 +143,8 @@ const onConfirm2 = (value: string) => {
<van-picker :title="t('title')" :columns="t('cascadeColumns')" />
</demo-block>
<!--
<demo-block card :title="t('disableOption')">
<van-picker :title="t('title')" :columns="t('disabledColumns')" />
</demo-block>
@ -196,5 +186,5 @@ const onConfirm2 = (value: string) => {
:columns="t('customChildrenColumns')"
:columns-field-names="customFieldName"
/>
</demo-block>
</demo-block> -->
</template>

View File

@ -9,9 +9,8 @@ export type {
PickerOption,
PickerInstance,
PickerFieldNames,
PickerObjectColumn,
PickerObjectOption,
PickerToolbarPosition,
PickerChangeEventParams,
} from './types';
declare module 'vue' {

View File

@ -6,42 +6,24 @@ export type PickerToolbarPosition = 'top' | 'bottom';
export type PickerFieldNames = {
text?: string;
values?: string;
value?: string;
children?: string;
};
export type PickerObjectOption = {
export type PickerOption = {
text?: string | number;
value?: string | number;
disabled?: boolean;
// for custom filed names
[key: PropertyKey]: any;
};
export type PickerOption = string | number | PickerObjectOption;
export type PickerObjectColumn = {
values?: PickerOption[];
children?: PickerColumn;
className?: unknown;
defaultIndex?: number;
// for custom filed names
[key: PropertyKey]: any;
};
export type PickerColumn = PickerOption[] | PickerObjectColumn;
export type PickerColumn = PickerOption[];
export type PickerExpose = {
confirm: () => void;
getValues: <T = PickerOption>() => T[];
setValues: (values: string[]) => void;
getIndexes: () => number[];
setIndexes: (indexes: number[]) => void;
getColumnIndex: (index: number) => number;
setColumnIndex: (columnIndex: number, optionIndex: number) => void;
getColumnValue: <T = PickerOption>(index: number) => T;
setColumnValue: (index: number, value: string) => void;
getColumnValues: <T = PickerOption>(index: number) => T[];
setColumnValues: (index: number, options: PickerOption[]) => void;
};
export type PickerColumnProvide = {
@ -59,3 +41,14 @@ export type PickerColumnProvide = {
};
export type PickerInstance = ComponentPublicInstance<PickerProps, PickerExpose>;
export type PickerConfirmEventParams = {
selectedValues: Array<number | string>;
selectedOptions: PickerOption[];
};
export type PickerCancelEventParams = PickerConfirmEventParams;
export type PickerChangeEventParams = PickerConfirmEventParams & {
columnIndex: number;
};