Browse Source

周期计划和响应记录功能优化

dev
wenlele 1 year ago
parent
commit
fb6076bb6f
  1. 10
      api/.vscode/launch.json
  2. 30
      api/app/lib/controllers/maintenancePlan/index.js
  3. 56
      api/app/lib/controllers/record/index.js
  4. 3
      api/app/lib/index.js
  5. 176
      api/app/lib/models/maintenance_plan.js
  6. 157
      api/app/lib/models/maintenance_record.js
  7. 4
      api/app/lib/routes/record/index.js
  8. 14
      script/0.29/schema/update_maintenance_plan.sql
  9. 84
      web/client/src/sections/means/containers/devOpsStandard.jsx
  10. 60
      web/client/src/sections/means/containers/faultInformation.jsx
  11. 65
      web/client/src/sections/means/containers/projectMeans.jsx
  12. 61
      web/client/src/sections/means/containers/repairFQA.jsx
  13. 24
      web/client/src/sections/means/nav-item.jsx
  14. 32
      web/client/src/sections/means/routes.js
  15. 2
      web/client/src/sections/means/style.less
  16. 18
      web/client/src/sections/service/actions/record.js
  17. 166
      web/client/src/sections/service/components/cycAddmodal.jsx
  18. 87
      web/client/src/sections/service/components/planAddmodal.jsx
  19. 278
      web/client/src/sections/service/components/recordModal.jsx
  20. 276
      web/client/src/sections/service/containers/cyclePlan.jsx
  21. 441
      web/client/src/sections/service/containers/maintenanceRecords.jsx
  22. 474
      web/client/src/sections/service/containers/serviceRecord.jsx
  23. 5
      web/client/src/utils/webapi.js

10
api/.vscode/launch.json

@ -42,13 +42,13 @@
// "--qndmn http://resources.anxinyun.cn", // "--qndmn http://resources.anxinyun.cn",
// "--qndmn http://rhvqdivo5.hn-bkt.clouddn.com", // "--qndmn http://rhvqdivo5.hn-bkt.clouddn.com",
// click // click
// "--clickHouseUrl http://10.8.30.71", "--clickHouseUrl http://10.8.30.71",
// "--clickHousePort 8123", "--clickHousePort 30123",
// click // click
// "--clickHouseUrl http://10.8.30.161", // "--clickHouseUrl http://10.8.30.161",
// "--clickHousePort 30123", // "--clickHousePort 30123",
"--clickHouseUrl http://10.8.30.156", // "--clickHouseUrl http://10.8.30.156",
"--clickHousePort 8123", // "--clickHousePort 8123",
// "--clickHouseUrl https://clickhouse01.anxinyun.cn/play", // "--clickHouseUrl https://clickhouse01.anxinyun.cn/play",
// click // click
@ -66,7 +66,7 @@
// "--clickHouseDataAlarm default", // "--clickHouseDataAlarm default",
// "--clickHouseIot iot", // "--clickHouseIot iot",
// //
"--clickHouseAnxincloud anxinyun888", "--clickHouseAnxincloud anxinyun1",
"--clickHousePepEmis pepca8", "--clickHousePepEmis pepca8",
"--clickHouseProjectManage peppm8", "--clickHouseProjectManage peppm8",
"--clickHouseVcmp video_access_dev", "--clickHouseVcmp video_access_dev",

30
api/app/lib/controllers/maintenancePlan/index.js

@ -2,27 +2,34 @@
//维护计划 //维护计划
async function getMaintenancePlan(ctx) { async function getMaintenancePlan (ctx) {
const query = ctx.query const query = ctx.query
try { try {
const { models } = ctx.fs.dc const { models } = ctx.fs.dc
const { clickHouse } = ctx.app.fs const { clickHouse } = ctx.app.fs
//console.log('11121', query) //console.log('11121', query)
let resCount = await models.MaintenancePlan.count({ let resCount = await models.MaintenancePlan.count({
where: { type: query.type } where: { type: query.type }
}) })
const res = await models.MaintenancePlan.findAndCount({ let option = {
order: [['id', 'DESC']], order: [['id', 'DESC']],
offset: (query.pageIndex - 1) * query.pageSize, attributes: ['id', 'missionName', 'remark', 'reason', 'planFinishTime', 'actualFinishTime', 'type', 'state', 'recordId'],
limit: query.pageSize,
attributes: ['id', 'missionName', 'remark', 'reason', 'planFinishTime', 'actualFinishTime', 'type', 'state'],
where: { type: query.type }, where: { type: query.type },
include: [{ include: [{
attributes: ['id', 'maintenancePlanId', 'pepUserId'], attributes: ['id', 'maintenancePlanId', 'pepUserId'],
model: models.MaintenancePlanExecuteUser model: models.MaintenancePlanExecuteUser
}] }]
}) }
if (query.pageIndex && query.pageSize) {
option.offset = (query.pageIndex - 1) * query.pageSize
option.limit = query.pageSize
}
if (query.recordId) {
option.where.recordId = { $contains: [query.recordId] }
}
const res = await models.MaintenancePlan.findAndCount(option)
//console.log('res1', res) //console.log('res1', res)
const arrayUserId = [] const arrayUserId = []
if (res.rows.length > 0) { if (res.rows.length > 0) {
@ -44,6 +51,7 @@ async function getMaintenancePlan(ctx) {
actualFinishTime: item.actualFinishTime, actualFinishTime: item.actualFinishTime,
type: item.type, type: item.type,
state: item.state, state: item.state,
recordId: item.recordId,
maintenancePlanExecuteUsers: maintenancePlanExecuteUsers:
item.maintenancePlanExecuteUsers.map((item1) => { item.maintenancePlanExecuteUsers.map((item1) => {
const nameArr = userRes.find((ac) => { return ac.id == item1.pepUserId }) const nameArr = userRes.find((ac) => { return ac.id == item1.pepUserId })
@ -76,7 +84,7 @@ async function getMaintenancePlan(ctx) {
} }
async function delMaintenancePlan(ctx) { async function delMaintenancePlan (ctx) {
const transaction = await ctx.fs.dc.orm.transaction(); const transaction = await ctx.fs.dc.orm.transaction();
try { try {
const { models } = ctx.fs.dc const { models } = ctx.fs.dc
@ -98,7 +106,7 @@ async function delMaintenancePlan(ctx) {
} }
} }
async function editMaintenancePlan(ctx) { async function editMaintenancePlan (ctx) {
const data = ctx.request.body const data = ctx.request.body
const transaction = await ctx.fs.dc.orm.transaction(); const transaction = await ctx.fs.dc.orm.transaction();
try { try {
@ -112,6 +120,7 @@ async function editMaintenancePlan(ctx) {
missionName: data.missionName, missionName: data.missionName,
remark: data.remark, remark: data.remark,
reason: data.reason, reason: data.reason,
recordId: data.recordId,
planFinishTime: data.planFinishTime, planFinishTime: data.planFinishTime,
state: data.state state: data.state
}, { where: { id: data.id } }) }, { where: { id: data.id } })
@ -120,6 +129,7 @@ async function editMaintenancePlan(ctx) {
actualFinishTime: data.actualFinishTime, actualFinishTime: data.actualFinishTime,
missionName: data.missionName, missionName: data.missionName,
remark: data.remark, remark: data.remark,
recordId: data.recordId,
planFinishTime: data.planFinishTime, planFinishTime: data.planFinishTime,
state: data.state state: data.state
}, { where: { id: data.id } }) }, { where: { id: data.id } })
@ -143,6 +153,7 @@ async function editMaintenancePlan(ctx) {
reason: data.reason, reason: data.reason,
planFinishTime: data.planFinishTime, planFinishTime: data.planFinishTime,
type: data.type, type: data.type,
recordId: data.recordId,
state: data.state state: data.state
}) })
//console.log('data.manger',data.manger) //console.log('data.manger',data.manger)
@ -159,6 +170,7 @@ async function editMaintenancePlan(ctx) {
missionName: data.missionName, missionName: data.missionName,
remark: data.remark, remark: data.remark,
planFinishTime: data.planFinishTime, planFinishTime: data.planFinishTime,
recordId: data.recordId,
type: data.type, type: data.type,
state: data.state state: data.state
}) })

56
api/app/lib/controllers/record/index.js

@ -1,11 +1,12 @@
'use strict'; 'use strict';
const moment = require('moment'); const moment = require('moment');
async function getRecord(ctx) { async function getRecord (ctx) {
try { try {
const { redis } = ctx.app const { redis } = ctx.app
const { models } = ctx.fs.dc; const { models } = ctx.fs.dc;
const sequelize = ctx.fs.dc.ORM; const sequelize = ctx.fs.dc.ORM;
const { clickHouse } = ctx.app.fs
const { startTime, endTime, pageSize, pageIndex } = ctx.query const { startTime, endTime, pageSize, pageIndex } = ctx.query
console.log('queryz', ctx.query) console.log('queryz', ctx.query)
@ -19,7 +20,7 @@ async function getRecord(ctx) {
}) })
let recordRes = await models.MaintenanceRecord.findAndCountAll({ let recordRes = await models.MaintenanceRecord.findAndCountAll({
order: [['id', 'DESC']], order: [['id', 'DESC']],
attributes: ['id', 'sketch', 'occurrenceTime', 'solvingTime', 'interruptDuration', 'type', 'record'], attributes: ['id', 'sketch', 'occurrenceTime', 'solvingTime', 'interruptDuration', 'type', 'record','files'],
offset: (pageIndex - 1) * pageSize, offset: (pageIndex - 1) * pageSize,
limit: pageSize, limit: pageSize,
where: { where: {
@ -35,7 +36,11 @@ async function getRecord(ctx) {
}) })
//console.log('recordRes', recordRes) //console.log('recordRes', recordRes)
const arrayUserId = [] const arrayUserId = []
recordRes.rows.forEach((item) => { item.maintenanceRecordExecuteUsers.forEach((item1) => { arrayUserId.push(item1.pepUserId) }) }) const recordId = []
recordRes.rows.forEach((item) => {
recordId.push(item.id)
item.maintenanceRecordExecuteUsers.forEach((item1) => { arrayUserId.push(item1.pepUserId) })
})
const arrayUserIdCopy = [...new Set(arrayUserId)] const arrayUserIdCopy = [...new Set(arrayUserId)]
// console.log('(' + arrayUserIdCopy.toString() + ')', '22222') // console.log('(' + arrayUserIdCopy.toString() + ')', '22222')
let userRes = await redis.get('allUser') let userRes = await redis.get('allUser')
@ -44,7 +49,12 @@ async function getRecord(ctx) {
return arrayUserIdCopy.some((children) => { return children == item.id }) return arrayUserIdCopy.some((children) => { return children == item.id })
}) })
//console.log('userRes', userRes) //console.log('userRes', userRes)
const res = recordRes.rows.map((item) => { let planList = await models.MaintenancePlan.findAll({
where: { recordId: { $overlap: recordId } },
})
const res = await recordRes.rows.map((item) => {
return { return {
id: item.id, id: item.id,
interruptDuration: item.interruptDuration, interruptDuration: item.interruptDuration,
@ -53,6 +63,8 @@ async function getRecord(ctx) {
sketch: item.sketch, sketch: item.sketch,
solvingTime: item.solvingTime, solvingTime: item.solvingTime,
type: item.type, type: item.type,
files: item.files || null,
planList: planList.filter(v => v.recordId && v.recordId.includes(item.id)) || [],
maintenanceRecordExecuteUsers: maintenanceRecordExecuteUsers:
item.maintenanceRecordExecuteUsers.map((item1) => { item.maintenanceRecordExecuteUsers.map((item1) => {
const userArr = userRes.find((ac) => { return ac.id == item1.pepUserId }) const userArr = userRes.find((ac) => { return ac.id == item1.pepUserId })
@ -67,7 +79,7 @@ async function getRecord(ctx) {
} }
) )
//console.log('res1', res) //console.log('res1', res)
ctx.body = { count: resCount, res } ctx.body = { count: resCount, res: res }
ctx.status = 200 ctx.status = 200
} catch (error) { } catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
@ -78,18 +90,18 @@ async function getRecord(ctx) {
} }
} }
//新增和编辑服务记录 //新增和编辑服务记录
async function addRecord(ctx) { async function addRecord (ctx) {
const transaction = await ctx.fs.dc.orm.transaction(); const transaction = await ctx.fs.dc.orm.transaction();
const { models } = ctx.fs.dc const { models } = ctx.fs.dc
const params = ctx.request.body const params = ctx.request.body
const { solvingTime, occurrencTime, sketch, record, settler, type, id, msg } = params const { solvingTime, occurrencTime, sketch, record, settler, type, id, msg, files } = params
const breakTime = (Date.parse(solvingTime) - Date.parse(occurrencTime)) / 1000 const breakTime = (Date.parse(solvingTime) - Date.parse(occurrencTime)) / 1000
try { try {
//中断时长 //中断时长
//console.log('resss1', Date.parse(solvingTime), occurrencTime) //console.log('resss1', Date.parse(solvingTime), occurrencTime)
if (id) { if (id) {
await models.MaintenanceRecord.update({ await models.MaintenanceRecord.update({
solvingTime, occurrenceTime: occurrencTime, sketch, record, settler, type, interruptDuration: breakTime solvingTime, occurrenceTime: occurrencTime, sketch, record, settler, type, interruptDuration: breakTime, files: files
}, { where: { id } }) }, { where: { id } })
await models.MaintenanceRecordExecuteUser.destroy({ where: { maintenanceRecordId: id } }) await models.MaintenanceRecordExecuteUser.destroy({ where: { maintenanceRecordId: id } })
const resArry = settler.map((item) => { const resArry = settler.map((item) => {
@ -99,7 +111,7 @@ async function addRecord(ctx) {
}) })
await models.MaintenanceRecordExecuteUser.bulkCreate(resArry) await models.MaintenanceRecordExecuteUser.bulkCreate(resArry)
} else { } else {
const aa = await models.MaintenanceRecord.create({ solvingTime, occurrenceTime: occurrencTime, sketch, record, settler, type, interruptDuration: breakTime }) const aa = await models.MaintenanceRecord.create({ solvingTime, occurrenceTime: occurrencTime, sketch, record, settler, type, interruptDuration: breakTime,files: files })
const recordId = aa.id const recordId = aa.id
const resArry = settler.map((item) => { const resArry = settler.map((item) => {
return { return {
@ -122,7 +134,7 @@ async function addRecord(ctx) {
} }
} }
//删除服务记录 //删除服务记录
async function delRecord(ctx) { async function delRecord (ctx) {
const transaction = await ctx.fs.dc.orm.transaction(); const transaction = await ctx.fs.dc.orm.transaction();
const { models } = ctx.fs.dc const { models } = ctx.fs.dc
const params = ctx.params const params = ctx.params
@ -143,7 +155,29 @@ async function delRecord(ctx) {
} }
async function respondRecord (ctx) {
try {
const { models } = ctx.fs.dc;
let res = await models.MaintenanceRecord.findAll({
order: [['id', 'DESC']],
attributes: ['id', 'sketch', 'occurrenceTime'],
})
ctx.body = res
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '获取响应记录数据失败'
}
}
}
module.exports = { module.exports = {
getRecord, addRecord, delRecord getRecord,
addRecord,
delRecord,
respondRecord
}; };

3
api/app/lib/index.js

@ -61,7 +61,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App, AlarmAppearRecord, AlarmConfirmLog, EmailSendLog, LatestDynamicList, AlarmPushConfig, AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App, AlarmAppearRecord, AlarmConfirmLog, EmailSendLog, LatestDynamicList, AlarmPushConfig,
MaintenanceRecord, MaintenanceRecordExecuteUser, MaintenancePlanExecuteUser, MaintenancePlan, EquipmentMaintenanceRecord, EquipmentMaintenanceRecordProject, MaintenanceRecord, MaintenanceRecordExecuteUser, MaintenancePlanExecuteUser, MaintenancePlan, EquipmentMaintenanceRecord, EquipmentMaintenanceRecordProject,
EquipmentMaintenanceRecordExecuteUser, ServerMaintenanceRecordRepairman, ServerMaintenanceRecord, EquipmentMaintenanceRecordExecuteUser, ServerMaintenanceRecordRepairman, ServerMaintenanceRecord,
AlarmDataContinuityType, AlarmDataContinuity AlarmDataContinuityType, AlarmDataContinuity,
} = dc.models; } = dc.models;
AppInspection.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' }); AppInspection.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' });
@ -112,6 +112,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
MaintenanceRecordExecuteUser.belongsTo(MaintenanceRecord, { foreignKey: 'maintenanceRecordId', targetKey: 'id' }) MaintenanceRecordExecuteUser.belongsTo(MaintenanceRecord, { foreignKey: 'maintenanceRecordId', targetKey: 'id' })
MaintenanceRecord.hasMany(MaintenanceRecordExecuteUser, { foreignKey: 'maintenanceRecordId', targetKey: 'id' }) MaintenanceRecord.hasMany(MaintenanceRecordExecuteUser, { foreignKey: 'maintenanceRecordId', targetKey: 'id' })
MaintenanceRecord.hasMany(MaintenanceRecordExecuteUser, { foreignKey: 'maintenanceRecordId', targetKey: 'id' })
MaintenancePlanExecuteUser.belongsTo(MaintenancePlan, { foreignKey: 'maintenancePlanId', targetKey: 'id' }) MaintenancePlanExecuteUser.belongsTo(MaintenancePlan, { foreignKey: 'maintenancePlanId', targetKey: 'id' })
MaintenancePlan.hasMany(MaintenancePlanExecuteUser, { foreignKey: 'maintenancePlanId', targetKey: 'id' }) MaintenancePlan.hasMany(MaintenancePlanExecuteUser, { foreignKey: 'maintenancePlanId', targetKey: 'id' })

176
api/app/lib/models/maintenance_plan.js

@ -3,87 +3,97 @@
'use strict'; 'use strict';
module.exports = dc => { module.exports = dc => {
const DataTypes = dc.ORM; const DataTypes = dc.ORM;
const sequelize = dc.orm; const sequelize = dc.orm;
const MaintenancePlan = sequelize.define("maintenancePlan", { const MaintenancePlan = sequelize.define("maintenancePlan", {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: null, defaultValue: null,
comment: null, comment: null,
primaryKey: true, primaryKey: true,
field: "id", field: "id",
autoIncrement: true, autoIncrement: true,
unique: "maintenance_plan_id_uindex" unique: "maintenance_plan_id_uindex"
}, },
missionName: { missionName: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
defaultValue: null, defaultValue: null,
comment: "任务名称", comment: "任务名称",
primaryKey: false, primaryKey: false,
field: "mission_name", field: "mission_name",
autoIncrement: false autoIncrement: false
}, },
remark: { remark: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "备注", comment: "备注",
primaryKey: false, primaryKey: false,
field: "remark", field: "remark",
autoIncrement: false autoIncrement: false
}, },
reason: { reason: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "操作/故障原因", comment: "操作/故障原因",
primaryKey: false, primaryKey: false,
field: "reason", field: "reason",
autoIncrement: false autoIncrement: false
}, },
planFinishTime: { planFinishTime: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "计划完成时间", comment: "计划完成时间",
primaryKey: false, primaryKey: false,
field: "plan_finish_time", field: "plan_finish_time",
autoIncrement: false autoIncrement: false
}, },
actualFinishTime: { actualFinishTime: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "实际完成时间\n", comment: "实际完成时间\n",
primaryKey: false, primaryKey: false,
field: "actual_finish_time", field: "actual_finish_time",
autoIncrement: false autoIncrement: false
}, },
type: { type: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
defaultValue: null, defaultValue: null,
comment: "分类 period 周期 / temp 临时", comment: "分类 period 周期 / temp 临时",
primaryKey: false, primaryKey: false,
field: "type", field: "type",
autoIncrement: false autoIncrement: false
}, },
state: { state: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "完成状态 unfinished 未完成 / underway 进行中 / completed 已完成 / suspend 挂起暂停 / inspected 已检查", comment: "完成状态 unfinished 未完成 / underway 进行中 / completed 已完成 / suspend 挂起暂停 / inspected 已检查",
primaryKey: false, primaryKey: false,
field: "state", field: "state",
autoIncrement: false autoIncrement: false
} },
}, { recordId: {
tableName: "maintenance_plan", type: DataTypes.ARRAY(DataTypes.INTEGER),
comment: "", allowNull: true,
indexes: [] defaultValue: null,
}); comment: "响应记录id",
dc.models.MaintenancePlan = MaintenancePlan; primaryKey: false,
return MaintenancePlan; field: "record_id",
autoIncrement: false
}
}, {
tableName: "maintenance_plan",
comment: "",
indexes: []
});
dc.models.MaintenancePlan = MaintenancePlan;
return MaintenancePlan;
}; };

157
api/app/lib/models/maintenance_record.js

@ -3,78 +3,87 @@
'use strict'; 'use strict';
module.exports = dc => { module.exports = dc => {
const DataTypes = dc.ORM; const DataTypes = dc.ORM;
const sequelize = dc.orm; const sequelize = dc.orm;
const MaintenanceRecord = sequelize.define("maintenanceRecord", { const MaintenanceRecord = sequelize.define("maintenanceRecord", {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: null, defaultValue: null,
comment: null, comment: null,
primaryKey: true, primaryKey: true,
field: "id", field: "id",
autoIncrement: true, autoIncrement: true,
unique: "maintenance_record_id_uindex" unique: "maintenance_record_id_uindex"
}, },
sketch: { sketch: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
defaultValue: null, defaultValue: null,
comment: "简述", comment: "简述",
primaryKey: false, primaryKey: false,
field: "sketch", field: "sketch",
autoIncrement: false autoIncrement: false
}, },
occurrenceTime: { occurrenceTime: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "发生时间", comment: "发生时间",
primaryKey: false, primaryKey: false,
field: "occurrence_time", field: "occurrence_time",
autoIncrement: false autoIncrement: false
}, },
solvingTime: { solvingTime: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "解决时间", comment: "解决时间",
primaryKey: false, primaryKey: false,
field: "solving_time", field: "solving_time",
autoIncrement: false autoIncrement: false
}, },
interruptDuration: { interruptDuration: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "中断时长 / 秒", comment: "中断时长 / 秒",
primaryKey: false, primaryKey: false,
field: "interrupt_duration", field: "interrupt_duration",
autoIncrement: false autoIncrement: false
}, },
type: { type: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "故障类型", comment: "故障类型",
primaryKey: false, primaryKey: false,
field: "type", field: "type",
autoIncrement: false autoIncrement: false
}, },
record: { record: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
defaultValue: null, defaultValue: null,
comment: "故障记录", comment: "故障记录",
primaryKey: false, primaryKey: false,
field: "record", field: "record",
autoIncrement: false autoIncrement: false
} },
}, { files: {
tableName: "maintenance_record", type: DataTypes.JSONB,
comment: "", allowNull: true,
indexes: [] defaultValue: null,
}); comment: "文件",
dc.models.MaintenanceRecord = MaintenanceRecord; primaryKey: false,
return MaintenanceRecord; field: "files",
autoIncrement: false
},
}, {
tableName: "maintenance_record",
comment: "",
indexes: []
});
dc.models.MaintenanceRecord = MaintenanceRecord;
return MaintenanceRecord;
}; };

4
api/app/lib/routes/record/index.js

@ -12,4 +12,8 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['DEL/record/:id'] = { content: '删除服务记录', visible: true }; app.fs.api.logAttr['DEL/record/:id'] = { content: '删除服务记录', visible: true };
router.del('/record/:id', record.delRecord); router.del('/record/:id', record.delRecord);
app.fs.api.logAttr['GET/respond-record'] = { content: '获取响应记录数据', visible: true };
router.get('/respond-record', record.respondRecord);
}; };

14
script/0.29/schema/update_maintenance_plan.sql

@ -0,0 +1,14 @@
alter table maintenance_plan
add record_id integer[];
comment on column maintenance_plan.record_id is '响应记录id';
alter table maintenance_record
add files jsonb;
comment on column maintenance_record.files is '文件';

84
web/client/src/sections/means/containers/devOpsStandard.jsx

@ -3,12 +3,15 @@ import { connect } from 'react-redux';
import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui'; import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui';
import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons'; import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons';
import SimpleBar from 'simplebar-react'; import SimpleBar from 'simplebar-react';
import PerfectScrollbar from "perfect-scrollbar";
import FileModal from '../components/fileModal'; import FileModal from '../components/fileModal';
import moment from 'moment'; import moment from 'moment';
import './style.less' import './style.less'
let itemList
let folderList
let tableList
const Rest = (props) => { const Rest = (props) => {
const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId, apiRoot } = props const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId, apiRoot } = props
const { install, means } = actions const { install, means } = actions
@ -44,8 +47,42 @@ const Rest = (props) => {
})) }))
// setPepProjectId(data[0]?.pepProjectId) // setPepProjectId(data[0]?.pepProjectId)
fileList(null) fileList(null)
const domItem = document.getElementById("itemList");
if (domItem) {
itemList = new PerfectScrollbar("#itemList", {
suppressScrollX: true,
});
}
const domFolder = document.getElementById("folderList");
if (domFolder) {
folderList = new PerfectScrollbar("#folderList", {
suppressScrollX: true,
});
}
const domTable = document.getElementById("tableList");
if (domTable) {
tableList = new PerfectScrollbar("#tableList", {
suppressScrollX: true,
});
}
}, []) }, [])
useEffect(() => {
const domItem = document.getElementById("itemList");
if (domItem && itemList) {
itemList.update();
}
const domFolder = document.getElementById("folderList");
if (domFolder && folderList) {
folderList.update();
}
const domTable = document.getElementById("tableList");
if (domTable && tableList) {
tableList.update();
}
})
useEffect(() => { useEffect(() => {
let data let data
if (overallProjectId) { if (overallProjectId) {
@ -236,29 +273,6 @@ const Rest = (props) => {
} }
}, },
] ]
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
const preview = (url) => { const preview = (url) => {
let link = encodeURI(`${qiniu}/${url}`) let link = encodeURI(`${qiniu}/${url}`)
@ -278,7 +292,8 @@ const Rest = (props) => {
<div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}> <div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}>
<Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} /> <Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} />
<SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > {/* <SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > */}
<div id='itemList' style={{ height: 'calc(100% - 24px', }}>
<div style={{ cursor: 'pointer', background: pepProjectId == null ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }} <div style={{ cursor: 'pointer', background: pepProjectId == null ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }}
onClick={() => { onClick={() => {
setPepProjectId(null) setPepProjectId(null)
@ -302,7 +317,9 @@ const Rest = (props) => {
</div> </div>
})} })}
</SimpleBar> </div>
{/* </SimpleBar> */}
</div> </div>
<div style={{ height: '100%', display: 'flex', flex: 1 }}> <div style={{ height: '100%', display: 'flex', flex: 1 }}>
@ -330,7 +347,8 @@ const Rest = (props) => {
}} }}
/> : "" /> : ""
} }
<SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > {/* <SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > */}
<div id='folderList' style={{ height: 'calc(100% - 70px', }}>
<Tree <Tree
treeData={treeData} treeData={treeData}
defaultExpandAll defaultExpandAll
@ -341,7 +359,10 @@ const Rest = (props) => {
setFileSearch('') setFileSearch('')
}} }}
/> />
</SimpleBar> </div>
{/* </SimpleBar> */}
</div> </div>
{/* 表格 */} {/* 表格 */}
@ -428,7 +449,9 @@ const Rest = (props) => {
} }
</div> </div>
<SimpleBar style={{ height: 'calc(100% - 100px' }} forceVisible='y' > {/* <SimpleBar style={{ height: 'calc(100% - 100px' }} forceVisible='y' > */}
<div id='tableList' style={{ height: 'calc(100% - 100px', }}>
<Table <Table
style={{ width: 'calc(100% - 10px)', marginLeft: 10 }} style={{ width: 'calc(100% - 10px)', marginLeft: 10 }}
columns={column} columns={column}
@ -463,7 +486,8 @@ const Rest = (props) => {
// }, // },
// }} // }}
/> />
</SimpleBar> </div>
{/* </SimpleBar> */}
{count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}> {count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}>
<span style={{ lineHeight: "30px", fontSize: 13 }}> <span style={{ lineHeight: "30px", fontSize: 13 }}>

60
web/client/src/sections/means/containers/faultInformation.jsx

@ -3,12 +3,15 @@ import { connect } from 'react-redux';
import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui'; import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui';
import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons'; import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons';
import SimpleBar from 'simplebar-react'; import SimpleBar from 'simplebar-react';
import PerfectScrollbar from "perfect-scrollbar";
import FileModal from '../components/fileModal'; import FileModal from '../components/fileModal';
import moment from 'moment'; import moment from 'moment';
import './style.less' import './style.less'
let itemList
let folderList
let tableList
const Rest = (props) => { const Rest = (props) => {
const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId, apiRoot } = props const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId, apiRoot } = props
const { install, means } = actions const { install, means } = actions
@ -43,8 +46,42 @@ const Rest = (props) => {
})) }))
// setPepProjectId(data[0]?.pepProjectId) // setPepProjectId(data[0]?.pepProjectId)
fileList(null) fileList(null)
const domItem = document.getElementById("itemList");
if (domItem) {
itemList = new PerfectScrollbar("#itemList", {
suppressScrollX: true,
});
}
const domFolder = document.getElementById("folderList");
if (domFolder) {
folderList = new PerfectScrollbar("#folderList", {
suppressScrollX: true,
});
}
const domTable = document.getElementById("tableList");
if (domTable) {
tableList = new PerfectScrollbar("#tableList", {
suppressScrollX: true,
});
}
}, []) }, [])
useEffect(() => {
const domItem = document.getElementById("itemList");
if (domItem && itemList) {
itemList.update();
}
const domFolder = document.getElementById("folderList");
if (domFolder && folderList) {
folderList.update();
}
const domTable = document.getElementById("tableList");
if (domTable && tableList) {
tableList.update();
}
})
useEffect(() => { useEffect(() => {
let data let data
if (overallProjectId) { if (overallProjectId) {
@ -277,7 +314,9 @@ const Rest = (props) => {
<div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}> <div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}>
<Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} /> <Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} />
<SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > {/* <SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > */}
<div id='itemList' style={{ height: 'calc(100% - 24px', }}>
<div style={{ cursor: 'pointer', background: pepProjectId == null ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }} <div style={{ cursor: 'pointer', background: pepProjectId == null ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }}
onClick={() => { onClick={() => {
setPepProjectId(null) setPepProjectId(null)
@ -301,7 +340,8 @@ const Rest = (props) => {
</div> </div>
})} })}
</SimpleBar> {/* </SimpleBar> */}
</div>
</div> </div>
<div style={{ height: '100%', display: 'flex', flex: 1 }}> <div style={{ height: '100%', display: 'flex', flex: 1 }}>
@ -329,7 +369,9 @@ const Rest = (props) => {
}} }}
/> : "" /> : ""
} }
<SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > {/* <SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > */}
<div id='folderList' style={{ height: 'calc(100% - 70px', }}>
<Tree <Tree
treeData={treeData} treeData={treeData}
defaultExpandAll defaultExpandAll
@ -340,8 +382,9 @@ const Rest = (props) => {
setFileSearch('') setFileSearch('')
}} }}
/> />
</SimpleBar> {/* </SimpleBar> */}
</div>
</div> </div>
{/* 表格 */} {/* 表格 */}
<div style={{ <div style={{
@ -427,7 +470,9 @@ const Rest = (props) => {
} }
</div> </div>
<SimpleBar style={{ height: 'calc(100% - 100px' }} forceVisible='y' > {/* <SimpleBar style={{ height: 'calc(100% - 100px' }} forceVisible='y' > */}
<div id='tableList' style={{ height: 'calc(100% - 100px', }}>
<Table <Table
style={{ width: 'calc(100% - 10px)', marginLeft: 10 }} style={{ width: 'calc(100% - 10px)', marginLeft: 10 }}
columns={column} columns={column}
@ -462,7 +507,8 @@ const Rest = (props) => {
// }, // },
// }} // }}
/> />
</SimpleBar> {/* </SimpleBar> */}
</div>
{count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}> {count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}>
<span style={{ lineHeight: "30px", fontSize: 13 }}> <span style={{ lineHeight: "30px", fontSize: 13 }}>

65
web/client/src/sections/means/containers/projectMeans.jsx

@ -3,14 +3,17 @@ import { connect } from 'react-redux';
import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui'; import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui';
import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons'; import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons';
import SimpleBar from 'simplebar-react'; import SimpleBar from 'simplebar-react';
import PerfectScrollbar from "perfect-scrollbar";
import FileModal from '../components/fileModal'; import FileModal from '../components/fileModal';
import moment from 'moment'; import moment from 'moment';
import './style.less' import './style.less'
let itemList
let folderList
let tableList
const Rest = (props) => { const Rest = (props) => {
const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId ,apiRoot} = props const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId, apiRoot } = props
const { install, means } = actions const { install, means } = actions
const [pomsList, setPomsList] = useState([]); // const [pomsList, setPomsList] = useState([]); //
const [showPomsList, setShowPomsList] = useState([]); // const [showPomsList, setShowPomsList] = useState([]); //
@ -44,8 +47,43 @@ const Rest = (props) => {
} }
})) }))
fileList(null)
const domItem = document.getElementById("itemList");
if (domItem) {
itemList = new PerfectScrollbar("#itemList", {
suppressScrollX: true,
});
}
const domFolder = document.getElementById("folderList");
if (domFolder) {
folderList = new PerfectScrollbar("#folderList", {
suppressScrollX: true,
});
}
const domTable = document.getElementById("tableList");
if (domTable) {
tableList = new PerfectScrollbar("#tableList", {
suppressScrollX: true,
});
}
}, []) }, [])
useEffect(() => {
const domItem = document.getElementById("itemList");
if (domItem && itemList) {
itemList.update();
}
const domFolder = document.getElementById("folderList");
if (domFolder && folderList) {
folderList.update();
}
const domTable = document.getElementById("tableList");
if (domTable && tableList) {
tableList.update();
}
})
useEffect(() => { useEffect(() => {
let data let data
if (overallProjectId) { if (overallProjectId) {
@ -278,7 +316,8 @@ const Rest = (props) => {
<div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}> <div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}>
<Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} /> <Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} />
<SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > {/* <SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > */}
<div id='itemList' style={{ height: 'calc(100% - 24px', }}>
{showPomsList?.map(v => { {showPomsList?.map(v => {
return <div key={'pepProjectId' + v.pepProjectId} title={v.pepProjectName} style={{ cursor: 'pointer', background: v.pepProjectId == pepProjectId ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }} return <div key={'pepProjectId' + v.pepProjectId} title={v.pepProjectName} style={{ cursor: 'pointer', background: v.pepProjectId == pepProjectId ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }}
onClick={() => { onClick={() => {
@ -292,7 +331,8 @@ const Rest = (props) => {
</div> </div>
})} })}
</SimpleBar> {/* </SimpleBar> */}
</div>
</div> </div>
<div style={{ height: '100%', display: 'flex', flex: 1 }}> <div style={{ height: '100%', display: 'flex', flex: 1 }}>
@ -320,7 +360,9 @@ const Rest = (props) => {
}} }}
/> : "" /> : ""
} }
<SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > {/* <SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > */}
<div id='folderList' style={{ height: 'calc(100% - 70px', }}>
<Tree <Tree
treeData={treeData} treeData={treeData}
defaultExpandAll defaultExpandAll
@ -331,8 +373,8 @@ const Rest = (props) => {
setFileSearch('') setFileSearch('')
}} }}
/> />
</SimpleBar> {/* </SimpleBar> */}
</div>
</div> </div>
{/* 表格 */} {/* 表格 */}
<div style={{ <div style={{
@ -418,7 +460,9 @@ const Rest = (props) => {
} }
</div> </div>
<SimpleBar style={{ height: 'calc(100% - 100px' }} forceVisible='y' > {/* <SimpleBar className='SimpleBar' style={{ height: 'calc(100% - 100px', border: 0 }} forceVisible='y' > */}
<div id='tableList' style={{ height: 'calc(100% - 100px', border: 0 }}>
<Table <Table
style={{ width: 'calc(100% - 10px)', marginLeft: 10 }} style={{ width: 'calc(100% - 10px)', marginLeft: 10 }}
columns={column} columns={column}
@ -453,7 +497,8 @@ const Rest = (props) => {
// }, // },
// }} // }}
/> />
</SimpleBar> </div>
{/* </SimpleBar> */}
{count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}> {count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}>
<span style={{ lineHeight: "30px", fontSize: 13 }}> <span style={{ lineHeight: "30px", fontSize: 13 }}>
@ -559,7 +604,7 @@ function mapStateToProps (state) {
// socket: webSocket.socket // socket: webSocket.socket
clientHeight: global.clientHeight, clientHeight: global.clientHeight,
qiniu: global.qiniu?.domain, qiniu: global.qiniu?.domain,
apiRoot:global.apiRoot apiRoot: global.apiRoot
}; };
} }

61
web/client/src/sections/means/containers/repairFQA.jsx

@ -3,12 +3,15 @@ import { connect } from 'react-redux';
import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui'; import { Input, Button, Tree, Modal, Table, Upload, Pagination, Popconfirm } from '@douyinfe/semi-ui';
import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons'; import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons';
import SimpleBar from 'simplebar-react'; import SimpleBar from 'simplebar-react';
import PerfectScrollbar from "perfect-scrollbar";
import FileModal from '../components/fileModal'; import FileModal from '../components/fileModal';
import moment from 'moment'; import moment from 'moment';
import './style.less' import './style.less'
let itemList
let folderList
let tableList
const Rest = (props) => { const Rest = (props) => {
const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId, apiRoot } = props const { dispatch, actions, user, qiniu, loading, clientHeight, overallProjectId, apiRoot } = props
const { install, means } = actions const { install, means } = actions
@ -43,8 +46,42 @@ const Rest = (props) => {
})) }))
// setPepProjectId(data[0]?.pepProjectId) // setPepProjectId(data[0]?.pepProjectId)
fileList(null) fileList(null)
const domItem = document.getElementById("itemList");
if (domItem) {
itemList = new PerfectScrollbar("#itemList", {
suppressScrollX: true,
});
}
const domFolder = document.getElementById("folderList");
if (domFolder) {
folderList = new PerfectScrollbar("#folderList", {
suppressScrollX: true,
});
}
const domTable = document.getElementById("tableList");
if (domTable) {
tableList = new PerfectScrollbar("#tableList", {
suppressScrollX: true,
});
}
}, []) }, [])
useEffect(() => {
const domItem = document.getElementById("itemList");
if (domItem && itemList) {
itemList.update();
}
const domFolder = document.getElementById("folderList");
if (domFolder && folderList) {
folderList.update();
}
const domTable = document.getElementById("tableList");
if (domTable && tableList) {
tableList.update();
}
})
useEffect(() => { useEffect(() => {
let data let data
if (overallProjectId) { if (overallProjectId) {
@ -277,7 +314,9 @@ const Rest = (props) => {
<div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}> <div style={{ width: 200, height: '100%', padding: '16px 10px', boxShadow: '0 0 4px 2px #0000000d' }}>
<Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} /> <Input placeholder='请输入项目名称' value={projectSearch} onChange={v => setProjectSearch(v)} />
<SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > {/* <SimpleBar style={{ height: 'calc(100% - 24px', }} forceVisible="y" > */}
<div id='itemList' style={{ height: 'calc(100% - 24px', }}>
<div style={{ cursor: 'pointer', background: pepProjectId == null ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }} <div style={{ cursor: 'pointer', background: pepProjectId == null ? 'rgb(15 126 251 / 16%)' : '', width: 180, height: 30, display: 'flex', alignItems: 'center' }}
onClick={() => { onClick={() => {
setPepProjectId(null) setPepProjectId(null)
@ -302,7 +341,8 @@ const Rest = (props) => {
</div> </div>
})} })}
</SimpleBar> {/* </SimpleBar> */}
</div>
</div> </div>
<div style={{ height: '100%', display: 'flex', flex: 1 }}> <div style={{ height: '100%', display: 'flex', flex: 1 }}>
@ -330,7 +370,9 @@ const Rest = (props) => {
}} }}
/> : "" /> : ""
} }
<SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > {/* <SimpleBar style={{ height: 'calc(100% - 70px' }} forceVisible='y' > */}
<div id='folderList' style={{ height: 'calc(100% - 70px', }}>
<Tree <Tree
treeData={treeData} treeData={treeData}
defaultExpandAll defaultExpandAll
@ -341,8 +383,8 @@ const Rest = (props) => {
setFileSearch('') setFileSearch('')
}} }}
/> />
</SimpleBar> {/* </SimpleBar> */}
</div>
</div> </div>
{/* 表格 */} {/* 表格 */}
<div style={{ <div style={{
@ -428,7 +470,9 @@ const Rest = (props) => {
} }
</div> </div>
<SimpleBar style={{ height: 'calc(100% - 100px' }} forceVisible='y' > {/* <SimpleBar style={{ height: 'calc(100% - 100px' }} forceVisible='y' > */}
<div id='tableList' style={{ height: 'calc(100% - 100px', }}>
<Table <Table
style={{ width: 'calc(100% - 10px)', marginLeft: 10 }} style={{ width: 'calc(100% - 10px)', marginLeft: 10 }}
columns={column} columns={column}
@ -463,7 +507,8 @@ const Rest = (props) => {
// }, // },
// }} // }}
/> />
</SimpleBar> </div>
{/* </SimpleBar> */}
{count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}> {count > 0 ? <div style={{ display: 'flex', width: '100%', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}>
<span style={{ lineHeight: "30px", fontSize: 13 }}> <span style={{ lineHeight: "30px", fontSize: 13 }}>

24
web/client/src/sections/means/nav-item.jsx

@ -10,36 +10,36 @@ export function getNavItem (user, dispatch) {
icon: <IconCode />, icon: <IconCode />,
items: [ items: [
{ {
itemKey: 'projectMeans', itemKey: 'project',
text: '项目资料', text: '项目资料',
icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>, icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>,
to: '/means/projectMeans/projectMeans1', to: '/means/project/projectMeans',
items: [{ items: [{
itemKey: 'projectMeans1', to: '/means/projectMeans/projectMeans1', text: '项目资料' itemKey: 'projectMeans', to: '/means/project/projectMeans', text: '项目资料'
}] }]
}, { }, {
itemKey: 'repairFQA', itemKey: 'repair',
text: '维修FAQ', text: '维修FAQ',
icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>, icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>,
to: '/means/repairFQA/repairFQA1', to: '/means/repair/repairFQA',
items: [{ items: [{
itemKey: 'repairFQA1', to: '/means/repairFQA/repairFQA1', text: '维修FAQ' itemKey: 'repairFQA', to: '/means/repair/repairFQA', text: '维修FAQ'
}] }]
}, { }, {
itemKey: 'faultInformation', itemKey: 'fault',
text: '故障资料', text: '故障资料',
icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>, icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>,
to: '/means/faultInformation/faultInformation1', to: '/means/fault/faultInformation',
items: [{ items: [{
itemKey: 'faultInformation1', to: '/means/faultInformation/faultInformation1', text: '故障资料' itemKey: 'faultInformation', to: '/means/fault/faultInformation', text: '故障资料'
}] }]
}, { }, {
itemKey: 'devOpsStandard', itemKey: 'standard',
text: '运维规范', text: '运维规范',
icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>, icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconjianshezhong"></iconpark-icon>,
to: '/means/devOpsStandard/devOpsStandard1', to: '/means/standard/devOpsStandard',
items: [{ items: [{
itemKey: 'devOpsStandard1', to: '/means/devOpsStandard/devOpsStandard1', text: '运维规范' itemKey: 'devOpsStandard', to: '/means/standard/devOpsStandard', text: '运维规范'
}] }]
} }
] ]

32
web/client/src/sections/means/routes.js

@ -8,42 +8,42 @@ export default [{
breadcrumb: '资料', breadcrumb: '资料',
// 不设置 component 则面包屑禁止跳转 // 不设置 component 则面包屑禁止跳转
childRoutes: [{ childRoutes: [{
path: '/projectMeans', path: '/project',
key: 'projectMeans', key: 'project',
breadcrumb: '项目资料', breadcrumb: '项目资料',
childRoutes: [{ childRoutes: [{
path: '/projectMeans1', path: '/projectMeans',
key: 'projectMeans1', key: 'projectMeans',
component: ProjectMeans, component: ProjectMeans,
breadcrumb: '项目资料', breadcrumb: '项目资料',
}] }]
}, { }, {
path: '/repairFQA', path: '/repair',
key: 'repairFQA', key: 'repair',
breadcrumb: '维修FAQ', breadcrumb: '维修FAQ',
childRoutes: [{ childRoutes: [{
path: '/repairFQA1', path: '/repairFQA',
key: 'repairFQA1', key: 'repairFQA',
component: RepairFQA, component: RepairFQA,
breadcrumb: '维修FAQ', breadcrumb: '维修FAQ',
}] }]
}, { }, {
path: '/faultInformation', path: '/fault',
key: 'faultInformation', key: 'fault',
breadcrumb: '故障资料', breadcrumb: '故障资料',
childRoutes: [{ childRoutes: [{
path: '/faultInformation1', path: '/faultInformation',
key: 'faultInformation1', key: 'faultInformation',
component: FaultInformation, component: FaultInformation,
breadcrumb: '故障资料', breadcrumb: '故障资料',
}] }]
}, { }, {
path: '/devOpsStandard', path: '/standard',
key: 'devOpsStandard', key: 'standard',
breadcrumb: '运维规范', breadcrumb: '运维规范',
childRoutes: [{ childRoutes: [{
path: '/devOpsStandard1', path: '/devOpsStandard',
key: 'devOpsStandard1', key: 'devOpsStandard',
component: DevOpsStandard, component: DevOpsStandard,
breadcrumb: '运维规范', breadcrumb: '运维规范',
}] }]

2
web/client/src/sections/means/style.less

@ -5,3 +5,5 @@
#example:hover { #example:hover {
color: yellowgreen; color: yellowgreen;
} }

18
web/client/src/sections/service/actions/record.js

@ -17,6 +17,16 @@ export function getRecord(query) { //获取服务记录
}); });
} }
export function respondRecord() { //获取服务记录
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_RESPOND_RECORD',
url: `${ApiTable.respondRecord}`,
msg: { option: '获取响应记录数据' },
});
}
export function addRecord(query) { //新增服务记录和编辑 export function addRecord(query) { //新增服务记录和编辑
let msg = '' let msg = ''
@ -29,8 +39,11 @@ export function addRecord(query) { //新增服务记录和编辑
data: query, data: query,
actionType: 'ADD_RECORD', actionType: 'ADD_RECORD',
url: `${ApiTable.addRecord}`, url: `${ApiTable.addRecord}`,
msg: { option: msg } msg: { option: msg },
reducer: {
name: "addRecord",
params: { noClear: true }
}
}); });
} }
export function calculability(query) {//计算系统可用性 export function calculability(query) {//计算系统可用性
@ -60,3 +73,4 @@ export function delRecord(query) {//删除服务记录
}); });
} }

166
web/client/src/sections/service/components/cycAddmodal.jsx

@ -1,89 +1,107 @@
import React,{useState,useEffect,useRef} from 'react' import React, { useState, useEffect, useRef } from 'react'
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Button,Table,Modal,Form } from '@douyinfe/semi-ui'; import moment from 'moment'
import { Button, Table, Modal, Form } from '@douyinfe/semi-ui';
const AddModal=(props)=>{ const AddModal = (props) => {
const {visible,onClose,recordRow,pepList,actions,dispatch}=props const { visible, onClose, recordRow, pepList, actions, dispatch, respondRecordData } = props
const{service}=actions const { service } = actions
const api = useRef(); const api = useRef();
useEffect(()=>{ const [selectValue, setSelectValue] = useState([])
},[])
//
const okHandler=()=>{
//api.current.setValues({'manger':recordRow?.maintenancePlanExecuteUsers.map((item)=>{return item.id})},)
api.current.validate().then((res)=>{
res.manger
recordRow?.maintenancePlanExecuteUsers
const query={
id:recordRow?.id,
actualFinishTime:res.realityTime,
planFinishTime:res.planTime,
remark:res.notes,
state:res.status,
type:'period',
missionName:res.taskName,
manger:res.manger,
msg:recordRow?'编辑周期性计划':'添加周期性计划'
}
dispatch(service.editMaintenancePlan(query)).then((res)=>{
if(res.success) onClose() ; api.current.reset()
useEffect(() => {
}, [])
//
const okHandler = () => {
api.current.validate().then((res) => {
console.log(111, res, respondRecordData?.map(v => ({ value: v.id, label: v.sketch })));
const query = {
id: recordRow?.id,
actualFinishTime: res.realityTime,
planFinishTime: res.planTime,
remark: res.notes,
state: res.status,
type: 'period',
missionName: res.taskName,
manger: res.manger,
recordId: res.recordId,
msg: recordRow ? '编辑周期性计划' : '添加周期性计划'
}
dispatch(service.editMaintenancePlan(query)).then((res) => {
if (res.success) onClose(); api.current.reset()
})
}) })
}
return (<div>
<Modal visible={visible} onCancel={()=>{onClose()}} title={recordRow?'周期性计划编辑':'周期性计划添加'}
onOk={okHandler}
> })
<Form }
initValues={{'taskName':recordRow?.missionName, return (<div>
'manger':recordRow?.maintenancePlanExecuteUsers.map((item)=>{return item.pepUserId}), <Modal visible={visible} onCancel={() => { onClose() }} title={recordRow ? '周期性计划编辑' : '周期性计划添加'}
'reason':recordRow?.reason, onOk={okHandler}
'status':recordRow?.state,
'notes':recordRow?.remark,
'planTime':recordRow?.planFinishTime,
'realityTime':recordRow?.actualFinishTime}}
getFormApi={formApi => api.current = formApi} >
labelCol={{ span: 6 }} <Form
labelPosition='left' initValues={{
> 'taskName': recordRow?.missionName,
<Form.Input field='taskName' label='任务名称:' maxLength={30} rules={[ 'manger': recordRow?.maintenancePlanExecuteUsers.map((item) => { return item.pepUserId }),
{ required: true, message: '请输入任务名称' }, 'reason': recordRow?.reason,
]} ></Form.Input> 'status': recordRow?.state,
<Form.Select field='manger' label='责任人' rules={[{ required: true, message: '请输入责任人' }]} trigger='blur' style={{ width:'100%' }} 'notes': recordRow?.remark,
multiple filter> 'planTime': recordRow?.planFinishTime,
{pepList?.map((item)=>{return ( <Form.Select.OptGroup label={item.name}> 'realityTime': recordRow?.actualFinishTime,
{item.users.map((item1)=>{ 'recordId': recordRow?.recordId || [],
return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option> }}
})}
</Form.Select.OptGroup> )})}
</Form.Select>
<Form.Select label='完成情况'style={{ width: 200 }} field='status'>
<Form.Select.Option value='未完成'>未完成</Form.Select.Option>
<Form.Select.Option value='进行中'>进行中</Form.Select.Option>
<Form.Select.Option value='已完成'>已完成</Form.Select.Option>
<Form.Select.Option value='挂起'>挂起</Form.Select.Option>
</Form.Select>
<Form.TextArea label='备注' field='notes' placeholder='故障发生原因及解决方案'></Form.TextArea>
<Form.DatePicker label='计划完成时间:' field='planTime' rules={[{ required: true, message: '请选择计划完成时间' },]}></Form.DatePicker>
<Form.DatePicker label='实际完成时间:' field='realityTime'></Form.DatePicker>
</Form> getFormApi={formApi => api.current = formApi}
</Modal> labelCol={{ span: 6 }}
labelPosition='left'
>
<Form.Input field='taskName' label='任务名称:' maxLength={30} rules={[
{ required: true, message: '请输入任务名称' },
]} ></Form.Input>
<Form.Select field='manger' label='责任人' rules={[{ required: true, message: '请输入责任人' }]} trigger='blur' style={{ width: '100%' }}
multiple filter>
{pepList?.map((item) => {
return (<Form.Select.OptGroup label={item.name}>
{item.users.map((item1) => {
return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option>
})}
</Form.Select.OptGroup>)
})}
</Form.Select>
<Form.Select label='完成情况' style={{ width: 200 }} field='status'>
<Form.Select.Option value='未完成'>未完成</Form.Select.Option>
<Form.Select.Option value='进行中'>进行中</Form.Select.Option>
<Form.Select.Option value='已完成'>已完成</Form.Select.Option>
<Form.Select.Option value='挂起'>挂起</Form.Select.Option>
</Form.Select>
<Form.TextArea label='备注' field='notes' placeholder='故障发生原因及解决方案'></Form.TextArea>
<Form.DatePicker label='计划完成时间:' field='planTime' rules={[{ required: true, message: '请选择计划完成时间' },]}></Form.DatePicker>
<Form.DatePicker label='实际完成时间:' field='realityTime'></Form.DatePicker>
</div>) <Form.Select field='recordId' label='响应关联:' style={{ width: '100%' }}
dropdownStyle={{ width: 400 }}
// optionList={respondRecordData.map(v => ({ value: v.id, label: `${v.sketch} / ${moment(v.occurrenceTime).format('YYYY-MM-DD')}` }))}
multiple filter
>
{respondRecordData?.map((v) => {
return <Form.Select.Option value={v.id} label={`${v.sketch} / ${moment(v.occurrenceTime).format('YYYY-MM-DD')}`}>
</Form.Select.Option>
})}
</Form.Select>
</Form>
</Modal>
</div>)
} }
function mapStateToProps (state) { function mapStateToProps (state) {
const { global } = state; const { global } = state;
return { return {
actions: global.actions, actions: global.actions,
}; };
} }
export default connect(mapStateToProps)(AddModal) export default connect(mapStateToProps)(AddModal)

87
web/client/src/sections/service/components/planAddmodal.jsx

@ -0,0 +1,87 @@
import React, { useState, useEffect, useRef } from 'react'
import { connect } from 'react-redux';
import moment from 'moment'
import { Button, Table, Modal, Form } from '@douyinfe/semi-ui';
const PlanAddmodal = (props) => {
const { visible, onClose, recordRow, actions, dispatch } = props
const { service, install } = actions
const [pepList, setPepList] = useState([])//
const api = useRef();
const [selectValue, setSelectValue] = useState([])
useEffect(() => {
dispatch(install.getOrganizationDeps()).then((res) => {//(PEP)
setPepList(res.payload.data)
})
}, [])
//
const okHandler = () => {
api.current.validate().then((res) => {
const query = {
actualFinishTime: res.realityTime,
planFinishTime: res.planTime,
remark: res.notes,
state: res.status,
type: 'period',
missionName: res.taskName,
manger: res.manger,
recordId: [recordRow.id],
msg: '添加周期性计划'
}
dispatch(service.editMaintenancePlan(query)).then((res) => {
if (res.success) onClose(); api.current.reset()
})
})
}
return (<div>
<Modal visible={visible} onCancel={() => { onClose() }} title={'添加周期性计划'}
onOk={okHandler}
>
<Form
getFormApi={formApi => api.current = formApi}
labelCol={{ span: 6 }}
labelPosition='left'
>
<Form.Input field='taskName' label='任务名称:' maxLength={30} rules={[
{ required: true, message: '请输入任务名称' },
]} ></Form.Input>
<Form.Select field='manger' label='责任人' rules={[{ required: true, message: '请输入责任人' }]} trigger='blur' style={{ width: '100%' }}
multiple filter>
{pepList?.map((item) => {
return (<Form.Select.OptGroup label={item.name}>
{item.users.map((item1) => {
return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option>
})}
</Form.Select.OptGroup>)
})}
</Form.Select>
<Form.Select label='完成情况' style={{ width: 200 }} field='status'>
<Form.Select.Option value='未完成'>未完成</Form.Select.Option>
<Form.Select.Option value='进行中'>进行中</Form.Select.Option>
<Form.Select.Option value='已完成'>已完成</Form.Select.Option>
<Form.Select.Option value='挂起'>挂起</Form.Select.Option>
</Form.Select>
<Form.TextArea label='备注' field='notes' placeholder='故障发生原因及解决方案'></Form.TextArea>
<Form.DatePicker label='计划完成时间:' field='planTime' rules={[{ required: true, message: '请选择计划完成时间' },]}></Form.DatePicker>
<Form.DatePicker label='实际完成时间:' field='realityTime'></Form.DatePicker>
</Form>
</Modal>
</div>)
}
function mapStateToProps (state) {
const { global } = state;
return {
actions: global.actions,
};
}
export default connect(mapStateToProps)(PlanAddmodal)

278
web/client/src/sections/service/components/recordModal.jsx

@ -1,144 +1,178 @@
'use strict'; 'use strict';
import React, { useEffect,useState,useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import { Modal,Form,DatePicker,useFormApi,actions,Button } from '@douyinfe/semi-ui'; import { Modal, Form, DatePicker, Upload, actions, Button } from '@douyinfe/semi-ui';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import moment from 'moment' import moment from 'moment'
import { IconDeleteStroked, IconEditStroked, IconUpload, IconAlertCircle, IconSearch } from '@douyinfe/semi-icons';
const RecordModal =(props)=>{ const RecordModal = (props) => {
const{visible,onClose,dispatch,recordRow,pepList,actions}=props const { visible, onClose, dispatch, recordRow, pepList, actions, addRecord, qiniu, apiRoot } = props
const [startTime,setStartTime]=useState('') const [startTime, setStartTime] = useState('')
const [endTime,setEndTime]=useState('') const [endTime, setEndTime] = useState('')
const FormApi = useRef(); const [uploadData, setUploadData] = useState({})
const{service} =actions const FormApi = useRef();
// let t=0// const { service } = actions
// let h=0// // let t=0//
// let s=0// // let h=0//
// let s=0//
// console.log('endTimex',endTime) // console.log('endTimex',endTime)
useEffect(()=>{ useEffect(() => {
setEndTime(recordRow?.solvingTime) setEndTime(recordRow?.solvingTime)
setStartTime(recordRow?.occurrenceTime) setStartTime(recordRow?.occurrenceTime)
console.log('recordRow',recordRow) console.log('recordRow', recordRow)
},[recordRow]) }, [recordRow])
const cancelHandler=()=>{ const cancelHandler = () => {
onClose() onClose()
setStartTime('') setStartTime('')
setEndTime('') setEndTime('')
} }
const okHandler=()=>{ const okHandler = () => {
FormApi.current.validate().then((res)=>{ FormApi.current.validate().then((res) => {
console.log('recordRow',res) console.log('recordRow', res)
const editVal={ const editVal = {
id:recordRow?.id, id: recordRow?.id,
solvingTime:res.endTime, solvingTime: res.endTime,
occurrencTime:res.startTime, occurrencTime: res.startTime,
sketch:res.name, sketch: res.name,
record:res.record, record: res.record,
settler:res.settler, settler: res.settler,
type:res.type, type: res.type,
msg:recordRow?'编辑服务记录':'添加服务记录' msg: recordRow ? '编辑响应记录' : '添加响应记录',
} files: [{ ...uploadData }]
dispatch(service.addRecord(editVal)).then(res => { }
dispatch(service.addRecord(editVal)).then(res => {
if (res.success) { if (res.success) {
onClose() onClose()
FormApi.current.reset() FormApi.current.reset()
setStartTime('');setEndTime('') setStartTime(''); setEndTime('')
} }
}) })
}) })
} }
return <Modal return <Modal
title={recordRow?'编辑响应记录':'添加响应记录'} title={recordRow ? '编辑响应记录' : '添加响应记录'}
visible={visible} visible={visible}
footer={ footer={
//recordRow?<Button onClick={cancelHandler}></Button>: //recordRow?<Button onClick={cancelHandler}></Button>:
<div> <div>
<Button onClick={cancelHandler}>取消</Button> <Button onClick={cancelHandler}>取消</Button>
<Button theme='solid' type='primary' onClick={okHandler}>确定</Button> <Button loading={addRecord} theme='solid' type='primary' onClick={okHandler}>确定</Button>
</div>} </div>}
onCancel={cancelHandler} onCancel={cancelHandler}
onOk={okHandler} // onOk={okHandler}
> >
<Form wrapperCol={{ span: 20 }} <Form wrapperCol={{ span: 20 }}
initValues={{'name':recordRow?.sketch, initValues={{
'startTime':recordRow?.occurrenceTime, 'name': recordRow?.sketch,
'endTime':recordRow?.solvingTime, 'startTime': recordRow?.occurrenceTime,
'settler':recordRow?.maintenanceRecordExecuteUsers.map((item)=>{return item.pepUserId}), 'endTime': recordRow?.solvingTime,
'type':recordRow?.type, 'settler': recordRow?.maintenanceRecordExecuteUsers.map((item) => { return item.pepUserId }),
'record':recordRow?.record, 'type': recordRow?.type,
'breakTime':recordRow?parseInt(recordRow.interruptDuration/60/60/24)+'天'+ 'record': recordRow?.record,
parseInt(recordRow.interruptDuration/60/60%24)+'时'+ 'breakTime': recordRow ? parseInt(recordRow.interruptDuration / 60 / 60 / 24) + '天' +
parseInt(recordRow.interruptDuration/60%60)+'分':'0天0时0秒' parseInt(recordRow.interruptDuration / 60 / 60 % 24) + '时' +
parseInt(recordRow.interruptDuration / 60 % 60) + '分' : '0天0时0秒'
}}
getFormApi={formApi => FormApi.current = formApi}
labelPosition='left'
labelAlign='right'>
<Form.Input field='name' label='故障简述:' trigger='blur'
placeholder='请输入故障简述' rules={[{ required: true, message: '请输入故障简述' }]} maxLength={30} />
<Form.DatePicker field='startTime' label='发生时间:' rules={[{ required: true, message: '请输入发生时间' }]}
type="dateTime" onChange={(e) => {
const seconds = (moment(endTime).format('x') - moment(e).format('x')) / 1000//
const tdd = e && endTime ? parseInt(seconds / 60 / 60 / 24) : 0//
const tdh = e && endTime ? parseInt(seconds / 60 / 60 % 24) : 0//
const tds = e && endTime ? parseInt(seconds / 60 % 60) : 0//
setStartTime(e)
FormApi.current.setValue('breakTime', `${tdd}${tdh}${tds}`)
}} }}
getFormApi={formApi => FormApi.current = formApi} />
labelPosition='left' <Form.DatePicker field='endTime' label='解决时间:' initValue={endTime} rules={[{ required: true, message: '请输入解决时间' }]}
labelAlign='right'> type="dateTime" onChange={(e) => {
<Form.Input field='name' label='故障简述:' trigger='blur' const seconds = (moment(e).format('x') - moment(startTime).format('x')) / 1000//
const tdd = e && startTime ? parseInt(seconds / 60 / 60 / 24) : 0//
const tdh = e && startTime ? parseInt(seconds / 60 / 60 % 24) : 0//
const tds = e && startTime ? parseInt(seconds / 60 % 60) : 0//
setEndTime(e);//console.log('sss',moment(endTime-startTime).format('DDhhmm'))
FormApi.current.setValue('breakTime', `${tdd}${tdh}${tds}`)
placeholder='请输入故障简述' rules={[{ required: true, message:'请输入故障简述' }]} maxLength={30}/> }} />
<Form.DatePicker field='startTime' label='发生时间:' rules={[{ required: true, message:'请输入发生时间' }]} <Form.Input field='breakTime' label='中断时间:' disabled />
type="dateTime" onChange={(e)=>{ {/* 中断时间:{endTime&&startTime? <span style={{marginLeft:30}}>{`${tdd}天${tdh}时${tds}分`}</span>:recordRow?.interruptDuration} */}
const seconds=(moment(endTime).format('x')-moment(e).format('x'))/1000// <Form.Select field='settler' label='解决者:' trigger='blur' style={{ width: '100%' }}
const tdd=e&&endTime?parseInt(seconds/60/60/24):0// filter
const tdh=e&&endTime?parseInt(seconds/60/60%24):0// rules={[{ required: true, message: '请输入解决者' }]} multiple>
const tds=e&&endTime?parseInt(seconds/60%60):0// {pepList?.map((item) => {
setStartTime(e) return (<Form.Select.OptGroup label={item.name}>
FormApi.current.setValue('breakTime',`${tdd}${tdh}${tds}`) {item.users.map((item1) => {
}} return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option>
/>
<Form.DatePicker field='endTime' label='解决时间:' initValue={endTime} rules={[{ required: true, message:'请输入解决时间' }]}
type="dateTime" onChange={(e)=>{
const seconds=(moment(e).format('x')-moment(startTime).format('x'))/1000//
const tdd=e&&startTime?parseInt(seconds/60/60/24):0//
const tdh=e&&startTime?parseInt(seconds/60/60%24):0//
const tds=e&&startTime?parseInt(seconds/60%60):0//
setEndTime(e);//console.log('sss',moment(endTime-startTime).format('DDhhmm'))
FormApi.current.setValue('breakTime',`${tdd}${tdh}${tds}`)
}} /> })}
<Form.Input field='breakTime' label='中断时间:' disabled/> </Form.Select.OptGroup>)
{/* 中断时间:{endTime&&startTime? <span style={{marginLeft:30}}>{`${tdd}天${tdh}时${tds}分`}</span>:recordRow?.interruptDuration} */} })}
<Form.Select field='settler' label='解决者:' trigger='blur' style={{ width:'100%' }} </Form.Select>
filter <Form.Select field="type" label={{ text: '故障类型' }} style={{ width: 200 }} rules={[{ required: true, message: '请输入故障类型' }]}>
rules={[{ required: true, message:'请输入解决者' }]} multiple> <Form.Select.Option value="es异常">es异常</Form.Select.Option>
{pepList?.map((item)=>{return ( <Form.Select.OptGroup label={item.name}> <Form.Select.Option value="数据库异常">数据库异常</Form.Select.Option>
{item.users.map((item1)=>{ <Form.Select.Option value="应用异常">应用异常</Form.Select.Option>
return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option> <Form.Select.Option value="kafka异常">kafka异常</Form.Select.Option>
<Form.Select.Option value="服务器异常">服务器异常</Form.Select.Option>
<Form.Select.Option value="DAC进程异常">DAC进程异常</Form.Select.Option>
<Form.Select.Option value="K8S集群异常">K8S集群异常</Form.Select.Option>
<Form.Select.Option value="redis服务异常">redis服务异常</Form.Select.Option>
<Form.Select.Option value="其他">其他</Form.Select.Option>
</Form.Select>
<Form.TextArea field="record" label={{ text: '故障记录' }} rules={[{ required: true, message: '请输入故障记录' }]}>
</Form.TextArea>
})} <div style={{ display: 'flex', marginLeft: 30 }}>
</Form.Select.OptGroup> )})} <div style={{ marginTop: 6 }}>文件</div>
</Form.Select> <Upload
<Form.Select field="type" label={{ text: '故障类型'}} style={{ width: 200 }} rules={[{ required: true, message:'请输入故障类型' }]}> style={{ display: 'inline-block' }}
<Form.Select.Option value="es异常">es异常</Form.Select.Option> action={`${apiRoot}/attachments/p`}
<Form.Select.Option value="数据库异常">数据库异常</Form.Select.Option> accept={'.txt, .doc, .docx, .xls, .xlsx, .pdf, .png, .jpg, .rar, .zip'}
<Form.Select.Option value="应用异常">应用异常</Form.Select.Option> limit={1}
<Form.Select.Option value="kafka异常">kafka异常</Form.Select.Option> maxSize={51200}
<Form.Select.Option value="服务器异常">服务器异常</Form.Select.Option> onRemove={() => {
<Form.Select.Option value="DAC进程异常">DAC进程异常</Form.Select.Option> setUploadData({})
<Form.Select.Option value="K8S集群异常">K8S集群异常</Form.Select.Option> }}
<Form.Select.Option value="redis服务异常">redis服务异常</Form.Select.Option> onSuccess={(responseBody, file) => {
<Form.Select.Option value="其他">其他</Form.Select.Option> setUploadData({
</Form.Select> name: file.name,
<Form.TextArea field="record" label={{text:'故障记录'}} rules={[{ required: true, message:'请输入故障记录' }]}> size: file.size,
</Form.TextArea> url: responseBody?.uploaded,
uploadTime: moment().format("YYYY-MM-DD HH:mm:ss")
})
}}
>
<Button icon={<IconUpload />} theme="light">
文件上传
</Button>
</Upload>
</div>
</Form> </Form>
</Modal> </Modal>
} }
function mapStateToProps (state) { function mapStateToProps (state) {
const { auth, global, members, webSocket } = state; const { auth, global, members, webSocket, addRecord } = state;
return { console.log(addRecord);
// loading: members.isRequesting, return {
// user: auth.user, addRecord: addRecord.isRequesting,
actions: global.actions, // user: auth.user,
// members: members.data, actions: global.actions,
// socket: webSocket.socket qiniu: global.qiniu?.domain,
}; apiRoot: global.apiRoot
} };
}
export default connect(mapStateToProps)(RecordModal); export default connect(mapStateToProps)(RecordModal);

276
web/client/src/sections/service/containers/cyclePlan.jsx

@ -1,156 +1,162 @@
import React, { useEffect,useState} from 'react'; import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Button,Table,Popconfirm,Pagination } from '@douyinfe/semi-ui'; import { Button, Table, Popconfirm, Pagination } from '@douyinfe/semi-ui';
import Addmodal from '../components/cycAddmodal' import Addmodal from '../components/cycAddmodal'
import moment from 'moment' import moment from 'moment'
const Server = (props) => { const Server = (props) => {
const { dispatch, actions, user, loading, socket } = props const { dispatch, actions, user, loading, socket } = props
const{service,install}=actions const { service, install } = actions
const [addVis,setAddVis]=useState(false) const [addVis, setAddVis] = useState(false)
const [cycPlan,setCysPlan]=useState([]) const [cycPlan, setCysPlan] = useState([])
const [recordRow,setRecordRow]=useState(null) const [recordRow, setRecordRow] = useState(null)
const [pepList, setPepList] = useState([])// const [pepList, setPepList] = useState([])//
const [pageSize,setPageSize]=useState(10) const [pageSize, setPageSize] = useState(10)
const [pageIndex,setPageIndex]=useState(1) const [pageIndex, setPageIndex] = useState(1)
const [total,setTotal]=useState() const [total, setTotal] = useState()
const [respondRecordData, setRespondRecordData] = useState([])
const getCycPlan=(query={type:'period',msg:'获取周期性计划',pageIndex,pageSize})=>{ const getCycPlan = (query = { type: 'period', msg: '获取周期性计划', pageIndex, pageSize }) => {
dispatch(service.getMaintenancePlan(query)).then((res)=>{ dispatch(service.getMaintenancePlan(query)).then((res) => {
setCysPlan(res?.payload.data.responseRes) setCysPlan(res?.payload.data.responseRes)
setTotal(res?.payload.data.count) setTotal(res?.payload.data.count)
}) })
} }
useEffect(()=>{ useEffect(() => {
getCycPlan() getCycPlan()
dispatch(install.getOrganizationDeps()).then((res) => {//(PEP) dispatch(install.getOrganizationDeps()).then((res) => {//(PEP)
setPepList(res.payload.data) setPepList(res.payload.data)
}) })
},[]) dispatch(service.respondRecord({})).then((res) => {
const delHandler=(record)=>{ if (res.success) {
const query={ setRespondRecordData(res?.payload.data)
responseId:record.id, }
msg:'删除周期性计划' });
} }, [])
dispatch(service.delMaintenancePlan(query)).then((res)=>{ const delHandler = (record) => {
if(res.success) getCycPlan({type:'period',msg:'获取周期性计划',pageIndex:1,pageSize});setPageIndex(1) const query = {
}) responseId: record.id,
} msg: '删除周期性计划'
// }
// const pagination={ dispatch(service.delMaintenancePlan(query)).then((res) => {
// total:total, if (res.success) getCycPlan({ type: 'period', msg: '获取周期性计划', pageIndex: 1, pageSize }); setPageIndex(1)
// defaultCurrent: 1, })
// pageSize:pageSize, }
// showSizeChanger: true, //
// currentPage:pageIndex, // const pagination={
// showQuickJumper: true, // total:total,
// pageSizeOpts: ["5", "10", "15"], // defaultCurrent: 1,
// showTotal: function () { // pageSize:pageSize,
// return `${total}` // showSizeChanger: true,
// }, // currentPage:pageIndex,
// onChange:(pageIndex,pageSize)=>{ // showQuickJumper: true,
// console.log('pageIndex1',pageIndex,pageSize) // pageSizeOpts: ["5", "10", "15"],
// setPageIndex(pageIndex) // showTotal: function () {
// setPageSize(pageSize) // return `${total}`
// const query={ // },
// pageIndex,pageSize,type:'temp',msg:'' // onChange:(pageIndex,pageSize)=>{
// } // console.log('pageIndex1',pageIndex,pageSize)
// getCycPlan(query) // setPageIndex(pageIndex)
// } // setPageSize(pageSize)
// } // const query={
//console.log('cycPlan',cycPlan) // pageIndex,pageSize,type:'temp',msg:''
// }
// getCycPlan(query)
// }
// }
//console.log('cycPlan',cycPlan)
const columns = [ const columns = [
{ {
title: '序号', title: '序号',
render:(t, r, i) => { render: (t, r, i) => {
return i + 1 return i + 1
} }
}, },
{ {
title: '任务名称', title: '任务名称',
dataIndex: 'missionName', dataIndex: 'missionName',
}, },
{ {
title: '责任人', title: '责任人',
render:(record)=>{ render: (record) => {
return <span> return <span>
{record?.maintenancePlanExecuteUsers.map((item)=>{ {record?.maintenancePlanExecuteUsers.map((item) => {
return item.name return item.name
}).toString() }).toString()
} }
</span> </span>
} }
}, },
{ {
title: '完成情况', title: '完成情况',
dataIndex: 'state', dataIndex: 'state',
}, },
{ {
title: '备注', title: '备注',
dataIndex: 'remark', dataIndex: 'remark',
}, },
{ {
title: '计划完成时间', title: '计划完成时间',
render:(record)=>{ render: (record) => {
return <span>{moment(record.planFinishTime).format('YYYY-MM-DD')}</span> return <span>{moment(record.planFinishTime).format('YYYY-MM-DD')}</span>
}, },
}, },
{ {
title: '实际完成时间', title: '实际完成时间',
render:(record)=>{ render: (record) => {
return record.actualFinishTime?<span>{moment(record.actualFinishTime).format('YYYY-MM-DD')}</span>:'' return record.actualFinishTime ? <span>{moment(record.actualFinishTime).format('YYYY-MM-DD')}</span> : ''
}, },
}, },
{ {
title: '操作', title: '操作',
render:(record)=>{ render: (record) => {
return (<div> return (<div>
<Button onClick={()=>{setAddVis(true);setRecordRow(record)}} style={{marginRight:10}}>编辑</Button> <Button onClick={() => { setAddVis(true); setRecordRow(record) }} style={{ marginRight: 10 }}>编辑</Button>
<Popconfirm title="确定是否删除?" onConfirm={()=>{delHandler(record)}}><Button type='danger'> 删除</Button></Popconfirm> <Popconfirm title="确定是否删除?" onConfirm={() => { delHandler(record) }}><Button type='danger'> 删除</Button></Popconfirm>
</div>) </div>)
} }
}, },
]; ];
return ( return (
<div style={{background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px'}}> <div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}>
<div style={{marginBottom:20}}> <div style={{ marginBottom: 20 }}>
<Button theme='solid' type='secondary' onClick={()=>{setAddVis(true)}}>新增</Button> <Button theme='solid' type='secondary' onClick={() => { setAddVis(true) }}>新增</Button>
{/* <Button theme='solid' type='secondary' style={{marginLeft:50}}>导入</Button> */} {/* <Button theme='solid' type='secondary' style={{marginLeft:50}}>导入</Button> */}
</div> </div>
<div> <div>
<Table columns={columns} dataSource={cycPlan} pagination={false}></Table> <Table columns={columns} dataSource={cycPlan} pagination={false}></Table>
</div> </div>
<div style={{ display: 'flex',justifyContent:'flex-end' ,margin:'10px 0 0 0'}}> <div style={{ display: 'flex', justifyContent: 'flex-end', margin: '10px 0 0 0' }}>
<span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}> <span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}>
{total}条信息 {total}条信息
</span> </span>
<Pagination total={total} <Pagination total={total}
showSizeChanger showSizeChanger
pageSize={pageSize} pageSize={pageSize}
currentPage={pageIndex} currentPage={pageIndex}
pageSizeOpts={[10, 20, 30]} pageSizeOpts={[10, 20, 30]}
onChange={(pageIndex,pageSize)=>{ onChange={(pageIndex, pageSize) => {
console.log('pageIndex1',pageIndex,pageSize) console.log('pageIndex1', pageIndex, pageSize)
setPageIndex(pageIndex) setPageIndex(pageIndex)
setPageSize(pageSize) setPageSize(pageSize)
const query={ const query = {
pageIndex,pageSize,type:'period',msg:'获取周期性计划' pageIndex, pageSize, type: 'period', msg: '获取周期性计划'
} }
getCycPlan(query) getCycPlan(query)
}}></Pagination> }}></Pagination>
</div>
<Addmodal visible={addVis} respondRecordData={respondRecordData} onClose={() => { setAddVis(false); setRecordRow(null); getCycPlan() }} recordRow={recordRow} pepList={pepList}></Addmodal>
</div> </div>
<Addmodal visible={addVis} onClose={()=>{setAddVis(false);setRecordRow(null);getCycPlan()}} recordRow={recordRow} pepList={pepList}></Addmodal>
</div>
) )
} }
function mapStateToProps (state) { function mapStateToProps (state) {
const { global } = state; const { global } = state;
return { return {
actions: global.actions, actions: global.actions,
}; };
} }
export default connect(mapStateToProps)(Server); export default connect(mapStateToProps)(Server);

441
web/client/src/sections/service/containers/maintenanceRecords.jsx

@ -5,236 +5,237 @@ import MaintenanceRecordModal from '../components/maintenanceRecordModal';
import moment from 'moment'; import moment from 'moment';
const MaintenanceRecords = (props) => { const MaintenanceRecords = (props) => {
const { dispatch, actions, user, loading, socket, projectList } = props; const { dispatch, actions, user, loading, socket, projectList } = props;
const [addVis, setAddVis] = useState(false); const [addVis, setAddVis] = useState(false);
const [recordRow, setRecordRow] = useState(null); const [recordRow, setRecordRow] = useState(null);
const [equipmentList, setEquipmentList] = useState([]); const [equipmentList, setEquipmentList] = useState([]);
const [pepList, setPepList] = useState([]); const [pepList, setPepList] = useState([]);
const [pageSize, setPageSize] = useState(10); const [pageSize, setPageSize] = useState(10);
const [pageIndex, setPageIndex] = useState(1); const [pageIndex, setPageIndex] = useState(1);
const [startTime, setStartTime] = useState('1970-1-1'); const [startTime, setStartTime] = useState('1970-1-1');
const [endTime, setEndTime] = useState('2099-1-1'); const [endTime, setEndTime] = useState('2099-1-1');
const { install, service } = actions; const { install, service } = actions;
const [total,setTotal]=useState() const [total, setTotal] = useState()
const [projectId,setProjectId]=useState(null) const [projectId, setProjectId] = useState(null)
const getEquipment = (query = { startTime, endTime, pageIndex, pageSize }) => {
dispatch(service.getEquipment(query)).then((res) => {
// console.log()
if (res.success) setEquipmentList(res?.payload.data.result); setTotal(res?.payload.data.resCount)
}); const getEquipment = (query = { startTime, endTime, pageIndex, pageSize }) => {
}; dispatch(service.getEquipment(query)).then((res) => {
// console.log()
if (res.success) setEquipmentList(res?.payload.data.result); setTotal(res?.payload.data.resCount)
});
};
// console.log('equipmentList', equipmentList); // console.log('equipmentList', equipmentList);
const findHandler=()=>{ const findHandler = () => {
const query={ const query = {
pageIndex,pageSize,msg:'获取维护记录',startTime, endTime,projectId pageIndex, pageSize, msg: '获取维护记录', startTime, endTime, projectId
} }
console.log('canshu',projectId,startTime, endTime) console.log('canshu', projectId, startTime, endTime)
getEquipment(query) getEquipment(query)
} }
const timeHandler=(e)=>{ const timeHandler = (e) => {
setEndTime(e[1]+'') setEndTime(e[1] + '')
setStartTime(e[0]+'') setStartTime(e[0] + '')
} }
useEffect(() => { useEffect(() => {
dispatch(install.getOrganizationDeps()).then((res) => { dispatch(install.getOrganizationDeps()).then((res) => {
//(PEP) //(PEP)
setPepList(res.payload.data); setPepList(res.payload.data);
}); });
getEquipment(); getEquipment();
}, []) }, [])
const delHandler=(record)=>{ const delHandler = (record) => {
dispatch(service.delEquipment(record.id)).then((res)=>{ dispatch(service.delEquipment(record.id)).then((res) => {
if(res.success) getEquipment({ if (res.success) getEquipment({
pageIndex:1,pageSize,msg:'获取维护记录',startTime, endTime,projectId pageIndex: 1, pageSize, msg: '获取维护记录', startTime, endTime, projectId
});setPageIndex(1) }); setPageIndex(1)
}) })
} }
const columns = [ const columns = [
{ {
title: '序号', title: '序号',
render: (t, r, i) => { render: (t, r, i) => {
return i + 1; return i + 1;
} }
}, },
{ {
title: '项目名称', title: '项目名称',
render: (record) => { render: (record) => {
const currentId = record.equipmentMaintenanceRecordProjects?.map((item1) => { const currentId = record.equipmentMaintenanceRecordProjects?.map((item1) => {
return item1.projectId; return item1.projectId;
})[0]; })[0];
//const currnetObj = projectList?.find((item) => item.id === record?.equipmentMaintenanceRecordProjects?.projectId)||{}; //const currnetObj = projectList?.find((item) => item.id === record?.equipmentMaintenanceRecordProjects?.projectId)||{};
const currnetObj = projectList?.find((item) => item.id === currentId)||{}; const currnetObj = projectList?.find((item) => item.id === currentId) || {};
const projectName = currnetObj.name ? currnetObj.name : currnetObj.pepProjectName; const projectName = currnetObj.name ? currnetObj.name : currnetObj.pepProjectName;
return projectName?.length > 15 ? ( return projectName?.length > 15 ? (
<Tooltip content={<div>{projectName}</div>}> <Tooltip content={<div>{projectName}</div>}>
<div style={{}}>{projectName?.length > 15 ? `${projectName?.substr(0, 15)}...` : projectName}</div> <div style={{}}>{projectName?.length > 15 ? `${projectName?.substr(0, 15)}...` : projectName}</div>
</Tooltip> </Tooltip>
) : ( ) : (
projectName projectName
); );
} }
}, },
{ {
title: '设备类型', title: '设备类型',
dataIndex: 'equipmentCategory' dataIndex: 'equipmentCategory'
}, },
{ {
title: '设备型号', title: '设备型号',
dataIndex: 'equipmentType' dataIndex: 'equipmentType'
}, },
{ {
title: '维修原因', title: '维修原因',
dataIndex: 'maintenanceReason' dataIndex: 'maintenanceReason'
}, },
{ {
title: '解决方案', title: '解决方案',
dataIndex: 'solution' dataIndex: 'solution'
}, },
{ {
title: '维修人', title: '维修人',
render: (record) => { render: (record) => {
return ( return (
<span> <span>
{record?.equipmentMaintenanceRecordExecuteUsers {record?.equipmentMaintenanceRecordExecuteUsers
.map((item) => { .map((item) => {
return item.name; return item.name;
}) })
.toString()} .toString()}
</span> </span>
); );
} }
}, },
{ {
title: '上报时间', title: '上报时间',
render: (record) => { render: (record) => {
return <span>{moment(record.reportTime).format('YYYY-MM-DD HH:mm:ss')}</span>; return <span>{moment(record.reportTime).format('YYYY-MM-DD HH:mm:ss')}</span>;
} }
}, },
{ {
title: '维修完成时间', title: '维修完成时间',
render: (record) => { render: (record) => {
//('record',record.completedTime) //('record',record.completedTime)
return (record.completedTime?(<span>{moment(record.reportTime).format('YYYY-MM-DD HH:mm:ss')}</span>):'') return (record.completedTime ? (<span>{moment(record.reportTime).format('YYYY-MM-DD HH:mm:ss')}</span>) : '')
} }
}, },
{ {
title: '状态', title: '状态',
dataIndex: 'status' dataIndex: 'status'
}, },
{ {
title: '操作', title: '操作',
render: (record) => { render: (record) => {
return ( return (
<div> <div>
<Button <Button
style={{marginRight:10}} style={{ marginRight: 10 }}
onClick={() => { onClick={() => {
setAddVis(true); setAddVis(true);
setRecordRow(record); setRecordRow(record);
}} }}
> >
编辑 编辑
</Button> </Button>
<Popconfirm <Popconfirm
title='确定是否删除?' title='确定是否删除?'
onConfirm={() => { onConfirm={() => {
delHandler(record); delHandler(record);
}} }}
> >
<Button type='danger' > 删除</Button> <Button type='danger' > 删除</Button>
</Popconfirm> </Popconfirm>
</div> </div>
); );
} }
} }
]; ];
return ( return (
<div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}> <div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}>
<div style={{ display: 'flex', marginBottom: 20, alignItems: 'baseline' }}> <div style={{ display: 'flex', marginBottom: 20, alignItems: 'baseline' }}>
<div style={{ marginRight: 100 }}> <div style={{ marginRight: 100 }}>
{' '} {' '}
<Button <Button
theme='solid' theme='solid'
type='secondary' type='secondary'
onClick={() => { onClick={() => {
setAddVis(true); setAddVis(true);
}} }}
> >
新增 新增
</Button> </Button>
</div> </div>
<div style={{ marginRight: 200 }}> <div style={{ marginRight: 200 }}>
{' '} {' '}
<span style={{ marginRight: 10 }}>项目名称</span> <span style={{ marginRight: 10 }}>项目名称</span>
<Select <Select
showClear showClear
onChange={(e)=>{setProjectId(e)}} onChange={(e) => { setProjectId(e) }}
onClear={()=>{setProjectId(null)}} onClear={() => { setProjectId(null) }}
style={{ width: 150 }} style={{ width: 150 }}
optionList={projectList?.map((item) => { optionList={projectList?.map((item) => {
return { value: item.id, label: item.pepProjectName||item.name } return { value: item.id, label: item.pepProjectName || item.name }
})} })}
></Select> ></Select>
</div> </div>
<div style={{ marginRight: 20, display: 'flex', alignItems: 'baseline' }}> <div style={{ marginRight: 20, display: 'flex', alignItems: 'baseline' }}>
<span style={{ marginRight: 10 }}>上报时间</span> <span style={{ marginRight: 10 }}>上报时间</span>
<DatePicker type='dateTimeRange' onChange={(e)=>{timeHandler(e)}} onClear={()=>{setStartTime('1970-1-1');setEndTime('2099-12-31')}}></DatePicker> <DatePicker type='dateTimeRange' onChange={(e) => { timeHandler(e) }} onClear={() => { setStartTime('1970-1-1'); setEndTime('2099-12-31') }}></DatePicker>
</div>
<div>
<Button theme='solid' type="primary" onClick={findHandler}>查询</Button>
</div>
</div> </div>
<div> <div>
<Table columns={columns} dataSource={equipmentList} pagination={false}></Table> <Button theme='solid' type="primary" onClick={findHandler}>查询</Button>
</div> </div>
<div style={{ display: 'flex',justifyContent:'flex-end' ,margin:'10px 0 0 0'}}> </div>
<span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}> <div>
{total}条信息 <Table columns={columns} dataSource={equipmentList} pagination={false}></Table>
</span> </div>
<Pagination total={total} <div style={{ display: 'flex', justifyContent: 'flex-end', margin: '10px 0 0 0' }}>
showSizeChanger <span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}>
pageSize={pageSize} {total}条信息
currentPage={pageIndex} </span>
pageSizeOpts={[10, 20, 30]} <Pagination total={total}
onChange={(pageIndex,pageSize)=>{ showSizeChanger
//console.log('pageIndex1',pageIndex,pageSize) pageSize={pageSize}
setPageIndex(pageIndex) currentPage={pageIndex}
setPageSize(pageSize) pageSizeOpts={[10, 20, 30]}
const query={ onChange={(pageIndex, pageSize) => {
pageIndex,pageSize,msg:'获取维护记录',startTime, endTime,projectId //console.log('pageIndex1',pageIndex,pageSize)
} setPageIndex(pageIndex)
getEquipment(query) setPageSize(pageSize)
}}></Pagination> const query = {
pageIndex, pageSize, msg: '获取维护记录', startTime, endTime, projectId
}
getEquipment(query)
}}></Pagination>
</div>
<MaintenanceRecordModal
visible={addVis}
onClose={() => {
setAddVis(false);
setRecordRow(null);
//getEquipment();
findHandler()
}}
recordRow={recordRow}
pepList={pepList}
projectList={projectList}
></MaintenanceRecordModal>
</div> </div>
<MaintenanceRecordModal );
visible={addVis}
onClose={() => {
setAddVis(false);
setRecordRow(null);
//getEquipment();
findHandler()
}}
recordRow={recordRow}
pepList={pepList}
projectList={projectList}
></MaintenanceRecordModal>
</div>
);
}; };
function mapStateToProps(state) { function mapStateToProps (state) {
const { auth, global, members, webSocket, ProjectPoms } = state; const { auth, global, members, webSocket, ProjectPoms } = state;
return { return {
// loading: members.isRequesting, // loading: members.isRequesting,
// user: auth.user, // user: auth.user,
actions: global.actions, actions: global.actions,
// members: members.data, // members: members.data,
// socket: webSocket.socket // socket: webSocket.socket
projectList: ProjectPoms.data?.rows projectList: ProjectPoms.data?.rows
}; };
} }
export default connect(mapStateToProps)(MaintenanceRecords); export default connect(mapStateToProps)(MaintenanceRecords);

474
web/client/src/sections/service/containers/serviceRecord.jsx

@ -1,247 +1,361 @@
'use strict'; 'use strict';
import React, { useEffect,useState,useMemo } from 'react'; import React, { useEffect, useState, useMemo } from 'react';
import { Calendar,DatePicker,RadioGroup, Radio,Button,Table,Modal,Tooltip,Pagination, Popconfirm } from '@douyinfe/semi-ui'; import { Calendar, DatePicker, RadioGroup, Radio, Button, Table, Modal, Tooltip, Pagination, Popconfirm } from '@douyinfe/semi-ui';
import moment from 'moment' import moment from 'moment'
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import RecordModal from '../components/recordModal' import RecordModal from '../components/recordModal'
import PlanAddmodal from '../components/planAddmodal'
const Server = (props) => { const Server = (props) => {
const { dispatch, actions, user, loading, socket } = props const { dispatch, actions, user, loading, socket } = props
//console.log('actions',actions) //console.log('actions',actions)
const {install,service}=actions const { install, service } = actions
const [dateValue,setDateValue]=useState([]) const [dateValue, setDateValue] = useState([])
const [mode,setMode]=useState('month') const [mode, setMode] = useState('month')
const [modalVis,setModalVis]=useState(false) const [modalVis, setModalVis] = useState(false)
const [recordRow,setRecordRow]=useState(null) const [recordRow, setRecordRow] = useState(null)
const [recordList,setRecordList]=useState([]) const [recordList, setRecordList] = useState([])
const [pepList, setPepList] = useState([])// const [pepList, setPepList] = useState([])//
const [startTime,setStartTime]=useState('1970-1-1' ) const [startTime, setStartTime] = useState('1970-1-1')
const [endTime,setEndTime]=useState('2099-1-1') const [endTime, setEndTime] = useState('2099-1-1')
const [sTime,setStime]=useState(moment().startOf('month').format('YYYY-MM-DD HH:mm:ss')) const [sTime, setStime] = useState(moment().startOf('month').format('YYYY-MM-DD HH:mm:ss'))
const [eTime,setEtime]=useState(moment().endOf('month').format('YYYY-MM-DD HH:mm:ss')) const [eTime, setEtime] = useState(moment().endOf('month').format('YYYY-MM-DD HH:mm:ss'))
const [calculability,setCalculability]=useState('') const [calculability, setCalculability] = useState('')
const [pageSize,setPageSize]=useState(10) const [pageSize, setPageSize] = useState(10)
const [pageIndex,setPageIndex]=useState(1) const [pageIndex, setPageIndex] = useState(1)
const [total,setTotal]=useState() const [total, setTotal] = useState()
const getRecordList=(query={ const [addVis, setAddVis] = useState(false)
startTime,endTime,pageIndex,pageSize const [cycPlan, setCysPlan] = useState([])
})=>{ const [visible, setVisible] = useState(false)
dispatch(service.getRecord(query)).then((res)=>{
console.log('res1',res)
const getRecordList = (query = {
startTime, endTime, pageIndex, pageSize
}) => {
dispatch(service.getRecord(query)).then((res) => {
// console.log('res1',res)
setRecordList(res?.payload.data.res) setRecordList(res?.payload.data.res)
setTotal(res?.payload.data.count) setTotal(res?.payload.data.count)
}) })
} }
//('endTime',endTime) //('endTime',endTime)
useEffect(() => { useEffect(() => {
getRecordList() getRecordList()
dispatch(install.getOrganizationDeps()).then((res) => {//(PEP) dispatch(install.getOrganizationDeps()).then((res) => {//(PEP)
if(res.success) setPepList(res.payload.data) if (res.success) setPepList(res.payload.data)
}) })
}, []) }, [])
const delHandler=(id)=>{ const delHandler = (id) => {
dispatch(service.delRecord(id)).then((res)=>{ dispatch(service.delRecord(id)).then((res) => {
if(res.success) { if (res.success) {
getRecordList({startTime,endTime,pageIndex:1,pageSize}) getRecordList({ startTime, endTime, pageIndex: 1, pageSize })
setPageIndex(1) setPageIndex(1)
} }
}) })
} }
const getCycPlan = (recordId) => {
dispatch(service.getMaintenancePlan({ type: 'period', msg: '获取周期性计划', recordId: recordId })).then((res) => {
setCysPlan(res?.payload.data.responseRes)
})
}
useEffect(() => { useEffect(() => {
const query={ const query = {
sTime, eTime sTime, eTime
} }
console.log('sTime',sTime,eTime) console.log('sTime', sTime, eTime)
dispatch(service.calculability(query)).then((res)=>{ dispatch(service.calculability(query)).then((res) => {
if(res.success) setCalculability((Math.round(res.payload.data * 10000)) / 100 + '%'); // console.log(res.payload.data,'dateee') if (res.success) setCalculability((Math.round(res.payload.data * 10000)) / 100 + '%'); // console.log(res.payload.data,'dateee')
}) })
}, [sTime,eTime]) }, [sTime, eTime])
const pagination={ const pagination = {
} }
//console.log(recordList,'11111111') //console.log(recordList,'11111111')
const columns = [ const columns = [
{ {
title: '序号', title: '序号',
render:(t, r, i) => { render: (t, r, i) => {
return i + 1 return i + 1
} }
}, },
{ {
title: '故障描述', title: '故障描述',
dataIndex: 'sketch', dataIndex: 'sketch',
}, },
{ {
title: '发生时间', title: '发生时间',
render:(record)=>{ render: (record) => {
return <span>{moment(record.occurrenceTime).format('YYYY-MM-DD HH:mm:ss')}</span> return <span>{moment(record.occurrenceTime).format('YYYY-MM-DD HH:mm:ss')}</span>
}, },
}, },
{ {
title: '解决时间', title: '解决时间',
render:(record)=>{ render: (record) => {
return <span>{moment(record.solvingTime).format('YYYY-MM-DD HH:mm:ss')}</span> return <span>{moment(record.solvingTime).format('YYYY-MM-DD HH:mm:ss')}</span>
}, },
}, },
{ {
title: '恢复时间', title: '恢复时间',
render:(record)=>{ render: (record) => {
const tdd=parseInt((moment(record.solvingTime).format('x')-moment(record.occurrenceTime).format('x'))/1000/60/60/24)// const tdd = parseInt((moment(record.solvingTime).format('x') - moment(record.occurrenceTime).format('x')) / 1000 / 60 / 60 / 24)//
const tdh=parseInt((moment(record.solvingTime).format('x')-moment(record.occurrenceTime).format('x'))/1000/60/60%24)// const tdh = parseInt((moment(record.solvingTime).format('x') - moment(record.occurrenceTime).format('x')) / 1000 / 60 / 60 % 24)//
const tds=parseInt((moment(record.solvingTime).format('x')-moment(record.occurrenceTime).format('x'))/1000/60%60)// const tds = parseInt((moment(record.solvingTime).format('x') - moment(record.occurrenceTime).format('x')) / 1000 / 60 % 60)//
return <span>{`${tdd}${tdh}${tds}`}</span> return <span>{`${tdd}${tdh}${tds}`}</span>
} }
}, },
{ {
title: '故障类型', title: '故障类型',
dataIndex: 'type', dataIndex: 'type',
}, },
{ {
title: '解决者', title: '解决者',
//width:20, //width:20,
render:(record)=>{ render: (record) => {
// console.log('ressss',record) // console.log('ressss',record)
return <span> return <span>
{record?.maintenanceRecordExecuteUsers.map((item)=>{ {record?.maintenanceRecordExecuteUsers.map((item) => {
return item.name return item.name
}).toString() }).toString()
} }
</span> </span>
} }
}, },
{ {
title: '操作', title: '文件',
render:(record)=>{ render: (record) => {
return <div> return record.files?.length > 0 && <a style={{ color: 'blue' }} href={`/_file-server/${record.files[0]?.url + '?filename=' + encodeURIComponent(record.files[0]?.name)}`}>
<Button {record.files[0]?.name}
style={{marginRight:10}} </a> || '--'
onClick={() => { },
setModalVis(true);setRecordRow(record) },
{
title: '操作',
render: (record) => {
return <div style={{ width: 294 }}>
<Button
style={{ marginRight: 10 }}
onClick={() => {
setModalVis(true); setRecordRow(record)
}}
>
编辑
</Button>
<Popconfirm
title='确定是否删除?'
onConfirm={() => {
delHandler(record.id);
}}
>
<Button type='danger' > 删除</Button>
</Popconfirm>
<Button
style={{ marginRight: 10 }}
onClick={() => {
setRecordRow(record)
setAddVis(true)
}}
>
添加计划
</Button>
}} {record.planList?.length > 0 && <Button
> style={{ marginRight: 10 }}
编辑 onClick={() => {
</Button> getCycPlan(record.id);
<Popconfirm setVisible(true)
title='确定是否删除?' }}
onConfirm={() => { >
delHandler(record.id); 查看计划
}} </Button>}
> </div>
<Button type='danger' > 删除</Button>
</Popconfirm>
</div>
}, },
}, },
]; ];
const onChangeDate=(e)=> { const onChangeDate = (e) => {
console.log('zzzz',e[0],e[1])
setMode('') setMode('')
setDateValue(e) setDateValue(e)
setStime(moment(e[0]).format('YYYY-MM-DD HH:mm:ss')) setStime(moment(e[0]).format('YYYY-MM-DD HH:mm:ss'))
setEtime(moment(e[1]).format('YYYY-MM-DD HH:mm:ss')) setEtime(moment(e[1]).format('YYYY-MM-DD HH:mm:ss'))
const query={ const query = {
sTime, eTime sTime, eTime
} }
//console.log('sTime',sTime,eTime) //console.log('sTime',sTime,eTime)
dispatch(service.calculability(query)).then((res)=>{ dispatch(service.calculability(query)).then((res) => {
if(res.success) setCalculability((Math.round(res.payload.data * 10000)) / 100 + '%'); // console.log(res.payload.data,'dateee') if (res.success) setCalculability((Math.round(res.payload.data * 10000)) / 100 + '%'); // console.log(res.payload.data,'dateee')
}) })
} }
const clearHandler=()=>{ const clearHandler = () => {
} }
const addHandler=()=>{ const addHandler = () => {
setModalVis(true) setModalVis(true)
} }
const onSelect=(e)=> { const onSelect = (e) => {
if(e.target.value==='month'){ if (e.target.value === 'month') {
setStime(moment().startOf('month').format('YYYY-MM-DD HH:mm:ss')); setStime(moment().startOf('month').format('YYYY-MM-DD HH:mm:ss'));
setEtime(moment().endOf('month').format('YYYY-MM-DD HH:mm:ss')) setEtime(moment().endOf('month').format('YYYY-MM-DD HH:mm:ss'))
}else{ } else {
setStime(moment().startOf('year').format("YYYY-MM-DD HH:mm:ss")) setStime(moment().startOf('year').format("YYYY-MM-DD HH:mm:ss"))
setEtime(moment().endOf('year').format('YYYY-MM-DD HH:mm:ss')) setEtime(moment().endOf('year').format('YYYY-MM-DD HH:mm:ss'))
}
setMode(e.target.value)
setDateValue([])
} }
console.log('11111',moment().startOf('year').format('YYYY-MM-DD HH:mm:ss'))
setMode(e.target.value)
setDateValue([])
} const column = [
{
title: '序号',
render: (t, r, i) => {
return i + 1
}
},
{
title: '任务名称',
dataIndex: 'missionName',
},
{
title: '责任人',
render: (record) => {
return <span>
{record?.maintenancePlanExecuteUsers.map((item) => {
return item.name
}).toString()
}
</span>
}
},
{
title: '完成情况',
dataIndex: 'state',
render: (t, record) => t || '--'
},
{
title: '备注',
dataIndex: 'remark',
render: (t, record) => t || '--'
},
{
title: '计划完成时间',
render: (record) => {
return <span>{moment(record.planFinishTime).format('YYYY-MM-DD')}</span>
},
},
{
title: '实际完成时间',
render: (record) => {
return record.actualFinishTime ? <span>{moment(record.actualFinishTime).format('YYYY-MM-DD')}</span> : '--'
},
},
];
return ( return (
<div style={{background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px'}}> <div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}>
<div style={{display:'flex',marginTop:20,alignItems:'flex-end'}}> <div style={{ display: 'flex', marginTop: 20, alignItems: 'flex-end' }}>
<div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div> <div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div>
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>系统可用性</div> <div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>系统可用性</div>
<div style={{marginLeft:350}}> <div style={{ marginLeft: 350 }}>
<RadioGroup onChange={e => onSelect(e)} value={mode} type="button"> <RadioGroup onChange={e => onSelect(e)} value={mode} type="button">
<Radio value={'month'} checked>本月</Radio> <Radio value={'month'} checked>本月</Radio>
<Radio value={'year'}>全年</Radio> <Radio value={'year'}>全年</Radio>
</RadioGroup> </RadioGroup>
<DatePicker type="dateTimeRange" <DatePicker type="dateTimeRange"
onChange={e => onChangeDate(e) } onChange={e => onChangeDate(e)}
density="compact" density="compact"
style={{ width: 400 }} style={{ width: 400 }}
value={dateValue} value={dateValue}
onClear={()=>{clearHandler()}} onClear={() => { clearHandler() }}
/> />
</div> </div>
</div> </div>
<div style={{marginLeft:20,marginTop:50,display:'flex',alignItems:'flex-end'}}> <div style={{ marginLeft: 20, marginTop: 50, display: 'flex', alignItems: 'flex-end' }}>
<span style={{width:340}}>{`系统可用性=(平均故障间隔MTBF)/(平均故障间隔MTBF+故障恢复时间MTTR)*100%`}</span> <span style={{ width: 340 }}>{`系统可用性=(平均故障间隔MTBF)/(平均故障间隔MTBF+故障恢复时间MTTR)*100%`}</span>
<span style={{fontSize:48,color: '#D9001B',marginLeft:150}}>{calculability}</span> <span style={{ fontSize: 48, color: '#D9001B', marginLeft: 150 }}>{calculability}</span>
</div> </div>
<div style={{display:'flex',marginTop:20}}> <div style={{ display: 'flex', marginTop: 20 }}>
<Button theme='solid' type='secondary' onClick={addHandler}>新增</Button> <Button theme='solid' type='secondary' onClick={addHandler}>新增</Button>
<div style={{display:'flex',alignItems:'baseline',marginLeft:700}}> <div style={{ display: 'flex', alignItems: 'baseline', marginLeft: 700 }}>
<span >产生时间</span> <span >产生时间</span>
<DatePicker type="dateRange" insetInput style={{ width: 280,marginLeft:10 }} onChange={(e)=>{ <DatePicker type="dateRange" insetInput style={{ width: 280, marginLeft: 10 }} onChange={(e) => {
setStartTime((e[0])+'');setEndTime(e[1]+'') }} setStartTime((e[0]) + ''); setEndTime(e[1] + '')
onClear={()=>{setStartTime('1970-1-1');setEndTime('2099-12-31')}} /> }}
<Button theme='solid' type="primary" style={{ marginLeft:20 }} onClick={()=>{ onClear={() => { setStartTime('1970-1-1'); setEndTime('2099-12-31') }} />
setPageIndex(1);setPageSize(10) <Button theme='solid' type="primary" style={{ marginLeft: 20 }} onClick={() => {
getRecordList({ startTime,endTime,pageIndex:1,pageSize:10}); setPageIndex(1); setPageSize(10)
getRecordList({ startTime, endTime, pageIndex: 1, pageSize: 10 });
//console.log('setStartTime',startTime,'setEndTime',endTime) //console.log('setStartTime',startTime,'setEndTime',endTime)
}}>查询</Button> }}>查询</Button>
</div> </div>
</div> </div>
<div style={{marginTop:10}}> <div style={{ marginTop: 10 }}>
<Table columns={columns} dataSource={recordList} pagination={false} onHeaderRow={()=>{ <Table columns={columns} dataSource={recordList} pagination={false} onHeaderRow={() => {
return { return {
background:'#000000', background: '#000000',
} }
}}/> }} />
</div> </div>
<div style={{ display: 'flex',justifyContent:'flex-end' ,margin:'10px 0 0 0'}}> <div style={{ display: 'flex', justifyContent: 'flex-end', margin: '10px 0 0 0' }}>
<span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}> <span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}>
{total}条信息 {total}条信息
</span> </span>
<Pagination total={total} <Pagination total={total}
showSizeChanger showSizeChanger
pageSize={pageSize} pageSize={pageSize}
currentPage={pageIndex} currentPage={pageIndex}
pageSizeOpts={[10, 20, 30]} pageSizeOpts={[10, 20, 30]}
onChange={(pageIndex,pageSize)=>{ onChange={(pageIndex, pageSize) => {
setPageIndex(pageIndex) setPageIndex(pageIndex)
setPageSize(pageSize) setPageSize(pageSize)
const query={ const query = {
startTime,endTime,pageIndex,pageSize startTime, endTime, pageIndex, pageSize
} }
getRecordList(query) getRecordList(query)
}}></Pagination> }}></Pagination>
</div>
<RecordModal visible={modalVis} onClose={() => { setModalVis(false);getRecordList();setRecordRow(null)}} recordRow={recordRow} pepList={pepList}></RecordModal>
</div> </div>
<RecordModal
visible={modalVis}
onClose={() => { setModalVis(false); getRecordList(); setRecordRow(null) }}
recordRow={recordRow} pepList={pepList}></RecordModal>
<PlanAddmodal
visible={addVis}
onClose={() => {
setAddVis(false);
getRecordList()
setRecordRow(null)
}}
recordRow={recordRow} />
<Modal
visible={visible}
title={'查看周期计划'}
width={800}
footer={null}
onOk={() => {
setVisible(false)
}}
onCancel={() => {
setVisible(false)
}}
>
<Table style={{ marginBottom: 20 }} columns={column} dataSource={cycPlan} pagination={false} onHeaderRow={() => {
return {
background: '#000000',
}
}} />
</Modal>
</div>
) )
} }
@ -251,7 +365,7 @@ function mapStateToProps (state) {
return { return {
// loading: members.isRequesting, // loading: members.isRequesting,
// user: auth.user, // user: auth.user,
actions: global.actions, actions: global.actions,
// members: members.data, // members: members.data,
// socket: webSocket.socket // socket: webSocket.socket
}; };

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

@ -129,7 +129,10 @@ export const ApiTable = {
//获取设备类型 //获取设备类型
getEquipmentCategory: 'equipmentCategory', getEquipmentCategory: 'equipmentCategory',
//获取状态数据 //获取状态数据
getMaintenanceStatus: 'maintenanceStatus' getMaintenanceStatus: 'maintenanceStatus',
respondRecord: 'respond-record',
}; };
// 项企的接口 // 项企的接口

Loading…
Cancel
Save