Cell.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <template>
  2. <Touchable
  3. direction="row"
  4. :touchable="touchable || Boolean($attrs['onClick'])"
  5. :pressedColor="pressedColor"
  6. :innerStyle="{
  7. ...viewStyle,
  8. ...style,
  9. }"
  10. :flex="1"
  11. :align="center ? 'center' : 'flex-start'"
  12. justify="space-between"
  13. @click="handleClick"
  14. >
  15. <slot>
  16. <FlexRow key="left" :flexShrink="1" center>
  17. <slot name="left">
  18. <FlexRow
  19. key="leftIcon"
  20. :width="(iconPlaceholder || icon) ? iconWidth : 0"
  21. center
  22. >
  23. <slot name="leftIcon">
  24. <Icon
  25. v-if="icon"
  26. key="leftIcon"
  27. :icon="icon"
  28. :size="iconSize"
  29. v-bind="iconProps"
  30. :innerStyle="{
  31. ...titleStyle,
  32. ...iconStyle,
  33. }"
  34. />
  35. </slot>
  36. </FlexRow>
  37. <FlexCol :innerStyle="leftViewStyle">
  38. <slot name="title">
  39. <Text v-if="title" :innerStyle="{ ...titleStyle, ...textStyle} ">{{ title }}</Text>
  40. </slot>
  41. <slot name="label">
  42. <Text v-if="label" :innerStyle="{ ...labelStyle, ...textStyle} ">{{ label }}</Text>
  43. </slot>
  44. </FlexCol>
  45. </slot>
  46. </FlexRow>
  47. <FlexRow key="right" :flexShrink="0" center>
  48. <slot name="right">
  49. <slot name="rightPrepend" />
  50. <slot name="value">
  51. <Text
  52. key="value"
  53. :selectable="valueSelectable"
  54. :innerStyle="{ ...valueStyle, ...textStyle }"
  55. >
  56. {{value ? ('' + value) : ''}}
  57. </Text>
  58. </slot>
  59. <Icon
  60. v-if="showArrow"
  61. key="rightArrow"
  62. icon="arrow-right"
  63. :size="textStyle.fontSize"
  64. :color="titleStyle.color"
  65. />
  66. <slot name="rightIcon">
  67. <Icon
  68. v-if="rightIcon"
  69. key="rightIcon"
  70. v-bind="rightIconProps"
  71. :size="iconSize"
  72. :icon="rightIcon"
  73. :innerStyle="{ ...titleStyle, ...rightIconStyle }"
  74. />
  75. </slot>
  76. </slot>
  77. </FlexRow>
  78. </slot>
  79. </Touchable>
  80. </template>
  81. <script setup lang="ts">
  82. import { computed, provide } from 'vue';
  83. import { propGetThemeVar, useTheme, type ThemePaddingMargin } from '../theme/ThemeDefine';
  84. import { CellContextKey, type CellContext } from './CellContext';
  85. import { configPadding } from '../theme/ThemeTools';
  86. import type { IconProps } from './Icon.vue';
  87. import FlexRow from '../layout/FlexRow.vue';
  88. import FlexCol from '../layout/FlexCol.vue';
  89. import Text from './Text.vue';
  90. import Icon from './Icon.vue';
  91. import Touchable from '../feedback/Touchable.vue';
  92. export interface CellProp {
  93. /**
  94. * 左侧标题
  95. */
  96. title?: string,
  97. /**
  98. * 右侧内容
  99. */
  100. value?: string|number,
  101. /**
  102. * 设置右侧内容是否可以选择
  103. * @default false
  104. */
  105. valueSelectable?: boolean,
  106. /**
  107. * 标题下方的描述信息
  108. */
  109. label?: string,
  110. /**
  111. * 左侧图标名称或图片链接(http/https),等同于 Icon 组件的 icon
  112. */
  113. icon?: string,
  114. /**
  115. * 当使用图标时,左图标的附加属性
  116. */
  117. iconProps?: IconProps;
  118. /**
  119. * 当左侧图标未设置时,是否在左侧追加一个占位区域,以和其他单元格对齐
  120. * @default false
  121. */
  122. iconPlaceholder?: boolean,
  123. /**
  124. * 左侧图标区域的宽度
  125. * @default 50
  126. */
  127. iconWidth?: number|string|'auto',
  128. /**
  129. * 左侧图标的大小
  130. * @default 40
  131. */
  132. iconSize?: number|string,
  133. /**
  134. * 右侧图标的大小
  135. * @default 40
  136. */
  137. rightIconSize?: number|string,
  138. /**
  139. * 右侧图标名称或图片链接(http/https),等同于 Icon 组件的 icon
  140. */
  141. rightIcon?: string,
  142. /**
  143. * 当使用图标时,右图标的附加属性
  144. */
  145. rightIconProps?: IconProps;
  146. /**
  147. * 是否可以点击
  148. * @default false
  149. */
  150. touchable?: boolean,
  151. /**
  152. * 是否展示右侧箭头
  153. * @default false
  154. */
  155. showArrow?: boolean,
  156. /**
  157. * 是否使内容垂直居中
  158. * @default false
  159. */
  160. center?: boolean,
  161. /**
  162. * 大小
  163. * @default medium
  164. */
  165. size?:'small'|'medium'|'large',
  166. /**
  167. * 背景颜色
  168. * @default Color.white
  169. */
  170. backgroundColor?: string;
  171. /**
  172. * 是否显示顶部边框
  173. * @default false
  174. */
  175. topBorder?: boolean;
  176. /**
  177. * 是否显示底部边框
  178. * @default true
  179. */
  180. bottomBorder?: boolean;
  181. /**
  182. * 按下的背景颜色
  183. * @default PressedColor(Color.white)
  184. */
  185. pressedColor?: string,
  186. /**
  187. * 圆角
  188. */
  189. radius?: string|number,
  190. /**
  191. * 自定义样式
  192. */
  193. innerStyle?: object,
  194. /**
  195. * 自定义左侧图标样式
  196. */
  197. iconStyle?: object,
  198. /**
  199. * 自定义右侧图标样式
  200. */
  201. rightIconStyle?: object,
  202. /**
  203. * 强制控制按钮的边距。如果是数字,则设置所有方向边距;两位数组 [vetical,horizontal];四位数组 [top,right,down,left]
  204. */
  205. padding?: number|number[]|ThemePaddingMargin,
  206. }
  207. const emit = defineEmits([ 'click' ]);
  208. const theme = useTheme();
  209. const props = withDefaults(defineProps<CellProp>(), {
  210. backgroundColor: () => propGetThemeVar('CellBackground', 'white'),
  211. size: () => propGetThemeVar('CellSize', 'medium'),
  212. padding: () => propGetThemeVar<any>('CellPadding', undefined),
  213. pressedColor: () => propGetThemeVar('CellPressedColor', 'pressed.white'),
  214. bottomBorder: () => propGetThemeVar('CellBottomBorder', true),
  215. topBorder: () => propGetThemeVar('CellTopBorder', false),
  216. center: () => propGetThemeVar('CellCenter', true),
  217. radius: () => propGetThemeVar('CellRadius', 0),
  218. iconWidth: () => propGetThemeVar('CellIconWidth', 50),
  219. iconSize: () => propGetThemeVar('CellIconSize', 40),
  220. rightIconSize: () => propGetThemeVar('CellIconSize', 40),
  221. valueSelectable: false,
  222. })
  223. provide<CellContext>(CellContextKey, {
  224. setOnClickListener: (listener) => {
  225. customClickListener = listener;
  226. },
  227. })
  228. const themeVars = computed(() => theme.resolveThemeSizes({
  229. CellBorderWidth: 1,
  230. CellFontSizeLarge: 36,
  231. CellFontSizeMedium: 32,
  232. CellFontSizeSmall: 28,
  233. CellIconSize: 30,
  234. CellIconWidth: 40,
  235. CellHeightLarge: 100,
  236. CellHeightMedium: 75,
  237. CellHeightSmall: 50,
  238. CellPaddingLarge: 15,
  239. CellPaddingMedium: 10,
  240. CellPaddingSmall: 7,
  241. }));
  242. const themeColorVars = computed(() => theme.resolveThemeColors({
  243. CellBorderColor: 'border.cell',
  244. }));
  245. const leftViewStyle = computed(() => ({
  246. marginLeft: theme.resolveThemeSize('CellLeftPaddingHorizontal', 15),
  247. marginRight: theme.resolveThemeSize('CellLeftPaddingHorizontal', 15),
  248. }));
  249. const titleStyle = computed(() => ({
  250. overflow: 'hidden',
  251. textOverflow: 'ellipsis',
  252. color: theme.resolveThemeColor('CellTitleColor', 'text.content'),
  253. }));
  254. const labelStyle = computed(() => ({
  255. overflow: 'hidden',
  256. textOverflow: 'ellipsis',
  257. color: theme.resolveThemeColor('CellLabelColor', 'text.second'),
  258. }));
  259. const valueStyle = computed(() => ({
  260. color: theme.resolveThemeColor('CellLabelColor', 'text.second'),
  261. marginLeft: theme.resolveThemeSize('CellValuePaddingHorizontal', 20),
  262. marginRight: theme.resolveThemeSize('CellValuePaddingHorizontal', 20),
  263. }));
  264. const viewStyle = computed(() => ({
  265. position: 'relative',
  266. flexDirection: 'row',
  267. justifyContent: 'space-between',
  268. alignItems: 'center',
  269. overflow: 'hidden',
  270. paddingLeft: theme.resolveThemeSize('CellPaddingHorizontal', 25),
  271. paddingRight: theme.resolveThemeSize('CellPaddingHorizontal', 25),
  272. }));
  273. const style = computed(() => {
  274. const styleObj : Record<string, any> = {
  275. backgroundColor: theme.resolveThemeColor(props.backgroundColor),
  276. borderRadius: theme.resolveThemeSize(props.radius),
  277. ...props.innerStyle,
  278. };
  279. switch (props.size) {
  280. case 'large':
  281. styleObj.minHeight = themeVars.value.CellHeightLarge as number;
  282. styleObj.paddingTop = themeVars.value.CellPaddingLarge as number;
  283. styleObj.paddingBottom = themeVars.value.CellPaddingLarge as number;
  284. break;
  285. default:
  286. case 'medium':
  287. styleObj.minHeight = themeVars.value.CellHeightMedium as number;
  288. styleObj.paddingTop = themeVars.value.CellPaddingMedium as number;
  289. styleObj.paddingBottom = themeVars.value.CellPaddingMedium as number;
  290. break;
  291. case 'small':
  292. styleObj.minHeight = themeVars.value.CellHeightSmall as number;
  293. styleObj.paddingTop = themeVars.value.CellPaddingSmall as number;
  294. styleObj.paddingBottom = themeVars.value.CellPaddingSmall as number;
  295. }
  296. //内边距样式的强制设置
  297. configPadding(styleObj, theme.theme, props.padding);
  298. //边框设置
  299. if (props.topBorder)
  300. styleObj.borderTop = `${themeVars.value.CellBorderWidth} solid ${themeColorVars.value.CellBorderColor}`;
  301. if (props.bottomBorder)
  302. styleObj.borderBottom = `${themeVars.value.CellBorderWidth} solid ${themeColorVars.value.CellBorderColor}`;
  303. return styleObj
  304. });
  305. //文字样式
  306. const textStyle = computed(() => {
  307. switch (props.size) {
  308. case 'large':
  309. return { fontSize: themeVars.value.CellFontSizeLarge };
  310. default:
  311. return { fontSize: themeVars.value.CellFontSizeMedium };
  312. case 'small':
  313. return { fontSize: themeVars.value.CellFontSizeSmall };
  314. }
  315. });
  316. let customClickListener: (() => void)|undefined;
  317. function handleClick() {
  318. customClickListener?.();
  319. emit('click');
  320. }
  321. </script>