HTTP请求相关
import { http } from "../utils";
/** 返回的数据格式 */
interface ResponseData<T> {
data: T;
success: boolean;
message: string;
}
/** 首页 - start */
/**
* 获取安灯请求类型列表
* @returns 返回的是安灯请求类型列表,里面的item是一个对象,包含id、name、imageUrl三个字段
*/
export const getAndonRequestTypeList = (params?: { name: string }) =>
http.get<ResponseData<{ id: number; name: string; imageUrl: string[] }[]>>(
"/andon/request/type/list",
{ params }
);
/**
* 获取处理人员列表
* @returns 返回的是处理人员列表,里面的item是一个对象,包含id和name两个字段
*/
export const getAndonHandlerList = (name: string) =>
http.get<ResponseData<{ id: number; name: string }[]>>("/andon/handler/list", {
params: { name }
});
/** 提交记录列表里面的item */
interface andonSubmitListItem {
/** 安灯请求id */
id: string;
/** 安灯工位 */
station: string;
/** 安灯类型 */
requestType: string;
/** 说明 */
description: string;
/** 附件 */
attachment: string;
/** 附件的copy变量,attachment与后端固有字段冲突,故添加 */
attachmentList: string[];
/** 处理人员 */
handler: string;
/** 发起时间 */
createTime: string;
}
/** 获取提交记录列表 */
export const getAndonSubmitList = (params: { name: string }) =>
http.get<ResponseData<andonSubmitListItem[]>>("/andon/request/history/list", { params });
/**
* 获取单号列表
* @param params 【可能有,也可能没有参数,待定】
* @returns 返回的是单号列表,里面的item是一个对象,包含id、label、value三个字段
*/
export const getAndonRequestNoList = (params?: any) =>
http.get<ResponseData<{ id: number; label: string; value: string }[]>>("/andon/request/no/list", {
params
});
/** 安灯请求的入参 */
interface andonRequestParams {
/** 请求发起人的名字 */
name?: string;
/** 安灯请求类型 */
requestType: string;
/** 处理人员 - 数组里面放的是发起请求时勾选的人员name */
handlerList: string[];
/** 安灯请求携带的单号 */
requestNo: string;
/** 是否同步到微信 */
isWechatNotifyEnable: boolean;
/** 说明 */
description: string;
/** 附件 - 数组里面存放的是照片路径对应字符串 */
attachmentList: string[];
}
/**
* 上传拍摄的照片
* @param data 图片二进制流的表单数据
* @returns 返回的是照片的路径字符串【塞data里面】
*/
export const uploadPhoto = (data: FormData) =>
http.post<ResponseData<string>>("/andon/request/image/upload", data, {
headers: { "Content-Type": "multipart/form-data" }
});
/**
* 删除上传的照片
*/
export const deletePhoto = (params: { path: string }) =>
http.delete<ResponseData<void>>("/andon/request/image/delete", { params });
/**
* 确认发起安灯请求
* @param data 安灯请求的入参
* @returns 【这里的void,表明放在data中的数据为null即可】
*/
export const confirmAddAndonRequest = (data: andonRequestParams) =>
http.post<ResponseData<void>>("/andon/request/add", data);
/**
* 是否有待处理的安灯请求
* @param name 传入的是当前操作人员的名称
* @returns boolean值,true表示有待处理的安灯请求,false表示没有
*/
export const hasPendingAndonRequest = (params: { name: string }) =>
http.get<ResponseData<boolean>>("/andon/request/has-pending", { params });
/** 首页 - end */
/** 安灯请求处理页 - start */
/** 安灯请求列表里面的item构成 */
interface andonRequestItem {
/** 安灯请求id */
id: string;
/** 工位名称 放在最上面的地方 */
station: string;
/** 安灯请求类型 */
requestType: string;
/** 发起人员 */
initiator: string[];
/** 处理人员 - 数组里面放的是发起请求时勾选的人员id */
handler: number[];
/** 安灯请求携带的单号 */
requestNo: string;
/** 是否同步到微信 */
isWechatNotifyEnable: boolean;
/** 说明 */
description: string;
/** 附件 */
attachment: string;
/** 附件的copy变量,attachment与后端固有字段冲突,故添加 */
attachmentList: string[];
/** 发起时间 */
createTime: string;
/** 是否紧急 */
isUrgent: boolean;
}
/** 历史记录列表里面的item */
interface andonHistoryItem {
/** 安灯请求id */
id: string;
/** 安灯工位 */
station: string;
/** 安灯类型 */
requestType: string;
/** 说明 */
description: string;
/** 附件 */
attachment: string;
/** 附件的copy变量,attachment与后端固有字段冲突,故添加 */
attachmentList: string[];
/** 发起人员 */
initiator: string;
/** 发起时间 */
createTime: string;
}
/**
* 获取历史记录列表
* @param name 传入的是当前操作人员的名称
* @returns 返回的是历史记录列表,里面的item是一个对象,包含id、station、requestType、description、attachment、initiator、createTime等字段【andonHistoryItem】
*/
export const getAndonHistoryList = (params?: { name: string }) =>
http.get<ResponseData<andonHistoryItem[]>>("/andon/receive/history/list", { params });
/**
* 获取安灯请求列表
* @param name 传入的是当前操作人员的名称,用于筛选出当前操作人员需要处理的安灯请求
*/
export const getAndonRequestList = (params: { name: string }) =>
http.get<ResponseData<andonRequestItem[]>>("/andon/request/list", { params });
/** 确认他人发来的安灯请求
* - 会传一个是否保留的参数 【不保留=> 这条安灯请求就移动到这个人的历史记录里面,保留=>无事发生】
* @param id 安灯请求id
* @param isHold 是否保留
* @param name 发起请求的这个人
*/
export const confirmAndonRequest = (params: { id: string; isHold: boolean; name?: string }) =>
http.post<ResponseData<void>>("/andon/request/confirm", params);
/** 安灯请求处理页 - end */
/** 安灯设置页 - start */
/** 安灯类型设置列表里面的item */
interface andonTypeItem {
/** 安灯类型id */
id: number;
/** 安灯类型名称 */
name: string;
/** 编辑时用到的名称 - 【前端用到,他的值默认是name的值】 */
tempName: string;
/** 是否启用 */
enable: boolean;
/** 顺序 */
orderId: number;
/** 是否处于编辑状态 - 【前端用到】 */
isEdit: boolean;
/** 说明图片 */
imageUrl: string[];
}
/** 获取安灯类型设置列表 */
export const getAndonTypeList = () =>
http.get<ResponseData<andonTypeItem[]>>("/andon/setting/type/list");
/**
* 新增安灯类型设置
* @param name 安灯类型名称
* @param enable 是否启用
*/
export const addAndonType = (data: andonTypeItem) =>
http.post<ResponseData<void>>("/andon/setting/type/add", data);
/**
* 修改安灯类型设置的入参
* @param ?问号表示这个参数不一定传【可选参数】,
* @param orderId 是修改这个安灯类型设置列表中 item 的顺序,会影响“/andon/request/type/list”这个接口里面返回的 item 顺序
*/
interface editAndonTypeParams {
/** 安灯类型id */
id: number;
/** 安灯类型名称 */
name?: string;
/** 是否启用 */
enable?: boolean;
/** 原来的顺序 */
oldIndex?: number;
/** 新的顺序 */
newIndex?: number;
}
/**
* 修改安灯类型设置
* @param id 安灯类型id
* @param name 安灯类型名称
* @param enable 是否启用
* @param oldIndex 原来的顺序
* @param newIndex 新的顺序
*/
export const editAndonType = (data: editAndonTypeParams) =>
http.post<ResponseData<void>>("/andon/setting/type/edit", data);
/**
* 删除安灯类型设置
* @param id 安灯类型id
*/
export const deleteAndonType = (params: { id: string }) =>
http.delete<ResponseData<void>>("/andon/setting/type/delete/" + params.id);
/**
* 安灯类型说明图片上传
* 【实际调用由组件进行,仅在此处说明有这个接口】
* @param data 图片二进制流的表单数据【FormData】
* @returns 返回的是图片的路径字符串【塞data里面】
*/
export const uploadAndonTypeDescriptionImage = (data: FormData) =>
http.post<ResponseData<string>>("/andon/setting/type/image/upload", data, {
headers: { "Content-Type": "multipart/form-data" }
});
/**
* 删除安灯类型说明图片
* @param path 图片路径
* @returns 返回的是void
* @description 由于后端没有返回值,所以这里的返回值是void
*/
export const deleteAndonTypeDescriptionImage = (id: string, path: string) =>
http.delete<ResponseData<void>>("/andon/setting/type/image/delete/" + id, { params: { path } });
/** 安灯工位设置列表里面的item */
interface andonStationItem {
/** 安灯工位id */
id: number;
/** 安灯工位名称 */
stationName: string;
/** 编辑时用到的名称 - 【前端用到,他的值默认是name的值】 */
tempStationName: string;
/** 安灯工位编号 */
stationNo: string;
/** 编辑时用到的工位编号 - 【前端用到,他的值默认是stationNo的值】 */
tempStationNo: string;
/** 人员 */
personnel: string;
/** 编辑时用到的人员 - 【前端用到,他的值默认是personnel的值】 */
tempPersonnel: string;
/** 当前工位 - 还没想好怎么弄 你自己看着办【也许放当前登录人的sessionId,如果说后面有人又点了同一个那么把之前的人挤掉,然后让新的人登录】 */
currentStation: any;
/** 是否处于编辑状态 - 【前端用到】 */
isEdit: boolean;
}
/** 获取安灯工位设置列表
* @returns 返回的是安灯工位设置列表,里面的item是一个对象,包含id、stationName、tempStationName、stationNo、tempStationNo、personnel、tempPersonnel、currentStation、isEdit等字段
*/
export const getAndonStationList = () =>
http.get<ResponseData<andonStationItem[]>>("/andon/setting/station/list");
/** 新增安灯工位设置
* @param name 安灯工位名称
* @param code 安灯工位编号
* @param personnel 人员
*/
export const addAndonStation = (data: andonStationItem) =>
http.post<ResponseData<void>>("/andon/setting/station/add", data);
/** 修改安灯工位设置 */
export const editAndonStation = (data: andonStationItem) =>
http.post<ResponseData<void>>("/andon/setting/station/edit", data);
/**
* 点击当前工位设置
* - 相当于就是登录操作,后端要在响应头中返回一个set-cookie来设置sessionId及其其他参数,
* - 后续发起的请求都会携带这个参数。以便区分当前操作的是哪个人
* @param id 安灯工位id
* @returns 返回这个人的信息给我,然后我就可以根据这个人的信息去请求其他接口了
*/
export const clickCurrentStation = (params: { id: string }) =>
http.get<ResponseData<andonStationItem>>("/andon/setting/station/current", { params });
/**
* 删除安灯工位设置
* @param id 安灯工位id
*/
export const deleteAndonStation = (params: { id: string }) =>
http.delete<ResponseData<void>>("/andon/setting/station/delete/" + params.id);
/** 安灯设置页 - end */
/** 微信扫码登记相关 - start */
/** 获取公司ID */
export const getCompanyIdForWeChatBinding = () =>
http.get<ResponseData<string>>("/andon/wechat/company-id");
/**
* 确认扫码登记参数
*/
interface wechatScanRegisterParams {
/** 扫码获取的code,由wechat认证返回 */
code: string;
/** 昵称 */
nickName: string;
/** 真实姓名 */
realName: string;
/** 手机号码 */
phone: string;
/** 工位名称 */
stationName: string;
/** 工位编号 */
stationNo: string;
}
/** 确认扫码登记*/
export const confirmScanRegister = (params: wechatScanRegisterParams) =>
http.post<ResponseData<void>>("/andon/wechat/bind", params);
/** 微信扫码登记相关 - end */
WebSocket相关
import { h, ref } from "vue";
import { defineStore } from "pinia";
import { wsApi } from "@/api/utils";
import { useRoute, useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
/** 封装一下log,简洁一点 */
class LOG {
private static _(color: string, title: string, message = "") {
console.log(
`%c ${title} %c ${message}${message ? " %c" : ""}`,
`background: ${color}; border: 1px solid ${color}; padding: 4px 3px; border-radius: ${message ? "4px 0 0 4px" : "4px"}; color: #fff;`,
message
? `border: 1px solid ${color}; padding: 4px 3px; border-radius: 0 4px 4px 0; color: ${color}; font-weight: bold;`
: "",
message ? "background: transparent;" : ""
);
}
static success(title: string, message = "") {
LOG._("#4caf50", title, message);
}
static warning(title: string, message = "") {
LOG._("#ff9800", title, message);
}
static error(title: string, message = "") {
LOG._("#f44336", title, message);
}
static primary(title: string, message = "") {
LOG._("#2196f3", title, message);
}
static info(title: string, message = "") {
LOG._("#90a4ae", title, message);
}
}
// websocket store
export const useWebSocketStore = defineStore("webSocket", () => {
const ws = ref(null);
const interval = ref(null);
const timeout = ref(null);
const userId = ref(null);
const reconnectInterval = ref(1000); // 初始重连间隔
const heartbeatInterval = ref(45000); // 心跳包间隔
const maxReconnectInterval = ref(30000); // 最大重连间隔
const serialPort = ref(null);
const route = useRoute();
const router = useRouter();
if (sessionStorage.getItem("AndonCurrentStation")) {
const userInfo = JSON.parse(sessionStorage.getItem("AndonCurrentStation") as string);
userId.value = userInfo.id;
}
const flags = ref({
refreshRequestMap: 0, // 用于刷新请求列表,也是一个标识,触发页面的watch,让他重新请求数据(当当前页面处于接收页的时候)
openSerialPortDevTools: 0, // 打开串口设备调试工具
webSocketUserIdChanged: false, // webSocket连接的userId发生变化,需要重新连接
isUnloading: false, // 是否正在卸载页面
serialPortDevDialogStatus: false, // 串口设备调试工具的弹窗状态
isSerialConnectionTipsShowed: false, // 是否已经显示过串口连接提示
isServerDisconnectActivelyTipsShowed: false // 服务端主动断开连接的提示是否已经显示
});
/** 清理 */
window.onbeforeunload = () => {
flags.value.isUnloading = true;
ws.value?.close();
clearInterval(interval.value);
clearTimeout(timeout.value);
};
/** 开始进行webSocket连接 */
function webSocketConnect(id: string | number) {
// 如果传来的ID和之前的不一样,先把上次的连接关闭,如果存在的话
if (id !== userId.value) {
flags.value.isUnloading = true;
ws.value?.close();
flags.value.webSocketUserIdChanged = true;
} else {
flags.value.webSocketUserIdChanged = false;
}
ws.value = new WebSocket(wsApi() + id);
userId.value = id;
initWebSocket();
}
/** 具体webSocket连接逻辑 */
function initWebSocket() {
ws.value.onopen = onopen;
ws.value.onmessage = onmessage;
ws.value.onclose = onclose;
ws.value.onerror = onerror;
keepWebSocketConnectionAlive();
}
/** webSocket连接成功 */
function onopen() {
LOG.primary("开始WebSocket连接", wsApi() + userId.value);
LOG.success("WebSocket连接成功");
ws.value.send(JSON.stringify({ type: "connected", msg: "客户端连接成功" }));
}
/** webSocket接收消息 */
function onmessage(evt) {
let { type, msg } = (evt.data as string).startsWith("{")
? JSON.parse(evt.data)
: { type: "default", msg: evt.data };
LOG.primary("接收到消息", `类型:${type} 内容:${msg}`);
switch (type) {
case "incomingRequest": // 收到新的请求,那么跳到接收页面
if (route.name === "receive") {
ElMessage.success("收到新的请求,已刷新请求列表");
flags.value.refreshRequestMap++; // 如果当前页面是接收页面,那么直接请求数据
} else {
handleIncomingMessage("接收到新的请求,是否跳转到处理页面?"); // 如果当前页面不是接收页面,那么弹出提示框,让用户选择是否跳转到接收页面
}
break;
case "pendingRequest": // 登录以后,如果有待处理的请求,那么跳到接收页面
if (route.name === "receive") {
ElMessage.success("收到新的请求,已更新请求列表");
flags.value.refreshRequestMap++;
} else {
handleIncomingMessage("收到新的待处理请求,是否跳转到处理页面?");
}
break;
case "towerLight": // 控制塔灯
try {
!Array.isArray(msg) && (msg = msg.split(","));
} catch (err) {
LOG.error("towerLight解析指令失败", err.message);
ElMessage.error("ws消息体异常,请查看console" + err.message);
}
sendMessageToSerialPort(msg, "ws");
break;
case "serialPort": // 串口连接
if (msg.includes("未开启") || msg.includes("已关闭")) {
router.push({ name: "setting" });
setTimeout(() => {
flags.value.openSerialPortDevTools++;
}, 1000);
ElMessageBox.alert(
h("p", { class: "text-xl" }, "请先选择串口设备,再点击打开串口按钮"),
"塔灯未连接",
{
confirmButtonText: "确定",
type: "warning",
draggable: true,
showClose: false
}
);
} else {
LOG.info("串口消息", msg);
}
break;
case "close": // "服务端主动关闭连接"
LOG.error("服务端主动关闭连接");
// 只展示一次提示,不然会一直弹窗,页面会黑屏
if (flags.value.isServerDisconnectActivelyTipsShowed) return;
flags.value.isServerDisconnectActivelyTipsShowed = true;
router.push({ name: "home" });
ElMessageBox.alert(
h("div", [
h("p", { class: "text-xl" }, "服务端主动关闭连接,请重新登录"),
h("p", { class: "text-lg !mt-2" }, "(当前用户可能在其他地方登录,被挤下线)")
]),
"连接中断",
{
confirmButtonText: "确认重连",
type: "warning",
draggable: true,
showClose: false
}
)
.then(() => {
ElMessage.info({
message: "请重新登录",
duration: 1000
});
sessionStorage.removeItem("AndonCurrentStation");
router.push({ name: "setting" });
})
.finally(() => {
flags.value.isServerDisconnectActivelyTipsShowed = false;
});
default:
// ElMessage.info("未知消息类型:" + type + "," + msg);
break;
}
}
/** 处理新消息或者待处理消息的情况 */
function handleIncomingMessage(msg: string, isAutoJump = true) {
let time = ref(5);
let timer = null;
if (isAutoJump) {
timer = setInterval(() => {
time.value--;
if (time.value === 0) {
clearInterval(timer);
router.push({ name: "receive" });
ElMessageBox.close();
}
}, 1000);
}
// 让用户选择是否跳转到接收页面,如果没操作,那么5秒后自动跳转
ElMessageBox.confirm(
() => {
return h("div", [
h("p", { class: "text-xl" }, msg),
(isAutoJump &&
h("p", { class: "text-lg !mt-2" }, [h("b", time.value), h("span", "秒后自动跳转")])) ||
null
]);
},
"提示",
{
confirmButtonText: `确定`,
cancelButtonText: "取消",
type: "warning",
draggable: true
}
)
.then(() => {
router.push({ name: "receive" });
})
.catch(() => {
clearInterval(timer);
});
}
/** webSocket关闭 */
function onclose() {
LOG.error("WebSocket连接关闭");
clearInterval(interval.value);
// 暂时不要重连,和用户点击当前工位冲突了
// /**
// * 重连的时机是:用户ID没变,但是由于某些原因断开了连接,那么就重连
// * 例如:网络波动,断网重连,或者服务端主动断开连接
// * 如果用户ID变了,那么说明切换用户了,那么就不需要重连
// */
// if (!flags.value.webSocketUserIdChanged) {
// reconnect();
// }
if (flags.value.isUnloading) return;
ElMessageBox.alert(
h("div", [
h("p", { class: "text-xl" }, "与服务器意外断开了连接,点击确认重连"),
h("p", { class: "text-lg !mt-2" }, "(塔灯也需要重新连接)")
]),
"连接中断",
{
confirmButtonText: "确认重连",
type: "warning",
draggable: true,
showClose: false
}
).then(() => {
location.reload();
});
}
/** webSocket错误 */
function onerror(err) {
console.warn("WebSocket连接错误:", err);
LOG.warning("WebSocket连接错误", JSON.stringify(err));
}
/** webSocket重连 */
function reconnect() {
if (!userId.value) {
ElMessage.error("WebSocket重连失败,请重新点击当前工位登录");
return;
}
if (reconnectInterval.value === maxReconnectInterval.value) {
LOG.warning("WebSocket重连", "已达最大重连次数");
return;
}
LOG.warning("WebSocket重连", `${reconnectInterval.value}ms将再次连接`);
timeout.value = setTimeout(() => {
webSocketConnect(userId.value);
reconnectInterval.value = Math.min(reconnectInterval.value * 2, maxReconnectInterval.value);
}, reconnectInterval.value);
}
/** websocket保活 心跳包 */
function keepWebSocketConnectionAlive() {
// tips: websocket.readyState值对应的状态 0:连接尚未建立;1:连接已建立;2:连接正在关闭;3:连接已关闭
interval.value = setInterval(() => {
if (ws.value && ws.value.readyState === 1) {
ws.value.send(JSON.stringify({ type: "heart", msg: "💖" }));
} else {
clearInterval(interval.value);
}
}, heartbeatInterval.value);
}
/** 选择串口设备来连接 */
async function startSerialPortConnection() {
if ("serial" in navigator) {
try {
// @ts-ignore
serialPort.value = await navigator.serial.requestPort();
const info = serialPort.value.getInfo();
console.log("串口设备对象信息:", serialPort.value);
LOG.success("串口设备信息", JSON.stringify(info));
ElMessage.success("串口设备连接成功");
} catch (err) {
LOG.error("连接串口设备失败", err.message);
if (err.message.includes("No port selected by the user")) {
ElMessage.info("用户取消选择串口设备");
} else {
ElMessage.error("连接串口设备失败:" + err.message);
}
}
} else {
ElMessage.error({
message: "您的浏览器不支持串口连接,请更换新版Chrome浏览器后重试",
duration: 3000
});
}
}
/** 打开串口 */
async function openSerialPortConnection() {
if (serialPort.value) {
if (!serialPort.value.readable && !serialPort.value.writable) {
try {
await serialPort.value.open({ baudRate: 9600 });
ElMessage.success("串口已打开");
if (ws.value) {
ws.value.send(JSON.stringify({ type: "serialPort", msg: "串口已打开,可以接收指令" }));
} else {
LOG.warning("请先点击当前工位", "以便正常接收指令");
ElMessage.warning({
message: "请先点击当前工位登录,以便正常接收指令",
duration: 3000
});
}
} catch (err) {
let msg = err.message;
LOG.error("打开串口失败", msg);
if (msg.includes("The port is already open")) {
ElMessage.warning("串口已经打开");
} else if (msg.includes("Access to the port is denied")) {
ElMessage.error("串口打开失败!权限被拒绝,请检查串口设备是否正确连接");
} else if (msg.includes("A call to open() is already in progress")) {
ElMessage.warning("串口开启中,请稍后...");
} else if (msg.includes("Failed to open serial port.")) {
ElMessage.error("串口打开失败!请检查设备是否能打开串口");
} else {
ElMessage.error("打开串口失败:" + msg);
}
}
} else {
ElMessage.warning("串口已开启,可以发送指令");
}
} else {
LOG.warning("打开串口失败", "请先选择串口设备");
ElMessage.warning("请先选择串口设备");
}
}
/** 断开串口 */
async function closeSerialPortConnection() {
if (serialPort.value) {
try {
await serialPort.value.close();
ElMessage.success("串口已关闭");
} catch (err) {
LOG.error("关闭串口失败", err.message);
if (err.message.includes("The port is already closed")) {
ElMessage.warning("串口未开启或已被关闭");
} else {
ElMessage.error("关闭串口失败:" + err.message);
}
}
} else {
ElMessage.warning("请先选择串口设备");
LOG.warning("关闭串口失败", "请先选择串口设备");
}
}
/** 发送消息给串口设备 */
async function sendMessageToSerialPort(msg?: Array<string>, source = "web") {
// 显示提示框,让用户选择是否前往设置
const showTips = (title, content, type) => {
const noTips = JSON.parse(
sessionStorage.getItem("notShowSerialConnectionTipsFor" + type) || "false"
);
// 如果用户选择了不再提示,那么就不显示提示框
if (noTips) return;
// 只展示一次提示,不然会一直弹窗,页面会黑屏
if (flags.value.isSerialConnectionTipsShowed) return;
// 如果串口设备调试工具弹窗打开了,那么就不再显示让用户去设置串口设备的提示(我都打开设置界面,由于服务器一直发请求,不拦截一下的话就会一直弹很烦)
if (flags.value.serialPortDevDialogStatus) return;
flags.value.isSerialConnectionTipsShowed = true;
ElMessageBox.alert(
h("div", [
h("p", { class: "text-xl" }, "接收到服务端指令,给塔灯发送指令失败!"),
h("b", { class: "text-lg" }, "是否前往设置?"),
h("div", { style: "display:flex;align-items:center;", class: "!mt-2" }, [
h("input", {
type: "checkbox",
id: "notShowSerialConnectionTips",
onclick: (e) => {
if ((e.target as HTMLInputElement).checked) {
sessionStorage.setItem("notShowSerialConnectionTipsFor" + type, "true");
} else {
sessionStorage.removeItem("notShowSerialConnectionTipsFor" + type);
}
}
}),
h("label", { class: "text-lg", for: "notShowSerialConnectionTips" }, "不再提示")
])
]),
title,
{
confirmButtonText: "去设置",
cancelButtonText: "取消",
showCancelButton: true,
type: "warning",
draggable: true
}
)
.then(() => {
router.push({ name: "setting" });
setTimeout(() => {
flags.value.openSerialPortDevTools++;
}, 500);
ElMessageBox.alert(h("p", { class: "text-xl" }, content), title, {
confirmButtonText: "确定",
type: "warning",
draggable: true,
showClose: false
});
})
.catch(() => {
LOG.warning("已取消前往设置");
})
.finally(() => {
flags.value.isSerialConnectionTipsShowed = false;
});
};
// 如果没有传入msg,那么就不发送
if (!msg) return;
// 如果没有选择串口设备,那么提示用户先选择串口设备
if (!serialPort.value) {
if (source === "ws") {
showTips("塔灯未连接", "请先【选择串口设备】,再点击【打开串口】按钮", "NotConnected");
} else {
ElMessage.warning("请先选择串口设备");
}
LOG.warning("发送指令失败", "请先在设置中选择串口设备");
return;
}
// 如果串口没有打开,就打开串口
if (!serialPort.value.readable || !serialPort.value.writable) {
if (source === "ws") {
showTips("串口未开启", "请点击【打开串口】按钮", "NotOpen");
} else {
ElMessage.warning("请先打开串口连接");
}
LOG.warning("发送指令失败", "请先打开串口连接");
return;
}
const writer = serialPort.value.writable.getWriter();
await writer.write(new Uint8Array(msg.map((item) => parseInt(item, 16))));
writer.releaseLock();
source === "web" &&
ElMessage.success({
message: "发送成功",
duration: 1000
});
}
return {
ws,
flags,
serialPort,
webSocketConnect,
sendMessageToSerialPort,
openSerialPortConnection,
startSerialPortConnection,
closeSerialPortConnection,
handleIncomingMessage
};
});