Browse Source

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

dev
wenlele 1 year ago
parent
commit
fb6076bb6f
  1. 10
      api/.vscode/launch.json
  2. 24
      api/app/lib/controllers/maintenancePlan/index.js
  3. 50
      api/app/lib/controllers/record/index.js
  4. 3
      api/app/lib/index.js
  5. 10
      api/app/lib/models/maintenance_plan.js
  6. 11
      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. 61
      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. 34
      web/client/src/sections/service/components/cycAddmodal.jsx
  18. 87
      web/client/src/sections/service/components/planAddmodal.jsx
  19. 58
      web/client/src/sections/service/components/recordModal.jsx
  20. 8
      web/client/src/sections/service/containers/cyclePlan.jsx
  21. 3
      web/client/src/sections/service/containers/maintenanceRecords.jsx
  22. 126
      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",

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

@ -5,24 +5,31 @@
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 })
@ -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
}) })

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

@ -6,6 +6,7 @@ async function getRecord(ctx) {
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}`);
@ -82,14 +94,14 @@ 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 {
@ -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' })

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

@ -78,7 +78,17 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "state", field: "state",
autoIncrement: false autoIncrement: false
},
recordId: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: true,
defaultValue: null,
comment: "响应记录id",
primaryKey: false,
field: "record_id",
autoIncrement: false
} }
}, { }, {
tableName: "maintenance_plan", tableName: "maintenance_plan",
comment: "", comment: "",

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

@ -69,7 +69,16 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "record", field: "record",
autoIncrement: false autoIncrement: false
} },
files: {
type: DataTypes.JSONB,
allowNull: true,
defaultValue: null,
comment: "文件",
primaryKey: false,
field: "files",
autoIncrement: false
},
}, { }, {
tableName: "maintenance_record", tableName: "maintenance_record",
comment: "", comment: "",

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,9 +382,10 @@ const Rest = (props) => {
setFileSearch('') setFileSearch('')
}} }}
/> />
</SimpleBar> {/* </SimpleBar> */}
</div> </div>
</div>
{/* 表格 */} {/* 表格 */}
<div style={{ <div style={{
flex: 1, height: '100%', flex: 1, height: '100%',
@ -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 }}>

61
web/client/src/sections/means/containers/projectMeans.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,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 }}>

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) {//删除服务记录
}); });
} }

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

@ -1,20 +1,21 @@
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 moment from 'moment'
import { Button, Table, Modal, Form } from '@douyinfe/semi-ui'; 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();
const [selectValue, setSelectValue] = useState([])
useEffect(() => { useEffect(() => {
}, []) }, [])
// //
const okHandler = () => { const okHandler = () => {
//api.current.setValues({'manger':recordRow?.maintenancePlanExecuteUsers.map((item)=>{return item.id})},)
api.current.validate().then((res) => { api.current.validate().then((res) => {
res.manger console.log(111, res, respondRecordData?.map(v => ({ value: v.id, label: v.sketch })));
recordRow?.maintenancePlanExecuteUsers
const query = { const query = {
id: recordRow?.id, id: recordRow?.id,
actualFinishTime: res.realityTime, actualFinishTime: res.realityTime,
@ -24,6 +25,7 @@ const okHandler=()=>{
type: 'period', type: 'period',
missionName: res.taskName, missionName: res.taskName,
manger: res.manger, manger: res.manger,
recordId: res.recordId,
msg: recordRow ? '编辑周期性计划' : '添加周期性计划' msg: recordRow ? '编辑周期性计划' : '添加周期性计划'
} }
dispatch(service.editMaintenancePlan(query)).then((res) => { dispatch(service.editMaintenancePlan(query)).then((res) => {
@ -40,13 +42,16 @@ const okHandler=()=>{
> >
<Form <Form
initValues={{'taskName':recordRow?.missionName, initValues={{
'taskName': recordRow?.missionName,
'manger': recordRow?.maintenancePlanExecuteUsers.map((item) => { return item.pepUserId }), 'manger': recordRow?.maintenancePlanExecuteUsers.map((item) => { return item.pepUserId }),
'reason': recordRow?.reason, 'reason': recordRow?.reason,
'status': recordRow?.state, 'status': recordRow?.state,
'notes': recordRow?.remark, 'notes': recordRow?.remark,
'planTime': recordRow?.planFinishTime, 'planTime': recordRow?.planFinishTime,
'realityTime':recordRow?.actualFinishTime}} 'realityTime': recordRow?.actualFinishTime,
'recordId': recordRow?.recordId || [],
}}
getFormApi={formApi => api.current = formApi} getFormApi={formApi => api.current = formApi}
labelCol={{ span: 6 }} labelCol={{ span: 6 }}
@ -57,11 +62,13 @@ const okHandler=()=>{
]} ></Form.Input> ]} ></Form.Input>
<Form.Select field='manger' label='责任人' rules={[{ required: true, message: '请输入责任人' }]} trigger='blur' style={{ width: '100%' }} <Form.Select field='manger' label='责任人' rules={[{ required: true, message: '请输入责任人' }]} trigger='blur' style={{ width: '100%' }}
multiple filter> multiple filter>
{pepList?.map((item)=>{return ( <Form.Select.OptGroup label={item.name}> {pepList?.map((item) => {
return (<Form.Select.OptGroup label={item.name}>
{item.users.map((item1) => { {item.users.map((item1) => {
return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option> return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option>
})} })}
</Form.Select.OptGroup> )})} </Form.Select.OptGroup>)
})}
</Form.Select> </Form.Select>
<Form.Select label='完成情况' style={{ width: 200 }} field='status'> <Form.Select label='完成情况' style={{ width: 200 }} field='status'>
<Form.Select.Option value='未完成'>未完成</Form.Select.Option> <Form.Select.Option value='未完成'>未完成</Form.Select.Option>
@ -73,6 +80,17 @@ const okHandler=()=>{
<Form.DatePicker label='计划完成时间:' field='planTime' rules={[{ required: true, message: '请选择计划完成时间' },]}></Form.DatePicker> <Form.DatePicker label='计划完成时间:' field='planTime' rules={[{ required: true, message: '请选择计划完成时间' },]}></Form.DatePicker>
<Form.DatePicker label='实际完成时间:' field='realityTime'></Form.DatePicker> <Form.DatePicker label='实际完成时间:' field='realityTime'></Form.DatePicker>
<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> </Form>
</Modal> </Modal>

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)

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

@ -1,14 +1,16 @@
'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 [uploadData, setUploadData] = useState({})
const FormApi = useRef(); const FormApi = useRef();
const { service } = actions const { service } = actions
// let t=0// // let t=0//
@ -38,8 +40,10 @@ const okHandler=()=>{
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()
@ -56,13 +60,14 @@ const okHandler=()=>{
//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={{
'name': recordRow?.sketch,
'startTime': recordRow?.occurrenceTime, 'startTime': recordRow?.occurrenceTime,
'endTime': recordRow?.solvingTime, 'endTime': recordRow?.solvingTime,
'settler': recordRow?.maintenanceRecordExecuteUsers.map((item) => { return item.pepUserId }), 'settler': recordRow?.maintenanceRecordExecuteUsers.map((item) => { return item.pepUserId }),
@ -103,12 +108,14 @@ const okHandler=()=>{
<Form.Select field='settler' label='解决者:' trigger='blur' style={{ width: '100%' }} <Form.Select field='settler' label='解决者:' trigger='blur' style={{ width: '100%' }}
filter filter
rules={[{ required: true, message: '请输入解决者' }]} multiple> rules={[{ required: true, message: '请输入解决者' }]} multiple>
{pepList?.map((item)=>{return ( <Form.Select.OptGroup label={item.name}> {pepList?.map((item) => {
return (<Form.Select.OptGroup label={item.name}>
{item.users.map((item1) => { {item.users.map((item1) => {
return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option> return <Form.Select.Option value={item1.id} label={item1.name}></Form.Select.Option>
})} })}
</Form.Select.OptGroup> )})} </Form.Select.OptGroup>)
})}
</Form.Select> </Form.Select>
<Form.Select field="type" label={{ text: '故障类型' }} style={{ width: 200 }} rules={[{ required: true, message: '请输入故障类型' }]}> <Form.Select field="type" label={{ text: '故障类型' }} style={{ width: 200 }} rules={[{ required: true, message: '请输入故障类型' }]}>
<Form.Select.Option value="es异常">es异常</Form.Select.Option> <Form.Select.Option value="es异常">es异常</Form.Select.Option>
@ -124,6 +131,32 @@ const okHandler=()=>{
<Form.TextArea field="record" label={{ text: '故障记录' }} rules={[{ required: true, message: '请输入故障记录' }]}> <Form.TextArea field="record" label={{ text: '故障记录' }} rules={[{ required: true, message: '请输入故障记录' }]}>
</Form.TextArea> </Form.TextArea>
<div style={{ display: 'flex', marginLeft: 30 }}>
<div style={{ marginTop: 6 }}>文件</div>
<Upload
style={{ display: 'inline-block' }}
action={`${apiRoot}/attachments/p`}
accept={'.txt, .doc, .docx, .xls, .xlsx, .pdf, .png, .jpg, .rar, .zip'}
limit={1}
maxSize={51200}
onRemove={() => {
setUploadData({})
}}
onSuccess={(responseBody, file) => {
setUploadData({
name: file.name,
size: file.size,
url: responseBody?.uploaded,
uploadTime: moment().format("YYYY-MM-DD HH:mm:ss")
})
}}
>
<Button icon={<IconUpload />} theme="light">
文件上传
</Button>
</Upload>
</div>
</Form> </Form>
@ -131,13 +164,14 @@ const okHandler=()=>{
</Modal> </Modal>
} }
function mapStateToProps (state) { function mapStateToProps (state) {
const { auth, global, members, webSocket } = state; const { auth, global, members, webSocket, addRecord } = state;
console.log(addRecord);
return { return {
// loading: members.isRequesting, addRecord: addRecord.isRequesting,
// user: auth.user, // user: auth.user,
actions: global.actions, actions: global.actions,
// members: members.data, qiniu: global.qiniu?.domain,
// socket: webSocket.socket apiRoot: global.apiRoot
}; };
} }

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

@ -15,6 +15,7 @@ const Server = (props) => {
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) => {
@ -27,6 +28,11 @@ const Server = (props) => {
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) => {
if (res.success) {
setRespondRecordData(res?.payload.data)
}
});
}, []) }, [])
const delHandler = (record) => { const delHandler = (record) => {
const query = { const query = {
@ -140,7 +146,7 @@ const Server = (props) => {
getCycPlan(query) getCycPlan(query)
}}></Pagination> }}></Pagination>
</div> </div>
<Addmodal visible={addVis} onClose={()=>{setAddVis(false);setRecordRow(null);getCycPlan()}} recordRow={recordRow} pepList={pepList}></Addmodal> <Addmodal visible={addVis} respondRecordData={respondRecordData} onClose={() => { setAddVis(false); setRecordRow(null); getCycPlan() }} recordRow={recordRow} pepList={pepList}></Addmodal>
</div> </div>
) )

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

@ -17,12 +17,13 @@ const MaintenanceRecords = (props) => {
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 }) => { const getEquipment = (query = { startTime, endTime, pageIndex, pageSize }) => {
dispatch(service.getEquipment(query)).then((res) => { dispatch(service.getEquipment(query)).then((res) => {
// console.log() // console.log()
if (res.success) setEquipmentList(res?.payload.data.result); setTotal(res?.payload.data.resCount) if (res.success) setEquipmentList(res?.payload.data.result); setTotal(res?.payload.data.resCount)
}); });
}; };
// console.log('equipmentList', equipmentList); // console.log('equipmentList', equipmentList);

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

@ -5,6 +5,7 @@ import { Calendar,DatePicker,RadioGroup, Radio,Button,Table,Modal,Tooltip,Pagina
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
@ -24,11 +25,16 @@ const Server = (props) => {
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 [addVis, setAddVis] = useState(false)
const [cycPlan, setCysPlan] = useState([])
const [visible, setVisible] = useState(false)
const getRecordList = (query = { const getRecordList = (query = {
startTime, endTime, pageIndex, pageSize startTime, endTime, pageIndex, pageSize
}) => { }) => {
dispatch(service.getRecord(query)).then((res) => { dispatch(service.getRecord(query)).then((res) => {
console.log('res1',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)
}) })
@ -39,6 +45,7 @@ const Server = (props) => {
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) => {
@ -48,6 +55,12 @@ const Server = (props) => {
} }
}) })
} }
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
@ -114,10 +127,18 @@ const Server = (props) => {
</span> </span>
} }
}, },
{
title: '文件',
render: (record) => {
return record.files?.length > 0 && <a style={{ color: 'blue' }} href={`/_file-server/${record.files[0]?.url + '?filename=' + encodeURIComponent(record.files[0]?.name)}`}>
{record.files[0]?.name}
</a> || '--'
},
},
{ {
title: '操作', title: '操作',
render: (record) => { render: (record) => {
return <div> return <div style={{ width: 294 }}>
<Button <Button
style={{ marginRight: 10 }} style={{ marginRight: 10 }}
onClick={() => { onClick={() => {
@ -135,13 +156,31 @@ const Server = (props) => {
> >
<Button type='danger' > 删除</Button> <Button type='danger' > 删除</Button>
</Popconfirm> </Popconfirm>
<Button
style={{ marginRight: 10 }}
onClick={() => {
setRecordRow(record)
setAddVis(true)
}}
>
添加计划
</Button>
{record.planList?.length > 0 && <Button
style={{ marginRight: 10 }}
onClick={() => {
getCycPlan(record.id);
setVisible(true)
}}
>
查看计划
</Button>}
</div> </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'))
@ -172,11 +211,56 @@ const addHandler=()=>{
setEtime(moment().endOf('year').format('YYYY-MM-DD HH:mm:ss')) setEtime(moment().endOf('year').format('YYYY-MM-DD HH:mm:ss'))
} }
console.log('11111',moment().startOf('year').format('YYYY-MM-DD HH:mm:ss'))
setMode(e.target.value) setMode(e.target.value)
setDateValue([]) 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' }}>
@ -206,7 +290,8 @@ const addHandler=()=>{
<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') }} /> onClear={() => { setStartTime('1970-1-1'); setEndTime('2099-12-31') }} />
<Button theme='solid' type="primary" style={{ marginLeft: 20 }} onClick={() => { <Button theme='solid' type="primary" style={{ marginLeft: 20 }} onClick={() => {
setPageIndex(1); setPageSize(10) setPageIndex(1); setPageSize(10)
@ -240,7 +325,36 @@ const addHandler=()=>{
getRecordList(query) getRecordList(query)
}}></Pagination> }}></Pagination>
</div> </div>
<RecordModal visible={modalVis} onClose={() => { setModalVis(false);getRecordList();setRecordRow(null)}} recordRow={recordRow} pepList={pepList}></RecordModal> <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> </div>
) )

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