Commit 70b1cac5 by 姜雷

添加蓝牙设备

parent 5f7b0ed3
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
LogoutCode, LogoutCode,
SuccessCode, SuccessCode,
OLD_BASE_SERVER_URL, OLD_BASE_SERVER_URL,
SHOWER_APP_URL,
} from '../constants/index'; } from '../constants/index';
export type ResponseDataEntity<T> = { export type ResponseDataEntity<T> = {
...@@ -35,22 +36,24 @@ const createFetch = (basePath: string) => { ...@@ -35,22 +36,24 @@ const createFetch = (basePath: string) => {
reqSource: 'wxmini', reqSource: 'wxmini',
}, },
url: basePath + entity.url, url: basePath + entity.url,
}).then(({ data }: ResponseEntity) => { }).then(
if (data.code === SuccessCode) { ({ data }: ResponseEntity): ResponseDataEntity<any> => {
return data; if (data.code === SuccessCode) {
} return data;
if (data.code === LogoutCode) { }
Taro.redirectTo({ if (data.code === LogoutCode) {
url: '/pages/Login/Login', Taro.redirectTo({
url: '/pages/Login/Login',
});
throw data;
}
Taro.showToast({
title: data.msg || '网络错误',
icon: 'none',
}); });
throw data; throw data;
} },
Taro.showToast({ );
title: data.msg || '网络错误',
icon: 'none',
});
throw data;
});
}; };
}; };
...@@ -58,5 +61,6 @@ export const baseFetch = createFetch(BASE_SERVER_URL); ...@@ -58,5 +61,6 @@ export const baseFetch = createFetch(BASE_SERVER_URL);
export const oldBaseFetch = createFetch(OLD_BASE_SERVER_URL); export const oldBaseFetch = createFetch(OLD_BASE_SERVER_URL);
export const customerFetch = createFetch(CUSTOMER_SERVER_URL); export const customerFetch = createFetch(CUSTOMER_SERVER_URL);
export const schoolMainFetch = createFetch(SCHOOL_MAIN_URL); export const schoolMainFetch = createFetch(SCHOOL_MAIN_URL);
export const showerFetch = createFetch(SHOWER_APP_URL);
export default createFetch; export default createFetch;
import { showerFetch } from './index';
type ShowerParams = {
deviceCode: string;
customerId: string;
campusId: number;
};
export type DeviceInfoResponse = {
code: string;
isOnlyBluetooth: number;
position: string;
};
/**
* 通过设备码获取设备信息
*/
export const fetchShowerInfo = (params: ShowerParams) =>
showerFetch({
url: `/dcxy/api/shower/devices/${params.deviceCode}`,
data: params,
});
type UsingParams = {
customerId: number;
};
/**
* 通过用户查询在使用的设备信息
*/
export const fetchUsingShowerInfo = (params: UsingParams) =>
showerFetch({
url: '/dcxy/api/shower/devices/inuseByCurrentUser',
data: params,
});
type StartParams = {
deviceCode: string;
customerId: string;
customerName: string;
customerPhone: string;
};
/**
* 开启设备使用
*/
export const startShowerEquipment = (params: StartParams) =>
showerFetch({
url: '/dcxy/api/shower/devices/inuseByCurrentUser',
data: params,
});
import Taro from '@tarojs/taro';
export const connectSocket = (url: string) =>
Taro.connectSocket({
url: url,
});
// .then(task => {
// task.onOpen(function() {
// console.log('onOpen');
// task.send({ data: 'xxx' });
// });
// task.onMessage(function(msg) {
// console.log('onMessage: ', msg);
// task.close({
// code: 1000,
// });
// });
// task.onError(function() {
// console.log('onError');
// });
// task.onClose(function(e) {
// console.log('onClose: ', e);
// });
// })
// .catch(err => {
// console.error(err);
// });
...@@ -39,6 +39,7 @@ class App extends Component { ...@@ -39,6 +39,7 @@ class App extends Component {
'pages/SelectCampus/SelectCampus', 'pages/SelectCampus/SelectCampus',
'pages/Content/Content', 'pages/Content/Content',
'pages/WebPage/WebPage', 'pages/WebPage/WebPage',
'pages/Shower/Shower',
], ],
window: { window: {
backgroundTextStyle: 'light', backgroundTextStyle: 'light',
......
...@@ -8,6 +8,7 @@ export const OLD_BASE_SERVER_URL = 'https://in-dev-selfbase.168cad.top'; ...@@ -8,6 +8,7 @@ export const OLD_BASE_SERVER_URL = 'https://in-dev-selfbase.168cad.top';
export const BASE_SERVER_URL = 'https://ex-dev-dcxy-base-app.168cad.top'; export const BASE_SERVER_URL = 'https://ex-dev-dcxy-base-app.168cad.top';
export const CUSTOMER_SERVER_URL = export const CUSTOMER_SERVER_URL =
'https://ex-dev-dcxy-customer-app.168cad.top'; 'https://ex-dev-dcxy-customer-app.168cad.top';
export const SCHOOL_MAIN_URL = export const SCHOOL_MAIN_URL = 'https://internal-dev-school-main.168cad.top';
'https://internal-dev-school-main.168cad.top';
export const ANN_LINK_URL = 'https://ex-dev-wx.168cad.top/announcement/'; export const ANN_LINK_URL = 'https://ex-dev-wx.168cad.top/announcement/';
export const SHOWER_APP_URL = 'http://ex-shower-app-server.168cad.top';
export const SOCKET_URL = 'ws://hard-tcp-server.nat123.net:29800';
...@@ -118,12 +118,18 @@ ...@@ -118,12 +118,18 @@
width: 40px; width: 40px;
height: 20px; height: 20px;
} }
.Home-HairDryer { .Home-Item {
position: relative; position: relative;
height: 192px; height: 192px;
margin-top: 68px; margin-top: 68px;
display: flex; display: flex;
align-items: center; align-items: center;
}
.Home-Item-arrow {
width: 15px;
height: 23px;
}
.Home-HairDryer {
justify-content: flex-end; justify-content: flex-end;
.Home-HairDryer-shadow { .Home-HairDryer-shadow {
position: absolute; position: absolute;
...@@ -139,10 +145,23 @@ ...@@ -139,10 +145,23 @@
margin-right: 50px; margin-right: 50px;
margin-top: 20px; margin-top: 20px;
} }
.Home-HairDryer-arrow { .Home-Item-arrow {
margin-left: 10px;
}
}
.Home-Shower {
margin-top: 40px;
.bg {
height: 225px;
transform: scale(1.1);
}
.Home-Shower-text {
color: #fff;
font-size: 32px;
margin-left: 50px;
}
.Home-Item-arrow {
margin-left: 10px; margin-left: 10px;
width: 15px;
height: 23px;
} }
} }
.Home-Announcement { .Home-Announcement {
......
...@@ -13,6 +13,7 @@ import HBeanIcon from '../../images/home/icon_chuifengdou@2x.png'; ...@@ -13,6 +13,7 @@ import HBeanIcon from '../../images/home/icon_chuifengdou@2x.png';
import TBeanIcon from '../../images/home/icon_tongyongdou@2x.png'; import TBeanIcon from '../../images/home/icon_tongyongdou@2x.png';
import UserHeaderM from '../../images/home/img_boy_touxiang@2x.png'; import UserHeaderM from '../../images/home/img_boy_touxiang@2x.png';
import UserHeaderF from '../../images/home/img_girl_touxiang@2x.png'; import UserHeaderF from '../../images/home/img_girl_touxiang@2x.png';
import ShowerIcon from '../../images/home/ic_linyu@2x.png';
import './Home.scss'; import './Home.scss';
import { connect } from '@tarojs/redux'; import { connect } from '@tarojs/redux';
...@@ -156,6 +157,12 @@ class Home extends Component { ...@@ -156,6 +157,12 @@ class Home extends Component {
}); });
} }
goShower() {
Taro.navigateTo({
url: '/pages/Shower/Shower',
});
}
toggleBarMenu() { toggleBarMenu() {
this.setState(({ barMenuVisiable }: PageState) => ({ this.setState(({ barMenuVisiable }: PageState) => ({
barMenuVisiable: !barMenuVisiable, barMenuVisiable: !barMenuVisiable,
...@@ -251,12 +258,20 @@ class Home extends Component { ...@@ -251,12 +258,20 @@ class Home extends Component {
</View> </View>
</View> </View>
</View> </View>
<View className='Home-HairDryer' onClick={this.goBarCode}> <View className='Home-Item Home-HairDryer' onClick={this.goBarCode}>
<Image className='bg' src={HairDryerBg} /> <Image className='bg' src={HairDryerBg} />
<View className='Home-HairDryer-shadow' /> <View className='Home-HairDryer-shadow' />
<View className='Home-HairDryer-text'> <View className='Home-HairDryer-text'>
吹风 吹风
<Image className='Home-HairDryer-arrow' src={HairDryerArr} /> <Image className='Home-Item-arrow' src={HairDryerArr} />
</View>
</View>
<View className='Home-Item Home-Shower' onClick={this.goShower}>
<Image className='bg' src={ShowerIcon} />
<View className='Home-Shower-shadow' />
<View className='Home-Shower-text'>
洗浴
<Image className='Home-Item-arrow' src={HairDryerArr} />
</View> </View>
</View> </View>
{annItem.id ? ( {annItem.id ? (
......
.Shower {
padding-top: 126px;
.equipment-info-box {
width: 630px;
height: 572px;
margin: 0 auto;
border-radius: 176px;
border: 2px solid #dfdfdf;
background-color: #f1f1f1;
}
.equipment-info {
width: 610px;
height: 552px;
border-radius: 176px;
margin: 10px auto 0;
background-color: #fff;
overflow: hidden;
.equipment-title {
margin-top: 114px;
font-size: 44px;
text-align: center;
&.matched {
color: #6180f4;
}
}
.equipment-line {
display: block;
width: 460px;
height: 8px;
margin: 46px auto 0;
}
.equipment-noCode {
margin-top: 108px;
text-align: center;
font-size: 32px;
color: #666;
}
.equipment-list {
margin-top: 64px;
}
.equipment-item {
display: flex;
margin-bottom: 20px;
align-items: center;
font-size: 32px;
.equipment-label {
margin-left: 110px;
}
.equipment-value {
width: 260px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.btn-box {
margin: 72px auto 0;
width: 534px;
display: flex;
justify-content: space-between;
align-items: center;
.shower-scan-btn {
width: 116px;
height: 116px;
border-radius: 50%;
&:active {
box-shadow: #ddd 0 0 40px;
}
}
.shower-start-btn {
width: 276px;
height: 128px;
border-radius: 50px;
&:active {
box-shadow: #ddd 0 0 40px;
}
}
}
}
import './Shower.scss';
import Taro, { Component, Config } from '@tarojs/taro';
import { View, Image, Text } from '@tarojs/components';
import ShowerStart from '../../images/shower/ic_sel@2x.png';
import ShowerCantStart from '../../images/shower/ic_nor@2x.png';
import LineImg from '../../images/shower/img_nor@2x.png';
import ScanIcon from '../../images/shower/ic_saoma@2x.png';
import { shareHandle } from '@/common/shareMethod';
import {
fetchShowerInfo,
DeviceInfoResponse,
startShowerEquipment,
fetchUsingShowerInfo,
} from '@/api/shower';
import { connect } from '@tarojs/redux';
import { Customer } from '@/types/Customer/Customer';
import { ComponentClass } from 'react';
import { ResponseDataEntity } from '@/api';
import { connectSocket } from '@/api/socket';
import { SOCKET_URL } from '@/constants';
type PageStateProps = {
userinfo: Customer;
};
type PageDispatchProps = {};
type IProps = PageStateProps & PageDispatchProps;
type PageState = {
bluetoothState: boolean;
code: string;
isOnlyBluetooth: number | undefined;
position: string;
deviceId: string;
task: Taro.SocketTask | null;
timer: number | undefined;
};
interface Shower {
props: IProps;
state: PageState;
}
@connect(({ userinfo }) => ({
userinfo,
}))
class Shower extends Component {
config: Config = {
navigationBarTitleText: '洗 浴',
};
constructor(props) {
super(props);
this.state = {
bluetoothState: false,
code: '',
isOnlyBluetooth: undefined,
position: '',
deviceId: '',
task: null,
timer: undefined,
};
}
onShareAppMessage = shareHandle;
componentWillMount() {
this.openBluetooth();
this.connectDeviceSocket();
this.checkUsingDevice();
}
componentWillUnmount() {
this.closeBluetooth();
}
connectDeviceSocket() {
connectSocket(SOCKET_URL).then(task => {
this.setState({
task: task,
});
task.onMessage(msg => {
console.log('onMessage: ', msg);
const data: string = msg.data;
if (data.length > 100) {
console.log('传递[]');
} else if (data.length > 20) {
} else {
}
});
});
}
checkUsingDevice() {
const { userinfo } = this.props;
fetchUsingShowerInfo({
customerId: userinfo.customerId,
})
.then(res => {
const data: DeviceInfoResponse | null = res.data;
if (data) {
this.setState({
code: data.code,
isOnlyBluetooth: data.isOnlyBluetooth,
position: data.position,
});
}
})
.catch(err => {
console.error(err);
});
}
openBluetooth() {
Taro.openBluetoothAdapter()
.then(res => {
console.log(res);
this.setState({
bluetoothState: true,
});
})
.catch(err => {
console.error(err);
Taro.onBluetoothAdapterStateChange(res => {
console.log('adapterState changed, now is', res);
if (res.available) {
this.setState({
bluetoothState: true,
});
}
});
});
}
checkBluetoothState() {
const { bluetoothState } = this.state;
return bluetoothState;
}
closeBluetooth() {
Taro.closeBluetoothAdapter();
}
warnBluetoothOpen() {
Taro.showModal({
title: '警告',
content: '为保障正常使用,请打开蓝牙功能',
});
}
scanEquipment() {
Taro.scanCode({
onlyFromCamera: true,
scanType: ['qrCode'],
})
.then(res => {
console.log(res);
const { result } = res;
const { userinfo } = this.props;
fetchShowerInfo({
deviceCode: result,
customerId: userinfo.customerId.toString(),
campusId: userinfo.areaId,
})
.then((res: ResponseDataEntity<DeviceInfoResponse>) => {
const data = res.data;
this.setState({
code: data.code,
isOnlyBluetooth: data.isOnlyBluetooth,
position: data.position,
});
})
.catch(err => {
console.error(err);
Taro.showToast({
title: err.msg || '请扫描正确的设备码',
icon: 'none',
});
});
})
.catch(err => {
console.error(err);
});
}
startDevicesDiscovery() {
if (this.checkBluetoothState()) {
return Taro.startBluetoothDevicesDiscovery()
.then(() => {
const { code, isOnlyBluetooth } = this.state;
return new Promise(resolve => {
Taro.onBluetoothDeviceFound(res => {
const devices = res.devices;
if (devices[0].name === code) {
let deviceId = devices[0].deviceId;
Taro.stopBluetoothDevicesDiscovery();
this.createConnection(deviceId);
this.setState({
deviceId,
});
resolve(deviceId);
}
});
if (isOnlyBluetooth) {
setTimeout(() => {}, 10000);
} else {
setTimeout(() => {}, 5000);
}
});
})
.catch(err => {
// Taro.stopBluetoothDevicesDiscovery();
Taro.hideLoading();
console.error('startBluetoothDevicesDiscovery: ', err);
this.warnBluetoothOpen();
});
} else {
this.warnBluetoothOpen();
throw '未开启蓝牙';
}
}
createConnection(deviceId: string) {
console.log(deviceId);
Taro.createBLEConnection({
deviceId,
});
Taro.onBLEConnectionStateChange(res => {
console.log(
`device ${res.deviceId} state has changed, connected: ${res.connected}`,
);
});
}
startUseShower() {
Taro.showLoading({
title: '开启中',
mask: true,
});
this.startDevicesDiscovery()
.then(() => {
const { code } = this.state;
const { userinfo } = this.props;
startShowerEquipment({
deviceCode: code,
customerId: userinfo.customerId.toString(),
customerName: userinfo.customerName,
customerPhone: userinfo.customerPhone,
})
.then(res => {
console.log(res);
Taro.showToast({
title: '开启成功!',
});
})
.catch(err => {
console.error(err);
Taro.showToast({
title: err.msg || '开启失败!',
icon: 'none',
});
});
})
.catch(err => {
console.error(err);
Taro.hideLoading();
});
}
render() {
const { code, position } = this.state;
return (
<View className='Shower'>
<View className='equipment-info-box'>
<View className='equipment-info'>
{code ? (
<View className='equipment-title matched'>已匹配设备</View>
) : (
<View className='equipment-title'>未匹配设备</View>
)}
<Image className='equipment-line' src={LineImg} />
{code ? (
<View className='equipment-list'>
<View className='equipment-item'>
<Text className='equipment-label'>设备编号:</Text>
<Text className='equipment-value'>{code}</Text>
</View>
<View className='equipment-item'>
<Text className='equipment-label'>设备位置:</Text>
<Text className='equipment-value'>{position}</Text>
</View>
</View>
) : (
<View className='equipment-noCode'>点击左下角扫一扫完成匹配</View>
)}
</View>
</View>
<View className='btn-box'>
<Image
className='shower-scan-btn'
src={ScanIcon}
onClick={this.scanEquipment}
/>
{code ? (
<Image
className='shower-start-btn'
src={ShowerStart}
onClick={this.startUseShower}
/>
) : (
<Image className='shower-start-btn' src={ShowerCantStart} />
)}
</View>
</View>
);
}
}
export default Shower as ComponentClass<PageStateProps, PageState>;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment