addEdit.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. <template>
  2. <div class="application-dialog-container">
  3. <el-dialog
  4. :title="state.dialog.title"
  5. @close="onCancel"
  6. :close-on-click-modal="false"
  7. v-model="state.dialog.isShowDialog"
  8. width="90%"
  9. >
  10. <el-form
  11. ref="expertDialogFormRef"
  12. :model="state.form"
  13. :rules="rules"
  14. size="default"
  15. label-width="140px"
  16. label-position="top"
  17. >
  18. <h4 class="mb20 mt8">申请人信息</h4>
  19. <el-row class="mb20">
  20. <el-col :span="24">
  21. <el-form-item
  22. label="申请人姓名"
  23. prop="userName"
  24. >
  25. <el-input
  26. v-model="state.form.userName"
  27. disabled
  28. />
  29. </el-form-item>
  30. </el-col>
  31. </el-row>
  32. <el-row>
  33. <el-col :span="24">
  34. <el-form-item
  35. label="联系电话"
  36. prop="phone"
  37. >
  38. <el-input
  39. v-model="state.form.phone"
  40. disabled
  41. />
  42. </el-form-item>
  43. </el-col>
  44. </el-row>
  45. <el-row>
  46. <el-col :span="24">
  47. <el-form-item
  48. label="课题组"
  49. prop="projectGroupId"
  50. >
  51. <el-input
  52. v-model="state.form.projectGroupName"
  53. disabled
  54. placeholder="请选择"
  55. class="w100"
  56. ></el-input>
  57. </el-form-item>
  58. </el-col>
  59. </el-row>
  60. <el-row>
  61. <el-col :span="24">
  62. <el-form-item
  63. label="科室"
  64. prop="deptId"
  65. >
  66. <el-input
  67. v-model="state.form.deptName"
  68. disabled
  69. />
  70. </el-form-item>
  71. </el-col>
  72. </el-row>
  73. <h4 class="mb20 mt20">转出情况</h4>
  74. <el-row
  75. class="mb20"
  76. >
  77. <el-col :span="24">
  78. <el-form-item
  79. label="品种品系"
  80. prop="categoryId"
  81. >
  82. <el-select
  83. v-model="state.form.categoryId"
  84. placeholder="请选择"
  85. :disabled="state.dialog.type === 'detail'"
  86. >
  87. <el-option
  88. v-for="item in animalTypeList"
  89. :key="item.id"
  90. :label="item.name"
  91. :value="item.id"
  92. />
  93. </el-select>
  94. </el-form-item>
  95. </el-col>
  96. </el-row>
  97. <el-row>
  98. <el-col :span="24">
  99. <el-form-item
  100. label="转出日期"
  101. prop="takeawayDate"
  102. >
  103. <el-date-picker
  104. v-model="state.form.takeawayDate"
  105. type="date"
  106. placeholder="请选择时间"
  107. clearable
  108. value-format="YYYY-MM-DD"
  109. style="width: 100%"
  110. :disabled="state.dialog.type === 'detail'"
  111. />
  112. </el-form-item>
  113. </el-col>
  114. </el-row>
  115. <el-row
  116. class="mb20"
  117. >
  118. <el-col :span="24">
  119. <el-form-item
  120. label="送往地点"
  121. prop="takeawayAddress"
  122. >
  123. <el-input
  124. v-model="state.form.takeawayAddress"
  125. placeholder="请输入"
  126. :disabled="state.dialog.type === 'detail'"
  127. />
  128. </el-form-item>
  129. </el-col>
  130. </el-row>
  131. <el-row>
  132. <el-col :span="24">
  133. <el-form-item
  134. label="带出原因"
  135. prop="takeawayReason"
  136. >
  137. <el-input
  138. v-model="state.form.takeawayReason"
  139. placeholder="请输入"
  140. :disabled="state.dialog.type === 'detail'"
  141. />
  142. </el-form-item>
  143. </el-col>
  144. </el-row>
  145. <el-row
  146. class="mb20"
  147. >
  148. <el-col :span="24">
  149. <el-form-item
  150. label="运输方式"
  151. prop="takeawayTransport"
  152. >
  153. <el-input
  154. v-model="state.form.takeawayTransport"
  155. placeholder="请输入"
  156. :disabled="state.dialog.type === 'detail'"
  157. />
  158. </el-form-item>
  159. </el-col>
  160. </el-row>
  161. <el-row>
  162. <el-col :span="24">
  163. <el-form-item
  164. label="门禁卡序列号"
  165. prop="accessCardNumber"
  166. >
  167. <el-input
  168. v-model="state.form.accessCardNumber"
  169. placeholder="请输入"
  170. :disabled="state.dialog.type === 'detail'"
  171. />
  172. </el-form-item>
  173. </el-col>
  174. </el-row>
  175. <el-row
  176. class="mt10"
  177. >
  178. <el-col :span="24">
  179. <el-form-item
  180. label="雄性"
  181. prop="takeawayMaleNumber"
  182. >
  183. <el-input-number
  184. style="width: 100%"
  185. placeholder="雄性数量"
  186. v-model="state.form.takeawayMaleNumber"
  187. :min="0"
  188. :disabled="state.dialog.type === 'detail'"
  189. />
  190. </el-form-item>
  191. </el-col>
  192. </el-row>
  193. <el-row>
  194. <el-col :span="24">
  195. <el-form-item
  196. label="雌性"
  197. prop="takewayFemaleNumber"
  198. >
  199. <el-input-number
  200. style="width: 100%"
  201. placeholder="雌性数量"
  202. v-model="state.form.takewayFemaleNumber"
  203. :min="0"
  204. :disabled="state.dialog.type === 'detail'"
  205. />
  206. </el-form-item>
  207. </el-col>
  208. </el-row>
  209. <el-row
  210. class="mt10"
  211. >
  212. <el-col
  213. :span="24"
  214. class="mt30 mb30"
  215. >
  216. <el-checkbox
  217. v-model="safePromiseStatus"
  218. :disabled="state.dialog.type === 'detail'"
  219. >
  220. 本人已阅读并同意
  221. <el-link
  222. type="primary"
  223. :underline="false"
  224. @click="handleReadNotice"
  225. >
  226. 《实验动物带离须知》
  227. </el-link>
  228. 内容
  229. </el-checkbox>
  230. </el-col>
  231. </el-row>
  232. </el-form>
  233. <template #footer>
  234. <span class="dialog-footer">
  235. <el-button
  236. type="info"
  237. @click="onCancel"
  238. size="default"
  239. >
  240. 取 消
  241. </el-button>
  242. <el-button
  243. v-if="state.dialog.type !== 'detail'"
  244. color="#2c78ff"
  245. @click="onSubmit()"
  246. size="default"
  247. >
  248. 提交
  249. </el-button>
  250. </span>
  251. </template>
  252. </el-dialog>
  253. <el-dialog
  254. v-model="isShowNotice"
  255. title="实验动物带离须知"
  256. width="90%"
  257. >
  258. <div class="text">
  259. <p
  260. class="mb20"
  261. v-for="item in AnimalRemovalApplicationNotice.split('\n')"
  262. :key="item"
  263. >
  264. {{ item }}
  265. </p>
  266. </div>
  267. <template #footer>
  268. <el-button
  269. type="primary"
  270. @click="isShowNotice = false"
  271. >
  272. 确定
  273. </el-button>
  274. </template>
  275. </el-dialog>
  276. </div>
  277. </template>
  278. <script setup lang="ts">
  279. import { reactive, ref, computed } from 'vue'
  280. import to from 'await-to-js'
  281. import { ElMessage } from 'element-plus'
  282. import type { FormRules, FormInstance } from 'element-plus'
  283. import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
  284. import {
  285. CreateAnimalApplyLeavePayload,
  286. TakeawayList,
  287. ActionType,
  288. AnimalRemovalApplicationNotice,
  289. } from '/@/constants/pageConstants'
  290. import { deepClone } from '/@/utils/other'
  291. import { useUserInfos } from '/@/hooks/useUserInfos'
  292. import { filterFields } from '/@/utils/func'
  293. const emit = defineEmits(['refresh'])
  294. const { userInfos } = useUserInfos()
  295. const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi()
  296. const expertDialogFormRef = ref<FormInstance>()
  297. const animalTypeList = ref<{ id: string; name: string }[]>([])
  298. const safePromiseStatus = ref<boolean>(false)
  299. const isShowNotice = ref<boolean>(false)
  300. const rules = reactive<FormRules<CreateAnimalApplyLeavePayload>>({
  301. categoryId: [{ required: true, message: '请选择动物种类', trigger: 'blur' }],
  302. projectGroupId: [{ required: true, message: '请选择项目组', trigger: 'blur' }],
  303. takeawayDate: [{ required: true, message: '请选择转出日期', trigger: 'blur' }],
  304. userName: [{ required: true, message: '请输入申请人姓名', trigger: 'blur' }],
  305. takeawayAddress: [{ required: true, message: '不能为空', trigger: 'change' }],
  306. takeawayReason: [{ required: true, message: '不能为空', trigger: 'change' }],
  307. takeawayTransport: [{ required: true, message: '不能为空', trigger: 'change' }],
  308. })
  309. const defaultFormFields: CreateAnimalApplyLeavePayload = {
  310. accessCardNumber: '',
  311. categoryId: null,
  312. projectGroupId: null,
  313. projectGroupName: '',
  314. takeawayDate: '',
  315. takeawayAddress: '',
  316. takeawayReason: '',
  317. takeawayTransport: '',
  318. takewayFemaleNumber: 0,
  319. takeawayMaleNumber: 0,
  320. userName: '',
  321. phone: '',
  322. deptId: '',
  323. deptName: '',
  324. }
  325. const state = reactive<{
  326. form: CreateAnimalApplyLeavePayload
  327. safePromise: boolean
  328. safeRead: boolean
  329. dialog: { isShowDialog: boolean; type: string; title: string; submitTxt: string }
  330. }>({
  331. form: defaultFormFields,
  332. safePromise: false,
  333. safeRead: false,
  334. dialog: {
  335. isShowDialog: false,
  336. type: '',
  337. title: '',
  338. submitTxt: '',
  339. },
  340. })
  341. const animalNumber = computed(() => {
  342. const maleNumber = state.form.takeawayMaleNumber || 0
  343. const famaleNumber = state.form.takewayFemaleNumber || 0
  344. return maleNumber + famaleNumber
  345. })
  346. const getDicts = async () => {
  347. const [_, res]: ToResponse = await to(platAnimalCageApplicationApi.getAnimalTypeList({}))
  348. if (res) {
  349. animalTypeList.value = res.data
  350. }
  351. }
  352. const openDialog = async (type: ActionType, sourceData?: TakeawayList) => {
  353. await getDicts()
  354. state.dialog.type = type
  355. state.form.userName = userInfos.value.nickName
  356. state.form.phone = userInfos.value.phone
  357. state.form.deptName = userInfos.value.deptName
  358. state.form.projectGroupId = userInfos.value.projectGroup?.id
  359. state.form.projectGroupName = userInfos.value.projectGroup?.pgName
  360. state.form.deptName = userInfos.value.deptName
  361. if (sourceData) {
  362. state.form = {
  363. ...state.form,
  364. ...sourceData,
  365. }
  366. }
  367. if (type === 'add') {
  368. state.dialog.title = '新增实验动物带离单'
  369. state.form.categoryId = animalTypeList.value[0].id
  370. }
  371. if (type === 'edit') {
  372. state.dialog.title = '编辑实验动物带离单'
  373. }
  374. if (type === 'detail') {
  375. state.dialog.title = '查看实验动物带离单'
  376. safePromiseStatus.value = true
  377. }
  378. state.dialog.isShowDialog = true
  379. }
  380. const closeDialog = () => {
  381. expertDialogFormRef.value.resetFields()
  382. state.form = defaultFormFields
  383. state.dialog.isShowDialog = false
  384. }
  385. const onCancel = () => {
  386. closeDialog()
  387. }
  388. const onSubmit = async () => {
  389. expertDialogFormRef.value.validate(async (valid: boolean) => {
  390. if (!valid) return
  391. if (!safePromiseStatus.value) {
  392. ElMessage.error('请阅读并勾选安全承诺!')
  393. return
  394. }
  395. if (!state.form.takeawayMaleNumber && !state.form.takewayFemaleNumber) {
  396. ElMessage.error('请添加雄性或雌性数量!')
  397. return
  398. }
  399. const post = platAnimalCageApplicationApi.createAnimalTakeawayApplications
  400. const [err]: ToResponse = await to(
  401. post(
  402. filterFields({
  403. ...deepClone(state.form),
  404. deptId: userInfos.value.deptId,
  405. projectGroupId: state.form.projectGroupId?.toString(),
  406. }),
  407. ),
  408. )
  409. if (err) return
  410. ElMessage.success('操作成功')
  411. closeDialog()
  412. emit('refresh')
  413. })
  414. }
  415. const handleReadNotice = () => {
  416. isShowNotice.value = true
  417. }
  418. defineExpose({
  419. openDialog,
  420. })
  421. </script>
  422. <style lang="scss" scoped>
  423. .application-dialog-container {
  424. .el-select {
  425. width: 100%;
  426. }
  427. }
  428. h4 {
  429. font-size: 18px;
  430. }
  431. ul {
  432. padding-left: 20px;
  433. }
  434. .text {
  435. p {
  436. text-indent: 2em;
  437. }
  438. }
  439. .el-upload + .el-button {
  440. vertical-align: top;
  441. }
  442. :deep(.el-checkbox) {
  443. white-space: pre-wrap;
  444. }
  445. </style>