|
|
@@ -0,0 +1,323 @@
|
|
|
+<template>
|
|
|
+ <div class="entry-container">
|
|
|
+ <div class="search-wrap" ref="searchWrapRef">
|
|
|
+ <el-form :model="state.queryParams" ref="queryRef">
|
|
|
+ <el-form-item prop="serialNo">
|
|
|
+ <el-select
|
|
|
+ v-model="state.queryParams.feeType"
|
|
|
+ style="width: 100%"
|
|
|
+ placeholder="费用类型"
|
|
|
+ clearable
|
|
|
+ @change="search"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in FeeTypeList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="serialNo">
|
|
|
+ <el-select
|
|
|
+ v-model="state.queryParams.feeStatus"
|
|
|
+ style="width: 100%"
|
|
|
+ placeholder="费用状态"
|
|
|
+ clearable
|
|
|
+ @change="search"
|
|
|
+ >
|
|
|
+ <el-option label="已支付" value="20"></el-option>
|
|
|
+ <el-option label="未支付" value="10"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="serialNo">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="state.queryParams.feeTime"
|
|
|
+ type="date"
|
|
|
+ style="width: 100%"
|
|
|
+ placeholder="费用时间"
|
|
|
+ clearable
|
|
|
+ @change="search"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div style="text-align: right">
|
|
|
+ <el-button @click="handleExport" style="height: 25px" type="primary">导出</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="list-container">
|
|
|
+ <van-list
|
|
|
+ v-model:loading="state.loading"
|
|
|
+ :finished="state.finished"
|
|
|
+ finished-text="没有更多了"
|
|
|
+ @load="onLoad"
|
|
|
+ >
|
|
|
+ <van-cell v-for="item in state.pageList" :key="item" @click="handleCheckDetail(item)">
|
|
|
+ <template #default>
|
|
|
+ <div class="list">
|
|
|
+ <header class="flex justify-between">
|
|
|
+ <strong class="title">{{ `${item.userName}的费用流水` }}</strong>
|
|
|
+ <van-tag v-if="item.approveStatus == ApproveStatus.WAIT_SUBMIT" type="warning"
|
|
|
+ >待提交</van-tag
|
|
|
+ >
|
|
|
+ <van-tag v-else-if="item.approveStatus == ApproveStatus.APPROVING" type="primary"
|
|
|
+ >审核中</van-tag
|
|
|
+ >
|
|
|
+ <van-tag v-else-if="item.approveStatus == ApproveStatus.PASS" type="success"
|
|
|
+ >通过</van-tag
|
|
|
+ >
|
|
|
+ <van-tag v-else-if="item.approveStatus == ApproveStatus.REVOKE" type="success"
|
|
|
+ >撤销</van-tag
|
|
|
+ >
|
|
|
+ <van-tag v-else-if="item.approveStatus == ApproveStatus.REFUSE" type="danger"
|
|
|
+ >拒绝</van-tag
|
|
|
+ >
|
|
|
+ </header>
|
|
|
+ <p class="inst-title">
|
|
|
+ <span>费用类型</span>
|
|
|
+ <span class="title ml8">{{ item.projectGroupName }}</span>
|
|
|
+ </p>
|
|
|
+ <p class="inst-title">
|
|
|
+ <span>费用时间</span>
|
|
|
+ <span class="title ml8">
|
|
|
+ {{ item.userName }}
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <p class="inst-title">
|
|
|
+ <span>确认时间</span>
|
|
|
+ <span class="title ml8">
|
|
|
+ {{ formatToChineseDate(item.createdTime) }}
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <p class="inst-title">
|
|
|
+ <span>总计金额</span>
|
|
|
+ <span class="title ml8">
|
|
|
+ {{ formatToChineseDate(item.startDate) }}
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <p class="inst-title">
|
|
|
+ <span>状态</span>
|
|
|
+ <span class="title ml8">
|
|
|
+ {{ item.number }}
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <footer class="flex justify-between mt16">
|
|
|
+ <span class="title">
|
|
|
+ <el-button
|
|
|
+ style="height: 25px"
|
|
|
+ type="primary"
|
|
|
+ @click.stop="handleRefundable(item)"
|
|
|
+ >确认</el-button
|
|
|
+ >
|
|
|
+ </span>
|
|
|
+ <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
|
|
|
+ </footer>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </van-cell>
|
|
|
+ </van-list>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <DetailModal
|
|
|
+ :showDialog="showDetailDialog"
|
|
|
+ :isReturnCageList="false"
|
|
|
+ ref="detailModalRef"
|
|
|
+ @close="() => (showDetailDialog = false)"
|
|
|
+ />
|
|
|
+ <ReturnCageDialog
|
|
|
+ ref="returnCageDialogRef"
|
|
|
+ :currentRefundableItemNumber="currentRefundableItemNumber"
|
|
|
+ :getTableData="handleRefresh"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+ import { reactive, ref, onMounted, defineAsyncComponent, ComponentPublicInstance } from 'vue';
|
|
|
+ import to from 'await-to-js';
|
|
|
+
|
|
|
+ import { formatDate, formatToChineseDate } from '/@/utils/formatTime';
|
|
|
+ import {
|
|
|
+ ApproveStatus,
|
|
|
+ ReturnStatus,
|
|
|
+ LeavelList,
|
|
|
+ ApproveStatusList,
|
|
|
+ MyCageType,
|
|
|
+ FeeTypeList,
|
|
|
+ } from '/@/constants/pageConstants';
|
|
|
+ import { useUserInfos } from '/@/hooks/useUserInfos';
|
|
|
+ import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal';
|
|
|
+ import { useFinaceApi } from '/@/api/finace';
|
|
|
+
|
|
|
+ interface ReturnCageDialogInstance extends ComponentPublicInstance {
|
|
|
+ handleOpenRefundableDialog: (id: number) => void;
|
|
|
+ }
|
|
|
+
|
|
|
+ interface DetailModalInstance extends ComponentPublicInstance {
|
|
|
+ initForm: (id: number) => void;
|
|
|
+ }
|
|
|
+
|
|
|
+ const DetailModal = defineAsyncComponent(
|
|
|
+ () => import('/@/view/animal/application/components/Detail.vue')
|
|
|
+ );
|
|
|
+ const ReturnCageDialog = defineAsyncComponent(
|
|
|
+ () => import('/@/view/animal/application/components/ReturnCageDialog.vue')
|
|
|
+ );
|
|
|
+
|
|
|
+ const { userInfos } = useUserInfos();
|
|
|
+ const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi();
|
|
|
+ const finaceApi = useFinaceApi();
|
|
|
+
|
|
|
+ const returnCageDialogRef = ref<ReturnCageDialogInstance>();
|
|
|
+ const detailModalRef = ref<DetailModalInstance>();
|
|
|
+
|
|
|
+ const showDetailDialog = ref<boolean>(false);
|
|
|
+ const activeStatus = ref<MyCageType>(MyCageType.MINE_CAGE);
|
|
|
+ const currentRefundableItemNumber = ref<number>(0);
|
|
|
+
|
|
|
+ const state = reactive({
|
|
|
+ pageList: [],
|
|
|
+ loading: false,
|
|
|
+ finished: false,
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 25,
|
|
|
+ isMyself: 1,
|
|
|
+ feeType: '',
|
|
|
+ feeStatus: '',
|
|
|
+ feeTime: '',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const formatApproveStatus = (status: number) => {
|
|
|
+ return ApproveStatusList.find(item => item.id === status)?.name || '';
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleRefundable = (row: any) => {
|
|
|
+ currentRefundableItemNumber.value = row.number;
|
|
|
+ returnCageDialogRef.value.handleOpenRefundableDialog(row.id);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleRefresh = () => {
|
|
|
+ resetQueryParams();
|
|
|
+ onLoad();
|
|
|
+ };
|
|
|
+
|
|
|
+ const resetQueryParams = () => {
|
|
|
+ (state.pageList = []),
|
|
|
+ (state.loading = false),
|
|
|
+ (state.finished = false),
|
|
|
+ (state.queryParams = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 25,
|
|
|
+ isMyself: 1,
|
|
|
+ feeType: '',
|
|
|
+ feeStatus: '',
|
|
|
+ feeTime: '',
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const onLoad = async (isSearch?: boolean) => {
|
|
|
+ state.loading = true;
|
|
|
+ const apiRequest =
|
|
|
+ activeStatus.value === MyCageType.MINE_CAGE
|
|
|
+ ? platAnimalCageApplicationApi.getList(state.queryParams)
|
|
|
+ : platAnimalCageApplicationApi.getMyCageHistoryList(state.queryParams);
|
|
|
+
|
|
|
+ const [err, res]: ToResponse = await to(apiRequest);
|
|
|
+
|
|
|
+ state.loading = false;
|
|
|
+
|
|
|
+ if (err) return;
|
|
|
+
|
|
|
+ const list = res?.data?.list || [];
|
|
|
+
|
|
|
+ if (!isSearch) {
|
|
|
+ for (const item of list) {
|
|
|
+ state.pageList.push(item);
|
|
|
+ }
|
|
|
+ state.queryParams.pageNum++;
|
|
|
+ if (list.length < state.queryParams.pageSize) {
|
|
|
+ state.finished = true;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ state.pageList = list;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleCheckDetail = (row: any) => {
|
|
|
+ detailModalRef.value.initForm(row.id);
|
|
|
+ showDetailDialog.value = true;
|
|
|
+ };
|
|
|
+
|
|
|
+ const search = () => {
|
|
|
+ onLoad(true);
|
|
|
+ };
|
|
|
+
|
|
|
+ const getList = async () => {
|
|
|
+ const [err, res]: ToResponse = await to(finaceApi.getList(state.queryParams));
|
|
|
+ if (err) return;
|
|
|
+ console.log(res);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleExport = async () => {};
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ getList();
|
|
|
+ onLoad();
|
|
|
+ });
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .entry-container {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ .search-wrap {
|
|
|
+ background: #fff;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ padding: 15px;
|
|
|
+ }
|
|
|
+ .list-container {
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+ .van-list {
|
|
|
+ .van-cell {
|
|
|
+ background-color: #fff;
|
|
|
+ + .van-cell {
|
|
|
+ margin-top: 10px;
|
|
|
+ }
|
|
|
+ header,
|
|
|
+ footer {
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+ .title {
|
|
|
+ flex: 1;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ text-align: left;
|
|
|
+ }
|
|
|
+ .inst-title {
|
|
|
+ color: #333;
|
|
|
+ text-align: left;
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ margin-top: 4px;
|
|
|
+ span:first-child {
|
|
|
+ color: rgb(120, 120, 120);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .time {
|
|
|
+ color: #f69a4d;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|