You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

678 lines
16 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. <script setup>
  2. import { ref, computed, onMounted, watch, nextTick, onUnmounted } from "vue";
  3. import { useDataStore } from "@/store/dataList.js";
  4. import { addFeedbackAPI, getFeedbackAPI } from "../api/AIxiaocaishen";
  5. import feedback from "../assets/img/Feedback/feedback.png";
  6. import feedbackImg from "../assets/img/Feedback/feedbackImg.png";
  7. import feedbackSuccess from "../assets/img/Feedback/feedbackSuccess.png";
  8. import noFeedback from "../assets/img/Feedback/noFeedback.png";
  9. import border from "../assets/img/Feedback/border.png";
  10. import success from "../assets/img/Feedback/success.png";
  11. import failure from "../assets/img/Feedback/failure.png";
  12. import save from "../assets/img/Feedback/save.png";
  13. import back from "../assets/img/Feedback/back.png";
  14. import purpleDot from "../assets/img/Feedback/purpleDot.png";
  15. import moment from "moment";
  16. const dataStore = useDataStore();
  17. const feedbackContent = ref("");
  18. const uploadUrl = import.meta.env.VITE_APP_IMG_API_BASE_URL;
  19. const feedbackFileList = ref([]);
  20. const isHistoryFeedback = ref(false);
  21. const submitSuccessDialogVisible = ref(false);
  22. const submitFailureDialogVisible = ref(false);
  23. const submitFailureContent = ref("");
  24. const feedbackSubmit = async () => {
  25. console.log(feedbackContent.value);
  26. console.log(feedbackFileList.value);
  27. const token = localStorage.getItem("localToken");
  28. if (feedbackContent.value == "" && feedbackFileList.value.length == 0) {
  29. submitFailureDialogVisible.value = true;
  30. submitFailureContent.value = "请输入反馈内容或上传图片";
  31. } else if (!token) {
  32. submitFailureDialogVisible.value = true;
  33. submitFailureContent.value = "token已失效,请重新登录!";
  34. } else {
  35. try {
  36. let img1 = "";
  37. let img2 = "";
  38. let img3 = "";
  39. if (feedbackFileList.value[0]) {
  40. img1 = feedbackFileList.value[0].url;
  41. }
  42. if (feedbackFileList.value[1]) {
  43. img2 = feedbackFileList.value[1].url;
  44. }
  45. if (feedbackFileList.value[2]) {
  46. img3 = feedbackFileList.value[2].url;
  47. }
  48. console.log(img1, img2, img3);
  49. const result = await addFeedbackAPI({
  50. token: token,
  51. content: feedbackContent.value,
  52. image1: img1,
  53. image2: img2,
  54. image3: img3,
  55. });
  56. console.log(result);
  57. feedbackHistory();
  58. submitSuccessDialogVisible.value = true;
  59. } catch (error) {
  60. submitFailureDialogVisible.value = true;
  61. submitFailureContent.value =
  62. "反馈提交异常(错误代码:" +
  63. error.response.status +
  64. "),建议尝试更换网络环境后重新提交。";
  65. }
  66. }
  67. };
  68. const feedbackHistoryList = ref([]);
  69. const feedbackHistory = async () => {
  70. try {
  71. const result = await getFeedbackAPI({
  72. token: localStorage.getItem("localToken"),
  73. });
  74. console.log(result);
  75. if (result.data.length > 0) {
  76. isHistoryFeedback.value = true;
  77. feedbackHistoryList.value = result.data;
  78. } else {
  79. isHistoryFeedback.value = false;
  80. }
  81. } catch (error) {
  82. console.log(error);
  83. }
  84. };
  85. const submitSuccessConfirm = () => {
  86. feedbackContent.value = "";
  87. feedbackFileList.value = [];
  88. if (localStorage.getItem("feedbackContent")) {
  89. localStorage.removeItem("feedbackContent");
  90. }
  91. if (localStorage.getItem("feedbackFileList")) {
  92. localStorage.removeItem("feedbackFileList");
  93. }
  94. submitSuccessDialogVisible.value = false;
  95. };
  96. const submitFailureConfirm = () => {
  97. submitFailureDialogVisible.value = false;
  98. };
  99. // 字数限制提示弹窗
  100. const feedbackContentOverLengthDialogVisible = ref(false);
  101. const feedbackContentLengthJudge = () => {
  102. console.log("字数判断");
  103. console.log(feedbackContent.value.length);
  104. if (feedbackContent.value.length >= 2000) {
  105. feedbackContentOverLengthDialogVisible.value = true;
  106. }
  107. };
  108. const feedbackContentOverLengthConfirm = () => {
  109. feedbackContentOverLengthDialogVisible.value = false;
  110. };
  111. const feedbackContentChange = () => {
  112. console.log("内容改变");
  113. console.log(feedbackContent.value);
  114. localStorage.setItem("feedbackContent", feedbackContent.value);
  115. };
  116. const check = function (file, fileList) {
  117. console.log("调用check方法");
  118. console.log(fileList);
  119. console.log(fileList.length);
  120. };
  121. const successChange = function (file, fileList) {
  122. console.log("调用successChange方法");
  123. console.log(fileList);
  124. console.log(fileList.response);
  125. if (fileList.response.code == 200) {
  126. feedbackFileList.value.push(fileList.response.data);
  127. }
  128. console.log(feedbackFileList.value);
  129. localStorage.setItem(
  130. "feedbackFileList",
  131. JSON.stringify(feedbackFileList.value)
  132. );
  133. };
  134. const dialogImageUrl = ref("");
  135. const dialogVisible = ref(false);
  136. const handleRemove = (uploadFile, uploadFiles) => {
  137. console.log(uploadFile, uploadFiles);
  138. feedbackFileList.value = uploadFiles;
  139. console.log("调用handleRemove方法");
  140. console.log(feedbackFileList.value);
  141. console.log(feedbackFileList.value.length);
  142. localStorage.setItem(
  143. "feedbackFileList",
  144. JSON.stringify(feedbackFileList.value)
  145. );
  146. };
  147. const handlePictureCardPreview = (uploadFile) => {
  148. dialogImageUrl.value = uploadFile.url;
  149. dialogVisible.value = true;
  150. };
  151. // 返回弹窗
  152. const feedbackBackDialogVisible = ref(false);
  153. const feedbackBack = () => {
  154. if (feedbackContent.value == "" && feedbackFileList.value.length == 0) {
  155. dataStore.isFeedback = false;
  156. return;
  157. }
  158. feedbackBackDialogVisible.value = true;
  159. };
  160. // 确人保存
  161. const feedbackBackConfirm = () => {
  162. feedbackBackDialogVisible.value = false;
  163. dataStore.isFeedback = false;
  164. };
  165. // 取消保存
  166. const feedbackBackCancel = () => {
  167. if (localStorage.getItem("feedbackContent")) {
  168. localStorage.removeItem("feedbackContent");
  169. }
  170. if (localStorage.getItem("feedbackFileList")) {
  171. localStorage.removeItem("feedbackFileList");
  172. }
  173. feedbackBackDialogVisible.value = false;
  174. dataStore.isFeedback = false;
  175. };
  176. onMounted(() => {
  177. feedbackHistory();
  178. if (localStorage.getItem("feedbackContent")) {
  179. feedbackContent.value = localStorage.getItem("feedbackContent");
  180. } else {
  181. feedbackContent.value = "";
  182. }
  183. if (localStorage.getItem("feedbackFileList")) {
  184. feedbackFileList.value = JSON.parse(
  185. localStorage.getItem("feedbackFileList")
  186. );
  187. } else {
  188. feedbackFileList.value = [];
  189. }
  190. console.log(uploadUrl);
  191. });
  192. </script>
  193. <template>
  194. <el-container>
  195. <div>
  196. <div @click="feedbackBack">
  197. <img :src="back" alt="返回按钮" class="backImg" />
  198. </div>
  199. </div>
  200. <el-scrollbar>
  201. <el-header>
  202. <div class="feedbackImgClass">
  203. <img :src="feedback" alt="用户反馈" class="img" />
  204. <img :src="feedbackImg" alt="用户反馈" class="img" />
  205. </div>
  206. </el-header>
  207. <el-main>
  208. <div class="card">
  209. <div class="feedbackTitle header-item">填写反馈内容</div>
  210. <div class="header-item">
  211. <el-input
  212. class="feedbackContent"
  213. v-model="feedbackContent"
  214. :rows="5"
  215. type="textarea"
  216. maxlength="2000"
  217. show-word-limit
  218. placeholder="请描写您想反馈的内容..."
  219. @change="feedbackContentChange"
  220. @input="feedbackContentLengthJudge"
  221. />
  222. </div>
  223. <div class="feedbackTitle header-item">照片上传</div>
  224. <div class="header-item">
  225. <el-upload
  226. class="uploadImg"
  227. :action="uploadUrl"
  228. list-type="picture-card"
  229. :auto-upload="true"
  230. :on-success="successChange"
  231. accept=".png, .jpg, .jpeg, .ico,"
  232. :on-change="check"
  233. :file-list="feedbackFileList"
  234. :on-preview="handlePictureCardPreview"
  235. :on-remove="handleRemove"
  236. :limit="3"
  237. >
  238. <el-icon>
  239. <Plus />
  240. </el-icon>
  241. <template #tip>
  242. <div class="el-upload__tip">最多上传三张</div>
  243. </template>
  244. </el-upload>
  245. </div>
  246. <div>
  247. <div class="feedbackSubmitBtn" @click="feedbackSubmit"> </div>
  248. </div>
  249. </div>
  250. <div class="card">
  251. <div class="feedbackTitle">历史反馈内容</div>
  252. <div v-if="isHistoryFeedback">
  253. <div
  254. v-for="(item, index) in feedbackHistoryList"
  255. :key="index"
  256. class="feedbackHistory"
  257. >
  258. <div class="feedbackHistoryItem">
  259. <div class="feedbackHistoryTitle">
  260. <img :src="purpleDot" alt="紫点" class="purpleDot" />
  261. {{ moment(item.created_at).format("YYYY-MM-DD HH:mm") }}
  262. <div class="feedbackSuccess">
  263. <div class="feedbackSuccessWord">反馈成功</div>
  264. <img
  265. class="feedbackSuccessImg"
  266. :src="feedbackSuccess"
  267. alt="成功"
  268. />
  269. </div>
  270. </div>
  271. <div class="feedbackHistoryContent">
  272. {{ item.content }}
  273. </div>
  274. <div class="feedbackHistoryImg">
  275. <el-image
  276. v-if="item.image1"
  277. :src="item.image1"
  278. alt="图片错误"
  279. class="feedbackHistoryImgItem"
  280. :preview-src-list="[item.image1]"
  281. />
  282. <el-image
  283. v-if="item.image2"
  284. :src="item.image2"
  285. alt="图片错误"
  286. class="feedbackHistoryImgItem"
  287. :preview-src-list="[item.image2]"
  288. />
  289. <el-image
  290. v-if="item.image3"
  291. :src="item.image3"
  292. alt="图片错误"
  293. class="feedbackHistoryImgItem"
  294. :preview-src-list="[item.image3]"
  295. />
  296. </div>
  297. </div>
  298. </div>
  299. </div>
  300. <div v-else>
  301. <div class="noFeedback">
  302. <img class="noFeedbackImg" :src="noFeedback" alt="暂无历史提交" />
  303. 暂无记录
  304. </div>
  305. </div>
  306. </div>
  307. </el-main>
  308. </el-scrollbar>
  309. </el-container>
  310. <el-dialog v-model="dialogVisible">
  311. <img w-full :src="dialogImageUrl" alt="Preview Image" />
  312. </el-dialog>
  313. <el-dialog v-model="feedbackBackDialogVisible" class="save-dialog">
  314. <div class="imgLine">
  315. <img class="dialogImg" :src="save" alt="保存" />
  316. </div>
  317. <div class="feedbackBackTitle">系统提示</div>
  318. <div class="feedbackBackAttention">
  319. 检测到为保存内容离开将丢失修改请选择是否保留此次编辑
  320. </div>
  321. <div class="feedbackBackBtnGroup">
  322. <el-button
  323. class="feedbackBackBtn nosave"
  324. plain
  325. @click="feedbackBackCancel"
  326. type="primary"
  327. >不保留</el-button
  328. >
  329. <el-button
  330. class="feedbackBackBtn save"
  331. @click="feedbackBackConfirm"
  332. type="primary"
  333. >保留</el-button
  334. >
  335. </div>
  336. </el-dialog>
  337. <el-dialog
  338. v-model="feedbackContentOverLengthDialogVisible"
  339. class="save-dialog"
  340. >
  341. <div class="feedbackContentOverLengthTitle">温馨提示</div>
  342. <div class="feedbackContentOverLengthContent">当前输入字数已达上限</div>
  343. <div class="feedbackBackBtnGroup">
  344. <el-button
  345. class="feedbackContentOverLengthBtn confirm"
  346. type="primary"
  347. @click="feedbackContentOverLengthConfirm"
  348. >确认</el-button
  349. >
  350. </div>
  351. </el-dialog>
  352. <el-dialog v-model="submitSuccessDialogVisible" class="save-dialog">
  353. <div class="imgLine">
  354. <img class="dialogImg" :src="success" alt="成功" />
  355. </div>
  356. <div class="feedbackSuccessTitle">提交成功</div>
  357. <div class="feedbackBackAttention">感谢您的反馈</div>
  358. <div class="feedbackBackBtnGroup">
  359. <el-button
  360. class="feedbackBackBtn confirm"
  361. @click="submitSuccessConfirm"
  362. type="primary"
  363. >确定</el-button
  364. >
  365. </div>
  366. </el-dialog>
  367. <el-dialog v-model="submitFailureDialogVisible" class="save-dialog">
  368. <div class="imgLine">
  369. <img class="dialogImg" :src="failure" alt="失败" />
  370. </div>
  371. <div class="feedbackFailureTitle">提交失败</div>
  372. <div class="feedbackBackAttention">
  373. {{ submitFailureContent }}
  374. </div>
  375. <div class="feedbackBackBtnGroup">
  376. <el-button
  377. class="feedbackBackBtn confirm"
  378. @click="submitFailureConfirm"
  379. type="primary"
  380. >确定</el-button
  381. >
  382. </div>
  383. </el-dialog>
  384. </template>
  385. <style scoped>
  386. .backImg {
  387. width: 40px;
  388. height: 40px;
  389. margin-left: 10px;
  390. }
  391. .el-container {
  392. display: flex;
  393. flex-direction: column;
  394. overflow: auto;
  395. }
  396. .el-header {
  397. height: auto;
  398. padding: 0;
  399. }
  400. .el-main {
  401. height: auto;
  402. padding: 0;
  403. display: flex;
  404. flex-direction: column;
  405. gap: 20px;
  406. overflow: hidden;
  407. }
  408. .card {
  409. background-color: white;
  410. padding: 20px;
  411. height: auto;
  412. border-radius: 15px;
  413. }
  414. .header-item {
  415. margin: 10px 0;
  416. }
  417. .feedbackImgClass {
  418. display: flex;
  419. justify-content: space-between;
  420. align-items: center;
  421. margin: 0 20px;
  422. }
  423. .img {
  424. max-width: 50%;
  425. height: auto;
  426. object-fit: contain;
  427. }
  428. .feedbackSubmitBtn {
  429. width: 60%;
  430. height: 50px;
  431. text-align: center;
  432. display: flex;
  433. align-items: center;
  434. justify-content: center;
  435. margin: 0 auto;
  436. background: linear-gradient(90deg, #cac4fe, #b9d0fc, #a7dbfc);
  437. border-radius: 60px;
  438. color: #4423ff;
  439. font-weight: bold;
  440. font-size: 25px;
  441. cursor: pointer;
  442. }
  443. .feedbackTitle {
  444. font-size: 25px;
  445. font-weight: bold;
  446. }
  447. .feedbackContent {
  448. font-size: 20px;
  449. }
  450. @media (max-width: 768px) {
  451. .feedbackTitle {
  452. font-size: 20px;
  453. }
  454. .feedbackContent {
  455. font-size: 15px;
  456. }
  457. }
  458. .noFeedback {
  459. margin: 20px 0;
  460. display: flex;
  461. flex-direction: column;
  462. align-items: center;
  463. justify-content: center;
  464. }
  465. .noFeedbackImg {
  466. width: 200px;
  467. height: 200px;
  468. }
  469. .feedbackBackTitle {
  470. width: 100%;
  471. text-align: center;
  472. font-size: 25px;
  473. font-weight: bold;
  474. color: #2961ff;
  475. }
  476. .feedbackBackAttention {
  477. font-size: 15px;
  478. font-weight: bold;
  479. margin: 10px 5px;
  480. text-align: center;
  481. }
  482. .feedbackBackBtnGroup {
  483. display: flex;
  484. align-items: center;
  485. justify-content: center;
  486. margin: 20px auto;
  487. width: 75%;
  488. }
  489. .feedbackBackBtn {
  490. margin: 0 auto;
  491. font-weight: bold;
  492. border-radius: 20px;
  493. }
  494. .save {
  495. background: #816cf6;
  496. border: none;
  497. }
  498. .nosave {
  499. color: #816cf6;
  500. background: white;
  501. border: none;
  502. }
  503. .confirm {
  504. background: #816cf6;
  505. border: none;
  506. }
  507. .feedbackContentOverLengthTitle {
  508. font-size: 25px;
  509. font-weight: bold;
  510. width: 100%;
  511. text-align: center;
  512. margin: 20px 0;
  513. color: #2961ff;
  514. }
  515. .feedbackContentOverLengthContent {
  516. font-size: 15px;
  517. font-weight: bold;
  518. width: 100%;
  519. text-align: center;
  520. margin: 20px 0;
  521. }
  522. .imgLine {
  523. width: 100%;
  524. display: flex;
  525. }
  526. .dialogImg {
  527. max-width: 300px;
  528. width: 50%;
  529. height: 50%;
  530. margin: 0 auto;
  531. }
  532. .feedbackSuccessTitle {
  533. width: 100%;
  534. text-align: center;
  535. font-size: 25px;
  536. font-weight: bold;
  537. color: #2961ff;
  538. }
  539. .feedbackFailureTitle {
  540. width: 100%;
  541. text-align: center;
  542. font-size: 25px;
  543. font-weight: bold;
  544. color: #ff4646;
  545. }
  546. .purpleDot {
  547. margin: 0px 5px 0px 0px;
  548. width: 20px;
  549. height: 20px;
  550. }
  551. .feedbackHistoryTitle {
  552. display: flex;
  553. }
  554. .feedbackSuccess {
  555. margin-left: auto;
  556. display: flex;
  557. }
  558. .feedbackSuccessWord {
  559. padding: 3px 0 0 0;
  560. color: #4221ff;
  561. font-weight: bold;
  562. }
  563. .feedbackSuccessImg {
  564. width: 30px;
  565. height: auto;
  566. margin: 0 5px;
  567. }
  568. .feedbackHistoryContent {
  569. min-height: 50px;
  570. margin: 0 0 0 25px;
  571. font-weight: bold;
  572. }
  573. .feedbackHistoryImg {
  574. display: flex;
  575. margin: 0px 0px 0px 20px;
  576. }
  577. .feedbackHistoryImgItem {
  578. width: 100px;
  579. height: auto;
  580. margin: 5px 10px;
  581. }
  582. </style>
  583. <style>
  584. .uploadImg .el-upload {
  585. width: 150px;
  586. height: 150px;
  587. }
  588. @media (max-width: 768px) {
  589. .uploadImg .el-upload {
  590. width: 100px;
  591. height: 100px;
  592. }
  593. }
  594. .feedbackContentOverLengthBtn {
  595. margin: 0 auto;
  596. color: white;
  597. &:hover,
  598. &:active,
  599. &:focus {
  600. color: white !important;
  601. }
  602. }
  603. .save-dialog {
  604. background: linear-gradient(90deg, #cac4fe, #b9d0fc, #a7dbfc);
  605. border-radius: 20px;
  606. }
  607. </style>