Initial commit

This commit is contained in:
Qingtian239113
2022-03-28 11:26:51 +08:00
commit a379f2c2da
37 changed files with 1689 additions and 0 deletions

12
.editorconfig Normal file
View File

@@ -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

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
dist/
deploy_versions/
.temp/
.rn_temp/
node_modules/
.DS_Store
.idea
/package-lock.json
/sandun-mobile.iml
yarn.lock
*.iml

10
babel.config.js Normal file
View File

@@ -0,0 +1,10 @@
// babel-preset-taro 更多选项和默认值:
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
module.exports = {
presets: [
['taro', {
framework: 'react',
ts: false
}]
]
}

57
build.js Normal file
View File

@@ -0,0 +1,57 @@
// 根据不同场景生成不同配置文件
const path = require('path');
const fs = require('fs');
function copyIt(from, to) {
fs.writeFileSync(to, fs.readFileSync(from));
}
function copyDir(from, to) {
fs.readdir(from, (err, files) => {
if (err) {
return;
}
if (!fs.existsSync(to)) {
fs.mkdirSync(to);
} else {
files.forEach((item, index) => {
let itemPath = path.join(from, item);
let tempFile = fs.statSync(itemPath);
if (tempFile.isFile()) {
copyIt(itemPath, path.join(to, item))
} else {
copyDir(itemPath, path.join(to, item));
}
});
}
})
}
let argv = process.argv[2] || "vast-box";
console.log('process.argv', argv);
// env配置文件
let fromEnvPath = path.join(__dirname, 'build_config', argv, '.env');
let toEnvPath = path.join(__dirname, '.env');
// assets目录
let fromConstantPath = path.join(__dirname, 'build_config', argv, 'assets');
let toConstantPath = path.join(__dirname, 'src', 'assets');
// 小程序可配置参数文件
let fromConfigPath = path.join(__dirname, 'build_config', argv, 'config.js');
let toConfigPath = path.join(__dirname, 'src', 'config', 'config.js');
// 小程序工程配置
let fromProjectPath = path.join(__dirname, 'build_config', argv, 'project.config.json');
let toProjectPath = path.join(__dirname, 'project.config.json');
let copyArr = [[fromEnvPath, toEnvPath], [fromConstantPath, toConstantPath], [fromConfigPath, toConfigPath], [fromProjectPath, toProjectPath]];
//拷贝文件到对应目录下
copyArr.map((item, idx) => {
if (idx == 1) {
copyDir(item[0], item[1])
} else {
copyIt(item[0], item[1])
}
});

8
config/dev.js Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
env: {
NODE_ENV: '"development"'
},
defineConstants: {},
mini: {},
h5: {}
};

73
config/index.js Normal file
View File

@@ -0,0 +1,73 @@
const config = {
projectName: 'template-taro',
date: '2021-5-21',
designWidth: 750,
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2
},
sourceRoot: 'src',
outputRoot: 'dist',
plugins: [],
copy: {
patterns: [],
options: {}
},
alias: {
"@": require('path').resolve(__dirname, "../", "src"),
},
framework: 'react',
mini: {
enableExtract: true,
optimizeMainPackage: {
enable: true,
},
miniCssExtractPluginOption: {
ignoreOrder: true,
},
postcss: {
pxtransform: {
enable: true,
config: {}
},
url: {
enable: true,
config: {
limit: 1024 // 设定转换尺寸上限
}
},
cssModules: {
enable: true, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
}
},
h5: {
publicPath: '/',
staticDirectory: 'static',
postcss: {
autoprefixer: {
enable: true,
config: {}
},
cssModules: {
enable: true, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
},
}
};
module.exports = function (merge) {
if (process.env.NODE_ENV === 'development') {
return merge({}, config, require('./dev'))
}
return merge({}, config, require('./prod'))
};

17
config/prod.js Normal file
View File

@@ -0,0 +1,17 @@
module.exports = {
env: {
NODE_ENV: '"production"'
},
defineConstants: {},
mini: {},
h5: {
/**
* 如果h5端编译后体积过大可以使用webpack-bundle-analyzer插件对打包体积进行分析。
* 参考代码如下:
* webpackChain (chain) {
* chain.plugin('analyzer')
* .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
* }
*/
}
};

18
global.d.ts vendored Normal file
View File

@@ -0,0 +1,18 @@
/// <reference path="node_modules/@tarojs/plugin-platform-weapp/types/shims-weapp.d.ts" />
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'
}
}

12
jsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}

73
package.json Normal file
View File

@@ -0,0 +1,73 @@
{
"name": "taro-demo",
"version": "1.0.0",
"private": true,
"description": "",
"templateInfo": {
"name": "default",
"typescript": true,
"css": "less"
},
"scripts": {
"build:weapp": "taro build --type weapp",
"build:vastbox": "node build.js vast-box && taro build --type weapp",
"build:xihu": "node build.js xihu && 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:quickapp": "taro build --type quickapp",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:vastbox": "node build.js vast-box && npm run build:weapp -- --watch",
"dev:xihu": "node build.js xihu && 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:quickapp": "npm run build:quickapp -- --watch",
"dev:weapp-pro": "NODE_ENV=production taro build --type weapp --watch"
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"author": "",
"dependencies": {
"@babel/runtime": "^7.7.7",
"@tarojs/components": "3.3.18",
"@tarojs/react": "3.3.18",
"@tarojs/runtime": "3.3.18",
"@tarojs/taro": "3.3.18",
"dayjs": "1.10.5",
"react": "17.0.2",
"react-dom": "17.0.2",
"taro-poster-render": "^2.0.5",
"taro-ui": "3.0.0-alpha.10",
"three-platformize": "^1.133.3"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@tarojs/mini-runner": "3.3.18",
"@tarojs/webpack-runner": "3.3.18",
"@types/webpack-env": "^1.13.6",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"babel-preset-taro": "3.3.18",
"dayjs": "1.10.5",
"dotenv": "^10.0.0",
"eslint": "^6.8.0",
"eslint-config-taro": "3.3.18",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-react-hooks": "^4.2.0",
"stylelint": "9.3.0",
"typescript": "^4.1.0"
}
}

14
project.config.json Normal file
View File

@@ -0,0 +1,14 @@
{
"miniprogramRoot": "./dist",
"projectname": "template-taro",
"description": "",
"appid": "xxxxxxxx",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram",
"checkSiteMap": false
}

53
src/app.config.js Normal file
View File

@@ -0,0 +1,53 @@
const config = require('./config/config');
export default {
pages: [
// tab切换
"pages/tabs/home",
"pages/tabs/mine",
"pages/login/index",
"pages/common/webview",
// home
"pages/home/pay",
"pages/home/payDetail",
"pages/home/news/index",
"pages/home/owner/index",
// mine
"pages/mine/set/index",
"pages/mine/set/contact",
"pages/mine/set/agreement",
"pages/mine/order/index",
"pages/mine/give/index",
"pages/mine/receive",
],
subpackages: [
{
root: "pages_goods/",
pages: [
// 藏品详情
"goods/detail",
]
},
],
plugins: {},
window: {
backgroundColor: "#000506",
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#000506',
navigationBarTextStyle: 'white',
backgroundColorTop: "#000506",
backgroundColorBottom: "#000506",
...config.app.window,
},
tabBar: {
custom: true,
list: [
{pagePath: "pages/tabs/home", text: "首页"},
{pagePath: "pages/tabs/mine", text: "我的"}
]
}
}

42
src/app.js Normal file
View File

@@ -0,0 +1,42 @@
import {Component} from 'react'
import './app.less'
import 'taro-ui/dist/style/index.scss'
import Taro from '@tarojs/taro';
import HttpUtils from "@/https/HttpUtils";
class App extends Component {
componentDidMount() {
const updateManager = Taro.getUpdateManager();
updateManager.onCheckForUpdate(res => {
console.log(res);
});
updateManager.onUpdateReady(() => {
Taro.showModal({
title: '更新提示', content: "新版本已经准备好,是否重启应用?",
success: res => {
if (res.confirm) {
updateManager.applyUpdate()
}
}
})
});
}
componentDidShow() {
}
componentDidHide() {
}
componentDidCatchError() {
}
// this.props.children 是将要会渲染的页面
render() {
return this.props.children
}
}
export default App

66
src/app.less Normal file
View File

@@ -0,0 +1,66 @@
@import './assets/less/constant.less';
page {
color: white;
background-color: @bgColor;
font-family: serif;
}
.primaryColor {
color: @primaryColor;
}
.empty {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 40vh;
font-size: 24px;
color: #888888;
}
.divider {
width: 100%;
height: 1px;
background-image: linear-gradient(to right, rgba(#979797, .32) 0%, rgba(#979797, .32) 50%, transparent 0%);
background-size: 20px 1px;
background-repeat: repeat-x;
}
.globalPadding {
padding: 20px;
}
.primaryBtn {
background: @primaryColor;
color: white;
font-size: 30px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
height: 84px;
}
.at-modal__container {
width: calc(100vw - 88px) !important;
background: transparent !important;
}
.at-modal__overlay {
background: rgba(0, 0, 0, 0.8) !important;
}
.wx-swiper-dot {
width: 16px !important;
height: 16px !important;
background: rgba(255, 255, 255, .3) !important;
}
.wx-swiper-dot-active {
.wx-swiper-dot;
width: 36px !important;
border-radius: 16px !important;
background: linear-gradient(@primaryTopColor, @primaryColor) !important;
}

View File

@@ -0,0 +1,40 @@
import React from "react";
import less from './index.module.less';
import Taro from "@tarojs/taro";
import Flex from "@/components/Flex";
import config from "@/config/config";
import GcImage from "@/components/GcImage";
const CustomTabBar = ({tabIdx = 1}) => {
///////////////////////////////////////// 逻辑方法
/**
* 切换tab
* @param idx
*/
const switchTab = (idx) => {
Taro.switchTab({url: config.app.tabbar.list[idx].url}).then(res => {
})
};
///////////////////////////////////////// 页面渲染
return <Flex className={less.tabbar} style={{backgroundColor: config.app.tabbar?.bgColor || "blank"}}>
{config.app.tabbar?.list?.map((item, idx) => {
return <Flex
direction={'column'} itemGrow={1} className={less.tab} key={item.id}
justify={'center'} alignItems={'center'}
onClick={() => switchTab(idx)}
>
<GcImage className={less.icn} src={tabIdx == item.id ? item.selectedIcon : item.icon}/>
<Flex
className={less.dot} style={{color: tabIdx == item.id ? item.selectedColor : item.color}}
>{item.title}</Flex>
</Flex>
})}
</Flex>
};
export default CustomTabBar;

View File

@@ -0,0 +1,31 @@
@import 'build_config/xihu/assets/less/constant.less';
.tabbar {
width: 100vw;
color: white;
height: calc(130px + env(safe-area-inset-bottom));
position: fixed;
bottom: 0;
left: 0;
z-index: 100;
padding-bottom: env(safe-area-inset-bottom);
.tab {
width: 0;
padding: 24px;
.icn {
width: 44px;
height: 44px;
}
.dot {
margin-top: 4px;
border-radius: 10px;
font-size: 24px;
}
}
}

View File

@@ -0,0 +1,17 @@
import React from "react";
import Flex from "@/components/Flex";
import GcImage from "@/components/GcImage";
import less from './index.module.less';
const EmptyView = ({type = "def", tips = "暂无记录"}) => {
let icnSrc = config.page.common.emptyObj[type] || config.page.common.emptyObj.def;
return <Flex direction={"column"} className={less.empty} alignItems={"center"}>
<GcImage src={icnSrc} className={less.icn} mode={'aspectFit'}/>
<Flex>{tips}</Flex>
</Flex>
};
export default EmptyView;

View File

@@ -0,0 +1,13 @@
.empty {
width: 100%;
margin: 136px 0;
font-size: 28px;
color: rgba(255, 255, 255, 0.7);
.icn {
width: 302px;
height: 276px;
margin: 0 auto 46px;
}
}

View File

@@ -0,0 +1,50 @@
import React from "react";
import {View} from "@tarojs/components";
import less from './index.module.less';
const Flex = ({
direction, wrap, justify, alignItems, itemOrder, itemSelf, itemGrow,
className, style, children, ...restProps
}) => {
const newStyle = {...style};
if (direction) {
newStyle['flexDirection'] = direction;
newStyle['WebkitFlexDirection'] = direction;
}
if (wrap) {
newStyle['flexWrap'] = wrap;
newStyle['WebkitFlexWrap'] = wrap;
}
if (justify) {
newStyle['justifyContent'] = justify;
newStyle['WebkitJustifyContent'] = justify;
}
if (alignItems) {
newStyle['alignItems'] = alignItems;
newStyle['WebkitAlignItems'] = alignItems;
}
if (itemSelf) {
newStyle['alignSelf'] = itemSelf;
newStyle['WebkitAlignSelf'] = itemSelf;
}
if (itemOrder) {
newStyle['order'] = itemOrder;
}
if (itemGrow) {
newStyle['flexGrow'] = itemGrow;
newStyle['WebkitFlexGrow'] = itemGrow;
}
///////////////////////////////////////// 页面渲染
return <View
className={less.flex + " " + className} style={newStyle}
{...restProps}
>{children}</View>
};
export default Flex

View File

@@ -0,0 +1,7 @@
.flex {
display: flex;
flex-direction: row;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}

View File

@@ -0,0 +1,37 @@
import React from "react";
import Taro from '@tarojs/taro';
import Common from "@/utils/Common";
import {Image, View} from "@tarojs/components";
import HttpUtils from "@/https/HttpUtils";
const GcImage = ({src, preview, empty = true, className, mode, onClick, children}) => {
const httpSrc = HttpUtils.getImageUrl(src);
///////////////////////////////////////// 逻辑方法
const onImgClick = (e) => {
if (!preview) {
onClick && onClick();
return
}
e.stopPropagation();
Taro.previewImage({
urls: [httpSrc], current: httpSrc
})
};
if (Common.isEmpty(src)) {
return <View className={empty ? null : className}/>
}
return <Image
src={httpSrc} className={className} mode={mode || "aspectFill"}
onClick={onImgClick}
>{children}</Image>
};
export default GcImage;

View File

@@ -0,0 +1,30 @@
import React from "react";
import Taro from '@tarojs/taro';
import less from './index.module.less';
import '@tarojs/taro/html.css'
import HttpUtils from "@/https/HttpUtils";
import {View} from "@tarojs/components";
Taro.options.html.transformElement = (el) => {
if (el.nodeName === "image") {
const path = HttpUtils.imageUrl(el.props.src);
el.setAttribute("src", path);
el.setAttribute("mode", "widthFix");
}
return el;
};
const GcRichText = ({html = "", className, style, ...restProps}) => {
///////////////////////////////////////// 逻辑方法
return <View className={less.rich + " " + className} style={style} {...restProps}>
<View className="taro_html" dangerouslySetInnerHTML={{__html: html}}/>
</View>
};
export default GcRichText;

View File

@@ -0,0 +1,11 @@
.rich {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
img, image {
width: 100% !important;
object-fit: contain;
}
}

View File

@@ -0,0 +1,20 @@
import React from "react";
import Flex from "@/components/Flex";
import config from "@/config/config";
import Common from "@/utils/Common";
/**
* 页面
* @constructor
*/
const PageContainer = ({bgUrlColor, children, ...restProps}) => {
return <Flex
direction={'column'} style={Common.bgUrlColor(bgUrlColor || config?.page?.common?.bgUrlColor)}
{...restProps}
>
{children}
</Flex>
};
export default PageContainer;

21
src/config/config.js Normal file
View File

@@ -0,0 +1,21 @@
const home = {
bgUrl: ''
};
const http = {
develop: {
serverUrl: "https://vast-box-api-test.tmp.vastchain.ltd",
imageUrl: "https://vast-box-api-test.tmp.vastchain.ltd",
cdnUrl: "",
},
release: {
serverUrl: "https://vast-box-api.app.vastchain.ltd",
imageUrl: "https://vast-box-api.app.vastchain.ltd",
cdnUrl: "https://vast-box-cdn.vastchain.cn",
}
};
module.exports = {
http,
home,
};

209
src/config/config_test.js Normal file
View File

@@ -0,0 +1,209 @@
/**
* app相关配置
* @param terminalId 小程序id
* @param deptId 服务商id
* @type {{}}
*/
const app = {
terminalId: "1480731682234568704",
deptId: "101",
appName: "西湖一号数字藏品平台",
appLogo: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/app_logo.png",
copyright: "元镜联盟链提供技术支持",
/**
* 窗口设置
*/
window: {
backgroundColor: "#0B1426",
navigationBarBackgroundColor: "#0B1426",
backgroundColorTop: "#0B1426",
backgroundColorBottom: "#0B1426",
},
/**
* tabbar配置
*/
tabbar: {
bgColor: "#171A20",
list: [
{
id: 1, title: "首页",
color: "#999999", selectedColor: "white", url: "/pages/tabs/home",
icon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/tab_index_gray.png",
selectedIcon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/tab_index_active.png"
},
{
id: 2, title: "我的",
color: "#999999", selectedColor: "white", url: "/pages/tabs/mine",
icon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/tab_mine_gray.png",
selectedIcon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/tab_mine_active.png"
},
],
},
};
/**
* 网络请求配置
* @param serverUrl API地址
* @param imageUrl 图片地址。如果没有则使用serverUrl
* @param cdnUrl cdn地址。如果没有则使用imageUrl
*/
const http = {
develop: {
serverUrl: "https://saas-server-api-test.tmp.vastchain.ltd",
imageUrl: "https://saas-server-api-test.tmp.vastchain.ltd",
cdnUrl: "",
},
trial: {
serverUrl: "https://saas-server-api-test.tmp.vastchain.ltd",
imageUrl: "https://saas-server-api-test.tmp.vastchain.ltd",
cdnUrl: "",
},
release: {
serverUrl: "https://saas-server-api-test.tmp.vastchain.ltd",
imageUrl: "https://saas-server-api-test.tmp.vastchain.ltd",
cdnUrl: "",
}
};
/**
* 页面相关配置
* @type {{home: {bgurl: string}}}
*/
const page = {
common: {
bgUrlColor: 'https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/common_bg.png',
defaultAvatar: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/avatar_default.png",
emptyObj: {
def: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/empty_box.png",
mine: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/empty_box_mine.png",
},
arrowLeft: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/white_arrow.png",
copyright: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/copyright.png",
shareIcon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/share_btn.png",
addressIcon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/order_address.png",
},
/**
* 首页配置
*/
home: {
bgUrlColor: '#0B1426'
},
/**
* 我的页面
*/
mine: {
menu: {
bgUrlColor: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/setting_menu_bg.png",
data: [
{
id: '1', name: '领取', url: '/pages/mine/receive', login: true,
img: 'https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/mine_receive.png'
},
{
id: '2', name: '我的订单', url: "/pages/mine/order/index", login: true,
img: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/mine_order.png"
},
{
id: '3', name: '转赠列表', url: '/pages/mine/give/index', login: true,
img: 'https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/mine_zz.png'
},
{
id: '4', name: '设置', url: '/pages/mine/set/index', login: false,
img: 'https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/mine_set.png'
}
]
},
},
setting: {
menu: [
{
id: 1, title: "用户协议", go: "/pages/mine/set/agreement", customStyle: {background: '#212838'},
icon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/mine_user.png"
},
{
id: 2, title: "关于我们", go: "/pages/mine/set/contact", customStyle: {background: "#212838"},
icon: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/white_circle_question.png",
content: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/about_us.png",
},
]
},
goods: {
titleUrl: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/goods_detail_title.png",
lamplightUrl: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/goods_detail_lamplight.png",
bgUrlColor: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/goods_detail_bg.png",
detail: {
author: {
customStyle: {
color: "#F0DCC0",
backgroundColor: "#212838",
}
},
owner: {
customStyle: {
backgroundColor: "#212838",
}
},
cpbq: {
titleUrl: "",
customStyle: {
color: "white",
backgroundColor: "#212838",
}
},
cpjs: {
titleUrl: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/goods_introduction.png",
customStyle: {
color: "#F0DCC0",
backgroundColor: "#212838",
}
},
cpxj: {
titleUrl: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/goods_detail.png",
customStyle: {
color: "#F0DCC0",
backgroundColor: "#212838",
}
},
gmxz: {
titleUrl: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/goods_detail_purchase.png",
customStyle: {
color: "#F0DCC0",
backgroundColor: "#212838",
}
}
},
share: {
bg: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/share_bg.png",
qrcode: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/share_qrcode.png",
shadow: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/share_shadow.png",
hashColor: "#717983",
}
},
/**
* 领取页面
*/
receive: {
bgUrlColor: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/receive_bg.png",
receiveTitle: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/receive_digital_collection.png",
holder: "https://static-test.tmp.vastchain.ltd/saas-front-admin/static/mini/receive_holder.png",
}
};
module.exports = {
app,
http,
page
};

141
src/https/HttpUtils.js Normal file
View File

@@ -0,0 +1,141 @@
import Taro from '@tarojs/taro';
/**
* 请求地址
* @type {{}}
*/
const RequestUrl = {
// 开发版
"develop": {
serverUrl: "https://xxxx",
mini: "https://xxxx",
imageUrl: "https://xxxx",
},
// 体验版
"trial": {
serverUrl: "https://xxxx",
mini: "https://xxxx",
imageUrl: "https://xxxx",
},
// 正式版
"release": {
serverUrl: "https://xxxx",
mini: "https://xxxx",
imageUrl: "https://xxxx",
},
};
/**
* 获取小程序的运行环境
*/
const getMiniEnv = () => {
if (!Taro.canIUse("getAccountInfoSync")) {
return "release";
}
return Taro.getAccountInfoSync().miniProgram.envVersion;
};
/**
* 获取API地址
* 正式环境时使用正式地址,其他环境使用测试地址
*/
const getServerUrl = () => {
return RequestUrl[getMiniEnv()].serverUrl;
};
/**
* 发起网络请求
* @param url
* @param method
* @param options
*/
const request = (url, method, options) => {
options.header = {
...options.header, token: Taro.getStorageSync("token"),
};
return Taro.request({
url: joinUrl(url), method, ...options, success: (res => {
if (res.data.code === 401) {
Taro.removeStorageSync('token');
Taro.switchTab({url: "/pages/index/index"})
}
})
}).then(res => res.data)
};
/**
* 加入url
* @param path
*/
const joinUrl = (path) => {
if (path?.startsWith("http")) {
return path;
}
return getServerUrl() + path;
};
/**
* 图片地址构造
* @param path
* @return {string|*}
*/
const getImageUrl = (path) => {
if (path?.startsWith("http")) {
return path;
}
if (path?.startsWith("data:")) {
return path;
}
if (path?.startsWith("mini://")) {
return RequestUrl[getMiniEnv()].mini + path.substr(6)
}
return joinUrl(path);
};
/**
* 提示返回信息
* @param res
*/
const showRespError = (res = {}) => {
if (res.code != 200) {
Taro.showToast({title: res.msg, icon: 'none'})
}
};
export default {
request,
joinUrl,
getImageUrl,
showRespError,
/**
* 发起get请求
* @param url
* @param data
* @param options
* @return {*}
*/
get: (url, data, options) => {
return request(url, "GET", {data, ...options})
},
/**
* 发起post请求
* @param url
* @param data
* @param options
* @return {*}
*/
post: (url, data, options) => {
return request(url, "POST", {data, ...options})
},
}

19
src/index.html Normal file
View File

@@ -0,0 +1,19 @@
<!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></title>
<script>
!function(x){function w(){var v,u,t,tes,s=x.document,r=s.documentElement,a=r.getBoundingClientRect().width;if(!v&&!u){var n=!!x.navigator.appVersion.match(/AppleWebKit.*Mobile.*/);v=x.devicePixelRatio;tes=x.devicePixelRatio;v=n?v:1,u=1/v}if(a>=640){r.style.fontSize="40px"}else{if(a<=320){r.style.fontSize="20px"}else{r.style.fontSize=a/320*20+"px"}}}x.addEventListener("resize",function(){w()});w()}(window);
</script>
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@@ -0,0 +1,6 @@
export default {
navigationBarTitleText: '',
enablePullDownRefresh: true,
enableShareAppMessage: true,
navigationStyle: "custom",
}

110
src/pages/tabs/home.js Normal file
View File

@@ -0,0 +1,110 @@
import React, {useEffect, useState} from "react";
import Flex from "@/components/Flex";
import {Swiper, SwiperItem} from "@tarojs/components";
import HttpUtils from "@/https/HttpUtils";
import GcImage from "@/components/GcImage";
import less from './home.module.less';
import Taro, {usePullDownRefresh, useReachBottom} from '@tarojs/taro'
import RouteGo from "@/route/RouteGo";
import CustomTabBar from "@/components/CustomTabBar";
import PageContainer from "@/components/PageContainer";
export default () => {
const [bannerList, setBannerList] = useState([]);
const [pageNo, setPageNo] = useState(1);
const [pageSize] = useState(100);
const [lastTotal, setLastTotal] = useState(0);
const [goodsList, setGoodsList] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
getBannerList();
}, [1]);
useEffect(() => {
getGoodsList();
}, [pageNo]);
usePullDownRefresh(() => {
getBannerList();
if (pageNo == 1) {
getGoodsList();
} else {
setPageNo(1);
}
});
useReachBottom(() => {
if (lastTotal >= pageSize) {
setPageNo(pageNo + 1);
}
});
///////////////////////////////////////// 逻辑方法
/**
* 轮播图
*/
const getBannerList = () => {
HttpUtils.get("/customer/news_body/list").then(res => {
if (res.code == 200) {
let tempDates = res.rows;
if (tempDates.length == 1) {
tempDates = tempDates.concat(tempDates).concat(tempDates);
} else if (tempDates.length == 2) {
tempDates = tempDates.concat(tempDates);
}
setBannerList(tempDates);
}
})
};
const getGoodsList = () => {
HttpUtils.get("/customer/release/item/list", {pageSize, pageNum: pageNo}).then(res => {
if (res.code == 200) {
if (pageNo == 1) {
setGoodsList(res.rows);
} else {
setGoodsList([...goodsList].concat(res.rows));
}
}
setLastTotal(res.total);
setLoading(false);
Taro.stopPullDownRefresh();
})
};
///////////////////////////////////////// 页面渲染
if (loading) {
return "";
}
return <PageContainer className={less.home}>
<Flex direction={'column'} className={less.content}>
<Swiper
className={less.swiper} circular nextMargin={'192rpx'} snapToEdge={true}
indicatorDots={true} indicatorColor={'#FFFFFF'}
autoplay={true}
>
{bannerList.map(item => {
return <SwiperItem key={item.id}>
<GcImage
className={less.banner} alt={''} src={item.img}
onClick={() => RouteGo.byBanner(item)}/>
</SwiperItem>
})}
</Swiper>
</Flex>
<CustomTabBar tabIdx={1}/>
</PageContainer>
}

View File

@@ -0,0 +1,25 @@
.home {
width: 100vw;
background-size: 101% 100%;
background-repeat: no-repeat;
padding-bottom: calc(130px + env(safe-area-inset-bottom));
.content {
width: 100%;
.swiper {
height: 426px;
.banner {
width: calc(100% - 56px);
margin: 12px 0 0 56px;
border-radius: 12px;
height: 356px;
}
}
}
}

View File

@@ -0,0 +1,5 @@
export default {
navigationBarTitleText: '我的',
enablePullDownRefresh: true,
enableShareAppMessage: true,
}

131
src/pages/tabs/mine.js Normal file
View File

@@ -0,0 +1,131 @@
import React, {useRef, useState} from "react";
import Flex from "@/components/Flex";
import {Text, View} from "@tarojs/components";
import less from "./mine.module.less";
import Taro, {usePullDownRefresh, useShareAppMessage, useDidShow} from "@tarojs/taro";
import HttpUtils from "@/https/HttpUtils";
import GcImage from "@/components/GcImage";
import RouteGo from "@/route/RouteGo";
import EmptyView from "@/components/EmptyView";
import Common from "@/utils/Common";
import config from "@/config/config";
import CustomTabBar from "@/components/CustomTabBar";
export default () => {
const headRef = useRef();
const [itemType] = useState("C");
const [myGoods, setMyGoods] = useState([]);
useDidShow(() => {
getCollectList();
headRef?.current?.getUserInfo();
// EventManager.order.addOrderChange("mine", getCollectList);
// EventManager.login.addLoginChange("mine", () => {
// headRef?.current?.getUserInfo();
// getCollectList();
// });
});
usePullDownRefresh(() => {
getCollectList();
});
useShareAppMessage(() => {
let shareMessage = config.page.common.shareMessage;
return {
...shareMessage, imageUrl: HttpUtils.imageUrl(shareMessage.imageUrl)
};
});
///////////////////////////////////////// 逻辑方法
/**
* 获取自己获得的藏品
*/
const getCollectList = () => {
HttpUtils.get("/customer/self/item/list", {itemType}).then(res => {
if (res.code == 200) {
setMyGoods(res.rows);
}
Taro.stopPullDownRefresh();
})
};
///////////////////////////////////////// 页面渲染
/**
* 菜单
*/
const Menu = () => {
return <Flex className={less.frame} style={Common.bgUrlColor(config?.page?.mine?.menu?.bgUrlColor)}>
{config.page.mine?.menu.data?.map((item) => {
return <Flex
key={item.id} direction={'column'} className={less.menuItem}
justify={'center'} alignItems={'center'}
onClick={() => {
if (!item.login) {
Taro.navigateTo({url: item.url})
} else {
RouteGo.checkLogin().then(res => {
Taro.navigateTo({url: item.url})
})
}
}}
>
<GcImage src={item.img} mode={'aspectFit'}/>
<View>{item.name}</View>
</Flex>
})}
</Flex>
};
/**
* 藏品
*/
const renderGoods = () => {
return <Flex direction={"column"} className={less.collection}>
<Flex justify={'flex-start'} className={less.title}>
<Flex className={less.title}>我的数字藏品 (<Text>{myGoods.length}</Text>)</Flex>
</Flex>
{(myGoods.length == 0 ? <EmptyView type={'mine'} tips={'暂无藏品'}/> :
<Flex wrap={'wrap'}>
{myGoods.map((item) => {
return <Flex
key={item.id} className={less.myItem} direction={'column'}
onClick={() => RouteGo.goodsDetail(item.objectType.id, itemType, item.id)}
>
<GcImage src={item.objectType.img} className={less.picture} mode={'aspectFill'}/>
<Flex className={less.picture_detail} direction={'column'}>
<Text className={less.name}>{item.objectType.name}</Text>
<Flex className={less.tags} wrap={'wrap'}>
{item.objectType.itemTags?.map(stringValue => {
return <Text className={less.tagsItem}>{stringValue}</Text>
})}
</Flex>
</Flex>
</Flex>
})}
</Flex>
)}
</Flex>;
};
return <Flex direction={'column'} className={less.mine}>
<Flex direction={'column'} className={less.content}>
<Menu/>
{renderGoods()}
</Flex>
<CustomTabBar tabIdx={2}/>
</Flex>
}

View File

@@ -0,0 +1,153 @@
@import "src/assets/less/constant.less";
.mine {
width: 100vw;
padding-bottom: calc(130px + env(safe-area-inset-bottom));
.content {
margin-bottom: 20px;
.frame {
background-repeat: no-repeat;
background-size: 100%;
flex-direction: row;
width: calc(100% - 92px);
justify-content: space-around;
align-items: center;
margin: 0 46px 46px;
font-size: 24px;
line-height: 34px;
color: #FFFFFF;
padding: 48px 0;
border-radius: 24px;
.menuItem {
image {
width: 45px;
height: 45px;
margin-bottom: 14px;
}
}
}
.collection {
width: calc(100% - 70px);
margin: 0 35px;
.title {
font-size: 32px;
font-weight: bold;
color: white;
margin: 0 11px;
text {
color: @mineGoodsNumColor;
}
}
.lingqu {
width: 100%;
margin: 136px 0;
button {
background: #2A49C8;
padding: 0 36px;
border-radius: 24px;
color: white;
height: 74px;
line-height: 74px;
font-size: 28px;
}
}
.myItem {
width: calc((100% - 44px) / 2);
margin: 22px 11px 0;
position: relative;
background: @goodsBg;
border-radius: 46px;
.picture {
width: 100%;
height: 380px;
border-radius: 46px;
}
.picture_detail {
margin: 0 12px 12px;
width: calc(100% - 24px);
border-radius: 46px;
padding: 24px 12px;
.name {
font-weight: 600;
color: #FFFFFF;
font-size: 32px;
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tags {
margin-top: 12px;
.tagsItem {
background-color: @goodsTagBg;
padding: 4px 12px;
font-size: 20px;
color: @goodsTagColor;
border-radius: 32px;
margin-right: 24px;
}
}
}
.limited_box {
margin-top: 16px;
.limited {
display: flex;
background: #FFF72F;
border-radius: 24px 0 0 24px;
position: relative;
width: 81px;
height: 36px;
margin-right: 71px;
font-size: 22px;
font-weight: 400;
color: #000506;
line-height: 32px;
justify-content: center;
align-items: center;
}
.number {
display: flex;
justify-content: center;
align-items: center;
background: #2C55FF;
border-radius: 24px;
position: absolute;
width: 86px;
height: 36px;
font-size: 22px;
font-weight: 400;
color: #FFFFFF;
line-height: 32px;
}
}
}
}
}
}

82
src/route/RouteGo.js Normal file
View File

@@ -0,0 +1,82 @@
import Taro from "@tarojs/taro";
import Common from "@/utils/Common";
import HttpUtils from "@/https/HttpUtils";
/**
* 藏品详情
* @param id
* @param itemType id类型
* @param drawId 领取记录id
* @param nfcCode 往领取页面
*/
const goodsDetail = (id, itemType, drawId = "", nfcCode = "",) => {
let params = {id, itemType, drawId, nfcCode};
Taro.navigateTo({
url: "/pages_goods/goods/detail?" + Object.keys(params).map(it => `${it}=${params[it]}`).join("&"),
})
};
/**
* 跳转到webview打开
*/
const goWebView = (httpUrl) => {
httpUrl = HttpUtils.joinUrl(httpUrl);
// 检查一下是否需要token
if (httpUrl.indexOf("TOKEN") != -1) {
checkLogin().then(tokenVal => {
httpUrl = httpUrl.replace("TOKEN", tokenVal);
Taro.navigateTo({url: "/pages/common/webview?src=" + encodeURIComponent(httpUrl)});
});
} else {
Taro.navigateTo({url: "/pages/common/webview?src=" + encodeURIComponent(httpUrl)});
}
};
/**
* 检查等
* @return {Promise<unknown>}
*/
const checkLogin = () => {
return new Promise((resolve, reject) => {
let phoneVal = Taro.getStorageSync("phone");
if (Common.isEmpty(phoneVal)) {
Taro.navigateTo({url: "/pages/login/index"});
reject();
} else {
resolve(Taro.getStorageSync("token"));
}
})
};
/**
* 返回
*/
const goBack = (delta = -1) => {
Taro.navigateBack({delta})
};
/**
* 路由之间的跳转
*/
export default {
goodsDetail,
goWebView,
checkLogin,
goBack,
/**
* 通过banner跳转
* @param banner
*/
byBanner: (banner) => {
if (!Common.isEmpty(banner.link)) {
Taro.navigateTo({url: "/pages/common/webview?src=" + encodeURIComponent(banner.link)});
} else {
Taro.navigateTo({url: "/pages/home/news/index?id=" + banner.id});
}
},
}

53
src/utils/Common.js Normal file
View File

@@ -0,0 +1,53 @@
import HttpUtils from "@/https/HttpUtils";
/**
* 是否为空
* @param value
* @return {boolean}
*/
const isEmpty = (value) => {
return value === '' || value === undefined || value === null;
};
/**
* 计算平均配速
* @param km
* @param hour
* @param minutes
* @param seconds
*/
const getRunAvg = (km = 1, hour = 0, minutes = 0, seconds = 0) => {
let allSeconds = hour * 60 * 60 + minutes * 60 + seconds;
let kmSeconds = allSeconds / km;
return (kmSeconds / 60).toFixed(0) + "'" + (kmSeconds % 60).toFixed(0) + "''";
};
/**
* 讲¥分转换为¥元
*/
const handlePrice = (yuan = 0) => {
const rlt = Number(yuan).toFixed(2);
if (!isEmpty(rlt)) {
return rlt;
} else {
return '未有价格';
}
};
/**
* 处理urlColor
*/
const bgUrlColor = (val = "") => {
if (val.startsWith("#")) {
return {backgroundColor: val};
}
return {
backgroundImage: "url('" + HttpUtils.imageUrl(val) + "')",
backgroundRepeat: "no-repeat", backgroundSize: "100%"
};
};
export default {
isEmpty, getRunAvg, handlePrice, bgUrlColor
}

12
webpack.config.js Normal file
View File

@@ -0,0 +1,12 @@
/**
* 不是真实的 webpack 配置,仅为兼容 webstorm 和 intellij idea 代码跳转
* ref: https://github.com/umijs/umi/issues/1109#issuecomment-423380125
*/
module.exports = {
resolve: {
alias: {
'@': require('path').resolve(__dirname, 'src'),
},
},
};