244 lines
6.0 KiB
Vue
244 lines
6.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed, reactive, ref, watch } from 'vue';
|
|
import {
|
|
fetchGetMyOwnedProjectPage,
|
|
fetchGetMyParticipatedProjectPage,
|
|
fetchGetProjectExecutionPage
|
|
} from '@/service/api';
|
|
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);
|
|
|
|
const model = reactive({
|
|
projectId: '',
|
|
executionId: ''
|
|
});
|
|
|
|
const rules = computed(
|
|
() =>
|
|
({
|
|
projectId: [createRequiredRule('请选择项目')],
|
|
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;
|
|
}
|
|
|
|
projectOptions.value = createProjectOptions(participatedResult.data.list, ownedResult.data.list);
|
|
}
|
|
|
|
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;
|
|
|
|
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
|
|
}));
|
|
}
|
|
|
|
async function initDialog() {
|
|
model.projectId = '';
|
|
model.executionId = '';
|
|
executionOptions.value = [];
|
|
await loadProjectOptions();
|
|
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');
|
|
}
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<BusinessFormDialog
|
|
v-model="visible"
|
|
title="批量关联执行"
|
|
preset="md"
|
|
:loading="loading"
|
|
:confirm-loading="props.submitLoading"
|
|
@confirm="handleConfirm"
|
|
>
|
|
<ElAlert
|
|
:title="`已选中 ${props.selectedCount} 条我的事项,关联成功后这些事项会从当前列表移除。`"
|
|
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>
|
|
</ElForm>
|
|
</BusinessFormDialog>
|
|
</template>
|
|
|
|
<style scoped></style>
|