yuan
5 天以前 9ea5ff67655241271bdf04bb50ca3a8d60c4e22a
src/views/index.vue
@@ -117,11 +117,10 @@
              <el-radio-button :value="'waiting'">待开始({{ orderProgressMeta.waitingCount }})</el-radio-button>
              <el-radio-button :value="'inProgress'">进行中({{ orderProgressMeta.inProgressCount }})</el-radio-button>
              <el-radio-button :value="'completed'">已完成({{ orderProgressMeta.completedCount }})</el-radio-button>
              <el-radio-button :value="'end'">已结束({{ orderProgressMeta.endCount }})</el-radio-button>
              <el-radio-button :value="'paused'">已暂停({{ orderProgressMeta.pausedCount }})</el-radio-button>
            </el-radio-group>
          </div>
          <div class="order-table-wrap">
            <el-table :data="filteredOrders" stripe max-height="440">
          <el-table :data="filteredOrders" stripe>
              <el-table-column prop="orderNo" label="订单编号" min-width="150" />
              <el-table-column prop="productName" label="产品名称" min-width="120" />
              <el-table-column prop="planQty" label="计划数量" min-width="90" />
@@ -148,7 +147,6 @@
                </template>
              </el-table-column>
            </el-table>
          </div>
        </div>
        <div v-if="visiblePanels.contract" class="cockpit-panel contract-panel">
@@ -216,7 +214,7 @@
        <div v-if="visiblePanels.todo" class="cockpit-panel todo-panel">
          <div class="panel-title-row">
            <div class="panel-title">待办事项</div>
            <span class="panel-more" @click="openTodoDialog">更多</span>
            <span class="panel-more" @click="goToApprovalProcess">更多</span>
          </div>
          <ul class="todo-list" v-if="todoList.length > 0">
            <li v-for="item in todoList" :key="item.id" class="todo-item">
@@ -234,6 +232,7 @@
        <div v-if="visiblePanels.realtime" class="cockpit-panel realtime-panel">
          <div class="panel-title-row">
            <div class="panel-title">生产实时看板</div>
            <span class="panel-more" @click="goToProductionOrder">更多</span>
          </div>
          <div class="realtime-grid">
            <div class="realtime-item" v-for="item in realtimeBoard" :key="item.key">
@@ -279,7 +278,7 @@
        <div v-if="visiblePanels.plan" class="cockpit-panel plan-panel">
          <div class="panel-title-row">
            <div class="panel-title">今日生产计划</div>
            <span class="panel-more">{{ todayPlanTotal }}项</span>
            <span class="panel-more" @click="goToProductionPlan">{{ todayPlanTotal }}项</span>
          </div>
          <ul class="plan-list">
            <li v-for="item in todayPlanList" :key="item.orderNo" class="plan-item">
@@ -333,18 +332,6 @@
          <el-button type="primary" @click="handleProcessDialogConfirm">确认</el-button>
        </span>
      </template>
    </el-dialog>
    <el-dialog v-model="todoDialogVisible" title="全部待办事项" width="900px" append-to-body destroy-on-close>
      <div v-loading="todoDialogLoading" class="todo-dialog-body">
        <el-table :data="todoDialogList" stripe max-height="520">
          <el-table-column prop="approveId" label="待办编号" min-width="160" />
          <el-table-column prop="approveDeptName" label="部门/模板" min-width="160" />
          <el-table-column prop="approveReason" label="事由" min-width="240" show-overflow-tooltip />
          <el-table-column prop="approveUserName" label="发起人" min-width="120" />
          <el-table-column prop="approveTime" label="时间" min-width="170" />
        </el-table>
      </div>
    </el-dialog>
  </div>
</template>
@@ -462,7 +449,6 @@
});
const realtimeBoardData = ref({
  deviceOee: { value: 0, compareYesterday: 0 },
  orderAchievementRate: { value: 0, compareYesterday: 0 },
  defectRate: { value: 0, compareYesterday: 0 },
});
@@ -477,14 +463,11 @@
  waitingCount: 0,
  inProgressCount: 0,
  completedCount: 0,
  endCount: 0,
  pausedCount: 0,
});
const todayPlanList = ref([]);
const todayPlanTotal = ref(0);
const todoDialogVisible = ref(false);
const todoDialogLoading = ref(false);
const todoDialogList = ref([]);
const sum = ref(0);
const yny = ref(0);
@@ -852,12 +835,12 @@
const productionOrders = ref([]);
const orderFilterOptions = ["all", "waiting", "inProgress", "completed", "end"];
const orderFilterOptions = ["all", "waiting", "inProgress", "completed", "paused"];
const orderFilterAliasMap = {
  1: "waiting",
  2: "inProgress",
  3: "completed",
  5: "end",
  4: "paused",
};
const orderFilter = ref("all");
const filteredOrders = computed(() => productionOrders.value);
@@ -899,22 +882,11 @@
};
const realtimeBoard = computed(() => {
  const oee = Number(realtimeBoardData.value.deviceOee?.value || 0);
  const orderAchievement = Number(realtimeBoardData.value.orderAchievementRate?.value || 0);
  const defectRate = Number(realtimeBoardData.value.defectRate?.value || 0);
  const oeeCompare = Number(realtimeBoardData.value.deviceOee?.compareYesterday || 0);
  const orderCompare = Number(realtimeBoardData.value.orderAchievementRate?.compareYesterday || 0);
  const defectCompare = Number(realtimeBoardData.value.defectRate?.compareYesterday || 0);
  return [
    {
      key: "oee",
      label: "设备 OEE",
      percent: clampPercent(oee),
      display: `${oee.toFixed(2)}%`,
      delta: getCompareText(oeeCompare),
      trend: getCompareTrend(oeeCompare),
      color: "#2d8cff",
    },
    {
      key: "order",
      label: "订单达成率",
@@ -922,7 +894,7 @@
      display: `${orderAchievement.toFixed(2)}%`,
      delta: getCompareText(orderCompare),
      trend: getCompareTrend(orderCompare),
      color: "#31d2ff",
      color: "#2d8cff",
    },
    {
      key: "defect",
@@ -1031,8 +1003,8 @@
    pathPrefixes: ["/inventorymanagement"],
  },
  production: {
    titles: ["生产管控"],
    pathPrefixes: ["/productionManagement", "/productionPlan"],
    titles: ["生产管理", "主生产计划", "生产订单", "生产报工"],
    pathPrefixes: ["/productionmanagement", "/productionplan"],
  },
  quality: {
    titles: ["质量管理"],
@@ -1086,7 +1058,7 @@
  realtime: visibleModules.value.production,
  quick: quickEntries.value.length > 0,
  plan: visibleModules.value.production,
  receipt: visibleModules.value.finance,
  receipt: visibleModules.value.sales || visibleModules.value.finance,
}));
const hasLeftPanels = computed(
@@ -1157,7 +1129,7 @@
    1: "待开始",
    2: "进行中",
    3: "已完成",
    5: "已结束",
    4: "已暂停",
  };
  return mapping[status] || "未知";
};
@@ -1221,10 +1193,6 @@
    const res = await productionRealtimeBoard();
    const data = res?.data || {};
    realtimeBoardData.value = {
      deviceOee: {
        value: Number(data.deviceOee?.value || 0),
        compareYesterday: Number(data.deviceOee?.compareYesterday || 0),
      },
      orderAchievementRate: {
        value: Number(data.orderAchievementRate?.value || 0),
        compareYesterday: Number(data.orderAchievementRate?.compareYesterday || 0),
@@ -1236,7 +1204,6 @@
    };
  } catch {
    realtimeBoardData.value = {
      deviceOee: { value: 0, compareYesterday: 0 },
      orderAchievementRate: { value: 0, compareYesterday: 0 },
      defectRate: { value: 0, compareYesterday: 0 },
    };
@@ -1248,8 +1215,8 @@
    const res = await productionOrderProgress({
      status: orderFilter.value,
      tab: orderFilter.value,
      pageNum: -1,
      pageSize: -1,
      pageNum: 1,
      pageSize: 10,
    });
    const data = res?.data || {};
    const statusValue = normalizeOrderFilter(data.status, orderFilter.value);
@@ -1264,7 +1231,7 @@
      waitingCount: resolveProgressCount(data.waitingCount, statusValue, "waiting", total),
      inProgressCount: resolveProgressCount(data.inProgressCount, statusValue, "inProgress", total),
      completedCount: resolveProgressCount(data.completedCount, statusValue, "completed", total),
      endCount: resolveProgressCount(data.endCount, statusValue, "end", total),
      pausedCount: resolveProgressCount(data.pausedCount, statusValue, "paused", total),
    };
    productionOrders.value = (data.records || []).map(mapOrderProgressRecord);
  } catch {
@@ -1278,7 +1245,7 @@
      waitingCount: 0,
      inProgressCount: 0,
      completedCount: 0,
      endCount: 0,
      pausedCount: 0,
    };
    productionOrders.value = [];
  }
@@ -1287,7 +1254,7 @@
const refreshTodayProductionPlan = async () => {
  try {
    const res = await todayProductionPlan({
      limit: 1000,
      limit: 4,
      planDate: nowDate.value,
    });
    const data = res?.data || {};
@@ -1319,19 +1286,6 @@
const todoInfoS = async () => {
  const res = await homeTodos();
  todoList.value = res.data || [];
};
const openTodoDialog = async () => {
  todoDialogVisible.value = true;
  todoDialogLoading.value = true;
  try {
    const res = await homeTodos();
    todoDialogList.value = res.data || [];
  } catch {
    todoDialogList.value = [];
  } finally {
    todoDialogLoading.value = false;
  }
};
const statisticsReceivable = async () => {
@@ -1425,6 +1379,18 @@
const goToQuick = (path) => {
  if (!path) return;
  router.push(path).catch(() => {});
};
const goToProductionOrder = () => {
  router.push('/productionManagement/productionOrder').catch(() => {});
};
const goToApprovalProcess = () => {
  router.push('/collaborativeApproval/approvalProcess').catch(() => {});
};
const goToProductionPlan = () => {
  router.push('/productionManagement/productionPlan').catch(() => {});
};
watch(orderFilter, () => {
@@ -1584,12 +1550,9 @@
}
.stats-grid {
  display: flex;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}
.stats-grid .stat-card {
  flex: 1;
}
.stat-card {
@@ -1771,34 +1734,6 @@
  font-size: 14px;
  color: #2563eb;
  cursor: pointer;
}
.todo-dialog-body {
  min-height: 220px;
}
.plan-panel {
  overflow: hidden;
}
.plan-list {
  margin: 10px 0 0;
  padding: 0 4px 0 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 300px;
  overflow-y: auto;
}
.plan-list::-webkit-scrollbar {
  width: 6px;
}
.plan-list::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: rgba(37, 99, 235, 0.28);
}
.todo-list {
@@ -2154,7 +2089,7 @@
.realtime-grid {
  margin-top: 10px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
@@ -2162,12 +2097,12 @@
  border: 1px solid rgba(148, 163, 184, 0.2);
  border-radius: 12px;
  background: #f8fbff;
  padding: 10px 8px;
  padding: 16px 12px;
  text-align: center;
}
.realtime-value {
  font-size: 26px;
  font-size: 32px;
  color: #0f172a;
  font-weight: 700;
}
@@ -2254,13 +2189,6 @@
.order-panel {
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.order-table-wrap {
  flex: 1;
  min-height: 0;
}
.quick-panel {
@@ -2300,12 +2228,25 @@
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.plan-panel {
  min-height: 236px;
}
.quality-panel {
  min-height: 0;
}
.receipt-panel {
  min-height: 340px;
}
.plan-list {
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.plan-item {
@@ -2460,6 +2401,10 @@
  .main-grid {
    grid-template-columns: minmax(0, 1fr) 340px;
  }
  .stats-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 1200px) {
@@ -2482,7 +2427,7 @@
  }
  .realtime-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .quality-cards {
@@ -2505,6 +2450,10 @@
    justify-content: flex-start;
  }
  .stats-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .right-column {
    grid-template-columns: 1fr;
  }