wenlele 2 years ago
parent
commit
c8e23bafe2
  1. 3
      api/app/lib/controllers/auth/index.js
  2. 87
      api/app/lib/controllers/organization/authority.js
  3. 373
      api/app/lib/controllers/organization/user.js
  4. 4
      api/app/lib/models/department.js
  5. 28
      api/app/lib/routes/organization/authority.js
  6. 41
      api/app/lib/routes/organization/user.js
  7. 2
      script/1.0.0/data/1.admin_user.sql
  8. 11
      script/1.0.0/data/2.updata_resource_data.sql
  9. 11
      script/1.0.0/data/3.updata_user_resource_data.sql
  10. 2
      script/1.0.0/schema/1.init_inspection.sql
  11. 31
      weapp/.eslintrc.js
  12. 38
      weapp/app.js
  13. 45
      weapp/app.json
  14. 10
      weapp/app.wxss
  15. 73
      weapp/common.js
  16. 1
      weapp/images/avatar.svg
  17. BIN
      weapp/images/logo.png
  18. BIN
      weapp/images/password.png
  19. 2
      weapp/images/right.svg
  20. BIN
      weapp/images/tabBar/icon_menu.png
  21. BIN
      weapp/images/tabBar/icon_menu_active.png
  22. BIN
      weapp/images/tabBar/icon_person.png
  23. BIN
      weapp/images/tabBar/icon_person_active.png
  24. BIN
      weapp/images/tabBar/icon_polling.png
  25. BIN
      weapp/images/tabBar/icon_polling_active.png
  26. BIN
      weapp/images/userName.png
  27. 1
      weapp/images/xunjian.svg
  28. 70
      weapp/package/basic/basic.js
  29. 6
      weapp/package/basic/basic.json
  30. 41
      weapp/package/basic/basic.wxml
  31. 60
      weapp/package/basic/basic.wxss
  32. 71
      weapp/package/polling/polling.js
  33. 6
      weapp/package/polling/polling.json
  34. 11
      weapp/package/polling/polling.wxml
  35. 33
      weapp/package/polling/polling.wxss
  36. 76
      weapp/pages/index/index.js
  37. 6
      weapp/pages/index/index.json
  38. 26
      weapp/pages/index/index.wxml
  39. 55
      weapp/pages/index/index.wxss
  40. 117
      weapp/pages/login/login.js
  41. 6
      weapp/pages/login/login.json
  42. 25
      weapp/pages/login/login.wxml
  43. 156
      weapp/pages/login/login.wxss
  44. 102
      weapp/pages/myInfo/myInfo.js
  45. 6
      weapp/pages/myInfo/myInfo.json
  46. 25
      weapp/pages/myInfo/myInfo.wxml
  47. 100
      weapp/pages/myInfo/myInfo.wxss
  48. 73
      weapp/pages/overview/overview.js
  49. 6
      weapp/pages/overview/overview.json
  50. 18
      weapp/pages/overview/overview.wxml
  51. 35
      weapp/pages/overview/overview.wxss
  52. 51
      weapp/project.config.json
  53. 36
      weapp/project.private.config.json
  54. 7
      weapp/sitemap.json
  55. 9
      weapp/utils/getApiUrl.js
  56. 19
      weapp/utils/util.js
  57. 3
      web/client/src/app.js
  58. 51
      web/client/src/sections/organization/actions/authority.js
  59. 11
      web/client/src/sections/organization/actions/index.js
  60. 113
      web/client/src/sections/organization/actions/user.js
  61. 88
      web/client/src/sections/organization/components/deptModal.js
  62. 74
      web/client/src/sections/organization/components/resetPwd.js
  63. 121
      web/client/src/sections/organization/components/resource.js
  64. 171
      web/client/src/sections/organization/components/userModal.js
  65. 149
      web/client/src/sections/organization/containers/authority.js
  66. 6
      web/client/src/sections/organization/containers/index.js
  67. 331
      web/client/src/sections/organization/containers/user.js
  68. 15
      web/client/src/sections/organization/index.js
  69. 22
      web/client/src/sections/organization/nav-item.js
  70. 5
      web/client/src/sections/organization/reducers/index.js
  71. 26
      web/client/src/sections/organization/routes.js
  72. 17
      web/client/src/utils/webapi.js

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

@ -61,7 +61,8 @@ async function login(ctx, next) {
authorized: true,
token: token,
userResources: userRes ? userRes.userResources.map(r => r.resourceId) : [],
type: deptInfo ? deptInfo.type : ''
type: deptInfo ? deptInfo.type : '',
deptName: deptInfo ? deptInfo.name : '',
});
await models.UserToken.create({
token: token,

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

@ -0,0 +1,87 @@
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
};

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

@ -0,0 +1,373 @@
'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
const userRes = await models.User.findAll({
where: {
departmentId: parseInt(depId),
delete: false
},
attributes: { exclude: ['password'] },
order: [['id', 'asc']],
})
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/models/department.js

@ -35,9 +35,9 @@ module.exports = dc => {
},
type: {
type: DataTypes.INTEGER,
allowNull: false,
allowNull: true,
defaultValue: null,
comment: "市1,区县2,乡镇3,村4",
comment: "",
primaryKey: false,
field: "type",
autoIncrement: false

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

@ -0,0 +1,28 @@
'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);
};

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

@ -0,0 +1,41 @@
'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);
};

2
script/1.0.0/data/1.admin_user.sql

@ -1 +1 @@
INSERT INTO "public"."user" VALUES (1, '管理员', 'SuperAdmin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL, 't', 'f', '123456789', NULL);
INSERT INTO "public"."user" VALUES (default, '管理员', 'SuperAdmin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL, 't', 'f', '123456789', NULL);

11
script/1.0.0/data/2.updata_resource_data.sql

@ -0,0 +1,11 @@
INSERT INTO "public"."resource" VALUES ('STRUCTURE_MANAGE', '结构物管理', NULL);
INSERT INTO "public"."resource" VALUES ('STRU_INFO_CONFIG', '基础信息维护', 'STRUCTURE_MANAGE');
INSERT INTO "public"."resource" VALUES ('QR_CODE_CONFIG', '二维码管理', 'STRUCTURE_MANAGE');
INSERT INTO "public"."resource" VALUES ('PATROL_MANAGE', '巡检管理', NULL);
INSERT INTO "public"."resource" VALUES ('PATROL_PLAN_CONFIG', '巡检计划制订', 'PATROL_MANAGE');
INSERT INTO "public"."resource" VALUES ('PATROL_RECORD_VIEW', '巡检记录查看', 'PATROL_MANAGE');
INSERT INTO "public"."resource" VALUES ('REPAIR_MANAGE', '维修处理', NULL);
INSERT INTO "public"."resource" VALUES ('HANDLE_REPAIR', '维修处理', 'REPAIR_MANAGE');
INSERT INTO "public"."resource" VALUES ('ORG_MANAGE', '组织管理', NULL);
INSERT INTO "public"."resource" VALUES ('USER_CONFIG', '部门成员', 'ORG_MANAGE');
INSERT INTO "public"."resource" VALUES ('AUTH_CONFIG', '权限配置', 'ORG_MANAGE');

11
script/1.0.0/data/3.updata_user_resource_data.sql

@ -0,0 +1,11 @@
INSERT INTO "public"."user_resource" VALUES (default, 1, 'STRUCTURE_MANAGE');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'STRU_INFO_CONFIG');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'QR_CODE_CONFIG');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'PATROL_MANAGE');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'PATROL_PLAN_CONFIG');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'PATROL_RECORD_VIEW');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'REPAIR_MANAGE');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'HANDLE_REPAIR');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'ORG_MANAGE');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'USER_CONFIG');
INSERT INTO "public"."user_resource" VALUES (default, 1, 'AUTH_CONFIG');

2
script/1.0.0/schema/1.init_inspection.sql

@ -69,7 +69,7 @@ CREATE TABLE "public"."department" (
"id" int4 NOT NULL DEFAULT nextval('department_id_seq'::regclass),
"name" varchar(128) COLLATE "pg_catalog"."default" NOT NULL,
"dependence" int4,
"type" int4 NOT NULL
"type" int4
);
COMMENT ON COLUMN "public"."department"."dependence" IS '上级部门/从属';
-- COMMENT ON COLUMN "public"."department"."type" IS '市1,区县2,乡镇3,村4';

31
weapp/.eslintrc.js

@ -0,0 +1,31 @@
/*
* Eslint config file
* Documentation: https://eslint.org/docs/user-guide/configuring/
* Install the Eslint extension before using this feature.
*/
module.exports = {
env: {
es6: true,
browser: true,
node: true,
},
ecmaFeatures: {
modules: true,
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
globals: {
wx: true,
App: true,
Page: true,
getCurrentPages: true,
getApp: true,
Component: true,
requirePlugin: true,
requireMiniProgram: true,
},
// extends: 'eslint:recommended',
rules: {},
}

38
weapp/app.js

@ -0,0 +1,38 @@
// app.js
App({
onLaunch() { },
globalData: {
userInfo: null,
baseUrl: 'http://10.8.16.221:4900', //api 本地环境
// imgUrl: 'http://10.8.16.221:5000/_file-server/', //本地环境
},
onShow(e) {
// 检查是否有更新
const updateManager = wx.getUpdateManager();
updateManager.onCheckForUpdate(function (res) {
// 请求完新版本信息的回调
console.log(res.hasUpdate);
});
updateManager.onUpdateReady(function () {
wx.showModal({
title: "更新提示",
content: "新版本已经准备好,是否重启应用?",
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
}
}
});
});
updateManager.onUpdateFailed(function () {
// 新版本下载失败
wx.showToast({
title: "新版本下载失败",
icon: "none",
duration: 1000
});
});
}
})

45
weapp/app.json

@ -0,0 +1,45 @@
{
"pages": [
"pages/index/index",
"pages/login/login",
"pages/myInfo/myInfo",
"pages/overview/overview"
],
"subPackages": [{
"root": "package",
"pages": [
"polling/polling",
"basic/basic"
]
}],
"window": {
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white"
},
"tabBar": {
"color": "#000000",
"selectedColor": "#2F54FF",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "images/tabBar/icon_polling.png",
"selectedIconPath": "images/tabBar/icon_polling_active.png",
"text": "巡检总览"
}, {
"pagePath": "pages/overview/overview",
"iconPath": "images/tabBar/icon_menu.png",
"selectedIconPath": "images/tabBar/icon_menu_active.png",
"text": "工作台"
},
{
"pagePath": "pages/myInfo/myInfo",
"iconPath": "images/tabBar/icon_person.png",
"selectedIconPath": "images/tabBar/icon_person_active.png",
"text": "我的"
}
]
},
"sitemapLocation": "sitemap.json"
}

10
weapp/app.wxss

@ -0,0 +1,10 @@
/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}

73
weapp/common.js

@ -0,0 +1,73 @@
const app = getApp();
const { baseUrl } = app.globalData;
// 全局配置 请求拦截, 长时间
const buildRequest = (type, url, data) => {
return new Promise((resolve, reject) => {
if (url.indexOf('token') == -1) {
let token = wx.getStorageSync('token');
if (token) {
url += url.indexOf('?') == -1 ? '?' : '&';
url += `token=${token}`;
}
}
if (url.indexOf('http') != 0) {
url = baseUrl + url;
}
wx.request({
url,
data,
method: type,
success: res => {
if (res.statusCode == 200 || res.statusCode == 204) {
resolve(res.data);
return;
}
if (res.statusCode == 400) {
console.error("400报错" + url, res);
wx.showToast({
title: res.data.message,
icon: "none",
duration: 1500
});
// reject(res);
}
if (res.statusCode == 401) {
wx.clearStorageSync();
wx.reLaunch({
url: '/pages/login/login'
});
}
},
fail: err => {
wx.showToast({
title: '网络异常',
icon: 'none',
duration: 1500
})
console.error('网络异常' + url, err);
reject(err);
}
});
});
}
// 请求拦截
let Request = {};
Request.get = (url, query) => {
return buildRequest('GET', url, query);
}
Request.post = (url, data) => {
return buildRequest('POST', url, data);
}
Request.put = (url, data) => {
return buildRequest('PUT', url, data);
}
Request.del = (url, data) => {
return buildRequest('DELETE', url, data);
}
module.exports = {
Request
}

1
weapp/images/avatar.svg

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1673618443252" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2117" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M512.3 106c-223.6 0-406.7 183-406.7 406.7s183 406.6 406.7 406.6 406.6-183 406.6-406.7C919 289 736 106 512.3 106z m10.2 122c155 0 155 116.9 155 183s-61 188.1-155 190.6c-91.5 0-155-122-155-190.6 0-66.1 0-183 155-183z m-10.2 668.4c-116.9 0-223.7-53.4-294.8-137.2 10.2-25.4 22.9-53.4 40.7-68.6 38.1-30.5 152.5-81.3 152.5-81.3l71.2 137.2 12.7-33-20.4-40.7 40.7-40.7 40.7 40.7-17.9 43.2 10.2 33.1 73.7-134.7s114.4 50.8 152.5 81.3c17.8 12.7 30.5 35.6 38.1 55.9-68.6 88.9-177.9 144.8-299.9 144.8z m0 0" p-id="2118" fill="#707070"></path></svg>

After

Width:  |  Height:  |  Size: 868 B

BIN
weapp/images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
weapp/images/password.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

2
weapp/images/right.svg

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1648710889701" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2504" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M534.826667 935.466667a47.36 47.36 0 0 1-66.986667-66.773334L835.413333 501.333333 467.84 133.973333a47.36 47.36 0 1 1 66.986667-66.773333l400.64 400.64a47.36 47.36 0 0 1 0 66.986667z" fill="#333333" p-id="2505"></path></svg>

After

Width:  |  Height:  |  Size: 915 B

BIN
weapp/images/tabBar/icon_menu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
weapp/images/tabBar/icon_menu_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
weapp/images/tabBar/icon_person.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
weapp/images/tabBar/icon_person_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
weapp/images/tabBar/icon_polling.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
weapp/images/tabBar/icon_polling_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
weapp/images/userName.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

1
weapp/images/xunjian.svg

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1673832203014" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4951" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M517.654925 447.136478c68.837254 0 124.897433-54.210866 124.897433-120.908418 0-66.697552-56.060179-120.908418-124.897433-120.908418-68.837254 0-124.897433 54.210866-124.897432 120.908418 0 66.697552 56.044896 120.908418 124.897432 120.908418zm0-182.44012c34.647881 0 62.693254 27.86197 62.693254 62.234746 0 34.326925-28.045373 62.234746-62.693254 62.234747-34.60203 0-62.693254-27.907821-62.693253-62.234747 0-34.372776 28.091224-62.234746 62.693253-62.234746zm-120.893134 365.338746c1.803463 2.567642 3.576358 4.630925 5.028299 6.113433 42.136836 51.001313 84.60991 84.930866 86.44394 86.367523l29.589015 23.521433 29.466746-23.704836c1.83403-1.436657 44.169552-35.763582 86.22997-86.581493 1.436657-1.528358 3.270687-3.652776 5.058866-6.266268 60.140896-78.282507 134.908179-191.732537 151.811821-289.944836 3.255403-22.390448 3.347104-35.946985 3.347104-37.429493 0.565493-73.391761-28.717851-142.366567-82.409074-193.994507-51.994746-50.007881-120.801433-77.533612-193.673553-77.533612-72.597015 0-141.220299 27.525731-193.215044 77.533612-53.981612 51.903045-83.448358 121.229373-82.882866 195.156059 0 1.176836 0.091701 14.779224 3.224836 36.099821 18.141612 105.08991 102.537552 226.594388 151.97994 290.663164zm120.893134-522.988895c113.816836 0 206.985552 90.937313 206.160239 203.928836 0 0 0 10.224716-2.47594 27.174209-13.617672 79.123104-77.915701 177.136716-129.467224 244.078806-0.412657 0.794746-1.23797 1.558925-1.635343 1.971582-35.885851 43.680478-72.581731 73.223642-72.581732 73.223642s-36.711164-29.145791-72.581731-72.826269c-0.397373-0.397373-1.222687-1.207403-1.650627-1.971582-51.551522-66.529433-115.849552-164.955701-129.45194-243.712-2.47594-16.903642-2.47594-27.174209-2.47594-27.174209-0.840597-112.976239 92.343403-204.693015 206.160238-204.693015zM219.884896 993.432836 901.471522 993.432836c58.643104 0 107.229612-44.521075 107.229612-100.825791 0-56.304716-48.586507-100.810507-107.229612-100.810508L122.513194 791.796537c-22.023642 0-38.835582-15.405851-38.835582-32.98197 0-17.591403 16.81194-32.997254 38.835582-32.997254l121.718448 0a34.036537 34.036537 0 0 0 34.189373-33.914268 34.051821 34.051821 0 0 0-34.189373-33.914269L122.513194 657.988776c-58.627821 0-107.214328 44.505791-107.214328 100.825791 0 56.304716 48.586507 100.810507 107.214328 100.810508l778.958328 0c22.038925 0 38.850866 15.390567 38.850866 32.98197s-16.81194 32.98197-38.850866 32.98197L219.884896 925.589015a34.067104 34.067104 0 0 0-34.189374 33.929552A34.067104 34.067104 0 0 0 219.884896 993.432836z" fill="#7F8389" p-id="4952"></path></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

70
weapp/package/basic/basic.js

@ -0,0 +1,70 @@
// package/basic/basic.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
let that = this;
let userInfo = wx.getStorageSync('userInfo');
that.setData({
userInfo
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

6
weapp/package/basic/basic.json

@ -0,0 +1,6 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "基本信息",
"enablePullDownRefresh": false
}

41
weapp/package/basic/basic.wxml

@ -0,0 +1,41 @@
<!--package/basic/basic.wxml-->
<view class="page">
<view class='header-item-container'>
<image class='logo' src='../../images/avatar.svg' />
<view>
<text class='userName'>{{userInfo.name || '--'}}</text>
</view>
</view>
<view class='list'>
<view class='content'>
<view class='title'>姓名:</view>
<view class='value'>
{{userInfo.name || '--'}}
</view>
</view>
<view class='content'>
<view class='title'>电话:</view>
<view class='value'>
{{userInfo.phone || '--'}}
</view>
</view>
<view class='content'>
<view class='title'>部门:</view>
<view class='value'>
管理部门
</view>
</view>
<view class='content'>
<view class='title'>职务:</view>
<view class='value'>
{{userInfo.post || '--'}}
</view>
</view>
<view class='content'>
<view class='title'>邮箱:</view>
<view class='value'>
{{userInfo.email || '--'}}
</view>
</view>
</view>
</view>

60
weapp/package/basic/basic.wxss

@ -0,0 +1,60 @@
/* package/basic/basic.wxss */
page {
background-color: rgb(242, 242, 245);
}
.page {
padding: 30rpx;
}
.header-item-container {
width: 100%;
height: 180rpx;
background: rgb(255, 255, 255);
border-radius: 10rpx;
flex-direction: row;
display: flex;
justify-content: start;
align-items: center;
box-shadow: 0rpx 0rpx 16rpx #ccc;
}
.logo {
width: 120rpx;
height: 120rpx;
padding: 30rpx;
}
.userName {
font-size: 36rpx;
font-weight: 600;
}
.list {
color: #6C6C6C;
background: #fff;
padding: 40rpx;
box-sizing: border-box;
width: 100%;
margin: 40rpx auto;
border-radius: 10rpx;
box-shadow: 0rpx 0rpx 16rpx #ccc;
}
.content {
width: 100%;
overflow: hidden;
margin: 20rpx auto;
}
.title {
float: left;
}
.value {
float: left;
margin-left: 80rpx;
width: 400rpx;
word-break: break-word;
}

71
weapp/package/polling/polling.js

@ -0,0 +1,71 @@
// package/polling/polling.js
Page({
/**
* 页面的初始数据
*/
data: {
},
// 顶部tab切换
clickTab(e) {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

6
weapp/package/polling/polling.json

@ -0,0 +1,6 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "巡检",
"enablePullDownRefresh": false
}

11
weapp/package/polling/polling.wxml

@ -0,0 +1,11 @@
<!--package/polling/polling.wxml-->
<view class="page">
<!-- 顶部tab切换 -->
<view class='swiper-tab'>
<view class="swiper-tab-item active" data-current="0" bindtap="clickTab">巡检记录</view>
<!-- <view class="swiper-tab-item active" data-current="1" bindtap="clickTab">问题上报</view> -->
</view>
<view class='swiper-tab' style="top:100rpx;">
<view class="swiper-tab-item" data-current="0" bindtap="clickTab">待巡检</view>
</view>
</view>

33
weapp/package/polling/polling.wxss

@ -0,0 +1,33 @@
/* package/polling/polling.wxss */
.page {
min-height: 100vh;
background: #F7F7FA;
position: relative;
}
/* 顶部tab */
.swiper-tab {
display: flex;
height: 98rpx;
line-height: 98rpx;
width: 100%;
position: fixed;
top: 0;
z-index: 100;
background: #fff;
border-bottom: 2rpx solid #e8e8e8;
}
.swiper-tab-item {
flex: 1;
font-size: 28rpx;
text-align: center;
color: #333333;
position: relative;
}
.active {
color: #1979ff;
position: relative;
}

76
weapp/pages/index/index.js

@ -0,0 +1,76 @@
// pages/index/index.js
Page({
/**
* 页面的初始数据
*/
data: {
dataList: [1, 2, 3, 4]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const userInfo = wx.getStorageSync('userInfo');
if (!userInfo || !userInfo.id) {
wx.reLaunch({
url: '/pages/login/login'
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
const userInfo = wx.getStorageSync('userInfo');
if (!userInfo || !userInfo.id) {
wx.reLaunch({
url: '/pages/login/login'
});
}
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

6
weapp/pages/index/index.json

@ -0,0 +1,6 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "巡检总览",
"enablePullDownRefresh": false
}

26
weapp/pages/index/index.wxml

@ -0,0 +1,26 @@
<!--pages/index/index.wxml-->
<view>
<view class="searchFixed">
<view class="searchBoxs">
<input class="searchInps" bindinput="formInp" bindconfirm="bindconfirm" value='{{name}}' type="text" placeholder="请输入结构物名称" />
<button class="btnSearch" bindtap="goSearch">搜索</button>
</view>
</view>
<view style="margin-top: 117rpx;">
<block wx:for="{{dataList}}">
<view style="padding-bottom: 60rpx;">
<image style="width: 100%;height: 372rpx;display: block;" src="https://dingyue.ws.126.net/2021/0428/099be11ej00qs9xld006bc000xc00m7m.jpg"></image>
<view class="box">
<view style="font-weight:bold;font-size:32rpx;">结构物A</view>
<view style="font-size:28rpx;color: rgb(166,166,166);">
<view style=" margin-top:6rpx;">今日问题:<text>12</text></view>
<view style="float:left;width:360rpx;">未处理问题:<text>12</text></view>
<view style="float:left;width:350rpx;">已处理问题:<text>12</text></view>
</view>
</view>
</view>
</block>
</view>
</view>

55
weapp/pages/index/index.wxss

@ -0,0 +1,55 @@
/* pages/index/index.wxss */
page {
background: rgb(242, 242, 245);
}
.searchBoxs {
position: relative;
margin: 20rpx 0rpx;
height: 76rpx;
}
.searchInps {
background-color: #fff;
width: 542rpx;
height: 76rpx;
border-radius: 8rpx;
padding-left: 32rpx;
padding-right: 144rpx;
border: 1px solid rgba(225, 225, 225, 0.44);
position: absolute;
top: -2rpx;
left: 16rpx;
font-size: 28rpx;
}
.btnSearch {
width: 128rpx;
height: 68rpx;
line-height: 68rpx;
border-radius: 8rpx;
color: #fff;
font-size: 28rpx;
background: linear-gradient(180deg, #1979ff 0%, #1979ff 100%);
position: absolute;
top: 6rpx;
right: 28rpx;
z-index: 10;
}
.searchFixed {
position: fixed;
top: 0;
width: 100%;
background-color: #fff;
box-shadow: 0rpx -4rpx 20rpx #c2c2c2;
z-index: 10;
}
.box {
padding: 20rpx;
box-shadow: 0rpx 0rpx 10rpx #ccc;
overflow: hidden;
line-height: 50rpx;
background: #fff;
}

117
weapp/pages/login/login.js

@ -0,0 +1,117 @@
// pages/login/login.js
import { loginUrl } from "../../utils/getApiUrl";
import { Request } from "../../common";
Page({
/**
* 页面的初始数据
*/
data: {
userNameCan: false,
passwordCan: false,
},
setColor: function () {
this.setData({
backgroundInput: this.data.userNameCan == true && this.data.passwordCan == true ? 'rgb(51, 133, 255)' : 'rgb(255,255,255)',
colorInput: this.data.userNameCan == true && this.data.passwordCan == true ? 'rgb(255, 255, 255)' : 'rgb(201,201,201)',
})
},
bindUserName: function (e) {
this.setData({
userNameCan: e.detail.value.length > 0 ? true : false,
})
this.setColor();
},
bindPassword: function (e) {
this.setData({
passwordCan: e.detail.value.length > 0 ? true : false,
})
this.setColor();
},
// 登录
getLogin: function (e) {
if (e.detail.value.username.length == 0 || e.detail.value.password == 0) {
wx.showToast({
title: '请输入用户名密码',
icon: 'none',
})
return
}
wx.showLoading()
Request.post(loginUrl(), {
"username": e.detail.value.username,
"password": e.detail.value.password
}).then((res) => {
if (!res.authorized) {
wx.showToast({ title: "登录失败", icon: "none" });
return;
}
wx.setStorageSync('token', res.token);
wx.setStorageSync("userInfo", res);
getApp().globalData.userInfo = res
wx.switchTab({
url: '/pages/index/index',
})
wx.hideLoading()
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

6
weapp/pages/login/login.json

@ -0,0 +1,6 @@
{
"navigationBarBackgroundColor": "#fff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "登录",
"enablePullDownRefresh": false
}

25
weapp/pages/login/login.wxml

@ -0,0 +1,25 @@
<!--pages/login/login.wxml-->
<view class='logo-container'>
<image class='logo' src='/images/logo.png' mode='aspectFit' />
</view>
<view class="body-container" wx:if='{{!isAutoLogin}}'>
<form report-submit='true' bindsubmit='getLogin'>
<view class='userName-container marginBottom'>
<image class='userName-logo' src='/images/userName.png' />
<input class='input' name='username' placeholder='请输入用户名' value='' type='text' maxlength="50" confirm-type="next" bindinput='bindUserName' />
</view>
<view class='userName-container'>
<image class='password-logo' src='/images/password.png' />
<input class='input' name='password' placeholder='输入密码' value='' type='text' password='true' maxlength="50" confirm-type="done" bindinput='bindPassword' />
</view>
<button class='login-btn' disabled="{{!userNameCan || !passwordCan}}" style='background:{{backgroundInput}}; color:{{colorInput}};' form-type="submit">登录</button>
</form>
</view>
<view class='co-info'>
<view>仅提供内部人员使用</view>
<view>©飞尚科技</view>
</view>

156
weapp/pages/login/login.wxss

@ -0,0 +1,156 @@
/* pages/login/login.wxss */
page {
background: rgb(255, 255, 255);
}
.logo-container {
margin-top: 85rpx;
margin-bottom: 125rpx;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.logo {
width: 180rpx;
height: 180rpx;
}
.body-container {
display: flex;
justify-content: center;
align-items: center;
}
.marginBottom {
margin-bottom: 30rpx;
}
.userName-container {
width: 580rpx;
height: 95rpx;
background: rgb(245, 245, 247);
border-radius: 10rpx;
display: flex;
justify-content: center;
align-items: center;
}
.userName-logo {
width: 30rpx;
height: 34rpx;
padding-right: 15rpx;
border-right: 1px dashed #8A8A8A;
}
.password-logo {
width: 30rpx;
height: 34rpx;
padding-right: 15rpx;
border-right: 1px dashed #8A8A8A;
}
.input {
width: 492rpx;
height: 95rpx;
padding-left: 15rpx;
font-size: 32rpx;
}
.login-btn {
width: 580rpx !important;
height: 98rpx;
margin-top: 90rpx;
color: rgb(201, 201, 201);
background: rgb(255, 255, 255);
display: flex;
justify-content: center;
align-items: center;
border-radius: 10rpx;
}
.backColor {
color: rgb(138, 138, 138);
}
/* 按钮 */
.cancle-sure {
width: 100%;
/* height: 100rpx; */
background: white;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #E5E5E5;
}
.sure {
color: rgb(36, 123, 255);
padding: 30rpx;
font-size: 36rpx;
}
.picker-view-column-container {
display: flex;
justify-content: center;
align-items: center;
}
.sitePicker {
width: 100%;
height: 240rpx;
font-size: 34rpx;
padding-left: 30rpx;
padding-right: 30rpx;
}
.picker-item {
display: flex;
justify-content: center;
align-items: center;
font-size: 34rpx;
overflow: hidden;
}
.sensorTypePicker {
width: 100%;
height: 240rpx;
font-size: 34rpx;
padding-left: 30rpx;
padding-right: 30rpx;
}
.indecate {
display: flex;
justify-content: center;
align-items: center;
color: rgb(241, 241, 242);
}
.noSensor-container {
height: 800rpx;
flex-direction: column;
display: flex;
justify-content: center;
align-items: center;
/* background: rgb(0,0,0); */
}
.selectIndex {
height: 80rpx;
}
.co-info {
position: fixed;
bottom: 10px;
left: 0;
width: 100vw;
color: #ccc;
text-align: center;
font-size: 14px
}

102
weapp/pages/myInfo/myInfo.js

@ -0,0 +1,102 @@
// pages/myInfo/myInfo.js
import { logoutUrl } from "../../utils/getApiUrl";
import { Request } from "../../common";
Page({
/**
* 页面的初始数据
*/
data: {
},
bindClick() {
wx.navigateTo({
url: '/package/basic/basic',
})
},
// 登出
logout() {
wx.showModal({
title: '切换用户',
content: '将退出当前用户,重新登录',
confirmText: '切换用户',
confirmColor: '#1979ff',
success: res => {
if (res.confirm) {
wx.showLoading()
Request.put(logoutUrl(), {}).then((res) => {
wx.clearStorage();
getApp().globalData.userInfo = null;
wx.reLaunch({
url: '../login/login',
})
wx.hideLoading()
})
}
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
let that = this;
let userInfo = wx.getStorageSync('userInfo');
that.setData({
userInfo
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

6
weapp/pages/myInfo/myInfo.json

@ -0,0 +1,6 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "我的",
"enablePullDownRefresh": false
}

25
weapp/pages/myInfo/myInfo.wxml

@ -0,0 +1,25 @@
<!--pages/myInfo/myInfo.wxml-->
<view class='box'>
<view class='header-item-container'>
<image class='logo' src='../../images/avatar.svg' />
<view>
<text class='userName'>{{userInfo.name}}</text>
</view>
</view>
<view class='body-container'>
<view class='body-item' bindtap="bindClick">
<view class='body-info'>基本信息</view>
<image class="right" src="/images/right.svg"></image>
</view>
<!-- <view class='body-item'>
<view class='body-info'>通知消息</view>
<image class="right" src="/images/right.svg"></image>
</view> -->
</view>
<view class='foot-container'>
<view class='foot-item' bindtap='logout'>退出登录</view>
</view>
</view>

100
weapp/pages/myInfo/myInfo.wxss

@ -0,0 +1,100 @@
/* pages/myInfo/myInfo.wxss */
page {
background-color: rgb(242, 242, 245);
}
/* 容器 */
.box {
padding: 30rpx;
font-family: 'PingFang SC-Medium';
}
.header-item-container {
width: 100%;
height: 180rpx;
background: rgb(255, 255, 255);
border-radius: 10rpx;
flex-direction: row;
display: flex;
justify-content: start;
align-items: center;
}
.logo {
width: 120rpx;
height: 120rpx;
padding: 30rpx;
}
.userName {
font-size: 36rpx;
font-weight: 600;
}
.company {
font-size: 28rpx;
font-family: "PingFang SC";
color: rgb(138, 138, 138);
margin-top: 14rpx;
}
.body-container {
width: 100%;
background: rgb(255, 255, 255);
border-radius: 10rpx;
flex-direction: column;
display: flex;
margin-top: 30rpx;
margin-bottom: 30rpx;
}
.body-item {
height: 110rpx;
flex-direction: row;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
border-bottom: 1px solid #EFEFF4;
}
.body-info {
font-size: 32rpx;
font-family: "PingFang SC";
font-weight: 600;
}
.body-number {
font-size: 30rpx;
font-family: "PingFang SC";
color: rgb(138, 138, 138);
}
.foot-container {
width: 100%;
background: rgb(255, 255, 255);
border-radius: 10rpx;
flex-direction: column;
display: flex;
border: 1px #FFFFFF;
}
.foot-item {
color: rgb(0, 0, 0);
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx;
font-size: 32rpx;
font-family: "PingFang SC";
font-weight: 600;
}
.right {
width: 32rpx;
height: 32rpx;
display: block;
float: right;
margin: 38rpx 40rpx;
}

73
weapp/pages/overview/overview.js

@ -0,0 +1,73 @@
// pages/overview/overview.js
Page({
/**
* 页面的初始数据
*/
data: {
},
// 巡检
bindPolling() {
wx.navigateTo({
url: '/package/polling/polling',
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

6
weapp/pages/overview/overview.json

@ -0,0 +1,6 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "工作台",
"enablePullDownRefresh": false
}

18
weapp/pages/overview/overview.wxml

@ -0,0 +1,18 @@
<!--pages/overview/overview.wxml-->
<view style="margin-top: 42rpx;">
<view class="box" bindtap="bindPolling">
<image class="logo" src="/images/xunjian.svg"></image>
<view class="txt">巡检</view>
<image class="right" src="/images/right.svg"></image>
</view>
<!-- <view class="box">
<image class="logo" src="/images/tabBar/icon_menu.png"></image>
<view class="txt">问题处理</view>
<image class="right" src="/images/right.svg"></image>
</view>
<view class="box">
<image class="logo" src="/images/tabBar/icon_menu.png"></image>
<view class="txt">巡检报告</view>
<image class="right" src="/images/right.svg"></image>
</view> -->
</view>

35
weapp/pages/overview/overview.wxss

@ -0,0 +1,35 @@
/* pages/overview/overview.wxss */
page {
background-color: rgb(242, 242, 245);
}
.box {
width: 672rpx;
height: 178rpx;
margin: 42rpx auto 21rpx;
background: #fff;
box-shadow: 0rpx 0rpx 10rpx #ccc;
border-radius: 8rpx;
}
.logo {
width: 90rpx;
height: 90rpx;
float: left;
margin: 48rpx 50rpx 0 50rpx;
}
.txt {
float: left;
line-height: 112rpx;
margin: 34rpx;
}
.right {
width: 32rpx;
height: 32rpx;
display: block;
float: right;
margin: 74rpx 40rpx;
}

51
weapp/project.config.json

@ -0,0 +1,51 @@
{
"description": "项目配置文件",
"packOptions": {
"ignore": [],
"include": []
},
"setting": {
"bundle": false,
"userConfirmedBundleSwitch": false,
"urlCheck": true,
"scopeDataCheck": false,
"coverView": true,
"es6": true,
"postcss": true,
"compileHotReLoad": false,
"lazyloadPlaceholderEnable": false,
"preloadBackgroundData": false,
"minified": true,
"autoAudits": false,
"newFeature": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"useIsolateContext": true,
"nodeModules": false,
"enhance": true,
"useMultiFrameRuntime": true,
"useApiHook": true,
"useApiHostProcess": true,
"showShadowRootInWxmlPanel": true,
"packNpmManually": false,
"enableEngineNative": false,
"packNpmRelationList": [],
"minifyWXSS": true,
"showES6CompileOption": false,
"minifyWXML": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
}
},
"compileType": "miniprogram",
"libVersion": "2.19.4",
"appid": "wxe9595234589a0147",
"projectname": "miniprogram-92",
"condition": {},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
}
}

36
weapp/project.private.config.json

@ -0,0 +1,36 @@
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "%E5%B7%A1%E6%A3%80%E5%B0%8F%E7%A8%8B%E5%BA%8F",
"setting": {
"compileHotReLoad": false,
"urlCheck": false
},
"condition": {
"miniprogram": {
"list": [
{
"name": "",
"pathName": "pages/login/login",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "",
"pathName": "package/polling/polling",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "",
"pathName": "package/basic/basic",
"query": "",
"launchMode": "default",
"scene": null
}
]
}
},
"libVersion": "2.29.0"
}

7
weapp/sitemap.json

@ -0,0 +1,7 @@
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}

9
weapp/utils/getApiUrl.js

@ -0,0 +1,9 @@
// 登录
exports.loginUrl = () => {
return `/login`
}
// 登出
exports.logoutUrl = () => {
return `/logout`
}

19
weapp/utils/util.js

@ -0,0 +1,19 @@
const formatTime = date => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
}
const formatNumber = n => {
n = n.toString()
return n[1] ? n : `0${n}`
}
module.exports = {
formatTime
}

3
web/client/src/app.js

@ -5,6 +5,7 @@ import Layout from './layout';
import Auth from './sections/auth';
import Safetymanage from './sections/safetymanage';
import ProjectRegime from './sections/projectRegime';
import Organization from './sections/organization';
const App = props => {
const { projectName } = props
@ -16,7 +17,7 @@ const App = props => {
return (
<Layout
title={projectName}
sections={[ Auth, ProjectRegime, Safetymanage]}
sections={[Auth, ProjectRegime, Safetymanage, Organization]}
/>
)

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

@ -0,0 +1,51 @@
'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
}

11
web/client/src/sections/organization/actions/index.js

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

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

@ -0,0 +1,113 @@
'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
}

88
web/client/src/sections/organization/components/deptModal.js

@ -0,0 +1,88 @@
import React from 'react';
import { connect } from 'react-redux';
import { ProFormText, ModalForm, ProFormSelect } from '@ant-design/pro-form';
const DeptModal = (props) => {
const { visible, modalType, onVisibleChange, onConfirm, editData, depts } = props
let deptOptions = [], sonArr = [];
depts.map(d => {
deptOptions.push({
value: d.id,
label: d.name
});
d.subordinate.map(ds => {
sonArr.push({
value: ds.id,
label: ds.name
})
})
})
const onFinish = (values) => {
if (onConfirm) {
if (modalType === 'edit') {
values.contract.parentDeptId = values.contract.parentDeptId || null
onConfirm(values)
} else {
onConfirm(values);
}
}
}
const checkName = (rule, value, callback) => {
const list = modalType == 'edit' ? deptOptions.concat(sonArr).filter(g => g.value != editData.id) : deptOptions.concat(sonArr)
if (list.filter(s => s.label == value).length) {
callback('该名称已存在');
} else {
callback();
}
}
return (
<ModalForm
width={400}
title={modalType == 'edit' ? '编辑部门' : '新建部门'}
visible={visible}
onVisibleChange={onVisibleChange}
onFinish={onFinish}
destroyOnClose
initialValues={
modalType == 'edit' ?
{
contract: editData
} :
{
contract: {
enable: true
}
}
}
>
<ProFormText
name={['contract', 'name']}
maxLength={24}
width="md"
label="部门名称"
required
placeholder="请输入部门名称"
rules={[{ required: true, message: '请输入部门名称' }, { validator: checkName }]}
/>
<ProFormSelect
name={['contract', 'dependence']}
label="上级部门"
request={async () => {
let t = modalType === 'edit' ? deptOptions.filter(i => i.value !== editData.id) : deptOptions
return t
}}
disabled={modalType === 'edit' ? editData.subordinate?.length === 0 ? false : true : false}
/>
</ModalForm>
)
}
function mapStateToProps(state) {
return {
};
}
export default connect(mapStateToProps)(DeptModal);

74
web/client/src/sections/organization/components/resetPwd.js

@ -0,0 +1,74 @@
import React, { useRef, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
const ResetPwd = (props) => {
const { visible, onVisibleChange, onConfirm } = props;
const formRef = useRef();
const onFinish = (values) => {
if (onConfirm) {
onConfirm(values);
}
}
return (
<Spin spinning={false}>
<ModalForm
title={'重置密码'}
visible={visible}
onVisibleChange={onVisibleChange}
onFinish={onFinish}
formRef={formRef}
destroyOnClose
>
<ProFormText.Password
name={['password']}
width="md"
label="新密码"
required
placeholder="请输入密码"
fieldProps={{
autocomplete: 'new-password'
}}
rules={[
{ required: true, message: '请填写密码' },
{ min: 6, message: '请填写至少6位密码' },
]}
/>
<ProFormText.Password
name={['checkPassword']}
width="md"
label="确认密码"
required
autocomplete='off'
placeholder="请输入密码"
rules={[
{ required: true, message: '请再次填写密码' },
{ min: 6, message: '请填写至少6位密码' },
{
validator: (rule, value, callback) => {
const pwd = formRef.current.getFieldValue('password');
if (!value) {
callback();
}
if (pwd == value) {
callback();
} else {
callback('两次输入的密码不一致');
}
}
}
]}
/>
</ModalForm>
</Spin>
)
}
function mapStateToProps(state) {
return {};
}
export default connect(mapStateToProps)(ResetPwd);

121
web/client/src/sections/organization/components/resource.js

@ -0,0 +1,121 @@
import React, { useEffect } from 'react';
import { Checkbox, Table } from 'antd';
import { useState } from 'react';
const CheckboxGroup = Checkbox.Group;
const Resource = props => {
const { roleData, userRole, userSelected, setResCode, userType } = props;
const [indeterminate, setIndeterminate] = useState({});
const [roleCheck, setRoleCheck] = useState({});//一级权限码
const [parentRoleCheck, setParentRoleCheck] = useState({}); //二级权限码
const roleDatas=roleData.slice(0)
useEffect(() => {
const check = {}
const parentCheck = {}
const initInd = {}
roleData && roleData.map && roleData.map(r => {
let currentInd = false;
let sum = 0;
if (r.resources) {
check[r.code] = []
r.resources.map(child => {
if (userRole.find(code => code.resourceId == child.code)) {
currentInd = true;
sum++;
check[r.code].push(child.code);
}
})
}
parentCheck[r.code] = r.resources.length === sum
initInd[r.code] = parentCheck[r.code] ? false : currentInd
});
setParentRoleCheck(parentCheck)
setRoleCheck(check);
setIndeterminate(initInd);
}, [userRole]);
const setResData = (role) => {
let codes = [];
// Object.keys(partRole).map(r => {
// if (partRole[r]) codes.push(r)
// })
Object.keys(role).map(r => {
if (role[r].length) {
codes.push(r);
}
codes = codes.concat(role[r])
})
setResCode(codes)
}
return (
<Table
bordered
pagination={false}
dataSource={roleDatas}
columns={[{
title: '功能',
key: 'name',
dataIndex: 'name',
render: (text, record) => {
const parentCode = record.code
return <Checkbox
indeterminate={indeterminate[parentCode]}
onChange={(e) => {
const currentParCheck = JSON.parse(JSON.stringify(parentRoleCheck));
currentParCheck[parentCode] = e.target.checked;
const currentCode = JSON.parse(JSON.stringify(roleCheck));
currentCode[parentCode] = e.target.checked ? roleData.find(r => r.code == parentCode).resources.map(r => r.code) : []
const currentInd = JSON.parse(JSON.stringify(indeterminate));
currentInd[parentCode] = false;
setParentRoleCheck(currentParCheck);
setRoleCheck(currentCode);
setIndeterminate(currentInd);
setResData(currentCode)
}}
checked={parentRoleCheck[parentCode] || false}
disabled={userSelected === "SuperAdmin" || userType === 4}
options={''}
>
{text}
</Checkbox>
}
}, {
title: '列表',
key: 'resources',
dataIndex: 'resources',
render: (text, record) => {
let data = [];
console.log()
text.map(s => { s.name !== "整治汇总编辑" ? data.push({ label: s.name, value: s.code }) : '' })
let parentCode = record.code;
return <CheckboxGroup
disabled={userSelected === "SuperAdmin" || userType === 4}
options={data}
value={roleCheck[parentCode] || []}
onChange={value => {
const checkArr = JSON.parse(JSON.stringify(roleCheck));
const parentCheck = JSON.parse(JSON.stringify(parentRoleCheck));
const ind = JSON.parse(JSON.stringify(indeterminate));
const currentCode = roleData.find(r => r.code == parentCode) || {}
checkArr[parentCode] = value;
ind[parentCode] = !!value.length && value.length < currentCode.resources.length
parentCheck[parentCode] = value.length === currentCode.resources.length
setRoleCheck(checkArr);
setIndeterminate(ind);
setParentRoleCheck(parentCheck);
setResData(checkArr)
}}
/>
}
}]}
></Table >
)
}
export default Resource

171
web/client/src/sections/organization/components/userModal.js

@ -0,0 +1,171 @@
import React from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
const UserModal = (props) => {
const { visible, modalType, depData, onVisibleChange, onConfirm, editData } = props
const reg_tel = /^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$/;
const onFinish = (values) => {
if (onConfirm) {
onConfirm(values);
}
}
const mobile = (value) => {
if (reg_tel.test(value)) {
return
}
return message('请输入姓名')
}
return (
<Spin spinning={false}>
<ModalForm
title={modalType == 'edit' ? '编辑用户' : '新建用户'}
visible={visible}
onVisibleChange={onVisibleChange}
onFinish={onFinish}
destroyOnClose
initialValues={
modalType == 'edit' ?
{
contract: editData
} :
{
contract: {
enable: true
}
}
}
>
<ProForm.Group>
<ProFormText
name={['contract', 'name']}
maxLength={24}
width="md"
label="姓名"
required
placeholder="请输入姓名"
rules={[{ required: true, message: '请输入姓名' }]}
/>
<ProFormText
name={['contract', 'phone']}
width="md"
label="用户名(手机号)"
required
fieldProps={{
maxLength: 11,
}}
getValueFromEvent={(event) => {
return event.target.value.replace(/\D/g, '')
}}
placeholder="请输入用户名(手机号)"
rules={[
{ required: true, valueType: Number, max: 11 }, { pattern: /^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$/, message: "请输入正确的手机号" }
]}
/>
</ProForm.Group>
<ProForm.Group>
<ProFormTreeSelect
name={['contract', 'departmentId']}
placeholder="请选择所属部门"
width="md"
label="所属部门"
required
fieldNames={{
title: 'name',
key: 'id',
children: 'subordinate'
}}
onSelect={(selectedKeys, { selected, selectedNodes }) => {
if (selected) {
setDepSelectedKeys(selectedKeys)
setDepSelected(selectedNodes[0].name || "")
dispatch(getDepUser(selectedKeys[0]))
}
}}
fieldProps={{
fieldNames: {
label: 'title',
},
treeDefaultExpandAll: false,
}}
rules={[{ required: true, message: '请选择所属部门' }]}
request={async () => {
console.log(depData);
return depData
}}
expandedKeys={["title"]}
/>
< ProFormText
name={['contract', 'post']}
width="md"
label="职位"
// required
placeholder="请输入职位"
/>
</ProForm.Group>
<ProForm.Group>
<ProFormText
name={['contract', 'email']}
width="md"
label="邮箱"
// required
placeholder="请输入邮箱"
rules={[
// { required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入正确格式的邮箱' },
]}
/>
{modalType == 'edit' ? null : <ProFormText.Password
name={['contract', 'password']}
width="md"
label="密码"
required
placeholder="请输入密码"
fieldProps={{
autocomplete: 'new-password'
}}
rules={[
{ required: true, message: '请填写密码' },
{ min: 6, message: '请填写至少6位密码' },
]}
/>}
</ProForm.Group>
<ProForm.Group>
<ProFormSwitch
name={['contract', 'enable']}
width="md"
label="是否启用"
placeholder="请选择"
// defaultChecked
valuePropName="checked"
/>
</ProForm.Group>
</ModalForm>
</Spin>
)
}
function mapStateToProps(state) {
const { depMessage } = state;
const pakData = (dep) => {
// console.log(dep);
return dep.map((d) => {
return {
title: d.name,
value: d.id,
// key: d.id,
children: pakData(d.subordinate)
}
})
}
let depData = pakData(depMessage.data || [])
return {
loading: depMessage.isRequesting,
depData,
};
}
export default connect(mapStateToProps)(UserModal);

149
web/client/src/sections/organization/containers/authority.js

@ -0,0 +1,149 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Row, Col, Card, Button, Tree, Empty } from 'antd';
import { getDepMessage, getDepUser } from '../actions/user';
import { getResource, getUserResource, postUserRes } from '../actions/authority';
import Resource from '../components/resource';
const Authority = (props) => {
const { dispatch, loading, depMessage, depUser, resource, userResource, clientHeight } = props
const [depSelectedKeys, setDepSelectedKeys] = useState([])
const [userSelectedKeys, setUserSelectedKeys] = useState([])
const [depSelected, setDepSelected] = useState()
const [userSelected, setUserSelected] = useState()
const [resCode, setResCode] = useState({})
const [useName, setUseName] = useState()// 选中名字
const [userType,setUserType]=useState()
console.log(resource)
useEffect(() => {
dispatch(getResource())
if (!(depMessage && depMessage.length)) {
dispatch(getDepMessage())
}
}, [])
useEffect(() => {
if (depMessage.length) {
setDepSelectedKeys([depMessage[0].id])
setDepSelected([depMessage[0].name])
dispatch(getDepUser(depMessage[0].id))
}
}, [depMessage])
useEffect(() => {
if (depUser.length) {
setUserSelectedKeys([depUser[0].id])
setUserSelected(depUser[0].username)
dispatch(getUserResource(depUser[0].id))
setUseName(depUser[0].name)
}
console.log(depUser, 'depUser')
}, [depUser])
const handleSave = () => {
dispatch(postUserRes({ userId: userSelectedKeys[0], resCode: resCode })).then(res => {
if (res.success) {
dispatch(getUserResource(userSelectedKeys[0]))
}
})
}
return (
<Spin spinning={loading}>
<Row gutter={16}>
<Col span={4} style={{ height: '100%' }}>
<Card title="部门" bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
{
depMessage.length ?
<Tree
height={clientHeight - 100}
defaultExpandedKeys={[depMessage[0].id]}
selectedKeys={depSelectedKeys}
onSelect={(selectedKeys, { selected, selectedNodes, node }) => {
setUserType(selectedNodes[0].type)
if (selected) {
setDepSelectedKeys(selectedKeys)
setDepSelected(selectedNodes[0].name || "")
dispatch(getDepUser(selectedKeys[0]))
}
}}
treeData={depMessage}
fieldNames={{
title: 'name',
key: 'id',
children: 'subordinate'
}}
/> : ''
}
</Card>
</Col>
<Col span={4} style={{ height: '100%', }}>
<Card title={`[${depSelected}] 用户列表`} bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
{
depUser.length ?
<Tree
height={clientHeight - 100}
defaultSelectedKeys={[depUser[0].id]}
selectedKeys={userSelectedKeys}
onSelect={(selectedKeys, { selected, selectedNodes, node, event }) => {
const name = node.name
setUseName(name)
if (selected) {
setUserSelectedKeys(selectedKeys)
setUserSelected(selectedNodes[0].username || '')
dispatch(getUserResource(selectedKeys[0]))
}
}}
treeData={depUser}
fieldNames={{
title: 'name',
key: 'id'
}}
/> : <Empty />
}
</Card>
</Col>
<Col span={16} style={{ height: '100%', }}>
{depUser.length ?
<Card title={`[${useName ? useName : '管理员'}] 功能范围`} bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
<Resource
userSelected={userSelected}
roleData={resource}
userRole={userResource}
setResCode={setResCode}
userType={userType}
/>
<Row type="flex" justify="center" style={{ marginBottom: 16, marginTop: 16, textAlign: 'center' }}>
<Col span="24">
<Button
disabled={userSelected === "SuperAdmin"||userType===4}
onClick={handleSave}
style={{ width: '60%' }}
type='primary'>保存修改</Button>
</Col></Row>
</Card>
: <Card title={`[]功能范围`} bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
<Empty />
</Card>
}
</Col>
</Row>
</Spin >
)
}
function mapStateToProps(state) {
const { userResource, resource, depMessage, depUser, global } = state;
return {
clientHeight: global.clientHeight,
loading: depMessage.isRequesting || depUser.isRequesting || resource.isRequesting,
userResource: userResource.data || [],
resource: resource.data || [],
depMessage: depMessage.data || [],
depUser: depUser.data || []
};
}
export default connect(mapStateToProps)(Authority);

6
web/client/src/sections/organization/containers/index.js

@ -0,0 +1,6 @@
'use strict';
import Authority from './authority';
import UserManage from './user';
export { Authority, UserManage };

331
web/client/src/sections/organization/containers/user.js

@ -0,0 +1,331 @@
import React, { useEffect, useState } from 'react';
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 UserModal from '../components/userModal';
import ResetPwd from '../components/resetPwd';
import DeptModal from '../components/deptModal';
const TreeNode = Tree.TreeNode;
const user = JSON.parse(sessionStorage.getItem('user'));
const UserManage = (props) => {
const { dispatch, loading, depMessage, depUser, clientHeight } = props;
// 部门
const [deptModalVisible, setDeptModalVisible] = useState(false);
const [deptModalType, setDeptModalType] = useState();
const [deptModalRecord, setDeptModalRecord] = useState();
// 成员
const [modalVisible, setModalVisible] = useState(false);
const [modalType, setModalType] = useState();
const [modalRecord, setModalRecord] = useState();
const [pwdModalVisible, setPwdModalVisible] = useState(false);
const [depSelectedKeys, setDepSelectedKeys] = useState([])
const [rowSelected, setRowSelected] = useState([])
useEffect(() => {
dispatch(getDepMessage())
}, [])
useEffect(() => {
if (depMessage.length) {
setDepSelectedKeys([depMessage[0].id])
dispatch(getDepUser(depMessage[0].id))
}
}, [depMessage])
const columns = [
{
title: '姓名',
dataIndex: 'name',
}, {
title: '用户名(手机号)',
dataIndex: 'username',
},
{
title: '职位',
dataIndex: 'post',
}, {
title: '邮箱',
dataIndex: 'email',
}, {
title: '启用状态',
dataIndex: 'enable',
render: (_, r) => {
return <Switch checkedChildren="启用" unCheckedChildren="禁用" disabled defaultChecked={r.enable} />
}
}, {
title: '操作',
dataIndex: 'action',
render: (dom, record) => {
return record.username == 'SuperAdmin' ? '' : [
<Button type="link" onClick={() => { openModal('edit', record) }}>编辑</Button>,
<Popconfirm
title="确认删除?"
onConfirm={() => {
delUsers([record.id])
}}
>
<Button type="link">删除</Button>
</Popconfirm>,
<Button
type="link"
onClick={() => {
setModalRecord(record);
setPwdModalVisible(true);
}}
>重置密码</Button>
]
},
},
];
//弹窗确认
const onConfirm = (values) => {
if (modalType == 'edit') {
dispatch(updateUser(modalRecord.id, values.contract)).then(res => {
if (res.success) {
setModalVisible(false);
dispatch(getDepUser(depSelectedKeys[0]));
}
});
} else {
dispatch(createUser(values.contract)).then(res => {
if (res.success) {
setModalVisible(false);
dispatch(getDepUser(depSelectedKeys[0]));
}
});
}
}
//打开弹窗
const openModal = (type, record) => {
setModalVisible(true);
setModalType(type);
if (type == 'edit') {
setModalRecord(record);
} else {
setModalRecord(null);
}
}
//删除用户
const delUsers = (ids, type) => {
dispatch(delUser(ids)).then(res => {
dispatch(getDepUser(depSelectedKeys[0]));
if (type == 'batch') {
setRowSelected([]);
}
});
}
//重置密码
const onPwdConfirm = (values) => {
dispatch(resetPwd(modalRecord.id, { password: values.password })).then(res => {
if (res.success) {
setPwdModalVisible(false);
dispatch(getDepUser(depSelectedKeys[0]));
}
});
}
const openDeptModal = (type, record) => {
console.log(type, record, 'type, record')
setDeptModalVisible(true);
setDeptModalType(type);
if (type === 'edit') {
setDeptModalRecord(record);
} else {
setDeptModalRecord(null);
}
}
const onDeptConfirm = (values) => {
if (deptModalType === 'edit') {
dispatch(updateDept(deptModalRecord.id, values.contract)).then(res => {
if (res.success) {
setDeptModalVisible(false);
dispatch(getDepMessage())
}
});
} else {
dispatch(createDept(values.contract)).then(res => {
if (res.success) {
setDeptModalVisible(false);
dispatch(getDepMessage())
}
});
}
}
const delDepartment = (id) => {
dispatch(delDept(id)).then(res => {
if (res.success) {
dispatch(getDepMessage())
}
});
}
const renderTree = (item, id) => {
// let cannotDel = item.users.length || item.subordinate?.filter(is => is.users.length).length;//自己下面有成员 或者下级部门下有成员 不能删除
return <div style={{ display: 'flex', width: '6vw', justifyContent: 'space-between' }}>
<Tooltip title={item.name}>
<div style={{ width: '70%', textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>{item.name}</div>
</Tooltip>
<div style={{ width: '30%' }} >
{
depSelectedKeys == id && user.username === "SuperAdmin" ?
<>
<FormOutlined onClick={() => {
setDeptModalRecord(item)
setDeptModalVisible(true)
setDeptModalType('edit')
}} />
{
<Popconfirm title='是否确认删除?' onConfirm={() => { delDepartment(id) }}>
<DeleteOutlined style={{ marginLeft: 5 }} />
</Popconfirm>
}
</> : null
}
</div>
</div>
}
return (<div >
<Spin spinning={loading} /* style={{ height: "calc(100vh - 70px)" }} */>
<Row gutter={16} /* style={{ overflow: "scroll" }} */>
<Col flex="260px" style={{ height: '100%' }}>
<Card title="部门" bordered={false} bodyStyle={{ padding: 8, paddingTop: 24, }}>
{
user.username === "SuperAdmin" && <Button
type="primary"
key="primary"
style={{ margin: '16px 0px' }}
onClick={() => openDeptModal('create')}
>新建部门</Button>
}
{
depMessage.length ?
<Tree
height={clientHeight - 95}
defaultExpandedKeys={[depMessage[0].id]}
selectedKeys={depSelectedKeys}
onSelect={(selectedKeys, e) => {
if (e.selected) {
setDepSelectedKeys(selectedKeys)
dispatch(getDepUser(selectedKeys[0]))
}
}}
// treeData={depMessage}
// fieldNames={{
// title: 'name',
// key: 'id',
// children: 'subordinate'
// }}
>
{
depMessage.map((s, index) => {
return <TreeNode title={renderTree(s, s.id)} key={s.id} >
{
s.subordinate.map(k => {
return <TreeNode title={renderTree(k, k.id)} key={k.id} onMouseOver={() => { setIShowIcon(k.id) }} onMouseOut={() => { setIShowIcon(null) }}>
</TreeNode>
})
}
</TreeNode>
})
}
</Tree> : ''
}
</Card>
</Col>
<Col /* flex="auto" */ style={{ width: "calc(100% - 260px)", height: '100%', display: "black" }}>
<Card title="用户" bordered={false} height={clientHeight} bodyStyle={{ padding: 8, paddingTop: 24, overflow: "hidden", width: "100%" }}>
<ProTable
columns={columns}
dataSource={depUser}
style={{ width: "100% ", height: clientHeight - 95, overflow: "auto" }}
rowSelection={{
selectedRowKeys: rowSelected,
onChange: (selectedRowKeys) => {
setRowSelected(selectedRowKeys);
},
getCheckboxProps: (record) => {
return {
disabled: record.username === 'SuperAdmin',
}
},
}}
options={false}
search={false}
rowKey="id"
toolBarRender={() => [
<span>
<Button
type="primary"
key="primary"
style={{ marginRight: 10 }}
onClick={() => openModal('create')}
>新建用户</Button>
<Button style={{ marginRight: 10 }} onClick={() => { dispatch(getDepUser(depSelectedKeys[0])); }}>刷新</Button>
<Popconfirm title="确认删除?" onConfirm={() => { delUsers(rowSelected, 'batch') }}>
<Button>批量删除</Button>
</Popconfirm>
</span>
]}
/>
</Card>
{
deptModalVisible ?
<DeptModal
visible={deptModalVisible}
onVisibleChange={setDeptModalVisible}
modalType={deptModalType}
onConfirm={onDeptConfirm}
editData={deptModalRecord}
depts={depMessage}
/>
: ''
}
{
depMessage.length && modalVisible ?
<UserModal
visible={modalVisible}
onVisibleChange={setModalVisible}
modalType={modalType}
onConfirm={onConfirm}
editData={modalRecord}
/>
: ''
}
{pwdModalVisible ? <ResetPwd visible={pwdModalVisible}
onVisibleChange={setPwdModalVisible}
onConfirm={onPwdConfirm} /> : ''}
</Col>
</Row>
</Spin>
</div>
)
}
function mapStateToProps(state) {
const { depMessage, depUser, global } = state;
return {
clientHeight: global.clientHeight,
loading: depMessage.isRequesting,
depMessage: depMessage.data || [],
depUser: depUser.data || []
};
}
export default connect(mapStateToProps)(UserManage);

15
web/client/src/sections/organization/index.js

@ -0,0 +1,15 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'organization',
name: '',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

22
web/client/src/sections/organization/nav-item.js

@ -0,0 +1,22 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem(user, dispatch) {
// if (!Func.isAuthorized("ORG_MANAGE")) {
// return null
// }
return (
<SubMenu key="organization" icon={<SettingOutlined />} title={'组织管理'}>
<Menu.Item key="userManage">
<Link to="/organization/user">部门成员</Link>
</Menu.Item>
<Menu.Item key="authority">
<Link to="/organization/authority">权限配置</Link>
</Menu.Item>
</SubMenu>
);
}

5
web/client/src/sections/organization/reducers/index.js

@ -0,0 +1,5 @@
'use strict';
export default {
};

26
web/client/src/sections/organization/routes.js

@ -0,0 +1,26 @@
'use strict';
import { UserManage, Authority } from './containers';
export default [{
type: 'inner',
route: {
path: '/organization',
key: 'organization',
breadcrumb: '组织管理',
menuSelectKeys: ['userManage'],
menuOpenKeys: ['organization'],
childRoutes: [{
path: '/user',
key: 'userManage',
menuSelectKeys: ['userManage'],
component: UserManage,
breadcrumb: '部门成员',
}, {
path: '/authority',
key: 'authority',
component: Authority,
menuSelectKeys: ['authority'],
breadcrumb: '权限配置',
}]
}
}];

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

@ -7,6 +7,23 @@ export const ApiTable = {
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}',
// 用户权限
getResource: 'resource',
getUserResource: 'user/resource',
postUserRes: 'user/resource',
//安全风险预报
getSiteWeekRegiste: 'sites/report',
getRiskReportList: 'risk/report',

Loading…
Cancel
Save