├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmrc ├── README.md ├── config ├── dev.js ├── index.js └── prod.js ├── global.d.ts ├── package-lock.json ├── package.json ├── project.config.json ├── src ├── app.styl ├── app.tsx ├── components │ └── garbageTypeItem │ │ ├── alipay.styl │ │ ├── index.styl │ │ └── index.tsx ├── custom-tab-bar │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── imgs │ ├── icon_album.png │ ├── icon_back.png │ ├── icon_camera_search.png │ ├── icon_change.png │ ├── icon_classification.png │ ├── icon_classification_HL.png │ ├── icon_clear_history.png │ ├── icon_contact.png │ ├── icon_contact_gray.png │ ├── icon_graphic.png │ ├── icon_graphic_HL.png │ ├── icon_home.png │ ├── icon_home_HL.png │ ├── icon_money.png │ ├── icon_notice.png │ ├── icon_share.png │ ├── icon_test.png │ └── icon_test_HL.png ├── index.html ├── pages │ ├── camera │ │ ├── index.styl │ │ └── index.tsx │ ├── classification │ │ ├── alipay.styl │ │ ├── index.styl │ │ └── index.tsx │ ├── detail │ │ ├── index.styl │ │ └── index.tsx │ ├── graphic │ │ ├── index.styl │ │ └── index.tsx │ ├── index │ │ ├── index.styl │ │ └── index.tsx │ ├── qrCode │ │ ├── index.styl │ │ └── index.tsx │ ├── search │ │ ├── index.styl │ │ └── index.tsx │ ├── start │ │ ├── index.styl │ │ └── index.tsx │ └── test │ │ ├── index.styl │ │ └── index.tsx └── utils │ └── util.tsx └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["taro"], 3 | "rules": { 4 | "no-unused-vars": ["error", { "varsIgnorePattern": "Taro" }], 5 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx"] }] 6 | }, 7 | "parser": "@typescript-eslint/parser", 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "jsx": true 11 | }, 12 | "useJSXTextNode": true, 13 | "project": "./tsconfig.json" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | deploy_versions/ 3 | .temp/ 4 | .rn_temp/ 5 | node_modules/ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://r.cnpmjs.org 2 | disturl=https://r.cnpmjs.org/node 3 | sass_binary_site=https://r.cnpmjs.org/node-sass/ 4 | fse_binary_host_mirror=https://r.cnpmjs.org/fsevents 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # garbageClassification 2 | 垃圾分类小程序 3 | 4 | ## 微信小程序名:垃圾的小窝 5 | 6 | ## 微信小程序码 7 | ![微信小程序码](https://i.screenshot.net/kkq4zsp) 8 | 9 | ## taro文档 10 | [文档地址](https://taro-docs.jd.com/taro/docs/README.html) 11 | 12 | ## 项目运行 13 | 14 | ``` 15 | npm install 16 | 17 | npm run dev:weapp 18 | 19 | 打开开发者工具运行 20 | ``` 21 | 22 | 有问题可查看文档,如若自己创建项目需要安装cli工具,如下: 23 | 24 | ``` 25 | npm install -g @tarojs/cli 26 | 27 | taro init projName 28 | ``` 29 | -------------------------------------------------------------------------------- /config/dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"development"' 4 | }, 5 | defineConstants: { 6 | }, 7 | weapp: {}, 8 | h5: {} 9 | } 10 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | projectName: 'myApp', 3 | date: '2019-7-4', 4 | designWidth: 750, 5 | deviceRatio: { 6 | '640': 2.34 / 2, 7 | '750': 1, 8 | '828': 1.81 / 2 9 | }, 10 | sourceRoot: 'src', 11 | outputRoot: 'dist', 12 | plugins: { 13 | babel: { 14 | sourceMap: true, 15 | presets: [ 16 | ['env', { 17 | modules: false 18 | }] 19 | ], 20 | plugins: [ 21 | 'transform-decorators-legacy', 22 | 'transform-class-properties', 23 | 'transform-object-rest-spread' 24 | ] 25 | } 26 | }, 27 | defineConstants: { 28 | }, 29 | copy: { 30 | patterns: [ 31 | { from: "src/custom-tab-bar/", to: "dist/custom-tab-bar/" } 32 | ], 33 | options: { 34 | } 35 | }, 36 | weapp: { 37 | module: { 38 | postcss: { 39 | autoprefixer: { 40 | enable: true, 41 | config: { 42 | browsers: [ 43 | 'last 3 versions', 44 | 'Android >= 4.1', 45 | 'ios >= 8' 46 | ] 47 | } 48 | }, 49 | pxtransform: { 50 | enable: true, 51 | config: { 52 | 53 | } 54 | }, 55 | url: { 56 | enable: true, 57 | config: { 58 | limit: 10240 // 设定转换尺寸上限 59 | } 60 | }, 61 | cssModules: { 62 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 63 | config: { 64 | namingPattern: 'module', // 转换模式,取值为 global/module 65 | generateScopedName: '[name]__[local]___[hash:base64:5]' 66 | } 67 | } 68 | } 69 | } 70 | }, 71 | h5: { 72 | publicPath: '/', 73 | staticDirectory: 'static', 74 | module: { 75 | postcss: { 76 | autoprefixer: { 77 | enable: true, 78 | config: { 79 | browsers: [ 80 | 'last 3 versions', 81 | 'Android >= 4.1', 82 | 'ios >= 8' 83 | ] 84 | } 85 | }, 86 | cssModules: { 87 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 88 | config: { 89 | namingPattern: 'module', // 转换模式,取值为 global/module 90 | generateScopedName: '[name]__[local]___[hash:base64:5]' 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | module.exports = function (merge) { 99 | if (process.env.NODE_ENV === 'development') { 100 | return merge({}, config, require('./dev')) 101 | } 102 | return merge({}, config, require('./prod')) 103 | } 104 | -------------------------------------------------------------------------------- /config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"production"' 4 | }, 5 | defineConstants: { 6 | }, 7 | weapp: {}, 8 | h5: { 9 | /** 10 | * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。 11 | * 参考代码如下: 12 | * webpackChain (chain) { 13 | * chain.plugin('analyzer') 14 | * .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []) 15 | * } 16 | */ 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png"; 2 | declare module "*.gif"; 3 | declare module "*.jpg"; 4 | declare module "*.jpeg"; 5 | declare module "*.svg"; 6 | declare module "*.css"; 7 | declare module "*.less"; 8 | declare module "*.scss"; 9 | declare module "*.sass"; 10 | declare module "*.styl"; 11 | declare var wx: any; 12 | 13 | // @ts-ignore 14 | declare const process: { 15 | env: { 16 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt'; 17 | [key: string]: any; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myApp", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "first taro app", 6 | "templateInfo": { 7 | "name": "default", 8 | "typescript": true, 9 | "css": "stylus" 10 | }, 11 | "scripts": { 12 | "build:weapp": "taro build --type weapp", 13 | "build:swan": "taro build --type swan", 14 | "build:alipay": "taro build --type alipay", 15 | "build:tt": "taro build --type tt", 16 | "build:h5": "taro build --type h5", 17 | "build:rn": "taro build --type rn", 18 | "dev:weapp": "npm run build:weapp -- --watch", 19 | "dev:swan": "npm run build:swan -- --watch", 20 | "dev:alipay": "npm run build:alipay -- --watch", 21 | "dev:tt": "npm run build:tt -- --watch", 22 | "dev:h5": "npm run build:h5 -- --watch", 23 | "dev:rn": "npm run build:rn -- --watch" 24 | }, 25 | "author": "", 26 | "license": "MIT", 27 | "dependencies": { 28 | "@tarojs/components": "1.3.5", 29 | "@tarojs/router": "1.3.5", 30 | "@tarojs/taro": "1.3.5", 31 | "@tarojs/taro-alipay": "1.3.5", 32 | "@tarojs/taro-h5": "1.3.5", 33 | "@tarojs/taro-swan": "1.3.5", 34 | "@tarojs/taro-tt": "1.3.5", 35 | "@tarojs/taro-weapp": "1.3.5", 36 | "nervjs": "^1.4.0", 37 | "nerv-devtools": "^1.4.0" 38 | }, 39 | "devDependencies": { 40 | "@types/react": "^16.4.6", 41 | "@types/webpack-env": "^1.13.6", 42 | "@tarojs/plugin-babel": "1.3.5", 43 | "@tarojs/plugin-csso": "1.3.5", 44 | "@tarojs/plugin-stylus": "1.3.5", 45 | "@tarojs/plugin-uglifyjs": "1.3.5", 46 | "@tarojs/webpack-runner": "1.3.5", 47 | "babel-plugin-transform-class-properties": "^6.24.1", 48 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 49 | "babel-plugin-transform-jsx-stylesheet": "^0.6.5", 50 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 51 | "babel-preset-env": "^1.6.1", 52 | "babel-eslint": "^8.2.3", 53 | "eslint": "^5.16.0", 54 | "eslint-config-taro": "1.3.5", 55 | "eslint-plugin-react": "^7.8.2", 56 | "eslint-plugin-import": "^2.12.0", 57 | "stylelint": "9.3.0", 58 | "stylelint-config-taro-rn": "1.3.5", 59 | "stylelint-taro-rn": "1.3.5", 60 | "eslint-plugin-taro": "1.3.5", 61 | "@typescript-eslint/parser": "^1.6.0", 62 | "typescript": "^3.0.1" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "dist/", 3 | "projectname": "myApp", 4 | "description": "first taro app", 5 | "appid": "wxb060341354abcac8", 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": true, 9 | "postcss": false, 10 | "minified": false, 11 | "newFeature": true, 12 | "autoAudits": false, 13 | "checkInvalidKey": true, 14 | "checkSiteMap": true, 15 | "uploadWithSourceMap": true, 16 | "babelSetting": { 17 | "ignore": [], 18 | "disablePlugins": [], 19 | "outputPath": "" 20 | } 21 | }, 22 | "compileType": "miniprogram", 23 | "simulatorType": "wechat", 24 | "simulatorPluginLibVersion": {}, 25 | "libVersion": "2.8.0", 26 | "condition": { 27 | "search": { 28 | "current": -1, 29 | "list": [] 30 | }, 31 | "conversation": { 32 | "current": -1, 33 | "list": [] 34 | }, 35 | "plugin": { 36 | "current": -1, 37 | "list": [] 38 | }, 39 | "game": { 40 | "list": [] 41 | }, 42 | "miniprogram": { 43 | "current": 0, 44 | "list": [ 45 | { 46 | "id": 0, 47 | "name": "pages/test/index", 48 | "pathName": "pages/test/index", 49 | "query": "", 50 | "scene": null 51 | } 52 | ] 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/app.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/app.styl -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import Index from './pages/index' 3 | 4 | import './app.styl' 5 | 6 | // 如果需要在 h5 环境中开启 React Devtools 7 | // 取消以下注释: 8 | // if (process.env.NODE_ENV !== 'production' && process.env.TARO_ENV === 'h5') { 9 | // require('nerv-devtools') 10 | // } 11 | 12 | class App extends Component { 13 | 14 | /** 15 | * 指定config的类型声明为: Taro.Config 16 | * 17 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 18 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 19 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 20 | */ 21 | config: Config = { 22 | pages: [ 23 | 'pages/index/index', 24 | 'pages/start/index', 25 | 'pages/search/index', 26 | 'pages/detail/index', 27 | 'pages/camera/index', 28 | 'pages/graphic/index', 29 | 'pages/classification/index', 30 | 'pages/qrCode/index', 31 | 'pages/test/index' 32 | ], 33 | window: { 34 | backgroundTextStyle: 'light', 35 | navigationBarBackgroundColor: '#fff', 36 | navigationBarTitleText: 'WeChat', 37 | navigationBarTextStyle: 'black' 38 | }, 39 | tabBar: { 40 | // custom: true, 41 | // borderStyle: 'white', 42 | // backgroundColor: '#f8f8f8', 43 | color: '#000', 44 | selectedColor: '#1789E5', 45 | list: [{ 46 | pagePath: 'pages/index/index', 47 | text: '首页', 48 | iconPath: './imgs/icon_home.png', 49 | selectedIconPath: './imgs/icon_home_HL.png', 50 | }, { 51 | pagePath: 'pages/classification/index', 52 | text: '分类', 53 | iconPath: './imgs/icon_classification.png', 54 | selectedIconPath: './imgs/icon_classification_HL.png', 55 | }, { 56 | pagePath: 'pages/test/index', 57 | text: '测试', 58 | iconPath: './imgs/icon_test.png', 59 | selectedIconPath: './imgs/icon_test_HL.png', 60 | }, { 61 | pagePath: 'pages/graphic/index', 62 | text: '图文', 63 | iconPath: './imgs/icon_graphic.png', 64 | selectedIconPath: './imgs/icon_graphic_HL.png', 65 | }] 66 | } 67 | } 68 | 69 | componentDidMount () {} 70 | 71 | componentDidShow () {} 72 | 73 | componentDidHide () {} 74 | 75 | componentDidCatchError () {} 76 | 77 | // 在 App 类中的 render() 函数没有实际作用 78 | // 请勿修改此函数 79 | render () { 80 | return ( 81 | 82 | ) 83 | } 84 | } 85 | 86 | Taro.render(, document.getElementById('app')) 87 | -------------------------------------------------------------------------------- /src/components/garbageTypeItem/alipay.styl: -------------------------------------------------------------------------------- 1 | $recyclable-color = #2761C9 2 | $harmful-color = #DD1717 3 | $wet-color = #16937B 4 | $dry-color = #FF8B00 5 | 6 | .item 7 | .title 8 | font-weight bold 9 | font-size 28rpx 10 | color #000 11 | position relative 12 | margin 30rpx 0 13 | background #eee 14 | padding 12rpx 30rpx 15 | &::after 16 | content: '' 17 | position absolute 18 | right 30rpx 19 | top 50% 20 | width 18rpx 21 | height 18rpx 22 | border-top 3rpx solid #999 23 | border-right 3rpx solid #999 24 | transform translateY(-50%) rotate(45deg) 25 | transition .3s transform 26 | .active::after 27 | transform translateY(-75%) rotate(135deg) 28 | .item-header 29 | display flex 30 | align-items center 31 | padding 30rpx 0 32 | .recyclable-img 33 | width 150rpx 34 | height 150rpx 35 | margin-right 18rpx 36 | border-radius 10rpx 37 | .desc 38 | font-size 26rpx 39 | &.recyclable 40 | color $recyclable-color 41 | &.harmful 42 | color $harmful-color 43 | &.wet 44 | color $wet-color 45 | &.dry 46 | color $dry-color 47 | .tip-header 48 | font-size 26rpx 49 | font-weight bold 50 | padding 12rpx 0 51 | padding-left 24rpx 52 | margin-bottom 30rpx 53 | color #fff 54 | &.recyclable 55 | background $recyclable-color 56 | &.harmful 57 | background $harmful-color 58 | &.wet 59 | background $wet-color 60 | &.dry 61 | background $dry-color 62 | .tip-item 63 | font-size 28rpx 64 | color #000 65 | margin-bottom 12rpx 66 | position relative 67 | padding-left 24rpx 68 | &.recyclable 69 | color $recyclable-color 70 | &.harmful 71 | color $harmful-color 72 | &.wet 73 | color $wet-color 74 | &.dry 75 | color $dry-color 76 | .garbage-list 77 | margin-top 30rpx 78 | .garbage-item 79 | // width 33% 80 | font-size 28rpx 81 | color #000 82 | // display inline-block 83 | // text-align center 84 | // margin-right 18rpx 85 | height 60rpx 86 | line-height 60rpx 87 | padding-left 24rpx 88 | background #eee 89 | margin-top 12rpx 90 | // View:nth-of-type(odd) 91 | // background #eee 92 | // View:nth-of-type(even) 93 | // background #fff 94 | // border-right 1rpx solid #999 95 | // border-bottom 1rpx solid #999 96 | // &.border-top 97 | // border-top 1rpx solid #999 98 | // &::after 99 | // content: '' 100 | // position absolute 101 | // right 18rpx 102 | // top 50% 103 | // width 16rpx 104 | // height 16rpx 105 | // border-top 2rpx solid #999 106 | // border-right 2rpx solid #999 107 | // transform translateY(-50%) rotate(45deg) -------------------------------------------------------------------------------- /src/components/garbageTypeItem/index.styl: -------------------------------------------------------------------------------- 1 | $recyclable-color = #2761C9 2 | $harmful-color = #DD1717 3 | $wet-color = #16937B 4 | $dry-color = #FF8B00 5 | 6 | .item 7 | .title 8 | font-weight bold 9 | font-size 28rpx 10 | color #000 11 | position relative 12 | margin 30rpx 0 13 | background #eee 14 | padding 12rpx 30rpx 15 | &::after 16 | content: '' 17 | position absolute 18 | right 30rpx 19 | top 50% 20 | width 18rpx 21 | height 18rpx 22 | border-top 3rpx solid #999 23 | border-right 3rpx solid #999 24 | transform translateY(-50%) rotate(45deg) 25 | transition .3s transform 26 | .active::after 27 | transform translateY(-75%) rotate(135deg) 28 | .item-header 29 | display flex 30 | align-items center 31 | padding 30rpx 0 32 | .recyclable-img 33 | width 150rpx 34 | height 150rpx 35 | margin-right 18rpx 36 | border-radius 10rpx 37 | .desc 38 | font-size 26rpx 39 | &.recyclable 40 | color $recyclable-color 41 | &.harmful 42 | color $harmful-color 43 | &.wet 44 | color $wet-color 45 | &.dry 46 | color $dry-color 47 | .tip-header 48 | font-size 26rpx 49 | font-weight bold 50 | padding 12rpx 0 51 | padding-left 24rpx 52 | margin-bottom 30rpx 53 | color #fff 54 | &.recyclable 55 | background $recyclable-color 56 | &.harmful 57 | background $harmful-color 58 | &.wet 59 | background $wet-color 60 | &.dry 61 | background $dry-color 62 | .tip-item 63 | font-size 28rpx 64 | color #000 65 | margin-bottom 12rpx 66 | position relative 67 | padding-left 24rpx 68 | &.recyclable 69 | color $recyclable-color 70 | &.harmful 71 | color $harmful-color 72 | &.wet 73 | color $wet-color 74 | &.dry 75 | color $dry-color 76 | .garbage-list 77 | margin-top 30rpx 78 | .garbage-item 79 | // width 33% 80 | font-size 28rpx 81 | color #000 82 | // display inline-block 83 | // text-align center 84 | // margin-right 18rpx 85 | height 60rpx 86 | line-height 60rpx 87 | padding-left 24rpx 88 | View:nth-of-type(odd) 89 | background #eee 90 | View:nth-of-type(even) 91 | background #fff 92 | // border-right 1rpx solid #999 93 | // border-bottom 1rpx solid #999 94 | // &.border-top 95 | // border-top 1rpx solid #999 96 | // &::after 97 | // content: '' 98 | // position absolute 99 | // right 18rpx 100 | // top 50% 101 | // width 16rpx 102 | // height 16rpx 103 | // border-top 2rpx solid #999 104 | // border-right 2rpx solid #999 105 | // transform translateY(-50%) rotate(45deg) -------------------------------------------------------------------------------- /src/components/garbageTypeItem/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Image, Text } from '@tarojs/components' 3 | 4 | if (process.env.TARO_ENV === 'alipay') { 5 | require('./alipay.styl') 6 | } else if (process.env.TARO_ENV === 'weapp') { 7 | require('./index.styl') 8 | } 9 | 10 | interface GarbageTypeItemInfo { 11 | logo: string, 12 | classification: string, 13 | desc: string, 14 | tip: string[], 15 | list: string[] 16 | } 17 | 18 | type Props = { 19 | data: GarbageTypeItemInfo, 20 | id: string 21 | } 22 | 23 | const TYPES = ['recyclable', 'harmful', 'wet', 'dry'] 24 | 25 | export default class GarbageTypeItem extends Component { 26 | 27 | /** 28 | * 指定config的类型声明为: Taro.Config 29 | * 30 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 31 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 32 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 33 | */ 34 | config: Config = { 35 | navigationBarTitleText: '我的' 36 | } 37 | 38 | state = { 39 | isShowTip: true, 40 | isShowIntroduction: true, 41 | isShowGarbageList: true, 42 | } 43 | 44 | componentWillMount () { } 45 | 46 | componentDidMount () { } 47 | 48 | componentWillUnmount () { } 49 | 50 | componentDidShow () { } 51 | 52 | componentDidHide () { } 53 | 54 | render () { 55 | const { 56 | logo, 57 | desc, 58 | classification, 59 | tip, 60 | list 61 | } = this.props.data || { 62 | logo: '', 63 | desc: '', 64 | classification: '', 65 | tip: '', 66 | list: '' 67 | } 68 | 69 | return ( 70 | 71 | {/* 类型简介 */} 72 | { 73 | this.state.isShowIntroduction ? 74 | 75 | 76 | 77 | 78 | {desc} 79 | 80 | : 81 | null 82 | } 83 | 投放要求 84 | {/* 投放要求 */} 85 | { 86 | this.state.isShowTip ? 87 | 88 | { 89 | tip.map((item) => {item}) 90 | } 91 | 92 | : 93 | null 94 | } 95 | {/* 生活常见 */} 96 | { 97 | this.state.isShowGarbageList ? 98 | 99 | { 100 | list.map((item) => { 101 | return ( 102 | {item} 103 | ) 104 | }) 105 | } 106 | 107 | : 108 | null 109 | } 110 | 111 | ) 112 | } 113 | 114 | jumpToSearchPage () { 115 | Taro.navigateTo({ 116 | url: '/pages/search/index' 117 | }) 118 | } 119 | 120 | showTip () { 121 | this.setState({ 122 | isShowTip: !this.state.isShowTip 123 | }) 124 | } 125 | 126 | showIntroduction () { 127 | this.setState({ 128 | isShowIntroduction: !this.state.isShowIntroduction 129 | }) 130 | } 131 | 132 | showGarbageList () { 133 | this.setState({ 134 | isShowGarbageList: !this.state.isShowGarbageList 135 | }) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/custom-tab-bar/index.js: -------------------------------------------------------------------------------- 1 | Component({ 2 | data: { 3 | selected: 0, 4 | color: "#000", 5 | selectedColor: "#1afa29", 6 | list: [{ 7 | pagePath: "/pages/index/index", 8 | iconPath: "../imgs/icon_home.png", 9 | selectedIconPath: "../imgs/icon_home_HL.png", 10 | text: "首页" 11 | }, { 12 | pagePath: "/pages/graphic/index", 13 | iconPath: "../imgs/icon_graphic.png", 14 | selectedIconPath: "../imgs/icon_graphic_HL.png", 15 | text: "图文" 16 | }], 17 | isIpX: false 18 | }, 19 | attached() { 20 | wx.getSystemInfo({ 21 | success: res => { 22 | this.setData({ 23 | isIpX: res.model.includes('iPhone X') 24 | }) 25 | } 26 | }) 27 | }, 28 | methods: { 29 | switchTab(e) { 30 | const data = e.currentTarget.dataset 31 | const url = data.path 32 | wx.switchTab({url}) 33 | this.setData({ 34 | selected: data.index 35 | }) 36 | }, 37 | jumpToCameraPage () { 38 | wx.navigateTo({ 39 | url: '/pages/camera/index' 40 | }) 41 | } 42 | } 43 | }) -------------------------------------------------------------------------------- /src/custom-tab-bar/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } -------------------------------------------------------------------------------- /src/custom-tab-bar/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{item.text}} 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/custom-tab-bar/index.wxss: -------------------------------------------------------------------------------- 1 | .tab-bar { 2 | position: fixed; 3 | bottom: 0; 4 | left: 0; 5 | right: 0; 6 | height: 96px; 7 | /* background: white; */ 8 | background: #fff; 9 | display: flex; 10 | padding: 0 120px; 11 | justify-content: space-between; 12 | padding-bottom: env(safe-area-inset-bottom); 13 | } 14 | 15 | /* .tab-bar-border { 16 | background-color: rgba(0, 0, 0, 0.33); 17 | position: absolute; 18 | left: 0; 19 | top: 0; 20 | width: 100%; 21 | height: 1px; 22 | transform: scaleY(0.5); 23 | } */ 24 | 25 | .tab-bar-item { 26 | /* flex: 1; */ 27 | text-align: center; 28 | display: flex; 29 | justify-content: center; 30 | align-items: center; 31 | flex-direction: column; 32 | } 33 | 34 | .tab-bar-item cover-image { 35 | width: 54px; 36 | height: 54px; 37 | } 38 | 39 | .tab-bar-item cover-view { 40 | font-size: 20px; 41 | } 42 | 43 | .icon-camera { 44 | width: 54px; 45 | height: 54px; 46 | } 47 | 48 | .camera-wrap { 49 | position: fixed; 50 | left: 50%; 51 | transform: translateX(-50%); 52 | bottom: 16px; 53 | display: flex; 54 | align-items: center; 55 | justify-content: center; 56 | width: 108px; 57 | height: 108px; 58 | border-radius: 108px; 59 | background: #f4ea2a; 60 | z-index: 9999; 61 | } 62 | 63 | .bottom { 64 | bottom: 80px 65 | } 66 | -------------------------------------------------------------------------------- /src/imgs/icon_album.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_album.png -------------------------------------------------------------------------------- /src/imgs/icon_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_back.png -------------------------------------------------------------------------------- /src/imgs/icon_camera_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_camera_search.png -------------------------------------------------------------------------------- /src/imgs/icon_change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_change.png -------------------------------------------------------------------------------- /src/imgs/icon_classification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_classification.png -------------------------------------------------------------------------------- /src/imgs/icon_classification_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_classification_HL.png -------------------------------------------------------------------------------- /src/imgs/icon_clear_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_clear_history.png -------------------------------------------------------------------------------- /src/imgs/icon_contact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_contact.png -------------------------------------------------------------------------------- /src/imgs/icon_contact_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_contact_gray.png -------------------------------------------------------------------------------- /src/imgs/icon_graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_graphic.png -------------------------------------------------------------------------------- /src/imgs/icon_graphic_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_graphic_HL.png -------------------------------------------------------------------------------- /src/imgs/icon_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_home.png -------------------------------------------------------------------------------- /src/imgs/icon_home_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_home_HL.png -------------------------------------------------------------------------------- /src/imgs/icon_money.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_money.png -------------------------------------------------------------------------------- /src/imgs/icon_notice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_notice.png -------------------------------------------------------------------------------- /src/imgs/icon_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_share.png -------------------------------------------------------------------------------- /src/imgs/icon_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_test.png -------------------------------------------------------------------------------- /src/imgs/icon_test_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpig/garbageClassification/586a45243ec408182c6870034ace435f4cd39791/src/imgs/icon_test_HL.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/pages/camera/index.styl: -------------------------------------------------------------------------------- 1 | .camera 2 | width 100% 3 | height 100vh 4 | .btn-wrap 5 | position fixed 6 | width 100% 7 | bottom 100rpx 8 | padding 0 66rpx 9 | box-sizing border-box 10 | display flex 11 | align-items center 12 | justify-content space-between 13 | .btn 14 | // position fixed 15 | width 100rpx 16 | height 100rpx 17 | border-radius 100rpx 18 | border 20rpx solid #eee 19 | background #fff 20 | .icon-back 21 | width 66rpx 22 | height 66rpx 23 | .icon-change 24 | width 66rpx 25 | height 66rpx 26 | .mask 27 | position fixed 28 | width 100% 29 | top 0 30 | bottom 0 31 | background rgba(0, 0, 0, .7) 32 | z-index 999 33 | display flex 34 | align-items center 35 | justify-content center 36 | .modal 37 | // position absolute 38 | // left 50% 39 | // top 50% 40 | // transform translate(-50%, -50%) 41 | border-radius 10rpx 42 | background #fff 43 | padding-top 30rpx 44 | .modal-title 45 | font-size 36rpx 46 | color #000 47 | font-weight bold 48 | text-align center 49 | .modal-content 50 | display flex 51 | align-items center 52 | padding 30rpx 53 | .img 54 | width 200rpx 55 | height 200rpx 56 | margin-right 28rpx 57 | .title 58 | font-size 30rpx 59 | font-weight bold 60 | color #000 61 | // margin-top 28rpx 62 | .item 63 | display flex 64 | align-items center 65 | margin-top 18rpx 66 | font-size: 28rpx 67 | color #000 68 | .type 69 | padding 8rpx 12rpx 70 | background #eee 71 | margin-left 12rpx 72 | font-size 24rpx 73 | .btn-row 74 | display flex 75 | // border-top 1rpx solid #999 76 | // position relative 77 | // &::after 78 | // content: '' 79 | // width 1rpx 80 | // height 88rpx 81 | // background #999 82 | // position absolute 83 | // left 50% 84 | // top 0 85 | .btn-item 86 | flex: 1 87 | text-align center 88 | height 88rpx 89 | line-height 88rpx 90 | font-size 30rpx 91 | &.exit 92 | color #666 93 | border 1rpx solid #999 94 | &.continue 95 | color #1789E5 96 | border 1rpx solid #999 -------------------------------------------------------------------------------- /src/pages/camera/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, CoverView, CoverImage, Image, Camera, Button } from '@tarojs/components' 3 | import './index.styl' 4 | 5 | interface garbageInfo { 6 | title: string, 7 | type: string, 8 | desc: string, 9 | type_name: string 10 | } 11 | 12 | const TYPE = ['可回收垃圾', '有害垃圾', '厨余(湿)垃圾', '其他(干)垃圾'] 13 | 14 | let times = 0 15 | 16 | export default class Index extends Component { 17 | 18 | /** 19 | * 指定config的类型声明为: Taro.Config 20 | * 21 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 22 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 23 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 24 | */ 25 | config: Config = { 26 | navigationBarTitleText: '拍照识别', 27 | // navigationStyle: 'custom' 28 | } 29 | 30 | state = { 31 | src: '', 32 | isCompletedQuery: false, 33 | imgHeight: 0, 34 | imgWidth: 0, 35 | list: [] as garbageInfo[], 36 | devicePositionIsBack: true 37 | } 38 | 39 | componentWillMount () { } 40 | 41 | componentDidMount () { } 42 | 43 | componentWillUnmount () { } 44 | 45 | componentDidShow () { } 46 | 47 | componentDidHide () { } 48 | 49 | render () { 50 | return ( 51 | 52 | { 53 | !this.state.isCompletedQuery ? 54 | 55 | 56 | 57 | 58 | 59 | 60 | { 61 | this.state.list.length > 0 ? 62 | 63 | 64 | 识别结果 65 | 66 | { 67 | this.state.src ? 68 | 73 | : 74 | null 75 | } 76 | 77 | 图中包含的垃圾有: 78 | { 79 | this.state.list.map((item) => { 80 | return ( 81 | 82 | {item.title.split('-')[0]} 83 | {TYPE[parseInt(item.type) - 1]} 84 | 85 | ) 86 | }) 87 | } 88 | 89 | 90 | 91 | 退出 92 | 继续 93 | 94 | 95 | 96 | : 97 | null 98 | } 99 | 100 | : 101 | null 102 | } 103 | {/* { 104 | this.state.list.length > 0 ? 105 | 106 | 107 | 识别结果 108 | 109 | { 110 | this.state.src ? 111 | 116 | : 117 | null 118 | } 119 | 120 | 图中包含的垃圾有: 121 | { 122 | this.state.list.map((item) => { 123 | return ( 124 | 125 | {item.title.split('-')[0]} 126 | {TYPE[parseInt(item.type) - 1]} 127 | 128 | ) 129 | }) 130 | } 131 | 132 | 133 | 134 | 退出 135 | 继续 136 | 137 | 138 | 139 | : 140 | null 141 | } */} 142 | {/* */} 143 | 144 | ) 145 | } 146 | 147 | back () { 148 | Taro.navigateBack() 149 | } 150 | 151 | chooseImage () { 152 | Taro.chooseImage({ 153 | count: 1, 154 | sourceType: ['album'], 155 | success: res => { 156 | this.prepareRequest(res.tempFilePaths[0]) 157 | } 158 | }) 159 | } 160 | 161 | changeDevicePosition () { 162 | this.setState({ 163 | devicePositionIsBack: !this.state.devicePositionIsBack 164 | }) 165 | } 166 | 167 | takePhoto () { 168 | const ctx = Taro.createCameraContext() 169 | 170 | ctx.takePhoto({ 171 | quality: 'high', 172 | success: (res) => { 173 | this.prepareRequest(res.tempImagePath) 174 | } 175 | }) 176 | } 177 | 178 | prepareRequest (tempImagePath: string) { 179 | if (tempImagePath) { 180 | Taro.showLoading({ 181 | title: '正在识别中...', 182 | mask: true 183 | }) 184 | 185 | this.setState({ 186 | src: tempImagePath 187 | }) 188 | 189 | // this.setImgSize(res.tempImagePath) 190 | this.filePathToBase64(tempImagePath) 191 | } 192 | } 193 | 194 | filePathToBase64 (tempImagePath: string) { 195 | wx 196 | .getFileSystemManager() 197 | .readFile({ 198 | filePath: tempImagePath, //选择图片返回的相对路径 199 | encoding: 'base64', //编码格式 200 | success: (res: any) => { //成功的回调 201 | this.requestAPI(res.data) 202 | } 203 | }); 204 | } 205 | 206 | requestAPI (base64: string) { 207 | Taro.request({ 208 | url: 'https://www.toolnb.com/ext/lajifenlei.json', 209 | method: 'POST', 210 | header: { 211 | 'content-type': 'application/x-www-form-urlencoded' // 默认值 212 | }, 213 | data: { 214 | token: '519a6560c8815be7e839166becc9f687', 215 | body: base64, 216 | suffix: 'jpg', 217 | type: 'file' 218 | }, 219 | success: res => { 220 | Taro.hideLoading() 221 | 222 | if (res.statusCode === 200) { 223 | const garbageTypes = ['1', '2', '3', '4'] 224 | let result = res.data.data.filter((item: any) => garbageTypes.includes(item.type)) 225 | let arr = [] as any[] 226 | result.forEach((resultItem: any) => { 227 | if (arr.some((item: any) => resultItem.title.split('-')[0] === item.title.split('-')[0])) { 228 | return 229 | } 230 | arr.push(resultItem) 231 | }) 232 | 233 | this.setState({ 234 | list: arr, 235 | }, () => { 236 | this.promptNoResults() 237 | }) 238 | } else { 239 | this.promptErrorMsg() 240 | } 241 | 242 | }, 243 | fail: () => { 244 | Taro.hideLoading() 245 | this.promptErrorMsg() 246 | } 247 | }) 248 | } 249 | 250 | promptErrorMsg () { 251 | if (times < 3) { 252 | times++ 253 | 254 | Taro.showToast({ 255 | title: '处理失败,请重试', 256 | icon: 'none' 257 | }) 258 | } else { 259 | Taro.showModal({ 260 | title: '提示', 261 | content: '拍照识别好像出了点故障,您可以选择手动搜索', 262 | confirmText: '重新拍照', 263 | cancelText: '去搜索', 264 | success: res => { 265 | if (res.cancel) { 266 | Taro.navigateTo({ 267 | url: '/pages/search/index' 268 | }) 269 | } 270 | } 271 | }) 272 | } 273 | } 274 | 275 | promptNoResults () { 276 | if (this.state.list.length === 0) { 277 | Taro.showModal({ 278 | title: '提示', 279 | content: '未识别到相应结果,请选择重新拍照或者去手动搜索', 280 | confirmText: '重新拍照', 281 | cancelText: '去搜索', 282 | success: res => { 283 | if (res.cancel) { 284 | Taro.navigateTo({ 285 | url: '/pages/search/index' 286 | }) 287 | } 288 | } 289 | }) 290 | } 291 | } 292 | 293 | setImgSize (src: string) { 294 | Taro 295 | .getImageInfo({ 296 | src, 297 | }) 298 | .then(res => { 299 | this.setState({ 300 | imgHeight: res.height, 301 | imgWidth: res.width 302 | }) 303 | }) 304 | } 305 | 306 | exit () { 307 | Taro.navigateBack() 308 | } 309 | 310 | continue () { 311 | this.setState({ 312 | list: [] 313 | }) 314 | } 315 | 316 | checkIfAuthorizeCamera () { 317 | Taro.getSetting({ 318 | success: (res: any) => { 319 | if (!res.authSetting['scope.camera']) { 320 | this.setState({ 321 | isCompletedQuery: true 322 | }) 323 | Taro.showModal({ 324 | title: '提示', 325 | content: '您未授权使用摄像头,请授权后再使用', 326 | confirmText: '去授权', 327 | cancelText: '不使用', 328 | success: res => { 329 | if (res.confirm) { 330 | Taro.openSetting({ 331 | complete: () => { 332 | this.checkIfAuthorizeCamera() 333 | this.setState({ 334 | isCompletedQuery: false 335 | }) 336 | } 337 | }) 338 | } else if (res.cancel) { 339 | Taro.navigateBack() 340 | } 341 | } 342 | }) 343 | } 344 | } 345 | }) 346 | } 347 | 348 | onShareAppMessage () { 349 | return {} 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/pages/classification/alipay.styl: -------------------------------------------------------------------------------- 1 | .index 2 | padding 0 30rpx 3 | padding-bottom 66rpx 4 | background #fff 5 | // .swiper 6 | // min-height 100vh 7 | .tab-container 8 | display flex 9 | position sticky 10 | top 0 11 | z-index 999 12 | // align-items center 13 | background #fff 14 | border-bottom 1rpx solid #eee 15 | padding-top: 18rpx 16 | .tab-item 17 | flex 1 18 | height 60rpx 19 | line-height 60rpx 20 | text-align center 21 | font-size 26rpx 22 | color #000 23 | &.recyclable 24 | color #2761C9 25 | &.harmful 26 | color #DD1717 27 | &.wet 28 | color #16937B 29 | &.dry 30 | color #FF8B00 31 | &.active 32 | font-size 28rpx 33 | font-weight bold -------------------------------------------------------------------------------- /src/pages/classification/index.styl: -------------------------------------------------------------------------------- 1 | .index 2 | // padding 0 30rpx 3 | padding-bottom 66rpx 4 | background #fff 5 | .swiper-item 6 | padding 0 30rpx 7 | box-sizing border-box 8 | .tab-container 9 | display flex 10 | position sticky 11 | top 0 12 | z-index 999 13 | // align-items center 14 | background #fff 15 | border-bottom 1rpx solid #eee 16 | // padding-top: 18rpx 17 | padding 0 12rpx 18 | .tab-item 19 | flex 1 20 | height 60rpx 21 | line-height 60rpx 22 | text-align center 23 | font-size 26rpx 24 | color #000 25 | &.recyclable 26 | color #2761C9 27 | &.harmful 28 | color #DD1717 29 | &.wet 30 | color #16937B 31 | &.dry 32 | color #FF8B00 33 | &.active 34 | font-size 30rpx 35 | font-weight bold -------------------------------------------------------------------------------- /src/pages/classification/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Swiper, SwiperItem, ScrollView } from '@tarojs/components' 3 | console.log(process.env.TARO_ENV) 4 | if (process.env.TARO_ENV === 'alipay') { 5 | require('./alipay.styl') 6 | } else if (process.env.TARO_ENV === 'weapp') { 7 | require('./index.styl') 8 | } 9 | import GarbageTypeItem from '../../components/garbageTypeItem' 10 | 11 | const recyclableItem = { 12 | logo: 'https://s2.ax1x.com/2019/07/18/ZjcNfH.png', 13 | classification: 'recyclable', 14 | desc: '可回收垃圾就是可以再生循环的垃圾。本身或材质可再利用的纸类、硬纸板、玻璃、塑料、金属、塑料包装等。', 15 | tip: ['1、可回收物应轻投轻放,清洁干燥、避免污染;', '2、废纸尽量平整;', '3、立体包装物请清空内容物,清洁后压扁投放;', '4、有尖锐边角的,应包裹后投放。'], 16 | list: ['纸板箱', '报纸', '书本', '快递纸袋', '打印纸', '信封', '广告单', '利乐包', '饮料瓶', '洗发水瓶', '乳液罐', '食用油桶', '塑料碗盆', '塑料盒', '塑料玩具', '塑料衣架', 'PE塑料', 'PVC塑料', '亚克力板', '塑料卡片', 'KT板', '泡沫塑料', '帆布袋', '调料瓶', '酒瓶', '化妆品瓶', '玻璃杯', '窗玻璃', '放大镜', '碎玻璃', '小型木制品', '易拉罐', '食品罐桶', '菜刀', '锅', '刀片', '指甲钳', '螺丝刀', '铁钉', '铁皮', '铅箔', '旧衣服', '床单', '枕头', '棉被', '皮鞋', '毛绒玩具', '棉袄', '包', '皮带', '丝绸制品', '电路板', '充电宝/U盘', '电线', '家电'] 17 | } 18 | 19 | const harmfulItem = { 20 | logo: 'https://s2.ax1x.com/2019/07/18/Zjctte.png', 21 | classification: 'harmful', 22 | desc: '有害垃圾指废电池、废灯管、废药品、废油漆及其容器等对人体健康或者自然环境造成直接或者潜在危害的生活废弃物。', 23 | tip: ['1、分类投放有害垃圾时,应注意轻放。', '2、废灯管等易破损的有害垃圾应连带包装或包裹后投放;', '3、废弃药品宜连带包装一并投放;杀虫剂等压力罐装容器,应排空内容物后投放;', '4、在公共场所产生有害垃圾且未发现对应收集容器时,应携带至有害垃圾投放点妥善投放。'], 24 | list: ['充电电池', '镉镍电池', '铅酸电池', '蓄电池', '纽扣电池', '荧光灯管', '日光灯管', '卤素灯', '药品', '药品内包装', '油漆桶', '染发剂壳', '指甲油', '洗甲水', '矿物油及包装', '含水银温度计', '含水银血压计', '老鼠药', '杀虫喷雾罐', '消毒剂', 'X光片', '相片底片'] 25 | } 26 | 27 | const wetItem = { 28 | logo: 'https://s2.ax1x.com/2019/07/18/Zjcapd.png', 29 | classification: 'wet', 30 | desc: '厨余垃圾是指居民日常生活及食品加工、饮食服务、单位供餐等活动中产生的垃圾,包括丢弃不用的菜叶、剩菜、剩饭、果皮、蛋壳、茶渣、骨头等。', 31 | tip: ['1、湿垃圾投放需破袋处理。盛放湿垃圾的容器,如塑料袋等,在投放时应予去除。', '2、湿垃圾应从产生时就与其他品种垃圾分开收集,投放前尽量沥干水分。', '3、有包装物的湿垃圾应将包装物去除后分类投放,包装物应投放到对应的可回收物或干垃圾收集容器。'], 32 | list: ['五谷杂粮', '米面豆制品', '肉', '内脏', '蛋', '蛋壳', '鱼', '鱼骨', '虾', '虾壳', '鱿鱼', '蔬菜', '菌菇', '调料/酱料', '小骨/碎骨', '茶叶渣', '咖啡渣', '糕饼', '糖果', '肉干', '香菇干', '宠物饲料', '果肉', '软果皮', '软果籽', '家养绿植', '中药材', '中药药渣'] 33 | } 34 | 35 | const dryItem = { 36 | logo: 'https://s2.ax1x.com/2019/07/18/Zjcd1A.png', 37 | classification: 'dry', 38 | desc: '其他(干)垃圾包括砖瓦陶瓷、渣土、卫生间废纸、瓷器碎片等难以回收的废弃物。', 39 | tip: ['1、禁止混投。', '2、干垃圾应投入干垃圾收集容器,并保持周边环境整洁。'], 40 | list: ['大骨头', '硬贝壳', '椰子壳', '榴莲壳', '核桃壳', '玉米衣', '甘蔗皮', '粽叶', '榴莲核', '菠萝蜜核', '餐巾纸', '卫生间用纸', '一次性纸杯', '一次性餐具', '一次性手套', '零食包装袋', '尿不湿', '湿纸巾', '卫生纸', '面膜', '眼影', '口红', '粉底液', '睫毛膏', '防晒霜', '护肤霜', '卸妆棉', '卸妆油', '棉签', '粉扑', '假睫毛', '隐形眼镜', '污损塑料袋', '毛巾', '丝袜', '内衣', '污损纸张', '玻璃钢', '猫砂', '狗屎垫', '干燥剂', '烟蒂', '编织袋', '尼龙制品', '毛发', '防碎气泡膜', '炉渣', '灰土', '胶带', '橡皮泥', '竹制品', '陶瓷碗盆', '打火机', '镜子', '伞', '笔'] 41 | } 42 | const recyclableArr = ['纸板箱', '报纸', '书本', '快递纸袋', '打印纸', '信封', '广告单', '饮料瓶', '洗发水瓶', '乳液罐', '食用油桶', '塑料碗盆', '塑料盒', '塑料玩具', '塑料衣架', 'PE塑料', 'PVC塑料', '亚克力板', '塑料卡片', 'KT板', '泡沫塑料', '帆布袋', '调料瓶', '酒瓶', '化妆品瓶', '玻璃杯', '窗玻璃', '放大镜', '碎玻璃', '小型木制品', '易拉罐', '食品罐桶', '菜刀', '锅', '刀片', '指甲钳', '螺丝刀', '铁钉', '铁皮', '铅箔', '旧衣服', '床单', '枕头', '棉被', '皮鞋', '毛绒玩具', '棉袄', '包', '皮带', '丝绸制品', '电路板', '充电宝/U盘', '电线', '家电'] 43 | const harmfulArr = ['充电电池', '镉镍电池', '铅酸电池', '纽扣电池', '荧光灯管', '日光灯管', '卤素灯', '药品', '药品内包装', '油漆桶', '染发剂壳', '指甲油', '洗甲水', '矿物油及包装', '含水银温度计', '含水银血压计', '老鼠药', '杀虫喷雾罐', '消毒剂', 'X光片', '相片底片'] 44 | const wetArr = ['五谷杂粮', '米面豆制品', '肉', '内脏', '蛋', '蛋壳', '鱼', '鱼骨', '虾', '虾壳', '鱿鱼', '蔬菜', '菌菇', '调料/酱料', '小骨/碎骨', '茶叶渣', '咖啡渣', '糕饼', '糖果', '肉干', '香菇干', '宠物饲料', '果肉', '软果皮', '软果籽', '家养绿植', '中药材', '中药药渣'] 45 | const dryArr = ['大骨头', '硬贝壳', '椰子壳', '榴莲壳', '核桃壳', '玉米衣', '甘蔗皮', '粽叶', '榴莲核', '菠萝蜜核', '餐巾纸', '卫生间用纸', '一次性纸杯', '一次性餐具', '一次性手套', '零食包装袋', '尿不湿', '湿纸巾', '卫生纸', '面膜', '眼影', '口红', '粉底液', '睫毛膏', '防晒霜', '护肤霜', '卸妆棉', '卸妆油', '棉签', '粉扑', '假睫毛', '隐形眼镜', '污损塑料袋', '毛巾', '丝袜', '内衣', '污损纸张', '玻璃钢', '猫砂', '狗屎垫', '干燥剂', '烟蒂', '编织袋', '尼龙制品', '毛发', '防碎气泡膜', '炉渣', '灰土', '胶带', '橡皮泥', '竹制品', '陶瓷碗盆', '打火机', '镜子', '伞', '笔'] 46 | 47 | const obj = { 48 | 'recyclable': 0, 49 | 'harmful': 1, 50 | 'wet': 2, 51 | 'dry': 3 52 | } 53 | const idArr = ['recyclable', 'harmful', 'wet', 'dry'] 54 | const attrArr = ['recyclableItemHeight', 'harmfulItemHeight', 'wetItemHeight', 'dryItemHeight'] 55 | 56 | export default class Index extends Component { 57 | 58 | /** 59 | * 指定config的类型声明为: Taro.Config 60 | * 61 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 62 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 63 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 64 | */ 65 | config: Config = { 66 | navigationBarTitleText: '垃圾分类' 67 | } 68 | 69 | state = { 70 | recyclableItemHeight: 0, 71 | harmfulItemHeight: 0, 72 | wetItemHeight: 0, 73 | darItemHeight: 0, 74 | current: 0 75 | } 76 | 77 | componentWillMount () { 78 | this.setState({ 79 | current: obj[this.$router.params.classification] || 0 80 | }) 81 | } 82 | 83 | componentDidMount () { } 84 | 85 | componentWillUnmount () { } 86 | 87 | componentDidShow () { 88 | process.env.TARO_ENV === 'weapp' && this.getComponentHeight() 89 | } 90 | 91 | componentDidHide () { } 92 | 93 | render () { 94 | return ( 95 | 96 | 97 | this.changeTab(0)} hoverClass='hover-bg'>可回收垃圾 98 | this.changeTab(1)} hoverClass='hover-bg'>有害垃圾 99 | this.changeTab(2)} hoverClass='hover-bg'>厨余(湿)垃圾 100 | this.changeTab(3)} hoverClass='hover-bg'>其他(干)垃圾 101 | 102 | { 103 | process.env.TARO_ENV === 'weapp' && 104 | this.changeTab(e.detail.current)} 109 | > 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | } 124 | {/* { 125 | process.env.TARO_ENV === 'alipay' && 126 | 127 | { 128 | this.state.current === 0 ? 129 | 130 | : 131 | this.state.current === 1 ? 132 | 133 | : 134 | this.state.current === 2 ? 135 | 136 | : 137 | 138 | } 139 | 140 | } */} 141 | { 142 | process.env.TARO_ENV === 'alipay' && 143 | 144 | { this.state.current === 0 ? : null} 145 | { this.state.current === 1 ? : null} 146 | { this.state.current === 2 ? : null} 147 | { this.state.current === 3 ? : null} 148 | 149 | } 150 | 151 | ) 152 | } 153 | 154 | changeTab (current: number) { 155 | this.setState({ 156 | current 157 | }) 158 | } 159 | 160 | getComponentHeight () { 161 | idArr.forEach((item, index) => { 162 | const query = Taro.createSelectorQuery() 163 | query.select(`#${item}`).boundingClientRect().exec((res: any) => { 164 | this.setState({ 165 | [`${attrArr[index]}`]: res[0].height 166 | }) 167 | }) 168 | }) 169 | } 170 | 171 | onShareAppMessage () { 172 | return { 173 | // title: '超实用的垃圾分类工具(支持文字搜索、图像识别等功能)', 174 | // path: '/pages/index/index', 175 | // imageUrl: 'https://s2.ax1x.com/2019/07/18/ZXmsKO.png' 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/pages/detail/index.styl: -------------------------------------------------------------------------------- 1 | .index 2 | padding 30rpx 3 | height 100vh 4 | background #fff 5 | .item 6 | font-size 28rpx 7 | color #000 8 | margin-bottom 18rpx 9 | // border 1rpx solid #eee 10 | .title 11 | background #eee 12 | padding 8rpx 30rpx 13 | font-size 26rpx 14 | .detail 15 | padding 18rpx 30rpx 16 | font-size 28rpx 17 | .strong 18 | font-weight bold -------------------------------------------------------------------------------- /src/pages/detail/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Text } from '@tarojs/components' 3 | import './index.styl' 4 | 5 | interface garbageInfo { 6 | name: string 7 | type: number 8 | explain: string 9 | contain: string 10 | tip: string 11 | } 12 | 13 | const TYPE = ['可回收垃圾', '有害垃圾', '厨余(湿)垃圾', '其他(干)垃圾'] 14 | 15 | export default class Index extends Component { 16 | 17 | /** 18 | * 指定config的类型声明为: Taro.Config 19 | * 20 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 21 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 22 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 23 | */ 24 | config: Config = { 25 | navigationBarTitleText: '' 26 | } 27 | 28 | state = { 29 | garbageInfo: {} as garbageInfo 30 | } 31 | 32 | componentWillMount () { 33 | this.setState({ 34 | garbageInfo: JSON.parse(this.$router.params.garbageInfo) 35 | }, () => { 36 | Taro.setNavigationBarTitle({ 37 | title: this.state.garbageInfo.name 38 | }) 39 | }) 40 | } 41 | 42 | componentDidMount () { } 43 | 44 | componentWillUnmount () { } 45 | 46 | componentDidShow () { } 47 | 48 | componentDidHide () { } 49 | 50 | render () { 51 | const { 52 | type, 53 | explain, 54 | contain, 55 | tip 56 | } = this.state.garbageInfo 57 | return ( 58 | 59 | 60 | 垃圾类型 61 | {TYPE[type]} 62 | 63 | 64 | 分类解释 65 | {explain} 66 | 67 | 68 | 包含类型 69 | {contain} 70 | 71 | 72 | 投放提示 73 | {tip} 74 | 75 | {/* 垃圾类型:{TYPE[type]} */} 76 | {/* 分类解释:{explain} 77 | 包含类型:{contain} 78 | 投放提示:{tip} */} 79 | 80 | ) 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/pages/graphic/index.styl: -------------------------------------------------------------------------------- 1 | 2 | .graphic-img 3 | width 100% 4 | -------------------------------------------------------------------------------- /src/pages/graphic/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Image, Text } from '@tarojs/components' 3 | import './index.styl' 4 | 5 | export default class Index extends Component { 6 | 7 | /** 8 | * 指定config的类型声明为: Taro.Config 9 | * 10 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 11 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 12 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 13 | */ 14 | config: Config = { 15 | navigationBarTitleText: '图文' 16 | } 17 | 18 | state = { 19 | screenWidth: 0, 20 | imgHeight: 0, 21 | isIpX: false 22 | } 23 | 24 | componentWillMount () { 25 | Taro.getSystemInfo({ 26 | success: (res: any) => { 27 | console.log(res) 28 | this.setState({ 29 | screenWidth: res.screenWidth, 30 | isIpX: res.model.includes('iPhone X') 31 | }) 32 | this.setImgHeight() 33 | } 34 | }) 35 | 36 | } 37 | 38 | setImgHeight () { 39 | Taro.showLoading({ 40 | title: '正在加载...' 41 | }) 42 | 43 | Taro 44 | .getImageInfo({ 45 | src: 'https://tva1.sinaimg.cn/large/0060lm7Tly1g52p7mhyigj30m84blwm4.jpg', 46 | }) 47 | .then(res => { 48 | this.setState({ 49 | imgHeight: res.height * this.state.screenWidth / res.width 50 | }) 51 | Taro.hideLoading() 52 | }) 53 | .catch(e => Taro.hideLoading()) 54 | } 55 | 56 | componentDidMount () { } 57 | 58 | componentWillUnmount () { } 59 | 60 | componentDidShow () { 61 | // this.$scope.getTabBar().setData({ 62 | // selected: 1 // 当前页面对应的 index 63 | // }) 64 | } 65 | 66 | componentDidHide () { } 67 | 68 | render () { 69 | return ( 70 | // 71 | 72 | {/* */} 73 | 74 | 75 | ) 76 | } 77 | 78 | preview () { 79 | Taro.previewImage({ 80 | current: 'https://tva1.sinaimg.cn/large/0060lm7Tly1g52p7mhyigj30m84blwm4.jpg', 81 | urls: ['https://tva1.sinaimg.cn/large/0060lm7Tly1g52p7mhyigj30m84blwm4.jpg'] 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/pages/index/index.styl: -------------------------------------------------------------------------------- 1 | .strong 2 | font-weight bold 3 | .bottom 4 | position fixed 5 | bottom 30rpx 6 | left 50% 7 | transform translate(-50%) 8 | font-size 26rpx 9 | line-height 40rpx 10 | color #999 11 | .icon-contact 12 | width 40rpx 13 | height 40rpx 14 | margin-right 12rpx 15 | vertical-align middle 16 | // .feed-back 17 | // color #999 18 | Button 19 | position absolute 20 | top 0 21 | right 0 22 | bottom 0 23 | left 0 24 | opacity 0 25 | .index 26 | // padding 28rpx 30rpx 140rpx 27 | width 100% 28 | height 100vh 29 | background #fff 30 | .header 31 | // background url('http://pic.sc.chinaz.com/files/pic/pic9/201610/fpic7918.jpg') 32 | // padding 28rpx 30rpx 28rpx 33 | // margin-bottom 68rpx 34 | position absolute 35 | top 30% 36 | left 66rpx 37 | right 66rpx 38 | text-align center 39 | // transform translateY(-50%) 40 | .type-item 41 | width 260rpx 42 | height 260rpx 43 | border-radius 10rpx 44 | margin-bottom 48rpx 45 | .type-container 46 | display flex 47 | justify-content space-between 48 | flex-wrap wrap 49 | padding 30rpx 77rpx 50 | .type-item 51 | width 260rpx 52 | height 260rpx 53 | border-radius 10rpx 54 | margin-bottom 48rpx 55 | &.recyclable 56 | background #0075C2 57 | &.harmful 58 | background #e80016 59 | &.wet 60 | background #009944 61 | &.dry 62 | background #F7AC00 63 | .input-box 64 | // position absolute 65 | // top 50% 66 | // left 30rpx 67 | // right 30rpx 68 | display flex 69 | align-items center 70 | // width 100% 71 | height 88rpx 72 | box-sizing border-box 73 | padding 0 30rpx 74 | background #eee 75 | border-radius 80rpx 76 | border 1rpx solid #eee 77 | .placeholder 78 | margin-left 12rpx 79 | font-size 28rpx 80 | color #999 81 | .icon-camera 82 | width 50rpx 83 | height 50rpx 84 | margin-left auto 85 | .banner 86 | width 100% 87 | border-radius 10rpx 88 | .img 89 | width 546rpx 90 | height 546rpx 91 | .tab-container 92 | display flex 93 | position sticky 94 | top 0 95 | z-index 999 96 | // align-items center 97 | background #fff 98 | border-bottom 1rpx solid #eee 99 | padding-top: 18rpx 100 | .tab-item 101 | flex 1 102 | height 60rpx 103 | line-height 60rpx 104 | text-align center 105 | font-size 28rpx 106 | color #000 107 | &.recyclable 108 | color #0075C2 109 | &.harmful 110 | color #e80016 111 | &.wet 112 | color #009944 113 | &.dry 114 | color #F7AC00 115 | &.active 116 | // font-size 28rpx 117 | // font-weight bold 118 | // border-bottom 1rpx solid #000 119 | &.hover-bg 120 | // background #eee 121 | .title 122 | position fixed 123 | right 30rpx 124 | bottom 200rpx 125 | font-weight bold 126 | display flex 127 | align-items center 128 | font-size 28rpx 129 | color #000 130 | background #eee 131 | padding 0 24rpx 132 | height 60rpx 133 | border-radius 60rpx 134 | // margin-top 48rpx 135 | // border-bottom 1rpx solid #999 136 | .nav-bar 137 | position fixed 138 | // display flex 139 | // align-items center 140 | .btn-wrap 141 | display flex 142 | // width 174px 143 | // height 64px 144 | border 1px solid #eee 145 | border-radius 100px 146 | background #fff 147 | &::after 148 | content: '' 149 | height 40rpx 150 | width 1rpx 151 | background #eee 152 | position absolute 153 | left 50% 154 | top 50% 155 | transform translate(-50%, -50%) 156 | .Image-wrap 157 | width 50% 158 | display flex 159 | align-items center 160 | justify-content center 161 | position relative 162 | .share-btn 163 | position absolute 164 | top 0 165 | right 0 166 | bottom 0 167 | left 0 168 | opacity 0 169 | Image 170 | width 40rpx 171 | height 40rpx 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/pages/index/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Icon, Text, Image, Button, CoverImage, CoverView } from '@tarojs/components' 3 | import './index.styl' 4 | 5 | export default class Index extends Component { 6 | 7 | /** 8 | * 指定config的类型声明为: Taro.Config 9 | * 10 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 11 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 12 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 13 | */ 14 | config: Config = { 15 | navigationBarTitleText: '首页', 16 | navigationStyle: 'custom' 17 | } 18 | 19 | state = { 20 | // currTabName: 'recyclable', 21 | // statusBarHeight: 0, 22 | // navBarHeight: 0, 23 | screenWidth: 0, 24 | menuButtonInfo: {} as { 25 | height: number, 26 | width: number, 27 | top: number, 28 | left: number, 29 | right: number, 30 | bottom: number 31 | } 32 | } 33 | 34 | componentWillMount () { 35 | process.env.TARO_ENV === 'weapp' && this.getScreenWidthAndMenuButtonInfo() 36 | // this.setState({ 37 | // menuButtonInfo 38 | // }) 39 | // Taro.getSystemInfo({ 40 | // success: ({statusBarHeight, system}) => { 41 | // this.setState({ 42 | // statusBarHeight, 43 | // navBarHeight: system.indexOf('iOS') > -1 ? 44 : 48, 44 | // menuButtonInfo 45 | // }) 46 | // } 47 | // }) 48 | } 49 | 50 | componentDidMount () { } 51 | 52 | componentWillUnmount () { } 53 | 54 | componentDidShow () { 55 | // this.$scope.getTabBar().setData({ 56 | // selected: 0 // 当前页面对应的 index 57 | // }) 58 | } 59 | 60 | componentDidHide () { } 61 | 62 | render () { 63 | return ( 64 | 65 | 66 | 67 | 68 | 69 | 请输入垃圾名称 70 | {/* */} 71 | 72 | 73 | {/* */} 74 | {/* 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | */} 88 | { 89 | process.env.TARO_ENV === 'weapp' && 90 | 91 | 92 | 93 | {/* */} 94 | 95 | 96 | 97 | 98 | 99 | {/* */} 100 | 101 | 102 | 103 | } 104 | 105 | 106 | 有问题?点我反馈 107 | 108 | 109 | 110 | ) 111 | } 112 | 113 | getScreenWidthAndMenuButtonInfo () { 114 | const menuButtonInfo = Taro.getMenuButtonBoundingClientRect() 115 | 116 | Taro.getSystemInfo({ 117 | success: ({screenWidth}) => { 118 | this.setState({ 119 | screenWidth, 120 | menuButtonInfo 121 | }) 122 | } 123 | }) 124 | } 125 | 126 | jumpToSearchPage () { 127 | Taro.navigateTo({ 128 | url: '/pages/search/index' 129 | }) 130 | } 131 | 132 | jumpToQrCodePage () { 133 | Taro.navigateTo({ 134 | url: '/pages/qrCode/index' 135 | }) 136 | } 137 | 138 | previewAppreciateCode () { 139 | Taro.previewImage({ 140 | urls: ['http://tva1.sinaimg.cn/large/0060lm7Tly1g57jkjiuy5j30u00u0t9l.jpg'], 141 | current: 'http://tva1.sinaimg.cn/large/0060lm7Tly1g57jkjiuy5j30u00u0t9l.jpg' 142 | }) 143 | } 144 | 145 | jumpToClassificationPage (classification: string) { 146 | Taro.navigateTo({ 147 | url: `/pages/classification/index?classification=${classification}` 148 | }) 149 | } 150 | 151 | clickCameraIcon (e: any) { 152 | e.stopPropagation() 153 | process.env.TARO_ENV === 'weapp' && this.jumpToCameraPage() 154 | // process.env.TARO_ENV === 'alipay' && this.chooseImage() 155 | } 156 | 157 | jumpToCameraPage () { 158 | Taro.navigateTo({ 159 | url: '/pages/camera/index' 160 | }) 161 | } 162 | 163 | chooseImage () { 164 | Taro.chooseImage({ 165 | count: 1, 166 | sourceType: ['camera'], 167 | success: res => { 168 | console.log(res) 169 | } 170 | }) 171 | } 172 | 173 | onShareAppMessage () { 174 | return { 175 | title: '超实用的垃圾分类工具(支持文字搜索、图像识别等功能)', 176 | path: '/pages/index/index', 177 | // imageUrl: 'https://s2.ax1x.com/2019/07/18/ZXmsKO.png' 178 | imageUrl: 'http://tva1.sinaimg.cn/large/007X8olVly1g6re2i1ss8j30dw0dw74k.jpg' 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/pages/qrCode/index.styl: -------------------------------------------------------------------------------- 1 | .index 2 | display flex 3 | flex-direction column 4 | align-items center 5 | justify-content center 6 | width 100% 7 | height 100vh 8 | background #f8f8f8 9 | .qrcode 10 | width 430rpx 11 | height 430rpx 12 | border 1rpx solid #eee 13 | .sub-title 14 | font-size 28rpx 15 | margin-bottom 18rpx 16 | -------------------------------------------------------------------------------- /src/pages/qrCode/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Image } from '@tarojs/components' 3 | import './index.styl' 4 | 5 | export default class Index extends Component { 6 | 7 | /** 8 | * 指定config的类型声明为: Taro.Config 9 | * 10 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 11 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 12 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 13 | */ 14 | config: Config = { 15 | navigationBarTitleText: '征婚启事' 16 | } 17 | 18 | componentWillMount () { } 19 | 20 | componentDidMount () { } 21 | 22 | componentWillUnmount () { } 23 | 24 | componentDidShow () { } 25 | 26 | componentDidHide () { } 27 | 28 | render () { 29 | return ( 30 | 31 | 优质单身男青年,求撩😄 32 | 33 | 34 | ) 35 | } 36 | 37 | preview () { 38 | Taro.previewImage({ 39 | current: 'https://tva1.sinaimg.cn/large/0060lm7Tly1g52v6387f9j30by0bywes.jpg', 40 | urls: ['https://tva1.sinaimg.cn/large/0060lm7Tly1g52v6387f9j30by0bywes.jpg'] 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/pages/search/index.styl: -------------------------------------------------------------------------------- 1 | .search 2 | // 支付宝 3 | padding 30rpx 4 | padding-bottom 0 5 | min-height 100vh 6 | background #fff 7 | // 微信 8 | // padding 0 30rpx 9 | .header 10 | display flex 11 | align-items center 12 | margin-bottom 28rpx 13 | .input-wrap 14 | position relative 15 | width 100% 16 | height 80rpx 17 | box-sizing border-box 18 | padding-right 60rpx 19 | background #eee 20 | border-radius 80rpx 21 | border 1rpx solid #eee 22 | .icon-search 23 | position absolute 24 | left 30rpx 25 | top 50% 26 | transform translateY(-50%) 27 | .icon-clear 28 | .icon-camera 29 | position absolute 30 | right 30rpx 31 | top 50% 32 | transform translateY(-50%) 33 | .icon-camera 34 | width 50rpx 35 | height 50rpx 36 | .input 37 | // 支付宝 38 | height 100% 39 | width 100% 40 | padding-right 60rpx 41 | box-sizing border-box 42 | padding-left 72rpx 43 | border-radius 80rpx 44 | font-size 28rpx 45 | background #eee 46 | // 微信 47 | // height 100% 48 | // padding-left 72rpx 49 | // font-size 28rpx 50 | .cancel-btn 51 | width 100rpx 52 | font-size 28rpx 53 | text-align right 54 | .list 55 | .item 56 | display flex 57 | align-items center 58 | height 80rpx 59 | border-bottom 1px solid #eee 60 | .name 61 | color #000 62 | font-size 28rpx 63 | .type 64 | font-size 24rpx 65 | color #666 66 | background #f4f4f4 67 | padding 4rpx 12rpx 68 | margin-left 12rpx 69 | .history-header 70 | display flex 71 | align-items center 72 | justify-content space-between 73 | font-size 28rpx 74 | color #000 75 | font-weight bold 76 | margin 28rpx 0 77 | .icon-clear-history 78 | width: 25.2rpx; 79 | height: 31.2rpx; 80 | .history-item 81 | padding 12rpx 24rpx 82 | height 30rpx 83 | line-height 30rpx 84 | font-size 24rpx 85 | color #333 86 | border-radius 30rpx 87 | display inline-block 88 | background #eee 89 | margin-right 18rpx 90 | margin-bottom 18rpx 91 | .empty 92 | text-align center 93 | margin-top 200rpx 94 | font-size 28rpx 95 | .recycle 96 | width 350rpx 97 | height 350rpx 98 | margin 0 auto 99 | display block 100 | margin-top 300rpx 101 | 102 | -------------------------------------------------------------------------------- /src/pages/search/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Image, Input, Icon, Text } from '@tarojs/components' 3 | import './index.styl' 4 | import util from '../../utils/util' 5 | 6 | const TYPE = ['可回收垃圾', '有害垃圾', '厨余(湿)垃圾', '其他(干)垃圾'] 7 | const SPECIAL_PERSON = { 8 | '郑鑫': '☺️小仙女', 9 | '朱宝华': '😄大帅比', 10 | '尹垆华': '你是个大帅比哦' 11 | } 12 | 13 | interface garbageInfo { 14 | name: string 15 | type: number 16 | explain: string 17 | contain: string 18 | tip: string 19 | } 20 | 21 | interface hotRecordInfo { 22 | name: string, 23 | type: number, 24 | index: number 25 | } 26 | 27 | export default class Index extends Component { 28 | 29 | /** 30 | * 指定config的类型声明为: Taro.Config 31 | * 32 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 33 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 34 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 35 | */ 36 | config: Config = { 37 | navigationBarTitleText: '搜索' 38 | } 39 | 40 | state = { 41 | garbageName: '', 42 | isLoading: false, 43 | list: [] as garbageInfo[], 44 | historyRecord: [] as string[], 45 | hotRecord: [] as hotRecordInfo[], 46 | } 47 | 48 | componentWillMount () { 49 | this.getHistoryRecord() 50 | this.getHotSearch() 51 | } 52 | 53 | componentDidMount () { } 54 | 55 | componentWillUnmount () { } 56 | 57 | componentDidShow () { } 58 | 59 | componentDidHide () { } 60 | 61 | render () { 62 | return ( 63 | 64 | 65 | 66 | 67 | this.search(e), 300)} 73 | confirm-type='search' 74 | onConfirm={this.search} 75 | /> 76 | {/* 支付宝Icon不能绑定事件 */} 77 | { 78 | this.state.garbageName ? 79 | 80 | 85 | 86 | : 87 | null 88 | } 89 | {/* { 90 | !this.state.garbageName ? 91 | 92 | : 93 | null 94 | } */} 95 | 96 | {/* { 97 | 取消 98 | } */} 99 | 100 | { 101 | !this.state.garbageName && this.state.hotRecord.length > 0 ? 102 | 103 | 热门搜索 104 | {/* */} 105 | 106 | : 107 | null 108 | } 109 | 110 | { 111 | !this.state.garbageName ? 112 | this.state.hotRecord.map((item) => {item.name}) 113 | : 114 | null 115 | } 116 | 117 | { 118 | !this.state.garbageName && this.state.historyRecord.length > 0 && this.state.hotRecord.length > 0 ? 119 | 120 | 历史搜索 121 | 122 | 123 | : 124 | null 125 | } 126 | 127 | { 128 | !this.state.garbageName ? 129 | this.state.historyRecord.map((item) => {item}) 130 | : 131 | null 132 | } 133 | 134 | 135 | { 136 | this.state.list.map((item, index) => 137 | 138 | {item.name} 139 | {TYPE[item.type]} 140 | {/* { item.type === 2 ? 湿垃圾 : null } */} 141 | {/* { item.type === 3 ? 干垃圾 : null } */} 142 | 143 | ) 144 | } 145 | 146 | {Object.keys(SPECIAL_PERSON).includes(this.state.garbageName) ? {SPECIAL_PERSON[this.state.garbageName]} : null} 147 | {!this.state.isLoading && this.state.list.length === 0 && this.state.garbageName && !Object.keys(SPECIAL_PERSON).includes(this.state.garbageName)? (︶︿︶)未匹配到相应信息 : null} 148 | {/* {!this.state.garbageName ? : null} */} 149 | 150 | ) 151 | } 152 | 153 | clickCameraIcon () { 154 | process.env.TARO_ENV === 'weapp' && this.jumpToCameraPage() 155 | process.env.TARO_ENV === 'alipay' && this.chooseImage() 156 | } 157 | 158 | jumpToCameraPage () { 159 | Taro.navigateTo({ 160 | url: '/pages/camera/index' 161 | }) 162 | } 163 | 164 | chooseImage () { 165 | 166 | } 167 | 168 | inputGarbageName (e: any) { 169 | this.setState({ 170 | garbageName: e.detail.value 171 | }) 172 | 173 | // this.state.garbageName && this.search() 174 | } 175 | 176 | clearGarbageName () { 177 | this.setState({ 178 | garbageName: '', 179 | list: [] 180 | }) 181 | } 182 | 183 | search (e?: any) { 184 | const value = e && e.detail.value || '' 185 | 186 | this.setState({ 187 | garbageName: value 188 | }, () => { 189 | if (!this.state.garbageName) { 190 | this.setState({ 191 | list: [], 192 | }) 193 | } 194 | 195 | if (this.state.garbageName && e.type === 'confirm') { 196 | this.saveHistoryRecord() 197 | } 198 | }) 199 | 200 | if (value) { 201 | this.setState({ 202 | isLoading: true 203 | }); 204 | 205 | Taro 206 | .request({ 207 | // url: `https://api.tianapi.com/txapi/lajifenlei/?key=633cadcfeccda00555fdc80463b609ca&word=${this.state.garbageName}`, 208 | url: 'https://api.tianapi.com/txapi/lajifenlei/', 209 | data: { 210 | key: '633cadcfeccda00555fdc80463b609ca', 211 | word: value || this.state.garbageName 212 | }, 213 | success: res => { 214 | console.log(res.data) 215 | if (res.data.code === 200) { 216 | this.setState({ 217 | list: res.data.newslist, 218 | isLoading: false 219 | }); 220 | } else { 221 | this.setState({ 222 | isLoading: false 223 | }); 224 | } 225 | } 226 | }) 227 | } 228 | } 229 | 230 | saveHistoryRecord (item?: string) { 231 | let arr = this.state.historyRecord.slice(0) 232 | arr.unshift(item || this.state.garbageName) 233 | 234 | Taro 235 | .setStorage({ 236 | key: 'search_history', 237 | data: [...new Set(arr)] 238 | }) 239 | .then(() => this.getHistoryRecord()) 240 | } 241 | 242 | getHistoryRecord () { 243 | Taro 244 | .getStorage({ 245 | key: 'search_history', 246 | }) 247 | .then(res => { 248 | res.data && this.setState({ 249 | historyRecord: res.data 250 | }) 251 | }) 252 | } 253 | 254 | clearHistoryRecord () { 255 | Taro.showModal({ 256 | title: '提示', 257 | content: '是否确认删除历史记录', 258 | cancelText: '取消', 259 | confirmText: '确认', 260 | success: res => { 261 | if (res.confirm) { 262 | Taro 263 | .setStorage({ 264 | key: 'search_history', 265 | data: [] 266 | }) 267 | .then(() => this.getHistoryRecord()) 268 | } 269 | } 270 | }) 271 | } 272 | 273 | clickHistoryItem (item: any) { 274 | this.setState({ 275 | garbageName: item 276 | }) 277 | this.search({ 278 | detail: { 279 | value: item 280 | } 281 | }) 282 | } 283 | 284 | getHotSearch () { 285 | Taro.request({ 286 | url: 'https://api.tianapi.com/txapi/hotlajifenlei/', 287 | data: { 288 | key: '633cadcfeccda00555fdc80463b609ca' 289 | }, 290 | success: res => { 291 | console.log(res); 292 | this.setState({ 293 | hotRecord: res.data.newslist.slice(0, 18) 294 | }) 295 | } 296 | }) 297 | } 298 | 299 | jumpToDetail (item: garbageInfo) { 300 | Taro.navigateTo({ 301 | url: `/pages/detail/index?garbageInfo=${JSON.stringify(item)}` 302 | }) 303 | this.saveHistoryRecord(item.name) 304 | } 305 | 306 | onShareAppMessage () { 307 | return {} 308 | } 309 | 310 | 311 | } 312 | -------------------------------------------------------------------------------- /src/pages/start/index.styl: -------------------------------------------------------------------------------- 1 | .img 2 | width 100% 3 | height 100vh -------------------------------------------------------------------------------- /src/pages/start/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Image } from '@tarojs/components' 3 | import './index.styl' 4 | 5 | export default class Index extends Component { 6 | 7 | /** 8 | * 指定config的类型声明为: Taro.Config 9 | * 10 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 11 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 12 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 13 | */ 14 | config: Config = { 15 | navigationBarTitleText: '垃圾的小窝' 16 | } 17 | 18 | componentWillMount () { } 19 | 20 | componentDidMount () { } 21 | 22 | componentWillUnmount () { } 23 | 24 | componentDidShow () { 25 | setTimeout(() => this.jumpToSearchPage(), 500); 26 | } 27 | 28 | componentDidHide () { } 29 | 30 | render () { 31 | return ( 32 | 33 | 34 | 35 | ) 36 | } 37 | 38 | jumpToSearchPage () { 39 | Taro.switchTab({ 40 | url: '/pages/index/index' 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/pages/test/index.styl: -------------------------------------------------------------------------------- 1 | @keyframes showBg { 2 | from { 3 | background #fff 4 | } to { 5 | background #409eff 6 | } 7 | } 8 | .swiper 9 | width 100% 10 | height 100vh 11 | background #f8f8f8 12 | .title 13 | font-size 40rpx 14 | font-weight bold 15 | text-align center 16 | margin-top 48rpx 17 | .score 18 | font-size 66rpx 19 | color #409eff 20 | margin-left 18rpx 21 | .table 22 | margin-top 48rpx 23 | .table-item 24 | display flex 25 | .table-title 26 | flex 1 27 | text-align center 28 | margin-bottom 28rpx 29 | font-weight bold 30 | .table-content 31 | font-size 28rpx 32 | flex 1 33 | text-align center 34 | margin-bottom 18rpx 35 | &.correct 36 | font-weight bold 37 | color #409eff 38 | &.error 39 | color red 40 | .btn-row 41 | width 100% 42 | display flex 43 | justify-content center 44 | position fixed 45 | bottom 48rpx 46 | .test-item 47 | padding 30rpx 48 | box-sizing border-box 49 | .test-wrap 50 | background #fff 51 | padding 48rpx 52 | padding-bottom 30rpx 53 | border-radius 10rpx 54 | .topic 55 | font-size 36rpx 56 | .count 57 | margin-top 48rpx 58 | text-align center 59 | .options 60 | margin-top 48rpx 61 | .option 62 | display flex 63 | align-items center 64 | justify-content center 65 | background #fff 66 | border 1rpx solid #999 67 | width 300rpx 68 | height 66rpx 69 | border-radius @height 70 | font-size 28rpx 71 | color #333 72 | margin-top 30rpx 73 | &.checked 74 | animation showBg .3s 75 | background #409eff 76 | border 1rpx solid #409eff 77 | color #fff 78 | &.wrong 79 | background red 80 | border 1rpx solid red 81 | color #fff 82 | .logo 83 | width 32rpx 84 | height 32rpx 85 | border-radius 4rpx 86 | margin-right 18rpx 87 | -------------------------------------------------------------------------------- /src/pages/test/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component, Config } from '@tarojs/taro' 2 | import { View, Image, Swiper, SwiperItem, Text, Button } from '@tarojs/components' 3 | import './index.styl' 4 | 5 | const TYPE = ['可回收垃圾', '有害垃圾', '厨余(湿)垃圾', '其他(干)垃圾'] 6 | const options = [{ 7 | logo: 'https://s2.ax1x.com/2019/07/18/ZjcNfH.png', 8 | type: 0, 9 | typeName: TYPE[0] 10 | }, { 11 | logo: 'https://s2.ax1x.com/2019/07/18/Zjctte.png', 12 | type: 1, 13 | typeName: TYPE[1] 14 | }, { 15 | logo: 'https://s2.ax1x.com/2019/07/18/Zjcapd.png', 16 | type: 2, 17 | typeName: TYPE[2] 18 | }, { 19 | logo: 'https://s2.ax1x.com/2019/07/18/Zjcd1A.png', 20 | type: 3, 21 | typeName: TYPE[3] 22 | }] 23 | 24 | interface hotRecordItem { 25 | name: string, 26 | type: number, 27 | index: number 28 | } 29 | 30 | interface testResultItem { 31 | name: string, 32 | checkedType: number, 33 | correctType: number 34 | } 35 | 36 | export default class Index extends Component { 37 | 38 | /** 39 | * 指定config的类型声明为: Taro.Config 40 | * 41 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 42 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string 43 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 44 | */ 45 | config: Config = { 46 | navigationBarTitleText: '测试' 47 | } 48 | 49 | state = { 50 | hotRecord: [], 51 | topics: [] as hotRecordItem[], 52 | checkedType: null as null | number, 53 | current: 0, 54 | testResult: [] as testResultItem[], 55 | score: 0 56 | } 57 | 58 | componentWillMount () { 59 | this.getHotSearch() 60 | } 61 | 62 | componentDidMount () { } 63 | 64 | componentWillUnmount () { } 65 | 66 | componentDidShow () { } 67 | 68 | componentDidHide () { } 69 | 70 | render () { 71 | return ( 72 | 73 | 74 | { 75 | this.state.topics.map((topic: hotRecordItem, index) => 76 | 77 | 78 | {index + 1}、{topic.name}属于什么垃圾? 79 | 80 | { 81 | options.map(option => 82 |  91 | 92 | {option.typeName} 93 | 94 | ) 95 | } 96 | 97 | {this.state.current + 1} / 10 98 | 99 | 100 | ) 101 | } 102 | { 103 | this.state.topics.length > 0 ? 104 | 105 | 本次得分{this.state.score} 106 | 107 | 108 | 垃圾名称 109 | 你的答案 110 | 正确答案 111 | 112 | { 113 | this.state.testResult.map(item => 114 | 115 | {item.name} 116 | {TYPE[item.checkedType]} 117 | {TYPE[item.correctType]} 118 | 119 | ) 120 | } 121 | 122 | 123 | 124 | 125 | 126 | 127 | : 128 | null 129 | } 130 | 131 | 132 | ) 133 | } 134 | 135 | getHotSearch () { 136 | Taro.showLoading({ 137 | title: '正在获取题库' 138 | }) 139 | Promise.all([ 140 | this.requestHotSearch(0), 141 | this.requestHotSearch(1), 142 | this.requestHotSearch(2), 143 | this.requestHotSearch(3) 144 | ]) 145 | .then((res: any) => { 146 | Taro.hideLoading() 147 | this.setState({ 148 | hotRecord: res[0].concat(res[1], res[2], res[3]) 149 | }, () => this.setTopics()) 150 | }) 151 | } 152 | 153 | setTopics () { 154 | let count = 0, arr = [], hotRecord = this.state.hotRecord 155 | 156 | while (count < 10) { 157 | count++ 158 | arr.push(hotRecord[Math.floor(Math.random() * hotRecord.length)]) 159 | } 160 | 161 | this.setState({ 162 | topics: arr 163 | }) 164 | } 165 | 166 | requestHotSearch (type: number) { 167 | return new Promise((resolve, reject) => { 168 | Taro.request({ 169 | url: 'https://api.tianapi.com/txapi/hotlajifenlei/', 170 | data: { 171 | key: '633cadcfeccda00555fdc80463b609ca', 172 | type: type 173 | }, 174 | success: res => resolve(res.data.newslist), 175 | fail: res => reject(res) 176 | }) 177 | }) 178 | } 179 | 180 | chooseAnAnswer (checkedType: number, topic: hotRecordItem) { 181 | this.setState({ 182 | checkedType 183 | }, () => { 184 | if (checkedType !== topic.type) { 185 | Taro.showModal({ 186 | title: '提示', 187 | content: `${topic.name}属于${TYPE[topic.type]}`, 188 | showCancel: false, 189 | confirmColor: '#409eff', 190 | confirmText: '我知道了', 191 | success: res => { 192 | if (res.confirm) { 193 | setTimeout(() => { 194 | this.resetCheckedTypeAndGoNext() 195 | }, 300) 196 | } 197 | } 198 | }) 199 | } else { 200 | this.setState({ 201 | score: this.state.score + 10 202 | }) 203 | setTimeout(() => { 204 | this.resetCheckedTypeAndGoNext() 205 | }, 400) 206 | } 207 | }) 208 | 209 | this.setState({ 210 | testResult: this.state.testResult.concat([{ 211 | name: topic.name, 212 | checkedType, 213 | correctType: topic.type 214 | }]) 215 | }) 216 | } 217 | 218 | resetCheckedTypeAndGoNext () { 219 | this.setState({ 220 | checkedType: null 221 | }, () => { 222 | this.setState({ 223 | current: this.state.current + 1, 224 | }) 225 | }) 226 | } 227 | 228 | handleTouchMove (e: any) { 229 | e.preventDefault() 230 | e.stopPropagation() 231 | } 232 | 233 | retest () { 234 | // Taro.reLaunch({ 235 | // url: '/pages/test/index' 236 | // }) 237 | this.setState({ 238 | hotRecord: [], 239 | topics: [], 240 | checkedType: null, 241 | current: 0, 242 | testResult: [], 243 | score: 0 244 | }) 245 | this.getHotSearch() 246 | } 247 | 248 | onShareAppMessage () { 249 | return { 250 | title: '垃圾分类知识问答,等你来挑战!', 251 | path: '/pages/test/index', 252 | // imageUrl: 'https://dataset.flyai.com/Garbage_flyai.png' 253 | imageUrl: 'http://tva1.sinaimg.cn/large/007X8olVly1g6rh20qsqcj31370u07wh.jpg' 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/utils/util.tsx: -------------------------------------------------------------------------------- 1 | function debounce (func: Function, delay: number) { 2 | let timer = 0 3 | return function () { 4 | clearTimeout(timer) 5 | timer = setTimeout(() => { 6 | func.apply(this, arguments) 7 | }, delay) 8 | } 9 | } 10 | 11 | export default { 12 | debounce 13 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "removeComments": false, 6 | "preserveConstEnums": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true, 11 | "outDir": "lib", 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "strictNullChecks": true, 15 | "sourceMap": true, 16 | "baseUrl": ".", 17 | "rootDir": ".", 18 | "jsx": "preserve", 19 | "jsxFactory": "Taro.createElement", 20 | "allowJs": true, 21 | "resolveJsonModule": true, 22 | "typeRoots": [ 23 | "node_modules/@types", 24 | "global.d.ts" 25 | ] 26 | }, 27 | "exclude": [ 28 | "node_modules", 29 | "dist" 30 | ], 31 | "compileOnSave": false 32 | } 33 | --------------------------------------------------------------------------------