index.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <hc-sys id="app-sys" class="hc-work-order-page" navBarUi='work-order-nav-bar'>
  3. <template #navBar>
  4. <hc-nav-back-bar ui='work-order-nav' title="消息动态圈">
  5. <text class="i-ri-add-circle-fill text-40 mr-1" @click="toPageClick('/pages/work-order/add')"/>
  6. <text class="i-ri-message-3-fill text-40 ml-2"/>
  7. </hc-nav-back-bar>
  8. </template>
  9. <!--下拉刷新区域-->
  10. <z-paging ref="pageRef" :style="pagingStyle" v-model="dataList" @query="getDataList">
  11. <uni-collapse>
  12. <template v-for="item in dataList" :key="item.id">
  13. <view class="relative bg-white mb-2 p-3">
  14. <view class="hc-flex">
  15. <view class="hc-flex-center mr-3">
  16. <hc-img class="round" :width="40" :height="40" :src="item.avatar" v-if="item.avatar"/>
  17. <hc-img class="round" :width="40" :height="40" src="/static/image/avatar.png" v-else/>
  18. </view>
  19. <view class="relative flex-1">
  20. <view class="text-black mb-1">{{item['createUserName'] ?? '用户名异常'}}</view>
  21. <view class="text-24 text-gray-4">{{item['createTime']}}</view>
  22. </view>
  23. <view class="text-24 text-gray-5" v-if="parseInt(item['isSolve']) === 1">已解决</view>
  24. </view>
  25. <view class="relative ml-12.5 text-gray-5">
  26. <view class="relative mt-3" v-html="item['opinionContent']"/>
  27. <view class="mt-3" v-if="item['returnFiles']?.length > 0">
  28. <hc-row :gutter="10">
  29. <hc-col :span="8" class="h-125" v-for="(img, index) in item['returnFiles']">
  30. <hc-image class="radius" un-border="1 solid gray-2" :src="img" @click="previewImage(item['returnFiles'], index)"/>
  31. </hc-col>
  32. </hc-row>
  33. </view>
  34. </view>
  35. <view class="hc-flex ml-12.5 mt-4 text-gray-5">
  36. <view class="hc-flex text-34" @click="commentExpanded(item)">
  37. <text class="i-ri-message-2-fill" v-if="item['commentExpanded']"/>
  38. <text class="i-ri-message-2-line" v-else/>
  39. <text class="text-26 text-gray-5 ml-1" v-if="item['commentsNumber'] >= 1">{{ item['commentsNumber'] }}</text>
  40. </view>
  41. <view class="flex-1"/>
  42. <view class="hc-flex text-34" @click="goodLikeClick(item)">
  43. <text class="i-ri-thumb-up-fill" v-if="item['currentUserGood']"/>
  44. <text class="i-ri-thumb-up-line" v-else/>
  45. <text class="text-26 text-gray-5 ml-1" v-if="item['goodNumber'] >= 1">{{ item['goodNumber'] }}</text>
  46. </view>
  47. </view>
  48. <!--评论区域-->
  49. <uni-collapse-item :class="item['commentExpanded']?'mt-3':''" title="评论区" :open="item['commentExpanded'] ?? false">
  50. <view class="relative pt-3" un-border-t="1 solid gray-2">
  51. <view class="relative flex items-end mb-5">
  52. <view class="relative flex-1">
  53. <textarea v-model="item.userCommentContent" class="hc-textarea p-2 text-26" placeholder="请输入评论内容" auto-height />
  54. </view>
  55. <button class="cu-btn text-24 ml-2" :class="item['userCommentContent']?'bg-blue-5 text-white':''" @click="submitComment(item)">提交</button>
  56. </view>
  57. <view class="relative flex items-start" v-for="(items, index) in item['expandedCommentList']" :key="items.id">
  58. <view class="mr-2">
  59. <hc-img class="round" :width="24" :height="24" :src="items.avatar" v-if="items.avatar"/>
  60. <hc-img class="round" :width="24" :height="24" src="/static/image/avatar.png" v-else/>
  61. </view>
  62. <view class="relative flex-1">
  63. <view class="flex items-start">
  64. <view class="flex-1 text-28 text-gray-7">{{items['userName'] ?? '用户名异常'}}</view>
  65. <text class="text-24 text-gray-4">{{items['createTime']}}</text>
  66. </view>
  67. <view class="text-24 text-gray-4 mt-1" v-html="items['replyContent']"/>
  68. <view class="relative my-3" un-border-b="1 solid gray-2" v-if="(item['expandedCommentList'].length - 1) !== index"/>
  69. </view>
  70. </view>
  71. </view>
  72. </uni-collapse-item>
  73. </view>
  74. </template>
  75. </uni-collapse>
  76. </z-paging>
  77. </hc-sys>
  78. </template>
  79. <script setup>
  80. import {getCurrentInstance, ref} from "vue";
  81. import mainApi from '~api/other/work-order';
  82. import {onShow, onReady} from '@dcloudio/uni-app'
  83. import {errorToast, querySelect, successToast} from "@/utils/tools";
  84. import {arrToKey, getArrValue, getObjValue} from "js-fast-way";
  85. import {useAppStore} from "@/store";
  86. //初始变量
  87. const store = useAppStore()
  88. const projectId = ref(store.projectId);
  89. const contractId = ref(store.contractId);
  90. const instance = getCurrentInstance().proxy
  91. const isNodes = ref(false)
  92. const pageRef = ref(null)
  93. onReady(() => {
  94. setPagingStyle()
  95. isNodes.value = true
  96. })
  97. onShow(() => {
  98. if (isNodes.value) {
  99. reloadData()
  100. }
  101. })
  102. //内容区域
  103. const pagingStyle = ref({
  104. position: 'relative',
  105. width: '100%',
  106. bottom: 0,
  107. })
  108. const setPagingStyle = async () => {
  109. const {height: appHeight} = await querySelect(instance, 'app-sys')
  110. const {height: navHeight} = await querySelect(instance, 'hc-nav-bar')
  111. // #ifdef H5
  112. pagingStyle.value.height = (appHeight - navHeight) + 'px'
  113. // #endif
  114. // #ifdef APP-PLUS
  115. const {screenHeight, safeArea} = uni.getWindowInfo()
  116. const content = navHeight + (screenHeight - safeArea.bottom)
  117. pagingStyle.value.height = (screenHeight - content) + 'px'
  118. // #endif
  119. }
  120. //重载数据
  121. const reloadData = () => {
  122. pageRef.value?.reload()
  123. }
  124. //获取数据
  125. const dataList = ref([])
  126. const getDataList = async (pageNo, pageSize) => {
  127. uni.showLoading({title: '获取数据中...', mask: true});
  128. const { data } = await mainApi.queryUserOpinionPage({
  129. projectId: projectId.value,
  130. contractId: contractId.value,
  131. current: pageNo,
  132. size: pageSize,
  133. })
  134. const res = getObjValue(data)
  135. isNodes.value = true
  136. pageRef.value?.complete(getArrValue(res?.records));
  137. uni.hideLoading();
  138. }
  139. //预览图片
  140. const previewImage = (imgs, index) => {
  141. uni.previewImage({
  142. urls: imgs,
  143. current: index
  144. });
  145. }
  146. //评论展开或收起
  147. const commentExpanded = (item) => {
  148. if (!item['commentExpanded']) {
  149. queryCommentsList(item)
  150. } else {
  151. item['commentExpanded'] = false
  152. }
  153. }
  154. //获取评论列表
  155. const queryCommentsList = async (item) => {
  156. uni.showLoading({title: '获取评论数据中...', mask: true});
  157. //关闭其他评论区
  158. for (let i = 0; i < dataList.value.length; i++) {
  159. const item = dataList.value[i]
  160. if (item['commentExpanded']) {
  161. item['commentExpanded'] = false
  162. }
  163. }
  164. //发起请求
  165. const {data} = await mainApi.queryCommentsList({
  166. userOpinionId: item.id
  167. })
  168. item['expandedCommentList'] = getArrValue(data)
  169. item['commentExpanded'] = true
  170. uni.hideLoading();
  171. }
  172. //提交评论
  173. const submitComment = async (item) => {
  174. if (!item['userCommentContent']) {
  175. errorToast('请先填写评论内容')
  176. return
  177. }
  178. //发起请求
  179. uni.showLoading({title: '提交数据中...', mask: true});
  180. const {error, code} = await mainApi.saveUserComments({
  181. userOpinionId: item.id,
  182. replyContent: item['userCommentContent'],
  183. projectId: projectId.value,
  184. contractId: contractId.value,
  185. })
  186. uni.hideLoading();
  187. if (!error && code === 200) {
  188. item['userCommentContent'] = ''
  189. queryCommentsList(item).then()
  190. } else {
  191. errorToast('评论失败')
  192. }
  193. }
  194. //点赞或取消点赞
  195. const goodLikeClick = async (item) => {
  196. if (item['currentUserGood']) {
  197. const {error, code} = await mainApi.cancelGood({
  198. userOpinionId: item.id
  199. })
  200. if (!error && code === 200) {
  201. item['currentUserGood'] = false
  202. item['goodNumber']--
  203. } else {
  204. errorToast('取消点赞失败')
  205. }
  206. } else {
  207. const {error, code} = await mainApi.addGoodNumber({
  208. userOpinionId: item.id,
  209. good: 1
  210. })
  211. if (!error && code === 200) {
  212. item['currentUserGood'] = true
  213. item['goodNumber']++
  214. } else {
  215. errorToast('点赞失败')
  216. }
  217. }
  218. }
  219. //跳转到其他页面
  220. const toPageClick = (url) => {
  221. uni.navigateTo({url: url});
  222. }
  223. </script>
  224. <style lang="scss">
  225. @import "@/style/work-order/index.scss";
  226. </style>