Files
webman-buildadmin/web/src/stores/terminal.ts
zhenhui 8a1287d8ed 1.修复矿建鉴权报错
2.优化登录跳转接口
3.优化登录跳转接口
4.修复CURD生成代码模块表不加前缀访问返回404问题
5.系统级报错***优化报错Fatal error: Type of app\common\library\token\TokenExpirationException::$message
2026-04-13 16:48:37 +08:00

303 lines
11 KiB
TypeScript

import { ElNotification } from 'element-plus'
import { defineStore } from 'pinia'
import { nextTick, reactive } from 'vue'
import { buildTerminalUrl } from '/@/api/common'
import { i18n } from '/@/lang/index'
import { STORE_TERMINAL } from '/@/stores/constant/cacheKey'
import { SYSTEM_ZINDEX } from '/@/stores/constant/common'
import { taskStatus } from '/@/stores/constant/terminalTaskStatus'
import type { Terminal } from '/@/stores/interface/index'
import { timeFormat } from '/@/utils/common'
import { uuid } from '/@/utils/random'
import { closeHotUpdate, openHotUpdate } from '/@/utils/vite'
export const useTerminal = defineStore(
'terminal',
() => {
const state: Terminal = reactive({
show: false,
showDot: false,
taskList: [],
packageManager: 'pnpm',
showConfig: false,
automaticCleanupTask: '1',
phpDevelopmentServer: false,
npmRegistry: 'unknown',
composerRegistry: 'unknown',
})
function init() {
for (const key in state.taskList) {
if (state.taskList[key].status == taskStatus.Connecting || state.taskList[key].status == taskStatus.Executing) {
state.taskList[key].status = taskStatus.Unknown
}
}
}
function toggle(val = !state.show) {
state.show = val
if (val) {
toggleDot(false)
}
}
function toggleDot(val = !state.showDot) {
state.showDot = val
}
function toggleConfigDialog(val = !state.showConfig) {
toggle(!val)
state.showConfig = val
}
function changeRegistry(val: string, type: 'npm' | 'composer') {
state[type == 'npm' ? 'npmRegistry' : 'composerRegistry'] = val
}
function changePackageManager(val: string) {
state.packageManager = val
}
function changePHPDevelopmentServer(val: boolean) {
state.phpDevelopmentServer = val
}
function changeAutomaticCleanupTask(val: '0' | '1') {
state.automaticCleanupTask = val
}
function setTaskStatus(idx: number, status: number) {
state.taskList[idx].status = status
if ((status == taskStatus.Failed || status == taskStatus.Unknown) && state.taskList[idx].blockOnFailure) {
setTaskShowMessage(idx, true)
}
}
function taskCompleted(idx: number) {
// 命令执行完毕,重新打开热更新
openHotUpdate('terminal')
if (typeof state.taskList[idx].callback != 'function') return
const status = state.taskList[idx].status
if (status == taskStatus.Failed || status == taskStatus.Unknown) {
state.taskList[idx].callback(taskStatus.Failed)
} else if (status == taskStatus.Success) {
state.taskList[idx].callback(taskStatus.Success)
}
}
function setTaskShowMessage(idx: number, val = !state.taskList[idx].showMessage) {
state.taskList[idx].showMessage = val
}
function addTaskMessage(idx: number, message: string) {
if (!state.show) toggleDot(true)
state.taskList[idx].message = state.taskList[idx].message.concat(message)
nextTick(() => {
execMessageScrollbarKeep(state.taskList[idx].uuid)
})
}
function addTask(command: string, blockOnFailure = true, extend = '', callback: Function = () => {}) {
const duplicatePending = state.taskList.some(
(item) =>
item.command === command &&
(item.status === taskStatus.Waiting ||
item.status === taskStatus.Connecting ||
item.status === taskStatus.Executing)
)
if (duplicatePending) {
return
}
if (!state.show) toggleDot(true)
state.taskList = state.taskList.concat({
uuid: uuid(),
createTime: timeFormat(),
status: taskStatus.Waiting,
command: command,
message: [],
showMessage: false,
blockOnFailure: blockOnFailure,
extend: extend,
callback: callback,
})
// 清理任务列表
if (parseInt(state.automaticCleanupTask) === 1) {
clearSuccessTask()
}
// 检查是否有已经失败的任务
if (state.show === false) {
for (const key in state.taskList) {
if (state.taskList[key].status == taskStatus.Failed || state.taskList[key].status == taskStatus.Unknown) {
ElNotification({
type: 'error',
message: i18n.global.t('terminal.Newly added tasks will never start because they are blocked by failed tasks'),
zIndex: SYSTEM_ZINDEX,
})
break
}
}
}
startTask()
}
function addTaskPM(command: string, blockOnFailure = true, extend = '', callback: Function = () => {}) {
addTask(command + '.' + state.packageManager, blockOnFailure, extend, callback)
}
function delTask(idx: number) {
if (state.taskList[idx].status != taskStatus.Connecting && state.taskList[idx].status != taskStatus.Executing) {
state.taskList.splice(idx, 1)
}
startTask()
}
function startTask() {
let taskKey = null
// 寻找可以开始执行的命令
for (const key in state.taskList) {
if (state.taskList[key].status == taskStatus.Waiting) {
taskKey = parseInt(key)
break
}
if (state.taskList[key].status == taskStatus.Connecting || state.taskList[key].status == taskStatus.Executing) {
break
}
if (state.taskList[key].status == taskStatus.Success) {
continue
}
if (state.taskList[key].status == taskStatus.Failed || state.taskList[key].status == taskStatus.Unknown) {
if (state.taskList[key].blockOnFailure) {
break
} else {
continue
}
}
}
if (taskKey !== null) {
setTaskStatus(taskKey, taskStatus.Connecting)
startEventSource(taskKey)
}
}
function startEventSource(taskKey: number) {
// 命令执行期间禁用热更新
closeHotUpdate('terminal')
window.eventSource = new EventSource(
buildTerminalUrl(state.taskList[taskKey].command, state.taskList[taskKey].uuid, state.taskList[taskKey].extend)
)
window.eventSource.onmessage = function (e) {
const data = JSON.parse(e.data)
if (!data || !data.data) {
return
}
const taskIdx = findTaskIdxFromUuid(data.uuid)
if (taskIdx === false) {
return
}
if (data.data == 'command-exec-error') {
setTaskStatus(taskIdx, taskStatus.Failed)
window.eventSource.close()
taskCompleted(taskIdx)
startTask()
} else if (data.data == 'command-exec-completed') {
window.eventSource.close()
if (state.taskList[taskIdx].status != taskStatus.Success) {
setTaskStatus(taskIdx, taskStatus.Failed)
}
taskCompleted(taskIdx)
startTask()
} else if (data.data == 'command-link-success') {
setTaskStatus(taskIdx, taskStatus.Executing)
} else if (data.data == 'command-exec-success') {
setTaskStatus(taskIdx, taskStatus.Success)
} else {
addTaskMessage(taskIdx, data.data)
}
}
window.eventSource.onerror = function () {
window.eventSource.close()
const taskIdx = findTaskIdxFromGuess(taskKey)
if (taskIdx === false) return
setTaskStatus(taskIdx, taskStatus.Failed)
taskCompleted(taskIdx)
}
}
function retryTask(idx: number) {
state.taskList[idx].message = []
setTaskStatus(idx, taskStatus.Waiting)
startTask()
}
function clearSuccessTask() {
state.taskList = state.taskList.filter((item) => item.status != taskStatus.Success)
}
function findTaskIdxFromUuid(uuid: string) {
for (const key in state.taskList) {
if (state.taskList[key].uuid == uuid) {
return parseInt(key)
}
}
return false
}
function findTaskIdxFromGuess(idx: number) {
if (!state.taskList[idx]) {
let taskKey = -1
for (const key in state.taskList) {
if (state.taskList[key].status == taskStatus.Connecting || state.taskList[key].status == taskStatus.Executing) {
taskKey = parseInt(key)
}
}
return taskKey === -1 ? false : taskKey
} else {
return idx
}
}
function execMessageScrollbarKeep(uuid: string) {
const execMessageEl = document.querySelector('.exec-message-' + uuid) as Element
if (execMessageEl && execMessageEl.scrollHeight) {
execMessageEl.scrollTop = execMessageEl.scrollHeight
}
}
return {
state,
init,
toggle,
toggleDot,
setTaskStatus,
setTaskShowMessage,
addTaskMessage,
addTask,
addTaskPM,
delTask,
startTask,
retryTask,
clearSuccessTask,
toggleConfigDialog,
changeRegistry,
changePackageManager,
changePHPDevelopmentServer,
changeAutomaticCleanupTask,
}
},
{
persist: {
key: STORE_TERMINAL,
pick: ['state.showDot', 'state.taskList', 'state.automaticCleanupTask', 'state.npmRegistry', 'state.composerRegistry'],
},
}
)