liveTopInfoBar.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <template>
  2. <view
  3. :class="rootClass"
  4. :style="barStyle">
  5. <view :class="userInfoSectionClass">
  6. <template v-if="!scene">
  7. <image
  8. v-if="showType === 2"
  9. :class="backIconClass"
  10. src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/userapp/images/return3.png"
  11. @click="onBack" />
  12. <image
  13. v-if="showType === 1"
  14. :class="backIconClassHorizontal"
  15. src="/static/images/live/back.png"
  16. @click="onBack" />
  17. </template>
  18. <view :class="avatarContainerClass">
  19. <u-avatar
  20. v-if="variant === 'outside'"
  21. :src="avatarUrl"
  22. :size="32" />
  23. <view
  24. v-else
  25. class="cover-avatar-round cover-avatar-round--host">
  26. <image
  27. class="cover-avatar-round__img"
  28. :src="avatarUrl" />
  29. </view>
  30. <view :class="userNameClass">{{ displayName }}</view>
  31. </view>
  32. </view>
  33. <view
  34. v-if="Array.isArray(viewers)"
  35. :class="viewersSectionClass">
  36. <view :class="viewersInnerClass">
  37. <view
  38. v-for="(item, index) in (viewers || [])"
  39. v-if="item"
  40. :key="viewerKeyPrefix + index"
  41. :class="viewerAvatarClass"
  42. :style="variant === 'overlay' ? viewerAvatarClipStyle : null">
  43. <image
  44. :class="viewerImgClass"
  45. :src="item" />
  46. </view>
  47. <view :class="viewerCountClass">{{ watchCount || 0 }}</view>
  48. </view>
  49. </view>
  50. </view>
  51. </template>
  52. <script>
  53. export default {
  54. name: 'LiveTopInfoBar',
  55. props: {
  56. variant: {
  57. type: String,
  58. default: 'overlay'
  59. },
  60. showType: {
  61. type: Number,
  62. default: 2
  63. },
  64. liveName: {
  65. type: String,
  66. default: ''
  67. },
  68. avatarUrl: {
  69. type: String,
  70. default: ''
  71. },
  72. viewers: {
  73. type: Array,
  74. default: () => []
  75. },
  76. watchCount: {
  77. type: [String, Number],
  78. default: 0
  79. },
  80. scene: {
  81. type: Boolean,
  82. default: false
  83. },
  84. paddingTop: {
  85. type: String,
  86. default: ''
  87. },
  88. viewerKeyPrefix: {
  89. type: String,
  90. default: 'ltib-'
  91. }
  92. },
  93. computed: {
  94. displayName() {
  95. const name = this.liveName || '未命名';
  96. return this.truncateString(name, 8);
  97. },
  98. themeClass() {
  99. return this.showType === 1 ?
  100. 'cover-top-info-bar--light' :
  101. 'cover-top-info-bar--dark';
  102. },
  103. rootClass() {
  104. if (this.variant === 'outside') {
  105. return ['top-info-bar-inner'];
  106. }
  107. return ['cover-top-info-bar', this.themeClass];
  108. },
  109. barStyle() {
  110. if (this.variant !== 'overlay') return {};
  111. const top = this.paddingTop || '0px';
  112. return {
  113. paddingTop: top,
  114. paddingLeft: '24rpx',
  115. paddingRight: '24rpx',
  116. boxSizing: 'border-box'
  117. };
  118. },
  119. userInfoSectionClass() {
  120. return this.variant === 'outside' ?
  121. 'user-info-section' :
  122. 'cover-user-info-section';
  123. },
  124. backIconClass() {
  125. return this.variant === 'outside' ?
  126. 'back-icon mr4' :
  127. 'cover-back-icon';
  128. },
  129. backIconClassHorizontal() {
  130. return this.variant === 'outside' ?
  131. 'w48 h48 mr4' :
  132. 'cover-back-icon-sm';
  133. },
  134. avatarContainerClass() {
  135. return this.variant === 'outside' ?
  136. 'user-avatar-container' :
  137. 'cover-user-avatar-container';
  138. },
  139. userNameClass() {
  140. return this.variant === 'outside' ?
  141. 'user-name ml10 mr6' :
  142. 'cover-user-name';
  143. },
  144. viewersSectionClass() {
  145. return this.variant === 'outside' ?
  146. 'viewers-section' :
  147. 'cover-viewers-section';
  148. },
  149. viewersInnerClass() {
  150. return this.variant === 'outside' ?
  151. 'viewers-inner' :
  152. 'cover-viewers-inner';
  153. },
  154. viewerAvatarClass() {
  155. if (this.variant === 'outside') {
  156. return 'viewer-avatar mr4';
  157. }
  158. return 'cover-avatar-round cover-avatar-round--sm';
  159. },
  160. viewerImgClass() {
  161. return this.variant === 'outside' ?
  162. '' :
  163. 'cover-avatar-round__img';
  164. },
  165. viewerCountClass() {
  166. return this.variant === 'outside' ?
  167. 'viewer-count' :
  168. 'cover-viewer-count';
  169. },
  170. viewerAvatarClipStyle() {
  171. return {
  172. width: '52rpx',
  173. height: '52rpx',
  174. borderRadius: '26rpx',
  175. overflow: 'hidden',
  176. marginRight: '8rpx'
  177. };
  178. }
  179. },
  180. methods: {
  181. onBack() {
  182. this.$emit('back');
  183. },
  184. truncateString(str, maxLength) {
  185. if (typeof str !== 'string' || str.length <= maxLength) {
  186. return str;
  187. }
  188. return str.slice(0, maxLength) + '...';
  189. }
  190. }
  191. };
  192. </script>
  193. <style scoped lang="scss">
  194. /* 叠加在视频上 */
  195. .cover-top-info-bar {
  196. position: absolute;
  197. top: 0;
  198. left: 0;
  199. width: 100%;
  200. display: flex;
  201. flex-direction: row;
  202. justify-content: space-between;
  203. align-items: center;
  204. z-index: 99999;
  205. box-sizing: border-box;
  206. }
  207. .cover-top-info-bar--dark .cover-user-avatar-container,
  208. .cover-top-info-bar--dark .cover-viewer-count {
  209. background-color: rgba(0, 0, 0, 0.3);
  210. }
  211. .cover-top-info-bar--dark .cover-user-name {
  212. color: #ffffff;
  213. }
  214. .cover-top-info-bar--light .cover-user-avatar-container,
  215. .cover-top-info-bar--light .cover-viewer-count {
  216. background-color: #ffffff;
  217. }
  218. .cover-top-info-bar--light .cover-user-name {
  219. color: #333333;
  220. }
  221. .cover-user-info-section {
  222. display: flex;
  223. flex-direction: row;
  224. align-items: center;
  225. }
  226. .cover-back-icon {
  227. width: 60rpx;
  228. height: 64rpx;
  229. margin-right: 8rpx;
  230. }
  231. .cover-back-icon-sm {
  232. width: 42rpx;
  233. height: 42rpx;
  234. margin-right: 8rpx;
  235. }
  236. .cover-user-avatar-container {
  237. display: flex;
  238. flex-direction: row;
  239. align-items: center;
  240. padding: 6rpx 16rpx 6rpx 6rpx;
  241. border-radius: 32rpx;
  242. }
  243. .cover-avatar-round {
  244. overflow: hidden;
  245. margin-right: 10rpx;
  246. }
  247. .cover-avatar-round--host {
  248. width: 32px;
  249. height: 32px;
  250. border-radius: 50px;
  251. }
  252. .cover-avatar-round--sm {
  253. width: 52rpx;
  254. height: 52rpx;
  255. margin-right: 8rpx;
  256. }
  257. .cover-avatar-round__img {
  258. width: 100%;
  259. height: 100%;
  260. display: block;
  261. }
  262. .cover-user-name {
  263. font-size: 28rpx;
  264. line-height: 64rpx;
  265. max-width: 240rpx;
  266. overflow: hidden;
  267. white-space: nowrap;
  268. }
  269. .cover-viewers-section {
  270. display: flex;
  271. flex-direction: row;
  272. align-items: center;
  273. }
  274. .cover-viewers-inner {
  275. display: flex;
  276. flex-direction: row;
  277. align-items: center;
  278. }
  279. .cover-viewer-count {
  280. min-width: 80rpx;
  281. height: 52rpx;
  282. border-radius: 26rpx;
  283. font-size: 30rpx;
  284. text-align: center;
  285. line-height: 52rpx;
  286. padding: 0 8rpx;
  287. }
  288. /* 视频区外(横屏或降级) */
  289. .top-info-bar-inner {
  290. width: 100%;
  291. display: flex;
  292. justify-content: space-between;
  293. flex-direction: row;
  294. align-items: center;
  295. }
  296. .user-info-section {
  297. display: flex;
  298. align-items: center;
  299. .back-icon {
  300. width: 60rpx;
  301. height: 64rpx;
  302. }
  303. .user-avatar-container {
  304. padding: 6rpx 8rpx;
  305. height: 64rpx;
  306. background: var(--normal-bg);
  307. border-radius: 32rpx;
  308. display: flex;
  309. align-items: center;
  310. .user-name {
  311. color: var(--text-color);
  312. }
  313. }
  314. }
  315. .viewers-section {
  316. display: flex;
  317. justify-content: flex-end;
  318. align-items: center;
  319. }
  320. .viewers-inner {
  321. display: flex;
  322. align-items: center;
  323. }
  324. .viewer-avatar {
  325. width: 52rpx;
  326. height: 52rpx;
  327. border-radius: 26rpx;
  328. overflow: hidden;
  329. image{
  330. width: 100%;
  331. height: 100%;
  332. }
  333. }
  334. .viewer-count {
  335. width: 80rpx;
  336. height: 52rpx;
  337. background: var(--normal-bg);
  338. border-radius: 26rpx;
  339. font-size: 30rpx;
  340. color: var(--text-color);
  341. text-align: center;
  342. line-height: 52rpx;
  343. }
  344. </style>