Files
cn-rdms-web/src/views/infra/state-machine/index.vue

396 lines
12 KiB
Vue

<script setup lang="tsx">
import { computed, nextTick, onActivated, reactive, ref } from 'vue';
import type { TableInstance } from 'element-plus';
import { ElTag } from 'element-plus';
import { useBoolean } from '@sa/hooks';
import { OBJECT_STATUS_MODEL_OBJECT_TYPE_DICT_CODE } from '@/constants/dict';
import {
fetchBatchDeleteObjectStatusModel,
fetchDeleteObjectStatusModel,
fetchGetObjectStatusModelPage
} from '@/service/api';
import { useAuth } from '@/hooks/business/auth';
import { useDict } from '@/hooks/business/dict';
import { useUIPaginatedTable } from '@/hooks/common/table';
import BusinessTableActionCell, { type BusinessTableAction } from '@/components/custom/business-table-action-cell';
import StateMachineOperateDialog from './modules/state-machine-operate-dialog.vue';
import StateMachineSearch from './modules/state-machine-search.vue';
import StateTransitionDialog from './modules/state-transition-dialog.vue';
import { formatDateTime, getBooleanLabel, getBooleanTagType, getStatusLabel, getStatusTagType } from './shared';
import IconMdiDeleteOutline from '~icons/mdi/delete-outline';
import IconMdiPencilOutline from '~icons/mdi/pencil-outline';
import IconMdiSourceBranch from '~icons/mdi/source-branch';
defineOptions({ name: 'StateMachineManage' });
function getInitSearchParams(): Api.Infra.ObjectStatusModelSearchParams {
return {
pageNo: 1,
pageSize: 10,
keyword: undefined,
objectType: undefined,
status: undefined,
initialFlag: undefined,
terminalFlag: undefined
};
}
function transformPageResult(
response: Awaited<ReturnType<typeof fetchGetObjectStatusModelPage>>,
pageNo: number,
pageSize: number
) {
if (!response.error) {
return {
data: response.data.list,
pageNum: pageNo,
pageSize,
total: response.data.total
};
}
return {
data: [],
pageNum: pageNo,
pageSize,
total: 0
};
}
const searchParams = reactive(getInitSearchParams());
const stateTableRef = ref<TableInstance>();
const checkedRowKeys = ref<string[]>([]);
const { getLabel: getObjectTypeLabel } = useDict(OBJECT_STATUS_MODEL_OBJECT_TYPE_DICT_CODE);
const { hasAuth } = useAuth();
const canDeleteStateMachine = computed(() => hasAuth('infra:state-machine:delete'));
const canUpdateStateMachine = computed(() => hasAuth('infra:state-machine:update'));
const canManageStateTransition = computed(() => hasAuth('infra:state-transition:manage'));
function getStatusModelActions(row: Api.Infra.ObjectStatusModel): BusinessTableAction[] {
const actions: BusinessTableAction[] = [];
if (canManageStateTransition.value) {
actions.push({
key: 'transition',
label: '状态流转',
icon: IconMdiSourceBranch,
buttonType: 'primary',
onClick: () => openTransitionDialog(row)
});
}
if (canUpdateStateMachine.value) {
actions.push({
key: 'edit',
label: '编辑',
icon: IconMdiPencilOutline,
buttonType: 'primary',
onClick: () => openEdit(row)
});
}
if (canDeleteStateMachine.value) {
actions.push({
key: 'delete',
label: '删除',
icon: IconMdiDeleteOutline,
buttonType: 'danger',
onClick: () => handleDeleteAction(row)
});
}
return actions;
}
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useUIPaginatedTable({
paginationProps: {
currentPage: searchParams.pageNo,
pageSize: searchParams.pageSize
},
api: () => fetchGetObjectStatusModelPage(searchParams),
transform: response => transformPageResult(response, searchParams.pageNo ?? 1, searchParams.pageSize ?? 10),
onPaginationParamsChange: params => {
searchParams.pageNo = params.currentPage ?? 1;
searchParams.pageSize = params.pageSize ?? 10;
},
columns: () => [
{ prop: 'selection', type: 'selection', width: 48 },
{ prop: 'index', type: 'index', label: '序号', width: 64 },
{
prop: 'objectType',
label: '对象类型',
minWidth: 130,
formatter: row => getObjectTypeLabel(row.objectType)
},
{ prop: 'statusName', label: '状态名称', minWidth: 140, showOverflowTooltip: true },
{ prop: 'statusCode', label: '状态编码', minWidth: 160, showOverflowTooltip: true },
{
prop: 'status',
label: '配置状态',
width: 110,
align: 'center',
formatter: row => <ElTag type={getStatusTagType(row.status)}>{getStatusLabel(row.status)}</ElTag>
},
{
prop: 'initialFlag',
label: '初始状态',
width: 110,
align: 'center',
formatter: row => <ElTag type={getBooleanTagType(row.initialFlag)}>{getBooleanLabel(row.initialFlag)}</ElTag>
},
{
prop: 'terminalFlag',
label: '终态',
width: 90,
align: 'center',
formatter: row => <ElTag type={getBooleanTagType(row.terminalFlag)}>{getBooleanLabel(row.terminalFlag)}</ElTag>
},
{
prop: 'allowEdit',
label: '允许编辑主数据',
width: 140,
align: 'center',
formatter: row => <ElTag type={getBooleanTagType(row.allowEdit)}>{getBooleanLabel(row.allowEdit)}</ElTag>
},
// {
// prop: 'progressExcludedFlag',
// label: '不参与上层进度统计',
// width: 160,
// align: 'center',
// formatter: row => (
// <ElTag type={getBooleanTagType(row.progressExcludedFlag)}>{getBooleanLabel(row.progressExcludedFlag)}</ElTag>
// )
// },
// {
// prop: 'allowCreateProject',
// label: '允许新建项目',
// width: 130,
// align: 'center',
// formatter: row => (
// <ElTag type={getBooleanTagType(row.allowCreateProject)}>{getBooleanLabel(row.allowCreateProject)}</ElTag>
// )
// },
// {
// prop: 'allowCreateRequirement',
// label: '允许新增需求',
// width: 130,
// align: 'center',
// formatter: row => (
// <ElTag type={getBooleanTagType(row.allowCreateRequirement)}>
// {getBooleanLabel(row.allowCreateRequirement)}
// </ElTag>
// )
// },
{ prop: 'sort', label: '排序', width: 90, align: 'center' },
{
prop: 'remark',
label: '备注',
minWidth: 180,
showOverflowTooltip: true,
formatter: row => row.remark || '--'
},
{
prop: 'createTime',
label: '创建时间',
minWidth: 170,
formatter: row => formatDateTime(row.createTime)
},
{
prop: 'operate',
label: '操作',
width: 220,
align: 'center',
fixed: 'right',
formatter: row => {
const actions = getStatusModelActions(row);
if (!actions.length) {
return <span>--</span>;
}
return <BusinessTableActionCell actions={actions} variant="icon" />;
}
}
]
});
const { bool: operateVisible, setTrue: openOperateModal, setFalse: closeOperateModal } = useBoolean();
const operateType = ref<UI.TableOperateType>('add');
const editingData = ref<Api.Infra.ObjectStatusModel | null>(null);
const { bool: transitionVisible, setTrue: openTransitionModal, setFalse: closeTransitionModal } = useBoolean();
const transitionRow = ref<Api.Infra.ObjectStatusModel | null>(null);
function openAdd() {
operateType.value = 'add';
editingData.value = null;
openOperateModal();
}
function openEdit(item: Api.Infra.ObjectStatusModel) {
operateType.value = 'edit';
editingData.value = item;
openOperateModal();
}
function openTransitionDialog(item: Api.Infra.ObjectStatusModel) {
transitionRow.value = item;
openTransitionModal();
}
async function handleDelete(item: Api.Infra.ObjectStatusModel) {
const { error } = await fetchDeleteObjectStatusModel(item.id);
if (error) {
return;
}
window.$message?.success('删除成功');
await reloadStatusTable();
}
async function handleDeleteAction(row: Api.Infra.ObjectStatusModel) {
try {
await window.$messageBox?.confirm('确认删除当前状态模型吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
await handleDelete(row);
}
async function handleBatchDelete() {
if (!checkedRowKeys.value.length) {
return;
}
const { error } = await fetchBatchDeleteObjectStatusModel(checkedRowKeys.value);
if (error) {
return;
}
window.$message?.success('删除成功');
await reloadStatusTable();
}
function handleSelectionChange(rows: Api.Infra.ObjectStatusModel[]) {
checkedRowKeys.value = rows.map(item => item.id);
}
async function reloadStatusTable(page = searchParams.pageNo) {
checkedRowKeys.value = [];
await getDataByPage(page);
await nextTick();
stateTableRef.value?.clearSelection();
}
function resetSearchParams() {
Object.assign(searchParams, getInitSearchParams());
reloadStatusTable(1);
}
function handleSearch() {
reloadStatusTable(1);
}
function handleSubmitted() {
closeOperateModal();
reloadStatusTable();
}
onActivated(() => {
resetSearchParams();
});
</script>
<template>
<div class="flex-col-stretch gap-16px overflow-hidden">
<StateMachineSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
<template #header>
<div class="flex items-center justify-between gap-12px">
<div class="flex items-center gap-10px">
<p>状态模型列表</p>
<ElTag effect="plain">{{ mobilePagination.total || data.length }}</ElTag>
</div>
<TableHeaderOperation
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
@refresh="getData"
>
<template #default>
<ElButton v-auth="'infra:state-machine:create'" plain type="primary" @click="openAdd">
<template #icon>
<icon-ic-round-plus class="text-icon" />
</template>
新增
</ElButton>
<ElPopconfirm
v-if="canDeleteStateMachine"
title="确认删除选中的状态模型吗?"
@confirm="handleBatchDelete"
>
<template #reference>
<ElButton type="danger" plain :disabled="checkedRowKeys.length === 0">
<template #icon>
<icon-ic-round-delete class="text-icon" />
</template>
批量删除
</ElButton>
</template>
</ElPopconfirm>
</template>
</TableHeaderOperation>
</div>
</template>
<div class="flex-1">
<ElTable
ref="stateTableRef"
v-loading="loading"
height="100%"
border
row-key="id"
:data="data"
@selection-change="handleSelectionChange"
>
<ElTableColumn v-for="col in columns" :key="String(col.prop)" v-bind="col" />
</ElTable>
</div>
<div class="mt-20px flex justify-end">
<ElPagination
v-if="mobilePagination.total"
layout="total,prev,pager,next,sizes"
v-bind="mobilePagination"
@current-change="mobilePagination['current-change']"
@size-change="mobilePagination['size-change']"
/>
</div>
</ElCard>
<StateMachineOperateDialog
v-model:visible="operateVisible"
:operate-type="operateType"
:row-data="editingData"
@submitted="handleSubmitted"
/>
<StateTransitionDialog
v-model:visible="transitionVisible"
:current-status="transitionRow"
@update:visible="value => !value && closeTransitionModal()"
/>
</div>
</template>
<style scoped></style>