fix(review-meeting): 修复编辑页草稿与时间同步
parent
8e85d4b768
commit
0194af250d
|
|
@ -1,4 +1,6 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import {
|
||||
buildProjectTimeBatchPayload,
|
||||
mapCopiedProjectItems,
|
||||
|
|
@ -50,4 +52,24 @@ assert.deepEqual(buildProjectTimeBatchPayload([baseProject]), {
|
|||
]
|
||||
})
|
||||
|
||||
const meetingEditSource = fs.readFileSync(
|
||||
path.resolve(process.cwd(), 'src/views/review/meeting/MeetingEdit.vue'),
|
||||
'utf8'
|
||||
)
|
||||
assert.match(
|
||||
meetingEditSource,
|
||||
/mapPersistedProjectItems/,
|
||||
'MeetingEdit.vue 应该接入编辑态项目映射,避免把现有项目 id 回填到 sourceProjectId'
|
||||
)
|
||||
assert.match(
|
||||
meetingEditSource,
|
||||
/mapCopiedProjectItems/,
|
||||
'MeetingEdit.vue 应该接入复制态项目映射,保留首次复制资料所需的 sourceProjectId'
|
||||
)
|
||||
assert.match(
|
||||
meetingEditSource,
|
||||
/updateReviewProjectTimeBatch/,
|
||||
'MeetingEdit.vue 应该在会议起始时间变化后同步已落库项目时间'
|
||||
)
|
||||
|
||||
console.log('reviewMeetingEditHelpers checks passed')
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormRules } from 'element-plus'
|
||||
|
|
@ -221,7 +221,7 @@ import {
|
|||
type ReviewMeetingSaveReqVO,
|
||||
type ReviewProjectItemVO
|
||||
} from '@/api/review/meeting'
|
||||
import { getReviewProjectPage } from '@/api/review/project'
|
||||
import { getReviewProjectPage, updateReviewProjectTimeBatch } from '@/api/review/project'
|
||||
import { getExpertUserList } from '@/api/system/user/index'
|
||||
import download from '@/utils/download'
|
||||
import ExpertSelectTable from './components/ExpertSelectTable.vue'
|
||||
|
|
@ -230,6 +230,13 @@ import {
|
|||
buildScheduledProjectItems,
|
||||
DEFAULT_REVIEW_MEETING_INTERVAL_MINUTES
|
||||
} from './projectSchedule'
|
||||
import {
|
||||
buildProjectTimeBatchPayload,
|
||||
mapCopiedProjectItems,
|
||||
mapPersistedProjectItems,
|
||||
shouldSyncPersistedProjectTimes,
|
||||
type MeetingEditProjectItem
|
||||
} from './meetingEditHelpers'
|
||||
|
||||
defineOptions({ name: 'ReviewMeetingEdit' })
|
||||
|
||||
|
|
@ -269,6 +276,7 @@ type FormData = ReviewMeetingSaveReqVO & {
|
|||
minutesAiStatusName?: string
|
||||
minutesAiErrorMessage?: string
|
||||
minutesAiUpdatedTime?: string
|
||||
projects: MeetingEditProjectItem[]
|
||||
}
|
||||
|
||||
const formData = reactive<FormData>({
|
||||
|
|
@ -311,6 +319,7 @@ const rules: FormRules = {
|
|||
}
|
||||
|
||||
const formRef = ref()
|
||||
const originalMeetingStart = ref<string | number | undefined>()
|
||||
|
||||
const getMinutesAiTagType = (status?: number) => {
|
||||
if (status === 2) return 'success'
|
||||
|
|
@ -319,25 +328,26 @@ const getMinutesAiTagType = (status?: number) => {
|
|||
return 'info'
|
||||
}
|
||||
|
||||
const mapProjectItems = (projects: any[]): ReviewProjectItemVO[] =>
|
||||
(projects || []).map((item: any) => ({
|
||||
sourceProjectId: item.sourceProjectId ?? item.id,
|
||||
seqNo: item.seqNo,
|
||||
startTime: item.startTime,
|
||||
endTime: item.endTime,
|
||||
agendaCategory: item.agendaCategory,
|
||||
projectTitle: item.projectTitle,
|
||||
reporter: item.reporter,
|
||||
reporterUnit: item.reporterUnit,
|
||||
reviewDate: item.reviewDate
|
||||
}))
|
||||
|
||||
const resetProjectReviewDate = (projects: ReviewProjectItemVO[]): ReviewProjectItemVO[] =>
|
||||
const resetProjectReviewDate = (projects: MeetingEditProjectItem[]): MeetingEditProjectItem[] =>
|
||||
(projects || []).map((item) => ({
|
||||
...item,
|
||||
reviewDate: undefined
|
||||
}))
|
||||
|
||||
const buildPreviewScheduledProjects = (projects: MeetingEditProjectItem[] = formData.projects): MeetingEditProjectItem[] => {
|
||||
const projectsWithReviewDate = applyDefaultReviewDate(projects, formData.meetingTimeRange) as MeetingEditProjectItem[]
|
||||
return (
|
||||
buildScheduledProjectItems(
|
||||
projectsWithReviewDate,
|
||||
formData.meetingTimeRange?.[0],
|
||||
DEFAULT_REVIEW_MEETING_INTERVAL_MINUTES
|
||||
) || projectsWithReviewDate
|
||||
)
|
||||
}
|
||||
|
||||
const stripProjectTransientFields = (projects: MeetingEditProjectItem[]): ReviewProjectItemVO[] =>
|
||||
(projects || []).map(({ id: _id, ...item }) => item)
|
||||
|
||||
const loadDetail = async (id: number) => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
|
|
@ -346,7 +356,7 @@ const loadDetail = async (id: number) => {
|
|||
getReviewProjectPage({ reviewMeetingId: id, pageNo: 1, pageSize: 200 })
|
||||
])
|
||||
Object.assign(formData, detail)
|
||||
formData.projects = mapProjectItems(projectData?.list ?? [])
|
||||
formData.projects = mapPersistedProjectItems(projectData?.list ?? [])
|
||||
if (detail.startTime && detail.endTime) {
|
||||
formData.meetingTimeRange = [
|
||||
new Date(detail.startTime.replace(' ', 'T')).getTime(),
|
||||
|
|
@ -359,6 +369,7 @@ const loadDetail = async (id: number) => {
|
|||
new Date(detail.materialViewEndTime.replace(' ', 'T')).getTime()
|
||||
]
|
||||
}
|
||||
originalMeetingStart.value = formData.meetingTimeRange?.[0]
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
|
@ -400,7 +411,7 @@ const loadCopySource = async (id: number) => {
|
|||
formData.minutesAiErrorMessage = undefined
|
||||
formData.minutesAiUpdatedTime = undefined
|
||||
formData.expertIds = detail.expertIds || []
|
||||
formData.projects = resetProjectReviewDate(mapProjectItems(projectData?.list ?? []))
|
||||
formData.projects = resetProjectReviewDate(mapCopiedProjectItems(projectData?.list ?? []))
|
||||
isProjectsModified.value = false
|
||||
|
||||
if (detail.startTime && detail.endTime) {
|
||||
|
|
@ -419,6 +430,7 @@ const loadCopySource = async (id: number) => {
|
|||
} else {
|
||||
formData.materialViewTimeRange = undefined
|
||||
}
|
||||
originalMeetingStart.value = formData.meetingTimeRange?.[0]
|
||||
|
||||
ElMessage.info('已带入会议信息和评审项目;保存草稿后将同步复制项目资料,议程附件与会议纪要不会复制')
|
||||
} finally {
|
||||
|
|
@ -437,6 +449,16 @@ onMounted(async () => {
|
|||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => formData.meetingTimeRange?.[0],
|
||||
(newValue, oldValue) => {
|
||||
if (newValue === oldValue || oldValue === undefined || !formData.projects.length) {
|
||||
return
|
||||
}
|
||||
formData.projects = buildPreviewScheduledProjects(formData.projects)
|
||||
}
|
||||
)
|
||||
|
||||
const handleExcelChange = async (uploadFile: UploadFile) => {
|
||||
if (!uploadFile.raw) return
|
||||
if (formData.projects && formData.projects.length > 0) {
|
||||
|
|
@ -446,7 +468,8 @@ const handleExcelChange = async (uploadFile: UploadFile) => {
|
|||
try {
|
||||
const result = await importProjectsFromExcel(uploadFile.raw)
|
||||
const projects = applyDefaultReviewDate(result as ReviewProjectItemVO[], formData.meetingTimeRange)
|
||||
formData.projects = buildScheduledProjectItems(projects, formData.meetingTimeRange?.[0]) || projects
|
||||
formData.projects = (buildScheduledProjectItems(projects, formData.meetingTimeRange?.[0]) ||
|
||||
projects) as MeetingEditProjectItem[]
|
||||
isProjectsModified.value = true
|
||||
ElMessage.success(`成功解析 ${formData.projects.length} 个评审项目`)
|
||||
} catch {
|
||||
|
|
@ -560,8 +583,8 @@ const formatFileSize = (bytes?: number): string => {
|
|||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
||||
}
|
||||
|
||||
const buildScheduledProjects = (): ReviewProjectItemVO[] => {
|
||||
const projectsWithReviewDate = applyDefaultReviewDate(formData.projects, formData.meetingTimeRange)
|
||||
const buildScheduledProjects = (): MeetingEditProjectItem[] => {
|
||||
const projectsWithReviewDate = applyDefaultReviewDate(formData.projects, formData.meetingTimeRange) as MeetingEditProjectItem[]
|
||||
const hasCompleteSchedule = projectsWithReviewDate.every((item) => item.startTime && item.endTime)
|
||||
if (hasCompleteSchedule) {
|
||||
return projectsWithReviewDate
|
||||
|
|
@ -658,18 +681,34 @@ const submitForm = async () => {
|
|||
formLoading.value = true
|
||||
try {
|
||||
const projects = buildScheduledProjects()
|
||||
const submitProjects = stripProjectTransientFields(projects)
|
||||
const submitData: ReviewMeetingSaveReqVO & { projects?: ReviewProjectItemVO[] } = {
|
||||
...formData,
|
||||
projects
|
||||
projects: submitProjects
|
||||
}
|
||||
if (isEdit.value && !isProjectsModified.value) {
|
||||
delete submitData.projects
|
||||
}
|
||||
if (isEdit.value) {
|
||||
await updateReviewMeeting(submitData)
|
||||
const shouldSyncProjectTimes = shouldSyncPersistedProjectTimes({
|
||||
isEdit: isEdit.value,
|
||||
isProjectsModified: isProjectsModified.value,
|
||||
originalMeetingStart: originalMeetingStart.value,
|
||||
currentMeetingStart: formData.meetingTimeRange?.[0],
|
||||
projects
|
||||
})
|
||||
if (shouldSyncProjectTimes) {
|
||||
const timeBatchPayload = buildProjectTimeBatchPayload(projects)
|
||||
if (timeBatchPayload) {
|
||||
await updateReviewProjectTimeBatch(timeBatchPayload)
|
||||
}
|
||||
}
|
||||
originalMeetingStart.value = formData.meetingTimeRange?.[0]
|
||||
ElMessage.success('更新成功')
|
||||
} else {
|
||||
await createReviewMeeting(submitData)
|
||||
originalMeetingStart.value = formData.meetingTimeRange?.[0]
|
||||
ElMessage.success('创建成功,会议已保存为草稿')
|
||||
}
|
||||
router.push({ name: 'ReviewMeeting' })
|
||||
|
|
|
|||
Loading…
Reference in New Issue