nsbp-cli 0.2.46 → 0.2.49

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/README.md CHANGED
@@ -147,7 +147,7 @@ node ./bin/nsbp.js --help # Test CLI locally
147
147
 
148
148
  - **Package Name**: `nsbp-cli`
149
149
  - **Bin Command**: `nsbp` (install globally and run `nsbp --help`)
150
- - **Version**: `0.2.46`
150
+ - **Version**: `0.2.49`
151
151
  - **Dependencies**: chalk, commander, fs-extra, inquirer
152
152
  - **Package Manager**: Uses pnpm (also compatible with npm)
153
153
  - **Node Version**: >=16.0.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nsbp-cli",
3
- "version": "0.2.46",
3
+ "version": "0.2.49",
4
4
  "description": "CLI tool for creating NSBP (Node React SSR by Webpack) projects",
5
5
  "main": "index.js",
6
6
  "homepage": "https://nsbp.erishen.cn/",
@@ -1,6 +1,3 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
1
  # Validate commit message format
5
2
  commit_regex='^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert|BREAKING CHANGE)(\(.+\))?: .{1,50}'
6
3
 
@@ -1,4 +1 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
1
  npx lint-staged
@@ -1,4 +1 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
1
  pnpm run lint
@@ -9,7 +9,7 @@
9
9
  "dev:init": "cross-env INIT=1 webpack --config config/webpack.server.js --mode development",
10
10
  "dev:build:server": "webpack --config config/webpack.server.js --mode development --watch",
11
11
  "dev:build:client": "webpack --config config/webpack.client.js --mode development --watch",
12
- "dev:build:start": "node ./scripts/start.js",
12
+ "dev:build:start": "nodemon --watch build --ext js --exec \"node ./scripts/start.js\"",
13
13
  "build": "pnpm run clean && npm-run-all -l -p build:**",
14
14
  "build:server": "webpack --config config/webpack.server.js --mode production",
15
15
  "build:client": "webpack --config config/webpack.client.js --mode production",
@@ -65,13 +65,13 @@
65
65
  "@babel/preset-react": "^7.24.0",
66
66
  "@babel/preset-typescript": "^7.25.0",
67
67
  "@eslint/js": "^9.39.2",
68
- "@jest/globals": "^30.2.0",
68
+ "@jest/globals": "^30.0.5",
69
69
  "@loadable/babel-plugin": "^5.13.2",
70
70
  "@loadable/webpack-plugin": "^5.15.0",
71
71
  "@playwright/test": "^1.44.0",
72
- "@testing-library/jest-dom": "^6.4.2",
73
- "@testing-library/react": "^15.0.0",
74
- "@testing-library/user-event": "^14.5.2",
72
+ "@testing-library/jest-dom": "^6.6.3",
73
+ "@testing-library/react": "^16.1.0",
74
+ "@testing-library/user-event": "^14.6.1",
75
75
  "@types/express": "^5.0.6",
76
76
  "@types/jest": "^30.0.0",
77
77
  "@types/loadable__component": "^5.13.3",
@@ -81,7 +81,6 @@
81
81
  "@types/react": "^19.2.8",
82
82
  "@types/react-dom": "^19.2.3",
83
83
  "@types/react-helmet": "^6.1.10",
84
- "@types/react-router-dom": "^5.3.3",
85
84
  "@types/serialize-javascript": "^5.0.3",
86
85
  "@types/styled-components": "^5.1.36",
87
86
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -102,8 +101,8 @@
102
101
  "globals": "^17.0.0",
103
102
  "html-webpack-plugin": "^5.6.0",
104
103
  "husky": "^9.0.0",
105
- "jest": "^29.7.0",
106
- "jest-environment-jsdom": "^29.7.0",
104
+ "jest": "^30.0.5",
105
+ "jest-environment-jsdom": "^30.0.5",
107
106
  "less": "^4.2.0",
108
107
  "less-loader": "^12.0.0",
109
108
  "lint-staged": "^15.0.0",
@@ -118,9 +117,9 @@
118
117
  "sass-loader": "^16.0.0",
119
118
  "style-loader": "^4.0.0",
120
119
  "terser-webpack-plugin": "^5.3.0",
121
- "ts-jest": "^29.1.2",
120
+ "ts-jest": "^29.4.6",
122
121
  "ts-loader": "^9.5.0",
123
- "typescript": "^5.0.0",
122
+ "typescript": "^5.8.3",
124
123
  "typescript-loadable-components-plugin": "^1.0.2",
125
124
  "webpack": "^5.96.0",
126
125
  "webpack-cli": "^6.0.1",
@@ -8,7 +8,7 @@ import '../css/test2.sass'
8
8
  import '../css/test3.scss'
9
9
  import { Container } from '@styled/test'
10
10
 
11
- const Login = ({ query }: any) => {
11
+ const Login = ({ query }: { query?: Record<string, string> }) => {
12
12
  return (
13
13
  <Fragment>
14
14
  <Helmet>
@@ -1,4 +1,4 @@
1
- import React, { Fragment, useState, useEffect, useRef } from 'react'
1
+ import React, { Fragment, useState, useEffect, useRef, useMemo } from 'react'
2
2
  import { connect } from 'react-redux'
3
3
  import { Link, useLocation } from 'react-router-dom'
4
4
  import Header from '@components/Header'
@@ -7,29 +7,44 @@ import { Helmet } from 'react-helmet'
7
7
  import { Container, Row } from '@styled/photo'
8
8
  import { motion } from 'framer-motion'
9
9
  import { isSEO, getLocationParams, usePreserveNSBP } from '@/utils'
10
- import { useCurrentFlag } from '@utils/clientConfig'
11
10
  import _ from 'lodash'
12
11
  import { loadDataForContainer } from '@services/photo'
13
12
 
14
13
  const springSettings = { type: 'spring' as const, stiffness: 170, damping: 26 }
15
14
  const NEXT = 'show-next'
16
15
 
17
- const Photo = ({ query, data, menu, getPhotoMenu }: any) => {
16
+ interface QueryParams {
17
+ from?: string
18
+ nsbp?: string | number
19
+ }
20
+
21
+ interface PhotoProps {
22
+ query: QueryParams
23
+ data: [number, number, string][]
24
+ menu: Array<{ name: string; cover?: string; count?: number }>
25
+ getPhotoMenu: (dic: string) => void
26
+ }
27
+
28
+ const Photo = ({ query, data, menu, getPhotoMenu }: PhotoProps) => {
18
29
  const location = useLocation()
19
30
  let { from } = query
20
31
  const { withNSBP } = usePreserveNSBP()
21
- const photos = Array.isArray(data) ? data : []
32
+ // 使用 useMemo 缓存 photos,避免每次渲染都创建新数组
33
+ const photos = useMemo(() => (Array.isArray(data) ? data : []), [data])
22
34
  const [currPhoto, setCurrPhoto] = useState(0)
23
35
  // 使用 ref 来跟踪初始的 dic 值,用于区分首次加载和分类切换
24
36
  const initialDicRef = useRef<string | null>(null)
25
37
 
26
- const [currPhotoData, setCurrPhotoData] = useState(photos[0] || [0, 0, ''])
38
+ const [currPhotoData, setCurrPhotoData] = useState<[number, number, string]>(
39
+ photos[0] || [0, 0, '']
40
+ )
27
41
 
28
42
  const [currWidth, currHeight] = currPhotoData
29
43
 
30
- const widths = photos.map(
31
- ([origW, origH]: any) => (currHeight / origH) * origW
32
- )
44
+ const widths = photos.map((photo) => {
45
+ const [origW, origH] = photo
46
+ return (currHeight / origH) * origW
47
+ })
33
48
 
34
49
  // 同步 currPhoto 和 currPhotoData
35
50
  useEffect(() => {
@@ -40,30 +55,33 @@ const Photo = ({ query, data, menu, getPhotoMenu }: any) => {
40
55
 
41
56
  const leftStartCoords = widths
42
57
  .slice(0, currPhoto)
43
- .reduce((sum: any, width: any) => sum - width, 0)
58
+ .reduce((sum: number, width: number) => sum - width, 0)
44
59
 
45
60
  // Calculate position for each photo
46
- const photoPositions = photos.reduce(
47
- (acc: any, [_origW, _origH]: any, i: any, _arr: any) => {
48
- const prevLeft =
49
- i === 0 ? leftStartCoords : acc[i - 1].left + acc[i - 1].width
50
- acc.push({
51
- left: prevLeft,
52
- height: currHeight,
53
- width: widths[i] || 0
54
- })
55
- return acc
56
- },
57
- []
58
- )
61
+ interface PhotoPosition {
62
+ left: number
63
+ height: number
64
+ width: number
65
+ }
66
+
67
+ const photoPositions = photos.reduce<PhotoPosition[]>((acc, _item, i) => {
68
+ const prevLeft =
69
+ i === 0 ? leftStartCoords : acc[i - 1].left + acc[i - 1].width
70
+ acc.push({
71
+ left: prevLeft,
72
+ height: currHeight,
73
+ width: widths[i] || 0
74
+ })
75
+ return acc
76
+ }, [])
59
77
 
60
78
  // console.log('photoPositions', photoPositions)
61
79
 
62
- const handleChange = ({ target: { value } }: any) => {
63
- setCurrPhoto(value)
80
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
81
+ setCurrPhoto(Number(e.target.value))
64
82
  }
65
83
 
66
- const clickHandler = (btn: any) => {
84
+ const clickHandler = (btn: string) => {
67
85
  let photoIndex = btn === NEXT ? currPhoto + 1 : currPhoto - 1
68
86
 
69
87
  photoIndex = photoIndex >= 0 ? photoIndex : photos.length - 1
@@ -101,6 +119,7 @@ const Photo = ({ query, data, menu, getPhotoMenu }: any) => {
101
119
 
102
120
  // 重置到第一张
103
121
  setCurrPhoto(0)
122
+ // eslint-disable-next-line react-hooks/exhaustive-deps
104
123
  }, [location?.search, from])
105
124
 
106
125
  return (
@@ -114,7 +133,7 @@ const Photo = ({ query, data, menu, getPhotoMenu }: any) => {
114
133
  <Layout query={query}>
115
134
  <Container>
116
135
  <Row>
117
- {_.map(menu, (item: any, index: number) => {
136
+ {_.map(menu, (item: { name: string }, index: number) => {
118
137
  return (
119
138
  <Link
120
139
  key={`menu${index}`}
@@ -143,7 +162,7 @@ const Photo = ({ query, data, menu, getPhotoMenu }: any) => {
143
162
  animate={{ height: currHeight, width: currWidth }}
144
163
  transition={springSettings}
145
164
  >
146
- {photoPositions.map((pos: any, i: any) => (
165
+ {photoPositions.map((pos, i) => (
147
166
  <motion.img
148
167
  key={i}
149
168
  className="demo4-photo"
@@ -175,7 +194,15 @@ const Photo = ({ query, data, menu, getPhotoMenu }: any) => {
175
194
  )
176
195
  }
177
196
 
178
- const mapStateToProps = (state: any) => {
197
+ interface RootState {
198
+ query: QueryParams
199
+ photo: {
200
+ menu: Array<{ name: string; cover?: string; count?: number }>
201
+ data: [number, number, string][]
202
+ }
203
+ }
204
+
205
+ const mapStateToProps = (state: RootState) => {
179
206
  return {
180
207
  query: state?.query,
181
208
  menu: state?.photo?.menu,
@@ -183,10 +210,8 @@ const mapStateToProps = (state: any) => {
183
210
  }
184
211
  }
185
212
 
186
- const mapDispatchToProps = (dispatch: any) => ({
187
- getPhotoMenu: (dic: any) => {
188
- dispatch(loadDataForContainer(null, dic))
189
- }
190
- })
213
+ const mapDispatchToProps = {
214
+ getPhotoMenu: (dic: string) => loadDataForContainer(null, dic)
215
+ }
191
216
 
192
217
  export default connect(mapStateToProps, mapDispatchToProps)(Photo)
@@ -2,10 +2,10 @@ interface ServerState {
2
2
  photo?: {
3
3
  data?: [number, number, string][]
4
4
  menu?:
5
- | Record<string, any>
5
+ | Record<string, unknown>
6
6
  | Array<{ name: string; cover?: string; count?: number }>
7
7
  }
8
- query?: Record<string, any>
8
+ query?: Record<string, unknown>
9
9
  }
10
10
 
11
11
  declare interface Window {
@@ -1,6 +1,11 @@
1
1
  import { GITHUB_ZEITNEXT_GET } from '@store/constants'
2
2
 
3
- export const homeReducer = (state = { data: {} }, action: any) => {
3
+ interface HomeAction {
4
+ type: string
5
+ data?: unknown
6
+ }
7
+
8
+ export const homeReducer = (state = { data: {} }, action: HomeAction) => {
4
9
  const { type, data } = action
5
10
 
6
11
  switch (type) {
@@ -2,7 +2,12 @@ import { homeReducer } from './home'
2
2
  import { photoReducer } from './photo'
3
3
  import { REQUEST_QUERY } from '@store/constants'
4
4
 
5
- const queryReducer = (state = {}, action: any) => {
5
+ interface QueryAction {
6
+ type: string
7
+ query?: Record<string, unknown>
8
+ }
9
+
10
+ const queryReducer = (state = {}, action: QueryAction) => {
6
11
  const { type, query } = action
7
12
 
8
13
  switch (type) {
@@ -3,13 +3,19 @@ import { GET_PHOTO_MENU, GET_PHOTO_WIDTH_HEIGHT } from '@store/constants'
3
3
  interface PhotoState {
4
4
  data: [number, number, string][]
5
5
  menu:
6
- | Record<string, any>
6
+ | Record<string, unknown>
7
7
  | Array<{ name: string; cover?: string; count?: number }>
8
8
  }
9
9
 
10
+ interface PhotoAction {
11
+ type: string
12
+ data?: [number, number, string][]
13
+ menu?: PhotoState['menu']
14
+ }
15
+
10
16
  export const photoReducer = (
11
17
  state: PhotoState = { data: [], menu: {} },
12
- action: any
18
+ action: PhotoAction
13
19
  ) => {
14
20
  const { type, data, menu } = action
15
21
 
@@ -51,13 +51,22 @@ app.use(
51
51
  express.static('public', {
52
52
  dotfiles: 'ignore',
53
53
  setHeaders: (res, filePath) => {
54
- // Cache static assets for 1 year
54
+ // 开发环境使用较短的缓存时间,避免代码更新后浏览器使用旧缓存
55
+ // 生产环境使用 1 年缓存(配合 hash 文件名)
56
+ const isDev = process.env.NODE_ENV !== 'production'
57
+
55
58
  if (
56
59
  filePath.match(
57
60
  /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$/
58
61
  )
59
62
  ) {
60
- res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
63
+ if (isDev) {
64
+ // 开发环境:缓存 1 小时,便于开发调试
65
+ res.setHeader('Cache-Control', 'public, max-age=3600')
66
+ } else {
67
+ // 生产环境:缓存 1 年(配合 webpack 的 contenthash)
68
+ res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
69
+ }
61
70
  }
62
71
  }
63
72
  })
@@ -62,7 +62,16 @@ const getFileMenu = (
62
62
  return result
63
63
  }
64
64
 
65
- export const getPhotoWH = (req: any, res: any) => {
65
+ interface Request {
66
+ query: { dic?: string }
67
+ }
68
+
69
+ interface Response {
70
+ status: (code: number) => Response
71
+ json: (data: unknown) => void
72
+ }
73
+
74
+ export const getPhotoWH = (req: Request, res: Response) => {
66
75
  try {
67
76
  const dic = req.query.dic || ''
68
77
  const photosDicPath = getPhotosDicPath()
@@ -72,7 +81,7 @@ export const getPhotoWH = (req: any, res: any) => {
72
81
  return res.status(400).json({ error: 'Invalid directory name' })
73
82
  }
74
83
 
75
- const fileList: any = []
84
+ const fileList: string[] = []
76
85
  let photoPath = photosDicPath
77
86
  if (dic) {
78
87
  photoPath = path.join(photosDicPath, dic)
@@ -108,25 +117,28 @@ export const getPhotoWH = (req: any, res: any) => {
108
117
 
109
118
  getFileList(photoPath, fileList)
110
119
 
111
- const whArr: any = []
112
- fileList.forEach((item: any, index: number) => {
120
+ const whArr: [number, number, string][] = []
121
+ fileList.forEach((item: string, index: number) => {
113
122
  const data = fs.readFileSync(item)
114
123
  let fileName = path.relative(photosDicPath, fileList[index])
115
- const { width, height }: any = probe.sync(data)
124
+ const result = probe.sync(data)
125
+ const width = result?.width ?? 0
126
+ const height = result?.height ?? 0
116
127
  whArr.push([width, height, fileName])
117
128
  })
118
129
 
119
130
  // 按前端期望的格式包装
120
131
  res.json({ data: whArr })
121
- } catch (err: any) {
122
- console.error('getPhotoWH error:', err)
132
+ } catch (err) {
133
+ const error = err as Error
134
+ console.error('getPhotoWH error:', error)
123
135
  res
124
136
  .status(500)
125
- .json({ error: 'Internal Server Error', details: err.message })
137
+ .json({ error: 'Internal Server Error', details: error.message })
126
138
  }
127
139
  }
128
140
 
129
- export const getPhotoMenu = (_req: any, res: any) => {
141
+ export const getPhotoMenu = (_req: Request, res: Response) => {
130
142
  try {
131
143
  const photosDicPath = getPhotosDicPath()
132
144
 
@@ -134,10 +146,11 @@ export const getPhotoMenu = (_req: any, res: any) => {
134
146
 
135
147
  // 按前端期望的格式包装
136
148
  res.json({ data: fileMenu })
137
- } catch (err: any) {
138
- console.error('getPhotoMenu error:', err)
149
+ } catch (err) {
150
+ const error = err as Error
151
+ console.error('getPhotoMenu error:', error)
139
152
  res
140
153
  .status(500)
141
- .json({ error: 'Internal Server Error', details: err.message })
154
+ .json({ error: 'Internal Server Error', details: error.message })
142
155
  }
143
156
  }
@@ -13,10 +13,25 @@ import Theme from '@components/Theme'
13
13
  import path from 'path'
14
14
  import { ChunkExtractor } from '@loadable/server'
15
15
 
16
- export const render = (req: any, res: any) => {
16
+ interface Request {
17
+ path: string
18
+ query: Record<string, string | string[] | ParsedQs | undefined>
19
+ }
20
+
21
+ interface Response {
22
+ send: (html: string) => void
23
+ status: (code: number) => { send: (msg: string) => void }
24
+ }
25
+
26
+ export const render = (req: Request, res: Response) => {
17
27
  const store = getStore()
18
28
  const { path: reqPath, query } = req
19
- const matchRoutes: any = []
29
+ const matchRoutes: {
30
+ loadData?: (
31
+ resolve: (data?: unknown) => void,
32
+ query: Request['query']
33
+ ) => unknown
34
+ }[] = []
20
35
  const promises = []
21
36
 
22
37
  let { nsbp } = query
@@ -29,13 +44,13 @@ export const render = (req: any, res: any) => {
29
44
  matchPath(reqPath, route.path) ? matchRoutes.push(route) : ''
30
45
  })
31
46
 
32
- matchRoutes.forEach((item: any) => {
47
+ matchRoutes.forEach((item) => {
33
48
  if (item?.loadData) {
34
49
  const promise = new Promise((resolve, reject) => {
35
50
  try {
36
51
  // 将 query 参数传递给 loadData,确保能正确预取数据
37
52
  store.dispatch(item?.loadData(resolve, query))
38
- } catch (e) {
53
+ } catch {
39
54
  reject()
40
55
  }
41
56
  })
@@ -44,8 +59,10 @@ export const render = (req: any, res: any) => {
44
59
  }
45
60
  })
46
61
 
47
- const queryDispatch = (callback: any) => {
48
- return (dispatch: any) => {
62
+ const queryDispatch = (callback: (() => void) | null) => {
63
+ return (
64
+ dispatch: (action: { type: string; query: Request['query'] }) => void
65
+ ) => {
49
66
  // 直接同步执行,避免 setTimeout 导致的竞态条件
50
67
  dispatch({ type: REQUEST_QUERY, query })
51
68
  callback && callback()
@@ -54,8 +71,8 @@ export const render = (req: any, res: any) => {
54
71
 
55
72
  const queryPromise = new Promise((resolve, reject) => {
56
73
  try {
57
- store.dispatch(queryDispatch(resolve))
58
- } catch (e) {
74
+ store.dispatch(queryDispatch(() => resolve()))
75
+ } catch {
59
76
  reject()
60
77
  }
61
78
  })
@@ -68,7 +85,7 @@ export const render = (req: any, res: any) => {
68
85
  const sheet = new ServerStyleSheet()
69
86
  const serverState = store.getState()
70
87
 
71
- const helmet: any = Helmet.renderStatic()
88
+ const helmet = Helmet.renderStatic()
72
89
 
73
90
  const webStats = path.resolve(__dirname, '../public/loadable-stats.json')
74
91
 
@@ -138,14 +155,14 @@ export const render = (req: any, res: any) => {
138
155
  `
139
156
 
140
157
  res.send(html)
141
- } catch (e: any) {
142
- console.error('SSR rendering error:', e)
158
+ } catch (e: Error) {
159
+ console.error('SSR rendering error:', e.message)
143
160
  sheet.seal()
144
161
  res.status(500).send('Internal Server Error')
145
162
  }
146
163
  })
147
- .catch((e: any) => {
148
- console.error('Data loading error:', e)
164
+ .catch((e: Error) => {
165
+ console.error('Data loading error:', e.message)
149
166
  res.status(500).send('Data loading failed')
150
167
  })
151
168
  }
@@ -1,11 +1,17 @@
1
1
  import { doGet } from '@utils/fetch'
2
2
  import { GET_PHOTO_MENU } from '@store/constants'
3
+ import type { Dispatch } from 'redux'
4
+ import type { AxiosResponse } from 'axios'
3
5
 
4
- export const loadData = (resolve: any = null) => {
5
- return (dispatch: any) => {
6
+ interface PhotoMenuResponse {
7
+ data?: unknown[]
8
+ }
9
+
10
+ export const loadData = (resolve: ((data?: unknown) => void) | null = null) => {
11
+ return (dispatch: Dispatch) => {
6
12
  // 预取图片菜单数据
7
13
  doGet('/getPhotoMenu')
8
- .then((res: any) => {
14
+ .then((res: AxiosResponse<PhotoMenuResponse>) => {
9
15
  // axios 响应结构: { data, status, statusText, headers, config, request }
10
16
  if (res.status >= 200 && res.status < 300) {
11
17
  // 请求成功,data 已经是解析后的 JSON 对象
@@ -18,7 +24,7 @@ export const loadData = (resolve: any = null) => {
18
24
  throw new Error(`Status ${res.status}`)
19
25
  }
20
26
  })
21
- .catch((err: any) => {
27
+ .catch((err: Error) => {
22
28
  console.error('Failed to preload photos:', err)
23
29
  // 预取失败但不影响主流程
24
30
  resolve && resolve()
@@ -1,14 +1,26 @@
1
1
  import { doGet } from '@utils/fetch'
2
2
  import { GET_PHOTO_MENU, GET_PHOTO_WIDTH_HEIGHT } from '@store/constants'
3
3
 
4
- const getPhotoWH = (dispatch: any, callback: any, dic = '') => {
4
+ type DispatchFunction = (action: {
5
+ type: string
6
+ data?: unknown
7
+ menu?: unknown
8
+ }) => void
9
+
10
+ type CallbackFunction = (data?: unknown) => void
11
+
12
+ const getPhotoWH = (
13
+ dispatch: DispatchFunction,
14
+ callback: CallbackFunction,
15
+ dic = ''
16
+ ) => {
5
17
  let action = 'getPhotoWH'
6
18
  if (dic) {
7
19
  action += `?dic=${dic}`
8
20
  }
9
21
 
10
22
  doGet(action)
11
- .then((res: any) => {
23
+ .then((res: { data?: { data?: unknown } }) => {
12
24
  // console.log('getPhotoWH_res', res)
13
25
  // axios 响应格式是 { data: { data: [...] }, status: ... },需要取 res.data.data
14
26
  dispatch({
@@ -17,14 +29,17 @@ const getPhotoWH = (dispatch: any, callback: any, dic = '') => {
17
29
  })
18
30
  callback && callback()
19
31
  })
20
- .catch((_e: any) => {
32
+ .catch(() => {
21
33
  callback && callback()
22
34
  })
23
35
  }
24
36
 
25
- const getPhotoMenu = (dispatch: any, callback: any) => {
37
+ const getPhotoMenu = (
38
+ dispatch: DispatchFunction,
39
+ callback: CallbackFunction
40
+ ) => {
26
41
  doGet('getPhotoMenu')
27
- .then((res: any) => {
42
+ .then((res: { data?: { data?: unknown } }) => {
28
43
  // console.log('getPhotoMenu_res', res)
29
44
  // axios 响应格式是 { data: { data: [...] }, status: ... },需要取 res.data.data
30
45
  const { data } = res?.data || {}
@@ -35,22 +50,28 @@ const getPhotoMenu = (dispatch: any, callback: any) => {
35
50
 
36
51
  callback && callback(data)
37
52
  })
38
- .catch((_e: any) => {
53
+ .catch(() => {
39
54
  callback && callback()
40
55
  })
41
56
  }
42
57
 
43
- const getData = (callback: any, dic: any) => {
44
- return (dispatch: any) => {
58
+ interface MenuItem {
59
+ name: string
60
+ cover?: string
61
+ }
62
+
63
+ const getData = (callback: CallbackFunction, dic: string) => {
64
+ return (dispatch: DispatchFunction) => {
45
65
  if (dic) {
46
66
  getPhotoMenu(dispatch, () => {
47
67
  getPhotoWH(dispatch, callback, dic)
48
68
  })
49
69
  } else {
50
- getPhotoMenu(dispatch, (data: any) => {
51
- if (data && data.length > 0) {
70
+ getPhotoMenu(dispatch, (data) => {
71
+ const menuData = data as MenuItem[] | undefined
72
+ if (menuData && menuData.length > 0) {
52
73
  // data[0] 是对象 {name, cover},需要取 name
53
- getPhotoWH(dispatch, callback, data[0].name)
74
+ getPhotoWH(dispatch, callback, menuData[0].name)
54
75
  }
55
76
  })
56
77
  }
@@ -58,13 +79,19 @@ const getData = (callback: any, dic: any) => {
58
79
  }
59
80
 
60
81
  // 用于路由预取数据的 loadData 函数
61
- export const loadData = (resolve: any = null, query: any = {}) => {
82
+ export const loadData = (
83
+ resolve: CallbackFunction | null = null,
84
+ query: { dic?: string } = {}
85
+ ) => {
62
86
  // 从 URL 查询参数中获取 dic
63
87
  const { dic } = query
64
88
  return getData(resolve, dic || '')
65
89
  }
66
90
 
67
91
  // 用于容器内部调用的 loadData 函数(保持向后兼容)
68
- export const loadDataForContainer = (resolve: any = null, dic = '') => {
92
+ export const loadDataForContainer = (
93
+ resolve: CallbackFunction | null = null,
94
+ dic = ''
95
+ ) => {
69
96
  return getData(resolve, dic)
70
97
  }
@@ -1,4 +1,4 @@
1
- import styled, { createGlobalStyle } from 'styled-components'
1
+ import { createGlobalStyle } from 'styled-components'
2
2
 
3
3
  export const GlobalStyle = createGlobalStyle`
4
4
  html,body,#__next {
@@ -7,7 +7,7 @@ export const GlobalStyle = createGlobalStyle`
7
7
  }
8
8
 
9
9
  body {
10
- background-color: ${(props: any) => (props.whiteColor ? 'white' : 'black')};
10
+ background-color: ${(props: { whiteColor?: boolean }) => (props.whiteColor ? 'white' : 'black')};
11
11
  font-family: Helvetica;
12
12
  margin: 0;
13
13
  }
@@ -2,7 +2,7 @@ import axios from 'axios'
2
2
 
3
3
  let prefix = 'http://localhost:3001'
4
4
 
5
- export const doGet = (action: any) => {
5
+ export const doGet = (action: string) => {
6
6
  return new Promise((resolve, reject) => {
7
7
  if (typeof window !== 'undefined') {
8
8
  prefix = window.location.origin