统一状态流转为前端状态机

This commit is contained in:
Cheng Zhou
2025-12-17 20:52:55 +08:00
parent c074c2b5bc
commit c2328594b0
15 changed files with 361 additions and 188 deletions
@@ -1,64 +1,49 @@
<template>
<div class="actions">
<PermissionAction action="finance.create">
<el-button
v-if="item.status === 'DRAFT'"
type="primary"
size="small"
:loading="loading"
@click="change('SUBMITTED')"
>
提交
</el-button>
</PermissionAction>
<PermissionAction action="finance.approve">
<el-button
v-if="item.status === 'SUBMITTED'"
type="success"
size="small"
:loading="loading"
@click="change('APPROVED')"
>
审批通过
</el-button>
</PermissionAction>
<PermissionAction action="finance.approve">
<el-button
v-if="item.status === 'SUBMITTED'"
type="danger"
size="small"
:loading="loading"
@click="reject"
>
驳回
</el-button>
</PermissionAction>
<PermissionAction action="finance.pay">
<el-button
v-if="item.status === 'APPROVED'"
type="warning"
size="small"
:loading="loading"
@click="change('PAID')"
>
确认支付
</el-button>
</PermissionAction>
<template v-for="action in availableActions" :key="action.key">
<PermissionAction :action="permissionMap[action.key]">
<el-button
size="small"
:type="buttonType(action)"
:loading="loading"
@click="onAction(action)"
>
{{ action.label }}
</el-button>
</PermissionAction>
</template>
</div>
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from "element-plus";
import { ref } from "vue";
import { computed, ref } from "vue";
import { changeFinanceStatus } from "../api/finance";
import { useStudyStore } from "../store/study";
import PermissionAction from "./PermissionAction.vue";
import { financeMachine, getAvailableActions } from "../state-machine";
import type { ActionConfig } from "../state-machine";
const props = defineProps<{ item: any }>();
const emit = defineEmits(["success"]);
const study = useStudyStore();
const loading = ref(false);
const permissionMap: Record<string, string> = {
submit: "finance.create",
approve: "finance.approve",
reject: "finance.approve",
pay: "finance.pay",
};
const availableActions = computed(() => getAvailableActions(financeMachine, props.item?.status));
const buttonType = (action: ActionConfig) => {
if (action.danger) return "danger";
if (action.key === "approve") return "success";
if (action.key === "pay") return "warning";
return "primary";
};
const change = async (status: string, reject_reason?: string) => {
if (!study.currentStudy) return;
@@ -74,13 +59,23 @@ const change = async (status: string, reject_reason?: string) => {
}
};
const reject = async () => {
const reason = await ElMessageBox.prompt("请输入驳回原因", "驳回审批", {
confirmButtonText: "确定",
cancelButtonText: "取消",
}).catch(() => null);
if (!reason || !reason.value) return;
change("REJECTED", reason.value);
const onAction = async (action: ActionConfig) => {
if (action.confirm) {
const ok = await ElMessageBox.confirm(action.confirmText || "确认执行该操作?", "提示", {
type: action.danger ? "warning" : "info",
}).catch(() => null);
if (!ok) return;
}
if (action.key === "reject") {
const reason = await ElMessageBox.prompt("请输入驳回原因", "驳回审批", {
confirmButtonText: "确定",
cancelButtonText: "取消",
}).catch(() => null);
if (!reason || !reason.value) return;
await change(action.to, reason.value);
return;
}
await change(action.to);
};
</script>