feat: add unsaved edits confirmation (dirty flag + route guard + beforeunload) to KPIDetail.vue
This commit is contained in:
@@ -229,9 +229,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed, watch } from 'vue'
|
import { ref, onMounted, computed, watch, onBeforeUnmount } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute, onBeforeRouteLeave } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import VChart from 'vue-echarts'
|
import VChart from 'vue-echarts'
|
||||||
import 'echarts'
|
import 'echarts'
|
||||||
import { kpiApi, mapApi, kpiCausalityApi } from '../api/index'
|
import { kpiApi, mapApi, kpiCausalityApi } from '../api/index'
|
||||||
@@ -260,6 +260,48 @@ const fullNetworkNodes = ref<any[]>([])
|
|||||||
const fullNetworkEdges = ref<any[]>([])
|
const fullNetworkEdges = ref<any[]>([])
|
||||||
const fullNetworkLoading = ref(false)
|
const fullNetworkLoading = ref(false)
|
||||||
|
|
||||||
|
// 编辑未保存离开确认
|
||||||
|
const isDirty = ref(false)
|
||||||
|
let kpiSnapshot: string = ''
|
||||||
|
|
||||||
|
let watchInitialized = false
|
||||||
|
|
||||||
|
// 路由离开确认
|
||||||
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
|
if (isDirty.value) {
|
||||||
|
ElMessageBox.confirm('有未保存的修改,确定离开吗?', '提示', {
|
||||||
|
confirmButtonText: '离开',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => next()).catch(() => next(false))
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 页面关闭/刷新确认
|
||||||
|
function onBeforeUnloadHandler(e: BeforeUnloadEvent) {
|
||||||
|
if (isDirty.value) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.returnValue = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件卸载时清理事件
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('beforeunload', onBeforeUnloadHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 kpi 变化设置脏标记(跳过初始化赋值)
|
||||||
|
watch(() => kpi.value, () => {
|
||||||
|
if (!kpi.value) return
|
||||||
|
if (!watchInitialized) {
|
||||||
|
watchInitialized = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isDirty.value = (JSON.stringify(kpi.value) !== kpiSnapshot)
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
// 层级分解
|
// 层级分解
|
||||||
const hierarchyTree = ref<any[]>([])
|
const hierarchyTree = ref<any[]>([])
|
||||||
const hierarchyLoading = ref(false)
|
const hierarchyLoading = ref(false)
|
||||||
@@ -463,8 +505,14 @@ const fullNetworkChartOption = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
try { await kpiApi.update(kpi.value.id, kpi.value); ElMessage.success('保存成功') }
|
try {
|
||||||
catch (e) { ElMessage.error('保存失败') }
|
await kpiApi.update(kpi.value.id, kpi.value)
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
isDirty.value = false
|
||||||
|
kpiSnapshot = JSON.stringify(kpi.value)
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('保存失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function suggestThreshold() {
|
async function suggestThreshold() {
|
||||||
@@ -548,8 +596,13 @@ watch(showFullNetwork, (val) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
// 注册页面关闭/刷新确认
|
||||||
|
window.addEventListener('beforeunload', onBeforeUnloadHandler)
|
||||||
|
|
||||||
const r: any = await kpiApi.get(Number(route.params.id))
|
const r: any = await kpiApi.get(Number(route.params.id))
|
||||||
kpi.value = r.data || r
|
kpi.value = r.data || r
|
||||||
|
// 保存初始快照用于脏检测
|
||||||
|
kpiSnapshot = JSON.stringify(kpi.value)
|
||||||
// 获取历史值
|
// 获取历史值
|
||||||
try {
|
try {
|
||||||
const kv: any = await api.get('/kpis/' + kpi.value.id)
|
const kv: any = await api.get('/kpis/' + kpi.value.id)
|
||||||
@@ -578,6 +631,8 @@ async function associateMap() {
|
|||||||
ElMessage.success('地图关联成功')
|
ElMessage.success('地图关联成功')
|
||||||
const r: any = await kpiApi.get(Number(route.params.id))
|
const r: any = await kpiApi.get(Number(route.params.id))
|
||||||
kpi.value = r.data || r
|
kpi.value = r.data || r
|
||||||
|
kpiSnapshot = JSON.stringify(kpi.value)
|
||||||
|
isDirty.value = false
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error('关联失败')
|
ElMessage.error('关联失败')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user