uni-list-item.vue 13 KB

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