<!--
|
OA / 审批管理 / 审批列表
|
-->
|
<template>
|
<view class="oa-approval-page">
|
<PageHeader title="审批列表"
|
@back="goBack" />
|
|
<view class="oa-toolbar">
|
<view class="oa-filter-chip active-search">
|
<up-icon name="search"
|
size="18"
|
color="#666" />
|
<up-input v-model="queryParams.keyword"
|
class="chip-input"
|
placeholder="审批标题 / 审批编号"
|
clearable
|
border="none"
|
@confirm="handleSearch" />
|
</view>
|
<view class="oa-icon-btn"
|
@click="handleSearch">
|
<up-icon name="search"
|
size="20"
|
color="#2979ff" />
|
</view>
|
</view>
|
|
<scroll-view class="oa-list-scroll"
|
scroll-y
|
:show-scrollbar="false"
|
:style="{ height: listScrollHeight + 'px' }"
|
@scrolltolower="loadMore">
|
<view v-if="list.length"
|
class="oa-card-list">
|
<view v-for="item in list"
|
:key="item.id"
|
class="oa-card"
|
@click="openDetail(item)">
|
<view class="oa-card-head">
|
<view class="oa-card-title-wrap">
|
<text class="oa-card-title">{{ item.title || item.instanceNo || "-" }}</text>
|
<text v-if="item.templateName"
|
class="oa-card-sub">{{ item.templateName }}</text>
|
</view>
|
<text :class="['oa-status', businessStatusClass(item.status)]">
|
{{ statusText(item.status) }}
|
</text>
|
</view>
|
|
<view class="oa-card-body">
|
<view class="oa-info-grid">
|
<view class="oa-info-row">
|
<text class="oa-info-label">审批编号</text>
|
<text class="oa-info-value">{{ item.instanceNo || "-" }}</text>
|
</view>
|
<view v-if="item.businessName"
|
class="oa-info-row">
|
<text class="oa-info-label">业务名称</text>
|
<text class="oa-info-value">{{ item.businessName }}</text>
|
</view>
|
<view class="oa-info-row">
|
<text class="oa-info-label">申请人</text>
|
<text class="oa-info-value">{{ item.applicantName || "-" }}</text>
|
</view>
|
<view class="oa-info-row">
|
<text class="oa-info-label">当前审批人</text>
|
<text class="oa-info-value">{{ currentApproverName(item) }}</text>
|
</view>
|
<view class="oa-info-row">
|
<text class="oa-info-label">申请时间</text>
|
<text class="oa-info-value">{{ formatDateTime(item.applyTime) }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view v-if="canModify(item) || item.isApprove"
|
class="oa-card-foot"
|
@click.stop>
|
<text v-if="canModify(item)"
|
class="oa-foot-btn btn-edit"
|
@click="goModify(item)">编辑</text>
|
<text v-if="item.isApprove"
|
class="oa-foot-btn btn-approve"
|
@click="handleApprove(item)">审批</text>
|
</view>
|
</view>
|
<up-loadmore :status="pageStatus" />
|
</view>
|
<view v-else
|
class="oa-empty">
|
<up-empty mode="list"
|
text="暂无审批数据" />
|
</view>
|
</scroll-view>
|
|
<view class="fab-button"
|
@click="goAdd">
|
<up-icon name="plus"
|
size="28"
|
color="#ffffff" />
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { onMounted, reactive, ref } from "vue";
|
import { onShow } from "@dcloudio/uni-app";
|
import PageHeader from "@/components/PageHeader.vue";
|
import { listApprovalInstancePage } from "@/api/oa/approvalInstance.js";
|
import { OA_NAV } from "@/config/oaPaths.js";
|
import useUserStore from "@/store/modules/user";
|
import { parseTime } from "@/utils/ruoyi";
|
import {
|
businessStatusClass,
|
businessStatusText,
|
canModifyInstance,
|
stashInstanceRow,
|
} from "../../_utils/approveListUtils.js";
|
import {
|
inferReimburseModuleKeyFromInstance,
|
resolveFinReimbursementIdFromInstance,
|
stashReimburseEditFromApprove,
|
} from "../../_utils/reimburseApproveBridge.js";
|
|
const userStore = useUserStore();
|
const queryParams = reactive({ keyword: "" });
|
const list = ref([]);
|
const pageStatus = ref("loadmore");
|
const page = reactive({ current: 1, size: 10, total: 0 });
|
const listScrollHeight = ref(400);
|
|
function calcListScrollHeight() {
|
const sys = uni.getSystemInfoSync();
|
const statusBar = sys.statusBarHeight || 0;
|
const navBar = 44;
|
const toolbar = 56;
|
const fabGap = 16;
|
listScrollHeight.value = Math.max(
|
200,
|
sys.windowHeight - statusBar - navBar - toolbar - fabGap
|
);
|
}
|
|
const statusText = status => businessStatusText(status);
|
|
const formatDateTime = val => {
|
if (!val) return "-";
|
return parseTime(val, "{y}-{m}-{d} {h}:{i}:{s}") || String(val);
|
};
|
|
const canModify = item => canModifyInstance(item, userStore);
|
|
const currentApproverName = item => {
|
const tasks = item?.tasks;
|
if (!Array.isArray(tasks) || !tasks.length) return "-";
|
const pending = tasks.find(t => t.taskStatus === "PENDING");
|
if (pending?.approverName) return pending.approverName;
|
const names = [...new Set(tasks.map(t => t.approverName).filter(Boolean))];
|
return names.length ? names.join("、") : "-";
|
};
|
|
const buildListParams = () => {
|
const keyword = queryParams.keyword?.trim();
|
const dto = {};
|
if (keyword) {
|
if (/[\u4e00-\u9fa5]/.test(keyword)) {
|
dto.title = keyword;
|
} else {
|
dto.instanceNo = keyword;
|
}
|
}
|
return { current: page.current, size: page.size, ...dto };
|
};
|
|
const getList = () => {
|
if (pageStatus.value === "loading" || pageStatus.value === "nomore") return;
|
pageStatus.value = "loading";
|
listApprovalInstancePage(buildListParams())
|
.then(res => {
|
const pageData = res?.data || {};
|
const records = pageData.records || [];
|
const total = pageData.total ?? 0;
|
if (page.current === 1) {
|
list.value = records;
|
} else {
|
list.value = [...list.value, ...records];
|
}
|
page.total = total;
|
if (list.value.length >= total || records.length < page.size) {
|
pageStatus.value = "nomore";
|
} else {
|
pageStatus.value = "loadmore";
|
page.current += 1;
|
}
|
})
|
.catch(() => {
|
if (page.current === 1) list.value = [];
|
pageStatus.value = "loadmore";
|
uni.showToast({ title: "查询失败", icon: "none" });
|
});
|
};
|
|
const handleSearch = () => {
|
page.current = 1;
|
pageStatus.value = "loadmore";
|
list.value = [];
|
getList();
|
};
|
|
const loadMore = () => {
|
if (pageStatus.value === "loadmore") getList();
|
};
|
|
const goBack = () => uni.navigateBack();
|
const goAdd = () => uni.navigateTo({ url: OA_NAV.approveListTemplateSelect });
|
|
const openDetail = item => {
|
if (!item?.id) return;
|
stashInstanceRow(item);
|
uni.navigateTo({ url: `${OA_NAV.approveListDetail}?id=${item.id}` });
|
};
|
|
const goModify = item => {
|
if (!canModify(item)) {
|
uni.showToast({ title: "仅进行中的本人申请可编辑", icon: "none" });
|
return;
|
}
|
const mk = inferReimburseModuleKeyFromInstance(item);
|
if (mk) {
|
const rid = resolveFinReimbursementIdFromInstance(item);
|
if (rid == null) {
|
uni.showToast({ title: "无法修改:缺少报销单 ID", icon: "none" });
|
return;
|
}
|
stashReimburseEditFromApprove(mk, rid);
|
uni.navigateTo({
|
url: `${OA_NAV.reimburseForm}?moduleKey=${mk}&mode=edit&reimbursementId=${rid}`,
|
});
|
return;
|
}
|
if (!item?.id) return;
|
stashInstanceRow(item);
|
uni.navigateTo({ url: `${OA_NAV.approveListApply}?id=${item.id}` });
|
};
|
|
const handleApprove = item => {
|
if (!item?.id) return;
|
if (!item.isApprove) {
|
uni.showToast({ title: "当前审批无需您处理", icon: "none" });
|
return;
|
}
|
stashInstanceRow(item);
|
uni.navigateTo({ url: `${OA_NAV.approveListApprove}?id=${item.id}` });
|
};
|
|
onMounted(() => calcListScrollHeight());
|
|
onShow(() => {
|
calcListScrollHeight();
|
handleSearch();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
@import "@/styles/sales-common.scss";
|
@import "../../_styles/oa-approval-list.scss";
|
|
.active-search {
|
padding-right: 4px;
|
}
|
|
.chip-input {
|
flex: 1;
|
font-size: 14px;
|
}
|
|
:deep(.chip-input .u-input__content) {
|
background: transparent !important;
|
padding: 0 !important;
|
}
|
</style>
|