Browse Source

能耗

master
wenlele 2 years ago
parent
commit
24a294a129
  1. 96
      api/.vscode/launch.json
  2. 67
      api/app/lib/controllers/bigScreen/index .js
  3. 87
      api/app/lib/controllers/organization/authority.js
  4. 389
      api/app/lib/controllers/organization/user.js
  5. 4
      api/app/lib/index.js
  6. 28
      api/app/lib/routes/organization/authority.js
  7. 9
      api/app/lib/routes/organization/index.js
  8. 41
      api/app/lib/routes/organization/user.js
  9. 53
      api/app/lib/schedule/clearExpiredData.js
  10. 208
      api/app/lib/schedule/hideDangerStatistic.js
  11. 64
      api/app/lib/schedule/metting.js
  12. 65
      api/config.js
  13. BIN
      web/client/assets/images/login/button-b.png
  14. BIN
      web/client/assets/images/login/icon_y.png
  15. BIN
      web/client/assets/images/login/icon_z.png
  16. BIN
      web/client/assets/images/login/input-b.png
  17. BIN
      web/client/assets/images/login/login-b.gif
  18. BIN
      web/client/assets/images/login/login_a.png
  19. BIN
      web/client/assets/images/login/login_b.png
  20. BIN
      web/client/assets/images/login/register-bg.png
  21. BIN
      web/client/assets/images/login/word.png
  22. BIN
      web/client/assets/images/monitor/ball-A.png
  23. BIN
      web/client/assets/images/monitor/ball-V.png
  24. BIN
      web/client/assets/images/monitor/bg-header.png
  25. BIN
      web/client/assets/images/monitor/headerTitle.png
  26. BIN
      web/client/assets/images/monitor/pedestal.png
  27. BIN
      web/client/assets/images/monitor/pump-head.png
  28. BIN
      web/client/assets/images/monitor/site.png
  29. BIN
      web/client/assets/images/monitor/title.png
  30. 4
      web/client/src/layout/actions/global.js
  31. 99
      web/client/src/layout/reducers/global.js
  32. 88
      web/client/src/sections/auth/actions/auth.js
  33. 106
      web/client/src/sections/auth/containers/login.js
  34. 28
      web/client/src/sections/auth/containers/login.less
  35. 51
      web/client/src/sections/bigScreen/actions/authority.js
  36. 19
      web/client/src/sections/bigScreen/actions/bigScreen.js
  37. 9
      web/client/src/sections/bigScreen/actions/index.js
  38. 113
      web/client/src/sections/bigScreen/actions/user.js
  39. 665
      web/client/src/sections/bigScreen/components/capacity.js
  40. 6
      web/client/src/sections/bigScreen/components/header.js
  41. 13
      web/client/src/sections/bigScreen/components/index.less
  42. 5
      web/client/src/sections/bigScreen/containers/systemManagement.js
  43. 2
      web/client/src/sections/bigScreen/index.js
  44. 13
      web/client/src/utils/index.js
  45. 121
      web/client/src/utils/webapi.js
  46. 169
      web/config.js
  47. 211
      web/package.json
  48. 262
      web/routes/attachment/index.js

96
api/.vscode/launch.json

@ -1,49 +1,51 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动 API",
"program": "${workspaceRoot}/server.js",
"env": {
"NODE_ENV": "development"
},
"args": [
"-p 4900",
//
"-g postgres://postgres:123456@10.8.30.166:5432/XunJian",
//
// "--apiEmisUrl http://10.8.30.161:1111",
"--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5",
"--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa",
"--qnbkt dev-hr",
// "--qndmn http://resources.anxinyun.cn",
"--qndmn http://rjkwed13l.hn-bkt.clouddn.com",
"--aliOssAccessKey LTAI5tNDfn7UhStYQcn3JBtw",
"--aliOssSecretKey rnoXtDWQA1djJ5Xqcdn1OSEol0lVyv",
"--aliOssBucket test-c371",
"--aliOssRegion oss-cn-hangzhou",
]
},
{
"type": "node",
"request": "launch",
"name": "run mocha",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"stopOnEntry": false,
"args": [
"app/test/*.test.js",
"--no-timeouts"
],
"cwd": "${workspaceRoot}",
"runtimeExecutable": null,
"env": {
"NODE_ENV": "development"
}
}
]
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动 API",
"program": "${workspaceRoot}/server.js",
"env": {
"NODE_ENV": "development"
},
"args": [
"-p 4900",
//
"-g postgres://postgres:123456@10.8.30.166:5432/XunJian",
//
// "--apiEmisUrl http://10.8.30.161:1111",
// "--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5",
// "--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa",
// "--qnbkt dev-hr",
// "--qndmn http://resources.anxinyun.cn",
// "--qndmn http://rjkwed13l.hn-bkt.clouddn.com",
// "--aliOssAccessKey LTAI5tNDfn7UhStYQcn3JBtw",
// "--aliOssSecretKey rnoXtDWQA1djJ5Xqcdn1OSEol0lVyv",
// "--aliOssBucket test-c371",
// "--aliOssRegion oss-cn-hangzhou",
"--apiAnxinyunUrl https://openapi.anxinyun.cn/api/v1",
"--axyProject 1a271f12-52f2-4d16-8dad-ec0c92d3e0cc/03bzzdh/123456",
]
},
{
"type": "node",
"request": "launch",
"name": "run mocha",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"stopOnEntry": false,
"args": [
"app/test/*.test.js",
"--no-timeouts"
],
"cwd": "${workspaceRoot}",
"runtimeExecutable": null,
"env": {
"NODE_ENV": "development"
}
}
]
}

67
api/app/lib/controllers/bigScreen/index .js

@ -0,0 +1,67 @@
'use strict';
const Hex = require('crypto-js/enc-hex');
const MD5 = require('crypto-js/md5');
const moment = require('moment');
let axyTokenCache = {
token: null,
orgId: null,
expireTime: null //过期时间
}
const getAnxinyunToken = async function (ctx) {
try {
if (!axyTokenCache.token || moment() > moment(axyTokenCache.expireTime)) {
if (ctx.app.fs.opts.axyProject.split('/').length === 3) {
const dataToAxy = {
p: ctx.app.fs.opts.axyProject.split('/')[0],
username: ctx.app.fs.opts.axyProject.split('/')[1],
password: ctx.app.fs.opts.axyProject.split('/')[2],
}
const axyResponse = await ctx.app.fs.anxinyun.post('project/login', { data: dataToAxy })
if (axyResponse.authorized) {
axyTokenCache.token = axyResponse.token //放进缓存
axyTokenCache.orgId = axyResponse.orgId //放进缓存
axyTokenCache.expireTime = moment().add(20, 'hour').format('YYYY-MM-DD HH:mm:ss')
}
}
}
return axyTokenCache
} catch (error) {
ctx.fs.logger.error(`sechedule: laborAttendance, error: ${error}`);
}
}
async function axyData (ctx, next) {
try {
let { type, url, params = {} } = ctx.request.body;
let data = await getAnxinyunToken(ctx)
if (url && url.indexOf('{orgId}') != -1) {
url = url.replace(`{orgId}`, data.orgId)
}
const res = await ctx.app.fs.anxinyun[type](`${url}?token=${data.token}`, { data: params.data || {}, query: params.query || {} })
ctx.status = 200;
ctx.body = res;
} catch (err) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
ctx.status = 400;
ctx.body = { name: 'FindError', message: '获取安心云数据失败' };
}
}
module.exports = {
axyData,
}

87
api/app/lib/controllers/organization/authority.js

@ -1,87 +0,0 @@
async function getResource(ctx, next) {
try {
const models = ctx.fs.dc.models;
const res = await models.Resource.findAll({
where: { parentResource: null },
include: [{
model: models.Resource,
}]
})
ctx.body = res;
ctx.status = 200;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "查询所有权限数据失败"
}
}
}
async function getUserResource(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { userId } = ctx.query;
const res = await models.UserResource.findAll({
where: { userId: userId },
include: [{
model: models.Resource,
}]
})
ctx.body = res;
ctx.status = 200;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "查询用户权限数据失败"
}
}
}
async function updateUserRes(ctx, next) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
const { userId, resCode } = ctx.request.body;
const res = await models.UserResource.findAll({
attributes: ["resourceId"],
raw: true,
where: { userId: userId }
})
const addRes = resCode.filter(r => !res.some(rr => rr.resourceId == r)).map(r => { return { userId: userId, resourceId: r } });
const delRes = res.filter(r => !resCode.includes(r.resourceId)).map(r => r.resourceId);
addRes.length && await models.UserResource.bulkCreate(addRes, { transaction: transaction });
delRes.length && await models.UserResource.destroy({
where: {
resourceId: { $in: delRes },
userId: userId
},
transaction: transaction
})
ctx.status = 204;
await transaction.commit();
} catch (error) {
await transaction.rollback();
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "更新用户权限数据失败"
}
}
}
module.exports = {
getResource,
getUserResource,
updateUserRes
};

389
api/app/lib/controllers/organization/user.js

@ -1,389 +0,0 @@
'use strict';
const Hex = require('crypto-js/enc-hex');
const MD5 = require('crypto-js/md5');
// async function getDepMessage (ctx, next) {
// try {
// const { fs: { api: { userInfo } } } = ctx
// const models = ctx.fs.dc.models;
// let deptWhere = {}
// if (userInfo.username !== 'SuperAdmin') {
// deptWhere.id = userInfo.departmentId
// }
// let depType1 = await models.Department.findAll({
// order: [['id', 'asc']],
// // include: [{
// // model: models.User,
// // required: false,
// // where: { delete: false },
// // attributes: { exclude: ['password'] },
// // }],
// where: deptWhere,
// })
// let depRslt = []
// const getDep = async (d) => {
// let subordinate = []
// let depRes = await models.Department.findAll({
// order: [['id', 'asc']],
// // include: [{
// // model: models.User,
// // required: false,
// // where: { delete: false },
// // attributes: { exclude: ['password'] },
// // }],
// where: {
// dependence: d.id
// },
// })
// if (depRes.length)
// for (let d of depRes) {
// let dep = d.dataValues
// dep.subordinate = await getDep(d.dataValues)
// subordinate.push(dep)
// }
// return subordinate
// }
// for (let d of depType1) {
// let dep_1 = d.dataValues
// dep_1.subordinate = await getDep(d.dataValues)
// depRslt.push(dep_1)
// }
// ctx.status = 200;
// ctx.body = depRslt
// } catch (error) {
// ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
// ctx.status = 400;
// ctx.body = {
// "message": "获取部门信息失败"
// }
// }
// }
async function getDepMessage(ctx, next) {
let error = { name: 'FindError', message: '获取部门列表失败' };
let rslt = [];
try {
const models = ctx.fs.dc.models;
let list = await models.Department.findAll({});
let deptMap = []
list.filter(l => !l.dependence).map(ld => {//一级
deptMap.push({
id: ld.id,
name: ld.name,
dependence: ld.dependence,
subordinate: []
})
})
list.filter(l => l.dependence).map(ld => {//二级
let parent = deptMap.find(dm => dm.id == ld.dependence);
if (parent) {
parent.subordinate.push({
id: ld.id,
name: ld.name,
dependence: ld.dependence,
subordinate: []
})
}
})
rslt = deptMap;
error = null;
} catch (err) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
}
if (error) {
ctx.status = 400;
ctx.body = error;
} else {
ctx.status = 200;
ctx.body = rslt;
}
}
async function createDept(ctx, next) {
const models = ctx.fs.dc.models;
try {
let rslt = ctx.request.body;
await models.Department.create(Object.assign({}, rslt, { read: 1 }))
ctx.status = 204;
ctx.body = { message: '新建部门成功' }
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '新建部门失败' }
}
}
async function updateDept(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { id } = ctx.params;
const body = ctx.request.body;
await models.Department.update(
body,
{ where: { id: id } }
)
ctx.status = 204;
ctx.body = { message: '修改部门成功' }
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '修改部门失败' }
}
}
async function delDept(ctx, next) {
let errMsg = "删除部门失败";
try {
const models = ctx.fs.dc.models;
const { id } = ctx.params;
let list = await models.Department.findAll({});
let deptIds = list.map(l => l.id);
const allUsers = await models.User.findAll({
where: {
departmentId: { $in: deptIds },
delete: false
}
})
const childrenDept = await models.Department.findAll({ where: { dependence: id } })
const childrenUser = allUsers.filter(au => au.departmentId == id);
if (childrenUser.length || childrenDept.length) {
errMsg = '请先删除部门下的用户或子部门';
throw errMsg;
}
await models.Department.destroy({ where: { id: id } });
await models.Department.destroy({ where: { dependence: id } });
ctx.status = 204;
ctx.body = { message: '删除部门成功' }
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: error }
}
}
async function getUser(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { depId } = ctx.params;
let userRes = null;
if (depId !== 'null') {
userRes = await models.User.findAll({
where: {
departmentId: parseInt(depId),
delete: false
},
attributes: { exclude: ['password'] },
order: [['id', 'asc']],
})
} else {
userRes = await models.User.findAll({
where: {
delete: false
},
attributes: { exclude: ['password'] },
order: [['id', 'asc']],
include: [{
required: true,
model: models.Department,
attributes: ['id', 'name'],
}]
})
}
ctx.status = 200;
ctx.body = userRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "获取用户信息失败"
}
}
}
async function creatUser(ctx, next) {
let errMsg = "新建用户失败"
try {
const models = ctx.fs.dc.models;
const data = ctx.request.body;
let repeatUserNameCount = await models.User.count({
where: {
username: data.phone,
delete: false
}
})
if (repeatUserNameCount) {
errMsg = '已有当前用户名'
throw errMsg
}
await models.User.create({
name: data.name,
username: data.phone,
password: Hex.stringify(MD5(data.password)),
departmentId: data.departmentId,
email: data.email,
enable: data.enable,
delete: false,
phone: data.phone,
post: data.post,
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": errMsg
}
}
}
async function updateUser(ctx, next) {
let errMsg = "修改用户失败"
try {
const models = ctx.fs.dc.models;
const data = ctx.request.body;
const { id } = ctx.params;
let repeatUserNameCount = await models.User.count({
where: {
username: data.username
}
})
if (repeatUserNameCount) {
errMsg = '已有当前用户名'
throw errMsg
}
await models.User.update({
name: data.name,
username: data.phone,
departmentId: data.departmentId,
email: data.email,
enable: data.enable,
delete: false,
phone: data.phone,
post: data.post,
}, {
where: {
id: id
}
});
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": errMsg
}
}
}
async function deleteUser(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { ids } = ctx.params;
const userIds = ids.split(',');
await models.User.update({
delete: true,
}, {
where: {
id: { $in: userIds },
}
});
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "删除用户失败"
}
}
}
async function resetPwd(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { id } = ctx.params;
const data = ctx.request.body;
await models.User.update({
password: Hex.stringify(MD5(data.password)),
}, {
where: {
id: id,
}
});
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "重置用户密码失败"
}
}
}
/**
* 修改用户密码
* @params {userId-用户Id} ctx
* @request.body {password-用户新密码} ctx
*/
async function updateUserPassword(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { userId } = ctx.params;
const { password } = ctx.request.body;
if (!password) {
ctx.status = 400;
ctx.body = { "message": "请输入修改密码" };
return;
}
const userRes = await models.User.findOne({
where: {
id: userId
},
attributes: ['id']
});
if (userRes) {
await models.User.update({ password: Hex.stringify(MD5(password)) }, { where: { id: userId, } });
ctx.status = 204;
} else {
ctx.status = 400;
ctx.body = {
"message": "用户不存在"
}
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "修改用户密码失败"
}
}
}
module.exports = {
getDepMessage,
createDept,
updateDept,
delDept,
getUser,
creatUser,
updateUser,
deleteUser,
resetPwd,
updateUserPassword
}

4
api/app/lib/index.js

@ -6,7 +6,7 @@ const utils = require('./utils')
const routes = require('./routes');
//const redisConnect = require('./service/redis')
const socketConect = require('./service/socket')
//const paasRequest = require('./service/paasRequest');
const paasRequest = require('./service/paasRequest');
const authenticator = require('./middlewares/authenticator');
//const clickHouseClient = require('./service/clickHouseClient')
const schedule = require('./schedule')
@ -26,7 +26,7 @@ module.exports.entry = function (app, router, opts) {
socketConect(app, opts)
// 实例其他平台请求方法
//paasRequest(app, opts)
paasRequest(app, opts)
// clickHouse 数据库 client
//clickHouseClient(app, opts)

28
api/app/lib/routes/organization/authority.js

@ -1,28 +0,0 @@
'use strict';
const Authority = require('../../controllers/organization/authority');
module.exports = function (app, router, opts) {
/**
* @api {GET} resource 查询所有权限码.
* @apiVersion 1.0.0
* @apiGroup Org
*/
app.fs.api.logAttr['GET/resource'] = { content: '查询所有权限码', visible: true };
router.get('resource', Authority.getResource);
/**
* @api {GET} user/resource 查询用户权限.
* @apiVersion 1.0.0
* @apiGroup Org
*/
app.fs.api.logAttr['GET/user/resource'] = { content: '查询用户权限', visible: true };
router.get('user/resource', Authority.getUserResource);
/**
* @api {POST} user/resource 更新用户权限.
* @apiVersion 1.0.0
* @apiGroup Org
*/
app.fs.api.logAttr['POST/user/resource'] = { content: '更新用户权限', visible: true };
router.post('user/resource', Authority.updateUserRes);
};

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

@ -0,0 +1,9 @@
'use strict';
const data = require('../../controllers/bigScreen/index ');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/axyData'] = { content: '获取安心云数据', visible: true };
router.post('/axyData', data.axyData);
};

41
api/app/lib/routes/organization/user.js

@ -1,41 +0,0 @@
'use strict';
const user = require('../../controllers/organization/user');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/organization/department'] = { content: '获取部门信息', visible: false };
router.get('/organization/department', user.getDepMessage);
app.fs.api.logAttr['POST/organization/dept/add'] = { content: '新增部门', visible: true };
router.post('/organization/dept/add', user.createDept);
app.fs.api.logAttr['PUT/organization/dept/:id/modify'] = { content: '修改部门', visible: true };
router.put('/organization/dept/:id/modify', user.updateDept);
app.fs.api.logAttr['DELETE/organization/dept/:id'] = { content: '删除部门', visible: true };
router.del('/organization/dept/:id', user.delDept);
app.fs.api.logAttr['GET/organization/department/:depId/user'] = { content: '获取部门下用户信息', visible: false };
router.get('/organization/department/:depId/user', user.getUser);
app.fs.api.logAttr['POST/organization/department/user'] = { content: '创建部门下用户信息', visible: false };
router.post('/organization/department/user', user.creatUser);
app.fs.api.logAttr['PUT/organization/department/user/:id'] = { content: '修改部门下用户信息', visible: false };
router.put('/organization/department/user/:id', user.updateUser);
app.fs.api.logAttr['DEL/organization/department/user/:ids'] = { content: '删除部门下用户信息', visible: false };
router.del('/organization/department/user/:ids', user.deleteUser);
app.fs.api.logAttr['PUT/organization/department/user/resetPwd/:id'] = { content: '重置用户密码', visible: false };
router.put('/organization/department/user/resetPwd/:id', user.resetPwd);
/**
* @api {PUT} user/password/:id 修改用户密码
* @apiVersion 1.0.0
* @apiGroup Organization
*/
app.fs.api.logAttr['PUT/user/password/:userId'] = { content: '修改用户密码', visible: false };
router.put('/user/password/:userId', user.updateUserPassword);
};

53
api/app/lib/schedule/clearExpiredData.js

@ -1,53 +0,0 @@
const moment = require('moment')
const rimraf = require('rimraf');
const fs = require("fs");
const path = require("path")
let TEST = false
// TEST = true
module.exports = function (app, opts) {
const clearExpiredData = app.fs.scheduleInit(
{
interval: '42 24 4 */3 * *',
immediate: TEST,
proRun: !TEST,
},
async () => {
try {
const { models } = app.fs.dc
const now = moment().format('YYYY-MM-DD HH:mm:ss')
await models.UserToken.destroy({
where: {
expired: { $lt: now }
}
})
await models.PhoneValidateCode.destroy({
where: {
expired: { $lt: now }
}
})
fs.readdir(path.join(__dirname, `../../downloadFiles`), function (err, files) {
if (err) {
return;
}
files.forEach((file) => {
fs.stat(path.join(__dirname, `../../downloadFiles/${file}`), (err, stats) => {
if (err) {
//return;
} else {
rimraf.sync(path.join(__dirname, `../../downloadFiles/${file}`));
}
});
});
});
} catch (error) {
app.fs.logger.error(`sechedule: clearExpiredToken, error: ${error}`);
}
}
);
return {
clearExpiredData
}
}

208
api/app/lib/schedule/hideDangerStatistic.js

@ -1,208 +0,0 @@
const fs = require('fs');
const moment = require('moment')
const path = require('path')
const OSS = require('ali-oss');
const uuid = require('uuid');
const TEST = false
// const TEST = true
module.exports = function (app, opts) {
const hideDangerStatistic = app.fs.scheduleInit(
// 按月、季度、年统计隐患整改
{
interval: '0 32 4 1 */1 *',
immediate: TEST,
proRun: !TEST,
},
async () => {
const { aliOss } = opts
const { utils: { simpleExcelDown } } = app.fs;
try {
const { models } = app.fs.dc
const today = moment()
const date = today.date()
const month = today.month() + 1
const quarter = today.quarter()
const year = today.year()
const client = new OSS({
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: aliOss.region,
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
accessKeyId: aliOss.accessKey,
accessKeySecret: aliOss.secretKey,
// 填写Bucket名称,例如examplebucket。
bucket: aliOss.bucket,
});
const statistic = async (startTime, endTime, type, timeShow, typeEnglish, time) => {
const siteRectifyRes = await models.HideDangerRectifySites.findAll({
where: {},
distinct: true,
include: [{
model: models.Site,
attributes: ['id', 'name'],
}, {
model: models.HideDangerDispose,
order: [['id', 'ASC']],
// include: [{
// model: models.User,
// attributes: ['id', 'displayName'],
// }]
}, {
model: models.HideDangerRectify,
where: {
createTime: {
$between: [
startTime.format('YYYY-MM-DD HH:mm:ss'),
endTime.format('YYYY-MM-DD HH:mm:ss')
]
}
},
},]
})
let reportHeader = [{
title: "工程项目名称",
key: "siteName",
}, {
title: "整改任务名称",
key: "name",
}, {
title: "提交时间",
key: "submitTime",
}, {
title: "审批状态",
key: "state",
},]
let reportData = []
let siteMap = new Set()
let completedRectifyCount = 0
let uncompletedRectifyCount = 0
for (let s of siteRectifyRes) {
siteMap.add(s.siteId);
let sts = s.status
let stsChinese = ''
if (sts == 0) {
stsChinese = '待整改'
} else if (sts == 1) {
stsChinese = '待审批'
} else if (sts == 2) {
stsChinese = '待复审'
} else if (sts == 3 || sts == 4) {
stsChinese = '审批驳回'
} else if (sts == 5) {
stsChinese = '审批通过'
}
if (
s.hideDangerDisposes.length
&& s.hideDangerDisposes.some(sd => sd.type == 3 && sd.admit)
) {
completedRectifyCount++
} else {
uncompletedRectifyCount++
}
reportData.push({
siteName: s.dataValues.site.dataValues.name,
name: s.dataValues.hideDangerRectify.dataValues.name,
submitTime: s.dataValues.lastDisposeTime ? moment(s.dataValues.lastDisposeTime).format('YYYY-MM-DD HH:mm:ss') : '',
state: stsChinese
})
}
const fileName = `中鼎国际隐患整改数据报表-${type}-${timeShow}` + '.xlsx'
const filePath = await simpleExcelDown({
data: reportData, header: reportHeader, fileName
})
// const fileData = fs.readFileSync(filePath);
// 保存文件到云
let uploadPath = path.posix.join('hideDangerReport', uuid.v4(), fileName);
let uploadResult = await client.put(
uploadPath,
filePath,
// { contentLength: size }
);
//保存信息到数据库
const existReportRes = await models.HideDangerReport.findOne({
where: {
type: typeEnglish,
time: String(time),
}
})
const storageData = {
siteCount: siteMap.size,
rectifyCount: completedRectifyCount + uncompletedRectifyCount,
completedRectifyCount,
uncompletedRectifyCount,
report: uploadResult.name,
type: typeEnglish,
time: String(time),
}
if (existReportRes) {
await models.HideDangerReport.update(storageData, {
where: {
id: existReportRes.id
}
})
} else {
await models.HideDangerReport.create(storageData)
}
}
if (month == 1) {
// 统计一下上一年
let startTime = today.clone().subtract(1, 'year').startOf('year')
let endTime = today.clone().subtract(1, 'year').endOf('year')
await statistic(
startTime,
endTime,
'年报',
`${startTime.year()}${startTime.month() + 1}-${endTime.month() + 1}`,
'year',
startTime.year()
)
}
if ([1, 4, 7, 10].includes(month)) {
// 统计一下上季度
let startTime = today.clone().subtract(3, 'month').startOf('month')
let endTime = today.clone().subtract(1, 'month').endOf('month')
await statistic(
startTime,
endTime,
'季报',
`${startTime.year()}${startTime.month() + 1}-${endTime.month() + 1}`,
'quarter',
`${startTime.year()}-${month == 1 ? 'Q4' : month == 4 ? 'Q1' : month == 7 ? 'Q2' : 'Q3'}`
)
}
// 统计一下上个月
let startTime = today.clone().subtract(1, 'month').startOf('month')
let endTime = today.clone().subtract(1, 'month').endOf('month')
await statistic(
startTime,
endTime,
'月报',
`${startTime.year()}${startTime.month() + 1}`,
'month',
startTime.format('YYYY-MM')
)
} catch (error) {
app.fs.logger.error(`sechedule: hideDangerStatistic, error: ${error}`);
}
}
);
return {
hideDangerStatistic
}
}

64
api/app/lib/schedule/metting.js

@ -1,64 +0,0 @@
const moment = require('moment')
const TEST = false
// const TEST = true
module.exports = function (app, opts) {
const mettingGenerate = app.fs.scheduleInit(
{
interval: '0 0 0 */1 * *',
immediate: TEST,
proRun: !TEST,
},
async () => {
try {
const { models } = app.fs.dc
const today = moment()
const date = today.date()
const dateFormat = moment().format('YYYY-MM-DD')
let sites = await models.Site.findAll({
where: { del: false },
attributes: ['id', 'name']
});
let datasM = [], datasB = [], datasD = [], datas6 = []
sites.map(s => {
datasM.push({
siteId: s.id,
type: '月度安全例会',
date: dateFormat
});
datasB.push({
siteId: s.id,
type: '班前会',
date: dateFormat
});
datasD.push({
siteId: s.id,
type: '日调度会',
date: dateFormat
});
datas6.push({
siteId: s.id,
type: '逢六教育培训',
date: dateFormat
})
})
if (date == 1) {
await models.Metting.bulkCreate(datasM)
}
await models.Metting.bulkCreate(datasB)
await models.Metting.bulkCreate(datasD)
if (parseInt(date) % 10 == 6) {
await models.Metting.bulkCreate(datas6)
}
} catch (error) {
app.fs.logger.error(`sechedule: mettingGenerate, error: ${error}`);
}
}
);
return {
mettingGenerate
}
}

65
api/config.js

@ -11,34 +11,18 @@ const dev = process.env.NODE_ENV == 'development';
args.option(['p', 'port'], '启动端口');
args.option(['g', 'pg'], 'postgre 服务 URL');
// 七牛云存储参数
args.option('qnak', 'qiniuAccessKey');
args.option('qnsk', 'qiniuSecretKey');
args.option('qnbkt', 'qiniuBucket');
args.option('qndmn', 'qiniuDomain');
args.option('aliOssAccessKey', '阿里OSS AccessKey');
args.option('aliOssSecretKey', '阿里OSS SecretKey');
args.option('aliOssBucket', '阿里OSS Bucket');
args.option('aliOssRegion', '阿里OSS Region');
args.option('apiAnxinyunUrl', "安心云api");
args.option('axyProject', '安心云泵站项目信息');
const flags = args.parse(process.argv);
const XUNJIAN_DB = process.env.XUNJIAN_DB || flags.pg;
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.apiAnxinyunUrl
const AXY_BZ_PROJECT = process.env.AXY_BZ_PROJECT || flags.axyProject
// 七牛云存储参数
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;
//阿里OSS
const ALI_OSS_ACCESSKEY = process.env.ALI_OSS_ACCESSKEY || flags.aliOssAccessKey;
const ALI_OSS_SECRETKET = process.env.ALI_OSS_SECRETKET || flags.aliOssSecretKey;
const ALI_OSS_BUCKET = process.env.ALI_OSS_BUCKET || flags.aliOssBucket;
const ALI_OSS_REGION = process.env.ALI_OSS_REGION || flags.aliOssRegion;
if (!XUNJIAN_DB || !QINIU_DOMAIN_QNDMN_RESOURCE || !QINIU_BUCKET_RESOURCE || !QINIU_AK || !QINIU_SK) {
if (!XUNJIAN_DB || !API_ANXINYUN_URL || !AXY_BZ_PROJECT) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
@ -51,12 +35,6 @@ const product = {
{
entry: require('@fs/attachment').entry,
opts: {
qiniu: {
domain: QINIU_DOMAIN_QNDMN_RESOURCE,
bucket: QINIU_BUCKET_RESOURCE,
accessKey: QINIU_AK,
secretKey: QINIU_SK
},
maxSize: 104857600, // 100M
}
}, {
@ -66,33 +44,12 @@ const product = {
exclude: [
// "*"
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
qiniu: {
domain: QINIU_DOMAIN_QNDMN_RESOURCE,
bucket: QINIU_BUCKET_RESOURCE,
accessKey: QINIU_AK,
secretKey: QINIU_SK
},
aliOss: {
accessKey: ALI_OSS_ACCESSKEY,
secretKey: ALI_OSS_SECRETKET,
bucket: ALI_OSS_BUCKET,
region: ALI_OSS_REGION
},
sms: {
///阿里云-安心云
accessKey: 'LTAI5tAFdjz7j38aNF2C9Qe8',
accessSecret: '1trYkmiqfBtvZL6BxkNH2uQcQQPs0S'
},
email: {
enabled: true,
host: 'smtp.exmail.qq.com',
port: 465,
sender: {
name: '中鼎服务',
address: 'fsiot@free-sun.com.cn',
password: 'Fs2689'
}
},
// apiAnxinyunUrl: API_ANXINYUN_URL,
axyProject: AXY_BZ_PROJECT,
pssaRequest: [{// name 会作为一个 request 出现在 ctx.app.fs
name: 'anxinyun',
root: API_ANXINYUN_URL
}],
}
}
],

BIN
web/client/assets/images/login/button-b.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
web/client/assets/images/login/icon_y.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
web/client/assets/images/login/icon_z.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
web/client/assets/images/login/input-b.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
web/client/assets/images/login/login-b.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
web/client/assets/images/login/login_a.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

BIN
web/client/assets/images/login/login_b.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

BIN
web/client/assets/images/login/register-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
web/client/assets/images/login/word.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
web/client/assets/images/monitor/ball-A.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
web/client/assets/images/monitor/ball-V.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
web/client/assets/images/monitor/bg-header.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 297 KiB

BIN
web/client/assets/images/monitor/headerTitle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
web/client/assets/images/monitor/pedestal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
web/client/assets/images/monitor/pump-head.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
web/client/assets/images/monitor/site.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
web/client/assets/images/monitor/title.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

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

@ -36,7 +36,9 @@ export function initApiRoot () {
dispatch({
type: INIT_API_ROOT,
payload: {
apiRoot: res.root
apiRoot: res.root,
axyApi: res.axyApi,
axyProject: res.axyProject,
}
})
});

99
web/client/src/layout/reducers/global.js

@ -1,52 +1,61 @@
'use strict';
import Immutable from 'immutable';
import { INIT_LAYOUT, RESIZE } from '../actions/global';
import { INIT_LAYOUT, RESIZE,INIT_API_ROOT } from '../actions/global';
import { SET_GLOBAL_SITE_LIST, CLEAR_GLOBAL_SITE_LIST } from '../actions/site'
function global(state = {
title: '',
copyright: '',
sections: [],
actions: {},
plugins: {},
clientHeight: 768,
clientWidth: 1024,
sites: []
function global (state = {
title: '',
copyright: '',
sections: [],
actions: {},
plugins: {},
clientHeight: 768,
clientWidth: 1024,
sites: [],
axyApi: '',
axyProject: ''
}, action) {
const payload = action.payload;
switch (action.type) {
case RESIZE:
return Immutable.fromJS(state).merge({
clientHeight: payload.clientHeight,
clientWidth: payload.clientWidth
}).toJS();
case INIT_LAYOUT:
return {
title: payload.title,
copyright: payload.copyright,
sections: payload.sections,
actions: payload.actions,
plugins: payload.plugins,
clientHeight: state.clientHeight,
detailsComponent: null,
sites: []
};
// case INIT_RESOURCE_ROOT:
// return Immutable.fromJS(state).merge(payload).toJS();
// case INIT_PAGE_HEADER_DETAILS:
// return Immutable.fromJS(state).merge({
// detailsComponent: payload.component
// }).toJS();
case SET_GLOBAL_SITE_LIST:
return Immutable.fromJS(state).merge({
sites: payload.data
}).toJS();
case CLEAR_GLOBAL_SITE_LIST:
return Immutable.fromJS(state).merge({
sites: []
}).toJS();
default:
return state;
}
const payload = action.payload;
switch (action.type) {
case RESIZE:
return Immutable.fromJS(state).merge({
clientHeight: payload.clientHeight,
clientWidth: payload.clientWidth
}).toJS();
case INIT_LAYOUT:
return {
title: payload.title,
copyright: payload.copyright,
sections: payload.sections,
actions: payload.actions,
plugins: payload.plugins,
clientHeight: state.clientHeight,
detailsComponent: null,
sites: [],
axyApi: payload.axyApi,
axyProject: payload.axyProject,
};
case INIT_API_ROOT:
return Immutable.fromJS(state).merge({
axyApi: payload.axyApi,
axyProject: payload.axyProject,
}).toJS();
// case INIT_RESOURCE_ROOT:
// return Immutable.fromJS(state).merge(payload).toJS();
// case INIT_PAGE_HEADER_DETAILS:
// return Immutable.fromJS(state).merge({
// detailsComponent: payload.component
// }).toJS();
case SET_GLOBAL_SITE_LIST:
return Immutable.fromJS(state).merge({
sites: payload.data
}).toJS();
case CLEAR_GLOBAL_SITE_LIST:
return Immutable.fromJS(state).merge({
sites: []
}).toJS();
default:
return state;
}
}
export default global;

88
web/client/src/sections/auth/actions/auth.js

@ -1,62 +1,66 @@
'use strict';
import { ApiTable } from '$utils'
import { ApiTable, AxyRequest, AxyApiTable } from '$utils'
import { Request } from '@peace/utils'
import superagent from "superagent"
// import config from "../../../../../config"
export const INIT_AUTH = 'INIT_AUTH';
export function initAuth () {
const user = JSON.parse(sessionStorage.getItem('user')) || {};
return {
type: INIT_AUTH,
payload: {
user: user
}
};
const user = JSON.parse(sessionStorage.getItem('user')) || {};
return {
type: INIT_AUTH,
payload: {
user: user
}
};
}
export const REQUEST_LOGIN = 'REQUEST_LOGIN';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_ERROR = 'LOGIN_ERROR';
export function login ({ username, password, phone, code }) {
return dispatch => {
dispatch({ type: REQUEST_LOGIN });
return Request.post(ApiTable.login, { username, password, phone, code })
.then(user => {
sessionStorage.setItem('user', JSON.stringify(user));
dispatch({
type: LOGIN_SUCCESS,
payload: { user: user },
});
}, error => {
let { body } = error.response;
dispatch({
type: LOGIN_ERROR,
payload: {
error: body && body.message ? body.message : '登录失败'
}
})
export function login ({ username, password, phone, code, axyApi, axyProject }) {
return dispatch => {
dispatch({ type: REQUEST_LOGIN });
return Request.post(ApiTable.login, { username, password, phone, code })
.then(user => {
sessionStorage.setItem('user', JSON.stringify(user));
dispatch({
type: LOGIN_SUCCESS,
payload: { user: user },
});
}
}, error => {
let { body } = error.response;
dispatch({
type: LOGIN_ERROR,
payload: {
error: body && body.message ? body.message : '登录失败'
}
})
});
}
}
export const LOGOUT = 'LOGOUT';
export function logout (user) {
const token = user.token;
const url = ApiTable.logout;
sessionStorage.removeItem('user');
localStorage.removeItem('zhongding_selected_sider')
localStorage.removeItem('zhongding_open_sider')
Request.put(url, {
token: token
});
return {
type: LOGOUT
};
const token = user.token;
const url = ApiTable.logout;
sessionStorage.removeItem('user');
localStorage.removeItem('zhongding_selected_sider')
localStorage.removeItem('zhongding_open_sider')
Request.put(url, {
token: token
});
return {
type: LOGOUT
};
}
export default {
initAuth,
login,
logout
initAuth,
login,
logout
}

106
web/client/src/sections/auth/containers/login.js

@ -11,7 +11,7 @@ import { login, LOGIN_ERROR } from '../actions/auth';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Uploads } from '$components'
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import '../style.less';
import './login.less';
const FormItem = Form.Item;
@ -74,7 +74,7 @@ const Login = props => {
alignItems: 'center',
justifyContent: 'center',
color: 'aliceblue',
backgroundImage: 'url(/assets/images/login/login_b.png)',
backgroundImage: 'url(/assets/images/login/login-b.gif)',
backgroundSize: 'cover',
position: 'relative',
}}
@ -82,66 +82,86 @@ const Login = props => {
{/* <img src='/assets/images/logo.png' style={{ height: 42, borderRadius: 4, position: 'fixed', top: 32, left: 32 }} /> */}
<div style={{
width: 556, height: 434, backgroundColor: '#rgba(255,255,255,0.50)',
borderRadius: '2px 2px 0 0', boxShadow: 'inset 0 0 8px 0 rgba(50,131,255,0.25)',
display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'absolute', right: 150, top: 'calc(50% - 217px)'
width: 556, height: 554, display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
borderRadius: '2px 2px 0 0', position: 'absolute', right: 150, top: 'calc(50% - 217px)'
}}>
<div style={{
width: 410,
height: 322,
backgroundColor: ''
}}>
<img src={'/assets/images/login/login_a.png'} style={{ width: 124, height: 37, display: 'inline-block', marginBottom: 40 }} />
<Form
form={form}
onFinish={r => {
form.validateFields().then(r => {
dispatch(login({ username: r.username, password: r.password }));
})
.catch(err => {
dispatch({
type: LOGIN_ERROR,
payload: { error: '请输入账号名和密码' }
})
<img src={'/assets/images/login/word.png'} style={{ width: '100%', height: 80, dispatch: 'inline-block' }} />
<div className='login' style={{ width: 556, height: 434, background: 'url(/assets/images/login/register-bg.png)', backgroundSize: '100% 100%', backgroundPosition: 'center', }}>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', margin: '22px 10px' }}>
<img src={'/assets/images/login/icon_z.png'} style={{ width: 26, height: 14 }} />
<div style={{ color: '#C5E0FF', fontSize: 24 }}>系统登录</div>
<img src={'/assets/images/login/icon_y.png'} style={{ width: 26, height: 14 }} />
</div>
<div style={{
// width: 410,
// height: 322,
}}>
<Form
form={form}
onFinish={r => {
form.validateFields().then(r => {
dispatch(login({ username: r.username, password: r.password}));
})
.catch(err => {
dispatch({
type: LOGIN_ERROR,
payload: { error: '请输入账号名和密码' }
})
})
}}
>
<Form.Item label='' name="username" rules={[{ required: true, message: '请输入用户名' },]}>
<Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="用户名" />
</Form.Item>
<Form.Item label='' name="password" rules={[{ required: true, message: '请输入密码' },]}>
<Input.Password prefix={<LockOutlined className="site-form-item-icon" />} placeholder="密码" />
</Form.Item>
<Tooltip title='请联系管理员'>
<a style={{ position: 'relative', left: 348, top: -17 }}>忘记密码</a>
</Tooltip>
<Form.Item
}}
style={{ width: '100%', height: 400, display: 'flex', alignItems: 'center', flexDirection: 'column' }}
>
<Button type="primary" htmlType="submit" style={{ width: 410, height: 50 }}>
登录
</Button>
</Form.Item>
</Form>
<Form.Item label='' name="username" rules={[{ required: true, message: '请输入账户' },]} style={{ marginTop: 50 }}>
<Input prefix={<>
<UserOutlined style={{ marginLeft: 20, color: 'white', fontSize: 18 }} />
<span style={{ color: 'white' }}>账户</span>
<span style={{ color: 'white', marginRight: 10, height: 18, borderRight: '1px solid white' }}></span>
</>} style={{ width: 380, margin: '6px 0px 6px 0' }} placeholder="" />
</Form.Item>
<Form.Item label='' name="password" rules={[{ required: true, message: '请输入密码' },]}>
<Input.Password prefix={<>
<LockOutlined style={{ marginLeft: 20, color: 'white', fontSize: 18 }} />
<span style={{ color: 'white' }}>密码</span>
<span style={{ color: 'white', marginRight: 10, height: 18, borderRight: '1px solid white' }}></span>
</>} style={{ width: 380, margin: '6px 0' }} placeholder="" />
</Form.Item>
<Form.Item
>
<Button type="primary" htmlType="submit" style={{ width: 300, height: 50, marginRight: 6, fontSize: 18, border: 0, background: 'url(/assets/images/login/button-b.png)', backgroundSize: '100% 100%', backgroundPosition: 'center', }}>
立即登录
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
</div >
);
}
function mapStateToProps (state) {
const { auth } = state;
const { auth, global } = state;
console.log(global);
return {
user: auth.user,
error: auth.error,
isRequesting: auth.isRequesting
isRequesting: auth.isRequesting,
}
}

28
web/client/src/sections/auth/containers/login.less

@ -0,0 +1,28 @@
.login {
.ant-form-item-control-input {
background: url(/assets/images/login/input-b.png);
background-size: 100% 108%;
background-position: center;
.ant-input-affix-wrapper,
.ant-input-affix-wrapper-status-success,
.ant-input-affix-wrapper-status-error {
background: transparent;
border: 0;
#username,#password {
background: transparent;
color: white;
}
}
.ant-input-affix-wrapper-status-error:not(
.ant-input-affix-wrapper-disabled
):not(.ant-input-affix-wrapper-borderless).ant-input-affix-wrapper,
.ant-input-affix-wrapper-status-error:not(
.ant-input-affix-wrapper-disabled
):not(.ant-input-affix-wrapper-borderless).ant-input-affix-wrapper:hover {
background: transparent;
}
.ant-input-password-icon{
color: white;
}
}
}

51
web/client/src/sections/bigScreen/actions/authority.js

@ -1,51 +0,0 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getAuthority(orgId) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_MEMBERS',
url: `${ApiTable.getEnterprisesMembers.replace('{enterpriseId}', orgId)}`,
msg: { error: '获取用户列表失败' },
reducer: { name: 'members' }
});
}
export function getResource(userId) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_RESOURCE',
url: `${ApiTable.getResource}`,
msg: { error: '获取权限失败' },
reducer: { name: 'resource' }
});
}
export function getUserResource(userId) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_USER_RESOURCE',
url: `${ApiTable.getUserResource}?userId=${userId}`,
msg: { error: '获取用户权限失败' },
reducer: { name: 'userResource' }
});
}
export function postUserRes(body) {
return dispatch => basicAction({
type: 'post',
dispatch: dispatch,
actionType: 'UPDATE_USER_RESOURCE',
url: `${ApiTable.postUserRes}`,
data: body,
msg: { success: '更新用户权限' }
});
}
export default {
getAuthority,
getResource,
getUserResource,
postUserRes
}

19
web/client/src/sections/bigScreen/actions/bigScreen.js

@ -0,0 +1,19 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function axyData (data = {}) {
return dispatch => basicAction({
type: 'post',
dispatch: dispatch,
actionType: 'POST_AXY_DATA',
url: `${ApiTable.axyData}`,
data: data,
msg: { success: '' }
});
}
export default {
axyData
}

9
web/client/src/sections/bigScreen/actions/index.js

@ -1,11 +1,8 @@
'use strict';
import * as authority from './authority'
import { getDepMessage, getDepUser, createUser } from './user'
import * as bigScreen from './bigScreen'
export default {
...authority,
getDepMessage,
getDepUser,
createUser,
...bigScreen
}

113
web/client/src/sections/bigScreen/actions/user.js

@ -1,113 +0,0 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getDepMessage() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_DEPARTMENT_MESSAGE',
url: ApiTable.getDepMessage,
msg: { error: '获取部门信息失败' },
reducer: { name: 'depMessage' }
});
}
//新建部门
export function createDept(data) {
return dispatch => basicAction({
type: 'post',
data,
dispatch: dispatch,
actionType: 'CREATE_DEPT',
url: ApiTable.createDept,
msg: { option: '新建部门' },
});
}
//修改部门
export function updateDept(id, data) {
return dispatch => basicAction({
type: 'put',
data,
dispatch: dispatch,
actionType: 'UPDATE_DEPT',
url: ApiTable.updateDept.replace('{id}', id),
msg: { option: '修改部门' },
});
}
//删除部门
export function delDept(id) {
return dispatch => basicAction({
type: 'del',
dispatch: dispatch,
actionType: 'DEL_DEPT',
url: ApiTable.delDept.replace('{id}', id),
msg: { option: '删除部门' },
});
}
export function getDepUser(depId) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_DEPARTMENT_USER',
url: ApiTable.getDepUser.replace('{depId}', depId),
msg: { error: '获取部门下用户信息失败' },
reducer: { name: 'depUser' }
});
}
export function createUser(data) {
return dispatch => basicAction({
type: 'post',
data,
dispatch: dispatch,
actionType: 'CREATE_DEPARTMENT_USER',
url: ApiTable.createUser,
msg: { option: '新建用户' },
});
}
export function updateUser(id, data) {
return dispatch => basicAction({
type: 'put',
data,
dispatch: dispatch,
actionType: 'UPDATE_DEPARTMENT_USER',
url: ApiTable.updateUser.replace('{id}', id),
msg: { option: '修改用户' },
});
}
export function delUser(ids) {
return dispatch => basicAction({
type: 'del',
dispatch: dispatch,
actionType: 'DEL_DEPARTMENT_USER',
url: ApiTable.delUser.replace('{ids}', ids),
msg: { option: '删除用户' },
});
}
export function resetPwd(id, data) {
return dispatch => basicAction({
type: 'put',
data,
dispatch: dispatch,
actionType: 'CREATE_DEPARTMENT_USER',
url: ApiTable.resetPwd.replace('{id}', id),
msg: { option: '重置用户密码' },
});
}
export default {
getDepMessage,
getDepUser,
createUser,
updateUser,
delUser,
resetPwd
}

665
web/client/src/sections/bigScreen/components/capacity.js

@ -1,48 +1,673 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import { Spin, Card, Modal, Select, Carousel, Progress } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
import Title from 'antd/lib/skeleton/Title';
import moment from 'moment'
import ReactECharts from 'echarts-for-react';
import './index.less'
const Capacity = ({ user, }) => {
const Capacity = ({ actions, dispatch, user, }) => {
const { bigScreen } = actions
const [pageLeft, setPageLeft] = useState(0) //左边翻页
const [pageRight, setPageRight] = useState(0) //左边翻页
const [siteList, setSiteList] = useState([]) //站点列表
const [strucId, setStrucId] = useState() //站点ID
const [pumpData, setPumpData] = useState([]) //水泵数据
const [cabinetData, setCabinetData] = useState([]) //进线柜数据
const [centreData, setCentreData] = useState({}) //中间数据
useEffect(() => {
dispatch(bigScreen.axyData({ type: 'get', url: `organizations/{orgId}/structures` })).then(res => {
// console.log(res);
if (res.success) {
setSiteList(res.payload.data?.map(v => ({ value: v.id, label: v.name })) || [])
setStrucId(res.payload.data[0]?.id)
}
})
}, [])
useEffect(async () => {
let pump = []
let cabinetSun = []
let pumpSun = []
let sun = {}
if (strucId) {
await dispatch(bigScreen.axyData({ type: 'get', url: `structures/${strucId}/factors` })).then(async r => {
if (r.success) {
//水泵信息
let waterId = r.payload.data?.find(v => v.name == '泵站水泵')?.id
if (waterId) {
await dispatch(bigScreen.axyData({
type: 'get', url: `structures/${strucId}/stations`,
params: { query: { factorId: waterId } }
})).then(async p => {
if (p.success) {
let dataId = []
p.payload.data?.map(v => {
v.groups?.map(s => {
s.stations?.map(f => {
dataId.push(f.id)
})
})
})
if (dataId.length) {
let sameDay //当天最新一条数据
let beforeDay //前一天最新一条数据
let month //上个月最新一条数据
let year //去年最新一条数据
// 当前时间
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().startOf('day').format('YYYY:MM:DD HH:mm:ss'),
endTime: moment().endOf('day').format('YYYY:MM:DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
pump = d.payload.data?.stations || []
sameDay = d.payload.data?.stations || []
}
})
// 昨天最后一条数据
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().day(moment().day() - 7).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().day(moment().day() - 1).endOf('day').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
beforeDay = d.payload.data?.stations || []
}
})
// 上月最后一条数据
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().month(moment().month() - 1).startOf('month').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().month(moment().month() - 1).endOf('month').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
month = d.payload.data?.stations || []
}
})
//去年最后一条数据
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().year(moment().year() - 1).month(moment().month() - 1).startOf('year').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().year(moment().year() - 1).month(moment().month() - 1).endOf('year').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
year = d.payload.data?.stations || []
}
})
sameDay?.map(u => {
let find1 = beforeDay?.find(c => c.id == u.id)
let find2 = month?.find(c => c.id == u.id)
let find3 = year?.find(c => c.id == u.id)
return <div style={{ width: '100%', height: 'calc(100% - 160px)' }}>
// console.log(find1);
pumpSun.push({
today: u?.data[0]?.eMotor_EQ && find1?.data[0]?.eMotor_EQ ? (u?.data[0]?.eMotor_EQ - find1?.data[0]?.eMotor_EQ) : '--',
sameMonth: u?.data[0]?.eMotor_EQ && find2?.data[0]?.eMotor_EQ ? (u?.data[0]?.eMotor_EQ - find2?.data[0]?.eMotor_EQ) : '--',
thisYear: u?.data[0]?.eMotor_EQ && find3?.data[0]?.eMotor_EQ ? (u?.data[0]?.eMotor_EQ - find3?.data[0]?.eMotor_EQ) : '--',
eMotor_EQ: u?.data[0]?.eMotor_EQ,
id: u.id,
name: u.name,
})
})
}
}
})
}
//进线柜
let wireCabinetId = r.payload.data?.find(v => v.name == '泵站进线柜')?.id
if (wireCabinetId) {
await dispatch(bigScreen.axyData({
type: 'get', url: `structures/${strucId}/stations`,
params: { query: { factorId: wireCabinetId } }
})).then(async p => {
if (p.success) {
let dataId = []
p.payload.data?.map(v => {
v.groups?.map(s => {
s.stations?.map(f => {
dataId.push(f.id)
})
})
})
if (dataId.length) {
let sameDay //当天最新一条数据
let beforeDay //前一天最新一条数据
let month //上个月最新一条数据
let year //去年最新一条数据
// 当前时间
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
sameDay = d.payload.data?.stations || []
}
})
// 昨天最后一条数据
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().day(moment().day() - 7).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().day(moment().day() - 1).endOf('day').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
beforeDay = d.payload.data?.stations || []
}
})
// 上月最后一条数据
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().month(moment().month() - 1).startOf('month').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().month(moment().month() - 1).endOf('month').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
month = d.payload.data?.stations || []
}
})
//去年最后一条数据
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().year(moment().year() - 1).month(moment().month() - 1).startOf('year').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().year(moment().year() - 1).month(moment().month() - 1).endOf('year').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
year = d.payload.data?.stations || []
}
})
sameDay?.map(u => {
let find1 = beforeDay?.find(c => c.id == u.id)
let find2 = month?.find(c => c.id == u.id)
let find3 = year?.find(c => c.id == u.id)
// console.log(find1);
cabinetSun.push({
today: u?.data[0]?.eQF_EQ && find1?.data[0]?.eQF_EQ ? (u?.data[0]?.eQF_EQ - find1?.data[0]?.eQF_EQ) : '--',
sameMonth: u?.data[0]?.eQF_EQ && find2?.data[0]?.eQF_EQ ? (u?.data[0]?.eQF_EQ - find2?.data[0]?.eQF_EQ) : '--',
thisYear: u?.data[0]?.eQF_EQ && find3?.data[0]?.eQF_EQ ? (u?.data[0]?.eQF_EQ - find3?.data[0]?.eQF_EQ) : '--',
eQF_EQ: u?.data[0]?.eQF_EQ,
id: u.id,
name: u.name,
sQF_CLOSING: u?.data[0]?.sQF_CLOSING
})
})
}
}
})
}
//泵站信息
let informationId = r.payload.data?.find(v => v.name == '泵站信息')?.id
if (informationId) {
await dispatch(bigScreen.axyData({
type: 'get', url: `structures/${strucId}/stations`,
params: { query: { factorId: informationId } }
})).then(async p => {
if (p.success) {
let dataId = []
p.payload.data?.map(v => {
v.groups?.map(s => {
s.stations?.map(f => {
dataId.push(f.id)
})
})
})
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: dataId.join(),
startTime: moment().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
sun.sHumidity = d.payload.data?.stations[0]?.data[0]?.sHumidity
sun.sTEMP = d.payload.data?.stations[0]?.data[0]?.sTEMP
sun.sGrille_level = d.payload.data?.stations[0]?.data[0]?.sGrille_level
}
})
}
})
}
}
})
}
await dispatch(bigScreen.axyData({
type: 'get', url: `stations/theme/data`, params: {
query: {
stations: 56643,
startTime: moment().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
endTime: moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'),
limit: 1
}
}
})).then(d => {
if (d.success) {
}
})
setPumpData(pump)
setCabinetData(cabinetSun)
//计算各个阶段的总点电量
let day1 = 0
let day30 = 0
let day365 = 0
let daySun = 0
pumpSun?.map(h => {
if (!isNaN(h.today)) day1 += h.today
if (!isNaN(h.sameMonth)) day30 += h.sameMonth
if (!isNaN(h.thisYear)) day365 += h.thisYear
if (!isNaN(h.eQF_EQ)) daySun += h.eQF_EQ
})
cabinetSun?.map(h => {
if (!isNaN(h.today)) day1 += h.today
if (!isNaN(h.sameMonth)) day30 += h.sameMonth
if (!isNaN(h.thisYear)) day365 += h.thisYear
if (!isNaN(h.eQF_EQ)) daySun += h.eQF_EQ
})
sun.day1 = day1
sun.day30 = day30
sun.day365 = day365
sun.daySun = daySun
setCentreData(sun)
}, [strucId])
return <div style={{ width: '100%', height: 'calc(100% - 160px)', position: "absolute", top: 160, right: 0 }}>
<div style={{ width: '100%', height: 'calc(60%)', display: 'flex', }}>
{/* 水泵 */}
<div style={{
width: '30%', height: '100%', borderRight: '1px solid white', display: 'flex', justifyContent: 'center',
width: '30%', height: '100%', display: 'flex', justifyContent: 'center',
}}>
<div style={{ width: '80%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<img style={{ width: 36, height: 36 }} src="/assets/images/monitor/left.png" />
<div style={{ display: 'flex', }}>
<div>
<div style={{ width: '80%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', }}>
{pumpData.length > 0 && <>
<img style={{ width: 36, height: 36 }} src="/assets/images/monitor/left.png"
onClick={() => {
if (pageLeft > 0) setPageLeft(pageLeft - 1)
}} />
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
{
pumpData?.slice(pageLeft * 2, (pageLeft + 1) * 2)?.map((v, index) => {
return <div key={'waterPump' + index} style={{ width: 250, height: 200 }}>
<div style={{
width: '100%', height: 38,
backgroundImage: 'url(/assets/images/monitor/pump-head.png)',
backgroundSize: '100% 100%',
backgroundPosition: 'center', display: 'flex', justifyContent: 'space-between', alignItems: 'center'
}}>
<div style={{ fontSize: 18, fontWeight: 400, marginLeft: 46, color: '#FFFFFF' }}>{v.name}</div>
<div style={{
width: 50, height: 26, background: '#a7110033', border: '1px solid #A71100', cursor: "pointer",
borderRadius: 4, color: ' #E83E2B', textAlign: 'center', lineHeight: '22px', marginRight: 10
}}>{[1, 3, 5].includes(v.data[0]?.sMotor_RunMode) ? '启动' : [2, 4, 7].includes(v.data[0]?.sMotor_RunMode) ? '停止' : v.data[0]?.sMotor_RunMode == 7 ? '故障' : '无状态'}</div>
</div>
<div style={{ background: 'linear-gradient(180deg, #000e28e6 1%, #021f48cc 100%)' }}>
<Carousel style={{ width: '100%', height: 66 }} autoplay>
<div style={{ width: '100%', height: '100%', }}>
<div style={{
width: '100%', height: '100%', display: 'flex', justifyContent: 'space-around'
}}>
<div style={{ width: '40%', height: '100%', display: 'flex', justifyContent: 'space-between', }}>
<img style={{ width: 40, height: 40 }} src='/assets/images/monitor/ball-A.png' />
<div style={{ display: 'flex', flexDirection: 'column', color: 'white', fontSize: 14 }}>
<div>{v.data[0]?.eMotor_A_A} A</div>
<div>A相电流</div>
</div>
</div>
<div style={{ width: '40%', height: '100%', display: 'flex', justifyContent: 'space-between', }}>
<img style={{ width: 40, height: 40 }} src='/assets/images/monitor/ball-V.png' />
<div style={{ display: 'flex', flexDirection: 'column', color: 'white', fontSize: 14 }}>
<div>{v.data[0]?.eMotor_A_V} V</div>
<div>A相电压</div>
</div>
</div>
</div>
</div>
<div style={{ width: '100%', height: '100%', }}>
<div style={{
width: '100%', height: '100%', display: 'flex', justifyContent: 'space-around'
}}>
<div style={{ width: '40%', height: '100%', display: 'flex', justifyContent: 'space-between', }}>
<img style={{ width: 40, height: 40 }} src='/assets/images/monitor/ball-A.png' />
<div style={{ display: 'flex', flexDirection: 'column', color: 'white', fontSize: 14 }}>
<div>{v.data[0]?.eMotor_B_A} A</div>
<div>B相电流</div>
</div>
</div>
<div style={{ width: '40%', height: '100%', display: 'flex', justifyContent: 'space-between', }}>
<img style={{ width: 40, height: 40 }} src='/assets/images/monitor/ball-V.png' />
<div style={{ display: 'flex', flexDirection: 'column', color: 'white', fontSize: 14 }}>
<div>{v.data[0]?.eMotor_B_V} V</div>
<div>B相电压</div>
</div>
</div>
</div>
</div>
<div style={{ width: '100%', height: '100%', }}>
<div style={{
width: '100%', height: '100%', display: 'flex', justifyContent: 'space-around'
}}>
<div style={{ width: '40%', height: '100%', display: 'flex', justifyContent: 'space-between', }}>
<img style={{ width: 40, height: 40 }} src='/assets/images/monitor/ball-A.png' />
<div style={{ display: 'flex', flexDirection: 'column', color: 'white', fontSize: 14 }}>
<div>{v.data[0]?.eMotor_C_A} A</div>
<div>C相电流</div>
</div>
</div>
<div style={{ width: '40%', height: '100%', display: 'flex', justifyContent: 'space-between', }}>
<img style={{ width: 40, height: 40 }} src='/assets/images/monitor/ball-V.png' />
<div style={{ display: 'flex', flexDirection: 'column', color: 'white', fontSize: 14 }}>
<div>{v.data[0]?.eMotor_C_V} V</div>
<div>C相电压</div>
</div>
</div>
</div>
</div>
</Carousel>
<div style={{ width: "100%", height: 90, padding: '10px 25px', display: 'flex', flexDirection: 'column', justifyContent: 'space-around', color: 'white' }}>
<div style={{ display: 'flex' }}>
<div style={{ width: 90, textAlign: 'end' }}>总用电量:</div>
<div style={{ width: 90, textAlign: 'end', color: '#FFCB00' }}>{v.data[0]?.eMotor_EQ} kwh</div>
</div>
<div style={{ display: 'flex' }}>
<div style={{ width: 90, textAlign: 'end' }}>单次运行时间:</div>
<div style={{ width: 90, textAlign: 'end', color: '#FFCB00' }}>{v.data[0]?.dPump_T_S} min</div>
</div>
<div style={{ display: 'flex' }}>
<div style={{ width: 90, textAlign: 'end' }}>总积累时间:</div>
<div style={{ width: 90, textAlign: 'end', color: '#FFCB00' }}>{v.data[0]?.dPump_T_T} h</div>
</div>
</div>
</div>
</div>
})
}
</div>
</div>
<img style={{ width: 36, height: 36 }} src="/assets/images/monitor/right.png" />
<img style={{ width: 36, height: 36 }} src="/assets/images/monitor/right.png"
onClick={() => {
if (pageLeft + 1 < Math.ceil(pumpData.length / 2)) setPageLeft(pageLeft + 1)
}} />
</>}
</div>
</div>
{/* 中间位置 */}
<div style={{
width: '40%', height: '100%', borderRight: '1px solid white',
width: '40%', height: '100%',
backgroundImage: 'url(/assets/images/monitor/pillar.png)',
backgroundSize: '80% 80%',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
position: 'relative'
}}>
<div style={{
backgroundImage: 'url(/assets/images/monitor/pedestal.png)',
backgroundSize: '100% 100%', backgroundPosition: 'center', backgroundRepeat: 'no-repeat',
color: '#00FFF8', fontSize: 20, width: 160, height: 160, textAlign: 'center',
position: 'absolute', top: '6%', left: 10,
}}>
<div style={{ fontSize: 20, color: '#E2F8FF', fontWeight: 600 }}>
湿度</div>{centreData?.sHumidity} %
</div>
<div style={{ color: '#00FFF8', fontSize: 20, position: 'absolute', top: '15%', left: "calc(50% - 90px)", display: 'inline-block' }}>
<span style={{ fontSize: 20, color: '#E2F8FF', fontWeight: 600 }}>
总用量</span>{centreData?.daySun} kWh
</div>
<div style={{
backgroundImage: 'url(/assets/images/monitor/pedestal.png)',
backgroundSize: '100% 100%', backgroundPosition: 'center', backgroundRepeat: 'no-repeat',
color: '#00FFF8', fontSize: 20, width: 160, height: 160, textAlign: 'center',
position: 'absolute', top: '6%', right: -10,
}}>
<div style={{ fontSize: 20, color: '#E2F8FF', fontWeight: 600 }}>
温度</div>{centreData?.sTEMP}
</div>
<div style={{
backgroundImage: 'linear-gradient(180deg, #0F3977 0%, #07327b9c 53%, #002B7E 100%)',
color: '#4CA1FF', fontSize: 20, width: 160, height: 60, textAlign: 'center',
position: 'absolute', top: '35%', left: -60,
}}>
<div style={{ fontSize: 20, color: '#E2F8FF', fontWeight: 600 }}>
今年用电</div>{centreData?.day365?.toFixed(2) || '--'}
</div>
<div style={{
backgroundImage: 'linear-gradient(180deg, #0F3977 0%, #07327b9c 53%, #002B7E 100%)',
color: '#4CA1FF', fontSize: 20, width: 160, height: 60, textAlign: 'center',
position: 'absolute', top: '54%', left: 0,
}}>
<div style={{ fontSize: 20, color: '#E2F8FF', fontWeight: 600 }}>
当月用电</div>{centreData?.day30?.toFixed(2) || '--'}
</div>
<div style={{
backgroundImage: ' linear-gradient(180deg, #0F3977 0%, #07327b9c 53%, #002B7E 100%)',
color: '#4CA1FF', fontSize: 20, width: 160, height: 60, textAlign: 'center',
position: 'absolute', top: '67%', left: 'calc(50% - 80px)',
}}>
<div style={{ fontSize: 20, color: '#E2F8FF', fontWeight: 600 }}>
当日用电</div>{centreData?.day1?.toFixed(2) || '--'}
</div>
<div style={{
backgroundImage: 'linear-gradient(180deg, #0F3977 0%, #07327b9c 53%, #002B7E 100%)',
height: 130, width: 180, position: 'absolute', top: '37%', right: 0,
display: 'flex', flexDirection: 'column', alignItems: 'center'
}}>
<div style={{ fontSize: 18, fontWeight: 600, color: "#E2F8FF", width: 160, marginBottom: 18 }}>集水池液位</div>
<div style={{ height: 160, width: 116, }}>
<Progress type="dashboard" percent={75} strokeColor={'#10D2E9'} gapDegree={180}
format={() => <div style={{ color: '#4CA1FF', fontSize: 20 }}>{centreData?.sGrille_level?.toFixed(2) || 0} m</div>}
/>
</div>
</div>
<div className='site' style={{
display: 'flex', justifyContent: 'center', alignItems: 'center',
width: '100%', tpo: '80%', position: 'absolute', top: '80%'
}}>
<img style={{ width: 67, height: 68, marginLeft: 94 }} src='/assets/images/monitor/site.png' />
<div style={{
width: 180, height: 41, backgroundImage: 'url(/assets/images/monitor/title.png)',
backgroundSize: '100% 100%', backgroundPosition: 'center', backgroundRepeat: 'no-repeat',
textAlign: 'center', lineHeight: '41px', fontSize: 20, color: '#FFF', fontStyle: "italic"
}}>{siteList?.filter(v => v.value == strucId)[0]?.label}</div>
<Select
showSearch
placeholder="请选择站点"
value={strucId}
style={{ width: 155 }}
optionFilterProp="children"
onChange={() => {
}}
onSearch={() => {
}}
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={siteList}
/>
</div>
</div>
{/* 进线柜 */}
<div style={{ width: '30%', height: '100%', display: 'flex', justifyContent: 'center', }}>
<div style={{ width: '80%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', }}>
{cabinetData.length > 0 && <>
<img style={{ width: 36, height: 36 }} src="/assets/images/monitor/left.png"
onClick={() => {
if (pageRight > 0) setPageRight(pageRight - 1)
}} />
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
{
cabinetData?.slice(pageRight * 2, (pageRight + 1) * 2)?.map((v, index) => {
return <div key={'waterPump' + index} style={{ width: 250, height: 200 }}>
<div style={{
width: '100%', height: 38,
backgroundImage: 'url(/assets/images/monitor/pump-head.png)',
backgroundSize: '100% 100%',
backgroundPosition: 'center', display: 'flex', justifyContent: 'space-between', alignItems: 'center'
}}>
<div style={{ fontSize: 18, fontWeight: 400, marginLeft: 46, color: '#FFFFFF' }}>{v.name}</div>
<div style={{
width: 50, height: 26, background: '#a7110033', border: '1px solid #A71100', cursor: "pointer",
borderRadius: 4, color: ' #E83E2B', textAlign: 'center', lineHeight: '22px', marginRight: 10
}}>{v.sQF_CLOSING ? '合闸' : '分闸'}</div>
</div>
<div style={{ background: 'linear-gradient(180deg, #000e28e6 1%, #021f48cc 100%)' }}>
<div style={{ width: "100%", height: 160, padding: '10px 25px', display: 'flex', flexDirection: 'column', justifyContent: 'space-around', color: 'white' }}>
<div style={{ display: 'flex' }}>
<div style={{ width: 90, textAlign: 'end' }}>当日用电:</div>
<div style={{ width: 90, textAlign: 'end', color: '#FFCB00' }}>{isNaN(v.today) ? v.today : v.today?.toFixed(2)} kwh</div>
</div>
<div style={{ display: 'flex' }}>
<div style={{ width: 90, textAlign: 'end' }}>当月用电:</div>
<div style={{ width: 90, textAlign: 'end', color: '#FFCB00' }}>{isNaN(v.sameMonth) ? v.sameMonth : v.sameMonth?.toFixed(2)} min</div>
</div>
<div style={{ display: 'flex' }}>
<div style={{ width: 90, textAlign: 'end' }}>今年用电:</div>
<div style={{ width: 90, textAlign: 'end', color: '#FFCB00' }}>{isNaN(v.thisYear) ? v.thisYear : v.thisYear?.toFixed(2)} h</div>
</div>
<div style={{ display: 'flex' }}>
<div style={{ width: 90, textAlign: 'end' }}>总用电量:</div>
<div style={{ width: 90, textAlign: 'end', color: '#FFCB00' }}>{v.eQF_EQ?.toFixed(2) || '--'} h</div>
</div>
</div>
</div>
</div>
})
}
</div>
<img style={{ width: 36, height: 36 }} src="/assets/images/monitor/right.png"
onClick={() => {
if (pageRight + 1 < Math.ceil(cabinetData.length / 2)) setPageRight(pageRight + 1)
}} /></>}
</div>
</div>
</div >
<div style={{ width: '100%', height: 'calc(36% )', display: 'flex', justifyContent: 'space-between' }}>
<div style={{
backgroundImage: 'url(/assets/images/monitor/headerTitle.png)',
backgroundSize: '100% 36px',
backgroundPosition: '0 0',
backgroundRepeat: 'no-repeat',
width: '31%', height: '100%',
}}>
<div className='site' style={{ display: 'flex' }}>
<div style={{ lineHeight: "36px", color: '#E2F8FF', fontSize: 20., textIndent: 20 }}>液位趋势</div>
<Select
showSearch
placeholder="请选择站点"
value={strucId}
style={{ width: 155,height:20 }}
optionFilterProp="children"
onChange={() => {
}}
onSearch={() => {
}}
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={siteList}
/>
</div>
<div style={{ width: '30%', height: '100%', }}></div>
</div>
<div style={{ width: '100%', height: 'calc(40% - 1px)', borderTop: '1px solid white', display: 'flex', }}>
<div style={{ width: '33%', height: '100%', borderRight: '1px solid white' }}></div>
<div style={{ width: '33%', height: '100%', borderRight: '1px solid white' }}></div>
<div style={{ width: '33%', height: '100%', }}></div>
<div style={{
backgroundImage: 'url(/assets/images/monitor/headerTitle.png)',
backgroundSize: '100% 36px',
backgroundPosition: '0 0',
backgroundRepeat: 'no-repeat',
width: '31%', height: '100%',
}}>
<div style={{ lineHeight: "36px", color: '#E2F8FF', fontSize: 20., textIndent: 20 }}>电流趋势</div>
</div>
<div style={{
backgroundImage: 'url(/assets/images/monitor/headerTitle.png)',
backgroundSize: '100% 36px',
backgroundPosition: '0 0',
backgroundRepeat: 'no-repeat',
width: '31%', height: '100%',
}}>
<div style={{ lineHeight: "36px", color: '#E2F8FF', fontSize: 20., textIndent: 20 }}>用电趋势</div>
</div>
</div>
</div >
}
@ -51,7 +676,7 @@ function mapStateToProps (state) {
return {
user: auth.user,
clientHeight: global.clientHeight,
actions: global.actions,
};
}
export default connect(mapStateToProps)(Capacity);
export default connect(mapStateToProps)(Capacity)

6
web/client/src/sections/bigScreen/components/header.js

@ -17,10 +17,6 @@ const Header = ({ dispatch, actions, user, module, setModule, history }) => {
alignItems: 'center'
}}>
<div style={{ width: 200, color: 'white', }}>天气</div>
<div style={{
lineHeight: '136px', fontFamily: 'YouSheBiaoTiHei',
fontSize: 48, color: '#E2F8FF', letterSpacing: 4,
}}>泵站自动化控制系统</div>
<div style={{ width: 200, color: 'white', display: 'flex', alignItems: 'center' }}>
<div style={{
width: 130, height: 52,
@ -58,7 +54,7 @@ const Header = ({ dispatch, actions, user, module, setModule, history }) => {
backgroundSize: '100% 100%',
backgroundPosition: '100% 100%',
backgroundRepeat: 'no-repeat',
color: 'white',
color: 'white',cursor: "pointer"
}} onClick={() => setModule(v.key)}>{v.title}</div>
})}
</div>

13
web/client/src/sections/bigScreen/components/index.less

@ -0,0 +1,13 @@
.site {
.ant-select:not(.ant-select-customize-input) .ant-select-selector {
border: 0.5px solid #89bdef66;
background-image: linear-gradient(180deg, #6187e400 30%, #6187e480 100%);
color: #fff;
}
.ant-select:not(.ant-select-customize-input) .ant-select-selector {
background-color: transparent;
}
.ant-select-arrow {
color: #fff;
}
}

5
web/client/src/sections/bigScreen/containers/systemManagement.js

@ -3,20 +3,19 @@ import { connect } from 'react-redux';
import { FormOutlined, DeleteOutlined } from '@ant-design/icons';
import { Spin, Tooltip, Button, Popconfirm, Row, Col, Tree, Card, Switch } from 'antd';
import ProTable from '@ant-design/pro-table';
import { getDepMessage, getDepUser, createUser, updateUser, delUser, resetPwd, createDept, updateDept, delDept } from '../actions/user';
import Header from '../components/header';
import Basis from '../components/basis';
import Capacity from '../components/capacity';
import Electrical from '../components/electrical';
import RealTime from '../components/realTime';
import Amap from '../components/AMap';
import Amap from '../components/amap';
import './style.less';
const TreeNode = Tree.TreeNode;
const SystemManagement = ({ clientHeight, user, history }) => {
const [module, setModule] = useState('basis')
const [module, setModule] = useState('capacity')
useEffect(() => {
}, [])

2
web/client/src/sections/bigScreen/index.js

@ -6,7 +6,7 @@ import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'organization',
key: 'bigScreen',
name: '',
reducers: reducers,
routes: routes,

13
web/client/src/utils/index.js

@ -1,14 +1,15 @@
'use strict';
import { AuthorizationCode } from './authCode';
import { ApiTable, RouteTable } from './webapi'
import { ApiTable, RouteTable, AxyRequest, AxyApiTable } from './webapi'
import Func from './func';
import { useFsRequest } from './hooks';
const Constans = {}
export {
AuthorizationCode,
Func,
ApiTable, RouteTable,
Constans,
useFsRequest
AuthorizationCode,
Func,
ApiTable, RouteTable,
Constans,
useFsRequest,
AxyRequest, AxyApiTable
}

121
web/client/src/utils/webapi.js

@ -1,120 +1,37 @@
'use strict';
import request from 'superagent';
import { ProxyRequest, customWebUtils } from "@peace/utils";
export const ApiTable = {
login: 'login',
logout: 'logout',
validatePhone: 'validate/phone',
getUserSiteList: 'user/site/list',
// 组织管理-用户管理
getDepMessage: 'organization/department',
createDept: '/organization/dept/add',
updateDept: '/organization/dept/{id}/modify',
delDept: '/organization/dept/{id}',
getDepUser: 'organization/department/{depId}/user',
createUser: 'organization/department/user',
updateUser: 'organization/department/user/{id}',
delUser: 'organization/department/user/{ids}',
resetPwd: '/organization/department/user/resetPwd/{id}',
// 巡检计划
patrolPlan: 'patrolPlan', // 增改查
delPatrolPlan: 'patrolPlan/{id}',
// 巡检记录
patrolRecord: 'patrolRecord/:patrolPlanId/:startTime/:endTime/:alarm/:pointId',
// 用户权限
getResource: 'resource',
getUserResource: 'user/resource',
postUserRes: 'user/resource',
//安全风险预报
getSiteWeekRegiste: 'sites/report',
getRiskReportList: 'risk/report',
modifyRiskReport: 'risk/report/{riskId}',
riskExport: 'risk/export',
getEnterprisesMembers: 'enterprises/{enterpriseId}/members',
const userKey = "axyUser";
//工程交底
getProjectDisclosureList: 'project/disclosure',
addProjectDisclosure: 'project/disclosure',
editProjectDisclosure: 'project/disclosure/{id}',
delProjectDisclosure: 'project/disclosure/{id}',
export const AxyRequest = new ProxyRequest("_axy", userKey);
//安全巡检
getCheckTask: '/getcheckTask',
addCheckTask: '/addcheckTask',
editCheckTask: '/editcheckTask',
delCheckTask: '/delcheckTask/:id',
export const webUtils = new customWebUtils({
userKey: userKey
});
const { basicAction, RouteRequest } = webUtils
//协调申请
getCoordinateList: 'risk/coordinate',
addCoordinate: 'risk/coordinate',
delCoordinate: 'risk/coordinate/{id}',
editCoordinate: 'risk/coordinate/{id}',
export {
basicAction, RouteRequest
}
//会议
mettingList: 'metting/list',
editMetting: 'metting',
//隐患整改
getRectifyList: 'rectify/list',
addRectify: 'rectify',
editRectify: 'rectify/{id}',
delRectify: 'rectify/{id}',
disposeRectify: 'rectify/dispose',
rectifyReport: 'rectify/report',
//问题上报
problemReport: 'report/problem',
//查阅人员
problemReportConsult: 'report/problem/consult',
export const ApiTable = {
login: 'login',
logout: 'logout',
//花名册管理
getWorkerList: 'get/worker/list',
addWorker: 'add/worker',
editWorker: 'worker/{id}',
delWorker: 'worker/{id}',
getWorkerIdcards: 'worker/idcards',
//安全管理
getTring: 'training/list/:siteid',
postTring: 'training',
putTring: 'training',
delTring: 'training/:id',
//考情信息
getChcekList: 'get/worker/attendance/list',
addCheck: 'add/worker/attendance',
editCheck: 'worker/attendance/:id',
delCheck: 'worker/attendance/:id',
verifyWoker: 'verify/worker/exist',
axyData: 'axyData', //安心云数据
//首页-我的待办
getDealTodoList: 'user/deal/list',
addDealTodo: 'user/deal',
//结构物
getProjectList: 'projectList',
postAddProject: 'addProject',
delProject: 'delProject/{id}',
};
//点位
position: 'position',
delPosition: 'delPosition/{id}',
qrCodeShow: 'qrCodeShow',
q:'q',
//视频接入配置
siteList: 'siteList',
addCamera: 'camera',
delCamera: 'camera/{id}',
// 安心云的接口
export const AxyApiTable = {
login: 'project/login'
//项目状态配置
editProjectStatus: 'project/status',
};
}
export const RouteTable = {
apiRoot: '/api/root',

169
web/config.js

@ -15,112 +15,95 @@ dev && console.log('\x1B[33m%s\x1b[0m', '请遵循并及时更新 readme.md,
// // 启动参数
args.option(['p', 'port'], '启动端口');
args.option(['u', 'api-url'], 'webapi的URL');
args.option('qnak', 'qiniuAccessKey');
args.option('qnsk', 'qiniuSecretKey');
args.option('qnbkt', 'qiniuBucket');
args.option('qndmn', 'qiniuDomain');
args.option('aliOssAccessKey', '阿里OSS AccessKey');
args.option('aliOssSecretKey', '阿里OSS SecretKey');
args.option('aliOssBucket', '阿里OSS Bucket');
args.option('aliOssRegion', '阿里OSS Region');
args.option('apiAnxinyunUrl', "安心云api");
args.option('axyProject', '安心云泵站项目信息');
const flags = args.parse(process.argv);
const FS_UNIAPP_API = process.env.FS_UNIAPP_API || flags.apiUrl;
const ANXINCLOUD_QINIU_ACCESSKEY = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak;
const ANXINCLOUD_QINIU_SECRETKEY = process.env.ANXINCLOUD_QINIU_SECRETKEY || flags.qnsk;
const ANXINCLOUD_QINIU_BUCKET_RESOURCE = process.env.ANXINCLOUD_QINIU_BUCKET_RESOURCE || flags.qnbkt;
const ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE = process.env.ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE || flags.qndmn;
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.apiAnxinyunUrl
const AXY_BZ_PROJECT = process.env.AXY_BZ_PROJECT || flags.axyProject
//阿里OSS
const ALI_OSS_ACCESSKEY = process.env.ALI_OSS_ACCESSKEY || flags.aliOssAccessKey;
const ALI_OSS_SECRETKET = process.env.ALI_OSS_SECRETKET || flags.aliOssSecretKey;
const ALI_OSS_BUCKET = process.env.ALI_OSS_BUCKET || flags.aliOssBucket;
const ALI_OSS_REGION = process.env.ALI_OSS_REGION || flags.aliOssRegion;
if (!FS_UNIAPP_API || !ANXINCLOUD_QINIU_ACCESSKEY || !ANXINCLOUD_QINIU_SECRETKEY || !ANXINCLOUD_QINIU_BUCKET_RESOURCE || !ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
if (
!FS_UNIAPP_API
|| !API_ANXINYUN_URL
) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
}
const product = {
port: flags.port || 8080,
staticDirs: [path.join(__dirname, './client')],
mws: [{
entry: require('./middlewares/proxy').entry,
opts: {
host: FS_UNIAPP_API,
match: /^\/_api\//,
}
}, {
entry: require('./middlewares/attachment').entry,
opts: {
qiniu: {
accessKey: ANXINCLOUD_QINIU_ACCESSKEY,
secretKey: ANXINCLOUD_QINIU_SECRETKEY,
bucket: ANXINCLOUD_QINIU_BUCKET_RESOURCE,
domain: ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE
},
maxSize: 104857600, // 100M
uploadPath: 'other'
}
}, {
entry: require('./routes').entry,
opts: {
apiUrl: FS_UNIAPP_API,
staticRoot: './client',
qiniu: {
fetchUrl: '/_file-server',
domain: ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE
},
aliOss: {
fetchUrl: '/_file-ali-server',
accessKey: ALI_OSS_ACCESSKEY,
secretKey: ALI_OSS_SECRETKET,
bucket: ALI_OSS_BUCKET,
region: ALI_OSS_REGION
}
}
}, {
entry: require('./client').entry,// 静态信息
opts: {}
}],
logger: {
level: 'debug',
json: false,
filename: path.join(__dirname, 'log', 'runtime.txt'),
colorize: true,
maxsize: 1024 * 1024 * 5,
rotationFormat: false,
zippedArchive: true,
maxFiles: 10,
prettyPrint: true,
label: '',
timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS'),
eol: os.EOL,
tailable: true,
depth: null,
showLevel: true,
maxRetries: 1
}
port: flags.port || 8080,
staticDirs: [path.join(__dirname, './client')],
mws: [{
entry: require('./middlewares/proxy').entry,
opts: {
host: FS_UNIAPP_API,
match: /^\/_api\//,
}
}, {
entry: require('./middlewares/proxy').entry,
opts: {
host: API_ANXINYUN_URL,
match: /^\/_axy\//,
}
}, {
entry: require('./middlewares/attachment').entry,
opts: {
maxSize: 104857600, // 100M
uploadPath: 'other'
}
}, {
entry: require('./routes').entry,
opts: {
axyApi: API_ANXINYUN_URL,
axyProject: AXY_BZ_PROJECT
}
}, {
entry: require('./client').entry,// 静态信息
opts: {
}
}],
logger: {
level: 'debug',
json: false,
filename: path.join(__dirname, 'log', 'runtime.txt'),
colorize: true,
maxsize: 1024 * 1024 * 5,
rotationFormat: false,
zippedArchive: true,
maxFiles: 10,
prettyPrint: true,
label: '',
timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS'),
eol: os.EOL,
tailable: true,
depth: null,
showLevel: true,
maxRetries: 1
}
};
let config;
if (dev) {
config = {
port: product.port,
staticDirs: product.staticDirs,
mws: product.mws.concat([
{
entry: require('./middlewares/webpack-dev').entry,
opts: {}
}
]),
logger: product.logger
}
config.logger.filename = path.join(__dirname, 'log', 'development.txt');
config = {
port: product.port,
staticDirs: product.staticDirs,
mws: product.mws.concat([
{
entry: require('./middlewares/webpack-dev').entry,
opts: {}
}
]),
logger: product.logger
}
config.logger.filename = path.join(__dirname, 'log', 'development.txt');
} else {
config = product;
config = product;
}
module.exports = config;//区分开发和发布

211
web/package.json

@ -1,107 +1,108 @@
{
"name": "fs-anxincloud-4.0",
"version": "1.0.0",
"description": "anxincloud-4.0",
"main": "server.js",
"scripts": {
"test": "mocha",
"start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5900 -u http://127.0.0.1:4900 --qnak 5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu --qnsk w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5 --qnbkt anxinyun-test --qndmn http://test.resources.anxinyun.cn --aliOssAccessKey LTAI5tNDfn7UhStYQcn3JBtw --aliOssSecretKey rnoXtDWQA1djJ5Xqcdn1OSEol0lVyv --aliOssBucket test-c371 --aliOssRegion oss-cn-hangzhou",
"deploy": "export NODE_ENV=production && npm run build && node server",
"build-dev": "export NODE_ENV=development&&webpack --config webpack.config.js",
"build": "export NODE_ENV=production&&webpack --config webpack.config.prod.js"
},
"keywords": [
"app"
],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/plugin-proposal-object-rest-spread": "^7.14.7",
"@babel/plugin-transform-runtime": "^7.14.5",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.14.7",
"@babel/preset-react": "^7.14.5",
"babel-loader": "^8.2.2",
"babel-polyfill": "^6.26.0",
"babel-plugin-import": "^1.13.3",
"connected-react-router": "^6.8.0",
"css-loader": "^3.5.0",
"express": "^4.17.1",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.5.0",
"immutable": "^4.0.0-rc.12",
"less": "^3.12.2",
"less-loader": "^7.0.2",
"natty-fetch": "^2.5.3",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"path-to-regexp": "^2.4.0",
"perfect-scrollbar": "^1.5.0",
"react": "^17.0.0",
"react-copy-to-clipboard": "^5.0.1",
"react-dnd": "^10.0.2",
"react-dnd-html5-backend": "^10.0.2",
"react-dom": "^17.0.0",
"react-if": "^2.2.1",
"react-jsonschema-form": "^1.8.1",
"react-quill": "^1.3.5",
"react-redux": "^7.2.1",
"react-router-dom": "^5.2.0",
"react-router-redux": "^4.0.8",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"redux-undo": "^1.0.1",
"style-loader": "^2.0.0",
"webpack": "^5.3.2",
"webpack-bundle-analyzer": "^4.1.0",
"webpack-cli": "^4.2.0",
"webpack-dev-middleware": "^4.0.2",
"webpack-hot-middleware": "^2.25.0"
},
"dependencies": {
"@ant-design/icons": "^4.6.2",
"@ant-design/pro-form": "^1.34.0",
"@ant-design/pro-table": "^2.48.0",
"@antv/g6": "^4.2.5",
"@fs/attachment": "^1.0.0",
"@peace/components": "0.0.35",
"@peace/utils": "0.0.37",
"ahooks": "^3.7.4",
"ali-oss": "^6.17.1",
"antd": "^4.24.5",
"antd-theme-generator": "^1.2.8",
"args": "^5.0.1",
"array-move": "^3.0.1",
"bpmn-js": "^6.5.1",
"camunda-bpmn-moddle": "^4.4.0",
"canvas": "^2.11.0",
"co-busboy": "^1.4.1",
"cross-env": "^7.0.3",
"crypto-js": "^4.1.1",
"echarts": "^5.4.1",
"file-saver": "^2.0.5",
"form-data": "^3.0.0",
"fs-attachment": "^1.0.0",
"fs-web-server-scaffold": "^1.0.6",
"i": "^0.3.6",
"koa-better-http-proxy": "^0.2.5",
"koa-proxy": "^1.0.0-alpha.3",
"koa-view": "^2.1.4",
"mini-dynamic-antd-theme": "^0.5.3",
"moment": "^2.22.0",
"npm": "^7.20.6",
"qrcode": "^1.5.1",
"qs": "^6.10.1",
"react-color": "^2.19.3",
"react-router-breadcrumbs-hoc": "^4.0.1",
"react-sortable-hoc": "^2.0.0",
"shortid": "^2.2.16",
"superagent": "^6.1.0",
"uuid": "^8.3.1",
"webpack-dev-server": "^3.11.2",
"xlsx": "^0.16.9"
}
"name": "fs-anxincloud-4.0",
"version": "1.0.0",
"description": "anxincloud-4.0",
"main": "server.js",
"scripts": {
"test": "mocha",
"start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5900 -u http://127.0.0.1:4900 --apiAnxinyunUrl https://openapi.anxinyun.cn/api/v1 --axyProject 1a271f12-52f2-4d16-8dad-ec0c92d3e0cc/03bzzdh/123456",
"deploy": "export NODE_ENV=production && npm run build && node server",
"build-dev": "export NODE_ENV=development&&webpack --config webpack.config.js",
"build": "export NODE_ENV=production&&webpack --config webpack.config.prod.js"
},
"keywords": [
"app"
],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/plugin-proposal-object-rest-spread": "^7.14.7",
"@babel/plugin-transform-runtime": "^7.14.5",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.14.7",
"@babel/preset-react": "^7.14.5",
"babel-loader": "^8.2.2",
"babel-polyfill": "^6.26.0",
"babel-plugin-import": "^1.13.3",
"connected-react-router": "^6.8.0",
"css-loader": "^3.5.0",
"express": "^4.17.1",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.5.0",
"immutable": "^4.0.0-rc.12",
"less": "^3.12.2",
"less-loader": "^7.0.2",
"natty-fetch": "^2.5.3",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"path-to-regexp": "^2.4.0",
"perfect-scrollbar": "^1.5.0",
"react": "^17.0.0",
"react-copy-to-clipboard": "^5.0.1",
"react-dnd": "^10.0.2",
"react-dnd-html5-backend": "^10.0.2",
"react-dom": "^17.0.0",
"react-if": "^2.2.1",
"react-jsonschema-form": "^1.8.1",
"react-quill": "^1.3.5",
"react-redux": "^7.2.1",
"react-router-dom": "^5.2.0",
"react-router-redux": "^4.0.8",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"redux-undo": "^1.0.1",
"style-loader": "^2.0.0",
"webpack": "^5.3.2",
"webpack-bundle-analyzer": "^4.1.0",
"webpack-cli": "^4.2.0",
"webpack-dev-middleware": "^4.0.2",
"webpack-hot-middleware": "^2.25.0"
},
"dependencies": {
"@ant-design/icons": "^4.6.2",
"@ant-design/pro-form": "^1.34.0",
"@ant-design/pro-table": "^2.48.0",
"@antv/g6": "^4.2.5",
"@fs/attachment": "^1.0.0",
"@peace/components": "0.0.35",
"@peace/utils": "0.0.66",
"ahooks": "^3.7.4",
"ali-oss": "^6.17.1",
"antd": "^4.24.5",
"antd-theme-generator": "^1.2.8",
"args": "^5.0.1",
"array-move": "^3.0.1",
"bpmn-js": "^6.5.1",
"camunda-bpmn-moddle": "^4.4.0",
"canvas": "^2.11.0",
"co-busboy": "^1.4.1",
"cross-env": "^7.0.3",
"crypto-js": "^4.1.1",
"echarts": "^5.4.1",
"echarts-for-react": "^3.0.2",
"file-saver": "^2.0.5",
"form-data": "^3.0.0",
"fs-attachment": "^1.0.0",
"fs-web-server-scaffold": "^1.0.6",
"i": "^0.3.6",
"koa-better-http-proxy": "^0.2.5",
"koa-proxy": "^1.0.0-alpha.3",
"koa-view": "^2.1.4",
"mini-dynamic-antd-theme": "^0.5.3",
"moment": "^2.22.0",
"npm": "^7.20.6",
"qrcode": "^1.5.1",
"qs": "^6.10.1",
"react-color": "^2.19.3",
"react-router-breadcrumbs-hoc": "^4.0.1",
"react-sortable-hoc": "^2.0.0",
"shortid": "^2.2.16",
"superagent": "^6.1.0",
"uuid": "^8.3.1",
"webpack-dev-server": "^3.11.2",
"xlsx": "^0.16.9"
}
}

262
web/routes/attachment/index.js

@ -7,245 +7,47 @@ const OSS = require('ali-oss');
const uuid = require('uuid');
const UploadPath = {
project: ['.txt', '.dwg', '.doc', '.docx', '.xls', '.xlsx', ".csv", '.pdf', '.pptx', '.png', '.jpg', '.svg', '.rar', '.zip', '.jpeg', '.mp4'],
report: ['.doc', '.docx', '.xls', '.xlsx', ".csv", '.pdf'],
data: ['.txt', '.xls', '.xlsx', ".csv"],
image: ['.png', '.jpg', '.svg'],
three: ['.js'],
video: ['.mp4']
project: ['.txt', '.dwg', '.doc', '.docx', '.xls', '.xlsx', ".csv", '.pdf', '.pptx', '.png', '.jpg', '.svg', '.rar', '.zip', '.jpeg', '.mp4'],
report: ['.doc', '.docx', '.xls', '.xlsx', ".csv", '.pdf'],
data: ['.txt', '.xls', '.xlsx', ".csv"],
image: ['.png', '.jpg', '.svg'],
three: ['.js'],
video: ['.mp4']
};
const ext = {
project: ['.txt', '.dwg', '.doc', '.docx', '.xls', '.xlsx', ".csv", '.pdf', '.pptx', '.png', '.jpg', '.svg', '.rar', '.zip', '.jpeg', '.mp4'],
report: [".doc", ".docx", ".xls", ".xlsx", ".pdf"],
data: [".txt", ".xls", ".xlsx"],
image: [".png", ".jpg", ".svg"],
three: [".js"],
video: [".mp4"],
bpmn: [".bpmn", ".bpmn20.xml", ".zip", ".bar"],
app: [".apk"]
project: ['.txt', '.dwg', '.doc', '.docx', '.xls', '.xlsx', ".csv", '.pdf', '.pptx', '.png', '.jpg', '.svg', '.rar', '.zip', '.jpeg', '.mp4'],
report: [".doc", ".docx", ".xls", ".xlsx", ".pdf"],
data: [".txt", ".xls", ".xlsx"],
image: [".png", ".jpg", ".svg"],
three: [".js"],
video: [".mp4"],
bpmn: [".bpmn", ".bpmn20.xml", ".zip", ".bar"],
app: [".apk"]
}
module.exports = {
entry: function (app, router, opts) {
let download_ = async function (ctx, next) {
const { fetchUrl } = opts.qiniu;
const { fetchUrl: aliFetchUrl, bucket, region } = opts.aliOss
if (ctx.path && ctx.path.includes(fetchUrl)) {
try {
const { filename } = ctx.request.query;
const fkey = decodeURI(ctx.path.slice(fetchUrl.length + 1)).replace(/\.json$/, '.js');
if (ctx.path) {
const extNames = ctx.path.split('.');
app.fs.logger.log('info', 'extNames', extNames);
if (extNames.length > 0) {
let fileType = extNames[extNames.length - 1].toLowerCase();
if (fileType === 'pdf') {
ctx.type = 'application/pdf';
app.fs.logger.log('info', 'application/pdf', fileType);
}
}
}
const publicDownloadUrl = await app.fs.attachment.download(fkey);
ctx.status = 200;
if (filename) ctx.attachment(filename);
ctx.body = request.get(publicDownloadUrl);
} catch (err) {
ctx.fs.logger.error(err);
ctx.status = 404;
ctx.body = { error: 'file not found.' };
}
} else if (ctx.path && ctx.path.includes(aliFetchUrl)) {
const { filename } = ctx.request.query;
const fkey = decodeURI(ctx.path.slice(aliFetchUrl.length + 1)).replace(/\.json$/, '.js');
if (ctx.path) {
const extNames = ctx.path.split('.');
app.fs.logger.log('info', 'extNames', extNames);
if (extNames.length > 0) {
let fileType = extNames[extNames.length - 1].toLowerCase();
if (fileType === 'pdf') {
ctx.type = 'application/pdf';
app.fs.logger.log('info', 'application/pdf', fileType);
}
}
}
const publicDownloadUrl = `http://${bucket}.${region}.aliyuncs.com/${encodeURIComponent(fkey)}`
ctx.status = 200;
ctx.body = request.get(publicDownloadUrl);
} else {
await next();
}
};
const getApiRoot = async function (ctx) {
const { apiUrl, qiniu } = opts;
const { bucket, region } = opts.aliOss
entry: function (app, router, opts) {
ctx.status = 200;
ctx.body = {
root: apiUrl,
qiniu: qiniu.domain,
aliAdmin: `http://${bucket}.${region}.aliyuncs.com`
};
};
const getApiRoot = async function (ctx) {
const { apiUrl, axyApi, axyProject } = opts;
let upload = async function (ctx, next) {
try {
const { files } = await parse(ctx.req);
const file = files[0];
const extname = path.extname(file.filename).toLowerCase();
const fileType = ctx.query.type || "image";
const fileFolder = ctx.query.fileFolder || 'common';
if (ext[fileType].indexOf(extname) < 0) {
ctx.status = 400;
ctx.body = JSON.stringify({ name: 'UploadFailed', message: '文件格式无效' });
return;
}
const date = new Date().toLocaleDateString();
const time = new Date().getTime();
let fileName = time + '_' + file.filename;
let saveFile = path.join(__dirname, '../../', `/client/assets/files/${fileFolder}`, fileName);
const pathUrl = `./client/assets/files/${fileFolder}`;
const res1 = fs.existsSync(`./client/assets/files/${fileFolder}`);
!res1 && fs.mkdirSync(`./client/assets/files/${fileFolder}`);
const res = fs.existsSync(pathUrl);
!res && fs.mkdirSync(pathUrl);
let stream = fs.createWriteStream(saveFile);
fs.createReadStream(file.path).pipe(stream);
stream.on('error', function (err) {
app.fs.logger.log('error', '[Upload Heatmap]', err);
});
ctx.status = 200;
ctx.body = { filename: path.join(`/assets/files/${fileFolder}`, fileName), name: 'UploadSuccess', message: '上传成功' };
} catch (err) {
ctx.status = 500;
ctx.fs.logger.error(err);
ctx.body = { err: 'upload error.' };
}
}
ctx.status = 200;
ctx.body = {
root: apiUrl,
axyApi: axyApi,
axyProject: axyProject
};
};
let remove = async function (ctx, next) {
try {
const fkeys = ctx.request.body;
let removeUrl = path.join(__dirname, '../../', './client', fkeys.url);
const res = fs.existsSync(removeUrl);
if (!res) {
ctx.status = 400;
ctx.body = JSON.stringify({ name: 'DeleteFailed', message: '文件地址不存在' });
return;
}
fs.unlink(removeUrl, function (error) {
if (error) {
console.log(error);
}
})
ctx.status = 200;
ctx.body = { name: 'DeleteSuccess.', message: '删除成功' };
} catch (err) {
ctx.status = 500;
ctx.fs.logger.error(err);
ctx.body = { err: 'upload cleanup error.' };
}
}
let upload_ = async function (ctx, next) {
let fkey = null;
try {
const { p } = ctx.params;
const { files } = await parse(ctx.req);
const file = files[0];
const extname = path.extname(file.filename).toLowerCase();
if (!UploadPath[p]) {
ctx.status = 400;
ctx.body = JSON.stringify({ error: '附件存放的文件夹名称无效' });
return;
} else if (UploadPath[p].indexOf(extname) < 0) {
ctx.status = 400;
ctx.body = JSON.stringify({ error: '文件格式无效' });
return;
} else {
const fileInfo = await ctx.app.fs.attachment.upload(file, { uploadPath: p });
fkey = fileInfo.key;
ctx.body = { uploaded: fkey };
}
} catch (err) {
ctx.status = 500;
ctx.fs.logger.error(err);
ctx.body = { err: 'upload error.' };
}
}
const uploadAliOSS = async (ctx,) => {
// 这个是上传到阿里
try {
const { aliOss } = opts
const { p = 'default' } = ctx.params;
const { files } = await parse(ctx.req);
const file = files[0];
const filename = file.filename || path.basename(file);
const client = new OSS({
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: aliOss.region,
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
accessKeyId: aliOss.accessKey,
accessKeySecret: aliOss.secretKey,
// 填写Bucket名称,例如examplebucket。
bucket: aliOss.bucket,
});
let uploadPath = path.posix.join(p, uuid.v4(), filename);
let result = await client.putStream(
uploadPath,
file,
// { contentLength: size }
);
ctx.status = 200;
ctx.body = {
key: result.name,
uploaded: result.name,
url: result.url,
};
} catch (error) {
ctx.status = 400;
ctx.fs.logger.error(error);
ctx.body = { err: 'upload error.' };
}
}
const downloadFromAli = async (ctx) => {
try {
const { aliOss } = opts
const { path, filename } = ctx.query
const client = new OSS({
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: aliOss.region,
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
accessKeyId: aliOss.accessKey,
accessKeySecret: aliOss.secretKey,
// 填写Bucket名称,例如examplebucket。
bucket: aliOss.bucket,
});
const filename_ = filename || path.split('/').pop()
const result = await client.get(path);
ctx.status = 200;
ctx.set('Content-Type', 'application/x-xls');
ctx.set('Content-disposition', 'attachment; filename=' + filename_);
ctx.body = result.content;
} catch (error) {
ctx.status = 400;
ctx.fs.logger.error(error);
ctx.body = { err: 'download error.' };
}
}
router.use(download_);
router.get('/api/root', getApiRoot);
router.post('/_upload/new', upload);
router.delete('/_upload/cleanup', remove);
router.post('/_upload/attachments/ali/:p', uploadAliOSS);
router.get('/_download/attachments/ali', downloadFromAli);
router.post('/_upload/attachments/:p', upload_);
}
// router.use(download_);
router.get('/api/root', getApiRoot);
// router.post('/_upload/new', upload);
// router.delete('/_upload/cleanup', remove);
// router.post('/_upload/attachments/ali/:p', uploadAliOSS);
// router.get('/_download/attachments/ali', downloadFromAli);
// router.post('/_upload/attachments/:p', upload_);
}
};

Loading…
Cancel
Save