鸿宇研学生前端代码
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.

359 lines
8.5 KiB

  1. <template>
  2. <view>
  3. <uv-popup ref="popup" mode="bottom" bgColor="none" @change="onPopupChange">
  4. <view class="popup__view" v-if="isShow">
  5. <view class="flex header">
  6. <view class="title">评价</view>
  7. <button class="btn" @click="close">关闭</button>
  8. </view>
  9. <view class="form">
  10. <uv-form
  11. ref="form"
  12. :model="form"
  13. :rules="rules"
  14. errorType="toast"
  15. >
  16. <view class="section">
  17. <productCard :data="productCardData" style="background: linear-gradient(120deg, #DAF3FF, #FBFEFF 30%, #FBFEFF); border: 2rpx solid #FFFFFF;"></productCard>
  18. </view>
  19. <view class="section">
  20. <view class="form-item">
  21. <uv-form-item prop="processScore" :customStyle="formItemStyle">
  22. <view class="flex row">
  23. <view class="form-item-label">行程</view>
  24. <view class="form-item-content">
  25. <formRate v-model="form.processScore"></formRate>
  26. </view>
  27. </view>
  28. </uv-form-item>
  29. </view>
  30. <view class="form-item">
  31. <uv-form-item prop="spotScore" :customStyle="formItemStyle">
  32. <view class="flex row">
  33. <view class="form-item-label">景点</view>
  34. <view class="form-item-content">
  35. <formRate v-model="form.spotScore"></formRate>
  36. </view>
  37. </view>
  38. </uv-form-item>
  39. </view>
  40. <view class="form-item">
  41. <uv-form-item prop="teacherScore" :customStyle="formItemStyle">
  42. <view class="flex row">
  43. <view class="form-item-label">导师</view>
  44. <view class="form-item-content">
  45. <formRate v-model="form.teacherScore"></formRate>
  46. </view>
  47. </view>
  48. </uv-form-item>
  49. </view>
  50. </view>
  51. <view class="form-item">
  52. <uv-form-item prop="contentIds" :customStyle="formItemStyle">
  53. <view class="form-item-content">
  54. <view class="tags">
  55. <view
  56. v-for="(item, oIdx) in configList.commentOptionList" :key="oIdx"
  57. :class="['tag', form.contentIds.includes(item.id) ? 'is-active' : '']"
  58. @click="onSelectContent(item.id)"
  59. >
  60. {{ item.title }}
  61. </view>
  62. </view>
  63. </view>
  64. </uv-form-item>
  65. </view>
  66. </uv-form>
  67. </view>
  68. <view class="footer">
  69. <button class="flex btn" @click="onPublish">发布</button>
  70. </view>
  71. </view>
  72. </uv-popup>
  73. </view>
  74. </template>
  75. <script>
  76. import productCard from '@/pages_order/order/components/productCard.vue'
  77. import formRate from '@/pages_order/components/formRate.vue'
  78. export default {
  79. components: {
  80. productCard,
  81. formRate,
  82. },
  83. data() {
  84. return {
  85. isShow: false,
  86. id: null,
  87. // todo: fetch
  88. detail: {},
  89. form: {
  90. processScore: null,
  91. spotScore: null,
  92. teacherScore: null,
  93. contentIds: [],
  94. },
  95. rules: {
  96. 'processScore': {
  97. type: 'number',
  98. required: true,
  99. message: '请为行程打分',
  100. },
  101. 'spotScore': {
  102. type: 'number',
  103. required: true,
  104. message: '请为景点打分',
  105. },
  106. 'teacherScore': {
  107. type: 'number',
  108. required: true,
  109. message: '请为导师打分',
  110. },
  111. 'contentIds': {
  112. type: 'array',
  113. required: true,
  114. message: '请选择评语',
  115. },
  116. },
  117. // todo: fetch
  118. options: [],
  119. }
  120. },
  121. computed: {
  122. productCardData() {
  123. const {
  124. activityId,
  125. activityTitle,
  126. activityBrief,
  127. activityTag,
  128. startDate,
  129. endDate,
  130. } = this.detail
  131. return {
  132. time: 'time',
  133. product: {
  134. id: activityId,
  135. title: activityTitle,
  136. brief: activityBrief,
  137. tagDetails: activityTag,
  138. dateList: [
  139. {
  140. id: 'time',
  141. startDate,
  142. endDate,
  143. }
  144. ]
  145. }
  146. }
  147. },
  148. },
  149. methods: {
  150. async getData() {
  151. // todo: fetch order product
  152. },
  153. async open(id, detail) {
  154. console.log('open', id, detail)
  155. this.id = id
  156. this.detail = detail
  157. // todo: fetch order product
  158. // await this.getData()
  159. this.form = {
  160. processScore: null,
  161. spotScore: null,
  162. teacherScore: null,
  163. contentIds: [],
  164. }
  165. this.$refs.popup.open()
  166. },
  167. close() {
  168. this.$refs.popup.close()
  169. },
  170. onPopupChange(e) {
  171. this.isShow = e.show
  172. },
  173. onSelectContent(id) {
  174. let arr = this.form.contentIds
  175. this.form.contentIds = arr.includes(id) ? arr.filter(item => item !== id) : arr.concat(id)
  176. },
  177. async onPublish() {
  178. try {
  179. await this.$refs.form.validate()
  180. const {
  181. processScore,
  182. spotScore,
  183. teacherScore,
  184. contentIds,
  185. } = this.form
  186. const commentOptionList = this.configList.commentOptionList
  187. const content = contentIds.map(id => {
  188. return commentOptionList.find(item => item.id === id)?.title
  189. }).join('、')
  190. const params = {
  191. orderId: this.id,
  192. processScore,
  193. spotScore,
  194. teacherScore,
  195. content,
  196. }
  197. await this.$fetch('addComment', params)
  198. uni.showToast({
  199. icon: 'success',
  200. title: '发布成功',
  201. });
  202. this.$emit('submitted')
  203. this.close()
  204. } catch (err) {
  205. console.log('onSave err', err)
  206. }
  207. },
  208. },
  209. }
  210. </script>
  211. <style lang="scss" scoped>
  212. .popup__view {
  213. width: 100vw;
  214. display: flex;
  215. flex-direction: column;
  216. box-sizing: border-box;
  217. background: #FFFFFF;
  218. border-top-left-radius: 32rpx;
  219. border-top-right-radius: 32rpx;
  220. }
  221. .header {
  222. position: relative;
  223. width: 100%;
  224. padding: 24rpx 0;
  225. box-sizing: border-box;
  226. border-bottom: 2rpx solid #EEEEEE;
  227. .title {
  228. font-family: PingFang SC;
  229. font-weight: 500;
  230. font-size: 34rpx;
  231. line-height: 1.4;
  232. color: #181818;
  233. }
  234. .btn {
  235. font-family: PingFang SC;
  236. font-weight: 500;
  237. font-size: 32rpx;
  238. line-height: 1.4;
  239. color: #8B8B8B;
  240. position: absolute;
  241. top: 26rpx;
  242. left: 40rpx;
  243. }
  244. }
  245. .section {
  246. & + & {
  247. margin-top: 24rpx;
  248. }
  249. }
  250. .form {
  251. max-height: 75vh;
  252. padding: 32rpx 40rpx;
  253. box-sizing: border-box;
  254. overflow-y: auto;
  255. &-item {
  256. &-label {
  257. margin-bottom: 14rpx;
  258. display: flex;
  259. align-items: center;
  260. font-family: PingFang SC;
  261. font-weight: 400;
  262. font-size: 26rpx;
  263. line-height: 1.4;
  264. color: #181818;
  265. .icon {
  266. margin-right: 8rpx;
  267. width: 16rpx;
  268. height: auto;
  269. }
  270. }
  271. &-content {
  272. }
  273. }
  274. }
  275. .row {
  276. justify-content: space-between;
  277. padding: 4rpx 0;
  278. & + & {
  279. margin-top: 4rpx;
  280. }
  281. .form-label {
  282. margin: 0;
  283. }
  284. }
  285. .tags {
  286. display: grid;
  287. grid-template-columns: repeat(2, 1fr);
  288. gap: 24rpx;
  289. }
  290. .tag {
  291. min-width: 0;
  292. padding: 16rpx;
  293. font-size: 26rpx;
  294. line-height: 1.4;
  295. color: #252545;
  296. background: #F5F8FF;
  297. border-radius: 24rpx;
  298. &.is-active {
  299. color: #FFFFFF;
  300. background: #00A9FF;
  301. }
  302. }
  303. .footer {
  304. width: 100%;
  305. padding: 32rpx 40rpx;
  306. box-sizing: border-box;
  307. border-top: 2rpx solid #F1F1F1;
  308. .btn {
  309. width: 100%;
  310. padding: 14rpx 0;
  311. box-sizing: border-box;
  312. font-family: PingFang SC;
  313. font-weight: 500;
  314. font-size: 36rpx;
  315. line-height: 1.4;
  316. color: #FFFFFF;
  317. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  318. border: 2rpx solid #00A9FF;
  319. border-radius: 41rpx;
  320. }
  321. }
  322. </style>