zhou zhou
108 分钟以前 46d872c1a5b77aa8799de4a64888a0a24a1422d6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import { tableConfig } from './tableConfig'
function extractRecords(obj, fields) {
  for (const field of fields) {
    if (field in obj && Array.isArray(obj[field])) {
      return obj[field]
    }
  }
  return []
}
function extractTotal(obj, records, fields) {
  for (const field of fields) {
    if (field in obj && typeof obj[field] === 'number') {
      return obj[field]
    }
  }
  return records.length
}
function extractPagination(obj, data) {
  const result = {}
  const sources = [obj, data ?? {}]
  const currentFields = tableConfig.currentFields
  for (const src of sources) {
    for (const field of currentFields) {
      if (field in src && typeof src[field] === 'number') {
        result.current = src[field]
        break
      }
    }
    if (result.current !== void 0) break
  }
  const sizeFields = tableConfig.sizeFields
  for (const src of sources) {
    for (const field of sizeFields) {
      if (field in src && typeof src[field] === 'number') {
        result.size = src[field]
        break
      }
    }
    if (result.size !== void 0) break
  }
  if (result.current === void 0 && result.size === void 0) return void 0
  return result
}
const defaultResponseAdapter = (response) => {
  const recordFields = tableConfig.recordFields
  if (!response) {
    return { records: [], total: 0 }
  }
  if (Array.isArray(response)) {
    return { records: response, total: response.length }
  }
  if (typeof response !== 'object') {
    console.warn(
      '[tableUtils] 无法识别的响应格式,支持的格式包括: 数组、包含' +
        recordFields.join('/') +
        '字段的对象、嵌套data对象。当前格式:',
      response
    )
    return { records: [], total: 0 }
  }
  const res = response
  let records = []
  let total = 0
  let pagination
  records = extractRecords(res, recordFields)
  total = extractTotal(res, records, tableConfig.totalFields)
  pagination = extractPagination(res)
  if (records.length === 0 && 'data' in res && typeof res.data === 'object') {
    const data = res.data
    records = extractRecords(data, ['list', 'records', 'items'])
    total = extractTotal(data, records, tableConfig.totalFields)
    pagination = extractPagination(res, data)
    if (Array.isArray(res.data)) {
      records = res.data
      total = records.length
    }
  }
  if (!recordFields.some((field) => field in res) && records.length === 0) {
    console.warn('[tableUtils] 无法识别的响应格式')
    console.warn('支持的字段包括: ' + recordFields.join('、'), response)
    console.warn('扩展字段请到 utils/table/tableConfig 文件配置')
  }
  const result = { records, total }
  if (pagination) {
    Object.assign(result, pagination)
  }
  return result
}
const extractTableData = (response) => {
  const data = response.records || response.data || []
  return Array.isArray(data) ? data : []
}
const updatePaginationFromResponse = (pagination, response) => {
  pagination.total = response.total ?? pagination.total ?? 0
  if (response.current !== void 0) {
    pagination.current = response.current
  }
  const maxPage = Math.max(1, Math.ceil(pagination.total / (pagination.size || 1)))
  if (pagination.current > maxPage) {
    pagination.current = maxPage
  }
}
const createSmartDebounce = (fn, delay) => {
  let timeoutId = null
  let lastArgs = null
  let lastResolve = null
  let lastReject = null
  const debouncedFn = (...args) => {
    return new Promise((resolve, reject) => {
      if (timeoutId) clearTimeout(timeoutId)
      lastArgs = args
      lastResolve = resolve
      lastReject = reject
      timeoutId = setTimeout(async () => {
        try {
          const result = await fn(...args)
          resolve(result)
        } catch (error) {
          reject(error)
        } finally {
          timeoutId = null
          lastArgs = null
          lastResolve = null
          lastReject = null
        }
      }, delay)
    })
  }
  debouncedFn.cancel = () => {
    if (timeoutId) clearTimeout(timeoutId)
    timeoutId = null
    lastArgs = null
    lastResolve = null
    lastReject = null
  }
  debouncedFn.flush = async () => {
    if (timeoutId && lastArgs && lastResolve && lastReject) {
      clearTimeout(timeoutId)
      timeoutId = null
      const args = lastArgs
      const resolve = lastResolve
      const reject = lastReject
      lastArgs = null
      lastResolve = null
      lastReject = null
      try {
        const result = await fn(...args)
        resolve(result)
        return result
      } catch (error) {
        reject(error)
        throw error
      }
    }
    return Promise.resolve()
  }
  return debouncedFn
}
const createErrorHandler = (onError, enableLog = false) => {
  const logger = {
    error: (message, ...args) => {
      if (enableLog) console.error(`[useTable] ${message}`, ...args)
    }
  }
  return (err, context) => {
    const tableError = {
      code: 'UNKNOWN_ERROR',
      message: '未知错误',
      details: err
    }
    if (err instanceof Error) {
      tableError.message = err.message
      tableError.code = err.name
    } else if (typeof err === 'string') {
      tableError.message = err
    }
    logger.error(`${context}:`, err)
    onError?.(tableError)
    return tableError
  }
}
export {
  createErrorHandler,
  createSmartDebounce,
  defaultResponseAdapter,
  extractTableData,
  updatePaginationFromResponse
}