detail.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. <!--
  2. * @Author: wanglj wanglijie@dashoo.cn
  3. * @Date: 2025-03-29 14:15:09
  4. * @LastEditors: wanglj wanglijie@dashoo.cn
  5. * @LastEditTime: 2025-04-16 15:25:17
  6. * @FilePath: \labsop_h5\src\view\entry\detail.vue
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. -->
  9. <!--
  10. * @Author: wanglj wanglijie@dashoo.cn
  11. * @Date: 2025-03-24 09:17:15
  12. * @LastEditors: wanglj wanglijie@dashoo.cn
  13. * @LastEditTime: 2025-03-29 14:28:02
  14. * @FilePath: \labsop_h5\src\view\instr\detail.vue
  15. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  16. -->
  17. <template>
  18. <div class="instr-detail">
  19. <header class="flex">
  20. <div class="i-right ml10">
  21. <div class="h100 flex flex-top flex-column flex-between">
  22. <div class="flex flex-top mb4 ml2">
  23. <div class="detailTxt name">{{ `${state.form.memberName}的${state.form.platformName}入室申请` }}</div>
  24. </div>
  25. <footer>
  26. <div class="flex flex-top mt-auto">
  27. <img
  28. class="i-r-icon"
  29. src="../../assets/img/user.png"
  30. />
  31. <div class="detailTxt">{{ state.form.createdName }}</div>
  32. </div>
  33. <div class="flex flex-top">
  34. <div class="detailTxt">{{ state.form.createdTime }}</div>
  35. </div>
  36. </footer>
  37. </div>
  38. </div>
  39. </header>
  40. <div class="content">
  41. <div class="card">
  42. <h4>申请人信息</h4>
  43. <ul>
  44. <li>
  45. <div class="label">申请人姓名:</div>
  46. <div class="value">{{ state.form.memberName }}</div>
  47. </li>
  48. <li>
  49. <div class="label">人员类型:</div>
  50. <div class="value">{{ getDictLabel(userTypeList, state.form.memberType) }}</div>
  51. </li>
  52. <li>
  53. <div class="label">申请人手机号:</div>
  54. <div class="value">{{ state.form.memberPhone }}</div>
  55. </li>
  56. <li>
  57. <div class="label">申请人科室:</div>
  58. <div class="value">{{ state.form.deptName }}</div>
  59. </li>
  60. </ul>
  61. </div>
  62. <div class="card">
  63. <h4>申请人课题组信息</h4>
  64. <ul>
  65. <li>
  66. <div class="label">课题组:</div>
  67. <div class="value">{{ state.form.pgName }}</div>
  68. </li>
  69. <li>
  70. <div class="label">课题组负责人:</div>
  71. <div class="value">{{ state.form.mentorName }}</div>
  72. </li>
  73. <li>
  74. <div class="label">课题组负责人科室:</div>
  75. <div class="value">{{ state.form.mentorDeptName }}</div>
  76. </li>
  77. <li>
  78. <div class="label">课题组负责人电话:</div>
  79. <div class="value">{{ state.form.mentorPhone }}</div>
  80. </li>
  81. <li>
  82. <div class="label">课题组负责人电话:</div>
  83. <div class="value">{{ state.form.mentorPhone }}</div>
  84. </li>
  85. </ul>
  86. </div>
  87. <div class="card">
  88. <h4>申请入室平台</h4>
  89. <ul v-if="state.form.platPlatformAppointCellRes && state.form.platPlatformAppointCellRes.length">
  90. <li>
  91. <div class="label">申请平台:</div>
  92. <div class="value">{{ state.form.platPlatformAppointCellRes[0].platformName }}</div>
  93. </li>
  94. <li>
  95. <div class="label">申请时长:</div>
  96. <div class="value">{{ state.form.platPlatformAppointCellRes[0].platformTime }}个月</div>
  97. </li>
  98. <li>
  99. <div class="label">费用:</div>
  100. <div class="value">{{ state.form.platPlatformAppointCellRes[0].price }}元</div>
  101. </li>
  102. <li>
  103. <div class="label">拟培养细胞种类:</div>
  104. <div class="value">{{ state.form.cellType }}</div>
  105. </li>
  106. <li>
  107. <div class="label">细胞房预约需求:</div>
  108. <div class="value">{{ state.form.cellSourceType == '10' ? '普通' : '层流' }}</div>
  109. </li>
  110. </ul>
  111. <ul v-if="state.form.platPlatformAppointMolecularRes && state.form.platPlatformAppointMolecularRes.length">
  112. <li>
  113. <div class="label">申请平台:</div>
  114. <div class="value">{{ state.form.platPlatformAppointMolecularRes[0].platformName }}</div>
  115. </li>
  116. <li>
  117. <div class="label">申请时长:</div>
  118. <div class="value">{{ state.form.platPlatformAppointMolecularRes[0].platformTime }}个月</div>
  119. </li>
  120. <li>
  121. <div class="label">费用:</div>
  122. <div class="value">{{ state.form.platPlatformAppointMolecularRes[0].price }}元</div>
  123. </li>
  124. <li>
  125. <div class="label">其他需求:</div>
  126. <div class="value">{{ state.form.platOtherNeed }}</div>
  127. </li>
  128. </ul>
  129. </div>
  130. <div class="card">
  131. <h4>审批记录</h4>
  132. <FlowTable
  133. :id="state.form.id"
  134. :businessCode="`${route.query.id}`"
  135. defCode="plat_platform_appoint"
  136. />
  137. </div>
  138. <div
  139. class="card"
  140. v-if="state.form.appointStatus === '20'"
  141. >
  142. <van-form
  143. class="mb20"
  144. ref="formRef"
  145. required="auto"
  146. >
  147. <van-cell-group>
  148. <van-field
  149. v-model="state.auditForm.approveDesc"
  150. label="审批意见"
  151. placeholder="请输入审批意见"
  152. :rules="[{ required: true, message: '请输入审批意见' }]"
  153. rows="3"
  154. type="textarea"
  155. />
  156. <van-field
  157. v-model="state.auditForm.approveResult"
  158. label="审批结果"
  159. placeholder="请选择审批结果"
  160. :rules="[{ required: true, message: '请选择审批结果' }]"
  161. >
  162. <template #input>
  163. <van-radio-group
  164. v-model="state.auditForm.approveResult"
  165. direction="horizontal"
  166. >
  167. <van-radio name="10">通过</van-radio>
  168. <van-radio name="20">不通过</van-radio>
  169. </van-radio-group>
  170. </template>
  171. </van-field>
  172. </van-cell-group>
  173. </van-form>
  174. </div>
  175. </div>
  176. <van-action-bar
  177. v-if="state.form.appointStatus === '10'"
  178. placeholder
  179. >
  180. <van-action-bar-icon
  181. icon="wap-home-o"
  182. text="首页"
  183. @click="onRouterPush('/home')"
  184. />
  185. <van-action-bar-icon
  186. icon="send-gift-o"
  187. text="入室申请"
  188. @click="onRouterPush('/entry')"
  189. />
  190. <van-action-bar-button
  191. class="w100"
  192. type="primary"
  193. text="上传缴费单"
  194. @click="openUpload()"
  195. />
  196. </van-action-bar>
  197. <van-action-bar
  198. v-else-if="state.form.appointStatus === '20'"
  199. placeholder
  200. >
  201. <van-action-bar-icon
  202. icon="wap-home-o"
  203. text="首页"
  204. @click="onRouterPush('/home')"
  205. />
  206. <van-action-bar-icon
  207. icon="send-gift-o"
  208. text="入室申请"
  209. @click="onRouterPush('/entry')"
  210. />
  211. <van-action-bar-button
  212. class="w100"
  213. type="primary"
  214. text="审核"
  215. @click="onAudit()"
  216. />
  217. </van-action-bar>
  218. </div>
  219. <van-popup
  220. v-model:show="showBottom"
  221. round
  222. closeable
  223. position="bottom"
  224. :style="{ height: '90vh' }"
  225. >
  226. <template #default>
  227. <div class="upload-container">
  228. <div class="content">
  229. <h4>上传须知</h4>
  230. <p>
  231. 根据遵义医科大学附属医院临床医学公共实验中心要求,申请入室人员需要上传缴费单:通过审批后,点击缴费单按钮下载缴费单模板,打印并带到财务处完成缴费和确认后经财务盖章,将盖章的单据拍照上传。
  232. </p>
  233. <h4>信息上传</h4>
  234. <div class="file-card">
  235. <h4>缴费单</h4>
  236. <van-uploader
  237. v-model="state.uploadForm.billList"
  238. :after-read="afterRead"
  239. preview-size="60"
  240. :preview-full-image="false"
  241. :max-count="1"
  242. />
  243. </div>
  244. <div class="file-card">
  245. <h4>其它附件</h4>
  246. <van-uploader
  247. v-model="state.uploadForm.fileList"
  248. :after-read="afterRead"
  249. preview-size="60"
  250. :preview-full-image="false"
  251. multiple
  252. />
  253. </div>
  254. </div>
  255. <footer>
  256. <van-button
  257. type="primary"
  258. class="w100"
  259. round
  260. @click="onSubmit"
  261. >
  262. 提交
  263. </van-button>
  264. </footer>
  265. </div>
  266. </template>
  267. </van-popup>
  268. </template>
  269. <script lang="ts" setup>
  270. import to from 'await-to-js'
  271. import { useRoute, useRouter } from 'vue-router'
  272. import { defineAsyncComponent, onMounted, reactive, ref } from 'vue'
  273. import { useTrainingApi } from '/@/api/training'
  274. import { useDictApi } from '/@/api/system/dict'
  275. import { getDictLabel } from '/@/utils/other'
  276. import { usePlatformAppointApi } from '/@/api/platform/appoint'
  277. import { useFlowApi } from '/@/api/execution/flow'
  278. import { handleUpload } from '/@/utils/upload'
  279. import { showNotify } from 'vant'
  280. const FlowTable = defineAsyncComponent(() => import('/@/components/FlowTable.vue'))
  281. const platformAppointApi = usePlatformAppointApi()
  282. const flowApi = useFlowApi()
  283. const route = useRoute()
  284. const router = useRouter()
  285. const dictApi = useDictApi()
  286. const apprList = ref<any[]>([])
  287. const userTypeList = ref(<RowDicDataType[]>[])
  288. const showBottom = ref(false)
  289. const formRef = ref()
  290. const state = reactive({
  291. detailsLoading: false,
  292. form: {
  293. id: 0,
  294. deptId: null,
  295. deptName: '',
  296. isTemporary: '10',
  297. memberId: 0,
  298. memberName: '',
  299. memberPhone: '',
  300. memberType: '',
  301. mentorObj: null,
  302. pgId: null,
  303. pgName: '',
  304. mentorId: null,
  305. mentorName: '',
  306. mentorDeptName: '',
  307. mentorPhone: '',
  308. platformId: null,
  309. platformName: '',
  310. platformTime: null,
  311. platformType: '',
  312. platformList: [] as any[],
  313. isCellChecked: '20',
  314. cellType: '',
  315. cellSourceType: '',
  316. isMolecularChecked: '20',
  317. molecularTime: null,
  318. platOtherNeed: '',
  319. createdName: '',
  320. createdTime: '',
  321. approveStatus: '',
  322. appointStatus: '',
  323. platPlatformAppointCellRes: [],
  324. platPlatformAppointMolecularRes: [],
  325. },
  326. uploadForm: {
  327. billList: [] as any[],
  328. fileList: [] as any[],
  329. },
  330. auditForm: {
  331. id: 0,
  332. approveResult: '',
  333. approveDesc: '',
  334. },
  335. queryParams: {
  336. pageNum: 1,
  337. pageSize: 10,
  338. appointStatus: [],
  339. },
  340. })
  341. const getDicts = () => {
  342. Promise.all([dictApi.getDictDataByType('sys_user_type')]).then(([type]) => {
  343. userTypeList.value = type?.data?.values || []
  344. })
  345. }
  346. //详情
  347. const getDetail = async (id: number) => {
  348. const { entryType, platformType } = route.query
  349. state.detailsLoading = true
  350. let request = platformAppointApi.getDetail
  351. if (entryType === 'manageGetDetail' || entryType === 'audit') {
  352. request = platformType === '10' ? platformAppointApi.getCellEntityById : platformAppointApi.getMolecularEntityById
  353. }
  354. const [err, res]: ToResponse = await to(request({ id }))
  355. state.detailsLoading = false
  356. if (err) return
  357. if (res?.code === 200) {
  358. state.form = res.data
  359. }
  360. }
  361. const onRouterPush = (val: string) => {
  362. router.push(val)
  363. }
  364. const openUpload = () => {
  365. showBottom.value = true
  366. }
  367. const afterRead = async (files: any) => {
  368. if (files.length) {
  369. for (const file of files) {
  370. file.status = 'uploading'
  371. const [err, res]: ToResponse = await to(handleUpload(file.file))
  372. if (err) {
  373. file.status = 'failed'
  374. return
  375. }
  376. file.status = 'success'
  377. file.url = res
  378. file.name = file.file.name
  379. }
  380. } else {
  381. const file = files
  382. file.status = 'uploading'
  383. const [err, res]: ToResponse = await to(handleUpload(file.file))
  384. if (err) {
  385. file.status = 'failed'
  386. return
  387. }
  388. file.status = 'success'
  389. file.url = res
  390. file.name = file.file.name
  391. }
  392. }
  393. // 提交
  394. const onSubmit = async () => {
  395. const params = JSON.parse(JSON.stringify(state.uploadForm))
  396. params.fileList = [...params.billList, ...params.fileList].map((item) => {
  397. return {
  398. fileName: item.name,
  399. fileUrl: item.url,
  400. }
  401. })
  402. delete params.billList
  403. params.appointId = state.form.id
  404. const [err]: ToResponse = await to(platformAppointApi.createFile(params))
  405. if (err) return
  406. showNotify({
  407. type: 'success',
  408. message: '操作成功',
  409. })
  410. router.push({
  411. path: '/entry',
  412. })
  413. }
  414. const onAudit = async () => {
  415. const [errValid] = await to(formRef.value.validate())
  416. if (errValid) return
  417. const params = JSON.parse(JSON.stringify(state.auditForm))
  418. params.id = state.form.id
  419. const [err]: ToResponse = await to(platformAppointApi.updateById(params))
  420. if (err) return
  421. showNotify({
  422. type: 'success',
  423. message: '操作成功',
  424. })
  425. router.push({
  426. path: '/entry',
  427. })
  428. }
  429. onMounted(() => {
  430. const id = route.query.id ? +route.query.id : 0
  431. getDicts()
  432. getDetail(id)
  433. })
  434. </script>
  435. <style lang="scss" scoped>
  436. .instr-detail {
  437. flex: 1;
  438. overflow-y: auto;
  439. background-color: #f7f8fa;
  440. .my-swipe {
  441. background-color: #fff;
  442. height: 30px !important;
  443. line-height: 30px !important;
  444. :deep(.flex) {
  445. height: 30px;
  446. overflow: hidden;
  447. padding: 0 12px;
  448. span {
  449. display: inline-block;
  450. height: 30px;
  451. line-height: 30px;
  452. }
  453. span:first-child {
  454. flex: 1;
  455. white-space: nowrap;
  456. overflow: hidden;
  457. text-overflow: ellipsis;
  458. }
  459. }
  460. }
  461. > header {
  462. height: 40px;
  463. background-color: #fff;
  464. padding: 12px;
  465. }
  466. .inst-info {
  467. display: flex;
  468. }
  469. .i-right {
  470. flex: 1;
  471. font-size: 14px;
  472. height: 40px;
  473. .i-r-icon {
  474. width: 15px;
  475. height: 15px;
  476. margin-right: 10px;
  477. }
  478. footer {
  479. width: 100%;
  480. display: flex;
  481. align-items: center;
  482. justify-content: space-between;
  483. }
  484. }
  485. .detailTxt {
  486. font-size: 12px;
  487. color: #333333;
  488. white-space: nowrap;
  489. overflow: hidden;
  490. text-overflow: ellipsis;
  491. &.name {
  492. font-weight: bold;
  493. font-size: 16px;
  494. }
  495. }
  496. .content {
  497. padding: 10px;
  498. }
  499. .card {
  500. border-radius: 4px;
  501. background-color: #fff;
  502. padding: 10px;
  503. box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  504. & + .card {
  505. margin-top: 10px;
  506. }
  507. h4 {
  508. height: 18px;
  509. line-height: 18px;
  510. display: flex;
  511. margin-bottom: 10px;
  512. span {
  513. font-weight: normal;
  514. margin-left: auto;
  515. }
  516. &::before {
  517. display: inline-block;
  518. content: '';
  519. width: 3px;
  520. height: 18px;
  521. background-color: #1c9bfd;
  522. margin-right: 4px;
  523. vertical-align: middle;
  524. }
  525. }
  526. > ul {
  527. li {
  528. /* display: flex;
  529. align-items: center; */
  530. padding: 6px 0;
  531. .label {
  532. color: #969799;
  533. margin-bottom: 8px;
  534. }
  535. label {
  536. width: 100px;
  537. min-width: 80px;
  538. color: #969799;
  539. }
  540. span {
  541. word-break: break-all;
  542. }
  543. }
  544. }
  545. .text {
  546. white-space: pre-wrap;
  547. }
  548. }
  549. .van-list {
  550. padding: 10px;
  551. border-radius: 4px;
  552. flex: 1;
  553. .van-cell {
  554. background-color: #fff;
  555. + .van-cell {
  556. margin-top: 10px;
  557. }
  558. header,
  559. footer {
  560. color: #333;
  561. }
  562. .title {
  563. flex: 1;
  564. white-space: nowrap;
  565. overflow: hidden;
  566. text-overflow: ellipsis;
  567. text-align: left;
  568. }
  569. .inst-title {
  570. color: #333;
  571. text-align: left;
  572. flex: 1;
  573. overflow: hidden;
  574. white-space: nowrap;
  575. text-overflow: ellipsis;
  576. margin-top: 4px;
  577. span:first-child {
  578. display: inline-block;
  579. width: 80px;
  580. min-width: 80px;
  581. color: rgb(120, 120, 120);
  582. }
  583. }
  584. .time {
  585. color: #f69a4d;
  586. }
  587. }
  588. }
  589. }
  590. .btns {
  591. flex: 1;
  592. display: flex;
  593. li {
  594. display: flex;
  595. flex-direction: column;
  596. align-items: center;
  597. justify-content: center;
  598. padding: 0 8px;
  599. font-size: 12px;
  600. i {
  601. margin-bottom: 4px;
  602. }
  603. }
  604. }
  605. :deep(.follow .van-icon) {
  606. color: #fdc33e;
  607. }
  608. .need-to-know {
  609. height: calc(100% - 20px);
  610. overflow: hidden;
  611. display: flex;
  612. flex-direction: column;
  613. padding: 10px 20px;
  614. white-space: pre-wrap;
  615. p {
  616. flex: 1;
  617. overflow-y: auto;
  618. }
  619. footer {
  620. flex: 0 0 45px;
  621. margin-top: 4px;
  622. border-top: 1px solid #f7f8fa;
  623. }
  624. }
  625. .upload-container {
  626. height: calc(100% - 40px);
  627. padding: 20px 10px;
  628. display: flex;
  629. flex-direction: column;
  630. justify-content: space-between;
  631. .content {
  632. flex: 1;
  633. overflow-y: auto;
  634. > h4 {
  635. height: 18px;
  636. line-height: 18px;
  637. margin: 10px 0;
  638. display: flex;
  639. align-items: center;
  640. &::before {
  641. display: inline-block;
  642. content: '';
  643. width: 3px;
  644. height: 18px;
  645. background-color: #1c9bfd;
  646. margin-right: 4px;
  647. vertical-align: middle;
  648. }
  649. }
  650. .file-card {
  651. background: #fff;
  652. h4 {
  653. margin-bottom: 4px;
  654. }
  655. }
  656. }
  657. }
  658. </style>