feat: 配置 TailwindCSS 并迁移到 Redux
- 配置 TailwindCSS 支持小程序开发
- 安装 tailwindcss, postcss, autoprefixer
- 安装 weapp-tailwindcss 插件支持小程序
- 配置 tailwind.config.js 和 postcss.config.js
- 更新 Taro 配置支持 TailwindCSS
- 从 Zustand 迁移到 Redux Toolkit
- 移除 zustand 依赖
- 安装 redux, react-redux, redux-thunk, @reduxjs/toolkit
- 重构状态管理架构:
- src/constants/ - Action 类型常量
- src/actions/ - Action creators 和异步 actions
- src/reducers/ - Reducers
- src/selectors/ - 状态选择器
- src/hooks/redux.ts - 类型化 hooks
- 更新组件使用新的 Redux API
- 保持数据持久化功能
- 更新应用配置
- 将 app.ts 重命名为 app.tsx 支持 JSX
- 添加 Redux Provider 到应用根组件
- 更新 TODO.md 标记完成状态
- 构建验证通过,所有功能正常
This commit is contained in:
13
TODO.md
13
TODO.md
@@ -53,11 +53,14 @@ interface HistoryRecord {
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### 2.2 状态管理
|
#### 2.2 状态管理
|
||||||
- [ ] 创建 `src/store/` 目录
|
- [x] 创建 `src/store/` 目录
|
||||||
- [ ] 实现 Zustand 状态管理
|
- [x] 实现 Redux 状态管理(已从 Zustand 迁移到 Redux Toolkit)
|
||||||
- [ ] `src/store/historyStore.ts` - 历史记录管理
|
- [x] `src/reducers/history.ts` - 历史记录管理
|
||||||
- [ ] `src/store/templateStore.ts` - 模板配置管理
|
- [x] `src/reducers/template.ts` - 模板配置管理
|
||||||
- [ ] 添加数据持久化功能
|
- [x] `src/actions/` - Action creators
|
||||||
|
- [x] `src/selectors/` - 选择器
|
||||||
|
- [x] `src/hooks/redux.ts` - 类型化 hooks
|
||||||
|
- [x] 添加数据持久化功能(通过 Taro Storage API)
|
||||||
|
|
||||||
### 阶段三:模板卡片首页开发 (优先级:高)
|
### 阶段三:模板卡片首页开发 (优先级:高)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig, type UserConfigExport } from '@tarojs/cli'
|
import { defineConfig, type UserConfigExport } from '@tarojs/cli'
|
||||||
|
import { UnifiedWebpackPluginV5 } from 'weapp-tailwindcss/webpack'
|
||||||
|
|
||||||
import devConfig from './dev'
|
import devConfig from './dev'
|
||||||
import prodConfig from './prod'
|
import prodConfig from './prod'
|
||||||
@@ -56,6 +57,20 @@ export default defineConfig<'vite'>(async (merge) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webpackChain(chain) {
|
||||||
|
chain.merge({
|
||||||
|
plugin: {
|
||||||
|
install: {
|
||||||
|
plugin: UnifiedWebpackPluginV5,
|
||||||
|
args: [{
|
||||||
|
appType: 'taro',
|
||||||
|
// 下面个配置,会开启 rem -> rpx 的转化
|
||||||
|
rem2rpx: true
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// 小程序平台特定配置
|
// 小程序平台特定配置
|
||||||
weapp: {
|
weapp: {
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -12,6 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"claude": "claude --dangerously-skip-permissions",
|
"claude": "claude --dangerously-skip-permissions",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
|
"postinstall": "npx weapp-tw patch",
|
||||||
"new": "taro new",
|
"new": "taro new",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.24.4",
|
"@babel/runtime": "^7.24.4",
|
||||||
|
"@reduxjs/toolkit": "^2.9.0",
|
||||||
"@tarojs/components": "4.1.6",
|
"@tarojs/components": "4.1.6",
|
||||||
"@tarojs/helper": "4.1.6",
|
"@tarojs/helper": "4.1.6",
|
||||||
"@tarojs/plugin-framework-react": "4.1.6",
|
"@tarojs/plugin-framework-react": "4.1.6",
|
||||||
@@ -68,7 +70,9 @@
|
|||||||
"@tarojs/taro": "4.1.6",
|
"@tarojs/taro": "4.1.6",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
"zustand": "^5.0.8"
|
"react-redux": "^9.2.0",
|
||||||
|
"redux": "^5.0.1",
|
||||||
|
"redux-thunk": "^3.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.4",
|
"@babel/core": "^7.24.4",
|
||||||
@@ -87,6 +91,7 @@
|
|||||||
"@types/minimatch": "^5",
|
"@types/minimatch": "^5",
|
||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
"babel-jest": "^30.1.2",
|
"babel-jest": "^30.1.2",
|
||||||
"babel-preset-taro": "4.1.6",
|
"babel-preset-taro": "4.1.6",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
@@ -102,9 +107,11 @@
|
|||||||
"react-refresh": "^0.14.0",
|
"react-refresh": "^0.14.0",
|
||||||
"stylelint": "^16.4.0",
|
"stylelint": "^16.4.0",
|
||||||
"stylelint-config-standard": "^38.0.0",
|
"stylelint-config-standard": "^38.0.0",
|
||||||
|
"tailwindcss": "^4.1.12",
|
||||||
"terser": "^5.30.4",
|
"terser": "^5.30.4",
|
||||||
"ts-jest": "^29.4.1",
|
"ts-jest": "^29.4.1",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"vite": "^4.2.0"
|
"vite": "^4.2.0",
|
||||||
|
"weapp-tailwindcss": "^4.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2001
pnpm-lock.yaml
generated
2001
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
7
postcss.config.js
Normal file
7
postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// postcss 插件以 object 方式注册的话,是按照由上到下的顺序执行的
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
200
src/actions/history.ts
Normal file
200
src/actions/history.ts
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { Dispatch } from 'redux'
|
||||||
|
import { HistoryRecord } from '../store/types'
|
||||||
|
import * as types from '../constants/history'
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'historyRecords'
|
||||||
|
|
||||||
|
// Action creators
|
||||||
|
export const loadRecordsRequest = () => ({
|
||||||
|
type: types.LOAD_RECORDS_REQUEST
|
||||||
|
})
|
||||||
|
|
||||||
|
export const loadRecordsSuccess = (records: HistoryRecord[]) => ({
|
||||||
|
type: types.LOAD_RECORDS_SUCCESS,
|
||||||
|
payload: records
|
||||||
|
})
|
||||||
|
|
||||||
|
export const loadRecordsFailure = (error: string) => ({
|
||||||
|
type: types.LOAD_RECORDS_FAILURE,
|
||||||
|
payload: error
|
||||||
|
})
|
||||||
|
|
||||||
|
export const addRecordRequest = () => ({
|
||||||
|
type: types.ADD_RECORD_REQUEST
|
||||||
|
})
|
||||||
|
|
||||||
|
export const addRecordSuccess = (record: HistoryRecord) => ({
|
||||||
|
type: types.ADD_RECORD_SUCCESS,
|
||||||
|
payload: record
|
||||||
|
})
|
||||||
|
|
||||||
|
export const addRecordFailure = (error: string) => ({
|
||||||
|
type: types.ADD_RECORD_FAILURE,
|
||||||
|
payload: error
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateRecordRequest = () => ({
|
||||||
|
type: types.UPDATE_RECORD_REQUEST
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateRecordSuccess = (record: HistoryRecord) => ({
|
||||||
|
type: types.UPDATE_RECORD_SUCCESS,
|
||||||
|
payload: record
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateRecordFailure = (error: string) => ({
|
||||||
|
type: types.UPDATE_RECORD_FAILURE,
|
||||||
|
payload: error
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteRecordRequest = () => ({
|
||||||
|
type: types.DELETE_RECORD_REQUEST
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteRecordSuccess = (id: string) => ({
|
||||||
|
type: types.DELETE_RECORD_SUCCESS,
|
||||||
|
payload: id
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteRecordFailure = (error: string) => ({
|
||||||
|
type: types.DELETE_RECORD_FAILURE,
|
||||||
|
payload: error
|
||||||
|
})
|
||||||
|
|
||||||
|
export const clearRecordsRequest = () => ({
|
||||||
|
type: types.CLEAR_RECORDS_REQUEST
|
||||||
|
})
|
||||||
|
|
||||||
|
export const clearRecordsSuccess = () => ({
|
||||||
|
type: types.CLEAR_RECORDS_SUCCESS
|
||||||
|
})
|
||||||
|
|
||||||
|
export const clearRecordsFailure = (error: string) => ({
|
||||||
|
type: types.CLEAR_RECORDS_FAILURE,
|
||||||
|
payload: error
|
||||||
|
})
|
||||||
|
|
||||||
|
export const clearError = () => ({
|
||||||
|
type: types.CLEAR_ERROR
|
||||||
|
})
|
||||||
|
|
||||||
|
// Async actions
|
||||||
|
export const loadRecords = () => {
|
||||||
|
return async (dispatch: Dispatch) => {
|
||||||
|
dispatch(loadRecordsRequest())
|
||||||
|
try {
|
||||||
|
const savedRecords = Taro.getStorageSync(STORAGE_KEY) || []
|
||||||
|
dispatch(loadRecordsSuccess(savedRecords))
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : '加载历史记录失败'
|
||||||
|
dispatch(loadRecordsFailure(errorMsg))
|
||||||
|
console.error('加载历史记录失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addRecord = (recordData: Omit<HistoryRecord, 'id' | 'createTime' | 'updateTime'>) => {
|
||||||
|
return async (dispatch: Dispatch, getState: () => any) => {
|
||||||
|
dispatch(addRecordRequest())
|
||||||
|
try {
|
||||||
|
const id = `record_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||||
|
const now = new Date().toISOString()
|
||||||
|
|
||||||
|
const newRecord: HistoryRecord = {
|
||||||
|
...recordData,
|
||||||
|
id,
|
||||||
|
createTime: now,
|
||||||
|
updateTime: now
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentRecords = getState().history.records
|
||||||
|
const updatedRecords = [newRecord, ...currentRecords]
|
||||||
|
|
||||||
|
await Taro.setStorage({
|
||||||
|
key: STORAGE_KEY,
|
||||||
|
data: updatedRecords
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(addRecordSuccess(newRecord))
|
||||||
|
return id
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : '添加记录失败'
|
||||||
|
dispatch(addRecordFailure(errorMsg))
|
||||||
|
console.error('添加记录失败:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateRecord = (id: string, updates: Partial<HistoryRecord>) => {
|
||||||
|
return async (dispatch: Dispatch, getState: () => any) => {
|
||||||
|
dispatch(updateRecordRequest())
|
||||||
|
try {
|
||||||
|
const currentRecords = getState().history.records
|
||||||
|
const recordIndex = currentRecords.findIndex((record: HistoryRecord) => record.id === id)
|
||||||
|
|
||||||
|
if (recordIndex === -1) {
|
||||||
|
throw new Error('记录不存在')
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedRecord = {
|
||||||
|
...currentRecords[recordIndex],
|
||||||
|
...updates,
|
||||||
|
updateTime: new Date().toISOString()
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedRecords = [...currentRecords]
|
||||||
|
updatedRecords[recordIndex] = updatedRecord
|
||||||
|
|
||||||
|
await Taro.setStorage({
|
||||||
|
key: STORAGE_KEY,
|
||||||
|
data: updatedRecords
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(updateRecordSuccess(updatedRecord))
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : '更新记录失败'
|
||||||
|
dispatch(updateRecordFailure(errorMsg))
|
||||||
|
console.error('更新记录失败:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteRecord = (id: string) => {
|
||||||
|
return async (dispatch: Dispatch, getState: () => any) => {
|
||||||
|
dispatch(deleteRecordRequest())
|
||||||
|
try {
|
||||||
|
const currentRecords = getState().history.records
|
||||||
|
const updatedRecords = currentRecords.filter((record: HistoryRecord) => record.id !== id)
|
||||||
|
|
||||||
|
await Taro.setStorage({
|
||||||
|
key: STORAGE_KEY,
|
||||||
|
data: updatedRecords
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(deleteRecordSuccess(id))
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : '删除记录失败'
|
||||||
|
dispatch(deleteRecordFailure(errorMsg))
|
||||||
|
console.error('删除记录失败:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const clearRecords = () => {
|
||||||
|
return async (dispatch: Dispatch) => {
|
||||||
|
dispatch(clearRecordsRequest())
|
||||||
|
try {
|
||||||
|
await Taro.removeStorage({ key: STORAGE_KEY })
|
||||||
|
dispatch(clearRecordsSuccess())
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : '清空记录失败'
|
||||||
|
dispatch(clearRecordsFailure(errorMsg))
|
||||||
|
console.error('清空记录失败:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/actions/template.ts
Normal file
12
src/actions/template.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Template } from '../store/types'
|
||||||
|
import * as types from '../constants/template'
|
||||||
|
|
||||||
|
export const initTemplates = (templates: Template[]) => ({
|
||||||
|
type: types.INIT_TEMPLATES,
|
||||||
|
payload: templates
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setSelectedTemplate = (template: Template | null) => ({
|
||||||
|
type: types.SET_SELECTED_TEMPLATE,
|
||||||
|
payload: template
|
||||||
|
})
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
#app{
|
#app{
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
import { PropsWithChildren } from 'react'
|
import { PropsWithChildren } from 'react'
|
||||||
import { useLaunch } from '@tarojs/taro'
|
import { useLaunch } from '@tarojs/taro'
|
||||||
|
import { Provider } from 'react-redux'
|
||||||
|
import configStore from './store'
|
||||||
|
|
||||||
import './app.css'
|
import './app.css'
|
||||||
|
|
||||||
|
const store = configStore()
|
||||||
|
|
||||||
function App({ children }: PropsWithChildren<any>) {
|
function App({ children }: PropsWithChildren<any>) {
|
||||||
useLaunch(() => {
|
useLaunch(() => {
|
||||||
console.log('App launched.')
|
console.log('App launched.')
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// children 是将要会渲染的页面
|
// children 是将要会渲染的页面
|
||||||
return children
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
{children}
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
22
src/constants/history.ts
Normal file
22
src/constants/history.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// History action types
|
||||||
|
export const LOAD_RECORDS_REQUEST = 'LOAD_RECORDS_REQUEST'
|
||||||
|
export const LOAD_RECORDS_SUCCESS = 'LOAD_RECORDS_SUCCESS'
|
||||||
|
export const LOAD_RECORDS_FAILURE = 'LOAD_RECORDS_FAILURE'
|
||||||
|
|
||||||
|
export const ADD_RECORD_REQUEST = 'ADD_RECORD_REQUEST'
|
||||||
|
export const ADD_RECORD_SUCCESS = 'ADD_RECORD_SUCCESS'
|
||||||
|
export const ADD_RECORD_FAILURE = 'ADD_RECORD_FAILURE'
|
||||||
|
|
||||||
|
export const UPDATE_RECORD_REQUEST = 'UPDATE_RECORD_REQUEST'
|
||||||
|
export const UPDATE_RECORD_SUCCESS = 'UPDATE_RECORD_SUCCESS'
|
||||||
|
export const UPDATE_RECORD_FAILURE = 'UPDATE_RECORD_FAILURE'
|
||||||
|
|
||||||
|
export const DELETE_RECORD_REQUEST = 'DELETE_RECORD_REQUEST'
|
||||||
|
export const DELETE_RECORD_SUCCESS = 'DELETE_RECORD_SUCCESS'
|
||||||
|
export const DELETE_RECORD_FAILURE = 'DELETE_RECORD_FAILURE'
|
||||||
|
|
||||||
|
export const CLEAR_RECORDS_REQUEST = 'CLEAR_RECORDS_REQUEST'
|
||||||
|
export const CLEAR_RECORDS_SUCCESS = 'CLEAR_RECORDS_SUCCESS'
|
||||||
|
export const CLEAR_RECORDS_FAILURE = 'CLEAR_RECORDS_FAILURE'
|
||||||
|
|
||||||
|
export const CLEAR_ERROR = 'CLEAR_ERROR'
|
||||||
3
src/constants/template.ts
Normal file
3
src/constants/template.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Template action types
|
||||||
|
export const INIT_TEMPLATES = 'INIT_TEMPLATES'
|
||||||
|
export const SET_SELECTED_TEMPLATE = 'SET_SELECTED_TEMPLATE'
|
||||||
6
src/hooks/redux.ts
Normal file
6
src/hooks/redux.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux'
|
||||||
|
import type { RootState, AppDispatch } from '../store'
|
||||||
|
|
||||||
|
// 使用类型化的 hooks
|
||||||
|
export const useAppDispatch = () => useDispatch<AppDispatch>()
|
||||||
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
import { View, Text, ScrollView } from '@tarojs/components'
|
import { View, Text, ScrollView } from '@tarojs/components'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { useHistoryStore } from '../../store'
|
import { useAppDispatch, useAppSelector } from '../../hooks/redux'
|
||||||
|
import { selectHistoryRecords } from '../../selectors/history'
|
||||||
|
import { loadRecords, clearRecords } from '../../actions/history'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
|
||||||
export default function History() {
|
export default function History() {
|
||||||
const {
|
const dispatch = useAppDispatch()
|
||||||
records,
|
const records = useAppSelector(selectHistoryRecords)
|
||||||
loadRecords,
|
|
||||||
clearRecords
|
|
||||||
} = useHistoryStore()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadRecords()
|
dispatch(loadRecords())
|
||||||
}, [loadRecords])
|
}, [dispatch])
|
||||||
|
|
||||||
const clearHistory = () => {
|
const clearHistory = () => {
|
||||||
Taro.showModal({
|
Taro.showModal({
|
||||||
@@ -22,7 +21,7 @@ export default function History() {
|
|||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
try {
|
try {
|
||||||
await clearRecords()
|
await dispatch(clearRecords())
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '清空成功',
|
title: '清空成功',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
import { View, Text } from '@tarojs/components'
|
import { View, Text } from '@tarojs/components'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { navigateTo } from '@tarojs/taro'
|
import { navigateTo } from '@tarojs/taro'
|
||||||
import { useTemplateStore, Template } from '../../store/index'
|
import { useAppDispatch, useAppSelector } from '../../hooks/redux'
|
||||||
|
import { selectTemplates } from '../../selectors/template'
|
||||||
|
import { initTemplates, setSelectedTemplate } from '../../actions/template'
|
||||||
|
import { Template } from '../../store/types'
|
||||||
import TemplateCard from '../../components/TemplateCard'
|
import TemplateCard from '../../components/TemplateCard'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { templates, initTemplates, setSelectedTemplate } = useTemplateStore()
|
const dispatch = useAppDispatch()
|
||||||
|
const templates = useAppSelector(selectTemplates)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initTemplates()
|
// 需要先检查是否有模板配置文件
|
||||||
}, [initTemplates])
|
const { TEMPLATE_CONFIG } = require('../../config/templates')
|
||||||
|
dispatch(initTemplates(TEMPLATE_CONFIG))
|
||||||
|
}, [dispatch])
|
||||||
|
|
||||||
const handleTemplateClick = (template: Template) => {
|
const handleTemplateClick = (template: Template) => {
|
||||||
// 设置选中的模板
|
// 设置选中的模板
|
||||||
setSelectedTemplate(template)
|
dispatch(setSelectedTemplate(template))
|
||||||
|
|
||||||
// 跳转到原有的功能页面,保持老功能可用
|
// 跳转到原有的功能页面,保持老功能可用
|
||||||
navigateTo({
|
navigateTo({
|
||||||
url: `/pages/index/index?templateId=${template.id}&templateName=${encodeURIComponent(template.name)}&templateType=${template.type}`
|
url: `/pages/index/index?templateId=${template.id}&templateName=${encodeURIComponent(template.name)}&templateType=${template.type}`
|
||||||
|
|||||||
91
src/reducers/history.ts
Normal file
91
src/reducers/history.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { HistoryRecord } from '../store/types'
|
||||||
|
import * as types from '../constants/history'
|
||||||
|
|
||||||
|
export interface HistoryState {
|
||||||
|
records: HistoryRecord[]
|
||||||
|
loading: boolean
|
||||||
|
error: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: HistoryState = {
|
||||||
|
records: [],
|
||||||
|
loading: false,
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function historyReducer(state = initialState, action: any): HistoryState {
|
||||||
|
switch (action.type) {
|
||||||
|
case types.LOAD_RECORDS_REQUEST:
|
||||||
|
case types.ADD_RECORD_REQUEST:
|
||||||
|
case types.UPDATE_RECORD_REQUEST:
|
||||||
|
case types.DELETE_RECORD_REQUEST:
|
||||||
|
case types.CLEAR_RECORDS_REQUEST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.LOAD_RECORDS_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
records: action.payload,
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.ADD_RECORD_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
records: [action.payload, ...state.records],
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.UPDATE_RECORD_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
records: state.records.map(record =>
|
||||||
|
record.id === action.payload.id ? action.payload : record
|
||||||
|
),
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.DELETE_RECORD_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
records: state.records.filter(record => record.id !== action.payload),
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.CLEAR_RECORDS_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
records: [],
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.LOAD_RECORDS_FAILURE:
|
||||||
|
case types.ADD_RECORD_FAILURE:
|
||||||
|
case types.UPDATE_RECORD_FAILURE:
|
||||||
|
case types.DELETE_RECORD_FAILURE:
|
||||||
|
case types.CLEAR_RECORDS_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
error: action.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.CLEAR_ERROR:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/reducers/index.ts
Normal file
11
src/reducers/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { combineReducers } from 'redux'
|
||||||
|
import history from './history'
|
||||||
|
import template from './template'
|
||||||
|
|
||||||
|
const rootReducer = combineReducers({
|
||||||
|
history,
|
||||||
|
template
|
||||||
|
})
|
||||||
|
|
||||||
|
export type RootState = ReturnType<typeof rootReducer>
|
||||||
|
export default rootReducer
|
||||||
31
src/reducers/template.ts
Normal file
31
src/reducers/template.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Template } from '../store/types'
|
||||||
|
import * as types from '../constants/template'
|
||||||
|
|
||||||
|
export interface TemplateState {
|
||||||
|
templates: Template[]
|
||||||
|
selectedTemplate: Template | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: TemplateState = {
|
||||||
|
templates: [],
|
||||||
|
selectedTemplate: null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function templateReducer(state = initialState, action: any): TemplateState {
|
||||||
|
switch (action.type) {
|
||||||
|
case types.INIT_TEMPLATES:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
templates: action.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.SET_SELECTED_TEMPLATE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
selectedTemplate: action.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/selectors/history.ts
Normal file
18
src/selectors/history.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { RootState } from '../store'
|
||||||
|
import { HistoryRecord } from '../store/types'
|
||||||
|
|
||||||
|
// History selectors
|
||||||
|
export const selectHistoryRecords = (state: RootState): HistoryRecord[] =>
|
||||||
|
state.history.records
|
||||||
|
|
||||||
|
export const selectHistoryLoading = (state: RootState): boolean =>
|
||||||
|
state.history.loading
|
||||||
|
|
||||||
|
export const selectHistoryError = (state: RootState): string | null =>
|
||||||
|
state.history.error
|
||||||
|
|
||||||
|
export const selectRecordById = (state: RootState, id: string): HistoryRecord | undefined =>
|
||||||
|
state.history.records.find(record => record.id === id)
|
||||||
|
|
||||||
|
export const selectRecordsByStatus = (state: RootState, status: HistoryRecord['status']): HistoryRecord[] =>
|
||||||
|
state.history.records.filter(record => record.status === status)
|
||||||
15
src/selectors/template.ts
Normal file
15
src/selectors/template.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { RootState } from '../store'
|
||||||
|
import { Template } from '../store/types'
|
||||||
|
|
||||||
|
// Template selectors
|
||||||
|
export const selectTemplates = (state: RootState): Template[] =>
|
||||||
|
state.template.templates
|
||||||
|
|
||||||
|
export const selectSelectedTemplate = (state: RootState): Template | null =>
|
||||||
|
state.template.selectedTemplate
|
||||||
|
|
||||||
|
export const selectTemplateById = (state: RootState, id: string): Template | undefined =>
|
||||||
|
state.template.templates.find(template => template.id === id)
|
||||||
|
|
||||||
|
export const selectTemplatesByType = (state: RootState, type: 'image-to-image' | 'image-to-video'): Template[] =>
|
||||||
|
state.template.templates.filter(template => template.type === type)
|
||||||
167
src/store/README.md
Normal file
167
src/store/README.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# Redux Store 使用指南
|
||||||
|
|
||||||
|
本项目已从 Zustand 迁移到 Redux Toolkit,以下是使用指南。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── store/
|
||||||
|
│ ├── index.ts # Store 配置
|
||||||
|
│ └── types.ts # 类型定义
|
||||||
|
├── constants/ # Action 类型常量
|
||||||
|
│ ├── history.ts
|
||||||
|
│ └── template.ts
|
||||||
|
├── actions/ # Action creators
|
||||||
|
│ ├── history.ts
|
||||||
|
│ └── template.ts
|
||||||
|
├── reducers/ # Reducers
|
||||||
|
│ ├── index.ts
|
||||||
|
│ ├── history.ts
|
||||||
|
│ └── template.ts
|
||||||
|
├── selectors/ # 选择器
|
||||||
|
│ ├── history.ts
|
||||||
|
│ └── template.ts
|
||||||
|
└── hooks/
|
||||||
|
└── redux.ts # 类型化的 hooks
|
||||||
|
```
|
||||||
|
|
||||||
|
## 基本使用
|
||||||
|
|
||||||
|
### 1. 在组件中使用 Redux
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../hooks/redux'
|
||||||
|
import { selectHistoryRecords, selectHistoryLoading } from '../../selectors/history'
|
||||||
|
import { loadRecords, addRecord } from '../../actions/history'
|
||||||
|
|
||||||
|
export default function MyComponent() {
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
const records = useAppSelector(selectHistoryRecords)
|
||||||
|
const loading = useAppSelector(selectHistoryLoading)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(loadRecords())
|
||||||
|
}, [dispatch])
|
||||||
|
|
||||||
|
const handleAddRecord = async () => {
|
||||||
|
try {
|
||||||
|
await dispatch(addRecord({
|
||||||
|
type: 'image-to-image',
|
||||||
|
templateId: '1',
|
||||||
|
templateName: '艺术风格',
|
||||||
|
inputImage: 'path/to/image.jpg',
|
||||||
|
status: 'generating'
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('添加记录失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{loading && <Text>加载中...</Text>}
|
||||||
|
{records.map(record => (
|
||||||
|
<View key={record.id}>
|
||||||
|
<Text>{record.templateName}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
<Button onClick={handleAddRecord}>添加记录</Button>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 历史记录管理
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// 加载历史记录
|
||||||
|
dispatch(loadRecords())
|
||||||
|
|
||||||
|
// 添加新记录
|
||||||
|
dispatch(addRecord({
|
||||||
|
type: 'image-to-video',
|
||||||
|
templateId: '2',
|
||||||
|
templateName: '视频生成',
|
||||||
|
inputImage: 'path/to/image.jpg',
|
||||||
|
status: 'generating'
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 更新记录
|
||||||
|
dispatch(updateRecord('record_id', {
|
||||||
|
status: 'completed',
|
||||||
|
outputResult: 'path/to/result.mp4'
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 删除记录
|
||||||
|
dispatch(deleteRecord('record_id'))
|
||||||
|
|
||||||
|
// 清空所有记录
|
||||||
|
dispatch(clearRecords())
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 模板管理
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// 初始化模板
|
||||||
|
import { TEMPLATE_CONFIG } from '../../config/templates'
|
||||||
|
dispatch(initTemplates(TEMPLATE_CONFIG))
|
||||||
|
|
||||||
|
// 设置选中的模板
|
||||||
|
dispatch(setSelectedTemplate(template))
|
||||||
|
|
||||||
|
// 获取模板列表
|
||||||
|
const templates = useAppSelector(selectTemplates)
|
||||||
|
|
||||||
|
// 获取选中的模板
|
||||||
|
const selectedTemplate = useAppSelector(selectSelectedTemplate)
|
||||||
|
|
||||||
|
// 根据类型筛选模板
|
||||||
|
const videoTemplates = useAppSelector(state =>
|
||||||
|
selectTemplatesByType(state, 'image-to-video')
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 选择器使用
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// 基本选择器
|
||||||
|
const records = useAppSelector(selectHistoryRecords)
|
||||||
|
const loading = useAppSelector(selectHistoryLoading)
|
||||||
|
const error = useAppSelector(selectHistoryError)
|
||||||
|
|
||||||
|
// 带参数的选择器
|
||||||
|
const record = useAppSelector(state => selectRecordById(state, 'record_id'))
|
||||||
|
const generatingRecords = useAppSelector(state =>
|
||||||
|
selectRecordsByStatus(state, 'generating')
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 迁移说明
|
||||||
|
|
||||||
|
### 从 Zustand 迁移到 Redux
|
||||||
|
|
||||||
|
原来的 Zustand 用法:
|
||||||
|
```tsx
|
||||||
|
// 旧的 Zustand 用法
|
||||||
|
const { records, loadRecords, addRecord } = useHistoryStore()
|
||||||
|
```
|
||||||
|
|
||||||
|
新的 Redux 用法:
|
||||||
|
```tsx
|
||||||
|
// 新的 Redux 用法
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
const records = useAppSelector(selectHistoryRecords)
|
||||||
|
|
||||||
|
// 调用 actions
|
||||||
|
dispatch(loadRecords())
|
||||||
|
dispatch(addRecord(recordData))
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 所有异步操作都通过 Redux Thunk 处理
|
||||||
|
2. 使用类型化的 hooks (`useAppDispatch`, `useAppSelector`)
|
||||||
|
3. 选择器函数提供了类型安全的状态访问
|
||||||
|
4. 所有状态更新都是不可变的
|
||||||
|
5. 错误处理通过 Redux 状态管理
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
import { create } from 'zustand'
|
|
||||||
import Taro from '@tarojs/taro'
|
|
||||||
import { HistoryRecord } from './types'
|
|
||||||
|
|
||||||
interface HistoryStore {
|
|
||||||
records: HistoryRecord[]
|
|
||||||
loading: boolean
|
|
||||||
error: string | null
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
loadRecords: () => Promise<void>
|
|
||||||
addRecord: (record: Omit<HistoryRecord, 'id' | 'createTime' | 'updateTime'>) => Promise<string>
|
|
||||||
updateRecord: (id: string, updates: Partial<HistoryRecord>) => Promise<void>
|
|
||||||
deleteRecord: (id: string) => Promise<void>
|
|
||||||
clearRecords: () => Promise<void>
|
|
||||||
getRecordById: (id: string) => HistoryRecord | undefined
|
|
||||||
getRecordsByStatus: (status: HistoryRecord['status']) => HistoryRecord[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const STORAGE_KEY = 'historyRecords'
|
|
||||||
|
|
||||||
export const useHistoryStore = create<HistoryStore>((set, get) => ({
|
|
||||||
records: [],
|
|
||||||
loading: false,
|
|
||||||
error: null,
|
|
||||||
|
|
||||||
// 加载历史记录
|
|
||||||
loadRecords: async () => {
|
|
||||||
set({ loading: true, error: null })
|
|
||||||
try {
|
|
||||||
const savedRecords = Taro.getStorageSync(STORAGE_KEY) || []
|
|
||||||
set({ records: savedRecords, loading: false })
|
|
||||||
} catch (error) {
|
|
||||||
const errorMsg = error instanceof Error ? error.message : '加载历史记录失败'
|
|
||||||
set({ error: errorMsg, loading: false })
|
|
||||||
console.error('加载历史记录失败:', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 添加新记录
|
|
||||||
addRecord: async (recordData) => {
|
|
||||||
const id = `record_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
||||||
const now = new Date().toISOString()
|
|
||||||
|
|
||||||
const newRecord: HistoryRecord = {
|
|
||||||
...recordData,
|
|
||||||
id,
|
|
||||||
createTime: now,
|
|
||||||
updateTime: now
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentRecords = get().records
|
|
||||||
const updatedRecords = [newRecord, ...currentRecords]
|
|
||||||
|
|
||||||
await Taro.setStorage({
|
|
||||||
key: STORAGE_KEY,
|
|
||||||
data: updatedRecords
|
|
||||||
})
|
|
||||||
|
|
||||||
set({ records: updatedRecords })
|
|
||||||
return id
|
|
||||||
} catch (error) {
|
|
||||||
const errorMsg = error instanceof Error ? error.message : '添加记录失败'
|
|
||||||
set({ error: errorMsg })
|
|
||||||
console.error('添加记录失败:', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 更新记录
|
|
||||||
updateRecord: async (id, updates) => {
|
|
||||||
try {
|
|
||||||
const currentRecords = get().records
|
|
||||||
const updatedRecords = currentRecords.map(record =>
|
|
||||||
record.id === id
|
|
||||||
? { ...record, ...updates, updateTime: new Date().toISOString() }
|
|
||||||
: record
|
|
||||||
)
|
|
||||||
|
|
||||||
await Taro.setStorage({
|
|
||||||
key: STORAGE_KEY,
|
|
||||||
data: updatedRecords
|
|
||||||
})
|
|
||||||
|
|
||||||
set({ records: updatedRecords })
|
|
||||||
} catch (error) {
|
|
||||||
const errorMsg = error instanceof Error ? error.message : '更新记录失败'
|
|
||||||
set({ error: errorMsg })
|
|
||||||
console.error('更新记录失败:', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 删除记录
|
|
||||||
deleteRecord: async (id) => {
|
|
||||||
try {
|
|
||||||
const currentRecords = get().records
|
|
||||||
const updatedRecords = currentRecords.filter(record => record.id !== id)
|
|
||||||
|
|
||||||
await Taro.setStorage({
|
|
||||||
key: STORAGE_KEY,
|
|
||||||
data: updatedRecords
|
|
||||||
})
|
|
||||||
|
|
||||||
set({ records: updatedRecords })
|
|
||||||
} catch (error) {
|
|
||||||
const errorMsg = error instanceof Error ? error.message : '删除记录失败'
|
|
||||||
set({ error: errorMsg })
|
|
||||||
console.error('删除记录失败:', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 清空所有记录
|
|
||||||
clearRecords: async () => {
|
|
||||||
try {
|
|
||||||
await Taro.removeStorage({ key: STORAGE_KEY })
|
|
||||||
set({ records: [] })
|
|
||||||
} catch (error) {
|
|
||||||
const errorMsg = error instanceof Error ? error.message : '清空记录失败'
|
|
||||||
set({ error: errorMsg })
|
|
||||||
console.error('清空记录失败:', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取单个记录
|
|
||||||
getRecordById: (id) => {
|
|
||||||
return get().records.find(record => record.id === id)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 按状态筛选记录
|
|
||||||
getRecordsByStatus: (status) => {
|
|
||||||
return get().records.filter(record => record.status === status)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
@@ -1,4 +1,25 @@
|
|||||||
// 统一导出所有store
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
export * from './types'
|
import historyReducer from '../reducers/history'
|
||||||
export * from './historyStore'
|
import templateReducer from '../reducers/template'
|
||||||
export * from './templateStore'
|
|
||||||
|
export default function configStore() {
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: {
|
||||||
|
history: historyReducer,
|
||||||
|
template: templateReducer
|
||||||
|
},
|
||||||
|
middleware: (getDefaultMiddleware) =>
|
||||||
|
getDefaultMiddleware({
|
||||||
|
serializableCheck: {
|
||||||
|
ignoredActions: ['persist/PERSIST']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RootState = ReturnType<ReturnType<typeof configStore>['getState']>
|
||||||
|
export type AppDispatch = ReturnType<typeof configStore>['dispatch']
|
||||||
|
|
||||||
|
// 导出类型
|
||||||
|
export * from './types'
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { create } from 'zustand'
|
|
||||||
import { Template } from './types'
|
|
||||||
import { TEMPLATE_CONFIG } from '../config/templates'
|
|
||||||
|
|
||||||
interface TemplateStore {
|
|
||||||
templates: Template[]
|
|
||||||
selectedTemplate: Template | null
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
initTemplates: () => void
|
|
||||||
getTemplateById: (id: string) => Template | undefined
|
|
||||||
setSelectedTemplate: (template: Template | null) => void
|
|
||||||
getTemplatesByType: (type: 'image-to-image' | 'image-to-video') => Template[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useTemplateStore = create<TemplateStore>((set, get) => ({
|
|
||||||
templates: [],
|
|
||||||
selectedTemplate: null,
|
|
||||||
|
|
||||||
// 初始化模板数据
|
|
||||||
initTemplates: () => {
|
|
||||||
set({ templates: TEMPLATE_CONFIG })
|
|
||||||
},
|
|
||||||
|
|
||||||
// 根据ID获取模板
|
|
||||||
getTemplateById: (id) => {
|
|
||||||
return get().templates.find(template => template.id === id)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 设置选中的模板
|
|
||||||
setSelectedTemplate: (template) => {
|
|
||||||
set({ selectedTemplate: template })
|
|
||||||
},
|
|
||||||
|
|
||||||
// 根据类型筛选模板
|
|
||||||
getTemplatesByType: (type) => {
|
|
||||||
return get().templates.filter(template => template.type === type)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
15
tailwind.config.js
Normal file
15
tailwind.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
// 这里给出了一份 taro 通用示例,具体要根据你自己项目的目录结构进行配置
|
||||||
|
// 比如你使用 vue3 项目,你就需要把 vue 这个格式也包括进来
|
||||||
|
// 不在 content glob 表达式中包括的文件,在里面编写 tailwindcss class,是不会生成对应的 css 工具类的
|
||||||
|
content: ['./public/index.html', './src/**/*.{html,js,ts,jsx,tsx}'],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
corePlugins: {
|
||||||
|
// 小程序不需要 preflight,因为这主要是给 h5 的,如果你要同时开发多端,你应该使用 process.env.TARO_ENV 环境变量来控制它
|
||||||
|
preflight: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user