complaintListDetail.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <template>
  2. <view class="mainbox">
  3. <view class="imgs">
  4. <image v-for="(img,i) in images" :key="i" :src="img" mode="aspectFill" @click="previewImage(i,images)"></image>
  5. </view>
  6. <view class="con">
  7. <view class="title">
  8. <text class="tag" :style="{backgroundColor: info.isProcessCompleted==1? '#eee': info.isHandlePlatform==0&&info.isHandleStore==0 ? 'red':'#2583EB', color: info.isProcessCompleted==1? '#666':'#fff'}">
  9. {{info.isProcessCompleted==1? '已结束':info.isHandlePlatform==0&&info.isHandleStore==0 ? '待处理':'已处理'}}
  10. </text>{{info.title}}</view>
  11. <view>{{info.content}}</view>
  12. <view style="margin-top: 24rpx;color:#999999">时间:{{info.createTime || '--'}}</view>
  13. </view>
  14. <view class="con" v-show="msgList&&msgList.length>0">
  15. <view class="msg">
  16. <view v-for="(msg,i) in msgList" :key="i" :class="msg.sendType !=1?'storemsg':''" style="margin-bottom: 24rpx;">
  17. <view class="lable">{{msg.sendType ==2?'商家回复:':msg.sendType ==3?'平台回复:':msg.sendType ==1?'我:':''}}</view>
  18. <view class="imgs" v-if="msg.images">
  19. <image v-for="(img,i) in msg.images.split(',')" :key="i" :src="img" mode="aspectFill" @click="previewImage(i,msg.images.split(','))"></image>
  20. </view>
  21. <view class="val">{{msg.content||''}}</view>
  22. <view class="val x-f" style="margin-top: 10rpx;font-size: 28rpx;color:#999999;">
  23. <text style="margin-right: 16rpx;">{{msg.createTime || '--'}}</text>
  24. <!-- <u-icon name="chat" color="#999" size="48rpx" v-if="info.isProcessCompleted!=1&&(msg.sendType ==2||msg.sendType ==3)" @click="open"></u-icon> -->
  25. </view>
  26. </view>
  27. </view>
  28. <!-- <view class="mymsg">
  29. <view class="val">问题问题问题问题问题问题问题问题问题问题</view>
  30. <view class="val x-f" style="margin-top: 10rpx;font-size: 28rpx;color:#999999;"><text style="margin-right: 16rpx;">2022-12-22 12:33</text></view>
  31. </view> -->
  32. </view>
  33. <view class="chatbox" v-if="info.isProcessCompleted!=1">
  34. <u-icon name="chat" color="#999" size="48rpx"@click="open"></u-icon>
  35. </view>
  36. <u-popup :show="show" :closeable="true" @close="close">
  37. <view class="replybox">
  38. <view class="replybox-title">回复:</view>
  39. <view class="form-item">
  40. <u-upload v-if="fileList1.length>0" :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" width="60" height="60":maxCount="4"></u-upload>
  41. <u--textarea v-model="content" placeholder="输入回复内容" border="none" count
  42. maxlength='300'></u--textarea>
  43. </view>
  44. <view class="replybox-footer x-bc">
  45. <view class="uploadbox" :style="{visibility: fileList1.length>0 ? 'hidden':'visible'}">
  46. <u-upload :disabled="fileList1.length>0" :fileList="fileList1" uploadIcon="photo" uploadIconColor="#666" @afterRead="afterRead" @delete="deletePic" name="1" width="24" height="24":maxCount="4"></u-upload>
  47. </view>
  48. <view class="send" @click="send">发送</view>
  49. </view>
  50. </view>
  51. </u-popup>
  52. </view>
  53. </template>
  54. <script>
  55. import {storeComplaintDetail,storeComplaintMsg,storeComplaintMsgAdd} from "@/api/user.js"
  56. export default {
  57. data() {
  58. return {
  59. id:'',
  60. images:[],
  61. info: {},
  62. msgList: [],
  63. fileList1: [],
  64. show: false,
  65. content:'',
  66. replyImages: ""
  67. }
  68. },
  69. onLoad(option) {
  70. this.id = option.id
  71. this.getDetail()
  72. this.getMsg()
  73. },
  74. methods: {
  75. open() {
  76. this.content = ''
  77. this.fileList1 = []
  78. this.replyImages = ''
  79. this.show = true
  80. },
  81. close() {
  82. this.show = false
  83. },
  84. previewImage(index,images) {
  85. uni.previewImage({
  86. current: index,
  87. urls: images
  88. });
  89. },
  90. getDetail() {
  91. storeComplaintDetail(this.id).then(res=>{
  92. if(res.code == 200) {
  93. this.info = res.data || {}
  94. this.images = res.data&&res.data.images.split(',')
  95. } else {
  96. uni.showToast({
  97. title:res.msg,
  98. icon: "none"
  99. })
  100. }
  101. })
  102. },
  103. getMsg() {
  104. storeComplaintMsg({
  105. complaintId: this.id,
  106. pageNum: 1,
  107. pageSize: 100,
  108. }).then(res=>{
  109. if(res.code == 200) {
  110. this.msgList = res.rows
  111. } else {
  112. uni.showToast({
  113. title:res.msg,
  114. icon: "none"
  115. })
  116. }
  117. })
  118. },
  119. send() {
  120. const replyImages = this.getSuccessUrls(this.fileList1);
  121. if (replyImages === 'failed') return;
  122. this.replyImages = replyImages;
  123. if(!this.content) {
  124. uni.showToast({
  125. title: '请输入回复内容',
  126. icon: "none"
  127. })
  128. return
  129. }
  130. const param = {
  131. complaintId: this.id,
  132. sendType: 1,
  133. content: this.content,
  134. images: this.replyImages
  135. }
  136. storeComplaintMsgAdd(param).then(res=>{
  137. if(res.code == 200) {
  138. this.getMsg()
  139. this.show = false
  140. } else {
  141. uni.showToast({
  142. title: res.msg,
  143. icon: "none"
  144. })
  145. }
  146. })
  147. },
  148. deletePic(event) {
  149. this[`fileList${event.name}`].splice(event.index, 1)
  150. },
  151. async afterRead(event) {
  152. // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
  153. let lists = [].concat(event.file)
  154. let fileListLen = this[`fileList${event.name}`].length
  155. lists.map((item) => {
  156. this[`fileList${event.name}`].push({
  157. ...item,
  158. status: 'uploading',
  159. message: '上传中'
  160. })
  161. })
  162. for (let i = 0; i < lists.length; i++) {
  163. const result = await this.uploadFilePromise(lists[i].url)
  164. let item = this[`fileList${event.name}`][fileListLen]
  165. this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
  166. status: result.status,
  167. message: result.status == 'failed' ? '上传失败' : '',
  168. url: result.url
  169. }))
  170. fileListLen++
  171. }
  172. },
  173. uploadFilePromise(url) {
  174. return new Promise((resolve, reject) => {
  175. let a = uni.uploadFile({
  176. url: uni.getStorageSync('requestPath') +'/app/common/uploadOSS', // 仅为示例,非真实的接口地址
  177. filePath: url,
  178. name: 'file',
  179. success: (res) => {
  180. if(res.statusCode == 200&&JSON.parse(res.data).code == 200) {
  181. resolve({
  182. url:JSON.parse(res.data).url,
  183. status: 'success'
  184. })
  185. } else {
  186. resolve({
  187. url:url,
  188. status: 'failed'
  189. })
  190. }
  191. },
  192. fail: (res) => {
  193. resolve({
  194. url:url,
  195. status: 'failed'
  196. })
  197. }
  198. });
  199. })
  200. },
  201. getSuccessUrls(list) {
  202. if (!list || list.length === 0) return '';
  203. const hasUploading = list.some(v => v.status === 'uploading');
  204. const hasFailed = list.some(v => v.status === 'failed');
  205. if (hasUploading) {
  206. uni.showToast({ title: '请等待图片上传完成', icon: 'none' });
  207. return 'failed';
  208. }
  209. if (hasFailed) {
  210. uni.showToast({ title: '请检查是否有图片上传失败', icon: 'none' });
  211. return 'failed';
  212. }
  213. return list.filter(v => v.status === 'success').map(v => v.url).join(',');
  214. },
  215. }
  216. }
  217. </script>
  218. <style scoped lang="scss">
  219. .chatbox {
  220. height: 80rpx;
  221. width: 80rpx;
  222. border-radius: 50%;
  223. display: flex;
  224. align-items: center;
  225. justify-content: center;
  226. position: fixed;
  227. bottom: 200rpx;
  228. right: 50rpx;
  229. z-index: 999;
  230. box-shadow: 0 0 20px 5px #eee;
  231. background-color: #fff;
  232. }
  233. .lable {
  234. margin-bottom: 16rpx;
  235. font-weight: bold;
  236. }
  237. .tag {
  238. padding: 5rpx 6rpx;
  239. font-size: 20rpx;
  240. color: #FFFFFF;
  241. background-color: red;
  242. border-radius: 10rpx;
  243. margin-right: 10rpx;
  244. }
  245. .send{
  246. flex-shrink: 0;
  247. padding: 10rpx 24rpx;
  248. font-size: 28rpx;
  249. border-radius: 28rpx 28rpx 28rpx 28rpx;
  250. background-color: #2583EB;
  251. font-weight: 500;
  252. font-size: 24rpx;
  253. color: #fff;
  254. }
  255. .replybox {
  256. padding: 0 24rpx 24rpx 24rpx;
  257. &-title {
  258. padding: 24rpx 0;
  259. }
  260. .form-item {
  261. background-color: #fff;
  262. border: 1rpx solid #ddd;
  263. border-radius: 10rpx;
  264. padding: 16rpx;
  265. }
  266. &-footer {
  267. margin-top: 24rpx;
  268. }
  269. .uploadbox {
  270. position: relative;
  271. }
  272. }
  273. .msg{
  274. margin-bottom: 20rpx;
  275. border-radius: 16rpx 16rpx 16rpx 16rpx;
  276. }
  277. .storemsg {
  278. background-color: #f5f5f5;
  279. padding: 16rpx;
  280. border-radius: 16rpx;
  281. }
  282. .val {
  283. color:#222;
  284. word-break: break-all;
  285. }
  286. .mainbox {
  287. padding: 24rpx;
  288. .imgs {
  289. display: flex;
  290. flex-wrap: wrap;
  291. image {
  292. height: 150rpx;
  293. width: 150rpx;
  294. margin: 0 20rpx 20rpx 0;
  295. }
  296. }
  297. .con{
  298. display: block;
  299. background: #FFFFFF;
  300. border-radius: 16rpx 16rpx 16rpx 16rpx;
  301. padding: 24rpx;
  302. overflow: hidden;
  303. font-size: 28rpx;
  304. margin-bottom: 24rpx;
  305. color: #666666;
  306. .title {
  307. font-size: 30rpx;
  308. font-weight: bold;
  309. color: #333;
  310. margin-bottom: 16rpx;
  311. }
  312. }
  313. }
  314. </style>