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

362 lines
9.6 KiB

2 months ago
  1. <template>
  2. <view>
  3. <uv-popup ref="popup" mode="bottom" bgColor="none" :zIndex="1000000" @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. errorType="toast"
  14. >
  15. <view class="form-item">
  16. <uv-form-item prop="experienceId" :customStyle="formItemStyle">
  17. <view class="form-item-label">
  18. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  19. 关联项目
  20. </view>
  21. <view class="form-item-content">
  22. <view class="flex row" @click="openRelatePojectPicker">
  23. <view v-if="form.experienceId" class="text">{{ projectDesc }}</view>
  24. <view v-else class="text placeholder">请选择关联项目</view>
  25. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  26. </view>
  27. <reloateProjectPopup ref="reloateProjectPopup" :options="projects" @confirm="onRelateProjectChange"></reloateProjectPopup>
  28. </view>
  29. </uv-form-item>
  30. </view>
  31. <!-- <view class="form-item">
  32. <uv-form-item prop="processScore" :customStyle="formItemStyle">
  33. <view class="flex row">
  34. <view class="form-item-label">行程</view>
  35. <view class="form-item-content">
  36. <formRate v-model="form.processScore"></formRate>
  37. </view>
  38. </view>
  39. </uv-form-item>
  40. </view>
  41. <view class="form-item">
  42. <uv-form-item prop="spotScore" :customStyle="formItemStyle">
  43. <view class="flex row">
  44. <view class="form-item-label">景点</view>
  45. <view class="form-item-content">
  46. <formRate v-model="form.spotScore"></formRate>
  47. </view>
  48. </view>
  49. </uv-form-item>
  50. </view>
  51. <view class="form-item">
  52. <uv-form-item prop="teacherScore" :customStyle="formItemStyle">
  53. <view class="flex row">
  54. <view class="form-item-label">导师</view>
  55. <view class="form-item-content">
  56. <formRate v-model="form.teacherScore"></formRate>
  57. </view>
  58. </view>
  59. </uv-form-item>
  60. </view> -->
  61. <view class="form-item">
  62. <uv-form-item prop="images" :customStyle="formItemStyle">
  63. <view class="form-item-label">上传图片</view>
  64. <view class="form-item-content">
  65. <formUpload v-model="form.images"></formUpload>
  66. </view>
  67. </uv-form-item>
  68. </view>
  69. <view class="form-item" v-for="(item, index) in configList.experienceQuestionList" :key="item.id">
  70. <uv-form-item :prop="`texts[${index}]`" :customStyle="formItemStyle">
  71. <view class="form-item-label">
  72. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  73. {{ item.question }}
  74. </view>
  75. <view class="form-item-content">
  76. <formTextarea v-model="form.texts[index]"></formTextarea>
  77. </view>
  78. </uv-form-item>
  79. </view>
  80. </uv-form>
  81. </view>
  82. <view class="footer">
  83. <button class="flex btn" @click="onPublish">发布</button>
  84. </view>
  85. </view>
  86. </uv-popup>
  87. </view>
  88. </template>
  89. <script>
  90. import { mapState } from 'vuex'
  91. import reloateProjectPopup from '@/pages_order/components/reloateProjectPopup.vue'
  92. import formTextarea from '@/pages_order/components/formTextarea.vue'
  93. import formUpload from '@/pages_order/components/formUpload.vue'
  94. import formRate from '@/pages_order/components/formRate.vue'
  95. export default {
  96. components: {
  97. reloateProjectPopup,
  98. formTextarea,
  99. formUpload,
  100. formRate,
  101. },
  102. data() {
  103. return {
  104. isShow: false,
  105. form: {
  106. experienceId: null,
  107. // processScore: null,
  108. // spotScore: null,
  109. // teacherScore: null,
  110. images: [],
  111. texts: [],
  112. },
  113. projects: [],
  114. }
  115. },
  116. computed: {
  117. ...mapState(['userInfo', 'configList']),
  118. projectDesc() {
  119. const { experienceId } = this.form
  120. const target = this.projects?.find?.(item => item.id === experienceId)
  121. return target?.name || ''
  122. },
  123. },
  124. methods: {
  125. async fetchProjectOptions() {
  126. try {
  127. const records = (await this.$fetch('queryExperienceList', { pageNo: 1, pageSize: 1000, }))?.records
  128. this.projects = records.map(item => {
  129. return {
  130. id: item.id,
  131. name: item.activityTitle || item.activityId_dictText || ''
  132. }
  133. })
  134. } catch (err) {
  135. }
  136. },
  137. setRules() {
  138. const rules = {
  139. 'experienceId': {
  140. type: 'string',
  141. required: true,
  142. message: '请选择关联项目',
  143. },
  144. 'texts': {
  145. type: 'array',
  146. required: true,
  147. message: '请完整回答',
  148. validator: (rule, value, callback) => {
  149. if (value.every(val => !!val)) {
  150. return true
  151. }
  152. return false
  153. },
  154. },
  155. }
  156. this.$refs.form.setRules(rules)
  157. },
  158. async open() {
  159. await this.fetchProjectOptions()
  160. console.log('projects', this.projects)
  161. console.log('experienceQuestionList', this.configList.experienceQuestionList)
  162. const texts = this.configList.experienceQuestionList.map(() => '')
  163. this.form = {
  164. experienceId: null,
  165. // processScore: null,
  166. // spotScore: null,
  167. // teacherScore: null,
  168. images: [],
  169. texts,
  170. }
  171. this.$refs.popup.open()
  172. },
  173. close() {
  174. this.$refs.popup.close()
  175. },
  176. onPopupChange(e) {
  177. this.isShow = e.show
  178. // todo: need settimeout?
  179. setTimeout(() => {
  180. this.setRules()
  181. }, 800)
  182. },
  183. openRelatePojectPicker() {
  184. this.$refs.reloateProjectPopup.open(this.form.experienceId || null)
  185. },
  186. onRelateProjectChange(id) {
  187. this.form.experienceId = id
  188. },
  189. async onPublish() {
  190. try {
  191. await this.$refs.form.validate()
  192. const {
  193. experienceId,
  194. // processScore,
  195. // spotScore,
  196. // teacherScore,
  197. images,
  198. texts,
  199. } = this.form
  200. const params = {
  201. // todo: check
  202. userId: this.userInfo.id,
  203. experienceId,
  204. image: images.join(','),
  205. content: texts.join('\r\n')
  206. }
  207. await this.$fetch('addExperience', params)
  208. uni.showToast({
  209. icon: 'success',
  210. title: '发布成功',
  211. });
  212. this.$emit('submitted')
  213. this.close()
  214. } catch (err) {
  215. console.log('onSave err', err)
  216. }
  217. },
  218. },
  219. }
  220. </script>
  221. <style lang="scss" scoped>
  222. .popup__view {
  223. width: 100vw;
  224. display: flex;
  225. flex-direction: column;
  226. box-sizing: border-box;
  227. background: #FFFFFF;
  228. border-top-left-radius: 32rpx;
  229. border-top-right-radius: 32rpx;
  230. }
  231. .header {
  232. position: relative;
  233. width: 100%;
  234. padding: 24rpx 0;
  235. box-sizing: border-box;
  236. border-bottom: 2rpx solid #EEEEEE;
  237. .title {
  238. font-family: PingFang SC;
  239. font-weight: 500;
  240. font-size: 34rpx;
  241. line-height: 1.4;
  242. color: #181818;
  243. }
  244. .btn {
  245. font-family: PingFang SC;
  246. font-weight: 500;
  247. font-size: 32rpx;
  248. line-height: 1.4;
  249. color: #8B8B8B;
  250. position: absolute;
  251. top: 26rpx;
  252. left: 40rpx;
  253. }
  254. }
  255. .form {
  256. max-height: 75vh;
  257. padding: 32rpx 40rpx;
  258. box-sizing: border-box;
  259. overflow-y: auto;
  260. &-item {
  261. padding: 8rpx 0 6rpx 0;
  262. & + & {
  263. padding-top: 24rpx;
  264. border-top: 2rpx solid #EEEEEE;
  265. }
  266. &-label {
  267. margin-bottom: 14rpx;
  268. display: flex;
  269. align-items: center;
  270. font-family: PingFang SC;
  271. font-weight: 400;
  272. font-size: 26rpx;
  273. line-height: 1.4;
  274. color: #181818;
  275. .icon {
  276. margin-right: 8rpx;
  277. width: 16rpx;
  278. height: auto;
  279. }
  280. }
  281. &-content {
  282. .text {
  283. padding: 2rpx 0;
  284. font-family: PingFang SC;
  285. font-weight: 400;
  286. font-size: 32rpx;
  287. line-height: 1.4;
  288. &.placeholder {
  289. color: #C6C6C6;
  290. }
  291. }
  292. }
  293. }
  294. }
  295. .row {
  296. justify-content: space-between;
  297. .form-label {
  298. margin: 0;
  299. }
  300. }
  301. .footer {
  302. width: 100%;
  303. padding: 32rpx 40rpx;
  304. box-sizing: border-box;
  305. border-top: 2rpx solid #F1F1F1;
  306. .btn {
  307. width: 100%;
  308. padding: 14rpx 0;
  309. box-sizing: border-box;
  310. font-family: PingFang SC;
  311. font-weight: 500;
  312. font-size: 36rpx;
  313. line-height: 1.4;
  314. color: #FFFFFF;
  315. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  316. border: 2rpx solid #00A9FF;
  317. border-radius: 41rpx;
  318. }
  319. }
  320. </style>