diff --git a/dashboard/app.js b/dashboard/app.js
new file mode 100644
index 00000000..d673f333
--- /dev/null
+++ b/dashboard/app.js
@@ -0,0 +1,189 @@
+// HoloLake 数据统计面板 · 交互脚本 v2(含导出 + 环形图)
+
+window.addEventListener('load', function() {
+ // 条形图动画
+ setTimeout(function() {
+ var bars = document.querySelectorAll('.bar-fill');
+ bars.forEach(function(bar) {
+ var percent = bar.getAttribute('data-percent');
+ bar.style.width = percent + '%';
+ });
+ }, 300);
+
+ // 柱状图动画
+ setTimeout(function() {
+ var trendBars = document.querySelectorAll('.trend-bar');
+ trendBars.forEach(function(bar) {
+ var height = bar.getAttribute('data-height');
+ var fill = bar.querySelector('.trend-fill');
+ fill.style.height = height + '%';
+ });
+ }, 600);
+
+ // 环形图绘制函数
+ function drawDonut(percentages) {
+ var segments = document.querySelectorAll('.donut-segment');
+ var total = 377; // 2 * Math.PI * 60 ≈ 377
+ var cumulative = 0;
+ segments.forEach(function(seg, i) {
+ var value = percentages[i] || 0;
+ var dashArray = (value / 100 * total) + ' ' + total;
+ seg.setAttribute('stroke-dasharray', dashArray);
+ seg.setAttribute('stroke-dashoffset', 0);
+ });
+ }
+
+ // 当前选中的时间范围
+ var currentRange = 'today';
+
+ // 模拟数据
+ var mockData = {
+ today: {
+ users: '128', chats: '1,024', personas: '6', api: '3,892',
+ bars: [72,15,8,3,2],
+ trends: [45,62,38,78,55,90,85],
+ donut: [72,15,8,3,2],
+ apiTotal: '3,892'
+ },
+ week: {
+ users: '128', chats: '6,847', personas: '6', api: '24,103',
+ bars: [68,18,9,3,2],
+ trends: [320,415,380,450,290,520,480],
+ donut: [68,18,9,3,2],
+ apiTotal: '24,103'
+ },
+ month: {
+ users: '128', chats: '28,392', personas: '6', api: '98,741',
+ bars: [65,20,10,3,2],
+ trends: [1200,1450,1100,1680,1320,1890,1750],
+ donut: [65,20,10,3,2],
+ apiTotal: '98,741'
+ }
+ };
+
+ // 更新所有图表和数字
+ function updateStats(range) {
+ currentRange = range;
+ var data = mockData[range];
+
+ // 更新统计卡片
+ var values = document.querySelectorAll('.stat-value');
+ values[0].textContent = data.users;
+ values[1].textContent = data.chats;
+ values[2].textContent = data.personas;
+ values[3].textContent = data.api;
+
+ // 更新条形图
+ var bars = document.querySelectorAll('.bar-fill');
+ bars.forEach(function(bar, i) {
+ bar.style.width = '0%';
+ setTimeout(function() {
+ bar.style.width = data.bars[i] + '%';
+ }, 100);
+ });
+
+ // 更新百分比文字
+ var barValues = document.querySelectorAll('.bar-value');
+ data.bars.forEach(function(v, i) {
+ barValues[i].textContent = v + '%';
+ });
+
+ // 更新柱状图
+ var maxTrend = Math.max.apply(null, data.trends);
+ var trendBars = document.querySelectorAll('.trend-bar');
+ trendBars.forEach(function(bar, i) {
+ var fill = bar.querySelector('.trend-fill');
+ fill.style.height = '0%';
+ setTimeout(function() {
+ fill.style.height = Math.round(data.trends[i] / maxTrend * 90) + '%';
+ }, 100);
+ });
+
+ // 更新环形图
+ drawDonut(data.donut);
+ document.querySelector('.donut-center-value').textContent = data.apiTotal;
+
+ // 更新图例百分比
+ var legendPercents = document.querySelectorAll('.legend-percent');
+ data.donut.forEach(function(v, i) {
+ legendPercents[i].textContent = v + '%';
+ });
+ }
+
+ // 时间选择器切换
+ var timeButtons = document.querySelectorAll('.time-btn');
+ timeButtons.forEach(function(btn) {
+ btn.addEventListener('click', function() {
+ timeButtons.forEach(function(b) { b.classList.remove('active'); });
+ btn.classList.add('active');
+ var range = btn.getAttribute('data-range');
+ updateStats(range);
+ });
+ });
+
+ // 初始化环形图(今日数据)
+ drawDonut(mockData.today.donut);
+
+ // 卡片点击高亮
+ var cards = document.querySelectorAll('.stat-card');
+ cards.forEach(function(card) {
+ card.addEventListener('click', function() {
+ cards.forEach(function(c) { c.style.borderColor = 'rgba(255,255,255,0.06)'; });
+ card.style.borderColor = 'rgba(79, 195, 247, 0.5)';
+ });
+ });
+
+ // CSV 导出功能
+ var exportBtn = document.getElementById('exportBtn');
+ var toast = document.getElementById('exportToast');
+
+ exportBtn.addEventListener('click', function() {
+ var data = mockData[currentRange];
+ var models = ['DeepSeek', '通义千问', 'Kimi', '豆包', 'GPT/Claude'];
+ var days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
+
+ var csv = '\uFEFF'; // BOM for Chinese support
+ csv += 'HoloLake 数据统计报表\n';
+ csv += '时间范围,' + currentRange + '\n';
+ csv += '导出时间,' + new Date().toLocaleString('zh-CN') + '\n\n';
+
+ csv += '【统计概览】\n';
+ csv += '指标,数值,变化\n';
+ csv += '总用户,' + data.users + ',↑12%\n';
+ csv += '今日对话,' + data.chats + ',↑8%\n';
+ csv += '活跃人格体,' + data.personas + ',—\n';
+ csv += 'API调用,' + data.api + ',↓3%\n\n';
+
+ csv += '【模型调用分布】\n';
+ csv += '模型,占比\n';
+ models.forEach(function(m, i) {
+ csv += m + ',' + data.donut[i] + '%\n';
+ });
+ csv += '\n';
+
+ csv += '【每日对话趋势】\n';
+ csv += '日期,对话次数\n';
+ days.forEach(function(d, i) {
+ csv += d + ',' + data.trends[i] + '\n';
+ });
+
+ // 创建下载链接
+ var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
+ var link = document.createElement('a');
+ var url = URL.createObjectURL(blob);
+ link.href = url;
+ link.setAttribute('download', 'hololake_stats_' + currentRange + '.csv');
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+
+ // 显示提示
+ toast.classList.add('show');
+ setTimeout(function() {
+ toast.classList.remove('show');
+ }, 2000);
+ });
+
+ console.log('HoloLake Analytics v2 · 交互已加载');
+});
diff --git a/dashboard/index.html b/dashboard/index.html
new file mode 100644
index 00000000..a4782efb
--- /dev/null
+++ b/dashboard/index.html
@@ -0,0 +1,182 @@
+
+
+
+
+
+ HoloLake · 数据统计
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
👥
+
128
+
总用户
+
↑ 12%
+
+
+
💬
+
1,024
+
今日对话
+
↑ 8%
+
+
+
+
⚡
+
3,892
+
API调用
+
↓ 3%
+
+
+
+
+
🔀 模型调用占比
+
+
+
+
DeepSeek 72%
+
通义千问 15%
+
Kimi 8%
+
豆包 3%
+
GPT/Claude 2%
+
+
+
+
+
+
+
+
+
+
最近活动
+
+
+
+
+
用户「星河」与 知秋 完成了32轮对话
+
5分钟前
+
+
+
+
+
+
新用户「雨落」注册并激活了人格体
+
12分钟前
+
+
+
+
+
+
DeepSeek API 调用量达到今日峰值(420次/小时)
+
28分钟前
+
+
+
+
+
+
人格体「小坍缩核」记忆库自动归档完成
+
1小时前
+
+
+
+
+
+
用户「晚风」上传了3个文件到云盘
+
2小时前
+
+
+
+
+
+
+
+
+ CSV已下载!
+
+
+
+
diff --git a/dashboard/style.css b/dashboard/style.css
new file mode 100644
index 00000000..428962ee
--- /dev/null
+++ b/dashboard/style.css
@@ -0,0 +1,889 @@
+/* HoloLake 数据统计面板 · 样式表 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
+ background: linear-gradient(135deg, #0a1628 0%, #1a2a4a 50%, #0d1f3c 100%);
+ color: #e0e6ed;
+ min-height: 100vh;
+}
+
+.container {
+ max-width: 640px;
+ margin: 0 auto;
+ padding: 0 20px;
+}
+
+/* 顶部导航 */
+.top-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 0;
+ border-bottom: 1px solid rgba(255,255,255,0.1);
+}
+
+.logo {
+ font-size: 20px;
+ font-weight: bold;
+}
+
+.nav-links a {
+ color: #8899aa;
+ text-decoration: none;
+ margin-left: 16px;
+ font-size: 14px;
+}
+
+.nav-links a.active {
+ color: #4fc3f7;
+}
+
+.nav-links a:hover {
+ color: #fff;
+}
+
+/* 页面标题 */
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 24px 0 16px;
+}
+
+.page-header h1 {
+ font-size: 22px;
+ font-weight: 700;
+}
+
+.time-selector {
+ display: flex;
+ gap: 6px;
+}
+
+.time-btn {
+ background: rgba(255,255,255,0.06);
+ color: #8899aa;
+ border: 1px solid rgba(255,255,255,0.1);
+ padding: 6px 14px;
+ border-radius: 20px;
+ font-size: 13px;
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.time-btn.active {
+ background: rgba(79,195,247,0.15);
+ color: #4fc3f7;
+ border-color: rgba(79,195,247,0.4);
+}
+
+.time-btn:hover {
+ border-color: rgba(79,195,247,0.3);
+ color: #b0c4d8;
+}
+
+/* 统计卡片 */
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 12px;
+ padding-bottom: 24px;
+}
+
+.stat-card {
+ background: rgba(255,255,255,0.04);
+ border-radius: 16px;
+ padding: 20px 16px;
+ border: 1px solid rgba(255,255,255,0.06);
+ transition: all 0.3s;
+ cursor: pointer;
+}
+
+.stat-card:hover {
+ background: rgba(79,195,247,0.06);
+ border-color: rgba(79,195,247,0.2);
+ transform: translateY(-2px);
+}
+
+.stat-icon {
+ font-size: 24px;
+ margin-bottom: 8px;
+}
+
+.stat-value {
+ font-size: 32px;
+ font-weight: 800;
+ margin-bottom: 4px;
+}
+
+.stat-label {
+ font-size: 13px;
+ color: #8899aa;
+ margin-bottom: 6px;
+}
+
+.stat-change {
+ font-size: 12px;
+ font-weight: 600;
+}
+
+.stat-change.up { color: #4caf50; }
+.stat-change.down { color: #ef5350; }
+.stat-change.same { color: #8899aa; }
+
+/* 区块通用 */
+.section {
+ padding-bottom: 28px;
+}
+
+.section h2 {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 16px;
+}
+
+/* 横向条形图 */
+.bar-chart {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.bar-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.bar-label {
+ font-size: 13px;
+ color: #aab;
+ min-width: 70px;
+ text-align: right;
+}
+
+.bar-track {
+ flex: 1;
+ height: 22px;
+ background: rgba(255,255,255,0.04);
+ border-radius: 11px;
+ overflow: hidden;
+}
+
+.bar-fill {
+ height: 100%;
+ border-radius: 11px;
+ transition: width 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+}
+
+.bar-fill.deepseek { background: linear-gradient(90deg, #4fc3f7, #29b6f6); }
+.bar-fill.qwen { background: linear-gradient(90deg, #7c4dff, #651fff); }
+.bar-fill.kimi { background: linear-gradient(90deg, #ff7043, #f4511e); }
+.bar-fill.doubao { background: linear-gradient(90deg, #66bb6a, #43a047); }
+.bar-fill.premium { background: linear-gradient(90deg, #ffd54f, #ffb300); }
+
+.bar-value {
+ font-size: 13px;
+ color: #8899aa;
+ min-width: 36px;
+ font-weight: 600;
+}
+
+/* 每日趋势柱状图 */
+.trend-chart {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ height: 160px;
+ padding: 16px 0;
+ border-bottom: 1px solid rgba(255,255,255,0.08);
+}
+
+.trend-bar {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex: 1;
+ height: 100%;
+ justify-content: flex-end;
+ gap: 8px;
+}
+
+.trend-fill {
+ width: 28px;
+ border-radius: 6px 6px 2px 2px;
+ background: linear-gradient(180deg, #4fc3f7, rgba(79,195,247,0.3));
+ transition: height 1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+ height: 0;
+}
+
+.trend-bar span {
+ font-size: 11px;
+ color: #667;
+}
+
+/* 最近活动 */
+.activity-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.activity-item {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ padding: 12px 0;
+ border-bottom: 1px solid rgba(255,255,255,0.04);
+}
+
+.activity-item:last-child {
+ border-bottom: none;
+}
+
+.activity-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ margin-top: 6px;
+ flex-shrink: 0;
+}
+
+.activity-dot.blue { background: #4fc3f7; }
+.activity-dot.green { background: #66bb6a; }
+.activity-dot.orange { background: #ffb74d; }
+.activity-dot.purple { background: #ab47bc; }
+
+.activity-content {
+ flex: 1;
+}
+
+.activity-text {
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+.activity-text strong {
+ color: #4fc3f7;
+}
+
+.activity-time {
+ font-size: 12px;
+ color: #556;
+ margin-top: 2px;
+}
+
+/* 底部 */
+.footer {
+ text-align: center;
+ padding: 32px 0;
+ color: #556677;
+ font-size: 12px;
+ line-height: 1.8;
+}
+
+/* 响应式 */
+@media (max-width: 480px) {
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 8px;
+ }
+ .stat-value {
+ font-size: 26px;
+ }
+ .page-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+ .trend-fill {
+ width: 22px;
+ }
+ .bar-label {
+ min-width: 56px;
+ font-size: 12px;
+ }
+}
+/* ========== 响应式布局增强 ========== */
+/* 平板(768px以下) */
+@media (max-width: 768px) {
+ .container {
+ padding: 0 16px;
+ }
+ .page-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+ .page-header h1 {
+ font-size: 20px;
+ }
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
+ }
+ .stat-value {
+ font-size: 28px;
+ }
+ .bar-label {
+ min-width: 60px;
+ font-size: 12px;
+ }
+ .trend-fill {
+ width: 24px;
+ }
+}
+/* 手机(480px以下) */
+📡 BC-M08-002 · DEV-010桔子 · 数据统计面板·环节2~3·响应式布局+数据导出+高级图表 2
+@media (max-width: 480px) {
+ .container {
+ padding: 0 12px;
+ }
+ .top-bar {
+ padding: 12px 0;
+ }
+ .logo {
+ font-size: 17px;
+ }
+ .nav-links a {
+ font-size: 13px;
+ margin-left: 12px;
+ }
+ .page-header h1 {
+ font-size: 18px;
+ }
+ .time-btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ }
+ .stats-grid {
+ grid-template-columns: 1fr 1fr;
+ gap: 8px;
+ }
+ .stat-card {
+ padding: 14px 12px;
+ border-radius: 12px;
+ }
+ .stat-icon {
+ font-size: 20px;
+ margin-bottom: 6px;
+ }
+ .stat-value {
+ font-size: 24px;
+ }
+ .stat-label {
+ font-size: 12px;
+📡 BC-M08-002 · DEV-010桔子 · 数据统计面板·环节2~3·响应式布局+数据导出+高级图表 3
+ }
+ .stat-change {
+ font-size: 11px;
+ }
+ .section h2 {
+ font-size: 15px;
+ }
+ .bar-row {
+ gap: 8px;
+ }
+ .bar-label {
+ min-width: 50px;
+ font-size: 11px;
+ }
+ .bar-track {
+ height: 18px;
+ }
+ .bar-value {
+ font-size: 11px;
+ min-width: 30px;
+ }
+ .trend-chart {
+ height: 120px;
+ }
+ .trend-fill {
+ width: 18px;
+ border-radius: 4px 4px 2px 2px;
+ }
+ .trend-bar span {
+ font-size: 10px;
+ }
+ .activity-item {
+ padding: 10px 0;
+ gap: 10px;
+ }
+ .activity-text {
+ font-size: 13px;
+ }
+📡 BC-M08-002 · DEV-010桔子 · 数据统计面板·环节2~3·响应式布局+数据导出+高级图表 4
+ .activity-time {
+ font-size: 11px;
+ }
+ .footer {
+ padding: 20px 0;
+ font-size: 11px;
+ }
+}
+/* 超小屏(360px以下) */
+@media (max-width: 360px) {
+ .stats-grid {
+ grid-template-columns: 1fr;
+ }
+ .stat-card {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 14px;
+ }
+ .stat-icon {
+ margin-bottom: 0;
+ }
+ .stat-value {
+ font-size: 22px;
+ }
+ .time-selector {
+ width: 100%;
+ justify-content: space-between;
+ }
+}
+/* ========== 导出按钮样式 ========== */
+.export-bar {
+ display: flex;
+ justify-content: flex-end;
+ padding: 8px 0;
+}
+📡 BC-M08-002 · DEV-010桔子 · 数据统计面板·环节2~3·响应式布局+数据导出+高级图表 5
+.export-btn {
+ background: rgba(76, 175, 80, 0.1);
+ color: #81c784;
+ border: 1px solid rgba(76, 175, 80, 0.2);
+ padding: 8px 18px;
+ border-radius: 8px;
+ font-size: 13px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+.export-btn:hover {
+ background: rgba(76, 175, 80, 0.2);
+ transform: scale(1.02);
+}
+.export-btn:active {
+ transform: scale(0.98);
+}
+.export-toast {
+ position: fixed;
+ bottom: 30px;
+ left: 50%;
+ transform: translateX(-50%) translateY(60px);
+ background: rgba(76, 175, 80, 0.9);
+ color: #fff;
+ padding: 10px 24px;
+ border-radius: 8px;
+ font-size: 14px;
+ opacity: 0;
+ transition: all 0.4s;
+ pointer-events: none;
+ z-index: 999;
+}
+.export-toast.show {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
+}
+/* ========== 环形图样式 ========== */
+📡 BC-M08-002 · DEV-010桔子 · 数据统计面板·环节2~3·响应式布局+数据导出+高级图表 6
+.donut-section {
+ padding-bottom: 28px;
+}
+.donut-container {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ justify-content: center;
+}
+.donut-chart {
+ width: 160px;
+ height: 160px;
+ position: relative;
+}
+.donut-chart svg {
+ width: 100%;
+ height: 100%;
+ transform: rotate(-90deg);
+}
+.donut-chart circle {
+ fill: none;
+ stroke-width: 20;
+ cx: 80;
+ cy: 80;
+ r: 60;
+}
+.donut-bg {
+ stroke: rgba(255,255,255,0.06);
+}
+.donut-segment {
+ transition: stroke-dasharray 1.2s cubic-bezier(0.25, 0.
+46, 0.45, 0.94);
+}
+.donut-center {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+📡 BC-M08-002 · DEV-010桔子 · 数据统计面板·环节2~3·响应式布局+数据导出+高级图表 7
+ text-align: center;
+}
+.donut-center-value {
+ font-size: 24px;
+ font-weight: 800;
+}
+.donut-center-label {
+ font-size: 11px;
+ color: #8899aa;
+}
+.donut-legend {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+.legend-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+}
+.legend-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 3px;
+ flex-shrink: 0;
+}
+.legend-dot.c-deepseek { background: #4fc3f7; }
+.legend-dot.c-qwen { background: #7c4dff; }
+.legend-dot.c-kimi { background: #ff7043; }
+.legend-dot.c-doubao { background: #66bb6a; }
+.legend-dot.c-premium { background: #ffd54f; }
+.legend-percent {
+ color: #8899aa;
+ margin-left: auto;
+ font-weight: 600;
+}
+📡 BC-M08-002 · DEV-010桔子 · 数据统计面板·环节2~3·响应式布局+数据导出+高级图表 8
+@media (max-width: 480px) {
+ .donut-container {
+ flex-direction: column;
+ gap: 16px;
+ }
+ .donut-chart {
+ width: 140px;
+ height: 140px;
+ }
+ .donut-legend {
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 6px 16px;
+ }
+}
+/* ========== 响应式布局增强 ========== */
+/* 平板(768px以下) */
+@media (max-width: 768px) {
+ .container { padding: 0 16px; }
+ .page-header { flex-direction: column; align-items: flex-start; gap: 12px; }
+ .page-header h1 { font-size: 20px; }
+ .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
+ .stat-value { font-size: 28px; }
+ .bar-label { min-width: 60px; font-size: 12px; }
+ .trend-fill { width: 24px; }
+}
+/* 手机(480px以下) */
+@media (max-width: 480px) {
+ .container { padding: 0 12px; }
+ .top-bar { padding: 12px 0; }
+ .logo { font-size: 17px; }
+ .nav-links a { font-size: 13px; margin-left: 12px; }
+ .page-header h1 { font-size: 18px; }
+ .time-btn { padding: 5px 10px; font-size: 12px; }
+ .stats-grid { grid-template-columns: 1fr 1fr; gap: 8px; }
+ .stat-card { padding: 14px 12px; border-radius: 12px; }
+ .stat-icon { font-size: 20px; margin-bottom: 6px; }
+ .stat-value { font-size: 24px; }
+ .stat-label { font-size: 12px; }
+ .activity-time { font-size: 11px; }
+ .footer { padding: 20px 0; font-size: 11px; }
+}
+/* 超小屏(360px以下) */
+@media (max-width: 360px) {
+ .stats-grid { grid-template-columns: 1fr; }
+ .stat-card { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
+ .stat-icon { margin-bottom: 0; }
+ .stat-value { font-size: 22px; }
+ .time-selector { width: 100%; justify-content: space-between; }
+}
+/* ========== 导出按钮样式 ========== */
+.export-bar {
+ display: flex;
+ justify-content: flex-end;
+ padding: 8px 0;
+}
+.export-btn {
+ background: rgba(76,175,80,0.1);
+ color: #81c784;
+ border: 1px solid rgba(76,175,80,0.2);
+ padding: 8px 18px;
+ border-radius: 8px;
+ font-size: 13px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+.export-btn:hover {
+ background: rgba(76,175,80,0.2);
+ transform: scale(1.02);
+}
+.export-btn:active {
+ transform: scale(0.98);
+}
+.export-toast {
+ position: fixed;
+ bottom: 30px;
+ left: 50%;
+ transform: translateX(-50%) translateY(60px);
+ background: rgba(76,175,80,0.9);
+ color: #fff;
+ padding: 10px 24px;
+ border-radius: 8px;
+ font-size: 14px;
+ opacity: 0;
+ transition: all 0.4s;
+ pointer-events: none;
+ z-index: 999;
+}
+.export-toast.show {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
+}
+/* ========== 环形图样式 ========== */
+.donut-section {
+ padding-bottom: 28px;
+}
+.donut-container {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ justify-content: center;
+}
+.donut-chart {
+ width: 160px;
+ height: 160px;
+ position: relative;
+}
+.donut-chart svg {
+ width: 100%;
+ height: 100%;
+ transform: rotate(-90deg);
+}
+.donut-chart circle {
+ fill: none;
+ stroke-width: 20;
+ cx: 80;
+ cy: 80;
+ r: 60;
+}
+.donut-bg {
+ stroke: rgba(255,255,255,0.06);
+}
+.donut-segment {
+ transition: stroke-dasharray 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+}
+.donut-center {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+}
+.donut-center-value {
+ font-size: 24px;
+ font-weight: 800;
+}
+.donut-center-label {
+ font-size: 11px;
+ color: #8899aa;
+}
+.donut-legend {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+.legend-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+}
+.legend-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 3px;
+ flex-shrink: 0;
+}
+.legend-dot.c-deepseek { background: #4fc3f7; }
+.legend-dot.c-qwen { background: #7c4dff; }
+.legend-dot.c-kimi { background: #ff7043; }
+.legend-dot.c-doubao { background: #66bb6a; }
+.legend-dot.c-premium { background: #ffd54f; }
+.legend-percent {
+ color: #8899aa;
+ margin-left: auto;
+ font-weight: 600;
+}
+/* ========== 响应式布局增强 ========== */
+@media (max-width: 768px) {
+ .container { padding: 0 16px; }
+ .page-header { flex-direction: column; align-items: flex-start; gap: 12px; }
+ .page-header h1 { font-size: 20px; }
+ .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
+ .stat-value { font-size: 28px; }
+ .bar-label { min-width: 60px; font-size: 12px; }
+ .trend-fill { width: 24px; }
+}
+@media (max-width: 480px) {
+ .container { padding: 0 12px; }
+ .top-bar { padding: 12px 0; }
+ .logo { font-size: 17px; }
+ .nav-links a { font-size: 13px; margin-left: 12px; }
+ .page-header h1 { font-size: 18px; }
+ .time-btn { padding: 5px 10px; font-size: 12px; }
+ .stats-grid { grid-template-columns: 1fr 1fr; gap: 8px; }
+ .stat-card { padding: 14px 12px; border-radius: 12px; }
+ .stat-icon { font-size: 20px; margin-bottom: 6px; }
+ .stat-value { font-size: 24px; }
+ .stat-label { font-size: 12px; }
+ .activity-time { font-size: 11px; }
+ .footer { padding: 20px 0; font-size: 11px; }
+}
+@media (max-width: 360px) {
+ .stats-grid { grid-template-columns: 1fr; }
+ .stat-card { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
+ .stat-icon { margin-bottom: 0; }
+ .stat-value { font-size: 22px; }
+ .time-selector { width: 100%; justify-content: space-between; }
+}
+/* ========== 导出按钮样式 ========== */
+.export-bar {
+ display: flex;
+ justify-content: flex-end;
+ padding: 8px 0;
+}
+.export-btn {
+ background: rgba(76,175,80,0.1);
+ color: #81c784;
+ border: 1px solid rgba(76,175,80,0.2);
+ padding: 8px 18px;
+ border-radius: 8px;
+ font-size: 13px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+.export-btn:hover {
+ background: rgba(76,175,80,0.2);
+ transform: scale(1.02);
+}
+.export-btn:active {
+ transform: scale(0.98);
+}
+.export-toast {
+ position: fixed;
+ bottom: 30px;
+ left: 50%;
+ transform: translateX(-50%) translateY(60px);
+ background: rgba(76,175,80,0.9);
+ color: #fff;
+ padding: 10px 24px;
+ border-radius: 8px;
+ font-size: 14px;
+ opacity: 0;
+ transition: all 0.4s;
+ pointer-events: none;
+ z-index: 999;
+}
+.export-toast.show {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
+}
+/* ========== 环形图样式 ========== */
+.donut-section {
+ padding-bottom: 28px;
+}
+.donut-container {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ justify-content: center;
+}
+.donut-chart {
+ width: 160px;
+ height: 160px;
+ position: relative;
+}
+.donut-chart svg {
+ width: 100%;
+ height: 100%;
+ transform: rotate(-90deg);
+}
+.donut-chart circle {
+ fill: none;
+ stroke-width: 20;
+ cx: 80;
+ cy: 80;
+ r: 60;
+}
+.donut-bg {
+ stroke: rgba(255,255,255,0.06);
+}
+.donut-segment {
+ transition: stroke-dasharray 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+}
+.donut-center {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+}
+.donut-center-value {
+ font-size: 24px;
+ font-weight: 800;
+}
+.donut-center-label {
+ font-size: 11px;
+ color: #8899aa;
+}
+.donut-legend {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+.legend-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+}
+.legend-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 3px;
+ flex-shrink: 0;
+}
+.legend-dot.c-deepseek { background: #4fc3f7; }
+.legend-dot.c-qwen { background: #7c4dff; }
+.legend-dot.c-kimi { background: #ff7043; }
+.legend-dot.c-doubao { background: #66bb6a; }
+.legend-dot.c-premium { background: #ffd54f; }
+.legend-percent {
+ color: #8899aa;
+ margin-left: auto;
+ font-weight: 600;
+}
diff --git a/ticket-system/index.html b/ticket-system/index.html
new file mode 100644
index 00000000..bfac39d1
--- /dev/null
+++ b/ticket-system/index.html
@@ -0,0 +1,809 @@
+
+
+
+
+
+ 工单管理 · HoloLake · 视觉优化版
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编号↑
+
+
+
+
+
+
+
+
+
+
+
+
+
+
新建工单
+
+
+
+
+
+
+
+
+
+
+
+
+background: linear-gradient(135deg, #0a1628 0%, #1a2a4a 50%, #0d1f3c 100%);
+
\ No newline at end of file
diff --git a/ticket-system/script.js b/ticket-system/script.js
new file mode 100644
index 00000000..358ac3b2
--- /dev/null
+++ b/ticket-system/script.js
@@ -0,0 +1,301 @@
+/* ============================================
+ HoloLake · M06 工单管理界面 · 逻辑层
+ 环节3:响应式 + 数据导出(JSON/CSV)
+ DEV-010 桔子
+ ============================================ */
+// === 状态流转规则 ===
+const STATUS_FLOW = {
+ 'pending': { next: 'active', label: '待处理' },
+ 'active': { next: 'done', label: '进行中' },
+ 'done': { next: 'pending', label: '已完成' }
+};
+// === 初始化 · 从 localStorage 读取工单 ===
+let tickets = JSON.parse(localStorage.getItem('holotake_tic
+kets') || '[]');
+// === 保存到 localStorage ===
+function saveTickets() {
+ localStorage.setItem('holotake_tickets', JSON.stringify(t
+ickets));
+}
+// === 生成工单编号 ===
+function generateId() {
+ const now = new Date();
+ const dateStr = [
+ now.getFullYear(),
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 18
+ String(now.getMonth() + 1).padStart(2, '0'),
+ String(now.getDate()).padStart(2, '0')
+ ].join('');
+ const rand = String(Math.floor(Math.random() * 1000)).pad
+Start(3, '0');
+ return 'TK-' + dateStr + '-' + rand;
+}
+// === 渲染统计卡片 ===
+function renderStats() {
+ const total = tickets.length;
+ const pending = tickets.filter(t => t.status === 'pendin
+g').length;
+ const active = tickets.filter(t => t.status === 'activ
+e').length;
+ const done = tickets.filter(t => t.status === 'done').len
+gth;
+ document.getElementById('statTotal').textContent = total;
+ document.getElementById('statPending').textContent = pend
+ing;
+ document.getElementById('statActive').textContent = activ
+e;
+ document.getElementById('statDone').textContent = done;
+}
+// === 获取筛选和排序后的工单列表 ===
+function getFilteredTickets() {
+ const filterStatus = document.getElementById('filterStatu
+s').value;
+ const sortBy = document.getElementById('sortBy').value;
+ let list = [...tickets];
+ // 筛选
+ if (filterStatus !== 'all') {
+ list = list.filter(t => t.status === filterStatus);
+ }
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 19
+ // 排序
+ if (sortBy === 'newest') {
+ list.sort((a, b) => new Date(b.createdAt) - new Date(a.
+createdAt));
+ } else if (sortBy === 'oldest') {
+ list.sort((a, b) => new Date(a.createdAt) - new Date(b.
+createdAt));
+ } else if (sortBy === 'status') {
+ const order = { 'pending': 0, 'active': 1, 'done': 2 };
+ list.sort((a, b) => order[a.status] - order[b.status]);
+ }
+ return list;
+}
+// === 渲染工单列表 ===
+function renderTickets() {
+ const list = getFilteredTickets();
+ const container = document.getElementById('ticketList');
+ if (list.length === 0) {
+ container.innerHTML = '';
+ return;
+ }
+ container.innerHTML = list.map(ticket => {
+ const statusInfo = STATUS_FLOW[ticket.status];
+ return '' +
+ '
' + ticket.id + '' +
+ '
' +
+ '
' + escapeHtml(ticket.ti
+tle) + '
' +
+ '
负责人:' + escapeHtml(tic
+ket.assignee) + ' · ' + formatDate(ticket.createdAt) + '' +
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 20
+ '
' +
+ '
' +
+ '' + statusInf
+o.label + '' +
+ '' +
+ '
' +
+ '
';
+ }).join('');
+}
+// === HTML转义(防XSS) ===
+function escapeHtml(str) {
+ const div = document.createElement('div');
+ div.textContent = str;
+ return div.innerHTML;
+}
+// === 格式化日期 ===
+function formatDate(dateStr) {
+ const d = new Date(dateStr);
+ return d.getFullYear() + '-' +
+ String(d.getMonth() + 1).padStart(2, '0') + '-' +
+ String(d.getDate()).padStart(2, '0') + ' ' +
+ String(d.getHours()).padStart(2, '0') + ':' +
+ String(d.getMinutes()).padStart(2, '0');
+}
+// === 切换工单状态 ===
+function toggleStatus(id) {
+ const ticket = tickets.find(t => t.id === id);
+ if (!ticket) return;
+ ticket.status = STATUS_FLOW[ticket.status].next;
+ saveTickets();
+ renderAll();
+}
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 21
+// === 删除工单 ===
+function deleteTicket(id) {
+ if (!confirm('确认删除这条工单?')) return;
+ tickets = tickets.filter(t => t.id !== id);
+ saveTickets();
+ renderAll();
+}
+// === 新建工单弹窗 ===
+function openModal() {
+ document.getElementById('modalOverlay').classList.add('sh
+ow');
+ document.getElementById('inputTitle').value = '';
+ document.getElementById('inputAssignee').value = '';
+ document.getElementById('inputDetail').value = '';
+ document.getElementById('inputTitle').focus();
+}
+function closeModal() {
+ document.getElementById('modalOverlay').classList.remove
+('show');
+}
+// === 提交新建工单 ===
+function submitTicket() {
+ const title = document.getElementById('inputTitle').valu
+e.trim();
+ const assignee = document.getElementById('inputAssigne
+e').value.trim();
+ const detail = document.getElementById('inputDetail').val
+ue.trim();
+ if (!title) {
+ alert('请填写工单标题');
+ return;
+ }
+ if (!assignee) {
+ alert('请填写负责人');
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 22
+ return;
+ }
+ const newTicket = {
+ id: generateId(),
+ title: title,
+ assignee: assignee,
+ detail: detail,
+ status: 'pending',
+ createdAt: new Date().toISOString()
+ };
+ tickets.unshift(newTicket);
+ saveTickets();
+ closeModal();
+ renderAll();
+}
+// === 重置全部工单 ===
+function resetTickets() {
+ if (!confirm('确认清空所有工单数据?此操作不可恢复!')) return;
+ tickets = [];
+ saveTickets();
+ renderAll();
+}
+// ============================================
+// 环节3新增:导出功能
+// ============================================
+// === 导出下拉菜单控制 ===
+function toggleExportMenu() {
+ const dropdown = document.getElementById('exportDropdow
+n');
+ const clickAway = document.getElementById('clickAway');
+ const isOpen = dropdown.classList.contains('show');
+ if (isOpen) {
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 23
+ closeExportMenu();
+ } else {
+ dropdown.classList.add('show');
+ clickAway.classList.add('show');
+ }
+}
+function closeExportMenu() {
+ document.getElementById('exportDropdown').classList.remov
+e('show');
+ document.getElementById('clickAway').classList.remove('sh
+ow');
+}
+// === 导出为 JSON 文件 ===
+function exportJSON() {
+ closeExportMenu();
+ if (tickets.length === 0) {
+ alert('当前没有工单数据可导出');
+ return;
+ }
+ const data = JSON.stringify(tickets, null, 2);
+ const blob = new Blob([data], { type: 'application/json;c
+harset=utf-8' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'HoloLake-Tickets-' + getDateStamp() + '.jso
+n';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+}
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 24
+// === 导出为 CSV 文件 ===
+function exportCSV() {
+ closeExportMenu();
+ if (tickets.length === 0) {
+ alert('当前没有工单数据可导出');
+ return;
+ }
+ // CSV表头
+ const header = '编号,标题,负责人,状态,详情,创建时间';
+ // CSV内容行
+ const rows = tickets.map(t => {
+ const statusLabel = STATUS_FLOW[t.status] ? STATUS_FLOW
+[t.status].label : t.status;
+ return [
+ t.id,
+ csvEscape(t.title),
+ csvEscape(t.assignee),
+ statusLabel,
+ csvEscape(t.detail || ''),
+ formatDate(t.createdAt)
+ ].join(',');
+ });
+ const csv = '\uFEFF' + header + '\n' + rows.join('\n');
+ const blob = new Blob([csv], { type: 'text/csv;charset=ut
+f-8' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'HoloLake-Tickets-' + getDateStamp() + '.cs
+v';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 25
+ URL.revokeObjectURL(url);
+}
+// === CSV字段转义(处理逗号和引号) ===
+function csvEscape(str) {
+ if (!str) return '';
+ if (str.includes(',') || str.includes('"') || str.include
+s('\n')) {
+ return '"' + str.replace(/"/g, '""') + '"';
+ }
+ return str;
+}
+// === 生成日期戳(用于文件名) ===
+function getDateStamp() {
+ const now = new Date();
+ return now.getFullYear() +
+ String(now.getMonth() + 1).padStart(2, '0') +
+ String(now.getDate()).padStart(2, '0') + '-' +
+ String(now.getHours()).padStart(2, '0') +
+ String(now.getMinutes()).padStart(2, '0');
+}
+// === 统一渲染 ===
+function renderAll() {
+ renderStats();
+ renderTickets();
+}
+// === 筛选/排序变化时重新渲染 ===
+document.getElementById('filterStatus').addEventListener('c
+hange', renderTickets);
+document.getElementById('sortBy').addEventListener('chang
+e', renderTickets);
+// === 弹窗点击遮罩关闭 ===
+document.getElementById('modalOverlay').addEventListener('c
+lick', function(e) {
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 26
+ if (e.target === this) closeModal();
+});
+// === 页面加载完成 · 首次渲染 ===
+renderAll();
diff --git a/ticket-system/style.css b/ticket-system/style.css
new file mode 100644
index 00000000..6a2a6ccf
--- /dev/null
+++ b/ticket-system/style.css
@@ -0,0 +1,527 @@
+/* ============================================
+ HoloLake · M06 工单管理界面 · 样式表
+ 环节3:响应式布局 · 深蓝主题
+ DEV-010 桔子
+ ============================================ */
+/* === 全局变量 === */
+:root {
+ --bg-primary: #0a0e27;
+ --bg-secondary: #111638;
+ --bg-card: #161b4a;
+ --bg-hover: #1e2460;
+ --border-color: #2a3070;
+ --text-primary: #e8eaff;
+ --text-secondary: #8b90c0;
+ --accent-blue: #4a6cf7;
+ --accent-blue-hover: #5b7bf8;
+ --accent-green: #22c55e;
+ --accent-yellow: #eab308;
+ --accent-red: #ef4444;
+ --accent-purple: #a855f7;
+ --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3);
+ --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.4);
+ --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.5);
+ --radius-sm: 6px;
+ --radius-md: 10px;
+ --radius-lg: 14px;
+}
+/* === 基础重置 === */
+* {
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 2
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe U
+I", "PingFang SC", "Microsoft YaHei", sans-serif;
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ min-height: 100vh;
+ line-height: 1.6;
+}
+/* === 主容器 === */
+.app-container {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 20px 24px;
+}
+/* === 顶部标题栏 === */
+.app-header {
+ text-align: center;
+ padding: 24px 0 20px;
+ border-bottom: 1px solid var(--border-color);
+ margin-bottom: 24px;
+}
+.app-header h1 {
+ font-size: 22px;
+ font-weight: 700;
+ color: var(--text-primary);
+ letter-spacing: 1px;
+}
+.app-header .subtitle {
+ font-size: 13px;
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 3
+ color: var(--text-secondary);
+ margin-top: 4px;
+}
+/* === 操作栏(新建 + 导出) === */
+.header-actions {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+}
+.btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 10px 20px;
+ border: none;
+ border-radius: var(--radius-sm);
+ font-size: 14px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ white-space: nowrap;
+}
+.btn-primary {
+ background: var(--accent-blue);
+ color: #fff;
+}
+.btn-primary:hover {
+ background: var(--accent-blue-hover);
+ box-shadow: var(--shadow-sm);
+}
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 4
+.btn-secondary {
+ background: var(--bg-card);
+ color: var(--text-primary);
+ border: 1px solid var(--border-color);
+}
+.btn-secondary:hover {
+ background: var(--bg-hover);
+}
+/* === 导出下拉菜单 === */
+.export-wrapper {
+ position: relative;
+}
+.export-dropdown {
+ display: none;
+ position: absolute;
+ top: calc(100% + 6px);
+ left: 50%;
+ transform: translateX(-50%);
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-sm);
+ box-shadow: var(--shadow-md);
+ z-index: 100;
+ min-width: 160px;
+ overflow: hidden;
+}
+.export-dropdown.show {
+ display: block;
+}
+.export-dropdown .export-item {
+ display: block;
+ width: 100%;
+ padding: 10px 16px;
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 5
+ background: none;
+ border: none;
+ color: var(--text-primary);
+ font-size: 14px;
+ text-align: left;
+ cursor: pointer;
+ transition: background 0.15s;
+}
+.export-dropdown .export-item:hover {
+ background: var(--bg-hover);
+}
+.export-dropdown .export-item + .export-item {
+ border-top: 1px solid var(--border-color);
+}
+/* === 统计卡片 === */
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 12px;
+ margin-bottom: 24px;
+}
+.stat-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-md);
+ padding: 16px;
+ text-align: center;
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+.stat-card:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-sm);
+}
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 6
+.stat-card .stat-number {
+ font-size: 28px;
+ font-weight: 700;
+ line-height: 1.2;
+}
+.stat-card .stat-label {
+ font-size: 12px;
+ color: var(--text-secondary);
+ margin-top: 4px;
+}
+.stat-card.total .stat-number { color: var(--accent-blue);
+}
+.stat-card.pending .stat-number { color: var(--accent-yello
+w); }
+.stat-card.active .stat-number { color: var(--accent-gree
+n); }
+.stat-card.done .stat-number { color: var(--accent-purple);
+}
+/* === 筛选排序栏 === */
+.filter-bar {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+}
+.filter-bar select {
+ flex: 1;
+ min-width: 120px;
+ padding: 8px 12px;
+ background: var(--bg-card);
+ color: var(--text-primary);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-sm);
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 7
+ font-size: 13px;
+ cursor: pointer;
+ appearance: none;
+ -webkit-appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='h
+ttp://www.w3.org/2000/svg' width='12' height='12' fill='%23
+8b90c0'%3E%3Cpath d='M6 8L1 3h10z'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 10px center;
+ padding-right: 30px;
+}
+.filter-bar select:focus {
+ outline: none;
+ border-color: var(--accent-blue);
+}
+/* === 工单列表 === */
+.ticket-list {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+.ticket-item {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-md);
+ padding: 16px;
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ transition: background 0.15s;
+}
+.ticket-item:hover {
+ background: var(--bg-hover);
+}
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 8
+.ticket-id {
+ font-size: 12px;
+ color: var(--text-secondary);
+ font-family: "SF Mono", "Fira Code", monospace;
+ min-width: 60px;
+}
+.ticket-info {
+ flex: 1;
+ min-width: 0;
+}
+.ticket-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--text-primary);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.ticket-meta {
+ font-size: 12px;
+ color: var(--text-secondary);
+ margin-top: 2px;
+}
+.ticket-actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex-shrink: 0;
+}
+/* === 状态标签(可点击切换) === */
+.status-badge {
+ display: inline-block;
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 9
+ padding: 4px 12px;
+ border-radius: 20px;
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: opacity 0.2s;
+ white-space: nowrap;
+}
+.status-badge:hover {
+ opacity: 0.85;
+}
+.status-badge.pending {
+ background: rgba(234, 179, 8, 0.15);
+ color: var(--accent-yellow);
+ border: 1px solid rgba(234, 179, 8, 0.3);
+}
+.status-badge.active {
+ background: rgba(34, 197, 94, 0.15);
+ color: var(--accent-green);
+ border: 1px solid rgba(34, 197, 94, 0.3);
+}
+.status-badge.done {
+ background: rgba(168, 85, 247, 0.15);
+ color: var(--accent-purple);
+ border: 1px solid rgba(168, 85, 247, 0.3);
+}
+/* === 删除按钮 === */
+.btn-delete {
+ background: none;
+ border: 1px solid transparent;
+ color: var(--text-secondary);
+ font-size: 16px;
+ cursor: pointer;
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 10
+ padding: 4px 8px;
+ border-radius: var(--radius-sm);
+ transition: all 0.2s;
+}
+.btn-delete:hover {
+ color: var(--accent-red);
+ background: rgba(239, 68, 68, 0.1);
+ border-color: rgba(239, 68, 68, 0.3);
+}
+/* === 空状态 === */
+.empty-state {
+ text-align: center;
+ padding: 60px 20px;
+ color: var(--text-secondary);
+}
+.empty-state .empty-icon {
+ font-size: 48px;
+ margin-bottom: 12px;
+}
+.empty-state .empty-text {
+ font-size: 15px;
+}
+/* === 弹窗遮罩 === */
+.modal-overlay {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.6);
+ z-index: 1000;
+ justify-content: center;
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 11
+ align-items: center;
+ padding: 20px;
+}
+.modal-overlay.show {
+ display: flex;
+}
+/* === 弹窗 === */
+.modal {
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-lg);
+ padding: 28px;
+ width: 100%;
+ max-width: 480px;
+ box-shadow: var(--shadow-lg);
+}
+.modal h2 {
+ font-size: 18px;
+ margin-bottom: 20px;
+ color: var(--text-primary);
+}
+.modal .form-group {
+ margin-bottom: 16px;
+}
+.modal label {
+ display: block;
+ font-size: 13px;
+ color: var(--text-secondary);
+ margin-bottom: 6px;
+}
+.modal input,
+.modal select,
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 12
+.modal textarea {
+ width: 100%;
+ padding: 10px 12px;
+ background: var(--bg-card);
+ color: var(--text-primary);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-sm);
+ font-size: 14px;
+}
+.modal input:focus,
+.modal select:focus,
+.modal textarea:focus {
+ outline: none;
+ border-color: var(--accent-blue);
+}
+.modal textarea {
+ resize: vertical;
+ min-height: 80px;
+}
+.modal .form-actions {
+ display: flex;
+ gap: 10px;
+ justify-content: flex-end;
+ margin-top: 20px;
+}
+/* === 点击外部关闭下拉菜单的辅助层 === */
+.click-away {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 99;
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 13
+}
+.click-away.show {
+ display: block;
+}
+/* ============================================
+ 响应式布局 · 平板端(481px ~ 768px)
+ ============================================ */
+@media screen and (max-width: 768px) {
+ .app-container {
+ padding: 16px;
+ }
+ .app-header h1 {
+ font-size: 20px;
+ }
+ .stats-grid {
+ grid-template-columns: repeat(4, 1fr);
+ gap: 8px;
+ }
+ .stat-card {
+ padding: 12px 8px;
+ }
+ .stat-card .stat-number {
+ font-size: 24px;
+ }
+ .stat-card .stat-label {
+ font-size: 11px;
+ }
+ .ticket-item {
+ padding: 12px;
+ gap: 10px;
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 14
+ }
+}
+/* ============================================
+ 响应式布局 · 手机端(<=480px)
+ ============================================ */
+@media screen and (max-width: 480px) {
+ .app-container {
+ padding: 12px;
+ }
+ .app-header {
+ padding: 16px 0 14px;
+ margin-bottom: 16px;
+ }
+ .app-header h1 {
+ font-size: 18px;
+ }
+ /* 统计卡片手机端变2列 */
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 8px;
+ }
+ .stat-card {
+ padding: 12px 10px;
+ }
+ .stat-card .stat-number {
+ font-size: 22px;
+ }
+ /* 操作按钮自适应 */
+ .header-actions {
+ gap: 8px;
+ }
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 15
+ .btn {
+ padding: 8px 14px;
+ font-size: 13px;
+ }
+ /* 筛选栏纵向排列 */
+ .filter-bar {
+ flex-direction: column;
+ gap: 8px;
+ }
+ .filter-bar select {
+ width: 100%;
+ }
+ /* 工单卡片纵向排列 */
+ .ticket-item {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+ padding: 14px;
+ }
+ .ticket-id {
+ min-width: auto;
+ }
+ .ticket-info {
+ width: 100%;
+ }
+ .ticket-title {
+ white-space: normal;
+ word-break: break-all;
+ }
+ .ticket-actions {
+📡 BC-M06-001 · DEV-010桔子 · 工单管理界面·环节3·响应式布局+数据导出 16
+ width: 100%;
+ justify-content: space-between;
+ }
+ /* 导出下拉菜单手机端定位优化 */
+ .export-dropdown {
+ left: auto;
+ right: 0;
+ transform: none;
+ }
+ /* 弹窗手机端全屏 */
+ .modal-overlay {
+ padding: 0;
+ align-items: flex-end;
+ }
+ .modal {
+ max-width: 100%;
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
+ padding: 24px 20px;
+ max-height: 90vh;
+ overflow-y: auto;
+ }
+ .modal .form-actions {
+ flex-direction: column;
+ }
+ .modal .form-actions .btn {
+ width: 100%;
+ justify-content: center;
+ }
+}