@ -0,0 +1,12 @@ |
|||
# http://editorconfig.org |
|||
root = true |
|||
|
|||
[*] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
charset = utf-8 |
|||
trim_trailing_whitespace = true |
|||
insert_final_newline = true |
|||
|
|||
[*.md] |
|||
trim_trailing_whitespace = false |
@ -0,0 +1,2 @@ |
|||
# 配置文档参考 https://taro-docs.jd.com/docs/next/env-mode-config |
|||
# TARO_APP_ID="开发环境下的小程序 AppID" |
@ -0,0 +1 @@ |
|||
# TARO_APP_ID="生产环境下的小程序 AppID" |
@ -0,0 +1 @@ |
|||
# TARO_APP_ID="测试环境下的小程序 AppID" |
@ -0,0 +1,7 @@ |
|||
{ |
|||
"extends": ["taro/react"], |
|||
"rules": { |
|||
"react/jsx-uses-react": "off", |
|||
"react/react-in-jsx-scope": "off" |
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
dist/ |
|||
deploy_versions/ |
|||
.temp/ |
|||
.rn_temp/ |
|||
node_modules/ |
|||
.DS_Store |
|||
.swc |
|||
*.local |
@ -0,0 +1,78 @@ |
|||
# jf_202506 |
|||
|
|||
#### 介绍 |
|||
202506小程序交付 |
|||
|
|||
#### 软件架构 |
|||
taro react |
|||
|
|||
### 依赖工具 |
|||
+ **开发框架**:[tarojs@4.1.2](https://docs.taro.zone/docs/) |
|||
+ **基础架构**:[react@18.0](https://reactjs.org/) |
|||
+ **ui框架**:[antd-mobile@5.39](https://mobile.ant.design/zh) |
|||
|
|||
### 项目启动 |
|||
1. node > ver.18 |
|||
2. 安装yarn@1.22.19,执行`yarn install` |
|||
3. 执行`npm run dev:h5`,本地启动 |
|||
4. 执行`npm run build:h5`,打包编译 |
|||
|
|||
### 项目部署 |
|||
|
|||
1. 本地构建 |
|||
|
|||
```bash |
|||
// 1. git 操作拉取项目,此处省略... |
|||
// 2. 进入到项目根目录 |
|||
yarn install |
|||
// 3. 编译构建 |
|||
npm run build:h5 |
|||
``` |
|||
|
|||
2. 登录宝塔平台 |
|||
|
|||
3. 网站/HTML项目 下 添加HTML项目 |
|||
|
|||
 |
|||
|
|||
4. 修改nginx配置文件 |
|||
|
|||
 |
|||
|
|||
```nginx |
|||
location ^~/api { |
|||
proxy_set_header X-Real-IP $remote_addr; |
|||
proxy_set_header REMOTE-HOST $remote_addr; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
|
|||
proxy_http_version 1.1; |
|||
proxy_set_header Connection ''; |
|||
|
|||
proxy_buffering off; |
|||
proxy_cache off; |
|||
|
|||
proxy_read_timeout 86400s; |
|||
send_timeout 86400s; |
|||
|
|||
client_max_body_size 50m; |
|||
|
|||
# 这里改成 [对应的域名 + /api] |
|||
proxy_pass https://he.lehuonianhua.com/api; |
|||
} |
|||
``` |
|||
|
|||
|
|||
|
|||
5. 上传生产包 |
|||
|
|||
 |
|||
|
|||
6. 重启nginx |
|||
|
|||
 |
|||
|
|||
|
|||
|
|||
|
|||
## 讨论 |
|||
联系人:jinech 13478707150@163.com |
@ -0,0 +1,12 @@ |
|||
// babel-preset-taro 更多选项和默认值:
|
|||
// https://docs.taro.zone/docs/next/babel-config
|
|||
module.exports = { |
|||
presets: [ |
|||
['taro', { |
|||
framework: 'react', |
|||
ts: false, |
|||
compiler: 'vite', |
|||
useBuiltIns: process.env.TARO_ENV === 'h5' ? 'usage' : false |
|||
}] |
|||
] |
|||
} |
@ -0,0 +1,16 @@ |
|||
|
|||
|
|||
export default { |
|||
|
|||
mini: {}, |
|||
h5: { |
|||
devServer: { |
|||
proxy: { |
|||
'/api/Open': { |
|||
target: 'https://he.lehuonianhua.com', |
|||
changeOrigin: true, |
|||
}, |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,89 @@ |
|||
import { defineConfig } from '@tarojs/cli' |
|||
|
|||
import devConfig from './dev' |
|||
import prodConfig from './prod' |
|||
|
|||
// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数
|
|||
export default defineConfig(async (merge, { command, mode }) => { |
|||
const baseConfig = { |
|||
projectName: 'jf_20250608', |
|||
date: '2025-6-8', |
|||
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: 'vite', |
|||
mini: { |
|||
postcss: { |
|||
pxtransform: { |
|||
enable: true, |
|||
config: { |
|||
|
|||
} |
|||
}, |
|||
cssModules: { |
|||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
|||
config: { |
|||
namingPattern: 'module', // 转换模式,取值为 global/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: true, // 默认为 false,如需使用 css modules 功能,则设为 true
|
|||
config: { |
|||
namingPattern: 'module', // 转换模式,取值为 global/module
|
|||
generateScopedName: '[name]__[local]___[hash:base64:5]' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
rn: { |
|||
appName: 'taroDemo', |
|||
postcss: { |
|||
cssModules: { |
|||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
process.env.BROWSERSLIST_ENV = process.env.NODE_ENV |
|||
|
|||
if (process.env.NODE_ENV === 'development') { |
|||
// 本地开发构建配置(不混淆压缩)
|
|||
return merge({}, baseConfig, devConfig) |
|||
} |
|||
// 生产构建配置(默认开启压缩混淆等)
|
|||
return merge({}, baseConfig, prodConfig) |
|||
}) |
@ -0,0 +1,37 @@ |
|||
|
|||
|
|||
export default { |
|||
mini: {}, |
|||
h5: { |
|||
// publicPath: '/ryc/',
|
|||
// staticDirectory: './ryc/static'
|
|||
// 确保产物为 es5
|
|||
// legacy: true,
|
|||
/** |
|||
* 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') })
|
|||
// }))
|
|||
// }
|
|||
} |
|||
} |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 74 KiB |
@ -0,0 +1,93 @@ |
|||
{ |
|||
"name": "jf_20250608", |
|||
"version": "1.0.0", |
|||
"private": true, |
|||
"description": "", |
|||
"templateInfo": { |
|||
"name": "default", |
|||
"typescript": false, |
|||
"css": "Less", |
|||
"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", |
|||
"build:harmony-hybrid": "taro build --type harmony-hybrid", |
|||
"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", |
|||
"dev:harmony-hybrid": "npm run build:harmony-hybrid -- --watch" |
|||
}, |
|||
"browserslist": { |
|||
"development": [ |
|||
"defaults and fully supports es6-module", |
|||
"maintained node versions" |
|||
], |
|||
"production": [ |
|||
"last 3 versions", |
|||
"Android >= 4.1", |
|||
"ios >= 8" |
|||
] |
|||
}, |
|||
"author": "", |
|||
"dependencies": { |
|||
"@babel/runtime": "^7.24.4", |
|||
"@tarojs/components": "4.1.2", |
|||
"@tarojs/helper": "4.1.2", |
|||
"@tarojs/plugin-framework-react": "4.1.2", |
|||
"@tarojs/plugin-platform-alipay": "4.1.2", |
|||
"@tarojs/plugin-platform-h5": "4.1.2", |
|||
"@tarojs/plugin-platform-harmony-hybrid": "4.1.2", |
|||
"@tarojs/plugin-platform-jd": "4.1.2", |
|||
"@tarojs/plugin-platform-qq": "4.1.2", |
|||
"@tarojs/plugin-platform-swan": "4.1.2", |
|||
"@tarojs/plugin-platform-tt": "4.1.2", |
|||
"@tarojs/plugin-platform-weapp": "4.1.2", |
|||
"@tarojs/react": "4.1.2", |
|||
"@tarojs/runtime": "4.1.2", |
|||
"@tarojs/shared": "4.1.2", |
|||
"@tarojs/taro": "4.1.2", |
|||
"ahooks": "^3.8.5", |
|||
"antd-mobile": "^5.39.0", |
|||
"axios": "^1.10.0", |
|||
"classnames": "^2.5.1", |
|||
"lodash": "^4.17.21", |
|||
"md5": "^2.3.0", |
|||
"qrcode.react": "^4.2.0", |
|||
"react": "^18.0.0", |
|||
"react-copy-to-clipboard": "^5.1.0", |
|||
"react-dom": "^18.0.0" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/core": "^7.24.4", |
|||
"@babel/helper-compilation-targets": "^7.27.2", |
|||
"@babel/plugin-transform-class-properties": "7.25.9", |
|||
"@babel/preset-react": "^7.24.1", |
|||
"@tarojs/cli": "4.1.2", |
|||
"@tarojs/vite-runner": "4.1.2", |
|||
"@types/react": "^18.0.0", |
|||
"@vitejs/plugin-react": "^4.3.0", |
|||
"babel-preset-taro": "4.1.2", |
|||
"eslint": "^8.57.0", |
|||
"eslint-config-taro": "4.1.2", |
|||
"eslint-plugin-react": "^7.34.1", |
|||
"eslint-plugin-react-hooks": "^4.4.0", |
|||
"less": "^4.2.0", |
|||
"postcss": "^8.4.38", |
|||
"react-refresh": "^0.14.0", |
|||
"stylelint": "^16.4.0", |
|||
"terser": "^5.30.4", |
|||
"vite": "^4.2.0" |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
{ |
|||
"miniprogramRoot": "./dist", |
|||
"projectname": "jf_20250608", |
|||
"description": "", |
|||
"appid": "touristappid", |
|||
"setting": { |
|||
"urlCheck": true, |
|||
"es6": false, |
|||
"enhance": false, |
|||
"compileHotReLoad": false, |
|||
"postcss": false, |
|||
"minified": false |
|||
}, |
|||
"compileType": "miniprogram" |
|||
} |
@ -0,0 +1,56 @@ |
|||
export default defineAppConfig({ |
|||
pages: [ |
|||
'pages/index/index', |
|||
'pages/dataset/index', |
|||
'pages/mine/index', |
|||
'pages/login/index', |
|||
'pages/forget/index', |
|||
'pages/payforget/index', |
|||
'pages/register/index', |
|||
'pages/shopinfo/index', |
|||
'pages/team/index', |
|||
'pages/invite/index', |
|||
'pages/paymentmethod/index', |
|||
'pages/funds/index', |
|||
'pages/safe/index', |
|||
'pages/feedback/index', |
|||
'pages/myfeedback/index', |
|||
'pages/notice/index', |
|||
'pages/notice-detail/index', |
|||
'pages/viplevelup/index', |
|||
'pages/sitemessage/index', |
|||
'pages/payconfirm/index', |
|||
'pages/feeconfirm/index', |
|||
], |
|||
window: { |
|||
backgroundTextStyle: 'light', |
|||
navigationBarBackgroundColor: '#fff', |
|||
navigationBarTitleText: 'WeChat', |
|||
navigationBarTextStyle: 'black' |
|||
}, |
|||
tabBar: { |
|||
color: '#7A7E83', |
|||
selectedColor: '#007AFF', |
|||
backgroundColor: '#FAFAFA', |
|||
list: [ |
|||
{ |
|||
pagePath: 'pages/index/index', |
|||
text: '首页', |
|||
iconPath: 'assets/icons/home.png', |
|||
selectedIconPath: 'assets/icons/home-active.png' |
|||
}, |
|||
{ |
|||
pagePath: 'pages/dataset/index', |
|||
text: '数据优化', |
|||
iconPath: 'assets/icons/dataset.png', |
|||
selectedIconPath: 'assets/icons/dataset-active.png' |
|||
}, |
|||
{ |
|||
pagePath: 'pages/mine/index', |
|||
text: '我的', |
|||
iconPath: 'assets/icons/mine.png', |
|||
selectedIconPath: 'assets/icons/mine-active.png' |
|||
} |
|||
] |
|||
} |
|||
}) |
@ -0,0 +1,15 @@ |
|||
|
|||
import { useLaunch } from '@tarojs/taro'; |
|||
|
|||
import './app.less'; |
|||
|
|||
function App({ children }) { |
|||
useLaunch(() => { |
|||
console.log('App launched.') |
|||
}); |
|||
|
|||
// children 是将要会渲染的页面
|
|||
return children; |
|||
} |
|||
|
|||
export default App; |
@ -0,0 +1,3 @@ |
|||
:root { |
|||
--color-primary: #007AFF; |
|||
} |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 172 KiB |
@ -0,0 +1,71 @@ |
|||
|
|||
import cls from 'classnames'; |
|||
import styles from './index.module.less'; |
|||
import { Image, ImageViewer } from 'antd-mobile'; |
|||
import { CameraOutline } from 'antd-mobile-icons'; |
|||
import { View } from '@tarojs/components'; |
|||
import { uploadImage64Action } from '../../request/actions'; |
|||
import Taro from '@tarojs/taro'; |
|||
import React, { useState } from 'react'; |
|||
|
|||
const FormImage = ({ value, className, onChange, readonly, ...props }) => { |
|||
const prefix = cls(styles.img, className); |
|||
const [visible, updateVisible] = useState(false); |
|||
|
|||
const handleClick = () => { |
|||
// TODO |
|||
if (readonly || value) { |
|||
updateVisible(true); |
|||
return; |
|||
} |
|||
const input = document.createElement('input'); |
|||
input.type = 'file'; |
|||
input.accept = 'image/*'; |
|||
input.onchange = (event) => { |
|||
const file = event.target.files[0]; |
|||
if (file) { |
|||
const reader = new FileReader(); |
|||
reader.onload = async () => { |
|||
const imageBase64 = reader.result; |
|||
const res = await uploadImage64Action({ imageBase64 }); |
|||
onChange(res?.logo); |
|||
}; |
|||
reader.readAsDataURL(file); // 将文件读取为 Base64 |
|||
} |
|||
}; |
|||
input.click(); |
|||
}; |
|||
|
|||
|
|||
if (!value) { |
|||
if (readonly) { |
|||
return ( |
|||
<Image src={value} className={prefix} fit='fill' {...props} /> |
|||
); |
|||
} |
|||
return ( |
|||
<View className={styles.empty} onClick={handleClick} {...props}> |
|||
<CameraOutline /> |
|||
</View> |
|||
); |
|||
} |
|||
|
|||
return ( |
|||
<React.Fragment> |
|||
<Image src={value} className={prefix} fit='fill' onClick={handleClick} {...props} /> |
|||
<ImageViewer |
|||
classNames={{ |
|||
mask: 'customize-mask', |
|||
body: 'customize-body', |
|||
}} |
|||
image={value} |
|||
visible={visible} |
|||
onClose={() => { |
|||
updateVisible(false) |
|||
}} |
|||
/> |
|||
</React.Fragment> |
|||
); |
|||
}; |
|||
|
|||
export default FormImage; |
@ -0,0 +1,15 @@ |
|||
|
|||
.img { |
|||
width: 72px; |
|||
height: 72px; |
|||
} |
|||
|
|||
.empty { |
|||
width: 72px; |
|||
height: 72px; |
|||
border-radius: 6px; |
|||
background-color: #f4f5f7; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
@ -0,0 +1,7 @@ |
|||
|
|||
const FormLabel = ({ value, render, ...props }) => { |
|||
|
|||
return <span {...props} >{render ? render(value) : value}</span>; |
|||
}; |
|||
|
|||
export default FormLabel; |
@ -0,0 +1,96 @@ |
|||
import { useEffect, useRef, useState } from 'react'; |
|||
|
|||
import { AxiosRequestConfig } from 'axios'; |
|||
import { Toast } from 'antd-mobile'; |
|||
|
|||
type Request<T, U> = (params: T, options?: any) => Promise<U>; |
|||
|
|||
interface UseRequestListProps<P, R> { |
|||
action: Request<P, R>; |
|||
immediate?: boolean; |
|||
onOk?: () => void | boolean; |
|||
initFilterParams?: Partial<P>; |
|||
showMsg?: boolean; |
|||
options?: AxiosRequestConfig; |
|||
loop?: boolean; |
|||
interval?: number; |
|||
onLoop?: (data: any) => boolean; |
|||
}; |
|||
|
|||
|
|||
function useRequestList<P, R>({ |
|||
action, |
|||
immediate = true, |
|||
onOk, |
|||
initFilterParams, |
|||
showMsg = false, |
|||
options = {}, |
|||
loop = false, |
|||
interval = 2000, |
|||
onLoop |
|||
}: UseRequestListProps<P, R>) { |
|||
const [loading, updateLoading] = useState(false); |
|||
const filterParamsRef = useRef(initFilterParams); |
|||
const [data, updateData] = useState<R>(); |
|||
|
|||
const timer = useRef<any>(null); |
|||
|
|||
const abortLoop = () => { |
|||
if (timer.current) { |
|||
clearInterval(timer.current); |
|||
timer.current = null; |
|||
} |
|||
}; |
|||
|
|||
const getDataTask = async (params: P, loading = true) => { |
|||
try { |
|||
loading && updateLoading(true); |
|||
const filterParams = filterParamsRef.current; |
|||
const queryParams = { ...filterParams, ...params }; |
|||
const res = await action(queryParams, options); |
|||
filterParamsRef.current = queryParams; |
|||
updateData(res); |
|||
if (loop && onLoop?.(res)) { |
|||
timer.current = setTimeout(() => { |
|||
getDataTask(queryParams, loading); |
|||
}, interval); |
|||
} |
|||
else { |
|||
loading && updateLoading(false); |
|||
onOk?.(); |
|||
return res; |
|||
} |
|||
} |
|||
catch (err) { |
|||
abortLoop(); |
|||
showMsg && Toast.show({ |
|||
icon: 'error', |
|||
content: `${err}` |
|||
}); |
|||
} |
|||
finally { |
|||
loading && !loop && updateLoading(false); |
|||
} |
|||
}; |
|||
|
|||
useEffect(() => { |
|||
return abortLoop; |
|||
}, []); |
|||
|
|||
useEffect(() => { |
|||
if (!immediate) { |
|||
return; |
|||
} |
|||
getDataTask({} as P); |
|||
}, []); |
|||
|
|||
return { |
|||
getDataTask, |
|||
loading, |
|||
data, |
|||
filterParams: filterParamsRef.current, |
|||
abortLoop |
|||
}; |
|||
} |
|||
|
|||
export default useRequestList; |
@ -0,0 +1,50 @@ |
|||
/* |
|||
* @Author: WIN-J7OL7MK489U\EDY 13478707150@163.com |
|||
* @Date: 2023-06-28 16:45:43 |
|||
* @LastEditors: BJ-HPC3\13478 13478707150@163.com |
|||
* @LastEditTime: 2025-02-21 17:15:40 |
|||
* @FilePath: \jsure_zdbg_web_react\src\hooks\useRequest\index.ts |
|||
* @Description: 通用请求 |
|||
*/ |
|||
|
|||
import { useState } from 'react'; |
|||
|
|||
import type { Request } from '@/request/utils'; |
|||
|
|||
interface UseRequestListProps<P, R> { |
|||
action?: Request<P, R>; |
|||
onOk?: () => void; |
|||
}; |
|||
|
|||
function useRequestSubmit<P, R>({ |
|||
action, |
|||
onOk, |
|||
}: UseRequestListProps<P, R>) { |
|||
const [loading, updateLoading] = useState(false); |
|||
const [data, updateData] = useState<R>(null); |
|||
|
|||
const actionTask = async (params: P, loading = true) => { |
|||
try { |
|||
loading && updateLoading(true); |
|||
const res = await action(params); |
|||
updateData(res); |
|||
onOk?.(); |
|||
return res; |
|||
} |
|||
catch (err) { |
|||
console.log('[request submit err]: ', err); |
|||
return Promise.reject(err); |
|||
} |
|||
finally { |
|||
loading && updateLoading(false); |
|||
} |
|||
}; |
|||
|
|||
return { |
|||
actionTask, |
|||
loading, |
|||
data |
|||
}; |
|||
} |
|||
|
|||
export default useRequestSubmit; |
@ -0,0 +1,17 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"> |
|||
<meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport"> |
|||
<meta name="apple-mobile-web-app-capable" content="yes"> |
|||
<meta name="apple-touch-fullscreen" content="yes"> |
|||
<meta name="format-detection" content="telephone=no,address=no"> |
|||
<meta name="apple-mobile-web-app-status-bar-style" content="white"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" > |
|||
<title>jf_20250608</title> |
|||
<script><%= htmlWebpackPlugin.options.script %></script> |
|||
</head> |
|||
<body> |
|||
<div id="app"></div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,111 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { PullToRefresh, List, Empty, Button, Modal, Toast } from 'antd-mobile'; |
|||
import { View, Text, ScrollView } from '@tarojs/components'; |
|||
import { getOrderListLoopAction, orderConfirmAction } from '../../../../request/actions'; |
|||
import { OrderStatusEnum } from '../../constants'; |
|||
import Taro, { useDidShow } from '@tarojs/taro'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
const fetchDataRecursively = async (times, params, datasource) => { |
|||
if (times <= 0 || (datasource && datasource?.length < 10)) { |
|||
return []; |
|||
} |
|||
const lastOrderId = datasource?.[datasource?.length - 1]?.orderId; |
|||
const result = await getOrderListLoopAction({ ...params, lastOrderId }); |
|||
const nextResults = await fetchDataRecursively(times - 1, params, result?.dataList); |
|||
return [...result?.dataList, ...nextResults]; |
|||
}; |
|||
|
|||
const DataList = ({ orderStatus }) => { |
|||
const [data, setData] = useState([]); |
|||
|
|||
const initData = async () => { |
|||
const dataSource = await fetchDataRecursively(1, { orderStatus }); |
|||
setData(dataSource); |
|||
}; |
|||
|
|||
|
|||
const handleScrollToLower = async () => { |
|||
const lastLogId = data?.[data?.length - 1]?.orderId; |
|||
const result = await getOrderListLoopAction({ lastLogId }); |
|||
const newDataSource = result?.dataList; |
|||
setData([...data, ...newDataSource]); |
|||
}; |
|||
|
|||
const handleJump = item => { |
|||
// 去支付 |
|||
if (orderStatus === OrderStatusEnum.waittingForPay.value) { |
|||
Taro.navigateTo({ |
|||
url: `/pages/payconfirm/index?orderId=${item.orderId}` |
|||
}); |
|||
} |
|||
// 确认订单 |
|||
if (orderStatus === OrderStatusEnum.waittingForConfirm.value) { |
|||
// Modal.confirm({ |
|||
// title: '是否确认收款?', |
|||
// onConfirm: async () => { |
|||
// try { |
|||
// const orderId = item.orderId; |
|||
// await orderConfirmAction({ orderId }); |
|||
// initData(); |
|||
// Toast.show({ |
|||
// icon: 'success', |
|||
// content: '已成功确认收款信息' |
|||
// }); |
|||
// } |
|||
// catch (err) { |
|||
// console.log('err: ', err); |
|||
// } |
|||
// } |
|||
// }) |
|||
Taro.navigateTo({ |
|||
url: `/pages/feeconfirm/index?orderId=${item.orderId}` |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
useDidShow(initData); |
|||
|
|||
useEffect(() => { |
|||
initData(); |
|||
}, [orderStatus]); |
|||
|
|||
return ( |
|||
<PullToRefresh |
|||
onRefresh={initData} |
|||
> |
|||
{data.length > 0 ? ( |
|||
<ScrollView |
|||
scrollY |
|||
onScrollToLower={handleScrollToLower} |
|||
lowerThreshold={50} |
|||
> |
|||
<List className={styles.list}> |
|||
{data.map((item, index) => ( |
|||
<List.Item key={index} onClick={() => handleJump(item)} arrowIcon={false}> |
|||
<View className={styles.row}> |
|||
<Text>订单编号: {item.orderId}</Text> |
|||
<Text>订单日期: {item.orderDate || '-'}</Text> |
|||
</View> |
|||
<View className={styles.row}> |
|||
<Text>订单金额: {item.orderPrice}</Text> |
|||
</View> |
|||
{orderStatus !== OrderStatusEnum.done.value && ( |
|||
<Button className={styles.btn}>{item.buttonText}</Button> |
|||
)} |
|||
</List.Item> |
|||
))} |
|||
</List> |
|||
</ScrollView> |
|||
) : ( |
|||
<Empty |
|||
style={{ padding: '64px 0' }} |
|||
description='暂无数据' |
|||
/> |
|||
)} |
|||
</PullToRefresh > |
|||
) |
|||
}; |
|||
|
|||
export default DataList; |
@ -0,0 +1,12 @@ |
|||
.row { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
font-size: 28px; |
|||
line-height: 2; |
|||
} |
|||
.btn { |
|||
width: 100%; |
|||
} |
|||
.list { |
|||
height: calc(100vh - 92px - 120px); |
|||
} |
@ -0,0 +1,15 @@ |
|||
|
|||
export const OrderStatusEnum = { |
|||
waittingForPay: { |
|||
label: '待付款', |
|||
value: '0' |
|||
}, |
|||
waittingForConfirm: { |
|||
label: '待确认', |
|||
value: '1' |
|||
}, |
|||
done: { |
|||
label: '已完成', |
|||
value: '9' |
|||
} |
|||
}; |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '数据优化', |
|||
usingComponents: {}, |
|||
}) |
@ -0,0 +1,25 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Tabs } from 'antd-mobile'; |
|||
|
|||
import DataList from './components/DataList'; |
|||
import { OrderStatusEnum } from './constants'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Dataset() { |
|||
return ( |
|||
<View className={styles.container}> |
|||
<Tabs className={styles.tabs} defaultActiveKey={OrderStatusEnum.waittingForPay.value}> |
|||
<Tabs.Tab title='待支付' key={OrderStatusEnum.waittingForPay.value}> |
|||
<DataList orderStatus={OrderStatusEnum.waittingForPay.value} /> |
|||
</Tabs.Tab> |
|||
<Tabs.Tab title='待确认' key={OrderStatusEnum.waittingForConfirm.value}> |
|||
<DataList orderStatus={OrderStatusEnum.waittingForConfirm.value} /> |
|||
</Tabs.Tab> |
|||
<Tabs.Tab title='已完成' key={OrderStatusEnum.done.value}> |
|||
<DataList orderStatus={OrderStatusEnum.done.value} /> |
|||
</Tabs.Tab> |
|||
</Tabs> |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,16 @@ |
|||
.container { |
|||
position: relative; |
|||
|
|||
.tabs { |
|||
height: 82px; |
|||
position: sticky; |
|||
top: 0; |
|||
z-index: 9; |
|||
background-color: #fff; |
|||
} |
|||
:global { |
|||
.adm-list-body { |
|||
border-top: unset; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Form } from 'antd-mobile'; |
|||
import FormLabel from '/components/FormLabel'; |
|||
import FormImage from '/components/FormImage'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Alipay({ namePrefix }) { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form.Item |
|||
name={`${namePrefix}-alipay`} |
|||
label='支付宝账号' |
|||
childElementPosition='right' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name={`${namePrefix}-alipayName`} |
|||
label='支付宝姓名' |
|||
childElementPosition='right' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name={`${namePrefix}-alipayImage`} |
|||
label='收款码' |
|||
childElementPosition='right' |
|||
> |
|||
<FormImage readonly width={72} height={72} /> |
|||
</Form.Item> */} |
|||
{/* <Form.Item |
|||
name='password' |
|||
label='支付密码' |
|||
rules={[{ required: true, message: '请输入支付密码' }]} |
|||
> |
|||
<Input type='password' placeholder='请输入支付密码' /> |
|||
</Form.Item> */} |
|||
</View > |
|||
) |
|||
} |
@ -0,0 +1,3 @@ |
|||
.container { |
|||
|
|||
} |
@ -0,0 +1,41 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Form } from 'antd-mobile'; |
|||
import FormLabel from '/components/FormLabel'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function BankPay({ namePrefix }) { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form.Item |
|||
name={`${namePrefix}-payeeBank`} |
|||
label='开户行' |
|||
childElementPosition='right' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name={`${namePrefix}-payeeAccount`} |
|||
label='银行卡号' |
|||
childElementPosition='right' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name={`${namePrefix}-payeeName`} |
|||
label='开户人' |
|||
childElementPosition='right' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name='password' |
|||
label='支付密码' |
|||
rules={[{ required: true, message: '请输入支付密码' }]} |
|||
> |
|||
<Input type='password' placeholder='请输入支付密码' /> |
|||
</Form.Item> */} |
|||
</View > |
|||
) |
|||
} |
@ -0,0 +1,2 @@ |
|||
.container { |
|||
} |
@ -0,0 +1,179 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Button, CapsuleTabs, Form, Modal, Toast } from 'antd-mobile'; |
|||
|
|||
import FormImage from '/components/FormImage'; |
|||
import FormLabel from '/components/FormLabel'; |
|||
import React, { useEffect, useMemo, useState } from 'react'; |
|||
import { getOrderInfoAction } from '/request/actions'; |
|||
import { getUrlParam } from '/utils'; |
|||
import Taro from '@tarojs/taro'; |
|||
|
|||
// import Alipay from '../Alipay'; |
|||
// import Wxpay from '../Wxpay'; |
|||
// import BankPay from '../BankPay'; |
|||
|
|||
// import aliSrc from '/assets/icons/ali.svg'; |
|||
// import wxSrc from '/assets/icons/wx.svg'; |
|||
// import bankSrc from '/assets/icons/bank.svg'; |
|||
import { orderConfirmAction } from '../../../../request/actions'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
// const TabItem = ({ src, text }) => ( |
|||
// <View className={styles['tabitem']}> |
|||
// <img src={src} /> |
|||
// {text} |
|||
// </View> |
|||
// ); |
|||
|
|||
export default function FormBar() { |
|||
const [form] = Form.useForm(); |
|||
// const [showFee, updateShowFee] = useState(false); |
|||
|
|||
useEffect(() => { |
|||
getOrderInfoAction({ |
|||
orderId: getUrlParam('orderId') |
|||
}).then(res => { |
|||
const orderShopInfo = res.orderShopInfo || {}; |
|||
const feeShopInfo = res.feeShopInfo || {}; |
|||
const _orderShopInfo = Object.keys(orderShopInfo).reduce((res, key) => { |
|||
return { |
|||
...res, |
|||
[`pay-${key}`]: orderShopInfo[key] |
|||
} |
|||
}, {}); |
|||
const _feeShopInfo = Object.keys(orderShopInfo).reduce((res, key) => { |
|||
return { |
|||
...res, |
|||
[`fee-${key}`]: feeShopInfo[key] |
|||
} |
|||
}, {}); |
|||
form.setFieldsValue({ |
|||
...res, |
|||
..._orderShopInfo, |
|||
..._feeShopInfo |
|||
}); |
|||
|
|||
// updateShowFee(res.feePrice || res.feeImage || res.feeShopInfo) |
|||
}); |
|||
}, []); |
|||
|
|||
const handleSubmit = () => { |
|||
Modal.confirm({ |
|||
title: '是否确认收款?', |
|||
onConfirm: async () => { |
|||
try { |
|||
const orderId = getUrlParam('orderId'); |
|||
await orderConfirmAction({ orderId }); |
|||
Toast.show({ |
|||
icon: 'success', |
|||
content: '已成功确认收款信息' |
|||
}); |
|||
Taro.navigateTo({ |
|||
url: '/pages/dataset/index' |
|||
}); |
|||
} |
|||
catch (err) { |
|||
console.log('err: ', err); |
|||
} |
|||
} |
|||
}) |
|||
}; |
|||
|
|||
// const way = Form.useWatch('way', form); |
|||
// const feeway = Form.useWatch('feeway', form); |
|||
|
|||
// const detailRenderer = useMemo(() => { |
|||
// return { |
|||
// alipay: <Alipay namePrefix='pay' />, |
|||
// wx: <Wxpay namePrefix='pay' />, |
|||
// bank: <BankPay namePrefix='pay' /> |
|||
// }[way]; |
|||
// }, [way]); |
|||
|
|||
// const feeDetailRenderer = useMemo(() => { |
|||
// return { |
|||
// alipay: <Alipay namePrefix='fee' />, |
|||
// wx: <Wxpay namePrefix='fee' />, |
|||
// bank: <BankPay namePrefix='fee' /> |
|||
// }[feeway]; |
|||
// }, [feeway]); |
|||
|
|||
const payImage = Form.useWatch('payImage', form); |
|||
const feeImage = Form.useWatch('feeImage', form); |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form form={form} className={styles.form} layout='horizontal' > |
|||
<View className={styles.title}> |
|||
收款信息 |
|||
</View> |
|||
<Form.Item name='orderId' label='订单编号' childElementPosition='right'> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item name='orderDate' label='订单日期' childElementPosition='right'> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item name='orderPrice' label='订单金额' childElementPosition='right'> |
|||
<FormLabel render={val => `¥${val || 0}`} /> |
|||
</Form.Item> |
|||
<Form.Item name='feePrice' label='服务费金额' childElementPosition='right'> |
|||
<FormLabel render={val => `¥${val || 0}`} /> |
|||
</Form.Item> |
|||
<Form.Item name='payUserTelephone' label='付款账号' childElementPosition='right'> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item style={!payImage ? { display: 'none' } : {}} name='payImage' label='付款凭证' childElementPosition='right'> |
|||
<FormImage readonly /> |
|||
</Form.Item> |
|||
<Form.Item style={!feeImage ? { display: 'none' } : {}} name='feeImage' label='服务费凭证' childElementPosition='right'> |
|||
<FormImage readonly /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name='way' |
|||
label='提现方式' |
|||
valuePropName='activeKey' |
|||
initialValue='alipay' |
|||
childElementPosition='right' |
|||
> |
|||
<CapsuleTabs style={{ padding: 0 }}> |
|||
<CapsuleTabs.Tab title={<TabItem text='支付宝' src={aliSrc} />} key='alipay' /> |
|||
<CapsuleTabs.Tab title={<TabItem text='微信' src={wxSrc} />} key='wx' /> |
|||
<CapsuleTabs.Tab title={<TabItem text='银行卡' src={bankSrc} />} key='bank' /> |
|||
</CapsuleTabs> |
|||
</Form.Item> |
|||
{detailRenderer} |
|||
{showFee && ( |
|||
<React.Fragment> |
|||
<View className={styles.title}> |
|||
服务费收款信息 |
|||
</View> |
|||
<Form.Item name='feePrice' label='服务费' childElementPosition='right'> |
|||
<FormLabel render={val => `¥${val || 0}`} /> |
|||
</Form.Item> |
|||
<Form.Item name='feeImage' label='服务费付款凭证' childElementPosition='right'> |
|||
<FormImage readonly /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='feeway' |
|||
label='提现方式' |
|||
valuePropName='activeKey' |
|||
initialValue='alipay' |
|||
childElementPosition='right' |
|||
> |
|||
<CapsuleTabs style={{ padding: 0 }}> |
|||
<CapsuleTabs.Tab title={<TabItem text='支付宝' src={aliSrc} />} key='alipay' /> |
|||
<CapsuleTabs.Tab title={<TabItem text='微信' src={wxSrc} />} key='wx' /> |
|||
<CapsuleTabs.Tab title={<TabItem text='银行卡' src={bankSrc} />} key='bank' /> |
|||
</CapsuleTabs> |
|||
</Form.Item> |
|||
{feeDetailRenderer} |
|||
</React.Fragment> |
|||
)} */} |
|||
</Form > |
|||
<Button color='primary' shape='rounded' className={styles.submit} onClick={handleSubmit}> |
|||
确认收款 |
|||
</Button> |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,71 @@ |
|||
.container { |
|||
background-color: #fff; |
|||
border-radius: 18px; |
|||
padding: 32px 32px 48px 32px; |
|||
|
|||
.tabitem { |
|||
font-size: 24px; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
img { |
|||
width: 28px; |
|||
margin-right: 8px; |
|||
} |
|||
} |
|||
|
|||
.btn { |
|||
width: 100%; |
|||
margin-top: 24px; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 28px; |
|||
font-weight: 600; |
|||
margin: 24px 0; |
|||
} |
|||
|
|||
.margin { |
|||
margin-top: 32px; |
|||
} |
|||
|
|||
.submit { |
|||
width: 100%; |
|||
margin-top: 72px; |
|||
} |
|||
|
|||
:global { |
|||
.adm-capsule-tabs-header { |
|||
padding: 0; |
|||
} |
|||
|
|||
.adm-list-item-content-prefix { |
|||
width: 168px; |
|||
} |
|||
|
|||
.adm-capsule-tabs-tab { |
|||
padding: 8px; |
|||
border-radius: 6px; |
|||
border: 1px solid transparent; |
|||
transition: all .2s ease-in-out; |
|||
} |
|||
|
|||
.adm-capsule-tabs-tab-active { |
|||
color: var(--color-primary); |
|||
background-color: #f0f9ff; |
|||
border-color: var(--color-primary); |
|||
} |
|||
|
|||
.adm-list-body { |
|||
border-top: unset; |
|||
} |
|||
|
|||
.adm-list-item { |
|||
padding-left: 0; |
|||
} |
|||
|
|||
.adm-list-item-content { |
|||
border-top: unset; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Form } from 'antd-mobile'; |
|||
import FormLabel from '/components/FormLabel'; |
|||
import FormImage from '/components/FormImage'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Wxpay({ namePrefix }) { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form.Item |
|||
name={`${namePrefix}-weixin`} |
|||
label='微信账号' |
|||
childElementPosition='right' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name={`${namePrefix}-weixinName`} |
|||
label='微信姓名' |
|||
childElementPosition='right' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name={`${namePrefix}-weixinImage`} |
|||
label='收款码' |
|||
childElementPosition='right' |
|||
> |
|||
<FormImage readonly width={72} height={72} /> |
|||
</Form.Item> */} |
|||
{/* <Form.Item |
|||
name='password' |
|||
label='支付密码' |
|||
rules={[{ required: true, message: '请输入支付密码' }]} |
|||
> |
|||
<Input type='password' placeholder='请输入支付密码' /> |
|||
</Form.Item> */} |
|||
</View > |
|||
) |
|||
} |
@ -0,0 +1,2 @@ |
|||
.container { |
|||
} |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '确认收款', |
|||
usingComponents: {}, |
|||
}) |
@ -0,0 +1,25 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import Taro from '@tarojs/taro'; |
|||
|
|||
import FormBar from './components/FormBar'; |
|||
import { NavBar } from 'antd-mobile'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function ShopInfo() { |
|||
|
|||
const handleBack = () => { |
|||
Taro.navigateTo({ |
|||
url: '/pages/dataset/index' |
|||
}); |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<NavBar onBack={handleBack} style={{ padding: 0 }}>确认收款</NavBar> |
|||
<View className={styles.wrapper}> |
|||
<FormBar /> |
|||
</View> |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,10 @@ |
|||
.container { |
|||
background: linear-gradient(180deg, #d7ebfa, #f5f5f5 30vh); |
|||
height: 100%; |
|||
overflow-y: auto; |
|||
|
|||
.wrapper { |
|||
box-sizing: border-box; |
|||
padding: 42px; |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '反馈', |
|||
usingComponents: {}, |
|||
}) |
@ -0,0 +1,45 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import Taro from '@tarojs/taro'; |
|||
|
|||
import { Button, Form, NavBar, TextArea } from 'antd-mobile'; |
|||
import { feedbackAction } from '../../request/actions'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Feedback() { |
|||
const [form] = Form.useForm(); |
|||
|
|||
const handleBack = () => { |
|||
Taro.navigateTo({ |
|||
url: '/pages/mine/index' |
|||
}); |
|||
}; |
|||
|
|||
const handleVery = async () => { |
|||
try { |
|||
const values = await form.validateFields(); |
|||
console.log('values: ', values); |
|||
await feedbackAction(values); |
|||
handleBack(); |
|||
} |
|||
catch (err) { |
|||
console.log('err: ', err); |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<NavBar onBack={handleBack} style={{ padding: 0 }}> |
|||
反馈 |
|||
</NavBar> |
|||
<Form form={form} className={styles.form} layout='horizontal'> |
|||
<Form.Item name='contents' rules={[{ required: true, message: '请填写反馈内容' }]}> |
|||
<TextArea placeholder='请输入反馈' autoFocus autoSize={{ minRows: 12 }} /> |
|||
</Form.Item> |
|||
<Button color='primary' shape='rounded' className={styles.submit} onClick={handleVery}> |
|||
提交 |
|||
</Button> |
|||
</Form > |
|||
</View> |
|||
); |
|||
} |
@ -0,0 +1,28 @@ |
|||
.container { |
|||
background: linear-gradient(180deg, #d7ebfa, #f5f5f5 30vh); |
|||
height: 100%; |
|||
|
|||
.form { |
|||
background-color: #fff; |
|||
border-radius: 18px; |
|||
padding: 32px 32px 48px 32px; |
|||
width: 620px; |
|||
margin: 0 auto; |
|||
margin-top: 72px; |
|||
|
|||
:global { |
|||
.adm-list-body { |
|||
border-top: unset; |
|||
} |
|||
|
|||
.adm-list-item { |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
.submit { |
|||
width: 100%; |
|||
margin-top: 72px; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,93 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Button, Form, Input, Toast } from 'antd-mobile'; |
|||
import { useState } from 'react'; |
|||
import Taro from '@tarojs/taro'; |
|||
import { editPasswordAction } from '../../../../request/actions'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function FormBar() { |
|||
const [form] = Form.useForm(); |
|||
|
|||
const [countdown, setCountdown] = useState(60); |
|||
const [isCounting, setIsCounting] = useState(false); |
|||
|
|||
const sendVerificationCode = () => { |
|||
if (isCounting) { |
|||
return; |
|||
} |
|||
setIsCounting(true); |
|||
// Simulate sending verification code |
|||
console.log('Sending verification code...'); |
|||
const interval = setInterval(() => { |
|||
setCountdown(prev => { |
|||
if (prev <= 1) { |
|||
clearInterval(interval); |
|||
setIsCounting(false); |
|||
return 60; // Reset countdown |
|||
} |
|||
return prev - 1; |
|||
}); |
|||
}, 1000); |
|||
}; |
|||
|
|||
|
|||
const handleReset = async () => { |
|||
try { |
|||
const values = await form.validateFields(); |
|||
await editPasswordAction(values); |
|||
Toast.show({ |
|||
icon: 'success', |
|||
content: '密码修改成功' |
|||
}); |
|||
Taro.navigateBack(); |
|||
} |
|||
catch (err) { |
|||
console.log('err: ', err); |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form form={form} className={styles.form}> |
|||
<Form.Item |
|||
name='newPassword' |
|||
label='新密码' |
|||
rules={[ |
|||
{ required: true, message: '请输入密码' }, |
|||
{ whitespace: true, message: '请输入有效的密码' } |
|||
]} |
|||
> |
|||
<Input placeholder='请输入密码' /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='repassword' |
|||
label='确认密码' |
|||
rules={[ |
|||
{ required: true, message: '请确认密码' }, |
|||
{ whitespace: true, message: '请输入有效的密码' } |
|||
]} |
|||
> |
|||
<Input type='password' placeholder='请确认密码' /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name='veryCode' |
|||
label='验证码' |
|||
rules={[{ required: true, message: '请输入新密码' }]} |
|||
extra={ |
|||
<View className={styles.extra} onClick={sendVerificationCode}> |
|||
<a>{isCounting ? `(${countdown}秒后重新获取)` : '发送验证码'}</a> |
|||
</View> |
|||
} |
|||
> |
|||
<Input |
|||
placeholder='请输入验证码' |
|||
style={{ '--text-align': 'left' }} |
|||
clearable |
|||
/> |
|||
</Form.Item> */} |
|||
</Form > |
|||
<Button color='primary' className={styles.submit} onClick={handleReset}>确认修改</Button> |
|||
</View > |
|||
) |
|||
} |
@ -0,0 +1,46 @@ |
|||
.container { |
|||
background-color: #fff; |
|||
border-radius: 18px; |
|||
margin-top: 72px; |
|||
padding: 32px; |
|||
|
|||
.form { |
|||
:global { |
|||
.adm-list-body { |
|||
border-top: unset; |
|||
} |
|||
|
|||
.adm-list-item { |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
|
|||
.extra { |
|||
color: #007AFF; |
|||
position: relative; |
|||
font-size: 24px; |
|||
top: 24px; |
|||
} |
|||
} |
|||
|
|||
.bar { |
|||
display: flex; |
|||
justify-content: center; |
|||
padding: 24px 72px; |
|||
|
|||
.btn { |
|||
font-size: 24px; |
|||
flex: 1; |
|||
text-align: center; |
|||
|
|||
&:first-child { |
|||
border-right: 1px solid #eee; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.submit { |
|||
width: 100%; |
|||
margin-top: 72px; |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { useLoad } from '@tarojs/taro'; |
|||
import logoSrc from '/assets/icons/logo.png'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function HeaderBar() { |
|||
|
|||
useLoad(() => { |
|||
console.log('Page loaded.') |
|||
}); |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<View className={styles.row}> |
|||
<View className={styles.title}> |
|||
Hi Weaith |
|||
</View> |
|||
<View className={styles.subtitle}> |
|||
欢迎您来到融易诚 |
|||
</View> |
|||
</View> |
|||
<View className={styles.row2}> |
|||
<View className={styles.item}> |
|||
<img src={logoSrc} className={styles.logo} alt='logo' /> |
|||
<View className={styles.title}> |
|||
融易诚 |
|||
</View> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,42 @@ |
|||
.container { |
|||
.row { |
|||
.title { |
|||
font-weight: normal; |
|||
overflow-wrap: normal; |
|||
font-size: 42px; |
|||
color: rgb(48, 49, 51); |
|||
} |
|||
|
|||
.subtitle { |
|||
font-weight: normal; |
|||
overflow-wrap: normal; |
|||
font-size: 32px; |
|||
color: rgb(48, 49, 51); |
|||
} |
|||
} |
|||
|
|||
.row2 { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
|
|||
.item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
margin-right: 42px; |
|||
|
|||
.title { |
|||
font-weight: normal; |
|||
overflow-wrap: normal; |
|||
font-size: 32px; |
|||
color: rgb(48, 49, 51); |
|||
text-align: center; |
|||
} |
|||
.logo { |
|||
width: 120px; |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '忘记密码', |
|||
usingComponents: {}, |
|||
}) |
@ -0,0 +1,34 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import Taro, { useLoad } from '@tarojs/taro'; |
|||
|
|||
import HeaderBar from './components/HeaderBar'; |
|||
import FormBar from './components/FormBar'; |
|||
import { NavBar } from 'antd-mobile'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Forget() { |
|||
const query = Taro.getCurrentInstance()?.router?.params; |
|||
|
|||
useLoad(() => { |
|||
console.log('Page loaded.') |
|||
}); |
|||
|
|||
const handleBack = () => { |
|||
Taro.navigateBack(); |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
{query?.hasBack && ( |
|||
<NavBar onBack={handleBack} style={{ padding: 0 }}> |
|||
忘记密码 |
|||
</NavBar> |
|||
)} |
|||
<View className={styles.wrapper}> |
|||
<HeaderBar /> |
|||
<FormBar /> |
|||
</View> |
|||
</View> |
|||
); |
|||
} |
@ -0,0 +1,51 @@ |
|||
.container { |
|||
background: linear-gradient(180deg, #d7ebfa, #f5f5f5 30vh); |
|||
height: 100%; |
|||
} |
|||
|
|||
.wrapper { |
|||
box-sizing: border-box; |
|||
padding: 120px 42px; |
|||
|
|||
.row { |
|||
.title { |
|||
font-weight: normal; |
|||
overflow-wrap: normal; |
|||
font-size: 42px; |
|||
color: rgb(48, 49, 51); |
|||
} |
|||
|
|||
.subtitle { |
|||
font-weight: normal; |
|||
overflow-wrap: normal; |
|||
font-size: 32px; |
|||
color: rgb(48, 49, 51); |
|||
} |
|||
} |
|||
|
|||
.row2 { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
|
|||
.item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
margin-right: 42px; |
|||
|
|||
.title { |
|||
font-weight: normal; |
|||
overflow-wrap: normal; |
|||
font-size: 32px; |
|||
color: rgb(48, 49, 51); |
|||
text-align: center; |
|||
} |
|||
|
|||
.logo { |
|||
width: 120px; |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,40 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Form } from 'antd-mobile'; |
|||
import FormLabel from '/components/FormLabel'; |
|||
import FormImage from '/components/FormImage'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Alipay() { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form.Item |
|||
name='alipay' |
|||
label='支付宝账号' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='alipayName' |
|||
label='支付宝姓名' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='alipayImage' |
|||
label='收款码' |
|||
rules={[{ required: true, message: '请选择请码' }]} |
|||
> |
|||
<FormImage readonly width={72} height={72} /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name='password' |
|||
label='支付密码' |
|||
rules={[{ required: true, message: '请输入支付密码' }]} |
|||
> |
|||
<Input type='password' placeholder='请输入支付密码' /> |
|||
</Form.Item> */} |
|||
</View > |
|||
) |
|||
} |
@ -0,0 +1,3 @@ |
|||
.container { |
|||
|
|||
} |
@ -0,0 +1,38 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Form } from 'antd-mobile'; |
|||
import FormLabel from '/components/FormLabel'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function BankPay() { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form.Item |
|||
name='payeeBank' |
|||
label='开户行' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='payeeAccount' |
|||
label='银行卡号' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='payeeName' |
|||
label='开户人' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name='password' |
|||
label='支付密码' |
|||
rules={[{ required: true, message: '请输入支付密码' }]} |
|||
> |
|||
<Input type='password' placeholder='请输入支付密码' /> |
|||
</Form.Item> */} |
|||
</View > |
|||
) |
|||
} |
@ -0,0 +1,2 @@ |
|||
.container { |
|||
} |
@ -0,0 +1,39 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Form } from 'antd-mobile'; |
|||
import FormLabel from '/components/FormLabel'; |
|||
import FormImage from '/components/FormImage'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Wxpay() { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form.Item |
|||
name='weixin' |
|||
label='微信账号' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='weixinName' |
|||
label='微信姓名' |
|||
> |
|||
<FormLabel /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name='weixinImage' |
|||
label='收款码' |
|||
> |
|||
<FormImage readonly width={72} height={72} /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name='password' |
|||
label='支付密码' |
|||
rules={[{ required: true, message: '请输入支付密码' }]} |
|||
> |
|||
<Input type='password' placeholder='请输入支付密码' /> |
|||
</Form.Item> */} |
|||
</View > |
|||
) |
|||
} |
@ -0,0 +1,2 @@ |
|||
.container { |
|||
} |
@ -0,0 +1,123 @@ |
|||
import { useState } from 'react'; |
|||
import cls from 'classnames'; |
|||
import { View } from '@tarojs/components'; |
|||
import { Button, Form, Input, Popup, Toast } from 'antd-mobile'; |
|||
|
|||
// import Alipay from './Alipay'; |
|||
// import Wxpay from './Wxpay'; |
|||
// import BankPay from './BankPay'; |
|||
|
|||
// import aliSrc from '/assets/icons/ali.svg'; |
|||
// import wxSrc from '/assets/icons/wx.svg'; |
|||
// import bankSrc from '/assets/icons/bank.svg'; |
|||
|
|||
import FormLabel from '/components/FormLabel'; |
|||
|
|||
import { getShopInfoAction, userWithdrawalAction } from '../../../../request/actions'; |
|||
import styles from './index.module.less'; |
|||
|
|||
// const TabItem = ({ src, text }) => ( |
|||
// <View className={styles['tabitem']}> |
|||
// <img src={src} /> |
|||
// {text} |
|||
// </View> |
|||
// ); |
|||
|
|||
const Balance = ({ balanceData, className, onAfterClose }) => { |
|||
const prefix = cls(styles.container, className); |
|||
const [visible, setVisible] = useState(false); |
|||
const [form] = Form.useForm(); |
|||
|
|||
const onShow = () => { |
|||
setVisible(true); |
|||
// 商铺收款信息 |
|||
getShopInfoAction().then(res => { |
|||
form.setFieldsValue(res); |
|||
}); |
|||
}; |
|||
|
|||
const onClose = () => { |
|||
setVisible(false); |
|||
onAfterClose(); |
|||
setTimeout(() => { |
|||
form.resetFields(); |
|||
}, 200); |
|||
}; |
|||
|
|||
const handleWithdrawal = async () => { |
|||
try { |
|||
const values = await form.validateFields(); |
|||
const cash = values.cash; |
|||
await userWithdrawalAction({ cash }); |
|||
Toast.show({ |
|||
content: '您已提现成功' |
|||
}); |
|||
onClose(); |
|||
} |
|||
catch (err) { |
|||
console.log('err: ', err); |
|||
} |
|||
}; |
|||
|
|||
// const way = Form.useWatch('way', form); |
|||
|
|||
// const detailRenderer = useMemo(() => { |
|||
// return { |
|||
// alipay: <Alipay />, |
|||
// wx: <Wxpay />, |
|||
// bank: <BankPay /> |
|||
// }[way]; |
|||
// }, [way]); |
|||
|
|||
const balance = balanceData?.balance || '0.00'; |
|||
|
|||
return ( |
|||
<View className={prefix}> |
|||
<View className={styles.title}>账户余额</View> |
|||
<View className={styles.subtitle}>¥{balance}</View> |
|||
<Button color='primary' className={styles.btn} onClick={onShow}>提现</Button> |
|||
<Popup |
|||
visible={visible} |
|||
onMaskClick={onClose} |
|||
bodyClassName={styles['popup-wrapper']} |
|||
> |
|||
<Form form={form} className={styles.popup} layout='horizontal'> |
|||
<View className={styles.balance}> |
|||
<FormLabel value={balance} render={val => `当前可用提现余额¥${val}`} /> |
|||
</View> |
|||
<Form.Item |
|||
name='cash' |
|||
label='提现金额' |
|||
// extra={`当前可用提现余额¥${balance}`} |
|||
rules={[{ required: true, message: '请输入提现金额' }]} |
|||
> |
|||
<Input className={styles.cash} type='number' min={0} max={9999999999} placeholder='请输入提现金额' /> |
|||
</Form.Item> |
|||
{/* <Form.Item |
|||
name='way' |
|||
label='提现方式' |
|||
valuePropName='activeKey' |
|||
initialValue='alipay' |
|||
> |
|||
<CapsuleTabs style={{ padding: 0 }}> |
|||
<CapsuleTabs.Tab title={<TabItem text='支付宝' src={aliSrc} />} key='alipay' /> |
|||
<CapsuleTabs.Tab title={<TabItem text='微信' src={wxSrc} />} key='wx' /> |
|||
<CapsuleTabs.Tab title={<TabItem text='银行卡' src={bankSrc} />} key='bank' /> |
|||
</CapsuleTabs> |
|||
</Form.Item> |
|||
{detailRenderer} */} |
|||
<Button |
|||
className={styles.btn} |
|||
color='primary' |
|||
shape='rounded' |
|||
onClick={handleWithdrawal} |
|||
> |
|||
确认提现 |
|||
</Button> |
|||
</Form> |
|||
</Popup> |
|||
</View> |
|||
) |
|||
}; |
|||
|
|||
export default Balance; |
@ -0,0 +1,98 @@ |
|||
.container { |
|||
background-color: #fff; |
|||
border-radius: 18px; |
|||
padding: 32px; |
|||
|
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
|
|||
.title { |
|||
font-size: 28px; |
|||
font-weight: 600; |
|||
margin-bottom: 24px; |
|||
} |
|||
|
|||
.subtitle { |
|||
font-size: 42px; |
|||
font-weight: 600; |
|||
margin-bottom: 24px; |
|||
color: var(--color-primary); |
|||
} |
|||
|
|||
.btn { |
|||
width: 380px; |
|||
} |
|||
} |
|||
|
|||
.popup-wrapper { |
|||
height: 480px; |
|||
} |
|||
|
|||
.balance { |
|||
display: flex; |
|||
justify-content: center; |
|||
margin: 48px 0 24px; |
|||
} |
|||
|
|||
.popup { |
|||
padding: 24px; |
|||
|
|||
.tabitem { |
|||
font-size: 24px; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
img { |
|||
width: 28px; |
|||
margin-right: 8px; |
|||
} |
|||
} |
|||
|
|||
.btn { |
|||
width: 100%; |
|||
margin-top: 24px; |
|||
} |
|||
|
|||
:global { |
|||
.adm-capsule-tabs-header { |
|||
padding: 0; |
|||
} |
|||
|
|||
.adm-capsule-tabs-tab { |
|||
padding: 8px 20px; |
|||
border-radius: 6px; |
|||
border: 1px solid transparent; |
|||
transition: all .2s ease-in-out; |
|||
} |
|||
|
|||
.adm-capsule-tabs-tab-active { |
|||
color: var(--color-primary); |
|||
background-color: #f0f9ff; |
|||
border-color: var(--color-primary); |
|||
} |
|||
|
|||
.adm-list-item { |
|||
padding: 32px 0; |
|||
} |
|||
|
|||
.adm-form-item-label { |
|||
font-size: 28px; |
|||
position: relative; |
|||
top: 12px; |
|||
} |
|||
.adm-input-element { |
|||
font-size: 42px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
:global { |
|||
.adm-list-default .adm-list-body { |
|||
border-top: unset; |
|||
} |
|||
.adm-list-item-content { |
|||
border-top: unset; |
|||
} |
|||
} |
@ -0,0 +1,38 @@ |
|||
import React from 'react'; |
|||
import { List, Empty, Tag } from 'antd-mobile'; |
|||
import { Text, View } from '@tarojs/components'; |
|||
import cls from 'classnames'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
const DataList = ({ dataSource = [], className }) => { |
|||
const prefix = cls(styles.container, className); |
|||
|
|||
return ( |
|||
<View className={prefix}> |
|||
<View className={styles.title}>交易记录</View> |
|||
{dataSource.length > 0 ? ( |
|||
<List> |
|||
{dataSource.map((item, index) => ( |
|||
<List.Item key={index}> |
|||
<View className={styles.row}> |
|||
<View className={styles.left}> |
|||
<Tag className={styles.status} color={item.isIn ? 'primary' : 'warning'}>{item.isIn ? '入账' : '出账'}</Tag> |
|||
{item.cash} |
|||
</View> |
|||
<Text className={styles.tag} color='default'>{item.remark}</Text> |
|||
</View> |
|||
</List.Item> |
|||
))} |
|||
</List> |
|||
) : ( |
|||
<Empty |
|||
style={{ padding: '64px 0' }} |
|||
description='暂无数据' |
|||
/> |
|||
)} |
|||
</View> |
|||
) |
|||
}; |
|||
|
|||
export default DataList; |
@ -0,0 +1,44 @@ |
|||
.container { |
|||
background-color: #fff; |
|||
border-radius: 18px; |
|||
padding: 32px; |
|||
margin-top: 24px; |
|||
|
|||
.title { |
|||
font-size: 28px; |
|||
font-weight: 600; |
|||
margin-bottom: 24px; |
|||
} |
|||
|
|||
.row { |
|||
width: 100%; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
.left { |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 24px; |
|||
|
|||
.status { |
|||
margin-right: 12px; |
|||
} |
|||
} |
|||
|
|||
.tag { |
|||
font-size: 24px; |
|||
} |
|||
|
|||
&:not(:last-child) { |
|||
border-bottom: 1px solid #eee; |
|||
} |
|||
} |
|||
|
|||
:global { |
|||
.adm-list-default .adm-list-body { |
|||
border: unset; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '资产管理', |
|||
usingComponents: {}, |
|||
}) |
@ -0,0 +1,77 @@ |
|||
import { useState } from 'react'; |
|||
import { View, ScrollView } from '@tarojs/components'; |
|||
import Taro, { useLoad } from '@tarojs/taro'; |
|||
import { NavBar, PullToRefresh } from 'antd-mobile'; |
|||
|
|||
import DataList from './components/DataList'; |
|||
import Balance from './components/Balance'; |
|||
import { myBalanceAction, userWalletLogListLoopAction } from '../../request/actions'; |
|||
import useRequestList from '../../hooks/useRequestList'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
const fetchDataRecursively = async (times, datasource) => { |
|||
if (times <= 0 || (datasource && datasource?.length < 10)) { |
|||
return []; |
|||
} |
|||
const lastLogId = datasource?.[datasource?.length - 1]?.logId; |
|||
const result = await userWalletLogListLoopAction({ lastLogId }); |
|||
const nextResults = await fetchDataRecursively(times - 1, result?.dataList); |
|||
return [...result?.dataList, ...nextResults]; |
|||
}; |
|||
|
|||
export default function Funds() { |
|||
const [data, setData] = useState([]); |
|||
|
|||
// 银行卡余额 |
|||
const { data: balanceData, getDataTask: getBananceData } = useRequestList({ |
|||
immediate: false, |
|||
showMsg: false, |
|||
action: myBalanceAction |
|||
}); |
|||
|
|||
const handleInitData = async () => { |
|||
getBananceData(); |
|||
const dataSource = await fetchDataRecursively(2); |
|||
setData(dataSource); |
|||
}; |
|||
|
|||
useLoad(() => { |
|||
handleInitData(); |
|||
}); |
|||
|
|||
const handleBack = () => { |
|||
Taro.navigateTo({ |
|||
url: '/pages/mine/index' |
|||
}); |
|||
}; |
|||
|
|||
const handleScrollToLower = async () => { |
|||
const lastLogId = data?.[data?.length - 1]?.logId; |
|||
const result = await userWalletLogListLoopAction({ lastLogId }); |
|||
const newDataSource = result?.dataList; |
|||
setData([...data, ...newDataSource]); |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<NavBar onBack={handleBack} style={{ padding: 0 }}> |
|||
资金管理 |
|||
</NavBar> |
|||
<PullToRefresh |
|||
onRefresh={handleInitData} |
|||
> |
|||
<ScrollView |
|||
scrollY |
|||
onScrollToLower={handleScrollToLower} |
|||
lowerThreshold={50} |
|||
> |
|||
<View className={styles.wrapper}> |
|||
<Balance balanceData={balanceData} onAfterClose={handleInitData} /> |
|||
<DataList className={styles.list} dataSource={data} /> |
|||
</View> |
|||
</ScrollView> |
|||
</PullToRefresh> |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,11 @@ |
|||
.container { |
|||
background: linear-gradient(180deg, #d7ebfa, #f5f5f5 30vh); |
|||
height: 100%; |
|||
|
|||
.wrapper { |
|||
box-sizing: border-box; |
|||
padding: 42px; |
|||
height: calc(100vh - 82px); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,29 @@ |
|||
|
|||
import { View, Swiper, SwiperItem, Image } from '@tarojs/components'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Banner({ |
|||
swiperList = [] |
|||
}) { |
|||
if (swiperList.length === 0) { |
|||
return <View className={styles.container} style={{ background: '#eee' }}></View> |
|||
} |
|||
|
|||
return ( |
|||
<Swiper |
|||
className={styles.container} |
|||
indicatorColor='#999' |
|||
indicatorActiveColor='#007AFF' |
|||
circular |
|||
indicatorDots |
|||
autoplay |
|||
> |
|||
{swiperList.map((bannerSrc, index) => ( |
|||
<SwiperItem key={index}> |
|||
<Image style={{ width: '100%' }} src={bannerSrc} mode='aspectFit' /> |
|||
</SwiperItem> |
|||
))} |
|||
</Swiper> |
|||
) |
|||
} |
@ -0,0 +1,5 @@ |
|||
|
|||
.container { |
|||
margin-top: 36px; |
|||
height: 328px; |
|||
} |
@ -0,0 +1,33 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { NoticeBar } from 'antd-mobile'; |
|||
import Taro from '@tarojs/taro'; |
|||
|
|||
import logoSrc from '/assets/icons/logo.png'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Header({ |
|||
noticeTitle |
|||
}) { |
|||
|
|||
return ( |
|||
<View className={styles.header}> |
|||
<View className={styles.left}> |
|||
<img src={logoSrc} className={styles.logo} alt='logo' /> |
|||
<View className={styles.title}> |
|||
融易诚 |
|||
</View> |
|||
</View> |
|||
{noticeTitle && ( |
|||
<NoticeBar |
|||
content={noticeTitle} |
|||
color='alert' |
|||
style={{ width: 206, height: 32, padding: '0 8px', color: 'red', background: '#f5f5f5' }} |
|||
shape='rounded' |
|||
bordered={false} |
|||
onClick={() => Taro.navigateTo({ url: '/pages/notice/index' })} |
|||
/> |
|||
)} |
|||
</View> |
|||
); |
|||
} |
@ -0,0 +1,20 @@ |
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.left { |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.logo { |
|||
width: 68px; |
|||
margin-right: 12px; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 28px; |
|||
font-weight: 500; |
|||
color: #333; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,53 @@ |
|||
|
|||
import { ExclamationCircleFill } from 'antd-mobile-icons' |
|||
import { View } from '@tarojs/components'; |
|||
import { Button } from 'antd-mobile'; |
|||
import Taro from '@tarojs/taro'; |
|||
import logoSrc from '/assets/icons/logo.png'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
const MyShop = () => { |
|||
|
|||
const handleVery = () => { |
|||
Taro.navigateTo({ |
|||
url: '/pages/shopinfo/index' |
|||
}); |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<View className={styles.title}> |
|||
<img src={logoSrc} className={styles.logo} alt='logo' /> |
|||
<View className={styles.title}> |
|||
我的小店 |
|||
</View> |
|||
</View> |
|||
<View className={styles.card}> |
|||
<View className={styles.title}> |
|||
完成考试 帮您开启成功之路 |
|||
</View> |
|||
<View className={styles.info}> |
|||
<View className={styles.left}> |
|||
<ExclamationCircleFill className={styles.icon} /> |
|||
<View> |
|||
<View className={styles.title}>店铺认证</View> |
|||
<View className={styles.subtitle}>请完成店铺认证以开启完整功能</View> |
|||
</View> |
|||
</View> |
|||
<Button |
|||
className={styles.btn} |
|||
color='primary' |
|||
shape='rounded' |
|||
size='small' |
|||
onClick={handleVery} |
|||
> |
|||
立即认证 |
|||
</Button> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
export default MyShop; |
@ -0,0 +1,70 @@ |
|||
.container { |
|||
background: linear-gradient(180deg, #1677ff, #fff 30vh); |
|||
padding: 24px; |
|||
border-radius: 12px; |
|||
margin-top: 32px; |
|||
|
|||
.title { |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.logo { |
|||
width: 98px; |
|||
margin-right: 12px; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 28px; |
|||
font-weight: 500; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.card { |
|||
padding: 18px 24px; |
|||
border-radius: 12px; |
|||
background-color: #fff; |
|||
margin-top: 18px; |
|||
|
|||
.title { |
|||
font-size: 28px; |
|||
font-weight: 500; |
|||
color: #333; |
|||
} |
|||
|
|||
.info { |
|||
margin-top: 18px; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
.left { |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.icon { |
|||
color: rgb(255, 0, 0); |
|||
font-size: 42px; |
|||
margin-right: 18px; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 24px; |
|||
color: #333; |
|||
font-weight: bolder; |
|||
} |
|||
.subtitle { |
|||
font-size: 22px; |
|||
color: #999; |
|||
margin-top: 4px; |
|||
} |
|||
} |
|||
|
|||
.btn { |
|||
font-size: 24px; |
|||
padding: 0px 18px; |
|||
height: 48px; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
|
|||
import { View, Text } from '@tarojs/components'; |
|||
import { List } from 'antd-mobile'; |
|||
import { FileOutline } from 'antd-mobile-icons'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
const TodoList = () => { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<View className={styles.title}> |
|||
<Text className={styles.text}>今日任务</Text> |
|||
</View> |
|||
<List className={styles.list}> |
|||
<List.Item prefix={<FileOutline />} onClick={() => { }}> |
|||
1、完成店铺认证 |
|||
</List.Item> |
|||
<List.Item prefix={<FileOutline />} onClick={() => { }}> |
|||
2、完成信用优化 |
|||
</List.Item> |
|||
<List.Item prefix={<FileOutline />} onClick={() => { }}> |
|||
3、完成店铺认证 |
|||
</List.Item> |
|||
</List> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
export default TodoList; |
@ -0,0 +1,30 @@ |
|||
.container { |
|||
padding: 24px; |
|||
border-radius: 12px; |
|||
background-color: #fff; |
|||
margin-top: 24px; |
|||
|
|||
.title { |
|||
.text { |
|||
font-size: 28px; |
|||
font-weight: 500; |
|||
color: #333; |
|||
padding: 12px 0; |
|||
border-bottom: 4px solid #007AFF; |
|||
} |
|||
} |
|||
|
|||
.list { |
|||
margin-top: 18px; |
|||
|
|||
>* { |
|||
font-size: 28px !important; |
|||
} |
|||
|
|||
:global { |
|||
.adm-list-item { |
|||
// padding-left: unset; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
|
|||
import { ExclamationCircleFill } from 'antd-mobile-icons' |
|||
import { View, Text } from '@tarojs/components'; |
|||
import styles from './index.module.less'; |
|||
import { Button } from 'antd-mobile'; |
|||
|
|||
import { AddCircleOutline, TeamFill, RedoOutline } from 'antd-mobile-icons'; |
|||
import Taro from '@tarojs/taro'; |
|||
|
|||
const Toolbar = () => { |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<View className={styles.card} onClick={() => Taro.navigateTo({ url: '/pages/dataset/index' })}> |
|||
<AddCircleOutline className={styles.icon} style={{ color: '#007AFF' }} /> |
|||
<View>数据优化</View> |
|||
</View> |
|||
<View className={styles.card} onClick={() => Taro.navigateTo({ url: '/pages/team/index' })}> |
|||
<TeamFill className={styles.icon} style={{ color: 'rgb(114, 46, 209)' }} /> |
|||
<View>团队矩阵</View> |
|||
</View> |
|||
<View className={styles.card} onClick={() => Taro.navigateTo({ url: '/pages/invite/index' })}> |
|||
<RedoOutline className={styles.icon} style={{ color: 'rgb(82, 196, 26)' }} /> |
|||
<View>邀请商户</View> |
|||
</View> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
export default Toolbar; |
@ -0,0 +1,29 @@ |
|||
.container { |
|||
padding: 8px 24px; |
|||
border-radius: 12px; |
|||
background-color: #fff; |
|||
margin-top: 24px; |
|||
|
|||
display: flex; |
|||
justify-content: space-around; |
|||
|
|||
.card { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
padding: 12px; |
|||
|
|||
transition: all .2s ease-in-out; |
|||
|
|||
&:active { |
|||
background-color: #fafafa; |
|||
border-radius: 12px; |
|||
} |
|||
|
|||
.icon { |
|||
font-size: 48px; |
|||
margin-bottom: 12px; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '首页', |
|||
usingComponents: {}, |
|||
}) |
@ -0,0 +1,58 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import Header from './components/Header'; |
|||
import Banner from './components/Banner'; |
|||
import MyShop from './components/MyShop'; |
|||
import Toolbar from './components/Toolbar'; |
|||
import Taro from '@tarojs/taro'; |
|||
|
|||
import TodoList from './components/TodoList'; |
|||
import useRequestList from '../../hooks/useRequestList'; |
|||
import { getIndexPageAction } from '../../request/actions'; |
|||
import { useEffect } from 'react'; |
|||
import { Modal } from 'antd-mobile'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Index() { |
|||
const { data } = useRequestList({ |
|||
action: getIndexPageAction |
|||
}); |
|||
|
|||
useEffect(() => { |
|||
if (!data) { |
|||
return; |
|||
} |
|||
if (data?.systemMessage && data?.systemMessage?.messageId !== localStorage.getItem('messageId')) { |
|||
let handler = null; |
|||
handler = Modal.show({ |
|||
title: '有新的站内新,点击查看详情', |
|||
showCloseButton: false, |
|||
actions: [ |
|||
{ |
|||
key: 'view', |
|||
text: '查看站内信', |
|||
}, |
|||
], |
|||
onAction: action => { |
|||
if (action.key === 'view') { |
|||
Taro.navigateTo({ |
|||
url: '/pages/sitemessage/index' |
|||
}); |
|||
localStorage.setItem('messageId', data.systemMessage?.messageId); |
|||
handler.close(); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
}, [data]); |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Header {...data} /> |
|||
<Banner {...data} /> |
|||
<MyShop {...data} /> |
|||
<Toolbar {...data} /> |
|||
<TodoList {...data} /> |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,7 @@ |
|||
|
|||
.container { |
|||
background: linear-gradient(180deg, #d7ebfa, #f5f5f5 30vh); |
|||
height: 100%; |
|||
box-sizing: border-box; |
|||
padding: 24px 24px; |
|||
} |
@ -0,0 +1,88 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Button, Divider, Form } from 'antd-mobile'; |
|||
|
|||
import { useEffect, useRef } from 'react'; |
|||
import { QRCodeCanvas } from 'qrcode.react'; |
|||
import { CopyToClipboard } from 'react-copy-to-clipboard'; |
|||
import { useLocalStorageState } from 'ahooks'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
const FormUser = ({ value }) => ( |
|||
<View className={styles.user}> |
|||
<View className={styles.logo}> |
|||
1 |
|||
</View> |
|||
<View className={styles.phone}> |
|||
{value} |
|||
</View> |
|||
</View> |
|||
); |
|||
|
|||
const FormCopyText = ({ value }) => ( |
|||
<View className={styles.copy}> |
|||
<View className={styles.text}>{value}</View> |
|||
<CopyToClipboard text={value}><a>复制</a></CopyToClipboard> |
|||
</View> |
|||
); |
|||
|
|||
const QRCode = ({ value }) => { |
|||
const ref = useRef(); |
|||
|
|||
const handleDownload = () => { |
|||
const canvas = ref.current; |
|||
if (canvas) { |
|||
const imageURL = canvas.toDataURL('image/png'); // 转换为图片数据 |
|||
const link = document.createElement('a'); |
|||
link.href = imageURL; |
|||
link.download = '邀请码.png'; |
|||
link.click(); |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.qrcode}> |
|||
<QRCodeCanvas value={value} ref={ref} /> |
|||
<Button className={styles.btn} color='primary' shape='default' onClick={handleDownload}> |
|||
下载 |
|||
</Button> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
export default function FormBar() { |
|||
const [form] = Form.useForm(); |
|||
const [storage] = useLocalStorageState('userInfo'); |
|||
|
|||
useEffect(() => { |
|||
const origin = window.location.origin; |
|||
const invite_code = storage?.shareCode; |
|||
const invite_url = `${origin}/#/pages/register/index?shareCode=${invite_code}`; |
|||
form.setFieldsValue({ |
|||
user_info: storage?.telephone, |
|||
qrcode: invite_url, |
|||
invite_code, |
|||
invite_url |
|||
}); |
|||
}, []); |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form form={form} className={styles.form} layout='vertical'> |
|||
<Form.Item name='user_info'> |
|||
<FormUser /> |
|||
</Form.Item> |
|||
<Form.Item name='qrcode' style={{ display: 'flex', justifyContent: 'center' }}> |
|||
<QRCode /> |
|||
</Form.Item> |
|||
<Divider /> |
|||
<Form.Item name='invite_code' label='我的邀请码'> |
|||
<FormCopyText /> |
|||
</Form.Item> |
|||
<Form.Item name='invite_url' label='邀请链接'> |
|||
<FormCopyText /> |
|||
</Form.Item> |
|||
</Form > |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,80 @@ |
|||
.container { |
|||
background-color: #fff; |
|||
border-radius: 18px; |
|||
margin-top: 72px; |
|||
padding: 32px 32px 48px 32px; |
|||
|
|||
.form { |
|||
:global { |
|||
.adm-list-body { |
|||
border-top: unset; |
|||
} |
|||
|
|||
.adm-list-item { |
|||
padding-left: 0; |
|||
} |
|||
|
|||
.adm-list-item-content { |
|||
border: unset; |
|||
} |
|||
} |
|||
|
|||
.user { |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.logo { |
|||
width: 72px; |
|||
height: 72px; |
|||
border-radius: 50%; |
|||
background-color: pink; |
|||
font-size: 42px; |
|||
color: #fff; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
margin-right: 18px; |
|||
} |
|||
|
|||
.phone { |
|||
font-weight: 700; |
|||
font-size: 32px; |
|||
margin-bottom: 4px; |
|||
} |
|||
} |
|||
|
|||
.qrcode { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
|
|||
.btn { |
|||
width: 100%; |
|||
margin-top: 24px; |
|||
} |
|||
} |
|||
|
|||
.copy { |
|||
display: flex; |
|||
background-color: #f8f8f8; |
|||
padding: 12px; |
|||
border-radius: 2px; |
|||
font-size: 24px; |
|||
|
|||
.text { |
|||
flex: 1; |
|||
word-break: break-all; |
|||
margin-right: 24px; |
|||
} |
|||
|
|||
a { |
|||
flex-basis: 72px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.submit { |
|||
width: 100%; |
|||
margin-top: 72px; |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '店铺信息', |
|||
usingComponents: {}, |
|||
}) |
@ -0,0 +1,31 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import Taro, { useLoad } from '@tarojs/taro'; |
|||
|
|||
import FormBar from './components/FormBar'; |
|||
import { NavBar } from 'antd-mobile'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function Invite() { |
|||
|
|||
useLoad(() => { |
|||
console.log('Page loaded.') |
|||
}); |
|||
|
|||
const handleBack = () => { |
|||
Taro.navigateTo({ |
|||
url: '/pages/index/index' |
|||
}); |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<NavBar onBack={handleBack} style={{ padding: 0 }}> |
|||
邀请商户 |
|||
</NavBar> |
|||
<View className={styles.wrapper}> |
|||
<FormBar /> |
|||
</View> |
|||
</View> |
|||
); |
|||
} |
@ -0,0 +1,9 @@ |
|||
.container { |
|||
background: linear-gradient(180deg, #d7ebfa, #f5f5f5 30vh); |
|||
height: 100%; |
|||
|
|||
.wrapper { |
|||
box-sizing: border-box; |
|||
padding: 42px; |
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { Button, Form, Input } from 'antd-mobile'; |
|||
import Taro from '@tarojs/taro'; |
|||
import { loginAction } from '../../../../request/actions'; |
|||
import { useLocalStorageState } from 'ahooks'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function FormBar() { |
|||
const [form] = Form.useForm(); |
|||
const [, updateStorage] = useLocalStorageState('userInfo', {defaultValue: null}); |
|||
|
|||
const handleLogin = async () => { |
|||
try { |
|||
const values = await form.validateFields(); |
|||
const data = await loginAction(values); |
|||
updateStorage(data); |
|||
Taro.navigateTo({ url: '/pages/index/index' }); |
|||
} |
|||
catch (err) { |
|||
console.log('err: ', err); |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<Form form={form} className={styles.form}> |
|||
<Form.Item name='telephone' label='账号' rules={[{ required: true, message: '请输入手机号或电子邮箱' }]}> |
|||
<Input placeholder='请输入手机号或电子邮箱' /> |
|||
</Form.Item> |
|||
<Form.Item name='password' label='密码' rules={[{ required: true, message: '请输入密码' }]}> |
|||
<Input type='password' placeholder='请输入密码' /> |
|||
</Form.Item> |
|||
</Form> |
|||
<Button color='primary' className={styles.submit} onClick={handleLogin}>登录</Button> |
|||
<View className={styles.bar}> |
|||
<View className={styles.btn} onClick={() => Taro.navigateTo({ url: '/pages/register/index' })}>注册</View> |
|||
</View> |
|||
</View> |
|||
) |
|||
} |
@ -0,0 +1,38 @@ |
|||
.container { |
|||
background-color: #fff; |
|||
border-radius: 18px; |
|||
margin-top: 72px; |
|||
padding: 32px 32px 0 32px; |
|||
|
|||
.form { |
|||
:global { |
|||
.adm-list-body { |
|||
border-top: unset; |
|||
} |
|||
.adm-list-item { |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.bar { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
padding: 24px 72px; |
|||
|
|||
.btn { |
|||
font-size: 24px; |
|||
flex: 1; |
|||
text-align: center; |
|||
|
|||
&:first-child { |
|||
border-right: 1px solid #eee; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.submit { |
|||
width: 100%; |
|||
margin-top: 72px; |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
import { View } from '@tarojs/components'; |
|||
import { useLoad } from '@tarojs/taro'; |
|||
|
|||
import logoSrc from '/assets/icons/logo.png'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
export default function HeaderBar() { |
|||
|
|||
useLoad(() => { |
|||
console.log('Page loaded.') |
|||
}); |
|||
|
|||
return ( |
|||
<View className={styles.container}> |
|||
<View className={styles.row}> |
|||
<View className={styles.title}> |
|||
Hi Weaith |
|||
</View> |
|||
<View className={styles.subtitle}> |
|||
欢迎您来到融易诚 |
|||
</View> |
|||
</View> |
|||
<View className={styles.row2}> |
|||
<View className={styles.item}> |
|||
<img src={logoSrc} className={styles.logo} alt='logo' /> |
|||
<View className={styles.title}> |
|||
融易诚 |
|||
</View> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
) |
|||
} |