聊天室创建工单
This commit is contained in:
@@ -227,7 +227,7 @@ DROP TABLE IF EXISTS workcase.tb_workcase CASCADE;
|
|||||||
CREATE TABLE workcase.tb_workcase(
|
CREATE TABLE workcase.tb_workcase(
|
||||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||||
workcase_id VARCHAR(50) NOT NULL, -- 工单ID
|
workcase_id VARCHAR(50) NOT NULL, -- 工单ID
|
||||||
chat_id VARCHAR(50) NOT NULL, -- 对话ID
|
room_id VARCHAR(50) NOT NULL, -- 聊天室ID
|
||||||
user_id VARCHAR(50) NOT NULL, -- 来客ID
|
user_id VARCHAR(50) NOT NULL, -- 来客ID
|
||||||
username VARCHAR(200) NOT NULL, -- 来客姓名
|
username VARCHAR(200) NOT NULL, -- 来客姓名
|
||||||
phone VARCHAR(20) NOT NULL, -- 来客电话
|
phone VARCHAR(20) NOT NULL, -- 来客电话
|
||||||
@@ -248,7 +248,7 @@ CREATE TABLE workcase.tb_workcase(
|
|||||||
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
|
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
|
||||||
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
|
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
|
||||||
PRIMARY KEY (workcase_id),
|
PRIMARY KEY (workcase_id),
|
||||||
UNIQUE (chat_id),
|
UNIQUE (room_id),
|
||||||
UNIQUE (optsn)
|
UNIQUE (optsn)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ public class TbWorkcaseDTO extends BaseDTO{
|
|||||||
@Schema(description = "工单ID")
|
@Schema(description = "工单ID")
|
||||||
private String workcaseId;
|
private String workcaseId;
|
||||||
|
|
||||||
|
@Schema(description = "聊天室ID")
|
||||||
|
private String roomId;
|
||||||
|
|
||||||
@Schema(description = "来客ID")
|
@Schema(description = "来客ID")
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,10 @@ public class WorkcaseController {
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
public ResultDomain<TbWorkcaseDTO> createWorkcase(@RequestBody TbWorkcaseDTO workcase) {
|
public ResultDomain<TbWorkcaseDTO> createWorkcase(@RequestBody TbWorkcaseDTO workcase) {
|
||||||
ValidationResult vr = ValidationUtils.validate(workcase, Arrays.asList(
|
ValidationResult vr = ValidationUtils.validate(workcase, Arrays.asList(
|
||||||
|
ValidationUtils.requiredString("deviceNamePlateImg", "设备名称牌图片"),
|
||||||
|
ValidationUtils.requiredString("type", "问题类型"),
|
||||||
ValidationUtils.requiredString("userId", "用户ID"),
|
ValidationUtils.requiredString("userId", "用户ID"),
|
||||||
ValidationUtils.requiredString("type", "问题类型")
|
ValidationUtils.requiredString("username", "用户名称")
|
||||||
));
|
));
|
||||||
if (!vr.isValid()) {
|
if (!vr.isValid()) {
|
||||||
return ResultDomain.failure(vr.getAllErrors());
|
return ResultDomain.failure(vr.getAllErrors());
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
|
|||||||
import org.xyzh.api.workcase.dto.TbWorkcaseDeviceDTO;
|
import org.xyzh.api.workcase.dto.TbWorkcaseDeviceDTO;
|
||||||
import org.xyzh.api.workcase.dto.TbWorkcaseProcessDTO;
|
import org.xyzh.api.workcase.dto.TbWorkcaseProcessDTO;
|
||||||
import org.xyzh.api.workcase.service.WorkcaseService;
|
import org.xyzh.api.workcase.service.WorkcaseService;
|
||||||
|
import org.xyzh.common.auth.utils.LoginUtil;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
import org.xyzh.common.core.page.PageDomain;
|
import org.xyzh.common.core.page.PageDomain;
|
||||||
import org.xyzh.common.core.page.PageParam;
|
import org.xyzh.common.core.page.PageParam;
|
||||||
@@ -19,6 +20,8 @@ import org.xyzh.workcase.enums.WorkcaseProcessAction;
|
|||||||
import org.xyzh.workcase.mapper.TbWorkcaseDeviceMapper;
|
import org.xyzh.workcase.mapper.TbWorkcaseDeviceMapper;
|
||||||
import org.xyzh.workcase.mapper.TbWorkcaseMapper;
|
import org.xyzh.workcase.mapper.TbWorkcaseMapper;
|
||||||
import org.xyzh.workcase.mapper.TbWorkcaseProcessMapper;
|
import org.xyzh.workcase.mapper.TbWorkcaseProcessMapper;
|
||||||
|
import org.xyzh.api.workcase.dto.TbChatRoomDTO;
|
||||||
|
import org.xyzh.api.workcase.service.ChatRoomService;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
|
||||||
@@ -35,11 +38,36 @@ public class WorkcaseServiceImpl implements WorkcaseService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TbWorkcaseDeviceMapper workcaseDeviceMapper;
|
private TbWorkcaseDeviceMapper workcaseDeviceMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChatRoomService chatRoomService;
|
||||||
|
|
||||||
// ====================== 工单管理 ======================
|
// ====================== 工单管理 ======================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResultDomain<TbWorkcaseDTO> createWorkcase(TbWorkcaseDTO workcase) {
|
public ResultDomain<TbWorkcaseDTO> createWorkcase(TbWorkcaseDTO workcase) {
|
||||||
logger.info("创建工单: userId={}, type={}", workcase.getUserId(), workcase.getType());
|
logger.info("创建工单: userId={}, type={}, roomId={}", workcase.getUserId(), workcase.getType(), workcase.getRoomId());
|
||||||
|
|
||||||
|
// 如果没有 roomId,先创建聊天室
|
||||||
|
boolean needCreateRoom = (workcase.getRoomId() == null || workcase.getRoomId().isEmpty());
|
||||||
|
if (needCreateRoom) {
|
||||||
|
logger.info("未提供 roomId,先创建聊天室");
|
||||||
|
TbChatRoomDTO chatRoom = new TbChatRoomDTO();
|
||||||
|
chatRoom.setGuestId(workcase.getUserId());
|
||||||
|
chatRoom.setGuestName(workcase.getUsername());
|
||||||
|
chatRoom.setRoomType("workcase");
|
||||||
|
chatRoom.setRoomName("工单客服-" + workcase.getUsername());
|
||||||
|
chatRoom.setStatus("active");
|
||||||
|
chatRoom.setCreator(workcase.getCreator());
|
||||||
|
|
||||||
|
ResultDomain<TbChatRoomDTO> roomResult = chatRoomService.createChatRoom(chatRoom);
|
||||||
|
if (!roomResult.getSuccess() || roomResult.getData() == null) {
|
||||||
|
logger.error("创建聊天室失败: {}", roomResult.getMessage());
|
||||||
|
return ResultDomain.failure("创建聊天室失败: " + roomResult.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
workcase.setRoomId(roomResult.getData().getRoomId());
|
||||||
|
logger.info("聊天室创建成功: roomId={}", workcase.getRoomId());
|
||||||
|
}
|
||||||
|
|
||||||
if (workcase.getWorkcaseId() == null || workcase.getWorkcaseId().isEmpty()) {
|
if (workcase.getWorkcaseId() == null || workcase.getWorkcaseId().isEmpty()) {
|
||||||
workcase.setWorkcaseId(IdUtil.generateUUID());
|
workcase.setWorkcaseId(IdUtil.generateUUID());
|
||||||
@@ -53,9 +81,11 @@ public class WorkcaseServiceImpl implements WorkcaseService {
|
|||||||
if (workcase.getEmergency() == null || workcase.getEmergency().isEmpty()) {
|
if (workcase.getEmergency() == null || workcase.getEmergency().isEmpty()) {
|
||||||
workcase.setEmergency("normal");
|
workcase.setEmergency("normal");
|
||||||
}
|
}
|
||||||
|
workcase.setCreator(LoginUtil.getCurrentUserId());
|
||||||
|
|
||||||
int rows = workcaseMapper.insertWorkcase(workcase);
|
int rows = workcaseMapper.insertWorkcase(workcase);
|
||||||
if (rows > 0) {
|
if (rows > 0) {
|
||||||
|
// 创建工单处理记录
|
||||||
TbWorkcaseProcessDTO process = new TbWorkcaseProcessDTO();
|
TbWorkcaseProcessDTO process = new TbWorkcaseProcessDTO();
|
||||||
process.setProcessId(IdUtil.generateUUID());
|
process.setProcessId(IdUtil.generateUUID());
|
||||||
process.setOptsn(IdUtil.getOptsn());
|
process.setOptsn(IdUtil.getOptsn());
|
||||||
@@ -65,9 +95,16 @@ public class WorkcaseServiceImpl implements WorkcaseService {
|
|||||||
process.setCreator(workcase.getCreator());
|
process.setCreator(workcase.getCreator());
|
||||||
workcaseProcessMapper.insertWorkcaseProcess(process);
|
workcaseProcessMapper.insertWorkcaseProcess(process);
|
||||||
|
|
||||||
syncWorkcaseToCrm(workcase);
|
// 如果是新创建的聊天室,更新聊天室的 workcaseId
|
||||||
sendWechatKefuWelcome(workcase);
|
|
||||||
|
logger.info("更新聊天室的工单ID: roomId={}, workcaseId={}", workcase.getRoomId(), workcase.getWorkcaseId());
|
||||||
|
TbChatRoomDTO updateRoom = new TbChatRoomDTO();
|
||||||
|
updateRoom.setRoomId(workcase.getRoomId());
|
||||||
|
updateRoom.setWorkcaseId(workcase.getWorkcaseId());
|
||||||
|
chatRoomService.updateChatRoom(updateRoom);
|
||||||
|
|
||||||
|
|
||||||
|
syncWorkcaseToCrm(workcase);
|
||||||
return ResultDomain.success("创建成功", workcase);
|
return ResultDomain.success("创建成功", workcase);
|
||||||
}
|
}
|
||||||
return ResultDomain.failure("创建失败");
|
return ResultDomain.failure("创建失败");
|
||||||
|
|||||||
@@ -76,6 +76,7 @@
|
|||||||
<update id="updateChatRoom" parameterType="org.xyzh.api.workcase.dto.TbChatRoomDTO">
|
<update id="updateChatRoom" parameterType="org.xyzh.api.workcase.dto.TbChatRoomDTO">
|
||||||
UPDATE workcase.tb_chat_room
|
UPDATE workcase.tb_chat_room
|
||||||
<set>
|
<set>
|
||||||
|
<if test="workcaseId != null">workcase_id = #{workcaseId},</if>
|
||||||
<if test="roomName != null and roomName != ''">room_name = #{roomName},</if>
|
<if test="roomName != null and roomName != ''">room_name = #{roomName},</if>
|
||||||
<if test="roomType != null and roomType != ''">room_type = #{roomType},</if>
|
<if test="roomType != null and roomType != ''">room_type = #{roomType},</if>
|
||||||
<if test="status != null and status != ''">status = #{status},</if>
|
<if test="status != null and status != ''">status = #{status},</if>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
<resultMap id="BaseResultMap" type="org.xyzh.api.workcase.dto.TbWorkcaseDTO">
|
<resultMap id="BaseResultMap" type="org.xyzh.api.workcase.dto.TbWorkcaseDTO">
|
||||||
<id column="workcase_id" property="workcaseId" jdbcType="VARCHAR"/>
|
<id column="workcase_id" property="workcaseId" jdbcType="VARCHAR"/>
|
||||||
|
<result column="room_id" property="roomId" jdbcType="VARCHAR"/>
|
||||||
<result column="optsn" property="optsn" jdbcType="VARCHAR"/>
|
<result column="optsn" property="optsn" jdbcType="VARCHAR"/>
|
||||||
<result column="user_id" property="userId" jdbcType="VARCHAR"/>
|
<result column="user_id" property="userId" jdbcType="VARCHAR"/>
|
||||||
<result column="username" property="username" jdbcType="VARCHAR"/>
|
<result column="username" property="username" jdbcType="VARCHAR"/>
|
||||||
@@ -27,13 +28,13 @@
|
|||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
workcase_id, optsn, user_id, username, phone, type, device, device_code, device_name_plate, device_name_plate_img,
|
workcase_id, room_id, optsn, user_id, username, phone, type, device, device_code, device_name_plate, device_name_plate_img,
|
||||||
address, description, imgs, emergency, status, processor, creator, create_time, update_time, delete_time, deleted
|
address, description, imgs, emergency, status, processor, creator, create_time, update_time, delete_time, deleted
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<insert id="insertWorkcase" parameterType="org.xyzh.api.workcase.dto.TbWorkcaseDTO">
|
<insert id="insertWorkcase" parameterType="org.xyzh.api.workcase.dto.TbWorkcaseDTO">
|
||||||
INSERT INTO workcase.tb_workcase (
|
INSERT INTO workcase.tb_workcase (
|
||||||
optsn, workcase_id, user_id, username, phone, type, device_name_plate_img, creator
|
optsn, workcase_id, room_id, user_id, username, phone, type, device_name_plate_img, creator
|
||||||
<if test="device != null">, device</if>
|
<if test="device != null">, device</if>
|
||||||
<if test="deviceCode != null">, device_code</if>
|
<if test="deviceCode != null">, device_code</if>
|
||||||
<if test="deviceNamePlate != null">, device_name_plate</if>
|
<if test="deviceNamePlate != null">, device_name_plate</if>
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
<if test="status != null">, status</if>
|
<if test="status != null">, status</if>
|
||||||
<if test="processor != null">, processor</if>
|
<if test="processor != null">, processor</if>
|
||||||
) VALUES (
|
) VALUES (
|
||||||
#{optsn}, #{workcaseId}, #{userId}, #{username}, #{phone}, #{type}, #{deviceNamePlateImg}, #{creator}
|
#{optsn}, #{workcaseId}, #{roomId}, #{userId}, #{username}, #{phone}, #{type}, #{deviceNamePlateImg}, #{creator}
|
||||||
<if test="device != null">, #{device}</if>
|
<if test="device != null">, #{device}</if>
|
||||||
<if test="deviceCode != null">, #{deviceCode}</if>
|
<if test="deviceCode != null">, #{deviceCode}</if>
|
||||||
<if test="deviceNamePlate != null">, #{deviceNamePlate}</if>
|
<if test="deviceNamePlate != null">, #{deviceNamePlate}</if>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import type { BaseDTO } from 'shared/types'
|
|||||||
export interface TbWorkcaseDTO extends BaseDTO {
|
export interface TbWorkcaseDTO extends BaseDTO {
|
||||||
/** 工单ID */
|
/** 工单ID */
|
||||||
workcaseId?: string
|
workcaseId?: string
|
||||||
|
/** 聊天室ID */
|
||||||
|
roomId: string
|
||||||
/** 来客ID */
|
/** 来客ID */
|
||||||
userId?: string
|
userId?: string
|
||||||
/** 来客姓名 */
|
/** 来客姓名 */
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
declare const uni: {
|
declare const uni: {
|
||||||
getStorageSync: (key: string) => any
|
getStorageSync: (key: string) => any
|
||||||
request: (options: any) => void
|
request: (options: any) => void
|
||||||
|
uploadFile: (options: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
import type { ResultDomain } from '../types'
|
import type { ResultDomain } from '../types'
|
||||||
@@ -46,4 +47,40 @@ export function request<T>(options: {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 文件上传方法
|
||||||
|
export function uploadFile<T>(options: {
|
||||||
|
url: string
|
||||||
|
filePath: string
|
||||||
|
name?: string
|
||||||
|
formData?: Record<string, any>
|
||||||
|
header?: Record<string, string>
|
||||||
|
}): Promise<ResultDomain<T>> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const token = uni.getStorageSync('token') as string
|
||||||
|
uni.uploadFile({
|
||||||
|
url: BASE_URL + options.url,
|
||||||
|
filePath: options.filePath,
|
||||||
|
name: options.name || 'file',
|
||||||
|
formData: options.formData,
|
||||||
|
header: {
|
||||||
|
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
|
||||||
|
...options.header
|
||||||
|
},
|
||||||
|
success: (res: any) => {
|
||||||
|
try {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
|
||||||
|
resolve(result as ResultDomain<T>)
|
||||||
|
} else {
|
||||||
|
reject(new Error(`上传失败: ${res.statusCode}`))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
reject(new Error('解析响应失败'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err: any) => {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
74
urbanLifelineWeb/packages/workcase_wechat/api/file/file.ts
Normal file
74
urbanLifelineWeb/packages/workcase_wechat/api/file/file.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { request, uploadFile } from '../base'
|
||||||
|
import type { ResultDomain, TbSysFileDTO, FileUploadParam, BatchFileUploadParam } from '../../types'
|
||||||
|
import { BASE_URL } from '../../config'
|
||||||
|
|
||||||
|
export const fileAPI = {
|
||||||
|
baseUrl: '/urban-lifeline/file',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传单个文件(uni-app 版本)
|
||||||
|
* @param filePath 文件临时路径
|
||||||
|
* @param param 文件上传参数
|
||||||
|
* @returns Promise<ResultDomain<TbSysFileDTO>>
|
||||||
|
*/
|
||||||
|
uploadFile(filePath: string, param?: FileUploadParam): Promise<ResultDomain<TbSysFileDTO>> {
|
||||||
|
return uploadFile<TbSysFileDTO>({
|
||||||
|
url: `${this.baseUrl}/upload`,
|
||||||
|
filePath: filePath,
|
||||||
|
name: 'file',
|
||||||
|
formData: {
|
||||||
|
module: param?.module || '',
|
||||||
|
optsn: param?.optsn || '',
|
||||||
|
uploader: param?.uploader || ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量上传文件(uni-app 版本)
|
||||||
|
* @param filePaths 文件临时路径数组
|
||||||
|
* @param param 批量文件上传参数
|
||||||
|
* @returns Promise<ResultDomain<TbSysFileDTO>>
|
||||||
|
*/
|
||||||
|
async batchUploadFiles(filePaths: string[], param?: BatchFileUploadParam): Promise<ResultDomain<TbSysFileDTO>> {
|
||||||
|
const uploadPromises = filePaths.map(filePath =>
|
||||||
|
this.uploadFile(filePath, param)
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const results = await Promise.all(uploadPromises)
|
||||||
|
// 返回包含所有上传文件的结果
|
||||||
|
const uploadedFiles = results
|
||||||
|
.filter((r: ResultDomain<TbSysFileDTO>) => r.success && r.data)
|
||||||
|
.map((r: ResultDomain<TbSysFileDTO>) => r.data!)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
dataList: uploadedFiles
|
||||||
|
} as ResultDomain<TbSysFileDTO>
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: '批量上传失败'
|
||||||
|
} as ResultDomain<TbSysFileDTO>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件信息
|
||||||
|
* @param fileId 文件ID
|
||||||
|
* @returns Promise<ResultDomain<TbSysFileDTO>>
|
||||||
|
*/
|
||||||
|
getFileById(fileId: string): Promise<ResultDomain<TbSysFileDTO>> {
|
||||||
|
return request<TbSysFileDTO>({ url: `${BASE_URL}${this.baseUrl}/${fileId}`, method: 'GET' })
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件下载 URL
|
||||||
|
* @param fileId 文件ID
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
getDownloadUrl(fileId: string): string {
|
||||||
|
return `${BASE_URL}${this.baseUrl}/download/${fileId}`
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './file'
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from "./base"
|
export * from "./base"
|
||||||
export * from "./sys"
|
export * from "./sys"
|
||||||
export * from "./workcase"
|
export * from "./workcase"
|
||||||
export * from "./ai"
|
export * from "./ai"
|
||||||
|
export * from "./file"
|
||||||
@@ -106,6 +106,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, nextTick, onMounted, onUnmounted, watch } from 'vue'
|
import { ref, reactive, computed, nextTick, onMounted, onUnmounted, watch } from 'vue'
|
||||||
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
import type { ChatRoomMessageVO, CustomerVO, ChatMemberVO, TbChatRoomMessageDTO } from '@/types/workcase'
|
import type { ChatRoomMessageVO, CustomerVO, ChatMemberVO, TbChatRoomMessageDTO } from '@/types/workcase'
|
||||||
import { workcaseChatAPI } from '@/api/workcase'
|
import { workcaseChatAPI } from '@/api/workcase'
|
||||||
import { wsClient } from '@/utils/websocket'
|
import { wsClient } from '@/utils/websocket'
|
||||||
@@ -232,6 +233,11 @@ onMounted(() => {
|
|||||||
initWebSocket()
|
initWebSocket()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 页面显示时重新查询聊天室信息(从工单页返回时会自动刷新)
|
||||||
|
onShow(() => {
|
||||||
|
refreshChatRoomInfo()
|
||||||
|
})
|
||||||
|
|
||||||
// 组件卸载时断开WebSocket
|
// 组件卸载时断开WebSocket
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
disconnectWebSocket()
|
disconnectWebSocket()
|
||||||
@@ -253,6 +259,21 @@ watch(roomId, (newRoomId, oldRoomId) => {
|
|||||||
const PAGE_SIZE = 20
|
const PAGE_SIZE = 20
|
||||||
const messageTotal = ref<number>(0)
|
const messageTotal = ref<number>(0)
|
||||||
|
|
||||||
|
// 刷新聊天室信息(仅更新 workcaseId 等基本信息,不重新加载消息)
|
||||||
|
async function refreshChatRoomInfo() {
|
||||||
|
if (!roomId.value) return
|
||||||
|
try {
|
||||||
|
const roomRes = await workcaseChatAPI.getChatRoomById(roomId.value)
|
||||||
|
if (roomRes.success && roomRes.data) {
|
||||||
|
roomName.value = roomRes.data.roomName || '聊天室'
|
||||||
|
workcaseId.value = roomRes.data.workcaseId || ''
|
||||||
|
messageTotal.value = roomRes.data.messageCount || 0
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('刷新聊天室信息失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadChatRoom() {
|
async function loadChatRoom() {
|
||||||
if (!roomId.value) return
|
if (!roomId.value) return
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|||||||
@@ -224,7 +224,45 @@
|
|||||||
margin-left: 16rpx;
|
margin-left: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 铭牌照片
|
// 铭牌照片上传
|
||||||
|
.nameplate-upload {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nameplate-preview {
|
||||||
|
position: relative;
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1rpx solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nameplate-placeholder {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
border: 2rpx dashed #d1d5db;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #f9fafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nameplate-delete {
|
||||||
|
position: absolute;
|
||||||
|
top: 4rpx;
|
||||||
|
right: 4rpx;
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.nameplate-photo {
|
.nameplate-photo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 400rpx;
|
max-width: 400rpx;
|
||||||
@@ -240,6 +278,18 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required {
|
||||||
|
color: #ef4444;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.status-tag {
|
.status-tag {
|
||||||
padding: 4rpx 12rpx;
|
padding: 4rpx 12rpx;
|
||||||
border-radius: 6rpx;
|
border-radius: 6rpx;
|
||||||
|
|||||||
@@ -37,28 +37,28 @@
|
|||||||
<view class="form-container">
|
<view class="form-container">
|
||||||
<!-- 客户姓名 -->
|
<!-- 客户姓名 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label">客户姓名</text>
|
<text class="form-label">客户姓名<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
<input v-if="mode === 'create'" class="form-input" v-model="workcase.username" placeholder="请输入客户姓名" />
|
<input v-if="mode === 'create'" class="form-input" v-model="workcase.username" placeholder="请输入客户姓名" />
|
||||||
<text v-else class="form-value">{{ workcase.username || '-' }}</text>
|
<text v-else class="form-value">{{ workcase.username || '-' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 联系电话 -->
|
<!-- 联系电话 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label">联系电话</text>
|
<text class="form-label">联系电话<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
<input v-if="mode === 'create'" class="form-input" v-model="workcase.phone" placeholder="请输入联系电话" />
|
<input v-if="mode === 'create'" class="form-input" v-model="workcase.phone" placeholder="请输入联系电话" />
|
||||||
<text v-else class="form-value">{{ workcase.phone || '-' }}</text>
|
<text v-else class="form-value">{{ workcase.phone || '-' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 设备名称 -->
|
<!-- 设备名称 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label">设备名称</text>
|
<text class="form-label">设备名称<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
<input v-if="mode === 'create'" class="form-input" v-model="workcase.device" placeholder="请输入设备名称" />
|
<input v-if="mode === 'create'" class="form-input" v-model="workcase.device" placeholder="请输入设备名称" />
|
||||||
<text v-else class="form-value">{{ workcase.device || '-' }}</text>
|
<text v-else class="form-value">{{ workcase.device || '-' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 故障类型 -->
|
<!-- 故障类型 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label">故障类型</text>
|
<text class="form-label">故障类型<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
<picker v-if="mode === 'create'" class="form-picker" :value="typeIndex" :range="faultTypes" @change="onTypeChange">
|
<picker v-if="mode === 'create'" class="form-picker" :value="typeIndex" :range="faultTypes" @change="onTypeChange">
|
||||||
<view class="picker-content">
|
<view class="picker-content">
|
||||||
<text class="picker-text">{{ workcase.type || '请选择故障类型' }}</text>
|
<text class="picker-text">{{ workcase.type || '请选择故障类型' }}</text>
|
||||||
@@ -81,30 +81,46 @@
|
|||||||
|
|
||||||
<!-- 现场地址 -->
|
<!-- 现场地址 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label">现场地址</text>
|
<text class="form-label">现场地址<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
<input v-if="mode === 'create'" class="form-input" v-model="workcase.address" placeholder="请输入现场地址" />
|
<input v-if="mode === 'create'" class="form-input" v-model="workcase.address" placeholder="请输入现场地址" />
|
||||||
<text v-else class="form-value">{{ workcase.address || '-' }}</text>
|
<text v-else class="form-value">{{ workcase.address || '-' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 故障描述 -->
|
<!-- 故障描述 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label">故障描述</text>
|
<text class="form-label">故障描述<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
<textarea v-if="mode === 'create'" class="form-textarea" v-model="workcase.description" placeholder="请详细描述故障现象..." maxlength="500" />
|
<textarea v-if="mode === 'create'" class="form-textarea" v-model="workcase.description" placeholder="请详细描述故障现象..." maxlength="500" />
|
||||||
<text v-else class="form-value">{{ workcase.description || '-' }}</text>
|
<text v-else class="form-value">{{ workcase.description || '-' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 设备铭牌 -->
|
<!-- 设备铭牌 -->
|
||||||
<view class="form-item" v-if="mode !== 'create'">
|
<view class="form-item">
|
||||||
<text class="form-label">设备铭牌</text>
|
<text class="form-label">设备铭牌</text>
|
||||||
<text class="form-value">{{ workcase.deviceNamePlate || '-' }}</text>
|
<input v-if="mode === 'create'" class="form-input" v-model="workcase.deviceNamePlate" placeholder="请输入设备铭牌"/>
|
||||||
|
<text v-else class="form-value">{{ workcase.deviceNamePlate || '-' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 铭牌照片 -->
|
<!-- 铭牌照片 -->
|
||||||
<view class="form-item" v-if="mode !== 'create' && workcase.deviceNamePlateImg">
|
<view class="form-item">
|
||||||
<text class="form-label">铭牌照片</text>
|
<text class="form-label">铭牌照片<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
<view class="nameplate-photo" @tap="previewNameplateImage">
|
<!-- 创建模式:上传铭牌 -->
|
||||||
<image class="nameplate-image" :src="workcase.deviceNamePlateImg" mode="aspectFit" />
|
<view v-if="mode === 'create'" class="nameplate-upload">
|
||||||
|
<view v-if="workcase.deviceNamePlateImg" class="nameplate-preview" @tap="previewNameplateImage">
|
||||||
|
<image class="nameplate-image" :src="getImageUrl(workcase.deviceNamePlateImg)" mode="aspectFit" />
|
||||||
|
<view class="nameplate-delete" @tap.stop="deleteNameplateImage">
|
||||||
|
<text class="delete-icon">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else class="nameplate-placeholder" @tap="chooseNameplateImage">
|
||||||
|
<text class="upload-plus">+</text>
|
||||||
|
<text class="upload-text">上传设备铭牌</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 查看模式:显示铭牌 -->
|
||||||
|
<view v-else-if="workcase.deviceNamePlateImg" class="nameplate-photo" @tap="previewNameplateImage">
|
||||||
|
<image class="nameplate-image" :src="getImageUrl(workcase.deviceNamePlateImg)" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<text v-else class="form-value">-</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 处理人 -->
|
<!-- 处理人 -->
|
||||||
@@ -125,11 +141,11 @@
|
|||||||
<view class="section">
|
<view class="section">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<view class="title-icon">📷</view>
|
<view class="title-icon">📷</view>
|
||||||
<text class="title-text">故障图片</text>
|
<text class="title-text">故障图片<text class="required" v-if="mode === 'create'">*</text></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="photos-grid">
|
<view class="photos-grid">
|
||||||
<view class="photo-item" v-for="(img, index) in workcase.imgs" :key="index" @tap="previewFaultImages(index)">
|
<view class="photo-item" v-for="(img, index) in workcase.imgs" :key="index" @tap="previewFaultImages(index)">
|
||||||
<image class="photo-image" :src="img" mode="aspectFill" />
|
<image class="photo-image" :src="getImageUrl(img)" mode="aspectFill" />
|
||||||
<view v-if="mode === 'create'" class="photo-delete" @tap.stop="deleteFaultImage(index)">
|
<view v-if="mode === 'create'" class="photo-delete" @tap.stop="deleteFaultImage(index)">
|
||||||
<text class="delete-icon">×</text>
|
<text class="delete-icon">×</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -192,7 +208,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import type { TbWorkcaseDTO } from '@/types/workcase'
|
import type { TbWorkcaseDTO, TbWorkcaseProcessDTO } from '@/types/workcase'
|
||||||
|
import { workcaseAPI, fileAPI } from '@/api'
|
||||||
|
|
||||||
// 接口定义
|
// 接口定义
|
||||||
interface ProcessItem {
|
interface ProcessItem {
|
||||||
@@ -216,49 +233,20 @@ const emergencies = ref<string[]>(['普通', '紧急'])
|
|||||||
const emergencyIndex = ref<number>(0)
|
const emergencyIndex = ref<number>(0)
|
||||||
|
|
||||||
// 工单数据
|
// 工单数据
|
||||||
const workcase = ref<TbWorkcaseDTO>({
|
const workcase = ref<TbWorkcaseDTO>({})
|
||||||
workcaseId: 'W0202501130001',
|
|
||||||
userId: '1',
|
|
||||||
username: '张伟',
|
|
||||||
phone: '138****5678',
|
|
||||||
type: '电气系统故障',
|
|
||||||
device: 'TH-500GF',
|
|
||||||
deviceCode: 'TH-500GF-2023-001',
|
|
||||||
deviceNamePlate: 'TH-500GF',
|
|
||||||
deviceNamePlateImg: 'https://via.placeholder.com/400x300?text=设备铭牌',
|
|
||||||
address: '江西省南昌市红谷滩区xxx路xxx号',
|
|
||||||
description: '发电机启动后电压不稳定,波动范围较大,影响正常使用',
|
|
||||||
emergency: 'emergency',
|
|
||||||
status: 'processing',
|
|
||||||
processorName: '小李',
|
|
||||||
createTime: '2025-01-13 09:30:00',
|
|
||||||
imgs: []
|
|
||||||
})
|
|
||||||
|
|
||||||
// 处理记录
|
// 处理记录
|
||||||
const processList = ref<ProcessItem[]>([
|
const processList = ref<ProcessItem[]>([])
|
||||||
{
|
|
||||||
status: 'engineer',
|
// 获取图片 URL(通过 fileId)
|
||||||
actor: '小李',
|
function getImageUrl(fileId: string): string {
|
||||||
action: '开始处理',
|
// 如果已经是完整 URL,直接返回
|
||||||
desc: '已联系客户,计划今日上门检修',
|
if (fileId.startsWith('http://') || fileId.startsWith('https://')) {
|
||||||
time: '2025-01-13 08:15:00'
|
return fileId
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 'manager',
|
|
||||||
actor: '管理员',
|
|
||||||
action: '指派工程师',
|
|
||||||
desc: '指派给小李工程师处理',
|
|
||||||
time: '2025-01-12 15:00:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 'system',
|
|
||||||
actor: '系统',
|
|
||||||
action: '工单创建',
|
|
||||||
desc: '客户通过小电对话提交',
|
|
||||||
time: '2025-01-12 14:20:00'
|
|
||||||
}
|
}
|
||||||
])
|
// 否则通过 fileId 构建下载 URL
|
||||||
|
return fileAPI.getDownloadUrl(fileId)
|
||||||
|
}
|
||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onLoad((options: any) => {
|
onLoad((options: any) => {
|
||||||
@@ -273,6 +261,8 @@ onLoad((options: any) => {
|
|||||||
|
|
||||||
let username = ''
|
let username = ''
|
||||||
let phone = ''
|
let phone = ''
|
||||||
|
let userId = ''
|
||||||
|
let roomId = options.roomId || ''
|
||||||
|
|
||||||
if (loginDomainRaw) {
|
if (loginDomainRaw) {
|
||||||
// 如果是字符串,需要先解析
|
// 如果是字符串,需要先解析
|
||||||
@@ -282,19 +272,17 @@ onLoad((options: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 尝试多种可能的字段路径
|
// 尝试多种可能的字段路径
|
||||||
username = loginDomain.userInfo?.username ||
|
username = loginDomain.userInfo?.username
|
||||||
loginDomain.user?.username ||
|
|
||||||
loginDomain.username || ''
|
|
||||||
|
|
||||||
phone = loginDomain.user?.phone ||
|
phone = loginDomain.user?.phone
|
||||||
loginDomain.userInfo?.phone ||
|
userId = loginDomain.userInfo?.userId
|
||||||
loginDomain.phone || ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建模式,初始化表单并填充用户信息
|
// 创建模式,初始化表单并填充用户信息
|
||||||
workcase.value = {
|
workcase.value = {
|
||||||
username: username,
|
username: username,
|
||||||
phone: phone,
|
phone: phone,
|
||||||
|
userId: userId,
|
||||||
device: '',
|
device: '',
|
||||||
type: '',
|
type: '',
|
||||||
address: '',
|
address: '',
|
||||||
@@ -302,6 +290,11 @@ onLoad((options: any) => {
|
|||||||
emergency: 'normal',
|
emergency: 'normal',
|
||||||
imgs: []
|
imgs: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有 roomId,添加到工单数据中
|
||||||
|
if (roomId) {
|
||||||
|
workcase.value.roomId = roomId
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('读取用户信息失败:', error)
|
console.error('读取用户信息失败:', error)
|
||||||
// 初始化空表单
|
// 初始化空表单
|
||||||
@@ -315,17 +308,18 @@ onLoad((options: any) => {
|
|||||||
emergency: 'normal',
|
emergency: 'normal',
|
||||||
imgs: []
|
imgs: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有 roomId,添加到工单数据中
|
||||||
|
if (options.roomId) {
|
||||||
|
workcase.value.roomId = options.roomId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (options.workcaseId) {
|
} else if (options.workcaseId) {
|
||||||
// 查看模式
|
// 查看模式
|
||||||
workcaseId.value = options.workcaseId
|
workcaseId.value = options.workcaseId
|
||||||
|
mode.value = 'view'
|
||||||
loadWorkcaseDetail(workcaseId.value)
|
loadWorkcaseDetail(workcaseId.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理 roomId 参数
|
|
||||||
if (options.roomId) {
|
|
||||||
// TODO: 可以将 roomId 关联到工单
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -349,8 +343,42 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 加载工单详情
|
// 加载工单详情
|
||||||
function loadWorkcaseDetail(id: string) {
|
async function loadWorkcaseDetail(id: string) {
|
||||||
// TODO: 调用 workcaseAPI.getWorkcaseById(id) 获取数据
|
uni.showLoading({ title: '加载中...' })
|
||||||
|
try {
|
||||||
|
const res = await workcaseAPI.getWorkcaseById(id)
|
||||||
|
if (res.success && res.data) {
|
||||||
|
workcase.value = res.data
|
||||||
|
// 加载处理记录
|
||||||
|
await loadProcessList(id)
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: res.message || '加载失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载工单详情失败:', error)
|
||||||
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||||
|
} finally {
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载处理记录
|
||||||
|
async function loadProcessList(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await workcaseAPI.getWorkcaseProcessList({ workcaseId: id })
|
||||||
|
if (res.success && res.dataList) {
|
||||||
|
// 转换为界面需要的格式
|
||||||
|
processList.value = res.dataList.map((item: TbWorkcaseProcessDTO) => ({
|
||||||
|
status: item.processorType === 'system' ? 'system' : item.processorType === 'manager' ? 'manager' : 'engineer',
|
||||||
|
actor: item.processorName || '未知',
|
||||||
|
action: item.action || '',
|
||||||
|
desc: item.description || '',
|
||||||
|
time: item.createTime || ''
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载处理记录失败:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态样式类
|
// 获取状态样式类
|
||||||
@@ -428,16 +456,43 @@ function onEmergencyChange(e: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 选择故障图片
|
// 选择故障图片
|
||||||
function chooseFaultImages() {
|
async function chooseFaultImages() {
|
||||||
uni.chooseImage({
|
uni.chooseImage({
|
||||||
count: 9 - (workcase.value.imgs?.length || 0),
|
count: 9 - (workcase.value.imgs?.length || 0),
|
||||||
sizeType: ['compressed'],
|
sizeType: ['compressed'],
|
||||||
sourceType: ['camera', 'album'],
|
sourceType: ['camera', 'album'],
|
||||||
success: (res) => {
|
success: async (res) => {
|
||||||
if (!workcase.value.imgs) {
|
if (!workcase.value.imgs) {
|
||||||
workcase.value.imgs = []
|
workcase.value.imgs = []
|
||||||
}
|
}
|
||||||
workcase.value.imgs.push(...res.tempFilePaths)
|
|
||||||
|
// 上传图片到服务器
|
||||||
|
uni.showLoading({ title: '上传中...' })
|
||||||
|
try {
|
||||||
|
const uploadPromises = res.tempFilePaths.map(filePath =>
|
||||||
|
fileAPI.uploadFile(filePath, {
|
||||||
|
module: 'workcase',
|
||||||
|
optsn: workcase.value.workcaseId || 'temp'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const results = await Promise.all(uploadPromises)
|
||||||
|
|
||||||
|
// 使用 fileId 添加图片
|
||||||
|
results.forEach(result => {
|
||||||
|
if (result.success && result.data?.fileId) {
|
||||||
|
// 存储 fileId,用于提交时使用
|
||||||
|
workcase.value.imgs!.push(result.data.fileId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '上传成功', icon: 'success' })
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('上传图片失败:', error)
|
||||||
|
uni.showToast({ title: '上传失败', icon: 'none' })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.log('选择图片失败:', err)
|
console.log('选择图片失败:', err)
|
||||||
@@ -452,11 +507,52 @@ function deleteFaultImage(index: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 选择铭牌照片
|
||||||
|
async function chooseNameplateImage() {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
sourceType: ['camera', 'album'],
|
||||||
|
success: async (res) => {
|
||||||
|
// 上传铭牌照片到服务器
|
||||||
|
uni.showLoading({ title: '上传中...' })
|
||||||
|
try {
|
||||||
|
const result = await fileAPI.uploadFile(res.tempFilePaths[0], {
|
||||||
|
module: 'workcase',
|
||||||
|
optsn: workcase.value.workcaseId || 'temp'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.success && result.data?.fileId) {
|
||||||
|
workcase.value.deviceNamePlateImg = result.data.fileId
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '上传成功', icon: 'success' })
|
||||||
|
} else {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({ title: '上传失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('上传铭牌照片失败:', error)
|
||||||
|
uni.showToast({ title: '上传失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log('选择图片失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除铭牌照片
|
||||||
|
function deleteNameplateImage() {
|
||||||
|
workcase.value.deviceNamePlateImg = ''
|
||||||
|
}
|
||||||
|
|
||||||
// 预览铭牌照片
|
// 预览铭牌照片
|
||||||
function previewNameplateImage() {
|
function previewNameplateImage() {
|
||||||
if (!workcase.value.deviceNamePlateImg) return
|
if (!workcase.value.deviceNamePlateImg) return
|
||||||
|
const imageUrl = getImageUrl(workcase.value.deviceNamePlateImg)
|
||||||
uni.previewImage({
|
uni.previewImage({
|
||||||
urls: [workcase.value.deviceNamePlateImg],
|
urls: [imageUrl],
|
||||||
current: 0
|
current: 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -464,38 +560,79 @@ function previewNameplateImage() {
|
|||||||
// 预览故障图片
|
// 预览故障图片
|
||||||
function previewFaultImages(index: number) {
|
function previewFaultImages(index: number) {
|
||||||
if (!workcase.value.imgs || workcase.value.imgs.length === 0) return
|
if (!workcase.value.imgs || workcase.value.imgs.length === 0) return
|
||||||
|
// 将 fileId 数组转换为 URL 数组
|
||||||
|
const imageUrls = workcase.value.imgs.map(fileId => getImageUrl(fileId))
|
||||||
uni.previewImage({
|
uni.previewImage({
|
||||||
urls: workcase.value.imgs,
|
urls: imageUrls,
|
||||||
current: index
|
current: index
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交工单
|
// 提交工单
|
||||||
function submitWorkcase() {
|
async function submitWorkcase() {
|
||||||
// 校验必填项
|
// 校验必填项
|
||||||
if (!workcase.value.username || !workcase.value.phone || !workcase.value.description) {
|
if (!workcase.value.username) {
|
||||||
uni.showToast({
|
uni.showToast({ title: '请输入客户姓名', icon: 'none' })
|
||||||
title: '请填写必填项',
|
return
|
||||||
icon: 'none'
|
}
|
||||||
})
|
if (!workcase.value.phone) {
|
||||||
|
uni.showToast({ title: '请输入联系电话', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!workcase.value.device) {
|
||||||
|
uni.showToast({ title: '请输入设备名称', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!workcase.value.type) {
|
||||||
|
uni.showToast({ title: '请选择故障类型', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!workcase.value.address) {
|
||||||
|
uni.showToast({ title: '请输入现场地址', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!workcase.value.description) {
|
||||||
|
uni.showToast({ title: '请输入故障描述', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!workcase.value.deviceNamePlateImg) {
|
||||||
|
uni.showToast({ title: '请上传设备铭牌照片', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!workcase.value.imgs || workcase.value.imgs.length === 0) {
|
||||||
|
uni.showToast({ title: '请至少上传一张故障图片', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uni.showLoading({
|
uni.showLoading({ title: '提交中...' })
|
||||||
title: '提交中...'
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: 调用 API 提交工单
|
try {
|
||||||
setTimeout(() => {
|
// 调用 API 提交工单
|
||||||
|
const res = await workcaseAPI.createWorkcase(workcase.value)
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (res.success && res.data) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '工单创建成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.message || '创建失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('提交工单失败:', error)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '工单创建成功',
|
title: '提交失败,请重试',
|
||||||
icon: 'success'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
}
|
||||||
uni.navigateBack()
|
|
||||||
}, 1500)
|
|
||||||
}, 1000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回上一页
|
// 返回上一页
|
||||||
|
|||||||
40
urbanLifelineWeb/packages/workcase_wechat/types/file/file.ts
Normal file
40
urbanLifelineWeb/packages/workcase_wechat/types/file/file.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 文件服务相关 types - 根据后端 VO 和 DTO 转换
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { BaseDTO } from "../base"
|
||||||
|
|
||||||
|
// TbSysFileDTO - 系统文件DTO
|
||||||
|
export interface TbSysFileDTO extends BaseDTO {
|
||||||
|
/** 文件ID (主键) */
|
||||||
|
fileId?: string
|
||||||
|
/** 文件名 */
|
||||||
|
name?: string
|
||||||
|
/** 文件路径 */
|
||||||
|
path?: string
|
||||||
|
/** 文件大小(字节) */
|
||||||
|
size?: number
|
||||||
|
/** 文件类型 */
|
||||||
|
type?: string
|
||||||
|
/** 存储类型 */
|
||||||
|
storageType?: string
|
||||||
|
/** MIME 类型 */
|
||||||
|
mimeType?: string
|
||||||
|
/** 文件访问 URL */
|
||||||
|
url?: string
|
||||||
|
/** 文件状态 */
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// uni-app 文件上传参数(不使用 File 对象,使用文件路径)
|
||||||
|
export interface FileUploadParam {
|
||||||
|
module?: string
|
||||||
|
optsn?: string
|
||||||
|
uploader?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BatchFileUploadParam {
|
||||||
|
module?: string
|
||||||
|
optsn?: string
|
||||||
|
uploader?: string
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./file"
|
||||||
@@ -4,4 +4,5 @@ export * from "./workcase"
|
|||||||
export * from "./auth"
|
export * from "./auth"
|
||||||
export * from "./response"
|
export * from "./response"
|
||||||
export * from "./page"
|
export * from "./page"
|
||||||
export * from "./ai"
|
export * from "./ai"
|
||||||
|
export * from "./file"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { BaseDTO } from '@/types/base'
|
import type { BaseDTO } from '../base'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工单表对象
|
* 工单表对象
|
||||||
@@ -6,14 +6,16 @@ import type { BaseDTO } from '@/types/base'
|
|||||||
export interface TbWorkcaseDTO extends BaseDTO {
|
export interface TbWorkcaseDTO extends BaseDTO {
|
||||||
/** 工单ID */
|
/** 工单ID */
|
||||||
workcaseId?: string
|
workcaseId?: string
|
||||||
|
/** 聊天室ID */
|
||||||
|
roomId?: string
|
||||||
/** 来客ID */
|
/** 来客ID */
|
||||||
userId?: string
|
userId?: string
|
||||||
/** 来客姓名 */
|
/** 来客姓名 */
|
||||||
username?: string
|
username: string
|
||||||
/** 来客电话 */
|
/** 来客电话 */
|
||||||
phone?: string
|
phone: string
|
||||||
/** 故障类型 */
|
/** 故障类型 */
|
||||||
type?: string
|
type: string
|
||||||
/** 设备名称 */
|
/** 设备名称 */
|
||||||
device?: string
|
device?: string
|
||||||
/** 设备代码 */
|
/** 设备代码 */
|
||||||
@@ -21,7 +23,7 @@ export interface TbWorkcaseDTO extends BaseDTO {
|
|||||||
/** 设备名称牌 */
|
/** 设备名称牌 */
|
||||||
deviceNamePlate?: string
|
deviceNamePlate?: string
|
||||||
/** 设备名称牌图片 */
|
/** 设备名称牌图片 */
|
||||||
deviceNamePlateImg?: string
|
deviceNamePlateImg: string
|
||||||
/** 地址 */
|
/** 地址 */
|
||||||
address?: string
|
address?: string
|
||||||
/** 故障描述 */
|
/** 故障描述 */
|
||||||
|
|||||||
Reference in New Issue
Block a user