@ -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'; |
'use strict'; |
||||
|
|
||||
import Upload from './Upload'; |
// 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' |
|
||||
|
|
||||
export { |
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' } |
|
||||
}); |
|
||||
} |
|