Initial commit of akmon project
This commit is contained in:
113
uni_modules/lime-picker/components/l-cascade/l-cascade.uvue
Normal file
113
uni_modules/lime-picker/components/l-cascade/l-cascade.uvue
Normal 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>
|
||||
124
uni_modules/lime-picker/components/l-cascade/l-cascade.vue
Normal file
124
uni_modules/lime-picker/components/l-cascade/l-cascade.vue
Normal 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>
|
||||
57
uni_modules/lime-picker/components/l-cascade/type.ts
Normal file
57
uni_modules/lime-picker/components/l-cascade/type.ts
Normal 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
|
||||
}
|
||||
122
uni_modules/lime-picker/components/l-cascade/utils.uts
Normal file
122
uni_modules/lime-picker/components/l-cascade/utils.uts
Normal 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;
|
||||
}
|
||||
72
uni_modules/lime-picker/components/l-picker-item/index.scss
Normal file
72
uni_modules/lime-picker/components/l-picker-item/index.scss
Normal 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 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
15
uni_modules/lime-picker/components/l-picker-item/props.ts
Normal file
15
uni_modules/lime-picker/components/l-picker-item/props.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// @ts-nocheck
|
||||
export default {
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: -1
|
||||
}
|
||||
}
|
||||
13
uni_modules/lime-picker/components/l-picker-item/type.ts
Normal file
13
uni_modules/lime-picker/components/l-picker-item/type.ts
Normal 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
|
||||
}
|
||||
121
uni_modules/lime-picker/components/l-picker/index.scss
Normal file
121
uni_modules/lime-picker/components/l-picker/index.scss
Normal 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
|
||||
}
|
||||
}
|
||||
292
uni_modules/lime-picker/components/l-picker/l-picker.uvue
Normal file
292
uni_modules/lime-picker/components/l-picker/l-picker.uvue
Normal 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>
|
||||
261
uni_modules/lime-picker/components/l-picker/l-picker.vue
Normal file
261
uni_modules/lime-picker/components/l-picker/l-picker.vue
Normal 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>
|
||||
121
uni_modules/lime-picker/components/l-picker/props.ts
Normal file
121
uni_modules/lime-picker/components/l-picker/props.ts
Normal 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
|
||||
}
|
||||
}
|
||||
80
uni_modules/lime-picker/components/l-picker/type.ts
Normal file
80
uni_modules/lime-picker/components/l-picker/type.ts
Normal 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
|
||||
}
|
||||
22
uni_modules/lime-picker/components/l-picker/utils.uts
Normal file
22
uni_modules/lime-picker/components/l-picker/utils.uts
Normal 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
|
||||
};
|
||||
405
uni_modules/lime-picker/components/lime-picker/lime-picker.uvue
Normal file
405
uni_modules/lime-picker/components/lime-picker/lime-picker.uvue
Normal 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>
|
||||
398
uni_modules/lime-picker/components/lime-picker/lime-picker.vue
Normal file
398
uni_modules/lime-picker/components/lime-picker/lime-picker.vue
Normal 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>
|
||||
Reference in New Issue
Block a user