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.

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