mirror of
https://gitee.com/wonderful-code/buildadmin
synced 2024-11-21 06:29:50 +00:00
feat:按需的在后台顶栏显示需要重启 Vite 热更新服务的警告按钮
This commit is contained in:
parent
8c6e17ac85
commit
978e77660d
@ -10,6 +10,7 @@ import { useRoute } from 'vue-router'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
import { setTitleFromRoute } from '/@/utils/common'
|
||||
import iconfontInit from '/@/utils/iconfont'
|
||||
import { init as viteInit } from '/@/utils/vite'
|
||||
// modules import mark, Please do not remove.
|
||||
|
||||
const route = useRoute()
|
||||
@ -19,6 +20,7 @@ const config = useConfig()
|
||||
const { getLocaleMessage } = useI18n()
|
||||
const lang = getLocaleMessage(config.lang.defaultLang) as any
|
||||
onMounted(() => {
|
||||
viteInit()
|
||||
iconfontInit()
|
||||
|
||||
// Modules onMounted mark, Please do not remove.
|
||||
|
@ -74,4 +74,18 @@ export default {
|
||||
'Newly added tasks will never start because they are blocked by failed tasks!(Web terminal)',
|
||||
'Failed to modify the source command, Please try again manually': 'Failed to modify the source command. Please try again manually.',
|
||||
},
|
||||
vite: {
|
||||
Later: '稍后',
|
||||
'Restart hot update': '重启热更新',
|
||||
'Close type terminal': 'WEB Terminal server',
|
||||
'Close type crud': 'CRUD server',
|
||||
'Close type modules': 'module install server',
|
||||
'Close type config': 'system configuration server',
|
||||
'Reload hot server title': 'Need to restart Vite hot update service',
|
||||
'Reload hot server tips 1': 'To ensure that ',
|
||||
'Reload hot server tips 2':
|
||||
" is not interrupted, the system has temporarily suspended Vite's hot update function. During this period, changes to front-end files will not be updated in real-time and web pages will not be automatically reloaded. It has been detected that there are file updates during the service suspension period, and the hot update service needs to be restarted.",
|
||||
'Reload hot server tips 3':
|
||||
'The pause of hot updates does not affect the already loaded functions. You can continue to operate and click to restart the hot update service after everything is ready.',
|
||||
},
|
||||
}
|
||||
|
@ -156,4 +156,9 @@ export default {
|
||||
'Wait for installation': 'Wait for installation',
|
||||
'Conflict pending': 'Conflict pending',
|
||||
'Dependency to be installed': 'Dependency to be installed',
|
||||
'Restart Vite hot server': 'Restart Vite hot server',
|
||||
'Restart Vite hot server tips':
|
||||
'Before successfully restarting the service, you can find the button to manually restart the service from the button group on the right side of the top bar.',
|
||||
'Manual restart': 'Manual restart',
|
||||
'Restart Now': 'Restart Now',
|
||||
}
|
||||
|
@ -74,4 +74,17 @@ export default {
|
||||
'Newly added tasks will never start because they are blocked by failed tasks': '新添加的任务永远不会开始,因为被失败的任务阻塞!(WEB终端)',
|
||||
'Failed to modify the source command, Please try again manually': '修改源的命令执行失败,请手动重试。',
|
||||
},
|
||||
vite: {
|
||||
Later: '稍后',
|
||||
'Restart hot update': '重启热更新',
|
||||
'Close type terminal': 'WEB终端执行命令',
|
||||
'Close type crud': 'CRUD代码生成服务',
|
||||
'Close type modules': '模块安装服务',
|
||||
'Close type config': '修改系统配置',
|
||||
'Reload hot server title': '需要重启 Vite 热更新服务',
|
||||
'Reload hot server tips 1': '为确保',
|
||||
'Reload hot server tips 2':
|
||||
'不被打断,系统暂停了 Vite 的热更新功能,期间前端文件变动将不会实时更新和自动重载网页,现检测到服务暂停期间存在文件更新,需要重启热更新服务。',
|
||||
'Reload hot server tips 3': '热更新暂停不影响已经加载好的功能,您可以继续操作,并在一切就绪后再点击重新启动热更新服务。',
|
||||
},
|
||||
}
|
||||
|
@ -148,4 +148,8 @@ export default {
|
||||
'Wait for installation': '等待安装',
|
||||
'Conflict pending': '冲突待处理',
|
||||
'Dependency to be installed': '依赖待安装',
|
||||
'Restart Vite hot server': '重启 Vite 热更新服务',
|
||||
'Restart Vite hot server tips': '在完成服务重启之前,您可以随时从顶栏右侧的按钮组中找到手动重启服务的按钮。',
|
||||
'Manual restart': '手动重启',
|
||||
'Restart Now': '立即重启',
|
||||
}
|
||||
|
@ -1,10 +1,43 @@
|
||||
<template>
|
||||
<div class="nav-menus" :class="configStore.layout.layoutMode">
|
||||
<!-- 需要重启 Vite 热更新服务警告 -->
|
||||
<el-popover
|
||||
ref="reloadHotServerPopover"
|
||||
@show="onCurrentNavMenu(true, 'reloadHotServer')"
|
||||
@hide="onCurrentNavMenu(false, 'reloadHotServer')"
|
||||
:width="360"
|
||||
v-if="hotUpdateState.dirtyFile"
|
||||
>
|
||||
<div>
|
||||
<div class="el-popover__title">{{ t('vite.Reload hot server title') }}</div>
|
||||
<div class="reload-hot-server-content">
|
||||
<p>
|
||||
<span>{{ t('vite.Reload hot server tips 1') }}</span>
|
||||
<span>【{{ t(`vite.Close type ${hotUpdateState.closeType}`) }}】</span>
|
||||
<span>{{ t('vite.Reload hot server tips 2') }}</span>
|
||||
</p>
|
||||
<p>{{ t('vite.Reload hot server tips 3') }}</p>
|
||||
<div class="reload-hot-server-buttons">
|
||||
<el-button @click="onHotServerOpt('cancel')">{{ t('vite.Later') }}</el-button>
|
||||
<el-button @click="onHotServerOpt('reload')" type="primary">{{ t('vite.Restart hot update') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<div class="nav-menu-item" :class="state.currentNavMenu == 'reloadHotServer' ? 'hover' : ''">
|
||||
<Icon color="var(--el-color-danger)" class="nav-menu-icon" name="el-icon-Warning" size="18" />
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
|
||||
<!-- 站点主页 -->
|
||||
<router-link class="h100" target="_blank" :title="t('Home')" to="/">
|
||||
<div class="nav-menu-item">
|
||||
<Icon :color="configStore.getColorVal('headerBarTabColor')" class="nav-menu-icon" name="el-icon-Monitor" size="18" />
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
<!-- 语言切换 -->
|
||||
<el-dropdown
|
||||
@visible-change="onCurrentNavMenu($event, 'lang')"
|
||||
class="h100"
|
||||
@ -25,6 +58,8 @@
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<!-- 全屏切换 -->
|
||||
<div @click="onFullScreen" class="nav-menu-item" :class="state.isFullScreen ? 'hover' : ''">
|
||||
<Icon
|
||||
:color="configStore.getColorVal('headerBarTabColor')"
|
||||
@ -35,11 +70,15 @@
|
||||
/>
|
||||
<Icon :color="configStore.getColorVal('headerBarTabColor')" class="nav-menu-icon" v-else name="el-icon-FullScreen" size="18" />
|
||||
</div>
|
||||
|
||||
<!-- 终端 - 仅超管 -->
|
||||
<div v-if="adminInfo.super" @click="terminal.toggle()" class="nav-menu-item pt2">
|
||||
<el-badge :is-dot="terminal.state.showDot">
|
||||
<Icon :color="configStore.getColorVal('headerBarTabColor')" class="nav-menu-icon" name="local-terminal" size="26" />
|
||||
</el-badge>
|
||||
</div>
|
||||
|
||||
<!-- 清理缓存 - 仅超管 -->
|
||||
<el-dropdown
|
||||
v-if="adminInfo.super"
|
||||
@visible-change="onCurrentNavMenu($event, 'clear')"
|
||||
@ -61,6 +100,8 @@
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<!-- 管理员信息 -->
|
||||
<el-popover
|
||||
v-if="siteConfig.userInitialize"
|
||||
@show="onCurrentNavMenu(true, 'adminInfo')"
|
||||
@ -92,33 +133,37 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<!-- 配置 -->
|
||||
<div @click="configStore.setLayout('showDrawer', true)" class="nav-menu-item">
|
||||
<Icon :color="configStore.getColorVal('headerBarTabColor')" class="nav-menu-icon" name="fa fa-cogs" size="18" />
|
||||
</div>
|
||||
|
||||
<Config />
|
||||
<TerminalVue />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue'
|
||||
import { editDefaultLang } from '/@/lang'
|
||||
import { ElMessage, type PopoverInstance } from 'element-plus'
|
||||
import screenfull from 'screenfull'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Config from './config.vue'
|
||||
import { useAdminInfo } from '/@/stores/adminInfo'
|
||||
import { useTerminal } from '/@/stores/terminal'
|
||||
import { Local, Session } from '/@/utils/storage'
|
||||
import { ADMIN_INFO, BA_ACCOUNT } from '/@/stores/constant/cacheKey'
|
||||
import router from '/@/router'
|
||||
import { routePush } from '/@/utils/router'
|
||||
import { logout } from '/@/api/backend/index'
|
||||
import { postClearCache } from '/@/api/common'
|
||||
import TerminalVue from '/@/components/terminal/index.vue'
|
||||
import { fullUrl } from '/@/utils/common'
|
||||
import { editDefaultLang } from '/@/lang'
|
||||
import router from '/@/router'
|
||||
import { useAdminInfo } from '/@/stores/adminInfo'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
import { ADMIN_INFO, BA_ACCOUNT } from '/@/stores/constant/cacheKey'
|
||||
import { useSiteConfig } from '/@/stores/siteConfig'
|
||||
import { useTerminal } from '/@/stores/terminal'
|
||||
import { fullUrl } from '/@/utils/common'
|
||||
import { routePush } from '/@/utils/router'
|
||||
import { Local, Session } from '/@/utils/storage'
|
||||
import { hotUpdateState, reloadServer } from '/@/utils/vite'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -126,6 +171,7 @@ const adminInfo = useAdminInfo()
|
||||
const configStore = useConfig()
|
||||
const terminal = useTerminal()
|
||||
const siteConfig = useSiteConfig()
|
||||
const reloadHotServerPopover = ref<PopoverInstance>()
|
||||
|
||||
const state = reactive({
|
||||
isFullScreen: false,
|
||||
@ -138,6 +184,14 @@ const onCurrentNavMenu = (status: boolean, name: string) => {
|
||||
state.currentNavMenu = status ? name : ''
|
||||
}
|
||||
|
||||
const onHotServerOpt = (opt: 'reload' | 'cancel') => {
|
||||
if (opt == 'cancel') {
|
||||
reloadHotServerPopover.value?.hide()
|
||||
} else {
|
||||
reloadServer('manual')
|
||||
}
|
||||
}
|
||||
|
||||
const onFullScreen = () => {
|
||||
if (!screenfull.isEnabled) {
|
||||
ElMessage.warning(t('layouts.Full screen is not supported'))
|
||||
@ -180,6 +234,16 @@ const onClearCache = (type: string) => {
|
||||
border-radius: var(--el-border-radius-base);
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
}
|
||||
.reload-hot-server-content {
|
||||
font-size: var(--el-font-size-small);
|
||||
p {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.reload-hot-server-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
.nav-menus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -1,5 +1,40 @@
|
||||
import { ref } from 'vue'
|
||||
import { Plugin } from 'vite'
|
||||
import type { Plugin, ViteDevServer } from 'vite'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
interface HotUpdateState {
|
||||
// 热更新状态
|
||||
switch: boolean
|
||||
// 热更新关闭类型:terminal=WEB终端执行命令,crud=CRUD,modules=模块安装服务,config=修改系统配置
|
||||
closeType: string
|
||||
// 是否有脏文件(热更新 switch 为 false,又触发了热更新就会产生脏文件)
|
||||
dirtyFile: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试模式下的 Vite 热更新相关状态(这些状态均由 Vite 服务器记录并随时同步至客户端)
|
||||
*/
|
||||
export const hotUpdateState = reactive<HotUpdateState>({
|
||||
switch: true,
|
||||
closeType: '',
|
||||
dirtyFile: false,
|
||||
})
|
||||
|
||||
/**
|
||||
* Vite 相关初始化
|
||||
*/
|
||||
export function init() {
|
||||
if (import.meta.hot) {
|
||||
// 监听 Vite 服务器通知热更新相关状态更新
|
||||
import.meta.hot.on('custom:change-hot-update-state', (state: Partial<HotUpdateState>) => {
|
||||
hotUpdateState.switch = state.switch ?? hotUpdateState.switch
|
||||
hotUpdateState.closeType = state.closeType ?? hotUpdateState.closeType
|
||||
hotUpdateState.dirtyFile = state.dirtyFile ?? hotUpdateState.dirtyFile
|
||||
})
|
||||
|
||||
// 主动从 Vite 服务器获取当前热更新的相关状态
|
||||
import.meta.hot.send('custom:get-hot-update-state', { type: 'init' })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在开发环境
|
||||
@ -15,18 +50,10 @@ export function isProd(mode: string | undefined): boolean {
|
||||
return mode === 'production'
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试模式下的热更新开关状态
|
||||
*/
|
||||
export const hotUpdateSwitch = ref(true)
|
||||
|
||||
/**
|
||||
* 调试模式下关闭热更新
|
||||
*/
|
||||
export const closeHotUpdate = (type: string) => {
|
||||
if (!hotUpdateSwitch.value) return
|
||||
hotUpdateSwitch.value = false
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.send('custom:close-hot', { type })
|
||||
}
|
||||
@ -36,9 +63,6 @@ export const closeHotUpdate = (type: string) => {
|
||||
* 调试模式下开启热更新
|
||||
*/
|
||||
export const openHotUpdate = (type: string) => {
|
||||
if (hotUpdateSwitch.value) return
|
||||
hotUpdateSwitch.value = true
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.send('custom:open-hot', { type })
|
||||
}
|
||||
@ -48,43 +72,92 @@ export const openHotUpdate = (type: string) => {
|
||||
* 调试模式下重启服务并刷新网页
|
||||
*/
|
||||
export const reloadServer = (type: string) => {
|
||||
hotUpdateSwitch.value = true
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.send('custom:reload-server', { type })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义热更新处理插件
|
||||
* 自定义热更新/热替换处理的 Vite 插件
|
||||
*/
|
||||
export const customHotUpdate = (): Plugin => {
|
||||
let hotUpdateSwitch = true
|
||||
type Listeners = ((...args: any[]) => void)[]
|
||||
|
||||
let addFunctionBack: Listeners = []
|
||||
let unlinkFunctionBack: Listeners = []
|
||||
|
||||
// 本服务端的热更新状态数据
|
||||
const hotUpdateState: HotUpdateState = {
|
||||
switch: true,
|
||||
closeType: '',
|
||||
dirtyFile: false,
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步所有热更新状态数据至客户端
|
||||
*/
|
||||
const syncHotUpdateState = (server: ViteDevServer) => {
|
||||
server.ws.send('custom:change-hot-update-state', hotUpdateState)
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'vite-plugin-custom-hot-update',
|
||||
apply: 'serve',
|
||||
configureServer(server) {
|
||||
server.ws.on('custom:close-hot', () => {
|
||||
hotUpdateSwitch = false
|
||||
// 关闭热更新
|
||||
server.ws.on('custom:close-hot', ({ type }) => {
|
||||
hotUpdateState.switch = false
|
||||
hotUpdateState.closeType = type
|
||||
|
||||
// 备份文件添加和删除时的函数列表
|
||||
addFunctionBack = server.watcher.listeners('add') as Listeners
|
||||
unlinkFunctionBack = server.watcher.listeners('unlink') as Listeners
|
||||
|
||||
// 关闭文件添加和删除的监听
|
||||
server.watcher.removeAllListeners('add')
|
||||
server.watcher.removeAllListeners('unlink')
|
||||
})
|
||||
server.ws.on('custom:open-hot', () => {
|
||||
hotUpdateSwitch = true
|
||||
|
||||
syncHotUpdateState(server)
|
||||
|
||||
// 文件添加时通知客户端新增了脏文件(文件删除无需记录为脏文件)
|
||||
server.watcher.on('add', () => {
|
||||
server.restart()
|
||||
})
|
||||
server.watcher.on('unlink', () => {
|
||||
server.restart()
|
||||
hotUpdateState.dirtyFile = true
|
||||
syncHotUpdateState(server)
|
||||
})
|
||||
})
|
||||
|
||||
// 开启热更新
|
||||
server.ws.on('custom:open-hot', () => {
|
||||
hotUpdateState.switch = true
|
||||
server.watcher.removeAllListeners('add')
|
||||
server.watcher.removeAllListeners('unlink')
|
||||
|
||||
// 恢复备份的函数列表
|
||||
for (const key in addFunctionBack) {
|
||||
server.watcher.on('add', addFunctionBack[key])
|
||||
}
|
||||
for (const key in unlinkFunctionBack) {
|
||||
server.watcher.on('unlink', unlinkFunctionBack[key])
|
||||
}
|
||||
|
||||
syncHotUpdateState(server)
|
||||
})
|
||||
|
||||
// 重启热更新
|
||||
server.ws.on('custom:reload-server', () => {
|
||||
hotUpdateSwitch = true
|
||||
server.restart()
|
||||
})
|
||||
|
||||
// 客户端可从本服务端获取热更新服务状态数据
|
||||
server.ws.on('custom:get-hot-update-state', () => {
|
||||
syncHotUpdateState(server)
|
||||
})
|
||||
},
|
||||
handleHotUpdate() {
|
||||
if (!hotUpdateSwitch) {
|
||||
handleHotUpdate({ server }) {
|
||||
if (!hotUpdateState.switch) {
|
||||
// 通知客户端出现了脏文件
|
||||
hotUpdateState.dirtyFile = true
|
||||
syncHotUpdateState(server)
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
@ -79,6 +79,26 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-tis-box max-install-box" v-if="hotUpdateState.dirtyFile">
|
||||
<div class="install-form">
|
||||
<FormItem
|
||||
:label="
|
||||
(state.common.moduleState == moduleInstallState.DISABLE ? '' : t('module.After installation 2')) +
|
||||
t('module.Restart Vite hot server')
|
||||
"
|
||||
v-model="form.reloadHotServer"
|
||||
type="radio"
|
||||
:input-attr="{
|
||||
border: true,
|
||||
content: {
|
||||
0: t('vite.Later') + t('module.Manual restart'),
|
||||
1: t('module.Restart Now'),
|
||||
},
|
||||
}"
|
||||
:tip="t('module.Restart Vite hot server tips')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-button
|
||||
v-blur
|
||||
class="install-done-button"
|
||||
@ -94,22 +114,23 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { reactive } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { onRefreshTableData } from '../index'
|
||||
import { state } from '../store'
|
||||
import { moduleInstallState } from '../types'
|
||||
import { onRefreshTableData } from '../index'
|
||||
import { useTerminal } from '/@/stores/terminal'
|
||||
import { dependentInstallComplete } from '/@/api/backend/module'
|
||||
import FormItem from '/@/components/formItem/index.vue'
|
||||
import { taskStatus } from '/@/stores/constant/terminalTaskStatus'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { dependentInstallComplete } from '/@/api/backend/module'
|
||||
import { reloadServer } from '/@/utils/vite'
|
||||
import { useTerminal } from '/@/stores/terminal'
|
||||
import { hotUpdateState, reloadServer } from '/@/utils/vite'
|
||||
|
||||
const { t } = useI18n()
|
||||
const terminal = useTerminal()
|
||||
const form = reactive({
|
||||
rebuild: 0,
|
||||
reloadHotServer: 0,
|
||||
})
|
||||
|
||||
const showTerminal = () => {
|
||||
@ -121,15 +142,17 @@ const onSubmitInstallDone = () => {
|
||||
if (form.rebuild == 1) {
|
||||
terminal.toggle(true)
|
||||
terminal.addTaskPM('web-build', false, '', (res: number) => {
|
||||
if (form.reloadHotServer == 1) {
|
||||
reloadServer('modules')
|
||||
}
|
||||
if (res == taskStatus.Success) {
|
||||
terminal.toggle(false)
|
||||
if (state.common.moduleState != moduleInstallState.DISABLE) {
|
||||
reloadServer('modules')
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (state.common.moduleState != moduleInstallState.DISABLE) {
|
||||
reloadServer('modules')
|
||||
} else {
|
||||
if (form.reloadHotServer == 1) {
|
||||
reloadServer('modules')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,4 +251,7 @@ const onConfirmDepend = () => {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
.max-install-box {
|
||||
width: 86%;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user