feat: 项目初始化

This commit is contained in:
tianyongbao
2025-10-11 22:48:19 +08:00
parent 3a51eed514
commit 7e10c62cf9
87 changed files with 46791 additions and 36 deletions

621
src/my/addController.vue Normal file
View File

@@ -0,0 +1,621 @@
<template>
<view class="controller_home">
<view class="body">
<nut-cell>
<nut-row>
<nut-col :span="24">
<nut-steps :current="current">
<nut-step title="第一步" />
<nut-step title="第二步" />
<nut-step title="第三步" />
<nut-step title="第四步" />
</nut-steps>
</nut-col>
</nut-row>
</nut-cell>
<nut-config-provider :theme-vars="themeVars1">
<nut-cell>
<nut-row v-if="current === 1">
<nut-col :span="24" class="font_36 f_weight c_222">
检查设备
</nut-col>
<nut-col :span="24" class="font_32 c_666 m_t_20">
请检查设备是否上电网络指示灯处于常亮状态
</nut-col>
</nut-row>
<nut-row v-else-if="current === 2">
<nut-col :span="24" class="font_36 f_weight c_222">
请选择当前进线电压
</nut-col>
<nut-col :span="24" class="font_36 f_weight c_222 m_t_20">
220V
</nut-col>
<nut-col :span="24" class="m_t_20">
<nut-radio-group v-model="inputVoltage" direction="horizontal">
<nut-radio :label="1" icon-size="20">
<template #checkedIcon>
<IconFont :name="r_v" size="20" />
</template>
A相
</nut-radio>
<nut-radio :label="2" icon-size="20">
<template #checkedIcon>
<IconFont :name="r_v" size="20" />
</template>
B相
</nut-radio>
<nut-radio :label="3" icon-size="20">
<template #checkedIcon>
<IconFont :name="r_v" size="20" />
</template>
C相
</nut-radio>
</nut-radio-group>
</nut-col>
<nut-col :span="24" class="font_36 f_weight c_222 m_t_20">
380V
</nut-col>
<nut-col :span="24" class="m_t_20">
<nut-radio-group v-model="inputVoltage" direction="horizontal">
<nut-radio :label="4" icon-size="20">
<template #checkedIcon>
<IconFont :name="r_v" size="20" />
</template>
ABC相
</nut-radio>
</nut-radio-group>
</nut-col>
</nut-row>
<nut-row v-else-if="current === 3">
<nut-col :span="24" class="font_36 f_weight c_222">
请选择设备是否插入溶解氧探头
</nut-col>
<nut-col :span="24" class="m_t_20">
<nut-radio-group v-model="isHave">
<nut-radio :label="1" icon-size="20">
<template #checkedIcon>
<IconFont :name="r_v" size="20" />
</template>
有溶解氧探头
</nut-radio>
<nut-radio :label="2" icon-size="20">
<template #checkedIcon>
<IconFont :name="r_v" size="20" />
</template>
无溶解氧探头
</nut-radio>
</nut-radio-group>
</nut-col>
</nut-row>
<nut-row v-else-if="current === 4">
<nut-col :span="24" class="font_36 f_weight c_222">
请选择要放置塘口的设备
</nut-col>
<nut-col :span="24" class="m_t_20">
<nut-checkbox-group v-model="listPutPondPartId">
<nut-checkbox
label="0"
:style="{ width: '100%' }"
v-if="isHave == 1"
>
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
溶解氧检测仪
</nut-checkbox>
<nut-checkbox
label="1"
:style="{ width: '100%' }"
class="m_t_15"
>
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
开关1
</nut-checkbox>
<nut-checkbox
label="2"
:style="{ width: '100%' }"
class="m_t_15"
>
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
开关2
</nut-checkbox>
<nut-checkbox
label="3"
:style="{ width: '100%' }"
class="m_t_15"
>
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
开关3
</nut-checkbox>
<nut-checkbox
label="4"
:style="{ width: '100%' }"
class="m_t_15"
>
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
开关4
</nut-checkbox>
</nut-checkbox-group>
</nut-col>
</nut-row>
</nut-cell>
<nut-cell v-if="current == 3 && isHave == 1">
<nut-row>
<nut-col :span="24" class="m_t_10">
<nut-radio-group v-model="waterType" direction="horizontal">
<nut-radio :label="1" icon-size="25">
淡水
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
</nut-radio>
<nut-radio :label="2" icon-size="25">
海水
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
</nut-radio>
</nut-radio-group>
</nut-col>
<nut-col :span="24" v-if="waterType == 2">
<nut-config-provider :theme-vars="themeVars">
<nut-cell
title="请设置您的盐度"
is-link
:desc="salt + '‰'"
@click="setSalt"
></nut-cell>
</nut-config-provider>
</nut-col>
<nut-col :span="24" class="c_888 font_28">
<view>淡水养殖无需设置海水根据实际盐度设置</view>
<!-- <view>(盐度输入数值0-40)</view> -->
</nut-col>
<nut-col :span="24" class="m_t_20">
<nut-config-provider :theme-vars="themeVars">
<nut-cell
title="设置告警值"
is-link
:desc="String(alarm)"
@click="setAlarm"
></nut-cell>
</nut-config-provider>
</nut-col>
<nut-col :span="24" class="c_888 font_28">
<view>当溶解氧低于设定数值时会电话告警</view>
<view>(数值默认为3可设定最低值不能低于1.5) </view>
</nut-col>
</nut-row>
</nut-cell>
</nut-config-provider>
<view>
<nut-button
block
shape="square"
size="large"
:loading="isLoading"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '10px',
marginBottom: '15px',
}"
@click="prev"
v-if="current != 1"
>
上一步
</nut-button>
<nut-button
block
shape="square"
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '10px',
}"
@click="next"
>{{ current == 4 ? "添加" : "下一步" }}</nut-button
>
</view>
<nut-config-provider :theme-vars="themeVars">
<nut-dialog v-model:visible="saltShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body">
<nut-row>
<nut-col :span="24">
<view class="d_title">请设置您的盐度</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24" class="view_f">
<view :style="{ width: '20%' }" class="font_32 c_444"
>盐度</view
>
<view :style="{ width: '80%' }">
<nut-input
type="digit"
@blur="upValue"
v-model="salt"
placeholder="(盐度输入数值0-40‰)"
:border="false"
input-align="right"
max-length="5"
clearable
:cursor="String(salt).length"
>
<template #right>
<text class="c_222"></text>
</template>
</nut-input>
</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
</nut-row>
</view>
<view class="d_btn">
<nut-row>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#222' }"
@click="saltShow = false"
>
取消
</nut-col>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#15BBC5' }"
@click="saltShow = false"
>
确定
</nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
<nut-dialog v-model:visible="alarmShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body">
<nut-row>
<nut-col :span="24">
<view class="d_title">低溶解氧告警值</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24" class="view_f">
<view :style="{ width: '20%' }" class="font_32 c_444"
>告警值</view
>
<view :style="{ width: '80%' }">
<nut-input
type="digit"
@blur="upAlarmValue"
v-model="alarm"
placeholder="1.5~20"
:border="false"
input-align="right"
max-length="5"
clearable
:cursor="String(alarm).length"
>
<template #right>
<text class="c_222">Mg/L</text>
</template>
</nut-input>
</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
</nut-row>
</view>
<view class="d_btn">
<nut-row>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#222' }"
@click="alarmShow = false"
>
取消
</nut-col>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#15BBC5' }"
@click="alarmShow = false"
>
确定
</nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
</nut-config-provider>
</view>
<!-- 弹出层提示 -->
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
</view>
</template>
<script lang="ts" setup>
import Taro from "@tarojs/taro";
import { stateType } from "@/utils/types";
import { addDeviceController, checkDeviceStatus } from "@/api/device";
import { IconFont } from "@nutui/icons-vue-taro";
import { imgUrl } from "@/utils/imgUrl";
definePageConfig({
navigationBarTitleText: "添加测控一体机",
});
const instance = Taro.getCurrentInstance();
const r = instance.router.params.params;
const page = instance.router.params.page;
const params = r ? JSON.parse(r) : undefined;
const check = `https://www.yuceyun.cn/wechat/check.png`;
const check_a = `https://www.yuceyun.cn/wechat/check_a.png`;
const r_v = `https://www.yuceyun.cn/wechat/r_v.png`;
// const params = {
// devNum: "12312399",
// devType: 2,
// pondId: [1],
// pondName: "我的一号塘口"
// }
// 步骤
const current = ref<number>(1);
const themeVars = ref({
cellBoxShadow: "0px 0px 0px 0px",
cellPadding: "0",
cellColor: "#666666",
cellTitleFont: "32rpx",
cellTitleDescFont: "30rpx",
cellDescFont: "30rpx",
cellDescColor: "#222222",
popupBorderRadius: "10rpx",
checkboxLabelFontSize: "30rpx",
});
const themeVars1 = ref({
radioLabelFontSize: "30rpx",
});
// 盐度
const salt = ref<number>(40);
// 电压
const inputVoltage = ref<number>(1);
const saltShow = ref<boolean>(false);
const alarm = ref<number>(3);
const alarmShow = ref<boolean>(false);
const isHave = ref<number>(1);
const listPutPondPartId = ref(["0"]);
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const status = ref(0);
const isLoading = ref<boolean>(false);
const waterType = ref<number>(1);
/** ----------------metod start------------------ */
// 监测设备在线状态
function getCheckDevice() {
const data = {
devicetype: params.devType,
serialnum: params.devNum,
};
isLoading.value = true;
checkDeviceStatus(data)
.then((res: any) => {
if (res.statusCode == 200) {
status.value = res.data;
if (status.value == 0) {
state.show = true;
state.msg = "设备未激活";
} else if (status.value == 1) {
current.value = 2;
} else if (status.value == 3) {
state.show = true;
state.msg = "设备离线";
} else if (status.value == 8) {
state.show = true;
state.msg = "设备禁用";
}
}
})
.finally(() => {
isLoading.value = false;
});
}
function next() {
const num = current.value;
if (num == 4) {
// 验证参数,发起添加设备请求
if (salt.value) {
if (Number(salt.value) < 0 || Number(salt.value) > 40) {
state.show = true;
state.msg = "盐度范围为0-40";
return;
}
} else {
state.show = true;
state.msg = "请录入盐度";
return;
}
if (alarm.value) {
if (Number(alarm.value) < 1.5 || Number(alarm.value) > 20) {
state.show = true;
state.msg = "告警值范围为1.5-20";
return;
}
} else {
state.show = true;
state.msg = "请录入告警值";
return;
}
if (listPutPondPartId.value.length <= 0) {
state.show = true;
state.msg = "请选择放置的设备";
return;
}
// 发起添加设备请求
const data = {
serialNum: params.devNum,
pondId: params.pondId[0],
salinityCompensation: waterType.value == 1 ? 0 : Number(salt.value),
oxyWarnLower: Number(alarm.value),
inputVoltage: Number(inputVoltage.value),
listPutPondPartId: listPutPondPartId.value,
isOxygenUsed:isHave.value==1?true:false
};
addDeviceController(data).then((res) => {
if (res.statusCode == 200) {
Taro.redirectTo({
url: "/my/addDevSuccess?name=添加测控一体机&page=" + page,
});
}
});
} else {
if (num != 1 && status.value == 1) {
current.value = num + 1;
}
if (num == 1) {
// 检查设备状态
getCheckDevice();
} else {
}
return;
}
}
// 上一步
function prev() {
const num = current.value;
current.value = num - 1;
}
// 设置盐度弹框
function setSalt() {
saltShow.value = true;
}
// 验证输入框内容
function upValue() {
const val = salt.value;
if (Number(val) < 0 || Number(val) > 40) {
salt.value = 40;
}
}
// 设置高净值弹框
function setAlarm() {
alarmShow.value = true;
}
// 验证输入框内容
function upAlarmValue() {
const val = alarm.value;
if (Number(val) < 1.5 || Number(val) > 20) {
alarm.value = 20;
}
}
/** ----------------metod end-------------------- */
</script>
<style lang="scss">
.controller_home {
width: 100%;
height: 100%;
background-image: url("https://www.yuceyun.cn/wechat/bg.jpg");
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
font-size: 32px;
font-family: "PingFang SC";
font-weight: 400;
.body {
padding: 15px;
.title {
font-size: 32px;
color: #222;
font-weight: bold;
}
.time {
font-size: 24px;
color: #666666;
}
.mt {
margin-top: 10px;
}
.content {
margin-top: 10px;
font-size: 28px;
color: #444;
}
.tag {
background: RGB(21, 137, 233, 0.1);
border-radius: 4px;
color: #1589e9;
font-size: 28rpx;
margin-left: 10px;
}
.price {
font-size: 48px;
color: #444444;
font-weight: bold;
text-align: right;
}
.nut-dialog {
padding: 0;
}
.nut-dialog__content {
margin: 0;
}
.nut-input {
padding: 20rpx 0;
}
}
}
</style>

500
src/my/addDetector.vue Normal file
View File

@@ -0,0 +1,500 @@
<template>
<view class="detector_home">
<view class="body">
<nut-cell>
<nut-row>
<nut-col :span="24">
<nut-steps :current="current">
<nut-step title="第一步" />
<nut-step title="第二步" />
<nut-step title="第三步" />
</nut-steps>
</nut-col>
</nut-row>
</nut-cell>
<nut-cell>
<nut-row v-if="current === 1">
<nut-col :span="24">
<nut-col :span="2"
><img
:src="dc_1"
:style="{
width: '48rpx',
height: '48rpx',
marginRight: '20rpx',
}"
/></nut-col>
<nut-col :span="22" class="font_36 f_weight c_222"
>检查设备</nut-col
>
</nut-col>
<nut-col :span="24">
<nut-col :span="2">&nbsp;</nut-col>
<nut-col :span="22" class="font_32 c_666"
>请检查设备是否上电网络指示灯处于常亮状态</nut-col
>
</nut-col>
<nut-col :span="24" class="m_t_20">
<nut-col :span="2"
><img
:src="dc_2"
:style="{
width: '48rpx',
height: '48rpx',
marginRight: '20rpx',
}"
/></nut-col>
<nut-col :span="22" class="font_36 f_weight c_222"
>检测设备</nut-col
>
</nut-col>
<nut-col :span="24">
<nut-col :span="2">&nbsp;</nut-col>
<nut-col :span="22" class="font_32 c_666"
>正式使用前将设备提前上电并将探头浸泡在水中24小时以上使用最佳</nut-col
>
</nut-col>
</nut-row>
<nut-row v-else-if="current === 2 && status == 1">
<nut-col :span="24" class="m_t_10">
<nut-radio-group v-model="waterType" direction="horizontal">
<nut-radio :label="1" icon-size="25">
淡水
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
</nut-radio>
<nut-radio :label="2" icon-size="25">
海水
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
</nut-radio>
</nut-radio-group>
</nut-col>
<nut-col :span="24" v-if="waterType == 2">
<nut-config-provider :theme-vars="themeVars">
<nut-cell
title="请设置您的盐度"
is-link
:desc="salt + '‰'"
@click="setSalt"
></nut-cell>
</nut-config-provider>
</nut-col>
<nut-col :span="24" class="c_888 font_28">
<view>淡水养殖无需设置海水根据实际盐度设置</view>
<!-- <view>(盐度输入数值0-40)</view> -->
</nut-col>
</nut-row>
<nut-row v-else-if="current === 3 && status == 1">
<nut-col :span="24">
<nut-config-provider :theme-vars="themeVars">
<nut-cell
title="溶解氧告警值设置"
is-link
:desc="String(alarm) + 'Mg/L'"
@click="setAlarm"
></nut-cell>
</nut-config-provider>
</nut-col>
<nut-col :span="24" class="c_888 font_28">
<view>当溶解氧低于设定数值时会电话告警</view>
<view>(数值默认为3Mg/L可设定最低值不能低于1.5Mg/L) </view>
</nut-col>
</nut-row>
</nut-cell>
<view>
<nut-button
block
shape="square"
size="large"
:loading="isLoading"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '10px',
marginBottom: '15px',
}"
@click="prev"
v-if="current != 1"
>
上一步
</nut-button>
<nut-button
block
shape="square"
type="primary"
size="large"
:loading="isLoading"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '10px',
}"
@click="next"
>{{ current == 3 ? "添加" : "下一步" }}</nut-button
>
</view>
<nut-config-provider :theme-vars="themeVars">
<nut-dialog v-model:visible="saltShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body">
<nut-row>
<nut-col :span="24">
<view class="d_title">请设置您的盐度</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24" class="view_f">
<view :style="{ width: '20%' }" class="font_32 c_444"
>盐度</view
>
<view :style="{ width: '80%' }">
<nut-input
type="digit"
@blur="upValue"
v-model="salt"
placeholder="(盐度输入数值0-40‰)"
:border="false"
input-align="right"
clearable
max-length="5"
:cursor="String(salt).length"
>
<template #right>
<text class="c_222"></text>
</template>
</nut-input>
</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
</nut-row>
</view>
<view class="d_btn">
<nut-row>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#222' }"
@click="saltShow = false"
>
取消
</nut-col>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#15BBC5' }"
@click="saltShow = false"
>
确定
</nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
<nut-dialog v-model:visible="alarmShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body">
<nut-row>
<nut-col :span="24">
<view class="d_title">溶解氧告警值设置</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24" class="view_f">
<view :style="{ width: '20%' }" class="font_32 c_444"
>告警值</view
>
<view :style="{ width: '80%' }">
<nut-input
type="digit"
@blur="upAlarmValue"
v-model="alarm"
placeholder="1.5~20"
:border="false"
input-align="right"
clearable
max-length="5"
:cursor="String(alarm).length"
>
<template #right>
<text class="c_222">Mg/L</text>
</template>
</nut-input>
</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
</nut-row>
</view>
<view class="d_btn">
<nut-row>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#222' }"
@click="alarmShow = false"
>
取消
</nut-col>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#15BBC5' }"
@click="alarmShow = false"
>
确定
</nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
</nut-config-provider>
</view>
<!-- 弹出层提示 -->
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
</view>
</template>
<script lang="ts" setup>
import Taro from "@tarojs/taro";
import { stateType } from "@/utils/types";
import { addDeviceDetector, checkDeviceStatus } from "@/api/device";
import { imgUrl } from "@/utils/imgUrl";
import { IconFont } from "@nutui/icons-vue-taro";
definePageConfig({
navigationBarTitleText: "添加水质检测仪",
});
const dc_1 = imgUrl + "dc_1.png";
const dc_2 = imgUrl + "dc_2.png";
const check = `https://www.yuceyun.cn/wechat/check.png`;
const check_a = `https://www.yuceyun.cn/wechat/check_a.png`;
const instance = Taro.getCurrentInstance();
const r = instance.router.params.params;
const page = instance.router.params.page;
const params = r ? JSON.parse(r) : undefined;
// 步骤
const current = ref<number>(1);
const themeVars = ref({
cellBoxShadow: "0px 0px 0px 0px",
cellPadding: "0",
cellColor: "#666666",
cellTitleFont: "32rpx",
cellTitleDescFont: "30rpx",
cellDescFont: "30rpx",
cellDescColor: "#222222",
popupBorderRadius: "10rpx",
});
// 盐度
const salt = ref<number>(30);
const waterType = ref<number>(1);
const saltShow = ref<boolean>(false);
const alarm = ref<number>(3);
const alarmShow = ref<boolean>(false);
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const status = ref(0);
const isLoading = ref<boolean>(false);
/** ----------------metod start------------------ */
// 监测设备在线状态
function getCheckDevice() {
const data = {
devicetype: params.devType,
serialnum: params.devNum,
};
isLoading.value = true;
checkDeviceStatus(data)
.then((res: any) => {
if (res.statusCode == 200) {
status.value = res.data;
if (status.value == 0) {
state.show = true;
state.msg = "设备未激活";
} else if (status.value == 1) {
current.value = 2;
} else if (status.value == 3) {
state.show = true;
state.msg = "设备离线";
} else if (status.value == 8) {
state.show = true;
state.msg = "设备禁用";
} else {
state.show = true;
state.msg = "设备异常";
}
}
})
.finally(() => {
isLoading.value = false;
});
}
function next() {
const num = current.value;
if (num == 3) {
isLoading.value = true;
// 验证参数,发起添加设备请求
if (salt.value) {
if (Number(salt.value) < 0 || Number(salt.value) > 40) {
state.show = true;
state.msg = "盐度范围为0-40";
return;
}
} else {
state.show = true;
state.msg = "请录入盐度";
return;
}
if (alarm.value) {
if (Number(alarm.value) < 1.5 || Number(alarm.value) > 20) {
state.show = true;
state.msg = "告警值范围为1.5-20";
return;
}
} else {
state.show = true;
state.msg = "请录入告警值";
return;
}
// 发起添加设备请求
const data = {
serialNum: Number(params.devNum),
pondId: Number(params.pondId[0]),
salinityCompensation: waterType.value == 1 ? 0 : Number(salt.value),
oxyWarnLower: Number(alarm.value),
};
addDeviceDetector(data)
.then((res) => {
if (res.statusCode == 200) {
Taro.redirectTo({
url: "/my/addDevSuccess?name=添加水质检测仪&page=" + page,
});
}
})
.finally(() => {
isLoading.value = false;
});
} else {
if (num != 1 && status.value == 1) {
current.value = num + 1;
}
if (num == 1) {
// 检查设备状态
getCheckDevice();
} else {
}
}
}
// 上一步
function prev() {
const num = current.value;
current.value = num - 1;
}
// 设置盐度弹框
function setSalt() {
saltShow.value = true;
}
// 验证输入框内容
function upValue() {
const val = salt.value;
if (Number(val) < 0 || Number(val) > 40) {
salt.value = 40;
}
}
// 设置高净值弹框
function setAlarm() {
alarmShow.value = true;
}
// 验证输入框内容
function upAlarmValue() {
const val = alarm.value;
if (Number(val) < 1.5 || Number(val) > 20) {
alarm.value = 20;
}
}
/** ----------------metod end-------------------- */
</script>
<style lang="scss">
.detector_home {
width: 100%;
height: 100%;
background-image: url("https://www.yuceyun.cn/wechat/bg.jpg");
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
font-size: 32px;
font-family: "PingFang SC";
font-weight: 400;
.body {
padding: 15px;
.title {
font-size: 32px;
color: #222;
font-weight: bold;
}
.time {
font-size: 24px;
color: #666666;
}
.mt {
margin-top: 10px;
}
.content {
margin-top: 10px;
font-size: 28px;
color: #444;
}
.tag {
background: RGB(21, 137, 233, 0.1);
border-radius: 4px;
color: #1589e9;
font-size: 28rpx;
margin-left: 10px;
}
.price {
font-size: 48px;
color: #444444;
font-weight: bold;
text-align: right;
}
.nut-dialog {
padding: 0;
}
.nut-dialog__content {
margin: 0;
}
.nut-input {
padding: 20rpx 0;
}
}
}
</style>

79
src/my/addDevSuccess.vue Normal file
View File

@@ -0,0 +1,79 @@
<template>
<view class="d_home">
<view class="body">
<nut-cell>
<nut-row class="center-grid">
<nut-col :span="24">
<nut-empty>
<template #image>
<img :src="addS" />
</template>
<template #description>
<view class="font_38 c_222 m_t_20">添加成功</view>
<view class="font_28 c_888 m_t_20">点击返回按钮查看</view>
</template>
<div class="m_t_20">
<nut-button
type="primary"
shape="square"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '10px',
}"
@click="back"
>返回</nut-button
>
</div>
</nut-empty>
</nut-col>
</nut-row>
</nut-cell>
</view>
</view>
</template>
<script setup lang="ts">
import { imgUrl } from "@/utils/imgUrl";
import Taro from "@tarojs/taro";
const getCurrentInstance = Taro.getCurrentInstance();
const params_router = getCurrentInstance.router.params;
Taro.setNavigationBarTitle({
title: params_router.name ? params_router.name : "添加成功",
});
const addS = `${imgUrl}addS.png`;
function back() {
if (params_router.page == "home") {
Taro.switchTab({
url: "/pages/main/home",
});
} else {
Taro.navigateBack({ delta: 2 });
}
}
</script>
<style lang="scss">
.d_home {
width: 100%;
height: 100%;
background-image: url("https://www.yuceyun.cn/wechat/bg.jpg");
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
display: flex;
flex-direction: column;
font-size: 32px;
font-family: "PingFang SC";
font-weight: 400;
.body {
padding: 15px;
flex: 1;
display: flex;
}
}
</style>

174
src/my/addDevice.vue Normal file
View File

@@ -0,0 +1,174 @@
<template>
<view class="dev_add_home">
<view class="body">
<view>
<nut-row>
<nut-col :span="24">
<nut-cell>
<nut-row>
<nut-col :span="24" class="title"> 设备类型 </nut-col>
<nut-col :span="24" class="m_t_10">
<nut-radio-group v-model="params.devType">
<nut-radio label="1" icon-size="25">
水质检测仪
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
</nut-radio>
<nut-radio label="2" icon-size="25">
控制一体机
<template #icon>
<IconFont :name="check" />
</template>
<template #checkedIcon>
<IconFont :name="check_a" />
</template>
</nut-radio>
</nut-radio-group>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24" class="title"> 设备号 </nut-col>
<nut-col :span="24" class="m_t_10">
<nut-input
v-model="params.devNum"
placeholder="请输入设备号"
type="number"
autofocus
:cursor="String(params.devNum).length"
>
<!-- <template #right>
<nut-button type="primary" size="small" shape="square"
@click="scan">扫一扫</nut-button>
</template> -->
</nut-input>
</nut-col>
</nut-row>
</nut-cell>
</nut-col>
<nut-col :span="24">
<nut-button
block
shape="square"
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '10px',
}"
:loading="isLoading"
@click="submit"
>提交</nut-button
>
</nut-col>
</nut-row>
</view>
<!-- 弹出层提示 -->
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
</view>
</view>
</template>
<script lang="ts" setup>
import Taro from "@tarojs/taro";
import { IconFont } from "@nutui/icons-vue-taro";
import { stateType } from "@/utils/types";
import { paramsType } from "./types";
definePageConfig({
navigationBarTitleText: "添加设备",
});
const instance = Taro.getCurrentInstance();
const page = instance.router.params.page;
const pondId = instance.router.params.pondId;
const check = `https://www.yuceyun.cn/wechat/check.png`;
const check_a = `https://www.yuceyun.cn/wechat/check_a.png`;
// const pondId = 1
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
// 1扫码2手动
const params = reactive<paramsType>({
pondId: [],
pondName: "",
devType: "1",
devNum: "",
});
const isLoading = ref(false)
/**---------------method start-------------------- */
Taro.useDidShow(() => {
if (pondId) {
params.pondId = [pondId];
}
});
// 进入不同设备添加页面
function submit() {
if (params.pondId.length <= 0) {
state.show = true;
state.msg = "请选择塘口";
return;
}
if (!params.devNum) {
state.show = true;
state.msg = "请输入设备号";
return;
}
if (params.devType == 1) {
isLoading.value = false
Taro.navigateTo({
url: "/my/addDetector?params=" + JSON.stringify(params) + "&page=" + page,
});
} else {
isLoading.value = false
Taro.navigateTo({
url:
"/my/addController?params=" + JSON.stringify(params) + "&page=" + page,
});
}
}
/**---------------method end---------------------- */
</script>
<style lang="scss">
.dev_add_home {
width: 100%;
height: 100%;
background-image: url("https://www.yuceyun.cn/wechat/devBg.png");
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
.body {
padding: 220px 20px;
.title {
font-size: 30px;
color: #666666;
}
.nut-input {
padding: 0;
}
.nut-dialog {
padding: 0;
}
.nut-dialog__content {
margin: 0;
}
}
}
</style>

275
src/my/alarmPhone.vue Normal file
View File

@@ -0,0 +1,275 @@
<template>
<view class="phone_home">
<nut-config-provider :theme="themeVars">
<scroll-view :scroll-y="true" :style="{ height: '100%' }">
<view class="body">
<view>
<nut-cell-group>
<nut-cell>
<template #title>
<view class="view_f">
<view>第一报警电话</view>
<view @click="changeSort(phone1, 1)">
<RectDown />
</view>
</view>
</template>
<template #link>
<nut-input
type="number"
max-length="11"
v-model="phone1"
placeholder="请输入第一报警电话"
:cursor="String(phone1).length"
:border="false"
input-align="right"
clearable
/>
</template>
</nut-cell>
<nut-cell title="第二报警电话">
<template #title>
<view class="view_f">
<view>第二报警电话</view>
<view @click="changeSort(phone2, 2)">
<RectUp />
</view>
</view>
</template>
<template #link>
<nut-input
type="number"
max-length="11"
v-model="phone2"
placeholder="请输入第二报警电话"
:cursor="String(phone2).length"
:border="false"
input-align="right"
clearable
/>
</template>
</nut-cell>
<nut-cell title="第三报警电话">
<template #title>
<view class="view_f">
<view>第三报警电话</view>
<view @click="changeSort(phone3, 3)">
<RectUp />
</view>
</view>
</template>
<template #link>
<nut-input
type="number"
max-length="11"
v-model="phone3"
placeholder="请输入第三报警电话"
:cursor="String(phone3).length"
:border="false"
input-align="right"
clearable
/>
</template>
</nut-cell>
</nut-cell-group>
<nut-row>
<nut-col :span="24" class="d_desc">
出现溶解氧过低水温过高或过低控制器开关异常等情况时会进行电话报警会按优先级别拨打电话如无人接听会拨打下一优先级电话报警电话号码为:
<text :style="{ color: '#222' }">05712367528105712367527105712367528702162321772</text>
</nut-col>
</nut-row>
</view>
</view>
</scroll-view>
<view class="btn">
<nut-row>
<nut-col :span="24" :style="{ padding: '0 20rpx' }">
<nut-row>
<nut-col :span="24">
<nut-button
block
shape="square"
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '0px',
}"
:loading="isLoading"
@click="save"
>保存</nut-button
>
</nut-col>
</nut-row>
</nut-col>
</nut-row>
</view>
</nut-config-provider>
<!-- 弹出层提示 -->
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
</view>
</template>
<script lang="ts" setup>
import { warnPhoneList, updateWarnPhone } from "@/api/my";
import Taro from "@tarojs/taro";
import { stateType } from "@/utils/types";
import { RectUp, RectDown } from "@nutui/icons-vue-taro";
definePageConfig({
navigationBarTitleText: "报警电话",
});
const themeVars = ref({
cellTitleFont: "32rpx",
cellDescFont: "32rpx",
cellDescColor: "#222222",
celColor: "#666666",
});
const phone1 = ref(
Taro.getStorageSync("Phone") ? Taro.getStorageSync("Phone") : "-"
);
const phone2 = ref();
const phone3 = ref();
const phoneList = ref([]);
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const isLoading = ref(false)
/** --------------- method start---------------------- */
Taro.useDidShow(() => {
// 获取报警电话列表
getWarnPhoneList();
});
// 获取报警电话列表
function getWarnPhoneList() {
warnPhoneList().then((res: any) => {
if (res.statusCode == 200) {
phoneList.value = res.data;
phone1.value = res.data[0] ? res.data[0] : "";
phone2.value = res.data[1] ? res.data[1] : "";
phone3.value = res.data[2] ? res.data[2] : "";
}
});
}
// 保存修改
function save() {
if (!phone1.value) {
state.show = true;
state.msg = "请输入第一报警电话";
return;
}
if (phone1.value) {
if (!isValidPhoneNumber(phone1.value)) {
state.show = true;
state.msg = "第一报警电话格式错误";
return;
}
}
if (phone2.value) {
if (!isValidPhoneNumber(phone2.value)) {
state.show = true;
state.msg = "第二报警电话格式错误";
return;
}
}
if (phone3.value) {
if (!isValidPhoneNumber(phone3.value)) {
state.show = true;
state.msg = "第三报警电话格式错误";
return;
}
}
Taro.showModal({
title: "提示",
content: "确认保存报警电话!",
success: function (res) {
if (res.confirm) {
let data = [
phone1.value ? phone1.value : undefined,
phone2.value ? phone2.value : undefined,
phone3.value ? phone3.value : undefined,
];
data = data.filter(
(item) => item !== null && item !== undefined && item !== ""
);
const params = {
listPhone: data,
};
isLoading.value = true
updateWarnPhone(params).then((res: any) => {
if (res.statusCode == 200) {
state.show = true;
state.msg = "保存成功";
}
}).finally(()=>{
isLoading.value = false
})
} else if (res.cancel) {
phone1.value = phoneList.value[0] ? phoneList.value[0] : "";
phone2.value = phoneList.value[1] ? phoneList.value[1] : "";
phone3.value = phoneList.value[2] ? phoneList.value[2] : "";
}
},
});
}
function isValidPhoneNumber(phoneNumber) {
const regex = /^1[3-9]\d{9}$/;
return regex.test(phoneNumber);
}
// 调整顺序
function changeSort(phone, sort) {
if (sort == 1) {
phone1.value = phone2.value;
phone2.value = phone;
} else if (sort == 2) {
phone2.value = phone1.value;
phone1.value = phone;
} else if (sort == 3) {
phone3.value = phone2.value;
phone2.value = phone;
}
}
/** --------------- method end------------------------ */
</script>
<style lang="scss">
.phone_home {
width: 100%;
height: 100%;
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
.body {
padding: 20px;
.nut-cell__value {
color: #222;
font-size: 32rpx;
}
.nut-cell__title {
min-width: 230px;
}
.d_desc {
font-size: 32px;
color: #929a9b;
}
}
.btn {
width: 100%;
position: fixed;
bottom: 30px;
// padding: 20px;
}
}
</style>

488
src/my/deviceList.vue Normal file
View File

@@ -0,0 +1,488 @@
<template>
<view class="dev_home">
<scroll-view :scroll-y="true" :style="{ height: '100%' }">
<view class="body">
<view v-if="deviceList.length > 0">
<view v-for="item in deviceList" :key="item.id">
<nut-cell @click="selectInfo(item.id, item.deviceType, item.deviceName)">
<nut-row class="view_f">
<nut-col
:span="4"
@click="selectInfo(item.id, item.deviceType, item.deviceName)"
>
<img
:src="item.deviceType == 1 ? rongjieyang : kongzhiqi"
width="80rpx"
height="80rpx"
/>
</nut-col>
<nut-col
:span="14"
>
<view class="title">{{ item.deviceName }}</view>
<view class="font_30 c_666 mt"
>{{ item.pondInfo ? item.pondInfo.pondName : "无塘口" }}
</view>
<view class="time mt"
>{{
item.deadTime ? formatDate(new Date(item.deadTime)) : ""
}}
</view>
</nut-col>
<nut-col
:span="6"
:style="{
justifyContent: 'flex-end',
height: '100%',
display: 'flex',
alignItems: 'center',
}"
>
<!-- <nut-button type="danger" size="mini" @click="removeDev(item.id)">移除</nut-button> -->
<view
class="price"
style="line-height: 1"
>
<RectRight />
</view>
</nut-col>
</nut-row>
</nut-cell>
</view>
</view>
<view v-else>
<nut-cell>
<nut-col :span="24">
<nut-empty description="暂无设备">
<template #image>
<img :src="pond404" />
</template>
</nut-empty>
</nut-col>
</nut-cell>
</view>
<view :style="{ marginBottom: '80px' }"> </view>
</view>
</scroll-view>
<view class="btn">
<nut-row>
<nut-col :span="24" :style="{ padding: '0 20rpx' }">
<nut-row>
<nut-col :span="24">
<nut-button
block
shape="square"
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '0px',
}"
@click="alertShow = true"
>新增设备</nut-button
>
</nut-col>
</nut-row>
</nut-col>
</nut-row>
</view>
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
<!-- 弹出添加设备提示 -->
<nut-config-provider :theme-vars="themeVars">
<nut-dialog v-model:visible="alertShow" text-align="left" no-footer>
<view :style="{ height: '100%' }" class="d_body">
<nut-row>
<nut-col :span="24" class="font_36 f_weight c_222">
请选择添加设备方式
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<view v-if="!pondId">
<nut-col :span="24">
<nut-cell
title="塘口"
:desc="params.pondName ? params.pondName : '请选择'"
@click="popupPond"
is-link
/>
</nut-col>
</view>
<nut-col :span="24">
<nut-button
block
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
marginTop: '20px',
}"
@click="goScan"
>扫描二维码</nut-button
>
<nut-button
block
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
marginTop: '20px',
}"
@click="goInput"
>输入设备号</nut-button
>
</nut-col>
</nut-row>
</view>
</nut-dialog>
<!-- 提醒扫码异常 -->
<nut-dialog v-model:visible="errorShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body">
<nut-row>
<nut-col
:span="24"
class="d_title"
:style="{ textAlign: 'center' }"
>
扫描识别失败请前往手动录入
</nut-col>
</nut-row>
</view>
<view class="d_btn" :style="{ position: 'absolute' }">
<nut-row>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#222' }"
@click="errorShow = false"
>
取消
</nut-col>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#15BBC5' }"
>
<nut-button type="primary" shape="square" block @click="goInput"
>确定</nut-button
>
</nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
<!-- 选择塘口 -->
<nut-popup v-model:visible="showPond" position="bottom">
<nut-picker
v-model="p"
:columns="pondList"
title="请选择塘口"
@confirm="confirm"
:field-names="{ text: 'pondName', value: 'id' }"
@cancel="cancel"
/>
</nut-popup>
</nut-config-provider>
</view>
</template>
<script lang="ts" setup>
import { imgUrl } from "@/utils/imgUrl";
import { formatDate } from "@/utils/tools";
import { IconFont, RectRight } from "@nutui/icons-vue-taro";
import {
allDeviceList,
deviceDel,
deviceScan,
deviceUnbind,
} from "@/api/device";
import { getPond2 } from "@/api/home";
import Taro from "@tarojs/taro";
import { stateType } from "@/utils/types";
definePageConfig({
navigationBarTitleText: "设备列表",
});
const dev = `${imgUrl}dev.png`;
const dev_exp = `${imgUrl}dev_exp.png`;
const pond404 = `${imgUrl}zanwurenwu.png`;
const rongjieyang = `${imgUrl}rongjieyang.png`;
const kongzhiqi = `${imgUrl}kongzhiqi.png`;
const deviceList = ref<any[]>([]);
const instance = Taro.getCurrentInstance();
const pondId = instance.router.params.pondId;
const page = instance.router.params.page;
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const themeVars = ref({
cellBoxShadow: "0px",
cellPadding: "0",
popupBorderRadius: "10rpx",
cellDescColor: "#222222",
});
const alertShow = ref(false);
const errorShow = ref(false);
const showPond = ref(false);
const p = ref([]);
const params = reactive<paramsType>({
pondId: [],
pondName: "",
devType: "1",
devNum: "",
});
const pondList = ref([]);
/** ------------------method start------------------------ */
Taro.useDidShow(() => {
// 查询设备列表
getDeviceList();
// 查询塘口列表
getPondList();
if (pondId) {
p.value = [pondId];
alertShow.value = true;
params.pondId = [pondId];
}
});
// 设备列表
function getDeviceList() {
allDeviceList({ type: 1 }).then((res: any) => {
if (res.statusCode == 200) {
deviceList.value = res.data;
}
});
}
// 添加设备-点击触发弹框
function addDevice() {
// 跳转到添加设备页面
Taro.navigateTo({
url: "/my/addDevice?page=deviceList",
});
}
// 跳转到设备详情页面
function selectInfo(id: number, type: number, name: string) {
if (type == 1) {
Taro.navigateTo({
url: `/home/wqm?id=${id}&name=${name}`,
});
} else {
Taro.navigateTo({
url: `/home/ctr?id=${id}&name=${name}`,
});
}
}
// 移除设备
function removeDev(id: number) {
Taro.showModal({
title: "提示",
content: "确认移除该设备?",
success: function (res) {
if (res.confirm) {
deviceUnbind({ id }).then((res) => {
if (res.statusCode == 200) {
state.show = true;
state.msg = "移除成功";
getDeviceList();
}
});
} else if (res.cancel) {
}
},
});
}
// 手动
function goInput() {
if (params.pondId.length > 0) {
const pid = params.pondId;
alertShow.value = false;
Taro.navigateTo({
url: "/my/addDevice?pondId=" + pid + "&page=" + page,
});
} else {
state.show = true;
state.msg = "请选择塘口";
}
}
/** 扫码识别 */
function goScan() {
if (params.pondId.length <= 0) {
state.show = true;
state.msg = "请选择塘口";
return;
}
Taro.scanCode({
success: (res) => {
const result = res.result;
deviceScan({ qrcode: result }).then((v) => {
if (v.statusCode == 200) {
params.devType = String(v.data.deviceType);
params.devNum = v.data.serialNum;
alertShow.value = false;
// 成功前往添加设备页面
submit();
} else {
errorShow.value = false;
alertShow.value = true;
state.show = true;
state.msg = "设备码识别失败";
}
});
},
fail: () => {
errorShow.value = false;
alertShow.value = true;
state.show = true;
state.msg = "设备码识别失败";
},
});
}
// 取消塘口选择
// 进入不同设备添加页面
function submit() {
if (params.pondId.length <= 0) {
state.show = true;
state.msg = "请选择塘口";
return;
}
if (!params.devNum) {
state.show = true;
state.msg = "请输入设备号";
return;
}
if (params.devType == 1) {
Taro.navigateTo({
url: "/my/addDetector?params=" + JSON.stringify(params) + "&page=" + page,
});
} else {
Taro.navigateTo({
url:
"/my/addController?params=" + JSON.stringify(params) + "&page=" + page,
});
}
}
// 查询塘口列表
function getPondList() {
getPond2().then((res: any) => {
if (res.statusCode == 200) {
pondList.value = res.data;
if (pondId) {
params.pondName = res.data.find((r) => r.id == pondId)?.["pondName"];
params.pondId = [pondId];
}
}
});
}
// 确定选择塘口
function confirm({ selectedValue, selectedOptions }) {
showPond.value = false;
params.pondId = selectedValue;
params.pondName = selectedOptions[0].pondName;
}
// 取消选择塘口
function cancel() {
showPond.value = false;
params.pondName = "";
params.pondId = [];
}
// 弹出塘口选择框
function popupPond() {
if (pondList.value.length > 0) {
showPond.value = true;
} else {
state.show = true;
state.msg = "暂无塘口,请添加塘口";
}
}
/** ------------------method end-------------------------- */
</script>
<style lang="scss">
.dev_home {
width: 100%;
height: 100%;
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
.body {
padding: 20px;
.title {
font-size: 32px;
color: #222;
font-weight: bold;
}
.time {
font-size: 24px;
color: #666666;
}
.mt {
margin-top: 10px;
}
.content {
margin-top: 10px;
font-size: 28px;
color: #444;
}
.price {
font-size: 48px;
color: #444444;
font-weight: bold;
}
.tag {
background: RGB(226, 35, 35, 0.1);
border-radius: 4px;
color: #e22323;
font-size: 28rpx;
}
}
.btn {
width: 100%;
position: fixed;
bottom: 30px;
// padding: 20px;
}
.nut-dialog {
padding: 0;
}
.nut-dialog__content {
margin: 0;
}
.nut-popup--center.round {
border-radius: 10px;
}
.nut-input--border {
border: 1px solid #eee;
}
.d_btn {
position: fixed;
bottom: 0px;
width: 100%;
font-size: 32rpx;
height: 80px;
text-align: center;
line-height: 80px;
}
}
</style>

740
src/my/deviceRecharge.vue Normal file
View File

@@ -0,0 +1,740 @@
<template>
<view class="dev_re_home">
<scroll-view :scroll-y="true" :style="{ height: '100%' }">
<view class="body">
<view v-if="deviceList.length > 0">
<nut-checkbox-group v-model="rechargeList">
<view v-for="(item, index) in deviceList" :key="index">
<nut-cell>
<nut-checkbox :label="item.id" :style="{ width: '100%' }">
<nut-row :gutter="10">
<nut-col :span="4">
<img
:src="item.deviceType == 1 ? rongjieyang : kongzhiqi"
width="80rpx"
height="80rpx"
/>
</nut-col>
<nut-col :span="10">
<view :style="{ display: 'flex', alignItems: 'center' }">
<view class="title">{{ item.deviceName }}</view>
</view>
<view class="pond">{{
item.pondInfo ? item.pondInfo.pondName : "无塘口"
}}</view>
</nut-col>
<nut-col
:span="10"
:style="{
height: '100%',
}"
>
<view
class="time"
:class="item.deadDay>=0 ?'c_E22323':item.ex? 'mt' : ''"
>
{{ item.deadTime ? formatDate(new Date(item.deadTime)) : "" }}
</view>
<view v-if="item.deadDay>=0" class="c_E22323">
已过期
</view>
<view v-else-if="item.ex" class="mt">
即将到期
</view>
</nut-col>
</nut-row>
</nut-checkbox>
</nut-cell>
</view>
<view :style="{ marginBottom: '80px' }"> </view>
</nut-checkbox-group>
</view>
<view v-else>
<nut-cell>
<nut-row>
<nut-col :span="24">
<nut-empty description="暂无设备">
<template #image>
<img :src="pond404" />
</template>
</nut-empty>
</nut-col>
<nut-col :span="24" :style="{ padding: '0 20rpx' }">
<nut-button
block
shape="square"
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '0px',
}"
@click="goBack"
>返回</nut-button
>
</nut-col>
</nut-row>
</nut-cell>
</view>
</view>
</scroll-view>
<view class="btn" v-if="deviceList.length > 0">
<nut-row>
<nut-col :span="24" :style="{ padding: '20rpx' }">
<nut-row
:style="{ display: 'flex', alignItems: 'center', justifyContent: 'center' }"
>
<nut-col :span="8" class="container">
<view>
<nut-checkbox v-model="selectAll" @change="changeAll">
全选
</nut-checkbox>
</view>
</nut-col>
<nut-col :span="8" class="container">
<view>
<nut-checkbox v-model="expSoon" @change="changeEx">
即将到期
</nut-checkbox>
</view>
</nut-col>
<nut-col
:span="8"
:style="{ background: '#17B4B2', color: '#fff' }"
class="container"
>
<view
@click="next"
:style="{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
}"
>
<view class="btn_title"> 下一步 </view>
<view class="btn_desc">
(已选{{ rechargeList.length }}{{ deviceList.length }})
</view>
</view>
</nut-col>
</nut-row>
</nut-col>
</nut-row>
</view>
<nut-popup v-model:visible="devOpen" position="bottom" :style="{ height: '55%' }">
<view class="d_body">
<nut-row>
<nut-col :span="6" @click="devOpen = false">
<RectLeft />
</nut-col>
<nut-col :span="12" class="d_title">设备充值</nut-col>
<nut-col :span="24">
<nut-radio-group
v-model="reType"
direction="horizontal"
text-position="left"
:style="{ widht: '100%' }"
@change="changeType"
>
<view v-if="payItem.length > 0">
<nut-cell
:class="reType == item.id ? 'nutActive' : ''"
v-for="(item, index) in payItem"
:key="index"
>
<nut-radio :label="item.id" :style="{ width: '100%' }">
<nut-row :gutter="10">
<nut-col :span="6">
<img
:src="year"
width="128rpx"
height="86rpx"
v-if="item.id == 10003"
/>
<img
:src="quarter"
width="128rpx"
height="86rpx"
v-if="item.id == 10002"
/>
<img
:src="month"
width="128rpx"
height="86rpx"
v-if="item.id == 10001"
/>
</nut-col>
<nut-col :span="12">
<view :style="{ display: 'flex', alignItems: 'center' }">
<view class="title">{{ item.title }}</view>
</view>
<view class="time">{{ item.description }}</view>
</nut-col>
<nut-col
:span="6"
:style="{
justifyContent: 'flex-end',
height: '100%',
display: 'flex',
alignItems: 'center',
}"
>
<view class="price" style="line-height: 98rpx">
¥{{ item.amount }}
</view>
</nut-col>
</nut-row>
<template #icon>
<IconFont :name="r_d" />
</template>
<template #checkedIcon>
<IconFont :name="r_v" />
</template>
</nut-radio>
</nut-cell>
</view>
<view v-else>
<view>
<nut-cell :class="reType == 0 ? 'nutActive' : ''">
<nut-radio :label="1" :style="{ width: '100%' }">
<nut-row :gutter="10">
<nut-col :span="6">
<img :src="year" width="128rpx" height="86rpx" />
</nut-col>
<nut-col :span="12">
<view :style="{ display: 'flex', alignItems: 'center' }">
<view class="title">年卡</view>
</view>
<view class="time">增加一年使用时间</view>
</nut-col>
<nut-col
:span="6"
:style="{
justifyContent: 'flex-end',
height: '100%',
display: 'flex',
alignItems: 'center',
}"
>
<view class="price" style="line-height: 98rpx"> ¥45 </view>
</nut-col>
</nut-row>
<template #icon>
<IconFont :name="r_d" />
</template>
<template #checkedIcon>
<IconFont :name="r_v" />
</template>
</nut-radio>
</nut-cell>
</view>
<view>
<nut-cell :class="reType == 1 ? 'nutActive' : ''">
<nut-radio :label="1" :style="{ width: '100%' }">
<nut-row :gutter="10">
<nut-col :span="6">
<img :src="quarter" width="128rpx" height="86rpx" />
</nut-col>
<nut-col :span="12">
<view :style="{ display: 'flex', alignItems: 'center' }">
<view class="title">季卡</view>
</view>
<view class="time">增加3个月使用时间</view>
</nut-col>
<nut-col
:span="6"
:style="{
justifyContent: 'flex-end',
height: '100%',
display: 'flex',
alignItems: 'center',
}"
>
<view class="price" style="line-height: 98rpx"> ¥45 </view>
</nut-col>
</nut-row>
<template #icon>
<IconFont :name="r_d" />
</template>
<template #checkedIcon>
<IconFont :name="r_v" />
</template>
</nut-radio>
</nut-cell>
</view>
<view>
<nut-cell :class="reType == 2 ? 'nutActive' : ''">
<nut-radio :label="2" :style="{ width: '100%' }">
<nut-row :gutter="10">
<nut-col :span="6">
<img :src="month" width="128rpx" height="86rpx" />
</nut-col>
<nut-col :span="12">
<view :style="{ display: 'flex', alignItems: 'center' }">
<view class="title">月卡</view>
</view>
<view class="time">增加一个月使用时间</view>
</nut-col>
<nut-col
:span="6"
:style="{
justifyContent: 'flex-end',
height: '100%',
display: 'flex',
alignItems: 'center',
}"
>
<view class="price" style="line-height: 98rpx"> ¥15 </view>
</nut-col>
</nut-row>
<template #icon>
<IconFont :name="r_d" />
</template>
<template #checkedIcon>
<IconFont :name="r_v" />
</template>
</nut-radio>
</nut-cell>
</view>
</view>
</nut-radio-group>
</nut-col>
<nut-col
:span="24"
class="font_32"
:style="{ textAlign: 'center', padding: '20rpx' }"
>
<text>续费</text>
<text class="font_36 c_E22323">{{ rechargeList.length }}</text>
<text>设备</text>
<text>总计</text>
<text class="font_36 c_E22323">{{ totalPrice }} </text>
</nut-col>
<nut-col :span="24">
<nut-button
block
shape="square"
type="primary"
size="large"
:loading="loading"
:style="{
fontWeight: 'bold',
fontSize: '38rpx !important',
borderRadius: '10px',
}"
@click="wxpay"
>微信支付</nut-button
>
</nut-col>
</nut-row>
</view>
</nut-popup>
<nut-toast :msg="state.msg" v-model:visible="state.show" :type="state.type" />
</view>
</template>
<script lang="ts" setup>
import { IconFont, RectLeft } from "@nutui/icons-vue-taro";
import { imgUrl } from "@/utils/imgUrl";
import Taro from "@tarojs/taro";
import { stateType } from "@/utils/types";
import { allDeviceList } from "@/api/device";
import { payOption, createOrder, wxPayOrder } from "@/api/my";
import CryptoJS from "crypto-js";
import jsrsasign from "jsrsasign";
definePageConfig({
navigationBarTitleText: "设备充值",
});
const kaiguan = `${imgUrl}kaiguan.png`;
const rongjieyang = `${imgUrl}rongjieyang.png`;
const kongzhiqi = `${imgUrl}kongzhiqi.png`;
const dev = `${imgUrl}dev.png`;
const dev1 = `${imgUrl}dev1.png`;
const dev2 = `${imgUrl}dev2.png`;
const month = `${imgUrl}month.png`;
const quarter = `${imgUrl}quarter.png`;
const year = `${imgUrl}year.png`;
const r_d = `${imgUrl}r_d.png`;
const r_v = `${imgUrl}r_v.png`;
const pond404 = `${imgUrl}zanwurenwu.png`;
const devOpen = ref(false);
const loading = ref(false);
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const rechargeList = ref([]);
const deviceList: any = ref([]);
// 全选
const selectAll = ref(false);
// 即将过期选择
const expSoon = ref(false);
// 充值的类目
const reType = ref(0);
const totalPrice = ref(0);
const payItem = ref([]);
/** ----------------method start------------------ */
Taro.useDidShow(() => {
// 查询设备列表
getDeviceList();
});
// 支付选项
function getPayItem() {
payOption().then((res: any) => {
if (res.statusCode == 200) {
payItem.value = res.data;
reType.value = res.data[0]["id"];
}
});
}
// 设备列表
function getDeviceList() {
allDeviceList({ type: 1 }).then((res: any) => {
if (res.statusCode == 200) {
res.data.forEach((item: any) => {
const deadDay = daysBeforeToday(formatDate(new Date(item.deadTime)));
item.deadDay = deadDay
if (deadDay >= -30) {
item.ex = true;
}
});
deviceList.value = res.data;
}
});
}
/** 全选 */
function changeAll(state) {
expSoon.value = false;
const res: any = [];
deviceList.value.forEach((r: any, i: number) => {
res.push(r.id);
});
if (state) {
rechargeList.value = res;
} else {
rechargeList.value = [];
}
}
/** 即将到期 */
function changeEx(state) {
selectAll.value = false;
const res: any = [];
deviceList.value.forEach((r: any, i: number) => {
if (r.ex) {
res.push(r.id);
}
});
if (state) {
rechargeList.value = res;
} else {
rechargeList.value = [];
}
}
/** 下一步 */
function next() {
payOption().then((res: any) => {
if (res.statusCode == 200) {
res.data.forEach((item) => {
// 分转元
item.amount = item.amount / 100;
});
payItem.value = res.data;
reType.value = res.data[0]["id"];
if (rechargeList.value.length > 0) {
devOpen.value = true;
if (payItem.value.length > 0) {
totalPrice.value =
Number(payItem.value[0]["amount"]) * rechargeList.value.length;
} else {
totalPrice.value = 99 * rechargeList.value.length;
}
} else {
state.msg = "请选择续费设备";
state.show = true;
}
}
});
}
/** 微信支付 */
function wxpay() {
loading.value = true;
Taro.login({
success: function (r) {
if (r.code) {
const data = {
js_code: r.code,
payId: reType.value,
listDeviceId: rechargeList.value,
};
// 创建订单
createOrder(data).then((res: any) => {
if (res.statusCode == 200) {
const orderInfo = res.data;
// 时间戳
const timestamp = String(Math.floor(Date.now() / 1000));
const appid = "wx9fb4034ebe52af77";
// 随机字符串
const nonceStr = generateRandomString(32);
const rsaParam = {
appId: appid,
timeStamp: timestamp,
nonceStr,
package: `prepay_id=${orderInfo.prepayId}`,
};
const sign = rsaEncrypt(rsaParam);
Taro.requestPayment({
timeStamp: timestamp,
nonceStr: nonceStr,
package: `prepay_id=${orderInfo.prepayId}`,
signType: "RSA",
paySign: sign,
success: function (res) {
devOpen.value = false;
state.msg = "续费成功";
state.show = true;
loading.value = false;
getDeviceList()
},
fail: function (res) {
devOpen.value = false;
state.msg = "续费异常,请稍后再试";
state.show = true;
loading.value = false;
},
});
}
});
} else {
}
},
fail: function () {
loading.value = false;
},
complete: function () {},
});
}
function generateRandomString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * chars.length);
result += chars.charAt(randomIndex);
}
return result;
}
function rsaEncrypt(data) {
const publicKeyPem = `-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDLEVxkdX6NJtDo
zXOzkxn7kqR3MMc05/duLON4yjmLhTPQD8iiDoCOEuZEy8dLloi1OlXO3zu/F5jd
9ynk8x++Px5A8gBQ16GZAMH18BQDzcFzy0EPe9ckiN3IXO8GT8Ht9oP430ugxF6t
GM7Ixh93v48n7tUOZTISX5TzZCbXOiITH40b++/LB2LaMo9xAC4dEQs+2S4x5gO9
PAh637ZAtVlbYxmJMYxzUM09KyXAoDlmO6YlO25EBkei7ZbyQob698s35+rSeq93
oFfUiegEL35SWeuuwp8ZFFvq24EUW7/n4U50/rS5hIFerEfcrFj2yPhP3sQtd4I+
FjaEKzQLAgMBAAECggEBALxeUV9a4labUCT0GETWyr5j7C4oUFHSb+KCv0uYA0NZ
/5McYRd67mNcQsBEa43BiPTbtSdeSnTbtdEI6pLXuHVo5W6HTiWvsNZWLpYt6tMQ
dUgtnh932D2qvFiVZKBtMc7E4uzkbaonHk/heEgkCKKzTl9Tm81rr8P9aBVSrPjt
6Xs7lORsD9bCoweYwfcrHELb+sLek/dOd9eCyabq7r5FV4EqOwJhw8btD7xgjw+o
uVF3kWyhLSEwjpN/oo4w5r4yVPlMnSTn7MtTRFyJcU/w5YBk8MedGSTKRIvzPv4s
RWU3ChTP5evGAh/FnpP6JGEhyvYGW0DqmuJUXK1n0fECgYEA/sSCT6Z+HSlwu/ui
OSlDDFKZ/ci7vJcuksgf5Tw5Kj0fBVhgImO8hTS9nqCQx73JJTsUpTzyYOVxUd9b
Aqp42dgVbDYTmkfBi0xAZpnQD4Z4LUj6OkS2k3+2CPNN+jQpibGjGy11NDV/U8EQ
P/9ioz+PoqwENG6wsaCT9LOC2DkCgYEAzAzUXJdH5T2LpSeO8e2NHtW6flemsJJ0
+m0O03OWxQr6KNjFpNkTFoK6A2NYOiyqMZcQb+viHGFTFpFHkozo3QPaXpZsz3BC
y9nQzqDbP3/MtfksE/MXOOw0qtdqT3csIxiXZqIjGYqAU08xcKyItZJGnIh/aKYn
yobDnKkCRmMCgYB9mvbAPE6bJA4/r/03/17eGW9wjuH4RfUhSudmxn1MlNvRb9Pd
wJx2dB00sucOg0RDRdCU8upw2U44Vk1xkAiLJpzRQAwEGXKTseFidFz++oYPlZZA
2hXFvMZLvWDphYQhLeJDiPLq7aE78siHNOs1nyW6xuI/037r5EZt838ECQKBgQDC
3+XY3+ob92Fsw5DzYIoMTtajXxalP9pUaN9l9tihKtCrPgvUWjSupP79yV0zggCx
B7L9EOyLai+uN+WBAu5KVimxeDxHCNHiWg3fqSR7SpS5nlUIYHtnM79BAiZX6lrO
0eeWb3bSJ8JSzilLkJunvSGO0ZXM3hLWi0o6TfcMPQKBgQDlmSC6/Ja4M+lGZEMx
raCryScSi1kj3KkZFcGQEEejltpet2u06af/qA+tmMW7uxjLj5R80yqsYmUqS6Md
pHc1U7XsqU2MinqVJz47H8WZwYMDDLjNOI2f4coqbtBBOA6GKt+gYYqYxkpvVanT
oShAmWqOcPsioBVCFRVGvMlcNA==
-----END PRIVATE KEY-----`;
let encipher = new jsrsasign.RSAKey();
// 把私钥转成16进制并设置秘钥
encipher = jsrsasign.KEYUTIL.getKey(publicKeyPem);
// 设置SHA256withRSA方式加密
const sig = new jsrsasign.KJUR.crypto.Signature({
alg: "SHA256withRSA",
});
// 初始化
sig.init(encipher);
// 需要加密的字段,这里注意下一定要按每一个字段一行,切末位加上 \n
const str =
`${data.appId}\n` +
`${data.timeStamp}\n` +
`${data.nonceStr}\n` +
`${data.package}\n`;
sig.updateString(str);
// 加密后,转成base64
return jsrsasign.hextob64(sig.sign());
}
function changeType() {
/**
* 0 99
* 1 45
* 2 15
*/
const count = rechargeList.value.length;
const type = reType.value;
var price = 0;
if (payItem.value.length > 0) {
const payRes = payItem.value.filter((item) => item.id == type);
price = count * Number(payRes[0]["amount"]);
} else {
var price = 99;
if (type == 0) {
price = count * 99;
} else if (type == 1) {
price = count * 45;
} else if (type == 2) {
price = count * 15;
}
}
totalPrice.value = price;
}
// 返回上一页
function goBack() {
Taro.switchTab({
url: "/pages/main/my",
});
}
/** ----------------method end-------------------- */
</script>
<style lang="scss">
.dev_re_home {
width: 100%;
height: 100%;
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
.container {
display: flex;
align-items: center;
justify-content: center;
background: #fff;
height: 100rpx;
}
.body {
padding: 20px;
.title {
font-size: 32px;
color: #222;
font-weight: bold;
}
.pond {
font-size: 30px;
color: #666666;
}
.mt {
color: rgb(255, 190, 13) !important;
}
.content {
margin-top: 10px;
font-size: 28px;
color: #444;
}
.time {
font-size: 30px;
color: #17b4b2;
font-weight: bold;
}
}
.btn {
width: 100%;
position: fixed;
bottom: 30px;
.btn_title {
font-size: 36px;
}
.btn_desc {
font-size: 24px;
opacity: 0.8;
}
}
.d_body {
padding: 30px;
.d_title {
font-size: 36rpx;
font-weight: bold;
color: #222;
text-align: center;
}
.d_content {
font-size: 28rpx;
color: #888888;
}
.d_c_title {
font-size: 32rpx;
color: #222;
}
.title {
font-size: 32px;
color: #222222;
font-weight: bold;
}
.price {
font-size: 38px;
color: #17b4b2;
font-weight: bold;
}
.time {
font-size: 26px;
color: #888888;
}
.nut-radio-group {
width: 100%;
}
.nut-cell {
padding: 20px;
}
.nutActive {
border: 1px solid #17b4b2;
borderradius: 10px;
}
}
.d_btn {
position: fixed;
bottom: 0px;
width: 100%;
font-size: 32rpx;
height: 80px;
text-align: center;
line-height: 80px;
}
}
</style>

160
src/my/help.vue Normal file
View File

@@ -0,0 +1,160 @@
<template>
<view class="help_home">
<nut-config-provider :theme="themeVars">
<scroll-view :scroll-y="true" :style="{ height: '100%' }">
<view class="body">
<view>
<nut-cell title="售后服务电话" desc="17685506229" />
<nut-cell-group>
<nut-cell title="溶解氧设备安装使用指导" is-link />
<nut-cell title="测控一体机安装使用指导" is-link />
</nut-cell-group>
<nut-cell-group>
<nut-cell title="用户使用协议" is-link @click="toAgreement" />
<nut-cell title="隐私保护政策" is-link @click="toPriAgreement" />
</nut-cell-group>
<nut-cell-group>
<nut-cell
title="检测更新"
:desc="version"
is-link
@click="checkUpdate"
/>
<!-- <nut-cell title="注销账号" is-link @click="logOut" /> -->
</nut-cell-group>
<nut-row>
<nut-col :span="24" class="d_desc">
提示:养殖设备为养殖工作的辅助工具不能完全代替养殖工作的管理您仍需留意养殖情况的变化并及时处理避免造成损失
</nut-col>
</nut-row>
</view>
</view>
</scroll-view>
</nut-config-provider>
<!-- 弹出层提示 -->
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
</view>
</template>
<script lang="ts" setup>
import Taro from "@tarojs/taro";
import { stateType } from "@/utils/types";
import { logoutFun } from "@/api/login";
definePageConfig({
navigationBarTitleText: "使用帮助",
});
const store: any = inject("store");
const themeVars = ref({
cellTitleFont: "32rpx",
cellDescFont: "32rpx",
cellDescColor: "#222222",
celColor: "#666666",
});
const version = Taro.getAccountInfoSync()["miniProgram"]["version"];
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
function checkUpdate() {
//判断目前微信版本是否支持自动更新
if (Taro.canIUse("getUpdateManager")) {
const update = Taro.getUpdateManager();
update.onCheckForUpdate((res) => {
//检测是否有新版本
if (res.hasUpdate) {
update.onUpdateReady(() => {
// update.applyUpdate();
//如果有新版本,给用户提示确认更新即可
Taro.showModal({
title: "更新提示",
content: "新版本已经准备好,是否重启应用?",
success: function (res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启,如果想静默更新,直接在检测有新版本的时候调用此方法即可
update.applyUpdate();
}
},
});
});
//如果自动更新失败给用户提示手动更新有些小程序涉及到多ID使用不更新会出现莫名的缓存bug大部分小程序不用执行这一步
update.onUpdateFailed(() => {
Taro.showModal({
title: "已经有新版本了",
content: "新版本已经上线,请您删除当前小程序,重新打开。",
});
});
} else {
state.show = true;
state.msg = "已是最新版本";
return;
}
});
} else {
Taro.showModal({
title: "提示",
content: "当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。",
});
}
}
/** 查看用户协议 */
function toAgreement() {
Taro.redirectTo({
url: "/agreement/index",
});
}
/** 隐私协议 */
function toPriAgreement() {
Taro.redirectTo({
url: "/agreement/indexpri",
});
}
/** 退出登录 */
function logOut() {
Taro.showModal({
title: "提示",
content: "确认注销当前账号?",
success: function (res) {
if (res.confirm) {
logoutFun();
Taro.clearStorageSync();
Taro.clearStorage();
store.updateRootUserId(null);
store.updateRootUserName(null);
Taro.redirectTo({
url: "/pages/login/index?agreement=1",
});
}
},
});
}
</script>
<style lang="scss">
.help_home {
width: 100%;
height: 100%;
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
position: fixed;
margin: 0;
.body {
padding: 20px;
.nut-cell__value {
color: #222;
font-size: 32rpx;
}
.d_desc {
font-size: 32px;
color: #929a9b;
}
}
}
</style>

4
src/my/index.config.ts Normal file
View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationStyle: 'custom',
disableScroll: true
})

257
src/my/subAccount.vue Normal file
View File

@@ -0,0 +1,257 @@
<template>
<nut-row class="sub_home">
<nut-col :span="24" class="body" :style="{ padding: '0 20rpx' }">
<nut-config-provider :theme="themeVars">
<view v-if="accountList.length > 0">
<nut-cell v-for="item in accountList" :key="item.id">
<template #icon>
<IconFont :name="phone" :style="{ height: '50rpx' }" />
</template>
<template #title>
<view class="c_222 font_36">{{ item.mobilePhone }}</view>
</template>
<template #link>
<view @click="del(item.id)" class="c_E22323 font_36">删除</view>
</template>
</nut-cell>
</view>
<view v-else>
<nut-cell>
<nut-col :span="24">
<nut-empty description="暂无子账号">
<template #image>
<img :src="pond404" />
</template>
</nut-empty>
</nut-col>
</nut-cell>
</view>
<!-- <nut-row>
<nut-col :span="24" class="d_desc">
1.如有其他用户需要查看您账号内绑定的设备信息将其注册账号时使用的手机号添加为子账号即可
</nut-col>
<nut-col :span="24" class="d_desc">
2.子账号有权进行参数配置告警定时设置等但无权转移塘口增删设备
</nut-col>
</nut-row> -->
</nut-config-provider>
</nut-col>
<nut-col :span="24" :style="{ padding: '0 20rpx' }">
<view class="btn">
<nut-button
block
shape="square"
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
borderRadius: '0px',
}"
@click="add"
>新增子账号</nut-button
>
</view>
</nut-col>
<!-- 弹出层提示 -->
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
<!-- 弹出添加设备提示 -->
<nut-config-provider :theme-vars="themeVars2">
<nut-dialog v-model:visible="addShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body">
<nut-row>
<nut-col :span="24">
<view class="d_title">设置子账号</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24">
<nut-input
type="digit"
v-model="account"
placeholder="请输入子账号的手机号"
:border="false"
input-align="left"
clearable
max-length="11"
>
</nut-input>
</nut-col>
</nut-row>
</view>
<view class="d_btn">
<nut-row>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#222' }"
@click="cancel"
>
取消
</nut-col>
<nut-col
:span="12"
:style="{ border: '1px solid #eee', color: '#15BBC5' }"
@click="submitD"
>
确定
</nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
</nut-config-provider>
</nut-row>
</template>
<script lang="ts" setup>
import { list_user_child, add_user_child, delete_user_child } from "@/api/sub";
import Taro from "@tarojs/taro";
import { stateType } from "@/utils/types";
import { IconFont } from "@nutui/icons-vue-taro";
import { imgUrl } from "@/utils/imgUrl";
import { debounce,throttle } from 'lodash';
definePageConfig({
navigationBarTitleText: "子账号管理",
});
const pond404 = `${imgUrl}zanwurenwu.png`;
const phone = `${imgUrl}phone.png`;
const themeVars = ref({
cellTitleFont: "36rpx",
cellDescFont: "36rpx",
cellDescColor: "#222222",
celColor: "#666666",
});
const themeVars2 = ref({
cellBoxShadow: "0px",
cellPadding: "0",
popupBorderRadius: "10rpx",
cellDescColor: "#222222",
});
const accountList = ref([]);
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const addShow = ref(false);
const account = ref("");
/** --------------- method start---------------------- */
Taro.useDidShow(() => {
// 获取列表
getAccountList();
});
// 获取子账号列表
function getAccountList() {
list_user_child().then((res: any) => {
if (res.statusCode == 200) {
accountList.value = res.data;
}
});
}
// 新增
function add() {
addShow.value = true;
}
// 验证手机号码
function isValidPhoneNumber(phoneNumber) {
const regex = /^1[3-9]\d{9}$/;
return regex.test(phoneNumber);
}
// 删除
function del(id) {
Taro.showModal({
title: "提示",
content: "确认删除该子账号!",
success: function (res) {
if (res.confirm) {
delete_user_child({ id }).then((res: any) => {
if (res.statusCode == 200) {
state.show = true;
state.msg = "删除成功";
getAccountList();
}
});
}
},
});
}
function cancel() {
account.value = "";
addShow.value = false;
}
const submitD = debounce(submit, 300);
function submit() {
if (!account.value) {
state.show = true;
state.msg = "请输入手机号";
return;
}
if (!isValidPhoneNumber(account.value)) {
state.show = true;
state.msg = "手机号格式错误";
return;
}
add_user_child(Number(account.value)).then((res) => {
if (res.statusCode == 200) {
state.show = true;
state.msg = "添加成功";
cancel();
getAccountList();
}
});
}
/** --------------- method end------------------------ */
</script>
<style lang="scss">
.sub_home {
width: 100%;
height: 100vh;
background-color: #e5ebed;
background-repeat: no-repeat;
background-size: 100% 100%;
margin: 0;
.body {
.nut-cell__value {
color: #222;
font-size: 32rpx;
}
.nut-cell__title {
min-width: 230px;
}
.d_desc {
font-size: 32px;
color: #929a9b;
}
}
.btn {
width: 95%;
position: fixed;
bottom: 30px;
// padding: 20px;
}
.nut-dialog {
padding: 0;
}
.nut-dialog__content {
margin: 0;
}
.nut-input {
padding: 20rpx 0;
}
}
</style>

6
src/my/types.ts Normal file
View File

@@ -0,0 +1,6 @@
export interface paramsType {
pondId: string | any;
pondName: string;
devType: number | undefined;
devNum: string | undefined;
}