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

374 lines
10 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. // todo: check
  157. // this.configList.experienceQuestionList.forEach((item, index) => {
  158. // rules[`texts[${index}]`] = {
  159. // type: 'string',
  160. // required: true,
  161. // message: `请回答“${item.question}”`,
  162. // }
  163. // })
  164. this.$refs.form.setRules(rules)
  165. },
  166. async open() {
  167. await this.fetchProjectOptions()
  168. console.log('projects', this.projects)
  169. console.log('experienceQuestionList', this.configList.experienceQuestionList)
  170. const texts = this.configList.experienceQuestionList.map(() => '')
  171. this.form = {
  172. experienceId: null,
  173. // processScore: null,
  174. // spotScore: null,
  175. // teacherScore: null,
  176. images: [],
  177. texts,
  178. }
  179. this.$refs.popup.open()
  180. },
  181. close() {
  182. this.$refs.popup.close()
  183. },
  184. onPopupChange(e) {
  185. this.isShow = e.show
  186. // todo: need settimeout?
  187. setTimeout(() => {
  188. this.setRules()
  189. }, 800)
  190. },
  191. openRelatePojectPicker() {
  192. this.$refs.reloateProjectPopup.open(this.form.experienceId || null)
  193. },
  194. onRelateProjectChange(id) {
  195. this.form.experienceId = id
  196. },
  197. async onPublish() {
  198. try {
  199. await this.$refs.form.validate()
  200. const {
  201. experienceId,
  202. // processScore,
  203. // spotScore,
  204. // teacherScore,
  205. images,
  206. texts,
  207. } = this.form
  208. const params = {
  209. // todo: check
  210. userId: this.userInfo.id,
  211. experienceId,
  212. // processScore,
  213. // spotScore,
  214. // teacherScore,
  215. image: images.join(','),
  216. content: texts.join('\r\n')
  217. }
  218. await this.$fetch('addExperience', params)
  219. uni.showToast({
  220. icon: 'success',
  221. title: '发布成功',
  222. });
  223. this.$emit('submitted')
  224. this.close()
  225. } catch (err) {
  226. console.log('onSave err', err)
  227. }
  228. },
  229. },
  230. }
  231. </script>
  232. <style lang="scss" scoped>
  233. .popup__view {
  234. width: 100vw;
  235. display: flex;
  236. flex-direction: column;
  237. box-sizing: border-box;
  238. background: #FFFFFF;
  239. border-top-left-radius: 32rpx;
  240. border-top-right-radius: 32rpx;
  241. }
  242. .header {
  243. position: relative;
  244. width: 100%;
  245. padding: 24rpx 0;
  246. box-sizing: border-box;
  247. border-bottom: 2rpx solid #EEEEEE;
  248. .title {
  249. font-family: PingFang SC;
  250. font-weight: 500;
  251. font-size: 34rpx;
  252. line-height: 1.4;
  253. color: #181818;
  254. }
  255. .btn {
  256. font-family: PingFang SC;
  257. font-weight: 500;
  258. font-size: 32rpx;
  259. line-height: 1.4;
  260. color: #8B8B8B;
  261. position: absolute;
  262. top: 26rpx;
  263. left: 40rpx;
  264. }
  265. }
  266. .form {
  267. max-height: 75vh;
  268. padding: 32rpx 40rpx;
  269. box-sizing: border-box;
  270. overflow-y: auto;
  271. &-item {
  272. padding: 8rpx 0 6rpx 0;
  273. & + & {
  274. padding-top: 24rpx;
  275. border-top: 2rpx solid #EEEEEE;
  276. }
  277. &-label {
  278. margin-bottom: 14rpx;
  279. display: flex;
  280. align-items: center;
  281. font-family: PingFang SC;
  282. font-weight: 400;
  283. font-size: 26rpx;
  284. line-height: 1.4;
  285. color: #181818;
  286. .icon {
  287. margin-right: 8rpx;
  288. width: 16rpx;
  289. height: auto;
  290. }
  291. }
  292. &-content {
  293. .text {
  294. padding: 2rpx 0;
  295. font-family: PingFang SC;
  296. font-weight: 400;
  297. font-size: 32rpx;
  298. line-height: 1.4;
  299. &.placeholder {
  300. color: #C6C6C6;
  301. }
  302. }
  303. }
  304. }
  305. }
  306. .row {
  307. justify-content: space-between;
  308. .form-label {
  309. margin: 0;
  310. }
  311. }
  312. .footer {
  313. width: 100%;
  314. padding: 32rpx 40rpx;
  315. box-sizing: border-box;
  316. border-top: 2rpx solid #F1F1F1;
  317. .btn {
  318. width: 100%;
  319. padding: 14rpx 0;
  320. box-sizing: border-box;
  321. font-family: PingFang SC;
  322. font-weight: 500;
  323. font-size: 36rpx;
  324. line-height: 1.4;
  325. color: #FFFFFF;
  326. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  327. border: 2rpx solid #00A9FF;
  328. border-radius: 41rpx;
  329. }
  330. }
  331. </style>