feat: 组件与页面
@ -53,7 +53,9 @@
|
|||||||
"@tarojs/runtime": "3.6.23",
|
"@tarojs/runtime": "3.6.23",
|
||||||
"@tarojs/shared": "3.6.23",
|
"@tarojs/shared": "3.6.23",
|
||||||
"@tarojs/taro": "3.6.23",
|
"@tarojs/taro": "3.6.23",
|
||||||
|
"axios": "^1.6.8",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
|
"qs": "^6.12.0",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0"
|
"react-dom": "^18.0.0"
|
||||||
},
|
},
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
"ignore": [],
|
"ignore": [],
|
||||||
"disablePlugins": [],
|
"disablePlugins": [],
|
||||||
"outputPath": ""
|
"outputPath": ""
|
||||||
}
|
},
|
||||||
|
"packNpmRelationList": []
|
||||||
},
|
},
|
||||||
"compileType": "miniprogram",
|
"compileType": "miniprogram",
|
||||||
"libVersion": "2.30.3",
|
"libVersion": "2.30.3",
|
||||||
|
@ -2,6 +2,7 @@ export default defineAppConfig({
|
|||||||
pages: [
|
pages: [
|
||||||
// 首页y以及首页tab页内容
|
// 首页y以及首页tab页内容
|
||||||
'pages/index/index', // 首页
|
'pages/index/index', // 首页
|
||||||
|
'pages/login/index', // 登录页
|
||||||
|
|
||||||
// 被其他页面引用的组件不要写在这里
|
// 被其他页面引用的组件不要写在这里
|
||||||
// 'pages/iot/index', // 物联网
|
// 'pages/iot/index', // 物联网
|
||||||
@ -9,12 +10,14 @@ export default defineAppConfig({
|
|||||||
// 'pages/equipment/index', // 巡检设备
|
// 'pages/equipment/index', // 巡检设备
|
||||||
|
|
||||||
// 跳转页面
|
// 跳转页面
|
||||||
|
'pages/weatherStation/index', // 气象站
|
||||||
'pages/msgDetail/index', // 消息详情
|
'pages/msgDetail/index', // 消息详情
|
||||||
],
|
],
|
||||||
window: {
|
window: {
|
||||||
backgroundTextStyle: 'light',
|
backgroundTextStyle: 'light',
|
||||||
navigationBarBackgroundColor: '#0fc87c',
|
navigationBarBackgroundColor: '#0fc87c',
|
||||||
navigationBarTitleText: 'WeChat',
|
navigationBarTitleText: 'WeChat',
|
||||||
navigationBarTextStyle: 'white'
|
navigationBarTextStyle: 'white',
|
||||||
|
navigationStyle: 'custom'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
BIN
src/assets/images/bg.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
src/assets/images/icons/back.png
Normal file
After Width: | Height: | Size: 164 B |
BIN
src/assets/images/icons/data/icon1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/images/icons/data/icon2.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/images/icons/data/icon3.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/images/icons/data/icon4.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/assets/images/icons/data/icon5.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/images/icons/data/icon6.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
6
src/assets/images/icons/data/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export {default as icon1} from './icon1.png';
|
||||||
|
export {default as icon2} from './icon2.png';
|
||||||
|
export {default as icon3} from './icon3.png';
|
||||||
|
export {default as icon4} from './icon4.png';
|
||||||
|
export {default as icon5} from './icon5.png';
|
||||||
|
export {default as icon6} from './icon6.png';
|
BIN
src/assets/images/icons/question-line.png
Normal file
After Width: | Height: | Size: 706 B |
BIN
src/assets/images/loginBg.png
Normal file
After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 868 B After Width: | Height: | Size: 895 B |
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 585 B After Width: | Height: | Size: 575 B |
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 633 B |
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 537 B |
20
src/components/customized/navigation/index.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Image, View } from "@tarojs/components"
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import arrow from '../../../assets/images/icons/back.png'
|
||||||
|
|
||||||
|
const HeaderNation = ({ title = '' }) => {
|
||||||
|
const showBack = Taro.getCurrentPages().length > 1
|
||||||
|
return (
|
||||||
|
<View className="w-full h-[180rpx] flex items-center justify-between pt-[90rpx] px-3">
|
||||||
|
<View className="w-8 flex justify-center" onClick={() => {
|
||||||
|
Taro.navigateBack()
|
||||||
|
}}>
|
||||||
|
{ showBack ? <Image src={arrow} className="w-6 h-6"></Image> : null }
|
||||||
|
</View>
|
||||||
|
<View className=" from-neutral-100 text-base text-center" style={{ color: 'white' }}>{ title }</View>
|
||||||
|
<View className="w-8"></View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HeaderNation;
|
6
src/config/axios/errorCode.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
'401': '认证失败,无法访问系统资源',
|
||||||
|
'403': '当前操作没有权限',
|
||||||
|
'404': '访问资源不存在',
|
||||||
|
default: '系统未知错误,请反馈给管理员'
|
||||||
|
}
|
0
src/config/axios/index.ts
Normal file
109
src/config/axios/service.ts
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import axios, { AxiosError, AxiosInstance, AxiosRequestHeaders, AxiosResponse, InternalAxiosRequestConfig } from "axios";
|
||||||
|
import { axiosConfig } from "..";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import qs from 'qs'
|
||||||
|
import errorCode from './errorCode'
|
||||||
|
|
||||||
|
// 请求白名单,无须token的接口
|
||||||
|
const whiteList: string[] = ['/login', '/refresh-token']
|
||||||
|
|
||||||
|
// 是否正在刷新中
|
||||||
|
let isRefreshToken = false
|
||||||
|
// 请求队列
|
||||||
|
let requestList: any[] = []
|
||||||
|
|
||||||
|
// 创建实例
|
||||||
|
const service: AxiosInstance = axios.create({
|
||||||
|
baseURL: axiosConfig.baseUrl,
|
||||||
|
timeout: axiosConfig.timeout,
|
||||||
|
withCredentials: axiosConfig.withCredentials
|
||||||
|
});
|
||||||
|
|
||||||
|
const getAccessToken = () => {
|
||||||
|
return Taro.getStorageSync('token')
|
||||||
|
}
|
||||||
|
|
||||||
|
// request拦截器
|
||||||
|
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||||
|
// 是否需要设置 token
|
||||||
|
let isToken = (config!.headers || {}).isToken === false
|
||||||
|
whiteList.some((v) => {
|
||||||
|
if (config.url) {
|
||||||
|
config.url.indexOf(v) > -1
|
||||||
|
return (isToken = false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (getAccessToken() && !isToken) {
|
||||||
|
; (config as any).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
|
||||||
|
}
|
||||||
|
const params = config.params || {}
|
||||||
|
const data = config.data || false
|
||||||
|
|
||||||
|
if (
|
||||||
|
config.method?.toUpperCase() === 'POST' &&
|
||||||
|
(config.headers as AxiosRequestHeaders)['Content-Type'] ===
|
||||||
|
'application/x-www-form-urlencoded'
|
||||||
|
) { config.data = qs.stringify(data) }
|
||||||
|
|
||||||
|
// get参数编码
|
||||||
|
if (config.method?.toUpperCase() === 'GET' && params) {
|
||||||
|
config.params = {}
|
||||||
|
const paramsStr = qs.stringify(params, { allowDots: true })
|
||||||
|
if (paramsStr) {
|
||||||
|
config.url = config.url + '?' + paramsStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}, (error: AxiosError) => {
|
||||||
|
Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// response 拦截器
|
||||||
|
service.interceptors.response.use(
|
||||||
|
async (response: AxiosResponse<any>) => {
|
||||||
|
let { data } = response
|
||||||
|
const config = response.config
|
||||||
|
if (!data) { throw new Error() }
|
||||||
|
|
||||||
|
const code = data.code || 200 // 默认成功
|
||||||
|
// 获取错误信息
|
||||||
|
const msg = data.msg || errorCode[code] || errorCode['default']
|
||||||
|
|
||||||
|
if (code === 401) {
|
||||||
|
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
|
||||||
|
// isRefreshToken false 未开始刷新token 进入刷新token程序
|
||||||
|
if (!isRefreshToken) {
|
||||||
|
isRefreshToken = true
|
||||||
|
try {
|
||||||
|
// 使用默认的用户名密码进行登录操作
|
||||||
|
const token = 'request login'
|
||||||
|
Taro.setStorageSync('token', token)
|
||||||
|
config.headers!.Authorization = 'Bearer ' + getAccessToken()
|
||||||
|
requestList.forEach((cb: any) => { cb() })
|
||||||
|
requestList = []
|
||||||
|
return service(config)
|
||||||
|
} catch (e) {
|
||||||
|
// 刷新失败时 处理错误
|
||||||
|
requestList = [] // 清空队列
|
||||||
|
return Promise.reject()
|
||||||
|
} finally {
|
||||||
|
requestList = []
|
||||||
|
isRefreshToken = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 正在尝试刷新token,放入等待队列
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
requestList.push(() => {
|
||||||
|
config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||||
|
resolve(service(config))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (code === 500) {
|
||||||
|
return Promise.reject(new Error(msg))
|
||||||
|
} else {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
5
src/config/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const axiosConfig = {
|
||||||
|
baseUrl: '',
|
||||||
|
timeout: 30000,
|
||||||
|
withCredentials: false // 禁用 Cookie 等信息
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { Image, View } from "@tarojs/components";
|
|||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Iot from "../iot";
|
import Iot from "../iot";
|
||||||
|
import basicBg from '../../assets/images/bg.png'
|
||||||
import MsgCenter from "../msgCenter";
|
import MsgCenter from "../msgCenter";
|
||||||
import EquipMent from "../equipment";
|
import EquipMent from "../equipment";
|
||||||
import tab1Icon from '../../assets/images/tab/tab1.png'
|
import tab1Icon from '../../assets/images/tab/tab1.png'
|
||||||
@ -12,10 +13,23 @@ import tab3Icon from '../../assets/images/tab/tab3.png'
|
|||||||
import tab3Icon_selected from '../../assets/images/tab/tab3_selected.png'
|
import tab3Icon_selected from '../../assets/images/tab/tab3_selected.png'
|
||||||
|
|
||||||
import Taro from "@tarojs/taro";
|
import Taro from "@tarojs/taro";
|
||||||
|
import HeaderNation from "../../components/customized/navigation";
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const [curSelectedItem, setCurSelectedItem] = useState('iot')
|
const [curSelectedItem, setCurSelectedItem] = useState('iot')
|
||||||
Taro.setNavigationBarTitle({ title: '物联网' })
|
Taro.setNavigationBarTitle({ title: '物联网' })
|
||||||
|
|
||||||
|
// 判断权限,没有权限进入登录页面
|
||||||
|
const handleAuth = () => {
|
||||||
|
const token = Taro.getStorageSync('token')
|
||||||
|
if (token) {
|
||||||
|
// Taro.redirectTo({ url: '/pages/login/index' })
|
||||||
|
} else {
|
||||||
|
Taro.redirectTo({ url: '/pages/login/index' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleAuth()
|
||||||
|
|
||||||
const bottomMenuList = [
|
const bottomMenuList = [
|
||||||
{
|
{
|
||||||
id: 'iot',
|
id: 'iot',
|
||||||
@ -41,7 +55,10 @@ const Index = () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="h-[100vh] flex flex-col-reverse">
|
<View
|
||||||
|
className="h-[100vh] flex flex-col-reverse"
|
||||||
|
style={{ 'backgroundImage': `url(${basicBg})`, 'backgroundSize': '100% auto', 'backgroundRepeat': 'no-repeat' }}
|
||||||
|
>
|
||||||
<View className="flex justify-evenly h-[6rem] align-top">
|
<View className="flex justify-evenly h-[6rem] align-top">
|
||||||
{
|
{
|
||||||
bottomMenuList.map(item => {
|
bottomMenuList.map(item => {
|
||||||
@ -79,6 +96,15 @@ const Index = () => {
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
|
<HeaderNation title={
|
||||||
|
curSelectedItem === 'iot'
|
||||||
|
? '物联网'
|
||||||
|
: curSelectedItem === 'msg'
|
||||||
|
? '消息中心'
|
||||||
|
: curSelectedItem === 'equip'
|
||||||
|
? '巡检设备'
|
||||||
|
: ''
|
||||||
|
} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ import onlineIcon from '../../assets/images/icons/online.png'
|
|||||||
import offlineIcon from '../../assets/images/icons/offline.png'
|
import offlineIcon from '../../assets/images/icons/offline.png'
|
||||||
import errorIcon from '../../assets/images/icons/error.png'
|
import errorIcon from '../../assets/images/icons/error.png'
|
||||||
import img from '../../assets/images/img.png'
|
import img from '../../assets/images/img.png'
|
||||||
|
import Taro from "@tarojs/taro"
|
||||||
|
|
||||||
const Iot = () => {
|
const Iot = () => {
|
||||||
const [selectedMenuId, setSelectedMenuId] = useState('all')
|
const [selectedMenuId, setSelectedMenuId] = useState('all')
|
||||||
@ -40,24 +41,30 @@ const Iot = () => {
|
|||||||
return (
|
return (
|
||||||
<View className="p-1 h-full">
|
<View className="p-1 h-full">
|
||||||
<View className="h-full flex flex-col">
|
<View className="h-full flex flex-col">
|
||||||
<View className="flex px-2 py-1 border-b-2 border-slate-100">
|
<View className="flex px-2 py-1">
|
||||||
{
|
{
|
||||||
topMenus.map(item => {
|
topMenus.map(item => {
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
className={`p-1 px-3 ${selectedMenuId === item.id ? 'text-lime-600 border-b-4 border-lime-800' : ''}`}
|
className={`p-1 px-3 ${selectedMenuId === item.id ? 'border-b-4 border-lime-800' : ''}`}
|
||||||
key={item.id}
|
key={item.id}
|
||||||
|
style={{ color: 'white' }}
|
||||||
onClick={() => setSelectedMenuId(item.id)}
|
onClick={() => setSelectedMenuId(item.id)}
|
||||||
>{ item.title }</View>
|
>{ item.title }</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
<View className="h-full border flex flex-col space-y-2 p-2 rounded-sm border-slate-50">
|
<View className="h-full flex flex-col space-y-3 p-2 rounded-sm">
|
||||||
{
|
{
|
||||||
dataList.map(item => {
|
dataList.map(item => {
|
||||||
return (
|
return (
|
||||||
<View className="border border-slate-300 px-4 py-2 rounded-md shadow-lg flex space-x-4">
|
<View
|
||||||
|
className="px-4 py-2 rounded-md shadow-xl flex space-x-4 bg-white border border-slate-200"
|
||||||
|
onClick={() => {
|
||||||
|
Taro.navigateTo({ url: '/pages/weatherStation/index?id=' + item.id })
|
||||||
|
}}
|
||||||
|
>
|
||||||
<View className="w-12 h-12 relative top-1">
|
<View className="w-12 h-12 relative top-1">
|
||||||
<Image src={img} className="w-12 h-12"></Image>
|
<Image src={img} className="w-12 h-12"></Image>
|
||||||
</View>
|
</View>
|
||||||
|
3
src/pages/login/index.config.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '登录'
|
||||||
|
})
|
76
src/pages/login/index.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { Button, Image, Input, Text, View } from "@tarojs/components"
|
||||||
|
import loginBg from '../../assets/images/loginBg.png'
|
||||||
|
import questionIcon from '../../assets/images/icons/question-line.png'
|
||||||
|
import Taro from "@tarojs/taro"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
|
const Login = () => {
|
||||||
|
const [username, setUserName] = useState('')
|
||||||
|
const [password, setPassWord] = useState('')
|
||||||
|
const handleLogin = () => {
|
||||||
|
if (!username || !password) return
|
||||||
|
Taro.setStorageSync('username', username)
|
||||||
|
Taro.setStorageSync('password', password)
|
||||||
|
// 请求完成后跳转
|
||||||
|
Taro.showLoading({ title: '加载中...'})
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.setStorageSync('token', 'testToken')
|
||||||
|
Taro.hideLoading()
|
||||||
|
Taro.redirectTo({ url: '/pages/index/index' })
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="w-full h-[100vh] relative">
|
||||||
|
<Image src={loginBg} className="w-full h-full" />
|
||||||
|
<View className="absolute left-0 top-20 pt-3">
|
||||||
|
<View className="text-slate-800 font-bold text-2xl px-6 pt-4">
|
||||||
|
您好,
|
||||||
|
</View>
|
||||||
|
<View className="text-slate-800 font-bold text-2xl px-6 pt-3">
|
||||||
|
欢迎登录
|
||||||
|
</View>
|
||||||
|
<View className="w-[100vw] mt-7 p-3 bg-white rounded-md">
|
||||||
|
<View className="p-2">
|
||||||
|
<View>用户名</View>
|
||||||
|
<View className=" border-b-2 border-slate-400 py-2">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
onInput={(_username) => setUserName((_username.target as any).value)}
|
||||||
|
></Input>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="p-2">
|
||||||
|
<View>密码</View>
|
||||||
|
<View className=" border-b-2 border-slate-400 py-2">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
onInput={(_password) => setPassWord((_password.target as any).value)}
|
||||||
|
></Input>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="flex flex-row-reverse p-1">
|
||||||
|
<View className="flex items-center">
|
||||||
|
<Image src={questionIcon} className="w-[1.1rem] h-[1.1rem]"></Image>
|
||||||
|
<Text className="text-[1rem] p-1" style={{ color: '#0fc87c' }}>忘记密码?</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="p-2">
|
||||||
|
<Button
|
||||||
|
className="rounded-full"
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(to right, #19b3c2, #0fc87e)',
|
||||||
|
color: 'white'
|
||||||
|
}}
|
||||||
|
onClick={() => handleLogin()}
|
||||||
|
>登录</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Login;
|
3
src/pages/weatherStation/index.config.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '气象站'
|
||||||
|
})
|
142
src/pages/weatherStation/index.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { Image, View } from "@tarojs/components"
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import { useState } from "react";
|
||||||
|
import img from '../../assets/images/img.png'
|
||||||
|
import basicBg from '../../assets/images/bg.png'
|
||||||
|
import HeaderNation from "../../components/customized/navigation";
|
||||||
|
import { icon1, icon2, icon3, icon4 } from "../../assets/images/icons/data";
|
||||||
|
|
||||||
|
const SubTitle = ({title = '', updateTime = ''}) => {
|
||||||
|
return (
|
||||||
|
<View className="w-full flex justify-between text-[.8rem] p-2 pb-1">
|
||||||
|
<View className=" font-bold">{ title }</View>
|
||||||
|
<View style={{ color: '#999999' }}>{ updateTime !== '' ? '数据更新于' + updateTime : '' }</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const IconLabelValue = ({ value, unit, label, img }) => {
|
||||||
|
return (
|
||||||
|
<View className="flex items-center justify-center">
|
||||||
|
<View className="flex flex-col items-start px-3 w-[calc(90% - 2.5rem)]">
|
||||||
|
<View className="flex items-end pb-2">
|
||||||
|
<View className="text-[1rem] font-bold text-primary">{ value }</View>
|
||||||
|
<View
|
||||||
|
className="text-slate-600 text-[.7rem] px-2"
|
||||||
|
style={{ 'color': '#252525A5' }}
|
||||||
|
>{ unit }</View>
|
||||||
|
</View>
|
||||||
|
<View className="text-[.7rem]">{ label }</View>
|
||||||
|
</View>
|
||||||
|
<View className="w-10 h-10 rounded-sm shadow-sm">
|
||||||
|
<Image src={img} className="w-full h-full"></Image>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const GridContainer = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<View className="border border-slate-200 px-4 py-3 rounded-md shadow-xl grid grid-cols-2">
|
||||||
|
{ children }
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const BasicStatus = (status = true) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{ backgroundColor: status ? '#0fc87c' : '#ff0000', 'color': 'white' }}
|
||||||
|
className={ 'text-[.7rem] px-[.4rem] py-[.1rem] ' + (status ? 'rounded-sm text-white' : '') }
|
||||||
|
>{ status ? '在线' : '离线' }</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const BasicInfoCard = ({ title, info, status, imgSrc }) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className="bg-white px-4 py-3 rounded-md shadow-xl flex space-x-4"
|
||||||
|
>
|
||||||
|
<View className="w-16 h-14">
|
||||||
|
<Image src={imgSrc} className="w-16 h-14"></Image>
|
||||||
|
</View>
|
||||||
|
<View className="w-full">
|
||||||
|
<View className="font-bold text-[.9rem]">{ title }</View>
|
||||||
|
<View className="text-[.8rem] text-slate-600 pb-2">{ info }</View>
|
||||||
|
<View className="flex">{ status ? BasicStatus() : '' }</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const WeatherStation = () => {
|
||||||
|
const baseId = Taro.getCurrentInstance().router?.params.id || 0; // 基地id
|
||||||
|
const [curDeviceId, setCurDeviceId] = useState('1')
|
||||||
|
const deviceList = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
title: '设备1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
title: '设备2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
title: '设备3'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const [data, setData] = useState([
|
||||||
|
{ genre: 'Sports', sold: 275 },
|
||||||
|
{ genre: 'Strategy', sold: 115 },
|
||||||
|
{ genre: 'Action', sold: 120 },
|
||||||
|
{ genre: 'Shooter', sold: 350 },
|
||||||
|
{ genre: 'Other', sold: 150 },
|
||||||
|
])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="h-[100vh] flex flex-col" style={{ 'backgroundImage': `url(${basicBg})`, 'backgroundSize': '100% auto', 'backgroundRepeat': 'no-repeat' }}>
|
||||||
|
<HeaderNation title="气象站" />
|
||||||
|
<View className="h-full flex flex-col px-1">
|
||||||
|
<View className="flex px-2 py-1">
|
||||||
|
{
|
||||||
|
deviceList.map(item => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={`p-1 px-3 ${curDeviceId === item.id ? 'text-lime-600 border-b-4 border-lime-800' : ''}`}
|
||||||
|
key={item.id}
|
||||||
|
style={{ color: 'white' }}
|
||||||
|
onClick={() => setCurDeviceId(item.id)}
|
||||||
|
>{ item.title }</View>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
<View className="h-full flex flex-col space-y-2 p-2 rounded-sm">
|
||||||
|
<BasicInfoCard imgSrc={img} title={'大棚环境监测一体机'} info={'BNYKLH09LK001'} status={true} />
|
||||||
|
<SubTitle title="实时数据" updateTime="2023-04-04" />
|
||||||
|
<GridContainer>
|
||||||
|
<View className="p-2 py-3">
|
||||||
|
<IconLabelValue value={27.34} label={'大气温度'} unit={'℃'} img={icon1} />
|
||||||
|
</View>
|
||||||
|
<View className="p-2 py-3">
|
||||||
|
<IconLabelValue value={27.34} label={'大气温度'} unit={'℃'} img={icon2} />
|
||||||
|
</View>
|
||||||
|
<View className="p-2 py-3">
|
||||||
|
<IconLabelValue value={27.34} label={'大气温度'} unit={'℃'} img={icon3} />
|
||||||
|
</View>
|
||||||
|
<View className="p-2 py-3">
|
||||||
|
<IconLabelValue value={27.34} label={'大气温度'} unit={'℃'} img={icon4} />
|
||||||
|
</View>
|
||||||
|
</GridContainer>
|
||||||
|
<SubTitle title="数据监测" />
|
||||||
|
<View className="border border-slate-200 px-4 py-3 rounded-md shadow-xl">
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WeatherStation;
|
@ -2,7 +2,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
content: ["./src/**/*.{html,js,ts,jsx,tsx}"],
|
content: ["./src/**/*.{html,js,ts,jsx,tsx}"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {}
|
extend: {},
|
||||||
|
textColor: {
|
||||||
|
'primary': '#0fc87c'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
// v3 版本的 tailwindcss 有些不同
|
// v3 版本的 tailwindcss 有些不同
|
||||||
|