@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
# Billiard App - 台球馆小程序开发规范 |
||||
|
||||
## 项目技术栈 |
||||
|
||||
- **框架**: Taro 4.0.9 |
||||
- **UI 框架**: React 18 |
||||
- **语言**: TypeScript |
||||
- **状态管理**: MobX 6 |
||||
- **UI 组件库**: @taroify/core 0.9.0 |
||||
- **样式**: SCSS |
||||
- **构建工具**: Webpack 5 |
||||
- **包管理器**: npm |
||||
|
||||
## 项目结构 |
||||
|
||||
``` |
||||
billiard-app/ |
||||
├── src/ |
||||
│ ├── pages/ # 页面目录 |
||||
│ │ ├── index/ # 首页 |
||||
│ │ ├── store/ # 门店列表 |
||||
│ │ ├── scan/ # 扫码 |
||||
│ │ ├── message/ # 消息 |
||||
│ │ └── user/ # 个人中心 |
||||
│ ├── store/ # MobX 状态管理 |
||||
│ ├── custom-tab-bar/ # 自定义 TabBar |
||||
│ ├── constants/ # 常量配置 |
||||
│ ├── app.ts # 入口文件 |
||||
│ └── app.config.ts # 全局配置 |
||||
├── config/ # 编译配置 |
||||
├── babel.config.js # Babel 配置 |
||||
└── package.json # 项目依赖 |
||||
``` |
||||
|
||||
## 代码规范 |
||||
|
||||
### 1. MobX 使用 |
||||
|
||||
所有组件必须用 `observer` 包裹才能响应状态变化: |
||||
|
||||
```typescript |
||||
import { observer } from 'mobx-react' |
||||
import { userStore } from '../../store' |
||||
|
||||
const MyPage = observer(() => { |
||||
const { userInfo } = userStore |
||||
return <View>{userInfo?.nickname}</View> |
||||
}) |
||||
``` |
||||
|
||||
### 2. Taroify 组件 |
||||
|
||||
按需导入(已配置 babel-plugin-import): |
||||
|
||||
```typescript |
||||
import { Button, Toast } from '@taroify/core' |
||||
import { HomeOutlined } from '@taroify/icons' |
||||
``` |
||||
|
||||
### 3. TypeScript |
||||
|
||||
所有变量和函数必须有类型定义。 |
||||
|
||||
### 4. 样式规范 |
||||
|
||||
- 使用 SCSS |
||||
- designWidth 为 750 |
||||
- 使用 px,会自动转换为 rpx |
||||
- 使用 BEM 命名规范 |
||||
|
||||
## 常用命令 |
||||
|
||||
```bash |
||||
# 开发 |
||||
npm run dev:weapp # 微信小程序 |
||||
npm run dev:h5 # H5 |
||||
|
||||
# 编译 |
||||
npm run build:weapp # 编译微信小程序 |
||||
npm run build:h5 # 编译 H5 |
||||
``` |
||||
|
||||
## 注意事项 |
||||
|
||||
1. MobX 组件必须用 `observer` 包裹 |
||||
2. Taroify 样式已全局引入 |
||||
3. 修改 TabBar 需同时更新 `custom-tab-bar/` 和 `app.config.ts` |
||||
4. 状态持久化使用 `Taro.setStorageSync` |
||||
|
||||
## 相关文档 |
||||
|
||||
- [Taro 官方文档](https://taro-docs.jd.com/) |
||||
- [Taroify 文档](https://taroify.github.io/taroify.com/) |
||||
- [MobX 文档](https://mobx.js.org/) |
||||
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
# 依赖 |
||||
node_modules/ |
||||
|
||||
# 构建产物 |
||||
dist/ |
||||
|
||||
# 日志 |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
|
||||
# 编辑器 |
||||
.vscode/ |
||||
.idea/ |
||||
*.swp |
||||
*.swo |
||||
*~ |
||||
|
||||
# 系统文件 |
||||
.DS_Store |
||||
Thumbs.db |
||||
|
||||
# 环境变量 |
||||
.env |
||||
.env.local |
||||
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
// babel-preset-taro 更多选项和默认值:
|
||||
// https://docs.taro.zone/docs/next/babel-config
|
||||
module.exports = { |
||||
presets: [ |
||||
['taro', { |
||||
framework: 'react', |
||||
ts: true, |
||||
compiler: 'webpack5', |
||||
reactRuntime: 'automatic', |
||||
}] |
||||
], |
||||
plugins: [ |
||||
// Taroify 组件按需导入配置
|
||||
[ |
||||
"import", |
||||
{ |
||||
libraryName: "@taroify/core", |
||||
libraryDirectory: "", |
||||
style: false, // 样式已在 app.ts 中全局导入
|
||||
}, |
||||
"@taroify/core", |
||||
], |
||||
[ |
||||
"import", |
||||
{ |
||||
libraryName: "@taroify/icons", |
||||
libraryDirectory: "", |
||||
camel2DashComponentName: false, |
||||
style: false, |
||||
customName: (name) => name === "Icon" ? "@taroify/icons/van/VanIcon" : `@taroify/icons/${name}`, |
||||
}, |
||||
"@taroify/icons", |
||||
], |
||||
], |
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
export default { |
||||
env: { |
||||
NODE_ENV: '"development"' |
||||
}, |
||||
defineConstants: { |
||||
}, |
||||
mini: {}, |
||||
h5: {} |
||||
} |
||||
@ -0,0 +1,80 @@
@@ -0,0 +1,80 @@
|
||||
import { defineConfig, type UserConfigExport } from '@tarojs/cli' |
||||
|
||||
// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数
|
||||
export default defineConfig<'webpack5'>(async (merge, { command, mode }) => { |
||||
const baseConfig: UserConfigExport<'webpack5'> = { |
||||
projectName: 'billiard-app', |
||||
date: '2026-1-15', |
||||
designWidth: 750, |
||||
deviceRatio: { |
||||
640: 2.34 / 2, |
||||
750: 1, |
||||
375: 2, |
||||
828: 1.81 / 2 |
||||
}, |
||||
sourceRoot: 'src', |
||||
outputRoot: 'dist', |
||||
plugins: [], |
||||
defineConstants: { |
||||
}, |
||||
copy: { |
||||
patterns: [ |
||||
], |
||||
options: { |
||||
} |
||||
}, |
||||
framework: 'react', |
||||
compiler: 'webpack5', |
||||
mini: { |
||||
postcss: { |
||||
pxtransform: { |
||||
enable: true, |
||||
config: { |
||||
} |
||||
}, |
||||
cssModules: { |
||||
enable: false, |
||||
config: { |
||||
namingPattern: 'module', |
||||
generateScopedName: '[name]__[local]___[hash:base64:5]' |
||||
} |
||||
} |
||||
}, |
||||
}, |
||||
h5: { |
||||
publicPath: '/', |
||||
staticDirectory: 'static', |
||||
miniCssExtractPluginOption: { |
||||
ignoreOrder: true, |
||||
filename: 'css/[name].[hash].css', |
||||
chunkFilename: 'css/[name].[chunkhash].css' |
||||
}, |
||||
postcss: { |
||||
autoprefixer: { |
||||
enable: true, |
||||
config: {} |
||||
}, |
||||
cssModules: { |
||||
enable: false, |
||||
config: { |
||||
namingPattern: 'module', |
||||
generateScopedName: '[name]__[local]___[hash:base64:5]' |
||||
} |
||||
} |
||||
}, |
||||
}, |
||||
rn: { |
||||
appName: 'billiardApp', |
||||
postcss: { |
||||
cssModules: { |
||||
enable: false, |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (process.env.NODE_ENV === 'development') { |
||||
return merge({}, baseConfig) |
||||
} |
||||
return merge({}, baseConfig) |
||||
}) |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
export default { |
||||
env: { |
||||
NODE_ENV: '"production"' |
||||
}, |
||||
defineConstants: { |
||||
}, |
||||
mini: {}, |
||||
h5: { |
||||
/** |
||||
* WebpackChain 插件配置 |
||||
* @docs https://github.com/neutrinojs/webpack-chain
|
||||
*/ |
||||
// webpackChain (chain) {
|
||||
// /**
|
||||
// * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
|
||||
// * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
|
||||
// */
|
||||
// chain.plugin('analyzer')
|
||||
// .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
|
||||
// /**
|
||||
// * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
|
||||
// * @docs https://github.com/chrisvfritz/prerender-spa-plugin
|
||||
// */
|
||||
// const path = require('path')
|
||||
// const Prerender = require('prerender-spa-plugin')
|
||||
// const staticDir = path.join(__dirname, '..', 'dist')
|
||||
// chain
|
||||
// .plugin('prerender')
|
||||
// .use(new Prerender({
|
||||
// staticDir,
|
||||
// routes: [ '/pages/index/index' ],
|
||||
// postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
|
||||
// }))
|
||||
// }
|
||||
} |
||||
} |
||||
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
{ |
||||
"name": "billiard-app", |
||||
"version": "1.0.0", |
||||
"private": true, |
||||
"description": "台球馆自助小程序 - React + TypeScript + MobX + Taroify", |
||||
"templateInfo": { |
||||
"name": "default", |
||||
"typescript": true, |
||||
"css": "Sass", |
||||
"framework": "React" |
||||
}, |
||||
"scripts": { |
||||
"build:weapp": "taro build --type weapp", |
||||
"build:swan": "taro build --type swan", |
||||
"build:alipay": "taro build --type alipay", |
||||
"build:tt": "taro build --type tt", |
||||
"build:h5": "taro build --type h5", |
||||
"build:rn": "taro build --type rn", |
||||
"build:qq": "taro build --type qq", |
||||
"build:jd": "taro build --type jd", |
||||
"dev:weapp": "npm run build:weapp -- --watch", |
||||
"dev:swan": "npm run build:swan -- --watch", |
||||
"dev:alipay": "npm run build:alipay -- --watch", |
||||
"dev:tt": "npm run build:tt -- --watch", |
||||
"dev:h5": "npm run build:h5 -- --watch", |
||||
"dev:rn": "npm run build:rn -- --watch", |
||||
"dev:qq": "npm run build:qq -- --watch", |
||||
"dev:jd": "npm run build:jd -- --watch" |
||||
}, |
||||
"browserslist": [ |
||||
"defaults and fully supports es6-module", |
||||
"maintained node versions" |
||||
], |
||||
"author": "", |
||||
"dependencies": { |
||||
"@babel/runtime": "^7.24.4", |
||||
"@taroify/core": "^0.9.0", |
||||
"@taroify/icons": "^0.9.0", |
||||
"@tarojs/components": "4.0.9", |
||||
"@tarojs/helper": "4.0.9", |
||||
"@tarojs/plugin-framework-react": "4.0.9", |
||||
"@tarojs/plugin-platform-alipay": "4.0.9", |
||||
"@tarojs/plugin-platform-h5": "4.0.9", |
||||
"@tarojs/plugin-platform-jd": "4.0.9", |
||||
"@tarojs/plugin-platform-qq": "4.0.9", |
||||
"@tarojs/plugin-platform-swan": "4.0.9", |
||||
"@tarojs/plugin-platform-tt": "4.0.9", |
||||
"@tarojs/plugin-platform-weapp": "4.0.9", |
||||
"@tarojs/react": "4.0.9", |
||||
"@tarojs/runtime": "4.0.9", |
||||
"@tarojs/shared": "4.0.9", |
||||
"@tarojs/taro": "4.0.9", |
||||
"mobx": "^6.15.0", |
||||
"mobx-react": "^9.2.1", |
||||
"react": "^18.0.0", |
||||
"react-dom": "^18.0.0" |
||||
}, |
||||
"devDependencies": { |
||||
"@babel/core": "^7.24.4", |
||||
"@babel/plugin-proposal-class-properties": "7.14.5", |
||||
"@babel/preset-react": "^7.24.1", |
||||
"@tarojs/cli": "4.0.9", |
||||
"@tarojs/webpack5-runner": "4.0.9", |
||||
"@types/react": "^18.0.0", |
||||
"@types/webpack-env": "^1.18.0", |
||||
"babel-plugin-import": "^1.13.8", |
||||
"babel-preset-taro": "4.0.9", |
||||
"eslint": "^8.57.0", |
||||
"eslint-config-taro": "4.0.9", |
||||
"eslint-plugin-react": "^7.34.1", |
||||
"eslint-plugin-react-hooks": "^4.4.0", |
||||
"postcss": "^8.4.38", |
||||
"sass": "^1.75.0", |
||||
"sass-loader": "^13.3.0", |
||||
"stylelint": "^16.4.0", |
||||
"typescript": "^5.4.5", |
||||
"webpack": "^5.88.0" |
||||
} |
||||
} |
||||
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
{ |
||||
"miniprogramRoot": "dist/", |
||||
"projectname": "billiard-app", |
||||
"description": "台球馆自助小程序", |
||||
"appid": "touristappid", |
||||
"setting": { |
||||
"urlCheck": false, |
||||
"es6": true, |
||||
"enhance": true, |
||||
"postcss": true, |
||||
"preloadBackgroundData": false, |
||||
"minified": true, |
||||
"newFeature": false, |
||||
"coverView": true, |
||||
"nodeModules": false, |
||||
"autoAudits": false, |
||||
"showShadowRootInWxmlPanel": true, |
||||
"scopeDataCheck": false, |
||||
"uglifyFileName": false, |
||||
"checkInvalidKey": true, |
||||
"checkSiteMap": true, |
||||
"uploadWithSourceMap": true, |
||||
"compileHotReLoad": false, |
||||
"lazyloadPlaceholderEnable": false, |
||||
"useMultiFrameRuntime": true, |
||||
"useApiHook": true, |
||||
"useApiHostProcess": true, |
||||
"babelSetting": { |
||||
"ignore": [], |
||||
"disablePlugins": [], |
||||
"outputPath": "" |
||||
}, |
||||
"enableEngineNative": false, |
||||
"useIsolateContext": true, |
||||
"userConfirmedBundleSwitch": false, |
||||
"packNpmManually": false, |
||||
"packNpmRelationList": [], |
||||
"minifyWXSS": true, |
||||
"disableUseStrict": false, |
||||
"minifyWXML": true, |
||||
"showES6CompileOption": false, |
||||
"useCompilerPlugins": false |
||||
}, |
||||
"compileType": "miniprogram", |
||||
"libVersion": "2.19.4", |
||||
"condition": {} |
||||
} |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
export default defineAppConfig({ |
||||
pages: [ |
||||
'pages/index/index', |
||||
'pages/store/index', |
||||
'pages/scan/index', |
||||
'pages/message/index', |
||||
'pages/user/index', |
||||
], |
||||
window: { |
||||
backgroundTextStyle: 'light', |
||||
navigationBarBackgroundColor: '#fff', |
||||
navigationBarTitleText: '台球馆', |
||||
navigationBarTextStyle: 'black', |
||||
}, |
||||
// 自定义 TabBar
|
||||
tabBar: { |
||||
custom: true, |
||||
color: '#999', |
||||
selectedColor: '#fff', |
||||
backgroundColor: '#121212', |
||||
borderStyle: 'black', |
||||
list: [ |
||||
{ |
||||
pagePath: 'pages/index/index', |
||||
text: '首页', |
||||
}, |
||||
{ |
||||
pagePath: 'pages/store/index', |
||||
text: '门店', |
||||
}, |
||||
{ |
||||
pagePath: 'pages/scan/index', |
||||
text: '扫码', |
||||
}, |
||||
{ |
||||
pagePath: 'pages/message/index', |
||||
text: '消息', |
||||
}, |
||||
{ |
||||
pagePath: 'pages/user/index', |
||||
text: '我的', |
||||
}, |
||||
], |
||||
}, |
||||
// 权限配置
|
||||
permission: { |
||||
'scope.userLocation': { |
||||
desc: '你的位置信息将用于查找附近的台球馆', |
||||
}, |
||||
}, |
||||
}) |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/** |
||||
* 全局样式 |
||||
*/ |
||||
|
||||
/* 重置样式 */ |
||||
page { |
||||
background-color: #f5f5f5; |
||||
font-size: 28px; |
||||
line-height: 1.6; |
||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif; |
||||
} |
||||
|
||||
/* 通用样式 */ |
||||
.container { |
||||
min-height: 100vh; |
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 100px); |
||||
} |
||||
|
||||
/* 文本溢出省略 */ |
||||
.ellipsis { |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
white-space: nowrap; |
||||
} |
||||
|
||||
.ellipsis-2 { |
||||
display: -webkit-box; |
||||
-webkit-box-orient: vertical; |
||||
-webkit-line-clamp: 2; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
} |
||||
|
||||
/* 清除浮动 */ |
||||
.clearfix::after { |
||||
content: ''; |
||||
display: block; |
||||
clear: both; |
||||
} |
||||
|
||||
/* Flex 布局 */ |
||||
.flex { |
||||
display: flex; |
||||
} |
||||
|
||||
.flex-center { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.flex-between { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
/* 占位图 */ |
||||
.placeholder { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
min-height: 400px; |
||||
color: #999; |
||||
font-size: 28px; |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
import { PropsWithChildren } from 'react' |
||||
import { useLaunch } from '@tarojs/taro' |
||||
import Taro from '@tarojs/taro' |
||||
import { userStore } from './store' |
||||
import './app.scss' |
||||
// 导入 taroify 全局样式
|
||||
import '@taroify/core/index.scss' |
||||
|
||||
function App({ children }: PropsWithChildren<any>) { |
||||
useLaunch(() => { |
||||
console.log('台球馆小程序启动') |
||||
|
||||
// 初始化:从本地存储恢复用户信息
|
||||
try { |
||||
const savedUserInfo = Taro.getStorageSync('userInfo') |
||||
if (savedUserInfo) { |
||||
userStore.setUserInfo(savedUserInfo) |
||||
} |
||||
} catch (error) { |
||||
console.warn('初始化用户信息失败:', error) |
||||
} |
||||
}) |
||||
|
||||
return children |
||||
} |
||||
|
||||
export default App |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
# 图标资源说明 |
||||
|
||||
## 占位图标 |
||||
|
||||
当前使用 `home_s.png` 作为所有 TabBar 图标的占位图。 |
||||
|
||||
## 需要替换的图标 |
||||
|
||||
请准备以下图标(建议尺寸 48x48px 或 96x96px @2x): |
||||
|
||||
### 首页 |
||||
- `home.png` - 未选中状态 |
||||
- `home_s.png` - 选中状态 |
||||
|
||||
### 门店 |
||||
- `store.png` - 未选中状态 |
||||
- `store_s.png` - 选中状态 |
||||
|
||||
### 扫码(中间大按钮) |
||||
- `scan.png` - 扫码图标(建议 56x56px) |
||||
|
||||
### 消息 |
||||
- `message.png` - 未选中状态 |
||||
- `message_s.png` - 选中状态 |
||||
|
||||
### 我的 |
||||
- `user.png` - 未选中状态 |
||||
- `user_s.png` - 选中状态 |
||||
|
||||
## 图标规范 |
||||
|
||||
- 格式:PNG(支持透明背景) |
||||
- 尺寸:48x48px(普通屏)/ 96x96px(@2x 高清屏) |
||||
- 颜色: |
||||
- 未选中:#999999 或 #666666 |
||||
- 选中:#667eea(主题色) |
||||
- 风格:线性图标,简洁清晰 |
||||
|
||||
## 使用方法 |
||||
|
||||
替换图标后,在 `custom-tab-bar/index.tsx` 中导入: |
||||
|
||||
```typescript |
||||
import homeIcon from '../assets/home.png' |
||||
import homeActiveIcon from '../assets/home_s.png' |
||||
import storeIcon from '../assets/store.png' |
||||
import storeActiveIcon from '../assets/store_s.png' |
||||
// ... |
||||
``` |
||||
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
/** |
||||
* 全局配置常量 |
||||
*/ |
||||
|
||||
// TabBar 配置
|
||||
export const TAB_BAR_CONFIG = { |
||||
HOME: { |
||||
path: '/pages/index/index', |
||||
text: '首页', |
||||
}, |
||||
STORE: { |
||||
path: '/pages/store/index', |
||||
text: '门店', |
||||
}, |
||||
SCAN: { |
||||
path: '/pages/scan/index', |
||||
text: '扫码', |
||||
isCenter: true, |
||||
}, |
||||
MESSAGE: { |
||||
path: '/pages/message/index', |
||||
text: '消息', |
||||
}, |
||||
USER: { |
||||
path: '/pages/user/index', |
||||
text: '我的', |
||||
}, |
||||
} as const |
||||
|
||||
// 页面路径
|
||||
export const PAGE_PATHS = { |
||||
HOME: '/pages/index/index', |
||||
STORE: '/pages/store/index', |
||||
SCAN: '/pages/scan/index', |
||||
MESSAGE: '/pages/message/index', |
||||
USER: '/pages/user/index', |
||||
} as const |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export default { |
||||
component: true, |
||||
usingComponents: {}, |
||||
} |
||||
@ -0,0 +1,200 @@
@@ -0,0 +1,200 @@
|
||||
/** |
||||
* 自定义 TabBar 样式 - 中间凸起 |
||||
* 参考:https://developer.aliyun.com/article/1228635 |
||||
* 已适配安全区域(iPhone X 等刘海屏) |
||||
* |
||||
* 注意:Taro 中 px 会被转换为 rpx,使用 Px (大写P) 可避免转换 |
||||
*/ |
||||
.custom-tab-bar { |
||||
position: fixed; |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
width: 100%; |
||||
height: 100Px; /* 继续减小高度,给凸起留足空间 */ |
||||
z-index: 9999; |
||||
pointer-events: none; |
||||
|
||||
// 背景白色层 #0E0D0D |
||||
.tab-bar-bg { |
||||
position: absolute; |
||||
top: 50px; |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
// height: 10Px; /* TabBar 实际高度 */ |
||||
background: #0E0D0D; |
||||
box-shadow: 0 -2Px 6Px rgba(0, 0, 0, 0.05); |
||||
z-index: 1; |
||||
pointer-events: auto; |
||||
} |
||||
|
||||
// 中间凸起的圆形白色背景 |
||||
.tab-bar-center-bg { |
||||
position: absolute; |
||||
bottom: 25Px; /* 凸起高度 - 确保顶部不被截取 */ |
||||
left: 50%; |
||||
transform: translateX(-50%); |
||||
width: 120Px; |
||||
height: 120Px; |
||||
background: #ffffff; |
||||
border-radius: 50%; |
||||
// box-shadow: 0 -2Px 8Px rgba(0, 0, 0, 0.08); |
||||
z-index: 2; |
||||
pointer-events: auto; |
||||
} |
||||
|
||||
// TabBar 内容层 |
||||
.tab-bar-content { |
||||
position: absolute; |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
// top: -60px; |
||||
display: flex; |
||||
align-items: flex-end; |
||||
justify-content: space-around; |
||||
height: 100%; |
||||
padding-bottom: 5Px; |
||||
padding-bottom: calc(5Px + env(safe-area-inset-bottom)); |
||||
z-index: 3; |
||||
pointer-events: auto; |
||||
overflow: visible; /* 重要:允许子元素溢出显示 */ |
||||
} |
||||
|
||||
// Tab 项 |
||||
.tab-item { |
||||
flex: 1; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
justify-content: flex-end; |
||||
height: 70Px; /* 普通项高度 */ |
||||
position: relative; |
||||
top: 30px; |
||||
pointer-events: auto; |
||||
|
||||
// 普通 Tab 项内容 |
||||
.tab-content { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
justify-content: center; |
||||
padding-bottom: 4Px; |
||||
|
||||
.tab-icon { |
||||
width: 41px; |
||||
height: 41px; |
||||
|
||||
margin-bottom: 2Px; |
||||
} |
||||
|
||||
.tab-text { |
||||
font-size: 20px; |
||||
color: #999999; |
||||
// line-height: 28Px; |
||||
} |
||||
} |
||||
|
||||
// 选中状态 |
||||
&.active { |
||||
.tab-text { |
||||
color: #ffffff; |
||||
font-weight: 500; |
||||
} |
||||
} |
||||
|
||||
// 中间扫码按钮 - 凸起样式 |
||||
&.center { |
||||
height: 100%; /* 占满整个容器 */ |
||||
justify-content: flex-end; |
||||
|
||||
.scan-wrapper { |
||||
position: absolute; |
||||
bottom: 10Px; /* 调整位置让按钮向上凸起,同时留出底部空间 */ |
||||
left: 50%; |
||||
transform: translateX(-50%); |
||||
z-index: 10; |
||||
|
||||
.scan-button { |
||||
width: 129px; |
||||
height: 129px; |
||||
border-radius: 50%; |
||||
// background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
box-shadow: 0 4Px 12Px rgba(102, 126, 234, 0.4); |
||||
// border: 5Px solid #ffffff; |
||||
|
||||
.scan-icon { |
||||
width: 129px; |
||||
height: 129px; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// iPhone X 及以上设备特殊适配 |
||||
@supports (bottom: env(safe-area-inset-bottom)) { |
||||
.custom-tab-bar { |
||||
.tab-bar-bg { |
||||
// 使用 constant 作为 env 的降级方案(iOS 11.0-11.2) |
||||
height: calc(60Px + constant(safe-area-inset-bottom)); |
||||
height: calc(60Px + env(safe-area-inset-bottom)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 适配不同屏幕尺寸 |
||||
@media screen and (max-width: 320Px) { |
||||
.custom-tab-bar { |
||||
height: 90Px; |
||||
|
||||
.tab-bar-bg { |
||||
height: 55Px; |
||||
} |
||||
|
||||
.tab-bar-center-bg { |
||||
width: 65Px; |
||||
height: 65Px; |
||||
bottom: 22Px; |
||||
} |
||||
|
||||
.tab-item { |
||||
height: 55Px; |
||||
|
||||
&.center .scan-wrapper { |
||||
bottom: 10Px; |
||||
|
||||
.scan-button { |
||||
width: 58Px; |
||||
height: 58Px; |
||||
border-width: 4Px; |
||||
|
||||
.scan-icon { |
||||
width: 34Px; |
||||
height: 34Px; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.tab-item .tab-content { |
||||
.tab-icon { |
||||
width: 28Px; |
||||
height: 28Px; |
||||
} |
||||
|
||||
.tab-text { |
||||
font-size: 20px; |
||||
} |
||||
} |
||||
|
||||
.tab-item.active .tab-content .tab-text { |
||||
color: #ffffff; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
/** |
||||
* 自定义 TabBar - 中间凸起样式 |
||||
* 参考:https://developer.aliyun.com/article/1228635
|
||||
*/ |
||||
import { CoverView, CoverImage } from '@tarojs/components' |
||||
import Taro from '@tarojs/taro' |
||||
import { observer } from 'mobx-react' |
||||
import { TAB_BAR_CONFIG } from '../constants/config' |
||||
import './index.scss' |
||||
|
||||
// 导入图标资源
|
||||
import homeIcon from '../assets/home.png' |
||||
import homeActiveIcon from '../assets/home_s.png' |
||||
import storeIcon from '../assets/store.png' |
||||
import storeActiveIcon from '../assets/store_s.png' |
||||
import scanIcon from '../assets/scan_s.png' |
||||
import messageIcon from '../assets/ms.png' |
||||
import messageActiveIcon from '../assets/ms_s.png' |
||||
import userIcon from '../assets/my.png' |
||||
import userActiveIcon from '../assets/my_s.png' |
||||
|
||||
// 图标映射配置
|
||||
const TAB_ICONS = { |
||||
'/pages/index/index': { normal: homeIcon, active: homeActiveIcon }, |
||||
'/pages/store/index': { normal: storeIcon, active: storeActiveIcon }, |
||||
'/pages/scan/index': { normal: scanIcon, active: scanIcon }, |
||||
'/pages/message/index': { normal: messageIcon, active: messageActiveIcon }, |
||||
'/pages/user/index': { normal: userIcon, active: userActiveIcon }, |
||||
} |
||||
|
||||
const TAB_BAR_ITEMS = [ |
||||
TAB_BAR_CONFIG.HOME, |
||||
TAB_BAR_CONFIG.STORE, |
||||
TAB_BAR_CONFIG.SCAN, |
||||
TAB_BAR_CONFIG.MESSAGE, |
||||
TAB_BAR_CONFIG.USER, |
||||
] |
||||
|
||||
const CustomTabBar = observer(() => { |
||||
const pages = Taro.getCurrentPages() |
||||
const currentPage = pages[pages.length - 1] |
||||
const currentPath = '/' + currentPage.route |
||||
|
||||
const handleTabClick = (path: string) => { |
||||
if (path === currentPath) return |
||||
|
||||
Taro.switchTab({ |
||||
url: path, |
||||
fail: () => { |
||||
Taro.navigateTo({ url: path }) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
// 获取图标
|
||||
const getIcon = (path: string, isActive: boolean) => { |
||||
const icons = TAB_ICONS[path] |
||||
if (!icons) return homeIcon |
||||
return isActive ? icons.active : icons.normal |
||||
} |
||||
|
||||
return ( |
||||
<CoverView className="custom-tab-bar"> |
||||
{/* 背景白色层 */} |
||||
<CoverView className="tab-bar-bg" /> |
||||
|
||||
{/* 中间凸起的圆形背景 */} |
||||
{/* <CoverView className="tab-bar-center-bg" /> */} |
||||
|
||||
{/* TabBar 项目 */} |
||||
<CoverView className="tab-bar-content"> |
||||
{TAB_BAR_ITEMS.map((item, index) => { |
||||
const isActive = currentPath === item.path |
||||
const isCenter = 'isCenter' in item && item.isCenter |
||||
|
||||
return ( |
||||
<CoverView |
||||
key={item.path} |
||||
className={`tab-item ${isActive ? 'active' : ''} ${isCenter ? 'center' : ''}`} |
||||
onClick={() => handleTabClick(item.path)} |
||||
> |
||||
{isCenter ? ( |
||||
// 中间扫码按钮 - 凸起样式
|
||||
<CoverView className="scan-wrapper"> |
||||
<CoverView className="scan-button"> |
||||
<CoverImage |
||||
className="scan-icon" |
||||
src={scanIcon} |
||||
/> |
||||
</CoverView> |
||||
</CoverView> |
||||
) : ( |
||||
// 普通 Tab 项
|
||||
<CoverView className="tab-content"> |
||||
<CoverImage |
||||
className="tab-icon" |
||||
src={getIcon(item.path, isActive)} |
||||
/> |
||||
<CoverView className="tab-text">{item.text}</CoverView> |
||||
</CoverView> |
||||
)} |
||||
</CoverView> |
||||
) |
||||
})} |
||||
</CoverView> |
||||
</CoverView> |
||||
) |
||||
}) |
||||
|
||||
export default CustomTabBar |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export default { |
||||
navigationBarTitleText: '首页', |
||||
usingComponents: {}, |
||||
} |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
.index-page { |
||||
min-height: 100vh; |
||||
// background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
||||
.header { |
||||
padding: 80px 32px 40px; |
||||
text-align: center; |
||||
|
||||
.title { |
||||
font-size: 48px; |
||||
font-weight: bold; |
||||
color: #fff; |
||||
} |
||||
} |
||||
|
||||
.content { |
||||
padding: 32px; |
||||
|
||||
.welcome { |
||||
background: #fff; |
||||
border-radius: 16px; |
||||
padding: 40px 32px; |
||||
text-align: center; |
||||
margin-bottom: 40px; |
||||
|
||||
.welcome-text { |
||||
font-size: 32px; |
||||
color: #333; |
||||
display: block; |
||||
margin-bottom: 16px; |
||||
} |
||||
|
||||
.user-name { |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
color: #667eea; |
||||
} |
||||
} |
||||
|
||||
.actions { |
||||
margin-top: 40px; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
/** |
||||
* 首页 |
||||
*/ |
||||
import { View, Text } from '@tarojs/components' |
||||
import { useLoad } from '@tarojs/taro' |
||||
import { observer } from 'mobx-react' |
||||
import { Button } from '@taroify/core' |
||||
import { userStore } from '../../store' |
||||
import './index.scss' |
||||
|
||||
const Index = observer(() => { |
||||
useLoad(() => { |
||||
console.log('首页加载') |
||||
}) |
||||
|
||||
return ( |
||||
<View className="index-page"> |
||||
<View className="header"> |
||||
<Text className="title">台球馆自助小程序</Text> |
||||
</View> |
||||
|
||||
<View className="content"> |
||||
<View className="welcome"> |
||||
<Text className="welcome-text"> |
||||
欢迎使用 |
||||
</Text> |
||||
{userStore.userInfo && ( |
||||
<Text className="user-name">{userStore.userInfo.nickname}</Text> |
||||
)} |
||||
</View> |
||||
|
||||
<View className="actions"> |
||||
<Button color="primary" block>扫码开台</Button> |
||||
<Button color="success" block style={{ marginTop: '20px' }}> |
||||
查找附近门店 |
||||
</Button> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
}) |
||||
|
||||
export default Index |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export default { |
||||
navigationBarTitleText: '消息', |
||||
usingComponents: {}, |
||||
} |
||||
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
.message-page { |
||||
min-height: 100vh; |
||||
background-color: #f5f5f5; |
||||
|
||||
.header { |
||||
padding: 32px; |
||||
background: #fff; |
||||
|
||||
.title { |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
color: #333; |
||||
} |
||||
} |
||||
|
||||
.content { |
||||
padding: 32px; |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
/** |
||||
* 消息列表页 |
||||
*/ |
||||
import { View, Text } from '@tarojs/components' |
||||
import { useLoad } from '@tarojs/taro' |
||||
import { observer } from 'mobx-react' |
||||
import './index.scss' |
||||
|
||||
const MessageIndex = observer(() => { |
||||
useLoad(() => { |
||||
console.log('消息页面加载') |
||||
}) |
||||
|
||||
return ( |
||||
<View className="message-page"> |
||||
<View className="header"> |
||||
<Text className="title">消息</Text> |
||||
</View> |
||||
<View className="content"> |
||||
<View className="placeholder"> |
||||
<Text>消息功能开发中...</Text> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
}) |
||||
|
||||
export default MessageIndex |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export default { |
||||
navigationBarTitleText: '扫码', |
||||
usingComponents: {}, |
||||
} |
||||
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
.scan-page { |
||||
min-height: 100vh; |
||||
background-color: #f5f5f5; |
||||
|
||||
.header { |
||||
padding: 32px; |
||||
background: #fff; |
||||
|
||||
.title { |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
color: #333; |
||||
} |
||||
} |
||||
|
||||
.content { |
||||
padding: 80px 32px; |
||||
text-align: center; |
||||
|
||||
.scan-btn { |
||||
width: 400px; |
||||
height: 400px; |
||||
border-radius: 50%; |
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
color: #fff; |
||||
font-size: 48px; |
||||
font-weight: bold; |
||||
border: none; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
/** |
||||
* 扫码页 |
||||
*/ |
||||
import { View, Text, Button } from '@tarojs/components' |
||||
import Taro, { useLoad } from '@tarojs/taro' |
||||
import { observer } from 'mobx-react' |
||||
import './index.scss' |
||||
|
||||
const ScanIndex = observer(() => { |
||||
useLoad(() => { |
||||
console.log('扫码页面加载') |
||||
}) |
||||
|
||||
const handleScan = () => { |
||||
Taro.scanCode({ |
||||
success: (res) => { |
||||
Taro.showToast({ |
||||
title: '扫码成功', |
||||
icon: 'success', |
||||
}) |
||||
console.log('扫码结果:', res) |
||||
}, |
||||
fail: (err) => { |
||||
console.error('扫码失败:', err) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
return ( |
||||
<View className="scan-page"> |
||||
<View className="header"> |
||||
<Text className="title">扫码开台</Text> |
||||
</View> |
||||
<View className="content"> |
||||
<Button className="scan-btn" onClick={handleScan}> |
||||
点击扫码 |
||||
</Button> |
||||
</View> |
||||
</View> |
||||
) |
||||
}) |
||||
|
||||
export default ScanIndex |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export default { |
||||
navigationBarTitleText: '门店', |
||||
usingComponents: {}, |
||||
} |
||||
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
.store-page { |
||||
min-height: 100vh; |
||||
background-color: #f5f5f5; |
||||
|
||||
.header { |
||||
padding: 32px; |
||||
background: #fff; |
||||
|
||||
.title { |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
color: #333; |
||||
} |
||||
} |
||||
|
||||
.content { |
||||
padding: 32px; |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
/** |
||||
* 门店列表页 |
||||
*/ |
||||
import { View, Text } from '@tarojs/components' |
||||
import { useLoad } from '@tarojs/taro' |
||||
import { observer } from 'mobx-react' |
||||
import './index.scss' |
||||
|
||||
const StoreIndex = observer(() => { |
||||
useLoad(() => { |
||||
console.log('门店页面加载') |
||||
}) |
||||
|
||||
return ( |
||||
<View className="store-page"> |
||||
<View className="header"> |
||||
<Text className="title">附近门店</Text> |
||||
</View> |
||||
<View className="content"> |
||||
<View className="placeholder"> |
||||
<Text>门店列表功能开发中...</Text> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
}) |
||||
|
||||
export default StoreIndex |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export default { |
||||
navigationBarTitleText: '我的', |
||||
usingComponents: {}, |
||||
} |
||||
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
.user-page { |
||||
min-height: 100vh; |
||||
background-color: #f5f5f5; |
||||
|
||||
.header { |
||||
padding: 60px 32px; |
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
text-align: center; |
||||
|
||||
.avatar { |
||||
width: 120px; |
||||
height: 120px; |
||||
border-radius: 50%; |
||||
background: rgba(255, 255, 255, 0.3); |
||||
margin: 0 auto 20px; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
.avatar-text { |
||||
font-size: 48px; |
||||
color: #fff; |
||||
font-weight: bold; |
||||
} |
||||
} |
||||
|
||||
.nickname { |
||||
font-size: 32px; |
||||
color: #fff; |
||||
font-weight: 500; |
||||
} |
||||
} |
||||
|
||||
.content { |
||||
padding: 32px; |
||||
|
||||
.info-card { |
||||
background: #fff; |
||||
border-radius: 16px; |
||||
padding: 32px; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
|
||||
.label { |
||||
font-size: 28px; |
||||
color: #666; |
||||
} |
||||
|
||||
.value { |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
color: #667eea; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/** |
||||
* 个人中心页 |
||||
*/ |
||||
import { View, Text } from '@tarojs/components' |
||||
import { useLoad } from '@tarojs/taro' |
||||
import { observer } from 'mobx-react' |
||||
import { userStore } from '../../store' |
||||
import './index.scss' |
||||
|
||||
const UserIndex = observer(() => { |
||||
useLoad(() => { |
||||
console.log('个人中心页面加载') |
||||
}) |
||||
|
||||
return ( |
||||
<View className="user-page"> |
||||
<View className="header"> |
||||
<View className="avatar"> |
||||
<Text className="avatar-text"> |
||||
{userStore.userInfo?.nickname?.[0] || '未'} |
||||
</Text> |
||||
</View> |
||||
<Text className="nickname"> |
||||
{userStore.userInfo?.nickname || '未登录'} |
||||
</Text> |
||||
</View> |
||||
|
||||
<View className="content"> |
||||
<View className="info-card"> |
||||
<Text className="label">账户余额</Text> |
||||
<Text className="value">¥ {userStore.balance.toFixed(2)}</Text> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
}) |
||||
|
||||
export default UserIndex |
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
/** |
||||
* Store 统一导出 - MobX |
||||
*/ |
||||
import { userStore as _userStore } from './user' |
||||
|
||||
// 重新导出
|
||||
export const userStore = _userStore |
||||
|
||||
// 导出所有 store 的集合
|
||||
export const stores = { |
||||
userStore: _userStore, |
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/** |
||||
* 用户状态管理 - MobX |
||||
*/ |
||||
import { makeAutoObservable } from 'mobx' |
||||
import Taro from '@tarojs/taro' |
||||
|
||||
export interface UserInfo { |
||||
id: string |
||||
nickname: string |
||||
avatar?: string |
||||
phone?: string |
||||
} |
||||
|
||||
class UserStore { |
||||
userInfo: UserInfo | null = null |
||||
isLoggedIn: boolean = false |
||||
balance: number = 0 |
||||
|
||||
constructor() { |
||||
makeAutoObservable(this) |
||||
} |
||||
|
||||
setUserInfo = (userInfo: UserInfo | null) => { |
||||
this.userInfo = userInfo |
||||
this.isLoggedIn = !!userInfo |
||||
|
||||
if (userInfo) { |
||||
Taro.setStorageSync('userInfo', userInfo) |
||||
} else { |
||||
Taro.removeStorageSync('userInfo') |
||||
} |
||||
} |
||||
|
||||
setBalance = (balance: number) => { |
||||
this.balance = balance |
||||
} |
||||
|
||||
logout = () => { |
||||
this.setUserInfo(null) |
||||
Taro.removeStorageSync('token') |
||||
Taro.removeStorageSync('userInfo') |
||||
} |
||||
} |
||||
|
||||
export const userStore = new UserStore() |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"target": "ES2017", |
||||
"module": "ESNext", |
||||
"lib": ["ESNext", "DOM"], |
||||
"jsx": "react-jsx", |
||||
"useDefineForClassFields": true, |
||||
"skipLibCheck": true, |
||||
"esModuleInterop": true, |
||||
"allowSyntheticDefaultImports": true, |
||||
"strict": true, |
||||
"forceConsistentCasingInFileNames": true, |
||||
"moduleResolution": "node", |
||||
"resolveJsonModule": true, |
||||
"isolatedModules": true, |
||||
"noEmit": true, |
||||
"typeRoots": [ |
||||
"node_modules/@types", |
||||
"types" |
||||
], |
||||
"baseUrl": ".", |
||||
"paths": { |
||||
"@/*": ["src/*"] |
||||
} |
||||
}, |
||||
"include": ["src", "types"], |
||||
"exclude": ["node_modules", "dist"] |
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/// <reference types="@tarojs/taro" />
|
||||
|
||||
declare module '*.png' |
||||
declare module '*.gif' |
||||
declare module '*.jpg' |
||||
declare module '*.jpeg' |
||||
declare module '*.svg' |
||||
declare module '*.css' |
||||
declare module '*.less' |
||||
declare module '*.scss' |
||||
declare module '*.sass' |
||||
declare module '*.styl' |
||||
|
||||
declare namespace NodeJS { |
||||
interface ProcessEnv { |
||||
TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd' |
||||
} |
||||
} |
||||
|
||||
declare function defineAppConfig(config: import('@tarojs/taro').Config): import('@tarojs/taro').Config |
||||