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

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '登录'
})

278
src/pages/login/index.vue Normal file
View File

@@ -0,0 +1,278 @@
<template>
<view class="login_home">
<nut-row>
<nut-col :span="24" class="login_tiitle">
<div class="title">你好</div>
<div class="title">欢迎登录鱼测云</div>
</nut-col>
<nut-popup
v-model:visible="show"
position="bottom"
:closeable="false"
:overlay="false"
round
:style="{ height: '83%' }"
>
<nut-col :span="24">
<nut-config-provider :theme-vars="themeVars">
<view :style="{ padding: '0 16px', marginTop: '20px' }">
<nut-button
block
type="primary"
size="large"
open-type="getPhoneNumber"
@getphonenumber="wxLogin"
:loading="isLoading"
v-if="loginForm.agreement"
>手机号一键登录</nut-button
>
<nut-button block size="large" type="primary" v-else @click="wxLoginCheck"
>手机号一键登录</nut-button
>
<nut-divider> OR </nut-divider>
<nut-button
block
type="primary"
size="large"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
color: '#09B8C2 !important',
}"
color="#E7F8F9"
@click="login"
>其它手机号登录</nut-button
>
<!-- 底部协议 -->
<view class="container agrBottom">
<view>
<nut-checkbox v-model="loginForm.agreement" />
</view>
<view class="font_28 c_888 view_f"
>我已阅读<view :style="{ color: '#09B8C2' }" @click="toAgreement()"
>用户使用协议</view
><view :style="{ color: '#09B8C2' }" @click="toPriAgreement()"
>隐私政策</view
>并理解相关条款内容</view
>
</view>
</view>
</nut-config-provider>
</nut-col>
<nut-col
:span="24"
@click="unlogin"
class="agrBottom2"
:style="{
fontWeight: 'bold',
fontSize: '14px !important',
color: '#09B8C2 !important',
marginTop: '10rpx',
textAlign: 'center',
}"
>
暂不登录
</nut-col>
</nut-popup>
<!-- 弹出层提示 -->
<nut-toast :msg="state.msg" v-model:visible="state.show" :type="state.type" />
</nut-row>
</view>
</template>
<script setup lang="ts" name="Login">
import { loginFormType } from "./types";
import { stateType } from "@/utils/types";
import { loginWxToPhone } from "@/api/login";
import Taro from "@tarojs/taro";
// 已阅读参数
const instance = Taro.getCurrentInstance();
const store: any = inject("store");
const agreement = instance.router.params.agreement;
const show = ref(true);
const loginForm: loginFormType = reactive({
phonenumber: "",
code: "",
identity: false,
agreement: agreement ? true : false,
});
const themeVars = ref({
formItemLabelFontSize: "15px",
cellBorderRadius: "0px",
cellBoxShadow: "0px",
checkboxLabelMarginLeft: "0px",
checkboxMarginRight: "2px",
});
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const isLoading = ref(false);
/** -----------------method start----------------- */
Taro.useDidShow(() => {
if(agreement){
loginForm.agreement = true
}else{
loginForm.agreement = false
}
Taro.setStorageSync("UnLogin", 1);
store.updateUnLogin(1);
})
/** 手机号码登录 */
function login() {
if (!loginForm.agreement) {
state.msg = "请阅读用户协议";
state.show = true;
return;
}
// 跳转手机号登录
Taro.navigateTo({ url: "/pages/login/loginPhone" });
}
function wxLoginCheck() {
if (!loginForm.agreement) {
state.msg = "请阅读用户协议";
state.show = true;
return;
}
}
/** 微信登录 */
function wxLogin(e) {
isLoading.value = true;
if (e.detail.code) {
Taro.login({
success: function (res) {
if (res.code) {
loginWxToPhone({ code: e.detail.code, js_code: res.code })
.then((res) => {
if (res.statusCode == 200) {
Taro.setStorageSync(
"ReTime",
res.data.createdTime
? formatDate(new Date(res.data.createdTime))
: formatDate(new Date())
);
Taro.setStorageSync("UnLogin", 2);
Taro.setStorageSync("UserName", res.data.userName);
Taro.setStorageSync("Phone", res.data.mobilePhone);
Taro.setStorageSync("UserId", res.data.id);
store.updateRootUserId(res.data.id);
store.updateUserId(res.data.id);
store.updateUnLogin(2);
store.updateRootUserName(res.data.userName);
store.updateLoginStatus(0);
state.msg = "登录成功";
state.show = true;
Taro.switchTab({
url: "/pages/main/home",
});
return;
}
})
.finally(() => {
isLoading.value = false;
});
} else {
state.msg = "登录失败";
state.show = true;
isLoading.value = false;
}
},
fail: function () {
state.msg = "登录失败";
state.show = true;
isLoading.value = false;
},
});
} else {
state.msg = "登录失败";
state.show = true;
isLoading.value = false;
}
}
/** 查看用户协议 */
function toAgreement() {
Taro.redirectTo({
url: "/agreement/index?phonenumber=" + loginForm.phonenumber + "&pages=login",
});
}
/** 隐私协议 */
function toPriAgreement() {
Taro.redirectTo({
url: "/agreement/indexpri?phonenumber=" + loginForm.phonenumber + "&pages=login",
});
}
Taro.useDidShow(() => {
if (!Taro.getStorageSync("Access-Token")) {
Taro.clearStorageSync();
} else {
Taro.switchTab({
url: "/pages/main/home",
});
}
});
/** 暂不登录 */
function unlogin() {
Taro.setStorageSync("UnLogin", 1);
store.updateUnLogin(1);
Taro.switchTab({
url: "/pages/main/home",
});
}
/** -----------------method end------------------- */
</script>
<style lang="scss">
.login_home {
height: 100%;
width: 100%;
background-size: 100% 100%;
position: fixed;
margin: 0;
background: linear-gradient(90deg, #74dce2 0%, #ddf7f2 100%);
font-size: 32px;
font-family: "PingFang SC";
font-weight: 400;
.login_tiitle {
padding: 20px !important;
.title {
font-size: 46px;
color: #000;
font-family: "PingFang SC";
font-weight: 800;
}
}
.itemLabel {
color: #000;
font-family: "PingFang SC";
font-weight: 400;
}
.code {
color: #09b8c2;
}
.disabled {
color: #ccc;
}
.container {
display: flex;
}
.agrBottom {
position: fixed;
bottom: 50%;
left: 2%;
}
.agrBottom2 {
position: fixed;
bottom: 45%;
}
}
</style>

View File

@@ -0,0 +1,252 @@
<template>
<view class="login_home">
<nut-row>
<nut-col :span="24" class="login_tiitle">
<div class="title">你好</div>
<div class="title">欢迎登录鱼测云</div>
</nut-col>
<nut-popup
v-model:visible="show"
position="bottom"
:closeable="false"
:overlay="false"
round
:style="{ height: '83%' }"
>
<nut-config-provider :theme-vars="themeVars">
<nut-form
label-position="top"
:show-error-line="false"
:show-error-message="false"
ref="loginRef"
:style="{ border: '0px !important' }"
>
<nut-form-item label="手机号" class="itemLabel">
<nut-input
v-model="loginForm.phonenumber"
type="number"
placeholder="请输入手机号"
max-length="11"
/>
</nut-form-item>
<nut-form-item label="验证码" class="itemLabel">
<nut-input
v-model="loginForm.code"
type="number"
placeholder="请输入验证码"
max-length="6"
>
<template #right>
<view
:class="btnContent.canClick ? 'code' : 'disabled'"
@click="getCode()"
>{{ btnContent.content }}
</view>
</template>
</nut-input>
</nut-form-item>
<view :style="{ padding: '0 16px', marginTop: '20px' }">
<nut-button
block
type="primary"
size="large"
:style="{ fontWeight: 'bold', fontSize: '14px !important' }"
@click="login"
:loading="isLoading"
>登录</nut-button
>
</view>
</nut-form>
</nut-config-provider>
</nut-popup>
<!-- 弹出层提示 -->
<nut-toast
:msg="state.msg"
v-model:visible="state.show"
:type="state.type"
/>
</nut-row>
</view>
</template>
<script setup lang="ts" name="Login">
import { loginFormType, contentBtnType } from "./types";
import { stateType } from "@/utils/types";
import validatePhone from "@/utils/validate";
import { loginSms, smsCode } from "@/api/login";
import Taro from "@tarojs/taro";
// 已阅读参数
const store: any = inject("store");
const show = ref(true);
const loginForm: loginFormType = reactive({
phonenumber: "",
code: "",
identity: false,
});
const themeVars = ref({
formItemLabelFontSize: "15px",
cellBorderRadius: "0px",
cellBoxShadow: "0px",
checkboxLabelMarginLeft: "0px",
checkboxMarginRight: "2px",
});
const btnContent: contentBtnType = reactive({
content: "获取验证码",
totalTime: 60,
canClick: true,
});
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
});
const isLoading = ref(false);
/** -----------------method start----------------- */
/** 验证码读秒 */
function setTime() {
if (!btnContent.canClick) return; // 如果是false 直接return出去
btnContent.canClick = false;
btnContent.content = btnContent.totalTime + "s后重新发送";
let clock = window.setInterval(() => {
btnContent.totalTime--;
btnContent.content = btnContent.totalTime + "s后重新发送";
if (btnContent.totalTime < 0) {
window.clearInterval(clock);
btnContent.content = "重新发送";
btnContent.totalTime = 60;
btnContent.canClick = true; // 这里重新开启
}
}, 1000);
}
/** 获取验证码 */
function getCode() {
if(!btnContent.canClick){
return
}
const v_phone_res = validatePhone(loginForm.phonenumber);
if (v_phone_res == 1) {
state.msg = "手机号不能为空";
state.show = true;
} else if (v_phone_res == 2) {
state.msg = "手机号格式不正确";
state.show = true;
} else {
smsCode(loginForm.phonenumber).then((res: any) => {
state.msg = "发送成功";
state.show = true;
setTime();
});
}
}
/** 手机号码登录 */
function login() {
const v_phone_res = validatePhone(loginForm.phonenumber);
if (v_phone_res == 1) {
state.msg = "手机号不能为空";
state.show = true;
return;
} else if (v_phone_res == 2) {
state.msg = "手机号格式不正确";
state.show = true;
return;
}
if (!loginForm.code) {
state.msg = "请输入验证码";
state.show = true;
return;
}
if (loginForm.code.length != 6) {
state.msg = "验证码为6位数字";
state.show = true;
return;
}
isLoading.value = true
loginSms(loginForm.phonenumber, loginForm.code).then((res: any) => {
if (res.statusCode == 200) {
Taro.setStorageSync(
"ReTime",
res.data.createdTime
? formatDate(new Date(res.data.createdTime))
: formatDate(new Date())
);
Taro.setStorageSync("UserName", res.data.userName);
Taro.setStorageSync("Phone", res.data.mobilePhone);
Taro.setStorageSync("LoginType", "1");
Taro.setStorageSync("UserId", res.data.id);
Taro.setStorageSync("UnLogin", 2);
store.updateLoginStatus(0);
store.updateUnLogin(2);
store.updateRootUserId(res.data.id);
store.updateUserId(res.data.id);
store.updateRootUserName(res.data.userName);
state.msg = "登录成功";
state.show = true;
Taro.switchTab({
url: "/pages/main/home",
});
return;
}
}).finally(()=>{
isLoading.value = false
})
}
Taro.useDidShow(() => {
if (!Taro.getStorageSync("Access-Token")) {
Taro.clearStorageSync();
} else {
Taro.switchTab({
url: "/pages/main/home",
});
}
});
/** -----------------method end------------------- */
</script>
<style lang="scss">
.login_home {
height: 100%;
width: 100%;
background-size: 100% 100%;
position: fixed;
margin: 0;
background: linear-gradient(90deg, #74dce2 0%, #ddf7f2 100%);
font-size: 32px;
font-family: "PingFang SC";
font-weight: 400;
.login_tiitle {
padding: 20px !important;
.title {
font-size: 46px;
color: #000;
font-family: "PingFang SC";
font-weight: 800;
}
}
.itemLabel {
color: #000;
font-family: "PingFang SC";
font-weight: 400;
}
.code {
color: #09b8c2;
}
.disabled {
color: #ccc;
}
.container {
display: flex;
}
.agrBottom {
position: fixed;
bottom: 8%;
}
}
</style>

11
src/pages/login/types.ts Normal file
View File

@@ -0,0 +1,11 @@
export interface loginFormType {
phonenumber: string;
code: string;
identity: boolean;
agreement: boolean;
}
export interface contentBtnType {
content: string;
totalTime: number;
canClick: boolean;
}

1450
src/pages/main/home.vue Normal file

File diff suppressed because it is too large Load Diff

View File

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

746
src/pages/main/my.vue Normal file
View File

@@ -0,0 +1,746 @@
<template>
<view class="my_home">
<CustomNavigationBar ref="custNavBar" />
<nut-row class="container">
<nut-config-provider :theme-vars="themeVars">
<nut-row class="body">
<nut-col :span="4">
<img :src="pond404" width="96rpx" height="96rpx" class="ava" />
</nut-col>
<nut-col :span="20">
<view class="nickname view_f" v-if="store.getUnLogin == 1">
<view @click="toLogin">Hello{{ username }}</view>
</view>
<view class="nickname view_f" @click="setNickname" v-else>
<view>Hello{{ username }}</view>
<Edit size="12" :style="{ marginLeft: '15rpx' }" />
</view>
<view class="phone view_f" @click="setPhoneNumber" v-if="store.getUnLogin != 1">
<view>{{ phonenumber }}</view>
<Edit size="12" :style="{ marginLeft: '15rpx' }" />
</view>
</nut-col>
</nut-row>
<scroll-view :scroll-y="true" :style="{ height: '70vh' }">
<nut-row>
<nut-col
:span="24"
class="menu"
:style="{
paddingLeft: '20rpx',
paddingRight: '20rpx',
paddingTop: '20rpx',
paddingBottom: '20rpx',
}">
<nut-cell class="menu_body">
<nut-row>
<nut-col :span="24" class="title"> 常用功能 </nut-col>
<nut-col :span="24">
<nut-cell-group>
<nut-cell title="设备列表" is-link @click="gotoDev">
<template #icon>
<IconFont :name="i1" size="24" />
</template>
</nut-cell>
<nut-cell title="设备充值" is-link @click="gotoDevRecharge">
<template #icon>
<IconFont :name="i2" size="24" />
</template>
</nut-cell>
<nut-cell title="使用帮助" is-link @click="gotoHelp">
<template #icon>
<IconFont :name="i3" size="24" />
</template>
</nut-cell>
<nut-cell title="子账号管理" is-link @click="gotoSub">
<template #icon>
<IconFont :name="i4" size="24" />
</template>
</nut-cell>
<nut-cell title="报警电话设置" is-link @click="gotoPhone">
<template #icon>
<IconFont :name="i5" size="24" />
</template>
</nut-cell>
<nut-cell title="在线客服" is-link @click="goContact">
<template #icon>
<IconFont :name="i6" size="24" />
</template>
</nut-cell>
<nut-cell title="退出登录" is-link @click="logOut">
<template #icon>
<IconFont :name="i7" size="24" />
</template>
</nut-cell>
<!-- <nut-cell :style="{ height: '100px' }">
</nut-cell> -->
</nut-cell-group>
</nut-col>
</nut-row>
</nut-cell>
<nut-cell>
<nut-row :style="{ padding: '0rpx 20rpx' }">
<nut-col :span="18">
<view class="font_32 c_222">
请添加报警电话到手机通讯录以免漏接
<!-- <text class="c_E22323">02162321772</text> -->
</view>
</nut-col>
<nut-col :span="6" :style="{ textAlign: 'right' }">
<!-- <nut-button type="primary" size="small">收藏</nut-button> -->
<nut-button type="primary" size="small" @click="collect"> 保存 </nut-button>
</nut-col>
<!-- <nut-col
:span="6"
@click="collect('02162321772')"
:style="{ textAlign: 'right' }"
>
<nut-button type="primary" size="small">收藏</nut-button>
</nut-col> -->
</nut-row>
</nut-cell>
</nut-col>
</nut-row>
</scroll-view>
<!-- 弹出层提示 -->
<nut-dialog v-model:visible="contactShow" text-align="center" pop-class="pop-class">
<template #default>
<view :style="{ height: '100%' }">
<nut-row>
<nut-col :span="24" class="font_36 c_222"> 确定前往在线客服? </nut-col>
</nut-row>
</view>
</template>
<template #footer>
<view class="d_btn" :style="{ position: 'absolute' }">
<nut-row>
<nut-col :span="12" :style="{ border: '1px solid #eee', color: '#222' }" @click="contactShow = false"> 取消 </nut-col>
<nut-col :span="12" :style="{ border: '1px solid #eee', color: '#15BBC5' }">
<nut-button type="primary" open-type="contact" shape="square" block>确定</nut-button>
</nut-col>
</nut-row>
</view>
</template>
</nut-dialog>
<!-- 修改手机号码弹出层 -->
<nut-dialog v-model:visible="setPhoneShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body" :style="{ paddingBottom: '0px' }">
<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: '30%' }" class="font_32 c_444">原手机号</view>
<view :style="{ width: '70%' }">
<nut-input type="number" v-model="phonenumber" :border="false" input-align="right" readonly />
</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24" class="view_f">
<view :style="{ width: '30%' }" class="font_32 c_444">新手机号</view>
<view :style="{ width: '70%' }">
<nut-input type="number" v-model="newPhone" :border="false" input-align="right" placeholder="请输入新手机号" clearable max-length="11" :cursor="String(phnewPhoneone1).length" />
</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 v-model="code" type="number" :border="false" input-align="right" placeholder="请输入验证码" max-length="6" clearable>
<template #right>
<view :class="btnContent.canClick ? 'code' : 'disabled'" @click="getCode()">{{ btnContent.content }} </view>
</template>
</nut-input>
</view>
</nut-col>
</nut-row>
</view>
<view class="d_btn">
<nut-row>
<nut-col :span="12" :style="{ border: '1px solid #eee', color: '#222' }" @click="cancelSetPhone"> 取消 </nut-col>
<nut-col :span="12" :style="{ border: '1px solid #eee', color: '#15BBC5' }" @click="toSetPhoneD"> 确定 </nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
<!-- 修改昵称 -->
<nut-dialog v-model:visible="setNameShow" text-align="left" no-footer>
<view :style="{ height: '100%' }">
<view class="d_body" :style="{ paddingBottom: '0px' }">
<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: '30%' }" class="font_32 c_444">昵称</view>
<view :style="{ width: '70%' }">
<nut-input v-model="newName" :border="false" input-align="right" max-length="8" placeholder="请输入昵称" clearable :cursor="String(newName).length" />
</view>
</nut-col>
</nut-row>
</view>
<view class="d_btn2">
<nut-row>
<nut-col :span="12" :style="{ border: '1px solid #eee', color: '#222' }" @click="cancelSetNickname"> 取消 </nut-col>
<nut-col :span="12" :style="{ border: '1px solid #eee', color: '#15BBC5' }" @click="toSetNameD"> 确定 </nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
<!-- 收藏号码弹窗 -->
<nut-dialog v-model:visible="collectShow" text-align="left" no-footer>
<view :style="{ height: '350px' }">
<!-- <view :style="{ height: '190px' }"> -->
<view class="d_body" :style="{ paddingBottom: '0px' }">
<nut-row>
<nut-col :span="24">
<view class="d_title">请保存以下报警号码</view>
</nut-col>
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
</nut-row>
<nut-row style="height: 40px;">
<nut-col :span="18">
<text>057123675281</text>
</nut-col>
<nut-col :span="6" :style="{ textAlign: 'right' }">
<nut-button type="primary" size="small" @click="saveNnmber('057123675281')"> 保存 </nut-button>
</nut-col>
</nut-row>
<nut-row style="height: 40px;">
<nut-col :span="18">
<text>057123675271</text>
</nut-col>
<nut-col :span="6" :style="{ textAlign: 'right' }">
<nut-button type="primary" size="small" @click="saveNnmber('057123675271')"> 保存 </nut-button>
</nut-col>
</nut-row>
<nut-row style="height: 40px;">
<nut-col :span="18">
<text>057123675287</text>
</nut-col>
<nut-col :span="6" :style="{ textAlign: 'right' }">
<nut-button type="primary" size="small" @click="saveNnmber('057123675287')"> 保存 </nut-button>
</nut-col>
</nut-row>
<nut-row style="height: 40px;">
<nut-col :span="18">
<text>02162321772</text>
</nut-col>
<nut-col :span="6" :style="{ textAlign: 'right' }">
<nut-button type="primary" size="small" @click="saveNnmber('02162321772')"> 保存 </nut-button>
</nut-col>
</nut-row>
<nut-row style="height: 40px;">
<nut-col :span="24">
<nut-divider class="divider" />
</nut-col>
<nut-col :span="24" :style="{ textAlign: 'left', fontSize: '14px', color: '#E6A23C' }">
<text>每添加完一个号码先彻底关闭电话本再点击添加第2个号码否则可能会出现卡在联系人页面</text>
</nut-col>
</nut-row>
</view>
<view class="d_btn2">
<nut-row>
<nut-col :span="24" :style="{ border: '1px solid #eee', color: '#222' }" @click="collectCancel"> 取消 </nut-col>
</nut-row>
</view>
</view>
</nut-dialog>
</nut-config-provider>
<!-- 弹出层提示 -->
<nut-toast :msg="state.msg" v-model:visible="state.show" :type="state.type" />
</nut-row>
</view>
</template>
<script setup lang="ts">
import CustomNavigationBar from "@/components/custom-navigation-bar/index"
import { IconFont, Edit } from "@nutui/icons-vue-taro"
import Taro from "@tarojs/taro"
import { updatePhone, updateNickName } from "@/api/my"
import { logoutFun } from "@/api/login"
import { smsCode } from "@/api/login"
import validatePhone from "@/utils/validate"
import { stateType } from "@/utils/types"
import { imgUrl } from "@/utils/imgUrl"
import { msgWarn } from "@/api/msg"
import { debounce, throttle } from "lodash"
definePageConfig({
navigationStyle: "custom",
disableScroll: true,
})
const store: any = inject("store")
const pond404 = `${imgUrl}zanwurenwu.png`
const i1 = `${imgUrl}i1.png`
const i2 = `${imgUrl}i2.png`
const i3 = `${imgUrl}i3.png`
const i4 = `${imgUrl}i4.png`
const i5 = `${imgUrl}i5.png`
const i6 = `${imgUrl}i6.png`
const i7 = `${imgUrl}i7.png`
const themeVars = ref({
cellBorderRadius: "4px",
cellBoxShadow: "0px",
cellPadding: "26rpx 0px",
popupBorderRadius: "10rpx",
})
const username = ref(Taro.getStorageSync("UserName") ? Taro.getStorageSync("UserName") : "请登录")
const phonenumber = ref(Taro.getStorageSync("Phone") ? Taro.getStorageSync("Phone") : "-")
const contactShow = ref(false)
const setPhoneShow = ref(false)
const setNameShow = ref(false)
const code = ref()
const state: stateType = reactive({
msg: "",
type: "text",
center: true,
show: false,
})
const btnContent = reactive({
content: "获取验证码",
totalTime: 60,
canClick: true,
})
const newPhone = ref()
const newName = ref()
const custNavBar = ref(null)
const timer = ref<any>(null)
/** -----------------method start----------------- */
Taro.useDidShow(() => {
startPolling()
})
Taro.useDidHide(() => {
stopPolling()
})
Taro.useUnload(() => {
stopPolling()
})
// 查询告警消息
function getWarnMsg() {
const warnParams = ref({
pageSize: 10,
curPage: 1,
})
msgWarn(warnParams.value).then((res: any) => {
if (res.statusCode == 200) {
const unReadCount = res.data.unReadCount
if (unReadCount) {
Taro.setTabBarBadge({
index: 1, // tabBar的位置从0开始计数
text: unReadCount > 9 ? "9+" : String(unReadCount),
})
} else {
Taro.removeTabBarBadge({
index: 1,
})
}
}
})
}
function startPolling() {
stopPolling() // 防止多个轮询同时运行
getWarnMsg()
timer.value = setInterval(() => {
getWarnMsg()
}, 120000) // 每3秒执行一次
}
function stopPolling() {
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
}
/////////////////////
function gotoDev() {
if (store.getUnLogin == 1) {
state.msg = "请前往登录"
state.show = true
return
}
Taro.navigateTo({
url: "/my/deviceList",
})
}
function gotoDevRecharge() {
if (store.getUnLogin == 1) {
state.msg = "请前往登录"
state.show = true
return
}
Taro.navigateTo({
url: "/my/deviceRecharge",
})
}
function gotoHelp() {
if (store.getUnLogin == 1) {
state.msg = "请前往登录"
state.show = true
return
}
Taro.navigateTo({
url: "/my/help",
})
}
function gotoPhone() {
if (store.getUnLogin == 1) {
state.msg = "请前往登录"
state.show = true
return
}
Taro.navigateTo({
url: "/my/alarmPhone",
})
}
function gotoSub() {
if (store.getUnLogin == 1) {
state.msg = "请前往登录"
state.show = true
return
}
Taro.navigateTo({
url: "/my/subAccount",
})
}
function goContact() {
if (store.getUnLogin == 1) {
state.msg = "请前往登录"
state.show = true
return
}
contactShow.value = true
}
// 修改手机号码
function setPhoneNumber() {
setPhoneShow.value = true
}
/** 验证码读秒 */
function setTime() {
if (!btnContent.canClick) return // 如果是false 直接return出去
btnContent.canClick = false
btnContent.content = btnContent.totalTime + "s后重新发送"
let clock = window.setInterval(() => {
btnContent.totalTime--
btnContent.content = btnContent.totalTime + "s后重新发送"
if (btnContent.totalTime < 0) {
window.clearInterval(clock)
btnContent.content = "重新发送"
btnContent.totalTime = 60
btnContent.canClick = true // 这里重新开启
}
}, 1000)
}
/** 获取验证码 */
function getCode() {
const v_phone_res = validatePhone(phonenumber.value)
if (v_phone_res == 1) {
state.msg = "手机号不能为空"
state.show = true
} else if (v_phone_res == 2) {
state.msg = "手机号格式不正确"
state.show = true
} else {
smsCode(phonenumber.value).then((res: any) => {
state.msg = "发送成功"
state.show = true
setTime()
})
}
}
const toSetPhoneD = debounce(toSetPhone, 300)
// 修改手机号
function toSetPhone() {
const v_phone_res = validatePhone(phonenumber.value)
const v_phone_res2 = validatePhone(newPhone.value)
if (v_phone_res == 1) {
state.msg = "原手机号不能为空"
state.show = true
return
} else if (v_phone_res == 2) {
state.msg = "原手机号格式不正确"
state.show = true
return
}
if (!code.value) {
state.msg = "请输入验证码"
state.show = true
return
}
if (v_phone_res2 == 1) {
state.msg = "新手机号不能为空"
state.show = true
return
} else if (v_phone_res2 == 2) {
state.msg = "新手机号格式不正确"
state.show = true
return
}
// 验证验证码
const data = {
oldMobilePhone: phonenumber.value,
newMobilePhone: newPhone.value,
smsCode: code.value,
}
updatePhone(data).then(res => {
if (res.statusCode == 200) {
state.msg = "修改成功"
state.show = true
Taro.setStorageSync("Phone", newPhone.value)
phonenumber.value = newPhone.value
setPhoneShow.value = false
}
})
// verifyCode({ MobilePhone: phonenumber.value, SmsCode: code.value }).then(res => {
// if (res.statusCode == 200) {
// updatePhone({ MobilePhone: phonenumber.value }).then(res => {
// })
// } else {
// state.msg = "验证码错误"
// state.show = true
// }
// })
}
// 取消修改手机号
function cancelSetPhone() {
setPhoneShow.value = false
newPhone.value = null
code.value = null
}
// 修改昵称
function setNickname() {
if (store.getUnLogin == 1) {
// state.msg = "请前往登录";
// state.show = true;
return
}
setNameShow.value = true
newName.value = username.value
}
function cancelSetNickname() {
setNameShow.value = false
newName.value = username.value
}
const toSetNameD = debounce(toSetName, 300)
function toSetName() {
if (!newName.value) {
state.msg = "请输入昵称"
state.show = true
return
}
const data = {
id: 0,
newName: newName.value,
}
updateNickName(data).then(res => {
if (res.statusCode == 200) {
state.msg = "修改成功"
state.show = true
nextTick(() => {
const n_id = store.getRootUserId
const u_id = Taro.getStorageSync("UserId")
username.value = newName.value
Taro.setStorageSync("UserName", newName.value)
if (n_id == u_id) {
store.updateRootUserName(newName.value)
}
custNavBar.value.setNickname()
})
username.value = newName.value
setNameShow.value = false
}
})
}
/** 退出登录 */
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",
})
}
},
})
}
// 收藏
// function collect(number) {
// console.log('测试')
// Taro.addPhoneContact({
// firstName: "鱼测云报警电话",
// mobilePhoneNumber: number,
// success: function (res) {
// state.msg = "收藏成功";
// state.show = true;
// },
// });
// }
// 在data部分添加
const collectShow = ref(false) // 控制收藏弹窗显示
const selectedNumbers = ref<string>("") // 已选号码
// 修改collect方法
function collect() {
collectShow.value = true
// 初始化已选号码为空
selectedNumbers.value = ""
}
// 确认收藏操作
function collectOk() {
if (!selectedNumbers.value) {
state.msg = "请选择要收藏的号码"
state.show = true
return
}
// 如果 selectedNumbers.value 为all则全部收藏
Taro.addPhoneContact({
firstName: "鱼测云报警电话",
mobilePhoneNumber: selectedNumbers.value,
success: function () {
state.msg = "收藏成功"
state.show = true
collectShow.value = false // 关闭弹窗
selectedNumbers.value = "" // 清空已选号码
},
fail: function () {
state.msg = "收藏失败,请检查权限"
state.show = true
},
})
}
function saveNnmber(number) {
console.log("测试")
Taro.addPhoneContact({
firstName: "鱼测云报警电话",
mobilePhoneNumber: number,
success: function (res) {
state.msg = "收藏成功"
state.show = true
},
})
}
// 取消收藏操作
function collectCancel() {
collectShow.value = false // 关闭弹窗
}
/** 前往登录 */
function toLogin() {
Taro.clearStorageSync()
store.updateUnLogin(2)
Taro.redirectTo({
url: "/pages/login/index",
})
}
</script>
<style lang="scss">
.my_home {
width: 100%;
height: 100vh;
.container {
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;
width: 100%;
height: 100vh;
font-size: 32px;
font-family: "PingFang SC";
font-weight: 400;
}
.pop-class {
.nut-dialog {
padding: 20rpx !important;
}
.nut-dialog__content {
margin-top: 15% !important;
}
}
.body {
padding: 100px 30px 20px 30px;
.ava {
border: 3px solid #ffffff;
border-radius: 40px;
width: 100px;
height: 100px;
}
.nickname {
font-size: 48px;
font-weight: bold;
color: #222;
}
.phone {
font-size: 28px;
color: #222;
}
}
.menu {
.menu_body {
padding: 20px;
.title {
font-size: 36px;
font-weight: bold;
color: #222;
margin-bottom: 20px;
}
.nut-cell__title {
font-size: 36px;
color: #444;
}
}
}
.nut-dialog {
padding: 0;
}
.nut-dialog__content {
margin: 0;
max-height: 100%;
}
.code {
color: #09b8c2;
}
.disabled {
color: #ccc;
}
}
</style>

26
src/pages/main/types.ts Normal file
View File

@@ -0,0 +1,26 @@
export interface FormType {
pondName: string;
fishKindIds: any;
area: number;
density: number;
placeTime: string;
}
export interface PondListType {
id: number,
pondName: string,
isNormal: boolean,
valueDissolvedOxygen: string,
valueTemperature: string,
valueSaturability: string,
valuePH: string,
valueSalinity: string,
switchOpenCount: number,
num: number,
deviceCount: number,
warnCodeInfo: any,
up: any,
isPh: any,
isSa: any,
ErrorMessage: any,
isAllDead: any,
}

View File

@@ -0,0 +1,8 @@
export default definePageConfig({
navigationStyle: 'custom',
disableScroll: true,
// enablePullDownRefresh: true,
// enablePullDownRefresh: true,
// onReachBottomDistance: 50,
// backgroundTextStyle: "dark",
})

553
src/pages/msg/index.vue Normal file
View File

@@ -0,0 +1,553 @@
<template>
<view>
<CustomNavigationBar @refresh="resUsetInfo"/>
<view class="msg_home">
<nut-row>
<nut-col
:span="8"
:style="{ textAlign: 'center' }"
@click="changeVal(0)"
>
<nut-badge
:value="unReadCount"
:max="9"
color="#e22323"
:style="{ width: '100%' }"
top="10"
right="20"
>
<view class="btn" :class="{ active: selectVal === 0 }"
>报警消息</view
>
</nut-badge>
</nut-col>
<nut-col
:span="8"
class="btn"
:class="{ active: selectVal === 1 }"
@click="changeVal(1)"
>操作记录</nut-col
>
<nut-col
:span="8"
class="btn"
:class="{ active: selectVal === 2 }"
@click="changeVal(2)"
>充值记录</nut-col
>
<!-- 消息条数已读未读全部已读 -->
<nut-col :span="24" v-if="msgCount > 0" class="msg_count">
<nut-row>
<nut-col
:span="12"
class="f_weight"
:style="{ paddingLeft: '15rpx' }"
>消息条数{{ msgCount }}</nut-col
>
<nut-col
:span="12"
:style="{ textAlign: 'right', paddingRight: '15rpx' }"
v-if="unReadCount>0&&selectVal==0"
>
<nut-button type="primary" size="small" @click="readAll">
<template #icon>
<Del2 />
</template>
全部已读
</nut-button>
</nut-col>
</nut-row>
</nut-col>
</nut-row>
<scroll-view
:scroll-y="true"
style="height: 80vh"
@scrolltolower="lower"
:refresherEnabled="true"
@RefresherRefresh="refData"
refresherThreshold="50"
:refresherTriggered="refStatus"
@RefresherPulling="onPulling"
>
<view class="body">
<view v-if="selectVal === 0">
<view v-if="warnList.length > 0">
<nut-cell v-for="item in warnList" :key="item.id">
<nut-row class="view_f">
<nut-col :span="4">
<img
:src="
item.warnType == 1 ? msg_money : item.warnType == 2 ? msg_o2 : item.warnType == 3 ? msg_alert : msg_dis
"
width="80rpx"
height="80rpx"
/>
</nut-col>
<nut-col :span="20">
<view
:style="{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}"
>
<view class="title">{{ item.title }}</view>
<view class="time">{{
item.createdTime ? formatDateMin(item.createdTime) : ""
}}</view>
</view>
<view class="view_f_between_2">
<view class="content f_weight">{{
item.deviceName
}}</view>
<view>
<nut-button
type="primary"
size="small"
plain
v-if="!item.isRead"
@click="read(item.id)"
>未读</nut-button
>
</view>
</view>
<view class="content">{{ item.message }}</view>
</nut-col>
</nut-row>
</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>
</view>
<view v-if="selectVal === 1">
<view v-if="switchList.length > 0">
<nut-cell v-for="item in switchList" :key="item.id">
<nut-row class="view_f">
<nut-col :span="4">
<img :src="msg_alert" width="80rpx" height="80rpx" />
</nut-col>
<nut-col :span="20">
<view
:style="{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}"
>
<view class="title">{{ item.title }}</view>
<view class="time">{{
item.createdTime ? formatDateMin(item.createdTime) : ""
}}</view>
</view>
<view class="content" v-show="item.opName">{{ '控制模式'+item.opName }}</view>
<view class="content">{{ item.message }}</view>
</nut-col>
</nut-row>
</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>
</view>
<view v-if="selectVal === 2">
<view v-if="payList.length > 0">
<nut-cell v-for="item in payList" :key="item.id">
<nut-row class="view_f">
<nut-col :span="4">
<img :src="msg_money" width="80rpx" height="80rpx" />
</nut-col>
<nut-col :span="14">
<view :style="{ display: 'flex', alignItems: 'center' }">
<view class="title">{{
item.deviceType == 2 ? "测控一体机" : "水质检测仪"
}}</view>
</view>
<view :style="{ display: 'flex', alignItems: 'center',marginTop:'10rpx' }">
<nut-tag class="tag" :style="{marginLeft:'0px !important'}"> {{ item.addMonth }}个月 </nut-tag>
<nut-tag class="tag"> {{ item.payType==1?'用户充值':'后台续期' }} </nut-tag>
</view>
<view class="content" v-if="item.serialNum ">
设备{{ item.serialNum }}
</view>
<view class="time mt">
续费日期{{
item.createdTime ? formatDateMin(item.createdTime) : ""
}}
</view>
<view class="time mt">
到期日期{{
item.deadTime ? formatDate_(item.deadTime) : ""
}}
</view>
</nut-col>
<nut-col
:span="6"
:style="{
textAlign: 'right',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
}"
>
<text class="price" style="line-height: 1"
>¥{{ Number(item.payAmount)/100 }}</text
>
</nut-col>
</nut-row>
</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>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup lang="ts">
import CustomNavigationBar from "@/components/custom-navigation-bar/index";
import { msgPay, msgWarn, msgSwitch, msgRead, msgReadAll } from "@/api/msg";
import { formatDateMin,formatDate_ } from "@/utils/tools";
import Taro from "@tarojs/taro";
import { imgUrl } from "@/utils/imgUrl";
import { Del2 } from "@nutui/icons-vue-taro";
const msg_alert = `${imgUrl}msg_alert.png`;
const msg_dis = `${imgUrl}msg_dis.png`;
const msg_money = `${imgUrl}msg_money.png`;
const msg_o2 = `${imgUrl}msg_o2.png`;
const pond404 = `${imgUrl}zanwurenwu.png`;
const selectVal = ref(0);
// 告警列表
const warnList = ref([]);
const warnParams = ref({
pageSize: 10,
curPage: 1
});
const warnTotal = ref(0);
const warnTotalPages = ref(1);
// 开关列表
const switchList = ref([]);
const switchParams = ref({
pageSize: 10,
curPage: 1
});
const switchTotal = ref(0);
const switchTotalPages = ref(1);
// 充值列表
const payList = ref([]);
const payParams = ref({
pageSize: 10,
curPage: 1
});
const payTotal = ref(0);
const payTotalPages = ref(1);
const refStatus = ref(false);
const unReadCount = ref<number>(0);
const msgCount = ref<number>(0);
/** ----------------method start------------------------ */
Taro.useDidShow(() => {
selectVal.value = 0;
// 查询设备列表
getWarnMsg();
});
// 头部组件刷新
function resUsetInfo(e){
changeVal(selectVal.value);
}
// 切换查询消息类型
function changeVal(e) {
setTimeout(() => {
refStatus.value = false;
}, 200);
selectVal.value = e;
if (e == 0) {
warnParams.value.curPage = 1;
getWarnMsg();
} else if (e == 1) {
switchParams.value.curPage = 1;
getSwitchMsg();
} else if (e == 2) {
payParams.value.curPage = 1;
getPayMsg();
}
}
// 查询告警消息
function getWarnMsg() {
msgWarn(warnParams.value).then((res: any) => {
if (res.statusCode == 200) {
warnList.value = res.data.items;
warnTotal.value = res.data.totalCount;
warnTotalPages.value = res.data.totalPages;
unReadCount.value = res.data.unReadCount;
msgCount.value = res.data.totalCount;
if (unReadCount.value) {
Taro.setTabBarBadge({
index: 1, // tabBar的位置从0开始计数
text: unReadCount.value > 9 ? "9+" : String(unReadCount.value),
});
} else {
Taro.removeTabBarBadge({
index: 1,
});
}
}
});
}
// 查询开关消息
function getSwitchMsg() {
msgSwitch(switchParams.value).then((res: any) => {
if (res.statusCode == 200) {
switchList.value = res.data.items;
switchTotal.value = res.data.totalCount;
switchTotalPages.value = res.data.totalPages;
msgCount.value = res.data.totalCount;
}
});
}
// 查询充值消息
function getPayMsg() {
msgPay(payParams.value).then((res: any) => {
if (res.statusCode == 200) {
payList.value = res.data.items;
payTotal.value = res.data.totalCount;
payTotalPages.value = res.data.totalPages;
msgCount.value = res.data.totalCount;
}
});
}
function lower() {
if (selectVal.value == 0) {
if (warnParams.value.curPage < warnTotalPages.value) {
warnParams.value.curPage = warnParams.value.curPage + 1;
msgWarn(warnParams.value).then((res: any) => {
if (res.statusCode == 200) {
res.data.items.forEach((r) => {
warnList.value.push(r);
});
}
});
}
} else if (selectVal.value == 1) {
if (switchParams.value.curPage < switchTotalPages.value) {
switchParams.value.curPage = switchParams.value.curPage + 1;
msgSwitch(switchParams.value).then((res: any) => {
if (res.statusCode == 200) {
res.data.items.forEach((r) => {
switchList.value.push(r);
});
}
});
}
} else if (selectVal.value == 2) {
if (payParams.value.curPage < payTotalPages.value) {
payParams.value.curPage = payParams.value.curPage + 1;
msgPay(payParams.value).then((res: any) => {
if (res.statusCode == 200) {
res.data.items.forEach((r) => {
payList.value.push(r);
});
}
});
}
}
}
/** 上拉触底分页 */
Taro.useReachBottom(() => {
if (selectVal.value == 0) {
if (warnParams.value.curPage < warnTotalPages.value) {
warnParams.value.curPage = warnParams.value.curPage + 1;
msgWarn(warnParams.value).then((res: any) => {
if (res.statusCode == 200) {
res.data.items.forEach((r) => {
warnList.value.push(r);
});
}
});
}
} else if (selectVal.value == 1) {
if (switchParams.value.curPage < switchTotalPages.value) {
switchParams.value.curPage = switchParams.value.curPage + 1;
msgSwitch(switchParams.value).then((res: any) => {
if (res.statusCode == 200) {
res.data.items.forEach((r) => {
switchList.value.push(r);
});
}
});
}
} else if (selectVal.value == 2) {
if (payParams.value.curPage < payTotalPages.value) {
payParams.value.curPage = payParams.value.curPage + 1;
msgPay(payParams.value).then((res: any) => {
if (res.statusCode == 200) {
res.data.items.forEach((r) => {
payList.value.push(r);
});
}
});
}
}
});
// 已读
function read(id) {
msgRead({ id }).then((res: any) => {
if (res.statusCode == 200) {
getWarnMsg();
}
});
}
// 全部已读
function readAll() {
const data = {
id:0
}
msgReadAll().then((res: any) => {
if (res.statusCode == 200) {
getWarnMsg();
}
});
}
/** 下拉刷新 */
Taro.usePullDownRefresh(() => {
getWarnMsg();
});
// 下拉刷新
function refData() {
changeVal(selectVal.value);
}
// 触发下拉
function onPulling(e) {
if (e.detail.deltaY < 0) {
return;
}
refStatus.value = true;
}
// 查询告警
function getWarnCode(warnCode) {
const a = [
{ code: 1, msg: 1 },
{ code: 2, msg: 1 },
{ code: 4, msg: 2 },
{ code: 8, msg: 2 },
{ code: 16, msg: 2 },
{ code: 32, msg: 2 },
{ code: 64, msg: 2 },
{ code: 128, msg: 2 },
{ code: 256, msg: 2 },
];
const selectedCodes = a.filter(
(item) => (warnCode & item.code) === item.code
);
if (selectedCodes) {
const res = selectedCodes.filter((item) => item.msg == 1);
const r = res.length > 0 ? 1 : 0;
return r;
}
}
/** ----------------method end-------------------------- */
</script>
<style lang="scss">
.msg_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;
.btn {
font-size: 32px;
text-align: center;
color: #222;
line-height: 96px;
border: 1px solid #eee;
background: #fff;
}
.btn.active {
background: #09b8c2;
color: #fff;
}
.msg_count {
margin-top: 15px;
}
.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: 36px;
color: #444444;
font-weight: bold;
text-align: right;
}
}
}
</style>