feat: 快速提交 - 周一 2025/09/22 11:54:13.80
This commit is contained in:
@@ -8,6 +8,7 @@ from flask import Blueprint, request, jsonify, render_template
|
||||
from src.integrations.feishu_client import FeishuClient
|
||||
from src.integrations.workorder_sync import WorkOrderSyncService
|
||||
from src.integrations.config_manager import config_manager
|
||||
from src.integrations.feishu_permission_checker import FeishuPermissionChecker
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -322,6 +323,22 @@ def remove_field_mapping():
|
||||
logger.error(f"移除字段映射失败: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@feishu_sync_bp.route('/check-permissions')
|
||||
def check_permissions():
|
||||
"""检查飞书权限"""
|
||||
try:
|
||||
checker = FeishuPermissionChecker()
|
||||
result = checker.check_permissions()
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"permission_check": result,
|
||||
"summary": checker.get_permission_summary()
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"权限检查失败: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@feishu_sync_bp.route('/field-mapping')
|
||||
def field_mapping_page():
|
||||
"""字段映射管理页面"""
|
||||
|
||||
@@ -15,6 +15,11 @@ class TSPDashboard {
|
||||
|
||||
this.init();
|
||||
this.restorePageState();
|
||||
|
||||
// 添加页面卸载时的清理逻辑
|
||||
window.addEventListener('beforeunload', () => {
|
||||
this.destroyAllCharts();
|
||||
});
|
||||
}
|
||||
|
||||
async generateAISuggestion(workorderId) {
|
||||
@@ -335,6 +340,14 @@ class TSPDashboard {
|
||||
|
||||
this.currentTab = tabName;
|
||||
|
||||
// 如果切换到分析页面,重新初始化图表
|
||||
if (tabName === 'analytics') {
|
||||
// 延迟一点时间确保DOM已更新
|
||||
setTimeout(() => {
|
||||
this.initializeCharts();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 保存当前页面状态
|
||||
this.savePageState();
|
||||
|
||||
@@ -3160,9 +3173,63 @@ class TSPDashboard {
|
||||
|
||||
// 初始化图表
|
||||
initializeCharts() {
|
||||
this.charts = {};
|
||||
if (!this.charts) {
|
||||
this.charts = {};
|
||||
}
|
||||
this.updateCharts();
|
||||
}
|
||||
|
||||
// 销毁所有图表
|
||||
destroyAllCharts() {
|
||||
if (!this.charts) return;
|
||||
|
||||
Object.keys(this.charts).forEach(chartId => {
|
||||
if (this.charts[chartId]) {
|
||||
try {
|
||||
this.charts[chartId].destroy();
|
||||
} catch (e) {
|
||||
console.warn(`Error destroying chart ${chartId}:`, e);
|
||||
}
|
||||
this.charts[chartId] = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 清理charts对象
|
||||
this.charts = {};
|
||||
}
|
||||
|
||||
// 安全的图表创建方法
|
||||
createChart(canvasId, chartConfig) {
|
||||
const canvas = document.getElementById(canvasId);
|
||||
if (!canvas) {
|
||||
console.error(`Canvas element '${canvasId}' not found`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 确保charts对象存在
|
||||
if (!this.charts) {
|
||||
this.charts = {};
|
||||
}
|
||||
|
||||
// 销毁现有图表
|
||||
if (this.charts[canvasId]) {
|
||||
try {
|
||||
this.charts[canvasId].destroy();
|
||||
} catch (e) {
|
||||
console.warn(`Error destroying chart ${canvasId}:`, e);
|
||||
}
|
||||
this.charts[canvasId] = null;
|
||||
}
|
||||
|
||||
try {
|
||||
const ctx = canvas.getContext('2d');
|
||||
this.charts[canvasId] = new Chart(ctx, chartConfig);
|
||||
return this.charts[canvasId];
|
||||
} catch (e) {
|
||||
console.error(`Error creating chart ${canvasId}:`, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新所有图表
|
||||
async updateCharts() {
|
||||
@@ -3215,16 +3282,9 @@ class TSPDashboard {
|
||||
|
||||
// 更新主图表
|
||||
updateMainChart(data, chartType) {
|
||||
const ctx = document.getElementById('mainChart').getContext('2d');
|
||||
|
||||
// 销毁现有图表
|
||||
if (this.charts.mainChart) {
|
||||
this.charts.mainChart.destroy();
|
||||
}
|
||||
|
||||
const chartData = this.prepareChartData(data, chartType);
|
||||
|
||||
this.charts.mainChart = new Chart(ctx, {
|
||||
const chartConfig = {
|
||||
type: chartType,
|
||||
data: chartData,
|
||||
options: {
|
||||
@@ -3257,22 +3317,18 @@ class TSPDashboard {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.createChart('mainChart', chartConfig);
|
||||
}
|
||||
|
||||
// 更新分布图表
|
||||
updateDistributionChart(data) {
|
||||
const ctx = document.getElementById('distributionChart').getContext('2d');
|
||||
|
||||
if (this.charts.distributionChart) {
|
||||
this.charts.distributionChart.destroy();
|
||||
}
|
||||
|
||||
const categories = data.workorders?.by_category || {};
|
||||
const labels = Object.keys(categories);
|
||||
const values = Object.values(categories);
|
||||
|
||||
this.charts.distributionChart = new Chart(ctx, {
|
||||
const chartConfig = {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: labels,
|
||||
@@ -3302,7 +3358,9 @@ class TSPDashboard {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.createChart('distributionChart', chartConfig);
|
||||
}
|
||||
|
||||
// 更新趋势图表
|
||||
@@ -4901,6 +4959,78 @@ class FeishuSyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
async checkPermissions() {
|
||||
try {
|
||||
this.showNotification('正在检查飞书权限...', 'info');
|
||||
|
||||
const response = await fetch('/api/feishu-sync/check-permissions');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.displayPermissionCheck(data.permission_check, data.summary);
|
||||
this.showNotification('权限检查完成', 'success');
|
||||
} else {
|
||||
this.showNotification('权限检查失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('权限检查失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
displayPermissionCheck(permissionCheck, summary) {
|
||||
const container = document.getElementById('fieldMappingContent');
|
||||
|
||||
let html = '<div class="card"><div class="card-header"><h6 class="card-title mb-0"><i class="fas fa-shield-alt me-2"></i>飞书权限检查结果</h6></div><div class="card-body">';
|
||||
|
||||
// 整体状态
|
||||
const statusClass = permissionCheck.success ? 'success' : 'danger';
|
||||
const statusIcon = permissionCheck.success ? 'check-circle' : 'exclamation-triangle';
|
||||
html += `<div class="alert alert-${statusClass}">
|
||||
<i class="fas fa-${statusIcon}"></i>
|
||||
整体状态: ${permissionCheck.success ? '正常' : '异常'}
|
||||
</div>`;
|
||||
|
||||
// 检查项目
|
||||
html += '<h6>检查项目:</h6>';
|
||||
for (const [checkName, checkResult] of Object.entries(permissionCheck.checks)) {
|
||||
const statusClass = checkResult.status === 'success' ? 'success' :
|
||||
checkResult.status === 'warning' ? 'warning' : 'danger';
|
||||
const statusIcon = checkResult.status === 'success' ? 'check-circle' :
|
||||
checkResult.status === 'warning' ? 'exclamation-triangle' : 'times-circle';
|
||||
|
||||
html += `<div class="alert alert-${statusClass} py-2">
|
||||
<i class="fas fa-${statusIcon}"></i>
|
||||
<strong>${checkName}</strong>: ${checkResult.message}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// 修复建议
|
||||
if (permissionCheck.recommendations && permissionCheck.recommendations.length > 0) {
|
||||
html += '<h6>修复建议:</h6><ul class="list-group mb-3">';
|
||||
permissionCheck.recommendations.forEach(rec => {
|
||||
html += `<li class="list-group-item">${rec}</li>`;
|
||||
});
|
||||
html += '</ul>';
|
||||
}
|
||||
|
||||
// 错误信息
|
||||
if (permissionCheck.errors && permissionCheck.errors.length > 0) {
|
||||
html += '<h6>错误信息:</h6><ul class="list-group">';
|
||||
permissionCheck.errors.forEach(error => {
|
||||
html += `<li class="list-group-item list-group-item-danger">${error}</li>`;
|
||||
});
|
||||
html += '</ul>';
|
||||
}
|
||||
|
||||
html += '</div></div>';
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
// 显示字段映射管理区域
|
||||
const section = document.getElementById('fieldMappingSection');
|
||||
section.style.display = 'block';
|
||||
}
|
||||
|
||||
showNotification(message, type = 'info') {
|
||||
const container = document.getElementById('notificationContainer');
|
||||
const alert = document.createElement('div');
|
||||
|
||||
@@ -1148,6 +1148,9 @@
|
||||
<button class="btn btn-warning" onclick="openFieldMapping()">
|
||||
<i class="fas fa-exchange-alt me-1"></i>字段映射管理
|
||||
</button>
|
||||
<button class="btn btn-outline-danger" onclick="feishuSync.checkPermissions()">
|
||||
<i class="fas fa-shield-alt me-1"></i>权限检查
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="feishuSync.refreshStatus()">
|
||||
<i class="fas fa-refresh me-1"></i>刷新状态
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user