<template>
  <vue-fullscreen
    class="fp-container"
    v-cloak
    v-model="isFullscreen"
    fullscreenClass="fp-container"
    :exit-on-click-wrapper="false"
  >
    <div class="fp-container" v-cloak>
      <div class="fp-header">
        <div class="fp-header-left">
          <el-select
            v-model="factoryId"
            size="mini"
            class="fp-select"
            placeholder="工厂名称"
          >
            <el-option
              v-for="f in factoryList"
              :key="f.id"
              :label="f.name"
              :value="f.id"
            />
          </el-select>
          <el-date-picker
            v-model="dateRange"
            class="fp-date"
            size="mini"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            value-format="yyyy-MM-dd"
          />
        </div>

        <div class="fp-title">工厂生产综合看板</div>

        <div class="fp-header-right">
          <div class="fp-time">
            <span class="fp-date-text">{{ date }}</span>
            <span class="fp-time-text">{{ time }}</span>
            <span class="fp-week-text">{{ week }}</span>
          </div>
          <span class="fp-fullscreen-toggle" @click.passive="onFullscreen">
            <i
              v-if="isFullscreen"
              title="取消全屏"
              class="el-icon-_screen-restore"
            ></i>
            <i v-else title="全屏" class="el-icon-_screen-full"></i>
          </span>
        </div>
      </div>

      <div class="fp-body">
        <!-- Left -->
        <div class="fp-col fp-col-left">
          <chart-card title="生产统计" size="small">
            <div class="fp-number-card">
              <div class="fp-number-display">
                <div class="fp-unit-box">
                  <span class="fp-unit">十万</span>
                  <div class="fp-digit-box">
                    <span class="fp-digit">{{
                      productionStats.transformQuantity[0]
                    }}</span>
                  </div>
                </div>
                <div class="fp-unit-box">
                  <span class="fp-unit">万</span>
                  <div class="fp-digit-box">
                    <span class="fp-digit">{{
                      productionStats.transformQuantity[1]
                    }}</span>
                  </div>
                </div>
                <div class="fp-unit-box">
                  <span class="fp-unit">千</span>
                  <div class="fp-digit-box">
                    <span class="fp-digit">{{
                      productionStats.transformQuantity[2]
                    }}</span>
                  </div>
                </div>
                <div class="fp-separator">,</div>
                <div class="fp-unit-box">
                  <span class="fp-unit"></span>
                  <div class="fp-digit-box">
                    <span class="fp-digit">{{
                      productionStats.transformQuantity[3]
                    }}</span>
                  </div>
                </div>
                <div class="fp-unit-box">
                  <span class="fp-unit"></span>
                  <div class="fp-digit-box">
                    <span class="fp-digit">{{
                      productionStats.transformQuantity[4]
                    }}</span>
                  </div>
                </div>
                <div class="fp-unit-box">
                  <span class="fp-unit"></span>
                  <div class="fp-digit-box">
                    <span class="fp-digit">{{
                      productionStats.transformQuantity[5]
                    }}</span>
                  </div>
                </div>
              </div>
              <div class="fp-mini-stats">
                <div class="fp-mini-stat">
                  <div class="fp-mini-icon">
                    <img src="@/assets/png/Group.png" alt="" />
                  </div>
                  <div class="fp-mini-info">
                    <div class="fp-mini-label">要求生产总数量：</div>
                    <div class="fp-mini-value">{{
                      productionStats.requiredTotal
                    }}</div>
                  </div>
                </div>
                <div class="fp-mini-stat">
                  <div class="fp-mini-icon">
                    <img src="@/assets/png/Group.png" alt="" />
                  </div>
                  <div class="fp-mini-info">
                    <div class="fp-mini-label">生产达成率：</div>
                    <div class="fp-mini-value"
                      >{{ productionStats.achievementRate }}%</div
                    >
                  </div>
                </div>
              </div>
            </div>
          </chart-card>

          <chart-card title="五品" size="small">
            <div class="fp-chart-box">
              <device-status-chart
                v-if="isFlag"
                :data="fiveCategoryData"
                class="fp-chart-fill"
              />
            </div>
          </chart-card>

          <chart-card title="四数" size="small">
            <div class="fp-chart-box">
              <Bar3DChart
                v-if="isFlag"
                :data="fourNumberBarData"
                :colors="['#1163fb', '#1163fb', '#1163fb', '#1163fb']"
                :show-label="true"
                class="fp-chart-fill"
              />
            </div>
          </chart-card>
        </div>

        <!-- Middle -->
        <div class="fp-col fp-col-middle">
          <div class="fp-middle-top">
            <div class="fp-middle-title">
              <img class="fp-title-icon" src="@/assets/png/icon.png" alt="" />
              <span>生产情况</span>
            </div>
            <div class="fp-kpis">
              <div class="fp-kpi">
                <div class="fp-kpi-screen">
                  <div class="fp-kpi-value fp-kpi-glow">{{
                    productionKpi.delayedCount
                  }}</div>
                </div>
                <div class="fp-kpi-base"></div>
                <div class="fp-kpi-label">已延期生产计划数</div>
              </div>
              <div class="fp-kpi">
                <div class="fp-kpi-screen">
                  <div class="fp-kpi-value fp-kpi-glow"
                    >{{ productionKpi.planAchievementRate }}%</div
                  >
                </div>
                <div class="fp-kpi-base"></div>
                <div class="fp-kpi-label">计划达成率</div>
              </div>
              <div class="fp-kpi">
                <div class="fp-kpi-screen">
                  <div class="fp-kpi-value fp-kpi-glow">{{
                    productionKpi.unpublishedCount
                  }}</div>
                </div>
                <div class="fp-kpi-base"></div>
                <div class="fp-kpi-label">未发布计划数</div>
              </div>
            </div>
            <div class="fp-middle-note"
              >注：生产计划完成率=已完成生产计划数/所有生产计划数</div
            >
          </div>

          <chart-card title="生产计划进度" size="large">
            <div class="fp-table-wrap">
              <div class="fp-table-tabs">
                <div
                  v-for="t in planTabs"
                  :key="t.key"
                  class="fp-tab"
                  :class="{ active: activePlanTab === t.key }"
                  @click="activePlanTab = t.key"
                >
                  {{ t.label }}
                </div>
              </div>

              <div class="fp-table">
                <table class="fp-main-table">
                  <colgroup>
                    <col style="width: 11%" />
                    <col style="width: 15%" />
                    <col style="width: 10%" />
                    <col style="width: 9%" />
                    <col style="width: 11%" />
                    <col style="width: 15%" />
                    <col style="width: 15%" />
                    <col style="width: 14%" />
                  </colgroup>
                  <thead>
                    <tr>
                      <th>计划编号</th>
                      <th>产品名称</th>
                      <th>批次号</th>
                      <th>计划数量</th>
                      <th>工艺路线</th>
                      <th>计划开始时间</th>
                      <th>计划结束时间</th>
                      <th>计划状态</th>
                    </tr>
                  </thead>
                </table>

                <div class="fp-scroll-body">
                  <table class="fp-main-table">
                    <colgroup>
                      <col style="width: 11%" />
                      <col style="width: 15%" />
                      <col style="width: 10%" />
                      <col style="width: 9%" />
                      <col style="width: 11%" />
                      <col style="width: 15%" />
                      <col style="width: 15%" />
                      <col style="width: 14%" />
                    </colgroup>
                    <tbody>
                      <tr
                        v-for="row in filteredPlanRows"
                        :key="row.planNo"
                        class="fp-row"
                        :style="{
                          backgroundColor: row.isDelayed ? '#ff4d4f' : '#031d42'
                        }"
                      >
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">{{
                          row.planNo
                        }}</td>
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">{{
                          row.productName
                        }}</td>
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">{{
                          row.batchNo
                        }}</td>
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">{{
                          row.planQty
                        }}</td>
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">{{
                          row.currentProcess
                        }}</td>
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">{{
                          row.planStart
                        }}</td>
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">{{
                          row.planEnd
                        }}</td>
                        <td class="fp-cell" @mouseenter="syncOverflowTitle">
                          <span
                            :class="
                              row.isDelayed ? 'fp-status-red' : 'fp-status-blue'
                            "
                          >
                            {{ row.statusText }}
                          </span>
                        </td>
                      </tr>
                      <tr v-if="filteredPlanRows.length === 0">
                        <td class="fp-empty" colspan="8">暂无生产计划数据</td>
                      </tr>
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
          </chart-card>
        </div>

        <!-- Right -->
        <div class="fp-col fp-col-right">
          <div class="fp-inventory-card chart-card">
            <div class="card-header fp-inventory-header">
              <div class="card-logo">
                <img :src="inventoryIcon" alt="" />
              </div>
              <div class="card-title">库存台账</div>
              <div class="fp-inventory-filters">
                <span
                  class="fp-filter-btn"
                  :class="{ active: activeInventoryFilter === 'category' }"
                  @click="setInventoryFilter('category')"
                >
                  分类维度
                </span>
                <span
                  class="fp-filter-btn"
                  :class="{ active: activeInventoryFilter === 'keeper' }"
                  @click="setInventoryFilter('keeper')"
                >
                  仓管
                </span>
                <i class="el-icon-setting fp-filter-icon"></i>
              </div>
            </div>
            <div class="fp-chart-box card-content">
              <Bar3DChart
                v-if="isFlag"
                :data="inventoryLedgerDisplayData"
                :colors="inventoryBarColors"
                :show-label="true"
                :bar-size="[10, 6]"
                class="fp-chart-fill"
              />
            </div>
          </div>

          <chart-card title="各类型设备台账" size="small">
            <div class="fp-chart-box">
              <bar-line-combo-chart
                v-if="isFlag"
                :categories="deviceLedger.categories"
                :bar-data="deviceLedger.barData"
                :top-bar-data="deviceLedger.topBarData"
                :line-data="deviceLedger.lineData"
                bar-name="占用数"
                top-bar-name="总数量"
                line-name="占比"
                :show-bar-label="false"
                :show-line-label="false"
                :show-legend="false"
                :bar-colors="['#1163fb', '#1163fb', '#1163fb', '#1163fb']"
                top-bar-color="#f9bb19"
                class="fp-chart-fill"
              />
            </div>
          </chart-card>

          <chart-card title="质量统计" size="small">
            <div class="fp-quality">
              <div class="fp-quality-top">
                <div class="fp-quality-item">
                  <div class="fp-quality-label">合格率</div>
                  <div class="fp-quality-value good"
                    >{{ quality.passRate }}%</div
                  >
                </div>
                <div class="fp-quality-item">
                  <div class="fp-quality-label">不合格率</div>
                  <div class="fp-quality-value bad"
                    >{{ quality.failRate }}%</div
                  >
                </div>
                <div class="fp-quality-item">
                  <div class="fp-quality-label">损耗率</div>
                  <div class="fp-quality-value warn"
                    >{{ quality.lossRate }}%</div
                  >
                </div>
              </div>

              <div class="fp-quality-bars">
                <div class="fp-qbar">
                  <div class="fp-qbar-name">合格率</div>
                  <div class="fp-qbar-track">
                    <div
                      class="fp-qbar-fill good"
                      :style="{ width: quality.passRate + '%' }"
                    ></div>
                  </div>
                </div>
                <div class="fp-qbar">
                  <div class="fp-qbar-name">不合格率</div>
                  <div class="fp-qbar-track">
                    <div
                      class="fp-qbar-fill bad"
                      :style="{ width: quality.failRate + '%' }"
                    ></div>
                  </div>
                </div>
                <div class="fp-qbar">
                  <div class="fp-qbar-name">损耗率</div>
                  <div class="fp-qbar-track">
                    <div
                      class="fp-qbar-fill warn"
                      :style="{ width: quality.lossRate + '%' }"
                    ></div>
                  </div>
                </div>
              </div>
            </div>
          </chart-card>
        </div>
      </div>
    </div>
  </vue-fullscreen>
</template>

<script>
  import { component as VueFullscreen } from 'vue-fullscreen';
  import ChartCard from './components/ChartCard';
  import BarChart from './components/charts/BarChart';
  import Bar3DChart from './components/charts/Bar3DChart';
  import DeviceStatusChart from './components/charts/DeviceStatusChart';
  import BarLineComboChart from './components/charts/BarLineComboChart';
  import {
    getFactoryProductionDashboardData,
    getFactoryProductionDataByPlan,
    getFactoryProductionDataByStock,
    getFactoryProductionDataByDevice,
    getFactoryListByUser
  } from '@/api/vis/factoryProductionDashboard';
  const inventoryIcon = require('@/assets/png/icon.png');

  export default {
    name: 'FactoryProductionDashboard',
    components: {
      VueFullscreen,
      ChartCard,
      BarChart,
      Bar3DChart,
      DeviceStatusChart,
      BarLineComboChart
    },
    computed: {
      contentWidth() {
        return this.$store.state.theme.contentWidth || '100%';
      },
      inventoryLedgerDisplayData() {
        return this.activeInventoryFilter === 'keeper'
          ? this.inventoryLedgerKeeperData
          : this.inventoryLedgerData;
      },
      inventoryBarColors() {
        return (this.inventoryLedgerDisplayData || []).map(() => '#1163fb');
      },
      filteredPlanRows() {
        if (this.activePlanTab === 'all') return this.planRows;
        return this.planRows.filter((r) => r.type === this.activePlanTab);
      }
    },
    watch: {
      isFullscreen: {
        handler(val) {
          this.isFlag = false;
          this.$nextTick(() => {
            const delay = this._initLayout ? 160 : 0;
            this._initLayout = true;
            const run = () => {
              this.applyScreenSize();
              this.$nextTick(() => {
                this.isFlag = true;
                this.triggerChartResize();
              });
            };
            delay ? setTimeout(run, delay) : run();
          });
        },
        immediate: true
      },
      contentWidth: {
        handler() {
          clearTimeout(this.resizeTimer);
          this.isFlag = false;
          this.resizeTimer = setTimeout(() => {
            this.$nextTick(() => {
              this.applyScreenSize();
              this.isFlag = true;
            });
          }, 300);
        },
        immediate: true
      },
      factoryId() {
        this.loadDashboardData();
      },
      dateRange: {
        handler() {
          this.loadDashboardData();
        },
        deep: true
      },
      activePlanTab() {
        this.fetchPlanData();
      },
      activeInventoryFilter() {
        this.fetchInventoryData();
      }
    },
    data() {
      return {
        isFullscreen: false,
        isFlag: false,
        _initLayout: false,
        resizeTimer: null,
        updateTimer: null,
        date: '',
        time: '',
        week: '',
        inventoryIcon,

        factoryId: '',
        factoryList: [],
        dateRange: [],

        productionStats: {
          quantity: 0,
          transformQuantity: [0, 0, 0, 0, 0, 0],
          requiredTotal: 0,
          achievementRate: 0
        },
        productionKpi: {
          delayedCount: 0,
          planAchievementRate: 0,
          unpublishedCount: 0
        },

        fiveCategoryData: [
          { name: '成品', value: 0 },
          { name: '半成品', value: 0 },
          { name: '在制品', value: 0 },
          { name: '废品', value: 0 },
          { name: '返修品', value: 0 }
        ],
        fourNumberBarData: [
          { name: '投入数', value: 0 },
          { name: '产出数', value: 0 },
          { name: '废品数', value: 0 },
          { name: '周转数', value: 0 }
        ],

        inventoryLedgerData: [],
        inventoryLedgerKeeperData: [],
        activeInventoryFilter: 'category',
        deviceLedger: {
          categories: [],
          barData: [],
          topBarData: [],
          lineData: []
        },

        quality: {
          passRate: 0,
          failRate: 0,
          lossRate: 0
        },

        planTabs: [
          { key: 'all', label: '全部' },
          { key: 'monthly', label: '月度滚动计划' },
          { key: 'temporary', label: '临时生产计划' },
          { key: 'research', label: '分厂临时计划' }
        ],
        activePlanTab: 'all',
        planRows: []
      };
    },
    created() {
      this.updateTime();
      this.updateTimer = setInterval(this.updateTime, 1000);
      const now = new Date();
      const today = this.formatDate(now);
      this.dateRange = [today, today];
      this.getFactoryListByData();
    },
    mounted() {
      this.applyScreenSize();
      window.addEventListener('resize', this.handleWindowResize);
      document.addEventListener(
        'fullscreenchange',
        this.handleFullscreenChange
      );
      document.addEventListener(
        'webkitfullscreenchange',
        this.handleFullscreenChange
      );
      this.$nextTick(() => {
        this.isFlag = true;
      });
    },
    beforeDestroy() {
      clearInterval(this.updateTimer);
      clearTimeout(this.resizeTimer);
      window.removeEventListener('resize', this.handleWindowResize);
      document.removeEventListener(
        'fullscreenchange',
        this.handleFullscreenChange
      );
      document.removeEventListener(
        'webkitfullscreenchange',
        this.handleFullscreenChange
      );
    },
    methods: {
      handleWindowResize() {
        clearTimeout(this.resizeTimer);
        this.resizeTimer = setTimeout(() => {
          this.applyScreenSize();
        }, 120);
      },
      syncOverflowTitle(event) {
        const el = event?.currentTarget;
        if (!el) return;
        const text = (el.innerText || '').trim();
        const isOverflowing = el.scrollWidth > el.clientWidth;
        el.title = isOverflowing ? text : '';
      },
      handleFullscreenChange() {
        this.isFlag = false;
        this.$nextTick(() => {
          setTimeout(() => {
            this.applyScreenSize();
            this.$nextTick(() => {
              this.isFlag = true;
              this.triggerChartResize();
            });
          }, 150);
        });
      },

      async getFactoryListByData() {
        const par = {
          type: 1,
          size: 9999
        };
        await getFactoryListByUser(par).then((res) => {
          if (res.list && res.list.length > 0) {
            this.factoryList = res.list.map((el) => {
              return {
                id: el.id,
                name: el.name
              };
            });
          }
        });
        this.factoryId = this.factoryList.length ? this.factoryList[0].id : '';
      },

      triggerChartResize() {
        this.$nextTick(() => {
          window.dispatchEvent(new Event('resize'));
        });
      },
      applyScreenSize() {
        const isFs = !!(
          document.fullscreenElement || document.webkitFullscreenElement
        );
        let deviceWidth;
        let deviceHeight;
        if (isFs) {
          deviceWidth =
            window.innerWidth || document.documentElement.clientWidth;
          deviceHeight =
            window.innerHeight || document.documentElement.clientHeight;
        } else {
          deviceWidth = document.documentElement.clientWidth;
          deviceHeight = document.documentElement.clientHeight;
        }

        const eleAdminHeaderHeight =
          (!isFs &&
            document.getElementsByClassName('ele-admin-header')[0]
              ?.offsetHeight) ||
          0;
        const eleAdminSidebarWidth =
          (!isFs &&
            document.getElementsByClassName('ele-admin-sidebar')[0]
              ?.offsetWidth) ||
          0;
        const eleAdminTabsHeight =
          (!isFs &&
            document.getElementsByClassName('ele-admin-tabs')[0]
              ?.offsetHeight) ||
          0;

        const h = isFs
          ? deviceHeight
          : deviceHeight - eleAdminHeaderHeight - eleAdminTabsHeight;
        const w = isFs ? deviceWidth : deviceWidth - eleAdminSidebarWidth;

        const setContainerSize = (item) => {
          item.style.height = h + 'px';
          item.style.width = w + 'px';
        };
        const containers = [...document.getElementsByClassName('fp-container')];
        containers.forEach(setContainerSize);
        document.documentElement.style.fontSize = isFs ? '24px' : '16px';
      },
      onFullscreen() {
        this.isFullscreen = !this.isFullscreen;
      },
      setInventoryFilter(type) {
        if (this.activeInventoryFilter === type) return;
        this.activeInventoryFilter = type;
      },
      updateTime() {
        const now = new Date();
        const pad = (n) => n.toString().padStart(2, '0');
        this.time = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(
          now.getSeconds()
        )}`;
        this.date = `${now.getFullYear()}年${
          now.getMonth() + 1
        }月${now.getDate()}日`;
        const weekMap = {
          0: '日',
          1: '一',
          2: '二',
          3: '三',
          4: '四',
          5: '五',
          6: '六'
        };
        this.week = `星期${weekMap[now.getDay()]}`;
      },
      formatDate(d) {
        const pad = (n) => n.toString().padStart(2, '0');
        return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(
          d.getDate()
        )}`;
      },
      formatNumber6(num) {
        const paddedStr = String(num ?? 0).padStart(6, '0');
        return paddedStr.split('').map((c) => Number(c));
      },
      normalizeNumber(value) {
        const num = Number(value);
        return Number.isFinite(num) ? num : 0;
      },
      normalizePercent(value) {
        if (value === null || value === undefined || value === '') return 0;
        const text = String(value).replace('%', '').trim();
        const num = Number(text);
        return Number.isFinite(num) ? Number(num.toFixed(2)) : 0;
      },
      normalizeNameValueList(list) {
        if (!Array.isArray(list)) return [];
        return list.map((item) => ({
          name: item?.name || '--',
          value: this.normalizeNumber(item?.value)
        }));
      },
      getBaseParams(extra = {}) {
        if (!this.factoryId) return null;
        const [rangeStart, rangeEnd] = this.dateRange || [];
        const today = this.formatDate(new Date());
        const startTime = rangeStart || today;
        const endTime = rangeEnd || startTime;
        return {
          factoryId: this.factoryId,
          startTime,
          endTime,
          ...extra
        };
      },
      getInventoryStockType(filterType = this.activeInventoryFilter) {
        return filterType === 'keeper' ? 2 : 1;
      },
      mapPlanTabType(tab = this.activePlanTab) {
        const tabTypeMap = {
          all: 0,
          monthly: 1,
          temporary: 2,
          research: 3
        };
        return tabTypeMap[tab] ?? 0;
      },
      mapPlanStatus(status) {
        const statusMap = {
          1: '待排产',
          2: '待发布',
          3: '发布失败',
          4: '待生产',
          5: '进行中',
          6: '已完成',
          7: '延期',
          8: '待下达'
        };
        return statusMap[Number(status)] || '--';
      },
      formatPlanDate(value) {
        if (!value) return '--';
        const text = String(value).replace('T', ' ');
        return text.slice(0, 10);
      },
      mapPlanRow(row, planTab = this.activePlanTab) {
        const status = Number(row?.status || 0);
        return {
          planNo: row?.code || '--',
          productName: row?.productName || '--',
          batchNo: row?.batchNo || '--',
          planQty: this.normalizeNumber(row?.planQuantity),
          currentProcess: row?.produceRoutingName || '--',
          planStart: this.formatPlanDate(row?.plannedStartTime),
          planEnd: this.formatPlanDate(row?.plannedEndTime),
          isDelayed: status === 7,
          statusText: this.mapPlanStatus(status),
          type: planTab
        };
      },
      buildDeviceLedger(list) {
        const normalized = Array.isArray(list)
          ? list.map((item) => {
              const total = this.normalizeNumber(item?.value);
              const occupied = this.normalizeNumber(
                item?.value2 !== undefined ? item?.value2 : item?.value
              );
              return {
                name: item?.name || '--',
                total,
                occupied
              };
            })
          : [];
        return {
          categories: normalized.map((item) => item.name),
          barData: normalized.map((item) => item.occupied),
          topBarData: normalized.map((item) => item.total),
          lineData: normalized.map((item) =>
            item.total
              ? Number(((item.occupied / item.total) * 100).toFixed(2))
              : 0
          )
        };
      },
      applyDashboardSummary(data = {}) {
        const quantity = this.normalizeNumber(data?.quantity);
        this.productionStats.quantity = quantity;
        this.productionStats.transformQuantity = this.formatNumber6(quantity);
        this.productionStats.requiredTotal = this.normalizeNumber(
          data?.totalQuantity
        );
        this.productionStats.achievementRate = this.normalizePercent(
          data?.quantityAchievementRate
        );

        this.productionKpi.delayedCount = this.normalizeNumber(data?.delayNumber);
        this.productionKpi.planAchievementRate = this.normalizePercent(
          data?.achievementRate
        );
        this.productionKpi.unpublishedCount = this.normalizeNumber(
          data?.numberToBeProduced
        );

        this.fiveCategoryData = [
          {
            name: '成品',
            value: this.normalizeNumber(data?.finishedProductQuantity)
          },
          {
            name: '半成品',
            value: this.normalizeNumber(data?.semiFinishedProductQuantity)
          },
          {
            name: '在制品',
            value: this.normalizeNumber(data?.workInProgressQuantity)
          },
          {
            name: '废品',
            value: this.normalizeNumber(data?.scrapProductQuantity)
          },
          {
            name: '返修品',
            value: this.normalizeNumber(data?.reworkProductQuantity)
          }
        ];

        this.fourNumberBarData = [
          { name: '投入数', value: this.normalizeNumber(data?.inputQuantity) },
          { name: '产出数', value: this.normalizeNumber(data?.outputQuantity) },
          { name: '废品数', value: this.normalizeNumber(data?.scrapQuantity) },
          { name: '周转数', value: this.normalizeNumber(data?.turnoverQuantity) }
        ];

        this.quality.passRate = this.normalizePercent(data?.passRate);
        this.quality.failRate = this.normalizePercent(data?.defectiveRate);
        this.quality.lossRate = this.normalizePercent(data?.attritionRate);
      },
      async loadDashboardData() {
        const baseParams = this.getBaseParams();
        if (!baseParams) return;
        await Promise.all([
          this.fetchSummaryData(baseParams),
          this.fetchInventoryData(baseParams),
          this.fetchDeviceData(baseParams),
          this.fetchPlanData(baseParams)
        ]);
      },
      async fetchSummaryData(baseParams = this.getBaseParams()) {
        if (!baseParams) return;
        try {
          const data = await getFactoryProductionDashboardData(baseParams);
          this.applyDashboardSummary(data || {});
        } catch (error) {
          console.error('获取工厂生产综合看板汇总数据失败:', error);
          this.applyDashboardSummary({});
        }
      },
      async fetchInventoryData(
        baseParams = this.getBaseParams(),
        filterType = this.activeInventoryFilter
      ) {
        if (!baseParams) return;
        try {
          const list = await getFactoryProductionDataByStock({
            ...baseParams,
            stockType: this.getInventoryStockType(filterType)
          });
          if (filterType !== this.activeInventoryFilter) return;
          const normalized = this.normalizeNameValueList(list);
          if (filterType === 'keeper') {
            this.inventoryLedgerKeeperData = normalized;
          } else {
            this.inventoryLedgerData = normalized;
          }
        } catch (error) {
          console.error('获取库存台账数据失败:', error);
          if (filterType === 'keeper') {
            this.inventoryLedgerKeeperData = [];
          } else {
            this.inventoryLedgerData = [];
          }
        }
      },
      async fetchDeviceData(baseParams = this.getBaseParams()) {
        if (!baseParams) return;
        try {
          const list = await getFactoryProductionDataByDevice(baseParams);
          this.deviceLedger = this.buildDeviceLedger(list);
        } catch (error) {
          console.error('获取各类型设备台账数据失败:', error);
          this.deviceLedger = this.buildDeviceLedger([]);
        }
      },
      async fetchPlanData(
        baseParams = this.getBaseParams(),
        planTab = this.activePlanTab
      ) {
        if (!baseParams) return;
        try {
          const list = await getFactoryProductionDataByPlan({
            ...baseParams,
            type: this.mapPlanTabType(planTab)
          });
          if (planTab !== this.activePlanTab) return;
          this.planRows = Array.isArray(list)
            ? list.map((item) => this.mapPlanRow(item, planTab))
            : [];
        } catch (error) {
          console.error('获取生产计划进度数据失败:', error);
          if (planTab === this.activePlanTab) {
            this.planRows = [];
          }
        }
      }
    }
  };
</script>

<style lang="scss" scoped>
  [v-cloak] {
    display: none;
  }

  .fp-container {
    font-size: 16px;
    font-family: 'AlibabaPuHuiTi';
    background-image: url('@/assets/png/bj.png');
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-color: rgb(8, 29, 65);
    height: 100%;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  .fp-header {
    background-image: url('@/assets/border2.png');
    background-repeat: no-repeat;
    background-size: 100% 100%;
    height: 8.5%;
    min-height: 64px;
    max-height: 72px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 14px;
    box-sizing: border-box;
  }

  .fp-header-left {
    width: 24%;
    display: flex;
    gap: 8px;
    align-items: center;
    justify-content: flex-start;
    padding: 0;
    margin-top: 1%;
  }

  .fp-select {
    width: 36%;
  }

  .fp-date {
    width: 64%;
  }

  /* 顶部筛选控件皮肤，贴近原始大屏样式 */
  ::v-deep .fp-header-left .el-input__inner,
  ::v-deep .fp-header-left .el-range-editor.el-input__inner {
    background-color: #011944;
    border: 1px solid #0b6fd5;
    color: #e9f3ff;
    height: 28px;
    line-height: 28px;
    border-radius: 2px;
    box-shadow: 0 0 4px rgba(11, 109, 213, 0.45);
  }

  ::v-deep .fp-header-left .el-input__inner::placeholder,
  ::v-deep .fp-header-left .el-range-input::placeholder {
    color: #6f8fb8;
  }

  ::v-deep .fp-header-left .el-input__suffix,
  ::v-deep .fp-header-left .el-range__icon,
  ::v-deep .fp-header-left .el-range-separator {
    color: #9fc5ff;
  }

  ::v-deep .fp-header-left .el-range-input {
    background-color: transparent;
    color: #e9f3ff;
  }

  .fp-title {
    flex: 1;
    min-width: 0;
    text-align: center;
    font-family: '优设标题黑';
    color: #fff;
    font-size: clamp(2rem, 2.45vw, 2.7rem);
    letter-spacing: clamp(0.16rem, 0.28vw, 0.4rem);
    line-height: 1.08;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: clip;
    transform: none;
  }

  .fp-header-right {
    width: 24%;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 12px;
    margin-top: 1%;
    color: #7fa7ce;
    font-size: 0.8rem;
  }

  .fp-time {
    color: rgb(210 215 221);
    font-weight: 600;
  }

  .fp-time-text {
    padding: 0 6px;
  }

  .fp-fullscreen-toggle {
    cursor: pointer;
  }

  .fp-body {
    flex: 1;
    display: flex;
    gap: 0.6rem;
    padding: 0.55rem 0.75rem 0.75rem;
    box-sizing: border-box;
    min-height: 0;
    overflow: hidden;
  }

  .fp-col {
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
    min-height: 0;
  }

  .fp-col-left,
  .fp-col-right {
    width: 24.5%;
    min-width: 0;
  }

  .fp-col-middle {
    flex: 1;
    min-width: 0;
  }

  .fp-chart-box {
    flex: 1;
    min-height: 0;
    padding: 6px 10px 10px;
    box-sizing: border-box;
  }

  .fp-chart-fill {
    width: 100%;
    height: 100%;
  }

  /* 库存台账：自定义卡片，标题右侧带筛选 */
  .fp-inventory-card {
    flex: 0.8 !important;
    background-color: rgba(8, 29, 65, 0.8);
    border: 1px solid #124c77;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  .fp-inventory-header {
    display: flex;
    align-items: center;
    padding: 0.2rem 0.5rem;
    gap: 0.3rem;
    background-color: rgb(0, 64, 133);
    border-bottom: 1px solid #124c77;
    font-family: '优设标题黑';
    font-size: 1.2rem;
    color: #fff;
    flex-shrink: 0;
  }

  .fp-inventory-header .card-title {
    flex: 1;
    font-size: clamp(1rem, 1.8vw, 1.4rem);
  }

  .fp-inventory-filters {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.75rem;
    color: #9fc5ff;
  }

  .fp-filter-btn {
    cursor: pointer;
    padding: 2px 6px;
    border: 1px solid rgba(18, 76, 119, 0.8);
    border-radius: 2px;
    white-space: nowrap;
  }

  .fp-filter-btn:hover {
    color: #3bfff2;
    border-color: rgba(20, 205, 201, 0.6);
  }

  .fp-filter-btn.active {
    color: #3bfff2;
    background: linear-gradient(
      180deg,
      rgba(20, 205, 201, 0.26),
      rgba(17, 99, 251, 0.2)
    );
    border-color: rgba(20, 205, 201, 0.85);
    box-shadow: 0 0 8px rgba(20, 205, 201, 0.35);
  }

  .fp-filter-icon {
    font-size: 1rem;
    cursor: pointer;
  }

  /* left number card */
  .fp-number-card {
    height: 100%;
    min-width: 0;
    padding: 0 10px 10px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    box-sizing: border-box;
  }

  .fp-number-display {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
  }

  .fp-unit-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1 1 0;
    min-width: 1.2rem;
    max-width: 3rem;
    height: 5rem;
    position: relative;
  }

  .fp-unit {
    font-size: 14px;
    color: #a4b9d7;
    position: absolute;
    top: -22px;
    left: 50%;
    transform: translateX(-50%);
    width: 35px;
    white-space: nowrap;
  }

  .fp-digit-box {
    background-image: url('@/assets/png/num_bj.png');
    background-repeat: no-repeat;
    background-size: 100% 100%;
    color: #fff;
    padding: 0 0.4rem;
    height: 5rem;
    font-size: clamp(1rem, 3.5vw, 2.2rem);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    border-radius: 2px;
    width: 100%;
    box-sizing: border-box;
  }

  .fp-separator {
    flex-shrink: 0;
    color: #fff;
    font-size: clamp(1.2rem, 3vw, 2.5rem);
    font-weight: 700;
    margin: 0 2px;
    display: flex;
    align-items: center;
  }

  .fp-mini-stats {
    display: flex;
    gap: 10px;
  }

  .fp-mini-stat {
    flex: 1;
    display: flex;
    gap: 8px;
    padding: 6px 8px;
    border: 1px solid rgba(88, 137, 196, 0.9);
    background-color: rgba(0, 123, 255, 0.1);
    border-radius: 4px;
    align-items: center;
    min-width: 0;
  }

  .fp-mini-icon {
    width: 1.6rem;
    height: 1.6rem;
    padding: 0.35rem;
    background-color: rgba(0, 64, 133, 0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }

  .fp-mini-icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }

  .fp-mini-info {
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1;
  }

  .fp-mini-label {
    color: #fff;
    font-size: 0.75rem;
    letter-spacing: 1px;
    white-space: nowrap;
  }

  .fp-mini-value {
    color: #3bfff2;
    font-weight: bold;
    font-size: 0.75rem;
    white-space: nowrap;
  }

  /* middle top */
  .fp-middle-top {
    border: 1px solid #031a64;
    background: radial-gradient(
        circle at top,
        rgba(50, 197, 225, 0.22),
        transparent 55%
      )
      rgba(5, 18, 86, 0.95);
    height: 21%;
    min-height: 118px;
    display: flex;
    flex-direction: column;
    padding: 8px 12px 10px;
    box-sizing: border-box;
  }

  .fp-middle-title {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #fff;
    font-family: '优设标题黑';
    font-size: 1.2rem;
  }

  .fp-title-icon {
    width: 18px;
    height: 18px;
  }

  .fp-kpis {
    flex: 1;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
    align-items: center;
    padding-top: 6px;
  }

  .fp-kpi {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    color: #fff;
    gap: 4px;
    position: relative;
    overflow: hidden;
    padding-top: 6px;
  }

  .fp-kpi-screen {
    width: 66%;
    min-height: 3.5rem;
    border: 1px solid rgba(43, 150, 255, 0.6);
    border-radius: 6px;
    background: linear-gradient(
      180deg,
      rgba(118, 255, 255, 0.22),
      rgba(40, 176, 255, 0.26) 54%,
      rgba(10, 36, 121, 0.36)
    );
    box-shadow: 0 0 20px rgba(43, 202, 255, 0.4),
      0 0 12px rgba(17, 99, 251, 0.28) inset;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    transform: perspective(280px) rotateX(8deg) skewX(-3deg);
    transform-origin: center bottom;
  }

  .fp-kpi-screen::before,
  .fp-kpi-screen::after {
    content: '';
    position: absolute;
    bottom: -8px;
    width: 24%;
    height: 10px;
    border-bottom: 2px solid rgba(43, 202, 255, 0.62);
  }

  .fp-kpi-screen::before {
    left: 3%;
    transform: skewX(-26deg);
  }

  .fp-kpi-screen::after {
    right: 3%;
    transform: skewX(26deg);
  }

  .fp-kpi-base {
    width: 44%;
    height: 0.32rem;
    border-radius: 999px;
    background: linear-gradient(
      180deg,
      rgba(142, 255, 255, 0.98),
      rgba(22, 238, 255, 0.95) 48%,
      rgba(17, 99, 251, 0.82)
    );
    box-shadow: 0 0 16px rgba(22, 238, 255, 0.72),
      0 0 8px rgba(17, 99, 251, 0.45);
  }

  .fp-kpi-value {
    font-family: 'LCD2B';
    font-size: clamp(1.45rem, 3vw, 2.5rem);
    font-weight: 800;
    letter-spacing: 0.06em;
  }

  .fp-kpi-label {
    font-size: 0.92rem;
    color: #cfe4ff;
    letter-spacing: 0.5px;
    margin-top: 4px;
  }

  .fp-kpi-glow {
    text-shadow: 0 0 8px rgba(185, 255, 255, 0.8),
      0 0 16px rgba(20, 205, 201, 0.65), 0 0 24px rgba(17, 99, 251, 0.42);
  }

  .fp-middle-note {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.85);
    letter-spacing: 0.12rem;
    text-align: right;
  }

  /* table */
  .fp-table-wrap {
    height: 100%;
    display: flex;
    flex-direction: column;
    padding: 4px 8px 8px;
    box-sizing: border-box;
    min-height: 0;
  }

  .fp-table-tabs {
    display: flex;
    gap: 6px;
    padding: 4px 0 8px;
    flex-wrap: wrap;
  }

  .fp-tab {
    padding: 6px 10px;
    border: 1px solid rgba(18, 76, 119, 0.9);
    background-color: rgba(3, 29, 66, 0.6);
    color: #e9ecef;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
    user-select: none;
  }

  .fp-tab.active {
    border-color: rgba(20, 205, 201, 0.95);
    color: #3bfff2;
    box-shadow: 0 0 10px rgba(20, 205, 201, 0.2);
  }

  .fp-table {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  .fp-main-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
  }

  .fp-main-table thead th {
    background-color: rgba(3, 29, 66, 0.95);
    padding: 0.55rem 0.2rem;
    text-align: center;
    font-weight: bold;
    color: #0b6fd5;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    font-size: clamp(0.74rem, 0.9vw, 0.82rem);
    line-height: 1.15;
    white-space: normal;
    overflow: hidden;
    text-overflow: clip;
    word-break: keep-all;
  }

  .fp-scroll-body {
    flex: 1;
    min-height: 0;
    overflow: hidden;
  }

  .fp-cell {
    padding: 0.35rem 0.2rem;
    text-align: center;
    color: #e9ecef;
    font-size: 0.82rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .fp-row:hover .fp-cell {
    background-color: rgba(3, 29, 66, 0.8) !important;
  }

  .fp-empty {
    text-align: center;
    padding: 1rem;
    color: #7fa7ce;
  }

  .fp-status-red {
    color: #fff;
    font-weight: 700;
  }

  .fp-status-blue {
    color: #ffd16c;
    font-weight: 700;
  }

  /* quality */
  .fp-quality {
    height: 100%;
    padding: 8px 10px 10px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    gap: 12px;
    perspective: 700px;
  }

  .fp-quality-top {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
  }

  .fp-quality-item {
    border: 1px solid rgba(18, 76, 119, 0.9);
    border-radius: 6px;
    background: linear-gradient(
      180deg,
      rgba(22, 120, 201, 0.18),
      rgba(3, 29, 66, 0.72) 62%,
      rgba(2, 21, 54, 0.9)
    );
    padding: 8px 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    position: relative;
    transform: perspective(260px) rotateX(8deg);
    transform-origin: center bottom;
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.22),
      0 0 12px rgba(18, 129, 214, 0.22) inset;
    overflow: hidden;
  }

  .fp-quality-item::before {
    content: '';
    position: absolute;
    top: 0;
    left: 8%;
    right: 8%;
    height: 1px;
    background: linear-gradient(
      90deg,
      rgba(255, 255, 255, 0),
      rgba(153, 245, 255, 0.95),
      rgba(255, 255, 255, 0)
    );
  }

  .fp-quality-label {
    color: #cfe4ff;
    font-size: 0.85rem;
    letter-spacing: 1px;
  }

  .fp-quality-value {
    font-family: 'LCD2B';
    font-size: clamp(1.25rem, 2.1vw, 1.6rem);
    font-weight: 800;
    text-shadow: 0 0 8px rgba(111, 233, 255, 0.35),
      0 0 14px rgba(17, 99, 251, 0.28);
  }

  .fp-quality-value.good {
    color: #14cdc9;
  }

  .fp-quality-value.bad {
    color: #ff4d4f;
  }

  .fp-quality-value.warn {
    color: #f9bb19;
  }

  .fp-quality-bars {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 10px;
  }

  .fp-qbar {
    display: flex;
    gap: 10px;
    align-items: center;
  }

  .fp-qbar-name {
    width: 72px;
    color: #e9ecef;
    font-size: 0.85rem;
  }

  .fp-qbar-track {
    flex: 1;
    height: 10px;
    border-radius: 999px;
    background: linear-gradient(
      180deg,
      rgba(6, 34, 82, 0.92),
      rgba(2, 25, 61, 0.88)
    );
    border: 1px solid rgba(18, 76, 119, 0.9);
    overflow: hidden;
    position: relative;
    box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3) inset;
  }

  .fp-qbar-track::before {
    content: '';
    position: absolute;
    left: 4px;
    right: 4px;
    top: 1px;
    height: 2px;
    border-radius: 999px;
    background: linear-gradient(
      90deg,
      rgba(255, 255, 255, 0),
      rgba(168, 233, 255, 0.5),
      rgba(255, 255, 255, 0)
    );
    pointer-events: none;
  }

  .fp-qbar-fill {
    height: 100%;
    border-radius: 999px;
    box-shadow: 0 0 10px currentColor;
    position: relative;
  }

  .fp-qbar-fill::after {
    content: '';
    position: absolute;
    right: 0;
    top: -1px;
    width: 16px;
    height: calc(100% + 2px);
    border-radius: 999px;
    background: radial-gradient(
      circle at 25% 50%,
      rgba(255, 255, 255, 0.62),
      rgba(255, 255, 255, 0) 70%
    );
  }

  .fp-qbar-fill.good {
    background: linear-gradient(
      90deg,
      rgba(20, 205, 201, 1),
      rgba(20, 205, 201, 0.25)
    );
  }

  .fp-qbar-fill.bad {
    background: linear-gradient(
      90deg,
      rgba(255, 77, 79, 1),
      rgba(255, 77, 79, 0.2)
    );
  }

  .fp-qbar-fill.warn {
    background: linear-gradient(
      90deg,
      rgba(249, 187, 25, 1),
      rgba(249, 187, 25, 0.25)
    );
  }

  /* 覆盖通用 ChartCard 的最小高度，避免低分辨率下被挤出可视区 */
  ::v-deep .chart-container,
  ::v-deep .process-chart-container,
  ::v-deep .product-top10-chart-container,
  ::v-deep .qualification-rate-chart-container {
    min-height: 0 !important;
  }

  ::v-deep .chart-card .card-content {
    min-height: 0;
  }
</style>

<style>
  .el-icon-_screen-full,
  .el-icon-_screen-restore {
    font-size: 1.2rem;
    cursor: pointer;
    transition: all 0.3s;
    color: #7fa7ce;
  }

  .el-icon-_screen-full:hover,
  .el-icon-_screen-restore:hover {
    color: #fff;
    transform: scale(1.1);
  }
</style>
