This commit is contained in:
2026-08-07 10:45:15 +08:00
parent ac762ed302
commit 54fe05d864
4 changed files with 29 additions and 7 deletions
+9 -5
View File
@@ -1,4 +1,4 @@
import { computed, onBeforeUnmount, ref, watchEffect, type Ref } from 'vue'
import { computed, onBeforeUnmount, ref, watch, type Ref } from 'vue'
import { defineStore } from 'pinia'
export interface UnsavedWorkEntry {
@@ -23,7 +23,11 @@ export const useUnsavedWorkStore = defineStore('client-unsaved-work', () => {
if (owner && owners.get(entry.key) !== owner) return
const index = entries.value.findIndex((item) => item.key === entry.key)
if (index === -1) entries.value.push(entry)
else entries.value[index] = entry
else {
const current = entries.value[index]
if (current.routeKey === entry.routeKey && current.label === entry.label && current.dirty === entry.dirty) return
entries.value[index] = entry
}
}
function remove(key: string, owner?: symbol) {
@@ -62,13 +66,13 @@ export function useUnsavedWorkRegistration(options: {
const store = useUnsavedWorkStore()
const owner = Symbol(options.key)
store.claim(options.key, owner)
watchEffect(() => {
watch(options.dirty, (dirty) => {
store.upsert({
key: options.key,
routeKey: options.routeKey,
label: options.label,
dirty: options.dirty.value,
dirty,
}, owner)
})
}, { immediate: true })
onBeforeUnmount(() => store.remove(options.key, owner))
}
+7
View File
@@ -65,6 +65,13 @@ test('unsaved registrations use instance ownership so stale cached views cannot
assert.match(unsavedSource, /store\.remove\(options\.key, owner\)/)
})
test('unsaved registration only tracks the dirty ref and cannot subscribe to its own store writes', () => {
assert.match(unsavedSource, /watch\(options\.dirty, \(dirty\) =>/)
assert.match(unsavedSource, /\}, \{ immediate: true \}\)/)
assert.doesNotMatch(unsavedSource, /watchEffect/)
assert.match(unsavedSource, /current\.routeKey === entry\.routeKey[\s\S]*current\.dirty === entry\.dirty\) return/)
})
test('the tab strip keeps touch targets and horizontal navigation usable on narrow screens', () => {
assert.match(tabStyles, /overflow-x: auto/)
assert.match(tabStyles, /scrollbar-width: none/)