| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- <script lang="ts" setup>
- import type { DeptOption, DetailItem, OperationMeeting } from './types'
- import MeetingDetailDrawer from './components/meeting-detail-drawer.vue'
- import OperationMeetingContent from './components/operation-meeting-content.vue'
- import { OperationMeetingApi } from '@/api/pms/meeting'
- import { useWindowSize } from '@vueuse/core'
- interface Props {
- visible: boolean
- meetingSeries?: string
- year?: string | number
- deptOptions?: DeptOption[]
- }
- interface OperationMeetingForm
- extends Omit<Partial<OperationMeeting>, 'meetingDate' | 'meetingSeries'> {
- meetingDate?: number | string | Date
- meetingSeries?: number
- }
- interface SummaryMeetingItem {
- key: string
- meeting: OperationMeetingForm
- details: DetailItem[]
- }
- const props = withDefaults(defineProps<Props>(), {
- meetingSeries: '',
- year: '',
- deptOptions: () => []
- })
- const emits = defineEmits<{
- 'update:visible': [visible: boolean]
- }>()
- const { width } = useWindowSize()
- const drawerSize = computed(() => (width.value <= 768 ? '100%' : '100%'))
- const loading = ref(false)
- const summaryMeetings = ref<SummaryMeetingItem[]>([])
- const detailDrawerVisible = ref(false)
- const detailForm = ref<DetailItem>(createDetailItem())
- function createDetailItem(): DetailItem {
- return {
- raw: {},
- projectName: '',
- currentRevenue: undefined,
- cumulativeRevenue: undefined,
- currentOnAccount: undefined,
- cumulativeOnAccount: undefined,
- currentPayment: undefined,
- cumulativePayment: undefined,
- plannedWorkload: '',
- actualCompletion: '',
- equipmentUtilizationRate: undefined,
- keyWorkCompletion: '',
- problemsAnalysis: '',
- nextPlannedWorkload: '',
- priorityTasks: ''
- }
- }
- const cloneDetailItem = (data?: Partial<DetailItem>): DetailItem => ({
- raw: data?.raw || {},
- projectName: data?.projectName || '',
- currentRevenue: data?.currentRevenue,
- cumulativeRevenue: data?.cumulativeRevenue,
- currentOnAccount: data?.currentOnAccount,
- cumulativeOnAccount: data?.cumulativeOnAccount,
- currentPayment: data?.currentPayment,
- cumulativePayment: data?.cumulativePayment,
- plannedWorkload: data?.plannedWorkload || '',
- actualCompletion: data?.actualCompletion || '',
- equipmentUtilizationRate: data?.equipmentUtilizationRate,
- keyWorkCompletion: data?.keyWorkCompletion || '',
- problemsAnalysis: data?.problemsAnalysis || '',
- nextPlannedWorkload: data?.nextPlannedWorkload || '',
- priorityTasks: data?.priorityTasks || ''
- })
- const parseNumberValue = (value: unknown) => {
- if (value === undefined || value === null || value === '') return undefined
- const parsed = Number(String(value).replace('%', ''))
- return Number.isNaN(parsed) ? undefined : parsed
- }
- const parseMeetingSeries = (value: unknown) => {
- if (value === undefined || value === null || value === '') return undefined
- const matched = String(value).match(/\d+/)
- return matched ? Number(matched[0]) : undefined
- }
- const normalizeDetailItem = (data?: Record<string, unknown>): DetailItem => ({
- raw: data || {},
- projectName: String(data?.projectName || ''),
- currentRevenue: parseNumberValue(data?.currentRevenue),
- cumulativeRevenue: parseNumberValue(data?.cumulativeRevenue),
- currentOnAccount: parseNumberValue(data?.currentOnAccount),
- cumulativeOnAccount: parseNumberValue(data?.cumulativeOnAccount),
- currentPayment: parseNumberValue(data?.currentPayment),
- cumulativePayment: parseNumberValue(data?.cumulativePayment),
- plannedWorkload: String(data?.plannedWorkload || ''),
- actualCompletion: String(data?.actualCompletion || ''),
- equipmentUtilizationRate: parseNumberValue(data?.equipmentUtilizationRate),
- keyWorkCompletion: String(data?.keyWorkCompletion || ''),
- problemsAnalysis: String(data?.problemsAnalysis || ''),
- nextPlannedWorkload: String(data?.nextPlannedWorkload || ''),
- priorityTasks: String(data?.priorityTasks || '')
- })
- const normalizeSummaryMeeting = (
- data: Record<string, unknown>,
- index: number
- ): SummaryMeetingItem => {
- const details = Array.isArray(data.details) ? data.details : []
- const id = data.id === undefined || data.id === null ? index : String(data.id)
- return {
- key: `${id}-${index}`,
- meeting: {
- id: parseNumberValue(data.id),
- deptId: parseNumberValue(data.deptId),
- companyName: String(data.companyName || ''),
- meetingDate: data.meetingDate as number | string | Date | undefined,
- support: String(data.support || ''),
- meetingSeries: parseMeetingSeries(data.meetingSeries)
- },
- details: details.map((item) => normalizeDetailItem(item as Record<string, unknown>))
- }
- }
- const resetForm = () => {
- summaryMeetings.value = []
- loading.value = false
- detailDrawerVisible.value = false
- detailForm.value = createDetailItem()
- }
- const handleVisibleChange = (visible: boolean) => {
- emits('update:visible', visible)
- if (!visible) {
- resetForm()
- }
- }
- const loadSummaryDetails = async () => {
- if (!props.meetingSeries || !props.year) {
- summaryMeetings.value = []
- return
- }
- const meetingSeries = props.meetingSeries
- const year = props.year
- loading.value = true
- try {
- const res = await OperationMeetingApi.getSummarizedProjectDetails({
- meetingSeries,
- year
- })
- if (!props.visible || props.meetingSeries !== meetingSeries || props.year !== year) return
- const meetings = Array.isArray(res) ? res : []
- summaryMeetings.value = meetings.map((item, index) =>
- normalizeSummaryMeeting(item as Record<string, unknown>, index)
- )
- } finally {
- loading.value = false
- }
- }
- const handleViewDetail = (row: DetailItem) => {
- detailForm.value = cloneDetailItem(row)
- detailDrawerVisible.value = true
- }
- const handleDetailDrawerChange = (visible: boolean) => {
- detailDrawerVisible.value = visible
- if (!visible) {
- detailForm.value = createDetailItem()
- }
- }
- watch(
- () => [props.visible, props.meetingSeries, props.year] as const,
- ([visible]) => {
- if (!visible) return
- loadSummaryDetails()
- }
- )
- </script>
- <template>
- <el-drawer
- :model-value="visible"
- @update:model-value="handleVisibleChange"
- body-class="bg-gray-100"
- footer-class="p-4!"
- :size="drawerSize"
- :with-header="false"
- >
- <div v-loading="loading" class="summary-form">
- <template v-if="summaryMeetings.length">
- <el-form
- v-for="(item, index) in summaryMeetings"
- :key="item.key"
- label-width="auto"
- label-position="top"
- size="default"
- :model="item.meeting"
- class="summary-form__meeting"
- >
- <OperationMeetingContent
- :meeting="item.meeting"
- :details="item.details"
- type="view"
- :dept-options="deptOptions"
- :loading="loading"
- :show-meeting-meta="index === 0"
- @edit-detail="handleViewDetail"
- />
- </el-form>
- </template>
- <el-empty v-else description="暂无汇总会议详情" :image-size="100" />
- </div>
- <template #footer>
- <el-button size="default" @click="handleVisibleChange(false)">关闭</el-button>
- </template>
- <MeetingDetailDrawer
- :visible="detailDrawerVisible"
- :detail="detailForm"
- type="view"
- form-type="edit"
- @update:visible="handleDetailDrawerChange"
- />
- </el-drawer>
- </template>
- <style scoped lang="scss">
- .summary-form {
- min-height: 240px;
- }
- .summary-form__meeting {
- margin-bottom: 16px;
- }
- .summary-form__meeting:last-child {
- margin-bottom: 0;
- }
- </style>
|