Files
WAI_Project_UNIX/uni_modules/cool-ui/components/cl-select-time/cl-select-time.uvue

338 lines
6.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<cl-select-trigger
v-if="showTrigger"
:pt="{
className: pt.trigger?.className,
icon: pt.trigger?.icon
}"
:placeholder="placeholder"
:disabled="disabled"
:focus="popupRef?.isOpen"
:text="text"
arrow-icon="time-line"
@tap="open()"
@clear="clear"
></cl-select-trigger>
<cl-popup
ref="popupRef"
v-model="visible"
:title="title"
:pt="{
className: pt.popup?.className,
header: pt.popup?.header,
container: pt.popup?.container,
mask: pt.popup?.mask,
draw: pt.popup?.draw
}"
>
<view class="cl-select-popup" @touchmove.stop>
<view class="cl-select-popup__picker">
<cl-picker-view
:value="indexes"
:headers="headers"
:columns="columns"
:reset-on-change="false"
@change="onChange"
></cl-picker-view>
</view>
<view class="cl-select-popup__op">
<cl-button
v-if="showCancel"
size="large"
text
border
type="light"
:pt="{
className: 'flex-1 !rounded-xl h-[80rpx]'
}"
@tap="close"
>{{ cancelText }}</cl-button
>
<cl-button
v-if="showConfirm"
size="large"
:pt="{
className: 'flex-1 !rounded-xl h-[80rpx]'
}"
@tap="confirm"
>{{ confirmText }}</cl-button
>
</view>
</view>
</cl-popup>
</template>
<script setup lang="ts">
import { ref, computed, type PropType, watch } from "vue";
import type { ClSelectOption } from "../../types";
import { isEmpty, isNull, parsePt } from "@/cool";
import type { ClSelectTriggerPassThrough } from "../cl-select-trigger/props";
import type { ClPopupPassThrough } from "../cl-popup/props";
import { t } from "@/locale";
defineOptions({
name: "cl-select-time"
});
// 组件属性定义
const props = defineProps({
// 透传样式配置
pt: {
type: Object,
default: () => ({})
},
// 选择器的值
modelValue: {
type: String,
default: ""
},
// 表头
headers: {
type: Array as PropType<string[]>,
default: () => [t("小时"), t("分钟"), t("秒数")]
},
// 选择器标题
title: {
type: String,
default: () => t("请选择")
},
// 选择器占位符
placeholder: {
type: String,
default: () => t("请选择")
},
// 是否显示选择器触发器
showTrigger: {
type: Boolean,
default: true
},
// 是否禁用选择器
disabled: {
type: Boolean,
default: false
},
// 确认按钮文本
confirmText: {
type: String,
default: () => t("确定")
},
// 是否显示确认按钮
showConfirm: {
type: Boolean,
default: true
},
// 取消按钮文本
cancelText: {
type: String,
default: () => t("取消")
},
// 是否显示取消按钮
showCancel: {
type: Boolean,
default: true
},
// 时间标签格式化
labelFormat: {
type: String as PropType<string>,
default: "{H}:{m}:{s}"
}
});
// 定义事件
const emit = defineEmits(["update:modelValue", "change"]);
// 弹出层引用
const popupRef = ref<ClPopupComponentPublicInstance | null>(null);
// 透传样式类型定义
type PassThrough = {
trigger?: ClSelectTriggerPassThrough;
popup?: ClPopupPassThrough;
};
// 解析透传样式配置
const pt = computed(() => parsePt<PassThrough>(props.pt));
// 当前选中的值
const value = ref<string[]>([]);
// 当前选中项的索引
const indexes = ref<number[]>([]);
// 时间选择器列表
const columns = computed(() => {
const arr = [[], [], []] as ClSelectOption[][];
for (let i = 0; i < 60; i++) {
const v = i.toString().padStart(2, "0");
const item = {
label: v,
value: v
} as ClSelectOption;
if (i < 24) {
arr[0].push(item);
}
arr[1].push(item);
arr[2].push(item);
}
return arr;
});
// 显示文本
const text = computed(() => {
// 获取当前v-model绑定的时间字符串
const val = props.modelValue;
// 如果值为空或为null返回空字符串
if (isEmpty(val) || isNull(val)) {
return "";
}
// 拆分时间字符串,分别获取小时、分钟、秒
const [h, m, s] = val.split(":");
// 按照labelFormat格式化显示文本
return props.labelFormat.replace("{H}", h).replace("{m}", m).replace("{s}", s);
});
// 选择器值改变事件
function onChange(a: number[]) {
// 复制当前组件内部维护的索引数组
const b = [...indexes.value];
// 遍历所有列,处理联动逻辑
for (let i = 0; i < a.length; i++) {
// 检查当前列是否发生改变
if (b[i] != a[i]) {
// 更新当前列的索引
b[i] = a[i];
}
}
// 更新组件内部维护的索引数组
indexes.value = b;
// 根据最新的索引数组,更新选中的值数组
value.value = b.map((e, i) => columns.value[i][e].value as string);
}
// 选择器显示状态
const visible = ref(false);
// 选择回调函数
let callback: ((value: string) => void) | null = null;
// 打开选择器
function open(cb: ((value: string) => void) | null = null) {
if (props.disabled) {
return;
}
visible.value = true;
callback = cb;
}
// 关闭选择器
function close() {
visible.value = false;
}
// 清空选择器
function clear() {
emit("update:modelValue", "");
emit("change", "");
}
// 确认选择
function confirm() {
// 将选中值转换为字符串格式
const val = value.value.join(":");
// 触发更新事件
emit("update:modelValue", val);
emit("change", val);
// 触发回调
if (callback != null) {
callback!(val);
}
// 关闭选择器
close();
}
// 监听modelValue变化
watch(
computed(() => props.modelValue),
(val: string) => {
// 声明选中值数组
let _value: string[];
// 判断输入值是否为空
if (isEmpty(val) || isNull(val)) {
// 设置空数组
_value = [];
} else {
// 按冒号分割字符串为数组
_value = val.split(":");
}
// 声明索引数组
let _indexes = [] as number[];
// 遍历时分秒三列
for (let i = 0; i < 3; i++) {
// 判断是否需要设置默认值
if (i >= _value.length) {
// 添加默认索引0
_indexes.push(0);
// 添加默认值
_value.push(columns.value[i][0].value as string);
} else {
// 查找当前值对应的索引
let index = columns.value[i].findIndex((e) => e.value == _value[i]);
// 索引无效时重置为0
if (index < 0) {
index = 0;
}
// 添加索引
_indexes.push(index);
}
}
// 更新选中值
value.value = _value;
// 更新索引值
indexes.value = _indexes;
},
{
immediate: true
}
);
defineExpose({
open,
close
});
</script>
<style lang="scss" scoped>
.cl-select {
&-popup {
&__op {
@apply flex flex-row items-center justify-center;
padding: 24rpx;
}
}
}
</style>