i18nized private console index

This commit is contained in:
strawmanbobi
2020-06-06 21:51:29 +08:00
parent ddad2de39e
commit 59153238f2
11 changed files with 188 additions and 375 deletions

View File

@@ -15,7 +15,7 @@ exports.setupEnvironment = function () {
MYSQL_DB_SERVER_ADDRESS = "localhost";
MYSQL_DB_NAME = "irext";
MYSQL_DB_USER = "root";
MYSQL_DB_PASSWORD = "root";
MYSQL_DB_PASSWORD = "421aWill.";
REDIS_HOST = "localhost";
REDIS_PORT = "6379";
REDIS_PASSWORD = null;

View File

@@ -335,7 +335,7 @@
<iframe name="post_iframe" id="post_iframe" style="border: none; width: 100%; height: 50px;">
<html>
<body>
<div style="width: 100%; text-align: center; font-family: '微软雅黑'">正在提交</div>
<div style="width: 100%; text-align: center; font-family: serif,serif">正在提交</div>
</body>
</html>
</iframe>
@@ -491,6 +491,7 @@
<script src="../public_js/utils/view_utils.js"></script>
<script src="../public_js/chinese/await.js"></script>
<script src="../public_js/chinese/chinese.js"></script>
<script src="../public_js/i18n/i18next-1.11.2.min.js"></script>
<script type="text/javascript" src="js/constants.js"></script>
<script type="text/javascript" src="js/manage.js"></script>
<script type="text/javascript" src="js/operate.js"></script>

View File

@@ -6,29 +6,34 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
<meta name="description" content="index page of IRext">
<meta name="author" content="strawmanbobi">
<title>IRext</title>
<title data-i18n="page_index.title">IRext</title>
<link rel="stylesheet" href="./public_js/bower_components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="./public_js/bower_components/toastr/toastr.min.css">
<link rel="stylesheet" href="css/cover.css">
</head>
<body>
<body class="page_index" style="background: #3F3F3F; color: #FFFFFF;">
<div class="navbar container">
<div class="header clearfix">
<h3>IRext 本地控制台 - 0.2.5</h3>
<h3 data-i18n="page_index.title">IRext 本地控制台 - 0.2.5</h3>
</div>
</div>
<div class="container">
<div class="container">
<div class="form-signin">
<h4 class="text-muted">登入</h4>
<label for="user_name" class="sr-only">邮件地址</label>
<input type="email" id="user_name" class="form-control" placeholder="邮件地址" required autofocus>
<label for="password" class="sr-only">密码</label>
<input type="password" id="password" class="form-control" placeholder="密码" required>
<button class="btn btn-lg btn-primary btn-block" onclick="signIn();">确定</button>
<br>
<div style="font-size: 12px; color: #AAA;"><a href="#" onclick="onChangePassword();">更改密码</a></div>
<div class="form-signin" style="margin-top: 100px;">
<h4 class="text-muted" data-i18n="page_index.d_signin_title" style="color: #FFFFFF;">登入</h4>
<label for="user_name" class="sr-only" data-i18n="page_index.d_signin_email">邮件地址</label>
<input type="email" id="user_name" class="form-control" placeholder="Email" required autofocus>
<label for="password" class="sr-only" data-i18n="page_index.d_signin_password">密码</label>
<input type="password" id="password" class="form-control" placeholder="Password" required>
<div class="row">
<div class="col-lg-6">
<button class="btn btn-lg btn-info" style="width: 100%;" data-i18n="page_index.d_signin_signin" onclick="signIn();">登入</button>
</div>
<div class="col-lg-6">
<button class="btn btn-lg btn-warning" style="width: 100%;" data-i18n="page_index.d_changepw_title" onclick="onChangePassword();">重置密码</button>
</div>
</div>
</div>
</div>
</div>
@@ -39,65 +44,14 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" style="color: #000;">更改密码</h4>
<h4 class="modal-title" style=" color: #000;" data-i18n="page_index.d_changepw_title">重置密码</h4>
</div>
<div class="modal-body">
<div id="changepw_hint" style="color: #000;">系统会自动为你挑选一个6位随机数的新密码并发送到你的邮件确认更改密码</div>
<div id="changepw_hint" style="color: #000;" data-i18n="page_index.d_changepw_hint">系统会自动为你挑选一个6位随机数的新密码并发送到你的邮件确认更改密码</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" onclick="changePassword();">确定</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">取消</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="stat_dialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" style="color: #000;">统计数据</h4>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="main_tab">
<li class="active" style="cursor: pointer;"><a data-target="#activation_stat" data-toggle="tab">概要信息</a></li>
<li style="cursor: pointer;"><a data-target="#remote_stat" data-toggle="tab" onclick="statCategories();">详细信息</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="activation_stat">
<div id="panel_active_stat">
<div class="row marketing">
<div class="row">
<div class="col-md-4" style="text-align: center;">
<h4>支持电器种类</h4>
<h1 style="color: #3FAF7F" id="categories_count"></h1>
</div>
<div class="col-md-4" style="text-align: center;">
<h4>支持品牌数量</h4>
<h1 style="color: #3FAF7F" id="brands_count"></h1>
</div>
<div class="col-md-4" style="text-align: center;">
<h4>支持索引数量</h4>
<h1 style="color: #3FAF7F" id="remote_indexes_count"></h1>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="remote_stat">
<br>
<div id="panel_remote_index_stat">
<div class="panel-group" role="tablist" aria-multiselectable="true" id="categories_panel">
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">已了解</button>
<button type="button" class="btn btn-danger" onclick="changePassword();" data-i18n="page_index.d_changepw_confirm">确定</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" data-i18n="page_index.d_changepw_cancel">取消</button>
</div>
</div>
</div>
@@ -107,6 +61,8 @@
<script src="./public_js/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="./public_js/bower_components/toastr/toastr.min.js"></script>
<script src="./public_js/bower_components/highcharts/highcharts.js"></script>
<script src="./public_js/i18n/i18next-1.11.2.min.js"></script>
<script src="./public_js/utils/url_parser.js"></script>
<script src="./public_js/utils/ciphering.js"></script>
<script src="./js/index.js"></script>
</html>

View File

@@ -5,28 +5,33 @@
var LS_KEY_ID = "user_name";
var LS_KEY_TOKEN = "token";
var LS_KEY_LANG = "lang";
var categoriesStated = false;
var userLang = "en-US";
var paramLang = getParameter('lang');
var colorClass = ["default", "primary", "success", "warning", "danger", "info"];
if (paramLang) {
localStorage.setItem(LS_KEY_LANG, paramLang);
} else {
// set LANG default to simplified Chinese
localStorage.setItem(LS_KEY_LANG, "zh-CN");
}
// global container var
var gCategories = [];
// 2-dimensions brand array
var gBrands = [];
// 1-dimension city array
var gCities = [];
userLang = navigator.language || paramLang;
i18n.init(function(err, t) {
$(".page_index").i18n({ lng: userLang });
});
$("#document").ready(function() {
});
// sign in
function signIn() {
var userName = $("#user_name").val();
var password = $("#password").val();
if (null == userName || "" == userName || null == password || "" == password) {
toastr.error("请填写用户账户和密码");
if (null == userName || "" === userName || null == password || "" === password) {
toastr.error(i18n.t('page_index.d_signin_fill_email_pw', { lng: userLang }));
return;
}
var pwHash = MD5(password);
@@ -35,16 +40,17 @@ function signIn() {
function onChangePassword() {
var userName = $("#user_name").val();
if (null == userName || "" == userName) {
toastr.error("请填写用户账户");
if (null == userName || "" === userName) {
toastr.error(i18n.t('page_index.d_signin_fill_email', { lng: userLang }));
return;
}
$("#changepw_confirm_dialog").modal();
}
function popUpHintDialog(hint) {
$("#text_hint").empty();
$("#text_hint").append(hint);
var textHint = $("#text_hint");
textHint.empty();
textHint.append(hint);
$("#hint_dialog").modal();
}
@@ -73,8 +79,8 @@ function navigateToPage(page, id, token) {
function changePassword() {
var userName = $("#user_name").val();
if (null == userName || "" == userName) {
popUpHintDialog("请填写用户账户");
if (null == userName || "" === userName) {
popUpHintDialog(i18n.t('page_index.d_signin_fill_email', { lng: userLang }));
return;
}
$.ajax({
@@ -86,25 +92,21 @@ function changePassword() {
},
timeout: 20000,
success: function (response) {
if(response.status.code == 0) {
if(response.status.code === 0) {
$("#changepw_confirm_dialog").modal('hide');
popUpHintDialog("新密码已经成功发送到您的邮箱,请查看并确认");
popUpHintDialog(i18n.t('page_index.d_signup_email_sent', { lng: userLang }));
} else {
$("#changepw_confirm_dialog").modal('hide');
popUpHintDialog("申请密码修改失败,请确认您是合法人员,且邮箱地址无误");
popUpHintDialog(i18n.t('page_index.d_signup_email_failed', { lng: userLang }));
}
},
error: function () {
$("#changepw_confirm_dialog").modal('hide');
popUpHintDialog("申请密码修改失败,请确认您是合法人员,且邮箱地址无误");
popUpHintDialog(i18n.t('page_index.d_signup_email_failed', { lng: userLang }));
}
});
}
function onSignIn() {
$("#signin_dialog").modal();
}
function doSignIn(userName, password) {
var token = "";
var adminID = "";
@@ -115,14 +117,14 @@ function doSignIn(userName, password) {
contentType: "application/json; charset=utf-8",
timeout: 20000,
success: function(response) {
if(response.status.code == 0) {
if(response.status.code === 0) {
token = response.entity.token;
adminID = response.entity.id;
toastr.success("登入成功3秒后自动进入控制台");
toastr.success(i18n.t('page_index.d_signin_success', { lng: userLang }));
var permission = token.substring(token.indexOf(",") + 1);
var index = null;
var page = "";
if (null != permission && permission != "") {
if (null != permission && permission !== "") {
index = permission.substring(0, 1);
}
if (null == index) {
@@ -131,290 +133,16 @@ function doSignIn(userName, password) {
page = "code";
}
setTimeout(function() {
// navigateToPage(page, adminID, token);
window.location = "./" + page + "/index.html";
}, 3000);
localStorage.setItem(LS_KEY_ID, adminID);
localStorage.setItem(LS_KEY_TOKEN, token);
} else {
toastr.error("登入失败,请确认密码是否正确");
toastr.error(i18n.t('page_index.d_signin_failed', { lng: userLang }));
}
},
error: function() {
toastr.error("登入失败,请确认密码是否正确");
toastr.error(i18n.t('page_index.d_signin_failed', { lng: userLang }));
}
});
}
// stat
function onStatCategories() {
if (true == categoriesStated) {
return;
}
console.debug("stat categories");
statCategories();
}
function onStatBrands(categoryIndex) {
var categoryID = 0;
if (true == gBrands[categoryIndex].brandStated) {
return;
}
categoryID = gCategories[categoryIndex].id;
if (3 != categoryID) {
statBrands(gCategories[categoryIndex].id, categoryIndex);
} else {
statCities(gCategories[categoryIndex].id, categoryIndex);
}
}
function getStatInfo() {
$.ajax({
url: "/irext/stat/generic_count",
type: "POST",
dataType: 'json',
data: {
admin_id : id,
token : token
},
timeout: 20000,
success: function (response) {
if(response.status.code == 0) {
refreshStatInfo(response.entity);
} else {
console.log("get remote instance count failed");
}
},
error: function () {
console.log("get remote instance count failed");
}
});
}
function statCategories() {
$.ajax({
url: "/irext/stat/stat_categories",
type: "POST",
timeout: 20000,
success: function (response) {
if(response.status.code == 0) {
gCategories = response.entity;
refreshCategoryList();
} else {
console.log("stat categories failed");
}
},
error: function () {
console.log("stat categories failed");
}
});
}
function statBrands(categoryID, categoryIndex) {
$.ajax({
url: "/irext/stat/stat_brands",
type: "POST",
dataTpe: "JSON",
data: {
category_id: categoryID
},
timeout: 20000,
success: function (response) {
if(response.status.code == 0) {
gBrands[categoryIndex].brands = response.entity;
// console.log("brands stat result = " + JSON.stringify(gBrands[categoryIndex].brands));
refreshBrandList(categoryID, categoryIndex);
} else {
console.log("stat brands failed");
}
},
error: function () {
console.log("stat brands failed");
}
});
}
function statCities(categoryID, categoryIndex) {
$.ajax({
url: "/irext/stat/stat_cities",
type: "POST",
timeout: 20000,
success: function (response) {
if(response.status.code == 0) {
gCities.cities = response.entity;
console.log("cities stat result = " + JSON.stringify(gCities.cities));
refreshCityList(categoryID, categoryIndex);
} else {
console.log("stat cities failed");
}
},
error: function () {
console.log("stat cities failed");
}
});
}
function showStat() {
$("#stat_dialog").modal();
}
function refreshStatInfo(statInfo) {
var categoriesCountPanel = $("#categories_count");
var brandsCountPanel = $("#brands_count");
var remoteIndexesCountPanel = $("#remote_indexes_count");
categoriesCountPanel.empty();
categoriesCountPanel.append(statInfo.categories_count);
brandsCountPanel.empty();
brandsCountPanel.append(statInfo.brands_count);
remoteIndexesCountPanel.empty();
remoteIndexesCountPanel.append(statInfo.remote_indexes_count);
}
function refreshCategoryList() {
var categoryContent = "";
gBrands = new Array();
for (var i = 0; i < gCategories.length; i++) {
var category = gCategories[i];
if (category.id == 11) {
category.name = "機上盒";
}
var panelID = "category_" + category.id;
var collapseID = "collapse" + category.id;
var colorSpace = i % 6;
var includingText = "";
if (3 != category.id) {
includingText = "个品牌";
} else {
includingText = "个省份";
}
// console.log(colorClass[colorSpace]);
categoryContent +=
"<div class='panel panel-default'>" +
"<div class='panel-heading' role='tab' id='" + panelID + "'>" +
"<h4 class='panel-title' style='text-align:left;'>" +
"<a style='display: block; width: 100%; text-decoration: none;'" +
"role='button' data-toggle='collapse' data-parent='#categories_panel'" +
"href='#" + collapseID +"' onclick='onStatBrands(" + i + ")' " +
"aria-expanded='true' aria-controls='" + collapseID + "'>" +
category.name + " (" + category.brands_count + " " + includingText + ")" +
"</a>" +
"</h4>" +
"</div>" +
"<div id='" + collapseID + "' class='panel-collapse collapse' role='tabpanel' aria-labelledby='headingOne'>" +
"<div class='panel-body' style='text-align:left;' id='brand_charts_" + category.id + "'>" +
"正在加载..." +
"</div>" +
"</div>" +
"</div>";
gBrands[i] = new Array();
gBrands[i].brandStated = false;
}
$("#categories_panel").html(categoryContent);
categoriesStated = true;
}
function refreshBrandList(categoryID, categoryIndex) {
// draw charts with highcharts
// adjust the container of charts according to the number of brands in this category
var containerHeight = gBrands[categoryIndex].brands.length * 30 + 200;
console.log("container height = " + containerHeight);
$("#brand_charts_" + categoryID).css("width", "100%");
$("#brand_charts_" + categoryID).css("height", containerHeight + "px");
$("#brand_charts_" + categoryID).css("padding", "0px");
// generate brand names and supported remote index counts
var brandNames = [];
var remoteIndexCounts = [];
for (var i = 0; i < gBrands[categoryIndex].brands.length; i++) {
brandNames[i] = gBrands[categoryIndex].brands[i].name;
remoteIndexCounts[i] = gBrands[categoryIndex].brands[i].remote_indexes_count;
}
$("#brand_charts_" + categoryID).highcharts({
chart: {
type: "bar",
style: {
fontFamily: '微软雅黑'
},
events: {
load: function(event) {
// nothing to do
}
}
},
title: {
text: gCategories[categoryIndex].name + "品牌分布"
},
xAxis: {
categories: brandNames
},
yAxis: {
title: {
text: '支持型号数'
}
},
series: [{
name: '型号数',
data: remoteIndexCounts,
dataLabels: {
enabled: true
}
}]
});
gBrands[categoryIndex].brandStated = true;
}
function refreshCityList(categoryID, categoryIndex) {
// draw charts with highcharts
// adjust the container of charts according to the number of brands in this category
var containerHeight = gCities.cities.length * 30 + 200;
console.log("container height = " + containerHeight);
$("#brand_charts_" + categoryID).css("width", "100%");
$("#brand_charts_" + categoryID).css("height", containerHeight + "px");
$("#brand_charts_" + categoryID).css("padding", "0px");
// generate brand names and supported remote index counts
var provinceNames = [];
var cityCounts = [];
for (var i = 0; i < gCities.cities.length; i++) {
provinceNames[i] = gCities.cities[i].name;
cityCounts[i] = gCities.cities[i].city_count;
}
$("#brand_charts_" + categoryID).highcharts({
chart: {
type: "bar",
style: {
fontFamily: '微软雅黑'
},
events: {
load: function(event) {
// nothing to do
}
}
},
title: {
text: gCategories[categoryIndex].name + "地区分布"
},
xAxis: {
categories: provinceNames
},
yAxis: {
title: {
text: '支持城市数'
}
},
series: [{
name: '城市数',
data: cityCounts,
dataLabels: {
enabled: true
}
}]
});
gBrands[categoryIndex].brandStated = true;
}

View File

@@ -0,0 +1,24 @@
{
"app": {
"name": "IRext"
},
"page_index": {
"title": "IRext 本地控制台 - 0.2.5",
"d_signin_title": "登入",
"d_signin_email": "邮箱地址",
"d_signin_password": "密码",
"d_signin_signin": "登入",
"d_changepw_title": "重置密码",
"d_changepw_hint": "系统会自动为你挑选一个6位随机数的新密码并发送到你的邮件确认更改密码",
"d_changepw_confirm": "确定",
"d_changepw_cancel": "取消",
"common_signout": "注销",
"d_signin_success": "登入成功3秒后自动进入控制台",
"d_signin_failed": "登入失败,请确认邮件地址和密码正确",
"d_signup_email_sent": "带有新密码的邮件已经发送到您的邮箱,请注意查收",
"d_signup_email_occupied": "这个邮件地址已经被使用",
"d_signin_fill_email_pw": "请填写邮件地址和密码",
"d_signin_fill_email": "请填写邮件地址",
"d_signup_email_failed": "申请密码修改失败,请确认您是合法人员,且邮箱地址无误"
}
}

View File

@@ -0,0 +1,24 @@
{
"app": {
"name": "IRext"
},
"page_index": {
"title": "IRext Local Console - 0.2.5",
"d_signin_title": "Sign in",
"d_signin_email": "Email",
"d_signin_password": "Password",
"d_signin_signin": "Sign in",
"d_changepw_title": "Reset PW",
"d_changepw_hint": "A new password will be generated and an email will be sent to your mail box, confirm it ?",
"d_changepw_confirm": "OK",
"d_changepw_cancel": "Cancel",
"common_signout": "Sign out",
"d_signin_success": "Sign in successfully, redirecting to console in 3 seconds",
"d_signin_failed": "Failed to sign in, please check your email and password",
"d_signup_email_sent": "New password is sent to your email, please check",
"d_signup_email_occupied": "This email address is already in use",
"d_signin_fill_email_pw": "Please fill email and password",
"d_signin_fill_email": "Please fill email",
"d_signup_email_failed": "Failed to reset password, please check your email address"
}
}

View File

@@ -0,0 +1,24 @@
{
"app": {
"name": "IRext"
},
"page_index": {
"title": "IRext Local Console - 0.2.5",
"d_signin_title": "Sign in",
"d_signin_email": "Email",
"d_signin_password": "Password",
"d_signin_signin": "Sign in",
"d_changepw_title": "Reset PW",
"d_changepw_hint": "A new password will be generated and an email will be sent to your mail box, confirm it ?",
"d_changepw_confirm": "OK",
"d_changepw_cancel": "Cancel",
"common_signout": "Sign out",
"d_signin_success": "Sign in successfully, redirecting to console in 3 seconds",
"d_signin_failed": "Failed to sign in, please check your email and password",
"d_signup_email_sent": "New password is sent to your email, please check",
"d_signup_email_occupied": "This email address is already in use",
"d_signin_fill_email_pw": "Please fill email and password",
"d_signin_fill_email": "Please fill email",
"d_signup_email_failed": "Failed to reset password, please check your email address"
}
}

View File

@@ -0,0 +1,24 @@
{
"app": {
"name": "IRext"
},
"page_index": {
"title": "IRext 本地控制台 - 0.2.5",
"d_signin_title": "登入",
"d_signin_email": "邮箱地址",
"d_signin_password": "密码",
"d_signin_signin": "登入",
"d_changepw_title": "重置密码",
"d_changepw_hint": "系统会自动为你挑选一个6位随机数的新密码并发送到你的邮件确认更改密码",
"d_changepw_confirm": "确定",
"d_changepw_cancel": "取消",
"common_signout": "注销",
"d_signin_success": "登入成功3秒后自动进入控制台",
"d_signin_failed": "登入失败,请确认邮件地址和密码正确",
"d_signup_email_sent": "带有新密码的邮件已经发送到您的邮箱,请注意查收",
"d_signup_email_occupied": "这个邮件地址已经被使用",
"d_signin_fill_email_pw": "请填写邮件地址和密码",
"d_signin_fill_email": "请填写邮件地址",
"d_signup_email_failed": "申请密码修改失败,请确认您是合法人员,且邮箱地址无误"
}
}

View File

@@ -0,0 +1,24 @@
{
"app": {
"name": "IRext"
},
"page_index": {
"title": "IRext 本地控制台 - 0.2.5",
"d_signin_title": "登入",
"d_signin_email": "邮箱地址",
"d_signin_password": "密码",
"d_signin_signin": "登入",
"d_changepw_title": "重置密码",
"d_changepw_hint": "系统会自动为你挑选一个6位随机数的新密码并发送到你的邮件确认更改密码",
"d_changepw_confirm": "确定",
"d_changepw_cancel": "取消",
"common_signout": "注销",
"d_signin_success": "登入成功3秒后自动进入控制台",
"d_signin_failed": "登入失败,请确认邮件地址和密码正确",
"d_signup_email_sent": "带有新密码的邮件已经发送到您的邮箱,请注意查收",
"d_signup_email_occupied": "这个邮件地址已经被使用",
"d_signin_fill_email_pw": "请填写邮件地址和密码",
"d_signin_fill_email": "请填写邮件地址",
"d_signup_email_failed": "申请密码修改失败,请确认您是合法人员,且邮箱地址无误"
}
}

View File

@@ -20,6 +20,7 @@
"highcharts": "^5.0.6",
"jquery": "^2.2.4",
"select2": "^4.0.3",
"toastr": "^2.1.3"
"toastr": "^2.1.3",
}
}

File diff suppressed because one or more lines are too long