Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
<template>
<l-picker
v-model="innerValue"
:cancelBtn="cancelBtn"
:cancelStyle="cancelStyle"
:confirmBtn="confirmBtn"
:confirmStyle="confirmStyle"
:title="title"
:titleStyle="titleStyle"
:loading="loading"
:loadingColor="loadingColor"
:loadingMaskColor="loadingMaskColor"
:loadingSize="loadingSize"
:itemHeight="itemHeight"
:itemColor="itemColor"
:itemFontSize="itemFontSize"
:itemActiveColor="itemActiveColor"
:indicatorStyle="indicatorStyle"
:bgColor="bgColor"
:groupHeight="groupHeight"
:radius="radius"
:resetIndex="resetIndex"
:columns="innerColumns"
@cancel="onCancel"
@confirm="onConfirm"
@pick="onPick">
</l-picker>
</template>
<script lang="uts" setup>
/**
* Cascade 级联选择器组件
* @description 支持多层级联数据选择,适用于地址选择、分类选择等场景
* <br>插件类型LCascadeComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-cascade
*
* @property {string} cancelBtn 取消按钮文字(默认:"取消"
* @property {string} cancelStyle 取消按钮样式支持CSS字符串
* @property {string} confirmBtn 确定按钮文字(默认:"确定"
* @property {string} confirmStyle] 确定按钮样式支持CSS字符串
* @property {string} title 标题文字
* @property {string} titleStyle 标题样式支持CSS字符串
* @property {UTSJSONObject} keys 字段别名配置参考KeysType结构
* @property {UTSJSONObject]} columns 级联数据源(必填)
* @property {PickerValue]} modelValue 选中值支持v-model
* @property {PickerValue]} defaultValue 默认选中值
* @property {PickerValue]} value 选中值(兼容旧版)
* @property {boolean} loading 是否显示加载状态
* @property {string} loadingColor 加载图标颜色(默认:主题色)
* @property {string} loadingMaskColor 加载遮罩颜色默认rgba(255,255,255,0.8)
* @property {string} loadingSize 加载图标尺寸支持CSS单位
* @property {string} itemHeight 选项行高默认44px
* @property {string} itemColor 选项文字颜色(默认:#333
* @property {string} itemFontSize 选项文字大小默认16px
* @property {string} itemActiveColor 选中项高亮颜色(默认:主题色)
* @property {string} indicatorStyle 指示器样式支持CSS字符串
* @property {string} bgColor 背景颜色(默认:#ffffff
* @property {string} groupHeight 选项组高度默认240px
* @property {string} radius 圆角半径支持CSS单位
* @property {boolean} resetIndex 是否重置选中索引(用于动态更新数据时)
* @event {Function} change 选项变化时触发(返回当前选中路径)
* @event {Function} cancel 点击关闭时触发
* @event {Function} confirm 点击确定时触发(返回最终选中值)
*/
import { PickerValue, PickerColumn, PickerColumnItem, PickerPickEvent, PickerConfirmEvent } from '../l-picker/type';
import { CascadeProps } from './type';
import { parseKeys, formatCascadeColumns } from './utils';
const emit = defineEmits(['change', 'cancel', 'pick', 'confirm', 'update:modelValue', 'update:value'])
const props = withDefaults(defineProps<CascadeProps>(), {
columns: [] as PickerColumnItem[],
loading: false,
resetIndex: false,
loadingSize: '64rpx'
})
const keys = parseKeys(props.keys)
const innerValue = ref<PickerValue[]>(props.value ?? props.modelValue ?? props.defaultValue ?? []);
// const innerValue = computed({
// set(value : PickerValue[]) {
// curValueArray.value = value;
// emit('update:modelValue', value)
// },
// get() : PickerValue[] {
// return props.value ?? props.modelValue ?? curValueArray.value
// }
// } as WritableComputedOptions<PickerValue[]>)
const innerColumns = computed(() : PickerColumn[] => {
return formatCascadeColumns(props.columns, keys, innerValue)
})
const onPick = ({ values, column, index } : PickerPickEvent) => {
innerValue.value = [...values]
}
const onConfirm = (options : PickerConfirmEvent) => {
emit('confirm', options)
}
const onCancel = () => {
emit('cancel')
}
watchEffect(()=>{
emit('update:modelValue', innerValue.value)
})
</script>
<style>
</style>

View File

@@ -0,0 +1,124 @@
<template>
<l-picker
v-model="innerValue"
:cancelBtn="cancelBtn"
:cancelStyle="cancelStyle"
:confirmBtn="confirmBtn"
:confirmStyle="confirmStyle"
:title="title"
:titleStyle="titleStyle"
:loading="loading"
:loadingColor="loadingColor"
:loadingMaskColor="loadingMaskColor"
:loadingSize="loadingSize"
:itemHeight="itemHeight"
:itemColor="itemColor"
:itemFontSize="itemFontSize"
:itemActiveColor="itemActiveColor"
:indicatorStyle="indicatorStyle"
:bgColor="bgColor"
:groupHeight="groupHeight"
:radius="radius"
:resetIndex="resetIndex"
:columns="innerColumns"
@cancel="onCancel"
@confirm="onConfirm"
@pick="onPick">
</l-picker>
</template>
<script lang="ts">
// @ts-nocheck
/**
* Cascade 级联选择器组件
* @description 支持多层级联数据选择,适用于地址选择、分类选择等场景
* <br>插件类型LCascadeComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-cascade
*
* @property {string} cancelBtn 取消按钮文字(默认:"取消"
* @property {string} cancelStyle 取消按钮样式支持CSS字符串
* @property {string} confirmBtn 确定按钮文字(默认:"确定"
* @property {string} confirmStyle] 确定按钮样式支持CSS字符串
* @property {string} title 标题文字
* @property {string} titleStyle 标题样式支持CSS字符串
* @property {UTSJSONObject} keys 字段别名配置参考KeysType结构
* @property {UTSJSONObject]} columns 级联数据源(必填)
* @property {PickerValue]} modelValue 选中值支持v-model
* @property {PickerValue]} defaultValue 默认选中值
* @property {PickerValue]} value 选中值(兼容旧版)
* @property {boolean} loading 是否显示加载状态
* @property {string} loadingColor 加载图标颜色(默认:主题色)
* @property {string} loadingMaskColor 加载遮罩颜色默认rgba(255,255,255,0.8)
* @property {string} loadingSize 加载图标尺寸支持CSS单位
* @property {string} itemHeight 选项行高默认44px
* @property {string} itemColor 选项文字颜色(默认:#333
* @property {string} itemFontSize 选项文字大小默认16px
* @property {string} itemActiveColor 选中项高亮颜色(默认:主题色)
* @property {string} indicatorStyle 指示器样式支持CSS字符串
* @property {string} bgColor 背景颜色(默认:#ffffff
* @property {string} groupHeight 选项组高度默认240px
* @property {string} radius 圆角半径支持CSS单位
* @property {boolean} resetIndex 是否重置选中索引(用于动态更新数据时)
* @event {Function} change 选项变化时触发(返回当前选中路径)
* @event {Function} cancel 点击关闭时触发
* @event {Function} confirm 点击确定时触发(返回最终选中值)
*/
import { defineComponent, ref, computed} from '@/uni_modules/lime-shared/vue';
import { PickerValue, PickerColumn, PickerColumnItem, PickerPickEvent} from '../l-picker/type';
import { CascadeProps } from './type';
import { parseKeys, formatCascadeColumns } from './utils';
import cascadeProps from '../l-picker/props';
export default defineComponent({
name: 'l-cascade',
props:cascadeProps,
emits: ['change', 'cancel', 'pick', 'confirm', 'update:modelValue', 'update:value', 'input'],
setup(props, {emit}) {
type UTSJSONObject = Record<string, any>
const keys = parseKeys(props.keys)
const curValueArray = ref(props.value || props.modelValue || props.defaultValue||[]);
const innerValue = computed({
set(value: PickerValue[]) {
curValueArray.value = value;
emit('update:modelValue', value)
// #ifdef VUE2
emit('input', value)
// #endif
},
get(): PickerValue[]{
return props.value || props.modelValue || curValueArray.value
}
} as WritableComputedOptions<PickerValue[]>)
const innerColumns = computed((): PickerColumn[] => {
return formatCascadeColumns(props.columns, keys, innerValue)
})
const onPick = ({values, column, index} : PickerPickEvent) => {
innerValue.value = values
}
const onConfirm = (options: PickerConfirmEvent) => {
emit('confirm', options)
}
const onCancel = () => {
emit('cancel')
}
return {
innerValue,
innerColumns,
onPick,
onConfirm,
onCancel
}
}
})
</script>
<style>
</style>

View File

@@ -0,0 +1,57 @@
// @ts-nocheck
import {PickerColumn, PickerColumnItem ,PickerValue} from '../l-picker/type';
export type KeysType = {
value: string;
label: string;
children:string;
disabled?: string;
}
export interface CascadeProps {
/**
* 取消按钮文字
*/
cancelBtn ?: string;
cancelStyle ?: string;
/**
* 确定按钮文字
*/
confirmBtn ?: string;
confirmStyle ?: string;
/**
* 标题
*/
title ?: string;
titleStyle ?: string;
/**
* 用来定义 value / label 在 `options` 中对应的字段别名
*/
keys?: UTSJSONObject;
/**
* 配置每一列的选项
*/
columns : UTSJSONObject[];
/**
* 选中值
*/
modelValue ?: PickerValue[];
defaultValue ?: PickerValue[];
value ?: PickerValue[];
/**
* 是否为加载状态
*/
loading: boolean;
loadingColor?: string;
loadingMaskColor ?: string;
loadingSize: string;
itemHeight?: string;
itemColor?: string;
itemFontSize?: string;
itemActiveColor?: string;
indicatorStyle?: string;
bgColor?: string;
groupHeight?: string;
radius?: string;
resetIndex: boolean
}

View File

@@ -0,0 +1,122 @@
// @ts-nocheck
import { clamp } from '@/uni_modules/lime-shared/clamp'
import { PickerValue, PickerColumn, PickerColumnItem, PickerPickEvent } from '../l-picker/type';
import { KeysType } from './type';
// #ifndef UNI-APP-X
import type { Ref } from '@/uni_modules/lime-shared/vue'
type UTSJSONObject = Record<string, any>
// #endif
/**
* 解析键配置对象,生成用于访问选项属性的键映射。
*
* @param {UTSJSONObject|null} keys - 配置对象,包含 label、value 和 children 的键名。
* @returns {KeysType} 包含 label、value 和 children 键名的映射对象。
*/
export function parseKeys(keys : UTSJSONObject | null) : KeysType {
const _labelKey = `${keys?.['label'] ?? 'label'}`
const _valueKey = `${keys?.['value'] ?? 'value'}`
const _childrenKey = `${keys?.['children'] ?? 'children'}`
return {
label: _labelKey,
value: _valueKey,
children: _childrenKey,
} as KeysType
}
/**
* 获取选项数组中第一个未被禁用的选项。
*
* @param {UTSJSONObject[]} options - 选项对象数组。
* @returns {UTSJSONObject|null} 第一个未被禁用的选项对象,如果所有选项都被禁用则返回第一个选项。
*/
export const getFirstEnabledOption = (
options : UTSJSONObject[],
) : UTSJSONObject | null =>
options.find((option) : boolean => option['disabled'] != true) ?? options[0];
/**
* 在选项数组中查找指定索引之后或之前的第一个未被禁用的选项的索引。
*
* @param {UTSJSONObject[]} options - 选项对象数组。
* @param {number} index - 当前索引位置。
* @returns {number} 第一个未被禁用的选项的索引,如果不存在则返回 0。
*/
export function findIndexOfEnabledOption(
options : UTSJSONObject[],
index : number,
) {
index = clamp(index, 0, options.length);
// for (let i = index; i < options.length; i++) {
// if (options[i]['disabled'] != true) return i;
// }
// for (let i = index - 1; i >= 0; i--) {
// if (options[i]['disabled'] != true) return i;
// }
// return 0;
return index
}
/**
* 根据指定的值在选项数组中查找对应的选项对象。
*
* @param {UTSJSONObject[]} options - 选项对象数组。
* @param {PickerValue} value - 要查找的值。
* @param {KeysType} fields - 包含 label、value 和 children 键名的映射对象。
* @returns {UTSJSONObject|null} 找到的选项对象,如果未找到则返回 null。
*/
export function findOptionByValue(
options : UTSJSONObject[],
value : PickerValue,
fields : KeysType,
) : UTSJSONObject | null {
const index = options.findIndex((option) : boolean => option[fields.value] == value);
const enabledIndex = findIndexOfEnabledOption(options, index);
return options[enabledIndex];
}
/**
* 格式化级联选择器的列数据。
*
* @param {UTSJSONObject[]} columns - 初始的列数据数组。
* @param {KeysType} fields - 包含 label、value 和 children 键名的映射对象。
* @param {Ref<PickerValue[]>} selectedValues - 当前选中的值数组。
* @returns {PickerColumn[]} 格式化后的级联列数据数组。
*/
export function formatCascadeColumns(
columns : UTSJSONObject[],
fields : KeysType,
selectedValues : Ref<PickerValue[]>
) {
const formatted : PickerColumn[] = [];
let cursor : UTSJSONObject | null = {};
cursor![fields.children] = columns;
let columnIndex = 0;
while (cursor != null && cursor[fields.children] != null) {
const options : UTSJSONObject[] = cursor[fields.children]! as UTSJSONObject[];
const value = selectedValues.value.length > columnIndex ? selectedValues.value[columnIndex] : null;
cursor = value != null
? findOptionByValue(options, value, fields)
: null;
if (cursor == null && options.length > 0) {
const firstValue = getFirstEnabledOption(options)![fields.value]!;
cursor = findOptionByValue(options, firstValue, fields);
}
columnIndex++;
formatted.push(
options.map((option : UTSJSONObject) : PickerColumnItem => ({
label: `${option['label']}`,
value: `${option['value']}`,
} as PickerColumnItem))
);
}
return formatted;
}

View File

@@ -0,0 +1,72 @@
@import '~@/uni_modules/lime-style/index.scss';
$prefix: l !default;
$item: #{$prefix}-picker-item;
$picker-group-height: create-var(picker-group-height, 400rpx);
$picker-indicator-bg-color: create-var(picker-indicator-bg-color, $fill-4);
$picker-indicator-border-radius: create-var(picker-indicator-border-radius, 12rpx);
$picker-item-height: create-var(picker-item-height, 50px);
$picker-item-active-color: create-var(picker-item-active-color, $text-color-1);
$picker-item-active-font-weight: create-var(picker-item-active-font-weight, 700);
$picker-item-color: create-var(picker-item-color, $text-color-1);
$picker-item-font-size: create-var(picker-item-font-size, $font-size-md);
/* #ifndef APP-ANDROID || APP-IOS || APP-HARMONY */
:host {
flex: 1;
display: flex;
}
/* #endif */
.#{$item} {
&__group {
/* #ifdef WEB */
:deep(.uni-picker-view-content) {
// 300ms
transition-duration: var(--picker-duration, 0);
}
/* #endif */
flex: 1;
// height: $picker-group-height;
&-item {
height: $picker-item-height;
line-height: $picker-item-height;
text-align: center;
/* #ifndef APP-ANDROID || APP-IOS || APP-HARMONY */
display: flex;
justify-content: center;
/* #endif */
transition-duration: 100ms;
transition-property: font-weight, color;
transition-timing-function: linear;
font-weight: 400;
color: $picker-item-color;
font-size: $picker-item-font-size;
white-space: nowrap;
&--active {
color: $picker-item-active-color;
font-weight: $picker-item-active-font-weight;
}
}
}
&__indicator {
// position: absolute;
left: 0rpx;
right: 0rpx;
width: auto;
height: $picker-item-height;
// top: 144rpx;
pointer-events: none;
background-color: $picker-indicator-bg-color;
// border-radius: $picker-indicator-border-radius;
/* #ifndef APP-ANDROID || APP-IOS || APP-HARMONY */
&:after,&:before{
display: none;
}
/* #endif */
}
}

View File

@@ -0,0 +1,234 @@
<template>
<picker-view
class="l-picker-item__group"
:style="{opacity: options.length > 0 ? 1 : 0}"
:indicator-style="indicatorStyles"
:value="innerIndex"
@change="handlePick"
indicator-class="l-picker-item__indicator">
<picker-view-column class="l-picker-item__wrapper">
<!-- #ifdef APP-ANDROID -->
<view ref="itemRef"></view>
<!-- #endif -->
<!-- #ifndef APP-ANDROID -->
<text class="l-picker-item__group-item" v-for="(item, i) in options"
:style="[itemStyles, curIndex == i ? itemActiveStyles: {}]"
:class="{'l-picker-item__group-item--active': curIndex == i}" :key="item.value">{{item.label}}</text>
<!-- #endif -->
</picker-view-column>
</picker-view>
</template>
<script lang="uts" setup>
/**
* PickerItem 选择器子项组件
* @description 用于构建多列选择器的单个列项,通常作为 Picker 组件的子组件使用
* <br>插件类型LPickerItemComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-picker
*
* @property {PickerColumnItem[]} options 当前列的选项列表(必填)
* @property {PickerValue} value 当前选中值
* @property {number} column 列索引标识从0开始计数
* @property {string | number} name 列名称标识符
*/
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
import { clamp } from '@/uni_modules/lime-shared/clamp'
import { PickerItemProps, ManageChildInList, OnPick, UpdateItems } from './type';
import { PickerColumnItem, PickerValue } from '../l-picker/type';
const instance = getCurrentInstance()!;
const props = withDefaults(defineProps<PickerItemProps>(), {
options: [] as PickerColumnItem[],
column: -1,
resetIndex: false
})
const picker = inject<LPickerComponentPublicInstance | null>('limePicker', null);
const pickerItemInstanceArray = inject<LPickerItemComponentPublicInstance[] | null>('limePickerItems', null);
const manageChildInList = inject<ManageChildInList | null>('limePickerManageChildInList', null);
manageChildInList?.(instance.proxy! as LPickerItemComponentPublicInstance, true)
const onPick = inject<OnPick | null>('limePickerOnPick', null);
const updateItems = inject<UpdateItems | null>('limePickerUpdateItems', null);
// web 如果初始是0 当数据加载后 无法指向0
// #ifdef WEB
const curIndex = ref(-1)
// #endif
// #ifndef WEB
const curIndex = ref(0)
// #endif
const curValue = ref<PickerValue | null>(props.value);
const column = computed(() : number => props.column != -1 ? props.column : pickerItemInstanceArray?.indexOf(instance.proxy! as LPickerItemComponentPublicInstance) ?? props.column);
const elementPosition = computed(() : boolean[] => {
const totalElements = pickerItemInstanceArray?.length ?? 0;
return [column.value == 0, column.value == totalElements - 1]
});
const innerIndex = computed(() : number[] => [curIndex.value])
const indicatorStyles = computed(() : string => {
const [isFirst, isLast] = elementPosition.value
let style = ``
if(isFirst) {
style+= 'border-top-left-radius:12rpx; border-bottom-left-radius:12rpx;'
}
if(isLast) {
style+= 'border-top-right-radius:12rpx; border-bottom-right-radius:12rpx;'
}
return `
${style}
height: ${picker?.itemHeight ?? '50px'};
background-color: rgba(0, 0, 0, 0.04); ${picker?.indicatorStyle}`
})
const itemStyles = computed(() : Map<string, any> => {
const style = new Map<string, any>();
if (picker?.itemColor != null) {
style.set('color', picker.itemColor!)
}
if (picker?.itemFontSize != null) {
style.set('font-size', picker.itemFontSize!)
}
if(picker?.itemHeight != null) {
style.set('line-height', picker.itemHeight!)
style.set('height', picker.itemHeight!)
}
return style
})
const itemActiveStyles = computed(() : Map<string, any> => {
const style = new Map<string, any>();
if (picker?.itemActiveColor != null) {
style.set('color', picker.itemActiveColor!)
}
if (picker?.itemActiveFontWeight != null) {
style.set('font-weight', picker.itemActiveFontWeight!)
}
return style
})
const getIndexByValue = (val : PickerValue | null) => {
let defaultIndex = 0;
if (val != null) {
defaultIndex = props.options.findIndex((item) => item.value == val);
}
return defaultIndex < 0 ? 0 : defaultIndex;
};
const setIndex = (index : number) => {
let lastIndex = curIndex.value
let _index = clamp(index, 0, props.options.length - 1)
if(props.options.length > _index) {
curIndex.value = _index;
curValue.value = props.options[_index].value
}
// #ifdef WEB
if(lastIndex == _index || lastIndex == -1) return
if (instance.proxy!.$el) {
const childs = Array.from(instance.proxy!.$el.parentElement.children).slice(column.value + 1);
childs.forEach((child) => {
(child as HTMLElement).style.setProperty('--picker-duration', '300ms');
setTimeout(function () {
(child as HTMLElement).style.setProperty('--picker-duration', '0ms');
}, 299);
})
}
// #endif
}
const setValue = (value : PickerValue | null) => {
if (value == curValue.value) return
curValue.value = value
const index = getIndexByValue(value)
setIndex(index)
}
const setOptions = () => { }
const setUpdateItems = () => {
const index = clamp(curIndex.value, 0, props.options.length - 1)
const curItem = props.options.length > index ? props.options[index] : null;
if (curItem == null) return
updateItems?.(curItem, index, column.value);
}
const handlePick = (e : UniPickerViewChangeEvent) => {
if(props.options.length == 0) return
const index = clamp(e.detail.value[0], 0, props.options.length - 1);
const curItem = props.options[index];
if(index == curIndex.value) return
setIndex(index)
onPick?.(curItem, index, column.value);
}
const stopValue = watch(() : PickerValue | null => props.value, (v : PickerValue | null) => {
setValue(v);
setUpdateItems();
}, { immediate: true })
// #ifdef APP-ANDROID
const itemRef = ref<UniElement | null>(null)
const updateItemStyle = () => {
if (itemRef.value != null) {
const ctx = itemRef.value!.getDrawableContext()!
const height = unitConvert(picker?.itemHeight ?? 50);
const fontSize = unitConvert(picker?.itemFontSize ?? '32rpx');
const rect = itemRef.value!.getBoundingClientRect()
const x = itemRef.value!.offsetWidth / 2;
const itemActiveFontWeight = picker?.itemActiveFontWeight ?? 700
itemRef.value!.style.setProperty('height', height * props.options.length);
ctx.reset()
ctx.fillStyle = picker?.itemActiveColor ?? 'rgba(0,0,0,0.88)';
ctx.font = `${fontSize}px`;
ctx.textAlign = 'center'
ctx.lineWidth = 0.5
props.options.forEach((item, index) => {
const y = height * index + fontSize + (height - fontSize) * 0.4; //(height - fontSize * 1.1)
ctx.fillText(item.label, x, y)
if (index == curIndex.value && itemActiveFontWeight > 600) {
ctx.strokeText(item.label, x, y)
}
})
ctx.update()
}
}
// 防止更新后尺寸不对
watchEffect(()=> {
if(curIndex.value > 0){}
nextTick(updateItemStyle)
})
const stop = watch(() : PickerColumnItem[] => props.options, (v : PickerColumnItem[], o : PickerColumnItem[]) => {
nextTick(updateItemStyle)
})
const resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
updateItemStyle()
})
const stopWatch = watch(():UniElement|null => itemRef.value, (el:UniElement|null) => {
if(el== null) return
resizeObserver.observe(el)
})
// onMounted(() => {
// nextTick(updateItemStyle)
// })
// #endif
onBeforeUnmount(() => {
manageChildInList?.(instance.proxy! as LPickerItemComponentPublicInstance, false)
// #ifdef APP-ANDROID
stop()
stopWatch()
resizeObserver.disconnect()
// #endif
})
// onMounted(()=>{
// if(props.options.length > 0 && curIndex.value == -1) {
// curIndex.value = 0
// }
// })
defineExpose({
setIndex,
setValue,
// setOptions,
// setUpdateItems,
getIndexByValue
})
</script>
<style lang="scss">
@import './index';
</style>

View File

@@ -0,0 +1,204 @@
<template>
<picker-view
class="l-picker-item__group"
:style="{opacity: options.length > 0 ? 1 : 0}"
:indicator-style="indicatorStyles"
:value="innerIndex"
@change="handlePick"
indicator-class="l-picker-item__indicator">
<picker-view-column class="l-picker-item__wrapper">
<text
class="l-picker-item__group-item"
v-for="(item, i) in options"
:style="[itemStyles, curIndex == i ? itemActiveStyles: {}]"
:class="{'l-picker-item__group-item--active': curIndex == i}"
:key="item.value">{{item.label}}</text>
</picker-view-column>
</picker-view>
</template>
<script lang="ts">
// @ts-nocheck
/**
* PickerItem 选择器子项组件
* @description 用于构建多列选择器的单个列项,通常作为 Picker 组件的子组件使用
* <br>插件类型LPickerItemComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-picker
*
* @property {PickerColumnItem[]} options 当前列的选项列表(必填)
* @property {PickerValue} value 当前选中值
* @property {number} column 列索引标识从0开始计数
* @property {string | number} name 列名称标识符
*/
import { defineComponent, getCurrentInstance, inject, ref, computed, watch, onBeforeUnmount } from '@/uni_modules/lime-shared/vue';
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert';
import { clamp } from '@/uni_modules/lime-shared/clamp'
import type { PickerItemProps, ManageChildInList, OnPick, UpdateItems } from './type';
import type { PickerColumnItem, PickerValue } from '../l-picker/type';
import pickerItemProps from './props';
export default defineComponent({
name: 'l-picker-item',
props: pickerItemProps,
setup(props, {expose}) {
const instance = getCurrentInstance()!;
const picker = inject<LPickerComponentPublicInstance|null>('limePicker', null);
const pickerItemInstanceArray = inject<Ref<LPickerItemComponentPublicInstance[]>|null>('limePickerItems', null);
const manageChildInList = inject<ManageChildInList|null>('limePickerManageChildInList', null);
manageChildInList?.(instance.proxy! as LPickerItemComponentPublicInstance , true)
const onPick = inject<OnPick|null>('limePickerOnPick', null);
const updateItems = inject<UpdateItems|null>('limePickerUpdateItems', null);
const curIndex = ref(-1)
const curValue = ref<PickerValue|null>(null);
const column = computed(() : number => props.column != -1 ? props.column : pickerItemInstanceArray?.value.indexOf(instance.proxy! as LPickerItemComponentPublicInstance) ?? props.column);
const elementPosition = computed(() : boolean[] => {
const totalElements = pickerItemInstanceArray?.value.length || 0;
return [column.value == 0, column.value == totalElements - 1]
});
const innerIndex = computed(():number[] => [curIndex.value])
const indicatorStyles = computed(() : string => {
const [isFirst, isLast] = elementPosition.value
let style = ``
if(isFirst) {
style+= 'border-top-left-radius:12rpx; border-bottom-left-radius:12rpx;'
}
if(isLast) {
style+= 'border-top-right-radius:12rpx; border-bottom-right-radius:12rpx;'
}
return `
${style}
height: ${picker?.itemHeight || '50px'};
background-color: rgba(0, 0, 0, 0.04); ${picker?.indicatorStyle}`
})
const itemStyles = computed(()=>{
const style:Record<string, any> = {};
if(picker?.itemColor) {
style['color'] = picker.itemColor!
}
if(picker?.itemFontSize) {
style['font-size'] = picker.itemFontSize!
}
return style
})
const itemActiveStyles = computed(() =>{
const style:Record<string, any> = {};
if(picker?.itemActiveColor) {
style['color'] = picker.itemActiveColor!
}
if (picker?.itemActiveFontWeight) {
style['font-weight'] = picker.itemActiveFontWeight!
}
return style
})
const getIndexByValue = (val: PickerValue | null) => {
let defaultIndex = 0;
if (val != null) {
defaultIndex = props.options.findIndex((item) => item.value == val);
}
return defaultIndex < 0 ? 0 : defaultIndex;
};
const setIndex = (index: number) =>{
let lastIndex = curIndex.value
let _index = clamp(index, 0, props.options.length - 1)
if(props.options.length > _index) {
curIndex.value = _index;
curValue.value = props.options[_index].value
}
// #ifdef WEB
if(lastIndex == _index || lastIndex == -1) return
if(instance.proxy!.$el) {
const childs = Array.from(instance.proxy!.$el.parentElement.children).slice(column.value + 1);
childs.forEach((child) => {
(child as HTMLElement).style.setProperty('--picker-duration', '300ms');
setTimeout(function() {
(child as HTMLElement).style.setProperty('--picker-duration', '0ms');
}, 299);
})
}
// #endif
}
const setValue = (value: PickerValue|null) =>{
if(value == curValue.value) return
curValue.value = value
const index = getIndexByValue(value)
setIndex(index)
}
const setOptions = () => {}
const setUpdateItems = () => {
const curItem = props.options.length > curIndex.value ? props.options[curIndex.value] : null;
if(curItem == null) return
updateItems?.(curItem, curIndex.value, column.value);
}
const handlePick = (e: UniPickerViewChangeEvent) => {
if(props.options.length == 0) return
const index = clamp(e.detail.value[0], 0, props.options.length - 1);
const curItem = props.options[index];
if(index == curIndex.value) return
setIndex(index)
onPick?.(curItem, index, column.value);
}
// const stop = watch(():PickerColumnItem[] => props.options, ()=> {
// if(JSON.stringify(o) == JSON.stringify(v)) return
// const index = (picker?.resetIndex || false) ? 0 : Math.max( Math.min(props.options.length - 1, curIndex.value), 0)
// const curItem = props.options[index];
// setIndex(index)
// setUpdateItems();
// // nextTick(()=>{
// // onPick?.(curItem, index, column.value);
// // })
// }) // ,{immediate: true}
const stopValue = watch(():PickerValue|null => props.value, (v : PickerValue|null)=>{
setValue(v);
setUpdateItems();
},{immediate: true})
onBeforeUnmount(()=>{
manageChildInList?.(instance.proxy! as LPickerItemComponentPublicInstance, false)
// stop()
stopValue()
})
// #ifdef VUE3
expose({
setIndex,
setValue,
// setOptions,
// setUpdateItems,
getIndexByValue
})
// #endif
return {
indicatorStyles,
innerIndex,
curIndex,
handlePick,
itemStyles,
itemActiveStyles,
// #ifdef VUE2
setIndex,
setValue,
getIndexByValue
// #endif
}
}
})
</script>
<style lang="scss">
@import './index';
</style>

View File

@@ -0,0 +1,15 @@
// @ts-nocheck
export default {
options: {
type: Array,
default: () => []
},
value: {
type: [String, Number],
default: null
},
column: {
type: Number,
default: -1
}
}

View File

@@ -0,0 +1,13 @@
// @ts-nocheck
import type{ PickerColumnItem, PickerValue } from '../l-picker/type';
export type ManageChildInList = (child: LPickerItemComponentPublicInstance, shouldAdd: boolean) => void;
export type OnPick = (value: PickerValue, index:number, column: number) => void;
export type UpdateItems = (value: PickerValue, index:number, column: number) => void;
export interface PickerItemProps {
options: PickerColumnItem[]
value?: PickerValue;
column: number
name?: string|number
}

View File

@@ -0,0 +1,121 @@
@import '~@/uni_modules/lime-style/index.scss';
$prefix: l !default;
$picker: #{$prefix}-picker;
$picker-border-radius: create-var(picker-border-radius, 24rpx);
$picker-bg-color: create-var(picker-bg-color, $bg-color-container);
$picker-toolbar-height: create-var(picker-toolbar-height, 116rpx);
$picker-cancel-color: create-var(picker-cancel-color, $text-color-2);
$picker-confirm-color: create-var(picker-confirm-color, $primary-color);
$picker-button-font-size: create-var(picker-button-font-size, 16px);
$picker-title-font-size: create-var(picker-title-font-size, 18px);
$picker-title-font-weight: create-var(picker-title-font-weight, 700);
$picker-title-line-height: create-var(picker-title-line-height, 52rpx);
$picker-title-color: create-var(picker-title-color, $text-color-1);
$picker-group-height: create-var(picker-group-height, 400rpx);
$picker-indicator-bg-color: create-var(picker-indicator-bg-color, $fill-4);
$picker-indicator-border-radius: create-var(picker-indicator-border-radius, 12rpx);
$picker-item-height: create-var(picker-item-height, 50px);
$picker-item-active-color: create-var(picker-item-active-color, $text-color-1);
$picker-loading-mask-color: create-var(picker-loading-mask-color, rgba(255,255,255,.9));
$picker-loading-color: create-var(picker-loading-color, $primary-color);
.#{$picker} {
position: relative;
background-color: $picker-bg-color;
border-top-left-radius: $picker-border-radius;
border-top-right-radius: $picker-border-radius;
&__toolbar {
display: flex;
align-items: center;
justify-content: space-between;
overflow: hidden;
height: $picker-toolbar-height;
flex-direction: row;
position: relative;
}
&__title {
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
// flex: 1;
// width: 100%;
text-align: center;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: $picker-title-color;
// line-height: $picker-toolbar-height;
line-height: $picker-title-line-height;
font-weight: $picker-title-font-weight;
font-size: $picker-title-font-size;
}
&__cancel,
&__confirm {
/* #ifndef APP-ANDROID || APP-IOS || APP-HARMONY */
display: flex;
align-items: center;
justify-content: center;
user-select: none;
/* #endif */
font-size: $picker-button-font-size;
line-height: $picker-toolbar-height;
height: 100%;
padding: 0 $spacer;
}
&__cancel {
color: $picker-cancel-color;
}
&__confirm {
color: $picker-confirm-color;
}
&__main {
display: flex;
height: $picker-group-height;
flex-direction: row;
padding: 0 $spacer-xs;
}
&__mask {
}
&__empty {
pointer-events: none;
justify-content: center;
align-items: center;
display: flex;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 3;
}
&__loading {
z-index: 3;
// color: $picker-loading-color;
background: $picker-loading-mask-color;
// background-color: red;
justify-content: center;
align-items: center;
display: flex;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0
}
}

View File

@@ -0,0 +1,292 @@
<template>
<view class="l-picker" :style="[styles]" ref="pickerRef">
<view class="l-picker__toolbar" :key="ohosShow" v-if="cancelBtn != null || title != null || confirmBtn != null">
<text class="l-picker__cancel" :key="ohosShow" :style="cancelStyle??''" v-if="cancelBtn != null"
@click="onCancel">{{cancelBtn}}</text>
<text class="l-picker__title" :key="ohosShow" :style="titleStyle??''">{{title}}</text>
<text class="l-picker__confirm" :key="ohosShow" :style="confirmStyle??''" v-if="confirmBtn != null"
@click="onConfirm">{{confirmBtn}}</text>
</view>
<slot name="header"></slot>
<view class="l-picker__main" :style="[groupHeight != null ? { height: groupHeight}: {}]">
<slot>
<l-picker-item v-for="(options, i) in props.columns" :options="options" :key="i" :column="i"
:value="pickerValue.length > i ? pickerValue[i]: null"></l-picker-item>
</slot>
<view class="l-picker__empty" v-if="isEmpty">
<slot name="empty"></slot>
</view>
</view>
<slot name="footer" />
<view class="l-picker__loading" ref="loadingRef" v-if="loading"
:style="[loadingMaskColor != null ? {background: loadingMaskColor}: {}]">
<!-- #ifndef APP -->
<l-loading :size="loadingSize" :color="loadingColor"></l-loading>
<!-- #endif -->
</view>
</view>
</template>
<script lang="uts" setup>
/**
* Picker 选择器组件
* @description 多列数据选择器,支持级联数据展示和自定义样式配置
* <br>插件类型LPickerComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-picker
*
* @property {string} cancelBtn 取消按钮文字
* @property {string | UTSJSONObject} cancelStyle 取消按钮样式
* @property {string} confirmBtn 确定按钮文字
* @property {string | UTSJSONObject} confirmStyle 确定按钮样式
* @property {string} title 标题文字
* @property {string | UTSJSONObject} titleStyle 标题样式
* @property {UTSJSONObject} keys 字段别名配置(例:{value: 'id', label: 'name'}
* @property {PickerColumn[]} columns 选择器列数据(必填)
* @property {PickerValue[]} modelValue 选中值支持v-model
* @property {PickerValue[]} defaultValue 默认选中值
* @property {PickerValue[]} value 选中值(兼容旧版)
* @property {boolean} loading 是否显示加载状态
* @property {string} loadingColor 加载图标颜色
* @property {string} loadingMaskColor 加载遮罩颜色
* @property {string} loadingSize 加载图标尺寸
* @property {string} itemHeight 选项行高度
* @property {string} itemColor 选项文字颜色
* @property {string} itemFontSize 选项字体大小
* @property {string} itemActiveColor 选中项颜色
* @property {string} indicatorStyle 指示器样式
* @property {string} bgColor 背景颜色
* @property {string} groupHeight 选项组高度
* @property {string} radius 圆角半径
* @property {boolean} resetIndex 是否重置选中索引
*
* @event {Function} confirm 点击确定时触发事件参数PickerConfirmEvent
* @event {Function} cancel 点击取消时触发
* @event {Function} change 值变化时触发事件参数PickerPickEvent
* @event {Function} column-change 列数据变化时触发事件参数PickerChangeInfo
*/
import { PickerProps, PickerColumn, PickerValue, PickerColumnItem, PickerConfirmEvent, PickerPickEvent } from './type';
import { pushAt } from './utils';
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
// #ifdef APP
import { useLoading } from '@/uni_modules/lime-loading'
// #endif
const emit = defineEmits(['change', 'cancel', 'pick', 'confirm', 'update:modelValue', 'update:value']);
const props = withDefaults(defineProps<PickerProps>(), {
columns: [] as PickerColumn[],
loading: false,
resetIndex: false,
loadingSize: '64rpx'
})
const pickerItemInstanceArray = reactive<LPickerItemComponentPublicInstance[]>([]);
const ohosShow = ref(0)
const modelValue = ref<PickerValue[]>(props.value ?? props.modelValue ?? props.defaultValue ?? [])
const pickerValue = computed({
set(value : PickerValue[]) {
if (value.join('') == modelValue.value.join('')) return
modelValue.value = value;
emit('update:modelValue', value)
emit('change', value)
},
get() : PickerValue[] {
return props.value ?? props.modelValue ?? modelValue.value
}
} as WritableComputedOptions<PickerValue[]>)
const isEmpty = computed(() : boolean => {
return props.columns.length == 0 && pickerItemInstanceArray.every(child => child.options.length == 0)
})
const styles = computed(() : Map<string, any> => {
const style = new Map<string, any>()
if (props.bgColor != null) {
style.set('background', props.bgColor!)
}
if (props.radius != null) {
style.set('border-top-left-radius', props.radius!)
style.set('border-top-right-radius', props.radius!)
}
return style
})
const curIndexArray = ref<number[]>([]);
const curValueArray = ref([...pickerValue.value]);
const curItemArray : PickerColumnItem[] = []
const realColumns = computed(() : PickerColumn[] => {
const pickerColumns = pickerItemInstanceArray.map((child) : PickerColumn => child.options)
if (pickerColumns.length > 0) {
return pickerColumns
}
return props.columns
})
const manageChildInList = (child : LPickerItemComponentPublicInstance, shouldAdd : boolean) => {
const index = pickerItemInstanceArray.indexOf(child);
if (shouldAdd) {
if (index != -1) return
pickerItemInstanceArray.push(child)
} else {
if (index == -1) return
pickerItemInstanceArray.splice(index, 1);
}
}
const updateItems = (item : PickerColumnItem, index : number, column : number) => {
pushAt(curIndexArray.value, column, index)
pushAt(curValueArray.value, column, item.value)
pushAt(curItemArray, column, item)
};
const updatePickerItems = () => {
const _indexs : number[] = []
const _values : any[] = []
pickerItemInstanceArray.forEach((child, column) => {
if (child.options.length == 0) return
const value = curValueArray.value.length > column ? curValueArray.value[column] : null;
// #ifndef APP
const index = value == null ? 0 : child._.exposed.getIndexByValue(value);
child._.exposed.setIndex(index)
// #endif
// #ifdef APP
// const index = value == null ? 0 : child.getIndexByValue(value)
// child.setIndex(index)
const index : number = (value == null ? 0 : child.$callMethod('getIndexByValue', value)) as number
child.$callMethod('setIndex', index)
// #endif
const item = child.options[index]
_indexs.push(index)
_values.push(item.value)
pushAt(curItemArray, column, item)
// pushAt(curIndexArray.value, column, index)
// pushAt(curValueArray.value, column, item.value)
// // 不能改变单向数据流, 只有值不存在时候才处理
// if(pickerValue.value.length == 0) {
// pickerValue.value = [...curValueArray.value]
// }
// if(pickerValue.value.join('') == curValueArray.value.join('')) return
// pickerValue.value = [...curValueArray.value]
})
if (curIndexArray.value.join('') == _indexs.join('')) return
curIndexArray.value = _indexs
curValueArray.value = _values
// if(pickerValue.value.length == 0) {
pickerValue.value = [...curValueArray.value]
// }
}
const onPick = (item : PickerColumnItem, index : number, column : number) => {
if (curIndexArray.value[column] == index) return
pushAt(curIndexArray.value, column, index)
pushAt(curValueArray.value, column, item.value)
pushAt(curItemArray, column, item)
const obj : PickerPickEvent = {
values: curValueArray.value,
column,
index
}
pickerValue.value = [...curValueArray.value]
emit('pick', obj)
};
const onCancel = (e : UniPointerEvent) => {
updatePickerItems()
emit('cancel', e)
}
const onConfirm = (e : UniPointerEvent) => {
const values = [...curValueArray.value];
const indexs = [...curIndexArray.value];
const items = curItemArray.map((item) : PickerColumnItem => toRaw(item))
if (pickerValue.value.join('') != values.join('')) {
pickerValue.value = values;
}
const obj : PickerConfirmEvent = {
values,
indexs,
items
}
emit('confirm', obj)
}
const stopPickerValue = watch(pickerValue, () => {
if (pickerValue.value.join('') == curValueArray.value.join('')) return
curValueArray.value = pickerValue.value.map((item : PickerValue) => item);
updatePickerItems()
})
const stopColumns = watch(realColumns, () => {
updatePickerItems()
// nextTick(() => {
// setTimeout(()=>{
// updatePickerItems()
// },2000)
// })
})
// #ifdef APP-HARMONY
// 在弹窗中 文本没有样式,给它重绘
const pickerRef = ref<UniElement|null>(null)
const updateOhosKey = () =>{
requestAnimationFrame(()=>{
ohosShow.value++
setTimeout(()=>{
ohosShow.value++
},200)
pickerRef.value?.getBoundingClientRectAsync((res)=>{
ohosShow.value++
})
})
}
const resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
nextTick(updateOhosKey)
})
const stopWatch = watch(():UniElement|null => pickerRef.value, (el:UniElement|null) => {
if(el== null) return
nextTick(updateOhosKey)
resizeObserver.observe(el)
})
onUnmounted(()=>{
stopWatch()
resizeObserver.disconnect()
})
// #endif
onMounted(() => {
nextTick(() => {
if (pickerValue.value.join('') != curValueArray.value.join('') && pickerValue.value.length > 0) {
curValueArray.value = [...pickerValue.value]
updatePickerItems()
}
})
})
// #ifdef APP
const loadingRef = ref<UniElement | null>(null);
// const {play, clear, failed} = useLoading(loadingRef, 'circular', props.loadingColor?? '#3283ff', unitConvert(props.loadingSize))
const loadingAni = useLoading(loadingRef)
loadingAni.type = 'circular'
loadingAni.color = props.loadingColor ?? '#3283ff'
loadingAni.ratio = unitConvert(props.loadingSize)
watchEffect(() => {
if (props.loading) {
loadingAni.play()
} else {
loadingAni.clear()
}
})
// #endif
onBeforeUnmount(() => {
stopPickerValue()
stopColumns()
})
provide('limePicker', props)
provide('limePickerOnPick', onPick)
provide('limePickerUpdateItems', updateItems)
provide('limePickerItems', pickerItemInstanceArray)
provide('limePickerManageChildInList', manageChildInList)
</script>
<style lang="scss">
@import './index.scss';
</style>

View File

@@ -0,0 +1,261 @@
<template>
<view class="l-picker" :style="[styles]">
<view class="l-picker__toolbar" v-if="cancelBtn || title || confirmBtn">
<text class="l-picker__cancel" :style="cancelStyle" v-if="cancelBtn" @click="onCancel">{{cancelBtn}}</text>
<text class="l-picker__title" :style="titleStyle" v-if="title">{{title}}</text>
<text class="l-picker__confirm" :style="confirmStyle" v-if="confirmBtn" @click="onConfirm">{{confirmBtn}}</text>
</view>
<slot name="header"></slot>
<view class="l-picker__main" :style="[groupHeight ? { height: groupHeight}: {}]">
<slot>
<l-picker-item v-for="(options, i) in columns" :options="options" :key="i" :column="i" :value="pickerValue.length > i ? pickerValue[i]: null"></l-picker-item>
</slot>
<view class="l-picker__empty" v-if="isEmpty">
<slot name="empty"></slot>
</view>
</view>
<slot name="footer" />
<view class="l-picker__loading" ref="loadingRef" v-if="loading" :style="[loadingMaskColor ? {background: loadingMaskColor}: {}]">
<l-loading :size="loadingSize" :color="loadingColor"></l-loading>
</view>
</view>
</template>
<script lang="ts">
// @ts-nocheck
/**
* Picker 选择器组件
* @description 多列数据选择器,支持级联数据展示和自定义样式配置
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-picker
*
* @property {string} cancelBtn 取消按钮文字
* @property {string | UTSJSONObject} cancelStyle 取消按钮样式
* @property {string} confirmBtn 确定按钮文字
* @property {string | UTSJSONObject} confirmStyle 确定按钮样式
* @property {string} title 标题文字
* @property {string | UTSJSONObject} titleStyle 标题样式
* @property {UTSJSONObject} keys 字段别名配置(例:{value: 'id', label: 'name'}
* @property {PickerColumn[]} columns 选择器列数据(必填)
* @property {PickerValue[]} modelValue 选中值支持v-model
* @property {PickerValue[]} defaultValue 默认选中值
* @property {PickerValue[]} value 选中值(兼容旧版)
* @property {boolean} loading 是否显示加载状态
* @property {string} loadingColor 加载图标颜色
* @property {string} loadingMaskColor 加载遮罩颜色
* @property {string} loadingSize 加载图标尺寸
* @property {string} itemHeight 选项行高度
* @property {string} itemColor 选项文字颜色
* @property {string} itemFontSize 选项字体大小
* @property {string} itemActiveColor 选中项颜色
* @property {string} indicatorStyle 指示器样式
* @property {string} bgColor 背景颜色
* @property {string} groupHeight 选项组高度
* @property {string} radius 圆角半径
* @property {boolean} resetIndex 是否重置选中索引
*
* @event {Function} confirm 点击确定时触发事件参数PickerConfirmEvent
* @event {Function} cancel 点击取消时触发
* @event {Function} change 值变化时触发事件参数PickerPickEvent
* @event {Function} column-change 列数据变化时触发事件参数PickerChangeInfo
*/
import type { PickerProps, PickerColumn, PickerValue, PickerColumnItem, PickerConfirmEvent, PickerPickEvent } from './type';
import { defineComponent, computed, ref, watch, onMounted, nextTick, onBeforeUnmount, provide, reactive, toRaw } from '@/uni_modules/lime-shared/vue';
import pickerProps from './props';
export default defineComponent({
name: 'l-picker',
props: pickerProps,
emits: ['change', 'cancel', 'pick','confirm' ,'update:modelValue', 'update:value'],
setup(props, {emit}) {
const pickerItemInstanceArray = ref<LPickerItemComponentPublicInstance[]>([]);
const modelValue = ref<PickerValue[]>(props.value || props.modelValue || props.defaultValue || [])
const pickerValue = computed({
set(value: PickerValue[]) {
if(value.join('') == modelValue.value.join('')) return
modelValue.value = value;
emit('update:modelValue', value)
emit('change', value)
// #ifdef VUE2
emit('input', value)
// #endif
},
get():PickerValue[] {
return props.value || props.modelValue || modelValue.value
}
} as WritableComputedOptions<PickerValue[]>)
const isEmpty = computed(():boolean => {
return props.columns.length == 0 && pickerItemInstanceArray.value.every(child => child.options.length == 0)
})
const styles = computed(()=>{
const style:Record<string, any> = {}
if(props.bgColor) {
style['background'] = props.bgColor!
}
if(props.radius) {
style['border-top-left-radius'] = props.radius!
style['border-top-right-radius'] = props.radius!
}
return style
})
const curIndexArray = ref<number[]>([]);
const curValueArray = ref([...pickerValue.value]);
const curItemArray:PickerColumnItem[] = []
const realColumns = computed(():PickerColumn[] => {
const pickerColumns = pickerItemInstanceArray.value.map((child):PickerColumn => child.options)
if(pickerColumns.length > 0) {
return pickerColumns
}
return props.columns
})
const valueArrayEquals = computed(():boolean => pickerValue.value.join('') == curValueArray.value.join(''))
const manageChildInList = (child: LPickerItemComponentPublicInstance, shouldAdd: boolean) => {
const index = pickerItemInstanceArray.value.indexOf(child);
if(shouldAdd) {
if(index != -1) return
pickerItemInstanceArray.value.push(child)
} else {
if(index == -1) return
pickerItemInstanceArray.value.splice(index, 1);
}
}
const updateItems = (item: PickerColumnItem, index:number, column: number) => {
curIndexArray.value[column] = index
curValueArray.value[column] = item.value
curItemArray[column] = item;
// clearTimeout(timer)
// timer = setTimeout(()=>{
// emit('change', [...curValueArray.value])
// },50)
};
const updatePickerItems = () => {
const _indexs : number[] = []
const _values : any[] = []
pickerItemInstanceArray.value.forEach((child, column)=>{
if(child.options.length == 0) return
const value = curValueArray.value.length > column ? curValueArray.value[column] : null
// #ifdef VUE3
const index = value == null ? 0 : child._.exposed.getIndexByValue(value)
child._.exposed.setIndex(index)
// #endif
// #ifdef VUE2
const index = value == null ? 0 : child.getIndexByValue(value)
child.setIndex(index)
// #endif
const item = child.options[index]
_indexs.push(index)
_values.push(item.value)
// curIndexArray.value[column] = index
// curValueArray.value[column] = item.value
curItemArray[column] = item
// 不能改变单向数据流, 只有值不存在时候才处理
// if(pickerValue.value.length == 0) {
// pickerValue.value = [...curValueArray.value]
// }
// if(pickerValue.value.join('') == curValueArray.value.join('')) return
// pickerValue.value = [...curValueArray.value]
})
if (curIndexArray.value.join('') == _indexs.join('')) return
curIndexArray.value = _indexs
curValueArray.value = _values
// if(pickerValue.value.length == 0) {
pickerValue.value = [...curValueArray.value]
// }
}
const onPick = (item: PickerColumnItem, index:number, column: number) => {
if( curIndexArray.value[column] == index &&
curValueArray.value[column] == item.value) return
curIndexArray.value[column] = index
curValueArray.value[column] = item.value
curItemArray[column] = item
const obj:PickerPickEvent = {
values: curValueArray.value,
column,
index
}
pickerValue.value = [...curValueArray.value]
emit('pick', obj)
};
const onCancel = (e: UniPointerEvent) => {
updatePickerItems()
emit('cancel', e)
}
const onConfirm = (e: UniPointerEvent) => {
const values = [...curValueArray.value];
const indexs = [...curIndexArray.value];
const items = curItemArray.map((item):PickerColumnItem => toRaw(item))
if(pickerValue.value.join('') != values.join('')) {
pickerValue.value = values;
}
const obj:PickerConfirmEvent = {
values,
indexs,
items
}
emit('confirm', obj)
}
const stopPickerValue = watch(pickerValue, () => {
nextTick(()=>{
curValueArray.value = pickerValue.value.map((item: PickerValue) => item);
updatePickerItems()
})
})
const stopColumns = watch(realColumns, ()=>{
// nextTick(()=>{
// updatePickerItems()
// })
updatePickerItems()
})
onMounted(()=>{
nextTick(()=>{
if(
!valueArrayEquals.value &&
pickerValue.value.length > 0) {
curValueArray.value = [...pickerValue.value]
updatePickerItems()
}
})
})
onBeforeUnmount(()=> {
stopPickerValue()
stopColumns()
})
provide('limePicker', props)
provide('limePickerOnPick', onPick)
provide('limePickerUpdateItems', updateItems)
provide('limePickerItems', pickerItemInstanceArray)
provide('limePickerManageChildInList', manageChildInList)
return {
styles,
pickerValue,
isEmpty,
onCancel,
onConfirm
}
}
})
</script>
<style lang="scss">
@import './index.scss';
</style>

View File

@@ -0,0 +1,121 @@
// @ts-nocheck
export default {
/**
* 取消按钮文字
*/
cancelBtn: {
type: String,
default: null
},
cancelStyle: {
type:[String, Object],
default: null
},
/**
* 确定按钮文字
*/
confirmBtn: {
type: String,
default: null
},
confirmStyle: {
type: [String, Object],
default: null
},
keys: {
type: Object,
default: null
},
/**
* 标题
*/
title: {
type: String,
default: null
},
titleStyle: {
type: [String, Object],
default: null
},
/**
* 配置每一列的选项
*/
columns: {
type: Array,
default: () => []
},
/**
* 选中值
*/
modelValue: {
type: Array,
default: null
},
defaultValue: {
type: Array,
default: null
},
value: {
type: Array,
default: null
},
/**
* 是否为加载状态
*/
loading: {
type: Boolean,
default: false
},
loadingColor: {
type: String,
default: null
},
loadingMaskColor: {
type: String,
default: null
},
loadingSize: {
type: String,
default: '64rpx'
},
itemHeight: {
type: String,
default: ''
},
itemColor: {
type: String,
default: ''
},
itemFontSize: {
type: String,
default: ''
},
itemActiveColor: {
type: String,
default: ''
},
indicatorStyle: {
type: String,
default: ''
},
bgColor:{
type: String,
default: null
},
groupHeight:{
type: String,
default: ''
},
radius:{
type: String,
default: null
},
/**
* 列表更新后是否归0
*/
resetIndex: {
type: Boolean,
default: false
}
}

View File

@@ -0,0 +1,80 @@
// @ts-nocheck
export type PickerValue = any;//string | number;
export type PickerColumnItem = {
id: any|null;
label : string;
disabled: boolean | null;
value : string;//string | number;
children : PickerColumn | null
}
export type PickerColumn = PickerColumnItem[];
export type PickerPickEvent= {
values: PickerValue[];
column : number;
index : number;
}
export type PickerConfirmEvent = {
values: PickerValue[]
indexs: number[]
items: PickerColumnItem[]
}
/**
* 定义比较数组时返回的变化对象类型。
*/
export type PickerChangeInfo = {
column: number; // 变化的列索引
direction: 1 | -1 | 0; // 变化方向1 表示增加,-1 表示减少, 0表示无变化
index: number; // 变化后的新值,在列表中表示下标
}
export interface PickerProps {
/**
* 取消按钮文字
*/
cancelBtn ?: string;
cancelStyle ?: string | UTSJSONObject;
/**
* 确定按钮文字
*/
confirmBtn ?: string;
confirmStyle ?: string | UTSJSONObject;
/**
* 标题
*/
title ?: string;
titleStyle ?: string | UTSJSONObject;
/**
* 用来定义 value / label 在 `options` 中对应的字段别名
*/
keys?: UTSJSONObject;
/**
* 配置每一列的选项
*/
columns : PickerColumn[];
/**
* 选中值
*/
modelValue ?: PickerValue[];
defaultValue ?: PickerValue[];
value ?: PickerValue[];
/**
* 是否为加载状态
*/
loading: boolean;
loadingColor?: string;
loadingMaskColor?: string;
loadingSize: string;
itemHeight?: string;
itemColor?: string;
itemFontSize?: string;
itemActiveColor?: string;
itemActiveFontWeight?: number;
indicatorStyle?: string;
bgColor?:string;
groupHeight?:string;
radius?:string;
resetIndex: boolean
}

View File

@@ -0,0 +1,22 @@
// @ts-nocheck
/**
* 在数组的指定位置插入或更新值。
* 如果指定的索引小于数组的长度,则更新该位置的值。
* 如果指定的索引大于或等于数组的长度,则将值添加到数组的末尾。
*
* @param {number[]} arr - 要操作的数字数组。
* @param {number} index - 要插入或更新值的索引位置。
* @param {number} value - 要插入或更新的值。
*/
export function pushAt<T>(arr: T[], index: number, value: T){
// #ifdef APP-ANDROID
if (index < arr.length) {
arr[index] = value;
} else {
arr.push(value);
}
// #endif
// #ifndef APP-ANDROID
arr[index] = value;
// #endif
};

View File

@@ -0,0 +1,405 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">Picker 选择器</text>
<text class="demo-block__desc-text">用于一组预设数据中的选择。</text>
<view class="demo-block__body">
<view class="demo-block card">
<text class="demo-block__title-text">基础用法</text>
<view class="demo-block__body">
<l-picker v-model="citys1" cancel-btn="取消" confirm-btn="确定" title="标题" :columns="cityOptions">
<l-picker-item :options="cityOptions"></l-picker-item>
<l-picker-item :options="cityOptions"></l-picker-item>
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">搭配弹出层使用</text>
<view class="demo-block__body">
<button type="primary" @click="showPicker = true">弹窗</button>
<l-popup v-model="showPicker" position="bottom">
<l-picker cancel-btn="取消" confirm-btn="确定" :columns="seasonColumns" @cancel="showPicker = false"
@confirm="showPicker = false"></l-picker>
</l-popup>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">双向绑定: {{citys.join(',')}}</text>
<view class="demo-block__body">
<l-picker v-model="citys" cancel-btn="取消" confirm-btn="确定">
<l-picker-item :options="cityOptions"></l-picker-item>
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">多列</text>
<view class="demo-block__body">
<l-picker cancel-btn="取消" confirm-btn="确定" :columns="seasonColumns" @confirm="onConfirm">
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">多列联动</text>
<view class="demo-block__body">
<l-picker resetIndex cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm" @pick="onColumnChange">
<l-picker-item :options="provinces"></l-picker-item>
<l-picker-item :options="cities"></l-picker-item>
<l-picker-item :options="counties"></l-picker-item>
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">级联选择</text>
<view class="demo-block__body">
<l-cascade :columns="cascadeColumns" cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm">
</l-cascade>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">加载状态</text>
<view class="demo-block__body">
<l-picker loading cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm" @pick="onColumnChange">
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">空状态</text>
<view class="demo-block__body">
<l-picker cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm" @pick="onColumnChange">
<template #empty>
<l-empty description="没有数据" />
</template>
</l-picker>
</view>
</view>
</view>
</view>
</template>
<script lang="uts" setup>
import { PickerColumn, PickerColumnItem, PickerConfirmEvent, PickerPickEvent } from '@/uni_modules/lime-picker';
const showPicker = ref(false)
const citys1 = ref<string[]>(['上海市'])
const citys = ref<string[]>(['上海市'])
const cascadeColumns = [
{
label: '浙江',
value: 'Zhejiang',
children: [
{
label: '杭州',
value: 'Hangzhou',
children: [
{ label: '西湖区', value: 'Xihu' },
{ label: '余杭区', value: 'Yuhang' },
],
},
{
label: '温州',
value: 'Wenzhou',
children: [
{ label: '鹿城区', value: 'Lucheng' },
{ label: '瓯海区', value: 'Ouhai' },
],
},
],
},
{
label: '福建',
value: 'Fujian',
children: [
{
label: '福州',
value: 'Fuzhou',
children: [
{ label: '鼓楼区', value: 'Gulou' },
{ label: '台江区', value: 'Taijiang' },
],
},
{
label: '厦门',
value: 'Xiamen',
children: [
{ label: '思明区', value: 'Siming' },
{ label: '海沧区', value: 'Haicang' },
],
},
],
},
];
setTimeout(() => {
// citys.value = ['广州市']
}, 3000)
const cityOptions = [
{
label: '北京市',
value: '北京市',
},
{
label: '上海市',
value: '上海市',
},
{
label: '广州市',
value: '广州市',
},
{
label: '深圳市',
value: '深圳市',
},
{
label: '杭州市',
value: '杭州市',
},
{
label: '成都市',
value: '成都市',
},
{
label: '长沙市',
value: '长沙市',
},
] as PickerColumn
const seasonColumns = computed(() : PickerColumn[] => {
const _year = 2018;
const _years : PickerColumnItem[] = []
for (let i = 2000; i <= _year; i++) {
_years.push({
label: `${i}`,
value: `${i}`
} as PickerColumnItem)
}
const seasonOptions = [
{
label: '春',
value: '春',
},
{
label: '夏',
value: '夏',
},
{
label: '秋',
value: '秋',
},
{
label: '冬',
value: '冬',
},
] as PickerColumn
return [
_years,
seasonOptions
]
})
const areaList = {
provinces: {
'110000': '北京市',
'440000': '广东省',
},
cities: {
'110100': '北京市',
'440100': '广州市',
'440200': '韶关市',
'440300': '深圳市',
'440400': '珠海市',
'440500': '汕头市',
'440600': '佛山市',
},
counties: {
'110101': '东城区',
'110102': '西城区',
'110105': '朝阳区',
'110106': '丰台区',
'110107': '石景山区',
'110108': '海淀区',
'110109': '门头沟区',
'110111': '房山区',
'110112': '通州区',
'110113': '顺义区',
'110114': '昌平区',
'110115': '大兴区',
'110116': '怀柔区',
'110117': '平谷区',
'110118': '密云区',
'110119': '延庆区',
'440103': '荔湾区',
'440104': '越秀区',
'440105': '海珠区',
'440106': '天河区',
'440111': '白云区',
'440112': '黄埔区',
'440113': '番禺区',
'440114': '花都区',
'440115': '南沙区',
'440117': '从化区',
'440118': '增城区',
'440203': '武江区',
'440204': '浈江区',
'440205': '曲江区',
'440222': '始兴县',
'440224': '仁化县',
'440229': '翁源县',
'440232': '乳源瑶族自治县',
'440233': '新丰县',
'440281': '乐昌市',
'440282': '南雄市',
'440303': '罗湖区',
'440304': '福田区',
'440305': '南山区',
'440306': '宝安区',
'440307': '龙岗区',
'440308': '盐田区',
'440309': '龙华区',
'440310': '坪山区',
'440311': '光明区',
'440402': '香洲区',
'440403': '斗门区',
'440404': '金湾区',
'440507': '龙湖区',
'440511': '金平区',
'440512': '濠江区',
'440513': '潮阳区',
'440514': '潮南区',
'440515': '澄海区',
'440523': '南澳县',
'440604': '禅城区',
'440605': '南海区',
'440606': '顺德区',
'440607': '三水区',
'440608': '高明区',
},
};
const getOptions = (obj : UTSJSONObject, filter ?: ((value : PickerColumnItem) => boolean) | null) : PickerColumn => {
const res = UTSJSONObject.keys(obj).map((key) : PickerColumnItem => {
return {
// id: key,
value: `${key}`,
label: `${obj[key]}`
} as PickerColumnItem
})
if (filter != null) {
return res.filter(filter)
}
return res
}
const match = (v1 : string, v2 : string, size : number) : boolean => {
return v1.slice(0, size) == v2.slice(0, size)
};
const getCounties = (cityValue : string) : PickerColumn => {
return getOptions(areaList.counties as UTSJSONObject, (county : PickerColumnItem) : boolean => match(county.value, cityValue, 4));
};
const getCities = (provinceValue : string) : PickerColumn[] => {
const cities = getOptions(areaList.cities as UTSJSONObject, (city : PickerColumnItem) : boolean => match(city.value, provinceValue, 2));
const counties = getCounties(cities[0].value);
return [cities, counties];
};
const provinces = ref<PickerColumnItem[]>([]);
const cities = ref<PickerColumnItem[]>([]);
const counties = ref<PickerColumnItem[]>([]);
const options = computed(() : PickerColumn[] => [provinces.value, cities.value, counties.value]);
const onConfirm = (context : PickerConfirmEvent) => {
console.log('context', context)
}
const onColumnChange = ({ values, column, index } : PickerPickEvent) => {
if (column == 0) {
// 更改省份
const data = getCities(provinces.value[index].value);
cities.value = data[0];
counties.value = data[1];
}
if (column == 1) {
// 更改城市
counties.value = getCounties(cities.value[index].value);
}
if (column == 2) {
// 更改区县
}
}
onMounted(() => {
provinces.value = getOptions(areaList.provinces as UTSJSONObject, null)
const data = getCities(provinces.value[0].value);
cities.value = data[0];
counties.value = data[1];
})
</script>
<style lang="scss">
.btn {
margin-bottom: 20rpx;
margin-right: 20rpx;
align-self: center;
}
.demo-block {
margin: 32px 0 0;
// overflow: visible;
&.card {
// background-color: white;
padding: 30rpx;
margin-bottom: 20rpx;
}
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
display: flex;
margin-left: 5px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
margin-left: 20px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
padding-left: 15px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
margin-left: 20px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
overflow: visible;
}
}
}
</style>

View File

@@ -0,0 +1,398 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">Picker 选择器</text>
<text class="demo-block__desc-text">用于一组预设数据中的选择</text>
<view class="demo-block__body">
<view class="demo-block card">
<text class="demo-block__title-text">基础用法</text>
<view class="demo-block__body">
<l-picker cancel-btn="取消" confirm-btn="确定" title="标题" :columns="cityOptions">
<l-picker-item :options="cityOptions"></l-picker-item>
<l-picker-item :options="cityOptions"></l-picker-item>
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">配合弹窗插件</text>
<view class="demo-block__body">
<button type="primary" @click="showPicker = true">弹窗</button>
<l-popup v-model="showPicker" position="bottom">
<l-picker cancel-btn="取消" confirm-btn="确定" :columns="seasonColumns" @cancel="showPicker = false" @confirm="showPicker = false"></l-picker>
</l-popup>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">双向绑定: {{citys.join(',')}}</text>
<view class="demo-block__body">
<l-picker v-model="citys" cancel-btn="取消" confirm-btn="确定">
<l-picker-item :options="cityOptions"></l-picker-item>
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">多列</text>
<view class="demo-block__body">
<l-picker cancel-btn="取消" confirm-btn="确定" :columns="seasonColumns" @confirm="onConfirm">
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">多列联动</text>
<view class="demo-block__body">
<l-picker cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm" @pick="onColumnChange">
<l-picker-item :options="provinces"></l-picker-item>
<l-picker-item :options="cities"></l-picker-item>
<l-picker-item :options="counties"></l-picker-item>
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">级联选择</text>
<view class="demo-block__body">
<l-cascade :columns="cascadeColumns" cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm">
</l-cascade>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">加载状态</text>
<view class="demo-block__body">
<l-picker loading cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm" @pick="onColumnChange">
</l-picker>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text">空状态</text>
<view class="demo-block__body">
<l-picker cancel-btn="取消" confirm-btn="确定" @confirm="onConfirm" @pick="onColumnChange">
<template #empty>
<l-empty description="没有数据" />
</template>
</l-picker>
</view>
</view>
</view>
</view>
</template>
<script>
const areaList = {
provinces: {
'110000': '北京市',
'440000': '广东省',
},
cities: {
'110100': '北京市',
'440100': '广州市',
'440200': '韶关市',
'440300': '深圳市',
'440400': '珠海市',
'440500': '汕头市',
'440600': '佛山市',
},
counties: {
'110101': '东城区',
'110102': '西城区',
'110105': '朝阳区',
'110106': '丰台区',
'110107': '石景山区',
'110108': '海淀区',
'110109': '门头沟区',
'110111': '房山区',
'110112': '通州区',
'110113': '顺义区',
'110114': '昌平区',
'110115': '大兴区',
'110116': '怀柔区',
'110117': '平谷区',
'110118': '密云区',
'110119': '延庆区',
'440103': '荔湾区',
'440104': '越秀区',
'440105': '海珠区',
'440106': '天河区',
'440111': '白云区',
'440112': '黄埔区',
'440113': '番禺区',
'440114': '花都区',
'440115': '南沙区',
'440117': '从化区',
'440118': '增城区',
'440203': '武江区',
'440204': '浈江区',
'440205': '曲江区',
'440222': '始兴县',
'440224': '仁化县',
'440229': '翁源县',
'440232': '乳源瑶族自治县',
'440233': '新丰县',
'440281': '乐昌市',
'440282': '南雄市',
'440303': '罗湖区',
'440304': '福田区',
'440305': '南山区',
'440306': '宝安区',
'440307': '龙岗区',
'440308': '盐田区',
'440309': '龙华区',
'440310': '坪山区',
'440311': '光明区',
'440402': '香洲区',
'440403': '斗门区',
'440404': '金湾区',
'440507': '龙湖区',
'440511': '金平区',
'440512': '濠江区',
'440513': '潮阳区',
'440514': '潮南区',
'440515': '澄海区',
'440523': '南澳县',
'440604': '禅城区',
'440605': '南海区',
'440606': '顺德区',
'440607': '三水区',
'440608': '高明区',
},
};
const getOptions = (obj, filter) => {
const res = Object.keys(obj).map((key) => {
return {
value: `${key}`,
label: `${obj[key]}`
}
})
if (filter) {
return res.filter(filter)
}
return res
}
const match = (v1, v2, size) => {
return v1.slice(0, size) == v2.slice(0, size)
};
const getCounties = (cityValue ) => {
return getOptions(areaList.counties, (county) => match(county.value, cityValue, 4));
};
const getCities = (provinceValue) => {
const cities = getOptions(areaList.cities, (city) => match(city.value, provinceValue, 2));
const counties = getCounties(cities[0].value);
return [cities, counties];
};
const cascadeColumns = [
{
label: '浙江',
value: 'Zhejiang',
children: [
{
label: '杭州',
value: 'Hangzhou',
children: [
{ label: '西湖区', value: 'Xihu' },
{ label: '余杭区', value: 'Yuhang' },
],
},
{
label: '温州',
value: 'Wenzhou',
children: [
{ label: '鹿城区', value: 'Lucheng' },
{ label: '瓯海区', value: 'Ouhai' },
],
},
],
},
{
label: '福建',
value: 'Fujian',
children: [
{
label: '福州',
value: 'Fuzhou',
children: [
{ label: '鼓楼区', value: 'Gulou' },
{ label: '台江区', value: 'Taijiang' },
],
},
{
label: '厦门',
value: 'Xiamen',
children: [
{ label: '思明区', value: 'Siming' },
{ label: '海沧区', value: 'Haicang' },
],
},
],
},
];
export default {
data() {
const _year = 2018;
const _years = []
for (let i = 2000; i <= _year; i++) {
_years.push({
label: `${i}`,
value: `${i}`
})
}
const seasonOptions = [
{
label: '春',
value: '春',
},
{
label: '夏',
value: '夏',
},
{
label: '秋',
value: '秋',
},
{
label: '冬',
value: '冬',
},
]
return {
showPicker: false,
citys: ['上海市'],
cityOptions: [
{
label: '北京市',
value: '北京市',
},
{
label: '上海市',
value: '上海市',
},
{
label: '广州市',
value: '广州市',
},
{
label: '深圳市',
value: '深圳市',
},
{
label: '杭州市',
value: '杭州市',
},
{
label: '成都市',
value: '成都市',
},
{
label: '长沙市',
value: '长沙市',
},
],
seasonColumns: [_years, seasonOptions],
provinces:[],
cities:[],
counties:[],
options:[],
cascadeColumns
}
},
methods: {
onConfirm(context) {
console.log('context', context)
},
onColumnChange({values, column, index}) {
if (column == 0) {
// 更改省份
const data = getCities(this.provinces[index].value);
this.cities = data[0];
this.counties = data[1];
this.options = [this.provinces, this.cities, this.counties]
}
if (column == 1) {
// 更改城市
this.counties = getCounties(this.cities[index].value);
this.options[2] = this.counties
}
if (column == 2) {
// 更改区县
}
}
},
mounted() {
this.provinces = getOptions(areaList.provinces, null)
const data = getCities(this.provinces[0].value);
this.cities = data[0];
this.counties = data[1];
this.options = [this.provinces, this.cities, this.counties]
setTimeout(()=>{
this.citys = ['广州市']
},3000)
}
}
</script>
<style lang="scss">
.btn {
margin-bottom: 20rpx;
margin-right: 20rpx;
align-self: center;
}
.demo-block {
margin: 32px 0 0;
// overflow: visible;
&.card {
// background-color: white;
padding: 30rpx;
margin-bottom: 20rpx;
}
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
display: flex;
margin-left: 5px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
margin-left: 20px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
padding-left: 15px ;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
margin-left: 20px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
overflow: visible;
}
}
}
</style>