添加查看更多组件
This commit is contained in:
@@ -316,6 +316,12 @@
|
|||||||
"navigationBarTitleText": "Avatar 头像"
|
"navigationBarTitleText": "Avatar 头像"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "data/read-more",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "ReadMore 查看更多"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "data/draggable",
|
"path": "data/draggable",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
85
pages/demo/data/read-more.uvue
Normal file
85
pages/demo/data/read-more.uvue
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<cl-page>
|
||||||
|
<view class="p-3">
|
||||||
|
<demo-item :label="t('基础用法')">
|
||||||
|
<cl-read-more>
|
||||||
|
<cl-text>
|
||||||
|
云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。
|
||||||
|
一枝红艳露凝香,云雨巫山枉断肠。借问汉宫谁得似?可怜飞燕倚新妆。
|
||||||
|
名花倾国两相欢,常得君王带笑看。解释春风无限恨,沉香亭北倚阑干。
|
||||||
|
</cl-text>
|
||||||
|
</cl-read-more>
|
||||||
|
</demo-item>
|
||||||
|
|
||||||
|
<demo-item :label="t('禁用切换按钮')">
|
||||||
|
<cl-read-more
|
||||||
|
v-model="visible"
|
||||||
|
:disabled="disabled"
|
||||||
|
:expand-text="disabled ? '付费解锁' : '展开'"
|
||||||
|
:expand-icon="disabled ? 'lock-line' : 'arrow-down-s-line'"
|
||||||
|
@toggle="toggle"
|
||||||
|
>
|
||||||
|
<cl-text>
|
||||||
|
云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。
|
||||||
|
一枝红艳露凝香,云雨巫山枉断肠。借问汉宫谁得似?可怜飞燕倚新妆。
|
||||||
|
名花倾国两相欢,常得君王带笑看。解释春风无限恨,沉香亭北倚阑干。
|
||||||
|
</cl-text>
|
||||||
|
</cl-read-more>
|
||||||
|
</demo-item>
|
||||||
|
|
||||||
|
<demo-item :label="t('自定义高度')">
|
||||||
|
<cl-read-more :height="300">
|
||||||
|
<cl-text>
|
||||||
|
云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。
|
||||||
|
一枝红艳露凝香,云雨巫山枉断肠。借问汉宫谁得似?可怜飞燕倚新妆。
|
||||||
|
</cl-text>
|
||||||
|
|
||||||
|
<cl-image
|
||||||
|
:height="300"
|
||||||
|
width="100%"
|
||||||
|
:pt="{
|
||||||
|
className: 'my-3'
|
||||||
|
}"
|
||||||
|
src="https://uni-docs.cool-js.com/demo/pages/demo/static/bg1.png"
|
||||||
|
></cl-image>
|
||||||
|
|
||||||
|
<cl-text>
|
||||||
|
名花倾国两相欢,常得君王带笑看。解释春风无限恨,沉香亭北倚阑干。
|
||||||
|
</cl-text>
|
||||||
|
</cl-read-more>
|
||||||
|
</demo-item>
|
||||||
|
</view>
|
||||||
|
</cl-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { router } from "@/cool";
|
||||||
|
import { t } from "@/locale";
|
||||||
|
import DemoItem from "../components/item.uvue";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useUi } from "@/uni_modules/cool-ui";
|
||||||
|
|
||||||
|
const ui = useUi();
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const disabled = ref(true);
|
||||||
|
|
||||||
|
function toggle(isExpanded: boolean) {
|
||||||
|
ui.showConfirm({
|
||||||
|
title: "提示",
|
||||||
|
message: "需支付100元才能解锁全部内容,是否继续?",
|
||||||
|
callback(action) {
|
||||||
|
if (action == "confirm") {
|
||||||
|
ui.showToast({
|
||||||
|
message: "支付成功"
|
||||||
|
});
|
||||||
|
|
||||||
|
disabled.value = false;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<view class="group" v-for="item in data" :key="item.label">
|
<view class="group" v-for="item in data" :key="item.label">
|
||||||
<cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-2' }">{{
|
<cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-2' }">{{
|
||||||
item.label
|
item.label
|
||||||
}}</cl-text>
|
}}({{ item.children?.length ?? 0 }})</cl-text>
|
||||||
|
|
||||||
<view class="list">
|
<view class="list">
|
||||||
<cl-row :gutter="10">
|
<cl-row :gutter="10">
|
||||||
@@ -275,6 +275,11 @@ const data = computed<Item[]>(() => {
|
|||||||
icon: "account-circle-line",
|
icon: "account-circle-line",
|
||||||
path: "/pages/demo/data/avatar"
|
path: "/pages/demo/data/avatar"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t("查看更多"),
|
||||||
|
icon: "menu-add-line",
|
||||||
|
path: "/pages/demo/data/read-more"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t("列表"),
|
label: t("列表"),
|
||||||
icon: "list-check",
|
icon: "list-check",
|
||||||
|
|||||||
230
uni_modules/cool-ui/components/cl-read-more/cl-read-more.uvue
Normal file
230
uni_modules/cool-ui/components/cl-read-more/cl-read-more.uvue
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
<template>
|
||||||
|
<view class="cl-read-more" :class="[pt.className]">
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view class="cl-read-more__wrapper" :class="[pt.wrapper?.className]" :style="wrapperStyle">
|
||||||
|
<view class="cl-read-more__content" :class="[pt.content?.className]">
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
class="cl-read-more__mask"
|
||||||
|
:class="[
|
||||||
|
{
|
||||||
|
'is-show': !isExpanded && showToggle,
|
||||||
|
'is-dark': isDark
|
||||||
|
},
|
||||||
|
pt.mask?.className
|
||||||
|
]"
|
||||||
|
></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 展开/收起按钮 -->
|
||||||
|
<slot name="toggle" :isExpanded="isExpanded">
|
||||||
|
<view
|
||||||
|
class="cl-read-more__toggle"
|
||||||
|
:class="[
|
||||||
|
{
|
||||||
|
'is-disabled': disabled
|
||||||
|
},
|
||||||
|
pt.toggle?.className
|
||||||
|
]"
|
||||||
|
@tap="toggle"
|
||||||
|
v-if="showToggle"
|
||||||
|
>
|
||||||
|
<cl-text
|
||||||
|
color="primary"
|
||||||
|
:pt="{
|
||||||
|
className: 'text-sm mr-1'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ isExpanded ? collapseText : expandText }}
|
||||||
|
</cl-text>
|
||||||
|
<cl-icon :name="isExpanded ? collapseIcon : expandIcon" color="primary"></cl-icon>
|
||||||
|
</view>
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, getCurrentInstance, ref, onMounted, watch } from "vue";
|
||||||
|
import { getPx, isDark, parsePt } from "@/cool";
|
||||||
|
import type { PassThroughProps } from "../../types";
|
||||||
|
import { t } from "@/locale";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "cl-read-more"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件属性定义
|
||||||
|
const props = defineProps({
|
||||||
|
// 透传样式
|
||||||
|
pt: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
// 是否展开
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 收起状态下的最大高度
|
||||||
|
height: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 80
|
||||||
|
},
|
||||||
|
// 展开文本
|
||||||
|
expandText: {
|
||||||
|
type: String,
|
||||||
|
default: () => t("展开")
|
||||||
|
},
|
||||||
|
// 收起文本
|
||||||
|
collapseText: {
|
||||||
|
type: String,
|
||||||
|
default: () => t("收起")
|
||||||
|
},
|
||||||
|
// 展开图标
|
||||||
|
expandIcon: {
|
||||||
|
type: String,
|
||||||
|
default: "arrow-down-s-line"
|
||||||
|
},
|
||||||
|
// 收起图标
|
||||||
|
collapseIcon: {
|
||||||
|
type: String,
|
||||||
|
default: "arrow-up-s-line"
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 事件定义
|
||||||
|
const emit = defineEmits(["update:modelValue", "change", "toggle"]);
|
||||||
|
|
||||||
|
// 插槽定义
|
||||||
|
defineSlots<{
|
||||||
|
toggle(props: { isExpanded: boolean }): any;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance()!;
|
||||||
|
|
||||||
|
// 透传样式类型
|
||||||
|
type PassThrough = {
|
||||||
|
className?: string;
|
||||||
|
wrapper?: PassThroughProps;
|
||||||
|
content?: PassThroughProps;
|
||||||
|
mask?: PassThroughProps;
|
||||||
|
toggle?: PassThroughProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 解析透传样式
|
||||||
|
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
||||||
|
|
||||||
|
// 展开状态
|
||||||
|
const isExpanded = ref(props.modelValue);
|
||||||
|
|
||||||
|
// 内容实际高度
|
||||||
|
const contentHeight = ref(0);
|
||||||
|
|
||||||
|
// 是否显示切换按钮
|
||||||
|
const showToggle = ref(true);
|
||||||
|
|
||||||
|
// 包裹器样式
|
||||||
|
const wrapperStyle = computed(() => {
|
||||||
|
const style = {};
|
||||||
|
|
||||||
|
if (showToggle.value) {
|
||||||
|
style["height"] = isExpanded.value ? `${contentHeight.value}px` : `${props.height}rpx`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换展开/收起状态
|
||||||
|
*/
|
||||||
|
function toggle() {
|
||||||
|
if (props.disabled) {
|
||||||
|
emit("toggle", isExpanded.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpanded.value = !isExpanded.value;
|
||||||
|
emit("update:modelValue", isExpanded.value);
|
||||||
|
emit("change", isExpanded.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取内容实际高度
|
||||||
|
*/
|
||||||
|
function getContentHeight() {
|
||||||
|
uni.createSelectorQuery()
|
||||||
|
.in(proxy)
|
||||||
|
.select(".cl-read-more__content")
|
||||||
|
.boundingClientRect((node) => {
|
||||||
|
// 获取内容高度
|
||||||
|
contentHeight.value = (node as NodeInfo).height ?? 0;
|
||||||
|
|
||||||
|
// 实际高度是否大于折叠高度
|
||||||
|
showToggle.value = contentHeight.value > getPx(props.height);
|
||||||
|
})
|
||||||
|
.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听modelValue变化
|
||||||
|
watch(
|
||||||
|
computed(() => props.modelValue),
|
||||||
|
(val: boolean) => {
|
||||||
|
isExpanded.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 组件挂载后获取内容高度
|
||||||
|
onMounted(() => {
|
||||||
|
getContentHeight();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
toggle
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-read-more {
|
||||||
|
@apply relative;
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
@apply relative duration-300;
|
||||||
|
transition-property: height;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__mask {
|
||||||
|
@apply absolute bottom-0 left-0 w-full h-14 opacity-0 pointer-events-none;
|
||||||
|
|
||||||
|
// #ifndef APP-IOS
|
||||||
|
transition-duration: 200ms;
|
||||||
|
transition-property: opacity;
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
background-image: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
|
||||||
|
|
||||||
|
&.is-dark {
|
||||||
|
background-image: linear-gradient(to top, rgba(30, 30, 30, 1), rgba(30, 30, 30, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-show {
|
||||||
|
@apply opacity-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__toggle {
|
||||||
|
@apply flex flex-row items-center justify-center mt-2;
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
@apply opacity-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
20
uni_modules/cool-ui/components/cl-read-more/props.ts
Normal file
20
uni_modules/cool-ui/components/cl-read-more/props.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { PassThroughProps } from "../../types";
|
||||||
|
|
||||||
|
export type ClReadMorePassThrough = {
|
||||||
|
className?: string;
|
||||||
|
content?: PassThroughProps;
|
||||||
|
mask?: PassThroughProps;
|
||||||
|
toggle?: PassThroughProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ClReadMoreProps = {
|
||||||
|
className?: string;
|
||||||
|
pt?: ClReadMorePassThrough;
|
||||||
|
modelValue?: boolean;
|
||||||
|
height?: any;
|
||||||
|
expandText?: string;
|
||||||
|
collapseText?: string;
|
||||||
|
expandIcon?: string;
|
||||||
|
collapseIcon?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
2
uni_modules/cool-ui/index.d.ts
vendored
2
uni_modules/cool-ui/index.d.ts
vendored
@@ -51,6 +51,7 @@ import type { ClProgressCircleProps, ClProgressCirclePassThrough } from "./compo
|
|||||||
import type { ClQrcodeProps } from "./components/cl-qrcode/props";
|
import type { ClQrcodeProps } from "./components/cl-qrcode/props";
|
||||||
import type { ClRadioProps, ClRadioPassThrough } from "./components/cl-radio/props";
|
import type { ClRadioProps, ClRadioPassThrough } from "./components/cl-radio/props";
|
||||||
import type { ClRateProps, ClRatePassThrough } from "./components/cl-rate/props";
|
import type { ClRateProps, ClRatePassThrough } from "./components/cl-rate/props";
|
||||||
|
import type { ClReadMoreProps, ClReadMorePassThrough } from "./components/cl-read-more/props";
|
||||||
import type { ClRowProps, ClRowPassThrough } from "./components/cl-row/props";
|
import type { ClRowProps, ClRowPassThrough } from "./components/cl-row/props";
|
||||||
import type { ClSafeAreaProps, ClSafeAreaPassThrough } from "./components/cl-safe-area/props";
|
import type { ClSafeAreaProps, ClSafeAreaPassThrough } from "./components/cl-safe-area/props";
|
||||||
import type { ClSelectProps, ClSelectPassThrough } from "./components/cl-select/props";
|
import type { ClSelectProps, ClSelectPassThrough } from "./components/cl-select/props";
|
||||||
@@ -130,6 +131,7 @@ declare module "vue" {
|
|||||||
"cl-qrcode": (typeof import('./components/cl-qrcode/cl-qrcode.uvue')['default']) & import('vue').DefineComponent<ClQrcodeProps>;
|
"cl-qrcode": (typeof import('./components/cl-qrcode/cl-qrcode.uvue')['default']) & import('vue').DefineComponent<ClQrcodeProps>;
|
||||||
"cl-radio": (typeof import('./components/cl-radio/cl-radio.uvue')['default']) & import('vue').DefineComponent<ClRadioProps>;
|
"cl-radio": (typeof import('./components/cl-radio/cl-radio.uvue')['default']) & import('vue').DefineComponent<ClRadioProps>;
|
||||||
"cl-rate": (typeof import('./components/cl-rate/cl-rate.uvue')['default']) & import('vue').DefineComponent<ClRateProps>;
|
"cl-rate": (typeof import('./components/cl-rate/cl-rate.uvue')['default']) & import('vue').DefineComponent<ClRateProps>;
|
||||||
|
"cl-read-more": (typeof import('./components/cl-read-more/cl-read-more.uvue')['default']) & import('vue').DefineComponent<ClReadMoreProps>;
|
||||||
"cl-row": (typeof import('./components/cl-row/cl-row.uvue')['default']) & import('vue').DefineComponent<ClRowProps>;
|
"cl-row": (typeof import('./components/cl-row/cl-row.uvue')['default']) & import('vue').DefineComponent<ClRowProps>;
|
||||||
"cl-safe-area": (typeof import('./components/cl-safe-area/cl-safe-area.uvue')['default']) & import('vue').DefineComponent<ClSafeAreaProps>;
|
"cl-safe-area": (typeof import('./components/cl-safe-area/cl-safe-area.uvue')['default']) & import('vue').DefineComponent<ClSafeAreaProps>;
|
||||||
"cl-select": (typeof import('./components/cl-select/cl-select.uvue')['default']) & import('vue').DefineComponent<ClSelectProps>;
|
"cl-select": (typeof import('./components/cl-select/cl-select.uvue')['default']) & import('vue').DefineComponent<ClSelectProps>;
|
||||||
|
|||||||
4
uni_modules/cool-ui/types/component.d.ts
vendored
4
uni_modules/cool-ui/types/component.d.ts
vendored
@@ -238,3 +238,7 @@ declare type ClMarqueeComponentPublicInstance = {
|
|||||||
stop(): void;
|
stop(): void;
|
||||||
reset(): void;
|
reset(): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type ClReadMoreComponentPublicInstance = {
|
||||||
|
toggle(): void;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user