fix(console): 纯色恢复初始回到原背景色(#0a0d13, hsl -140/31/6);修复上传后缩略图挂图(不再撤销共享对象URL);photoStore 迁移改惰性、升级事务不再删旧store
This commit is contained in:
@@ -1394,7 +1394,7 @@ function WorktableSection(props: any) {
|
||||
return { id, url: URL.createObjectURL(blob) }
|
||||
},
|
||||
setPhotoId: (id: string) => persistView({ consoleBgPhotoId: id }),
|
||||
getPlainHsl: () => viewRef.current.consoleBgPlainHsl ?? { h: 0, s: 31, l: 6 },
|
||||
getPlainHsl: () => viewRef.current.consoleBgPlainHsl ?? { h: -140, s: 31, l: 6 },
|
||||
setPlainHsl: (v: { h: number; s: number; l: number }) => persistView({ consoleBgPlainHsl: { ...v } }),
|
||||
getGlowHsl: () => viewRef.current.consoleBgGlowHsl ?? { h: 0, s: 100, l: 100 },
|
||||
setGlowHsl: (v: { h: number; s: number; l: number }) => persistView({ consoleBgGlowHsl: { ...v } }),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** dsh-worktable 控制室背景照片库:IndexedDB 存原始图片(Blob 记录,零压缩)。
|
||||
* v2:多张照片记录(id/createdAt/blob),v1 单图自动迁移为一条 'legacy' 记录。 */
|
||||
* v2:多张照片记录(id/createdAt/blob);v1 单图在首次 list() 时惰性迁移为 'legacy' 记录。
|
||||
* (不在 onupgradeneeded 里动旧 store —— 升级事务内异步读写易出竞态,迁移放到读路径最稳。) */
|
||||
const DB_NAME = 'dsh-worktable'
|
||||
const STORE = 'photoRecords'
|
||||
const LEGACY_STORE = 'consoleBgPhoto'
|
||||
@@ -13,53 +14,64 @@ function openDb(): Promise<IDBDatabase> {
|
||||
req.onupgradeneeded = () => {
|
||||
const db = req.result
|
||||
if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE)
|
||||
// v1→v2 迁移:旧单图(consoleBgPhoto/original)转为一条 'legacy' 记录
|
||||
if (db.objectStoreNames.contains(LEGACY_STORE)) {
|
||||
try {
|
||||
const tx = req.transaction as IDBTransaction
|
||||
const getReq = tx.objectStore(LEGACY_STORE).get(LEGACY_KEY)
|
||||
getReq.onsuccess = () => {
|
||||
try {
|
||||
const blob = getReq.result
|
||||
if (blob instanceof Blob) {
|
||||
const rec: PhotoRecord = { id: 'legacy', createdAt: Date.now(), blob }
|
||||
tx.objectStore(STORE).put(rec, rec.id)
|
||||
}
|
||||
tx.objectStore(LEGACY_STORE).delete(LEGACY_KEY)
|
||||
} catch {}
|
||||
}
|
||||
} catch {}
|
||||
try { db.deleteObjectStore(LEGACY_STORE) } catch {}
|
||||
}
|
||||
}
|
||||
req.onsuccess = () => resolve(req.result)
|
||||
req.onerror = () => reject(req.error)
|
||||
})
|
||||
}
|
||||
|
||||
function getAll(db: IDBDatabase): Promise<PhotoRecord[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE, 'readonly')
|
||||
const req = tx.objectStore(STORE).getAll()
|
||||
req.onsuccess = () => {
|
||||
const arr: PhotoRecord[] = []
|
||||
for (const v of req.result as unknown[]) {
|
||||
const r = v as PhotoRecord | null
|
||||
if (r && typeof r === 'object' && typeof (r as any).id === 'string' && (r as any).blob instanceof Blob) {
|
||||
arr.push(r)
|
||||
}
|
||||
}
|
||||
arr.sort((a, b) => b.createdAt - a.createdAt)
|
||||
db.close()
|
||||
resolve(arr)
|
||||
}
|
||||
req.onerror = () => { db.close(); reject(req.error) }
|
||||
})
|
||||
}
|
||||
|
||||
export const photoStore = {
|
||||
/** 全部照片记录(最新在前) */
|
||||
/** 全部照片记录(最新在前);无记录时惰性迁移 v1 单图 */
|
||||
async list(): Promise<PhotoRecord[]> {
|
||||
const db = await openDb()
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(STORE, 'readonly')
|
||||
const req = tx.objectStore(STORE).getAll()
|
||||
req.onsuccess = () => {
|
||||
const arr: PhotoRecord[] = []
|
||||
for (const v of req.result as unknown[]) {
|
||||
const r = v as PhotoRecord | null
|
||||
if (r && typeof r === 'object' && typeof (r as any).id === 'string' && (r as any).blob instanceof Blob) {
|
||||
arr.push(r)
|
||||
}
|
||||
const main = await getAll(db)
|
||||
if (main.length > 0) return main
|
||||
// 迁移旧单图(若有):读取 → 转为 'legacy' 记录
|
||||
try {
|
||||
if (db.objectStoreNames.contains(LEGACY_STORE)) {
|
||||
const blob = await new Promise<Blob | null>((resolve, reject) => {
|
||||
const tx = db.transaction(LEGACY_STORE, 'readonly')
|
||||
const req = tx.objectStore(LEGACY_STORE).get(LEGACY_KEY)
|
||||
req.onsuccess = () => resolve(req.result instanceof Blob ? req.result : null)
|
||||
req.onerror = () => reject(req.error)
|
||||
})
|
||||
if (blob) {
|
||||
const rec: PhotoRecord = { id: 'legacy', createdAt: Date.now(), blob }
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tx = db.transaction(STORE, 'readwrite')
|
||||
tx.objectStore(STORE).put(rec, rec.id)
|
||||
tx.oncomplete = () => resolve()
|
||||
tx.onerror = () => reject(tx.error)
|
||||
})
|
||||
db.close()
|
||||
return [rec]
|
||||
}
|
||||
arr.sort((a, b) => b.createdAt - a.createdAt)
|
||||
db.close()
|
||||
resolve(arr)
|
||||
}
|
||||
req.onerror = () => { db.close(); reject(req.error) }
|
||||
})
|
||||
} catch {}
|
||||
db.close()
|
||||
return []
|
||||
},
|
||||
/** 新增一张原始照片,返回其 id(自动置为最新) */
|
||||
/** 新增一张原始照片,返回其 id(自动成为最新) */
|
||||
async add(blob: Blob): Promise<string> {
|
||||
const db = await openDb()
|
||||
const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 7)
|
||||
|
||||
@@ -1090,7 +1090,7 @@ function ConsolePane() {
|
||||
const photoUrlRef = useRef<string>('')
|
||||
const [openMenu, setOpenMenu] = useState<string | null>(null)
|
||||
const [bgEdit, setBgEdit] = useState<'plain' | 'glow' | 'photo' | null>(null)
|
||||
const [plainHsl, setPlainHslState] = useState<{ h: number; s: number; l: number }>(() => splitEnv?.console?.getPlainHsl?.() ?? { h: 220, s: 31, l: 6 })
|
||||
const [plainHsl, setPlainHslState] = useState<{ h: number; s: number; l: number }>(() => splitEnv?.console?.getPlainHsl?.() ?? { h: -140, s: 31, l: 6 })
|
||||
const [glowHsl, setGlowHslState] = useState<{ h: number; s: number; l: number }>(() => splitEnv?.console?.getGlowHsl?.() ?? { h: 0, s: 100, l: 100 })
|
||||
const [photoHsl, setPhotoHslState] = useState<{ h: number; s: number; l: number }>({ h: 0, s: 100, l: 100 })
|
||||
const gridRef = useRef<HTMLDivElement | null>(null)
|
||||
@@ -1129,11 +1129,10 @@ function ConsolePane() {
|
||||
const onFilePhoto = async (file: Blob) => {
|
||||
let id = ''
|
||||
let url = ''
|
||||
const rec = await splitEnv?.console?.addPhoto?.(file).catch(() => null)
|
||||
const rec = await (splitEnv?.console?.addPhoto?.(file) ?? Promise.resolve(null)).catch(() => null)
|
||||
if (rec) { id = rec.id; url = rec.url } else {
|
||||
url = URL.createObjectURL(file)
|
||||
}
|
||||
if (photoUrlRef.current) { try { URL.revokeObjectURL(photoUrlRef.current) } catch {} }
|
||||
photoUrlRef.current = url
|
||||
setPhotoIdLocal(id)
|
||||
if (id) setPhotoList((prev) => [{ id, url }, ...prev])
|
||||
@@ -1145,7 +1144,6 @@ function ConsolePane() {
|
||||
const selectPhoto = (p: { id: string; url: string }) => {
|
||||
setPhotoIdLocal(p.id)
|
||||
splitEnv?.console?.setPhotoId?.(p.id)
|
||||
if (photoUrlRef.current) { try { URL.revokeObjectURL(photoUrlRef.current) } catch {} }
|
||||
photoUrlRef.current = p.url
|
||||
setBgPhoto(p.url)
|
||||
setBgState('photo')
|
||||
@@ -1177,8 +1175,8 @@ function ConsolePane() {
|
||||
}
|
||||
const resetHsl = (kind: 'plain' | 'glow' | 'photo') => {
|
||||
if (kind === 'plain') {
|
||||
setPlainHslState({ h: 0, s: 31, l: 6 })
|
||||
splitEnv?.console?.setPlainHsl?.({ h: 0, s: 31, l: 6 })
|
||||
setPlainHslState({ h: -140, s: 31, l: 6 })
|
||||
splitEnv?.console?.setPlainHsl?.({ h: -140, s: 31, l: 6 })
|
||||
} else if (kind === 'glow') {
|
||||
setGlowHslState({ h: 0, s: 100, l: 100 })
|
||||
splitEnv?.console?.setGlowHsl?.({ h: 0, s: 100, l: 100 })
|
||||
@@ -1239,7 +1237,6 @@ function ConsolePane() {
|
||||
const active = lib.list.find((p) => p.id === lib.activeId) ?? lib.list[0] ?? null
|
||||
if (active) {
|
||||
setPhotoIdLocal(active.id)
|
||||
if (photoUrlRef.current) { try { URL.revokeObjectURL(photoUrlRef.current) } catch {} }
|
||||
photoUrlRef.current = active.url
|
||||
setBgPhoto(active.url)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user