feedback.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <template>
  2. <view>
  3. <view class="header-nav" :style="{height: `calc(88rpx + ${statusBarHeight}px)`,paddingTop: statusBarHeight + 'px'}">
  4. <view class="arrow-left" :style="{top: statusBarHeight + 'px'}" @click="goBack"><u-icon name="arrow-left" size="24"></u-icon></view>
  5. <view class="header-title" :style="{height:menuButtonH+'px',lineHeight:menuButtonH+'px'}">投诉反馈</view>
  6. </view>
  7. <view class="container" :style="{paddingTop: `calc(88rpx + ${statusBarHeight}px)`}">
  8. <view class="formbox" v-if="isLastChild==1">
  9. <view class="formbox-title">{{ text }}</view>
  10. <view class="form">
  11. <u-form labelPosition="top" labelWidth='auto' :model="formdata" :rules="rules" ref="uForm" errorType="toast">
  12. <u-form-item label="" prop="complaintContent">
  13. <u--textarea v-model="formdata.complaintContent" border="none" :clearable="true" placeholder="请填写反馈内容" count maxlength='200'></u--textarea>
  14. </u-form-item>
  15. <view class="box">
  16. <u-form-item label="图片(最多9张)">
  17. <view class="imgitem">
  18. <u-upload
  19. :fileList="fileList1"
  20. @afterRead="afterRead"
  21. @delete="deletePic"
  22. name="1"
  23. :maxCount="9"
  24. ></u-upload>
  25. </view>
  26. </u-form-item>
  27. </view>
  28. </u-form>
  29. </view>
  30. <view class="footer-btn">
  31. <button class="submit-btn" @click="submit">提交</button>
  32. <!-- <button class="submit-btn back-btn" @click="goBack">返回</button> -->
  33. </view>
  34. </view>
  35. <view class="container" v-else>
  36. <view class="list-item title">请选择反馈类型</view>
  37. <view class="list-item" v-for="(item, index) in feedbackItems" :key="index"
  38. @click="handleClick(item,index)">
  39. <view>{{ item.complaintTypeName }}</view>
  40. <uni-icons type="right" size="20" color="rgba(0,0,0,.3)" v-if="isLastChild==0"></uni-icons>
  41. </view>
  42. <view class="list-item" v-if="pageIndex!=0&&isLastChild==0" @click="goBack">
  43. 返回上一层
  44. </view>
  45. </view>
  46. </view>
  47. </view>
  48. </template>
  49. <script>
  50. import{ getTypeTree, complaintRecord,loginByMp } from "@/api/course.js"
  51. export default {
  52. data() {
  53. return {
  54. statusBarHeight: uni.getSystemInfoSync().statusBarHeight,
  55. menuButtonH: 45,
  56. pageIndex: 0,
  57. list: [],
  58. feedbackItems: [],
  59. userId: '',
  60. courseId: '',
  61. videoId: '',
  62. formdata: {
  63. complaintContent: ""
  64. },
  65. rules: {
  66. complaintContent:[{
  67. required: true,
  68. message: '投诉反馈内容不能为空',
  69. trigger: ["change", "blur"]
  70. }]
  71. },
  72. text: '',
  73. templateId: 0,
  74. user: {},
  75. isLastChild: 0,
  76. isLogin: false,
  77. fileList1: [],
  78. };
  79. },
  80. onLoad(option) {
  81. this.userId = option.userId || ''
  82. this.courseId = option.courseId || ''
  83. this.videoId = option.videoId || ''
  84. this.$isLoginCourse().then(
  85. res => {
  86. if(res){
  87. this.isLogin = true
  88. this.getMenuButton()
  89. this.getList()
  90. } else{
  91. this.isLogin = false
  92. this.goLogin()
  93. }
  94. },
  95. rej => {}
  96. );
  97. },
  98. methods: {
  99. getMenuButton(){
  100. const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
  101. this.menuButtonH = menuButtonInfo.height
  102. },
  103. goBack() {
  104. // 返回上一层逻辑
  105. if (this.pageIndex == 0) {
  106. uni.navigateBack();
  107. } else {
  108. this.pageIndex--;
  109. this.formdata = {
  110. complaintContent: ""
  111. }
  112. if (this.isLastChild == 1) {
  113. this.isLastChild = 0
  114. } else {
  115. if (this.pageIndex == 0) {
  116. this.feedbackItems = this.list
  117. this.templateId = 0
  118. } else {
  119. const list = this.findGrandparentOrAllData(this.list, this.templateId)
  120. this.feedbackItems = list.childrenType
  121. this.templateId = list.complaintTypeId
  122. }
  123. }
  124. }
  125. },
  126. findGrandparentOrAllData(data, targetId) {
  127. // 递归函数,用于查找目标节点的父级节点
  128. function findParent(node, targetId) {
  129. if (!node || !node.childrenType) return null;
  130. for (let child of node.childrenType) {
  131. if (child.complaintTypeId === targetId) {
  132. return node; // 找到目标节点的父级节点
  133. }
  134. const result = findParent(child, targetId); // 递归查找子节点
  135. if (result) return result;
  136. }
  137. return null;
  138. }
  139. // 遍历顶层节点,查找目标节点的父级和祖父级节点
  140. for (let root of data) {
  141. if (root.complaintTypeId === targetId) {
  142. return data; // 如果目标节点是顶层节点,返回所有数据
  143. }
  144. const parent = findParent(root, targetId); // 查找目标节点的父级节点
  145. if (parent) {
  146. const grandparent = findParent(root, parent.complaintTypeId); // 查找父级节点的父级节点
  147. return grandparent || data; // 如果找到祖父节点返回祖父节点,否则返回所有数据
  148. }
  149. }
  150. return data; // 如果没有找到目标节点,返回所有数据
  151. },
  152. handleClick(item,index) {
  153. if (this.isLastChild == 1) return
  154. if (this.pageIndex >= 0) {
  155. this.pageIndex++
  156. let children = this.feedbackItems[index].childrenType || [];
  157. this.templateId = this.feedbackItems[index].complaintTypeId
  158. this.formdata = {
  159. complaintContent: ""
  160. }
  161. this.text = this.feedbackItems[index].complaintTypeName
  162. if (children.length > 0) {
  163. this.isLastChild = 0
  164. this.feedbackItems = children
  165. this.templateId = this.feedbackItems[0].complaintTypeId
  166. } else {
  167. this.isLastChild = 1
  168. this.formdata = {
  169. complaintContent: ""
  170. }
  171. setTimeout(() => {
  172. this.$refs.uForm.setRules(this.rules)
  173. }, 200)
  174. }
  175. }
  176. },
  177. getList(){
  178. getTypeTree().then(res=>{
  179. if(res.code == 200) {
  180. this.list = res.data
  181. this.pageIndex = 0
  182. this.feedbackItems = this.list
  183. }
  184. })
  185. },
  186. submit() {
  187. if(this.fileList1.some(item=>item.status == 'uploading')) {
  188. uni.showToast({
  189. title: '等待图片上传中',
  190. icon: 'none'
  191. })
  192. return
  193. }
  194. var images=[];
  195. this.fileList1.forEach(function(element) {
  196. images.push(element.url)
  197. });
  198. this.$refs.uForm.validate().then(res => {
  199. if (res) {
  200. const param = {
  201. userId: this.userId,
  202. complaintTypeId: this.templateId,
  203. complaintContent: this.formdata.complaintContent,
  204. courseId: this.courseId,
  205. videoId: this.videoId,
  206. complaintUrl: images.toString()
  207. }
  208. complaintRecord(param).then(res=>{
  209. uni.showModal({
  210. title: '',
  211. content: '我们已收到您的反馈,谢谢',
  212. showCancel: false
  213. });
  214. })
  215. }
  216. })
  217. },
  218. deletePic(event) {
  219. this[`fileList${event.name}`].splice(event.index, 1)
  220. },
  221. async afterRead(event) {
  222. // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
  223. let lists = [].concat(event.file)
  224. let fileListLen = this[`fileList${event.name}`].length
  225. lists.map((item) => {
  226. this[`fileList${event.name}`].push({
  227. ...item,
  228. status: 'uploading',
  229. message: '上传中'
  230. })
  231. })
  232. for (let i = 0; i < lists.length; i++) {
  233. const result = await this.uploadFilePromise(lists[i].url)
  234. let item = this[`fileList${event.name}`][fileListLen]
  235. this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
  236. status: 'success',
  237. message: '',
  238. url: result
  239. }))
  240. fileListLen++
  241. }
  242. },
  243. uploadFilePromise(url) {
  244. return new Promise((resolve, reject) => {
  245. let a = uni.uploadFile({
  246. url: 'https://h5api.his.cdwjyyh.com/app/common/uploadOSS', // 仅为示例,非真实的接口地址
  247. filePath: url,
  248. name: 'file',
  249. success: (res) => {
  250. setTimeout(() => {
  251. console.log(JSON.parse(res.data).url)
  252. resolve(JSON.parse(res.data).url)
  253. }, 1000)
  254. }
  255. });
  256. })
  257. },
  258. goLogin() {
  259. this.$getProvider().then(provider=>{
  260. console.log('当前的环境商',provider)
  261. if (!provider) {
  262. reject()
  263. }
  264. uni.login({
  265. provider: provider,
  266. success: async loginRes => {
  267. console.log(loginRes)
  268. uni.getUserInfo({
  269. provider: provider,
  270. success: (infoRes)=> {
  271. uni.showToast({
  272. title: '处理中...',
  273. icon: 'loading'
  274. });
  275. loginByMp({code: loginRes.code,encryptedData:infoRes.encryptedData,iv:infoRes.iv}).then(res=>{
  276. uni.hideLoading();
  277. if (res.code == 200) {
  278. uni.setStorageSync('AppTokenmini_RTCourse', res.token);
  279. uni.setStorageSync('userInfo', JSON.stringify(res.user));
  280. this.userId = res.user.userId || ''
  281. this.isLogin = true
  282. this.getMenuButton()
  283. this.getList()
  284. } else {
  285. uni.showToast({
  286. title: res.msg,
  287. icon: 'none'
  288. });
  289. }
  290. }).catch(err=>{
  291. uni.hideLoading();
  292. uni.showToast({
  293. icon:'none',
  294. title: "登录失败,请重新登录",
  295. });
  296. });
  297. }
  298. });
  299. }
  300. })
  301. }).catch(err => {})
  302. },
  303. }
  304. };
  305. </script>
  306. <style scoped lang="scss">
  307. .container {
  308. background-color: #fff;
  309. }
  310. .formbox-title {
  311. padding-bottom: 30rpx;
  312. border-bottom: 1px solid #f4f4f4;
  313. }
  314. .formbox {
  315. border-top: 1px solid #f4f4f4;
  316. padding: 30rpx;
  317. }
  318. .box {
  319. padding-bottom: 24rpx;
  320. border-top: 1px solid #f4f4f4;
  321. .imgitem {
  322. padding-top: 20rpx;
  323. }
  324. }
  325. .footer-btn {
  326. margin-top: 50rpx;
  327. }
  328. .submit-btn {
  329. width: 50%;
  330. height: 88rpx;
  331. line-height: 88rpx;
  332. text-align: center;
  333. font-size: 30rpx;
  334. font-family: PingFang SC;
  335. color: #FFFFFF;
  336. background: rgb(0,178,106);
  337. border-radius: 16rpx;
  338. border: 1rpx solid ;
  339. margin-bottom: 30rpx;
  340. &::after {
  341. border: none;
  342. }
  343. }
  344. .back-btn {
  345. color: #bbb;
  346. background: transparent;
  347. border-radius: 16rpx;
  348. border: 1rpx solid #999;
  349. }
  350. .header-nav {
  351. height: 88rpx;
  352. display: flex;
  353. align-items: center;
  354. justify-content: center;
  355. overflow: hidden;
  356. background-color: #fff;
  357. box-sizing: border-box;
  358. width: 100%;
  359. position: fixed;
  360. top: 0;
  361. left: 0;
  362. .header-title {
  363. flex: 1;
  364. text-align: center;
  365. overflow: hidden;
  366. white-space: nowrap;
  367. text-overflow: ellipsis;
  368. font-family: PingFang SC,PingFang SC;
  369. font-weight: 500;
  370. font-size: 15px;
  371. color: #000;
  372. box-sizing: border-box;
  373. }
  374. }
  375. .arrow-left {
  376. position: absolute;
  377. left: 24rpx;
  378. height: 88rpx;
  379. display: flex;
  380. align-items: center;
  381. justify-content: center;
  382. overflow: hidden;
  383. }
  384. .list-item {
  385. background-color: #fff;
  386. padding: 24rpx;
  387. border-bottom: 1rpx solid #f4f4f4;
  388. font-size: 15px;
  389. color: #333;
  390. }
  391. .title {
  392. background-color: #f4f4f4;
  393. }
  394. </style>