Commit 66112172 authored by 徐乐's avatar 徐乐

Merge branch 'dev' of https://g.civnet.cn:8443/ReactWeb5/CivWeb into dev

parents a0f1a1bf 54f8c4bc
......@@ -107,13 +107,13 @@
"@babel/polyfill": "7.4.3",
"@babel/runtime": "^7.10.5",
"@esri/calcite-colors": "6.0.3",
"@wisdom-cesium/cesium": "^1.1.33",
"@wisdom-cesium/krpano": "1.0.29-52",
"@wisdom-cesium/cesium": "^1.1.35",
"@wisdom-cesium/krpano": "1.0.29-60",
"@wisdom-components/basictable": "^1.5.16",
"@wisdom-components/empty": "^1.4.1",
"@wisdom-map/amap": "1.1.0-beta.56",
"@wisdom-map/arcgismap": "1.4.0-164",
"@wisdom-map/basemap": "1.1.0-24",
"@wisdom-map/arcgismap": "1.4.0-185",
"@wisdom-map/basemap": "1.1.0-29",
"@wisdom-map/util": "^1.0.28-0",
"@wisdom-utils/components": "0.1.319",
"@wisdom-utils/runtime": "0.0.46",
......@@ -156,6 +156,7 @@
"react-infinite-scroll-component": "^6.1.0",
"react-intl": "^3.12.1",
"react-redux": "7.0.2",
"react-reveal": "^1.2.2",
"react-router-config": "^5.1.1",
"react-router-dom": "6.3.0",
"react-use": "^15.3.4",
......@@ -166,7 +167,8 @@
"reselect": "4.0.0",
"sanitize.css": "8.0.0",
"sha1": "^1.1.1",
"styled-components": "4.2.0"
"styled-components": "4.2.0",
"swiper": "6.8.4"
},
"devDependencies": {
"@babel/cli": "7.4.3",
......
import { jsonp, request } from '@wisdom-utils/utils';
import * as constants from '../../constants';
/**
* 兼容老接口
*/
const transformGateWay = url => {
if (!/^(cityinterface|CityInterface|CityServer)/) {
return url.replace(/GeteWay/, '');
}
};
export const API = {
AUTHORIZATION_TOKEN: '/PandaCore/Identity/AuthorizationToken', // 授权验证,不需要走网关加前缀
......@@ -45,8 +37,11 @@ export const API = {
GET_InOnLine: '/PandaEnergy/GZshuiwuju/GuangZhou/InOnLine', // 广州登陆接口
GET_UserMobile: '/PandaEnergy/GZshuiwuju/SingleLogin/SingleLoginByAccess_token', // 获取登录手机号
GET_JIANGXI_MANAGE: '/PandaWorkFlow/WorkFlow/JiangXiManage/Login', // 江西登录接口
GET_INTEGRATION_CONFIG: '/PandaCore/GCK/IntegrationAuth/GetIntegrationConfig', // 获取集成站点数据
GET_YANSHI_AUTHORITY: '/PandaCore/GCK/Demonstrate/GetYanshiAuthority', // 获取当前用户开始演示模式的所有菜单ID
IS_OPEN_YANSHI: '/PandaCore/GCK/Demonstrate/IsOpenYanshi', // 获取是否开启演示模式服务
GET_WEB_CONFIG: '/PandaOMS/OMS/GetIntegratedLoginbyName',
GET_WEB_TICKET: '/PandaOMS/OMS/GetTicketByToken',
};
const services = {
......@@ -95,19 +90,16 @@ const services = {
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getWebSiteConfig: {
url: API.GET_WEB_SITE_CONFIG,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getUserInfo: {
url: API.GET_USER_INFO,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getOA: {
url: API.GET_OA,
method: constants.REQUEST_METHOD_GET,
......@@ -126,31 +118,26 @@ const services = {
method: constants.REQUEST_METHOD_POST,
type: constants.REQUEST_HTTP,
},
getCity: {
url: API.GET_CITY,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
generateTokenQuick: {
url: API.GENERATE_TOKEN_CHANGE,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getAllGroupsInfoForUser: {
url: API.GET_ALL_GROUPS_INFO_FORUSER,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getWeather: {
url: API.GET_WEATHER,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
sendMessVerificationCode: {
url: API.SEND_MESSAGE_CODE,
method: constants.REQUEST_METHOD_GET,
......@@ -212,6 +199,26 @@ const services = {
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getYanshiAuthority: {
url: API.GET_YANSHI_AUTHORITY,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
isOpenYanshi: {
url: API.IS_OPEN_YANSHI,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getIntegratedLoginbyName: {
url: API.GET_WEB_CONFIG,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
getTicketByToken: {
url: API.GET_WEB_TICKET,
method: constants.REQUEST_METHOD_GET,
type: constants.REQUEST_HTTP,
},
};
export const searchAutoCity = keywords => {
......
......@@ -4,900 +4,884 @@
* @author sirxemic / https://sirxemic.com/
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
typeof define === 'function' && define.amd ? define(['jquery'], factory) :
(factory(global.$));
}(this, (function ($) { 'use strict';
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
? factory(require('jquery'))
: typeof define === 'function' && define.amd
? define(['jquery'], factory)
: factory(global.$);
})(this, function($) {
$ = $ && 'default' in $ ? $.default : $;
let gl;
const $window = $(window); // There is only one window, so why not cache the jQuery-wrapped window?
function isPercentage(str) {
return str[str.length - 1] == '%';
}
/**
* Load a configuration of GL settings which the browser supports.
* For example:
* - not all browsers support WebGL
* - not all browsers support floating point textures
* - not all browsers support linear filtering for floating point textures
* - not all browsers support rendering to floating point textures
* - some browsers *do* support rendering to half-floating point textures instead.
*/
function loadConfig() {
const canvas = document.createElement('canvas');
gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
// Browser does not support WebGL.
return null;
}
// Load extensions
const extensions = {};
[
'OES_texture_float',
'OES_texture_half_float',
'OES_texture_float_linear',
'OES_texture_half_float_linear',
].forEach(function(name) {
const extension = gl.getExtension(name);
if (extension) {
extensions[name] = extension;
}
});
// If no floating point extensions are supported we can bail out early.
if (!extensions.OES_texture_float) {
return null;
}
const configs = [];
function createConfig(type, glType, arrayType) {
const name = `OES_texture_${type}`;
const nameLinear = `${name}_linear`;
const linearSupport = nameLinear in extensions;
const configExtensions = [name];
if (linearSupport) {
configExtensions.push(nameLinear);
}
return {
type: glType,
arrayType,
linearSupport,
extensions: configExtensions,
};
}
configs.push(createConfig('float', gl.FLOAT, Float32Array));
if (extensions.OES_texture_half_float) {
configs.push(
// Array type should be Uint16Array, but at least on iOS that breaks. In that case we
// just initialize the textures with data=null, instead of data=new Uint16Array(...).
// This makes initialization a tad slower, but it's still negligible.
createConfig('half_float', extensions.OES_texture_half_float.HALF_FLOAT_OES, null),
);
}
// Setup the texture and framebuffer
const texture = gl.createTexture();
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// Check for each supported texture type if rendering to it is supported
let config = null;
for (let i = 0; i < configs.length; i++) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 32, 0, gl.RGBA, configs[i].type, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
config = configs[i];
break;
}
}
return config;
}
function createImageData(width, height) {
try {
return new ImageData(width, height);
} catch (e) {
// Fallback for IE
const canvas = document.createElement('canvas');
return canvas.getContext('2d').createImageData(width, height);
}
}
function translateBackgroundPosition(value) {
const parts = value.split(' ');
if (parts.length === 1) {
switch (value) {
case 'center':
return ['50%', '50%'];
case 'top':
return ['50%', '0'];
case 'bottom':
return ['50%', '100%'];
case 'left':
return ['0', '50%'];
case 'right':
return ['100%', '50%'];
default:
return [value, '50%'];
}
} else {
return parts.map(function(part) {
switch (value) {
case 'center':
return '50%';
case 'top':
case 'left':
return '0';
case 'right':
case 'bottom':
return '100%';
default:
return part;
}
});
}
}
function createProgram(vertexSource, fragmentSource, uniformValues) {
function compileSource(type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(`compile error: ${gl.getShaderInfoLog(shader)}`);
}
return shader;
}
const program = {};
program.id = gl.createProgram();
gl.attachShader(program.id, compileSource(gl.VERTEX_SHADER, vertexSource));
gl.attachShader(program.id, compileSource(gl.FRAGMENT_SHADER, fragmentSource));
gl.linkProgram(program.id);
if (!gl.getProgramParameter(program.id, gl.LINK_STATUS)) {
throw new Error(`link error: ${gl.getProgramInfoLog(program.id)}`);
}
// Fetch the uniform and attribute locations
program.uniforms = {};
program.locations = {};
gl.useProgram(program.id);
gl.enableVertexAttribArray(0);
let match;
let name;
const regex = /uniform (\w+) (\w+)/g;
const shaderCode = vertexSource + fragmentSource;
while ((match = regex.exec(shaderCode)) != null) {
name = match[2];
program.locations[name] = gl.getUniformLocation(program.id, name);
}
return program;
}
function bindTexture(texture, unit) {
gl.activeTexture(gl.TEXTURE0 + (unit || 0));
gl.bindTexture(gl.TEXTURE_2D, texture);
}
function extractUrl(value) {
const urlMatch = /url\(["']?([^"']*)["']?\)/.exec(value);
if (urlMatch == null) {
return null;
}
return urlMatch[1];
}
function isDataUri(url) {
return url.match(/^data:/);
}
const config = loadConfig();
const transparentPixels = createImageData(32, 32);
// Extend the css
$('head').prepend('<style>.jquery-ripples { position: relative; z-index: 0; }</style>');
// RIPPLES CLASS DEFINITION
// =========================
const Ripples = function(el, options) {
const that = this;
this.$el = $(el);
// Init properties from options
this.interactive = options.interactive;
this.resolution = options.resolution;
this.textureDelta = new Float32Array([1 / this.resolution, 1 / this.resolution]);
this.perturbance = options.perturbance;
this.dropRadius = options.dropRadius;
this.crossOrigin = options.crossOrigin;
this.imageUrl = options.imageUrl;
// Init WebGL canvas
const canvas = document.createElement('canvas');
canvas.width = this.$el.innerWidth();
canvas.height = this.$el.innerHeight();
this.canvas = canvas;
this.$canvas = $(canvas);
this.$canvas.css({
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
zIndex: -1,
});
this.$el.addClass('jquery-ripples').append(canvas);
this.context = gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
// Load extensions
config.extensions.forEach(function(name) {
gl.getExtension(name);
});
// Auto-resize when window size changes.
this.updateSize = this.updateSize.bind(this);
$(window).on('resize', this.updateSize);
// Init rendertargets for ripple data.
this.textures = [];
this.framebuffers = [];
this.bufferWriteIndex = 0;
this.bufferReadIndex = 1;
const { arrayType } = config;
const textureData = arrayType ? new arrayType(this.resolution * this.resolution * 4) : null;
for (let i = 0; i < 2; i++) {
const texture = gl.createTexture();
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.resolution, this.resolution, 0, gl.RGBA, config.type, textureData);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
this.textures.push(texture);
this.framebuffers.push(framebuffer);
}
// Init GL stuff
this.quad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, +1, -1, +1, +1, -1, +1]), gl.STATIC_DRAW);
this.initShaders();
this.initTexture();
this.setTransparentTexture();
// Load the image either from the options or CSS rules
this.loadImage();
// Set correct clear color and blend mode (regular alpha blending)
gl.clearColor(0, 0, 0, 0);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// Plugin is successfully initialized!
this.visible = true;
this.running = true;
this.inited = true;
this.destroyed = false;
this.setupPointerEvents();
// Init animation
function step() {
if (!that.destroyed) {
that.step();
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
};
Ripples.DEFAULTS = {
imageUrl: null,
resolution: 256,
dropRadius: 20,
perturbance: 0.03,
interactive: true,
crossOrigin: '',
};
Ripples.prototype = {
// Set up pointer (mouse + touch) events
setupPointerEvents() {
const that = this;
function pointerEventsEnabled() {
return that.visible && that.running && that.interactive;
}
function dropAtPointer(pointer, big) {
if (pointerEventsEnabled()) {
that.dropAtPointer(pointer, that.dropRadius * (big ? 1.5 : 1), big ? 0.14 : 0.01);
}
}
// Start listening to pointer events
this.$el
// Create regular, small ripples for mouse move and touch events...
.on('mousemove.ripples', function(e) {
dropAtPointer(e);
})
.on('touchmove.ripples touchstart.ripples', function(e) {
const touches = e.originalEvent.changedTouches;
for (let i = 0; i < touches.length; i++) {
dropAtPointer(touches[i]);
}
})
$ = $ && 'default' in $ ? $['default'] : $;
// ...and only a big ripple on mouse down events.
.on('mousedown.ripples', function(e) {
dropAtPointer(e, true);
});
},
// Load the image either from the options or the element's CSS rules.
loadImage() {
const that = this;
gl = this.context;
const newImageSource =
this.imageUrl || extractUrl(this.originalCssBackgroundImage) || extractUrl(this.$el.css('backgroundImage'));
// If image source is unchanged, don't reload it.
if (newImageSource == this.imageSource) {
return;
}
var gl;
var $window = $(window); // There is only one window, so why not cache the jQuery-wrapped window?
this.imageSource = newImageSource;
function isPercentage(str) {
return str[str.length - 1] == '%';
}
// Falsy source means no background.
if (!this.imageSource) {
this.setTransparentTexture();
return;
}
/**
* Load a configuration of GL settings which the browser supports.
* For example:
* - not all browsers support WebGL
* - not all browsers support floating point textures
* - not all browsers support linear filtering for floating point textures
* - not all browsers support rendering to floating point textures
* - some browsers *do* support rendering to half-floating point textures instead.
*/
function loadConfig() {
var canvas = document.createElement('canvas');
gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
// Browser does not support WebGL.
return null;
}
// Load extensions
var extensions = {};
[
'OES_texture_float',
'OES_texture_half_float',
'OES_texture_float_linear',
'OES_texture_half_float_linear'
].forEach(function(name) {
var extension = gl.getExtension(name);
if (extension) {
extensions[name] = extension;
}
});
// If no floating point extensions are supported we can bail out early.
if (!extensions.OES_texture_float) {
return null;
}
var configs = [];
function createConfig(type, glType, arrayType) {
var name = 'OES_texture_' + type,
nameLinear = name + '_linear',
linearSupport = nameLinear in extensions,
configExtensions = [name];
if (linearSupport) {
configExtensions.push(nameLinear);
}
return {
type: glType,
arrayType: arrayType,
linearSupport: linearSupport,
extensions: configExtensions
};
}
configs.push(
createConfig('float', gl.FLOAT, Float32Array)
);
if (extensions.OES_texture_half_float) {
configs.push(
// Array type should be Uint16Array, but at least on iOS that breaks. In that case we
// just initialize the textures with data=null, instead of data=new Uint16Array(...).
// This makes initialization a tad slower, but it's still negligible.
createConfig('half_float', extensions.OES_texture_half_float.HALF_FLOAT_OES, null)
);
}
// Setup the texture and framebuffer
var texture = gl.createTexture();
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// Check for each supported texture type if rendering to it is supported
var config = null;
for (var i = 0; i < configs.length; i++) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 32, 0, gl.RGBA, configs[i].type, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
config = configs[i];
break;
}
}
return config;
}
function createImageData(width, height) {
try {
return new ImageData(width, height);
}
catch (e) {
// Fallback for IE
var canvas = document.createElement('canvas');
return canvas.getContext('2d').createImageData(width, height);
}
}
function translateBackgroundPosition(value) {
var parts = value.split(' ');
if (parts.length === 1) {
switch (value) {
case 'center':
return ['50%', '50%'];
case 'top':
return ['50%', '0'];
case 'bottom':
return ['50%', '100%'];
case 'left':
return ['0', '50%'];
case 'right':
return ['100%', '50%'];
default:
return [value, '50%'];
}
}
else {
return parts.map(function(part) {
switch (value) {
case 'center':
return '50%';
case 'top':
case 'left':
return '0';
case 'right':
case 'bottom':
return '100%';
default:
return part;
}
});
}
}
function createProgram(vertexSource, fragmentSource, uniformValues) {
function compileSource(type, source) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error('compile error: ' + gl.getShaderInfoLog(shader));
}
return shader;
}
var program = {};
program.id = gl.createProgram();
gl.attachShader(program.id, compileSource(gl.VERTEX_SHADER, vertexSource));
gl.attachShader(program.id, compileSource(gl.FRAGMENT_SHADER, fragmentSource));
gl.linkProgram(program.id);
if (!gl.getProgramParameter(program.id, gl.LINK_STATUS)) {
throw new Error('link error: ' + gl.getProgramInfoLog(program.id));
}
// Fetch the uniform and attribute locations
program.uniforms = {};
program.locations = {};
gl.useProgram(program.id);
gl.enableVertexAttribArray(0);
var match, name, regex = /uniform (\w+) (\w+)/g, shaderCode = vertexSource + fragmentSource;
while ((match = regex.exec(shaderCode)) != null) {
name = match[2];
program.locations[name] = gl.getUniformLocation(program.id, name);
}
return program;
}
function bindTexture(texture, unit) {
gl.activeTexture(gl.TEXTURE0 + (unit || 0));
gl.bindTexture(gl.TEXTURE_2D, texture);
}
function extractUrl(value) {
var urlMatch = /url\(["']?([^"']*)["']?\)/.exec(value);
if (urlMatch == null) {
return null;
}
return urlMatch[1];
}
function isDataUri(url) {
return url.match(/^data:/);
}
var config = loadConfig();
var transparentPixels = createImageData(32, 32);
// Extend the css
$('head').prepend('<style>.jquery-ripples { position: relative; z-index: 0; }</style>');
// RIPPLES CLASS DEFINITION
// =========================
var Ripples = function (el, options) {
var that = this;
this.$el = $(el);
// Init properties from options
this.interactive = options.interactive;
this.resolution = options.resolution;
this.textureDelta = new Float32Array([1 / this.resolution, 1 / this.resolution]);
this.perturbance = options.perturbance;
this.dropRadius = options.dropRadius;
this.crossOrigin = options.crossOrigin;
this.imageUrl = options.imageUrl;
// Init WebGL canvas
var canvas = document.createElement('canvas');
canvas.width = this.$el.innerWidth();
canvas.height = this.$el.innerHeight();
this.canvas = canvas;
this.$canvas = $(canvas);
this.$canvas.css({
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
zIndex: -1
});
this.$el.addClass('jquery-ripples').append(canvas);
this.context = gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
// Load extensions
config.extensions.forEach(function(name) {
gl.getExtension(name);
});
// Auto-resize when window size changes.
this.updateSize = this.updateSize.bind(this);
$(window).on('resize', this.updateSize);
// Init rendertargets for ripple data.
this.textures = [];
this.framebuffers = [];
this.bufferWriteIndex = 0;
this.bufferReadIndex = 1;
var arrayType = config.arrayType;
var textureData = arrayType ? new arrayType(this.resolution * this.resolution * 4) : null;
for (var i = 0; i < 2; i++) {
var texture = gl.createTexture();
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.resolution, this.resolution, 0, gl.RGBA, config.type, textureData);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
this.textures.push(texture);
this.framebuffers.push(framebuffer);
}
// Init GL stuff
this.quad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1,
+1, -1,
+1, +1,
-1, +1
]), gl.STATIC_DRAW);
this.initShaders();
this.initTexture();
this.setTransparentTexture();
// Load the image either from the options or CSS rules
this.loadImage();
// Set correct clear color and blend mode (regular alpha blending)
gl.clearColor(0, 0, 0, 0);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// Plugin is successfully initialized!
this.visible = true;
this.running = true;
this.inited = true;
this.destroyed = false;
this.setupPointerEvents();
// Init animation
function step() {
if (!that.destroyed) {
that.step();
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
};
Ripples.DEFAULTS = {
imageUrl: null,
resolution: 256,
dropRadius: 20,
perturbance: 0.03,
interactive: true,
crossOrigin: ''
};
Ripples.prototype = {
// Set up pointer (mouse + touch) events
setupPointerEvents: function() {
var that = this;
function pointerEventsEnabled() {
return that.visible && that.running && that.interactive;
}
function dropAtPointer(pointer, big) {
if (pointerEventsEnabled()) {
that.dropAtPointer(
pointer,
that.dropRadius * (big ? 1.5 : 1),
(big ? 0.14 : 0.01)
);
}
}
// Start listening to pointer events
this.$el
// Create regular, small ripples for mouse move and touch events...
.on('mousemove.ripples', function(e) {
dropAtPointer(e);
})
.on('touchmove.ripples touchstart.ripples', function(e) {
var touches = e.originalEvent.changedTouches;
for (var i = 0; i < touches.length; i++) {
dropAtPointer(touches[i]);
}
})
// ...and only a big ripple on mouse down events.
.on('mousedown.ripples', function(e) {
dropAtPointer(e, true);
});
},
// Load the image either from the options or the element's CSS rules.
loadImage: function() {
var that = this;
gl = this.context;
var newImageSource = this.imageUrl ||
extractUrl(this.originalCssBackgroundImage) ||
extractUrl(this.$el.css('backgroundImage'));
// If image source is unchanged, don't reload it.
if (newImageSource == this.imageSource) {
return;
}
this.imageSource = newImageSource;
// Falsy source means no background.
if (!this.imageSource) {
this.setTransparentTexture();
return;
}
// Load the texture from a new image.
var image = new Image;
image.onload = function() {
gl = that.context;
// Only textures with dimensions of powers of two can have repeat wrapping.
function isPowerOfTwo(x) {
return (x & (x - 1)) == 0;
}
var wrapping = (isPowerOfTwo(image.width) && isPowerOfTwo(image.height)) ? gl.REPEAT : gl.CLAMP_TO_EDGE;
gl.bindTexture(gl.TEXTURE_2D, that.backgroundTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapping);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapping);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
that.backgroundWidth = image.width;
that.backgroundHeight = image.height;
// Hide the background that we're replacing.
that.hideCssBackground();
};
// Fall back to a transparent texture when loading the image failed.
image.onerror = function() {
gl = that.context;
that.setTransparentTexture();
};
// Disable CORS when the image source is a data URI.
image.crossOrigin = isDataUri(this.imageSource) ? null : this.crossOrigin;
image.src = this.imageSource;
},
step: function() {
gl = this.context;
if (!this.visible) {
return;
}
this.computeTextureBoundaries();
if (this.running) {
this.update();
}
this.render();
},
drawQuad: function() {
gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
},
render: function() {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
gl.enable(gl.BLEND);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(this.renderProgram.id);
bindTexture(this.backgroundTexture, 0);
bindTexture(this.textures[0], 1);
gl.uniform1f(this.renderProgram.locations.perturbance, this.perturbance);
gl.uniform2fv(this.renderProgram.locations.topLeft, this.renderProgram.uniforms.topLeft);
gl.uniform2fv(this.renderProgram.locations.bottomRight, this.renderProgram.uniforms.bottomRight);
gl.uniform2fv(this.renderProgram.locations.containerRatio, this.renderProgram.uniforms.containerRatio);
gl.uniform1i(this.renderProgram.locations.samplerBackground, 0);
gl.uniform1i(this.renderProgram.locations.samplerRipples, 1);
this.drawQuad();
gl.disable(gl.BLEND);
},
update: function() {
gl.viewport(0, 0, this.resolution, this.resolution);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]);
bindTexture(this.textures[this.bufferReadIndex]);
gl.useProgram(this.updateProgram.id);
this.drawQuad();
this.swapBufferIndices();
},
swapBufferIndices: function() {
this.bufferWriteIndex = 1 - this.bufferWriteIndex;
this.bufferReadIndex = 1 - this.bufferReadIndex;
},
computeTextureBoundaries: function() {
var backgroundSize = this.$el.css('background-size');
var backgroundAttachment = this.$el.css('background-attachment');
var backgroundPosition = translateBackgroundPosition(this.$el.css('background-position'));
// Here the 'container' is the element which the background adapts to
// (either the chrome window or some element, depending on attachment)
var container;
if (backgroundAttachment == 'fixed') {
container = { left: window.pageXOffset, top: window.pageYOffset };
container.width = $window.width();
container.height = $window.height();
}
else {
container = this.$el.offset();
container.width = this.$el.innerWidth();
container.height = this.$el.innerHeight();
}
// TODO: background-clip
if (backgroundSize == 'cover') {
var scale = Math.max(container.width / this.backgroundWidth, container.height / this.backgroundHeight);
var backgroundWidth = this.backgroundWidth * scale;
var backgroundHeight = this.backgroundHeight * scale;
}
else if (backgroundSize == 'contain') {
var scale = Math.min(container.width / this.backgroundWidth, container.height / this.backgroundHeight);
var backgroundWidth = this.backgroundWidth * scale;
var backgroundHeight = this.backgroundHeight * scale;
}
else {
backgroundSize = backgroundSize.split(' ');
var backgroundWidth = backgroundSize[0] || '';
var backgroundHeight = backgroundSize[1] || backgroundWidth;
if (isPercentage(backgroundWidth)) {
backgroundWidth = container.width * parseFloat(backgroundWidth) / 100;
}
else if (backgroundWidth != 'auto') {
backgroundWidth = parseFloat(backgroundWidth);
}
if (isPercentage(backgroundHeight)) {
backgroundHeight = container.height * parseFloat(backgroundHeight) / 100;
}
else if (backgroundHeight != 'auto') {
backgroundHeight = parseFloat(backgroundHeight);
}
if (backgroundWidth == 'auto' && backgroundHeight == 'auto') {
backgroundWidth = this.backgroundWidth;
backgroundHeight = this.backgroundHeight;
}
else {
if (backgroundWidth == 'auto') {
backgroundWidth = this.backgroundWidth * (backgroundHeight / this.backgroundHeight);
}
if (backgroundHeight == 'auto') {
backgroundHeight = this.backgroundHeight * (backgroundWidth / this.backgroundWidth);
}
}
}
// Compute backgroundX and backgroundY in page coordinates
var backgroundX = backgroundPosition[0];
var backgroundY = backgroundPosition[1];
if (isPercentage(backgroundX)) {
backgroundX = container.left + (container.width - backgroundWidth) * parseFloat(backgroundX) / 100;
}
else {
backgroundX = container.left + parseFloat(backgroundX);
}
if (isPercentage(backgroundY)) {
backgroundY = container.top + (container.height - backgroundHeight) * parseFloat(backgroundY) / 100;
}
else {
backgroundY = container.top + parseFloat(backgroundY);
}
var elementOffset = this.$el.offset();
this.renderProgram.uniforms.topLeft = new Float32Array([
(elementOffset.left - backgroundX) / backgroundWidth,
(elementOffset.top - backgroundY) / backgroundHeight
]);
this.renderProgram.uniforms.bottomRight = new Float32Array([
this.renderProgram.uniforms.topLeft[0] + this.$el.innerWidth() / backgroundWidth,
this.renderProgram.uniforms.topLeft[1] + this.$el.innerHeight() / backgroundHeight
]);
var maxSide = Math.max(this.canvas.width, this.canvas.height);
this.renderProgram.uniforms.containerRatio = new Float32Array([
this.canvas.width / maxSide,
this.canvas.height / maxSide
]);
},
initShaders: function() {
var vertexShader = [
'attribute vec2 vertex;',
'varying vec2 coord;',
'void main() {',
'coord = vertex * 0.5 + 0.5;',
'gl_Position = vec4(vertex, 0.0, 1.0);',
'}'
].join('\n');
this.dropProgram = createProgram(vertexShader, [
'precision highp float;',
'const float PI = 3.141592653589793;',
'uniform sampler2D texture;',
'uniform vec2 center;',
'uniform float radius;',
'uniform float strength;',
'varying vec2 coord;',
'void main() {',
'vec4 info = texture2D(texture, coord);',
'float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);',
'drop = 0.5 - cos(drop * PI) * 0.5;',
'info.r += drop * strength;',
'gl_FragColor = info;',
'}'
].join('\n'));
this.updateProgram = createProgram(vertexShader, [
'precision highp float;',
'uniform sampler2D texture;',
'uniform vec2 delta;',
'varying vec2 coord;',
'void main() {',
'vec4 info = texture2D(texture, coord);',
'vec2 dx = vec2(delta.x, 0.0);',
'vec2 dy = vec2(0.0, delta.y);',
'float average = (',
'texture2D(texture, coord - dx).r +',
'texture2D(texture, coord - dy).r +',
'texture2D(texture, coord + dx).r +',
'texture2D(texture, coord + dy).r',
') * 0.25;',
'info.g += (average - info.r) * 2.0;',
'info.g *= 0.995;',
'info.r += info.g;',
'gl_FragColor = info;',
'}'
].join('\n'));
gl.uniform2fv(this.updateProgram.locations.delta, this.textureDelta);
this.renderProgram = createProgram([
'precision highp float;',
'attribute vec2 vertex;',
'uniform vec2 topLeft;',
'uniform vec2 bottomRight;',
'uniform vec2 containerRatio;',
'varying vec2 ripplesCoord;',
'varying vec2 backgroundCoord;',
'void main() {',
'backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);',
'backgroundCoord.y = 1.0 - backgroundCoord.y;',
'ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;',
'gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);',
'}'
].join('\n'), [
'precision highp float;',
'uniform sampler2D samplerBackground;',
'uniform sampler2D samplerRipples;',
'uniform vec2 delta;',
'uniform float perturbance;',
'varying vec2 ripplesCoord;',
'varying vec2 backgroundCoord;',
'void main() {',
'float height = texture2D(samplerRipples, ripplesCoord).r;',
'float heightX = texture2D(samplerRipples, vec2(ripplesCoord.x + delta.x, ripplesCoord.y)).r;',
'float heightY = texture2D(samplerRipples, vec2(ripplesCoord.x, ripplesCoord.y + delta.y)).r;',
'vec3 dx = vec3(delta.x, heightX - height, 0.0);',
'vec3 dy = vec3(0.0, heightY - height, delta.y);',
'vec2 offset = -normalize(cross(dy, dx)).xz;',
'float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);',
'gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;',
'}'
].join('\n'));
gl.uniform2fv(this.renderProgram.locations.delta, this.textureDelta);
},
initTexture: function() {
this.backgroundTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
},
setTransparentTexture: function() {
gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, transparentPixels);
},
hideCssBackground: function() {
// Check whether we're changing inline CSS or overriding a global CSS rule.
var inlineCss = this.$el[0].style.backgroundImage;
if (inlineCss == 'none') {
return;
}
this.originalInlineCss = inlineCss;
this.originalCssBackgroundImage = this.$el.css('backgroundImage');
this.$el.css('backgroundImage', 'none');
},
restoreCssBackground: function() {
// Restore background by either changing the inline CSS rule to what it was, or
// simply remove the inline CSS rule if it never was inlined.
this.$el.css('backgroundImage', this.originalInlineCss || '');
},
dropAtPointer: function(pointer, radius, strength) {
var borderLeft = parseInt(this.$el.css('border-left-width')) || 0,
borderTop = parseInt(this.$el.css('border-top-width')) || 0;
this.drop(
pointer.pageX - this.$el.offset().left - borderLeft,
pointer.pageY - this.$el.offset().top - borderTop,
radius,
strength
);
},
/**
* Public methods
*/
drop: function(x, y, radius, strength) {
gl = this.context;
var elWidth = this.$el.innerWidth();
var elHeight = this.$el.innerHeight();
var longestSide = Math.max(elWidth, elHeight);
radius = radius / longestSide;
var dropPosition = new Float32Array([
(2 * x - elWidth) / longestSide,
(elHeight - 2 * y) / longestSide
]);
gl.viewport(0, 0, this.resolution, this.resolution);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]);
bindTexture(this.textures[this.bufferReadIndex]);
gl.useProgram(this.dropProgram.id);
gl.uniform2fv(this.dropProgram.locations.center, dropPosition);
gl.uniform1f(this.dropProgram.locations.radius, radius);
gl.uniform1f(this.dropProgram.locations.strength, strength);
this.drawQuad();
this.swapBufferIndices();
},
updateSize: function() {
var newWidth = this.$el.innerWidth(),
newHeight = this.$el.innerHeight();
if (newWidth != this.canvas.width || newHeight != this.canvas.height) {
this.canvas.width = newWidth;
this.canvas.height = newHeight;
}
},
destroy: function() {
this.$el
.off('.ripples')
.removeClass('jquery-ripples')
.removeData('ripples');
// Make sure the last used context is garbage-collected
gl = null;
$(window).off('resize', this.updateSize);
this.$canvas.remove();
this.restoreCssBackground();
this.destroyed = true;
},
show: function() {
this.visible = true;
this.$canvas.show();
this.hideCssBackground();
},
hide: function() {
this.visible = false;
this.$canvas.hide();
this.restoreCssBackground();
},
pause: function() {
this.running = false;
},
play: function() {
this.running = true;
},
set: function(property, value) {
switch (property) {
case 'dropRadius':
case 'perturbance':
case 'interactive':
case 'crossOrigin':
this[property] = value;
break;
case 'imageUrl':
this.imageUrl = value;
this.loadImage();
break;
}
}
};
// RIPPLES PLUGIN DEFINITION
// ==========================
var old = $.fn.ripples;
$.fn.ripples = function(option) {
if (!config) {
throw new Error('Your browser does not support WebGL, the OES_texture_float extension or rendering to floating point textures.');
}
var args = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : undefined;
// Load the texture from a new image.
const image = new Image();
image.onload = function() {
gl = that.context;
return this.each(function() {
var $this = $(this),
data = $this.data('ripples'),
options = $.extend({}, Ripples.DEFAULTS, $this.data(), typeof option == 'object' && option);
if (!data && typeof option == 'string') {
return;
}
if (!data) {
$this.data('ripples', (data = new Ripples(this, options)));
}
else if (typeof option == 'string') {
Ripples.prototype[option].apply(data, args);
}
});
};
$.fn.ripples.Constructor = Ripples;
// RIPPLES NO CONFLICT
// ====================
$.fn.ripples.noConflict = function() {
$.fn.ripples = old;
return this;
};
})));
// Only textures with dimensions of powers of two can have repeat wrapping.
function isPowerOfTwo(x) {
return (x & (x - 1)) == 0;
}
const wrapping = isPowerOfTwo(image.width) && isPowerOfTwo(image.height) ? gl.REPEAT : gl.CLAMP_TO_EDGE;
gl.bindTexture(gl.TEXTURE_2D, that.backgroundTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapping);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapping);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
that.backgroundWidth = image.width;
that.backgroundHeight = image.height;
// Hide the background that we're replacing.
that.hideCssBackground();
};
// Fall back to a transparent texture when loading the image failed.
image.onerror = function() {
gl = that.context;
that.setTransparentTexture();
};
// Disable CORS when the image source is a data URI.
image.crossOrigin = isDataUri(this.imageSource) ? null : this.crossOrigin;
image.src = this.imageSource;
},
step() {
gl = this.context;
if (!this.visible) {
return;
}
this.computeTextureBoundaries();
if (this.running) {
this.update();
}
this.render();
},
drawQuad() {
gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
},
render() {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
gl.enable(gl.BLEND);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(this.renderProgram.id);
bindTexture(this.backgroundTexture, 0);
bindTexture(this.textures[0], 1);
gl.uniform1f(this.renderProgram.locations.perturbance, this.perturbance);
gl.uniform2fv(this.renderProgram.locations.topLeft, this.renderProgram.uniforms.topLeft);
gl.uniform2fv(this.renderProgram.locations.bottomRight, this.renderProgram.uniforms.bottomRight);
gl.uniform2fv(this.renderProgram.locations.containerRatio, this.renderProgram.uniforms.containerRatio);
gl.uniform1i(this.renderProgram.locations.samplerBackground, 0);
gl.uniform1i(this.renderProgram.locations.samplerRipples, 1);
this.drawQuad();
gl.disable(gl.BLEND);
},
update() {
gl.viewport(0, 0, this.resolution, this.resolution);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]);
bindTexture(this.textures[this.bufferReadIndex]);
gl.useProgram(this.updateProgram.id);
this.drawQuad();
this.swapBufferIndices();
},
swapBufferIndices() {
this.bufferWriteIndex = 1 - this.bufferWriteIndex;
this.bufferReadIndex = 1 - this.bufferReadIndex;
},
computeTextureBoundaries() {
let backgroundSize = this.$el.css('background-size');
const backgroundAttachment = this.$el.css('background-attachment');
const backgroundPosition = translateBackgroundPosition(this.$el.css('background-position'));
// Here the 'container' is the element which the background adapts to
// (either the chrome window or some element, depending on attachment)
let container;
if (backgroundAttachment == 'fixed') {
container = { left: window.pageXOffset, top: window.pageYOffset };
container.width = $window.width();
container.height = $window.height();
} else {
container = this.$el.offset();
container.width = this.$el.innerWidth();
container.height = this.$el.innerHeight();
}
// TODO: background-clip
if (backgroundSize == 'cover') {
var scale = Math.max(container.width / this.backgroundWidth, container.height / this.backgroundHeight);
var backgroundWidth = this.backgroundWidth * scale;
var backgroundHeight = this.backgroundHeight * scale;
} else if (backgroundSize == 'contain') {
var scale = Math.min(container.width / this.backgroundWidth, container.height / this.backgroundHeight);
var backgroundWidth = this.backgroundWidth * scale;
var backgroundHeight = this.backgroundHeight * scale;
} else {
backgroundSize = backgroundSize.split(' ');
var backgroundWidth = backgroundSize[0] || '';
var backgroundHeight = backgroundSize[1] || backgroundWidth;
if (isPercentage(backgroundWidth)) {
backgroundWidth = (container.width * parseFloat(backgroundWidth)) / 100;
} else if (backgroundWidth != 'auto') {
backgroundWidth = parseFloat(backgroundWidth);
}
if (isPercentage(backgroundHeight)) {
backgroundHeight = (container.height * parseFloat(backgroundHeight)) / 100;
} else if (backgroundHeight != 'auto') {
backgroundHeight = parseFloat(backgroundHeight);
}
if (backgroundWidth == 'auto' && backgroundHeight == 'auto') {
backgroundWidth = this.backgroundWidth;
backgroundHeight = this.backgroundHeight;
} else {
if (backgroundWidth == 'auto') {
backgroundWidth = this.backgroundWidth * (backgroundHeight / this.backgroundHeight);
}
if (backgroundHeight == 'auto') {
backgroundHeight = this.backgroundHeight * (backgroundWidth / this.backgroundWidth);
}
}
}
// Compute backgroundX and backgroundY in page coordinates
let backgroundX = backgroundPosition[0];
let backgroundY = backgroundPosition[1];
if (isPercentage(backgroundX)) {
backgroundX = container.left + ((container.width - backgroundWidth) * parseFloat(backgroundX)) / 100;
} else {
backgroundX = container.left + parseFloat(backgroundX);
}
if (isPercentage(backgroundY)) {
backgroundY = container.top + ((container.height - backgroundHeight) * parseFloat(backgroundY)) / 100;
} else {
backgroundY = container.top + parseFloat(backgroundY);
}
const elementOffset = this.$el.offset();
this.renderProgram.uniforms.topLeft = new Float32Array([
(elementOffset.left - backgroundX) / backgroundWidth,
(elementOffset.top - backgroundY) / backgroundHeight,
]);
this.renderProgram.uniforms.bottomRight = new Float32Array([
this.renderProgram.uniforms.topLeft[0] + this.$el.innerWidth() / backgroundWidth,
this.renderProgram.uniforms.topLeft[1] + this.$el.innerHeight() / backgroundHeight,
]);
const maxSide = Math.max(this.canvas.width, this.canvas.height);
this.renderProgram.uniforms.containerRatio = new Float32Array([
this.canvas.width / maxSide,
this.canvas.height / maxSide,
]);
},
initShaders() {
const vertexShader = [
'attribute vec2 vertex;',
'varying vec2 coord;',
'void main() {',
'coord = vertex * 0.5 + 0.5;',
'gl_Position = vec4(vertex, 0.0, 1.0);',
'}',
].join('\n');
this.dropProgram = createProgram(
vertexShader,
[
'precision highp float;',
'const float PI = 3.141592653589793;',
'uniform sampler2D texture;',
'uniform vec2 center;',
'uniform float radius;',
'uniform float strength;',
'varying vec2 coord;',
'void main() {',
'vec4 info = texture2D(texture, coord);',
'float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);',
'drop = 0.5 - cos(drop * PI) * 0.5;',
'info.r += drop * strength;',
'gl_FragColor = info;',
'}',
].join('\n'),
);
this.updateProgram = createProgram(
vertexShader,
[
'precision highp float;',
'uniform sampler2D texture;',
'uniform vec2 delta;',
'varying vec2 coord;',
'void main() {',
'vec4 info = texture2D(texture, coord);',
'vec2 dx = vec2(delta.x, 0.0);',
'vec2 dy = vec2(0.0, delta.y);',
'float average = (',
'texture2D(texture, coord - dx).r +',
'texture2D(texture, coord - dy).r +',
'texture2D(texture, coord + dx).r +',
'texture2D(texture, coord + dy).r',
') * 0.25;',
'info.g += (average - info.r) * 2.0;',
'info.g *= 0.995;',
'info.r += info.g;',
'gl_FragColor = info;',
'}',
].join('\n'),
);
gl.uniform2fv(this.updateProgram.locations.delta, this.textureDelta);
this.renderProgram = createProgram(
[
'precision highp float;',
'attribute vec2 vertex;',
'uniform vec2 topLeft;',
'uniform vec2 bottomRight;',
'uniform vec2 containerRatio;',
'varying vec2 ripplesCoord;',
'varying vec2 backgroundCoord;',
'void main() {',
'backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);',
'backgroundCoord.y = 1.0 - backgroundCoord.y;',
'ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;',
'gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);',
'}',
].join('\n'),
[
'precision highp float;',
'uniform sampler2D samplerBackground;',
'uniform sampler2D samplerRipples;',
'uniform vec2 delta;',
'uniform float perturbance;',
'varying vec2 ripplesCoord;',
'varying vec2 backgroundCoord;',
'void main() {',
'float height = texture2D(samplerRipples, ripplesCoord).r;',
'float heightX = texture2D(samplerRipples, vec2(ripplesCoord.x + delta.x, ripplesCoord.y)).r;',
'float heightY = texture2D(samplerRipples, vec2(ripplesCoord.x, ripplesCoord.y + delta.y)).r;',
'vec3 dx = vec3(delta.x, heightX - height, 0.0);',
'vec3 dy = vec3(0.0, heightY - height, delta.y);',
'vec2 offset = -normalize(cross(dy, dx)).xz;',
'float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);',
'gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;',
'}',
].join('\n'),
);
gl.uniform2fv(this.renderProgram.locations.delta, this.textureDelta);
},
initTexture() {
this.backgroundTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
},
setTransparentTexture() {
gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, transparentPixels);
},
hideCssBackground() {
// Check whether we're changing inline CSS or overriding a global CSS rule.
const inlineCss = this.$el[0].style.backgroundImage;
if (inlineCss == 'none') {
return;
}
this.originalInlineCss = inlineCss;
this.originalCssBackgroundImage = this.$el.css('backgroundImage');
this.$el.css('backgroundImage', 'none');
},
restoreCssBackground() {
// Restore background by either changing the inline CSS rule to what it was, or
// simply remove the inline CSS rule if it never was inlined.
this.$el.css('backgroundImage', this.originalInlineCss || '');
},
dropAtPointer(pointer, radius, strength) {
const borderLeft = parseInt(this.$el.css('border-left-width')) || 0;
const borderTop = parseInt(this.$el.css('border-top-width')) || 0;
this.drop(
pointer.pageX - this.$el.offset().left - borderLeft,
pointer.pageY - this.$el.offset().top - borderTop,
radius,
strength,
);
},
/**
* Public methods
*/
drop(x, y, radius, strength) {
gl = this.context;
const elWidth = this.$el.innerWidth();
const elHeight = this.$el.innerHeight();
const longestSide = Math.max(elWidth, elHeight);
radius /= longestSide;
const dropPosition = new Float32Array([(2 * x - elWidth) / longestSide, (elHeight - 2 * y) / longestSide]);
gl.viewport(0, 0, this.resolution, this.resolution);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]);
bindTexture(this.textures[this.bufferReadIndex]);
gl.useProgram(this.dropProgram.id);
gl.uniform2fv(this.dropProgram.locations.center, dropPosition);
gl.uniform1f(this.dropProgram.locations.radius, radius);
gl.uniform1f(this.dropProgram.locations.strength, strength);
this.drawQuad();
this.swapBufferIndices();
},
updateSize() {
const newWidth = this.$el.innerWidth();
const newHeight = this.$el.innerHeight();
if (newWidth != this.canvas.width || newHeight != this.canvas.height) {
this.canvas.width = newWidth;
this.canvas.height = newHeight;
}
},
destroy() {
this.$el
.off('.ripples')
.removeClass('jquery-ripples')
.removeData('ripples');
// Make sure the last used context is garbage-collected
gl = null;
$(window).off('resize', this.updateSize);
this.$canvas.remove();
this.restoreCssBackground();
this.destroyed = true;
},
show() {
this.visible = true;
this.$canvas.show();
this.hideCssBackground();
},
hide() {
this.visible = false;
this.$canvas.hide();
this.restoreCssBackground();
},
pause() {
this.running = false;
},
play() {
this.running = true;
},
set(property, value) {
switch (property) {
case 'dropRadius':
case 'perturbance':
case 'interactive':
case 'crossOrigin':
this[property] = value;
break;
case 'imageUrl':
this.imageUrl = value;
this.loadImage();
break;
}
},
};
// RIPPLES PLUGIN DEFINITION
// ==========================
const old = $.fn.ripples;
$.fn.ripples = function(option) {
if (!config) {
throw new Error(
'Your browser does not support WebGL, the OES_texture_float extension or rendering to floating point textures.',
);
}
const args = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : undefined;
return this.each(function() {
const $this = $(this);
let data = $this.data('ripples');
const options = $.extend({}, Ripples.DEFAULTS, $this.data(), typeof option === 'object' && option);
if (!data && typeof option === 'string') {
return;
}
if (!data) {
$this.data('ripples', (data = new Ripples(this, options)));
} else if (typeof option === 'string') {
Ripples.prototype[option].apply(data, args);
}
});
};
$.fn.ripples.Constructor = Ripples;
// RIPPLES NO CONFLICT
// ====================
$.fn.ripples.noConflict = function() {
$.fn.ripples = old;
return this;
};
});
......@@ -2,26 +2,20 @@
/* globals jQuery, navigator */
(function($, window, document, undefined) {
"use strict";
/**
* Define the name of the plugin
*/
var ripples = "ripples";
const ripples = 'ripples';
/**
* Get an instance of the plugin
*/
var self = null;
let self = null;
/**
* Define the defaults of the plugin
*/
var defaults = {};
const defaults = {};
/**
* Create the main plugin function
......@@ -39,90 +33,79 @@
this.init();
}
/**
* Initialize the plugin
*/
Ripples.prototype.init = function() {
var $element = this.element;
const $element = this.element;
$element.on("mousedown touchstart", function(event) {
$element.on('mousedown touchstart', function(event) {
/**
* Verify if the user is just touching on a device and return if so
*/
if(self.isTouch() && event.type === "mousedown") {
if (self.isTouch() && event.type === 'mousedown') {
return;
}
/**
* Verify if the current element already has a ripple wrapper element and
* creates if it doesn't
*/
if(!($element.find(".ripple-container").length)) {
$element.append("<div class=\"ripple-container\"></div>");
if (!$element.find('.ripple-container').length) {
$element.append('<div class="ripple-container"></div>');
}
/**
* Find the ripple wrapper
*/
var $wrapper = $element.children(".ripple-container");
const $wrapper = $element.children('.ripple-container');
/**
* Get relY and relX positions
*/
var relY = self.getRelY($wrapper, event);
var relX = self.getRelX($wrapper, event);
const relY = self.getRelY($wrapper, event);
const relX = self.getRelX($wrapper, event);
/**
* If relY and/or relX are false, return the event
*/
if(!relY && !relX) {
if (!relY && !relX) {
return;
}
/**
* Get the ripple color
*/
var rippleColor = self.getRipplesColor($element);
const rippleColor = self.getRipplesColor($element);
/**
* Create the ripple element
*/
var $ripple = $("<div></div>");
const $ripple = $('<div></div>');
$ripple
.addClass("ripple")
.css({
"left": relX,
"top": relY,
"background-color": rippleColor
$ripple.addClass('ripple').css({
left: relX,
top: relY,
'background-color': rippleColor,
});
/**
* Append the ripple to the wrapper
*/
$wrapper.append($ripple);
/**
* Make sure the ripple has the styles applied (ugly hack but it works)
*/
(function() { return window.getComputedStyle($ripple[0]).opacity; })();
(function() {
return window.getComputedStyle($ripple[0]).opacity;
})();
/**
* Turn on the ripple animation
*/
self.rippleOn($element, $ripple);
/**
* Call the rippleEnd function when the transition "on" ends
*/
......@@ -130,115 +113,104 @@
self.rippleEnd($ripple);
}, 500);
/**
* Detect when the user leaves the element
*/
$element.on("mouseup mouseleave touchend", function() {
$ripple.data("mousedown", "off");
$element.on('mouseup mouseleave touchend', function() {
$ripple.data('mousedown', 'off');
if($ripple.data("animating") === "off") {
if ($ripple.data('animating') === 'off') {
self.rippleOut($ripple);
}
});
});
};
/**
* Get the new size based on the element height/width and the ripple width
*/
Ripples.prototype.getNewSize = function($element, $ripple) {
return (Math.max($element.outerWidth(), $element.outerHeight()) / $ripple.outerWidth()) * 2.5;
};
/**
* Get the relX
*/
Ripples.prototype.getRelX = function($wrapper, event) {
var wrapperOffset = $wrapper.offset();
Ripples.prototype.getRelX = function($wrapper, event) {
const wrapperOffset = $wrapper.offset();
if(!self.isTouch()) {
if (!self.isTouch()) {
/**
* Get the mouse position relative to the ripple wrapper
*/
return event.pageX - wrapperOffset.left;
} else {
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event = event.originalEvent;
if(event.touches.length === 1) {
return event.touches[0].pageX - wrapperOffset.left;
}
return false;
}
};
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event = event.originalEvent;
if (event.touches.length === 1) {
return event.touches[0].pageX - wrapperOffset.left;
}
return false;
};
/**
* Get the relY
*/
Ripples.prototype.getRelY = function($wrapper, event) {
var wrapperOffset = $wrapper.offset();
const wrapperOffset = $wrapper.offset();
if(!self.isTouch()) {
if (!self.isTouch()) {
/**
* Get the mouse position relative to the ripple wrapper
*/
return event.pageY - wrapperOffset.top;
} else {
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event = event.originalEvent;
if(event.touches.length === 1) {
return event.touches[0].pageY - wrapperOffset.top;
}
return false;
}
};
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event = event.originalEvent;
if (event.touches.length === 1) {
return event.touches[0].pageY - wrapperOffset.top;
}
return false;
};
/**
* Get the ripple color
*/
Ripples.prototype.getRipplesColor = function($element) {
var color = $element.data("ripple-color") ? $element.data("ripple-color") : window.getComputedStyle($element[0]).color;
const color = $element.data('ripple-color')
? $element.data('ripple-color')
: window.getComputedStyle($element[0]).color;
return color;
};
/**
* Verify if the client browser has transistion support
*/
Ripples.prototype.hasTransitionSupport = function() {
var thisBody = document.body || document.documentElement;
var thisStyle = thisBody.style;
const thisBody = document.body || document.documentElement;
const thisStyle = thisBody.style;
var support = (
const support =
thisStyle.transition !== undefined ||
thisStyle.WebkitTransition !== undefined ||
thisStyle.MozTransition !== undefined ||
thisStyle.MsTransition !== undefined ||
thisStyle.OTransition !== undefined
);
thisStyle.OTransition !== undefined;
return support;
};
/**
* Verify if the client is using a mobile device
*/
......@@ -246,79 +218,78 @@
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
};
/**
* End the animation of the ripple
*/
Ripples.prototype.rippleEnd = function($ripple) {
$ripple.data("animating", "off");
$ripple.data('animating', 'off');
if($ripple.data("mousedown") === "off") {
if ($ripple.data('mousedown') === 'off') {
self.rippleOut($ripple);
}
};
/**
* Turn off the ripple effect
*/
Ripples.prototype.rippleOut = function($ripple) {
$ripple.off();
if(self.hasTransitionSupport()) {
$ripple.addClass("ripple-out");
if (self.hasTransitionSupport()) {
$ripple.addClass('ripple-out');
} else {
$ripple.animate({"opacity": 0}, 100, function() {
$ripple.trigger("transitionend");
$ripple.animate({ opacity: 0 }, 100, function() {
$ripple.trigger('transitionend');
});
}
$ripple.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function() {
$ripple.on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function() {
$ripple.remove();
});
};
/**
* Turn on the ripple effect
*/
Ripples.prototype.rippleOn = function($element, $ripple) {
var size = self.getNewSize($element, $ripple);
const size = self.getNewSize($element, $ripple);
if(self.hasTransitionSupport()) {
if (self.hasTransitionSupport()) {
$ripple
.css({
"-ms-transform": "scale(" + size + ")",
"-moz-transform": "scale(" + size + ")",
"-webkit-transform": "scale(" + size + ")",
"transform": "scale(" + size + ")"
})
.addClass("ripple-on")
.data("animating", "on")
.data("mousedown", "on");
.css({
'-ms-transform': `scale(${size})`,
'-moz-transform': `scale(${size})`,
'-webkit-transform': `scale(${size})`,
transform: `scale(${size})`,
})
.addClass('ripple-on')
.data('animating', 'on')
.data('mousedown', 'on');
} else {
$ripple.animate({
"width": Math.max($element.outerWidth(), $element.outerHeight()) * 2,
"height": Math.max($element.outerWidth(), $element.outerHeight()) * 2,
"margin-left": Math.max($element.outerWidth(), $element.outerHeight()) * (-1),
"margin-top": Math.max($element.outerWidth(), $element.outerHeight()) * (-1),
"opacity": 0.2
}, 500, function() {
$ripple.trigger("transitionend");
});
$ripple.animate(
{
width: Math.max($element.outerWidth(), $element.outerHeight()) * 2,
height: Math.max($element.outerWidth(), $element.outerHeight()) * 2,
'margin-left': Math.max($element.outerWidth(), $element.outerHeight()) * -1,
'margin-top': Math.max($element.outerWidth(), $element.outerHeight()) * -1,
opacity: 0.2,
},
500,
function() {
$ripple.trigger('transitionend');
},
);
}
};
/**
* Create the jquery plugin function
*/
$.fn.ripples = function(options) {
return this.each(function() {
if(!$.data(this, "plugin_" + ripples)) {
$.data(this, "plugin_" + ripples, new Ripples(this, options));
if (!$.data(this, `plugin_${ripples}`)) {
$.data(this, `plugin_${ripples}`, new Ripples(this, options));
}
});
};
})(jQuery, window, document);
......@@ -5,12 +5,14 @@ import { connect } from 'react-redux';
import { FormattedMessage } from '@wisdom-utils/components';
import { history } from '@wisdom-utils/runtime';
import { findPathByWidget, isJSON } from '@wisdom-utils/components/lib/AppLayout/helpers';
import classNames from 'classnames';
import Notifier from '../../layouts/AppLayout/notifier/notice';
import NoticeIcon from '../../layouts/AppLayout/notifier';
import { ERR_OK, MESSAGE_TYPE, NEW_MESSAGE } from '../../layouts/AppLayout/notifier/constants';
import service from '../../api/service/notification';
import { actionCreators } from '../../containers/App/store';
import isProd from '../../utils/env';
import { getMessageTypeIcon } from '../../layouts/AppLayout/notifier/utils';
// import NoticeIcon from '../NoticeIcon';
import styles from './index.less';
......@@ -19,17 +21,27 @@ const { TextArea } = Input;
const PlatformModal = ({ platformVisible, handleClosePlatform, handlerMointer, messages = [] }) => {
const [pageIndex, setPageIndex] = useState(1);
const message = useMemo(() => messages[pageIndex - 1], [messages, pageIndex]);
const isAlarm = message.infoClasses === MESSAGE_TYPE.ALARM_TYPE;
const defaultIcon = isAlarm
? 'https://panda-water.com/web4/assets/images/message/报警图标.svg'
: getMessageTypeIcon(message);
const icon = message.webIcon ? `${window.location.origin}/${message.webIcon}` : defaultIcon;
return (
<Modal
title={<FormattedMessage id="component.noticeIcon.modal.alarm.title" />}
title={
isAlarm ? (
<FormattedMessage id="component.noticeIcon.modal.alarm.title" />
) : (
<FormattedMessage id="component.noticeIcon.model.system.title" />
)
}
maskClosable={false}
mask={false}
maskStyle={{ pointerEvents: 'none' }}
visible={platformVisible}
zIndex={5000}
wrapClassName={styles.platformModalWrap}
className={styles.platformModal}
className={classNames(styles.platformModal, !isAlarm && styles.platformModalDefault)}
footer={
<Pagination
simple
......@@ -45,7 +57,7 @@ const PlatformModal = ({ platformVisible, handleClosePlatform, handlerMointer, m
>
<div className={styles.alarmContent}>
{/* eslint-disable-next-line jsx-a11y/alt-text */}
<img src="https://panda-water.com/web4/assets/images/message/报警图标.svg" alt="" />
<img src={icon} alt="" />
<div className={styles.content}>
<div className={styles['content-top']}>
<a onClick={event => handlerMointer(event, message, true)}>
......@@ -53,20 +65,27 @@ const PlatformModal = ({ platformVisible, handleClosePlatform, handlerMointer, m
</a>
<span title="点击标为已读" onClick={event => handlerMointer(event, message, false)} />
</div>
<div className={styles['content-mid']}>
<b>{message && message.infoContent && message.infoContent.alarmType}</b>
{`|${message && message.infoContent && message.infoContent.alarmContent}`}
</div>
<div className={styles['content-bottom']}>
<p>
报警值:
<b>{message && message.infoContent && message.infoContent.alarmValue}</b>
{' / '}
预设值:
{message && message.infoContent && message.infoContent.alarmThreshold}
</p>
</div>
{isAlarm ? (
<>
<div className={styles['content-mid']}>
<b>{message && message.infoContent && message.infoContent.alarmType}</b>
{`|${message && message.infoContent && message.infoContent.alarmContent}`}
</div>
<div className={styles['content-bottom']}>
<p>
报警值:
<b>{message && message.infoContent && message.infoContent.alarmValue}</b>
{' / '}
预设值:
{message && message.infoContent && message.infoContent.alarmThreshold}
</p>
</div>
</>
) : (
<div className={styles['content-bottom']}>
<p>{message && message.infoContent && message.infoContent.content}</p>
</div>
)}
<p className={styles['message-time']}>{message && message.time}</p>
</div>
</div>
......@@ -110,7 +129,7 @@ class NoticeIconView extends Component {
}
get platformMessages() {
return this.state.noticeData.filter(item => item.infoLevel === '4' && item.infoClasses === 'alarmType');
return this.state.noticeData.filter(item => item.infoLevel === '4');
}
async componentDidMount() {
......
......@@ -376,6 +376,13 @@
}
}
}
.platformModalDefault {
:global {
.@{ant-prefix}-modal-header {
background: var(--panda-console-base-header-bg-color);
}
}
}
.videoPopup {
:global(.@{ant-prefix}-modal-content) {
height: 520px;
......
......@@ -15,7 +15,8 @@ class Message {
defaultContent,
webIcon,
title,
infoClasses
infoClasses,
webVoice
} = message) {
this.id = id;
this.infoContent = infoContent;
......@@ -30,6 +31,7 @@ class Message {
this.title = title; // 消息标题
this.defaultContent = defaultContent;
this.infoClasses = infoClasses;
this.webVoice = webVoice;
}
}
......@@ -68,6 +70,7 @@ export const createMessageFromHis = (hisMessage, options = {}) => {
dateTime: hisMessage.hisCreateTime,
infoLevel: hisMessage.infoLevel,
webConfig: hisMessage.web_config,
webVoice: hisMessage.webVoice,
webPath: hisMessage.web_path,
messType: hisMessage.messType,
defaultContent: (_.isString(hisMessage.defaultContent) && hisMessage.defaultContent.replace(new RegExp(/ /g), "").length > 0) ? hisMessage.defaultContent : null,
......@@ -109,6 +112,7 @@ export const createMessageFromReal = (realMesssage, options = {}) => {
dateTime: realMesssage.createTime,
webConfig: realMesssage.web_config,
webPath: realMesssage.web_path,
webVoice: realMesssage.webVoice,
messType: realMesssage.messType || realMesssage.MessType,
defaultContent: (_.isString(realMesssage.defaultContent) && realMesssage.defaultContent.replace(new RegExp(/ /g), "").length > 0) ? realMesssage.defaultContent : null,
webIcon: (_.isString(realMesssage.webIcon) && realMesssage.webIcon.replace(new RegExp(/ /g), "").length > 0) ? realMesssage.webIcon : null,
......
......@@ -425,7 +425,8 @@ class Notifier {
*/
speakMessage = message => {
if (!message) return;
const { version } = message;
const { version, webVoice } = message;
if(webVoice) return this.speakWebVoice(message);
if(version === '3.0') return this.speakDefault(message);
switch(message.infoType) {
case 'SCADA报警':
......@@ -447,6 +448,7 @@ class Notifier {
speakAlarm = message => {
const device =
message?.infoContent?.title
?.replace(/_/g, ',')
?.split(' ')
?.splice(1)
?.join(' ') ?? ''; // 报警设备:“【阈值报警】 二供泵房 | 光谷物联港”
......@@ -488,9 +490,15 @@ class Notifier {
this.speak(msg);
};
speakDefault = message => {
let msg = `${message.title},${message.title}: ${message.infoContent ?? ''}`
let msg = replaceSpeak(`${message.title},${message.title}: ${message.infoContent ?? ''}`)
this.speak(msg);
};
speakWebVoice = message => {
const { webVoice } = message;
if(typeof webVoice === "string") {
this.speak(replaceSpeak(webVoice.replace(/_/g, ',')));
}
}
speak = msg => {
if (!this.speakState) return;
this.speakState.text = msg;
......@@ -513,11 +521,7 @@ class Notifier {
this.renderPopVideo(message); // 显示视频弹窗
return;
}
if (message.infoLevel === PLATFORM_LEVEL && message.infoClasses === MESSAGE_TYPE.ALARM_TYPE) {
this.renderPopPlatform(message); // 显示报警弹窗
return;
}
if (message.version === '3.0' && message.infoLevel === PLATFORM_LEVEL) {
if (message.infoLevel === PLATFORM_LEVEL) {
this.renderPopPlatform(message); // 显示报警弹窗
return;
}
......
......@@ -548,6 +548,7 @@ const Layout = props => {
loading: menuLoading,
// onLoadingChange: (loading) => setMenuLoading(loading)
}}
hideFeedback={props.global.hideFeedback}
logo={logo}
topMenuActiveKey={Number(sessionStorage.getItem(currentProduct)) || 0}
rightContentRender={() => <RightContent />}
......
......@@ -399,10 +399,11 @@ class Site {
config.widgets = [];
config.allWidgets = [];
config.userInfo = window?.globalConfig?.transformUserInfo?.(res.data) ?? res.data;
window.globalConfig = config;
// 重置网关配置
// eslint-disable-next-line prettier/prettier, no-undef
const hasGateWay = !gatewayRes || !gatewayRes.data ? false : _.isString(gatewayRes.data) ? JSON.parse(gatewayRes.data) : (typeof gatewayRes.data === 'boolean' ? gatewayRes.data : false);
config.hasGateWay = hasGateWay;
config.apiGatewayDomain = `${window.location.origin}${hasGateWay ? '/PandaCore/GateWay' : ''}`;
if (hasGateWay) {
appService
.authorizationToken({
......@@ -411,14 +412,17 @@ class Site {
})
.then(tokenRes => {
const accessToken = tokenRes.data?.access_token ?? null;
config.access_token = accessToken;
localStorage.setItem('access_token', accessToken);
this.writeCookie(token, site, onChangeVisible, actionRef, accessToken);
})
.catch(err => {
this.setLoading(false);
config.access_token = null;
this.writeCookie(token, site, onChangeVisible, actionRef, null);
});
} else {
config.access_token = null;
localStorage.setItem('access_token', null);
this.writeCookie(token, site, onChangeVisible, actionRef, null);
}
......
......@@ -261,10 +261,9 @@ export const initMicroApps = (url) => {
start(Configuration);
runAfterFirstMounted(() => {
event.emit('loading', false);
const time = micro.length > 3 ? 3000 : 500;
setTimeout(() => {
prefetchApps(micro);
}, time)
}, 5000)
Logger.info('[MainApp] first app mounted');
});
addGlobalUncaughtErrorHandler(event => {
......
......@@ -27,6 +27,8 @@
.page {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.pageTitle {
margin-bottom: 18px;
......@@ -41,6 +43,7 @@
background-size: 100% 100%;
background-repeat: no-repeat;
font-family: "zihun";
flex: none;
}
.page-time {
......@@ -97,6 +100,8 @@
min-height: 396px;
width: 100%;
position: relative;
flex: 1;
overflow-y: auto;
}
.cus-arrow {
......@@ -133,16 +138,20 @@
margin: 0 auto;
width: 42em;
&[num="2"],
&[num="4"] {
&:not(&[num="1"]) {
width: (42em * 2 + 1em);
}
&[num="3"],
&[num="5"],
&[num="6"] {
width: (42em * 3 + 2em);
}
// &[num="2"],
// &[num="4"] {
// width: (42em * 2 + 1em);
// }
// &[num="3"],
// &[num="5"],
// &[num="6"] {
// width: (42em * 3 + 2em);
// }
&[num="1"],
&[num="2"],
......@@ -387,16 +396,19 @@
gap: 1.5em;
width: 44em;
&[num="2"],
&[num="4"] {
&:not(&[num="1"]) {
width: (44em * 2 + 2em);
}
// &[num="2"],
// &[num="4"] {
// width: (44em * 2 + 2em);
// }
&[num="3"],
&[num="5"],
&[num="6"] {
width: (44em * 3 + 3em);
}
// &[num="3"],
// &[num="5"],
// &[num="6"] {
// width: (44em * 3 + 3em);
// }
&[num="1"],
&[num="2"],
......@@ -475,16 +487,20 @@
gap: 1.5em;
width: 54em;
&[num="2"],
&[num="4"] {
&:not(&[num="1"]) {
width: (54em * 2 + 2em);
}
&[num="3"],
&[num="5"],
&[num="6"] {
width: (54em * 3 + 8em);
}
// &[num="2"],
// &[num="4"] {
// width: (54em * 2 + 2em);
// }
// &[num="3"],
// &[num="5"],
// &[num="6"] {
// width: (54em * 3 + 8em);
// }
&[num="1"],
&[num="2"],
......
/* eslint-disable no-lonely-if */
import React, { useEffect, useRef, useState } from 'react';
import Iframe from 'react-iframe';
import { appService } from '@/api';
import Empty from '@wisdom-components/empty';
import axios from 'axios';
import { Message, Spin, notification } from 'antd';
import styles from './index.less';
const IntegratedLogin = props => {
const { sysName } = props.params;
const [linkUrl, setLinkUrl] = useState('');
const [loading, setLoading] = useState(false);
const ticketData = useRef();
const getTicket = () => {
appService.getTicketByToken({ token: window.globalConfig.token }).then(res => {
if (res.code === 0) {
ticketData.current = res.data;
} else {
notification.error({
message: '提示',
duration: 3,
description: res.msg,
});
}
});
};
// 外网通不通判断
const ping = () => {
fetch('https://www.google.com')
.then(response => {
if (response.ok) {
return true;
}
return false;
})
.catch(error => {
return false;
});
};
const getUrlParam = url => {
let arrObj = url.split('?');
let params = Object.create(null);
if (arrObj.length > 1) {
arrObj = arrObj[1].split('&');
arrObj.forEach(item => {
item = item.split('=');
params[item[0]] = item[1];
});
}
return params;
};
const getSiteConfig = () => {
if (linkUrl) {
return;
}
let ticket = '';
setLoading(true);
appService
.getIntegratedLoginbyName({ sysName })
.then(res => {
if (res.code === 0) {
if (res.data.AccountParam[0].value === 1) {
ticket = ticketData.current;
}
const parma = res.data.AccountParam[0].value === 1 ? ticket : window.globalConfig.token;
if (res.data.siteType === 1) {
// 集成SaaS平台
const userId = window.globalConfig.userInfo.OID;
axios({
method: 'get',
url: `${window.location.origin}${res.data.serviceUrl}?userId=${userId}`,
}).then(resData => {
const url = resData.data.data;
if (res.data.target === 1) {
window.open(url);
} else {
setLinkUrl(url);
}
});
} else {
// 第三方接入
const pingState = ping;
let url = '';
// 外网通
if (pingState) {
if (res.data.InternetAddress) {
url = res.data.InternetAddress;
} else {
url = res.data.IntranetAddress;
}
} else {
if (res.data.IntranetAddress) {
url = res.data.IntranetAddress;
} else {
Message.error('请配置内网地址');
url = res.data.InternetAddress;
return;
}
}
if (url) {
const params = getUrlParam(url);
if (JSON.stringify(params) === '{}') {
url = `${url}?${res.data.AccountParam[0].key}=${parma}`;
} else {
url = `${url}&${res.data.AccountParam[0].key}=${parma}`;
}
}
if (res.data.target === 1) {
window.open(url);
} else {
setLinkUrl(url);
}
}
setLoading(false);
} else {
setLoading(false);
notification.error({
message: '提示',
duration: 3,
description: res.msg,
});
}
})
.catch(error => {});
};
useEffect(() => {
getTicket();
getSiteConfig();
}, []);
return (
<div className={styles.IntegratedLogin}>
{linkUrl !== '' ? (
<Iframe
url={linkUrl}
width="100%"
height="100%"
display="block"
position="relative"
styles={{ border: 'none' }}
/>
) : (
<Spin Spin={loading} tip="加载中..." style={{ width: '100%', height: '100%', marginTop: '300px' }} />
)}
</div>
);
};
export default IntegratedLogin;
......@@ -8,7 +8,6 @@
border: none;
}
.oper-wrap {
top: 0;
right: 0;
......@@ -19,13 +18,13 @@
.oper-btn {
cursor: pointer;
color: #FFF;
color: #fff;
position: absolute;
top: 10px;
right: 15px;
transition: right ease-in-out 0.8s;
}
.btn-fullscreen_exit {
display: block;
width: 1.2rem;
......@@ -40,5 +39,8 @@
}
}
}
}
\ No newline at end of file
}
.IntegratedLogin {
width: 100%;
height: 100%;
}
/*
* @Author: 634665781 634665781@qq.com
* @Date: 2022-07-08 14:28:01
* @LastEditors: Please set LastEditors
* @LastEditTime: 2023-08-22 11:36:58
* @LastEditors: 634665781 634665781@qq.com
* @LastEditTime: 2023-08-23 17:56:41
* @FilePath: \CivWeb\src\pages\user\login\index.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -33,9 +33,10 @@ import DazuLogin from './template/project/dazu';
import WeixinLogin from './template/project/weixin';
import PanoramaLogin from './template/panorama';
import JiangXi from './template/project/jiangxi';
import JiangXiNew from './template/project/jiangxiNew';
import Queshan from './template/project/queshan';
import Queshansl from './template/project/queshansl';
import PiZhouLogin from './template/project/pizhou';
import { AppInitState } from '../../../render';
const LoginTemplate = {
'新春.html': baseLoginNewYear,
......@@ -64,8 +65,10 @@ const LoginTemplate = {
'项目 - 威信.html': WeixinLogin,
'全景图.html': PanoramaLogin,
'项目 - 江西.html': JiangXi,
'项目 - 江西轮播.html': JiangXiNew,
'项目 - 长水机场.html': ChangShuiJiChang,
'项目 - 确山.html': Queshan,
'项目 - 确山水利.html': Queshansl,
'项目 - 邳州.html': PiZhouLogin,
default: BaseLogin,
};
......
......@@ -137,12 +137,13 @@ class Login {
});
}
init() {
init(site) {
const self = this;
this.loginFailed = false;
this.guid = Math.round(Math.random() * 10000 + Date.now()).toString(16);
this.redirect_state = Cookies.get('redirect_state');
const token = this.globalConfig.initToken || Cookies.get('token') || params.getParams('token');
const noTokenSite = site ?? null;
let { ddCode } = this.globalConfig;
let { loginName } = this.globalConfig;
let { password } = this.globalConfig;
......@@ -170,7 +171,7 @@ class Login {
if (loginMode && loginMode === 'iotWechat') ddCode = null;
// self.qrcodeLogin(self.globalConfig.qrcodeData.code);
if (token) {
self.getToweb(token);
self.getToweb(token, noTokenSite);
// eslint-disable-next-line no-empty
} else if (ddCode && loginMode === 'dingding') {
self.ddLoginIn(ddCode);
......@@ -192,14 +193,14 @@ class Login {
}
}
getToweb(token) {
getToweb(token, noTokenSite) {
this.globalConfig.token = token;
const self = this;
// eslint-disable-next-line no-undef
getUserInfo({
token: this.globalConfig.token,
subOID: 'subOID',
site: this.getLocalSiteBytoken(token),
site: noTokenSite ?? this.getLocalSiteBytoken(token),
'request.preventCache': Date.now(),
ignoreSite: true,
})
......@@ -284,7 +285,52 @@ class Login {
this.history.push('/industry');
return false;
}
this.getWebConfig(token, getIndustry);
this.initGateWay(token, getIndustry);
}
initGateWay(token, getIndustry) {
const self = this;
const gateWayParam = {
_site: this?.globalConfig?.userInfo?.site ?? '',
};
appService
.getWateWayConfig(gateWayParam)
.then(res => {
// eslint-disable-next-line prettier/prettier, no-undef
const hasGateWay =!res || !res.data? false: _.isString(res.data)? JSON.parse(res.data): typeof res.data === 'boolean'? res.data: false;
self.globalConfig.hasGateWay = hasGateWay;
self.globalConfig.apiGatewayDomain = `${window.location.origin}${hasGateWay ? '/PandaCore/GateWay' : ''}`;
if (hasGateWay) {
appService
.authorizationToken({
loginName: self.globalConfig.userInfo?.loginName || '',
type: 'WorkNo',
})
.then(tokenRes => {
if (tokenRes.code === 0) {
self.globalConfig.access_token = tokenRes.data?.access_token ?? '';
localStorage.setItem('access_token', self.globalConfig.access_token);
}
self.updateConfig(self.globalConfig);
})
.then(() => {
// eslint-disable-next-line no-use-before-define
this.getWebConfig(token, getIndustry);
})
.catch(err => {
self.updateConfig(self.globalConfig);
});
} else {
self.globalConfig.access_token = null;
localStorage.setItem('access_token', self.globalConfig.access_token);
self.updateConfig(self.globalConfig);
this.getWebConfig(token, getIndustry);
}
})
.catch(err => {
// eslint-disable-next-line no-use-before-define
this.getWebConfig(token, getIndustry);
});
}
getWebConfig(token, getIndustry) {
......@@ -322,7 +368,9 @@ class Login {
const data = res.data[0];
mqttConfig.ipConfig = data;
mqttConfig.mqtt_IsSSL = data.IsSSL ? data.IsSSL : false;
mqttConfig.mqtt_mess.site_code = mqttConfig.mqtt_site_code = data.SiteCode || self.globalConfig.userInfo.site;
// eslint-disable-next-line no-multi-assign
mqttConfig.mqtt_mess.site_code = mqttConfig.mqtt_site_code =
data.SiteCode || self.globalConfig.userInfo.site;
mqttConfig.mqtt_mess.TcpIP = data.TcpIP;
// eslint-disable-next-line radix
mqttConfig.mqtt_mess.TcpPort = data.TcpPort ? parseInt(data.TcpPort) : 8083;
......@@ -368,17 +416,24 @@ class Login {
// 产品类型和首页路径同时有才行
// eslint-disable-next-line prettier/prettier
const homepage = params.getParams('homepage') ? params.getParams('homepage') : homeType && config.homepage ? `${homeType}/${params.getParams('homepage') || config.homepage}` : '';
window.qiankunIsCache = config.isCache || false;
window.qiankunIsCache =
window.location.origin.replace(/^(http|https):\/\//, '') === 'panda-water.cn'
? false
: config.isCache || false;
if (config.productType === 'civweb4') window.qiankunIsCache = false;
const hideFeedback =
config.afterSales && typeof config.afterSales === 'string'
? config.afterSales.split(',').includes(`${self.globalConfig?.userInfo?.OID}`)
: false;
self.globalConfig = Object.assign(self.globalConfig, config, {
theme: self.globalConfig.theme,
menu: self.globalConfig.menu,
style: self.globalConfig.style,
homepage: homepage || '',
products: self.globalConfig.products,
hideFeedback,
// mapsettings: config.mapsettings
});
self.updateConfig && self.updateConfig(self.globalConfig);
self.getProjectItems().then(res => {
window.subSysCfg = {};
self.globalConfig = Object.assign(self.globalConfig, {
......@@ -387,7 +442,6 @@ class Login {
result.forEach(item => {
window.subSysCfg[item.client] = item;
});
if (self.globalConfig.isShared) {
self.globalConfig.mode = 'single';
self.globalConfig.style = 'sandbox';
......@@ -403,7 +457,6 @@ class Login {
self.globalConfig.userInfo.site.length > 0
) {
// eslint-disable-next-line no-undef,no-underscore-dangle
getWebSiteConfig(
{
client: self.globalConfig.client,
......@@ -434,7 +487,6 @@ class Login {
if (mainConf && mainConf.hasOwnProperty('topMenu')) self.globalConfig.topMenu = mainConf.topMenu;
if (mainConf && mainConf.navTheme && self.globalConfig.CloudStyle !== '否')
self.globalConfig.navTheme = mainConf.navTheme; // 云平台统一navTheme
// 云平台切换站点 应用站点自己主题配置
if (config.CloudStyle === '否') {
self.globalConfig.variableTheme = {
......@@ -451,37 +503,12 @@ class Login {
}
// if (mainConf.productType)
// self.globalConfig.productType = mainConf.productType;
self.updateConfig && self.updateConfig(self.globalConfig);
}
if (self.globalConfig.widgets.length === 0) {
message.error({
duration: 3,
content: '当前账号未配置菜单',
});
self.events.emit('loginError', '当前账号未配置菜单');
self.handleLoginError();
return;
}
window.share.event = self.events;
self.callback && self.callback();
getIndustry ? self.events.emit('toggleIndustry') : self.events.emit('loginSuccess', self.history);
this.isOpenYanshi(self, getIndustry);
});
} else {
// loginSuccess
self.updateConfig && self.updateConfig(self.globalConfig);
if (self.globalConfig.widgets.length === 0) {
message.error({
duration: 3,
content: '当前账号未配置菜单',
});
self.events.emit('loginError', '当前账号未配置菜单');
self.handleLoginError();
return;
}
getIndustry ? self.events.emit('toggleIndustry') : self.events.emit('loginSuccess', self.history);
window.share.event = self.events;
self.callback && self.callback();
this.isOpenYanshi(self, getIndustry);
}
});
} else {
......@@ -490,17 +517,57 @@ class Login {
}
this.handleLoginError();
// Logger.log('获取网络配置失败');
console.log('获取网络配置失败');
}
})
.catch(error => {
console.log(error);
// Logger.log(error);
this.handleLoginError();
// Logger.log('获取网络配置失败');
});
}
isOpenYanshi(self, getIndustry) {
appService
.isOpenYanshi()
.then(res => {
if (res.data) {
appService
.getYanshiAuthority({ userId: self?.globalConfig?.userInfo?.OID })
.then(response => {
if (response.data && response.data.length > 0) {
const menu = response.data.map(item => `widget=${item}`);
self.globalConfig.mockMenu = menu;
}
self.getYanshiAuthority(self, getIndustry);
})
.catch(error => {
this.handleLoginError();
});
} else {
self.getYanshiAuthority(self, getIndustry);
}
})
.catch(error => {
self.getYanshiAuthority(self, getIndustry);
});
}
getYanshiAuthority(self, getIndustry) {
self.updateConfig && self.updateConfig(self.globalConfig);
if (self.globalConfig.widgets.length === 0) {
message.error({
duration: 3,
content: '当前账号未配置菜单',
});
self.events.emit('loginError', '当前账号未配置菜单');
self.handleLoginError();
return;
}
getIndustry ? self.events.emit('toggleIndustry') : self.events.emit('loginSuccess', self.history);
window.share.event = self.events;
self.callback && self.callback();
}
getProjectItems() {
let MAX_BOTTOM = 0;
let isScale = false;
......@@ -834,7 +901,6 @@ class Login {
}
self.updateConfig && self.updateConfig(self.globalConfig);
} catch (error) {
console.log(error);
message.error('登录失败');
if (self.goLogin()) {
return false;
......@@ -1133,10 +1199,37 @@ class Login {
.getJiangXiManage({
username: usr,
password: btoa(pwd),
<<<<<<< src/pages/user/login/login.js
'request.preventCache': Date.now(),
})
.then(response => {
self.transformLoginHander({ token: response.data.user_token }, isRememberPWD, pwd);
=======
IsGateWay: window.globalConfig.hasGateWay,
'request.preventCache': Date.now(),
})
.then(response => {
if (response.code === 0) {
if (window.globalConfig?.hasGateWay) {
const data = response.data;
self.updateConfig &&
self.updateConfig(
Object.assign({}, self.globalConfig, {
access_token: data.access_token,
token: data.user_token,
}),
);
self.transformLoginHander(response.data, isRememberPWD, pwd);
} else {
self.transformLoginHander({ token: response.data.user_token }, isRememberPWD, pwd);
}
} else {
self.hasTry = true;
self.handleLoginError();
self.events.emit('loginError', response.msg);
message.error(response.msg);
}
>>>>>>> src/pages/user/login/login.js
})
.catch(error => {
self.hasTry = true;
......
......@@ -23,8 +23,8 @@ const Login = forwardRef((props, _ref) => {
const param = params.getParams('token')?.replaceAll(';', '&');
return param ? decodeURIComponent(escape(param)) : null;
});
const [authCode] = useState(() => {
const param = params.getParams('authCode')?.replaceAll(';', '&');
const [site] = useState(() => {
const param = params.getParams('site')?.replaceAll(';', '&');
return param ? decodeURIComponent(escape(param)) : null;
});
// 需要在 GetGateWay 和 GetConfig 之后再执行登录。避免信息错落乱
......@@ -70,7 +70,7 @@ const Login = forwardRef((props, _ref) => {
action.transformLoginHander(res, false);
});
} else {
action.init();
action.init(site);
}
}
}, [action, action.globalConfig, props.global]);
......
import { Checkbox } from 'antd';
import React, { useRef } from 'react';
import { useIntl } from '@wisdom-utils/components';
import LoginForm from './LoginForm';
import LoginMessage from '../../../js/loginMessage';
/* eslint-disable */
const { UserName, Password, Submit } = LoginForm;
const useAccount = props => {
const formRef = useRef(null);
return (
<LoginForm onSubmit={props.onSubmit} welcome={props.welcome} ref={formRef} form={props.form}>
{props.status === 'error' && props.type === 'account' && !props.submitting && (
<LoginMessage
content={useIntl().formatMessage({
id: 'pages.login.accountLogin.errorMessage',
})}
/>
)}
<UserName
name="userName"
placeholder={useIntl().formatMessage({
id: 'pages.login.username.placeholder',
})}
rules={[
{
required: true,
message: useIntl().formatMessage({
id: 'pages.login.username.required',
}),
},
]}
/>
<Password
name="password"
placeholder={useIntl().formatMessage({
id: 'pages.login.password.placeholder',
})}
rules={[
{
required: true,
message: useIntl().formatMessage({
id: 'pages.login.password.required',
}),
},
]}
/>
<div>
<Checkbox checked={props.autoLogin} onChange={e => props.setAutoLogin(e.target.checked)}>
{useIntl().formatMessage({ id: 'pages.login.rememberMe' })}
</Checkbox>
</div>
<Submit loading={props.submitting}>{useIntl().formatMessage({ id: 'pages.login.submit' })}</Submit>
</LoginForm>
);
};
export default useAccount;
import React, { forwardRef, useImperativeHandle, useEffect } from 'react';
import { Form } from 'antd';
import classNames from 'classnames';
import { useIntl } from '@wisdom-utils/components';
import styles from '../../../components/Login/index.less';
import LoginContext from '../../../components/Login/LoginContext';
import LoginItem from '../../../components/Login/LoginItem';
import LoginSubmit from '../../../components/Login/LoginSubmit';
const Login = (props, ref) => {
const intl = useIntl();
const { className } = props;
const otherChildren = [];
React.Children.forEach(props.children, child => {
if (!child) {
return;
}
otherChildren.push(child);
});
return (
<LoginContext.Provider value={{}}>
<div className={classNames(className, styles.login)}>
{props.welcome === void 0 ? (
<div className={styles.desc}>{intl.formatMessage({ id: 'pages.login.welcome' })}</div>
) : (
props.welcome
)}
<Form
form={props.form}
onFinish={values => {
if (props.onSubmit) {
props.onSubmit(values);
}
}}
>
{props.children}
</Form>
</div>
</LoginContext.Provider>
);
};
Login.Submit = LoginSubmit;
Login.UserName = LoginItem.UserName;
Login.Password = LoginItem.Password;
Login.NewYearPassword = LoginItem.NewYearPassword;
Login.Mobile = LoginItem.Mobile;
Login.Captcha = LoginItem.Captcha;
Login.NewYearMobile = LoginItem.NewYearMobile;
Login.NewYearCaptcha = LoginItem.NewYearCaptcha;
export default Login;
import React, { forwardRef, useEffect, useRef, useState } from 'react';
import { Modal, Popover } from 'antd';
import { Modal, Popover, Form, Tooltip } from 'antd';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import { connect } from 'react-redux';
import { useHistory, withRouter } from '@wisdom-utils/runtime';
......@@ -12,6 +12,7 @@ import defaultSetting from '../../../../../../../config/defaultSetting';
import LoginAction from '../../../login';
import styles from './index.less';
import Account from '../../../js/useAccount';
import FormLogin from './Account';
import { defaultApp } from '../../../../../../micro';
import logoPng from './images/8.png';
import qrcodePng from './images/1.png';
......@@ -26,6 +27,7 @@ const PopOvercontent = () => {
};
const Login = forwardRef((props, _ref) => {
const [form] = Form.useForm();
const sliVerify = useRef();
const loginFormRef = useRef();
const formRef = useRef(null);
......@@ -38,12 +40,12 @@ const Login = forwardRef((props, _ref) => {
const history = useHistory();
const [action, setAction] = useState(() => new LoginAction(Object.assign({}, props, { history }), setVisible, false));
const [dateObj, setDateObj] = useState({});
const [loginType, setLoginType] = useState('4a平台');
const [loginType, setLoginType] = useState('OA账号');
const handleSubmit = values => {
/* eslint-disable */
action &&
(type === 'Account'
? loginType === '4a平台'
? loginType === 'OA账号'
? action.loginHandlerJiangXi(values.userName, values.password, null, autoLogin, sliVerify)
: action.loginHandler(values.userName, values.password, null, autoLogin, sliVerify)
: type === 'Mobile'
......@@ -87,6 +89,7 @@ const Login = forwardRef((props, _ref) => {
const renderPlatform = () => {
const template = props.global.loginTemplate;
const params = {
form: form,
fromRef: formRef,
type,
setType,
......@@ -100,7 +103,8 @@ const Login = forwardRef((props, _ref) => {
updateLoginMode: props.updateLoginMode,
welcome: null,
};
return <Account {...params} />;
// return <Account {...params} />;
return <FormLogin {...params} />;
};
/* eslint-disable */
......@@ -150,6 +154,10 @@ const Login = forwardRef((props, _ref) => {
const toLink = () => {
window.open('http://gis.panda-water.cn/jiangxi/index.html');
};
const tabChange = key => {
form.resetFields();
setLoginType(key);
};
return (
<HelmetProvider>
<Helmet>
......@@ -159,22 +167,30 @@ const Login = forwardRef((props, _ref) => {
<div className={classnames(styles.lvchenglogin, 'lvcheng')}>
<div className={styles['inner-wrapper']}>
<div className={styles['title-image-box']}>
<img src={logoPng} alt="" className={styles['title-image']} />
<span className={styles['title-name']}>江西省水务集团有限公司 - 智慧水务平台</span>
<img
src={
props.global &&
props.global.transformDevAssetsBaseURL &&
props.global.transformDevAssetsBaseURL(props.global.logo)
}
alt=""
className={styles['title-image']}
/>
<span className={styles['title-name']}>{props.global.title}</span>
</div>
<div className={styles['inner-center']}>
<div className={styles['welcome-title']}>
<div
className={classnames(styles.tabItem, { [styles.tabActive]: loginType === '4a平台' })}
onClick={() => setLoginType('4a平台')}
className={classnames(styles.tabItem, { [styles.tabActive]: loginType === 'OA账号' })}
onClick={() => tabChange('OA账号')}
>
4a平台
OA账号
</div>
<div
className={classnames(styles.tabItem, { [styles.tabActive]: loginType === '熊猫平台' })}
onClick={() => setLoginType('熊猫平台')}
className={classnames(styles.tabItem, { [styles.tabActive]: loginType === '平台账号' })}
onClick={() => tabChange('平台账号')}
>
熊猫平台
平台账号
</div>
</div>
<div className={classnames(styles['inner-bg'], styles['login-part'])} ref={loginFormRef}>
......@@ -198,10 +214,13 @@ const Login = forwardRef((props, _ref) => {
</div>
</div>
</div>
<div className={styles.linkBox} onClick={toLink}>
<img src={require('./images/9.png')} alt="" />
APP下载
</div>
<Tooltip title="打开网站下载">
<div className={styles.linkBox} onClick={toLink}>
<img src={require('./images/9.png')} alt="" />
APP下载
</div>
</Tooltip>
<Modal centered visible={visible} width={340} footer={null} closable={false} bodyStyle={{ padding: '15px' }}>
<div ref={sliVerify} />
</Modal>
......
.lvchenglogin {
width: 100%;
height: 100%;
......@@ -44,13 +43,12 @@
}
.title-image {
width: 59px;
width: 75px;
margin-right: 20px;
}
.title-name {
font-size: 36px;
font-weight: 400;
color: #FFFFFF;
text-shadow: 0px 1px 5px rgba(6, 27, 54, 0.8);
......
import { Checkbox } from 'antd';
import React, { useRef } from 'react';
import { useIntl } from '@wisdom-utils/components';
import LoginForm from './LoginForm';
import LoginMessage from '../../../js/loginMessage';
/* eslint-disable */
const { UserName, Password, Submit } = LoginForm;
const useAccount = props => {
const formRef = useRef(null);
return (
<LoginForm onSubmit={props.onSubmit} welcome={props.welcome} ref={formRef} form={props.form}>
{props.status === 'error' && props.type === 'account' && !props.submitting && (
<LoginMessage
content={useIntl().formatMessage({
id: 'pages.login.accountLogin.errorMessage',
})}
/>
)}
<UserName
name="userName"
placeholder={useIntl().formatMessage({
id: 'pages.login.username.placeholder',
})}
rules={[
{
required: true,
message: useIntl().formatMessage({
id: 'pages.login.username.required',
}),
},
]}
/>
<Password
name="password"
placeholder={useIntl().formatMessage({
id: 'pages.login.password.placeholder',
})}
rules={[
{
required: true,
message: useIntl().formatMessage({
id: 'pages.login.password.required',
}),
},
]}
/>
<div>
<Checkbox checked={props.autoLogin} onChange={e => props.setAutoLogin(e.target.checked)}>
{useIntl().formatMessage({ id: 'pages.login.rememberMe' })}
</Checkbox>
</div>
<Submit loading={props.submitting}>{useIntl().formatMessage({ id: 'pages.login.submit' })}</Submit>
</LoginForm>
);
};
export default useAccount;
import React, { forwardRef, useImperativeHandle, useEffect } from 'react';
import { Form } from 'antd';
import classNames from 'classnames';
import { useIntl } from '@wisdom-utils/components';
import styles from '../../../components/Login/index.less';
import LoginContext from '../../../components/Login/LoginContext';
import LoginItem from '../../../components/Login/LoginItem';
import LoginSubmit from '../../../components/Login/LoginSubmit';
const Login = (props, ref) => {
const intl = useIntl();
const { className } = props;
const otherChildren = [];
React.Children.forEach(props.children, child => {
if (!child) {
return;
}
otherChildren.push(child);
});
return (
<LoginContext.Provider value={{}}>
<div className={classNames(className, styles.login)}>
{props.welcome === void 0 ? (
<div className={styles.desc}>{intl.formatMessage({ id: 'pages.login.welcome' })}</div>
) : (
props.welcome
)}
<Form
form={props.form}
onFinish={values => {
if (props.onSubmit) {
props.onSubmit(values);
}
}}
>
{props.children}
</Form>
</div>
</LoginContext.Provider>
);
};
Login.Submit = LoginSubmit;
Login.UserName = LoginItem.UserName;
Login.Password = LoginItem.Password;
Login.NewYearPassword = LoginItem.NewYearPassword;
Login.Mobile = LoginItem.Mobile;
Login.Captcha = LoginItem.Captcha;
Login.NewYearMobile = LoginItem.NewYearMobile;
Login.NewYearCaptcha = LoginItem.NewYearCaptcha;
export default Login;
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
import React, { forwardRef, useEffect, useRef, useState } from 'react';
import Zoom from 'react-reveal/Zoom';
import Fade from 'react-reveal/Fade';
import Slide from 'react-reveal/Slide';
import Roll from 'react-reveal/Roll';
import Pulse from 'react-reveal/Pulse';
import QueueAnim from 'rc-queue-anim';
import { Modal, Popover, Form, Tooltip } from 'antd';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import { connect } from 'react-redux';
import { useHistory, withRouter } from '@wisdom-utils/runtime';
import { actionCreators } from '@/containers/App/store';
import moment from 'moment';
import classnames from 'classnames';
import QRCode from 'qrcode.react';
import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { Autoplay } from 'swiper/core';
import $ from 'jquery';
import defaultSetting from '../../../../../../../config/defaultSetting';
import LoginAction from '../../../login';
import styles from './index.less';
import FormLogin from './Account';
import { defaultApp } from '../../../../../../micro';
import logoPng from './images/8.png';
import qrcodePng from './images/1.png';
import welcomePng from './images/4.png';
// import SwiperCore, { Autoplay, Navigation } from 'swiper/core';
// SwiperCore.use([Autoplay, Navigation]);
import 湖口润泉水源地 from './images/江西水投子公司图片/湖口润泉水源地.png';
import 会昌润泉石壁坑水库 from './images/江西水投子公司图片/会昌润泉石壁坑水库.png';
import 景德镇水务公司 from './images/江西水投子公司图片/景德镇水务公司.png';
import 景德镇水务新洋湖水厂 from './images/江西水投子公司图片/景德镇水务新洋湖水厂.png';
import 乐平润泉水务公司 from './images/江西水投子公司图片/乐平润泉水务公司.png';
import 南城润泉麻源水库1 from './images/江西水投子公司图片/南城润泉麻源水库1.png';
import 南城润泉麻源水库2 from './images/江西水投子公司图片/南城润泉麻源水库2.png';
import 南城润泉麻源水库3 from './images/江西水投子公司图片/南城润泉麻源水库3.png';
import 南康润泉龙华水厂 from './images/江西水投子公司图片/南康润泉龙华水厂.png';
import 南康润泉龙华水厂2 from './images/江西水投子公司图片/南康润泉龙华水厂2.png';
import 上高润泉第二水厂 from './images/江西水投子公司图片/上高润泉第二水厂.png';
import 上高润泉第二水厂平流沉淀池 from './images/江西水投子公司图片/上高润泉第二水厂平流沉淀池.png';
import 上高润泉水务公司 from './images/江西水投子公司图片/上高润泉水务公司.png';
import 上高润泉水务公司2 from './images/江西水投子公司图片/上高润泉水务公司2.png';
import 寻乌润泉大路水厂 from './images/江西水投子公司图片/寻乌润泉大路水厂.png';
import 永修润泉湖东水厂 from './images/江西水投子公司图片/永修润泉湖东水厂.png';
import 'swiper/swiper-bundle.css';
const Cripples = require('@/assets/js/ripples/jquery.ripples');
SwiperCore.use([Autoplay]);
const PopOvercontent = () => {
const qrcodes = window.globalConfig && window.globalConfig.qrcode;
if (qrcodes) {
return <QRCode value={qrcodes} />;
}
return <span>手持APP下载未配置</span>;
};
const Login = forwardRef((props, _ref) => {
const [form] = Form.useForm();
const sliVerify = useRef();
const loginFormRef = useRef();
const timeRef = useRef();
const formRef = useRef(null);
const [status, setStatus] = useState('normal');
const [num, setNum] = useState(0);
const [autoLogin, setAutoLogin] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [type, setType] = useState('Account');
const [visible, setVisible] = useState(false);
const history = useHistory();
const [action, setAction] = useState(() => new LoginAction(Object.assign({}, props, { history }), setVisible, false));
const [dateObj, setDateObj] = useState({});
const [loginType, setLoginType] = useState('OA账号');
const [img, setImg] = useState(
[
'湖口润泉水源地',
'会昌润泉石壁坑水库',
'景德镇水务公司',
'景德镇水务新洋湖水厂',
'乐平润泉水务公司',
'南城润泉麻源水库1',
'南城润泉麻源水库2',
'南城润泉麻源水库3',
'南康润泉龙华水厂',
'南康润泉龙华水厂2',
// '上高润泉第二水厂',
'上高润泉第二水厂平流沉淀池',
'上高润泉水务公司',
// '上高润泉水务公司2',
'寻乌润泉大路水厂',
// '永修润泉湖东水厂',
].sort(() => 0.5 - Math.random()),
);
const [imgObj, setimgObj] = useState({
湖口润泉水源地,
会昌润泉石壁坑水库,
景德镇水务公司,
景德镇水务新洋湖水厂,
乐平润泉水务公司,
南城润泉麻源水库1,
南城润泉麻源水库2,
南城润泉麻源水库3,
南康润泉龙华水厂,
南康润泉龙华水厂2,
// 上高润泉第二水厂,
上高润泉第二水厂平流沉淀池,
上高润泉水务公司,
// 上高润泉水务公司2,
寻乌润泉大路水厂,
// 永修润泉湖东水厂,
});
const handleSubmit = values => {
/* eslint-disable */
action &&
(type === 'Account'
? loginType === 'OA账号'
? action.loginHandlerJiangXi(values.userName, values.password, null, autoLogin, sliVerify)
: action.loginHandler(values.userName, values.password, null, autoLogin, sliVerify)
: type === 'Mobile'
? action.phoneLoginFormHandler(values.mobile, values.captcha)
: null);
setSubmitting(true);
props.updateCurrentIndex && props.updateCurrentIndex(-1);
};
useEffect(() => {
// setTimeout(() => {
// onShow();
// }, 300);
setTimeout(() => {
$('.CarouselRipples').ripples({
resolution: 800,
dropRadius: 20, //px
perturbance: 2,
});
}, 1000);
}, []);
useEffect(() => {
action &&
action.events.on('loginSuccess', event => {
setSubmitting(false);
props.updateCurrentIndex && props.updateCurrentIndex(0);
props.history.push(`/?client=${props.global.client}`);
// window.share.event.emit('triggerMicro', props.global);
// initMicroApps();
defaultApp();
});
action &&
action.events.on('loginError', event => {
setVisible(false);
setSubmitting(false);
});
action &&
action.events.on('loginVisible', status => {
setVisible(status);
});
return () => {
action && action.events && action.events.removeAllListeners('loginSuccess');
action && action.events && action.events.removeAllListeners('loginError');
action && action.events && action.events.removeAllListeners('loginVisible');
};
}, [props.loginMode]);
useEffect(() => {
setSubmitting(false);
}, [visible]);
const renderPlatform = () => {
const template = props.global.loginTemplate;
const params = {
form: form,
fromRef: formRef,
type,
setType,
status,
submitting,
autoLogin,
setAutoLogin,
action,
onSubmit: handleSubmit,
loginMode: props.loginMode,
updateLoginMode: props.updateLoginMode,
welcome: null,
};
// return <Account {...params} />;
return <FormLogin {...params} />;
};
/* eslint-disable */
const handleWeek = () => {
const weekOfDay = Number(moment().format('E'));
let weekDayName = '';
switch (weekOfDay) {
case 1:
weekDayName = '周一';
break;
case 2:
weekDayName = '周二';
break;
case 3:
weekDayName = '周三';
break;
case 4:
weekDayName = '周四';
break;
case 5:
weekDayName = '周五';
break;
case 6:
weekDayName = '周六';
break;
case 0:
weekDayName = '周日';
break;
default:
weekDayName = '';
}
return weekDayName;
};
useEffect(() => {
const timer = setInterval(() => {
setDateObj({
curTime: moment().format('HH:mm:ss'),
week: handleWeek(),
date: moment().format('YYYY/MM/DD'),
});
}, 1000);
return () => {
clearInterval(timer);
// clearInterval(timer1)
};
}, []);
useEffect(() => {
if (timeRef.current) clearTimeout(timeRef.current);
timeRef.current = setTimeout(() => {
if (num < 12) {
setNum(num + 1);
} else {
setNum(0);
}
}, 5000);
}, [num]);
const toLink = () => {
window.open('http://gis.panda-water.cn/jiangxi/index.html');
};
const tabChange = key => {
form.resetFields();
setLoginType(key);
};
const getBJ = (url) => {
return (
<div
style={{ background: `url(${url}) no-repeat`, transition: 'background-image 1s' }}
className={classnames(styles.lvchenglogin, 'lvcheng')}
>
<div className={styles['inner-wrapper']}>
<div className={styles['title-image-box']}>
<img
src={
props.global &&
props.global.transformDevAssetsBaseURL &&
props.global.transformDevAssetsBaseURL(props.global.logo)
}
alt=""
className={styles['title-image']}
/>
<span className={styles['title-name']}>{props.global.title}</span>
</div>
<QueueAnim type="scale" duration={1000}>
<div key={'innerwrapper'} style={{position: 'relative',zIndex:2,backdropFilter: 'blur(6px)'}} className={styles['inner-center']}>
<Tooltip title="打开网站下载">
<div className={styles.linkBox} onClick={toLink}>
<img style={{ width: '14px', marginRight: '4px' }} src={require('./images/下载.png')} alt="" />
点击下载APP
</div>
</Tooltip>
<div className={styles['welcome-title']}>
<div
className={classnames(styles.tabItem, { [styles.tabActive]: loginType === 'OA账号' })}
onClick={() => tabChange('OA账号')}
>
OA账号
</div>
<div
className={classnames(styles.tabItem, { [styles.tabActive]: loginType === '平台账号' })}
onClick={() => tabChange('平台账号')}
>
平台账号
</div>
</div>
<div className={classnames(styles['inner-bg'], styles['login-part'])} ref={loginFormRef}>
{renderPlatform()}
</div>
{/* <Popover content={PopOvercontent} title="扫码下载APP" overlayClassName={'popover-style'}>
<img src={qrcodePng} alt="APP" className={styles['qrcode-box']} />
</Popover> */}
</div>
</QueueAnim>
</div>
<div className={styles['login-header']}>
{/* <div className={styles['left-title']}>
<img src={logoPng} alt='logo' className={styles['logo-png']}/>
<div className={styles['cn-title']}>南宁市二次供水管理平台</div>
</div> */}
<div className={styles['right-timebox']}>
<div className={styles['curr-time']}>{dateObj.curTime}</div>
<div className={styles['curr-week-date']}>
<div className={styles['curr-week']}>{dateObj.week}</div>
<div className={styles['curr-date']}>{dateObj.date}</div>
</div>
</div>
</div>
<div className={classnames(styles.CarouselRipples, 'CarouselRipples')} data-ripple="ripple" />
<Modal centered visible={visible} width={340} footer={null} closable={false} bodyStyle={{ padding: '15px' }}>
<div ref={sliVerify} />
</Modal>
<div className={styles['lbt']}>
<div
onClick={() => {
if (num > 0) {
setNum(num - 1);
} else {
setNum(12);
}
}}
>
{' '}
<img style={{ width: '30px', cursor: 'pointer' }} src={require('./images/左.png')} alt="" />
</div>
<div className={styles['divImgAll']}>
<Swiper
autoplay={{ delay: 5000 }}
loop
spaceBetween={1}
slidesPerView={12}
onSlideChange={() => console.log('slide change')}
onSwiper={swiper => console.log(swiper)}
style={{ width: 'calc(100% - 340px)', height: '100%' }}
>
{img.map((v, i) => {
return (
<SwiperSlide>
<div
onClick={() => {
setNum(i);
}}
className={styles['divT']}
style={{ cursor: 'pointer', position: 'relative' }}
>
<img
title={v}
style={{
border: i == num ? '2px solid #FFDFA0' : '0px',
width: i == num ? '82px' : '82px',
height: i == num ? '82px' : '82px',
}}
src={require(`./images/江西水投子公司图片/${v}.png`)}
alt=""
/>
<div
style={{
position: 'absolute',
overflow: 'hidden',
whiteSpace: 'nowrap',
color: 'white',
textOverflow: 'ellipsis',
top: '61px',
width: '82px',
fontSize: '12px',
textAlign: 'center',
background: 'rgba(0,0,0,0.4)',
// opacity: 0.4
}}
>
{v.indexOf('景德镇')>-1?'景德镇':v.substring(0, 4)}
</div>
</div>
</SwiperSlide>
);
})}
</Swiper>
</div>
<div
onClick={() => {
if (num < 12) {
setNum(num + 1);
} else {
setNum(0);
}
}}
>
{' '}
<img style={{ width: '30px', cursor: 'pointer' }} src={require('./images/右.png')} alt="" />
</div>
</div>
</div>
);
};
return (
<HelmetProvider>
<Helmet>
<title>{props.global.title || defaultSetting.title}</title>
<meta name="description" content={props.global.title || defaultSetting.title} />
</Helmet>
{getBJ(imgObj[img[num]])}
</HelmetProvider>
);
});
const mapStateToProps = state => ({
global: state.getIn(['global', 'globalConfig']),
loginMode: state.getIn(['global', 'loginMode']),
});
const mapDispatchToProps = dispatch => ({
updateConfig(config) {
dispatch(actionCreators.getConfig(config));
},
createContext(data) {
dispatch(actionCreators.createContext(data));
},
updateLoginMode(mode) {
dispatch(actionCreators.changeLoginMode(mode));
},
updateCurrentIndex(index) {
dispatch(actionCreators.updateCurrentIndex(index));
},
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(withRouter(Login));
\ No newline at end of file
@font-face {
font-family: PingFang SC;
src: url('@/assets/fonts/PingFang.ttf');
}
.lvchenglogin {
width: 100%;
height: 100%;
background-size: 100% 100%;
background-clip: border-box;
position: relative;
min-height: 7.0rem;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
.lbt{
width: 100vw;
height:108px;
background-color: rgba(0,0,0,0.4);
// border: 1px solid #000000;
/// opacity: 0.6;
position: absolute;
/* top: 10px; */
bottom: 89px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 70px;
}
.divImgAll{
width: calc(100% - 44px);
// width: calc(100% - 200px);
// display: flex;
.divT{
// width: 60px;
// height: 60px;
// background-color: white;
margin: 0px 30px;
cursor: 'pointer'
}
}
.inner-bg {
width: 100%;
// height: 100%;
&>div {
width: 70%;
margin: 0 auto;
}
}
.inner-wrapper {
position: relative;
width: 100%;
// height: 380px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 10px;
margin-top: -100px;
// margin-bottom: 100px;
}
.title-image-box {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 160px;
font-size: 44px;
font-family: Alibaba PuHuiTi;
font-weight: 500;
color: #FFFFFF;
line-height: 91px;
// text-shadow: 0px 1px 5px rgba(6,27,54,0.8);
}
.title-image {
width: 75px;
margin-right: 20px;
}
.title-name {
font-size: 36px;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
text-shadow: 0px 1px 5px rgba(6, 27, 54, 0.8);
letter-spacing: 2px;
font-family: Alibaba PuHuiTi;
font-weight: 500;
color: #FFFFFF;
line-height: 91px;
text-shadow: 0px 1px 5px rgba(6,27,54,0.8);
}
.inner-bg .title {
font-size: 27px;
font-weight: bold;
color: rgba(255, 255, 255, 1);
letter-spacing: 2px;
}
.form-control:focus {
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: none;
box-shadow: none;
}
.inner-center {
position: relative;
width: 600px;
// height: 316px;
// background: rgba(255,255,255, 0.3);
// border: 1px solid #FFFFFF;
// box-shadow: 0px 15px 15px 3px rgba(140,142,145,0.52);
border-radius: 10px;
background: rgba(255, 255, 255, 0.5);
border-radius: 15px 15px 15px 15px;
box-shadow: 0px 6px 18px 0px rgb(25 61 79 / 35%);
padding: 20px 0;
top: -57px;
height: 360px;
padding-top: 10px;
}
.welcome-title {
width: 100%;
height: 50px;
// background: linear-gradient(0deg, rgba(9, 128, 238, 1) 0%, rgba(0, 182, 251, 1) 100%);
border-radius: 10px 10px 0 0;
padding: 16px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 25px;
.tabItem {
padding: 5px 15px;
cursor: pointer;
text-align: center;
font-size: 20px;
font-weight: bold;
// color: white;
color: #838383;
margin-left: 15px;
}
.tabActive {
border-bottom: 3px solid #1E77D3;
color: #080A10;
}
}
.welcome-img {
width: 480px;
}
.formgroup2 {
display: flex;
align-items: center;
display: flex;
margin: 0px 5.5%;
margin-bottom: 10%;
align-items: center;
margin-bottom: 40px;
}
.APPcodeBox {
width: 100%;
display: flex;
flex-flow: column;
align-items: center;
cursor: pointer;
position: relative;
}
.APPCtext {
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #000000;
line-height: 30px;
opacity: 0.75;
}
.login-header {
width: 100%;
position: absolute;
top: 0;
height: 90px;
padding: 0 70px;
display: flex;
justify-content: flex-end;
align-items: center;
// box-shadow: 0px 1px 3px 0px rgba(235, 235, 235, 0.55);
.left-title {
display: flex;
justify-content: center;
align-items: center;
.cn-title {
font-size: 28px;
font-family: PingFang SC;
font-weight: 400;
color: #FFFFFF;
}
.logo-png {
width: 54px;
margin-right: 20px;
}
}
.right-timebox {
display: flex;
justify-content: center;
align-items: center;
height: 60px;
.curr-time {
width: 154px;
font-size: 36px;
// font-family: Microsoft YaHei;
font-weight: 300;
color:white;
font-family: Alibaba PuHuiTi;
font-weight: 500;
color: #FFFFFF;
// line-height: 91px;
text-shadow: 0px 1px 5px rgba(6,27,54,0.8);
}
.curr-week-date {
margin-left: 10px;
.curr-week {
font-size: 16px;
// font-family: Microsoft YaHei;
font-weight: 400;
color:white;
color:white;
font-family: Alibaba PuHuiTi;
font-weight: 500;
color: #FFFFFF;
// line-height: 91px;
text-shadow: 0px 1px 5px rgba(6,27,54,0.8);
}
.curr-date {
font-size: 14px;
// font-family: Microsoft YaHei;
font-weight: 400;
color:white;
color:white;
font-family: Alibaba PuHuiTi;
font-weight: 500;
color: #FFFFFF;
// line-height: 91px;
text-shadow: 0px 1px 5px rgba(6,27,54,0.8);
}
}
}
}
.copyright {
position: absolute;
bottom: 7%;
width: 100%;
margin: 0 auto;
font-size: 12px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #F1F6FD;
text-align: center;
}
.qrcode-box {
position: absolute;
bottom: 0;
right: -23px;
width: 23px;
height: 23px;
background: #FFFFFF;
opacity: 0.8;
cursor: pointer;
}
.linkBox {
position: absolute;
flex-direction: column;
align-items: center;
left: 50%;
transform: translateX(-50%);
bottom: 100px;
color: #108CEE;
font-size: 15px;
cursor: pointer;
top: 307px;
img {
width: 35px;
margin-bottom: 5px;
}
}
}
:global {
.popover-style {
.@{ant-prefix}-popover-inner-content {
display: flex;
justify-content: center;
align-items: center;
}
.@{ant-prefix}-popover-title {
display: flex;
justify-content: center;
align-items: center;
}
}
}
@keyframes waving {
from {
top: 0;
}
to {
top: 10px;
}
}
@keyframes waving2 {
from {
left: 0;
}
to {
left: -9999px;
}
}
.CarouselRipples {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
& :global {
.CarouselRipples:before {
content: '';
display: inline-block;
vertical-align: middle;
height: 100%;
}
}
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
import React, { forwardRef, useEffect, useRef, useState } from 'react';
import { Modal } from 'antd';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import { connect } from 'react-redux';
import { useHistory, withRouter } from '@wisdom-utils/runtime';
import { actionCreators } from '@/containers/App/store';
import QRCode from 'qrcode.react';
import classnames from 'classnames';
import moment from 'moment';
import defaultSetting from '../../../../../../../config/defaultSetting';
import LoginAction from '../../../login';
import styles from './index.less';
import Account from '../../../js/useAccount';
import { defaultApp } from '../../../../../../micro';
import imagelogo from './images/logo@2x.png';
const renderQRCode = () => {
const qrcodes = window.globalConfig && window.globalConfig.qrcode;
console.log('qrcodes', qrcodes);
if (qrcodes) {
return (
<div>
<QRCode value={qrcodes} />
<p>扫码下载APP</p>
</div>
);
}
return <span>手持APP下载未配置</span>;
};
const Login = forwardRef((props, _ref) => {
const sliVerify = useRef();
const loginFormRef = useRef();
const formRef = useRef(null);
const [status, setStatus] = useState('normal');
const [autoLogin, setAutoLogin] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [type, setType] = useState('Account');
const [visible, setVisible] = useState(false);
const history = useHistory();
const [action, setAction] = useState(() => new LoginAction(Object.assign({}, props, { history }), setVisible, false));
const [dateObj, setDateObj] = useState({});
const handleSubmit = values => {
/* eslint-disable */
action &&
(type === 'Account'
? action.loginHandler(values.userName, values.password, null, autoLogin, sliVerify)
: type === 'Mobile'
? action.phoneLoginFormHandler(values.mobile, values.captcha)
: null);
setSubmitting(true);
props.updateCurrentIndex && props.updateCurrentIndex(-1);
};
useEffect(() => {
action &&
action.events.on('loginSuccess', event => {
setSubmitting(false);
props.updateCurrentIndex && props.updateCurrentIndex(0);
props.history.push(`/?client=${props.global.client}`);
// window.share.event.emit('triggerMicro', props.global);
// initMicroApps();
defaultApp();
});
action &&
action.events.on('loginError', event => {
setVisible(false);
setSubmitting(false);
});
action &&
action.events.on('loginVisible', status => {
setVisible(status);
});
return () => {
action && action.events && action.events.removeAllListeners('loginSuccess');
action && action.events && action.events.removeAllListeners('loginError');
action && action.events && action.events.removeAllListeners('loginVisible');
};
}, [props.loginMode]);
useEffect(() => {
setSubmitting(false);
}, [visible]);
const renderPlatform = () => {
const template = props.global.loginTemplate;
const params = {
fromRef: formRef,
type,
setType,
status,
submitting,
autoLogin,
setAutoLogin,
action,
onSubmit: handleSubmit,
loginMode: props.loginMode,
updateLoginMode: props.updateLoginMode,
welcome: null,
};
return <Account {...params} />;
};
/* eslint-disable */
const handleWeek = () => {
const weekOfDay = Number(moment().format('E'));
let weekDayName = ''
switch (weekOfDay) {
case 1:
weekDayName = '周一'
break
case 2:
weekDayName = '周二'
break
case 3:
weekDayName = '周三'
break
case 4:
weekDayName = '周四'
break
case 5:
weekDayName = '周五'
break
case 6:
weekDayName = '周六'
break
case 0:
weekDayName = '周日'
break
default:
weekDayName = ''
}
return weekDayName
}
useEffect(() => {
const timer = setInterval(() => {
setDateObj({
curTime: moment().format('HH:mm:ss'),
week: handleWeek(),
date: moment().format('YYYY/MM/DD')
});
}, 1000);
return () => {
clearInterval(timer);
};
}, [])
return (
<HelmetProvider>
<Helmet>
<title>{props.global.title || defaultSetting.title}</title>
<meta name="description" content={props.global.title || defaultSetting.title} />
</Helmet>
<div className={classnames(styles.login, 'WXlogin')}>
<div className={styles['inner-wrapper']}>
<div className={styles['left-imgbox']}></div>
<div className={styles['inner-center']}>
<div className={styles['welcome-title']}></div>
<div className={classnames(styles['inner-bg'], styles['login-part'])} ref={loginFormRef}>
{renderPlatform()}
</div>
{/* <div className={styles.WXqrcodeBox}>
<p className={styles.WXimg}></p>
<div className={styles.WXbigImg}>{renderQRCode()}</div>
</div> */}
</div>
</div>
<div className={styles['login-header']}>
<div className={styles['left-title']}>
{/* <p className={styles.WXlogoImg}></p> */}
<img className={styles.WXlogoImg} src={imagelogo} ></img>
{/* <div className={styles['cn-title']}>威信县供排水公司</div> */}
</div>
<div className={styles['right-timebox']}>
<div className={styles['curr-time']}>{dateObj.curTime}</div>
<div className={styles['curr-week-date']}>
<div className={styles['curr-week']}>{dateObj.week}</div>
<div className={styles['curr-date']}>{dateObj.date}</div>
</div>
</div>
</div>
<Modal centered visible={visible} width={340} footer={null} closable={false} bodyStyle={{ padding: '15px' }}>
<div ref={sliVerify} />
</Modal>
</div>
</HelmetProvider>
);
});
const mapStateToProps = state => ({
global: state.getIn(['global', 'globalConfig']),
loginMode: state.getIn(['global', 'loginMode']),
});
const mapDispatchToProps = dispatch => ({
updateConfig(config) {
dispatch(actionCreators.getConfig(config));
},
createContext(data) {
dispatch(actionCreators.createContext(data));
},
updateLoginMode(mode) {
dispatch(actionCreators.changeLoginMode(mode));
},
updateCurrentIndex(index) {
dispatch(actionCreators.updateCurrentIndex(index));
},
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(withRouter(Login));
:global {
.WXlogin {
.@{ant-prefix}-input-affix-wrapper {
border: none;
border-bottom: 1px solid #D1DCEB;
}
.@{ant-prefix}-input-prefix {
margin-right: 16px;
}
.panda-console-basesrc-pages-user-login-components-login-index-prefixIcon {
font-size: 18px;
}
.panda-console-basesrc-pages-user-login-components-login-index-submit {
border-radius: 20px;
width: 200px;
margin-top: 30px;
}
.panda-console-base-form-item-control-input-content {
text-align: center;
}
}
}
.login {
width: 100%;
height: 100%;
background: url('./images/登陆背景图.png') no-repeat center;
background-size: 100% 100%;
background-clip: border-box;
position: relative;
min-height: 7.0rem;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.login .inner-bg {
width: 100%;
height: 50%;
&>div {
width: 70%;
margin: 0 auto;
}
}
.inner-wrapper {
width: 1100px;
height: 650px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
background: url('./images/登录框@2x.png') no-repeat center;
background-size: 100% 100%;
background-clip: border-box;
margin-top: 50px;
// position: absolute;
// top: 46%;
// right: 0;
// transform: translateY(-50%);
}
.left-imgbox {
width: 700px;
height: 100%;
}
// .img-wrapper {
// width: 388px;
// height: 134px;
// background: url('./images/1.png') no-repeat center;
// background-size: 100% 100%;
// margin-bottom: 48px;
// }
.login .inner-bg img[role="logo"] {
height: 40px;
}
.login .inner-bg .title {
font-size: 27px;
font-weight: bold;
color: rgba(255, 255, 255, 1);
letter-spacing: 2px;
}
@keyframes waving {
from {
top: 0;
}
to {
top: 10px;
}
}
@keyframes waving2 {
from {
left: 0;
}
to {
left: -9999px;
}
}
.form-control:focus {
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: none;
box-shadow: none;
}
.inner-center {
width: 400px;
height: 82%;
background-color: #ffffff;
display: flex;
flex-flow: column;
align-items: center;
border-radius: 0 10px 10px 0;
position: relative;
}
.WXqrcodeBox {
position: absolute;
background: rgba(255, 255, 255, 0.5);
border-radius: 0px 5px 5px 0px;
padding: 7px;
bottom: 20px;
right: -36px;
}
.WXqrcodeBox:hover {
right: -142px;
}
.WXqrcodeBox:hover .WXimg {
display: none;
}
.WXqrcodeBox:hover .WXbigImg {
display: block;
}
.welcome-title {
width: 113px;
height: 113px;
line-height: 115px;
font-size: 21px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #000000;
opacity: 0.85;
background: url('./images/logo.png');
background-size: 100%;
background-repeat: no-repeat;
margin: 80px 0 30px 0;
}
.formgroup2 {
display: flex;
align-items: center;
display: flex;
margin: 0px 5.5%;
margin-bottom: 10%;
align-items: center;
margin-bottom: 40px;
}
.APPcodeBox {
width: 100%;
display: flex;
flex-flow: column;
align-items: center;
cursor: pointer;
position: relative;
}
.APPCtext {
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #000000;
line-height: 30px;
opacity: 0.75;
}
.APPcodeBox:hover .APPCtext {
color: #1685FF;
}
.APPcodeBox:hover .APPCimg {
display: flex;
}
.APPCimg .codeCase {
text-align: center;
padding-top: 32px;
}
.WXlogoImg {
width: 575px;
// height: 40px;
// background: url('./images/logo@2x.png') no-repeat center;
// background-size: 100% 100%;
// margin: 0 10px 0 0;
}
.login-header {
width: 100%;
position: absolute;
top: 10px;
padding: 0 38px;
display: flex;
justify-content: space-between;
align-items: center;
.left-title {
display: flex;
align-items: center;
.cn-title {
font-size: 26px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
text-shadow: 0px 2px 2px rgb(6 15 49 / 33%);
}
}
.right-timebox {
display: flex;
justify-content: center;
align-items: center;
height: 60px;
.curr-time {
width: 150px;
font-size: 36px;
font-family: Microsoft YaHei;
font-weight: 400;
color: white;
}
.curr-week-date {
margin-left: 10px;
.curr-week {
font-size: 16px;
font-family: Microsoft YaHei;
font-weight: 400;
color: white;
}
.curr-date {
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
color: white;
}
}
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ import Login from '../pages/user/login';
import NoSecret from '../pages/user/login/noSecret';
import Notifications from '../pages/system/notifications';
import TabIframe from '../pages/system/iframe/TabWidget';
import IntegratedLogin from '../pages/system/iframe/IntegratedLogin';
import PrevieView from '../pages/system/previews/preview';
export const dyRoutes = (routes, layout, theme) => {
......@@ -100,6 +101,9 @@ export const dyRoutes = (routes, layout, theme) => {
if (/system\/previews\/preview/.test(path)) {
return <PrevieView {...routeConfig} />;
}
if (/system\/iframe\/integratedLogin/.test(path)) {
return <IntegratedLogin {...routeConfig} />;
}
},
},
],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment