修复AI建议逻辑和字段映射问题
- 修复AI建议基于问题描述而不是处理过程生成 - 修复工单详情页面显示逻辑 - 修复飞书时间字段处理(毫秒时间戳转换) - 优化字段映射和转换逻辑 - 添加飞书集成功能 - 改进对话历史合并功能 - 优化系统优化反馈机制
This commit is contained in:
@@ -450,25 +450,25 @@
|
||||
<!-- 仪表板标签页 -->
|
||||
<div id="dashboard-tab" class="tab-content">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card success">
|
||||
<div class="stat-number" id="total-sessions">0</div>
|
||||
<div class="stat-label">活跃会话</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card warning">
|
||||
<div class="stat-number" id="total-alerts">0</div>
|
||||
<div class="stat-label">活跃预警</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card danger">
|
||||
<div class="stat-number" id="total-workorders">0</div>
|
||||
<div class="stat-label">待处理工单</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card info">
|
||||
<div class="stat-number" id="knowledge-count">0</div>
|
||||
<div class="stat-label">知识条目</div>
|
||||
@@ -1380,25 +1380,25 @@
|
||||
<!-- 系统优化标签页 -->
|
||||
<div id="system-optimizer-tab" class="tab-content" style="display: none;">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card success">
|
||||
<div class="stat-number" id="cpu-usage">0%</div>
|
||||
<div class="stat-label">CPU使用率</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card warning">
|
||||
<div class="stat-number" id="memory-usage-percent">0%</div>
|
||||
<div class="stat-label">内存使用率</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card danger">
|
||||
<div class="stat-number" id="disk-usage">0%</div>
|
||||
<div class="stat-label">磁盘使用率</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card info">
|
||||
<div class="stat-number" id="network-latency">0ms</div>
|
||||
<div class="stat-label">网络延迟</div>
|
||||
@@ -1440,6 +1440,14 @@
|
||||
<i class="fas fa-hdd me-1"></i>优化磁盘
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-success btn-sm" onclick="dashboard.optimizeAll()">
|
||||
<i class="fas fa-magic me-1"></i>一键优化
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="dashboard.clearCache()">
|
||||
<i class="fas fa-broom me-1"></i>清理缓存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
662
src/web/templates/feishu_sync.html
Normal file
662
src/web/templates/feishu_sync.html
Normal file
@@ -0,0 +1,662 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>飞书同步管理 - TSP助手</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="/static/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- 侧边栏 -->
|
||||
<nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
|
||||
<div class="position-sticky pt-3">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">
|
||||
<i class="fas fa-tachometer-alt me-2"></i>仪表板
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/alerts">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>预警管理
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/workorders">
|
||||
<i class="fas fa-tasks me-2"></i>工单管理
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/conversations">
|
||||
<i class="fas fa-comments me-2"></i>对话历史
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/knowledge">
|
||||
<i class="fas fa-book me-2"></i>知识库
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/feishu-sync">
|
||||
<i class="fas fa-sync me-2"></i>飞书同步
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/monitoring">
|
||||
<i class="fas fa-chart-line me-2"></i>系统监控
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/settings">
|
||||
<i class="fas fa-cog me-2"></i>系统设置
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">
|
||||
<i class="fas fa-sync me-2"></i>飞书同步管理
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- 配置区域 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-cog me-2"></i>飞书配置
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="feishuConfigForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="appId" class="form-label">应用ID</label>
|
||||
<input type="text" class="form-control" id="appId" placeholder="cli_xxxxxxxxxx">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="appSecret" class="form-label">应用密钥</label>
|
||||
<input type="password" class="form-control" id="appSecret" placeholder="输入应用密钥">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="appToken" class="form-label">应用Token</label>
|
||||
<input type="text" class="form-control" id="appToken" placeholder="XXnEbiCmEaMblSs6FDJcFCqsnIg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="tableId" class="form-label">表格ID</label>
|
||||
<input type="text" class="form-control" id="tableId" placeholder="tblnl3vJPpgMTSiP">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<button type="button" class="btn btn-primary" onclick="feishuSync.saveConfig()">
|
||||
<i class="fas fa-save me-1"></i>保存配置
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="feishuSync.testConnection()">
|
||||
<i class="fas fa-plug me-1"></i>测试连接
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-info" onclick="feishuSync.exportConfig()">
|
||||
<i class="fas fa-download me-1"></i>导出配置
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-warning" onclick="feishuSync.showImportModal()">
|
||||
<i class="fas fa-upload me-1"></i>导入配置
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" onclick="feishuSync.resetConfig()">
|
||||
<i class="fas fa-undo me-1"></i>重置配置
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 同步状态 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-primary" id="totalLocalWorkorders">0</h5>
|
||||
<p class="card-text">本地工单总数</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-success" id="syncedWorkorders">0</h5>
|
||||
<p class="card-text">已同步工单</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-warning" id="unsyncedWorkorders">0</h5>
|
||||
<p class="card-text">未同步工单</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 同步操作 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-sync-alt me-2"></i>同步操作
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex gap-2 mb-3">
|
||||
<button class="btn btn-success" onclick="feishuSync.syncFromFeishu()">
|
||||
<i class="fas fa-download me-1"></i>从飞书同步
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick="feishuSync.syncWithAI()">
|
||||
<i class="fas fa-robot me-1"></i>同步+AI建议
|
||||
</button>
|
||||
<button class="btn btn-info" onclick="feishuSync.previewFeishuData()">
|
||||
<i class="fas fa-eye me-1"></i>预览飞书数据
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="feishuSync.refreshStatus()">
|
||||
<i class="fas fa-refresh me-1"></i>刷新状态
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="syncLimit" class="form-label">同步数量限制:</label>
|
||||
<select class="form-select" id="syncLimit" style="width: auto; display: inline-block;">
|
||||
<option value="10">前10条</option>
|
||||
<option value="20">前20条</option>
|
||||
<option value="50">前50条</option>
|
||||
<option value="100">前100条</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 同步进度 -->
|
||||
<div class="progress mb-3" id="syncProgress" style="display: none;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
||||
role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
|
||||
<!-- 同步日志 -->
|
||||
<div class="mt-3">
|
||||
<h6>同步日志</h6>
|
||||
<div id="syncLog" class="bg-light p-3 rounded" style="max-height: 300px; overflow-y: auto;">
|
||||
<div class="text-muted">暂无同步记录</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 预览数据 -->
|
||||
<div class="row" id="previewSection" style="display: none;">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-table me-2"></i>飞书数据预览
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="previewTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>记录ID</th>
|
||||
<th>TR编号</th>
|
||||
<th>TR描述</th>
|
||||
<th>问题类型</th>
|
||||
<th>来源</th>
|
||||
<th>优先级/状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导入配置模态框 -->
|
||||
<div class="modal fade" id="importConfigModal" tabindex="-1" aria-labelledby="importConfigModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="importConfigModalLabel">导入配置</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="configJson" class="form-label">配置JSON数据:</label>
|
||||
<textarea class="form-control" id="configJson" rows="10" placeholder="粘贴导出的配置JSON数据..."></textarea>
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
导入配置将覆盖当前所有配置,请谨慎操作!
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" onclick="feishuSync.importConfig()">导入</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知容器 -->
|
||||
<div id="notificationContainer" class="position-fixed top-0 end-0 p-3" style="z-index: 1050;"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
class FeishuSyncManager {
|
||||
constructor() {
|
||||
this.loadConfig();
|
||||
this.refreshStatus();
|
||||
}
|
||||
|
||||
async loadConfig() {
|
||||
try {
|
||||
const response = await fetch('/api/feishu-sync/config');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
const config = data.config;
|
||||
document.getElementById('appId').value = config.feishu.app_id || '';
|
||||
document.getElementById('appSecret').value = '';
|
||||
document.getElementById('appToken').value = config.feishu.app_token || '';
|
||||
document.getElementById('tableId').value = config.feishu.table_id || '';
|
||||
|
||||
// 显示配置状态
|
||||
const statusBadge = config.feishu.status === 'active' ?
|
||||
'<span class="badge bg-success">已配置</span>' :
|
||||
'<span class="badge bg-warning">未配置</span>';
|
||||
|
||||
// 可以在这里添加状态显示
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载配置失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async saveConfig() {
|
||||
const config = {
|
||||
app_id: document.getElementById('appId').value,
|
||||
app_secret: document.getElementById('appSecret').value,
|
||||
app_token: document.getElementById('appToken').value,
|
||||
table_id: document.getElementById('tableId').value
|
||||
};
|
||||
|
||||
if (!config.app_id || !config.app_secret || !config.app_token || !config.table_id) {
|
||||
this.showNotification('请填写完整的配置信息', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/feishu-sync/config', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification('配置保存成功', 'success');
|
||||
} else {
|
||||
this.showNotification('配置保存失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('配置保存失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async testConnection() {
|
||||
try {
|
||||
this.showNotification('正在测试连接...', 'info');
|
||||
|
||||
const response = await fetch('/api/feishu-sync/test-connection');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification('飞书连接正常', 'success');
|
||||
} else {
|
||||
this.showNotification('连接失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('连接测试失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async syncFromFeishu() {
|
||||
try {
|
||||
const limit = document.getElementById('syncLimit').value;
|
||||
this.showNotification('开始从飞书同步数据...', 'info');
|
||||
this.showProgress(true);
|
||||
|
||||
const response = await fetch('/api/feishu-sync/sync-from-feishu', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
generate_ai_suggestions: false,
|
||||
limit: parseInt(limit)
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
this.addSyncLog(data.message);
|
||||
this.refreshStatus();
|
||||
} else {
|
||||
this.showNotification('同步失败: ' + data.error, 'error');
|
||||
this.addSyncLog('同步失败: ' + data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('同步失败: ' + error.message, 'error');
|
||||
this.addSyncLog('同步失败: ' + error.message);
|
||||
} finally {
|
||||
this.showProgress(false);
|
||||
}
|
||||
}
|
||||
|
||||
async syncWithAI() {
|
||||
try {
|
||||
const limit = document.getElementById('syncLimit').value;
|
||||
this.showNotification('开始同步数据并生成AI建议...', 'info');
|
||||
this.showProgress(true);
|
||||
|
||||
const response = await fetch('/api/feishu-sync/sync-from-feishu', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
generate_ai_suggestions: true,
|
||||
limit: parseInt(limit)
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
this.addSyncLog(data.message);
|
||||
this.refreshStatus();
|
||||
} else {
|
||||
this.showNotification('同步失败: ' + data.error, 'error');
|
||||
this.addSyncLog('同步失败: ' + data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('同步失败: ' + error.message, 'error');
|
||||
this.addSyncLog('同步失败: ' + error.message);
|
||||
} finally {
|
||||
this.showProgress(false);
|
||||
}
|
||||
}
|
||||
|
||||
async previewFeishuData() {
|
||||
try {
|
||||
this.showNotification('正在获取飞书数据预览...', 'info');
|
||||
|
||||
const response = await fetch('/api/feishu-sync/preview-feishu-data');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.displayPreviewData(data.preview_data);
|
||||
this.showNotification(`获取到 ${data.total_count} 条预览数据`, 'success');
|
||||
} else {
|
||||
this.showNotification('获取预览数据失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('获取预览数据失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
displayPreviewData(data) {
|
||||
const tbody = document.querySelector('#previewTable tbody');
|
||||
tbody.innerHTML = '';
|
||||
|
||||
data.forEach(item => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${item.record_id}</td>
|
||||
<td>${item.fields['TR Number'] || '-'}</td>
|
||||
<td>${item.fields['TR Description'] || '-'}</td>
|
||||
<td>${item.fields['Type of problem'] || '-'}</td>
|
||||
<td>${item.fields['Source'] || '-'}</td>
|
||||
<td>${item.fields['TR (Priority/Status)'] || '-'}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" onclick="feishuSync.createWorkorder('${item.record_id}')">
|
||||
<i class="fas fa-plus"></i> 创建工单
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
document.getElementById('previewSection').style.display = 'block';
|
||||
}
|
||||
|
||||
async refreshStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/feishu-sync/status');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
const status = data.status;
|
||||
document.getElementById('totalLocalWorkorders').textContent = status.total_local_workorders || 0;
|
||||
document.getElementById('syncedWorkorders').textContent = status.synced_workorders || 0;
|
||||
document.getElementById('unsyncedWorkorders').textContent = status.unsynced_workorders || 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('刷新状态失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
showProgress(show) {
|
||||
const progress = document.getElementById('syncProgress');
|
||||
if (show) {
|
||||
progress.style.display = 'block';
|
||||
const bar = progress.querySelector('.progress-bar');
|
||||
bar.style.width = '100%';
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
progress.style.display = 'none';
|
||||
const bar = progress.querySelector('.progress-bar');
|
||||
bar.style.width = '0%';
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
addSyncLog(message) {
|
||||
const log = document.getElementById('syncLog');
|
||||
const timestamp = new Date().toLocaleString();
|
||||
const logEntry = document.createElement('div');
|
||||
logEntry.innerHTML = `<small class="text-muted">[${timestamp}]</small> ${message}`;
|
||||
|
||||
if (log.querySelector('.text-muted')) {
|
||||
log.innerHTML = '';
|
||||
}
|
||||
|
||||
log.appendChild(logEntry);
|
||||
log.scrollTop = log.scrollHeight;
|
||||
}
|
||||
|
||||
async exportConfig() {
|
||||
try {
|
||||
const response = await fetch('/api/feishu-sync/config/export');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// 创建下载链接
|
||||
const blob = new Blob([data.config], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `feishu_config_${new Date().toISOString().split('T')[0]}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
this.showNotification('配置导出成功', 'success');
|
||||
} else {
|
||||
this.showNotification('配置导出失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('配置导出失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
showImportModal() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('importConfigModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
async importConfig() {
|
||||
try {
|
||||
const configJson = document.getElementById('configJson').value.trim();
|
||||
|
||||
if (!configJson) {
|
||||
this.showNotification('请输入配置JSON数据', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch('/api/feishu-sync/config/import', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ config: configJson })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification('配置导入成功', 'success');
|
||||
this.loadConfig();
|
||||
this.refreshStatus();
|
||||
|
||||
// 关闭模态框
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('importConfigModal'));
|
||||
modal.hide();
|
||||
document.getElementById('configJson').value = '';
|
||||
} else {
|
||||
this.showNotification('配置导入失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('配置导入失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async resetConfig() {
|
||||
if (confirm('确定要重置所有配置吗?此操作不可撤销!')) {
|
||||
try {
|
||||
const response = await fetch('/api/feishu-sync/config/reset', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification('配置重置成功', 'success');
|
||||
this.loadConfig();
|
||||
this.refreshStatus();
|
||||
} else {
|
||||
this.showNotification('配置重置失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('配置重置失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createWorkorder(recordId) {
|
||||
if (confirm(`确定要从飞书记录 ${recordId} 创建工单吗?`)) {
|
||||
try {
|
||||
this.showNotification('正在创建工单...', 'info');
|
||||
|
||||
const response = await fetch('/api/feishu-sync/create-workorder', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
record_id: recordId
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
// 刷新工单列表(如果用户在工单页面)
|
||||
if (typeof window.refreshWorkOrders === 'function') {
|
||||
window.refreshWorkOrders();
|
||||
}
|
||||
} else {
|
||||
this.showNotification('创建工单失败: ' + data.message, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showNotification('创建工单失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showNotification(message, type = 'info') {
|
||||
const container = document.getElementById('notificationContainer');
|
||||
const alert = document.createElement('div');
|
||||
alert.className = `alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show`;
|
||||
alert.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
container.appendChild(alert);
|
||||
|
||||
setTimeout(() => {
|
||||
if (alert.parentNode) {
|
||||
alert.parentNode.removeChild(alert);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
const feishuSync = new FeishuSyncManager();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user