Files
cn-rdms-web/src/views/personal-center/my-item/modules/personal-item-bind-execution-dialog.vue

244 lines
6.0 KiB
Vue
Raw Normal View History

2026-05-19 10:59:07 +08:00
<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue';
import {
fetchGetMyOwnedProjectPage,
fetchGetMyParticipatedProjectPage,
fetchGetProjectExecutionPage
} from '@/service/api';
2026-05-19 10:59:07 +08:00
import { useForm, useFormRules } from '@/hooks/common/form';
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
defineOptions({ name: 'PersonalItemBindExecutionDialog' });
interface Props {
selectedCount: number;
submitLoading?: boolean;
}
interface Emits {
(e: 'submit', payload: { executionId: string }): void;
}
const props = withDefaults(defineProps<Props>(), {
submitLoading: false
});
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
const { formRef, validate } = useForm();
const { createRequiredRule } = useFormRules();
interface ProjectOption {
label: string;
value: string;
}
interface ExecutionOption {
label: string;
value: string;
}
const projectLoading = ref(false);
const executionLoading = ref(false);
const projectOptions = ref<ProjectOption[]>([]);
const executionOptions = ref<ExecutionOption[]>([]);
const loading = computed(() => projectLoading.value || executionLoading.value);
2026-05-19 10:59:07 +08:00
const model = reactive({
projectId: '',
2026-05-19 10:59:07 +08:00
executionId: ''
});
const rules = computed(
() =>
({
projectId: [createRequiredRule('请选择项目')],
2026-05-19 10:59:07 +08:00
executionId: [createRequiredRule('请选择执行')]
}) satisfies Record<string, App.Global.FormRule[]>
);
function createProjectOptions(
participated: Api.Project.MyParticipatedProjectItem[],
owned: Api.Project.MyOwnedProjectItem[]
): ProjectOption[] {
const optionMap = new Map<string, ProjectOption>();
owned.forEach(item => {
if (!item.id || !item.name?.trim()) return;
optionMap.set(item.id, {
label: item.name.trim(),
value: item.id
});
});
participated.forEach(item => {
if (!item.id || !item.name?.trim()) return;
if (item.statusCode === 'completed' || item.statusCode === 'cancelled' || item.statusCode === 'archived') {
return;
}
if (!optionMap.has(item.id)) {
optionMap.set(item.id, {
label: item.name.trim(),
value: item.id
});
}
});
return Array.from(optionMap.values());
}
async function loadProjectOptions() {
projectLoading.value = true;
const [participatedResult, ownedResult] = await Promise.all([
fetchGetMyParticipatedProjectPage({ pageNo: 1, pageSize: -1 }),
fetchGetMyOwnedProjectPage({ pageNo: 1, pageSize: -1 })
]);
projectLoading.value = false;
if (participatedResult.error || ownedResult.error || !participatedResult.data || !ownedResult.data) {
projectOptions.value = [];
executionOptions.value = [];
return;
2026-05-19 10:59:07 +08:00
}
projectOptions.value = createProjectOptions(participatedResult.data.list, ownedResult.data.list);
2026-05-19 10:59:07 +08:00
}
async function loadExecutionOptions(projectId: string) {
if (!projectId) {
executionOptions.value = [];
return;
}
executionLoading.value = true;
const { error, data } = await fetchGetProjectExecutionPage(projectId, {
pageNo: 1,
pageSize: -1
});
executionLoading.value = false;
2026-05-19 10:59:07 +08:00
if (error || !data) {
executionOptions.value = [];
return;
}
executionOptions.value = data.list
.filter(item => !item.terminal && item.statusCode !== 'completed' && item.statusCode !== 'cancelled')
.map(item => ({
label: item.executionName,
value: item.id
}));
2026-05-19 10:59:07 +08:00
}
async function initDialog() {
model.projectId = '';
2026-05-19 10:59:07 +08:00
model.executionId = '';
executionOptions.value = [];
await loadProjectOptions();
2026-05-19 10:59:07 +08:00
formRef.value?.clearValidate();
}
async function handleConfirm() {
await validate();
emit('submit', {
executionId: model.executionId
});
}
watch(
() => visible.value,
value => {
if (value) {
initDialog();
}
}
);
watch(
() => model.projectId,
async (projectId, previousProjectId) => {
if (projectId === previousProjectId) return;
model.executionId = '';
if (!projectId) {
executionOptions.value = [];
formRef.value?.clearValidate('executionId');
return;
}
await loadExecutionOptions(projectId);
formRef.value?.clearValidate('executionId');
}
);
2026-05-19 10:59:07 +08:00
</script>
<template>
<BusinessFormDialog
v-model="visible"
title="批量关联执行"
preset="md"
2026-05-19 10:59:07 +08:00
:loading="loading"
:confirm-loading="props.submitLoading"
@confirm="handleConfirm"
>
<ElAlert
:title="`已选中 ${props.selectedCount} 条我的事项,关联成功后这些事项会从当前列表移除。`"
2026-05-19 10:59:07 +08:00
type="info"
:closable="false"
class="mb-16px"
/>
<ElForm ref="formRef" :model="model" :rules="rules" label-position="top" :validate-on-rule-change="false">
<ElRow :gutter="16">
<ElCol :span="12">
<ElFormItem label="项目" prop="projectId">
<ElSelect
v-model="model.projectId"
clearable
filterable
placeholder="请选择项目"
class="w-full"
:loading="projectLoading"
>
<ElOption
v-for="option in projectOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="执行" prop="executionId">
<ElSelect
v-model="model.executionId"
clearable
filterable
placeholder="请选择执行"
class="w-full"
:loading="executionLoading"
:disabled="!model.projectId"
>
<ElOption
v-for="option in executionOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</ElSelect>
</ElFormItem>
</ElCol>
</ElRow>
2026-05-19 10:59:07 +08:00
</ElForm>
</BusinessFormDialog>
</template>
<style scoped></style>