nsbp-cli 0.2.46 → 0.2.47
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 +1 -1
- package/package.json +1 -1
- package/templates/basic/.husky/commit-msg +0 -3
- package/templates/basic/.husky/pre-commit +0 -3
- package/templates/basic/.husky/pre-push +0 -3
- package/templates/basic/package.json +8 -9
- package/templates/basic/src/containers/Login.tsx +1 -1
- package/templates/basic/src/containers/Photo.tsx +58 -33
- package/templates/basic/src/externals/window.d.ts +2 -2
- package/templates/basic/src/reducers/home.ts +6 -1
- package/templates/basic/src/reducers/index.ts +6 -1
- package/templates/basic/src/reducers/photo.ts +8 -2
- package/templates/basic/src/server/photo.ts +25 -12
- package/templates/basic/src/server/utils.tsx +30 -13
- package/templates/basic/src/services/home.ts +10 -4
- package/templates/basic/src/services/photo.ts +40 -13
- package/templates/basic/src/styled/common.ts +2 -2
- package/templates/basic/src/utils/fetch.ts +1 -1
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.
|
|
150
|
+
- **Version**: `0.2.47`
|
|
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
|
@@ -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.
|
|
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.
|
|
73
|
-
"@testing-library/react": "^
|
|
74
|
-
"@testing-library/user-event": "^14.
|
|
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": "^
|
|
106
|
-
"jest-environment-jsdom": "^
|
|
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.
|
|
120
|
+
"ts-jest": "^29.4.6",
|
|
122
121
|
"ts-loader": "^9.5.0",
|
|
123
|
-
"typescript": "^5.
|
|
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",
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
58
|
+
.reduce((sum: number, width: number) => sum - width, 0)
|
|
44
59
|
|
|
45
60
|
// Calculate position for each photo
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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 = (
|
|
63
|
-
setCurrPhoto(value)
|
|
80
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
81
|
+
setCurrPhoto(Number(e.target.value))
|
|
64
82
|
}
|
|
65
83
|
|
|
66
|
-
const clickHandler = (btn:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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 =
|
|
187
|
-
getPhotoMenu: (dic:
|
|
188
|
-
|
|
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,
|
|
5
|
+
| Record<string, unknown>
|
|
6
6
|
| Array<{ name: string; cover?: string; count?: number }>
|
|
7
7
|
}
|
|
8
|
-
query?: Record<string,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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:
|
|
18
|
+
action: PhotoAction
|
|
13
19
|
) => {
|
|
14
20
|
const { type, data, menu } = action
|
|
15
21
|
|
|
@@ -62,7 +62,16 @@ const getFileMenu = (
|
|
|
62
62
|
return result
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
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:
|
|
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:
|
|
112
|
-
fileList.forEach((item:
|
|
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
|
|
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
|
|
122
|
-
|
|
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:
|
|
137
|
+
.json({ error: 'Internal Server Error', details: error.message })
|
|
126
138
|
}
|
|
127
139
|
}
|
|
128
140
|
|
|
129
|
-
export const getPhotoMenu = (_req:
|
|
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
|
|
138
|
-
|
|
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:
|
|
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
|
-
|
|
16
|
+
interface Request {
|
|
17
|
+
path: string
|
|
18
|
+
query: Record<string, string | 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:
|
|
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
|
|
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
|
|
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:
|
|
48
|
-
return (
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
|
|
5
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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((
|
|
32
|
+
.catch(() => {
|
|
21
33
|
callback && callback()
|
|
22
34
|
})
|
|
23
35
|
}
|
|
24
36
|
|
|
25
|
-
const getPhotoMenu = (
|
|
37
|
+
const getPhotoMenu = (
|
|
38
|
+
dispatch: DispatchFunction,
|
|
39
|
+
callback: CallbackFunction
|
|
40
|
+
) => {
|
|
26
41
|
doGet('getPhotoMenu')
|
|
27
|
-
.then((res:
|
|
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((
|
|
53
|
+
.catch(() => {
|
|
39
54
|
callback && callback()
|
|
40
55
|
})
|
|
41
56
|
}
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
51
|
-
|
|
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,
|
|
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 = (
|
|
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 = (
|
|
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
|
|
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:
|
|
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:
|
|
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
|