fs-comment.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <template>
  2. <view>
  3. <fs-cell-group full border :radius="false">
  4. <fs-cell align="top" v-for="(item, index) in list" :key="item.id">
  5. <template #title>
  6. <fs-avatar :src="item[keyMap.avatar]"></fs-avatar>
  7. </template>
  8. <template #value>
  9. <view class="fs-comment-hd">
  10. <view class="fs-comment-hd-item fs-comment-hd-title">{{ item[keyMap.name] }}</view>
  11. <view class="fs-comment-hd-item">{{ item[keyMap.time] }}</view>
  12. </view>
  13. <view class="fs-comment-content">{{ item[keyMap.content] }}</view>
  14. <view class="fs-comment-reply">
  15. <view class="fs-comment-reply-item" @click="handleLike(item)" v-if="showLike">
  16. <fs-icon type="icon-like" :color="item[keyMap.isLike] ? 'red' : ''"></fs-icon>
  17. <view v-if="!item[keyMap.isLike]">点赞</view>
  18. </view>
  19. <view class="fs-comment-reply-item" @click="initInput(item)">
  20. <fs-icon type="icon-comment"></fs-icon>
  21. 回复
  22. </view>
  23. </view>
  24. <view class="fs-comment-reply-box">
  25. <fs-cell-group border bgColor="transparent" full>
  26. <fs-cell align="top" v-for="reply in item[keyMap.replyList]" :key="reply.id">
  27. <template #title>
  28. <fs-avatar :src="reply[keyMap.avatar]"></fs-avatar>
  29. </template>
  30. <template #value>
  31. <view class="fs-comment-hd fs-comment-hd-title">
  32. <view class="fs-comment-hd-item">{{ reply[keyMap.name] }}</view>
  33. <view class="fs-comment-hd-item">{{ reply[keyMap.time] }}</view>
  34. </view>
  35. <view class="fs-comment-content">{{ reply[keyMap.content] }}</view>
  36. <view class="fs-comment-reply">
  37. <view class="fs-comment-reply-item" @click="handleLike(reply)" v-if="showLike">
  38. <fs-icon type="icon-like" :color="reply[keyMap.isLike] ? 'red' : ''"></fs-icon>
  39. <view v-if="!reply[keyMap.isLike]">点赞</view>
  40. </view>
  41. <view class="fs-comment-reply-item" @click="initInput(reply)">
  42. <fs-icon type="icon-comment"></fs-icon>
  43. 回复
  44. </view>
  45. </view>
  46. </template>
  47. </fs-cell>
  48. </fs-cell-group>
  49. </view>
  50. </template>
  51. </fs-cell>
  52. </fs-cell-group>
  53. <view class="fs-comment-input-box" v-if="showInput">
  54. <input
  55. type="text"
  56. class="fs-comment-input"
  57. placeholder="回复..."
  58. v-model="replyValue"
  59. :focus="focus"
  60. @confirm="handleConfirm"
  61. />
  62. <view class="fs-comment-button" @click="handleConfirm">发送</view>
  63. <!-- :style="{backgroundColor: replyValue ? '#08bf65' : '#f7f7f7',color: replyValue ? '#fff' : '#cecece'}" -->
  64. </view>
  65. </view>
  66. </template>
  67. <script>
  68. /**
  69. * 评论组件
  70. * @description 评论组件
  71. * @property {Array} list 手风琴效果
  72. * @property {Boolean} showLike 激活项name
  73. * @property {Object} keyMap 箭头位置
  74. * @event {Function} like 点赞事件
  75. * @event {Function} reply 发送事件
  76. */
  77. export default {
  78. name: 'fs-comment'
  79. }
  80. </script>
  81. <script setup>
  82. import { ref, nextTick } from 'vue'
  83. const props = defineProps({
  84. list: {
  85. type: Array,
  86. default() {
  87. return []
  88. }
  89. },
  90. showLike: Boolean,
  91. keyMap: {
  92. type: Object,
  93. default() {
  94. return {
  95. avatar: 'avatar',
  96. name: 'name',
  97. time: 'time',
  98. content: 'content',
  99. replyList: 'children',
  100. isLike: 'isLike'
  101. }
  102. }
  103. }
  104. })
  105. const emits = defineEmits(['like', 'reply'])
  106. let focus = ref(false)
  107. let showInput = ref(false)
  108. let reply = ref(null)
  109. let replyValue = ref('')
  110. const initInput = item => {
  111. showInput.value = true
  112. focus.value = true
  113. reply.value = item
  114. replyValue.value = ''
  115. }
  116. const handleBlur = () => {
  117. nextTick(() => {
  118. showInput.value = false
  119. focus.value = false
  120. })
  121. }
  122. const handleConfirm = () => {
  123. if (replyValue.value) {
  124. emits('reply', reply.value)
  125. }
  126. showInput.value = false
  127. focus.value = false
  128. }
  129. const handleLike = item => {
  130. item[props.keyMap.isLike] = !item[props.keyMap.isLike]
  131. emits('like', item)
  132. }
  133. </script>
  134. <style lang="scss">
  135. .fs-comment {
  136. font-size: 14px;
  137. &-hd {
  138. display: flex;
  139. &-title {
  140. color: #222;
  141. }
  142. &-item {
  143. padding-right: 30rpx;
  144. position: relative;
  145. margin-bottom: 10rpx;
  146. & + & {
  147. padding-left: 30rpx;
  148. &::before {
  149. position: absolute;
  150. left: 0;
  151. top: 50%;
  152. transform: translateY(-50%);
  153. width: 2rpx;
  154. height: 24rpx;
  155. background-color: #d9d9d9;
  156. content: '';
  157. }
  158. }
  159. }
  160. }
  161. &-content {
  162. font-size: 14px;
  163. }
  164. &-reply-box {
  165. background-color: #f7f8fa;
  166. margin-top: 10rpx;
  167. border-radius: var(--radius);
  168. overflow: hidden;
  169. }
  170. &-reply {
  171. display: flex;
  172. font-size: 14px;
  173. margin-top: 10rpx;
  174. align-items: center;
  175. &-item {
  176. display: flex;
  177. align-items: center;
  178. margin-right: 30rpx;
  179. }
  180. }
  181. &-input-box {
  182. height: 110rpx;
  183. position: fixed;
  184. display: flex;
  185. left: 0;
  186. right: 0;
  187. bottom: 0;
  188. padding: 20rpx;
  189. border-top: 1rpx solid #eaeaea;
  190. background-color: #fff;
  191. align-items: center;
  192. z-index: 10;
  193. }
  194. &-input {
  195. height: 100%;
  196. margin-right: 20rpx;
  197. flex: 1;
  198. background-color: #fff;
  199. border-radius: 8rpx;
  200. padding-left: 20rpx;
  201. }
  202. &-button {
  203. height: 100%;
  204. padding: 10rpx 20rpx;
  205. border-radius: 8rpx;
  206. box-sizing: border-box;
  207. display: flex;
  208. align-items: center;
  209. background-color: #08bf65;
  210. color: #fff;
  211. }
  212. }
  213. </style>