|
@@ -2,7 +2,7 @@
|
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
|
import type { DetailItem, ExtPropertyItem } from '../types'
|
|
import type { DetailItem, ExtPropertyItem } from '../types'
|
|
|
import { OperationMeetingApi } from '@/api/pms/meeting'
|
|
import { OperationMeetingApi } from '@/api/pms/meeting'
|
|
|
-import { useDebounceFn, useWindowSize } from '@vueuse/core'
|
|
|
|
|
|
|
+import { useWindowSize } from '@vueuse/core'
|
|
|
|
|
|
|
|
interface Props {
|
|
interface Props {
|
|
|
visible: boolean
|
|
visible: boolean
|
|
@@ -28,8 +28,6 @@ const emits = defineEmits<{
|
|
|
const { width } = useWindowSize()
|
|
const { width } = useWindowSize()
|
|
|
const detailFormRef = ref<FormInstance>()
|
|
const detailFormRef = ref<FormInstance>()
|
|
|
const detailForm = ref<DetailItem>(createDetailItem())
|
|
const detailForm = ref<DetailItem>(createDetailItem())
|
|
|
-const previousWorkPlanQueryKey = ref('')
|
|
|
|
|
-const equipmentUtilizationRateRequired = ref(true)
|
|
|
|
|
|
|
|
|
|
const detailDrawerSize = computed(() => (width.value <= 768 ? '100%' : '50%'))
|
|
const detailDrawerSize = computed(() => (width.value <= 768 ? '100%' : '50%'))
|
|
|
const detailDrawerTitle = computed(() => {
|
|
const detailDrawerTitle = computed(() => {
|
|
@@ -219,32 +217,6 @@ const nonNegativeNumberRule = (requiredMessage: string, fieldLabel: string) => [
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
-const numberRangeRule = (requiredMessage: string, fieldLabel: string, min: number, max: number) => [
|
|
|
|
|
- ...requiredNumberRule(requiredMessage),
|
|
|
|
|
- {
|
|
|
|
|
- type: 'number' as const,
|
|
|
|
|
- min,
|
|
|
|
|
- max,
|
|
|
|
|
- message: `${fieldLabel}需在${min}到${max}之间`,
|
|
|
|
|
- trigger: ['blur', 'change']
|
|
|
|
|
- }
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
-const optionalNumberRangeRule = (fieldLabel: string, min: number, max: number) => [
|
|
|
|
|
- {
|
|
|
|
|
- type: 'number' as const,
|
|
|
|
|
- min,
|
|
|
|
|
- max,
|
|
|
|
|
- message: `${fieldLabel}需在${min}到${max}之间`,
|
|
|
|
|
- trigger: ['blur', 'change']
|
|
|
|
|
- }
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
-const getEquipmentUtilizationRateRules = () =>
|
|
|
|
|
- equipmentUtilizationRateRequired.value
|
|
|
|
|
- ? numberRangeRule('请输入设备利用率', '设备利用率', 0, 100)
|
|
|
|
|
- : optionalNumberRangeRule('设备利用率', 0, 100)
|
|
|
|
|
-
|
|
|
|
|
const detailRules = reactive<FormRules>({
|
|
const detailRules = reactive<FormRules>({
|
|
|
projectName: requiredTextRule('请选择或者输入项目名称'),
|
|
projectName: requiredTextRule('请选择或者输入项目名称'),
|
|
|
currentRevenue: nonNegativeNumberRule('请输入收入-本期', '收入-本期'),
|
|
currentRevenue: nonNegativeNumberRule('请输入收入-本期', '收入-本期'),
|
|
@@ -253,9 +225,6 @@ const detailRules = reactive<FormRules>({
|
|
|
cumulativeOnAccount: nonNegativeNumberRule('请输入挂帐-累计', '挂帐-累计'),
|
|
cumulativeOnAccount: nonNegativeNumberRule('请输入挂帐-累计', '挂帐-累计'),
|
|
|
currentPayment: nonNegativeNumberRule('请输入回款-本期', '回款-本期'),
|
|
currentPayment: nonNegativeNumberRule('请输入回款-本期', '回款-本期'),
|
|
|
cumulativePayment: nonNegativeNumberRule('请输入回款-累计', '回款-累计'),
|
|
cumulativePayment: nonNegativeNumberRule('请输入回款-累计', '回款-累计'),
|
|
|
- plannedWorkload: requiredTextRule('请输入计划工作量'),
|
|
|
|
|
- actualCompletion: requiredTextRule('请输入实际完成'),
|
|
|
|
|
- equipmentUtilizationRate: getEquipmentUtilizationRateRules(),
|
|
|
|
|
keyWorkCompletion: requiredTextRule('请输入重点工作及完成情况'),
|
|
keyWorkCompletion: requiredTextRule('请输入重点工作及完成情况'),
|
|
|
problemsAnalysis: requiredTextRule('请输入存在问题及分析'),
|
|
problemsAnalysis: requiredTextRule('请输入存在问题及分析'),
|
|
|
qhse: requiredTextRule('请输入设备安全管理工作'),
|
|
qhse: requiredTextRule('请输入设备安全管理工作'),
|
|
@@ -263,27 +232,80 @@ const detailRules = reactive<FormRules>({
|
|
|
priorityTasks: requiredTextRule('请输入重点工作事项')
|
|
priorityTasks: requiredTextRule('请输入重点工作事项')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const updateEquipmentUtilizationRateRule = (required: boolean) => {
|
|
|
|
|
- equipmentUtilizationRateRequired.value = required
|
|
|
|
|
- detailRules.equipmentUtilizationRate = getEquipmentUtilizationRateRules()
|
|
|
|
|
- nextTick(() => detailFormRef.value?.clearValidate('equipmentUtilizationRate'))
|
|
|
|
|
|
|
+const isRequiredExtProperty = (item: ExtPropertyItem) =>
|
|
|
|
|
+ item.required === true || item.required === 1 || item.required === '1'
|
|
|
|
|
+
|
|
|
|
|
+const isDoubleExtProperty = (item: ExtPropertyItem) => item.dataType === 'double'
|
|
|
|
|
+
|
|
|
|
|
+const normalizeExtPropertyKeyword = (value: unknown) =>
|
|
|
|
|
+ String(value ?? '')
|
|
|
|
|
+ .toLowerCase()
|
|
|
|
|
+ .replace(/[\s%()()]/g, '')
|
|
|
|
|
+
|
|
|
|
|
+const getExtPropertySearchText = (item: ExtPropertyItem) =>
|
|
|
|
|
+ `${normalizeExtPropertyKeyword(item.identifier)}${normalizeExtPropertyKeyword(item.name)}`
|
|
|
|
|
+
|
|
|
|
|
+const isEquipmentUtilizationExtProperty = (item: ExtPropertyItem) => {
|
|
|
|
|
+ const text = getExtPropertySearchText(item)
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ text.includes('设备利用率') ||
|
|
|
|
|
+ text.includes('equipmentutilization') ||
|
|
|
|
|
+ text.includes('utilizationrate')
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const loadEquipmentUtilizationRateRule = async () => {
|
|
|
|
|
- updateEquipmentUtilizationRateRule(true)
|
|
|
|
|
|
|
+const isConstructionEquipmentCountExtProperty = (item: ExtPropertyItem) => {
|
|
|
|
|
+ const text = getExtPropertySearchText(item)
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- const data = await OperationMeetingApi.getMandatoryOrNot()
|
|
|
|
|
- updateEquipmentUtilizationRateRule(data?.mandatory !== false)
|
|
|
|
|
- } catch {
|
|
|
|
|
- updateEquipmentUtilizationRateRule(true)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return (
|
|
|
|
|
+ text.includes('施工设备数量') ||
|
|
|
|
|
+ text.includes('施工设备数') ||
|
|
|
|
|
+ text.includes('constructionequipmentcount') ||
|
|
|
|
|
+ text.includes('constructiondevicecount')
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const isRequiredExtProperty = (item: ExtPropertyItem) =>
|
|
|
|
|
- item.required === true || item.required === 1 || item.required === '1'
|
|
|
|
|
|
|
+const isOperatingEquipmentCountExtProperty = (item: ExtPropertyItem) => {
|
|
|
|
|
+ const text = getExtPropertySearchText(item)
|
|
|
|
|
|
|
|
-const isDoubleExtProperty = (item: ExtPropertyItem) => item.dataType === 'double'
|
|
|
|
|
|
|
+ return (
|
|
|
|
|
+ text.includes('投运设备数量') ||
|
|
|
|
|
+ text.includes('投运设备数') ||
|
|
|
|
|
+ text.includes('operatingequipmentcount') ||
|
|
|
|
|
+ text.includes('operationequipmentcount') ||
|
|
|
|
|
+ text.includes('runningequipmentcount')
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const equipmentUtilizationExtProperty = computed(() =>
|
|
|
|
|
+ currentPeriodExtProperties.value.find(isEquipmentUtilizationExtProperty)
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const constructionEquipmentCountExtProperty = computed(() =>
|
|
|
|
|
+ currentPeriodExtProperties.value.find(isConstructionEquipmentCountExtProperty)
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const operatingEquipmentCountExtProperty = computed(() =>
|
|
|
|
|
+ currentPeriodExtProperties.value.find(isOperatingEquipmentCountExtProperty)
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const updateEquipmentUtilizationExtProperty = () => {
|
|
|
|
|
+ const utilizationItem = equipmentUtilizationExtProperty.value
|
|
|
|
|
+ const constructionItem = constructionEquipmentCountExtProperty.value
|
|
|
|
|
+ const operatingItem = operatingEquipmentCountExtProperty.value
|
|
|
|
|
+ if (!utilizationItem || !constructionItem || !operatingItem) return
|
|
|
|
|
+
|
|
|
|
|
+ const constructionCount = parseNumberValue(constructionItem.actualValue)
|
|
|
|
|
+ const operatingCount = parseNumberValue(operatingItem.actualValue)
|
|
|
|
|
+
|
|
|
|
|
+ utilizationItem.actualValue =
|
|
|
|
|
+ constructionCount === undefined || operatingCount === undefined || operatingCount === 0
|
|
|
|
|
+ ? undefined
|
|
|
|
|
+ : Number(((constructionCount / operatingCount) * 100).toFixed(2))
|
|
|
|
|
+
|
|
|
|
|
+ nextTick(() => detailFormRef.value?.clearValidate(getExtPropertyProp(utilizationItem)))
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const hasExtPropertyActualValue = (item: ExtPropertyItem) => {
|
|
const hasExtPropertyActualValue = (item: ExtPropertyItem) => {
|
|
|
const value = item.actualValue
|
|
const value = item.actualValue
|
|
@@ -334,6 +356,8 @@ const getExtPropertyAlternativeProps = (item: ExtPropertyItem) =>
|
|
|
.map((property) => getExtPropertyProp(property))
|
|
.map((property) => getExtPropertyProp(property))
|
|
|
|
|
|
|
|
const handleExtPropertyValueChange = (item: ExtPropertyItem) => {
|
|
const handleExtPropertyValueChange = (item: ExtPropertyItem) => {
|
|
|
|
|
+ updateEquipmentUtilizationExtProperty()
|
|
|
|
|
+
|
|
|
if (
|
|
if (
|
|
|
!isAlternativeExtProperty(item) ||
|
|
!isAlternativeExtProperty(item) ||
|
|
|
!hasCompleteExtPropertyAlternativeGroup(getExtPropertyScope(item))
|
|
!hasCompleteExtPropertyAlternativeGroup(getExtPropertyScope(item))
|
|
@@ -393,63 +417,22 @@ const queryProjectNameSearch = (
|
|
|
cb(projectNameSuggestions.value.filter((item) => item.value.toLowerCase().includes(keyword)))
|
|
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 () => {
|
|
|
|
|
- if (hasExtProperties.value) return
|
|
|
|
|
-
|
|
|
|
|
- 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()
|
|
|
|
|
-}
|
|
|
|
|
|
|
+watch(
|
|
|
|
|
+ () =>
|
|
|
|
|
+ [
|
|
|
|
|
+ constructionEquipmentCountExtProperty.value?.actualValue,
|
|
|
|
|
+ operatingEquipmentCountExtProperty.value?.actualValue,
|
|
|
|
|
+ equipmentUtilizationExtProperty.value?.identifier
|
|
|
|
|
+ ] as const,
|
|
|
|
|
+ () => updateEquipmentUtilizationExtProperty(),
|
|
|
|
|
+ { immediate: true }
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
const handleVisibleChange = (visible: boolean) => {
|
|
const handleVisibleChange = (visible: boolean) => {
|
|
|
emits('update:visible', visible)
|
|
emits('update:visible', visible)
|
|
|
|
|
|
|
|
if (!visible) {
|
|
if (!visible) {
|
|
|
detailForm.value = createDetailItem()
|
|
detailForm.value = createDetailItem()
|
|
|
- previousWorkPlanQueryKey.value = ''
|
|
|
|
|
nextTick(() => detailFormRef.value?.clearValidate())
|
|
nextTick(() => detailFormRef.value?.clearValidate())
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -479,7 +462,6 @@ watch(
|
|
|
if (!visible) return
|
|
if (!visible) return
|
|
|
|
|
|
|
|
detailForm.value = cloneDetailItem(props.detail)
|
|
detailForm.value = cloneDetailItem(props.detail)
|
|
|
- previousWorkPlanQueryKey.value = ''
|
|
|
|
|
nextTick(() => detailFormRef.value?.clearValidate())
|
|
nextTick(() => detailFormRef.value?.clearValidate())
|
|
|
},
|
|
},
|
|
|
{ immediate: true }
|
|
{ immediate: true }
|
|
@@ -487,7 +469,6 @@ watch(
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
loadProjectNameOptions()
|
|
loadProjectNameOptions()
|
|
|
- loadEquipmentUtilizationRateRule()
|
|
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -527,9 +508,7 @@ onMounted(() => {
|
|
|
placeholder="请选择或输入项目名称"
|
|
placeholder="请选择或输入项目名称"
|
|
|
clearable
|
|
clearable
|
|
|
:fetch-suggestions="queryProjectNameSearch"
|
|
:fetch-suggestions="queryProjectNameSearch"
|
|
|
- :trigger-on-focus="true"
|
|
|
|
|
- @change="handleProjectNameComplete"
|
|
|
|
|
- @select="handleProjectNameSelect" />
|
|
|
|
|
|
|
+ :trigger-on-focus="true" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</div>
|
|
</div>
|
|
|
</section>
|
|
</section>
|
|
@@ -582,55 +561,31 @@ onMounted(() => {
|
|
|
</div>
|
|
</div>
|
|
|
</section>
|
|
</section>
|
|
|
|
|
|
|
|
- <section class="detail-section">
|
|
|
|
|
|
|
+ <section v-if="currentPeriodExtProperties.length" class="detail-section">
|
|
|
<h4 class="detail-section__title">本期生产运行情况</h4>
|
|
<h4 class="detail-section__title">本期生产运行情况</h4>
|
|
|
<div class="detail-section__grid">
|
|
<div class="detail-section__grid">
|
|
|
- <template v-if="hasExtProperties">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-for="item in currentPeriodExtProperties"
|
|
|
|
|
- :key="item.identifier"
|
|
|
|
|
- :label="getExtPropertyLabel(item)"
|
|
|
|
|
- :prop="getExtPropertyProp(item)"
|
|
|
|
|
- :rules="getExtPropertyRules(item)">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-if="isDoubleExtProperty(item)"
|
|
|
|
|
- v-model="item.actualValue"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- @change="handleExtPropertyValueChange(item)" />
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-else
|
|
|
|
|
- v-model="item.actualValue"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="3"
|
|
|
|
|
- :placeholder="`请输入${item.name}`"
|
|
|
|
|
- @input="handleExtPropertyValueChange(item)" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template v-else>
|
|
|
|
|
- <el-form-item label="计划工作量" prop="plannedWorkload">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.plannedWorkload"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="3"
|
|
|
|
|
- placeholder="请输入计划工作量" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="实际完成" prop="actualCompletion">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.actualCompletion"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="3"
|
|
|
|
|
- placeholder="请输入实际完成" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="设备利用率(%)" prop="equipmentUtilizationRate">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.equipmentUtilizationRate"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ v-for="item in currentPeriodExtProperties"
|
|
|
|
|
+ :key="item.identifier"
|
|
|
|
|
+ :label="getExtPropertyLabel(item)"
|
|
|
|
|
+ :prop="getExtPropertyProp(item)"
|
|
|
|
|
+ :rules="getExtPropertyRules(item)">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-if="isDoubleExtProperty(item)"
|
|
|
|
|
+ v-model="item.actualValue"
|
|
|
|
|
+ class="w-full!"
|
|
|
|
|
+ :controls="false"
|
|
|
|
|
+ :disabled="isEquipmentUtilizationExtProperty(item)"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ @change="handleExtPropertyValueChange(item)" />
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-else
|
|
|
|
|
+ v-model="item.actualValue"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ :placeholder="`请输入${item.name}`"
|
|
|
|
|
+ @input="handleExtPropertyValueChange(item)" />
|
|
|
|
|
+ </el-form-item>
|
|
|
</div>
|
|
</div>
|
|
|
</section>
|
|
</section>
|
|
|
|
|
|