|
|
@@ -30,7 +30,6 @@
|
|
|
:rules="rules.takeawayReason" />
|
|
|
<van-field v-model="state.form.takeawayTransport" label="运输方式" placeholder="请输入" :readonly="isReadOnly" required
|
|
|
:rules="rules.takeawayTransport" />
|
|
|
-<!-- <van-field v-model="state.form.accessCardNumber" label="门禁卡序列号" placeholder="请输入" :readonly="isReadOnly" />-->
|
|
|
<van-field v-model="state.form.takeawayMaleNumber" label="雄性" placeholder="雄性数量" type="digit" :readonly="isReadOnly">
|
|
|
<template #button>
|
|
|
<van-stepper v-model="state.form.takeawayMaleNumber" :min="0" integer :disabled="isReadOnly" />
|
|
|
@@ -43,9 +42,71 @@
|
|
|
</van-field>
|
|
|
</van-cell-group>
|
|
|
|
|
|
- <h4 class="mb20 mt20">转回备注</h4>
|
|
|
- <van-field v-model="isReturnLabel" label="是否转回" placeholder="请选择" :readonly="isReadOnly" :is-link="!isReadOnly" required
|
|
|
- @click="!isReadOnly && (showReturnPicker = true)" :rules="rules.isReturn"/>
|
|
|
+ <van-field
|
|
|
+ v-model="isReturnLabel"
|
|
|
+ label="是否转回"
|
|
|
+ placeholder="请选择"
|
|
|
+ :readonly="isReadOnly"
|
|
|
+ :is-link="!isReadOnly"
|
|
|
+ required
|
|
|
+ @click="!isReadOnly && (showReturnPicker = true)"
|
|
|
+ :rules="rules.isReturn"
|
|
|
+ />
|
|
|
+
|
|
|
+ <template v-if="state.dialog.type === 'detail'">
|
|
|
+ <h4 class="mb20 mt20">转回信息</h4>
|
|
|
+ <van-cell-group>
|
|
|
+ <van-field
|
|
|
+ label="门禁卡是否归还"
|
|
|
+ :model-value="accessCardReturnText"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ label="转回日期"
|
|
|
+ :model-value="returnDateText"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ label="转回数量(雄性+雌性)"
|
|
|
+ :model-value="returnAnimalNumberText"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ v-model="state.form.returnTransport"
|
|
|
+ label="转回运输方式"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ v-model="state.form.notReturnReason"
|
|
|
+ label="未返回动物情况说明"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ </van-cell-group>
|
|
|
+
|
|
|
+ <h4 class="mb20 mt20">淘汰上报信息</h4>
|
|
|
+ <van-cell-group>
|
|
|
+ <van-field
|
|
|
+ label="淘汰日期"
|
|
|
+ :model-value="dieTimeText"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ v-model="state.form.dieReason"
|
|
|
+ label="淘汰原因"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ label="淘汰数量(雄性+雌性)"
|
|
|
+ :model-value="dieAnimalNumberText"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ <van-field
|
|
|
+ v-model="state.form.location"
|
|
|
+ label="动物尸体存放位置"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ </van-cell-group>
|
|
|
+ </template>
|
|
|
<div class="mt30 mb30 checkbox-wrapper">
|
|
|
<van-checkbox v-model="safePromiseStatus" :disabled="isReadOnly">
|
|
|
本人已阅读并同意
|
|
|
@@ -56,6 +117,17 @@
|
|
|
</van-checkbox>
|
|
|
</div>
|
|
|
</van-form>
|
|
|
+
|
|
|
+ <template v-if="showApprovalInfo">
|
|
|
+ <h4 class="mb20 mt20">审批记录</h4>
|
|
|
+ <FlowTable
|
|
|
+ v-if="approvalFlowId"
|
|
|
+ :id="approvalFlowId"
|
|
|
+ :businessCode="approvalBusinessCode"
|
|
|
+ defCode="plat_animal_takeway_applications"
|
|
|
+ />
|
|
|
+ <div v-else class="approval-empty">暂无审批记录</div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
<div class="dialog-footer">
|
|
|
<van-button
|
|
|
@@ -112,7 +184,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { reactive, ref, computed } from 'vue'
|
|
|
+import { reactive, ref, computed, defineAsyncComponent } from 'vue'
|
|
|
import to from 'await-to-js'
|
|
|
import { showToast, showNotify } from 'vant'
|
|
|
import type { FormInstance } from 'vant/es'
|
|
|
@@ -123,6 +195,7 @@ import {
|
|
|
TakeawayList,
|
|
|
ActionType,
|
|
|
AnimalRemovalApplicationNotice,
|
|
|
+ ApplyLeaveApproveStatus,
|
|
|
} from '/@/constants/pageConstants'
|
|
|
import { deepClone } from '/@/utils/other'
|
|
|
import { useUserInfos } from '/@/hooks/useUserInfos'
|
|
|
@@ -134,6 +207,8 @@ const emit = defineEmits(['refresh'])
|
|
|
const { userInfos } = useUserInfos()
|
|
|
const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi()
|
|
|
|
|
|
+const FlowTable = defineAsyncComponent(() => import('/@/components/FlowTable.vue'))
|
|
|
+
|
|
|
const expertDialogFormRef = ref<FormInstance>()
|
|
|
const animalTypeList = ref<{ id: string; name: string }[]>([])
|
|
|
const safePromiseStatus = ref<boolean>(false)
|
|
|
@@ -145,6 +220,27 @@ const showReturnPicker = ref<boolean>(false)
|
|
|
|
|
|
const isReadOnly = computed(() => state.dialog.type === 'detail')
|
|
|
|
|
|
+type FormModel = CreateAnimalApplyLeavePayload & {
|
|
|
+ categoryName?: string
|
|
|
+ id?: number
|
|
|
+ approvalId?: number
|
|
|
+ approveStatus?: string
|
|
|
+ businessCode?: string
|
|
|
+ createdTime?: string
|
|
|
+ createdName?: string
|
|
|
+ returnDate?: string
|
|
|
+ returnFemaleNumber?: number
|
|
|
+ returnMaleNumber?: number
|
|
|
+ returnTransport?: string
|
|
|
+ accessCardReturn?: number
|
|
|
+ dieFemaleNumber?: number
|
|
|
+ dieMaleNumber?: number
|
|
|
+ dieTime?: string
|
|
|
+ dieReason?: string
|
|
|
+ location?: string
|
|
|
+ notReturnReason?: string
|
|
|
+}
|
|
|
+
|
|
|
const rules = {
|
|
|
categoryId: [{ required: true, message: '动物类别不能为空' }],
|
|
|
variety: [{ required: true, message: '品种品系不能为空' }],
|
|
|
@@ -157,7 +253,7 @@ const rules = {
|
|
|
isReturn: [{ required: true, message: '是否转回不能为空' }],
|
|
|
}
|
|
|
|
|
|
-const defaultFormFields: CreateAnimalApplyLeavePayload & { categoryName?: string } = {
|
|
|
+const defaultFormFields: FormModel = {
|
|
|
accessCardNumber: '',
|
|
|
categoryId: null,
|
|
|
categoryName: '',
|
|
|
@@ -175,10 +271,21 @@ const defaultFormFields: CreateAnimalApplyLeavePayload & { categoryName?: string
|
|
|
deptId: '',
|
|
|
deptName: '',
|
|
|
isReturn: 0,
|
|
|
+ returnDate: '',
|
|
|
+ returnFemaleNumber: 0,
|
|
|
+ returnMaleNumber: 0,
|
|
|
+ returnTransport: '',
|
|
|
+ accessCardReturn: 0,
|
|
|
+ dieFemaleNumber: 0,
|
|
|
+ dieMaleNumber: 0,
|
|
|
+ dieTime: '',
|
|
|
+ dieReason: '',
|
|
|
+ location: '',
|
|
|
+ notReturnReason: '',
|
|
|
}
|
|
|
|
|
|
const state = reactive<{
|
|
|
- form: CreateAnimalApplyLeavePayload & { categoryName?: string }
|
|
|
+ form: FormModel
|
|
|
safePromise: boolean
|
|
|
safeRead: boolean
|
|
|
loading: boolean
|
|
|
@@ -196,6 +303,63 @@ const state = reactive<{
|
|
|
},
|
|
|
})
|
|
|
|
|
|
+const showApprovalInfo = computed(() => {
|
|
|
+ return Boolean(state.form.approvalId || state.form.businessCode)
|
|
|
+})
|
|
|
+
|
|
|
+const approveStatusTag = computed(() => {
|
|
|
+ if (!state.form.approveStatus) return null
|
|
|
+ if (state.form.approveStatus === ApplyLeaveApproveStatus.SUBMIT) return { text: '提交', type: 'primary' as const }
|
|
|
+ if (state.form.approveStatus === ApplyLeaveApproveStatus.WAIT_APPROVE) return { text: '待审核', type: 'primary' as const }
|
|
|
+ if (state.form.approveStatus === ApplyLeaveApproveStatus.PASS) return { text: '通过', type: 'success' as const }
|
|
|
+ if (state.form.approveStatus === ApplyLeaveApproveStatus.REVOKE) return { text: '撤回', type: 'success' as const }
|
|
|
+ if (state.form.approveStatus === ApplyLeaveApproveStatus.REFUSE) return { text: '审核不通过', type: 'danger' as const }
|
|
|
+ return { text: state.form.approveStatus, type: 'primary' as const }
|
|
|
+})
|
|
|
+
|
|
|
+const createdTimeText = computed(() => {
|
|
|
+ if (!state.form.createdTime) return '-'
|
|
|
+ return formatDate(new Date(state.form.createdTime), 'YYYY-mm-dd HH:MM:SS')
|
|
|
+})
|
|
|
+
|
|
|
+const approvalFlowId = computed(() => {
|
|
|
+ return Number(state.form.approvalId || 0)
|
|
|
+})
|
|
|
+
|
|
|
+const approvalBusinessCode = computed(() => {
|
|
|
+ return state.form.businessCode ? String(state.form.businessCode) : ''
|
|
|
+})
|
|
|
+
|
|
|
+const formatYmd = (val?: string) => {
|
|
|
+ if (!val) return ''
|
|
|
+ const str = String(val)
|
|
|
+ return str.length >= 10 ? str.slice(0, 10) : str
|
|
|
+}
|
|
|
+
|
|
|
+const returnDateText = computed(() => formatYmd(state.form.returnDate))
|
|
|
+
|
|
|
+const dieTimeText = computed(() => formatYmd(state.form.dieTime))
|
|
|
+
|
|
|
+const returnAnimalNumberText = computed(() => {
|
|
|
+ const maleNumber = state.form.returnMaleNumber || 0
|
|
|
+ const femaleNumber = state.form.returnFemaleNumber || 0
|
|
|
+ const total = maleNumber + femaleNumber
|
|
|
+ return `${maleNumber} + ${femaleNumber} = ${total}`
|
|
|
+})
|
|
|
+
|
|
|
+const dieAnimalNumberText = computed(() => {
|
|
|
+ const maleNumber = state.form.dieMaleNumber || 0
|
|
|
+ const femaleNumber = state.form.dieFemaleNumber || 0
|
|
|
+ const total = maleNumber + femaleNumber
|
|
|
+ return `${maleNumber} + ${femaleNumber} = ${total}`
|
|
|
+})
|
|
|
+
|
|
|
+const accessCardReturnText = computed(() => {
|
|
|
+ if (state.form.accessCardReturn === 1) return '是'
|
|
|
+ if (state.form.accessCardReturn === 0) return '否'
|
|
|
+ return ''
|
|
|
+})
|
|
|
+
|
|
|
const getDicts = async () => {
|
|
|
const [_, res]: ToResponse = await to(platAnimalCageApplicationApi.getAnimalTypeList({}))
|
|
|
|
|
|
@@ -204,6 +368,54 @@ const getDicts = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const loadRebackInfo = async (takeawayId?: number) => {
|
|
|
+ if (!takeawayId) return
|
|
|
+ const [err, res]: ToResponse = await to(
|
|
|
+ platAnimalCageApplicationApi.getPlatAnimalTakeawayRebackList({ takeawayId }),
|
|
|
+ )
|
|
|
+ if (err || !res) return
|
|
|
+
|
|
|
+ const rawList = (res as any).data
|
|
|
+ const list = Array.isArray(rawList?.list)
|
|
|
+ ? rawList.list
|
|
|
+ : Array.isArray(rawList)
|
|
|
+ ? rawList
|
|
|
+ : []
|
|
|
+
|
|
|
+ const turnBackList = list.filter((item: any) => String(item.takeawayType) === '10')
|
|
|
+ const dieList = list.filter((item: any) => String(item.takeawayType) === '20')
|
|
|
+
|
|
|
+ const turnBack = turnBackList[turnBackList.length - 1]
|
|
|
+ const die = dieList[dieList.length - 1]
|
|
|
+
|
|
|
+ if (turnBack) {
|
|
|
+ state.form.returnDate = turnBack.returnDate || ''
|
|
|
+ state.form.returnTransport = turnBack.returnTransport || ''
|
|
|
+ if (typeof turnBack.accessCardReturn === 'number') {
|
|
|
+ state.form.accessCardReturn = turnBack.accessCardReturn
|
|
|
+ }
|
|
|
+ state.form.returnFemaleNumber = turnBack.returnFemaleNumber || 0
|
|
|
+ state.form.returnMaleNumber = turnBack.returnMaleNumber || 0
|
|
|
+ if (turnBack.notReturnReason) {
|
|
|
+ state.form.notReturnReason = turnBack.notReturnReason
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (die) {
|
|
|
+ state.form.dieTime = die.dieTime || ''
|
|
|
+ state.form.dieReason = die.dieReason || ''
|
|
|
+ state.form.location = die.location || ''
|
|
|
+ const dieFemale = die.returnFemaleNumber ?? die.dieFemaleNumber
|
|
|
+ const dieMale = die.returnMaleNumber ?? die.dieMaleNumber
|
|
|
+ if (typeof dieFemale === 'number') {
|
|
|
+ state.form.dieFemaleNumber = dieFemale
|
|
|
+ }
|
|
|
+ if (typeof dieMale === 'number') {
|
|
|
+ state.form.dieMaleNumber = dieMale
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const openDialog = async (type: ActionType, sourceData?: TakeawayList) => {
|
|
|
await getDicts()
|
|
|
state.dialog.type = type
|
|
|
@@ -237,6 +449,10 @@ const openDialog = async (type: ActionType, sourceData?: TakeawayList) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (type === 'detail') {
|
|
|
+ await loadRebackInfo(state.form.id)
|
|
|
+ }
|
|
|
+
|
|
|
if (type === 'add') {
|
|
|
state.dialog.title = '新增实验动物带离单'
|
|
|
if (animalTypeList.value.length > 0) {
|
|
|
@@ -276,6 +492,17 @@ const closeDialog = () => {
|
|
|
deptId: '',
|
|
|
deptName: '',
|
|
|
isReturn: 0,
|
|
|
+ returnDate: '',
|
|
|
+ returnFemaleNumber: 0,
|
|
|
+ returnMaleNumber: 0,
|
|
|
+ returnTransport: '',
|
|
|
+ accessCardReturn: 0,
|
|
|
+ dieFemaleNumber: 0,
|
|
|
+ dieMaleNumber: 0,
|
|
|
+ dieTime: '',
|
|
|
+ dieReason: '',
|
|
|
+ location: '',
|
|
|
+ notReturnReason: '',
|
|
|
}
|
|
|
safePromiseStatus.value = false
|
|
|
selectedDate.value = null
|
|
|
@@ -334,10 +561,12 @@ const onSubmit = async () => {
|
|
|
}
|
|
|
|
|
|
const post = platAnimalCageApplicationApi.createAnimalTakeawayApplications
|
|
|
+ const submit = platAnimalCageApplicationApi.submitAnimalTakeawayApplications
|
|
|
const [err]: ToResponse = await to(
|
|
|
- post(
|
|
|
+ (state.dialog.type === 'edit' ? submit : post)(
|
|
|
filterFields({
|
|
|
...deepClone(state.form),
|
|
|
+ categoryId: state.form.categoryId?.toString() || null,
|
|
|
deptId: userInfos.value.deptId,
|
|
|
projectGroupId: state.form.projectGroupId?.toString(),
|
|
|
takeawayMaleNumber: Number(state.form.takeawayMaleNumber) || 0,
|
|
|
@@ -367,7 +596,7 @@ const onSubmit = async () => {
|
|
|
const onCategoryConfirm = ({ selectedOptions }: { selectedOptions: any[] }) => {
|
|
|
if (selectedOptions.length > 0) {
|
|
|
const selected = selectedOptions[0]
|
|
|
- state.form.categoryId = selected.id
|
|
|
+ state.form.categoryId = selected.id?.toString?.() ?? String(selected.id)
|
|
|
state.form.categoryName = selected.name
|
|
|
}
|
|
|
showCategoryPicker.value = false
|
|
|
@@ -468,6 +697,14 @@ defineExpose({
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .approval-empty {
|
|
|
+ padding: 16px;
|
|
|
+ text-align: center;
|
|
|
+ color: #969799;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
.dialog-footer {
|
|
|
padding: 16px;
|
|
|
padding-bottom: calc(16px + env(safe-area-inset-bottom));
|
|
|
@@ -529,4 +766,4 @@ defineExpose({
|
|
|
top: 16px;
|
|
|
right: 16px;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|