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.

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