Răsfoiți Sursa

调整生产运营会议,动态字段,收入环比等等

Zimo 3 zile în urmă
părinte
comite
9b50df6b7a

+ 69 - 53
src/views/pms/operation-meeting/components/meeting-detail-drawer.vue

@@ -45,13 +45,14 @@ const projectNameSuggestions = computed<ProjectNameSuggestion[]>(() =>
   projectNameOptions.value.map((item) => ({ value: item }))
 )
 
-const hasExtProperties = computed(() => Boolean(props.extProperties?.length))
+const hasExtProperties = computed(() =>
+  Boolean(props.extProperties?.length || detailForm.value.extProperty.length)
+)
 const currentPeriodExtProperties = computed(() =>
   hasExtProperties.value
     ? detailForm.value.extProperty.filter((item) => item.defaultValue !== 'next')
     : []
 )
-
 const nextPlanExtProperties = computed(() =>
   hasExtProperties.value
     ? detailForm.value.extProperty.filter((item) => item.defaultValue === 'next')
@@ -64,15 +65,19 @@ function createDetailItem(): DetailItem {
     projectName: '',
     currentRevenue: undefined,
     cumulativeRevenue: undefined,
+    beforeRevenue: undefined,
     currentOnAccount: undefined,
     cumulativeOnAccount: undefined,
+    beforeOnAccount: undefined,
     currentPayment: undefined,
     cumulativePayment: undefined,
+    beforePayment: undefined,
     plannedWorkload: '',
     actualCompletion: '',
     equipmentUtilizationRate: undefined,
     keyWorkCompletion: '',
     problemsAnalysis: '',
+    qhse: '',
     nextPlannedWorkload: '',
     priorityTasks: '',
     extProperty: cloneExtProperties(props.extProperties)
@@ -84,15 +89,19 @@ const cloneDetailItem = (data?: Partial<DetailItem>): DetailItem => ({
   projectName: data?.projectName || '',
   currentRevenue: data?.currentRevenue,
   cumulativeRevenue: data?.cumulativeRevenue,
+  beforeRevenue: data?.beforeRevenue,
   currentOnAccount: data?.currentOnAccount,
   cumulativeOnAccount: data?.cumulativeOnAccount,
+  beforeOnAccount: data?.beforeOnAccount,
   currentPayment: data?.currentPayment,
   cumulativePayment: data?.cumulativePayment,
+  beforePayment: data?.beforePayment,
   plannedWorkload: data?.plannedWorkload || '',
   actualCompletion: data?.actualCompletion || '',
   equipmentUtilizationRate: data?.equipmentUtilizationRate,
   keyWorkCompletion: data?.keyWorkCompletion || '',
   problemsAnalysis: data?.problemsAnalysis || '',
+  qhse: data?.qhse || '',
   nextPlannedWorkload: data?.nextPlannedWorkload || '',
   priorityTasks: data?.priorityTasks || '',
   extProperty: mergeExtProperties(props.extProperties || [], data?.extProperty)
@@ -128,21 +137,42 @@ function normalizeExtProperty(data?: Partial<ExtPropertyItem>): ExtPropertyItem
   }
 }
 
+function parseExtPropertyList(data?: ExtPropertyItem[] | unknown): unknown[] {
+  if (Array.isArray(data)) return data
+
+  if (typeof data === 'string') {
+    const text = data.trim()
+    if (!text) return []
+
+    try {
+      return parseExtPropertyList(JSON.parse(text))
+    } catch {
+      return []
+    }
+  }
+
+  if (data && typeof data === 'object') {
+    const values = Object.values(data as Record<string, unknown>)
+
+    return values.every((item) => item && typeof item === 'object') ? values : []
+  }
+
+  return []
+}
+
 function cloneExtProperties(data?: ExtPropertyItem[] | unknown): ExtPropertyItem[] {
-  return Array.isArray(data)
-    ? data
-        .map((item) => normalizeExtProperty(item as Partial<ExtPropertyItem>))
-        .filter((item) => item.identifier)
-    : []
+  return parseExtPropertyList(data)
+    .map((item) => normalizeExtProperty(item as Partial<ExtPropertyItem>))
+    .filter((item) => item.identifier)
 }
 
 function mergeExtProperties(
   defaults: ExtPropertyItem[],
   current?: ExtPropertyItem[] | unknown
 ): ExtPropertyItem[] {
-  if (!defaults.length) return []
-
   const currentItems = cloneExtProperties(current)
+  if (!defaults.length) return currentItems
+
   const currentMap = new Map(currentItems.map((item) => [item.identifier, item]))
   const defaultIdentifiers = new Set(defaults.map((item) => item.identifier))
   const mergedDefaults = defaults.map((item) => {
@@ -228,6 +258,7 @@ const detailRules = reactive<FormRules>({
   equipmentUtilizationRate: getEquipmentUtilizationRateRules(),
   keyWorkCompletion: requiredTextRule('请输入重点工作及完成情况'),
   problemsAnalysis: requiredTextRule('请输入存在问题及分析'),
+  qhse: requiredTextRule('请输入设备安全管理工作'),
   nextPlannedWorkload: requiredTextRule('请输入下期计划工作量'),
   priorityTasks: requiredTextRule('请输入重点工作事项')
 })
@@ -398,8 +429,7 @@ onMounted(() => {
     :show-close="false"
     header-class="mb-0! p-4!"
     body-class="bg-gray-100"
-    footer-class="p-4!"
-  >
+    footer-class="p-4!">
     <template #header>
       <div class="flex items-center">
         <span class="font-bold text-xl">{{ detailDrawerTitle }}</span>
@@ -414,8 +444,7 @@ onMounted(() => {
       :rules="detailRules"
       :disabled="type === 'view'"
       scroll-to-error
-      require-asterisk-position="right"
-    >
+      require-asterisk-position="right">
       <section class="detail-section">
         <div class="detail-section__grid detail-section__grid--single">
           <el-form-item label="项目名称" prop="projectName">
@@ -427,8 +456,7 @@ onMounted(() => {
               :fetch-suggestions="queryProjectNameSearch"
               :trigger-on-focus="true"
               @change="handleProjectNameComplete"
-              @select="handleProjectNameSelect"
-            />
+              @select="handleProjectNameSelect" />
           </el-form-item>
         </div>
       </section>
@@ -441,48 +469,42 @@ onMounted(() => {
               v-model="detailForm.currentRevenue"
               class="w-full!"
               :controls="false"
-              :precision="2"
-            />
+              :precision="2" />
           </el-form-item>
           <el-form-item label="收入-累计" prop="cumulativeRevenue">
             <el-input-number
               v-model="detailForm.cumulativeRevenue"
               class="w-full!"
               :controls="false"
-              :precision="2"
-            />
+              :precision="2" />
           </el-form-item>
           <el-form-item label="挂帐-本期" prop="currentOnAccount">
             <el-input-number
               v-model="detailForm.currentOnAccount"
               class="w-full!"
               :controls="false"
-              :precision="2"
-            />
+              :precision="2" />
           </el-form-item>
           <el-form-item label="挂帐-累计" prop="cumulativeOnAccount">
             <el-input-number
               v-model="detailForm.cumulativeOnAccount"
               class="w-full!"
               :controls="false"
-              :precision="2"
-            />
+              :precision="2" />
           </el-form-item>
           <el-form-item label="回款-本期" prop="currentPayment">
             <el-input-number
               v-model="detailForm.currentPayment"
               class="w-full!"
               :controls="false"
-              :precision="2"
-            />
+              :precision="2" />
           </el-form-item>
           <el-form-item label="回款-累计" prop="cumulativePayment">
             <el-input-number
               v-model="detailForm.cumulativePayment"
               class="w-full!"
               :controls="false"
-              :precision="2"
-            />
+              :precision="2" />
           </el-form-item>
         </div>
       </section>
@@ -496,8 +518,7 @@ onMounted(() => {
               :key="item.identifier"
               :label="getExtPropertyLabel(item)"
               :prop="getExtPropertyProp(item)"
-              :rules="getExtPropertyRules(item)"
-            >
+              :rules="getExtPropertyRules(item)">
               <el-input-number
                 v-if="isDoubleExtProperty(item)"
                 v-model="item.actualValue"
@@ -505,15 +526,13 @@ onMounted(() => {
                 :controls="false"
                 :precision="2"
                 :min="getExtPropertyNumberBoundary(item.minValue)"
-                :max="getExtPropertyNumberBoundary(item.maxValue)"
-              />
+                :max="getExtPropertyNumberBoundary(item.maxValue)" />
               <el-input
                 v-else
                 v-model="item.actualValue"
                 type="textarea"
                 :rows="3"
-                :placeholder="`请输入${item.name}`"
-              />
+                :placeholder="`请输入${item.name}`" />
             </el-form-item>
           </template>
           <template v-else>
@@ -522,24 +541,21 @@ onMounted(() => {
                 v-model="detailForm.plannedWorkload"
                 type="textarea"
                 :rows="3"
-                placeholder="请输入计划工作量"
-              />
+                placeholder="请输入计划工作量" />
             </el-form-item>
             <el-form-item label="实际完成" prop="actualCompletion">
               <el-input
                 v-model="detailForm.actualCompletion"
                 type="textarea"
                 :rows="3"
-                placeholder="请输入实际完成"
-              />
+                placeholder="请输入实际完成" />
             </el-form-item>
             <el-form-item label="设备利用率(%)" prop="equipmentUtilizationRate">
               <el-input-number
                 v-model="detailForm.equipmentUtilizationRate"
                 class="w-full!"
                 :controls="false"
-                :precision="2"
-              />
+                :precision="2" />
             </el-form-item>
           </template>
         </div>
@@ -553,16 +569,21 @@ onMounted(() => {
               v-model="detailForm.keyWorkCompletion"
               type="textarea"
               :rows="4"
-              placeholder="请输入重点工作及完成情况"
-            />
+              placeholder="请输入重点工作及完成情况" />
           </el-form-item>
           <el-form-item label="存在问题及分析" prop="problemsAnalysis">
             <el-input
               v-model="detailForm.problemsAnalysis"
               type="textarea"
               :rows="4"
-              placeholder="请输入存在问题及分析"
-            />
+              placeholder="请输入存在问题及分析" />
+          </el-form-item>
+          <el-form-item label="设备安全管理工作" prop="qhse">
+            <el-input
+              v-model="detailForm.qhse"
+              type="textarea"
+              :rows="4"
+              placeholder="请输入设备安全管理工作" />
           </el-form-item>
         </div>
       </section>
@@ -575,24 +596,21 @@ onMounted(() => {
               v-model="detailForm.nextPlannedWorkload"
               type="textarea"
               :rows="4"
-              placeholder="请输入下期计划工作量"
-            />
+              placeholder="请输入下期计划工作量" />
           </el-form-item>
           <el-form-item label="重点工作事项" prop="priorityTasks">
             <el-input
               v-model="detailForm.priorityTasks"
               type="textarea"
               :rows="4"
-              placeholder="请输入重点工作事项"
-            />
+              placeholder="请输入重点工作事项" />
           </el-form-item>
           <el-form-item
             v-for="item in nextPlanExtProperties"
             :key="item.identifier"
             :label="getExtPropertyLabel(item)"
             :prop="getExtPropertyProp(item)"
-            :rules="getExtPropertyRules(item)"
-          >
+            :rules="getExtPropertyRules(item)">
             <el-input-number
               v-if="isDoubleExtProperty(item)"
               v-model="item.actualValue"
@@ -600,15 +618,13 @@ onMounted(() => {
               :controls="false"
               :precision="2"
               :min="getExtPropertyNumberBoundary(item.minValue)"
-              :max="getExtPropertyNumberBoundary(item.maxValue)"
-            />
+              :max="getExtPropertyNumberBoundary(item.maxValue)" />
             <el-input
               v-else
               v-model="item.actualValue"
               type="textarea"
               :rows="4"
-              :placeholder="`请输入${item.name}`"
-            />
+              :placeholder="`请输入${item.name}`" />
           </el-form-item>
         </div>
       </section>

Fișier diff suprimat deoarece este prea mare
+ 568 - 192
src/views/pms/operation-meeting/components/operation-meeting-content.vue


+ 51 - 9
src/views/pms/operation-meeting/meeting-form.vue

@@ -64,15 +64,19 @@ function createDetailItem(): DetailItem {
     projectName: '',
     currentRevenue: undefined,
     cumulativeRevenue: undefined,
+    beforeRevenue: undefined,
     currentOnAccount: undefined,
     cumulativeOnAccount: undefined,
+    beforeOnAccount: undefined,
     currentPayment: undefined,
     cumulativePayment: undefined,
+    beforePayment: undefined,
     plannedWorkload: '',
     actualCompletion: '',
     equipmentUtilizationRate: undefined,
     keyWorkCompletion: '',
     problemsAnalysis: '',
+    qhse: '',
     nextPlannedWorkload: '',
     priorityTasks: '',
     extProperty: cloneExtProperties(extProperties.value)
@@ -82,6 +86,7 @@ function createDetailItem(): DetailItem {
 function createMeetingFormItem(
   meeting: OperationMeetingForm = {},
   details: DetailItem[] = [],
+  extProperty: ExtPropertyItem[] = [],
   index = 0
 ): OperationMeetingFormItem {
   const id = meeting.id === undefined || meeting.id === null ? 'new' : String(meeting.id)
@@ -89,7 +94,11 @@ function createMeetingFormItem(
   return {
     key: `${id}-${index}`,
     meeting,
-    details
+    details,
+    extProperty,
+    beforeRevenue: undefined,
+    beforeOnAccount: undefined,
+    beforePayment: undefined
   }
 }
 
@@ -98,15 +107,19 @@ const cloneDetailItem = (data?: Partial<DetailItem>): DetailItem => ({
   projectName: data?.projectName || '',
   currentRevenue: data?.currentRevenue,
   cumulativeRevenue: data?.cumulativeRevenue,
+  beforeRevenue: data?.beforeRevenue,
   currentOnAccount: data?.currentOnAccount,
   cumulativeOnAccount: data?.cumulativeOnAccount,
+  beforeOnAccount: data?.beforeOnAccount,
   currentPayment: data?.currentPayment,
   cumulativePayment: data?.cumulativePayment,
+  beforePayment: data?.beforePayment,
   plannedWorkload: data?.plannedWorkload || '',
   actualCompletion: data?.actualCompletion || '',
   equipmentUtilizationRate: data?.equipmentUtilizationRate,
   keyWorkCompletion: data?.keyWorkCompletion || '',
   problemsAnalysis: data?.problemsAnalysis || '',
+  qhse: data?.qhse || '',
   nextPlannedWorkload: data?.nextPlannedWorkload || '',
   priorityTasks: data?.priorityTasks || '',
   extProperty: cloneExtProperties(data?.extProperty)
@@ -147,21 +160,42 @@ function normalizeExtProperty(data?: Partial<ExtPropertyItem>): ExtPropertyItem
   }
 }
 
+function parseExtPropertyList(data?: ExtPropertyItem[] | unknown): unknown[] {
+  if (Array.isArray(data)) return data
+
+  if (typeof data === 'string') {
+    const text = data.trim()
+    if (!text) return []
+
+    try {
+      return parseExtPropertyList(JSON.parse(text))
+    } catch {
+      return []
+    }
+  }
+
+  if (data && typeof data === 'object') {
+    const values = Object.values(data as Record<string, unknown>)
+
+    return values.every((item) => item && typeof item === 'object') ? values : []
+  }
+
+  return []
+}
+
 function cloneExtProperties(data?: ExtPropertyItem[] | unknown): ExtPropertyItem[] {
-  return Array.isArray(data)
-    ? data
-        .map((item) => normalizeExtProperty(item as Partial<ExtPropertyItem>))
-        .filter((item) => item.identifier)
-    : []
+  return parseExtPropertyList(data)
+    .map((item) => normalizeExtProperty(item as Partial<ExtPropertyItem>))
+    .filter((item) => item.identifier)
 }
 
 function mergeExtProperties(
   defaults: ExtPropertyItem[],
   current?: ExtPropertyItem[] | unknown
 ): ExtPropertyItem[] {
-  if (!defaults.length) return []
-
   const currentItems = cloneExtProperties(current)
+  if (!defaults.length) return currentItems
+
   const currentMap = new Map(currentItems.map((item) => [item.identifier, item]))
   const defaultIdentifiers = new Set(defaults.map((item) => item.identifier))
   const mergedDefaults = defaults.map((item) => {
@@ -185,15 +219,19 @@ const normalizeDetailItem = (data?: Record<string, unknown>): DetailItem => ({
   projectName: normalizeTextValue(data?.projectName),
   currentRevenue: parseNumberValue(data?.currentRevenue),
   cumulativeRevenue: parseNumberValue(data?.cumulativeRevenue),
+  beforeRevenue: parseNumberValue(data?.beforeRevenue),
   currentOnAccount: parseNumberValue(data?.currentOnAccount),
   cumulativeOnAccount: parseNumberValue(data?.cumulativeOnAccount),
+  beforeOnAccount: parseNumberValue(data?.beforeOnAccount),
   currentPayment: parseNumberValue(data?.currentPayment),
   cumulativePayment: parseNumberValue(data?.cumulativePayment),
+  beforePayment: parseNumberValue(data?.beforePayment),
   plannedWorkload: normalizeTextValue(data?.plannedWorkload),
   actualCompletion: normalizeTextValue(data?.actualCompletion),
   equipmentUtilizationRate: parseNumberValue(data?.equipmentUtilizationRate),
   keyWorkCompletion: normalizeTextValue(data?.keyWorkCompletion),
   problemsAnalysis: normalizeTextValue(data?.problemsAnalysis),
+  qhse: normalizeTextValue(data?.qhse),
   nextPlannedWorkload: normalizeTextValue(data?.nextPlannedWorkload),
   priorityTasks: normalizeTextValue(data?.priorityTasks),
   extProperty: mergeExtProperties(extProperties.value, data?.extProperty)
@@ -217,7 +255,11 @@ const normalizeMeetingFormItem = (
       cumulative: data.cumulative as boolean | undefined,
       meetingSeries: parseMeetingSeries(data.meetingSeries)
     },
-    details: details.map((item) => normalizeDetailItem(item as Record<string, unknown>))
+    details: details.map((item) => normalizeDetailItem(item as Record<string, unknown>)),
+    extProperty: cloneExtProperties(data.extProperty),
+    beforeRevenue: parseNumberValue(data.beforeRevenue),
+    beforeOnAccount: parseNumberValue(data.beforeOnAccount),
+    beforePayment: parseNumberValue(data.beforePayment)
   }
 }
 

+ 7 - 14
src/views/pms/operation-meeting/summary.vue

@@ -69,17 +69,14 @@ onMounted(() => {
 
 <template>
   <div
-    class="operation-meeting-page min-w-0 overflow-x-hidden grid grid-rows-[auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
-  >
+    class="operation-meeting-page min-w-0 overflow-x-hidden grid grid-rows-[auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]">
     <el-form
       size="default"
       label-position="left"
-      class="operation-meeting-query min-w-0 overflow-hidden rounded-lg bg-white p-4 shadow dark:bg-[#1d1e1f]"
-    >
+      class="operation-meeting-query min-w-0 overflow-hidden rounded-lg bg-white p-4 shadow dark:bg-[#1d1e1f]">
       <div class="min-w-0 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-end">
         <div
-          class="operation-meeting-query__actions min-w-0 flex flex-col gap-3 sm:flex-row lg:shrink-0"
-        >
+          class="operation-meeting-query__actions min-w-0 flex flex-col gap-3 sm:flex-row lg:shrink-0">
           <el-button type="primary" class="!ml-0 w-full sm:w-auto" @click="handleQuery">
             搜索
           </el-button>
@@ -89,8 +86,7 @@ onMounted(() => {
     </el-form>
 
     <div
-      class="operation-meeting-data-panel bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4 min-h-0"
-    >
+      class="operation-meeting-data-panel bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4 min-h-0">
       <div class="flex-1 min-h-0 relative">
         <el-auto-resizer class="operation-meeting-table-view absolute">
           <template #default="{ width, height }">
@@ -100,8 +96,7 @@ onMounted(() => {
               :width="width"
               :max-height="height"
               :height="height"
-              show-border
-            >
+              show-border>
               <ZmTableColumn label="会议期次" prop="meetingSeries" width="180" />
               <ZmTableColumn label="公司名称" prop="companyName" :min-width="220" />
               <ZmTableColumn
@@ -109,8 +104,7 @@ onMounted(() => {
                 prop="meetingDate"
                 cover-formatter
                 :real-value="formatMeetingDate"
-                :min-width="160"
-              />
+                :min-width="160" />
               <ZmTableColumn label="操作" width="100" fixed="right">
                 <template #default="{ row }">
                   <el-button size="default" link type="success" @click="handleView(row)">
@@ -163,8 +157,7 @@ onMounted(() => {
     :meeting-series="currentMeetingSeries"
     :year="currentYear"
     :dept-options="deptOptions"
-    @update:visible="visible = $event"
-  />
+    @update:visible="visible = $event" />
 </template>
 
 <style scoped lang="scss">

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

@@ -23,6 +23,10 @@ export interface OperationMeetingFormItem {
   key: string
   meeting: OperationMeetingForm
   details: DetailItem[]
+  extProperty: ExtPropertyItem[]
+  beforeRevenue: number | undefined // 上期收入
+  beforeOnAccount: number | undefined // 上期挂帐
+  beforePayment: number | undefined // 上期回款
 }
 
 export type ExtPropertyActualValue = string | number | null | undefined
@@ -47,15 +51,19 @@ export interface DetailItem {
   projectName: string
   currentRevenue: number | undefined // 本期收入
   cumulativeRevenue: number | undefined // 累计收入
+  beforeRevenue: number | undefined // 上期收入
   currentOnAccount: number | undefined // 本期挂帐
   cumulativeOnAccount: number | undefined // 累计挂帐
+  beforeOnAccount: number | undefined // 上期挂帐
   currentPayment: number | undefined // 本期回款
   cumulativePayment: number | undefined // 累计回款
+  beforePayment: number | undefined // 上期回款
   plannedWorkload: string // 计划工作量
   actualCompletion: string // 实际完成
   equipmentUtilizationRate: number | undefined // 设备利用率
   keyWorkCompletion: string // 重点工作及完成情况
   problemsAnalysis: string // 存在问题及分析
+  qhse: string // 设备安全管理工作
   nextPlannedWorkload: string // 下期计划工作量
   priorityTasks: string // 重点工作事项
   extProperty: ExtPropertyItem[] // 扩展属性

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff