星期-时间范围选择器 滑动选择时间 最小粒度 vue3
星期-时间范围选择器
- 功能介绍
- 属性说明
- 事件说明
- 实现代码
- 使用范例
根据业务需要,实现了一个可选择时间范围的周视图。用户可以通过鼠标拖动来选择时间段,并且可以通过快速选择组件来快速选择特定的时间范围。

功能介绍
- 时间范围选择:用户可以通过鼠标拖动来选择时间段。
- 快速选择:提供快速选择组件,用户可以通过点击快速选择特定的时间范围,如上午、下午、工作日、周末等。
- 自定义样式:可以通过
selectionColor属性自定义选中区域的颜色。 - 数据绑定:通过
modelValue属性与父组件进行数据绑定,实时更新选择的时间范围。
属性说明
modelValue:绑定的时间范围数据,类型为数组。
selectionColor:选中区域的颜色,类型为字符串,默认为 ‘rgba(5, 146, 245, 0.6)’。
showQuickSelect:是否显示快速选择组件,类型为布尔值,默认为 true。
事件说明
update:modelValue:当选择的时间范围发生变化时触发,返回更新后的时间范围数据。
实现代码
index.vue
<template><div class="zt-weektime"><div :class="{ 'zt-schedule-notransi': mode }" :style="[styleValue, { backgroundColor: selectionColor }]" class="zt-schedule"></div><table class="zt-weektime-table"><thead class="zt-weektime-head"><tr><td class="week-td" rowspan="8"></td><td v-for="t in theadArr" :key="t" :colspan="2">{{ t }}:00</td></tr><!-- <tr>--><!-- <td v-for="t in 48" :key="t" class="half-hour">--><!-- {{ t % 2 === 0 ? "00" : "30" }}--><!-- </td>--><!-- </tr>--></thead><tbody class="zt-weektime-body"><tr v-for="t in weekData" :key="t.row"><td>{{ t.value }}</td><tdv-for="n in t.child":key="`${n.row}-${n.col}`":class="['weektime-atom-item', { selected: isSelected(n) }]":data-time="n.col":data-week="n.row":style="{ '--selection-color': selectionColor }"@mousedown="cellDown(n)"@mouseenter="cellEnter(n)"@mouseup="cellUp(n)"></td></tr><tr><td class="zt-weektime-preview" colspan="49"><QuickSelect v-if="showQuickSelect" style="padding: 10px 0" @select="handleQuickSelect" /><!-- <div class="g-clearfix zt-weektime-con">--><!-- <span class="g-pull-left">{{ hasSelection ? "已选择时间段" : "可拖动鼠标选择时间段" }}</span>--><!-- </div>--><!-- <div v-if="hasSelection" class="zt-weektime-time">--><!-- <div v-for="(ranges, week) in formattedSelections" :key="week">--><!-- <p v-if="ranges.length">--><!-- <span class="g-tip-text">{{ week }}:</span>--><!-- <span>{{ ranges.join("、") }}</span>--><!-- </p>--><!-- </div>--><!-- </div>--></td></tr></tbody></table></div>
</template>
<script setup>
import { computed, defineEmits, defineProps, onMounted, ref, watch } from "vue";
import QuickSelect from "./quickSelect.vue";defineOptions({name: "ZtWeekTimeRange"
});const props = defineProps({modelValue: {type: Array,required: true,default: () => []},selectionColor: {type: String,default: "rgba(5, 146, 245, 0.6)"},showQuickSelect: {type: Boolean,default: true}
});const emit = defineEmits(["update:modelValue"]);const weekData = ref([{ row: 0, value: "周一", child: [] },{ row: 1, value: "周二", child: [] },{ row: 2, value: "周三", child: [] },{ row: 3, value: "周四", child: [] },{ row: 4, value: "周五", child: [] },{ row: 5, value: "周六", child: [] },{ row: 6, value: "周日", child: [] }
]);// UI State
const width = ref(0);
const height = ref(0);
const left = ref(0);
const top = ref(0);
const mode = ref(0);
const startRow = ref(0);
const startCol = ref(0);
const endRow = ref(0);
const endCol = ref(0);
const isDragging = ref(false);
const theadArr = ref([]);onMounted(() => {theadArr.value = Array.from({ length: 24 }, (_, i) => i);initializeGridData();syncSelectionFromValue();
});watch(() => props.modelValue, syncSelectionFromValue, { deep: true });function handleQuickSelect({ type, start, end, days }) {if (type === "morning" || type === "afternoon") {// 清除现有选择weekData.value.forEach((week) => {week.child.forEach((slot) => {if (slot.col >= start && slot.col <= end) {slot.selected = true;}});});} else if (type === "workdays" || type === "weekend") {days.forEach((dayIndex) => {weekData.value[dayIndex].child.forEach((slot) => {slot.selected = true;});});} else if (type === "all") {weekData.value.forEach((week) => {week.child.forEach((slot) => {slot.selected = true;});});} else if (type === "clean") {weekData.value.forEach((week) => {week.child.forEach((slot) => {slot.selected = false;});});}emitSelectionChange();
}function formatTimeRange(start, end) {const formatTime = (slots) => {const hours = Math.floor(slots / 2);const minutes = (slots % 2) * 30;return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;};return `${formatTime(start)}-${formatTime(end)}`;
}function initializeGridData() {weekData.value.forEach((week) => {week.child = Array.from({ length: 48 }, (_, i) => ({row: week.row,col: i,selected: false}));});
}function syncSelectionFromValue() {weekData.value.forEach((week) => {week.child.forEach((slot) => {slot.selected = false;});});props.modelValue.forEach((selection) => {const { week, ranges } = selection;const weekIndex = weekData.value.findIndex((w) => w.value === week);if (weekIndex !== -1) {ranges.forEach((range) => {const [start, end] = range.split("-").map((time) => {const [hours, minutes] = time.split(":").map(Number);return hours * 2 + (minutes === 30 ? 1 : 0);});for (let i = start; i <= end; i++) {const slot = weekData.value[weekIndex].child[i];if (slot) slot.selected = true;}});}});
}const styleValue = computed(() => ({width: `${width.value}px`,height: `${height.value}px`,left: `${left.value}px`,top: `${top.value}px`
}));const hasSelection = computed(() => {return weekData.value.some((week) => week.child.some((slot) => slot.selected));
});const formattedSelections = computed(() => {const selections = {};weekData.value.forEach((week) => {const ranges = [];let start = null;week.child.forEach((slot, index) => {if (slot.selected && start === null) {start = index;} else if (!slot.selected && start !== null) {ranges.push(formatTimeRange(start, index - 1));start = null;}});if (start !== null) {ranges.push(formatTimeRange(start, week.child.length - 1));}if (ranges.length) {selections[week.value] = ranges;}});return selections;
});function isSelected(slot) {return slot.selected;
}function cellDown(item) {isDragging.value = true;startRow.value = item.row;startCol.value = item.col;endRow.value = item.row;endCol.value = item.col;const ele = document.querySelector(`td[data-week='${item.row}'][data-time='${item.col}']`);if (ele) {width.value = ele.offsetWidth;height.value = ele.offsetHeight;left.value = ele.offsetLeft;top.value = ele.offsetTop;}mode.value = 1;
}function cellEnter(item) {if (!isDragging.value) return;endRow.value = item.row;endCol.value = item.col;const ele = document.querySelector(`td[data-week='${item.row}'][data-time='${item.col}']`);if (!ele) return;const minRow = Math.min(startRow.value, endRow.value);const maxRow = Math.max(startRow.value, endRow.value);const minCol = Math.min(startCol.value, endCol.value);const maxCol = Math.max(startCol.value, endCol.value);const startEle = document.querySelector(`td[data-week='${minRow}'][data-time='${minCol}']`);if (startEle) {left.value = startEle.offsetLeft;top.value = startEle.offsetTop;width.value = (maxCol - minCol + 1) * ele.offsetWidth;height.value = (maxRow - minRow + 1) * ele.offsetHeight;}
}function cellUp() {if (!isDragging.value) return;const minRow = Math.min(startRow.value, endRow.value);const maxRow = Math.max(startRow.value, endRow.value);const minCol = Math.min(startCol.value, endCol.value);const maxCol = Math.max(startCol.value, endCol.value);const isDeselecting = weekData.value[minRow].child[minCol].selected;weekData.value.forEach((week, weekIndex) => {if (weekIndex >= minRow && weekIndex <= maxRow) {week.child.forEach((slot, slotIndex) => {if (slotIndex >= minCol && slotIndex <= maxCol) {slot.selected = !isDeselecting;}});}});isDragging.value = false;width.value = 0;height.value = 0;mode.value = 0;emitSelectionChange();
}function emitSelectionChange() {const selections = weekData.value.map((week) => {const ranges = [];let start = null;week.child.forEach((slot, index) => {if (slot.selected && start === null) {start = index;} else if (!slot.selected && start !== null) {ranges.push(formatTimeRange(start, index - 1));start = null;}});if (start !== null) {ranges.push(formatTimeRange(start, week.child.length - 1));}return {week: week.value,ranges};}).filter((week) => week.ranges.length > 0);emit("update:modelValue", selections);
}
</script><style scoped>
.zt-weektime {min-width: 600px;position: relative;display: inline-block;
}.zt-schedule {position: absolute;width: 0;height: 0;pointer-events: none;transition: background-color 0.3s ease;
}.zt-schedule-notransi {transition:width 0.12s cubizt-bezier(0.4, 0, 0.2, 1),height 0.12s cubizt-bezier(0.4, 0, 0.2, 1),top 0.12s cubizt-bezier(0.4, 0, 0.2, 1),left 0.12s cubizt-bezier(0.4, 0, 0.2, 1);
}.zt-weektime-table {border-collapse: collapse;width: 100%;
}.zt-weektime-table th,
.zt-weektime-table td {user-select: none;border: 1px solid #dee4f5;text-align: center;min-width: 12px;line-height: 1.8em;transition: background 0.2s ease;
}.zt-weektime-table tr {height: 30px;
}.zt-weektime-head {font-size: 12px;
}.zt-weektime-head .week-td {min-width: 40px;width: 70px;
}.half-hour {color: #666;font-size: 10px;
}.zt-weektime-body {font-size: 12px;
}.weektime-atom-item {user-select: unset;background-color: #f5f5f5;cursor: pointer;width: 20px;transition: background-color 0.3s ease;
}.weektime-atom-item.selected {background-color: var(--selection-color, rgba(5, 146, 245, 0.6));animation: selectPulse 0.3s ease-out;
}@keyframes selectPulse {0% {transform: scale(0.95);opacity: 0.7;}50% {transform: scale(1.02);opacity: 0.85;}100% {transform: scale(1);opacity: 1;}
}.zt-weektime-preview {line-height: 2.4em;padding: 0 10px;font-size: 14px;
}.zt-weektime-preview .zt-weektime-con {line-height: 46px;user-select: none;
}.zt-weektime-preview .zt-weektime-time {text-align: left;line-height: 2.4em;
}.zt-weektime-preview .zt-weektime-time p {max-width: 625px;line-height: 1.4em;word-break: break-all;margin-bottom: 8px;
}.g-clearfix:after,
.g-clearfix:before {clear: both;content: " ";display: table;
}.g-pull-left {float: left;
}.g-tip-text {color: #999;
}
</style>
quickSelect.vue
<template><div class="quick-select"><el-button-group><el-button v-for="option in quickOptions" :key="option.key" size="small" @click="handleQuickSelect(option.key)">{{ option.label }}</el-button></el-button-group><el-button-group><el-button size="small" @click="handleQuickSelect('all')"> 全选</el-button><el-button size="small" @click="handleQuickSelect('clean')"> 清空</el-button></el-button-group></div>
</template><script setup>
const props = defineProps({show: {type: Boolean,default: true}
});const emit = defineEmits(["select"]);const quickOptions = [{ key: "morning", label: "上午" },{ key: "afternoon", label: "下午" },{ key: "workdays", label: "工作日" },{ key: "weekend", label: "周末" }
];const timeRanges = {morning: { start: 16, end: 23 }, // 8:00-12:00afternoon: { start: 26, end: 35 }, // 13:00-18:00workdays: { days: [0, 1, 2, 3, 4] }, // 周一到周五weekend: { days: [5, 6] }, // 周六周日all: {}, // 全选clean: {} // 清空
};function handleQuickSelect(type) {emit("select", { type, ...timeRanges[type] });
}
</script><style scoped>
.quick-select {display: flex;justify-content: space-between;
}
</style>
使用范例
效果:

实现代码:
<template><div><h1>时间段选择示例</h1><div class="color-picker"><span>选择颜色:</span><el-color-picker v-model="selectedColor" :predefine="predefineColors" show-alpha /></div><ZtWeekTimeRangev-model="selectedTimeRanges":selection-color="selectedColor":show-quick-select="true"@update:modelValue="handleTimeRangeChange"/><div class="selected-ranges"><h3>选中的时间段:</h3><pre style="height: 200px">{{ JSON.stringify(selectedTimeRanges, null, 2) }}</pre></div><div class="demo-controls"><button class="demo-button" @click="setDemoData">加载示例数据</button><button class="demo-button" @click="clearSelection">清除选择</button></div></div>
</template><script setup>
import { ref } from "vue";defineOptions({name: "星期时间范围选择器",
});const selectedTimeRanges = ref([]);
const selectedColor = ref("rgba(5, 146, 245, 0.6)");const predefineColors = ["rgba(5, 146, 245, 0.6)","rgba(64, 158, 255, 0.6)","rgba(103, 194, 58, 0.6)","rgba(230, 162, 60, 0.6)","rgba(245, 108, 108, 0.6)"
];function handleTimeRangeChange(newRanges) {console.log("Time ranges updated:", newRanges);
}function setDemoData() {selectedTimeRanges.value = [{week: "周一",ranges: ["09:00-12:00", "14:00-18:30"]},{week: "周三",ranges: ["10:30-16:00"]},{week: "周五",ranges: ["09:30-12:00", "13:30-17:30"]}];
}function clearSelection() {selectedTimeRanges.value = [];
}
</script><style>
.selected-ranges {padding: 15px;background: #f5f5f5;border-radius: 4px;
}pre {background: #fff;padding: 10px;border-radius: 4px;overflow-x: auto;
}.demo-controls {margin-top: 20px;display: flex;gap: 10px;
}.demo-button {padding: 8px 16px;background-color: #0592f5;color: white;border: none;border-radius: 4px;cursor: pointer;transition: background-color 0.2s;
}.demo-button:hover {background-color: #0481d9;
}
</style>相关文章:
星期-时间范围选择器 滑动选择时间 最小粒度 vue3
星期-时间范围选择器 功能介绍属性说明事件说明实现代码使用范例 根据业务需要,实现了一个可选择时间范围的周视图。用户可以通过鼠标拖动来选择时间段,并且可以通过快速选择组件来快速选择特定的时间范围。 功能介绍 时间范围选择:用户可以…...
一条SQL查询语句的执行流程(MySQL)
第一步:连接器(负责跟客户端建立连接、获取权限、维持和管理连接) 第二步:查询缓存 之前执行过的查询,MySQL以"Key - Value"的形式存在内存(key为SQL,value为结果集)&…...
linux基础——详细篇
免责声明 学习视频来自B 站up主泷羽sec,如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识,以下代码、网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。 linux 基础命令重现 cd(切…...
大数据学习10之Hive高级
1.Hive高级 将大的文件按照某一列属性进行GROUP BY 就是分区,只是默认开窗存储; 分区是按行,如一百行数据,按十位上的数字分区,则有十个分区,每个分区里有十行; 分桶是根据某个字段哈希对桶数取…...
MongoDB笔记01-概念与安装
文章目录 前言一、MongoDB相关概念1.1 业务应用场景具体的应用场景什么时候选择MongoDB 1.2 MongoDB简介1.3 体系结构1.4 数据模型1.5 MongoDB的特点 二、本地单机部署2.1 Windows系统中的安装启动第一步:下载安装包第二步:解压安装启动1.命令行参数方式…...
ollama + fastGPT + m3e 本地部署指南
[TOC](ollama fastgptm3e本地部署) 开启WSL 因为这里使用的win部署,所以要安装wsl,如果是linux系统就没那么麻烦 控制面板->程序->程序和功能 更新wsl wsl --set-default-version 2wsl --update --web-download安装ubuntu wsl --install -d Ubuntudoc…...
【设计模式系列】享元模式(十五)
目录 一、什么是享元模式 二、享元模式的角色 三、享元模式的典型应用场景 四、享元模式在ThreadPoolExecutor中的应用 1. 享元对象(Flyweight)- 工作线程(Worker) 2. 享元工厂(Flyweight Factory)- …...
2024大兴区火锅美食节即将开幕——品味多元火锅,点燃冬季消费热潮
为响应“中国国际精品消费月”活动,由大兴区商务局主办、大兴区餐饮行业协会承办的2024大兴区火锅美食节将于11月15日正式启动,为期一个半月的美食盛宴将在大兴区掀起一场冬日的火锅热潮。此次火锅节作为北京市“食在京城、沸腾火锅”火锅美食节的重要组…...
可视化建模与UML《类图实验报告》
史铁生: 余华和莫言扛着我上火车, 推着走打雪仗, 还带我偷西瓜, 被人发现后他们拔腿就跑, 却忘了我还在西瓜地里。 一、实验目的: 1、熟悉类图的构件事物。 2、熟悉类之间的泛化、依赖、聚合和组合关系…...
VS2022项目配置笔记
文章目录 $(ProjectDir)与 $(SolutionDir) 宏附加包含目录VC目录和C/C的区别 $(ProjectDir)与 $(SolutionDir) 宏 假设有一个解决方案 MySolution,其中包含两个项目 ProjectA 和 ProjectB,目录结构如下: C:\Projects\…...
springboot029基于springboot的网上购物商城系统
🍅点赞收藏关注 → 添加文档最下方联系方式领取本源代码、数据库🍅 本人在Java毕业设计领域有多年的经验,陆续会更新更多优质的Java实战项目希望你能有所收获,少走一些弯路。🍅关注我不迷路🍅 项目视频 基于…...
网站访问在TCP/IP四层模型中的流程
访问一个网站的过程可以通过 TCP/IP 网络模型来描述。TCP/IP 模型通常被分为四层:应用层、传输层、网络层和链路层。以下是从这些层级的角度描述你访问一个网站时所发生的过程: 1. 应用层 (Application Layer) 当你在浏览器中输入一个 URL(…...
C++笔记---包装器
1. 什么是包装器 C中的包装器是一种设计模式,用于将一个复杂或底层的接口进行封装,以便提供一个更简洁、易用的接口。包装器可以包装任何类型的可调用实体,如函数,成员函数,函数指针,仿函数对象࿰…...
算力与能量的全分布式在线共享来降低5G网络的用电成本。基于随机对偶次梯度法的多时隙约束耦合问题解耦方法示例;随机对偶次梯度法的在线管理策略
目录 算力与能量的全分布式在线共享来降低5G网络的用电成本。 基于随机对偶次梯度法的多时隙约束耦合问题解耦方法示例 随机对偶次梯度法的在线管理策略 策略概述 具体步骤 示例说明 算力与能量的全分布式在线共享来降低5G网络的用电成本。 主要探讨了5G网络与边缘计算设…...
海鲜特写镜头视频素材去哪找 热门视频素材网站分享
作为美食自媒体创作者,海鲜特写镜头的视频素材无疑是提升内容吸引力和质量的重要利器。无论你想展示新鲜的海鲜原料、精美的烹饪过程,还是诱人的餐桌美食,精致的海鲜特写镜头都能极大地吸引观众的注意力。那么,问题来了࿱…...
JMM内存模型(面试回答)
1.什么是JMM JMM就是Java内存模型(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的代码运行在不同的系统上会出现各种问题。所以Java内存模型(JMM)屏蔽掉各种硬件和操作系统的内存访问差异&…...
Greiner 经典力学(多体系统和哈密顿力学)第十二章 学习笔记(Rotation About a Point)
第十二章 学习笔记(Rotation About a Point) 上一章是绕定轴转动,这章是绕定点转动。这一章明显上难度了。 12.1 Tensor of Inertia 在正式的公式推导之前,我们先复习一个矢量公式,下面推导时会用到这个公式&#x…...
SQL进阶技巧:如何计算复合增长率?
目录 0 场景描述 1 数据准备 2 问题分析 3 小结 0 场景描述 复合增长率是第N期的数据除以第一期的基准数据,然后开N-1次方再减去1得到的结果。假如2018年的产品销售额为10000,2019年的产品销售额为12500,2020年的产品销售额为15000(销售额单位省略,下同)。那么这两…...
十一:java web(3)-- Spring框架 -- Spring简介
目录 1. Servlet 与 Spring 的关系 2. Spring 框架介绍 Spring 框架的起源与发展 Spring 框架的核心特性 Spring 主要模块介绍 核心模块(Core Container) 数据访问与集成模块(Data Access/Integration) Web 模块࿰…...
ts 如何配置引入 json 文件
ts 如何配置引入 json 文件 参考文档: https://maxgadget.dev/article/how-to-import-a-json-file-in-typescript-a-comprehensive-guide 项目中有一个 .json 的文件是配置文件,如何引入到 ts 项目中 配置 tsconfig.json 文件,添加这两个 {…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
