Browse Source

Merge branch 'dev' of

release_0.0.1
deartibers 2 years ago
parent
commit
de3eba495e
  1. 7
      api/.vscode/launch.json
  2. 128
      api/app/lib/controllers/alarm/app.js
  3. 38
      api/app/lib/controllers/auth/index.js
  4. 157
      api/app/lib/controllers/organization/index.js
  5. 25
      api/app/lib/controllers/project/index.js
  6. 218
      api/app/lib/middlewares/authenticator.js
  7. 24
      api/app/lib/models/app_alarm.js
  8. 28
      api/app/lib/models/app_inspection.js
  9. 193
      api/app/lib/models/user.js
  10. 20
      api/app/lib/routes/alarm/index.js
  11. 8
      api/app/lib/routes/auth/index.js
  12. 17
      api/app/lib/routes/organization/index.js
  13. 8
      api/app/lib/routes/project/index.js
  14. 30
      api/config.js
  15. 2
      api/sequelize-automate.config.js
  16. BIN
      web/client/assets/images/problem/setup.png
  17. 13
      web/client/src/app.jsx
  18. 5
      web/client/src/components/setup.jsx
  19. 2
      web/client/src/layout/actions/global.js
  20. 2
      web/client/src/layout/containers/layout/index.jsx
  21. 42
      web/client/src/sections/problem/components/statistics.jsx
  22. 167
      web/client/src/sections/problem/components/tableData.jsx
  23. 254
      web/client/src/sections/problem/containers/dataAlarm.jsx
  24. 6
      web/client/src/sections/problem/nav-item.jsx

7
api/.vscode/launch.json

@ -22,7 +22,12 @@
"--apiEmisUrl http://10.8.30.112:14000", "--apiEmisUrl http://10.8.30.112:14000",
"--godUrl https://restapi.amap.com/v3", "--godUrl https://restapi.amap.com/v3",
"--godKey 21c2d970e1646bb9a795900dd00093ce", "--godKey 21c2d970e1646bb9a795900dd00093ce",
"--mqttVideoServer mqtt://10.8.30.71:30883" "--mqttVideoServer mqtt://10.8.30.71:30883",
"--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5",
"--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa",
"--qnbkt dev-operational-service",
// "--qndmn http://resources.anxinyun.cn",
"--qndmn http://rhvqdivo5.hn-bkt.clouddn.com",
] ]
}, },
{ {

128
api/app/lib/controllers/alarm/app.js

@ -0,0 +1,128 @@
'use strict';
const moment = require('moment')
async function inspection (ctx) {
try {
const models = ctx.fs.dc.models;
const { projectAppId, screenshot = [] } = ctx.request.body
const now = moment().format()
const storageData = screenshot.map(s => {
return {
projectAppId,
screenshot: [s],
createTime: now,
}
})
await models.AppInspection.bulkCreate(storageData)
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function notedInspection (ctx) {
try {
const models = ctx.fs.dc.models;
const { inspectionId } = ctx.request.body
const { userId } = ctx.fs.api
await models.AppInspection.update({
notedPepUserId: userId,
notedTime: moment().format()
}, {
where: {
id: inspectionId
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function apiError (ctx) {
try {
const models = ctx.fs.dc.models;
const { projectAppId, alarmContent, router, statusCode } = ctx.request.body
const now = moment().format()
let storageData = {
projectAppId, alarmContent, router, statusCode
}
const existRes = await models.AppAlarm.findOne({
where: {
projectAppId, alarmContent, router, statusCode,
confirm: null
}
})
if (existRes) {
await models.AppAlarm.update({
updateTime: now
}, {
where: {
id: existRes.id
}
})
} else {
const existCount = await models.AppAlarm.count({
where: {
}
})
storageData.serialNumber = 'WEB' + (existCount < 9 ? '0' + (existCount + 1) : existCount)
storageData.createTime = now
await models.AppAlarm.create(storageData)
}
ctx.status = 200;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function confirmApiError (ctx) {
try {
const models = ctx.fs.dc.models;
const { confirm, appAlarmId } = ctx.request.body
await models.AppAlarm.update({
confirm,
}, {
where: {
id: appAlarmId
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
inspection,
notedInspection,
apiError,
confirmApiError,
};

38
api/app/lib/controllers/auth/index.js

@ -19,18 +19,48 @@ async function login (ctx, next) {
} else { } else {
const pomsRegisterRes = await models.User.findOne({ const pomsRegisterRes = await models.User.findOne({
where: { where: {
pepUserId: emisLoginRes.id pepUserId: emisLoginRes.id,
$or: {
deleted: false,
role: { $contains: ['admin'] }
}
} }
}) })
if (!pomsRegisterRes) { if (!pomsRegisterRes) {
throw '当前账号尚未在此系统启用' throw '当前账号尚未在此系统启用'
} else if (
pomsRegisterRes.disable && (!pomsRegisterRes.role || !pomsRegisterRes.role.includes('admin'))
) {
throw '当前账号已禁用'
} }
emisLoginRes.pomsUserInfo = pomsRegisterRes.dataValues emisLoginRes.pomsUserInfo = pomsRegisterRes.dataValues
await models.User.update({ let userUpdateData = {
lastInTime: moment().format(), lastInTime: moment().format(),
inTimes: pomsRegisterRes.inTimes + 1 inTimes: pomsRegisterRes.inTimes + 1,
}, { lastInAddress: ''
}
try {
// 获取ip转为地点并记录
let ip =
// '117.90.39.49' ||
ctx.ip
if (ip && /^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$/.test(ip)) {
const ipLocationRes = await ctx.app.fs.godRequest.post('ip', {
query: {
ip,
}
})
if (ipLocationRes) {
userUpdateData.lastInAddress = ipLocationRes.province + ipLocationRes.city
}
}
} catch (error) {
ctx.fs.logger.error(`IP GET, error: ${error}`);
}
await models.User.update(userUpdateData, {
where: { where: {
id: emisLoginRes.id id: emisLoginRes.id
} }

157
api/app/lib/controllers/organization/index.js

@ -0,0 +1,157 @@
'use strict';
async function allDeps (ctx) {
try {
const models = ctx.fs.dc.models;
const { redis } = ctx.app
let depRes = await redis.get('allDepartments')
if (depRes) {
depRes = JSON.parse(depRes)
depRes = depRes.departments
}
ctx.status = 200;
ctx.body = depRes || []
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
async function editUser (ctx) {
try {
const models = ctx.fs.dc.models;
const { pepUserId, role = [], correlationProject = [] } = ctx.request.body
const existUserRes = await models.User.findOne({
where: {
pepUserId
}
})
let storageData = {
pepUserId,
role,
correlationProject,
updateTime: moment().format()
}
if (existUserRes) {
// 存在且传递id 或者 不传id也存在
// 修改 update
if (
role.includes('admin')
) {
storageData.role = [...new Set([...existUserRes.role, ...role])]
storageData.disabled = false
}
await models.User.update(storageData, {
where: {
pepUserId
}
})
} else {
// 新增
await models.User.create(storageData)
}
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
async function putUser (ctx) {
try {
const models = ctx.fs.dc.models;
const { pomsUserId } = ctx.params
const { disabled = undefined, deleted = undefined } = ctx.request.body
const updateData = {
disabled,
deleted,
}
for (let k in updateData) {
if (updateData[k] == undefined) {
delete updateData[k]
}
}
await models.User.update(updateData, {
where: {
id: pomsUserId
}
})
ctx.status = 204
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
async function user (ctx) {
try {
const models = ctx.fs.dc.models;
const { role, limit, page, } = ctx.query
const excludeField = ['lastInTime', 'inTimes', 'onlineDuration', 'lastInAddress', 'deleted', 'updateTime']
let findOption = {
attributes: {
exclude: excludeField,
},
where: {
deleted: false,
},
order: [['updateTime', 'DESC']]
}
if (role) {
findOption.where.role = { $contains: [role] }
}
if (limit) {
findOption.limit = limit
}
if (page && limit) {
findOption.offset = page * limit
}
const userRes = await models.User.findAndCountAll(findOption)
const adminRes = await models.User.findAll({
where: {
role: { $contains: ['admin'] }
},
attributes: {
exclude: excludeField,
},
order: [['updateTime', 'DESC']]
})
ctx.status = 200
ctx.body = {
admin: adminRes,
users: userRes
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
module.exports = {
allDeps,
editUser,
putUser,
user,
};

25
api/app/lib/controllers/project/index.js

@ -0,0 +1,25 @@
'use strict';
async function appList (ctx) {
try {
const models = ctx.fs.dc.models;
const appRes = await models.ProjectApp.findAll({
attributes: {
exclude: ['projectId']
}
})
ctx.status = 200;
ctx.body = appRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
appList
};

218
api/app/lib/middlewares/authenticator.js

@ -8,42 +8,42 @@ const util = require('util');
const moment = require('moment'); const moment = require('moment');
class ExcludesUrls { class ExcludesUrls {
constructor(opts) { constructor(opts) {
this.allUrls = undefined; this.allUrls = undefined;
this.reload(opts); this.reload(opts);
} }
sanitizePath (path) { sanitizePath (path) {
if (!path) return '/'; if (!path) return '/';
const p = '/' + path.replace(/^\/+/i, '').replace(/\/+$/, '').replace(/\/{2,}/, '/'); const p = '/' + path.replace(/^\/+/i, '').replace(/\/+$/, '').replace(/\/{2,}/, '/');
return p; return p;
} }
reload (opts) { reload (opts) {
// load all url // load all url
if (!this.allUrls) { if (!this.allUrls) {
this.allUrls = opts; this.allUrls = opts;
let that = this; let that = this;
this.allUrls.forEach(function (url, i, arr) { this.allUrls.forEach(function (url, i, arr) {
if (typeof url === "string") { if (typeof url === "string") {
url = { p: url, o: '*' }; url = { p: url, o: '*' };
arr[i] = url; arr[i] = url;
} }
const keys = []; const keys = [];
let eachPath = url.p; let eachPath = url.p;
url.p = (!eachPath || eachPath === '(.*)' || util.isRegExp(eachPath)) ? eachPath : that.sanitizePath(eachPath); url.p = (!eachPath || eachPath === '(.*)' || util.isRegExp(eachPath)) ? eachPath : that.sanitizePath(eachPath);
url.pregexp = pathToRegexp(eachPath, keys); url.pregexp = pathToRegexp(eachPath, keys);
}); });
} }
} }
isExcluded (path, method) { isExcluded (path, method) {
return this.allUrls.some(function (url) { return this.allUrls.some(function (url) {
return !url.auth return !url.auth
&& url.pregexp.test(path) && url.pregexp.test(path)
&& (url.o === '*' || url.o.indexOf(method) !== -1); && (url.o === '*' || url.o.indexOf(method) !== -1);
}); });
} }
} }
/** /**
@ -53,92 +53,94 @@ class ExcludesUrls {
* @param {*} method 当前request的method * @param {*} method 当前request的method
*/ */
let isPathExcluded = function (opts, path, method) { let isPathExcluded = function (opts, path, method) {
let excludeAll = Boolean(opts.exclude && opts.exclude.length && opts.exclude[0] == '*'); let excludeAll = Boolean(opts.exclude && opts.exclude.length && opts.exclude[0] == '*');
let excludes = null; let excludes = null;
if (!excludeAll) { if (!excludeAll) {
let excludeOpts = opts.exclude || []; let excludeOpts = opts.exclude || [];
excludeOpts.push({ p: '/login', o: 'POST' }); excludeOpts.push({ p: '/login', o: 'POST' });
excludeOpts.push({ p: '/logout', o: 'PUT' }); excludeOpts.push({ p: '/logout', o: 'PUT' });
excludes = new ExcludesUrls(excludeOpts); excludes = new ExcludesUrls(excludeOpts);
} }
let excluded = excludeAll || excludes.isExcluded(path, method); let excluded = excludeAll || excludes.isExcluded(path, method);
return excluded; return excluded;
}; };
let authorizeToken = async function (ctx, token) { let authorizeToken = async function (ctx, token) {
let rslt = null; let rslt = null;
const tokenFormatRegexp = /^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/g; const tokenFormatRegexp = /^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/g;
if (token && tokenFormatRegexp.test(token)) { if (token && tokenFormatRegexp.test(token)) {
try { try {
const expired = await ctx.redis.hget(token, 'expired');
if (expired && moment().valueOf() <= moment(expired).valueOf()) { const authorizeRes = await ctx.app.fs.emisRequest.get('authorize', {
const userInfo = JSON.parse(await ctx.redis.hget(token, 'userInfo')); query: { token }
rslt = { })
'authorized': userInfo.authorized, const { userInfo, expired } = authorizeRes;
'resources': (userInfo || {}).resources || [], if (expired && moment().valueOf() <= moment(expired).valueOf()) {
}; rslt = {
ctx.fs.api.userId = userInfo.id; 'authorized': userInfo.authorized,
ctx.fs.api.userInfo = userInfo; 'resources': (userInfo || {}).resources || [],
ctx.fs.api.token = token; };
} ctx.fs.api.userId = userInfo.id;
} catch (err) { ctx.fs.api.userInfo = userInfo;
const { error } = err.response || {}; ctx.fs.api.token = token;
ctx.fs.logger.log('[anxinyun]', '[AUTH] failed', (error || {}).message || `cannot GET /users/${token}`); }
} } catch (err) {
} const { error } = err.response || {};
return rslt; ctx.fs.logger.log('[anxinyun]', '[AUTH] failed', (error || {}).message || `cannot GET /users/${token}`);
}
}
return rslt;
}; };
let isResourceAvailable = function (resources, options) { let isResourceAvailable = function (resources, options) {
let authCode = null; let authCode = null;
// authorize user by authorization attribute // authorize user by authorization attribute
const { authAttr, method, path } = options; const { authAttr, method, path } = options;
for (let prop in authAttr) { for (let prop in authAttr) {
let keys = []; let keys = [];
let re = pathToRegexp(prop.replace(/\:[A-Za-z_\-]+\b/g, '(\\d+)'), keys); let re = pathToRegexp(prop.replace(/\:[A-Za-z_\-]+\b/g, '(\\d+)'), keys);
if (re.test(`${method}${path}`)) { if (re.test(`${method}${path}`)) {
authCode = authAttr[prop]; authCode = authAttr[prop];
break; break;
} }
} }
return !authCode || (resources || []).some(code => code === authCode); return !authCode || (resources || []).some(code => code === authCode);
}; };
function factory (app, opts) { function factory (app, opts) {
return async function auth (ctx, next) { return async function auth (ctx, next) {
const { path, method, header, query } = ctx; const { path, method, header, query } = ctx;
ctx.fs.logger.log('[AUTH] start', path, method); ctx.fs.logger.log('[AUTH] start', path, method);
ctx.fs.api = ctx.fs.api || {}; ctx.fs.api = ctx.fs.api || {};
ctx.fs.port = opts.port; ctx.fs.port = opts.port;
ctx.redis = app.redis; ctx.redis = app.redis;
ctx.redisTools = app.redisTools; ctx.redisTools = app.redisTools;
let error = null; let error = null;
if (path) { if (path) {
if (!isPathExcluded(opts, path, method)) { if (!isPathExcluded(opts, path, method)) {
const user = await authorizeToken(ctx, header.token || query.token); const user = await authorizeToken(ctx, header.token || query.token);
if (user && user.authorized) { if (user && user.authorized) {
// if (!isResourceAvailable(user.resources, { authAttr: app.fs.auth.authAttr, path, method })) { // if (!isResourceAvailable(user.resources, { authAttr: app.fs.auth.authAttr, path, method })) {
// error = { status: 403, name: 'Forbidden' } // error = { status: 403, name: 'Forbidden' }
// } else { // } else {
// error = { status: 401, name: 'Unauthorized' } // error = { status: 401, name: 'Unauthorized' }
// } // }
} else { } else {
error = { status: 401, name: 'Unauthorized' } error = { status: 401, name: 'Unauthorized' }
}
} }
} else { }
error = { status: 401, name: 'Unauthorized' }; } else {
} error = { status: 401, name: 'Unauthorized' };
if (error) { }
ctx.fs.logger.log('[AUTH] failed', path, method); if (error) {
ctx.status = error.status; ctx.fs.logger.log('[AUTH] failed', path, method);
ctx.body = error.name; ctx.status = error.status;
} else { ctx.body = error.name;
ctx.fs.logger.log('[AUTH] passed', path, method); } else {
await next(); ctx.fs.logger.log('[AUTH] passed', path, method);
} await next();
} }
}
} }
module.exports = factory; module.exports = factory;

24
api/app/lib/models/app_alarm.js

@ -24,18 +24,18 @@ module.exports = dc => {
field: "serial_number", field: "serial_number",
autoIncrement: false autoIncrement: false
}, },
pepProjectId: { projectAppId: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "对应的项目id", comment: "对应的项目id",
primaryKey: false, primaryKey: false,
field: "pep_project_id", field: "project_app_id",
autoIncrement: false autoIncrement: false
}, },
appDomain: { appDomain: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "应用域名", comment: "应用域名",
primaryKey: false, primaryKey: false,
@ -77,6 +77,24 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "confirm", field: "confirm",
autoIncrement: false autoIncrement: false
},
router: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "路由",
primaryKey: false,
field: "router",
autoIncrement: false
},
statusCode: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "状态码",
primaryKey: false,
field: "status_code",
autoIncrement: false
} }
}, { }, {
tableName: "app_alarm", tableName: "app_alarm",

28
api/app/lib/models/app_inspection.js

@ -15,14 +15,18 @@ module.exports = dc => {
autoIncrement: true, autoIncrement: true,
unique: "app_inspection_id_uindex" unique: "app_inspection_id_uindex"
}, },
pepProjectId: { projectAppId: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: null, comment: null,
primaryKey: false, primaryKey: false,
field: "pep_project_id", field: "project_app_id",
autoIncrement: false autoIncrement: false,
references: {
key: "id",
model: "projectApp"
}
}, },
createTime: { createTime: {
type: DataTypes.DATE, type: DataTypes.DATE,
@ -41,6 +45,24 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "screenshot", field: "screenshot",
autoIncrement: false autoIncrement: false
},
notedPepUserId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "核验人员",
primaryKey: false,
field: "noted_pep_user_id",
autoIncrement: false
},
notedTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "noted_time",
autoIncrement: false
} }
}, { }, {
tableName: "app_inspection", tableName: "app_inspection",

193
api/app/lib/models/user.js

@ -2,87 +2,114 @@
'use strict'; 'use strict';
module.exports = dc => { module.exports = dc => {
const DataTypes = dc.ORM; const DataTypes = dc.ORM;
const sequelize = dc.orm; const sequelize = dc.orm;
const User = sequelize.define("user", { const User = sequelize.define("user", {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: null, defaultValue: null,
comment: null, comment: null,
primaryKey: true, primaryKey: true,
field: "id", field: "id",
autoIncrement: true, autoIncrement: true,
unique: "user_id_uindex" unique: "user_id_uindex"
}, },
pepUserId: { pepUserId: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: null, defaultValue: null,
comment: "项企对应用户id", comment: "项企对应用户id",
primaryKey: false, primaryKey: false,
field: "pep_user_id", field: "pep_user_id",
autoIncrement: false autoIncrement: false
}, },
role: { role: {
type: DataTypes.ARRAY(DataTypes.STRING), type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "角色 也对应权限 admin 管理员 / all 全部角色 / data_analyst 数据分析 / after_sale 售后运维 / resource_manage 资源管理 / customer_service 客户服务", comment: "角色 也对应权限 admin 管理员 / all 全部角色 / data_analyst 数据分析 / after_sale 售后运维 / resource_manage 资源管理 / customer_service 客户服务",
primaryKey: false, primaryKey: false,
field: "role", field: "role",
autoIncrement: false autoIncrement: false
}, },
correlationProject: { correlationProject: {
type: DataTypes.ARRAY(DataTypes.INTEGER), type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "关联的项目管理的项目id", comment: "关联的项目管理的项目id",
primaryKey: false, primaryKey: false,
field: "correlation_project", field: "correlation_project",
autoIncrement: false autoIncrement: false
}, },
lastInTime: { lastInTime: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: null, comment: null,
primaryKey: false, primaryKey: false,
field: "last_in_time", field: "last_in_time",
autoIncrement: false autoIncrement: false
}, },
inTimes: { inTimes: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: "0", defaultValue: "0",
comment: null, comment: null,
primaryKey: false, primaryKey: false,
field: "in_times", field: "in_times",
autoIncrement: false autoIncrement: false
}, },
onlineDuration: { onlineDuration: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: "0",
comment: "在线时长 单位 s", comment: "在线时长 单位 s",
primaryKey: false, primaryKey: false,
field: "online_duration", field: "online_duration",
autoIncrement: false autoIncrement: false
}, },
lastInAddress: { lastInAddress: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "上次登录地点", comment: "上次登录地点",
primaryKey: false, primaryKey: false,
field: "last_in_address", field: "last_in_address",
autoIncrement: false autoIncrement: false
} },
}, { disabled: {
tableName: "user", type: DataTypes.BOOLEAN,
comment: "", allowNull: false,
indexes: [] defaultValue: null,
}); comment: null,
dc.models.User = User; primaryKey: false,
return User; field: "disabled",
autoIncrement: false
},
deleted: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "deleted",
autoIncrement: false
},
updateTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: sequelize.fn('now'),
comment: null,
primaryKey: false,
field: "update_time",
autoIncrement: false
}
}, {
tableName: "user",
comment: "",
indexes: []
});
dc.models.User = User;
return User;
}; };

20
api/app/lib/routes/alarm/index.js

@ -0,0 +1,20 @@
'use strict';
const application = require('../../controllers/alarm/app');
module.exports = function (app, router, opts) {
// 应用告警
app.fs.api.logAttr['POST/alarm/application/inspection'] = { content: '保存应用巡检信息', visible: true };
router.post('/alarm/application/inspection', application.inspection);
app.fs.api.logAttr['PUT/alarm/application/noted'] = { content: '保存检验状态', visible: true };
router.put('/alarm/application/noted', application.notedInspection);
app.fs.api.logAttr['POST/alarm/application/api'] = { content: '保存应用接口错误信息', visible: true };
router.post('/alarm/application/api', application.apiError);
app.fs.api.logAttr['POST/alarm/application/api_confirm'] = { content: '确认应用接口错误信息', visible: true };
router.post('/alarm/application/api_confirm', application.confirmApiError);
};

8
api/app/lib/routes/auth/index.js

@ -3,15 +3,9 @@
const auth = require('../../controllers/auth'); const auth = require('../../controllers/auth');
module.exports = function (app, router, opts) { module.exports = function (app, router, opts) {
/**
* @api {Post} login 登录.
* @apiVersion 1.0.0
* @apiGroup Auth
*/
app.fs.api.logAttr['POST/login'] = { content: '登录', visible: true }; app.fs.api.logAttr['POST/login'] = { content: '登录', visible: true };
router.post('/login', auth.login); router.post('/login', auth.login);
app.fs.api.logAttr['PUT/logout'] = { content: '登出', visible: false }; app.fs.api.logAttr['PUT/logout'] = { content: '登出', visible: false };
router.put('/logout', auth.logout); router.put('/logout', auth.logout);
};
};

17
api/app/lib/routes/organization/index.js

@ -0,0 +1,17 @@
'use strict';
const organization = require('../../controllers/organization');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/organization/deps'] = { content: '获取全部部门及其下用户', visible: true };
router.get('/organization/deps', organization.allDeps);
app.fs.api.logAttr['POST/organization/user'] = { content: '编辑成员', visible: true };
router.post('/organization/user', organization.editUser);
app.fs.api.logAttr['PUT/organization/user/:pomsUserId'] = { content: '修改成员状态', visible: true };
router.put('/organization/user/:pomsUserId', organization.putUser);
app.fs.api.logAttr['GET/organization/user'] = { content: '获取成员列表', visible: true };
router.get('/organization/user', organization.user);
};

8
api/app/lib/routes/project/index.js

@ -0,0 +1,8 @@
'use strict';
const project = require('../../controllers/project');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/project/app_list'] = { content: '获取应用列表', visible: true };
router.get('/project/app_list', project.appList);
};

30
api/config.js

@ -24,6 +24,12 @@ args.option('godKey', '高德地图API key');
args.option('mqttVideoServer', '视频后台 mqtt 服务 URL'); args.option('mqttVideoServer', '视频后台 mqtt 服务 URL');
// 七牛云存储参数
args.option('qnak', 'qiniuAccessKey');
args.option('qnsk', 'qiniuSecretKey');
args.option('qnbkt', 'qiniuBucket');
args.option('qndmn', 'qiniuDomain');
const flags = args.parse(process.argv); const flags = args.parse(process.argv);
const POMS_DB = process.env.POMS_DB || flags.pg; const POMS_DB = process.env.POMS_DB || flags.pg;
@ -46,11 +52,19 @@ const GOD_KEY = process.env.GOD_KEY || flags.godKey;
// 视频后台 mqtt 信息推送地址 // 视频后台 mqtt 信息推送地址
const MQTT_VIDEO_SERVER = process.env.MQTT_VIDEO_SERVER || flags.mqttVideoServer; const MQTT_VIDEO_SERVER = process.env.MQTT_VIDEO_SERVER || flags.mqttVideoServer;
// 七牛云存储参数
const QINIU_DOMAIN_QNDMN_RESOURCE = process.env.ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE || flags.qndmn;
const QINIU_BUCKET_RESOURCE = process.env.ANXINCLOUD_QINIU_BUCKET_RESOURCE || flags.qnbkt;
const QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak;
const QINIU_SK = process.env.ANXINCLOUD_QINIU_SECRETKEY || flags.qnsk;
if ( if (
!POMS_DB || !POMS_DB ||
!IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY || !MQTT_VIDEO_SERVER || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY || !MQTT_VIDEO_SERVER ||
!AXY_API_URL || !AXY_API_URL ||
!API_EMIS_URL !API_EMIS_URL ||
!QINIU_DOMAIN_QNDMN_RESOURCE || !QINIU_BUCKET_RESOURCE || !QINIU_AK || !QINIU_SK
) { ) {
console.log('缺少启动参数,异常退出'); console.log('缺少启动参数,异常退出');
args.showHelp(); args.showHelp();
@ -64,10 +78,11 @@ const product = {
{ {
entry: require('@fs/attachment').entry, entry: require('@fs/attachment').entry,
opts: { opts: {
local: { qiniu: {
origin: IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || `http://localhost:${flags.port || 8080}`, domain: QINIU_DOMAIN_QNDMN_RESOURCE,
rootPath: 'static', bucket: QINIU_BUCKET_RESOURCE,
childPath: 'upload', accessKey: QINIU_AK,
secretKey: QINIU_SK
}, },
maxSize: 104857600, // 100M maxSize: 104857600, // 100M
} }
@ -76,8 +91,9 @@ const product = {
opts: { opts: {
dev, dev,
exclude: [ exclude: [
{ p: '/camera', o: 'GET' }, // 暂时滴 { p: '/attachments/:p', o: 'POST' },
{ p: '/application/check', o: 'GET' }, // 暂时滴 { p: '/alarm/application/inspection', o: 'POST' },
{ p: '/project/app_list', o: 'GET' }
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由 ], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
redis: { redis: {
host: IOTA_REDIS_SERVER_HOST, host: IOTA_REDIS_SERVER_HOST,

2
api/sequelize-automate.config.js

@ -26,7 +26,7 @@ module.exports = {
dir: './app/lib/models', // 指定输出 models 文件的目录 dir: './app/lib/models', // 指定输出 models 文件的目录
typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义
emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir`
tables: null, // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 tables: ['app_alarm'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面

BIN
web/client/assets/images/problem/setup.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

13
web/client/src/app.jsx

@ -14,6 +14,19 @@ const App = props => {
useEffect(() => { useEffect(() => {
document.title = projectName; document.title = projectName;
console.log(`
_ _
         
      |  _  _ l
      \` ミ_xノ
     /      |
    /    
      | | |
 |   | | |
 | (ヽ__ヽ_)__)
 二つ
`);
}, []) }, [])
return ( return (

5
web/client/src/components/setup.jsx

@ -11,6 +11,9 @@ function Setup(props) {
tableType, tableType,
tableList tableList
} = props; } = props;
console.log(tableType,
tableList);
const [check, setCheck] = useState([]); const [check, setCheck] = useState([]);
const checkboxcss = { width: "25%", height: 16, margin: "0 0 20px 0" }; const checkboxcss = { width: "25%", height: 16, margin: "0 0 20px 0" };
@ -98,7 +101,7 @@ function Setup(props) {
{item.title} {item.title}
</div> </div>
<div style={{ padding: "15px 12px", width: 530 }}> <div style={{ padding: "15px 12px", width: 530 }}>
{item.list.map((itm) => { {item.list?.map((itm) => {
return ( return (
<Checkbox <Checkbox
key={itm.value} key={itm.value}

2
web/client/src/layout/actions/global.js

@ -18,7 +18,7 @@ export function initLayout (title, copyright, sections, actions) {
export const RESIZE = 'RESIZE'; export const RESIZE = 'RESIZE';
export function resize (clientHeight, clientWidth) { export function resize (clientHeight, clientWidth) {
const headerHeight = 60 const headerHeight = 50
const footerHeight = 0 const footerHeight = 0
return { return {
type: RESIZE, type: RESIZE,

2
web/client/src/layout/containers/layout/index.jsx

@ -237,7 +237,7 @@ const LayoutContainer = props => {
}} }}
/> />
</Layout.Header> </Layout.Header>
<Layout style={{ height: 'calc(100% - 60px)' }}> <Layout style={{ height: 'calc(100% - 50px)' }}>
{leftShow ? (<Layout.Sider> {leftShow ? (<Layout.Sider>
<Sider <Sider
sections={sections} sections={sections}

42
web/client/src/sections/problem/components/statistics.jsx

@ -0,0 +1,42 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import { DatePicker } from "@douyinfe/semi-ui";
const Statistics = ({ dispatch, actions, close, modalName, visible, appData }) => {
return (
<div style={{ width: '100%', height: 180, border: '1px solid #00000012', backgroundColor: 'white', marginBottom: 20 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>数据异常统计</div>
<DatePicker
type="dateTimeRange"
style={{ width: 405 }}
// defaultPickerValue={[new Date('2022-08-08 00:00'), new Date('2022-08-09 12:00')]}
value={[new Date('2022-08-08 00:00'), new Date('2022-08-09 00:00')]}
prefix='统计时段:'
onChange={console.log}
/>
</div>
<div style={{ width: '100%', display: 'flex', justifyContent: 'space-around', marginTop: 8 }}>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
</div>
</div>
)
}
function mapStateToProps (state) {
const { auth, global, members } = state;
return {
// user: auth.user,
// actions: global.actions,
// global: global,
// members: members.data,
};
}
export default connect(mapStateToProps)(Statistics);

167
web/client/src/sections/problem/components/tableData.jsx

@ -0,0 +1,167 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import { Button, Form, Modal, Skeleton, Pagination, Table } from "@douyinfe/semi-ui";
import { SkeletonScreen, } from "$components";
const TableData = ({ dispatch, actions, route, collectData, setSetup }) => {
const api = useRef();
useEffect(() => {
}, [route])
return (
<>
<div style={{ backgroundColor: '#FFFFFF' }}>
<div style={{}}>
<div style={{ display: "flex", marginBottom: 16 }}>
<Form
onSubmit={(values) => console.log(values)}
// onValueChange={values=>console.log(values)}
getFormApi={(formApi) => (api.current = formApi)}
layout="horizontal"
style={{ position: "relative", width: "100%", flex: 1 }}
>
{(() => {
let frame = []
collectData[route]?.map((v, index) => {
if (index == 0) {
frame.push(<Form.Input
label={v.name + ':'}
field={v.field}
key={v.field}
maxLength="16"
placeholder="搜索项目名称、结构物名称、或告警源名称"
labelPosition="left"
style={{ width: 292, marginRight: 10, }}
/>)
} else {
frame.push(<Form.Select
label={v.name + ':'}
labelPosition="left"
field={v.field}
key={v.field}
style={{ width: 116, marginRight: 10, color: "#F9F9F9", }}
placeholder="全部"
showClear
>
{v.data?.map((item) => {
return (
<Form.Select.Option key={item.value} value={item.value}>
{item.name}
</Form.Select.Option>
);
})}
</Form.Select>)
}
})
frame.push(<Form.DatePicker
type="dateTimeRange"
labelPosition="left"
style={{ width: 352 }}
label={collectData.common.name + ':'}
field={collectData.common.field}
key={collectData.common.field}
// defaultPickerValue={[new Date('2022-08-08 00:00'), new Date('2022-08-09 12:00')]}
values={[new Date('2022-08-08 00:00'), new Date()]}
onChange={(v) => console.log(v)}
/>)
return frame
})()}
</Form>
<div
style={{
width: 100,
display: "flex",
justifyContent: "space-between",
alignItems: "flex-end",
marginRight: 10
}}
>
<img src="/assets/images/problem/setup.png"
style={{ width: 24, height: 24 }}
onClick={() => setSetup(true)} />
<Button
theme="solid"
type="primary"
style={{
width: 65,
height: 30,
borderRadius: 3,
}}
onClick={() => {
api.current.validate().then((v) => {
console.log(v);
// setSearch(v);
// setQuery({ limit: 10, page: 0 })
});
}}
>
搜索
</Button>
</div>
</div>
</div>
<Skeleton
loading={false}
active={true}
placeholder={SkeletonScreen()}
>
<Table
columns={[{ title: '叫', dataIndex: 'name', }]}
dataSource={[{ name: '1', }]}
bordered={false}
empty="暂无数据"
style={{
padding: "0px 20px",
}}
pagination={false}
onRow={(record, index) => {
if (index % 1 === 0) {
return { style: { background: '' } }
}
}}
/>
</Skeleton>
<div
style={{
display: "flex",
justifyContent: "flex-end",
padding: "20px 20px",
}}
>
<span style={{ lineHeight: "30px" }}>
{100}个设备
</span>
<Pagination
className="22"
total={100}
showSizeChanger
currentPage={1}
pageSizeOpts={[10, 20, 30, 40]}
onChange={(currentPage, pageSize) => {
// setQuery({ limit: pageSize, page: currentPage - 1 });
// page.current = currentPage - 1
}}
/>
</div>
</div >
</>
)
}
function mapStateToProps (state) {
const { auth, global, members } = state;
return {
// user: auth.user,
// actions: global.actions,
// global: global,
// members: members.data,
};
}
export default connect(mapStateToProps)(TableData);

254
web/client/src/sections/problem/containers/dataAlarm.jsx

@ -1,35 +1,261 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux'; import { connect } from 'react-redux'
import { IconAlertCircle } from '@douyinfe/semi-icons'; import { IconAlertCircle } from '@douyinfe/semi-icons'
import Statistics from '../components/statistics'
import TableData from '../components/tableData'
import { Setup } from "$components";
import '../style.less' import '../style.less'
const DataAlarm = (props) => { const DataAlarm = ({ match, dispatch, actions, user, loading, socket }) => {
const { dispatch, actions, user, loading, socket } = props
const [abnormalLenght, setAbnormalLenght] = useState(1) // const [route, setRoute] = useState('') //
const [abnormalLenght, setAbnormalLenght] = useState(0) //
const [collect, setCollect] = useState([]) //
const [setup, setSetup] = useState(false); //
const [tableSetup, setTableSetup] = useState([]); //
const tableType = { dataLnterrupt: 'dataLnterrupt', dataAbnormal: 'dataAbnormal', strategyHit: 'strategyHit', videoAbnormal: 'videoAbnormal', useAbnormal: 'useAbnormal', deviceAbnormal: 'deviceAbnormal' }
useEffect(() => { useEffect(() => {
console.log(props); setRoute(match.url.substring(match.url.lastIndexOf("/") + 1, match.url.length))
console.log(match.url)
console.log(tableType);
}, []) }, [])
useEffect(() => {
attribute(tableType[route], route);
}, [route])
//
const collectData = {
dataLnterrupt: [ //(dataLnterrupt)
{ name: '搜索', field: '1' },
{
name: '中断类型', field: '2',
data: [
{ name: '服务异常', value: '11' },
{ name: '链接中断', value: '22' },
{ name: '设备异常', value: '33' }]
},
{
name: '中断状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
dataAbnormal: [ //(dataAbnormal)
{ name: '搜索', field: '1' },
{
name: '异常类型', field: '2',
data: [
{ name: '超远程', value: '11' },
{ name: '超限幅', value: '22' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
strategyHit: [ // (strategyHit)
{ name: '搜索', field: '1' },
{
name: '策略类型', field: '2',
data: [
{ name: '超阀值', value: '11' },
{ name: '防驶入告警', value: '22' },
{ name: '设备关闭', value: '33' },
{ name: '超围栏', value: '44' }]
},
{
name: '命中状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
videoAbnormal: [ // (videoAbnormal)
{ name: '搜索', field: '1' },
{
name: '设备类型', field: '2',
data: [
{ name: '枪机', value: '11' },
{ name: '球机', value: '22' },
{ name: '其他', value: '33' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
useAbnormal: [ // (useAbnormal)
{ name: '搜索', field: '1' },
{
name: '异常类型', field: '2',
data: [
{ name: '接口报错', value: '11' },
{ name: '加载超时', value: '22' },
{ name: '元素异常', value: '33' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
deviceAbnormal: [ // (deviceAbnormal)
{ name: '搜索', field: '1' },
{
name: '设备类型', field: '2',
data: [
{ name: '传感器', value: '11' },
{ name: 'DTU', value: '22' },
{ name: '服务器', value: '33' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
},
{
name: '异常类型', field: '4',
data: [
{ name: '离线', value: '11' }]
}],
common: {
name: '持续时间',
field: '5'
}
}
//
const tableList = {
dataLnterrupt: ['1', '2', '3', '4', '6', '9', '7', '5', '10', '8', '11', '12', '13', '14'],
dataAbnormal: ['1', '2', '3', '4', '15', '16', '9', '7', '5', '10', '8', '11', '12', '13'],
strategyHit: ['1', '2', '3', '17', '18', '7', '5', '10', '8', '11', '12', '13'],
videoAbnormal: ['1', '2', '3', '19', '20', '21', '22', '5', '6', '7', '8', '12', '13'],
useAbnormal: ['1', '23', '24', '15', '25', '7', '8', '12', '13'],
deviceAbnormal: ['1', '2', '3', '19', '15', '21', '5', '6', '7', '8', '12', '13'],
}
//
const columns = {
dataLnterrupt: ['0', '1', '2', '3', '4', '5', '6', '7', '8',],
dataAbnormal: ['0', '1', '2', '3', '15', '5', '6', '7', '8'],
strategyHit: ['0', '1', '2', '3', '17', '5', '10', '11', '7', '8'],
videoAbnormal: ['0', '1', '2', '3', '21', '20', '5', '7', '8'],
useAbnormal: ['0', '1', '23', '24', '15', '25', '7', '8'],
deviceAbnormal: ['0', '1', '2', '3', '15', '19', '5', '7', '8'],
}
//
const columnAll = [
{ name: '问题编号', value: '0' },
{ name: '项目名称', value: '1' },
{ name: '结构物名称', value: '2' },
{ name: '告警源', value: '3' },
{ name: '中断类型', value: '4' },
{ name: '告警信息', value: '5' },
{ name: '常见原因', value: '6' },
{ name: '产生时间', value: '7' },
{ name: '更新时间', value: '8' },
{ name: '服务器地址', value: '9' },
{ name: '告警等级', value: '10' },
{ name: '产生次数', value: '11' },
{ name: '确认信息', value: '12' },
{ name: '确认/恢复时间', value: '13' },
{ name: '项目阶段', value: '14' },
{ name: '异常类型', value: '15' },
{ name: '异常原因', value: '16' },
{ name: '策略类型', value: '17' },
{ name: '命中状态', value: '18' },
{ name: '位置信息', value: '19' },
{ name: '设备类型', value: '20' },
{ name: '设备厂家', value: '21' },
{ name: '接入方式', value: '22' },
{ name: '应用名称', value: '23' },
{ name: 'URL地址', value: '24' },
{ name: '异常信息', value: '25' },
]
const attribute = (tableType, route) => {
let arr = localStorage.getItem(tableType)
? JSON.parse(localStorage.getItem(tableType))
: [];
// if (route) {
// console.log(tableList[route]);
// console.log([...[1,2,3],...[2,3]]);
// let setup = tableList[route].map(v => columnAll.find(vv => v == vv.value))
// console.log(setup);
// setTableSetup([{ list: setup }])
// }
// for (let i = 0; i < arr.length; i++) {
// let colum = column.filter((item) => {
// return item.key === arr[i];
// });
// columns.splice(i + 2, 0, colum[0]);
// }
// setSetupp(columns);
}
const distinguish = (route) => {
switch (route) {
case value:
break;
default:
break;
}
}
return ( return (
<> <div>
{/* 滞留提醒 */}
<div> <div>
{abnormalLenght > 0 ? <IconAlertCircle /> : ""} {abnormalLenght > 0 ? <div style={{ height: 30, fontSize: 12, display: 'flex' }}><IconAlertCircle /><div>当前滞留5个工单即将超时请尽快处理</div></div> : ""}
</div> </div>
<Statistics />
</> <TableData
route={route}
collectData={collectData}
setSetup={setSetup}
/>
{setup ? (
<Setup
tableType={tableType[route] || []}
tableList={tableSetup}
close={() => {
setSetup(false);
attribute(tableType[route]);
// setcameraSetup(false);
}}
/>
) : (
""
)}
</div>
) )
} }
function mapStateToProps (state) { function mapStateToProps (state) {
const { auth, global, members, webSocket } = state; const { auth, global, members, webSocket } = state
return { return {
// loading: members.isRequesting, // loading: members.isRequesting,
user: auth.user, user: auth.user,
actions: global.actions, actions: global.actions,
// members: members.data, // members: members.data,
// socket: webSocket.socket // socket: webSocket.socket
}; }
} }
export default connect(mapStateToProps)(DataAlarm); export default connect(mapStateToProps)(DataAlarm)

6
web/client/src/sections/problem/nav-item.jsx

@ -7,7 +7,7 @@ export function getNavItem (user, dispatch) {
itemKey: 'problem', text: '问题', itemKey: 'problem', text: '问题',
items: [ items: [
{ {
itemKey: 'dataAlarm', text: '数据告警', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>, itemKey: 'dataAlarm', text: '数据告警', to: '/problem/dataAlarm/dataLnterrupt', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>,
items: [ items: [
{ itemKey: 'dataLnterrupt', to: '/problem/dataAlarm/dataLnterrupt', text: '数据中断' }, { itemKey: 'dataLnterrupt', to: '/problem/dataAlarm/dataLnterrupt', text: '数据中断' },
{ itemKey: 'dataAbnormal', to: '/problem/dataAlarm/dataAbnormal', text: '数据异常' }, { itemKey: 'dataAbnormal', to: '/problem/dataAlarm/dataAbnormal', text: '数据异常' },
@ -15,12 +15,12 @@ export function getNavItem (user, dispatch) {
{ itemKey: 'videoAbnormal', to: '/problem/dataAlarm/videoAbnormal', text: '视频异常' }, { itemKey: 'videoAbnormal', to: '/problem/dataAlarm/videoAbnormal', text: '视频异常' },
] ]
}, { }, {
itemKey: 'useAlarm', text: '应用告警', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>, itemKey: 'useAlarm', text: '应用告警', to: '/problem/useAlarm/useAbnormal', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>,
items: [ items: [
{ itemKey: 'useAbnormal', to: '/problem/useAlarm/useAbnormal', text: '应用异常' }, { itemKey: 'useAbnormal', to: '/problem/useAlarm/useAbnormal', text: '应用异常' },
] ]
}, { }, {
itemKey: 'deviceAlarm', text: '设备告警', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>, itemKey: 'deviceAlarm', text: '设备告警', to: '/problem/deviceAlarm/deviceAbnormal', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>,
items: [ items: [
{ itemKey: 'deviceAbnormal', to: '/problem/deviceAlarm/deviceAbnormal', text: '设备异常' }, { itemKey: 'deviceAbnormal', to: '/problem/deviceAlarm/deviceAbnormal', text: '设备异常' },
] ]

Loading…
Cancel
Save