Files
hospital-rescue-app-v2/uni_modules/tmx-ui/components/x-cell/x-cell.uvue
T
2026-09-24 16:25:22 +08:00

519 lines
12 KiB
Plaintext

<script lang="ts" setup>
import { computed, watch } from "vue"
import { getDefaultColor, colorAddDeepen } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit, getUnit, fillArrayCssValue } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
type xCellItemType = {
icon : string,
title : string,
desc : string,
label : string,
bottom : boolean,
link : boolean,
url : string,
iconColor : string,
labelColor : string,
card : boolean
}
/**
* @name 列表 xCell
* @page /pages/index/cell
* @category 展示组件
* @description card为true时,圆角可统一全局配置和动态全局配置,保持所有页面列表样式统一,免于一个一个配置。
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({ name: "xCell" })
defineSlots<{
avatar(props : { icon : string }) : any
default() : any
desc(props : { desc : string }) : any
label(props : { label : string }) : any
right() : any
}>()
const emits = defineEmits([
/**
* 项目点击
*/
'click',
/**
* 等同v-model:show
*/
'update:show'
])
type xCellPropsType = {
/**
* 左图标
*/
icon : string,
/**
* 左侧图标、头像圆角。默认为8
*/
avatarRound : string,
/**
* 背景的主题色
*/
color : string,
/**
* 暗黑背景的主题色,空值时取sheetDarkColor
*/
darkColor : string,
/**
* 图标色,空值时取全局主题值。
*/
iconColor : string,
/**
* 标题
*/
title : string,
/**
* 标题颜色
*/
titleColor : string,
/**
* 暗黑标题颜色,如果不填写取白
*/
darkTitleColor : string,
/**
* 标题大小
*/
titleSize : string,
/**
* 图标大小
*/
iconSize : string,
/**
* 右边文本
*/
label : string,
/**
* 右边文本颜色
*/
labelColor : string,
/**
* 右侧label文字大小
*/
labelSize : string,
/**
* 标题正文的简介文本
*/
desc : string,
/**
* 是否显示下边线
*/
showBottomBorder : boolean,
/**
* 是否让下边线显示居右,不贯穿到左边。
*/
bottomBorderInsert : boolean,
/**
* 下边线的颜色。如果你设定了的话。
* 暗黑的边颜色失效,采用你自定的颜色。
*/
bottomBorderColor : string,
/**
* 是否显示链接状态,有点按效果。包括出现右边跳转指示。
* 关闭的话,事件反应和跳转会更快。
* 如果true右侧箭头图标会显示
*/
link : boolean,
/**
* 右指示图标的颜色
*/
linkColor : string,
/**
* 右指示图标的暗黑颜色
*/
linkDarkColor : string,
/**
* 需要跳转的页面地址。
* 如果填写了右侧箭头图标会显示
* 跳转时如果失败会回退到switchTab跳转。
*/
url : string,
/**
* 是否是卡片模式
*/
card : boolean,
/**
* 卡片模式圆角,不填写采用全局的cardRadius属性值.
*/
round : string,
/**
* 左边图标区域宽和高的大小。
*/
leftSize : string,
/**
* 最小高度,主要是用来统一风格高度不至于让点击范围过小
* 如果你需要紧凑型可以设置为auto
*/
minHeight : string,
/**
* 是否禁用url跳转,当link为true或者url需要跳转时
* 如果禁用,点击时不会触发跳转。
*/
disabled : boolean,
/**
* 内间隙[x]全部,[x,x]左右,上下,[x,x,x]左上右,[x,x,x,x]左上右下
* 空数组时取全局值
*/
padding : string[],
/**
* margin 同sheet原理
* [x]全部,[x,x]左右,上下,[x,x,x]左上右,[x,x,x,x]左上右下
* 空数组时取全局值cellMargin
*/
margin : string[],
/**
* 右侧label宽,插槽时,这个属性不会生效
* 以你自己布局宽为准。
*/
rightWidth : string
}
const props = withDefaults(defineProps<xCellPropsType>(), {
icon: "",
avatarRound: "8",
color: 'white',
darkColor: '',
iconColor: "",
title: "标题",
titleColor: "black",
darkTitleColor: "white",
titleSize: "16",
iconSize: "24",
label: "",
labelColor: "#bfbfbf",
labelSize: "13",
desc: "",
showBottomBorder: true,
bottomBorderInsert: false,
bottomBorderColor: "",
link: true,
linkColor: '#bfbfbf',
linkDarkColor: '#bfbfbf',
url: "",
card: true,
round: "",
leftSize: '32',
minHeight: "55",
disabled: false,
padding: () : string[] => ['12', '0'] as string[],
margin: () : string[] => [] as string[],
rightWidth: "100"
})
// 计算属性
const _padding = computed(() : string => {
if (props.padding.length == 0) {
let par = fillArrayCssValue(xConfig.sheetPadding)
if (par.length == 0) return "0px 0px 0px 0px";
return par.join(" ")
}
let ar : string[] = fillArrayCssValue(props.padding as string[])
if (ar.length == 0) return "0px 0px 0px 0px";
return ar.join(" ")
})
const _margin = computed(() : string => {
if (props.margin.length == 0) {
let par = fillArrayCssValue(xConfig.cellMargin)
if (par.length == 0) return "0px 0px 0px 0px";
return par.join(" ")
}
let ar : string[] = fillArrayCssValue(props.margin as string[])
if (ar.length == 0) return "0px 0px 0px 0px";
return ar.join(" ")
})
const _disabled = computed(() : boolean => {
return props.disabled;
})
const _color = computed(() : string => {
if (xConfig.dark == 'dark') {
if (props.darkColor != '') return getDefaultColor(props.darkColor)
return getDefaultColor(xConfig.sheetDarkColor)
}
return getDefaultColor(props.color)
})
const _titleColor = computed(() : string => {
if (xConfig.dark == 'dark') {
if (props.darkTitleColor != '') return getDefaultColor(props.darkTitleColor)
return '#ffffff'
}
return getDefaultColor(props.titleColor)
})
const _leftSize = computed(() : string => {
return checkIsCssUnit(props.leftSize, xConfig.unit);
})
const _rightWidth = computed(() : string => {
return checkIsCssUnit(props.rightWidth, xConfig.unit);
})
const _avatarRound = computed(() : string => {
return checkIsCssUnit(props.avatarRound, xConfig.unit);
})
const _minHeight = computed(() : string => {
return checkIsCssUnit(props.minHeight, xConfig.unit);
})
const _bottomBorderColor = computed(() : string => {
if (props.bottomBorderColor != "") return getDefaultColor(props.bottomBorderColor)
if (xConfig.dark == 'dark') return xConfig.borderDarkColor
return "#f5f5f5"
})
const _icon = computed(() : string => {
return props.icon
})
const _allAttr = computed(() : xCellItemType => {
let iconColor = props.iconColor;
if (iconColor == '') {
iconColor = xConfig.color;
}
let p = {
icon: props.icon,
title: props.title,
desc: props.desc,
label: props.label,
bottom: props.showBottomBorder,
link: props.link,
url: props.url,
iconColor: getDefaultColor(iconColor),
labelColor: getDefaultColor(props.labelColor),
card: props.card
} as xCellItemType
return p
})
const _cardRadius = computed(() : string => {
if (props.round == "") return checkIsCssUnit(xConfig.inputRadius, xConfig.unit)
return checkIsCssUnit(xConfig.cellRadius, xConfig.unit)
})
const _titleSize = computed(() : string => {
let fontSize = checkIsCssUnit(props.titleSize, xConfig.unit);
if (xConfig.fontScale == 1) return fontSize;
let sizeNumber = parseInt(fontSize)
if (isNaN(sizeNumber)) {
sizeNumber = 16
}
return (sizeNumber * xConfig.fontScale).toString() + getUnit(fontSize)
})
const _iconSize = computed(() : string => {
let fontSize = checkIsCssUnit(props.iconSize, xConfig.unit);
if (xConfig.fontScale == 1) return fontSize;
let sizeNumber = parseInt(fontSize)
if (isNaN(sizeNumber)) {
sizeNumber = 17
}
return (sizeNumber * xConfig.fontScale).toString() + getUnit(fontSize)
})
const _rightLableSize = computed(() : string => {
let fontSize = checkIsCssUnit(props.labelSize, xConfig.unit);
if (xConfig.fontScale == 1) return fontSize;
let sizeNumber = parseInt(fontSize)
if (isNaN(sizeNumber)) {
sizeNumber = 13
}
return (sizeNumber * xConfig.fontScale).toString() + getUnit(fontSize)
})
const _isLinksHover = computed(() : boolean => {
return props.link
})
// 方法
function clickLisent() : void {
/**
* 整个列表被点击
*/
emits("click");
if (props.url != "" && !_disabled.value) {
uni.navigateTo({
url: props.url,
fail() {
uni.switchTab({
url: props.url,
})
}
})
}
}
</script>
<template>
<view @click="clickLisent" <!-- #ifndef APP-HARMONY -->
:hover-start-time="_isLinksHover?50:0"
:hover-stay-time="_isLinksHover?100:0"
:hover-class="_isLinksHover?'cellHover':''"
<!-- #endif -->
class="xCell "
:style="{
backgroundColor:_color,
borderRadius:_allAttr.card==true?_cardRadius:'0px',
minHeight:_minHeight,
padding:_padding,
margin:_allAttr.card?_margin:'0px',
borderBottom:_allAttr.bottom&& !_allAttr.card&&!bottomBorderInsert?`1px solid ${_bottomBorderColor}`:'none'
}">
<view v-if="_icon" class="xCellAvatar" :style="{width:_leftSize,height:_leftSize,borderRadius:_avatarRound}">
<!--
@slot 头像图标
@prop {string} icon - 图标名称
-->
<slot name="avatar" :icon="_icon">
<x-icon :color="_allAttr.iconColor" :font-size="_iconSize" :name="_icon"></x-icon>
</slot>
</view>
<view class="xCellWrap" :style="{
borderBottom:_allAttr.bottom&& !_allAttr.card&&bottomBorderInsert?`1px solid ${_bottomBorderColor}`:'none'
}">
<view class="center">
<!--
@slot 默认标题插槽
-->
<slot>
<text class="title" :style="{color:_titleColor,fontSize:_titleSize}">{{ _allAttr.title}}</text>
</slot>
<!--
@slot 简介
@prop {string} desc - 简介
-->
<slot name="desc" :desc="_allAttr.desc">
<x-text v-if="_allAttr.desc!=''" font-size="12" color='#bfbfbf' dark-color='#bfbfbf'
class="desc">{{_allAttr.desc}}</x-text>
</slot>
</view>
<view class="xcellRight">
<!--
@slot 右边文字
@prop {string} label - 标签内容
-->
<slot name="label" :label="_allAttr.label">
<text v-if="_allAttr.label!=''"
:style="{marginLeft:'16px',color:_allAttr.labelColor,fontSize:_rightLableSize,width:_rightWidth }"
class="rightLabel">{{_allAttr.label}}</text>
</slot>
<!--
@slot 右插槽
-->
<slot name="right"></slot>
<view v-if="_allAttr.url!=''||_allAttr.link" style="margin-left: 5px;">
<x-icon :dark-color="linkDarkColor" :color="linkColor" font-size="20"
name="arrow-right-s-line"></x-icon>
</view>
</view>
</view>
</view>
</template>
<style scoped lang="scss">
.cellHover {
opacity: 0.9;
}
.xCell {
padding: 12px 0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.cardInset {
// padding: 0 12px;
}
.cellCard {
// padding: 0 12px;
// margin-bottom: 6px;
// margin-left: 12px;
// margin-right: 12px;
}
.xCellWrap {
flex: 1;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 100%;
padding: 12px 0px;
}
.title {
lines: 2;
text-overflow: ellipsis;
flex: 1;
flex-shrink: 0;
line-height: 1.7;
}
.desc {
font-size: 12px;
padding-top: 2px;
}
.xCellAvatar {
margin-right: 10px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-shrink: 0;
overflow: hidden;
}
.center {
flex: 1;
}
.xcellRight {
// padding-left: 16px;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.rightLabel {
lines: 1;
text-overflow: ellipsis;
font-size: 12px;
text-align: right;
// 以下是sdk4.51+不支持
// max-width: 100px;
// width:100px;
// #ifndef APP
white-space: nowrap;
/* 防止文本换行 */
overflow: hidden;
/* 超出部分隐藏 */
text-overflow: ellipsis;
/* 显示省略号 */
// #endif
}
</style>