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.

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