12.9
This commit is contained in:
commit
d9766f43e8
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
# Build Tools
|
||||
unpackage/*
|
||||
!unpackage/res
|
||||
node_modules/*
|
||||
|
||||
|
||||
# Development Tools
|
||||
.idea
|
||||
.vscode
|
||||
.hbuilderx/*
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.DS_Store
|
128
App.vue
Normal file
128
App.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<script>
|
||||
import store from '@/store';
|
||||
export default {
|
||||
//监听路由变化,实现路由拦截,实现全局路由守卫
|
||||
// watch: {
|
||||
// $route(to, from) {
|
||||
// let token = uni.getStorageSync('token')
|
||||
// // vuex存储token
|
||||
// uni.$u.vuex('vuex_token', token);
|
||||
// if (to.path === '/pages/tabBar/scene/index' && (token == '' || token == null)) {
|
||||
// uni.showModal({
|
||||
// title: "提示",
|
||||
// content: '该功能需要登陆后使用,是否前往登录?',
|
||||
// success: function(res) {
|
||||
// if (res.confirm) {
|
||||
// console.log('用户点击确定');
|
||||
// self.$router.push('/pages/login/index');
|
||||
// } else if (res.cancel) {
|
||||
// console.log('用户点击取消');
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// // 如果需要登录验证且未登录,则跳转到登录页
|
||||
// }
|
||||
// if (to.path === '/pagesA/scene/alert/index' && (token == '' || token == null)) {
|
||||
// // 如果需要登录验证且未登录,则跳转到登录页
|
||||
// this.$router.push('/pages/login/index');
|
||||
// }
|
||||
// if (to.path === '/pagesA/scene/alert/index' && (token == '' || token == null)) {
|
||||
// // 如果需要登录验证且未登录,则跳转到登录页
|
||||
// this.$router.push('/pages/login/index');
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
onLaunch: function() {
|
||||
console.log('App Launch');
|
||||
//判断用户是否首次打开
|
||||
var isFirstOpen = uni.getStorageSync('isFirstOpen');
|
||||
// 如果isFirstOpen不存在,说明是首次打开系统
|
||||
if (!isFirstOpen) {
|
||||
// 执行首次打开系统的逻辑,比如显示弹窗
|
||||
uni.reLaunch({
|
||||
url: '/pagesB/login/firstOpen'
|
||||
});
|
||||
}
|
||||
//else {
|
||||
// // 如果不是首次打开系统,直接跳转到登录页
|
||||
// uni.reLaunch({
|
||||
// url: '/pages/login/index'
|
||||
// });
|
||||
// // uni.setStorageSync('isFirstOpen', false);
|
||||
// }
|
||||
},
|
||||
onShow: function() {
|
||||
this.$watch('$route', (to, from) => {
|
||||
this.checkLoginStatus(to);
|
||||
});
|
||||
console.log('App Show');
|
||||
// #ifndef H5 || APP-PLUS||MP-WEIXIN
|
||||
//判断当前微信版本是否支持版本更新
|
||||
if (uni.canIUse('getUpdateManager')) {
|
||||
const updateManager = uni.getUpdateManager();
|
||||
// 请求完新版本信息的回调
|
||||
updateManager.onCheckForUpdate(function(res) {
|
||||
if (res.hasUpdate) {
|
||||
updateManager.onUpdateReady(function() {
|
||||
uni.showModal({
|
||||
title: '更新提示',
|
||||
content: '新版本已经准备好,是否重启应用?',
|
||||
success: function(res) {
|
||||
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
|
||||
if (res.confirm) {
|
||||
updateManager.applyUpdate()
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
// 新的版本下载失败
|
||||
updateManager.onUpdateFailed(function() {
|
||||
uni.showModal({
|
||||
title: '已经有新版本了哟~',
|
||||
content: '新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~',
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
checkLoginStatus(route) {
|
||||
let token = uni.getStorageSync('token');
|
||||
if ((route.path === '/pages/tabBar/scene/index' || route.path === '/pages/tabBar/alert/index') && (token ==
|
||||
'' || token == null)) {
|
||||
uni.showModal({
|
||||
title: this.$tt('common.tips'),
|
||||
content: this.$tt('common.loginTips'),
|
||||
cancelText: "取消",
|
||||
confirmText: "确定",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
console.log('用户点击确定');
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/login/waitLogin'
|
||||
});
|
||||
} else if (res.cancel) {
|
||||
console.log('用户点击取消');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
onHide: function() {
|
||||
console.log('App Hide');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/uni_modules/uview-ui/index.scss';
|
||||
</style>
|
124
README.md
Normal file
124
README.md
Normal file
@ -0,0 +1,124 @@
|
||||
### 请勿随意分发和转售
|
||||
|
||||
|微信小程序|安卓|IOS|H5|Vue2|
|
||||
|:---:|:---:|:---:|:---:|:---:|
|
||||
|√|√|√|√|√|
|
||||
|
||||
|
||||
### 一、项目介绍
|
||||
1. 项目使用uniapp开发,适配微信小程序、安卓、IOS和H5,其他平台未测试。
|
||||
2. UI框架使用uView2.0。
|
||||
3. 组件使用easycom模式,只要组件安装在项目的components目录下或uni_modules目录下,并符合components/组件名称/组件名称.vue目录结构。就可以不用引用、注册,直接在页面中使用。
|
||||
4. 开发工具为Hbuilder3.3以上版本。
|
||||
5. 近期在开发项目升级过程中会逐步优化项目结构,由于旧代码比较庞大需要很多时间优化,所以项目结构目录这块暂时没有更新,希望谅解。
|
||||
|
||||
|
||||
### 二、项目结构
|
||||
|
||||
```
|
||||
├─apis // 接口管理
|
||||
│ ├─modules // api模块化目录
|
||||
│ │ └─device.js // 设备接口地址
|
||||
│ ├─http.api.js // 接口定义文件
|
||||
│ └─http.interceptor // 拦截器
|
||||
├─common // 公共文件
|
||||
│ ├─mqttTool // mqtt工具
|
||||
│ ├─extend // 扩展原型方法
|
||||
│ ├─filters // 全局过滤器
|
||||
│ └─tools // 全局公共方法
|
||||
├─components // 项目组件库,组件放置这里,其他页面可直接使用
|
||||
│ ├─cl-test // easycom测试组件
|
||||
│ ├─cl-icon // iconfont图标组件
|
||||
│ ├─deviceMonitor // 设备实时监测组件
|
||||
│ └─other... // 使用的其他组件等等
|
||||
├─pages // 页面目录
|
||||
│ ├─public // 公共页面
|
||||
│ └─tarbar // 底部导航栏页面
|
||||
│ ├─home // 首页的所有页面
|
||||
│ ├─scene // 场景联动页面
|
||||
│ ├─trend // 新闻动态页面
|
||||
│ └─user // 个人中心页面
|
||||
├─static // 图片目录
|
||||
├─store // vuex
|
||||
│ ├─$u.mixin // store全局混入方法
|
||||
│ └─index // vuex 组件全局状态管理
|
||||
├─uni_modules // 插件市场插件目录
|
||||
│ └─uview-ui // uview-ui
|
||||
├─env.config.js // 接口地址和mqtt地址配置文件
|
||||
├─mainfest.json // 各个平台的配置信息
|
||||
```
|
||||
|
||||
### 三、基本配置
|
||||
1. 打开根目录的mainfest.json文件, 基础配置中 AppId改为自己的 [uni-app应用标识](https://ask.dcloud.net.cn/article/35907),微信小程序配置中,AppId改为自己的 [微信小程序AppId](https://mp.weixin.qq.com/) 。
|
||||
|
||||
2. 打开根目录的 `env.config.js` 文件,修改服务端接口地址和emqx消息服务器地址
|
||||
```
|
||||
// H5端开发和生产环境协议
|
||||
let protocalDev = "ws://";
|
||||
let protocalProd = "wss://";
|
||||
|
||||
// 条件编译,微信端和App端使用wxs协议
|
||||
// #ifdef MP-WEIXIN || APP-PLUS
|
||||
protocalDev = 'wxs://';
|
||||
protocalProd = 'wxs://';
|
||||
// #endif
|
||||
|
||||
const CONFIG = {
|
||||
// 开发环境配置
|
||||
development: {
|
||||
baseUrl: 'http://localhost:8080',
|
||||
mqttServer: protocalDev + 'localhost:8083/mqtt',
|
||||
},
|
||||
// 生产环境配置
|
||||
production: {
|
||||
baseUrl: 'https://domain.com/prod-api/',
|
||||
mqttServer: protocalProd + 'domain.com/mqtt',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* 小程序调用接口必须使用https协议,可以去阿里、腾讯、百度等平台申请免费证书,Nginx配置https和wss参考 [官网文档](https://fastbee.cn/doc/pages/applet/)
|
||||
* H5端使用ws(未加密)或者wss(加密)协议
|
||||
* 微信小程序和App端使用wxs协议,同时需要配置域名,App端使用wss连接失败,可能跟mqtt.js版本有关系。
|
||||
|
||||
### 四、Mqtt协议
|
||||
|协议 |一般使用端口 |说明 |
|
||||
|:-----|:----------|:------------------|
|
||||
| mqtt | 1883端口 | 未加密 TCP 连接,硬件端和服务端使用|
|
||||
| mqtts| 8883端口 | 加密 TCP 连接,硬件端和服务端使用|
|
||||
| ws | 8083端口 | 未加密 WebSocket 连接,前端和移动端使用|
|
||||
| wss | 8084端口 | 加密 WebSocket 连接,前端和移动端使用,通过代理访问8083端口|
|
||||
| wxs | 8084端口 | 微信小程序连接,微信小程序端使用,通过代理访问8083端口|
|
||||
| alis | 8084端口 | 支付宝小程序连接,支付宝小程序端使用,通过代理访问8083端口|
|
||||
|
||||
### 五、使用说明
|
||||
1. 项目使用uChart图表,部分图标启用canvas2d模式,解决小程序层级过高及拖拽卡顿问题。微信开发工具中图表显示会有问题,发布后正常显示。
|
||||
2. 微信小程序支持多设备配网,mainfest.json微信小程序配置中要启用位置接口才能使用。微信小程序端配网需要使用真机调试。
|
||||
3. 微信小程序视频需要向微信官方申请权限,Android 和 IOS 需要用Wap2App 方式打包,播放器暂时不支持原生App。
|
||||
4. 项目天气预报使用的是心知天气API需要自行前往官网申请(小程序配置心知天气域名),也可以使用其他天气API,但需要调整一下weather的数据(已自定义组件),由于某些浏览器原因,为保证H5正常运行,可以申请腾讯地图key来定位。
|
||||
|
||||
### 六、设备配网 / 扫码添加设备
|
||||
1. 有两种情况:第一种是系统不存在该设备,配网或扫码后会新建设备到用户账号下;第二种是系统已存在该设备,配网或扫码后是关联设备到用户账号下。
|
||||
2. 设备配网:通过配网可以把wifi信息配置到设备,以及新建设备到用户账号下。目前H5、微信小程序、安卓和IOS都支持单设备配网,多设备配网只有微信小程序支持。单设备配网时用户手动切换手机wifi为设备热点,然后进行配网。
|
||||
3. 扫码添加设备:用户通过扫码新建设备到自己账号下。系统中的每个设备都有二维码,在设备详情摘要中查看。二维码固定为下面JSON格式:
|
||||
```
|
||||
# type固定值为1,代表扫码添加设备
|
||||
# type、deviceNumber、productId 为必填项,productName为可选项
|
||||
# 如果系统中还不存在该设备,设备编号使用一个唯一性编码即可,不能包含特殊字符
|
||||
{
|
||||
"type": 1,
|
||||
"deviceNumber": "D888666",
|
||||
"productId": 5,
|
||||
"productName": "智能插座"
|
||||
}
|
||||
```
|
||||
|
||||
### 七、相关文档
|
||||
[uView2.0文档 >>](https://www.uviewui.com/components/intro.html) <br />
|
||||
[uniapp文档 >>](https://uniapp.dcloud.io/tutorial/) <br />
|
||||
[easycom说明 >>](https://uniapp.dcloud.io/component/#easycom%E7%BB%84%E4%BB%B6%E8%A7%84%E8%8C%83) <br />
|
||||
[uChart2.0文档 >>](https://www.ucharts.cn/v2/#/guide/index) <br />
|
||||
[Wap2App打包](https://code.wumei.live/ultimate/wumei-smart/-/wikis/Wap2App打包)
|
||||
|
||||
### 八、项目运行
|
||||
运行前先跑 npm install 下载所需要的依赖包!
|
30
apis/http.api.js
Normal file
30
apis/http.api.js
Normal file
@ -0,0 +1,30 @@
|
||||
import * as common from './modules/common.js';
|
||||
import * as account from './modules/account.js';
|
||||
import * as scene from './modules/scene.js';
|
||||
import * as group from './modules/group.js';
|
||||
import * as deviceUser from './modules/deviceUser.js';
|
||||
import * as deviceLog from './modules/deviceLog.js';
|
||||
import * as device from './modules/device.js';
|
||||
// const http = uni.$u.http;
|
||||
|
||||
// api 接口管理
|
||||
const install = (Vue, vm) => {
|
||||
|
||||
Vue.prototype.$api = {
|
||||
// 登录
|
||||
// login:(params = {})=>http.post('/login',params),
|
||||
|
||||
// import modules
|
||||
common,
|
||||
scene,
|
||||
group,
|
||||
deviceUser,
|
||||
deviceLog,
|
||||
device,
|
||||
account
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
install
|
||||
}
|
129
apis/http.interceptor.js
Normal file
129
apis/http.interceptor.js
Normal file
@ -0,0 +1,129 @@
|
||||
// common/http.interceptor.js
|
||||
|
||||
import projectConfig from '@/env.config.js';
|
||||
|
||||
const codeMessage = {
|
||||
404: '您所请求的资源无法找到',
|
||||
500: '服务器内部错误,无法完成请求',
|
||||
};
|
||||
|
||||
const install = (Vue, vm) => {
|
||||
// 这个配置是一次配置,全局通用的,具体参数见 https://www.uviewui.com/js/http.html
|
||||
uni.$u.http.setConfig((config) => {
|
||||
// 域名设置
|
||||
config.baseURL = projectConfig.baseUrl;
|
||||
// 全局header
|
||||
config.header = {};
|
||||
//
|
||||
config.method = '';
|
||||
// 设置为json,返回后会对数据进行一次JSON.parse()
|
||||
config.dataType = 'json';
|
||||
//
|
||||
config.responseType = 'text';
|
||||
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
|
||||
config.custom = {
|
||||
// 请求接口展示Loading
|
||||
ShowLoading: true,
|
||||
// Loading中是否遮罩
|
||||
LoadingMask: true,
|
||||
// Loading文本
|
||||
LoadingText: '正在加载',
|
||||
}; // 全局自定义参数默认值
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
config.timeout = 60000;
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
// 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+)
|
||||
config.sslVerify = true;
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
// 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+)
|
||||
config.withCredentials = false;
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
// DNS解析时优先使用ipv4 仅 App-Android 支持 (HBuilderX 2.8.0+)
|
||||
config.firstIpv4 = false;
|
||||
// #endif
|
||||
// 局部优先级高于全局,返回当前请求的task,options。请勿在此处修改options。非必填
|
||||
// getTask: (task, options) => {
|
||||
// 相当于设置了请求超时时间500ms
|
||||
// setTimeout(() => {
|
||||
// task.abort()
|
||||
// }, 500)
|
||||
// },
|
||||
// 全局自定义验证器。参数为statusCode 且必存在,不用判断空情况。
|
||||
config.validateStatus = (statusCode) => { // statusCode 必存在。此处示例为全局默认配置
|
||||
return statusCode >= 200 && statusCode < 300
|
||||
};
|
||||
return config;
|
||||
});
|
||||
|
||||
// 请求拦截部分,如配置,每次请求前都会执行
|
||||
uni.$u.http.interceptors.request.use((config) => {
|
||||
config.header.language = uni.getLocale('lang');
|
||||
if (config.custom.ShowLoading) {
|
||||
uni.showLoading({
|
||||
title: config.custom.LoadingText || '正在加载',
|
||||
mask: config.custom.LoadingMask || false
|
||||
});
|
||||
}
|
||||
|
||||
// 引用token
|
||||
// 方式一,存放在vuex的token,假设使用了uView封装的vuex方式
|
||||
// 见:https://uviewui.com/components/globalVariable.html
|
||||
// config.header.token = vm.token;
|
||||
|
||||
// 方式二,如果没有使用uView封装的vuex方法,那么需要使用$store.state获取
|
||||
if (config.url != '/captchaImage' && config.url != '/login' && config.url != '/iot/tool/register') {
|
||||
config.header.Authorization = 'Bearer ' + vm.$store.state.vuex_token;
|
||||
}
|
||||
|
||||
// 方式三,如果token放在了globalData,通过getApp().globalData获取
|
||||
// config.header.token = getApp().globalData.username;
|
||||
|
||||
// 方式四,如果token放在了Storage本地存储中,拦截是每次请求都执行的
|
||||
// 所以哪怕您重新登录修改了Storage,下一次的请求将会是最新值
|
||||
// const token = uni.getStorageSync('token');
|
||||
// config.header.token = token;
|
||||
// config.header.Token = 'xxxxxx';
|
||||
|
||||
// 可以对某个url进行特别处理,此url参数为this.$u.get(url)中的url值
|
||||
// if (config.url == '/pages/login') config.header.noToken = true;
|
||||
// 最后需要将config进行return
|
||||
return config;
|
||||
// 如果return一个false值,则会取消本次请求
|
||||
// if(config.url == '/user/rest') return false; // 取消某次请求
|
||||
})
|
||||
|
||||
// 响应拦截,如配置,每次请求结束都会执行本方法
|
||||
uni.$u.http.interceptors.response.use((res) => {
|
||||
if (res.config.custom.ShowLoading) {
|
||||
uni.hideLoading();
|
||||
}
|
||||
// if 状态码是否正常
|
||||
if (res.statusCode === 200) {
|
||||
let result = res.data;
|
||||
// if 与后台规定的成功码是否正常
|
||||
if (result.code === 200 || result.code === 500 || result.code === 450) {
|
||||
return result
|
||||
} else if (result.code == 401) {
|
||||
// 本地缓存存储token
|
||||
uni.setStorageSync('token', '');
|
||||
uni.reLaunch({
|
||||
url: '/pages/tabBar/home/index'
|
||||
});
|
||||
return result
|
||||
} else {
|
||||
console.log(result);
|
||||
vm.$u.toast(result.msg);
|
||||
}
|
||||
} else {
|
||||
vm.$u.toast(res.data.msg);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
install
|
||||
}
|
25
apis/modules/account.js
Normal file
25
apis/modules/account.js
Normal file
@ -0,0 +1,25 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
import upload from '@/utils/upload'
|
||||
//修改个人信息
|
||||
export function updateProfile(params) {
|
||||
return http.put('/system/user/profile', params);
|
||||
}
|
||||
|
||||
//用户头像上传
|
||||
export function uploadAvatar(data) {
|
||||
return upload({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
name: data.name,
|
||||
filePath: data.filePath
|
||||
})
|
||||
}
|
||||
|
||||
//用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
return http.request({
|
||||
url: '/system/user/profile/updatePwd?oldPassword=' + oldPassword + '&newPassword=' + newPassword,
|
||||
method: 'put',
|
||||
})
|
||||
}
|
32
apis/modules/alertLog.js
Normal file
32
apis/modules/alertLog.js
Normal file
@ -0,0 +1,32 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询设备告警数据
|
||||
export function getAlertList (query) {
|
||||
return http.request({
|
||||
url: '/iot/alertLog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询单个设备告警详情
|
||||
export function getAlertLog (id) {
|
||||
return http.get('/iot/alertLog/' + id);
|
||||
}
|
||||
|
||||
// 修改单个设备告警详情
|
||||
export function editAlertLog (data) {
|
||||
return http.request({
|
||||
url: '/iot/alertLog',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询微信小程序告警通知模板id
|
||||
export function getAlertTemplateId() {
|
||||
return http.request({
|
||||
url: '/notify/template/getAlertWechatMini',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
78
apis/modules/common.js
Normal file
78
apis/modules/common.js
Normal file
@ -0,0 +1,78 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询所有产品列表
|
||||
export function listShortProduct () {
|
||||
return http.get('/iot/product/shortList');
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function captchaImage (showCode) {
|
||||
return http.request({
|
||||
url: '/captchaImage?showCode='+ showCode,
|
||||
method: 'get',
|
||||
})
|
||||
|
||||
//return http.get('/captchaImage',params);
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
export function getProfile () {
|
||||
return http.get('/system/user/profile');
|
||||
}
|
||||
|
||||
// 个人中心-微信绑定-app和小程序
|
||||
export function wechatBind (params) {
|
||||
return http.post('/wechat/bind', params);
|
||||
}
|
||||
|
||||
//解除微信绑定
|
||||
export function secureBind (params) {
|
||||
return http.post('/wechat/cancelBind', params);
|
||||
}
|
||||
|
||||
// 登录
|
||||
export function login (params) {
|
||||
return http.post('/login', params);
|
||||
}
|
||||
|
||||
// 绑定登录
|
||||
export function bindLogin (params) {
|
||||
return http.post('/auth/bind/login', params);
|
||||
}
|
||||
|
||||
// 绑定注册
|
||||
export function bindRegister (params) {
|
||||
return http.post('/auth/bind/register', params);
|
||||
}
|
||||
//获取短信验证码
|
||||
export function getSmsCode (phoneNumber) {
|
||||
return http.request({
|
||||
url: '/notify/smsLoginCaptcha?phoneNumber=' + phoneNumber,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
//短信登录
|
||||
export function smsLogin (params) {
|
||||
return http.post('/auth/sms/login', params);
|
||||
}
|
||||
// 退出登录
|
||||
export function logout () {
|
||||
return http.post('/logout');
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register (params) {
|
||||
return http.post('/iot/tool/register', params);
|
||||
}
|
||||
|
||||
// 查询用户列表
|
||||
export function listUser (params) {
|
||||
return http.get('/iot/tool/userList', {
|
||||
params: params
|
||||
});
|
||||
}
|
||||
|
||||
// 注销账号
|
||||
export function unsubscribe () {
|
||||
return http.post('/unsubscribe');
|
||||
}
|
123
apis/modules/device.js
Normal file
123
apis/modules/device.js
Normal file
@ -0,0 +1,123 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询设备简短列表
|
||||
export function listDeviceShort(params) {
|
||||
return http.get('/iot/device/shortList', {
|
||||
params: params
|
||||
});
|
||||
}
|
||||
|
||||
// 查询设备详细
|
||||
export function getDevice(deviceId) {
|
||||
return http.get('/iot/device/' + deviceId);
|
||||
}
|
||||
|
||||
// 设备数据同步
|
||||
export function deviceSynchronization(serialNumber) {
|
||||
return http.get('/iot/device/synchronization/' + serialNumber);
|
||||
}
|
||||
|
||||
// 查询设备运行状态详细
|
||||
export function getRunningStatus(deviceId, slaveId, type, productId, serialNumber) {
|
||||
let params = {
|
||||
deviceId: deviceId,
|
||||
slaveId: slaveId,
|
||||
type: type,
|
||||
productId: productId,
|
||||
serialNumber: serialNumber,
|
||||
|
||||
};
|
||||
return http.get('/iot/device/runningStatus', {
|
||||
params: params
|
||||
});
|
||||
}
|
||||
|
||||
// 查询设备物模型的值
|
||||
export function getDeviceThingsModelValue(deviceId) {
|
||||
return http.get('/iot/device/thingsModelValue/' + deviceId);
|
||||
}
|
||||
|
||||
// 根据产品ID获取缓存的物模型
|
||||
export function cacheJsonThingsModel(productId) {
|
||||
return http.get('/iot/model/cache/' + productId, {
|
||||
custom: {
|
||||
ShowLoading: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 根据产品ID获取缓存的物模型
|
||||
export function getCacheThingsModel(productId) {
|
||||
return http.get('/iot/model/cache/' + productId);
|
||||
}
|
||||
|
||||
// 修改设备
|
||||
export function updateDevice(data) {
|
||||
return http.put('/iot/device', data)
|
||||
}
|
||||
|
||||
// 用户关联设备
|
||||
export function deviceRelateUser(data) {
|
||||
return http.post('/iot/device/relateUser', data)
|
||||
}
|
||||
|
||||
// 删除设备
|
||||
export function delDevice(deviceId) {
|
||||
return http.delete('/iot/device/' + deviceId);
|
||||
}
|
||||
|
||||
// 查询设备最新固件
|
||||
export function getLatestFirmware(deviceId) {
|
||||
return http.get('/iot/firmware/getLatest/' + deviceId);
|
||||
}
|
||||
|
||||
// 查询分组可添加设备分页列表
|
||||
export function listDeviceByGroup(query) {
|
||||
return http.get('/iot/device/listByGroup', {
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询设备绑定的视频通道列表
|
||||
export function relateChannelList(deviceId) {
|
||||
return http.get('/iot/relation/dev/' + deviceId);
|
||||
}
|
||||
|
||||
// 查询设备变量概况
|
||||
export function listThingsModel(data) {
|
||||
return http.get('/iot/device/listThingsModel', {
|
||||
params: data
|
||||
})
|
||||
}
|
||||
// 查询指令权限
|
||||
export function getOrderControl(params) {
|
||||
return http.request({
|
||||
url: '/order/control/get',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
//主动采集
|
||||
export function propGet(params) {
|
||||
return http.request({
|
||||
url: '/iot/runtime/prop/get',
|
||||
method: 'get',
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
//告警记录列表
|
||||
export function listAlertLog(params) {
|
||||
return http.request({
|
||||
url: '/iot/alertLog/list',
|
||||
method: 'get',
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
//服务调用,等待设备响应
|
||||
export function serviceInvokeReply(data) {
|
||||
return http.request({
|
||||
url: '/iot/runtime/service/invokeReply',
|
||||
method: 'post',
|
||||
data: data,
|
||||
});
|
||||
}
|
74
apis/modules/deviceLog.js
Normal file
74
apis/modules/deviceLog.js
Normal file
@ -0,0 +1,74 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询设备日志列表
|
||||
export function listDeviceLog (query) {
|
||||
return http.request({
|
||||
url: '/iot/deviceLog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询事件日志列表
|
||||
export function getEventLogList (query) {
|
||||
return http.request({
|
||||
url: '/iot/event/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询设备监测数据
|
||||
export function listMonitor (query) {
|
||||
return http.request({
|
||||
url: '/iot/deviceLog/monitor',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询设备历史监测数据
|
||||
export function getDeviceHistory (params) {
|
||||
return http.get('/iot/deviceLog/history', { params: params });
|
||||
}
|
||||
//查询历史监测数据
|
||||
export function getHistoryList (data) {
|
||||
return http.request({
|
||||
url: '/data/center/deviceHistory',
|
||||
method: 'post',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
// 查询设备日志详细
|
||||
export function getDeviceLog (logId) {
|
||||
return http.request({
|
||||
url: '/iot/deviceLog/' + logId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增设备日志
|
||||
export function addDeviceLog (data) {
|
||||
return http.request({
|
||||
url: '/iot/deviceLog',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改设备日志
|
||||
export function updateDeviceLog (data) {
|
||||
return http.request({
|
||||
url: '/iot/deviceLog',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除设备日志
|
||||
export function delDeviceLog (logId) {
|
||||
return http.request({
|
||||
url: '/iot/deviceLog/' + logId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
31
apis/modules/deviceUser.js
Normal file
31
apis/modules/deviceUser.js
Normal file
@ -0,0 +1,31 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询设备用户详细
|
||||
export function getDeviceUser (deviceId) {
|
||||
return http.get('/iot/deviceUser/' + deviceId)
|
||||
}
|
||||
|
||||
// 新增设备用户
|
||||
export function addDeviceUser (data) {
|
||||
return http.post('/iot/share', data);
|
||||
}
|
||||
|
||||
// 修改设备用户
|
||||
export function updateDeviceUser (data) {
|
||||
return http.put('/iot/share', data)
|
||||
}
|
||||
|
||||
// 删除设备用户
|
||||
export function delDeviceUser (device) {
|
||||
return http.delete('/iot/share', device)
|
||||
}
|
||||
|
||||
// 查询可分享用户列表
|
||||
export function getShareUser (data) {
|
||||
return http.get('/iot/share/shareUser', { params: data });
|
||||
}
|
||||
|
||||
// 查询已经分享用户列表
|
||||
export function getUserList (params) {
|
||||
return http.get('/iot/share/list', { params: params });
|
||||
}
|
8
apis/modules/gateway.js
Normal file
8
apis/modules/gateway.js
Normal file
@ -0,0 +1,8 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询设备简短列表
|
||||
export function getSubGatewayList (query) {
|
||||
return http.get('/sub/gateway/list', {
|
||||
params: query
|
||||
});
|
||||
}
|
58
apis/modules/group.js
Normal file
58
apis/modules/group.js
Normal file
@ -0,0 +1,58 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 获取设备分组列表
|
||||
export function getGroupList (query) {
|
||||
return http.request({
|
||||
url: '/iot/group/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询设备分组详细
|
||||
export function getGroup (groupId) {
|
||||
return http.request({
|
||||
url: '/iot/group/' + groupId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增设备分组
|
||||
export function addGroup (data) {
|
||||
return http.request({
|
||||
url: '/iot/group',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改设备分组
|
||||
export function updateGroup (data) {
|
||||
return http.request({
|
||||
url: '/iot/group',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新分组下的设备
|
||||
export function updateDeviceGroups (data) {
|
||||
return http.request({
|
||||
url: '/iot/group/updateDeviceGroups',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除设备分组
|
||||
export function delGroup (groupId) {
|
||||
return http.request({
|
||||
url: '/iot/group/' + groupId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询分组下的关联设备ID数组
|
||||
export function getDeviceIds (groupId) {
|
||||
return http.get('/iot/group/getDeviceIds/' + groupId);
|
||||
}
|
62
apis/modules/job.js
Normal file
62
apis/modules/job.js
Normal file
@ -0,0 +1,62 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询定时任务调度列表
|
||||
export function getJobList (query) {
|
||||
return http.request({
|
||||
url: '/iot/job/list',
|
||||
method: 'get',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 定时任务立即执行一次
|
||||
export function runJob (data) {
|
||||
return http.request({
|
||||
url: '/iot/job/run',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 任务状态修改
|
||||
export function changeJobStatus (data) {
|
||||
return http.request({
|
||||
url: '/iot/job/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 新增定时任务调度
|
||||
export function addJob (data) {
|
||||
return http.request({
|
||||
url: '/iot/job',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询定时任务调度详细
|
||||
export function getJob (jobId) {
|
||||
return http.request({
|
||||
url: '/iot/job/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改定时任务调度
|
||||
export function updateJob (data) {
|
||||
return http.request({
|
||||
url: '/iot/job',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除定时任务调度
|
||||
export function delJob (jobId) {
|
||||
return http.request({
|
||||
url: '/iot/job/' + jobId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
10
apis/modules/log.js
Normal file
10
apis/modules/log.js
Normal file
@ -0,0 +1,10 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询指令日志列表
|
||||
export function getOrderLogList (query) {
|
||||
return http.request({
|
||||
url: '/iot/log/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
6
apis/modules/model.js
Normal file
6
apis/modules/model.js
Normal file
@ -0,0 +1,6 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询物模型对应分享设备用户权限列表
|
||||
export function getModelPermList (productId) {
|
||||
return http.get('/iot/model/permList/' + productId);
|
||||
}
|
11
apis/modules/notice.js
Normal file
11
apis/modules/notice.js
Normal file
@ -0,0 +1,11 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询公告列表
|
||||
export function listNotice(params) {
|
||||
return http.get('/system/notice/list',{params})
|
||||
}
|
||||
|
||||
// 查询公告详情
|
||||
export function getNotice(noticeId) {
|
||||
return http.get('/system/notice/' + noticeId);
|
||||
}
|
50
apis/modules/player.js
Normal file
50
apis/modules/player.js
Normal file
@ -0,0 +1,50 @@
|
||||
//播放器
|
||||
const http = uni.$u.http;
|
||||
export function startPlay(deviceId, channelId) {
|
||||
return http.get('/sip/player/play/' + deviceId + "/" + channelId);
|
||||
}
|
||||
|
||||
export function closeStream(deviceId, channelId, streamId) {
|
||||
return http.get('/sip/player/closeStream/' + deviceId + "/" + channelId + "/" + streamId);
|
||||
}
|
||||
|
||||
export function playback(deviceId, channelId, query) {
|
||||
return http.get('/sip/player/playback/' + deviceId + "/" + channelId,{params: query});
|
||||
}
|
||||
|
||||
export function playbackPause(deviceId, channelId, streamId) {
|
||||
return http.get('/sip/player/playbackPause/' + deviceId + "/" + channelId + "/" + streamId);
|
||||
}
|
||||
|
||||
export function playbackReplay(deviceId, channelId, streamId) {
|
||||
return http.get('/sip/player/playbackReplay/' + deviceId + "/" + channelId + "/" + streamId);
|
||||
}
|
||||
|
||||
export function playbackSeek(deviceId, channelId, streamId, query) {
|
||||
return http.get('/sip/player/playbackSeek/' + deviceId + "/" + channelId + "/" + streamId,{params: query});
|
||||
}
|
||||
|
||||
export function playbackSpeed(deviceId, channelId, streamId, query) {
|
||||
return http.get('/sip/player/playbackSpeed/' + deviceId + "/" + channelId + "/" + streamId,{params: query});
|
||||
}
|
||||
|
||||
// record
|
||||
export function getDevRecord(deviceId,channelId,query) {
|
||||
return http.get('/sip/record/devquery/' + deviceId + "/" + channelId,{params: query});
|
||||
}
|
||||
|
||||
export function getRecord(channelId,sn) {
|
||||
return http.get('/sip/record/query/' + channelId + "/" + sn);
|
||||
}
|
||||
|
||||
export function getPushUrl(deviceId, channelId) {
|
||||
return http.get('/sip/talk/getPushUrl/' + deviceId + "/" + channelId);
|
||||
}
|
||||
|
||||
export function startBroadcast(deviceId, channelId) {
|
||||
return http.get('/sip/talk/broadcast/' + deviceId + "/" + channelId);
|
||||
}
|
||||
|
||||
export function stopBroadcast(deviceId, channelId) {
|
||||
return http.get('/sip/talk/broadcast/stop/' + deviceId + "/" + channelId);
|
||||
}
|
8
apis/modules/product.js
Normal file
8
apis/modules/product.js
Normal file
@ -0,0 +1,8 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询产品列表
|
||||
export function getProductList (query) {
|
||||
return http.get('/iot/product/list', {
|
||||
params: query
|
||||
})
|
||||
}
|
6
apis/modules/runtime.js
Normal file
6
apis/modules/runtime.js
Normal file
@ -0,0 +1,6 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 发送设备实时数据
|
||||
export function serviceInvoke (data) {
|
||||
return http.post('/iot/runtime/service/invoke', data);
|
||||
}
|
10
apis/modules/scada.js
Normal file
10
apis/modules/scada.js
Normal file
@ -0,0 +1,10 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询独立组态列表
|
||||
export function getIndeScadaList (query) {
|
||||
return http.request({
|
||||
url: '/scada/center/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
41
apis/modules/scene.js
Normal file
41
apis/modules/scene.js
Normal file
@ -0,0 +1,41 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询场景联动列表
|
||||
export function listScene (query) {
|
||||
return http.get('/iot/scene/list', { params: query });
|
||||
}
|
||||
|
||||
// 查询场景联动详细
|
||||
export function getScene (sceneId) {
|
||||
return http.get('/iot/scene/' + sceneId);
|
||||
}
|
||||
|
||||
// 新增场景联动
|
||||
export function addScene (data) {
|
||||
return http.post('/iot/scene', data);
|
||||
}
|
||||
|
||||
// 修改场景联动
|
||||
export function updateScene (data) {
|
||||
return http.put('/iot/scene', data);
|
||||
}
|
||||
|
||||
// 删除场景联动
|
||||
export function delScene (sceneId) {
|
||||
return http.delete('/iot/scene/' + sceneId);
|
||||
}
|
||||
|
||||
//获取设备列表
|
||||
export function deviceShortList (params) {
|
||||
return http.get('/iot/device/shortList', { params });
|
||||
}
|
||||
|
||||
//根据设备id获取下拉选项数据
|
||||
export function runScene (query) {
|
||||
return http.post('/iot/runtime/runScene', null, { params: query });
|
||||
}
|
||||
|
||||
// 修改场景状态
|
||||
export function updateStatus (data) {
|
||||
return http.put('/iot/scene/updateStatus', data);
|
||||
}
|
40
apis/modules/sip.js
Normal file
40
apis/modules/sip.js
Normal file
@ -0,0 +1,40 @@
|
||||
// sipchannel
|
||||
// 查询监控设备通道信息列表
|
||||
const http = uni.$u.http;
|
||||
|
||||
export function listChannel(query) {
|
||||
return http.request({
|
||||
url: '/sip/channel/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询监控设备通道信息详细
|
||||
export function getChannel(channelId) {
|
||||
return http.get('/sip/channel/' + channelId);
|
||||
}
|
||||
|
||||
// 新增监控设备通道信息
|
||||
export function addChannel(createNum, data) {
|
||||
return http.post('/sip/channel/'+ createNum,data);
|
||||
}
|
||||
|
||||
// 修改监控设备通道信息
|
||||
export function updateChannel(data) {
|
||||
return http.put('/sip/channel',data)
|
||||
}
|
||||
|
||||
// 删除监控设备通道信息
|
||||
export function delChannel(channelId) {
|
||||
return http.delete('/sip/channel/'+ channelId)
|
||||
}
|
||||
|
||||
// ptz控制
|
||||
export function ptzdirection(deviceId,channelId,data) {
|
||||
return http.post('/sip/ptz/direction/'+ deviceId + "/" + channelId,data);
|
||||
}
|
||||
|
||||
export function ptzscale(deviceId,channelId,data) {
|
||||
return http.post('/sip/ptz/scale/'+ deviceId + "/" + channelId,data);
|
||||
}
|
30
apis/modules/trend.js
Normal file
30
apis/modules/trend.js
Normal file
@ -0,0 +1,30 @@
|
||||
const http = uni.$u.http;
|
||||
|
||||
// 查询动态
|
||||
export function topListTrend () {
|
||||
return http.get('/iot/news/topList')
|
||||
}
|
||||
|
||||
// 查询轮播广告图
|
||||
export function bannerListTrend () {
|
||||
return http.get('/iot/news/bannerList')
|
||||
}
|
||||
|
||||
// 查询分类下动态
|
||||
export function listTrend (params) {
|
||||
return http.get('/iot/news/list', { params })
|
||||
}
|
||||
|
||||
// 查询动态详情
|
||||
export function getTrend (newsId) {
|
||||
return http.get('/iot/news/' + newsId);
|
||||
}
|
||||
|
||||
// 查询动态详情
|
||||
export function getNewsDetail (query) {
|
||||
return http.request({
|
||||
url: '/iot/news/getDetail',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
5
common/bus.js
Normal file
5
common/bus.js
Normal file
@ -0,0 +1,5 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
const bus = new Vue();
|
||||
|
||||
export default bus
|
24
common/extend.js
Normal file
24
common/extend.js
Normal file
@ -0,0 +1,24 @@
|
||||
// 对Date的扩展,将 Date 转化为指定格式的String
|
||||
// 月(M)、日(d)、小时(H)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
|
||||
// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
|
||||
// 例子:
|
||||
// (new Date()).Format("yyyy-MM-dd HH:mm:ss.S") ==> 2006-07-02 08:09:04.423
|
||||
// (new Date()).Format("yyyy-M-d H:m:s.S") ==> 2006-7-2 8:9:4.18
|
||||
Date.prototype.Format = function(fmt)
|
||||
{ //author: meizz
|
||||
var o = {
|
||||
"M+" : this.getMonth()+1, //月份
|
||||
"d+" : this.getDate(), //日
|
||||
"h+" : this.getHours(), //小时
|
||||
"m+" : this.getMinutes(), //分
|
||||
"s+" : this.getSeconds(), //秒
|
||||
"q+" : Math.floor((this.getMonth()+3)/3), //季度
|
||||
"S" : this.getMilliseconds() //毫秒
|
||||
};
|
||||
if(/(y+)/.test(fmt))
|
||||
fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
|
||||
for(var k in o)
|
||||
if(new RegExp("("+ k +")").test(fmt))
|
||||
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
|
||||
return fmt;
|
||||
}
|
7
common/filters.js
Normal file
7
common/filters.js
Normal file
@ -0,0 +1,7 @@
|
||||
import Vue from 'vue';
|
||||
let vm = new Vue();
|
||||
|
||||
// 测试过滤器
|
||||
export function toUpperCase(arg) {
|
||||
return arg && arg.toUpperCase();
|
||||
}
|
149
common/mqttTool.js
Normal file
149
common/mqttTool.js
Normal file
@ -0,0 +1,149 @@
|
||||
import projectConfig from '@/env.config.js';
|
||||
|
||||
var mqtt = require('mqtt/dist/mqtt.min.js')
|
||||
var url = projectConfig.mqttServer;
|
||||
|
||||
let mqttTool = {
|
||||
client: null
|
||||
}
|
||||
|
||||
mqttTool.connect = function (token) {
|
||||
let options = {
|
||||
clientId: 'phone-' + Math.random().toString(16).substr(2),
|
||||
username: 'fastbee',
|
||||
password: token,
|
||||
cleanSession: true,
|
||||
keepalive: 30,
|
||||
connectTimeout: 60000,
|
||||
};
|
||||
mqttTool.client = mqtt.connect(url, options);
|
||||
// 连接成功
|
||||
mqttTool.client.on('connect', function (res) {
|
||||
console.log('mqtt连接成功');
|
||||
});
|
||||
// 重新连接
|
||||
mqttTool.client.on('reconnect', function (res) {
|
||||
console.log('mqtt重连');
|
||||
});
|
||||
// 发生错误
|
||||
mqttTool.client.on('error', function (err) {
|
||||
console.log('mqtt连接错误:', err);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: 'mqtt连接错误',
|
||||
});
|
||||
});
|
||||
// 断开连接
|
||||
mqttTool.client.on('close', function (res) {
|
||||
console.log('mqtt断开连接');
|
||||
});
|
||||
}
|
||||
|
||||
mqttTool.end = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (mqttTool.client == null) {
|
||||
resolve('未连接')
|
||||
console.log("未连接")
|
||||
return;
|
||||
}
|
||||
mqttTool.client.end()
|
||||
mqttTool.client = null
|
||||
resolve('连接终止')
|
||||
})
|
||||
}
|
||||
|
||||
mqttTool.reconnect = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (mqttTool.client == null) {
|
||||
// 调用resolve方法,Promise变为操作成功状态(fulfilled)
|
||||
resolve('未连接')
|
||||
console.log("未连接")
|
||||
return;
|
||||
}
|
||||
console.log('正在重连...', res);
|
||||
mqttTool.client.reconnect()
|
||||
})
|
||||
}
|
||||
|
||||
mqttTool.subscribe = function (topics) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (mqttTool.client == null) {
|
||||
resolve('未连接')
|
||||
console.log("未连接")
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: 'mqtt未连接',
|
||||
});
|
||||
return;
|
||||
}
|
||||
mqttTool.client.subscribe(topics, {
|
||||
qos: 0
|
||||
}, function (err, res) {
|
||||
console.log("订阅主题:", topics);
|
||||
if (!err && res.length > 0) {
|
||||
console.log("订阅成功")
|
||||
resolve('订阅成功')
|
||||
} else {
|
||||
console.log("订阅失败,主题可能已经订阅")
|
||||
resolve('订阅失败')
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
mqttTool.unsubscribe = function (topics) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (mqttTool.client == null) {
|
||||
resolve('未连接')
|
||||
console.log("未连接")
|
||||
return;
|
||||
}
|
||||
mqttTool.client.unsubscribe(topics, function (err) {
|
||||
if (!err) {
|
||||
resolve('取消订阅成功')
|
||||
console.log("取消订阅成功")
|
||||
} else {
|
||||
resolve('取消订阅失败')
|
||||
console.log("取消订阅失败")
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
mqttTool.publish = function (topic, message, name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (mqttTool.client == null) {
|
||||
resolve('未连接')
|
||||
console.log("未连接")
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '已断开Mqtt连接',
|
||||
});
|
||||
return;
|
||||
}
|
||||
mqttTool.client.publish(topic, message, function (err) {
|
||||
if (!err) {
|
||||
resolve(topic + '-' + message + '-发布成功')
|
||||
console.log('发布主题:' + topic + ",内容:" + message);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "[ " + name + " ] 指令发送成功",
|
||||
duration: 1000,
|
||||
});
|
||||
} else {
|
||||
resolve(topic + '-' + message + '-发布失败')
|
||||
console.log("发布失败")
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "[ " + name + " ] 指令发送失败",
|
||||
duration: 1000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default mqttTool
|
1
common/qqmap-wx-jssdk.min.js
vendored
Normal file
1
common/qqmap-wx-jssdk.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
60
common/tools.js
Normal file
60
common/tools.js
Normal file
@ -0,0 +1,60 @@
|
||||
const install = (Vue, vm) => {
|
||||
|
||||
Vue.prototype.$t = {
|
||||
// 测试加法
|
||||
toUpperCase(arg){
|
||||
return arg && arg.toUpperCase();
|
||||
},
|
||||
// 转换字符串,undefined,null等转化为""
|
||||
praseStrEmpty(str) {
|
||||
if (!str || str == "undefined" || str == "null") {
|
||||
return "";
|
||||
}
|
||||
return str;
|
||||
},
|
||||
// 日期格式化
|
||||
parseTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null
|
||||
}
|
||||
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
} else if (typeof time === 'string') {
|
||||
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm),'');
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
}
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||
let value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = '0' + value
|
||||
}
|
||||
return value || 0
|
||||
})
|
||||
return time_str
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
install
|
||||
}
|
304
components/NavBar/NavBar.vue
Normal file
304
components/NavBar/NavBar.vue
Normal file
@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- #ifdef H5-->
|
||||
<view class="navbar h5">
|
||||
<view class="navbar-left" @click="toggleSearchMode">
|
||||
<view v-if="!isSearching" class="logo">
|
||||
<image src="../../static/750.png" mode="widthFix" style="width: 260rpx;"></image>
|
||||
</view>
|
||||
<view v-else class="icon"></view>
|
||||
</view>
|
||||
<view class="navbar-center" v-if="isSearching">
|
||||
<input class="search-input" type="text" v-model="searchQuery" placeholder="请输入设备名称"
|
||||
confirm-type="search" @confirm="sendMessage" />
|
||||
<uni-icons v-if="searchQuery" class="icon-right" type="clear" size="22" color="#007aff"
|
||||
@click="clearinput()"></uni-icons>
|
||||
</view>
|
||||
<view class="navbar-right">
|
||||
<view v-if="!isSearching" class="icon search-icon" @click="toggleSearchMode"></view>
|
||||
<view v-else class="search-btn" @click="sendMessage">搜索</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN-->
|
||||
<view class="navbar weixin">
|
||||
<view class="navbar-left" @click="toggleSearchMode">
|
||||
<view v-if="!isSearching" class="logo">
|
||||
<image src="../../static/750.png" mode="widthFix" style="width: 260rpx;"></image>
|
||||
</view>
|
||||
<view v-else class="icon"></view>
|
||||
</view>
|
||||
<view class="navbar-center" v-if="isSearching">
|
||||
<input class="search-input-weixin" type="text" v-model="searchQuery" placeholder="请输入设备名称"
|
||||
confirm-type="search" @confirm="sendMessage" />
|
||||
<uni-icons v-if="searchQuery" class="icon-right" type="clear" size="22" color="#007aff"
|
||||
@click="clearinput()"></uni-icons>
|
||||
</view>
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="navbar-right">
|
||||
<view v-if="!isSearching" class="icon search-icon" @click="toggleSearchMode"></view>
|
||||
<view v-else class="search-btn" @click="sendMessage">搜索</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN-->
|
||||
<view class="navbar-right-weixin">
|
||||
<view v-if="!isSearching" class="icon search-icon" @click="toggleSearchMode"></view>
|
||||
<view v-else class="search-btn" @click="sendMessage">搜索</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Navbar",
|
||||
data() {
|
||||
return {
|
||||
statusBarHeight: 20, // 默认值,等会会动态获取
|
||||
isSearching: false,
|
||||
searchQuery: ""
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
this.statusBarHeight = systemInfo.statusBarHeight; // 获取状态栏高度
|
||||
},
|
||||
methods: {
|
||||
clearinput() {
|
||||
this.searchQuery = ''
|
||||
this.$emit('messageSent', this.searchQuery); // 发送搜索事件
|
||||
},
|
||||
toggleSearchMode() {
|
||||
this.isSearching = !this.isSearching;
|
||||
},
|
||||
sendMessage() {
|
||||
console.log("搜索内容:", this.searchQuery);
|
||||
this.$emit('messageSent', this.searchQuery); // 发送搜索事件
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20%);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scaleUp {
|
||||
from {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hoverAnimation {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.h5 {
|
||||
padding: 10rpx 15rpx;
|
||||
}
|
||||
|
||||
.weixin {
|
||||
padding-left: 15rpx;
|
||||
padding-right: 15rpx;
|
||||
padding-top: calc(15px + var(--status-bar-height));
|
||||
padding-bottom: 15rpx;
|
||||
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: rgba(0, 122, 255, 0.8);
|
||||
backdrop-filter: blur(25rpx) saturate(180%);
|
||||
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.25);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.4);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 44px;
|
||||
z-index: 999;
|
||||
|
||||
border-radius: 0 0 30rpx 30rpx;
|
||||
animation: fadeIn 0.5s ease;
|
||||
|
||||
.navbar-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.logo {
|
||||
animation: fadeIn 0.8s ease;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
background-image: url('/static/750.png');
|
||||
background-size: cover;
|
||||
filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.2));
|
||||
animation: scaleUp 0.5s ease;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
animation: hoverAnimation 0.3s forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.navbar-center {
|
||||
position: relative;
|
||||
flex: 4;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
animation: fadeIn 0.8s ease;
|
||||
|
||||
.search-input {
|
||||
padding-left: 2rem;
|
||||
margin-left: -40rpx;
|
||||
/* 向左移动 input */
|
||||
width: 97%;
|
||||
padding: 12rpx 20rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50rpx;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(12rpx);
|
||||
height: 38rpx;
|
||||
color: #535353;
|
||||
font-size: 28rpx;
|
||||
box-shadow: inset 0 2rpx 12rpx rgba(0, 122, 255, 0.3);
|
||||
animation: scaleUp 0.5s ease;
|
||||
}
|
||||
|
||||
.icon-right {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0.75rem;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.search-input-weixin {
|
||||
margin-left: -20rpx;
|
||||
/* 向左移动 input */
|
||||
width: 87%;
|
||||
padding: 12rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50rpx;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(12rpx);
|
||||
height: 35rpx;
|
||||
color: #535353;
|
||||
font-size: 28rpx;
|
||||
box-shadow: inset 0 2rpx 12rpx rgba(0, 122, 255, 0.3);
|
||||
animation: scaleUp 0.5s ease;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.navbar-right-weixin {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-right: 180rpx;
|
||||
/* 使用安全区域避免关闭按钮重叠 */
|
||||
|
||||
.search-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-image: url('/static/login/search.png');
|
||||
background-size: cover;
|
||||
filter: drop-shadow(0 3rpx 6rpx rgba(225, 225, 225, 0.3));
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
animation: hoverAnimation 0.3s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
padding: 10rpx 15rpx;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
color: #0069d9;
|
||||
border-radius: 50rpx;
|
||||
font-size: 30rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 6rpx 12rpx rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(8rpx);
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.search-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background-image: url('/static/login/search.png');
|
||||
background-size: cover;
|
||||
filter: drop-shadow(0 3rpx 6rpx rgba(225, 225, 225, 0.3));
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
animation: hoverAnimation 0.3s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
padding: 10rpx 25rpx;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
color: #0069d9;
|
||||
border-radius: 50rpx;
|
||||
font-size: 30rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 6rpx 12rpx rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(8rpx);
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
180
components/circleProgress/circleProgress.vue
Normal file
180
components/circleProgress/circleProgress.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<view
|
||||
class="circle-progress"
|
||||
:style="{
|
||||
width: widthPx + 'px',
|
||||
height: widthPx + 'px',
|
||||
backgroundColor: bgColor
|
||||
}"
|
||||
>
|
||||
<!-- 有的不支持canvas-id属性,必须用id属性 -->
|
||||
<canvas
|
||||
v-if="canvasId"
|
||||
class="canvas-bg"
|
||||
:canvas-id="canvasId"
|
||||
:id="canvasId"
|
||||
:style="{
|
||||
width: widthPx + 'px',
|
||||
height: widthPx + 'px'
|
||||
}"
|
||||
></canvas>
|
||||
<canvas
|
||||
class="canvas"
|
||||
v-if="elId"
|
||||
:canvas-id="elId"
|
||||
:id="elId"
|
||||
:style="{
|
||||
width: widthPx + 'px',
|
||||
height: widthPx + 'px'
|
||||
}"
|
||||
></canvas>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'circle-progress',
|
||||
props: {
|
||||
// 圆环进度百分比值
|
||||
percent: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
// 值在0到100之间
|
||||
validator: val => {
|
||||
return val >= 0 && val <= 100;
|
||||
}
|
||||
},
|
||||
// 圆环底色(灰色的圆环)
|
||||
inactiveColor: {
|
||||
type: String,
|
||||
default: '#ececec'
|
||||
},
|
||||
// 圆环激活部分的颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#009dff'
|
||||
},
|
||||
// 圆环线条的宽度,单位rpx
|
||||
borderWidth: {
|
||||
type: [Number, String],
|
||||
default: 14
|
||||
},
|
||||
// 整个圆形的宽度,单位rpx
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 200
|
||||
},
|
||||
// 整个圆环执行一圈的时间,单位ms
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 1500
|
||||
},
|
||||
// 圆环进度区域的背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
canvasId: this.randomId(), //一个页面多个圆形进度
|
||||
elId: this.randomId(),
|
||||
widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度
|
||||
borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度
|
||||
startAngle: -Math.PI / 2, // canvas画圆的起始角度,默认为3点钟方向,定位到12点钟方向
|
||||
progressContext: null, // 活动圆的canvas上下文
|
||||
newPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
|
||||
oldPercent: 0 // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
percent(nVal, oVal = 0) {
|
||||
if (nVal > 100) nVal = 100;
|
||||
if (nVal < 0) oVal = 0;
|
||||
this.newPercent = nVal;
|
||||
this.oldPercent = oVal;
|
||||
setTimeout(() => {
|
||||
this.drawCircleByProgress(oVal);
|
||||
}, 50);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 赋值,用于加载后第一个画圆使用
|
||||
this.newPercent = this.percent;
|
||||
this.oldPercent = 0;
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.drawProgressBg();
|
||||
this.drawCircleByProgress(this.oldPercent);
|
||||
},
|
||||
methods: {
|
||||
//一个页面多个progress时ID需不同
|
||||
randomId(){
|
||||
return 'progressId'+parseInt(Math.random()*1000000)
|
||||
},
|
||||
drawProgressBg() {
|
||||
let ctx = uni.createCanvasContext(this.canvasId, this);
|
||||
ctx.setLineWidth(this.borderWidthPx); // 设置圆环宽度
|
||||
ctx.setStrokeStyle(this.inactiveColor); // 线条颜色
|
||||
ctx.beginPath(); // 开始描绘路径
|
||||
let radius = this.widthPx / 2;
|
||||
ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 2 * Math.PI, false);
|
||||
ctx.stroke(); // 对路径进行描绘
|
||||
ctx.draw();
|
||||
},
|
||||
drawCircleByProgress(progress) {
|
||||
if (this.oldPercent === 0 && this.newPercent === 0) { return; }
|
||||
let ctx = this.progressContext;
|
||||
if (!ctx) {
|
||||
ctx = uni.createCanvasContext(this.elId, this);
|
||||
this.progressContext = ctx;
|
||||
}
|
||||
// 表示进度的两端为圆形
|
||||
ctx.setLineCap('round');
|
||||
// 设置线条的宽度和颜色
|
||||
ctx.setLineWidth(this.borderWidthPx);
|
||||
ctx.setStrokeStyle(this.activeColor);
|
||||
// 计算过渡时间
|
||||
let time = Math.floor(this.duration / 200);
|
||||
let endAngle = ((2 * Math.PI) / 100) * progress + this.startAngle;
|
||||
ctx.beginPath();
|
||||
// 半径为整个canvas宽度的一半
|
||||
let radius = this.widthPx / 2;
|
||||
ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false);
|
||||
ctx.stroke();
|
||||
ctx.draw();
|
||||
// 增大了百分比
|
||||
if (this.newPercent > this.oldPercent) {
|
||||
progress++;
|
||||
if (progress > this.newPercent) return;
|
||||
} else {
|
||||
// 减少百分比
|
||||
progress--;
|
||||
if (progress < this.newPercent) return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
// 定时器,为了让进度条有动画效果
|
||||
this.drawCircleByProgress(progress);
|
||||
}, time);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.circle-progress {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin:auto;
|
||||
}
|
||||
.canvas-bg {
|
||||
position: absolute;
|
||||
}
|
||||
.canvas {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
38
components/cl-icon/cl-icon.vue
Normal file
38
components/cl-icon/cl-icon.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<view :class="'iconfont ' + type" :style="{ color: color,'line-height':size + 'px', 'font-size': size + 'px' }"
|
||||
@click="_onClick" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UniIcon',
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 20
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_onClick() {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import './iconfont.css';
|
||||
|
||||
.iconfont {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
19
components/cl-icon/iconfont.css
Normal file
19
components/cl-icon/iconfont.css
Normal file
@ -0,0 +1,19 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2505219 */
|
||||
src: url('iconfont.woff2?t=1626686156492') format('woff2'),
|
||||
url('iconfont.woff?t=1626686156492') format('woff'),
|
||||
url('iconfont.ttf?t=1626686156492') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconlaba:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
16
components/cl-icon/iconfont.json
Normal file
16
components/cl-icon/iconfont.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": "2505219",
|
||||
"name": "uniapp 插件市场",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "8478792",
|
||||
"name": "喇叭",
|
||||
"font_class": "laba",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
}
|
||||
]
|
||||
}
|
BIN
components/cl-icon/iconfont.ttf
Normal file
BIN
components/cl-icon/iconfont.ttf
Normal file
Binary file not shown.
BIN
components/cl-icon/iconfont.woff
Normal file
BIN
components/cl-icon/iconfont.woff
Normal file
Binary file not shown.
BIN
components/cl-icon/iconfont.woff2
Normal file
BIN
components/cl-icon/iconfont.woff2
Normal file
Binary file not shown.
38
components/cl-icon/index.vue
Normal file
38
components/cl-icon/index.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<view :class="'iconfont ' + type" :style="{ color: color,'line-height':size + 'px', 'font-size': size + 'px' }"
|
||||
@click="_onClick" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UniIcon',
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 20
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_onClick() {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import './iconfont.css';
|
||||
|
||||
.iconfont {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
33
components/cl-icon/readme.md
Normal file
33
components/cl-icon/readme.md
Normal file
@ -0,0 +1,33 @@
|
||||
### Tag 标签
|
||||
|
||||
图标
|
||||
项目图标当前管理者1209559047@qq.com
|
||||
|
||||
**使用方式:**
|
||||
|
||||
在 ``script`` 中引用组件
|
||||
|
||||
```javascript
|
||||
import mIcon from "@/components/m-icon/m-icon.vue"
|
||||
export default {
|
||||
components: {mIcon},
|
||||
}
|
||||
```
|
||||
|
||||
在 ``template`` 中使用组件
|
||||
|
||||
```html
|
||||
<mIcon type="test" :size="20"></mIcon>
|
||||
```
|
||||
|
||||
|
||||
**属性说明:**
|
||||
|
||||
|属性名 |类型 |默认值 |说明 |
|
||||
|--- |---- |--- |--- |
|
||||
|
||||
**事件说明:**
|
||||
|
||||
|事件称名|说明|
|
||||
|---|----|---|
|
||||
|click|接管了点击事件|
|
19
components/cl-test/cl-test.vue
Normal file
19
components/cl-test/cl-test.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<view>
|
||||
我是cl-test组件
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
19
components/cl-test/index.vue
Normal file
19
components/cl-test/index.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<view>
|
||||
我是cl-test组件
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
1053
components/cropping/index.vue
Normal file
1053
components/cropping/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
216
components/model/vip-model.vue
Normal file
216
components/model/vip-model.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<u-popup :show="isShow" :round="10" :closeOnClickOverlay="false" mode="bottom" closeable overlay @close="hadleClose"
|
||||
@open="hadleOpen">
|
||||
<view class="vip-wrap">
|
||||
<view class="title">
|
||||
<u--text type="info" text="当前账号未开通会员"></u--text>
|
||||
</view>
|
||||
<view class="recharge">
|
||||
<view class="recharge-item" :class="current == index ? 'recharge-item-active': ''"
|
||||
v-for="(item, index) in memberGrade" :key="index" @click="handleRechargeItem(index)">
|
||||
<view class="recharge-tag">
|
||||
<text class="recharge-tag-text"
|
||||
v-if="parseInt(item.validDay) > 0">{{ item.validDay }}天有效期</text>
|
||||
<text class="recharge-tag-text" v-else>永久有效期</text>
|
||||
</view>
|
||||
<text class="recharge-item-duration">{{ item.name }}</text>
|
||||
<view class="recharge-item-price">
|
||||
<text class="rmb">¥</text>
|
||||
<text class="recharge-item-price-text">{{ item.catchValue }}</text>
|
||||
</view>
|
||||
<text class="recharge-item-des"
|
||||
v-if="item.validDay > 30">折合¥{{ Math.round((item.catchValue/item.validDay)*30 * 100) / 100 }}/月</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="agreement">
|
||||
<checkbox-group @change="handleClauseCheckbox">
|
||||
<label>
|
||||
<checkbox :checked="isClause" style="transform:scale(0.6)" />
|
||||
</label>
|
||||
<text class="check_text">
|
||||
<text>已阅读并勾选</text>
|
||||
<text class="service" @click="handleService">《会员服务协议》</text>
|
||||
</text>
|
||||
</checkbox-group>
|
||||
</view>
|
||||
<view class="bug-btn-wrap">
|
||||
<u-button type="warning" shape="circle" text="立即购买" @click="handleBug"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
show: function (newVal, oldVal) {
|
||||
this.isShow = newVal;
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isShow: false,
|
||||
// 会员信息
|
||||
memberGrade: [{
|
||||
name: '年度会员',
|
||||
validDay: 365,
|
||||
catchValue: 33
|
||||
}, {
|
||||
name: '月度会员',
|
||||
validDay: 30,
|
||||
catchValue: 5
|
||||
}],
|
||||
current: 0, // 当前会员类型选项
|
||||
isClause: false, // 勾选协议
|
||||
};
|
||||
},
|
||||
created () {
|
||||
// 获取设备状态(兼容H5和APP)
|
||||
if (this.show) {
|
||||
this.isShow = this.show;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hadleClose (e) {
|
||||
this.$emit('close');
|
||||
},
|
||||
hadleOpen (e) {
|
||||
this.$emit('open', e);
|
||||
},
|
||||
// 选择会员类型
|
||||
handleRechargeItem (index) {
|
||||
this.current = index
|
||||
},
|
||||
// 勾选协议
|
||||
handleClauseCheckbox () {
|
||||
this.isClause = !this.isClause;
|
||||
},
|
||||
// 购买
|
||||
handleBug () {
|
||||
if (this.isClause) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '功能开发中,敬请期待!'
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请先阅读并勾选协议'
|
||||
})
|
||||
}
|
||||
},
|
||||
handleService () {}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vip-wrap {
|
||||
padding: 10px;
|
||||
|
||||
.title {
|
||||
padding: 10px
|
||||
}
|
||||
|
||||
.recharge {
|
||||
position: relative;
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&-tag {
|
||||
position: absolute;
|
||||
top: -2rpx;
|
||||
left: -2rpx;
|
||||
width: 170rpx;
|
||||
height: 36rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-image: url('/static/tag.png');
|
||||
background-size: 100%;
|
||||
|
||||
&-text {
|
||||
font-size: 20rpx;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
position: relative;
|
||||
padding: 40rpx 0;
|
||||
width: 40%;
|
||||
height: 123px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border: solid 1rpx #CBCCCE;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
&-active {
|
||||
border: solid 2rpx #EDD2A9;
|
||||
background-color: #FBF1E5;
|
||||
}
|
||||
|
||||
&-duration {
|
||||
margin: 10px 0;
|
||||
font-size: 16px;
|
||||
color: #1C1C1C;
|
||||
}
|
||||
|
||||
&-price {
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
|
||||
&-text {
|
||||
font-size: 38px;
|
||||
color: #E3BE83;
|
||||
}
|
||||
}
|
||||
|
||||
&-des {
|
||||
font-size: 22rpx;
|
||||
color: #A5A3A2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.agreement {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
|
||||
.check_text {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.service {
|
||||
color: #3c9cff;
|
||||
}
|
||||
}
|
||||
|
||||
.bug-btn-wrap {
|
||||
margin-top: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
341
components/weather/index.vue
Normal file
341
components/weather/index.vue
Normal file
@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<view class="weather-wrap" :style="[weatherBg(nowData.code)]" @click="handleLocation">
|
||||
<view class="content-wrap">
|
||||
<view class="temp-wrap">
|
||||
<view class="img-wrap">
|
||||
<u--image :showLoading="true" :src="`/static/weather/${nowData.code}.svg`" width="130rpx"
|
||||
height="130rpx"></u--image>
|
||||
</view>
|
||||
<view class="num-wrap">
|
||||
<view v-if="nowData.temperature" style="margin-left: 30rpx; font-size: 64rpx;">
|
||||
{{nowData.temperature}}℃
|
||||
</view>
|
||||
<view v-else class="null-data" style="margin-left: 30rpx;"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="city">
|
||||
<text v-if="nowData.city">{{nowData.city}}</text>
|
||||
<view v-else class="null-loc-wrap">
|
||||
<view>未知位置</view>
|
||||
<view class="loc">点击屏幕重新定位</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom-wrap">
|
||||
<view class="item-wrap" v-for="(item,index) in nextData" :key="index">
|
||||
<view class="i-top-wrap">
|
||||
<text>{{item.date_text}}</text>
|
||||
<text style="margin-left: 30rpx;">{{item.date}}</text>
|
||||
</view>
|
||||
<view class="i-bot-wrap">
|
||||
<image :src="'/static/weather/'+item.code_day+'.svg'" style="width: 50rpx; height: 50rpx;"
|
||||
mode="aspectFit"></image>
|
||||
<text v-if="item.high && item.low" style="margin-left: 30rpx;">{{item.high}}°/{{item.low}}°</text>
|
||||
<text v-else class="null-data" style="margin-left: 30rpx;"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
Date.prototype.formatCode = function (formatStr = "yyyy-MM-DD HH:mm:ss") {
|
||||
const paddingZero = num => num >= 10 ? num : '0' + num;
|
||||
let str = formatStr;
|
||||
str = str.replace(/yyyy|YYYY/, this.getFullYear());
|
||||
str = str.replace(/MM/, paddingZero(this.getMonth() + 1));
|
||||
str = str.replace(/dd|DD/, paddingZero(this.getDate()));
|
||||
str = str.replace(/hh|HH/, paddingZero(this.getHours()));
|
||||
str = str.replace(/mm/, paddingZero(this.getMinutes()));
|
||||
str = str.replace(/ss/, paddingZero(this.getSeconds()));
|
||||
str = str.replace(/SS/, paddingZero(this.getMilliseconds()));
|
||||
return str;
|
||||
};
|
||||
|
||||
import projectConfig from '@/env.config.js';
|
||||
import QQMapWX from "@/common/qqmap-wx-jssdk.min.js";
|
||||
|
||||
export default {
|
||||
name: 'weather',
|
||||
data () {
|
||||
return {
|
||||
// 天气数据
|
||||
nowData: {
|
||||
code: "10",
|
||||
temperature: "",
|
||||
text: "阵雨",
|
||||
city: ""
|
||||
},
|
||||
nextData: [{
|
||||
date_text: '今天',
|
||||
code_day: "10",
|
||||
date: "08/14",
|
||||
high: "",
|
||||
low: "",
|
||||
}, {
|
||||
date_text: '明天',
|
||||
code_day: "4",
|
||||
date: "08/15",
|
||||
high: "",
|
||||
low: "",
|
||||
}, {
|
||||
date_text: '后天',
|
||||
code_day: "4",
|
||||
date: "08/16",
|
||||
high: "",
|
||||
low: "",
|
||||
}]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
// 由于权限问题,app 平台除外,其他平台自动定位
|
||||
// #ifndef APP-PLUS
|
||||
this.initData();
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const location = uni.getStorageSync('location');
|
||||
if (location) {
|
||||
this.initData();
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
computed: {
|
||||
weatherBg () {
|
||||
return icon => {
|
||||
if (icon >= 0 && icon <= 1) {
|
||||
return {
|
||||
background: 'linear-gradient(235deg, #FFF1E3 0%, #F3F7FF 45%, #DDE4FB 100%)'
|
||||
}
|
||||
}
|
||||
if (icon >= 2 && icon <= 12) {
|
||||
return {
|
||||
background: 'linear-gradient(235deg, #ECF4FF 0%, #E0EBFB 46%, #CBD6E5 100%)'
|
||||
}
|
||||
}
|
||||
if (icon >= 13 && icon <= 17) {
|
||||
return {
|
||||
background: 'linear-gradient(229deg, rgba(225,243,251,0.99) 0%, #DCF5FE 43%, #DBF0F1 100%)'
|
||||
}
|
||||
}
|
||||
if (icon >= 18 && icon <= 20) {
|
||||
return {
|
||||
background: 'linear-gradient(235deg, #D1C3B4 0%, #E1D9D3 38%, #EEEAE7 100%)'
|
||||
}
|
||||
}
|
||||
return {
|
||||
background: 'linear-gradient(235deg, #EEF5FF 0%, #D6DDEE 100%)'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
this.getLocation();
|
||||
},
|
||||
format (index) {
|
||||
var b = "";
|
||||
switch (index) {
|
||||
case 0:
|
||||
b = "星期日";
|
||||
break;
|
||||
case 1:
|
||||
b = "星期一";
|
||||
break;
|
||||
case 2:
|
||||
b = "星期二";
|
||||
break;
|
||||
case 3:
|
||||
b = "星期三";
|
||||
break;
|
||||
case 4:
|
||||
b = "星期四";
|
||||
break;
|
||||
case 5:
|
||||
b = "星期五";
|
||||
break;
|
||||
default:
|
||||
b = "星期六";
|
||||
}
|
||||
return b;
|
||||
},
|
||||
// 获取定位查询天气
|
||||
getLocation () {
|
||||
console.log('获取定位');
|
||||
let _this = this;
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success (res) {
|
||||
_this.getCurrWeather(res);
|
||||
_this.getNextWeather(res);
|
||||
},
|
||||
fail (e) {
|
||||
console.log(e);
|
||||
uni.showToast({
|
||||
title: '无法获取定位',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取天气信息
|
||||
getCurrWeather (ad) {
|
||||
let _this = this;
|
||||
uni.request({
|
||||
url: 'https://api.seniverse.com/v3/weather/now.json',
|
||||
data: {
|
||||
key: projectConfig.xinzhiKey,
|
||||
location: `${ad.latitude}:${ad.longitude}`,
|
||||
},
|
||||
success (res) {
|
||||
const { statusCode, data } = res;
|
||||
uni.showToast({
|
||||
image: '/static/right.png',
|
||||
title: data.results[0].now,
|
||||
})
|
||||
if (statusCode === 200) {
|
||||
_this.nowData = { ...data.results[0].now, city: data.results[0].location.name };
|
||||
} else {
|
||||
console.log('获取当前天气信息失败!');
|
||||
}
|
||||
},
|
||||
fail (err) {
|
||||
console.log('获取当前天气信息失败!', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
getNextWeather (ad) {
|
||||
let _this = this;
|
||||
uni.request({
|
||||
url: 'https://api.seniverse.com/v3/weather/daily.json',
|
||||
data: {
|
||||
key: projectConfig.xinzhiKey,
|
||||
location: `${ad.latitude}:${ad.longitude}`,
|
||||
},
|
||||
success (res) {
|
||||
const { statusCode, data } = res;
|
||||
if (statusCode === 200) {
|
||||
_this.nextData = _this.formNextData(data.results[0].daily);
|
||||
} else {
|
||||
console.log('获取未来3天天气信息失败!');
|
||||
}
|
||||
},
|
||||
fail (err) {
|
||||
console.log('获取未来3天天气信息失败!', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
formNextData (list) {
|
||||
return list.map((item, index) => ({
|
||||
...item,
|
||||
date: `${item.date.split('-')[1]}/${item.date.split('-')[2]}`,
|
||||
date_text: index === 0 ? '今天' : index === 1 ? '明天' : '后天',
|
||||
}))
|
||||
},
|
||||
// 点击界面获取定位
|
||||
async handleLocation () {
|
||||
// 权限问题,app 平台需要判断
|
||||
// #ifdef APP-PLUS
|
||||
const location = uni.getStorageSync('location');
|
||||
if (!location) {
|
||||
let result = await this.$store.dispatch("permission/requestPermissions", 'ACCESS_FINE_LOCATION');
|
||||
if (result !== 1) return;
|
||||
uni.setStorageSync('location', true);
|
||||
}
|
||||
// #endif
|
||||
// 其他平台直接获取定位
|
||||
this.getLocation();
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.weather-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
border-radius: 36rpx;
|
||||
padding: 20rpx;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.content-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 20rpx 10rpx;
|
||||
|
||||
.temp-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
.num-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.img-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.city {
|
||||
padding: 20rpx;
|
||||
font-size: 36rpx;
|
||||
|
||||
.null-loc-wrap {
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
|
||||
.loc {
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10rpx 20rpx 20rpx;
|
||||
font-size: 28rpx;
|
||||
|
||||
.item-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.i-top-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.i-bot-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.null-data {
|
||||
border-bottom: 10rpx solid #000000;
|
||||
width: 60rpx;
|
||||
transform: scaleY(0.5);
|
||||
border-top-color: #000000;
|
||||
border-right-color: #000000;
|
||||
border-left-color: #000000;
|
||||
}
|
||||
}
|
||||
</style>
|
37
env.config.js
Normal file
37
env.config.js
Normal file
@ -0,0 +1,37 @@
|
||||
// H5端开发和生产环境协议
|
||||
let protocalDev = "wss://";
|
||||
let protocalProd = "wss://";
|
||||
|
||||
// 条件编译,微信端和移动端使用wxs协议
|
||||
// #ifdef MP-WEIXIN || APP-PLUS
|
||||
protocalDev = 'wxs://';
|
||||
protocalProd = 'wxs://';
|
||||
// #endif
|
||||
|
||||
// 腾讯地图key
|
||||
let qqmapKey = '4PDBZ-4KQKU-AX6VO-GU7NB-INDZJ-YBFXC';
|
||||
|
||||
// 心知天气key
|
||||
let xinzhiKey = 'SBh45_yy21FU5ErV_';
|
||||
|
||||
const CONFIG = {
|
||||
// 开发环境配置
|
||||
development: {
|
||||
officialWebUrl: 'https://www.xcwl-aiot.cn/',
|
||||
baseUrl: 'https://www.xcwl-aiot.cn/prod-api/',
|
||||
mqttServer: protocalDev + 'www.xcwl-aiot.cn/mqtt',
|
||||
decoderUrl: 'https://www.xcwl-aiot.cn/',
|
||||
qqmapKey,
|
||||
xinzhiKey,
|
||||
},
|
||||
// 生产环境配置
|
||||
production: {
|
||||
officialWebUrl: 'https://www.xcwl-aiot.cn/',
|
||||
baseUrl: 'https://www.xcwl-aiot.cn/prod-api/',
|
||||
mqttServer: protocalDev + 'www.xcwl-aiot.cn/mqtt',
|
||||
decoderUrl: 'https://www.xcwl-aiot.cn/',
|
||||
qqmapKey,
|
||||
xinzhiKey,
|
||||
}
|
||||
}
|
||||
export default CONFIG[process.env.NODE_ENV];
|
55
helpers/common.js
Normal file
55
helpers/common.js
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 获取sip状态名称、颜色等信息
|
||||
* @param {Number} value 状态id
|
||||
* @returns {Object} { id, name, type }
|
||||
*/
|
||||
export function getSipStatusInfo (id) {
|
||||
if (id === 1) {
|
||||
return { id: id, name: '未使用', type: 'info' };
|
||||
}else if (id === 2) {
|
||||
return { id: id, name: '禁用', type: 'info' };
|
||||
} else if (id === 3) {
|
||||
return { id: id, name: '在线', type: 'success' };
|
||||
} else if (id === 4) {
|
||||
return { id: id, name: '离线', type: 'warning' };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取device状态名称、颜色等信息
|
||||
* @param {Number} value 状态id
|
||||
* @returns {Object} { id, name, type }
|
||||
*/
|
||||
export function getDeviceStatusInfo (id) {
|
||||
if (id === 1) {
|
||||
return { id: id, name: '未激活', type: 'info' };
|
||||
} else if (id === 2) {
|
||||
return { id: id, name: '禁用', type: 'info' };
|
||||
} else if (id === 3) {
|
||||
return { id: id, name: '在线', type: 'success' };
|
||||
} else if (id === 4) {
|
||||
return { id: id, name: '离线', type: 'warning' };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态名称、颜色等信息
|
||||
* @param {Number} value 定位方式id
|
||||
* @returns {Object} { id, name }
|
||||
*/
|
||||
export function getLocationWayInfo (id) {
|
||||
if (id === 1) {
|
||||
return { id: id, name: '自动定位' };
|
||||
} else if (id === 2) {
|
||||
return { id: id, name: '设备定位' };
|
||||
} else if (id === 3) {
|
||||
return { id: id, name: '自定义位置' };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
698
locale/en-US.json
Normal file
698
locale/en-US.json
Normal file
@ -0,0 +1,698 @@
|
||||
{
|
||||
"locale.auto": "System",
|
||||
"locale.en-US": "English",
|
||||
"locale.zh-CN": "简体中文",
|
||||
"navBar.login": "Login",
|
||||
"navBar.registration": "User Registration",
|
||||
"navBar.loginBinding": "User Login Binding",
|
||||
"navBar.registrationBinding": "User Registration Binding",
|
||||
"navBar.smsBogin": "SMS Login",
|
||||
"navBar.home": "Home",
|
||||
"navBar.scene": "Scene",
|
||||
"navBar.alert": "Alert",
|
||||
"navBar.news": "Dynamic",
|
||||
"navBar.user": "Me",
|
||||
"navBar.browse": "Browsing web pages",
|
||||
"navBar.sceneDetail": "Scene Detail",
|
||||
"navBar.sceneList": "Scene List",
|
||||
"navBar.productList": "Product List",
|
||||
"navBar.deviceList": "Device List",
|
||||
"navBar.physicalModels": "List Of Physical Models",
|
||||
"navBar.alarmList": "Alarm List",
|
||||
"navBar.timer": "Timer",
|
||||
"navBar.alarmLog": "Alarm Log",
|
||||
"navBar.alarmHandling": "Alarm Handling",
|
||||
"navBar.deviceDetails ": "Device Details ",
|
||||
"navBar.timedList": "Timed List",
|
||||
"navBar.timedDetails": "Timed Details",
|
||||
"navBar.dynamic": "Latest updates",
|
||||
"navBar.dynamicDetails": "Dynamic Details",
|
||||
"navBar.classification": "Classification Dynamics",
|
||||
"navBar.addDevice": "Add Device",
|
||||
"navBar.associatedDevices": "Associated Devices",
|
||||
"navBar.deviceSharing": "Device Dharing",
|
||||
"navBar.shareDetails": "Share User Details",
|
||||
"navBar.about ": "About",
|
||||
"common.fastbee": "Fastbee",
|
||||
"navBar.latest": "Latest news",
|
||||
"navBar.newsDetail": "News Detail",
|
||||
"navBar.setting": "System Setting",
|
||||
"navBar.simulation": "Device Simulation",
|
||||
"navBar.mobile": "Mobile monitoring",
|
||||
"navBar.account": "Account Messages",
|
||||
"navBar.language": "Multilingual",
|
||||
"navBar.updateAvatar": "Update Avatar",
|
||||
"navBar.unbind": "Unbind WeChat",
|
||||
"navBar.group": "Device grouping",
|
||||
"navBar.running": "Runiing Status",
|
||||
"navBar.updateGroup": "Edit group",
|
||||
"navBar.updateDevice": "Device Update",
|
||||
"navBar.eventLog": "Event Log",
|
||||
"navBar.orderLog": "Instruction log",
|
||||
"navBar.player": "Player",
|
||||
"common.add": "add",
|
||||
"common.delete": "Delete",
|
||||
"common.save": "Save",
|
||||
"common.cancel": "Cancel",
|
||||
"common.confirm": "Comfirm",
|
||||
"common.back": "Back",
|
||||
"common.edit": "Edit",
|
||||
"common.updateSuccessful": "upadte successful",
|
||||
"common.addSuccessful": "add successsful",
|
||||
"common.saveError": "Save failed",
|
||||
"common.saveSuccessful": "Save Successful",
|
||||
"common.operation": "Operation successful",
|
||||
"common.search": "Search",
|
||||
"common.tips": "Tips",
|
||||
"common.loginTips": "This function can be used only after you log in. Do you want to log in ?",
|
||||
"login": {
|
||||
"accountLogin": "Account Login",
|
||||
"inputUserName": "Please Input Username",
|
||||
"inputPassword": "Please Input Password",
|
||||
"inputCode": "Please Input Code",
|
||||
"login": "Log in",
|
||||
"exampleAccount": "Example:fastbee 123456",
|
||||
"HuapuIoT": "HuapuIoT",
|
||||
"registerNow": "Register",
|
||||
"readAndAgree": "Read and Agree",
|
||||
"serviceAgreement": "Service Agreement",
|
||||
"and": "and",
|
||||
"privacyPolicy": "Privacy Policy",
|
||||
"childProtectionStatement": "Children's Privacy Statement",
|
||||
"commonBill": "Third-party Sharing And SDK Bill",
|
||||
"readAndCheckTheAgreement": "Please read and check the agreement",
|
||||
"accontMsg": "Account information cannot be empty",
|
||||
"phoneLogin": "Phone Login",
|
||||
"welcomeToLogin": "Welcome to login FastBee",
|
||||
"phoneAuthorization": "phone Login",
|
||||
"remember": "Remember password",
|
||||
"other": "Other login methods"
|
||||
},
|
||||
"register": {
|
||||
"userRegister": "User Registration",
|
||||
"inputUserName": "Please Input Username",
|
||||
"inputPhone": "Please Input Phonenumber",
|
||||
"inputPassword": "Please Input Password",
|
||||
"inputPasswordAgain": "Please Input Password Again",
|
||||
"inputUserNameSize": "Please Input Username And Length greater than four",
|
||||
"inputCode": "Please Input Code",
|
||||
"registration": "Sign up",
|
||||
"accountLogin": "Log In To An Existing Account",
|
||||
"congratulations": "congratulations,your account ",
|
||||
"registeredSuccessfully": "Registered Successfully"
|
||||
},
|
||||
"smsLogin": {
|
||||
"smsLogin": "Sms Login",
|
||||
"login": "Log in",
|
||||
"HuapuIoT": "HuapuIoT ",
|
||||
"accountLogin": "Account Login",
|
||||
"obtainCode": "Obtain Code",
|
||||
"correctPhone": "Please Input Correct PhoneNumber",
|
||||
"obtainSmsCode": "Obtaining Verification Code",
|
||||
"codeSent": "Verification code has been sent",
|
||||
"sending": "The countdown ends before sending"
|
||||
},
|
||||
"bindLogin": {
|
||||
"bindLogin": "Bind Account Login",
|
||||
"bindRegister": "Bind Register",
|
||||
"alert": "Alert",
|
||||
"alertTitle": "The account does not exist. Do you want to register a new account",
|
||||
"notRegister": "Cancel",
|
||||
"confirmRegister": "Confirm",
|
||||
"accountNotNull": "Account cannot be empty",
|
||||
"bind": "Bind"
|
||||
},
|
||||
"bindRegister": {
|
||||
"bindRegister": "Bind Register",
|
||||
"incorrectPhone": "Incorrect phone number format",
|
||||
"inputPasswordSize": "Please Input Code And Length greater than five",
|
||||
"enterPasswordsDiffer": "Entered passwords differ"
|
||||
},
|
||||
"user": {
|
||||
"personalCentre": "Personal Centre",
|
||||
"openIOT": "FastBee-Open source IoT platform",
|
||||
"addDevice": "Add Device",
|
||||
"addScene": "Add Scene",
|
||||
"groupManagement": "Group Manage",
|
||||
"platformMsg": "Platform Msg",
|
||||
"ordinaryMembers": "Ordinary Members",
|
||||
"currentAccountAlert": "Current Account No Membership Has Been Opened",
|
||||
"openAtOnce": "Open At Once",
|
||||
"account": "Account Number",
|
||||
"messages": "Messages",
|
||||
"signOut": "Sign Out",
|
||||
"about": "About",
|
||||
"language": "language",
|
||||
"appDownload": "App Download",
|
||||
"exitAccount": "Exit",
|
||||
"confirmExit": "confirm Exit",
|
||||
"confirmSignOut": "confirm Sign Out",
|
||||
"signOutAlert": "After account cancellation, all information will be cleared and cannot be restored. Do you want to cancel?",
|
||||
"notWriteOff": "Not Write Off",
|
||||
"confirmWriteOff": "Confirm Write Off",
|
||||
"confirmWxBind": "Confirm Bind Wechat",
|
||||
"wxBindAlertTitle": "Do you want to bind WeChat",
|
||||
"notBind": "Not Currently Bind",
|
||||
"confirmBind": "Confirm Binding",
|
||||
"saveSuccess": "Save Success",
|
||||
"occurError": "Occur Error",
|
||||
"scanning": "Scanning only supports apps and mini programs",
|
||||
"parseQrCode": "After parsing the QR code,Unable to find corresponding processing type",
|
||||
"errorParseQRcode": "Error parsing QR code,Incorrect format",
|
||||
"language-01": "Language",
|
||||
"updatePassword": "Edit Password",
|
||||
"login": "Login/Register",
|
||||
"visitor": "Visitor",
|
||||
"configuration": "Independent Configuration",
|
||||
"notice": "This feature requires you to log in to use it. Would you like to log in?"
|
||||
},
|
||||
"account": {
|
||||
"avatar": "Avatar",
|
||||
"nikeName": "Nikename",
|
||||
"email": "Email",
|
||||
"phone": "Phonenumber",
|
||||
"createTime": "Createtime",
|
||||
"Ip": "IP",
|
||||
"save": "Save",
|
||||
"inputPhone": "Please Input PhoneNumber",
|
||||
"inputEmail": "Please Input Email",
|
||||
"inputNickname": "PleaseInput NickName",
|
||||
"inputCreatetime": "Please Input Createtime",
|
||||
"inputIP": "Please Input IP",
|
||||
"incorrectEmail": "Incorrect Email format"
|
||||
},
|
||||
"home": {
|
||||
"netWork": "Distribution Network Add",
|
||||
"wifi-type": "Common For WIFI-type Devices",
|
||||
"qrCode": "SCAN QR Code To Add",
|
||||
"networksDevices": "For Cellular Networks/Ethernet Devices",
|
||||
"association": "Association Is Added",
|
||||
"supportBatchOperations": "For Cellular Networks/Ethernet Devices, And Support Batch Operations",
|
||||
"errorLoading": "Loading Error",
|
||||
"sureAdd": "Are you sure to add it?",
|
||||
"productName": "productName",
|
||||
"serialNumber": "serialNumber",
|
||||
"notActive": "Not Active",
|
||||
"disabled": "disAbled",
|
||||
"onLine": "onLine",
|
||||
"offline": "offline",
|
||||
"all": "All",
|
||||
"shadow": "Shadow",
|
||||
"eventLog": "Event Log",
|
||||
"Instructionlog": "Instruction log"
|
||||
},
|
||||
"about": {
|
||||
"about": "about",
|
||||
"authorQQ": "Author QQ : 164770707",
|
||||
"authorWechat": "Author Wechat : QQ164770707",
|
||||
"group": "Group: 720136372, 1073236354",
|
||||
"webSite": "Official Website",
|
||||
"SourceCode": "View The Source Code",
|
||||
"open": "FastBee-Open source IoT platform Version 2.5.1",
|
||||
"message": "Qujing Fengxin Technology Co., Ltd | HuapuIOT | Official website fastbee.cn"
|
||||
},
|
||||
"deviceDetail": {
|
||||
"basicMsg": "Basic Messages",
|
||||
"deviceName": "DeviceName",
|
||||
"position": "Position",
|
||||
"longitude": "Longitude",
|
||||
"latitude": "Latitude",
|
||||
"address": "Address",
|
||||
"shadow": "Shadow",
|
||||
"disabledDevice": "DSisable",
|
||||
"remark": "Remarks",
|
||||
"inputMsg": "Please Input The Content",
|
||||
"inputLongitude": "Please Input Device Longitude",
|
||||
"inputLatitude": "Please Input Device latitudel",
|
||||
"loadingFail": "Loading Fail",
|
||||
"veryGood": "Very Good",
|
||||
"excellent": "excellent",
|
||||
"good": "Good",
|
||||
"average": "Average",
|
||||
"poor": "Poor",
|
||||
"device": "Device",
|
||||
"timing": "Timing",
|
||||
"journal": "Journal",
|
||||
"static": "static",
|
||||
"share": "Share",
|
||||
"preserve": "Preserve",
|
||||
"deviceStatus": "Status",
|
||||
"serialNumber": "Serial Number",
|
||||
"belongingProducts": "Product",
|
||||
"firmwareVersion": "Version",
|
||||
"positionMethod": "Position",
|
||||
"equipmentSignal": "Signal",
|
||||
"networkAddress": "networkIp",
|
||||
"address": "Address",
|
||||
"activationTime": "activeTime",
|
||||
"deleteDevice": "Delete",
|
||||
"monitor": "monitor",
|
||||
"runningStatus": "runningStatus",
|
||||
"deviceDetail": "Device Detail",
|
||||
"channel": "Channel",
|
||||
"autoPosition": "Auto Position",
|
||||
"devicePosition": "Device Position",
|
||||
"customLocation": "Custom Location",
|
||||
"deviceCheck": "The device name cannot be empty",
|
||||
"positionCheck": "The position methods cannot be empty",
|
||||
"updateSuccess": "Update Success",
|
||||
"Overview": "Device Overview",
|
||||
"Surveillance": "Video Surveillance",
|
||||
"Alert": "Alert Log",
|
||||
"scada": "scada",
|
||||
"emptyNull": "Data Null"
|
||||
},
|
||||
"timing": {
|
||||
"timingName": "Please Input Timing Name",
|
||||
"search": "search",
|
||||
"execute": "Execute once",
|
||||
"effortLoading": "hard Loading...",
|
||||
"loadMore": "Click on me to load more",
|
||||
"nothing": "There's really nothing left",
|
||||
"emptyNull": "Data is empty",
|
||||
"all": "All",
|
||||
"enable": "Enable",
|
||||
"notEnabled": "Not Enabled",
|
||||
"nosupported": "WeChat mini programs are currently not supported",
|
||||
"wap2": "Please use wap2app to package!"
|
||||
},
|
||||
"deviceStatic": {
|
||||
"selectSub": "Please Select Device Slave",
|
||||
"rangeTime": "BeginTime-EndTime",
|
||||
"selectTime": "Please Select Range Time",
|
||||
"search": "search"
|
||||
},
|
||||
"monitior": {
|
||||
"stop": "stop",
|
||||
"monitoringInterval": "Monitoring interval(ms) And number of times",
|
||||
"inputNumber": "Enter the number of milliseconds between inputs",
|
||||
"inputTimes": " Input count",
|
||||
"stop": "Stop",
|
||||
"monitior": "Monitior",
|
||||
"receivingData": "Receive Data...",
|
||||
"monitoringRange": "Monitoring interval range 500-10000 milliseconds",
|
||||
"monitoringQuantity": "Monitoring quantity range 1-300",
|
||||
"real-timeMonitor": "real-time monitor",
|
||||
"stopMonitor": "Stop Monitior"
|
||||
},
|
||||
"scene": {
|
||||
"sceneLink": "Scene Linkage",
|
||||
"inputSceneName": "Please Enter The Scene Name",
|
||||
"search": "Search",
|
||||
"condition": "Condition",
|
||||
"way": "Way",
|
||||
"task": "Task",
|
||||
"execute": "Execute once",
|
||||
"tryingToLoad": "Trying To Load...",
|
||||
"gentlyPullUp": "Gently Pull Up",
|
||||
"nothingLeft": "There's Really Nothing Left",
|
||||
"emptyData": "Data Is Empty",
|
||||
"administration": "Administration",
|
||||
"alarm": "Alarm",
|
||||
"anyCondition": " Any Condition",
|
||||
"allCondition": "All Conditions",
|
||||
"notConditions": "Not Meeting The Conditions",
|
||||
"unknown": "Unknown",
|
||||
"serial": "Serial",
|
||||
"parallel": "Parallel",
|
||||
"switchSuccessful": "Switch Successful",
|
||||
"switchFail": "Switch Fail",
|
||||
"seeMore": "See more >"
|
||||
},
|
||||
"share": {
|
||||
"userDetail": "User Details",
|
||||
"userMsg": "User Information ",
|
||||
"userId": "userId:",
|
||||
"userName": "userName:",
|
||||
"phoneNumber": "phoneNumber:",
|
||||
"userPermissions": "User permissions",
|
||||
"remark": "Remark",
|
||||
"notRemark": "There are currently no notes available",
|
||||
"confirmShare": "Confirm Share",
|
||||
"deviceUpgradation": "Device upgradation",
|
||||
"otaUpgradation": "Device OTA upgradation",
|
||||
"timing": "Device Timing",
|
||||
"timedTaskExecution": "Timed task execution",
|
||||
"deviceLog": "Device Log",
|
||||
"contains": "Contains event logs and instruction logs",
|
||||
"monitior": "Monitior",
|
||||
"afterMonitior": "After Issuing Real-time Monitoring Instructions, The Chart Displays The Data Reported By The Device In Real Time",
|
||||
"monitioringStatic": "Monitoring Statistics",
|
||||
"chartDisplay": "Chart Displays Stored Historical Monitoring Data",
|
||||
"deviceShareSuccess": "Device Sharing Successful",
|
||||
"title": "prompted",
|
||||
"alert": "Are you sure to delete the current user ?",
|
||||
"delete": "Delete...",
|
||||
"inputUserPhone": "Please enter the user's mobile phone number ",
|
||||
"deviceShare": "Share Device",
|
||||
"query": "query",
|
||||
"sharedUsers": "Shared Users",
|
||||
"master": "master",
|
||||
"share": "Share",
|
||||
"unable": "Unable to find user information, or the user is already a device user",
|
||||
"correct": "Please enter the correct phone number"
|
||||
},
|
||||
"sceneDetail": {
|
||||
"inputName": "Please Enter A Name",
|
||||
"sceneState": "Scene State",
|
||||
"trigger": "Trigger",
|
||||
"addConditions": "Add Conditions",
|
||||
"deviceNumber": "Number Of Devices",
|
||||
"deviceOnline": "Device Online",
|
||||
"Equipment": "Equipment Offline",
|
||||
"action": "Execute Action",
|
||||
"addTask": "Add Task",
|
||||
"alertExecute": "Alarm Execution",
|
||||
"moreSetting": "More Settings",
|
||||
"silentTimeAlert": "Silent Time: Within The Set Time Range, It Will No Longer Be Executed Repeatedly;Delay Execution: The Delay Will Not Be Stored And Is limited To 90 Seconds.",
|
||||
"silentTime": "Silent Time",
|
||||
"inputTime": "Please Enter The Silence Time",
|
||||
"minute": "Minute",
|
||||
"executionMethod": "Execution Method",
|
||||
"serial": " Serial",
|
||||
"parallel": "Parallel",
|
||||
"delayed ": "Delayed Execution",
|
||||
"inputExtensionTime": "Please Enter An Extension Time ",
|
||||
"seconds": "Seconds",
|
||||
"complete": "Complete",
|
||||
"deviceTriggered": "Device Triggered",
|
||||
"productTriggering": "Product Triggering",
|
||||
"timedTrigger": "Timed Trigger",
|
||||
"deviceExecution": "Device Execution",
|
||||
"productExecution": "Product Execution",
|
||||
"sceneValidate": "Scene name cannot be empty",
|
||||
"triggerValidate": "Trigger condition cannot be empty",
|
||||
"taskValidate": "Task execution cannot be empty",
|
||||
"tips": "Tips"
|
||||
},
|
||||
"product": {
|
||||
"inputDeviceName": "Please Input DeviceName",
|
||||
"next": "Next",
|
||||
"selectThingModel": "Selcet ThingsModel",
|
||||
"attribute": "attribute",
|
||||
"function": "function",
|
||||
"event": "Event",
|
||||
"selectDevice": "Please Select DeviceName",
|
||||
"selectThingsModels": "Please Select ThingsModels",
|
||||
"inputProduct": "Please input Product Name",
|
||||
"selectProducts": "Please Select Product",
|
||||
"operator": "Operator",
|
||||
"selectOperator": "select Operator",
|
||||
"on": "ON",
|
||||
"off": "OFF",
|
||||
"input": "Please Enter",
|
||||
"inputValue": "Please Set corresponding values",
|
||||
"selectValue": "Please Select Value",
|
||||
"timedName": "Timed name cannot be empty",
|
||||
"taskExecution": "Task execution cannot be empty"
|
||||
},
|
||||
"sceneTiming": {
|
||||
"time": "Time",
|
||||
"selectTime": "Please Select Time",
|
||||
"again": "Again",
|
||||
"selectWeek": "Please Select Week",
|
||||
"default": "Default",
|
||||
"auto": "Auto",
|
||||
"inputCRON": "Please Input CRON",
|
||||
"expression": "Generate expression",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday",
|
||||
"everyDay": "EveryDay",
|
||||
"status": "Timing Status",
|
||||
"trigger": "The trigger has only one timing, and the alarm during the execution action is invalid",
|
||||
"alarm": "Alarm execution",
|
||||
"develop": "Under development...",
|
||||
"select": "Please select an alarm"
|
||||
},
|
||||
"alert": {
|
||||
"inputAlertName": "Please input alert name",
|
||||
"all": "All",
|
||||
"pending": "Pending",
|
||||
"untreated": "Untreated",
|
||||
"processed": "Processed",
|
||||
"notice": "Notice",
|
||||
"minor": "Minor",
|
||||
"warning": "Warning",
|
||||
"alertName": "Alert Name",
|
||||
"processState": "Process State",
|
||||
"data": "Data",
|
||||
"alertTime": "Alert Time",
|
||||
"process": "Process",
|
||||
"alertInformation": "Warning Information",
|
||||
"serialNumber": "Serial Number",
|
||||
"deviceName": "Device Name",
|
||||
"alertType": "Alert Level",
|
||||
"alertProcess": "Alert Process",
|
||||
"processAlert": "Process Alert",
|
||||
"processResult": "Process Result",
|
||||
"processTime": "Process Time",
|
||||
"inputProcessMsg": "Please input process message, do not fill"
|
||||
},
|
||||
"status": {
|
||||
"deviceVersion": "Version",
|
||||
"deviceFirmware": "Device Firmware Upgrade",
|
||||
"grade": "grade",
|
||||
"noNeedToUpgrade": "It Is Already The Latest Version, No Need To Upgrade",
|
||||
"upgraded": "There Is A New Version That Can Be Upgraded",
|
||||
"name": "Name:",
|
||||
"version": "Version:",
|
||||
"description": "Description:",
|
||||
"inputString": "Please Enter A String",
|
||||
"unit": "Unit",
|
||||
"send": "Send Out",
|
||||
"decimals": "Please Enter Decimals",
|
||||
"integer": " Please Enter An Integer",
|
||||
"monitior": "Monitoring Data",
|
||||
"offline": "Data When The Device Is Offline",
|
||||
"service": "Service Call Successful",
|
||||
"equip": "Equipment Upgrade",
|
||||
"online": "Device Online",
|
||||
"shadow": "Shadow Mode",
|
||||
"deviceOffline": "Device Offline",
|
||||
"control": "Equipment control",
|
||||
"stateModel": "State Model",
|
||||
"dataModel": "Data Model",
|
||||
"devDetail": "Device Detail"
|
||||
},
|
||||
"deviceAdd": {
|
||||
"addDevice": "Add Device",
|
||||
"wifi": "Name",
|
||||
"inputWifiName": "Please Enter The WIFI Name",
|
||||
"passsword": "Password",
|
||||
"inputWifiPassword": "Please Enter The WIFI Password",
|
||||
"remember": "Remember Password",
|
||||
"senior": "Senior",
|
||||
"userId": "User Number",
|
||||
"inputUserId": "Please Enter The User Number",
|
||||
"deviceNum": "DeviceNum",
|
||||
"inputDeviceNum": "Please Enter The Device Number",
|
||||
"authorization": "Authorization Code",
|
||||
"inputAuthor": "Please Enter The Authorization Code",
|
||||
"supplementary": "Supplementary Information",
|
||||
"inputSupplementary": "Please Enter Supplementary Information",
|
||||
"step1": "Step 1: Fill in WIFI information",
|
||||
"step2": "Step 2: The Device Enters Distribution Network Mode",
|
||||
"step3": "Step 3: Distribution Network",
|
||||
"single": "Single",
|
||||
"multiple": "Multiple",
|
||||
"networkMode": "Equipment Enters Distribution Network Mode",
|
||||
"wifiHotspot": "The Device Will Activate WIFI Hotspot",
|
||||
"manually": "Manually Connecting Device Hotspots",
|
||||
"mobile": "Mobile Phone Connection Device WIFI Hotspot",
|
||||
"detection": "Detection Equipment",
|
||||
"system": "System Automatic Detection",
|
||||
"end": "End Of Distribution Network",
|
||||
"reconnected": "Mobile Phone Can Be Reconnected To WIFI",
|
||||
"startWork": "Start Network Distribution",
|
||||
"redistributingNetwork": "Redistributing The Network",
|
||||
"after": "After Reconnecting ThePhone To WiFi, Return To View",
|
||||
"tip": "Tip: Currently, Multi Device Networking Only Supports WeChat Mini Programs And Requires Enabling The Mobile WiFi Switch",
|
||||
"noData": "No Further Data Available At The Moment",
|
||||
"select": "Please Select A Device Hotspot",
|
||||
"refresh": "Refresh",
|
||||
"start": "Start Distribution Network",
|
||||
"distribution": "Distribution network...",
|
||||
"connect": "Connecting to network...",
|
||||
"return": "Return To View",
|
||||
"noDevice": "No device detected",
|
||||
"wifiName": "WIFI Name cannot be empty",
|
||||
"wifiPassword": "WIFI Password Cannot be empty",
|
||||
"userNum": "userName is Cannot be empty",
|
||||
"deviceDetected": "Device detected",
|
||||
"userIdAndWifiAccount": "User ID and WIFI account password cannot be empty",
|
||||
"sendInformation": "Send configuration information...",
|
||||
"successNetwork": "Network configuration successful. If the device is not connected properly, please check if the WIFI information is correct and the network condition",
|
||||
"fail": "Distribution network failed. Please confirm that the device has entered distribution network mode and is connected to the hotspot",
|
||||
"selectNetwork": "Please select a device hotspot",
|
||||
"successDistribution": "Successful distribution network",
|
||||
"afterNetwork": "After network configuration, please connect to the WiFi network for internet connection.",
|
||||
"failNetwork": "Network configuration failed",
|
||||
"phoneTurnOn": "Please ensure that your phone's WiFi is turned on, and then re-enter the page",
|
||||
"prepare": "Prepare the distribution network",
|
||||
"miniProgrem": "Failed to start WiFi module, please reopen the mini program"
|
||||
},
|
||||
"linkDevice": {
|
||||
"linkDevice": "Device Linkage",
|
||||
"scan": "Scan",
|
||||
"deviceNum": "DeviceNum",
|
||||
"productNum": "ProductNum",
|
||||
"productName": "ProductName",
|
||||
"userName": "userName cannot be empty",
|
||||
"deviceEmpty": "deviceNum cannot be empty",
|
||||
"productIdEmpty": "productId cannot be empty ",
|
||||
"format": "Parsing QR code, incorrect format",
|
||||
"inputDeviceId": "Please Input DeviceId",
|
||||
"inputProductId": "Please Input productId",
|
||||
"product": "ProductName can be left blank"
|
||||
},
|
||||
"group": {
|
||||
"equipment": "Device Grouping",
|
||||
"add": " Add Device",
|
||||
"detail": "Details",
|
||||
"select": "Select Device",
|
||||
"update": "Device Update Successful",
|
||||
"name": "Name",
|
||||
"inputName": "Please Enter The Group Name",
|
||||
"sort": "Sort",
|
||||
"inputSort": "Please Enter Grouping Sorting",
|
||||
"remark": "Remarks",
|
||||
"content": "Please Enter The Content",
|
||||
"groupName": "Group Name Cannot Be Empty",
|
||||
"serialNumber": "The Serial Number Cannot Be Empty",
|
||||
"updateGroup": "Update Group",
|
||||
"submit": "Submitting",
|
||||
"system": "System Prompt",
|
||||
"delete": "Are You Sure To Delete The Current Group ?",
|
||||
"deleting": "Deleting...",
|
||||
"inputContent": "Please enter keywords",
|
||||
"nomore": "No More",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"log": {
|
||||
"all": "All",
|
||||
"function": "function",
|
||||
"attribute": "Attribute reporting",
|
||||
"event": "Event reporting",
|
||||
"online": "online",
|
||||
"offline": "offline",
|
||||
"upgrade": "Upgrade",
|
||||
"element": "Element",
|
||||
"service": "Service distribution",
|
||||
"acquisition": "Attribute acquisition",
|
||||
"ota": "OTA Upgrade"
|
||||
},
|
||||
"modbus": {
|
||||
"deviceName": "Device Name",
|
||||
"firmware": "Firmware version",
|
||||
"inputDeviceName": "Please Enter Device Name",
|
||||
"inputVersion": "Please Enter Firmware version",
|
||||
"time": "Time",
|
||||
"range": "Range",
|
||||
"unknown": "unknown"
|
||||
},
|
||||
"message": {
|
||||
"message": "message",
|
||||
"inform": "Inform",
|
||||
"notice": "Notice",
|
||||
"noticeDetail": "Notice Detail",
|
||||
"noContent": "There is currently no content available",
|
||||
"config": "Equipment configuration",
|
||||
"NTP": "NTP address",
|
||||
"productConfig": "Product configuration",
|
||||
"productPassword": "Product Password",
|
||||
"mqttConfig": "Mqtt Configuration",
|
||||
"mqttAddress": "Mqtt Address",
|
||||
"mqttAccount": " Mqtt Account",
|
||||
"mqttPassword": " Mqtt Password",
|
||||
"save": "Save Config",
|
||||
"report": "Reporting attributes",
|
||||
"reportfunction": "Reporting function",
|
||||
"event": "Reporting Event",
|
||||
"abtainTime": "Get Time",
|
||||
"monitior": "Reporting monitoring data",
|
||||
"mobile": "Mobile Phone monitoring",
|
||||
"function": "Function",
|
||||
"call": "Call",
|
||||
"bright": "screen brightness",
|
||||
"vibration": "vibration",
|
||||
"bluetooth": "Bluetooth",
|
||||
"photo": "Taking photos",
|
||||
"record": "Recording",
|
||||
"videos": "Videos",
|
||||
"attribute": "attribute",
|
||||
"information": "Device Information",
|
||||
"pointer": "Acceleration pointer",
|
||||
"gyroscope": "gyroscope",
|
||||
"compass": "compass",
|
||||
"memory": "insufficient memory",
|
||||
"network": "Network changes",
|
||||
"screenshot": "User screenshot",
|
||||
"setting": "System Setting",
|
||||
"system": "system configuration",
|
||||
"address": "Server address",
|
||||
"text": "Text",
|
||||
"statusReset": "Device status reset",
|
||||
"reset": "Reset",
|
||||
"mqttReconnection": "Mqtt reconnection",
|
||||
"reconnection": "reconnection"
|
||||
},
|
||||
"avatar": {
|
||||
"cropping": "Cropping",
|
||||
"album": "album",
|
||||
"relieve": "relieve"
|
||||
},
|
||||
"player": {
|
||||
"stream": "Live streaming on devices",
|
||||
"replay": "Replay Theater",
|
||||
"play": "Play",
|
||||
"suspend": "suspend",
|
||||
"enlarge": "enlarge",
|
||||
"narrow": "narrow",
|
||||
"recording": "Select recording date",
|
||||
"storage": "Device storage",
|
||||
"screenshot": "screenshot",
|
||||
"channel": "Device Channel",
|
||||
"loading": "Loading",
|
||||
"noRecording": "There is no recording in the current channel",
|
||||
"purchase": "Commercial version, please purchase authorization, loading in progress",
|
||||
"monitior": "Monitoring details"
|
||||
},
|
||||
"waitLogin": {
|
||||
"loginOrregister": "Login/Register",
|
||||
"ExperienceAccountLogin": "Experience account login",
|
||||
"agree": "I have read and agree",
|
||||
"privacy": "Privacy Policy",
|
||||
"agreement": "User Agreement",
|
||||
"children": "Children's Privacy Protection Statement",
|
||||
"third": "Third-Party Sharing and SDK List"
|
||||
},
|
||||
"indeScada": {
|
||||
"inputScada": "Please enter a scada name",
|
||||
"search": "Search",
|
||||
"tryingToLoad": "Trying To Load...",
|
||||
"gentlyPullUp": "Gently Pull Up",
|
||||
"nothingLeft": "There's Really Nothing Left",
|
||||
"emptyData": "Data Is Empty"
|
||||
},
|
||||
"deviceHistory": {
|
||||
"lastTwoHours": "last 2 hours",
|
||||
"lastOneDay": "last 1 day",
|
||||
"lastThirtyDay": "last 30 day",
|
||||
"custom": "custom",
|
||||
"inputDate": "starting time",
|
||||
"cancel": "Cancel",
|
||||
"nextStep": "Next Step",
|
||||
"confirm": "Confirm",
|
||||
"filtrate": "Filtrate",
|
||||
"variable": "Variable",
|
||||
"updateTime": "Update Time",
|
||||
"emptyTable": "No further data available",
|
||||
"emptyData": "No data"
|
||||
}
|
||||
}
|
6
locale/index.js
Normal file
6
locale/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
import en from './en-US.json';
|
||||
import zhHans from './zh-CN.json';
|
||||
export default {
|
||||
'zh-CN': zhHans,
|
||||
'en-US': en,
|
||||
}
|
697
locale/zh-CN.json
Normal file
697
locale/zh-CN.json
Normal file
@ -0,0 +1,697 @@
|
||||
{
|
||||
"locale.en-US": "English",
|
||||
"locale.zh-CN": "简体中文",
|
||||
"navBar.login": "登录",
|
||||
"navBar.registration": "用户注册",
|
||||
"navBar.loginBinding": "用户登录绑定",
|
||||
"navBar.registrationBinding": "用户注册绑定",
|
||||
"navBar.smsBogin": "短信登录",
|
||||
"navBar.home": "首页",
|
||||
"navBar.scene": "场景",
|
||||
"navBar.alert": "告警",
|
||||
"navBar.news": "动态",
|
||||
"navBar.user": "我的",
|
||||
"navBar.browse": "浏览网页",
|
||||
"navBar.sceneDetail": "场景详情",
|
||||
"navBar.sceneList": "场景列表",
|
||||
"navBar.productList": "产品列表",
|
||||
"navBar.deviceList": "设备列表",
|
||||
"navBar.physicalModels": "物模列表",
|
||||
"navBar.alarmList": "告警列表",
|
||||
"navBar.timer": "定时器",
|
||||
"navBar.alarmLog": "告警记录",
|
||||
"navBar.alarmHandling": "告警处理",
|
||||
"navBar.deviceDetails ": "设备详情",
|
||||
"navBar.timedList": "定时列表",
|
||||
"navBar.timedDetails": "定时详情",
|
||||
"navBar.dynamic": "最新动态",
|
||||
"navBar.dynamicDetails": "动态详情",
|
||||
"navBar.classification": "分类动态",
|
||||
"navBar.addDevice": "添加设备",
|
||||
"navBar.associatedDevices": "关联设备",
|
||||
"navBar.deviceSharing": "设备分享",
|
||||
"navBar.shareDetails": "分享用户详情",
|
||||
"navBar.latest": "最新消息",
|
||||
"navBar.newsDetail": "消息详情",
|
||||
"navBar.setting": "系统设置",
|
||||
"navBar.simulation": "设备模拟",
|
||||
"navBar.mobile": "手机监控",
|
||||
"navBar.account": "账号信息",
|
||||
"navBar.language": "多语言",
|
||||
"navBar.updateAvatar": "修改头像",
|
||||
"navBar.unbind": "解除微信绑定",
|
||||
"navBar.group": "设备分组",
|
||||
"navBar.running": "运行状态",
|
||||
"navBar.updateGroup": "编辑分组",
|
||||
"navBar.updateDevice": "设备修改",
|
||||
"navBar.eventLog": "事件日志",
|
||||
"navBar.orderLog": "指令日志",
|
||||
"navBar.player": "播放器",
|
||||
"common.fastbee": "芯程物联",
|
||||
"common.add": "添加",
|
||||
"common.delete": "删除",
|
||||
"common.save": "保存",
|
||||
"common.cancel": "取消",
|
||||
"common.confirm": "确认",
|
||||
"common.back": "返回",
|
||||
"common.edit": "编辑",
|
||||
"common.updateSuccessful": "编辑成功",
|
||||
"common.addSuccessful": "新增成功",
|
||||
"common.saveError": "保存失败",
|
||||
"common.saveSuccessful": "保存成功",
|
||||
"common.operation": "操作成功",
|
||||
"common.search": "搜索",
|
||||
"common.tips": "提示",
|
||||
"common.loginTips": "该功能需要登录后才可使用,是否去登录?",
|
||||
"login": {
|
||||
"accountLogin": "账号登录",
|
||||
"inputUserName": "请输入用户名",
|
||||
"inputPassword": "请输入密码",
|
||||
"inputCode": "请输入验证码",
|
||||
"login": "登录",
|
||||
"exampleAccount": "演示账号: fastbee 123456",
|
||||
"HuapuIoT": "华普物联",
|
||||
"registerNow": "现在注册",
|
||||
"readAndAgree": "已阅读并同意",
|
||||
"serviceAgreement": "服务协议",
|
||||
"and": "与",
|
||||
"privacyPolicy": "隐私政策",
|
||||
"childProtectionStatement": "儿童隐私保护声明",
|
||||
"commonBill": "第三方共享与SDK清单",
|
||||
"readAndCheckTheAgreement": "请阅读并勾选协议",
|
||||
"accontMsg": "账号信息不能为空",
|
||||
"phoneLogin": "手机号快捷登录",
|
||||
"welcomeToLogin": "欢迎登录峰信物联",
|
||||
"phoneAuthorization": "手机授权登录",
|
||||
"remember": "记住密码",
|
||||
"other": "其他方式登录"
|
||||
},
|
||||
"register": {
|
||||
"userRegister": "用户注册",
|
||||
"inputUserName": "请输入用户名",
|
||||
"inputPhone": "请输入手机号码",
|
||||
"inputPassword": "请输入密码",
|
||||
"inputPasswordAgain": "请再次输入密码",
|
||||
"inputUserNameSize": "请输入用户名,长度大于4",
|
||||
"inputCode": "请输入验证码",
|
||||
"registration": "注册",
|
||||
"accountLogin": "已有账号登录",
|
||||
"congratulations": "恭喜你,您的账号",
|
||||
"registeredSuccessfully": "注册成功"
|
||||
},
|
||||
"smsLogin": {
|
||||
"smsLogin": "短信登录",
|
||||
"inputPhone": "请输入手机号码",
|
||||
"inputCode": "请输入验证码",
|
||||
"login": "登录",
|
||||
"HuapuIoT": "华普物联",
|
||||
"accountLogin": "账号登录",
|
||||
"obtainCode": "获取验证码",
|
||||
"correctPhone": "请输入正确的手机号码",
|
||||
"obtainSmsCode": "正在获取验证码",
|
||||
"codeSent": "验证码已发送",
|
||||
"sending": "倒计时结束在发送!"
|
||||
},
|
||||
"bindLogin": {
|
||||
"bindLogin": "账号绑定登录",
|
||||
"bindRegister": "注册绑定",
|
||||
"alert": "提示",
|
||||
"alertTitle": "账户不存在,您是否要注册新账户",
|
||||
"notRegister": "暂不注册",
|
||||
"confirmRegister": "确认注册",
|
||||
"accountNotNull": "账号不能为空",
|
||||
"bind": "绑 定"
|
||||
},
|
||||
"bindRegister": {
|
||||
"bindRegister": "绑 定 注 册",
|
||||
"incorrectPhone": "手机号码格式不正确",
|
||||
"inputPasswordSize": "请输入密码且长度大于",
|
||||
"enterPasswordsDiffer": "两次输入的密码不一致"
|
||||
},
|
||||
"user": {
|
||||
"personalCentre": "个人中心",
|
||||
"openIOT": "芯程物联",
|
||||
"addDevice": "添加设备",
|
||||
"addScene": "添加场景",
|
||||
"groupManagement": "分组管理",
|
||||
"platformMsg": "平台消息",
|
||||
"ordinaryMembers": "普通会员",
|
||||
"currentAccountAlert": "当前账号未开通会员",
|
||||
"openAtOnce": "立即开通",
|
||||
"account": "账号",
|
||||
"messages": "信息",
|
||||
"signOut": "注销账号",
|
||||
"about": "关于",
|
||||
"language": "语言设置",
|
||||
"appDownload": "app下载",
|
||||
"exitAccount": "退出登录",
|
||||
"confirmExit": "确认退出账号",
|
||||
"confirmSignOut": "账户注销确认",
|
||||
"signOutAlert": "账户注销后,所有信息将被清空,且无法恢复,您是否要注销?",
|
||||
"notWriteOff": "暂不注销",
|
||||
"confirmWriteOff": "确认注销",
|
||||
"confirmWxBind": "微信绑定确认",
|
||||
"wxBindAlertTitle": "您是否要绑定微信",
|
||||
"notBind": "暂不绑定",
|
||||
"confirmBind": "确认绑定",
|
||||
"saveSuccess": "保存成功",
|
||||
"occurError": "发生错误",
|
||||
"scanning": "扫码只支持App和小程序",
|
||||
"parseQrCode": "解析二维码后,找不到对应处理类型",
|
||||
"errorParseQRcode": "解析二维码错误,格式不正确",
|
||||
"language-01": "多语言",
|
||||
"updatePassword": "密码修改",
|
||||
"login": "登陆/注册",
|
||||
"visitor": "游客",
|
||||
"configuration": "独立组态",
|
||||
"notice": "该功能需要登录才可使用,是否去登录?"
|
||||
},
|
||||
"account": {
|
||||
"avatar": "用户头像",
|
||||
"nikeName": "用户昵称",
|
||||
"email": "电子邮箱",
|
||||
"phone": "电话号码",
|
||||
"createTime": "创建时间",
|
||||
"Ip": "IP",
|
||||
"save": "保存",
|
||||
"inputNickname": "请输入用户昵称",
|
||||
"inputEmail": "请输入电子邮箱",
|
||||
"inputPhone": "请输入电话号码",
|
||||
"inputCreatetime": "请输入创建时间",
|
||||
"inputIP": "请输入登录ip",
|
||||
"incorrectEmail": "邮箱号码格式不正确"
|
||||
},
|
||||
"home": {
|
||||
"netWork": "配网添加",
|
||||
"wifi-type": "通用于WIFI类型的设备",
|
||||
"qrCode": "扫码添加",
|
||||
"networksDevices": "适用于蜂窝网络/以太网类设备",
|
||||
"association": "关联添加",
|
||||
"supportBatchOperations": "适用于蜂窝网络/以太网类设备,并支持批量操作",
|
||||
"errorLoading": "加载失败",
|
||||
"serialNumber": "设备编号",
|
||||
"productName": "产品名称",
|
||||
"sureAdd": "确定添加吗",
|
||||
"notActive": "未激活",
|
||||
"disabled": "禁用",
|
||||
"onLine": "在线",
|
||||
"offline": "离线",
|
||||
"shadow": "影子",
|
||||
"all": "全部",
|
||||
"eventLog": "事件日志",
|
||||
"Instructionlog": "指令日志"
|
||||
},
|
||||
"about": {
|
||||
"about": "关于",
|
||||
"authorQQ": "作者企鹅:164770707",
|
||||
"authorWechat": "作者微信:QQ164770707",
|
||||
"group": "交流群:720136372,1073236354",
|
||||
"webSite": "官方网站",
|
||||
"SourceCode": "查看源码",
|
||||
"open": "开源物联网平台 Version 2.5.1",
|
||||
"message": "曲靖蜂信科技有限公司 | 华普物联 | 官网 fastbee.cn"
|
||||
},
|
||||
"deviceDetail": {
|
||||
"basicMsg": "基本信息",
|
||||
"deviceName": "设备名称",
|
||||
"position": "定位方式",
|
||||
"longitude": "设备经度",
|
||||
"latitude": "设备纬度",
|
||||
"address": "所在地址",
|
||||
"shadow": "设备影子",
|
||||
"disabledDevice": "禁用设备",
|
||||
"remark": "备注信息",
|
||||
"inputMsg": "请输入内容",
|
||||
"inputLongitude": "请输入设备经度",
|
||||
"inputLatitude": "请输入设备经度",
|
||||
"loadingFail": "加载失败",
|
||||
"veryGood": "极好",
|
||||
"excellent": "优",
|
||||
"good": "良",
|
||||
"average": "中",
|
||||
"poor": "差",
|
||||
"device": "设备",
|
||||
"timing": "定时",
|
||||
"journal": "日志",
|
||||
"static": "统计",
|
||||
"share": "分享",
|
||||
"preserve": "保存",
|
||||
"deviceStatus": "设备状态",
|
||||
"serialNumber": "设备编号",
|
||||
"belongingProducts": "所属产品",
|
||||
"firmwareVersion": "固件版本",
|
||||
"positionMethod": "定位方式",
|
||||
"equipmentSignal": "设备信号",
|
||||
"address": "所在地址",
|
||||
"networkAddress": "入网地址",
|
||||
"activationTime": "激活时间",
|
||||
"deleteDevice": "删除设备",
|
||||
"runningStatus": "运行状态",
|
||||
"monitor": "实时监测",
|
||||
"deviceDetail": "设备详情",
|
||||
"channel": "设备通道",
|
||||
"autoPosition": "自动定位",
|
||||
"devicePosition": "设备定位",
|
||||
"customLocation": "自定义位置",
|
||||
"deviceCheck": "设备名称不能为空",
|
||||
"positionCheck": "定位方式不能为空",
|
||||
"updateSuccess": "修改成功",
|
||||
"Overview": "设备概况",
|
||||
"Surveillance": "视频监控",
|
||||
"Alert": "告警记录",
|
||||
"scada": "组态",
|
||||
"emptyNull": "数据为空"
|
||||
},
|
||||
"timing": {
|
||||
"timingName": "请输入定时名称",
|
||||
"search": "搜索",
|
||||
"execute": "执行一次",
|
||||
"effortLoading": "努力加载中...",
|
||||
"loadMore": "点击我加载更多",
|
||||
"nothing": "实在没有了",
|
||||
"emptyNull": "数据为空",
|
||||
"all": "全部",
|
||||
"enable": "启用",
|
||||
"notEnabled": "未启用",
|
||||
"nosupported": "暂不支持微信小程序",
|
||||
"wap2": "请使用wap2app方式打包!"
|
||||
},
|
||||
"deviceStatic": {
|
||||
"selectSub": "请选择设备从机",
|
||||
"rangeTime": "开始时间 - 结束时间",
|
||||
"selectTime": "选择时间范围",
|
||||
"search": "查询"
|
||||
},
|
||||
"monitior": {
|
||||
"monitoringInterval": "监测间隔(ms) 和 次数:",
|
||||
"inputNumber": "输入间隔毫秒数",
|
||||
"inputTimes": " 输入次数",
|
||||
"stop": "停止",
|
||||
"monitior": "监测",
|
||||
"receivingData": "正在接收数据...",
|
||||
"MonitoringRange": "监测的间隔范围500-10000毫秒",
|
||||
"monitoringQuantity": " 监测数量范围1-300",
|
||||
"real-timeMonitor": "实时监测",
|
||||
"stopMonitor": "停止监测"
|
||||
},
|
||||
"scene": {
|
||||
"sceneLink": "场景联动",
|
||||
"inputSceneName": "请输入场景名称",
|
||||
"search": "搜索",
|
||||
"condition": "条件",
|
||||
"way": "方式",
|
||||
"task": "任务",
|
||||
"execute": "执行一次",
|
||||
"tryingToLoad": "努力加载中...",
|
||||
"gentlyPullUp": "轻轻上拉",
|
||||
"nothingLeft": "实在没有了",
|
||||
"emptyData": "数据为空",
|
||||
"administration": "管理",
|
||||
"alarm": "告警",
|
||||
"anyCondition": "任意条件",
|
||||
"allCondition": "所有条件",
|
||||
"notConditions": "不满足条件",
|
||||
"unknown": "未知",
|
||||
"serial": "串行",
|
||||
"parallel": "并行",
|
||||
"switchSuccessful": "切换成功",
|
||||
"switchFail": "切换失败",
|
||||
"seeMore": "查看更多 >"
|
||||
},
|
||||
"share": {
|
||||
"userDetail": "用户详情",
|
||||
"userMsg": "用户信息",
|
||||
"userId": "用户编号:",
|
||||
"userName": "用户名称:",
|
||||
"phoneNumber": "手机号码:",
|
||||
"userPermissions": "设置用户权限",
|
||||
"notRemark": "暂无备注",
|
||||
"remark": "备注信息",
|
||||
"confirmShare": "确认分享",
|
||||
"deviceUpgradation": "设备升级",
|
||||
"otaUpgradation": "设备OTA升级",
|
||||
"timing": "设备定时",
|
||||
"timedTaskExecution": "定时执行任务",
|
||||
"deviceLog": "设备日志",
|
||||
"contains": "包含事件日志和指令日志",
|
||||
"monitior": "实时监测",
|
||||
"afterMonitior": "下发实时监测指令后,图表实时显示设备上报数据",
|
||||
"monitioringStatic": "监测统计",
|
||||
"chartDisplay": "图表显示存储的历史监测数据",
|
||||
"deviceShareSuccess": "设备分享成功",
|
||||
"title": "系统提示",
|
||||
"alert": "确定删除当前用户吗?",
|
||||
"delete": "删除中...",
|
||||
"inputUserPhone": "请输入用户手机号码",
|
||||
"deviceShare": "设备分享",
|
||||
"query": "查 询",
|
||||
"sharedUsers": "已分享用户",
|
||||
"master": "主人",
|
||||
"share": "分享",
|
||||
"unable": "查询不到用户信息,或者该用户已经是设备用户",
|
||||
"correct": "请输入正确手机号码"
|
||||
},
|
||||
"sceneDetail": {
|
||||
"inputName": "请输入名称",
|
||||
"sceneState": "场景状态",
|
||||
"trigger": "触发器",
|
||||
"addConditions": "添加条件",
|
||||
"deviceNumber": "设备数量",
|
||||
"deviceOnline": "设备上线",
|
||||
"Equipment": "设备下线",
|
||||
"action": "执行动作",
|
||||
"addTask": "添加任务",
|
||||
"alertExecute": "告警执行",
|
||||
"moreSetting": "更多设置",
|
||||
"silentTimeAlert": "静默时间:在设定的时间范围内将不再重复执行;延时执行:延时不会存储,限制为90秒内。",
|
||||
"silentTime": "静默时间",
|
||||
"inputTime": "请输入静默时间",
|
||||
"minute": "分钟",
|
||||
"executionMethod": "执行方式",
|
||||
"serial": "串行",
|
||||
"parallel": "并行",
|
||||
"delayed ": "延时执行",
|
||||
"inputExtensionTime": "请输入延长时间 ",
|
||||
"seconds": "秒钟",
|
||||
"complete": "完成",
|
||||
"deviceTriggered": "设备触发",
|
||||
"productTriggering": "产品触发",
|
||||
"timedTrigger": "定时触发",
|
||||
"deviceExecution": "设备执行",
|
||||
"productExecution": "产品执行",
|
||||
"sceneValidate": "场景名称不能为空",
|
||||
"triggerValidate": "触发条件不能为空",
|
||||
"taskValidate": "执行任务不能为空",
|
||||
"tips": "温馨提示"
|
||||
},
|
||||
"product": {
|
||||
"inputDeviceName": "请输入设备名称",
|
||||
"next": "下一步",
|
||||
"selectThingModel": "物模型选择",
|
||||
"attribute": "属性",
|
||||
"function": "功能",
|
||||
"event": "事件",
|
||||
"selectDevice": "请选择设备",
|
||||
"selectThingsModels": "请选择物模型",
|
||||
"inputProduct": "请输入产品名称",
|
||||
"selectProducts": "请选择产品",
|
||||
"operator": "操作符",
|
||||
"selectOperator": "请选择操作符",
|
||||
"on": "开",
|
||||
"off": "关",
|
||||
"input": "请输入",
|
||||
"inputValue": "请设置对应值",
|
||||
"selectValue": "请选择值",
|
||||
"timedName": "定时名称不能为空",
|
||||
"taskExecution": "执行任务不能为空"
|
||||
},
|
||||
"sceneTiming": {
|
||||
"time": "时间",
|
||||
"selectTime": "请选择时间",
|
||||
"again": "重复",
|
||||
"selectWeek": "请选择星期",
|
||||
"default": "默认",
|
||||
"auto": "自定义",
|
||||
"inputCRON": "请输入CRON",
|
||||
"expression": "生成表达式",
|
||||
"monday": "周一",
|
||||
"tuesday": "周二",
|
||||
"wednesday": "周三",
|
||||
"thursday": "周四",
|
||||
"friday": "周五",
|
||||
"saturday": "周六",
|
||||
"sunday": "周天",
|
||||
"everyDay": "每天",
|
||||
"status": "定时状态",
|
||||
"trigger": "触发器有且只有一个定时,执行动作中的告警无效",
|
||||
"alarm": "告警执行",
|
||||
"develop": "开发中...",
|
||||
"select": "请选择告警"
|
||||
},
|
||||
"alert": {
|
||||
"inputAlertName": "请输入告警名称",
|
||||
"all": "全部",
|
||||
"pending": "待处理",
|
||||
"untreated": "无需处理",
|
||||
"processed": "已处理",
|
||||
"notice": "提醒通知",
|
||||
"minor": "轻微问题",
|
||||
"warning": "严重警告",
|
||||
"alertName": "告警名称",
|
||||
"processState": "处理状态",
|
||||
"data": "数据",
|
||||
"alertTime": "告警时间",
|
||||
"process": "处理",
|
||||
"alertInformation": "告警信息",
|
||||
"serialNumber": "设备编号",
|
||||
"deviceName": "设备名称",
|
||||
"alertType": "告警级别",
|
||||
"alertProcess": "告警处理",
|
||||
"processAlert": "处理告警",
|
||||
"processResult": "处理结果",
|
||||
"processTime": "处理时间",
|
||||
"inputProcessMsg": "请输入处理内容,可不填"
|
||||
},
|
||||
"status": {
|
||||
"deviceVersion": "设备版本",
|
||||
"deviceFirmware": "设备固件升级",
|
||||
"grade": "升级",
|
||||
"noNeedToUpgrade": "已经是最新版本,不需要升级",
|
||||
"upgraded": "有新版本可以升级",
|
||||
"name": "名称:",
|
||||
"version": "版本:",
|
||||
"description": "描述:",
|
||||
"inputString": "请输入字符串",
|
||||
"unit": "单位",
|
||||
"send": "发送",
|
||||
"decimals": "请输入小数",
|
||||
"integer": "请输入整数",
|
||||
"monitior": "监测数据",
|
||||
"offline": "设备离线时状态",
|
||||
"service": "服务调用成功",
|
||||
"equip": "设备升级",
|
||||
"online": "设备在线",
|
||||
"shadow": "影子模式",
|
||||
"deviceOffline": "设备离线",
|
||||
"control": "设备控制",
|
||||
"stateModel": "状态模式",
|
||||
"dataModel": "数据模式",
|
||||
"devDetail": "设备详情"
|
||||
},
|
||||
"deviceAdd": {
|
||||
"addDevice": "添加设备",
|
||||
"wifi": "WIFI名称",
|
||||
"inputWifiName": "请输入WIFI名称",
|
||||
"passsword": "WIFI密码",
|
||||
"inputWifiPassword": "请输入WIFI密码",
|
||||
"remember": "记住密码",
|
||||
"senior": "高级",
|
||||
"userId": "用户编号",
|
||||
"inputUserId": "请输入用户编号",
|
||||
"deviceNum": "设备编号",
|
||||
"inputDeviceNum": "请输入设备编号",
|
||||
"authorization": "授 权 码",
|
||||
"inputAuthor": "请输入授权码",
|
||||
"supplementary": "补充信息",
|
||||
"inputSupplementary": "请输入补充信息",
|
||||
"step1": "第一步: 填写WIFI信息",
|
||||
"step2": "第二步: 设备进入配网模式",
|
||||
"step3": "第三步: 配网",
|
||||
"single": "单设备",
|
||||
"multiple": "多设备",
|
||||
"networkMode": "设备进入配网模式",
|
||||
"wifiHotspot": "设备会启动WIFI热点",
|
||||
"manually": "手动连接设备热点",
|
||||
"mobile": "手机连接设备WIFI热点",
|
||||
"detection": "检测设备",
|
||||
"system": "系统自动检测",
|
||||
"end": "配网结束",
|
||||
"reconnected": "手机可重新连接WIFI",
|
||||
"startWork": "开始配网",
|
||||
"redistributingNetwork": "重新配网",
|
||||
"after": "手机重连Wifi后, 返回查看",
|
||||
"tip": "提示:多设备配网目前只支持微信小程序, 需要启用手机Wifi开关 ",
|
||||
"noData": "暂无更多数据",
|
||||
"select": "请选择设备热点",
|
||||
"refresh": "刷新",
|
||||
"start": "开始配网",
|
||||
"distribution": "配网中...",
|
||||
"connect": "正在连接网络...",
|
||||
"return": "返回查看",
|
||||
"noDevice": "未检测到设备",
|
||||
"wifiName": "WIFI名称不能为空",
|
||||
"wifiPassword": "WIFI密码不能为空",
|
||||
"userNum": "用户编号不能为空",
|
||||
"deviceDetected": "已检测到设备",
|
||||
"userIdAndWifiAccount": "用户编号和WIFI账号密码不能为空",
|
||||
"sendInformation": "发送配置信息...",
|
||||
"successNetwork": "配网成功,如果设备没有正常连接,请检查WIFI信息是否正确以及网络状况",
|
||||
"fail": "配网失败,请确认设备进入配网模式,并连接了该热点",
|
||||
"selectNetwork": "请选择设备热点",
|
||||
"successDistribution": "配网成功",
|
||||
"afterNetwork": "配网后,请连接上网用的Wifi",
|
||||
"failNetwork": "配网失败",
|
||||
"phoneTurnOn": "请确保手机Wifi已打开,然后重新进入页面",
|
||||
"prepare": "准备配网",
|
||||
"miniProgrem": "启动Wifi模块失败,请重新打开小程序"
|
||||
},
|
||||
"linkDevice": {
|
||||
"linkDevice": "关联设备",
|
||||
"scan": "扫码识别",
|
||||
"deviceNum": "设备编号",
|
||||
"productNum": "产品编号",
|
||||
"productName": "产品名称",
|
||||
"userName": "用户名不能为空",
|
||||
"deviceEmpty": "设备编号不能为空",
|
||||
"productIdEmpty": "产品ID不能为空 ",
|
||||
"format": "解析二维码,格式不正确",
|
||||
"inputDeviceId": "请输入设备编号",
|
||||
"inputProductId": "请输入产品编号",
|
||||
"product": "产品名称可不填"
|
||||
},
|
||||
"group": {
|
||||
"equipment": "设备分组",
|
||||
"add": "添加设备",
|
||||
"detail": "详情",
|
||||
"select": "选择设备",
|
||||
"update": "设备更新成功",
|
||||
"name": "名称",
|
||||
"inputName": "请输入分组名称",
|
||||
"sort": "排序",
|
||||
"inputSort": "请输入分组排序",
|
||||
"remark": "备注",
|
||||
"content": "请输入内容",
|
||||
"groupName": "分组名称不能为空",
|
||||
"serialNumber": "序号不能为空",
|
||||
"updateGroup": "修改分组",
|
||||
"submit": "提交中...",
|
||||
"system": "系统提示",
|
||||
"delete": "确定删除当前分组吗?",
|
||||
"deleting": "删除中...",
|
||||
"inputContent": "请输入关键字",
|
||||
"nomore": "没有更多了",
|
||||
"confirm": "确认",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"log": {
|
||||
"all": "全部",
|
||||
"function": "功能调用",
|
||||
"attribute": "属性上报",
|
||||
"event": "事件上报",
|
||||
"online": "设备上线",
|
||||
"offline": "设备离线",
|
||||
"upgrade": "设备升级",
|
||||
"element": "元素",
|
||||
"service": "服务下发",
|
||||
"acquisition": "属性获取",
|
||||
"ota": "OTA升级"
|
||||
},
|
||||
"modbus": {
|
||||
"deviceName": "设备名",
|
||||
"firmware": "固件版本",
|
||||
"inputDeviceName": "请填写设备名",
|
||||
"inputVersion": "请填写固件版本",
|
||||
"time": "时间",
|
||||
"range": "数据范围",
|
||||
"unknown": "未知"
|
||||
},
|
||||
"message": {
|
||||
"message": "消息",
|
||||
"inform": "通知",
|
||||
"notice": "公告",
|
||||
"noticeDetail": "公告详情",
|
||||
"noContent": "暂无内容...",
|
||||
"config": "设备配置",
|
||||
"NTP": " NTP地址",
|
||||
"productConfig": "产品配置",
|
||||
"productPassword": "产品秘钥",
|
||||
"mqttConfig": "Mqtt配置",
|
||||
"mqttAddress": "Mqtt地址",
|
||||
"mqttAccount": " Mqtt账号",
|
||||
"mqttPassword": " Mqtt密码",
|
||||
"save": "保存配置",
|
||||
"report": "上报属性",
|
||||
"reportfunction": "上报功能",
|
||||
"event": "上报事件",
|
||||
"abtainTime": "获取时间",
|
||||
"monitior": "上报监测数据",
|
||||
"mobile": "手机监控",
|
||||
"function": "功能",
|
||||
"call": "拨打电话",
|
||||
"bright": "屏幕亮度",
|
||||
"vibration": "震动",
|
||||
"bluetooth": "蓝牙",
|
||||
"photo": "拍照",
|
||||
"record": "录音",
|
||||
"videos": "视频",
|
||||
"attribute": "属性",
|
||||
"information": "设备信息",
|
||||
"pointer": "加速指针",
|
||||
"gyroscope": "陀螺仪",
|
||||
"compass": "罗盘",
|
||||
"memory": "内存不足",
|
||||
"network": "网络变化",
|
||||
"screenshot": "用户截屏",
|
||||
"setting": "系统设置",
|
||||
"system": "系统配置",
|
||||
"address": "服务端地址",
|
||||
"text": "测试",
|
||||
"statusReset": "设备状态重置",
|
||||
"reset": "重置",
|
||||
"mqttReconnection": "Mqtt重连",
|
||||
"reconnection": "重连"
|
||||
},
|
||||
"avatar": {
|
||||
"cropping": "裁剪",
|
||||
"album": "相册",
|
||||
"relieve": "解 除"
|
||||
},
|
||||
"player": {
|
||||
"stream": "设备直播",
|
||||
"replay": "录像回放",
|
||||
"play": "播放",
|
||||
"suspend": "暂停",
|
||||
"enlarge": "放大",
|
||||
"narrow": "缩小",
|
||||
"recording": "选择录像日期",
|
||||
"storage": "设备存储",
|
||||
"screenshot": "截图",
|
||||
"channel": "设备通道",
|
||||
"loading": "加载中...",
|
||||
"noRecording": "当前通道没有录像",
|
||||
"purchase": "商用版请购买授权,加载中",
|
||||
"monitior": "监控详情"
|
||||
},
|
||||
"waitLogin": {
|
||||
"loginOrregister": "登录/注册",
|
||||
"ExperienceAccountLogin": "体验账号登录",
|
||||
"agree": "我已阅读并同意",
|
||||
"privacy": "隐私政策",
|
||||
"agreement": "用户协议",
|
||||
"children": "儿童隐私保护申明",
|
||||
"third": "第三方共享与SDK清单"
|
||||
},
|
||||
"indeScada": {
|
||||
"inputScada": "请输入组态名称",
|
||||
"search": "搜索",
|
||||
"tryingToLoad": "努力加载中...",
|
||||
"gentlyPullUp": "轻轻上拉",
|
||||
"nothingLeft": "实在没有了",
|
||||
"emptyData": "数据为空"
|
||||
},
|
||||
"deviceHistory": {
|
||||
"lastTwoHours": "最近2小时",
|
||||
"lastOneDay": "最近1天",
|
||||
"lastThirtyDay": "最近30天",
|
||||
"custom": "自定义",
|
||||
"inputDate": "起始时间",
|
||||
"cancel": "取消",
|
||||
"nextStep": "下一步",
|
||||
"confirm": "确定",
|
||||
"filtrate": "筛选",
|
||||
"variable": "变量",
|
||||
"updateTime": "更新时间",
|
||||
"emptyTable": "暂无更多数据",
|
||||
"emptyData": "暂无数据"
|
||||
}
|
||||
}
|
55
main.js
Normal file
55
main.js
Normal file
@ -0,0 +1,55 @@
|
||||
import Vue from 'vue';
|
||||
import App from './App';
|
||||
import store from '@/store';
|
||||
import * as filters from '@/common/filters.js';
|
||||
import mqttTool from '@/common/mqttTool.js';
|
||||
import bus from "@/common/bus.js"
|
||||
|
||||
Vue.prototype.$mqttTool = mqttTool;
|
||||
Vue.prototype.$bus = bus;
|
||||
|
||||
// 注入全局过滤器
|
||||
Object.keys(filters).forEach(key => {
|
||||
Vue.filter(key, filters[key])
|
||||
})
|
||||
|
||||
// 引入uview
|
||||
import uView from '@/uni_modules/uview-ui';
|
||||
Vue.use(uView);
|
||||
|
||||
// 全局引入vuex
|
||||
let vuexStore = require("@/store/$u.mixin.js");
|
||||
Vue.mixin(vuexStore);
|
||||
|
||||
// 引入扩展方法
|
||||
import '@/common/extend.js';
|
||||
Vue.config.productionTip = false
|
||||
|
||||
App.mpType = 'app'
|
||||
|
||||
import VueI18n from 'vue-i18n';
|
||||
import messages from '@/locale';
|
||||
Vue.use(VueI18n);
|
||||
const i18n = new VueI18n({
|
||||
locale: wx.getStorageSync('language') || 'zh-CN',
|
||||
messages, // 设置语言环境信息
|
||||
silentFallbackWarn: true,
|
||||
})
|
||||
Vue.prototype.$tt = Vue.prototype.$t; //设置别名,因为$t已经在项目中有使用
|
||||
const app = new Vue({
|
||||
store,
|
||||
i18n,
|
||||
...App
|
||||
})
|
||||
// http拦截器,将此部分放在new Vue()和app.$mount()之间,才能App.vue中正常使用
|
||||
import httpInterceptor from '@/apis/http.interceptor.js'
|
||||
Vue.use(httpInterceptor, app)
|
||||
|
||||
// http接口API集中管理引入部分
|
||||
import httpApi from '@/apis/http.api.js'
|
||||
Vue.use(httpApi, app)
|
||||
|
||||
import tools from '@/common/tools.js';
|
||||
Vue.use(tools, app)
|
||||
|
||||
app.$mount()
|
182
manifest.json
Normal file
182
manifest.json
Normal file
@ -0,0 +1,182 @@
|
||||
{
|
||||
"name" : "芯程物联",
|
||||
"appid" : "__UNI__BEE3050",
|
||||
"description" : "开源物联网平台",
|
||||
"versionName" : "2.2.0",
|
||||
"versionCode" : 200,
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
"kernel" : {
|
||||
"ios" : "WKWebview"
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"modules" : {
|
||||
"Barcode" : {},
|
||||
"OAuth" : {},
|
||||
"Camera" : {}
|
||||
},
|
||||
"distribute" : {
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>"
|
||||
],
|
||||
"minSdkVersion" : 22,
|
||||
"targetSdkVersion" : 30,
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a" ],
|
||||
"permissionExternalStorage" : {
|
||||
"request" : "none",
|
||||
"prompt" : "应用保存运行状态等信息,需要获取读写手机存储权限,请允许。"
|
||||
},
|
||||
"permissionPhoneState" : {
|
||||
"request" : "none",
|
||||
"prompt" : "为保证您正常、安全地使用,需要获取设备识别码使用权限,请允许。"
|
||||
}
|
||||
},
|
||||
"ios" : {
|
||||
"idfa" : true,
|
||||
"privacyDescription" : {
|
||||
"NSLocationAlwaysUsageDescription" : "便于您使用该功能获取当前位置天气情况、WIFI列表等场景。",
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription" : "便于您使用该功能获取当前位置天气情况、WIFI列表等场景。",
|
||||
"NSLocalNetworkUsageDescription" : "允许访问蜂窝网络,用于扫码/关联式添加设备",
|
||||
"NSLocationWhenInUseUsageDescription" : "便于您使用该功能获取当前位置天气情况、WIFI列表等场景。"
|
||||
},
|
||||
"dSYMs" : false
|
||||
},
|
||||
"sdkConfigs" : {
|
||||
"push" : {},
|
||||
"statics" : {},
|
||||
"maps" : {},
|
||||
"ad" : {},
|
||||
"oauth" : {
|
||||
"weixin" : {
|
||||
"appid" : "wx6be3f0d7bf7154e1",
|
||||
"appsecret" : "b6c1d0da60bd5250857d211cdc64fdc9",
|
||||
"UniversalLinks" : ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"splashscreen" : {
|
||||
"iosStyle" : "common",
|
||||
"androidStyle" : "common",
|
||||
"alwaysShowBeforeRender" : false,
|
||||
"waiting" : true,
|
||||
"autoclose" : false,
|
||||
"delay" : 0,
|
||||
"android" : {
|
||||
"hdpi" : "./static/logo.9.png",
|
||||
"xhdpi" : "./static/logo.9.png",
|
||||
"xxhdpi" : "./static/logo.9.png"
|
||||
}
|
||||
},
|
||||
"icons" : {
|
||||
"android" : {
|
||||
"hdpi" : "unpackage/res/icons/72x72.png",
|
||||
"xhdpi" : "unpackage/res/icons/96x96.png",
|
||||
"xxhdpi" : "unpackage/res/icons/144x144.png",
|
||||
"xxxhdpi" : "unpackage/res/icons/192x192.png"
|
||||
},
|
||||
"ios" : {
|
||||
"appstore" : "unpackage/res/icons/1024x1024.png",
|
||||
"ipad" : {
|
||||
"app" : "unpackage/res/icons/76x76.png",
|
||||
"app@2x" : "unpackage/res/icons/152x152.png",
|
||||
"notification" : "unpackage/res/icons/20x20.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"proapp@2x" : "unpackage/res/icons/167x167.png",
|
||||
"settings" : "unpackage/res/icons/29x29.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"spotlight" : "unpackage/res/icons/40x40.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png"
|
||||
},
|
||||
"iphone" : {
|
||||
"app@2x" : "unpackage/res/icons/120x120.png",
|
||||
"app@3x" : "unpackage/res/icons/180x180.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"notification@3x" : "unpackage/res/icons/60x60.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"settings@3x" : "unpackage/res/icons/87x87.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png",
|
||||
"spotlight@3x" : "unpackage/res/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
},
|
||||
"nativePlugins" : {}
|
||||
},
|
||||
"quickapp" : {},
|
||||
"mp-weixin" : {
|
||||
"appid" : "wx5bfbadf52adc17f3",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"minified" : true
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
},
|
||||
"optimization" : {
|
||||
"subPackages" : true
|
||||
},
|
||||
"permission" : {
|
||||
"scope.userLocation" : {
|
||||
"desc" : "便于您使用该功能获取当前位置天气情况、WIFI列表等场景。"
|
||||
}
|
||||
},
|
||||
"requiredPrivateInfos" : [ "getLocation" ],
|
||||
"lazyCodeLoading" : "requiredComponents"
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
},
|
||||
"h5" : {
|
||||
"title" : "芯程物联",
|
||||
"router" : {
|
||||
"mode" : "hash",
|
||||
"base" : "./"
|
||||
},
|
||||
"devServer" : {
|
||||
"port" : 8090
|
||||
},
|
||||
"optimization" : {
|
||||
"treeShaking" : {
|
||||
"enable" : false
|
||||
}
|
||||
},
|
||||
"template" : "uni_modules/jessibuca/hybrid/index.html",
|
||||
"sdkConfigs" : {
|
||||
"maps" : {
|
||||
"qqmap" : {
|
||||
"key" : "4PDBZ-4KQKU-AX6VO-GU7NB-INDZJ-YBFXC"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
package.json
Normal file
28
package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "xcwl-app",
|
||||
"version": "2.5.1",
|
||||
"description": "手机端,可用于中小企业快速搭建物联网系统,个人学习搭建智能家居平台;适用于智能家居、智慧办公、智慧社区、农业监测、工业控制等",
|
||||
"homepage": "https://fastbee.cn",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"jsencrypt": "^3.3.2",
|
||||
"moment": "^2.30.1",
|
||||
"mqtt": "^3.0.0",
|
||||
"vue-i18n": "^9.13.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.wumei.live/ultimate/wumei-smart-app.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fastbee"
|
||||
],
|
||||
"author": "fastbee",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"code-inspector-plugin": "^0.18.0"
|
||||
}
|
||||
}
|
678
pages.json
Normal file
678
pages.json
Normal file
@ -0,0 +1,678 @@
|
||||
{
|
||||
"easycom": {
|
||||
// uview 组件
|
||||
"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue",
|
||||
// 自定义组件
|
||||
"^cl-(.*)": "@/components/cl-$1/index.vue"
|
||||
},
|
||||
|
||||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
|
||||
{
|
||||
"path": "pages/tabBar/home/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.home%",
|
||||
"enablePullDownRefresh": true,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabBar/user/user",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.user%",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.login%",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "pages/tabBar/scene/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.scene%",
|
||||
"enablePullDownRefresh": true
|
||||
},
|
||||
"meta": {
|
||||
"requireAuth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabBar/alert/index",
|
||||
"meta": {
|
||||
"title": "%navBar.alert%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.alert%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabBar/alert/edit",
|
||||
"meta": {
|
||||
"title": "%navBar.alarmHandling%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.alarmHandling%",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabBar/trend/trend",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.news%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/webview/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.browse%"
|
||||
},
|
||||
"meta": {
|
||||
"requireAuth": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [{
|
||||
"root": "pagesA",
|
||||
"pages": [{
|
||||
"path": "scene/detail",
|
||||
"meta": {
|
||||
"title": "%navBar.sceneDetail%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.sceneDetail%",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "scene/list",
|
||||
"meta": {
|
||||
"title": "%navBar.productList%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.productList%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "scene/product/index",
|
||||
"meta": {
|
||||
"title": "%navBar.productList%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.productList%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "scene/product/device",
|
||||
"meta": {
|
||||
"title": "%navBar.deviceList%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.deviceList%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "scene/product/model",
|
||||
"meta": {
|
||||
"title": "%navBar.physicalModels%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.physicalModels%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "scene/warning/index",
|
||||
"meta": {
|
||||
"title": "%navBar.alarmList%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.alarmList%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "scene/timing/index",
|
||||
"meta": {
|
||||
"title": "%navBar.timer%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.timer%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/index",
|
||||
"meta": {
|
||||
"title": "%navBar.deviceDetails%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.deviceDetails%",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/timing/list",
|
||||
"meta": {
|
||||
"title": "%navBar.timedList%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.timedList%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/timing/detail",
|
||||
"meta": {
|
||||
"title": "%navBar.timedDetails%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.timedDetails%",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/model",
|
||||
"meta": {
|
||||
"title": "%navBar.physicalModels%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.physicalModels%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/detail/index",
|
||||
"meta": {
|
||||
"title": "%navBar.deviceDetails%",
|
||||
"requireAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.deviceDetails%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/trend/trendDetail",
|
||||
"meta": {
|
||||
"title": "%navBar.dynamicDetails%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.dynamicDetails%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/trend/categoryTrend",
|
||||
"meta": {
|
||||
"title": "%navBar.classification%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.classification%",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/home/deviceAdd",
|
||||
"meta": {
|
||||
"title": "%navBar.addDevice%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.addDevice%",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/home/deviceRelate",
|
||||
"meta": {
|
||||
"title": "%navBar.associatedDevices%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.associatedDevices%",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "device/share/list",
|
||||
"meta": {
|
||||
"title": "%navBar.deviceSharing%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.deviceSharing%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "device/share/detail",
|
||||
"meta": {
|
||||
"title": "%navBar.shareDetails%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.shareDetails%",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"root": "pagesB",
|
||||
"pages": [{
|
||||
"path": "user/about",
|
||||
"meta": {
|
||||
"title": "%navBar.about%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.about%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/user/message",
|
||||
"meta": {
|
||||
"title": "%navBar.latest%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.latest%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/user/messageDetail",
|
||||
"meta": {
|
||||
"title": "%navBar.newsDetail%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.newsDetail%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/user/setting",
|
||||
"meta": {
|
||||
"title": "%navBar.setting%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.setting%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/user/emulator",
|
||||
"meta": {
|
||||
"title": "%navBar.simulation%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.simulation%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list/user/phone",
|
||||
"meta": {
|
||||
"title": "%navBar.mobile%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.mobile%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/account",
|
||||
"meta": {
|
||||
"title": "%navBar.account%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.account%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "language/index",
|
||||
"meta": {
|
||||
"title": "%navBar.language%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.language%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/scada/indeScada",
|
||||
"meta": {
|
||||
"title": "独立组态"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "独立组态",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/resetPsd",
|
||||
"meta": {
|
||||
"title": "密码修改"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "密码修改",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/avatar",
|
||||
"meta": {
|
||||
"title": "%navBar.updateAvatar%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.updateAvatar%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/secureBind",
|
||||
"meta": {
|
||||
"title": "%navBar.unbind%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.unbind%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/deviceGroup/index",
|
||||
"meta": {
|
||||
"title": "%navBar.group%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.group%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/deviceGroup/detail",
|
||||
"meta": {
|
||||
"title": "%navBar.updateGroup%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.updateGroup%",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user/deviceGroup/devices",
|
||||
"meta": {
|
||||
"title": "%navBar.deviceList%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.deviceList%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/status/modbus/index",
|
||||
"meta": {
|
||||
"title": "%navBar.running%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.running%",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/status/modbus/edit",
|
||||
"meta": {
|
||||
"title": "%navBar.updateDevice%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.updateDevice%",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/log/event",
|
||||
"meta": {
|
||||
"title": "%navBar.eventLog%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.eventLog%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "home/device/log/order",
|
||||
"meta": {
|
||||
"title": "%navBar.orderLog%"
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.orderLog%",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "login/register",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.registration%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "login/bindLogin",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.loginBinding%",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "login/bindRegister",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.registrationBinding%",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "login/smsLogin",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.smsBogin%",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "login/firstOpen",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "login/waitLogin",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"root": "pages_player", //分包所在路径
|
||||
"pages": [ //页面数组
|
||||
{
|
||||
"path": "list/devicePlayer",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.player%", //页面头部标题
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
"path": "list/devicePlayerApp",
|
||||
"style": {
|
||||
"navigationBarTitleText": "%navBar.player%", //页面头部标题
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"bounce": "none" // 将回弹属性关掉
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}],
|
||||
"globalStyle": {
|
||||
"pageOrientation": "portrait",
|
||||
"navigationBarTitleText": "%common.fastbee%",
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarBackgroundColor": "#007AFF",
|
||||
"backgroundColor": "#F8F8F8",
|
||||
"backgroundColorTop": "#F4F5F6",
|
||||
"backgroundColorBottom": "#F4F5F6",
|
||||
"onReachBottomDistance": 50
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#7A7E83",
|
||||
"selectedColor": "#007AFF",
|
||||
"borderStyle": "black",
|
||||
"backgroundColor": "#F8F8F8",
|
||||
"list": [{
|
||||
"pagePath": "pages/tabBar/home/index",
|
||||
"iconPath": "static/home.png",
|
||||
"selectedIconPath": "static/home_active.png",
|
||||
// "text": "%navBar.home%"
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/tabBar/scene/index",
|
||||
"iconPath": "static/scene.png",
|
||||
"selectedIconPath": "static/scene_active.png",
|
||||
// "text": "%navBar.scene%"
|
||||
"text": "场景"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/tabBar/alert/index",
|
||||
"iconPath": "static/alert.png",
|
||||
"selectedIconPath": "static/alert_active.png",
|
||||
// "text": "%navBar.alert%"
|
||||
"text": "告警"
|
||||
},
|
||||
// {
|
||||
// "pagePath": "pages/tabBar/trend/trend",
|
||||
// "iconPath": "static/trend.png",
|
||||
// "selectedIconPath": "static/trend_active.png",
|
||||
// "text": "%navBar.news%"
|
||||
// },
|
||||
{
|
||||
"pagePath": "pages/tabBar/user/user",
|
||||
"iconPath": "static/user.png",
|
||||
"selectedIconPath": "static/user_active.png",
|
||||
"text": "用户"
|
||||
// "text": "%navBar.user%"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
38
pages/common/webview/index.vue
Normal file
38
pages/common/webview/index.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<view v-if="params.url">
|
||||
<web-view :src="`${params.url}`"></web-view>
|
||||
<u-loading-page :loading="loading"></u-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
params: {}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
src: {
|
||||
type: [String],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
onLoad (event) {
|
||||
const { url, ...res } = event;
|
||||
this.params = {
|
||||
url: decodeURIComponent(url),
|
||||
res
|
||||
};
|
||||
if (event.title) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: event.title
|
||||
})
|
||||
};
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
}, 1000);
|
||||
},
|
||||
}
|
||||
</script>
|
561
pages/login/index.vue
Normal file
561
pages/login/index.vue
Normal file
@ -0,0 +1,561 @@
|
||||
<template>
|
||||
<view class="login-wrap">
|
||||
<view class="top-wrap">
|
||||
<image src="../../static/538.png" mode="widthFix" style="width: 500rpx;"></image>
|
||||
</view>
|
||||
<view class="main-wrap">
|
||||
<u--form :model="loginForm" :rules="rules" ref="form" labelWidth="31">
|
||||
<u-form-item prop="username" borderBottom>
|
||||
<uni-easyinput v-model="loginForm.username" clearable :inputBorder="false"
|
||||
:placeholder="$tt('login.inputUserName')" prefixIcon="person"
|
||||
prefixIconStyle="font-size: 44rpx; margin-right: 10rpx"></uni-easyinput>
|
||||
</u-form-item>
|
||||
<u-form-item prop="password" borderBottom>
|
||||
<uni-easyinput prefixIcon="locked" prefixIconStyle="font-size: 44rpx; margin-right: 10rpx"
|
||||
type="password" :inputBorder="false" v-model="loginForm.password"
|
||||
:placeholder="$tt('login.inputPassword')"></uni-easyinput>
|
||||
</u-form-item>
|
||||
<u-form-item prop="code" borderBottom>
|
||||
<!-- 注意:由于兼容性差异,如果需要使用前后插槽,nvue下需使用u--input,非nvue下需使用u-input -->
|
||||
<!-- 验证码 -->
|
||||
<uni-easyinput :placeholder="$tt('login.inputCode')" v-model="loginForm.code" :inputBorder="false"
|
||||
prefixIcon="checkbox" prefixIconStyle="font-size: 44rpx; margin-right: 10rpx">
|
||||
<template slot="right">
|
||||
<u--image :src="codeUrl" width="240rpx" height="60rpx" @click="getCode"></u--image>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</u-form-item>
|
||||
<!-- 记住密码 -->
|
||||
<view prop="rememberMe" style="display: flex; justify-content:flex-end; padding: 20rpx;">
|
||||
<view style="font-size: 13px;">
|
||||
<u-checkbox-group @change="handleRememberCheckbox">
|
||||
<label>
|
||||
<u-checkbox :checked="loginForm.rememberMe" style="transform:scale(0.8)" />
|
||||
</label>
|
||||
<text>
|
||||
{{$tt('login.remember')}}
|
||||
</text>
|
||||
</u-checkbox-group>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view style="margin-top: 50rpx;">
|
||||
<u-button style="height: 90rpx; width: 650rpx;" shape="circle" type="primary"
|
||||
:text="$tt('login.login')" @click="handleLogin()"></u-button>
|
||||
</view>
|
||||
</u--form>
|
||||
|
||||
<view class="footer-wrap">
|
||||
<view class="tipbox">
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN -->
|
||||
<view class="txt">
|
||||
—— {{$tt('login.other')}} ——
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<view class="otherUser">
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<uni-icons style="background-color: #e7e7e7; padding: 10rpx; border-radius: 50%; margin: 20rpx;"
|
||||
type="weixin" size="24" color="#3c9cff" @click="handleWeChatLogin()"></uni-icons>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<uni-icons style="background-color: #e7e7e7; padding: 10rpx; border-radius: 50%; margin: 20rpx;"
|
||||
type="phone-filled" size="24" color="#3c9cff"
|
||||
@click="handleWeChatOneClickLogin"></uni-icons>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="other-wrap">
|
||||
<!-- 微信授权弹出框 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="one-click-login-pop-wrap">
|
||||
<u-popup :show="isShowPop" mode="bottom" :round="10" closeable="true" @close="isShowPop = false">
|
||||
<view class="content-wrap">
|
||||
<u--image :showLoading="true" src="/static/logo_title.png" width="260rpx" height="90rpx"
|
||||
customStyle="float:left"></u--image>
|
||||
<text class="title">{{$tt("login.welcomeToLogin")}}</text>
|
||||
<view class="btn-login">
|
||||
<u-button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber"
|
||||
@click="isShowPop = false;"
|
||||
type="success">{{$tt("login.phoneAuthorization")}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
import projectConfig from '@/env.config.js';
|
||||
import { getProfile } from '@/apis/modules/common.js'
|
||||
import { encrypt, decrypt } from '@/utils/jsencrypt.js'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
token: '',
|
||||
codeUrl: '',
|
||||
isClause: false,
|
||||
show: false,
|
||||
systemLocale: '',
|
||||
applicationLocale: '',
|
||||
columns: [
|
||||
[{
|
||||
text: this.$tt('locale.en-US'),
|
||||
code: 'en-US'
|
||||
},
|
||||
{
|
||||
text: this.$tt('locale.zh-CN'),
|
||||
code: 'zh-CN'
|
||||
},
|
||||
]
|
||||
],
|
||||
value: 'English',
|
||||
loginForm: {
|
||||
username: '',
|
||||
password: '',
|
||||
rememberMe: false,
|
||||
code: '',
|
||||
uuid: '',
|
||||
},
|
||||
rules: {
|
||||
username: {
|
||||
type: 'string',
|
||||
min: 2,
|
||||
max: 20,
|
||||
required: true,
|
||||
message: this.$tt('login.inputUserName'),
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
min: 5,
|
||||
max: 20,
|
||||
message: this.$tt('login.inputPassword'),
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
code: {
|
||||
type: 'integer',
|
||||
required: true,
|
||||
message: this.$tt('login.inputCode'),
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
},
|
||||
isShowPop: false,
|
||||
};
|
||||
},
|
||||
onLoad () {
|
||||
this.loadSelectedLanguage();
|
||||
},
|
||||
mounted () {
|
||||
this.getToken();
|
||||
if (this.token != '' && this.token != null) {
|
||||
// 跳转主页
|
||||
uni.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
});
|
||||
} else {
|
||||
this.getCode();
|
||||
this.getAccount();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadSelectedLanguage () {
|
||||
// 获取之前保存的语言设置
|
||||
const selectedLanguage = wx.getStorageSync('language');
|
||||
// 如果获取到了,使用该语言设置
|
||||
if (selectedLanguage === 'zh-CN') {
|
||||
this.applicationLocale = 'zh-CN';
|
||||
this.value = '简体中文';
|
||||
} else if (selectedLanguage === 'en-US') {
|
||||
this.applicationLocale = 'en-US';
|
||||
this.value = 'English';
|
||||
} else {
|
||||
// 如果没有获取到,使用默认设置
|
||||
this.applicationLocale = 'zh-CN'; // 默认设置为中文
|
||||
this.$i18n.locale = this.applicationLocale;
|
||||
this.value = '简体中文';
|
||||
}
|
||||
},
|
||||
onLocaleChange () {
|
||||
this.show = true;
|
||||
},
|
||||
close () {
|
||||
this.show = false
|
||||
},
|
||||
cancel () {
|
||||
this.show = false
|
||||
},
|
||||
confirm (e) {
|
||||
this.show = false;
|
||||
this.value = e.value[0].text;
|
||||
this.$i18n.locale = e.value[0].code;
|
||||
uni.setLocale(e.value[0].code);
|
||||
wx.setStorageSync('language', e.value[0].code)
|
||||
},
|
||||
change (e) {
|
||||
// console.log('change', e)
|
||||
},
|
||||
handleLogin () {
|
||||
this.$refs.form.validate().then(res => {
|
||||
// if (!this.isClause) {
|
||||
// uni.showToast({
|
||||
// icon: 'none',
|
||||
// title: this.$tt('login.readAndCheckTheAgreement'),
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// 调用登录
|
||||
this.$api.common.login(this.loginForm).then(async res => {
|
||||
if (res.code == 200) {
|
||||
// 存储token和账号
|
||||
this.saveToken(res.token);
|
||||
this.saveAccount();
|
||||
// 获取用户信息
|
||||
let profile = await this.getProfile();
|
||||
uni.$u.vuex('profile', profile);
|
||||
// 跳转主页
|
||||
uni.reLaunch({
|
||||
url: '/pages/tabBar/home/index'
|
||||
});
|
||||
} else {
|
||||
if (res.msg) {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: res.msg,
|
||||
complete: (res) => {
|
||||
setTimeout(() => {
|
||||
this.getCode();
|
||||
}, 1500);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}).catch(errors => {
|
||||
uni.$u.toast(this.$tt('login.accontMsg'));
|
||||
});
|
||||
},
|
||||
handletestLogin () {
|
||||
this.loginForm.username = "fastbee"
|
||||
this.loginForm.password = "123456"
|
||||
},
|
||||
// 微信登录
|
||||
handleWeChatLogin () {
|
||||
let that = this;
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
onlyAuthorize: false,
|
||||
success: function (loginRes) {
|
||||
if (loginRes) {
|
||||
console.log('用户授权成功');
|
||||
uni.request({
|
||||
url: projectConfig.baseUrl + '/wechat/mobileLogin',
|
||||
data: {
|
||||
//业务服务器通过code + 仅保存在服务器的appsecret参数,向:微信开放平台接口发起网络请求
|
||||
code: loginRes.code,
|
||||
accessToken: loginRes.authResult.access_token,
|
||||
expiresIn: loginRes.authResult.expires_in,
|
||||
refreshToken: loginRes.authResult.refresh_token,
|
||||
openId: loginRes.authResult.openid,
|
||||
unionId: loginRes.authResult.unionid,
|
||||
},
|
||||
method: 'post',
|
||||
success: async res => {
|
||||
if (res.data.code == 200) {
|
||||
if (res.data.data.token != null) {
|
||||
that.saveToken(res.data.data.token);
|
||||
// 获取用户信息
|
||||
let profile = await that.getProfile();
|
||||
uni.$u.vuex('profile', profile);
|
||||
// 跳转主页面
|
||||
wx.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
})
|
||||
} else {
|
||||
//跳转绑定页面
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/login/bindLogin?bindId=' +
|
||||
res
|
||||
.data.data.bindId,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
mask: true,
|
||||
title: res.data.msg,
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
},
|
||||
fail (err) {
|
||||
console.log(err)
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
fail (err) {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 微信一键登录
|
||||
handleWeChatOneClickLogin () {
|
||||
this.isShowPop = true;
|
||||
},
|
||||
// 获取验证码
|
||||
getCode () {
|
||||
this.$api.common.captchaImage(true).then(res => {
|
||||
this.codeUrl = 'data:image/gif;base64,' + res.img;
|
||||
this.loginForm.uuid = res.uuid;
|
||||
this.loginForm.code = '';
|
||||
})
|
||||
.catch(err => {
|
||||
this.$u.toast(err.msg);
|
||||
});
|
||||
},
|
||||
// 用户注册
|
||||
handleRegister () {
|
||||
uni.$u.route('/pagesB/login/register');
|
||||
},
|
||||
//短信登录
|
||||
gotoSmsLogin () {
|
||||
uni.$u.route('/pagesB/login/smsLogin');
|
||||
},
|
||||
// 获取用户信息
|
||||
getProfile () {
|
||||
return new Promise((resolve, reject) => {
|
||||
getProfile().then(res => {
|
||||
resolve(res.data);
|
||||
}).catch(err => {
|
||||
this.$u.toast(err.msg);
|
||||
})
|
||||
});
|
||||
},
|
||||
saveToken (token) {
|
||||
// 本地缓存存储token
|
||||
uni.setStorageSync('token', token);
|
||||
// vuex存储token
|
||||
uni.$u.vuex('vuex_token', token);
|
||||
},
|
||||
getToken () {
|
||||
// 本地缓存获取token
|
||||
this.token = uni.getStorageSync('token');
|
||||
// vuex存储token
|
||||
uni.$u.vuex('vuex_token', this.token);
|
||||
},
|
||||
// 本地缓存存储
|
||||
saveAccount () {
|
||||
if (this.loginForm.rememberMe) {
|
||||
uni.setStorageSync('username', this.loginForm.username);
|
||||
uni.setStorageSync('password', encrypt(this.loginForm.password));
|
||||
uni.setStorageSync('rememberMe', this.loginForm.rememberMe)
|
||||
} else {
|
||||
uni.removeStorageSync('username');
|
||||
uni.removeStorageSync('password');
|
||||
uni.removeStorageSync('rememberMe');
|
||||
}
|
||||
},
|
||||
// 本地缓存获取
|
||||
getAccount () {
|
||||
let username = uni.getStorageSync('username');
|
||||
let password = uni.getStorageSync('password');
|
||||
let rememberMe = uni.getStorageSync('rememberMe');
|
||||
if (username && username != '') {
|
||||
this.loginForm.username = username;
|
||||
}
|
||||
if (password && password != '') {
|
||||
this.loginForm.password = decrypt(password);
|
||||
}
|
||||
if (rememberMe != '') {
|
||||
this.loginForm.rememberMe = rememberMe;
|
||||
}
|
||||
},
|
||||
//微信小程序登录获取手机号
|
||||
getPhoneNumber (e) {
|
||||
let that = this;
|
||||
wx.login({
|
||||
success (res) {
|
||||
if (e.detail.code && res.code) {
|
||||
console.log('用户授权成功');
|
||||
//发起网络请求
|
||||
wx.request({
|
||||
url: projectConfig.baseUrl + '/wechat/miniLogin',
|
||||
data: {
|
||||
code: res.code,
|
||||
phoneCode: e.detail.code,
|
||||
},
|
||||
method: 'post',
|
||||
success: async res => {
|
||||
if (res.data.code == 200) {
|
||||
// 存储token和账号
|
||||
const token = wx.setStorageSync('token', res.data.data.token)
|
||||
that.saveToken(res.data.data.token);
|
||||
// 获取用户信息
|
||||
let profile = await that.getProfile();
|
||||
uni.$u.vuex('profile', profile);
|
||||
// 跳转主页
|
||||
wx.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
});
|
||||
} else {
|
||||
wx.showToast({
|
||||
icon: "none",
|
||||
mask: true,
|
||||
title: res.data.msg,
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
console.log('用户拒绝授权');
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 服务协议
|
||||
handleService () {
|
||||
let title = this.$tt('login.serviceAgreement');
|
||||
let url = projectConfig.officialWebUrl + 'service-agreement.html';
|
||||
uni.navigateTo({
|
||||
url: `/pages/common/webview/index?title=${title}&url=${url}`
|
||||
});
|
||||
},
|
||||
// 隐私政策
|
||||
handlePrivacy () {
|
||||
let title = this.$tt('login.privacyPolicy');
|
||||
let url = projectConfig.officialWebUrl + 'privacy-policy.html';
|
||||
uni.navigateTo({
|
||||
url: `/pages/common/webview/index?title=${title}&url=${url}`
|
||||
});
|
||||
},
|
||||
//记住密码
|
||||
handleRememberCheckbox (e) {
|
||||
this.loginForm.rememberMe = !this.loginForm.rememberMe
|
||||
},
|
||||
// 勾选协议
|
||||
handleClauseCheckbox (e) {
|
||||
this.isClause = !this.isClause;
|
||||
},
|
||||
openhpLink () {
|
||||
uni.navigateTo({
|
||||
url: '/pages/common/webview/index?url=http://www.hpiot.cn/'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.login-wrap {
|
||||
height: 100%;
|
||||
|
||||
.top-wrap {
|
||||
height: 510rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.main-wrap {
|
||||
padding: 0 40rpx;
|
||||
|
||||
.register-wrap {
|
||||
margin-top: 40rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
.now-btn {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-wrap {
|
||||
position: fixed;
|
||||
bottom: 60rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
.tipbox {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.otherUser {
|
||||
margin-top: 40rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.txt {
|
||||
font-size: 28rpx;
|
||||
color: #cbcbcb;
|
||||
}
|
||||
|
||||
.item-wrap {
|
||||
background-color: #eaeaea;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 10rpx 20rpx;
|
||||
}
|
||||
|
||||
.clause {
|
||||
font-size: 28rpx;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
.check_text {}
|
||||
|
||||
.service,
|
||||
.privacy {
|
||||
color: #3c9cff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other-wrap {
|
||||
.one-click-login-pop-wrap {
|
||||
.content-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 90rpx;
|
||||
|
||||
.title {
|
||||
margin-top: 30rpx;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
margin-top: 100rpx;
|
||||
margin-bottom: 60rpx;
|
||||
width: 320rpx;
|
||||
border-radius: 20rpx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
40
pages/public/richPage/richPage.vue
Normal file
40
pages/public/richPage/richPage.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<view>
|
||||
<u-parse :content="html" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
html: ""
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
console.log('this.$Route.query:', this.$Route.query);
|
||||
|
||||
let query = this.$Route.query;
|
||||
if (Object.keys(query).length > 0) {
|
||||
let ul = '<ul style="font-size:26px;color:#ffaa00;">';
|
||||
for (let key in query) {
|
||||
ul += `<li>
|
||||
<b>key</b>: <i>${key}</i>,
|
||||
<b>value:</b> <i>${query[key]}</i>
|
||||
</li>`;
|
||||
}
|
||||
ul += '</ul>';
|
||||
this.html = ul;
|
||||
} else {
|
||||
this.html = '<h1>什么参数也没有!</h1>';
|
||||
}
|
||||
|
||||
this.html += '<div></div>'
|
||||
console.log('this.html:',this.html);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
214
pages/tabBar/alert/edit.vue
Normal file
214
pages/tabBar/alert/edit.vue
Normal file
@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.alarmHandling')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
|
||||
<view class="alert-edit">
|
||||
<view class="alert-info">
|
||||
<view class="title">{{$tt('alert.alertInformation')}}</view>
|
||||
<u--form labelPosition="left" :model="model" labelWidth="90"
|
||||
:labelStyle="{ fontSize: '28rpx', color: '#8f9ca2' }">
|
||||
<u-form-item :label="$tt('alert.alertName')">
|
||||
{{ model.alertName }}
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('alert.serialNumber')">
|
||||
{{ model.serialNumber }}
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('alert.deviceName')">
|
||||
{{ model.deviceName }}
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('alert.alertType')">
|
||||
<text
|
||||
:class="{'success': model.alertLevel === 1, 'warning': model.alertLevel === 2, 'error': model.alertLevel === 3}">
|
||||
{{ getaLertLevelDisplay(model) }}
|
||||
</text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('alert.alertTime')">
|
||||
{{ model.createTime }}
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('alert.data')">
|
||||
<div v-html="formatDetail(model.detail)"></div>
|
||||
</u-form-item>
|
||||
</u--form>
|
||||
</view>
|
||||
<view class="alert-dispose" v-if="model.status !== 1">
|
||||
<view class="title">{{$tt('alert.alertProcess')}}</view>
|
||||
<u--form labelPosition="left" :model="model" labelWidth="90"
|
||||
:labelStyle="{ fontSize: '28rpx', color: '#8f9ca2' }">
|
||||
<u-form-item :label="$tt('alert.processAlert')">
|
||||
<u-radio-group v-if="model.status === 2" v-model="alertLevel" size="16" labelSize="14">
|
||||
<u-radio shape="circle" :name="1" :label="$tt('alert.untreated')"></u-radio>
|
||||
<text style="margin-left: 30rpx;"></text>
|
||||
<u-radio shape="circle" :name="3" :label="$tt('alert.processed')"></u-radio>
|
||||
</u-radio-group>
|
||||
<text v-if="model.status === 3">{{ getaStatusDisplay(model) }}</text>
|
||||
</u-form-item>
|
||||
<u-form-item v-if="model.status === 2" :label="$tt('alert.processResult')" labelPosition="top">
|
||||
<u-textarea v-model="model.remark" fontSize="14" :placeholder="$tt('alert.inputProcessMsg')"
|
||||
confirmType="done" border="none"></u-textarea>
|
||||
</u-form-item>
|
||||
<u-form-item v-if="model.status === 3" :label="$tt('alert.processResult')">
|
||||
{{ model.remark }}
|
||||
</u-form-item>
|
||||
<u-form-item v-if="model.status === 3" :label="$tt('alert.processTime')">
|
||||
{{ model.updateTime }}
|
||||
</u-form-item>
|
||||
</u--form>
|
||||
</view>
|
||||
|
||||
<view class="btn-wrap" v-if="model.status === 2">
|
||||
<u-button :customStyle="{ height: '96rpx' }" type="primary" size="normal" :text="$tt('common.save')"
|
||||
@click="handleSaveForm"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAlertLog, editAlertLog } from '@/apis/modules/alertLog.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
alertLogId: null, // 告警id
|
||||
source: null, // 数据来源-设备/告警列表
|
||||
// 详情数据
|
||||
model: {
|
||||
remark: '',
|
||||
},
|
||||
alertLevel: 1, // 处理级别
|
||||
};
|
||||
},
|
||||
onLoad: function (option) {
|
||||
this.alertLogId = Number(option.alertLogId) || null;
|
||||
this.source = Number(option.source) || null;
|
||||
this.getAlertDetail();
|
||||
},
|
||||
methods: {
|
||||
// 获取遥控器详情
|
||||
getAlertDetail () {
|
||||
getAlertLog(this.alertLogId).then(res => {
|
||||
const { code, data } = res;
|
||||
if (code === 200) {
|
||||
this.model = { ...data };
|
||||
}
|
||||
});
|
||||
},
|
||||
// 格式化detail字段
|
||||
formatDetail (json) {
|
||||
if (json == null || json == "") {
|
||||
return;
|
||||
}
|
||||
let item = JSON.parse(json);
|
||||
let result = 'id:<span style="color:#F56C6C">' + item.id + '</span><br />';
|
||||
result = result + 'value:<span style="color:#F56C6C">' + item.value + '</span><br />';
|
||||
result = result + 'remark:<span style="color:#F56C6C">' + item.remark + '</span>';
|
||||
return result;
|
||||
},
|
||||
// 保存
|
||||
handleSaveForm () {
|
||||
const params = { ...this.model, status: this.alertLevel };
|
||||
editAlertLog(params).then(res => {
|
||||
if (res.code === 200) {
|
||||
let pages = getCurrentPages();
|
||||
let prevPage = pages[pages.length - 2];
|
||||
// 更新列表
|
||||
if (this.source === 1) {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
prevPage.$refs.deviceAlertLog.handleSearch();
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
prevPage.$vm.$refs.deviceAlertLog.handleSearch();
|
||||
// #endif
|
||||
}
|
||||
if (this.source === 2) {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
prevPage.handleSearch();
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
prevPage.$vm.handleSearch();
|
||||
// #endif
|
||||
}
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'error',
|
||||
title: this.$tt('common.saveError')
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取告警级别显示
|
||||
getaLertLevelDisplay (item) {
|
||||
const { alertLevel } = item;
|
||||
if (alertLevel === 1) {
|
||||
return '提醒通知';
|
||||
} else if (alertLevel === 2) {
|
||||
return '轻微问题';
|
||||
} else if (alertLevel === 3) {
|
||||
return '严重警告';
|
||||
}
|
||||
},
|
||||
// 获取告警状态显示
|
||||
getaStatusDisplay (item) {
|
||||
const { status } = item;
|
||||
if (status === 1) {
|
||||
return '不需要处理';
|
||||
} else if (status === 2) {
|
||||
return '未处理';
|
||||
} else if (status === 3) {
|
||||
return '已处理';
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.alert-edit {
|
||||
width: 100%;
|
||||
|
||||
.alert-info,
|
||||
.alert-dispose {
|
||||
background: #fff;
|
||||
margin: 20rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.title {
|
||||
color: #303133;
|
||||
font-size: 32rpx;
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-wrap {
|
||||
margin: 30rpx 20rpx;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #5ac725;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #f9ae3d;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .u-form-item__body {
|
||||
padding: 5px 0 !important;
|
||||
}
|
||||
|
||||
::v-deep .u-textarea__field {
|
||||
font-size: 28rpx !important;
|
||||
}
|
||||
</style>
|
401
pages/tabBar/alert/index.vue
Normal file
401
pages/tabBar/alert/index.vue
Normal file
@ -0,0 +1,401 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.alert')" background-color="#007AFF"></navigation-bar>
|
||||
</page-meta>
|
||||
|
||||
<view class="alert-wrap">
|
||||
<u-sticky>
|
||||
<view class="nav-bar">
|
||||
<view class="left-wrap">
|
||||
<view v-if="!isSearch">
|
||||
<u-icon name="search" size="23" @click="isSearch = true"></u-icon>
|
||||
</view>
|
||||
<view v-else style="width: 100%;">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.alertName"
|
||||
:placeholder="$tt('alert.inputAlertName')" shape="circle" @clear="handleClearSearch"
|
||||
clearable>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.alertName"
|
||||
:placeholder="$tt('alert.inputAlertName')" shape="circle" @clear="handleClearSearch"
|
||||
clearable>
|
||||
<!-- #endif -->
|
||||
<template slot="prefix">
|
||||
<u-icon name="search" size="22" @click="isSearch = false"></u-icon>
|
||||
</template>
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('common.search')" type="primary" shape="circle" size="mini"
|
||||
@click="handleSearch"></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tabs-wrap">
|
||||
<u-tabs :list="tabList" :scrollable="true" lineWidth="40" lineHeight="2" lineColor="transparent"
|
||||
:duration="100" :activeStyle="{ fontSize: '36rpx', color: '#3c9cff', fontWeight: 'bold' }"
|
||||
@change="handleTabsClick">
|
||||
</u-tabs>
|
||||
</view>
|
||||
</u-sticky>
|
||||
|
||||
<view class="log-content">
|
||||
<view class="item-wrap" v-for="(item, index) in datas" :key="index" @click="handleDispose(item)">
|
||||
<view class="title">
|
||||
<view class="name">{{item.deviceName}}</view>
|
||||
<view class="status"
|
||||
:class="{'success': item.alertLevel === 1, 'warning': item.alertLevel === 2, 'error': item.alertLevel === 3}">
|
||||
{{ getaLertLevelDisplay(item) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="describe">
|
||||
<view class="item">{{$tt('alert.alertName')}}:{{item.alertName}}</view>
|
||||
<view class="item">
|
||||
{{$tt('alert.processState')}}:<text
|
||||
:class="{'success': item.status === 1 || item.status === 3, 'warning' : item.status === 2}">
|
||||
{{ getaStatusDisplay(item) }}</text>
|
||||
</view>
|
||||
<view class="item">{{$tt('alert.data')}}:<rich-text
|
||||
:nodes="getaDataDetailDisplay(item)"></rich-text>
|
||||
</view>
|
||||
<view class="item">{{$tt('alert.alertTime')}}:{{item.createTime}}</view>
|
||||
</view>
|
||||
<view class="tools" v-if="item.status === 2">
|
||||
<view class="btn"><u-button type="primary" size="mini" :text="$tt('alert.process')"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="list" :show="total === 0" marginTop="60"></u-empty>
|
||||
</view>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize" marginTop="20" />
|
||||
<u-loading-page :loading="loading" bg-color="#eef3f7" loadingText="XCWL.cn"></u-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAlertList, getAlertTemplateId } from '@/apis/modules/alertLog.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
isSearch: true, // 是否开启搜索框
|
||||
// tabs列表
|
||||
tabList: [{
|
||||
id: null,
|
||||
name: this.$tt('alert.all'),
|
||||
}, {
|
||||
id: 2,
|
||||
name: this.$tt('alert.pending'),
|
||||
}, {
|
||||
id: 1,
|
||||
name: this.$tt('alert.untreated'),
|
||||
}, {
|
||||
id: 3,
|
||||
name: this.$tt('alert.processed'),
|
||||
}],
|
||||
queryParams: {
|
||||
alertName: null,
|
||||
status: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
datas: [], // log列表数据
|
||||
total: 0, // 总条数
|
||||
loadmoreStatus: 'loadmore',
|
||||
tmpIds: [], //模板ids
|
||||
};
|
||||
},
|
||||
onShow () {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 检查用户是否登录
|
||||
if (!this.isLogin()) {
|
||||
uni.showModal({
|
||||
title: this.$tt('common.tips'),
|
||||
content: this.$tt('common.loginTips'),
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 用户点击确定,跳转到登录页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
} else if (res.cancel) {
|
||||
uni.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
})
|
||||
console.log('用户点击取消');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
created () {
|
||||
this.getToken();
|
||||
if (this.token != '' && this.token != null) {
|
||||
this.getDatas();
|
||||
this.getTmplIds();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getToken () {
|
||||
// 本地缓存获取token
|
||||
this.token = uni.getStorageSync('token');
|
||||
// vuex存储token
|
||||
uni.$u.vuex('vuex_token', this.token);
|
||||
console.log(this.token)
|
||||
},
|
||||
//判断是否登录
|
||||
isLogin () {
|
||||
if (this.token == '' || this.token == null) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
// 获取列表数据
|
||||
getDatas () {
|
||||
this.loading = true;
|
||||
getAlertList(this.queryParams).then(res => {
|
||||
const { code, rows, total } = res;
|
||||
if (this.queryParams.pageNum === 1) {
|
||||
this.datas = rows;
|
||||
} else {
|
||||
this.datas = this.datas.concat(rows);
|
||||
}
|
||||
this.total = total;
|
||||
this.loading = false;
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
handleSearch () {
|
||||
this.datas = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getDatas();
|
||||
},
|
||||
handleClearSearch () {
|
||||
this.handleSearch();
|
||||
},
|
||||
// 单击查询
|
||||
handleTabsClick (e) {
|
||||
this.queryParams.status = e.id;
|
||||
this.datas = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getDatas();
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = this.queryParams.pageNum + 1;
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize > this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.getDatas();
|
||||
this.loadmoreStatus = 'loadmore';
|
||||
}
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.datas = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getDatas();
|
||||
},
|
||||
// 获取告警级别显示
|
||||
getaLertLevelDisplay (item) {
|
||||
const { alertLevel } = item;
|
||||
if (alertLevel === 1) {
|
||||
return this.$tt('alert.notice');
|
||||
} else if (alertLevel === 2) {
|
||||
return this.$tt('alert.minor');
|
||||
} else if (alertLevel === 3) {
|
||||
return this.$tt('alert.warning');
|
||||
}
|
||||
},
|
||||
// 获取告警状态显示
|
||||
getaStatusDisplay (item) {
|
||||
const { status } = item;
|
||||
if (status === 1) {
|
||||
return this.$tt('alert.untreated');
|
||||
} else if (status === 2) {
|
||||
return this.$tt('alert.pending');
|
||||
} else if (status === 3) {
|
||||
return this.$tt('alert.processed');
|
||||
}
|
||||
},
|
||||
// 获取告警数据内容
|
||||
getaDataDetailDisplay (item) {
|
||||
const { detail } = item;
|
||||
if (detail) {
|
||||
const detailObj = JSON.parse(detail);
|
||||
return `<div>id:<span style="color:#F56C6C">${detailObj.id}</span>,value:<span style="color:#F56C6C">${detailObj.value}</span></div>`
|
||||
}
|
||||
},
|
||||
// 处理
|
||||
handleDispose (item) {
|
||||
// 订阅通知
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: [this.tmpIds],
|
||||
success (res) {
|
||||
console.log(res);
|
||||
},
|
||||
fail (err) {
|
||||
console.error(err); //失败
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
uni.navigateTo({
|
||||
url: `/pages/tabBar/alert/edit?alertLogId=${item.alertLogId}&source=2`
|
||||
});
|
||||
},
|
||||
// 获取模板id
|
||||
getTmplIds () {
|
||||
getAlertTemplateId().then(res => {
|
||||
if (res.code == 200) {
|
||||
if (res.msg != '') {
|
||||
this.tmpIds = res.msg;
|
||||
} else {
|
||||
console.log('模板id为空!');
|
||||
}
|
||||
} else {
|
||||
console.log(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.alert-wrap {
|
||||
width: 100%;
|
||||
padding-bottom: 20rpx;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 20rpx 0;
|
||||
height: 74rpx;
|
||||
background: #eef3f7;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-wrap {
|
||||
background: #eef3f7;
|
||||
padding: 16rpx 10rpx 10rpx;
|
||||
}
|
||||
|
||||
.log-content {
|
||||
width: 100%;
|
||||
|
||||
.item-wrap {
|
||||
box-shadow: 0 2rpx 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10rpx;
|
||||
margin: 20rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding: 20rpx 0;
|
||||
|
||||
.name {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
margin-right: 20rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.describe {
|
||||
font-size: 28rpx;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
margin: 16rpx 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #5ac725;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #f9ae3d;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.content {
|
||||
.alert-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
background-color: #ffffff;
|
||||
|
||||
.left {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.middle {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
751
pages/tabBar/home/index.vue
Normal file
751
pages/tabBar/home/index.vue
Normal file
@ -0,0 +1,751 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('common.fastbee')" background-color="#007AFF">
|
||||
</navigation-bar>
|
||||
</page-meta>
|
||||
<view class="home-wrap">
|
||||
<NavBar @messageSent="handleMessage"></NavBar>
|
||||
<!-- #ifdef H5-->
|
||||
<view class="container-wrap h5">
|
||||
<u-sticky zIndex="98">
|
||||
<view class="top-wrap">
|
||||
<view class="swiper-wrap">
|
||||
<swiper class="swiper-item" circular :indicator-dots="true">
|
||||
<swiper-item>
|
||||
<weather></weather>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN-->
|
||||
<view class="container-wrap ">
|
||||
<u-sticky zIndex="98">
|
||||
<view class="top-wrap">
|
||||
<view class="swiper-wrap-weixin">
|
||||
<swiper class="swiper-item" circular :indicator-dots="true">
|
||||
<swiper-item>
|
||||
<weather></weather>
|
||||
</swiper-item>
|
||||
<!-- <swiper-item>
|
||||
<u--image :showLoading="true" src="/static/home/fastbee.png" width="100%"
|
||||
height="334rpx" radius="10"></u--image>
|
||||
</swiper-item> -->
|
||||
</swiper>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<view class="tab-wrap" v-if="token !== null || token !== ''">
|
||||
<u-tabs :list="groupList" :scrollable="true" lineWidth="40" lineHeight="2"
|
||||
lineColor="transparent" :duration="100"
|
||||
:activeStyle="{ fontSize: '36rpx', color: '#3c9cff', fontWeight: 'bold' }"
|
||||
@change="handleTabChange">
|
||||
<view v-if="token !== null || token !== ''" slot="right" class="add-btn"
|
||||
@tap="handleTopPopOpen">
|
||||
<u-icon name="plus-circle-fill" size="24" color='#3c9cff' bold></u-icon>
|
||||
</view>
|
||||
</u-tabs>
|
||||
</view>
|
||||
</view>
|
||||
</u-sticky>
|
||||
|
||||
<view class="device-wrap" v-if="token != null && token != ''">
|
||||
<view class="content-wrap">
|
||||
<view class="item-wrap" v-for="(item, index) in deviceList" :key="index">
|
||||
<view class="card"
|
||||
:style="{margin:(index%2==0?'15rpx 15rpx 15rpx 30rpx':'15rpx 30rpx 15rpx 15rpx')}"
|
||||
@tap="gotoDeviceDetail(item)">
|
||||
<div style="height:25rpx;">
|
||||
<u--image v-if="item.isOwner===0" src="/static/home/device/share.png"
|
||||
mode="aspectFill" width="20" height="25rpx"></u--image>
|
||||
</div>
|
||||
<view class="top">
|
||||
<view class="img-wrap">
|
||||
<u--image v-if="item.imgUrl" :src="item.imageUrl" radius="10"
|
||||
mode="aspectFill" width="20" height="20">
|
||||
<view slot="error" style="font-size: 12px;">
|
||||
{{$tt('home.errorLoading')}}
|
||||
</view>
|
||||
<template v-slot:loading>
|
||||
<u-loading-icon></u-loading-icon>
|
||||
</template>
|
||||
</u--image>
|
||||
<u--image v-else-if="item.deviceType === 2"
|
||||
src="/static/common/gateway.png" radius="10" mode="aspectFill"
|
||||
width="55" height="55">
|
||||
</u--image>
|
||||
<u--image v-else-if="item.deviceType === 3"
|
||||
src="/static/common/video.png" radius="10" mode="aspectFill"
|
||||
width="55" height="55">
|
||||
</u--image>
|
||||
<u--image v-else src="/static/common/device.png" radius="10"
|
||||
mode="aspectFill" width="55" height="55">
|
||||
</u--image>
|
||||
</view>
|
||||
<view class="right-wrap">
|
||||
<view class="status-wrap">
|
||||
<u--text v-if="item.status == 3 && item.rssi >= '-55'" lines="1"
|
||||
prefixIcon="/static/wifi_4.png" iconStyle="margin-right:6rpx;"
|
||||
size="12" :text="statusTxt(item.status)"></u--text>
|
||||
<u--text
|
||||
v-else-if="item.status == 3 && item.rssi >= '-70' && item.rssi < '-55'"
|
||||
lines="1" prefixIcon="/static/wifi_3.png"
|
||||
iconStyle="margin-right:6rpx;" size="12"
|
||||
:text="statusTxt(item.status)"></u--text>
|
||||
<u--text
|
||||
v-else-if="item.status == 3 && item.rssi >= '-85' && item.rssi < '-70'"
|
||||
lines="1" prefixIcon="/static/wifi_2.png"
|
||||
iconStyle="margin-right:6rpx;" size="12"
|
||||
:text="statusTxt(item.status)"></u--text>
|
||||
<u--text
|
||||
v-else-if="item.status == 3 && item.rssi >= '-100' && item.rssi < '-85'"
|
||||
lines="1" prefixIcon="/static/wifi_1.png"
|
||||
iconStyle="margin-right:6rpx;" size="12"
|
||||
:text="statusTxt(item.status)"></u--text>
|
||||
<u--text v-else lines="1" prefixIcon="/static/wifi_0.png"
|
||||
iconStyle="margin-right:6rpx;" size="12"
|
||||
:text="statusTxt(item.status)">
|
||||
</u--text>
|
||||
</view>
|
||||
<view class="shadow-wrap">
|
||||
<u--text lines="1" prefixIcon="/static/state_active.png"
|
||||
iconStyle="margin-right:6rpx;" size="12"
|
||||
v-if="item.isShadow == 1" :text="$tt('home.shadow')">
|
||||
</u--text>
|
||||
<u--text lines="1" prefixIcon="/static/state.png"
|
||||
iconStyle="margin-right:6rpx;" size="12" v-else
|
||||
:text="$tt('home.shadow')"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="title">{{item.deviceName}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60"
|
||||
:text="$tt('scene.emptyData')"></u-empty>
|
||||
<u-loadmore :status="loadStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
|
||||
:nomoreText="$tt('scene.emptyData')" marginTop="20" />
|
||||
</view>
|
||||
|
||||
<view class="token-null" v-if="token == null || token == ''">
|
||||
<u-empty mode="data" :show="!token" marginTop="60"
|
||||
:text="$tt('timing.emptyNull')"></u-empty>
|
||||
<u-button type="primary" @click="() => openAlert = true"
|
||||
customStyle="width:400rpx;margin-top:60rpx;">创建设备</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-popup :show="isShow" @close="handleTopPopClose" mode="top" round="10">
|
||||
<view style="padding:20px 0 10px 0;">
|
||||
<!-- #ifdef MP-WEIXIN || H5 -->
|
||||
<u-grid :border="false" col="3">
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<u-grid :border="false" col="3">
|
||||
<!-- #endif -->
|
||||
<u-grid-item>
|
||||
<!-- 配网添加 -->
|
||||
<u-icon name="/static/ap.png" size="25" color="#fff"
|
||||
:label="$tt('home.netWork')" labelPos="bottom" labelSize="15"
|
||||
space="10px" @click="gotoAddDevice()"
|
||||
customStyle="background-color:#f56c6c;border-radius:3px;padding:10px;"></u-icon>
|
||||
</u-grid-item>
|
||||
<u-grid-item>
|
||||
<!-- 扫码添加-->
|
||||
<u-icon name="/static/scan.png" size="25" :label="$tt('home.qrCode')"
|
||||
labelPos="bottom" labelSize="15" space="10px" @click="openScan"
|
||||
customStyle="background-color:#3c9cff;border-radius:3px;padding:10px;"></u-icon>
|
||||
</u-grid-item>
|
||||
<u-grid-item>
|
||||
<!-- 关联添加 -->
|
||||
<u-icon name="/static/relate.png" size="25" :label="$tt('home.association')"
|
||||
labelPos="bottom" labelSize="15" space="10px"
|
||||
@click="gotoRelateDevice()"
|
||||
customStyle="background-color:#f9ae3d;border-radius:3px;padding:10px;"></u-icon>
|
||||
</u-grid-item>
|
||||
</u-grid>
|
||||
<view>
|
||||
<u-row>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<u-col :span="4">
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN || H5 -->
|
||||
<u-col :span="4">
|
||||
<!-- #endif -->
|
||||
<u--text type="info" :text="$tt('home.wifi-type')" size="12"
|
||||
customStyle="padding:10px 15px;">
|
||||
</u--text>
|
||||
</u-col>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<u-col :span="4">
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN || H5 -->
|
||||
<u-col :span="4">
|
||||
<!-- #endif -->
|
||||
<u--text type="info" :text="$tt('home.networksDevices')"
|
||||
size="12" customStyle="padding:10px 15px;">
|
||||
</u--text>
|
||||
</u-col>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<u-col :span="4">
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN || H5 -->
|
||||
<u-col :span="4">
|
||||
<!-- #endif -->
|
||||
<u--text type="info"
|
||||
:text="$tt('home.supportBatchOperations')" size="12"
|
||||
customStyle="padding:10px 15px;"></u--text>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<u-modal :show="modal.show" :content="modal.content" @confirm="confirm" @cancel="cancel"
|
||||
:showConfirmButton="modal.showConfirmButton" showCancelButton></u-modal>
|
||||
<u-loading-page style="z-index: 98" :loading="loading" bg-color="#eef3f7"
|
||||
loadingText="XCWL.cn"></u-loading-page>
|
||||
<u-modal :show="openAlert" content="该功能需要登录才可使用,是否去登录?" @confirm="gotoLogin"
|
||||
@cancel="() => openAlert = false" showCancelButton></u-modal>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import projectConfig from '@/env.config.js';
|
||||
import NavBar from '@/components/NavBar/NavBar.vue'
|
||||
import weather from '@/components/weather/index.vue';
|
||||
import { getGroupList } from '@/apis/modules/group';
|
||||
import { listDeviceShort, deviceRelateUser } from '@/apis/modules/device';
|
||||
|
||||
export default {
|
||||
beforeRouteEnter (to, from, next) {
|
||||
// 在进入路由前执行一些操作,例如加载数据等
|
||||
next(vm => {
|
||||
// 这里可以访问组件实例 `vm`
|
||||
vm.onLoad() // 执行加载操作
|
||||
})
|
||||
},
|
||||
components: {
|
||||
weather,
|
||||
NavBar
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
token: '',
|
||||
groupList: [], // 分组列表
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
groupId: 0,
|
||||
deviceName: null
|
||||
},
|
||||
loadStatus: 'loadmore', // 加载更多
|
||||
deviceList: [], // 设备列表
|
||||
total: 0, // 总条数
|
||||
isShow: false, // 顶部弹出层
|
||||
// 扫码模态窗
|
||||
modal: {
|
||||
show: false,
|
||||
showConfirmButton: false,
|
||||
content: ''
|
||||
},
|
||||
//提示需要登录框
|
||||
openAlert: false,
|
||||
scanJson: {}, // 扫码获取的Json
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
onLoad () {
|
||||
this.getToken();
|
||||
if (this.token != '' && this.token != null) {
|
||||
this.connectMqtt();
|
||||
this.getDatas();
|
||||
}
|
||||
this.groupList = [{
|
||||
name: this.$tt('home.all'),
|
||||
id: 0
|
||||
}];
|
||||
},
|
||||
onShow () {
|
||||
//小程序tabBar导航国际化特殊性
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.setTabBarItem({
|
||||
index: 0,
|
||||
text: this.$tt('navBar.home'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 1,
|
||||
text: this.$tt('navBar.scene'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 2,
|
||||
text: this.$tt('navBar.alert'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 3,
|
||||
text: this.$tt('navBar.news'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 4,
|
||||
text: this.$tt('navBar.user'),
|
||||
})
|
||||
//#endif
|
||||
},
|
||||
methods: {
|
||||
//接受子组件导航栏传递的搜索信息
|
||||
handleMessage (msg) {
|
||||
this.deviceList = []
|
||||
this.queryParams.deviceName = msg;
|
||||
console.log('需要搜索的数据为', this.queryParams)
|
||||
this.getDevices()
|
||||
},
|
||||
getToken () {
|
||||
// 本地缓存获取token
|
||||
this.token = uni.getStorageSync('token');
|
||||
// vuex存储token
|
||||
uni.$u.vuex('vuex_token', this.token);
|
||||
},
|
||||
// 连接Mqtt消息服务器
|
||||
async connectMqtt () {
|
||||
if (this.$mqttTool.client == null) {
|
||||
await this.$mqttTool.connect(this.vuex_token);
|
||||
}
|
||||
this.mqttCallback();
|
||||
this.getDatas();
|
||||
},
|
||||
// Mqtt回调处理
|
||||
mqttCallback () {
|
||||
this.$mqttTool.client.on('message', (topic, message, buffer) => {
|
||||
let topics = topic.split('/');
|
||||
let productId = topics[1];
|
||||
let deviceNum = topics[2];
|
||||
message = JSON.parse(message.toString());
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
if (topics[3] == 'status') {
|
||||
console.log('接收到【设备状态】主题:', topic);
|
||||
console.log('接收到【设备状态】内容:', message);
|
||||
// 更新列表中设备的状态
|
||||
for (let i = 0; i < this.deviceList.length; i++) {
|
||||
if (this.deviceList[i].serialNumber == deviceNum) {
|
||||
this.deviceList[i].status = message.status;
|
||||
this.deviceList[i].isShadow = message.isShadow;
|
||||
this.deviceList[i].rssi = message.rssi;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 订阅消息
|
||||
mqttSubscribe (list) {
|
||||
// 订阅当前页面设备状态和实时监测
|
||||
let topics = [];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let topicStatus = '/' + list[i].productId + '/' + list[i].serialNumber + '/status/post';
|
||||
topics.push(topicStatus);
|
||||
}
|
||||
this.$mqttTool.subscribe(topics);
|
||||
},
|
||||
// 获取数据
|
||||
getDatas () {
|
||||
this.getGroups();
|
||||
this.getDevices();
|
||||
},
|
||||
// 获取分组列表
|
||||
getGroups () {
|
||||
getGroupList({
|
||||
userId: this.profile.userId
|
||||
}).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.groupList = [{
|
||||
name: this.$tt('home.all'),
|
||||
id: 0
|
||||
}];
|
||||
if (res.rows.length !== 0) {
|
||||
for (let i = 0; i < res.rows.length; i++) {
|
||||
this.groupList.push({
|
||||
name: res.rows[i].groupName,
|
||||
id: res.rows[i].groupId
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取设备列表
|
||||
getDevices () {
|
||||
this.loading = true;
|
||||
listDeviceShort(this.queryParams).then(response => {
|
||||
let {
|
||||
code,
|
||||
rows,
|
||||
total
|
||||
} = response;
|
||||
if (code === 200) {
|
||||
rows = rows.map(item => {
|
||||
item.imageUrl = item.imgUrl !== null && item.imgUrl !== '' ? projectConfig
|
||||
.baseUrl + item.imgUrl : item.imgUrl;
|
||||
return item;
|
||||
});
|
||||
if (this.queryParams.pageNum == 1) {
|
||||
this.deviceList = rows;
|
||||
} else {
|
||||
this.deviceList = this.deviceList.concat(rows);
|
||||
}
|
||||
this.total = total;
|
||||
const {
|
||||
pageNum,
|
||||
pageSize
|
||||
} = this.queryParams;
|
||||
this.loadStatus = total > pageNum * pageSize ? 'loadmore' : 'nomore';
|
||||
// 订阅消息
|
||||
if (this.deviceList && this.deviceList.length > 0) {
|
||||
this.mqttSubscribe(this.deviceList);
|
||||
}
|
||||
}
|
||||
this.loading = false;
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
getProfileInfo () {
|
||||
// 调用用户信息接口
|
||||
this.$api.common.getProfile().then(res => {
|
||||
//存储用户信息,TODO 需要调用一次,不然其他页面调用返回空
|
||||
uni.$u.vuex('profile', res.data);
|
||||
this.profile;
|
||||
}).catch(err => {
|
||||
this.$u.toast(err.msg);
|
||||
});
|
||||
},
|
||||
// 设备分组改变事件
|
||||
handleTabChange (item) {
|
||||
this.queryParams.groupId = item.id;
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getDevices();
|
||||
},
|
||||
// 设备状态
|
||||
statusTxt (status) {
|
||||
let txt = '';
|
||||
switch (status) {
|
||||
case 1:
|
||||
txt = this.$tt('home.notActive');
|
||||
break;
|
||||
case 2:
|
||||
txt = this.$tt('home.disabled');
|
||||
break;
|
||||
case 3:
|
||||
txt = this.$tt('home.onLine');
|
||||
break;
|
||||
case 4:
|
||||
txt = this.$tt('home.offline');
|
||||
break;
|
||||
}
|
||||
return txt;
|
||||
},
|
||||
// 跳转详情
|
||||
gotoDeviceDetail (item) {
|
||||
const { deviceId, protocolCode } = item;
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/home/device/index?deviceId=' + deviceId + '&protocolCode=' + protocolCode
|
||||
});
|
||||
},
|
||||
// 未登录跳转登录页
|
||||
gotoLogin () {
|
||||
uni.$u.route('/pagesB/login/waitLogin');
|
||||
this.openAlert = false;
|
||||
},
|
||||
// 打开顶部弹窗
|
||||
handleTopPopOpen () {
|
||||
this.isShow = true;
|
||||
},
|
||||
// 关闭顶部弹窗
|
||||
handleTopPopClose () {
|
||||
this.isShow = false;
|
||||
},
|
||||
// 添加设备
|
||||
gotoAddDevice () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
this.isShow = false;
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/list/home/deviceAdd'
|
||||
});
|
||||
} else {
|
||||
this.openAlert = true;
|
||||
}
|
||||
|
||||
},
|
||||
// 关联设备
|
||||
gotoRelateDevice () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
this.isShow = false;
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/list/home/deviceRelate'
|
||||
});
|
||||
} else {
|
||||
this.openAlert = true;
|
||||
}
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getDatas(); //重新获取数据
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom () {
|
||||
this.queryParams.pageNum = this.queryParams.pageNum + 1;
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize > this.total) {
|
||||
this.loadStatus = 'nomore';
|
||||
} else {
|
||||
this.loadStatus = 'loading';
|
||||
this.getDevices();
|
||||
}
|
||||
},
|
||||
/***************************************扫码关联设备***********************************************/
|
||||
// 模态窗确定
|
||||
confirm () {
|
||||
this.cancel();
|
||||
let form = {
|
||||
deviceNumberAndProductIds: [{
|
||||
deviceNumber: this.scanJson.deviceNumber,
|
||||
productId: this.scanJson.productId
|
||||
}],
|
||||
userId: this.profile.userId
|
||||
};
|
||||
deviceRelateUser(form).then(res => {
|
||||
if (res.code == 200) {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: this.$tt('user.saveSuccess')
|
||||
});
|
||||
this.isShow = false;
|
||||
} else {
|
||||
this.modal = {
|
||||
show: true,
|
||||
showConfirmButton: false,
|
||||
content:res.msg
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
// 模态窗取消
|
||||
cancel () {
|
||||
this.modal = {
|
||||
show: false,
|
||||
showConfirmButton: false,
|
||||
content: ''
|
||||
};
|
||||
},
|
||||
// 扫码
|
||||
async openScan () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
// #ifndef MP-WEIXIN || APP-PLUS
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('user.scanning')
|
||||
});
|
||||
return;
|
||||
// #endif
|
||||
|
||||
// 权限问题,app 需要做权限说明
|
||||
let onlyFromCamera = false;
|
||||
// #ifdef APP-PLUS
|
||||
onlyFromCamera = true;
|
||||
let result = await this.$store.dispatch("permission/requestPermissions", 'CAMERA');
|
||||
if (result !== 1) return;
|
||||
// #endif
|
||||
this.startScanCode(onlyFromCamera);
|
||||
} else {
|
||||
this.openAlert = true;
|
||||
}
|
||||
},
|
||||
startScanCode (onlyFromCamera) {
|
||||
uni.scanCode({
|
||||
onlyFromCamera, // 是否允许从相册扫码
|
||||
success: res => {
|
||||
console.log('条码类型:' + res.scanType);
|
||||
console.log('条码内容:' + res.result);
|
||||
if (res.result.substr(0, 1) != '{') {
|
||||
console.log('坑点:解析二维码后第一个位置包含一个特殊字符,大部分编译器和调试工具识别不了这个特殊字符');
|
||||
res.result = res.result.substring(1);
|
||||
}
|
||||
// 解析JSON
|
||||
try {
|
||||
this.scanJson = JSON.parse(res.result);
|
||||
// type=1 代表扫码关联设备
|
||||
if (this.scanJson.type == 1) {
|
||||
this.modal = {
|
||||
show: true,
|
||||
showConfirmButton: true,
|
||||
content: '【' + this.$tt('home.serialNumber') +
|
||||
':' + this.scanJson.deviceNumber + ',' + this.$tt(
|
||||
'home.productName') +
|
||||
':' + this
|
||||
.scanJson.productName + '】' + this.$tt('home.sureAdd')
|
||||
};
|
||||
return;
|
||||
}
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('user.parseQrCode')
|
||||
});
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('user.errorParseQRcode')
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.home-wrap {
|
||||
|
||||
// 不要设置height会影响u-sticky 算uviewbug吧
|
||||
//height: 100%;
|
||||
.h5 {
|
||||
padding-top: calc(52px + var(--status-bar-height));
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
//padding-top: calc(52px + var(--status-bar-height));
|
||||
background: #eef3f7;
|
||||
// height: 100%;
|
||||
|
||||
.top-wrap {
|
||||
background: #eef3f7;
|
||||
|
||||
.swiper-wrap {
|
||||
padding: 30rpx;
|
||||
padding-top: calc(40rpx + var(--status-bar-height));
|
||||
|
||||
.swiper-item {
|
||||
display: block;
|
||||
height: 334rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.swiper-wrap-weixin {
|
||||
padding: 30rpx;
|
||||
padding-top: calc(165rpx + var(--status-bar-height));
|
||||
|
||||
.swiper-item {
|
||||
display: block;
|
||||
height: 334rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-wrap {
|
||||
padding: 0 12rpx;
|
||||
|
||||
.add-btn {
|
||||
padding: 0 12rpx;
|
||||
background: #eef3f7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.device-wrap {
|
||||
background: #eef3f7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-bottom: 120rpx;
|
||||
|
||||
.content-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.item-wrap {
|
||||
width: 50%;
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
box-shadow: 0 8rpx 12rpx rgba(0, 79, 159, 0.2); // 更加柔和的阴影
|
||||
padding: 20rpx;
|
||||
background-color: #fafafa; // 更浅的背景颜色
|
||||
border-radius: 35rpx;
|
||||
border: 2rpx solid #d3e0f5;
|
||||
overflow: hidden;
|
||||
|
||||
.circle-one {
|
||||
position: absolute;
|
||||
// z-index: 1;
|
||||
border-radius: 50%;
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
top: -20rpx;
|
||||
left: -25rpx;
|
||||
background-color: rgba(220, 235, 248, 0.8); // 浅蓝色
|
||||
}
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.img-wrap {
|
||||
border-radius: 10rpx;
|
||||
// border: 3rpx dashed #819ac0;
|
||||
padding: 6rpx;
|
||||
background-color: #eaf2fc;
|
||||
}
|
||||
|
||||
.right-wrap {
|
||||
margin-right: 25rpx;
|
||||
|
||||
.status-wrap {
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
|
||||
.shadow-wrap {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 20rpx;
|
||||
font-size: 30rpx;
|
||||
padding: 10rpx;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
color: #496d89;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
// border-bottom: 2rpx dotted #7fa1ce; // 增加实线边框
|
||||
text-overflow: ellipsis;
|
||||
text-shadow: 1rpx 1rpx 2rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.token-null {
|
||||
background: #eef3f7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
496
pages/tabBar/scene/index.vue
Normal file
496
pages/tabBar/scene/index.vue
Normal file
@ -0,0 +1,496 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('scene.sceneLink')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="scene-wrap">
|
||||
<u-sticky zIndex="98" bgColor="#eef3f7">
|
||||
<view class="nav-bar">
|
||||
<view class="left-wrap">
|
||||
<view style="margin-right: 20rpx;">
|
||||
<u-icon name="list-dot" size="23" @click="isMenu = true"></u-icon>
|
||||
</view>
|
||||
<view v-if="!isSearch" style="margin-right: 20rpx;">
|
||||
<u-icon name="search" size="23" @click="isSearch = true"></u-icon>
|
||||
</view>
|
||||
<view v-else style="margin-right: 20rpx; width: 100%;">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.sceneName"
|
||||
:placeholder="$tt('scene.inputSceneName')" shape="circle" @clear="handleClearSearch"
|
||||
clearable>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.sceneName"
|
||||
:placeholder="$tt('scene.inputSceneName')" shape="circle" @clear="handleClearSearch"
|
||||
clearable>
|
||||
<!-- #endif -->
|
||||
<template slot="prefix">
|
||||
<u-icon name="search" size="22" @click="isSearch = false"></u-icon>
|
||||
</template>
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('scene.search')" type="primary" shape="circle" size="mini"
|
||||
@click="handleSearch"></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
<u-icon name="plus-circle-fill" size="23" color='#3c9cff' bold @tap="handleAddScene"></u-icon>
|
||||
</view>
|
||||
<view class="tab-wrap">
|
||||
<u-tabs :list="statusGroup" :scrollable="true" lineWidth="40" lineHeight="2" lineColor="transparent"
|
||||
:duration="100" :activeStyle="{ fontSize: '36rpx', color: '#3c9cff', fontWeight: 'bold' }"
|
||||
@change="handleStatusChange">
|
||||
</u-tabs>
|
||||
</view>
|
||||
</u-sticky>
|
||||
|
||||
<view class="container-wrap">
|
||||
<view class="item-wrap" v-for="(item, index) in list" :key="index">
|
||||
<view class="card" :style="{ margin: index === 0 ? '0 30rpx 30rpx' : '30rpx'}">
|
||||
<view class="delIcon" @click="handleDelete(item)">
|
||||
<u-icon size="18" name="close-circle-fill" color="#007aff"></u-icon>
|
||||
</view>
|
||||
|
||||
<view @click="handleSceneDetail(item.sceneId)">
|
||||
<view class="title">
|
||||
<u--text lines="1" :text="item.sceneName"></u--text>
|
||||
<u-icon name="arrow-right" color="#606266" customStyle="margin-right:5px;"
|
||||
size="14"></u-icon>
|
||||
</view>
|
||||
|
||||
<view class="cond">{{$tt('scene.condition')}}:{{getCondName(item.cond)}}</view>
|
||||
<view class="cond">{{$tt('scene.way')}}:{{getExecuteModeName(item.executeMode)}}</view>
|
||||
<view class="cond">{{$tt('scene.task')}}:{{item.actionCount}}个</view>
|
||||
<view class="bottom-wrap" @tap.stop>
|
||||
<u--text lines="1" prefixIcon="/static/scene/once.png"
|
||||
iconStyle="width: 14px;height: 14px;margin-right: 3px;" size="13"
|
||||
:text="$tt('scene.execute')" color='#3C9CFF' lineHeight="16"
|
||||
@click="handleRunOnce(item)"></u--text>
|
||||
<u-switch v-model="item.enable" @change="handleSwitchStatus(item)" :activeValue="1"
|
||||
:inactiveValue="2" activeColor="#3C9CFF" inactiveColor="#dbdbdb" size="18"></u-switch>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
|
||||
:nomoreText="$tt('scene.emptyData')" marginTop="20" />
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
|
||||
<u-popup :show="isMenu" mode="top" :closeOnClickOverlay="true" :round="5" @close="isMenu = false">
|
||||
<view class="menu-popup-wrap">
|
||||
<view class="group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell icon="grid" :title="$tt('scene.administration')" :border="false" isLink
|
||||
@click="goToSceneList"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
<u-modal :show="isDelete" showCancelButton :confirmText="$tt('common.delete')" @cancel="() => isDelete = false"
|
||||
:title="$tt('sceneDetail.tips')" :content="deleteContent" @confirm="handleConfirmDelete">
|
||||
</u-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listScene,
|
||||
runScene,
|
||||
updateStatus,
|
||||
delScene
|
||||
} from '@/apis/modules/scene';
|
||||
|
||||
export default {
|
||||
beforeRouteEnter (to, from, next) {
|
||||
// 在进入路由前执行一些操作,例如加载数据等
|
||||
next(vm => {
|
||||
// 这里可以访问组件实例 `vm`
|
||||
vm.onLoad() // 执行加载操作
|
||||
})
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
deleteId: '',
|
||||
isDelete: false,
|
||||
deleteContent: '',
|
||||
isMenu: false,
|
||||
isSearch: true,
|
||||
// 状态列表
|
||||
statusGroup: [{
|
||||
id: null,
|
||||
name: this.$tt('timing.all')
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: this.$tt('timing.enable')
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: this.$tt('timing.notEnabled')
|
||||
}
|
||||
],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 4,
|
||||
enable: null,
|
||||
sceneName: '',
|
||||
},
|
||||
list: [],
|
||||
total: 0,
|
||||
loadmoreStatus: 'loadmore', // 刷新和加载相关
|
||||
};
|
||||
},
|
||||
onLoad () {
|
||||
this.getToken();
|
||||
if (this.token != '' && this.token != null) {
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
onShow () {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 检查用户是否登录
|
||||
if (!this.isLogin()) {
|
||||
uni.showModal({
|
||||
title: this.$tt('common.tips'),
|
||||
content: this.$tt('common.loginTips'),
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 用户点击确定,跳转到登录页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
} else if (res.cancel) {
|
||||
uni.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
})
|
||||
console.log('用户点击取消');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
getToken () {
|
||||
// 本地缓存获取token
|
||||
this.token = uni.getStorageSync('token');
|
||||
// vuex存储token
|
||||
uni.$u.vuex('vuex_token', this.token);
|
||||
console.log(this.token)
|
||||
},
|
||||
//删除
|
||||
handleDelete (item) {
|
||||
this.isDelete = true;
|
||||
this.deleteId = item.sceneId;
|
||||
this.deleteContent = `确定删除“${item.sceneName}”?`
|
||||
},
|
||||
// 确认删除
|
||||
handleConfirmDelete () {
|
||||
delScene(this.deleteId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
this.isDelete = false;
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
},
|
||||
//判断是否登录
|
||||
isLogin () {
|
||||
if (this.token == '' || this.token == null) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
// 场景状态改变事件
|
||||
handleStatusChange (item) {
|
||||
this.list = [];
|
||||
this.queryParams.enable = item.id;
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
// 场景列表
|
||||
goToSceneList () {
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/scene/list'
|
||||
});
|
||||
this.isMenu = false;
|
||||
},
|
||||
// 新增场景
|
||||
handleAddScene () {
|
||||
// 使用vuex会导致小程序性能问题所以存储本地
|
||||
let sceneData = {
|
||||
sceneId: null,
|
||||
sceneName: '',
|
||||
remark: '',
|
||||
enable: 1, // 状态,1:启动,2:暂停
|
||||
cond: 1, // 触发条件,1:任意条件,2:所有条件,3:不满足
|
||||
silentPeriod: 0, // 静默时间
|
||||
executeMode: 1, // 执行方式,1:串行,2:并行
|
||||
executeDelay: 0, // 延时执行
|
||||
hasAlert: 2, // 1:包含告警,2:不包含告警
|
||||
applicationName: 'fastbee', // 固定值
|
||||
triggers: [],
|
||||
actions: [],
|
||||
};
|
||||
const storage = uni.getStorageSync('sceneData');
|
||||
if (storage) {
|
||||
uni.removeStorageSync('sceneData');
|
||||
}
|
||||
uni.setStorageSync('sceneData', sceneData);
|
||||
uni.removeStorageSync('callback');
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/scene/detail'
|
||||
});
|
||||
},
|
||||
// 场景详情
|
||||
handleSceneDetail (id) {
|
||||
const storage = uni.getStorageSync('sceneData');
|
||||
if (storage) {
|
||||
uni.removeStorageSync('sceneData');
|
||||
}
|
||||
const sceneData = {
|
||||
sceneId: null,
|
||||
sceneName: '',
|
||||
remark: '',
|
||||
enable: 1, // 状态,1:启动,2:暂停
|
||||
cond: 1, // 触发条件,1:任意条件,2:所有条件,3:不满足
|
||||
silentPeriod: 0, // 静默时间
|
||||
executeMode: 1, // 执行方式,1:串行,2:并行
|
||||
executeDelay: 0, // 延时执行
|
||||
hasAlert: 2, // 1:包含告警,2:不包含告警
|
||||
applicationName: 'fastbee', // 固定值
|
||||
triggers: [],
|
||||
actions: [],
|
||||
};
|
||||
uni.setStorageSync('sceneData', sceneData);
|
||||
uni.removeStorageSync('callback');
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/scene/detail?sceneId=' + id
|
||||
});
|
||||
},
|
||||
// 获取条件
|
||||
getCondName (cond) {
|
||||
if (cond === 1) {
|
||||
return this.$tt('scene.anyCondition')
|
||||
} else if (cond === 2) {
|
||||
return this.$tt('scene.allCondition')
|
||||
} else if (cond === 3) {
|
||||
return this.$tt('scene.notConditions')
|
||||
} else {
|
||||
return this.$tt('scene.unknown')
|
||||
}
|
||||
},
|
||||
getExecuteModeName (mode) {
|
||||
if (mode === 1) {
|
||||
return this.$tt('scene.serial')
|
||||
} else if (mode === 2) {
|
||||
return this.$tt('scene.parallel')
|
||||
} else {
|
||||
return this.$tt('scene.unknown')
|
||||
}
|
||||
},
|
||||
// 获取场景列表
|
||||
getList () {
|
||||
listScene(this.queryParams)
|
||||
.then(res => {
|
||||
const {
|
||||
rows,
|
||||
total
|
||||
} = res;
|
||||
this.list = this.list.concat(rows);
|
||||
this.total = total;
|
||||
console.log(rows, '场景联动列表');
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
// 搜索
|
||||
handleSearch () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleClearSearch () {
|
||||
this.handleSearch();
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
console.log('下拉刷新');
|
||||
this.list = [];
|
||||
this.total = 0;
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.getList();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom () {
|
||||
console.log('上拉加载');
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getList();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
//执行一次
|
||||
handleRunOnce (data) {
|
||||
const params = {
|
||||
sceneId: data.sceneId
|
||||
};
|
||||
runScene(params).then((res) => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
});
|
||||
},
|
||||
// 切换状态
|
||||
handleSwitchStatus (data) {
|
||||
const params = {
|
||||
sceneId: data.sceneId,
|
||||
enable: data.enable,
|
||||
}
|
||||
updateStatus(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('scene.switchSuccessful')
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('scene.switchFail')
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.scene-wrap {
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 26rpx 26rpx 0;
|
||||
height: 74rpx;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-wrap {
|
||||
padding: 14rpx 12rpx 0 12rpx;
|
||||
|
||||
.add-btn {
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
margin-top: 12rpx;
|
||||
padding-bottom: 120rpx;
|
||||
|
||||
.item-wrap {
|
||||
.card {
|
||||
position: relative;
|
||||
box-shadow: 0 2rpx 0rpx 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.title {
|
||||
padding: 10rpx 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.cond {
|
||||
font-size: 12px;
|
||||
color: #7e7e7e;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.bottom-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 20rpx 0 10rpx;
|
||||
}
|
||||
|
||||
.delIcon {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -10rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.menu-popup-wrap {
|
||||
.group-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 6rpx 0;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 小程序中popup坑
|
||||
/deep/ .u-safe-bottom {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
126
pages/tabBar/trend/trend.vue
Normal file
126
pages/tabBar/trend/trend.vue
Normal file
@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<page-meta><navigation-bar :title="$tt('navBar.dynamic')" title-align="center"
|
||||
background-color="#007AFF" /></page-meta>
|
||||
<view class="container">
|
||||
<u-swiper v-if="listSwiper.length" :list="listSwiper" keyName="imgUrl" indicator indicatorMode="line"
|
||||
@click="gotoSwiperDetail" circular></u-swiper>
|
||||
<view v-for="(item, index) in list" :key="index">
|
||||
<view
|
||||
style="margin-top:30px;display:flex;flex-direction:row; justify-content:space-between;align-items: flex-end;">
|
||||
<view><u--text prefixIcon="grid-fill" iconStyle="font-size: 16px;color:#606266"
|
||||
:text="item.categoryName" bold color="#606266"></u--text></view>
|
||||
<view @click="gotoCategoryTrend(item.categoryId, item.categoryName)"><u--text
|
||||
:text="$tt('scene.seeMore')" color="#606266" customStyle="font-size:12px;"></u--text></view>
|
||||
</view>
|
||||
<u-line margin="5px 0 15px 0"></u-line>
|
||||
<view v-for="(subItem, i) in item.newsList" :key="i"
|
||||
style="display:flex;align-items: center;margin-bottom:15px;">
|
||||
<view style="margin-right:10px;">
|
||||
<u--image :src="subItem.imgUrl" @click="gotoDetail(subItem.newsId)" mode="aspectFill" radius="5"
|
||||
width="120" height="80">
|
||||
<template v-slot:loading>
|
||||
<u-loading-icon></u-loading-icon>
|
||||
</template>
|
||||
</u--image>
|
||||
</view>
|
||||
<view>
|
||||
<u--text lines="2" lineHeight="24" size="16" @click="gotoDetail(subItem.newsId)"
|
||||
:text="subItem.title"></u--text>
|
||||
<view style="display:flex;margin-top:6px;">
|
||||
<view style="margin-right:20px;">
|
||||
<u--text prefixIcon="account" iconStyle="color:#606266;font-size:14px;margin-right:3px;"
|
||||
size="12" color="#606266" mode="name" :text="subItem.author"></u--text>
|
||||
</view>
|
||||
<view>
|
||||
<u--text prefixIcon="calendar" iconStyle="color:#606266;font-size:14px;margin-right:3px;"
|
||||
size="12" color="#606266" mode="date" :text="subItem.createTime"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"
|
||||
:show="listSwiper.length === 0 && list.length === 0" marginTop="30"></u-empty>
|
||||
<u-loading-page :loading="loading" bg-color="#eef3f7" loadingText="XCWL.cn"></u-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import projectConfig from '@/env.config.js';
|
||||
import { bannerListTrend, listTrend, topListTrend } from '@/apis/modules/trend';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
// 置顶新闻列表
|
||||
list: [],
|
||||
// Banner图列表
|
||||
listSwiper: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 10
|
||||
},
|
||||
};
|
||||
},
|
||||
onShow () {
|
||||
this.getbannerListTrend();
|
||||
this.getlistTrend();
|
||||
},
|
||||
methods: {
|
||||
gotoSwiperDetail (e) {
|
||||
this.gotoDetail(this.listSwiper[e].newsId);
|
||||
},
|
||||
gotoDetail (newsId) {
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/list/trend/trendDetail?newsId=' + newsId
|
||||
});
|
||||
},
|
||||
gotoCategoryTrend (categoryId, categoryName) {
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/list/trend/categoryTrend?categoryId=' + categoryId + '&categoryName=' +
|
||||
categoryName
|
||||
});
|
||||
},
|
||||
getbannerListTrend () {
|
||||
bannerListTrend().then(res => {
|
||||
let { code, data } = res;
|
||||
if (code === 200) {
|
||||
if (data && data.length > 3) {
|
||||
data = data.slice(0, 3);
|
||||
}
|
||||
this.listSwiper = data && data.map(item => {
|
||||
item.imgUrl = projectConfig.baseUrl + item.imgUrl;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
getlistTrend () {
|
||||
topListTrend().then(res => {
|
||||
let { code, data } = res;
|
||||
if (data && data.length > 6) {
|
||||
data = data.slice(0, 6);
|
||||
}
|
||||
this.list = data && data.map((item, i) => {
|
||||
item.newsList.map((subItem, index) => {
|
||||
subItem.imgUrl = projectConfig.baseUrl + subItem.imgUrl;
|
||||
return subItem;
|
||||
});
|
||||
return item;
|
||||
});
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 10px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
</style>
|
729
pages/tabBar/user/user.vue
Normal file
729
pages/tabBar/user/user.vue
Normal file
@ -0,0 +1,729 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="tops">
|
||||
<view class="infos">
|
||||
<view class="account">
|
||||
<image :src="avatarUrl ? avatarUrl : '/static/avatar.png'" mode="aspectFill" @click="handleToAvatar"
|
||||
v-if="token != '' && token != null"></image>
|
||||
<image src="/static/avatar.png" mode="aspectFill" @click="handleToAvatar" v-else></image>
|
||||
<view class="names">
|
||||
<view class="title-wrap">
|
||||
<view class="name-wrap" v-if="wxStatus">
|
||||
<u--text size=16 bold :text="profile.nickName ? profile.nickName : $tt('user.visitor')"
|
||||
color="#fff" @click="handleGotoLogin"></u--text>
|
||||
<span style="margin-left: 16rpx;">
|
||||
<u-icon name="/static/wechat_bind.png" size="22" color="#fff"
|
||||
@click="handleUnbindWeChart"></u-icon>
|
||||
</span>
|
||||
</view>
|
||||
<!-- #ifdef H5-->
|
||||
<view class="name-wrap" v-if="!wxStatus">
|
||||
<u--text size=16 bold :text="profile.nickName ? profile.nickName : $tt('user.visitor')"
|
||||
v-if="token != '' && token != null" color="#fff" @click="gotoAccount()"></u--text>
|
||||
<u--text size=16 bold :text="$tt('user.login')" v-else color="#fff"
|
||||
@click="handleGotoLogin"></u--text>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN-->
|
||||
<view class="name-wrap" v-if="!wxStatus">
|
||||
<u--text size=16 bold :text="profile.nickName ? profile.nickName : $tt('user.visitor')"
|
||||
color="#fff" @click="gotoAccount()" v-if="token != '' && token != null"></u--text>
|
||||
<u--text size=16 bold :text="$tt('user.login')" v-else color="#fff"
|
||||
@click="handleGotoLogin"></u--text>
|
||||
<span style="margin-left: 16rpx;">
|
||||
<u-icon name="/static/wechat_unbind.png" size="22" color="#fff"
|
||||
@click="handleBindWeChart" v-if="token != '' && token != null"></u-icon>
|
||||
</span>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="users">
|
||||
|
||||
<view class="u-item" @click="gotoScene()">
|
||||
<view class="num">
|
||||
<u-icon name="/static/scene(1).png" size="25" color="#fff" labelPos="bottom" labelSize="11"
|
||||
space="10rpx" customStyle=" border-radius:6rpx; padding:20rpx;"></u-icon>
|
||||
</view>
|
||||
<view class="u-tit">{{$tt('user.addScene')}}</view>
|
||||
</view>
|
||||
<view class="u-item" @click="gotoGroup()">
|
||||
<view class="num">
|
||||
<u-icon name="/static/group.png" size="25" color="#fff" labelPos="bottom" labelSize="11"
|
||||
space="10rpx" customStyle=" border-radius:6rpx; padding:20rpx;"></u-icon>
|
||||
</view>
|
||||
<view class="u-tit">{{$tt('user.groupManagement')}}</view>
|
||||
</view>
|
||||
<view class="u-item" @click="gotoEmulator()">
|
||||
<view class="num">
|
||||
<u-icon name="/static/message.png" size="25" color="#fff" labelPos="bottom" labelSize="11"
|
||||
space="10rpx" customStyle=" border-radius:6rpx; padding:20rpx;"></u-icon>
|
||||
</view>
|
||||
<view class="u-tit">{{$tt('user.platformMsg')}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg">
|
||||
<image src="/static/bg.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="main">
|
||||
<view class="list">
|
||||
<view class="group">
|
||||
<!-- 平台消息 -->
|
||||
<view class="item" @click="gotoMessageList()">
|
||||
<view class="left">
|
||||
<u-icon class="iconfont" name="chat-fill" size="22" color="#6699FF"></u-icon>
|
||||
<text class="text">{{$tt('user.platformMsg')}}</text>
|
||||
</view>
|
||||
<view class="right">
|
||||
<u-icon class="iconfont" name="arrow-right" size="12" color="#333"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 多语言 -->
|
||||
<view class="item" @click="gotoLanguage()">
|
||||
<view class="left">
|
||||
<u-icon class="iconfont" name="/static/language.png" size="22" color="#40E0D0"></u-icon>
|
||||
<text class="text">{{$tt('user.language')}}</text>
|
||||
</view>
|
||||
<view class="right">
|
||||
<u-icon class="iconfont" name="arrow-right" size="12" color="#333"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 密码修改 -->
|
||||
<view class="item" @click="gotoResetPsd()" v-if="token != '' && token != null">
|
||||
<view class="left">
|
||||
<u-icon class="iconfont" name="lock-fill" size="22" color="#b73df9"></u-icon>
|
||||
<text class="text">{{$tt('user.updatePassword')}}</text>
|
||||
</view>
|
||||
<view class="right"><u-icon class="iconfont" name="arrow-right" size="12" color="#333"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="group" v-if="token != '' && token != null">
|
||||
<view class="logout" @click="handleExit">
|
||||
<view class="left">
|
||||
<text class="text">{{$tt('user.exitAccount')}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="logout" @click="handleUnsubscribe()">
|
||||
<view class="left">
|
||||
<text class="text">{{$tt('user.signOut')}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="other-wrap">
|
||||
<!-- 退出系统提示框 -->
|
||||
<u-modal :show="show" :title="$tt('user.confirmExit')" :showCancelButton="true" @cancel="cancelExit"
|
||||
@confirm="confirmExit"></u-modal>
|
||||
<!-- 扫码模态框 -->
|
||||
<u-modal :show="modal.show" :content="modal.content" @confirm="confirm" @cancel="cancel"
|
||||
:showConfirmButton="modal.showConfirmButton" showCancelButton></u-modal>
|
||||
<!-- 注销账号提示 -->
|
||||
<u-modal :show="isUnsubscribe" :title="$tt('user.confirmSignOut')" :content="$tt('user.signOutAlert')"
|
||||
:cancelText="$tt('user.notWriteOff')" :confirmText="$tt('user.confirmWriteOff')"
|
||||
:showCancelButton="true" confirmColor="#606266" cancelColor="#2979ff" @cancel="isUnsubscribe = false"
|
||||
@confirm="confirmUnsubscribe"></u-modal>
|
||||
<!-- 解除微信绑定提示框 -->
|
||||
<u-modal :show="isBindWeChart" :title="$tt('user.confirmWxBind')" :content="$tt('user.wxBindAlertTitle')"
|
||||
:cancelText="$tt('user.notBind')" :confirmText="$tt('user.confirmBind')" :showCancelButton="true"
|
||||
@cancel="isBindWeChart = false" @confirm="confirmBindWeChart"></u-modal>
|
||||
<!-- 提示登录 -->
|
||||
<u-modal :show="showModel" :content="$tt('user.notice')" @confirm="confirmGotoLogin"
|
||||
@cancel="() => showModel = false" showCancelButton></u-modal>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import projectConfig from '@/env.config.js';
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
import { logout, secureBind, wechatBind } from '@/apis/modules/common';
|
||||
import { deviceRelateUser } from '@/apis/modules/device';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data () {
|
||||
return {
|
||||
avatarUrl: '', // 头像
|
||||
// 扫码模态窗
|
||||
modal: {
|
||||
show: false,
|
||||
showConfirmButton: false,
|
||||
content: ''
|
||||
},
|
||||
showModel: false,
|
||||
wxStatus: false,
|
||||
scanJson: {}, // 扫码获取的Json
|
||||
// 退出模态窗
|
||||
show: false,
|
||||
isTopShow: false, // 顶部弹出层
|
||||
isUnsubscribe: false, // 是否注销账号
|
||||
isBindWeChart: false, // 是否绑定微信
|
||||
isVipModel: false, // 是否会员升级
|
||||
token: '',
|
||||
};
|
||||
},
|
||||
onShow () {
|
||||
//小程序tabBar导航国际化特殊性
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.setTabBarItem({
|
||||
index: 0,
|
||||
text: this.$tt('navBar.home'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 1,
|
||||
text: this.$tt('navBar.scene'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 2,
|
||||
text: this.$tt('navBar.alert'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 3,
|
||||
text: this.$tt('navBar.news'),
|
||||
})
|
||||
uni.setTabBarItem({
|
||||
index: 4,
|
||||
text: this.$tt('navBar.user'),
|
||||
})
|
||||
//#endif
|
||||
},
|
||||
computed: {
|
||||
...mapState(['isLoggedIn']) // 获取登录状态
|
||||
},
|
||||
onLoad () {
|
||||
this.getToken();
|
||||
if (this.token != '' && this.token != null) {
|
||||
this.getProfile();
|
||||
} else {}
|
||||
|
||||
},
|
||||
methods: {
|
||||
getToken () {
|
||||
// 本地缓存获取token
|
||||
this.token = uni.getStorageSync('token');
|
||||
// vuex存储token
|
||||
uni.$u.vuex('vuex_token', this.token);
|
||||
},
|
||||
...mapMutations(['setLoggedIn']),
|
||||
// 退出系统
|
||||
handleExit () {
|
||||
this.show = true;
|
||||
},
|
||||
// 取消退出系统
|
||||
cancelExit () {
|
||||
this.show = false;
|
||||
},
|
||||
// 确认退出系统
|
||||
confirmExit () {
|
||||
logout().then(() => {
|
||||
this.clearToken();
|
||||
this.setLoggedIn(false);
|
||||
// 跳转
|
||||
uni.reLaunch({
|
||||
url: '/pagesB/login/waitLogin'
|
||||
});
|
||||
});
|
||||
},
|
||||
//移动端微信解绑
|
||||
handleAppSecureBind () {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/user/secureBind'
|
||||
});
|
||||
},
|
||||
gotoAccount () {
|
||||
uni.$u.route('/pagesB/user/account');
|
||||
},
|
||||
//跳转信息
|
||||
gotoMessageList () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/list/user/message'
|
||||
});
|
||||
} else {
|
||||
this.showModel = true;
|
||||
}
|
||||
},
|
||||
gotoLanguage () {
|
||||
//this.clearToken()
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/language/index'
|
||||
})
|
||||
},
|
||||
gotoEmulator () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/list/user/message'
|
||||
});
|
||||
} else {
|
||||
this.showModel = true;
|
||||
}
|
||||
},
|
||||
gotoScene () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
uni.switchTab({
|
||||
url: '/pages/tabBar/scene/index'
|
||||
});
|
||||
} else {
|
||||
this.showModel = true;
|
||||
}
|
||||
},
|
||||
//确定去登录
|
||||
confirmGotoLogin () {
|
||||
// this.clearToken();
|
||||
uni.reLaunch({
|
||||
url: '/pagesB/login/waitLogin'
|
||||
});
|
||||
this.showModel = false;
|
||||
},
|
||||
handleGotoLogin () {
|
||||
this.showModel = true;
|
||||
},
|
||||
// 设备分组
|
||||
gotoGroup () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/user/deviceGroup/index'
|
||||
});
|
||||
} else {
|
||||
this.showModel = true;
|
||||
}
|
||||
},
|
||||
// 清除token
|
||||
clearToken () {
|
||||
uni.setStorageSync('token', '');
|
||||
},
|
||||
getProfile () {
|
||||
// 调用用户信息接口
|
||||
this.$api.common.getProfile().then(res => {
|
||||
//存储用户信息,TODO 需要调用一次,不然其他页面调用返回空
|
||||
uni.$u.vuex('profile', res.data);
|
||||
this.avatarUrl = this.profile.avatar && projectConfig.baseUrl + this.profile.avatar;
|
||||
this.wxStatus = res.wxBind;
|
||||
}).catch(err => {
|
||||
this.$u.toast(err.msg);
|
||||
});
|
||||
},
|
||||
// 注销账户
|
||||
handleUnsubscribe () {
|
||||
this.isUnsubscribe = true;
|
||||
},
|
||||
//跳转密码修改
|
||||
gotoResetPsd () {
|
||||
/*uni.navigateTo({
|
||||
url:"/pagesB/user/resetPsd"
|
||||
})*/
|
||||
uni.$u.route('/pagesB/user/resetPsd');
|
||||
},
|
||||
openhpLink () {
|
||||
uni.navigateTo({
|
||||
url: '/pages/common/webview/index?url=http://www.hpiot.cn/'
|
||||
});
|
||||
},
|
||||
gotoAbout () {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/user/about'
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
confirmUnsubscribe () {
|
||||
logout().then(() => {
|
||||
this.clearToken();
|
||||
// 跳转
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
});
|
||||
},
|
||||
//跳转个人信息
|
||||
handleToAvatar () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
const source = {
|
||||
album: '从手机相册选择',
|
||||
camera: '拍照',
|
||||
};
|
||||
const success = ({
|
||||
tempFilePaths: a,
|
||||
tempFiles: b
|
||||
}) => {
|
||||
const image = a ? a[0] : b[0].path;
|
||||
uni.$u.route('/pagesB/user/avatar', {
|
||||
url: image
|
||||
});
|
||||
};
|
||||
const _uploadImage = (type) => {
|
||||
const sizeType = ['original', 'compressed'];
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType,
|
||||
sourceType: [type],
|
||||
success
|
||||
});
|
||||
}
|
||||
const list = Object.entries(source);
|
||||
// #ifdef H5
|
||||
_uploadImage(list[0][0]);
|
||||
return;
|
||||
// #endif
|
||||
uni.showActionSheet({
|
||||
itemList: list.map(v => v[1]),
|
||||
success: async ({
|
||||
tapIndex: i
|
||||
}) => {
|
||||
// #ifdef APP-PLUS
|
||||
const permissionID = list[i][0] === 'album' ? 'READ_EXTERNAL_STORAGE' : 'CAMERA';
|
||||
let result = await this.$store.dispatch("permission/requestPermissions",
|
||||
permissionID);
|
||||
if (result !== 1) return;
|
||||
// #endif
|
||||
_uploadImage(list[i][0]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.showModel = true;
|
||||
}
|
||||
},
|
||||
// 开通会员
|
||||
handleVipUpgrade () {
|
||||
this.isVipModel = true;
|
||||
},
|
||||
//独立组态
|
||||
gotoScada () {
|
||||
if (this.token != '' && this.token != null) {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/user/scada/indeScada'
|
||||
});
|
||||
} else {
|
||||
this.showModel = true;
|
||||
}
|
||||
},
|
||||
openappLink () {
|
||||
uni.navigateTo({
|
||||
url: '/pages/common/webview/index?url=https://gitee.com/beecue/fastbee-app/tree/master/app'
|
||||
});
|
||||
},
|
||||
// 解绑微信
|
||||
handleUnbindWeChart () {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/user/secureBind'
|
||||
});
|
||||
},
|
||||
// 绑定微信
|
||||
handleBindWeChart () {
|
||||
this.isBindWeChart = true;
|
||||
},
|
||||
confirmBindWeChart () {
|
||||
this.isBindWeChart = false;
|
||||
let that = this;
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: function (res) {
|
||||
if (res) {
|
||||
console.log('用户授权成功');
|
||||
// #ifdef APP-PLUS
|
||||
const params = {
|
||||
sourceClient: 'wechat_open_mobile',
|
||||
openId: res.authResult.openid,
|
||||
unionId: res.authResult.unionid,
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
const params = {
|
||||
code: res.code,
|
||||
sourceClient: 'wechat_open_mini_program',
|
||||
}
|
||||
// #endif
|
||||
wechatBind(params).then(res => {
|
||||
if (res.code == 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '绑定成功!',
|
||||
});
|
||||
that.getProfile();
|
||||
} else {
|
||||
uni.$u.toast(res.msg);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.content {
|
||||
background-color: #f5f5f5;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.tops {
|
||||
position: relative;
|
||||
height: 450rpx;
|
||||
background-color: #007AFF;
|
||||
/* background: url("https://img1.baidu.com/it/u=3643237076,897388802&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=400") no-repeat; */
|
||||
background-size: 100% 100%;
|
||||
z-index: 1;
|
||||
margin-bottom: 150rpx;
|
||||
}
|
||||
|
||||
.setIcon {
|
||||
position: absolute;
|
||||
top: 50rpx;
|
||||
right: 50rpx;
|
||||
}
|
||||
|
||||
.main {
|
||||
// width: 100%;
|
||||
min-height: 200rpx;
|
||||
background: #f5f5f5;
|
||||
// border-radius: 30rpx;
|
||||
// transform: translateY(-30rpx);
|
||||
margin: 30rpx;
|
||||
z-index: 0;
|
||||
|
||||
.list {
|
||||
.group {
|
||||
border-radius: 20rpx;
|
||||
background-color: #fff;
|
||||
padding: 15rpx 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 5px 5px 5px 0px #eaeaea;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 30rpx;
|
||||
color: #555;
|
||||
border-bottom: 1rpx solid #f8f8f8;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.iconfont {
|
||||
font-size: 38rpx;
|
||||
// margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.right {
|
||||
.iconfont {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logout {
|
||||
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15rpx 0;
|
||||
font-size: 32rpx;
|
||||
color: #555;
|
||||
border-bottom: 1rpx solid #f8f8f8;
|
||||
|
||||
}
|
||||
|
||||
.logout:last-child {
|
||||
border: none;
|
||||
|
||||
}
|
||||
|
||||
.item:last-child {
|
||||
border: none;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.group:last-child {
|
||||
border: none;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logout {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tops .infos {
|
||||
width: 92%;
|
||||
height: 200rpx;
|
||||
position: absolute;
|
||||
top: 35%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -25%);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
|
||||
}
|
||||
|
||||
.infos image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #fff;
|
||||
}
|
||||
|
||||
.infos .names {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
margin: 20rpx 0 10rpx;
|
||||
|
||||
.name-wrap {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.infos .desc {
|
||||
max-width: 62%;
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
margin: 10rpx auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.infos .t-list {
|
||||
margin-top: 60rpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.t-item .tit {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.t-item .vals {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.users {
|
||||
margin-top: 30rpx;
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
height: 200rpx;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
box-shadow: 1px 10rpx 20rpx #ececec;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
z-index: 3;
|
||||
|
||||
}
|
||||
|
||||
.u-top {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.users .u-top image {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.u-top .tit {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.u-item {
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
.u-item .u-tit {
|
||||
color: #757575;
|
||||
font-size: 26rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.u-item .num {
|
||||
color: #000000;
|
||||
font-size: 33rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: blur(3px);
|
||||
/*transform: scale(2); */
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.borderless-button {
|
||||
border: none !important;
|
||||
background-color: transparent;
|
||||
color: #007bff;
|
||||
/* 设置文字颜色 */
|
||||
}
|
||||
|
||||
/deep/.uni-list-item__content-title[data-v-296a3d7e] {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/deep/.uni-list-item__container {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.other-wrap {
|
||||
.popup-title {
|
||||
margin: 20px 20px;
|
||||
}
|
||||
|
||||
.btnclass {
|
||||
margin: 0px 20px 20px 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
821
pagesA/components/uni-data-checkbox/index.vue
Normal file
821
pagesA/components/uni-data-checkbox/index.vue
Normal file
@ -0,0 +1,821 @@
|
||||
<template>
|
||||
<view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}">
|
||||
<template v-if="!isLocal">
|
||||
<view class="uni-data-loading">
|
||||
<uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18" :content-text="contentText"></uni-load-more>
|
||||
<text v-else>{{mixinDatacomErrorMessage}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}" @change="chagne">
|
||||
<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
|
||||
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
|
||||
<checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''" :checked="item.selected" />
|
||||
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="checkbox__inner" :style="item.styleIcon">
|
||||
<view class="checkbox__inner-icon"></view>
|
||||
</view>
|
||||
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
|
||||
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
|
||||
<view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view>
|
||||
</view>
|
||||
</label>
|
||||
</checkbox-group>
|
||||
<radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="chagne">
|
||||
<!-- -->
|
||||
<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
|
||||
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
|
||||
<radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''" :checked="item.selected" />
|
||||
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner"
|
||||
:style="item.styleBackgroud">
|
||||
<view class="radio__inner-icon" :style="item.styleIcon"></view>
|
||||
</view>
|
||||
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
|
||||
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
|
||||
<view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view>
|
||||
</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* DataChecklist 数据选择器
|
||||
* @description 通过数据渲染 checkbox 和 radio
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
|
||||
* @property {String} mode = [default| list | button | tag] 显示模式
|
||||
* @value default 默认横排模式
|
||||
* @value list 列表模式
|
||||
* @value button 按钮模式
|
||||
* @value tag 标签模式
|
||||
* @property {Boolean} multiple = [true|false] 是否多选
|
||||
* @property {Array|String|Number} value 默认值
|
||||
* @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
|
||||
* @property {Number|String} min 最小选择个数 ,multiple为true时生效
|
||||
* @property {Number|String} max 最大选择个数 ,multiple为true时生效
|
||||
* @property {Boolean} wrap 是否换行显示
|
||||
* @property {String} icon = [left|right] list 列表模式下icon显示位置
|
||||
* @property {Boolean} selectedColor 选中颜色
|
||||
* @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
|
||||
* @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示
|
||||
* @property {Object} map 字段映射, 默认 map={text:'text',value:'value'}
|
||||
* @value left 左侧显示
|
||||
* @value right 右侧显示
|
||||
* @event {Function} change 选中发生变化触发
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'uniDataChecklist',
|
||||
mixins: [uniCloud.mixinDatacom || {}],
|
||||
emits:['input','update:modelValue','change'],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: [Array, String, Number],
|
||||
default () {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
// TODO vue3
|
||||
modelValue: {
|
||||
type: [Array, String, Number],
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
localdata: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
min: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
max: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
wrap: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
selectedColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selectedTextColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
emptyText:{
|
||||
type: String,
|
||||
default: '暂无数据'
|
||||
},
|
||||
disabled:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
map:{
|
||||
type: Object,
|
||||
default(){
|
||||
return {
|
||||
text:'text',
|
||||
value:'value'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
localdata: {
|
||||
handler(newVal) {
|
||||
this.range = newVal
|
||||
this.dataList = this.getDataList(this.getSelectedValue(newVal))
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
mixinDatacomResData(newVal) {
|
||||
this.range = newVal
|
||||
this.dataList = this.getDataList(this.getSelectedValue(newVal))
|
||||
},
|
||||
value(newVal) {
|
||||
this.dataList = this.getDataList(newVal)
|
||||
// fix by mehaotian is_reset 在 uni-forms 中定义
|
||||
// if(!this.is_reset){
|
||||
// this.is_reset = false
|
||||
// this.formItem && this.formItem.setValue(newVal)
|
||||
// }
|
||||
},
|
||||
modelValue(newVal) {
|
||||
this.dataList = this.getDataList(newVal);
|
||||
// if(!this.is_reset){
|
||||
// this.is_reset = false
|
||||
// this.formItem && this.formItem.setValue(newVal)
|
||||
// }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataList: [],
|
||||
range: [],
|
||||
contentText: {
|
||||
contentdown: '查看更多',
|
||||
contentrefresh: '加载中',
|
||||
contentnomore: '没有更多'
|
||||
},
|
||||
isLocal:true,
|
||||
styles: {
|
||||
selectedColor: '#2979ff',
|
||||
selectedTextColor: '#666',
|
||||
},
|
||||
isTop:0
|
||||
};
|
||||
},
|
||||
computed:{
|
||||
dataValue(){
|
||||
if(this.value === '')return this.modelValue
|
||||
if(this.modelValue === '') return this.value
|
||||
return this.value
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// this.form = this.getForm('uniForms')
|
||||
// this.formItem = this.getForm('uniFormsItem')
|
||||
// this.formItem && this.formItem.setValue(this.value)
|
||||
|
||||
// if (this.formItem) {
|
||||
// this.isTop = 6
|
||||
// if (this.formItem.name) {
|
||||
// // 如果存在name添加默认值,否则formData 中不存在这个字段不校验
|
||||
// if(!this.is_reset){
|
||||
// this.is_reset = false
|
||||
// this.formItem.setValue(this.dataValue)
|
||||
// }
|
||||
// this.rename = this.formItem.name
|
||||
// this.form.inputChildrens.push(this)
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this.localdata && this.localdata.length !== 0) {
|
||||
this.isLocal = true
|
||||
this.range = this.localdata
|
||||
this.dataList = this.getDataList(this.getSelectedValue(this.range))
|
||||
} else {
|
||||
if (this.collection) {
|
||||
this.isLocal = false
|
||||
this.loadData()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
this.mixinDatacomGet().then(res=>{
|
||||
this.mixinDatacomResData = res.result.data
|
||||
if(this.mixinDatacomResData.length === 0){
|
||||
this.isLocal = false
|
||||
this.mixinDatacomErrorMessage = this.emptyText
|
||||
}else{
|
||||
this.isLocal = true
|
||||
}
|
||||
}).catch(err=>{
|
||||
this.mixinDatacomErrorMessage = err.message
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取父元素实例
|
||||
*/
|
||||
getForm(name = 'uniForms') {
|
||||
let parent = this.$parent;
|
||||
let parentName = parent.$options.name;
|
||||
while (parentName !== name) {
|
||||
parent = parent.$parent;
|
||||
if (!parent) return false
|
||||
parentName = parent.$options.name;
|
||||
}
|
||||
return parent;
|
||||
},
|
||||
chagne(e) {
|
||||
const values = e.detail.value
|
||||
|
||||
let detail = {
|
||||
value: [],
|
||||
data: []
|
||||
}
|
||||
|
||||
if (this.multiple) {
|
||||
this.range.forEach(item => {
|
||||
|
||||
if (values.includes(item[this.map.value] + '')) {
|
||||
detail.value.push(item[this.map.value])
|
||||
detail.data.push(item)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const range = this.range.find(item => (item[this.map.value] + '') === values)
|
||||
if (range) {
|
||||
detail = {
|
||||
value: range[this.map.value],
|
||||
data: range
|
||||
}
|
||||
}
|
||||
}
|
||||
// this.formItem && this.formItem.setValue(detail.value)
|
||||
// TODO 兼容 vue2
|
||||
this.$emit('input', detail.value);
|
||||
// // TOTO 兼容 vue3
|
||||
this.$emit('update:modelValue', detail.value);
|
||||
this.$emit('change', {
|
||||
detail
|
||||
})
|
||||
if (this.multiple) {
|
||||
// 如果 v-model 没有绑定 ,则走内部逻辑
|
||||
// if (this.value.length === 0) {
|
||||
this.dataList = this.getDataList(detail.value, true)
|
||||
// }
|
||||
} else {
|
||||
this.dataList = this.getDataList(detail.value)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取渲染的新数组
|
||||
* @param {Object} value 选中内容
|
||||
*/
|
||||
getDataList(value) {
|
||||
// 解除引用关系,破坏原引用关系,避免污染源数据
|
||||
let dataList = JSON.parse(JSON.stringify(this.range))
|
||||
let list = []
|
||||
if (this.multiple) {
|
||||
if (!Array.isArray(value)) {
|
||||
value = []
|
||||
}
|
||||
}
|
||||
dataList.forEach((item, index) => {
|
||||
item.disabled = item.disable || item.disabled || false
|
||||
if (this.multiple) {
|
||||
if (value.length > 0) {
|
||||
let have = value.find(val => val === item[this.map.value])
|
||||
item.selected = have !== undefined
|
||||
} else {
|
||||
item.selected = false
|
||||
}
|
||||
} else {
|
||||
item.selected = value === item[this.map.value]
|
||||
}
|
||||
|
||||
list.push(item)
|
||||
})
|
||||
return this.setRange(list)
|
||||
},
|
||||
/**
|
||||
* 处理最大最小值
|
||||
* @param {Object} list
|
||||
*/
|
||||
setRange(list) {
|
||||
let selectList = list.filter(item => item.selected)
|
||||
let min = Number(this.min) || 0
|
||||
let max = Number(this.max) || ''
|
||||
list.forEach((item, index) => {
|
||||
if (this.multiple) {
|
||||
if (selectList.length <= min) {
|
||||
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
|
||||
if (have !== undefined) {
|
||||
item.disabled = true
|
||||
}
|
||||
}
|
||||
|
||||
if (selectList.length >= max && max !== '') {
|
||||
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
|
||||
if (have === undefined) {
|
||||
item.disabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setStyles(item, index)
|
||||
list[index] = item
|
||||
})
|
||||
return list
|
||||
},
|
||||
/**
|
||||
* 设置 class
|
||||
* @param {Object} item
|
||||
* @param {Object} index
|
||||
*/
|
||||
setStyles(item, index) {
|
||||
// 设置自定义样式
|
||||
item.styleBackgroud = this.setStyleBackgroud(item)
|
||||
item.styleIcon = this.setStyleIcon(item)
|
||||
item.styleIconText = this.setStyleIconText(item)
|
||||
item.styleRightIcon = this.setStyleRightIcon(item)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取选中值
|
||||
* @param {Object} range
|
||||
*/
|
||||
getSelectedValue(range) {
|
||||
if (!this.multiple) return this.dataValue
|
||||
let selectedArr = []
|
||||
range.forEach((item) => {
|
||||
if (item.selected) {
|
||||
selectedArr.push(item[this.map.value])
|
||||
}
|
||||
})
|
||||
return this.dataValue.length > 0 ? this.dataValue : selectedArr
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置背景样式
|
||||
*/
|
||||
setStyleBackgroud(item) {
|
||||
let styles = {}
|
||||
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
|
||||
if (this.selectedColor) {
|
||||
if (this.mode !== 'list') {
|
||||
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
|
||||
}
|
||||
if (this.mode === 'tag') {
|
||||
styles['background-color'] = item.selected? selectedColor:'#f5f5f5'
|
||||
}
|
||||
}
|
||||
let classles = ''
|
||||
for (let i in styles) {
|
||||
classles += `${i}:${styles[i]};`
|
||||
}
|
||||
return classles
|
||||
},
|
||||
setStyleIcon(item) {
|
||||
let styles = {}
|
||||
let classles = ''
|
||||
if (this.selectedColor) {
|
||||
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
|
||||
styles['background-color'] = item.selected?selectedColor:'#fff'
|
||||
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
|
||||
|
||||
if(!item.selected && item.disabled){
|
||||
styles['background-color'] = '#F2F6FC'
|
||||
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
|
||||
}
|
||||
}
|
||||
for (let i in styles) {
|
||||
classles += `${i}:${styles[i]};`
|
||||
}
|
||||
return classles
|
||||
},
|
||||
setStyleIconText(item) {
|
||||
let styles = {}
|
||||
let classles = ''
|
||||
if (this.selectedColor) {
|
||||
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
|
||||
if (this.mode === 'tag') {
|
||||
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:'#fff'):'#666'
|
||||
} else {
|
||||
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:selectedColor):'#666'
|
||||
}
|
||||
if(!item.selected && item.disabled){
|
||||
styles.color = '#999'
|
||||
}
|
||||
}
|
||||
for (let i in styles) {
|
||||
classles += `${i}:${styles[i]};`
|
||||
}
|
||||
return classles
|
||||
},
|
||||
setStyleRightIcon(item) {
|
||||
let styles = {}
|
||||
let classles = ''
|
||||
if (this.mode === 'list') {
|
||||
styles['border-color'] = item.selected?this.styles.selectedColor:'#DCDFE6'
|
||||
}
|
||||
for (let i in styles) {
|
||||
classles += `${i}:${styles[i]};`
|
||||
}
|
||||
|
||||
return classles
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$uni-primary: #2979ff !default;
|
||||
$border-color: #DCDFE6;
|
||||
$disable:0.4;
|
||||
|
||||
@mixin flex {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-data-loading {
|
||||
@include flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
padding-left: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.uni-data-checklist {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
flex: 1;
|
||||
// 多选样式
|
||||
.checklist-group {
|
||||
@include flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.is-list {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.checklist-box {
|
||||
@include flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
margin: 5px 0;
|
||||
margin-right: 25px;
|
||||
|
||||
.hidden {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
// 文字样式
|
||||
.checklist-content {
|
||||
@include flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.checklist-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-left: 5px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.checkobx__list {
|
||||
border-right-width: 1px;
|
||||
border-right-color: #007aff;
|
||||
border-right-style: solid;
|
||||
border-bottom-width:1px;
|
||||
border-bottom-color: #007aff;
|
||||
border-bottom-style: solid;
|
||||
height: 12px;
|
||||
width: 6px;
|
||||
left: -5px;
|
||||
transform-origin: center;
|
||||
transform: rotate(45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 多选样式
|
||||
.checkbox__inner {
|
||||
/* #ifndef APP-NVUE */
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
z-index: 1;
|
||||
.checkbox__inner-icon {
|
||||
position: absolute;
|
||||
/* #ifdef APP-NVUE */
|
||||
top: 2px;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
top: 1px;
|
||||
/* #endif */
|
||||
left: 5px;
|
||||
height: 8px;
|
||||
width: 4px;
|
||||
border-right-width: 1px;
|
||||
border-right-color: #fff;
|
||||
border-right-style: solid;
|
||||
border-bottom-width:1px ;
|
||||
border-bottom-color: #fff;
|
||||
border-bottom-style: solid;
|
||||
opacity: 0;
|
||||
transform-origin: center;
|
||||
transform: rotate(40deg);
|
||||
}
|
||||
}
|
||||
|
||||
// 单选样式
|
||||
.radio__inner {
|
||||
@include flex;
|
||||
/* #ifndef APP-NVUE */
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 16px;
|
||||
background-color: #fff;
|
||||
z-index: 1;
|
||||
|
||||
.radio__inner-icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 10px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认样式
|
||||
&.is--default {
|
||||
|
||||
// 禁用
|
||||
&.is-disable {
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
.checkbox__inner {
|
||||
background-color: #F2F6FC;
|
||||
border-color: $border-color;
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.radio__inner {
|
||||
background-color: #F2F6FC;
|
||||
border-color: $border-color;
|
||||
}
|
||||
.checklist-text {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
// 选中
|
||||
&.is-checked {
|
||||
.checkbox__inner {
|
||||
border-color: $uni-primary;
|
||||
background-color: $uni-primary;
|
||||
|
||||
.checkbox__inner-icon {
|
||||
opacity: 1;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
.radio__inner {
|
||||
border-color: $uni-primary;
|
||||
.radio__inner-icon {
|
||||
opacity: 1;
|
||||
background-color: $uni-primary;
|
||||
}
|
||||
}
|
||||
.checklist-text {
|
||||
color: $uni-primary;
|
||||
}
|
||||
// 选中禁用
|
||||
&.is-disable {
|
||||
.checkbox__inner {
|
||||
opacity: $disable;
|
||||
}
|
||||
|
||||
.checklist-text {
|
||||
opacity: $disable;
|
||||
}
|
||||
.radio__inner {
|
||||
opacity: $disable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮样式
|
||||
&.is--button {
|
||||
margin-right: 10px;
|
||||
padding: 5px 10px;
|
||||
border: 1px $border-color solid;
|
||||
border-radius: 3px;
|
||||
transition: border-color 0.2s;
|
||||
|
||||
// 禁用
|
||||
&.is-disable {
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
border: 1px #eee solid;
|
||||
opacity: $disable;
|
||||
.checkbox__inner {
|
||||
background-color: #F2F6FC;
|
||||
border-color: $border-color;
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
}
|
||||
.radio__inner {
|
||||
background-color: #F2F6FC;
|
||||
border-color: $border-color;
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
}
|
||||
.checklist-text {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-checked {
|
||||
border-color: $uni-primary;
|
||||
.checkbox__inner {
|
||||
border-color: $uni-primary;
|
||||
background-color: $uni-primary;
|
||||
.checkbox__inner-icon {
|
||||
opacity: 1;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.radio__inner {
|
||||
border-color: $uni-primary;
|
||||
|
||||
.radio__inner-icon {
|
||||
opacity: 1;
|
||||
background-color: $uni-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.checklist-text {
|
||||
color: $uni-primary;
|
||||
}
|
||||
|
||||
// 选中禁用
|
||||
&.is-disable {
|
||||
opacity: $disable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 标签样式
|
||||
&.is--tag {
|
||||
margin-right: 10px;
|
||||
padding: 5px 10px;
|
||||
border: 1px $border-color solid;
|
||||
border-radius: 3px;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.checklist-text {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
// 禁用
|
||||
&.is-disable {
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
opacity: $disable;
|
||||
}
|
||||
|
||||
&.is-checked {
|
||||
background-color: $uni-primary;
|
||||
border-color: $uni-primary;
|
||||
|
||||
.checklist-text {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 列表样式
|
||||
&.is--list {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
padding: 10px 15px;
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
|
||||
&.is-list-border {
|
||||
border-top: 1px #eee solid;
|
||||
}
|
||||
|
||||
// 禁用
|
||||
&.is-disable {
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
.checkbox__inner {
|
||||
background-color: #F2F6FC;
|
||||
border-color: $border-color;
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
}
|
||||
.checklist-text {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-checked {
|
||||
.checkbox__inner {
|
||||
border-color: $uni-primary;
|
||||
background-color: $uni-primary;
|
||||
|
||||
.checkbox__inner-icon {
|
||||
opacity: 1;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
.radio__inner {
|
||||
.radio__inner-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.checklist-text {
|
||||
color: $uni-primary;
|
||||
}
|
||||
|
||||
.checklist-content {
|
||||
.checkobx__list {
|
||||
opacity: 1;
|
||||
border-color: $uni-primary;
|
||||
}
|
||||
}
|
||||
|
||||
// 选中禁用
|
||||
&.is-disable {
|
||||
.checkbox__inner {
|
||||
opacity: $disable;
|
||||
}
|
||||
|
||||
.checklist-text {
|
||||
opacity: $disable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
5
pagesA/components/uni-pagination/i18n/en.json
Normal file
5
pagesA/components/uni-pagination/i18n/en.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"uni-pagination.prevText": "prev",
|
||||
"uni-pagination.nextText": "next",
|
||||
"uni-pagination.piecePerPage": "piece/page"
|
||||
}
|
5
pagesA/components/uni-pagination/i18n/es.json
Normal file
5
pagesA/components/uni-pagination/i18n/es.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"uni-pagination.prevText": "anterior",
|
||||
"uni-pagination.nextText": "prxima",
|
||||
"uni-pagination.piecePerPage": "Art¨ªculo/P¨¢gina"
|
||||
}
|
5
pagesA/components/uni-pagination/i18n/fr.json
Normal file
5
pagesA/components/uni-pagination/i18n/fr.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"uni-pagination.prevText": "précédente",
|
||||
"uni-pagination.nextText": "suivante",
|
||||
"uni-pagination.piecePerPage": "Articles/Pages"
|
||||
}
|
12
pagesA/components/uni-pagination/i18n/index.js
Normal file
12
pagesA/components/uni-pagination/i18n/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
import en from './en.json'
|
||||
import es from './es.json'
|
||||
import fr from './fr.json'
|
||||
import zhHans from './zh-Hans.json'
|
||||
import zhHant from './zh-Hant.json'
|
||||
export default {
|
||||
en,
|
||||
es,
|
||||
fr,
|
||||
'zh-Hans': zhHans,
|
||||
'zh-Hant': zhHant
|
||||
}
|
5
pagesA/components/uni-pagination/i18n/zh-Hans.json
Normal file
5
pagesA/components/uni-pagination/i18n/zh-Hans.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"uni-pagination.prevText": "上一页",
|
||||
"uni-pagination.nextText": "下一页",
|
||||
"uni-pagination.piecePerPage": "条/页"
|
||||
}
|
5
pagesA/components/uni-pagination/i18n/zh-Hant.json
Normal file
5
pagesA/components/uni-pagination/i18n/zh-Hant.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"uni-pagination.prevText": "上一頁",
|
||||
"uni-pagination.nextText": "下一頁",
|
||||
"uni-pagination.piecePerPage": "條/頁"
|
||||
}
|
465
pagesA/components/uni-pagination/index.vue
Normal file
465
pagesA/components/uni-pagination/index.vue
Normal file
@ -0,0 +1,465 @@
|
||||
<template>
|
||||
<view class="uni-pagination">
|
||||
<!-- #ifndef MP -->
|
||||
<picker v-if="showPageSize === true || showPageSize === 'true'" class="select-picker" mode="selector"
|
||||
:value="pageSizeIndex" :range="pageSizeRange" @change="pickerChange" @cancel="pickerClick"
|
||||
@click.native="pickerClick">
|
||||
<button type="default" size="mini" :plain="true">
|
||||
<text>{{pageSizeRange[pageSizeIndex]}} {{piecePerPage}}</text>
|
||||
<uni-icons class="select-picker-icon" type="arrowdown" size="12" color="#999"></uni-icons>
|
||||
</button>
|
||||
</picker>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view class="uni-pagination__total is-phone-hide">共 {{ total }} 条</view>
|
||||
<!-- #endif -->
|
||||
<view class="uni-pagination__btn"
|
||||
:class="currentIndex === 1 ? 'uni-pagination--disabled' : 'uni-pagination--enabled'"
|
||||
:hover-class="currentIndex === 1 ? '' : 'uni-pagination--hover'" :hover-start-time="20"
|
||||
:hover-stay-time="70" @click="clickLeft">
|
||||
<template v-if="showIcon === true || showIcon === 'true'">
|
||||
<uni-icons color="#666" size="16" type="left" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="uni-pagination__child-btn">{{ prevPageText }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<view class="uni-pagination__num uni-pagination__num-flex-none">
|
||||
<view class="uni-pagination__num-current">
|
||||
<text class="uni-pagination__num-current-text is-pc-hide current-index-text">{{ currentIndex }}</text>
|
||||
<text class="uni-pagination__num-current-text is-pc-hide">/{{ maxPage || 0 }}</text>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view v-for="(item, index) in paper" :key="index" :class="{ 'page--active': item === currentIndex }"
|
||||
class="uni-pagination__num-tag tag--active is-phone-hide" @click.top="selectPage(item, index)">
|
||||
<text>{{ item }}</text>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="uni-pagination__btn"
|
||||
:class="currentIndex >= maxPage ? 'uni-pagination--disabled' : 'uni-pagination--enabled'"
|
||||
:hover-class="currentIndex === maxPage ? '' : 'uni-pagination--hover'" :hover-start-time="20"
|
||||
:hover-stay-time="70" @click="clickRight">
|
||||
<template v-if="showIcon === true || showIcon === 'true'">
|
||||
<uni-icons color="#666" size="16" type="right" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="uni-pagination__child-btn">{{ nextPageText }}</text>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Pagination 分页器
|
||||
* @description 分页器组件,用于展示页码、请求数据等
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=32
|
||||
* @property {String} prevText 左侧按钮文字
|
||||
* @property {String} nextText 右侧按钮文字
|
||||
* @property {String} piecePerPageText 条/页文字
|
||||
* @property {Number} current 当前页
|
||||
* @property {Number} total 数据总量
|
||||
* @property {Number} pageSize 每页数据量
|
||||
* @property {Boolean} showIcon = [true|false] 是否以 icon 形式展示按钮
|
||||
* @property {Boolean} showPageSize = [true|false] 是否展示每页条数
|
||||
* @property {Array} pageSizeRange = [20, 50, 100, 500] 每页条数选框
|
||||
* @event {Function} change 点击页码按钮时触发 ,e={type,current} current为当前页,type值为:next/prev,表示点击的是上一页还是下一个
|
||||
* * @event {Function} pageSizeChange 当前每页条数改变时触发 ,e={pageSize} pageSize 为当前所选的每页条数
|
||||
*/
|
||||
|
||||
import {
|
||||
initVueI18n
|
||||
} from '@dcloudio/uni-i18n'
|
||||
import messages from './i18n/index.js'
|
||||
const {
|
||||
t
|
||||
} = initVueI18n(messages)
|
||||
export default {
|
||||
name: 'UniPagination',
|
||||
emits: ['update:modelValue', 'input', 'change', 'pageSizeChange'],
|
||||
props: {
|
||||
value: {
|
||||
type: [Number, String],
|
||||
default: 1
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number, String],
|
||||
default: 1
|
||||
},
|
||||
prevText: {
|
||||
type: String,
|
||||
},
|
||||
nextText: {
|
||||
type: String,
|
||||
},
|
||||
piecePerPageText: {
|
||||
type: String
|
||||
},
|
||||
current: {
|
||||
type: [Number, String],
|
||||
default: 1
|
||||
},
|
||||
total: {
|
||||
// 数据总量
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
pageSize: {
|
||||
// 每页数据量
|
||||
type: [Number, String],
|
||||
default: 10
|
||||
},
|
||||
showIcon: {
|
||||
// 是否以 icon 形式展示按钮
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
showPageSize: {
|
||||
// 是否以 icon 形式展示按钮
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
pagerCount: {
|
||||
type: Number,
|
||||
default: 7
|
||||
},
|
||||
pageSizeRange: {
|
||||
type: Array,
|
||||
default: () => [20, 50, 100, 500]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageSizeIndex: 0,
|
||||
currentIndex: 1,
|
||||
paperData: [],
|
||||
pickerShow: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
piecePerPage() {
|
||||
return this.piecePerPageText || t('uni-pagination.piecePerPage')
|
||||
},
|
||||
prevPageText() {
|
||||
return this.prevText || t('uni-pagination.prevText')
|
||||
},
|
||||
nextPageText() {
|
||||
return this.nextText || t('uni-pagination.nextText')
|
||||
},
|
||||
maxPage() {
|
||||
let maxPage = 1
|
||||
let total = Number(this.total)
|
||||
let pageSize = Number(this.pageSize)
|
||||
if (total && pageSize) {
|
||||
maxPage = Math.ceil(total / pageSize)
|
||||
}
|
||||
return maxPage
|
||||
},
|
||||
paper() {
|
||||
const num = this.currentIndex
|
||||
// TODO 最大页数
|
||||
const pagerCount = this.pagerCount
|
||||
// const total = 181
|
||||
const total = this.total
|
||||
const pageSize = this.pageSize
|
||||
let totalArr = []
|
||||
let showPagerArr = []
|
||||
let pagerNum = Math.ceil(total / pageSize)
|
||||
for (let i = 0; i < pagerNum; i++) {
|
||||
totalArr.push(i + 1)
|
||||
}
|
||||
showPagerArr.push(1)
|
||||
const totalNum = totalArr[totalArr.length - (pagerCount + 1) / 2]
|
||||
totalArr.forEach((item, index) => {
|
||||
if ((pagerCount + 1) / 2 >= num) {
|
||||
if (item < pagerCount + 1 && item > 1) {
|
||||
showPagerArr.push(item)
|
||||
}
|
||||
} else if (num + 2 <= totalNum) {
|
||||
if (item > num - (pagerCount + 1) / 2 && item < num + (pagerCount + 1) / 2) {
|
||||
showPagerArr.push(item)
|
||||
}
|
||||
} else {
|
||||
if ((item > num - (pagerCount + 1) / 2 || pagerNum - pagerCount < item) && item < totalArr[
|
||||
totalArr.length - 1]) {
|
||||
showPagerArr.push(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (pagerNum > pagerCount) {
|
||||
if ((pagerCount + 1) / 2 >= num) {
|
||||
showPagerArr[showPagerArr.length - 1] = '...'
|
||||
} else if (num + 2 <= totalNum) {
|
||||
showPagerArr[1] = '...'
|
||||
showPagerArr[showPagerArr.length - 1] = '...'
|
||||
} else {
|
||||
showPagerArr[1] = '...'
|
||||
}
|
||||
showPagerArr.push(totalArr[totalArr.length - 1])
|
||||
} else {
|
||||
if ((pagerCount + 1) / 2 >= num) {} else if (num + 2 <= totalNum) {} else {
|
||||
showPagerArr.shift()
|
||||
showPagerArr.push(totalArr[totalArr.length - 1])
|
||||
}
|
||||
}
|
||||
|
||||
return showPagerArr
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
current: {
|
||||
immediate: true,
|
||||
handler(val, old) {
|
||||
if (val < 1) {
|
||||
this.currentIndex = 1
|
||||
} else {
|
||||
this.currentIndex = val
|
||||
}
|
||||
}
|
||||
},
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (Number(this.current) !== 1) return
|
||||
if (val < 1) {
|
||||
this.currentIndex = 1
|
||||
} else {
|
||||
this.currentIndex = val
|
||||
}
|
||||
}
|
||||
},
|
||||
pageSizeIndex(val) {
|
||||
this.$emit('pageSizeChange', this.pageSizeRange[val])
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pickerChange(e) {
|
||||
this.pageSizeIndex = e.detail.value
|
||||
this.pickerClick()
|
||||
},
|
||||
pickerClick() {
|
||||
// #ifdef H5
|
||||
const body = document.querySelector('body')
|
||||
if (!body) return
|
||||
|
||||
const className = 'uni-pagination-picker-show'
|
||||
this.pickerShow = !this.pickerShow
|
||||
|
||||
if (this.pickerShow) {
|
||||
body.classList.add(className)
|
||||
} else {
|
||||
setTimeout(() => body.classList.remove(className), 300)
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// 选择标签
|
||||
selectPage(e, index) {
|
||||
if (parseInt(e)) {
|
||||
this.currentIndex = e
|
||||
this.change('current')
|
||||
} else {
|
||||
let pagerNum = Math.ceil(this.total / this.pageSize)
|
||||
// let pagerNum = Math.ceil(181 / this.pageSize)
|
||||
// 上一页
|
||||
if (index <= 1) {
|
||||
if (this.currentIndex - 5 > 1) {
|
||||
this.currentIndex -= 5
|
||||
} else {
|
||||
this.currentIndex = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
// 下一页
|
||||
if (index >= 6) {
|
||||
if (this.currentIndex + 5 > pagerNum) {
|
||||
this.currentIndex = pagerNum
|
||||
} else {
|
||||
this.currentIndex += 5
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
clickLeft() {
|
||||
if (Number(this.currentIndex) === 1) {
|
||||
return
|
||||
}
|
||||
this.currentIndex -= 1
|
||||
this.change('prev')
|
||||
},
|
||||
clickRight() {
|
||||
if (Number(this.currentIndex) >= this.maxPage) {
|
||||
return
|
||||
}
|
||||
this.currentIndex += 1
|
||||
this.change('next')
|
||||
},
|
||||
change(e) {
|
||||
this.$emit('input', this.currentIndex)
|
||||
this.$emit('update:modelValue', this.currentIndex)
|
||||
this.$emit('change', {
|
||||
type: e,
|
||||
current: this.currentIndex
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$uni-primary: #2979ff !default;
|
||||
.uni-pagination {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-pagination__total {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.uni-pagination__btn {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
padding: 0 8px;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
background-color: #F0F0F0;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
// border-width: 1px;
|
||||
// border-style: solid;
|
||||
// border-color: $uni-border-color;
|
||||
}
|
||||
|
||||
.uni-pagination__child-btn {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.uni-pagination__num {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.uni-pagination__num-tag {
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
min-width: 30px;
|
||||
/* #endif */
|
||||
margin: 0 5px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
// border: 1px red solid;
|
||||
color: #999;
|
||||
border-radius: 4px;
|
||||
// border-width: 1px;
|
||||
// border-style: solid;
|
||||
// border-color: $uni-border-color;
|
||||
}
|
||||
|
||||
.uni-pagination__num-current {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.uni-pagination__num-current-text {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.current-index-text{
|
||||
color: $uni-primary;
|
||||
}
|
||||
|
||||
.uni-pagination--enabled {
|
||||
color: #333333;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.uni-pagination--disabled {
|
||||
opacity: 0.5;
|
||||
/* #ifdef H5 */
|
||||
cursor: default;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-pagination--hover {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.tag--active:hover {
|
||||
color: $uni-primary;
|
||||
}
|
||||
|
||||
.page--active {
|
||||
color: #fff;
|
||||
background-color: $uni-primary;
|
||||
}
|
||||
|
||||
.page--active:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
.is-pc-hide {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.is-phone-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 450px) {
|
||||
.is-pc-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.is-phone-hide {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.uni-pagination__num-flex-none {
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
</style>
|
285
pagesA/device/share/detail.vue
Normal file
285
pagesA/device/share/detail.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('share.userDetail')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="device-share-detail-wrap">
|
||||
<view class="user-info-wrap">
|
||||
<view class="title-wrap">
|
||||
<u--text bold size="15" prefixIcon="account-fill" iconStyle="font-size: 40rpx; margin-right: 10rpx;"
|
||||
:text="$tt('share.userMsg')"></u--text>
|
||||
</view>
|
||||
<view class="container-wrap">
|
||||
<u--text :text="$tt('share.userId')+form.userId" lineHeight="28"></u--text>
|
||||
<u--text :text="$tt('share.userName')+form.userName" lineHeight="28"></u--text>
|
||||
<u--text :text="$tt('share.phoneNumber')+form.phonenumber" lineHeight="28"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="permission-wrap">
|
||||
<view class="title-wrap">
|
||||
<u--text bold size="15" prefixIcon="integral-fill" iconStyle="font-size: 40rpx; margin-right: 10rpx;"
|
||||
:text="$tt('share.userPermissions')"></u--text>
|
||||
</view>
|
||||
<view class="container-wrap">
|
||||
<u-checkbox-group placement="column" @change="checkboxChange">
|
||||
<view class="checkbox-item-wrap" v-for="(item, index) in permissionList">
|
||||
<u-checkbox :customStyle="{marginBottom: '26rpx'}" :key="index"
|
||||
:label="`${item.modelName} (${item.identifier})`" :name="item.modelName"
|
||||
:checked="item.checked">
|
||||
</u-checkbox>
|
||||
<u--text type="info" :text="item.remark ? item.remark: $tt('share.notRemark')" size="14"
|
||||
margin="-20rpx 0rpx 20rpx 48rpx"></u--text>
|
||||
</view>
|
||||
</u-checkbox-group>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="remark-wrap">
|
||||
<view class="title-wrap">
|
||||
<u--text bold size="15" prefixIcon="file-text-fill" iconStyle="font-size: 40rpx; margin-right: 10rpx;"
|
||||
:text="$tt('share.remark')"></u--text>
|
||||
</view>
|
||||
<view class="container-wrap">
|
||||
<u--textarea v-model="form.remark" :placeholder="$tt('deviceDetail.inputMsg')" border="bottom" confirmType="done"></u--textarea>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-wrap">
|
||||
<u-button v-if="type === 1" type="success" customStyle="margin: 20rpx; padding: 30rpx;" @tap="handleAddUser"
|
||||
size="small">{{$tt('share.confirmShare')}}</u-button>
|
||||
<u-button v-if="type === 2" type="error" @tap="handleDeleteUser" size="small"
|
||||
customStyle="margin: 20rpx; padding: 30rpx;">{{$tt('common.delete')}}</u-button>
|
||||
<u-button v-if="type === 2" type="primary" @tap="handleEditUser" size="small"
|
||||
customStyle="margin: 20rpx; padding: 30rpx;">{{$tt('common.save')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { addDeviceUser, updateDeviceUser, delDeviceUser } from '@/apis/modules/deviceUser';
|
||||
import { getModelPermList } from "@/apis/modules/model";
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
type: 1, // 类型: 1=新增,2=更新
|
||||
permissionList: [], // 权限列表
|
||||
selectPerms: [], // 选中的权限列表
|
||||
// 表单参数
|
||||
form: {
|
||||
deviceId: null,
|
||||
userId: null,
|
||||
deviceName: null,
|
||||
userName: null,
|
||||
perms: null,
|
||||
phonenumber: null,
|
||||
remark: null
|
||||
}
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
this.form.phonenumber = option.phonenumber;
|
||||
this.form.remark = option.remark;
|
||||
this.form.userId = option.userId;
|
||||
this.form.userName = option.userName;
|
||||
this.form.deviceId = option.deviceId;
|
||||
this.form.deviceName = option.deviceName;
|
||||
this.form.productId = option.productId;
|
||||
this.form.perms = option.perms;
|
||||
this.type = Number(option.type);
|
||||
|
||||
this.getPermissionList();
|
||||
},
|
||||
methods: {
|
||||
// 查询产品物模型设备权限列表
|
||||
async getPermissionList () {
|
||||
if (this.form.perms) {
|
||||
this.selectPerms = this.form.perms.split(',');
|
||||
}
|
||||
getModelPermList(this.form.productId).then((response) => {
|
||||
// 固定增加设备系统相关权限
|
||||
this.permissionList = [{
|
||||
identifier: "ota",
|
||||
modelName: this.$tt('share.deviceUpgradation'),
|
||||
remark: this.$tt('share.otaUpgradation')
|
||||
}, {
|
||||
identifier: "timer",
|
||||
modelName: this.$tt('share.timing'),
|
||||
remark: this.$tt('share.timedTaskExecution')
|
||||
}, {
|
||||
identifier: "log",
|
||||
modelName: this.$tt('share.deviceLog'),
|
||||
remark: this.$tt('share.contains')
|
||||
}, {
|
||||
identifier: "monitor",
|
||||
modelName: this.$tt('share.monitior'),
|
||||
remark: this.$tt('share.afterMonitior')
|
||||
}, {
|
||||
identifier: "statistic",
|
||||
modelName:this.$tt('share.monitioringStatic'),
|
||||
remark: this.$tt('share.chartDisplay')
|
||||
}];
|
||||
this.permissionList = this.permissionList.concat(response.data);
|
||||
|
||||
if (this.selectPerms.length > 0) {
|
||||
for (let i = 0; i < this.permissionList.length; i++) {
|
||||
for (let j = 0; j < this.selectPerms.length; j++) {
|
||||
if (this.permissionList[i].identifier == this.selectPerms[j]) {
|
||||
this.permissionList[i].checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 复选框改变事件
|
||||
checkboxChange (data) {
|
||||
this.selectPerms = [];
|
||||
console.log(data)
|
||||
for (let i = 0; i < this.permissionList.length; i++) {
|
||||
if (data.indexOf(this.permissionList[i].modelName) != -1) {
|
||||
this.selectPerms.push(this.permissionList[i].identifier)
|
||||
}
|
||||
}
|
||||
console.log(this.selectPerms)
|
||||
},
|
||||
// 添加设备用户
|
||||
handleAddUser () {
|
||||
this.form.perms = this.selectPerms.join(',');
|
||||
addDeviceUser(this.form).then(response => {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: this.$tt('share.deviceShareSuccess')
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
success: (e) => {
|
||||
var pages = getCurrentPages();
|
||||
var page = pages[pages.length - 2];
|
||||
if (page === undefined || page === null)
|
||||
return;
|
||||
// 更新列表
|
||||
if (uni.getSystemInfoSync().platform === 'devtools') {
|
||||
page.$vm.dataList = [];
|
||||
page.$vm.getList();
|
||||
} else {
|
||||
page.dataList = [];
|
||||
page.getList();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
// 编辑
|
||||
handleEditUser () {
|
||||
this.form.perms = this.selectPerms.join(',');
|
||||
updateDeviceUser(this.form).then(response => {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: this.$tt('common.updateSuccessful')
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
success: (e) => {
|
||||
var pages = getCurrentPages();
|
||||
var page = pages[pages.length - 2];
|
||||
if (page === undefined || page === null)
|
||||
return;
|
||||
// 更新列表
|
||||
if (uni.getSystemInfoSync().platform === 'devtools') {
|
||||
page.$vm.dataList = [];
|
||||
page.$vm.getList();
|
||||
} else {
|
||||
page.dataList = [];
|
||||
page.getList();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
// 删除
|
||||
handleDeleteUser () {
|
||||
uni.showModal({
|
||||
title: this.$tt('share.title'),
|
||||
content: this.$tt('share.alert'),
|
||||
success: result => {
|
||||
if (result.confirm) {
|
||||
uni.showLoading({
|
||||
title: this.$tt('share.delete')
|
||||
});
|
||||
delDeviceUser(this.form).then(res => {
|
||||
if (res) {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: res.msg
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
success: (e) => {
|
||||
var pages = getCurrentPages();
|
||||
var page = pages[pages.length - 2];
|
||||
if (page === undefined || page ===
|
||||
null)
|
||||
return;
|
||||
// 更新列表
|
||||
if (uni.getSystemInfoSync()
|
||||
.platform === 'devtools') {
|
||||
page.$vm.dataList = [];
|
||||
page.$vm.getList();
|
||||
} else {
|
||||
page.dataList = [];
|
||||
page.getList();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}).finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.device-share-detail-wrap {
|
||||
|
||||
padding: 40rpx 0;
|
||||
|
||||
.user-info-wrap,
|
||||
.permission-wrap,
|
||||
.remark-wrap {
|
||||
.title-wrap {
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
margin-top: 10rpx;
|
||||
padding: 30rpx 40rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.permission-wrap,
|
||||
.remark-wrap {
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.btn-wrap {
|
||||
display: flex;
|
||||
margin: 80rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
254
pagesA/device/share/list.vue
Normal file
254
pagesA/device/share/list.vue
Normal file
@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('share.deviceShare')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="device-share-list-wrap">
|
||||
<u-sticky>
|
||||
<view class="search-wrap">
|
||||
<u-search v-model="phoneNumber" shape="square" maxlength="11" :placeholder="$tt('share.inputUserPhone')" :actionText="$tt('share.query')"
|
||||
@search="handleUserSearch" @custom="handleUserSearch" :inputStyle="{padding: '4rpx 0'}"
|
||||
:actionStyle="{ backgroundColor: '#398ade', color: '#fff', padding: '16rpx', borderRadius: '6rpx' }">
|
||||
</u-search>
|
||||
<view class="message-wrap" v-if="message != ''">
|
||||
<u--text :text="message" prefixIcon="info-circle" iconStyle="margin-right:5px; color: #f56c6c;"
|
||||
type="error" size="14" margin="0 0 0 20rpx"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
</u-sticky>
|
||||
<view class="list-wrap" v-if="dataList.length > 0">
|
||||
<view class="title-wrap">
|
||||
<u--text prefixIcon="share-fill" iconStyle="size:16px;margin-right:5px;" bold size="16" color="#666"
|
||||
:text="$tt('share.sharedUsers')"></u--text>
|
||||
</view>
|
||||
<view class="container-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in dataList" :key="index" @click="gotoEdit(item)">
|
||||
<u-row>
|
||||
<view class="image">
|
||||
<u--image src="/static/avatar.png" shape="square" radius="5" width="38" height="38">
|
||||
</u--image>
|
||||
</view>
|
||||
<view class="middle">
|
||||
<u--text prefixIcon="account" iconStyle="font-size: 40rpx; margin-right: 10rpx;"
|
||||
:text="item.userName" size="14"></u--text>
|
||||
<u--text type="info" prefixIcon="phone"
|
||||
iconStyle="font-size: 40rpx; margin-right: 10rpx;" :text="item.phonenumber"
|
||||
size="14"></u--text>
|
||||
</view>
|
||||
<view class="right">
|
||||
<text v-if="item.isOwner === 1" class="title"
|
||||
style="color: #3c9cff; margin-right: 48rpx">{{$tt('share.master')}}</text>
|
||||
<text v-else class="title" style="color: #13ce66;">{{$tt('share.share')}}</text>
|
||||
<u--text v-if="item.isOwner !== 1" margin="0 10rpx" suffixIcon="arrow-right"
|
||||
align="right"></u--text>
|
||||
</view>
|
||||
</u-row>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
|
||||
:nomoreText="$tt('scene.emptyData')" marginTop="20" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getShareUser,
|
||||
getUserList
|
||||
} from '@/apis/modules/deviceUser';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
deviceId: 0, //设备信息
|
||||
productId: 0,
|
||||
deviceName: '',
|
||||
phoneNumber: null, // 手机号码查询
|
||||
message: '', // 消息提示
|
||||
loadStatus: 'loadmore', // 加载更多
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 6,
|
||||
deviceId: 0
|
||||
},
|
||||
total: 0, // 总条数
|
||||
dataList: [], // 列表数据
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
this.queryParams.deviceId = option.deviceId;
|
||||
this.deviceId = option.deviceId;
|
||||
this.productId = option.productId;
|
||||
this.deviceName = option.deviceName;
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
// 获取列表数据
|
||||
getList() {
|
||||
getUserList(this.queryParams).then(response => {
|
||||
const {
|
||||
rows,
|
||||
total
|
||||
} = response;
|
||||
if (this.queryParams.pageNum == 1) {
|
||||
this.dataList = rows;
|
||||
} else {
|
||||
this.dataList = this.dataList.concat(rows);
|
||||
}
|
||||
this.total = total;
|
||||
const {
|
||||
pageNum,
|
||||
pageSize
|
||||
} = this.queryParams;
|
||||
this.loadStatus = total > pageNum * pageSize ? 'loadmore' : 'nomore';
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
// 查询用户列表
|
||||
handleUserSearch() {
|
||||
if (this.phoneNumber && uni.$u.test.mobile(this.phoneNumber)) {
|
||||
let params = {
|
||||
phonenumber: this.phoneNumber,
|
||||
deviceId: this.deviceId
|
||||
};
|
||||
getShareUser(params).then(response => {
|
||||
if (response.data) {
|
||||
this.message = '';
|
||||
let user = response.data;
|
||||
uni.$u.route({
|
||||
url: '/pagesA/device/share/detail',
|
||||
params: {
|
||||
userName: user.userName,
|
||||
phonenumber: user.phonenumber,
|
||||
createTime: user.createTime,
|
||||
perms: user.perms,
|
||||
userId: user.userId,
|
||||
remark: user.remark,
|
||||
deviceId: this.deviceId,
|
||||
deviceName: this.deviceName,
|
||||
productId: this.productId,
|
||||
type: 1 // 类型,1=新增,2=更新
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.message = this.$tt('share.unable');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.message = this.$tt('share.correct');
|
||||
}
|
||||
},
|
||||
// 编辑
|
||||
gotoEdit(item) {
|
||||
if (item.isOwner !== 1) {
|
||||
uni.$u.route({
|
||||
url: '/pagesA/device/share/detail',
|
||||
params: {
|
||||
userName: item.userName,
|
||||
phonenumber: item.phonenumber,
|
||||
createTime: item.createTime,
|
||||
remark: item.remark,
|
||||
userId: item.userId,
|
||||
deviceId: this.deviceId,
|
||||
deviceName: this.deviceName,
|
||||
productId: this.productId,
|
||||
perms: item.perms,
|
||||
type: 2, // 类型: 1=新增,2=更新
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh() {
|
||||
this.dataList = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom() {
|
||||
this.queryParams.pageNum = this.queryParams.pageNum + 1;
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize > this.total) {
|
||||
this.loadStatus = 'nomore';
|
||||
} else {
|
||||
this.getList();
|
||||
this.loadStatus = 'loading';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.device-share-list-wrap {
|
||||
.search-wrap {
|
||||
margin-top: 40rpx;
|
||||
padding: 60rpx 40rpx;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2rpx 1px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.message-wrap {
|
||||
padding: 26rpx 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.list-wrap {
|
||||
margin-top: 40rpx;
|
||||
|
||||
.title-wrap {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
padding: 20rpx 40rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
.cell-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
padding: 40rpx 20rpx;
|
||||
box-shadow: 0 2rpx 0 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.middle {
|
||||
flex: 1;
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.title {
|
||||
color: #606266;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.item-wrap {
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx 20rpx;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
290
pagesA/home/device/detail/index.vue
Normal file
290
pagesA/home/device/detail/index.vue
Normal file
@ -0,0 +1,290 @@
|
||||
<template>
|
||||
<view class="device-detail">
|
||||
<u--form labelPosition="left" :model="deviceForm" :rules="deviceRules" ref="deviceForm" labelWidth="90"
|
||||
labelAlign="center">
|
||||
<view class="card">
|
||||
<view class="card-title-wrap">
|
||||
<u--text prefixIcon="grid-fill" iconStyle="font-size: 16px;color:#606266"
|
||||
:text="$tt('deviceDetail.basicMsg')" bold color="#606266"></u--text>
|
||||
</view>
|
||||
<u-form-item :label="$tt('deviceDetail.deviceName')" prop="deviceName">
|
||||
<u--input v-model="deviceForm.deviceName"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.position')" prop="locationWay">
|
||||
<uni-data-select v-model="deviceForm.locationWay" :localdata="locationList" :clear="false">
|
||||
</uni-data-select>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.longitude')" v-if="deviceForm.locationWay === 3">
|
||||
<u--input v-model="deviceForm.longitude" type="digit"
|
||||
:placeholder="$tt('deviceDetail.inputLongitude')"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.latitude')" v-if="deviceForm.locationWay === 3">
|
||||
<u--input v-model="deviceForm.latitude" type="digit"
|
||||
:placeholder="$tt('deviceDetail.inputLatitude')"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.address')" v-if="deviceForm.locationWay === 3">
|
||||
<u--input v-model="deviceForm.networkAddress"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.shadow')">
|
||||
<u-switch v-model="deviceForm.isShadow" activeColor="#3c9cff" :inactiveValue="0" :activeValue="1"
|
||||
size="20"></u-switch>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.disabledDevice')">
|
||||
<u-switch v-model="deviceDisable" activeColor="#f56c6c" :inactiveValue="0" :activeValue="1"
|
||||
size="20"></u-switch>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.remark')">
|
||||
<u-textarea v-model="deviceForm.remark" height="40" fontSize="14"
|
||||
:placeholder="$tt('deviceDetail.inputMsg')" confirmType="done"></u-textarea>
|
||||
</u-form-item>
|
||||
<view style="margin-top:10px;display:flex;">
|
||||
<u-button type="success" v-if="profile.deptId==null" @tap="handleGoToShare" size="small"
|
||||
customStyle="margin:10px;">{{$tt('deviceDetail.share')}}</u-button>
|
||||
<u-button type="primary" @tap="handleSubmitForm" size="small"
|
||||
customStyle="margin:10px;">{{$tt('deviceDetail.preserve')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view style="margin:10px;">
|
||||
<u--image :src="imageUrl" radius="10" mode="aspectFill" width="100%" height="200">
|
||||
<view slot="error" style="font-size: 18rpx;">{{$tt('deviceDetail.loadingFail')}}</view>
|
||||
<template v-slot:loading>
|
||||
<u-loading-icon></u-loading-icon>
|
||||
</template>
|
||||
</u--image>
|
||||
</view>
|
||||
<u-form-item :label="$tt('deviceDetail.deviceStatus')">
|
||||
<u-button :text="$tt('home.notActive')" type="warning" size="mini" plain
|
||||
v-if="deviceForm.status == 1" customStyle="width:50px;margin:0;"></u-button>
|
||||
<u-button :text="$tt('home.disabled')" type="error" size="mini" plain v-if="deviceForm.status == 2"
|
||||
customStyle="width:50px;margin:0;"></u-button>
|
||||
<u-button :text="$tt('home.onLine')" type="success" size="mini" plain v-if="deviceForm.status == 3"
|
||||
customStyle="width:50px;margin:0;"></u-button>
|
||||
<u-button :text="$tt('home.offline')" type="info" size="mini" plain v-if="deviceForm.status == 4"
|
||||
customStyle="width:50px;margin:0;"></u-button>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.serialNumber')">
|
||||
<u--text :text="deviceForm.serialNumber"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.belongingProducts')">
|
||||
<u--text :text="deviceForm.productName"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.firmwareVersion')">
|
||||
<u--text :text="formatVersion(deviceForm.firmwareVersion)"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.positionMethod')" v-if="deviceForm.deviceType === 3">
|
||||
<u--text :text="deviceForm.locationWayInfo.name"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.longitude')"
|
||||
v-if="deviceForm.longitude && deviceForm.locationWay !== 3">
|
||||
<u--text :text="deviceForm.longitude"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.latitude')"
|
||||
v-if="deviceForm.latitude && deviceForm.locationWay !== 3">
|
||||
<u--text :text="deviceForm.latitude"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.equipmentSignal')">
|
||||
<u--text :text="$tt('deviceDetail.veryGood')" type="success"
|
||||
v-if="deviceForm.rssi >= '-55'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.excellent')" type="success"
|
||||
v-else-if="deviceForm.rssi >= '-70' && deviceForm.rssi < '-55'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.good ')" type="success"
|
||||
v-else-if="deviceForm.rssi >= '-85' && deviceForm.rssi < '-70'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.average ')" type="warning"
|
||||
v-else-if="deviceForm.rssi >= '-100' && deviceForm.rssi < '-85'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.poor')" type="error" v-else></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.address')"
|
||||
v-if="deviceForm.networkAddress && deviceForm.locationWay !== 3">
|
||||
<u--text :text="deviceForm.networkAddress"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.networkAddress')">
|
||||
<u--text :text="deviceForm.networkIp"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.activationTime')">
|
||||
<u--text :text="deviceForm.activeTime"></u--text>
|
||||
</u-form-item>
|
||||
|
||||
<view style="margin-top:10px;display:flex;">
|
||||
<u-modal :show="show" title="删除警告" :content="content" showCancelButton
|
||||
@confirm="handleConfirmDelete" @cancel="show = false"></u-modal>
|
||||
<u-button type="error" @click="handleDeleteDevice" size="small"
|
||||
customStyle="margin:10px;">{{$tt('deviceDetail.deleteDevice')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u--form>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getDevice, updateDevice, delDevice } from '@/apis/modules/device';
|
||||
import { getLocationWayInfo } from '@/helpers/common.js'
|
||||
|
||||
export default {
|
||||
name: "deviceDetail",
|
||||
data () {
|
||||
return {
|
||||
// 设备详情
|
||||
deviceForm: {
|
||||
isShadow: 0,
|
||||
},
|
||||
// 表单校验
|
||||
deviceRules: {
|
||||
deviceName: [{
|
||||
required: true,
|
||||
message: this.$tt('deviceDetail.deviceCheck'),
|
||||
trigger: ['blur', 'change']
|
||||
}]
|
||||
},
|
||||
// 定位方式
|
||||
locationList: [{
|
||||
value: 1,
|
||||
text: this.$tt('deviceDetail.autoPosition')
|
||||
}, {
|
||||
value: 2,
|
||||
text: this.$tt('deviceDetail.devicePosition')
|
||||
}, {
|
||||
value: 3,
|
||||
text: this.$tt('deviceDetail.customLocation')
|
||||
}],
|
||||
deviceDisable: 0, // 设备状态(1=禁用,0=不禁用)
|
||||
imageUrl: '', // 图片地址
|
||||
show: false, // 删除设备模态框
|
||||
content: '', // 对话框内容
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
const deviceId = option.deviceId;
|
||||
this.getDeviceDetails(deviceId);
|
||||
},
|
||||
methods: {
|
||||
// 连接Mqtt消息服务器
|
||||
async connectMqtt () {
|
||||
if (this.$mqttTool.client === null) {
|
||||
await this.$mqttTool.connect(this.vuex_token);
|
||||
}
|
||||
this.mqttCallback();
|
||||
},
|
||||
// Mqtt回调处理
|
||||
mqttCallback () {
|
||||
this.$mqttTool.client.on('message', (topic, message, buffer) => {
|
||||
let topics = topic.split('/');
|
||||
let productId = topics[1];
|
||||
let deviceNum = topics[2];
|
||||
message = JSON.parse(message.toString());
|
||||
if (!message) {
|
||||
return;
|
||||
};
|
||||
if (topics[3] == 'status' || topics[2] == 'status') {
|
||||
console.log('接收到【设备状态-详情】主题:', topic);
|
||||
console.log('接收到【设备状态-详情】内容:', message);
|
||||
// 更新列表中设备的状态
|
||||
if (this.deviceForm.serialNumber == deviceNum) {
|
||||
this.deviceForm.status = message.status;
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
getDeviceDetails (deviceId) {
|
||||
getDevice(deviceId).then(res => {
|
||||
let data = res.data;
|
||||
this.imageUrl = data.imgUrl;
|
||||
if (this.imageUrl != null && this.imageUrl != '') {
|
||||
this.imageUrl = projectConfig.baseUrl + this.imageUrl;
|
||||
} else {
|
||||
this.imageUrl = '/static/common/device.png';
|
||||
}
|
||||
// 禁用状态
|
||||
if (data.status == 2) {
|
||||
this.deviceDisable = 1;
|
||||
}
|
||||
// 获取定位方式
|
||||
data.locationWayInfo = getLocationWayInfo(data.locationWay);
|
||||
this.deviceForm = data;
|
||||
this.connectMqtt();
|
||||
});
|
||||
},
|
||||
// 格式化版本显示
|
||||
formatVersion (version) {
|
||||
return `Version ${version}`;
|
||||
},
|
||||
// 调转到分享
|
||||
handleGoToShare () {
|
||||
const { deviceId, productId, deviceName } = this.deviceForm;
|
||||
uni.$u.route('/pagesA/device/share/list', { deviceId, productId, deviceName });
|
||||
},
|
||||
// 提交按钮
|
||||
handleSubmitForm () {
|
||||
this.$refs.deviceForm.validate().then(e => {
|
||||
if (this.deviceForm.deviceId !== 0) {
|
||||
this.setDeviceStatus(); // 设置设备状态
|
||||
let device = { ...this.deviceForm };
|
||||
updateDevice(device).then(res => {
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: this.$tt('deviceDetail.updateSuccess')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 设置设备的状态(1-未激活,2-禁用,3-在线,4-离线)
|
||||
setDeviceStatus () {
|
||||
if (this.deviceDisable == 1) {
|
||||
this.deviceForm.status = 2;
|
||||
} else {
|
||||
// 禁用状态,启用后状态是离线
|
||||
if (this.deviceForm.status == 2) {
|
||||
this.deviceForm.status = 4;
|
||||
}
|
||||
}
|
||||
},
|
||||
// 删除设备按钮操作
|
||||
handleDeleteDevice () {
|
||||
this.content = '是否确认删除 ' + this.deviceForm.deviceName + ' ?';
|
||||
this.show = true;
|
||||
},
|
||||
// 确认删除设备
|
||||
handleConfirmDelete () {
|
||||
this.show = false;
|
||||
delDevice(this.deviceForm.deviceId).then(res => {
|
||||
if (res.code == 200) {
|
||||
// 跳转主页,通过globalData传递参数
|
||||
getApp().globalData.operate = 'operate';
|
||||
uni.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
});
|
||||
} else if (res.code == 500) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-detail {
|
||||
padding-bottom: 100rpx;
|
||||
|
||||
.card {
|
||||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.card-title-wrap {
|
||||
display: flex;
|
||||
padding: 6px;
|
||||
padding-left: 0;
|
||||
border-bottom: 1px solid #efefef;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
812
pagesA/home/device/index.vue
Normal file
812
pagesA/home/device/index.vue
Normal file
@ -0,0 +1,812 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="title" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="container">
|
||||
<!-- 设备 -->
|
||||
<view v-show="tabbarIndex === 0 && deviceForm.deviceType !== 3">
|
||||
<u-sticky bgColor="#FFF" customStyle="border-bottom:1px solid #eee;">
|
||||
<u-tabs :list="tabList" :scrollable="false" lineWidth="70" lineHeight="2" :duration="100"
|
||||
@click="tabClick"></u-tabs>
|
||||
</u-sticky>
|
||||
<!-- 设备运行状态 -->
|
||||
<running-status v-show="tabIndex == 0" :device="deviceForm" ref="runningStatus"></running-status>
|
||||
<!-- 设备实时监测 -->
|
||||
<device-monitor v-show="tabIndex == 1" :show="tabIndex == 1" :device="deviceForm" ref="deviceMonitor">
|
||||
</device-monitor>
|
||||
<!-- 视频监控 -->
|
||||
<video-monitor v-show="tabIndex == 2" :device="deviceForm" ref="videoMonitor"></video-monitor>
|
||||
<!-- 告警记录 -->
|
||||
<device-alertLog v-show="tabIndex == 3" :device="deviceForm" ref="deviceAlertLog"></device-alertLog>
|
||||
</view>
|
||||
|
||||
<!-- 视频监控设备 -->
|
||||
<view v-show="tabbarIndex === 0 && deviceForm.deviceType === 3">
|
||||
<u-sticky bgColor="#FFF" customStyle="border-bottom:1px solid #eee;">
|
||||
<u-tabs :list="tabList" :scrollable="false" lineWidth="80" lineHeight="2" :duration="100"
|
||||
@click="tabClick"></u-tabs>
|
||||
</u-sticky>
|
||||
<!-- 设备通道 -->
|
||||
<view class="channel_wrap" v-show="tabIndex == 0">
|
||||
<view class="item_body" v-for="(item, i) in channelList" :key="i"
|
||||
@click="gotoDevicePlayer(item.deviceSipId, item.channelSipId, item.status)">
|
||||
<view class="img">
|
||||
<u-icon name="play-circle" color="#2979ff" size="28"></u-icon>
|
||||
</view>
|
||||
<view>
|
||||
<u--text lines="2" lineHeight="24" size="16" :text="item.channelName"></u--text>
|
||||
<view style="display:flex;margin-top:6px;">
|
||||
<view style="margin-right:20px;">
|
||||
<u--text prefixIcon="info-circle"
|
||||
iconStyle="color:#606266;font-size:14px;margin-right:3px;" size="12" color="#606266"
|
||||
mode="name" :text="item.model"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="status">
|
||||
<u-tag v-if="item.statusInfo" :type="item.statusInfo.type" :plain="true" size="mini"
|
||||
:text="item.statusInfo.name"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="list" :show="channelTotal === 0" marginTop="30"></u-empty>
|
||||
<u-loadmore :status="channelLoadStatus" v-if="channelTotal > channelQueryParams.pageSize"
|
||||
marginTop="20" />
|
||||
</view>
|
||||
<view v-show="tabIndex === 1">
|
||||
<u--form labelPosition="left" :model="deviceForm" :rules="deviceRules" ref="deviceForm" labelWidth="90"
|
||||
labelAlign="center">
|
||||
<view class="card">
|
||||
<view class="card-title-wrap">
|
||||
<u--text prefixIcon="grid-fill" iconStyle="font-size: 16px;color:#606266"
|
||||
:text="$tt('deviceDetail.basicMsg')" bold color="#606266"></u--text>
|
||||
</view>
|
||||
<u-form-item :label="$tt('deviceDetail.deviceName')" prop="deviceName">
|
||||
<u--input v-model="deviceForm.deviceName"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.position')" prop="locationWay"
|
||||
v-if="deviceForm.deviceType !== 3">
|
||||
<uni-data-select v-model="deviceForm.locationWay" :localdata="locationList" :clear="false">
|
||||
</uni-data-select>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.longitude')" v-if="deviceForm.locationWay === 3">
|
||||
<u--input v-model="deviceForm.longitude" type="digit"
|
||||
:placeholder="$tt('deviceDetail.inputLongitude')"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.latitude')" v-if="deviceForm.locationWay === 3">
|
||||
<u--input v-model="deviceForm.latitude" type="digit"
|
||||
:placeholder="$tt('deviceDetail.inputLatitude')"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.address')" v-if="deviceForm.locationWay === 3">
|
||||
<u--input v-model="deviceForm.networkAddress"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.remark')">
|
||||
<u-textarea v-model="deviceForm.remark" height="40" fontSize="14"
|
||||
:placeholder="$tt('deviceDetail.inputMsg')" confirmType="done"></u-textarea>
|
||||
</u-form-item>
|
||||
<view style="margin-top:10px;display:flex;">
|
||||
<u-button v-if="profile.deptId==null" type="success" @tap="gotoShare" size="small"
|
||||
customStyle="margin:10px;">{{$tt('deviceDetail.share')}}</u-button>
|
||||
<u-button type="primary" @tap="submitForm" size="small"
|
||||
customStyle="margin:10px;">{{$tt('deviceDetail.preserve')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view style="margin:10px;">
|
||||
<u--image :src="imageUrl" radius="10" mode="aspectFill" width="100%" height="200">
|
||||
<view slot="error" style="font-size: 18rpx;">{{$tt('deviceDetail.loadingFail')}}</view>
|
||||
<template v-slot:loading>
|
||||
<u-loading-icon></u-loading-icon>
|
||||
</template>
|
||||
</u--image>
|
||||
</view>
|
||||
<u-form-item :label="$tt('deviceDetail.deviceStatus')">
|
||||
<u-button :text="$tt('home.notActive')" type="warning" size="mini" plain
|
||||
v-if="deviceForm.status == 1" customStyle="width:50px;margin:0;"></u-button>
|
||||
<u-button :text="$tt('home.disabled')" type="error" size="mini" plain
|
||||
v-if="deviceForm.status == 2" customStyle="width:50px;margin:0;"></u-button>
|
||||
<u-button :text="$tt('home.onLine')" type="success" size="mini" plain
|
||||
v-if="deviceForm.status == 3" customStyle="width:50px;margin:0;"></u-button>
|
||||
<u-button :text="$tt('home.offline')" type="info" size="mini" plain
|
||||
v-if="deviceForm.status == 4" customStyle="width:50px;margin:0;"></u-button>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.serialNumber')">
|
||||
<u--text :text="deviceForm.serialNumber"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.belongingProducts')">
|
||||
<u--text :text="deviceForm.productName"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.firmwareVersion')">
|
||||
<u--text :text="formatVersion(deviceForm.firmwareVersion)"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.positionMethod')" v-if="deviceForm.deviceType === 3">
|
||||
<u--text :text="deviceForm.locationWayInfo.name"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.longitude')"
|
||||
v-if="deviceForm.longitude && deviceForm.locationWay !== 3">
|
||||
<u--text :text="deviceForm.longitude"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.latitude')"
|
||||
v-if="deviceForm.latitude && deviceForm.locationWay !== 3">
|
||||
<u--text :text="deviceForm.latitude"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.equipmentSignal')">
|
||||
<u--text :text="$tt('deviceDetail.veryGood')" type="success"
|
||||
v-if="deviceForm.rssi >= '-55'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.excellent')" type="success"
|
||||
v-else-if="deviceForm.rssi >= '-70' && deviceForm.rssi < '-55'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.good')" type="success"
|
||||
v-else-if="deviceForm.rssi >= '-85' && deviceForm.rssi < '-70'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.average')" type="warning"
|
||||
v-else-if="deviceForm.rssi >= '-100' && deviceForm.rssi < '-85'"></u--text>
|
||||
<u--text :text="$tt('deviceDetail.poor')" type="error" v-else></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.address')"
|
||||
v-if="deviceForm.networkAddress && deviceForm.locationWay !== 3">
|
||||
<u--text :text="deviceForm.networkAddress"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.networkAddress')">
|
||||
<u--text :text="deviceForm.networkIp"></u--text>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceDetail.activationTime')">
|
||||
<u--text :text="deviceForm.activeTime"></u--text>
|
||||
</u-form-item>
|
||||
|
||||
<view style="margin-top:10px;display:flex;">
|
||||
<u-modal :show="show" :title="modalTitle" :content="content" showCancelButton
|
||||
@confirm="confirm" @cancel="cancel"></u-modal>
|
||||
<u-button type="error" @click="handleDelete" size="small"
|
||||
customStyle="margin:10px;">{{$tt('deviceDetail.deleteDevice')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u--form>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 设备定时 -->
|
||||
<device-timing v-show="tabbarIndex === 1" :device="deviceForm" :type="tabbarIndex"
|
||||
ref="deviceTiming"></device-timing>
|
||||
<!-- 设备日志 -->
|
||||
<device-log v-show="tabbarIndex === 2" :device="deviceForm"></device-log>
|
||||
<!-- 设备统计 -->
|
||||
<!-- <device-statistic v-show="tabbarIndex === 3" :device="deviceForm" :type="tabbarIndex"
|
||||
ref="deviceStatistic"></device-statistic> -->
|
||||
<device-history v-show="tabbarIndex === 3" :device="deviceForm" :type="tabbarIndex"
|
||||
ref="deviceHistory"></device-history>
|
||||
<!-- 设备组态 -->
|
||||
<device-scada v-show="tabbarIndex === 4" :show="tabbarIndex === 4" :device="deviceForm"
|
||||
ref="deviceScada"></device-scada>
|
||||
<u-tabbar :value="tabbarIndex" @change="tabbarChange" :fixed="true" :placeholder="true"
|
||||
:safeAreaInsetBottom="true" :border="false" v-if="deviceForm.deviceType !==3">
|
||||
<u-tabbar-item :text="$tt('deviceDetail.device')" @click="tabbarClick(0)">
|
||||
<image style="width:20px;height:20px;" slot="active-icon" src="/static/device_blue.png"></image>
|
||||
<image style="width:15px;height:15px;" slot="inactive-icon" src="/static/device_black.png"></image>
|
||||
</u-tabbar-item>
|
||||
<u-tabbar-item :text="$tt('deviceDetail.timing')" @click="tabbarClick(1)">
|
||||
<image style="width:20px;height:20px;" slot="active-icon" src="/static/time_blue.png"></image>
|
||||
<image style="width:15px;height:15px;" slot="inactive-icon" src="/static/time_black.png"></image>
|
||||
</u-tabbar-item>
|
||||
<u-tabbar-item :text="$tt('deviceDetail.journal')" @click="tabbarClick(2)">
|
||||
<image style="width:20px;height:20px;" slot="active-icon" src="/static/log_blue.png"></image>
|
||||
<image style="width:15px;height:15px;" slot="inactive-icon" src="/static/log_black.png"></image>
|
||||
</u-tabbar-item>
|
||||
<u-tabbar-item :text="$tt('deviceDetail.static')" @click="tabbarClick(3)">
|
||||
<image style="width:20px;height:20px;" slot="active-icon" src="/static/statistic_blue.png"></image>
|
||||
<image style="width:15px;height:15px;" slot="inactive-icon" src="/static/statistic_black.png"></image>
|
||||
</u-tabbar-item>
|
||||
<u-tabbar-item :text="$tt('deviceDetail.scada')" @click="tabbarClick(4)">
|
||||
<image style="width:20px;height:20px;" slot="active-icon" src="/static/common/scada_blue.png"></image>
|
||||
<image style="width:15px;height:15px;" slot="inactive-icon" src="/static/common/scada_black.png">
|
||||
</image>
|
||||
</u-tabbar-item>
|
||||
</u-tabbar>
|
||||
<u-modal :show="showScada" content="暂无组态,请先去网页端配置模板组态" @confirm="() => showScada = false"
|
||||
@cancel="() => showScada = false" showCancelButton></u-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getDevice,
|
||||
getRunningStatus,
|
||||
cacheJsonThingsModel,
|
||||
deviceSynchronization,
|
||||
updateDevice,
|
||||
delDevice
|
||||
} from '@/apis/modules/device';
|
||||
import videoMonitor from './video/index.vue';
|
||||
import deviceAlertLog from './log/alertLog.vue';
|
||||
import runningStatus from './status/index.vue';
|
||||
import deviceLog from './log/index.vue';
|
||||
import deviceHistory from './statistic/history.vue';
|
||||
import deviceMonitor from './monitor/index.vue';
|
||||
import deviceTiming from './timing/index.vue';
|
||||
import deviceScada from './scada.vue';
|
||||
import { listChannel } from '@/apis/modules/sip';
|
||||
import { getSipStatusInfo, getLocationWayInfo } from '@/helpers/common.js'
|
||||
import projectConfig from '@/env.config.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
runningStatus,
|
||||
deviceLog,
|
||||
deviceMonitor,
|
||||
deviceTiming,
|
||||
videoMonitor,
|
||||
deviceAlertLog,
|
||||
deviceHistory,
|
||||
deviceScada
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: '设备',
|
||||
deviceId: 0,
|
||||
tabIndex: 0,
|
||||
tabbarIndex: 0,
|
||||
tabList: [{
|
||||
name: this.$tt('deviceDetail.Overview')
|
||||
}, {
|
||||
name: this.$tt('deviceDetail.monitor')
|
||||
}, {
|
||||
name: this.$tt('deviceDetail.Surveillance')
|
||||
}, {
|
||||
name: this.$tt('deviceDetail.Alert')
|
||||
}],
|
||||
show: false, // 删除设备模态框
|
||||
modalTitle: '删除警告', // 对话框标题
|
||||
content: '', // 对话框内容
|
||||
deviceForm: {
|
||||
isShadow: 0, // 避免u-switch报错,初始化
|
||||
deviceName: '',
|
||||
locationWay: 1,
|
||||
protocolCode: '',
|
||||
}, // 设备详情
|
||||
deviceDisable: 0, // 设备状态(1=禁用,0=不禁用)
|
||||
isSubDev: false, // 是否有子设备
|
||||
imageUrl: '', // 图片地址
|
||||
locationList: [{ // 定位方式
|
||||
value: 1,
|
||||
text: this.$tt('deviceDetail.autoPosition')
|
||||
}, {
|
||||
value: 2,
|
||||
text: this.$tt('deviceDetail.devicePosition')
|
||||
}, {
|
||||
value: 3,
|
||||
text: this.$tt('deviceDetail.customLocation')
|
||||
}],
|
||||
deviceRules: { // 表单校验
|
||||
deviceName: [{
|
||||
required: true,
|
||||
message: this.$tt('deviceDetail.deviceCheck'),
|
||||
trigger: ['blur', 'change']
|
||||
}]
|
||||
},
|
||||
// 设备通道
|
||||
channelLoadStatus: 'nomore',
|
||||
channelQueryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deviceSipId: '',
|
||||
},
|
||||
channelTotal: 0,
|
||||
channelList: [],
|
||||
showScada: false,
|
||||
};
|
||||
},
|
||||
onLoad: function (option) {
|
||||
this.deviceId = Number(option.deviceId);
|
||||
this.deviceForm.protocolCode = option.protocolCode;
|
||||
this.connectMqtt(); // 连接mqtt
|
||||
},
|
||||
onUnload () {
|
||||
this.mqttUnSubscribe(this.deviceForm); // 取消订阅主题
|
||||
},
|
||||
methods: {
|
||||
/* 连接Mqtt消息服务器 */
|
||||
async connectMqtt () {
|
||||
if (this.$mqttTool.client === null) {
|
||||
await this.$mqttTool.connect(this.vuex_token);
|
||||
}
|
||||
this.mqttCallback();
|
||||
// 获取设备信息并订阅主题
|
||||
this.getDevice();
|
||||
},
|
||||
/* Mqtt回调处理 */
|
||||
mqttCallback () {
|
||||
this.$mqttTool.client.on('message', (topic, message, buffer) => {
|
||||
let topics = topic.split('/');
|
||||
let productId = topics[1];
|
||||
let deviceNum = topics[2];
|
||||
message = JSON.parse(message.toString());
|
||||
if (!message) {
|
||||
return;
|
||||
};
|
||||
if (topics[3] == 'status' || topics[2] == 'status') {
|
||||
console.log('接收到【设备状态-详情】主题:', topic);
|
||||
console.log('接收到【设备状态-详情】内容:', message);
|
||||
// 更新列表中设备的状态
|
||||
if (this.deviceForm.serialNumber == deviceNum) {
|
||||
this.deviceForm.status = message.status;
|
||||
this.deviceForm.isShadow = message.isShadow;
|
||||
this.deviceForm.rssid = message.rssid;
|
||||
}
|
||||
};
|
||||
//不是modbus不转发到子页面,其他设备的页面有回调方法
|
||||
if (this.isSubDev) {
|
||||
/*发送设备上报到子模块*/
|
||||
if (topic.endsWith('ws/service')) {
|
||||
console.log('接收到【设备数据上报】主题:', topic)
|
||||
console.log('接收到【设备数据上报】主题:', message)
|
||||
this.$bus.$emit("updateDeviceStatus", {
|
||||
serialNumber: topics[2],
|
||||
productId: this.deviceForm.productId,
|
||||
data: message.message,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** Mqtt订阅主题 */
|
||||
mqttSubscribe (device) {
|
||||
// 订阅当前设备状态和实时监测
|
||||
let topicStatus = '/' + device.productId + '/' + device.serialNumber + '/status/post';
|
||||
let topicMonitor = '/' + device.productId + '/' + device.serialNumber + '/monitor/post';
|
||||
let topicReply = '/' + device.productId + '/' + device.serialNumber + '/service/reply';
|
||||
let topicService = '/' + device.productId + '/' + device.serialNumber + '/ws/service';
|
||||
let topics = [];
|
||||
topics.push(topicStatus);
|
||||
topics.push(topicMonitor);
|
||||
topics.push(topicReply);
|
||||
topics.push(topicService);
|
||||
this.$mqttTool.subscribe(topics);
|
||||
},
|
||||
/** Mqtt取消订阅主题 */
|
||||
mqttUnSubscribe (device) {
|
||||
// 订阅当前设备状态和实时监测
|
||||
let topicStatus = '/' + device.productId + '/' + device.serialNumber + '/status/post';
|
||||
let topicMonitor = '/' + device.productId + '/' + device.serialNumber + '/monitor/post';
|
||||
let topicReply = '/' + device.productId + '/' + device.serialNumber + '/service/reply';
|
||||
let topicService = '/' + device.productId + '/' + device.serialNumber + '/ws/service';
|
||||
let topics = [];
|
||||
topics.push(topicStatus);
|
||||
topics.push(topicMonitor);
|
||||
topics.push(topicReply);
|
||||
topics.push(topicService);
|
||||
this.$mqttTool.unsubscribe(topics);
|
||||
console.log('取消订阅', topics);
|
||||
},
|
||||
// 单击选显卡
|
||||
tabClick (item) {
|
||||
this.tabIndex = item.index;
|
||||
},
|
||||
// 导航栏改变
|
||||
tabbarChange () {},
|
||||
// 导航栏单击
|
||||
tabbarClick (index) {
|
||||
this.tabbarIndex = index;
|
||||
},
|
||||
// 格式化版本显示
|
||||
formatVersion (version) {
|
||||
return `Version ${version}`;
|
||||
},
|
||||
// 刷新设备
|
||||
onPullDownRefresh () {
|
||||
if (this.tabbarIndex == 0) {
|
||||
if (this.tabIndex == 0) {
|
||||
// 设备运行状态
|
||||
this.getDevice();
|
||||
this.$refs.runningStatus.baseStatusRefresh();
|
||||
} else if (this.tabIndex == 2) {
|
||||
// 设备信息
|
||||
this.getDevice();
|
||||
} else {
|
||||
uni.stopPullDownRefresh();
|
||||
}
|
||||
} else if (this.tabbarIndex == 1) {
|
||||
// 设备定时
|
||||
this.$refs.deviceTiming.deviceTimerRefresh();
|
||||
uni.stopPullDownRefresh();
|
||||
} else if (this.tabbarIndex == 3) {
|
||||
// 设备统计
|
||||
this.$refs.deviceStatistic.getCacheThingsModdel();
|
||||
} else {
|
||||
uni.stopPullDownRefresh();
|
||||
}
|
||||
},
|
||||
/**获取设备详情*/
|
||||
getDevice () {
|
||||
getDevice(this.deviceId).then(async response => {
|
||||
try {
|
||||
let data = response.data;
|
||||
if (data.deviceType !== 3) {
|
||||
// 获取缓存物模型
|
||||
data.cacheThingsModel = await this.getCacheThingsModdel(data.productId);
|
||||
// 获取设备运行状态
|
||||
data.thingsModels = await this.getRunningStatusInfo(this.deviceId, data.slaveId);
|
||||
// 格式化物模型,拆分出监测值,数组添加前缀
|
||||
this.formatThingsModel(data);
|
||||
} else {
|
||||
// 获取通道列表
|
||||
this.channelList = await this.getChannelList(data.serialNumber);
|
||||
}
|
||||
this.title = data.deviceName;
|
||||
// this.isSubDev = data.subDeviceList.length > 0;
|
||||
this.imageUrl = data.imgUrl;
|
||||
if (this.imageUrl != null && this.imageUrl != '') {
|
||||
this.imageUrl = projectConfig.baseUrl + this.imageUrl;
|
||||
} else {
|
||||
this.imageUrl = '/static/common/device.png';
|
||||
}
|
||||
// 禁用状态
|
||||
if (data.status == 2) {
|
||||
this.deviceDisable = 1;
|
||||
}
|
||||
// 设置监控设备
|
||||
if (data.deviceType == 3) {
|
||||
this.tabList = [{
|
||||
name: this.$tt('deviceDetail.channel')
|
||||
}, {
|
||||
name: this.$tt('deviceDetail.deviceDetail')
|
||||
}];
|
||||
}
|
||||
// 获取定位方式
|
||||
data.locationWayInfo = getLocationWayInfo(data.locationWay);
|
||||
//Mqtt订阅
|
||||
uni.stopPullDownRefresh();
|
||||
this.mqttSubscribe(data);
|
||||
//增加协议字段为后面数据采集做判断
|
||||
data.protocolCode = this.deviceForm.protocolCode;
|
||||
this.deviceForm = data;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 设置设备的状态(1-未激活,2-禁用,3-在线,4-离线)
|
||||
setDeviceStatus () {
|
||||
if (this.deviceDisable == 1) {
|
||||
this.deviceForm.status = 2;
|
||||
} else {
|
||||
// 禁用状态,启用后状态是离线
|
||||
if (this.deviceForm.status == 2) {
|
||||
this.deviceForm.status = 4;
|
||||
}
|
||||
}
|
||||
},
|
||||
// 设备数据同步
|
||||
deviceSynchronization () {
|
||||
deviceSynchronization(this.deviceForm.serialNumber).then(async response => {
|
||||
try {
|
||||
let data = response.data;
|
||||
if (data.deviceType !== 3) {
|
||||
// 获取缓存物模型
|
||||
data.cacheThingsModel = await this.getCacheThingsModdel(data.productId);
|
||||
// 获取设备运行状态
|
||||
data.thingsModels = await this.getRunningStatusInfo(this.deviceId, data.slaveId);
|
||||
// 格式化物模型,拆分出监测值,数组添加前缀
|
||||
this.formatThingsModel(data);
|
||||
}
|
||||
this.title = data.deviceName;
|
||||
this.imageUrl = data.imgUrl;
|
||||
if (this.imageUrl != null && this.imageUrl != '') {
|
||||
this.imageUrl = projectConfig.baseUrl + this.imageUrl;
|
||||
} else {
|
||||
this.imageUrl = '/static/common/device.png';
|
||||
}
|
||||
// 禁用状态
|
||||
if (data.status == 2) {
|
||||
this.deviceDisable = 1;
|
||||
}
|
||||
this.deviceForm = response.data;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除设备按钮操作 */
|
||||
handleDelete () {
|
||||
this.content = '是否确认删除 ' + this.deviceForm.deviceName + ' ?';
|
||||
this.show = true;
|
||||
},
|
||||
// 确认删除设备
|
||||
confirm () {
|
||||
this.show = false;
|
||||
delDevice(this.deviceForm.deviceId).then(res => {
|
||||
if (res.code == 200) {
|
||||
// 跳转主页,通过globalData传递参数
|
||||
getApp().globalData.operate = 'operate';
|
||||
uni.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
});
|
||||
} else if (res.code == 500) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 取消
|
||||
cancel () {
|
||||
this.show = false;
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm () {
|
||||
if (!this.deviceForm.locationWay) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('deviceDetail.positionCheck')
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$refs.deviceForm.validate().then(e => {
|
||||
if (this.deviceForm.deviceId !== 0) {
|
||||
this.setDeviceStatus(); // 设置设备状态
|
||||
let device = {
|
||||
deviceId: this.deviceForm.deviceId,
|
||||
deviceName: this.deviceForm.deviceName,
|
||||
isShadow: this.deviceForm.isShadow,
|
||||
status: this.deviceForm.status,
|
||||
remark: this.deviceForm.remark,
|
||||
locationWay: this.deviceForm.locationWay,
|
||||
longitude: this.deviceForm.longitude,
|
||||
latitude: this.deviceForm.latitude,
|
||||
networkAddress: this.deviceForm.networkAddress
|
||||
};
|
||||
updateDevice(device).then(res => {
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: this.$tt('deviceDetail.updateSuccess')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
gotoShare () {
|
||||
uni.$u.route('/pagesA/device/share/list', {
|
||||
deviceId: this.deviceForm.deviceId,
|
||||
productId: this.deviceForm.productId,
|
||||
deviceName: this.deviceForm.deviceName
|
||||
});
|
||||
},
|
||||
/** 获取缓存物模型*/
|
||||
getCacheThingsModdel (productId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
cacheJsonThingsModel(productId).then(res => {
|
||||
resolve(JSON.parse(res.data));
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取设备运行状态
|
||||
getRunningStatusInfo (deviceId, slaveId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getRunningStatus(deviceId, slaveId).then(res => {
|
||||
resolve(res.data.thingsModels);
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
})
|
||||
});
|
||||
},
|
||||
// 物模型格式化
|
||||
formatThingsModel (data) {
|
||||
if (data && data.length !== 0) {
|
||||
data.chartList = [];
|
||||
data.monitorList = [];
|
||||
data.statisticList = [];
|
||||
for (let i = 0; i < data.thingsModels.length; i++) {
|
||||
if (data.thingsModels[i].datatype.type === "array") {
|
||||
if (data.thingsModels[i].datatype.arrayType === "object") {
|
||||
for (let k = 0; k < data.thingsModels[i].datatype.arrayParams.length; k++) {
|
||||
for (let j = 0; j < data.thingsModels[i].datatype.arrayParams[k].length; j++) {
|
||||
// 数组元素中参数ID添加前缀,例如:array_00_
|
||||
let index = k > 9 ? String(k) : '0' + k;
|
||||
let prefix = 'array_' + index + '_';
|
||||
data.thingsModels[i].datatype.arrayParams[k][j].id = prefix + data.thingsModels[i]
|
||||
.datatype.arrayParams[k][j].id;
|
||||
// 图表、实时监测、监测统计分类放置
|
||||
if (data.thingsModels[i].datatype.arrayParams[k][j].isChart === 1) {
|
||||
data.thingsModels[i].datatype.arrayParams[k][j].name = "[" + data.thingsModels[
|
||||
i].name + (k + 1) + "] " + data.thingsModels[i].datatype.arrayParams[k]
|
||||
[j].name;
|
||||
data.thingsModels[i].datatype.arrayParams[k][j].datatype.arrayType = "object";
|
||||
data.chartList.push(data.thingsModels[i].datatype.arrayParams[k][j]);
|
||||
// 监测统计
|
||||
if (data.thingsModels[i].datatype.arrayParams[k][j].isHistory == 1) {
|
||||
data.statisticList.push(data.thingsModels[i].datatype.arrayParams[k][j]);
|
||||
}
|
||||
// 实时监测
|
||||
if (data.thingsModels[i].datatype.arrayParams[k][j].isMonitor == 1) {
|
||||
data.monitorList.push(data.thingsModels[i].datatype.arrayParams[k][j]);
|
||||
}
|
||||
data.thingsModels[i].datatype.arrayParams[k].splice(j--, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 字符串拆分为物模型数组 model=id/name/type/isReadonly/value/shadow
|
||||
let values = data.thingsModels[i].value != "" ? data.thingsModels[i].value.split(',') : [];
|
||||
let shadows = data.thingsModels[i].shadow != "" ? data.thingsModels[i].shadow.split(',') :
|
||||
[];
|
||||
for (let j = 0; j < data.thingsModels[i].datatype.arrayCount; j++) {
|
||||
if (!data.thingsModels[i].datatype.arrayModel) {
|
||||
data.thingsModels[i].datatype.arrayModel = [];
|
||||
}
|
||||
// 数组里面的ID需要添加前缀和索引,例如:array_00_temperature
|
||||
let index = j > 9 ? String(j) : '0' + j;
|
||||
let prefix = 'array_' + index + '_';
|
||||
data.thingsModels[i].datatype.arrayModel[j] = {
|
||||
id: prefix + data.thingsModels[i].id,
|
||||
name: data.thingsModels[i].name,
|
||||
type: data.thingsModels[i].type,
|
||||
isReadonly: data.thingsModels[i].isReadonly,
|
||||
value: values[j] ? values[j] : "",
|
||||
shadow: shadows[j] ? shadows[j] : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data.thingsModels[i].datatype.type === "object") {
|
||||
for (let j = 0; j < data.thingsModels[i].datatype.params.length; j++) {
|
||||
// 图表、实时监测、监测统计分类放置
|
||||
if (data.thingsModels[i].datatype.params[j].isChart === 1) {
|
||||
// 图表
|
||||
data.thingsModels[i].datatype.params[j].name = "[" + data.thingsModels[i].name + "] " +
|
||||
data.thingsModels[i].datatype.params[j].name;
|
||||
data.chartList.push(data.thingsModels[i].datatype.params[j]);
|
||||
// 监测统计
|
||||
if (data.thingsModels[i].datatype.params[j].isHistory == 1) {
|
||||
data.statisticList.push(data.thingsModels[i].datatype.params[j]);
|
||||
}
|
||||
// 实时监测
|
||||
if (data.thingsModels[i].datatype.params[j].isMonitor == 1) {
|
||||
data.monitorList.push(data.thingsModels[i].datatype.params[j]);
|
||||
}
|
||||
data.thingsModels[i].datatype.params.splice(j--, 1);
|
||||
}
|
||||
}
|
||||
} else if (data.thingsModels[i].isChart === 1) {
|
||||
// 图表、实时监测、监测统计分类放置
|
||||
data.chartList.push(data.thingsModels[i]);
|
||||
// 监测统计
|
||||
if (data.thingsModels[i].isHistory == 1) {
|
||||
data.statisticList.push(data.thingsModels[i]);
|
||||
}
|
||||
// 实时监测
|
||||
if (data.thingsModels[i].isMonitor == 1) {
|
||||
data.monitorList.push(data.thingsModels[i]);
|
||||
}
|
||||
// 使用i--解决索引变更问题
|
||||
data.thingsModels.splice(i--, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 设备通道
|
||||
getChannelList (serialNumber) {
|
||||
this.channelQueryParams.deviceSipId = serialNumber;
|
||||
return new Promise((resolve, reject) => {
|
||||
listChannel(this.channelQueryParams).then(response => {
|
||||
this.channelTotal = response.total;
|
||||
response.rows.map(item => {
|
||||
item.statusInfo = getSipStatusInfo(item.status);
|
||||
return item;
|
||||
})
|
||||
resolve(response.rows);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
gotoDevicePlayer (deviceSipId, channelSipId, status) {
|
||||
let statusInfo = getSipStatusInfo(status)
|
||||
if (status !== 3) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: `无法查看${getSipStatusInfo(status).name}数据`
|
||||
});
|
||||
return;
|
||||
}
|
||||
// #ifdef H5
|
||||
uni.$u.route('/pages_player/list/devicePlayer', {
|
||||
deviceId: deviceSipId,
|
||||
channelSipId: channelSipId,
|
||||
});
|
||||
//#endif
|
||||
// #ifdef APP-PLUS
|
||||
uni.$u.route('/pages_player/list/devicePlayerApp', {
|
||||
deviceId: deviceSipId,
|
||||
channelSipId: channelSipId,
|
||||
});
|
||||
//#endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('timing.nosupported')
|
||||
});
|
||||
//#endif
|
||||
},
|
||||
async onReachBottom () {
|
||||
if (this.tabIndex === 0) {
|
||||
this.channelLoadStatus = 'loading';
|
||||
this.channelQueryParams.pageNum = this.channelQueryParams.pageNum + 1;
|
||||
if ((this.channelQueryParams.pageNum - 1) * this.channelQueryParams.pageSize >= this
|
||||
.channelTotal) {
|
||||
this.channelLoadStatus = 'nomore';
|
||||
} else {
|
||||
let list = await this.getChannelList(this.deviceForm.serialNumber);
|
||||
this.channelList = this.channelList.concat(list);
|
||||
this.channelLoadStatus = 'nomore';
|
||||
}
|
||||
}
|
||||
},
|
||||
// 返回监听
|
||||
onBackPress (options) {
|
||||
if (options.from === 'navigateBack') {
|
||||
return false;
|
||||
}
|
||||
uni.switchTab({
|
||||
url: '/pages/tabBar/home/index'
|
||||
});
|
||||
return true;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.card-title-wrap {
|
||||
display: flex;
|
||||
padding: 6px;
|
||||
padding-left: 0;
|
||||
border-bottom: 1px solid #efefef;
|
||||
}
|
||||
}
|
||||
|
||||
.channel_wrap {
|
||||
padding: 6px 0;
|
||||
|
||||
.item_body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
background: #fff;
|
||||
|
||||
.img {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 5px;
|
||||
width: 90px;
|
||||
height: 60px;
|
||||
background: #e5e5e5;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.status {
|
||||
position: absolute;
|
||||
right: 13px;
|
||||
top: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
324
pagesA/home/device/log/alertLog.vue
Normal file
324
pagesA/home/device/log/alertLog.vue
Normal file
@ -0,0 +1,324 @@
|
||||
<template>
|
||||
<view class="device-alert-log">
|
||||
<u-sticky offset-top="45" zIndex="98" bgColor="#eef3f7">
|
||||
<view class="nav-bar">
|
||||
<view class="left-wrap">
|
||||
<view v-if="!isSearch">
|
||||
<u-icon name="search" size="23" @click="isSearch = true"></u-icon>
|
||||
</view>
|
||||
<view v-else style="width: 100%;">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.alertName"
|
||||
placeholder="请输入告警名称" shape="circle" @clear="handleClearSearch" clearable>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.alertName"
|
||||
placeholder="请输入告警名称" shape="circle" @clear="handleClearSearch" clearable>
|
||||
<!-- #endif -->
|
||||
<template slot="prefix">
|
||||
<u-icon name="search" size="22" @click="isSearch = false"></u-icon>
|
||||
</template>
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('scene.search')" type="primary" shape="circle" size="mini"
|
||||
@click="handleSearch"></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-sticky>
|
||||
<view class="log-content">
|
||||
<view class="item-wrap" v-for="(item, index) in dataList" :key="index" @click="handleDispose(item)">
|
||||
<view class="title">
|
||||
<view class="name">{{item.deviceName}}</view>
|
||||
<view class="status"
|
||||
:class="{'success': item.alertLevel === 1, 'warning': item.alertLevel === 2, 'error': item.alertLevel === 3}">
|
||||
{{ getaLertLevelDisplay(item) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="describe">
|
||||
<view class="item">告警名称:{{item.alertName}}</view>
|
||||
<view class="item">
|
||||
处理状态:<text
|
||||
:class="{'success': item.status === 1 || item.status === 3, 'warning' : item.status === 2}">
|
||||
{{ getaStatusDisplay(item) }}</text>
|
||||
</view>
|
||||
<view class="item">数据:<rich-text :nodes="getaDataDetailDisplay(item)"></rich-text></view>
|
||||
<view class="item">告警时间:{{item.createTime}}</view>
|
||||
</view>
|
||||
<view class="tools" v-if="item.status === 2">
|
||||
<view class="btn"><u-button type="primary" size="mini" text="处理"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
</view>
|
||||
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize" loading-text="努力加载中..."
|
||||
loadmoreText="点击我加载更多" nomoreText="实在没有了" marginTop="20" @loadmore="loadMoreLogs" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listAlertLog } from '@/apis/modules/device.js';
|
||||
import { getAlertTemplateId } from '@/apis/modules/alertLog.js';
|
||||
|
||||
export default {
|
||||
name: "deviceAlertLog",
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
if (newVal.deviceId && newVal.deviceId !== oldVal.deviceId) {
|
||||
this.queryParams.serialNumber = newVal.serialNumber;
|
||||
this.getAlertLogList();
|
||||
}
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isSearch: true, // 是否开启搜索框
|
||||
queryParams: {
|
||||
alertName: null,
|
||||
serialNumber: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
dataList: [],
|
||||
total: 0, // 总条数
|
||||
loadmoreStatus: 'loadmore', // 加载更多
|
||||
tmpIds: null, // 模版id
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
const { deviceId, serialNumber } = this.device;
|
||||
if (deviceId) {
|
||||
this.queryParams.serialNumber = serialNumber;
|
||||
this.getAlertLogList();
|
||||
}
|
||||
this.getTmplIds(); // 获取模版id
|
||||
},
|
||||
methods: {
|
||||
getAlertLogList () {
|
||||
listAlertLog(this.queryParams).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (this.queryParams.pageNum == 1) {
|
||||
this.dataList = res.rows;
|
||||
} else {
|
||||
this.dataList = this.dataList.concat(res.rows);
|
||||
}
|
||||
this.total = res.total;
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loadmore';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
handleSearch () {
|
||||
this.dataList = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getAlertLogList();
|
||||
},
|
||||
handleClearSearch () {
|
||||
this.handleSearch();
|
||||
},
|
||||
// 处理
|
||||
handleDispose (item) {
|
||||
// 订阅通知
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: [this.tmpIds],
|
||||
success (res) {
|
||||
console.log(res);
|
||||
},
|
||||
fail (err) {
|
||||
console.error(err); //失败
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
uni.navigateTo({
|
||||
url: `/pages/tabBar/alert/edit?alertLogId=${item.alertLogId}&source=1`
|
||||
});
|
||||
},
|
||||
// 获取模板id
|
||||
getTmplIds () {
|
||||
getAlertTemplateId().then(res => {
|
||||
if (res.code == 200) {
|
||||
if (res.msg != '') {
|
||||
this.tmpIds = res.msg;
|
||||
} else {
|
||||
console.log('模板id为空!');
|
||||
}
|
||||
} else {
|
||||
console.log(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取告警级别显示
|
||||
getaLertLevelDisplay (item) {
|
||||
const { alertLevel } = item;
|
||||
if (alertLevel === 1) {
|
||||
return '提醒通知';
|
||||
} else if (alertLevel === 2) {
|
||||
return '轻微问题';
|
||||
} else if (alertLevel === 3) {
|
||||
return '严重警告';
|
||||
}
|
||||
},
|
||||
// 获取告警状态显示
|
||||
getaStatusDisplay (item) {
|
||||
const { status } = item;
|
||||
if (status === 1) {
|
||||
return '不需要处理';
|
||||
} else if (status === 2) {
|
||||
return '未处理';
|
||||
} else if (status === 3) {
|
||||
return '已处理';
|
||||
}
|
||||
},
|
||||
// 获取告警数据内容
|
||||
getaDataDetailDisplay (item) {
|
||||
const { detail } = item;
|
||||
if (detail) {
|
||||
const detailObj = JSON.parse(detail);
|
||||
return `<div>id:<span style="color:#F56C6C">${detailObj.id}</span>,value:<span style="color:#F56C6C">${detailObj.value}</span></div>`
|
||||
}
|
||||
},
|
||||
// 上拉加载
|
||||
loadMoreLogs () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getAlertLogList();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.dataList = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.getList();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.device-alert-log {
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
height: 74rpx;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.log-content {
|
||||
width: 100%;
|
||||
|
||||
.item-wrap {
|
||||
box-shadow: 0 2rpx 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10rpx;
|
||||
margin: 20rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding: 20rpx 0;
|
||||
|
||||
.name {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
margin-right: 20rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.describe {
|
||||
font-size: 28rpx;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
margin: 16rpx 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #5ac725;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #f9ae3d;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
</style>
|
74
pagesA/home/device/log/index.vue
Normal file
74
pagesA/home/device/log/index.vue
Normal file
@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<view class="device-log">
|
||||
<view class="cell-group">
|
||||
<u-cell-group :border="false">
|
||||
<u-cell icon="file-text" :title="$tt('home.eventLog')" :isLink="true" size="large"
|
||||
@click="handleEventLogClick"></u-cell>
|
||||
<u-cell icon="bookmark" :title="$tt('home.Instructionlog')" :isLink="true" size="large"
|
||||
@click="handleOrderLogClick"></u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'deviceLog',
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
this.deviceId = newVal.deviceId;
|
||||
this.serialNumber = newVal.serialNumber;
|
||||
this.productId = newVal.productId;
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
deviceId: 0,
|
||||
serialNumber: '',
|
||||
productId: '',
|
||||
thingsModel: {}, // 物模型
|
||||
deviceInfo: {} // 设备信息
|
||||
};
|
||||
},
|
||||
created () {
|
||||
// 获取设备状态(兼容H5和APP)
|
||||
if (this.device.serialNumber) {
|
||||
this.deviceInfo = this.device;
|
||||
this.deviceId = this.device.deviceId;
|
||||
this.serialNumber = this.device.serialNumber;
|
||||
this.productId = this.device.productId;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleEventLogClick () {
|
||||
uni.navigateTo({
|
||||
url: `/pagesB/home/device/log/event?serialNumber=${this.serialNumber}&productId=${this.productId}`
|
||||
});
|
||||
},
|
||||
handleOrderLogClick () {
|
||||
uni.navigateTo({
|
||||
url: `/pagesB/home/device/log/order?deviceId=${this.deviceId}&serialNumber=${this.serialNumber}`
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.device-log {
|
||||
.cell-group {
|
||||
margin: 20rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
711
pagesA/home/device/model.vue
Normal file
711
pagesA/home/device/model.vue
Normal file
@ -0,0 +1,711 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.physicalModels')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="device-model-wrap">
|
||||
<view class="container-wrap">
|
||||
<view class="group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in list" :key="index">
|
||||
<u-cell :border="false" isLink
|
||||
:arrow-direction="item.datatype.type === 'object' || item.datatype.type === 'array' ? 'left' : 'down'"
|
||||
@click="handleModel(item)">
|
||||
<view slot="title"> {{item.name}}</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="list.length === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
|
||||
<!-- integer、decimal 类型 -->
|
||||
<u-popup :show="isInteger" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isInteger = false">
|
||||
<view class="integer-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isInteger = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmInteger">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="select-wrap">
|
||||
<view class="num-wrap">
|
||||
<text class="num">{{value}}</text>
|
||||
<text class="unit">{{model.datatype.unit}}</text>
|
||||
</view>
|
||||
<view class="slider-wrap">
|
||||
<u-slider :min="model.datatype.min === undefined ? 0 : model.datatype.min"
|
||||
:max="model.datatype.max === undefined ? 100 : model.datatype.max"
|
||||
:step="model.datatype.step === undefined ? 1 : model.datatype.step"
|
||||
@input="handleSliderChange"></u-slider>
|
||||
<view class="limit">
|
||||
<text>
|
||||
{{`${model.datatype.min === undefined ? 0 : model.datatype.min} ${model.datatype.unit || ''}`}}
|
||||
</text>
|
||||
<text>
|
||||
{{`${model.datatype.max === undefined ? 0 : model.datatype.max} ${model.datatype.unit || ''}`}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- bool 类型 -->
|
||||
<u-popup :show="isBool" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isBool = false">
|
||||
<view class="bool-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isBool = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmBool">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="radio-group-wrap">
|
||||
<u-radio-group v-model="value" :borderBottom="false" placement="column" iconPlacement="right">
|
||||
<view class="radio-wrap">
|
||||
<u-radio :label="$tt('product.on')" :name="1" iconSize="16"></u-radio>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-radio :label="$tt('product.off')" :name="0" iconSize="16"></u-radio>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- enum 类型 -->
|
||||
<u-popup :show="isEnum" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isEnum = false">
|
||||
<view class="enum-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isEnum = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmEnum">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="radio-group-wrap">
|
||||
<u-radio-group v-model="value" :borderBottom="false" placement="column" iconPlacement="right">
|
||||
<view class="radio-wrap" v-for="(item, index) in model.datatype.enumList" :key="index">
|
||||
<u-radio :label="item.text" :name="item.value" iconSize="16"></u-radio>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- string 类型 -->
|
||||
<u-popup :show="isString" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isString = false">
|
||||
<view class="string-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isString = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmString">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="select-wrap">
|
||||
<view class="input-wrap">
|
||||
<u--input v-model="value" :customStyle="{ padding: '24rpx' }"
|
||||
:placeholder="$tt('deviceDetail.inputMsg')" border="surround" fontSize="16"
|
||||
clearable></u--input>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 操作符 -->
|
||||
<u-popup :show="isOperator" :round="5" mode="bottom" :closeOnClickOverlay="true" zIndex="10076"
|
||||
@close="isOperator = false">
|
||||
<view class="operator-popup-wrap">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in oprGroup" :key="index"
|
||||
v-if="item.types.length === 0 || item.types.includes(model.datatype.type)">
|
||||
<u-cell :title="item.name" :name="item.value"
|
||||
@click="handleConfirmOperator(item)"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('common.cancel')" name="cancel" :border="false"
|
||||
@click="handleConfirmOperator({ value: 'cancel' })"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getCacheThingsModel
|
||||
} from '@/apis/modules/device.js';
|
||||
import {
|
||||
navigateBackTo
|
||||
} from '@/utils/common.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
editIndex: null, // null 代表新增
|
||||
parentId: null,
|
||||
list: [], // 属性
|
||||
isInteger: false, // 整型
|
||||
oprGroup: [{
|
||||
types: [],
|
||||
name: '等于(=)',
|
||||
value: '=',
|
||||
},
|
||||
{
|
||||
types: [],
|
||||
name: '不等于(!=)',
|
||||
value: '!=',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '大于(>)',
|
||||
value: '>',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '小于(<)',
|
||||
value: '<',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '大于等于(>=)',
|
||||
value: '>=',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '小于等于(<=)',
|
||||
value: '<=',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '在...之间(between)',
|
||||
value: 'between',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '不在...之间(notBetween)',
|
||||
value: 'notBetween',
|
||||
},
|
||||
{
|
||||
types: ['string'],
|
||||
name: '包含(contain)',
|
||||
value: 'contain',
|
||||
},
|
||||
{
|
||||
types: ['string'],
|
||||
name: '不包含(notContain)',
|
||||
value: 'notContain',
|
||||
}
|
||||
],
|
||||
isOperator: false, // 操作符
|
||||
isBool: false,
|
||||
isEnum: false, // 枚举
|
||||
isString: false,
|
||||
model: {
|
||||
datatype: {},
|
||||
},
|
||||
value: null, // 属性值
|
||||
action: {},
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
const {
|
||||
data,
|
||||
editIndex
|
||||
} = option;
|
||||
this.editIndex = Number(editIndex);
|
||||
if (data) {
|
||||
this.list = JSON.parse(data);
|
||||
} else {
|
||||
const {
|
||||
productId
|
||||
} = uni.getStorageSync('timingData');
|
||||
this.action = uni.getStorageSync('action');
|
||||
this.productId = productId;
|
||||
this.$nextTick(() => {
|
||||
this.getModelDatas();
|
||||
})
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 获取区设备列表
|
||||
getModelDatas () {
|
||||
getCacheThingsModel(this.productId)
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
let data = JSON.parse(res.data);
|
||||
this.list = this.formatArrayIndex(data);
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
// 物模型格式化处理
|
||||
formatArrayIndex (data) {
|
||||
let obj = {
|
||||
...data
|
||||
};
|
||||
for (let o in obj) {
|
||||
obj[o] = obj[o].map((item) => {
|
||||
item = {
|
||||
...item,
|
||||
level: 'parent'
|
||||
};
|
||||
if (item.datatype.type === 'array') {
|
||||
let arrayModel = [];
|
||||
for (let k = 0; k < item.datatype.arrayCount; k++) {
|
||||
let index = k > 9 ? String(k) : '0' + k;
|
||||
if (item.datatype.arrayType === 'object') {
|
||||
arrayModel.push({
|
||||
id: index,
|
||||
name: item.name + ' ' + (k + 1),
|
||||
level: 'index',
|
||||
datatype: {
|
||||
type: item.datatype.arrayType,
|
||||
params: item.datatype.params
|
||||
},
|
||||
});
|
||||
} else {
|
||||
arrayModel.push({
|
||||
id: index,
|
||||
name: item.name + ' ' + (k + 1),
|
||||
level: 'index',
|
||||
datatype: {
|
||||
type: item.datatype.arrayType,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
item.datatype.arrayModel = arrayModel;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
let list = [];
|
||||
if (this.action.type == 1) {
|
||||
list = obj.properties || [];
|
||||
} else if (this.action.type == 2) {
|
||||
list = obj.functions || [];
|
||||
} else if (this.action.type == 3) {
|
||||
list = obj.events || [];
|
||||
}
|
||||
list = list.filter((item) => {
|
||||
if (item.datatype.params && item.datatype.params.length !== 0) {
|
||||
item.datatype.params = item.datatype.params.filter((item) => item.isMonitor == 0 && item
|
||||
.isReadonly == 0);
|
||||
}
|
||||
return item.isMonitor == 0 && item.isReadonly == 0;
|
||||
});
|
||||
return list;
|
||||
},
|
||||
// 选中选择
|
||||
handleModel (item) {
|
||||
this.model = item;
|
||||
// 存储 parentId 和 arrayIndex
|
||||
let action = uni.getStorageSync('action');
|
||||
if (item.level === 'parent') {
|
||||
action = {
|
||||
...action,
|
||||
parentId: item.id,
|
||||
parentName: item.name,
|
||||
arrayIndex: '',
|
||||
arrayIndexName: ''
|
||||
};
|
||||
}
|
||||
if (item.level === 'index') {
|
||||
action = {
|
||||
...action,
|
||||
arrayIndex: item.id,
|
||||
arrayIndexName: item.name
|
||||
};
|
||||
}
|
||||
uni.setStorageSync('action', action);
|
||||
// 根据类型弹出对应框
|
||||
const {
|
||||
datatype
|
||||
} = item;
|
||||
const {
|
||||
type,
|
||||
params,
|
||||
arrayModel
|
||||
} = datatype;
|
||||
this.value = null;
|
||||
if (type === 'integer' || type === 'decimal') {
|
||||
this.isInteger = true;
|
||||
this.value = datatype.min ? datatype.min : 0;
|
||||
this.valueA = null;
|
||||
this.valueB = null;
|
||||
} else if (type === 'bool') {
|
||||
this.isBool = true;
|
||||
} else if (type === 'enum') {
|
||||
this.isEnum = true;
|
||||
} else if (type === 'string') {
|
||||
this.isString = true
|
||||
} else if (type === 'object') {
|
||||
uni.$u.route('/pagesA/home/device/model', {
|
||||
type: this.type,
|
||||
data: JSON.stringify(params),
|
||||
editIndex: this.editIndex
|
||||
});
|
||||
} else if (type === "array") {
|
||||
uni.$u.route('/pagesA/home/device/model', {
|
||||
type: this.type,
|
||||
data: JSON.stringify(arrayModel),
|
||||
editIndex: this.editIndex
|
||||
});
|
||||
}
|
||||
},
|
||||
// slider 负数会有问题,所以采用这方式
|
||||
handleSliderChange (value) {
|
||||
if (this.model.datatype.step) {
|
||||
const str = this.model.datatype.step.toString();
|
||||
const places = str.match(/\.(\d+)/);
|
||||
const num = places ? places[1].length : 0;
|
||||
this.value = value.toFixed(num);
|
||||
} else {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
// 整型、浮点型
|
||||
handleConfirmInteger () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: this.value,
|
||||
valueName: ''
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isInteger = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/home/device/timing/detail');
|
||||
},
|
||||
// bool 类型
|
||||
handleConfirmBool () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
if (this.value === null) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectValue')
|
||||
});
|
||||
return;
|
||||
};
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: this.value,
|
||||
valueName: this.value === 1 ? this.$tt('product.on') : this.$tt('product.off')
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isBool = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/home/device/timing/detail');
|
||||
},
|
||||
// enum 类型
|
||||
handleConfirmEnum () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
if (!this.value) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectValue')
|
||||
});
|
||||
return;
|
||||
};
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const obj = this.model.datatype.enumList.find(item => item.value === this.value);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: this.value,
|
||||
valueName: obj.text,
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isEnum = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/home/device/timing/detail');
|
||||
},
|
||||
// string 类型
|
||||
handleConfirmString () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
if (!this.value) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.inputValue')
|
||||
});
|
||||
return;
|
||||
};
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: this.value,
|
||||
valueName: ''
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isString = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/home/device/timing/detail');
|
||||
},
|
||||
// 获取model的id和name
|
||||
getModelIdAndName (id, name) {
|
||||
let tempId = id;
|
||||
let tempName = name;
|
||||
const {
|
||||
parentId,
|
||||
parentName,
|
||||
arrayIndex,
|
||||
arrayIndexName
|
||||
} = uni.getStorageSync('action');
|
||||
if (arrayIndex) { // 代表是数组类型
|
||||
if (arrayIndex === id) { // 数组
|
||||
tempId = `array_${arrayIndex}_${parentId}`;
|
||||
tempName = parentName;
|
||||
} else { // 数组对象
|
||||
tempId = `array_${arrayIndex}_${id}`;
|
||||
}
|
||||
} else {
|
||||
tempId = id;
|
||||
}
|
||||
return {
|
||||
tempId,
|
||||
tempName
|
||||
}
|
||||
},
|
||||
// 存储数据
|
||||
setModelData (data) {
|
||||
let action = uni.getStorageSync('action');
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
valueName
|
||||
} = data;
|
||||
action = {
|
||||
...action,
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
valueName
|
||||
};
|
||||
uni.setStorageSync('action', action);
|
||||
// 更新或者插入新的触发或者执行
|
||||
let {
|
||||
actions,
|
||||
...res
|
||||
} = uni.getStorageSync('timingData');
|
||||
if (Number.isNaN(this.editIndex) || this.editIndex === null) {
|
||||
actions.push(action);
|
||||
uni.setStorageSync('timingData', {
|
||||
actions,
|
||||
...res
|
||||
});
|
||||
} else {
|
||||
let list = actions.map((item, i) => {
|
||||
if (i == this.editIndex) {
|
||||
return action
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
});
|
||||
uni.setStorageSync('timingData', {
|
||||
actions: [...list],
|
||||
...res
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.device-model-wrap {
|
||||
.container-wrap {
|
||||
.group-wrap {
|
||||
margin: 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 7rpx 0;
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.integer-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.select-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
padding: 40rpx 20rpx;
|
||||
|
||||
.form-wrap {
|
||||
.form-item-wrap {
|
||||
padding: 4rpx 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.num-wrap {
|
||||
position: relative;
|
||||
margin-top: 60rpx;
|
||||
text-align: center;
|
||||
|
||||
.num {
|
||||
font-size: 80rpx;
|
||||
}
|
||||
|
||||
.unit {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -23rpx;
|
||||
font-size: 32rpx;
|
||||
color: #868686;
|
||||
margin-left: 60rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-wrap {
|
||||
.limit {
|
||||
font-size: 28rpx;
|
||||
color: #868686;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.range-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operator-popup-wrap {
|
||||
padding: 10rpx 0 20rpx;
|
||||
|
||||
.cell-group-wrap {
|
||||
background: #eef3f7;
|
||||
|
||||
.cell-wrap {
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
padding: 10rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bool-popup-wrap,
|
||||
.enum-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.radio-group-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.radio-wrap {
|
||||
padding: 40rpx;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.string-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.select-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
padding: 40rpx 20rpx;
|
||||
|
||||
.form-wrap {
|
||||
.form-item-wrap {
|
||||
padding: 4rpx 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
margin: 32rpx 12rpx 20rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
354
pagesA/home/device/monitor/index.vue
Normal file
354
pagesA/home/device/monitor/index.vue
Normal file
@ -0,0 +1,354 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view style="padding:10px;margin:1px 0 15px;background-color:#fff;">
|
||||
<u--text :text="$tt('monitior.monitoringInterval')"
|
||||
customStyle="padding-bottom:6px;font-size:12px;color:#3fd1ad"></u--text>
|
||||
<u-row :gutter="10">
|
||||
<u-col span="3">
|
||||
<u--input :placeholder=" $tt('monitior.inputNumber')" placeholderStyle="font-size: 12px"
|
||||
v-model="monitorInterval" border="surround" color="#666" customStyle="padding:4px;"></u--input>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<u--input :placeholder="$tt('monitior.inputTimes')" placeholderStyle="font-size: 12px"
|
||||
v-model="monitorNumber" border="surround" color="#666" customStyle="padding:4px"></u--input>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<u-button type="warning" :text="$tt('monitior.stop')" @tap="stopMonitor()" size="small"
|
||||
:disabled="deviceInfo.monitorList && deviceInfo.monitorList.length == 0"></u-button>
|
||||
</u-col>
|
||||
<u-col span="3">
|
||||
<u-button type="success" :text="$tt('monitior.monitior')" @tap="updateMonitorParameters()"
|
||||
size="small"
|
||||
:disabled="deviceInfo.monitorList && deviceInfo.monitorList.length == 0"></u-button>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
|
||||
<view class="charts-box" v-for="(chart, index) in monitorChart" :key="index">
|
||||
<qiun-data-charts type="area" :canvas2d="true" :reshow="reshow" :opts="chart.opts" :chartData="chart.data"
|
||||
:animation="false" tooltipFormat="tooltipStatistic" />
|
||||
<view style="position:relative;bottom:100px;">
|
||||
<u-loading-icon :text="$tt('monitior.receivingData')" textSize="14" :show="chart.show"></u-loading-icon>
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="data" :show="deviceInfo.monitorList && deviceInfo.monitorList.length === 0"
|
||||
marginTop="30"></u-empty>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listMonitor } from '@/apis/modules/deviceLog';
|
||||
|
||||
export default {
|
||||
name: 'device-monitor',
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
},
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
show: function (newVal, oldVal) {
|
||||
this.reshow = newVal;
|
||||
},
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
this.initChart(); // 初始化图表
|
||||
uni.stopPullDownRefresh();
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
reshow: false,
|
||||
// 图表数据集合
|
||||
monitorChart: [{
|
||||
opts: {},
|
||||
data: {
|
||||
categories: [],
|
||||
series: []
|
||||
},
|
||||
show: false,
|
||||
id: ''
|
||||
}],
|
||||
// 设备信息
|
||||
deviceInfo: {
|
||||
monitorList: [],
|
||||
},
|
||||
// 实时监测间隔
|
||||
monitorInterval: 1000,
|
||||
// 实时监测次数
|
||||
monitorNumber: 30,
|
||||
// 停止实时监测
|
||||
isStopMonitor: true
|
||||
};
|
||||
},
|
||||
created () {
|
||||
// 获取显示状态(兼容H5和APP)
|
||||
if (this.show != null) {
|
||||
this.reshow = this.show;
|
||||
}
|
||||
if (this.device.monitorList && this.device.monitorList.length !== 0) {
|
||||
this.deviceInfo = this.device;
|
||||
this.initChart(); // 初始化图表
|
||||
uni.stopPullDownRefresh();
|
||||
}
|
||||
|
||||
// 监听回调
|
||||
this.$nextTick(function () {
|
||||
this.mqttCallback();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
/* Mqtt回调处理 */
|
||||
mqttCallback () {
|
||||
this.$mqttTool.client.on('message', (topic, message, buffer) => {
|
||||
let topics = topic.split('/');
|
||||
let productId = topics[1];
|
||||
let deviceNum = topics[2];
|
||||
message = JSON.parse(message.toString());
|
||||
if (topics[3] == 'monitor' && !this.isStopMonitor) {
|
||||
console.log('接收到【实时监测】主题:', topic);
|
||||
console.log('接收到【实时监测】内容:', message);
|
||||
// 实时监测
|
||||
for (let k = 0; k < message.length; k++) {
|
||||
let value = message[k].value;
|
||||
let id = message[k].id;
|
||||
let remark = message[k].remark;
|
||||
// 数据加载到图表
|
||||
for (let i = 0; i < this.monitorChart.length; i++) {
|
||||
this.monitorChart[i].show = false;
|
||||
if (id == this.monitorChart[i].id) {
|
||||
if (this.monitorChart[i].data.series[0].data.length > 50) {
|
||||
this.monitorChart[i].data.series[0].data.shift();
|
||||
}
|
||||
this.monitorChart[i].data.categories.push(this.getTime());
|
||||
this.monitorChart[i].data.series[0].data.push(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 更新实时监测参数*/
|
||||
updateMonitorParameters () {
|
||||
this.isStopMonitor = false;
|
||||
if (this.monitorInterval < 500 || this.monitorInterval > 10000) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('monitior.MonitoringRange'),
|
||||
duration: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.monitorNumber == 0 || this.monitorNumber > 300) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('monitior.monitoringQuantity'),
|
||||
duration: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 清空图表数据
|
||||
for (let i = 0; i < this.monitorChart.length; i++) {
|
||||
this.monitorChart[i].data.series[0].data = [];
|
||||
this.monitorChart[i].data.categories = [];
|
||||
this.monitorChart[i].show = true;
|
||||
}
|
||||
this.publishMonitor({
|
||||
name: this.$tt('monitior.real-timeMonitor'),
|
||||
value: this.monitorNumber
|
||||
|
||||
});
|
||||
},
|
||||
/*停止实时监测*/
|
||||
stopMonitor () {
|
||||
this.isStopMonitor = true;
|
||||
// 发布指令
|
||||
this.publishMonitor({
|
||||
name: this.$tt('monitior.stopMonitor'),
|
||||
value: 0
|
||||
});
|
||||
},
|
||||
/* 发布消息 */
|
||||
publishMonitor (model) {
|
||||
// 实时监测
|
||||
const { productId, serialNumber } = this.deviceInfo;
|
||||
let topic = '/' + productId + '/' + serialNumber + '/monitor/get';
|
||||
let message = '{"count":' + model.value + ',"interval":' + this.monitorInterval + '}';
|
||||
if (topic != '') {
|
||||
this.$mqttTool.publish(topic, message, model.name); // 发布
|
||||
}
|
||||
},
|
||||
// 获取当前时间
|
||||
getTime () {
|
||||
let date = new Date();
|
||||
let y = date.getFullYear();
|
||||
let m = date.getMonth() + 1;
|
||||
let d = date.getDate();
|
||||
let H = date.getHours();
|
||||
let mm = date.getMinutes();
|
||||
let s = date.getSeconds();
|
||||
m = m < 10 ? '0' + m : m;
|
||||
d = d < 10 ? '0' + d : d;
|
||||
H = H < 10 ? '0' + H : H;
|
||||
return y + '-' + m + '-' + d + ' ' + H + ':' + mm + ':' + s;
|
||||
},
|
||||
// 初始化图表
|
||||
initChart () {
|
||||
let color = ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4',
|
||||
'#ea7ccc'
|
||||
];
|
||||
this.monitorChart = [];
|
||||
if (this.deviceInfo.monitorList && this.deviceInfo.monitorList.length !== 0) {
|
||||
for (let i = 0; i < this.deviceInfo.monitorList.length; i++) {
|
||||
let data = {};
|
||||
let res = {
|
||||
categories: [],
|
||||
series: [{
|
||||
name: this.deviceInfo.monitorList[i].name + '(单位 ' + (this.deviceInfo
|
||||
.monitorList[i].datatype.unit !=
|
||||
undefined ? this.deviceInfo.monitorList[i].datatype.unit : '无') + ')',
|
||||
data: []
|
||||
}]
|
||||
};
|
||||
data = JSON.parse(JSON.stringify(res));
|
||||
let opts = {
|
||||
// 自定义名称和单位,用于图表提示框显示
|
||||
name: this.deviceInfo.monitorList[i].name,
|
||||
unit: this.deviceInfo.monitorList[i].datatype.unit,
|
||||
update: true,
|
||||
timing: 'easeOut',
|
||||
duration: 1000,
|
||||
color: [color[i % color.length]],
|
||||
padding: [15, 15, 0, 15],
|
||||
fontSize: 12,
|
||||
fontColor: '#666666',
|
||||
dataLabel: false, //是否显示值
|
||||
dataPointShape: true,
|
||||
dataPointShapeType: 'solid',
|
||||
touchMoveLimit: 60,
|
||||
enableScroll: true,
|
||||
enableMarkLine: false,
|
||||
legend: {
|
||||
show: true,
|
||||
position: 'bottom',
|
||||
float: 'center',
|
||||
padding: 5,
|
||||
margin: 5,
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
borderColor: 'rgba(0,0,0,0)',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
fontColor: '#333333',
|
||||
lineHeight: 16,
|
||||
hiddenColor: '#CECECE',
|
||||
itemGap: 10
|
||||
},
|
||||
xAxis: {
|
||||
disableGrid: true,
|
||||
disabled: true, // 隐藏分类名称
|
||||
axisLine: true,
|
||||
axisLineColor: '#CCCCCC',
|
||||
calibration: false,
|
||||
fontColor: '#666666',
|
||||
fontSize: 13,
|
||||
rotateLabel: false,
|
||||
rotateAngle: 45,
|
||||
itemCount: 30,
|
||||
boundaryGap: 'justify',
|
||||
splitNumber: 5,
|
||||
gridColor: '#CCCCCC',
|
||||
gridType: 'solid',
|
||||
dashLength: 1,
|
||||
gridEval: 1,
|
||||
scrollShow: false,
|
||||
scrollAlign: 'right',
|
||||
scrollColor: '#A6A6A6',
|
||||
scrollBackgroundColor: '#ccc',
|
||||
format: ''
|
||||
},
|
||||
yAxis: {
|
||||
gridType: 'dash',
|
||||
dashLength: 2,
|
||||
disabled: false,
|
||||
tofix: 3, //刻度保留小数位数
|
||||
disableGrid: false,
|
||||
splitNumber: 5,
|
||||
gridColor: '#CCCCCC',
|
||||
padding: 10,
|
||||
showTitle: false,
|
||||
data: []
|
||||
},
|
||||
extra: {
|
||||
area: {
|
||||
type: 'curve',
|
||||
opacity: 0.5,
|
||||
addLine: true,
|
||||
width: 1,
|
||||
gradient: false
|
||||
},
|
||||
tooltip: {
|
||||
showBox: true,
|
||||
showArrow: true,
|
||||
showCategory: true,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
borderColor: '#000000',
|
||||
borderOpacity: 0.7,
|
||||
bgColor: '#000000',
|
||||
bgOpacity: 0.7,
|
||||
gridType: 'solid',
|
||||
dashLength: 4,
|
||||
gridColor: '#CCCCCC',
|
||||
fontColor: '#FFFFFF',
|
||||
splitLine: true,
|
||||
horizentalLine: false,
|
||||
xAxisLabel: false,
|
||||
yAxisLabel: false,
|
||||
labelBgColor: '#FFFFFF',
|
||||
labelBgOpacity: 0.7,
|
||||
labelFontColor: '#666666'
|
||||
},
|
||||
markLine: {
|
||||
type: 'solid',
|
||||
dashLength: 2,
|
||||
data: []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
opts && this.monitorChart.push({
|
||||
opts: opts,
|
||||
data: data,
|
||||
show: false,
|
||||
id: this.deviceInfo.monitorList[i].id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
/* background-color: #fff; */
|
||||
}
|
||||
|
||||
/* 请根据实际需求修改父元素尺寸,组件自动识别宽高 */
|
||||
.charts-box {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background-color: #ffffff;
|
||||
padding: 10px 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
71
pagesA/home/device/scada.vue
Normal file
71
pagesA/home/device/scada.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<view class="device-scada">
|
||||
<web-view v-if="deviceInfo.guid && show" :src="scadaUrl"></web-view>
|
||||
<u-empty mode="data" :show="!deviceInfo.guid" marginTop="60" :text="$tt('deviceDetail.emptyNull')"></u-empty>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import projectConfig from '@/env.config.js';
|
||||
|
||||
export default {
|
||||
name: "deviceScada",
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
},
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
},
|
||||
show: function (newVal, oldVal) {
|
||||
if (newVal) {
|
||||
if (this.deviceInfo.guid) {
|
||||
this.scadaUrl = this.getScadaUrl();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
deviceInfo: {
|
||||
guid: ''
|
||||
},
|
||||
scadaUrl: ''
|
||||
};
|
||||
},
|
||||
mounted () {},
|
||||
methods: {
|
||||
getScadaUrl () {
|
||||
const { guid, serialNumber } = this.deviceInfo;
|
||||
let baseUrl = projectConfig.baseUrl;
|
||||
// 去掉 '/prod-api/' 部分
|
||||
if (baseUrl.includes('prod-api')) {
|
||||
baseUrl = baseUrl.replace('/prod-api/', '/');
|
||||
}
|
||||
const token = uni.getStorageSync('token');
|
||||
return baseUrl +
|
||||
`${'scada/topo/fullscreen'}?guid=${guid}&type=1&serialNumber=${serialNumber}&share=${token}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-scada {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 50px;
|
||||
}
|
||||
</style>
|
500
pagesA/home/device/statistic/history.vue
Normal file
500
pagesA/home/device/statistic/history.vue
Normal file
@ -0,0 +1,500 @@
|
||||
<template>
|
||||
<view class="device-history">
|
||||
<view class="tools-wrap">
|
||||
<view class="time-shortcut">
|
||||
<uni-data-select v-model="queryParams.timeShortcut" :localdata="timeShortcuts"
|
||||
@change="handleTimeShortcutChange" :clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="date-wrap" @click="handleDateTimeClick">
|
||||
<u--input
|
||||
customStyle="height: 68rpx; padding: 0 18rpx; background-color:transparent; pointer-events:none;"
|
||||
:placeholder="$tt('deviceHistory.inputDate')" :value="formatDate(queryParams.strTime)" disabled>
|
||||
</u--input>
|
||||
</view>
|
||||
<view class="filter-btn">
|
||||
<u-icon :label="$tt('deviceHistory.filtrate')" labelPos="left" name="hourglass" size="18"
|
||||
@click="handleFilterClick"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 折线图 -->
|
||||
<view class="charts-box" v-if="historys.length !== 0">
|
||||
<qiun-data-charts type="line" :opts="chartOpts" :chartData="chartData" canvasId="multiLineChart" />
|
||||
</view>
|
||||
<!-- 表格 -->
|
||||
<view class="table-box" v-if="historys.length !== 0">
|
||||
<uni-table :border="false" :show="devTotal === 0" stripe :emptyText="$tt('deviceHistory.emptyTable')">
|
||||
<uni-tr>
|
||||
<uni-td align="center">{{ $tt('deviceHistory.updateTime') }}</uni-td>
|
||||
<uni-td align="center" v-for="item in tableHeaders" :key="item.value">{{item.name}}</uni-td>
|
||||
</uni-tr>
|
||||
<uni-tr v-for="item in tableCom" :key="item.value">
|
||||
<uni-td align="center">{{item.time}}</uni-td>
|
||||
<uni-td align="center" v-for="chil in tableHeaders" :key="chil.value">{{item[chil.value]}}</uni-td>
|
||||
</uni-tr>
|
||||
</uni-table>
|
||||
<view class="pagination-box">
|
||||
<uni-pagination show-icon :page-size="devPageSize" :current="devPageNum" :total="devTotal"
|
||||
@change="getTableComsList" />
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="data" :show="historys.length === 0" :text="$tt('deviceHistory.emptyData')"
|
||||
marginTop="30"></u-empty>
|
||||
<view class="other-wrap">
|
||||
<!-- 日期选择器 -->
|
||||
<u-datetime-picker ref="datetimePicker" :show="isDateTime" mode="datetime" :closeOnClickOverlay="true"
|
||||
:confirmText="dateTimeIndex > 1 ? $tt('deviceHistory.confirm') : $tt('deviceHistory.nextStep')"
|
||||
:cancelText="$tt('deviceHistory.cancel')" @confirm="handleConfirmDateTime"
|
||||
@cancel="handleCancelDateTime" @close="handleCloseDateTime">
|
||||
</u-datetime-picker>
|
||||
<u-popup :show="isFilter" mode="top" :safeAreaInsetBottom="false" @close="isFilter = false">
|
||||
<view class="filter-pop">
|
||||
<view class="content-wrap">
|
||||
<!-- <view class="title-wrap" v-if="deviceInfo.deviceType === 2">子设备</view>
|
||||
<view class="check-box-wrap" v-if="deviceInfo.deviceType === 2">
|
||||
<uni-data-checkbox mode="tag" v-model="queryParams.slaveId"
|
||||
:localdata="slaveList"></uni-data-checkbox>
|
||||
</view> -->
|
||||
<view class="title-wrap">{{ $tt('deviceHistory.variable')}}</view>
|
||||
<view class="check-box-wrap">
|
||||
<uni-data-checkbox mode="tag" multiple v-model="queryParams.identifiers"
|
||||
:localdata="identifierList" @change="handleIdentifierCheck"></uni-data-checkbox>
|
||||
</view>
|
||||
</view>
|
||||
<u-line direction='row'></u-line>
|
||||
<view class="bottom-wrap">
|
||||
<u-button customStyle="border: none;" plain :text="$tt('deviceHistory.cancel')"
|
||||
@click="isFilter = false"></u-button>
|
||||
<u-line direction='col' length="40"></u-line>
|
||||
<u-button customStyle="border: none;" type="primary" plain :text="$tt('deviceHistory.confirm')"
|
||||
@click="handleConfirmFilter"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import uniDataCheckbox from '@/pagesA/components/uni-data-checkbox/index.vue';
|
||||
import uniPagination from '@/pagesA/components/uni-pagination/index.vue';
|
||||
import { getSubGatewayList } from '@/apis/modules/gateway.js';
|
||||
import { listThingsModel } from '@/apis/modules/device.js';
|
||||
import { getHistoryList } from '@/apis/modules/deviceLog.js';
|
||||
|
||||
export default {
|
||||
name: 'deviceHistory',
|
||||
components: {
|
||||
uniDataCheckbox,
|
||||
uniPagination
|
||||
},
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
require: true,
|
||||
},
|
||||
type: Number
|
||||
},
|
||||
watch: {
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
},
|
||||
type (val) {
|
||||
if (val === 3) {
|
||||
// const { deviceType } = this.deviceInfo || {};
|
||||
// if (deviceType === 2) {
|
||||
// this.getSlaveDatas();
|
||||
// }
|
||||
this.getData();
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const beginTime = new Date().getTime() - 3600 * 1000 * 24;
|
||||
const endTime = new Date().getTime();
|
||||
|
||||
return {
|
||||
deviceInfo: {}, // 设备信息
|
||||
deviceId: null,
|
||||
// 时间快捷数据列表
|
||||
timeShortcuts: [{
|
||||
value: 0,
|
||||
text: this.$tt('deviceHistory.lastTwoHours')
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
text: this.$tt('deviceHistory.lastOneDay')
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
text: this.$tt('deviceHistory.lastThirtyDay')
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
text: this.$tt('deviceHistory.custom')
|
||||
},
|
||||
],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
timeShortcut: 1,
|
||||
strTime: `${beginTime}-${endTime}`,
|
||||
beginTime: beginTime,
|
||||
endTime: endTime,
|
||||
slaveId: '',
|
||||
identifiers: [],
|
||||
},
|
||||
isDateTime: false, // 是否开启日期选择器
|
||||
dateTimeIndex: 1, // 次数
|
||||
isFilter: false, // 是否开启筛选
|
||||
slaveList: [], // 子设备列表
|
||||
identifierList: [], // 变量列表
|
||||
// chart option
|
||||
chartOpts: {
|
||||
color: ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4',
|
||||
'#ea7ccc'
|
||||
], // 三组数据的颜色
|
||||
padding: [15, 10, 0, 15], // 图表的内边距
|
||||
dataLabel: false, // 不显示数据标签
|
||||
dataPointShape: false, // 显示数据点形状
|
||||
enableScroll: false, // 禁用图表滚动
|
||||
legend: {}, // 启用图例
|
||||
xAxis: {
|
||||
disableGrid: true, // 禁用X轴网格线
|
||||
disabled: true, // 禁用x轴的显示
|
||||
},
|
||||
yAxis: {
|
||||
disableGrid: false, // 禁用Y轴网格线
|
||||
data: [{
|
||||
// min: 0,
|
||||
// max: 100 // 设置Y轴的最小值和最大值
|
||||
}],
|
||||
},
|
||||
extra: {
|
||||
line: {
|
||||
type: "step", // 直线类型的折线图
|
||||
width: 1, // 线条宽度
|
||||
activeType: "hollow", // 激活状态下的线条样式
|
||||
linearType: "custom" // 自定义线条渐变颜色
|
||||
},
|
||||
},
|
||||
},
|
||||
// 存储图表数据
|
||||
chartData: {
|
||||
categories: [],
|
||||
series: []
|
||||
},
|
||||
historys: [], // 历史数据
|
||||
tableHeaders: [], // 设备列表table数据
|
||||
tableComTemp: [],
|
||||
devPageSize: 10,
|
||||
devPageNum: 1,
|
||||
devTotal: 0, // 数据总量
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tableCom () {
|
||||
const start = (this.devPageNum - 1) * this.devPageSize;
|
||||
const end = start + this.devPageSize;
|
||||
return this.tableComTemp.slice(start, end);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 快捷时间选择
|
||||
handleTimeShortcutChange (val) {
|
||||
if (val === 3) {
|
||||
return;
|
||||
}
|
||||
const endTime = new Date().getTime();
|
||||
let beginTime = null;
|
||||
if (val === 0) {
|
||||
beginTime = endTime - 3600 * 1000 * 2;
|
||||
} else if (val === 1) {
|
||||
beginTime = endTime - 3600 * 1000 * 24;
|
||||
} else if (val === 2) {
|
||||
beginTime = endTime - 3600 * 1000 * 24 * 30;
|
||||
};
|
||||
this.queryParams.beginTime = beginTime;
|
||||
this.queryParams.endTime = endTime;
|
||||
this.queryParams.strTime = beginTime + '-' + endTime;
|
||||
this.getHistory();
|
||||
},
|
||||
// 格式化时间
|
||||
formatDate (strTime) {
|
||||
const times = strTime.split('-');
|
||||
const beginTime = new Date(Number(times[0]));
|
||||
const endTime = new Date(Number(times[1]));
|
||||
const beginMonth = (beginTime.getMonth() + 1).toString().padStart(2, '0');
|
||||
const beginDay = beginTime.getDate().toString().padStart(2, '0');
|
||||
const beginHours = beginTime.getHours().toString().padStart(2, '0');
|
||||
const beginMinutes = beginTime.getMinutes().toString().padStart(2, '0');
|
||||
const endMonth = (endTime.getMonth() + 1).toString().padStart(2, '0');
|
||||
const endDay = endTime.getDate().toString().padStart(2, '0');
|
||||
const endHours = endTime.getHours().toString().padStart(2, '0');
|
||||
const endMinutes = endTime.getMinutes().toString().padStart(2, '0');
|
||||
return `${beginMonth}-${beginDay} ${beginHours}:${beginMinutes} - ${endMonth}-${endDay} ${endHours}:${endMinutes}`;
|
||||
},
|
||||
// 日期选择
|
||||
handleDateTimeClick () {
|
||||
const { beginTime, endTime } = this.queryParams;
|
||||
if (this.dateTimeIndex === 1) {
|
||||
this.$refs.datetimePicker.innerValue = beginTime;
|
||||
} else {
|
||||
this.$refs.datetimePicker.innerValue = endTime;
|
||||
}
|
||||
this.isDateTime = true;
|
||||
},
|
||||
// 时间选择下一步/确定
|
||||
handleConfirmDateTime (e) {
|
||||
const { beginTime, endTime } = this.queryParams;
|
||||
if (this.dateTimeIndex === 1) {
|
||||
this.queryParams.beginTime = e.value;
|
||||
this.dateTimeIndex = this.dateTimeIndex + 1;
|
||||
this.queryParams.strTime = e.value + '-' + endTime;
|
||||
this.$refs.datetimePicker.innerValue = endTime;
|
||||
this.isDateTime = false;
|
||||
setTimeout(() => {
|
||||
this.isDateTime = true;
|
||||
}, 1);
|
||||
} else {
|
||||
this.queryParams.endTime = e.value;
|
||||
this.dateTimeIndex = 1;
|
||||
this.queryParams.strTime = beginTime + '-' + e.value;
|
||||
this.getHistory();
|
||||
this.isDateTime = false;
|
||||
this.queryParams.timeShortcut = 3;
|
||||
}
|
||||
},
|
||||
// 取消日期选择
|
||||
handleCancelDateTime () {
|
||||
this.isDateTime = false;
|
||||
},
|
||||
// 点击遮取消日期选择
|
||||
handleCloseDateTime () {
|
||||
this.isDateTime = false;
|
||||
},
|
||||
// 筛选
|
||||
handleFilterClick () {
|
||||
this.isFilter = true;
|
||||
},
|
||||
// 变量选择
|
||||
handleIdentifierCheck (e) {
|
||||
const value = e.detail.value;
|
||||
const length = value.length;
|
||||
if (length > 9) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '变量数不能超过9个!'
|
||||
});
|
||||
this.queryParams.identifiers = value.slice(0, 9);
|
||||
}
|
||||
},
|
||||
// 筛选确认
|
||||
handleConfirmFilter () {
|
||||
const { identifiers } = this.queryParams;
|
||||
if (identifiers.length === 0) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择变量!'
|
||||
});
|
||||
} else {
|
||||
this.getHistory();
|
||||
this.isFilter = false;
|
||||
}
|
||||
},
|
||||
// 获取子设备列表数据
|
||||
getSlaveDatas () {
|
||||
const { deviceId } = this.deviceInfo;
|
||||
const params = {
|
||||
gwDeviceId: deviceId,
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
};
|
||||
getSubGatewayList(params).then(res => {
|
||||
const { code, rows } = res;
|
||||
if (code === 200) {
|
||||
this.slaveList = rows.map(item => ({
|
||||
text: item.subDeviceName,
|
||||
value: item.slaveId
|
||||
}));
|
||||
this.queryParams.slaveId = this.slaveList[0].value;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取物模型
|
||||
getIdentifierList () {
|
||||
const { deviceId } = this.deviceInfo;
|
||||
const params = {
|
||||
deviceId: deviceId,
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
};
|
||||
listThingsModel(params).then((res) => {
|
||||
const { code, rows } = res;
|
||||
if (code === 200) {
|
||||
this.identifierList = rows.map(item => ({
|
||||
text: item.modelName,
|
||||
value: item.identifier,
|
||||
type: item.type,
|
||||
}));
|
||||
this.queryParams.identifiers = [this.identifierList[0].value];
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取统计数据
|
||||
getData () {
|
||||
this.getIdentifierList();
|
||||
this.getHistory(); // 获取统计数据
|
||||
},
|
||||
// 获取统计数据
|
||||
getHistory () {
|
||||
const { identifiers, beginTime, endTime, slaveId } = this.queryParams;
|
||||
const { deviceId, serialNumber } = this.deviceInfo;
|
||||
if (identifiers.length !== 0) {
|
||||
const idenList = identifiers.map((item) => {
|
||||
const iden = this.identifierList.find((chil) => chil.value === item);
|
||||
return {
|
||||
identifier: iden.value,
|
||||
type: iden.type
|
||||
};
|
||||
});
|
||||
const params = {
|
||||
identifierList: idenList,
|
||||
beginTime: moment(beginTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
deviceId: deviceId,
|
||||
// slaveId: slaveId,
|
||||
serialNumber: serialNumber,
|
||||
};
|
||||
getHistoryList(params).then(res => {
|
||||
this.historys = res.data;
|
||||
this.chartData = this.getChartDatas();
|
||||
this.getTableDatas();
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取折线图数据
|
||||
getChartDatas () {
|
||||
const categories = this.historys.map((item) => Object.keys(item)[0]);
|
||||
const series = this.queryParams.identifiers.map((item) => {
|
||||
return {
|
||||
name: this.identifierList.find((chil) => chil.value === item).text,
|
||||
data: this.historys.map((d) => {
|
||||
const ide = Object.values(d)[0].find((f) => Object.keys(f)[0] === item);
|
||||
return Object.values(ide)[0];
|
||||
}),
|
||||
};
|
||||
});
|
||||
return {
|
||||
categories,
|
||||
series
|
||||
}
|
||||
},
|
||||
// 获取表格数据
|
||||
getTableDatas () {
|
||||
this.tableHeaders = this.queryParams.identifiers.map((item) => ({
|
||||
name: this.identifierList.find((chil) => chil.value === item).text,
|
||||
value: item,
|
||||
}));
|
||||
this.tableComTemp = this.historys.map((item) => {
|
||||
const time = Object.keys(item)[0];
|
||||
let obj = {};
|
||||
Object.values(item)[0].forEach((chil) => {
|
||||
obj[Object.keys(chil)[0]] = Object.values(chil)[0];
|
||||
});
|
||||
return {
|
||||
time,
|
||||
...obj
|
||||
};
|
||||
});
|
||||
this.devTotal = this.historys.length;
|
||||
},
|
||||
// 获取设备列表数据
|
||||
getTableComsList (e) {
|
||||
this.devPageNum = e.current;
|
||||
},
|
||||
},
|
||||
options: {
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-history {
|
||||
width: 100%;
|
||||
|
||||
.tools-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 20rpx 14rpx;
|
||||
background: #fff;
|
||||
|
||||
.time-shortcut {
|
||||
width: 190rpx;
|
||||
|
||||
/deep/ .uni-select {
|
||||
height: 70rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.date-wrap {
|
||||
flex: 1;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
font-size: 28rpx;
|
||||
margin-left: 14rpx;
|
||||
height: 70rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.charts-box {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
padding: 20rpx 0;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.table-box {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
padding: 10px 0;
|
||||
margin-top: 10px;
|
||||
|
||||
.pagination-box {
|
||||
margin: 10px 10px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.other-wrap {
|
||||
.filter-pop {
|
||||
.content-wrap {
|
||||
padding: 10px;
|
||||
|
||||
.title-wrap {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.check-box-wrap {
|
||||
margin: 10rpx 0 30rpx 0;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
407
pagesA/home/device/statistic/index.vue
Normal file
407
pagesA/home/device/statistic/index.vue
Normal file
@ -0,0 +1,407 @@
|
||||
<template>
|
||||
<view class="statistic-wrap">
|
||||
<view class="statistic-title">
|
||||
<view class="slave-wrap" v-if="isSubDev">
|
||||
<uni-data-select :placeholder="$tt('deviceStatic.selectSub')" v-model="queryParams.slaveId"
|
||||
:localdata="slaveList" @change="handleSlavechange" :clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="time-wrap" @click="openCalendar">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input v-model="dateRangeText" :placeholder="$tt('deviceStatic.rangeTime')" prefixIcon="search"
|
||||
prefixIconStyle="color: #909399" readonly :disabled="statisticList.length === 0"
|
||||
customStyle="height:20px;">
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input v-model="dateRangeText" :placeholder="$tt('deviceStatic.rangeTime')" prefixIcon="search"
|
||||
prefixIconStyle="color: #909399" readonly :disabled="statisticList.length === 0"
|
||||
customStyle="height:20px;">
|
||||
<!-- #endif -->
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('deviceStatic.search')" type="primary" size="mini"
|
||||
@click="handleSearchTap()"></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
<u-calendar :title="$tt('deviceStatic.selectTime')" :show="isShowCalendar" monthNum="12"
|
||||
:minDate="getDate(-12)" mode="range" :defaultDate="defaultDateRange"
|
||||
@confirm="handleCalendarConfirm" @close="handleCalendarClose"></u-calendar>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="statisticList.length !== 0" class="charts-box" v-for="(chart, index) in monitorChart" :key="index">
|
||||
<qiun-data-charts type="area" :canvas2d="true" :opts="chart.opts" :chartData="chart.data" :ontouch="true"
|
||||
:onzoom="true" tooltipFormat="tooltipStatistic" />
|
||||
</view>
|
||||
|
||||
<u-empty mode="data" :show="statisticList.length === 0" marginTop="30"></u-empty>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getDeviceHistory
|
||||
} from '@/apis/modules/deviceLog.js';
|
||||
|
||||
export default {
|
||||
name: 'device-statistics',
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
require: true,
|
||||
},
|
||||
type: Number
|
||||
},
|
||||
watch: {
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
this.isSubDev = newVal.subDeviceList && newVal.subDeviceList.length > 0;
|
||||
if (newVal.deviceType === 1) {
|
||||
this.slaveList = this.getSlaveDatas(newVal.subDeviceList);
|
||||
}
|
||||
|
||||
// 监测数据
|
||||
if (this.isSubDev) {
|
||||
const slaveId = this.slaveList[0] && this.slaveList[0].value;
|
||||
this.queryParams.slaveId = slaveId;
|
||||
this.preSlaveId = slaveId;
|
||||
this.queryParams.serialNumber = newVal.subDeviceList[0].serialNumber;
|
||||
this.statisticList = newVal.cacheThingsModel["properties"] && newVal.cacheThingsModel["properties"]
|
||||
.filter(item => {
|
||||
return item.tempSlaveId == this.queryParams.slaveId;
|
||||
}) || [];
|
||||
} else {
|
||||
this.queryParams.serialNumber = newVal.serialNumber;
|
||||
this.statisticList = newVal.statisticList || [];
|
||||
}
|
||||
},
|
||||
type (val) {
|
||||
if (val === 3) {
|
||||
this.getData();
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dateRangeText: {
|
||||
get() {
|
||||
if (this.queryParams.beginTime && this.queryParams.beginTime != '') {
|
||||
return this.queryParams.beginTime + ' - ' + this.queryParams.endTime;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
set(val) {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const beginTime = uni.$u.timeFormat((new Date().getTime() - 3600 * 1000 * 24), 'yyyy-mm-dd');
|
||||
const endTime = uni.$u.timeFormat(new Date(), 'yyyy-mm-dd');
|
||||
return {
|
||||
// 图表数据集合
|
||||
monitorChart: [{
|
||||
name: ''
|
||||
}],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
serialNumber: '',
|
||||
slaveId: '',
|
||||
beginTime: beginTime,
|
||||
endTime: endTime
|
||||
},
|
||||
defaultDateRange: [beginTime, endTime],
|
||||
// 子设备
|
||||
isSubDev: false,
|
||||
slaveList: [],
|
||||
preSlaveId: '',
|
||||
// 时间范围组件显示
|
||||
isShowCalendar: false,
|
||||
// 监控物模型
|
||||
statisticList: [],
|
||||
// 设备信息
|
||||
deviceInfo: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 打开日期范围组件
|
||||
openCalendar () {
|
||||
if (this.monitorChart.length > 0) {
|
||||
this.isShowCalendar = true;
|
||||
}
|
||||
},
|
||||
// 关闭日期范围组件
|
||||
handleCalendarClose () {
|
||||
this.isShowCalendar = false;
|
||||
},
|
||||
// 确定选择日期范围
|
||||
handleCalendarConfirm (data) {
|
||||
if (null != data[0] && '' != data[0]) {
|
||||
this.queryParams.beginTime = data[0];
|
||||
this.queryParams.endTime = data[data.length - 1];
|
||||
}
|
||||
this.isShowCalendar = false;
|
||||
},
|
||||
// 查询
|
||||
handleSearchTap () {
|
||||
// chart 组件问题,需要自己控制是清除还是重新初始化图表
|
||||
if (this.preSlaveId === this.queryParams.slaveId) {
|
||||
this.clearChart();
|
||||
} else {
|
||||
this.initChart();
|
||||
}
|
||||
this.getHistory();
|
||||
event.stopPropagation();
|
||||
},
|
||||
// 清除
|
||||
handleClearTap () {
|
||||
this.queryParams.beginTime = null;
|
||||
this.queryParams.endTime = null;
|
||||
if (this.isSubDev) {
|
||||
this.handleSlavechange(this.slaveList[0].value)
|
||||
}
|
||||
},
|
||||
// 获取当前日期
|
||||
getDate (month) {
|
||||
let date = new Date();
|
||||
let y = date.getFullYear();
|
||||
let m = date.getMonth() + 1 + month;
|
||||
let d = date.getDate();
|
||||
m = m < 10 ? '0' + m : m;
|
||||
d = d < 10 ? '0' + d : d;
|
||||
return y + '-' + m + '-' + d;
|
||||
},
|
||||
// 获取统计数据
|
||||
getData () {
|
||||
this.initChart(); // 初始化图表
|
||||
this.getHistory(); // 获取统计数据
|
||||
uni.stopPullDownRefresh();
|
||||
},
|
||||
// 获取统计数据
|
||||
getHistory () {
|
||||
const {
|
||||
beginTime,
|
||||
endTime,
|
||||
...pres
|
||||
} = this.queryParams;
|
||||
const params = {
|
||||
...pres,
|
||||
beginTime: beginTime + ' 23:59',
|
||||
endTime: endTime + ' 23:59'
|
||||
};
|
||||
getDeviceHistory(params).then(res => {
|
||||
for (let i = 0; i < this.statisticList.length; i++) {
|
||||
let obj = {
|
||||
categories: [],
|
||||
series: [{
|
||||
name: this.statisticList[i].name + '(单位 ' + (this
|
||||
.statisticList[i]
|
||||
.datatype.unit != undefined ? this.statisticList[i]
|
||||
.datatype.unit : '无') + ')',
|
||||
data: []
|
||||
}],
|
||||
};
|
||||
for (let key in res.data) {
|
||||
if (key === this.statisticList[i].id) {
|
||||
for (let j = 0; j < res.data[key].length; j++) {
|
||||
obj.categories.push(res.data[key][j].time);
|
||||
obj.series[0].data.push(Number(res.data[key][j].value));
|
||||
}
|
||||
}
|
||||
}
|
||||
let data = JSON.parse(JSON.stringify(obj));
|
||||
this.monitorChart[i].data = data;
|
||||
};
|
||||
|
||||
// 数据请求成功后更新preSlaveId
|
||||
this.preSlaveId = this.queryParams.slaveId;
|
||||
})
|
||||
},
|
||||
// 初始化图表
|
||||
initChart () {
|
||||
let color = ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4',
|
||||
'#ea7ccc'
|
||||
];
|
||||
this.monitorChart = [];
|
||||
for (let i = 0; i < this.statisticList.length; i++) {
|
||||
// data里面设置值是因为如果不加这个设置了最大最小值,第一次加载数据Y轴最大最小无法生效
|
||||
let data = {
|
||||
categories: [],
|
||||
series: [{
|
||||
name: '',
|
||||
data: []
|
||||
}],
|
||||
};
|
||||
let opts = {
|
||||
// 自定义名称和单位,用于图表提示框显示
|
||||
name: this.statisticList[i].name,
|
||||
unit: this.statisticList[i].datatype.unit,
|
||||
timing: 'easeOut',
|
||||
color: [color[i % color.length]],
|
||||
padding: [15, 15, 0, 15],
|
||||
fontSize: 12,
|
||||
fontColor: '#666666',
|
||||
dataLabel: false, //是否显示值
|
||||
dataPointShape: true,
|
||||
dataPointShapeType: 'solid',
|
||||
touchMoveLimit: 60,
|
||||
enableScroll: true,
|
||||
enableMarkLine: false,
|
||||
update: true,
|
||||
legend: {
|
||||
show: true,
|
||||
position: 'bottom',
|
||||
float: 'center',
|
||||
padding: 5,
|
||||
margin: 5,
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
borderColor: 'rgba(0,0,0,0)',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
fontColor: '#333333',
|
||||
lineHeight: 16,
|
||||
hiddenColor: '#CECECE',
|
||||
itemGap: 10
|
||||
},
|
||||
xAxis: {
|
||||
disableGrid: true,
|
||||
disabled: true, // 隐藏分类名称
|
||||
axisLine: true,
|
||||
axisLineColor: '#CCCCCC',
|
||||
calibration: false,
|
||||
fontColor: '#666666',
|
||||
fontSize: 13,
|
||||
rotateLabel: false,
|
||||
rotateAngle: 45,
|
||||
itemCount: 100,
|
||||
boundaryGap: 'justify',
|
||||
splitNumber: 5,
|
||||
gridColor: '#CCCCCC',
|
||||
gridType: 'solid',
|
||||
dashLength: 1,
|
||||
gridEval: 1,
|
||||
scrollShow: false,
|
||||
scrollAlign: 'right',
|
||||
scrollColor: '#A6A6A6',
|
||||
scrollBackgroundColor: '#ccc',
|
||||
format: ''
|
||||
},
|
||||
yAxis: {
|
||||
gridType: 'dash',
|
||||
dashLength: 2,
|
||||
disabled: false,
|
||||
tofix: 3, // 刻度保留小数位数
|
||||
disableGrid: false,
|
||||
splitNumber: 5,
|
||||
gridColor: '#CCCCCC',
|
||||
padding: 10,
|
||||
showTitle: false,
|
||||
max: this.statisticList[i].datatype.max,
|
||||
min: this.statisticList[i].datatype.min,
|
||||
data: [],
|
||||
},
|
||||
extra: {
|
||||
area: {
|
||||
type: 'curve',
|
||||
opacity: 0.5,
|
||||
addLine: true,
|
||||
width: 1,
|
||||
gradient: false
|
||||
},
|
||||
tooltip: {
|
||||
showBox: true,
|
||||
showArrow: true,
|
||||
showCategory: true,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
borderColor: '#000000',
|
||||
borderOpacity: 0.7,
|
||||
bgColor: '#000000',
|
||||
bgOpacity: 0.7,
|
||||
gridType: 'solid',
|
||||
dashLength: 4,
|
||||
gridColor: '#CCCCCC',
|
||||
fontColor: '#FFFFFF',
|
||||
splitLine: true,
|
||||
horizentalLine: false,
|
||||
xAxisLabel: false,
|
||||
yAxisLabel: false,
|
||||
labelBgColor: '#FFFFFF',
|
||||
labelBgOpacity: 0.7,
|
||||
labelFontColor: '#666666'
|
||||
},
|
||||
markLine: {
|
||||
type: 'solid',
|
||||
dashLength: 2,
|
||||
data: []
|
||||
}
|
||||
}
|
||||
};
|
||||
this.monitorChart.push({
|
||||
opts: opts,
|
||||
data: data,
|
||||
show: false,
|
||||
id: this.statisticList[i].id
|
||||
});
|
||||
}
|
||||
},
|
||||
// 清空图标
|
||||
clearChart () {
|
||||
for (let i = 0; i < this.monitorChart.length; i++) {
|
||||
this.monitorChart[i].data.series[0].data = [];
|
||||
this.monitorChart[i].data.categories = [];
|
||||
this.monitorChart[i].show = true;
|
||||
}
|
||||
},
|
||||
// 获取子设备列表数据
|
||||
getSlaveDatas (data) {
|
||||
if (data != undefined) {
|
||||
return data.map(item => ({
|
||||
value: item.slaveId,
|
||||
text: `${item.deviceName}(从机地址:${item.slaveId})`
|
||||
}));
|
||||
}
|
||||
},
|
||||
// 从机选择
|
||||
handleSlavechange (value) {
|
||||
this.statisticList = this.deviceInfo.cacheThingsModel["properties"].filter(item => {
|
||||
return item.tempSlaveId === value;
|
||||
});
|
||||
this.queryParams.slaveId = value;
|
||||
this.queryParams.serialNumber = this.deviceInfo.subDeviceList.find(item => item.slaveId === value)
|
||||
.serialNumber;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.statistic-wrap {
|
||||
|
||||
.statistic-title {
|
||||
padding: 0px 10px 10px 10px;
|
||||
background-color: #ffffff;
|
||||
|
||||
.slave-wrap {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.time-wrap {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.search-wrap {
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 请根据实际需求修改父元素尺寸,组件自动识别宽高 */
|
||||
.charts-box {
|
||||
width: 100%;
|
||||
height: 230px;
|
||||
background-color: #ffffff;
|
||||
padding: 10px 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
1459
pagesA/home/device/status/base.vue
Normal file
1459
pagesA/home/device/status/base.vue
Normal file
File diff suppressed because it is too large
Load Diff
88
pagesA/home/device/status/index.vue
Normal file
88
pagesA/home/device/status/index.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<view class="device-status">
|
||||
<view class="title-wrap">
|
||||
<view style="width: 175px;">
|
||||
<u-subsection :list="modeList" mode="subsection" :current="current"
|
||||
@change="sectionChange"></u-subsection>
|
||||
</view>
|
||||
<u--text iconStyle="color: #3c9cff; margin-right: 4px; font-size: 26px;" type="primary"
|
||||
prefixIcon="file-text" align="right" :text="$tt('status.devDetail')"
|
||||
@click="handleGoToDeviceDetail"></u--text>
|
||||
</view>
|
||||
<view class="running-status" v-show="current==0">
|
||||
<base-status :device="device" ref="baseStatus"></base-status>
|
||||
</view>
|
||||
<view class="deviceVariable" v-show="current==1">
|
||||
<device-variable ref="deviceVariable" :device="device"></device-variable>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import baseStatus from './base.vue';
|
||||
import deviceVariable from './variable.vue';
|
||||
|
||||
export default {
|
||||
name: 'running-status',
|
||||
components: {
|
||||
baseStatus,
|
||||
deviceVariable
|
||||
},
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
this.isSubDev = newVal.subDeviceList && newVal.subDeviceList.length > 0;
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
modeList: [this.$tt('status.stateModel'), this.$tt('status.dataModel')],
|
||||
current: 0,
|
||||
isSubDev: false,
|
||||
deviceInfo: {} // 设备信息
|
||||
};
|
||||
},
|
||||
created () {
|
||||
// 获取设备状态(兼容H5和APP)
|
||||
if (this.device.subDeviceList) {
|
||||
this.deviceInfo = this.device;
|
||||
this.isSubDev = this.device.subDeviceList.length > 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sectionChange (index) {
|
||||
this.current = index;
|
||||
},
|
||||
baseStatusRefresh () {
|
||||
this.$refs.baseStatus.deviceStatusRefresh();
|
||||
},
|
||||
// 调整到设备详情
|
||||
handleGoToDeviceDetail () {
|
||||
const { deviceId } = this.deviceInfo;
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/home/device/detail/index?deviceId=' + deviceId
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-status {
|
||||
.title-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
254
pagesA/home/device/status/modbus.vue
Normal file
254
pagesA/home/device/status/modbus.vue
Normal file
@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<view>
|
||||
<u-swipe-action>
|
||||
<u-swipe-action-item :options="swipeOptions" v-for="(item, i) in deviceInfo.subDeviceList" :key="i"
|
||||
@click="handleEditClick(item)">
|
||||
<view class="modbus-wrap u-border-top u-border-bottom"
|
||||
@click="gotoDeviceDetail(deviceInfo.deviceId, item.slaveId, item.deviceId)">
|
||||
<view class="modbus-content">
|
||||
<u-cell>
|
||||
<view slot="title" class="u-slot-title">
|
||||
<text class="cell-title">{{item.deviceName}}</text>
|
||||
<view class="cell-ver">
|
||||
<u-tag borderColor="transparent" :text="`ver ${item.firmwareVersion}`" plain
|
||||
size="mini" type="warning">
|
||||
</u-tag>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cell-val" slot="value">
|
||||
<u--text :type="item.statusInfo.type" :text="item.statusInfo.name"></u--text>
|
||||
</view>
|
||||
<view class="cell-label" slot="label">{{item.serialNumber}}</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
</view>
|
||||
</u-swipe-action-item>
|
||||
</u-swipe-action>
|
||||
|
||||
<!-- 设备通道 -->
|
||||
<view class="card"v-if="isShowChannel==true">
|
||||
<view style="padding:10px;" >
|
||||
<u--text prefixIcon="grid-fill" iconStyle="font-size: 16px; color: #606266; margin-right: 5px;"
|
||||
text="视频监控" bold color="#606266"></u--text>
|
||||
</view>
|
||||
<view class="channel_wrap">
|
||||
<view class="item_body" v-for="(item, i) in channelList" :key="i"
|
||||
@click="gotoDevicePlayer(item.deviceSipId, item.channelId, item.status)">
|
||||
<view class="img">
|
||||
<u-icon name="play-circle" color="#2979ff" size="28"></u-icon>
|
||||
</view>
|
||||
<view>
|
||||
<u--text lines="2" lineHeight="24" size="16" :text="item.channelName"></u--text>
|
||||
<view style="display:flex;margin-top:6px;">
|
||||
<view style="margin-right:20px;">
|
||||
<u--text prefixIcon="info-circle"
|
||||
iconStyle="color:#606266;font-size:14px;margin-right:3px;" size="12" color="#606266"
|
||||
mode="name" :text="item.model"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="status">
|
||||
<u-tag v-if="item.statusInfo" :type="item.statusInfo.type" :plain="true" size="mini"
|
||||
:text="item.statusInfo.name"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view style="padding:10px;">
|
||||
<u-empty mode="list" :show="isShowChannel==false" marginTop="30"></u-empty>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getDeviceStatusInfo
|
||||
} from '@/helpers/common.js'
|
||||
import {
|
||||
listChannel,
|
||||
} from '@/apis/modules/sip';
|
||||
import {
|
||||
getSipStatusInfo,
|
||||
getLocationWayInfo
|
||||
} from '@/helpers/common.js'
|
||||
import {
|
||||
relateChannelList
|
||||
} from '@/apis/modules/device.js';
|
||||
export default {
|
||||
name: 'modbus-status',
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
device: function(newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
this.deviceInfo.subDeviceList = this.formatSubDeviceList(newVal.subDeviceList);
|
||||
if (this.deviceInfo.sipRelationList && this.deviceInfo.sipRelationList.length > 0) {
|
||||
this.getRelateChannelList();
|
||||
this.isShowChannel = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// swipe选项配置
|
||||
swipeOptions: [{
|
||||
text: this.$tt('common.edit'),
|
||||
style: {
|
||||
backgroundColor: '#3c9cff'
|
||||
}
|
||||
}],
|
||||
//通道列表
|
||||
channelList: [],
|
||||
// 设备通道
|
||||
channelLoadStatus: 'nomore',
|
||||
queryParams: {
|
||||
deviceSipId: '', //设备sipid
|
||||
},
|
||||
isShowChannel: false,
|
||||
// 设备数据
|
||||
deviceInfo: {
|
||||
subDeviceList: [],
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 获取设备状态(兼容H5和APP)
|
||||
if (this.device.subDeviceList) {
|
||||
this.deviceInfo = this.device;
|
||||
this.deviceInfo.subDeviceList = this.formatSubDeviceList(this.device.subDeviceList);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取子设备状态信息
|
||||
formatSubDeviceList(data) {
|
||||
if (data && data.length !== 0) {
|
||||
return data.map(item => {
|
||||
item.statusInfo = getDeviceStatusInfo(item.status);
|
||||
return item;
|
||||
})
|
||||
}
|
||||
},
|
||||
// 跳转详情
|
||||
gotoDeviceDetail(deviceId, slaveId, subDeviceId) {
|
||||
uni.navigateTo({
|
||||
url: `/pagesB/home/device/status/modbus/index?deviceId=${deviceId}&slaveId=${slaveId}&subDeviceId=${subDeviceId}`
|
||||
});
|
||||
},
|
||||
// 设备通道
|
||||
getRelateChannelList() {
|
||||
return new Promise((resolve, reject) => {
|
||||
relateChannelList(this.deviceInfo.deviceId).then(response => {
|
||||
this.channelList = response.data;
|
||||
response.data.map(item => {
|
||||
item.statusInfo = getSipStatusInfo(item.status);
|
||||
return item;
|
||||
})
|
||||
resolve(response.data);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
gotoDevicePlayer(deviceSipId, channelSipId, status) {
|
||||
let statusInfo = getSipStatusInfo(status)
|
||||
if (status !== 3) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: `无法查看${getSipStatusInfo(status).name}数据`
|
||||
});
|
||||
return;
|
||||
}
|
||||
// #ifdef H5
|
||||
uni.$u.route('/pages_player/list/devicePlayer', {
|
||||
deviceId: deviceSipId,
|
||||
channelSipId: channelSipId,
|
||||
});
|
||||
//#endif
|
||||
// #ifdef APP-PLUS
|
||||
uni.$u.route('/pages_player/list/devicePlayerApp', {
|
||||
deviceId: deviceSipId,
|
||||
channelSipId: channelSipId,
|
||||
});
|
||||
//#endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('timing.nosupported')
|
||||
});
|
||||
//#endif
|
||||
},
|
||||
// 编辑子设备
|
||||
handleEditClick(item) {
|
||||
uni.navigateTo({
|
||||
url: `/pagesB/home/device/status/modbus/edit?item=${encodeURIComponent(JSON.stringify(item))}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.modbus-wrap {
|
||||
.modbus-content {
|
||||
|
||||
.cell-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.cell-ver {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.cell-label {
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #909193;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.cell-val {
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.channel_wrap {
|
||||
padding: 6px 0;
|
||||
|
||||
.item_body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
background: #fff;
|
||||
|
||||
.img {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 5px;
|
||||
width: 90px;
|
||||
height: 60px;
|
||||
background: #e5e5e5;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.status {
|
||||
position: absolute;
|
||||
right: 13px;
|
||||
top: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
420
pagesA/home/device/status/variable.vue
Normal file
420
pagesA/home/device/status/variable.vue
Normal file
@ -0,0 +1,420 @@
|
||||
<template>
|
||||
<view>
|
||||
<u--input v-model="queryParams.modelName" placeholder="请输入变量名称" prefixIcon="search"
|
||||
prefixIconStyle="color: #909399" customStyle="height:50rpx;margin:0 20rpx;background-color: #ffffff;">
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('scene.search')" type="primary" size="mini" @click="handleSearch"></u-button>
|
||||
</template>
|
||||
</u--input>
|
||||
<view class="event-wrap">
|
||||
<view class="alert-item" v-for="(item, index) in datas" :key="index">
|
||||
<view class="left">
|
||||
<u--text color="#666" :text="item.modelName"></u--text>
|
||||
<view style="width:40px;margin-top: 10px;">
|
||||
<u-tag text="事件" type="warning" size="mini" :plain="true" v-if="item.type==3"></u-tag>
|
||||
<u-tag text="属性" type="primary" size="mini" :plain="true" v-if="item.type==1"></u-tag>
|
||||
<u-tag text="功能" type="success" size="mini" :plain="true" v-if="item.type==2"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
<view :class="deviceInfo.protocolCode==='MODBUS-RTU' ? 'middle' : 'right'">
|
||||
<u-row justify="space-between">
|
||||
<u-col :span="3">
|
||||
<u--text color="#666" :text="item.value=== '' ||item.value === null ? '-' : item.value">
|
||||
</u--text>
|
||||
</u-col>
|
||||
<u-col :span="2">
|
||||
<u--text :text="item.unit"></u--text>
|
||||
</u-col>
|
||||
<u-col :span="2">
|
||||
<!-- 下发按钮 -->
|
||||
<u-icon name="edit-pen" color="#2979ff" size="20" v-if="item.isReadonly === 0&&item.type!=3"
|
||||
@click="editFunc(item)"></u-icon>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
<view class="right" v-if="deviceInfo.protocolCode==='MODBUS-RTU'">
|
||||
<u-button type="primary" size="mini" :plain="true" custom-style="margin:10px;"
|
||||
@click="activeCollection(item)" v-if="deviceInfo.protocolCode==='MODBUS-RTU'">主动采集</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
</view>
|
||||
<!-- 下发数据弹窗 -->
|
||||
<u-popup :show="isShowModel" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isShowModel = false">
|
||||
<view class="integer-popup-wrap" style="height:150px;padding: 10px;">
|
||||
<view class="nav">
|
||||
<text @click="isShowModel = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="sendService">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view style="background-color: #fff;height:50px;padding: 10px;border-radius: 5px;">
|
||||
<u--form labelPosition="left" label-width="180px">
|
||||
<u-form-item v-for="(item, index) in opationList" :label="`${item.label}:`" :key="index">
|
||||
<!--枚举,布尔型,弹出选择框 -->
|
||||
<u-popup :show="showSelectModel" :round="5" mode="bottom" :closeOnClickOverlay="true"
|
||||
@close="showSelectModel = false">
|
||||
<view class="operator-popup-wrap">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item1,index1) in item.options" :key="index">
|
||||
<u-cell :title="item1.label" :name="item1.value"
|
||||
@click="handleConfirmOperator(item1)"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<!-- 字符串 -->
|
||||
<u--input v-model="funVal[item.key]" border="none" inputAlign="right" placeholder="请输入"
|
||||
:customStyle="{ marginRight: '20rpx',width:'150px' }"
|
||||
v-if="item.dataTypeName == 'string'||(item.dataTypeName == 'array'&&item.arrayType=='string')" />
|
||||
<!-- 整数,小数 -->
|
||||
<u--input v-model="funVal[item.key]" border="none" inputAlign="right" placeholder="请输入"
|
||||
@input="justNumber(item)" type="number"
|
||||
:customStyle="{ marginRight: '10rpx',width:'10px',color:'#000' }"
|
||||
v-if="item.dataTypeName == 'integer' || item.dataTypeName == 'decimal'||item.dataTypeName == 'array'||(item.dataTypeName == 'array'&&item.arrayType=='integer')||(item.dataTypeName == 'array'&&item.arrayType=='decimal')"></u--input>
|
||||
<span
|
||||
v-if="(item.dataTypeName == 'integer' || item.dataTypeName == 'decimal'||(item.dataTypeName == 'array'&&item.arrayType=='integer')||(item.dataTypeName == 'array'&&item.arrayType=='decimal')) && item.unit && item.unit != 'un' && item.unit != '/'">{{
|
||||
item.unit
|
||||
}}</span>
|
||||
<span class="range"
|
||||
v-if="item.dataTypeName == 'integer'|| item.dataTypeName == 'decimal'||(item.dataTypeName == 'array'&&item.arrayType=='integer')||(item.dataTypeName == 'array'&&item.arrayType=='decimal')">
|
||||
({{ item.min }} ~ {{ item.max }})
|
||||
</span>
|
||||
<!-- 枚举,布尔型 -->
|
||||
<u--input v-model="operators.label" border="none" inputAlign="right" placeholder="请选择"
|
||||
disabledColor="#fff" disabled :customStyle="{ marginRight: '20rpx' }"
|
||||
v-if="item.dataTypeName == 'enum' || item.dataTypeName == 'bool'" />
|
||||
<u-icon slot="right" name="arrow-right" @click="openList"
|
||||
v-if="item.dataTypeName == 'enum' || item.dataTypeName == 'bool'"></u-icon>
|
||||
</u-form-item>
|
||||
</u--form>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-modal :show="centerDialogVisible" content="下发指令主动采集变量,是否继续?" @confirm="confirmCollection()"
|
||||
@cancel="() => centerDialogVisible = false" showCancelButton></u-modal>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize" loading-text="努力加载中..."
|
||||
loadmoreText="点击我加载更多" nomoreText="实在没有了" marginTop="20" @loadmore="loadMoreLogs" />
|
||||
<u-loading-page :loading="loading" bg-color="#eef3f7" loadingText="XCWL.cn"></u-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listThingsModel,
|
||||
getOrderControl,
|
||||
propGet,
|
||||
serviceInvokeReply
|
||||
} from '@/apis/modules/device.js';
|
||||
export default {
|
||||
name: "device-variable",
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
if (newVal.deviceId && newVal.deviceId !== oldVal.deviceId) {
|
||||
this.queryParams.deviceId = newVal.deviceId;
|
||||
this.getVariableList();
|
||||
}
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
queryParams: {
|
||||
deviceId: '',
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
datas: [],
|
||||
centerDialogVisible: false, //采集弹窗
|
||||
form: {},
|
||||
canSend: false, //是否可以下发,主要判断数值在不在范围
|
||||
funVal: {},
|
||||
chooseFun: {},
|
||||
//下发的设备
|
||||
serialNumber: '',
|
||||
opationList: [],
|
||||
total: 0, // 总条数
|
||||
loadmoreStatus: 'loadmore', // 加载更多
|
||||
loading: false,
|
||||
isShowModel: false, //下发弹窗
|
||||
operators: {},
|
||||
showSelectModel: false,
|
||||
deviceInfo: {},
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
const {
|
||||
deviceId,
|
||||
serialNumber
|
||||
} = this.device;
|
||||
if (deviceId) {
|
||||
this.queryParams.deviceId = deviceId;
|
||||
this.serialNumber = serialNumber;
|
||||
this.getVariableList();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//查询物模型列表
|
||||
getVariableList () {
|
||||
listThingsModel(this.queryParams).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (this.queryParams.pageNum == 1) {
|
||||
this.datas = res.rows;
|
||||
} else {
|
||||
this.datas = this.datas.concat(res.rows);
|
||||
}
|
||||
this.total = res.total;
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loadmore';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleConfirmOperator (item) {
|
||||
this.operators = item;
|
||||
for (let key in this.funVal) {
|
||||
this.funVal[key] = this.operators.value;
|
||||
}
|
||||
this.showSelectModel = false;
|
||||
},
|
||||
openList () {
|
||||
this.operators = {};
|
||||
this.showSelectModel = true;
|
||||
},
|
||||
//搜索
|
||||
handleSearch () {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getVariableList();
|
||||
},
|
||||
//指令下发
|
||||
async editFunc (item) {
|
||||
const params = {
|
||||
deviceId: this.device.deviceId,
|
||||
modelId: item.modelId
|
||||
}
|
||||
//判断是否有权限
|
||||
const response = await getOrderControl(params);
|
||||
if (response.code != 200) {
|
||||
uni.$u.toast(response.msg);
|
||||
return;
|
||||
}
|
||||
//这里兼容子设备的下发,在网关设备下发的时候选择实际子设备的编号
|
||||
this.serialNumber = item.serialNumber;
|
||||
let title = '';
|
||||
if (this.device.status !== 3 && this.device.isShadow !== 1) {
|
||||
if (this.device.status === 1) {
|
||||
title = '设备未激活';
|
||||
} else if (this.device.status === 2) {
|
||||
title = '设备处于禁用状态';
|
||||
} else {
|
||||
title = '设备处于离线状态';
|
||||
}
|
||||
uni.$u.toast(title);
|
||||
return;
|
||||
}
|
||||
this.isShowModel = true;
|
||||
this.canSend = true;
|
||||
this.funVal = {};
|
||||
this.operators.label = '';
|
||||
this.chooseFun = item;
|
||||
this.getOpationList(item);
|
||||
},
|
||||
// 发送指令
|
||||
async sendService () {
|
||||
if (this.canSend) {
|
||||
try {
|
||||
let params = this.funVal;
|
||||
const pas = {
|
||||
serialNumber: this.serialNumber,
|
||||
identifier: this.chooseFun.identifier,
|
||||
remoteCommand: params
|
||||
}
|
||||
this.isShowModel = true;
|
||||
const res = await serviceInvokeReply(pas);
|
||||
if (res.code == 200) {
|
||||
this.$message.success('下发成功')
|
||||
} else {
|
||||
this.$message.error(res.data)
|
||||
}
|
||||
} finally {}
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '数值不在范围内,不能下发,请重新输入!'
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
//主动采集数据
|
||||
activeCollection (item) {
|
||||
this.centerDialogVisible = true;
|
||||
this.form.serialNumber = item.serialNumber;
|
||||
this.form.type = 1;
|
||||
this.form.identifier = item.identifier;
|
||||
},
|
||||
//确认采集
|
||||
confirmCollection () {
|
||||
propGet(this.form).then(response => {
|
||||
if (response.code == 200) {
|
||||
this.centerDialogVisible = false;
|
||||
} else {
|
||||
this.centerDialogVisible = false;
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: response.msg
|
||||
});
|
||||
return;
|
||||
}
|
||||
})
|
||||
},
|
||||
// 封装操作列表
|
||||
getOpationList (item) {
|
||||
this.opationList = []
|
||||
let options = []
|
||||
this.funVal = {}
|
||||
const datatype = item.datatype;
|
||||
if (datatype.type == 'enum') {
|
||||
options = datatype.enumList?.map(option => {
|
||||
return {
|
||||
label: option.text,
|
||||
value: option.value + ''
|
||||
}
|
||||
}) || []
|
||||
}
|
||||
if (datatype.type == 'bool') {
|
||||
options = [{
|
||||
label: datatype.falseText || '',
|
||||
value: '0'
|
||||
}, {
|
||||
label: datatype.trueText || '',
|
||||
value: '1'
|
||||
}]
|
||||
}
|
||||
this.opationList.push({
|
||||
dataTypeName: datatype.type,
|
||||
arrayType: datatype.arrayType,
|
||||
label: item.modelName,
|
||||
key: item.identifier,
|
||||
max: parseInt(datatype?.max || 100),
|
||||
min: parseInt(datatype?.min || -100),
|
||||
options: options,
|
||||
value: item.value
|
||||
})
|
||||
this.opationList.forEach(item => {
|
||||
let value = item.value
|
||||
if (item.datatype == 'integer' || item.datatype == 'decimal' || (item.dataTypeName ==
|
||||
'array' && item.arrayType == 'integer') || (item.dataTypeName == 'array' && item
|
||||
.arrayType == 'decimal')) {
|
||||
value = parseInt(value)
|
||||
}
|
||||
this.funVal[item.key] = value;
|
||||
})
|
||||
},
|
||||
//判断输入是否超过范围
|
||||
justNumber (val) {
|
||||
this.canSend = true
|
||||
this.opationList.some(item => {
|
||||
if (item.max < this.funVal[item.key] || item.min > this.funVal[item.key]) {
|
||||
this.canSend = false
|
||||
return true
|
||||
}
|
||||
})
|
||||
this.$forceUpdate()
|
||||
},
|
||||
// 上拉加载
|
||||
loadMoreLogs () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getVariableList();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.datas = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.type === 1 && this.getList();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.event-wrap {
|
||||
.log-item {
|
||||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.alert-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
background-color: #ffffff;
|
||||
|
||||
.left {
|
||||
margin-right: 10px;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.middle {
|
||||
flex: 1;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-right: 10rpx;
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.u-popup {
|
||||
text-align: center;
|
||||
/* 文字居中 */
|
||||
}
|
||||
</style>
|
588
pagesA/home/device/timing/detail.vue
Normal file
588
pagesA/home/device/timing/detail.vue
Normal file
@ -0,0 +1,588 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.timedDetails')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="device-timing-detail-wrap">
|
||||
<view class="name-wrap">
|
||||
<u--input clearable border="false" :placeholder="$tt('sceneDetail.inputName')"
|
||||
:customStyle="{ backgroundColor: '#fff', padding: '12px 30rpx', borderRadius: '10rpx' }" fontSize="16"
|
||||
v-model="timingData.jobName"></u--input>
|
||||
</view>
|
||||
<view class="container-wrap">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell size="large" :border="false" :title="$tt('sceneTiming.status')">
|
||||
<u-switch v-model="timingData.status" slot="value" activeValue="0" inactiveValue="1"
|
||||
activeColor="#3C9CFF" inactiveColor="#dbdbdb" size="18"></u-switch>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
<view class="condition-wrap">
|
||||
<view class="form-wrap">
|
||||
<u--form errorType="toast" labelPosition="left" labelWidth="80" :labelStyle="{ fontSize: '32rpx' }">
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item :label="$tt('sceneTiming.time')" @click="isTime = true">
|
||||
<u--input v-model="timingData.time" border="none" inputAlign="right" :placeholder="$tt('sceneTiming.selectTime')"
|
||||
disabledColor="#fff" disabled :customStyle="{ marginRight: '20rpx' }" />
|
||||
<u-icon slot="right" name="arrow-right"></u-icon>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item :label="$tt('sceneTiming.again')" @click="isRepeat = true">
|
||||
<u--input v-model="timingData.repeat" border="none" inputAlign="right"
|
||||
:placeholder="$tt('sceneTiming.selectWeek')" disabledColor="#fff" disabled
|
||||
:customStyle="{ marginRight: '20rpx' }" />
|
||||
<u-icon slot="right" name="arrow-right"></u-icon>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item label="CRON">
|
||||
<template slot="right">
|
||||
<u-radio-group v-model="timingData.isAdvance">
|
||||
<u-radio :customStyle="{marginRight: '30rpx'}" :label="$tt('sceneTiming.default')" :name="0"></u-radio>
|
||||
<u-radio :customStyle="{marginRight: '10rpx'}" :label="$tt('sceneTiming.auto')" :name="1"
|
||||
disabled></u-radio>
|
||||
</u-radio-group>
|
||||
</template>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input v-model="timingData.cronExpression" :placeholder="$tt('sceneTiming.inputCRON')" disabled>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input v-model="timingData.cronExpression" :placeholder="$tt('sceneTiming.inputCRON')" disabled>
|
||||
<!-- #endif -->
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('sceneTiming.expression')" icon="calendar" type="primary" size="mini"
|
||||
disabled></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u--form>
|
||||
</view>
|
||||
</view>
|
||||
<view class="execute-wrap">
|
||||
<view class="title-wrap">
|
||||
<view class="left-wrap">
|
||||
<text class="title">{{$tt('sceneDetail.action')}}</text>
|
||||
</view>
|
||||
<view class="right-wrap">
|
||||
<u-icon name="plus-circle-fill" size="24" color='#3c9cff' bold @click="handleAddModel"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="timingData.actions.length === 0" class="empty-cell" @click="handleAddModel">
|
||||
<view class="text">{{$tt('sceneDetail.addTask')}}</view>
|
||||
</view>
|
||||
<view v-else class="swipe-action-wrap">
|
||||
<u-swipe-action autoClose>
|
||||
<u-cell-group :border="false">
|
||||
<view class="action-item-wrap" v-for="(item,index) in timingData.actions"
|
||||
:key="getKey(index, item)">
|
||||
<!-- 设置key为不同id是为了解决uview的SwipeAction 滑动单元格 关闭事件 ,删除当前项,下一项自动弹出删除选择bug -->
|
||||
<u-swipe-action-item :options="swipeOptions" @click="handleDeleteSceneItem(index)">
|
||||
<u-cell :border="false" isLink
|
||||
@click="handleEditTimingItem({item:item, index:index})">
|
||||
<view slot="title" class="slot-title">
|
||||
<u--image :src="getImageUrl(item)" radius="4" width="33"
|
||||
height="33"></u--image>
|
||||
<view class="cell-text">
|
||||
<view class="title">{{item.deviceName}}</view>
|
||||
<view class="des">
|
||||
<template v-if="item.parentName && item.parentName !== item.name">
|
||||
{{item.parentName}} >>
|
||||
</template>
|
||||
<template v-if="item.arrayIndexName">
|
||||
{{item.arrayIndexName}} >>
|
||||
</template>
|
||||
{{item.name}}
|
||||
{{item.valueName ? item.valueName : item.value !== '' ? item.value : ''}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-cell>
|
||||
</u-swipe-action-item>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</u-swipe-action>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-save-wrap">
|
||||
<u-button type="primary" :customStyle="{ height: '96rpx' }" size="normal" :text="$tt('common.save')"
|
||||
@click="handleSave"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="other">
|
||||
<!-- 选择时间 -->
|
||||
<u-datetime-picker :show="isTime" v-model="timingData.time" mode="time" @cancel="isTime=false"
|
||||
@close="isTime=false" @confirm="handleDatetimeConfirm"></u-datetime-picker>
|
||||
|
||||
<!-- 星期 -->
|
||||
<u-popup :show="isRepeat" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="true"
|
||||
@close="isRepeat = false">
|
||||
<view class="repeat-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isRepeat = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleRepeatConfirm">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="radio-group-wrap">
|
||||
<u-checkbox-group v-model="timingData.repeatValue" :borderBottom="false" placement="column"
|
||||
iconPlacement="right">
|
||||
<view class="radio-wrap">
|
||||
<u-checkbox :label="$tt('sceneTiming.monday')" :name="1" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-checkbox :label="$tt('sceneTiming.tuesday')" :name="2" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-checkbox :label="$tt('sceneTiming.wednesday')" :name="3" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-checkbox :label="$tt('sceneTiming.thursday')" :name="4" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-checkbox :label="$tt('sceneTiming.friday')" :name="5" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-checkbox :label="$tt('sceneTiming.saturday')" :name="6" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-checkbox :label="$tt('sceneTiming.sunday')" :name="7" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
</u-checkbox-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<u-popup :show="isModel" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="true"
|
||||
@close="isModel = false">
|
||||
<view class="model-popup-wrap">
|
||||
<view class="title">{{$tt('product.selectThingModel')}}</view>
|
||||
<u-radio-group>
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('product.attribute')" :name="1" :border="false" isLink @click="goToModelList"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('product.function')" :name="2" :border="false" isLink @click="goToModelList"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getJob, addJob, updateJob } from '@/apis/modules/job.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
jobId: null,
|
||||
isTime: false,
|
||||
isRepeat: false,
|
||||
isModel: false,
|
||||
swipeOptions: [{ text:this.$tt('common.delete'), style: { backgroundColor: '#f56c6c' } }],
|
||||
timingData: {
|
||||
actions: []
|
||||
}
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
this.jobId = Number(option.jobId);
|
||||
if (this.jobId) {
|
||||
this.getDetail();
|
||||
} else {
|
||||
this.timingData = uni.getStorageSync('timingData');
|
||||
}
|
||||
},
|
||||
onShow () {
|
||||
const callback = uni.getStorageSync('callback');
|
||||
if (callback) {
|
||||
this.timingData = uni.getStorageSync('timingData');
|
||||
}
|
||||
},
|
||||
onHide () {
|
||||
// 页面隐藏时关闭弹框
|
||||
this.isModel = false;
|
||||
// 保存当前页面数据
|
||||
uni.setStorageSync('timingData', this.timingData);
|
||||
},
|
||||
methods: {
|
||||
// 获取一键任务详情
|
||||
getDetail () {
|
||||
getJob(this.jobId).then(res => {
|
||||
if (res.code === 200) {
|
||||
// 计算时间和重复周期
|
||||
const { cronExpression } = res.data;
|
||||
const time = cronExpression.substring(5, 7) + ':' + cronExpression.substring(2, 4);
|
||||
const repeatValue = cronExpression.substring(12).split(',').map(Number);
|
||||
const repeat = this.getRepeatName(repeatValue)
|
||||
const actions = JSON.parse(res.data.actions);
|
||||
let timingData = uni.getStorageSync('timingData');
|
||||
this.timingData = {
|
||||
...timingData,
|
||||
...res.data,
|
||||
time,
|
||||
repeat,
|
||||
repeatValue,
|
||||
cronExpression,
|
||||
actions: actions,
|
||||
};
|
||||
uni.setStorageSync('timingData', this.timingData);
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log('定时详情:', err);
|
||||
})
|
||||
},
|
||||
// 时间选择
|
||||
handleDatetimeConfirm () {
|
||||
this.$nextTick(() => {
|
||||
this.gentCronExpression();
|
||||
})
|
||||
this.isTime = false;
|
||||
},
|
||||
// 星期选择
|
||||
handleRepeatConfirm () {
|
||||
const repeat = this.getRepeatName(this.timingData.repeatValue);
|
||||
this.timingData.repeat = repeat;
|
||||
this.gentCronExpression();
|
||||
this.isRepeat = false;
|
||||
},
|
||||
// 获取repeat名称
|
||||
getRepeatName (repeatValue) {
|
||||
const list = repeatValue.map(item => {
|
||||
if (item === 1) {
|
||||
return this.$tt('sceneTiming.monday')
|
||||
} else if (item === 2) {
|
||||
return this.$tt('sceneTiming.tuesday')
|
||||
} else if (item === 3) {
|
||||
return this.$tt('sceneTiming.wednesday')
|
||||
} else if (item === 4) {
|
||||
return this.$tt('sceneTiming.thursday')
|
||||
} else if (item === 5) {
|
||||
return this.$tt('sceneTiming.friday')
|
||||
} else if (item === 6) {
|
||||
return this.$tt('sceneTiming.saturday')
|
||||
} else if (item === 7) {
|
||||
return this.$tt('sceneTiming.sunday')
|
||||
}
|
||||
})
|
||||
if (list.length === 7) {
|
||||
return this.$tt('sceneTiming.everyDay');
|
||||
} else {
|
||||
return list.join(',');
|
||||
}
|
||||
},
|
||||
// 生成cron表达式
|
||||
gentCronExpression () {
|
||||
let hour = '00';
|
||||
let minute = '00';
|
||||
if (this.timingData.time) {
|
||||
hour = this.timingData.time.substring(0, 2);
|
||||
minute = this.timingData.time.substring(3);
|
||||
}
|
||||
let week = '*';
|
||||
if (this.timingData.repeatValue.length > 0) {
|
||||
week = this.timingData.repeatValue.sort();
|
||||
}
|
||||
this.timingData.cronExpression = '0 ' + minute + ' ' + hour + ' ? * ' + week;
|
||||
},
|
||||
// 获取设备图片
|
||||
getImageUrl (item) {
|
||||
if (item.type === 1) {
|
||||
return '/static/home/device/attribute.png';
|
||||
} else if (item.type === 2) {
|
||||
return '/static/home/device/function.png';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
handleAddModel () {
|
||||
uni.removeStorageSync('callback');
|
||||
this.isModel = true;
|
||||
},
|
||||
// 物模跳转
|
||||
goToModelList ({ name }) {
|
||||
let action = {
|
||||
id: '',
|
||||
name: '',
|
||||
value: '',
|
||||
valueName: '',
|
||||
type: 1,
|
||||
parentId: '',
|
||||
parentName: '',
|
||||
arrayIndex: '',
|
||||
arrayIndexName: '',
|
||||
};
|
||||
const { deviceId, deviceName } = this.timingData;
|
||||
const storage = uni.getStorageSync('action');
|
||||
if (storage) {
|
||||
uni.removeStorageSync('action');
|
||||
}
|
||||
action = { ...action, type: name, deviceId, deviceName };
|
||||
uni.setStorageSync('action', action);
|
||||
uni.$u.route('/pagesA/home/device/model');
|
||||
},
|
||||
// 删除触发、执行项
|
||||
handleDeleteSceneItem (index) {
|
||||
const { actions } = this.timingData;
|
||||
const list = actions.filter((item, i) => i !== index);
|
||||
this.timingData = { ...this.timingData, actions: [...list] };
|
||||
},
|
||||
// 编辑触发、执行项, 小程序转换问题,所以用对象传进来
|
||||
handleEditTimingItem (val) {
|
||||
const storage = uni.getStorageSync('action');
|
||||
if (storage) {
|
||||
uni.removeStorageSync('action');
|
||||
}
|
||||
uni.setStorageSync('action', val.item);
|
||||
uni.removeStorageSync('callback');
|
||||
uni.$u.route('/pagesA/home/device/model', { editIndex: val.index });
|
||||
},
|
||||
// 保存
|
||||
handleSave () {
|
||||
const { jobId, jobName, deviceId, actions, time, repeat, ...resd } = this.timingData;
|
||||
if (!jobName) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.timedName'),
|
||||
});
|
||||
return
|
||||
};
|
||||
if (actions.length === 0) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.taskExecution'),
|
||||
});
|
||||
return
|
||||
};
|
||||
const strActions = JSON.stringify(actions);
|
||||
const data = { ...resd, jobId, jobName, deviceId, actions: strActions };
|
||||
if (jobId) {
|
||||
updateJob(data).then(res => {
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('common.updateSuccessful'),
|
||||
});
|
||||
uni.navigateBack({ delta: 1 });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addJob(data).then(res => {
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('common.addSuccessful'),
|
||||
});
|
||||
uni.navigateBack({ delta: 1 });
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
getKey (index, item) {
|
||||
return `${index}-${item.id}`
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.device-timing-detail-wrap {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.name-wrap {
|
||||
margin: 30rpx;
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
padding-bottom: 140rpx;
|
||||
|
||||
.cell-group-wrap {
|
||||
margin: 30rpx;
|
||||
|
||||
.cell-wrap {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.condition-wrap {
|
||||
.form-wrap {
|
||||
background: #fff;
|
||||
margin: 30rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.form-item-wrap {
|
||||
padding: 4rpx 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.execute-wrap {
|
||||
margin: 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding-bottom: 12rpx;
|
||||
|
||||
.title-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-wrap {}
|
||||
}
|
||||
|
||||
.empty-cell {
|
||||
padding: 30rpx;
|
||||
|
||||
.text {
|
||||
height: 92rpx;
|
||||
border: 2rpx dashed #8686862e;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.swipe-action-wrap {
|
||||
.action-item-wrap {
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 2rpx solid #eaeefb;
|
||||
}
|
||||
|
||||
.slot-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.cell-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 24rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.des {
|
||||
font-size: 28rpx;
|
||||
color: #868686;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-wrap {
|
||||
padding: 0 28rpx;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-save-wrap {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
padding: 30rpx 26rpx 40rpx;
|
||||
background: #eef3f7f7;
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.repeat-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.radio-group-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.radio-wrap {
|
||||
padding: 40rpx;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.model-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.cell-group-wrap {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 6rpx 0;
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
436
pagesA/home/device/timing/index.vue
Normal file
436
pagesA/home/device/timing/index.vue
Normal file
@ -0,0 +1,436 @@
|
||||
<template>
|
||||
<view class="device-timing-wrap">
|
||||
<u-sticky zIndex="98" bgColor="#eef3f7">
|
||||
<view class="nav-bar">
|
||||
<view class="left-wrap">
|
||||
<view style="margin-right: 20rpx;">
|
||||
<u-icon name="list-dot" size="23" @click="goToTimingList"></u-icon>
|
||||
</view>
|
||||
<view v-if="!isSearch" style="margin-right: 20rpx;">
|
||||
<u-icon name="search" size="23" @click="isSearch = true"></u-icon>
|
||||
</view>
|
||||
<view v-else style="margin-right: 20rpx; width: 100%;">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.jobName"
|
||||
:placeholder="$tt('timing.timingName')" shape="circle" @clear="handleClearSearch" clearable>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input :customStyle="{ padding: '6rpx 18rpx'}" v-model="queryParams.jobName"
|
||||
:placeholder="$tt('timing.timingName')" shape="circle" @clear="handleClearSearch"
|
||||
clearable>
|
||||
<!-- #endif -->
|
||||
<template slot="prefix">
|
||||
<u-icon name="search" size="22" @click="isSearch = false"></u-icon>
|
||||
</template>
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('timing.search')" type="primary" shape="circle" size="mini"
|
||||
@click="handleSearch"></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
<u-icon name="plus-circle-fill" size="23" color='#3c9cff' bold @tap="handleAddTiming"></u-icon>
|
||||
</view>
|
||||
<view class="tab-wrap">
|
||||
<u-tabs :list="statusGroup" :scrollable="true" lineWidth="40" lineHeight="2" lineColor="transparent"
|
||||
:duration="100" :activeStyle="{ fontSize: '36rpx', color: '#3c9cff', fontWeight: 'bold' }"
|
||||
@change="handleStatusChange">
|
||||
</u-tabs>
|
||||
</view>
|
||||
</u-sticky>
|
||||
|
||||
<view class="container-wrap">
|
||||
<view class="item-wrap" v-for="(item, index) in dataList" :key="index">
|
||||
<view class="card" :style="{ margin: index === 0 ? '0 30rpx 30rpx' : '30rpx'}"
|
||||
@click="handleTimingDetail(item)">
|
||||
<view class="title">
|
||||
<u--text lines="1" prefixIcon="clock" :text="item.jobName"
|
||||
iconStyle="font-size: 30rpx; margin-right: 12rpx;"></u--text>
|
||||
<u-icon name="arrow-right" color="#606266" customStyle="margin-right:5px;" size="14"></u-icon>
|
||||
</view>
|
||||
|
||||
<view class="cond" v-html="formatCronDisplay(item)"></view>
|
||||
<view class="cond">{{getActionMun(item.actions)}} 个任务</view>
|
||||
|
||||
<view class="bottom-wrap" @tap.stop>
|
||||
<u--text lines="1" prefixIcon="/static/scene/once.png"
|
||||
iconStyle="width: 14px;height: 14px;margin-right: 3px;" size="13"
|
||||
:text="$tt('timing.execute')" color='#3C9CFF' lineHeight="16"
|
||||
@click="handleRunOne(item)"></u--text>
|
||||
<u-switch v-model="item.status" @change="handleSwitchStatus(item)" activeValue="0"
|
||||
inactiveValue="1" activeColor="#3C9CFF" inactiveColor="#dbdbdb" size="18"></u-switch>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('timing.effortLoading')" :loadmoreText="$tt('timing.loadMore')"
|
||||
:nomoreText="$tt('timing.nothing')" marginTop="20" @loadmore="loadMoreLogs" />
|
||||
</view>
|
||||
<u-loading-page :loading="loading" loadingText="XCWL.cn" bg-color="#eef3f7"></u-loading-page>
|
||||
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('timing.emptyNull')"></u-empty>
|
||||
<u-modal :show="isModal" :title="modalTitle" :content="modalContent" @confirm="modalConfirm"
|
||||
@cancel="modalCancle" :showCancelButton="true"></u-modal>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getJobList, runJob, changeJobStatus } from '@/apis/modules/job';
|
||||
|
||||
export default {
|
||||
name: 'device-timing',
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
},
|
||||
type: Number
|
||||
},
|
||||
watch: {
|
||||
device: {
|
||||
handler (val) {
|
||||
this.deviceInfo = val;
|
||||
this.queryParams.deviceId = val.deviceId;
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
type (val) {
|
||||
if (val === 1) {
|
||||
this.getList();
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: true, // 遮罩层
|
||||
isSearch: true,
|
||||
// 状态列表
|
||||
statusGroup: [{
|
||||
id: null,
|
||||
name: this.$tt('timing.all')
|
||||
},
|
||||
{
|
||||
id: '0',
|
||||
name: this.$tt('timing.enable')
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
name: this.$tt('timing.notEnabled')
|
||||
}
|
||||
],
|
||||
deviceInfo: {}, // 设备信息
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 4,
|
||||
deviceId: 0
|
||||
},
|
||||
dataList: [], // 列表数据
|
||||
total: 0, // 总条数
|
||||
loadmoreStatus: 'loadmore', // 加载更多
|
||||
// 模态窗显示
|
||||
handleType: 1, // 1-修改状态,2-执行一次
|
||||
isModal: false,
|
||||
modalTitle: '',
|
||||
modalContent: '',
|
||||
currentTiming: {}, // 当前选中的设备定时项
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 获取列表数据
|
||||
getList () {
|
||||
getJobList(this.queryParams).then(res => {
|
||||
if (res.code === 200) {
|
||||
if (this.queryParams.pageNum == 1) {
|
||||
this.dataList = res.rows;
|
||||
} else {
|
||||
this.dataList = this.dataList.concat(res.rows);
|
||||
}
|
||||
this.total = res.total;
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loadmore';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 搜索
|
||||
handleSearch () {
|
||||
this.dataList = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleClearSearch () {
|
||||
this.handleSearch();
|
||||
},
|
||||
goToTimingList () {
|
||||
const { deviceId } = this.deviceInfo;
|
||||
uni.$u.route('/pagesA/home/device/timing/list', { deviceId: deviceId });
|
||||
},
|
||||
// 新增
|
||||
handleAddTiming () {
|
||||
// 使用vuex会导致小程序性能问题所以存储本地
|
||||
let timingData = {
|
||||
jobId: null,
|
||||
jobName: '',
|
||||
jobGroup: 'DEFAULT', // 定时分组
|
||||
jobType: 1, // 任务类型 1=设备定时,2=设备告警,3=场景联动
|
||||
status: '0',
|
||||
isAdvance: 0, // 是否详细cron表达式
|
||||
time: uni.$u.timeFormat(new Date(), 'hh:MM'), // 辅助暂存数据
|
||||
repeat: this.$tt('sceneTiming.everyDay'), // 辅助暂存数据
|
||||
repeatValue: [1, 2, 3, 4, 5, 6, 7], // 辅助暂存数据
|
||||
cronExpression: `0 ${uni.$u.timeFormat(new Date(),'MM')} ${uni.$u.timeFormat(new Date(),'hh')} ? * 1,2,3,4,5,6,7`,
|
||||
misfirePolicy: 2, // 1=立即执行,2=执行一次,3=放弃执行
|
||||
concurrent: 1, // 是否并发,1=禁止,0=允许
|
||||
productId: null,
|
||||
productName: '',
|
||||
deviceId: null,
|
||||
deviceName: '',
|
||||
serialNumber: '',
|
||||
sceneId: 0, // 场景ID
|
||||
alertId: 0, // 告警ID
|
||||
actions: [],
|
||||
};
|
||||
const storage = uni.getStorageSync('timingData');
|
||||
if (storage) {
|
||||
uni.removeStorageSync('timingData');
|
||||
}
|
||||
const { productId, productName, deviceId, deviceName, serialNumber } = this.deviceInfo;
|
||||
timingData = { ...timingData, productId, productName, deviceId, deviceName, serialNumber };
|
||||
uni.setStorageSync('timingData', timingData);
|
||||
uni.removeStorageSync('callback');
|
||||
uni.$u.route('/pagesA/home/device/timing/detail');
|
||||
},
|
||||
// 定时状态改变事件
|
||||
handleStatusChange (item) {
|
||||
this.dataList = [];
|
||||
this.queryParams.status = item.id;
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
// 格式化显示CRON描述
|
||||
formatCronDisplay (item) {
|
||||
let result = '';
|
||||
let time = '<span>' + item.cronExpression.substring(5, 7) + ':' + item.cronExpression.substring(2, 4) +
|
||||
'</span>';
|
||||
let week = item.cronExpression.substring(12);
|
||||
if (week == '1,2,3,4,5,6,7') {
|
||||
result = this.$tt('sceneTiming.everyDay') + time;
|
||||
} else {
|
||||
let weekArray = week.split(',');
|
||||
for (let i = 0; i < weekArray.length; i++) {
|
||||
if (weekArray[i] == '1') {
|
||||
result = result + '周一、';
|
||||
} else if (weekArray[i] == '2') {
|
||||
result = result + '周二、';
|
||||
} else if (weekArray[i] == '3') {
|
||||
result = result + '周三、';
|
||||
} else if (weekArray[i] == '4') {
|
||||
result = result + '周四、';
|
||||
} else if (weekArray[i] == '5') {
|
||||
result = result + '周五、';
|
||||
} else if (weekArray[i] == '6') {
|
||||
result = result + '周六、';
|
||||
} else if (weekArray[i] == '7') {
|
||||
result = result + '周日、';
|
||||
}
|
||||
}
|
||||
result = result.substring(0, result.length - 1) + " <span style='color:#3fd1ad'>( " + time +
|
||||
' )</span>';
|
||||
}
|
||||
return result;
|
||||
},
|
||||
// 获取任务个数
|
||||
getActionMun (json) {
|
||||
if (json) {
|
||||
return JSON.parse(json).length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
// 立即执行一次
|
||||
handleRunOne (row) {
|
||||
this.modalContent = '确认要立即执行一次"' + row.jobName + '"定时吗?';
|
||||
this.isModal = true;
|
||||
this.handleType = 2;
|
||||
this.currentTiming = row;
|
||||
},
|
||||
// 定时状态修改
|
||||
handleSwitchStatus (row) {
|
||||
let text = row.status === '0' ? '启用' : '停用';
|
||||
this.modalContent = '确认要"' + text + '""' + row.jobName + '"定时吗?';
|
||||
this.isModal = true;
|
||||
this.handleType = 1;
|
||||
this.currentTiming = row;
|
||||
},
|
||||
// 模态窗确定
|
||||
modalConfirm () {
|
||||
if (this.handleType == 1) { // 修改定时状态
|
||||
const { jobId, status } = this.currentTiming;
|
||||
const data = {
|
||||
jobId: jobId,
|
||||
status: status
|
||||
};
|
||||
changeJobStatus(data).then(res => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
});
|
||||
} else if (this.handleType === 2) { // 执行一次
|
||||
const { jobId, jobGroup } = this.currentTiming;
|
||||
const data = {
|
||||
jobId: jobId,
|
||||
jobGroup: jobGroup,
|
||||
};
|
||||
runJob(data).then(res => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
});
|
||||
}
|
||||
this.isModal = false;
|
||||
},
|
||||
// 模态窗取消
|
||||
modalCancle () {
|
||||
if (this.handleType == 1) {
|
||||
this.currentTiming.status = this.currentTiming.status === '0' ? '1' : '0';
|
||||
}
|
||||
this.isModal = false;
|
||||
},
|
||||
// 编辑
|
||||
handleTimingDetail (item) {
|
||||
const storage = uni.getStorageSync('timingData');
|
||||
if (storage) {
|
||||
uni.removeStorageSync('timingData');
|
||||
}
|
||||
const timingData = {
|
||||
jobId: null,
|
||||
jobName: '',
|
||||
jobGroup: 'DEFAULT', // 定时分组
|
||||
jobType: 1, // 任务类型 1=设备定时,2=设备告警,3=场景联动
|
||||
status: '0',
|
||||
isAdvance: 0, // 是否详细cron表达式
|
||||
time: uni.$u.timeFormat(new Date(), 'hh:MM'), // 辅助暂存数据
|
||||
repeat: this.$tt('sceneTiming.everyDay'), // 辅助暂存数据
|
||||
repeatValue: [1, 2, 3, 4, 5, 6, 7], // 辅助暂存数据
|
||||
cronExpression: `0 ${uni.$u.timeFormat(new Date(),'MM')} ${uni.$u.timeFormat(new Date(),'hh')} ? * 1,2,3,4,5,6,7`,
|
||||
misfirePolicy: 2, // 1=立即执行,2=执行一次,3=放弃执行
|
||||
concurrent: 1, // 是否并发,1=禁止,0=允许
|
||||
productId: null,
|
||||
productName: '',
|
||||
deviceId: null,
|
||||
deviceName: '',
|
||||
serialNumber: '',
|
||||
sceneId: 0, // 场景ID
|
||||
alertId: 0, // 告警ID
|
||||
actions: [],
|
||||
};
|
||||
uni.setStorageSync('timingData', timingData);
|
||||
uni.removeStorageSync('callback');
|
||||
uni.$u.route('/pagesA/home/device/timing/detail', { jobId: item.jobId });
|
||||
},
|
||||
// 上拉加载(由于不是在新页面,所以只能点击加载)
|
||||
loadMoreLogs () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getList();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.dataList = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.type === 1 && this.getList();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.device-timing-wrap {
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 24rpx 26rpx 0;
|
||||
height: 74rpx;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-wrap {
|
||||
padding: 0 12rpx 10rpx 12rpx;
|
||||
|
||||
.add-btn {
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
.item-wrap {
|
||||
.card {
|
||||
box-shadow: 0 2rpx 0rpx 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.title {
|
||||
padding: 10rpx 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.cond {
|
||||
font-size: 12px;
|
||||
color: #7e7e7e;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.bottom-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 20rpx 0 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
143
pagesA/home/device/timing/list.vue
Normal file
143
pagesA/home/device/timing/list.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.timedList')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="device-timing-list-wrap">
|
||||
<view class="container-wrap">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in list" :key="index">
|
||||
<u-cell :title="item.jobName" :border="false" @click="handleDelete(item)">
|
||||
<u-icon slot="right-icon" size="18" name="minus-circle-fill" color="#ff7e7e"></u-icon>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
|
||||
:nomoreText="$tt('scene.emptyData')" marginTop="20" />
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
|
||||
<u-modal :show="isDelete" showCancelButton :confirmText="$tt('common.delete')"
|
||||
@cancel="() => isDelete = false" :title="$tt('sceneDetail.tips')" :content="deleteContent"
|
||||
@confirm="handleConfirmDelete">
|
||||
</u-modal>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getJobList, delJob } from '@/apis/modules/job.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 16,
|
||||
deviceId: null,
|
||||
},
|
||||
list: [], // 场景列表
|
||||
total: 0,
|
||||
loadmoreStatus: 'loadmore', // 刷新和加载相关
|
||||
isDelete: false,
|
||||
deleteId: '',
|
||||
deleteContent: '',
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
this.queryParams.deviceId = Number(option.deviceId);
|
||||
this.getTimingDatas();
|
||||
},
|
||||
methods: {
|
||||
// 获取区设备列表
|
||||
getTimingDatas () {
|
||||
getJobList(this.queryParams)
|
||||
.then(res => {
|
||||
const { rows, total } = res;
|
||||
this.list = this.list.concat(rows);
|
||||
this.total = total;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.getTimingDatas();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getTimingDatas();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 删除
|
||||
handleDelete (item) {
|
||||
this.isDelete = true;
|
||||
this.deleteId = item.jobId;
|
||||
this.deleteContent = `确定删除“${item.jobName}”?`
|
||||
},
|
||||
// 确认删除
|
||||
handleConfirmDelete () {
|
||||
delJob(this.deleteId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getTimingDatas();
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
this.isDelete = false;
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.device-timing-list-wrap {
|
||||
.container-wrap {
|
||||
.cell-group-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin: 30rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 6rpx;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
237
pagesA/home/device/video/index.vue
Normal file
237
pagesA/home/device/video/index.vue
Normal file
@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<view class="video-monitor">
|
||||
<view class="channel_wrap">
|
||||
<view class="item_body" v-for="(item, i) in channelList" :key="i"
|
||||
@click="gotoDevicePlayer(item.deviceSipId, item.channelId, item.status)">
|
||||
<view class="img">
|
||||
<image src="@/static/common/cameras.png" mode="widthFix" style="width: 200rpx;" class="play-icon">
|
||||
</image>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="left_content">
|
||||
<u--text class="channel_name" :text="item.channelName"></u--text>
|
||||
</view>
|
||||
<view class="right_content">
|
||||
<u--text class="device_info" :text="'@'+item.model"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="status">
|
||||
<u-tag v-if="item.statusInfo" :type="item.statusInfo.type" :plain="true" size="mini"
|
||||
:text="item.statusInfo.name"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
</view>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
relateChannelList
|
||||
} from '@/apis/modules/device.js';
|
||||
import {
|
||||
getSipStatusInfo
|
||||
} from '@/helpers/common.js';
|
||||
|
||||
export default {
|
||||
name: "videoMonitor",
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 兼容小程序
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
this.isSubDev = newVal.subDeviceList && newVal.subDeviceList.length > 0;
|
||||
if (this.deviceInfo.sipRelationList) {
|
||||
this.getRelateChannelList();
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isSubDev: false,
|
||||
// 设备信息
|
||||
deviceInfo: {
|
||||
chartList: [],
|
||||
},
|
||||
//通道列表
|
||||
channelList: [],
|
||||
// 设备通道
|
||||
channelLoadStatus: 'nomore',
|
||||
queryParams: {
|
||||
deviceSipId: '', //设备sipid
|
||||
},
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
created () {
|
||||
// 获取设备状态(兼容H5和APP)
|
||||
if (this.device.subDeviceList) {
|
||||
this.deviceInfo = this.device;
|
||||
this.isSubDev = this.device.subDeviceList.length > 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 设备通道
|
||||
getRelateChannelList () {
|
||||
return new Promise((resolve, reject) => {
|
||||
relateChannelList(this.deviceInfo.deviceId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.channelList = res.data;
|
||||
this.total = res.data.length;
|
||||
res.data.map(item => {
|
||||
item.statusInfo = getSipStatusInfo(item.status);
|
||||
return item;
|
||||
})
|
||||
resolve(res.data);
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
gotoDevicePlayer (deviceSipId, channelSipId, status) {
|
||||
let statusInfo = getSipStatusInfo(status)
|
||||
if (status !== 3) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: `无法查看${getSipStatusInfo(status).name}数据`
|
||||
});
|
||||
return;
|
||||
}
|
||||
// #ifdef H5
|
||||
uni.$u.route('/pages_player/list/devicePlayer', {
|
||||
deviceId: deviceSipId,
|
||||
channelSipId: channelSipId,
|
||||
});
|
||||
//#endif
|
||||
// #ifdef APP-PLUS
|
||||
uni.$u.route('/pages_player/list/devicePlayerApp', {
|
||||
deviceId: deviceSipId,
|
||||
channelSipId: channelSipId,
|
||||
});
|
||||
//#endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('timing.nosupported')
|
||||
});
|
||||
//#endif
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.video-monitor {
|
||||
padding: 20rpx;
|
||||
|
||||
.channel_wrap {
|
||||
padding: 20rpx 0;
|
||||
|
||||
.item_body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 30rpx;
|
||||
background: rgba(255, 255, 255, 0.8); // 更轻的透明背景
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1), inset 0 1rpx 1rpx rgba(255, 255, 255, 0.3); // 外部阴影+内部高光
|
||||
backdrop-filter: blur(15rpx) saturate(180%); // 轻微模糊和饱和度提升
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3); // 玻璃效果边框
|
||||
transition: all 0.3s ease;
|
||||
padding: 40rpx;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-10rpx); // 悬停时轻微提升
|
||||
background: rgba(255, 255, 255, 0.2); // 悬停时背景变得更透明
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.2), inset 0 2rpx 2rpx rgba(255, 255, 255, 0.4); // 更强的阴影和高光
|
||||
}
|
||||
|
||||
.img {
|
||||
margin-top: 40rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(62, 135, 230, 0.3), rgba(0, 255, 255, 0.3)); // 渐变背景增强
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.play-icon {
|
||||
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover .play-icon {
|
||||
transform: scale(1.2); // 放大效果
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.2), rgba(0, 0, 0, 0));
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0 30rpx;
|
||||
|
||||
.left_content {
|
||||
.channel_name {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.right_content {
|
||||
.device_info {
|
||||
font-size: 28rpx;
|
||||
color: #777777;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.channel_wrap .item_body {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
</style>
|
815
pagesA/list/home/deviceAdd.vue
Normal file
815
pagesA/list/home/deviceAdd.vue
Normal file
@ -0,0 +1,815 @@
|
||||
<template>
|
||||
<page-meta><navigation-bar :title="$tt('deviceAdd.addDevice')" title-align="center"
|
||||
background-color="#007AFF" /></page-meta>
|
||||
<view class="container">
|
||||
<view class="card">
|
||||
<u--form labelPosition="left" :model="form" :rules="rules" ref="form" labelWidth="80" labelAlign="center">
|
||||
<view style="padding:10px 0;;border-bottom:1px solid #EFEFEF;display:flex;">
|
||||
<u--text prefixIcon="grid-fill" iconStyle="font-size: 16px;color:#606266" :text="$tt('deviceAdd.step1')" bold
|
||||
color="#606266"></u--text>
|
||||
</view>
|
||||
<u-form-item :label="$tt('deviceAdd.wifi')" prop="SSID"><u-input v-model="form.SSID"
|
||||
:placeholder="$tt('deviceAdd.inputWifiName')"></u-input></u-form-item>
|
||||
<u-form-item :label="$tt('deviceAdd.passsword')" prop="password">
|
||||
<u-input v-model="form.password" :placeholder="$tt('deviceAdd.inputWifiPassword')"
|
||||
:type="passwordShow ? 'text' : 'password'">
|
||||
<template slot="suffix">
|
||||
<view @click="passwordShow = !passwordShow"><u-icon name="eye-off" size="24"
|
||||
v-if="!passwordShow" color="#666"></u-icon></view>
|
||||
<view @click="passwordShow = !passwordShow"><u-icon name="eye-fill" size="24"
|
||||
v-if="passwordShow" color="#666"></u-icon></view>
|
||||
</template>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label=" ">
|
||||
<u-checkbox-group v-model="checkboxConfigs">
|
||||
<u-checkbox :customStyle="{ marginRight: '15px' }" :label="$tt('deviceAdd.remember')"
|
||||
name="remeber"></u-checkbox>
|
||||
<u-checkbox :customStyle="{}" :label="$tt('deviceAdd.senior')" name="advance"></u-checkbox>
|
||||
</u-checkbox-group>
|
||||
</u-form-item>
|
||||
</u--form>
|
||||
<view v-if="checkboxConfigs.indexOf('advance') != -1">
|
||||
<u--form labelPosition="left" :model="form" :rules="rules" ref="form2" labelWidth="80"
|
||||
labelAlign="center" :labelStyle="{ color: '#3fd1ad', fontSize: '14px' }">
|
||||
<u-form-item :label="$tt('deviceAdd.userId')" prop="userId">
|
||||
<u-input v-model="form.userId" :placeholder="$tt('deviceAdd.inputUserId')"
|
||||
:disabled="userIdDisabled">
|
||||
<template slot="suffix">
|
||||
<view @click="userIdDisabled = !userIdDisabled"><u-icon name="lock-fill" size="24"
|
||||
v-if="userIdDisabled" color="#666"></u-icon></view>
|
||||
<view @click="userIdDisabled = !userIdDisabled"><u-icon name="lock-opened-fill"
|
||||
size="24" v-if="!userIdDisabled" color="#666"></u-icon></view>
|
||||
</template>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceAdd.deviceNum')" prop="deviceNum">
|
||||
<u-input v-model="form.deviceNum" :placeholder="$tt('deviceAdd.inputDeviceNum')">
|
||||
<template slot="suffix">
|
||||
<u-icon name="scan" color="#666" size="24" @click="openScan"></u-icon>
|
||||
</template>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item :label="$tt('deviceAdd.authorization')" prop="authCode"><u-input
|
||||
v-model="form.authCode" :placeholder="$tt('deviceAdd.inputAuthor')"></u-input></u-form-item>
|
||||
<u-form-item :label="$tt('deviceAdd.supplementary')" prop="extra">
|
||||
<u-textarea v-model="form.extra" height="40" fontSize="14"
|
||||
:placeholder="$tt('deviceAdd.inputSupplementary')" confirmType="done"></u-textarea>
|
||||
</u-form-item>
|
||||
</u--form>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view style="padding:10px 0;">
|
||||
<u--text prefixIcon="grid-fill" iconStyle="font-size: 16px;color:#606266" :text="$tt('deviceAdd.step2')"
|
||||
bold color="#606266"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view style="padding:0;border-bottom:1px solid #EFEFEF;display:flex;">
|
||||
<u--text prefixIcon="grid-fill" iconStyle="font-size: 16px;color:#606266" :text="$tt('deviceAdd.step3')"
|
||||
bold color="#606266"></u--text>
|
||||
<view>
|
||||
<u-tabs :list="[{ name: $tt('deviceAdd.single') }, { name: $tt('deviceAdd.multiple') }]"
|
||||
:current="tabIndex" :scrollable="false" lineWidth="50" lineHeight="2" :duration="100"
|
||||
@click="tabClick"></u-tabs>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="tabIndex == 0">
|
||||
<view style="margin:30px 0 0 0;display:flex;">
|
||||
<u-steps :current="step" direction="column">
|
||||
<u-steps-item :title="$tt('deviceAdd.networkMode')"
|
||||
:desc="$tt('deviceAdd.wifiHotspot')"></u-steps-item>
|
||||
<u-steps-item :title="$tt('deviceAdd.manually')" :desc="$tt('deviceAdd.mobile')"></u-steps-item>
|
||||
<u-steps-item :title="$tt('deviceAdd.detection')"
|
||||
:desc="$tt('deviceAdd.system')"></u-steps-item>
|
||||
<u-steps-item :title="$tt('deviceAdd.end')" :desc="$tt('deviceAdd.reconnected')"></u-steps-item>
|
||||
</u-steps>
|
||||
<view style="margin:0 auto;">
|
||||
<circleProgress :percent="progress" borderWidth="12" width="260">
|
||||
<view>
|
||||
<text style="font-size:24px;display:block;">{{ progress }}%</text>
|
||||
</view>
|
||||
</circleProgress>
|
||||
<view style="display:flex;">
|
||||
<u-loading-icon customStyle="width:30px;margin-left:10px;" size="12"
|
||||
v-if="step < 2"></u-loading-icon>
|
||||
<u--text :text="count.text" :type="count.type" size="12"
|
||||
:customStyle="step < 2 ? '' : 'margin:0 auto;'"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="padding:30px 10px;" v-if="step < 3"><u-button :text="$tt('deviceAdd.startWork')"
|
||||
type="primary" @click="beginConfig" :disabled="step < 2"></u-button></view>
|
||||
<view style="padding:30px 10px;" v-if="step == 4"><u-button
|
||||
:text="$tt('deviceAdd.redistributingNetwork')" type="warning" @click="restartConfig"></u-button>
|
||||
</view>
|
||||
<view style="padding:30px 10px;" v-if="step == 3"><u-button :text="$tt('deviceAdd.after')"
|
||||
type="primary" @click="goBack"></u-button></view>
|
||||
</view>
|
||||
<view v-if="tabIndex == 1">
|
||||
<view style="margin:20px 0 0 10px;"><u--text type="warning" :text="$tt('deviceAdd.tip')"
|
||||
size="12"></u--text></view>
|
||||
<uni-table ref="table" :loading="loading" :emptyText="$tt('deviceAdd.noData')" style="margin-top:10px;">
|
||||
<uni-tr>
|
||||
<uni-th>
|
||||
<view style="display:flex;width:100%;">
|
||||
<view style="flex:1;"><u--text size="15" color="#606266" lines="1"
|
||||
:text="$tt('deviceAdd.select')"></u--text></view>
|
||||
<view style="width:40px;">
|
||||
<u-button :text="$tt('deviceAdd.refresh')" :disabled="!isWeChat || updateDisable"
|
||||
size="mini" type="primary" @click="updateWfifiListInWeChat"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-th>
|
||||
</uni-tr>
|
||||
<uni-tr v-for="(wifi, index) in wifiList" :key="index">
|
||||
<uni-td v-if="wifi.signalStrength > 0">
|
||||
<view @click="selectChange(wifi)">
|
||||
<view style="display:flex;margin:-10px;padding:10px 0;">
|
||||
<view style="width:35px;align-self:center;">
|
||||
<checkbox :checked="wifi.checked" style="transform:scale(0.8)"
|
||||
color="#3c9cff" />
|
||||
</view>
|
||||
<view style="flex:1">
|
||||
<view style="display:flex;line-height:30px;align-items: center;">
|
||||
<view style="flex:1;">
|
||||
<u--text size="14" :color="wifi.secure ? '#999' : '#3c9cff'"
|
||||
:text="wifi.SSID"></u--text>
|
||||
</view>
|
||||
<view style="margin-right: 10px;">
|
||||
<u-icon v-if="wifi.signalStrength > 0 && wifi.signalStrength < 25"
|
||||
name="/static/wifi_1.png" size="14"></u-icon>
|
||||
<u-icon v-if="wifi.signalStrength >= 25 && wifi.signalStrength < 50"
|
||||
name="/static/wifi_2.png" size="14"></u-icon>
|
||||
<u-icon v-if="wifi.signalStrength >= 50 && wifi.signalStrength < 75"
|
||||
name="/static/wifi_3.png" size="14"></u-icon>
|
||||
<u-icon v-if="wifi.signalStrength >= 75 && wifi.signalStrength <= 100"
|
||||
name="/static/wifi_4.png" size="14"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view style="display:flex;" v-if="wifi.checked == true">
|
||||
<view style="width:100%;margin-top:6px;"><u-line-progress
|
||||
:percentage="wifi.process" activeColor="#3c9cff"></u-line-progress>
|
||||
</view>
|
||||
<view style="width:70px;margin-left:6px;"><u--text size="12"
|
||||
:text="wifi.text" :type="wifi.type"></u--text></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-td>
|
||||
</uni-tr>
|
||||
</uni-table>
|
||||
<view style="padding:20px 10px;" v-if="mstep == 0"><u-button :text="$tt('deviceAdd.start')"
|
||||
type="primary" @click="beginConfigInWeChart" :disabled="!isWeChat"></u-button></view>
|
||||
<view style="padding:20px 10px;" v-if="mstep == 1"><u-button :text="$tt('deviceAdd.distribution')"
|
||||
type="primary" :loading="true"></u-button></view>
|
||||
<view style="padding:20px 10px;" v-if="mstep == 2"><u-button :text="$tt('deviceAdd.connect')"
|
||||
type="primary" :loading="true"></u-button></view>
|
||||
<view style="padding:20px 10px;" v-if="mstep == 3"><u-button :text="$tt('deviceAdd.return')"
|
||||
type="primary" @click="goBack"></u-button></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getProfile
|
||||
} from '@/apis/modules/common';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 是否为微信小程序
|
||||
isWeChat: false,
|
||||
// 复选框配置remeber、advance
|
||||
checkboxConfigs: ['remeber'],
|
||||
// 配网模式选项卡索引
|
||||
tabIndex: 0,
|
||||
// wifi密码可见性
|
||||
passwordShow: true,
|
||||
// 用户编号是否可编辑
|
||||
userIdDisabled: true,
|
||||
// wifi信息
|
||||
form: {
|
||||
type: 0, // 类型0=设备配网,1=关联设备
|
||||
SSID: '',
|
||||
password: '',
|
||||
userId: 0,
|
||||
deviceNum: '',
|
||||
authCode: '',
|
||||
extra: ''
|
||||
},
|
||||
// 计数
|
||||
count: {
|
||||
// 进度定时器
|
||||
timer: {},
|
||||
// 显示文本
|
||||
text: this.$tt('deviceAdd.noDevice'),
|
||||
// 文字类型
|
||||
type: 'warning'
|
||||
},
|
||||
// 发现设备计时器
|
||||
discoverTimer: {},
|
||||
// 单设备配网进度
|
||||
progress: 0,
|
||||
// 单设备配网步骤
|
||||
step: 1,
|
||||
|
||||
// wifi列表加载
|
||||
loading: false,
|
||||
// wifi列表
|
||||
wifiList: [
|
||||
// {
|
||||
// SSID: 'wumei-device1',
|
||||
// secure: false,
|
||||
// signalStrength: 30,
|
||||
// checked: false,
|
||||
// process: 0,
|
||||
// text: '准备配网',
|
||||
// type: 'primary'
|
||||
// },
|
||||
// {
|
||||
// SSID: 'wumei-device2',
|
||||
// secure: true,
|
||||
// signalStrength: 90,
|
||||
// checked: false,
|
||||
// process: 0,
|
||||
// text: '准备配网',
|
||||
// type: 'primary'
|
||||
// }
|
||||
],
|
||||
// 选中的wifi列表
|
||||
selectedWifiList: [],
|
||||
// Wifi表格选中的索引
|
||||
// index: [],
|
||||
// 多设备配网步骤,0=未开始配网,1=配网中,2=配网结束
|
||||
mstep: 0,
|
||||
// 更新WIFI按钮
|
||||
updateDisable: false,
|
||||
rules: {
|
||||
SSID: [{
|
||||
required: true,
|
||||
message: this.$tt('deviceAdd.wifiName'),
|
||||
trigger: ['blur', 'change']
|
||||
}],
|
||||
password: [{
|
||||
required: true,
|
||||
message: this.$tt('deviceAdd.wifiPassword'),
|
||||
trigger: ['blur', 'change']
|
||||
}],
|
||||
userId: [{
|
||||
required: true,
|
||||
message: this.$tt('deviceAdd.userNum'),
|
||||
trigger: ['blur', 'change']
|
||||
}]
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.initInWeChat();
|
||||
this.tabIndex = 1;
|
||||
this.isWeChat = true;
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
this.tabIndex = 0;
|
||||
// #endif
|
||||
|
||||
//获取登录用户信息
|
||||
if (this.profile == null) {
|
||||
this.getProfile();
|
||||
} else {
|
||||
this.form.userId = this.profile.userId;
|
||||
}
|
||||
// 断开Mqtt连接
|
||||
this.endMqtt();
|
||||
// 获取WIFI信息
|
||||
this.getWifi();
|
||||
if (this.tabIndex == 0) {
|
||||
// 发现设备
|
||||
this.discoverDevice();
|
||||
}
|
||||
},
|
||||
onUnload() {
|
||||
clearInterval(this.discoverTimer);
|
||||
// 页面卸载时连接mqtt
|
||||
this.connectMqtt();
|
||||
},
|
||||
methods: {
|
||||
/* 断开Mqtt消息服务器 */
|
||||
async endMqtt() {
|
||||
await this.$mqttTool.end();
|
||||
},
|
||||
/* 连接Mqtt消息服务器 */
|
||||
async connectMqtt() {
|
||||
if (this.$mqttTool.client == null) {
|
||||
console.log('连接mqtt...');
|
||||
await this.$mqttTool.connect(this.vuex_token);
|
||||
}
|
||||
},
|
||||
// 获取登录用户信息
|
||||
getProfile() {
|
||||
// 调用用户信息接口
|
||||
getProfile().then(res => {
|
||||
//存储用户信息,TODO 需要调用一次,不然其他页面调用返回空
|
||||
uni.$u.vuex('profile', res.data);
|
||||
this.profile;
|
||||
this.form.userId = this.profile.userId;
|
||||
}).catch(err => {
|
||||
this.$u.toast(err.msg);
|
||||
});
|
||||
},
|
||||
// 单击选显卡
|
||||
tabClick(item) {
|
||||
this.tabIndex = item.index;
|
||||
if (this.tabIndex == 0) {
|
||||
// 发现设备
|
||||
this.discoverDevice();
|
||||
} else {
|
||||
// 清除发现设备定时器
|
||||
clearInterval(this.discoverTimer);
|
||||
}
|
||||
},
|
||||
// 返回
|
||||
goBack() {
|
||||
getApp().globalData.operate = 'operate';
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
},
|
||||
// 扫码
|
||||
openScan() {
|
||||
// #ifndef MP-WEIXIN || APP-PLUS
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('user.scanning')
|
||||
});
|
||||
return;
|
||||
// #endif
|
||||
|
||||
// 允许从相机和相册扫码
|
||||
uni.scanCode({
|
||||
success: res => {
|
||||
console.log('条码类型:' + res.scanType);
|
||||
console.log('条码内容:' + res.result);
|
||||
if (res.result.substr(0, 1) != '{') {
|
||||
console.log('坑点:解析二维码后第一个位置包含一个特殊字符,大部分编译器和调试工具识别不了这个特殊字符');
|
||||
res.result = res.result.substring(1);
|
||||
}
|
||||
// 解析JSON
|
||||
try {
|
||||
let json = JSON.parse(res.result);
|
||||
// type=1 代表扫码关联设备
|
||||
if (json.type == 1) {
|
||||
this.form.deviceNum = json.deviceNumber;
|
||||
return;
|
||||
}
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '解析二维码,格式不正确'
|
||||
});
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '解析二维码,格式不正确'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 保存WIFI
|
||||
saveWifi() {
|
||||
if (this.form.SSID != '' && this.form.password != '') {
|
||||
// 本地缓存存储
|
||||
uni.setStorageSync('WIFI_SSID', this.form.SSID);
|
||||
uni.setStorageSync('WIFI_PASSWORD', this.form.password);
|
||||
}
|
||||
},
|
||||
// 获取WIFI
|
||||
getWifi() {
|
||||
// 本地缓存获取
|
||||
let wifi_ssid = uni.getStorageSync('WIFI_SSID');
|
||||
let wifi_password = uni.getStorageSync('WIFI_PASSWORD');
|
||||
if (wifi_ssid && wifi_ssid != '') {
|
||||
this.form.SSID = wifi_ssid;
|
||||
}
|
||||
if (wifi_password && wifi_password != '') {
|
||||
this.form.password = wifi_password;
|
||||
}
|
||||
},
|
||||
// 获取配网接口地址
|
||||
getParamString() {
|
||||
console.log('form', this.form);
|
||||
let ip = 'http://192.168.4.1/config';
|
||||
let params = '?SSID=' + this.form.SSID + '&password=' + this.form.password + '&userId=' + this.form.userId;
|
||||
if (this.form.deviceNum && this.form.deviceNum != '') {
|
||||
params = params + '&deviceNum=' + this.form.deviceNum;
|
||||
}
|
||||
if (this.form.deviceName && this.form.deviceName != '') {
|
||||
params = params + '&deviceName=' + this.form.deviceName;
|
||||
}
|
||||
if (this.form.authCode && this.form.authCode != '') {
|
||||
params = params + '&authCode=' + this.form.authCode;
|
||||
}
|
||||
if (this.form.extra && this.form.extra != '') {
|
||||
params = params + '&extra=' + this.form.extra;
|
||||
}
|
||||
return ip + params;
|
||||
},
|
||||
// 设备AP配网
|
||||
apConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: this.getParamString(),
|
||||
method: 'POST',
|
||||
timeout: 10000,
|
||||
success: res => {
|
||||
resolve(true);
|
||||
},
|
||||
fail: res => {
|
||||
reject(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/****************************************** 单设备 *********************************************/
|
||||
// 检查设备是否配网准备就绪
|
||||
discoverDevice() {
|
||||
this.discoverTimer = setInterval(() => {
|
||||
if (this.tabIndex == 0) {
|
||||
uni.request({
|
||||
url: 'http://192.168.4.1/status',
|
||||
method: 'GET',
|
||||
timeout: 5000,
|
||||
success: res => {
|
||||
clearInterval(this.discoverTimer);
|
||||
this.step = 2;
|
||||
this.progress == 0;
|
||||
this.count = {
|
||||
text: this.$tt('deviceAdd.deviceDetected'),
|
||||
type: 'success',
|
||||
process: 0
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 5000);
|
||||
},
|
||||
// 配网进度显示
|
||||
showConfigProgress() {
|
||||
this.count.timer = setInterval(() => {
|
||||
if (this.progress == 96) {
|
||||
clearInterval(this.count.timer);
|
||||
} else {
|
||||
this.progress = this.progress + 1;
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
// 重新开始配网
|
||||
restartConfig() {
|
||||
this.progress = 0;
|
||||
this.step = 1;
|
||||
this.count = {
|
||||
// 进度定时器
|
||||
timer: {},
|
||||
// 显示文本
|
||||
text: this.$tt('deviceAdd.noDevice'),
|
||||
// 文字类型
|
||||
type: 'warning'
|
||||
};
|
||||
this.discoverDevice();
|
||||
},
|
||||
// 开始配网
|
||||
async beginConfig() {
|
||||
// 验证用户编号和WIFI信息
|
||||
if (!this.$refs.form.validate()) {
|
||||
uni.$u.toast(this.$tt('deviceAdd.userIdAndWifiAccount'));
|
||||
return;
|
||||
}
|
||||
//保存WIFI信息
|
||||
if (this.checkboxConfigs.indexOf('remeber') != -1) {
|
||||
this.saveWifi();
|
||||
}
|
||||
// 设置进度
|
||||
this.progress = 0;
|
||||
this.showConfigProgress();
|
||||
this.count.text = this.$tt('deviceAdd.sendInformation');
|
||||
this.count.type = 'info';
|
||||
// 配网
|
||||
try {
|
||||
let result = await this.apConfig();
|
||||
console.log('ap config result:', result);
|
||||
if (result) {
|
||||
// 清除计数器
|
||||
clearInterval(this.count.timer);
|
||||
this.progress = 100;
|
||||
this.count.text = this.$tt('deviceAdd.successNetwork');
|
||||
this.count.type = 'success';
|
||||
this.step = 3;
|
||||
} else {
|
||||
// 清除计数器
|
||||
clearInterval(this.count.timer);
|
||||
this.count.text = this.$tt('deviceAdd.successNetwork');
|
||||
this.count.type = 'error';
|
||||
this.step = 4;
|
||||
}
|
||||
} catch (e) {
|
||||
// 清除计数器
|
||||
clearInterval(this.count.timer);
|
||||
this.count.text = this.$tt('deviceAdd.fail');
|
||||
this.count.type = 'error';
|
||||
this.step = 4;
|
||||
}
|
||||
},
|
||||
|
||||
/****************************************** 多设备,小程序可用 *********************************************/
|
||||
// 表格中的Wifi选择改变
|
||||
selectChange(wifi) {
|
||||
if (wifi.checked == true) {
|
||||
wifi.checked = false;
|
||||
for (let i = 0; i < this.selectedWifiList.length; i++) {
|
||||
if (wifi.SSID == this.selectedWifiList[i].SSID) {
|
||||
this.selectedWifiList.splice(i, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wifi.checked = true;
|
||||
this.selectedWifiList.push(wifi);
|
||||
}
|
||||
console.log(this.selectedWifiList);
|
||||
},
|
||||
// 获取选中Wifi在所有wifi列表中的索引
|
||||
getWifiIndex(SSID) {
|
||||
for (let i = 0; i < this.wifiList.length; i++) {
|
||||
if (SSID == this.wifiList[i].SSID) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
// 小程序多设备配网
|
||||
async beginConfigInWeChart() {
|
||||
// 验证用户编号和WIFI信息
|
||||
if (!this.$refs.form.validate()) {
|
||||
uni.$u.toast(this.$tt('deviceAdd.userIdAndWifiAccount'));
|
||||
return;
|
||||
}
|
||||
// 判断是否选择了设备热点
|
||||
if (this.selectedWifiList.length == 0) {
|
||||
uni.$u.toast(thius.$tt('deviceAdd.selectNetwork'));
|
||||
return;
|
||||
}
|
||||
//保存WIFI信息
|
||||
if (this.checkboxConfigs.indexOf('remeber') != -1) {
|
||||
this.saveWifi();
|
||||
}
|
||||
this.mstep = 1;
|
||||
let that = this;
|
||||
for (let i = 0; i < that.selectedWifiList.length; i++) {
|
||||
let index = this.getWifiIndex(that.selectedWifiList[i].SSID);
|
||||
console.log('wifi在列表中的索引:', index);
|
||||
// 配网进度显示
|
||||
that.wifiList[index].type = 'primary';
|
||||
that.wifiList[index].text = '配网中...';
|
||||
let processTimer = setInterval(() => {
|
||||
if (that.wifiList[index].process == 90) {
|
||||
clearInterval(processTimer);
|
||||
} else {
|
||||
that.wifiList[index].process = that.wifiList[index].process + 1;
|
||||
}
|
||||
}, 350);
|
||||
// 微信连接设备热点
|
||||
try {
|
||||
let result = await that.connectWifiInWeChat(that.wifiList[index].SSID, '');
|
||||
console.log('连接设备热点: ' + that.wifiList[index].SSID + ' result:', result);
|
||||
if (result == true) {
|
||||
// 配网
|
||||
let apResult = await that.apConfig();
|
||||
console.log('AP配网,result:', apResult);
|
||||
if (apResult == true) {
|
||||
// 配网成功
|
||||
clearInterval(processTimer);
|
||||
that.wifiList[index].process = 100;
|
||||
that.wifiList[index].type = 'success';
|
||||
that.wifiList[index].text = this.$tt('deviceAdd.successDistribution');
|
||||
} else {
|
||||
// 配网失败
|
||||
that.failConfigInWeChat(that.wifiList[index], processTimer);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('连接WIFI异常,catch: ', e);
|
||||
// 配网失败
|
||||
that.failConfigInWeChat(that.wifiList[index], processTimer);
|
||||
}
|
||||
}
|
||||
//配网成功后停止wifi然后启动、手机自动连接wifi
|
||||
that.mstep = 2;
|
||||
try {
|
||||
console.log('停止Wifi...');
|
||||
await that.stopWifiInWeChat();
|
||||
} catch (e) {
|
||||
console.log('停止Wifi异常', e);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('deviceAdd.afterNetwork')
|
||||
});
|
||||
}
|
||||
try {
|
||||
console.log('启动Wifi...');
|
||||
await that.startWifiInWeChat();
|
||||
} catch (e) {
|
||||
console.log('启动Wifi异常', e);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('deviceAdd.afterNetwork')
|
||||
});
|
||||
}
|
||||
that.mstep = 3;
|
||||
},
|
||||
// 多设备配网失败
|
||||
failConfigInWeChat(wifi, processTimer) {
|
||||
clearInterval(processTimer);
|
||||
wifi.type = 'error';
|
||||
wifi.text = this.$tt('deviceAdd.failNetwork');
|
||||
},
|
||||
// 微信小程序连接wifi
|
||||
connectWifiInWeChat(ssid, password) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.connectWifi({
|
||||
SSID: ssid,
|
||||
password: password,
|
||||
success: res => {
|
||||
resolve(true);
|
||||
},
|
||||
fail: res => {
|
||||
reject(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// 微信小程序停止wifi
|
||||
stopWifiInWeChat() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.stopWifi({
|
||||
success: res => {
|
||||
resolve(true);
|
||||
},
|
||||
fail: res => {
|
||||
reject(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// 微信小程序启动wifi
|
||||
startWifiInWeChat() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.startWifi({
|
||||
success: res => {
|
||||
resolve(true);
|
||||
},
|
||||
fail: res => {
|
||||
reject(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// 获取连接的wifi信息
|
||||
getConnectedWifiInWeChart() {
|
||||
let that = this;
|
||||
wx.getConnectedWifi({
|
||||
success: res => {
|
||||
console.log('success wifi info ', res);
|
||||
if (that.form.SSID == '') {
|
||||
that.form.SSID = res.wifi.SSID;
|
||||
}
|
||||
},
|
||||
fail: res => {
|
||||
console.log(res);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('deviceAdd.phoneTurnOn')
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取wifi列表
|
||||
getWifiListInWeChart() {
|
||||
let that = this;
|
||||
that.wifiList = [];
|
||||
wx.getWifiList({
|
||||
// 请求获取wifi列表
|
||||
success: function(e) {
|
||||
wx.onGetWifiList(function(res) {
|
||||
console.log('wifi列表');
|
||||
console.log(res.wifiList);
|
||||
let platform = uni.getSystemInfoSync().platform;
|
||||
let tmpList = [];
|
||||
res.wifiList.map(item => {
|
||||
if (item.SSID.length > 0) {
|
||||
// 设置热点的配网进度默认为0,未选中状态,进度条默认蓝色
|
||||
item.process = 0;
|
||||
item.checked = false;
|
||||
item.type = 'primary';
|
||||
item.text = this.$tt('deviceAdd.prepare');
|
||||
item.signalStrength = platform === 'ios' ? Math.round(item
|
||||
.signalStrength * 100) : item
|
||||
.signalStrength //ios signalStrength 值是0-1, android 值是1-100
|
||||
tmpList.push(item);
|
||||
}
|
||||
});
|
||||
that.wifiList = tmpList; // 存放wifi列表
|
||||
console.log('列表数据', that.wifiList);
|
||||
that.loading = false;
|
||||
// 配网未开始
|
||||
that.mstep = 0;
|
||||
});
|
||||
},
|
||||
fail: res => {
|
||||
console.log(res);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('deviceAdd.phoneTurnOn')
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 更新WIFI列表
|
||||
updateWfifiListInWeChat() {
|
||||
// 清空选中的wifi
|
||||
this.selectedWifiList = [];
|
||||
this.updateDisable = true;
|
||||
this.initInWeChat();
|
||||
setTimeout(res => {
|
||||
this.updateDisable = false;
|
||||
}, 6000);
|
||||
},
|
||||
// 小程序初始化Wifi模块
|
||||
initInWeChat() {
|
||||
let that = this;
|
||||
that.loading = true;
|
||||
uni.getLocation({
|
||||
//授权定位后才能获取wifi
|
||||
type: 'wgs84',
|
||||
success: function(res) {
|
||||
that.wifiList = [];
|
||||
wx.startWifi({
|
||||
success(res) {
|
||||
console.log('启动Wifi模块成功' + res.errMsg);
|
||||
// 获取连接的wifi信息
|
||||
that.getConnectedWifiInWeChart();
|
||||
// 获取wifi列表
|
||||
that.getWifiListInWeChart();
|
||||
},
|
||||
fail: res => {
|
||||
that.loading = false;
|
||||
console.log('启动Wifi模块失败' + res);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('deviceAdd.miniProgrem')
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
fail: function(error) {
|
||||
console.log(error, 'xxx');
|
||||
that.loading = false;
|
||||
uni.showToast({
|
||||
title: this.$tt('deviceAdd.miniProgrem'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
// background: #eef3f7;
|
||||
background: linear-gradient(30deg, #46e9a9 30%, #007aff 70%);
|
||||
background-size: 100% 100%;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
margin: 10px;
|
||||
margin-bottom: 15px;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
</style>
|
189
pagesA/list/home/deviceRelate.vue
Normal file
189
pagesA/list/home/deviceRelate.vue
Normal file
@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<page-meta><navigation-bar :title="$tt('linkDevice.linkDevice')" title-align="center" background-color="#007AFF" /></page-meta>
|
||||
<view class="container">
|
||||
<u--form labelWidth="100" labelAlign="left">
|
||||
<view class="card" v-for="(item, index) in form.deviceNumberAndProductIds" :key="index">
|
||||
<view style="display:flex;justify-content: flex-start;">
|
||||
<u-button type="error" :text="$tt('common.delete')" icon="minus-circle" size="small" customStyle="width:70px;margin:0;" v-if="index != 0" @click="removeDeviceNumber(index)"></u-button>
|
||||
<u-button type="primary" :text="$tt('common.add')" icon="plus-circle" size="small" customStyle="width:70px;margin:0;" v-if="index == 0" @click="addDeviceNumber"></u-button>
|
||||
<u-button
|
||||
type="success"
|
||||
:text="$tt('linkDevice.scan')"
|
||||
icon="scan"
|
||||
size="small"
|
||||
customStyle="margin-left:20px;width:90px;"
|
||||
@click="openScan(form.deviceNumberAndProductIds[index])"
|
||||
></u-button>
|
||||
</view>
|
||||
<u-form-item :label="$tt('linkDevice.deviceNum')"><u-input v-model="form.deviceNumberAndProductIds[index].deviceNumber" :placeholder="$tt('linkDevice.inputDeviceId')"></u-input></u-form-item>
|
||||
<u-form-item :label="$tt('linkDevice.productNum')"><u-input v-model="form.deviceNumberAndProductIds[index].productId" :placeholder="$tt('linkDevice.inputProductId')"></u-input></u-form-item>
|
||||
<u-form-item :label="$tt('linkDevice.productName')"><u-input v-model="form.deviceNumberAndProductIds[index].productName" disabled :placeholder="$tt('linkDevice.product')"></u-input></u-form-item>
|
||||
</view>
|
||||
</u--form>
|
||||
|
||||
<view class="card">
|
||||
<view style="display:flex;">
|
||||
<u-button type="warning" @click="goBack" customStyle="margin:10px;">{{$tt('common.back')}}</u-button>
|
||||
<u-button type="primary" @click="submitForm" customStyle="margin:10px;">{{$tt('common.save')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-modal :show="modal.show" :content="modal.content" @confirm="confirm"></u-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { deviceRelateUser } from '@/apis/modules/device';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 模态窗
|
||||
modal: {
|
||||
show: false,
|
||||
content: ''
|
||||
},
|
||||
// 表单内容
|
||||
form: {
|
||||
deviceNumberAndProductIds: [
|
||||
{
|
||||
deviceNumber: '',
|
||||
productId: null,
|
||||
productName: ''
|
||||
}
|
||||
],
|
||||
userId: null
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
//获取登录用户信息
|
||||
if (this.profile == null) {
|
||||
this.getProfile();
|
||||
} else {
|
||||
this.form.userId = this.profile.userId;
|
||||
}
|
||||
},
|
||||
onUnload() {},
|
||||
methods: {
|
||||
// 获取登录用户信息
|
||||
getProfile() {
|
||||
// 调用用户信息接口
|
||||
this.$api.common
|
||||
.getProfile()
|
||||
.then(res => {
|
||||
//存储用户信息,TODO 需要调用一次,不然其他页面调用返回空
|
||||
uni.$u.vuex('profile', res.data);
|
||||
this.profile;
|
||||
this.form.userId = this.profile.userId;
|
||||
})
|
||||
.catch(err => {
|
||||
this.$u.toast(err.msg);
|
||||
});
|
||||
},
|
||||
// 添加一项
|
||||
addDeviceNumber() {
|
||||
this.form.deviceNumberAndProductIds.push({ deviceNumber: '', productId: null, productName: '' });
|
||||
},
|
||||
// 移除一项
|
||||
removeDeviceNumber(index) {
|
||||
this.form.deviceNumberAndProductIds.splice(index, 1);
|
||||
},
|
||||
// 返回
|
||||
goBack() {
|
||||
getApp().globalData.operate = 'operate';
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
},
|
||||
// 模态窗取消
|
||||
confirm() {
|
||||
this.modal = {
|
||||
show: false,
|
||||
content: ''
|
||||
};
|
||||
},
|
||||
// 全部保存
|
||||
submitForm() {
|
||||
if (this.form.userId == null || this.form.userId == 0) {
|
||||
uni.showToast({ icon: 'none', title: this.$tt('linkDevice.userName') });
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < this.form.deviceNumberAndProductIds.length; i++) {
|
||||
if (this.form.deviceNumberAndProductIds[i].deviceNumber == '') {
|
||||
uni.showToast({ icon: 'none', title: this.$tt('linkDevice.deviceEmpty') });
|
||||
return;
|
||||
}
|
||||
if (this.form.deviceNumberAndProductIds[i].productId == null || this.form.deviceNumberAndProductIds[i].productId == 0) {
|
||||
uni.showToast({ icon: 'none', title: this.$tt('linkDevice.productIdEmpty') });
|
||||
return;
|
||||
}
|
||||
}
|
||||
deviceRelateUser(this.form).then(res => {
|
||||
if (res.code == 200) {
|
||||
uni.showToast({ icon: 'success', title: this.$tt('common.saveSuccessful') });
|
||||
setTimeout(() => {
|
||||
this.goBack();
|
||||
}, 1000);
|
||||
} else {
|
||||
this.modal = {
|
||||
show: true,
|
||||
content:res.msg
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
// 扫码
|
||||
openScan(item) {
|
||||
// #ifndef MP-WEIXIN || APP-PLUS
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('user.scanning')
|
||||
});
|
||||
return;
|
||||
// #endif
|
||||
|
||||
// 允许从相机和相册扫码
|
||||
uni.scanCode({
|
||||
success: res => {
|
||||
console.log('条码类型:' + res.scanType);
|
||||
console.log('条码内容:' + res.result);
|
||||
if (res.result.substr(0, 1) != '{') {
|
||||
console.log('坑点:解析二维码后第一个位置包含一个特殊字符,大部分编译器和调试工具识别不了这个特殊字符');
|
||||
res.result = res.result.substring(1);
|
||||
}
|
||||
// 解析JSON
|
||||
try {
|
||||
let json = JSON.parse(res.result);
|
||||
// type=1 代表扫码关联设备
|
||||
if (json.type == 1) {
|
||||
item.deviceNumber = json.deviceNumber;
|
||||
item.productId = json.productId;
|
||||
item.productName = json.productName;
|
||||
return;
|
||||
}
|
||||
uni.showToast({ icon: 'none', title: this.$tt('linlDevice.format') });
|
||||
} catch (error) {
|
||||
uni.showToast({ icon: 'none', title: this.$tt('linlDevice.format') });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #eef3f7;
|
||||
}
|
||||
.container {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.card {
|
||||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
131
pagesA/list/trend/categoryTrend.vue
Normal file
131
pagesA/list/trend/categoryTrend.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<page-meta><navigation-bar :title="$tt('navBar.classification')" title-align="center" background-color="#007AFF" /></page-meta>
|
||||
<view class="container">
|
||||
<view style="margin-top: 20px">
|
||||
<view><u--text prefixIcon="grid-fill" iconStyle="font-size: 16px;color:#606266" :text="categoryName" bold color="#606266"></u--text></view>
|
||||
</view>
|
||||
<view v-for="(item, index) in list" :key="index">
|
||||
<u-line margin="10px 0 15px 0"></u-line>
|
||||
<view style="display: flex; align-items: center">
|
||||
<view style="margin-right: 10px">
|
||||
<u--image :src="item.imgUrl" @click="gotoDetail(item.newsId)" mode="aspectFill" radius="5" width="120" height="80">
|
||||
<template v-slot:loading>
|
||||
<u-loading-icon></u-loading-icon>
|
||||
</template>
|
||||
</u--image>
|
||||
</view>
|
||||
<view>
|
||||
<u--text lines="2" lineHeight="24" size="16" @click="gotoDetail(item.newsId)" :text="item.title"></u--text>
|
||||
<view style="display: flex; align-items: center; margin-top: 5px">
|
||||
<view style="margin-right: 10px">
|
||||
<u--text
|
||||
prefixIcon="account"
|
||||
iconStyle="color:#606266;font-size:14px;margin-right:3px;"
|
||||
size="12"
|
||||
color="#606266"
|
||||
mode="name"
|
||||
:text="item.author"
|
||||
></u--text>
|
||||
</view>
|
||||
<view>
|
||||
<u--text
|
||||
prefixIcon="calendar"
|
||||
iconStyle="color:#606266;font-size:14px;margin-right:3px;"
|
||||
size="12"
|
||||
color="#606266"
|
||||
mode="date"
|
||||
:text="item.createTime"
|
||||
></u--text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-empty mode="list" :show="total == 0" marginTop="30"></u-empty>
|
||||
<u-loadmore :status="status" v-if="total>queryParams.pageSize" marginTop="20" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listTrend } from '@/apis/modules/trend';
|
||||
import projectConfig from '@/env.config.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 刷新和加载相关
|
||||
status: 'nomore',
|
||||
list: [],
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
status:1,
|
||||
categoryId: null,
|
||||
},
|
||||
categoryName: ''
|
||||
};
|
||||
},
|
||||
//option为object类型,会序列化上个页面传递的参数
|
||||
onLoad: function(option) {
|
||||
this.queryParams.categoryId = option.categoryId;
|
||||
this.categoryName = option.categoryName;
|
||||
},
|
||||
created() {
|
||||
this.getlistTrend();
|
||||
},
|
||||
methods: {
|
||||
gotoDetail(newsId) {
|
||||
uni.navigateTo({
|
||||
url: '/pagesA/list/trend/trendDetail?newsId=' + newsId
|
||||
});
|
||||
},
|
||||
//列表
|
||||
getlistTrend() {
|
||||
listTrend(this.queryParams).then(res => {
|
||||
this.queryParams.pageNum++;
|
||||
let list = null;
|
||||
let rows = res.rows
|
||||
.map((item, i) => {
|
||||
item.imgUrl = projectConfig.baseUrl + item.imgUrl;
|
||||
return item;
|
||||
})
|
||||
// .filter(item => item.categoryId == this.queryParams.categoryId);
|
||||
if (this.queryParams.pageNum == 1) {
|
||||
this.list = rows;
|
||||
} else {
|
||||
this.list = this.list.concat(rows);
|
||||
}
|
||||
this.total = res.total;
|
||||
this.loading = false;
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh() {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// Http获取列表
|
||||
this.getlistTrend();
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom() {
|
||||
this.status = 'loading';
|
||||
this.queryParams.pageNum = this.queryParams.pageNum + 1;
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize > this.total) {
|
||||
this.status = 'nomore';
|
||||
} else {
|
||||
this.getlistTrend();
|
||||
this.status = 'loading';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
padding: 10px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
</style>
|
116
pagesA/list/trend/trendDetail.vue
Normal file
116
pagesA/list/trend/trendDetail.vue
Normal file
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<page-meta><navigation-bar :title="$tt('navBar.classification')" title-align="center"
|
||||
background-color="#007AFF" /></page-meta>
|
||||
<view class="container">
|
||||
<u--text :text="trendObject.title" size="18" align="center" bold
|
||||
customStyle="margin-bottom:10px;color:#666;line-height:30px;"></u--text>
|
||||
<view style="display:flex;margin-top:6px;justify-content:flex-start;">
|
||||
<view style="margin-right:50px;">
|
||||
<u--text prefixIcon="calendar" iconStyle="color:#606266;font-size:14px;" size="12" color="#606266"
|
||||
mode="date" align="left" :text="trendObject.createTime"></u--text>
|
||||
</view>
|
||||
<view>
|
||||
<u--text prefixIcon="account" iconStyle="color:#606266;font-size:14px;margin-right:3px;" size="12"
|
||||
color="#606266" mode="name" :text="trendObject.author"></u--text>
|
||||
</view>
|
||||
</view>
|
||||
<u-line margin="5px 0 10px"></u-line>
|
||||
<view class="content"><rich-text :nodes="trendObject.content"></rich-text></view>
|
||||
<view><u--text v-if="isShow" text="暂无内容..." customStyle="margin:auto;"></u--text></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getNewsDetail } from '@/apis/modules/trend';
|
||||
import projectConfig from '@/env.config.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
newsId: '',
|
||||
trendObject: {
|
||||
createTime: '2022-01-01',
|
||||
author: ''
|
||||
},
|
||||
isShow: false
|
||||
};
|
||||
},
|
||||
onLoad: function (option) {
|
||||
this.newsId = option.newsId;
|
||||
},
|
||||
onShow () {
|
||||
this.getTrendFunc();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 转换富文本的图片最大为100%
|
||||
* 转换行内样式的双引号问题
|
||||
*/
|
||||
formatRichText (html) {
|
||||
//控制小程序中图片大小
|
||||
let that = this;
|
||||
let baseurl = projectConfig.baseUrl.replace(/\/prod-api\//gi, '');
|
||||
console.log('baseurl', baseurl);
|
||||
// 双引号改为单引号
|
||||
let newContent = html.replace(/<img[^>]*>/gi, function (match, capture) {
|
||||
match = match.replace(/style="[^"]+"/gi, '').replace(/style='[^']+'/gi, '');
|
||||
match = match.replace(/width="[^"]+"/gi, '').replace(/width='[^']+'/gi, '');
|
||||
match = match.replace(/height="[^"]+"/gi, '').replace(/height='[^']+'/gi, '');
|
||||
return match;
|
||||
});
|
||||
// 限制元素最大宽度
|
||||
newContent = newContent.replace(/style="[^"]+"/gi, function (match, capture) {
|
||||
match = match.replace(/width:[^;]+;/gi, 'max-width:100%;').replace(/width:[^;]+;/gi,
|
||||
'max-width:100%;');
|
||||
return match;
|
||||
});
|
||||
// 删除换行
|
||||
newContent = newContent.replace(/<br[^>]*\/>/gi, '');
|
||||
newContent = newContent.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function (matchs, capture) {
|
||||
return "<img style='max-width:100%;height:auto;border-radius:5px; display:block' src='" +
|
||||
baseurl + capture + "'/>";
|
||||
});
|
||||
|
||||
// 替换为单引号
|
||||
var stylePattern = /style="[^=>]*"[\s+\w+=|>]/g;
|
||||
return newContent.replace(stylePattern, function (matches) {
|
||||
return matches.replace(/<["]*>/g, "'");
|
||||
});
|
||||
},
|
||||
getTrendFunc () {
|
||||
const params = { newsId: this.newsId };
|
||||
getNewsDetail(params).then(res => {
|
||||
this.trendObject = res.data;
|
||||
this.trendObject.content = this.formatRichText(this.trendObject.content);
|
||||
console.log('content:', this.trendObject.content);
|
||||
this.trendObject.imgUrl = projectConfig.baseUrl + this.trendObject.imgUrl;
|
||||
if (JSON.stringify(this.trendObject) == '{}') {
|
||||
this.isShow = true;
|
||||
} else {
|
||||
this.isshow = false;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
padding: 15px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.content {
|
||||
line-height: 26px;
|
||||
text-align: justify;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.content img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
</style>
|
26
pagesA/public/player/index.vue
Normal file
26
pagesA/public/player/index.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<view>
|
||||
123
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
props: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
839
pagesA/scene/detail.vue
Normal file
839
pagesA/scene/detail.vue
Normal file
@ -0,0 +1,839 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.sceneDetail')" background-color="#007AFF">
|
||||
</navigation-bar>
|
||||
</page-meta>
|
||||
<view class="scene-detail-wrap">
|
||||
<view class="name-wrap">
|
||||
<u--input clearable border="false" :placeholder="$tt('sceneDetail.inputName')"
|
||||
:customStyle="{ backgroundColor: '#fff', padding: '12px 30rpx', borderRadius: '10rpx' }" fontSize="16"
|
||||
v-model="sceneData.sceneName"></u--input>
|
||||
</view>
|
||||
<view class="container-wrap">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell size="large" :border="false" :title="$tt('sceneDetail.sceneState')">
|
||||
<u-switch v-model="sceneData.enable" slot="value" :activeValue="1" :inactiveValue="2"
|
||||
activeColor="#3C9CFF" inactiveColor="#dbdbdb" size="18"></u-switch>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
<view class="condition-wrap">
|
||||
<view class="title-wrap">
|
||||
<view class="left-wrap">
|
||||
<u-row>
|
||||
<u-col span="2.5">
|
||||
<text class="title">{{$tt('sceneDetail.trigger')}}</text>
|
||||
</u-col>
|
||||
<u--text suffixIcon="arrow-down" iconStyle="font-size: 10px; margin-left: 10rpx"
|
||||
:text="getCondName(sceneData.cond)" color="#868686" size="12"
|
||||
@click="isCond = true"></u--text>
|
||||
</u-row>
|
||||
<u--form labelWidth="60" :labelStyle="{ color: '#868686',fontSize: '12px' }"
|
||||
style="margin:10rpx 0 0">
|
||||
<u-form-item label="延时匹配 :">
|
||||
<view style="width:240rpx">
|
||||
<u-input placeholder="请输入延时时间" clearable @input="onInput" type="number"
|
||||
v-model="checkDelay" placeholder-style="font-size:10px;width:80px;"
|
||||
:maxlength="3" style="height:30rpx;">
|
||||
<u--text text="秒钟" slot="suffix" type="tips" margin="0 0 0 5px"
|
||||
size="10"></u--text>
|
||||
</u-input>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</u--form>
|
||||
</view>
|
||||
<view class="right-wrap">
|
||||
<u-icon name="plus-circle-fill" size="24" color='#3c9cff' bold
|
||||
@click="handleAddTrigger"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="sceneData.triggers.length === 0" class="empty-cell" @click="handleAddTrigger">
|
||||
<view class="text">{{$tt('sceneDetail.addConditions')}}</view>
|
||||
</view>
|
||||
<view v-else class="swipe-action-wrap">
|
||||
<u-swipe-action autoClose>
|
||||
<u-cell-group :border="false">
|
||||
<!-- 设置key为不同id是为了解决uview的SwipeAction 滑动单元格 关闭事件 ,删除当前项,下一项自动弹出删除选择bug -->
|
||||
<view class="action-item-wrap" v-for="(item,index) in sceneData.triggers"
|
||||
:key="getKey(index, item)">
|
||||
<u-swipe-action-item :options="swipeOptions"
|
||||
@click="handleDeleteSceneItem('trigger', index)">
|
||||
<u-cell :border="false" isLink
|
||||
@click="handleEditSceneItem({type:'trigger', item:item, index:index})">
|
||||
<view slot="title" class="slot-title">
|
||||
<u--image :src="getImageUrl(item)" radius="4" width="33"
|
||||
height="33"></u--image>
|
||||
<view class="cell-text">
|
||||
<view class="title">
|
||||
{{item.productName}}{{item.cronExpression ? 'CRON' : ''}}
|
||||
</view>
|
||||
<view v-if="item.deviceCount > 0" class="des">
|
||||
{{$tt('sceneDetail.deviceNumber')}}:{{item.deviceCount}}
|
||||
个
|
||||
</view>
|
||||
<view class="des">
|
||||
<template v-if="item.parentName && item.parentName !== item.name">
|
||||
{{item.parentName}} >>
|
||||
</template>
|
||||
<template v-if="item.arrayIndexName">
|
||||
{{item.arrayIndexName}} >>
|
||||
</template>
|
||||
{{item.name}}
|
||||
{{item.value !== '' ? item.operator ? `${item.operator} ${item.value}` : item.value : ''}}
|
||||
<!-- 定时触发事件 -->
|
||||
{{item.cronExpression ? item.cronExpression : ''}}
|
||||
<!-- 触发条件为上下线设备 -->
|
||||
{{item.type === 5 ? $tt('sceneDetail.deviceOnline') : item.type === 6 ? $tt('sceneDetail.Equipment') : ''}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-cell>
|
||||
</u-swipe-action-item>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</u-swipe-action>
|
||||
</view>
|
||||
</view>
|
||||
<view class="execute-wrap">
|
||||
<view class="title-wrap">
|
||||
<view class="left-wrap">
|
||||
<text class="title">{{$tt('sceneDetail.action')}}</text>
|
||||
</view>
|
||||
<view class="right-wrap">
|
||||
<u-icon name="plus-circle-fill" size="24" color='#3c9cff' bold
|
||||
@click="handleAddAction"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="sceneData.actions.length === 0" class="empty-cell" @click="handleAddAction">
|
||||
<view class="text">{{$tt('sceneDetail.addTask')}}</view>
|
||||
</view>
|
||||
<view v-else class="swipe-action-wrap">
|
||||
<u-swipe-action autoClose>
|
||||
<u-cell-group :border="false">
|
||||
<view class="action-item-wrap" v-for="(item,index) in sceneData.actions"
|
||||
:key="getKey(index, item)">
|
||||
<!-- 设置key为不同id是为了解决uview的SwipeAction 滑动单元格 关闭事件 ,删除当前项,下一项自动弹出删除选择bug -->
|
||||
<u-swipe-action-item :options="swipeOptions"
|
||||
@click="handleDeleteSceneItem('action', index)">
|
||||
<u-cell :border="false" isLink
|
||||
@click="handleEditSceneItem({type:'action', item:item, index:index})">
|
||||
<view slot="title" class="slot-title">
|
||||
<u--image :src="getImageUrl(item)" radius="4" width="33"
|
||||
height="33"></u--image>
|
||||
<view class="cell-text">
|
||||
<!-- 告警执行 -->
|
||||
<view class="title">
|
||||
{{item.productName}}{{item.source ? $tt('sceneDetail.alertExecute') : ''}}
|
||||
</view>
|
||||
<view v-if="item.deviceCount > 0" class="des">
|
||||
{{$tt('sceneDetail.deviceNumber')}}:{{item.deviceCount}}
|
||||
个
|
||||
</view>
|
||||
<view class="des">
|
||||
<template v-if="item.parentName && item.parentName !== item.name">
|
||||
{{item.parentName}} >>
|
||||
</template>
|
||||
<template v-if="item.arrayIndexName">
|
||||
{{item.arrayIndexName}} >>
|
||||
</template>
|
||||
{{item.name}}
|
||||
{{item.value !== '' ? item.value : ''}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-cell>
|
||||
</u-swipe-action-item>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</u-swipe-action>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell size="large" :border="false" :title="$tt('sceneDetail.moreSetting')" isLink
|
||||
@click="handleMore"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<view class="btn-save-wrap">
|
||||
<u-button type="primary" :customStyle="{ height: '96rpx' }" size="normal" :text="$tt('common.save')"
|
||||
@click="handleSave"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-popup :show="isCond" :round="5" mode="bottom" :closeOnClickOverlay="true" @close="isCond = false">
|
||||
<view class="cond-popup-wrap">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('scene.anyCondition')" :name="1"
|
||||
@click="handleConfirmCond"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('scene.allCondition')" :name="2"
|
||||
@click="handleConfirmCond"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('scene.notConditions')" :name="3"
|
||||
@click="handleConfirmCond"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('common.cancel')" name="cancel" :border="false"
|
||||
@click="handleConfirmCond"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-popup :show="isMore" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="true"
|
||||
@close="isMore = false">
|
||||
<view class="more-popup-wrap">
|
||||
<view class="remark">{{$tt('sceneDetail.moreSetting')}}</view>
|
||||
<view class="form-wrap">
|
||||
<u--form :model="more" labelPosition="left" labelWidth="80">
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item :label="$tt('sceneDetail.silentTime')" prop="more.silentPeriod">
|
||||
<u-input v-model="more.silentPeriod" border="none" type="number" inputAlign="right"
|
||||
:placeholder="$tt('sceneDetail.inputTime')" clearable>
|
||||
<u--text :text="$tt('sceneDetail.minute')" slot="suffix" margin="0 0 0 6rpx"
|
||||
type="tips"></u--text>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item :label="$tt('sceneDetail.executionMethod')" prop="more.executeMode">
|
||||
<template slot="right">
|
||||
<u-radio-group v-model="more.executeMode">
|
||||
<u-radio :label="$tt('sceneDetail.serial')" :name="1"
|
||||
:customStyle="{marginRight: '30rpx'}">
|
||||
</u-radio>
|
||||
<u-radio :label="$tt('sceneDetail.parallel')" :name="2">
|
||||
</u-radio>
|
||||
</u-radio-group>
|
||||
</template>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item :label="$tt('sceneDetail.delayed ')" prop="more.executeDelay">
|
||||
<u-input v-model="more.executeDelay" border="none" type="number" inputAlign="right"
|
||||
:placeholder="$tt('sceneDetail.inputExtensionTime')" clearable>
|
||||
<u--text :text="$tt('sceneDetail.seconds')" slot="suffix" margin="0 0 0 6rpx"
|
||||
type="tips"></u--text>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u--form>
|
||||
</view>
|
||||
<u-button type="primary" @click="handleConfirmMore">{{$tt('sceneDetail.complete')}}</u-button>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<u-popup :show="isTrigger" :round="5" mode="bottom" :closeOnClickOverlay="true" @close="isTrigger = false">
|
||||
<view class="trigger-popup-wrap">
|
||||
<view class="title">{{$tt('sceneDetail.addConditions')}}</view>
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.deviceTriggered')" icon="/static/scene/device.png"
|
||||
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
|
||||
@click="goToDeviceList('trigger')"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap" v-if="deptId!=null">
|
||||
<u-cell :title="$tt('sceneDetail.productTriggering')" icon="/static/scene/product.png"
|
||||
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
|
||||
@click="goToProductList('trigger')"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.timedTrigger')" icon="/static/scene/timing.png"
|
||||
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
|
||||
@click="goToTimingList('trigger')"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<u-popup :show="isAction" :round="5" mode="bottom" :closeOnClickOverlay="true" @close="isAction = false">
|
||||
<view class="trigger-popup-wrap">
|
||||
<view class="title">{{$tt('sceneDetail.addTask')}}</view>
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.deviceExecution')" icon="/static/scene/device.png"
|
||||
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
|
||||
@click="goToDeviceList('action')"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap" v-if="deptId!=null">
|
||||
<u-cell :title="$tt('sceneDetail.productExecution')" icon="/static/scene/product.png"
|
||||
:iconStyle="{ marginRight: '10rpx' }" :border="false" isLink
|
||||
@click="goToProductList('action')"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.alertExecute')" icon="/static/scene/alarm.png"
|
||||
:iconStyle="{ marginRight: '10rpx' }" :border="false"
|
||||
@click="goToWarningList('action')" isLink></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getScene,
|
||||
addScene,
|
||||
updateScene
|
||||
} from '@/apis/modules/scene.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
sceneId: null,
|
||||
isCond: false,
|
||||
isMore: false,
|
||||
isTrigger: false,
|
||||
isAction: false,
|
||||
deptId: null,
|
||||
// 更多设置
|
||||
more: {
|
||||
silentPeriod: 0,
|
||||
executeMode: 1,
|
||||
executeDelay: 0
|
||||
},
|
||||
swipeOptions: [{
|
||||
text: this.$tt('common.delete'),
|
||||
style: {
|
||||
backgroundColor: '#f56c6c'
|
||||
}
|
||||
}],
|
||||
checkDelay:0,
|
||||
sceneData: {
|
||||
triggers: [],
|
||||
actions: [],
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
this.deptId = this.profile.deptId;
|
||||
this.sceneId = Number(option.sceneId);
|
||||
if (this.sceneId) {
|
||||
this.getDetail();
|
||||
} else {
|
||||
this.sceneData = uni.getStorageSync('sceneData');
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
const callback = uni.getStorageSync('callback');
|
||||
if (callback) {
|
||||
this.sceneData = uni.getStorageSync('sceneData');
|
||||
}
|
||||
},
|
||||
onHide() {
|
||||
// 页面隐藏时关闭弹框
|
||||
this.isTrigger = false;
|
||||
this.isAction = false;
|
||||
// 保存当前页面数据
|
||||
uni.setStorageSync('sceneData', this.sceneData);
|
||||
},
|
||||
methods: {
|
||||
// 获取一键任务详情
|
||||
getDetail() {
|
||||
getScene(this.sceneId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.sceneData = {
|
||||
...res.data
|
||||
};
|
||||
uni.setStorageSync('sceneData', this.sceneData);
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log('场景详情:', err);
|
||||
})
|
||||
},
|
||||
// 获取条件
|
||||
getCondName(cond) {
|
||||
if (cond === 1) {
|
||||
return this.$tt('scene.anyCondition')
|
||||
} else if (cond === 2) {
|
||||
return this.$tt('scene.allCondition')
|
||||
} else if (cond === 3) {
|
||||
return this.$tt('scene.notConditions')
|
||||
} else {
|
||||
return this.$tt('scene.unknown')
|
||||
}
|
||||
},
|
||||
//判断输入延时匹配条件
|
||||
onInput(event) {
|
||||
let value = event;
|
||||
if (value > 600) {
|
||||
this.checkDelay = '600';
|
||||
} else if (value < 0) {
|
||||
this.checkDelay = '0';
|
||||
}
|
||||
this.sceneData.checkDelay=this.checkDelay;
|
||||
},
|
||||
// 确认选择条件
|
||||
handleConfirmCond({
|
||||
name
|
||||
}) {
|
||||
if (name !== 'cancel') {
|
||||
this.sceneData.cond = name;
|
||||
}
|
||||
this.isCond = false;
|
||||
},
|
||||
// 更多设置
|
||||
handleMore() {
|
||||
const sceneData = uni.getStorageSync('sceneData');
|
||||
const {
|
||||
silentPeriod,
|
||||
executeMode,
|
||||
executeDelay
|
||||
} = sceneData;
|
||||
this.more = {
|
||||
silentPeriod,
|
||||
executeMode,
|
||||
executeDelay
|
||||
};
|
||||
this.isMore = true;
|
||||
},
|
||||
// 确认选择更多
|
||||
handleConfirmMore() {
|
||||
const {
|
||||
silentPeriod,
|
||||
executeMode,
|
||||
executeDelay
|
||||
} = this.more;
|
||||
let sceneData = uni.getStorageSync('sceneData');
|
||||
sceneData = {
|
||||
...sceneData,
|
||||
silentPeriod,
|
||||
executeMode,
|
||||
executeDelay
|
||||
};
|
||||
uni.setStorageSync('sceneData', sceneData);
|
||||
this.sceneData = {
|
||||
...sceneData
|
||||
};
|
||||
this.isMore = false;
|
||||
},
|
||||
// 添加条件触发器
|
||||
handleAddTrigger() {
|
||||
this.isTrigger = true;
|
||||
},
|
||||
// 添加执行动作
|
||||
handleAddAction() {
|
||||
this.isAction = true;
|
||||
},
|
||||
// 设备触发,租户
|
||||
goToDeviceList(type) {
|
||||
this.setSourceData(type, 1);
|
||||
uni.$u.route('/pagesA/scene/product/index', {
|
||||
type: type
|
||||
});
|
||||
},
|
||||
// 设备触发,终端用户
|
||||
goToDeviceListTerminal(type) {
|
||||
this.setSourceData(type, 1);
|
||||
uni.$u.route('/pagesA/scene/product/device', {
|
||||
type: type
|
||||
});
|
||||
},
|
||||
// 产品触发
|
||||
goToProductList(type) {
|
||||
this.setSourceData(type, 3);
|
||||
uni.$u.route('/pagesA/scene/product/index', {
|
||||
type: type
|
||||
});
|
||||
},
|
||||
// 定时
|
||||
goToTimingList(type) {
|
||||
this.setSourceData(type, 2);
|
||||
uni.$u.route('/pagesA/scene/timing/index', {
|
||||
type: type
|
||||
});
|
||||
},
|
||||
// 告警列表
|
||||
goToWarningList(type) {
|
||||
this.setSourceData(type, 4);
|
||||
uni.$u.route('/pagesA/scene/warning/index');
|
||||
},
|
||||
// 获取设备图片
|
||||
getImageUrl(item) {
|
||||
if (item.source === 1) {
|
||||
return '/static/scene/device.png';
|
||||
} else if (item.source === 2) {
|
||||
return '/static/scene/timing.png';
|
||||
} else if (item.source === 3) {
|
||||
return '/static/scene/product.png';
|
||||
} else if (item.source === 4) {
|
||||
return '/static/scene/alarm.png';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
// 删除触发、执行项
|
||||
handleDeleteSceneItem(type, index) {
|
||||
const {
|
||||
triggers,
|
||||
actions
|
||||
} = this.sceneData;
|
||||
if (type === 'trigger') {
|
||||
const list = triggers.filter((item, i) => i !== index);
|
||||
this.sceneData = {
|
||||
...this.sceneData,
|
||||
triggers: [...list]
|
||||
};
|
||||
} else {
|
||||
const list = actions.filter((item, i) => i !== index);
|
||||
this.sceneData = {
|
||||
...this.sceneData,
|
||||
actions: [...list]
|
||||
};
|
||||
}
|
||||
},
|
||||
// 编辑触发、执行项, 小程序转换问题,所以用对象传进来
|
||||
handleEditSceneItem(val) {
|
||||
const storage = uni.getStorageSync(val.type);
|
||||
if (storage) {
|
||||
uni.removeStorageSync(val.type);
|
||||
}
|
||||
uni.setStorageSync(val.type, val.item);
|
||||
uni.removeStorageSync('callback');
|
||||
if (val.item.type === 5 || val.item.type === 6) {
|
||||
if (val.item.source === 1) {
|
||||
uni.$u.route('/pagesA/scene/product/device', {
|
||||
type: val.type,
|
||||
editIndex: val.index
|
||||
});
|
||||
} else if (val.item.source === 3) {
|
||||
uni.$u.route('/pagesA/scene/product/index', {
|
||||
type: val.type,
|
||||
editIndex: val.index
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (val.item.source === 1 || val.item.source === 3) {
|
||||
uni.$u.route('/pagesA/scene/product/model', {
|
||||
type: val.type,
|
||||
editIndex: val.index
|
||||
});
|
||||
} else if (val.item.source === 2) {
|
||||
uni.$u.route('/pagesA/scene/timing/index', {
|
||||
type: val.type,
|
||||
editIndex: val.index
|
||||
});
|
||||
} else if (val.item.source === 4) {
|
||||
uni.$u.route('/pagesA/scene/warning/index', {
|
||||
editIndex: val.index
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
// 保存
|
||||
handleSave() {
|
||||
const {
|
||||
sceneId,
|
||||
sceneName,
|
||||
triggers,
|
||||
actions
|
||||
} = this.sceneData;
|
||||
this.sceneData.checkDelay=this.checkDelay;
|
||||
if (!sceneName) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('sceneDetail.sceneValidate'),
|
||||
});
|
||||
return
|
||||
};
|
||||
if (triggers.length === 0) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('sceneDetail.triggerValidate'),
|
||||
});
|
||||
return
|
||||
};
|
||||
if (actions.length === 0) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('sceneDetail.taskValidate'),
|
||||
});
|
||||
return
|
||||
};
|
||||
if (sceneId) {
|
||||
updateScene(this.sceneData).then(res => {
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('common.updateSuccessful'),
|
||||
});
|
||||
uni.reLaunch({
|
||||
url: '/pages/tabBar/scene/index'
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addScene(this.sceneData).then(res => {
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('common.addSuccessful'),
|
||||
});
|
||||
uni.reLaunch({
|
||||
url: '/pages/tabBar/scene/index'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
getKey(index, item) {
|
||||
return `${index}-${item.id}`
|
||||
},
|
||||
// 存储条件、动作来源
|
||||
setSourceData(type, source) {
|
||||
let trigger = {
|
||||
id: '',
|
||||
name: '',
|
||||
operator: '',
|
||||
value: '',
|
||||
parentId: '', // 父类id
|
||||
parentName: '', // 父类名称
|
||||
arrayIndex: '', // 索引
|
||||
arrayIndexName: '', // 索引名称
|
||||
productId: null,
|
||||
productName: '',
|
||||
deviceCount: 0, // 设备数量
|
||||
deviceNums: [], // 设备编号
|
||||
isAdvance: 0, // 自定义cron
|
||||
cronExpression: '', // cron表达式
|
||||
source: 1, // 1=设备,2=定时,3=产品
|
||||
type: 1, // 1=属性,2=功能,3=事件
|
||||
scriptPurpose: 2, // 脚本用途(1=数据流,2=触发器,3=执行动作)
|
||||
};
|
||||
let action = {
|
||||
id: '',
|
||||
name: '',
|
||||
value: '',
|
||||
parentId: '',
|
||||
parentName: '',
|
||||
arrayIndex: '',
|
||||
arrayIndexName: '',
|
||||
productId: '',
|
||||
productName: '',
|
||||
deviceCount: 0,
|
||||
deviceNums: [],
|
||||
source: 1,
|
||||
type: 1,
|
||||
scriptPurpose: 3,
|
||||
};
|
||||
const storage = uni.getStorageSync(type);
|
||||
if (storage) {
|
||||
uni.removeStorageSync(type);
|
||||
}
|
||||
if (type === 'trigger') {
|
||||
trigger = {
|
||||
...trigger,
|
||||
source
|
||||
};
|
||||
uni.setStorageSync('trigger', trigger);
|
||||
} else {
|
||||
action = {
|
||||
...action,
|
||||
source
|
||||
};
|
||||
uni.setStorageSync('action', action);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.scene-detail-wrap {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.name-wrap {
|
||||
margin: 30rpx;
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
padding-bottom: 140rpx;
|
||||
|
||||
.cell-group-wrap {
|
||||
margin: 30rpx;
|
||||
|
||||
.cell-wrap {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.condition-wrap,
|
||||
.execute-wrap {
|
||||
margin: 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding-bottom: 12rpx;
|
||||
|
||||
.title-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-wrap {}
|
||||
}
|
||||
|
||||
.empty-cell {
|
||||
padding: 30rpx;
|
||||
|
||||
.text {
|
||||
height: 92rpx;
|
||||
border: 2rpx dashed #8686862e;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.swipe-action-wrap {
|
||||
.action-item-wrap {
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 2rpx solid #eaeefb;
|
||||
}
|
||||
|
||||
.slot-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.cell-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 24rpx;
|
||||
min-height: 124rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.des {
|
||||
font-size: 28rpx;
|
||||
color: #868686;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-wrap {
|
||||
padding: 0 28rpx;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-save-wrap {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
padding: 30rpx 26rpx 40rpx;
|
||||
background: #eef3f7f7;
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.cond-popup-wrap {
|
||||
padding: 10rpx 0 20rpx;
|
||||
|
||||
.cell-group-wrap {
|
||||
background: #eef3f7;
|
||||
|
||||
.cell-wrap {
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
|
||||
&:last-child {
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-popup-wrap {
|
||||
margin: 30rpx;
|
||||
|
||||
.remark {
|
||||
color: #868686;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
margin: 30rpx 0;
|
||||
|
||||
.form-item-wrap {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 4rpx 30rpx;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trigger-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.cell-group-wrap {
|
||||
.cell-wrap {
|
||||
padding: 6rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
143
pagesA/scene/list.vue
Normal file
143
pagesA/scene/list.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.productList')" background-color="#007AFF">
|
||||
</navigation-bar>
|
||||
</page-meta>
|
||||
<view class="scene-list-wrap">
|
||||
<view class="container-wrap">
|
||||
<view class="cell-group">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in list" :key="index">
|
||||
<u-cell :title="item.sceneName" :border="false">
|
||||
<u-icon slot="right-icon" size="18" name="minus-circle-fill" color="#ff7e7e"
|
||||
@click="handleDelete(item)"></u-icon>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
|
||||
:nomoreText="$tt('scene.emptyData')" marginTop="20" />
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
|
||||
<u-modal :show="isDelete" showCancelButton :confirmText="$tt('common.delete')"
|
||||
@cancel="() => isDelete = false" :title="$tt('sceneDetail.tips')" :content="deleteContent"
|
||||
@confirm="handleConfirmDelete">
|
||||
</u-modal>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listScene, delScene } from '@/apis/modules/scene';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 16,
|
||||
},
|
||||
list: [], // 场景列表
|
||||
total: 0,
|
||||
loadmoreStatus: 'loadmore', // 刷新和加载相关
|
||||
isDelete: false,
|
||||
deleteId: '',
|
||||
deleteContent: '',
|
||||
};
|
||||
},
|
||||
onLoad () {
|
||||
this.getSceneDatas();
|
||||
},
|
||||
methods: {
|
||||
// 获取区设备列表
|
||||
getSceneDatas () {
|
||||
listScene(this.queryParams)
|
||||
.then(res => {
|
||||
const { rows, total } = res;
|
||||
this.list = this.list.concat(rows);
|
||||
this.total = total;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.getSceneDatas();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getSceneDatas();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 删除
|
||||
handleDelete (item) {
|
||||
this.isDelete = true;
|
||||
this.deleteId = item.sceneId;
|
||||
this.deleteContent = `确定删除“${item.sceneName}”?`
|
||||
},
|
||||
// 确认删除
|
||||
handleConfirmDelete () {
|
||||
delScene(this.deleteId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getSceneDatas();
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
this.isDelete = false;
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.scene-list-wrap {
|
||||
.container-wrap {
|
||||
.cell-group {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
margin: 20rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 6rpx;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
396
pagesA/scene/product/device.vue
Normal file
396
pagesA/scene/product/device.vue
Normal file
@ -0,0 +1,396 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.deviceList')" background-color="#007AFF">
|
||||
</navigation-bar>
|
||||
</page-meta>
|
||||
<view class="scene-device-wrap">
|
||||
<u-sticky>
|
||||
<view class="nav-bar">
|
||||
<view class="left-wrap">
|
||||
<u-icon v-if="!isSearch" name="search" size="22" @click="isSearch = true"></u-icon>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input v-else v-model="queryParams.deviceName" :placeholder="$tt('product.inputDeviceName')"
|
||||
shape="circle" @clear="handleClearSearch" clearable>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input v-else v-model="queryParams.deviceName" :placeholder="$tt('product.inputDeviceName')"
|
||||
shape="circle" @clear="handleClearSearch" clearable>
|
||||
<!-- #endif -->
|
||||
<template slot="prefix">
|
||||
<u-icon name="search" size="22" @click="isSearch = false"></u-icon>
|
||||
</template>
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('timing.search')" type="primary" shape="circle" size="mini"
|
||||
@click="handleSearch"></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="right-wrap" @click="handleNextStep">{{$tt('product.next')}}</view>
|
||||
</view>
|
||||
<view class="tab-wrap">
|
||||
<u-tabs :list="statusGroup" :scrollable="true" lineWidth="40" lineHeight="2" lineColor="transparent"
|
||||
:duration="100" :activeStyle="{ fontSize: '36rpx', color: '#3c9cff', fontWeight: 'bold' }"
|
||||
@change="handleStatusChange">
|
||||
</u-tabs>
|
||||
</view>
|
||||
</u-sticky>
|
||||
<view class="container-wrap">
|
||||
<u-checkbox-group v-model="devices">
|
||||
<view class="group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in list" :key="index">
|
||||
<u-cell :border="false">
|
||||
<view slot="title" class="slot-title">
|
||||
<u--image :src="getImageUrl(item)" radius="4" width="28" height="28"></u--image>
|
||||
<text class="cell-text">{{item.deviceName}}</text>
|
||||
</view>
|
||||
<view slot="value">
|
||||
<u-checkbox :name="item.serialNumber" iconSize="16"></u-checkbox>
|
||||
</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</u-checkbox-group>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
|
||||
:nomoreText="$tt('scene.emptyData')" marginTop="20" />
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
|
||||
<u-popup :show="isModel" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="true"
|
||||
@close="isModel = false">
|
||||
<view class="model-popup-wrap">
|
||||
<view class="title">{{$tt('product.selectThingModel')}}</view>
|
||||
<u-radio-group v-model="modelType">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('product.attribute')" :name="1" :border="false" isLink
|
||||
@click="goToModelList"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('product.function')" :name="2" :border="false" isLink
|
||||
@click="goToModelList"></u-cell>
|
||||
</view>
|
||||
<view v-if="type === 'trigger'" class="cell-wrap">
|
||||
<u-cell :title="$tt('product.event')" :name="3" :border="false" isLink
|
||||
@click="goToModelList"></u-cell>
|
||||
</view>
|
||||
<view v-if="type === 'trigger'" class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.deviceOnline')" :border="false">
|
||||
<view slot="value" class="u-slot-value">
|
||||
<u-radio shape="circle" :name="5"></u-radio>
|
||||
</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
<view v-if="type === 'trigger'" class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.Equipment')" :border="false">
|
||||
<view slot="value" class="u-slot-value">
|
||||
<u-radio shape="circle" :name="6"></u-radio>
|
||||
</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
<view v-if="type === 'trigger'" class="btn">
|
||||
<u-button type="primary" @click="handleConfirmMore">{{$tt('sceneDetail.complete')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listDeviceShort } from '@/apis/modules/device.js';
|
||||
import { navigateBackTo } from '@/utils/common.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
type: 'trigger',
|
||||
editIndex: null, // null 代表新增
|
||||
isSearch: true,
|
||||
isModel: false,
|
||||
modelType: null, // 物模类型
|
||||
// 状态列表
|
||||
statusGroup: [{
|
||||
id: null,
|
||||
name: this.$tt('home.all')
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: this.$tt('home.onLine')
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: this.$tt('home.disabled')
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: this.$tt('home.offline')
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: this.$tt('home.notActive')
|
||||
}
|
||||
],
|
||||
queryParams: {
|
||||
productId: null,
|
||||
pageNum: 1,
|
||||
pageSize: 11,
|
||||
deviceName: '',
|
||||
status: null
|
||||
},
|
||||
list: [], // 产品列表
|
||||
total: 0,
|
||||
loadmoreStatus: 'loadmore', // 刷新和加载相关
|
||||
devices: [], // 选中设备
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
this.type = option.type;
|
||||
const trigger = uni.getStorageSync('trigger');
|
||||
const action = uni.getStorageSync('action');
|
||||
if (this.type === 'trigger') {
|
||||
this.queryParams.productId = trigger.productId;
|
||||
} else {
|
||||
this.queryParams.productId = action.productId;
|
||||
}
|
||||
this.getDeviceDatas();
|
||||
},
|
||||
methods: {
|
||||
// 获取区设备列表
|
||||
getDeviceDatas () {
|
||||
listDeviceShort(this.queryParams)
|
||||
.then(res => {
|
||||
const { rows, total } = res;
|
||||
this.list = this.list.concat(rows);
|
||||
this.total = total;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
handleSearch () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getDeviceDatas();
|
||||
},
|
||||
handleClearSearch () {
|
||||
this.handleSearch();
|
||||
},
|
||||
// 状态改变事件
|
||||
handleStatusChange (item) {
|
||||
this.list = [];
|
||||
this.queryParams.status = item.id;
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getDeviceDatas();
|
||||
},
|
||||
// 获取设备图片
|
||||
getImageUrl (item) {
|
||||
if (item.imgUrl) {
|
||||
return item.imgUrl;
|
||||
} else {
|
||||
if (item.deviceType === 1) {
|
||||
return '/static/common/device.png';
|
||||
} else if (item.deviceType === 2) {
|
||||
return '/static/common/gateway.png';
|
||||
} else if (item.deviceType === 3) {
|
||||
return '/static/common/video.png';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.getDeviceDatas();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getDeviceDatas();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 下一步
|
||||
handleNextStep () {
|
||||
if (this.devices.length !== 0) {
|
||||
this.isModel = true;
|
||||
let action = uni.getStorageSync(this.type);
|
||||
action = { ...action, deviceCount: this.devices.length, deviceNums: this.devices };
|
||||
uni.setStorageSync(this.type, action);
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectDevice')
|
||||
});
|
||||
}
|
||||
},
|
||||
// 物模跳转
|
||||
goToModelList ({ name }) {
|
||||
let action = uni.getStorageSync(this.type);
|
||||
action = { ...action, type: name, arrayIndex: '', parentId: '' };
|
||||
uni.setStorageSync(this.type, action);
|
||||
uni.$u.route('/pagesA/scene/product/model', { type: this.type });
|
||||
},
|
||||
// 选择物模
|
||||
handleConfirmMore () {
|
||||
if (!this.modelType) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectThingsModels')
|
||||
});
|
||||
return
|
||||
}
|
||||
let action = uni.getStorageSync(this.type);
|
||||
action = { ...action, type: this.modelType };
|
||||
uni.setStorageSync(this.type, action);
|
||||
// 更新或者插入新的触发或者执行
|
||||
let { triggers, actions, ...res } = uni.getStorageSync('sceneData');
|
||||
if (Number.isNaN(this.editIndex) || this.editIndex === null) {
|
||||
if (this.type === 'trigger') {
|
||||
triggers.push(action);
|
||||
} else {
|
||||
actions.push(action);
|
||||
}
|
||||
uni.setStorageSync('sceneData', { triggers, actions, ...res });
|
||||
} else {
|
||||
if (this.type === 'trigger') {
|
||||
let list = triggers.map((item, i) => {
|
||||
if (i == this.editIndex) {
|
||||
return action
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
});
|
||||
uni.setStorageSync('sceneData', { triggers: [...list], actions, ...res });
|
||||
} else {
|
||||
let list = actions.map((item, i) => {
|
||||
if (i == this.editIndex) {
|
||||
return action
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
});
|
||||
uni.setStorageSync('sceneData', { triggers, actions: [...list], ...res });
|
||||
}
|
||||
}
|
||||
this.isModel = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/scene/detail');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.scene-device-wrap {
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
height: 74rpx;
|
||||
background: #eef3f7;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.right-wrap {
|
||||
color: #3c9cff;
|
||||
font-size: 28rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-wrap {
|
||||
padding: 0rpx 10rpx;
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
.group-wrap {
|
||||
width: 100%;
|
||||
margin: 30rpx;
|
||||
|
||||
.cell-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.slot-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.cell-text {
|
||||
font-size: 28rpx;
|
||||
margin-left: 20rpx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.model-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.cell-group-wrap {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 6rpx 0;
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
375
pagesA/scene/product/index.vue
Normal file
375
pagesA/scene/product/index.vue
Normal file
@ -0,0 +1,375 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.productList')" background-color="#007AFF">
|
||||
</navigation-bar>
|
||||
</page-meta>
|
||||
<view class="scene-product-wrap">
|
||||
<u-sticky>
|
||||
<view class="nav-bar">
|
||||
<view class="left-wrap">
|
||||
<u-icon v-if="!isSearch" name="search" size="22" @click="isSearch = true"></u-icon>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<u-input v-else v-model="queryParams.productName" :placeholder="$tt('product.inputProduct')"
|
||||
shape="circle" @clear="handleClearSearch" clearable>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<u--input v-else v-model="queryParams.productName" :placeholder="$tt('product.inputProduct')"
|
||||
shape="circle" @clear="handleClearSearch" clearable>
|
||||
<!-- #endif -->
|
||||
<template slot="prefix">
|
||||
<u-icon name="search" size="22" @click="isSearch = false"></u-icon>
|
||||
</template>
|
||||
<template slot="suffix">
|
||||
<u-button :text="$tt('timing.search')" type="primary" shape="circle" size="mini"
|
||||
@click="handleSearch"></u-button>
|
||||
</template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
</u-input>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</u--input>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view v-if="type === 'trigger' ? trigger.source === 3 : action.source === 3" class="right-wrap"
|
||||
@click="handleNextStep">{{$tt('product.next')}}</view>
|
||||
</view>
|
||||
</u-sticky>
|
||||
<view class="container-wrap">
|
||||
<u-radio-group v-model="productId">
|
||||
<view class="group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in list" :key="index">
|
||||
<u-cell :border="false" @click="goToDeviceList(item)">
|
||||
<view slot="title" class="slot-title">
|
||||
<u--image :src="getImageUrl(item)" radius="4" width="28" height="28"></u--image>
|
||||
<text class="cell-text">{{item.productName}}</text>
|
||||
</view>
|
||||
<view slot="value">
|
||||
<u-radio v-if="type === 'trigger' ? trigger.source === 3 : action.source === 3"
|
||||
shape="circle" :name="item.productId" iconSize="16"></u-radio>
|
||||
<u-icon v-else name="arrow-right" color="#909399" size="16"></u-icon>
|
||||
</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
|
||||
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
|
||||
:nomoreText="$tt('scene.emptyData')" marginTop="20" />
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
|
||||
<u-popup :show="isModel" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="true"
|
||||
@close="isModel = false">
|
||||
<view class="model-popup-wrap">
|
||||
<view class="title">{{$tt('product.selectThingModel')}}</view>
|
||||
<u-radio-group v-model="modelType">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('product.attribute')" :name="1" :border="false" isLink
|
||||
@click="goToModelList"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('product.function')" :name="2" :border="false" isLink
|
||||
@click="goToModelList"></u-cell>
|
||||
</view>
|
||||
<view v-if="type === 'trigger'" class="cell-wrap">
|
||||
<u-cell :title="$tt('product.event')" :name="3" :border="false" isLink
|
||||
@click="goToModelList"></u-cell>
|
||||
</view>
|
||||
<view v-if="type === 'trigger'" class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.deviceOnline')" :border="false">
|
||||
<view slot="value" class="u-slot-value">
|
||||
<u-radio shape="circle" :name="5"></u-radio>
|
||||
</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
<view v-if="type === 'trigger'" class="cell-wrap">
|
||||
<u-cell :title="$tt('sceneDetail.Equipment')" :border="false">
|
||||
<view slot="value" class="u-slot-value">
|
||||
<u-radio shape="circle" :name="6"></u-radio>
|
||||
</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
<view v-if="type === 'trigger'" class="btn">
|
||||
<u-button type="primary" @click="handleConfirmMore">{{$tt('sceneDetail.complete')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProductList } from '@/apis/modules/product.js';
|
||||
import { navigateBackTo } from '@/utils/common.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
type: 'trigger',
|
||||
editIndex: null, // null 代表新增
|
||||
isSearch: true,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 12,
|
||||
productName: ''
|
||||
},
|
||||
list: [], // 产品列表
|
||||
total: 0,
|
||||
loadmoreStatus: 'loadmore', // 刷新和加载相关
|
||||
productId: null, // 选择产品
|
||||
isModel: false, // 物模选择
|
||||
modelType: null, // 物模类型
|
||||
trigger: {},
|
||||
action: {}
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
const { type, editIndex } = option;
|
||||
this.type = type;
|
||||
this.editIndex = Number(editIndex);
|
||||
this.trigger = uni.getStorageSync('trigger');
|
||||
this.action = uni.getStorageSync('action');
|
||||
this.getProductDatas();
|
||||
},
|
||||
methods: {
|
||||
// 获取区设备列表
|
||||
getProductDatas () {
|
||||
getProductList(this.queryParams)
|
||||
.then(res => {
|
||||
const { rows, total } = res;
|
||||
this.list = this.list.concat(rows);
|
||||
this.total = total;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
handleSearch () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getProductDatas();
|
||||
},
|
||||
handleClearSearch () {
|
||||
this.handleSearch();
|
||||
},
|
||||
// 获取设备图片
|
||||
getImageUrl (item) {
|
||||
if (item.imgUrl) {
|
||||
return item.imgUrl;
|
||||
} else {
|
||||
if (item.deviceType === 1) {
|
||||
return '/static/common/device.png';
|
||||
} else if (item.deviceType === 2) {
|
||||
return '/static/common/gateway.png';
|
||||
} else if (item.deviceType === 3) {
|
||||
return '/static/common/video.png';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh () {
|
||||
this.list = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
// 模拟网络请求
|
||||
setTimeout(x => {
|
||||
this.getProductDatas();
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
},
|
||||
// 上拉加载
|
||||
onReachBottom () {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.queryParams.pageNum = ++this.queryParams.pageNum;
|
||||
// 模拟网络请求
|
||||
setTimeout(() => {
|
||||
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
|
||||
this.loadmoreStatus = 'nomore';
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
this.getProductDatas();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 设备跳转
|
||||
goToDeviceList (item) {
|
||||
if (this.type === 'trigger') {
|
||||
if (this.trigger.source === 1) {
|
||||
let trigger = uni.getStorageSync('trigger');
|
||||
trigger = { ...trigger, productId: item.productId, productName: item.productName };
|
||||
uni.setStorageSync('trigger', trigger);
|
||||
uni.$u.route('/pagesA/scene/product/device', { type: this.type });
|
||||
}
|
||||
} else {
|
||||
if (this.action.source === 1) {
|
||||
let action = uni.getStorageSync('action');
|
||||
action = { ...action, productId: item.productId, productName: item.productName };
|
||||
uni.setStorageSync('action', action);
|
||||
uni.$u.route('/pagesA/scene/product/device', { type: this.type });
|
||||
}
|
||||
}
|
||||
},
|
||||
// 下一步
|
||||
handleNextStep () {
|
||||
if (this.productId) {
|
||||
this.isModel = true;
|
||||
const { productId, productName } = this.list.find(item => item.productId === this.productId);
|
||||
let action = uni.getStorageSync(this.type);
|
||||
action = { ...action, productId, productName };
|
||||
uni.setStorageSync(this.type, action);
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectProducts')
|
||||
});
|
||||
}
|
||||
},
|
||||
// 物模跳转
|
||||
goToModelList ({ name }) {
|
||||
let action = uni.getStorageSync(this.type);
|
||||
action = { ...action, type: name, arrayIndex: '', parentId: '' };
|
||||
uni.setStorageSync(this.type, action);
|
||||
uni.$u.route('/pagesA/scene/product/model', { type: this.type, editIndex: this.editIndex });
|
||||
},
|
||||
// 选择物模
|
||||
handleConfirmMore () {
|
||||
if (!this.modelType) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectThingsModels')
|
||||
});
|
||||
return
|
||||
}
|
||||
let action = uni.getStorageSync(this.type);
|
||||
action = { ...action, type: this.modelType };
|
||||
uni.setStorageSync(this.type, action);
|
||||
// 更新或者插入新的触发或者执行
|
||||
let { triggers, actions, ...res } = uni.getStorageSync('sceneData');
|
||||
if (Number.isNaN(this.editIndex) || this.editIndex === null) {
|
||||
if (this.type === 'trigger') {
|
||||
triggers.push(action);
|
||||
} else {
|
||||
actions.push(action);
|
||||
}
|
||||
uni.setStorageSync('sceneData', { triggers, actions, ...res });
|
||||
} else {
|
||||
if (this.type === 'trigger') {
|
||||
let list = triggers.map((item, i) => {
|
||||
if (i == this.editIndex) {
|
||||
return action
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
});
|
||||
uni.setStorageSync('sceneData', { triggers: [...list], actions, ...res });
|
||||
} else {
|
||||
let list = actions.map((item, i) => {
|
||||
if (i == this.editIndex) {
|
||||
return action
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
});
|
||||
uni.setStorageSync('sceneData', { triggers, actions: [...list], ...res });
|
||||
}
|
||||
}
|
||||
this.isModel = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/scene/detail');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.scene-product-wrap {
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
height: 74rpx;
|
||||
background: #eef3f7;
|
||||
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.right-wrap {
|
||||
color: #3c9cff;
|
||||
font-size: 28rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.container-wrap {
|
||||
.group-wrap {
|
||||
width: 100%;
|
||||
margin: 0 30rpx 30rpx 30rpx;
|
||||
|
||||
.cell-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.slot-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.cell-text {
|
||||
font-size: 28rpx;
|
||||
margin-left: 20rpx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.model-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.cell-group-wrap {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 6rpx 0;
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
830
pagesA/scene/product/model.vue
Normal file
830
pagesA/scene/product/model.vue
Normal file
@ -0,0 +1,830 @@
|
||||
<template>
|
||||
<page-meta>
|
||||
<navigation-bar :title="$tt('navBar.physicalModels')" title-align="center" background-color="#007AFF" />
|
||||
</page-meta>
|
||||
<view class="scene-model-wrap">
|
||||
<view class="container-wrap">
|
||||
<view class="group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item, index) in list" :key="index">
|
||||
<u-cell :border="false" isLink
|
||||
:arrow-direction="item.datatype.type === 'object' || item.datatype.type === 'array' ? 'left' : 'down'"
|
||||
@click="handleModel(item)">
|
||||
<view slot="title"> {{item.name}}</view>
|
||||
</u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
<view class="other">
|
||||
<u-empty mode="data" :show="list.length === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
|
||||
|
||||
<!-- integer、decimal 类型 -->
|
||||
<u-popup :show="isInteger" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isInteger = false">
|
||||
<view class="integer-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isInteger = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmInteger">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="select-wrap">
|
||||
<view class="form-wrap" v-if="type === 'trigger'">
|
||||
<u--form labelPosition="left" labelWidth="80">
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item :label="$tt('product.operator')" @click="isOperator = true">
|
||||
<u--input v-model="operators.name" border="none" inputAlign="right"
|
||||
:placeholder="$tt('product.selectOperator')" disabledColor="#fff" disabled
|
||||
:customStyle="{ marginRight: '20rpx' }" />
|
||||
<u-icon slot="right" name="arrow-right"></u-icon>
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u--form>
|
||||
</view>
|
||||
<template v-if="operators.value !== 'between' && operators.value !== 'notBetween' ">
|
||||
<view class="num-wrap">
|
||||
<text class="num">{{value}}</text>
|
||||
<text class="unit">{{model.datatype.unit}}</text>
|
||||
</view>
|
||||
<view class="slider-wrap">
|
||||
<u-slider :min="model.datatype.min === undefined ? 0 : model.datatype.min"
|
||||
:max="model.datatype.max === undefined ? 100 : model.datatype.max"
|
||||
:step="model.datatype.step === undefined ? 1 : model.datatype.step"
|
||||
@input="handleSliderChange"></u-slider>
|
||||
<view class="limit">
|
||||
<text>
|
||||
{{`${model.datatype.min === undefined ? 0 : model.datatype.min} ${model.datatype.unit || ''}`}}
|
||||
</text>
|
||||
<text>
|
||||
{{`${model.datatype.max === undefined ? 0 : model.datatype.max} ${model.datatype.unit || ''}`}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view v-else class="range-wrap">
|
||||
<u--input v-model="valueA" type="number" :placeholder="$tt('product.input')"
|
||||
border="surround"></u--input>
|
||||
<text style="padding: 0 20rpx;">-</text>
|
||||
<u--input v-model="valueB" type="number" :placeholder="$tt('product.input')"
|
||||
border="surround"></u--input>
|
||||
<text v-if="model.datatype.unit" style="margin-left: 20rpx;">{{model.datatype.unit}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- bool 类型 -->
|
||||
<u-popup :show="isBool" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isBool = false">
|
||||
<view class="bool-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isBool = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmBool">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="radio-group-wrap">
|
||||
<u-radio-group v-model="value" :borderBottom="false" placement="column" iconPlacement="right">
|
||||
<view class="radio-wrap">
|
||||
<u-radio :label="$tt('product.on')" :name="1" iconSize="16"></u-radio>
|
||||
</view>
|
||||
<view class="radio-wrap">
|
||||
<u-radio :label="$tt('product.off')" :name="0" iconSize="16"></u-radio>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- enum 类型 -->
|
||||
<u-popup :show="isEnum" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isEnum = false">
|
||||
<view class="enum-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isEnum = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmEnum">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="radio-group-wrap">
|
||||
<u-radio-group v-model="value" :borderBottom="false" placement="column" iconPlacement="right">
|
||||
<view class="radio-wrap" v-for="(item, index) in model.datatype.enumList" :key="index">
|
||||
<u-radio :label="item.text" :name="item.value" iconSize="16"></u-radio>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- string 类型 -->
|
||||
<u-popup :show="isString" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
|
||||
@close="isString = false">
|
||||
<view class="string-popup-wrap">
|
||||
<view class="nav">
|
||||
<text @click="isString = false">{{$tt('common.cancel')}}</text>
|
||||
<text @click="handleConfirmString">{{$tt('common.confirm')}}</text>
|
||||
</view>
|
||||
<view class="select-wrap">
|
||||
<view class="form-wrap" v-if="type === 'trigger'">
|
||||
<u--form labelPosition="left" labelWidth="80">
|
||||
<view class="form-item-wrap">
|
||||
<u-form-item :label="$tt('product.operator')" @click="isOperator = true">
|
||||
<u--input v-model="operators.name" border="none" inputAlign="right"
|
||||
:placeholder="$tt('product.selectOperator')" disabledColor="#fff" disabled
|
||||
:customStyle="{ marginRight: '20rpx' }" />
|
||||
<u-icon slot="right" name="arrow-right"></u-icon>
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u--form>
|
||||
</view>
|
||||
<view class="input-wrap">
|
||||
<u--input v-model="value" :customStyle="{ padding: '24rpx' }"
|
||||
:placeholder="$tt('deviceDetail.inputMsg')" border="surround" fontSize="16"
|
||||
clearable></u--input>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<!-- 操作符 -->
|
||||
<u-popup :show="isOperator" :round="5" mode="bottom" :closeOnClickOverlay="true" zIndex="10076"
|
||||
@close="isOperator = false">
|
||||
<view class="operator-popup-wrap">
|
||||
<view class="cell-group-wrap">
|
||||
<u-cell-group :border="false">
|
||||
<view class="cell-wrap" v-for="(item,index) in oprGroup" :key="index"
|
||||
v-if="item.types.length === 0 || item.types.includes(model.datatype.type)">
|
||||
<u-cell :title="item.name" :name="item.value" :border="false"
|
||||
@click="handleConfirmOperator(item)"></u-cell>
|
||||
</view>
|
||||
<view class="cell-wrap">
|
||||
<u-cell :title="$tt('common.cancel')" name="cancel" :border="false"
|
||||
@click="handleConfirmOperator({ value: 'cancel' })"></u-cell>
|
||||
</view>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getCacheThingsModel
|
||||
} from '@/apis/modules/device.js';
|
||||
import {
|
||||
navigateBackTo
|
||||
} from '@/utils/common.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
type: 'trigger',
|
||||
editIndex: null, // null 代表新增
|
||||
parentId: null,
|
||||
list: [], // 属性
|
||||
isInteger: false, // 整型
|
||||
oprGroup: [{
|
||||
types: [],
|
||||
name: '等于(=)',
|
||||
value: '=',
|
||||
},
|
||||
{
|
||||
types: [],
|
||||
name: '不等于(!=)',
|
||||
value: '!=',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '大于(>)',
|
||||
value: '>',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '小于(<)',
|
||||
value: '<',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '大于等于(>=)',
|
||||
value: '>=',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '小于等于(<=)',
|
||||
value: '<=',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '在...之间(between)',
|
||||
value: 'between',
|
||||
},
|
||||
{
|
||||
types: ['integer', 'decimal'],
|
||||
name: '不在...之间(notBetween)',
|
||||
value: 'notBetween',
|
||||
},
|
||||
{
|
||||
types: ['string'],
|
||||
name: '包含(contain)',
|
||||
value: 'contain',
|
||||
},
|
||||
{
|
||||
types: ['string'],
|
||||
name: '不包含(notContain)',
|
||||
value: 'notContain',
|
||||
}
|
||||
],
|
||||
operators: {}, // 对象
|
||||
isOperator: false, // 操作符
|
||||
isBool: false,
|
||||
isEnum: false, // 枚举
|
||||
isString: false,
|
||||
model: {
|
||||
datatype: {},
|
||||
},
|
||||
value: null, // 属性值
|
||||
valueA: null,
|
||||
valueB: null,
|
||||
trigger: {},
|
||||
action: {},
|
||||
};
|
||||
},
|
||||
onLoad (option) {
|
||||
const {
|
||||
type,
|
||||
data,
|
||||
editIndex
|
||||
} = option;
|
||||
this.type = type;
|
||||
this.editIndex = Number(editIndex);
|
||||
if (data) {
|
||||
this.list = JSON.parse(data);
|
||||
} else {
|
||||
if (type === 'trigger') {
|
||||
this.trigger = uni.getStorageSync('trigger');
|
||||
const {
|
||||
productId
|
||||
} = this.trigger;
|
||||
this.productId = productId;
|
||||
this.$nextTick(() => {
|
||||
this.getModelDatas();
|
||||
})
|
||||
} else {
|
||||
this.action = uni.getStorageSync('action');
|
||||
const {
|
||||
productId
|
||||
} = this.action;
|
||||
this.productId = productId;
|
||||
this.$nextTick(() => {
|
||||
this.getModelDatas();
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 获取区设备列表
|
||||
getModelDatas () {
|
||||
getCacheThingsModel(this.productId)
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
let data = JSON.parse(res.data);
|
||||
this.list = this.formatArrayIndex(data);
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.msg
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
// 物模型格式化处理
|
||||
formatArrayIndex (data) {
|
||||
let obj = {
|
||||
...data
|
||||
};
|
||||
for (let o in obj) {
|
||||
obj[o] = obj[o].map((item) => {
|
||||
item = {
|
||||
...item,
|
||||
level: 'parent'
|
||||
};
|
||||
if (item.datatype.type === 'array') {
|
||||
let arrayModel = [];
|
||||
for (let k = 0; k < item.datatype.arrayCount; k++) {
|
||||
let index = k > 9 ? String(k) : '0' + k;
|
||||
if (item.datatype.arrayType === 'object') {
|
||||
arrayModel.push({
|
||||
id: index,
|
||||
name: item.name + ' ' + (k + 1),
|
||||
level: 'index',
|
||||
datatype: {
|
||||
type: item.datatype.arrayType,
|
||||
params: item.datatype.params
|
||||
},
|
||||
});
|
||||
} else {
|
||||
arrayModel.push({
|
||||
id: index,
|
||||
name: item.name + ' ' + (k + 1),
|
||||
level: 'index',
|
||||
datatype: {
|
||||
type: item.datatype.arrayType,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
item.datatype.arrayModel = arrayModel;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
const ap = this.type;
|
||||
let list = [];
|
||||
if (this[ap].type == 1) {
|
||||
list = obj.properties || [];
|
||||
} else if (this[ap].type == 2) {
|
||||
list = obj.functions || [];
|
||||
} else if (this[ap].type == 3) {
|
||||
list = obj.events || [];
|
||||
}
|
||||
if (ap === 'action') {
|
||||
list = list.filter((item) => {
|
||||
if (item.datatype.params && item.datatype.params.length !== 0) {
|
||||
item.datatype.params = item.datatype.params.filter((item) => item.isMonitor == 0 &&
|
||||
item
|
||||
.isReadonly == 0);
|
||||
}
|
||||
return item.isMonitor == 0 && item.isReadonly == 0;
|
||||
});
|
||||
}
|
||||
return list;
|
||||
},
|
||||
// 选中选择
|
||||
handleModel (item) {
|
||||
this.model = item;
|
||||
// 存储 parentId 和 arrayIndex
|
||||
let action = uni.getStorageSync(this.type);
|
||||
if (item.level === 'parent') {
|
||||
action = {
|
||||
...action,
|
||||
parentId: item.id,
|
||||
parentName: item.name,
|
||||
arrayIndex: '',
|
||||
arrayIndexName: ''
|
||||
};
|
||||
}
|
||||
if (item.level === 'index') {
|
||||
action = {
|
||||
...action,
|
||||
arrayIndex: item.id,
|
||||
arrayIndexName: item.name
|
||||
};
|
||||
}
|
||||
uni.setStorageSync(this.type, action);
|
||||
// 根据类型弹出对应框
|
||||
this.operators = {};
|
||||
const {
|
||||
datatype
|
||||
} = item;
|
||||
const {
|
||||
type,
|
||||
params,
|
||||
arrayModel
|
||||
} = datatype;
|
||||
this.value = null;
|
||||
if (type === 'integer' || type === 'decimal') {
|
||||
this.isInteger = true;
|
||||
this.value = datatype.min ? datatype.min : 0;
|
||||
this.valueA = null;
|
||||
this.valueB = null;
|
||||
} else if (type === 'bool') {
|
||||
this.isBool = true;
|
||||
} else if (type === 'enum') {
|
||||
this.isEnum = true;
|
||||
} else if (type === 'string') {
|
||||
this.isString = true
|
||||
} else if (type === 'object') {
|
||||
uni.$u.route('/pagesA/scene/product/model', {
|
||||
type: this.type,
|
||||
data: JSON.stringify(params),
|
||||
editIndex: this.editIndex
|
||||
});
|
||||
} else if (type === "array") {
|
||||
uni.$u.route('/pagesA/scene/product/model', {
|
||||
type: this.type,
|
||||
data: JSON.stringify(arrayModel),
|
||||
editIndex: this.editIndex
|
||||
});
|
||||
}
|
||||
},
|
||||
// 操作符选择{{$tt('common.confirm')}}
|
||||
handleConfirmOperator (item) {
|
||||
if (item.value !== 'cancel') {
|
||||
this.operators = item;
|
||||
}
|
||||
this.isOperator = false;
|
||||
},
|
||||
// slider 负数会有问题,所以采用这方式
|
||||
handleSliderChange (value) {
|
||||
if (this.model.datatype.step) {
|
||||
const str = this.model.datatype.step.toString();
|
||||
const places = str.match(/\.(\d+)/);
|
||||
const num = places ? places[1].length : 0;
|
||||
this.value = value.toFixed(num);
|
||||
} else {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
// 整型、浮点型
|
||||
handleConfirmInteger () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
if (this.type === 'trigger' && Object.keys(this.operators).length === 0) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectOperator')
|
||||
});
|
||||
return;
|
||||
};
|
||||
let value = '';
|
||||
if (this.operators.value !== 'between' && this.operators.value !== 'notBetween') {
|
||||
if (this.value === this.model.datatype.min || !this.value) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.inputValue')
|
||||
});
|
||||
return;
|
||||
};
|
||||
value = this.value;
|
||||
} else {
|
||||
if (!this.valueA || !this.valueB) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.inputValue')
|
||||
});
|
||||
return;
|
||||
}
|
||||
value = this.valueA + '-' + this.valueB;
|
||||
}
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: value,
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isInteger = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/scene/detail');
|
||||
},
|
||||
// bool 类型
|
||||
handleConfirmBool () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
if (this.value === null) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectValue')
|
||||
});
|
||||
return;
|
||||
};
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: this.value,
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isBool = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/scene/detail');
|
||||
},
|
||||
// enum 类型
|
||||
handleConfirmEnum () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
if (!this.value) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectValue')
|
||||
});
|
||||
return;
|
||||
};
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: this.value,
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isEnum = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/scene/detail');
|
||||
},
|
||||
// string 类型
|
||||
handleConfirmString () {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = this.model;
|
||||
if (this.type === 'trigger' && Object.keys(this.operators).length === 0) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.selectOperator')
|
||||
});
|
||||
return;
|
||||
};
|
||||
if (!this.value) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: this.$tt('product.inputValue')
|
||||
});
|
||||
return;
|
||||
};
|
||||
const {
|
||||
tempId,
|
||||
tempName
|
||||
} = this.getModelIdAndName(id, name);
|
||||
const model = {
|
||||
id: tempId,
|
||||
name: tempName,
|
||||
value: this.value,
|
||||
};
|
||||
this.setModelData(model);
|
||||
this.isString = false;
|
||||
uni.setStorageSync('callback', true);
|
||||
navigateBackTo('/pagesA/scene/detail');
|
||||
},
|
||||
// 获取model的id和name
|
||||
getModelIdAndName (id, name) {
|
||||
let tempId = id;
|
||||
let tempName = name;
|
||||
const {
|
||||
parentId,
|
||||
parentName,
|
||||
arrayIndex,
|
||||
arrayIndexName
|
||||
} = uni.getStorageSync(this.type);
|
||||
if (arrayIndex) {
|
||||
if (arrayIndex === id) { // 数组
|
||||
tempId = `array_${arrayIndex}_${parentId}`;
|
||||
tempName = parentName;
|
||||
} else { // 数组对象
|
||||
tempId = `array_${arrayIndex}_${id}`;
|
||||
}
|
||||
} else {
|
||||
tempId = id;
|
||||
}
|
||||
return {
|
||||
tempId,
|
||||
tempName
|
||||
}
|
||||
},
|
||||
// 存储数据
|
||||
setModelData (data) {
|
||||
let action = uni.getStorageSync(this.type);
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
value
|
||||
} = data;
|
||||
action = {
|
||||
...action,
|
||||
id,
|
||||
name,
|
||||
value
|
||||
};
|
||||
if (this.type === 'trigger') {
|
||||
action = {
|
||||
...action,
|
||||
operator: this.operators.value || ''
|
||||
};
|
||||
}
|
||||
uni.setStorageSync(this.type, action);
|
||||
// 更新或者插入新的触发或者执行
|
||||
let {
|
||||
triggers,
|
||||
actions,
|
||||
...res
|
||||
} = uni.getStorageSync('sceneData');
|
||||
if (Number.isNaN(this.editIndex) || this.editIndex === null) {
|
||||
if (this.type === 'trigger') {
|
||||
triggers.push(action);
|
||||
} else {
|
||||
actions.push(action);
|
||||
}
|
||||
uni.setStorageSync('sceneData', {
|
||||
triggers,
|
||||
actions,
|
||||
...res
|
||||
});
|
||||
} else {
|
||||
if (this.type === 'trigger') {
|
||||
let list = triggers.map((item, i) => {
|
||||
if (i == this.editIndex) {
|
||||
return action
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
});
|
||||
uni.setStorageSync('sceneData', {
|
||||
triggers: [...list],
|
||||
actions,
|
||||
...res
|
||||
});
|
||||
} else {
|
||||
let list = actions.map((item, i) => {
|
||||
if (i == this.editIndex) {
|
||||
return action
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
});
|
||||
uni.setStorageSync('sceneData', {
|
||||
triggers,
|
||||
actions: [...list],
|
||||
...res
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
background: #eef3f7;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.scene-model-wrap {
|
||||
.container-wrap {
|
||||
.group-wrap {
|
||||
margin: 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.cell-wrap {
|
||||
padding: 7rpx 0;
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.integer-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.select-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
padding: 40rpx 20rpx;
|
||||
|
||||
.form-wrap {
|
||||
.form-item-wrap {
|
||||
padding: 4rpx 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.num-wrap {
|
||||
position: relative;
|
||||
margin-top: 60rpx;
|
||||
text-align: center;
|
||||
|
||||
.num {
|
||||
font-size: 80rpx;
|
||||
}
|
||||
|
||||
.unit {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -23rpx;
|
||||
font-size: 32rpx;
|
||||
color: #868686;
|
||||
margin-left: 60rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-wrap {
|
||||
.limit {
|
||||
font-size: 28rpx;
|
||||
color: #868686;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.range-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operator-popup-wrap {
|
||||
padding: 10rpx 0 20rpx;
|
||||
|
||||
.cell-group-wrap {
|
||||
background: #eef3f7;
|
||||
|
||||
.cell-wrap {
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
padding: 10rpx;
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
|
||||
&:last-child {
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bool-popup-wrap,
|
||||
.enum-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.radio-group-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.radio-wrap {
|
||||
padding: 40rpx;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1rpx solid #F1F2F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.string-popup-wrap {
|
||||
padding: 30rpx;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.select-wrap {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
padding: 40rpx 20rpx;
|
||||
|
||||
.form-wrap {
|
||||
.form-item-wrap {
|
||||
padding: 4rpx 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
margin: 32rpx 12rpx 20rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user