import {
|
getApprovalTemplateDetail,
|
listApprovalTemplate,
|
TEMPLATE_TYPE_CUSTOM,
|
} from "@/api/officeProcessAutomation/approvalTemplate.js";
|
import {
|
approveApprovalInstance,
|
deleteApprovalInstance,
|
listApprovalInstancePage,
|
saveApprovalInstance,
|
updateApprovalInstance,
|
} from "@/api/officeProcessAutomation/approvalInstance.js";
|
import useUserStore from "@/store/modules/user";
|
import { Search } from "@element-plus/icons-vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import { computed, reactive, ref } from "vue";
|
import {
|
fetchBusinessTypeOptions,
|
formatDisplayTime,
|
mapEnabledFromApi,
|
mapTemplateFromApi,
|
unwrapTemplateDetail,
|
unwrapTemplateList,
|
} from "../approve-template/approveTemplateConstants.js";
|
import { buildSubmitTemplateFromRow } from "../approve-template/formConfigUtils.js";
|
import {
|
APPROVAL_TYPE_OPTIONS,
|
approvalStatusLabel,
|
approvalStatusTagType,
|
approvalTypeLabel,
|
buildApprovalInstanceListParams,
|
buildApproveInstanceDto,
|
buildEditFormFromInstanceRow,
|
buildInstanceDto,
|
clearLegacyApproveListStorage,
|
createEmptySubmitForm,
|
mapInstanceFromApi,
|
mapSubmitTemplateCard,
|
matchBusinessTypeValue,
|
validateSubmitFlowNodes,
|
unwrapInstancePage,
|
} from "./approveListConstants.js";
|
|
export function useApproveList() {
|
clearLegacyApproveListStorage();
|
const userStore = useUserStore();
|
|
const tableData = ref([]);
|
const submitBusinessTypeOptions = ref([]);
|
const allSubmitTemplates = ref([]);
|
const selectedBusinessType = ref("");
|
const submitTemplatesLoading = ref(false);
|
|
const submitTemplateCards = computed(() => {
|
if (selectedBusinessType.value == null || selectedBusinessType.value === "") return [];
|
return allSubmitTemplates.value.filter((card) =>
|
matchBusinessTypeValue(card.businessType, selectedBusinessType.value)
|
);
|
});
|
|
const searchForm = reactive({
|
approvalType: "",
|
applicantKeyword: "",
|
createTimeRange: [],
|
});
|
|
const tableLoading = ref(false);
|
const page = reactive({ current: 1, size: 10, total: 0 });
|
|
const detailDialog = reactive({ visible: false });
|
const detailRow = ref({});
|
|
const approveDialog = reactive({ visible: false, row: null });
|
const approveOpinion = ref("");
|
const approveSubmitting = ref(false);
|
|
const submitDialog = reactive({ visible: false, step: 1, mode: "add" });
|
const submitEditRow = ref(null);
|
const submitForm = reactive(createEmptySubmitForm(""));
|
const submitFormRef = ref();
|
const submitSaving = ref(false);
|
|
const isSubmitEdit = computed(() => submitDialog.mode === "edit");
|
const submitDialogTitle = computed(() => {
|
if (submitDialog.mode === "edit") {
|
return `修改${activeTemplate.value?.label || submitForm.templateName || "审批"}`;
|
}
|
if (submitDialog.step === 1) return "选择模板类型";
|
if (submitDialog.step === 2) return `选择审批模板${businessTypeLabel(selectedBusinessType.value) ? `(${businessTypeLabel(selectedBusinessType.value)})` : ""}`;
|
return `提交${activeTemplate.value?.label || "审批"}`;
|
});
|
|
const selectedBusinessTypeLabel = computed(() => businessTypeLabel(selectedBusinessType.value));
|
|
function businessTypeLabel(type) {
|
if (type == null || type === "") return "";
|
const hit = submitBusinessTypeOptions.value.find((x) => matchBusinessTypeValue(x.value, type));
|
return hit?.label || "";
|
}
|
|
function countTemplatesByBusinessType(type) {
|
return allSubmitTemplates.value.filter((card) => matchBusinessTypeValue(card.businessType, type)).length;
|
}
|
|
const activeTemplate = computed(() => submitForm.templateSnapshot || null);
|
|
/** 填报项定义(新增/修改与 formConfig 一致) */
|
const submitFormFields = computed(() => {
|
const tplFields = activeTemplate.value?.fields;
|
if (tplFields?.length) return tplFields;
|
return submitForm.formFieldDefs || [];
|
});
|
|
const submitFormRules = computed(() => {
|
const rules = {
|
templateKey: [{ required: true, message: "请选择审批类型", trigger: "change" }],
|
};
|
submitFormFields.value.forEach((f) => {
|
if (!f.required) return;
|
if (f.type === "number") {
|
rules[`formPayload.${f.key}`] = [{ required: true, message: `请填写${f.label}`, trigger: "blur" }];
|
} else if (f.type === "datetimerange") {
|
rules[`formPayload.${f.key}`] = [{ required: true, message: `请选择${f.label}`, trigger: "change" }];
|
} else {
|
rules[`formPayload.${f.key}`] = [{ required: true, message: `请填写${f.label}`, trigger: "blur" }];
|
}
|
});
|
return rules;
|
});
|
|
const tableColumn = ref([
|
{ label: "申请人编号", prop: "applicantNo", width: 110 },
|
{ label: "申请人名称", prop: "applicantName", minWidth: 100 },
|
{ label: "业务类型", prop: "businessName", minWidth: 120 },
|
{
|
label: "审批类型",
|
prop: "approvalType",
|
minWidth: 140,
|
dataType: "slot",
|
slot: "approveType",
|
},
|
{
|
label: "待我审批",
|
prop: "unread",
|
width: 90,
|
align: "center",
|
formatData: (v) => (v ? "是" : "否"),
|
},
|
{
|
label: "审批状态",
|
prop: "approvalStatus",
|
width: 100,
|
dataType: "tag",
|
formatData: (v) => approvalStatusLabel(v),
|
formatType: (v) => approvalStatusTagType(v),
|
},
|
{
|
label: "创建时间",
|
prop: "createTime",
|
width: 170,
|
formatData: (v) => formatDisplayTime(v),
|
},
|
{
|
dataType: "action",
|
label: "操作",
|
align: "center",
|
fixed: "right",
|
width: 240,
|
operation: [
|
{ name: "详情", type: "text", clickFun: (row) => openDetail(row) },
|
{
|
name: "修改",
|
type: "text",
|
disabled: (row) => row.approvalStatus !== "pending",
|
clickFun: (row) => openEditDialog(row),
|
},
|
{
|
name: "审批",
|
type: "text",
|
disabled: (row) => row.approvalStatus !== "pending" || !row.isApprove,
|
clickFun: (row) => openApprove(row),
|
},
|
{
|
name: "删除",
|
type: "danger",
|
clickFun: (row) => removeInstance(row),
|
},
|
],
|
},
|
]);
|
|
async function fetchApprovalList() {
|
tableLoading.value = true;
|
try {
|
const res = await listApprovalInstancePage(
|
buildApprovalInstanceListParams({ page, searchForm })
|
);
|
const { records, total } = unwrapInstancePage(res);
|
tableData.value = records.map(mapInstanceFromApi);
|
page.total = total;
|
} catch {
|
tableData.value = [];
|
page.total = 0;
|
ElMessage.error("审批列表加载失败");
|
} finally {
|
tableLoading.value = false;
|
}
|
}
|
|
async function loadSubmitTemplates() {
|
submitTemplatesLoading.value = true;
|
try {
|
const [typeOptions, customRes] = await Promise.all([
|
fetchBusinessTypeOptions(),
|
listApprovalTemplate(TEMPLATE_TYPE_CUSTOM),
|
]);
|
submitBusinessTypeOptions.value = typeOptions;
|
allSubmitTemplates.value = unwrapTemplateList(customRes)
|
.filter((row) => mapEnabledFromApi(row.enabled))
|
.map(mapSubmitTemplateCard);
|
} catch {
|
submitBusinessTypeOptions.value = [];
|
allSubmitTemplates.value = [];
|
ElMessage.error("加载审批模板失败");
|
} finally {
|
submitTemplatesLoading.value = false;
|
}
|
}
|
|
function handleQuery() {
|
page.current = 1;
|
fetchApprovalList();
|
}
|
|
function resetSearch() {
|
searchForm.approvalType = "";
|
searchForm.applicantKeyword = "";
|
searchForm.createTimeRange = [];
|
handleQuery();
|
}
|
|
function pagination({ page: p, limit }) {
|
page.current = p;
|
page.size = limit;
|
fetchApprovalList();
|
}
|
|
function openDetail(row) {
|
detailRow.value = { ...row };
|
detailDialog.visible = true;
|
}
|
|
function openApprove(row) {
|
approveDialog.row = { ...row };
|
approveOpinion.value = "";
|
approveDialog.visible = true;
|
}
|
|
function resetSubmitDialogState() {
|
submitDialog.mode = "add";
|
submitDialog.step = 1;
|
selectedBusinessType.value = "";
|
submitEditRow.value = null;
|
Object.assign(submitForm, createEmptySubmitForm(""));
|
}
|
|
function openSubmitDialog() {
|
resetSubmitDialogState();
|
submitDialog.visible = true;
|
loadSubmitTemplates();
|
}
|
|
function openEditDialog(row) {
|
if (row?.approvalStatus !== "pending") {
|
ElMessage.warning("仅审核中的审批可修改");
|
return;
|
}
|
if (!row?.id) {
|
ElMessage.warning("无法修改:缺少审批实例 ID");
|
return;
|
}
|
submitDialog.mode = "edit";
|
submitDialog.step = 3;
|
submitEditRow.value = { ...row };
|
Object.assign(submitForm, buildEditFormFromInstanceRow(row));
|
submitDialog.visible = true;
|
}
|
|
async function onTemplatePick(card) {
|
if (!card?.id) return;
|
submitTemplatesLoading.value = true;
|
try {
|
const res = await getApprovalTemplateDetail(card.id);
|
const mapped = mapTemplateFromApi(unwrapTemplateDetail(res));
|
const tpl = {
|
...buildSubmitTemplateFromRow(mapped),
|
templateId: mapped.id,
|
};
|
const base = createEmptySubmitForm(String(card.id), tpl, mapped.flowNodes);
|
Object.assign(submitForm, {
|
...base,
|
templateName: mapped.templateName || tpl.label || "",
|
businessType: mapped.businessType ?? card.businessType ?? selectedBusinessType.value,
|
templateSnapshot: tpl,
|
formFieldDefs: tpl.fields || [],
|
});
|
submitDialog.step = 3;
|
} catch {
|
ElMessage.error("加载模板详情失败");
|
} finally {
|
submitTemplatesLoading.value = false;
|
}
|
}
|
|
function onBusinessTypePick(type) {
|
if (!countTemplatesByBusinessType(type)) {
|
ElMessage.warning("该类型下暂无可用审批模板");
|
return;
|
}
|
selectedBusinessType.value = type;
|
submitDialog.step = 2;
|
}
|
|
function backToBusinessTypePick() {
|
selectedBusinessType.value = "";
|
submitDialog.step = 1;
|
}
|
|
function backToTemplatePick() {
|
submitDialog.step = 2;
|
}
|
|
async function submitInstanceForm() {
|
if (submitDialog.mode === "edit") return submitEditApproval();
|
return submitNewApproval();
|
}
|
|
async function submitNewApproval() {
|
if (!submitFormRef.value) return false;
|
try {
|
await submitFormRef.value.validate();
|
} catch {
|
return false;
|
}
|
if (!activeTemplate.value) return false;
|
const flowCheck = validateSubmitFlowNodes(submitForm.flowNodes);
|
if (!flowCheck.ok) {
|
ElMessage.warning(flowCheck.message);
|
return false;
|
}
|
if (!submitForm.templateId) {
|
ElMessage.warning("缺少模板 ID,无法提交");
|
return false;
|
}
|
if (submitSaving.value) return false;
|
submitSaving.value = true;
|
try {
|
await saveApprovalInstance(
|
buildInstanceDto({
|
submitForm,
|
activeTemplate: activeTemplate.value,
|
userStore,
|
flowNodes: flowCheck.nodes,
|
})
|
);
|
submitDialog.visible = false;
|
page.current = 1;
|
await fetchApprovalList();
|
return true;
|
} catch {
|
return false;
|
} finally {
|
submitSaving.value = false;
|
}
|
}
|
|
async function submitEditApproval() {
|
if (!submitFormRef.value) return false;
|
try {
|
await submitFormRef.value.validate();
|
} catch {
|
return false;
|
}
|
if (!activeTemplate.value) return false;
|
const flowCheck = validateSubmitFlowNodes(submitForm.flowNodes);
|
if (!flowCheck.ok) {
|
ElMessage.warning(flowCheck.message);
|
return false;
|
}
|
if (!submitForm.instanceId) {
|
ElMessage.warning("缺少审批实例 ID,无法保存");
|
return false;
|
}
|
if (submitSaving.value) return false;
|
submitSaving.value = true;
|
try {
|
await updateApprovalInstance(
|
buildInstanceDto({
|
submitForm,
|
activeTemplate: activeTemplate.value,
|
flowNodes: flowCheck.nodes,
|
existingRow: submitEditRow.value,
|
})
|
);
|
submitDialog.visible = false;
|
await fetchApprovalList();
|
if (detailDialog.visible && detailRow.value?.id === submitForm.instanceId) {
|
const hit = tableData.value.find((r) => r.id === submitForm.instanceId);
|
if (hit) detailRow.value = { ...hit };
|
else detailDialog.visible = false;
|
}
|
return true;
|
} catch {
|
return false;
|
} finally {
|
submitSaving.value = false;
|
}
|
}
|
|
async function removeInstance(row) {
|
if (row?.id == null || row.id === "") {
|
ElMessage.warning("无法删除:缺少审批实例 ID");
|
return;
|
}
|
const title = row.title || row.templateName || row.instanceNo || "该审批";
|
try {
|
await ElMessageBox.confirm(
|
`确定要删除审批「${title}」吗?删除后不可恢复。`,
|
"删除确认",
|
{
|
type: "warning",
|
confirmButtonText: "确定删除",
|
cancelButtonText: "取消",
|
distinguishCancelAndClose: true,
|
autofocus: false,
|
}
|
);
|
} catch {
|
return;
|
}
|
try {
|
await deleteApprovalInstance([row.id]);
|
ElMessage.success("删除成功");
|
if (detailDialog.visible && detailRow.value?.id === row.id) {
|
detailDialog.visible = false;
|
}
|
if (approveDialog.visible && approveDialog.row?.id === row.id) {
|
approveDialog.visible = false;
|
}
|
await fetchApprovalList();
|
} catch {
|
/* 错误由拦截器提示 */
|
}
|
}
|
|
async function submitApprove(result) {
|
const row = approveDialog.row;
|
if (!row?.id) return { ok: false };
|
if (result === "rejected" && !(approveOpinion.value || "").trim()) {
|
return { needOpinion: true };
|
}
|
if (approveSubmitting.value) return { ok: false };
|
approveSubmitting.value = true;
|
try {
|
await approveApprovalInstance(
|
buildApproveInstanceDto(row, result, approveOpinion.value)
|
);
|
approveDialog.visible = false;
|
await fetchApprovalList();
|
if (detailDialog.visible && detailRow.value?.id === row.id) {
|
const hit = tableData.value.find((r) => r.id === row.id);
|
if (hit) detailRow.value = { ...hit };
|
else detailDialog.visible = false;
|
}
|
return { ok: true, result };
|
} catch {
|
ElMessage.error("审批操作失败");
|
return { ok: false };
|
} finally {
|
approveSubmitting.value = false;
|
}
|
}
|
|
function approvalActionLabel(result) {
|
if (result === "approved") return "通过";
|
if (result === "rejected") return "驳回";
|
return "待处理";
|
}
|
|
return {
|
Search,
|
APPROVAL_TYPE_OPTIONS,
|
approvalTypeLabel,
|
approvalStatusLabel,
|
approvalStatusTagType,
|
approvalActionLabel,
|
searchForm,
|
tableLoading,
|
page,
|
tableData,
|
tableColumn,
|
detailDialog,
|
detailRow,
|
approveDialog,
|
approveOpinion,
|
approveSubmitting,
|
submitDialog,
|
isSubmitEdit,
|
submitDialogTitle,
|
submitForm,
|
submitFormRef,
|
submitSaving,
|
activeTemplate,
|
submitFormFields,
|
submitFormRules,
|
submitBusinessTypeOptions,
|
submitTemplateCards,
|
selectedBusinessType,
|
selectedBusinessTypeLabel,
|
businessTypeLabel,
|
countTemplatesByBusinessType,
|
submitTemplatesLoading,
|
handleQuery,
|
resetSearch,
|
pagination,
|
resetSubmitDialogState,
|
openSubmitDialog,
|
openEditDialog,
|
onBusinessTypePick,
|
onTemplatePick,
|
backToBusinessTypePick,
|
backToTemplatePick,
|
submitInstanceForm,
|
submitNewApproval,
|
submitApprove,
|
openDetail,
|
openApprove,
|
fetchApprovalList,
|
};
|
}
|