Quellcode durchsuchen

SOC合并代码

yanghao vor 5 Tagen
Ursprung
Commit
701ab73884
39 geänderte Dateien mit 1249 neuen und 772 gelöschten Zeilen
  1. 11 1
      src/api/pms/iotrhdailyreport/index.ts
  2. 139 121
      src/components/DeptTreeSelect/index.vue
  3. 133 27
      src/components/WellSelect/index.vue
  4. 9 10
      src/views/oli-connection/monitoring-query/index.vue
  5. 17 14
      src/views/oli-connection/monitoring/index.vue
  6. 15 18
      src/views/pms/device/monitor/TdDeviceInfo.vue
  7. 171 116
      src/views/pms/iotmainworkorder/WorkOrderMaterial.vue
  8. 29 2
      src/views/pms/iotopeationfill/index1.vue
  9. 271 224
      src/views/pms/iotprojectinfo/IotProjectInfoForm.vue
  10. 4 5
      src/views/pms/iotprojectinfo/IotProjectTaskForm.vue
  11. 62 5
      src/views/pms/iotprojectinfo/index.vue
  12. 49 4
      src/views/pms/iotprojecttask/IotProjectTaskForm.vue
  13. 19 3
      src/views/pms/iotrddailyreport/create-rd-form.vue
  14. 4 4
      src/views/pms/iotrddailyreport/fillDailyReport.vue
  15. 4 4
      src/views/pms/iotrddailyreport/index.vue
  16. 4 4
      src/views/pms/iotrddailyreport/statistics.vue
  17. 10 9
      src/views/pms/iotrddailyreport/summary.vue
  18. 5 4
      src/views/pms/iotrhdailyreport/approval.vue
  19. 3 4
      src/views/pms/iotrhdailyreport/fill.vue
  20. 69 35
      src/views/pms/iotrhdailyreport/index.vue
  21. 2 0
      src/views/pms/iotrhdailyreport/rh-table.vue
  22. 66 29
      src/views/pms/iotrhdailyreport/summary.vue
  23. 5 4
      src/views/pms/iotrydailyreport/approval.vue
  24. 5 4
      src/views/pms/iotrydailyreport/fill.vue
  25. 3 4
      src/views/pms/iotrydailyreport/index.vue
  26. 24 2
      src/views/pms/iotrydailyreport/ry-xj-form.vue
  27. 7 0
      src/views/pms/iotrydailyreport/ry-xj-table.vue
  28. 8 9
      src/views/pms/iotrydailyreport/summary.vue
  29. 3 4
      src/views/pms/iotrydailyreport/xapproval.vue
  30. 3 4
      src/views/pms/iotrydailyreport/xfill.vue
  31. 4 4
      src/views/pms/iotrydailyreport/xjindex.vue
  32. 8 9
      src/views/pms/iotrydailyreport/xsummary.vue
  33. 9 10
      src/views/report-statistics/costs.vue
  34. 15 17
      src/views/report-statistics/daily-report.vue
  35. 15 16
      src/views/report-statistics/rd-daily-report.vue
  36. 17 16
      src/views/report-statistics/ry-daily-report.vue
  37. 17 16
      src/views/report-statistics/ry-xj-daily-report.vue
  38. 9 10
      src/views/report-statistics/work-order-completion.vue
  39. 1 0
      types/global.d.ts

+ 11 - 1
src/api/pms/iotrhdailyreport/index.ts

@@ -36,6 +36,16 @@ export interface IotRhDailyReportVO {
   auditStatus: number // 审批状态 未提交、审批中、审批通过、审批不通过、已取消
 }
 
+export interface IotRhDailyReportTotalWorkloadVO {
+  totalFuelConsumption?: number
+  totalPowerConsumption?: number
+  totalWaterInjection?: number
+  totalGasInjection?: number
+  totalN2GasInjection?: number
+  totalNaturalGasInjection?: number
+  utilizationRate?: number
+}
+
 // 瑞恒日报 API
 export const IotRhDailyReportApi = {
   // 查询瑞恒日报分页
@@ -60,7 +70,7 @@ export const IotRhDailyReportApi = {
   },
 
   // 累计工作量统计
-  totalWorkload: async (params: any) => {
+  totalWorkload: async (params: any): Promise<IotRhDailyReportTotalWorkloadVO> => {
     return await request.get({ url: `/pms/iot-rh-daily-report/totalWorkload`, params })
   },
 

+ 139 - 121
src/components/DeptTreeSelect/index.vue

@@ -2,54 +2,39 @@
 import { defaultProps, handleTree } from '@/utils/tree'
 import { ElTree } from 'element-plus'
 import * as DeptApi from '@/api/system/dept'
-import { Search } from '@element-plus/icons-vue'
+import { Search, CaretLeft, CaretRight } from '@element-plus/icons-vue'
+
+interface Tree {
+  id: number
+  name: string
+  children?: Tree[]
+  sort?: number
+}
 
 const props = defineProps({
-  deptId: {
-    type: Number,
-    required: true
-  },
-  modelValue: {
-    type: Number,
-    default: undefined
-  },
-  topId: {
-    type: Number,
-    required: true
-  },
-  title: {
-    type: String,
-    default: '部门'
-  },
-  initSelect: {
-    type: Boolean,
-    default: true
-  },
-  showTitle: {
-    type: Boolean,
-    default: true
-  }
+  deptId: { type: Number, required: true },
+  modelValue: { type: Number, default: undefined },
+  topId: { type: Number, required: true },
+  title: { type: String, default: '部门' },
+  initSelect: { type: Boolean, default: true },
+  showTitle: { type: Boolean, default: true }
 })
 
 const emits = defineEmits(['update:modelValue', 'node-click'])
 
+// --- 状态控制 ---
+const isCollapsed = ref(false)
 const deptName = ref('')
 const deptList = ref<Tree[]>([])
 const treeRef = ref<InstanceType<typeof ElTree>>()
 const expandedKeys = ref<number[]>([])
 
+// --- 逻辑处理 (保持不变) ---
 const sortTreeBySort = (treeNodes: Tree[]) => {
   if (!treeNodes || !Array.isArray(treeNodes)) return treeNodes
-  const sortedNodes = [...treeNodes].sort((a, b) => {
-    const sortA = a.sort != null ? a.sort : 999999
-    const sortB = b.sort != null ? b.sort : 999999
-    return sortA - sortB
-  })
-
+  const sortedNodes = [...treeNodes].sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999))
   sortedNodes.forEach((node) => {
-    if (node.children && Array.isArray(node.children)) {
-      node.children = sortTreeBySort(node.children)
-    }
+    if (node.children) node.children = sortTreeBySort(node.children)
   })
   return sortedNodes
 }
@@ -57,51 +42,26 @@ const sortTreeBySort = (treeNodes: Tree[]) => {
 const loadTree = async () => {
   try {
     let id = props.deptId
-
-    // 1. 校验 ID 范围逻辑 (保持原有逻辑:确保 deptId 在 topId 范围内)
     if (id !== props.topId) {
       const depts = await DeptApi.specifiedSimpleDepts(props.topId)
-      const self = depts.find((item) => item.id === props.deptId)
-      if (depts.length && !self) {
-        id = props.topId
-      }
+      if (depts.length && !depts.find((item) => item.id === props.deptId)) id = props.topId
     }
-
-    // 2. 获取最终 ID 对应的部门列表
     const res = await DeptApi.specifiedSimpleDepts(id)
-
-    // 3. 处理 modelValue 的赋值逻辑 (关键修改点)
-    if (props.initSelect) {
-      // 检查传入的 modelValue 是否存在于当前加载的树数据中
-      const isModelValueValid = props.modelValue && res.some((item) => item.id === props.modelValue)
-
-      if (!isModelValueValid) {
-        emits('update:modelValue', id)
-      }
+    if (props.initSelect && props.modelValue && !res.some((item) => item.id === props.modelValue)) {
+      emits('update:modelValue', id)
     }
-
-    // 4. 生成树结构
     deptList.value = sortTreeBySort(handleTree(res))
-
-    // 5. 界面交互:高亮并展开
     nextTick(() => {
-      // 优先使用 props.modelValue (如果刚才触发了 update,父组件可能还没传回来,所以这里取 props.modelValue 或者 id)
-      // 但为了稳妥,我们再次检查逻辑
-      const targetKey = props.modelValue ? props.modelValue : props.initSelect ? id : null
-
+      const targetKey = props.modelValue ?? (props.initSelect ? id : null)
       if (targetKey && treeRef.value) {
         treeRef.value.setCurrentKey(targetKey)
-        // 确保该节点被展开
-        if (!expandedKeys.value.includes(targetKey)) {
-          expandedKeys.value.push(targetKey)
-        }
-      } else if (deptList.value.length > 0) {
-        // 如果没有选中项,默认展开第一级
-        expandedKeys.value = deptList.value.map((item) => item.id)
+        if (!expandedKeys.value.includes(targetKey)) expandedKeys.value.push(targetKey)
+        else if (deptList.value.length > 0)
+          expandedKeys.value = deptList.value.map((item) => item.id)
       }
     })
-  } catch (error) {
-    console.error('加载部门树失败:', error)
+  } catch (e) {
+    console.error(e)
   }
 }
 
@@ -110,70 +70,128 @@ const handleNodeClick = (data: Tree) => {
   emits('node-click', data)
 }
 
-const filterNode = (value: string, data: Tree) => {
-  if (!value) return true
-  return data.name.includes(value)
-}
-
-watch(deptName, (val) => {
-  treeRef.value?.filter(val)
-})
-
-watch(
-  () => props.deptId,
-  (newVal, oldVal) => {
-    if (newVal !== oldVal) {
-      loadTree()
-    }
-  }
-)
+const filterNode = (val: string, data: Tree) => !val || data.name.includes(val)
 
+watch(deptName, (val) => treeRef.value?.filter(val))
+watch(() => props.deptId, loadTree)
 watch(
   () => props.modelValue,
-  (newVal) => {
-    if (newVal && treeRef.value) {
-      treeRef.value.setCurrentKey(newVal)
-      if (!expandedKeys.value.includes(newVal)) {
-        expandedKeys.value.push(newVal)
-      }
+  (val) => {
+    if (val && treeRef.value) treeRef.value.setCurrentKey(val)
+
+    if (val && !expandedKeys.value.includes(val)) {
+      expandedKeys.value.push(val)
     }
   },
   { immediate: true }
 )
 
-onMounted(() => {
-  loadTree()
-})
+onMounted(loadTree)
 </script>
 
 <template>
-  <div class="gap-4 flex flex-col h-full">
-    <h1 v-if="showTitle" class="text-lg font-medium">{{ props.title }}</h1>
-    <el-input
-      v-model="deptName"
-      size="default"
-      placeholder="请输入部门名称"
-      clearable
-      :prefix-icon="Search"
-    />
-    <div class="flex-1 relative">
-      <el-auto-resizer class="absolute">
-        <template #default="{ height }">
-          <el-scrollbar :style="{ height: `${height}px` }">
-            <el-tree
-              ref="treeRef"
-              :data="deptList"
-              :props="defaultProps"
-              :expand-on-click-node="false"
-              :filter-node-method="filterNode"
-              node-key="id"
-              highlight-current
-              :default-expanded-keys="expandedKeys"
-              @node-click="handleNodeClick"
-            />
-          </el-scrollbar>
-        </template>
-      </el-auto-resizer>
+  <div
+    class="dept-aside-container relative bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-4 transition-all duration-300 ease-in-out overflow-visible"
+    :class="[isCollapsed ? 'is-collapsed' : 'p-4']"
+  >
+    <div v-show="!isCollapsed" class="h-full flex flex-col gap-4 overflow-hidden w-full">
+      <h1 v-if="showTitle" class="text-lg font-medium truncate shrink-0">{{ props.title }}</h1>
+
+      <div class="shrink-0">
+        <el-input v-model="deptName" placeholder="请输入部门名称" clearable :prefix-icon="Search" />
+      </div>
+
+      <div class="flex-1 relative overflow-hidden">
+        <el-auto-resizer class="absolute">
+          <template #default="{ height }">
+            <el-scrollbar :style="{ height: `${height}px` }">
+              <el-tree
+                ref="treeRef"
+                :data="deptList"
+                :props="defaultProps"
+                :expand-on-click-node="false"
+                :filter-node-method="filterNode"
+                node-key="id"
+                highlight-current
+                :default-expanded-keys="expandedKeys"
+                @node-click="handleNodeClick"
+              />
+            </el-scrollbar>
+          </template>
+        </el-auto-resizer>
+      </div>
+    </div>
+
+    <div class="collapse-handle" @click="isCollapsed = !isCollapsed">
+      <el-icon size="12">
+        <CaretLeft v-if="!isCollapsed" />
+        <CaretRight v-else />
+      </el-icon>
     </div>
   </div>
 </template>
+
+<style scoped>
+.dept-aside-container {
+  /* 关键点 1:初始宽度设为 15% */
+  width: 14vw; /* 或者使用百分比,但在 Grid 容器内使用 vw 更稳定 */
+  height: calc(
+    100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+  );
+  min-width: 200px; /* 防止在极小屏幕下 15% 太窄看不清 */
+  box-sizing: border-box;
+  flex-shrink: 0;
+}
+
+/* 关键点 2:折叠状态 */
+.dept-aside-container.is-collapsed {
+  width: 0 !important;
+  min-width: 0 !important;
+  padding: 0 !important;
+  margin-right: -16px; /* 抵消父级 grid 的 gap-x-4,让右侧内容贴合 */
+  overflow: visible !important;
+  pointer-events: none; /* 折叠后不响应鼠标事件,除了 handle */
+
+  /* opacity: 0; */
+  box-shadow: none;
+}
+
+/* 即使父级折叠,handle 也要可见并可点击 */
+.collapse-handle {
+  position: absolute;
+  top: 50%;
+  right: -14px;
+  z-index: 200;
+  display: flex;
+  width: 14px;
+  height: 60px;
+  color: var(--el-text-color-secondary);
+  pointer-events: auto;
+  cursor: pointer;
+  background-color: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-light);
+  border-left: none;
+  border-radius: 0 12px 12px 0;
+  transform: translateY(-50%);
+  box-shadow: 2px 0 6px rgb(0 0 0 / 5%);
+  transition: right 0.3s;
+  align-items: center;
+  justify-content: center;
+}
+
+.is-collapsed .collapse-handle {
+  right: -8px; /* 在边缘露出一半 */
+  border-left: 1px solid var(--el-border-color-light);
+}
+
+.collapse-handle:hover {
+  color: var(--el-color-primary);
+  background-color: var(--el-fill-color-light);
+}
+
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+</style>

+ 133 - 27
src/components/WellSelect/index.vue

@@ -1,7 +1,9 @@
 <script lang="ts" setup>
+import { ref, watch, onMounted, nextTick } from 'vue'
 import { ElTree } from 'element-plus'
 import * as DeptApi from '@/api/system/dept'
-import { Search } from '@element-plus/icons-vue'
+// 1. 引入需要的收缩/展开图标
+import { Search, CaretLeft, CaretRight } from '@element-plus/icons-vue'
 
 const props = defineProps({
   deptId: {
@@ -24,6 +26,10 @@ const props = defineProps({
 
 const emits = defineEmits(['update:modelValue', 'node-click', 'update:contractName'])
 
+// --- 展开/收缩状态 ---
+const isCollapsed = ref(false)
+
+// --- 原有业务逻辑保持不变 ---
 const wellName = ref('')
 
 interface Tree {
@@ -152,32 +158,132 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="gap-4 flex flex-col h-full">
-    <h1 class="text-lg font-medium">{{ props.title }}</h1>
-    <el-input
-      v-model="wellName"
-      size="default"
-      placeholder="请输入井名"
-      clearable
-      :prefix-icon="Search"
-    />
-    <div class="flex-1 relative">
-      <el-auto-resizer class="absolute">
-        <template #default="{ height }">
-          <el-scrollbar :style="{ height: `${height}px` }">
-            <el-tree
-              ref="treeRef"
-              :data="deptList"
-              :expand-on-click-node="false"
-              :filter-node-method="filterNode"
-              node-key="value"
-              highlight-current
-              :default-expanded-keys="expandedKeys"
-              @node-click="handleNodeClick"
-            />
-          </el-scrollbar>
-        </template>
-      </el-auto-resizer>
+  <div
+    class="dept-aside-container relative bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-4 transition-all duration-300 ease-in-out overflow-visible"
+    :class="[isCollapsed ? 'is-collapsed' : 'p-4']"
+  >
+    <div class="inner-content flex flex-col gap-4 h-full w-full">
+      <h1 class="text-lg font-medium truncate shrink-0">{{ props.title }}</h1>
+
+      <div class="shrink-0">
+        <el-input
+          v-model="wellName"
+          size="default"
+          placeholder="请输入井名"
+          clearable
+          :prefix-icon="Search"
+        />
+      </div>
+
+      <div class="flex-1 relative overflow-hidden">
+        <el-auto-resizer class="absolute">
+          <template #default="{ height }">
+            <el-scrollbar :style="{ height: `${height}px` }">
+              <el-tree
+                ref="treeRef"
+                :data="deptList"
+                :expand-on-click-node="false"
+                :filter-node-method="filterNode"
+                node-key="value"
+                highlight-current
+                :default-expanded-keys="expandedKeys"
+                @node-click="handleNodeClick"
+              />
+            </el-scrollbar>
+          </template>
+        </el-auto-resizer>
+      </div>
+    </div>
+
+    <div class="collapse-handle" @click="isCollapsed = !isCollapsed">
+      <el-icon size="12">
+        <CaretLeft v-if="!isCollapsed" />
+        <CaretRight v-else />
+      </el-icon>
     </div>
   </div>
 </template>
+
+<style scoped>
+/* 容器基础样式及动画 */
+.dept-aside-container {
+  width: 14vw;
+  height: calc(
+    100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+  );
+  min-width: 200px;
+  box-sizing: border-box;
+  flex-shrink: 0;
+  transition:
+    width 0.3s ease-in-out,
+    min-width 0.3s ease-in-out,
+    padding 0.3s ease-in-out,
+    margin 0.3s ease-in-out;
+}
+
+/* 内部内容区域防挤压及淡入淡出 */
+.inner-content {
+  min-width: 168px;
+  opacity: 1;
+  transition: opacity 0.2s ease-in-out;
+}
+
+/* 折叠状态下的容器形态 */
+.dept-aside-container.is-collapsed {
+  width: 0;
+  min-width: 0;
+  padding: 0;
+  margin-right: -16px; /* 根据实际父级 grid gap 调整,若无间距可设为 0 */
+  pointer-events: none;
+  box-shadow: none;
+}
+
+/* 折叠状态下隐藏内部内容 */
+.dept-aside-container.is-collapsed .inner-content {
+  opacity: 0;
+  visibility: hidden;
+  transition:
+    opacity 0.1s ease-in-out,
+    visibility 0s 0.1s;
+}
+
+/* 交互把手样式 */
+.collapse-handle {
+  position: absolute;
+  top: 50%;
+  right: -14px;
+  z-index: 200;
+  display: flex;
+  width: 14px;
+  height: 60px;
+  color: var(--el-text-color-secondary);
+  pointer-events: auto;
+  cursor: pointer;
+  background-color: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-light);
+  border-left: none;
+  border-radius: 0 12px 12px 0;
+  transform: translateY(-50%);
+  box-shadow: 2px 0 6px rgb(0 0 0 / 5%);
+  transition: right 0.3s ease-in-out;
+  align-items: center;
+  justify-content: center;
+}
+
+/* 折叠时把手向左位移贴边 */
+.is-collapsed .collapse-handle {
+  right: -8px;
+  border-left: 1px solid var(--el-border-color-light);
+}
+
+.collapse-handle:hover {
+  color: var(--el-color-primary);
+  background-color: var(--el-fill-color-light);
+}
+
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+</style>

+ 9 - 10
src/views/oli-connection/monitoring-query/index.vue

@@ -247,17 +247,16 @@ function formatterValue(row: ListItem) {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect
-        :top-id="156"
-        :deptId="deptId"
-        :init-select="false"
-        :show-title="false"
-        @node-click="handleNodeClick"
-      />
-    </div>
+    <DeptTreeSelect
+      :top-id="156"
+      :deptId="deptId"
+      :init-select="false"
+      :show-title="false"
+      @node-click="handleNodeClick"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
 
     <el-form
       size="default"

+ 17 - 14
src/views/oli-connection/monitoring/index.vue

@@ -42,6 +42,7 @@ interface OliDevice {
   carOnline: string
   deptName: string
   vehicleName: string
+  mqttUrl: string
 }
 
 const list = ref<OliDevice[]>([])
@@ -154,7 +155,8 @@ const openDetail = (
   code: string,
   dept: string,
   vehicle: string,
-  carOnline: string
+  carOnline: string,
+  mqttUrl: string
 ) => {
   if (time === null || time === undefined) {
     message.warning('没有数采数据')
@@ -162,24 +164,23 @@ const openDetail = (
   }
   router.push({
     name: 'MonitoringDetail',
-    query: { id, ifInline, carOnline, time, name, code, dept, vehicle }
+    query: { id, ifInline, carOnline, time, name, code, dept, vehicle, mqttUrl }
   })
 }
 </script>
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect
-        :top-id="156"
-        :deptId="deptId"
-        v-model="query.deptId"
-        :init-select="false"
-        :show-title="false"
-      />
-    </div>
+    <DeptTreeSelect
+      :top-id="156"
+      :deptId="deptId"
+      v-model="query.deptId"
+      :init-select="false"
+      :show-title="false"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
@@ -281,7 +282,8 @@ const openDetail = (
                           scope.row.deviceCode,
                           scope.row.deptName,
                           scope.row.vehicleName,
-                          scope.row.carOnline ?? ''
+                          scope.row.carOnline ?? '',
+                          scope.row.mqttUrl ?? ''
                         )
                       "
                     >
@@ -411,7 +413,8 @@ const openDetail = (
                             item.deviceCode,
                             item.deptName,
                             item.vehicleName,
-                            item.carOnline ?? ''
+                            item.carOnline ?? '',
+                            item.mqttUrl ?? ''
                           )
                         "
                         >查看详情</span

+ 15 - 18
src/views/pms/device/monitor/TdDeviceInfo.vue

@@ -658,31 +658,28 @@ const { toggle, isFullscreen } = useFullscreen(targetArea)
         </template>
       </div>
     </div>
-    <div ref="targetArea" class="h-full min-h-0 relative">
-      <div class="grid grid-cols-[260px_1fr] gap-4 h-full">
-        <el-scrollbar
-          class="rounded-xl shadow-sm border border-gray-100 border-solid overflow-hidden bg-gradient-to-b from-blue-100 to-white"
-          view-class="flex flex-col min-h-full"
-          v-loading="dimensionLoading"
-        >
-          <template v-for="citem in dimensionsContent" :key="citem.label">
-            <template v-if="citem.judgment ? Boolean(citem.value.length) : true">
-              <div
-                class="sticky-title bg-blue-100 z-10 flex justify-between items-center py-3 px-4 border-0 border-solid"
-              >
-                <span class="font-bold text-sm text-gray-700! flex items-center gap-2">
+    <div ref="targetArea" class="relative">
+      <div class="flex flex-col gap-4 h-full">
+        <template v-for="citem in dimensionsContent" :key="citem.label">
+          <template v-if="citem.judgment ? Boolean(citem.value.length) : true">
+            <div
+              class="rounded-xl shadow-sm border border-gray-100 border-solid overflow-hidden bg-gradient-to-b from-blue-100 to-white p-4 flex flex-col gap-2"
+              v-loading="dimensionLoading"
+            >
+              <div class="flex justify-center items-center gap-2 border-0 border-solid">
+                <span class="font-bold text-gray-700! flex items-center gap-2">
                   <el-icon><component :is="citem.icon" /></el-icon>
                   {{ citem.label }}
                 </span>
                 <span
-                  class="text-xs px-2 py-0.5 rounded-full font-mono"
+                  class="text-xs px-2 py-0.5 rounded-full font-mono bg-gray-200"
                   :class="[citem.countBg, citem.countColor]"
                 >
                   {{ citem.value.length }}
                 </span>
               </div>
 
-              <div class="px-3 pb-4 pt-2 space-y-3">
+              <div class="grid grid-cols-4 gap-2">
                 <div
                   :data-disabled="disabledDimensions.includes(item.identifier)"
                   v-for="item in citem.value"
@@ -734,12 +731,12 @@ const { toggle, isFullscreen } = useFullscreen(targetArea)
                   </div>
                 </div>
               </div>
-            </template>
+            </div>
           </template>
-        </el-scrollbar>
+        </template>
 
         <div
-          class="rounded-xl shadow-sm border border-gray-100 border-solid p-4 flex flex-col bg-gradient-to-b from-blue-100 to-white"
+          class="flex-1 min-h-200 rounded-xl shadow-sm border border-gray-100 border-solid p-4 flex flex-col bg-gradient-to-b from-blue-100 to-white"
         >
           <header class="flex items-center justify-between mb-4">
             <h3 class="flex items-center gap-2">

+ 171 - 116
src/views/pms/iotmainworkorder/WorkOrderMaterial.vue

@@ -1,45 +1,63 @@
 <template>
-  <Dialog v-model="dialogVisible" title="选择物料" style="width: 1400px; padding: 0px 0px 0px 0px; min-height: 300px">
-
-    <div ref="measureSpan"
-         style="position:absolute;left:-9999px;top:-9999px;visibility:hidden;
-         white-space:nowrap;padding:0 12px;font:13px Arial, PingFang SC, Microsoft YaHei, sans-serif;">
+  <Dialog
+    v-model="dialogVisible"
+    title="选择物料"
+    style="width: 1400px; min-height: 300px; padding: 0"
+  >
+    <div
+      ref="measureSpan"
+      style="
+        position: absolute;
+        top: -9999px;
+        left: -9999px;
+        padding: 0 12px;
+        font:
+          13px Arial,
+          'PingFang SC',
+          'Microsoft YaHei',
+          sans-serif;
+        white-space: nowrap;
+        visibility: hidden;
+      "
+    >
     </div>
 
     <!-- 设备分类和BOM节点信息显示 -->
-    <div style="margin: 0 0px 0px; background: #f5f7fa; padding: 5px 15px; border-radius: 4px;">
+    <div style="padding: 5px 15px; margin: 0; background: #f5f7fa; border-radius: 4px">
       <el-row>
         <el-col :span="8">
-          <span style="font-weight: bold; margin-right: 8px; font-size: 13px;">设备编码:</span>
-          <span style="font-size: 13px;">{{ deviceCode }}</span>
+          <span style="margin-right: 8px; font-size: 13px; font-weight: bold">设备编码:</span>
+          <span style="font-size: 13px">{{ deviceCode }}</span>
         </el-col>
         <el-col :span="8">
-          <span style="font-weight: bold; margin-right: 8px; font-size: 13px;">设备名称:</span>
-          <span style="font-size: 13px;">{{ deviceName }}</span>
+          <span style="margin-right: 8px; font-size: 13px; font-weight: bold">设备名称:</span>
+          <span style="font-size: 13px">{{ deviceName }}</span>
         </el-col>
         <el-col :span="8">
-          <span style="font-weight: bold; margin-right: 8px; font-size: 13px;">{{bomNodeLabel}}:</span>
-          <span style="font-size: 13px;">{{ bomNodeName }}</span>
+          <span style="margin-right: 8px; font-size: 13px; font-weight: bold"
+            >{{ bomNodeLabel }}:</span
+          >
+          <span style="font-size: 13px">{{ bomNodeName }}</span>
         </el-col>
       </el-row>
     </div>
 
     <!-- 可折叠的区域 展示当前保养项 已经绑定的物料-->
-    <div v-if="showFoldArea" style="margin-bottom: 2px;">
+    <div v-if="showFoldArea" style="margin-bottom: 2px">
       <el-button
         type="text"
         @click="showMaterialArea = !showMaterialArea"
-        style="padding-left: 8px; margin-bottom: 2px; font-size: 13px;"
+        style="padding-left: 8px; margin-bottom: 2px; font-size: 13px"
       >
         {{ showMaterialArea ? '隐藏保养项物料' : '显示保养项物料' }}
       </el-button>
 
       <div
         v-show="showMaterialArea"
-        style="background: #f5f7fa; padding: 5px 8px; border-radius: 4px;"
+        style="padding: 5px 8px; background: #f5f7fa; border-radius: 4px"
       >
         <template v-if="deviceBomMaterials.length === 0">
-          <div style="text-align: center; padding: 8px; color: #999; font-size: 13px;">
+          <div style="padding: 8px; font-size: 13px; color: #999; text-align: center">
             无绑定物料
           </div>
         </template>
@@ -47,15 +65,15 @@
           <!-- 表头 -->
           <div
             style="
-            display: grid;
-            grid-template-columns: 1fr 1fr 1fr 1fr;
-            gap: 12px;
-            margin-bottom: 8px;
-            font-weight: bold;
-            border-bottom: 1px solid #e4e7ed;
-            padding-bottom: 6px;
-            font-size: 13px;
-          "
+              display: grid;
+              padding-bottom: 6px;
+              margin-bottom: 8px;
+              font-size: 13px;
+              font-weight: bold;
+              border-bottom: 1px solid #e4e7ed;
+              grid-template-columns: 1fr 1fr 1fr 1fr;
+              gap: 12px;
+            "
           >
             <div>物料编码</div>
             <div>物料名称</div>
@@ -67,58 +85,68 @@
             v-for="(item, index) in deviceBomMaterials"
             :key="index"
             style="
-            display: grid;
-            grid-template-columns: 1fr 1fr 1fr 1fr;
-            gap: 8px;
-            padding: 6px 0;
-            border-bottom: 1px dashed #ebeef5;
-            font-size: 13px;
-          "
+              display: grid;
+              padding: 6px 0;
+              font-size: 13px;
+              border-bottom: 1px dashed #ebeef5;
+              grid-template-columns: 1fr 1fr 1fr 1fr;
+              gap: 8px;
+            "
           >
-            <div style="word-break: break-all;">{{ item.code || '-' }}</div>
-            <div style="word-break: break-all;">{{ item.name || '-' }}</div>
+            <div style="word-break: break-all">{{ item.code || '-' }}</div>
+            <div style="word-break: break-all">{{ item.name || '-' }}</div>
             <div>{{ item.quantity !== undefined ? item.quantity : '-' }}</div>
-            <div style="word-break: break-all;">{{ item.unit || '-' }}</div>
+            <div style="word-break: break-all">{{ item.unit || '-' }}</div>
           </div>
         </template>
       </div>
     </div>
 
-    <ContentWrap style="padding: 0px 0;">
+    <ContentWrap style="padding: 0">
       <el-form
         class="-mb-5px"
         :model="queryParams"
         ref="queryFormRef"
         :inline="true"
         label-width="68px"
-        style="font-size: 13px;"
+        style="font-size: 13px"
       >
-        <el-form-item :label="t('workOrderMaterial.materialCode')" prop="materialCode" style="margin-bottom: 2px;">
+        <el-form-item
+          :label="t('workOrderMaterial.materialName')"
+          prop="materialName"
+          style="margin-bottom: 2px"
+        >
           <el-input
-            v-model="queryParams.materialCode"
-            :placeholder="t('workOrderMaterial.codeHolder')"
+            v-model="queryParams.materialName"
+            :placeholder="t('workOrderMaterial.nameHolder')"
             clearable
             @keyup.enter="handleQuery"
             size="small"
           />
         </el-form-item>
-        <el-form-item :label="t('workOrderMaterial.materialName')" prop="materialName" style="margin-bottom: 2px;">
+        <el-form-item
+          :label="t('workOrderMaterial.materialCode')"
+          prop="materialCode"
+          style="margin-bottom: 2px"
+        >
           <el-input
-            v-model="queryParams.materialName"
-            :placeholder="t('workOrderMaterial.nameHolder')"
+            v-model="queryParams.materialCode"
+            :placeholder="t('workOrderMaterial.codeHolder')"
             clearable
             @keyup.enter="handleQuery"
             size="small"
           />
         </el-form-item>
-        <el-form-item style="margin-bottom: 2px;">
-          <el-button @click="handleQuery" size="small"><Icon icon="ep:search" class="mr-5px" />
-            {{ t('workOrderMaterial.search') }}</el-button>
-          <el-button @click="resetQuery" size="small"><Icon icon="ep:refresh" class="mr-5px" />
-            {{ t('workOrderMaterial.reset') }}</el-button>
+
+        <el-form-item style="margin-bottom: 2px">
+          <el-button @click="handleQuery" size="small"
+            ><Icon icon="ep:search" class="mr-5px" /> {{ t('workOrderMaterial.search') }}</el-button
+          >
+          <el-button @click="resetQuery" size="small"
+            ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('workOrderMaterial.reset') }}</el-button
+          >
           <el-button @click="handleConfirm" class="custom-green-button" size="small"
-            ><Icon icon="ep:check" class="mr-5px" />
-            {{ t('workOrderMaterial.confirm') }}</el-button
+            ><Icon icon="ep:check" class="mr-5px" /> {{ t('workOrderMaterial.confirm') }}</el-button
           >
           <!-- <el-button @click="handleView" type="warning" size="small"
             ><Icon icon="ep:plus" class="mr-5px" />
@@ -127,7 +155,7 @@
         </el-form-item>
       </el-form>
     </ContentWrap>
-    <ContentWrap style="padding: 0px 0px 0px 0px; margin-bottom: 0px">
+    <ContentWrap style="padding: 0; margin-bottom: 0">
       <el-table
         v-loading="loading"
         :data="list"
@@ -141,12 +169,15 @@
         <el-table-column width="60" :label="t('workOrderMaterial.select')">
           <template #default="{ row }">
             <el-checkbox
-              :model-value="selectedRows.some(item =>
-                item.factoryId === row.factoryId &&
-                item.costCenterId === row.costCenterId &&
-                item.storageLocationId === row.storageLocationId &&
-                item.materialCode === row.materialCode
-              )"
+              :model-value="
+                selectedRows.some(
+                  (item) =>
+                    item.factoryId === row.factoryId &&
+                    item.costCenterId === row.costCenterId &&
+                    item.storageLocationId === row.storageLocationId &&
+                    item.materialCode === row.materialCode
+                )
+              "
               @click.stop="toggleRow(row)"
               class="no-label-radio"
             />
@@ -204,8 +235,12 @@
           :class-name="'material-name-cell'"
           :width="materialNameWidth"
         />
-        <el-table-column :label="t('workOrderMaterial.ConsumptionQuantity')" align="center" prop="quantity"
-                         :width="flexColumnWidth('quantity', t('workOrderMaterial.ConsumptionQuantity'), 20)">
+        <el-table-column
+          :label="t('workOrderMaterial.ConsumptionQuantity')"
+          align="center"
+          prop="quantity"
+          :width="flexColumnWidth('quantity', t('workOrderMaterial.ConsumptionQuantity'), 20)"
+        >
           <template #default="scope">
             <el-input
               type="number"
@@ -218,15 +253,30 @@
             />
           </template>
         </el-table-column>
-        <el-table-column :label="t('workOrderMaterial.unit')" align="center" prop="unit"
-                         :width="flexColumnWidth('unit', t('workOrderMaterial.unit'))"/>
-        <el-table-column :label="t('workOrderMaterial.unitPrice')" align="center" prop="unitPrice"
-                         :width="flexColumnWidth('unitPrice', t('workOrderMaterial.unitPrice'))"/>
-        <el-table-column :label="t('workOrderMaterial.total')" align="center" prop="totalInventoryQuantity"
-                         :width="flexColumnWidth('totalInventoryQuantity', t('workOrderMaterial.total'))"/>
-        <el-table-column :label="t('workOrderMaterial.source')" align="center" prop="materialSource"
-                         :width="flexColumnWidth('materialSource', t('workOrderMaterial.source'))"/>
-
+        <el-table-column
+          :label="t('workOrderMaterial.unit')"
+          align="center"
+          prop="unit"
+          :width="flexColumnWidth('unit', t('workOrderMaterial.unit'))"
+        />
+        <el-table-column
+          :label="t('workOrderMaterial.unitPrice')"
+          align="center"
+          prop="unitPrice"
+          :width="flexColumnWidth('unitPrice', t('workOrderMaterial.unitPrice'))"
+        />
+        <el-table-column
+          :label="t('workOrderMaterial.total')"
+          align="center"
+          prop="totalInventoryQuantity"
+          :width="flexColumnWidth('totalInventoryQuantity', t('workOrderMaterial.total'))"
+        />
+        <el-table-column
+          :label="t('workOrderMaterial.source')"
+          align="center"
+          prop="materialSource"
+          :width="flexColumnWidth('materialSource', t('workOrderMaterial.source'))"
+        />
       </el-table>
       <!-- 分页 -->
       <Pagination
@@ -234,7 +284,7 @@
         v-model:page="queryParams.pageNo"
         v-model:limit="queryParams.pageSize"
         @pagination="getList"
-        style="margin-bottom: 5px; padding: 0px 0px 5px 0px;"
+        style="padding: 0 0 5px; margin-bottom: 5px"
       />
     </ContentWrap>
   </Dialog>
@@ -281,9 +331,9 @@ const showMaterialArea = ref(false) // 控制A区域显示状态
 const deviceBomMaterials = ref<any[]>([]) // 存储绑定物料数据
 
 const deviceName = ref('') // 设备名称
-const deviceCode = ref('')    // 设备编码
-const bomNodeName = ref('')   // 保养项 or 维修项 名称
-const bomNodeLabel = ref('')   // 保养项 or 维修项 名称
+const deviceCode = ref('') // 设备编码
+const bomNodeName = ref('') // 保养项 or 维修项 名称
+const bomNodeLabel = ref('') // 保养项 or 维修项 名称
 
 // 控制折叠区域的响应式变量
 const showFoldArea = ref(false)
@@ -297,7 +347,7 @@ const queryParams = reactive({
   name: '',
   code: '',
   materialCode: '',
-  materialName: '',
+  materialName: ''
 })
 
 // 保存初始查询参数用于重置
@@ -305,7 +355,7 @@ const defaultQueryParams = {
   materialName: '',
   materialCode: '',
   pageNo: 1,
-  pageSize: 10,
+  pageSize: 10
 }
 
 const drawerVisible = ref<boolean>(false)
@@ -318,19 +368,19 @@ const addMaterial = ref(null)
 
 const handleChildSubmit = (formData) => {
   const modified = removeOnesFromKeys(formData)
-  modified.materialName = modified.name;
-  modified.materialCode = modified.code;
-  modified.materialSource = '手动添加';
-  modified.quantity = modified.depleteCount;
-  modified.unitPrice = modified.price;
-  addMaterial.value = modified;
+  modified.materialName = modified.name
+  modified.materialCode = modified.code
+  modified.materialSource = '手动添加'
+  modified.quantity = modified.depleteCount
+  modified.unitPrice = modified.price
+  addMaterial.value = modified
   list.value.unshift(modified)
-  total.value = total.value+1
+  total.value = total.value + 1
 
   // 将新添加的行添加到选中列表
   selectedRows.value.push(modified)
   // 设置新添加行的索引
-  lastAddedIndex.value = 0;
+  lastAddedIndex.value = 0
 }
 
 const removeOnesFromKeys = (obj: Record<string, any>) => {
@@ -359,7 +409,7 @@ const open = async (deptId: number, bomNodeId: number, row: any, type: string) =
   queryParams.deptId = deptId
   queryParams.bomNodeId = bomNodeId
   queryParams.deviceId = row.deviceId
-  if(type === 'repair'){
+  if (type === 'repair') {
     queryParams.deviceId = row.deviceId
   }
   deviceName.value = row.deviceName
@@ -392,14 +442,14 @@ const flexColumnWidth = (prop: string, label: string, extra: number = 0) => {
     return getTextWidth(label) + extra
   }
 
-  const contentList = list.value.map(item => {
+  const contentList = list.value.map((item) => {
     const value = item[prop]
     if (value == null) return ' '
     return typeof value === 'number' ? value.toString() : value
   })
 
   contentList.push(label)
-  const maxWidth = Math.max(...contentList.map(text => getTextWidth(text)))
+  const maxWidth = Math.max(...contentList.map((text) => getTextWidth(text)))
   return Math.min(maxWidth + extra, 400)
 }
 
@@ -478,21 +528,23 @@ const getList = async () => {
 // 确认选择
 const handleConfirm = () => {
   // 检查是否有未通过校验的行
-  const invalidRows = selectedRows.value.filter(row => {
-    const quantity = parseFloat(row.quantity);
-    return isNaN(quantity) || quantity <= 0;
-  });
+  const invalidRows = selectedRows.value.filter((row) => {
+    const quantity = parseFloat(row.quantity)
+    return isNaN(quantity) || quantity <= 0
+  })
 
   if (invalidRows.length > 0) {
-    ElMessage.error('存在无效的 消耗数量,请检查');
-    return;
+    ElMessage.error('存在无效的 消耗数量,请检查')
+    return
   }
 
   if (selectedRows.value.length === 0) {
     ElMessage.warning('请至少选择一个物料')
     return
   }
-  const filters = selectedRows.value.filter((item) => item.quantity === null||item.quantity === undefined)
+  const filters = selectedRows.value.filter(
+    (item) => item.quantity === null || item.quantity === undefined
+  )
   if (filters.length > 0) {
     message.error('消耗数量必填')
     return
@@ -522,8 +574,8 @@ const handleClose = () => {
 }
 
 const rowClassName = ({ row }: { row: any }) => {
-  let className = '';
-  if(row.bomMaterialFlag === 'Y'){
+  let className = ''
+  if (row.bomMaterialFlag === 'Y') {
     className = ''
   }
   if (row.materialSource === '本地库存' && row.bomMaterialFlag != 'Y') {
@@ -543,9 +595,7 @@ const toggleRow = (row) => {
   }
 
   const currentKey = getRowUniqueKey(row)
-  const index = selectedRows.value.findIndex(item =>
-    getRowUniqueKey(item) === currentKey
-  )
+  const index = selectedRows.value.findIndex((item) => getRowUniqueKey(item) === currentKey)
 
   if (index > -1) {
     selectedRows.value.splice(index, 1) // 取消选中
@@ -556,21 +606,21 @@ const toggleRow = (row) => {
 
 // 处理 消耗数量 quantity 输入框失焦事件
 const handleQuantityBlur = (event: Event, row: any) => {
-  const inputValue = (event.target as HTMLInputElement).value;
-  let num = parseFloat(inputValue);
+  const inputValue = (event.target as HTMLInputElement).value
+  let num = parseFloat(inputValue)
   // 处理无效值
   if (isNaN(num)) {
-    row.quantity = 0;
-    return;
+    row.quantity = 0
+    return
   }
   // 处理小于等于0的情况
   if (num <= 0) {
-    row.quantity = 0;
-    return;
+    row.quantity = 0
+    return
   }
   // 保留两位小数
-  row.quantity = parseFloat(num.toFixed(4));
-};
+  row.quantity = parseFloat(num.toFixed(4))
+}
 
 // 处理输入框焦点事件(自动选中当前行)
 const handleInputFocus = (row: WorkOrderBomMaterialApi.IotMainWorkOrderBomMaterialVO) => {
@@ -579,7 +629,7 @@ const handleInputFocus = (row: WorkOrderBomMaterialApi.IotMainWorkOrderBomMateri
   }
   const currentKey = getRowUniqueKey(row)
   // 如果未选中则添加到选中列表
-  const exists = selectedRows.value.some(item => getRowUniqueKey(item) === currentKey)
+  const exists = selectedRows.value.some((item) => getRowUniqueKey(item) === currentKey)
   if (!exists) {
     selectedRows.value.push(row)
   }
@@ -617,28 +667,28 @@ onUnmounted(() => {
   // 移除窗口大小变化监听器
   window.removeEventListener('resize', handleResize)
 })
-
 </script>
 <style lang="scss" scoped>
 .no-label-radio .el-radio__label {
   display: none;
 }
+
 .no-label-radio .el-radio__inner {
   margin-right: 0;
 }
 
 /* 自定义淡绿色按钮 */
 :deep(.custom-green-button) {
+  color: #67c23a;
   background-color: #e1f3d8;
   border-color: #e1f3d8;
-  color: #67c23a;
 }
 
 /* 悬停效果 */
 :deep(.custom-green-button:hover) {
+  color: #5daf34;
   background-color: #d1e8c0;
   border-color: #d1e8c0;
-  color: #5daf34;
 }
 
 /* 点击效果 */
@@ -659,21 +709,24 @@ onUnmounted(() => {
 :deep(.adaptive-table) {
   width: 100%;
   font-size: 13px;
+
   .el-table__cell {
     padding: 4px 0;
+
     .cell {
-      white-space: nowrap; /* 禁止换行 */
       overflow: hidden;
       text-overflow: ellipsis;
+      white-space: nowrap; /* 禁止换行 */
     }
   }
   // 确保基准列使用可用空间
   .material-name-cell .cell {
     display: block;
+    max-width: 100%;
     overflow: hidden;
     text-overflow: ellipsis;
-    max-width: 100%;
   }
+
   .el-table__row {
     height: 36px;
   }
@@ -688,9 +741,10 @@ onUnmounted(() => {
 :deep(.adaptive-table .el-table__header) {
   th {
     padding: 6px 0;
+
     .cell {
-      font-weight: bold;
       font-size: 13px;
+      font-weight: bold;
     }
   }
 }
@@ -699,9 +753,11 @@ onUnmounted(() => {
 :deep(.el-pagination) {
   padding: 8px 0;
 
-  .btn-prev, .btn-next, .number {
-    min-width: 28px;
+  .btn-prev,
+  .btn-next,
+  .number {
     height: 28px;
+    min-width: 28px;
     line-height: 28px;
   }
 }
@@ -719,9 +775,8 @@ onUnmounted(() => {
 
 // 调整表单标签样式
 :deep(.el-form-item__label) {
-  font-size: 13px;
   height: 28px;
+  font-size: 13px;
   line-height: 28px;
 }
-
 </style>

+ 29 - 2
src/views/pms/iotopeationfill/index1.vue

@@ -136,8 +136,13 @@
               <el-form-item
                 v-if="companyName === 'ry' ? attrItem.description !== 'productionStatus' : true"
                 :label="attrItem.name"
-                prop="deviceId"
+                :prop="
+                  'attrList.' +
+                  attrList.findIndex((i) => i.description === attrItem.description) +
+                  '.fillContent'
+                "
                 label-position="top"
+                :rules="rules[attrItem.description]"
               >
                 <div v-if="fillStatus === '1'">
                   <el-select
@@ -727,6 +732,28 @@ const rules = reactive<FormRules>({
     { required: true, message: '请输入进尺工作时间' },
     { validator: ryValidateTotalTime() }
   ],
+  nightSupervisors: { required: true, message: '请输入夜班跟班干部' },
+  daySupervisors: { required: true, message: '请输入白班跟班干部' },
+  dailyGasInjection: [
+    {
+      validator: (_rule, value, callback) => {
+        if (companyName.value === 'rh') {
+          const dailyInjectGasTime = attrList.value.find(
+            (item) => item.description === 'dailyInjectGasTime'
+          )
+
+          if (dailyInjectGasTime && dailyInjectGasTime.fillContent > 0) {
+            if (value > 0) {
+              callback()
+            } else {
+              callback(new Error('当日运转时间大于0,注气量也需要大于0'))
+            }
+          }
+        }
+      },
+      trigger: ['blur', 'change']
+    }
+  ],
   otherProductionTime: [{ validator: ryValidateTotalTime() }],
   // ratedProductionTime: [
   //   { required: true, message: '请输入额定生产时间', trigger: 'blur' },
@@ -812,7 +839,7 @@ const getList = async () => {
 
     const daily = list.value.find((item) => item.deviceName === '生产日报') ?? { wellNamePair: {} }
 
-    taskOptions.value = Object.keys(daily.wellNamePair).map((key) => ({
+    taskOptions.value = Object.keys(daily.wellNamePair ?? {}).map((key) => ({
       label: daily.wellNamePair[key],
       value: Number(key)
     }))

+ 271 - 224
src/views/pms/iotprojectinfo/IotProjectInfoForm.vue

@@ -85,6 +85,20 @@
             />
           </el-form-item>
         </el-col>
+        <el-col :span="12">
+          <el-form-item label="项目部" prop="projectDeptId">
+            <el-select
+              v-model="formData.projectDeptId"
+              :options="deptOptions"
+              filterable
+              placeholder="请选择项目部"
+              clearable
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
         <el-col :span="12">
           <el-form-item label="责任人" prop="responsiblePerson">
             <el-tooltip
@@ -101,7 +115,7 @@
               type="primary"
               size="small"
               @click="openUserDialog"
-              style="margin-left: 10px;"
+              style="margin-left: 10px"
               :disabled="!formData.deptId"
             >
               选择责任人
@@ -109,9 +123,6 @@
             <div v-if="!formData.deptId" class="el-form-item__error">请先选择所属公司</div>
           </el-form-item>
         </el-col>
-      </el-row>
-
-      <el-row>
         <el-col :span="12">
           <el-form-item :label="t('project.overseaFlag')" prop="overseas">
             <el-select v-model="formData.overseas" placeholder="请选择" clearable>
@@ -124,6 +135,9 @@
             </el-select>
           </el-form-item>
         </el-col>
+      </el-row>
+
+      <el-row>
         <el-col :span="12">
           <el-form-item :label="t('project.overseaArea')" prop="overseasArea">
             <el-select v-model="formData.overseasArea" placeholder="请选择" clearable>
@@ -136,9 +150,6 @@
             </el-select>
           </el-form-item>
         </el-col>
-      </el-row>
-
-      <el-row>
         <el-col :span="12">
           <el-form-item :label="t('project.workArea')" prop="location">
             <el-autocomplete
@@ -163,7 +174,8 @@
             </div>
           </el-form-item>
         </el-col>
-
+      </el-row>
+      <el-row>
         <el-col :span="12">
           <el-form-item label="虚拟项目" prop="contractSubject">
             <el-switch
@@ -175,10 +187,7 @@
             />
           </el-form-item>
         </el-col>
-      </el-row>
-
-      <el-row>
-        <el-col :span="24">
+        <el-col :span="12">
           <el-form-item label="备注" prop="remark">
             <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
           </el-form-item>
@@ -235,18 +244,17 @@
   </ContentWrap>
 </template>
 <script setup lang="ts">
-
 import { IotProjectInfoApi, IotProjectInfoVO } from '@/api/pms/iotprojectinfo'
-import {defaultProps,handleTree} from "@/utils/tree";
-import * as DeptApi from "@/api/system/dept";
+import { defaultProps } from '@/utils/tree'
+import * as DeptApi from '@/api/system/dept'
 import CustomerList from '@/views/pms/device/CustomerList.vue'
-import {ref} from "vue";
-import {useUserStore} from "@/store/modules/user";
-import {IotProjectTaskApi, IotProjectTaskVO} from "@/api/pms/iotprojecttask";
-import {useTagsViewStore} from "@/store/modules/tagsView";
-import * as UserApi from "@/api/system/user";
-import {UserVO} from "@/api/system/user";
-import {DICT_TYPE, getIntDictOptions, getStrDictOptions, getDictLabel} from "@/utils/dict";
+import { ref } from 'vue'
+import { useUserStore } from '@/store/modules/user'
+import { IotProjectTaskVO } from '@/api/pms/iotprojecttask'
+import { useTagsViewStore } from '@/store/modules/tagsView'
+import * as UserApi from '@/api/system/user'
+import { UserVO } from '@/api/system/user'
+import { DICT_TYPE, getIntDictOptions, getStrDictOptions, getDictLabel } from '@/utils/dict'
 import { Loading } from '@element-plus/icons-vue'
 
 /** 项目信息 表单 */
@@ -265,21 +273,49 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
 const taskList = ref<IotProjectTaskVO[]>([]) // 列表的数据
 const formType = ref('') // 表单的类型:create - 新增;update - 修改
 const loading = ref(true) // 列表的加载中
-const simpleUsers = ref<UserVO[]>([])     // 责任人列表
+const simpleUsers = ref<UserVO[]>([]) // 责任人列表
+
+const deptLoading = ref(false)
+const deptOptions = ref<any[]>([])
+
+const user = useUserStore().getUser
+
+const userdeptId = user.deptId
+
+async function loadDept(deptId?: number) {
+  deptLoading.value = true
+  try {
+    const res = await DeptApi.specifiedSimpleDepts(deptId ?? userdeptId)
+    deptOptions.value = res
+      .map((item: any) => ({
+        label: item.name,
+        value: item.id,
+        raw: item
+      }))
+      .filter((item) => item.raw.type === '2')
+  } catch (error) {
+  } finally {
+    deptLoading.value = false
+  }
+}
+
+onMounted(() => {
+  loadDept()
+})
 
 // 添加责任人相关变量
-const userDialogVisible = ref(false);
-const userList = ref([]); // 所有用户列表
-const selectedUserIds = ref([]); // 选中的用户ID
-const selectedUserNames = ref(''); // 选中的用户名称显示
-const selectedUserList = ref([]); // 存储选中的用户对象列表
+const userDialogVisible = ref(false)
+const userList = ref([]) // 所有用户列表
+const selectedUserIds = ref([]) // 选中的用户ID
+const selectedUserNames = ref('') // 选中的用户名称显示
+const selectedUserList = ref([]) // 存储选中的用户对象列表
 
 // 添加 workAreaOptions 引用和加载状态
 const workAreaOptions = ref<any[]>([])
 const loadingWorkAreaOptions = ref(false)
 const workAreaAutocomplete = ref() // 添加对el-autocomplete的引用
 // currentDictLabel 响应式变量
-const currentDictLabel = ref(''); // 存储当前项目对应的施工区域字典类型
+const currentDictLabel = ref('') // 存储当前项目对应的施工区域字典类型
 
 const formData = ref({
   id: undefined,
@@ -299,16 +335,25 @@ const formData = ref({
   overseasArea: undefined,
   remark: undefined,
   dictType: undefined,
+  projectDeptId: undefined,
   userName: useUserStore().getUser.name,
-  userId: useUserStore().getUser.userId,
+  userId: useUserStore().getUser.userId
 })
+
+watch(
+  () => formData.value.deptId,
+  (newVal) => {
+    loadDept(newVal)
+  }
+)
+
 const close = () => {
   delView(unref(currentRoute))
-  push({ name: 'iotProjectInfo', params:{}})
+  push({ name: 'iotProjectInfo', params: {} })
 }
 
 const queryParams = reactive({
-  projectId:undefined
+  projectId: undefined
 })
 const formRules = reactive({
   manufacturerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
@@ -317,13 +362,13 @@ const formRules = reactive({
   contractCode: [{ required: true, message: '合同编码不能为空', trigger: 'blur' }],
   workloadTotal: [{ required: true, message: '工作量总数不能为空', trigger: 'blur' }],
   startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
-  endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }],
+  endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }]
 })
 const formRef1 = ref() // 表单 Ref
 const openForm = (type: string, id?: number) => {
   formRef1.value.open(type, id)
 }
-const zzClear = () =>{
+const zzClear = () => {
   formData.value.manufacturerId = undefined
   formData.value.manufactureName = undefined
 }
@@ -340,37 +385,37 @@ const openCustomerZz = () => {
 
 // 格式化用户名称显示
 const formatUserNames = (users) => {
-  if (!users || users.length === 0) return '';
+  if (!users || users.length === 0) return ''
 
-  const names = users.map(user => user.nickname);
+  const names = users.map((user) => user.nickname)
 
   if (names.length <= 2) {
-    return names.join(', ');
+    return names.join(', ')
   } else {
-    return `${names[0]}, ${names[1]}...`;
+    return `${names[0]}, ${names[1]}...`
   }
-};
+}
 
 // 根据用户ID获取用户信息
 const fetchUserInfoByIds = async (userIds) => {
   if (!userIds || userIds.length === 0) {
-    selectedUserList.value = [];
-    selectedUserNames.value = '';
-    return;
+    selectedUserList.value = []
+    selectedUserNames.value = ''
+    return
   }
 
   try {
     const params = {
       userIds: userIds
-    };
-    const response = await UserApi.companyDeptsEmployee(params); // 假设有这个API
-    selectedUserList.value = response;
-    selectedUserNames.value = formatUserNames(response);
+    }
+    const response = await UserApi.companyDeptsEmployee(params) // 假设有这个API
+    selectedUserList.value = response
+    selectedUserNames.value = formatUserNames(response)
   } catch (error) {
-    console.error('获取用户信息失败:', error);
-    ElMessage.error('获取用户信息失败');
+    console.error('获取用户信息失败:', error)
+    ElMessage.error('获取用户信息失败')
   }
-};
+}
 
 // 添加处理施工区域获取焦点的方法
 const handleWorkAreaFocus = async () => {
@@ -380,7 +425,7 @@ const handleWorkAreaFocus = async () => {
     try {
       // 直接调用el-autocomplete的focus方法
       if (workAreaAutocomplete.value && typeof workAreaAutocomplete.value.focus === 'function') {
-        workAreaAutocomplete.value.focus();
+        workAreaAutocomplete.value.focus()
       }
     } catch (error) {
       console.error('触发下拉框显示失败:', error)
@@ -395,7 +440,7 @@ const handleWorkAreaFocus = async () => {
     if (workAreaOptions.value.length > 0) {
       try {
         if (workAreaAutocomplete.value && typeof workAreaAutocomplete.value.focus === 'function') {
-          workAreaAutocomplete.value.focus();
+          workAreaAutocomplete.value.focus()
         }
       } catch (error) {
         console.error('触发下拉框显示失败:', error)
@@ -406,30 +451,30 @@ const handleWorkAreaFocus = async () => {
 
 // 根据部门ID获取部门名称
 const getDeptNames = (deptIds) => {
-  if (!deptIds || deptIds.length === 0) return '';
+  if (!deptIds || deptIds.length === 0) return ''
 
-  const names = [];
+  const names = []
   const findDept = (list, id) => {
     for (const item of list) {
       if (item.id === id) {
-        names.push(item.name);
-        return true;
+        names.push(item.name)
+        return true
       }
       if (item.children && findDept(item.children, id)) {
-        return true;
+        return true
       }
     }
-    return false;
-  };
+    return false
+  }
 
-  deptIds.forEach(id => findDept(deptList.value, id));
-  return names.join(', ');
-};
+  deptIds.forEach((id) => findDept(deptList.value, id))
+  return names.join(', ')
+}
 
 // 计算属性:判断部门选择框是否应该禁用
 const isDeptDisabled = computed(() => {
-  return formType.value === 'update' || deptList.value.length === 1;
-});
+  return formType.value === 'update' || deptList.value.length === 1
+})
 
 // 打开用户选择对话框
 const openUserDialog = async () => {
@@ -437,64 +482,61 @@ const openUserDialog = async () => {
     // 获取用户列表,传递部门ID参数
     const params = {
       deptIds: [formData.value.deptId] // 将部门ID转换为数组格式
-    };
+    }
     // 获取用户列表
-    const response = await UserApi.companyDeptsEmployee(params);
-    userList.value = response;
+    const response = await UserApi.companyDeptsEmployee(params)
+    userList.value = response
 
     // 设置当前已选中的用户
-    selectedUserIds.value = formData.value.responsiblePerson || [];
-
-    userDialogVisible.value = true;
+    selectedUserIds.value = formData.value.responsiblePerson || []
 
+    userDialogVisible.value = true
   } catch (error) {
-    console.error('获取用户列表失败:', error);
-    ElMessage.error('获取用户列表失败');
+    console.error('获取用户列表失败:', error)
+    ElMessage.error('获取用户列表失败')
   }
-};
+}
 
 // 确认用户选择
 const confirmUserSelection = () => {
   // 更新表单数据
-  formData.value.responsiblePerson = [...selectedUserIds.value];
+  formData.value.responsiblePerson = [...selectedUserIds.value]
 
   // 更新显示的用户名称
   // updateSelectedUserNames();
 
-  selectedUserList.value = userList.value.filter(user =>
-    selectedUserIds.value.includes(user.id)
-  );
+  selectedUserList.value = userList.value.filter((user) => selectedUserIds.value.includes(user.id))
   // 更新显示的用户名称
-  selectedUserNames.value = formatUserNames(selectedUserList.value);
+  selectedUserNames.value = formatUserNames(selectedUserList.value)
 
-  userDialogVisible.value = false;
-};
+  userDialogVisible.value = false
+}
 
 // 更新显示的用户名称
 const updateSelectedUserNames = () => {
   console.log('已经保存的责任人:' + formData.value.responsiblePerson)
   if (!formData.value.responsiblePerson || formData.value.responsiblePerson.length === 0) {
-    selectedUserNames.value = '';
-    return;
+    selectedUserNames.value = ''
+    return
   }
 
-  const selectedUsers = userList.value.filter(user =>
+  const selectedUsers = userList.value.filter((user) =>
     formData.value.responsiblePerson.includes(user.id)
-  );
+  )
 
-  const names = selectedUsers.map(user => user.nickname);
+  const names = selectedUsers.map((user) => user.nickname)
 
   if (names.length <= 2) {
-    selectedUserNames.value = names.join(', ');
+    selectedUserNames.value = names.join(', ')
   } else {
-    selectedUserNames.value = `${names[0]}, ${names[1]}...`;
+    selectedUserNames.value = `${names[0]}, ${names[1]}...`
   }
-};
+}
 
 // 关闭用户选择对话框
 const handleUserDialogClose = () => {
-  userDialogVisible.value = false;
-};
+  userDialogVisible.value = false
+}
 
 const tableData = ref([
   {
@@ -505,42 +547,42 @@ const tableData = ref([
     technique: '',
     workloadDesign: '',
     deptIds: [],
-    remark:'',
+    remark: '',
     editData: {},
-    editing:true,
+    editing: true,
     originalData: {},
     errors: {}
-  },
-]);
-let deptInfo: any[] = "";
+  }
+])
+let deptInfo: any[] = ''
 /** 打开弹窗 */
 const open = async () => {
   resetForm()
   // 修改时,设置数据
   if (id) {
-    formType.value = 'update';
+    formType.value = 'update'
     formLoading.value = true
     try {
       formData.value = await IotProjectInfoApi.getIotProjectInfo(id)
 
       // 如果已有责任人数据,获取用户信息并显示
       if (formData.value.responsiblePerson && formData.value.responsiblePerson.length > 0) {
-        await fetchUserInfoByIds(formData.value.responsiblePerson);
-        selectedUserIds.value = [...formData.value.responsiblePerson];
+        await fetchUserInfoByIds(formData.value.responsiblePerson)
+        selectedUserIds.value = [...formData.value.responsiblePerson]
       }
 
       // 编辑页面:根据项目所属公司加载施工区域选项
       if (formData.value.deptId) {
-        await loadWorkAreaOptions(formData.value.deptId);
+        await loadWorkAreaOptions(formData.value.deptId)
       }
     } finally {
       formLoading.value = false
     }
   } else {
-    formType.value = 'create';
+    formType.value = 'create'
     // 如果只有一个部门,则自动选中
     if (deptList.value.length === 1) {
-      formData.value.deptId = deptList.value[0].id;
+      formData.value.deptId = deptList.value[0].id
     }
   }
 }
@@ -555,14 +597,14 @@ const submitForm = async () => {
   await formRef.value.validate()
 
   // 将当前字典类型赋值给formData
-  formData.value.dictType = currentDictLabel.value;
+  formData.value.dictType = currentDictLabel.value
 
   // 提交请求
   formLoading.value = true
   try {
     // formData.value.deptId = useUserStore().getUser.deptId;
     const data = {
-      projectData: formData.value as unknown as IotProjectInfoVO,
+      projectData: formData.value as unknown as IotProjectInfoVO
       //taskList:tableData.value
     }
     if (formType.value === 'create') {
@@ -599,9 +641,9 @@ const resetForm = () => {
     payment: undefined,
     userName: undefined,
     userId: undefined,
-    responsiblePerson: [], // 重置责任人字段
+    responsiblePerson: [] // 重置责任人字段
   }
-  selectedUserNames.value = '';
+  selectedUserNames.value = ''
   formRef.value?.resetFields()
 }
 onMounted(async () => {
@@ -611,35 +653,34 @@ onMounted(async () => {
 
   // 如果只有一个部门,则自动选中
   if (deptList.value.length === 1) {
-    formData.value.deptId = deptList.value[0].id;
+    formData.value.deptId = deptList.value[0].id
   }
-  open();
+  open()
 })
 
-const showCloumn = false;
+const showCloumn = false
 // 表格数据
 
 // 计算正在编辑的行数
 const editingRowsCount = computed(() => {
-  return tableData.value.filter(row => row.editing).length;
-});
+  return tableData.value.filter((row) => row.editing).length
+})
 
 // 格式化日期
 const formatDate = (timestamp) => {
-  const date = new Date(timestamp);
-  return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
-};
+  const date = new Date(timestamp)
+  return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
+}
 
 // 为编辑中的行添加特殊样式
 const rowClassName = ({ row }) => {
-  return row.editing ? 'editable-row' : '';
-};
+  return row.editing ? 'editable-row' : ''
+}
 
 // 添加新行
 const addNewRow = () => {
-  const newId = tableData.value.length > 0
-    ? Math.max(...tableData.value.map(item => item.id)) + 1
-    : 1;
+  const newId =
+    tableData.value.length > 0 ? Math.max(...tableData.value.map((item) => item.id)) + 1 : 1
 
   const newRow = {
     id: newId,
@@ -649,7 +690,7 @@ const addNewRow = () => {
     technique: '',
     workloadDesign: '',
     deptIds: [],
-    remark:'',
+    remark: '',
     editing: true,
     editData: {
       wellName: '',
@@ -657,20 +698,20 @@ const addNewRow = () => {
       location: '',
       technique: '',
       workloadDesign: '',
-      deptIds:[],
-      remark:'',
+      deptIds: [],
+      remark: ''
     },
     originalData: {},
     errors: {}
-  };
+  }
 
-  tableData.value.unshift(newRow);
-};
+  tableData.value.unshift(newRow)
+}
 
 // 编辑行
 const editRow = (row) => {
   // 保存原始数据用于取消编辑时恢复
-  row.originalData = {...row};
+  row.originalData = { ...row }
   // 设置编辑数据
   row.editData = {
     id: row.id,
@@ -680,166 +721,173 @@ const editRow = (row) => {
     technique: row.technique,
     workloadDesign: row.workloadDesign,
     deptIds: row.deptIds,
-    remark:row.remark,
-  };
+    remark: row.remark
+  }
   // 进入编辑状态
-  row.editing = true;
+  row.editing = true
   // 清空错误信息
-  row.errors = {};
-};
+  row.errors = {}
+}
 
 // 验证行数据
 const validateRow = (row) => {
-  row.errors = {};
-  let valid = true;
+  row.errors = {}
+  let valid = true
 
   if (!row.editData.wellName || row.editData.wellName.trim() === '') {
-    row.errors.name = '井号不能为空';
-    valid = false;
+    row.errors.name = '井号不能为空'
+    valid = false
   }
 
   if (!row.editData.wellType || row.editData.wellType.trim() === '') {
-    row.errors.email = '井型不能为空';
-    valid = false;
+    row.errors.email = '井型不能为空'
+    valid = false
   }
 
-  return valid;
-};
+  return valid
+}
 
 // 保存行
 const saveRow = (row) => {
-  if (!validateRow(row)) return;
+  if (!validateRow(row)) return
 
   // 将编辑数据应用到行
-  row.id = row.editData.id;
-  row.wellName = row.editData.wellName;
-  row.wellType = row.editData.wellType;
-  row.location = row.editData.location;
-  row.technique = row.editData.technique;
-  row.workloadDesign = row.editData.workloadDesign;
-  row.deptIds = row.editData.deptIds;
-  row.remark = row.editData.remark;
+  row.id = row.editData.id
+  row.wellName = row.editData.wellName
+  row.wellType = row.editData.wellType
+  row.location = row.editData.location
+  row.technique = row.editData.technique
+  row.workloadDesign = row.editData.workloadDesign
+  row.deptIds = row.editData.deptIds
+  row.remark = row.editData.remark
   // 退出编辑状态
-  row.editing = false;
-};
+  row.editing = false
+}
 
 // 生成Tooltip要显示的完整责任人名称字符串
 const getFullSelectedUserNames = () => {
   if (!selectedUserList.value || selectedUserList.value.length === 0) {
-    return '';
+    return ''
   }
-  return selectedUserList.value.map(user => user.nickname).join(', ');
-};
+  return selectedUserList.value.map((user) => user.nickname).join(', ')
+}
 
 // 保存所有更改
 const saveAll = () => {
-  let allValid = true;
+  let allValid = true
 
   // 验证所有编辑中的行
-  tableData.value.forEach(row => {
+  tableData.value.forEach((row) => {
     if (row.editing && !validateRow(row)) {
-      allValid = false;
+      allValid = false
     }
-  });
+  })
 
   if (!allValid) {
-    ElMessage.error('部分数据验证失败,请检查输入');
-    return;
+    ElMessage.error('部分数据验证失败,请检查输入')
+    return
   }
 
   // 保存所有编辑中的行
-  tableData.value.forEach(row => {
+  tableData.value.forEach((row) => {
     if (row.editing) {
-      row.id = row.editData.id;
-      row.wellName = row.editData.wellName;
-      row.wellType = row.editData.wellType;
-      row.location = row.editData.location;
-      row.technique = row.editData.technique;
-      row.workloadDesign = row.editData.workloadDesign;
-      row.deptIds = row.editData.deptIds;
-      row.remark = row.editData.remark;
+      row.id = row.editData.id
+      row.wellName = row.editData.wellName
+      row.wellType = row.editData.wellType
+      row.location = row.editData.location
+      row.technique = row.editData.technique
+      row.workloadDesign = row.editData.workloadDesign
+      row.deptIds = row.editData.deptIds
+      row.remark = row.editData.remark
     }
-  });
+  })
 
-  ElMessage.success('所有更改已保存');
-};
+  ElMessage.success('所有更改已保存')
+}
 
 // 取消编辑
 const cancelEdit = (row) => {
   // 恢复原始数据
   if (row.originalData.id) {
-    row.id = row.originalData.id;
-    row.wellName = row.originalData.wellName;
-    row.wellType = row.originalData.wellType;
-    row.location = row.originalData.location;
-    row.technique = row.originalData.technique;
-    row.workloadDesign = row.originalData.workloadDesign;
-    row.deptIds = row.originalData.deptIds;
-    row.remark = row.originalData.remark;
+    row.id = row.originalData.id
+    row.wellName = row.originalData.wellName
+    row.wellType = row.originalData.wellType
+    row.location = row.originalData.location
+    row.technique = row.originalData.technique
+    row.workloadDesign = row.originalData.workloadDesign
+    row.deptIds = row.originalData.deptIds
+    row.remark = row.originalData.remark
   } else {
     // 如果是新增行,则删除
-    const index = tableData.value.indexOf(row);
+    const index = tableData.value.indexOf(row)
     if (index !== -1) {
-      tableData.value.splice(index, 1);
+      tableData.value.splice(index, 1)
     }
   }
 
-  row.editing = false;
-};
+  row.editing = false
+}
 
 // 删除行
 const deleteRow = (index) => {
-  tableData.value.splice(index, 1);
-  ElMessage.success('行已删除');
-};
+  tableData.value.splice(index, 1)
+  ElMessage.success('行已删除')
+}
 
 // 添加监听部门ID变化的方法
-watch(() => formData.value.deptId, async (newDeptId, oldDeptId) => {
-  // 只有当部门ID确实发生变化时才加载选项
-  if (newDeptId && newDeptId !== oldDeptId) {
-    await loadWorkAreaOptions(newDeptId);
+watch(
+  () => formData.value.deptId,
+  async (newDeptId, oldDeptId) => {
+    // 只有当部门ID确实发生变化时才加载选项
+    if (newDeptId && newDeptId !== oldDeptId) {
+      await loadWorkAreaOptions(newDeptId)
+    }
   }
-});
+)
 
 // 添加立即执行的监听器,用于处理编辑页面
-watch(() => formData.value.deptId, async (newDeptId) => {
-  if (newDeptId) {
-    await loadWorkAreaOptions(newDeptId);
-  } else {
-    workAreaOptions.value = [];
-  }
-}, { immediate: true });
+watch(
+  () => formData.value.deptId,
+  async (newDeptId) => {
+    if (newDeptId) {
+      await loadWorkAreaOptions(newDeptId)
+    } else {
+      workAreaOptions.value = []
+    }
+  },
+  { immediate: true }
+)
 
 // 加载工作区域选项的方法
 const loadWorkAreaOptions = async (deptId: number) => {
   // 如果正在加载,直接返回
-  if (loadingWorkAreaOptions.value) return;
+  if (loadingWorkAreaOptions.value) return
 
-  loadingWorkAreaOptions.value = true;
+  loadingWorkAreaOptions.value = true
   try {
     // 先尝试通过 getDictLabel 获取字典标签
-    const dictLabel = await getDictLabel(DICT_TYPE.PMS_PROJECT_WORK_AREA, deptId);
+    const dictLabel = await getDictLabel(DICT_TYPE.PMS_PROJECT_WORK_AREA, deptId)
     console.log('当前专业公司的施工区域数据字典类型:' + dictLabel)
     // 将 dictLabel 存储到 currentDictLabel
-    currentDictLabel.value = dictLabel || ''; // 如果没有获取到,设为空字符串
+    currentDictLabel.value = dictLabel || '' // 如果没有获取到,设为空字符串
 
     if (dictLabel) {
       // 如果获取到标签,再获取完整的字典选项
-      const dictOptions = getStrDictOptions(dictLabel);
+      const dictOptions = getStrDictOptions(dictLabel)
       console.log('当前专业公司的施工区域数据字典集合长度:' + dictOptions.length)
-      workAreaOptions.value = dictOptions.map(option => ({
+      workAreaOptions.value = dictOptions.map((option) => ({
         value: option.label, // 使用标签作为显示值
-        id: option.value     // 保留原始值
-      }));
+        id: option.value // 保留原始值
+      }))
     } else {
       // 如果获取不到标签,清空选项
-      workAreaOptions.value = [];
+      workAreaOptions.value = []
     }
   } catch (error) {
-    console.error('获取工作区域选项失败:', error);
-    workAreaOptions.value = [];
+    console.error('获取工作区域选项失败:', error)
+    workAreaOptions.value = []
   } finally {
-    loadingWorkAreaOptions.value = false;
+    loadingWorkAreaOptions.value = false
   }
 }
 
@@ -847,41 +895,40 @@ const loadWorkAreaOptions = async (deptId: number) => {
 const querySearch = (queryString: string, cb: any) => {
   // 如果没有查询字符串,返回所有选项
   if (!queryString) {
-    cb(workAreaOptions.value);
-    return;
+    cb(workAreaOptions.value)
+    return
   }
 
   // 有查询字符串时进行筛选
-  const results = workAreaOptions.value.filter(option =>
+  const results = workAreaOptions.value.filter((option) =>
     option.value.toLowerCase().includes(queryString.toLowerCase())
-  );
+  )
 
   // 调用 callback 返回建议列表的数据
-  cb(results);
+  cb(results)
 }
 
 // 添加选择处理方法
 const handleSelect = (item: any) => {
   // 这里可以根据需要处理选择后的逻辑
-  console.log('选择了:', item.value);
+  console.log('选择了:', item.value)
 }
-
 </script>
 
 <style scoped>
 .user-names {
-  padding: 0px 10px;
-  border: 1px solid #dcdfe6;
-  border-radius: 4px;
-  background-color: #f5f7fa;
   display: inline-block;
-  min-width: 200px;
   max-height: 32px;
+  min-width: 200px;
+  padding: 0 10px;
+  background-color: #f5f7fa;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
 }
 
 .transfer-container {
+  padding: 0;
   text-align: center;
-  padding: 0px 0;
 }
 
 /* 3. 直接调整 el-transfer 左右窗口的宽度 */
@@ -896,14 +943,14 @@ const handleSelect = (item: any) => {
 
 :deep(.el-transfer-panel__item) {
   display: flex !important;
-  align-items: center !important;
   height: 32px !important;
-  line-height: 32px !important;
   padding: 0 8px !important;
   margin: 0 !important;
-  white-space: nowrap;
   overflow: hidden;
+  line-height: 32px !important;
   text-overflow: ellipsis;
+  white-space: nowrap;
+  align-items: center !important;
 }
 
 .transfer-option-text {

+ 4 - 5
src/views/pms/iotprojectinfo/IotProjectTaskForm.vue

@@ -46,7 +46,7 @@
         </el-col>
       </el-row>
       <el-form-item label="备注" prop="remark">
-        <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea"/>
+        <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
       </el-form-item>
     </el-form>
     <template #footer>
@@ -78,10 +78,9 @@ const formData = ref({
   workloadDesign: undefined,
   userName: undefined,
   userId: undefined,
-  remark: undefined,
-})
-const formRules = reactive({
+  remark: undefined
 })
+const formRules = reactive({})
 const formRef = ref() // 表单 Ref
 
 /** 打开弹窗 */
@@ -138,7 +137,7 @@ const resetForm = () => {
     workloadDesign: undefined,
     userName: undefined,
     userId: undefined,
-    remark: undefined,
+    remark: undefined
   }
   formRef.value?.resetFields()
 }

+ 62 - 5
src/views/pms/iotprojectinfo/index.vue

@@ -23,6 +23,24 @@
           />
         </el-select>
       </el-form-item>
+      <el-form-item label="项目部" prop="projectDeptId">
+        <el-select
+          v-model="queryParams.projectDeptId"
+          :placeholder="queryParams.companyDeptId ? '请选择项目部' : '请先选择公司'"
+          :disabled="!queryParams.companyDeptId"
+          :loading="projectDeptLoading"
+          clearable
+          filterable
+          class="!w-240px"
+        >
+          <el-option
+            v-for="item in projectDeptOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </el-form-item>
       <!--
       <el-form-item label="客户名称" prop="manufactureName">
         <el-input
@@ -367,14 +385,14 @@ import { IotProjectInfoApi, IotProjectInfoVO } from '@/api/pms/iotprojectinfo'
 import { IotProjectTaskApi } from '@/api/pms/iotprojecttask'
 import { useUserStore } from '@/store/modules/user'
 import { DICT_TYPE, getIntDictOptions, getDictLabel, getStrDictOptions } from '@/utils/dict'
-import { IotDeviceApi } from '@/api/pms/device' // 引入设备API
-import * as UserApi from '@/api/system/user' // 引入用户API
-import * as DeptApi from '@/api/system/dept' // 引入部门API
-import { handleTree } from '@/utils/tree' // 引入树形处理工具
+import { IotDeviceApi } from '@/api/pms/device'; // 引入设备API
+import * as UserApi from '@/api/system/user'; // 引入用户API
+import * as DeptApi from '@/api/system/dept'; // 引入部门API
+import { handleTree } from '@/utils/tree'; // 引入树形处理工具
 import { IotProjectTaskScheduleApi } from '@/api/pms/iotprojecttaskschedule'
 import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
 import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
-import dayjs from 'dayjs' // 引入 dayjs 用于时间格式化
+import dayjs from 'dayjs'; // 引入 dayjs 用于时间格式化
 import { ref, reactive, onMounted, computed, nextTick, watch } from 'vue'
 
 /** 项目信息 列表 */
@@ -389,6 +407,7 @@ const taskList = ref([]) // 任务列表的数据
 const selectedProject = ref(null) // 当前选中的项目
 const deptList = ref([]) // 部门列表
 const companyDeptList = ref<any[]>([]) // 在公司级部门列表
+const projectDeptOptions = ref<any[]>([]) // 项目部选项
 const deviceMap = ref({}) // 设备映射表
 const responsiblePersonList = ref([]) // 责任人列表
 
@@ -418,6 +437,7 @@ const queryParams = reactive({
   pageSize: 10,
   deptId: undefined,
   companyDeptId: undefined,
+  projectDeptId: undefined,
   deptName: undefined,
   contractName: undefined,
   contractCode: undefined,
@@ -434,6 +454,7 @@ const queryParams = reactive({
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
+const projectDeptLoading = ref(false)
 const { push } = useRouter() // 路由跳转
 
 // 列宽度配置
@@ -612,6 +633,29 @@ const resetQuery = () => {
   handleQuery()
 }
 
+const loadProjectDeptOptions = async (companyDeptId?: number) => {
+  if (!companyDeptId) {
+    projectDeptOptions.value = []
+    return
+  }
+
+  projectDeptLoading.value = true
+  try {
+    const res = await DeptApi.specifiedSimpleDepts(companyDeptId)
+    projectDeptOptions.value = res
+      .map((item: any) => ({
+        label: item.name,
+        value: item.id,
+        raw: item
+      }))
+      .filter((item) => item.raw.type === '2')
+  } catch (error) {
+    projectDeptOptions.value = []
+  } finally {
+    projectDeptLoading.value = false
+  }
+}
+
 // 加载施工工艺数据字典选项
 const loadTechnologyDictOptions = async (deptId: number) => {
   // 如果正在加载或已加载,直接返回
@@ -1037,6 +1081,9 @@ onMounted(async () => {
   companyDeptList.value = await DeptApi.companyLevelDepts()
 
   deptList.value = handleTree(await DeptApi.companyLevelChildrenDepts())
+  if (queryParams.companyDeptId) {
+    await loadProjectDeptOptions(queryParams.companyDeptId)
+  }
   getList()
   // 预加载任务进度字典
   // getTaskScheduleDictOptions()
@@ -1067,6 +1114,16 @@ onUnmounted(() => {
   }
 })
 
+watch(
+  () => queryParams.companyDeptId,
+  async (newVal, oldVal) => {
+    if (newVal === oldVal) return
+
+    queryParams.projectDeptId = undefined
+    await loadProjectDeptOptions(newVal)
+  }
+)
+
 // 监听列表数据变化重新计算列宽
 watch(
   list,

+ 49 - 4
src/views/pms/iotprojecttask/IotProjectTaskForm.vue

@@ -87,9 +87,24 @@
           </el-form-item>
         </el-col>
       </el-row>
-      <el-form-item label="备注" prop="remark">
-        <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" disabled />
-      </el-form-item>
+      <el-row>
+        <el-col :span="12"
+          ><el-form-item label="项目部" prop="projectDeptId">
+            <el-select
+              v-model="formData.projectDeptId"
+              :options="deptOptions"
+              filterable
+              placeholder="请选择项目部"
+              clearable
+              disabled
+            /> </el-form-item
+        ></el-col>
+        <el-col :span="12"
+          ><el-form-item label="备注" prop="remark">
+            <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" disabled />
+          </el-form-item>
+        </el-col>
+      </el-row>
     </el-form>
   </ContentWrap>
 
@@ -860,6 +875,34 @@ function handlePreview(file: any) {
   }
 }
 
+const deptLoading = ref(false)
+const deptOptions = ref<any[]>([])
+
+const user = useUserStore().getUser
+
+const userdeptId = user.deptId
+
+async function loadDept(deptId?: number) {
+  deptLoading.value = true
+  try {
+    const res = await DeptApi.specifiedSimpleDepts(deptId ?? userdeptId)
+    deptOptions.value = res
+      .map((item: any) => ({
+        label: item.name,
+        value: item.id,
+        raw: item
+      }))
+      .filter((item) => item.raw.type === '2')
+  } catch (error) {
+  } finally {
+    deptLoading.value = false
+  }
+}
+
+onMounted(() => {
+  loadDept()
+})
+
 // 施工队伍 选择树 响应式变量
 const defaultExpandedKeys = ref<number[]>([]) // 默认展开的部门节点keys
 const treeSelectRef = ref() // 树选择组件的引用
@@ -958,7 +1001,8 @@ const formData = ref({
   manufacturerId: undefined,
   userName: undefined,
   userId: undefined,
-  deptId: undefined // 新增项目部门ID字段
+  deptId: undefined, // 新增项目部门ID字段
+  projectDeptId: undefined // 新增项目部门ID字段
 })
 
 const close = () => {
@@ -995,6 +1039,7 @@ const getProjectInfo = async (contractId: number) => {
     formData.value.manufacturerId = project.manufacturerId
     formData.value.id = project.id
     formData.value.deptId = project.deptId // 保存项目部门ID
+    formData.value.projectDeptId = project.projectDeptId // 保存项目部门ID
     // 获取施工区域数据字典集合
     await loadWorkAreaOptions(formData.value.deptId)
     // 获取施工工艺数据字典集合

+ 19 - 3
src/views/pms/iotrddailyreport/create-rd-form.vue

@@ -60,7 +60,6 @@ interface Form {
 }
 
 const original = {
-  deptId,
   timeRange: ['08:00', '08:00'],
   ...NON_PROD_FIELDS.reduce((acc, field) => ({ ...acc, [field.key]: 0 }), {}),
   winterBreakTime: 24
@@ -122,6 +121,7 @@ interface UserOptions {
   label: string
   value: number
   raw: any
+  disabled: boolean
 }
 
 const userOptions = ref<UserOptions[]>([])
@@ -143,6 +143,7 @@ async function loadDept() {
       })
 
       sortedNodes.forEach((node) => {
+        node.disabled = (node as any).type !== '3'
         if (node.children && Array.isArray(node.children)) {
           node.children = sortTreeBySort(node.children)
         }
@@ -169,6 +170,8 @@ async function loadUserOptions() {
 
 const loading = ref(false)
 
+const detail = ref<any>()
+
 async function load() {
   loading.value = true
 
@@ -179,6 +182,7 @@ async function load() {
 
   if (props.isview && props.id) {
     const res = await IotRdDailyReportApi.getIotRdDailyReport(props.id)
+    detail.value = res
     submitterNames.value = res.submitterNames
     form.value = {
       deptId: res.deptId,
@@ -243,6 +247,18 @@ async function submitForm() {
     formLoading.value = false
   }
 }
+
+const disabled = computed(() => {
+  if (props.isview !== 'create') {
+    if (props.isview === 'time') {
+      return !(detail.value?.contractName === null && detail.value?.taskName === null)
+    }
+
+    return true
+  }
+
+  return false
+})
 </script>
 
 <template>
@@ -271,7 +287,7 @@ async function submitForm() {
         class="flex flex-col"
         :rules="rules"
         :loading="loading"
-        :disabled="props.isview !== 'create'"
+        :disabled="disabled"
       >
         <div class="grid grid-cols-2 gap-x-8">
           <el-form-item label="施工队伍" prop="deptId">
@@ -289,7 +305,7 @@ async function submitForm() {
           <el-form-item label="施工日期" prop="createTime">
             <el-date-picker
               v-model="form.createTime"
-              type="datetime"
+              type="date"
               clearable
               class="w-full!"
               placeholder="请选择施工日期"

+ 4 - 4
src/views/pms/iotrddailyreport/fillDailyReport.vue

@@ -190,11 +190,11 @@ function realValue(type: any, value: string) {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="163" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="163" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 4 - 4
src/views/pms/iotrddailyreport/index.vue

@@ -237,11 +237,11 @@ onMounted(() => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="163" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="163" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 4 - 4
src/views/pms/iotrddailyreport/statistics.vue

@@ -167,11 +167,11 @@ function handleWellNameClick(taskId: number) {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="163" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="163" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 10 - 9
src/views/pms/iotrddailyreport/summary.vue

@@ -460,16 +460,17 @@ const { ZmTable, ZmTableColumn } = useTableComponents()
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
-      <DeptTreeSelect
-        :deptId="id"
-        :top-id="163"
-        v-model="query.deptId"
-        @node-click="handleDeptNodeClick"
-      />
-    </div>
+    <DeptTreeSelect
+      :deptId="id"
+      :top-id="163"
+      v-model="query.deptId"
+      @node-click="handleDeptNodeClick"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
+
+    </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 5 - 4
src/views/pms/iotrhdailyreport/approval.vue

@@ -106,11 +106,12 @@ onMounted(() => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="157" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="157" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
+
+    </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 3 - 4
src/views/pms/iotrhdailyreport/fill.vue

@@ -106,11 +106,10 @@ onMounted(() => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="157" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="157" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 69 - 35
src/views/pms/iotrhdailyreport/index.vue

@@ -1,5 +1,8 @@
 <script setup lang="ts">
-import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
+import {
+  IotRhDailyReportApi,
+  type IotRhDailyReportTotalWorkloadVO
+} from '@/api/pms/iotrhdailyreport'
 import { useUserStore } from '@/store/modules/user'
 import download from '@/utils/download'
 import { rangeShortcuts } from '@/utils/formatTime'
@@ -95,6 +98,13 @@ const totalWork = ref({
 
 const totalLoading = ref(false)
 
+const totalWorkloadDetail = ref({
+  totalN2GasInjection: 0,
+  totalNaturalGasInjection: 0
+})
+
+const formatGasInjectionTooltipValue = (value?: number | null) => ((value || 0) / 10000).toFixed(2)
+
 const getTotal = useDebounceFn(async function () {
   totalLoading.value = true
 
@@ -113,7 +123,12 @@ const getTotal = useDebounceFn(async function () {
       totalWork.value.notReported = res1[2].count
     }
 
-    const res2 = await IotRhDailyReportApi.totalWorkload(other)
+    const res2: IotRhDailyReportTotalWorkloadVO = await IotRhDailyReportApi.totalWorkload(other)
+
+    totalWorkloadDetail.value = {
+      totalN2GasInjection: res2.totalN2GasInjection || 0,
+      totalNaturalGasInjection: res2.totalNaturalGasInjection || 0
+    }
 
     totalWork.value = {
       ...totalWork.value,
@@ -233,11 +248,12 @@ const openUnfilledDialog = () => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[48px_auto_1fr_auto] gap-x-4 gap-y-3 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[48px_auto_1fr_auto] gap-x-4 gap-y-3 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-4">
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-4">
       <DeptTreeSelect :top-id="157" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    </div> -->
+    <DeptTreeSelect :top-id="157" :deptId="deptId" v-model="query.deptId" :show-title="false" />
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-6 gap-8 flex items-center justify-between"
@@ -302,38 +318,56 @@ const openUnfilledDialog = () => {
       </el-form-item>
     </el-form>
     <div class="grid grid-cols-5 gap-3">
-      <div
-        v-for="info in totalWorkKeys"
-        :key="info[0]"
-        class="group relative bg-white dark:bg-[#1d1e1f] rounded-lg shadow-sm hover:shadow-md border border-gray-100 dark:border-gray-700 p-2.5 overflow-hidden transition-all duration-300"
-        :class="{
-          'cursor-pointer hover:border-blue-200 dark:hover:border-blue-800': info[2] === '未填报'
-        }"
-        @click="info[2] === '未填报' ? openUnfilledDialog() : ''"
-      >
-        <div class="relative z-10 flex flex-col h-full justify-center">
-          <span
-            class="text-[11px] text-gray-400 dark:text-gray-500 font-medium tracking-wide mb-0.5"
+      <template v-for="info in totalWorkKeys" :key="info[0]">
+        <el-tooltip :disabled="info[0] !== 'totalGasInjection'" placement="top">
+          <template #content>
+            <div>
+              累计氮气注气量:{{
+                formatGasInjectionTooltipValue(totalWorkloadDetail.totalN2GasInjection)
+              }}
+              万方
+            </div>
+            <div>
+              累计天然气注气量:{{
+                formatGasInjectionTooltipValue(totalWorkloadDetail.totalNaturalGasInjection)
+              }}
+              万方
+            </div>
+          </template>
+          <div
+            class="group relative bg-white dark:bg-[#1d1e1f] rounded-lg shadow-sm hover:shadow-md border border-gray-100 dark:border-gray-700 p-2.5 overflow-hidden transition-all duration-300"
+            :class="{
+              'cursor-pointer hover:border-blue-200 dark:hover:border-blue-800':
+                info[2] === '未填报',
+              'cursor-help': info[0] === 'totalGasInjection'
+            }"
+            @click="info[2] === '未填报' ? openUnfilledDialog() : ''"
           >
-            {{ info[2] }}
-          </span>
-          <div class="flex items-baseline gap-1">
-            <count-to
-              class="text-lg font-bold text-gray-800 dark:text-gray-100 leading-none tracking-tight font-sans"
-              :start-val="0"
-              :end-val="totalWork[info[0]]"
-              :decimals="info[4]"
-            />
-            <span class="text-[10px] text-gray-400 font-normal">{{ info[1] }}</span>
+            <div class="relative z-10 flex flex-col h-full justify-center">
+              <span
+                class="text-[11px] text-gray-400 dark:text-gray-500 font-medium tracking-wide mb-0.5"
+              >
+                {{ info[2] }}
+              </span>
+              <div class="flex items-baseline gap-1">
+                <count-to
+                  class="text-lg font-bold text-gray-800 dark:text-gray-100 leading-none tracking-tight font-sans"
+                  :start-val="0"
+                  :end-val="totalWork[info[0]]"
+                  :decimals="info[4]"
+                />
+                <span class="text-[10px] text-gray-400 font-normal">{{ info[1] }}</span>
+              </div>
+            </div>
+
+            <div
+              class="absolute -right-2 -bottom-3 opacity-40 dark:opacity-60 transform rotate-[-10deg] transition-transform duration-500 group-hover:scale-110 group-hover:rotate-0"
+            >
+              <div :class="info[3]" class="text-5xl"></div>
+            </div>
           </div>
-        </div>
-
-        <div
-          class="absolute -right-2 -bottom-3 opacity-40 dark:opacity-60 transform rotate-[-10deg] transition-transform duration-500 group-hover:scale-110 group-hover:rotate-0"
-        >
-          <div :class="info[3]" class="text-5xl"></div>
-        </div>
-      </div>
+        </el-tooltip>
+      </template>
     </div>
 
     <rh-table

+ 2 - 0
src/views/pms/iotrhdailyreport/rh-table.vue

@@ -45,6 +45,7 @@ interface ListItem {
   yearTotalPower: number
   yearTotalFuel: number
   capacity: number
+  techniqueNames: string
 }
 
 const props = defineProps({
@@ -196,6 +197,7 @@ function handleCurrentChange(val: number) {
               </template>
             </zm-table-column>
             <zm-table-column label="施工区域" prop="location" />
+            <zm-table-column label="施工工艺" prop="techniqueNames" />
             <zm-table-column label="搬迁安装天数" prop="relocationDays" />
             <zm-table-column label="设计注气量(万方)" prop="designInjection" />
             <zm-table-column

+ 66 - 29
src/views/pms/iotrhdailyreport/summary.vue

@@ -1,6 +1,9 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
-import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
+import {
+  IotRhDailyReportApi,
+  type IotRhDailyReportTotalWorkloadVO
+} from '@/api/pms/iotrhdailyreport'
 import { useDebounceFn } from '@vueuse/core'
 import CountTo from '@/components/count-to1.vue'
 import * as echarts from 'echarts'
@@ -90,6 +93,13 @@ const totalWork = ref({
 
 const totalLoading = ref(false)
 
+const totalWorkloadDetail = ref({
+  totalN2GasInjection: 0,
+  totalNaturalGasInjection: 0
+})
+
+const formatGasInjectionTooltipValue = (value?: number | null) => ((value || 0) / 10000).toFixed(2)
+
 const getTotal = useDebounceFn(async () => {
   totalLoading.value = true
 
@@ -108,7 +118,12 @@ const getTotal = useDebounceFn(async () => {
       totalWork.value.notReported = res1[2].count
     }
 
-    const res2 = await IotRhDailyReportApi.totalWorkload(other)
+    const res2: IotRhDailyReportTotalWorkloadVO = await IotRhDailyReportApi.totalWorkload(other)
+
+    totalWorkloadDetail.value = {
+      totalN2GasInjection: res2.totalN2GasInjection || 0,
+      totalNaturalGasInjection: res2.totalNaturalGasInjection || 0
+    }
 
     totalWork.value = {
       ...totalWork.value,
@@ -598,16 +613,17 @@ const { ZmTable, ZmTableColumn } = useTableComponents()
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
-      <DeptTreeSelect
-        :deptId="id"
-        :top-id="157"
-        v-model="query.deptId"
-        @node-click="handleDeptNodeClick"
-      />
-    </div>
+    <DeptTreeSelect
+      :deptId="id"
+      :top-id="157"
+      v-model="query.deptId"
+      @node-click="handleDeptNodeClick"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
+
+    </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
@@ -652,24 +668,45 @@ const { ZmTable, ZmTableColumn } = useTableComponents()
       </el-form-item>
     </el-form>
     <div class="grid grid-cols-8 gap-8">
-      <div
-        v-for="info in totalWorkKeys"
-        :key="info[0]"
-        class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-1 flex flex-col items-center justify-center gap-1"
-      >
-        <div class="size-7.5" :class="info[3]"></div>
-        <count-to
-          class="text-2xl font-medium"
-          :start-val="0"
-          :end-val="totalWork[info[0]]"
-          :decimals="info[4]"
-          @click="info[2] === '未填报' ? openUnfilledDialog() : ''"
-        >
-          <span class="text-xs leading-8 text-[var(--el-text-color-regular)]">暂无数据</span>
-        </count-to>
-        <div class="text-xs font-medium text-[var(--el-text-color-regular)]">{{ info[1] }}</div>
-        <div class="text-sm font-medium text-[var(--el-text-color-regular)]">{{ info[2] }}</div>
-      </div>
+      <template v-for="info in totalWorkKeys" :key="info[0]">
+        <el-tooltip :disabled="info[0] !== 'totalGasInjection'" placement="top">
+          <template #content>
+            <div>
+              累计氮气注气量:{{
+                formatGasInjectionTooltipValue(totalWorkloadDetail.totalN2GasInjection)
+              }}
+              万方
+            </div>
+            <div>
+              累计天然气注气量:{{
+                formatGasInjectionTooltipValue(totalWorkloadDetail.totalNaturalGasInjection)
+              }}
+              万方
+            </div>
+          </template>
+          <div
+            class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-1 flex flex-col items-center justify-center gap-1"
+          >
+            <div class="size-7.5" :class="info[3]"></div>
+
+            <count-to
+              class="text-2xl font-medium"
+              :class="{ 'cursor-help': info[0] === 'totalGasInjection' }"
+              :start-val="0"
+              :end-val="totalWork[info[0]]"
+              :decimals="info[4]"
+              @click="info[2] === '未填报' ? openUnfilledDialog() : ''"
+            >
+              <span class="text-xs leading-8 text-[var(--el-text-color-regular)]">暂无数据</span>
+            </count-to>
+
+            <div class="text-xs font-medium text-[var(--el-text-color-regular)]">{{ info[1] }}</div>
+            <div class="text-sm font-medium text-[var(--el-text-color-regular)]">
+              {{ info[2] }}
+            </div>
+          </div>
+        </el-tooltip>
+      </template>
     </div>
 
     <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow flex flex-col p-4 gap-2">

+ 5 - 4
src/views/pms/iotrydailyreport/approval.vue

@@ -108,11 +108,12 @@ onMounted(() => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
+
+    </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 5 - 4
src/views/pms/iotrydailyreport/fill.vue

@@ -108,11 +108,12 @@ onMounted(() => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
+
+    </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 3 - 4
src/views/pms/iotrydailyreport/index.vue

@@ -134,11 +134,10 @@ function handleOpenForm(id: number, type: 'edit' | 'readonly') {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[48px_1fr_auto] gap-x-4 gap-y-3 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[48px_1fr_auto] gap-x-4 gap-y-3 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
-      <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-6 gap-8 flex items-center justify-between"

+ 24 - 2
src/views/pms/iotrydailyreport/ry-xj-form.vue

@@ -95,6 +95,8 @@ interface FormOriginal {
 
   auditStatus: number
   location: string
+  daySupervisors: string
+  nightSupervisors: string
 }
 
 type Form = Partial<FormOriginal>
@@ -141,7 +143,9 @@ const FORM_KEYS: (keyof FormOriginal)[] = [
   'otherNptTime',
   'otherNptReason',
   'auditStatus',
-  'location'
+  'location',
+  'daySupervisors',
+  'nightSupervisors'
 ]
 
 const formRef = ref<FormInstance>()
@@ -315,6 +319,10 @@ const validateOtherReason = (_rule: any, value: any, callback: any) => {
 // 动态构建校验规则
 const rules = reactive<FormRules>({
   repairStatus: [{ required: true, message: '请选择施工状态', trigger: ['change', 'blur'] }],
+  daySupervisors: [{ required: true, message: '请输入白班跟班干部', trigger: ['change', 'blur'] }],
+  nightSupervisors: [
+    { required: true, message: '请输入夜班跟班干部', trigger: ['change', 'blur'] }
+  ],
   reportDetails: [{ required: true, message: '请填写生产动态', type: 'array' }],
   constructionBrief: [
     {
@@ -638,7 +646,14 @@ const orange = computed(() => {
             class="w-full!"
           />
         </el-form-item>
-        <el-form-item class="col-span-2" label="备注" prop="remark">
+        <el-form-item label="白班跟班干部" prop="daySupervisors">
+          <el-input
+            v-model="form.daySupervisors"
+            placeholder="请输入白班跟班干部"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
           <el-input
             v-model="form.remark"
             type="textarea"
@@ -647,6 +662,13 @@ const orange = computed(() => {
             :disabled="isMainFieldDisabled"
           />
         </el-form-item>
+        <el-form-item label="夜班跟班干部" prop="nightSupervisors">
+          <el-input
+            v-model="form.nightSupervisors"
+            placeholder="请输入夜班跟班干部"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
 
         <div class="col-span-2">
           <div class="flex items-center justify-between mb-6">

+ 7 - 0
src/views/pms/iotrydailyreport/ry-xj-table.vue

@@ -57,6 +57,8 @@ interface ListItem {
   offDutyStaffNum: number
   remark: string
   nonProductionRate: number
+  daySupervisors: string
+  nightSupervisors: string
 }
 const props = defineProps({
   list: {
@@ -430,6 +432,11 @@ function handleCurrentChange(val: number) {
             </zm-table-column>
             <zm-table-column prop="constructionBrief" label="当日施工简报" />
             <zm-table-column prop="contractName" label="项目" />
+            <zm-table-column label="跟班干部">
+              <zm-table-column prop="daySupervisors" label="白班" />
+              <zm-table-column prop="nightSupervisors" label="夜班" />
+            </zm-table-column>
+
             <zm-table-column prop="totalStaffNum" label="全员数量" />
             <zm-table-column
               prop="onDutyStaffNum"

+ 8 - 9
src/views/pms/iotrydailyreport/summary.vue

@@ -566,16 +566,15 @@ const tolist = (id: number, non: boolean = false) => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
-      <DeptTreeSelect
-        :deptId="id"
-        :top-id="158"
-        v-model="query.deptId"
-        @node-click="handleDeptNodeClick"
-      />
-    </div>
+    <DeptTreeSelect
+      :deptId="id"
+      :top-id="158"
+      v-model="query.deptId"
+      @node-click="handleDeptNodeClick"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 3 - 4
src/views/pms/iotrydailyreport/xapproval.vue

@@ -108,11 +108,10 @@ onMounted(() => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 3 - 4
src/views/pms/iotrydailyreport/xfill.vue

@@ -108,11 +108,10 @@ onMounted(() => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 4 - 4
src/views/pms/iotrydailyreport/xjindex.vue

@@ -133,11 +133,11 @@ function handleOpenForm(id: number, type: 'edit' | 'readonly') {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[48px_1fr_auto] gap-x-4 gap-y-3 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[48px_1fr_auto] gap-x-4 gap-y-3 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
-      <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
-    </div>
+    <DeptTreeSelect :top-id="158" :deptId="deptId" v-model="query.deptId" :show-title="false" />
+
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-6 gap-8 flex items-center justify-between"

+ 8 - 9
src/views/pms/iotrydailyreport/xsummary.vue

@@ -565,16 +565,15 @@ const tolist = (id: number, non: boolean = false) => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_164px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
-      <DeptTreeSelect
-        :deptId="id"
-        :top-id="158"
-        v-model="query.deptId"
-        @node-click="handleDeptNodeClick"
-      />
-    </div>
+    <DeptTreeSelect
+      :deptId="id"
+      :top-id="158"
+      v-model="query.deptId"
+      @node-click="handleDeptNodeClick"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3"> </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"

+ 9 - 10
src/views/report-statistics/costs.vue

@@ -227,17 +227,16 @@ const handleChange = () => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[196px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[196px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect
-        :top-id="156"
-        :deptId="156"
-        v-model="query.deptId"
-        :init-select="false"
-        :show-title="false"
-      />
-    </div>
+    <DeptTreeSelect
+      :top-id="156"
+      :deptId="156"
+      v-model="query.deptId"
+      :init-select="false"
+      :show-title="false"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <div class="grid grid-rows-[1fr_32px] gap-4">
       <div class="grid grid-cols-3 gap-4" v-loading="dataLoading">
         <!-- 使用 v-for 循环渲染 -->

+ 15 - 17
src/views/report-statistics/daily-report.vue

@@ -451,7 +451,7 @@ const handleExport = () => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
     <el-form
       size="default"
@@ -502,23 +502,21 @@ const handleExport = () => {
         </el-button>
       </el-form-item>
     </el-form>
-
+    <DeptTreeSelect
+      v-show="tab === '队伍'"
+      :top-id="157"
+      :deptId="deptId"
+      v-model="query.deptId"
+      title="队伍"
+    />
+    <WellSelect
+      v-show="tab === '井'"
+      v-model:contract-name="query.contractName"
+      :deptId="157"
+      v-model:model-value="query.wellName"
+    />
     <!-- 第二行左侧:自动落入第 2 行第 1 列 -->
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
-      <DeptTreeSelect
-        v-show="tab === '队伍'"
-        :top-id="157"
-        :deptId="deptId"
-        v-model="query.deptId"
-        title="队伍"
-      />
-      <WellSelect
-        v-show="tab === '井'"
-        v-model:contract-name="query.contractName"
-        :deptId="157"
-        v-model:model-value="query.wellName"
-      />
-    </div>
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full"> </div> -->
 
     <!-- 第二行右侧:自动落入第 2 行第 2 列 -->
     <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg p-4 flex flex-col">

+ 15 - 16
src/views/report-statistics/rd-daily-report.vue

@@ -388,7 +388,7 @@ const handleExport = () => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
     <el-form
       size="default"
@@ -441,21 +441,20 @@ const handleExport = () => {
     </el-form>
 
     <!-- 第二行左侧:自动落入第 2 行第 1 列 -->
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
-      <DeptTreeSelect
-        v-show="tab === '队伍'"
-        :top-id="163"
-        :deptId="deptId"
-        v-model="query.deptId"
-        title="队伍"
-      />
-      <WellSelect
-        v-show="tab === '井'"
-        :deptId="163"
-        v-model="query.wellName"
-        v-model:contract-name="query.contractName"
-      />
-    </div>
+    <DeptTreeSelect
+      v-show="tab === '队伍'"
+      :top-id="163"
+      :deptId="deptId"
+      v-model="query.deptId"
+      title="队伍"
+    />
+    <WellSelect
+      v-show="tab === '井'"
+      :deptId="163"
+      v-model="query.wellName"
+      v-model:contract-name="query.contractName"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full"> </div> -->
 
     <!-- 第二行右侧:自动落入第 2 行第 2 列 -->
     <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg p-4 flex flex-col">

+ 17 - 16
src/views/report-statistics/ry-daily-report.vue

@@ -554,7 +554,7 @@ const handleExport = () => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
     <el-form
       size="default"
@@ -605,23 +605,24 @@ const handleExport = () => {
         </el-button>
       </el-form-item>
     </el-form>
+    <DeptTreeSelect
+      v-show="tab === '队伍'"
+      :top-id="158"
+      :deptId="deptId"
+      v-model="query.deptId"
+      title="队伍"
+    />
+    <WellSelect
+      v-show="tab === '井'"
+      :deptId="158"
+      v-model="query.wellName"
+      v-model:contract-name="query.contractName"
+    />
 
     <!-- 第二行左侧:自动落入第 2 行第 1 列 -->
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
-      <DeptTreeSelect
-        v-show="tab === '队伍'"
-        :top-id="158"
-        :deptId="deptId"
-        v-model="query.deptId"
-        title="队伍"
-      />
-      <WellSelect
-        v-show="tab === '井'"
-        :deptId="158"
-        v-model="query.wellName"
-        v-model:contract-name="query.contractName"
-      />
-    </div>
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
+
+    </div> -->
 
     <!-- 第二行右侧:自动落入第 2 行第 2 列 -->
     <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg p-4 flex flex-col">

+ 17 - 16
src/views/report-statistics/ry-xj-daily-report.vue

@@ -495,7 +495,7 @@ const handleExport = () => {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
     <el-form
       size="default"
@@ -547,22 +547,23 @@ const handleExport = () => {
       </el-form-item>
     </el-form>
 
+    <DeptTreeSelect
+      v-show="tab === '队伍'"
+      :top-id="158"
+      :deptId="deptId"
+      v-model="query.deptId"
+      title="队伍"
+    />
+    <WellSelect
+      v-show="tab === '井'"
+      :deptId="158"
+      v-model="query.wellName"
+      v-model:contract-name="query.contractName"
+    />
     <!-- 第二行左侧:自动落入第 2 行第 1 列 -->
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
-      <DeptTreeSelect
-        v-show="tab === '队伍'"
-        :top-id="158"
-        :deptId="deptId"
-        v-model="query.deptId"
-        title="队伍"
-      />
-      <WellSelect
-        v-show="tab === '井'"
-        :deptId="158"
-        v-model="query.wellName"
-        v-model:contract-name="query.contractName"
-      />
-    </div>
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
+
+    </div> -->
 
     <!-- 第二行右侧:自动落入第 2 行第 2 列 -->
     <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg p-4 flex flex-col">

+ 9 - 10
src/views/report-statistics/work-order-completion.vue

@@ -278,17 +278,16 @@ async function handleExport() {
 
 <template>
   <div
-    class="grid grid-cols-[15%_1fr] grid-rows-[208px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[208px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
-    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2">
-      <DeptTreeSelect
-        :top-id="156"
-        :deptId="156"
-        v-model="query.deptId"
-        :init-select="false"
-        :show-title="false"
-      />
-    </div>
+    <DeptTreeSelect
+      :top-id="156"
+      :deptId="156"
+      v-model="query.deptId"
+      :init-select="false"
+      :show-title="false"
+    />
+    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-2"> </div> -->
     <div class="flex flex-col gap-4 h-full">
       <div
         class="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-5 gap-4 flex-1"

+ 1 - 0
types/global.d.ts

@@ -50,6 +50,7 @@ declare global {
     name: string
     sort?: number
     children?: Tree[] | any[]
+    disabled?: boolean
   }
   // 分页数据公共返回
   interface PageResult<T> {