Bilingual support, with full system support for Chinese and English switching.
This commit is contained in:
@@ -142,15 +142,27 @@ def not_found_error(error):
|
|||||||
|
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
def internal_error(error):
|
def internal_error(error):
|
||||||
return render_template('500.html'), 500
|
return render_template('error.html',
|
||||||
|
error_code=500,
|
||||||
|
error_title='服务器错误',
|
||||||
|
error_message='服务器遇到了一个问题,请稍后再试。',
|
||||||
|
error_i18n_key='serverError'), 500
|
||||||
|
|
||||||
@app.errorhandler(403)
|
@app.errorhandler(403)
|
||||||
def forbidden_error(error):
|
def forbidden_error(error):
|
||||||
return render_template('403.html'), 403
|
return render_template('error.html',
|
||||||
|
error_code=403,
|
||||||
|
error_title='禁止访问',
|
||||||
|
error_message='您没有权限访问此页面。',
|
||||||
|
error_i18n_key='forbidden'), 403
|
||||||
|
|
||||||
@app.errorhandler(400)
|
@app.errorhandler(400)
|
||||||
def bad_request_error(error):
|
def bad_request_error(error):
|
||||||
return render_template('400.html'), 400
|
return render_template('error.html',
|
||||||
|
error_code=400,
|
||||||
|
error_title='错误请求',
|
||||||
|
error_message='服务器无法理解您的请求。',
|
||||||
|
error_i18n_key='badRequest'), 400
|
||||||
|
|
||||||
# 数据库配置
|
# 数据库配置
|
||||||
DB_CONFIG = {
|
DB_CONFIG = {
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
// 多语言支持文件
|
||||||
|
const translations = {
|
||||||
|
'zh': {
|
||||||
|
// 导航菜单
|
||||||
|
'home': '首页',
|
||||||
|
'hotWord': '热词统计',
|
||||||
|
'tableData': '微博舆情统计',
|
||||||
|
'articleChar': '文章分析',
|
||||||
|
'ipChar': 'IP分析',
|
||||||
|
'commentChar': '评论分析',
|
||||||
|
'yuqingChar': '舆情分析',
|
||||||
|
'yuqingpredict': '舆情预测',
|
||||||
|
'articleCloud': '文章内容词云图',
|
||||||
|
'dataVisualization': '数据可视化',
|
||||||
|
'weiboSystem': '微博舆情分析系统',
|
||||||
|
'wordCloud': '词云图',
|
||||||
|
|
||||||
|
// 首页
|
||||||
|
'articleCount': '文章个数',
|
||||||
|
'articleCrawlRule': '文章爬取规则',
|
||||||
|
'nextCrawlTime': '下次爬取时间',
|
||||||
|
'articlePublishTimeCount': '文章发布时间个数',
|
||||||
|
'commentLikeCountTopFore': '评论点赞量 Top Fore',
|
||||||
|
'viewAll': '查看全部',
|
||||||
|
'articleTypeRatio': '文章类型占比',
|
||||||
|
'commentUserWordCloud': '评论用户名词云图',
|
||||||
|
'commentUserTimeRatio': '评论用户时间占比',
|
||||||
|
|
||||||
|
// 热词页面
|
||||||
|
'hotWordStatistics': '热词统计页',
|
||||||
|
'hotWordCloud': '热词词云图',
|
||||||
|
'hotWordRanking': '热词查询表格',
|
||||||
|
'wordFrequency': '词频',
|
||||||
|
'hotWordSelection': '热词选择',
|
||||||
|
'hotWordName': '热词名称',
|
||||||
|
'occurrenceCount': '出现次数',
|
||||||
|
'hotWordSentiment': '热词情感',
|
||||||
|
'hotWordYearTrend': '热词年份变化趋势',
|
||||||
|
'queryCommentsByHotWord': '根据选择的热词从而查询出评论数据',
|
||||||
|
'hotWordTimeDistribution': '热词出现时间分布个数',
|
||||||
|
|
||||||
|
// 舆情分析页面
|
||||||
|
'hotWordSentimentTrendBar': '热词情感趋势柱状图',
|
||||||
|
'hotWordSentimentTrendTree': '热词情感趋势树形图',
|
||||||
|
'articleCommentSentimentTrendPie': '文章内容与评论内容舆情趋势饼状图',
|
||||||
|
|
||||||
|
// 舆情预测页面
|
||||||
|
'topicStatisticsPage': '话题统计页',
|
||||||
|
|
||||||
|
// 文章分析页面
|
||||||
|
'articleCharPage': '文章分析页',
|
||||||
|
'typeSelection': '类型选择',
|
||||||
|
'articleLikeAnalysis': '文章点赞量分析 👍',
|
||||||
|
'articleCommentAnalysis': '文章评论量分析 🔥',
|
||||||
|
'articleForwardAnalysis': '文章转发量分析 🥇',
|
||||||
|
'likeRangeStatistics': '点赞区间统计',
|
||||||
|
'rangeCount': '区间个数',
|
||||||
|
|
||||||
|
// 评论分析页面
|
||||||
|
'commentLikeRangeChart': '评论点赞次数区间图',
|
||||||
|
'commentUserGenderRatio': '评论用户性别占比',
|
||||||
|
'userCommentWordCloud': '用户评论词云图',
|
||||||
|
|
||||||
|
// IP分析页面
|
||||||
|
'articleIpLocationAnalysis': '文章IP位置分析图',
|
||||||
|
'commentIpLocationAnalysis': '评论IP位置分析图',
|
||||||
|
|
||||||
|
// 评论相关
|
||||||
|
'commentUser': '评论用户',
|
||||||
|
'commentGender': '评论性别',
|
||||||
|
'commentAddress': '评论地址',
|
||||||
|
'commentContent': '评论内容',
|
||||||
|
'likeCount': '点赞量',
|
||||||
|
|
||||||
|
// 微博舆情统计页面
|
||||||
|
'weiboArticleStatTable': '微博文章统计表格 - 舆情 情感分类',
|
||||||
|
'sentimentClassification': '情感分类',
|
||||||
|
'articleId': '文章ID',
|
||||||
|
'articleIp': '文章IP',
|
||||||
|
'articleTitle': '文章标题',
|
||||||
|
'articleLike': '点赞量',
|
||||||
|
'articleForward': '转发量',
|
||||||
|
'articleComment': '评论量',
|
||||||
|
'articleType': '类型',
|
||||||
|
'articleContent': '内容',
|
||||||
|
'articleTime': '发布时间',
|
||||||
|
|
||||||
|
// 通用
|
||||||
|
'switchToEnglish': '切换到英文',
|
||||||
|
'switchToChinese': '切换到中文',
|
||||||
|
'semester': '网安小学期',
|
||||||
|
|
||||||
|
// 错误页面
|
||||||
|
'pageNotFound': '页面未找到',
|
||||||
|
'backToHome': '返回首页',
|
||||||
|
'serverError': '服务器错误',
|
||||||
|
'forbidden': '禁止访问',
|
||||||
|
'badRequest': '错误请求'
|
||||||
|
},
|
||||||
|
'en': {
|
||||||
|
// Navigation menu
|
||||||
|
'home': 'Home',
|
||||||
|
'hotWord': 'Hot Words',
|
||||||
|
'tableData': 'Weibo Public Opinion Stats',
|
||||||
|
'articleChar': 'Article Analysis',
|
||||||
|
'ipChar': 'IP Analysis',
|
||||||
|
'commentChar': 'Comment Analysis',
|
||||||
|
'yuqingChar': 'Public Opinion Analysis',
|
||||||
|
'yuqingpredict': 'Opinion Prediction',
|
||||||
|
'articleCloud': 'Article Content Word Cloud',
|
||||||
|
'dataVisualization': 'Data Visualization',
|
||||||
|
'weiboSystem': 'Weibo Public Opinion Analysis System',
|
||||||
|
'wordCloud': 'Word Cloud',
|
||||||
|
|
||||||
|
// Home page
|
||||||
|
'articleCount': 'Article Count',
|
||||||
|
'articleCrawlRule': 'Article Crawl Rule',
|
||||||
|
'nextCrawlTime': 'Next Crawl Time',
|
||||||
|
'articlePublishTimeCount': 'Article Publish Time Count',
|
||||||
|
'commentLikeCountTopFore': 'Comment Like Count Top Four',
|
||||||
|
'viewAll': 'View All',
|
||||||
|
'articleTypeRatio': 'Article Type Ratio',
|
||||||
|
'commentUserWordCloud': 'Comment User Word Cloud',
|
||||||
|
'commentUserTimeRatio': 'Comment User Time Ratio',
|
||||||
|
|
||||||
|
// Hot word page
|
||||||
|
'hotWordStatistics': 'Hot Word Statistics',
|
||||||
|
'hotWordCloud': 'Hot Word Cloud',
|
||||||
|
'hotWordRanking': 'Hot Word Ranking',
|
||||||
|
'wordFrequency': 'Word Frequency',
|
||||||
|
'hotWordSelection': 'Hot Word Selection',
|
||||||
|
'hotWordName': 'Hot Word Name',
|
||||||
|
'occurrenceCount': 'Occurrence Count',
|
||||||
|
'hotWordSentiment': 'Hot Word Sentiment',
|
||||||
|
'hotWordYearTrend': 'Hot Word Year Trend',
|
||||||
|
'queryCommentsByHotWord': 'Query comments based on selected hot word',
|
||||||
|
'hotWordTimeDistribution': 'Hot Word Time Distribution Count',
|
||||||
|
|
||||||
|
// Public opinion analysis page
|
||||||
|
'hotWordSentimentTrendBar': 'Hot Word Sentiment Trend Bar Chart',
|
||||||
|
'hotWordSentimentTrendTree': 'Hot Word Sentiment Trend Tree Chart',
|
||||||
|
'articleCommentSentimentTrendPie': 'Article and Comment Sentiment Trend Pie Chart',
|
||||||
|
|
||||||
|
// Opinion prediction page
|
||||||
|
'topicStatisticsPage': 'Topic Statistics Page',
|
||||||
|
|
||||||
|
// Article analysis page
|
||||||
|
'articleCharPage': 'Article Analysis Page',
|
||||||
|
'typeSelection': 'Type Selection',
|
||||||
|
'articleLikeAnalysis': 'Article Like Analysis 👍',
|
||||||
|
'articleCommentAnalysis': 'Article Comment Analysis 🔥',
|
||||||
|
'articleForwardAnalysis': 'Article Forward Analysis 🥇',
|
||||||
|
'likeRangeStatistics': 'Like Range Statistics',
|
||||||
|
'rangeCount': 'Range Count',
|
||||||
|
|
||||||
|
// Comment analysis page
|
||||||
|
'commentLikeRangeChart': 'Comment Like Range Chart',
|
||||||
|
'commentUserGenderRatio': 'Comment User Gender Ratio',
|
||||||
|
'userCommentWordCloud': 'User Comment Word Cloud',
|
||||||
|
|
||||||
|
// IP analysis page
|
||||||
|
'articleIpLocationAnalysis': 'Article IP Location Analysis',
|
||||||
|
'commentIpLocationAnalysis': 'Comment IP Location Analysis',
|
||||||
|
|
||||||
|
// Comment related
|
||||||
|
'commentUser': 'Comment User',
|
||||||
|
'commentGender': 'Gender',
|
||||||
|
'commentAddress': 'Address',
|
||||||
|
'commentContent': 'Content',
|
||||||
|
'likeCount': 'Likes',
|
||||||
|
|
||||||
|
// Weibo public opinion stats page
|
||||||
|
'weiboArticleStatTable': 'Weibo Article Statistics Table - Sentiment Classification',
|
||||||
|
'sentimentClassification': 'Sentiment Classification',
|
||||||
|
'articleId': 'Article ID',
|
||||||
|
'articleIp': 'Article IP',
|
||||||
|
'articleTitle': 'Article Title',
|
||||||
|
'articleLike': 'Likes',
|
||||||
|
'articleForward': 'Forwards',
|
||||||
|
'articleComment': 'Comments',
|
||||||
|
'articleType': 'Type',
|
||||||
|
'articleContent': 'Content',
|
||||||
|
'articleTime': 'Publish Time',
|
||||||
|
|
||||||
|
// Common
|
||||||
|
'switchToEnglish': 'Switch to English',
|
||||||
|
'switchToChinese': 'Switch to Chinese',
|
||||||
|
'semester': 'Network Security Semester',
|
||||||
|
|
||||||
|
// Error pages
|
||||||
|
'pageNotFound': 'Page Not Found',
|
||||||
|
'backToHome': 'Back to Home',
|
||||||
|
'serverError': 'Server Error',
|
||||||
|
'forbidden': 'Forbidden',
|
||||||
|
'badRequest': 'Bad Request'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取当前语言
|
||||||
|
function getCurrentLanguage() {
|
||||||
|
return localStorage.getItem('language') || 'zh';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置语言
|
||||||
|
function setLanguage(lang) {
|
||||||
|
localStorage.setItem('language', lang);
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 翻译函数
|
||||||
|
function t(key) {
|
||||||
|
const lang = getCurrentLanguage();
|
||||||
|
return translations[lang][key] || key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时应用翻译
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 应用当前语言
|
||||||
|
applyTranslations();
|
||||||
|
|
||||||
|
// 添加语言切换按钮事件
|
||||||
|
const langSwitcher = document.getElementById('language-switcher');
|
||||||
|
if (langSwitcher) {
|
||||||
|
langSwitcher.addEventListener('click', function() {
|
||||||
|
const currentLang = getCurrentLanguage();
|
||||||
|
const newLang = currentLang === 'zh' ? 'en' : 'zh';
|
||||||
|
setLanguage(newLang);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 应用翻译到页面元素
|
||||||
|
function applyTranslations() {
|
||||||
|
const elements = document.querySelectorAll('[data-i18n]');
|
||||||
|
elements.forEach(el => {
|
||||||
|
const key = el.getAttribute('data-i18n');
|
||||||
|
el.textContent = t(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
+57
-28
@@ -1,29 +1,58 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>404页面</title>
|
<title>404 - 页面未找到</title>
|
||||||
<link rel="stylesheet" href="/static/css/backend-plugin.min.css">
|
<link rel="stylesheet" href="/static/css/backend.css">
|
||||||
<link rel="stylesheet" href="/static/css/backend.css"> </head>
|
<!-- 添加语言支持脚本 -->
|
||||||
<body class=" ">
|
<script src="/static/js/i18n.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
<div class="wrapper">
|
display: flex;
|
||||||
<div class="container">
|
justify-content: center;
|
||||||
<div class="row no-gutters height-self-center">
|
align-items: center;
|
||||||
<div class="col-sm-12 text-center align-self-center">
|
height: 100vh;
|
||||||
<div class="iq-error position-relative">
|
margin: 0;
|
||||||
<img src="/static/picture/Datum_404.png" class="img-fluid iq-error-img iq-error-img-dark mx-auto" alt="">
|
background-color: #f8f9fa;
|
||||||
<img src="/static/picture/Datum_404.png" class="img-fluid iq-error-img mb-0" alt="">
|
}
|
||||||
<h2 class="mb-0">噢!该页面没有找到..</h2>
|
.error-container {
|
||||||
<p>本次请求没有任何反应.</p>
|
text-align: center;
|
||||||
<a class="btn btn-primary d-inline-flex align-items-center mt-3" href="/user/login"><i class="ri-home-4-line mr-2"></i>回到登录页</a>
|
padding: 2rem;
|
||||||
</div>
|
}
|
||||||
</div>
|
.error-code {
|
||||||
</div>
|
font-size: 6rem;
|
||||||
</div>
|
font-weight: bold;
|
||||||
</div>
|
color: #dc3545;
|
||||||
/
|
}
|
||||||
</body>
|
.error-message {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
.back-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="error-container">
|
||||||
|
<div class="error-code">404</div>
|
||||||
|
<div class="error-message" data-i18n="pageNotFound">页面未找到</div>
|
||||||
|
<p>您请求的页面不存在或已被移除。</p>
|
||||||
|
<a href="/" class="back-link" data-i18n="backToHome">返回首页</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 初始化语言支持 -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
applyTranslations();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+57
-32
@@ -1,33 +1,58 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>错误页面</title>
|
<title>{{ error_code }} - {{ error_title }}</title>
|
||||||
|
<link rel="stylesheet" href="/static/css/backend.css">
|
||||||
<link rel="stylesheet" href="/static/css/backend-plugin.min.css">
|
<!-- 添加语言支持脚本 -->
|
||||||
<link rel="stylesheet" href="/static/css/backend.css"> </head>
|
<script src="/static/js/i18n.js"></script>
|
||||||
<body class=" ">
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
<div class="wrapper">
|
justify-content: center;
|
||||||
<div class="mt-5 iq-maintenance">
|
align-items: center;
|
||||||
<div class="container-fluid p-0">
|
height: 100vh;
|
||||||
<div class="row no-gutters">
|
margin: 0;
|
||||||
<div class="col-sm-12 text-center">
|
background-color: #f8f9fa;
|
||||||
<div class="iq-maintenance">
|
}
|
||||||
<img src="/static/picture/maintenance.png" class="img-fluid" alt="">
|
.error-container {
|
||||||
<h3 class="mt-4 mb-2">{{ errorMsg }}</h3>
|
text-align: center;
|
||||||
<p class="mb-2">请回去再次检查问题并且修改问题.</p>
|
padding: 2rem;
|
||||||
<p><a href="/user/login" class="btn btn-primary">回到登录页</a></p>
|
}
|
||||||
</div>
|
.error-code {
|
||||||
</div>
|
font-size: 6rem;
|
||||||
</div>
|
font-weight: bold;
|
||||||
</div>
|
color: #dc3545;
|
||||||
</div>
|
}
|
||||||
</div>
|
.error-message {
|
||||||
|
font-size: 1.5rem;
|
||||||
<!-- Backend Bundle JavaScript -->
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
</body>
|
.back-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="error-container">
|
||||||
|
<div class="error-code">{{ error_code }}</div>
|
||||||
|
<div class="error-message" data-i18n="{{ error_i18n_key }}">{{ error_title }}</div>
|
||||||
|
<p>{{ error_message }}</p>
|
||||||
|
<a href="/" class="back-link" data-i18n="backToHome">返回首页</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 初始化语言支持 -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
applyTranslations();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+380
-380
@@ -1,381 +1,381 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>爬虫控制面板</title>
|
<title>爬虫控制面板</title>
|
||||||
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
|
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
.topic-item {
|
.topic-item {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.topic-item.selected {
|
.topic-item.selected {
|
||||||
background-color: #0d6efd;
|
background-color: #0d6efd;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.custom-topic-input {
|
.custom-topic-input {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
.parameter-section {
|
.parameter-section {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<h2 class="mb-4">爬虫控制面板</h2>
|
<h2 class="mb-4">爬虫控制面板</h2>
|
||||||
|
|
||||||
<!-- 话题选择区域 -->
|
<!-- 话题选择区域 -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">选择话题类型</h5>
|
<h5 class="mb-0">选择话题类型</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="predefinedTopics" class="mb-3">
|
<div id="predefinedTopics" class="mb-3">
|
||||||
<!-- 预定义话题将通过JavaScript动态加载 -->
|
<!-- 预定义话题将通过JavaScript动态加载 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="custom-topic-input">
|
<div class="custom-topic-input">
|
||||||
<h6>添加自定义话题</h6>
|
<h6>添加自定义话题</h6>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" id="customTopic" placeholder="输入自定义话题">
|
<input type="text" class="form-control" id="customTopic" placeholder="输入自定义话题">
|
||||||
<button class="btn btn-primary" onclick="addCustomTopic()">
|
<button class="btn btn-primary" onclick="addCustomTopic()">
|
||||||
<i class="fas fa-plus"></i> 添加
|
<i class="fas fa-plus"></i> 添加
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="selectedTopics" class="mt-3">
|
<div id="selectedTopics" class="mt-3">
|
||||||
<h6>已选择的话题:</h6>
|
<h6>已选择的话题:</h6>
|
||||||
<div id="selectedTopicsList" class="mt-2">
|
<div id="selectedTopicsList" class="mt-2">
|
||||||
<!-- 已选择的话题将在这里显示 -->
|
<!-- 已选择的话题将在这里显示 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 爬虫参数配置 -->
|
<!-- 爬虫参数配置 -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">爬虫参数配置</h5>
|
<h5 class="mb-0">爬虫参数配置</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="crawlDepth" class="form-label">爬取深度</label>
|
<label for="crawlDepth" class="form-label">爬取深度</label>
|
||||||
<input type="number" class="form-control" id="crawlDepth" value="3" min="1" max="10">
|
<input type="number" class="form-control" id="crawlDepth" value="3" min="1" max="10">
|
||||||
<small class="text-muted">每个话题爬取的页数(1-10)</small>
|
<small class="text-muted">每个话题爬取的页数(1-10)</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="interval" class="form-label">爬取间隔(秒)</label>
|
<label for="interval" class="form-label">爬取间隔(秒)</label>
|
||||||
<input type="number" class="form-control" id="interval" value="5" min="1">
|
<input type="number" class="form-control" id="interval" value="5" min="1">
|
||||||
<small class="text-muted">每次请求之间的间隔时间</small>
|
<small class="text-muted">每次请求之间的间隔时间</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="maxRetries" class="form-label">最大重试次数</label>
|
<label for="maxRetries" class="form-label">最大重试次数</label>
|
||||||
<input type="number" class="form-control" id="maxRetries" value="3" min="1">
|
<input type="number" class="form-control" id="maxRetries" value="3" min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="timeout" class="form-label">请求超时时间(秒)</label>
|
<label for="timeout" class="form-label">请求超时时间(秒)</label>
|
||||||
<input type="number" class="form-control" id="timeout" value="30" min="1">
|
<input type="number" class="form-control" id="timeout" value="30" min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- AI配置助手 -->
|
<!-- AI配置助手 -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">
|
<h5 class="mb-0">
|
||||||
<i class="fas fa-robot"></i> AI配置助手
|
<i class="fas fa-robot"></i> AI配置助手
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="aiPrompt" class="form-label">用自然语言描述您的爬虫需求</label>
|
<label for="aiPrompt" class="form-label">用自然语言描述您的爬虫需求</label>
|
||||||
<textarea class="form-control" id="aiPrompt" rows="3"
|
<textarea class="form-control" id="aiPrompt" rows="3"
|
||||||
placeholder="例如:我想爬取最近一周关于人工智能的热门微博,重点关注转发量超过1000的内容,每个话题爬取前5页内容。"></textarea>
|
placeholder="例如:我想爬取最近一周关于人工智能的热门微博,重点关注转发量超过1000的内容,每个话题爬取前5页内容。"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<button class="btn btn-primary" onclick="generateConfig()">
|
<button class="btn btn-primary" onclick="generateConfig()">
|
||||||
<i class="fas fa-magic"></i> 生成配置
|
<i class="fas fa-magic"></i> 生成配置
|
||||||
</button>
|
</button>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" id="autoApply" checked>
|
<input class="form-check-input" type="checkbox" id="autoApply" checked>
|
||||||
<label class="form-check-label" for="autoApply">
|
<label class="form-check-label" for="autoApply">
|
||||||
自动应用生成的配置
|
自动应用生成的配置
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="aiResponse" class="mt-3" style="display: none;">
|
<div id="aiResponse" class="mt-3" style="display: none;">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<h6 class="alert-heading">AI助手建议:</h6>
|
<h6 class="alert-heading">AI助手建议:</h6>
|
||||||
<p id="aiSuggestion" class="mb-0"></p>
|
<p id="aiSuggestion" class="mb-0"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<div class="d-flex justify-content-between mb-5">
|
<div class="d-flex justify-content-between mb-5">
|
||||||
<button class="btn btn-primary" onclick="startCrawling()">
|
<button class="btn btn-primary" onclick="startCrawling()">
|
||||||
<i class="fas fa-play"></i> 开始爬取
|
<i class="fas fa-play"></i> 开始爬取
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-secondary" onclick="saveConfig()">
|
<button class="btn btn-secondary" onclick="saveConfig()">
|
||||||
<i class="fas fa-save"></i> 保存配置
|
<i class="fas fa-save"></i> 保存配置
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 爬虫状态和日志 -->
|
<!-- 爬虫状态和日志 -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">爬虫状态</h5>
|
<h5 class="mb-0">爬虫状态</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="progress mb-3">
|
<div class="progress mb-3">
|
||||||
<div id="crawlProgress" class="progress-bar" role="progressbar" style="width: 0%"></div>
|
<div id="crawlProgress" class="progress-bar" role="progressbar" style="width: 0%"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border p-3 bg-light" style="height: 200px; overflow-y: auto;">
|
<div class="border p-3 bg-light" style="height: 200px; overflow-y: auto;">
|
||||||
<pre id="crawlLog" class="mb-0"></pre>
|
<pre id="crawlLog" class="mb-0"></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.bundle.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// 预定义话题列表
|
// 预定义话题列表
|
||||||
const predefinedTopics = [
|
const predefinedTopics = [
|
||||||
'热门', '社会', '科技', '娱乐', '体育', '财经',
|
'热门', '社会', '科技', '娱乐', '体育', '财经',
|
||||||
'教育', '健康', '军事', '文化', '汽车', '美食'
|
'教育', '健康', '军事', '文化', '汽车', '美食'
|
||||||
];
|
];
|
||||||
|
|
||||||
// 已选择的话题
|
// 已选择的话题
|
||||||
let selectedTopics = new Set();
|
let selectedTopics = new Set();
|
||||||
|
|
||||||
// 初始化页面
|
// 初始化页面
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
loadPredefinedTopics();
|
loadPredefinedTopics();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载预定义话题
|
// 加载预定义话题
|
||||||
function loadPredefinedTopics() {
|
function loadPredefinedTopics() {
|
||||||
const topicsDiv = document.getElementById('predefinedTopics');
|
const topicsDiv = document.getElementById('predefinedTopics');
|
||||||
predefinedTopics.forEach(topic => {
|
predefinedTopics.forEach(topic => {
|
||||||
const topicElement = document.createElement('span');
|
const topicElement = document.createElement('span');
|
||||||
topicElement.className = 'topic-item';
|
topicElement.className = 'topic-item';
|
||||||
topicElement.textContent = topic;
|
topicElement.textContent = topic;
|
||||||
topicElement.onclick = () => toggleTopic(topic, topicElement);
|
topicElement.onclick = () => toggleTopic(topic, topicElement);
|
||||||
topicsDiv.appendChild(topicElement);
|
topicsDiv.appendChild(topicElement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换话题选择状态
|
// 切换话题选择状态
|
||||||
function toggleTopic(topic, element) {
|
function toggleTopic(topic, element) {
|
||||||
if (selectedTopics.has(topic)) {
|
if (selectedTopics.has(topic)) {
|
||||||
selectedTopics.delete(topic);
|
selectedTopics.delete(topic);
|
||||||
element.classList.remove('selected');
|
element.classList.remove('selected');
|
||||||
} else {
|
} else {
|
||||||
selectedTopics.add(topic);
|
selectedTopics.add(topic);
|
||||||
element.classList.add('selected');
|
element.classList.add('selected');
|
||||||
}
|
}
|
||||||
updateSelectedTopicsList();
|
updateSelectedTopicsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加自定义话题
|
// 添加自定义话题
|
||||||
function addCustomTopic() {
|
function addCustomTopic() {
|
||||||
const input = document.getElementById('customTopic');
|
const input = document.getElementById('customTopic');
|
||||||
const topic = input.value.trim();
|
const topic = input.value.trim();
|
||||||
if (topic) {
|
if (topic) {
|
||||||
selectedTopics.add(topic);
|
selectedTopics.add(topic);
|
||||||
input.value = '';
|
input.value = '';
|
||||||
updateSelectedTopicsList();
|
updateSelectedTopicsList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新已选择的话题列表
|
// 更新已选择的话题列表
|
||||||
function updateSelectedTopicsList() {
|
function updateSelectedTopicsList() {
|
||||||
const listDiv = document.getElementById('selectedTopicsList');
|
const listDiv = document.getElementById('selectedTopicsList');
|
||||||
listDiv.innerHTML = '';
|
listDiv.innerHTML = '';
|
||||||
selectedTopics.forEach(topic => {
|
selectedTopics.forEach(topic => {
|
||||||
const topicElement = document.createElement('span');
|
const topicElement = document.createElement('span');
|
||||||
topicElement.className = 'topic-item selected';
|
topicElement.className = 'topic-item selected';
|
||||||
topicElement.textContent = topic;
|
topicElement.textContent = topic;
|
||||||
topicElement.onclick = () => {
|
topicElement.onclick = () => {
|
||||||
selectedTopics.delete(topic);
|
selectedTopics.delete(topic);
|
||||||
updateSelectedTopicsList();
|
updateSelectedTopicsList();
|
||||||
};
|
};
|
||||||
listDiv.appendChild(topicElement);
|
listDiv.appendChild(topicElement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开始爬取
|
// 开始爬取
|
||||||
function startCrawling() {
|
function startCrawling() {
|
||||||
if (selectedTopics.size === 0) {
|
if (selectedTopics.size === 0) {
|
||||||
alert('请至少选择一个话题!');
|
alert('请至少选择一个话题!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
topics: Array.from(selectedTopics),
|
topics: Array.from(selectedTopics),
|
||||||
parameters: {
|
parameters: {
|
||||||
crawlDepth: parseInt(document.getElementById('crawlDepth').value),
|
crawlDepth: parseInt(document.getElementById('crawlDepth').value),
|
||||||
interval: parseInt(document.getElementById('interval').value),
|
interval: parseInt(document.getElementById('interval').value),
|
||||||
maxRetries: parseInt(document.getElementById('maxRetries').value),
|
maxRetries: parseInt(document.getElementById('maxRetries').value),
|
||||||
timeout: parseInt(document.getElementById('timeout').value)
|
timeout: parseInt(document.getElementById('timeout').value)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送爬虫配置到后端
|
// 发送爬虫配置到后端
|
||||||
fetch('/api/spider/start', {
|
fetch('/api/spider/start', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify(config)
|
body: JSON.stringify(config)
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
updateCrawlLog('爬虫任务已启动...');
|
updateCrawlLog('爬虫任务已启动...');
|
||||||
} else {
|
} else {
|
||||||
updateCrawlLog('启动失败:' + data.message);
|
updateCrawlLog('启动失败:' + data.message);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
updateCrawlLog('错误:' + error.message);
|
updateCrawlLog('错误:' + error.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存配置
|
// 保存配置
|
||||||
function saveConfig() {
|
function saveConfig() {
|
||||||
const config = {
|
const config = {
|
||||||
topics: Array.from(selectedTopics),
|
topics: Array.from(selectedTopics),
|
||||||
parameters: {
|
parameters: {
|
||||||
crawlDepth: parseInt(document.getElementById('crawlDepth').value),
|
crawlDepth: parseInt(document.getElementById('crawlDepth').value),
|
||||||
interval: parseInt(document.getElementById('interval').value),
|
interval: parseInt(document.getElementById('interval').value),
|
||||||
maxRetries: parseInt(document.getElementById('maxRetries').value),
|
maxRetries: parseInt(document.getElementById('maxRetries').value),
|
||||||
timeout: parseInt(document.getElementById('timeout').value)
|
timeout: parseInt(document.getElementById('timeout').value)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch('/api/spider/save-config', {
|
fetch('/api/spider/save-config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify(config)
|
body: JSON.stringify(config)
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
alert('配置已保存!');
|
alert('配置已保存!');
|
||||||
} else {
|
} else {
|
||||||
alert('保存失败:' + data.message);
|
alert('保存失败:' + data.message);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
alert('保存出错:' + error.message);
|
alert('保存出错:' + error.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新爬虫日志
|
// 更新爬虫日志
|
||||||
function updateCrawlLog(message) {
|
function updateCrawlLog(message) {
|
||||||
const log = document.getElementById('crawlLog');
|
const log = document.getElementById('crawlLog');
|
||||||
const timestamp = new Date().toLocaleTimeString();
|
const timestamp = new Date().toLocaleTimeString();
|
||||||
log.innerHTML += `[${timestamp}] ${message}\n`;
|
log.innerHTML += `[${timestamp}] ${message}\n`;
|
||||||
log.scrollTop = log.scrollHeight;
|
log.scrollTop = log.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebSocket连接用于实时更新爬虫状态
|
// WebSocket连接用于实时更新爬虫状态
|
||||||
const ws = new WebSocket(`ws://${window.location.host}/ws/spider-status`);
|
const ws = new WebSocket(`ws://${window.location.host}/ws/spider-status`);
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
if (data.type === 'progress') {
|
if (data.type === 'progress') {
|
||||||
document.getElementById('crawlProgress').style.width = data.value + '%';
|
document.getElementById('crawlProgress').style.width = data.value + '%';
|
||||||
} else if (data.type === 'log') {
|
} else if (data.type === 'log') {
|
||||||
updateCrawlLog(data.message);
|
updateCrawlLog(data.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// AI配置生成
|
// AI配置生成
|
||||||
async function generateConfig() {
|
async function generateConfig() {
|
||||||
const prompt = document.getElementById('aiPrompt').value.trim();
|
const prompt = document.getElementById('aiPrompt').value.trim();
|
||||||
if (!prompt) {
|
if (!prompt) {
|
||||||
alert('请输入您的爬虫需求描述!');
|
alert('请输入您的爬虫需求描述!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const aiResponse = document.getElementById('aiResponse');
|
const aiResponse = document.getElementById('aiResponse');
|
||||||
const aiSuggestion = document.getElementById('aiSuggestion');
|
const aiSuggestion = document.getElementById('aiSuggestion');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/spider/ai-config', {
|
const response = await fetch('/api/spider/ai-config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ prompt })
|
body: JSON.stringify({ prompt })
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// 显示AI建议
|
// 显示AI建议
|
||||||
aiSuggestion.textContent = data.suggestion;
|
aiSuggestion.textContent = data.suggestion;
|
||||||
aiResponse.style.display = 'block';
|
aiResponse.style.display = 'block';
|
||||||
|
|
||||||
// 如果选择自动应用配置
|
// 如果选择自动应用配置
|
||||||
if (document.getElementById('autoApply').checked) {
|
if (document.getElementById('autoApply').checked) {
|
||||||
// 清除现有选择
|
// 清除现有选择
|
||||||
selectedTopics.clear();
|
selectedTopics.clear();
|
||||||
|
|
||||||
// 应用新的话题
|
// 应用新的话题
|
||||||
data.config.topics.forEach(topic => {
|
data.config.topics.forEach(topic => {
|
||||||
selectedTopics.add(topic);
|
selectedTopics.add(topic);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新参数
|
// 更新参数
|
||||||
document.getElementById('crawlDepth').value = data.config.parameters.crawlDepth;
|
document.getElementById('crawlDepth').value = data.config.parameters.crawlDepth;
|
||||||
document.getElementById('interval').value = data.config.parameters.interval;
|
document.getElementById('interval').value = data.config.parameters.interval;
|
||||||
document.getElementById('maxRetries').value = data.config.parameters.maxRetries;
|
document.getElementById('maxRetries').value = data.config.parameters.maxRetries;
|
||||||
document.getElementById('timeout').value = data.config.parameters.timeout;
|
document.getElementById('timeout').value = data.config.parameters.timeout;
|
||||||
|
|
||||||
// 更新UI
|
// 更新UI
|
||||||
updateSelectedTopicsList();
|
updateSelectedTopicsList();
|
||||||
|
|
||||||
// 添加提示
|
// 添加提示
|
||||||
updateCrawlLog('AI配置已自动应用');
|
updateCrawlLog('AI配置已自动应用');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(data.message);
|
throw new Error(data.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
aiSuggestion.textContent = '生成配置时出错:' + error.message;
|
aiSuggestion.textContent = '生成配置时出错:' + error.message;
|
||||||
aiResponse.style.display = 'block';
|
aiResponse.style.display = 'block';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Binary file not shown.
+548
-548
File diff suppressed because it is too large
Load Diff
@@ -1,345 +1,345 @@
|
|||||||
{% extends 'base_page.html' %}
|
{% extends 'base_page.html' %}
|
||||||
{% block title %}
|
{% block title %}
|
||||||
文章分析
|
<span data-i18n="articleChar">文章分析</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="iq-sidebar-menu">
|
<nav class="iq-sidebar-menu">
|
||||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/home" class="svg-icon">
|
<a href="/page/home" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">首页</span>
|
<span class="ml-2" data-i18n="home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/hotWord" class="svg-icon ">
|
<a href="/page/hotWord" class="svg-icon ">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">热词统计</span>
|
<span class="ml-2">热词统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/tableData" class="svg-icon">
|
<a href="/page/tableData" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">微博舆情统计</span>
|
<span class="ml-2">微博舆情统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleChar" class="svg-icon">
|
<a href="/page/articleChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">文章分析</span>
|
<span class="ml-2">文章分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/ipChar" class="svg-icon">
|
<a href="/page/ipChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">IP分析</span>
|
<span class="ml-2">IP分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/commentChar" class="svg-icon">
|
<a href="/page/commentChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">评论分析</span>
|
</i><span class="ml-2">评论分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingChar" class="svg-icon">
|
<a href="/page/yuqingChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情分析</span>
|
<span class="ml-2">舆情分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingpredict" class="svg-icon">
|
<a href="/page/yuqingpredict" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情预测</span>
|
<span class="ml-2">舆情预测</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2">
|
<li class="px-3 pt-3 pb-2">
|
||||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleCloud" class="svg-icon">
|
<a href="/page/articleCloud" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">文章内容词云图</span>
|
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-4 mt-1">
|
<div class="col-md-12 mb-4 mt-1">
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
<h4 class="font-weight-bold">文章分析页</h4>
|
<h4 class="font-weight-bold" data-i18n="articleCharPage">文章分析页</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card card-block card-stretch card-height">
|
<div class="card card-block card-stretch card-height">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>类型选择</label>
|
<label data-i18n="typeSelection">类型选择</label>
|
||||||
<select onchange="typeChange(event)" class="form-control mb-3">
|
<select onchange="typeChange(event)" class="form-control mb-3">
|
||||||
{% for i in typeList %}
|
{% for i in typeList %}
|
||||||
{% if defaultType == i %}
|
{% if defaultType == i %}
|
||||||
<option selected value="{{ i }}">{{ i }}</option>
|
<option selected value="{{ i }}">{{ i }}</option>
|
||||||
{% else %}
|
{% else %}
|
||||||
<option value="{{ i }}">{{ i }}</option>
|
<option value="{{ i }}">{{ i }}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<script>
|
<script>
|
||||||
function typeChange(e){
|
function typeChange(e){
|
||||||
window.location.href = 'http://127.0.0.1:5000/page/articleChar?type=' + e.target.value
|
window.location.href = 'http://127.0.0.1:5000/page/articleChar?type=' + e.target.value
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">文章点赞量分析 👍</h4>
|
<h4 class="card-title mb-0" data-i18n="articleLikeAnalysis">文章点赞量分析 👍</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="main" style="width: 100%;height: 450px"></div>
|
<div id="main" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">文章评论量分析 🔥</h4>
|
<h4 class="card-title mb-0" data-i18n="articleCommentAnalysis">文章评论量分析 🔥</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">文章转发量分析 🥇</h4>
|
<h4 class="card-title mb-0" data-i18n="articleForwardAnalysis">文章转发量分析 🥇</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block echarts %}
|
{% block echarts %}
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('main');
|
var chartDom = document.getElementById('main');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
var colors = ['#66cc99','#ffcc66','#ff6666','#6699cc']
|
var colors = ['#66cc99','#ffcc66','#ff6666','#6699cc']
|
||||||
option = {
|
option = {
|
||||||
title: {
|
title: {
|
||||||
text: '点赞区间统计'
|
text: t('likeRangeStatistics')
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis'
|
trigger: 'axis'
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: ['区间个数']
|
data: [t('rangeCount')]
|
||||||
},
|
},
|
||||||
toolbox: {
|
toolbox: {
|
||||||
show: true,
|
show: true,
|
||||||
feature: {
|
feature: {
|
||||||
dataView: { show: true, readOnly: false },
|
dataView: { show: true, readOnly: false },
|
||||||
magicType: { show: true, type: ['line', 'bar'] },
|
magicType: { show: true, type: ['line', 'bar'] },
|
||||||
restore: { show: true },
|
restore: { show: true },
|
||||||
saveAsImage: { show: true }
|
saveAsImage: { show: true }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calculable: true,
|
calculable: true,
|
||||||
xAxis: [
|
xAxis: [
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
data:{{ xData |tojson }}
|
data:{{ xData |tojson }}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value'
|
type: 'value'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '区间个数',
|
name: '区间个数',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {{ yData }},
|
data: {{ yData }},
|
||||||
itemStyle:{
|
itemStyle:{
|
||||||
color:function(params){
|
color:function(params){
|
||||||
return colors[params.dataIndex % colors.length];
|
return colors[params.dataIndex % colors.length];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
markPoint: {
|
markPoint: {
|
||||||
data: [
|
data: [
|
||||||
{ type: 'max', name: 'Max' },
|
{ type: 'max', name: 'Max' },
|
||||||
{ type: 'min', name: 'Min' }
|
{ type: 'min', name: 'Min' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
markLine: {
|
markLine: {
|
||||||
data: [{ type: 'average', name: 'Avg' }]
|
data: [{ type: 'average', name: 'Avg' }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('mainTwo');
|
var chartDom = document.getElementById('mainTwo');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
toolbox: {
|
toolbox: {
|
||||||
show: true,
|
show: true,
|
||||||
feature: {
|
feature: {
|
||||||
dataView: { show: true, readOnly: false },
|
dataView: { show: true, readOnly: false },
|
||||||
magicType: { show: true, type: ['line', 'bar'] },
|
magicType: { show: true, type: ['line', 'bar'] },
|
||||||
restore: { show: true },
|
restore: { show: true },
|
||||||
saveAsImage: { show: true }
|
saveAsImage: { show: true }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: '文章评论区间统计'
|
text: '文章评论区间统计'
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis'
|
trigger: 'axis'
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: {{ x1Data | tojson }}
|
data: {{ x1Data | tojson }}
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value'
|
type: 'value'
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name:"区间个数",
|
name:"区间个数",
|
||||||
data: {{ y1Data }},
|
data: {{ y1Data }},
|
||||||
type: 'line',
|
type: 'line',
|
||||||
symbol: 'triangle',
|
symbol: 'triangle',
|
||||||
symbolSize: 20,
|
symbolSize: 20,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: '#5470C6',
|
color: '#5470C6',
|
||||||
width: 4,
|
width: 4,
|
||||||
type: 'dashed'
|
type: 'dashed'
|
||||||
},
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
borderWidth: 3,
|
borderWidth: 3,
|
||||||
borderColor: '#EE6666',
|
borderColor: '#EE6666',
|
||||||
color: 'yellow'
|
color: 'yellow'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('mainThree');
|
var chartDom = document.getElementById('mainThree');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
title: {
|
title: {
|
||||||
text: '转发量分析区间图'
|
text: '转发量分析区间图'
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
type: 'shadow'
|
type: 'shadow'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
legend: {},
|
legend: {},
|
||||||
grid: {
|
grid: {
|
||||||
left: '3%',
|
left: '3%',
|
||||||
right: '4%',
|
right: '4%',
|
||||||
bottom: '3%',
|
bottom: '3%',
|
||||||
containLabel: true
|
containLabel: true
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
boundaryGap: [0, 0.01]
|
boundaryGap: [0, 0.01]
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: {{ x2Data | tojson }}
|
data: {{ x2Data | tojson }}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '区间个数',
|
name: '区间个数',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {{ y2Data }}
|
data: {{ y2Data }}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,144 +1,144 @@
|
|||||||
{% extends 'base_page.html' %}
|
{% extends 'base_page.html' %}
|
||||||
{% block title %}
|
{% block title %}
|
||||||
文章内容词云图
|
<span data-i18n="articleCloud">文章内容词云图</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="iq-sidebar-menu">
|
<nav class="iq-sidebar-menu">
|
||||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/home" class="svg-icon">
|
<a href="/page/home" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">首页</span>
|
<span class="ml-2" data-i18n="home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/hotWord" class="svg-icon ">
|
<a href="/page/hotWord" class="svg-icon ">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">热词统计</span>
|
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/tableData" class="svg-icon">
|
<a href="/page/tableData" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">微博舆情统计</span>
|
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleChar" class="svg-icon">
|
<a href="/page/articleChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">文章分析</span>
|
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/ipChar" class="svg-icon">
|
<a href="/page/ipChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">IP分析</span>
|
<span class="ml-2">IP分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/commentChar" class="svg-icon">
|
<a href="/page/commentChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">评论分析</span>
|
</i><span class="ml-2">评论分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingChar" class="svg-icon">
|
<a href="/page/yuqingChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情分析</span>
|
<span class="ml-2">舆情分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingpredict" class="svg-icon">
|
<a href="/page/yuqingpredict" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情预测</span>
|
<span class="ml-2">舆情预测</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2">
|
<li class="px-3 pt-3 pb-2">
|
||||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleCloud" class="svg-icon">
|
<a href="/page/articleCloud" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">文章内容词云图</span>
|
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-4 mt-1">
|
<div class="col-md-12 mb-4 mt-1">
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
<h4 class="font-weight-bold">文章分析页</h4>
|
<h4 class="font-weight-bold" data-i18n="articleCharPage">文章分析页</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">文章内容词云图</h4>
|
<h4 class="card-title mb-0" data-i18n="articleCloud">文章内容词云图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainThree" style="width: 100%;height: 750px;text-align: center">
|
<div id="mainThree" style="width: 100%;height: 750px;text-align: center">
|
||||||
<img style="width:60%" src="/static/contentCloud.jpg" alt="">
|
<img style="width:60%" src="/static/contentCloud.jpg" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block echarts %}
|
{% block echarts %}
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+504
-477
@@ -1,478 +1,505 @@
|
|||||||
|
<!doctype html>
|
||||||
<!doctype html>
|
<html lang="en">
|
||||||
<html lang="en">
|
<head>
|
||||||
<head>
|
<meta charset="utf-8">
|
||||||
<meta charset="utf-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<title>{% block title %}首页{% endblock %}</title>
|
||||||
<title>{% block title %}首页{% endblock %}</title>
|
|
||||||
|
<link rel="stylesheet" href="/static/css/backend-plugin.min.css">
|
||||||
<link rel="stylesheet" href="/static/css/backend-plugin.min.css">
|
<link rel="stylesheet" href="/static/css/backend.css">
|
||||||
<link rel="stylesheet" href="/static/css/backend.css">
|
<meta name="referrer" content="no-referrer" />
|
||||||
<meta name="referrer" content="no-referrer" />
|
<script src="/static/js/i18n.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class=" ">
|
<body class=" ">
|
||||||
<!-- loader Start -->
|
<!-- loader Start -->
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<div id="loading-center">
|
<div id="loading-center">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- loader END -->
|
<!-- loader END -->
|
||||||
<!-- Wrapper Start -->
|
<!-- Wrapper Start -->
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="iq-sidebar sidebar-default ">
|
<div class="iq-sidebar sidebar-default ">
|
||||||
<div class="iq-sidebar-logo d-flex align-items-end justify-content-between">
|
<div class="iq-sidebar-logo d-flex align-items-end justify-content-between">
|
||||||
<a href="" class="header-logo">
|
<a href="" class="header-logo">
|
||||||
<img src="https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/202407051027268.png" class="img-fluid rounded-normal light-logo" alt="logo">
|
<img src="https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/202407051027268.png" class="img-fluid rounded-normal light-logo" alt="logo">
|
||||||
<span>微博舆情分析系统</span>
|
<span data-i18n="weiboSystem">微博舆情分析系统</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="side-menu-bt-sidebar-1">
|
<div class="side-menu-bt-sidebar-1">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-light wrapper-menu" width="30" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="text-light wrapper-menu" width="30" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="data-scrollbar" data-scroll="1">
|
<div class="data-scrollbar" data-scroll="1">
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="iq-sidebar-menu">
|
<nav class="iq-sidebar-menu">
|
||||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/home" class="svg-icon">
|
<a href="/page/home" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">首页</span>
|
<span class="ml-2" data-i18n="home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/hotWord" class="svg-icon ">
|
<a href="/page/hotWord" class="svg-icon ">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">热词统计</span>
|
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/tableData" class="svg-icon">
|
<a href="/page/tableData" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">微博舆情统计</span>
|
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleChar" class="svg-icon">
|
<a href="/page/articleChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">文章分析</span>
|
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/ipChar" class="svg-icon">
|
<a href="/page/ipChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">IP分析</span>
|
<span class="ml-2" data-i18n="ipChar">IP分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/commentChar" class="svg-icon">
|
<a href="/page/commentChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">评论分析</span>
|
</i><span class="ml-2" data-i18n="commentChar">评论分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingChar" class="svg-icon">
|
<a href="/page/yuqingChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情分析</span>
|
<span class="ml-2" data-i18n="yuqingChar">舆情分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingpredict" class="svg-icon">
|
<a href="/page/yuqingpredict" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情预测</span>
|
<span class="ml-2" data-i18n="yuqingpredict">舆情预测</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleCloud" class="svg-icon">
|
<a href="/page/articleCloud" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">文章内容词云图</span>
|
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
<li class="sidebar-layout">
|
||||||
</nav>
|
<a href="javascript:void(0)" id="language-switcher" class="svg-icon">
|
||||||
{% endblock %}
|
<i class="">
|
||||||
<div class="pt-5 pb-5"></div>
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
</div>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"></path>
|
||||||
</div>
|
</svg>
|
||||||
<div class="content-page">
|
</i>
|
||||||
{% block content %}
|
<span class="ml-2" id="language-text">切换语言</span>
|
||||||
<div class="container-fluid">
|
</a>
|
||||||
<div class="row">
|
</li>
|
||||||
<div class="col-md-12 mb-4 mt-1">
|
</ul>
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
</nav>
|
||||||
<h4 class="font-weight-bold">首页</h4>
|
{% endblock %}
|
||||||
</div>
|
<div class="pt-5 pb-5"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-8 col-md-12">
|
</div>
|
||||||
<div class="row">
|
<div class="content-page">
|
||||||
<div class="col-md-4">
|
{% block content %}
|
||||||
<div class="card">
|
<div class="container-fluid">
|
||||||
<div class="card-body">
|
<div class="row">
|
||||||
<div class="d-flex align-items-center">
|
<div class="col-md-12 mb-4 mt-1">
|
||||||
<div class="">
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
<p class="mb-2 text-secondary">文章个数</p>
|
<h4 class="font-weight-bold" data-i18n="home">首页</h4>
|
||||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
</div>
|
||||||
<h5 class="mb-0 font-weight-bold">{{ articleLenMax }}个</h5>
|
</div>
|
||||||
</div>
|
<div class="col-lg-8 col-md-12">
|
||||||
</div>
|
<div class="row">
|
||||||
</div>
|
<div class="col-md-4">
|
||||||
</div>
|
<div class="card">
|
||||||
</div>
|
<div class="card-body">
|
||||||
</div>
|
<div class="d-flex align-items-center">
|
||||||
<div class="col-md-4">
|
<div class="">
|
||||||
<div class="card">
|
<p class="mb-2 text-secondary" data-i18n="articleCount">文章个数</p>
|
||||||
<div class="card-body">
|
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||||
<div class="d-flex align-items-center">
|
<h5 class="mb-0 font-weight-bold">{{ articleLenMax }}个</h5>
|
||||||
<div class="">
|
</div>
|
||||||
<p class="mb-2 text-secondary">文章爬取规则</p>
|
</div>
|
||||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
</div>
|
||||||
<h5 class="mb-0 font-weight-bold">每 5 小时更新一次爬取内容</h5>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col-md-4">
|
||||||
</div>
|
<div class="card">
|
||||||
</div>
|
<div class="card-body">
|
||||||
</div>
|
<div class="d-flex align-items-center">
|
||||||
<div class="col-md-4">
|
<div class="">
|
||||||
<div class="card">
|
<p class="mb-2 text-secondary" data-i18n="articleCrawlRule">文章爬取规则</p>
|
||||||
<div class="card-body">
|
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||||
<div class="d-flex align-items-center">
|
<h5 class="mb-0 font-weight-bold">每 5 小时更新一次爬取内容</h5>
|
||||||
<div class="">
|
</div>
|
||||||
<p class="mb-2 text-secondary">下次爬取时间</p>
|
</div>
|
||||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
</div>
|
||||||
<h5 class="mb-0 font-weight-bold">7-5-18:00</h5>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col-md-4">
|
||||||
</div>
|
<div class="card">
|
||||||
</div>
|
<div class="card-body">
|
||||||
</div>
|
<div class="d-flex align-items-center">
|
||||||
<div class="col-md-12">
|
<div class="">
|
||||||
<div class="card">
|
<p class="mb-2 text-secondary" data-i18n="nextCrawlTime">下次爬取时间</p>
|
||||||
<div class="card-body">
|
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap">
|
<h5 class="mb-0 font-weight-bold">7-5-18:00</h5>
|
||||||
<h4 class="font-weight-bold">文章发布时间个数</h4>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="main" style="width:100%;height: 350px" class="custom-chart"></div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col-md-12">
|
||||||
</div>
|
<div class="card">
|
||||||
<div class="col-lg-4 col-md-8">
|
<div class="card-body">
|
||||||
<div class="card card-block card-stretch card-height">
|
<div class="d-flex justify-content-between align-items-center flex-wrap">
|
||||||
<div class="card-header card-header-border d-flex justify-content-between">
|
<h4 class="font-weight-bold" data-i18n="articlePublishTimeCount">文章发布时间个数</h4>
|
||||||
<div class="header-title">
|
</div>
|
||||||
<h4 class="card-title">评论点赞量 Top Fore</h4>
|
<div id="main" style="width:100%;height: 350px" class="custom-chart"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body-list">
|
</div>
|
||||||
<ul class="list-style-3 mb-0">
|
</div>
|
||||||
{% for i in commentsLikeCountTopFore %}
|
</div>
|
||||||
<li class="p-3 list-item d-flex justify-content-start align-items-center">
|
<div class="col-lg-4 col-md-8">
|
||||||
<div class="avatar">
|
<div class="card card-block card-stretch card-height">
|
||||||
<p>
|
<div class="card-header card-header-border d-flex justify-content-between">
|
||||||
🧑 {{ i[5] }}
|
<div class="header-title">
|
||||||
</p>
|
<h4 class="card-title" data-i18n="commentLikeCountTopFore">评论点赞量 Top Fore</h4>
|
||||||
<p class="mb-0" style="color:#ccc;width:320px;overflow: hidden;text-overflow: ellipsis;white-space:nowrap">
|
</div>
|
||||||
{{ i[4] }}
|
</div>
|
||||||
</p>
|
<div class="card-body-list">
|
||||||
</div>
|
<ul class="list-style-3 mb-0">
|
||||||
<div class="list-style-action d-flex justify-content-end ml-auto">
|
{% for i in commentsLikeCountTopFore %}
|
||||||
<h6 class="font-weight-bold text-danger">👍 {{ i[2] }}</h6>
|
<li class="p-3 list-item d-flex justify-content-start align-items-center">
|
||||||
</div>
|
<div class="avatar">
|
||||||
</li>
|
<p>
|
||||||
{% endfor %}
|
🧑 {{ i[5] }}
|
||||||
<div class="d-flex justify-content-end align-items-center border-top-table p-3">
|
</p>
|
||||||
<a href="/page/tableData" class="btn btn-secondary btn-sm">查看全部</a>
|
<p class="mb-0" style="color:#ccc;width:320px;overflow: hidden;text-overflow: ellipsis;white-space:nowrap">
|
||||||
</div>
|
{{ i[4] }}
|
||||||
</ul>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="list-style-action d-flex justify-content-end ml-auto">
|
||||||
|
<h6 class="font-weight-bold text-danger">👍 {{ i[2] }}</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4 col-md-6">
|
</li>
|
||||||
<div class="card card-block card-stretch card-height">
|
{% endfor %}
|
||||||
<div class="card-header d-flex justify-content-between">
|
<div class="d-flex justify-content-end align-items-center border-top-table p-3">
|
||||||
<div class="header-title">
|
<a href="/page/tableData" class="btn btn-secondary btn-sm" data-i18n="viewAll">查看全部</a>
|
||||||
<h4 class="card-title">文章类型占比</h4>
|
</div>
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
</div>
|
||||||
<div id="mainTwo" style="width:100%;height:350px">
|
|
||||||
|
</div>
|
||||||
</div>
|
<div class="col-lg-4 col-md-6">
|
||||||
</div>
|
<div class="card card-block card-stretch card-height">
|
||||||
</div>
|
<div class="card-header d-flex justify-content-between">
|
||||||
</div>
|
<div class="header-title">
|
||||||
<div class="col-lg-4 col-md-6">
|
<h4 class="card-title" data-i18n="articleTypeRatio">文章类型占比</h4>
|
||||||
<div class="card card-block card-stretch card-height">
|
</div>
|
||||||
<div class="card-header d-flex justify-content-between">
|
</div>
|
||||||
<div class="header-title">
|
<div class="card-body p-0">
|
||||||
<h4 class="card-title">评论用户名词云图</h4>
|
<div id="mainTwo" style="width:100%;height:350px">
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
</div>
|
||||||
<div id="mainTwo" style="width:100%;height:350px;text-align: center">
|
</div>
|
||||||
<img style="width:85%" src="/static/authorNameCloud.jpg" alt="">
|
</div>
|
||||||
</div>
|
<div class="col-lg-4 col-md-6">
|
||||||
</div>
|
<div class="card card-block card-stretch card-height">
|
||||||
</div>
|
<div class="card-header d-flex justify-content-between">
|
||||||
</div>
|
<div class="header-title">
|
||||||
<div class="col-lg-4 col-md-6">
|
<h4 class="card-title" data-i18n="commentUserWordCloud">评论用户名词云图</h4>
|
||||||
<div class="card card-block card-stretch card-height">
|
</div>
|
||||||
<div class="card-header d-flex justify-content-between">
|
</div>
|
||||||
<div class="header-title">
|
<div class="card-body p-0">
|
||||||
<h4 class="card-title">评论用户时间占比</h4>
|
<div id="mainTwo" style="width:100%;height:350px;text-align: center">
|
||||||
</div>
|
<img style="width:85%" src="/static/authorNameCloud.jpg" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
</div>
|
||||||
<div id="mainThree" style="width:100%;height:350px">
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
<div class="col-lg-4 col-md-6">
|
||||||
</div>
|
<div class="card card-block card-stretch card-height">
|
||||||
</div>
|
<div class="card-header d-flex justify-content-between">
|
||||||
</div>
|
<div class="header-title">
|
||||||
</div>
|
<h4 class="card-title" data-i18n="commentUserTimeRatio">评论用户时间占比</h4>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
</div>
|
||||||
</div>
|
<div class="card-body p-0">
|
||||||
</div>
|
<div id="mainThree" style="width:100%;height:350px">
|
||||||
<!-- Wrapper End-->
|
|
||||||
<footer class="iq-footer">
|
</div>
|
||||||
<div class="container-fluid">
|
</div>
|
||||||
<div class="row">
|
</div>
|
||||||
<div class="col-lg-6">
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6 text-right">
|
</div>
|
||||||
<span class="mr-1">网安小学期 © 2024.<a target="_blank" href="#">郭航江</a></span>
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!-- Wrapper End-->
|
||||||
</footer>
|
<footer class="iq-footer">
|
||||||
<script src="/static/echarts.min.js"></script>
|
<div class="container-fluid">
|
||||||
<script src="/static/china.js"></script>
|
<div class="row">
|
||||||
{% block echarts %}
|
<div class="col-lg-6">
|
||||||
<script>
|
</div>
|
||||||
var chartDom = document.getElementById('main');
|
<div class="col-lg-6 text-right">
|
||||||
var myCharts = echarts.init(chartDom);
|
<span class="mr-1"><span data-i18n="semester">网安小学期</span> © 2024.<a target="_blank" href="#">郭航江</a></span>
|
||||||
var option;
|
</div>
|
||||||
var xData = {{ xData | tojson }};
|
</div>
|
||||||
var yData = {{ yData }};
|
</div>
|
||||||
var xRes = []
|
</footer>
|
||||||
var yRes = []
|
<script src="/static/echarts.min.js"></script>
|
||||||
for(var i = 0;i < 8;i++){
|
<script src="/static/china.js"></script>
|
||||||
xRes.push(xData[i])
|
<!-- 添加语言切换脚本 -->
|
||||||
yRes.push(yData[i])
|
<script>
|
||||||
}
|
// 页面加载完成后初始化语言切换功能
|
||||||
option = {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
tooltip:{
|
// 更新语言切换按钮文本
|
||||||
trigger:"axis"
|
updateLanguageSwitcherText();
|
||||||
},
|
});
|
||||||
legend:{},
|
|
||||||
toolbox: {
|
// 更新语言切换按钮文本
|
||||||
show: true,
|
function updateLanguageSwitcherText() {
|
||||||
feature: {
|
const currentLang = getCurrentLanguage();
|
||||||
dataZoom: {
|
const langText = document.getElementById('language-text');
|
||||||
yAxisIndex: 'none'
|
if (langText) {
|
||||||
},
|
langText.textContent = currentLang === 'zh' ? '切换到英文' : 'Switch to Chinese';
|
||||||
dataView: { readOnly: false },
|
}
|
||||||
magicType: { type: ['line', 'bar'] },
|
}
|
||||||
restore: {},
|
</script>
|
||||||
saveAsImage: {}
|
{% block echarts %}
|
||||||
}
|
<script>
|
||||||
},
|
var chartDom = document.getElementById('main');
|
||||||
xAxis: {
|
var myCharts = echarts.init(chartDom);
|
||||||
type: 'category',
|
var option;
|
||||||
boundaryGap: false,
|
var xData = {{ xData | tojson }};
|
||||||
data: xRes
|
var yData = {{ yData }};
|
||||||
},
|
var xRes = []
|
||||||
yAxis: {
|
var yRes = []
|
||||||
type: 'value'
|
for(var i = 0;i < 8;i++){
|
||||||
},
|
xRes.push(xData[i])
|
||||||
series: [
|
yRes.push(yData[i])
|
||||||
{
|
}
|
||||||
name:"日期个数",
|
option = {
|
||||||
data: yRes,
|
tooltip:{
|
||||||
type: 'bar',
|
trigger:"axis"
|
||||||
areaStyle: {
|
},
|
||||||
color:"rgba(0,128,255,0.2)"
|
legend:{},
|
||||||
},
|
toolbox: {
|
||||||
smooth:true,
|
show: true,
|
||||||
lineStyle:{
|
feature: {
|
||||||
width:5
|
dataZoom: {
|
||||||
},
|
yAxisIndex: 'none'
|
||||||
emphasis:{
|
},
|
||||||
focus:'series'
|
dataView: { readOnly: false },
|
||||||
}
|
magicType: { type: ['line', 'bar'] },
|
||||||
}
|
restore: {},
|
||||||
]
|
saveAsImage: {}
|
||||||
};
|
}
|
||||||
let count = 8;
|
},
|
||||||
setInterval(()=>{
|
xAxis: {
|
||||||
if(count >= xData.length) count=0
|
type: 'category',
|
||||||
xRes.shift()
|
boundaryGap: false,
|
||||||
xRes.push(xData[count])
|
data: xRes
|
||||||
yRes.shift()
|
},
|
||||||
yRes.push(yData[count])
|
yAxis: {
|
||||||
count++
|
type: 'value'
|
||||||
myCharts.setOption({
|
},
|
||||||
xAxis:[{
|
series: [
|
||||||
data:xRes
|
{
|
||||||
}],
|
name:"日期个数",
|
||||||
series:[{
|
data: yRes,
|
||||||
data:yRes
|
type: 'bar',
|
||||||
}]
|
areaStyle: {
|
||||||
})
|
color:"rgba(0,128,255,0.2)"
|
||||||
},2000)
|
},
|
||||||
|
smooth:true,
|
||||||
option && myCharts.setOption(option);
|
lineStyle:{
|
||||||
|
width:5
|
||||||
</script>
|
},
|
||||||
<script>
|
emphasis:{
|
||||||
var chartDom = document.getElementById('mainTwo');
|
focus:'series'
|
||||||
var myChart = echarts.init(chartDom);
|
}
|
||||||
var option;
|
}
|
||||||
|
]
|
||||||
option = {
|
};
|
||||||
title: {
|
let count = 8;
|
||||||
text: '各微博类型占比饼状图',
|
setInterval(()=>{
|
||||||
left: 'center'
|
if(count >= xData.length) count=0
|
||||||
},
|
xRes.shift()
|
||||||
tooltip: {
|
xRes.push(xData[count])
|
||||||
trigger: 'item'
|
yRes.shift()
|
||||||
},
|
yRes.push(yData[count])
|
||||||
legend: {
|
count++
|
||||||
orient: 'vertical',
|
myCharts.setOption({
|
||||||
left: 'left',
|
xAxis:[{
|
||||||
padding:[10,20,30,20],
|
data:xRes
|
||||||
},
|
}],
|
||||||
series: [
|
series:[{
|
||||||
{
|
data:yRes
|
||||||
name: '博客类型占比',
|
}]
|
||||||
type: 'pie',
|
})
|
||||||
radius: '50%',
|
},2000)
|
||||||
data: {{ typeChart |tojson }},
|
|
||||||
emphasis: {
|
option && myCharts.setOption(option);
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 10,
|
</script>
|
||||||
shadowOffsetX: 0,
|
<script>
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
var chartDom = document.getElementById('mainTwo');
|
||||||
}
|
var myChart = echarts.init(chartDom);
|
||||||
}
|
var option;
|
||||||
}
|
|
||||||
]
|
option = {
|
||||||
};
|
title: {
|
||||||
|
text: '各微博类型占比饼状图',
|
||||||
option && myChart.setOption(option);
|
left: 'center'
|
||||||
|
},
|
||||||
</script>
|
tooltip: {
|
||||||
<script>
|
trigger: 'item'
|
||||||
var chartDom = document.getElementById('mainThree');
|
},
|
||||||
var myChart = echarts.init(chartDom);
|
legend: {
|
||||||
var option;
|
orient: 'vertical',
|
||||||
|
left: 'left',
|
||||||
option = {
|
padding:[10,20,30,20],
|
||||||
tooltip: {
|
},
|
||||||
trigger: 'item'
|
series: [
|
||||||
},
|
{
|
||||||
legend: {
|
name: '博客类型占比',
|
||||||
top: '5%',
|
type: 'pie',
|
||||||
left:10,
|
radius: '50%',
|
||||||
right:10,
|
data: {{ typeChart |tojson }},
|
||||||
type: 'scroll',
|
emphasis: {
|
||||||
},
|
itemStyle: {
|
||||||
series: [
|
shadowBlur: 10,
|
||||||
{
|
shadowOffsetX: 0,
|
||||||
name: '评论时间发布个数',
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
type: 'pie',
|
}
|
||||||
radius: ['60%', '50%'],
|
}
|
||||||
avoidLabelOverlap: false,
|
}
|
||||||
label: {
|
]
|
||||||
show: false,
|
};
|
||||||
position: 'center'
|
|
||||||
},
|
option && myChart.setOption(option);
|
||||||
emphasis: {
|
|
||||||
label: {
|
</script>
|
||||||
show: true,
|
<script>
|
||||||
fontSize: 20,
|
var chartDom = document.getElementById('mainThree');
|
||||||
fontWeight: 'bold'
|
var myChart = echarts.init(chartDom);
|
||||||
}
|
var option;
|
||||||
},
|
|
||||||
labelLine: {
|
option = {
|
||||||
show: false
|
tooltip: {
|
||||||
},
|
trigger: 'item'
|
||||||
data: {{ createAtChart | tojson }}
|
},
|
||||||
}
|
legend: {
|
||||||
]
|
top: '5%',
|
||||||
};
|
left:10,
|
||||||
|
right:10,
|
||||||
option && myChart.setOption(option);
|
type: 'scroll',
|
||||||
|
},
|
||||||
</script>
|
series: [
|
||||||
{% endblock %}
|
{
|
||||||
<script src="/static/js/backend-bundle.min.js"></script>
|
name: '评论时间发布个数',
|
||||||
<script src="/static/js/customizer.js"></script>
|
type: 'pie',
|
||||||
<script src="/static/js/sidebar.js"></script>
|
radius: ['60%', '50%'],
|
||||||
<script src="/static/js/flex-tree.min.js"></script>
|
avoidLabelOverlap: false,
|
||||||
<script src="/static/js/tree.js"></script>
|
label: {
|
||||||
<script src="/static/js/table-treeview.js"></script>
|
show: false,
|
||||||
<script src="/static/js/sweetalert.js"></script>
|
position: 'center'
|
||||||
<script src="/static/js/vector-map-custom.js"></script>
|
},
|
||||||
<script src="/static/js/chart-custom.js"></script>
|
emphasis: {
|
||||||
<script src="/static/js/01.js"></script>
|
label: {
|
||||||
<script src="/static/js/02.js"></script>
|
show: true,
|
||||||
<script src="/static/js/slider.js"></script>
|
fontSize: 20,
|
||||||
<script src="/static/js/index.js" type="module"></script>
|
fontWeight: 'bold'
|
||||||
<script src="/static/js/app.js">
|
}
|
||||||
|
},
|
||||||
</script>
|
labelLine: {
|
||||||
</body>
|
show: false
|
||||||
</html>
|
},
|
||||||
|
data: {{ createAtChart | tojson }}
|
||||||
<style>
|
}
|
||||||
body {
|
]
|
||||||
background-color: #add8e6; /* 浅蓝色 */
|
};
|
||||||
}
|
|
||||||
|
option && myChart.setOption(option);
|
||||||
.dark .card {
|
|
||||||
background-color: #3a4d76;
|
</script>
|
||||||
border-radius: 0px;
|
{% endblock %}
|
||||||
}
|
<script src="/static/js/backend-bundle.min.js"></script>
|
||||||
|
<script src="/static/js/customizer.js"></script>
|
||||||
|
<script src="/static/js/sidebar.js"></script>
|
||||||
|
<script src="/static/js/flex-tree.min.js"></script>
|
||||||
|
<script src="/static/js/tree.js"></script>
|
||||||
|
<script src="/static/js/table-treeview.js"></script>
|
||||||
|
<script src="/static/js/sweetalert.js"></script>
|
||||||
|
<script src="/static/js/vector-map-custom.js"></script>
|
||||||
|
<script src="/static/js/chart-custom.js"></script>
|
||||||
|
<script src="/static/js/01.js"></script>
|
||||||
|
<script src="/static/js/02.js"></script>
|
||||||
|
<script src="/static/js/slider.js"></script>
|
||||||
|
<script src="/static/js/index.js" type="module"></script>
|
||||||
|
<script src="/static/js/app.js">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #add8e6; /* 浅蓝色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .card {
|
||||||
|
background-color: #3a4d76;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,317 +1,317 @@
|
|||||||
{% extends 'base_page.html' %}
|
{% extends 'base_page.html' %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
评论分析
|
<span data-i18n="commentChar">评论分析</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="iq-sidebar-menu">
|
<nav class="iq-sidebar-menu">
|
||||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/home" class="svg-icon">
|
<a href="/page/home" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">首页</span>
|
<span class="ml-2" data-i18n="home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/hotWord" class="svg-icon ">
|
<a href="/page/hotWord" class="svg-icon ">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">热词统计</span>
|
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/tableData" class="svg-icon">
|
<a href="/page/tableData" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">微博舆情统计</span>
|
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleChar" class="svg-icon">
|
<a href="/page/articleChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">文章分析</span>
|
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/ipChar" class="svg-icon">
|
<a href="/page/ipChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">IP分析</span>
|
<span class="ml-2">IP分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/commentChar" class="svg-icon">
|
<a href="/page/commentChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">评论分析</span>
|
</i><span class="ml-2">评论分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingChar" class="svg-icon">
|
<a href="/page/yuqingChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情分析</span>
|
<span class="ml-2">舆情分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingpredict" class="svg-icon">
|
<a href="/page/yuqingpredict" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情预测</span>
|
<span class="ml-2">舆情预测</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2">
|
<li class="px-3 pt-3 pb-2">
|
||||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleCloud" class="svg-icon">
|
<a href="/page/articleCloud" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">文章内容词云图</span>
|
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-4 mt-1">
|
<div class="col-md-12 mb-4 mt-1">
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
<h4 class="font-weight-bold">评论分析</h4>
|
<h4 class="font-weight-bold" data-i18n="commentChar">评论分析</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">评论点赞次数区间图</h4>
|
<h4 class="card-title mb-0" data-i18n="commentLikeRangeChart">评论点赞次数区间图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="main" style="width: 100%;height: 450px"></div>
|
<div id="main" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">评论用户性别占比</h4>
|
<h4 class="card-title mb-0" data-i18n="commentUserGenderRatio">评论用户性别占比</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">用户评论词云图</h4>
|
<h4 class="card-title mb-0" data-i18n="userCommentWordCloud">用户评论词云图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainThree" style="width: 100%;height: 450px;text-align: center">
|
<div id="mainThree" style="width: 100%;height: 450px;text-align: center">
|
||||||
<img style="width:84%" src="/static/commentCloud.jpg" alt="">
|
<img style="width:84%" src="/static/commentCloud.jpg" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block echarts %}
|
{% block echarts %}
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('main');
|
var chartDom = document.getElementById('main');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
title: {
|
title: {
|
||||||
text: '评论点赞量区间折线图',
|
text: '评论点赞量区间折线图',
|
||||||
left: '1%'
|
left: '1%'
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis'
|
trigger: 'axis'
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: '5%',
|
left: '5%',
|
||||||
right: '15%',
|
right: '15%',
|
||||||
bottom: '10%'
|
bottom: '10%'
|
||||||
},
|
},
|
||||||
legend:{},
|
legend:{},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
data: {{ xData | tojson }}
|
data: {{ xData | tojson }}
|
||||||
},
|
},
|
||||||
yAxis: {},
|
yAxis: {},
|
||||||
toolbox: {
|
toolbox: {
|
||||||
right: 10,
|
right: 10,
|
||||||
feature: {
|
feature: {
|
||||||
dataZoom: {
|
dataZoom: {
|
||||||
yAxisIndex: 'none'
|
yAxisIndex: 'none'
|
||||||
},
|
},
|
||||||
restore: {},
|
restore: {},
|
||||||
saveAsImage: {}
|
saveAsImage: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dataZoom: [
|
dataZoom: [
|
||||||
{
|
{
|
||||||
show: true,
|
show: true,
|
||||||
start: 80,
|
start: 80,
|
||||||
end: 100
|
end: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'inside',
|
type: 'inside',
|
||||||
start: 80,
|
start: 80,
|
||||||
end: 100
|
end: 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
visualMap: {
|
visualMap: {
|
||||||
top: 50,
|
top: 50,
|
||||||
right: 10,
|
right: 10,
|
||||||
pieces: [
|
pieces: [
|
||||||
{
|
{
|
||||||
gt: 0,
|
gt: 0,
|
||||||
lte: 50,
|
lte: 50,
|
||||||
color: '#93CE07'
|
color: '#93CE07'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
gt: 50,
|
gt: 50,
|
||||||
lte: 100,
|
lte: 100,
|
||||||
color: '#FBDB0F'
|
color: '#FBDB0F'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
gt: 100,
|
gt: 100,
|
||||||
lte: 150,
|
lte: 150,
|
||||||
color: '#FC7D02'
|
color: '#FC7D02'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
gt: 150,
|
gt: 150,
|
||||||
lte: 200,
|
lte: 200,
|
||||||
color: '#FD0100'
|
color: '#FD0100'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
gt: 200,
|
gt: 200,
|
||||||
lte: 300,
|
lte: 300,
|
||||||
color: '#AA069F'
|
color: '#AA069F'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
gt: 300,
|
gt: 300,
|
||||||
color: '#AC3B2A'
|
color: '#AC3B2A'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outOfRange: {
|
outOfRange: {
|
||||||
color: '#999'
|
color: '#999'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: {
|
series: {
|
||||||
name: '点赞个数',
|
name: '点赞个数',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {{ yData }},
|
data: {{ yData }},
|
||||||
markLine: {
|
markLine: {
|
||||||
silent: true,
|
silent: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: '#333'
|
color: '#333'
|
||||||
},
|
},
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
yAxis: 50
|
yAxis: 50
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
yAxis: 100
|
yAxis: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
yAxis: 150
|
yAxis: 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
yAxis: 200
|
yAxis: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
yAxis: 300
|
yAxis: 300
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('mainTwo');
|
var chartDom = document.getElementById('mainTwo');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
title: {
|
title: {
|
||||||
text: '评论性别占比饼图',
|
text: '评论性别占比饼图',
|
||||||
left: 'center'
|
left: 'center'
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item'
|
trigger: 'item'
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
left: 'left'
|
left: 'left'
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '性别个数',
|
name: '性别个数',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: '50%',
|
radius: '50%',
|
||||||
data: {{ genderPieData | tojson }},
|
data: {{ genderPieData | tojson }},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
shadowBlur: 10,
|
shadowBlur: 10,
|
||||||
shadowOffsetX: 0,
|
shadowOffsetX: 0,
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+502
-502
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,30 @@
|
|||||||
<!--
|
<!--
|
||||||
_oo0oo_
|
_oo0oo_
|
||||||
o8888888o
|
o8888888o
|
||||||
88" . "88
|
88" . "88
|
||||||
(| -_- |)
|
(| -_- |)
|
||||||
0\ = /0
|
0\ = /0
|
||||||
___/`---'\___
|
___/`---'\___
|
||||||
.' \\| |// '.
|
.' \\| |// '.
|
||||||
/ \\||| : |||// \
|
/ \\||| : |||// \
|
||||||
/ _||||| -:- |||||- \
|
/ _||||| -:- |||||- \
|
||||||
| | \\\ - /// | |
|
| | \\\ - /// | |
|
||||||
| \_| ''\---/'' |_/ |
|
| \_| ''\---/'' |_/ |
|
||||||
\ .-\__ '-' ___/-. /
|
\ .-\__ '-' ___/-. /
|
||||||
___'. .' /--.--\ `. .'___
|
___'. .' /--.--\ `. .'___
|
||||||
."" '< `.___\_<|>_/___.' >' "".
|
."" '< `.___\_<|>_/___.' >' "".
|
||||||
| | : `- \`.;`\ _ /`;.`/ - ` : | |
|
| | : `- \`.;`\ _ /`;.`/ - ` : | |
|
||||||
\ \ `_. \_ __\ /__ _/ .-` / /
|
\ \ `_. \_ __\ /__ _/ .-` / /
|
||||||
=====`-.____`.___ \_____/___.-`___.-'=====
|
=====`-.____`.___ \_____/___.-`___.-'=====
|
||||||
`=---='
|
`=---='
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
佛祖保佑 代码永无BUG
|
佛祖保佑 代码永无BUG
|
||||||
|
|
||||||
-->
|
-->
|
||||||
{% extends 'base_page.html' %}
|
{% extends 'base_page.html' %}
|
||||||
|
{% block title %}
|
||||||
|
<span data-i18n="home">首页</span>
|
||||||
|
{% endblock %}
|
||||||
+274
-274
@@ -1,275 +1,275 @@
|
|||||||
{% extends 'base_page.html' %}
|
{% extends 'base_page.html' %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
IP分析
|
<span data-i18n="ipChar">IP分析</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="iq-sidebar-menu">
|
<nav class="iq-sidebar-menu">
|
||||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/home" class="svg-icon">
|
<a href="/page/home" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">首页</span>
|
<span class="ml-2" data-i18n="home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/hotWord" class="svg-icon ">
|
<a href="/page/hotWord" class="svg-icon ">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">热词统计</span>
|
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/tableData" class="svg-icon">
|
<a href="/page/tableData" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">微博舆情统计</span>
|
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleChar" class="svg-icon">
|
<a href="/page/articleChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">文章分析</span>
|
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/ipChar" class="svg-icon">
|
<a href="/page/ipChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">IP分析</span>
|
<span class="ml-2">IP分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/commentChar" class="svg-icon">
|
<a href="/page/commentChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">评论分析</span>
|
</i><span class="ml-2">评论分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingpredict" class="svg-icon">
|
<a href="/page/yuqingpredict" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情分析</span>
|
<span class="ml-2">舆情分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingChar" class="svg-icon">
|
<a href="/page/yuqingChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情预测</span>
|
<span class="ml-2">舆情预测</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2">
|
<li class="px-3 pt-3 pb-2">
|
||||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleCloud" class="svg-icon">
|
<a href="/page/articleCloud" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">文章内容词云图</span>
|
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-4 mt-1">
|
<div class="col-md-12 mb-4 mt-1">
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
<h4 class="font-weight-bold">IP分析</h4>
|
<h4 class="font-weight-bold" data-i18n="ipChar">IP分析</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">文章IP位置分析图</h4>
|
<h4 class="card-title mb-0" data-i18n="articleIpLocationAnalysis">文章IP位置分析图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="main" style="width: 100%;height: 750px"></div>
|
<div id="main" style="width: 100%;height: 750px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">评论IP位置分析图</h4>
|
<h4 class="card-title mb-0" data-i18n="commentIpLocationAnalysis">评论IP位置分析图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainTwo" style="width: 100%;height: 750px"></div>
|
<div id="mainTwo" style="width: 100%;height: 750px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block echarts %}
|
{% block echarts %}
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('main');
|
var chartDom = document.getElementById('main');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
title:{
|
title:{
|
||||||
text:'文章IP发布地址地图',
|
text:'文章IP发布地址地图',
|
||||||
left:'center',
|
left:'center',
|
||||||
textStyle:{
|
textStyle:{
|
||||||
color:"#333",
|
color:"#333",
|
||||||
fontWeight:"bold"
|
fontWeight:"bold"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip:{
|
tooltip:{
|
||||||
trigger:'item',
|
trigger:'item',
|
||||||
formatter:function(params){
|
formatter:function(params){
|
||||||
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visualMap:{
|
visualMap:{
|
||||||
min:0,
|
min:0,
|
||||||
max:50,
|
max:50,
|
||||||
text:['高','低'],
|
text:['高','低'],
|
||||||
realtime:true,
|
realtime:true,
|
||||||
calulable:true,
|
calulable:true,
|
||||||
inRange:{
|
inRange:{
|
||||||
color:['orange','green']
|
color:['orange','green']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series:[{
|
series:[{
|
||||||
type:'map',
|
type:'map',
|
||||||
map:"china",
|
map:"china",
|
||||||
label:{
|
label:{
|
||||||
normal:{
|
normal:{
|
||||||
show:true,
|
show:true,
|
||||||
color:"white",
|
color:"white",
|
||||||
fontSize:'12'
|
fontSize:'12'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emphasis:{
|
emphasis:{
|
||||||
label:{
|
label:{
|
||||||
show:true
|
show:true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data:{{ articleRegionData | tojson }},
|
data:{{ articleRegionData | tojson }},
|
||||||
itemStyle:{
|
itemStyle:{
|
||||||
normal: {
|
normal: {
|
||||||
areaColor:"skyblue",
|
areaColor:"skyblue",
|
||||||
borderColor:"#fff"
|
borderColor:"#fff"
|
||||||
},
|
},
|
||||||
emphasis:{
|
emphasis:{
|
||||||
areaColor: '#2b91b7'
|
areaColor: '#2b91b7'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
zoom:1.4,
|
zoom:1.4,
|
||||||
roam:true
|
roam:true
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('mainTwo');
|
var chartDom = document.getElementById('mainTwo');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
title:{
|
title:{
|
||||||
text:'文章IP发布地址地图',
|
text:'文章IP发布地址地图',
|
||||||
left:'center',
|
left:'center',
|
||||||
textStyle:{
|
textStyle:{
|
||||||
color:"#333",
|
color:"#333",
|
||||||
fontWeight:"bold"
|
fontWeight:"bold"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip:{
|
tooltip:{
|
||||||
trigger:'item',
|
trigger:'item',
|
||||||
formatter:function(params){
|
formatter:function(params){
|
||||||
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visualMap:{
|
visualMap:{
|
||||||
min:0,
|
min:0,
|
||||||
max:200,
|
max:200,
|
||||||
text:['高','低'],
|
text:['高','低'],
|
||||||
realtime:true,
|
realtime:true,
|
||||||
calulable:true,
|
calulable:true,
|
||||||
inRange:{
|
inRange:{
|
||||||
color:['orange','green']
|
color:['orange','green']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series:[{
|
series:[{
|
||||||
type:'map',
|
type:'map',
|
||||||
map:"china",
|
map:"china",
|
||||||
label:{
|
label:{
|
||||||
normal:{
|
normal:{
|
||||||
show:true,
|
show:true,
|
||||||
color:"white",
|
color:"white",
|
||||||
fontSize:'12'
|
fontSize:'12'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emphasis:{
|
emphasis:{
|
||||||
label:{
|
label:{
|
||||||
show:true
|
show:true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data:{{ commentRegionData | tojson }},
|
data:{{ commentRegionData | tojson }},
|
||||||
itemStyle:{
|
itemStyle:{
|
||||||
normal: {
|
normal: {
|
||||||
areaColor:"skyblue",
|
areaColor:"skyblue",
|
||||||
borderColor:"#fff"
|
borderColor:"#fff"
|
||||||
},
|
},
|
||||||
emphasis:{
|
emphasis:{
|
||||||
areaColor: '#2b91b7'
|
areaColor: '#2b91b7'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
zoom:1.4,
|
zoom:1.4,
|
||||||
roam:true
|
roam:true
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+227
-224
@@ -1,225 +1,228 @@
|
|||||||
{% extends 'base_page.html' %}
|
{% extends 'base_page.html' %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
微博舆情统计页
|
<span data-i18n="tableData">微博舆情统计页</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="iq-sidebar-menu">
|
<nav class="iq-sidebar-menu">
|
||||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/home" class="svg-icon">
|
<a href="/page/home" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">首页</span>
|
<span class="ml-2" data-i18n="home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/hotWord" class="svg-icon ">
|
<a href="/page/hotWord" class="svg-icon ">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">热词统计</span>
|
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/tableData" class="svg-icon">
|
<a href="/page/tableData" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">微博舆情统计</span>
|
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleChar" class="svg-icon">
|
<a href="/page/articleChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">文章分析</span>
|
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/ipChar" class="svg-icon">
|
<a href="/page/ipChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">IP分析</span>
|
<span class="ml-2">IP分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/commentChar" class="svg-icon">
|
<a href="/page/commentChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">评论分析</span>
|
</i><span class="ml-2">评论分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingChar" class="svg-icon">
|
<a href="/page/yuqingChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情分析</span>
|
<span class="ml-2">舆情分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingpredict" class="svg-icon">
|
<a href="/page/yuqingpredict" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情预测</span>
|
<span class="ml-2">舆情预测</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2">
|
<li class="px-3 pt-3 pb-2">
|
||||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleCloud" class="svg-icon">
|
<a href="/page/articleCloud" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">文章内容词云图</span>
|
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-4 mt-1">
|
<div class="col-md-12 mb-4 mt-1">
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
<h4 class="font-weight-bold">微博舆情统计页</h4>
|
<h4 class="font-weight-bold" data-i18n="tableData">微博舆情统计页</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header d-flex justify-content-between">
|
<div class="card-header d-flex justify-content-between">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title">微博文章统计表格 - 舆情 情感分类
|
<h4 class="card-title" data-i18n="weiboArticleStatTable">微博文章统计表格 - 舆情 情感分类</h4>
|
||||||
</h4>
|
</div>
|
||||||
</div>
|
<div class="header-action">
|
||||||
<div class="header-action">
|
<i data-toggle="collapse" data-target="#datatable-1" aria-expanded="false">
|
||||||
<i data-toggle="collapse" data-target="#datatable-1" aria-expanded="false">
|
<svg width="20" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<svg width="20" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path>
|
</svg>
|
||||||
</svg>
|
</i>
|
||||||
</i>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="card-body">
|
||||||
<div class="card-body">
|
<p><a href="/page/tableData?flag=True" class="btn btn-primary btn-sm" data-i18n="sentimentClassification">情感分类</a></p>
|
||||||
<p><a href="/page/tableData?flag=True" class="btn btn-primary btn-sm">情感分类</a></p>
|
<div class="table-responsive">
|
||||||
<div class="table-responsive">
|
<div id="datatable-1_wrapper" class="dataTables_wrapper dt-bootstrap4">
|
||||||
<div id="datatable-1_wrapper" class="dataTables_wrapper dt-bootstrap4">
|
<div class="row">
|
||||||
<div class="row">
|
<table id="datatable-1" class="table data-table table-striped table-bordered dataTable" role="grid" aria-describedby="datatable-1_info">
|
||||||
<table id="datatable-1" class="table data-table table-striped table-bordered dataTable" role="grid" aria-describedby="datatable-1_info">
|
<thead>
|
||||||
<thead>
|
<tr>
|
||||||
<tr>
|
<th style="font-weight: bold" data-i18n="articleId">
|
||||||
<th style="font-weight: bold">
|
文章ID
|
||||||
文章ID
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleIp">
|
||||||
<th style="font-weight: bold">
|
文章IP
|
||||||
文章IP
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleTitle">
|
||||||
<th style="font-weight: bold">
|
文章标题
|
||||||
点赞量
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleLike">
|
||||||
<th style="font-weight: bold">
|
点赞量
|
||||||
转发量
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleForward">
|
||||||
<th style="font-weight: bold">
|
转发量
|
||||||
评论量
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleComment">
|
||||||
<th style="font-weight: bold">
|
评论量
|
||||||
类型
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleType">
|
||||||
<th style="font-weight: bold">
|
类型
|
||||||
内容
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleContent">
|
||||||
<th style="font-weight: bold">
|
内容
|
||||||
发布时间
|
</th>
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="articleTime">
|
||||||
{% if defaultFlag %}
|
发布时间
|
||||||
<th style="font-weight: bold">
|
</th>
|
||||||
情感分类
|
{% if defaultFlag %}
|
||||||
</th>
|
<th style="font-weight: bold" data-i18n="sentimentClassification">
|
||||||
{% endif %}
|
情感分类
|
||||||
</tr>
|
</th>
|
||||||
</thead>
|
{% endif %}
|
||||||
<tbody>
|
</tr>
|
||||||
{% for article in tableData %}
|
</thead>
|
||||||
<tr class="even">
|
<tbody>
|
||||||
<td style="width:330px" class="sorting_1">
|
{% for article in tableData %}
|
||||||
<a href="{{ article[9] }}">
|
<tr class="even">
|
||||||
{{ article[0] }}
|
<td style="width:330px" class="sorting_1">
|
||||||
</a>
|
<a href="{{ article[9] }}">
|
||||||
</td>
|
{{ article[0] }}
|
||||||
<td style="width:90px">{{ article[4] }}</td>
|
</a>
|
||||||
<td style="width:90px">👍{{ article[1] }}</td>
|
</td>
|
||||||
<td style="width:90px">🥇{{ article[2] }}</td>
|
<td style="width:90px">{{ article[4] }}</td>
|
||||||
<td style="width:90px">🔥{{ article[3] }}</td>
|
<td style="width:90px" class="text-right">{{ article[5] }}</td>
|
||||||
<td style="width:90px" class="text-right">{{ article[8] }}</td>
|
<td style="width:90px">👍{{ article[1] }}</td>
|
||||||
<td style="width:155px" class="text-right">{{ article[5] }}</td>
|
<td style="width:90px">🥇{{ article[2] }}</td>
|
||||||
<td style="width:90px" class="text-right">{{ article[7] }}</td>
|
<td style="width:90px">🔥{{ article[3] }}</td>
|
||||||
{% if defaultFlag %}
|
<td style="width:90px" class="text-right">{{ article[8] }}</td>
|
||||||
<td style="width:90px">
|
<td style="width:155px" class="text-right">{{ article[5] }}</td>
|
||||||
{% if article[-1] == '正面' %}
|
<td style="width:90px" class="text-right">{{ article[7] }}</td>
|
||||||
<span class="text-success">
|
{% if defaultFlag %}
|
||||||
{{ article[-1] }}
|
<td style="width:90px">
|
||||||
</span>
|
{% if article[-1] == '正面' %}
|
||||||
|
<span class="text-success">
|
||||||
{% else %}
|
{{ article[-1] }}
|
||||||
<span class="text-danger">
|
</span>
|
||||||
{{ article[-1] }}
|
|
||||||
</span>
|
{% else %}
|
||||||
|
<span class="text-danger">
|
||||||
{% endif %}
|
{{ article[-1] }}
|
||||||
</td>
|
</span>
|
||||||
{% endif %}
|
|
||||||
</tr>
|
{% endif %}
|
||||||
{% endfor %}
|
</td>
|
||||||
</tbody>
|
{% endif %}
|
||||||
</table>
|
</tr>
|
||||||
</div>
|
{% endfor %}
|
||||||
</div>
|
</tbody>
|
||||||
</div>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
{% block echarts %}
|
{% endblock %}
|
||||||
<script>
|
|
||||||
|
|
||||||
</script>
|
{% block echarts %}
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,401 +1,401 @@
|
|||||||
{% extends 'base_page.html' %}
|
{% extends 'base_page.html' %}
|
||||||
{% block title %}
|
{% block title %}
|
||||||
舆情分析
|
<span data-i18n="yuqingChar">舆情分析</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="iq-sidebar-menu">
|
<nav class="iq-sidebar-menu">
|
||||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/home" class="svg-icon">
|
<a href="/page/home" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">首页</span>
|
<span class="ml-2" data-i18n="home">首页</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/hotWord" class="svg-icon ">
|
<a href="/page/hotWord" class="svg-icon ">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">热词统计</span>
|
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/tableData" class="svg-icon">
|
<a href="/page/tableData" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">微博舆情统计</span>
|
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2 ">
|
<li class="px-3 pt-3 pb-2 ">
|
||||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleChar" class="svg-icon">
|
<a href="/page/articleChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">文章分析</span>
|
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/ipChar" class="svg-icon">
|
<a href="/page/ipChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">IP分析</span>
|
<span class="ml-2">IP分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/commentChar" class="svg-icon">
|
<a href="/page/commentChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">评论分析</span>
|
</i><span class="ml-2">评论分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingChar" class="svg-icon">
|
<a href="/page/yuqingChar" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情分析</span>
|
<span class="ml-2">舆情分析</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active sidebar-layout">
|
<li class="active sidebar-layout">
|
||||||
<a href="/page/yuqingpredict" class="svg-icon">
|
<a href="/page/yuqingpredict" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i>
|
</i>
|
||||||
<span class="ml-2">舆情预测</span>
|
<span class="ml-2">舆情预测</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="px-3 pt-3 pb-2">
|
<li class="px-3 pt-3 pb-2">
|
||||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||||
</li>
|
</li>
|
||||||
<li class=" sidebar-layout">
|
<li class=" sidebar-layout">
|
||||||
<a href="/page/articleCloud" class="svg-icon">
|
<a href="/page/articleCloud" class="svg-icon">
|
||||||
<i class="">
|
<i class="">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</i><span class="ml-2">文章内容词云图</span>
|
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-4 mt-1">
|
<div class="col-md-12 mb-4 mt-1">
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
<h4 class="font-weight-bold">舆情分析</h4>
|
<h4 class="font-weight-bold" data-i18n="yuqingChar">舆情分析</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">热词情感趋势柱状图</h4>
|
<h4 class="card-title mb-0" data-i18n="hotWordSentimentTrendBar">热词情感趋势柱状图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="main" style="width: 100%;height: 450px"></div>
|
<div id="main" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">热词情感趋势树形图</h4>
|
<h4 class="card-title mb-0" data-i18n="hotWordSentimentTrendTree">热词情感趋势树形图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">文章内容与评论内容舆情趋势饼状图</h4>
|
<h4 class="card-title mb-0" data-i18n="articleCommentSentimentTrendPie">文章内容与评论内容舆情趋势饼状图</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card card-block">
|
<div class="card card-block">
|
||||||
<div class="card-header d-flex justify-content-between pb-0">
|
<div class="card-header d-flex justify-content-between pb-0">
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<h4 class="card-title mb-0">热词TOP10</h4>
|
<h4 class="card-title mb-0">热词TOP10</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="mainFore" style="width: 100%;height: 450px"></div>
|
<div id="mainFore" style="width: 100%;height: 450px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 mb-3">
|
<div class="col-lg-12 mb-3">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="modelSelect">选择分析模型:</label>
|
<label for="modelSelect">选择分析模型:</label>
|
||||||
<select class="form-control" id="modelSelect" onchange="updateModel(this.value)">
|
<select class="form-control" id="modelSelect" onchange="updateModel(this.value)">
|
||||||
<optgroup label="基础模型">
|
<optgroup label="基础模型">
|
||||||
<option value="basic" {% if model_type == 'basic' %}selected{% endif %}>SnowNLP</option>
|
<option value="basic" {% if model_type == 'basic' %}selected{% endif %}>SnowNLP</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="OpenAI 模型">
|
<optgroup label="OpenAI 模型">
|
||||||
<option value="gpt-3.5-turbo" {% if model_type == 'gpt-3.5-turbo' %}selected{% endif %}>GPT-3.5-Turbo</option>
|
<option value="gpt-3.5-turbo" {% if model_type == 'gpt-3.5-turbo' %}selected{% endif %}>GPT-3.5-Turbo</option>
|
||||||
<option value="gpt-4" {% if model_type == 'gpt-4' %}selected{% endif %}>GPT-4</option>
|
<option value="gpt-4" {% if model_type == 'gpt-4' %}selected{% endif %}>GPT-4</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Claude 模型">
|
<optgroup label="Claude 模型">
|
||||||
<option value="claude-3-opus-20240229" {% if model_type == 'claude-3-opus-20240229' %}selected{% endif %}>Claude-3 Opus</option>
|
<option value="claude-3-opus-20240229" {% if model_type == 'claude-3-opus-20240229' %}selected{% endif %}>Claude-3 Opus</option>
|
||||||
<option value="claude-3-sonnet-20240229" {% if model_type == 'claude-3-sonnet-20240229' %}selected{% endif %}>Claude-3 Sonnet</option>
|
<option value="claude-3-sonnet-20240229" {% if model_type == 'claude-3-sonnet-20240229' %}selected{% endif %}>Claude-3 Sonnet</option>
|
||||||
<option value="claude-3-haiku-20240307" {% if model_type == 'claude-3-haiku-20240307' %}selected{% endif %}>Claude-3 Haiku</option>
|
<option value="claude-3-haiku-20240307" {% if model_type == 'claude-3-haiku-20240307' %}selected{% endif %}>Claude-3 Haiku</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="DeepSeek 模型">
|
<optgroup label="DeepSeek 模型">
|
||||||
<option value="deepseek-chat" {% if model_type == 'deepseek-chat' %}selected{% endif %}>DeepSeek-V3</option>
|
<option value="deepseek-chat" {% if model_type == 'deepseek-chat' %}selected{% endif %}>DeepSeek-V3</option>
|
||||||
<option value="deepseek-reasoner" {% if model_type == 'deepseek-reasoner' %}selected{% endif %}>DeepSeek-R1</option>
|
<option value="deepseek-reasoner" {% if model_type == 'deepseek-reasoner' %}selected{% endif %}>DeepSeek-R1</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block echarts %}
|
{% block echarts %}
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('main');
|
var chartDom = document.getElementById('main');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
var colors = ['#66CC99', '#FFCC66', '#FF6666'];
|
var colors = ['#66CC99', '#FFCC66', '#FF6666'];
|
||||||
option = {
|
option = {
|
||||||
title: {
|
title: {
|
||||||
text: '热词情感分析柱状图'
|
text: '热词情感分析柱状图'
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis'
|
trigger: 'axis'
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: ['Rainfall']
|
data: ['Rainfall']
|
||||||
},
|
},
|
||||||
toolbox: {
|
toolbox: {
|
||||||
show: true,
|
show: true,
|
||||||
feature: {
|
feature: {
|
||||||
dataView: { show: true, readOnly: false },
|
dataView: { show: true, readOnly: false },
|
||||||
magicType: { show: true, type: ['line', 'bar'] },
|
magicType: { show: true, type: ['line', 'bar'] },
|
||||||
restore: { show: true },
|
restore: { show: true },
|
||||||
saveAsImage: { show: true }
|
saveAsImage: { show: true }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calculable: true,
|
calculable: true,
|
||||||
xAxis: [
|
xAxis: [
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
data: {{ xData | tojson }}
|
data: {{ xData | tojson }}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value'
|
type: 'value'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '舆情个数',
|
name: '舆情个数',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {{ yData }},
|
data: {{ yData }},
|
||||||
markPoint: {
|
markPoint: {
|
||||||
data: [
|
data: [
|
||||||
{ type: 'max', name: 'Max' },
|
{ type: 'max', name: 'Max' },
|
||||||
{ type: 'min', name: 'Min' }
|
{ type: 'min', name: 'Min' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
markLine: {
|
markLine: {
|
||||||
data: [{ type: 'average', name: 'Avg' }]
|
data: [{ type: 'average', name: 'Avg' }]
|
||||||
},
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: function (parmas) {
|
color: function (parmas) {
|
||||||
return colors[parmas.dataIndex % colors.length];
|
return colors[parmas.dataIndex % colors.length];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('mainTwo');
|
var chartDom = document.getElementById('mainTwo');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
type: 'treemap',
|
type: 'treemap',
|
||||||
data: {{ biedata | tojson }}
|
data: {{ biedata | tojson }}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('mainThree');
|
var chartDom = document.getElementById('mainThree');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: [
|
data: [
|
||||||
'正面',
|
'正面',
|
||||||
'负面',
|
'负面',
|
||||||
'中性'
|
'中性'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '评论舆情结果',
|
name: '评论舆情结果',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
selectedMode: 'single',
|
selectedMode: 'single',
|
||||||
radius: [0, '30%'],
|
radius: [0, '30%'],
|
||||||
label: {
|
label: {
|
||||||
position: 'inner',
|
position: 'inner',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
labelLine: {
|
labelLine: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
data: {{ biedata1 | tojson }}
|
data: {{ biedata1 | tojson }}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '文章舆情结果',
|
name: '文章舆情结果',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['45%', '60%'],
|
radius: ['45%', '60%'],
|
||||||
labelLine: {
|
labelLine: {
|
||||||
length: 30
|
length: 30
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
|
formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
|
||||||
backgroundColor: '#F6F8FC',
|
backgroundColor: '#F6F8FC',
|
||||||
borderColor: '#8C8D8E',
|
borderColor: '#8C8D8E',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
rich: {
|
rich: {
|
||||||
a: {
|
a: {
|
||||||
color: '#6E7079',
|
color: '#6E7079',
|
||||||
lineHeight: 22,
|
lineHeight: 22,
|
||||||
align: 'center'
|
align: 'center'
|
||||||
},
|
},
|
||||||
hr: {
|
hr: {
|
||||||
borderColor: '#8C8D8E',
|
borderColor: '#8C8D8E',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
height: 0
|
height: 0
|
||||||
},
|
},
|
||||||
b: {
|
b: {
|
||||||
color: '#4C5058',
|
color: '#4C5058',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
lineHeight: 33
|
lineHeight: 33
|
||||||
},
|
},
|
||||||
per: {
|
per: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
backgroundColor: '#4C5058',
|
backgroundColor: '#4C5058',
|
||||||
padding: [3, 4],
|
padding: [3, 4],
|
||||||
borderRadius: 4
|
borderRadius: 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data: {{ biedata2 | tojson }}
|
data: {{ biedata2 | tojson }}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var chartDom = document.getElementById('mainFore');
|
var chartDom = document.getElementById('mainFore');
|
||||||
var myChart = echarts.init(chartDom);
|
var myChart = echarts.init(chartDom);
|
||||||
var option;
|
var option;
|
||||||
|
|
||||||
option = {
|
option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
type: 'shadow'
|
type: 'shadow'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
legend: {},
|
legend: {},
|
||||||
grid: {
|
grid: {
|
||||||
left: '3%',
|
left: '3%',
|
||||||
right: '4%',
|
right: '4%',
|
||||||
bottom: '3%',
|
bottom: '3%',
|
||||||
containLabel: true
|
containLabel: true
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
boundaryGap: [0, 0.01]
|
boundaryGap: [0, 0.01]
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: {{ x1Data | tojson }}
|
data: {{ x1Data | tojson }}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '热词出现个数',
|
name: '热词出现个数',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data:{{ y1Data }}
|
data:{{ y1Data }}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
option && myChart.setOption(option);
|
option && myChart.setOption(option);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
function updateModel(value) {
|
function updateModel(value) {
|
||||||
window.location.href = '/page/yuqingChar?model=' + value;
|
window.location.href = '/page/yuqingChar?model=' + value;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
File diff suppressed because it is too large
Load Diff
+370
-370
@@ -1,371 +1,371 @@
|
|||||||
from flask import Blueprint, jsonify, request, render_template
|
from flask import Blueprint, jsonify, request, render_template
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import threading
|
import threading
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
import asyncio
|
import asyncio
|
||||||
import websockets
|
import websockets
|
||||||
import logging
|
import logging
|
||||||
from spider.spiderData import SpiderData
|
from spider.spiderData import SpiderData
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
from anthropic import Anthropic
|
from anthropic import Anthropic
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from ratelimit import limits, sleep_and_retry
|
from ratelimit import limits, sleep_and_retry
|
||||||
from tenacity import retry, stop_after_attempt, wait_exponential
|
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||||
|
|
||||||
# 创建蓝图
|
# 创建蓝图
|
||||||
spider_bp = Blueprint('spider', __name__)
|
spider_bp = Blueprint('spider', __name__)
|
||||||
|
|
||||||
# 创建日志记录器
|
# 创建日志记录器
|
||||||
logger = logging.getLogger('spider_control')
|
logger = logging.getLogger('spider_control')
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
# 存储WebSocket连接的集合
|
# 存储WebSocket连接的集合
|
||||||
websocket_connections = set()
|
websocket_connections = set()
|
||||||
|
|
||||||
# 创建消息队列
|
# 创建消息队列
|
||||||
message_queue = Queue()
|
message_queue = Queue()
|
||||||
|
|
||||||
# 创建线程池
|
# 创建线程池
|
||||||
thread_pool = ThreadPoolExecutor(max_workers=3)
|
thread_pool = ThreadPoolExecutor(max_workers=3)
|
||||||
|
|
||||||
# 创建异步事件循环
|
# 创建异步事件循环
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
# 默认配置
|
# 默认配置
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
'crawlDepth': 3,
|
'crawlDepth': 3,
|
||||||
'interval': 5,
|
'interval': 5,
|
||||||
'maxRetries': 3,
|
'maxRetries': 3,
|
||||||
'timeout': 30,
|
'timeout': 30,
|
||||||
'maxConcurrent': 2
|
'maxConcurrent': 2
|
||||||
}
|
}
|
||||||
|
|
||||||
# 限流装饰器
|
# 限流装饰器
|
||||||
@sleep_and_retry
|
@sleep_and_retry
|
||||||
@limits(calls=100, period=60) # 每分钟最多100个请求
|
@limits(calls=100, period=60) # 每分钟最多100个请求
|
||||||
def rate_limited_request():
|
def rate_limited_request():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class SpiderWorker:
|
class SpiderWorker:
|
||||||
def __init__(self, topics, parameters):
|
def __init__(self, topics, parameters):
|
||||||
self.topics = topics
|
self.topics = topics
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
self.total_topics = len(topics)
|
self.total_topics = len(topics)
|
||||||
self.completed_topics = 0
|
self.completed_topics = 0
|
||||||
self.spider = SpiderData()
|
self.spider = SpiderData()
|
||||||
self.message_buffer = []
|
self.message_buffer = []
|
||||||
self.message_buffer_size = 10
|
self.message_buffer_size = 10
|
||||||
self.semaphore = asyncio.Semaphore(parameters.get('maxConcurrent', DEFAULT_CONFIG['maxConcurrent']))
|
self.semaphore = asyncio.Semaphore(parameters.get('maxConcurrent', DEFAULT_CONFIG['maxConcurrent']))
|
||||||
|
|
||||||
async def send_message(self, message):
|
async def send_message(self, message):
|
||||||
"""异步发送消息,使用缓冲区优化"""
|
"""异步发送消息,使用缓冲区优化"""
|
||||||
self.message_buffer.append(message)
|
self.message_buffer.append(message)
|
||||||
if len(self.message_buffer) >= self.message_buffer_size:
|
if len(self.message_buffer) >= self.message_buffer_size:
|
||||||
await self.flush_messages()
|
await self.flush_messages()
|
||||||
|
|
||||||
async def flush_messages(self):
|
async def flush_messages(self):
|
||||||
"""刷新消息缓冲区"""
|
"""刷新消息缓冲区"""
|
||||||
if not self.message_buffer:
|
if not self.message_buffer:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await broadcast_message(self.message_buffer)
|
await broadcast_message(self.message_buffer)
|
||||||
self.message_buffer.clear()
|
self.message_buffer.clear()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送消息失败: {e}")
|
logger.error(f"发送消息失败: {e}")
|
||||||
|
|
||||||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
||||||
async def crawl_single_topic(self, topic):
|
async def crawl_single_topic(self, topic):
|
||||||
"""爬取单个话题"""
|
"""爬取单个话题"""
|
||||||
try:
|
try:
|
||||||
rate_limited_request()
|
rate_limited_request()
|
||||||
|
|
||||||
await self.send_message({
|
await self.send_message({
|
||||||
'type': 'log',
|
'type': 'log',
|
||||||
'message': f'开始爬取话题: {topic}'
|
'message': f'开始爬取话题: {topic}'
|
||||||
})
|
})
|
||||||
|
|
||||||
async with self.semaphore:
|
async with self.semaphore:
|
||||||
await asyncio.get_event_loop().run_in_executor(
|
await asyncio.get_event_loop().run_in_executor(
|
||||||
thread_pool,
|
thread_pool,
|
||||||
self.spider.crawl_topic,
|
self.spider.crawl_topic,
|
||||||
topic,
|
topic,
|
||||||
self.parameters['crawlDepth'],
|
self.parameters['crawlDepth'],
|
||||||
self.parameters['interval'],
|
self.parameters['interval'],
|
||||||
self.parameters['maxRetries'],
|
self.parameters['maxRetries'],
|
||||||
self.parameters['timeout']
|
self.parameters['timeout']
|
||||||
)
|
)
|
||||||
|
|
||||||
self.completed_topics += 1
|
self.completed_topics += 1
|
||||||
progress = int((self.completed_topics / self.total_topics) * 100)
|
progress = int((self.completed_topics / self.total_topics) * 100)
|
||||||
|
|
||||||
await self.send_message({
|
await self.send_message({
|
||||||
'type': 'progress',
|
'type': 'progress',
|
||||||
'value': progress
|
'value': progress
|
||||||
})
|
})
|
||||||
|
|
||||||
await self.send_message({
|
await self.send_message({
|
||||||
'type': 'log',
|
'type': 'log',
|
||||||
'message': f'话题 {topic} 爬取完成'
|
'message': f'话题 {topic} 爬取完成'
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"爬取话题 {topic} 失败: {e}")
|
logger.error(f"爬取话题 {topic} 失败: {e}")
|
||||||
await self.send_message({
|
await self.send_message({
|
||||||
'type': 'log',
|
'type': 'log',
|
||||||
'message': f'爬取话题 {topic} 时出错: {str(e)}'
|
'message': f'爬取话题 {topic} 时出错: {str(e)}'
|
||||||
})
|
})
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
"""运行爬虫任务"""
|
"""运行爬虫任务"""
|
||||||
try:
|
try:
|
||||||
tasks = [self.crawl_single_topic(topic) for topic in self.topics]
|
tasks = [self.crawl_single_topic(topic) for topic in self.topics]
|
||||||
await asyncio.gather(*tasks)
|
await asyncio.gather(*tasks)
|
||||||
await self.flush_messages()
|
await self.flush_messages()
|
||||||
|
|
||||||
await self.send_message({
|
await self.send_message({
|
||||||
'type': 'log',
|
'type': 'log',
|
||||||
'message': '所有话题爬取完成'
|
'message': '所有话题爬取完成'
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"爬虫任务执行出错: {e}")
|
logger.error(f"爬虫任务执行出错: {e}")
|
||||||
await self.send_message({
|
await self.send_message({
|
||||||
'type': 'log',
|
'type': 'log',
|
||||||
'message': f'爬虫任务执行出错: {str(e)}'
|
'message': f'爬虫任务执行出错: {str(e)}'
|
||||||
})
|
})
|
||||||
finally:
|
finally:
|
||||||
await self.flush_messages()
|
await self.flush_messages()
|
||||||
|
|
||||||
async def broadcast_message(messages):
|
async def broadcast_message(messages):
|
||||||
"""广播消息到所有WebSocket连接"""
|
"""广播消息到所有WebSocket连接"""
|
||||||
if not websocket_connections:
|
if not websocket_connections:
|
||||||
return
|
return
|
||||||
|
|
||||||
for websocket in websocket_connections.copy():
|
for websocket in websocket_connections.copy():
|
||||||
try:
|
try:
|
||||||
if isinstance(messages, list):
|
if isinstance(messages, list):
|
||||||
for message in messages:
|
for message in messages:
|
||||||
await websocket.send(json.dumps(message))
|
await websocket.send(json.dumps(message))
|
||||||
else:
|
else:
|
||||||
await websocket.send(json.dumps(messages))
|
await websocket.send(json.dumps(messages))
|
||||||
except websockets.exceptions.ConnectionClosed:
|
except websockets.exceptions.ConnectionClosed:
|
||||||
websocket_connections.remove(websocket)
|
websocket_connections.remove(websocket)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送WebSocket消息失败: {e}")
|
logger.error(f"发送WebSocket消息失败: {e}")
|
||||||
websocket_connections.remove(websocket)
|
websocket_connections.remove(websocket)
|
||||||
|
|
||||||
@spider_bp.route('/spider/control')
|
@spider_bp.route('/spider/control')
|
||||||
def spider_control():
|
def spider_control():
|
||||||
"""渲染爬虫控制页面"""
|
"""渲染爬虫控制页面"""
|
||||||
return render_template('spider_control.html')
|
return render_template('spider_control.html')
|
||||||
|
|
||||||
@spider_bp.route('/api/spider/start', methods=['POST'])
|
@spider_bp.route('/api/spider/start', methods=['POST'])
|
||||||
async def start_spider():
|
async def start_spider():
|
||||||
"""启动爬虫任务"""
|
"""启动爬虫任务"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
topics = data.get('topics', [])
|
topics = data.get('topics', [])
|
||||||
parameters = {**DEFAULT_CONFIG, **data.get('parameters', {})}
|
parameters = {**DEFAULT_CONFIG, **data.get('parameters', {})}
|
||||||
|
|
||||||
if not topics:
|
if not topics:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': '请选择至少一个话题'
|
'message': '请选择至少一个话题'
|
||||||
})
|
})
|
||||||
|
|
||||||
# 创建爬虫工作器
|
# 创建爬虫工作器
|
||||||
worker = SpiderWorker(topics, parameters)
|
worker = SpiderWorker(topics, parameters)
|
||||||
|
|
||||||
# 在事件循环中运行爬虫任务
|
# 在事件循环中运行爬虫任务
|
||||||
asyncio.create_task(worker.run())
|
asyncio.create_task(worker.run())
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': '爬虫任务已启动'
|
'message': '爬虫任务已启动'
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"启动爬虫任务失败: {e}")
|
logger.error(f"启动爬虫任务失败: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': str(e)
|
'message': str(e)
|
||||||
})
|
})
|
||||||
|
|
||||||
@spider_bp.route('/api/spider/save-config', methods=['POST'])
|
@spider_bp.route('/api/spider/save-config', methods=['POST'])
|
||||||
def save_spider_config():
|
def save_spider_config():
|
||||||
"""保存爬虫配置"""
|
"""保存爬虫配置"""
|
||||||
try:
|
try:
|
||||||
config = request.get_json()
|
config = request.get_json()
|
||||||
if save_config(config):
|
if save_config(config):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': '配置保存成功'
|
'message': '配置保存成功'
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': '配置保存失败'
|
'message': '配置保存失败'
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"保存配置失败: {e}")
|
logger.error(f"保存配置失败: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': str(e)
|
'message': str(e)
|
||||||
})
|
})
|
||||||
|
|
||||||
@spider_bp.websocket('/ws/spider-status')
|
@spider_bp.websocket('/ws/spider-status')
|
||||||
async def spider_status_socket(websocket):
|
async def spider_status_socket(websocket):
|
||||||
"""WebSocket连接处理"""
|
"""WebSocket连接处理"""
|
||||||
try:
|
try:
|
||||||
websocket_connections.add(websocket)
|
websocket_connections.add(websocket)
|
||||||
logging.info("新的WebSocket连接已建立")
|
logging.info("新的WebSocket连接已建立")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
# 等待消息,保持连接活跃
|
# 等待消息,保持连接活跃
|
||||||
message = await websocket.receive()
|
message = await websocket.receive()
|
||||||
if message is None:
|
if message is None:
|
||||||
break
|
break
|
||||||
except websockets.exceptions.ConnectionClosed:
|
except websockets.exceptions.ConnectionClosed:
|
||||||
logging.info("WebSocket连接已关闭")
|
logging.info("WebSocket连接已关闭")
|
||||||
finally:
|
finally:
|
||||||
websocket_connections.remove(websocket)
|
websocket_connections.remove(websocket)
|
||||||
logging.info("WebSocket连接已移除")
|
logging.info("WebSocket连接已移除")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"WebSocket连接处理失败: {e}")
|
logger.error(f"WebSocket连接处理失败: {e}")
|
||||||
if websocket in websocket_connections:
|
if websocket in websocket_connections:
|
||||||
websocket_connections.remove(websocket)
|
websocket_connections.remove(websocket)
|
||||||
|
|
||||||
def get_ai_client():
|
def get_ai_client():
|
||||||
"""获取可用的AI客户端"""
|
"""获取可用的AI客户端"""
|
||||||
# 按优先级尝试不同的AI服务
|
# 按优先级尝试不同的AI服务
|
||||||
if os.getenv('ANTHROPIC_API_KEY'):
|
if os.getenv('ANTHROPIC_API_KEY'):
|
||||||
return {
|
return {
|
||||||
'type': 'anthropic',
|
'type': 'anthropic',
|
||||||
'client': Anthropic(api_key=os.getenv('ANTHROPIC_API_KEY'))
|
'client': Anthropic(api_key=os.getenv('ANTHROPIC_API_KEY'))
|
||||||
}
|
}
|
||||||
elif os.getenv('OPENAI_API_KEY'):
|
elif os.getenv('OPENAI_API_KEY'):
|
||||||
return {
|
return {
|
||||||
'type': 'openai',
|
'type': 'openai',
|
||||||
'client': OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
|
'client': OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
raise ValueError("未找到可用的AI API密钥")
|
raise ValueError("未找到可用的AI API密钥")
|
||||||
|
|
||||||
def parse_ai_response(response_text):
|
def parse_ai_response(response_text):
|
||||||
"""解析AI响应中的JSON配置"""
|
"""解析AI响应中的JSON配置"""
|
||||||
try:
|
try:
|
||||||
# 查找JSON内容
|
# 查找JSON内容
|
||||||
start = response_text.find('{')
|
start = response_text.find('{')
|
||||||
end = response_text.rfind('}') + 1
|
end = response_text.rfind('}') + 1
|
||||||
if start == -1 or end == 0:
|
if start == -1 or end == 0:
|
||||||
raise ValueError("未找到有效的JSON配置")
|
raise ValueError("未找到有效的JSON配置")
|
||||||
|
|
||||||
json_str = response_text[start:end]
|
json_str = response_text[start:end]
|
||||||
config = json.loads(json_str)
|
config = json.loads(json_str)
|
||||||
|
|
||||||
# 验证配置格式
|
# 验证配置格式
|
||||||
if not isinstance(config.get('topics'), list):
|
if not isinstance(config.get('topics'), list):
|
||||||
raise ValueError("配置必须包含话题列表")
|
raise ValueError("配置必须包含话题列表")
|
||||||
|
|
||||||
parameters = config.get('parameters', {})
|
parameters = config.get('parameters', {})
|
||||||
if not all(key in parameters for key in ['crawlDepth', 'interval', 'maxRetries', 'timeout']):
|
if not all(key in parameters for key in ['crawlDepth', 'interval', 'maxRetries', 'timeout']):
|
||||||
raise ValueError("配置缺少必要的参数")
|
raise ValueError("配置缺少必要的参数")
|
||||||
|
|
||||||
# 提取建议文本(JSON之前的部分)
|
# 提取建议文本(JSON之前的部分)
|
||||||
suggestion = response_text[:start].strip()
|
suggestion = response_text[:start].strip()
|
||||||
|
|
||||||
return config, suggestion
|
return config, suggestion
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"解析AI响应失败: {str(e)}")
|
raise ValueError(f"解析AI响应失败: {str(e)}")
|
||||||
|
|
||||||
@spider_bp.route('/api/spider/ai-config', methods=['POST'])
|
@spider_bp.route('/api/spider/ai-config', methods=['POST'])
|
||||||
def generate_ai_config():
|
def generate_ai_config():
|
||||||
"""使用AI生成爬虫配置"""
|
"""使用AI生成爬虫配置"""
|
||||||
try:
|
try:
|
||||||
prompt = request.json.get('prompt', '')
|
prompt = request.json.get('prompt', '')
|
||||||
if not prompt:
|
if not prompt:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': '请提供爬虫需求描述'
|
'message': '请提供爬虫需求描述'
|
||||||
})
|
})
|
||||||
|
|
||||||
# 构建AI提示
|
# 构建AI提示
|
||||||
system_prompt = """你是一个专业的爬虫配置助手。请根据用户的自然语言描述,生成合适的微博爬虫配置。
|
system_prompt = """你是一个专业的爬虫配置助手。请根据用户的自然语言描述,生成合适的微博爬虫配置。
|
||||||
配置应包含以下内容:
|
配置应包含以下内容:
|
||||||
1. 要爬取的话题列表
|
1. 要爬取的话题列表
|
||||||
2. 爬虫参数(爬取深度、间隔时间、重试次数、超时时间)
|
2. 爬虫参数(爬取深度、间隔时间、重试次数、超时时间)
|
||||||
|
|
||||||
请先用通俗易懂的语言解释你的配置建议,然后在最后提供一个JSON格式的具体配置。
|
请先用通俗易懂的语言解释你的配置建议,然后在最后提供一个JSON格式的具体配置。
|
||||||
注意:
|
注意:
|
||||||
- 爬取深度(crawlDepth)范围:1-10页
|
- 爬取深度(crawlDepth)范围:1-10页
|
||||||
- 间隔时间(interval)范围:3-30秒
|
- 间隔时间(interval)范围:3-30秒
|
||||||
- 重试次数(maxRetries)范围:1-5次
|
- 重试次数(maxRetries)范围:1-5次
|
||||||
- 超时时间(timeout)范围:10-60秒
|
- 超时时间(timeout)范围:10-60秒
|
||||||
- 所有参数都必须是整数
|
- 所有参数都必须是整数
|
||||||
|
|
||||||
示例输出格式:
|
示例输出格式:
|
||||||
根据您的需求,我建议...
|
根据您的需求,我建议...
|
||||||
|
|
||||||
{
|
{
|
||||||
"topics": ["话题1", "话题2"],
|
"topics": ["话题1", "话题2"],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"crawlDepth": 5,
|
"crawlDepth": 5,
|
||||||
"interval": 5,
|
"interval": 5,
|
||||||
"maxRetries": 3,
|
"maxRetries": 3,
|
||||||
"timeout": 30
|
"timeout": 30
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
# 获取AI客户端
|
# 获取AI客户端
|
||||||
ai = get_ai_client()
|
ai = get_ai_client()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if ai['type'] == 'anthropic':
|
if ai['type'] == 'anthropic':
|
||||||
response = ai['client'].messages.create(
|
response = ai['client'].messages.create(
|
||||||
model="claude-3-sonnet-20240229",
|
model="claude-3-sonnet-20240229",
|
||||||
max_tokens=1000,
|
max_tokens=1000,
|
||||||
messages=[
|
messages=[
|
||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
{"role": "user", "content": prompt}
|
{"role": "user", "content": prompt}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
response_text = response.content[0].text
|
response_text = response.content[0].text
|
||||||
else: # OpenAI
|
else: # OpenAI
|
||||||
response = ai['client'].chat.completions.create(
|
response = ai['client'].chat.completions.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[
|
messages=[
|
||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
{"role": "user", "content": prompt}
|
{"role": "user", "content": prompt}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
response_text = response.choices[0].message.content
|
response_text = response.choices[0].message.content
|
||||||
|
|
||||||
# 解析AI响应
|
# 解析AI响应
|
||||||
config, suggestion = parse_ai_response(response_text)
|
config, suggestion = parse_ai_response(response_text)
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'config': config,
|
'config': config,
|
||||||
'suggestion': suggestion
|
'suggestion': suggestion
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"AI服务调用失败: {e}")
|
logger.error(f"AI服务调用失败: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': f"AI配置生成失败: {str(e)}"
|
'message': f"AI配置生成失败: {str(e)}"
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"生成配置失败: {e}")
|
logger.error(f"生成配置失败: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': str(e)
|
'message': str(e)
|
||||||
})
|
})
|
||||||
Binary file not shown.
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>微博舆情分析系统 | 登录</title>
|
<title>微博舆情分析系统 | 登录</title>
|
||||||
<link rel="icon" href="../../../static/原神启动/favicon.ico" />
|
<link rel="icon" href="../../../static/原神启动/favicon.ico" />
|
||||||
|
<script src="/static/js/i18n.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -287,9 +288,31 @@
|
|||||||
.yanzhengma:hover {
|
.yanzhengma:hover {
|
||||||
color: #aa863e;
|
color: #aa863e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 添加语言切换按钮样式 */
|
||||||
|
.language-switcher {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
padding: 8px 15px;
|
||||||
|
border-radius: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1000;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body onclick="playAudio()">
|
<body onclick="playAudio()">
|
||||||
|
<div class="language-switcher" id="language-switcher">
|
||||||
|
<span id="language-text">切换语言</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<audio id="audio-player" autoplay class="hide-player">
|
<audio id="audio-player" autoplay class="hide-player">
|
||||||
<source
|
<source
|
||||||
src="https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/%E5%8E%9F%E7%A5%9E%E9%A6%96%E9%A1%B5%E8%83%8C%E6%99%AF%E9%9F%B3.mp4"
|
src="https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/%E5%8E%9F%E7%A5%9E%E9%A6%96%E9%A1%B5%E8%83%8C%E6%99%AF%E9%9F%B3.mp4"
|
||||||
|
|||||||
+372
-372
@@ -1,372 +1,372 @@
|
|||||||
import time
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
from flask import Blueprint, redirect, render_template, request, Flask, session, current_app, make_response
|
from flask import Blueprint, redirect, render_template, request, Flask, session, current_app, make_response
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import re
|
import re
|
||||||
from utils.query import query
|
from utils.query import query
|
||||||
from utils.errorResponse import errorResponse
|
from utils.errorResponse import errorResponse
|
||||||
from utils.logger import app_logger as logging
|
from utils.logger import app_logger as logging
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import secrets
|
import secrets
|
||||||
from flask_limiter import Limiter
|
from flask_limiter import Limiter
|
||||||
from flask_limiter.util import get_remote_address
|
from flask_limiter.util import get_remote_address
|
||||||
import redis
|
import redis
|
||||||
import json
|
import json
|
||||||
import bleach
|
import bleach
|
||||||
from argon2 import PasswordHasher
|
from argon2 import PasswordHasher
|
||||||
from argon2.exceptions import VerifyMismatchError
|
from argon2.exceptions import VerifyMismatchError
|
||||||
import html
|
import html
|
||||||
|
|
||||||
# 创建Argon2密码哈希器
|
# 创建Argon2密码哈希器
|
||||||
ph = PasswordHasher()
|
ph = PasswordHasher()
|
||||||
|
|
||||||
# Redis连接
|
# Redis连接
|
||||||
redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
|
redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
|
||||||
|
|
||||||
# 创建限流器
|
# 创建限流器
|
||||||
limiter = Limiter(
|
limiter = Limiter(
|
||||||
key_func=get_remote_address,
|
key_func=get_remote_address,
|
||||||
default_limits=["200 per day", "50 per hour"]
|
default_limits=["200 per day", "50 per hour"]
|
||||||
)
|
)
|
||||||
|
|
||||||
ub = Blueprint('user',
|
ub = Blueprint('user',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='/user',
|
url_prefix='/user',
|
||||||
template_folder='templates')
|
template_folder='templates')
|
||||||
|
|
||||||
def sanitize_input(text):
|
def sanitize_input(text):
|
||||||
"""清理用户输入,防止XSS攻击"""
|
"""清理用户输入,防止XSS攻击"""
|
||||||
if text is None:
|
if text is None:
|
||||||
return None
|
return None
|
||||||
return bleach.clean(str(text), strip=True)
|
return bleach.clean(str(text), strip=True)
|
||||||
|
|
||||||
def validate_csrf_token():
|
def validate_csrf_token():
|
||||||
"""验证CSRF令牌"""
|
"""验证CSRF令牌"""
|
||||||
token = request.form.get('csrf_token')
|
token = request.form.get('csrf_token')
|
||||||
stored_token = session.get('csrf_token')
|
stored_token = session.get('csrf_token')
|
||||||
if not token or not stored_token or token != stored_token:
|
if not token or not stored_token or token != stored_token:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_client_info():
|
def get_client_info():
|
||||||
"""获取客户端信息"""
|
"""获取客户端信息"""
|
||||||
return {
|
return {
|
||||||
'ip': request.remote_addr,
|
'ip': request.remote_addr,
|
||||||
'user_agent': str(request.user_agent.string),
|
'user_agent': str(request.user_agent.string),
|
||||||
'platform': str(request.user_agent.platform),
|
'platform': str(request.user_agent.platform),
|
||||||
'browser': str(request.user_agent.browser),
|
'browser': str(request.user_agent.browser),
|
||||||
}
|
}
|
||||||
|
|
||||||
def is_suspicious_ip(ip):
|
def is_suspicious_ip(ip):
|
||||||
"""检查IP是否可疑"""
|
"""检查IP是否可疑"""
|
||||||
key = f"login_attempts:{ip}"
|
key = f"login_attempts:{ip}"
|
||||||
attempts = redis_client.get(key)
|
attempts = redis_client.get(key)
|
||||||
if attempts and int(attempts) >= 5: # 5次失败尝试
|
if attempts and int(attempts) >= 5: # 5次失败尝试
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def record_failed_attempt(ip):
|
def record_failed_attempt(ip):
|
||||||
"""记录失败的登录尝试"""
|
"""记录失败的登录尝试"""
|
||||||
key = f"login_attempts:{ip}"
|
key = f"login_attempts:{ip}"
|
||||||
pipe = redis_client.pipeline()
|
pipe = redis_client.pipeline()
|
||||||
pipe.incr(key)
|
pipe.incr(key)
|
||||||
pipe.expire(key, 1800) # 30分钟后重置
|
pipe.expire(key, 1800) # 30分钟后重置
|
||||||
pipe.execute()
|
pipe.execute()
|
||||||
|
|
||||||
def clear_login_attempts(ip):
|
def clear_login_attempts(ip):
|
||||||
"""清除登录尝试记录"""
|
"""清除登录尝试记录"""
|
||||||
redis_client.delete(f"login_attempts:{ip}")
|
redis_client.delete(f"login_attempts:{ip}")
|
||||||
|
|
||||||
def set_secure_headers(response):
|
def set_secure_headers(response):
|
||||||
"""设置安全响应头"""
|
"""设置安全响应头"""
|
||||||
response.headers['X-Content-Type-Options'] = 'nosniff'
|
response.headers['X-Content-Type-Options'] = 'nosniff'
|
||||||
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
||||||
response.headers['X-XSS-Protection'] = '1; mode=block'
|
response.headers['X-XSS-Protection'] = '1; mode=block'
|
||||||
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
|
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
|
||||||
response.headers['Content-Security-Policy'] = "default-src 'self'"
|
response.headers['Content-Security-Policy'] = "default-src 'self'"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def login_required(f):
|
def login_required(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if 'username' not in session:
|
if 'username' not in session:
|
||||||
return redirect('/user/login')
|
return redirect('/user/login')
|
||||||
|
|
||||||
# 验证会话完整性
|
# 验证会话完整性
|
||||||
if 'client_info' not in session or 'session_id' not in session:
|
if 'client_info' not in session or 'session_id' not in session:
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect('/user/login')
|
return redirect('/user/login')
|
||||||
|
|
||||||
# 验证客户端信息
|
# 验证客户端信息
|
||||||
current_client = get_client_info()
|
current_client = get_client_info()
|
||||||
stored_client = session['client_info']
|
stored_client = session['client_info']
|
||||||
|
|
||||||
if (current_client['ip'] != stored_client['ip'] or
|
if (current_client['ip'] != stored_client['ip'] or
|
||||||
current_client['user_agent'] != stored_client['user_agent']):
|
current_client['user_agent'] != stored_client['user_agent']):
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect('/user/login')
|
return redirect('/user/login')
|
||||||
|
|
||||||
# 验证会话ID
|
# 验证会话ID
|
||||||
stored_session_id = redis_client.get(f"session:{session['username']}")
|
stored_session_id = redis_client.get(f"session:{session['username']}")
|
||||||
if not stored_session_id or stored_session_id != session['session_id']:
|
if not stored_session_id or stored_session_id != session['session_id']:
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect('/user/login')
|
return redirect('/user/login')
|
||||||
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
def hash_password(password: str) -> str:
|
def hash_password(password: str) -> str:
|
||||||
"""
|
"""
|
||||||
使用Argon2id算法哈希密码
|
使用Argon2id算法哈希密码
|
||||||
:param password: 用户输入的密码
|
:param password: 用户输入的密码
|
||||||
:return: 哈希后的密码
|
:return: 哈希后的密码
|
||||||
"""
|
"""
|
||||||
return ph.hash(password)
|
return ph.hash(password)
|
||||||
|
|
||||||
def verify_password(stored_hash: str, password: str) -> bool:
|
def verify_password(stored_hash: str, password: str) -> bool:
|
||||||
"""
|
"""
|
||||||
验证密码
|
验证密码
|
||||||
:param stored_hash: 存储的密码哈希
|
:param stored_hash: 存储的密码哈希
|
||||||
:param password: 用户输入的密码
|
:param password: 用户输入的密码
|
||||||
:return: 是否匹配
|
:return: 是否匹配
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return ph.verify(stored_hash, password)
|
return ph.verify(stored_hash, password)
|
||||||
except VerifyMismatchError:
|
except VerifyMismatchError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def validate_password(password: str) -> bool:
|
def validate_password(password: str) -> bool:
|
||||||
"""
|
"""
|
||||||
验证密码强度
|
验证密码强度
|
||||||
"""
|
"""
|
||||||
if len(password) < 12: # 增加最小长度要求
|
if len(password) < 12: # 增加最小长度要求
|
||||||
return False
|
return False
|
||||||
if not re.search(r"[A-Z]", password):
|
if not re.search(r"[A-Z]", password):
|
||||||
return False
|
return False
|
||||||
if not re.search(r"[a-z]", password):
|
if not re.search(r"[a-z]", password):
|
||||||
return False
|
return False
|
||||||
if not re.search(r"\d", password):
|
if not re.search(r"\d", password):
|
||||||
return False
|
return False
|
||||||
if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
|
if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
|
||||||
return False
|
return False
|
||||||
# 检查常见密码模式
|
# 检查常见密码模式
|
||||||
common_patterns = ['password', '123456', 'qwerty']
|
common_patterns = ['password', '123456', 'qwerty']
|
||||||
if any(pattern in password.lower() for pattern in common_patterns):
|
if any(pattern in password.lower() for pattern in common_patterns):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ub.route('/login', methods=['GET', 'POST'])
|
@ub.route('/login', methods=['GET', 'POST'])
|
||||||
@limiter.limit("5 per minute")
|
@limiter.limit("5 per minute")
|
||||||
def login():
|
def login():
|
||||||
"""处理用户登录请求"""
|
"""处理用户登录请求"""
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
response = make_response(render_template('login_and_register.html'))
|
response = make_response(render_template('login_and_register.html'))
|
||||||
return set_secure_headers(response)
|
return set_secure_headers(response)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if request.method == 'POST' and not validate_csrf_token():
|
if request.method == 'POST' and not validate_csrf_token():
|
||||||
logging.warning("CSRF验证失败")
|
logging.warning("CSRF验证失败")
|
||||||
return errorResponse('无效的请求')
|
return errorResponse('无效的请求')
|
||||||
|
|
||||||
client_ip = request.remote_addr
|
client_ip = request.remote_addr
|
||||||
|
|
||||||
if is_suspicious_ip(client_ip):
|
if is_suspicious_ip(client_ip):
|
||||||
logging.warning(f"可疑IP尝试登录: {client_ip}")
|
logging.warning(f"可疑IP尝试登录: {client_ip}")
|
||||||
return errorResponse('由于多次失败尝试,请30分钟后再试')
|
return errorResponse('由于多次失败尝试,请30分钟后再试')
|
||||||
|
|
||||||
username = sanitize_input(request.form.get('username'))
|
username = sanitize_input(request.form.get('username'))
|
||||||
password = request.form.get('password') # 密码不需要sanitize
|
password = request.form.get('password') # 密码不需要sanitize
|
||||||
|
|
||||||
if not username or not password:
|
if not username or not password:
|
||||||
logging.warning("登录失败:用户名或密码为空")
|
logging.warning("登录失败:用户名或密码为空")
|
||||||
return errorResponse('用户名和密码不能为空')
|
return errorResponse('用户名和密码不能为空')
|
||||||
|
|
||||||
# 查询用户信息
|
# 查询用户信息
|
||||||
sql = "SELECT password, status FROM user WHERE username = %s"
|
sql = "SELECT password, status FROM user WHERE username = %s"
|
||||||
result = query(sql, [username], "select")
|
result = query(sql, [username], "select")
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
stored_password = result[0]['password']
|
stored_password = result[0]['password']
|
||||||
status = result[0]['status']
|
status = result[0]['status']
|
||||||
|
|
||||||
if status != 'active':
|
if status != 'active':
|
||||||
logging.warning(f"已禁用的账户尝试登录: {username}")
|
logging.warning(f"已禁用的账户尝试登录: {username}")
|
||||||
return errorResponse('账户已被禁用')
|
return errorResponse('账户已被禁用')
|
||||||
|
|
||||||
if verify_password(stored_password, password):
|
if verify_password(stored_password, password):
|
||||||
session.clear()
|
session.clear()
|
||||||
session.regenerate()
|
session.regenerate()
|
||||||
|
|
||||||
# 生成唯一会话ID
|
# 生成唯一会话ID
|
||||||
session_id = secrets.token_hex(32)
|
session_id = secrets.token_hex(32)
|
||||||
client_info = get_client_info()
|
client_info = get_client_info()
|
||||||
|
|
||||||
# 存储会话信息
|
# 存储会话信息
|
||||||
session['username'] = username
|
session['username'] = username
|
||||||
session['login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
session['login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
session['csrf_token'] = secrets.token_hex(32)
|
session['csrf_token'] = secrets.token_hex(32)
|
||||||
session['client_info'] = client_info
|
session['client_info'] = client_info
|
||||||
session['session_id'] = session_id
|
session['session_id'] = session_id
|
||||||
session.permanent = True
|
session.permanent = True
|
||||||
current_app.permanent_session_lifetime = timedelta(hours=2)
|
current_app.permanent_session_lifetime = timedelta(hours=2)
|
||||||
|
|
||||||
# 在Redis中存储会话ID
|
# 在Redis中存储会话ID
|
||||||
redis_client.setex(
|
redis_client.setex(
|
||||||
f"session:{username}",
|
f"session:{username}",
|
||||||
int(current_app.permanent_session_lifetime.total_seconds()),
|
int(current_app.permanent_session_lifetime.total_seconds()),
|
||||||
session_id
|
session_id
|
||||||
)
|
)
|
||||||
|
|
||||||
clear_login_attempts(client_ip)
|
clear_login_attempts(client_ip)
|
||||||
|
|
||||||
# 记录登录历史
|
# 记录登录历史
|
||||||
login_history_sql = '''
|
login_history_sql = '''
|
||||||
INSERT INTO login_history
|
INSERT INTO login_history
|
||||||
(username, login_time, ip_address, user_agent, success, attempt_count)
|
(username, login_time, ip_address, user_agent, success, attempt_count)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s)
|
VALUES (%s, %s, %s, %s, %s, %s)
|
||||||
'''
|
'''
|
||||||
query(login_history_sql, [
|
query(login_history_sql, [
|
||||||
username,
|
username,
|
||||||
datetime.now(),
|
datetime.now(),
|
||||||
client_info['ip'],
|
client_info['ip'],
|
||||||
client_info['user_agent'],
|
client_info['user_agent'],
|
||||||
True,
|
True,
|
||||||
redis_client.get(f"login_attempts:{client_ip}") or 0
|
redis_client.get(f"login_attempts:{client_ip}") or 0
|
||||||
])
|
])
|
||||||
|
|
||||||
logging.info(f"用户 {username} 登录成功")
|
logging.info(f"用户 {username} 登录成功")
|
||||||
response = make_response(redirect('/page/home'))
|
response = make_response(redirect('/page/home'))
|
||||||
return set_secure_headers(response)
|
return set_secure_headers(response)
|
||||||
|
|
||||||
record_failed_attempt(client_ip)
|
record_failed_attempt(client_ip)
|
||||||
logging.warning(f"登录失败:用户名或密码错误")
|
logging.warning(f"登录失败:用户名或密码错误")
|
||||||
return errorResponse('用户名或密码错误')
|
return errorResponse('用户名或密码错误')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"登录过程发生错误: {e}")
|
logging.error(f"登录过程发生错误: {e}")
|
||||||
return errorResponse('登录失败,请稍后重试')
|
return errorResponse('登录失败,请稍后重试')
|
||||||
|
|
||||||
@ub.route('/register', methods=['GET', 'POST'])
|
@ub.route('/register', methods=['GET', 'POST'])
|
||||||
@limiter.limit("3 per hour")
|
@limiter.limit("3 per hour")
|
||||||
def register():
|
def register():
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
response = make_response(render_template('login_and_register.html'))
|
response = make_response(render_template('login_and_register.html'))
|
||||||
return set_secure_headers(response)
|
return set_secure_headers(response)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if request.method == 'POST' and not validate_csrf_token():
|
if request.method == 'POST' and not validate_csrf_token():
|
||||||
logging.warning("CSRF验证失败")
|
logging.warning("CSRF验证失败")
|
||||||
return errorResponse('无效的请求')
|
return errorResponse('无效的请求')
|
||||||
|
|
||||||
username = sanitize_input(request.form.get('username'))
|
username = sanitize_input(request.form.get('username'))
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
email = sanitize_input(request.form.get('email'))
|
email = sanitize_input(request.form.get('email'))
|
||||||
|
|
||||||
if not username or not password or not email:
|
if not username or not password or not email:
|
||||||
return errorResponse('用户名、密码和邮箱不能为空')
|
return errorResponse('用户名、密码和邮箱不能为空')
|
||||||
|
|
||||||
# 验证用户名格式
|
# 验证用户名格式
|
||||||
if not re.match(r'^[a-zA-Z0-9_]{4,20}$', username):
|
if not re.match(r'^[a-zA-Z0-9_]{4,20}$', username):
|
||||||
return errorResponse('用户名只能包含字母、数字和下划线,长度4-20位')
|
return errorResponse('用户名只能包含字母、数字和下划线,长度4-20位')
|
||||||
|
|
||||||
# 验证邮箱格式
|
# 验证邮箱格式
|
||||||
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
|
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
|
||||||
return errorResponse('邮箱格式不正确')
|
return errorResponse('邮箱格式不正确')
|
||||||
|
|
||||||
# 验证密码强度
|
# 验证密码强度
|
||||||
if not validate_password(password):
|
if not validate_password(password):
|
||||||
return errorResponse('密码必须包含大小写字母、数字和特殊字符,且长度至少12位')
|
return errorResponse('密码必须包含大小写字母、数字和特殊字符,且长度至少12位')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 检查用户名和邮箱是否存在
|
# 检查用户名和邮箱是否存在
|
||||||
check_sql = """
|
check_sql = """
|
||||||
SELECT
|
SELECT
|
||||||
(SELECT COUNT(*) FROM user WHERE LOWER(username) = LOWER(%s)) as username_count,
|
(SELECT COUNT(*) FROM user WHERE LOWER(username) = LOWER(%s)) as username_count,
|
||||||
(SELECT COUNT(*) FROM user WHERE LOWER(email) = LOWER(%s)) as email_count
|
(SELECT COUNT(*) FROM user WHERE LOWER(email) = LOWER(%s)) as email_count
|
||||||
"""
|
"""
|
||||||
result = query(check_sql, [username.lower(), email.lower()], "select")
|
result = query(check_sql, [username.lower(), email.lower()], "select")
|
||||||
|
|
||||||
if result[0]['username_count'] > 0:
|
if result[0]['username_count'] > 0:
|
||||||
return errorResponse('该用户名已被注册')
|
return errorResponse('该用户名已被注册')
|
||||||
|
|
||||||
if result[0]['email_count'] > 0:
|
if result[0]['email_count'] > 0:
|
||||||
return errorResponse('该邮箱已被注册')
|
return errorResponse('该邮箱已被注册')
|
||||||
|
|
||||||
# 哈希密码
|
# 哈希密码
|
||||||
hashed_password = hash_password(password)
|
hashed_password = hash_password(password)
|
||||||
|
|
||||||
# 插入新用户
|
# 插入新用户
|
||||||
insert_sql = '''
|
insert_sql = '''
|
||||||
INSERT INTO user(username, password, email, status, createTime, last_password_change)
|
INSERT INTO user(username, password, email, status, createTime, last_password_change)
|
||||||
VALUES(%s, %s, %s, %s, %s, %s)
|
VALUES(%s, %s, %s, %s, %s, %s)
|
||||||
'''
|
'''
|
||||||
current_time = datetime.now()
|
current_time = datetime.now()
|
||||||
query(insert_sql, [
|
query(insert_sql, [
|
||||||
username,
|
username,
|
||||||
hashed_password,
|
hashed_password,
|
||||||
email,
|
email,
|
||||||
'active',
|
'active',
|
||||||
current_time,
|
current_time,
|
||||||
current_time
|
current_time
|
||||||
])
|
])
|
||||||
|
|
||||||
# 记录注册信息
|
# 记录注册信息
|
||||||
client_info = get_client_info()
|
client_info = get_client_info()
|
||||||
register_history_sql = '''
|
register_history_sql = '''
|
||||||
INSERT INTO register_history
|
INSERT INTO register_history
|
||||||
(username, register_time, ip_address, user_agent, email)
|
(username, register_time, ip_address, user_agent, email)
|
||||||
VALUES (%s, %s, %s, %s, %s)
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
'''
|
'''
|
||||||
query(register_history_sql, [
|
query(register_history_sql, [
|
||||||
username,
|
username,
|
||||||
current_time,
|
current_time,
|
||||||
client_info['ip'],
|
client_info['ip'],
|
||||||
client_info['user_agent'],
|
client_info['user_agent'],
|
||||||
email
|
email
|
||||||
])
|
])
|
||||||
|
|
||||||
logging.info(f"新用户注册成功: {username}")
|
logging.info(f"新用户注册成功: {username}")
|
||||||
response = make_response(redirect('/user/login'))
|
response = make_response(redirect('/user/login'))
|
||||||
return set_secure_headers(response)
|
return set_secure_headers(response)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"注册过程发生错误: {e}")
|
logging.error(f"注册过程发生错误: {e}")
|
||||||
return errorResponse('注册失败,请稍后重试')
|
return errorResponse('注册失败,请稍后重试')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"注册过程发生错误: {e}")
|
logging.error(f"注册过程发生错误: {e}")
|
||||||
return errorResponse('注册失败,请稍后重试')
|
return errorResponse('注册失败,请稍后重试')
|
||||||
|
|
||||||
@ub.route('/logout')
|
@ub.route('/logout')
|
||||||
@login_required
|
@login_required
|
||||||
def logout():
|
def logout():
|
||||||
"""用户登出"""
|
"""用户登出"""
|
||||||
try:
|
try:
|
||||||
username = session.get('username')
|
username = session.get('username')
|
||||||
client_info = session.get('client_info', {})
|
client_info = session.get('client_info', {})
|
||||||
|
|
||||||
# 记录登出历史
|
# 记录登出历史
|
||||||
logout_history_sql = '''
|
logout_history_sql = '''
|
||||||
INSERT INTO logout_history
|
INSERT INTO logout_history
|
||||||
(username, logout_time, ip_address, user_agent, session_id)
|
(username, logout_time, ip_address, user_agent, session_id)
|
||||||
VALUES (%s, %s, %s, %s, %s)
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
'''
|
'''
|
||||||
query(logout_history_sql, [
|
query(logout_history_sql, [
|
||||||
username,
|
username,
|
||||||
datetime.now(),
|
datetime.now(),
|
||||||
client_info.get('ip'),
|
client_info.get('ip'),
|
||||||
client_info.get('user_agent'),
|
client_info.get('user_agent'),
|
||||||
session.get('session_id')
|
session.get('session_id')
|
||||||
])
|
])
|
||||||
|
|
||||||
# 删除Redis中的会话
|
# 删除Redis中的会话
|
||||||
redis_client.delete(f"session:{username}")
|
redis_client.delete(f"session:{username}")
|
||||||
|
|
||||||
session.clear()
|
session.clear()
|
||||||
logging.info(f"用户 {username} 成功登出")
|
logging.info(f"用户 {username} 成功登出")
|
||||||
response = make_response(redirect('/user/login'))
|
response = make_response(redirect('/user/login'))
|
||||||
return set_secure_headers(response)
|
return set_secure_headers(response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"登出过程发生错误: {e}")
|
logging.error(f"登出过程发生错误: {e}")
|
||||||
response = make_response(redirect('/user/login'))
|
response = make_response(redirect('/user/login'))
|
||||||
return set_secure_headers(response)
|
return set_secure_headers(response)
|
||||||
|
|||||||
Reference in New Issue
Block a user