增加了图表[0.2.7.2][ci]
This commit is contained in:
@@ -589,6 +589,25 @@ def analytics_recent(limit: int = 10, gte: str = None, lte: str = None):
|
|||||||
pass
|
pass
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def _extract_detail(s: str):
|
||||||
|
if not s:
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
obj = json.loads(s)
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
# 尝试获取常见的标题字段
|
||||||
|
for key in ["标题", "名称", "项目名称", "成果名称", "软件名称", "专利名称", "获奖名称", "证书名称", "姓名"]:
|
||||||
|
v = obj.get(key)
|
||||||
|
if isinstance(v, str) and v:
|
||||||
|
return v
|
||||||
|
# 如果没有找到常见标题,尝试获取第一个非"数据类型"的字符串值
|
||||||
|
for k, v in obj.items():
|
||||||
|
if k != "数据类型" and isinstance(v, str) and v and len(v) < 50:
|
||||||
|
return v
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return ""
|
||||||
|
|
||||||
search = AchievementDocument.search()
|
search = AchievementDocument.search()
|
||||||
body = {
|
body = {
|
||||||
"size": max(1, min(limit, 100)),
|
"size": max(1, min(limit, 100)),
|
||||||
@@ -619,11 +638,13 @@ def analytics_recent(limit: int = 10, gte: str = None, lte: str = None):
|
|||||||
except Exception:
|
except Exception:
|
||||||
uname = None
|
uname = None
|
||||||
tval = _extract_type(getattr(hit, 'data', ''))
|
tval = _extract_type(getattr(hit, 'data', ''))
|
||||||
|
dval = _extract_detail(getattr(hit, 'data', ''))
|
||||||
results.append({
|
results.append({
|
||||||
"_id": hit.meta.id,
|
"_id": hit.meta.id,
|
||||||
"writer_id": w,
|
"writer_id": w,
|
||||||
"username": uname or "",
|
"username": uname or "",
|
||||||
"type": tval or "",
|
"type": tval or "",
|
||||||
|
"detail": dval or "",
|
||||||
"time": getattr(hit, 'time', None)
|
"time": getattr(hit, 'time', None)
|
||||||
})
|
})
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -67,7 +67,10 @@
|
|||||||
<div id="chartTrend" style="width:100%;height:320px;"></div>
|
<div id="chartTrend" style="width:100%;height:320px;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="header"><h3>类型占比(近30天)</h3></div>
|
<div class="header">
|
||||||
|
<h3>类型占比(近30天)</h3>
|
||||||
|
<button id="toggleTypesChartBtn" class="btn btn-primary" style="font-size: 12px; padding: 4px 8px;">切换图表</button>
|
||||||
|
</div>
|
||||||
<div id="chartTypes" style="width:100%;height:320px;"></div>
|
<div id="chartTypes" style="width:100%;height:320px;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -170,19 +173,74 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let typesChartData = [];
|
||||||
|
let currentChartType = 'pie';
|
||||||
|
let typesChartInterval = null;
|
||||||
|
|
||||||
async function loadTypes(){
|
async function loadTypes(){
|
||||||
const url = '/elastic/analytics/types/?' + qs({ from:'now-30d', to:'now', size:10 });
|
const url = '/elastic/analytics/types/?' + qs({ from:'now-30d', to:'now', size:10 });
|
||||||
const res = await fetchJSON(url);
|
const res = await fetchJSON(url);
|
||||||
if(res.status!=='success') return;
|
if(res.status!=='success') return;
|
||||||
const buckets = res.data || [];
|
const buckets = res.data || [];
|
||||||
const data = buckets.map(b=>({ name: String(b.key||'未知'), value: b.doc_count||0 }));
|
typesChartData = buckets.map(b=>({ name: String(b.key||'未知'), value: b.doc_count||0 }));
|
||||||
typesChart.setOption({
|
renderTypesChart();
|
||||||
tooltip:{trigger:'item'},
|
startTypesChartRotation();
|
||||||
legend:{type:'scroll'},
|
|
||||||
series:[{ type:'pie', radius:['40%','70%'], data }]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderTypesChart() {
|
||||||
|
if (currentChartType === 'pie') {
|
||||||
|
typesChart.setOption({
|
||||||
|
tooltip:{trigger:'item'},
|
||||||
|
legend:{type:'scroll', top:'bottom'},
|
||||||
|
grid: { top: 0, bottom: 0, left: 0, right: 0 },
|
||||||
|
xAxis: { show: false },
|
||||||
|
yAxis: { show: false },
|
||||||
|
series:[{
|
||||||
|
type:'pie',
|
||||||
|
radius:['40%','70%'],
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
data: typesChartData,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 }
|
||||||
|
}]
|
||||||
|
}, true);
|
||||||
|
} else {
|
||||||
|
const names = typesChartData.map(d => d.name);
|
||||||
|
const values = typesChartData.map(d => d.value);
|
||||||
|
typesChart.setOption({
|
||||||
|
tooltip:{trigger:'axis', axisPointer:{type:'shadow'}},
|
||||||
|
legend:{show: false},
|
||||||
|
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||||
|
xAxis: { type: 'category', data: names, show: true },
|
||||||
|
yAxis: { type: 'value', show: true },
|
||||||
|
series: [{
|
||||||
|
type: 'bar',
|
||||||
|
data: values,
|
||||||
|
itemStyle: { color: '#5470c6' },
|
||||||
|
barWidth: '60%'
|
||||||
|
}]
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleChartType() {
|
||||||
|
currentChartType = currentChartType === 'pie' ? 'bar' : 'pie';
|
||||||
|
renderTypesChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTypesChartRotation() {
|
||||||
|
if (typesChartInterval) clearInterval(typesChartInterval);
|
||||||
|
typesChartInterval = setInterval(() => {
|
||||||
|
toggleChartType();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('toggleTypesChartBtn').addEventListener('click', () => {
|
||||||
|
toggleChartType();
|
||||||
|
// Reset timer on manual interaction
|
||||||
|
startTypesChartRotation();
|
||||||
|
});
|
||||||
|
|
||||||
async function loadTypesTrend(){
|
async function loadTypesTrend(){
|
||||||
const url = '/elastic/analytics/types_trend/?' + qs({ from:'now-180d', to:'now', interval:'week', size:6 });
|
const url = '/elastic/analytics/types_trend/?' + qs({ from:'now-180d', to:'now', interval:'week', size:6 });
|
||||||
const res = await fetchJSON(url);
|
const res = await fetchJSON(url);
|
||||||
@@ -233,7 +291,8 @@
|
|||||||
const t = formatTime(it.time);
|
const t = formatTime(it.time);
|
||||||
const u = it.username || '';
|
const u = it.username || '';
|
||||||
const ty = it.type || '未知';
|
const ty = it.type || '未知';
|
||||||
li.textContent = `${t},${u},${ty}`;
|
const de = it.detail ? `,${it.detail}` : '';
|
||||||
|
li.textContent = `${t},${u},${ty}${de}`;
|
||||||
listEl.appendChild(li);
|
listEl.appendChild(li);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user