<template>
  <dv-full-screen-container :style="{background: '#0f1c2b', color: '#fff'}">
    <!-- 顶部标题 -->
    <dv-border-box-8 style="height:80px; padding: 10px;">
      <div style="font-size: 38px; font-weight: 600; text-align: center; color: #40e0d0;">设计任务进度看板</div>
      <div style="font-size: 28px;position: absolute; top: 50%; right: 100px; transform: translateY(-50%); font-size: 28px; font-weight: 600; color: #40e0d0;">{{ getCurrentTime() }}</div>
    </dv-border-box-8>

    <!-- 中间布局 -->
    <div style="display:flex; height: calc(100% - 100px); padding: 10px;">

      <!-- 左侧：轮播表 -->
      <dv-border-box-2 style="flex:2; margin-right: 10px; padding:10px;">
        <dv-scroll-board :config="tableConfig" style="width:100%; height:100%;"/>
      </dv-border-box-2>

      <!-- 右侧：图表区 -->
      <div style="flex:1; display: flex; flex-direction: column; gap: 10px;">

        <!-- 超期项目 -->
        <dv-border style="flex:1; padding:10px;">
          <div style="height: 100%; display: flex; flex-direction: column;">
            <!-- 标题和统计信息 -->
            <div style="display: flex; justify-content: center; gap: 20px; align-items: center; margin-bottom: 10px;">
              <h4 style="margin: 0; color: #ff6b6b; font-size: 24px;font-weight: 600;">超期项目</h4>
              <div style="display: flex; gap: 15px; font-size: 18px;font-weight: 600;">
                <span style="color: #ff6b6b;">总超期: {{ overdueStats.total }}项</span>
                <span style="color: #ffa726;">严重超期: {{ overdueStats.critical }}项</span>
                <span style="color: #42a5f5;">一般超期: {{ overdueStats.normal }}项</span>
              </div>
            </div>

            <!-- 超期项目列表 -->
            <div class="overdue-list" style="flex: 2; overflow-y: auto;">
               <!-- 轮播显示：当项目超过3条时启用 -->
               <div
                 v-if="overdueProjects.length > 3"
                 class="carousel-container"
                 @mouseenter="stopCarousel"
                 @mouseleave="startCarousel"
               >
                 <div
                   v-for="(item, index) in visibleOverdueProjects"
                   :key="`carousel-${index}`"
                   class="overdue-item"
                   :class="getOverdueLevelClass(item.overdueDays)"
                 >
                   <div class="item-header">
                     <span class="project-code">{{ item.projectCode }}</span>
                     <span class="overdue-days" v-if="item.overdueDays > 0" :class="getOverdueLevelClass(item.overdueDays)">
                       超期{{ item.overdueDays }}天
                     </span>
                   </div>
                   <div class="item-content">
                     <div class="designer">{{ item.designer }}</div>
                     <div class="description">{{ item.description }}</div>
                     <div class="time-info">
                       <span>开始: {{ item.startDate }}</span>
                       <span>计划: {{ item.planDate }}</span>
                     </div>
                   </div>
                 </div>
               </div>

              <!-- 普通显示：当项目不超过1条时 -->
              <div v-else>
                <div
                  v-for="(item, index) in overdueProjects"
                  :key="index"
                  class="overdue-item"
                  :class="getOverdueLevelClass(item.overdueDays)"
                >
                  <div class="item-header">
                    <span class="project-code">{{ item.projectCode }}</span>
                    <span class="overdue-days" :class="getOverdueLevelClass(item.overdueDays)">
                      {{ item.overdueDays * -1 }}天
                    </span>
                  </div>
                  <div class="item-content">
                    <div class="designer">{{ item.designer }}</div>
                    <div class="description">{{ item.description }}</div>
                    <div class="time-info">
                      <span>开始: {{ item.startDate }}</span>
                      <span>计划: {{ item.planDate }}</span>
                    </div>
                  </div>
                </div>
              </div>

              <!-- 无超期项目时的提示 -->
              <div v-if="overdueProjects.length === 0" class="no-overdue">
                <div style="text-align: center; color: #4caf50; padding: 20px;">
                  <i class="el-icon-success" style="font-size: 24px;"></i>
                  <div style="margin-top: 10px;">暂无超期项目</div>
                </div>
              </div>
            </div>
          </div>
        </dv-border>

        <!-- 临期项目排名轮播表 -->
        <dv-border-box-10 style="flex:1; padding:10px;">
          <div style="height: 100%; display: flex; flex-direction: column;">
            <!-- 标题 -->
            <div style="text-align: center; margin-bottom: 10px;">
              <h3 style="margin: 0; color: #ffa726; font-size: 20px;">临期项目</h3>
            </div>

            <!-- 临期项目轮播表 -->
            <div class="deadline-list" style="flex: 1; overflow: hidden;">
              <div
                v-if="overdueProjects.length > 3"
                class="deadline-carousel"
                @mouseenter="stopDeadlineCarousel"
                @mouseleave="startDeadlineCarousel"
              >
                <div
                  v-for="(item, index) in visibleDeadlineProjects"
                  :key="`deadline-${index}`"
                  class="deadline-item"
                  :class="getDeadlineLevelClass(item.overdueDays)"
                >
                  <div class="rank-number">{{ item.rank }}</div>
                  <div class="item-info">
                    <div class="designer-name" style="flex: 1;">{{ item.designer }}</div>
                    <div style="display: flex;flex: 1;">
                        <div v-if="item.overdueDays > 0" class="deadline-days" :class="getDeadlineLevelClass(item.overdueDays)">临期{{ item.overdueDays }}天</div>
                    </div>
                    <div class="customer-center">{{item.projectCode}}</div>
                    <div class="customer-center">{{item.planDate}}</div>
                  </div>
                </div>

                <!-- 轮播指示器 -->
                <div class="deadline-indicators">
                  <span
                    v-for="(_, index) in deadlinePages"
                    :key="index"
                    class="deadline-indicator"
                    :class="{ active: index === currentDeadlineIndex }"
                    @click="goToDeadlinePage(index)"
                  ></span>
                </div>
              </div>

              <!-- 普通显示：当项目不超过4条时 -->
              <div v-else>
                <div
                  v-for="(item, index) in overdueProjects"
                  :key="index"
                  class="deadline-item"
                  :class="getDeadlineLevelClass(item.overdueDays)"
                >
                  <div class="rank-number">{{ index + 1 }}</div>
                  <div class="item-info">
                    <div class="designer-name">{{ item.designer }}</div>
                    <div class="deadline-days" :class="getDeadlineLevelClass(item.overdueDays)">
                      临期{{ item.overdueDays }}天
                    </div>
                  </div>
                </div>
              </div>

              <!-- 无临期项目时的提示 -->
              <div v-if="overdueProjects.length === 0" class="no-deadline">
                <div style="text-align: center; color: #4caf50; padding: 20px;">
                  <i class="el-icon-success" style="font-size: 24px;"></i>
                  <div style="margin-top: 10px;">暂无临期项目</div>
                </div>
              </div>
            </div>
          </div>
        </dv-border-box-10>

        <!-- 项目数量热力图（周/月/年 x 设计人） -->
        <dv-border-box-13 style="flex:1; padding:10px;">
          <div style="display:flex; justify-content: space-between; align-items:center; margin-bottom: 8px;">
            <h4 style="margin:0; color:#40e0d0;">项目方案数</h4>
            <el-radio-group v-model="timeGranularity" size="mini">
              <el-radio-button label="week">周</el-radio-button>
              <el-radio-button label="month">月</el-radio-button>
              <el-radio-button label="year">年</el-radio-button>
            </el-radio-group>
          </div>
          <dv-capsule-chart :config="capsuleConfig" style="width:100%; height:calc(100% - 30px);" />
        </dv-border-box-13>

      </div>
    </div>

    <!-- Loading -->
    <dv-loading v-if="loading">加载中...</dv-loading>
  </dv-full-screen-container>
</template>

<script>
import { listProPlan2, listProPlanOverdue ,listProPlanExpirys, getDesignerSummary } from '@/api/system/proPlan'
import dayjs from 'dayjs';
export default {
  data () {
    return {
      loading: false,
      timer: null, // 定时器
      carouselTimer: null, // 轮播定时器
      currentCarouselIndex: 0, // 当前轮播索引
      carouselInterval: 4000, // 轮播间隔时间（毫秒）
      deadlineTimer: null, // 临期项目轮播定时器
      currentDeadlineIndex: 0, // 当前临期项目轮播索引
      deadlineInterval: 4000, // 临期项目轮播间隔时间（毫秒）
      // 超期项目数据
      overdueProjects: [],
      // 临期项目数据
      expiryOverdueProjects: [],
      tableConfig: {
        header: ['执行令号/方案号', '设计人', '内容', '开始时间', '完成时间'],
        data: [],
        index: true,
        // columnWidth: [120, 200, 230, 400, 150, 150, 200, 100],
        align: ['center','center','center','center','center','center','center'],
        rowNum: 8,
        waitTime: 5000,
        hoverPause: false
      }
,
      timeGranularity: 'month',
      designerSummary: []
    }
  },
  async created() {
    // 初始化超期项目数据，动态计算超期天数
    this.initProjects();
    // 先拉取超期项目数据
    await this.fetchOverdue();
    // 再拉取临期项目数据
    await this.ExpiryOverdue();
    // 拉取方案列表填充滚动表格（此时超期和临期数据已准备好）
    this.fetchProPlanList();
    // 拉取设计人汇总数据（用于胶囊图）
    await this.fetchDesignerSummary();
  },
  mounted() {
    // 每分钟完全刷新一次数据
    this.timer = setInterval(async () => {
      await this.fetchOverdue();
      await this.ExpiryOverdue();
      this.fetchProPlanList();
      this.initProjects();
      await this.fetchDesignerSummary();
    }, 60 * 1000); // 1分钟

    // 启动轮播（当项目超过3条时）
    this.startCarousel();

    // 启动临期项目轮播（当项目超过4条时）
    this.startDeadlineCarousel();
  },
  beforeDestroy() {
    // 清理定时器
    if (this.timer) {
      clearInterval(this.timer);
    }
    if (this.carouselTimer) {
      clearInterval(this.carouselTimer);
    }
    if (this.deadlineTimer) {
      clearInterval(this.deadlineTimer);
    }
  },
  computed: {
    // 超期项目统计
    overdueStats() {
      console.log(this.overdueProjects);
      const total = this.overdueProjects.length;
      const critical = this.overdueProjects.filter(item => item.overdueDays > 5).length;
      const normal = this.overdueProjects.filter(item => item.overdueDays <= 5).length;

      return { total, critical, normal };
    },
    // 轮播相关计算属性
    carouselPages() {
      return Math.ceil(this.overdueProjects.length / 3);
    },
    visibleOverdueProjects() {
      const start = this.currentCarouselIndex * 3;
      const end = start + 3;
      return this.overdueProjects.slice(start, end);
    },
    // 临期项目相关计算属性
    deadlinePages() {
      return Math.ceil(this.expiryOverdueProjects.length / 3);
    },
    visibleDeadlineProjects() {
      const start = this.currentDeadlineIndex * 3;
      const end = start + 3;
      return this.expiryOverdueProjects.slice(start, end).map((item, index) => ({
        ...item,
        rank: start + index + 1
      }));
    },
    heatmapOption() {
      const designers = this.getUniqueDesigners();
      const timeLabels = this.getTimeLabels(this.timeGranularity);

      const indexOfDesigner = new Map();
      designers.forEach((d, i) => indexOfDesigner.set(d, i));
      const indexOfTime = new Map();
      timeLabels.forEach((t, i) => indexOfTime.set(t, i));

      const counts = Array.from({ length: designers.length }, () => Array(timeLabels.length).fill(0));

      (this.tableConfig.data || []).forEach(row => {
        // 清理HTML标签
        const designer = row[1] ? row[1].replace(/<[^>]*>/g, '') : '';
        const dateStr = row[4] ? row[4].replace(/<[^>]*>/g, '') : '';

        if (!designer || !dateStr || designer === '-') return;
        const label = this.formatTimeLabel(dateStr, this.timeGranularity);
        if (!indexOfDesigner.has(designer) || !indexOfTime.has(label)) return;
        counts[indexOfDesigner.get(designer)][indexOfTime.get(label)] += 1;
      });

      const seriesData = [];
      let maxVal = 0;
      for (let y = 0; y < designers.length; y++) {
        for (let x = 0; x < timeLabels.length; x++) {
          const val = counts[y][x];
          maxVal = Math.max(maxVal, val);
          seriesData.push([x, y, val]);
        }
      }

      return {
        backgroundColor: 'transparent',
        tooltip: {
          position: 'top',
          formatter: (p) => {
            const t = timeLabels[p.data[0]];
            const d = designers[p.data[1]];
            const v = p.data[2];
            return `${d}<br/>${t}: ${v}`;
          }
        },
        grid: { top: 20, right: 10, bottom: 20, left: 80 },
        xAxis: {
          type: 'category',
          data: timeLabels,
          axisLabel: { color: '#e2e8f0' },
          axisLine: { lineStyle: { color: '#334155' } },
          splitLine: { show: false }
        },
        yAxis: {
          type: 'category',
          data: designers,
          axisLabel: { color: '#e2e8f0' },
          axisLine: { lineStyle: { color: '#334155' } },
          splitLine: { show: false }
        },
        visualMap: {
          min: 0,
          max: Math.max(5, maxVal),
          calculable: true,
          orient: 'vertical',
          right: 0,
          top: 'middle',
          textStyle: { color: '#e2e8f0' },
          inRange: {
            color: ['#0f172a', '#22d3ee']
          }
        },
        series: [
          {
            type: 'heatmap',
            data: seriesData,
            label: { show: false },
            emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)' } }
          }
        ]
      };
    },
    capsuleConfig() {
      const list = Array.isArray(this.designerSummary) ? this.designerSummary : [];
      const key =
        this.timeGranularity === 'week'
          ? 'weeklyCount'
          : this.timeGranularity === 'year'
          ? 'yearlyCount'
          : 'monthlyCount';
      const data = list
        .map(item => ({
          name: item.designer || item.name || '-',
          value: Number(item[key] ?? 0)
        }))
        .sort((a, b) => b.value - a.value);
      return {
        data,
        colors: ['#ef4444', '#f59e0b', '#22c55e', '#3b82f6', '#8b5cf6', '#06b6d4', '#ea580c'],
        unit: '项',
        showValue: true
      };
    }
  },
  methods: {
    getCurrentTime() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份是从0开始的，所以加1
    const day = String(now.getDate()).padStart(2, '0');
    const hours = String(now.getHours()).padStart(2, '0');
    const minutes = String(now.getMinutes()).padStart(2, '0');
    const seconds = String(now.getSeconds()).padStart(2, '0');

    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  },

    // 初始化项目数据
    initProjects() {
      const today = new Date();

      // 超期项目天数更新
      this.overdueProjects.forEach(project => {
        const planDate = new Date(project.planDate);
        const diffTime = planDate.getTime() - today.getTime();
        const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));

        // 超期天数：正数显示超期天数，负数不显示
        project.overdueDays = diffDays > 0 ? diffDays : null;
      });

      // 临期项目天数更新
      this.expiryOverdueProjects.forEach(project => {
        const planDate = new Date(project.planDate);
        const diffTime = planDate.getTime() - today.getTime();
        const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));

        // 临期天数：正数显示剩余天数，负数不显示
        project.overdueDays = diffDays > 0 ? diffDays : null;
      });

      // 按临期天数排序，临期时间短的排在前面
      // this.overdueProjects.sort((a, b) => {
      //   if (a.overdueDays === null) return 1;
      //   if (b.overdueDays === null) return -1;
      //   return a.overdueDays - b.overdueDays;
      // });

      // 重新启动超期项目轮播
      this.startCarousel();

      // 重新启动临期项目轮播
      this.startDeadlineCarousel();
    },

    // 获取超期等级样式类
    getOverdueLevelClass(overdueDays) {
      if (overdueDays > 5) {
        return 'critical-overdue';
      } else if (overdueDays > 2) {
        return 'normal-overdue';
      } else {
        return 'light-overdue';
      }
    },
    // 获取临期等级样式类
    getDeadlineLevelClass(deadlineDays) {
      if (deadlineDays > 5) {
        return 'critical-deadline';
      } else if (deadlineDays > 2) {
        return 'normal-deadline';
      } else {
        return 'light-deadline';
      }
    },
    // 轮播相关方法
    goToPage(index) {
      this.currentCarouselIndex = index;
    },
    // 临期项目轮播相关方法
    goToDeadlinePage(index) {
      this.currentDeadlineIndex = index;
    },

    // 启动临期项目轮播
    startDeadlineCarousel() {
      // 先停止现有的轮播
      this.stopDeadlineCarousel();

      if (this.expiryOverdueProjects.length > 4) {
        this.deadlineTimer = setInterval(() => {
          this.nextDeadlinePage();
        }, this.deadlineInterval);
      }
    },

    // 停止临期项目轮播
    stopDeadlineCarousel() {
      if (this.deadlineTimer) {
        clearInterval(this.deadlineTimer);
        this.deadlineTimer = null;
      }
    },

    // 临期项目下一页
    nextDeadlinePage() {
      if (this.deadlinePages > 1) {
        this.currentDeadlineIndex = (this.currentDeadlineIndex + 1) % this.deadlinePages;
      }
    },

    // 临期项目上一页
    prevDeadlinePage() {
      if (this.deadlinePages > 1) {
        this.currentDeadlineIndex = this.currentDeadlineIndex === 0
          ? this.deadlinePages - 1
          : this.currentDeadlineIndex - 1;
      }
    },

    // 启动轮播
    startCarousel() {
      // 先停止现有的轮播
      this.stopCarousel();

      if (this.overdueProjects.length > 3) {
        this.carouselTimer = setInterval(() => {
          this.nextPage();
        }, this.carouselInterval);
      }
    },

    // 停止轮播
    stopCarousel() {
      if (this.carouselTimer) {
        clearInterval(this.carouselTimer);
        this.carouselTimer = null;
      }
    },

    // 下一页
    nextPage() {
      if (this.carouselPages > 1) {
        this.currentCarouselIndex = (this.currentCarouselIndex + 1) % this.carouselPages;
      }
    },

    // 上一页
    prevPage() {
      if (this.carouselPages > 1) {
        this.currentCarouselIndex = this.currentCarouselIndex === 0
          ? this.carouselPages - 1
          : this.currentCarouselIndex - 1;
      }
    },
    // 获取临期数据
    async  ExpiryOverdue(){
      try {
        this.loading = true;
        const res = await listProPlanExpirys();
        const list = res?.rows || res || [];
        this.expiryOverdueProjects = (list || []).map(item => ({
          projectCode: item.projectCode || item.code || '-',
          designer: item.designer || '-',
          description: item.description || item.remark || '',
          startDate: (item.startDate || '').toString().slice(0, 10),
          planDate: (item.planDate || item.endDate || '').toString().slice(0, 10),
          overdueDays: Number(item.overdueDays ?? 0)
        }));
        // 排序与轮播刷新
        this.expiryOverdueProjects.sort((a, b) => b.overdueDays - a.overdueDays);
        this.startDeadlineCarousel();

        console.log('临期项目数据加载完成:', this.expiryOverdueProjects.length, '项');
      } catch (e) {
        console.error('fetchOverdue error:', e);
      } finally {
        this.loading = false;
      }
    },
    // 从后端加载超期数据并刷新视图
    async fetchOverdue() {
      try {
        this.loading = true;
        const res = await listProPlanOverdue();
        const list = res?.rows || res || [];
        this.overdueProjects = (list || []).map(item => ({
          projectCode: item.projectCode || item.code || '-',
          designer: item.designer || '-',
          description: item.description || item.remark || '',
          startDate: (item.startDate || '').toString().slice(0, 10),
          planDate: (item.planDate || item.endDate || '').toString().slice(0, 10),
          overdueDays: Number(item.overdueDays ?? 0)
        }));
        // 排序与轮播刷新
        this.overdueProjects.sort((a, b) => b.overdueDays - a.overdueDays);
        this.startCarousel();

        console.log('超期项目数据加载完成:', this.overdueProjects.length, '项');
      } catch (e) {
        console.error('fetchOverdue error:', e);
      } finally {
        this.loading = false;
      }
    },
    // 拉取方案列表
    async fetchProPlanList() {
      try {
        const { rows = [] } = await listProPlan2();
        // 映射为表格行：[执行令号, 设计人, 内容, 开始时间, 完成时间]
        const mapped = rows.map(item => {
          const projectCode = item.projectCode || '-';
          const designer = item.designer || '-';
          const remark = item.remark || '-';
          const startDate = item.startDate ? dayjs(item.startDate).format('YYYY-MM-DD') : '-';
          const endDate = item.endDate ? dayjs(item.endDate).format('YYYY-MM-DD') : '-';

          // 检查是否为超期或临期项目
          const overdueProject = this.overdueProjects.find(p =>
            p.projectCode === projectCode && p.designer === designer
          );
          const deadlineProject = this.expiryOverdueProjects.find(p =>
            p.projectCode === projectCode && p.designer === designer
          );

          // 根据项目状态应用不同的HTML样式
          let styledRow = [
            projectCode,
            designer,
            remark,
            startDate,
            endDate
          ];

          if (overdueProject) {
            // 超期项目：名字和日期变红
            console.log('应用超期项目HTML样式:', projectCode, designer);
            styledRow = [
              `<span style="color:#ff4757; font-weight:bold;">${projectCode}</span>`,
              `<span style="color:#ff4757; font-weight:bold;">${designer}</span>`,
              remark,
              startDate,
              `<span style="color:#ff4757; font-weight:bold;">${endDate}</span>`
            ];
          } else if (deadlineProject) {
            // 临期项目：名字和日期变黄
            console.log('应用临期项目HTML样式:', projectCode, designer);
            styledRow = [
              `<span style="color:#ffa502; font-weight:bold;">${projectCode}</span>`,
              `<span style="color:#ffa502; font-weight:bold;">${designer}</span>`,
              remark,
              startDate,
              `<span style="color:#ffa502; font-weight:bold;">${endDate}</span>`
            ];
          }

          return styledRow;
        });

        // 生成新的配置对象以触发组件刷新
        this.tableConfig = {
          header: ['执行令号/方案号', '设计人', '内容', '开始时间', '完成时间'],
          data: mapped,
          index: true,
          align: ['center','center','center','center','center'],
          rowNum: 8,
          waitTime: 5000,
          hoverPause: false
        };

        console.log('轮播表数据已更新，包含HTML样式');
      } catch (e) {
        console.error('fetchProPlanList error:', e);
      }
    },
    // 拉取设计人汇总数据
    async fetchDesignerSummary() {
      try {
        const res = await getDesignerSummary();
        const rows = res?.rows || res?.data || res || [];
        this.designerSummary = Array.isArray(rows) ? rows : [];
      } catch (e) {
        console.error('fetchDesignerSummary error:', e);
        this.designerSummary = [];
      }
    },
    getUniqueDesigners() {
      const set = new Set();

      // 从原始数据中获取设计人，避免HTML标签
      (this.overdueProjects || []).forEach(item => {
        if (item.designer) set.add(item.designer);
      });
      (this.expiryOverdueProjects || []).forEach(item => {
        if (item.designer) set.add(item.designer);
      });

      // 从轮播表数据中提取设计人，清理HTML标签
      (this.tableConfig.data || []).forEach(row => {
        if (row[1]) {
          // 移除HTML标签，只保留纯文本
          const cleanDesigner = row[1].replace(/<[^>]*>/g, '');
          if (cleanDesigner && cleanDesigner !== '-') {
            set.add(cleanDesigner);
          }
        }
      });

      return Array.from(set);
    },
    getTimeLabels(granularity) {
      const labelsSet = new Set();
      const add = (dateStr) => {
        if (!dateStr) return;
        // 清理HTML标签
        const cleanDateStr = dateStr.replace(/<[^>]*>/g, '');
        const lbl = this.formatTimeLabel(cleanDateStr, granularity);
        if (lbl) labelsSet.add(lbl);
      };
      (this.tableConfig.data || []).forEach(row => {
        add(row[4]);
      });
      const labels = Array.from(labelsSet);
      labels.sort((a, b) => a.localeCompare(b));
      return labels;
    },
    formatTimeLabel(dateStr, granularity) {
      const d = new Date(dateStr);
      if (isNaN(d.getTime())) return '';
      const year = d.getFullYear();
      const month = String(d.getMonth() + 1).padStart(2, '0');
      if (granularity === 'year') {
        return `${year}`;
      }
      if (granularity === 'month') {
        return `${year}-${month}`;
      }
      const { week, isoYear } = this.getISOWeek(d);
      return `${isoYear}-W${String(week).padStart(2, '0')}`;
    },
    getISOWeek(dateObj) {
      const d = new Date(Date.UTC(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate()));
      const dayNum = d.getUTCDay() || 7;
      d.setUTCDate(d.getUTCDate() + 4 - dayNum);
      const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
      const weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
      return { week: weekNo, isoYear: d.getUTCFullYear() };
    },


  }
}
</script>

<style>
body, html, #app {
  margin: 0;
  height: 100%;
  background: #0f1c2b;
}
.grid-item {
  margin: 30px;
}

/* 超期项目样式 */
.overdue-list {
  scrollbar-width: thin;
  scrollbar-color: #4a5568 #2d3748;
}

.overdue-list::-webkit-scrollbar {
  width: 6px;
}

.overdue-list::-webkit-scrollbar-track {
  background: #2d3748;
  border-radius: 3px;
}

.overdue-list::-webkit-scrollbar-thumb {
  background: #4a5568;
  border-radius: 3px;
}

.overdue-list::-webkit-scrollbar-thumb:hover {
  background: #718096;
}

.overdue-item {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 10px;
  border-left: 4px solid;
  transition: all 0.3s ease;
}

.overdue-item:hover {
  background: rgba(255, 255, 255, 0.1);
  transform: translateX(5px);
}

.overdue-item.critical-overdue {
  border-left-color: #ff6b6b;
  background: rgba(255, 107, 107, 0.1);
}

.overdue-item.normal-overdue {
  border-left-color: #ffa726;
  background: rgba(255, 167, 38, 0.1);
}

.overdue-item.light-overdue {
  border-left-color: #42a5f5;
  background: rgba(66, 165, 245, 0.1);
}

.item-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.project-code {
  font-weight: bold;
  color: #fff;
  font-size: 20px;
}

.overdue-days {
  font-weight: bold;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.2);
}

.overdue-days.critical-overdue {
  color: #ff6b6b;
  background: rgba(255, 107, 107, 0.2);
}

.overdue-days.normal-overdue {
  color: #ffa726;
  background: rgba(255, 167, 38, 0.2);
}

.overdue-days.light-overdue {
  color: #42a5f5;
  background: rgba(66, 165, 245, 0.2);
}

.item-content {
  font-size: 12px;
}

.designer {
  color: #40e0d0;
  font-weight: bold;
  margin-bottom: 19px;
}

.description {
  color: #e2e8f0;
  margin-bottom: 6px;
  line-height: 1.3;
}

.time-info {
  display: flex;
  gap: 15px;
  color: #a0aec0;
  font-size: 11px;
}

.no-overdue {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}



 /* 轮播容器样式 */
 .carousel-container {
   position: relative;
   height: 100%;
 }



 /* 轮播项目动画 */
 .overdue-item {
   animation: fadeIn 0.5s ease-in-out;
 }

 @keyframes fadeIn {
   from {
     opacity: 0;
     transform: translateY(10px);
   }
   to {
     opacity: 1;
     transform: translateY(0);
   }
 }

 /* 临期项目样式 */
 .deadline-list {
   scrollbar-width: thin;
   scrollbar-color: #4a5568 #2d3748;
 }

 .deadline-list::-webkit-scrollbar {
   width: 6px;
 }

 .deadline-list::-webkit-scrollbar-track {
   background: #2d3748;
   border-radius: 3px;
 }

 .deadline-list::-webkit-scrollbar-thumb {
   background: #4a5568;
   border-radius: 3px;
 }

 .deadline-list::-webkit-scrollbar-thumb:hover {
   background: #718096;
 }

 .deadline-item {
   background: rgba(255, 255, 255, 0.05);
   border-radius: 8px;
   padding: 12px;
   margin-bottom: 10px;
   border-left: 4px solid;
   transition: all 0.3s ease;
   display: flex;
   align-items: center;
   gap: 10px;
 }

 .deadline-item:hover {
   background: rgba(255, 255, 255, 0.1);
   transform: translateX(5px);
 }

 .deadline-item.critical-deadline {
   border-left-color: #ff6b6b;
   background: rgba(255, 107, 107, 0.1);
 }

 .deadline-item.normal-deadline {
   border-left-color: #ffa726;
   background: rgba(255, 167, 38, 0.1);
 }

 .deadline-item.light-deadline {
   border-left-color: #42a5f5;
   background: rgba(66, 165, 245, 0.1);
 }

 .rank-number {
   font-weight: bold;
   font-size: 18px;
   color: #40e0d0;
   min-width: 30px;
   text-align: center;
 }

 .item-info {
   display: flex;
   flex: 1;
   justify-content: space-around;
   gap: 20px;
   justify-items: center;
   text-align: center;
   align-items: center;

 }

 .designer-name {
   font-weight: bold;
   color: #fff;
   font-size: 18px;
   margin-bottom: 4px;
 }

 .customer-center{
  display: flex;
   align-items: center;
   flex: 1;
   margin: 0 auto;
 }

 .deadline-days {
  margin: 0 auto;
   font-weight: bold;
   font-size: 16px;
   padding: 5px 8px;
   border-radius: 8px;
   background: rgba(255, 255, 255, 0.2);
 }

 .deadline-days.critical-deadline {
   color: #ff6b6b;
   background: rgba(255, 107, 107, 0.2);
 }

 .deadline-days.normal-deadline {
   color: #ffa726;
   background: rgba(255, 167, 38, 0.2);
 }

 .deadline-days.light-deadline {
   color: #42a5f5;
   background: rgba(66, 165, 245, 0.2);
 }

 .header{
  font-size: 22px !important;
  font-weight: 600 !important;
 }

.row-item {
  font-size: 20px !important;
}

/* 轮播表样式 */
.dv-scroll-board .header {
  background: linear-gradient(90deg, #1e3a8a 0%, #3b82f6 100%) !important;
  color: #ffffff !important;
  font-weight: bold !important;
}

/* 轮播表单元格样式 */
.dv-scroll-board .rows .row-item .ceil {
  transition: all 0.3s ease;
}





 @keyframes fadeIn {
   from {
     opacity: 0;
     transform: translateY(10px);
   }
   to {
     opacity: 1;
     transform: translateY(0);
   }
 }
</style>
