add.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <!--
  2. * @Author: wanglj wanglijie@dashoo.cn
  3. * @Date: 2025-03-18 20:02:42
  4. * @LastEditors: wanglj wanglijie@dashoo.cn
  5. * @LastEditTime: 2025-03-21 09:46:32
  6. * @FilePath: \labsop_h5\src\view\training\enroll.vue
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. -->
  9. <template>
  10. <div class="app-container">
  11. <van-form
  12. ref="formRef"
  13. @submit="onSubmit('20')"
  14. class="mt10"
  15. required="auto"
  16. >
  17. <!-- <h4 class="mb8 mt8">申请人课题组信息</h4>
  18. <van-cell-group>
  19. <van-field v-model="state.form.pgName" label="课题组" placeholder="课题组" readonly :rules="[{ required: true }]" />
  20. <van-field v-model="state.form.mentorName" label="课题组负责人" placeholder="课题组负责人" readonly />
  21. <van-field v-model="state.form.mentorDeptName" label="课题组负责人科室" placeholder="课题组负责人科室" readonly />
  22. <van-field v-model="state.form.mentorPhone" label="课题组负责人电话" placeholder="课题组负责人电话" readonly />
  23. </van-cell-group> -->
  24. <h4 class="mb8 mt8">申请入室平台</h4>
  25. <van-cell-group
  26. v-for="(item, index) in state.form.platformList"
  27. :key="item.id"
  28. >
  29. <van-field
  30. label="申请平台"
  31. placeholder="申请平台"
  32. >
  33. <template #input>
  34. <van-radio-group v-model="selectPlatform">
  35. <van-radio :name="item.platformType">{{ item.platformName }}</van-radio>
  36. </van-radio-group>
  37. </template>
  38. </van-field>
  39. <van-field
  40. v-model.number="item.platformTime"
  41. label="申请时长(月)"
  42. placeholder="申请时长(月)"
  43. type="number"
  44. :disabled="!item.isChecked"
  45. >
  46. <template #extra>个月</template>
  47. </van-field>
  48. <template v-if="item.platformType == '10'">
  49. <!-- <van-field-->
  50. <!-- v-model="item.cellType"-->
  51. <!-- label="拟培养细胞种类"-->
  52. <!-- placeholder="拟培养细胞种类"-->
  53. <!-- :rules="[{ required: item.isChecked, message: '请输入拟培养细胞种类' }]"-->
  54. <!-- :disabled="!item.isChecked"-->
  55. <!-- />-->
  56. <van-field
  57. v-model="item.cellType"
  58. label="细胞房预约需求"
  59. placeholder="细胞房预约需求"
  60. >
  61. <template #input>
  62. <van-radio-group
  63. v-model="item.cellSourceType"
  64. :disabled="!item.isChecked"
  65. direction="horizontal"
  66. >
  67. <van-radio
  68. v-for="o in item.platformResourceType"
  69. :name="o.dictValue"
  70. >
  71. {{ o.dictLabel }}
  72. </van-radio>
  73. </van-radio-group>
  74. </template>
  75. </van-field>
  76. </template>
  77. <template v-else-if="item.platformType == '20'">
  78. <van-field
  79. v-model="item.platOtherNeed"
  80. label="其他需求"
  81. placeholder="其他需求"
  82. :disabled="!item.isChecked"
  83. />
  84. </template>
  85. </van-cell-group>
  86. <h4 class="mb8 mt8">申请人信息</h4>
  87. <van-cell-group>
  88. <van-field
  89. v-model="state.form.memberName"
  90. label="申请人姓名"
  91. placeholder="申请人姓名"
  92. readonly
  93. :rules="[{ required: true }]"
  94. />
  95. <van-field
  96. v-model="state.form.memberType"
  97. label="人员类型"
  98. placeholder="人员类型"
  99. readonly
  100. :rules="[{ required: true }]"
  101. >
  102. <template #input>{{ getDictLabel(userTypeList, state.form.memberType) }}</template>
  103. </van-field>
  104. <van-field
  105. v-model="state.form.memberPhone"
  106. label="申请人手机号"
  107. placeholder="申请人手机号"
  108. readonly
  109. />
  110. <van-field
  111. v-model="state.form.deptName"
  112. label="申请人科室"
  113. placeholder="申请人科室"
  114. readonly
  115. />
  116. <van-field
  117. v-model="state.form.pgName"
  118. label="课题组"
  119. placeholder="课题组"
  120. readonly
  121. :rules="[{ required: true }]"
  122. />
  123. </van-cell-group>
  124. <h4 class="mb8 mt8">安全承诺</h4>
  125. <van-checkbox v-model="state.safePromise">
  126. 本人承诺:如时候遵守实验室及个平台的各项规章制度,遵守在室的积分管理制度。
  127. </van-checkbox>
  128. <!-- <h4 class="mb8 mt8">安全承诺</h4>
  129. <van-checkbox v-model="state.safePromise">本人承诺:如时候遵守实验室及个平台的各项规章制度,遵守在室的积分管理制度。</van-checkbox>
  130. <van-checkbox v-model="state.safeRead" :disabled="!state.isRead">
  131. 我已完整阅读并同意
  132. <a href="javascript:void(0);" @click.stop="onRead">《预约须知》</a>
  133. 的内容,承诺如时候遵守实验室及个平台的各项规章制度,遵守在室的积分管理制度。
  134. </van-checkbox>
  135. <div style="margin: 16px">
  136. <van-button round block type="primary" native-type="submit"> 提交 </van-button>
  137. </div> -->
  138. <van-action-bar placeholder>
  139. <van-action-bar-icon
  140. icon="wap-home-o"
  141. text="首页"
  142. @click="router.push('/home')"
  143. />
  144. <van-action-bar-icon
  145. icon="revoke"
  146. text="返回"
  147. @click="router.push('/entry')"
  148. />
  149. <!-- <van-action-bar-icon
  150. :icon="state.instDetail.following ? 'star' : 'star-o'"
  151. :class="{ follow: state.instDetail.following }"
  152. :text="state.instDetail.following ? '取消收藏' : '收藏'"
  153. @click="handleFollowInst"
  154. /> -->
  155. <van-action-bar-button
  156. type="primary"
  157. text="立即预约"
  158. native-type="submit"
  159. :loading="submitting"
  160. />
  161. </van-action-bar>
  162. </van-form>
  163. </div>
  164. <!-- 申请须知弹窗已移动到跳转页面 -->
  165. </template>
  166. <script name="home" lang="ts" setup>
  167. import to from 'await-to-js'
  168. import { onMounted, reactive, ref, watch } from 'vue'
  169. import { useRouter, useRoute } from 'vue-router'
  170. import { useDictApi } from '/@/api/system/dict'
  171. import { formatDate } from '/@/utils/formatTime'
  172. import { storeToRefs } from 'pinia'
  173. import { useUserInfo } from '/@/stores/userInfo'
  174. import { getDictLabel } from '/@/utils/other'
  175. import { useTrainingApi } from '/@/api/training'
  176. import { usePlatformApi } from '/@/api/platform/home'
  177. import { usePlatformAppointApi } from '/@/api/platform/appoint'
  178. import { showDialog, showNotify } from 'vant'
  179. const platformAppointApi = usePlatformAppointApi()
  180. const trainingApi = useTrainingApi()
  181. const storesUseUserInfo = useUserInfo()
  182. const { userInfos, openId } = storeToRefs(storesUseUserInfo)
  183. const router = useRouter()
  184. const route = useRoute()
  185. const formRef = ref()
  186. const showPicker = ref(false)
  187. const dictApi = useDictApi()
  188. const userTypeList = ref(<RowDicDataType[]>[])
  189. const platformApi = usePlatformApi()
  190. const platformList = ref()
  191. // confirmDisabled变量已移除,申请须知已移动到跳转页面
  192. const state = reactive({
  193. safePromise: false,
  194. form: {
  195. id: 0,
  196. deptId: null,
  197. deptName: '',
  198. isTemporary: '10',
  199. memberId: 0,
  200. memberName: '',
  201. memberPhone: '',
  202. memberType: '',
  203. mentorObj: null,
  204. pgId: null,
  205. pgName: '',
  206. mentorId: null,
  207. mentorName: '',
  208. mentorDeptName: '',
  209. mentorPhone: '',
  210. platformId: null,
  211. platformName: '',
  212. platformTime: null,
  213. platformType: '',
  214. platformList: [] as any[],
  215. isCellChecked: '20',
  216. cellType: '',
  217. cellSourceType: '',
  218. isMolecularChecked: '20',
  219. molecularTime: null,
  220. platOtherNeed: '',
  221. }
  222. })
  223. const selectPlatform = ref('') // 10: 细胞 20: 分子
  224. const debounceTimer = ref(0) // 防抖计时器
  225. const submitting = ref<boolean>(false) // 提交状态,用于控制按钮加载状态
  226. watch(selectPlatform, (newVal) => {
  227. if (newVal === '10' || newVal === '20') {
  228. state.form.platformList = state.form.platformList.map((item) => ({
  229. ...item,
  230. isChecked: item.platformType === newVal,
  231. }))
  232. }
  233. })
  234. // confirmAppoint方法已移除,申请须知已移动到跳转页面
  235. const getDicts = async () => {
  236. await Promise.all([
  237. dictApi.getDictDataByType('sys_user_type'),
  238. platformApi.getAllPlatformList({ noPage: true }),
  239. ]).then(([type, plat]) => {
  240. userTypeList.value = type.data.values || []
  241. const platList = plat?.data?.list || []
  242. const moleculesAndCells = platList.filter((item) => item.platformType === '10' || item.platformType === '20')
  243. platformList.value = moleculesAndCells.map((item: any) => {
  244. const options = JSON.parse(item.platformResourceType)
  245. return {
  246. isChecked: false,
  247. platformId: item.id,
  248. platformName: item.platformName,
  249. platformType: item.platformType,
  250. platformTime: null,
  251. cellType: '',
  252. cellSourceType: options?.length ? options[0].dictValue : '',
  253. platOtherNeed: '',
  254. platformResourceType: options,
  255. platformDesc: item.platformDesc,
  256. }
  257. })
  258. })
  259. }
  260. const initForm = async () => {
  261. state.form = {
  262. id: 0,
  263. deptId: null,
  264. deptName: '',
  265. isTemporary: '10',
  266. memberId: 0,
  267. memberName: '',
  268. memberPhone: '',
  269. memberType: '',
  270. mentorObj: null,
  271. pgId: null,
  272. pgName: '',
  273. mentorId: null,
  274. mentorName: '',
  275. mentorDeptName: '',
  276. mentorPhone: '',
  277. platformId: null,
  278. platformName: '',
  279. platformTime: null,
  280. platformType: '',
  281. platformList: [...platformList.value],
  282. isCellChecked: '20',
  283. cellType: '',
  284. cellSourceType: '',
  285. isMolecularChecked: '20',
  286. molecularTime: null,
  287. platOtherNeed: '',
  288. }
  289. state.form.memberId = userInfos.value.id
  290. state.form.memberName = userInfos.value.nickName
  291. state.form.memberType = userInfos.value.userType
  292. state.form.memberPhone = userInfos.value.phone
  293. state.form.deptId = userInfos.value.deptId
  294. state.form.deptName = userInfos.value.deptName
  295. const { projectGroup } = userInfos.value
  296. if (projectGroup) {
  297. state.form.pgId = projectGroup.id
  298. state.form.pgName = projectGroup.pgName
  299. state.form.mentorId = projectGroup.pgLeaderId
  300. state.form.mentorName = projectGroup.pgLeaderName
  301. state.form.mentorDeptName = projectGroup.pgOrgPaths
  302. state.form.mentorPhone = projectGroup.pgLeaderContact
  303. } else {
  304. showNotify({
  305. message: '当前用户未加入课题组,无法发起入室申请',
  306. type: 'danger',
  307. })
  308. }
  309. }
  310. // onRead方法已移除,申请须知已移动到跳转页面
  311. const onSubmit = async (type: string) => {
  312. // 设置提交状态为加载中
  313. submitting.value = true
  314. // 防抖处理:如果已有计时器,清除并重新设置
  315. if (debounceTimer.value) {
  316. clearTimeout(debounceTimer.value)
  317. }
  318. // 设置新的防抖计时器,500ms后执行
  319. debounceTimer.value = setTimeout(async () => {
  320. if (!state.safePromise) {
  321. showNotify({
  322. type: 'warning',
  323. message: '请阅读并勾选安全承诺!',
  324. })
  325. debounceTimer.value = 0
  326. submitting.value = false // 重置提交状态
  327. return
  328. }
  329. const [errValid] = await to(formRef.value.validate())
  330. if (errValid) {
  331. debounceTimer.value = 0
  332. submitting.value = false // 重置提交状态
  333. return
  334. }
  335. const params = JSON.parse(JSON.stringify(state.form))
  336. params.isTemporary = type
  337. const arr = params.platformList.filter((item: any) => item.isChecked)
  338. params.platformList = arr.map((item: any) => {
  339. return {
  340. ...item,
  341. isChecked: '10',
  342. }
  343. })
  344. if (!params.platformList.length) {
  345. showNotify({
  346. type: 'warning',
  347. message: '请选择平台',
  348. })
  349. debounceTimer.value = 0
  350. submitting.value = false // 重置提交状态
  351. return
  352. }
  353. for (const item of params.platformList) {
  354. if (!item.platformTime) {
  355. showNotify({
  356. type: 'warning',
  357. message: '请选择平台预约时间',
  358. })
  359. debounceTimer.value = 0
  360. submitting.value = false // 重置提交状态
  361. return
  362. }
  363. }
  364. // 直接提交申请,不再显示申请须知(已在跳转页面显示)
  365. const [err]: ToResponse = await to(platformAppointApi.create(params))
  366. if (err) {
  367. debounceTimer.value = 0
  368. submitting.value = false // 重置提交状态
  369. return
  370. }
  371. showNotify({
  372. type: 'success',
  373. message: '入室申请创建成功'
  374. })
  375. // 接口成功后继续保持加载状态2秒,提供更好的用户体验
  376. setTimeout(() => {
  377. debounceTimer.value = 0
  378. router.push('/entry')
  379. submitting.value = false // 5秒后重置提交状态
  380. }, 5000)
  381. }, 500)
  382. }
  383. onMounted(async () => {
  384. await getDicts()
  385. initForm()
  386. })
  387. </script>
  388. <style lang="scss" scoped>
  389. .app-container {
  390. header {
  391. padding: 14px;
  392. background-color: #f9ffff;
  393. border-radius: 4px;
  394. margin-top: 10px;
  395. }
  396. h4 {
  397. margin: 10px 0;
  398. height: 20px;
  399. line-height: 20px;
  400. &::before {
  401. display: inline-block;
  402. content: '';
  403. background-color: #3c78e3;
  404. width: 4px;
  405. height: 20px;
  406. margin-right: 4px;
  407. vertical-align: top;
  408. }
  409. }
  410. :deep(.flow-cell) {
  411. margin-left: 22px;
  412. display: flex;
  413. justify-content: space-between;
  414. color: #333;
  415. }
  416. }
  417. /* 申请须知相关样式已移除,申请须知已移动到跳转页面 */
  418. </style>