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.

686 lines
14 KiB

4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
  1. <template>
  2. <view class="main">
  3. <view class="top" :style="{height:iSMT+'px'}"></view>
  4. <!-- 头部导航 -->
  5. <view class="header">
  6. <view class="back-icon">
  7. <image @click="onBack" src="/static/customer-service-platform/cs-platform-back.png"
  8. class="header-icon-image"></image>
  9. </view>
  10. <view class="title">{{headerTitle}}</view>
  11. <view class="notification-icon">
  12. <image src="/static/customer-service-platform/message.png" class="header-icon-image"></image>
  13. </view>
  14. </view>
  15. <!-- 内容区域 - 使用滚动视图 -->
  16. <scroll-view scroll-y class="content-container">
  17. <view class="content-header">
  18. <view class="content-header-area">
  19. <view class="logo">
  20. <image mode="aspectFit" src="/static/customer-service-platform/ellipse-dc-img.png"></image>
  21. </view>
  22. <view class="greeting">
  23. <text class="greet-title">我能为你做点什么</text>
  24. <text class="greet-sub">DeepChart随时为您提供服务</text>
  25. </view>
  26. </view>
  27. </view>
  28. <!--猜你想问卡片部分-->
  29. <view class="card">
  30. <view class="suggest-header">
  31. <text class="suggest-title">猜你想问</text>
  32. <view class="swap" @click="getQuestionList()">
  33. <image class="swap-icon" src="/static/customer-service-platform/refresh-icon.png"></image>
  34. <text class="swap-title">换一换</text>
  35. </view>
  36. </view>
  37. <view class="card-line"></view>
  38. <view class="suggest-list">
  39. <view class="suggest-item" v-for="(q, idx) in showQuestions" :key="idx" @click="onQuestionClick(q)">
  40. <view class="left">
  41. <view :class="['num', 'num-' + ((idx % 5) + 1)]">{{ idx + 1 }}</view>
  42. <text class="q-text">{{ q }}</text>
  43. </view>
  44. <view class="right">
  45. <text class="arrow"></text>
  46. </view>
  47. </view>
  48. </view>
  49. </view>
  50. <!--反馈卡片部分-->
  51. <text class="feedback-card-title">反馈中心</text>
  52. <view class="card">
  53. <view class="suggest-header">
  54. <text class="feedback-title">填写反馈内容</text>
  55. </view>
  56. <view class="card-line"></view>
  57. <textarea class="feedback-input" placeholder="请描述您想反馈的内容 最多可输入200字" maxlength="200"
  58. v-model="feedbackText" />
  59. <view class="meta-row">
  60. <text class="char-count">{{ feedbackText.length }}/200</text>
  61. </view>
  62. <view class="suggest-header">
  63. <text class="upload-img-tip">上传图片</text>
  64. </view>
  65. <view class="upload-row">
  66. <view class="img-slot" v-for="(img, index) in images" :key="index">
  67. <image :src="img" mode="scaleToFill" class="slot-img" />
  68. <button v-if="img" class="remove" @click="removeImage(index)">×</button>
  69. </view>
  70. <view class="img-slot" v-if="images.length < 3">
  71. <view class="slot-empty" @click="chooseImage()">
  72. <image src="/static/customer-service-platform/camera.png" class="camera-icon" />
  73. </view>
  74. </view>
  75. <text class="tip-text" v-if="images.length === 0">最多添加3张图片</text>
  76. </view>
  77. <button class="feedback-btn" @click="onSumbitFeedback()">提交</button>
  78. <feedback-modal ref="feedback" @confirm="onConfirm" />
  79. </view>
  80. <!--历史反馈卡片部分-->
  81. <view class="card">
  82. <text class="feedback-title">历史反馈内容</text>
  83. <view class="card-line"></view>
  84. <button class="feedback-btn" @click="viewHistory">查看</button>
  85. </view>
  86. </scroll-view>
  87. </view>
  88. </template>
  89. <script>
  90. import { useUserStore } from "../../stores/modules/userInfo.js"
  91. import {
  92. getQuestionApi,
  93. addFeedbackRecordApi,
  94. uploadImageApi
  95. } from "../../api/customerServicePlatform/customerServicePlatform";
  96. import FeedbackModal from '@/components/FeedbackModal.vue'
  97. export default {
  98. components: {
  99. FeedbackModal
  100. },
  101. data() {
  102. return {
  103. headerTitle: '智能客服中台',
  104. iSMT: 0,
  105. questions: [
  106. "DeepChart 有免费功能和付费功能的区分吗?具体有哪些?",
  107. "如何参与平台的用户反馈活动?反馈的问题会被采纳吗?",
  108. "我的自选股最多能添加多少只?能否按市场分类管理?",
  109. "注册时必须提供手机号 / 邮箱吗?能否匿名使用?",
  110. "忘记登录密码了,如何找回?",
  111. '如何注册账号?',
  112. '为什么无法注册账户?'
  113. ],
  114. showQuestions: [],
  115. feedbackText: '',
  116. images: [],
  117. token:''
  118. }
  119. },
  120. mounted() {
  121. // 状态栏高度
  122. this.iSMT = uni.getSystemInfoSync().statusBarHeight;
  123. this.getQuestionList()
  124. const memberStore = useUserStore()
  125. this.token = memberStore.userInfo?.token
  126. },
  127. methods: {
  128. onSuccess() {
  129. this.$refs.feedback.show({
  130. status: 'success',
  131. title: '提交成功',
  132. subtitle: '— 感谢您的反馈 —',
  133. buttonText: '确定',
  134. width: '80%',
  135. });
  136. },
  137. onFail() {
  138. this.$refs.feedback.show({
  139. status: 'fail',
  140. title: '提交失败',
  141. subtitle: '— 请重新提交 —',
  142. buttonText: '确定',
  143. width: '80%',
  144. });
  145. },
  146. onBack() {
  147. if (typeof uni !== 'undefined') uni.navigateBack();
  148. },
  149. async getQuestionList() {
  150. const res = await getQuestionApi()
  151. console.log(res)
  152. if (res.code == 200) {
  153. this.showQuestions = res.data
  154. }
  155. },
  156. onQuestionClick(q) {
  157. if (typeof uni !== 'undefined') uni.navigateTo({
  158. url: `/pages/customerServicePlatform/questionDetail?question=${encodeURIComponent(q)}`
  159. });
  160. },
  161. chooseImage() {
  162. const that = this;
  163. if (typeof uni === 'undefined' || !uni.chooseImage) return;
  164. const remain = 3 - (that.images ? that.images.length : 0);
  165. if (remain <= 0) {
  166. if (typeof uni !== 'undefined') uni.showToast({
  167. title: '最多只能上传3张',
  168. icon: 'none'
  169. });
  170. return;
  171. }
  172. uni.chooseImage({
  173. count: remain,
  174. sizeType: ['original', 'compressed'],
  175. sourceType: ['album', 'camera'],
  176. success(res) {
  177. const paths = res.tempFilePaths || (res.tempFiles && res.tempFiles.map(f => f.path)) || [];
  178. for (let p of paths) {
  179. if (that.images.length < 3) {
  180. that.images.push(p);
  181. }
  182. }
  183. },
  184. fail(err) {
  185. uni.showToast({
  186. title: `选择图片失败`,
  187. icon: 'none'
  188. });
  189. }
  190. });
  191. },
  192. removeImage(index) {
  193. // 删除并保持响应
  194. this.images.splice(index, 1);
  195. },
  196. async onSumbitFeedback() {
  197. if (!this.feedbackText.trim()) {
  198. if (typeof uni !== 'undefined') uni.showToast({
  199. title: '请填写反馈内容',
  200. icon: 'none'
  201. });
  202. return;
  203. }
  204. if (typeof uni !== 'undefined') uni.showLoading({
  205. title: '提交中...'
  206. });
  207. try {
  208. let uploadedImages = [];
  209. let imgFlag = true
  210. for (let i = 0; i < this.images.length; i++) {
  211. const f = this.images[i];
  212. await new Promise((resolve, reject) => {
  213. uni.getImageInfo({
  214. src: f,
  215. success: () => resolve(),
  216. fail: reject
  217. });
  218. });
  219. const uploadRes = await new Promise((resolve, reject) => {
  220. uni.uploadFile({
  221. url: 'http://39.101.133.168:8828/hljw/api/aws/upload',
  222. filePath: f,
  223. name: 'file',
  224. formData: {
  225. dir: 'deepchart'
  226. },
  227. success: (res) => {
  228. try {
  229. const data = JSON.parse(res.data);
  230. if (data.code === 200) {
  231. uploadedImages.push(data.data.url);
  232. resolve(data);
  233. } else {
  234. uni.showToast({
  235. title: `${i + 1}张图片上传失败`,
  236. icon: 'none'
  237. });
  238. imgFlag = false;
  239. reject(data);
  240. }
  241. } catch (err) {
  242. imgFlag = false;
  243. reject(err);
  244. }
  245. },
  246. fail: (err) => {
  247. imgFlag = false;
  248. uni.showToast({
  249. title: `${i + 1}张图片上传失败`,
  250. icon: 'none'
  251. });
  252. reject(err);
  253. }
  254. });
  255. });
  256. }
  257. if (!imgFlag) {
  258. return
  259. }
  260. const [image1 = '', image2 = '', image3 = ''] = uploadedImages;
  261. const res = await addFeedbackRecordApi({
  262. token: this.token,
  263. content: this.feedbackText,
  264. image1,
  265. image2,
  266. image3
  267. })
  268. if (res.code == 200) {
  269. this.onSuccess()
  270. } else {
  271. this.onFail()
  272. }
  273. } catch {
  274. this.onFail()
  275. } finally {
  276. uni.hideLoading();
  277. this.feedbackText = '';
  278. this.images = [];
  279. }
  280. },
  281. viewHistory() {
  282. // 跳转到历史页
  283. if (typeof uni !== 'undefined') uni.navigateTo({
  284. url: '/pages/customerServicePlatform/historyRecord'
  285. });
  286. }
  287. }
  288. }
  289. </script>
  290. <style scoped>
  291. .main {
  292. display: flex;
  293. flex-direction: column;
  294. height: 100vh;
  295. background-color: #ffffff;
  296. }
  297. .header {
  298. display: flex;
  299. justify-content: space-between;
  300. align-items: center;
  301. padding: 20rpx 30rpx;
  302. background-color: #ffffff;
  303. }
  304. .title {
  305. color: #000000;
  306. text-align: center;
  307. font-size: 32rpx;
  308. font-style: normal;
  309. font-weight: 400;
  310. }
  311. .back-icon,
  312. .notification-icon {
  313. width: 40rpx;
  314. display: flex;
  315. align-items: center;
  316. justify-content: center;
  317. }
  318. .header-icon-image {
  319. width: 40rpx;
  320. height: 40rpx;
  321. object-fit: contain;
  322. }
  323. .content-container {
  324. padding: 20rpx;
  325. width: 100%;
  326. box-sizing: border-box;
  327. overflow-x: hidden;
  328. }
  329. .content-header {
  330. display: flex;
  331. align-items: center;
  332. justify-content: center;
  333. gap: 24rpx;
  334. padding: 0 60rpx;
  335. width: 100%;
  336. box-sizing: border-box;
  337. height: 188rpx;
  338. }
  339. .content-header-area {
  340. display: flex;
  341. gap: 20rpx;
  342. }
  343. .logo {
  344. width: 120rpx;
  345. height: 120rpx;
  346. display: flex;
  347. align-items: center;
  348. justify-content: center;
  349. flex: 0 0 112rpx;
  350. }
  351. .greeting {
  352. display: flex;
  353. flex-direction: column;
  354. justify-content: center;
  355. flex: 1 1 auto;
  356. }
  357. .greet-title {
  358. color: #000;
  359. font-size: 40rpx;
  360. font-style: normal;
  361. font-weight: 500;
  362. line-height: normal;
  363. margin: 0;
  364. overflow: hidden;
  365. text-overflow: ellipsis;
  366. white-space: nowrap;
  367. }
  368. .greet-sub {
  369. color: #838383;
  370. font-size: 28rpx;
  371. font-style: normal;
  372. font-weight: 400;
  373. line-height: normal;
  374. margin-top: 12rpx;
  375. overflow: hidden;
  376. text-overflow: ellipsis;
  377. white-space: nowrap;
  378. }
  379. .card {
  380. width: 90%;
  381. margin: 0 auto;
  382. border-radius: 16rpx;
  383. padding: 20rpx 40rpx;
  384. box-sizing: border-box;
  385. border-radius: 12rpx;
  386. border: 4rpx solid #FCC8D4;
  387. background: linear-gradient(180deg, #FCC8D3 0%, #FEF0F3 30%, #FFF 100%);
  388. margin-bottom: 20rpx;
  389. }
  390. .suggest-header {
  391. width: 100%;
  392. display: flex;
  393. align-items: center;
  394. justify-content: space-between;
  395. }
  396. .suggest-title {
  397. color: #000000;
  398. font-size: 32rpx;
  399. font-style: normal;
  400. font-weight: 400;
  401. line-height: normal;
  402. }
  403. .swap {
  404. display: flex;
  405. align-items: center;
  406. transition: transform 0.1s ease, background-color 0.1s ease;
  407. }
  408. .swap:active {
  409. transform: scale(0.95);
  410. }
  411. .swap-icon {
  412. width: 30rpx;
  413. height: 30rpx;
  414. }
  415. .swap-title {
  416. padding-left: 8rpx;
  417. color: #000000;
  418. font-size: 24rpx;
  419. font-style: normal;
  420. font-weight: 400;
  421. line-height: normal;
  422. }
  423. .suggest-list {
  424. margin-top: 20rpx;
  425. }
  426. .card-line {
  427. margin-top: 20rpx;
  428. width: 100%;
  429. height: 2rpx;
  430. border-radius: 2rpx;
  431. background: #FFF;
  432. }
  433. .suggest-item {
  434. display: flex;
  435. justify-content: space-between;
  436. align-items: center;
  437. padding-top: 10rpx;
  438. border-radius: 12rpx;
  439. margin-bottom: 20rpx;
  440. width: 100%;
  441. box-sizing: border-box;
  442. }
  443. .left {
  444. width: 90%;
  445. display: flex;
  446. align-items: center;
  447. }
  448. .num {
  449. font-size: 40rpx;
  450. font-style: normal;
  451. font-weight: 700;
  452. line-height: normal;
  453. }
  454. .num-1 {
  455. color: #df5662;
  456. }
  457. .num-2 {
  458. color: #ec6d4f;
  459. }
  460. .num-3 {
  461. color: #f3ba40;
  462. }
  463. .num-4 {
  464. color: #9296a0;
  465. }
  466. .num-5 {
  467. color: #9296a0;
  468. }
  469. .q-text {
  470. padding-left: 14rpx;
  471. display: block;
  472. color: #333;
  473. font-size: 28rpx;
  474. white-space: nowrap;
  475. overflow: hidden;
  476. text-overflow: ellipsis;
  477. }
  478. .right {
  479. width: 48rpx;
  480. display: flex;
  481. align-items: center;
  482. justify-content: flex-end;
  483. }
  484. .arrow {
  485. color: #cfcfcf;
  486. font-size: 36rpx;
  487. }
  488. .suggest-item:active {
  489. background: rgba(255, 77, 128, 0.06);
  490. }
  491. .feedback-card-title {
  492. display: flex;
  493. justify-content: center;
  494. color: #000000;
  495. font-size: 32rpx;
  496. font-weight: 700;
  497. line-height: 40rpx;
  498. width: 100%;
  499. margin-bottom: 20rpx;
  500. }
  501. .feedback-title {
  502. color: #000000;
  503. font-size: 32rpx;
  504. font-style: normal;
  505. font-weight: 400;
  506. line-height: normal;
  507. }
  508. .feedback-input {
  509. width: 100%;
  510. display: flex;
  511. padding: 20rpx;
  512. flex-direction: column;
  513. box-sizing: border-box;
  514. align-items: flex-start;
  515. gap: 12rpx;
  516. align-self: stretch;
  517. border-radius: 12rpx;
  518. border: 2rpx solid #F0F1F1;
  519. margin-top: 20rpx;
  520. display: flex;
  521. background: #FFF;
  522. color: #8a8a8a;
  523. font-size: 24rpx;
  524. font-weight: 700;
  525. line-height: normal;
  526. }
  527. .meta-row {
  528. display: flex;
  529. justify-content: flex-end;
  530. margin-top: 12rpx;
  531. }
  532. .char-count {
  533. color: #999
  534. }
  535. .upload-img-tip {
  536. color: #000000;
  537. font-size: 24rpx;
  538. font-style: normal;
  539. font-weight: 400;
  540. line-height: normal;
  541. }
  542. .upload-row {
  543. display: flex;
  544. justify-content: flex-start;
  545. align-items: flex-start;
  546. align-content: flex-start;
  547. flex-wrap: wrap;
  548. gap: 20rpx;
  549. margin-top: 20rpx;
  550. width: 100%;
  551. }
  552. .img-slot {
  553. width: calc((100% - 2 * 30rpx) / 3);
  554. aspect-ratio: 1 / 1;
  555. border-radius: 6px;
  556. border: 1px solid #F0F1F1;
  557. background: #FFF;
  558. position: relative;
  559. display: flex;
  560. align-items: center;
  561. justify-content: center;
  562. overflow: hidden;
  563. transition: transform 0.2s ease;
  564. }
  565. .slot-empty {
  566. width: 100%;
  567. height: 100%;
  568. display: flex;
  569. align-items: center;
  570. justify-content: center;
  571. }
  572. .camera-icon {
  573. width: 34rpx;
  574. height: 34rpx;
  575. }
  576. .slot-img {
  577. width: 100%;
  578. height: 100%;
  579. border-radius: 16rpx;
  580. }
  581. .remove {
  582. position: absolute;
  583. right: 6rpx;
  584. top: 6rpx;
  585. border-radius: 50%;
  586. background: #fd5c58;
  587. padding: 0;
  588. color: #fff;
  589. width: 36rpx;
  590. height: 36rpx;
  591. font-size: 28rpx;
  592. line-height: 36rpx;
  593. text-align: center;
  594. border: none;
  595. outline: none;
  596. cursor: pointer;
  597. }
  598. .remove:active {
  599. background: rgba(0, 0, 0, 0.75);
  600. }
  601. .image-upload-tip {
  602. display: flex;
  603. align-items: center;
  604. }
  605. .tip-text {
  606. color: #999999;
  607. font-size: 24rpx;
  608. font-style: normal;
  609. font-weight: 400;
  610. line-height: 40rpx;
  611. padding-top: 64rpx;
  612. }
  613. .feedback-btn {
  614. margin-top: 24rpx;
  615. width: 180rpx;
  616. height: 60rpx;
  617. aspect-ratio: 89/30;
  618. border-radius: 30rpx;
  619. background: #090A08;
  620. color: #ffffff;
  621. display: flex;
  622. justify-content: center;
  623. align-items: center;
  624. font-size: 32rpx;
  625. font-style: normal;
  626. font-weight: 700;
  627. line-height: normal;
  628. }
  629. </style>