<template>

  <div class="order-value-view">

    <!-- 头部区域 -->

    <header class="page-header">

      <h1 class="page-title">订单价值</h1>

      <div class="header-controls">

        <span class="control-label">时光回溯控制器</span>

        <div class="control-item-group">

      <button :class="{'time-tab': true, 'active': activeTab === '7d'}" @click="selectDateRange('7d')">最近7天</button>

      <button :class="{'time-tab': true, 'active': activeTab === 'tm'}" @click="selectDateRange('tm')">本月</button>

      <button :class="{'time-tab': true, 'active': activeTab === 'lm'}" @click="selectDateRange('lm')">上月</button>

      <button :class="{'time-tab': true, 'active': activeTab === 'all'}" @click="selectDateRange('all')">全量数据</button>

    </div>

    <button class="upload-btn" :disabled="uploadingOrder" @click="triggerOrderUpload">{{ uploadingOrder ? '上传中...' : '上传CSV导入' }}</button>
    <input ref="orderUploadInput" type="file" accept=".csv" multiple style="display: none;" @change="handleOrderUploadChange">

  </div>

    </header>



    <!-- ✨【修改点1：KPI卡片的值现在是动态的】✨ -->

    <section class="kpi-cards-grid">

      <KpiCard title="总交易额 (GMV)" :value="kpiData.gmv" :trend="kpiData.gmvTrend" :trend-color="getTrendColor(kpiData.gmvTrend)" icon="📈" />

      <KpiCard title="P80 订单贡献比" :value="kpiData.p80Contribution" :trend="kpiData.p80Trend" :trend-color="getTrendColor(kpiData.p80Trend)" icon="🏆" />

      <KpiCard title="Top 5 商品贡献比" :value="kpiData.top5Contribution" :trend="kpiData.top5Trend" :trend-color="getTrendColor(kpiData.top5Trend)" icon="🔥" />

      <KpiCard title="平均支付响应" :value="kpiData.averagePaymentTime" :trend="kpiData.avgTimeTrend" :trend-color="getTrendColor(kpiData.avgTimeTrend)" icon="⏱" />

    </section>

    

    <!-- 图表区域 -->

    <section class="charts-area">

      <!-- 支付决策漏斗图 -->

      <div class="chart-wrapper funnel-chart-wrapper">

        <FunnelChart :date-range="currentDateRange" /> 

      </div>

      

      <!-- 明星商品价值环图 (Top 5) -->

      <div class="chart-wrapper">

        <Top5PieChart :date-range="currentDateRange" /> 

      </div>

    </section>

    

    <!-- 订单价值漏损分析 (退款) - 铺满整排 -->

    <section class="leakage-section">

      <div class="chart-wrapper leakage-card-wrapper full-width">

        <LeakageCard :date-range="currentDateRange" />

      </div>

    </section>

  </div>

</template>



<script>
import KpiCard from './KpiCard/index.vue';
import FunnelChart from './FunnelChart/index.vue';
import Top5PieChart from './Top5PieChart/index.vue';
import LeakageCard from './LeakageCard/index.vue';
import {
  getOrderAveragePaymentTime,
  getOrderGmv,
  getOrderMaxDate,
  getOrderRBig,
  getOrderTop5Percentage,
  uploadOrderValueFiles
} from '@/api/order';



export default {

  name: 'OrderValue',

  components: {

    KpiCard,

    FunnelChart,

    Top5PieChart,

    LeakageCard

  },

  data() {

    return {

      selectedDate: new Date().toISOString().split('T')[0],

      activeTab: '7d',

      maxDate: '',

      currentDateRange: { start: '', end: '' },

      uploadingOrder: false,

      kpiData: {

        gmv: '?0',

        gmvTrend: '+0%',

        p80Contribution: '0%',

        p80Trend: '+0%',

        top5Contribution: '0%',

        top5Trend: '+0%',

        averagePaymentTime: '00:00 秒',

        avgTimeTrend: '+0%'

      }

    };

  },

  watch: {

    selectedDate(newVal) {

      if (this.activeTab === '7d') {

        const startDate = this.formatYmd(this.addDays(newVal, -6));

        const endDate = this.formatYmd(newVal);

        this.currentDateRange = { start: startDate, end: endDate };

        this.fetchAllApiData({ start: startDate, end: endDate });

      } else {

        this.selectDateRange(this.activeTab);

      }

    }

  },

  mounted() {

    this.initDashboard();

  },

  methods: {

    triggerOrderUpload() {

      if (this.uploadingOrder) {

        return;

      }

      if (this.$refs.orderUploadInput) {

        this.$refs.orderUploadInput.click();

      }

    },

    async handleOrderUploadChange(event) {

      const files = Array.from((event && event.target && event.target.files) || []);

      if (!files.length) {

        return;

      }

      const invalid = files.find(file => !String(file.name || '').toLowerCase().endsWith('.csv'));

      if (invalid) {

        this.$modal.msgError('仅支持上传CSV文件');

        event.target.value = '';

        return;

      }

      await this.uploadOrderFiles(files);

      event.target.value = '';

    },

    async uploadOrderFiles(files) {

      this.uploadingOrder = true;

      try {

        const formData = new FormData();

        files.forEach(file => formData.append('files', file));

        const data = await uploadOrderValueFiles(formData);

        if (data && data.success) {

          this.$modal.msgSuccess(data.message || '上传并导入成功');

          await this.initDashboard();

          return;

        }

        this.$modal.msgError((data && data.message) || '上传导入失败');

      } catch (error) {

        const msg = error && error.response && error.response.data && error.response.data.message

          ? error.response.data.message

          : (error && error.message ? error.message : '上传导入失败');

        this.$modal.msgError(msg);

      } finally {

        this.uploadingOrder = false;

      }

    },

    pad2(value) {

      return String(value).padStart(2, '0');

    },

    toDate(value) {

      if (value instanceof Date) return new Date(value.getTime());

      if (!value) return new Date();

      return new Date(`${value}T00:00:00`);

    },

    formatYmd(value) {

      const date = this.toDate(value);

      return `${date.getFullYear()}-${this.pad2(date.getMonth() + 1)}-${this.pad2(date.getDate())}`;

    },

    addDays(value, days) {

      const date = this.toDate(value);

      date.setDate(date.getDate() + days);

      return date;

    },

    addMonths(value, months) {

      const date = this.toDate(value);

      date.setMonth(date.getMonth() + months);

      return date;

    },

    startOfMonth(value) {

      const date = this.toDate(value);

      return new Date(date.getFullYear(), date.getMonth(), 1);

    },

    endOfMonth(value) {

      const date = this.toDate(value);

      return new Date(date.getFullYear(), date.getMonth() + 1, 0);

    },

    async fetchAllApiData(customRange = null) {

      try {

        let startDate;

        let endDate;

        if (customRange && customRange.start && customRange.end) {

          startDate = customRange.start;

          endDate = customRange.end;

        } else {

          endDate = this.selectedDate;

          startDate = this.formatYmd(this.addDays(endDate, -6));

        }



        const [gmvRes, p80Res, top5Res, avgTimeRes] = await Promise.all([

          getOrderGmv({ startDate, endDate }),

          getOrderRBig({ startDate, endDate }),

          getOrderTop5Percentage({ startDate, endDate }),

          getOrderAveragePaymentTime({ startDate, endDate })

        ]);



        const gmvValue = gmvRes || 0;

        this.kpiData.gmv = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(gmvValue);



        const p80Value = p80Res?.rBigRatio || 0;

        this.kpiData.p80Contribution = `${Math.round(p80Value)}%`;



        const top5Value = top5Res?.data?.top5Percentage || 0;

        this.kpiData.top5Contribution = `${Math.round(top5Value)}%`;



        const avgSeconds = avgTimeRes?.averagePaymentSeconds || 0;

        const minutes = Math.floor(avgSeconds / 60);

        const seconds = Math.round(avgSeconds % 60);

        this.kpiData.averagePaymentTime = `${this.pad2(minutes)}:${this.pad2(seconds)} 秒`;



        if (this.activeTab !== 'tm') {

          this.kpiData.gmvTrend = '+0%';

          this.kpiData.p80Trend = '+0%';

          this.kpiData.top5Trend = '+0%';

          this.kpiData.avgTimeTrend = '+0%';

        }

      } catch (error) {

        console.error('获取订单价值数据失败:', error);

      }

    },

    calculateTrend(currentValue, previousValue) {

      if (previousValue === 0) return '+0%';

      const change = ((currentValue - previousValue) / previousValue) * 100;

      const sign = change >= 0 ? '+' : '';

      return `${sign}${change.toFixed(1)}%`;

    },

    parseTimeString(timeString) {

      if (!timeString) return 0;

      const timeMatch = timeString.match(/(\d{2}):(\d{2})/);

      if (timeMatch) {

        const minutes = parseInt(timeMatch[1], 10);

        const seconds = parseInt(timeMatch[2], 10);

        return minutes * 60 + seconds;

      }

      return 0;

    },

    getTrendColor(trend) {

      if (!trend) return 'green';

      const value = parseFloat(trend);

      return value >= 0 ? 'green' : 'red';

    },

    async fetchPreviousMonthData(currentStart) {

      try {

        const currentStartDate = this.toDate(currentStart);

        const previousMonthBase = this.addMonths(currentStartDate, -1);

        const previousMonthStart = this.formatYmd(this.startOfMonth(previousMonthBase));

        const previousMonthEnd = this.formatYmd(this.endOfMonth(previousMonthBase));



        const [gmvRes, p80Res, top5Res, avgTimeRes] = await Promise.all([

          getOrderGmv({ startDate: previousMonthStart, endDate: previousMonthEnd }),

          getOrderRBig({ startDate: previousMonthStart, endDate: previousMonthEnd }),

          getOrderTop5Percentage({ startDate: previousMonthStart, endDate: previousMonthEnd }),

          getOrderAveragePaymentTime({ startDate: previousMonthStart, endDate: previousMonthEnd })

        ]);



        return {

          gmv: gmvRes || 0,

          p80: p80Res?.rBigRatio || 0,

          top5: top5Res?.data?.top5Percentage || 0,

          avgTime: avgTimeRes?.averagePaymentSeconds || 0

        };

      } catch (error) {

        console.error('获取订单价值数据失败:', error);

        return null;

      }

    },

    async initDashboard() {

      try {

        const res = await getOrderMaxDate();

        this.maxDate = res;

        this.selectedDate = res;



        const startDate = this.formatYmd(this.addDays(res, -6));

        this.currentDateRange = { start: startDate, end: res };

        await this.fetchAllApiData();

        this.kpiData.gmvTrend = '+0%';

        this.kpiData.p80Trend = '+0%';

        this.kpiData.top5Trend = '+0%';

        this.kpiData.avgTimeTrend = '+0%';

      } catch (error) {

        console.error('获取最大日期失败');

        const today = this.formatYmd(new Date());

        this.maxDate = today;

        this.selectedDate = today;

        const startDate = this.formatYmd(this.addDays(today, -6));

        this.currentDateRange = { start: startDate, end: today };

        await this.fetchAllApiData();

        this.kpiData.gmvTrend = '+0%';

        this.kpiData.p80Trend = '+0%';

        this.kpiData.top5Trend = '+0%';

        this.kpiData.avgTimeTrend = '+0%';

      }

    },

    async selectDateRange(type) {

      this.activeTab = type;

      const baseDate = this.toDate(this.selectedDate);

      let start;

      let end;



      if (type === '7d') {

        end = this.formatYmd(baseDate);

        start = this.formatYmd(this.addDays(baseDate, -6));

      } else if (type === 'tm') {

        start = this.formatYmd(this.startOfMonth(baseDate));

        end = this.formatYmd(this.endOfMonth(baseDate));

      } else if (type === 'lm') {

        const lastMonth = this.addMonths(baseDate, -1);

        start = this.formatYmd(this.startOfMonth(lastMonth));

        end = this.formatYmd(this.endOfMonth(lastMonth));

      } else if (type === 'all') {

        start = '2022-01-01';

        end = this.maxDate || this.formatYmd(baseDate);

      }



      this.currentDateRange = { start, end };



      if (type === 'tm') {

        await this.fetchAllApiData({ start, end });

        const previousMonthData = await this.fetchPreviousMonthData(start);

        if (previousMonthData) {

          const currentGmv = parseFloat(this.kpiData.gmv.replace(/[?,]/g, '')) || 0;

          const currentP80 = parseFloat(this.kpiData.p80Contribution.replace('%', '')) || 0;

          const currentTop5 = parseFloat(this.kpiData.top5Contribution.replace('%', '')) || 0;

          const currentAvgTime = this.parseTimeString(this.kpiData.averagePaymentTime) || 0;



          this.kpiData.gmvTrend = this.calculateTrend(currentGmv, previousMonthData.gmv);

          this.kpiData.p80Trend = this.calculateTrend(currentP80, previousMonthData.p80);

          this.kpiData.top5Trend = this.calculateTrend(currentTop5, previousMonthData.top5);

          this.kpiData.avgTimeTrend = this.calculateTrend(currentAvgTime, previousMonthData.avgTime);

        }

      } else {

        this.fetchAllApiData({ start, end });

      }

    },

    async fetchAllKpiData() {

      try {

        const [gmvRes, p80Res, top5Res, avgTimeRes] = await Promise.all([

          getOrderGmv(),

          getOrderRBig(),

          getOrderTop5Percentage(),

          getOrderAveragePaymentTime()

        ]);



        const gmvValue = gmvRes || 0;

        this.kpiData.gmv = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(gmvValue);

        const p80Value = p80Res?.rBigRatio || 0;

        this.kpiData.p80Contribution = `${Math.round(p80Value)}%`;

        const top5Value = top5Res?.data?.top5Percentage || 0;

        this.kpiData.top5Contribution = `${Math.round(top5Value)}%`;

        const avgSeconds = avgTimeRes?.averagePaymentSeconds || 0;

        const minutes = Math.floor(avgSeconds / 60);

        const seconds = Math.round(avgSeconds % 60);

        this.kpiData.averagePaymentTime = `${this.pad2(minutes)}:${this.pad2(seconds)} 秒`;

      } catch (error) {

        console.error('获取KPI数据失败:', error);

        this.kpiData.gmv = '?0';

        this.kpiData.p80Contribution = '0%';

        this.kpiData.top5Contribution = '0%';

        this.kpiData.averagePaymentTime = '00:00 秒';

      }

    }

  }

};

</script>





<style scoped>

.header-controls {

  display: flex;

  align-items: center;

  gap: 15px;

  margin-top: 10px;

}

.control-label {

  font-size: 14px;

  color: #666;

}

.control-item-group {

  display: flex;

  align-items: center;

  gap: 10px;

}

.date-input {

  padding: 4px 8px;

  border: 1px solid #ddd;

  border-radius: 4px;

  font-size: 14px;

}

.time-tab {

  padding: 5px 15px;

  border: 1px solid #ddd;

  background-color: #fff;

  border-radius: 4px;

  cursor: pointer;

  font-size: 14px;

  transition: all 0.2s;

}

.time-tab:hover {

  border-color: #188df0;

  color: #188df0;

}

.time-tab.active {

  background-color: #188df0;

  color: #fff;

  border-color: #188df0;

}

.upload-btn {

  padding: 5px 12px;

  border: 1px solid #188df0;

  background-color: #188df0;

  color: #fff;

  border-radius: 4px;

  cursor: pointer;

  font-size: 13px;

}

.upload-btn:disabled {

  opacity: 0.7;

  cursor: not-allowed;

}

/* 样式部分和之前一样，不用修改 */

.order-value-view {

  display: flex;

  flex-direction: column;

  gap: 20px;

}

.page-header {

  display: flex;

  justify-content: space-between;

  align-items: center;

  background-color: white;

  padding: 15px 20px;

  border-radius: 8px;

  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);

}

.page-title {

  font-size: 18px;

  font-weight: 600;

  color: #333;

  margin: 0;

}

.header-controls {

  display: flex;

  align-items: center;

  font-size: 14px;

}

.control-item {

  margin-left: 20px;

  color: #666;

}

.time-range-selector {

  display: flex;

  align-items: center;

}

.date-input {

  padding: 6px 10px;

  border: 1px solid #ddd;

  border-radius: 4px;

  margin-right: 10px;

}

.time-tab {

  background: white;

  border: 1px solid #ddd;

  padding: 8px 12px;

  margin-left: -1px;

  cursor: pointer;

  font-size: 12px;

  color: #555;

  transition: all 0.2s;

}

.time-tab:first-of-type {

  border-top-left-radius: 4px;

  border-bottom-left-radius: 4px;

}

.time-tab:last-of-type {

  border-top-right-radius: 4px;

  border-bottom-right-radius: 4px;

}

.time-tab.active {

  background-color: #3366CC;

  color: white;

  border-color: #3366CC;

  z-index: 1;

}

.kpi-cards-grid {

  display: grid;

  grid-template-columns: repeat(4, 1fr);

  gap: 20px;

}

.charts-area {

  display: flex;

  flex-direction: column;

  gap: 20px;

}

.leakage-section {

  display: flex;

}

.full-width {

  width: 100%;

}

.chart-wrapper {

  background-color: white;

  padding: 15px;

  border-radius: 8px;

  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);

}

</style>

