uni-list-item.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <template>
  2. <!-- #ifdef APP-NVUE -->
  3. <cell :keep-scroll-position="keepScrollPosition">
  4. <!-- #endif -->
  5. <view :class="{ 'uni-list-item--disabled': disabled }"
  6. :style="{ 'background-color': customStyle.backgroundColor }"
  7. :hover-class="(!clickable && !link) || disabled || showSwitch ? '' : 'uni-list-item--hover'"
  8. class="uni-list-item" @click="onClick">
  9. <view v-if="!isFirstChild" class="border--left" :class="{ 'uni-list--border': border }"></view>
  10. <view class="uni-list-item__container"
  11. :class="{ 'container--right': showArrow || link, 'flex--direction': direction === 'column' }"
  12. :style="{ paddingTop: padding.top, paddingLeft: padding.left, paddingRight: padding.right, paddingBottom: padding.bottom }">
  13. <slot name="header">
  14. <view class="uni-list-item__header">
  15. <view v-if="thumb" class="uni-list-item__icon">
  16. <image :src="thumb" class="uni-list-item__icon-img" :class="['uni-list--' + thumbSize]" />
  17. </view>
  18. <view v-else-if="showExtraIcon" class="uni-list-item__icon">
  19. <uni-icons :customPrefix="extraIcon.customPrefix" :color="extraIcon.color"
  20. :size="extraIcon.size" :type="extraIcon.type" />
  21. </view>
  22. </view>
  23. </slot>
  24. <slot name="body">
  25. <view class="uni-list-item__content"
  26. :class="{ 'uni-list-item__content--center': thumb || showExtraIcon || showBadge || showSwitch }">
  27. <text v-if="title" class="uni-list-item__content-title"
  28. :class="[ellipsis !== 0 && ellipsis <= 2 ? 'uni-ellipsis-' + ellipsis : '']">{{ title
  29. }}</text>
  30. <text v-if="note" class="uni-list-item__content-note">{{ note }}</text>
  31. </view>
  32. </slot>
  33. <slot name="footer">
  34. <view v-if="rightText || showBadge || showSwitch" class="uni-list-item__extra"
  35. :class="{ 'flex--justify': direction === 'column' }">
  36. <text v-if="rightText" class="uni-list-item__extra-text">{{ rightText }}</text>
  37. <uni-badge v-if="showBadge" :type="badgeType" :text="badgeText" :custom-style="badgeStyle" />
  38. <switch v-if="showSwitch" :disabled="disabled" :checked="switchChecked"
  39. @change="onSwitchChange" />
  40. </view>
  41. </slot>
  42. </view>
  43. <uni-icons v-if="showArrow || link" :size="16" class="uni-icon-wrapper" color="#bbb" type="right" />
  44. </view>
  45. <!-- #ifdef APP-NVUE -->
  46. </cell>
  47. <!-- #endif -->
  48. </template>
  49. <script>
  50. /**
  51. * ListItem 列表子组件
  52. * @description 列表子组件
  53. * @tutorial https://ext.dcloud.net.cn/plugin?id=24
  54. * @property {String} title 标题
  55. * @property {String} note 描述
  56. * @property {String} thumb 左侧缩略图,若thumb有值,则不会显示扩展图标
  57. * @property {String} thumbSize = [lg|base|sm] 略缩图大小
  58. * @value lg 大图
  59. * @value base 一般
  60. * @value sm 小图
  61. * @property {String} badgeText 数字角标内容
  62. * @property {String} badgeType 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21)
  63. * @property {Object} badgeStyle 数字角标样式
  64. * @property {String} rightText 右侧文字内容
  65. * @property {Boolean} disabled = [true|false] 是否禁用
  66. * @property {Boolean} clickable = [true|false] 是否开启点击反馈
  67. * @property {String} link = [navigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈
  68. * @value navigateTo 同 uni.navigateTo()
  69. * @value redirectTo 同 uni.redirectTo()
  70. * @value reLaunch 同 uni.reLaunch()
  71. * @value switchTab 同 uni.switchTab()
  72. * @property {String | PageURIString} to 跳转目标页面
  73. * @property {Boolean} showBadge = [true|false] 是否显示数字角标
  74. * @property {Boolean} showSwitch = [true|false] 是否显示Switch
  75. * @property {Boolean} switchChecked = [true|false] Switch是否被选中
  76. * @property {Boolean} showExtraIcon = [true|false] 左侧是否显示扩展图标
  77. * @property {Object} extraIcon 扩展图标参数,格式为 {color: '#4cd964',size: '22',type: 'spinner'}
  78. * @property {String} direction = [row|column] 排版方向
  79. * @value row 水平排列
  80. * @value column 垂直排列
  81. * @event {Function} click 点击 uniListItem 触发事件
  82. * @event {Function} switchChange 点击切换 Switch 时触发
  83. */
  84. export default {
  85. name: 'UniListItem',
  86. emits: ['click', 'switchChange'],
  87. props: {
  88. direction: {
  89. type: String,
  90. default: 'row'
  91. },
  92. title: {
  93. type: String,
  94. default: ''
  95. },
  96. note: {
  97. type: String,
  98. default: ''
  99. },
  100. ellipsis: {
  101. type: [Number, String],
  102. default: 0
  103. },
  104. disabled: {
  105. type: [Boolean, String],
  106. default: false
  107. },
  108. clickable: {
  109. type: Boolean,
  110. default: false
  111. },
  112. showArrow: {
  113. type: [Boolean, String],
  114. default: false
  115. },
  116. link: {
  117. type: [Boolean, String],
  118. default: false
  119. },
  120. to: {
  121. type: String,
  122. default: ''
  123. },
  124. showBadge: {
  125. type: [Boolean, String],
  126. default: false
  127. },
  128. showSwitch: {
  129. type: [Boolean, String],
  130. default: false
  131. },
  132. switchChecked: {
  133. type: [Boolean, String],
  134. default: false
  135. },
  136. badgeText: {
  137. type: String,
  138. default: ''
  139. },
  140. badgeType: {
  141. type: String,
  142. default: 'success'
  143. },
  144. badgeStyle: {
  145. type: Object,
  146. default() {
  147. return {}
  148. }
  149. },
  150. rightText: {
  151. type: String,
  152. default: ''
  153. },
  154. thumb: {
  155. type: String,
  156. default: ''
  157. },
  158. thumbSize: {
  159. type: String,
  160. default: 'base'
  161. },
  162. showExtraIcon: {
  163. type: [Boolean, String],
  164. default: false
  165. },
  166. extraIcon: {
  167. type: Object,
  168. default() {
  169. return {
  170. type: '',
  171. color: '#000000',
  172. size: 20,
  173. customPrefix: ''
  174. };
  175. }
  176. },
  177. border: {
  178. type: Boolean,
  179. default: true
  180. },
  181. customStyle: {
  182. type: Object,
  183. default() {
  184. return {
  185. padding: '',
  186. backgroundColor: '#FFFFFF'
  187. }
  188. }
  189. },
  190. keepScrollPosition: {
  191. type: Boolean,
  192. default: false
  193. }
  194. },
  195. watch: {
  196. 'customStyle.padding': {
  197. handler(padding) {
  198. if (typeof padding == 'number') {
  199. padding += ''
  200. }
  201. let paddingArr = padding.split(' ')
  202. if (paddingArr.length === 1) {
  203. const allPadding = paddingArr[0]
  204. this.padding = {
  205. "top": allPadding,
  206. "right": allPadding,
  207. "bottom": allPadding,
  208. "left": allPadding
  209. }
  210. } else if (paddingArr.length === 2) {
  211. const [verticalPadding, horizontalPadding] = paddingArr;
  212. this.padding = {
  213. "top": verticalPadding,
  214. "right": horizontalPadding,
  215. "bottom": verticalPadding,
  216. "left": horizontalPadding
  217. }
  218. } else if (paddingArr.length === 3) {
  219. const [topPadding, horizontalPadding, bottomPadding] = paddingArr;
  220. this.padding = {
  221. "top": topPadding,
  222. "right": horizontalPadding,
  223. "bottom": bottomPadding,
  224. "left": horizontalPadding
  225. }
  226. } else if (paddingArr.length === 4) {
  227. const [topPadding, rightPadding, bottomPadding, leftPadding] = paddingArr;
  228. this.padding = {
  229. "top": topPadding,
  230. "right": rightPadding,
  231. "bottom": bottomPadding,
  232. "left": leftPadding
  233. }
  234. }
  235. },
  236. immediate: true
  237. }
  238. },
  239. // inject: ['list'],
  240. data() {
  241. return {
  242. isFirstChild: false,
  243. padding: {
  244. top: "",
  245. right: "",
  246. bottom: "",
  247. left: ""
  248. }
  249. };
  250. },
  251. mounted() {
  252. this.list = this.getForm()
  253. // 判断是否存在 uni-list 组件
  254. if (this.list) {
  255. if (!this.list.firstChildAppend) {
  256. this.list.firstChildAppend = true;
  257. this.isFirstChild = true;
  258. }
  259. }
  260. },
  261. methods: {
  262. /**
  263. * 获取父元素实例
  264. */
  265. getForm(name = 'uniList') {
  266. let parent = this.$parent;
  267. let parentName = parent.$options.name;
  268. while (parentName !== name) {
  269. parent = parent.$parent;
  270. if (!parent) return false
  271. parentName = parent.$options.name;
  272. }
  273. return parent;
  274. },
  275. onClick() {
  276. if (this.to !== '') {
  277. this.openPage();
  278. return;
  279. }
  280. if (this.clickable || this.link) {
  281. this.$emit('click', {
  282. data: {}
  283. });
  284. }
  285. },
  286. onSwitchChange(e) {
  287. this.$emit('switchChange', e.detail);
  288. },
  289. openPage() {
  290. if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) {
  291. this.pageApi(this.link);
  292. } else {
  293. this.pageApi('navigateTo');
  294. }
  295. },
  296. pageApi(api) {
  297. let callback = {
  298. url: this.to,
  299. success: res => {
  300. this.$emit('click', {
  301. data: res
  302. });
  303. },
  304. fail: err => {
  305. this.$emit('click', {
  306. data: err
  307. });
  308. }
  309. }
  310. switch (api) {
  311. case 'navigateTo':
  312. uni.navigateTo(callback)
  313. break
  314. case 'redirectTo':
  315. uni.redirectTo(callback)
  316. break
  317. case 'reLaunch':
  318. uni.reLaunch(callback)
  319. break
  320. case 'switchTab':
  321. uni.switchTab(callback)
  322. break
  323. default:
  324. uni.navigateTo(callback)
  325. }
  326. }
  327. }
  328. };
  329. </script>
  330. <style lang="scss">
  331. $uni-font-size-sm: 12px;
  332. $uni-font-size-base: 14px;
  333. $uni-font-size-lg: 16px;
  334. $uni-spacing-col-lg: 12px;
  335. $uni-spacing-row-lg: 15px;
  336. $uni-img-size-sm: 20px;
  337. $uni-img-size-base: 26px;
  338. $uni-img-size-lg: 40px;
  339. $uni-border-color: #e5e5e5;
  340. $uni-bg-color-hover: #f1f1f1;
  341. $uni-text-color-grey: #999;
  342. $list-item-pd: $uni-spacing-col-lg $uni-spacing-row-lg;
  343. .uni-list-item {
  344. /* #ifndef APP-NVUE */
  345. display: flex;
  346. /* #endif */
  347. font-size: $uni-font-size-lg;
  348. position: relative;
  349. justify-content: space-between;
  350. align-items: center;
  351. background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;
  352. flex-direction: row;
  353. /* #ifdef H5 */
  354. cursor: pointer;
  355. /* #endif */
  356. }
  357. .uni-list-item--disabled {
  358. opacity: 0.3;
  359. }
  360. .uni-list-item--hover {
  361. background-color: $uni-bg-color-hover !important;
  362. }
  363. .uni-list-item__container {
  364. position: relative;
  365. /* #ifndef APP-NVUE */
  366. display: flex;
  367. /* #endif */
  368. flex-direction: row;
  369. padding: $list-item-pd;
  370. padding-left: $uni-spacing-row-lg;
  371. flex: 1;
  372. overflow: hidden;
  373. // align-items: center;
  374. }
  375. .container--right {
  376. padding-right: 0;
  377. }
  378. // .border--left {
  379. // margin-left: $uni-spacing-row-lg;
  380. // }
  381. .uni-list--border {
  382. position: absolute;
  383. top: 0;
  384. right: 0;
  385. left: 0;
  386. /* #ifdef APP-NVUE */
  387. border-top-color: $uni-border-color;
  388. border-top-style: solid;
  389. border-top-width: 0.5px;
  390. /* #endif */
  391. }
  392. /* #ifndef APP-NVUE */
  393. .uni-list--border:after {
  394. position: absolute;
  395. top: 0;
  396. right: 0;
  397. left: 0;
  398. height: 1px;
  399. content: '';
  400. -webkit-transform: scaleY(0.5);
  401. transform: scaleY(0.5);
  402. background-color: $uni-border-color;
  403. }
  404. /* #endif */
  405. .uni-list-item__content {
  406. /* #ifndef APP-NVUE */
  407. display: flex;
  408. /* #endif */
  409. padding-right: 8px;
  410. flex: 1;
  411. color: #3b4144;
  412. // overflow: hidden;
  413. flex-direction: column;
  414. justify-content: space-between;
  415. overflow: hidden;
  416. }
  417. .uni-list-item__content--center {
  418. justify-content: center;
  419. }
  420. .uni-list-item__content-title {
  421. font-size: $uni-font-size-base;
  422. color: #3b4144;
  423. overflow: hidden;
  424. }
  425. .uni-list-item__content-note {
  426. margin-top: 6rpx;
  427. color: $uni-text-color-grey;
  428. font-size: $uni-font-size-sm;
  429. overflow: hidden;
  430. }
  431. .uni-list-item__extra {
  432. // width: 25%;
  433. /* #ifndef APP-NVUE */
  434. display: flex;
  435. /* #endif */
  436. flex-direction: row;
  437. justify-content: flex-end;
  438. align-items: center;
  439. }
  440. .uni-list-item__header {
  441. /* #ifndef APP-NVUE */
  442. display: flex;
  443. /* #endif */
  444. flex-direction: row;
  445. align-items: center;
  446. }
  447. .uni-list-item__icon {
  448. margin-right: 18rpx;
  449. flex-direction: row;
  450. justify-content: center;
  451. align-items: center;
  452. }
  453. .uni-list-item__icon-img {
  454. /* #ifndef APP-NVUE */
  455. display: block;
  456. /* #endif */
  457. height: $uni-img-size-base;
  458. width: $uni-img-size-base;
  459. margin-right: 10px;
  460. }
  461. .uni-icon-wrapper {
  462. /* #ifndef APP-NVUE */
  463. display: flex;
  464. /* #endif */
  465. align-items: center;
  466. padding: 0 10px;
  467. }
  468. .flex--direction {
  469. flex-direction: column;
  470. /* #ifndef APP-NVUE */
  471. align-items: initial;
  472. /* #endif */
  473. }
  474. .flex--justify {
  475. /* #ifndef APP-NVUE */
  476. justify-content: initial;
  477. /* #endif */
  478. }
  479. .uni-list--lg {
  480. height: $uni-img-size-lg;
  481. width: $uni-img-size-lg;
  482. }
  483. .uni-list--base {
  484. height: $uni-img-size-base;
  485. width: $uni-img-size-base;
  486. }
  487. .uni-list--sm {
  488. height: $uni-img-size-sm;
  489. width: $uni-img-size-sm;
  490. }
  491. .uni-list-item__extra-text {
  492. color: $uni-text-color-grey;
  493. font-size: $uni-font-size-sm;
  494. }
  495. .uni-ellipsis-1 {
  496. /* #ifndef APP-NVUE */
  497. overflow: hidden;
  498. white-space: nowrap;
  499. text-overflow: ellipsis;
  500. /* #endif */
  501. /* #ifdef APP-NVUE */
  502. lines: 1;
  503. text-overflow: ellipsis;
  504. /* #endif */
  505. }
  506. .uni-ellipsis-2 {
  507. /* #ifndef APP-NVUE */
  508. overflow: hidden;
  509. text-overflow: ellipsis;
  510. display: -webkit-box;
  511. -webkit-line-clamp: 2;
  512. -webkit-box-orient: vertical;
  513. /* #endif */
  514. /* #ifdef APP-NVUE */
  515. lines: 2;
  516. text-overflow: ellipsis;
  517. /* #endif */
  518. }
  519. </style>