Sfoglia il codice sorgente

调整运营会议新建明细选择项目名称自动填充本期计划工作量

Co-authored-by: Copilot <copilot@github.com>
Zimo 3 giorni fa
parent
commit
d92f28ef1c

+ 8 - 0
src/api/pms/meeting/index.ts

@@ -23,5 +23,13 @@ export const OperationMeetingApi = {
 
   getProjectNameOptions: async () => {
     return await request.get({ url: `/pms/iot-operation-meeting/cachedProjects` })
+  },
+
+  getPreviousWorkPlan: async (params: {
+    projectName: string
+    id?: string | number
+    meetingId?: string | number
+  }) => {
+    return await request.get({ url: `/pms/iot-operation-meeting/previousWorkPlan`, params })
   }
 }

+ 64 - 3
src/views/pms/operation-meeting/meeting-form.vue

@@ -3,7 +3,7 @@ import type { FormInstance, FormRules } from 'element-plus'
 import type { DeptOption, DetailItem, OperationMeeting } from './types'
 import { OperationMeetingApi } from '@/api/pms/meeting'
 import { useTableComponents } from '@/components/ZmTable/useTableComponents'
-import { useWindowSize } from '@vueuse/core'
+import { useDebounceFn, useWindowSize } from '@vueuse/core'
 
 interface Props {
   visible: boolean
@@ -78,6 +78,7 @@ const detailDrawerTitle = computed(() => {
 })
 
 const createDetailItem = (): DetailItem => ({
+  raw: {},
   projectName: '',
   currentRevenue: undefined,
   cumulativeRevenue: undefined,
@@ -95,6 +96,7 @@ const createDetailItem = (): DetailItem => ({
 })
 
 const cloneDetailItem = (data?: Partial<DetailItem>): DetailItem => ({
+  raw: data?.raw || {},
   projectName: data?.projectName || '',
   currentRevenue: data?.currentRevenue,
   cumulativeRevenue: data?.cumulativeRevenue,
@@ -126,6 +128,7 @@ const parseMeetingSeries = (value: unknown) => {
 }
 
 const normalizeDetailItem = (data?: Record<string, unknown>): DetailItem => ({
+  raw: data || {},
   projectName: String(data?.projectName || ''),
   currentRevenue: parseNumberValue(data?.currentRevenue),
   cumulativeRevenue: parseNumberValue(data?.cumulativeRevenue),
@@ -143,6 +146,7 @@ const normalizeDetailItem = (data?: Record<string, unknown>): DetailItem => ({
 })
 
 const detailForm = ref<DetailItem>(createDetailItem())
+const previousWorkPlanQueryKey = ref('')
 
 const detailSummaryFields = [
   'currentRevenue',
@@ -212,7 +216,7 @@ const requiredTextRule = (message: string) => [
     required: true,
     whitespace: true,
     message,
-    trigger: 'blur'
+    trigger: ['blur', 'change']
   }
 ]
 
@@ -346,6 +350,7 @@ const handleAddDetailItem = () => {
   detailFormType.value = 'create'
   detailEditingIndex.value = -1
   detailForm.value = createDetailItem()
+  previousWorkPlanQueryKey.value = ''
   detailDrawerVisible.value = true
   nextTick(() => detailFormRef.value?.clearValidate())
 }
@@ -354,6 +359,7 @@ const handleEditDetailItem = (row: DetailItem, index: number) => {
   detailFormType.value = 'edit'
   detailEditingIndex.value = index
   detailForm.value = cloneDetailItem(row)
+  previousWorkPlanQueryKey.value = ''
   detailDrawerVisible.value = true
   nextTick(() => detailFormRef.value?.clearValidate())
 }
@@ -369,6 +375,7 @@ const handleDetailDrawerChange = (visible: boolean) => {
     detailForm.value = createDetailItem()
     detailFormType.value = 'create'
     detailEditingIndex.value = -1
+    previousWorkPlanQueryKey.value = ''
     nextTick(() => detailFormRef.value?.clearValidate())
   }
 }
@@ -457,7 +464,10 @@ const buildOperationMeetingPayload = (): Partial<OperationMeeting> => {
   return payload
 }
 
-const buildDetailPayload = (item: DetailItem): DetailItem => cloneDetailItem(item)
+const buildDetailPayload = (item: DetailItem): Omit<DetailItem, 'raw'> => {
+  const { raw, ...rest } = item
+  return rest
+}
 
 const submitForm = async () => {
   if (props.type === 'view' || !operationMeetingRef.value) return
@@ -527,6 +537,55 @@ const queryProjectNameSearch = (
   cb(projectNameSuggestions.value.filter((item) => item.value.toLowerCase().includes(keyword)))
 }
 
+const getDetailRawQueryValue = (key: 'id' | 'meetingId') => {
+  const value = detailForm.value.raw?.[key]
+
+  if (value === undefined || value === null) {
+    return ''
+  }
+
+  return typeof value === 'string' || typeof value === 'number' ? value : String(value)
+}
+
+const updatePlannedWorkloadFromPreviousPlan = (data?: Record<string, unknown>) => {
+  detailForm.value.plannedWorkload = String(data?.nextPlannedWorkload || '')
+}
+
+const loadPreviousWorkPlan = async () => {
+  const projectName = detailForm.value.projectName.trim()
+
+  if (!projectName) {
+    previousWorkPlanQueryKey.value = ''
+    return
+  }
+
+  const params = {
+    projectName,
+    id: getDetailRawQueryValue('id'),
+    meetingId: getDetailRawQueryValue('meetingId')
+  }
+  const queryKey = JSON.stringify(params)
+
+  if (previousWorkPlanQueryKey.value === queryKey) return
+
+  previousWorkPlanQueryKey.value = queryKey
+
+  try {
+    const data = await OperationMeetingApi.getPreviousWorkPlan(params)
+    if (detailForm.value.projectName.trim() !== projectName) return
+    updatePlannedWorkloadFromPreviousPlan(data)
+  } catch {
+    previousWorkPlanQueryKey.value = ''
+  }
+}
+
+const handleProjectNameComplete = useDebounceFn(loadPreviousWorkPlan, 300)
+
+const handleProjectNameSelect = (item: ProjectNameSuggestion) => {
+  detailForm.value.projectName = item.value
+  void handleProjectNameComplete()
+}
+
 async function loadProjectNameOptions() {
   try {
     const data = await OperationMeetingApi.getProjectNameOptions()
@@ -810,6 +869,8 @@ onMounted(() => {
                 clearable
                 :fetch-suggestions="queryProjectNameSearch"
                 :trigger-on-focus="true"
+                @change="handleProjectNameComplete"
+                @select="handleProjectNameSelect"
               />
             </el-form-item>
           </div>

+ 1 - 0
src/views/pms/operation-meeting/types.ts

@@ -13,6 +13,7 @@ export interface OperationMeeting {
 }
 
 export interface DetailItem {
+  raw: Record<string, unknown>
   projectName: string
   currentRevenue: number | undefined // 本期收入
   cumulativeRevenue: number | undefined // 累计收入