| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- <template>
- <FixedVirtualList
- v-if="virtual"
- :data="data"
- :dataKey="dataKey"
- :containerStyle="innerStyle"
- :itemSize="virtualItemHeight"
- :bufferSize="2"
- v-bind="$attrs"
- >
- <template #item="{ item, index }">
- <SimpleListItem
- :item="item"
- :index="index"
- :dataDisplayProp="dataDisplayProp"
- :colorProp="colorProp"
- :disabledProp="disabledProp"
- :showCheck="mode !== 'select'"
- :checked="checkedList.indexOf(item) >= 0"
- :overrideItem="Boolean($slots.itemContent)"
- @click="onItemPress(item, index)"
- >
- <template v-if="$slots.itemContent" #itemContent>
- <slot name="itemContent" :item="item" :index="index" />
- </template>
- </SimpleListItem>
- </template>
- <template #empty>
- <slot name="empty">
- <Empty :description="emptyText" />
- </slot>
- </template>
- <template #inner>
- <slot name="inner" />
- </template>
- </FixedVirtualList>
- <FlexCol v-else :innerStyle="innerStyle">
- <SimpleListItem
- v-for="(item, index) in data"
- :key="dataKey ? (item as Record<string, any>)[dataKey] : index"
- :item="item"
- :index="index"
- :dataDisplayProp="dataDisplayProp"
- :colorProp="colorProp"
- :disabledProp="disabledProp"
- :showCheck="mode !== 'select'"
- :checked="checkedList.indexOf(item) >= 0"
- :overrideItem="Boolean($slots.itemContent)"
- @click="onItemPress(item, index)"
- >
- <template v-if="$slots.itemContent" #itemContent>
- <slot name="itemContent" :item="item" :index="index" />
- </template>
- </SimpleListItem>
- <slot v-if="data.length == 0" name="empty">
- <Empty :description="emptyText" />
- </slot>
- <slot name="inner" />
- </FlexCol>
- </template>
- <script setup lang="ts" generic="T">
- import { computed, nextTick, provide, ref, watch, type ComputedRef, type Ref } from 'vue';
- import { useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
- import { DynamicColor, DynamicSize, DynamicSize2, DynamicVar } from '../theme/ThemeTools';
- import type { CheckBoxDefaultButtonProps } from '../form/CheckBoxDefaultButton.vue';
- import FlexCol from '../layout/FlexCol.vue';
- import Empty from '../feedback/Empty.vue';
- import FixedVirtualList from './FixedVirtualList.vue';
- import SimpleListItem from './SimpleListItem.vue';
- export interface SimpleListProps<T> {
- /**
- * 是否使用虚拟列表
- * @default false
- */
- virtual?: boolean;
- /**
- * 虚拟列表条目高度px
- * @default 40
- */
- virtualItemHeight?: number;
- /**
- * 条目的自定义样式
- */
- itemStyle?: ViewStyle;
- /**
- * 条目文字的自定义样式
- */
- textStyle?: TextStyle;
- /**
- * 选中的条目的自定义样式
- */
- checkedItemStyle?: ViewStyle;
- /**
- * 选中的条目文字的自定义样式
- */
- checkedTextStyle?: TextStyle;
- /**
- * 空数据时显示的文字
- */
- emptyText?: string,
- /**
- * 源数据
- */
- data: T[],
- /**
- * 数据项的唯一键名,用于vue循环优化
- */
- dataKey?: string,
- /**
- * 显示数据的prop,如果为空,则尝试直接把数据当 string 显示。
- */
- dataDisplayProp?: string,
- /**
- * 控制数据条目颜色的字段名称,为空则使用默认颜色。
- */
- colorProp?: string,
- /**
- * 控制是否禁用数据条目的字段名称,为空则不禁用。
- */
- disabledProp?: string,
- /**
- * 列表的选择模式
- *
- * * select 点击选择模式
- * * single-check 单选选择模式,条目右边有选择框
- * * mulit-check 多选选择模式,条目右边有选择框
- * @default 'select'
- */
- mode?: 'select'|'single-check'|'mulit-check',
- /**
- * 当列表显示选择框时,选择框的自定义属性
- * @default {
- * borderColor: 'border.cell',
- * checkColor: 'white',
- * color: 'primary',
- * size: 20,
- * iconSize: mode === 'single-check' ? 10 : 16,
- * type: mode === 'single-check' ? 'radio' : 'icon',
- * disabled: false,
- * }
- */
- checkProps?: CheckBoxDefaultButtonProps,
- /**
- * 当用使用选择框模式时,默认选中条目
- * @default []
- */
- checkedItems?: T[],
- innerStyle?: ViewStyle;
- }
- export interface SimpleListContext {
- itemStyle: ComputedRef<ViewStyle>;
- textStyle: ComputedRef<TextStyle>;
- checkedItemStyle: ComputedRef<ViewStyle>;
- checkedTextStyle: ComputedRef<TextStyle>;
- checkProps: ComputedRef<CheckBoxDefaultButtonProps>;
- pressedColor: ComputedRef<string>;
- }
- const props = withDefaults(defineProps<SimpleListProps<T>>(), {
- mode: 'select',
- virtual: false,
- virtualItemHeight: 40,
- });
- const emit = defineEmits([ 'itemClick', 'selectedItemChanged' ]);
- const themeContext = useTheme();
- const themeStyles = themeContext.useThemeStyles({
- item: {
- padding: DynamicSize2('SimpleListItemPaddingVertical', 'SimpleListItemPaddingHorizontal', 20, 35),
- fontSize: DynamicSize('SimpleListItemFontSize', 28),
- borderTopStyle: 'solid',
- borderTopWidth: DynamicSize('SimpleListItemBorderTopWidth', 0),
- borderTopColor: DynamicColor('SimpleListItemBorderColor', 'border.cell'),
- borderBottomStyle: 'solid',
- borderBottomWidth: DynamicSize('SimpleListItemBorderBottomWidth', 2),
- borderBottomColor: DynamicColor('SimpleListItemBorderColor', 'border.cell'),
- color: DynamicColor('SimpleListItemColor', 'text.content'),
- backgroundColor: DynamicColor('SimpleListItemBackgroundColor', 'white'),
- flexDirection: 'row',
- },
- itemText: {
- flex: 1,
- fontSize: DynamicSize('SimpleListItemTextFontSize', 28),
- fontWeight: DynamicSize('SimpleListItemTextFontWeight', 'normal'),
- color: DynamicColor('SimpleListItemTextColor', 'text.content'),
- },
- itemTextChecked: {
- fontSize: DynamicSize('SimpleListItemCheckedTextFontSize', 28),
- fontWeight: DynamicVar('SimpleListItemCheckedTextFontWeight', 'bold'),
- color: DynamicColor('SimpleListItemCheckedTextColor', 'primary'),
- },
- });
- const itemStyle = computed(() => ({
- ...themeStyles.item.value,
- ...props.itemStyle,
- } as ViewStyle));
- const textStyle = computed(() => ({
- ...themeStyles.itemText.value,
- ...props.textStyle,
- } as ViewStyle));
- const checkedItemStyle = computed(() => ({
- ...themeStyles.item.value,
- ...props.checkedItemStyle,
- } as ViewStyle));
- const checkedTextStyle = computed(() => ({
- ...themeStyles.itemText.value,
- ...themeStyles.itemTextChecked.value,
- ...props.checkedTextStyle,
- } as ViewStyle));
- const checkProps = computed(() => ({
- borderColor: 'border.cell',
- checkColor: 'white',
- color: 'primary',
- size: 40,
- iconSize: props.mode === 'single-check' ? 20 : 32,
- type: props.mode === 'single-check' ? 'radio' : 'icon',
- disabled: false,
- ...props.checkProps,
- } as CheckBoxDefaultButtonProps));
- const pressedColor = computed(() => {
- return themeContext.resolveThemeColor('SimpleListItemPressedColor', 'pressed.white')!;
- });
- provide<SimpleListContext>('SimpleListContext', {
- itemStyle,
- textStyle,
- checkedItemStyle,
- checkedTextStyle,
- checkProps,
- pressedColor,
- });
- const checkedList = ref(props.checkedItems || []) as Ref<T[]>;
- watch(() => props.checkedItems, (val) => {
- checkedList.value = val || [];
- });
- function onItemPress(item: T, index: number) {
- if (props.mode === 'single-check') {
- checkedList.value = ([ item ]);
- emit('selectedItemChanged', [ item ]);
- } else if (props.mode === 'mulit-check') {
- checkedList.value = ((prev) => {
- let arr : any[];
- if (prev.indexOf(item) >= 0)
- arr = prev.filter(k => {
- if (props.dataKey)
- return (k as any)[props.dataKey] !== (item as any)[props.dataKey];
- return k !== item;
- });
- else
- arr = prev.concat([ item ]);
- nextTick(() => {
- emit('selectedItemChanged', arr);
- console.log('selectedItemChanged', arr);
-
- });
- return arr;
- })(checkedList.value);
- }
- emit('itemClick', item, index);
- }
- </script>
|