@ -1,122 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
async function getPatrolPlan(ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const { limit, page } = ctx.query; |
|||
let options = { |
|||
include: [{ |
|||
required: true, |
|||
model: models.User, |
|||
attributes: ['id', 'name'], |
|||
include: [{ |
|||
required: true, |
|||
model: models.Department, |
|||
attributes: ['id', 'name'], |
|||
}] |
|||
}, { |
|||
required: true, |
|||
model: models.Project, |
|||
attributes: ['id', 'name'], |
|||
}] |
|||
}; |
|||
if (limit) { |
|||
options.limit = Number(limit); |
|||
} |
|||
if (page && limit) { |
|||
options.offset = Number(page) * Number(limit); |
|||
} |
|||
let res = await models.PatrolPlan.findAndCountAll(options); |
|||
ctx.status = 200; |
|||
ctx.body = res; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取巡检计划失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function createPatrolPlan(ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const data = ctx.request.body; |
|||
const { name, way, structureId, startTime, endTime, frequency, points, userId } = data; |
|||
|
|||
let plan = { name, way, structureId, startTime, endTime, frequency, points, userId }; |
|||
|
|||
await models.PatrolPlan.create(plan); |
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": '新增巡检计划失败' |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function updatePatrolPlan(ctx, next) { |
|||
try { |
|||
let errMsg = '修改巡检计划失败'; |
|||
const models = ctx.fs.dc.models; |
|||
const data = ctx.request.body; |
|||
const { name, way, structureId, startTime, endTime, frequency, points, userId } = data; |
|||
|
|||
let plan = { name, way, structureId, startTime, endTime, frequency, points, userId }; |
|||
|
|||
if (data && data.id) { |
|||
await models.PatrolPlan.update(plan, { |
|||
where: { id: data.id } |
|||
}) |
|||
} else { |
|||
errMsg = '请传入巡检计划id'; |
|||
throw errMsg; |
|||
} |
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": errMsg |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function delPatrolPlan(ctx, next) { |
|||
try { |
|||
let errMsg = '删除巡检计划失败'; |
|||
|
|||
const models = ctx.fs.dc.models; |
|||
const { id } = ctx.params; |
|||
|
|||
const record = await models.PatrolRecord.findOne({ |
|||
where: { patrolPlanId: id } |
|||
}); |
|||
|
|||
if (record) { |
|||
errMsg = '不能删除有巡检记录的计划'; |
|||
throw errMsg; |
|||
} |
|||
|
|||
await models.PatrolPlan.destroy({ |
|||
where: { id } |
|||
}) |
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { message: error } |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
getPatrolPlan, |
|||
createPatrolPlan, |
|||
updatePatrolPlan, |
|||
delPatrolPlan, |
|||
} |
@ -1,113 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
async function findPatrolRecord(ctx, next) { |
|||
let rslt = []; |
|||
let error = { name: 'FindError', message: '获取巡检记录失败' }; |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const { startTime, endTime, alarm, patrolPlanId, pointId } = ctx.params; |
|||
// patrolPlanId传all查所有
|
|||
if (patrolPlanId == 'all') { |
|||
/* 如果有startTime && endTime,查询所有符合条件的数据 */ |
|||
if (startTime !== 'null' && endTime !== 'null') { |
|||
if (pointId !== 'null') { |
|||
if (alarm == 'null') { |
|||
rslt = await models.PatrolRecord.findAll({ |
|||
where: { inspectionTime: { $between: [startTime, endTime] }, pointId: { $in: pointId.split(',') } }, |
|||
}); |
|||
} else { |
|||
rslt = await models.PatrolRecord.findAll({ |
|||
where: { alarm, inspectionTime: { $between: [startTime, endTime] }, pointId: { $in: pointId.split(',') } }, |
|||
}); |
|||
} |
|||
} else { |
|||
if (alarm == 'null') { |
|||
rslt = await models.PatrolRecord.findAll({ |
|||
where: { inspectionTime: { $between: [startTime, endTime] } }, |
|||
}); |
|||
} else { |
|||
rslt = await models.PatrolRecord.findAll({ |
|||
where: { alarm, inspectionTime: { $between: [startTime, endTime] } }, |
|||
}); |
|||
} |
|||
} |
|||
} else { |
|||
/* 如果没有startTime && endTime,查询每个点位最新一条符合条件的数据 */ |
|||
let a = [] |
|||
if (pointId !== 'null') { |
|||
a = await models.PatrolRecord.findAll({ |
|||
where: { pointId: { $in: pointId.split(',') } }, |
|||
}); |
|||
} |
|||
rslt = pointId.split(',').map(i => { |
|||
return a.filter(t => t.pointId == i).sort((a, b) => b.id - a.id)[0] || null |
|||
}) |
|||
} |
|||
} else { |
|||
if (startTime !== 'null' && endTime !== 'null') { |
|||
if (pointId !== 'null') { |
|||
rslt = await models.PatrolRecord.findAll({ |
|||
where: { patrolPlanId: { $in: patrolPlanId.split(',') }, alarm, inspectionTime: { $between: [startTime, endTime] }, pointId: { $in: pointId.split(',') } }, |
|||
}); |
|||
} else { |
|||
rslt = await models.PatrolRecord.findAll({ |
|||
where: { patrolPlanId: { $in: patrolPlanId.split(',') }, alarm, inspectionTime: { $between: [startTime, endTime] } }, |
|||
}); |
|||
} |
|||
|
|||
} else { |
|||
let a = [] |
|||
/* 如果没有startTime && endTime,查询每个点位最新一条符合条件的数据 */ |
|||
if (pointId !== 'null') { |
|||
a = await models.PatrolRecord.findAll({ |
|||
where: { patrolPlanId: { $in: patrolPlanId.split(',') }, pointId: { $in: pointId.split(',') } }, |
|||
}); |
|||
} else { |
|||
a = await models.PatrolRecord.findAll({ |
|||
where: { patrolPlanId: { $in: patrolPlanId.split(',') } }, |
|||
}); |
|||
} |
|||
|
|||
rslt = pointId.split(',').map(i => { |
|||
return a.filter(t => t.pointId == i).sort((a, b) => b.id - a.id)[0] || null |
|||
}) |
|||
} |
|||
} |
|||
|
|||
ctx.status = 200; |
|||
ctx.body = rslt; |
|||
error = null |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取巡检记录失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function addPatrolRecord(ctx, next) { |
|||
let error = { name: 'addError', message: '新增巡检记录失败' }; |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
const data = ctx.request.body; |
|||
let { patrolPlanId, lastInspectionTime, inspectionTime, points, alarm, pointId } = data |
|||
let record = { patrolPlanId, lastInspectionTime, inspectionTime, points, alarm, pointId } |
|||
|
|||
await models.PatrolRecord.create(record); |
|||
|
|||
ctx.status = 204; |
|||
error = null |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": '新增巡检计划失败' |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
findPatrolRecord, |
|||
addPatrolRecord, |
|||
} |
@ -1,431 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
|
|||
async function projectList (ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let userInfo = ctx.fs.api.userInfo; |
|||
const { limit, page, name, justStructure } = ctx.query; |
|||
|
|||
let options = { |
|||
where: { |
|||
|
|||
}, |
|||
// include: [{
|
|||
// as: 'company',
|
|||
// model: models.Company,
|
|||
// attributes: ['id', 'name'],
|
|||
// },],
|
|||
} |
|||
if (limit) { |
|||
options.limit = Number(limit) |
|||
} |
|||
if (page && limit) { |
|||
options.offset = Number(page) * Number(limit) |
|||
} |
|||
if (name) { |
|||
options.where.name = { $like: `%${name}%` } |
|||
} |
|||
|
|||
let res = [] |
|||
if (justStructure) { |
|||
res = await models.Project.findAndCountAll({ |
|||
attributes: ['id', 'name'], |
|||
}) |
|||
} else { |
|||
res = await models.Project.findAndCountAll(options) |
|||
} |
|||
|
|||
ctx.status = 200; |
|||
ctx.body = res |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取结构列表失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
async function postAddProject (ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let userInfo = ctx.fs.api.userInfo; |
|||
const data = ctx.request.body; |
|||
const { img, longitude, latitude, name, type, describe } = data |
|||
|
|||
let errMsg = data.id ? '结构物编辑失败' : '结构物新增失败' |
|||
let project = { img, longitude, latitude, name, type, describe, userId: userInfo.id } |
|||
|
|||
const alikeProject = await models.Project.findOne({ |
|||
where: { |
|||
name: name, |
|||
} |
|||
}) |
|||
|
|||
if ((!data.id && alikeProject) || (alikeProject && alikeProject.id !== data.id)) { |
|||
errMsg = '已有相同结构物名称' |
|||
throw errMsg |
|||
} |
|||
if (data && data.id) { |
|||
await models.Project.update(project, { |
|||
where: { |
|||
id: data.id |
|||
} |
|||
}) |
|||
} else { |
|||
await models.Project.create(project) |
|||
} |
|||
|
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": errMsg |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function delProject (ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let userInfo = ctx.fs.api.userInfo; |
|||
const { id } = ctx.params |
|||
|
|||
//删除结构物
|
|||
await models.Project.destroy({ |
|||
where: { |
|||
id, |
|||
} |
|||
}) |
|||
const pointId = [] |
|||
const pointLIst = await models.Point.findAll({ |
|||
where: { |
|||
projectId: id, |
|||
}, |
|||
attributes: ['id'], |
|||
}) |
|||
pointLIst.map(v => pointId.push(v.id)) |
|||
|
|||
//点位
|
|||
await models.Point.destroy({ |
|||
where: { |
|||
projectId: id |
|||
} |
|||
}) |
|||
|
|||
|
|||
//巡检计划
|
|||
const planId = [] |
|||
const planLIst = await models.PatrolPlan.findAll({ |
|||
where: { |
|||
structureId: id, |
|||
}, |
|||
attributes: ['id'], |
|||
}) |
|||
planLIst.map(v => planId.push(v.id)) |
|||
await models.PatrolPlan.destroy({ |
|||
where: { |
|||
structureId: id |
|||
} |
|||
}) |
|||
|
|||
//巡检记录
|
|||
await models.PatrolRecord.destroy({ |
|||
where: { |
|||
pointId: { $in: pointId }, |
|||
patrolPlanId: { $in: planId } |
|||
} |
|||
}) |
|||
|
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": '删除结构物失败' |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function addPosition (ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let userInfo = ctx.fs.api.userInfo; |
|||
const data = ctx.request.body; |
|||
const { longitude, latitude, name, describe, qrCode, projectId, } = data |
|||
|
|||
let errMsg = data.id ? '点位编辑失败' : '点位新增失败' |
|||
let pointData = { longitude, latitude, name, describe, qrCode, projectId } |
|||
|
|||
const alikeProject = await models.Point.findOne({ |
|||
where: { |
|||
id: data.id, |
|||
} |
|||
}) |
|||
|
|||
if (data && data.id) { |
|||
if (qrCode) { |
|||
await models.Point.update({ ...alikeProject, qrCode }, { |
|||
where: { |
|||
id: data.id, |
|||
} |
|||
}) |
|||
} else { |
|||
await models.Point.update({ pointData }, { |
|||
where: { |
|||
id: data.id, |
|||
} |
|||
}) |
|||
} |
|||
|
|||
} else { |
|||
await models.Point.create(pointData) |
|||
} |
|||
|
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": errMsg |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function position (ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let userInfo = ctx.fs.api.userInfo; |
|||
const { limit, page, projectId } = ctx.query; |
|||
|
|||
let options = { |
|||
where: { |
|||
id: projectId |
|||
}, |
|||
include: [{ |
|||
model: models.Point, |
|||
},], |
|||
} |
|||
if (limit) { |
|||
options.limit = Number(limit) |
|||
} |
|||
if (page && limit) { |
|||
options.offset = Number(page) * Number(limit) |
|||
} |
|||
|
|||
let res = await models.Project.findAndCountAll(options) |
|||
ctx.status = 200; |
|||
ctx.body = res |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取结构列表失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function delPosition (ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let userInfo = ctx.fs.api.userInfo; |
|||
const { id } = ctx.params |
|||
|
|||
const pointOne = await models.Point.findOne({ |
|||
where: { |
|||
id |
|||
}, |
|||
attributes: ['projectId'], |
|||
}) |
|||
if (pointOne) { |
|||
const patrolPlanLIst = await models.PatrolPlan.findAll({ |
|||
where: { |
|||
structureId: pointOne.projectId, |
|||
}, |
|||
}) |
|||
|
|||
for (var u of patrolPlanLIst) { |
|||
const points = [] |
|||
u.points.map(r => { |
|||
if (r.id == id) { |
|||
} else { |
|||
points.push(r) |
|||
} |
|||
}) |
|||
u.points = points |
|||
|
|||
await models.PatrolRecord.destroy({ |
|||
where: { |
|||
pointId: id, |
|||
patrolPlanId: u.id |
|||
} |
|||
}) |
|||
|
|||
if (points.length > 0) { |
|||
let data = { |
|||
name: u.dataValues.name, |
|||
way: u.dataValues.way, |
|||
structureId: u.dataValues.structureId, |
|||
startTime: u.dataValues.startTime, |
|||
endTime: u.dataValues.endTime, |
|||
frequency: u.dataValues.frequency, |
|||
points: u.dataValues.points, |
|||
userId: u.dataValues.userId, |
|||
patrolCount: u.dataValues.patrolCount |
|||
} |
|||
await models.PatrolPlan.update(data,{ |
|||
where: { |
|||
id: u.dataValues.id |
|||
} |
|||
}) |
|||
} else { |
|||
await models.PatrolPlan.destroy({ |
|||
where: { |
|||
id: u.id |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
await models.Point.destroy({ |
|||
where: { |
|||
id, |
|||
} |
|||
}) |
|||
|
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": '删除点位失败' |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
async function qrCodeShow (ctx, next) { |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let userInfo = ctx.fs.api.userInfo; |
|||
const { projectId, name } = ctx.query; |
|||
|
|||
let options = { |
|||
where: { |
|||
qrCode: { $ne: null } |
|||
}, |
|||
} |
|||
if (projectId) { |
|||
options.where.projectId = projectId |
|||
} |
|||
if (name) { |
|||
options.where.name = { $like: `%${name}%` } |
|||
} |
|||
|
|||
let res = await models.Point.findAndCountAll(options) |
|||
ctx.status = 200; |
|||
ctx.body = res |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
"message": "获取二维码列表失败" |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
async function q (ctx) { |
|||
// let error = {
|
|||
// name: 'FindError',
|
|||
// message: "获取失败!"
|
|||
// };
|
|||
// const models = ctx.fs.dc.models;
|
|||
// const { devices } = ctx.request.body
|
|||
// const attachment = ctx.app.fs.qn_attachment
|
|||
|
|||
// try {
|
|||
|
|||
// if (!Array.isArray(devices)) {
|
|||
// error = { name: 'paramsError', message: '参数不能为空' };
|
|||
// ctx.throw(400);
|
|||
// }
|
|||
// const devicesArr = await models.Device.findAll({
|
|||
// attributes: ['deviceNo', 'periodCode', 'qrSrc'],
|
|||
// where: { deviceNo: { $in: devices } }
|
|||
// })
|
|||
|
|||
// let ids = [], idsMap = {}, qnImages = []
|
|||
// devicesArr.forEach(d => {
|
|||
// const qrSrc = d.qrSrc
|
|||
// const deviceNo = d.deviceNo
|
|||
// const periodCode = d.periodCode
|
|||
// if (qrSrc) {
|
|||
// if (/^\d+$/.test(qrSrc)) {
|
|||
// ids.push(qrSrc)
|
|||
// idsMap[qrSrc] = { deviceNo, periodCode }
|
|||
// } else {
|
|||
// let domain = globalCache.getQnDomain()
|
|||
// let imgUrl = `${domain}/${qrSrc}`
|
|||
// qnImages.push({ src: imgUrl, deviceNo, periodCode })
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
|
|||
// const docs = await models.QrcodePng.findAll({
|
|||
// where: {
|
|||
// id: { $in: ids }
|
|||
// },
|
|||
// attributes: ["id", "base64"]
|
|||
// })
|
|||
|
|||
// let pics = []
|
|||
|
|||
// if (docs.length > 0) {
|
|||
// pics = docs.map((d) => {
|
|||
// let { deviceNo, periodCode } = idsMap[d.id] || {}
|
|||
// let base64 = d.base64.replace(/^data:image\/\w+;base64,/, '')
|
|||
// return {
|
|||
// url: Buffer.from(base64, 'base64'),
|
|||
// name: deviceNo,
|
|||
// periodCode
|
|||
// }
|
|||
// })
|
|||
// }
|
|||
|
|||
// if (qnImages.length > 0) {
|
|||
// let qns = await downloadImgsAsBase64(qnImages)
|
|||
// pics = pics.concat(qns)
|
|||
// }
|
|||
|
|||
// let fileUrl = await downLoadImageBiz(pics, { zipName: "二维码_" + moment().format("YYYY-MM-DD-HH-mm-ss"), attachment })
|
|||
// add2CleanCache(fileUrl, attachment)
|
|||
// ctx.status = 200
|
|||
// ctx.body = { fileUrl }
|
|||
|
|||
// } catch (err) {
|
|||
// ctx.fs.logger.error(err);
|
|||
// ctx.status = 400;
|
|||
// ctx.body = error;
|
|||
// }
|
|||
} |
|||
|
|||
module.exports = { |
|||
projectList, |
|||
postAddProject, |
|||
delProject, |
|||
addPosition, |
|||
position, |
|||
delPosition, |
|||
qrCodeShow, |
|||
q |
|||
} |
@ -1,17 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
const patrolPlan = require('../../controllers/patrolPlan/patrolPlan'); |
|||
|
|||
module.exports = function (app, router, opts) { |
|||
app.fs.api.logAttr['GET/patrolPlan'] = { content: '获取巡检计划', visible: false }; |
|||
router.get('/patrolPlan', patrolPlan.getPatrolPlan); |
|||
|
|||
app.fs.api.logAttr['POST/patrolPlan'] = { content: '新增巡检计划', visible: true }; |
|||
router.post('/patrolPlan', patrolPlan.createPatrolPlan); |
|||
|
|||
app.fs.api.logAttr['PUT/patrolPlan'] = { content: '修改巡检计划', visible: true }; |
|||
router.put('/patrolPlan', patrolPlan.updatePatrolPlan); |
|||
|
|||
app.fs.api.logAttr['DELETE/patrolPlan/:id'] = { content: '删除巡检计划', visible: true }; |
|||
router.del('/patrolPlan/:id', patrolPlan.delPatrolPlan); |
|||
}; |
@ -1,12 +0,0 @@ |
|||
'use strict'; |
|||
const patrolRecord = require('../../controllers/patrolRecord/patrolRecord'); |
|||
|
|||
module.exports = function (app, router, opts) { |
|||
app.fs.api.logAttr['GET/patrolRecord/:patrolPlanId/:startTime/:endTime/:alarm/:pointId'] = { content: '获取巡检记录', visible: true }; |
|||
// web端、小程序端查数据:patrolPlanId为all,不需要传pointId
|
|||
// 小程序端查点位最新一条数据:startTime、endTime、alarm不传
|
|||
router.get('/patrolRecord/:patrolPlanId/:startTime/:endTime/:alarm/:pointId', patrolRecord.findPatrolRecord); |
|||
|
|||
app.fs.api.logAttr['POST/patrolRecord/add'] = { content: '新增巡检记录', visible: true } |
|||
router.post('/patrolRecord/add', patrolRecord.addPatrolRecord); |
|||
}; |
@ -1,31 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
const projectSituation = require('../../controllers/projectRegime/projectSituation'); |
|||
|
|||
module.exports = function (app, router, opts) { |
|||
|
|||
app.fs.api.logAttr['GET/projectList'] = { content: '获取结构物列表', visible: false }; |
|||
router.get('/projectList', projectSituation.projectList); |
|||
|
|||
app.fs.api.logAttr['POST/addProject'] = { content: '新增修改结构物', visible: false }; |
|||
router.post('/addProject', projectSituation.postAddProject); |
|||
|
|||
app.fs.api.logAttr['DEL/delProject/:id'] = { content: '删除结构物', visible: false }; |
|||
router.del('/delProject/:id', projectSituation.delProject); |
|||
|
|||
app.fs.api.logAttr['POST/position'] = { content: '新增修改点位', visible: false }; |
|||
router.post('/position', projectSituation.addPosition); |
|||
|
|||
app.fs.api.logAttr['GET/position'] = { content: '获取点位列表', visible: false }; |
|||
router.get('/position', projectSituation.position); |
|||
|
|||
app.fs.api.logAttr['DEL/delPosition/:id'] = { content: '删除点位', visible: false }; |
|||
router.del('/delPosition/:id', projectSituation.delPosition); |
|||
|
|||
app.fs.api.logAttr['GET/qrCodeShow'] = { content: '获取二维码列表', visible: false }; |
|||
router.get('/qrCodeShow', projectSituation.qrCodeShow); |
|||
|
|||
app.fs.api.logAttr['GET/q'] = { content: '获取二维码列表', visible: false }; |
|||
router.get('/q', projectSituation.q); |
|||
|
|||
} |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 202 KiB |
Before Width: | Height: | Size: 725 B |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 169 KiB |
Before Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 873 B |
Before Width: | Height: | Size: 206 KiB |
Before Width: | Height: | Size: 150 B |
Before Width: | Height: | Size: 950 B |
Before Width: | Height: | Size: 873 B |
Before Width: | Height: | Size: 815 B |
Before Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 147 B |
Before Width: | Height: | Size: 999 B |
Before Width: | Height: | Size: 648 B |
Before Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 646 KiB |
Before Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 234 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 23 KiB |
@ -1,316 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import React, { Component } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { Spin, Upload, message, Modal, Card, Button } from 'antd'; |
|||
import moment from 'moment'; |
|||
import { PlusOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons'; |
|||
|
|||
class Uploads extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.ApiRoot = localStorage.getItem('tyApiRoot') |
|||
this.state = { |
|||
fileUploading: false, |
|||
fileList: [], |
|||
curPreviewPic: '', |
|||
delPicIng: false, |
|||
removeFilesList: [] |
|||
}; |
|||
} |
|||
|
|||
dealName = (uploaded) => { |
|||
let realName = uploaded.split('/')[2] |
|||
let x1 = realName.split('.') |
|||
let x2 = x1[0].split('_') |
|||
let showName = `${x2[0]}.${x1[1]}` |
|||
return showName |
|||
} |
|||
|
|||
// setFileList = (value) => {
|
|||
// let defaultFileList = [];
|
|||
// defaultFileList = value.map((u, index) => {
|
|||
// let fileUrl = `${this.ApiRoot}/${u.url}`;
|
|||
// return {
|
|||
// uid: -index - 1,
|
|||
// name: this.dealName(u.url),
|
|||
// status: 'done',
|
|||
// storageUrl: u.url,
|
|||
// url: fileUrl
|
|||
// };
|
|||
// });
|
|||
// onChange(defaultFileList)
|
|||
// this.setState({
|
|||
// fileList: defaultFileList
|
|||
// });
|
|||
// };
|
|||
|
|||
componentDidMount() { |
|||
const { value } = this.props; |
|||
if (value) { |
|||
this.setState(value); |
|||
} |
|||
} |
|||
|
|||
componentWillReceiveProps(np) { |
|||
const { dispatch, value: thisEditData, onChange } = this.props; |
|||
const { value: nextEditData } = np; |
|||
|
|||
const setFileList = () => { |
|||
let defaultFileList = []; |
|||
defaultFileList = nextEditData.map((u, index) => { |
|||
let fileUrl = `${this.ApiRoot}/${u.storageUrl}`; |
|||
return { |
|||
uid: -index - 1, |
|||
name: this.dealName(u.storageUrl), |
|||
status: 'done', |
|||
storageUrl: u.storageUrl, |
|||
url: fileUrl, |
|||
size: u.size || -1 |
|||
}; |
|||
}); |
|||
this.setState({ |
|||
fileList: defaultFileList |
|||
}); |
|||
}; |
|||
|
|||
if (nextEditData && nextEditData.length) { |
|||
if (!thisEditData || !this.state.fileList.length) { |
|||
setFileList(); |
|||
} else if (nextEditData.length != thisEditData.length) { |
|||
setFileList(); |
|||
} else { |
|||
let repeat = true; |
|||
for (let i = 0; i < thisEditData.length; i++) { |
|||
if (thisEditData[i] != nextEditData[i]) { |
|||
repeat = false; |
|||
break; |
|||
} |
|||
} |
|||
if (!repeat) { |
|||
setFileList(); |
|||
} |
|||
} |
|||
} |
|||
// else{
|
|||
// this.setState({
|
|||
// fileList:[],
|
|||
// })
|
|||
// }
|
|||
} |
|||
|
|||
render() { |
|||
const UploadPath = { |
|||
project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'png', 'jpg', 'rar', 'zip'], |
|||
report: ['doc', 'docx', 'xls', 'xlsx', 'pdf'], |
|||
data: ['txt', 'xls', 'xlsx'], |
|||
image: ['png', 'jpg', 'svg', 'jpeg'], |
|||
three: ['js'], |
|||
video: ['mp4'] |
|||
}; |
|||
/** |
|||
* uploadType 【string】 主要区别文件上传路径 以及类型 以 web/routes/attachment/index.js 中 UploadPath 的 key 值为准;默认 project; |
|||
* disabled 【boolean】 上传是否可用 |
|||
* maxFilesNum 【number】 最大上传数量 |
|||
* fileTypes 【array[string]】 可允许上传的文件类型; |
|||
* maxFileSize 【number】 单个文件最大大小 M |
|||
* listType 【antd】 upload 组件的属性 |
|||
* onChange 【function】 文件数量变化时候回调 返回文件 |
|||
* value 【array[obj]】 编辑数据 [{url:'xxx', [size:999]}] |
|||
* onStateChange 【function】 文件状态改变回调函数 上传中 return { uploading:true/false } |
|||
*/ |
|||
const { |
|||
uploadType, |
|||
disabled, |
|||
maxFilesNum, |
|||
fileTypes, |
|||
maxFileSize, |
|||
listType, |
|||
onChange, |
|||
value, |
|||
showUploadList, |
|||
onStateChange |
|||
} = this.props; |
|||
const { fileList, curPreviewPic, delPicIng, removeFilesList } = this.state; |
|||
const that = this; |
|||
let uploadType_ = uploadType || 'project'; |
|||
let maxFilesNum_ = maxFilesNum || 1; |
|||
let defaultFileTypes = fileTypes || UploadPath[uploadType_]; |
|||
const uploadProps = { |
|||
name: 'checkFile_', |
|||
multiple: false, |
|||
showUploadList: showUploadList || true, |
|||
action: `${this.ApiRoot}/attachments/${uploadType_}`, |
|||
listType: listType || 'text', |
|||
disabled: disabled, |
|||
beforeUpload: (file) => { |
|||
if (fileList.length >= maxFilesNum_) { |
|||
message.warning(`最多选择${maxFilesNum_}个文件上传`); |
|||
return false; |
|||
} |
|||
if (file.name.length > 60) { |
|||
message.warning(`文件名过长(大于60字符),请修改后上传`); |
|||
return false; |
|||
} |
|||
const extNames = file.name.split('.'); |
|||
var reg = /^[\.\s\u4e00-\u9fa5a-zA-Z0-9_-]{0,}$/; |
|||
if (!reg.exec(file.name)) { |
|||
message.warning(`文件名包含除字母、汉字、数字、中划线、下划线之外的字符,请修改后上传`); |
|||
return false; |
|||
} |
|||
let isDAE = false; |
|||
if (extNames.length > 0) { |
|||
let fileType = extNames[extNames.length - 1].toLowerCase(); |
|||
isDAE = defaultFileTypes.some((f) => f == fileType); |
|||
} |
|||
if (!isDAE) { |
|||
message.error(`只能上传 ${defaultFileTypes.join()} 格式的文件!`); |
|||
return false; |
|||
} |
|||
const isLt = file.size / 1024 / 1024 < (maxFileSize || 3); |
|||
if (!isLt) { |
|||
message.error(`文件必须小于${maxFileSize || 3}MB!`); |
|||
return false; |
|||
} |
|||
this.setState({ |
|||
fileUploading: true |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: true }); |
|||
} |
|||
}, |
|||
onChange(info) { |
|||
const status = info.file.status; |
|||
if (status === 'uploading') { |
|||
that.setState({ |
|||
fileList: info.fileList |
|||
}); |
|||
} |
|||
if (status === 'done') { |
|||
let { uploaded, url } = info.file.response; |
|||
let size = info.file.size; |
|||
let nextFileList = fileList; |
|||
nextFileList[nextFileList.length - 1] = { |
|||
uid: -moment().unix(), |
|||
name: that.dealName(uploaded), |
|||
status: 'done', |
|||
storageUrl: uploaded, |
|||
url: url, |
|||
size: size |
|||
}; |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileUploading: false, |
|||
fileList: nextFileList |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} else if (status === 'error') { |
|||
that.setState({ |
|||
fileUploading: false |
|||
}); |
|||
message.error(`${info.file.name} 上传失败,请重试`); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} |
|||
}, |
|||
onRemove(file) { |
|||
let nextFileList = []; |
|||
fileList.map((f, i) => { |
|||
if (f.uid != file.uid) { |
|||
nextFileList.push(f); |
|||
} |
|||
}); |
|||
let nextRemoveFiles = removeFilesList.concat([file.storageUrl]); |
|||
if (curPreviewPic == file.url) { |
|||
that.setState({ |
|||
curPreviewPic: '' |
|||
}); |
|||
} |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileList: nextFileList, |
|||
removeFilesList: nextRemoveFiles |
|||
}); |
|||
}, |
|||
onPreview(file) { |
|||
let filePostfix = file.url.split('.').pop(); |
|||
filePostfix = filePostfix.toLowerCase(); |
|||
if (UploadPath.image.some((img) => img == filePostfix)) { |
|||
that.setState({ |
|||
curPreviewPic: file.url |
|||
}); |
|||
} else { |
|||
message.warn('仅支持图片预览'); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
let fileList_ = fileList |
|||
// .map(f => {
|
|||
// if (f.storageUrl) {
|
|||
// let realName = f.storageUrl.split('/').pop()
|
|||
// if (f.name != realName) {
|
|||
// f.name = realName
|
|||
// }
|
|||
// }
|
|||
// return f
|
|||
// })
|
|||
|
|||
return ( |
|||
<div> |
|||
<Spin spinning={delPicIng}> |
|||
<Upload {...uploadProps} fileList={fileList_}> |
|||
{ |
|||
disabled ? ( |
|||
'' |
|||
) : |
|||
listType == 'picture-card' ? |
|||
( |
|||
fileList.length >= maxFilesNum_ ? null : ( |
|||
<div style={{}}> |
|||
<PlusOutlined /> |
|||
<div>上传图片</div> |
|||
</div> |
|||
) |
|||
) : ( |
|||
<Button disabled={fileList.length >= maxFilesNum_} icon={<UploadOutlined />}> 文件上传 </Button> |
|||
) |
|||
} |
|||
</Upload> |
|||
{ |
|||
curPreviewPic ? ( |
|||
<Card |
|||
bodyStyle={{ |
|||
padding: 8 |
|||
}} |
|||
> |
|||
<div style={{ marginBottom: 8 }} > |
|||
<span>文件预览</span> |
|||
<span |
|||
style={{ float: 'right' }} |
|||
onClick={() => { this.setState({ curPreviewPic: '' }); }} |
|||
> |
|||
<CloseOutlined style={{ fontSize: 20 }} /> |
|||
</span> |
|||
</div> |
|||
<img style={{ width: '100%' }} src={curPreviewPic}></img> |
|||
</Card> |
|||
) : '' |
|||
} |
|||
</Spin> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth } = state |
|||
return { |
|||
user: auth.user |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(Uploads); |
@ -1,389 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import React, { Component } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { Spin, Upload, message, Modal, Card, Button } from 'antd'; |
|||
import moment from 'moment'; |
|||
import { PlusOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons'; |
|||
|
|||
class Uploads extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.ApiRoot = localStorage.getItem('tyApiRoot') |
|||
this.qnDomain = localStorage.getItem('qnDomain'); |
|||
this.aliAdmin = localStorage.getItem('aliAdmin'); |
|||
this.state = { |
|||
fileUploading: false, |
|||
fileList: [], |
|||
curPreviewPic: '', |
|||
curPreviewVideo: '', |
|||
delPicIng: false, |
|||
removeFilesList: [] |
|||
}; |
|||
} |
|||
|
|||
dealName = (uploaded) => { |
|||
let realName = uploaded.split('/')[2] |
|||
// let x1 = realName.split('.')
|
|||
// let postfix = x1.pop()
|
|||
// let allName = x1.join('.')
|
|||
// let x2 = allName.split('_')
|
|||
// let showName = `${x2[0]}.${postfix}`
|
|||
return realName |
|||
} |
|||
|
|||
// setFileList = (value) => {
|
|||
// let defaultFileList = [];
|
|||
// defaultFileList = value.map((u, index) => {
|
|||
// let fileUrl = `${this.ApiRoot}/${u.url}`;
|
|||
// return {
|
|||
// uid: -index - 1,
|
|||
// name: this.dealName(u.url),
|
|||
// status: 'done',
|
|||
// storageUrl: u.url,
|
|||
// url: fileUrl
|
|||
// };
|
|||
// });
|
|||
// onChange(defaultFileList)
|
|||
// this.setState({
|
|||
// fileList: defaultFileList
|
|||
// });
|
|||
// };
|
|||
|
|||
setFileList = (nextEditData, isQiniu, isAli) => { |
|||
let defaultFileList = []; |
|||
defaultFileList = nextEditData.map((u, index) => { |
|||
let fileUrl = |
|||
isQiniu ? `/_file-server/${u.storageUrl}` |
|||
: isAli ? `/_file-ali-server/${u.storageUrl}` |
|||
: `${this.ApiRoot}/${u.storageUrl}`; |
|||
|
|||
return { |
|||
uid: -index - 1, |
|||
name: this.dealName(u.storageUrl), |
|||
status: 'done', |
|||
storageUrl: u.storageUrl, |
|||
url: fileUrl, |
|||
size: u.size || -1 |
|||
}; |
|||
}); |
|||
this.setState({ |
|||
fileList: defaultFileList |
|||
}); |
|||
}; |
|||
|
|||
componentDidMount() { |
|||
const { value, defaultValue, isQiniu, isAli } = this.props; |
|||
if (defaultValue) { |
|||
this.setFileList(defaultValue, isQiniu, isAli) |
|||
} |
|||
} |
|||
|
|||
UNSAFE_componentWillReceiveProps(np) { |
|||
const { dispatch, value: thisEditData, onChange } = this.props; |
|||
const { value: nextEditData, isQiniu, isAli } = np; |
|||
// this.setFileList(nextEditData, isQiniu)
|
|||
// const setFileList = () => {
|
|||
// let defaultFileList = [];
|
|||
// defaultFileList = nextEditData.map((u, index) => {
|
|||
// let fileUrl = isQiniu ? `/_file-server/${u.storageUrl}` : `${this.ApiRoot}/${u.storageUrl}`;
|
|||
// return {
|
|||
// uid: -index - 1,
|
|||
// name: this.dealName(u.storageUrl),
|
|||
// status: 'done',
|
|||
// storageUrl: u.storageUrl,
|
|||
// url: fileUrl,
|
|||
// size: u.size || -1
|
|||
// };
|
|||
// });
|
|||
// this.setState({
|
|||
// fileList: defaultFileList
|
|||
// });
|
|||
// };
|
|||
if (nextEditData && nextEditData.length) { |
|||
if (!thisEditData || !this.state.fileList.length) { |
|||
this.setFileList(nextEditData, isQiniu, isAli); |
|||
} else if (nextEditData.length != thisEditData.length) { |
|||
this.setFileList(nextEditData, isQiniu, isAli); |
|||
} else { |
|||
let repeat = true; |
|||
for (let i = 0; i < thisEditData.length; i++) { |
|||
if (thisEditData[i] != nextEditData[i]) { |
|||
repeat = false; |
|||
break; |
|||
} |
|||
} |
|||
if (!repeat) { |
|||
this.setFileList(nextEditData, isQiniu, isAli); |
|||
} |
|||
} |
|||
} |
|||
// else{
|
|||
// this.setState({
|
|||
// fileList:[],
|
|||
// })
|
|||
// }
|
|||
} |
|||
|
|||
render() { |
|||
const UploadPath = { |
|||
project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf', 'pptx', 'png', 'jpg', 'svg', 'jpeg', 'rar', 'zip', 'jpeg', 'mp4'], |
|||
report: ['doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf'], |
|||
data: ['txt', 'xls', 'xlsx', 'csv'], |
|||
image: ['png', 'jpg', 'svg', 'jpeg'], |
|||
three: ['js'], |
|||
video: ['mp4'] |
|||
}; |
|||
/** |
|||
* uploadType 【string】 主要区别文件上传路径 以及类型 以 web/routes/attachment/index.js 中 UploadPath 的 key 值为准;默认 project; |
|||
* disabled 【boolean】 上传是否可用 |
|||
* maxFilesNum 【number】 最大上传数量 |
|||
* fileTypes 【array[string]】 可允许上传的文件类型; |
|||
* maxFileSize 【number】 单个文件最大大小 M |
|||
* listType 【antd】 upload 组件的属性 |
|||
* onChange 【function】 文件数量变化时候回调 返回文件 |
|||
* value 【array[obj]】 编辑数据 [{url:'xxx', [size:999]}] |
|||
* onStateChange 【function】 文件状态改变回调函数 上传中 return { uploading:true/false } |
|||
*/ |
|||
const { |
|||
uploadType, |
|||
disabled, |
|||
maxFilesNum, |
|||
fileTypes, |
|||
maxFileSize, |
|||
listType, |
|||
onChange = () => { }, |
|||
value, |
|||
showUploadList, |
|||
onStateChange, |
|||
isQiniu, |
|||
isAli, |
|||
} = this.props; |
|||
const { fileList, curPreviewPic, curPreviewVideo, delPicIng, removeFilesList } = this.state; |
|||
const that = this; |
|||
let uploadType_ = uploadType || 'project'; |
|||
let maxFilesNum_ = maxFilesNum || 1; |
|||
let defaultFileTypes = fileTypes || UploadPath[uploadType_]; |
|||
// debugger
|
|||
const uploadProps = { |
|||
name: 'checkFile_', |
|||
multiple: false, |
|||
showUploadList: showUploadList || true, |
|||
action: |
|||
isQiniu ? `/_upload/attachments/${uploadType_}` |
|||
: isAli ? `/_upload/attachments/ali/${uploadType_}` |
|||
: `${this.ApiRoot}/attachments/${uploadType_}`, |
|||
listType: listType || 'text', |
|||
disabled: disabled, |
|||
beforeUpload: (file) => { |
|||
if (fileList.length >= maxFilesNum_) { |
|||
message.warning(`最多选择${maxFilesNum_}个文件上传`); |
|||
return false; |
|||
} |
|||
if (file.name.length > 60) { |
|||
message.warning(`文件名过长(大于60字符),请修改后上传`); |
|||
return false; |
|||
} |
|||
const extNames = file.name.split('.'); |
|||
// var reg = /^[\.\s\u4e00-\u9fa5a-zA-Z0-9_-]{0,}$/;
|
|||
// if (!reg.exec(file.name)) {
|
|||
// message.warning(`文件名包含除字母、汉字、数字、中划线、下划线之外的字符,请修改后上传`);
|
|||
// return false;
|
|||
// }
|
|||
let isDAE = false; |
|||
if (extNames.length > 0) { |
|||
let fileType = extNames[extNames.length - 1].toLowerCase(); |
|||
isDAE = defaultFileTypes.some((f) => f == fileType); |
|||
} |
|||
if (!isDAE) { |
|||
message.error(`只能上传 ${defaultFileTypes.join()} 格式的文件!`); |
|||
return false; |
|||
} |
|||
const isLt = file.size / 1024 / 1024 < (maxFileSize || 3); |
|||
if (!isLt) { |
|||
message.error(`文件必须小于${maxFileSize || 3}MB!`); |
|||
return false; |
|||
} |
|||
this.setState({ |
|||
fileUploading: true |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: true }); |
|||
} |
|||
}, |
|||
onChange(info) { |
|||
const status = info.file.status; |
|||
if (status === 'uploading') { |
|||
that.setState({ |
|||
fileList: info.fileList |
|||
}); |
|||
} |
|||
if (status === 'done') { |
|||
let { uploaded, url } = info.file.response; |
|||
let size = info.file.size; |
|||
let nextFileList = fileList; |
|||
nextFileList[nextFileList.length - 1] = { |
|||
uid: -moment().unix(), |
|||
name: that.dealName(uploaded), |
|||
status: 'done', |
|||
storageUrl: uploaded, |
|||
url: |
|||
isQiniu ? '/_file-server/' + uploaded : |
|||
isAli ? `/_file-ali-server/${uploaded}` : |
|||
url, |
|||
size: size |
|||
}; |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileUploading: false, |
|||
fileList: nextFileList |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} else if (status === 'error') { |
|||
that.setState({ |
|||
fileUploading: false |
|||
}); |
|||
message.error(`${info.file.name} 上传失败,请重试`); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} |
|||
}, |
|||
onRemove(file) { |
|||
let nextFileList = []; |
|||
fileList.map((f, i) => { |
|||
if (f.uid != file.uid) { |
|||
nextFileList.push(f); |
|||
} |
|||
}); |
|||
let nextRemoveFiles = removeFilesList.concat([file.storageUrl]); |
|||
if (curPreviewPic == file.url) { |
|||
that.setState({ |
|||
curPreviewPic: '' |
|||
}); |
|||
} |
|||
if (curPreviewVideo == file.url) { |
|||
that.setState({ |
|||
curPreviewVideo: '' |
|||
}); |
|||
} |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileList: nextFileList, |
|||
removeFilesList: nextRemoveFiles |
|||
}); |
|||
}, |
|||
onPreview(file) { |
|||
let filePostfix = file.url.split('.').pop(); |
|||
filePostfix = filePostfix.toLowerCase(); |
|||
if (UploadPath.image.some((img) => img == filePostfix)) { |
|||
that.setState({ |
|||
curPreviewPic: file.url |
|||
}); |
|||
} else if (UploadPath.video.some((img) => img == filePostfix)) { |
|||
that.setState({ |
|||
curPreviewVideo: file.url |
|||
}); |
|||
} else { |
|||
//message.warn('仅支持图片预览');
|
|||
preview(file.storageUrl) |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const preview = (url) => { |
|||
let link = isQiniu ? encodeURI(`${this.qnDomain}/${url}`) : |
|||
isAli ? encodeURI(`${this.aliAdmin}/${url}`) : '' |
|||
if (link) |
|||
if (url.indexOf("pdf") !== -1 || url.indexOf("csv") !== -1) { |
|||
window.open(link) |
|||
} else { |
|||
window.open(`https://view.officeapps.live.com/op/view.aspx?src=${link}`) |
|||
} |
|||
} |
|||
|
|||
let fileList_ = fileList |
|||
// .map(f => {
|
|||
// if (f.storageUrl) {
|
|||
// let realName = f.storageUrl.split('/').pop()
|
|||
// if (f.name != realName) {
|
|||
// f.name = realName
|
|||
// }
|
|||
// }
|
|||
// return f
|
|||
// })
|
|||
//下载文件
|
|||
const handleDownload = (file) => { |
|||
saveAs(file) |
|||
}; |
|||
const saveAs = (file) => { |
|||
const link = document.createElement('a'); |
|||
link.href = file.url; |
|||
link.download = file.name; |
|||
link.style.display = 'none'; |
|||
link.click(); |
|||
} |
|||
//自定义下载
|
|||
return ( |
|||
<div> |
|||
<Spin spinning={delPicIng}> |
|||
<Upload {...uploadProps} fileList={fileList_} showUploadList={{ showDownloadIcon: true }} onDownload={handleDownload}> |
|||
{ |
|||
disabled ? ( |
|||
'' |
|||
) : |
|||
listType == 'picture-card' ? |
|||
( |
|||
fileList.length >= maxFilesNum_ ? null : ( |
|||
<div style={{}}> |
|||
<PlusOutlined /> |
|||
<div>添加附件</div> |
|||
</div> |
|||
) |
|||
) : ( |
|||
<Button disabled={fileList.length >= maxFilesNum_} icon={<UploadOutlined />}> 文件上传 </Button> |
|||
) |
|||
} |
|||
</Upload> |
|||
{ |
|||
curPreviewPic ? ( |
|||
<Card bodyStyle={{ padding: 8 }}> |
|||
<div style={{ marginBottom: 8 }} > |
|||
<span>图片预览</span> |
|||
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewPic: '' }) }}> |
|||
<CloseOutlined style={{ fontSize: 20 }} /> |
|||
</span> |
|||
</div> |
|||
<img style={{ width: '100%' }} src={curPreviewPic} /> |
|||
</Card> |
|||
) : '' |
|||
} |
|||
{ |
|||
curPreviewVideo ? (<Card bodyStyle={{ padding: 8 }}> |
|||
<div style={{ marginBottom: 8 }} > |
|||
<span>视频预览</span> |
|||
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewVideo: '' }) }}> |
|||
<CloseOutlined style={{ fontSize: 20 }} /> |
|||
</span> |
|||
</div> |
|||
<video controls style={{ width: '100%' }}> |
|||
<source src={curPreviewVideo} type="video/mp4"></source> |
|||
</video> |
|||
</Card>) : '' |
|||
} |
|||
</Spin> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth } = state |
|||
return { |
|||
user: auth.user |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(Uploads); |
@ -1,683 +0,0 @@ |
|||
/** |
|||
* Created by Xumeng 2020/04/22. |
|||
*/ |
|||
|
|||
import React, { useState, useEffect } from 'react'; |
|||
import PropTypes from 'prop-types'; |
|||
import { |
|||
Row, Col, Space, Button, message, notification, Form, Input, Tooltip, Menu, Dropdown, |
|||
} from 'antd'; |
|||
import moment from 'moment'; |
|||
import XLSX from 'xlsx'; |
|||
import { fromJS } from 'immutable'; |
|||
import { Request } from '@peace/utils'; |
|||
import FileSaver from 'file-saver'; |
|||
import './index.less'; |
|||
|
|||
// 通用前端导入导出组件 使用方法查看底部propTypes
|
|||
function ExportAndImport(props) { |
|||
const [form] = Form.useForm(); |
|||
const [exportLoading, setExportLoading] = useState(false); |
|||
const [importLoading, setImportLoading] = useState(false); |
|||
const { |
|||
importDataCallback, onImportSucess, handelData, importMethod = 'post', |
|||
} = props; |
|||
|
|||
useEffect(() => () => { |
|||
// 只有unmount 时调用
|
|||
notification.close('import-notification'); |
|||
}); |
|||
const importExcel = (file, type) => { |
|||
setImportLoading(true); |
|||
// 获取上传的文件对象
|
|||
const { files } = file.target; |
|||
// 判断xls、xlsx格式
|
|||
if (files[0].type.indexOf('sheet') > -1 || files[0].type.indexOf('ms-excel') > -1) { |
|||
// 通过FileReader对象读取文件
|
|||
const fileReader = new FileReader(); |
|||
fileReader.onload = (event) => { |
|||
try { |
|||
const { importRequest = true, importUrl, importQuery } = props; |
|||
if (importRequest && !importUrl) { |
|||
message.error('获取导入接口失败!'); |
|||
form.resetFields(); |
|||
return; |
|||
} |
|||
const { result } = event.target; |
|||
// 以二进制流方式读取得到整份excel表格对象
|
|||
const workbook = XLSX.read(result, { type: 'binary', cellDates: true }); |
|||
let data = []; // 存储获取到的数据
|
|||
// 遍历每张工作表进行读取(这里默认只读取第一张表)
|
|||
for (const sheet in workbook.Sheets) { |
|||
if (workbook.Sheets.hasOwnProperty(sheet)) { |
|||
// 利用 sheet_to_json 方法将 excel 转成 json 数据
|
|||
data = data.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet])); |
|||
break; // 如果只取第一张表,就取消注释这行
|
|||
} |
|||
} |
|||
if (data.length > 10000) { |
|||
message.error('一次最多导入10000条数据,请分批导入!'); |
|||
form.resetFields(); |
|||
setImportLoading(false); |
|||
return; |
|||
} |
|||
if (importRequest) { |
|||
message.success('获取文件数据成功,开始处理导入...'); |
|||
const importData = handelData ? handelData(data) : data; |
|||
Request[importMethod](importUrl, { data: importData }, importQuery || {}).then((res) => { |
|||
message.success('导入数据成功'); |
|||
form.resetFields(); |
|||
notification.close('import-notification'); |
|||
setImportLoading(false); |
|||
onImportSucess && onImportSucess(); |
|||
}, (err) => { |
|||
if (err.status === 500) { |
|||
message.error('数据导入出错,导入失败'); |
|||
} else if (err.status === 400) { |
|||
message.error(err.body.message || '数据验证出错,请检查数据格式是否正确'); |
|||
} else { |
|||
message.error('导入失败'); |
|||
} |
|||
form.resetFields(); |
|||
setImportLoading(false); |
|||
}); |
|||
} else { |
|||
form.resetFields(); |
|||
setImportLoading(false); |
|||
importDataCallback && importDataCallback(data, type); |
|||
notification.close('import-notification'); |
|||
} |
|||
} catch (e) { |
|||
console.log(e); |
|||
// 这里可以抛出文件类型错误不正确的相关提示
|
|||
message.error('文件格式不正确!'); |
|||
setImportLoading(false); |
|||
form.resetFields(); |
|||
} |
|||
}; |
|||
// fileReader.onloadend = (event) => {
|
|||
// console.log(event)
|
|||
// }
|
|||
// 以二进制方式打开文件
|
|||
fileReader.readAsBinaryString(files[0]); |
|||
} else { |
|||
message.error('文件格式不正确!'); |
|||
form.resetFields(); |
|||
setImportLoading(false); |
|||
} |
|||
}; |
|||
|
|||
const loop = (data, keypath, values) => { // deal with array
|
|||
const dkey = keypath.slice(0, 1)[0]; |
|||
console.log(dkey); |
|||
if (dkey) { |
|||
const dvalue = data[dkey]; |
|||
const otherKeypath = keypath.slice(1); |
|||
if (Array.isArray(dvalue)) { |
|||
if (otherKeypath.length) { |
|||
const immutableData = fromJS(data); |
|||
for (let index = 0; index < dvalue.length; index++) { |
|||
const tmp = immutableData.getIn([dkey, index]).toJS(); |
|||
loop(tmp, otherKeypath, values); |
|||
} |
|||
} |
|||
} else { |
|||
values.push(dvalue); |
|||
} |
|||
} |
|||
return values; |
|||
}; |
|||
const getColumnData = (opts) => { |
|||
const { |
|||
data, keypath, render, spliter, rawdata, |
|||
} = opts; |
|||
let v = null; |
|||
const outer = data[keypath[0]]; |
|||
|
|||
if (Array.isArray(outer)) { |
|||
const values = loop(data, keypath, []); |
|||
v = rawdata ? values : values.join(spliter || ','); |
|||
} else { |
|||
v = fromJS(data).getIn(keypath); |
|||
} |
|||
// 处理render
|
|||
if (render && typeof render === 'function') { |
|||
v = render(outer, data); |
|||
} |
|||
return v; |
|||
}; |
|||
const getDataSource = (attrs, filterData) => { |
|||
// let token = JSON.parse(sessionStorage.getItem('user')).token;
|
|||
const dataSource = filterData.map((item) => { |
|||
const record = {}; |
|||
attrs.forEach((attr) => { |
|||
const { |
|||
key, dataIndex, render, child, |
|||
} = attr; |
|||
if (child) { |
|||
record[key] = getDataSource(child, item[key]); |
|||
} else { |
|||
const v = getColumnData({ |
|||
data: item, |
|||
keypath: dataIndex || [key], |
|||
render: render || null, |
|||
}); |
|||
record[key] = v; |
|||
} |
|||
}); |
|||
|
|||
return record; |
|||
}); |
|||
return dataSource; |
|||
}; |
|||
// 暂时只处理两层
|
|||
const getFlatData = (attrs, filterData, dataToAoa, deep = 0) => { |
|||
filterData.map((item) => { |
|||
let cur = dataToAoa[deep]; |
|||
if (!cur) { |
|||
cur = dataToAoa[deep] = []; |
|||
} |
|||
attrs.map((attr, index) => { |
|||
const { key, child } = attr; |
|||
if (child) { |
|||
if (Array.isArray(item[key])) { |
|||
// getFlatData(child,item[key],dataToAoa,deep)
|
|||
|
|||
item[key].map((s, i) => { |
|||
if (i == 0) { |
|||
child.map((c) => { |
|||
cur.push(s[c.key]); |
|||
}); |
|||
} else { |
|||
deep++; |
|||
const childCur = dataToAoa[deep] = []; |
|||
pushNull(childCur, index); |
|||
child.map((c) => { |
|||
childCur.push(s[c.key]); |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
} else { |
|||
cur.push(item[key]); |
|||
} |
|||
}); |
|||
deep++; |
|||
}); |
|||
}; |
|||
|
|||
const getHeader = (headers, excelHeader, deep, perOffset) => { |
|||
let offset = 0; |
|||
let cur = excelHeader[deep]; |
|||
if (!cur) { |
|||
cur = excelHeader[deep] = []; |
|||
} |
|||
pushNull(cur, perOffset - cur.length); |
|||
for (let i = 0; i < headers.length; i++) { |
|||
const head = headers[i]; |
|||
cur.push(head.name); |
|||
if (head.hasOwnProperty('child') && Array.isArray(head.child) && head.child.length > 0) { |
|||
const childOffset = getHeader(head.child, excelHeader, deep + 1, cur.length - 1); |
|||
pushNull(cur, childOffset - 1); |
|||
offset += childOffset; |
|||
} else { |
|||
offset++; |
|||
} |
|||
} |
|||
return offset; |
|||
}; |
|||
|
|||
const pushNull = (arr, count) => { |
|||
for (let i = 0; i < count; i++) { |
|||
arr.push(null); |
|||
} |
|||
}; |
|||
const fillNull = (arr) => { |
|||
const max = Math.max(...(arr.map((a) => a.length))); |
|||
arr.filter((e) => e.length < max).forEach((e) => pushNull(e, max - e.length)); |
|||
}; |
|||
const doMerges = (arr) => { |
|||
// 要么横向合并 要么纵向合并
|
|||
const deep = arr.length; |
|||
const merges = []; |
|||
for (let y = 0; y < deep; y++) { |
|||
// 先处理横向合并
|
|||
const row = arr[y]; |
|||
let colSpan = 0; |
|||
for (let x = 0; x < row.length; x++) { |
|||
if (row[x] === null) { |
|||
colSpan++; |
|||
if (((x + 1) === row.length) && (colSpan > 0 && x > colSpan)) { |
|||
merges.push({ s: { r: y, c: x - colSpan }, e: { r: y, c: x } }); |
|||
} |
|||
} else if (colSpan > 0 && x > colSpan) { |
|||
merges.push({ s: { r: y, c: x - colSpan - 1 }, e: { r: y, c: x - 1 } }); |
|||
colSpan = 0; |
|||
} else { |
|||
colSpan = 0; |
|||
} |
|||
} |
|||
} |
|||
// 再处理纵向合并
|
|||
const colLength = arr[0].length; |
|||
for (let x = 0; x < colLength; x++) { |
|||
let rowSpan = 0; |
|||
for (let y = 0; y < deep; y++) { |
|||
if (arr[y][x] != null) { |
|||
rowSpan = 0; |
|||
} else { |
|||
rowSpan++; |
|||
} |
|||
} |
|||
if (rowSpan > 0) { |
|||
merges.push({ s: { r: deep - rowSpan - 1, c: x }, e: { r: deep - 1, c: x } }); |
|||
} |
|||
} |
|||
return merges; |
|||
}; |
|||
// 内容暂只出了纵向合并
|
|||
const doContetMerges = (arr, headerLength) => { |
|||
const deep = arr.length; |
|||
const merges = []; |
|||
// 处理纵向合并
|
|||
const colLength = arr[0].length; |
|||
for (let x = 0; x < colLength; x++) { |
|||
let rowSpan = 0; |
|||
const mergY = 0; |
|||
for (let y = 0; y < deep; y++) { |
|||
if (rowSpan > 0) { |
|||
// 如果还有null 继续加
|
|||
if (arr[y][x] === null) { |
|||
rowSpan += 1; |
|||
} else { |
|||
// 不为null 增加merge
|
|||
merges.push({ s: { r: headerLength + (y - rowSpan - 1), c: x }, e: { r: headerLength + y - 1, c: x } }); |
|||
rowSpan = 0; |
|||
} |
|||
} else if (arr[y][x] === null) { |
|||
rowSpan += 1; |
|||
} |
|||
} |
|||
if (rowSpan > 0) { |
|||
merges.push({ s: { r: headerLength + (deep - rowSpan - 1), c: x }, e: { r: headerLength + deep - 1, c: x } }); |
|||
rowSpan = 0; |
|||
} |
|||
} |
|||
return merges; |
|||
}; |
|||
const exportMergeExcel = async () => { |
|||
setExportLoading(true); |
|||
const { |
|||
column, data, fileName, exportUrl, exportQuery, exportBody, requestType, header, showYearMouth, |
|||
} = props || {}; |
|||
|
|||
let resultData = []; |
|||
if (exportUrl) { |
|||
resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => { |
|||
// 数据接口返回的结果 如果是对象 必须把返回数组放入rows
|
|||
if (typeof data === 'object' && data.rows) { |
|||
return data.rows; |
|||
} |
|||
return data; |
|||
}, (err) => { |
|||
message.error('获取数据失败,导出失败!'); |
|||
}) : await Request.get(exportUrl, exportQuery || {}).then((data) => { |
|||
if (typeof data === 'object' && data.rows) { |
|||
return data.rows; |
|||
} |
|||
return data; |
|||
}, (err) => { |
|||
message.error('获取数据失败,导出失败!'); |
|||
}); |
|||
if (!resultData) { |
|||
return; |
|||
} |
|||
} else { |
|||
resultData = data; |
|||
} |
|||
const excelHeader = []; |
|||
getHeader(column, excelHeader, 0, 0); |
|||
fillNull(excelHeader); |
|||
|
|||
// console.log(excelHeader);
|
|||
|
|||
const loopData = getDataSource(column, resultData); |
|||
// console.log(loopData)
|
|||
|
|||
const dataToAoa = []; |
|||
getFlatData(column, loopData, dataToAoa, 0); |
|||
fillNull(dataToAoa); |
|||
// console.log(dataToAoa);
|
|||
|
|||
const aoa = [].concat(excelHeader, dataToAoa); |
|||
// console.log(aoa)
|
|||
|
|||
const headerMerges = doMerges(excelHeader); |
|||
const contentMerages = doContetMerges(dataToAoa, excelHeader.length); |
|||
const merges = [].concat(headerMerges, contentMerages); |
|||
// console.log(contentMerages)
|
|||
|
|||
// let opts = {
|
|||
// defaultCellStyle: {
|
|||
// font: { name: "宋体", sz: 11, color: { auto: 1 } },
|
|||
// border: {
|
|||
// color: { auto: 1 }
|
|||
// },
|
|||
// alignment: {
|
|||
// /// 自动换行
|
|||
// wrapText: 1,
|
|||
// // 居中
|
|||
// horizontal: "center",
|
|||
// vertical: "center",
|
|||
// indent: 0
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
const sheet = XLSX.utils.aoa_to_sheet(aoa); |
|||
// let newSheet = {};
|
|||
// for (let [key, value] of Object.entries(sheet)) {
|
|||
// if(key == '!ref'){
|
|||
// newSheet[key] = value
|
|||
// }else if(typeof value === 'object'){
|
|||
// newSheet[key] = {
|
|||
// ...value,
|
|||
// s: opts.defaultCellStyle
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
const wpx = column.map((c) => ({ |
|||
wpx: Number.parseInt(c.wpx) ? Number.parseInt(c.wpx) : 100, |
|||
})); |
|||
sheet['!cols'] = wpx; |
|||
sheet['!merges'] = merges; |
|||
|
|||
// 构建 workbook 对象
|
|||
const workbook = XLSX.utils.book_new(); |
|||
|
|||
const time = moment().format('YYYY-MM-DD'); |
|||
|
|||
XLSX.utils.book_append_sheet(workbook, sheet, 'mySheet'); |
|||
// 导出 Excel
|
|||
XLSX.writeFile(workbook, fileName ? `${fileName}-${time}.xlsx` : '导出数据.xlsx'); |
|||
setExportLoading(false); |
|||
// message.success(`成功导出了 ${loopData.length || 0} 条数据`);
|
|||
}; |
|||
|
|||
const exportProExcel = async () => { |
|||
setExportLoading(true); |
|||
const { |
|||
column, data, fileName, exportUrl, exportQuery, exportBody, requestType, showYearMouth, |
|||
} = props || {}; |
|||
let resultData = []; |
|||
if (exportUrl) { |
|||
resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => { |
|||
// 数据接口返回的结果 如果是对象 必须把返回数组放入rows
|
|||
if (typeof data === 'object') { |
|||
return data.data ? data.data : data.rows; |
|||
} |
|||
return data; |
|||
}, (err) => { |
|||
message.error('获取数据失败,导出失败!'); |
|||
}) : await Request.get(exportUrl, exportQuery || {}).then((data) => { |
|||
if (showYearMouth) { |
|||
|
|||
} |
|||
if (typeof data === 'object' && data.rows) { |
|||
return data.rows; |
|||
} |
|||
return data; |
|||
}, (err) => { |
|||
message.error('获取数据失败,导出失败!'); |
|||
}); |
|||
if (!resultData) { |
|||
return; |
|||
} |
|||
} else { |
|||
resultData = data; |
|||
} |
|||
|
|||
const loopData = getDataSource(column, resultData); |
|||
|
|||
let content = ''; |
|||
let header = '<tr>'; |
|||
// header += `<th><div>序号</div></th>`;
|
|||
column.map((colum) => { |
|||
header += `<th><div>${colum.name}</div></th>`; |
|||
}); |
|||
header += '</tr>'; |
|||
loopData.map((data) => { |
|||
content += '<tr>'; |
|||
column.map((c) => { |
|||
if (c.style) { |
|||
content += `<th style="${c.style}"><div>${data[c.dataIndex || c.key]}</div></th>`; |
|||
} else { |
|||
content += `<th><div>${data[c.dataIndex || c.key]}</div></th>`; |
|||
} |
|||
}); |
|||
content += '</tr>'; |
|||
}); |
|||
|
|||
const exportTable = `\uFEFF
|
|||
<table style='text-alagin:center' border="1"> |
|||
${header} |
|||
${content} |
|||
</table> |
|||
`;
|
|||
const time = moment().format('YYYY-MM-DD'); |
|||
const tempStrs = new Blob([exportTable], { type: 'text/xls' }); |
|||
FileSaver.saveAs(tempStrs, fileName ? `${fileName}-${time}.xls` : '导出数据.xls'); |
|||
setExportLoading(false); |
|||
// message.success(`成功导出了 ${loopData.length || 0} 条数据`);
|
|||
}; |
|||
|
|||
const exportExcel = async () => { |
|||
setExportLoading(true); |
|||
const { |
|||
column, data, fileName, exportUrl, exportQuery, exportBody, requestType, |
|||
} = props || {}; |
|||
const _headers = column |
|||
.map((item, i) => ({ key: item.key, title: item.name, position: String.fromCharCode(65 + i) + 1 })) |
|||
.reduce((prev, next) => ({ ...prev, [next.position]: { key: next.key, v: next.title } }), {}); |
|||
let resultData = []; |
|||
if (exportUrl) { |
|||
resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => { |
|||
// 数据接口返回的结果 如果是对象 必须把返回数组放入rows
|
|||
|
|||
if (typeof data === 'object' && (data.rows || data.data)) { |
|||
return data.data ? data.data : data.rows; |
|||
} |
|||
return data; |
|||
}, (err) => { |
|||
message.error('获取数据失败,导出失败!'); |
|||
}) : await Request.get(exportUrl, exportQuery || {}).then((data) => { |
|||
if (typeof data === 'object' && data.rows) { |
|||
return data.rows; |
|||
} |
|||
return data; |
|||
}, (err) => { |
|||
message.error('获取数据失败,导出失败!'); |
|||
}); |
|||
if (!resultData) { |
|||
return; |
|||
} |
|||
} else { |
|||
resultData = data; |
|||
} |
|||
|
|||
const loopDate = getDataSource(column, resultData); |
|||
|
|||
const wpx = column.map((c) => ({ |
|||
wpx: Number.parseInt(c.wpx) ? Number.parseInt(c.wpx) : 100, |
|||
})); |
|||
if (!(loopDate.length > 0)) { |
|||
setExportLoading(false); |
|||
return; |
|||
} |
|||
const _data = loopDate |
|||
.map((item, i) => column.map((key, j) => ({ content: item[key.key], position: String.fromCharCode(65 + j) + (i + 2) }))) |
|||
// 对刚才的结果进行降维处理(二维数组变成一维数组)
|
|||
.reduce((prev, next) => prev.concat(next)) |
|||
// 转换成 worksheet 需要的结构
|
|||
.reduce((prev, next) => ({ ...prev, [next.position]: { v: next.content } }), {}); |
|||
|
|||
// 合并 column 和 data
|
|||
const output = { ..._headers, ..._data }; |
|||
// 获取所有单元格的位置
|
|||
const outputPos = Object.keys(output); |
|||
// 计算出范围 ,["A1",..., "H2"]
|
|||
const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`; |
|||
|
|||
// 构建 workbook 对象
|
|||
const workbook = { |
|||
SheetNames: ['mySheet'], |
|||
Sheets: { |
|||
mySheet: { |
|||
|
|||
...output, |
|||
'!ref': ref, |
|||
'!cols': wpx, |
|||
}, |
|||
}, |
|||
}; |
|||
const time = moment().format('YYYY-MM-DD'); |
|||
// 导出 Excel
|
|||
XLSX.writeFile(workbook, fileName ? `${fileName}-${time}.xlsx` : '导出数据.xlsx'); |
|||
setExportLoading(false); |
|||
// message.success(`成功导出了 ${loopDate.length || 0} 条数据`);
|
|||
}; |
|||
|
|||
const exportTemplete = async () => { |
|||
const { importTemColumn, importTemData, fileName } = props || {}; |
|||
const _headers = importTemColumn |
|||
.map((item, i) => { |
|||
let group = 0; // 用于处理Z1的时候,重计算AA1
|
|||
if (parseInt(i / 26) > group) { |
|||
group = parseInt(i / 26); |
|||
} |
|||
if (group > 0) { // AA1 BA1 CA1
|
|||
const position = String.fromCharCode(65 + (group - 1)); |
|||
return { key: item.key, title: item.name, position: position + String.fromCharCode(65 + (i % 26)) + 1 }; |
|||
} return { key: item.key, title: item.name, position: String.fromCharCode(65 + i) + 1 }; |
|||
}) |
|||
.reduce((prev, next) => ({ ...prev, [next.position]: { key: next.key, v: next.title } }), {}); |
|||
|
|||
const loopDate = getDataSource(importTemColumn, importTemData); |
|||
|
|||
const wpx = importTemColumn.map((c) => ({ |
|||
wpx: Number.parseInt(c.wpx) ? Number.parseInt(c.wpx) : 100, |
|||
})); |
|||
const _data = loopDate.length ? loopDate |
|||
.map((item, i) => importTemColumn.map((key, j) => ({ content: item[key.key], position: String.fromCharCode(65 + j) + (i + 2) }))) |
|||
// 对刚才的结果进行降维处理(二维数组变成一维数组)
|
|||
.reduce((prev, next) => prev.concat(next)) |
|||
// 转换成 worksheet 需要的结构
|
|||
.reduce((prev, next) => ({ ...prev, [next.position]: { v: next.content } }), {}) : []; |
|||
// 合并 column 和 data
|
|||
const output = { ..._headers, ..._data }; |
|||
// 获取所有单元格的位置
|
|||
const outputPos = Object.keys(output); |
|||
// 计算出范围 ,["A1",..., "H2"]
|
|||
const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`; |
|||
|
|||
// 构建 workbook 对象
|
|||
const workbook = { |
|||
SheetNames: ['mySheet'], |
|||
Sheets: { |
|||
mySheet: { |
|||
|
|||
...output, |
|||
'!ref': ref, |
|||
'!cols': wpx, |
|||
}, |
|||
}, |
|||
}; |
|||
// 导出 Excel
|
|||
XLSX.writeFile(workbook, fileName ? `${fileName}-导入模板.xlsx` : '导入模板.xlsx'); |
|||
}; |
|||
const tips = (type) => { |
|||
const { tips, templeteBth = true } = props; |
|||
const description = ( |
|||
<div className="export-import"> |
|||
{tips && tips} |
|||
<Row gutter={16}> |
|||
<Col span={12}> |
|||
<Form form={form} initialValues={{}}> |
|||
<Form.Item name="import-file"> |
|||
<Input className="file-uploader" type="file" accept=".xlsx, .xls" onChange={(e) => importExcel(e, type)} /> |
|||
<Button style={props.btnStyle} className={props.btnClass} loading={importLoading}> |
|||
选择文件 |
|||
</Button> |
|||
</Form.Item> |
|||
</Form> |
|||
</Col> |
|||
{templeteBth && ( |
|||
<Col span={12}> |
|||
<Button style={props.btnStyle} className={props.btnClass} onClick={exportTemplete}> |
|||
模板下载 |
|||
</Button> |
|||
</Col> |
|||
)} |
|||
</Row> |
|||
</div> |
|||
); |
|||
|
|||
notification.info({ |
|||
message: '支持 .xlsx、.xls 格式的文件', |
|||
description, |
|||
key: 'import-notification', |
|||
duration: null, |
|||
}); |
|||
}; |
|||
|
|||
return ( |
|||
<Space> |
|||
{ |
|||
props.import && ( |
|||
<Button style={props.btnStyle} className={props.btnClass} loading={importLoading} onClick={tips}> |
|||
{props.importBtnName || '导入'} |
|||
</Button> |
|||
) |
|||
} |
|||
{ |
|||
props.export && ( |
|||
<Tooltip placement="top" title={props.exportBtnTips || '默认导出所有数据'}> |
|||
<Button style={props.btnStyle} className={props.btnClass} loading={exportLoading} onClick={props.exportType === 'pro' ? exportProExcel : exportExcel}> |
|||
{props.exportBtnName || '导出'} |
|||
</Button> |
|||
</Tooltip> |
|||
) |
|||
} |
|||
</Space> |
|||
); |
|||
} |
|||
|
|||
ExportAndImport.propTypes = { |
|||
export: PropTypes.bool, // 是否显示导出按钮
|
|||
exportBtnName: PropTypes.string, // 导出按钮文字
|
|||
importBtnName: PropTypes.string, // 导入按钮文字
|
|||
import: PropTypes.bool, // 是否显示导入按钮
|
|||
variedImport: PropTypes.bool, // 是否显示多样导入
|
|||
variedImportDisable: PropTypes.bool, // 多样导入禁用
|
|||
variedImportBtnName: PropTypes.string, // 多样导入文字
|
|||
column: PropTypes.array, // 导出显示的header数组 兼容antd column 可直接拿table的column使用 注:column每列的属性wpx设置导出的execl每列的宽度值 默认 100
|
|||
data: PropTypes.array, // 导出的数据 兼容antd table 数组嵌套处理
|
|||
exportUrl: PropTypes.string, // 导出数据从接口获取的url地址 返回的数据1、数组必须支持column的设置 ,2、如果是对象,数组需放在rows属性上
|
|||
exportBody: PropTypes.object, // 导出数据接口body参数
|
|||
exportQuery: PropTypes.object, // 导出数据从接口获取的url地址上的参数
|
|||
exportBtnTips: PropTypes.string, // 导出按钮tips文字提示
|
|||
importUrl: PropTypes.string, // 导入接口url
|
|||
importQuery: PropTypes.object, // 导入接口url地址上的参数
|
|||
btnClass: PropTypes.string, // 按钮className
|
|||
btnStyle: PropTypes.object, // 按钮style
|
|||
tips: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), // 上传文件提示的信息
|
|||
onImportSucess: PropTypes.func, // 上传成功后 返回处理函数
|
|||
importTemColumn: PropTypes.array, // 导入模板设置 头部字段数组
|
|||
importTemData: PropTypes.array, // 导入模板默认数据
|
|||
requestType: PropTypes.string, // 请求类型
|
|||
importDataCallback: PropTypes.func, // 上传后数据返回
|
|||
templeteBth: PropTypes.bool, // 模板按钮
|
|||
importRequest: PropTypes.bool, // 请求导入接口,false时搭配importDataCallback,
|
|||
exportType: PropTypes.string, // 导出执行的函数名
|
|||
}; |
|||
|
|||
export default ExportAndImport; |
@ -1,13 +0,0 @@ |
|||
.export-import { |
|||
.file-uploader { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
top: 0; |
|||
left: 0; |
|||
outline: none; |
|||
opacity: 0; |
|||
background-color: transparent; |
|||
z-index: 10; |
|||
} |
|||
} |
@ -1,74 +0,0 @@ |
|||
'use strict'; |
|||
import React, { Component } from 'react'; |
|||
import { Table } from 'antd'; |
|||
import './index.less'; |
|||
import moment from 'moment'; |
|||
|
|||
class FlowRecordTable extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
isRequesting: false, |
|||
pagination: { |
|||
showTotal: total => `共${total}条`, |
|||
responsive: true, |
|||
defaultPageSize: 10, |
|||
}, |
|||
data: [] |
|||
} |
|||
this.token = JSON.parse(sessionStorage.getItem('user')).token; |
|||
} |
|||
|
|||
|
|||
getPagination = () => { |
|||
const { pagination, data } = this.state; |
|||
const { actiPage } = this.props; |
|||
pagination.total = data.length > 0 ? data.length : 0; |
|||
pagination.current = actiPage; |
|||
return pagination; |
|||
}; |
|||
|
|||
onTableChange = (pagination, filters, sorter) => { |
|||
this.props.onPageChange(pagination.current) |
|||
} |
|||
componentDidMount() { |
|||
} |
|||
render() { |
|||
const { flowRecord } = this.props; |
|||
const tableColumnAttrs = [ |
|||
{ |
|||
title: '序号', |
|||
dataIndex: 'id', |
|||
width: '12%', |
|||
render: (text, record, index) => { return index + 1 } |
|||
}, |
|||
{ |
|||
title: '操作人', |
|||
dataIndex: 'processBy', |
|||
width: '25%', |
|||
}, |
|||
{ |
|||
title: '操作时间', |
|||
dataIndex: 'processAt', |
|||
width: '25%', |
|||
render: (text, record, index) => { return moment(text).format('YYYY-MM-DD HH:mm') } |
|||
}, { |
|||
title: '操作内容', |
|||
dataIndex: 'processContent', |
|||
width: '38%', |
|||
}, |
|||
]; |
|||
return ( |
|||
<Table |
|||
rowKey="id" |
|||
className={'fs-table'} |
|||
dataSource={flowRecord} |
|||
columns={tableColumnAttrs} |
|||
onChange={this.onTableChange} |
|||
pagination={this.getPagination()} |
|||
/> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default FlowRecordTable; |
@ -1,27 +1,10 @@ |
|||
|
|||
'use strict'; |
|||
|
|||
import Upload from './Upload'; |
|||
import Uploads from './Uploads'; |
|||
import NoResource from './no-resource'; |
|||
import LimitTextArea from './limit-textarea'; |
|||
// import ProcessForm from './process_form'
|
|||
import FlowRecordTable from './flowRecordTable' |
|||
import Table from './table' |
|||
import Search from './search' |
|||
import SketchColor from './sketchColor' |
|||
import ExportAndImport from './export' |
|||
// import Upload from './Upload';
|
|||
|
|||
|
|||
export { |
|||
Upload, |
|||
Uploads, |
|||
NoResource, |
|||
LimitTextArea, |
|||
// ProcessForm,
|
|||
FlowRecordTable, |
|||
Table, |
|||
Search, |
|||
SketchColor, |
|||
ExportAndImport, |
|||
|
|||
|
|||
}; |
|||
|
@ -1,70 +0,0 @@ |
|||
import React, { PureComponent } from 'react'; |
|||
import { Input } from 'antd'; |
|||
const { TextArea } = Input; |
|||
import './index.less'; |
|||
|
|||
/*** |
|||
* 显示最大输入字符数 |
|||
* maxLength:300(默认) |
|||
*/ |
|||
class LimitTextArea extends PureComponent { |
|||
|
|||
constructor(props) { |
|||
super(props) |
|||
this.state = { |
|||
len: 0, |
|||
maxLength: 300, |
|||
isvalid: true, // 是否显示最大字符数
|
|||
} |
|||
// 若需要覆盖onChange时,value必填
|
|||
if (props.onChange && !props.hasOwnProperty('value')) { |
|||
this.state.isvalid = false |
|||
console.warn('LimitTextArea:绑定onChange时,value属性必填,否则显示最大输入字符数将失效!') |
|||
} |
|||
} |
|||
|
|||
// 若外部定义了onChange事件,handleChange将会被覆盖
|
|||
handleChange = (e) => { |
|||
const { sep } = this.props |
|||
const val = e.target.value |
|||
const arr = (val || '').split(sep) |
|||
this.setState({ |
|||
len: arr.length |
|||
}) |
|||
} |
|||
|
|||
render () { |
|||
const { maxLength: defaultMax, isvalid } = this.state |
|||
const { sep, maxLength, value, ...restProps } = this.props |
|||
const max = maxLength > 0 ? maxLength : defaultMax |
|||
/** form组件中,value有值 */ |
|||
const arr = (value || '').split(sep) |
|||
let len = value ? arr.length : this.state.len |
|||
len = len > max ? max : len |
|||
/**截取最大字符串 */ |
|||
const val = arr.slice(0, len).join(sep) |
|||
const n = val ? len : 0 |
|||
const suffix = `${n}/${max}` |
|||
|
|||
return isvalid ? ( |
|||
<div className={'block'}> |
|||
<TextArea |
|||
onChange={ e => this.handleChange(e) } |
|||
value={val} |
|||
{ ...restProps } |
|||
/> |
|||
<span className={'counter'}>{suffix}</span> |
|||
</div> |
|||
) : <TextArea { ...this.props } /> |
|||
} |
|||
} |
|||
|
|||
LimitTextArea.defaultProps = { |
|||
/** 分割符 |
|||
* 可以是个字符串,如:'\n' |
|||
* 也可以是个正则表达式,如:/\n\r/ |
|||
*/ |
|||
sep: '' |
|||
} |
|||
|
|||
export default LimitTextArea; |
@ -1,10 +0,0 @@ |
|||
.block { |
|||
position: relative; |
|||
.counter { |
|||
position: absolute; |
|||
bottom: 5px; |
|||
right: 15px; |
|||
color: #adadad; |
|||
z-index: 2; |
|||
} |
|||
} |
@ -1,21 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import React from 'react'; |
|||
import { Result} from 'antd'; |
|||
import { MehOutlined } from '@ant-design/icons'; |
|||
class NoResource extends React.Component { |
|||
constructor(props) { |
|||
super(props); |
|||
} |
|||
render() { |
|||
const title = this.props.title ? this.props.title : "抱歉,没有可访问的资源!" |
|||
return ( |
|||
<Result |
|||
icon={<MehOutlined />} |
|||
title={title} |
|||
/> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default NoResource; |
@ -1,217 +0,0 @@ |
|||
/** |
|||
* Created by Xumeng 2020/04/01. |
|||
*/ |
|||
'use strict'; |
|||
|
|||
import React, { useState } from 'react'; |
|||
import PropTypes from 'prop-types'; |
|||
import { Space, Row, Col, Form, DatePicker, Input, Select, Button } from 'antd'; |
|||
import { DownOutlined, UpOutlined } from '@ant-design/icons'; |
|||
//import { fromJS } from 'immutable';
|
|||
import { Constans } from '$utils'; |
|||
import moment from 'moment'; |
|||
import './index.less'; |
|||
const FormItem = Form.Item; |
|||
const { RangePicker } = DatePicker; |
|||
|
|||
//通用搜索栏组件 使用方法查看底部propTypes
|
|||
|
|||
const Search = (props) => { |
|||
const [expand, setExpand] = useState(false); |
|||
const [form] = Form.useForm(); |
|||
const { colSpan } = props; |
|||
//初始化表单数据
|
|||
const getinitialValues = () => { |
|||
const { formList } = props; |
|||
let obj = {}; |
|||
formList.forEach((v) => { |
|||
const { field, initialValue } = v; |
|||
if (initialValue) { |
|||
obj[field] = initialValue; |
|||
} |
|||
}); |
|||
|
|||
return obj; |
|||
}; |
|||
//获取表单项
|
|||
const getFields = () => { |
|||
const formItemList = []; |
|||
const { formList, showNumber, offset } = props; |
|||
let showNum = showNumber ? showNumber : 3; |
|||
let offsetNum = offset ? offset : 0; |
|||
const span = Number.parseInt((24 - offsetNum) / showNum); |
|||
if (formList && formList.length > 0) { |
|||
formList.forEach((item, index) => { |
|||
const { label, field, type, placeholder, style, labelSpan, showTime, optionName, list, rules, itemProps } = item; |
|||
let num = 0; |
|||
if (index === 0 && offsetNum) { |
|||
num = offsetNum; |
|||
} |
|||
switch (type) { |
|||
case 'TIME': |
|||
const TIMES = ( |
|||
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}> |
|||
<FormItem label={label || '选择日期'} name={field} rules={rules}> |
|||
<DatePicker |
|||
getPopupContainer={(triggerNode) => triggerNode.parentNode} |
|||
//showTime={showTime ? showTime : false}
|
|||
style={style ? style : {}} |
|||
placeholder={placeholder || '选择日期'} |
|||
format='YYYY-MM-DD' |
|||
{...itemProps} |
|||
/> |
|||
</FormItem> |
|||
</Col> |
|||
); |
|||
formItemList.push(TIMES); |
|||
break; |
|||
case 'RANGETIME': |
|||
const RANGETIMES = ( |
|||
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}> |
|||
<FormItem label={label || '选择日期时间段'} name={field} rules={rules}> |
|||
<RangePicker |
|||
getPopupContainer={(triggerNode) => triggerNode.parentNode} |
|||
showTime={showTime ? showTime : false} |
|||
style={style ? style : {}} |
|||
//placeholder={placeholder || '选择日期时间段'}
|
|||
format={showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'} |
|||
{...itemProps} |
|||
/> |
|||
</FormItem> |
|||
</Col> |
|||
); |
|||
formItemList.push(RANGETIMES); |
|||
break; |
|||
case 'SELECT': |
|||
const SELECT = ( |
|||
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}> |
|||
<FormItem label={label || '请选择'} name={field} rules={rules}> |
|||
<Select |
|||
style={style ? style : {}} |
|||
getPopupContainer={(triggerNode) => triggerNode.parentNode} |
|||
placeholder={placeholder || '请选择'} |
|||
{...itemProps} |
|||
> |
|||
{getOptionList(list, optionName)} |
|||
</Select> |
|||
</FormItem> |
|||
</Col> |
|||
); |
|||
formItemList.push(SELECT); |
|||
break; |
|||
default: |
|||
const INPUT = ( |
|||
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}> |
|||
<FormItem label={label || '关键字'} name={field} rules={rules}> |
|||
<Input style={style ? style : {}} placeholder={placeholder || '请输入关键字'} {...itemProps} /> |
|||
</FormItem> |
|||
</Col> |
|||
); |
|||
formItemList.push(INPUT); |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
//默认显示个数处理
|
|||
return expand ? formItemList : formItemList.slice(0, showNum); |
|||
}; |
|||
|
|||
const onFinish = (values) => { |
|||
const { formList } = props; |
|||
let obj = Object.assign({}, values); |
|||
//处理时间
|
|||
formList.forEach((v) => { |
|||
if (v.type == 'TIME' && obj.hasOwnProperty(v.field)) { |
|||
if (obj[v.field]) { |
|||
obj[v.field] = [ |
|||
moment(obj[v.field]).startOf('day').format('YYYY-MM-DD HH:mm:ss'), |
|||
moment(obj[v.field]).endOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
]; |
|||
} |
|||
} |
|||
if (v.type == 'RANGETIME' && obj.hasOwnProperty(v.field)) { |
|||
if (Array.isArray(obj[v.field])) { |
|||
obj[v.field] = v.showTime |
|||
? [moment(obj[v.field][0]).format('YYYY-MM-DD HH:mm:ss'), moment(obj[v.field][1]).format('YYYY-MM-DD HH:mm:ss')] |
|||
: [ |
|||
moment(obj[v.field][0]).startOf('day').format('YYYY-MM-DD HH:mm:ss'), |
|||
moment(obj[v.field][1]).endOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
]; |
|||
} |
|||
} |
|||
//处理undefind
|
|||
if (obj[v.field] === undefined) { |
|||
obj[v.field] = null; |
|||
} |
|||
}); |
|||
|
|||
props.onSearch(obj); |
|||
}; |
|||
|
|||
const getOptionList = (data, name) => { |
|||
if (!data) { |
|||
return []; |
|||
} |
|||
return data.map((item) => ( |
|||
<Select.Option disabled={item.disabled || false} value={item.value} key={item.key || item.value}> |
|||
{item[`${name}`]} |
|||
</Select.Option> |
|||
)); |
|||
}; |
|||
|
|||
return ( |
|||
<Form |
|||
form={form} |
|||
name='smart_seal_search' |
|||
className='smart-seal-search-form' |
|||
onFinish={onFinish} |
|||
initialValues={getinitialValues()} |
|||
validateMessages={Constans.defaultValidateMessages} |
|||
> |
|||
<Row gutter={16}> |
|||
<Col span={colSpan ? colSpan.label : 18}> |
|||
<Row gutter={16}>{getFields()}</Row> |
|||
</Col> |
|||
<Col span={colSpan ? colSpan.button : 6}> |
|||
<Space> |
|||
<Button htmlType='submit' className='smart-seal-default-btn'> |
|||
查询 |
|||
</Button> |
|||
{props.showRest && ( |
|||
<Button |
|||
onClick={() => { |
|||
form.resetFields(); |
|||
}} |
|||
className='smart-seal-default-btn' |
|||
> |
|||
重置 |
|||
</Button> |
|||
)} |
|||
{props.formList.length > props.showNumber && ( |
|||
<a |
|||
style={{ marginLeft: 8, fontSize: 12 }} |
|||
onClick={() => { |
|||
setExpand(!expand); |
|||
}} |
|||
> |
|||
{expand ? <UpOutlined /> : <DownOutlined />} {expand ? '收起' : '展开'} |
|||
</a> |
|||
)} |
|||
</Space> |
|||
</Col> |
|||
</Row> |
|||
</Form> |
|||
); |
|||
}; |
|||
|
|||
Search.propTypes = { |
|||
//查询配置数组 [{label, field, type[TIME,RANGETIME,SELECT,INPUT], initialValue, placeholder, width, list(select使用) optionName(select使用) , showTime(是否显示时间) }]
|
|||
formList: PropTypes.array.isRequired, |
|||
showNumber: PropTypes.number, //默认展示几个item ,其余展开按钮控制
|
|||
offset: PropTypes.number, //设置第一个item的偏移值
|
|||
onSearch: PropTypes.func.isRequired, //查询提交函数
|
|||
showRest: PropTypes.bool, //是否显示重置按钮
|
|||
colSpan: PropTypes.object // 搜索栏整体布局 默认 {label: 18 : button: 6}
|
|||
}; |
|||
|
|||
export default Search; |
@ -1,3 +0,0 @@ |
|||
.ant-form-horizontal .ant-form-item-control { |
|||
flex: 1 1 0%; |
|||
} |
@ -1,82 +0,0 @@ |
|||
'use strict' |
|||
import React from 'react'; |
|||
import { SketchPicker } from 'react-color'; |
|||
|
|||
class SketchColor extends React.Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
displayColorPicker: false, |
|||
color: "#fff", |
|||
}; |
|||
|
|||
} |
|||
componentDidMount() { |
|||
const { color } = this.props; |
|||
|
|||
color && this.setState({ |
|||
color: color |
|||
}) |
|||
|
|||
} |
|||
handleClick = () => { |
|||
this.setState({ displayColorPicker: !this.state.displayColorPicker }) |
|||
}; |
|||
|
|||
handleClose = () => { |
|||
this.setState({ displayColorPicker: false }) |
|||
}; |
|||
|
|||
handleChange = (color) => { |
|||
const {onChangeComplete} = this.props; |
|||
this.setState({ color: color.hex }); |
|||
onChangeComplete && onChangeComplete(color) |
|||
}; |
|||
|
|||
render() { |
|||
const { color } = this.state; |
|||
const styles = { |
|||
color: { |
|||
width: '36px', |
|||
height: '14px', |
|||
borderRadius: '2px', |
|||
background: color, |
|||
}, |
|||
swatch: { |
|||
padding: '5px', |
|||
background: '#fff', |
|||
borderRadius: '1px', |
|||
boxShadow: '0 0 0 1px rgba(0,0,0,.1)', |
|||
display: 'inline-block', |
|||
cursor: 'pointer', |
|||
}, |
|||
popover: { |
|||
position: 'absolute', |
|||
zIndex: '2', |
|||
}, |
|||
cover: { |
|||
position: 'fixed', |
|||
top: '0px', |
|||
right: '0px', |
|||
bottom: '0px', |
|||
left: '0px', |
|||
}, |
|||
|
|||
}; |
|||
|
|||
return ( |
|||
<div> |
|||
<div style={ styles.swatch } onClick={ this.handleClick }> |
|||
<div style={ styles.color } /> |
|||
</div> |
|||
{ this.state.displayColorPicker ? <div style={ styles.popover }> |
|||
<div style={ styles.cover } onClick={ this.handleClose }/> |
|||
<SketchPicker width={300} color={ color } onChange={ this.handleChange } /> |
|||
</div> : null } |
|||
|
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default SketchColor |
@ -1,259 +0,0 @@ |
|||
'use strict'; |
|||
import React, { Component } from 'react'; |
|||
import PropTypes from 'prop-types'; |
|||
import { Table as AntdTable, Divider, Dropdown, Menu, Spin, Popconfirm } from 'antd'; |
|||
import { DownOutlined } from '@ant-design/icons'; |
|||
import { fromJS } from 'immutable'; |
|||
import './index.less'; |
|||
|
|||
const loop = (data, keypath, values) => { |
|||
// deal with array
|
|||
let dkey = keypath.slice(0, 1)[0]; |
|||
if (dkey) { |
|||
let dvalue = data[dkey]; |
|||
let otherKeypath = keypath.slice(1); |
|||
if (Array.isArray(dvalue)) { |
|||
if (otherKeypath.length) { |
|||
let immutableData = fromJS(data); |
|||
for (let index = 0; index < dvalue.length; index++) { |
|||
let tmp = immutableData.getIn([dkey, index]).toJS(); |
|||
loop(tmp, otherKeypath, values); |
|||
} |
|||
} |
|||
} else { |
|||
values.push(dvalue); |
|||
} |
|||
} |
|||
return values; |
|||
}; |
|||
|
|||
//通用table组件 使用方法查看底部propTypes
|
|||
class Table extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
selectedRowKeys: [], |
|||
pagination: { |
|||
showTotal: (total) => `共${total}条`, |
|||
showSizeChanger: true, |
|||
showQuickJumper: true, |
|||
responsive: true, |
|||
pageSizeOptions: ['10', '20', '30', '40'], |
|||
defaultPageSize: 10 |
|||
}, |
|||
visible: {}, |
|||
current: 1 |
|||
}; |
|||
this.token = JSON.parse(sessionStorage.getItem('user')).token; |
|||
} |
|||
handleMenuClick = (e) => { |
|||
//TODO 点击删除
|
|||
}; |
|||
|
|||
UNSAFE_componentWillReceiveProps() { |
|||
this.setState({ |
|||
visible: {} |
|||
}); |
|||
} |
|||
|
|||
handleVisibleChange = (flag, key) => { |
|||
let visible = this.state.visible; |
|||
visible[key] = flag; |
|||
this.setState({ visible }); |
|||
}; |
|||
|
|||
getColumns = () => { |
|||
let columns = this.props.attrs.map((attr) => { |
|||
const { dataIndex, key, name, render, width, ellipsis, fixed } = attr; |
|||
let obj = { |
|||
title: name, |
|||
dataIndex: dataIndex || key, |
|||
key: key, |
|||
ellipsis: ellipsis == undefined ? true : ellipsis, |
|||
width: width |
|||
}; |
|||
if (render) { |
|||
obj.render = render; |
|||
} |
|||
if (fixed) { |
|||
obj.fixed = fixed; |
|||
} |
|||
return obj; |
|||
}); |
|||
|
|||
if (!this.props.actions) { |
|||
return columns; |
|||
} |
|||
|
|||
columns.push({ |
|||
title: '操作', |
|||
key: 'action', |
|||
className: 'fs-table-column-action', |
|||
render: (text, record) => { |
|||
let actions = this.props.actions; |
|||
if (record && record.actions) actions = actions.filter((act) => record.actions.includes(act.key)); |
|||
|
|||
return ( |
|||
<span style={{ whiteSpace: 'normal' }}> |
|||
{actions.reduce((p, c, i) => { |
|||
const { key, name, handler, dom, style, dropdown, hidden, popconfirm, getPopTitleFun, definedTitle } = c; |
|||
if (typeof hidden === 'function') { |
|||
let unVisiable = hidden(record); |
|||
if (unVisiable) return p; |
|||
} |
|||
|
|||
if (dropdown && Array.isArray(dropdown)) { |
|||
// 操作按钮 下拉菜单处理
|
|||
const menus = ( |
|||
<Menu onClick={this.handleMenuClick}> |
|||
{dropdown.map((m) => { |
|||
if (m.popconfirm) { |
|||
return ( |
|||
<Menu.Item key={m.key}> |
|||
<Popconfirm |
|||
placement='left' |
|||
title={m.title ? m.title : `确认${m.name}?`} |
|||
onConfirm={() => { |
|||
m.handler && m.handler(record); |
|||
}} |
|||
> |
|||
<a onClick={(e) => e.preventDefault()}>{m.name}</a> |
|||
</Popconfirm> |
|||
</Menu.Item> |
|||
); |
|||
} else { |
|||
return ( |
|||
<Menu.Item key={m.key}> |
|||
<a onClick={(e) => m.handler && m.handler(record)}>{m.name}</a> |
|||
</Menu.Item> |
|||
); |
|||
} |
|||
})} |
|||
</Menu> |
|||
); |
|||
p.push( |
|||
<Dropdown |
|||
overlay={menus} |
|||
onVisibleChange={(e) => this.handleVisibleChange(e, text.id)} |
|||
visible={this.state.visible ? this.state.visible[text.id] : false} |
|||
key={key} |
|||
> |
|||
<a style={style ? style : { display: 'inline-block' }} onClick={(e) => e.preventDefault()}> |
|||
{name} <DownOutlined /> |
|||
</a> |
|||
</Dropdown> |
|||
); |
|||
} else { |
|||
if (dom) { |
|||
popconfirm |
|||
? p.push( |
|||
<Popconfirm placement='left' title={`确认${name}吗?`} onConfirm={() => handler(record)} key={key}> |
|||
<span |
|||
style={style ? style : { display: 'inline-block' }} |
|||
key={key} |
|||
onClick={(e) => e.preventDefault()} |
|||
> |
|||
{dom} |
|||
</span> |
|||
</Popconfirm> |
|||
) |
|||
: p.push( |
|||
<span style={style ? style : { display: 'inline-block' }} key={key} onClick={(e) => handler(record)}> |
|||
{dom} |
|||
</span> |
|||
); |
|||
} else { |
|||
popconfirm |
|||
? p.push( |
|||
<Popconfirm placement='left' title={definedTitle && getPopTitleFun ? getPopTitleFun(record) : `确认${name}吗?`} onConfirm={() => handler(record)} key={key}> |
|||
<a |
|||
style={style ? style : { display: 'inline-block' }} |
|||
key={key} |
|||
onClick={(e) => e.preventDefault()} |
|||
> |
|||
{name} |
|||
</a> |
|||
</Popconfirm> |
|||
) |
|||
: p.push( |
|||
<a style={style ? style : { display: 'inline-block' }} key={key} onClick={(e) => handler(record)}> |
|||
{name} |
|||
</a> |
|||
); |
|||
} |
|||
} |
|||
|
|||
if (i < actions.length - 1) p.push(<Divider key={`divider${i}`} type='vertical' />); |
|||
return p; |
|||
}, [])} |
|||
</span> |
|||
); |
|||
} |
|||
}); |
|||
|
|||
return columns; |
|||
}; |
|||
|
|||
getColumnData = (opts) => { |
|||
const { data, keypath, spliter, rawdata } = opts; |
|||
let v = null; |
|||
let outer = data[keypath[0]]; |
|||
if (Array.isArray(outer)) { |
|||
let values = loop(data, keypath, []); |
|||
v = rawdata ? values : values.join(spliter || ','); |
|||
} else { |
|||
v = fromJS(data).getIn(keypath); |
|||
} |
|||
return v; |
|||
}; |
|||
|
|||
getPagination = () => { |
|||
const { total, curpage } = this.props; |
|||
const { pagination, current } = this.state; |
|||
pagination.total = total ? total : 0; |
|||
pagination.current = curpage ? curpage : current; |
|||
return pagination; |
|||
}; |
|||
//暂时只支持分页远程处理
|
|||
handleTableChange = (pagination, filters, sorter) => { |
|||
this.setState({ |
|||
current: pagination.current |
|||
}); |
|||
if (this.props.onTableChange) { |
|||
let limit = Number.parseInt(pagination.pageSize); |
|||
let offset = limit * (Number.parseInt(pagination.current) - 1); |
|||
this.props.onTableChange(limit, offset, pagination.current); |
|||
} |
|||
}; |
|||
render() { |
|||
const { scroll, rowSelection, data, isRequesting, showHeader, noShowPagination, rowKey } = this.props; |
|||
return ( |
|||
<AntdTable |
|||
className={'fs-table'} |
|||
id='fs-smart-seal-table' |
|||
dataSource={data} |
|||
loading={isRequesting} |
|||
showHeader={showHeader} |
|||
scroll={scroll ? scroll : {}} |
|||
rowKey={rowKey ? rowKey : 'id'} |
|||
rowSelection={rowSelection ? rowSelection : null} |
|||
columns={this.getColumns()} |
|||
onChange={this.handleTableChange} |
|||
pagination={noShowPagination ? false : this.getPagination()} |
|||
/> |
|||
); |
|||
} |
|||
} |
|||
Table.propTypes = { |
|||
data: PropTypes.array.isRequired, //数据资源
|
|||
attrs: PropTypes.array.isRequired, //属性数组用于colums {key,name,|render|isImg|nullable}
|
|||
actions: PropTypes.array, //操作栏数组 { key,name,style,handler,dropdown,dom}
|
|||
scroll: PropTypes.object, //同antd 用法
|
|||
rowSelection: PropTypes.object, //表格行是否可选择 配置项同antd
|
|||
onTableChange: PropTypes.func, //onChange触发函数
|
|||
showHeader: PropTypes.bool, //是否显示表头
|
|||
noShowPagination: PropTypes.bool, //是否显示分页器,
|
|||
showLessItems: PropTypes.bool, //是否显示较少页面内容,
|
|||
rowKey: PropTypes.string//表格记录的key
|
|||
}; |
|||
export default Table; |
@ -1,34 +0,0 @@ |
|||
//宽度小于1920px |
|||
@media screen and (max-width:1920px) { |
|||
.fs-table :global(.ant-table tbody > tr > td) { |
|||
border: none; |
|||
font-size: 14px; |
|||
font-weight: 400; |
|||
color: #666; |
|||
} |
|||
.fs-table :global(.ant-table thead > tr > th) { |
|||
border: none; |
|||
font-size: 14px; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
//宽度大于等于1920px |
|||
@media screen and (min-width:1920px) { |
|||
.fs-table :global(.ant-table tbody > tr > td) { |
|||
border: none; |
|||
font-size: 14px; |
|||
font-weight: 400; |
|||
color: #666; |
|||
} |
|||
.fs-table :global(.ant-table thead > tr > th) { |
|||
border: none; |
|||
font-size: 16px; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
|
|||
// .fs-table :global(.fs-table-column-action) { |
|||
// font-size: 16px; |
|||
// } |
@ -0,0 +1,26 @@ |
|||
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 Basis = ({ user, module, setModule }) => { |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
return <div style={{ width: '100%', heigh: '100%' }}> |
|||
基础信息 |
|||
|
|||
</div > |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
clientHeight: global.clientHeight, |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(Basis); |
@ -0,0 +1,26 @@ |
|||
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'; |
|||
import Title from 'antd/lib/skeleton/Title'; |
|||
|
|||
const Capacity = ({ user, }) => { |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
return <div style={{ width: '100%', heigh: '100%' }}> |
|||
能耗监测 |
|||
</div > |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
clientHeight: global.clientHeight, |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(Capacity); |
@ -0,0 +1,27 @@ |
|||
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'; |
|||
import Title from 'antd/lib/skeleton/Title'; |
|||
|
|||
const Electrical = ({ user, module, setModule }) => { |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
return <div style={{ width: '100%', heigh: '100%' }}> |
|||
电排远控 |
|||
|
|||
</div > |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
clientHeight: global.clientHeight, |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(Electrical); |
@ -0,0 +1,75 @@ |
|||
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'; |
|||
import Title from 'antd/lib/skeleton/Title'; |
|||
|
|||
const Header = ({ user, module, setModule }) => { |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
return <div style={{ width: '100%', heigh: '100%' }}> |
|||
<div style={{ |
|||
width: '100%', height: 130, |
|||
backgroundImage: 'url(/assets/images/monitor/header-bg.png)', |
|||
backgroundSize: 'cover', |
|||
backgroundPosition: 'center', |
|||
display: 'flex', |
|||
justifyContent: 'space-between', |
|||
}}> |
|||
<div style={{ width: 140 }}>天气</div> |
|||
<div style={{ |
|||
width: 424, lineHeight: '136px', |
|||
fontFamily: 'YouSheBiaoTiHei', fontSize: 43, color: '#E2F8FF', letterSpacing: 4, fontWeight: 600 |
|||
}}>泵站自动化控制系统</div> |
|||
<div> |
|||
<div style={{ |
|||
width: 140, height: 52, |
|||
backgroundImage: 'url(/assets/images/monitor/user.png)', |
|||
backgroundSize: 'cover', |
|||
backgroundPosition: 'center' |
|||
}}>{user?.name} |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
<div style={{ display: 'flex', justifyContent: 'center' }}> |
|||
<div style={{ |
|||
width: '45%', height: 44, textAlign: 'center', |
|||
backgroundImage: 'url(/assets/images/monitor/strip.png)', |
|||
backgroundSize: '100% 25%', |
|||
backgroundPosition: 'center', |
|||
backgroundRepeat: 'no-repeat', |
|||
display: 'flex', |
|||
alignItems: 'flex-end', justifyContent: 'space-around', |
|||
}}> |
|||
{[{ title: '基础信息', key: 'basis' }, |
|||
{ title: '能耗监测', key: 'capacity' }, |
|||
{ title: '电排远控', key: 'electrical ' }, |
|||
{ title: '实时监测', key: 'realTime' },].map(v => { |
|||
return <div key={v.key} style={{ |
|||
width: 80, height: 30, lineHeight: '30px', |
|||
backgroundImage: `url(/assets/images/monitor/${module == v.key ? 'pitch-on' : 'chose-none'}.png)`, |
|||
backgroundSize: 'center', |
|||
backgroundPosition: 'center', |
|||
color: 'white', |
|||
}} onClick={() => setModule(v.key)}>{v.title}</div> |
|||
})} |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
</div > |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
clientHeight: global.clientHeight, |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(Header); |
@ -0,0 +1,26 @@ |
|||
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'; |
|||
import Title from 'antd/lib/skeleton/Title'; |
|||
|
|||
const RealTime = ({ user, module, setModule }) => { |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
return <div style={{ width: '100%', heigh: '100%' }}> |
|||
实时监测 |
|||
</div > |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
clientHeight: global.clientHeight, |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(RealTime); |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
import SystemManagement from './systemManagement'; |
|||
|
|||
export { SystemManagement }; |
@ -0,0 +1,50 @@ |
|||
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 Header from '../components/header'; |
|||
import Basis from '../components/basis'; |
|||
import Capacity from '../components/capacity'; |
|||
import Electrical from '../components/electrical'; |
|||
import RealTime from '../components/realTime'; |
|||
|
|||
const TreeNode = Tree.TreeNode; |
|||
|
|||
const SystemManagement = ({ clientHeight, user }) => { |
|||
|
|||
const [module, setModule] = useState('basis') |
|||
useEffect(() => { |
|||
|
|||
}, []) |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
return ( |
|||
<div style={{ |
|||
width: '100vw', height: '100vh', |
|||
backgroundImage: 'url(/assets/images/monitor/screen-bg.png)', |
|||
backgroundSize: 'cover', |
|||
backgroundPosition: 'center' |
|||
}}> |
|||
<Header module={module} setModule={setModule} /> |
|||
{module == 'basis' ? <Basis /> : ""} |
|||
{module == 'capacity' ? <Capacity /> : ""} |
|||
{module == 'electrical' ? <Electrical /> : ""} |
|||
{module == 'realTime' ? <RealTime /> : ""} |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
clientHeight: global.clientHeight, |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(SystemManagement); |
@ -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>
|
|||
); |
|||
} |
@ -0,0 +1,27 @@ |
|||
'use strict'; |
|||
import { SystemManagement } from './containers'; |
|||
|
|||
export default [{ |
|||
type: 'outer', |
|||
route: { |
|||
path: '/systemManagement', |
|||
key: 'systemManagement', |
|||
breadcrumb: '泵站大屏', |
|||
component: SystemManagement, |
|||
// menuSelectKeys: ['userManage'],
|
|||
// menuOpenKeys: ['organization'],
|
|||
// childRoutes: [{
|
|||
// path: '/user',
|
|||
// key: 'userManage',
|
|||
// menuSelectKeys: ['userManage'],
|
|||
// component: UserManage,
|
|||
// breadcrumb: '部门成员',
|
|||
// }, {
|
|||
// path: '/authority',
|
|||
// key: 'authority',
|
|||
// component: Authority,
|
|||
// menuSelectKeys: ['authority'],
|
|||
// breadcrumb: '权限配置',
|
|||
// }]
|
|||
} |
|||
}]; |
@ -1,88 +0,0 @@ |
|||
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); |
@ -1,74 +0,0 @@ |
|||
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); |
@ -1,121 +0,0 @@ |
|||
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 |
@ -1,171 +0,0 @@ |
|||
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); |
@ -1,149 +0,0 @@ |
|||
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 ? `[${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); |
@ -1,6 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import Authority from './authority'; |
|||
import UserManage from './user'; |
|||
|
|||
export { Authority, UserManage }; |
@ -1,332 +0,0 @@ |
|||
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 UserManage = (props) => { |
|||
const user = JSON.parse(sessionStorage.getItem('user')); |
|||
|
|||
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); |
@ -1,22 +0,0 @@ |
|||
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> |
|||
); |
|||
} |
@ -1,26 +0,0 @@ |
|||
'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: '权限配置', |
|||
}] |
|||
} |
|||
}]; |
@ -1,9 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import * as plan from './plan' |
|||
import * as record from './record' |
|||
|
|||
export default { |
|||
...plan, |
|||
...record, |
|||
} |
@ -1,80 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import { basicAction } from '@peace/utils' |
|||
import { ApiTable } from '$utils' |
|||
|
|||
export function getPatrolPlan() { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'GET_PATROL_PLAN', |
|||
url: ApiTable.patrolPlan, |
|||
msg: { error: '获取巡检计划失败' }, |
|||
}); |
|||
} |
|||
|
|||
export function createPatrolPlan(data) { |
|||
return dispatch => basicAction({ |
|||
type: 'post', |
|||
data, |
|||
dispatch: dispatch, |
|||
actionType: 'CREATE_PATROL_PLAN', |
|||
url: ApiTable.patrolPlan, |
|||
msg: { error: '新增巡检计划失败' }, |
|||
}); |
|||
} |
|||
|
|||
export function delPatrolPlan(id) { |
|||
return dispatch => basicAction({ |
|||
type: 'del', |
|||
dispatch: dispatch, |
|||
actionType: 'DEL_PATROL_PLAN', |
|||
url: ApiTable.delPatrolPlan.replace('{id}', id), |
|||
msg: { error: '删除巡检计划失败' }, |
|||
}); |
|||
} |
|||
|
|||
export function updatePatrolPlan(data) { |
|||
return dispatch => basicAction({ |
|||
type: 'put', |
|||
data, |
|||
dispatch: dispatch, |
|||
actionType: 'UPDATE_PATROL_PLAN', |
|||
url: ApiTable.patrolPlan, |
|||
msg: { error: '修改巡检计划失败' }, |
|||
}); |
|||
} |
|||
|
|||
export function getUserList() { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'GET_USER_LIST', |
|||
url: ApiTable.getDepUser.replace('{depId}', null), |
|||
msg: { error: '获取人员列表失败' }, |
|||
reducer: { name: 'userList' } |
|||
}); |
|||
} |
|||
|
|||
export function getProjectList(query) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'get', |
|||
query, |
|||
dispatch, |
|||
actionType: 'GET_PROJEECT_LIST', |
|||
url: ApiTable.getProjectList, |
|||
msg: { error: '获取结构物列表失败', }, |
|||
reducer: { name: 'structureList' } |
|||
}); |
|||
} |
|||
|
|||
export function positionList(query) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'get', |
|||
query, |
|||
dispatch, |
|||
actionType: 'POSITION_LIST', |
|||
url: ApiTable.position, |
|||
msg: { error: '获取点位列表失败', }, |
|||
}); |
|||
} |
@ -1,17 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import { basicAction } from '@peace/utils' |
|||
|
|||
export const GET_PATROL_RECORD_LIST = 'GET_PATROL_RECORD_LIST'; |
|||
export const GET_PATROL_RECORD_LIST_SUCCESS = 'GET_PATROL_RECORD_LIST_SUCCESS'; |
|||
export const GET_PATROL_RECORD_LIST_ERROR = 'GET_PATROL_RECORD_LIST_ERROR'; |
|||
export function records(url) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'get', |
|||
dispatch, |
|||
actionType: GET_PATROL_RECORD_LIST, |
|||
url: url, |
|||
msg: { error: '获取巡检记录失败', }, |
|||
reducer: { name: 'record' } |
|||
}); |
|||
} |