├── .editorconfig ├── .eslintrc ├── .gitignore ├── .prettierrc ├── README.md ├── config ├── dev.js ├── index.js └── prod.js ├── global.d.ts ├── package.json ├── project.config.json ├── screenshot ├── IMG_1444.PNG ├── IMG_1445.PNG ├── IMG_1446.PNG ├── IMG_1447.PNG └── gh_3cc3c3064657_430.jpg ├── src ├── app.less ├── app.tsx ├── assets │ ├── fonts │ │ └── font_1279133_zcf4btattbf │ │ │ ├── demo.css │ │ │ ├── demo_index.html │ │ │ ├── iconfont.css │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.js │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ └── iconfont.woff2 │ └── images │ │ └── footer.png ├── common │ ├── hooks.ts │ ├── lifestyle.conf.json │ ├── promise-polyfill.js │ └── utils.ts ├── components │ └── WeatherIcon │ │ ├── index.less │ │ └── index.tsx ├── index.html ├── lib │ └── f2-canvas │ │ ├── f2-canvas.js │ │ ├── f2-canvas.json │ │ ├── f2-canvas.wxml │ │ ├── f2-canvas.wxss │ │ └── lib │ │ ├── EventEmitter.min.js │ │ ├── f2.js │ │ └── renderer.js ├── pages │ └── Index │ │ ├── index.less │ │ └── index.tsx └── store │ ├── index.ts │ ├── locationStore.ts │ └── weatherStore.ts ├── tsconfig.json └── yarn.lock /.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 | src/common/const.ts 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "jsxSingleQuote": true, 5 | "semi": false 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 瓜皮天气 2 | 3 |

4 | 5 |

6 | 7 | 瓜皮天气微信小程序,个人初学小程序练手所写,基于 taro + ts + mobx 开发,天气背景素材用 sketch 临摹自 https://dribbble.com/shots/3802809-Weather-A, 侵权联系必删。 8 | 为了保护个人隐私,有个配置文件未上传,请自行申请对应开发者账号,并按照以下格式新增 `src/common/const.ts` 9 | 10 | ```typescript 11 | // src/common/const.ts 12 | const QQ_MAP_KEY = '' // 腾讯地图 key 13 | const GEOCODER_URL = 'https://apis.map.qq.com/ws/geocoder/v1/' // 腾讯地图逆地址解析 url 14 | const HEFENG_BASE_URL = 'https://free-api.heweather.net/s6/' // 和风天气 base url 15 | const HEFENG_KEY = '' // 和风天气 key 16 | 17 | export { QQ_MAP_KEY, GEOCODER_URL, HEFENG_BASE_URL, HEFENG_KEY } 18 | ``` 19 | 20 | ## 特点 21 | 22 | - 简约美观 23 | - 自动定位,支持地图上直接挑选位置 24 | - 当前天气信息,空气质量等 25 | - 后几个小时天气预报 26 | - 7 天内天气图表显示 27 | - 生活指数推荐 28 | 29 | ## 预览 30 | 31 |

32 | 33 | 34 | 35 | 36 |

37 | -------------------------------------------------------------------------------- /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-10-30', 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 | ], 32 | options: { 33 | } 34 | }, 35 | weapp: { 36 | module: { 37 | postcss: { 38 | autoprefixer: { 39 | enable: true, 40 | config: { 41 | browsers: [ 42 | 'last 3 versions', 43 | 'Android >= 4.1', 44 | 'ios >= 8' 45 | ] 46 | } 47 | }, 48 | pxtransform: { 49 | enable: true, 50 | config: { 51 | 52 | } 53 | }, 54 | url: { 55 | enable: true, 56 | config: { 57 | limit: 10240 // 设定转换尺寸上限 58 | } 59 | }, 60 | cssModules: { 61 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 62 | config: { 63 | namingPattern: 'module', // 转换模式,取值为 global/module 64 | generateScopedName: '[name]__[local]___[hash:base64:5]' 65 | } 66 | } 67 | } 68 | } 69 | }, 70 | h5: { 71 | publicPath: '/', 72 | staticDirectory: 'static', 73 | module: { 74 | postcss: { 75 | autoprefixer: { 76 | enable: true, 77 | config: { 78 | browsers: [ 79 | 'last 3 versions', 80 | 'Android >= 4.1', 81 | 'ios >= 8' 82 | ] 83 | } 84 | }, 85 | cssModules: { 86 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 87 | config: { 88 | namingPattern: 'module', // 转换模式,取值为 global/module 89 | generateScopedName: '[name]__[local]___[hash:base64:5]' 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | module.exports = function (merge) { 98 | if (process.env.NODE_ENV === 'development') { 99 | return merge({}, config, require('./dev')) 100 | } 101 | return merge({}, config, require('./prod')) 102 | } 103 | -------------------------------------------------------------------------------- /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 | 12 | declare namespace JSX { 13 | interface IntrinsicElements { 14 | 'import': React.DetailedHTMLProps, HTMLEmbedElement>, 15 | 'ff-canvas': any 16 | } 17 | } 18 | 19 | // @ts-ignore 20 | declare const process: { 21 | env: { 22 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt'; 23 | [key: string]: any; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "guapi-weather", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "", 6 | "templateInfo": { 7 | "name": "mobx", 8 | "typescript": true, 9 | "css": "less" 10 | }, 11 | "main": "index.js", 12 | "scripts": { 13 | "build:weapp": "taro build --type weapp", 14 | "build:swan": "taro build --type swan", 15 | "build:alipay": "taro build --type alipay", 16 | "build:tt": "taro build --type tt", 17 | "build:h5": "taro build --type h5", 18 | "build:rn": "taro build --type rn", 19 | "dev:weapp": "npm run build:weapp -- --watch", 20 | "dev:swan": "npm run build:swan -- --watch", 21 | "dev:alipay": "npm run build:alipay -- --watch", 22 | "dev:tt": "npm run build:tt -- --watch", 23 | "dev:h5": "npm run build:h5 -- --watch", 24 | "dev:rn": "npm run build:rn -- --watch" 25 | }, 26 | "author": "", 27 | "license": "MIT", 28 | "dependencies": { 29 | "@tarojs/async-await": "^1.3.22", 30 | "@tarojs/components": "1.3.22", 31 | "@tarojs/mobx": "1.3.22", 32 | "@tarojs/mobx-h5": "1.3.22", 33 | "@tarojs/mobx-rn": "1.3.22", 34 | "@tarojs/rn-runner": "1.3.22", 35 | "@tarojs/router": "1.3.22", 36 | "@tarojs/taro": "1.3.22", 37 | "@tarojs/taro-alipay": "1.3.22", 38 | "@tarojs/taro-h5": "1.3.22", 39 | "@tarojs/taro-swan": "1.3.22", 40 | "@tarojs/taro-tt": "1.3.22", 41 | "@tarojs/taro-weapp": "1.3.22", 42 | "mobx": "4.8.0", 43 | "nerv-devtools": "^1.4.0", 44 | "nervjs": "^1.4.0" 45 | }, 46 | "devDependencies": { 47 | "@tarojs/plugin-babel": "1.3.22", 48 | "@tarojs/plugin-csso": "1.3.22", 49 | "@tarojs/plugin-less": "1.3.22", 50 | "@tarojs/plugin-uglifyjs": "1.3.22", 51 | "@tarojs/webpack-runner": "1.3.22", 52 | "@types/promise-polyfill": "^6.0.3", 53 | "@types/react": "16.3.14", 54 | "@types/webpack-env": "^1.13.6", 55 | "@typescript-eslint/parser": "^1.6.0", 56 | "babel-eslint": "^8.2.3", 57 | "babel-plugin-transform-class-properties": "^6.24.1", 58 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 59 | "babel-plugin-transform-jsx-stylesheet": "^0.6.5", 60 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 61 | "babel-preset-env": "^1.6.1", 62 | "eslint": "^5.16.0", 63 | "eslint-config-taro": "1.3.22", 64 | "eslint-plugin-import": "^2.12.0", 65 | "eslint-plugin-react": "^7.8.2", 66 | "eslint-plugin-react-hooks": "^1.6.1", 67 | "eslint-plugin-taro": "1.3.22", 68 | "stylelint": "9.3.0", 69 | "stylelint-config-taro-rn": "1.3.22", 70 | "stylelint-taro-rn": "1.3.22", 71 | "typescript": "^3.0.1" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "dist/", 3 | "projectname": "%E7%93%9C%E7%9A%AE%E5%A4%A9%E6%B0%94", 4 | "description": "", 5 | "appid": "wxe35330e1a13dd883", 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": false, 9 | "enhance": false, 10 | "postcss": false, 11 | "minified": false, 12 | "newFeature": true, 13 | "nodeModules": false, 14 | "autoAudits": false, 15 | "uglifyFileName": false, 16 | "checkInvalidKey": true, 17 | "checkSiteMap": true, 18 | "uploadWithSourceMap": true, 19 | "babelSetting": { 20 | "ignore": [], 21 | "disablePlugins": [], 22 | "outputPath": "" 23 | } 24 | }, 25 | "compileType": "miniprogram", 26 | "libVersion": "2.8.3", 27 | "simulatorType": "wechat", 28 | "simulatorPluginLibVersion": {}, 29 | "condition": { 30 | "search": { 31 | "current": -1, 32 | "list": [] 33 | }, 34 | "conversation": { 35 | "current": -1, 36 | "list": [] 37 | }, 38 | "plugin": { 39 | "current": -1, 40 | "list": [] 41 | }, 42 | "game": { 43 | "list": [] 44 | }, 45 | "gamePlugin": { 46 | "current": -1, 47 | "list": [] 48 | }, 49 | "miniprogram": { 50 | "current": -1, 51 | "list": [] 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /screenshot/IMG_1444.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/screenshot/IMG_1444.PNG -------------------------------------------------------------------------------- /screenshot/IMG_1445.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/screenshot/IMG_1445.PNG -------------------------------------------------------------------------------- /screenshot/IMG_1446.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/screenshot/IMG_1446.PNG -------------------------------------------------------------------------------- /screenshot/IMG_1447.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/screenshot/IMG_1447.PNG -------------------------------------------------------------------------------- /screenshot/gh_3cc3c3064657_430.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/screenshot/gh_3cc3c3064657_430.jpg -------------------------------------------------------------------------------- /src/app.less: -------------------------------------------------------------------------------- 1 | .ellipsis { 2 | white-space: nowrap; 3 | overflow: hidden; 4 | text-overflow: ellipsis; 5 | } 6 | -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { Component } from '@tarojs/taro' 2 | import '@tarojs/async-await' 3 | import './common/promise-polyfill' 4 | import Index from './pages/index' 5 | import './app.less' 6 | import './assets/fonts/font_1279133_zcf4btattbf/iconfont.css' 7 | 8 | // 如果需要在 h5 环境中开启 React Devtools 9 | // 取消以下注释: 10 | // if (process.env.NODE_ENV !== 'production' && process.env.TARO_ENV === 'h5') { 11 | // require('nerv-devtools') 12 | // } 13 | 14 | class App extends Component { 15 | componentDidMount() { 16 | const updateManager = Taro.getUpdateManager() 17 | updateManager.onUpdateReady(() => { 18 | Taro.showModal({ 19 | title: '发现新版本', 20 | content: '精彩等你来发现~', 21 | cancelText: '下次再说', 22 | confirmText: '马上更新', 23 | cancelColor: '#999', 24 | confirmColor: '#f3cc49', 25 | success(res) { 26 | if (res.confirm) { 27 | updateManager.applyUpdate() 28 | } 29 | } 30 | }) 31 | }) 32 | } 33 | 34 | config: Taro.Config = { 35 | pages: ['pages/Index/index'], 36 | window: { 37 | navigationBarTitleText: '瓜皮天气' 38 | }, 39 | permission: { 40 | 'scope.userLocation': { 41 | desc: '不授权没法查询你所在地天气数据哦' 42 | } 43 | } 44 | } 45 | 46 | componentDidShow() {} 47 | 48 | componentDidHide() {} 49 | 50 | componentDidCatchError() {} 51 | // 在 App 类中的 render() 函数没有实际作用 52 | // 请勿修改此函数 53 | render() { 54 | return 55 | } 56 | } 57 | 58 | Taro.render(, document.getElementById('app')) 59 | -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/demo.css: -------------------------------------------------------------------------------- 1 | /* Logo 字体 */ 2 | @font-face { 3 | font-family: "iconfont logo"; 4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); 5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), 6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), 7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), 8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); 9 | } 10 | 11 | .logo { 12 | font-family: "iconfont logo"; 13 | font-size: 160px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | /* tabs */ 20 | .nav-tabs { 21 | position: relative; 22 | } 23 | 24 | .nav-tabs .nav-more { 25 | position: absolute; 26 | right: 0; 27 | bottom: 0; 28 | height: 42px; 29 | line-height: 42px; 30 | color: #666; 31 | } 32 | 33 | #tabs { 34 | border-bottom: 1px solid #eee; 35 | } 36 | 37 | #tabs li { 38 | cursor: pointer; 39 | width: 100px; 40 | height: 40px; 41 | line-height: 40px; 42 | text-align: center; 43 | font-size: 16px; 44 | border-bottom: 2px solid transparent; 45 | position: relative; 46 | z-index: 1; 47 | margin-bottom: -1px; 48 | color: #666; 49 | } 50 | 51 | 52 | #tabs .active { 53 | border-bottom-color: #f00; 54 | color: #222; 55 | } 56 | 57 | .tab-container .content { 58 | display: none; 59 | } 60 | 61 | /* 页面布局 */ 62 | .main { 63 | padding: 30px 100px; 64 | width: 960px; 65 | margin: 0 auto; 66 | } 67 | 68 | .main .logo { 69 | color: #333; 70 | text-align: left; 71 | margin-bottom: 30px; 72 | line-height: 1; 73 | height: 110px; 74 | margin-top: -50px; 75 | overflow: hidden; 76 | *zoom: 1; 77 | } 78 | 79 | .main .logo a { 80 | font-size: 160px; 81 | color: #333; 82 | } 83 | 84 | .helps { 85 | margin-top: 40px; 86 | } 87 | 88 | .helps pre { 89 | padding: 20px; 90 | margin: 10px 0; 91 | border: solid 1px #e7e1cd; 92 | background-color: #fffdef; 93 | overflow: auto; 94 | } 95 | 96 | .icon_lists { 97 | width: 100% !important; 98 | overflow: hidden; 99 | *zoom: 1; 100 | } 101 | 102 | .icon_lists li { 103 | width: 100px; 104 | margin-bottom: 10px; 105 | margin-right: 20px; 106 | text-align: center; 107 | list-style: none !important; 108 | cursor: default; 109 | } 110 | 111 | .icon_lists li .code-name { 112 | line-height: 1.2; 113 | } 114 | 115 | .icon_lists .icon { 116 | display: block; 117 | height: 100px; 118 | line-height: 100px; 119 | font-size: 42px; 120 | margin: 10px auto; 121 | color: #333; 122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear; 123 | -moz-transition: font-size 0.25s linear, width 0.25s linear; 124 | transition: font-size 0.25s linear, width 0.25s linear; 125 | } 126 | 127 | .icon_lists .icon:hover { 128 | font-size: 100px; 129 | } 130 | 131 | .icon_lists .svg-icon { 132 | /* 通过设置 font-size 来改变图标大小 */ 133 | width: 1em; 134 | /* 图标和文字相邻时,垂直对齐 */ 135 | vertical-align: -0.15em; 136 | /* 通过设置 color 来改变 SVG 的颜色/fill */ 137 | fill: currentColor; 138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 139 | normalize.css 中也包含这行 */ 140 | overflow: hidden; 141 | } 142 | 143 | .icon_lists li .name, 144 | .icon_lists li .code-name { 145 | color: #666; 146 | } 147 | 148 | /* markdown 样式 */ 149 | .markdown { 150 | color: #666; 151 | font-size: 14px; 152 | line-height: 1.8; 153 | } 154 | 155 | .highlight { 156 | line-height: 1.5; 157 | } 158 | 159 | .markdown img { 160 | vertical-align: middle; 161 | max-width: 100%; 162 | } 163 | 164 | .markdown h1 { 165 | color: #404040; 166 | font-weight: 500; 167 | line-height: 40px; 168 | margin-bottom: 24px; 169 | } 170 | 171 | .markdown h2, 172 | .markdown h3, 173 | .markdown h4, 174 | .markdown h5, 175 | .markdown h6 { 176 | color: #404040; 177 | margin: 1.6em 0 0.6em 0; 178 | font-weight: 500; 179 | clear: both; 180 | } 181 | 182 | .markdown h1 { 183 | font-size: 28px; 184 | } 185 | 186 | .markdown h2 { 187 | font-size: 22px; 188 | } 189 | 190 | .markdown h3 { 191 | font-size: 16px; 192 | } 193 | 194 | .markdown h4 { 195 | font-size: 14px; 196 | } 197 | 198 | .markdown h5 { 199 | font-size: 12px; 200 | } 201 | 202 | .markdown h6 { 203 | font-size: 12px; 204 | } 205 | 206 | .markdown hr { 207 | height: 1px; 208 | border: 0; 209 | background: #e9e9e9; 210 | margin: 16px 0; 211 | clear: both; 212 | } 213 | 214 | .markdown p { 215 | margin: 1em 0; 216 | } 217 | 218 | .markdown>p, 219 | .markdown>blockquote, 220 | .markdown>.highlight, 221 | .markdown>ol, 222 | .markdown>ul { 223 | width: 80%; 224 | } 225 | 226 | .markdown ul>li { 227 | list-style: circle; 228 | } 229 | 230 | .markdown>ul li, 231 | .markdown blockquote ul>li { 232 | margin-left: 20px; 233 | padding-left: 4px; 234 | } 235 | 236 | .markdown>ul li p, 237 | .markdown>ol li p { 238 | margin: 0.6em 0; 239 | } 240 | 241 | .markdown ol>li { 242 | list-style: decimal; 243 | } 244 | 245 | .markdown>ol li, 246 | .markdown blockquote ol>li { 247 | margin-left: 20px; 248 | padding-left: 4px; 249 | } 250 | 251 | .markdown code { 252 | margin: 0 3px; 253 | padding: 0 5px; 254 | background: #eee; 255 | border-radius: 3px; 256 | } 257 | 258 | .markdown strong, 259 | .markdown b { 260 | font-weight: 600; 261 | } 262 | 263 | .markdown>table { 264 | border-collapse: collapse; 265 | border-spacing: 0px; 266 | empty-cells: show; 267 | border: 1px solid #e9e9e9; 268 | width: 95%; 269 | margin-bottom: 24px; 270 | } 271 | 272 | .markdown>table th { 273 | white-space: nowrap; 274 | color: #333; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown>table th, 279 | .markdown>table td { 280 | border: 1px solid #e9e9e9; 281 | padding: 8px 16px; 282 | text-align: left; 283 | } 284 | 285 | .markdown>table th { 286 | background: #F7F7F7; 287 | } 288 | 289 | .markdown blockquote { 290 | font-size: 90%; 291 | color: #999; 292 | border-left: 4px solid #e9e9e9; 293 | padding-left: 0.8em; 294 | margin: 1em 0; 295 | } 296 | 297 | .markdown blockquote p { 298 | margin: 0; 299 | } 300 | 301 | .markdown .anchor { 302 | opacity: 0; 303 | transition: opacity 0.3s ease; 304 | margin-left: 8px; 305 | } 306 | 307 | .markdown .waiting { 308 | color: #ccc; 309 | } 310 | 311 | .markdown h1:hover .anchor, 312 | .markdown h2:hover .anchor, 313 | .markdown h3:hover .anchor, 314 | .markdown h4:hover .anchor, 315 | .markdown h5:hover .anchor, 316 | .markdown h6:hover .anchor { 317 | opacity: 1; 318 | display: inline-block; 319 | } 320 | 321 | .markdown>br, 322 | .markdown>p>br { 323 | clear: both; 324 | } 325 | 326 | 327 | .hljs { 328 | display: block; 329 | background: white; 330 | padding: 0.5em; 331 | color: #333333; 332 | overflow-x: auto; 333 | } 334 | 335 | .hljs-comment, 336 | .hljs-meta { 337 | color: #969896; 338 | } 339 | 340 | .hljs-string, 341 | .hljs-variable, 342 | .hljs-template-variable, 343 | .hljs-strong, 344 | .hljs-emphasis, 345 | .hljs-quote { 346 | color: #df5000; 347 | } 348 | 349 | .hljs-keyword, 350 | .hljs-selector-tag, 351 | .hljs-type { 352 | color: #a71d5d; 353 | } 354 | 355 | .hljs-literal, 356 | .hljs-symbol, 357 | .hljs-bullet, 358 | .hljs-attribute { 359 | color: #0086b3; 360 | } 361 | 362 | .hljs-section, 363 | .hljs-name { 364 | color: #63a35c; 365 | } 366 | 367 | .hljs-tag { 368 | color: #333333; 369 | } 370 | 371 | .hljs-title, 372 | .hljs-attr, 373 | .hljs-selector-id, 374 | .hljs-selector-class, 375 | .hljs-selector-attr, 376 | .hljs-selector-pseudo { 377 | color: #795da3; 378 | } 379 | 380 | .hljs-addition { 381 | color: #55a532; 382 | background-color: #eaffea; 383 | } 384 | 385 | .hljs-deletion { 386 | color: #bd2c00; 387 | background-color: #ffecec; 388 | } 389 | 390 | .hljs-link { 391 | text-decoration: underline; 392 | } 393 | 394 | /* 代码高亮 */ 395 | /* PrismJS 1.15.0 396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 397 | /** 398 | * prism.js default theme for JavaScript, CSS and HTML 399 | * Based on dabblet (http://dabblet.com) 400 | * @author Lea Verou 401 | */ 402 | code[class*="language-"], 403 | pre[class*="language-"] { 404 | color: black; 405 | background: none; 406 | text-shadow: 0 1px white; 407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 408 | text-align: left; 409 | white-space: pre; 410 | word-spacing: normal; 411 | word-break: normal; 412 | word-wrap: normal; 413 | line-height: 1.5; 414 | 415 | -moz-tab-size: 4; 416 | -o-tab-size: 4; 417 | tab-size: 4; 418 | 419 | -webkit-hyphens: none; 420 | -moz-hyphens: none; 421 | -ms-hyphens: none; 422 | hyphens: none; 423 | } 424 | 425 | pre[class*="language-"]::-moz-selection, 426 | pre[class*="language-"] ::-moz-selection, 427 | code[class*="language-"]::-moz-selection, 428 | code[class*="language-"] ::-moz-selection { 429 | text-shadow: none; 430 | background: #b3d4fc; 431 | } 432 | 433 | pre[class*="language-"]::selection, 434 | pre[class*="language-"] ::selection, 435 | code[class*="language-"]::selection, 436 | code[class*="language-"] ::selection { 437 | text-shadow: none; 438 | background: #b3d4fc; 439 | } 440 | 441 | @media print { 442 | 443 | code[class*="language-"], 444 | pre[class*="language-"] { 445 | text-shadow: none; 446 | } 447 | } 448 | 449 | /* Code blocks */ 450 | pre[class*="language-"] { 451 | padding: 1em; 452 | margin: .5em 0; 453 | overflow: auto; 454 | } 455 | 456 | :not(pre)>code[class*="language-"], 457 | pre[class*="language-"] { 458 | background: #f5f2f0; 459 | } 460 | 461 | /* Inline code */ 462 | :not(pre)>code[class*="language-"] { 463 | padding: .1em; 464 | border-radius: .3em; 465 | white-space: normal; 466 | } 467 | 468 | .token.comment, 469 | .token.prolog, 470 | .token.doctype, 471 | .token.cdata { 472 | color: slategray; 473 | } 474 | 475 | .token.punctuation { 476 | color: #999; 477 | } 478 | 479 | .namespace { 480 | opacity: .7; 481 | } 482 | 483 | .token.property, 484 | .token.tag, 485 | .token.boolean, 486 | .token.number, 487 | .token.constant, 488 | .token.symbol, 489 | .token.deleted { 490 | color: #905; 491 | } 492 | 493 | .token.selector, 494 | .token.attr-name, 495 | .token.string, 496 | .token.char, 497 | .token.builtin, 498 | .token.inserted { 499 | color: #690; 500 | } 501 | 502 | .token.operator, 503 | .token.entity, 504 | .token.url, 505 | .language-css .token.string, 506 | .style .token.string { 507 | color: #9a6e3a; 508 | background: hsla(0, 0%, 100%, .5); 509 | } 510 | 511 | .token.atrule, 512 | .token.attr-value, 513 | .token.keyword { 514 | color: #07a; 515 | } 516 | 517 | .token.function, 518 | .token.class-name { 519 | color: #DD4A68; 520 | } 521 | 522 | .token.regex, 523 | .token.important, 524 | .token.variable { 525 | color: #e90; 526 | } 527 | 528 | .token.important, 529 | .token.bold { 530 | font-weight: bold; 531 | } 532 | 533 | .token.italic { 534 | font-style: italic; 535 | } 536 | 537 | .token.entity { 538 | cursor: help; 539 | } 540 | -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/demo_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IconFont Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 29 |
30 |
31 |
    32 | 33 |
  • 34 | 35 |
    跑步
    36 |
    
    37 |
  • 38 | 39 |
  • 40 | 41 |
    42 |
    
    43 |
  • 44 | 45 |
  • 46 | 47 |
    location
    48 |
    
    49 |
  • 50 | 51 |
  • 52 | 53 |
    污染源
    54 |
    
    55 |
  • 56 | 57 |
  • 58 | 59 |
    旅游
    60 |
    
    61 |
  • 62 | 63 |
  • 64 | 65 |
    衣服
    66 |
    
    67 |
  • 68 | 69 |
  • 70 | 71 |
    感冒指数
    72 |
    
    73 |
  • 74 | 75 |
  • 76 | 77 |
    紫外线
    78 |
    
    79 |
  • 80 | 81 |
  • 82 | 83 |
    281-阳光躺椅
    84 |
    
    85 |
  • 86 | 87 |
88 |
89 |

Unicode 引用

90 |
91 | 92 |

Unicode 是字体在网页端最原始的应用方式,特点是:

93 |
    94 |
  • 兼容性最好,支持 IE6+,及所有现代浏览器。
  • 95 |
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • 96 |
  • 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
  • 97 |
98 |
99 |

注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式

100 |
101 |

Unicode 使用步骤如下:

102 |

第一步:拷贝项目下面生成的 @font-face

103 |
@font-face {
105 |   font-family: 'iconfont';
106 |   src: url('iconfont.eot');
107 |   src: url('iconfont.eot?#iefix') format('embedded-opentype'),
108 |       url('iconfont.woff2') format('woff2'),
109 |       url('iconfont.woff') format('woff'),
110 |       url('iconfont.ttf') format('truetype'),
111 |       url('iconfont.svg#iconfont') format('svg');
112 | }
113 | 
114 |

第二步:定义使用 iconfont 的样式

115 |
.iconfont {
117 |   font-family: "iconfont" !important;
118 |   font-size: 16px;
119 |   font-style: normal;
120 |   -webkit-font-smoothing: antialiased;
121 |   -moz-osx-font-smoothing: grayscale;
122 | }
123 | 
124 |

第三步:挑选相应图标并获取字体编码,应用于页面

125 |
126 | <span class="iconfont">&#x33;</span>
128 | 
129 |
130 |

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

131 |
132 |
133 |
134 |
135 |
    136 | 137 |
  • 138 | 139 |
    140 | 跑步 141 |
    142 |
    .icon-paobu 143 |
    144 |
  • 145 | 146 |
  • 147 | 148 |
    149 | 车 150 |
    151 |
    .icon-che 152 |
    153 |
  • 154 | 155 |
  • 156 | 157 |
    158 | location 159 |
    160 |
    .icon-location 161 |
    162 |
  • 163 | 164 |
  • 165 | 166 |
    167 | 污染源 168 |
    169 |
    .icon-wuranyuan 170 |
    171 |
  • 172 | 173 |
  • 174 | 175 |
    176 | 旅游 177 |
    178 |
    .icon-lvyou 179 |
    180 |
  • 181 | 182 |
  • 183 | 184 |
    185 | 衣服 186 |
    187 |
    .icon-yifu 188 |
    189 |
  • 190 | 191 |
  • 192 | 193 |
    194 | 感冒指数 195 |
    196 |
    .icon-ganmaozhishu 197 |
    198 |
  • 199 | 200 |
  • 201 | 202 |
    203 | 紫外线 204 |
    205 |
    .icon-ziwaixian 206 |
    207 |
  • 208 | 209 |
  • 210 | 211 |
    212 | 281-阳光躺椅 213 |
    214 |
    .icon-1153 215 |
    216 |
  • 217 | 218 |
219 |
220 |

font-class 引用

221 |
222 | 223 |

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

224 |

与 Unicode 使用方式相比,具有如下特点:

225 |
    226 |
  • 兼容性良好,支持 IE8+,及所有现代浏览器。
  • 227 |
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • 228 |
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • 229 |
  • 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
  • 230 |
231 |

使用步骤如下:

232 |

第一步:引入项目下面生成的 fontclass 代码:

233 |
<link rel="stylesheet" href="./iconfont.css">
234 | 
235 |

第二步:挑选相应图标并获取类名,应用于页面:

236 |
<span class="iconfont icon-xxx"></span>
237 | 
238 |
239 |

" 240 | iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

241 |
242 |
243 |
244 |
245 |
    246 | 247 |
  • 248 | 251 |
    跑步
    252 |
    #icon-paobu
    253 |
  • 254 | 255 |
  • 256 | 259 |
    260 |
    #icon-che
    261 |
  • 262 | 263 |
  • 264 | 267 |
    location
    268 |
    #icon-location
    269 |
  • 270 | 271 |
  • 272 | 275 |
    污染源
    276 |
    #icon-wuranyuan
    277 |
  • 278 | 279 |
  • 280 | 283 |
    旅游
    284 |
    #icon-lvyou
    285 |
  • 286 | 287 |
  • 288 | 291 |
    衣服
    292 |
    #icon-yifu
    293 |
  • 294 | 295 |
  • 296 | 299 |
    感冒指数
    300 |
    #icon-ganmaozhishu
    301 |
  • 302 | 303 |
  • 304 | 307 |
    紫外线
    308 |
    #icon-ziwaixian
    309 |
  • 310 | 311 |
  • 312 | 315 |
    281-阳光躺椅
    316 |
    #icon-1153
    317 |
  • 318 | 319 |
320 |
321 |

Symbol 引用

322 |
323 | 324 |

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 325 | 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

326 |
    327 |
  • 支持多色图标了,不再受单色限制。
  • 328 |
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • 329 |
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • 330 |
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • 331 |
332 |

使用步骤如下:

333 |

第一步:引入项目下面生成的 symbol 代码:

334 |
<script src="./iconfont.js"></script>
335 | 
336 |

第二步:加入通用 CSS 代码(引入一次就行):

337 |
<style>
338 | .icon {
339 |   width: 1em;
340 |   height: 1em;
341 |   vertical-align: -0.15em;
342 |   fill: currentColor;
343 |   overflow: hidden;
344 | }
345 | </style>
346 | 
347 |

第三步:挑选相应图标并获取类名,应用于页面:

348 |
<svg class="icon" aria-hidden="true">
349 |   <use xlink:href="#icon-xxx"></use>
350 | </svg>
351 | 
352 |
353 |
354 | 355 |
356 |
357 | 376 | 377 | 378 | -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1564567076598'); /* IE9 */ 3 | src: url('iconfont.eot?t=1564567076598#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('iconfont.woff?t=1564567076598') format('woff'), 6 | url('iconfont.ttf?t=1564567076598') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1564567076598#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .icon { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-paobu:before { 19 | content: "\e60b"; 20 | } 21 | 22 | .icon-che:before { 23 | content: "\e602"; 24 | } 25 | 26 | .icon-location:before { 27 | content: "\e663"; 28 | } 29 | 30 | .icon-wuranyuan:before { 31 | content: "\e63a"; 32 | } 33 | 34 | .icon-lvyou:before { 35 | content: "\e651"; 36 | } 37 | 38 | .icon-yifu:before { 39 | content: "\e693"; 40 | } 41 | 42 | .icon-ganmaozhishu:before { 43 | content: "\e6e4"; 44 | } 45 | 46 | .icon-ziwaixian:before { 47 | content: "\eb0d"; 48 | } 49 | 50 | .icon-1153:before { 51 | content: "\e712"; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/src/assets/fonts/font_1279133_zcf4btattbf/iconfont.eot -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(i){var t,e='',c=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss");if(c&&!i.__iconfont__svg__cssinject__){i.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}!function(t){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(t,0);else{var c=function(){document.removeEventListener("DOMContentLoaded",c,!1),t()};document.addEventListener("DOMContentLoaded",c,!1)}else document.attachEvent&&(l=t,o=i.document,a=!1,(n=function(){try{o.documentElement.doScroll("left")}catch(t){return void setTimeout(n,50)}e()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,e())});function e(){a||(a=!0,l())}var l,o,a,n}(function(){var t,c;(t=document.createElement("div")).innerHTML=e,e=null,(c=t.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",function(t,c){c.firstChild?function(t,c){c.parentNode.insertBefore(t,c)}(t,c.firstChild):c.appendChild(t)}(c,document.body))})}(window); -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/src/assets/fonts/font_1279133_zcf4btattbf/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/src/assets/fonts/font_1279133_zcf4btattbf/iconfont.woff -------------------------------------------------------------------------------- /src/assets/fonts/font_1279133_zcf4btattbf/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/src/assets/fonts/font_1279133_zcf4btattbf/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/images/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaojundebug/guapi-weather/668e739bd62bf20ee08cbf825f2a36e90624417c/src/assets/images/footer.png -------------------------------------------------------------------------------- /src/common/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from '@tarojs/taro' 2 | 3 | function useThrottle(fn: Function, delay: number, cb?: Function) { 4 | let time: number 5 | return useCallback((...args) => { 6 | const curTime = new Date().getTime() 7 | if (time && curTime - time < delay) { 8 | return cb && cb() 9 | } 10 | time = curTime 11 | fn.call(this, ...args) 12 | }, []) 13 | } 14 | 15 | export { useThrottle } 16 | -------------------------------------------------------------------------------- /src/common/lifestyle.conf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "title": "舒适度指数", "type": "comf", "icon": "icon-1153" }, 3 | { "title": "穿衣指数", "type": "drsg", "icon": "icon-yifu" }, 4 | { "title": "感冒指数", "type": "flu", "icon": "icon-ganmaozhishu" }, 5 | { "title": "运动指数", "type": "sport", "icon": "icon-paobu" }, 6 | { "title": "旅游指数", "type": "trav", "icon": "icon-lvyou" }, 7 | { "title": "紫外线指数", "type": "uv", "icon": "icon-ziwaixian" }, 8 | { "title": "洗车", "type": "cw", "icon": "icon-che" }, 9 | { "title": "空气污染扩散条件指数", "type": "air", "icon": "icon-wuranyuan" } 10 | ] 11 | -------------------------------------------------------------------------------- /src/common/promise-polyfill.js: -------------------------------------------------------------------------------- 1 | import P from 'promise-polyfill' 2 | 3 | Promise = P 4 | -------------------------------------------------------------------------------- /src/common/utils.ts: -------------------------------------------------------------------------------- 1 | function dateFormat(date: string, fmt: string = 'yyyy-MM-dd hh:mm:ss') { 2 | const d = new Date(date.replace(/-/g, '/')) 3 | var o = { 4 | 'M+': d.getMonth() + 1, 5 | 'd+': d.getDate(), 6 | 'h+': d.getHours(), 7 | 'm+': d.getMinutes(), 8 | 's+': d.getSeconds(), 9 | 'q+': Math.floor((d.getMonth() + 3) / 3), 10 | S: d.getMilliseconds() 11 | } 12 | if (/(y+)/.test(fmt)) 13 | fmt = fmt.replace(RegExp.$1, (d.getFullYear() + '').substr(4 - RegExp.$1.length)) 14 | for (var k in o) 15 | if (new RegExp('(' + k + ')').test(fmt)) 16 | fmt = fmt.replace( 17 | RegExp.$1, 18 | RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) 19 | ) 20 | return fmt 21 | } 22 | 23 | function array2object(arr: T[], key: K) { 24 | const result = {} 25 | arr.forEach(el => { 26 | result[el[key] as any] = el 27 | delete el[key] 28 | }) 29 | return result 30 | } 31 | 32 | // 根据天气 code 设置背景图 33 | function getBackgroundByCode(cond_code: number | string) { 34 | cond_code = Number(cond_code) 35 | let url = '' 36 | switch (cond_code) { 37 | case 100: 38 | url = 'https://s2.ax1x.com/2019/07/30/eGmf4s.png' 39 | break // 晴 40 | case 101: 41 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 42 | break // 多云 43 | case 102: 44 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 45 | break // 少云 46 | case 103: 47 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 48 | break // 晴间多云 49 | case 104: 50 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 51 | break // 阴 52 | case 200: 53 | url = 'https://s2.ax1x.com/2019/07/30/eGmf4s.png' 54 | break // 有风 55 | case 201: 56 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 57 | break // 平静 58 | case 202: 59 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 60 | break // 微风 61 | case 203: 62 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 63 | break // 和风 64 | case 204: 65 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 66 | break // 清风 67 | case 205: 68 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 69 | break // 强风/劲风 70 | case 206: 71 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 72 | break // 疾风 73 | case 207: 74 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 75 | break // 大风 76 | case 208: 77 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 78 | break // 烈风 79 | case 209: 80 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 81 | break // 风暴 82 | case 210: 83 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 84 | break // 狂爆风 85 | case 211: 86 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 87 | break // 飓风 88 | case 212: 89 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 90 | break // 龙卷风 91 | case 213: 92 | url = 'https://s2.ax1x.com/2019/07/30/eGm7uT.png' 93 | break // 热带风暴 94 | case 300: 95 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 96 | break // 阵雨 97 | case 301: 98 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 99 | break // 强阵雨 100 | case 302: 101 | url = 'https://s2.ax1x.com/2019/07/30/eGmovV.png' 102 | break // 雷阵雨 103 | case 303: 104 | url = 'https://s2.ax1x.com/2019/07/30/eGmovV.png' 105 | break // 强雷阵雨 106 | case 304: 107 | url = 'https://s2.ax1x.com/2019/07/30/eGmovV.png' 108 | break // 雷阵雨伴有冰雹 109 | case 305: 110 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 111 | break // 小雨 112 | case 306: 113 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 114 | break // 中雨 115 | case 307: 116 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 117 | break // 大雨 118 | case 308: 119 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 120 | break // 极端降雨 121 | case 309: 122 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 123 | break // 毛毛雨/细雨 124 | case 310: 125 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 126 | break // 暴雨 127 | case 311: 128 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 129 | break // 大暴雨 130 | case 312: 131 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 132 | break // 特大暴雨 133 | case 313: 134 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 135 | break // 冻雨 136 | case 314: 137 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 138 | break // 小到中雨 139 | case 315: 140 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 141 | break // 中到大雨 142 | case 316: 143 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 144 | break // 大到暴雨 145 | case 317: 146 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 147 | break // 暴雨到大暴雨 148 | case 318: 149 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 150 | break // 大暴雨到特大暴雨 151 | case 399: 152 | url = 'https://s2.ax1x.com/2019/07/30/eG2Szj.png' 153 | break // 雨 154 | case 400: 155 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 156 | break // 小雪 157 | case 401: 158 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 159 | break // 中雪 160 | case 402: 161 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 162 | break // 大雪 163 | case 403: 164 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 165 | break // 暴雪 166 | case 404: 167 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 168 | break // 雨夹雪 169 | case 405: 170 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 171 | break // 雨雪天气 172 | case 406: 173 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 174 | break // 阵雨夹雪 175 | case 407: 176 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 177 | break // 阵雪 178 | case 408: 179 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 180 | break // 小到中雪 181 | case 409: 182 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 183 | break // 中到大雪 184 | case 410: 185 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 186 | break // 大到暴雪 187 | case 499: 188 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 189 | break // 雪 190 | case 500: 191 | url = 'https://s2.ax1x.com/2019/07/30/eGcj29.png' 192 | break // 薄雾 193 | case 501: 194 | url = 'https://s2.ax1x.com/2019/07/30/eGcj29.png' 195 | break // 雾 196 | case 502: 197 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 198 | break // 霾 199 | case 503: 200 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 201 | break // 扬沙 202 | case 504: 203 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 204 | break // 浮尘 205 | case 507: 206 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 207 | break // 沙尘暴 208 | case 508: 209 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 210 | break // 强沙尘暴 211 | case 509: 212 | url = 'https://s2.ax1x.com/2019/07/30/eGcj29.png' 213 | break // 浓雾 214 | case 510: 215 | url = 'https://s2.ax1x.com/2019/07/30/eGcj29.png' 216 | break // 强浓雾 217 | case 511: 218 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 219 | break // 中度霾 220 | case 512: 221 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 222 | break // 重度霾 223 | case 513: 224 | url = 'https://s2.ax1x.com/2019/07/30/eGuwlt.png' 225 | break // 严重霾 226 | case 514: 227 | url = 'https://s2.ax1x.com/2019/07/30/eGcj29.png' 228 | break // 大雾 229 | case 515: 230 | url = 'https://s2.ax1x.com/2019/07/30/eGcj29.png' 231 | break // 特强浓雾 232 | case 900: 233 | url = 'https://s2.ax1x.com/2019/07/30/eGmf4s.png' 234 | break // 热 235 | case 901: 236 | url = 'https://s2.ax1x.com/2019/07/30/eGm4Cn.png' 237 | break // 冷 238 | case 999: 239 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 240 | break // 未知 241 | default: 242 | url = 'https://s2.ax1x.com/2019/07/30/eGmbbF.png' 243 | break 244 | } 245 | return url 246 | } 247 | 248 | // 计算某天是星期几 249 | function getDayOfWeek(date: string | Date) { 250 | return ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][new Date(date).getDay()] 251 | } 252 | 253 | // aqi 转为空气质量级别 254 | function aqi2level(aqi: number | string) { 255 | if (aqi <= 50) { 256 | // 优 257 | return 1 258 | } else if (aqi <= 100) { 259 | // 良 260 | return 2 261 | } else if (aqi <= 150) { 262 | // 轻度污染 263 | return 3 264 | } else if (aqi <= 200) { 265 | // 中度污染 266 | return 4 267 | } else if (aqi <= 300) { 268 | // 重度污染 269 | return 5 270 | } else { 271 | //严重污染 272 | return 6 273 | } 274 | } 275 | 276 | export { dateFormat, array2object, getBackgroundByCode, getDayOfWeek, aqi2level } 277 | -------------------------------------------------------------------------------- /src/components/WeatherIcon/index.less: -------------------------------------------------------------------------------- 1 | .icon { 2 | font-size: 0; 3 | image { 4 | width: 50px; 5 | height: 50px; 6 | filter: grayscale(1); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/components/WeatherIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import { Image, View } from '@tarojs/components' 3 | import './index.less' 4 | 5 | interface IProps { 6 | cond_code: string | number 7 | } 8 | 9 | const WeacherIcon: Taro.FC = props => { 10 | return ( 11 | 12 | 13 | 14 | ) 15 | } 16 | 17 | WeacherIcon.options = { 18 | addGlobalClass: true 19 | } 20 | 21 | WeacherIcon.defaultProps = { 22 | cond_code: '999' 23 | } 24 | 25 | export default WeacherIcon 26 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/lib/f2-canvas/f2-canvas.js: -------------------------------------------------------------------------------- 1 | // f2-canvas.js 2 | import Renderer from './lib/renderer' 3 | import F2 from './lib/f2' 4 | 5 | // 适配小程序的事件机制 6 | F2.Util.addEventListener = function(source, type, listener) { 7 | source.addListener(type, listener) 8 | } 9 | 10 | F2.Util.removeEventListener = function(source, type, listener) { 11 | source.removeListener(type, listener) 12 | } 13 | 14 | F2.Util.createEvent = function(event, chart) { 15 | const type = event.type 16 | let x = 0 17 | let y = 0 18 | const touches = event.touches 19 | if (touches && touches.length > 0) { 20 | x = touches[0].x 21 | y = touches[0].y 22 | } 23 | 24 | return { 25 | type, 26 | chart, 27 | x, 28 | y 29 | } 30 | } 31 | 32 | Component({ 33 | /** 34 | * 组件的属性列表 35 | */ 36 | properties: { 37 | canvasId: { 38 | type: String, 39 | value: 'f2-canvas' 40 | } 41 | }, 42 | 43 | /** 44 | * 组件的初始数据 45 | */ 46 | data: {}, 47 | 48 | ready: function() { 49 | this.init() 50 | }, 51 | 52 | /** 53 | * 组件的方法列表 54 | */ 55 | methods: { 56 | init: function() { 57 | const version = wx.version.version.split('.').map(n => parseInt(n, 10)) 58 | const isValid = 59 | version[0] > 1 || 60 | (version[0] === 1 && version[1] > 9) || 61 | (version[0] === 1 && version[1] === 9 && version[2] >= 91) 62 | if (!isValid) { 63 | console.error('微信基础库版本过低,需大于等于 1.9.91。') 64 | return 65 | } 66 | 67 | const ctx = wx.createCanvasContext(this.data.canvasId, this) // 获取小程序上下文 68 | const canvas = new Renderer(ctx) 69 | this.canvas = canvas 70 | 71 | const query = wx.createSelectorQuery().in(this) 72 | query 73 | .select('.f2-canvas') 74 | .boundingClientRect(res => { 75 | const detail = { 76 | canvas, 77 | width: res.width, 78 | height: res.height, 79 | F2 80 | } 81 | this.triggerEvent('init', detail) 82 | }) 83 | .exec() 84 | }, 85 | touchStart(e) { 86 | if (this.canvas) { 87 | this.canvas.emitEvent('touchstart', [e]) 88 | } 89 | }, 90 | touchMove(e) { 91 | if (this.canvas) { 92 | this.canvas.emitEvent('touchmove', [e]) 93 | } 94 | }, 95 | touchEnd(e) { 96 | if (this.canvas) { 97 | this.canvas.emitEvent('touchend', [e]) 98 | } 99 | }, 100 | press(e) { 101 | if (this.canvas) { 102 | this.canvas.emitEvent('press', [e]) 103 | } 104 | } 105 | } 106 | }) 107 | -------------------------------------------------------------------------------- /src/lib/f2-canvas/f2-canvas.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /src/lib/f2-canvas/f2-canvas.wxml: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /src/lib/f2-canvas/f2-canvas.wxss: -------------------------------------------------------------------------------- 1 | /* f2-canvas.wxss */ 2 | .f2-canvas { 3 | width: 100%; 4 | height: 100%; 5 | } -------------------------------------------------------------------------------- /src/lib/f2-canvas/lib/EventEmitter.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * EventEmitter v5.2.4 - git.io/ee 3 | * Unlicense - http://unlicense.org/ 4 | * Oliver Caldwell - http://oli.me.uk/ 5 | * @preserve 6 | */ 7 | !function(e){"use strict";function t(){}function n(e,t){for(var n=e.length;n--;)if(e[n].listener===t)return n;return-1}function r(e){return function(){return this[e].apply(this,arguments)}}function i(e){return"function"==typeof e||e instanceof RegExp||!(!e||"object"!=typeof e)&&i(e.listener)}var s=t.prototype,o=e.EventEmitter;s.getListeners=function(e){var t,n,r=this._getEvents();if(e instanceof RegExp){t={};for(n in r)r.hasOwnProperty(n)&&e.test(n)&&(t[n]=r[n])}else t=r[e]||(r[e]=[]);return t},s.flattenListeners=function(e){var t,n=[];for(t=0;t { 36 | Object.defineProperty(wxCtx, style, { 37 | set: value => { 38 | if (style == "textAlign") { 39 | value = TEXT_ALIGN_MAP[value] ? TEXT_ALIGN_MAP[value] : value; 40 | } 41 | const name = 'set' + CAPITALIZED_ATTRS_MAP[style]; 42 | wxCtx[name](value); 43 | } 44 | }); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/pages/Index/index.less: -------------------------------------------------------------------------------- 1 | .flex-row { 2 | display: flex; 3 | flex-direction: row; 4 | } 5 | 6 | .flex-column { 7 | display: flex; 8 | flex-direction: column; 9 | } 10 | 11 | @border-color: rgba(0, 0, 0, 0.15); 12 | 13 | .container { 14 | position: relative; 15 | overflow: hidden; 16 | background-repeat: no-repeat; 17 | background-position: top right; 18 | background-size: 80% auto; 19 | color: #4a4a4a; 20 | 21 | .location { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | max-width: 30%; 26 | color: #666; 27 | .icon { 28 | font-size: 38px; 29 | } 30 | .address { 31 | display: inline-block; 32 | font-size: 30px; 33 | } 34 | } 35 | 36 | .now { 37 | .flex-row(); 38 | align-items: center; 39 | justify-content: center; 40 | margin-top: 280px; 41 | .curr-tmp { 42 | font-size: 200px; 43 | font-weight: 100; 44 | } 45 | .weather { 46 | .flex-column(); 47 | align-items: center; 48 | justify-content: space-between; 49 | height: 140px; 50 | font-weight: 300; 51 | .cond_txt { 52 | font-size: 52px; 53 | } 54 | .tmp-range { 55 | font-size: 36px; 56 | } 57 | } 58 | } 59 | 60 | .detail { 61 | .flex-row(); 62 | justify-content: center; 63 | .block { 64 | position: relative; 65 | width: 30%; 66 | text-align: center; 67 | font-size: 32px; 68 | font-weight: 200; 69 | &::after { 70 | content: ''; 71 | position: absolute; 72 | right: 0; 73 | top: 25%; 74 | width: 2px; 75 | height: 50%; 76 | background-color: @border-color; 77 | } 78 | &:last-child::after { 79 | display: none; 80 | } 81 | } 82 | } 83 | 84 | .hourly { 85 | margin-top: 50px; 86 | .content { 87 | display: flex; 88 | padding: 25px 0; 89 | .item { 90 | .flex-column(); 91 | text-align: center; 92 | flex-grow: 1; 93 | padding: 0 45px; 94 | Text { 95 | font-size: 28px; 96 | } 97 | } 98 | } 99 | canvas { 100 | height: 180px; 101 | } 102 | } 103 | 104 | .week { 105 | position: relative; 106 | border-top: 1px solid @border-color; 107 | .top, 108 | .bottom { 109 | display: flex; 110 | padding: 25px 0; 111 | .item { 112 | .flex-column(); 113 | text-align: center; 114 | flex: 1; 115 | Text { 116 | font-size: 28px; 117 | } 118 | } 119 | } 120 | canvas { 121 | height: 250px; 122 | } 123 | .mask { 124 | position: absolute; 125 | top: 0; 126 | left: 0; 127 | width: 100%; 128 | height: 100%; 129 | } 130 | } 131 | 132 | .lifestyle { 133 | border-top: 1px solid @border-color; 134 | border-bottom: 1px solid @border-color; 135 | .item { 136 | position: relative; 137 | display: table; 138 | width: 100%; 139 | &:not(:first-child) { 140 | border-top: 1px solid @border-color; 141 | } 142 | .icon { 143 | display: table-cell; 144 | width: 150px; 145 | vertical-align: middle; 146 | text-align: center; 147 | font-size: 56px; 148 | } 149 | .text-content { 150 | padding: 25px 0; 151 | Text { 152 | font-size: 28px; 153 | &:nth-of-type(1) { 154 | } 155 | &:nth-of-type(2) { 156 | display: block; 157 | margin-top: 10px; 158 | padding-right: 25px; 159 | font-size: 24px; 160 | text-align: justify; 161 | color: #888; 162 | } 163 | } 164 | } 165 | } 166 | } 167 | 168 | .footer { 169 | display: block; 170 | width: 220px; 171 | height: 20px; 172 | margin: 50px auto; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/pages/Index/index.tsx: -------------------------------------------------------------------------------- 1 | import Taro, { 2 | useContext, 3 | usePullDownRefresh, 4 | useShareAppMessage, 5 | useEffect, 6 | useLayoutEffect, 7 | useCallback 8 | } from '@tarojs/taro' 9 | import { View, CoverView, Text, ScrollView, Image } from '@tarojs/components' 10 | import { observer } from '@tarojs/mobx' 11 | import { locationStore, weatherStore } from '../../store' 12 | import WeatherIcon from '../../components/WeatherIcon' 13 | import { dateFormat, getBackgroundByCode, getDayOfWeek } from '../../common/utils' 14 | import { useThrottle } from '../../common/hooks' 15 | import lifestyleConf from '../../common/lifestyle.conf.json' 16 | import './index.less' 17 | 18 | let chartDetail = null 19 | const menuRect = Taro.getMenuButtonBoundingClientRect() 20 | const locationBarStyle = { 21 | margin: `${menuRect.top}px auto 0 auto`, 22 | height: `${menuRect.height}px` 23 | } 24 | 25 | const Weather: Taro.FC = () => { 26 | const { 27 | now, 28 | today, 29 | daily_forecast, 30 | hourly, 31 | air, 32 | lifestyle, 33 | syncNow, 34 | syncAir, 35 | syncDailyForecast, 36 | syncHourly, 37 | syncLifestyle 38 | } = useContext(weatherStore) 39 | const { address, syncLocation, setAddress, setLocation } = useContext(locationStore) 40 | 41 | usePullDownRefresh(async () => { 42 | Taro.vibrateShort() 43 | try { 44 | await fetchData() 45 | } catch (error) {} 46 | Taro.stopPullDownRefresh() 47 | }) 48 | 49 | useShareAppMessage(() => { 50 | return { 51 | title: '我发现一个好看的天气小程序,分享给你看看', 52 | path: '/pages/Weather/index' 53 | } 54 | }) 55 | 56 | useEffect(() => { 57 | syncLocation().then(fetchData) 58 | // eslint-disable-next-line 59 | }, []) 60 | 61 | useLayoutEffect(() => { 62 | function initChart() { 63 | const { F2, canvas, width, height } = chartDetail as any 64 | const data = daily_forecast.map(el => ({ 65 | date: el.date, 66 | tmp_max: Number(el.tmp_max), 67 | tmp_min: Number(el.tmp_min) 68 | })) 69 | 70 | const chart = new F2.Chart({ 71 | el: canvas, 72 | padding: [30, 0, 30, 0], 73 | syncY: true, 74 | width, 75 | height 76 | }) 77 | chart.source(data) 78 | // 不显示坐标轴 79 | chart.axis(false) 80 | // 不显示提示 81 | chart.tooltip(false) 82 | // 画线 83 | chart 84 | .line() 85 | .position('date*tmp_max') 86 | .shape('smooth') 87 | .style({ stroke: '#f3cc49', lineWidth: 2 }) 88 | chart 89 | .line() 90 | .position('date*tmp_min') 91 | .shape('smooth') 92 | .style({ stroke: '#4091f7', lineWidth: 2 }) 93 | // 画点 94 | chart 95 | .point() 96 | .position('date*tmp_max') 97 | .style({ 98 | stroke: '#f3cc49', 99 | fill: '#fff', 100 | lineWidth: 2, 101 | r: 4 102 | }) 103 | chart 104 | .point() 105 | .position('date*tmp_min') 106 | .style({ 107 | stroke: '#4091f7', 108 | fill: '#fff', 109 | lineWidth: 2, 110 | r: 4 111 | }) 112 | 113 | data.map(function(obj: any) { 114 | // 画文本 115 | chart.guide().text({ 116 | position: [obj.date, obj.tmp_max], 117 | content: obj.tmp_max + '°', 118 | style: { 119 | fill: '#f3cc49', 120 | textAlign: 'center', 121 | fontSize: 13 122 | }, 123 | offsetY: -15 124 | }) 125 | chart.guide().text({ 126 | position: [obj.date, obj.tmp_min], 127 | content: obj.tmp_min + '°', 128 | style: { 129 | fill: '#4091f7', 130 | textAlign: 'center', 131 | fontSize: 13 132 | }, 133 | offsetY: 15 134 | }) 135 | }) 136 | chart.render() 137 | } 138 | if (daily_forecast.length > 0) initChart() 139 | }, [daily_forecast]) 140 | 141 | const fetchData = useThrottle( 142 | () => { 143 | Taro.showLoading({ title: '拼命请求中', mask: true }) 144 | return Promise.all([ 145 | syncNow(), 146 | syncAir(), 147 | syncHourly(), 148 | syncDailyForecast(), 149 | syncLifestyle() 150 | ]).finally(() => { 151 | Taro.hideLoading() 152 | }) 153 | }, 154 | 10000, 155 | () => { 156 | Taro.showToast({ title: '已经更新过了哦~', icon: 'none' }) 157 | } 158 | ) 159 | 160 | const chooseLocation = useCallback(() => { 161 | Taro.chooseLocation().then(res => { 162 | setAddress(res.address) 163 | setLocation({ latitude: res.latitude, longitude: res.longitude }) 164 | fetchData() 165 | }) 166 | // eslint-disable-next-line 167 | }, []) 168 | 169 | return ( 170 | 174 | 175 | 176 | {address} 177 | 178 | 179 | 180 | 181 | {now.tmp || 'N/A'}° 182 | 183 | 184 | {now.cond_txt || '未知'} 185 | 186 | {today.tmp_min || 'N/A'} ~ {today.tmp_max || 'N/A'}° 187 | 188 | 189 | 190 | 191 | 192 | 193 | {air.qlty || '未知'} 194 | 空气 195 | 196 | 197 | {now.hum ? now.hum + '%' : '未知'} 198 | 湿度 199 | 200 | 201 | 202 | {now.wind_dir ? `${now.wind_dir} ${now.wind_sc}级` : '未知'} 203 | 204 | 风向 205 | 206 | 207 | 208 | {/* 时段 */} 209 | 210 | 211 | {hourly.map(el => { 212 | return ( 213 | 214 | {dateFormat(el.time, 'hh:mm')} 215 | 216 | {el.cond_txt} 217 | 218 | ) 219 | })} 220 | 221 | 222 | 223 | {/* 七天内 */} 224 | 225 | 226 | {daily_forecast.map(el => { 227 | return ( 228 | 229 | {getDayOfWeek(el.date)} 230 | 231 | {el.cond_txt_d} 232 | 233 | ) 234 | })} 235 | 236 | (chartDetail = detail)} /> 237 | 238 | {daily_forecast.map(el => { 239 | return ( 240 | 241 | 242 | {el.cond_txt_n} 243 | {/* {el.wind_dir} */} 244 | 245 | ) 246 | })} 247 | 248 | {/* 由于图表会阻碍滑动,这里放个View覆盖一下 */} 249 | 250 | 251 | 252 | {/* 生活指数 */} 253 | 254 | {lifestyleConf.map(el => ( 255 | 256 | 257 | 258 | 259 | {el.title} {lifestyle[el.type].brf} 260 | 261 | {lifestyle[el.type].txt} 262 | 263 | 264 | ))} 265 | 266 | 267 | {/* 底部小文本 */} 268 | 269 | 270 | ) 271 | } 272 | 273 | Weather.config = { 274 | usingComponents: { 275 | 'ff-canvas': '../../lib/f2-canvas/f2-canvas' 276 | }, 277 | navigationStyle: 'custom', 278 | navigationBarTextStyle: 'black', 279 | enablePullDownRefresh: true 280 | } 281 | 282 | export default observer(Weather) 283 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | export { default as locationStore } from './locationStore' 2 | export { default as weatherStore } from './weatherStore' 3 | -------------------------------------------------------------------------------- /src/store/locationStore.ts: -------------------------------------------------------------------------------- 1 | import { observable, action } from 'mobx' 2 | import Taro, { createContext } from '@tarojs/taro' 3 | import { GEOCODER_URL, QQ_MAP_KEY } from '../common/const' 4 | 5 | class LocationStore { 6 | @observable address: any = '北京' // 地址 7 | @observable location: any = { 8 | latitude: 30.25961, // 纬度 9 | longitude: 120.13026 // 经度 10 | } 11 | 12 | syncLocation = () => { 13 | return new Promise((resolve, reject) => { 14 | Taro.getLocation({ type: 'gcj02' }) 15 | // 拿到当前坐标 16 | .then( 17 | res => { 18 | return Taro.request({ 19 | url: GEOCODER_URL, 20 | data: { 21 | location: res.latitude + ',' + res.longitude, 22 | key: QQ_MAP_KEY 23 | } 24 | }) 25 | }, 26 | () => { 27 | Taro.showToast({ title: '检测到您未授权使用位置权限,请先开启哦', icon: 'none' }) 28 | reject() 29 | } 30 | ) 31 | // 根据坐标拿到地址 32 | .then(({ data }: any) => { 33 | if (data.status !== 0) { 34 | Taro.showToast({ 35 | title: data.message, 36 | icon: 'none', 37 | duration: 3000 38 | }) 39 | return reject() 40 | } 41 | 42 | this.setAddress(data.result.address) 43 | this.setLocation({ 44 | latitude: data.result.location.lat, 45 | longitude: data.result.location.lng 46 | }) 47 | resolve() 48 | }) 49 | }) 50 | } 51 | 52 | @action 53 | setAddress = val => { 54 | this.address = val 55 | } 56 | 57 | @action 58 | setLocation = val => { 59 | this.location = val 60 | } 61 | } 62 | 63 | export default createContext(new LocationStore()) 64 | -------------------------------------------------------------------------------- /src/store/weatherStore.ts: -------------------------------------------------------------------------------- 1 | import { observable, computed, action } from 'mobx' 2 | import Taro, { createContext, useContext } from '@tarojs/taro' 3 | import locationStore from './locationStore' 4 | import { HEFENG_BASE_URL, HEFENG_KEY } from '../common/const' 5 | import { array2object } from '../common/utils' 6 | 7 | // weatherStore 会用到 locationStore 内容 8 | const ls = useContext(locationStore) 9 | 10 | class WeatherStore { 11 | @observable now: any = {} // 当前天气 12 | @observable air: any = {} // 空气质量 13 | @observable hourly: any = [] // 时段天气 14 | @observable daily_forecast: any = [] // 一周天气 15 | @observable lifestyle: any = {} // 生活指数 16 | 17 | // 今天天气 18 | @computed get today() { 19 | return this.daily_forecast.length ? this.daily_forecast[0] : {} 20 | } 21 | // 明天天气 22 | @computed get tomorrow() { 23 | return this.daily_forecast.length ? this.daily_forecast[1] : {} 24 | } 25 | 26 | @action 27 | syncNow = () => { 28 | const { location } = ls 29 | return Taro.request({ 30 | url: HEFENG_BASE_URL + 'weather/now', 31 | data: { 32 | location: location.latitude + ',' + location.longitude, 33 | key: HEFENG_KEY 34 | } 35 | }).then(res => { 36 | this.now = res.data.HeWeather6[0].now 37 | }) 38 | } 39 | 40 | @action 41 | syncAir = () => { 42 | return Taro.request({ 43 | url: HEFENG_BASE_URL + 'air/now', 44 | data: { 45 | location: 'auto_ip', 46 | key: HEFENG_KEY 47 | } 48 | }).then(res => { 49 | this.air = res.data.HeWeather6[0].air_now_city 50 | }) 51 | } 52 | 53 | @action 54 | syncHourly = () => { 55 | const { location } = ls 56 | return Taro.request({ 57 | url: HEFENG_BASE_URL + 'weather/hourly', 58 | data: { 59 | location: location.latitude + ',' + location.longitude, 60 | key: HEFENG_KEY 61 | } 62 | }).then(res => { 63 | this.hourly = res.data.HeWeather6[0].hourly 64 | }) 65 | } 66 | 67 | @action 68 | syncDailyForecast = () => { 69 | const { location } = ls 70 | return Taro.request({ 71 | url: HEFENG_BASE_URL + 'weather/forecast', 72 | data: { 73 | location: location.latitude + ',' + location.longitude, 74 | key: HEFENG_KEY 75 | } 76 | }).then(res => { 77 | this.daily_forecast = res.data.HeWeather6[0].daily_forecast 78 | }) 79 | } 80 | 81 | @action 82 | syncLifestyle = () => { 83 | const { location } = ls 84 | return Taro.request({ 85 | url: HEFENG_BASE_URL + 'weather/lifestyle', 86 | data: { 87 | location: location.latitude + ',' + location.longitude, 88 | key: HEFENG_KEY 89 | } 90 | }).then(res => { 91 | const lifestyle = res.data.HeWeather6[0].lifestyle as any[] 92 | this.lifestyle = array2object(lifestyle, 'type') 93 | }) 94 | } 95 | } 96 | 97 | export default createContext(new WeatherStore()) 98 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------