vue2-client 1.13.30 → 1.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-client",
3
- "version": "1.13.30",
3
+ "version": "1.14.2",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
@@ -81,7 +81,8 @@
81
81
  @gotoUserDetail="gotoUserDetail"
82
82
  @editButtonStateDataClick="editButtonStateDataClick"
83
83
  @importExcelOk="importExcelOk"
84
- @rowClick="handleRowClick">
84
+ @rowClick="handleRowClick"
85
+ @expand="onExpand">
85
86
  <template slot="leftButton" slot-scope="{selectedRowKeys, selectedRows}">
86
87
  <slot name="leftButton" :selectedRowKeys="selectedRowKeys" :selectedRows="selectedRows"></slot>
87
88
  </template>
@@ -217,6 +218,7 @@ export default {
217
218
  table_selectedRows: [],
218
219
  // 数据只有一页时是否展示分页,true:展示,auto:隐藏
219
220
  showPagination: true,
221
+ isMounted: false
220
222
  }
221
223
  },
222
224
  computed: {
@@ -821,6 +823,9 @@ export default {
821
823
  // 添加处理 rowClick 的方法
822
824
  handleRowClick (record) {
823
825
  this.$emit('rowClick', record)
826
+ },
827
+ onExpand (expanded, record) {
828
+ this.$emit('expand', expanded, record)
824
829
  }
825
830
  },
826
831
  action: {
@@ -840,7 +845,12 @@ export default {
840
845
  }
841
846
  },
842
847
  mounted () {
843
- this.initConfig()
848
+ if (!this.isMounted) {
849
+ // 防止多次调用
850
+ // 不知道为啥他会执行两次 mounted 暂时处理方式
851
+ this.initConfig()
852
+ this.isMounted = true
853
+ }
844
854
  if (this.getSelectedData && typeof this.getSelectedData === 'function') {
845
855
  const selectedId = this.getSelectedData()
846
856
  if (!selectedId) {
@@ -26,7 +26,7 @@ export default {
26
26
  data () {
27
27
  return {
28
28
  // 查询配置文件名
29
- queryParamsName: 'crud_dictionary_manage',
29
+ queryParamsName: 'ChargeQueryCRUD',
30
30
  // 查询配置左侧tree
31
31
  // xTreeConfigName: 'addressType',
32
32
  // 新增表单固定值
@@ -35,6 +35,7 @@
35
35
  v-on="getEventHandlers(cell)"
36
36
  @hook:mounted="(h)=>onComponentMounted(h,cell,cellIndex)"
37
37
  @rowClick="handleRowClick"
38
+ @onExpand="onExpand"
38
39
  :queryParamsName="cell.slotConfig"
39
40
  :configName="cell.slotConfig"
40
41
  :countVisible="false"
@@ -71,6 +72,7 @@
71
72
  v-on="getEventHandlers(cell)"
72
73
  @hook:mounted="(h)=>onComponentMounted(h,cell,cellIndex)"
73
74
  @rowClick="handleRowClick"
75
+ @onExpand="onExpand"
74
76
  :queryParamsName="cell.slotConfig"
75
77
  :configName="cell.slotConfig"
76
78
  :countVisible="false"
@@ -218,6 +220,9 @@ export default {
218
220
  handleRowClick (record) {
219
221
  this.$emit('rowClick', record)
220
222
  },
223
+ onExpand (expanded, record) {
224
+ this.$emit('expand', expanded, record)
225
+ },
221
226
  listClick (data) {
222
227
  this.$emit('listClick', data)
223
228
  },
@@ -149,7 +149,10 @@
149
149
  </a-row>
150
150
  <!-- 如果当前是表格模式 -->
151
151
  <template v-if="isTableMode">
152
- <x-table-wrapper ref="table" @rowClick="handleRowClick">
152
+ <x-table-wrapper
153
+ ref="table"
154
+ @rowClick="handleRowClick"
155
+ @expand="onExpand">
153
156
  <template slot="expandedRowRender">
154
157
  <!-- 列扩展栅格 -->
155
158
  <x-report
@@ -250,7 +253,11 @@
250
253
  ref="exportExcel"
251
254
  />
252
255
  <a-modal @cancel="$refs.table.refresh()" v-model="selectedRowModalVisible" width="80vw" title="已选中数据" :footer="null">
253
- <x-table-wrapper ref="selectedDataTable" :load-selected-data="true" @rowClick="handleRowClick"></x-table-wrapper>
256
+ <x-table-wrapper
257
+ ref="selectedDataTable"
258
+ :load-selected-data="true"
259
+ @rowClick="handleRowClick">
260
+ </x-table-wrapper>
254
261
  </a-modal>
255
262
  </div>
256
263
  </template>
@@ -1323,6 +1330,9 @@ export default {
1323
1330
  },
1324
1331
  handleRowClick (record) {
1325
1332
  this.$emit('rowClick', record)
1333
+ },
1334
+ onExpand (expanded, record) {
1335
+ this.$emit('expand', expanded, record)
1326
1336
  }
1327
1337
  }
1328
1338
  }
@@ -18,6 +18,7 @@
18
18
  :setScrollYHeight="tableContext.setScrollYHeight"
19
19
  :selectRowMode="tableContext.selectRowMode"
20
20
  :size="tableContext.tableSize"
21
+ @expand="onExpand"
21
22
  @rowClick="handleRowClick"
22
23
  >
23
24
  <template
@@ -296,6 +297,9 @@ export default {
296
297
  handleRowClick (record) {
297
298
  this.$emit('rowClick', record)
298
299
  },
300
+ onExpand (expanded, record) {
301
+ this.$emit('expand', expanded, record)
302
+ },
299
303
  setLocalDataSource (data) {
300
304
  this.activeTable?.setLocalDataSource(data)
301
305
  },
@@ -325,7 +325,7 @@ export default {
325
325
  white-space: nowrap !important;
326
326
  min-width: v-bind('config?.style?.labelWidth || "80px"');
327
327
  justify-content: flex-start;
328
- padding-right: 8px !important;
328
+ padding-right: 2px !important;
329
329
  }
330
330
 
331
331
  :deep(.ant-descriptions-item-content) {
@@ -334,7 +334,7 @@ export default {
334
334
  align-items: center !important;
335
335
  padding: 0 !important;
336
336
  margin: 0 !important;
337
- margin-left: 2px !important;
337
+ margin-left: 0px !important;
338
338
  }
339
339
 
340
340
  :deep(.ant-descriptions-item-colon) {
@@ -343,6 +343,7 @@ export default {
343
343
  align-items: center !important;
344
344
  margin: 0 !important;
345
345
  padding: 0 !important;
346
+ margin-right: 2px !important;
346
347
  }
347
348
 
348
349
  :deep(.ant-descriptions-item-container:hover) {
package/src/bootstrap.js CHANGED
@@ -5,6 +5,7 @@ import guards from '@vue2-client/router/guards'
5
5
  import interceptors from '@vue2-client/utils/axios-interceptors'
6
6
  import { getConfigByName } from '@vue2-client/services/api/common'
7
7
  import { hiPrintPlugin } from '@vue2-client/plugins/HiPrintPlugin'
8
+ import { isMicroAppEnv } from '@vue2-client/utils/microAppUtils'
8
9
 
9
10
  /**
10
11
  * 启动引导方法
@@ -18,12 +19,15 @@ async function bootstrap ({ router, store, i18n, message }) {
18
19
  Vue.$store = store
19
20
  // 获取系统配置
20
21
  if (store) {
21
- await getConfigByName('webConfig', undefined, res => {
22
- localStorage.setItem(process.env.VUE_APP_WEB_CONFIG_KEY, JSON.stringify(res))
23
- if (res?.setting) {
24
- Vue.$store.commit('setting/setSetting', res?.setting)
25
- }
26
- })
22
+ // microApp 环境下作为子应用不获取webConfig
23
+ if (!isMicroAppEnv) {
24
+ await getConfigByName('webConfig', undefined, res => {
25
+ localStorage.setItem(process.env.VUE_APP_WEB_CONFIG_KEY, JSON.stringify(res))
26
+ if (res?.setting) {
27
+ Vue.$store.commit('setting/setSetting', res?.setting)
28
+ }
29
+ })
30
+ }
27
31
  await getConfigByName('componentStyles', undefined, res => {
28
32
  localStorage.setItem(process.env.VUE_APP_WEB_STYLES_KEY, JSON.stringify(res))
29
33
  })
@@ -18,7 +18,10 @@ routerResource.blank = view.blank
18
18
  routerResource.singlePage = view.blank
19
19
  // 栅格配置视图
20
20
  routerResource.gridView = view.gridView
21
-
21
+ // 测试
22
+ routerResource.businessQuery = view.blank
23
+ // 业务查询 - 收费查询
24
+ routerResource.chargeQuery = () => import('@vue2-client/base-client/components/common/XFormTable/demo.vue')
22
25
  // --------------------------------------仪表盘--------------------------------------
23
26
  routerResource.dashboard = view.blank
24
27
  // 工作台
@@ -1,5 +1,9 @@
1
1
  import { post } from '@vue2-client/services/api'
2
2
 
3
+ // 内存缓存作为IndexedDB的备用方案,防止数据库损坏时应用崩溃
4
+ const MEMORY_CACHE = new Map()
5
+ const USE_MEMORY_CACHE = true // 是否启用内存缓存
6
+
3
7
  const DB_CONFIG = {
4
8
  NAME: window.__MICRO_APP_NAME__ ? `view_${window.__MICRO_APP_NAME__}` : 'view',
5
9
  STORE_NAME: 'metaCache',
@@ -7,12 +11,16 @@ const DB_CONFIG = {
7
11
  CURRENT_VERSION: 1
8
12
  }
9
13
 
14
+ // 数据库健康状态标记,用于在数据库异常时切换到备用方案
15
+ let IS_DB_HEALTHY = true
16
+
10
17
  const LIULI_WEB_DB_NAME = 'view_liuli_web'
11
18
 
12
19
  export class IndexedDBManager {
13
20
  constructor () {
14
21
  this.db = undefined
15
- this.locks = {}
22
+ this.locks = new Map()
23
+ this.requestTimeout = 10000
16
24
  this.isInMicroApp = !!window.__MICRO_APP_NAME__
17
25
  this.microAppName = window.__MICRO_APP_NAME__ || ''
18
26
  this.indexedDB = window?.rawWindow?.indexedDB || window.indexedDB || window.webkitindexedDB
@@ -22,9 +30,16 @@ export class IndexedDBManager {
22
30
  async openDatabase () {
23
31
  try {
24
32
  return await new Promise((resolve, reject) => {
33
+ // 添加超时机制,防止请求永久挂起
34
+ const timeout = setTimeout(() => {
35
+ console.error('[openDatabase] 请求超时,将尝试重建数据库')
36
+ reject(new Error('IndexedDB open timeout'))
37
+ }, 5000) // 5秒超时
38
+
25
39
  const checkRequest = this.indexedDB.open(DB_CONFIG.NAME)
26
40
 
27
41
  checkRequest.onsuccess = (e) => {
42
+ clearTimeout(timeout) // 清除超时
28
43
  const db = e.target.result
29
44
  const currentVersion = db.version
30
45
  db.close()
@@ -32,9 +47,19 @@ export class IndexedDBManager {
32
47
  DB_CONFIG.CURRENT_VERSION = Math.max(currentVersion, DB_CONFIG.VERSION)
33
48
  const request = this.indexedDB.open(DB_CONFIG.NAME, DB_CONFIG.CURRENT_VERSION)
34
49
 
35
- request.onerror = (e) => reject(e.currentTarget.error)
50
+ // 为第二次打开也添加超时
51
+ const openTimeout = setTimeout(() => {
52
+ console.error('[openDatabase] 二次打开请求超时')
53
+ reject(new Error('IndexedDB second open timeout'))
54
+ }, 5000)
55
+
56
+ request.onerror = (e) => {
57
+ clearTimeout(openTimeout)
58
+ reject(e.currentTarget.error)
59
+ }
36
60
 
37
61
  request.onsuccess = (e) => {
62
+ clearTimeout(openTimeout)
38
63
  const db = e.target.result
39
64
  if (!db.objectStoreNames.contains(DB_CONFIG.STORE_NAME)) {
40
65
  db.close()
@@ -46,6 +71,7 @@ export class IndexedDBManager {
46
71
  }
47
72
 
48
73
  request.onupgradeneeded = (e) => {
74
+ clearTimeout(openTimeout)
49
75
  const db = e.target.result
50
76
  if (!db.objectStoreNames.contains(DB_CONFIG.STORE_NAME)) {
51
77
  db.createObjectStore(DB_CONFIG.STORE_NAME, { keyPath: 'key' })
@@ -53,15 +79,26 @@ export class IndexedDBManager {
53
79
  }
54
80
  }
55
81
 
56
- checkRequest.onerror = (e) => reject(e.currentTarget.error)
82
+ checkRequest.onerror = (e) => {
83
+ clearTimeout(timeout)
84
+ reject(e.currentTarget.error)
85
+ }
57
86
  })
58
87
  } catch (error) {
59
- console.error('打开数据库失败:', error)
88
+ console.error('[openDatabase] 打开数据库失败:', error)
89
+
90
+ // 如果是超时错误或版本错误,尝试自动重建数据库
91
+ if (error.message?.includes('timeout') || error.message?.includes('version')) {
92
+ await this.forceRecreateDatabase()
93
+ return this.openDatabase() // 递归调用,重新尝试打开
94
+ }
95
+
60
96
  throw error
61
97
  }
62
98
  }
63
99
 
64
100
  async upgradeDatabase (resolve, reject) {
101
+ console.warn('[upgradeDatabase] 升级数据库到版本:', DB_CONFIG.CURRENT_VERSION)
65
102
  const request = this.indexedDB.open(DB_CONFIG.NAME, DB_CONFIG.CURRENT_VERSION)
66
103
 
67
104
  request.onupgradeneeded = (e) => {
@@ -71,11 +108,28 @@ export class IndexedDBManager {
71
108
  }
72
109
  }
73
110
 
74
- request.onsuccess = (e) => resolve(e.target.result)
75
- request.onerror = (e) => reject(e.currentTarget.error)
111
+ request.onsuccess = (e) => {
112
+ resolve(e.target.result)
113
+ }
114
+ request.onerror = (e) => {
115
+ reject(e.currentTarget.error)
116
+ }
76
117
  }
77
118
 
78
119
  async openDB (callback) {
120
+ // 如果数据库不健康,直接调用回调并返回
121
+ if (!IS_DB_HEALTHY) {
122
+ console.warn('[openDB] 数据库不健康,跳过数据库操作')
123
+ setTimeout(() => {
124
+ try {
125
+ callback(null)
126
+ } catch (e) {
127
+ console.error('[openDB] 回调执行错误:', e)
128
+ }
129
+ }, 0)
130
+ return
131
+ }
132
+
79
133
  try {
80
134
  if (this.db) {
81
135
  const isAlive = await this.checkConnection()
@@ -85,13 +139,51 @@ export class IndexedDBManager {
85
139
  }
86
140
  }
87
141
 
88
- this.db = await this.openDatabase()
89
- callback(this.db)
142
+ // 添加超时机制,防止卡死
143
+ const openDbPromise = this.openDatabase()
144
+ const timeoutPromise = new Promise((resolve, reject) => {
145
+ setTimeout(() => reject(new Error('openDB timeout')), 5000)
146
+ })
147
+
148
+ try {
149
+ this.db = await Promise.race([openDbPromise, timeoutPromise])
150
+ callback(this.db)
151
+ } catch (error) {
152
+ console.error('[openDB] 打开数据库连接超时或失败', error)
153
+ // 标记数据库为不健康状态,后续操作使用备用方案
154
+ IS_DB_HEALTHY = false
155
+ try {
156
+ callback(null)
157
+ } catch (callbackError) {
158
+ console.error('[openDB] 回调执行错误:', callbackError)
159
+ }
160
+ }
90
161
  } catch (error) {
91
- console.error('数据库操作失败:', error)
162
+ console.error('[openDB] 数据库操作失败:', error)
163
+
164
+ // 版本错误时尝试重建数据库
92
165
  if (error.message?.includes('version')) {
93
- await this.recreateDatabase()
94
- callback(this.db)
166
+ try {
167
+ await this.recreateDatabase()
168
+ callback(this.db)
169
+ } catch (rebuildError) {
170
+ console.error('[openDB] 重建失败,标记数据库为不健康状态', rebuildError)
171
+ IS_DB_HEALTHY = false
172
+ try {
173
+ callback(null)
174
+ } catch (callbackError) {
175
+ console.error('[openDB] 回调执行错误:', callbackError)
176
+ }
177
+ }
178
+ } else {
179
+ // 其他错误,标记为不健康并使用备用方案
180
+ IS_DB_HEALTHY = false
181
+ console.error('[openDB] 不可恢复的错误,标记数据库为不健康状态')
182
+ try {
183
+ callback(null)
184
+ } catch (callbackError) {
185
+ console.error('[openDB] 回调执行错误:', callbackError)
186
+ }
95
187
  }
96
188
  }
97
189
  }
@@ -102,41 +194,93 @@ export class IndexedDBManager {
102
194
  const store = transaction.objectStore(DB_CONFIG.STORE_NAME)
103
195
  await this.promisifyRequest(store.add({ key: 'alive', data: true }))
104
196
  return true
105
- } catch {
197
+ } catch (error) {
106
198
  return false
107
199
  }
108
200
  }
109
201
 
110
202
  promisifyRequest (request) {
111
203
  return new Promise((resolve, reject) => {
112
- request.onsuccess = () => resolve(request.result)
113
- request.onerror = () => reject(request.error)
204
+ request.onsuccess = () => {
205
+ resolve(request.result)
206
+ }
207
+ request.onerror = () => {
208
+ reject(request.error)
209
+ }
114
210
  })
115
211
  }
116
212
 
117
213
  async getByWeb (key, url, params, callback, processFun) {
118
- if (this.locks[key]) {
119
- await this.locks[key]
120
- return this.getByWeb(key, url, params, callback, processFun)
214
+ // 数据库不健康时使用备用方案
215
+ if (!IS_DB_HEALTHY) {
216
+ console.warn(`[getByWeb] 数据库不健康,使用内存缓存或直接API调用`)
217
+ return this.getByWebWithoutDB(key, url, params, callback, processFun)
121
218
  }
122
219
 
123
- this.locks[key] = (async () => {
220
+ // 锁机制:防止同一键的并发请求
221
+ if (this.locks.has(key)) {
222
+ const timeoutPromise = new Promise((resolve, reject) => {
223
+ setTimeout(() => {
224
+ reject(new Error(`请求超时: ${key}`))
225
+ }, this.requestTimeout)
226
+ })
227
+
228
+ try {
229
+ await Promise.race([this.locks.get(key), timeoutPromise])
230
+ return this.getByWeb(key, url, params, callback, processFun)
231
+ } catch (error) {
232
+ console.error(`[getByWeb] 等待锁期间出错或超时: ${error.message}`)
233
+ this.locks.delete(key)
234
+ }
235
+ }
236
+
237
+ const lockPromise = (async () => {
124
238
  try {
239
+ // 首先检查内存缓存
240
+ if (USE_MEMORY_CACHE && MEMORY_CACHE.has(key)) {
241
+ const cachedData = MEMORY_CACHE.get(key)
242
+ callback(cachedData)
243
+ return
244
+ }
245
+
246
+ // 再检查IndexedDB缓存
125
247
  const data = DB_CONFIG.NAME === LIULI_WEB_DB_NAME ? undefined : await this.getData(key)
248
+
126
249
  if (!data && url) {
250
+ // 缓存未命中,请求网络数据
127
251
  const res = await post(url, params)
128
252
  const processedData = processFun ? processFun(res) : res
129
253
 
254
+ // 同时保存到内存缓存
255
+ if (USE_MEMORY_CACHE) {
256
+ MEMORY_CACHE.set(key, processedData)
257
+ }
258
+
259
+ // 生产环境或非移动配置时保存到IndexedDB
130
260
  if (process.env.NODE_ENV === 'production' || key !== 'webMobileConfig') {
131
261
  await this.add(key, processedData)
132
262
  }
133
263
 
134
264
  callback(processedData)
135
265
  } else {
266
+ // 使用缓存数据
267
+ if (USE_MEMORY_CACHE && data) {
268
+ MEMORY_CACHE.set(key, data)
269
+ }
136
270
  callback(data)
137
271
  }
138
272
  } catch (error) {
139
- console.error('获取数据失败:', error)
273
+ console.error(`[getByWeb] 获取数据失败: ${error.message}`)
274
+
275
+ // IndexedDB操作失败时切换到备用方案
276
+ if (error.name === 'InvalidStateError' || error.message?.includes('database') || error.message?.includes('transaction')) {
277
+ console.error(`[getByWeb] 数据库操作失败,标记为不健康`)
278
+ IS_DB_HEALTHY = false
279
+
280
+ // 使用备用方案获取数据
281
+ return this.getByWebWithoutDB(key, url, params, callback, processFun)
282
+ }
283
+
140
284
  if (process.env.NODE_ENV === 'production' && key !== 'webMobileConfig') {
141
285
  await this.add(key, null)
142
286
  }
@@ -144,65 +288,130 @@ export class IndexedDBManager {
144
288
  }
145
289
  })()
146
290
 
291
+ this.locks.set(key, lockPromise)
292
+
147
293
  try {
148
- await this.locks[key]
294
+ const timeoutPromise = new Promise((resolve, reject) => {
295
+ setTimeout(() => {
296
+ reject(new Error(`操作超时: ${key}`))
297
+ }, this.requestTimeout)
298
+ })
299
+
300
+ await Promise.race([this.locks.get(key), timeoutPromise])
301
+ } catch (error) {
302
+ console.error(`[getByWeb] 执行期间出错或超时: ${error.message}`)
149
303
  } finally {
150
- delete this.locks[key]
304
+ this.locks.delete(key)
305
+ }
306
+ }
307
+
308
+ // 备用方法:不使用IndexedDB,直接使用内存缓存和API
309
+ async getByWebWithoutDB (key, url, params, callback, processFun) {
310
+ // 首先检查内存缓存
311
+ if (USE_MEMORY_CACHE && MEMORY_CACHE.has(key)) {
312
+ const cachedData = MEMORY_CACHE.get(key)
313
+ callback(cachedData)
314
+ return
315
+ }
316
+
317
+ // 无URL则无法获取数据
318
+ if (!url) {
319
+ callback(null)
320
+ return
321
+ }
322
+
323
+ // 直接通过API获取数据
324
+ try {
325
+ const res = await post(url, params)
326
+ const processedData = processFun ? processFun(res) : res
327
+
328
+ // 保存到内存缓存
329
+ if (USE_MEMORY_CACHE) {
330
+ MEMORY_CACHE.set(key, processedData)
331
+ }
332
+
333
+ callback(processedData)
334
+ } catch (error) {
335
+ console.error(`[getByWebWithoutDB] 获取数据失败: ${error.message}`)
336
+ callback(null)
151
337
  }
152
338
  }
153
339
 
154
340
  async getData (key) {
155
341
  return new Promise((resolve) => {
156
342
  this.openDB((db) => {
343
+ if (!db) {
344
+ resolve(null)
345
+ return
346
+ }
157
347
  const store = db.transaction(DB_CONFIG.STORE_NAME, 'readwrite').objectStore(DB_CONFIG.STORE_NAME)
158
348
  const request = store.get(key)
159
- request.onsuccess = (e) => resolve(e.target.result?.data)
160
- request.onerror = () => resolve(null)
349
+
350
+ request.onsuccess = (e) => {
351
+ resolve(e.target.result?.data)
352
+ }
353
+
354
+ request.onerror = () => {
355
+ resolve(null)
356
+ }
161
357
  })
162
358
  })
163
359
  }
164
360
 
165
361
  async add (key, data) {
166
362
  this.openDB((db) => {
363
+ if (!db) return
364
+
167
365
  const store = db.transaction(DB_CONFIG.STORE_NAME, 'readwrite').objectStore(DB_CONFIG.STORE_NAME)
168
366
  const request = store.add({ key, data })
169
- request.onerror = () => this.update(key, data)
367
+
368
+ request.onerror = () => {
369
+ // 添加失败时尝试更新
370
+ this.update(key, data)
371
+ }
170
372
  })
171
373
  }
172
374
 
173
375
  async update (key, data) {
174
376
  this.openDB((db) => {
377
+ if (!db) return
378
+
175
379
  const store = db.transaction(DB_CONFIG.STORE_NAME, 'readwrite').objectStore(DB_CONFIG.STORE_NAME)
176
- const request = store.put({ key, data })
177
- request.onerror = () => console.error('数据更新失败')
380
+ store.put({ key, data })
178
381
  })
179
382
  }
180
383
 
181
384
  async delete (key) {
182
385
  this.openDB((db) => {
386
+ if (!db) return
387
+
183
388
  const store = db.transaction(DB_CONFIG.STORE_NAME, 'readwrite').objectStore(DB_CONFIG.STORE_NAME)
184
- const request = store.delete(key)
185
- request.onerror = () => console.error('数据删除失败')
389
+ store.delete(key)
186
390
  })
187
391
  }
188
392
 
189
393
  async clear (callback) {
190
394
  this.openDB((db) => {
395
+ if (!db) {
396
+ if (typeof callback === 'function') callback()
397
+ return
398
+ }
399
+
191
400
  const store = db.transaction(DB_CONFIG.STORE_NAME, 'readwrite').objectStore(DB_CONFIG.STORE_NAME)
192
401
  const request = store.clear()
402
+
193
403
  request.onerror = () => {
194
- console.error('数据删除失败')
195
- callback()
404
+ if (typeof callback === 'function') callback()
196
405
  }
406
+
197
407
  request.onsuccess = () => {
198
- if (typeof callback === 'function') {
199
- callback()
200
- }
408
+ if (typeof callback === 'function') callback()
201
409
  }
202
410
  })
203
411
  }
204
412
 
205
413
  clearCache () {
414
+ // 清除缓存并刷新页面
206
415
  if (this.indexedDB) {
207
416
  this.clear(() => {
208
417
  location.reload()
@@ -215,22 +424,115 @@ export class IndexedDBManager {
215
424
  async getAll () {
216
425
  return new Promise((resolve, reject) => {
217
426
  this.openDB((db) => {
427
+ if (!db) {
428
+ resolve([])
429
+ return
430
+ }
431
+
218
432
  const store = db.transaction(DB_CONFIG.STORE_NAME, 'readonly').objectStore(DB_CONFIG.STORE_NAME)
219
433
  const request = store.getAll()
220
- request.onsuccess = (e) => resolve(e.target.result)
221
- request.onerror = (e) => reject(e.target.error)
434
+
435
+ request.onsuccess = (e) => {
436
+ resolve(e.target.result)
437
+ }
438
+
439
+ request.onerror = (e) => {
440
+ reject(e.target.error)
441
+ }
222
442
  })
223
443
  })
224
444
  }
225
445
 
226
446
  getAllLegacy (callback) {
227
447
  this.openDB((res) => {
448
+ if (!res) {
449
+ callback(null, new Error('打开数据库失败'))
450
+ return
451
+ }
452
+
228
453
  const store = res.transaction(DB_CONFIG.STORE_NAME, 'readonly').objectStore(DB_CONFIG.STORE_NAME)
229
454
  const request = store.getAll()
230
- request.onerror = (e) => callback(null, e.target.error)
231
- request.onsuccess = (e) => callback(e.target.result)
455
+
456
+ request.onerror = (e) => {
457
+ callback(null, e.target.error)
458
+ }
459
+
460
+ request.onsuccess = (e) => {
461
+ callback(e.target.result)
462
+ }
232
463
  })
233
464
  }
465
+
466
+ async recreateDatabase () {
467
+ try {
468
+ if (this.db) {
469
+ this.db.close()
470
+ this.db = undefined
471
+ }
472
+
473
+ await new Promise((resolve, reject) => {
474
+ const deleteRequest = this.indexedDB.deleteDatabase(DB_CONFIG.NAME)
475
+
476
+ deleteRequest.onsuccess = () => {
477
+ resolve()
478
+ }
479
+
480
+ deleteRequest.onerror = (e) => {
481
+ reject(e.target.error)
482
+ }
483
+ })
484
+
485
+ DB_CONFIG.CURRENT_VERSION = DB_CONFIG.VERSION
486
+ this.db = await this.openDatabase()
487
+ return this.db
488
+ } catch (error) {
489
+ console.error('重新创建数据库失败:', error)
490
+ throw error
491
+ }
492
+ }
493
+
494
+ // 强制重建数据库的方法,带有超时保护
495
+ async forceRecreateDatabase () {
496
+ console.warn('[forceRecreateDatabase] 开始强制重建数据库')
497
+ try {
498
+ // 确保关闭所有连接
499
+ if (this.db) {
500
+ try {
501
+ this.db.close()
502
+ } catch (e) {
503
+ console.warn('[forceRecreateDatabase] 关闭现有连接出错')
504
+ }
505
+ this.db = undefined
506
+ }
507
+
508
+ // 清空锁防止死锁
509
+ this.locks.clear()
510
+
511
+ // 强制删除数据库,带超时保护
512
+ await new Promise((resolve) => {
513
+ const deleteRequest = this.indexedDB.deleteDatabase(DB_CONFIG.NAME)
514
+
515
+ const timeout = setTimeout(() => {
516
+ resolve() // 即使超时也继续执行
517
+ }, 3000)
518
+
519
+ deleteRequest.onsuccess = () => {
520
+ clearTimeout(timeout)
521
+ resolve()
522
+ }
523
+
524
+ deleteRequest.onerror = () => {
525
+ clearTimeout(timeout)
526
+ resolve() // 即使出错也继续执行
527
+ }
528
+ })
529
+
530
+ // 重置版本
531
+ DB_CONFIG.CURRENT_VERSION = DB_CONFIG.VERSION
532
+ } catch (error) {
533
+ console.error('[forceRecreateDatabase] 强制重建过程中出错:', error)
534
+ }
535
+ }
234
536
  }
235
537
 
236
538
  export const indexedDB = new IndexedDBManager()