|
|
@@ -1,97 +1,1630 @@
|
|
|
<template>
|
|
|
- <div class="data-page">
|
|
|
- <div class="data-header">
|
|
|
- <div class="data-title">🌍 市场情报</div>
|
|
|
- <button class="btn btn-primary"><i class="fas fa-plus"></i> 添加监控</button>
|
|
|
+ <div class="market-page">
|
|
|
+ <!-- Header -->
|
|
|
+ <div class="market-header">
|
|
|
+ <div class="header-left">
|
|
|
+ <div class="data-title">🌍 市场情报</div>
|
|
|
+ <p class="header-desc">实时掌握市场动态,AI驱动的市场机会发现</p>
|
|
|
+ </div>
|
|
|
+ <div class="header-actions">
|
|
|
+ <button class="btn btn-secondary" @click="showMonitorModal = true">
|
|
|
+ <i class="fas fa-bell"></i> 监控设置
|
|
|
+ </button>
|
|
|
+ <button class="btn btn-secondary" @click="showTrendModal = true">
|
|
|
+ <i class="fas fa-chart-line"></i> 趋势分析
|
|
|
+ </button>
|
|
|
+ <button class="btn btn-secondary" @click="exportIntelligence">
|
|
|
+ <i class="fas fa-download"></i> 导出报告
|
|
|
+ </button>
|
|
|
+ <button class="btn btn-primary" @click="showAddMonitorModal = true">
|
|
|
+ <i class="fas fa-plus"></i> 添加监控
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Quick Stats -->
|
|
|
+ <div class="stats-row">
|
|
|
+ <div class="stat-card" @click="filterByType('market')">
|
|
|
+ <div class="stat-icon blue"><i class="fas fa-globe"></i></div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <h3>{{ activeMarkets }}</h3>
|
|
|
+ <p>活跃市场</p>
|
|
|
+ </div>
|
|
|
+ <div class="stat-trend up"><i class="fas fa-arrow-up"></i> 5个</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card" @click="filterByType('competitor')">
|
|
|
+ <div class="stat-icon purple"><i class="fas fa-chess"></i></div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <h3>{{ monitoredCompetitors }}</h3>
|
|
|
+ <p>竞品监控</p>
|
|
|
+ </div>
|
|
|
+ <div class="stat-badge">{{ newCompetitorNews }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card" @click="filterByType('opportunity')">
|
|
|
+ <div class="stat-icon green"><i class="fas fa-lightbulb"></i></div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <h3>{{ marketOpportunities.length }}</h3>
|
|
|
+ <p>市场机会</p>
|
|
|
+ </div>
|
|
|
+ <div class="stat-badge">{{ highPotentialCount }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card" @click="filterByType('risk')">
|
|
|
+ <div class="stat-icon red"><i class="fas fa-exclamation-triangle"></i></div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <h3>{{ marketRisks.length }}</h3>
|
|
|
+ <p>市场风险</p>
|
|
|
+ </div>
|
|
|
+ <div class="stat-badge danger">{{ criticalRisks }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card" @click="filterByType('trend')">
|
|
|
+ <div class="stat-icon cyan"><i class="fas fa-wave-square"></i></div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <h3>{{ trendingTopics.length }}</h3>
|
|
|
+ <p>热点话题</p>
|
|
|
+ </div>
|
|
|
+ <div class="stat-trend up"><i class="fas fa-fire"></i> 热</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Tabs & Filters -->
|
|
|
+ <div class="tabs-section">
|
|
|
+ <div class="tabs">
|
|
|
+ <div v-for="tab in tabs" :key="tab.key"
|
|
|
+ class="tab-item" :class="{ active: activeTab === tab.key }"
|
|
|
+ @click="activeTab = tab.key">
|
|
|
+ {{ tab.label }}
|
|
|
+ <span class="tab-count">{{ tab.count }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="filter-controls">
|
|
|
+ <input type="text" class="search-input" v-model="searchQuery" placeholder="搜索市场、竞品、话题...">
|
|
|
+ <select class="filter-select" v-model="filterRegion">
|
|
|
+ <option value="">全部地区</option>
|
|
|
+ <option value="middle-east">中东</option>
|
|
|
+ <option value="europe">欧洲</option>
|
|
|
+ <option value="asia">亚洲</option>
|
|
|
+ <option value="america">美洲</option>
|
|
|
+ </select>
|
|
|
+ <select class="filter-select" v-model="sortBy">
|
|
|
+ <option value="latest">最新</option>
|
|
|
+ <option value="hot">热度</option>
|
|
|
+ <option value="importance">重要性</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Content Sections -->
|
|
|
+ <div v-if="activeTab === 'overview'" class="content-section">
|
|
|
+ <!-- Market Overview Grid -->
|
|
|
+ <div class="overview-grid">
|
|
|
+ <!-- Hot Markets -->
|
|
|
+ <div class="card-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4><i class="fas fa-fire"></i> 热门市场 TOP 5</h4>
|
|
|
+ <button class="btn-link" @click="showMarketDetail">查看全部</button>
|
|
|
+ </div>
|
|
|
+ <div class="market-list">
|
|
|
+ <div v-for="(market, index) in hotMarkets" :key="index"
|
|
|
+ class="market-item" @click="openMarketDetail(market)">
|
|
|
+ <div class="rank-badge" :class="{ gold: index === 0, silver: index === 1, bronze: index === 2 }">
|
|
|
+ {{ index + 1 }}
|
|
|
+ </div>
|
|
|
+ <div class="market-info">
|
|
|
+ <div class="market-name">{{ market.name }}</div>
|
|
|
+ <div class="market-meta">
|
|
|
+ <span><i class="fas fa-chart-line"></i> {{ market.growth }}</span>
|
|
|
+ <span><i class="fas fa-users"></i> {{ market.population }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="market-score">
|
|
|
+ <div class="score-value">{{ market.score }}</div>
|
|
|
+ <div class="score-label">潜力指数</div>
|
|
|
+ </div>
|
|
|
+ <div class="market-indicators">
|
|
|
+ <span class="indicator" :class="market.trend">
|
|
|
+ <i :class="market.trend === 'up' ? 'fas fa-arrow-up' : 'fas fa-arrow-down'"></i>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Competitor News -->
|
|
|
+ <div class="card-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4><i class="fas fa-chess"></i> 竞品动态</h4>
|
|
|
+ <button class="btn-link" @click="showAllNews">查看全部</button>
|
|
|
+ </div>
|
|
|
+ <div class="news-list">
|
|
|
+ <div v-for="news in competitorNews.slice(0, 5)" :key="news.id"
|
|
|
+ class="news-item" :class="news.level"
|
|
|
+ @click="openCompetitorNewsDetail(news)">
|
|
|
+ <div class="news-header">
|
|
|
+ <span class="news-title">{{ news.title }}</span>
|
|
|
+ <span class="news-level" :class="news.level">{{ getLevelLabel(news.level) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="news-content">
|
|
|
+ <div class="news-company">
|
|
|
+ <i class="fas fa-building"></i> {{ news.company }}
|
|
|
+ </div>
|
|
|
+ <div class="news-description">{{ news.description }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="news-footer">
|
|
|
+ <span class="news-time">{{ news.time }}</span>
|
|
|
+ <span class="news-impact">影响: {{ news.impact }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Market Opportunities -->
|
|
|
+ <div class="opportunities-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4><i class="fas fa-lightbulb"></i> 市场机会</h4>
|
|
|
+ <div class="header-controls">
|
|
|
+ <select class="filter-select-sm" v-model="opportunityFilter">
|
|
|
+ <option value="">全部</option>
|
|
|
+ <option value="high">高潜力</option>
|
|
|
+ <option value="medium">中潜力</option>
|
|
|
+ <option value="low">低潜力</option>
|
|
|
+ </select>
|
|
|
+ <button class="btn-link" @click="showOpportunitiesDetail">查看详情</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="opportunities-grid">
|
|
|
+ <div v-for="opp in filteredOpportunities" :key="opp.id"
|
|
|
+ class="opportunity-card" :class="opp.potential"
|
|
|
+ @click="openOpportunityDetail(opp)">
|
|
|
+ <div class="opp-header">
|
|
|
+ <span class="opp-icon">{{ opp.icon }}</span>
|
|
|
+ <span class="opp-potential" :class="opp.potential">{{ opp.potential }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="opp-title">{{ opp.market }}</div>
|
|
|
+ <div class="opp-product">{{ opp.product }}</div>
|
|
|
+ <div class="opp-insight">{{ opp.insight }}</div>
|
|
|
+ <div class="opp-metrics">
|
|
|
+ <span class="metric">
|
|
|
+ <i class="fas fa-chart-bar"></i> 市场规模: {{ opp.marketSize }}
|
|
|
+ </span>
|
|
|
+ <span class="metric">
|
|
|
+ <i class="fas fa-users"></i> 竞争: {{ opp.competition }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="opp-footer">
|
|
|
+ <span class="opp-score">评分: {{ opp.score }}/100</span>
|
|
|
+ <button class="btn btn-sm btn-outline" @click.stop="trackOpportunity(opp)">
|
|
|
+ <i class="fas fa-star"></i> 跟踪
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Market Risks -->
|
|
|
+ <div class="risks-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4><i class="fas fa-exclamation-triangle"></i> 市场风险</h4>
|
|
|
+ <button class="btn-link" @click="showRisksDetail">查看全部</button>
|
|
|
+ </div>
|
|
|
+ <div class="risks-grid">
|
|
|
+ <div v-for="risk in marketRisks" :key="risk.id"
|
|
|
+ class="risk-card" :class="risk.level"
|
|
|
+ @click="openMarketRiskDetail(risk)">
|
|
|
+ <div class="risk-header">
|
|
|
+ <span class="risk-title">{{ risk.title }}</span>
|
|
|
+ <span class="risk-level" :class="risk.level">{{ risk.level }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="risk-description">{{ risk.description }}</div>
|
|
|
+ <div class="risk-impact">
|
|
|
+ <span class="impact-label">影响范围:</span>
|
|
|
+ <span class="impact-regions">{{ risk.affectedRegions }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="risk-action">
|
|
|
+ <button class="btn btn-sm btn-outline" @click.stop="openMarketRiskDetail(risk)">
|
|
|
+ <i class="fas fa-arrow-right"></i> 查看详情
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- Market Overview -->
|
|
|
- <div class="market-grid">
|
|
|
- <div class="market-card">
|
|
|
- <div class="market-card-header">
|
|
|
- <div class="market-card-title">🏆 热门市场 TOP 5</div>
|
|
|
+ <!-- Trending Topics -->
|
|
|
+ <div v-if="activeTab === 'trends'" class="content-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4><i class="fas fa-wave-square"></i> 热点话题</h4>
|
|
|
+ <button class="btn btn-secondary btn-sm" @click="refreshTrends">
|
|
|
+ <i class="fas fa-sync"></i> 刷新
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div class="trends-grid">
|
|
|
+ <div v-for="topic in trendingTopics" :key="topic.id"
|
|
|
+ class="trend-card" @click="openTrendDetail(topic)">
|
|
|
+ <div class="trend-header">
|
|
|
+ <span class="trend-tag">{{ topic.category }}</span>
|
|
|
+ <span class="trend-heat" :class="{ hot: topic.heat > 80, warm: topic.heat > 50 }">
|
|
|
+ <i class="fas fa-fire"></i> {{ topic.heat }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="trend-title">{{ topic.title }}</div>
|
|
|
+ <div class="trend-description">{{ topic.description }}</div>
|
|
|
+ <div class="trend-stats">
|
|
|
+ <span>提及: {{ topic.mentions }}</span>
|
|
|
+ <span>增长: {{ topic.growth }}%</span>
|
|
|
+ <span>相关产品: {{ topic.relatedProducts }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="trend-sources">
|
|
|
+ <span v-for="source in topic.sources" :key="source" class="source-tag">{{ source }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div style="padding: 12px;">
|
|
|
- <div v-for="(market, index) in hotMarkets" :key="index"
|
|
|
- style="display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--bg-hover); border-radius: 10px; margin-bottom: 8px;">
|
|
|
- <span :style="{ fontWeight: 700, width: '24px', color: index === 0 ? 'var(--success)' : index === 1 ? 'var(--primary)' : 'var(--accent)' }">{{ index + 1 }}</span>
|
|
|
- <div style="flex: 1;">
|
|
|
- <div style="font-weight: 600;">{{ market.name }}</div>
|
|
|
- <div style="font-size: 12px; color: var(--text-secondary);">{{ market.growth }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Monitored Items -->
|
|
|
+ <div v-if="activeTab === 'monitored'" class="content-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4><i class="fas fa-bell"></i> 监控项目</h4>
|
|
|
+ <button class="btn btn-secondary btn-sm" @click="showMonitorModal = true">
|
|
|
+ <i class="fas fa-cog"></i> 管理监控
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div class="monitored-list">
|
|
|
+ <div v-for="item in monitoredItems" :key="item.id"
|
|
|
+ class="monitored-item" :class="item.type">
|
|
|
+ <div class="item-header">
|
|
|
+ <span class="item-type" :class="item.type">{{ item.type }}</span>
|
|
|
+ <span class="item-name">{{ item.name }}</span>
|
|
|
+ <span class="item-status" :class="item.status">{{ item.status }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="item-content">
|
|
|
+ <div class="item-description">{{ item.description }}</div>
|
|
|
+ <div class="item-metrics">
|
|
|
+ <span>最后更新: {{ item.lastUpdate }}</span>
|
|
|
+ <span>更新频率: {{ item.frequency }}</span>
|
|
|
+ <span>通知: {{ item.notifications }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="item-actions">
|
|
|
+ <button class="btn btn-sm btn-outline" @click="editMonitor(item)">
|
|
|
+ <i class="fas fa-edit"></i> 编辑
|
|
|
+ </button>
|
|
|
+ <button class="btn btn-sm btn-outline" @click="removeMonitor(item)">
|
|
|
+ <i class="fas fa-trash"></i> 删除
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Intelligence Reports -->
|
|
|
+ <div v-if="activeTab === 'reports'" class="content-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4><i class="fas fa-file-alt"></i> 情报报告</h4>
|
|
|
+ <button class="btn btn-secondary btn-sm" @click="generateReport">
|
|
|
+ <i class="fas fa-plus"></i> 生成报告
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div class="reports-list">
|
|
|
+ <div v-for="report in intelligenceReports" :key="report.id"
|
|
|
+ class="report-item" @click="openReport(report)">
|
|
|
+ <div class="report-header">
|
|
|
+ <span class="report-title">{{ report.title }}</span>
|
|
|
+ <span class="report-date">{{ report.date }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="report-content">
|
|
|
+ <div class="report-summary">{{ report.summary }}</div>
|
|
|
+ <div class="report-stats">
|
|
|
+ <span><i class="fas fa-chart-bar"></i> 数据点: {{ report.dataPoints }}</span>
|
|
|
+ <span><i class="fas fa-globe"></i> 覆盖地区: {{ report.regions }}</span>
|
|
|
+ <span><i class="fas fa-chess"></i> 竞品: {{ report.competitors }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="report-actions">
|
|
|
+ <button class="btn btn-sm btn-outline" @click.stop="viewReport(report)">
|
|
|
+ <i class="fas fa-eye"></i> 查看
|
|
|
+ </button>
|
|
|
+ <button class="btn btn-sm btn-outline" @click.stop="downloadReport(report)">
|
|
|
+ <i class="fas fa-download"></i> 下载
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Add Monitor Modal -->
|
|
|
+ <div v-if="showAddMonitorModal" class="modal-overlay" @click.self="showAddMonitorModal = false">
|
|
|
+ <div class="modal add-monitor-modal">
|
|
|
+ <div class="modal-header">
|
|
|
+ <div class="modal-title"><i class="fas fa-plus-circle"></i> 添加监控</div>
|
|
|
+ <button class="modal-close" @click="showAddMonitorModal = false"><i class="fas fa-times"></i></button>
|
|
|
+ </div>
|
|
|
+ <div class="modal-body">
|
|
|
+ <div class="form-group">
|
|
|
+ <label class="form-label">监控类型 <span class="required">*</span></label>
|
|
|
+ <select class="form-select" v-model="newMonitor.type">
|
|
|
+ <option value="market">市场</option>
|
|
|
+ <option value="competitor">竞品</option>
|
|
|
+ <option value="product">产品</option>
|
|
|
+ <option value="keyword">关键词</option>
|
|
|
+ <option value="region">地区</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div class="form-group">
|
|
|
+ <label class="form-label">监控对象 <span class="required">*</span></label>
|
|
|
+ <input type="text" class="form-input" v-model="newMonitor.target" placeholder="输入市场名称、竞品名称等">
|
|
|
+ </div>
|
|
|
+ <div class="form-group">
|
|
|
+ <label class="form-label">监控指标</label>
|
|
|
+ <div class="checkbox-group">
|
|
|
+ <label class="checkbox-item">
|
|
|
+ <input type="checkbox" v-model="newMonitor.metrics" value="price">
|
|
|
+ <span class="checkmark"></span>
|
|
|
+ <span>价格变动</span>
|
|
|
+ </label>
|
|
|
+ <label class="checkbox-item">
|
|
|
+ <input type="checkbox" v-model="newMonitor.metrics" value="news">
|
|
|
+ <span class="checkmark"></span>
|
|
|
+ <span>新闻动态</span>
|
|
|
+ </label>
|
|
|
+ <label class="checkbox-item">
|
|
|
+ <input type="checkbox" v-model="newMonitor.metrics" value="product">
|
|
|
+ <span class="checkmark"></span>
|
|
|
+ <span>产品发布</span>
|
|
|
+ </label>
|
|
|
+ <label class="checkbox-item">
|
|
|
+ <input type="checkbox" v-model="newMonitor.metrics" value="trend">
|
|
|
+ <span class="checkmark"></span>
|
|
|
+ <span>趋势变化</span>
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="form-group">
|
|
|
+ <label class="form-label">通知频率</label>
|
|
|
+ <select class="form-select" v-model="newMonitor.frequency">
|
|
|
+ <option value="realtime">实时</option>
|
|
|
+ <option value="daily">每日</option>
|
|
|
+ <option value="weekly">每周</option>
|
|
|
+ <option value="monthly">每月</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div class="form-group">
|
|
|
+ <label class="form-label">通知方式</label>
|
|
|
+ <div class="checkbox-group">
|
|
|
+ <label class="checkbox-item">
|
|
|
+ <input type="checkbox" v-model="newMonitor.notifications" value="email">
|
|
|
+ <span class="checkmark"></span>
|
|
|
+ <span>邮件</span>
|
|
|
+ </label>
|
|
|
+ <label class="checkbox-item">
|
|
|
+ <input type="checkbox" v-model="newMonitor.notifications" value="wechat">
|
|
|
+ <span class="checkmark"></span>
|
|
|
+ <span>微信</span>
|
|
|
+ </label>
|
|
|
+ <label class="checkbox-item">
|
|
|
+ <input type="checkbox" v-model="newMonitor.notifications" value="system">
|
|
|
+ <span class="checkmark"></span>
|
|
|
+ <span>系统通知</span>
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="modal-footer">
|
|
|
+ <button class="btn btn-secondary" @click="showAddMonitorModal = false">取消</button>
|
|
|
+ <button class="btn btn-primary" @click="createMonitor">
|
|
|
+ <i class="fas fa-check"></i> 创建监控
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Monitor Settings Modal -->
|
|
|
+ <div v-if="showMonitorModal" class="modal-overlay" @click.self="showMonitorModal = false">
|
|
|
+ <div class="modal monitor-modal">
|
|
|
+ <div class="modal-header">
|
|
|
+ <div class="modal-title"><i class="fas fa-cog"></i> 监控设置</div>
|
|
|
+ <button class="modal-close" @click="showMonitorModal = false"><i class="fas fa-times"></i></button>
|
|
|
+ </div>
|
|
|
+ <div class="modal-body">
|
|
|
+ <div class="settings-section">
|
|
|
+ <h5>全局设置</h5>
|
|
|
+ <div class="setting-item">
|
|
|
+ <label class="setting-label">启用所有监控</label>
|
|
|
+ <input type="checkbox" class="toggle-switch" v-model="globalSettings.enableAll">
|
|
|
+ </div>
|
|
|
+ <div class="setting-item">
|
|
|
+ <label class="setting-label">通知提醒</label>
|
|
|
+ <select class="form-select" v-model="globalSettings.notificationLevel">
|
|
|
+ <option value="all">全部</option>
|
|
|
+ <option value="important">仅重要</option>
|
|
|
+ <option value="critical">仅紧急</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div class="setting-item">
|
|
|
+ <label class="setting-label">数据更新频率</label>
|
|
|
+ <select class="form-select" v-model="globalSettings.updateFrequency">
|
|
|
+ <option value="realtime">实时</option>
|
|
|
+ <option value="hourly">每小时</option>
|
|
|
+ <option value="daily">每日</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="settings-section">
|
|
|
+ <h5>监控项目 ({{ monitoredItems.length }})</h5>
|
|
|
+ <div class="monitored-summary">
|
|
|
+ <div v-for="item in monitoredItems" :key="item.id" class="summary-item">
|
|
|
+ <span class="item-name">{{ item.name }}</span>
|
|
|
+ <input type="checkbox" class="toggle-switch" v-model="item.enabled">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="modal-footer">
|
|
|
+ <button class="btn btn-secondary" @click="showMonitorModal = false">关闭</button>
|
|
|
+ <button class="btn btn-primary" @click="saveMonitorSettings">
|
|
|
+ <i class="fas fa-save"></i> 保存设置
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Trend Analysis Modal -->
|
|
|
+ <div v-if="showTrendModal" class="modal-overlay" @click.self="showTrendModal = false">
|
|
|
+ <div class="modal trend-modal">
|
|
|
+ <div class="modal-header">
|
|
|
+ <div class="modal-title"><i class="fas fa-chart-line"></i> 趋势分析</div>
|
|
|
+ <button class="modal-close" @click="showTrendModal = false"><i class="fas fa-times"></i></button>
|
|
|
+ </div>
|
|
|
+ <div class="modal-body">
|
|
|
+ <div class="trend-controls">
|
|
|
+ <select class="form-select" v-model="trendMetric">
|
|
|
+ <option value="market">市场规模</option>
|
|
|
+ <option value="growth">增长率</option>
|
|
|
+ <option value="competition">竞争强度</option>
|
|
|
+ <option value="demand">需求量</option>
|
|
|
+ </select>
|
|
|
+ <select class="form-select" v-model="trendPeriod">
|
|
|
+ <option value="3m">3个月</option>
|
|
|
+ <option value="6m">6个月</option>
|
|
|
+ <option value="1y">1年</option>
|
|
|
+ <option value="all">全部</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div class="trend-chart">
|
|
|
+ <div class="chart-placeholder">
|
|
|
+ <i class="fas fa-chart-line"></i>
|
|
|
+ <p>趋势图表加载中...</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="trend-insights">
|
|
|
+ <h5>趋势洞察</h5>
|
|
|
+ <div class="insight-item">
|
|
|
+ <span class="insight-label">最高增长:</span>
|
|
|
+ <span class="insight-value">中东市场 +35%</span>
|
|
|
+ </div>
|
|
|
+ <div class="insight-item">
|
|
|
+ <span class="insight-label">最快下降:</span>
|
|
|
+ <span class="insight-value">南美市场 -8%</span>
|
|
|
+ </div>
|
|
|
+ <div class="insight-item">
|
|
|
+ <span class="insight-label">最稳定:</span>
|
|
|
+ <span class="insight-value">欧洲市场 ±2%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="modal-footer">
|
|
|
+ <button class="btn btn-secondary" @click="showTrendModal = false">关闭</button>
|
|
|
+ <button class="btn btn-primary" @click="exportTrendReport">
|
|
|
+ <i class="fas fa-download"></i> 导出趋势报告
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Competitor News Detail Modal -->
|
|
|
+ <div v-if="showCompetitorDetailModal" class="modal-overlay" @click.self="showCompetitorDetailModal = false">
|
|
|
+ <div class="modal detail-modal">
|
|
|
+ <div class="modal-header">
|
|
|
+ <div class="modal-title">
|
|
|
+ <i class="fas fa-chess"></i> {{ selectedCompetitorDetail?.title }}
|
|
|
+ </div>
|
|
|
+ <button class="modal-close" @click="showCompetitorDetailModal = false"><i class="fas fa-times"></i></button>
|
|
|
+ </div>
|
|
|
+ <div class="modal-body" v-if="selectedCompetitorDetail">
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>基本信息</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">竞品公司</span>
|
|
|
+ <span class="value">{{ selectedCompetitorDetail.details.company }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">动态标题</span>
|
|
|
+ <span class="value">{{ selectedCompetitorDetail.details.title }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">发生时间</span>
|
|
|
+ <span class="value">{{ selectedCompetitorDetail.details.time }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">重要程度</span>
|
|
|
+ <span class="value" :class="selectedCompetitorDetail.details.level">{{ getLevelLabel(selectedCompetitorDetail.details.level) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">影响程度</span>
|
|
|
+ <span class="value">{{ selectedCompetitorDetail.details.impact }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">紧急程度</span>
|
|
|
+ <span class="value danger">{{ selectedCompetitorDetail.details.urgency }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>动态描述</h5>
|
|
|
+ <p class="reason-text">{{ selectedCompetitorDetail.details.description }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>风险分析</h5>
|
|
|
+ <p class="reason-text">{{ selectedCompetitorDetail.details.analysis }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>影响范围</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">影响市场</span>
|
|
|
+ <span class="value">{{ selectedCompetitorDetail.details.affectedMarkets }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">影响客户</span>
|
|
|
+ <span class="value">{{ selectedCompetitorDetail.details.affectedCustomers }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>我们的竞争优势</h5>
|
|
|
+ <div class="actions-list">
|
|
|
+ <div v-for="(advantage, index) in selectedCompetitorDetail.details.ourAdvantage" :key="index" class="action-item">
|
|
|
+ <i class="fas fa-check-circle"></i>
|
|
|
+ <span>{{ advantage }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div style="text-align: right;">
|
|
|
- <div style="font-weight: 600; color: var(--success);">{{ market.score }}</div>
|
|
|
- <div style="font-size: 10px; color: var(--text-muted);">潜力指数</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>应对建议</h5>
|
|
|
+ <div class="suggestion-box">
|
|
|
+ <i class="fas fa-lightbulb"></i>
|
|
|
+ <p style="white-space: pre-line;">{{ selectedCompetitorDetail.details.recommendation }}</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div class="modal-footer">
|
|
|
+ <button class="btn btn-secondary" @click="showCompetitorDetailModal = false">关闭</button>
|
|
|
+ <button class="btn btn-primary" @click="showCompetitorDetailModal = false">
|
|
|
+ <i class="fas fa-check"></i> 已了解
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="market-card">
|
|
|
- <div class="market-card-header">
|
|
|
- <div class="market-card-title">📢 竞品动态</div>
|
|
|
+ <!-- Market Risk Detail Modal -->
|
|
|
+ <div v-if="showRiskDetailModal" class="modal-overlay" @click.self="showRiskDetailModal = false">
|
|
|
+ <div class="modal detail-modal">
|
|
|
+ <div class="modal-header">
|
|
|
+ <div class="modal-title">
|
|
|
+ <i class="fas fa-exclamation-triangle"></i> {{ selectedRiskDetail?.title }}
|
|
|
+ </div>
|
|
|
+ <button class="modal-close" @click="showRiskDetailModal = false"><i class="fas fa-times"></i></button>
|
|
|
</div>
|
|
|
- <div style="padding: 12px;">
|
|
|
- <div v-for="(news, index) in competitorNews" :key="index"
|
|
|
- :style="{
|
|
|
- padding: '12px',
|
|
|
- background: 'var(--bg-hover)',
|
|
|
- borderRadius: '10px',
|
|
|
- marginBottom: '8px',
|
|
|
- borderLeft: '3px solid ' + (news.level === 'danger' ? 'var(--danger)' : 'var(--warning)')
|
|
|
- }">
|
|
|
- <div style="font-weight: 600; margin-bottom: 4px;">{{ news.title }}</div>
|
|
|
- <div style="font-size: 12px; color: var(--text-secondary);">{{ news.description }}</div>
|
|
|
- <div style="font-size: 11px; color: var(--text-muted); margin-top: 8px;">{{ news.time }}</div>
|
|
|
+ <div class="modal-body" v-if="selectedRiskDetail">
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>风险概况</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">风险标题</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.title }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">风险等级</span>
|
|
|
+ <span class="value danger">{{ selectedRiskDetail.details.level }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">发生概率</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.probability }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">影响程度</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.impact }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">影响范围</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.affectedRegions }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">预计损失</span>
|
|
|
+ <span class="value danger">{{ selectedRiskDetail.details.estimatedLoss }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>风险描述</h5>
|
|
|
+ <p class="reason-text">{{ selectedRiskDetail.details.description }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>根本原因</h5>
|
|
|
+ <p class="reason-text">{{ selectedRiskDetail.details.rootCause }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>影响产品</h5>
|
|
|
+ <div class="actions-list">
|
|
|
+ <div v-for="(product, index) in selectedRiskDetail.details.affectedProducts" :key="index" class="action-item">
|
|
|
+ <i class="fas fa-box"></i>
|
|
|
+ <span>{{ product }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>风险缓解措施</h5>
|
|
|
+ <div class="actions-list">
|
|
|
+ <div v-for="(measure, index) in selectedRiskDetail.details.mitigation" :key="index" class="action-item">
|
|
|
+ <i class="fas fa-shield-alt"></i>
|
|
|
+ <span>{{ measure }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>应急预案</h5>
|
|
|
+ <div class="suggestion-box">
|
|
|
+ <i class="fas fa-exclamation-circle"></i>
|
|
|
+ <p style="white-space: pre-line;">{{ selectedRiskDetail.details.contingencyPlan }}</p>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>监控信息</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">当前状态</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.status }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">下次评审</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.nextReview }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">影响客户数</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.affectedCustomers }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">预计周期</span>
|
|
|
+ <span class="value">{{ selectedRiskDetail.details.timeline }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="modal-footer">
|
|
|
+ <button class="btn btn-secondary" @click="showRiskDetailModal = false">关闭</button>
|
|
|
+ <button class="btn btn-primary" @click="showRiskDetailModal = false">
|
|
|
+ <i class="fas fa-check"></i> 已知悉
|
|
|
+ </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div v-if="showDetailModal" class="modal-overlay" @click.self="showDetailModal = false">
|
|
|
+ <div class="modal detail-modal">
|
|
|
+ <div class="modal-header">
|
|
|
+ <div class="modal-title">
|
|
|
+ <i class="fas fa-info-circle"></i> {{ selectedDetail?.title }}
|
|
|
+ </div>
|
|
|
+ <button class="modal-close" @click="showDetailModal = false"><i class="fas fa-times"></i></button>
|
|
|
+ </div>
|
|
|
+ <div class="modal-body" v-if="selectedDetail">
|
|
|
+ <!-- Market Detail -->
|
|
|
+ <div v-if="selectedDetail.type === 'market'" class="detail-content">
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>市场概况</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">市场名称</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.name }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">预计增长</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.growth }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">人口规模</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.population }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">潜力指数</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.score }}/100</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">市场规模</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.marketSize }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">GDP增长</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.gdpGrowth }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- Market Opportunities -->
|
|
|
- <div style="margin-top: 24px;">
|
|
|
- <h3 style="font-size: 16px; margin-bottom: 16px;">🎯 市场机会</h3>
|
|
|
- <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;">
|
|
|
- <div v-for="opp in marketOpportunities" :key="opp.id"
|
|
|
- class="smart-card" style="cursor: pointer;">
|
|
|
- <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 12px;">
|
|
|
- <div style="font-size: 28px;">{{ opp.icon }}</div>
|
|
|
- <div>
|
|
|
- <div style="font-weight: 600;">{{ opp.market }}</div>
|
|
|
- <div style="font-size: 11px; color: var(--text-muted);">{{ opp.product }}</div>
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>主要产业</h5>
|
|
|
+ <p class="reason-text">{{ selectedDetail.details.mainIndustries }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>关键参与者</h5>
|
|
|
+ <p class="reason-text">{{ selectedDetail.details.keyPlayers }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>市场机会</h5>
|
|
|
+ <div class="actions-list">
|
|
|
+ <div v-for="(opp, index) in selectedDetail.details.opportunities" :key="index" class="action-item">
|
|
|
+ <i class="fas fa-star"></i>
|
|
|
+ <span>{{ opp }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>市场风险</h5>
|
|
|
+ <div class="actions-list">
|
|
|
+ <div v-for="(risk, index) in selectedDetail.details.risks" :key="index" class="action-item danger">
|
|
|
+ <i class="fas fa-exclamation-circle"></i>
|
|
|
+ <span>{{ risk }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>建议</h5>
|
|
|
+ <div class="suggestion-box">
|
|
|
+ <i class="fas fa-lightbulb"></i>
|
|
|
+ <p>{{ selectedDetail.details.recommendation }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Opportunity Detail -->
|
|
|
+ <div v-if="selectedDetail.type === 'opportunity'" class="detail-content">
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>机会概况</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">市场</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.market }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">产品</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.product }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">潜力等级</span>
|
|
|
+ <span class="value" :class="selectedDetail.details.potential">{{ selectedDetail.details.potential }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">评分</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.score }}/100</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">市场规模</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.marketSize }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">竞争强度</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.competition }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>市场洞察</h5>
|
|
|
+ <p class="reason-text">{{ selectedDetail.details.insight }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>进入策略</h5>
|
|
|
+ <div class="suggestion-box">
|
|
|
+ <i class="fas fa-map-marker-alt"></i>
|
|
|
+ <p style="white-space: pre-line;">{{ selectedDetail.details.entryStrategy }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>投资信息</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">市场增长</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.marketGrowth }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">目标客户</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.targetCustomers }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">进入周期</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.timeline }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">投资需求</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.investmentRequired }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">预期ROI</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.expectedROI }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 12px;">{{ opp.insight }}</div>
|
|
|
- <div style="display: flex; justify-content: space-between; font-size: 11px;">
|
|
|
- <span style="color: var(--success);">{{ opp.potential }}</span>
|
|
|
- <span style="color: var(--text-muted);">{{ opp.competition }}竞争</span>
|
|
|
+
|
|
|
+ <!-- Trend Detail -->
|
|
|
+ <div v-if="selectedDetail.type === 'trend'" class="detail-content">
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>话题概况</h5>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">话题</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.title }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">分类</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.category }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">热度</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.heat }}/100</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">提及数</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.mentions }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">增长率</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.growth }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">相关产品</span>
|
|
|
+ <span class="value">{{ selectedDetail.details.relatedProducts }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>话题描述</h5>
|
|
|
+ <p class="reason-text">{{ selectedDetail.details.description }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>关键洞察</h5>
|
|
|
+ <div class="actions-list">
|
|
|
+ <div v-for="(insight, index) in selectedDetail.details.keyInsights" :key="index" class="action-item">
|
|
|
+ <i class="fas fa-lightbulb"></i>
|
|
|
+ <span>{{ insight }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>业务影响</h5>
|
|
|
+ <div class="suggestion-box">
|
|
|
+ <i class="fas fa-chart-line"></i>
|
|
|
+ <p>{{ selectedDetail.details.businessImplication }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>建议行动</h5>
|
|
|
+ <div class="actions-list">
|
|
|
+ <div v-for="(action, index) in selectedDetail.details.actionItems" :key="index" class="action-item">
|
|
|
+ <i class="fas fa-check-circle"></i>
|
|
|
+ <span>{{ action }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <h5>信息来源</h5>
|
|
|
+ <div class="sources-list">
|
|
|
+ <span v-for="source in selectedDetail.details.sources" :key="source" class="source-tag">{{ source }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div class="modal-footer">
|
|
|
+ <button class="btn btn-secondary" @click="showDetailModal = false">关闭</button>
|
|
|
+ <button class="btn btn-primary" @click="trackOpportunity(selectedDetail.data)" v-if="selectedDetail?.type === 'opportunity'">
|
|
|
+ <i class="fas fa-star"></i> 跟踪机会
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <transition name="toast">
|
|
|
+ <div v-if="showToast" class="toast" :class="toastType">
|
|
|
+ <i :class="toastType === 'success' ? 'fas fa-check-circle' : 'fas fa-info-circle'"></i>
|
|
|
+ {{ toastMessage }}
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref } from 'vue'
|
|
|
+import { ref, computed } from 'vue'
|
|
|
+
|
|
|
+const activeTab = ref('overview')
|
|
|
+const searchQuery = ref('')
|
|
|
+const filterRegion = ref('')
|
|
|
+const sortBy = ref('latest')
|
|
|
+const opportunityFilter = ref('')
|
|
|
+const showAddMonitorModal = ref(false)
|
|
|
+const showMonitorModal = ref(false)
|
|
|
+const showTrendModal = ref(false)
|
|
|
+const showToast = ref(false)
|
|
|
+const toastMessage = ref('')
|
|
|
+const toastType = ref('success')
|
|
|
+const showDetailModal = ref(false)
|
|
|
+const selectedDetail = ref(null)
|
|
|
+const showCompetitorDetailModal = ref(false)
|
|
|
+const selectedCompetitorDetail = ref(null)
|
|
|
+const showRiskDetailModal = ref(false)
|
|
|
+const selectedRiskDetail = ref(null)
|
|
|
+
|
|
|
+// Trend modal
|
|
|
+const trendMetric = ref('market')
|
|
|
+const trendPeriod = ref('6m')
|
|
|
+
|
|
|
+// New monitor form
|
|
|
+const newMonitor = ref({
|
|
|
+ type: 'market',
|
|
|
+ target: '',
|
|
|
+ metrics: [],
|
|
|
+ frequency: 'daily',
|
|
|
+ notifications: []
|
|
|
+})
|
|
|
|
|
|
+// Global settings
|
|
|
+const globalSettings = ref({
|
|
|
+ enableAll: true,
|
|
|
+ notificationLevel: 'important',
|
|
|
+ updateFrequency: 'daily'
|
|
|
+})
|
|
|
+
|
|
|
+// Tabs
|
|
|
+const tabs = [
|
|
|
+ { key: 'overview', label: '概览', count: 0 },
|
|
|
+ { key: 'trends', label: '热点话题', count: 12 },
|
|
|
+ { key: 'monitored', label: '监控项目', count: 8 },
|
|
|
+ { key: 'reports', label: '情报报告', count: 5 }
|
|
|
+]
|
|
|
+
|
|
|
+// Stats
|
|
|
+const activeMarkets = 5
|
|
|
+const monitoredCompetitors = 12
|
|
|
+const newCompetitorNews = 3
|
|
|
+const marketOpportunities = ref([])
|
|
|
+const highPotentialCount = 4
|
|
|
+const marketRisks = ref([])
|
|
|
+const criticalRisks = 1
|
|
|
+const trendingTopics = ref([])
|
|
|
+
|
|
|
+// Hot Markets
|
|
|
const hotMarkets = ref([
|
|
|
- { name: '中东地区', growth: '预计增长35%', score: 95 },
|
|
|
- { name: '东南亚', growth: '预计增长28%', score: 88 },
|
|
|
- { name: '北美市场', growth: '预计增长18%', score: 82 },
|
|
|
- { name: '欧洲', growth: '预计增长12%', score: 75 },
|
|
|
- { name: '南美', growth: '预计增长8%', score: 68 }
|
|
|
+ { id: 1, name: '中东地区', growth: '预计增长35%', population: '4.2亿', score: 95, trend: 'up', region: 'middle-east' },
|
|
|
+ { id: 2, name: '东南亚', growth: '预计增长28%', population: '6.8亿', score: 88, trend: 'up', region: 'asia' },
|
|
|
+ { id: 3, name: '北美市场', growth: '预计增长18%', population: '5.7亿', score: 82, trend: 'up', region: 'america' },
|
|
|
+ { id: 4, name: '欧洲', growth: '预计增长12%', population: '7.4亿', score: 75, trend: 'flat', region: 'europe' },
|
|
|
+ { id: 5, name: '南美', growth: '预计增长8%', population: '4.3亿', score: 68, trend: 'down', region: 'america' }
|
|
|
])
|
|
|
|
|
|
+// Competitor News
|
|
|
const competitorNews = ref([
|
|
|
- { title: '三星发布新品', description: '三星发布新一代990 Pro SSD,建议更新话术', time: '2小时前', level: 'danger' },
|
|
|
- { title: '西部数据降价', description: '西部数据企业级SSD降价10%,需关注', time: '昨天', level: 'warning' }
|
|
|
+ { id: 1, title: '三星发布新品', company: '三星', description: '三星发布新一代990 Pro SSD,性能提升40%,建议更新话术', time: '2小时前', level: 'danger', impact: '高' },
|
|
|
+ { id: 2, title: '西部数据降价', company: '西部数据', description: '西部数据企业级SSD降价10%,需关注竞争压力', time: '昨天', level: 'warning', impact: '中' },
|
|
|
+ { id: 3, title: '英特尔新产品线', company: '英特尔', description: '英特尔推出新的数据中心存储解决方案', time: '3天前', level: 'info', impact: '中' },
|
|
|
+ { id: 4, title: '美光收购案', company: '美光', description: '美光收购存储技术公司,扩大产品线', time: '1周前', level: 'info', impact: '低' },
|
|
|
+ { id: 5, title: 'SK海力士投资', company: 'SK海力士', description: 'SK海力士在韩国投资新工厂,产能提升30%', time: '1周前', level: 'warning', impact: '中' }
|
|
|
])
|
|
|
|
|
|
-const marketOpportunities = ref([
|
|
|
- { id: 1, icon: '🇸🇦', market: '沙特阿拉伯', product: '企业级SSD', insight: '政府数字化采购增加', potential: '高潜力', competition: '低' },
|
|
|
- { id: 2, icon: '🇦🇪', market: '阿联酋', product: '数据中心存储', insight: '迪拜数据中心扩建', potential: '高潜力', competition: '中' },
|
|
|
- { id: 3, icon: '🇸🇬', market: '新加坡', product: '云存储方案', insight: '东南亚云计算中心', potential: '中潜力', competition: '高' },
|
|
|
- { id: 4, icon: '🇩🇪', market: '德国', product: '工业级存储', insight: '工业4.0需求增长', potential: '中潜力', competition: '高' }
|
|
|
+// Market Opportunities
|
|
|
+marketOpportunities.value = [
|
|
|
+ { id: 1, icon: '🇸🇦', market: '沙特阿拉伯', product: '企业级SSD', insight: '政府数字化采购增加,Vision 2030计划推动', potential: 'high', marketSize: '¥2.5B', competition: '低', score: 92 },
|
|
|
+ { id: 2, icon: '🇦🇪', market: '阿联酋', product: '数据中心存储', insight: '迪拜数据中心扩建,云计算需求增长', potential: 'high', marketSize: '¥1.8B', competition: '中', score: 88 },
|
|
|
+ { id: 3, icon: '🇸🇬', market: '新加坡', product: '云存储方案', insight: '东南亚云计算中心,区域枢纽地位', potential: 'medium', marketSize: '¥1.2B', competition: '高', score: 75 },
|
|
|
+ { id: 4, icon: '🇩🇪', market: '德国', product: '工业级存储', insight: '工业4.0需求增长,制造业升级', potential: 'medium', marketSize: '¥2.1B', competition: '高', score: 72 },
|
|
|
+ { id: 5, icon: '🇮🇳', market: '印度', product: '消费级SSD', insight: '互联网用户增长,数据中心建设加速', potential: 'high', marketSize: '¥3.2B', competition: '中', score: 85 },
|
|
|
+ { id: 6, icon: '🇧🇷', market: '巴西', product: '企业存储', insight: '拉美最大经济体,数字化转型机会', potential: 'medium', marketSize: '¥0.9B', competition: '低', score: 68 }
|
|
|
+]
|
|
|
+
|
|
|
+// Market Risks
|
|
|
+marketRisks.value = [
|
|
|
+ { id: 1, title: '芯片供应链风险', level: 'critical', description: '全球芯片短缺可能持续,影响产品交期', affectedRegions: '全球' },
|
|
|
+ { id: 2, title: '贸易政策变化', level: 'high', description: '美国对华芯片出口管制升级,可能影响供应链', affectedRegions: '中东、亚洲' },
|
|
|
+ { id: 3, title: '汇率波动', level: 'medium', description: '美元升值可能增加采购成本', affectedRegions: '新兴市场' },
|
|
|
+ { id: 4, title: '竞争加剧', level: 'medium', description: '新进入者增加,价格竞争压力上升', affectedRegions: '全球' }
|
|
|
+]
|
|
|
+
|
|
|
+// Trending Topics
|
|
|
+trendingTopics.value = [
|
|
|
+ { id: 1, category: '技术趋势', title: 'AI芯片需求爆发', description: '生成式AI应用推动高性能芯片需求', heat: 95, mentions: 2847, growth: 45, relatedProducts: 'GPU、高端SSD', sources: ['TechCrunch', '36氪', 'VentureBeat'] },
|
|
|
+ { id: 2, category: '市场动态', title: '云计算市场扩张', description: '企业上云加速,数据中心投资增加', heat: 88, mentions: 2156, growth: 32, relatedProducts: '企业级存储', sources: ['Gartner', 'IDC', '云计算周刊'] },
|
|
|
+ { id: 3, category: '政策导向', title: '数据安全法规', description: '各国加强数据隐私保护法规', heat: 82, mentions: 1923, growth: 28, relatedProducts: '安全存储', sources: ['政策新闻', '法律周刊'] },
|
|
|
+ { id: 4, category: '产业动向', title: '绿色存储', description: '低功耗、环保存储产品受关注', heat: 76, mentions: 1654, growth: 22, relatedProducts: '节能SSD', sources: ['ESG报告', '环保新闻'] },
|
|
|
+ { id: 5, category: '竞争格局', title: '存储芯片整合', description: '大型厂商并购小型创新公司', heat: 71, mentions: 1432, growth: 18, relatedProducts: '全线产品', sources: ['并购新闻', '行业分析'] },
|
|
|
+ { id: 6, category: '应用创新', title: '边缘计算存储', description: '边缘计算场景对存储的新需求', heat: 68, mentions: 1289, growth: 15, relatedProducts: '边缘存储', sources: ['技术博客', '行业论坛'] }
|
|
|
+]
|
|
|
+
|
|
|
+// Monitored Items
|
|
|
+const monitoredItems = ref([
|
|
|
+ { id: 1, type: 'competitor', name: '三星电子', description: '监控三星SSD产品线动态和价格变化', lastUpdate: '2小时前', frequency: '实时', notifications: 3, status: 'active', enabled: true },
|
|
|
+ { id: 2, type: 'market', name: '中东市场', description: '监控中东地区市场规模和增长趋势', lastUpdate: '1天前', frequency: '每日', notifications: 1, status: 'active', enabled: true },
|
|
|
+ { id: 3, type: 'product', name: 'NVMe SSD', description: '监控NVMe SSD产品类别的市场动态', lastUpdate: '3小时前', frequency: '实时', notifications: 5, status: 'active', enabled: true },
|
|
|
+ { id: 4, type: 'keyword', name: '数据中心', description: '监控"数据中心"相关新闻和趋势', lastUpdate: '2小时前', frequency: '每日', notifications: 2, status: 'active', enabled: true },
|
|
|
+ { id: 5, type: 'competitor', name: '西部数据', description: '监控西部数据产品和市场动向', lastUpdate: '1天前', frequency: '每日', notifications: 1, status: 'active', enabled: true },
|
|
|
+ { id: 6, type: 'region', name: '东南亚', description: '监控东南亚地区市场机会和风险', lastUpdate: '1天前', frequency: '每周', notifications: 0, status: 'active', enabled: true },
|
|
|
+ { id: 7, type: 'keyword', name: 'AI芯片', description: '监控AI芯片相关动态', lastUpdate: '2小时前', frequency: '实时', notifications: 8, status: 'active', enabled: true },
|
|
|
+ { id: 8, type: 'product', name: '企业级存储', description: '监控企业级存储产品市场', lastUpdate: '3小时前', frequency: '每日', notifications: 2, status: 'active', enabled: true }
|
|
|
+])
|
|
|
+
|
|
|
+// Intelligence Reports
|
|
|
+const intelligenceReports = ref([
|
|
|
+ { id: 1, title: '2026年Q1市场情报报告', date: '2026-03-20', summary: '全球存储市场持续增长,中东和东南亚成为新增长极', dataPoints: 247, regions: 12, competitors: 8 },
|
|
|
+ { id: 2, title: '竞品动态分析报告', date: '2026-03-15', summary: '三星、西部数据等主要竞品最新动向分析', dataPoints: 156, regions: 8, competitors: 5 },
|
|
|
+ { id: 3, title: '市场机会评估报告', date: '2026-03-10', summary: '识别出6个高潜力市场机会,评估进入策略', dataPoints: 189, regions: 6, competitors: 12 },
|
|
|
+ { id: 4, title: '风险预警报告', date: '2026-03-05', summary: '芯片供应链风险、贸易政策变化等关键风险分析', dataPoints: 98, regions: 15, competitors: 10 },
|
|
|
+ { id: 5, title: '趋势分析报告', date: '2026-02-28', summary: 'AI芯片、云计算、绿色存储等热点趋势深度分析', dataPoints: 312, regions: 20, competitors: 15 }
|
|
|
])
|
|
|
+
|
|
|
+// Computed
|
|
|
+const filteredOpportunities = computed(() => {
|
|
|
+ let result = marketOpportunities.value
|
|
|
+ if (opportunityFilter.value) {
|
|
|
+ result = result.filter(opp => opp.potential === opportunityFilter.value)
|
|
|
+ }
|
|
|
+ return result
|
|
|
+})
|
|
|
+
|
|
|
+// Methods
|
|
|
+const getLevelLabel = (level) => {
|
|
|
+ const map = { danger: '紧急', warning: '重要', info: '信息' }
|
|
|
+ return map[level] || level
|
|
|
+}
|
|
|
+
|
|
|
+const filterByType = (type) => {
|
|
|
+ toast(`查看${type}详情`, 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const openMarketDetail = (market) => {
|
|
|
+ selectedDetail.value = {
|
|
|
+ type: 'market',
|
|
|
+ title: `${market.name} - 市场详情`,
|
|
|
+ data: market,
|
|
|
+ details: {
|
|
|
+ name: market.name,
|
|
|
+ growth: market.growth,
|
|
|
+ population: market.population,
|
|
|
+ score: market.score,
|
|
|
+ trend: market.trend,
|
|
|
+ marketSize: '¥2.5B',
|
|
|
+ gdpGrowth: '4.2%',
|
|
|
+ mainIndustries: '石油、金融、电信、零售',
|
|
|
+ keyPlayers: '沙特阿美、沙特电信、沙特银行',
|
|
|
+ opportunities: [
|
|
|
+ '政府数字化采购增加',
|
|
|
+ 'Vision 2030计划推动',
|
|
|
+ '数据中心建设加速',
|
|
|
+ '云计算需求增长'
|
|
|
+ ],
|
|
|
+ risks: [
|
|
|
+ '地缘政治风险',
|
|
|
+ '汇率波动',
|
|
|
+ '贸易政策变化'
|
|
|
+ ],
|
|
|
+ recommendation: '中东市场潜力巨大,建议加大投入。重点关注沙特和阿联酋市场。'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ showDetailModal.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const showMarketDetail = () => {
|
|
|
+ toast('查看全部热门市场', 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const showAllNews = () => {
|
|
|
+ toast('查看全部竞品动态', 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const showOpportunitiesDetail = () => {
|
|
|
+ toast('查看全部市场机会', 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const openOpportunityDetail = (opp) => {
|
|
|
+ selectedDetail.value = {
|
|
|
+ type: 'opportunity',
|
|
|
+ title: `${opp.market} - 市场机会详情`,
|
|
|
+ data: opp,
|
|
|
+ details: {
|
|
|
+ market: opp.market,
|
|
|
+ product: opp.product,
|
|
|
+ icon: opp.icon,
|
|
|
+ insight: opp.insight,
|
|
|
+ potential: opp.potential,
|
|
|
+ marketSize: opp.marketSize,
|
|
|
+ competition: opp.competition,
|
|
|
+ score: opp.score,
|
|
|
+ marketGrowth: '35%',
|
|
|
+ targetCustomers: '50-100',
|
|
|
+ entryStrategy: '1. 建立本地代理\n2. 参加行业展会\n3. 与当地企业合作\n4. 提供定制化解决方案',
|
|
|
+ timeline: '6-12个月',
|
|
|
+ investmentRequired: '¥500K-1M',
|
|
|
+ expectedROI: '200-300%'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ showDetailModal.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const trackOpportunity = (opp) => {
|
|
|
+ toast(`已跟踪${opp.market}市场机会`, 'success')
|
|
|
+}
|
|
|
+
|
|
|
+const showRisksDetail = () => {
|
|
|
+ toast('查看全部市场风险', 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const handleRisk = (risk) => {
|
|
|
+ toast(`查看${risk.title}的应对建议`, 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const openTrendDetail = (topic) => {
|
|
|
+ selectedDetail.value = {
|
|
|
+ type: 'trend',
|
|
|
+ title: `${topic.title} - 热点话题详情`,
|
|
|
+ data: topic,
|
|
|
+ details: {
|
|
|
+ title: topic.title,
|
|
|
+ category: topic.category,
|
|
|
+ heat: topic.heat,
|
|
|
+ mentions: topic.mentions,
|
|
|
+ growth: topic.growth,
|
|
|
+ relatedProducts: topic.relatedProducts,
|
|
|
+ sources: topic.sources,
|
|
|
+ description: topic.description,
|
|
|
+ keyInsights: [
|
|
|
+ '生成式AI应用推动高性能芯片需求',
|
|
|
+ '数据中心投资增加,存储需求旺盛',
|
|
|
+ '企业级应用对性能要求提高',
|
|
|
+ '新兴市场云计算普及加速'
|
|
|
+ ],
|
|
|
+ businessImplication: '我们的高端SSD产品将受益于这一趋势,建议加大营销投入。',
|
|
|
+ actionItems: [
|
|
|
+ '推出AI优化的SSD产品',
|
|
|
+ '加强与云计算厂商的合作',
|
|
|
+ '开发针对性的营销方案',
|
|
|
+ '建立技术领先的品牌形象'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ showDetailModal.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const refreshTrends = () => {
|
|
|
+ toast('正在刷新热点话题...', 'info')
|
|
|
+ setTimeout(() => {
|
|
|
+ toast('热点话题已更新', 'success')
|
|
|
+ }, 1500)
|
|
|
+}
|
|
|
+
|
|
|
+const editMonitor = (item) => {
|
|
|
+ toast(`编辑${item.name}监控`, 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const removeMonitor = (item) => {
|
|
|
+ toast(`已删除${item.name}监控`, 'success')
|
|
|
+}
|
|
|
+
|
|
|
+const createMonitor = () => {
|
|
|
+ showAddMonitorModal.value = false
|
|
|
+ toast('监控已创建', 'success')
|
|
|
+}
|
|
|
+
|
|
|
+const saveMonitorSettings = () => {
|
|
|
+ showMonitorModal.value = false
|
|
|
+ toast('监控设置已保存', 'success')
|
|
|
+}
|
|
|
+
|
|
|
+const exportTrendReport = () => {
|
|
|
+ toast('趋势报告导出中...', 'success')
|
|
|
+}
|
|
|
+
|
|
|
+const exportIntelligence = () => {
|
|
|
+ toast('情报报告导出中...', 'success')
|
|
|
+}
|
|
|
+
|
|
|
+const generateReport = () => {
|
|
|
+ toast('正在生成情报报告...', 'info')
|
|
|
+ setTimeout(() => {
|
|
|
+ toast('情报报告已生成', 'success')
|
|
|
+ }, 2000)
|
|
|
+}
|
|
|
+
|
|
|
+const openReport = (report) => {
|
|
|
+ toast(`查看${report.title}`, 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const viewReport = (report) => {
|
|
|
+ toast(`查看${report.title}`, 'info')
|
|
|
+}
|
|
|
+
|
|
|
+const downloadReport = (report) => {
|
|
|
+ toast(`${report.title}下载中...`, 'success')
|
|
|
+}
|
|
|
+
|
|
|
+const toast = (message, type = 'success') => {
|
|
|
+ toastMessage.value = message
|
|
|
+ toastType.value = type
|
|
|
+ showToast.value = true
|
|
|
+ setTimeout(() => { showToast.value = false }, 3000)
|
|
|
+}
|
|
|
+
|
|
|
+const openCompetitorNewsDetail = (news) => {
|
|
|
+ selectedCompetitorDetail.value = {
|
|
|
+ type: 'competitor-news',
|
|
|
+ title: `${news.title} - 竞品动态详情`,
|
|
|
+ data: news,
|
|
|
+ details: {
|
|
|
+ title: news.title,
|
|
|
+ company: news.company,
|
|
|
+ description: news.description,
|
|
|
+ time: news.time,
|
|
|
+ level: news.level,
|
|
|
+ impact: news.impact,
|
|
|
+ analysis: '竞品发布新产品,性能提升40%,价格下调15%。这可能对我们的市场份额造成3-5%的影响。',
|
|
|
+ ourAdvantage: [
|
|
|
+ '更好的客户服务',
|
|
|
+ '更灵活的定制方案',
|
|
|
+ '更强的技术支持',
|
|
|
+ '更稳定的供应链'
|
|
|
+ ],
|
|
|
+ recommendation: '1. 立即评估竞品产品\n2. 分析我们的竞争优势\n3. 制定应对策略\n4. 加强客户沟通',
|
|
|
+ affectedMarkets: '全球',
|
|
|
+ affectedCustomers: '50-100',
|
|
|
+ urgency: '高'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ showCompetitorDetailModal.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const openMarketRiskDetail = (risk) => {
|
|
|
+ selectedRiskDetail.value = {
|
|
|
+ type: 'market-risk',
|
|
|
+ title: `${risk.title} - 市场风险详情`,
|
|
|
+ data: risk,
|
|
|
+ details: {
|
|
|
+ title: risk.title,
|
|
|
+ description: risk.description,
|
|
|
+ affectedRegions: risk.affectedRegions,
|
|
|
+ level: risk.level,
|
|
|
+ probability: '高',
|
|
|
+ impact: '中等',
|
|
|
+ rootCause: risk.rootCause || '全球芯片短缺持续,主要供应商产能受限',
|
|
|
+ timeline: '3-6个月',
|
|
|
+ affectedProducts: [
|
|
|
+ 'NVMe SSD 2TB',
|
|
|
+ 'SATA SSD 2TB',
|
|
|
+ '企业级SSD 4TB'
|
|
|
+ ],
|
|
|
+ affectedCustomers: 45,
|
|
|
+ estimatedLoss: '¥2-5M',
|
|
|
+ mitigation: [
|
|
|
+ '开发备用供应商',
|
|
|
+ '增加库存储备',
|
|
|
+ '优化生产计划',
|
|
|
+ '与客户沟通交期调整',
|
|
|
+ '寻求替代方案'
|
|
|
+ ],
|
|
|
+ contingencyPlan: '1. 启动备用供应商\n2. 加班生产\n3. 优先供应重点客户\n4. 提供替代产品方案',
|
|
|
+ status: '监控中',
|
|
|
+ nextReview: '2026-03-30'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ showRiskDetailModal.value = true
|
|
|
+}
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.market-page { flex: 1; display: flex; flex-direction: column; padding: 24px; overflow-y: auto; background: var(--bg-dark); }
|
|
|
+.market-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; }
|
|
|
+.header-left { display: flex; flex-direction: column; gap: 4px; }
|
|
|
+.data-title { font-size: 20px; font-weight: 600; }
|
|
|
+.header-desc { font-size: 13px; color: var(--text-secondary); }
|
|
|
+.header-actions { display: flex; gap: 8px; }
|
|
|
+
|
|
|
+/* Stats Row */
|
|
|
+.stats-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; margin-bottom: 24px; }
|
|
|
+.stat-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 16px; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 12px; }
|
|
|
+.stat-card:hover { border-color: var(--primary); transform: translateY(-2px); }
|
|
|
+.stat-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 16px; }
|
|
|
+.stat-icon.blue { background: rgba(99, 102, 241, 0.2); color: #6366F1; }
|
|
|
+.stat-icon.purple { background: rgba(139, 92, 246, 0.2); color: #8B5CF6; }
|
|
|
+.stat-icon.green { background: rgba(16, 185, 129, 0.2); color: #10B981; }
|
|
|
+.stat-icon.red { background: rgba(239, 68, 68, 0.2); color: #EF4444; }
|
|
|
+.stat-icon.cyan { background: rgba(6, 182, 212, 0.2); color: #06B6D4; }
|
|
|
+.stat-content h3 { font-size: 18px; font-weight: 700; margin: 0; }
|
|
|
+.stat-content p { font-size: 11px; color: var(--text-muted); margin: 0; }
|
|
|
+.stat-trend { font-size: 11px; padding: 2px 8px; border-radius: 10px; background: rgba(16, 185, 129, 0.2); color: var(--success); display: flex; align-items: center; gap: 4px; }
|
|
|
+.stat-badge { font-size: 10px; padding: 2px 8px; border-radius: 10px; background: rgba(245, 158, 11, 0.2); color: var(--warning); }
|
|
|
+.stat-badge.danger { background: rgba(239, 68, 68, 0.2); color: #EF4444; }
|
|
|
+
|
|
|
+/* Tabs */
|
|
|
+.tabs-section { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 12px; border-bottom: 1px solid var(--border); }
|
|
|
+.tabs { display: flex; gap: 4px; }
|
|
|
+.tab-item { padding: 8px 16px; border-radius: 8px; cursor: pointer; font-size: 13px; color: var(--text-secondary); display: flex; align-items: center; gap: 6px; transition: all 0.2s; }
|
|
|
+.tab-item:hover { background: var(--bg-hover); }
|
|
|
+.tab-item.active { background: var(--bg-hover); color: var(--text-primary); font-weight: 600; }
|
|
|
+.tab-count { font-size: 11px; padding: 2px 6px; background: var(--bg-dark); border-radius: 10px; }
|
|
|
+.tab-item.active .tab-count { background: var(--primary); color: white; }
|
|
|
+.filter-controls { display: flex; gap: 8px; }
|
|
|
+.search-input { padding: 8px 12px; background: var(--bg-hover); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary); font-size: 13px; width: 200px; }
|
|
|
+.filter-select { padding: 8px 12px; background: var(--bg-hover); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary); font-size: 13px; }
|
|
|
+.filter-select-sm { padding: 6px 10px; background: var(--bg-hover); border: 1px solid var(--border); border-radius: 6px; color: var(--text-primary); font-size: 12px; }
|
|
|
+
|
|
|
+/* Content Section */
|
|
|
+.content-section { flex: 1; overflow-y: auto; }
|
|
|
+
|
|
|
+/* Overview Grid */
|
|
|
+.overview-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px; }
|
|
|
+.card-section { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 20px; }
|
|
|
+.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
|
|
|
+.section-header h4 { font-size: 14px; font-weight: 600; margin: 0; display: flex; align-items: center; gap: 8px; }
|
|
|
+.section-header h4 i { color: var(--primary); }
|
|
|
+.btn-link { background: none; border: none; color: var(--primary); font-size: 12px; cursor: pointer; }
|
|
|
+
|
|
|
+/* Market List */
|
|
|
+.market-list { display: flex; flex-direction: column; gap: 10px; }
|
|
|
+.market-item { display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--bg-hover); border-radius: 10px; cursor: pointer; transition: all 0.2s; }
|
|
|
+.market-item:hover { background: rgba(99, 102, 241, 0.1); }
|
|
|
+.rank-badge { width: 28px; height: 28px; border-radius: 50%; background: var(--bg-dark); display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; }
|
|
|
+.rank-badge.gold { background: rgba(245, 158, 11, 0.2); color: #F59E0B; }
|
|
|
+.rank-badge.silver { background: rgba(156, 163, 175, 0.2); color: #9CA3AF; }
|
|
|
+.rank-badge.bronze { background: rgba(217, 119, 6, 0.2); color: #D97706; }
|
|
|
+.market-info { flex: 1; min-width: 0; }
|
|
|
+.market-name { font-size: 13px; font-weight: 600; }
|
|
|
+.market-meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; display: flex; gap: 12px; }
|
|
|
+.market-score { text-align: center; }
|
|
|
+.score-value { font-size: 16px; font-weight: 700; }
|
|
|
+.score-label { font-size: 10px; color: var(--text-muted); }
|
|
|
+.market-indicators { }
|
|
|
+.indicator { font-size: 12px; }
|
|
|
+.indicator.up { color: var(--success); }
|
|
|
+.indicator.down { color: #EF4444; }
|
|
|
+
|
|
|
+/* News List */
|
|
|
+.news-list { display: flex; flex-direction: column; gap: 10px; }
|
|
|
+.news-item { padding: 12px; background: var(--bg-hover); border-radius: 10px; border-left: 3px solid var(--border); cursor: pointer; transition: all 0.2s; }
|
|
|
+.news-item:hover { background: rgba(99, 102, 241, 0.1); }
|
|
|
+.news-item.danger { border-left-color: #EF4444; }
|
|
|
+.news-item.warning { border-left-color: #F59E0B; }
|
|
|
+.news-item.info { border-left-color: #6366F1; }
|
|
|
+.news-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
|
|
|
+.news-title { font-size: 13px; font-weight: 600; }
|
|
|
+.news-level { font-size: 10px; padding: 2px 8px; border-radius: 10px; }
|
|
|
+.news-level.danger { background: rgba(239, 68, 68, 0.2); color: #EF4444; }
|
|
|
+.news-level.warning { background: rgba(245, 158, 11, 0.2); color: #F59E0B; }
|
|
|
+.news-level.info { background: rgba(99, 102, 241, 0.2); color: #6366F1; }
|
|
|
+.news-content { margin-bottom: 8px; }
|
|
|
+.news-company { font-size: 11px; color: var(--text-secondary); margin-bottom: 4px; }
|
|
|
+.news-description { font-size: 12px; color: var(--text-secondary); line-height: 1.4; }
|
|
|
+.news-footer { display: flex; justify-content: space-between; font-size: 10px; color: var(--text-muted); }
|
|
|
+
|
|
|
+/* Opportunities Section */
|
|
|
+.opportunities-section { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 24px; }
|
|
|
+.opportunities-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
|
|
|
+.opportunity-card { background: var(--bg-hover); border: 1px solid var(--border); border-radius: 12px; padding: 16px; cursor: pointer; transition: all 0.2s; }
|
|
|
+.opportunity-card:hover { border-color: var(--primary); transform: translateY(-2px); }
|
|
|
+.opportunity-card.high { border-left: 4px solid #10B981; }
|
|
|
+.opportunity-card.medium { border-left: 4px solid #F59E0B; }
|
|
|
+.opportunity-card.low { border-left: 4px solid #6366F1; }
|
|
|
+.opp-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
|
|
|
+.opp-icon { font-size: 24px; }
|
|
|
+.opp-potential { font-size: 10px; padding: 2px 8px; border-radius: 10px; }
|
|
|
+.opp-potential.high { background: rgba(16, 185, 129, 0.2); color: #10B981; }
|
|
|
+.opp-potential.medium { background: rgba(245, 158, 11, 0.2); color: #F59E0B; }
|
|
|
+.opp-potential.low { background: rgba(99, 102, 241, 0.2); color: #6366F1; }
|
|
|
+.opp-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
|
|
|
+.opp-product { font-size: 12px; color: var(--text-secondary); margin-bottom: 8px; }
|
|
|
+.opp-insight { font-size: 12px; color: var(--text-secondary); line-height: 1.4; margin-bottom: 10px; }
|
|
|
+.opp-metrics { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: var(--text-muted); margin-bottom: 10px; }
|
|
|
+.metric { display: flex; align-items: center; gap: 4px; }
|
|
|
+.opp-footer { display: flex; justify-content: space-between; align-items: center; }
|
|
|
+.opp-score { font-size: 12px; font-weight: 600; }
|
|
|
+
|
|
|
+/* Risks Section */
|
|
|
+.risks-section { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 24px; }
|
|
|
+.risks-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
|
|
+.risk-card { background: var(--bg-hover); border: 1px solid var(--border); border-radius: 12px; padding: 16px; border-left: 4px solid var(--border); }
|
|
|
+.risk-card.critical { border-left-color: #EF4444; }
|
|
|
+.risk-card.high { border-left-color: #F59E0B; }
|
|
|
+.risk-card.medium { border-left-color: #6366F1; }
|
|
|
+.risk-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
|
|
|
+.risk-title { font-size: 13px; font-weight: 600; }
|
|
|
+.risk-level { font-size: 10px; padding: 2px 8px; border-radius: 10px; }
|
|
|
+.risk-level.critical { background: rgba(239, 68, 68, 0.2); color: #EF4444; }
|
|
|
+.risk-level.high { background: rgba(245, 158, 11, 0.2); color: #F59E0B; }
|
|
|
+.risk-level.medium { background: rgba(99, 102, 241, 0.2); color: #6366F1; }
|
|
|
+.risk-description { font-size: 12px; color: var(--text-secondary); margin-bottom: 10px; }
|
|
|
+.risk-impact { font-size: 11px; color: var(--text-muted); margin-bottom: 10px; }
|
|
|
+.impact-label { font-weight: 600; }
|
|
|
+.impact-regions { color: var(--text-secondary); }
|
|
|
+.risk-action { }
|
|
|
+
|
|
|
+/* Trends Grid */
|
|
|
+.trends-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
|
|
+.trend-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 16px; cursor: pointer; transition: all 0.2s; }
|
|
|
+.trend-card:hover { border-color: var(--primary); transform: translateY(-2px); }
|
|
|
+.trend-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
|
|
|
+.trend-tag { font-size: 10px; padding: 2px 8px; background: var(--bg-hover); border-radius: 10px; color: var(--text-secondary); }
|
|
|
+.trend-heat { font-size: 11px; padding: 2px 8px; border-radius: 10px; display: flex; align-items: center; gap: 4px; }
|
|
|
+.trend-heat.hot { background: rgba(239, 68, 68, 0.2); color: #EF4444; }
|
|
|
+.trend-heat.warm { background: rgba(245, 158, 11, 0.2); color: #F59E0B; }
|
|
|
+.trend-title { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
|
|
|
+.trend-description { font-size: 12px; color: var(--text-secondary); margin-bottom: 10px; }
|
|
|
+.trend-stats { display: flex; gap: 12px; font-size: 11px; color: var(--text-muted); margin-bottom: 10px; }
|
|
|
+.trend-sources { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
|
+.source-tag { font-size: 10px; padding: 2px 8px; background: var(--bg-hover); border-radius: 10px; color: var(--text-secondary); }
|
|
|
+
|
|
|
+/* Monitored List */
|
|
|
+.monitored-list { display: flex; flex-direction: column; gap: 12px; }
|
|
|
+.monitored-item { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
|
|
|
+.monitored-item.competitor { border-left: 4px solid #8B5CF6; }
|
|
|
+.monitored-item.market { border-left: 4px solid #6366F1; }
|
|
|
+.monitored-item.product { border-left: 4px solid #10B981; }
|
|
|
+.monitored-item.keyword { border-left: 4px solid #F59E0B; }
|
|
|
+.monitored-item.region { border-left: 4px solid #06B6D4; }
|
|
|
+.item-header { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
|
|
|
+.item-type { font-size: 10px; padding: 2px 8px; border-radius: 10px; }
|
|
|
+.item-type.competitor { background: rgba(139, 92, 246, 0.2); color: #8B5CF6; }
|
|
|
+.item-type.market { background: rgba(99, 102, 241, 0.2); color: #6366F1; }
|
|
|
+.item-type.product { background: rgba(16, 185, 129, 0.2); color: #10B981; }
|
|
|
+.item-type.keyword { background: rgba(245, 158, 11, 0.2); color: #F59E0B; }
|
|
|
+.item-type.region { background: rgba(6, 182, 212, 0.2); color: #06B6D4; }
|
|
|
+.item-name { font-size: 13px; font-weight: 600; flex: 1; }
|
|
|
+.item-status { font-size: 10px; padding: 2px 8px; border-radius: 10px; background: rgba(16, 185, 129, 0.2); color: #10B981; }
|
|
|
+.item-content { margin-bottom: 10px; }
|
|
|
+.item-description { font-size: 12px; color: var(--text-secondary); margin-bottom: 6px; }
|
|
|
+.item-metrics { display: flex; gap: 16px; font-size: 11px; color: var(--text-muted); }
|
|
|
+.item-actions { display: flex; gap: 8px; }
|
|
|
+
|
|
|
+/* Reports List */
|
|
|
+.reports-list { display: flex; flex-direction: column; gap: 12px; }
|
|
|
+.report-item { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 16px; cursor: pointer; transition: all 0.2s; }
|
|
|
+.report-item:hover { border-color: var(--primary); }
|
|
|
+.report-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
|
|
|
+.report-title { font-size: 14px; font-weight: 600; }
|
|
|
+.report-date { font-size: 11px; color: var(--text-muted); }
|
|
|
+.report-content { margin-bottom: 10px; }
|
|
|
+.report-summary { font-size: 12px; color: var(--text-secondary); margin-bottom: 8px; }
|
|
|
+.report-stats { display: flex; gap: 16px; font-size: 11px; color: var(--text-muted); }
|
|
|
+.report-actions { display: flex; gap: 8px; }
|
|
|
+
|
|
|
+/* Modal */
|
|
|
+.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 20px; }
|
|
|
+.modal { background: var(--bg-card); border-radius: 16px; width: 100%; max-width: 600px; max-height: 90vh; overflow: hidden; display: flex; flex-direction: column; }
|
|
|
+.add-monitor-modal { max-width: 500px; }
|
|
|
+.monitor-modal { max-width: 600px; }
|
|
|
+.trend-modal { max-width: 700px; }
|
|
|
+.modal-header { display: flex; justify-content: space-between; align-items: center; padding: 20px; border-bottom: 1px solid var(--border); }
|
|
|
+.modal-title { font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
|
|
|
+.modal-close { width: 32px; height: 32px; background: var(--bg-hover); border: none; border-radius: 8px; color: var(--text-secondary); cursor: pointer; display: flex; align-items: center; justify-content: center; }
|
|
|
+.modal-body { flex: 1; overflow-y: auto; padding: 20px; }
|
|
|
+.modal-footer { display: flex; justify-content: flex-end; gap: 12px; padding: 20px; border-top: 1px solid var(--border); }
|
|
|
+
|
|
|
+/* Form */
|
|
|
+.form-group { margin-bottom: 16px; }
|
|
|
+.form-label { display: block; font-size: 12px; color: var(--text-secondary); margin-bottom: 6px; }
|
|
|
+.form-input, .form-select { width: 100%; padding: 10px 14px; background: var(--bg-dark); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary); font-size: 13px; }
|
|
|
+.form-input:focus, .form-select:focus { outline: none; border-color: var(--primary); }
|
|
|
+.required { color: var(--danger); }
|
|
|
+.checkbox-group { display: flex; flex-direction: column; gap: 8px; }
|
|
|
+.checkbox-item { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; }
|
|
|
+.checkbox-item input { width: 16px; height: 16px; }
|
|
|
+.checkmark { width: 16px; height: 16px; border: 1px solid var(--border); border-radius: 4px; display: flex; align-items: center; justify-content: center; }
|
|
|
+.checkbox-item input:checked ~ .checkmark { background: var(--primary); border-color: var(--primary); color: white; }
|
|
|
+
|
|
|
+/* Settings */
|
|
|
+.settings-section { margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--border); }
|
|
|
+.settings-section h5 { font-size: 13px; font-weight: 600; margin-bottom: 12px; }
|
|
|
+.setting-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
|
|
|
+.setting-label { font-size: 12px; }
|
|
|
+.toggle-switch { width: 40px; height: 24px; border-radius: 12px; background: var(--bg-hover); border: 1px solid var(--border); cursor: pointer; }
|
|
|
+.monitored-summary { display: flex; flex-direction: column; gap: 8px; }
|
|
|
+.summary-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; background: var(--bg-hover); border-radius: 8px; font-size: 12px; }
|
|
|
+
|
|
|
+/* Trend Controls */
|
|
|
+.trend-controls { display: flex; gap: 12px; margin-bottom: 20px; }
|
|
|
+.trend-chart { height: 200px; background: var(--bg-hover); border-radius: 10px; display: flex; align-items: center; justify-content: center; margin-bottom: 20px; }
|
|
|
+.chart-placeholder { text-align: center; color: var(--text-muted); }
|
|
|
+.chart-placeholder i { font-size: 32px; margin-bottom: 8px; }
|
|
|
+.trend-insights { }
|
|
|
+.trend-insights h5 { font-size: 13px; font-weight: 600; margin-bottom: 12px; }
|
|
|
+.insight-item { display: flex; justify-content: space-between; padding: 8px 12px; background: var(--bg-hover); border-radius: 8px; font-size: 12px; margin-bottom: 8px; }
|
|
|
+.insight-label { color: var(--text-muted); }
|
|
|
+.insight-value { font-weight: 600; }
|
|
|
+
|
|
|
+/* Detail Content */
|
|
|
+.detail-content { }
|
|
|
+.detail-section { margin-bottom: 24px; }
|
|
|
+.detail-section h5 { font-size: 14px; font-weight: 600; margin-bottom: 12px; }
|
|
|
+.info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
|
|
|
+.info-item { display: flex; flex-direction: column; gap: 4px; padding: 12px; background: var(--bg-hover); border-radius: 8px; }
|
|
|
+.info-item .label { font-size: 11px; color: var(--text-muted); }
|
|
|
+.info-item .value { font-size: 14px; font-weight: 600; }
|
|
|
+.info-item .value.high { color: #10B981; }
|
|
|
+.info-item .value.medium { color: #F59E0B; }
|
|
|
+.info-item .value.low { color: #6366F1; }
|
|
|
+.reason-text { font-size: 13px; color: var(--text-secondary); line-height: 1.6; padding: 12px; background: var(--bg-hover); border-radius: 8px; }
|
|
|
+.suggestion-box { display: flex; gap: 12px; padding: 14px; background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.2); border-radius: 10px; }
|
|
|
+.suggestion-box i { color: var(--success); font-size: 18px; flex-shrink: 0; }
|
|
|
+.suggestion-box p { font-size: 13px; color: var(--text-secondary); line-height: 1.6; margin: 0; }
|
|
|
+.actions-list { display: flex; flex-direction: column; gap: 8px; }
|
|
|
+.action-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: var(--bg-hover); border-radius: 8px; font-size: 13px; }
|
|
|
+.action-item i { color: var(--success); }
|
|
|
+.action-item.danger i { color: #EF4444; }
|
|
|
+.sources-list { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
|
+.source-tag { font-size: 11px; padding: 4px 10px; background: var(--bg-hover); border-radius: 10px; color: var(--text-secondary); }
|
|
|
+
|
|
|
+/* Toast */
|
|
|
+.toast { position: fixed; bottom: 24px; right: 24px; padding: 12px 20px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; font-size: 13px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 2000; }
|
|
|
+.toast.success { border-color: var(--success); }
|
|
|
+.toast.success i { color: var(--success); }
|
|
|
+.toast-enter-active, .toast-leave-active { transition: all 0.3s ease; }
|
|
|
+.toast-enter-from, .toast-leave-to { opacity: 0; transform: translateY(20px); }
|
|
|
+
|
|
|
+/* Detail Modal Styles */
|
|
|
+.detail-modal { max-width: 700px; }
|
|
|
+.detail-content { }
|
|
|
+.detail-section { margin-bottom: 24px; }
|
|
|
+.detail-section h5 { font-size: 14px; font-weight: 600; margin-bottom: 12px; }
|
|
|
+.info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
|
|
|
+.info-item { display: flex; flex-direction: column; gap: 4px; padding: 12px; background: var(--bg-hover); border-radius: 8px; }
|
|
|
+.info-item .label { font-size: 11px; color: var(--text-muted); }
|
|
|
+.info-item .value { font-size: 14px; font-weight: 600; }
|
|
|
+.info-item .value.high { color: #10B981; }
|
|
|
+.info-item .value.medium { color: #F59E0B; }
|
|
|
+.info-item .value.low { color: #6366F1; }
|
|
|
+.reason-text { font-size: 13px; color: var(--text-secondary); line-height: 1.6; padding: 12px; background: var(--bg-hover); border-radius: 8px; }
|
|
|
+.suggestion-box { display: flex; gap: 12px; padding: 14px; background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.2); border-radius: 10px; }
|
|
|
+.suggestion-box i { color: var(--success); font-size: 18px; flex-shrink: 0; }
|
|
|
+.suggestion-box p { font-size: 13px; color: var(--text-secondary); line-height: 1.6; margin: 0; }
|
|
|
+.actions-list { display: flex; flex-direction: column; gap: 8px; }
|
|
|
+.action-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: var(--bg-hover); border-radius: 8px; font-size: 13px; }
|
|
|
+.action-item i { color: var(--success); }
|
|
|
+.action-item.danger i { color: #EF4444; }
|
|
|
+.sources-list { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
|
+.source-tag { font-size: 11px; padding: 4px 10px; background: var(--bg-hover); border-radius: 10px; color: var(--text-secondary); }
|
|
|
+
|
|
|
+/* Responsive */
|
|
|
+@media (max-width: 1400px) {
|
|
|
+ .stats-row { grid-template-columns: repeat(3, 1fr); }
|
|
|
+ .overview-grid { grid-template-columns: 1fr; }
|
|
|
+ .opportunities-grid { grid-template-columns: repeat(2, 1fr); }
|
|
|
+ .trends-grid { grid-template-columns: 1fr; }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 992px) {
|
|
|
+ .stats-row { grid-template-columns: repeat(2, 1fr); }
|
|
|
+ .opportunities-grid { grid-template-columns: 1fr; }
|
|
|
+}
|
|
|
+</style>
|