线下汇款复制换行

This commit is contained in:
czm 2022-06-29 14:01:12 +08:00
commit a28d4859b0
13 changed files with 1063 additions and 32 deletions

View File

@ -30,7 +30,8 @@
"outputPath": "" "outputPath": ""
}, },
"disableUseStrict": false, "disableUseStrict": false,
"useCompilerPlugins": false "useCompilerPlugins": false,
"minifyWXML": true
}, },
"compileType": "miniprogram", "compileType": "miniprogram",
"libVersion": "2.24.5", "libVersion": "2.24.5",

View File

@ -10,3 +10,17 @@ export const userassets = () => {
method: "get", method: "get",
}) })
} }
//取色对比
export const productabsorbcontrast = () => {
return useRequest({
url: `/v1/mall/product/color/absorb/contrast`,
method: "get",
})
}
//订单统计
export const userorderStatistics = () => {
return useRequest({
url: `/v1/mall/user/orderStatistics`,
method: "get",
})
}

View File

@ -177,6 +177,13 @@ export default {
pages: [ pages: [
"index", "index",
] ]
} },
{
root: "pages/sampleComparison",
pages: [
"index",
]
},
] ]
} }

View File

@ -5,7 +5,7 @@
// export const BASE_URL = `http://192.168.0.89:40001/lymarket` // export const BASE_URL = `http://192.168.0.89:40001/lymarket`
// export const BASE_URL = `http://192.168.1.165:40001/lymarket` // 王霞 // export const BASE_URL = `http://192.168.1.165:40001/lymarket` // 王霞
// export const BASE_URL = `https://test.zzfzyc.com/lymarket` // 测试环境 // export const BASE_URL = `https://test.zzfzyc.com/lymarket` // 测试环境
// export const BASE_URL = `http://192.168.1.9:40001/lymarket` // 发 // export const BASE_URL = `http://192.168.1.30:40001/lymarket` // 发
// export const BASE_URL = `http://192.168.1.30:50001/lymarket` // 发 // export const BASE_URL = `http://192.168.1.30:50001/lymarket` // 发
// export const BASE_URL = `https://dev.zzfzyc.com/lymarket` // 开发环境 // export const BASE_URL = `https://dev.zzfzyc.com/lymarket` // 开发环境
// export const BASE_URL = `https://www.zzfzyc.com/lymarket` // 正式环境 // export const BASE_URL = `https://www.zzfzyc.com/lymarket` // 正式环境

View File

@ -17,7 +17,7 @@ export default memo(({show = true, onClose}:Param) => {
//复制功能 //复制功能
const clipboardData = () => { const clipboardData = () => {
Taro.setClipboardData({ Taro.setClipboardData({
data: '开户名称:佛山市浩川盛世科技有限公司; 开户银行:招商银行汾江支行; 转账汇款账号:62062342120001221231212', data: '开户名称:佛山市浩川盛世科技有限公司;\n\r 开户银行:招商银行汾江支行;\n\r 转账汇款账号62062342120001221231212;',
success: function (res) { success: function (res) {
Taro.showToast({ Taro.showToast({
icon: 'none', icon: 'none',

View File

@ -0,0 +1,90 @@
import { View } from "@tarojs/components";
import { memo, useEffect, useMemo, useState } from "react";
import Taro from "@tarojs/taro";
import { useBluetooth } from "@/use/contextBlueTooth"
import SearchInput from "@/components/searchInput";
import Popup from "./Popup"
import classnames from "classnames";
import styles from "../../css/linkBlueTooth.module.scss"
export default memo(() => {
const { state, init, startScan, connect, disconnect } = useBluetooth()
useEffect(() => {
init()
}, [])
const [linkStatus, setLinkStatus] = useState(1)
useEffect(() => {
if (!state.available) {
setLinkStatus(1)
} else if (state.available && state.connected?.name) {
setLinkStatus(3)
} else {
setLinkStatus(2)
}
console.log('aaa:::', state.connected)
}, [state.available, state.connected])
const linkName = useMemo(() => {
return state.connected?.localName || ''
}, [state.connected])
//链接设备
const onLinkListen = (item) => {
if (!state.connected && !state.connecting)
connect(item)
}
const [popupShow, setPopupShow] = useState(false)
//显示设备列表
const onFindDevice = () => {
if (linkStatus == 1) {
Taro.showToast({
title: '请打开蓝牙',
icon: 'none'
})
} else {
setPopupShow(true)
onFindEven()
}
}
const onFindEven = () => {
if (!state.discovering && !state.connected && !state.connecting)
startScan()
}
//断开链接
const onDisconnect = () => {
disconnect()
setPopupShow(false)
}
return (
<>
<View className={styles.main}>
<SearchInput title="蓝牙设备" showIcon={true} showBorder={false}>
<View className={styles.bluetooth_link} onClick={onFindDevice}>
<View className={classnames(styles.link_status, linkStatus == 3 && styles.link_statused, linkStatus == 2 && styles.link_statused_no)}></View>
{
linkStatus == 1 && <View className={classnames(styles.link_name, styles.link_name_no)}></View> ||
linkStatus == 2 && <View className={classnames(styles.link_name, styles.link_name_no_link)}></View> ||
linkStatus == 3 && <View className={classnames(styles.link_name)}>{linkName}</View>
}
</View>
</SearchInput>
<Popup
state={state}
show={popupShow}
onClose={() => setPopupShow(false)}
onLink={item => onLinkListen(item)}
onOff={onDisconnect}
onFind={onFindEven}
/>
</View>
</>
);
})

View File

@ -0,0 +1,73 @@
import { ScrollView, View } from "@tarojs/components"
import { memo, useEffect, useState } from "react"
import Loading from "@/components/loading"
import style from "../../css/popup.module.scss"
interface params {
state: any,
show: Boolean,
onClose: (Boolean) => void,
onLink: (any) => void,
children?: React.ReactNode
onOff: () => void,
onFind: () => void,
}
export default memo(({state, show=false, onClose, onLink, onOff, onFind}:params) => {
const [popupShow, setPopupShow] = useState(show)
useEffect(() => {
setPopupShow(show)
}, [show])
const onCloseListener = () => {
onClose(false)
}
return (
<>
{
popupShow&&<View className={style.popup}>
<View className={style.content}>
<View className={style.title}></View>
<View className={style.list}>
<ScrollView scrollY className={style.scroll}>
{
(state.devices&&state.devices.length > 0)&&state?.devices.map(item => {
return (
<View className={style.item} onClick={() => onLink(item)}>
<View>{item.name}</View>
{
(!state.connecting&&!state.connected)&&<View ></View>||
(state.connecting&&item.deviceId == state.connecting.deviceId)&&<View className={style.link_ing}>...</View>||
(state.connected&&item.deviceId == state.connected.deviceId)&&<View className={style.link_success}></View>
}
</View>
)
})||
<View className={style.noDevice}>
{
(!state.discovering)&& <>
<View>,</View>
<View className={style.n_item}>1.</View>
<View className={style.n_item}>2.</View>
<View className={style.n_item}>3.</View>
</>||
<View></View>
}
</View>
}
</ScrollView>
</View>
{
state.connected&&<View className={`${style.footer} ${style.footer_off}`} onClick={onOff}></View>||
(!state.connected&&state.discovering)&&<View className={`${style.footer} ${style.finding}`}><Loading width={30} color='orange'/></View>||
<View className={style.footer} onClick={onFind}></View>
}
</View>
<View className={style.mask} onClick={onCloseListener}></View>
</View>
}
</>
)
})

View File

@ -0,0 +1,42 @@
.main {
width: 690px;
height: 86px;
background: #ffffff;
border-radius: 10px;
margin-top: 24px;
margin-left: 30px;
.bluetooth_link {
display: flex;
align-items: center;
.link_status {
width: 12px;
height: 12px;
background: #f02409;
border-radius: 50%;
}
.link_statused {
background: #07C160;
}
.link_statused_no {
background: #f0ec09;
}
.link_name {
font-size: $font_size;
margin-left: 20px;
}
.link_name_no {
color: #f02409;
}
.link_name_no_link {
color: #f0ec09;
}
}
}

View File

@ -0,0 +1,90 @@
.popup{
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
.mask{
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
position: fixed;
top:0;
left:0;
z-index: 9;
}
.content{
z-index: 99;
background-color: #fff;
width: 75vw;
height: 600px;
position: fixed;
top: 50%;
left: 50%;
border-radius: 20px;
transform: translateX(-50%) translateY(-50%);
display: flex;
flex-direction: column;
font-size: 28px;
.title{
text-align: center;
margin: 20px;
}
.list{
height: 480px;
padding: 0 20px;
.scroll{
height: 100%;
}
.item{
margin-bottom: 20px;
display: flex;
justify-content: space-between;
border-bottom: 1px dashed #ccc;
padding: 15px 0;
color: #3b3b3b;
@mixin link{
font-size: 25px;
}
.link_success{
@include link;
color: green;
}
.link_ing {
color: orange;
}
}
.noDevice{
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #a8a8a8;
.n_item{
width: 100%;
text-align: left;
margin-top: 20px;
padding: 0 30px;
box-sizing: border-box;
}
}
}
.footer{
text-align: center;
padding: 20px 0;
background-color: #f1f1f1;
border-radius: 0 0 10px 10px;
display: flex;
justify-content: center;
align-items: center;
}
.finding{
color: orange;
}
.footer_off{
color: red;
}
}
}

View File

@ -94,7 +94,21 @@ page {
font-family: Microsoft YaHei, Microsoft YaHei-Regular; font-family: Microsoft YaHei, Microsoft YaHei-Regular;
font-weight: 400; font-weight: 400;
color: #707070; color: #707070;
} }
}
.color_bock {
width: 290px;
height: 290px;
border-radius: 50%;
}
.color_bocktwo {
width: 290px;
height: 290px;
border-radius: 50%;
} }
.nameColor { .nameColor {
@ -195,4 +209,45 @@ page {
margin-right: 37px; margin-right: 37px;
} }
} }
.bottomBox {
position: fixed;
bottom: 0;
width: 750px;
height: 182px;
background: #f3f3f3;
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.16);
overflow: hidden;
display: flex;
.leftBtn {
width: 264px;
height: 82px;
background: #ffffff;
border-radius: 42px;
font-size: 32px;
font-family: Microsoft YaHei, Microsoft YaHei-Regular;
font-weight: 400;
text-align: center;
color: #007aff;
line-height: 82px;
margin-top: 34px;
margin-left: 67px;
}
.rightBtn {
width: 264px;
height: 82px;
background: #007aff;
border-radius: 42px;
font-size: 32px;
font-family: Microsoft YaHei, Microsoft YaHei-Regular;
font-weight: 400;
text-align: center;
color: #ffffff;
line-height: 82px;
margin-top: 34px;
margin-left: 88px;
}
}
} }

View File

@ -1,14 +1,38 @@
import { Image, Text, Textarea, View } from "@tarojs/components" import { Image, Text, Textarea, View } from "@tarojs/components"
import Taro, { useDidShow, usePullDownRefresh, useRouter } from "@tarojs/taro"; import Taro, { useDidShow, usePullDownRefresh, useRouter } from "@tarojs/taro";
import { useBluetooth } from "@/use/contextBlueTooth" import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useBluetooth } from "../../use/contextBlueTooth"
import classnames from "classnames"; import classnames from "classnames";
import LinkBlueTooth from "./compoents/bluetooth/LinkBlueTooth";
import { toRgb } from '../../common/bluetooth/color/colorSpace'
import styles from './index.module.scss' import styles from './index.module.scss'
import { productabsorbcontrast } from "@/api/mine"
export default () => { export default () => {
//搜索参数
const [searchField, setSearchField] = useState({
l1: '',
a1: '',
b1: '',
l2: '',
a2: '',
b2: '',
})
const [colorList, setColorList] = useState({
one: null,
two: null
})
const { state: colorState, measureAndGetLab } = useBluetooth() const { state: colorState, measureAndGetLab } = useBluetooth()
const getLab = () => {
const getLab = async (val) => {
if (colorState.connected) { if (colorState.connected) {
measureAndGetLab() let res = await measureAndGetLab()
if (val === 1) {
setColorList({ ...colorList, one: res })
} else {
setColorList({ ...colorList, two: res })
}
} else { } else {
Taro.showToast({ Taro.showToast({
title: '请链接设备', title: '请链接设备',
@ -16,9 +40,84 @@ export default () => {
}) })
} }
} }
const getNowTime = () => {
const yy = new Date().getFullYear()
const MM = (new Date().getMonth() + 1) < 10 ? '0' + (new Date().getMonth() + 1) : (new Date().getMonth() + 1)
const dd = new Date().getDate() < 10 ? '0' + new Date().getDate() : new Date().getDate()
const HH = new Date().getHours() < 10 ? '0' + new Date().getHours() : new Date().getHours()
const mm = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes()
const ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds()
return yy + '-' + MM + '-' + dd + ' ' + HH + ':' + mm + ':' + ss
}
//监听lab数据变化
const [blueToothColor, setBlueToothColor] = useState('')
const [blueToothColorTwo, setBlueToothColorTwo] = useState('')
const [time, setTime] = useState('')
const [timeTwo, setTimeTwo] = useState('')
useEffect(() => {
if (colorState.deviceLab) {
if (colorList.one?.constructor === Object) {
const rgb = toRgb([colorList.one.L, colorList.one.a, colorList.one.b])
setBlueToothColor(`rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`)
setTime(getNowTime())
setSearchField({ ...searchField, l1: rgb[0], a1: rgb[1], b1: rgb[2] })
}
if (colorList.two?.constructor === Object) {
const rgb = toRgb([colorList.two.L, colorList.two.a, colorList.two.b])
setBlueToothColorTwo(`rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`)
setTimeTwo(getNowTime())
setSearchField({ ...searchField, l2: rgb[0], a2: rgb[1], b2: rgb[2] })
}
}
}, [colorList])
const reset = () => {
setBlueToothColor('')
setBlueToothColorTwo('')
setTime('')
setTime('')
setData('')
setResult('')
}
const [data, setData] = useState('')
const [result, setResult] = useState('')
const { fetchData } = productabsorbcontrast()
const handTake = async () => {
if (searchField.l1 === '') {
Taro.showToast({
title: '请填充基础样品',
icon: 'none'
})
return
}
if (searchField.l2 === '') {
Taro.showToast({
title: '请填充对比样品',
icon: 'none'
})
return
}
const query = {
...searchField
}
const res = await fetchData(query)
if (res.data) {
setData(res.data)
let diffarray = [
res.data.reddish && "偏红",
res.data.yellowish && "偏黄",
res.data.greenish && "偏绿",
res.data.bluish && "偏蓝",
res.data.whitish && "偏亮",
res.data.darker && "偏暗",
];
let resCont = diffarray.filter(item => item).join(",");
setResult(resCont)
}
}
return ( return (
<View className={styles.body}> <View className={styles.body}>
<View className={styles.topBox}> {/* <View className={styles.topBox}>
<View className={styles.leftBox}> <View className={styles.leftBox}>
<View className={styles.leftFont}></View> <View className={styles.leftFont}></View>
<View className={styles.borderBox}></View> <View className={styles.borderBox}></View>
@ -26,35 +125,82 @@ export default () => {
<View className={styles.notNet}></View> <View className={styles.notNet}></View>
</View> </View>
<Text className={classnames("iconfont icon-a-moreback", styles.iconMore)}></Text> <Text className={classnames("iconfont icon-a-moreback", styles.iconMore)}></Text>
</View> </View> */}
<LinkBlueTooth />
<View className={styles.contBox} > <View className={styles.contBox} >
<View className={styles.firstBox} style="margin-right:27px"> <View className={styles.firstBox} style="margin-right:27px">
<View className={styles.firstLeftName}></View> <View className={styles.firstLeftName} ></View>
<View className={styles.firstLeftbox}> {
<View className={styles.clickFont}></View> blueToothColor === '' &&
</View> <View className={styles.firstLeftbox} onClick={() => getLab(1)}>
<View className={styles.nameColor}>--</View> <View className={styles.clickFont} ></View>
</View>
}
{blueToothColor && <View className={classnames(styles.color_bock)} onClick={() => getLab(1)} style={{ background: blueToothColor }}></View>
}
{
time === '' && <View className={styles.nameColor}>--</View>
}
{
time && <View className={styles.nameColor}>{time}</View>
}
</View> </View>
<View className={styles.firstBox}> <View className={styles.firstBox}>
<View className={styles.firstLeftName}></View> <View className={styles.firstLeftName}></View>
<View className={styles.firstLeftbox}> {
<View className={styles.clickFont}></View> blueToothColorTwo === '' &&
</View> <View className={styles.firstLeftbox} onClick={() => getLab(2)}>
<View className={styles.nameColor}>--</View> <View className={styles.clickFont} ></View>
</View>
}
{blueToothColorTwo && <View className={classnames(styles.color_bocktwo)} onClick={() => getLab(2)} style={{ background: blueToothColorTwo }}></View>
}
{
timeTwo === '' && <View className={styles.nameColor}>--</View>
}
{
timeTwo && <View className={styles.nameColor}>{timeTwo}</View>
}
</View> </View>
</View> </View>
<View className={styles.reslutBox}> <View className={styles.reslutBox}>
<View className={styles.reslutTitle}> <View className={styles.reslutTitle}>
<View className={styles.titleLeft}></View> <View className={styles.titleLeft}></View>
{/* <View className={styles.titleRiht}>不合格</View> */} {
<View className={styles.titleRiht1}></View> (data as any).ciede_2000 >= 1 &&
</View> <View className={styles.titleRiht}></View>
{/* <View className={styles.notResult}>暂无数据</View> */} }
<View className={styles.notBox}> {
<View className={styles.notLeft}>色差值: 0.2</View> (data as any).ciede_2000 <= 1 &&
<View className={styles.notright}></View> <View className={styles.titleRiht1}></View>
}
</View> </View>
{
data === '' &&
<View className={styles.notResult}></View>
}
{
data &&
<View className={styles.notBox}>
<View className={styles.notLeft}>: {(data as any).ciede_2000}</View>
{
!(data as any).reddish && !(data as any).yellowish && !(data as any).greenish && !(data as any).bluish && !(data as any).whitish && !(data as any).darker &&
<View className={styles.notright}></View>
}
{
result &&
<View className={styles.notright}>{result}</View>
}
</View>
}
</View> </View>
</View> <View className={styles.bottomBox}>
<View className={styles.leftBtn} onClick={() => reset()}></View>
<View className={styles.rightBtn} onClick={() => handTake()}></View>
</View>
</View >
) )
} }

View File

@ -7,8 +7,9 @@ import { alert, goLink } from "@/common/common";
import useLogin from '@/use/useLogin' import useLogin from '@/use/useLogin'
import { BASE_URL } from '@/common/constant' import { BASE_URL } from '@/common/constant'
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { userassets } from "@/api/mine" import { userassets, userorderStatistics } from "@/api/mine"
import { formatPriceDiv } from "@/common/fotmat" import { formatPriceDiv } from "@/common/fotmat"
import { useDidShow, } from '@tarojs/taro'
export default () => { export default () => {
// 用户信息 // 用户信息
const { getSelfUserInfo } = useLogin(); const { getSelfUserInfo } = useLogin();
@ -23,10 +24,14 @@ export default () => {
alert.none("授权失败,请授权后再使用"); alert.none("授权失败,请授权后再使用");
}); });
} }
const { fetchData, state } = userassets() const { fetchData: Apiassets, state } = userassets()
const { fetchData: ApigetTotal, state: stateData } = userorderStatistics()
useEffect(() => { useEffect(() => {
fetchData(); Apiassets();
}, []) }, [])
useDidShow(() => {
ApigetTotal()
})
const checkGo = () => { const checkGo = () => {
if (adminUserInfo?.authentication_status === 0 || adminUserInfo?.authentication_status === 1 || adminUserInfo?.authentication_status === 2 || adminUserInfo?.authentication_status === 3) { if (adminUserInfo?.authentication_status === 0 || adminUserInfo?.authentication_status === 1 || adminUserInfo?.authentication_status === 2 || adminUserInfo?.authentication_status === 3) {
Taro.showModal({ Taro.showModal({
@ -50,7 +55,7 @@ export default () => {
} }
return ( return (
<View className={styles.main}> <View className={styles.main}>
<Header data={adminUserInfo} /> <Header data={adminUserInfo} MenuData={stateData} />
<Assets data={state} checkShow={() => checkGo()} /> <Assets data={state} checkShow={() => checkGo()} />
<Main /> <Main />
{/* {(adminUserInfo as any)?.authentication_status==1&&<Modal data={adminUserInfo}/>} */} {/* {(adminUserInfo as any)?.authentication_status==1&&<Modal data={adminUserInfo}/>} */}
@ -91,6 +96,7 @@ const Modal = memo((props: any) => {
// 头部 | 订单 // 头部 | 订单
const Header = memo((props: any) => { const Header = memo((props: any) => {
const { data } = props; const { data } = props;
console.log(props, 'propsprops')
let menu = [{ text: "待配布", icon: "icon-daipeibu", url: "/pages/orderList/index" }, { text: "待付款", icon: "icon-daifukuan", url: "/pages/orderList/index" }, let menu = [{ text: "待配布", icon: "icon-daipeibu", url: "/pages/orderList/index" }, { text: "待付款", icon: "icon-daifukuan", url: "/pages/orderList/index" },
{ text: "待发货", icon: "icon-daifahuo", url: "/pages/orderList/index" }, { text: "已发货", icon: "icon-yifahuo", url: "/pages/orderList/index" }, { text: "待发货", icon: "icon-daifahuo", url: "/pages/orderList/index" }, { text: "已发货", icon: "icon-yifahuo", url: "/pages/orderList/index" },
{ text: "退款/售后", icon: "icon-a-tuikuanshouhou", url: "/pages/salesAfterList/index" }]; { text: "退款/售后", icon: "icon-a-tuikuanshouhou", url: "/pages/salesAfterList/index" }];
@ -224,7 +230,37 @@ const Header = memo((props: any) => {
<Navigator hoverClass="none" url={item.url} className={styles['card-main-title-content-item']}> <Navigator hoverClass="none" url={item.url} className={styles['card-main-title-content-item']}>
<Text className={`iconfont ${item.icon}`}></Text> <Text className={`iconfont ${item.icon}`}></Text>
<View>{item.text}</View> <View>{item.text}</View>
<View className={styles['card-main-title-content-item-badge']}>{index == 2 ? "99+" : 10}</View> {
index == 0 && props.MenuData.data.wait_match !== 0 &&
<View className={styles['card-main-title-content-item-badge']}>
{props.MenuData.data.wait_match > 99 ? '99+' : props.MenuData.data.wait_match}
</View>
}
{
index == 1 && props.MenuData.data.wait_pay !== 0 &&
<View className={styles['card-main-title-content-item-badge']}>
{props.MenuData.data.wait_pay > 99 ? '99+' : props.MenuData.data.wait_pay}
</View>
}
{
index == 2 && props.MenuData.data.wait_shipped !== 0 &&
<View className={styles['card-main-title-content-item-badge']}>
{props.MenuData.data.wait_shipped > 99 ? '99+' : props.MenuData.data.wait_shipped}
</View>
}
{
index == 3 && props.MenuData.data.already_shipped !== 0 &&
<View className={styles['card-main-title-content-item-badge']}>
{props.MenuData.data.already_shipped > 99 ? '99+' : props.MenuData.data.already_shipped}
</View>
}
{
index == 4 && props.MenuData.data.after_return !== 0 &&
<View className={styles['card-main-title-content-item-badge']}>
{props.MenuData.data.after_return > 99 ? '99+' : props.MenuData.data.after_return}
</View>
}
</Navigator> </Navigator>
) )
}) })
@ -271,7 +307,7 @@ const Assets = (props: any) => {
// 功能 // 功能
const Main = memo(() => { const Main = memo(() => {
let menu = [{ text: "地址管理", icon: "icon-shoucang", url: "/pages/addressManager/index" }, { text: "码单管理", icon: "icon-shoucang", url: "/pages/weightList/index" }, let menu = [{ text: "地址管理", icon: "icon-daohang", url: "/pages/addressManager/index" }, { text: "码单管理", icon: "icon-a-yuanmadanmadanguanli", url: "/pages/weightList/index" },
{ text: "我的收藏", icon: "icon-shoucang" }, { text: "颜色对比", icon: "icon-yanseduibi", url: "/pages/sampleComparison/index" }, { text: "我的收藏", icon: "icon-shoucang" }, { text: "颜色对比", icon: "icon-yanseduibi", url: "/pages/sampleComparison/index" },
{ text: "分享推广", icon: "icon-fenxiang" }, { text: "团队邀请", icon: "icon-yaoqingtuandui" }] { text: "分享推广", icon: "icon-fenxiang" }, { text: "团队邀请", icon: "icon-yaoqingtuandui" }]
return ( return (

477
src/use/BlueToothCopy.tsx Normal file
View File

@ -0,0 +1,477 @@
import React, {useRef, useState } from "react"
import Taro from "@tarojs/taro";
import { Command } from "@/common/bluetooth/command";
import { uint8ArrayToFloat32, uint8ArrayToHex, waitFor } from "@/common/bluetooth/utils";
interface params {
init: () => void
state: Object,
startScan: () => void,
measureAndGetLab: () => any,
getAdapterState: () => void,
connect: (any) => void,
disconnect: () => void
}
const Context = React.createContext<params|unknown>(null)
interface stateStype {
listeners: any,
discovering: boolean,
available: boolean,
connected: any,
connecting: any,
serviceRule: any,
serviceId: any,
characteristicRule: any,
characteristicId: any,
/** 正在执行的命令 */
command: any,
responseResolve: any,
responseReject: any,
responseTimer: any,
/** 是否显示蓝牙调试信息 */
debug: any,
//搜索到的设备
devices: any,
//取色仪主动返回的数据
deviceLab: any
}
let stateObj: stateStype = {
/** 事件监听器 */
listeners: new Set(),
/** 正在扫描设备 */
discovering: false,
/** 蓝牙是否可用 */
available: true,
/** 当前连接的设备 */
connected: null,
/** 正在连接的设备 */
connecting: null,
serviceRule: /^0000FFE0/,
serviceId: null,
characteristicRule: /^0000FFE1/,
characteristicId: null,
/** 正在执行的命令 */
command: null,
responseResolve: null,
responseReject: null,
responseTimer: null,
/** 是否显示蓝牙调试信息 */
debug: true,
//搜索到的设备
devices: [],
//取色仪主动返回的数据
deviceLab: null
}
export default (props) => {
let refStatus = useRef(stateObj)
let [state, setState] = useState(refStatus.current)
const changeStatus = (obj:Object): void => {
refStatus.current = {...refStatus.current, ...obj}
setState({...refStatus.current})
}
const init = async () => {
try{
await openAdapter();
}catch(e) {
changeStatus({available:false})
}
// 绑定事件通知
Taro.onBluetoothAdapterStateChange(res => {
emit({ type: 'stateUpdate', detail: res });
});
Taro.onBLEConnectionStateChange(res => {
emit({ type: res.connected ? 'connected' : 'disconnect', detail: res });
});
Taro.onBLECharacteristicValueChange(({ value }) => notifySubscriber(value));
subscribe(async ev => {
if (ev.type === 'stateUpdate') {
// 蓝牙状态发生的变化
changeStatus({discovering:ev.detail.discovering, available:ev.detail.available})
} else if (ev.type === 'disconnect' && refStatus.current.connected && refStatus.current.connected.deviceId === ev.detail.deviceId) {
// 断开连接
changeStatus({
connected:null,
serviceId:null,
characteristicId:null,
deviceLab:null,
devices:[]
})
Taro.showToast({ icon: 'none', title: '蓝牙连接已断开' });
} else if (ev.type === 'connected' && refStatus.current.connecting) {
// 连接成功
changeStatus({connected: refStatus.current.connecting, connecting: null})
Taro.showToast({ title: '蓝牙已连接' });
} else if (ev.type === 'measure') {
//监听取色仪主动推送lab
await measureAndGetLab()
}
});
}
/** 打开蓝牙适配器 */
const openAdapter = () => {
return new Promise((resolve, reject) => {
Taro.openBluetoothAdapter({
success: resolve,
fail: reject
});
});
}
/**
*
* @param {{type: string; data: any}} event
*/
const emit = (event) => {
refStatus.current.listeners.forEach(cb => {
cb && cb(event);
});
}
const subscribe = (cb) => {
if (cb) {
changeStatus({
listeners: refStatus.current.listeners.add(cb)
})
}
}
/**
*
* @returns {Promise<{discovering: boolean; available: boolean}>}
*/
const getAdapterState = () => {
return new Promise((resolve, reject) => {
Taro.getBluetoothAdapterState({
success: resolve,
fail: reject
})
})
}
/**
*
* @param {(res: { devices: { name: string, deviceId: string, RSSI: number }[] }) => void} cb
* @param {number} duration
*/
const startScan = (duration = 30000) => {
console.log('开始寻找')
changeStatus({devices:[]})
Taro.onBluetoothDeviceFound(getDevices);
return new Promise((resolve, reject) => {
Taro.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: resolve,
fail: reject
});
if (duration > 0) {
setTimeout(() => {
Taro.offBluetoothDeviceFound(getDevices);
Taro.stopBluetoothDevicesDiscovery();
console.log("停止搜索")
}, duration);
}
});
}
//获取搜索到的设备
const getDevices = (res) => {
res.devices.forEach(device => {
// 排除掉已搜索到的设备和名称不合法的设备, 将新发现的设备添加到列表中
if (/^CM/.test(device.name) && !refStatus.current.devices.find(i => i.deviceId === device.deviceId)) {
changeStatus({devices: [ ...refStatus.current.devices, device ]})
}
});
}
/**
*
* @param {{ name: string, deviceId: string, RSSI: number }} device
*/
const connect = async (device) => {
try {
changeStatus({connecting: device})
console.log('connecting::', device)
await createConnection(device.deviceId);
await discoverService(device.deviceId);
await discoverCharacteristic(device.deviceId);
await notifyCharacteristicValueChange(device.deviceId);
} catch (e) {
changeStatus({connecting: null})
Taro.showToast({ icon: 'none', title: '蓝牙连接失败' });
throw e;
}
}
/** 断开当前连接的设备 */
const disconnect = async () => {
if (!refStatus.current.connected && !refStatus.current.connecting) return;
if (refStatus.current.connected) {
await closeConnection(refStatus.current.connected.deviceId);
resetCommand();
changeStatus({
connected: null,
serviceId: null,
characteristicId: null,
devices: [],
deviceLab: null
})
}
if (refStatus.current.connecting) {
await closeConnection(refStatus.current.connecting.deviceId);
changeStatus({connecting:null})
}
}
/** 创建 BLE 连接 */
function createConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.createBLEConnection({
deviceId,
timeout: 2000,
success: resolve,
fail: reject
});
});
}
/** 关闭 BLE 连接 */
function closeConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.closeBLEConnection({
deviceId,
success: resolve,
fail: reject
});
});
}
/** 搜索服务 */
function discoverService(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceServices({
deviceId,
success: ({ services }) => {
const service = services.find(i => refStatus.current.serviceRule.test(i.uuid));
if (!service) {
reject(new Error('服务不可用'));
} else {
changeStatus({serviceId: service.uuid})
resolve(service);
}
},
fail: reject
});
});
}
/** 搜索特征 */
function discoverCharacteristic(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceCharacteristics({
deviceId,
serviceId: refStatus.current.serviceId,
success: ({ characteristics }) => {
const characteristic = characteristics.find(i => refStatus.current.characteristicRule.test(i.uuid));
if (!characteristic) {
reject(new Error('特征不可用'));
} else {
changeStatus({characteristicId: characteristic.uuid})
resolve(characteristic);
}
},
fail: reject
})
});
}
/** 启动特征通知 */
function notifyCharacteristicValueChange(deviceId, stateParm = true) {
return new Promise((resolve, reject) => {
Taro.notifyBLECharacteristicValueChange({
deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
state:stateParm,
success: resolve,
fail: reject
});
});
}
/**
*
* @param {ArrayBuffer} buffer
*/
function notifySubscriber(buffer) {
if (refStatus.current.command) {
if (refStatus.current.debug) {
console.log(`[BLE RESP] ${uint8ArrayToHex(new Uint8Array(buffer))}`);
}
refStatus.current.command.fillResponse(buffer);
if (refStatus.current.command.isComplete) {
if (refStatus.current.command.isValid && refStatus.current.responseResolve) {
refStatus.current.responseResolve(refStatus.current.command.response);
} else if (!refStatus.current.command.isValid) {
refStatus.current.responseReject(new Error('无效数据'));
}
resetCommand();
}
} else {
const uint8Array = new Uint8Array(buffer);
if (uint8Array[0] === 0xbb && uint8Array[1] === 1 && uint8Array[3] === 0) {
const ev = { type: 'measure', detail: { mode: uint8Array[2] } };
emit(ev);
}
}
}
/**
*
* @param {Command}} command
* @returns {Promise<Uint8Array>}
*/
function exec(command) {
return new Promise(async (resolve, reject) => {
if (refStatus.current.command) {
reject(new Error('正在执行其他命令'));
} else {
try {
refStatus.current.command = command;
const data = command.data;
for (let i = 0; i < data.length; i++) {
await sendData(data[i]);
}
if (command.responseSize <= 0) {
resolve(true);
resetCommand();
} else {
refStatus.current.responseReject = reject;
refStatus.current.responseResolve = resolve;
refStatus.current.responseTimer = setTimeout(() => {
reject(new Error('命令响应超时'));
resetCommand();
}, command.timeout);
}
} catch (e) {
reject(e);
}
}
});
}
/**
*
* @param {ArrayBuffer} buffer
*/
function sendData(buffer) {
if (refStatus.current.debug) {
console.log(`[BLE SEND] ${uint8ArrayToHex(new Uint8Array(buffer))}`);
}
return new Promise((resolve, reject) => {
console.log('current:::',refStatus.current)
Taro.writeBLECharacteristicValue({
deviceId: refStatus.current.connected.deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
value: buffer,
success: resolve,
fail: reject
})
});
}
function resetCommand() {
if (refStatus.current.responseTimer) {
clearTimeout(refStatus.current.responseTimer);
}
changeStatus({
command: null,
responseResolve: null,
responseReject: null,
responseTimer: null
})
}
/**
*
* @param {number} mode
* @returns {Promise}
*/
async function measure (mode = 0) {
console.log('current1:::',Command.WakeUp)
await exec(Command.WakeUp);
console.log('current2:::',Command.WakeUp)
await waitFor(50);
console.log('current3:::',Command.WakeUp)
return await exec(Command.measure(mode));
}
/**
* lab
* @param {number} mode
* @returns {Promise<{ L: number, a: number, b: number }>}
*/
async function getLab(mode = 0) {
await exec(Command.WakeUp);
await waitFor(50);
const data: any = await exec(Command.getLab(mode));
return {
L: uint8ArrayToFloat32(data.slice(5, 9)),
a: uint8ArrayToFloat32(data.slice(9, 13)),
b: uint8ArrayToFloat32(data.slice(13, 17)),
};
}
/**
* lab
* @param {number} mode
* @returns {Promise<{L: number, a: number, b: number}>}
*/
async function measureAndGetLab(mode = 0) {
await measure(mode);
await waitFor(50);
const lab = await getLab(mode);
console.log('lab2::',lab)
changeStatus({deviceLab:lab})
return lab
}
return <Context.Provider children={props.children} value={{
init,
state,
startScan,
measureAndGetLab,
getAdapterState,
connect,
disconnect
}} />
}
export const useBluetoothTwo = () => {
const res = React.useContext<any>(Context)
if(res) {
return {...res}
} else {
return {}
}
}