automated_uniapp/uni_modules/cool-ui/components/cl-list-item/cl-list-item.vue

259 lines
4.9 KiB
Vue
Raw Normal View History

2025-01-09 16:16:11 +08:00
<template>
<view
class="cl-list-item"
:class="[itemClass]"
:style="[baseStyle]"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
@click="tap"
>
<view
class="cl-list-item__swiper"
:style="{
transform,
}"
>
<view class="cl-list-item__container">
<view class="cl-list-item__icon" v-if="$slots.icon">
<slot name="icon"></slot>
</view>
<text class="cl-list-item__label" v-if="label && label != 'true'">{{ label }}</text>
<view class="cl-list-item__content" :class="contentClass">
<slot></slot>
</view>
<view class="cl-list-item__append">
<slot name="append">
<text v-if="arrowIcon" class="cl-icon-arrow-right"></text>
</slot>
</view>
</view>
<template v-if="swipe">
<view :class="[`cl-list-item__menu-${swipe}`]">
<slot name="menu"></slot>
</view>
</template>
</view>
</view>
</template>
<script lang="ts">
import { computed, defineComponent, getCurrentInstance, onMounted, reactive, watch } from "vue";
import { type PropType } from "vue";
import { isBoolean } from "lodash-es";
import { useTap, useStyle } from "../../hooks";
import { getParent } from "/@/cool/utils";
export default defineComponent({
name: "cl-list-item",
props: {
// 标签内容
label: String,
// 类型
type: String as PropType<"primary" | "success" | "error" | "warning" | "info">,
// 水平排序
justify: String as PropType<"start" | "end" | "center">,
// 是否滑动
swipe: String as PropType<"left" | "right">,
// 是否禁用
disabled: {
type: Boolean,
default: null,
},
// 是否带有下边框
border: {
type: Boolean,
default: null,
},
// 是否显示右侧箭头
arrowIcon: {
type: Boolean,
default: true,
},
},
setup(props, { slots, emit }) {
const instance = getCurrentInstance();
// <cl-list />
const parent = getParent("cl-list", ["justify", "border", "disabled"]);
// 是否禁用
const isDisabled = computed(() => {
return isBoolean(props.disabled) ? props.disabled : parent.value?.disabled;
});
// 样式
const itemClass = computed(() => {
return [
{
"is-disabled": isDisabled.value,
"is-append": !!slots.append,
"is-swipe": !!props.swipe,
"is-border": isBoolean(props.border) ? props.border : parent.value?.border,
},
];
});
// 内容样式
const contentClass = computed(() => {
const list: string[] = [];
if (props.type) {
list.push(`is-color-${props.type}`);
}
return [
`is-color-${props.type}`,
`is-justify-${props.justify || parent.value?.justify || "end"}`,
];
});
// 触摸
const touch = reactive({
start: 0,
end: 0,
x: 0,
maxX: 0,
direction: "left",
lock: true,
});
// 滑动菜单
const menu = reactive({
width: 0,
});
// 横向位置移动
const transform = computed(() => {
return props.swipe ? `translateX(${touch.x}px)` : "none";
});
// 滑动开始
function onTouchStart(e: TouchEvent) {
if (props.swipe) {
touch.start = e.touches[0].pageX;
touch.lock = false;
}
}
// 滑动中
function onTouchMove(e: TouchEvent) {
const { start, end, lock, maxX } = touch;
if (!lock) {
// 滑动距离
const offsetX = e.touches[0].pageX - start;
// 移动方向
touch.direction = offsetX > 0 ? "right" : "left";
// 偏移距离
let x = end + offsetX;
if (props.swipe == "left") {
if (x > maxX) {
x = maxX;
}
if (x < 0) {
x = 0;
}
}
if (props.swipe == "right") {
if (x < maxX) {
x = maxX;
}
if (x > 0) {
x = 0;
}
}
touch.x = x;
}
}
// 滑动结束
function onTouchEnd() {
const { direction, x, end, lock, maxX } = touch;
const sw = menu.width / 2 > 50 ? 50 : menu.width / 2;
if (!lock) {
if (Math.abs(x - end) > sw) {
if (direction === props.swipe) {
touch.x = 0;
} else {
touch.x = maxX;
}
touch.end = touch.x;
} else {
touch.x = end === 0 ? 0 : maxX;
}
touch.lock = true;
}
}
// 设置菜单宽度
function setMenu() {
if (props.swipe) {
const query = uni.createSelectorQuery().in(instance?.proxy);
query
.select(`.cl-list-item__menu-${props.swipe}`)
.boundingClientRect((data) => {
if (data) {
menu.width = data.width || 0;
touch.maxX = menu.width * (props.swipe === "right" ? -1 : 1);
}
})
.exec();
}
}
// 滑动后还原位置的方法
function restore(cb: () => void) {
touch.start = 0;
touch.end = 0;
touch.lock = true;
touch.x = 0;
if (cb) {
setTimeout(() => {
cb();
}, 300);
}
}
// 重新设置滑动菜单
watch(() => props.swipe, setMenu);
onMounted(() => {
setMenu();
});
return {
itemClass,
contentClass,
touch,
menu,
transform,
restore,
onTouchStart,
onTouchMove,
onTouchEnd,
...useTap(emit),
...useStyle(),
};
},
});
</script>