feat: admin web dev

This commit is contained in:
2025-12-02 13:31:25 +08:00
parent 44d55d2a6e
commit 9ed58de1ce
30 changed files with 5129 additions and 1 deletions

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArDeviceVO, ArDeviceForm, ArDeviceQuery } from '@/api/inspection/device/types';
/**
* 查询AR设备列表
* @param query
* @returns {*}
*/
export const listArDevice = (query?: ArDeviceQuery): AxiosPromise<ArDeviceVO[]> => {
return request({
url: '/inspection/device/list',
method: 'get',
params: query
});
};
/**
* 查询AR设备详细
* @param id
*/
export const getArDevice = (id: string | number): AxiosPromise<ArDeviceVO> => {
return request({
url: '/inspection/device/' + id,
method: 'get'
});
};
/**
* 新增AR设备
* @param data
*/
export const addArDevice = (data: ArDeviceForm) => {
return request({
url: '/inspection/device',
method: 'post',
data: data
});
};
/**
* 修改AR设备
* @param data
*/
export const updateArDevice = (data: ArDeviceForm) => {
return request({
url: '/inspection/device',
method: 'put',
data: data
});
};
/**
* 删除AR设备
* @param id
*/
export const delArDevice = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/device/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,90 @@
export interface ArDeviceVO {
/**
* 设备ID
*/
id: string | number;
/**
* 设备名称
*/
deviceName: string;
/**
* 设备编号
*/
deviceNo: string;
/**
* 设备型号
*/
deviceModel: string;
/**
* 状态(0正常 1停用)
*/
status: string;
/**
* 备注
*/
remark: string;
/**
* 创建时间
*/
createTime: string;
}
export interface ArDeviceForm extends BaseEntity {
/**
* 设备ID
*/
id?: string | number;
/**
* 设备名称
*/
deviceName?: string;
/**
* 设备编号
*/
deviceNo?: string;
/**
* 设备型号
*/
deviceModel?: string;
/**
* 状态(0正常 1停用)
*/
status?: string;
/**
* 备注
*/
remark?: string;
}
export interface ArDeviceQuery extends PageQuery {
/**
* 设备名称
*/
deviceName?: string;
/**
* 设备编号
*/
deviceNo?: string;
/**
* 设备型号
*/
deviceModel?: string;
/**
* 状态(0正常 1停用)
*/
status?: string;
}

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArExecutionVO, ArExecutionForm, ArExecutionQuery } from '@/api/inspection/execution/types';
/**
* 查询任务执行记录列表
* @param query
* @returns {*}
*/
export const listArExecution = (query?: ArExecutionQuery): AxiosPromise<ArExecutionVO[]> => {
return request({
url: '/inspection/execution/list',
method: 'get',
params: query
});
};
/**
* 查询任务执行记录详细
* @param id
*/
export const getArExecution = (id: string | number): AxiosPromise<ArExecutionVO> => {
return request({
url: '/inspection/execution/' + id,
method: 'get'
});
};
/**
* 新增任务执行记录
* @param data
*/
export const addArExecution = (data: ArExecutionForm) => {
return request({
url: '/inspection/execution',
method: 'post',
data: data
});
};
/**
* 修改任务执行记录
* @param data
*/
export const updateArExecution = (data: ArExecutionForm) => {
return request({
url: '/inspection/execution',
method: 'put',
data: data
});
};
/**
* 删除任务执行记录
* @param id
*/
export const delArExecution = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/execution/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,240 @@
export interface ArExecutionVO {
/**
* 执行ID
*/
id: string | number;
/**
* 任务模板ID
*/
taskId: string | number;
/**
* 任务名称
*/
taskName?: string;
/**
* 执行编号
*/
executionCode: string;
/**
* 区域ID
*/
regionId: string | number;
/**
* 区域名称
*/
regionName?: string;
/**
* 使用的AR设备ID
*/
deviceId: string | number;
/**
* 设备名称
*/
deviceName?: string;
/**
* 操作人ID
*/
operatorId: string | number;
/**
* 操作人姓名
*/
operatorName: string;
/**
* 监护人ID
*/
custodianId: string | number;
/**
* 监护人姓名
*/
custodianName: string;
/**
* 送电人ID
*/
senderId: string | number;
/**
* 送电人姓名
*/
senderName: string;
/**
* 受电人ID
*/
recipientId: string | number;
/**
* 受电人姓名
*/
recipientName: string;
/**
* 指挥人ID
*/
commanderId: string | number;
/**
* 指挥人姓名
*/
commanderName: string;
/**
* 执行状态(pending待执行 in_progress执行中 completed已完成 cancelled已取消)
*/
status: string;
/**
* 开始时间
*/
startTime: string;
/**
* 结束时间
*/
endTime: string;
/**
* 总步骤数
*/
totalSteps: number;
/**
* 已完成步骤数
*/
completedSteps: number;
/**
* 创建时间
*/
createTime: string;
}
export interface ArExecutionForm extends BaseEntity {
/**
* 执行ID
*/
id?: string | number;
/**
* 任务模板ID
*/
taskId?: string | number;
/**
* 执行编号
*/
executionCode?: string;
/**
* 区域ID
*/
regionId?: string | number;
/**
* 使用的AR设备ID
*/
deviceId?: string | number;
/**
* 操作人ID
*/
operatorId?: string | number;
/**
* 操作人姓名
*/
operatorName?: string;
/**
* 监护人ID
*/
custodianId?: string | number;
/**
* 监护人姓名
*/
custodianName?: string;
/**
* 送电人ID
*/
senderId?: string | number;
/**
* 送电人姓名
*/
senderName?: string;
/**
* 受电人ID
*/
recipientId?: string | number;
/**
* 受电人姓名
*/
recipientName?: string;
/**
* 指挥人ID
*/
commanderId?: string | number;
/**
* 指挥人姓名
*/
commanderName?: string;
/**
* 执行状态
*/
status?: string;
}
export interface ArExecutionQuery extends PageQuery {
/**
* 任务模板ID
*/
taskId?: string | number;
/**
* 执行编号
*/
executionCode?: string;
/**
* 区域ID
*/
regionId?: string | number;
/**
* 使用的AR设备ID
*/
deviceId?: string | number;
/**
* 执行状态
*/
status?: string;
/**
* 开始时间-开始
*/
startTimeBegin?: string;
/**
* 开始时间-结束
*/
startTimeEnd?: string;
}

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArPointVO, ArPointForm, ArPointQuery } from '@/api/inspection/point/types';
/**
* 查询巡检点位列表
* @param query
* @returns {*}
*/
export const listArPoint = (query?: ArPointQuery): AxiosPromise<ArPointVO[]> => {
return request({
url: '/inspection/point/list',
method: 'get',
params: query
});
};
/**
* 查询巡检点位详细
* @param id
*/
export const getArPoint = (id: string | number): AxiosPromise<ArPointVO> => {
return request({
url: '/inspection/point/' + id,
method: 'get'
});
};
/**
* 新增巡检点位
* @param data
*/
export const addArPoint = (data: ArPointForm) => {
return request({
url: '/inspection/point',
method: 'post',
data: data
});
};
/**
* 修改巡检点位
* @param data
*/
export const updateArPoint = (data: ArPointForm) => {
return request({
url: '/inspection/point',
method: 'put',
data: data
});
};
/**
* 删除巡检点位
* @param id
*/
export const delArPoint = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/point/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,90 @@
export interface ArPointVO {
/**
* 点位ID
*/
id: string | number;
/**
* 所属区域ID
*/
regionId: string | number;
/**
* 所属区域名称
*/
regionName?: string;
/**
* 点位名称
*/
pointName: string;
/**
* 点位代码
*/
pointCode: string;
/**
* 位置数据(JSON格式,包含坐标等)
*/
positionData: Record<string, any> | string;
/**
* 备注
*/
remark: string;
/**
* 创建时间
*/
createTime: string;
}
export interface ArPointForm extends BaseEntity {
/**
* 点位ID
*/
id?: string | number;
/**
* 所属区域ID
*/
regionId?: string | number;
/**
* 点位名称
*/
pointName?: string;
/**
* 点位代码
*/
pointCode?: string;
/**
* 位置数据(JSON格式,包含坐标等)
*/
positionData?: Record<string, any> | string;
/**
* 备注
*/
remark?: string;
}
export interface ArPointQuery extends PageQuery {
/**
* 所属区域ID
*/
regionId?: string | number;
/**
* 点位名称
*/
pointName?: string;
/**
* 点位代码
*/
pointCode?: string;
}

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArRegionVO, ArRegionForm, ArRegionQuery } from '@/api/inspection/region/types';
/**
* 查询巡检区域列表
* @param query
* @returns {*}
*/
export const listArRegion = (query?: ArRegionQuery): AxiosPromise<ArRegionVO[]> => {
return request({
url: '/inspection/region/list',
method: 'get',
params: query
});
};
/**
* 查询巡检区域详细
* @param id
*/
export const getArRegion = (id: string | number): AxiosPromise<ArRegionVO> => {
return request({
url: '/inspection/region/' + id,
method: 'get'
});
};
/**
* 新增巡检区域
* @param data
*/
export const addArRegion = (data: ArRegionForm) => {
return request({
url: '/inspection/region',
method: 'post',
data: data
});
};
/**
* 修改巡检区域
* @param data
*/
export const updateArRegion = (data: ArRegionForm) => {
return request({
url: '/inspection/region',
method: 'put',
data: data
});
};
/**
* 删除巡检区域
* @param id
*/
export const delArRegion = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/region/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,85 @@
export interface ArRegionVO {
/**
* 区域ID
*/
id: string | number;
/**
* 区域名称
*/
regionName: string;
/**
* 区域代码
*/
regionCode: string;
/**
* 区域数据(JSON格式)
*/
regionData: Record<string, any> | string;
/**
* 状态(0正常 1停用)
*/
status: string;
/**
* 备注
*/
remark: string;
/**
* 创建时间
*/
createTime: string;
}
export interface ArRegionForm extends BaseEntity {
/**
* 区域ID
*/
id?: string | number;
/**
* 区域名称
*/
regionName?: string;
/**
* 区域代码
*/
regionCode?: string;
/**
* 区域数据(JSON格式)
*/
regionData?: Record<string, any> | string;
/**
* 状态(0正常 1停用)
*/
status?: string;
/**
* 备注
*/
remark?: string;
}
export interface ArRegionQuery extends PageQuery {
/**
* 区域名称
*/
regionName?: string;
/**
* 区域代码
*/
regionCode?: string;
/**
* 状态(0正常 1停用)
*/
status?: string;
}

View File

@@ -0,0 +1,74 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArStepVO, ArStepForm, ArStepQuery } from '@/api/inspection/step/types';
/**
* 查询巡检步骤列表
* @param query
* @returns {*}
*/
export const listArStep = (query?: ArStepQuery): AxiosPromise<ArStepVO[]> => {
return request({
url: '/inspection/step/list',
method: 'get',
params: query
});
};
/**
* 查询任务的步骤树形结构
* @param taskId
* @returns {*}
*/
export const getArStepTree = (taskId: string | number): AxiosPromise<ArStepVO[]> => {
return request({
url: '/inspection/step/tree/' + taskId,
method: 'get'
});
};
/**
* 查询巡检步骤详细
* @param id
*/
export const getArStep = (id: string | number): AxiosPromise<ArStepVO> => {
return request({
url: '/inspection/step/' + id,
method: 'get'
});
};
/**
* 新增巡检步骤
* @param data
*/
export const addArStep = (data: ArStepForm) => {
return request({
url: '/inspection/step',
method: 'post',
data: data
});
};
/**
* 修改巡检步骤
* @param data
*/
export const updateArStep = (data: ArStepForm) => {
return request({
url: '/inspection/step',
method: 'put',
data: data
});
};
/**
* 删除巡检步骤(级联删除子步骤)
* @param id
*/
export const delArStep = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/step/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,245 @@
export interface ArStepVO {
/**
* 步骤ID
*/
id: string | number;
/**
* 所属任务ID
*/
taskId: string | number;
/**
* 父步骤ID(0表示根节点)
*/
parentId: string | number;
/**
* 祖级列表
*/
ancestors: string;
/**
* 步骤名称
*/
stepName: string;
/**
* 步骤内容
*/
stepContent: string;
/**
* 内容语音URL
*/
contentVoice: string;
/**
* 显示顺序
*/
orderNum: number;
/**
* 关联点位ID
*/
pointId: string | number;
/**
* 关联点位名称
*/
pointName?: string;
/**
* 是否需要语音播报(0否 1是)
*/
needVoiceRead: string;
/**
* 是否需要复述(0否 1是)
*/
needVoiceRephrase: string;
/**
* 复述内容
*/
rephraseContent: string;
/**
* 复述语音URL
*/
rephraseVoice: string;
/**
* 是否需要语音确认(0否 1是)
*/
needVoiceConfirm: string;
/**
* 确认内容
*/
confirmContent: string;
/**
* 确认语音URL
*/
confirmVoice: string;
/**
* 确认关键词
*/
confirmWord: string;
/**
* 是否需要AI识别(0否 1是)
*/
needAi: string;
/**
* AI识别目标名称
*/
aiTargetName: string;
/**
* AI配置数据(JSON格式)
*/
aiData: Record<string, any> | string;
/**
* 是否需要操作(0否 1是)
*/
isOperation: string;
/**
* 是否叶子节点(0否 1是)
*/
isLeaf: string;
/**
* 子节点列表(用于树形结构)
*/
children?: ArStepVO[];
}
export interface ArStepForm extends BaseEntity {
/**
* 步骤ID
*/
id?: string | number;
/**
* 所属任务ID
*/
taskId?: string | number;
/**
* 父步骤ID(0表示根节点)
*/
parentId?: string | number;
/**
* 步骤名称
*/
stepName?: string;
/**
* 步骤内容
*/
stepContent?: string;
/**
* 内容语音URL
*/
contentVoice?: string;
/**
* 显示顺序
*/
orderNum?: number;
/**
* 关联点位ID
*/
pointId?: string | number;
/**
* 是否需要语音播报(0否 1是)
*/
needVoiceRead?: string;
/**
* 是否需要复述(0否 1是)
*/
needVoiceRephrase?: string;
/**
* 复述内容
*/
rephraseContent?: string;
/**
* 复述语音URL
*/
rephraseVoice?: string;
/**
* 是否需要语音确认(0否 1是)
*/
needVoiceConfirm?: string;
/**
* 确认内容
*/
confirmContent?: string;
/**
* 确认语音URL
*/
confirmVoice?: string;
/**
* 确认关键词
*/
confirmWord?: string;
/**
* 是否需要AI识别(0否 1是)
*/
needAi?: string;
/**
* AI识别目标名称
*/
aiTargetName?: string;
/**
* AI配置数据(JSON格式)
*/
aiData?: Record<string, any> | string;
/**
* 是否需要操作(0否 1是)
*/
isOperation?: string;
}
export interface ArStepQuery {
/**
* 所属任务ID
*/
taskId?: string | number;
/**
* 父步骤ID
*/
parentId?: string | number;
/**
* 步骤名称
*/
stepName?: string;
/**
* 关联点位ID
*/
pointId?: string | number;
}

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArStepMediaVO, ArStepMediaForm, ArStepMediaQuery } from '@/api/inspection/stepMedia/types';
/**
* 查询步骤媒体文件列表
* @param query
* @returns {*}
*/
export const listArStepMedia = (query?: ArStepMediaQuery): AxiosPromise<ArStepMediaVO[]> => {
return request({
url: '/inspection/stepMedia/list',
method: 'get',
params: query
});
};
/**
* 查询步骤媒体文件详细
* @param id
*/
export const getArStepMedia = (id: string | number): AxiosPromise<ArStepMediaVO> => {
return request({
url: '/inspection/stepMedia/' + id,
method: 'get'
});
};
/**
* 新增步骤媒体文件
* @param data
*/
export const addArStepMedia = (data: ArStepMediaForm) => {
return request({
url: '/inspection/stepMedia',
method: 'post',
data: data
});
};
/**
* 修改步骤媒体文件
* @param data
*/
export const updateArStepMedia = (data: ArStepMediaForm) => {
return request({
url: '/inspection/stepMedia',
method: 'put',
data: data
});
};
/**
* 删除步骤媒体文件
* @param id
*/
export const delArStepMedia = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/stepMedia/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,95 @@
export interface ArStepMediaVO {
/**
* 媒体ID
*/
id: string | number;
/**
* 步骤记录ID
*/
stepRecordId: string | number;
/**
* 媒体类型(image图片 video视频 audio音频)
*/
mediaType: string;
/**
* 文件URL
*/
fileUrl: string;
/**
* 文件名称
*/
fileName: string;
/**
* 文件大小(字节)
*/
fileSize: number;
/**
* 上传时间
*/
uploadTime: string;
/**
* 创建时间
*/
createTime: string;
}
export interface ArStepMediaForm extends BaseEntity {
/**
* 媒体ID
*/
id?: string | number;
/**
* 步骤记录ID
*/
stepRecordId?: string | number;
/**
* 媒体类型(image图片 video视频 audio音频)
*/
mediaType?: string;
/**
* 文件URL
*/
fileUrl?: string;
/**
* 文件名称
*/
fileName?: string;
/**
* 文件大小(字节)
*/
fileSize?: number;
}
export interface ArStepMediaQuery extends PageQuery {
/**
* 步骤记录ID
*/
stepRecordId?: string | number;
/**
* 媒体类型
*/
mediaType?: string;
/**
* 上传时间-开始
*/
uploadTimeBegin?: string;
/**
* 上传时间-结束
*/
uploadTimeEnd?: string;
}

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArStepRecordVO, ArStepRecordForm, ArStepRecordQuery } from '@/api/inspection/stepRecord/types';
/**
* 查询步骤执行记录列表
* @param query
* @returns {*}
*/
export const listArStepRecord = (query?: ArStepRecordQuery): AxiosPromise<ArStepRecordVO[]> => {
return request({
url: '/inspection/stepRecord/list',
method: 'get',
params: query
});
};
/**
* 查询步骤执行记录详细
* @param id
*/
export const getArStepRecord = (id: string | number): AxiosPromise<ArStepRecordVO> => {
return request({
url: '/inspection/stepRecord/' + id,
method: 'get'
});
};
/**
* 新增步骤执行记录
* @param data
*/
export const addArStepRecord = (data: ArStepRecordForm) => {
return request({
url: '/inspection/stepRecord',
method: 'post',
data: data
});
};
/**
* 修改步骤执行记录
* @param data
*/
export const updateArStepRecord = (data: ArStepRecordForm) => {
return request({
url: '/inspection/stepRecord',
method: 'put',
data: data
});
};
/**
* 删除步骤执行记录
* @param id
*/
export const delArStepRecord = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/stepRecord/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,155 @@
export interface ArStepRecordVO {
/**
* 记录ID
*/
id: string | number;
/**
* 任务执行ID
*/
executionId: string | number;
/**
* 执行编号
*/
executionCode?: string;
/**
* 步骤ID
*/
stepId: string | number;
/**
* 步骤名称
*/
stepName?: string;
/**
* 状态(pending待执行 completed已完成 skipped已跳过)
*/
status: string;
/**
* 是否完成(0否 1是)
*/
isDone: string;
/**
* 开始时间
*/
startTime: string;
/**
* 完成时间
*/
completionTime: string;
/**
* 耗时(秒)
*/
duration: number;
/**
* 文本反馈
*/
textFeedback: string;
/**
* 语音识别文本
*/
voiceText: string;
/**
* AI识别结果(JSON格式)
*/
aiResult: Record<string, any> | string;
/**
* 执行人ID
*/
executorId: string | number;
/**
* 执行人姓名
*/
executorName: string;
/**
* 创建时间
*/
createTime: string;
}
export interface ArStepRecordForm extends BaseEntity {
/**
* 记录ID
*/
id?: string | number;
/**
* 任务执行ID
*/
executionId?: string | number;
/**
* 步骤ID
*/
stepId?: string | number;
/**
* 状态(pending待执行 completed已完成 skipped已跳过)
*/
status?: string;
/**
* 开始时间
*/
startTime?: string;
/**
* 文本反馈
*/
textFeedback?: string;
/**
* 语音识别文本
*/
voiceText?: string;
/**
* AI识别结果(JSON格式)
*/
aiResult?: Record<string, any> | string;
/**
* 执行人ID
*/
executorId?: string | number;
/**
* 执行人姓名
*/
executorName?: string;
}
export interface ArStepRecordQuery extends PageQuery {
/**
* 任务执行ID
*/
executionId?: string | number;
/**
* 步骤ID
*/
stepId?: string | number;
/**
* 状态
*/
status?: string;
/**
* 执行人ID
*/
executorId?: string | number;
}

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ArTaskVO, ArTaskForm, ArTaskQuery } from '@/api/inspection/task/types';
/**
* 查询巡检任务模板列表
* @param query
* @returns {*}
*/
export const listArTask = (query?: ArTaskQuery): AxiosPromise<ArTaskVO[]> => {
return request({
url: '/inspection/task/list',
method: 'get',
params: query
});
};
/**
* 查询巡检任务模板详细
* @param id
*/
export const getArTask = (id: string | number): AxiosPromise<ArTaskVO> => {
return request({
url: '/inspection/task/' + id,
method: 'get'
});
};
/**
* 新增巡检任务模板
* @param data
*/
export const addArTask = (data: ArTaskForm) => {
return request({
url: '/inspection/task',
method: 'post',
data: data
});
};
/**
* 修改巡检任务模板
* @param data
*/
export const updateArTask = (data: ArTaskForm) => {
return request({
url: '/inspection/task',
method: 'put',
data: data
});
};
/**
* 删除巡检任务模板
* @param id
*/
export const delArTask = (id: string | number | Array<string | number>) => {
return request({
url: '/inspection/task/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,110 @@
export interface ArTaskVO {
/**
* 任务ID
*/
id: string | number;
/**
* 任务名称
*/
taskName: string;
/**
* 任务代码
*/
taskCode: string;
/**
* 关联区域ID
*/
regionId: string | number;
/**
* 关联区域名称
*/
regionName?: string;
/**
* 任务类型
*/
taskType: string;
/**
* 状态(0正常 1停用)
*/
status: string;
/**
* 备注
*/
remark: string;
/**
* 创建时间
*/
createTime: string;
}
export interface ArTaskForm extends BaseEntity {
/**
* 任务ID
*/
id?: string | number;
/**
* 任务名称
*/
taskName?: string;
/**
* 任务代码
*/
taskCode?: string;
/**
* 关联区域ID
*/
regionId?: string | number;
/**
* 任务类型
*/
taskType?: string;
/**
* 状态(0正常 1停用)
*/
status?: string;
/**
* 备注
*/
remark?: string;
}
export interface ArTaskQuery extends PageQuery {
/**
* 任务名称
*/
taskName?: string;
/**
* 任务代码
*/
taskCode?: string;
/**
* 关联区域ID
*/
regionId?: string | number;
/**
* 任务类型
*/
taskType?: string;
/**
* 状态(0正常 1停用)
*/
status?: string;
}

View File

@@ -93,7 +93,91 @@ export const constantRoutes: RouteRecordRaw[] = [
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes: RouteRecordRaw[] = [
{
path: '/inspection',
component: Layout,
redirect: 'noRedirect',
name: 'Inspection',
alwaysShow: true,
meta: {
title: 'AR巡检',
icon: 'guide'
},
children: [
{
path: 'device',
component: () => import('@/views/inspection/device/index.vue'),
name: 'ArDevice',
meta: {
title: '设备管理',
icon: 'monitor'
}
},
{
path: 'region',
component: () => import('@/views/inspection/region/index.vue'),
name: 'ArRegion',
meta: {
title: '区域管理',
icon: 'location'
}
},
{
path: 'point',
component: () => import('@/views/inspection/point/index.vue'),
name: 'ArPoint',
meta: {
title: '点位管理',
icon: 'position'
}
},
{
path: 'task',
component: () => import('@/views/inspection/task/index.vue'),
name: 'ArTask',
meta: {
title: '任务模板',
icon: 'list'
}
},
{
path: 'step',
component: () => import('@/views/inspection/step/index.vue'),
name: 'ArStep',
meta: {
title: '巡检步骤',
icon: 'tree'
}
},
{
path: 'stepMedia',
component: () => import('@/views/inspection/stepMedia/index.vue'),
name: 'ArStepMedia',
meta: {
title: '步骤媒体',
icon: 'picture'
}
},
{
path: 'execution',
component: () => import('@/views/inspection/execution/index.vue'),
name: 'ArExecution',
meta: {
title: '执行记录',
icon: 'documentation'
}
},
{
path: 'stepRecord',
component: () => import('@/views/inspection/stepRecord/index.vue'),
name: 'ArStepRecord',
meta: {
title: '步骤记录',
icon: 'edit'
}
}
]
}
];
/**

View File

@@ -0,0 +1,258 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="设备编号" prop="deviceNo">
<el-input v-model="queryParams.deviceNo" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="设备型号" prop="deviceModel">
<el-input v-model="queryParams.deviceModel" placeholder="请输入设备型号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="正常" value="0" />
<el-option label="停用" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:device:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:device:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:device:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:device:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="deviceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="设备ID" align="center" prop="id" width="80" />
<el-table-column label="设备名称" align="center" prop="deviceName" :show-overflow-tooltip="true" />
<el-table-column label="设备编号" align="center" prop="deviceNo" :show-overflow-tooltip="true" />
<el-table-column label="设备型号" align="center" prop="deviceModel" :show-overflow-tooltip="true" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-tag v-if="scope.row.status === '0'" type="success">正常</el-tag>
<el-tag v-else type="danger">停用</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['inspection:device:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:device:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改AR设备对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body>
<el-form ref="deviceFormRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="form.deviceName" placeholder="请输入设备名称" maxlength="100" />
</el-form-item>
<el-form-item label="设备编号" prop="deviceNo">
<el-input v-model="form.deviceNo" placeholder="请输入设备编号" maxlength="50" />
</el-form-item>
<el-form-item label="设备型号" prop="deviceModel">
<el-input v-model="form.deviceModel" placeholder="请输入设备型号" maxlength="100" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio value="0">正常</el-radio>
<el-radio value="1">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="500" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArDevice" lang="ts">
import { listArDevice, getArDevice, delArDevice, addArDevice, updateArDevice } from '@/api/inspection/device';
import { ArDeviceVO, ArDeviceQuery, ArDeviceForm } from '@/api/inspection/device/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const deviceList = ref<ArDeviceVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const deviceFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArDeviceForm = {
id: undefined,
deviceName: undefined,
deviceNo: undefined,
deviceModel: undefined,
status: '0',
remark: undefined
};
const data = reactive<PageData<ArDeviceForm, ArDeviceQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
deviceName: undefined,
deviceNo: undefined,
deviceModel: undefined,
status: undefined
},
rules: {
deviceName: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }],
deviceNo: [{ required: true, message: '设备编号不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询AR设备列表 */
const getList = async () => {
loading.value = true;
const res = await listArDevice(queryParams.value);
deviceList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
deviceFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ArDeviceVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加AR设备';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ArDeviceVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getArDevice(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改AR设备';
};
/** 提交按钮 */
const submitForm = () => {
deviceFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateArDevice(form.value).finally(() => (buttonLoading.value = false));
} else {
await addArDevice(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess(form.value.id ? '修改成功' : '新增成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ArDeviceVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除AR设备编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delArDevice(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'inspection/device/export',
{
...queryParams.value
},
`ar_device_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>

View File

@@ -0,0 +1,376 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="执行编号" prop="executionCode">
<el-input v-model="queryParams.executionCode" placeholder="请输入执行编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="任务模板" prop="taskId">
<el-select v-model="queryParams.taskId" placeholder="请选择任务模板" clearable filterable>
<el-option v-for="task in taskOptions" :key="task.id" :label="task.taskName" :value="task.id" />
</el-select>
</el-form-item>
<el-form-item label="区域" prop="regionId">
<el-select v-model="queryParams.regionId" placeholder="请选择区域" clearable filterable>
<el-option v-for="region in regionOptions" :key="region.id" :label="region.regionName" :value="region.id" />
</el-select>
</el-form-item>
<el-form-item label="设备" prop="deviceId">
<el-select v-model="queryParams.deviceId" placeholder="请选择设备" clearable filterable>
<el-option v-for="device in deviceOptions" :key="device.id" :label="device.deviceName" :value="device.id" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="待执行" value="pending" />
<el-option label="执行中" value="in_progress" />
<el-option label="已完成" value="completed" />
<el-option label="已取消" value="cancelled" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:execution:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:execution:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:execution:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:execution:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="executionList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="执行编号" align="center" prop="executionCode" :show-overflow-tooltip="true" width="180" />
<el-table-column label="任务名称" align="center" prop="taskName" :show-overflow-tooltip="true" />
<el-table-column label="区域" align="center" prop="regionName" :show-overflow-tooltip="true" />
<el-table-column label="设备" align="center" prop="deviceName" :show-overflow-tooltip="true" />
<el-table-column label="操作人" align="center" prop="operatorName" :show-overflow-tooltip="true" />
<el-table-column label="状态" align="center" prop="status" width="90">
<template #default="scope">
<el-tag v-if="scope.row.status === 'pending'" type="info">待执行</el-tag>
<el-tag v-else-if="scope.row.status === 'in_progress'" type="warning">执行中</el-tag>
<el-tag v-else-if="scope.row.status === 'completed'" type="success">已完成</el-tag>
<el-tag v-else type="danger">已取消</el-tag>
</template>
</el-table-column>
<el-table-column label="进度" align="center" width="120">
<template #default="scope">
<el-progress :percentage="calculateProgress(scope.row)" :color="getProgressColor(scope.row.status)" />
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="180" />
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['inspection:execution:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:execution:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改任务执行记录对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
<el-form ref="executionFormRef" :model="form" :rules="rules" label-width="100px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="任务模板" prop="taskId">
<el-select v-model="form.taskId" placeholder="请选择任务模板" filterable style="width: 100%">
<el-option v-for="task in taskOptions" :key="task.id" :label="task.taskName" :value="task.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="区域" prop="regionId">
<el-select v-model="form.regionId" placeholder="请选择区域" filterable style="width: 100%">
<el-option v-for="region in regionOptions" :key="region.id" :label="region.regionName" :value="region.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="使用设备" prop="deviceId">
<el-select v-model="form.deviceId" placeholder="请选择使用设备" clearable filterable style="width: 100%">
<el-option v-for="device in deviceOptions" :key="device.id" :label="device.deviceName" :value="device.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
<el-option label="待执行" value="pending" />
<el-option label="执行中" value="in_progress" />
<el-option label="已完成" value="completed" />
<el-option label="已取消" value="cancelled" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">执行角色</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="操作人" prop="operatorName">
<el-input v-model="form.operatorName" placeholder="请输入操作人姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="监护人" prop="custodianName">
<el-input v-model="form.custodianName" placeholder="请输入监护人姓名" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="送电人" prop="senderName">
<el-input v-model="form.senderName" placeholder="请输入送电人姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="受电人" prop="recipientName">
<el-input v-model="form.recipientName" placeholder="请输入受电人姓名" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="指挥人" prop="commanderName">
<el-input v-model="form.commanderName" placeholder="请输入指挥人姓名" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArExecution" lang="ts">
import { listArExecution, getArExecution, delArExecution, addArExecution, updateArExecution } from '@/api/inspection/execution';
import { ArExecutionVO, ArExecutionQuery, ArExecutionForm } from '@/api/inspection/execution/types';
import { listArTask } from '@/api/inspection/task';
import { ArTaskVO } from '@/api/inspection/task/types';
import { listArRegion } from '@/api/inspection/region';
import { ArRegionVO } from '@/api/inspection/region/types';
import { listArDevice } from '@/api/inspection/device';
import { ArDeviceVO } from '@/api/inspection/device/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const executionList = ref<ArExecutionVO[]>([]);
const taskOptions = ref<ArTaskVO[]>([]);
const regionOptions = ref<ArRegionVO[]>([]);
const deviceOptions = ref<ArDeviceVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const executionFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArExecutionForm = {
id: undefined,
taskId: undefined,
executionCode: undefined,
regionId: undefined,
deviceId: undefined,
operatorId: undefined,
operatorName: undefined,
custodianId: undefined,
custodianName: undefined,
senderId: undefined,
senderName: undefined,
recipientId: undefined,
recipientName: undefined,
commanderId: undefined,
commanderName: undefined,
status: 'pending'
};
const data = reactive<PageData<ArExecutionForm, ArExecutionQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
taskId: undefined,
executionCode: undefined,
regionId: undefined,
deviceId: undefined,
status: undefined,
startTimeBegin: undefined,
startTimeEnd: undefined
},
rules: {
taskId: [{ required: true, message: '任务模板不能为空', trigger: 'change' }],
regionId: [{ required: true, message: '区域不能为空', trigger: 'change' }],
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 计算进度百分比 */
const calculateProgress = (row: ArExecutionVO) => {
if (!row.totalSteps || row.totalSteps === 0) return 0;
return Math.round((row.completedSteps / row.totalSteps) * 100);
};
/** 获取进度条颜色 */
const getProgressColor = (status: string) => {
if (status === 'completed') return '#67c23a';
if (status === 'in_progress') return '#e6a23c';
if (status === 'cancelled') return '#f56c6c';
return '#909399';
};
/** 查询选项数据 */
const getOptions = async () => {
const [taskRes, regionRes, deviceRes] = await Promise.all([
listArTask({ pageNum: 1, pageSize: 1000, status: '0' }),
listArRegion({ pageNum: 1, pageSize: 1000, status: '0' }),
listArDevice({ pageNum: 1, pageSize: 1000, status: '0' })
]);
taskOptions.value = taskRes.rows;
regionOptions.value = regionRes.rows;
deviceOptions.value = deviceRes.rows;
};
/** 查询任务执行记录列表 */
const getList = async () => {
loading.value = true;
const res = await listArExecution(queryParams.value);
executionList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
executionFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ArExecutionVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加任务执行记录';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ArExecutionVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getArExecution(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改任务执行记录';
};
/** 提交按钮 */
const submitForm = () => {
executionFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateArExecution(form.value).finally(() => (buttonLoading.value = false));
} else {
await addArExecution(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess(form.value.id ? '修改成功' : '新增成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ArExecutionVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除任务执行记录编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delArExecution(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'inspection/execution/export',
{
...queryParams.value
},
`ar_execution_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getOptions();
getList();
});
</script>

View File

@@ -0,0 +1,297 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="所属区域" prop="regionId">
<el-select v-model="queryParams.regionId" placeholder="请选择所属区域" clearable filterable>
<el-option v-for="region in regionOptions" :key="region.id" :label="region.regionName" :value="region.id" />
</el-select>
</el-form-item>
<el-form-item label="点位名称" prop="pointName">
<el-input v-model="queryParams.pointName" placeholder="请输入点位名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="点位代码" prop="pointCode">
<el-input v-model="queryParams.pointCode" placeholder="请输入点位代码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:point:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:point:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:point:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:point:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="pointList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="点位ID" align="center" prop="id" width="80" />
<el-table-column label="所属区域" align="center" prop="regionName" :show-overflow-tooltip="true" />
<el-table-column label="点位名称" align="center" prop="pointName" :show-overflow-tooltip="true" />
<el-table-column label="点位代码" align="center" prop="pointCode" :show-overflow-tooltip="true" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['inspection:point:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:point:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改巡检点位对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="700px" append-to-body>
<el-form ref="pointFormRef" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="所属区域" prop="regionId">
<el-select v-model="form.regionId" placeholder="请选择所属区域" filterable>
<el-option v-for="region in regionOptions" :key="region.id" :label="region.regionName" :value="region.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="点位代码" prop="pointCode">
<el-input v-model="form.pointCode" placeholder="请输入点位代码" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="点位名称" prop="pointName">
<el-input v-model="form.pointName" placeholder="请输入点位名称" maxlength="100" />
</el-form-item>
<el-form-item label="位置数据" prop="positionData">
<el-input
v-model="positionDataStr"
type="textarea"
:rows="8"
placeholder='请输入位置数据JSON格式例如: {"x": 10.5, "y": 20.3, "z": 1.5, "rotation": {"x": 0, "y": 90, "z": 0}}'
/>
<div v-if="jsonError" class="el-form-item__error">{{ jsonError }}</div>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="500" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArPoint" lang="ts">
import { listArPoint, getArPoint, delArPoint, addArPoint, updateArPoint } from '@/api/inspection/point';
import { ArPointVO, ArPointQuery, ArPointForm } from '@/api/inspection/point/types';
import { listArRegion } from '@/api/inspection/region';
import { ArRegionVO } from '@/api/inspection/region/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const pointList = ref<ArPointVO[]>([]);
const regionOptions = ref<ArRegionVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const positionDataStr = ref('');
const jsonError = ref('');
const queryFormRef = ref<ElFormInstance>();
const pointFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArPointForm = {
id: undefined,
regionId: undefined,
pointName: undefined,
pointCode: undefined,
positionData: undefined,
remark: undefined
};
const data = reactive<PageData<ArPointForm, ArPointQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
regionId: undefined,
pointName: undefined,
pointCode: undefined
},
rules: {
regionId: [{ required: true, message: '所属区域不能为空', trigger: 'change' }],
pointName: [{ required: true, message: '点位名称不能为空', trigger: 'blur' }],
pointCode: [{ required: true, message: '点位代码不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询区域选项 */
const getRegionOptions = async () => {
const res = await listArRegion({ pageNum: 1, pageSize: 1000, status: '0' });
regionOptions.value = res.rows;
};
/** 验证并解析JSON */
const parsePositionData = () => {
jsonError.value = '';
if (!positionDataStr.value || positionDataStr.value.trim() === '') {
form.value.positionData = undefined;
return true;
}
try {
form.value.positionData = JSON.parse(positionDataStr.value);
return true;
} catch (e) {
jsonError.value = 'JSON格式不正确';
return false;
}
};
/** 查询巡检点位列表 */
const getList = async () => {
loading.value = true;
const res = await listArPoint(queryParams.value);
pointList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
positionDataStr.value = '';
jsonError.value = '';
pointFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ArPointVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加巡检点位';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ArPointVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getArPoint(_id);
Object.assign(form.value, res.data);
// 格式化JSON显示
if (form.value.positionData) {
positionDataStr.value = JSON.stringify(form.value.positionData, null, 2);
}
dialog.visible = true;
dialog.title = '修改巡检点位';
};
/** 提交按钮 */
const submitForm = () => {
pointFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
// 验证JSON格式
if (!parsePositionData()) {
return;
}
buttonLoading.value = true;
if (form.value.id) {
await updateArPoint(form.value).finally(() => (buttonLoading.value = false));
} else {
await addArPoint(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess(form.value.id ? '修改成功' : '新增成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ArPointVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除巡检点位编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delArPoint(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'inspection/point/export',
{
...queryParams.value
},
`ar_point_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getRegionOptions();
getList();
});
</script>

View File

@@ -0,0 +1,298 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="区域名称" prop="regionName">
<el-input v-model="queryParams.regionName" placeholder="请输入区域名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="区域代码" prop="regionCode">
<el-input v-model="queryParams.regionCode" placeholder="请输入区域代码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="正常" value="0" />
<el-option label="停用" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:region:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:region:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:region:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:region:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="regionList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="区域ID" align="center" prop="id" width="80" />
<el-table-column label="区域名称" align="center" prop="regionName" :show-overflow-tooltip="true" />
<el-table-column label="区域代码" align="center" prop="regionCode" :show-overflow-tooltip="true" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-tag v-if="scope.row.status === '0'" type="success">正常</el-tag>
<el-tag v-else type="danger">停用</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['inspection:region:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:region:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改巡检区域对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="700px" append-to-body>
<el-form ref="regionFormRef" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="区域名称" prop="regionName">
<el-input v-model="form.regionName" placeholder="请输入区域名称" maxlength="100" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="区域代码" prop="regionCode">
<el-input v-model="form.regionCode" placeholder="请输入区域代码" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio value="0">正常</el-radio>
<el-radio value="1">停用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="区域数据" prop="regionData">
<el-input
v-model="regionDataStr"
type="textarea"
:rows="6"
placeholder='请输入区域数据JSON格式例如: {"area": 1000, "building": "主楼", "floor": 3}'
/>
<div v-if="jsonError" class="el-form-item__error">{{ jsonError }}</div>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="500" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArRegion" lang="ts">
import { listArRegion, getArRegion, delArRegion, addArRegion, updateArRegion } from '@/api/inspection/region';
import { ArRegionVO, ArRegionQuery, ArRegionForm } from '@/api/inspection/region/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const regionList = ref<ArRegionVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const regionDataStr = ref('');
const jsonError = ref('');
const queryFormRef = ref<ElFormInstance>();
const regionFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArRegionForm = {
id: undefined,
regionName: undefined,
regionCode: undefined,
regionData: undefined,
status: '0',
remark: undefined
};
const data = reactive<PageData<ArRegionForm, ArRegionQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
regionName: undefined,
regionCode: undefined,
status: undefined
},
rules: {
regionName: [{ required: true, message: '区域名称不能为空', trigger: 'blur' }],
regionCode: [{ required: true, message: '区域代码不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 验证并解析JSON */
const parseRegionData = () => {
jsonError.value = '';
if (!regionDataStr.value || regionDataStr.value.trim() === '') {
form.value.regionData = undefined;
return true;
}
try {
form.value.regionData = JSON.parse(regionDataStr.value);
return true;
} catch (e) {
jsonError.value = 'JSON格式不正确';
return false;
}
};
/** 查询巡检区域列表 */
const getList = async () => {
loading.value = true;
const res = await listArRegion(queryParams.value);
regionList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
regionDataStr.value = '';
jsonError.value = '';
regionFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ArRegionVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加巡检区域';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ArRegionVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getArRegion(_id);
Object.assign(form.value, res.data);
// 格式化JSON显示
if (form.value.regionData) {
regionDataStr.value = JSON.stringify(form.value.regionData, null, 2);
}
dialog.visible = true;
dialog.title = '修改巡检区域';
};
/** 提交按钮 */
const submitForm = () => {
regionFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
// 验证JSON格式
if (!parseRegionData()) {
return;
}
buttonLoading.value = true;
if (form.value.id) {
await updateArRegion(form.value).finally(() => (buttonLoading.value = false));
} else {
await addArRegion(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess(form.value.id ? '修改成功' : '新增成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ArRegionVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除巡检区域编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delArRegion(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'inspection/region/export',
{
...queryParams.value
},
`ar_region_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>

View File

@@ -0,0 +1,485 @@
<template>
<div class="p-2">
<!-- 任务信息展示 -->
<el-card v-if="currentTaskId" shadow="hover" class="mb-[10px]">
<div class="flex items-center">
<el-tag type="primary" size="large">当前任务: {{ currentTaskName || '未指定任务' }}</el-tag>
<el-button v-if="route.query.taskId" link type="primary" icon="Back" @click="handleBack" class="ml-4">返回任务列表</el-button>
</div>
</el-card>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item v-if="!currentTaskId" label="所属任务" prop="taskId">
<el-select v-model="queryParams.taskId" placeholder="请选择所属任务" clearable filterable @change="handleTaskChange">
<el-option v-for="task in taskOptions" :key="task.id" :label="task.taskName" :value="task.id" />
</el-select>
</el-form-item>
<el-form-item label="步骤名称" prop="stepName">
<el-input v-model="queryParams.stepName" placeholder="请输入步骤名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:step:add']" type="primary" plain icon="Plus" :disabled="!queryParams.taskId" @click="handleAdd()">新增根步骤</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table
ref="stepTableRef"
v-loading="loading"
:data="stepList"
row-key="id"
border
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="步骤名称" align="left" prop="stepName" :show-overflow-tooltip="true" width="250" />
<el-table-column label="步骤内容" align="left" prop="stepContent" :show-overflow-tooltip="true" />
<el-table-column label="关联点位" align="center" prop="pointName" :show-overflow-tooltip="true" width="150" />
<el-table-column label="排序" align="center" prop="orderNum" width="80" />
<el-table-column label="语音播报" align="center" prop="needVoiceRead" width="90">
<template #default="scope">
<el-tag v-if="scope.row.needVoiceRead === '1'" type="success" size="small"></el-tag>
<el-tag v-else type="info" size="small"></el-tag>
</template>
</el-table-column>
<el-table-column label="AI识别" align="center" prop="needAi" width="80">
<template #default="scope">
<el-tag v-if="scope.row.needAi === '1'" type="warning" size="small"></el-tag>
<el-tag v-else type="info" size="small"></el-tag>
</template>
</el-table-column>
<el-table-column label="需要操作" align="center" prop="isOperation" width="90">
<template #default="scope">
<el-tag v-if="scope.row.isOperation === '1'" type="danger" size="small"></el-tag>
<el-tag v-else type="info" size="small"></el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="230" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="新增子步骤" placement="top">
<el-button v-hasPermi="['inspection:step:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['inspection:step:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:step:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 添加或修改巡检步骤对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="900px" append-to-body>
<el-form ref="stepFormRef" :model="form" :rules="rules" label-width="120px">
<!-- 基本信息 -->
<el-divider content-position="left">基本信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属任务" prop="taskId">
<el-select v-model="form.taskId" placeholder="请选择所属任务" disabled style="width: 100%">
<el-option v-for="task in taskOptions" :key="task.id" :label="task.taskName" :value="task.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="父步骤" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="stepTreeOptions"
:props="{ value: 'id', label: 'stepName', children: 'children' } as any"
value-key="id"
placeholder="请选择父步骤(0为根节点)"
check-strictly
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="16">
<el-form-item label="步骤名称" prop="stepName">
<el-input v-model="form.stepName" placeholder="请输入步骤名称" maxlength="100" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="排序号" prop="orderNum">
<el-input-number v-model="form.orderNum" :min="0" :max="9999" controls-position="right" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="步骤内容" prop="stepContent">
<el-input v-model="form.stepContent" type="textarea" :rows="3" placeholder="请输入步骤内容" />
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="关联点位" prop="pointId">
<el-select v-model="form.pointId" placeholder="请选择关联点位" clearable filterable style="width: 100%">
<el-option v-for="point in pointOptions" :key="point.id" :label="point.pointName" :value="point.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="内容语音URL" prop="contentVoice">
<el-input v-model="form.contentVoice" placeholder="请输入内容语音URL" />
</el-form-item>
</el-col>
</el-row>
<!-- 语音配置 -->
<el-divider content-position="left">语音配置</el-divider>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="语音播报" prop="needVoiceRead">
<el-switch v-model="form.needVoiceRead" active-value="1" inactive-value="0" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="需要复述" prop="needVoiceRephrase">
<el-switch v-model="form.needVoiceRephrase" active-value="1" inactive-value="0" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="语音确认" prop="needVoiceConfirm">
<el-switch v-model="form.needVoiceConfirm" active-value="1" inactive-value="0" />
</el-form-item>
</el-col>
</el-row>
<el-row v-if="form.needVoiceRephrase === '1'" :gutter="20">
<el-col :span="16">
<el-form-item label="复述内容" prop="rephraseContent">
<el-input v-model="form.rephraseContent" placeholder="请输入复述内容" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="复述语音URL" prop="rephraseVoice">
<el-input v-model="form.rephraseVoice" placeholder="语音URL" />
</el-form-item>
</el-col>
</el-row>
<el-row v-if="form.needVoiceConfirm === '1'" :gutter="20">
<el-col :span="12">
<el-form-item label="确认内容" prop="confirmContent">
<el-input v-model="form.confirmContent" placeholder="请输入确认内容" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="确认关键词" prop="confirmWord">
<el-input v-model="form.confirmWord" placeholder="关键词" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="确认语音URL" prop="confirmVoice">
<el-input v-model="form.confirmVoice" placeholder="语音URL" />
</el-form-item>
</el-col>
</el-row>
<!-- AI配置 -->
<el-divider content-position="left">AI配置</el-divider>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="需要AI识别" prop="needAi">
<el-switch v-model="form.needAi" active-value="1" inactive-value="0" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="需要操作" prop="isOperation">
<el-switch v-model="form.isOperation" active-value="1" inactive-value="0" />
</el-form-item>
</el-col>
</el-row>
<el-row v-if="form.needAi === '1'" :gutter="20">
<el-col :span="12">
<el-form-item label="AI目标名称" prop="aiTargetName">
<el-input v-model="form.aiTargetName" placeholder="请输入AI识别目标名称" />
</el-form-item>
</el-col>
</el-row>
<el-form-item v-if="form.needAi === '1'" label="AI配置数据" prop="aiData">
<el-input
v-model="aiDataStr"
type="textarea"
:rows="5"
placeholder='请输入AI配置JSON格式例如: {"modelName": "yolov8", "confidence": 0.8, "classes": ["红灯", "绿灯"]}'
/>
<div v-if="jsonError" class="el-form-item__error">{{ jsonError }}</div>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArStep" lang="ts">
import { listArStep, getArStep, delArStep, addArStep, updateArStep, getArStepTree } from '@/api/inspection/step';
import { ArStepVO, ArStepQuery, ArStepForm } from '@/api/inspection/step/types';
import { listArTask } from '@/api/inspection/task';
import { ArTaskVO } from '@/api/inspection/task/types';
import { listArPoint } from '@/api/inspection/point';
import { ArPointVO } from '@/api/inspection/point/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute();
const router = useRouter();
const stepList = ref<ArStepVO[]>([]);
const taskOptions = ref<ArTaskVO[]>([]);
const pointOptions = ref<ArPointVO[]>([]);
const stepTreeOptions = ref<any[]>([]);
const buttonLoading = ref(false);
const showSearch = ref(true);
const isExpandAll = ref(true);
const loading = ref(false);
const currentTaskId = ref<string | number | undefined>(undefined);
const currentTaskName = ref<string | undefined>(undefined);
const aiDataStr = ref('');
const jsonError = ref('');
const queryFormRef = ref<ElFormInstance>();
const stepFormRef = ref<ElFormInstance>();
const stepTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArStepForm = {
id: undefined,
taskId: undefined,
parentId: 0,
stepName: undefined,
stepContent: undefined,
contentVoice: undefined,
orderNum: 0,
pointId: undefined,
needVoiceRead: '0',
needVoiceRephrase: '0',
rephraseContent: undefined,
rephraseVoice: undefined,
needVoiceConfirm: '0',
confirmContent: undefined,
confirmVoice: undefined,
confirmWord: undefined,
needAi: '0',
aiTargetName: undefined,
aiData: undefined,
isOperation: '0'
};
const data = reactive<PageData<ArStepForm, ArStepQuery>>({
form: { ...initFormData },
queryParams: {
taskId: undefined,
parentId: undefined,
stepName: undefined,
pointId: undefined
},
rules: {
taskId: [{ required: true, message: '所属任务不能为空', trigger: 'change' }],
stepName: [{ required: true, message: '步骤名称不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 从路由参数初始化任务ID */
onMounted(() => {
if (route.query.taskId) {
currentTaskId.value = route.query.taskId as string;
currentTaskName.value = route.query.taskName as string;
queryParams.value.taskId = currentTaskId.value;
}
getTaskOptions();
getPointOptions();
if (queryParams.value.taskId) {
getList();
}
});
/** 返回任务列表 */
const handleBack = () => {
router.back();
};
/** 查询任务选项 */
const getTaskOptions = async () => {
const res = await listArTask({ pageNum: 1, pageSize: 1000, status: '0' });
taskOptions.value = res.rows;
};
/** 查询点位选项 */
const getPointOptions = async () => {
const res = await listArPoint({ pageNum: 1, pageSize: 1000 });
pointOptions.value = res.rows;
};
/** 查询步骤下拉树结构 */
const getStepTreeselect = async () => {
if (!queryParams.value.taskId) {
stepTreeOptions.value = [];
return;
}
const res = await getArStepTree(queryParams.value.taskId);
stepTreeOptions.value = [];
const data: any = { id: 0, stepName: '根节点', children: [] };
data.children = res.data || [];
stepTreeOptions.value.push(data);
};
/** 验证并解析JSON */
const parseAiData = () => {
jsonError.value = '';
if (!aiDataStr.value || aiDataStr.value.trim() === '') {
form.value.aiData = undefined;
return true;
}
try {
form.value.aiData = JSON.parse(aiDataStr.value);
return true;
} catch (e) {
jsonError.value = 'JSON格式不正确';
return false;
}
};
/** 查询巡检步骤树形列表 */
const getList = async () => {
if (!queryParams.value.taskId) {
proxy?.$modal.msgWarning('请先选择任务');
return;
}
loading.value = true;
const res = await getArStepTree(queryParams.value.taskId);
stepList.value = res.data || [];
loading.value = false;
};
/** 任务改变事件 */
const handleTaskChange = () => {
getList();
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
aiDataStr.value = '';
jsonError.value = '';
stepFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 新增按钮操作 */
const handleAdd = async (row?: ArStepVO) => {
reset();
await getStepTreeselect();
form.value.taskId = queryParams.value.taskId;
if (row && row.id) {
form.value.parentId = row.id;
} else {
form.value.parentId = 0;
}
dialog.visible = true;
dialog.title = row ? '添加子步骤' : '添加根步骤';
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(stepList.value, isExpandAll.value);
};
/** 展开/折叠操作 */
const toggleExpandAll = (data: ArStepVO[], status: boolean) => {
data.forEach((item) => {
stepTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = async (row: ArStepVO) => {
reset();
await getStepTreeselect();
const res = await getArStep(row.id);
Object.assign(form.value, res.data);
// 格式化JSON显示
if (form.value.aiData) {
aiDataStr.value = JSON.stringify(form.value.aiData, null, 2);
}
dialog.visible = true;
dialog.title = '修改巡检步骤';
};
/** 提交按钮 */
const submitForm = () => {
stepFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
// 验证JSON格式
if (form.value.needAi === '1' && !parseAiData()) {
return;
}
buttonLoading.value = true;
if (form.value.id) {
await updateArStep(form.value).finally(() => (buttonLoading.value = false));
} else {
await addArStep(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row: ArStepVO) => {
await proxy?.$modal.confirm('是否确认删除步骤"' + row.stepName + '"?注意:将级联删除所有子步骤!');
loading.value = true;
await delArStep(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
</script>

View File

@@ -0,0 +1,272 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="步骤记录" prop="stepRecordId">
<el-input v-model="queryParams.stepRecordId" placeholder="请输入步骤记录ID" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="媒体类型" prop="mediaType">
<el-select v-model="queryParams.mediaType" placeholder="请选择媒体类型" clearable>
<el-option label="图片" value="image" />
<el-option label="视频" value="video" />
<el-option label="音频" value="audio" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:stepMedia:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:stepMedia:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:stepMedia:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="stepMediaList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="预览" align="center" width="100">
<template #default="scope">
<el-image
v-if="scope.row.mediaType === 'image'"
:src="scope.row.fileUrl"
:preview-src-list="[scope.row.fileUrl]"
fit="cover"
style="width: 60px; height: 60px"
/>
<el-icon v-else-if="scope.row.mediaType === 'video'" :size="40" color="#409eff">
<VideoPlay />
</el-icon>
<el-icon v-else :size="40" color="#67c23a">
<Microphone />
</el-icon>
</template>
</el-table-column>
<el-table-column label="文件名称" align="center" prop="fileName" :show-overflow-tooltip="true" />
<el-table-column label="媒体类型" align="center" prop="mediaType" width="100">
<template #default="scope">
<el-tag v-if="scope.row.mediaType === 'image'" type="success">图片</el-tag>
<el-tag v-else-if="scope.row.mediaType === 'video'" type="primary">视频</el-tag>
<el-tag v-else type="warning">音频</el-tag>
</template>
</el-table-column>
<el-table-column label="文件大小" align="center" prop="fileSize" width="120">
<template #default="scope">{{ formatFileSize(scope.row.fileSize) }}</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="uploadTime" width="180" />
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="下载" placement="top">
<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:stepMedia:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加步骤媒体文件对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body>
<el-form ref="stepMediaFormRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="步骤记录ID" prop="stepRecordId">
<el-input v-model="form.stepRecordId" placeholder="请输入步骤记录ID" />
</el-form-item>
<el-form-item label="媒体类型" prop="mediaType">
<el-radio-group v-model="form.mediaType">
<el-radio value="image">图片</el-radio>
<el-radio value="video">视频</el-radio>
<el-radio value="audio">音频</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="文件URL" prop="fileUrl">
<el-input v-model="form.fileUrl" placeholder="请输入文件URL" />
</el-form-item>
<el-form-item label="文件名称" prop="fileName">
<el-input v-model="form.fileName" placeholder="请输入文件名称" />
</el-form-item>
<el-form-item label="文件大小" prop="fileSize">
<el-input-number v-model="form.fileSize" :min="0" controls-position="right" style="width: 100%" />
<span class="ml-2 text-gray-500">字节</span>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArStepMedia" lang="ts">
import { VideoPlay, Microphone } from '@element-plus/icons-vue';
import { listArStepMedia, getArStepMedia, delArStepMedia, addArStepMedia } from '@/api/inspection/stepMedia';
import { ArStepMediaVO, ArStepMediaQuery, ArStepMediaForm } from '@/api/inspection/stepMedia/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const stepMediaList = ref<ArStepMediaVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const stepMediaFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArStepMediaForm = {
id: undefined,
stepRecordId: undefined,
mediaType: 'image',
fileUrl: undefined,
fileName: undefined,
fileSize: undefined
};
const data = reactive<PageData<ArStepMediaForm, ArStepMediaQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
stepRecordId: undefined,
mediaType: undefined,
uploadTimeBegin: undefined,
uploadTimeEnd: undefined
},
rules: {
stepRecordId: [{ required: true, message: '步骤记录ID不能为空', trigger: 'blur' }],
mediaType: [{ required: true, message: '媒体类型不能为空', trigger: 'change' }],
fileUrl: [{ required: true, message: '文件URL不能为空', trigger: 'blur' }],
fileName: [{ required: true, message: '文件名称不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 格式化文件大小 */
const formatFileSize = (size: number) => {
if (!size) return '-';
if (size < 1024) return size + ' B';
if (size < 1024 * 1024) return (size / 1024).toFixed(2) + ' KB';
if (size < 1024 * 1024 * 1024) return (size / (1024 * 1024)).toFixed(2) + ' MB';
return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
};
/** 查询步骤媒体文件列表 */
const getList = async () => {
loading.value = true;
const res = await listArStepMedia(queryParams.value);
stepMediaList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
stepMediaFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ArStepMediaVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加步骤媒体文件';
};
/** 下载文件 */
const handleDownload = (row: ArStepMediaVO) => {
window.open(row.fileUrl, '_blank');
};
/** 提交按钮 */
const submitForm = () => {
stepMediaFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
await addArStepMedia(form.value).finally(() => (buttonLoading.value = false));
proxy?.$modal.msgSuccess('新增成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ArStepMediaVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除步骤媒体文件编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delArStepMedia(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'inspection/stepMedia/export',
{
...queryParams.value
},
`ar_step_media_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>

View File

@@ -0,0 +1,326 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="任务执行" prop="executionId">
<el-select v-model="queryParams.executionId" placeholder="请选择任务执行" clearable filterable>
<el-option v-for="execution in executionOptions" :key="execution.id" :label="execution.executionCode" :value="execution.id" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="待执行" value="pending" />
<el-option label="已完成" value="completed" />
<el-option label="已跳过" value="skipped" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:stepRecord:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:stepRecord:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:stepRecord:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:stepRecord:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="stepRecordList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="执行编号" align="center" prop="executionCode" :show-overflow-tooltip="true" width="180" />
<el-table-column label="步骤名称" align="center" prop="stepName" :show-overflow-tooltip="true" />
<el-table-column label="执行人" align="center" prop="executorName" :show-overflow-tooltip="true" />
<el-table-column label="状态" align="center" prop="status" width="90">
<template #default="scope">
<el-tag v-if="scope.row.status === 'pending'" type="info">待执行</el-tag>
<el-tag v-else-if="scope.row.status === 'completed'" type="success">已完成</el-tag>
<el-tag v-else type="warning">已跳过</el-tag>
</template>
</el-table-column>
<el-table-column label="耗时(秒)" align="center" prop="duration" width="100" />
<el-table-column label="开始时间" align="center" prop="startTime" width="180" />
<el-table-column label="完成时间" align="center" prop="completionTime" width="180" />
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['inspection:stepRecord:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:stepRecord:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改步骤执行记录对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="700px" append-to-body>
<el-form ref="stepRecordFormRef" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="任务执行" prop="executionId">
<el-select v-model="form.executionId" placeholder="请选择任务执行" filterable style="width: 100%">
<el-option v-for="execution in executionOptions" :key="execution.id" :label="execution.executionCode" :value="execution.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="步骤" prop="stepId">
<el-select v-model="form.stepId" placeholder="请选择步骤" filterable style="width: 100%">
<el-option v-for="step in stepOptions" :key="step.id" :label="step.stepName" :value="step.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
<el-option label="待执行" value="pending" />
<el-option label="已完成" value="completed" />
<el-option label="已跳过" value="skipped" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行人" prop="executorName">
<el-input v-model="form.executorName" placeholder="请输入执行人姓名" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="文本反馈" prop="textFeedback">
<el-input v-model="form.textFeedback" type="textarea" :rows="3" placeholder="请输入文本反馈" />
</el-form-item>
<el-form-item label="语音识别文本" prop="voiceText">
<el-input v-model="form.voiceText" type="textarea" :rows="2" placeholder="请输入语音识别文本" />
</el-form-item>
<el-form-item label="AI识别结果" prop="aiResult">
<el-input v-model="aiResultStr" type="textarea" :rows="4" placeholder='请输入AI识别结果JSON格式' />
<div v-if="jsonError" class="el-form-item__error">{{ jsonError }}</div>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArStepRecord" lang="ts">
import { listArStepRecord, getArStepRecord, delArStepRecord, addArStepRecord, updateArStepRecord } from '@/api/inspection/stepRecord';
import { ArStepRecordVO, ArStepRecordQuery, ArStepRecordForm } from '@/api/inspection/stepRecord/types';
import { listArExecution } from '@/api/inspection/execution';
import { ArExecutionVO } from '@/api/inspection/execution/types';
import { listArStep } from '@/api/inspection/step';
import { ArStepVO } from '@/api/inspection/step/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const stepRecordList = ref<ArStepRecordVO[]>([]);
const executionOptions = ref<ArExecutionVO[]>([]);
const stepOptions = ref<ArStepVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const aiResultStr = ref('');
const jsonError = ref('');
const queryFormRef = ref<ElFormInstance>();
const stepRecordFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArStepRecordForm = {
id: undefined,
executionId: undefined,
stepId: undefined,
status: 'pending',
textFeedback: undefined,
voiceText: undefined,
aiResult: undefined,
executorId: undefined,
executorName: undefined
};
const data = reactive<PageData<ArStepRecordForm, ArStepRecordQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
executionId: undefined,
stepId: undefined,
status: undefined,
executorId: undefined
},
rules: {
executionId: [{ required: true, message: '任务执行不能为空', trigger: 'change' }],
stepId: [{ required: true, message: '步骤不能为空', trigger: 'change' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 验证并解析JSON */
const parseAiResult = () => {
jsonError.value = '';
if (!aiResultStr.value || aiResultStr.value.trim() === '') {
form.value.aiResult = undefined;
return true;
}
try {
form.value.aiResult = JSON.parse(aiResultStr.value);
return true;
} catch (e) {
jsonError.value = 'JSON格式不正确';
return false;
}
};
/** 查询选项数据 */
const getOptions = async () => {
const [executionRes, stepRes] = await Promise.all([
listArExecution({ pageNum: 1, pageSize: 1000 }),
listArStep({ pageNum: 1, pageSize: 1000 })
]);
executionOptions.value = executionRes.rows;
stepOptions.value = stepRes.rows;
};
/** 查询步骤执行记录列表 */
const getList = async () => {
loading.value = true;
const res = await listArStepRecord(queryParams.value);
stepRecordList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
aiResultStr.value = '';
jsonError.value = '';
stepRecordFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ArStepRecordVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加步骤执行记录';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ArStepRecordVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getArStepRecord(_id);
Object.assign(form.value, res.data);
if (form.value.aiResult) {
aiResultStr.value = JSON.stringify(form.value.aiResult, null, 2);
}
dialog.visible = true;
dialog.title = '修改步骤执行记录';
};
/** 提交按钮 */
const submitForm = () => {
stepRecordFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (!parseAiResult()) {
return;
}
buttonLoading.value = true;
if (form.value.id) {
await updateArStepRecord(form.value).finally(() => (buttonLoading.value = false));
} else {
await addArStepRecord(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess(form.value.id ? '修改成功' : '新增成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ArStepRecordVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除步骤执行记录编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delArStepRecord(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'inspection/stepRecord/export',
{
...queryParams.value
},
`ar_step_record_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getOptions();
getList();
});
</script>

View File

@@ -0,0 +1,297 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="任务名称" prop="taskName">
<el-input v-model="queryParams.taskName" placeholder="请输入任务名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="任务代码" prop="taskCode">
<el-input v-model="queryParams.taskCode" placeholder="请输入任务代码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="关联区域" prop="regionId">
<el-select v-model="queryParams.regionId" placeholder="请选择关联区域" clearable filterable>
<el-option v-for="region in regionOptions" :key="region.id" :label="region.regionName" :value="region.id" />
</el-select>
</el-form-item>
<el-form-item label="任务类型" prop="taskType">
<el-input v-model="queryParams.taskType" placeholder="请输入任务类型" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="正常" value="0" />
<el-option label="停用" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:task:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:task:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:task:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['inspection:task:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="任务ID" align="center" prop="id" width="80" />
<el-table-column label="任务名称" align="center" prop="taskName" :show-overflow-tooltip="true" />
<el-table-column label="任务代码" align="center" prop="taskCode" :show-overflow-tooltip="true" />
<el-table-column label="关联区域" align="center" prop="regionName" :show-overflow-tooltip="true" />
<el-table-column label="任务类型" align="center" prop="taskType" :show-overflow-tooltip="true" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-tag v-if="scope.row.status === '0'" type="success">正常</el-tag>
<el-tag v-else type="danger">停用</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" width="250" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="管理步骤" placement="top">
<el-button v-hasPermi="['inspection:step:list']" link type="primary" icon="List" @click="handleManageSteps(scope.row)">步骤</el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['inspection:task:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['inspection:task:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改巡检任务模板对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body>
<el-form ref="taskFormRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="任务名称" prop="taskName">
<el-input v-model="form.taskName" placeholder="请输入任务名称" maxlength="100" />
</el-form-item>
<el-form-item label="任务代码" prop="taskCode">
<el-input v-model="form.taskCode" placeholder="请输入任务代码" maxlength="50" />
</el-form-item>
<el-form-item label="关联区域" prop="regionId">
<el-select v-model="form.regionId" placeholder="请选择关联区域" filterable style="width: 100%">
<el-option v-for="region in regionOptions" :key="region.id" :label="region.regionName" :value="region.id" />
</el-select>
</el-form-item>
<el-form-item label="任务类型" prop="taskType">
<el-input v-model="form.taskType" placeholder="请输入任务类型" maxlength="50" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio value="0">正常</el-radio>
<el-radio value="1">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="500" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ArTask" lang="ts">
import { listArTask, getArTask, delArTask, addArTask, updateArTask } from '@/api/inspection/task';
import { ArTaskVO, ArTaskQuery, ArTaskForm } from '@/api/inspection/task/types';
import { listArRegion } from '@/api/inspection/region';
import { ArRegionVO } from '@/api/inspection/region/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
const taskList = ref<ArTaskVO[]>([]);
const regionOptions = ref<ArRegionVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const taskFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ArTaskForm = {
id: undefined,
taskName: undefined,
taskCode: undefined,
regionId: undefined,
taskType: undefined,
status: '0',
remark: undefined
};
const data = reactive<PageData<ArTaskForm, ArTaskQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
taskName: undefined,
taskCode: undefined,
regionId: undefined,
taskType: undefined,
status: undefined
},
rules: {
taskName: [{ required: true, message: '任务名称不能为空', trigger: 'blur' }],
taskCode: [{ required: true, message: '任务代码不能为空', trigger: 'blur' }],
regionId: [{ required: true, message: '关联区域不能为空', trigger: 'change' }],
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询区域选项 */
const getRegionOptions = async () => {
const res = await listArRegion({ pageNum: 1, pageSize: 1000, status: '0' });
regionOptions.value = res.rows;
};
/** 查询巡检任务模板列表 */
const getList = async () => {
loading.value = true;
const res = await listArTask(queryParams.value);
taskList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
taskFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ArTaskVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加巡检任务模板';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ArTaskVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getArTask(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改巡检任务模板';
};
/** 管理步骤按钮操作 */
const handleManageSteps = (row: ArTaskVO) => {
router.push({
path: '/inspection/step',
query: {
taskId: row.id,
taskName: row.taskName
}
});
};
/** 提交按钮 */
const submitForm = () => {
taskFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateArTask(form.value).finally(() => (buttonLoading.value = false));
} else {
await addArTask(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess(form.value.id ? '修改成功' : '新增成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ArTaskVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除巡检任务模板编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delArTask(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'inspection/task/export',
{
...queryParams.value
},
`ar_task_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getRegionOptions();
getList();
});
</script>