|
|
@@ -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>
|