mirror of
https://github.com/getrebuild/rebuild.git
synced 2025-09-26 16:45:52 +08:00
parent
6032b9e2e5
commit
80253cc974
9 changed files with 297 additions and 27 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -45,5 +45,4 @@ test.*
|
|||
|
||||
# Build
|
||||
|
||||
**/public/h5app/
|
||||
/.deploy/build/
|
||||
|
|
|
@ -7,10 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.web.user.signup;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.commons.CodecUtils;
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.commons.RegexUtils;
|
||||
import cn.devezhao.commons.*;
|
||||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import cn.devezhao.commons.web.WebUtils;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
|
@ -46,10 +43,7 @@ import org.springframework.web.servlet.ModelAndView;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author zhaofang123@gmail.com
|
||||
|
@ -63,6 +57,7 @@ public class LoginController extends BaseController {
|
|||
public static final String CK_AUTOLOGIN = "rb.alt";
|
||||
public static final String SK_USER_THEME = "currentUseTheme";
|
||||
private static final String SK_NEED_VCODE = "needLoginVCode";
|
||||
private static final String SK_START_TOUR = "needStartTour";
|
||||
|
||||
@GetMapping("login")
|
||||
public ModelAndView checkLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
@ -221,12 +216,22 @@ public class LoginController extends BaseController {
|
|||
ServletUtils.removeCookie(request, response, CK_AUTOLOGIN);
|
||||
}
|
||||
|
||||
createLoginLog(request, user);
|
||||
ThreadPool.exec(() -> createLoginLog(request, user));
|
||||
|
||||
ServletUtils.setSessionAttribute(request, WebUtils.CURRENT_USER, user);
|
||||
ServletUtils.setSessionAttribute(request, SK_USER_THEME,
|
||||
KVStorage.getCustomValue("THEME." + user));
|
||||
ServletUtils.setSessionAttribute(request, SK_USER_THEME, KVStorage.getCustomValue("THEME." + user));
|
||||
Application.getSessionStore().storeLoginSuccessed(request);
|
||||
|
||||
// TODO Tour 显示规则
|
||||
Object[] initLoginTime = Application.createQueryNoFilter(
|
||||
"select min(loginTime) from LoginLog where user = ? and loginTime > '2021-12-31'")
|
||||
.setParameter(1, user)
|
||||
.unique();
|
||||
int dayLeft = initLoginTime == null || initLoginTime[0] == null ? 0
|
||||
: CalendarUtils.getDayLeft((Date) initLoginTime[0]);
|
||||
if (dayLeft >= -30 || "true".equals(System.getProperty("ForceTour"))) { // 30d
|
||||
ServletUtils.setSessionAttribute(request, SK_START_TOUR, "yes");
|
||||
}
|
||||
}
|
||||
|
||||
private void createLoginLog(HttpServletRequest request, ID user) {
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<script th:src="@{/commons/frontjs/use-frontjs}" type="text/babel"></script>
|
||||
</th:block>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script th:if="${env == 'prodution'}" async src="https://www.googletagmanager.com/gtag/js?id=UA-70670864-2"></script>
|
||||
<script th:if="${env == 'prodution'}" src="https://www.googletagmanager.com/gtag/js?id=UA-70670864-2" async></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || []
|
||||
function gtag() {
|
||||
|
@ -34,4 +34,10 @@
|
|||
gtag('js', new Date())
|
||||
gtag('config', 'UA-70670864-2')
|
||||
</script>
|
||||
<th:block th:if="${user != null && session.needStartTour != null}">
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/assets/lib/introjs.min.css}" />
|
||||
<script th:src="@{/assets/lib/intro.min.js}"></script>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/rebuild-tour.css}" />
|
||||
<script th:src="@{/assets/js/rebuild-tour.js}"></script>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
|
84
src/main/resources/web/assets/css/rebuild-tour.css
Normal file
84
src/main/resources/web/assets/css/rebuild-tour.css
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
body.rebuild-tour-body {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip {
|
||||
background-color: #4285f4;
|
||||
color: #fff;
|
||||
border-radius: 0;
|
||||
border: 0 none;
|
||||
width: 280px;
|
||||
min-width: 280px;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-tooltip-header .introjs-tooltip-title {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-tooltip-header .introjs-skipbutton {
|
||||
color: #fff;
|
||||
opacity: 0.6;
|
||||
padding: 5px 0;
|
||||
font-weight: normal;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-tooltip-header .introjs-skipbutton:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-tooltiptext {
|
||||
padding-bottom: 10px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-arrow.left {
|
||||
border-right-color: #4285f4;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-arrow.bottom {
|
||||
border-top-color: #4285f4;
|
||||
}
|
||||
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-arrow.top-middle,
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-arrow.top-right,
|
||||
.introjs-tooltip.rebuild-tour-tooltip .introjs-arrow.top {
|
||||
border-bottom-color: #4285f4;
|
||||
}
|
||||
|
||||
.introjs-helperLayer.rebuild-tour-highlight {
|
||||
border-radius: unset !important;
|
||||
box-shadow: unset !important;
|
||||
}
|
||||
|
||||
.introjs-button {
|
||||
border: 0 none !important;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.introjs-bullets ul li a {
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.introjs-bullets ul li a.active,
|
||||
.introjs-bullets ul li a:focus,
|
||||
.introjs-bullets ul li a:hover {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.introjs-tooltipbuttons {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.introjs-tooltipbuttons a > i {
|
||||
font: normal normal normal 17px/1 'Material-Design-Iconic-Font';
|
||||
}
|
|
@ -5,7 +5,7 @@ rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
|||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
/* Page of editor */
|
||||
/* Editor */
|
||||
|
||||
.timeline.spare {
|
||||
max-width: 2000px;
|
||||
|
@ -141,7 +141,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* formula */
|
||||
/* Formula */
|
||||
|
||||
.formula-calc {
|
||||
position: relative;
|
||||
|
|
|
@ -20,6 +20,8 @@ $(document).ready(function () {
|
|||
|
||||
let dash_list = null
|
||||
$.get('/dashboard/dash-gets', (res) => {
|
||||
typeof window.startTour === 'function' && window.startTour(1000)
|
||||
|
||||
dash_list = res.data
|
||||
if (!dash_list || dash_list.length === 0) {
|
||||
$('.chart-grid').removeClass('invisible')
|
||||
|
@ -67,12 +69,12 @@ $(document).ready(function () {
|
|||
$('.chart-grid').addClass('uneditable')
|
||||
}
|
||||
|
||||
$('.J_dash-new').click(() => dlgShow('DlgDashAdd'))
|
||||
$('.J_dash-edit').click(() => dlgShow('DlgDashSettings', { title: d[4], shareTo: d[1] }))
|
||||
$('.J_chart-new').click(() => dlgShow('DlgAddChart'))
|
||||
$('.J_dash-select').click(() => dlgShow('DashSelect', { dashList: dash_list }))
|
||||
$('.J_dash-new').on('click', () => dlgShow('DlgDashAdd'))
|
||||
$('.J_dash-edit').on('click', () => dlgShow('DlgDashSettings', { title: d[4], shareTo: d[1] }))
|
||||
$('.J_chart-new').on('click', () => dlgShow('DlgAddChart'))
|
||||
$('.J_dash-select').on('click', () => dlgShow('DashSelect', { dashList: dash_list }))
|
||||
|
||||
$('.J_dash-refresh .dropdown-item').click(function () {
|
||||
$('.J_dash-refresh .dropdown-item').on('click', function () {
|
||||
const $this = $(this)
|
||||
$('.J_dash-refresh .btn span').text($this.text())
|
||||
refresh_timeout = ~~$this.data('time')
|
||||
|
@ -89,7 +91,7 @@ $(document).ready(function () {
|
|||
}
|
||||
})
|
||||
|
||||
$('.J_dash-fullscreen').click(() => {
|
||||
$('.J_dash-fullscreen').on('click', () => {
|
||||
const $body = $(document.body)
|
||||
if ($body.hasClass('fullscreen')) exitFullscreen()
|
||||
else fullScreen()
|
||||
|
@ -98,7 +100,7 @@ $(document).ready(function () {
|
|||
})
|
||||
|
||||
let dlgChartSelect
|
||||
$('.J_chart-select').click(() => {
|
||||
$('.J_chart-select').on('click', () => {
|
||||
const appended = []
|
||||
$('.grid-stack-item-content').each(function () {
|
||||
appended.push($(this).attr('id').substr(6))
|
||||
|
@ -202,7 +204,7 @@ const render_dashboard = function (init) {
|
|||
if (rendered_charts.length === 0) {
|
||||
const gsi = `<div class="grid-stack-item"><div id="chart-add" class="grid-stack-item-content"><a class="chart-add"><i class="zmdi zmdi-plus"></i><p>${$L('添加图表')}</p></a></div></div>`
|
||||
const $gsi = gridstack.addWidget(gsi, 0, 0, 2, 2)
|
||||
$gsi.find('a').click(() => {
|
||||
$gsi.find('a').on('click', () => {
|
||||
if ($('.J_chart-new').length === 0) $('.J_chart-select').trigger('click')
|
||||
else dlgShow('DlgAddChart')
|
||||
})
|
||||
|
@ -233,7 +235,7 @@ const add_widget = function (item) {
|
|||
const chart_add = $('#chart-add')
|
||||
if (chart_add.length > 0) gridstack.removeWidget(chart_add.parent())
|
||||
|
||||
const gsi = '<div class="grid-stack-item"><div id="' + chid + '" class="grid-stack-item-content"></div></div>'
|
||||
const gsi = `<div class="grid-stack-item"><div id="${chid}" class="grid-stack-item-content"></div></div>`
|
||||
// Use gridstar
|
||||
if (item.size_x || item.size_y) {
|
||||
gridstack.addWidget(gsi, (item.col || 1) - 1, (item.row || 1) - 1, item.size_x || 2, item.size_y || 2, 2, 12, 2, 12)
|
||||
|
@ -267,7 +269,7 @@ const save_dashboard = function () {
|
|||
gridstack_serialize = s
|
||||
$setTimeout(
|
||||
() => {
|
||||
$.post('/dashboard/dash-config?id=' + dashid, JSON.stringify(gridstack_serialize), () => {
|
||||
$.post(`/dashboard/dash-config?id=${dashid}`, JSON.stringify(gridstack_serialize), () => {
|
||||
if (rb.env === 'dev') console.log('Saved dashboard : ' + JSON.stringify(gridstack_serialize))
|
||||
})
|
||||
},
|
||||
|
@ -320,7 +322,7 @@ class DlgAddChart extends RbFormHandler {
|
|||
next() {
|
||||
const e = this.__select2.val()
|
||||
if (!e) return
|
||||
location.href = rb.baseUrl + '/dashboard/chart-design?source=' + e + '&dashid=' + this.props.dashid
|
||||
location.href = `${rb.baseUrl}/dashboard/chart-design?source=${e}&dashid=${this.props.dashid}`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,7 +396,7 @@ class DlgDashSettings extends RbFormHandler {
|
|||
confirmText: $L('删除'),
|
||||
confirm: function () {
|
||||
this.disabled(true)
|
||||
$.post('/app/entity/common-delete?id=' + dashid, function (res) {
|
||||
$.post(`/app/entity/common-delete?id=${dashid}`, function (res) {
|
||||
// if (res.error_code === 0) location.replace('home#del=' + dashid) // Chrome no refresh?
|
||||
if (res.error_code === 0) location.reload()
|
||||
else RbHighbar.error(res.error_msg)
|
||||
|
|
162
src/main/resources/web/assets/js/rebuild-tour.js
Normal file
162
src/main/resources/web/assets/js/rebuild-tour.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
/* global introJs */
|
||||
|
||||
window.startTour = function (delay) {
|
||||
if ($(window).width() < 1000) return
|
||||
setTimeout(startTour123, delay || 100)
|
||||
}
|
||||
|
||||
const startTour123 = function () {
|
||||
let stepName
|
||||
let steps
|
||||
if (location.href.includes('/dashboard/home')) {
|
||||
stepName = 'TourEnd-Dashboard'
|
||||
steps = StepRebuild()
|
||||
StepDashboard().forEach((item) => steps.push(item))
|
||||
}
|
||||
if (!steps) return
|
||||
|
||||
const isEnd = $storage.get(stepName)
|
||||
if (isEnd) return // 已经展示完
|
||||
|
||||
const stepsObj = []
|
||||
steps.forEach((item) => {
|
||||
const $el = item.element ? $(item.element) : [null]
|
||||
if ($el.length > 0) stepsObj.push({ ...item, element: $el[0] })
|
||||
})
|
||||
if (stepsObj.length === 0) return
|
||||
|
||||
// 隐藏滚动条
|
||||
$(document.body).addClass('rebuild-tour-body')
|
||||
|
||||
const $introJs = introJs()
|
||||
.setOptions({
|
||||
steps: stepsObj,
|
||||
overlayOpacity: 0,
|
||||
disableInteraction: true,
|
||||
exitOnOverlayClick: false,
|
||||
exitOnEsc: false,
|
||||
scrollToElement: false,
|
||||
tooltipClass: 'rebuild-tour-tooltip',
|
||||
highlightClass: 'rebuild-tour-highlight',
|
||||
prevLabel: '<i class="zmdi zmdi-arrow-left"></i>',
|
||||
nextLabel: '<i class="zmdi zmdi-arrow-right"></i>',
|
||||
doneLabel: $L('完成'),
|
||||
})
|
||||
.onchange((target) => {
|
||||
const $target = $(target)
|
||||
let stepIndex = -1
|
||||
for (let i = 1; i < steps.length; i++) {
|
||||
if ($target.hasClass(steps[i].element.substr(1))) {
|
||||
stepIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (stepIndex < 0) return
|
||||
|
||||
// hack: 位置更新
|
||||
$('.rebuild-tour-highlight').css('box-shadow', 'none')
|
||||
const pos = { margin: 0 }
|
||||
const s = steps[stepIndex]
|
||||
if (s && s.rbLeft) pos.marginLeft = s.rbLeft
|
||||
else if (s && s.rbRight) pos.marginRight = s.rbRight
|
||||
if (s && s.rbTop) pos.marginTop = s.rbTop
|
||||
else if (s && s.rbBottom) pos.marginBottom = s.rbBottom
|
||||
setTimeout(() => $('.rebuild-tour-tooltip').css(pos), 360)
|
||||
|
||||
// $introJs.refresh()
|
||||
})
|
||||
.oncomplete(() => {
|
||||
$storage.set(stepName, 'yes')
|
||||
})
|
||||
.onexit(() => {
|
||||
$(document.body).removeClass('rebuild-tour-body')
|
||||
})
|
||||
|
||||
// $introJs.goToStep(1)
|
||||
$introJs.start()
|
||||
}
|
||||
|
||||
// ~~ 向导步骤
|
||||
|
||||
const StepRebuild = () => {
|
||||
return [
|
||||
{
|
||||
title: $L('欢迎使用'),
|
||||
intro: $L('本向导将带你了解系统的基本功能使用,让我们开始吧!'),
|
||||
},
|
||||
{
|
||||
element: '.rb-left-sidebar',
|
||||
title: $L('导航菜单'),
|
||||
intro: $L('使用导航菜单可以在各个功能模块之间切换'),
|
||||
rbLeft: -10,
|
||||
rbTop: 16,
|
||||
},
|
||||
{
|
||||
element: '.nav-settings',
|
||||
title: $L('导航菜单设置'),
|
||||
intro: $L('点击此处进行个性化导航菜单设置'),
|
||||
rbLeft: -10,
|
||||
rbBottom: -10,
|
||||
},
|
||||
{
|
||||
element: '.global-search',
|
||||
title: $L('全局搜索'),
|
||||
intro: $L('全局搜索可以帮助你快速查询需要的数据'),
|
||||
rbLeft: 5,
|
||||
},
|
||||
{
|
||||
element: '.global-create',
|
||||
title: $L('快速新建'),
|
||||
intro: $L('点击此处快速新建业务记录'),
|
||||
rbLeft: 8,
|
||||
},
|
||||
{
|
||||
element: '.admin-settings',
|
||||
title: $L('管理中心'),
|
||||
intro: $L('REBUILD 拥有强大的配置管理中心,你可以根据需求自由搭建系统'),
|
||||
rbLeft: 5,
|
||||
},
|
||||
{
|
||||
element: '.page-help',
|
||||
title: $L('帮助中心'),
|
||||
intro: $L('使用遇到问题可以查阅帮助文档,你也可以通过阅读文档 GET 更多技能'),
|
||||
rbLeft: 5,
|
||||
},
|
||||
{
|
||||
element: '.J_top-notifications',
|
||||
title: $L('通知'),
|
||||
intro: $L('与你相关的通知消息都在这里'),
|
||||
rbRight: 9,
|
||||
},
|
||||
{
|
||||
element: '.J_top-user',
|
||||
title: $L('个人设置'),
|
||||
intro: $L('点击此处设置你的个人信息,或选择界面主题等'),
|
||||
rbRight: 11,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const StepDashboard = () => {
|
||||
return [
|
||||
{
|
||||
element: '.dash-head',
|
||||
title: $L('切换仪表盘'),
|
||||
intro: $L('点击此处切换仪表盘显示,或进行设置/新增仪表盘'),
|
||||
rbTop: -6,
|
||||
},
|
||||
{
|
||||
element: '.J_chart-adds',
|
||||
title: $L('添加图表'),
|
||||
intro: $L('点击此处为当前仪表盘添加图表'),
|
||||
rbTop: -1,
|
||||
rbRight: 21,
|
||||
},
|
||||
]
|
||||
}
|
10
src/main/resources/web/assets/lib/intro.min.js
vendored
Normal file
10
src/main/resources/web/assets/lib/intro.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
src/main/resources/web/assets/lib/introjs.min.css
vendored
Normal file
2
src/main/resources/web/assets/lib/introjs.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Reference in a new issue