Compare commits
1 Commits
feasure-li
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
565a5b143b |
56
App.vue
|
|
@ -3,7 +3,10 @@ import config from './config'
|
|||
import { getToken } from '@/utils/auth'
|
||||
import mqttUtil from '@/utils/mqtt'
|
||||
import {startMqttOnlinePing, stopMqttOnlinePing} from "./utils/mqtt";
|
||||
import {unReadCount} from "@/api/warn/message";
|
||||
import {batchSubscribe} from "./api/system/mqtt";
|
||||
import store from "store";
|
||||
import {listAgri} from "./api/system/assets/agri";
|
||||
|
||||
export default {
|
||||
subscribeList:[],
|
||||
globalData: {
|
||||
|
|
@ -57,6 +60,7 @@ export default {
|
|||
console.log('小程序切前台/首次显示')
|
||||
const token = getToken() || this.globalData.mqtt.token
|
||||
if (token) {
|
||||
this.$tab.reLaunch('/pages/control/index')
|
||||
// 兜底检查:如果globalData里没有列表,但缓存里有,补充恢复
|
||||
if (this.globalData.mqtt.subscribeList.length === 0) {
|
||||
const savedSubscribeList = uni.getStorageSync('mqtt_subscribe_list')
|
||||
|
|
@ -72,7 +76,6 @@ export default {
|
|||
|
||||
// 防止 mqtt client 还没连上:可以等 connected 后再 start(见下面提示)
|
||||
startMqttOnlinePing( 20000);
|
||||
this.unReadCount()
|
||||
}
|
||||
},
|
||||
onHide() {
|
||||
|
|
@ -121,7 +124,27 @@ export default {
|
|||
console.error('MQTT连接失败')
|
||||
return
|
||||
}
|
||||
mqttUtil.updateSubscribeTopic();
|
||||
var clientId = mqttUtil.getMqttState().clientId;
|
||||
this.getSubscribeImei(clientId).then(res=>{
|
||||
console.info("subscribeList",res)
|
||||
const subscribeList = res;
|
||||
this.globalData.mqtt.subscribeList = subscribeList || []
|
||||
batchSubscribe({clientId: clientId}).then((result) => {
|
||||
if (result.code === 200) {
|
||||
console.info(`设备列表订阅成功:${subscribeList}`)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 新增:登录成功时同步到localStorage ==========
|
||||
uni.setStorageSync('mqtt_subscribe_list', subscribeList || [])
|
||||
|
||||
if (subscribeList.length > 0) {
|
||||
mqttUtil.updateSubscribeList(subscribeList)
|
||||
console.log('恢复MQTT订阅列表:', subscribeList)
|
||||
// ========== 新增:恢复订阅后同步到localStorage ==========
|
||||
uni.setStorageSync('mqtt_subscribe_list', subscribeList)
|
||||
}
|
||||
})
|
||||
},
|
||||
loginSuccess(token) {
|
||||
this.globalData.mqtt.hasLogin = true
|
||||
|
|
@ -139,9 +162,23 @@ export default {
|
|||
}
|
||||
// ========== 新增:登出时清空localStorage的订阅列表 ==========
|
||||
uni.removeStorageSync('mqtt_subscribe_list')
|
||||
uni.removeStorageSync('agri_list')
|
||||
console.log('登出成功,MQTT已断开')
|
||||
},
|
||||
|
||||
getSubscribeImei(clientId) {
|
||||
return listAgri().then(response => {
|
||||
const subscribeList = [];
|
||||
if (response.code === 200) {
|
||||
response.rows.forEach(item =>
|
||||
subscribeList.push(`frontend/${clientId}/dtu/${item.imei}/+`)
|
||||
);
|
||||
if (store.getters && store.getters.name === 'admin') {
|
||||
subscribeList.push(`frontend/${clientId}/dtu/862538065276061/+`)
|
||||
}
|
||||
}
|
||||
return subscribeList;
|
||||
})
|
||||
},
|
||||
// token过期处理逻辑(核心)
|
||||
handleTokenExpired() {
|
||||
|
||||
|
|
@ -214,20 +251,11 @@ export default {
|
|||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
unReadCount() {
|
||||
unReadCount().then(response => {
|
||||
if (response.code === 200 && response.data > 0) {
|
||||
uni.showTabBarRedDot({index: 2})
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// 加上图鸟会全变白
|
||||
//@import './tuniao-ui/iconfont.css'; // 去掉main.wxss少一半
|
||||
@import '@/static/scss/index.scss';
|
||||
@import '@/static/scss/index.scss'
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询卷膜运行条件列表
|
||||
export function listAutoTerm(query) {
|
||||
return request({
|
||||
url: '/control/autoTerm/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询卷膜运行条件详细
|
||||
export function getAutoTerm(id) {
|
||||
return request({
|
||||
url: '/control/autoTerm/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增卷膜运行条件
|
||||
export function addAutoTerm(data) {
|
||||
return request({
|
||||
url: '/control/autoTerm',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改卷膜运行条件
|
||||
export function updateAutoTerm(data) {
|
||||
return request({
|
||||
url: '/control/autoTerm',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除卷膜运行条件
|
||||
export function delAutoTerm(id) {
|
||||
return request({
|
||||
url: '/control/autoTerm/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function getAgriTerm(imei) {
|
||||
return request({
|
||||
url: '/control/autoTerm/getAgriTerm/' + imei,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function saveAgriTerm(data) {
|
||||
return request({
|
||||
url: '/control/autoTerm/saveAgriTerm',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询卷膜参数配置列表
|
||||
export function listRollerParam(query) {
|
||||
return request({
|
||||
url: '/control/rollerParam/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询卷膜参数配置详细
|
||||
export function getRollerParam(id) {
|
||||
return request({
|
||||
url: '/control/rollerParam/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增卷膜参数配置
|
||||
export function addRollerParam(data) {
|
||||
return request({
|
||||
url: '/control/rollerParam',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改卷膜参数配置
|
||||
export function updateRollerParam(data) {
|
||||
return request({
|
||||
url: '/control/rollerParam',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除卷膜参数配置
|
||||
export function delRollerParam(id) {
|
||||
return request({
|
||||
url: '/control/rollerParam/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -16,13 +16,6 @@ export function getAgri(id) {
|
|||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function getAgriInfo(query) {
|
||||
return request({
|
||||
url: '/assets/agri/findAgriInfoByUser',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 新增大棚管理
|
||||
export function addAgri(data) {
|
||||
|
|
@ -41,13 +34,6 @@ export function updateAgri(data) {
|
|||
data: data
|
||||
})
|
||||
}
|
||||
export function renameAgriName(data) {
|
||||
return request({
|
||||
url: '/assets/agri/renameAgriName',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除大棚管理
|
||||
export function delAgri(id) {
|
||||
|
|
@ -56,32 +42,3 @@ export function delAgri(id) {
|
|||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端添加大棚
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function addAgriMobile(data) {
|
||||
return request({
|
||||
url: '/assets/agri/addAgriFromMobile',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function switchAgriMode(imei, code) {
|
||||
return request({
|
||||
url: '/assets/agri/switchAgriMode/' + imei,
|
||||
method: 'post',
|
||||
params: code
|
||||
})
|
||||
}
|
||||
|
||||
// 查询设备分享信息
|
||||
export function selectShareInfo(data) {
|
||||
return request({
|
||||
url: '/assets/agri/selectShareInfo',
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询自动化卷膜风口大小设置列表
|
||||
export function listAir(query) {
|
||||
return request({
|
||||
url: '/assets/air/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询自动化卷膜风口大小设置详细
|
||||
export function getAir(id) {
|
||||
return request({
|
||||
url: '/assets/air/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增自动化卷膜风口大小设置
|
||||
export function addAir(data) {
|
||||
return request({
|
||||
url: '/assets/air',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改自动化卷膜风口大小设置
|
||||
export function updateAir(data) {
|
||||
return request({
|
||||
url: '/assets/air',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除自动化卷膜风口大小设置
|
||||
export function delAir(id) {
|
||||
return request({
|
||||
url: '/assets/air/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询大棚信息(用户-设备关联)列表
|
||||
export function listUserAgri(query) {
|
||||
return request({
|
||||
url: '/assets/userAgri/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询大棚信息(用户-设备关联)详细
|
||||
export function getUserAgri(id) {
|
||||
return request({
|
||||
url: '/assets/userAgri/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function removeAgri(data) {
|
||||
return request({
|
||||
url: '/assets/userAgri/removeAgri',
|
||||
method: 'delete',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 新增大棚信息(用户-设备关联)
|
||||
export function addUserAgri(data) {
|
||||
return request({
|
||||
url: '/assets/userAgri',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改大棚信息(用户-设备关联)
|
||||
export function updateUserAgri(data) {
|
||||
return request({
|
||||
url: '/assets/userAgri',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除大棚信息(用户-设备关联)
|
||||
export function delUserAgri(id) {
|
||||
return request({
|
||||
url: '/assets/userAgri/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function findAgriUser(query) {
|
||||
return request({
|
||||
url: '/assets/userAgri/findAgriUser',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function findAllUser(query) {
|
||||
return request({
|
||||
url: '/assets/userAgri/findAllUser',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
export function getRecentShareUser(query) {
|
||||
return request({
|
||||
url: '/assets/userAgri/getRecentShareUser',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function batchAssociaUser(data) {
|
||||
return request({
|
||||
url: '/assets/userAgri/batchAssociaUser',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -8,12 +8,5 @@ export function findDtuDataByInfo(query) {
|
|||
params: query
|
||||
})
|
||||
}
|
||||
export function getHistoryData(query) {
|
||||
return request({
|
||||
url: '/system/data/getHistoryData',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,12 +47,3 @@ export function status() {
|
|||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function getAgriStatus(data) {
|
||||
return request({
|
||||
url: '/api/mqtt/getAgriStatus',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询自动模式日志列表
|
||||
export function listAutolog(query) {
|
||||
return request({
|
||||
url: '/warn/autolog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询自动模式日志详细
|
||||
export function getAutolog(id) {
|
||||
return request({
|
||||
url: '/warn/autolog/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增自动模式日志
|
||||
export function addAutolog(data) {
|
||||
return request({
|
||||
url: '/warn/autolog',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改自动模式日志
|
||||
export function updateAutolog(data) {
|
||||
return request({
|
||||
url: '/warn/autolog',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除自动模式日志
|
||||
export function delAutolog(id) {
|
||||
return request({
|
||||
url: '/warn/autolog/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
export function getAutoLogList(query) {
|
||||
return request({
|
||||
url: '/warn/autolog/getAutoLogList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询系统消息中心列表
|
||||
export function listMessage(query) {
|
||||
return request({
|
||||
url: '/warn/message/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询系统消息中心详细
|
||||
export function getMessage(id) {
|
||||
return request({
|
||||
url: '/warn/message/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function unReadCount() {
|
||||
return request({
|
||||
url: '/warn/message/unReadCount',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增系统消息中心
|
||||
export function addMessage(data) {
|
||||
return request({
|
||||
url: '/warn/message',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改系统消息中心
|
||||
export function updateMessage(data) {
|
||||
return request({
|
||||
url: '/warn/message',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function updateRead(data) {
|
||||
return request({
|
||||
url: '/warn/message/updateRead',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除系统消息中心
|
||||
export function delMessage(id) {
|
||||
return request({
|
||||
url: '/warn/message/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
// 删除系统消息中心
|
||||
export function removeBatch(data) {
|
||||
return request({
|
||||
url: '/warn/message/removeBatch',
|
||||
method: 'delete',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function getMessages(query) {
|
||||
return request({
|
||||
url: '/warn/message/getMessage',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getMsgOverview(data) {
|
||||
return request({
|
||||
url: '/warn/message/getMsgOverview',
|
||||
method: 'get',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
Animation 微动画
|
||||
基于ColorUI-GA组建库的动画模块
|
||||
*/
|
||||
.gif-black {
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
.gif-white {
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
|
||||
/* Animation css */
|
||||
[class*='animation-'] {
|
||||
animation-duration: 0.5s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.animation-fade {
|
||||
animation-name: fade;
|
||||
animation-duration: 0.8s;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.animation-scale-up {
|
||||
animation-name: scale-up;
|
||||
}
|
||||
|
||||
.animation-scale-down {
|
||||
animation-name: scale-down;
|
||||
}
|
||||
|
||||
.animation-slide-top {
|
||||
animation-name: slide-top;
|
||||
}
|
||||
|
||||
.animation-slide-bottom {
|
||||
animation-name: slide-bottom;
|
||||
}
|
||||
|
||||
.animation-slide-left {
|
||||
animation-name: slide-left;
|
||||
}
|
||||
|
||||
.animation-slide-right {
|
||||
animation-name: slide-right;
|
||||
}
|
||||
|
||||
.animation-shake {
|
||||
animation-name: shake;
|
||||
}
|
||||
|
||||
.animation-reverse {
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale-up {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale-down {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(1.8);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-top {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-bottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
10% {
|
||||
transform: translateX(-9px);
|
||||
}
|
||||
|
||||
20% {
|
||||
transform: translateX(8px);
|
||||
}
|
||||
|
||||
30% {
|
||||
transform: translateX(-7px);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateX(6px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translateX(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-left {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-right {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,507 +0,0 @@
|
|||
<template>
|
||||
<!-- colorui/components/calendar/calendar.wxml -->
|
||||
<view class="calendar">
|
||||
<view class="title flex">
|
||||
<view class="flex">
|
||||
<picker :value="selectDay.year + '-' + selectDay.month" @change="editMonth" mode="date" fields="month" class="year-month">
|
||||
{{ selectDay.year }}.{{ selectDay.month > 9 ? selectDay.month : '0' + selectDay.month }}
|
||||
</picker>
|
||||
<view class="icon" @tap="lastMonth" style="transform: rotate(180deg)">
|
||||
<view class="iconfont icon-playfill"></view>
|
||||
</view>
|
||||
<view class="icon" @tap="nextMonth">
|
||||
<view class="iconfont icon-playfill"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view @tap.stop.prevent="openChange" class="flex open">
|
||||
<view>{{ open ? '收起' : '展开' }}</view>
|
||||
<view style="margin-left: 6rpx; font-size: 20rpx" :class="'iconfont icon-' + (open ? 'fold' : 'unfold')"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日历头部 -->
|
||||
<view class="flex-around calendar-week">
|
||||
<view class="view">日</view>
|
||||
<view class="view">一</view>
|
||||
<view class="view">二</view>
|
||||
<view class="view">三</view>
|
||||
<view class="view">四</view>
|
||||
<view class="view">五</view>
|
||||
<view class="view">六</view>
|
||||
</view>
|
||||
|
||||
<!-- 日历主体 -->
|
||||
<view class="flex-start flex-wrap calendar-main" :style="'height:' + (dateList.length / 7) * 92 + 'rpx'">
|
||||
<view class="day" v-for="(item, index) in dateList" :key="index">
|
||||
<view
|
||||
:class="'bg ' + (item.year === selectDay.year && item.month === selectDay.month ? (item.day === selectDay.day ? 'select' : '') : 'other-month')"
|
||||
@tap.stop.prevent="selectChange"
|
||||
:data-day="item.day"
|
||||
:data-year="item.year"
|
||||
:data-month="item.month"
|
||||
:data-date-string="item.dateString"
|
||||
>
|
||||
{{ item.day }}
|
||||
</view>
|
||||
|
||||
<view class="spot" v-if="item.spot"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// colorui/components/calendar/calendar.js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dateList: [],
|
||||
|
||||
//日历主体渲染数组
|
||||
//选中时间
|
||||
selectDay: {
|
||||
year: '',
|
||||
month: '',
|
||||
day: ''
|
||||
},
|
||||
|
||||
open: ''
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
props: {
|
||||
spot: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
defaultTime: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
attached() {
|
||||
if (this.defaultTime) {
|
||||
let now = new Date(this.defaultTime);
|
||||
} else {
|
||||
let now = new Date();
|
||||
}
|
||||
let selectDay = {
|
||||
year: now.getFullYear(),
|
||||
month: now.getMonth() + 1,
|
||||
day: now.getDate(),
|
||||
dateString: this.formatTime(now, 'Y-M-D')
|
||||
};
|
||||
this.setMonth(selectDay.year, selectDay.month, selectDay.day);
|
||||
},
|
||||
|
||||
/**
|
||||
* 时间戳转化为年 月 日 时 分 秒
|
||||
* time: 需要被格式化的时间,可以被new Date()解析即可
|
||||
* format:格式化之后返回的格式,年月日时分秒分别为Y, M, D, h, m, s,这个参数不填的话则显示多久前
|
||||
*/
|
||||
formatTime(time, format) {
|
||||
function formatNumber(n) {
|
||||
n = n.toString();
|
||||
return n[1] ? n : '0' + n;
|
||||
}
|
||||
function getDate(time, format) {
|
||||
const formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
|
||||
const returnArr = [];
|
||||
const date = new Date(time);
|
||||
returnArr.push(date.getFullYear());
|
||||
returnArr.push(formatNumber(date.getMonth() + 1));
|
||||
returnArr.push(formatNumber(date.getDate()));
|
||||
returnArr.push(formatNumber(date.getHours()));
|
||||
returnArr.push(formatNumber(date.getMinutes()));
|
||||
returnArr.push(formatNumber(date.getSeconds()));
|
||||
for (const i in returnArr) {
|
||||
format = format.replace(formateArr[i], returnArr[i]);
|
||||
}
|
||||
return format;
|
||||
}
|
||||
function getDateDiff(time) {
|
||||
let r = '';
|
||||
const ft = new Date(time);
|
||||
const nt = new Date();
|
||||
const nd = new Date(nt);
|
||||
nd.setHours(23);
|
||||
nd.setMinutes(59);
|
||||
nd.setSeconds(59);
|
||||
nd.setMilliseconds(999);
|
||||
const d = parseInt((nd - ft) / 86400000);
|
||||
switch (true) {
|
||||
case d === 0:
|
||||
const t = parseInt(nt / 1000) - parseInt(ft / 1000);
|
||||
switch (true) {
|
||||
case t < 60:
|
||||
r = '刚刚';
|
||||
break;
|
||||
case t < 3600:
|
||||
r = parseInt(t / 60) + '分钟前';
|
||||
break;
|
||||
default:
|
||||
r = parseInt(t / 3600) + '小时前';
|
||||
}
|
||||
break;
|
||||
case d === 1:
|
||||
r = '昨天';
|
||||
break;
|
||||
case d === 2:
|
||||
r = '前天';
|
||||
break;
|
||||
case d > 2 && d < 30:
|
||||
r = d + '天前';
|
||||
break;
|
||||
default:
|
||||
r = getDate(time, 'Y-M-D');
|
||||
}
|
||||
return r;
|
||||
}
|
||||
if (!format) {
|
||||
return getDateDiff(time);
|
||||
} else {
|
||||
return getDate(time, format);
|
||||
}
|
||||
},
|
||||
|
||||
//picker设置月份
|
||||
editMonth(e) {
|
||||
const arr = e.detail.value.split('-');
|
||||
const year = parseInt(arr[0]);
|
||||
const month = parseInt(arr[1]);
|
||||
this.setMonth(year, month);
|
||||
},
|
||||
|
||||
//上月切换按钮点击
|
||||
lastMonth() {
|
||||
const lastMonth = new Date(this.selectDay.year, this.selectDay.month - 2);
|
||||
const year = lastMonth.getFullYear();
|
||||
const month = lastMonth.getMonth() + 1;
|
||||
this.setMonth(year, month);
|
||||
},
|
||||
|
||||
//下月切换按钮点击
|
||||
nextMonth() {
|
||||
const nextMonth = new Date(this.selectDay.year, this.selectDay.month);
|
||||
const year = nextMonth.getFullYear();
|
||||
const month = nextMonth.getMonth() + 1;
|
||||
this.setMonth(year, month);
|
||||
},
|
||||
|
||||
//设置月份
|
||||
setMonth(setYear, setMonth, setDay) {
|
||||
if (this.selectDay.year !== setYear || this.selectDay.month !== setMonth) {
|
||||
const day = Math.min(new Date(setYear, setMonth, 0).getDate(), this.selectDay.day);
|
||||
const time = new Date(setYear, setMonth - 1, setDay ? setDay : day);
|
||||
const data = {
|
||||
selectDay: {
|
||||
year: setYear,
|
||||
month: setMonth,
|
||||
day: setDay ? setDay : day,
|
||||
dateString: this.formatTime(time, 'Y-M-D')
|
||||
}
|
||||
};
|
||||
if (!setDay) {
|
||||
data.open = true;
|
||||
}
|
||||
this.setData(data);
|
||||
this.dateInit(setYear, setMonth);
|
||||
this.setSpot();
|
||||
this.$emit('change', {
|
||||
detail: this.selectDay
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
//展开收起
|
||||
openChange() {
|
||||
this.setData({
|
||||
open: !this.open
|
||||
});
|
||||
this.$emit('aaa', {
|
||||
detail: {
|
||||
a: 0
|
||||
}
|
||||
});
|
||||
this.dateInit();
|
||||
this.setSpot();
|
||||
},
|
||||
|
||||
//设置日历底下是否展示小圆点
|
||||
setSpot() {
|
||||
const timeArr = this.spot.map((item) => {
|
||||
return this.formatTime(item, 'Y-M-D');
|
||||
});
|
||||
this.dateList.forEach((item) => {
|
||||
if (timeArr.indexOf(item.dateString) !== -1) {
|
||||
item.spot = true;
|
||||
} else {
|
||||
item.spot = false;
|
||||
}
|
||||
});
|
||||
this.setData({
|
||||
dateList: this.dateList
|
||||
});
|
||||
},
|
||||
|
||||
//日历主体的渲染方法
|
||||
dateInit(setYear = this.selectDay.year, setMonth = this.selectDay.month) {
|
||||
let dateList = []; //需要遍历的日历数组数据
|
||||
let now = new Date(setYear, setMonth - 1); //当前月份的1号
|
||||
let startWeek = now.getDay(); //目标月1号对应的星期
|
||||
let dayNum = new Date(setYear, setMonth, 0).getDate(); //当前月有多少天
|
||||
let forNum = Math.ceil((startWeek + dayNum) / 7) * 7; //当前月跨越的周数
|
||||
if (this.open) {
|
||||
//展开状态,需要渲染完整的月份
|
||||
for (let i = 0; i < forNum; i++) {
|
||||
const now2 = new Date(now);
|
||||
now2.setDate(i - startWeek + 1);
|
||||
let obj = {};
|
||||
obj = {
|
||||
day: now2.getDate(),
|
||||
month: now2.getMonth() + 1,
|
||||
year: now2.getFullYear(),
|
||||
dateString: this.formatTime(now2, 'Y-M-D')
|
||||
};
|
||||
dateList[i] = obj;
|
||||
}
|
||||
} else {
|
||||
//非展开状态,只需要渲染当前周
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const now2 = new Date(now);
|
||||
//当前周的7天
|
||||
now2.setDate(Math.ceil((this.selectDay.day + startWeek) / 7) * 7 - 6 - startWeek + i);
|
||||
let obj = {};
|
||||
obj = {
|
||||
day: now2.getDate(),
|
||||
month: now2.getMonth() + 1,
|
||||
year: now2.getFullYear(),
|
||||
dateString: this.formatTime(now2, 'Y-M-D')
|
||||
};
|
||||
dateList[i] = obj;
|
||||
}
|
||||
}
|
||||
this.setData({
|
||||
dateList: dateList
|
||||
});
|
||||
},
|
||||
|
||||
//一天被点击时
|
||||
selectChange(e) {
|
||||
const year = e.currentTarget.dataset.year;
|
||||
const month = e.currentTarget.dataset.month;
|
||||
const day = e.currentTarget.dataset.day;
|
||||
const dateString = e.currentTarget.dataset.dateString;
|
||||
const selectDay = {
|
||||
year: year,
|
||||
month: month,
|
||||
day: day,
|
||||
dateString: dateString
|
||||
};
|
||||
if (this.selectDay.year !== year || this.selectDay.month !== month) {
|
||||
this.setMonth(year, month, day);
|
||||
} else if (this.selectDay.day !== day) {
|
||||
this.setData({
|
||||
selectDay: selectDay
|
||||
});
|
||||
this.$emit('change', {
|
||||
detail: this.selectDay
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// 处理小程序 attached 生命周期
|
||||
this.attached();
|
||||
},
|
||||
|
||||
watch: {
|
||||
spot: function (spot) {
|
||||
this.setSpot();
|
||||
}
|
||||
},
|
||||
|
||||
created: function () {}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
/* colorui/components/calendar/calendar.wxss */
|
||||
@font-face {
|
||||
font-family: 'iconfont';
|
||||
/* IE9 */
|
||||
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAL8AAsAAAAABxQAAAKvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqBVIFEATYCJAMQCwoABCAFhG0HQhswBsgekiRFQgQU8AMAmmCIh3/HXu9L8hFVWwXoyALpqqoqwAEfD6g66abEPBux/13TPwBUeZ0lAldI75LS0ctlzLY8NwVk3NQm5Co3lqhaWPE+z+X0pi+Q1bez3MZatCYtPuoFGAcU4F7UiyIroBPkltpFHBi88q89JtA0p1jEYe/wNMQryKpAPHbYMMT3CooStetCdeZgEd9U1NPX9ADgQf8+/kFlxJNUMrLx5LpHhPaf+GUsavP/JpxJQpHgHq8gYx0oxO1s41yUiMQoTWOibQN1dYmf+Dn0MhYO2zgfttC/PEKSiSrS3QabXjPzE3ON4GdII/FrrEsGhPjr/dYGcOyzM56gg6PM1BXvcbZIxY7zzRyS4znJs/m2VmjuRNq9edF1kcx9FbW85/nRgbXX6s5q/AX+vsGh/kBhUUHAYszwFx4acaAA6l7k9bgjo8Vbue4acDcbCqFqcBru+htf4l/I4EnzzQAa8lT+kNoAyG/TJuTdyDcsG+NB4r05rfm73irgx/jRrw4XMB+gyga24dQGHCqlg8RKyZ3cWuWLEWhFQlMTmOwwFLrCPwH3E+omB/I1sy2yunmysOuoaNlEVd0Omtb0HW8ZYaJEaWDVnoPQd4ek6zuyvkeysE+omPpCVT+i0HQaWRe2LIYjsYsRzImIrEvIIagKZQmeaHoniW1Owqzr0NIsYbqjjCpKy4ftPqIQdsUWfd5WyTlFlKky6nWeI5KkIo2pTiLwUjvnWmNZGZ37UKmgypDoAENgHCFCrJYgDgKVgsrxXOLo+5MImzkSjK3oFuazCEbnmB6pUKocQPZ5FZDuV97RzbOpxHEUQjEqGdLrzCMkEhWizc9zIgRcKfuBmqZRGR1Fod7S5/3yH56AJnJDiRQ5SlRU3yhUlcRoMStek/ASdUgSAAA=')
|
||||
format('woff2');
|
||||
/* iOS 4.1- */
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: 'iconfont' !important;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
line-height: 1;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.icon-unfold:before {
|
||||
content: '\e661';
|
||||
}
|
||||
|
||||
.icon-fold:before {
|
||||
content: '\e6de';
|
||||
}
|
||||
|
||||
.icon-playfill:before {
|
||||
content: '\e74f';
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.direction-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-start {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-end {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-around {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.align-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.align-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.align-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.calendar .title {
|
||||
font-size: 40rpx;
|
||||
color: #333;
|
||||
padding: 30rpx;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
|
||||
.calendar .title .year-month {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.calendar .title .icon {
|
||||
padding: 0 16rpx;
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.calendar .title .open {
|
||||
background-color: #f6f6f6;
|
||||
color: #999;
|
||||
font-size: 22rpx;
|
||||
line-height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
padding: 0 14rpx;
|
||||
}
|
||||
|
||||
.calendar .calendar-week {
|
||||
line-height: 40rpx;
|
||||
padding: 0 25rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.calendar .calendar-week .view {
|
||||
width: 100rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar .calendar-main {
|
||||
padding: 30rpx 25rpx;
|
||||
transition: height 0.3s;
|
||||
align-content: flex-start;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.calendar .calendar-main .day {
|
||||
position: relative;
|
||||
width: 100rpx;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
height: 66rpx;
|
||||
}
|
||||
|
||||
.calendar .calendar-main .day .bg {
|
||||
height: 56rpx;
|
||||
line-height: 56rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendar .calendar-main .day .select {
|
||||
width: 56rpx;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
background: linear-gradient(-60deg, #0fdac5, #1bc7b0);
|
||||
box-shadow: 0px 5px 16px 0px #c6f3ed;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.calendar .calendar-main .day .other-month {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.calendar .calendar-main .day .spot {
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
background-color: #1dcdb8;
|
||||
border-radius: 50%;
|
||||
margin: 6rpx auto 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
<template>
|
||||
<view style="height: 100%">
|
||||
<!-- colorui/components/canvas2d-ring/canvas2d-ring.wxml -->
|
||||
<canvas :style="'width:' + canvasWidth + 'px;height:' + canvasWidth + 'px; position:relative'" type="2d" id="myCanvas">
|
||||
<view class="circle-bar" :style="'height:' + canvasWidth + 'px;'">
|
||||
<view class="title_name">
|
||||
{{ title }}
|
||||
</view>
|
||||
<view
|
||||
class="title_val"
|
||||
:style="'color: ' + valueColor + '; font-weight:' + f_weight + '; margin-top:' + (show_tip ? '10' : '0') + 'rpx;font-size:' + f_size + 'px'"
|
||||
>
|
||||
{{ value }} {{ suffix }}
|
||||
</view>
|
||||
</view>
|
||||
</canvas>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// colorui/components/canvas2d-ring/canvas2d-ring.js
|
||||
var windWidth = uni.getSystemInfoSync().windowWidth;
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
canvasWidth: ' windWidth * 0.4',
|
||||
show_tip: true
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
props: {
|
||||
//画布的宽度 默认占屏幕宽度的0.4倍
|
||||
canvasWidth: {
|
||||
type: Number,
|
||||
default: windWidth * 0.4
|
||||
},
|
||||
//线条宽度 默认10
|
||||
lineWidth: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
//线条颜色
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#3696FA'
|
||||
},
|
||||
//标题 默认“完成率”
|
||||
title: {
|
||||
type: String,
|
||||
default: '完成率'
|
||||
},
|
||||
//当前的值 默认45
|
||||
value: {
|
||||
type: Number,
|
||||
default: 45
|
||||
},
|
||||
//值的颜色 默认""
|
||||
valueColor: {
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
//值的字体的大小颜色 默认28rpx
|
||||
f_size: {
|
||||
type: Number,
|
||||
default: 14
|
||||
},
|
||||
f_weight: {
|
||||
type: String,
|
||||
default: '500'
|
||||
},
|
||||
//最大值 默认100
|
||||
maxValue: {
|
||||
type: Number,
|
||||
default: 100
|
||||
},
|
||||
//最小值 默认0
|
||||
minValue: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
//当前值的后缀名
|
||||
suffix: {
|
||||
type: null,
|
||||
default: '%'
|
||||
},
|
||||
//从什么角度开始 0~360之间 (12点方向为0,18点方向为180,0点方向为360)
|
||||
startDegree: {
|
||||
type: Number,
|
||||
default: 180
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
showCanvasRing() {
|
||||
//没标题的时候去掉margin-top的值
|
||||
if (this.title.replace(/(^\s*)|(\s*$)/g, '').length == 0) {
|
||||
this.setData({
|
||||
show_tip: false
|
||||
});
|
||||
}
|
||||
//canvas 2d
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query
|
||||
.select('#myCanvas')
|
||||
.fields({
|
||||
node: true,
|
||||
size: true
|
||||
})
|
||||
.exec(this.init.bind(this));
|
||||
},
|
||||
init(res) {
|
||||
const canvas = res[0].node;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const dpr = uni.getSystemInfoSync().pixelRatio;
|
||||
canvas.width = res[0].width * dpr;
|
||||
canvas.height = res[0].height * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
// 大小值的计算
|
||||
var circle_r = this.canvasWidth / 2; //画布的一半,用来找中心点和半径
|
||||
var startDegree = this.startDegree; //从什么角度开始
|
||||
var maxValue = this.maxValue; //最大值
|
||||
var minValue = this.minValue; //最小值
|
||||
var value = this.value; //当前的值
|
||||
var lineColor = this.lineColor; //线条颜色
|
||||
var lineWidth = this.lineWidth; //线条宽度
|
||||
var percent = 360 * ((value - minValue) / (maxValue - minValue)); //计算结果
|
||||
|
||||
//定义起始点
|
||||
ctx.translate(circle_r, circle_r);
|
||||
//灰色圆弧
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = '#ebebeb';
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.arc(0, 0, circle_r - 10, 0, 2 * Math.PI, true);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
//有色彩的圆弧
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = lineColor;
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.arc(0, 0, circle_r - 10, (startDegree * Math.PI) / 180 - 0.5 * Math.PI, (percent * Math.PI) / 180 + (startDegree * Math.PI) / 180 - 0.5 * Math.PI, false);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
},
|
||||
created: function () {}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
/* colorui/components/canvas2d-ring/canvas2d-ring.wxss */
|
||||
.circle-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.circle-bar .title_name {
|
||||
max-height: 62rpx;
|
||||
font-size: 26rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
white-space: normal;
|
||||
}
|
||||
.circle-bar .title_val {
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<template>
|
||||
<view>
|
||||
<view class="cu-custom" :style="[{height:CustomBar + 'px'}]">
|
||||
<view class="cu-bar fixed" :style="style" :class="[bgImage!=''?'none-bg text-white bg-img':'',bgColor]">
|
||||
<view class="action" @tap="BackPage" v-if="isBack">
|
||||
<text class="cuIcon-back"></text>
|
||||
<slot name="backText"></slot>
|
||||
</view>
|
||||
<view class="content" :style="[{top:StatusBar + 'px'}]">
|
||||
<slot name="content"></slot>
|
||||
</view>
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
StatusBar: this.StatusBar,
|
||||
CustomBar: this.CustomBar
|
||||
};
|
||||
},
|
||||
name: 'cu-custom',
|
||||
computed: {
|
||||
style() {
|
||||
var StatusBar= this.StatusBar;
|
||||
var CustomBar= this.CustomBar;
|
||||
var bgImage = this.bgImage;
|
||||
var style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
|
||||
if (this.bgImage) {
|
||||
style = `${style}background-image:url(${bgImage});`;
|
||||
}
|
||||
return style
|
||||
}
|
||||
},
|
||||
props: {
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isBack: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
bgImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
BackPage() {
|
||||
if (getCurrentPages().length < 2 && 'undefined' !== typeof __wxConfig) {
|
||||
let url = '/' + __wxConfig.pages[0]
|
||||
return uni.redirectTo({url})
|
||||
}
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
<template>
|
||||
<!-- colorui/components/cu-custom.wxml -->
|
||||
<view :class="'cu-custom ' + (isLucency == true ? 'lucency' : '')" :style="'height:' + (isLucency == true ? 0 : CustomBar) + 'px'">
|
||||
<view
|
||||
:class="'cu-bar ' + (noFixed ? '' : 'fixed') + ' ' + (bgImage != '' ? 'none-bg text-white bg-img' : '') + ' ' + bgColor"
|
||||
:style="'height:' + CustomBar + 'px;padding-top:' + StatusBar + 'px;' + (bgImage ? 'background-image:url( + bgImage+)' : '')"
|
||||
>
|
||||
<view class="action" @tap="BackPage" v-if="isBack && mode != 'singlePage'">
|
||||
<text class="cuIcon-back"></text>
|
||||
<slot name="backText"></slot>
|
||||
</view>
|
||||
<view class="action" @tap="toHome" :data-url="homePage" v-if="isBack && mode == 'singlePage'">
|
||||
<text class="cuIcon-home padding-left-sm"></text>
|
||||
<slot name="homeText"></slot>
|
||||
</view>
|
||||
<view
|
||||
class="action border-custom"
|
||||
v-if="isCustom"
|
||||
:style="'width:' + Custom.width + 'px;height:' + Custom.height + 'px;margin-left:calc(750rpx - ' + Custom.right + 'px)'"
|
||||
>
|
||||
<text class="cuIcon-back" @tap="BackPage"></text>
|
||||
<text class="cuIcon-homefill" @tap="toHome" :data-url="homePage"></text>
|
||||
</view>
|
||||
<view class="content" :style="'top:' + StatusBar + 'px'">
|
||||
<slot name="content"></slot>
|
||||
</view>
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// colorui/components/cu-custom.js
|
||||
const app = getApp();
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
StatusBar: app.globalData.StatusBar,
|
||||
CustomBar: app.globalData.CustomBar,
|
||||
Custom: app.globalData.Custom,
|
||||
mode: 'default'
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 组件的一些选项
|
||||
*/
|
||||
options: {
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true
|
||||
},
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
props: {
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isCustom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isBack: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
bgImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isLucency: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
noFixed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
homePage: {
|
||||
type: String,
|
||||
default: '/pages/index/index'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
handlePageShow() {
|
||||
this.getInfo();
|
||||
},
|
||||
|
||||
BackPage() {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
},
|
||||
|
||||
toHome(e) {
|
||||
if (e.currentTarget.dataset.url != '') {
|
||||
uni.reLaunch({
|
||||
url: e.currentTarget.dataset.url
|
||||
});
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getInfo() {
|
||||
var that = this;
|
||||
if (getCurrentPages().length === 1) {
|
||||
that.setData({
|
||||
mode: 'singlePage'
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
/* colorui/components/cu-custom.wxss */
|
||||
.lucency {
|
||||
/* position: absolute;
|
||||
z-index: 1; */
|
||||
background-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
<template>
|
||||
<view style="height: 100%">
|
||||
<!-- colorui/components/skeleton/skeleton.wxml -->
|
||||
<view v-if="loading" class="box padding">
|
||||
<view v-if="avatar" class="bg avatar"></view>
|
||||
<view class="column">
|
||||
<view
|
||||
:class="(active ? 'active' : '') + ' bg list'"
|
||||
:style="'width: ' + columnWidth[index] + ';height:' + (index + 1 == column && endHeight ? endHeight : '')"
|
||||
v-for="(item, index) in column"
|
||||
:key="index"
|
||||
></view>
|
||||
</view>
|
||||
</view>
|
||||
<slot v-else />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// colorui/components/skeleton/skeleton.js
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
options: {
|
||||
addGlobalClass: true
|
||||
},
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
columnWidth: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
endHeight: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
avatar: Boolean
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {},
|
||||
created: function () {}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
/* colorui/components/skeleton/skeleton.wxss */
|
||||
.box {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.column > view {
|
||||
height: 36rpx;
|
||||
margin-bottom: 15rpx;
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.bg {
|
||||
background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 37%, #f2f2f2 63%);
|
||||
background-size: 400% 100%;
|
||||
}
|
||||
|
||||
.active {
|
||||
animation: loading 1.4s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 扩展头像功能 */
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
float: left;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
</style>
|
||||
168
colorui/dark.css
|
|
@ -1,168 +0,0 @@
|
|||
/* 暗黑模式下应用的样式 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
page {
|
||||
/* 暗黑模式背景色 */
|
||||
--darkBlack: #111111;
|
||||
--lightGray: #222222;
|
||||
--darkGray: #333333;
|
||||
/* 暗黑模式按钮颜色 */
|
||||
--btnGray: #444444;
|
||||
/* 暗黑模式字体颜色 */
|
||||
--darkTitle: rgba(255, 255, 255, 0.85);
|
||||
--darkPrimaryText: rgba(255, 255, 255, 0.65);
|
||||
--darkSecondaryText: rgba(255, 255, 255, 0.45);
|
||||
--darkDisable: rgba(255, 255, 255, 0.1);
|
||||
/* 暗黑模式渐变色 */
|
||||
--darkGradualRed: linear-gradient(45deg, #f43f3b, #ec008c);
|
||||
--darkGradualOrange: linear-gradient(45deg, #ff9700, #ed1c24);
|
||||
--darkGradualGreen: linear-gradient(45deg, #39b54a, #8dc63f);
|
||||
--darkGradualPurple: linear-gradient(45deg, #9000ff, #5e00ff);
|
||||
--darkGradualPink: linear-gradient(45deg, #ec008c, #6739b6);
|
||||
--darkGradualBlue: linear-gradient(45deg, #112a45, #1765ad);
|
||||
/* 阴影透明色 */
|
||||
--ShadowSize: 6rpx 6rpx 8rpx;
|
||||
--redShadow: rgba(204, 69, 59, 0.8);
|
||||
--orangeShadow: rgba(217, 109, 26, 0.8);
|
||||
--yellowShadow: rgba(224, 170, 7, 0.8);
|
||||
--oliveShadow: rgba(124, 173, 55, 0.8);
|
||||
--greenShadow: rgba(48, 156, 63, 0.8);
|
||||
--cyanShadow: rgba(28, 187, 180, 0.8);
|
||||
--blueShadow: rgba(0, 102, 204, 0.8);
|
||||
--purpleShadow: rgba(88, 48, 156, 0.8);
|
||||
--mauveShadow: rgba(133, 33, 150, 0.8);
|
||||
--pinkShadow: rgba(199, 50, 134, 0.8);
|
||||
--brownShadow: rgba(140, 88, 53, 0.8);
|
||||
--greyShadow: rgba(114, 130, 138, 0.8);
|
||||
--grayShadow: rgba(114, 130, 138, 0.8);
|
||||
--blackShadow: rgba(26, 26, 26, 0.8);
|
||||
}
|
||||
|
||||
page {
|
||||
background-color: var(--darkBlack);
|
||||
color: var(--darkPrimaryText);
|
||||
}
|
||||
|
||||
.bg-gradual-blue {
|
||||
background-image: var(--darkGradualBlue);
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
color: var(--gray);
|
||||
background-color: var(--darkGray);
|
||||
}
|
||||
|
||||
.cu-list.menu > .cu-item {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.text-black,
|
||||
.line-black,
|
||||
.lines-black {
|
||||
color: var(--darkTitle);
|
||||
}
|
||||
|
||||
.solids::after {
|
||||
border: 8rpx solid var(--darkDisable);
|
||||
}
|
||||
|
||||
.solids-top::after {
|
||||
border-top: 8rpx solid var(--darkDisable);
|
||||
}
|
||||
|
||||
.solids-right::after {
|
||||
border-right: 8rpx solid var(--darkDisable);
|
||||
}
|
||||
|
||||
.solids-bottom::after {
|
||||
border-bottom: 8rpx solid var(--darkDisable);
|
||||
}
|
||||
|
||||
.solids-left::after {
|
||||
border-left: 8rpx solid var(--darkDisable);
|
||||
}
|
||||
|
||||
.cu-bar .search-form {
|
||||
background-color: var(--lightGray);
|
||||
color: var(--darkPrimaryText);
|
||||
}
|
||||
|
||||
.cu-list.grid {
|
||||
background-color: var(--darkGray);
|
||||
}
|
||||
|
||||
.cu-btn:not([class*='bg-']) {
|
||||
background-color: var(--btnGray);
|
||||
}
|
||||
|
||||
radio.radio[checked]::after {
|
||||
border: 8px solid var(--darkDisable) !important;
|
||||
}
|
||||
|
||||
.cu-progress {
|
||||
background-color: #424242;
|
||||
}
|
||||
|
||||
.cu-progress view {
|
||||
color: var(--darkPrimaryText);
|
||||
}
|
||||
|
||||
.cu-load.load-modal {
|
||||
background-color: var(--lightGray);
|
||||
color: var(--darkPrimaryText);
|
||||
}
|
||||
|
||||
.cu-load.load-modal::after {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-bar {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-bar,
|
||||
.cu-bar.input {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-chat .cu-item > .main .content:not([class*='bg-']) {
|
||||
background-color: var(--lightGray);
|
||||
color: var(--darkPrimaryText);
|
||||
}
|
||||
|
||||
.cu-chat .cu-info {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: var(--darkPrimaryText);
|
||||
}
|
||||
|
||||
.cu-list.menu-avatar > .cu-item {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-card.dynamic > .cu-item {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-card > .cu-item {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-timeline {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-timeline > .cu-item::before {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-timeline > .cu-item[class*='cuIcon-']::before {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-form-group {
|
||||
background-color: var(--lightGray);
|
||||
}
|
||||
|
||||
.cu-form-group + .cu-form-group {
|
||||
border-top: 1rpx solid var(--darkDisable);
|
||||
}
|
||||
}
|
||||
1221
colorui/icon.css
6516
colorui/main.css
|
|
@ -1,171 +0,0 @@
|
|||
<template>
|
||||
<view>
|
||||
<uni-popup :mask-click="false" ref="addForm" mode="center" >
|
||||
<!-- 新增:修改运行时间的弹窗 -->
|
||||
<view class="modal-container">
|
||||
<view class="modal-title">{{ `添加大棚` }}</view>
|
||||
<uni-forms ref="form" :model="formData" :rules="rules" label-width="180rpx" validateTrigger="blur">
|
||||
<uni-forms-item label="大棚名称:" name="agriName" required>
|
||||
<uni-easyinput type="text" v-model="formData.agriName" placeholder="请输入大棚名称" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="设备imei:" name="imei" required>
|
||||
<uni-easyinput v-model="formData.imei" placeholder="在此输入设备imei号" >
|
||||
<template #right>
|
||||
<yt-scanCode @getScanCode="getScanCode"></yt-scanCode>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
|
||||
<view class="modal-input-wrap" >
|
||||
<text class="modal-label"></text>
|
||||
<uni-icons type="info" size="15" color="#666"/>
|
||||
<text class="modal-text">可直接点击右侧扫描设备二维码;<br>
|
||||
如若失效,亦可手动输入设备imei号。</text>
|
||||
</view>
|
||||
<view class="modal-btn-wrap">
|
||||
<button class="modal-btn cancel" @click="close">取消</button>
|
||||
<button class="modal-btn confirm" @click="confirm">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import UniPopup from "../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
|
||||
import {addAgriMobile} from "../../api/system/assets/agri";
|
||||
import {updateSubscribeTopic} from "../../utils/mqtt";
|
||||
import {batchUnsubscribe} from "../../api/system/mqtt";
|
||||
import * as mqttUtil from "../../utils/mqtt";
|
||||
|
||||
export default {
|
||||
name: "addAgri",
|
||||
components: {UniPopup},
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
agriName: null,
|
||||
imei: null,
|
||||
sourceCode:0
|
||||
},
|
||||
rules: {
|
||||
agriName: {
|
||||
rules:[
|
||||
{
|
||||
required: true,
|
||||
errorMessage: '请输入温室名称!',
|
||||
}
|
||||
]
|
||||
},
|
||||
// 对name字段进行必填验证
|
||||
imei: {
|
||||
// name 字段的校验规则
|
||||
rules:[
|
||||
// 校验 name 不能为空
|
||||
{
|
||||
required: true,
|
||||
errorMessage: '请输入设备imei!',
|
||||
},
|
||||
// 对name字段进行长度验证
|
||||
{
|
||||
minLength: 15,
|
||||
maxLength: 15,
|
||||
errorMessage: '{label}不合法!需为 {maxLength} 位!',
|
||||
},
|
||||
{
|
||||
pattern: '^\\d{15}$', // 核心正则
|
||||
errorMessage: '请输入15位纯数字编号'
|
||||
}
|
||||
],
|
||||
// 当前表单域的字段中文名,可不填写
|
||||
label:'设备imei'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
close() {
|
||||
this.$refs.addForm.close();
|
||||
},
|
||||
confirm() {
|
||||
this.$refs.form.validate().then(res => {
|
||||
addAgriMobile(this.formData).then((response) => {
|
||||
if (response.code===200) {
|
||||
uni.showModal({
|
||||
title: `操作提示`,
|
||||
content: `${response.data.msg}`,
|
||||
showCancal: false,
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
// 4. 只有用户点击弹窗的“确定”后,才执行后续操作
|
||||
if (res.confirm) {
|
||||
this.$emit("reload"); // 向父组件传值触发刷新
|
||||
batchUnsubscribe({clientId: mqttUtil.getMqttState().clientId}).then(response => {
|
||||
|
||||
}).finally(() => {
|
||||
updateSubscribeTopic();
|
||||
})
|
||||
this.close(); // 关闭添加大棚的弹窗(移到这里)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}).catch(err => {
|
||||
return
|
||||
})
|
||||
},
|
||||
open() {
|
||||
this.formData = {
|
||||
agriName: null,
|
||||
imei: null,
|
||||
sourceCode:0
|
||||
};
|
||||
this.$refs.addForm.open();
|
||||
},
|
||||
getScanCode(res){
|
||||
this.formData.imei = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped >
|
||||
|
||||
.modal-input-wrap:nth-child(2) {
|
||||
margin-bottom: 35rpx;
|
||||
}
|
||||
.modal-btn-wrap {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
.modal-btn {
|
||||
flex: 1;
|
||||
height: 70rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.modal-btn.cancel {
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
.modal-btn.confirm {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
.modal-text {
|
||||
font-size: 22rpx;
|
||||
color: #6C6C6C;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
/deep/ .uni-forms-item__label {
|
||||
padding: 0!important;
|
||||
margin-left: 20rpx;
|
||||
width: 154rpx!important;
|
||||
}
|
||||
|
||||
/deep/ .uni-forms-item:nth-child(2) {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<template>
|
||||
<view class="refresher-container">
|
||||
<!-- 这里的图片请换成自己项目的图片 -->
|
||||
<image class="refresher-image" mode="aspectFit" src="https://img.xiaoces.com/photos/refresher_loading.gif"></image>
|
||||
<image class="refresher-image" mode="aspectFit" src="@/static/refresher_loading.gif"></image>
|
||||
<text class="refresher-text">{{statusText}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<view class="container" :style="{padding: linePadding}">
|
||||
<!-- 带文字的分割线 -->
|
||||
<view class="divider" :style="{margin: lineMargin,width: width}">
|
||||
<view class="divider" :style="{margin: lineMargin}">
|
||||
<view class="line" :style="{height: lineHeight,backgroundColor: lineColor}"></view>
|
||||
<text :style="{padding: textPadding,fontSize: fontSize,color: fontColor,fontWeight: fontWeight}">{{ lineText }}</text>
|
||||
<view class="line" :style="{height: lineHeight,backgroundColor: lineColor,}"></view>
|
||||
|
|
@ -60,24 +60,15 @@ export default {
|
|||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.line {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module.exports = {
|
|||
// 应用版本
|
||||
version: "1.2.0",
|
||||
// 应用logo
|
||||
logo: "https://img.xiaoces.com/photos/logo200.png",
|
||||
logo: "/static/logo200.png",
|
||||
// 官方网站
|
||||
site_url: "http://ruoyi.vip",
|
||||
// 政策协议
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
<template>
|
||||
<view class="demo-title">
|
||||
<view>
|
||||
<view v-if="type === 'first'" class="main_title">
|
||||
<view v-if="leftIcon" class="main_title__icon main_title__icon--left" :class="[`tn-icon-${leftIcon}`]"></view>
|
||||
<view class="main_title__content">{{ title }}</view>
|
||||
<view v-if="rightIcon" class="main_title__icon main_title__icon--right" :class="[`tn-icon-${rightIcon}`]"></view>
|
||||
</view>
|
||||
<view v-if="type === 'second'" class="second_title">
|
||||
<view class="second_title__content">{{ title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content" :class="[{
|
||||
'content--padding': contentPadding
|
||||
}]">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'demo-title',
|
||||
options: {
|
||||
// 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
|
||||
virtualHost: true
|
||||
},
|
||||
props: {
|
||||
// 标题类型
|
||||
type: {
|
||||
type: String,
|
||||
default: 'first'
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 左图标
|
||||
leftIcon: {
|
||||
type: String,
|
||||
default: 'star'
|
||||
},
|
||||
// 右图标
|
||||
rightIcon: {
|
||||
type: String,
|
||||
default: 'star'
|
||||
},
|
||||
// 内容容器是否有两边边距
|
||||
contentPadding: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main_title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 50rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
|
||||
&__content {
|
||||
padding: 0 18rpx;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.second_title {
|
||||
margin: 24rpx 0;
|
||||
margin-left: 30rpx;
|
||||
|
||||
&__content {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 30rpx;
|
||||
|
||||
&--padding {
|
||||
margin-left: 30rpx;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,689 +0,0 @@
|
|||
<template>
|
||||
<view class="dynamic-demo">
|
||||
|
||||
<!-- 效果预览窗口 -->
|
||||
<view v-if="!noDemo" class="demo-container" :class="{'demo-container--full': full}">
|
||||
<view class="demo">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<!-- 提示信息 -->
|
||||
<view v-if="haveTips">
|
||||
<view class="demo__tips__icon" @click="demoTipsClick">
|
||||
<view class="icon tn-icon-help"></view>
|
||||
</view>
|
||||
<view class="demo__tips__content"
|
||||
:class="[showContentTips ? 'demo__tips__content--show' : 'demo__tips__content--hide']">
|
||||
<view v-for="(item,index) in tipsData" :key="index" class="demo__tips__content--item">{{ item }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 模式切换 -->
|
||||
<view v-if="multiMode" class="mode-switch">
|
||||
<view class="mode-switch__container">
|
||||
<view v-for="(item, index) in sectionModeListInfos" :key="index" class="mode-switch__item"
|
||||
:class="[`mode-switch-item-${index}`,{'mode-switch__item--active': modeIndex === index}]"
|
||||
@click="switchMode(index)">{{ item.name }}</view>
|
||||
|
||||
<!-- 滑块样式 -->
|
||||
<view class="mode-switch__slider" :style="[modeSwitchSliderStyle]"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 组件对应可选项容器 -->
|
||||
<view class="section-container">
|
||||
<scroll-view
|
||||
class="section__scroll-view"
|
||||
:class="{'section__scroll-view--auto': sectionScrollViewStyle.height === 'auto'}"
|
||||
:style="[sectionScrollViewStyle]"
|
||||
:scroll-y="sectionScrollViewStyle.height !== 'auto'"
|
||||
>
|
||||
<block v-for="(item,index) in btnsList" :key="index">
|
||||
<view class="section__content" :class="{'section__content--visible': item.show}">
|
||||
<view class="section__content__title">
|
||||
<view class="section__content__title__left-line" :class="[`tn-main-gradient-${tuniaoColorList[index]}`]"></view>
|
||||
<view class="section__content__title--text tn-text-ellipsis" :class="[`tn-main-gradient-${tuniaoColorList[index]}`]">{{ item.title }}</view>
|
||||
<view class="section__content__title__right-line" :class="[`tn-main-gradient-${tuniaoColorList[index]}`]"></view>
|
||||
</view>
|
||||
<view class="section__content__btns">
|
||||
<view v-for="(section_btn,section_index) in item.optional" :key="section_index"
|
||||
class="section__content__btns__item" :class="[`tn-main-gradient-${tuniaoColorList[index]}--light`]" @click="sectionBtnClick(index, section_index)">
|
||||
<view class="section__content__btns__item__bg"
|
||||
:class="[`tn-main-gradient-${tuniaoColorList[index]}`, {'section__content__btns__item__bg--active':sectionIndex[modeIndex][index]['value'] === section_index}]"></view>
|
||||
<view class="section__content__btns__item--text tn-text-ellipsis"
|
||||
:class="[sectionIndex[modeIndex][index]['value'] === section_index ? 'section__content__btns__item--text--active' : `tn-color-${tuniaoColorList[index]}`]">{{ section_btn }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'dynamic-demo-template',
|
||||
props: {
|
||||
// 可选项列表数据
|
||||
sectionList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 提示信息
|
||||
tips: {
|
||||
type: [String, Array],
|
||||
default: ''
|
||||
},
|
||||
// 演示框的内容是否为铺满
|
||||
full: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否使用了自定义顶部导航栏
|
||||
customBar: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否全屏滚动
|
||||
fullWindowsScroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 没有演示内容
|
||||
noDemo: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tipsData() {
|
||||
if (typeof this.tips === 'string') {
|
||||
return [this.tips]
|
||||
}
|
||||
return this.tips
|
||||
},
|
||||
haveTips() {
|
||||
return this.tips && this.tips.length > 0
|
||||
},
|
||||
multiMode() {
|
||||
return this.sectionList.length > 1
|
||||
},
|
||||
sectionModeList() {
|
||||
return this.sectionList.map((item) => {
|
||||
return item.name
|
||||
})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 图鸟颜色列表
|
||||
tuniaoColorList: this.$tn.color.getTuniaoColorList(),
|
||||
// 保存选项列表信息(由于prop中的数据时不能被修改的)
|
||||
_sectionList: [],
|
||||
// 模式列表信息
|
||||
sectionModeListInfos: [],
|
||||
// 所选模式的序号
|
||||
modeIndex: 0,
|
||||
// 模式选择滑块样式
|
||||
modeSwitchSliderStyle: {
|
||||
width: 0,
|
||||
left: 0
|
||||
},
|
||||
// 显示组件相关提示信息
|
||||
showContentTips: false,
|
||||
// 可选项滚动容器样式
|
||||
sectionScrollViewStyle: {
|
||||
height: 0
|
||||
},
|
||||
// 按钮列表信息
|
||||
btnsList: [],
|
||||
// 标记当前所选按钮
|
||||
sectionIndex: [],
|
||||
// 标记选项按钮是否可以滑动(使用scroll-view进行包裹)
|
||||
sectionScrollFlag: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
sectionList: {
|
||||
handler(value) {
|
||||
// 如果sectionList发生改变,重新初始化选项列表信息
|
||||
this.initSectionBtns()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
sectionScrollFlag(value) {
|
||||
if (!value) {
|
||||
this.sectionScrollViewStyle.height = 'auto'
|
||||
}
|
||||
},
|
||||
fullWindowsScroll: {
|
||||
handler(value) {
|
||||
if (value) {
|
||||
this.sectionScrollViewStyle.height = 'auto'
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化可选项模式列表
|
||||
this.sectionModeListInfos = this.sectionModeList.map((item) => {
|
||||
return {
|
||||
name: item
|
||||
}
|
||||
})
|
||||
// 初始化选项按钮默认信息
|
||||
this.initSectionBtns()
|
||||
},
|
||||
mounted() {
|
||||
// 等待加载组件完成
|
||||
// setTimeout(() => {
|
||||
// // 计算出底部scroll-view的高度
|
||||
// this.initSectionScrollView()
|
||||
|
||||
// if (this.multiMode) {
|
||||
// // 获取模式切换标签的信息
|
||||
// this.getModeTabsInfo()
|
||||
// }
|
||||
// }, 10)
|
||||
this.$nextTick(() => {
|
||||
// 计算出底部scroll-view的高度
|
||||
this.initSectionScrollView()
|
||||
|
||||
if (this.multiMode) {
|
||||
// 获取模式切换标签的信息
|
||||
this.getModeTabsInfo()
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 初始化选项滑动窗口的高度
|
||||
initSectionScrollView() {
|
||||
// 全屏滚动时不进行任何的操作
|
||||
if (this.fullWindowsScroll) {
|
||||
return
|
||||
}
|
||||
// 获取屏幕的高度
|
||||
uni.getSystemInfo({
|
||||
success: (systemInfo) => {
|
||||
// 通过当前屏幕的安全高度减去上一个元素的底部和距离上一个元素的外边距,然后减获取到的值减去标题栏的高度即可
|
||||
const navBarHeight = this.customBar ? 0 : this.vuex_custom_bar_height
|
||||
if (this.multiMode) {
|
||||
uni.createSelectorQuery().in(this).select('.mode-switch').boundingClientRect(data => {
|
||||
if (data.bottom >= systemInfo.safeArea.height) {
|
||||
this.sectionScrollFlag = false
|
||||
} else {
|
||||
this.sectionScrollFlag = true
|
||||
const containerBaseHeight = systemInfo.safeArea.height - data.bottom
|
||||
this.sectionScrollViewStyle.height = (containerBaseHeight - navBarHeight) + systemInfo.statusBarHeight - uni.upx2px(75) + 'px'
|
||||
}
|
||||
}).exec()
|
||||
} else {
|
||||
if (!this.noDemo) {
|
||||
uni.createSelectorQuery().in(this).select('.demo-container').boundingClientRect(data => {
|
||||
if (data.bottom >= systemInfo.safeArea.height) {
|
||||
this.sectionScrollFlag = false
|
||||
} else {
|
||||
this.sectionScrollFlag = true
|
||||
const containerBaseHeight = systemInfo.safeArea.height - data.bottom
|
||||
this.sectionScrollViewStyle.height = (containerBaseHeight - navBarHeight) + systemInfo.statusBarHeight - uni.upx2px(75) + 'px'
|
||||
}
|
||||
}).exec()
|
||||
} else {
|
||||
this.sectionScrollFlag = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
// 更新选项滑动容器的高度
|
||||
updateSectionScrollView() {
|
||||
this.$nextTick(() => {
|
||||
this.initSectionScrollView()
|
||||
})
|
||||
},
|
||||
// 获取各个模式tab的节点信息
|
||||
getModeTabsInfo() {
|
||||
let view = uni.createSelectorQuery().in(this)
|
||||
for (let i = 0; i < this.sectionModeListInfos.length; i++) {
|
||||
view.select('.mode-switch-item-' + i).boundingClientRect()
|
||||
}
|
||||
view.exec(res => {
|
||||
// 如果没有获取到,则重新获取
|
||||
if (!res.length) {
|
||||
setTimeout(() => {
|
||||
this.getModeTabsInfo()
|
||||
}, 10)
|
||||
return
|
||||
}
|
||||
// 将每个模式的宽度放入list中
|
||||
res.map((item, index) => {
|
||||
this.sectionModeListInfos[index].width = item.width
|
||||
})
|
||||
// 初始化滑块的宽度
|
||||
this.modeSwitchSliderStyle.width = this.sectionModeListInfos[0].width + 'px'
|
||||
|
||||
// 初始化滑块的位置
|
||||
this.modeSliderPosition()
|
||||
})
|
||||
},
|
||||
// 设置模式滑块的位置
|
||||
modeSliderPosition() {
|
||||
let left = 0
|
||||
// 计算当前所选模式选项到组件左边的距离
|
||||
this.sectionModeListInfos.map((item, index) => {
|
||||
if (index < this.modeIndex) left += item.width
|
||||
})
|
||||
|
||||
this.modeSwitchSliderStyle.left = left + 'px'
|
||||
},
|
||||
// 切换模式
|
||||
switchMode(index) {
|
||||
// 不允许点击当前激活的选项
|
||||
if (index === this.modeIndex) return
|
||||
this.modeIndex = index
|
||||
this.modeSliderPosition()
|
||||
this.updateSectionBtns()
|
||||
this.$emit('modeClick', {
|
||||
index: index
|
||||
})
|
||||
},
|
||||
// 点击内容提示信息
|
||||
demoTipsClick() {
|
||||
this.showContentTips = !this.showContentTips
|
||||
},
|
||||
// 初始化被选中选项按钮
|
||||
initSectionBtns() {
|
||||
this.sectionIndex = []
|
||||
this.sectionIndex = this.sectionList.map((item) => {
|
||||
if (item.hasOwnProperty('section') && item.section.length > 0) {
|
||||
return Array(item.section.length).fill({
|
||||
value: 0,
|
||||
change: false
|
||||
})
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
this._sectionList = this.$tn.deepClone(this.sectionList)
|
||||
// 给本地选项按钮列表给默认show属性
|
||||
this._sectionList.map((item) => {
|
||||
const section = item.section.map((section_item) => {
|
||||
if (!section_item.hasOwnProperty('show')) {
|
||||
section_item.show = true
|
||||
}
|
||||
return section_item
|
||||
})
|
||||
item.section = section
|
||||
return item
|
||||
})
|
||||
|
||||
// 更新按钮信息
|
||||
this.updateSectionBtns()
|
||||
},
|
||||
// 跟新选项按钮信息
|
||||
updateSectionBtns(sectionIndex = -1, showState = true) {
|
||||
let sectionOptional = this._sectionList[this.modeIndex]['section']
|
||||
this.btnsList = sectionOptional.map((item, index) => {
|
||||
// 判断是否已经修改了对应的值
|
||||
let changeValue = this.sectionIndex[this.modeIndex][index]['change'] || false
|
||||
let currentSectionIndexValue = this.sectionIndex[this.modeIndex][index]['value'] || 0
|
||||
// 取出默认值(如果是已经修改过的选项,则使用之前的选项信息)
|
||||
let indexValue = changeValue ? currentSectionIndexValue : item.hasOwnProperty('current') ? item.current : 0
|
||||
// 取出是否显示当前选项
|
||||
let show = (sectionIndex !== -1 && sectionIndex === index) ? showState : item.hasOwnProperty('show') ? item.show : true
|
||||
// 处理最大最小值
|
||||
if (indexValue < 0) {
|
||||
indexValue = 0
|
||||
}
|
||||
if (indexValue >= item.optional.length) {
|
||||
indexValue = item.optional.length
|
||||
}
|
||||
// this.sectionIndex[this.modeIndex][index]['value'] = indexValue
|
||||
this.$set(this.sectionIndex[this.modeIndex], index, {value: indexValue, change: changeValue})
|
||||
item.show = show
|
||||
return item
|
||||
})
|
||||
},
|
||||
// 更新选项按钮状态信息
|
||||
updateSectionBtnsState(sectionIndex = -1, showState = true) {
|
||||
// 判断sectionIndex是否为数组
|
||||
if (this.$tn.array.isArray(sectionIndex)) {
|
||||
if (sectionIndex.length === 0) {
|
||||
return
|
||||
}
|
||||
sectionIndex = sectionIndex.filter((item) => item >= 0 && item < this.sectionList[this.modeIndex]['section'].length)
|
||||
sectionIndex.map((item) => {
|
||||
this.btnsList[item]['show'] = showState
|
||||
this._sectionList[this.modeIndex]['section'][item]['show'] = showState
|
||||
})
|
||||
} else {
|
||||
if (sectionIndex < 0 || sectionIndex >= this.sectionList[this.modeIndex]['section'].length) {
|
||||
return
|
||||
}
|
||||
// 将按键的对应显示状态设置为对应的状态
|
||||
this.btnsList[sectionIndex]['show'] = showState
|
||||
this._sectionList[this.modeIndex]['section'][sectionIndex]['show'] = showState
|
||||
}
|
||||
|
||||
},
|
||||
// 更新选项按钮选中信息
|
||||
updateSectionBtnsValue(modeIndex = 0, sectionIndex = -1, value = 0) {
|
||||
if (sectionIndex < 0 || sectionIndex >= this.sectionList[modeIndex]['section'].length) {
|
||||
return
|
||||
}
|
||||
// 如果showState为false则移除对应的选项按钮,否则往对应的位置添加上对应的选项按钮
|
||||
this.sectionIndex[modeIndex][sectionIndex] = {
|
||||
value,
|
||||
change: true
|
||||
}
|
||||
},
|
||||
// 选项按钮点击事件
|
||||
sectionBtnClick(index, sectionIndex) {
|
||||
// if (this.sectionIndex[this.modeIndex][index] === sectionIndex) {
|
||||
// return
|
||||
// }
|
||||
this.$set(this.sectionIndex[this.modeIndex], index, {value: sectionIndex, change: true})
|
||||
this.$emit('click', {
|
||||
methods: this.btnsList[index]['methods'],
|
||||
index: sectionIndex,
|
||||
name: this.btnsList[index]['optional'][sectionIndex]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dynamic-demo {
|
||||
padding-top: 78rpx;
|
||||
|
||||
/* 顶部模式切换start */
|
||||
.mode-switch {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 75rpx;
|
||||
padding: 0 30rpx;
|
||||
|
||||
&__container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 476rpx;
|
||||
height: 62rpx;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0rpx 10rpx 50rpx 0rpx rgba(0, 3, 72, 0.1);
|
||||
border-radius: 31rpx;
|
||||
}
|
||||
|
||||
&__item {
|
||||
flex: 1;
|
||||
height: 62rpx;
|
||||
width: 100%;
|
||||
line-height: 62rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: $tn-font-sub-color;
|
||||
z-index: 2;
|
||||
transition: all 0.3s;
|
||||
|
||||
&--active {
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
&__slider {
|
||||
position: absolute;
|
||||
height: 62rpx;
|
||||
border-radius: 31rpx;
|
||||
// background-image: linear-gradient(-86deg, #FF8359 0%, #FFDF40 100%);
|
||||
background-image: linear-gradient(-86deg, #00C3FF 0%, #58FFF5 100%);
|
||||
box-shadow: 1rpx 10rpx 24rpx 0rpx #00C3FF77;
|
||||
z-index: 1;
|
||||
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
}
|
||||
|
||||
/* 顶部模式切换end */
|
||||
|
||||
/* 演示内容展示start */
|
||||
.demo-container {
|
||||
min-height: 327rpx;
|
||||
width: calc(100% - 60rpx);
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0rpx 10rpx 50rpx 0rpx rgba(0, 3, 72, 0.1);
|
||||
margin: 0 30rpx 5rpx 30rpx;
|
||||
border-radius: 20rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&--full {
|
||||
display: inline-block;
|
||||
padding-bottom: 20rpx;
|
||||
min-height: 0rpx;
|
||||
padding: 10rpx 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.demo {
|
||||
padding-top: 70rpx;
|
||||
|
||||
&__tips {
|
||||
&__icon {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 16rpx;
|
||||
width: 39rpx;
|
||||
height: 39rpx;
|
||||
line-height: 39rpx;
|
||||
font-size: 39rpx;
|
||||
|
||||
.icon {
|
||||
background: linear-gradient(-45deg, #FF8359 0%, #FFDF40 100%);
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
text-shadow: 0rpx 10rpx 10rpx rgba(255, 156, 82, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
position: absolute;
|
||||
top: 65rpx;
|
||||
right: 16rpx;
|
||||
font-size: 20rpx;
|
||||
margin-left: 20rpx;
|
||||
word-wrap: normal;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #E6E6E6;
|
||||
padding: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1);
|
||||
transform-origin: 0 0;
|
||||
z-index: 999999;
|
||||
|
||||
&--hide {
|
||||
transform: scaleY(0);
|
||||
}
|
||||
|
||||
&--show {
|
||||
transform: scaleY(100%);
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
border-width: 4px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent rgba(149, 149, 149, 0.1) transparent;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 演示内容展示end */
|
||||
|
||||
/* 可选项start */
|
||||
.section-container {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-top: 70rpx;
|
||||
|
||||
.section {
|
||||
&__content {
|
||||
margin-top: 70rpx;
|
||||
display: none;
|
||||
|
||||
&--visible {
|
||||
display: block;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: calc(70rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
margin-top: 0rpx;
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0 30rpx;
|
||||
text-align: center;
|
||||
|
||||
&__left-line,
|
||||
&__right-line {
|
||||
|
||||
width: 100rpx;
|
||||
height: 2rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__left-line {
|
||||
&::after {
|
||||
content: '';
|
||||
background: inherit;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
right: 0rpx;
|
||||
border-radius: 50%;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
}
|
||||
|
||||
&__right-line {
|
||||
&::after {
|
||||
content: '';
|
||||
background: inherit;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
left: 0rpx;
|
||||
border-radius: 50%;
|
||||
transform: translateY(50%);
|
||||
}
|
||||
}
|
||||
|
||||
&--text {
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
min-width: 124rpx;
|
||||
height: 30rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 1;
|
||||
margin: 0 35rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&__btns {
|
||||
width: calc(100% - 60rpx);
|
||||
margin: 0 30rpx;
|
||||
margin-top: 29rpx;
|
||||
padding: 50rpx 30rpx 0rpx 0rpx;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0rpx 10rpx 50rpx 0rpx rgba(0, 3, 72, 0.1);
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&__item {
|
||||
max-width: 30%;
|
||||
padding: 17rpx 36rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 40rpx;
|
||||
margin-left: 40rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
// &::before {
|
||||
// content: " ";
|
||||
// position: absolute;
|
||||
// top: 10rpx;
|
||||
// left: 1rpx;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// background: inherit;
|
||||
// filter: blur(24rpx);
|
||||
// opacity: 1;
|
||||
// z-index: -1;
|
||||
// }
|
||||
|
||||
&__bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
|
||||
&--active {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
&--text {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.2em;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
|
||||
&--active {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 可选项end */
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
<template>
|
||||
<view class="multiple-options">
|
||||
<view class="list">
|
||||
<block v-for="(item, index) in listData" :key="index">
|
||||
<view
|
||||
class="list__item"
|
||||
:class="[`tn-main-gradient-${tuniaoColorList[item.bgColorIndex]}--light`]"
|
||||
@tap="navOptionsPage(item.url)"
|
||||
>
|
||||
<view class="list__content">
|
||||
<view class="list__content__title">{{ item.title }}</view>
|
||||
<view class="list__content__desc">{{ item.desc }}</view>
|
||||
</view>
|
||||
<view class="list__icon">
|
||||
<view class="list__icon__main" :class="[`tn-icon-${item.mainIcon}`, `tn-main-gradient-${tuniaoColorList[item.bgColorIndex]}`]"></view>
|
||||
<view class="list__icon__sub" :class="[`tn-icon-${item.subIcon}`, `tn-main-gradient-${tuniaoColorList[item.bgColorIndex]}`]"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'multiple-options-demo',
|
||||
props: {
|
||||
// 显示的列表数据
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 图鸟颜色列表
|
||||
tuniaoColorList: [
|
||||
'red',
|
||||
'purplered',
|
||||
'purple',
|
||||
'bluepurple',
|
||||
'aquablue',
|
||||
'blue',
|
||||
'indigo',
|
||||
'cyan',
|
||||
'teal',
|
||||
'green',
|
||||
'orange',
|
||||
'orangered'
|
||||
],
|
||||
listData: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
list(val) {
|
||||
this.initList()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initList()
|
||||
},
|
||||
methods: {
|
||||
// 初始化列表数据
|
||||
initList() {
|
||||
// 给列表添加背景颜色数据
|
||||
this.listData = this.list.map((item, index) => {
|
||||
item.bgColorIndex = this.getBgNum()
|
||||
item.mainIcon = item?.mainIcon || 'computer-fill'
|
||||
item.subIcon = item?.subIcon || 'share'
|
||||
return item
|
||||
})
|
||||
},
|
||||
// 跳转到对应的选项页面
|
||||
navOptionsPage(url) {
|
||||
uni.navigateTo({
|
||||
url: url
|
||||
})
|
||||
},
|
||||
// 获取酷炫背景随机数
|
||||
getBgNum() {
|
||||
return Math.floor((Math.random() * this.tuniaoColorList.length))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.list {
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: calc(100% - 60rpx);
|
||||
margin: 108rpx 30rpx 0rpx 30rpx;
|
||||
box-shadow: 0rpx 10rpx 50rpx 0rpx rgba(0, 3, 72, 0.1);
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
// color: $tn-font-color;
|
||||
margin: 34rpx 0rpx 27rpx 37rpx;
|
||||
|
||||
&__title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
&__desc {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
flex: 1;
|
||||
margin-right: 26rpx;
|
||||
position: relative;
|
||||
|
||||
&__main, &__sub {
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
&__main {
|
||||
font-size: 200rpx;
|
||||
width: 190rpx;
|
||||
line-height: 200rpx;
|
||||
top: 0;
|
||||
right: 0rpx;
|
||||
transform: translateY(-60%);
|
||||
}
|
||||
&__sub {
|
||||
font-size: 70rpx;
|
||||
top: 0;
|
||||
right: 175rpx;
|
||||
transform: translateY(-5rpx);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
<template>
|
||||
<view class="nav-index-button" :style="{bottom: `${bottom}rpx`, right: `${right}rpx`}" @tap.stop="navIndex">
|
||||
<view class="nav-index-button__content">
|
||||
<view class="nav-index-button__content--icon tn-flex tn-flex-row-center tn-flex-col-center tn-shadow-blur tn-cool-bg-color-7">
|
||||
<view class="tn-icon-home-vertical-fill"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="nav-index-button__meteor">
|
||||
<view class="nav-index-button__meteor__wrapper">
|
||||
<view v-for="(item,index) in 6" :key="index" class="nav-index-button__meteor__item" :style="{transform: `rotateX(${-60 + (30 * index)}deg) rotateZ(${-60 + (30 * index)}deg)`}">
|
||||
<view class="nav-index-button__meteor__item--pic"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'nav-index-button',
|
||||
props: {
|
||||
// 距离底部的距离
|
||||
bottom: {
|
||||
type: [Number, String],
|
||||
default: 300
|
||||
},
|
||||
// 距离右边的距离
|
||||
right: {
|
||||
type: [Number, String],
|
||||
default: 75
|
||||
},
|
||||
// 首页地址
|
||||
indexPath: {
|
||||
type: String,
|
||||
default: '/pages/index/index'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 跳转回首页
|
||||
navIndex() {
|
||||
// 通过判断当前页面的页面栈信息,是否有上一页进行返回,如果没有则跳转到首页
|
||||
const pages = getCurrentPages()
|
||||
if (pages && pages.length > 0) {
|
||||
const indexPath = this.indexPath || '/pages/index/index'
|
||||
const firstPage = pages[0]
|
||||
if (pages.length == 1 && (!firstPage.route || firstPage.route != indexPath.substring(1, indexPath.length))) {
|
||||
uni.reLaunch({
|
||||
url: indexPath
|
||||
})
|
||||
} else {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: indexPath
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.nav-index-button {
|
||||
position: fixed;
|
||||
animation: suspension 3s ease-in-out infinite;
|
||||
z-index: 999999;
|
||||
|
||||
&__content {
|
||||
position: absolute;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
&--icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
font-size: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 18rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transform: scale(0.85);
|
||||
|
||||
&::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
border-radius: inherit;
|
||||
opacity: 1;
|
||||
transform: scale(1, 1);
|
||||
background-size: 100% 100%;
|
||||
background-image: url(https://resource.tuniaokj.com/images/cool_bg_image/icon_bg6.png);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__meteor {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
transform-style: preserve-3d;
|
||||
transform: translate(-50%, -50%) rotateY(75deg) rotateZ(10deg);
|
||||
|
||||
&__wrapper {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
transform-style: preserve-3d;
|
||||
animation: spin 20s linear infinite;
|
||||
}
|
||||
|
||||
&__item {
|
||||
position: absolute;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 1000rpx;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
&--pic {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url(https://resource.tuniaokj.com/images/cool_bg_image/arc3.png) no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
animation: arc 4s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes suspension {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-0.8rem);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotateX(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateX(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes arc {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* 动态参数演示mixin
|
||||
*/
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
// 效果显示框top的值
|
||||
contentContainerTop: '0px',
|
||||
contentContainerIsTop: false,
|
||||
|
||||
// 参数显示框top的值
|
||||
sectionContainerTop: '0px'
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
this.updateSectionContainerTop()
|
||||
},
|
||||
methods: {
|
||||
// 处理演示效果框的位置
|
||||
async _handleContentConatinerPosition() {
|
||||
// 获取效果演示框的节点信息
|
||||
const contentContainer = await this._tGetRect('#content_container')
|
||||
// 获取参数框的节点信息
|
||||
this._tGetRect('#section_container').then((res) => {
|
||||
// 判断参数框是否在移动,如果是则更新效果框的位置
|
||||
// 如果效果框的顶部已经触控到顶部导航栏就停止跟随
|
||||
if (res.top - contentContainer.bottom != 15) {
|
||||
const newTop = res.top - (contentContainer.height + uni.upx2px(20))
|
||||
const minTop = this.vuex_custom_bar_height + 1
|
||||
if (newTop < minTop) {
|
||||
this.contentContainerTop = minTop + 'px'
|
||||
this.contentContainerIsTop = true
|
||||
} else {
|
||||
this.contentContainerTop = newTop + 'px'
|
||||
this.contentContainerIsTop = false
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 更新状态切换栏位置信息
|
||||
updateSectionContainerTop() {
|
||||
this._tGetRect('#content_container').then((res) => {
|
||||
this.contentContainerTop = (this.vuex_custom_bar_height + 148) + 'px'
|
||||
this.sectionContainerTop = (res.height + 20) + 'px'
|
||||
})
|
||||
}
|
||||
},
|
||||
// 监听页面滚动
|
||||
onPageScroll() {
|
||||
this._handleContentConatinerPosition()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/**
|
||||
* 演示页面mixin
|
||||
*/
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 更新顶部导航栏信息
|
||||
this.updateCustomBarInfo()
|
||||
},
|
||||
methods: {
|
||||
// 点击左上角返回按钮时触发事件
|
||||
goBack() {
|
||||
// 通过判断当前页面的页面栈信息,是否有上一页进行返回,如果没有则跳转到首页
|
||||
const pages = getCurrentPages()
|
||||
if (pages && pages.length > 0) {
|
||||
const firstPage = pages[0]
|
||||
if (pages.length == 1 && (!firstPage.route || firstPage.route != 'pages/index/index')) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
} else {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
},
|
||||
// 更新顶部导航栏信息
|
||||
async updateCustomBarInfo() {
|
||||
// 获取vuex中的自定义顶栏的高度
|
||||
let customBarHeight = this.vuex_custom_bar_height
|
||||
let statusBarHeight = this.vuex_status_bar_height
|
||||
// 如果获取失败则重新获取
|
||||
if (!customBarHeight) {
|
||||
try {
|
||||
const navBarInfo = await this.$tn.updateCustomBar()
|
||||
customBarHeight = navBarInfo.customBarHeight
|
||||
statusBarHeight = navBarInfo.statusBarHeight
|
||||
} catch(e) {
|
||||
setTimeout(() => {
|
||||
this.updateCustomBarInfo()
|
||||
}, 10)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 更新vuex中的导航栏信息
|
||||
this.$tn.vuex('vuex_status_bar_height', statusBarHeight)
|
||||
this.$tn.vuex('vuex_custom_bar_height', customBarHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
12
main.js
|
|
@ -6,26 +6,16 @@ import plugins from './plugins' // plugins
|
|||
import './permission' // permission
|
||||
import { getDicts } from "@/api/system/dict/data"
|
||||
import "./utils/uni.css";
|
||||
// 引入全局TuniaoUI
|
||||
import TuniaoUI from 'tuniao-ui'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
import share from './utils/share.js' //注意路径是上一步新建文件的路径
|
||||
|
||||
Vue.use(VueCompositionAPI)
|
||||
// 引入TuniaoUI提供的vuex简写方法
|
||||
let vuexStore = require('@/store/$tn.mixin.js')
|
||||
|
||||
Vue.use(plugins)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$store = store
|
||||
Vue.prototype.getDicts = getDicts
|
||||
Vue.use(TuniaoUI)
|
||||
Vue.mixin(vuexStore)
|
||||
Vue.mixin(share)
|
||||
App.mpType = 'app'
|
||||
|
||||
const app = new Vue({
|
||||
store,
|
||||
...App
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
"versionName" : "1.2.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
"sassImplementationName" : "node-sass",
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.3.0",
|
||||
"mqtt": "^2.18.8"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
271
pages.json
|
|
@ -1,285 +1,108 @@
|
|||
{
|
||||
"easycom": {
|
||||
"^tn-(.*)": "@/tuniao-ui/components/tn-$1/tn-$1.vue"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"pages": [{
|
||||
"path": "pages/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"path": "pages/register",
|
||||
"style": {
|
||||
"navigationBarTitleText": "注册"
|
||||
}
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"path": "pages/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页"
|
||||
"navigationBarTitleText": "智能农业移动端框架",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/data/index",
|
||||
}, {
|
||||
"path": "pages/work/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "数据中心",
|
||||
// "navigationBarBackgroundColor": "pink",
|
||||
// "navigationBarTextStyle": "white",
|
||||
"navigationBarShadow": {
|
||||
// grey、blue、green、orange、red、yellow
|
||||
"colorType": "red"
|
||||
"navigationBarTitleText": "工作台"
|
||||
}
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "path": "pages/data/indexDemo",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "数据中心11"
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"path": "pages/news/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息中心",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"path": "pages/mine/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/common",
|
||||
"pages": [
|
||||
{
|
||||
"path": "webview/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览网页"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "textview/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览文本"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/home",
|
||||
"pages": [
|
||||
{
|
||||
"path": "control/index",
|
||||
}, {
|
||||
"path": "pages/control/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "控制中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite/shareDevice/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "设备分享"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite/shareTarget/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分享对象"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite/selectDevice/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择设备"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite/setPermission/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "设置权限"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite/myShareDetail/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的分享"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite/shareConfirm/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "确认分享",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite/sharedToMe/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分享给我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
|
||||
// {
|
||||
// "path": "control/automatic",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "自动控制"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "path": "control/manual",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "手动控制"
|
||||
// }
|
||||
// }
|
||||
]
|
||||
},
|
||||
/* {
|
||||
"root": "pages/data",
|
||||
"pages": [
|
||||
]
|
||||
},*/
|
||||
{
|
||||
"root": "pages/news/subpages",
|
||||
"pages": [
|
||||
{
|
||||
"path": "sysNotice/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "系统公告"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "eventCenter/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "活动中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "videoCenter/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "视频中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "deviceCenter/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "设备状态"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "deviceLog/log",
|
||||
"style": {
|
||||
"navigationBarTitleText": "自动模式设备日志"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/work",
|
||||
"pages": [
|
||||
{
|
||||
"path": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "工作台"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/mine/subpages",
|
||||
"pages": [
|
||||
{
|
||||
"path": "avatar/index",
|
||||
},{
|
||||
"path": "pages/mine/avatar/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改头像"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "info/index",
|
||||
}, {
|
||||
"path": "pages/mine/info/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人信息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "info/edit",
|
||||
}, {
|
||||
"path": "pages/mine/info/edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "编辑资料"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pwd/index",
|
||||
}, {
|
||||
"path": "pages/mine/pwd/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改密码"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "setting/index",
|
||||
}, {
|
||||
"path": "pages/mine/setting/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "应用设置"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "help/index",
|
||||
}, {
|
||||
"path": "pages/mine/help/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "常见问题"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "mqtt/index",
|
||||
}, {
|
||||
"path": "pages/mine/mqtt/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "mqtt工具"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "require/index",
|
||||
}, {
|
||||
"path": "pages/mine/require/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "需求清单"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "about/index",
|
||||
}, {
|
||||
"path": "pages/mine/about/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "关于我们"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/common/webview/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览网页"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"path": "pages/common/textview/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览文本"
|
||||
}
|
||||
],
|
||||
}],
|
||||
"tabBar": {
|
||||
"color": "#000000",
|
||||
"selectedColor": "#406ee9",
|
||||
"selectedColor": "#000000",
|
||||
"borderStyle": "white",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index",
|
||||
"iconPath": "static/images/tabbar/home.png",
|
||||
"selectedIconPath": "static/images/tabbar/home_.png",
|
||||
"text": "首页"
|
||||
"pagePath": "pages/control/index",
|
||||
"iconPath": "static/images/tabbar/work.png",
|
||||
"selectedIconPath": "static/images/tabbar/work_.png",
|
||||
"text": "控制中心"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/data/index",
|
||||
"iconPath": "static/images/tabbar/data.png",
|
||||
"selectedIconPath": "static/images/tabbar/data_.png",
|
||||
"text": "历史数据"
|
||||
},
|
||||
// {
|
||||
// "pagePath": "pages/data/indexDemo",
|
||||
// "iconPath": "static/images/tabbar/data.png",
|
||||
// "selectedIconPath": "static/images/tabbar/data_.png",
|
||||
// "text": "历史数据"
|
||||
// },
|
||||
{
|
||||
"pagePath": "pages/news/index",
|
||||
"iconPath": "static/images/tabbar/news.png",
|
||||
"selectedIconPath": "static/images/tabbar/news_.png",
|
||||
"text": "消息中心"
|
||||
"pagePath": "pages/work/index",
|
||||
"iconPath": "static/images/tabbar/work.png",
|
||||
"selectedIconPath": "static/images/tabbar/work_.png",
|
||||
"text": "工作台"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mine/index",
|
||||
|
|
|
|||
|
|
@ -1,236 +0,0 @@
|
|||
<template>
|
||||
<view class="data-view">
|
||||
<view class="select-box">
|
||||
<uni-row >
|
||||
<uni-col :span="8">
|
||||
<uni-data-select :clear="false" v-model="imei" :localdata="agriList" @change="changeAgri"></uni-data-select>
|
||||
</uni-col>
|
||||
<uni-col :span="16">
|
||||
<uni-datetime-picker v-model="dateRange" type="datetimerange" :end="end" @change="changeDate"/>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
</view>
|
||||
<view class="charts-box">
|
||||
<qiun-data-charts
|
||||
type="line"
|
||||
:opts="opts"
|
||||
background="#ffffff"
|
||||
:chartData="chartData"
|
||||
:canvas2d="true"
|
||||
canvasId="nMPkeQGNEosMwoWKKNRBZBIEhguMoMWp"
|
||||
:ontouch="true"
|
||||
:disableScroll="true"
|
||||
:onmovetip="true"
|
||||
tooltipFormat="tooltipDemo"
|
||||
/>
|
||||
</view>
|
||||
<view class="uni-padding-wrap uni-common-mt tag-row">
|
||||
<button @click.stop="jumpDate(-1)" style="margin-right: 20rpx;" type="primary" size="mini">前一天</button>
|
||||
<button :disabled="isTodayDate(dateRange[0]) || isTodayDate(dateRange[1]) || dateRange.length===0" @click.stop="jumpDate(1)" style="margin-right: 20rpx;" type="default" size="mini">后一天</button>
|
||||
<button @click.stop="jumpDate(0)" type="warn" size="mini">回到当天</button>
|
||||
</view>
|
||||
<!-- 暂无数据 -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getHistoryData} from "../../api/system/data";
|
||||
import {listAgri} from "../../api/system/assets/agri";
|
||||
import {parseTime, isIntervalMoreThanNDays, getExactDiffDays, isTodayDate} from "../../utils/agri";
|
||||
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
chartData: {},
|
||||
//这里的 opts 是图表类型 type="line" 的全部配置参数,您可以将此配置复制到 config-ucharts.js 文件中下标为 ['line'] 的节点中来覆盖全局默认参数。实际应用过程中 opts 只需传入与全局默认参数中不一致的【某一个属性】即可实现同类型的图表显示不同的样式,达到页面简洁的需求。
|
||||
opts: {
|
||||
legend: {
|
||||
},
|
||||
xAxis: {
|
||||
rotateAngle: 75,
|
||||
format: "xAxisDemo"
|
||||
},
|
||||
yAxis: {
|
||||
gridType: "dash",
|
||||
dashLength: 2,
|
||||
disabled: false,
|
||||
disableGrid: false,
|
||||
splitNumber: 5,
|
||||
gridColor: "#CCCCCC",
|
||||
padding: 10,
|
||||
showTitle: true,
|
||||
data: [
|
||||
{
|
||||
type: "value",
|
||||
position: "left",
|
||||
disabled: false,
|
||||
axisLine: true,
|
||||
axisLineColor: "#CCCCCC",
|
||||
calibration: true,
|
||||
fontColor: "#666666",
|
||||
fontSize: 13,
|
||||
textAlign: "left",
|
||||
title: "温度(℃)",
|
||||
titleFontSize: 14,
|
||||
titleOffsetY: -10,
|
||||
titleOffsetX: 5,
|
||||
titleFontColor: "#333333",
|
||||
min: 0,
|
||||
max: 50,
|
||||
tofix: 1,
|
||||
unit: "",
|
||||
format: ""
|
||||
},
|
||||
{
|
||||
type: "value",
|
||||
position: "right",
|
||||
disabled: false,
|
||||
axisLine: true,
|
||||
axisLineColor: "#CCCCCC",
|
||||
calibration: true,
|
||||
fontColor: "#666666",
|
||||
fontSize: 13,
|
||||
textAlign: "right",
|
||||
title: "湿度(%RH)",
|
||||
titleFontSize: 14,
|
||||
titleOffsetY: -10,
|
||||
titleOffsetX: 0,
|
||||
titleFontColor: "#333333",
|
||||
min: 0,
|
||||
max: 100,
|
||||
tofix: 1,
|
||||
unit: "",
|
||||
format: ""
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
dateRange:[],
|
||||
end:Date.now(),
|
||||
agriList:[],
|
||||
imei:null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// this.getAgriList();
|
||||
},
|
||||
methods: {
|
||||
isTodayDate,
|
||||
getServerData() {
|
||||
const param = {
|
||||
imei: this.imei,
|
||||
startTime: this.dateRange[0],
|
||||
endTime: this.dateRange[1]
|
||||
}
|
||||
//模拟从服务器获取数据时的延时
|
||||
getHistoryData(param).then(response => {
|
||||
if (response.code === 200 && response.data) {
|
||||
var data = response.data;
|
||||
this.chartData = JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
})
|
||||
},
|
||||
getAgriList() {
|
||||
listAgri({status: 1}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.agriList = response.rows.map(item => ({
|
||||
agriId: item.id,
|
||||
text: item.agriName,
|
||||
value: item.imei // 提取并改名
|
||||
}));
|
||||
|
||||
this.imei = this.agriList[0].value;
|
||||
this.getServerData();
|
||||
}
|
||||
})
|
||||
},
|
||||
changeAgri(imei) {
|
||||
if (imei) {
|
||||
this.getServerData();
|
||||
}
|
||||
},
|
||||
changeDate(date) {
|
||||
// date[0]-date[1]
|
||||
if (isIntervalMoreThanNDays(date[0],date[1],7)) {
|
||||
this.$modal.alert('时间范围暂不支持查询超过7天以上的数据!');
|
||||
return;
|
||||
}
|
||||
if (date[0] === date[1]) {
|
||||
this.dateRange[0] = parseTime(date[0], '{y}-{m}-{d} 00:00:00')
|
||||
}
|
||||
if (this.imei) {
|
||||
this.getServerData();
|
||||
}
|
||||
},
|
||||
jumpDate(days) {
|
||||
|
||||
// 多选情况 || 时间范围今天以后不能选
|
||||
// days: 前一天(-1) 后一天(1) 回到当天(0)
|
||||
let targetDate;
|
||||
if (this.dateRange.length === 0 || getExactDiffDays(this.dateRange[0],this.dateRange[1])>1) {
|
||||
this.dateRange[0] = new Date();
|
||||
}
|
||||
if (days === 0) {
|
||||
// 回到当天
|
||||
targetDate = new Date();
|
||||
} else {
|
||||
// 前一天或后一天
|
||||
const currentStartDate = new Date(this.dateRange[0]);
|
||||
targetDate = new Date(currentStartDate);
|
||||
targetDate.setDate(targetDate.getDate() + days);
|
||||
}
|
||||
|
||||
// 设置开始时间为目标日期的 00:00:00
|
||||
const startDate = new Date(targetDate);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
|
||||
// 设置结束时间
|
||||
let endDate;
|
||||
if (days === 0) {
|
||||
// 回到当天,结束时间为当前时间
|
||||
endDate = new Date();
|
||||
} else {
|
||||
// 前一天或后一天,结束时间为目标日期的 23:59:59
|
||||
endDate = new Date(targetDate);
|
||||
endDate.setHours(23, 59, 59, 999);
|
||||
}
|
||||
|
||||
// 更新 dateRange
|
||||
this.dateRange = [
|
||||
parseTime(startDate ),
|
||||
parseTime(endDate)
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
this.$nextTick(() => {
|
||||
// 如果已选择设备,重新获取数据
|
||||
if (this.imei) {
|
||||
this.getServerData();
|
||||
}
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.data-view {
|
||||
background: #ffffff;
|
||||
}
|
||||
.select-box {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
.charts-box {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
/deep/ .uni-select, /deep/ .uni-date-editor--x {
|
||||
background: #fff !important;
|
||||
height: 68rpx;
|
||||
}
|
||||
.tag-row {
|
||||
text-align: center;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<view>
|
||||
<HistoryData ref="historyData" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HistoryData from './data.vue'
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
components: {
|
||||
HistoryData
|
||||
},
|
||||
onShow() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.historyData.getAgriList();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
<template>
|
||||
<view style="width:100%; height: 1000rpx; background:#FFFFFF;">
|
||||
<view style="width:100%; height: 800rpx;">
|
||||
<l-echart ref="chart"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import LEchart from "../../uni_modules/lime-echart/components/l-echart/l-echart.vue";
|
||||
|
||||
export default {
|
||||
components: { LEchart },
|
||||
data() {
|
||||
return {
|
||||
option: {
|
||||
title: {
|
||||
text: 'Stacked Line',
|
||||
x: 'center',
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
color: '#333'
|
||||
},
|
||||
padding: [10, 0, 10, 0] // 预留标题上下间距,避免与图例冲突
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['价格', '数量'],
|
||||
orient: 'horizontal',
|
||||
bottom: '10%',
|
||||
padding: [10, 20, 20, 20],
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
fontSize: 24,
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
top: '15%',
|
||||
bottom: '30%', // 增大底部预留空间
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
axisLabel: {
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '价格',
|
||||
position: 'left',
|
||||
alignTicks: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#c23531'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: '{value} 元',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '数量',
|
||||
position: 'right',
|
||||
alignTicks: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#2f4554'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: '{value} 件',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
],
|
||||
dataZoom: [{}],
|
||||
series: [
|
||||
{
|
||||
name: '价格',
|
||||
type: 'line',
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
yAxisIndex: 0,
|
||||
smooth: true,
|
||||
symbolSize: 6,
|
||||
itemStyle: { color: '#c23531' },
|
||||
lineStyle: { width: 2 }
|
||||
},
|
||||
{
|
||||
name: '数量',
|
||||
type: 'line',
|
||||
data: [120, 132, 101, 134, 90, 230, 210],
|
||||
yAxisIndex: 1,
|
||||
smooth: true,
|
||||
symbolSize: 6,
|
||||
itemStyle: { color: '#2f4554' },
|
||||
lineStyle: { width: 2 }
|
||||
}
|
||||
],
|
||||
color: ['#c23531', '#2f4554']
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.chart.init(echarts, chart => {
|
||||
chart.setOption(this.option);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,601 +0,0 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<z-paging ref="paging" refresher-only bg-color="var(--gradualCyanLight)" :show-empty="false" :show-footer="false" @onRefresh="refresh">
|
||||
|
||||
<template #refresher="{refresherStatus}">
|
||||
<!-- 此处的custom-refresh为demo中自定义的组件,非z-paging的内置组件,请在实际项目中自行创建。这里插入什么view,下拉刷新就显示什么view -->
|
||||
<custom-refresher :status="refresherStatus" />
|
||||
</template>
|
||||
<view class="card shadow shadow-lg bg-white ">
|
||||
<uni-section :title="`当前大棚:【${selectedText}】`" :subTitle="imei" titleFontSize="16px" type="line" >
|
||||
<template v-slot:right>
|
||||
<view class="switch-row" v-if="value!== 1 && !['862538065276939','A','B','C'].includes(value)">
|
||||
<text class="modal-text">手动</text>
|
||||
<tn-switch
|
||||
v-model="currentMode"
|
||||
:size="60"
|
||||
@change="confirmSwitch"
|
||||
/>
|
||||
<text class="modal-text">自动</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<uni-divider margin="10rpx 0"></uni-divider>
|
||||
|
||||
<!-- 把解析好的完整数据传给子组件 -->
|
||||
<auto-page
|
||||
ref="autoPage"
|
||||
v-if="currentMode === true"
|
||||
:show="show"
|
||||
:status="status"
|
||||
:limitTimes="limitTimes"
|
||||
:dtu_remark="dtu_remark"
|
||||
:selectedText="selectedText"
|
||||
:value="value"
|
||||
:ventTotalLen="ventTotalLen"
|
||||
:agriId="agriId"
|
||||
@publicMsg="publishMessage"
|
||||
@sendSettingMsg="sendSettingMsg"
|
||||
@getAgriRemark="getRemarkByImei"
|
||||
@getAgriLimit="getAgriByImei"
|
||||
/>
|
||||
<manual-page
|
||||
ref="manualPage"
|
||||
v-else-if="currentMode === false"
|
||||
:liveData="liveData"
|
||||
:show="show"
|
||||
:status="status"
|
||||
:fontStyle="fontStyle"
|
||||
:limitTimes="limitTimes"
|
||||
:dtu_remark="dtu_remark"
|
||||
:selectedText="selectedText"
|
||||
:value="value"
|
||||
:agriId="agriId"
|
||||
@publicMsg="publishMessage"
|
||||
@getAgriRemark="getRemarkByImei"
|
||||
@getAgriLimit="getAgriByImei"
|
||||
/>
|
||||
</uni-section>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ManualPage from "./manual.vue"
|
||||
import AutoPage from "./automatic.vue"
|
||||
// 优化:抽离魔法值常量
|
||||
const SENSOR_MAP = {
|
||||
temp1: "201", temp2: "202", temp3: "203", temp4: "204",
|
||||
humi1: "101", humi2: "102", humi3: "103", humi4: "104"
|
||||
};
|
||||
const MQTT_TOPIC_SUFFIX = { UP: "/+", DOWN: "/control" };
|
||||
|
||||
import mqttUtil from '@/utils/mqtt';
|
||||
import {listAgri, switchAgriMode} from "../../../api/system/assets/agri";
|
||||
import {getNewSpecialData} from "../../../api/data/specialData";
|
||||
import store from "../../../store";
|
||||
import {getRemarkByImei} from "../../../api/system/assets/remark";
|
||||
import CustomRefresher from "../../../components/custom-refresher/custom-refresher.vue";
|
||||
import ZPaging from "../../../uni_modules/z-paging/components/z-paging/z-paging.vue";
|
||||
import {findDtuDataByInfo} from "../../../api/system/data";
|
||||
import {getAgriByImei} from "../../../api/system/assets/limit";
|
||||
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: 'shared'
|
||||
},
|
||||
dicts: ['sys_data_map'],
|
||||
components: {
|
||||
ZPaging,
|
||||
CustomRefresher,
|
||||
ManualPage,
|
||||
AutoPage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentMode:false,
|
||||
mqttConfig: {
|
||||
subscribeTopic:'/listener',
|
||||
},
|
||||
value: 1,
|
||||
selectedText: '',
|
||||
// range: [],
|
||||
agriId:'',
|
||||
imei:'',
|
||||
publishTopic: '/control',
|
||||
// 优化:声明响应式变量 connected
|
||||
connected: false,
|
||||
dtu_remark:{},
|
||||
fontStyle: '',
|
||||
liveData: {
|
||||
temp1: '数据加载中...',
|
||||
temp2: '数据加载中...',
|
||||
temp3: '数据加载中...',
|
||||
temp4: '数据加载中...',
|
||||
humi1: '数据加载中...',
|
||||
humi2: '数据加载中...',
|
||||
humi3: '数据加载中...',
|
||||
humi4: '数据加载中...',
|
||||
temp: "正在加载中..."
|
||||
},
|
||||
// 卡片状态
|
||||
show: {
|
||||
jbk: "暂停",
|
||||
jbg: "暂停",
|
||||
jlg: "暂停",
|
||||
jlk: "暂停",
|
||||
jm1k: "暂停",
|
||||
jm1g: "暂停",
|
||||
jm2k: "暂停",
|
||||
jm2g: "暂停",
|
||||
jm3k: "暂停",
|
||||
jm3g: "暂停"
|
||||
},
|
||||
status: {
|
||||
jbk: 0,
|
||||
jbg: 0,
|
||||
jlk: 0,
|
||||
jlg: 0,
|
||||
jm1k: 0,
|
||||
jm1g: 0,
|
||||
jm2k: 0,
|
||||
jm2g: 0,
|
||||
jm3k: 0,
|
||||
jm3g: 0,
|
||||
deviceTime:"正在加载中..."
|
||||
},
|
||||
// 新增:限位时间配置
|
||||
limitTimes: {
|
||||
jbkLimit: 0,
|
||||
jbgLimit: 0,
|
||||
jlkLimit: 0,
|
||||
jlgLimit: 0,
|
||||
jm1kLimit: 0,
|
||||
jm1gLimit: 0,
|
||||
jm2kLimit: 0,
|
||||
jm2gLimit: 0,
|
||||
jm3kLimit: 0,
|
||||
jm3gLimit: 0
|
||||
},
|
||||
testMsg:'由于线上为真实数据。任何操作均可影响线上功能,故仅作演示',
|
||||
ventTotalLen:0
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
if (option.agriInfo) {
|
||||
const decodedStr = decodeURIComponent(option.agriInfo);
|
||||
const agriInfo = JSON.parse(decodedStr); // 反序列化为原对象
|
||||
this.value = agriInfo.imei;
|
||||
this.selectedText = agriInfo.agriName;
|
||||
this.agriId = (String(agriInfo.agriId));
|
||||
this.currentMode = (agriInfo.workMode === 1)
|
||||
this.change(this.value)
|
||||
}
|
||||
// 定义所有互斥的键对:[k键, g键]
|
||||
const mutexPairs = [
|
||||
['jbk', 'jbg'],
|
||||
['jlk', 'jlg'],
|
||||
['jm1k', 'jm1g'],
|
||||
['jm2k', 'jm2g'],
|
||||
['jm3k', 'jm3g']
|
||||
];
|
||||
|
||||
// 遍历处理每一组互斥规则(k=1则g=0,g=1则k=0)
|
||||
mutexPairs.forEach(([kKey, gKey]) => {
|
||||
if (this.status[kKey] === 1) this.status[gKey] = 0;
|
||||
if (this.status[gKey] === 1) this.status[kKey] = 0;
|
||||
});
|
||||
},
|
||||
onShow() {
|
||||
// this.getAgriList();
|
||||
// 注册MQTT消息回调(接收设备消息)
|
||||
mqttUtil.setOnMessageCallback(this.ackMessage);
|
||||
// 更新连接状态
|
||||
this.connected = mqttUtil.getMqttState().isConnected;
|
||||
},
|
||||
onUnload() {
|
||||
// 移除MQTT消息回调(避免内存泄漏)
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
// this.getAgriList()
|
||||
this.change(this.imei)
|
||||
mqttUtil.setOnMessageCallback(this.ackMessage);
|
||||
if (this.currentMode) {
|
||||
this.$refs.autoPage.refresh();
|
||||
}
|
||||
this.$refs.paging.complete();
|
||||
},
|
||||
getNewSpecialData() {
|
||||
getNewSpecialData().then(response => {
|
||||
if (response.code === 200 && response.data) {
|
||||
this.makeSpecialData(response.data,false);
|
||||
this.liveData.temp = "最后更新时间:"+response.data.time;
|
||||
this.fontStyle = 'font-size:16px;'
|
||||
}
|
||||
})
|
||||
},
|
||||
change(e) {
|
||||
this.imei = e;
|
||||
var clientId = mqttUtil.getMqttState().clientId;
|
||||
this.connected = mqttUtil.getMqttState().isConnected;
|
||||
if ((e === 'A' || e==='B' || e==='C')
|
||||
&& store.getters && store.getters.name === 'admin'
|
||||
&& this.currentMode===false) {
|
||||
if (!this.currentMode) {
|
||||
this.getNewSpecialData(e);
|
||||
}
|
||||
this.mqttConfig.subscribeTopic = `frontend/${clientId}/dtu/862538065276061`;
|
||||
}
|
||||
if (e !== 'A' && e!=='B' && e!=='C') {
|
||||
// 优化:使用常量拼接MQTT主题
|
||||
this.publishTopic = `frontend/${clientId}${MQTT_TOPIC_SUFFIX.DOWN}/${this.imei}`;
|
||||
this.mqttConfig.subscribeTopic = `frontend/${clientId}/dtu/${this.imei}`;
|
||||
var queryParams = {
|
||||
imei: this.imei
|
||||
}
|
||||
if (!this.currentMode) {
|
||||
// 最新温湿度数据
|
||||
findDtuDataByInfo(queryParams).then(response => {
|
||||
if (response.code === 200 && response.data) {
|
||||
Object.keys(response.data).forEach(key => {
|
||||
this.liveData[key] = response.data[key] || '已离线..';
|
||||
});
|
||||
this.liveData.temp = "最后更新时间:"+response.data.time;
|
||||
this.fontStyle = 'font-size:16px;'
|
||||
}
|
||||
})
|
||||
}
|
||||
this.getAgriByImei();
|
||||
// 备注
|
||||
this.getRemarkByImei();
|
||||
if (e!=="862538065276939"){
|
||||
const message = JSON.stringify({jbk: 0,read:true})
|
||||
this.publishMessage(message)
|
||||
}
|
||||
if (e==="864536071851206"){
|
||||
const message = JSON.stringify({jm3k: 0,read:true})
|
||||
this.publishMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
this.reset();
|
||||
this.style="";
|
||||
},
|
||||
getRemarkByImei() {
|
||||
getRemarkByImei({imei:this.imei}).then(response => {
|
||||
if (response.code===200 && (response.data)) {
|
||||
this.dtu_remark={...response.data}
|
||||
}
|
||||
});
|
||||
},
|
||||
getAgriByImei() {
|
||||
getAgriByImei(this.imei).then(response => {
|
||||
if (response.code === 200) {
|
||||
if (!response.data) {
|
||||
return;
|
||||
}
|
||||
this.limitTimes = response.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
getAgriList() {
|
||||
// 获取已启用的设备列表
|
||||
listAgri({status: 1}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.range = response.rows.map(item => ({
|
||||
agriId: item.id,
|
||||
text: item.agriName,
|
||||
value: item.imei // 提取并改名
|
||||
}));
|
||||
|
||||
if (store.getters && store.getters.name === 'admin') {
|
||||
this.range.push(
|
||||
{
|
||||
text:"八方南棚",
|
||||
value:"A"
|
||||
},
|
||||
{
|
||||
text:"九方春棚",
|
||||
value:"B"
|
||||
},
|
||||
{
|
||||
text:"十二方棚",
|
||||
value:"C"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
testFunction(content) {
|
||||
uni.showModal({
|
||||
title: '操作提示:',
|
||||
content: content,
|
||||
cancelText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
makeSpecialData(msgData, tag) {
|
||||
const div10 = (v) => (v == null ? null : Math.round((Number(v)/10)*10)/10);
|
||||
|
||||
// 1. 提取B/C的tag对应键名(精简重复三元)
|
||||
const B_KEYS = {
|
||||
true: {temp1:'205',humi1:'105',temp2:'206',humi2:'106',temp3:'207',humi3:'107',temp4:'208',humi4:'108'},
|
||||
false: {temp1:'temp5',humi1:'humi5',temp2:'temp6',humi2:'humi6',temp3:'temp7',humi3:'humi7',temp4:'temp8',humi4:'humi8'}
|
||||
};
|
||||
const C_KEYS = {
|
||||
true: {temp1:'209',humi1:'109',temp2:'210',humi2:'110',temp3:'211',humi3:'111',temp4:'212',humi4:'112'},
|
||||
false: {temp1:'temp9',humi1:'humi9',temp2:'temp10',humi2:'humi10',temp3:'temp11',humi3:'humi11',temp4:'temp12',humi4:'humi12'}
|
||||
};
|
||||
|
||||
// 2. 简化传感器键名映射表
|
||||
const IMEI_SENSOR_MAP = {
|
||||
A: SENSOR_MAP,
|
||||
B: B_KEYS[tag], // 直接取tag对应的键名,替代三元
|
||||
C: C_KEYS[tag]
|
||||
};
|
||||
|
||||
// 3. 确定传感器键名(精简变量名)
|
||||
const isAdmin = store.getters?.name === 'admin';
|
||||
const sk = (isAdmin && ['A','B','C'].includes(this.imei)) ? IMEI_SENSOR_MAP[this.imei] : IMEI_SENSOR_MAP.A;
|
||||
// 4. 简化liveData的嵌套三元(核心优化)
|
||||
this.liveData = Object.fromEntries(
|
||||
Object.entries(sk).map(([k, skVal]) => [
|
||||
k,
|
||||
(tag
|
||||
? div10(msgData[skVal])
|
||||
: (this.imei === 'A' ? msgData[k] : msgData[skVal]))
|
||||
|| "已离线..."
|
||||
])
|
||||
);
|
||||
this.liveData.temp = "最后更新时间:" + this.getCurrentTime();
|
||||
},
|
||||
reset() {
|
||||
Object.keys(this.show).forEach(key => {
|
||||
this.show[key] = "暂停";
|
||||
});
|
||||
Object.keys(this.status).forEach(key => {
|
||||
if (key === "deviceTime") {
|
||||
this.status[key] = '正在加载中...';
|
||||
return;
|
||||
}
|
||||
this.status[key] = 0;
|
||||
});
|
||||
Object.keys(this.liveData).forEach(key => {
|
||||
if (key==="temp") {
|
||||
this.liveData[key] = '正在加载中...';
|
||||
return;
|
||||
}
|
||||
this.liveData[key] = '数据加载中...';
|
||||
});
|
||||
|
||||
},
|
||||
// 测试专用
|
||||
testAuto(type) {
|
||||
this.$set(this.status, type, this.status[type] === 0 ? 1 : 0);
|
||||
this.$set(this.show, type, this.status[type] === 0 ? "暂停" : "运行");
|
||||
},
|
||||
publishMessage(message) {
|
||||
if (!this.connected || !this.publishTopic || !message) {
|
||||
return
|
||||
}
|
||||
// 调用全局MQTT工具类发布消息
|
||||
const publishSuccess = mqttUtil.publishMqtt(this.publishTopic, message);
|
||||
if (publishSuccess) {
|
||||
this.addMessage(`【指令已发送】imei: ${this.publishTopic},指令: ${message}`);
|
||||
} else {
|
||||
this.addMessage(`发布失败:设备:[${this.publishTopic}]`)
|
||||
}
|
||||
},
|
||||
|
||||
sendSettingMsg(message) {
|
||||
if (!this.connected || !message) {
|
||||
return
|
||||
}
|
||||
const clientId = mqttUtil.getMqttState().clientId;
|
||||
const controlTopic = `frontend/${clientId}/${this.imei}/config`;
|
||||
// 调用全局MQTT工具类发布消息
|
||||
const publishSuccess = mqttUtil.publishMqtt(controlTopic, message);
|
||||
if (publishSuccess) {
|
||||
this.addMessage(`【指令已发送】imei: ${controlTopic},指令: ${message}`);
|
||||
} else {
|
||||
this.addMessage(`发布失败:设备:[${controlTopic}]`)
|
||||
}
|
||||
},
|
||||
|
||||
// 消息回调逻辑完全保留(仅依赖全局工具类转发消息)
|
||||
ackMessage(topic, payload) {
|
||||
// 1. 先判断是否是目标订阅主题(如frontend/\\w+/control/\\w+")
|
||||
if ((topic !== this.mqttConfig.subscribeTopic+"/ack")
|
||||
&& (topic !== this.mqttConfig.subscribeTopic+"/listener")
|
||||
&& topic !== this.mqttConfig.subscribeTopic+"/config") return;
|
||||
const regexWithGroup = /^frontend\/[^/]+\/dtu\/\d+\/(.+)$/;
|
||||
let msgData = {};
|
||||
// 优化:捕获JSON解析异常
|
||||
try {
|
||||
msgData = JSON.parse(payload);
|
||||
} catch (e) {
|
||||
console.error("MQTT消息解析失败:", e, payload);
|
||||
return;
|
||||
}
|
||||
|
||||
const matchResult = topic.match(regexWithGroup);
|
||||
const tag = matchResult[1]; // 提取结果:'1234567890'
|
||||
|
||||
// 3. 区分“回执”和“其他内容”
|
||||
if (tag==='ack' && msgData.prop && "suc" in msgData) {
|
||||
this.handleCommandAck(msgData);
|
||||
} else if (tag==='listener' && "msg" in msgData && "clientId" in msgData) {
|
||||
if (mqttUtil.getMqttState().clientId === msgData.clientId) {
|
||||
this.$modal.msg(
|
||||
`${msgData.msg}`
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.handleOtherContent(msgData,tag)
|
||||
}
|
||||
},
|
||||
|
||||
addMessage(content) {
|
||||
console.info("提示消息:" + content)
|
||||
},
|
||||
|
||||
// 处理指令回执的函数(核心修改:添加定时器+快照)
|
||||
handleCommandAck(ackData) {
|
||||
// 拿到指令字段(如jm2k)和执行状态(suc)
|
||||
const commandField = Object.keys(ackData.prop)[0]; // 这里是"jm2k"
|
||||
const commandValue = ackData.prop[commandField]; // 这里是0/1
|
||||
const isSuccess = ackData.suc; // 这里是true
|
||||
|
||||
const type = commandField.substring(0,commandField.length -1);
|
||||
if (isSuccess) {
|
||||
// 优化:使用$set确保响应式更新
|
||||
this.$set(this.status, type, commandValue);
|
||||
this.$set(this.show, type, commandValue === 0 ? "暂停" : "运行");
|
||||
console.info(`收到回执,更新功能码成功:{"${type}":${commandValue}}`)
|
||||
}
|
||||
|
||||
|
||||
// ========== 修改:自动停止指令不弹窗 ==========
|
||||
if (ackData.clientId && (ackData.clientId===mqttUtil.getMqttState().clientId)) {
|
||||
console.info("用户提示成功!")
|
||||
this.$modal[isSuccess ? 'msgSuccess' : 'msgError'](
|
||||
`设备操作${isSuccess ? "成功" : "失败"}!`
|
||||
);
|
||||
}
|
||||
console.log(`指令[${commandField}=${commandValue}]执行${isSuccess ? "成功" : "失败"}`);
|
||||
},
|
||||
|
||||
handleOtherContent(msgData,tag) {
|
||||
// 业务逻辑:处理传感器数据、设备状态等
|
||||
// 设备状态展示
|
||||
if (this.value === 1) return;
|
||||
if (tag === 'listener') {
|
||||
var arr = ['jbk', "jbg", "jm1k", "jm1g", "jm2k", "jm2g", "jm3k", "jm3g","jlk","jlg"]
|
||||
const allKeysNumeric = Object.keys(msgData).some(key => arr.includes(key));
|
||||
if (allKeysNumeric) {
|
||||
//todo
|
||||
this.status = {...msgData}
|
||||
Object.keys(msgData).forEach(key => {
|
||||
const value = msgData[key];
|
||||
this.show[key] = value === 0 ? '暂停' : '运行';
|
||||
});
|
||||
this.status.deviceTime = '最后更新时间:' + this.getCurrentTime();
|
||||
}
|
||||
if (this.currentMode) return;
|
||||
const allKeysNumeric2 = Object.keys(msgData).every(key => /^\d+$/.test(key));
|
||||
if (Object.keys(msgData).length > 0 && allKeysNumeric2) {
|
||||
this.fontStyle = 'font-size:16px;'
|
||||
this.makeSpecialData(msgData, true);
|
||||
}
|
||||
} else if (tag === 'config') {
|
||||
if ("ventTotalLen" in msgData) {
|
||||
console.info("参数设置:自动校准风口:",msgData.ventTotalLen)
|
||||
this.ventTotalLen = msgData.ventTotalLen;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取格式化后的当前时间
|
||||
* @returns {string} 格式为 YYYY-MM-DD HH:mm:ss 的当前时间
|
||||
*/
|
||||
getCurrentTime() {
|
||||
const now = new Date();
|
||||
|
||||
// 获取年、月、日(补零确保两位数)
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需 +1
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
|
||||
// 获取时、分、秒(补零确保两位数)
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
|
||||
// 拼接成标准格式
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
},
|
||||
confirmSwitch(e) {
|
||||
const mode = e;
|
||||
this.currentMode = !mode
|
||||
var showTip = mode ? '自动模式':'手动模式';
|
||||
|
||||
uni.showModal({
|
||||
title: '操作提示',
|
||||
content: `确定将【${this.selectedText}】切换为${showTip}?`,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
switchAgriMode(this.imei, {code:(mode?1:0)}).then(response => {
|
||||
if (response.code===200) {
|
||||
this.currentMode = mode
|
||||
if (!mode) {
|
||||
this.change(this.imei)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
onHide() {
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
beforeDestroy() {
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<!-- 仅保留这个无 scoped 的 style 块引入样式 -->
|
||||
<style lang="scss">
|
||||
@import '@/tuniao-ui/index.scss';
|
||||
@import '@/colorui/main.css';
|
||||
@import '@/colorui/icon.css';
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
/deep/ .z-paging-content-fixed {
|
||||
padding: 20rpx !important;
|
||||
/* 可选:防止margin塌陷,加overflow */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
/deep/ .uni-section-header__slot-right {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 15rpx
|
||||
}
|
||||
.uni-stat-tooltip {
|
||||
width: 300rpx;
|
||||
}
|
||||
/deep/ .is-input-border {
|
||||
width: 340rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.switch-row {
|
||||
display: flex;
|
||||
align-items: center; /* 垂直居中 */
|
||||
gap: 8px; /* 元素之间的间距,可根据需要调整 */
|
||||
}
|
||||
|
||||
.modal-text {
|
||||
/* 确保文字和开关在视觉上对齐 */
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,673 +0,0 @@
|
|||
<template>
|
||||
<view>
|
||||
|
||||
<uni-section title="实时温湿度" titleFontSize="16px" type="line" v-if="value!== '1' && value!=='864865085003722'">
|
||||
<template v-slot:right>
|
||||
{{ liveData.temp }}
|
||||
</template>
|
||||
|
||||
<view>
|
||||
<!-- 优化:温湿度卡片循环渲染 -->
|
||||
<view class="uni-flex_control uni-row" v-for="data in dataType" :key="data.name">
|
||||
<view
|
||||
:class="['text', `uni-flex_control_${data.name}`,'uni-view']"
|
||||
v-for="item in sensorCards[data.name]"
|
||||
:key="item.key"
|
||||
@click="openDataModal(item)"
|
||||
>
|
||||
<text class="data" :style="fontStyle">
|
||||
{{ liveData[item.key] }}
|
||||
<text v-if="isEffectiveValue(liveData[item.key])" class="dataStyle">{{data.unit}}</text>
|
||||
</text>
|
||||
<text class="data" v-if="dtu_remark[item.key]">{{ dtu_remark[item.key] }}</text>
|
||||
<text class="data" v-else>{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-section>
|
||||
|
||||
<uni-section title="设备控制" titleFontSize="16px" type="line"
|
||||
v-if="value!== 1 && !['862538065276939','A','B','C'].includes(value)">
|
||||
<template v-slot:right>
|
||||
{{ status.deviceTime }}
|
||||
</template>
|
||||
<!-- 优化:设备卡片循环渲染 -->
|
||||
<view class="card-grid">
|
||||
<view
|
||||
class="control-card"
|
||||
v-for="card in deviceCards"
|
||||
:key="card.type"
|
||||
@click="openTimeModal(card)"
|
||||
>
|
||||
<view class="card-text">
|
||||
<text class="card-main" v-if="dtu_remark[card.type]">{{ dtu_remark[card.type] }}</text>
|
||||
<text class="card-main" v-else>{{ card.name }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="showStatusText">{{ show[card.type] || '未知' }}</text>
|
||||
<text class="limit-time">
|
||||
运行时间:{{
|
||||
limitTimes[`${card.type}Limit`] && limitTimes[`${card.type}Limit`] !== '0' ? `${limitTimes[`${card.type}Limit`]} s` : '- -'
|
||||
}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="showFlag" class="card-icon" :class="{ active: status[card.type] === 1 }"
|
||||
@click.stop="handleCardClick(1 - status[card.type], card.type)">
|
||||
<!-- 加@click.stop防止冒泡触发卡片点击的弹窗事件 -->
|
||||
<uni-icons
|
||||
:type="status[card.type] === 1 ? 'circle' : 'circle-filled'"
|
||||
size="24"
|
||||
color="#fff"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-section>
|
||||
|
||||
<uni-popup :mask-click="false" ref="inputNamelog" mode="center">
|
||||
<!-- 新增:修改运行时间的弹窗 -->
|
||||
<view class="modal-container">
|
||||
<view class="modal-title">{{ `【${selectedText} - 实时温湿度】别名设置` }}</view>
|
||||
<view class="modal-input-wrap">
|
||||
<text class="modal-label">别名设置:</text>
|
||||
<uni-easyinput style="width: 100px" v-model="remark" placeholder="不填展示默认备注"/>
|
||||
</view>
|
||||
<view class="modal-btn-wrap">
|
||||
<button class="modal-btn cancel" @click="closeModalDataName">取消</button>
|
||||
<button class="modal-btn confirm" @click="confirmModifyName">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<uni-popup :mask-click="false" ref="inputDialog" mode="center">
|
||||
<!-- 新增:修改运行时间的弹窗 -->
|
||||
<view class="modal-container">
|
||||
<view class="modal-title">{{ `【${selectedText} - ${currentCard.name}】设置` }}</view>
|
||||
<view class="modal-input-wrap">
|
||||
<text class="modal-label">当前时间:</text>
|
||||
<text class="modal-current">{{ currentCardTime > 0 ? `${currentCardTime} 秒` : '未设置' }}</text>
|
||||
</view>
|
||||
<view class="modal-input-wrap">
|
||||
<text class="modal-label">修改后时间:</text>
|
||||
<input
|
||||
class="modal-input"
|
||||
type="number"
|
||||
v-model.number="newLimitTime"
|
||||
/>
|
||||
<!-- <uni-number-box v-model="newLimitTime" />-->
|
||||
<text class="modal-unit">秒</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-input-wrap">
|
||||
<text class="modal-label">别名设置:</text>
|
||||
<uni-easyinput style="width: 100px" v-model="remark" placeholder="不填展示默认备注"/>
|
||||
</view>
|
||||
<view class="modal-btn-wrap">
|
||||
<button class="modal-btn cancel" @click="close">取消</button>
|
||||
<button class="modal-btn confirm" @click="confirmModifyTime">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 优化:抽离魔法值常量
|
||||
import UniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue"; // 引入弹窗组件
|
||||
import mqttUtil from '@/utils/mqtt';
|
||||
import {addLimit, updateLimit} from "../../../api/system/assets/limit";
|
||||
import store from "../../../store";
|
||||
import {addRemark, updateRemark} from "../../../api/system/assets/remark";
|
||||
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: 'shared'
|
||||
},
|
||||
dicts: ['sys_data_map'],
|
||||
name: "manual",
|
||||
props: {
|
||||
// 尺寸
|
||||
value: {
|
||||
type: String,
|
||||
default: "1"
|
||||
},
|
||||
// 打开时的背景颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
dtu_remark: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
status: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
liveData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
limitTimes: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
show: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
selectedText: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
agriId: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
fontStyle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
deep: true, // 监听对象内部属性变化(防止数据是对象时监听不到)
|
||||
immediate: true, // 关键:进入组件就执行,不用等数据变化
|
||||
handler(newVal) {
|
||||
// 确保数据有值后执行方法
|
||||
if (newVal) {
|
||||
this.imei = this.value
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
UniPopup // 注册弹窗组件
|
||||
},
|
||||
mounted() {
|
||||
this.refresh()
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sensorCards: {
|
||||
temp: [
|
||||
{label: '温度1', key: 'temp1'},
|
||||
{label: '温度2', key: 'temp2'},
|
||||
{label: '温度3', key: 'temp3'},
|
||||
{label: '温度4', key: 'temp4'}
|
||||
],
|
||||
humi: [
|
||||
{label: '湿度1', key: 'humi1'},
|
||||
{label: '湿度2', key: 'humi2'},
|
||||
{label: '湿度3', key: 'humi3'},
|
||||
{label: '湿度4', key: 'humi4'}
|
||||
]
|
||||
},
|
||||
deviceCards: [
|
||||
{type: 'jbk', name: '卷被开'},
|
||||
{type: 'jbg', name: '卷被关'},
|
||||
{type: 'jm1k', name: '卷膜1开'},
|
||||
{type: 'jm1g', name: '卷膜1关'},
|
||||
{type: 'jm2k', name: '卷膜2开'},
|
||||
{type: 'jm2g', name: '卷膜2关'},
|
||||
{type: 'jm3k', name: '卷膜3开'},
|
||||
{type: 'jm3g', name: '卷膜3关'},
|
||||
{type: 'jlk', name: '卷帘开'},
|
||||
{type: 'jlg', name: '卷帘关'},
|
||||
],
|
||||
// 优化:语义化变量名(替换原 hide: false)
|
||||
showStatusText: false,
|
||||
showFlag: true,
|
||||
remark: '',
|
||||
// 新增:弹窗相关变量
|
||||
currentCard: {}, // 当前点击的卡片信息
|
||||
currentCardTime: '', // 当前卡片的运行时间
|
||||
newLimitTime: 0, // 新的运行时间
|
||||
message: {},
|
||||
// 优化:声明响应式变量 connected
|
||||
connected: false,
|
||||
imei: '',
|
||||
// 优化:温湿度卡片配置(固定顺序:温度1→2→3→4,湿度1→2→3→4)
|
||||
sensorCard:{},
|
||||
dataType: [{name:"temp", unit: "℃"}, {name:"humi", unit: "%RH"}]
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
refresh() {
|
||||
this.showFlag = !((store.getters && store.getters.name !== 'admin') && this.$auth.hasRole("test"))
|
||||
},
|
||||
openDataModal(sensorCard) {
|
||||
if ((store.getters && store.getters.name !== 'admin')
|
||||
&& this.$auth.hasRole("test")) {
|
||||
return;
|
||||
}
|
||||
this.sensorCard = sensorCard;
|
||||
this.remark = this.dtu_remark[sensorCard.key] || sensorCard.label;
|
||||
this.$refs.inputNamelog.open()
|
||||
},
|
||||
|
||||
// 优化:封装温湿度单位判断函数
|
||||
isEffectiveValue(value) {
|
||||
return this.testNumber(value);
|
||||
},
|
||||
testNumber(data) {
|
||||
const reg = /^-?\d+(\.\d+)?$/;
|
||||
return reg.test(String(data).trim());
|
||||
},
|
||||
// 新增:打开修改运行时间的弹窗
|
||||
openTimeModal(card) {
|
||||
if ((store.getters && store.getters.name !== 'admin')
|
||||
&& this.$auth.hasRole("test")) {
|
||||
return;
|
||||
}
|
||||
this.currentCard = card; // 记录当前卡片信息
|
||||
this.currentCardTime = this.limitTimes[`${card.type}Limit`]; // 记录当前时间
|
||||
this.newLimitTime = this.currentCardTime; // 默认填充当前时间
|
||||
this.remark = this.dtu_remark[card.type] || card.name;
|
||||
this.$refs.inputDialog.open()
|
||||
},
|
||||
// 卡片点击事件(实际项目中调用接口修改状态) 功能标识
|
||||
handleCardClick(status, type) {
|
||||
const funcMsg = "该功能用来开启或暂停设备,按钮亮为开启,按钮暗为暂停设备"
|
||||
if ((store.getters && store.getters.name !== 'admin')
|
||||
&& this.$auth.hasRole("test")) {
|
||||
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
|
||||
return;
|
||||
}
|
||||
// 校验
|
||||
// 定义类型与提示文案的映射关系,减少重复代码
|
||||
const tipMap = {
|
||||
'jbk': {opposite: 'jbg', name: '卷被关', op: '卷被开'},
|
||||
'jbg': {opposite: 'jbk', name: '卷被开', op: '卷被关'},
|
||||
'jlk': {opposite: 'jlg', name: '卷帘关', op: '卷帘开'},
|
||||
'jlg': {opposite: 'jlk', name: '卷帘开', op: '卷帘关'},
|
||||
'jm1k': {opposite: 'jm1g', name: '卷膜1关', op: '卷膜1开'},
|
||||
'jm1g': {opposite: 'jm1k', name: '卷膜1开', op: '卷膜1关'},
|
||||
'jm2k': {opposite: 'jm2g', name: '卷膜2关', op: '卷膜2开'},
|
||||
'jm2g': {opposite: 'jm2k', name: '卷膜2开', op: '卷膜2关'},
|
||||
'jm3k': {opposite: 'jm3g', name: '卷膜3关', op: '卷膜3开'},
|
||||
'jm3g': {opposite: 'jm3k', name: '卷膜3开', op: '卷膜3关'}
|
||||
};
|
||||
|
||||
// 先判断类型是否在映射表中,避免无效case
|
||||
if (!tipMap[type]) return;
|
||||
|
||||
const {opposite, name, op} = tipMap[type];
|
||||
// 核心校验逻辑(只写一次,无需重复)
|
||||
if (status === 1 && this.status[opposite] === 1) {
|
||||
this.$modal.msgError(`【${this.selectedText}】${name}在运行状态,不能运行${op}操作!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// ========== 修改:从全局工具类获取连接状态 ==========
|
||||
this.connected = mqttUtil.getMqttState().isConnected;
|
||||
if (!this.connected) {
|
||||
this.$modal.msgError("设备连接异常");
|
||||
return;
|
||||
}
|
||||
if (this.value === 1) {
|
||||
this.$modal.msgError("设备控制失败!");
|
||||
return;
|
||||
}
|
||||
uni.showModal({
|
||||
title: '操作提示:',
|
||||
content: `确定 ${status === 1 ? "运行" : "暂停"} 【${this.selectedText} - ${op}】设备?`,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 组装消息
|
||||
this.message = JSON.stringify({[`${type}1`]: status})
|
||||
// 控制设备
|
||||
this.$emit("publicMsg", this.message)
|
||||
//todo
|
||||
// this.testAuto(type);
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
closeModalDataName() {
|
||||
this.$refs.inputNamelog.close()
|
||||
},
|
||||
close() {
|
||||
this.$refs.inputDialog.close()
|
||||
},
|
||||
// 新增:确认修改运行时间
|
||||
// 确认修改运行时间
|
||||
confirmModifyTime: function () {
|
||||
const funcMsg = "该功能用来设置设备运行时间及设备别名,设备运行时间即开启设备后到达运行时间自行暂停设备;设备别名不填则展示默认设备名称"
|
||||
if ((store.getters && store.getters.name !== 'admin')
|
||||
&& this.$auth.hasRole("test")) {
|
||||
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
|
||||
this.$refs?.inputDialog?.close();
|
||||
return;
|
||||
}
|
||||
// 1. 解构赋值:简化频繁的this.xxx调用,提升可读性
|
||||
let {
|
||||
newLimitTime, currentCardTime, selectedText, currentCard,
|
||||
limitTimes, dtu_remark, imei, agriId, remark
|
||||
} = this;
|
||||
|
||||
// 2. 恢复并优化数字校验(如需启用,取消注释即可)
|
||||
/*if (!newLimitTime || newLimitTime < 1 || newLimitTime > 60) {
|
||||
uni.showToast({ title: '请输入1-60的有效数字', icon: 'none' });
|
||||
return;
|
||||
}*/
|
||||
|
||||
// 3. 定义核心判定变量(语义化命名)
|
||||
const isLimitTimeModified = newLimitTime !== currentCardTime; // 运行时间是否修改
|
||||
remark = remark || currentCard.name;
|
||||
const isRemarkModified = (remark !== (dtu_remark[currentCard.type] || currentCard.name)); // 别名是否修改
|
||||
|
||||
// 4. 无修改则直接返回,避免无效弹窗
|
||||
if (!isLimitTimeModified && !isRemarkModified) {
|
||||
// 关闭弹窗(无论确认/取消都关闭)
|
||||
this.$refs?.inputDialog?.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 简化提示文案拼接(减少嵌套判断)
|
||||
let confirmContent = `确定修改${selectedText}-${currentCard.name}:`;
|
||||
const tips = [];
|
||||
if (isLimitTimeModified) tips.push(`运行时间为:${newLimitTime} 秒`);
|
||||
if (isRemarkModified) {
|
||||
tips.push(`别名设置为:${remark}`)
|
||||
}
|
||||
confirmContent += `\n${tips.map((tip, idx) => `${idx + 1}. ${tip}?`).join('\n')}`;
|
||||
|
||||
// 6. 确认弹窗(async/await处理异步请求)
|
||||
uni.showModal({
|
||||
title: '温馨提示:',
|
||||
content: confirmContent,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: async (res) => {
|
||||
// 关闭弹窗(无论确认/取消都关闭)
|
||||
this.$refs?.inputDialog?.close();
|
||||
|
||||
if (!res.confirm) return; // 取消操作则直接返回
|
||||
|
||||
let isAllSuccess = false; // 统一判定所有请求是否成功
|
||||
|
||||
try {
|
||||
// 7. 处理运行时间修改
|
||||
if (isLimitTimeModified) {
|
||||
this.$set(limitTimes, `${currentCard.type}Limit`, newLimitTime || 0);
|
||||
// 补全必要字段(合并重复逻辑)
|
||||
if (!limitTimes.imei) {
|
||||
this.$set(limitTimes, 'imei', imei);
|
||||
this.$set(limitTimes, 'agriName', selectedText);
|
||||
this.$set(limitTimes, 'agriId', agriId);
|
||||
}
|
||||
// 发起请求并等待结果(async/await确保顺序执行)
|
||||
const limitRes = limitTimes.id
|
||||
? await updateLimit(limitTimes)
|
||||
: await addLimit(limitTimes);
|
||||
if (limitRes.code === 200) {
|
||||
isAllSuccess = true;
|
||||
this.$emit("getAgriLimit")
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 处理别名修改
|
||||
if (isRemarkModified) {
|
||||
this.$set(dtu_remark, currentCard.type, remark);
|
||||
// 补全imei字段
|
||||
if (!dtu_remark.imei) {
|
||||
this.$set(dtu_remark, 'imei', imei);
|
||||
}
|
||||
// 发起请求并等待结果
|
||||
const remarkRes = dtu_remark.id
|
||||
? await updateRemark(dtu_remark)
|
||||
: await addRemark(dtu_remark);
|
||||
if (remarkRes.code === 200) {
|
||||
this.$emit("getAgriRemark")
|
||||
isAllSuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 9. 正确的提示逻辑(修复原本文案反写的问题)
|
||||
this.$modal[isAllSuccess ? 'msgSuccess' : 'msgError'](
|
||||
isAllSuccess ? '修改成功' : '修改失败'
|
||||
);
|
||||
} catch (error) {
|
||||
// 捕获网络异常/接口报错,避免页面卡死
|
||||
console.error('修改失败:', error);
|
||||
this.$modal.msgError('修改失败,请重试');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
confirmModifyName() {
|
||||
const funcMsg = "该功能用来设置温湿度卡片别名,如若不填则展示默认备注"
|
||||
if ((store.getters && store.getters.name !== 'admin')
|
||||
&& this.$auth.hasRole("test")) {
|
||||
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
|
||||
this.$refs?.inputNamelog?.close();
|
||||
return;
|
||||
}
|
||||
// 1. 解构赋值优化:直接拆分sensorCard的key/label,减少冗余
|
||||
let {
|
||||
selectedText,
|
||||
dtu_remark,
|
||||
imei,
|
||||
sensorCard: {key: sensorKey, label: sensorLabel},
|
||||
remark
|
||||
} = this;
|
||||
|
||||
remark = remark || sensorLabel;
|
||||
// 2. 语义化判定:别名是否修改
|
||||
const isRemarkModified = remark !== (dtu_remark[sensorKey] || sensorLabel);
|
||||
if (!isRemarkModified) {
|
||||
// 统一关闭弹窗方式
|
||||
this.$refs?.inputNamelog?.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 确认弹窗(async/await处理异步请求)
|
||||
uni.showModal({
|
||||
title: '温馨提示:',
|
||||
content: `确定将${selectedText}-${sensorLabel}别名设置为:${remark}?`, // 修复文案冗余冒号,补全remark变量
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: async (res) => {
|
||||
// 关闭弹窗(无论确认/取消都关闭)
|
||||
this.$refs?.inputNamelog?.close();
|
||||
|
||||
if (!res.confirm) return; // 取消操作直接返回
|
||||
|
||||
try {
|
||||
// 4. 设置loading(可选,防止重复点击)
|
||||
// uni.showLoading({ title: '处理中...' });
|
||||
|
||||
// 5. 更新响应式数据
|
||||
this.$set(dtu_remark, sensorKey, remark); // sensorKey本身是字符串,无需拼接
|
||||
// 补全imei字段(确保非空)
|
||||
if (!dtu_remark.imei) {
|
||||
this.$set(dtu_remark, 'imei', imei);
|
||||
}
|
||||
|
||||
// 6. 异步请求:await等待结果(核心修复)
|
||||
const remarkRes = dtu_remark.id ? await updateRemark(dtu_remark) : await addRemark(dtu_remark);
|
||||
|
||||
// 7. 根据接口响应提示
|
||||
const isSuccess = remarkRes.code === 200;
|
||||
this.$modal[isSuccess ? 'msgSuccess' : 'msgError'](
|
||||
isSuccess ? '修改成功' : '修改失败'
|
||||
);
|
||||
|
||||
// 8. 仅成功时刷新数据(失败时不刷新,避免覆盖现有数据)
|
||||
if (isSuccess) {
|
||||
this.$emit("getRemark")
|
||||
}
|
||||
} catch (error) {
|
||||
// 捕获网络异常/接口报错
|
||||
console.error('别名修改失败:', error);
|
||||
this.$modal.msgError('修改失败,请重试');
|
||||
} finally {
|
||||
// 无论成功/失败,关闭loading
|
||||
// uni.hideLoading();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 2列栅格布局 */
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.control-card {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 8rpx #bfbec1;
|
||||
}
|
||||
|
||||
/* 卡片文字区域 */
|
||||
.card-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.card-main {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.card-sub {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 保留原有样式,仅修改/新增以下部分 */
|
||||
.card-sub-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 10rpx;
|
||||
/* 新增:给容器加最小宽度,确保所有卡片一致 */
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.limit-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
/* 可选:加固定左边距,确保和暂停的间距统一 */
|
||||
margin-left: 0rpx;
|
||||
}
|
||||
|
||||
/* 卡片图标容器 */
|
||||
.card-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #e5e5e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 激活状态(运行) */
|
||||
.card-icon.active {
|
||||
background-color: #007aff;
|
||||
}
|
||||
|
||||
.uni-px-5 {
|
||||
padding-left: 20rpx;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.uni-pb-5 {
|
||||
padding-bottom: 15rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 50rpx;
|
||||
margin: 10rpx 10rpx 8rpx 0;
|
||||
padding: 0;
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
box-shadow: 0 2rpx 8rpx #bfbec1
|
||||
}
|
||||
|
||||
.dataStyle {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.text:first-child {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.uni-view {
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
height: 150rpx;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.data {
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
line-height: 23px;
|
||||
color: #3a3a3a;
|
||||
}
|
||||
|
||||
.data:nth-child(even) {
|
||||
font-size: 12px;
|
||||
margin-top: 3px;
|
||||
color: #9c9c9c;
|
||||
}
|
||||
|
||||
/deep/ .uni-section-header__slot-right {
|
||||
color: green;
|
||||
}
|
||||
|
||||
|
||||
.icon {
|
||||
margin-left: 15rpx
|
||||
}
|
||||
|
||||
/deep/ .is-input-border {
|
||||
width: 340rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
<template>
|
||||
<view class="page">
|
||||
<!-- 设备卡片 -->
|
||||
<view class="device-card">
|
||||
<image src="https://img.xiaoces.com/photos/share/agri.png" class="device-img" mode="aspectFit" />
|
||||
<view class="device-info">
|
||||
<text class="device-name">{{ deviceInfo.name }}</text>
|
||||
<text class="device-serial">序列号:{{ deviceInfo.serial }}</text>
|
||||
</view>
|
||||
<button class="add-share-btn" @click="addShare">添加分享</button>
|
||||
</view>
|
||||
|
||||
<!-- 分享对象列表标题 -->
|
||||
<view class="targets-header">
|
||||
<text class="targets-title">分享对象 ({{ shareTargets.length }}/10)</text>
|
||||
<text class="cancel-all" @click="cancelAll">取消所有分享</text>
|
||||
</view>
|
||||
|
||||
<!-- 分享对象列表 -->
|
||||
<view class="targets-list">
|
||||
<view v-for="target in shareTargets" :key="target.id" class="target-item">
|
||||
<view class="target-top">
|
||||
<view class="target-name-row">
|
||||
<text class="target-name">{{ target.name }}</text>
|
||||
<image src="/static/images/share/icon_edit.png" class="edit-icon" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="shared-badge">已分享</view>
|
||||
</view>
|
||||
<text class="target-permissions">{{ target.permissions.join(' / ') }}</text>
|
||||
<text class="target-last-use">最新使用:{{ target.lastUse }}</text>
|
||||
<view class="target-actions">
|
||||
<button class="action-btn" @click="cancelShare(target)">取消分享</button>
|
||||
<button class="action-btn" @click="editPermission(target)">修改权限</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!shareTargets.length" class="empty-tip">暂无分享对象</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
deviceInfo: {
|
||||
id: 3,
|
||||
name: 'DS-2DE4423DW-D/GL...',
|
||||
serial: 'FW1167162',
|
||||
img: '/static/images/share/camera1.png'
|
||||
},
|
||||
shareTargets: [
|
||||
{
|
||||
id: 1,
|
||||
name: '132*****131/94198854的互联',
|
||||
permissions: ['看视频', '看录像', '报警消息', '控制设备'],
|
||||
lastUse: '2026-04-03'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options.id) {
|
||||
// 实际项目中根据id请求接口获取设备和分享详情
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addShare() {
|
||||
uni.navigateTo({ url: '/pages/home/invite/shareTarget/index' })
|
||||
},
|
||||
cancelAll() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定取消所有分享?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.shareTargets = []
|
||||
uni.showToast({ title: '已取消所有分享', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
cancelShare(target) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `确定取消对【${target.name}】的分享?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.shareTargets = this.shareTargets.filter(t => t.id !== target.id)
|
||||
uni.showToast({ title: '已取消分享', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
editPermission(target) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/home/invite/setPermission/index?targetId=${target.id}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: linear-gradient(180deg, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
padding: 30rpx 25rpx;
|
||||
}
|
||||
|
||||
/* 设备卡片 */
|
||||
.device-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
|
||||
.device-img {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.device-info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.device-name {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.device-serial {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.add-share-btn {
|
||||
background: #3d7ff5;
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
border-radius: 30rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
border: none;
|
||||
line-height: 1.4;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 分享对象标题 */
|
||||
.targets-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 26rpx 10rpx;
|
||||
|
||||
.targets-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cancel-all {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
/* 分享对象列表 */
|
||||
.targets-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.target-item {
|
||||
padding: 24rpx 34rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
.target-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.target-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.target-name {
|
||||
font-size: 28rpx;
|
||||
color: #3d7ff5;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.shared-badge {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
background: #f5f5f5;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.target-permissions {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.target-last-use {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.target-actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
|
||||
.action-btn {
|
||||
font-size: 24rpx;
|
||||
color: #555;
|
||||
background: #fff;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 30rpx;
|
||||
padding: 10rpx 80rpx;
|
||||
line-height: 1.4;
|
||||
justify-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
font-size: 26rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,236 +0,0 @@
|
|||
<template>
|
||||
<view class="page">
|
||||
<!-- 提示条 -->
|
||||
<view class="notice-bar">
|
||||
<text class="notice-icon">ⓘ</text>
|
||||
<text class="notice-text">已分享、分享已达上限的设备请前往 [我的-分享管理] 进行权限编辑或取消分享</text>
|
||||
</view>
|
||||
|
||||
<!-- 设备列表 -->
|
||||
<view class="device-list">
|
||||
<view
|
||||
v-for="device in deviceList"
|
||||
:key="device.id"
|
||||
class="device-item"
|
||||
@click="toggleSelect(device)"
|
||||
>
|
||||
<!-- 单选框 -->
|
||||
<view class="radio" :class="{ 'radio-checked': selectedIds.includes(device.id), 'radio-disabled': device.shared }">
|
||||
<view v-if="selectedIds.includes(device.id)" class="radio-inner" />
|
||||
</view>
|
||||
<image :src="device.img" class="device-img" mode="aspectFit" />
|
||||
<view class="device-info">
|
||||
<text class="device-name">{{ device.name }}</text>
|
||||
<text class="device-serial">{{ device.serial }}</text>
|
||||
</view>
|
||||
<text v-if="device.shared" class="shared-tag">已分享</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部:已选 + 下一步 -->
|
||||
<view class="bottom-bar">
|
||||
<text class="selected-tip">已选中:{{ selectedIds.length }}/{{ maxShare }}</text>
|
||||
<button
|
||||
class="next-btn"
|
||||
:class="{ 'next-btn-active': selectedIds.length > 0 }"
|
||||
:disabled="selectedIds.length === 0"
|
||||
@click="goNext"
|
||||
>下一步</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const MAX_SHARE = 10
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
maxShare: MAX_SHARE,
|
||||
selectedIds: [],
|
||||
phone: '',
|
||||
deviceList: [
|
||||
{ id: 1, name: '八方', serial: 'GL5074046', img: '/static/images/share/camera1.png', shared: true },
|
||||
{ id: 2, name: '十方', serial: 'GK5192621', img: '/static/images/share/camera2.png', shared: true },
|
||||
{ id: 3, name: 'DS-2DE4423DW-D/GL...', serial: 'FW1167162', img: '/static/images/share/camera1.png', shared: true },
|
||||
{ id: 4, name: 'DS-7804N-Z1/X(D)(FR...', serial: 'FR5725238', img: '/static/images/share/nvr.png', shared: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.phone = options.phone || ''
|
||||
},
|
||||
methods: {
|
||||
toggleSelect(device) {
|
||||
if (device.shared) return // 已分享不可选
|
||||
const idx = this.selectedIds.indexOf(device.id)
|
||||
if (idx > -1) {
|
||||
this.selectedIds.splice(idx, 1)
|
||||
} else {
|
||||
if (this.selectedIds.length >= this.maxShare) {
|
||||
uni.showToast({ title: `最多分享${this.maxShare}人`, icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.selectedIds.push(device.id)
|
||||
}
|
||||
},
|
||||
goNext() {
|
||||
const selected = this.deviceList.filter(d => this.selectedIds.includes(d.id))
|
||||
uni.navigateTo({
|
||||
url: `/pages/home/invite/setPermission/index?phone=${this.phone}&devices=${encodeURIComponent(JSON.stringify(selected))}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: linear-gradient(180deg, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
/* 提示条 */
|
||||
.notice-bar {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
background: #fff8ee;
|
||||
border-left: 6rpx solid #f5a623;
|
||||
padding: 20rpx 24rpx;
|
||||
margin: 20rpx 20rpx 0;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.notice-icon {
|
||||
color: #f5a623;
|
||||
font-size: 28rpx;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.notice-text {
|
||||
font-size: 24rpx;
|
||||
color: #f5a623;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
/* 设备列表 */
|
||||
.device-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin: 20rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.device-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 24rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
.radio {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
&-checked {
|
||||
border-color: #3d7ff5;
|
||||
background: #3d7ff5;
|
||||
}
|
||||
|
||||
&-inner {
|
||||
width: 18rpx;
|
||||
height: 18rpx;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
background: #f5f5f5;
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
}
|
||||
|
||||
.device-img {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.device-info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.device-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.device-serial {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.shared-tag {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
background: #edf1f7;
|
||||
|
||||
.selected-tip {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
width: 200rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
background: #ccc;
|
||||
color: #fff;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
&-active {
|
||||
background: #3d7ff5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
<template>
|
||||
<view class="page">
|
||||
<text class="page-hint">请设置分享对象的视频权限</text>
|
||||
|
||||
<!-- 权限选项列表 -->
|
||||
<view class="permission-list">
|
||||
<view
|
||||
v-for="item in permissionOptions"
|
||||
:key="item.value"
|
||||
class="permission-item"
|
||||
@click="togglePermission(item)"
|
||||
>
|
||||
<view class="radio" :class="{ 'radio-checked': selectedPermissions.includes(item.value) }">
|
||||
<view v-if="selectedPermissions.includes(item.value)" class="check-icon">✓</view>
|
||||
</view>
|
||||
<view class="permission-info">
|
||||
<text class="permission-name">{{ item.label }}</text>
|
||||
<text class="permission-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部提示 + 按钮 -->
|
||||
<view class="footer">
|
||||
<view class="footer-notice">
|
||||
<text class="notice-icon">ⓘ</text>
|
||||
<text class="notice-text">分享成功后,设备仅限被分享账号本人使用</text>
|
||||
</view>
|
||||
<button class="next-btn" @click="goNext">下一步</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
phone: '',
|
||||
devices: [],
|
||||
selectedPermissions: ['watch_video'], // 默认勾选"看视频"
|
||||
permissionOptions: [
|
||||
{ value: 'watch_video', label: '看视频', desc: '可查看实时画面' },
|
||||
{ value: 'watch_record', label: '看录像', desc: '可查看录像回放' },
|
||||
{ value: 'alarm_msg', label: '报警消息', desc: '可接收、查看设备消息' },
|
||||
{ value: 'control_device', label: '控制设备', desc: '可调整云台、画面、声音灯光等设置' }
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.phone = options.phone || ''
|
||||
if (options.devices) {
|
||||
try {
|
||||
this.devices = JSON.parse(decodeURIComponent(options.devices))
|
||||
} catch (e) {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
togglePermission(item) {
|
||||
const idx = this.selectedPermissions.indexOf(item.value)
|
||||
if (idx > -1) {
|
||||
this.selectedPermissions.splice(idx, 1)
|
||||
} else {
|
||||
this.selectedPermissions.push(item.value)
|
||||
}
|
||||
},
|
||||
goNext() {
|
||||
if (!this.selectedPermissions.length) {
|
||||
uni.showToast({ title: '请至少选择一项权限', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/home/invite/shareConfirm/index?phone=${this.phone}&permissions=${encodeURIComponent(JSON.stringify(this.selectedPermissions))}&devices=${encodeURIComponent(JSON.stringify(this.devices))}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: linear-gradient(180deg, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 40rpx 30rpx;
|
||||
}
|
||||
|
||||
.page-hint {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 4rpx;
|
||||
}
|
||||
|
||||
/* 权限列表 */
|
||||
.permission-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
|
||||
.permission-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx 28rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
.radio {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
&-checked {
|
||||
background: #555;
|
||||
border-color: #555;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.permission-info {
|
||||
.permission-name {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.permission-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部 */
|
||||
.footer {
|
||||
margin-top: 40rpx;
|
||||
|
||||
.footer-notice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.notice-icon {
|
||||
color: #f5a623;
|
||||
font-size: 26rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.notice-text {
|
||||
font-size: 24rpx;
|
||||
color: #f5a623;
|
||||
}
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
background: #3d7ff5;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
font-size: 32rpx;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,344 +0,0 @@
|
|||
<template>
|
||||
<view class="page">
|
||||
<!-- 顶部蓝色区:分享对象信息 -->
|
||||
<view class="header-section">
|
||||
<view class="back-btn" @click="goBack">‹</view>
|
||||
<view class="header-info">
|
||||
<text class="header-title">分享对象</text>
|
||||
<text class="header-phone">{{ phone }}</text>
|
||||
<text class="header-permissions">{{ permissionLabels }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 白色区:分享设备卡片 -->
|
||||
<view class="content-section">
|
||||
<view class="share-devices-card">
|
||||
<text class="card-title">分享设备</text>
|
||||
<view
|
||||
v-for="device in devices"
|
||||
:key="device.id"
|
||||
class="device-row"
|
||||
>
|
||||
<image :src="device.img" class="device-img" mode="aspectFit" />
|
||||
<view class="device-info">
|
||||
<text class="device-name">{{ device.name }}</text>
|
||||
<text class="device-serial">{{ device.serial }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<view class="footer">
|
||||
<view class="footer-notice">
|
||||
<text class="notice-icon">ⓘ</text>
|
||||
<text class="notice-text">
|
||||
请仔细核对并确认,分享对象在注册账号并接受邀请后,将能在权限内使用您分享的设备(分享成功后,设备仅限被分享账号本人使用)
|
||||
</text>
|
||||
</view>
|
||||
<button class="confirm-btn" @click="confirmShare">确认分享并邀请注册</button>
|
||||
</view>
|
||||
|
||||
<!-- 微信通知弹窗 -->
|
||||
<view v-if="showNotifyModal" class="modal-mask" @click.self="closeModal">
|
||||
<view class="modal-content">
|
||||
<view class="modal-close" @click="closeModal">×</view>
|
||||
<text class="modal-title">是否微信发送分享邀请?</text>
|
||||
<text class="modal-desc">推荐通过微信通知对方,便于对方即时接受分享</text>
|
||||
<image src="/static/images/share/share_card.png" class="modal-img" mode="aspectFit" />
|
||||
<button class="wx-notify-btn" @click="sendWxNotify">微信通知</button>
|
||||
<button class="skip-btn" @click="skipNotify">不通知,完成分享</button>
|
||||
<view class="privacy-row">
|
||||
<text class="privacy-icon">ⓘ</text>
|
||||
<text class="privacy-text">微信隐私协议</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 权限value -> 中文label 映射
|
||||
const PERMISSION_MAP = {
|
||||
watch_video: '看视频',
|
||||
watch_record: '看录像',
|
||||
alarm_msg: '报警消息',
|
||||
control_device: '控制设备'
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
phone: '',
|
||||
permissions: [],
|
||||
devices: [],
|
||||
showNotifyModal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
permissionLabels() {
|
||||
return this.permissions.map(p => PERMISSION_MAP[p] || p).join(' / ')
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.phone = options.phone || ''
|
||||
try {
|
||||
this.permissions = JSON.parse(decodeURIComponent(options.permissions || '[]'))
|
||||
this.devices = JSON.parse(decodeURIComponent(options.devices || '[]'))
|
||||
} catch (e) {}
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
confirmShare() {
|
||||
this.showNotifyModal = true
|
||||
},
|
||||
closeModal() {
|
||||
this.showNotifyModal = false
|
||||
},
|
||||
sendWxNotify() {
|
||||
this.showNotifyModal = false
|
||||
// 实际调用wx.shareAppMessage或openCustomerServiceChat等微信能力
|
||||
uni.showToast({ title: '微信通知已发送', icon: 'success' })
|
||||
setTimeout(() => uni.navigateBack({ delta: 4 }), 1500)
|
||||
},
|
||||
skipNotify() {
|
||||
this.showNotifyModal = false
|
||||
uni.showToast({ title: '分享成功', icon: 'success' })
|
||||
setTimeout(() => uni.navigateBack({ delta: 4 }), 1500)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: linear-gradient(180deg, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 顶部蓝色区 */
|
||||
.header-section {
|
||||
background: #3d7ff5;
|
||||
padding: 60rpx 30rpx 120rpx;
|
||||
position: relative;
|
||||
|
||||
.back-btn {
|
||||
color: #fff;
|
||||
font-size: 50rpx;
|
||||
position: absolute;
|
||||
top: 50rpx;
|
||||
left: 24rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
padding-left: 20rpx;
|
||||
|
||||
.header-title {
|
||||
display: block;
|
||||
color: rgba(255,255,255,0.8);
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.header-phone {
|
||||
display: block;
|
||||
color: #fff;
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.header-permissions {
|
||||
display: block;
|
||||
color: rgba(255,255,255,0.85);
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 内容区(白色卡片)*/
|
||||
.content-section {
|
||||
margin: -60rpx 20rpx 0;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
|
||||
.share-devices-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
|
||||
.card-title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.device-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10rpx 0;
|
||||
|
||||
.device-img {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.device-info {
|
||||
.device-name {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.device-serial {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #aaa;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部 */
|
||||
.footer {
|
||||
padding: 30rpx 20rpx;
|
||||
margin-top: auto;
|
||||
|
||||
.footer-notice {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
background: #fff8ee;
|
||||
border-radius: 8rpx;
|
||||
padding: 18rpx 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.notice-icon {
|
||||
color: #f5a623;
|
||||
font-size: 26rpx;
|
||||
margin-right: 10rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.notice-text {
|
||||
font-size: 24rpx;
|
||||
color: #f5a623;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
background: #3d7ff5;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
font-size: 30rpx;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 弹窗遮罩 */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
z-index: 999;
|
||||
|
||||
.modal-content {
|
||||
background: #fff;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
padding: 40rpx 40rpx 60rpx;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 28rpx;
|
||||
right: 36rpx;
|
||||
font-size: 40rpx;
|
||||
color: #999;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.modal-desc {
|
||||
font-size: 26rpx;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.modal-img {
|
||||
width: 260rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.wx-notify-btn {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
background: #3d7ff5;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
font-size: 32rpx;
|
||||
border: none;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.skip-btn {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
border-radius: 50rpx;
|
||||
font-size: 32rpx;
|
||||
border: none;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.privacy-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.privacy-icon {
|
||||
color: #aaa;
|
||||
font-size: 22rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
.privacy-text {
|
||||
font-size: 22rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
<template>
|
||||
<view class="page-bg">
|
||||
<!-- 顶部标签切换 -->
|
||||
<!-- <view class="tab-bar">-->
|
||||
<!-- <view-->
|
||||
<!-- v-for="tab in tabs"-->
|
||||
<!-- :key="tab.value"-->
|
||||
<!-- class="tab-item"-->
|
||||
<!-- :class="{ active: activeTab === tab.value }"-->
|
||||
<!-- @click="activeTab = tab.value"-->
|
||||
<!-- >{{ tab.label }}</view>-->
|
||||
<!-- </view>-->
|
||||
|
||||
<!-- 步骤引导条 -->
|
||||
<view class="steps-bar">
|
||||
<view v-for="(step, idx) in steps" :key="idx" class="step-item">
|
||||
<view class="step-icon-wrap">
|
||||
<image :src="step.icon" class="step-icon" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="step-label">{{ step.label }}</text>
|
||||
<view v-if="idx < steps.length - 1" class="step-line">···</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能说明标签 -->
|
||||
<view class="feature-tags">
|
||||
<view v-for="tag in featureTags" :key="tag" class="feature-tag">{{ tag }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的分享 / 分享给我的 切换 -->
|
||||
<view class="sub-tabs">
|
||||
<view
|
||||
v-for="sub in subTabs"
|
||||
:key="sub.value"
|
||||
class="sub-tab"
|
||||
:class="{ 'sub-tab-active': activeSubTab === sub.value }"
|
||||
@click="activeSubTab = sub.value"
|
||||
>{{ sub.label }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 设备列表 -->
|
||||
<view class="device-list">
|
||||
<!-- 我的分享列表 -->
|
||||
<view v-if="activeSubTab === 'mine'">
|
||||
<view
|
||||
v-for="device in mySharedDevices"
|
||||
:key="device.id"
|
||||
class="device-item"
|
||||
@click="goToDetail(device)"
|
||||
>
|
||||
<image :src="agriImg" class="device-img" mode="aspectFit" />
|
||||
<view class="device-info">
|
||||
<text class="device-name">{{ device.agriName }}</text>
|
||||
<text class="device-sub">已分享:{{ device.sharedCount }}次</text>
|
||||
</view>
|
||||
<view class="arrow">›</view>
|
||||
</view>
|
||||
<view v-if="!mySharedDevices.length" class="empty-tip">暂无可分享的设备</view>
|
||||
</view>
|
||||
|
||||
<!-- 分享给我的列表 仅显示已接受的-->
|
||||
<view v-if="activeSubTab === 'tome'">
|
||||
<view
|
||||
v-for="device in toMeDevices"
|
||||
:key="device.id"
|
||||
class="device-item"
|
||||
>
|
||||
<image :src="agriImg" class="device-img" mode="aspectFit" />
|
||||
<view class="device-info">
|
||||
<text class="device-name">{{ device.agriName }}</text>
|
||||
<!-- <text class="device-sub">{{ device.channels }}个通道 | {{ device.serial }}</text>-->
|
||||
</view>
|
||||
<button class="quit-btn" @click="quitShare(device)">退出分享</button>
|
||||
</view>
|
||||
<view v-if="!toMeDevices.length" class="empty-tip">暂无分享给我的设备</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部发起分享按钮 -->
|
||||
<view class="bottom-bar">
|
||||
<button class="share-btn" @click="goToShareTarget">发起分享</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {selectShareInfo} from "@/api/system/assets/agri";
|
||||
import store from "@/store";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'device',
|
||||
tabs: [
|
||||
{ label: '分享设备', value: 'device' },
|
||||
{ label: '分享团队', value: 'team' }
|
||||
],
|
||||
steps: [
|
||||
{ icon: 'https://img.xiaoces.com/photos/share/share.png', label: '发起分享' },
|
||||
{ icon: 'https://img.xiaoces.com/photos/share/user2.png', label: '添加分享对象' },
|
||||
{ icon: 'https://img.xiaoces.com/photos/share/access.png', label: '选择视频和权限' },
|
||||
{ icon: 'https://img.xiaoces.com/photos/share/share__.png', label: '接受分享' }
|
||||
],
|
||||
featureTags: ['功能按需分享', '最多分享 5 人', '仅大棚设备'],
|
||||
activeSubTab: 'mine',
|
||||
subTabs: [
|
||||
{ label: '我的分享', value: 'mine' },
|
||||
{ label: '分享给我的', value: 'tome' }
|
||||
],
|
||||
// 我的分享设备列表(实际对接API)
|
||||
mySharedDevices: [],
|
||||
// 分享给我的设备列表
|
||||
toMeDevices: [],
|
||||
agriImg: 'https://img.xiaoces.com/photos/share/agri.png'
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.getShareAgriList()
|
||||
},
|
||||
methods: {
|
||||
goToDetail(device) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/home/invite/myShareDetail/index?id=${device.id}`
|
||||
})
|
||||
},
|
||||
goToShareTarget() {
|
||||
uni.navigateTo({ url: '/pages/home/invite/shareTarget/index' })
|
||||
},
|
||||
quitShare(device) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `确定退出【${device.name}】的分享?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.$modal.msgSuccess('已退出分享')
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
getShareAgriList() {
|
||||
selectShareInfo({inviteBy: store.getters.id, status:1}).then(response => {
|
||||
if (response.code === 200) {
|
||||
// 直接使用后端返回的两个列表
|
||||
this.mySharedDevices = response.data.mySharedDevices || [];
|
||||
this.toMeDevices = response.data.toMeDevices || [];
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: radial-gradient(circle at top, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.page-bg {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
/* 顶部Tab */
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 24rpx 30rpx 0;
|
||||
background: #e0e5ef;
|
||||
border-radius: 40rpx;
|
||||
padding: 6rpx;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 14rpx 0;
|
||||
border-radius: 34rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.active {
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 步骤条 */
|
||||
.steps-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 50rpx 30rpx 20rpx;
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-icon-wrap {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.step-label {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
margin-top: 8rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-line {
|
||||
position: absolute;
|
||||
top: 30rpx;
|
||||
right: -20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #4a90e2;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 功能标签 */
|
||||
.feature-tags {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 40rpx 30rpx;
|
||||
gap: 20rpx;
|
||||
|
||||
.feature-tag {
|
||||
padding: 8rpx 40rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 22rpx;
|
||||
|
||||
&:first-child {
|
||||
background: rgba(8, 115, 255, 0.14);
|
||||
color: rgb(45, 86, 216);
|
||||
}
|
||||
&:nth-child(2) {
|
||||
background: #fff3e0;
|
||||
color: #f5a623;
|
||||
}
|
||||
&:nth-child(3) {
|
||||
background: rgba(253, 60, 60, 0.13);
|
||||
color: #fd3c3c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 二级Tab */
|
||||
.sub-tabs {
|
||||
display: flex;
|
||||
margin: 30rpx 30rpx;
|
||||
|
||||
.sub-tab {
|
||||
padding: 16rpx 30rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
position: relative;
|
||||
|
||||
&-active {
|
||||
color: #1a1a1a;
|
||||
font-weight: bold;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background: #1a1a1a;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 设备列表 */
|
||||
.device-list {
|
||||
margin: 16rpx 25rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.device-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 34rpx 28rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
.device-img {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.device-info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.device-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.device-sub {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
color: #ccc;
|
||||
font-size: 36rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.quit-btn {
|
||||
font-size: 24rpx;
|
||||
color: #555;
|
||||
background: #fff;
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 30rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
font-size: 26rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
|
||||
/* 底部按钮 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 20rpx 30rpx;
|
||||
background: #edf1f7;
|
||||
|
||||
.share-btn {
|
||||
background: #3d7ff5;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
font-size: 32rpx;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
<template>
|
||||
<view class="page">
|
||||
<!-- 手机号输入区 -->
|
||||
<view class="input-row">
|
||||
<input
|
||||
v-model="phone"
|
||||
class="phone-input"
|
||||
type="number"
|
||||
placeholder="请输入正确的手机号"
|
||||
maxlength="11"
|
||||
@input="onPhoneInput"
|
||||
/>
|
||||
<button class="next-btn" :disabled="!phoneValid" @click="goNext">下一步</button>
|
||||
</view>
|
||||
<view class="hint-row">
|
||||
<text class="hint-icon">ⓘ</text>
|
||||
<text class="hint-text">您正在尝试将视频分享至其他人</text>
|
||||
</view>
|
||||
|
||||
<!-- 最近分享 -->
|
||||
<view class="section-title">最近分享</view>
|
||||
<view class="recent-list">
|
||||
<view
|
||||
v-for="item in recentList"
|
||||
:key="item.userId"
|
||||
class="recent-item"
|
||||
@click="selectRecent(item)"
|
||||
>
|
||||
<text class="recent-phone">{{ item.privity }}</text>
|
||||
<view class="arrow">›</view>
|
||||
</view>
|
||||
<view v-if="recentList.length==0" class="empty-tip">暂无最近分享记录</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {findAgriUser, getRecentShareUser} from "@/api/system/assets/userAgri";
|
||||
import store from "@/store";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
phone: '',
|
||||
// 最近分享记录(实际对接API)
|
||||
recentList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
phoneValid() {
|
||||
return /^1\d{10}$/.test(this.phone)
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.getRecentList()
|
||||
},
|
||||
methods: {
|
||||
onPhoneInput(e) {
|
||||
this.phone = e.detail.value
|
||||
},
|
||||
selectRecent(item) {
|
||||
this.phone = item.phone
|
||||
this.goNext()
|
||||
},
|
||||
goNext() {
|
||||
if (!this.phoneValid) {
|
||||
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/home/invite/selectDevice/index?phone=${this.phone}`
|
||||
})
|
||||
},
|
||||
getRecentList() {
|
||||
this.recentList = [];
|
||||
getRecentShareUser().then(res => {
|
||||
if (res.code === 200) {
|
||||
res.data.map(item => {
|
||||
this.recentList.push({
|
||||
id: item.userId,
|
||||
phone: item.phonenumber,
|
||||
privity: item.phonenumber.slice(0, 3) + '****' + item.phonenumber.slice(7, 11)
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: radial-gradient(circle at top, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 30rpx;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 输入行 */
|
||||
.input-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid #3d7ff5;
|
||||
|
||||
.phone-input {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
width: 180rpx;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
background: #3d7ff5;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 提示 */
|
||||
.hint-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 16rpx;
|
||||
padding-left: 4rpx;
|
||||
|
||||
.hint-icon {
|
||||
color: #aaa;
|
||||
font-size: 26rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.hint-text {
|
||||
font-size: 24rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
/* 最近分享 */
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
margin: 36rpx 0 16rpx;
|
||||
}
|
||||
|
||||
.recent-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.recent-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 28rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
.recent-phone {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
color: #ccc;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
font-size: 26rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,371 +0,0 @@
|
|||
<template>
|
||||
<view class="page-bg">
|
||||
<!-- 顶部标签切换 -->
|
||||
<view class="tab-bar">
|
||||
<view
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === tab.value }"
|
||||
@click="activeTab = tab.value"
|
||||
>{{ tab.label }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 步骤引导条 -->
|
||||
<view class="steps-bar">
|
||||
<view v-for="(step, idx) in steps" :key="idx" class="step-item">
|
||||
<view class="step-icon-wrap">
|
||||
<image :src="step.icon" class="step-icon" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="step-label">{{ step.label }}</text>
|
||||
<view v-if="idx < steps.length - 1" class="step-line">···</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能说明标签 -->
|
||||
<view class="feature-tags">
|
||||
<view v-for="tag in featureTags" :key="tag" class="feature-tag">{{ tag }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的分享 / 分享给我的 切换 -->
|
||||
<view class="sub-tabs">
|
||||
<view
|
||||
v-for="sub in subTabs"
|
||||
:key="sub.value"
|
||||
class="sub-tab"
|
||||
:class="{ 'sub-tab-active': activeSubTab === sub.value }"
|
||||
@click="activeSubTab = sub.value"
|
||||
>{{ sub.label }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 已接受分组 -->
|
||||
<view v-if="activeSubTab === 'tome'" class="device-section">
|
||||
<text class="section-label">已接受</text>
|
||||
<view class="device-list">
|
||||
<view
|
||||
v-for="device in toMeDevices"
|
||||
:key="device.id"
|
||||
class="device-item"
|
||||
>
|
||||
<image :src="device.img" class="device-img" mode="aspectFit" />
|
||||
<view class="device-info">
|
||||
<text class="device-name">{{ device.name }}</text>
|
||||
<text class="device-sub">{{ device.channels }}个通道 | {{ device.serial }}</text>
|
||||
</view>
|
||||
<button class="quit-btn" @click="quitShare(device)">退出分享</button>
|
||||
</view>
|
||||
<view v-if="!toMeDevices.length" class="empty-tip">暂无分享给我的设备</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的分享列表 -->
|
||||
<view v-if="activeSubTab === 'mine'" class="device-list">
|
||||
<view
|
||||
v-for="device in mySharedDevices"
|
||||
:key="device.id"
|
||||
class="device-item"
|
||||
@click="goToDetail(device)"
|
||||
>
|
||||
<image :src="device.img" class="device-img" mode="aspectFit" />
|
||||
<view class="device-info">
|
||||
<text class="device-name">{{ device.name }}</text>
|
||||
<text class="device-sub">已分享:{{ device.sharedCount }}次</text>
|
||||
</view>
|
||||
<view class="arrow">›</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部发起分享按钮 -->
|
||||
<view class="bottom-bar">
|
||||
<button class="share-btn" @click="goToShareTarget">发起分享</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'device',
|
||||
tabs: [
|
||||
{ label: '分享设备', value: 'device' },
|
||||
{ label: '分享团队', value: 'team' }
|
||||
],
|
||||
steps: [
|
||||
{ icon: '/static/images/share/step_send.png', label: '发起分享' },
|
||||
{ icon: '/static/images/share/step_add.png', label: '添加分享对象' },
|
||||
{ icon: '/static/images/share/step_key.png', label: '选择视频和权限' },
|
||||
{ icon: '/static/images/share/step_mail.png', label: '接受分享' }
|
||||
],
|
||||
featureTags: ['功能按需分享', '最多分享10人', '仅视频设备'],
|
||||
activeSubTab: 'tome', // 默认展示"分享给我的"
|
||||
subTabs: [
|
||||
{ label: '我的分享', value: 'mine' },
|
||||
{ label: '分享给我的', value: 'tome' }
|
||||
],
|
||||
toMeDevices: [
|
||||
{
|
||||
id: 5,
|
||||
name: 'DS-7804N-Z1/X(D)(F...',
|
||||
img: '/static/images/share/nvr.png',
|
||||
channels: 3,
|
||||
serial: 'FX3773731'
|
||||
}
|
||||
],
|
||||
mySharedDevices: [
|
||||
{ id: 1, name: '八方', img: '/static/images/share/camera1.png', sharedCount: 1 },
|
||||
{ id: 2, name: '十方', img: '/static/images/share/camera2.png', sharedCount: 1 },
|
||||
{ id: 3, name: 'DS-2DE4423DW-D/GLT/XM(...', img: '/static/images/share/camera1.png', sharedCount: 1 },
|
||||
{ id: 4, name: 'DS-7804N-Z1/X(D)(FR5725238)', img: '/static/images/share/nvr.png', sharedCount: 5 }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToDetail(device) {
|
||||
uni.navigateTo({ url: `/pages/home/invite/myShareDetail/index?id=${device.id}` })
|
||||
},
|
||||
goToShareTarget() {
|
||||
uni.navigateTo({ url: '/pages/home/invite/shareTarget/index' })
|
||||
},
|
||||
quitShare(device) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `确定退出【${device.name}】的分享?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.toMeDevices = this.toMeDevices.filter(d => d.id !== device.id)
|
||||
uni.showToast({ title: '已退出分享', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: linear-gradient(180deg, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.page-bg {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 24rpx 30rpx 0;
|
||||
background: #e0e5ef;
|
||||
border-radius: 40rpx;
|
||||
padding: 6rpx;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 14rpx 0;
|
||||
border-radius: 34rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
|
||||
&.active {
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.steps-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 30rpx 30rpx 0;
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-icon-wrap {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.step-label {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
margin-top: 8rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-line {
|
||||
position: absolute;
|
||||
top: 30rpx;
|
||||
right: -20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #4a90e2;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.feature-tags {
|
||||
display: flex;
|
||||
margin: 20rpx 30rpx;
|
||||
gap: 16rpx;
|
||||
|
||||
.feature-tag {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 22rpx;
|
||||
|
||||
&:first-child { background: #e8f0fe; color: #3d7ff5; }
|
||||
&:nth-child(2) { background: #fff3e0; color: #f5a623; }
|
||||
&:nth-child(3) { background: #fff3e0; color: #f5a623; }
|
||||
}
|
||||
}
|
||||
|
||||
.sub-tabs {
|
||||
display: flex;
|
||||
margin: 0 30rpx 0;
|
||||
border-bottom: 2rpx solid #e5e5e5;
|
||||
|
||||
.sub-tab {
|
||||
padding: 16rpx 30rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
position: relative;
|
||||
|
||||
&-active {
|
||||
color: #1a1a1a;
|
||||
font-weight: bold;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background: #1a1a1a;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-label {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
padding: 20rpx 20rpx 8rpx;
|
||||
}
|
||||
|
||||
.device-list {
|
||||
margin: 0 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.device-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 28rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
.device-img {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.device-info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.device-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.device-sub {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
color: #ccc;
|
||||
font-size: 36rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.quit-btn {
|
||||
font-size: 24rpx;
|
||||
color: #555;
|
||||
background: #fff;
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 30rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.device-section {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
font-size: 26rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 20rpx 30rpx;
|
||||
background: #edf1f7;
|
||||
|
||||
.share-btn {
|
||||
background: #3d7ff5;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
font-size: 32rpx;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
855
pages/index.vue
|
|
@ -1,853 +1,36 @@
|
|||
<template>
|
||||
<view class="page-container">
|
||||
<z-paging ref="home" refresher-only bg-color="radial-gradient(circle at top left, #E8F4F8 40%, #F8FCFE 100%)"
|
||||
:show-empty="false" :show-footer="false" @onRefresh="refresh">
|
||||
<template #refresher="{refresherStatus}">
|
||||
<!-- 此处的custom-refresh为demo中自定义的组件,非z-paging的内置组件,请在实际项目中自行创建。这里插入什么view,下拉刷新就显示什么view -->
|
||||
<custom-refresher :status="refresherStatus" />
|
||||
</template>
|
||||
<template #top>
|
||||
<uni-row class="demo-uni-row" >
|
||||
<uni-col :span="7">
|
||||
<uni-data-select align="left" :clear="false" v-model="value" @change="change" :localdata="range" ></uni-data-select>
|
||||
</uni-col>
|
||||
<uni-col :span="17">
|
||||
<!-- confirm:confirm; input:change; cancel:点击取消;
|
||||
clear:点击清除;focus:获取焦点;blur:失去焦点 -->
|
||||
<uni-search-bar bgColor="#fbfdfe"
|
||||
v-model="searchValue"
|
||||
cancelButton="always"
|
||||
@input="input"
|
||||
clearButton="always"
|
||||
@cancel="cancel"
|
||||
@clear="cancel"
|
||||
placeholder="请输入搜索内容">
|
||||
<template v-slot:searchIcon>
|
||||
<yt-scanCode @getScanCode="getScanCode"></yt-scanCode>
|
||||
</template>
|
||||
</uni-search-bar>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
</template>
|
||||
|
||||
<uni-section class="mb-10" title="温室列表" style="background:transparent;" type="line">
|
||||
<template v-slot:right>
|
||||
<view>
|
||||
<text style="color: #f64040">{{ dataDetails.online }} 在线 </text>
|
||||
<text style="color: #2d56d8;margin-left: 10rpx">{{ dataDetails.offline }} 离线</text>
|
||||
<view class="content">
|
||||
<image class="logo" src="@/static/logo200.png"></image>
|
||||
<view class="text-area">
|
||||
<text class="title">Hello Agri</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="list-scroll"
|
||||
@scrolltolower="loadMore"
|
||||
lower-threshold="50"
|
||||
scroll-with-animation
|
||||
>
|
||||
<uni-swipe-action>
|
||||
<uni-swipe-action-item
|
||||
v-for="(item, index) in listData"
|
||||
:key="index"
|
||||
@click="onDeleteItem($event, item)"
|
||||
:class="['list-item', { 'list-item-disabled': item.online === '离线' }]"
|
||||
:right-options="options">
|
||||
<view class="item-card"
|
||||
@click="onItemTap(item)">
|
||||
<view class="item-title">
|
||||
<view class="title-text">{{ item.agriName }}</view>
|
||||
<text :class="['tag','tag-status',{'tag-manual':item.workModeDesc==='手动模式'}]" v-if="item.workModeDesc">{{ item.workModeDesc }}</text>
|
||||
<text :class="['tag','tag-online',{'tag-offline':item.online==='离线'}]" v-if="item.online">{{ item.online }}</text>
|
||||
</view>
|
||||
|
||||
<view class="item-subtitle">
|
||||
设备编号:{{ item.imei }}
|
||||
</view>
|
||||
|
||||
<view class="item-tags">
|
||||
<text class="tag tag-temp1">温度1: {{ item.temp1 }}℃</text>
|
||||
<text class="tag tag-temp2">温度2: {{ item.temp2 }}℃</text>
|
||||
<text class="tag tag-temp3">温度3: {{ item.temp3 }}℃</text>
|
||||
<text class="tag tag-temp4">温度4: {{ item.temp4 }}℃</text>
|
||||
</view>
|
||||
</view>
|
||||
</uni-swipe-action-item>
|
||||
|
||||
<view v-if="isLoading" class="loading-more">
|
||||
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="noMore" class="no-more">没有更多数据了</view>
|
||||
<!-- v-if="store.getters.name !== 'admin'"-->
|
||||
<view class="add-agri" @tap="handleAddAgri" v-if="!isLoading">
|
||||
<image :src="imageSrc" :style="'width:'+80+'rpx;height:'+80+'rpx'"></image>
|
||||
<text class="text">点击添加大棚</text>
|
||||
</view>
|
||||
</uni-swipe-action>
|
||||
</scroll-view>
|
||||
</uni-section>
|
||||
<!-- <uni-fab ref="fab" :pattern="pattern" :content="content" :horizontal="horizontal" :vertical="vertical"-->
|
||||
<!-- :direction="direction" @trigger="trigger" @fabClick="fabClick" />-->
|
||||
|
||||
</z-paging>
|
||||
<add-agri ref="addAgri" @reload="getListData"/>
|
||||
|
||||
|
||||
<uni-popup :mask-click="false" ref="renameAgri" mode="center">
|
||||
<!-- 新增:修改运行时间的弹窗 -->
|
||||
<view class="modal-container">
|
||||
<view class="modal-title">{{ `『 ${agriName} 』重命名设置` }}</view>
|
||||
<view class="modal-input-wrap">
|
||||
<text class="modal-label">旧名称:</text>
|
||||
<text class="modal-label">{{ agriName }}</text>
|
||||
</view>
|
||||
<view class="modal-input-wrap">
|
||||
<text class="modal-label">新名称:</text>
|
||||
<uni-easyinput style="width: 100px" v-model="newAgriName" placeholder="请填写新名称"/>
|
||||
</view>
|
||||
<view class="modal-btn-wrap">
|
||||
<button class="modal-btn cancel" @click="closeWindow">取消</button>
|
||||
<button class="modal-btn confirm" @click="updateAgriName">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ZPaging from "../uni_modules/z-paging/components/z-paging/z-paging.vue";
|
||||
import CustomRefresher from "../components/custom-refresher/custom-refresher.vue";
|
||||
import {getAgriInfo, renameAgriName} from "../api/system/assets/agri";
|
||||
import store from "../store";
|
||||
import TimeUtil from "../utils/TimeUtil";
|
||||
import {getNewSpecialData} from "../api/data/specialData";
|
||||
import AddAgri from "../components/addAgri/addAgri.vue";
|
||||
import {removeAgri} from "../api/system/assets/userAgri";
|
||||
import * as mqttUtil from "../utils/mqtt";
|
||||
import {batchUnsubscribe, getAgriStatus} from "../api/system/mqtt";
|
||||
import UniPopup from "../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
|
||||
import {updateSubscribeTopic} from "../utils/mqtt";
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
store() {
|
||||
return store
|
||||
}
|
||||
},
|
||||
components: {UniPopup, AddAgri, CustomRefresher, ZPaging},
|
||||
data() {
|
||||
return {
|
||||
listData: [],
|
||||
datas: [],
|
||||
pageSize: 6,
|
||||
isLoading: false,
|
||||
noMore: false,
|
||||
total: 20, // 模拟总条数
|
||||
value: 0,
|
||||
options: [
|
||||
{
|
||||
text: '重命名',
|
||||
style: {
|
||||
backgroundColor: 'rgba(45,86,216,0.12)',
|
||||
color: '#2d56d8'
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '邀请用户',
|
||||
style: {
|
||||
backgroundColor: 'rgba(27,209,167,0.12)',
|
||||
color: '#1bd1a7'
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '删除',
|
||||
style: {
|
||||
backgroundColor: 'rgba(232,58,48,0.12)',
|
||||
color: "#E83A30"
|
||||
}
|
||||
}
|
||||
],
|
||||
range: [{
|
||||
"value": 0,
|
||||
"text": "大棚名称",
|
||||
}, {
|
||||
"value": 1,
|
||||
"text": "设备编号",
|
||||
}],
|
||||
imageSrc:'https://img.xiaoces.com/photos/agri.png',
|
||||
scrollHeight: 0,
|
||||
// horizontal: 'right',
|
||||
// vertical: 'bottom',
|
||||
// direction: 'horizontal',
|
||||
searchValue: null,
|
||||
imeiToDeviceMap: new Map(),
|
||||
dataDetails: {
|
||||
tip: null,
|
||||
online: 0,
|
||||
offline: 0
|
||||
},
|
||||
/* pattern: {
|
||||
color: '#7A7E83',
|
||||
backgroundColor: '#fff',
|
||||
selectedColor: '#007AFF',
|
||||
buttonColor: '#007AFF',
|
||||
iconColor: '#fff'
|
||||
},
|
||||
content: [{
|
||||
iconPath: '/static/image.png',
|
||||
selectedIconPath: '/static/image-active.png',
|
||||
text: '添加大棚',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
iconPath: '/static/home.png',
|
||||
selectedIconPath: '/static/home-active.png',
|
||||
text: '',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
iconPath: '/static/star.png',
|
||||
selectedIconPath: '/static/star-active.png',
|
||||
text: '收藏',
|
||||
active: false
|
||||
}
|
||||
]*/
|
||||
agriName:null,
|
||||
imei:null,
|
||||
newAgriName:null
|
||||
}
|
||||
|
||||
},
|
||||
onBackPress() {
|
||||
/* if (this.$refs.fab.isShow) {
|
||||
this.$refs.fab.close()
|
||||
return true
|
||||
}
|
||||
return false*/
|
||||
},
|
||||
onShow() {
|
||||
console.info("首页在线状态注册回调")
|
||||
this.$modal.loading("数据加载中,请耐心等待...")
|
||||
this.refresh();
|
||||
},
|
||||
onLoad() {
|
||||
},
|
||||
onHide() {
|
||||
console.info("首页在线状态注销回调")
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
onReady() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
getScanCode(res){
|
||||
this.searchValue = res;
|
||||
this.value = 1;
|
||||
if (this.searchValue) {
|
||||
this.change(this.searchValue)
|
||||
}
|
||||
},
|
||||
handleAddAgri() {
|
||||
this.$refs.addAgri.open();
|
||||
// 这里可以添加跳转到添加大棚页面的逻辑
|
||||
// uni.navigateTo({ url: '/pages/add-agri/add-agri' })
|
||||
},
|
||||
change(e) {
|
||||
this.input(this.searchValue)
|
||||
},
|
||||
input(res) {
|
||||
if (!res) {
|
||||
this.cancel(res)
|
||||
return;
|
||||
}
|
||||
|
||||
this.listData = this.datas.filter(item =>
|
||||
item[this.value === 0 ? 'agriName' : 'imei'].includes(res));
|
||||
},
|
||||
cancel(res) {
|
||||
this.listData = [...this.datas]
|
||||
},
|
||||
/*trigger(e) {
|
||||
console.log(e)
|
||||
this.content[e.index].active = !e.item.active
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `您${this.content[e.index].active ? '选中了' : '取消了'}${e.item.text}`,
|
||||
success: function(res) {
|
||||
if (res.confirm) {
|
||||
console.log('用户点击确定')
|
||||
} else if (res.cancel) {
|
||||
console.log('用户点击取消')
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
fabClick() {
|
||||
uni.showToast({
|
||||
title: '点击了悬浮按钮',
|
||||
icon: 'none'
|
||||
})
|
||||
},*/
|
||||
|
||||
// 模拟获取列表数据
|
||||
getListData() {
|
||||
this.noMore = false;
|
||||
this.listData = [];
|
||||
this.isLoading = true
|
||||
|
||||
getAgriInfo().then(response => {
|
||||
if (response.code === 200) {
|
||||
// 先清空旧索引
|
||||
this.imeiToDeviceMap.clear();
|
||||
// 1. 用 map 处理数据,返回新数组,避免 forEach 返回 undefined
|
||||
// 2. 区分刷新/加载更多,更新 listData
|
||||
this.listData = response.data.map(item => {
|
||||
const deviceItem = {
|
||||
...item,
|
||||
deviceStatus: TimeUtil.isLessThanSpecifiedSeconds(item.time, 90) ? '在线' : '离线',
|
||||
online: '在线',
|
||||
agriName: item.agriName || '未知大棚',
|
||||
imei: `${item.imei || '未知'}`,
|
||||
workModeDesc: item.workMode === 0 ? '手动模式':'自动模式',
|
||||
workMode: item.workMode,
|
||||
temp1: item.temp1,
|
||||
temp2: item.temp2,
|
||||
temp3: item.temp3,
|
||||
temp4: item.temp4
|
||||
};
|
||||
// 存入索引Map
|
||||
this.imeiToDeviceMap.set(deviceItem.imei, deviceItem);
|
||||
return deviceItem;
|
||||
});
|
||||
// 5. 判断是否还有更多数据
|
||||
this.noMore = this.listData.length >= this.total;
|
||||
if (store.getters && store.getters.name === 'admin') {
|
||||
this.getNewSpecialData();
|
||||
}
|
||||
// 左上角详情
|
||||
this.getDataDetails();
|
||||
this.datas = [...this.listData]
|
||||
// 下拉刷新
|
||||
this.getAgriStatus();
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error("获取大棚信息失败:", err);
|
||||
}).finally(() => {
|
||||
// 4. 无论成功失败,都结束加载状态
|
||||
this.isLoading = false;
|
||||
this.$modal.closeLoading()
|
||||
this.$refs.home.complete();
|
||||
});
|
||||
},
|
||||
// 加载更多(防抖:避免快速上拉重复触发)
|
||||
loadMore() {
|
||||
if (!this.isLoading && !this.noMore) {
|
||||
|
||||
}
|
||||
},
|
||||
refresh() {
|
||||
mqttUtil.setOnMessageCallback(this.ackMessage);
|
||||
this.getListData();
|
||||
},
|
||||
onDeleteItem(e, item) {
|
||||
if (e.content.text === '重命名') { // 通过事件对象的 content.text 判断
|
||||
this.agriId = item.id;
|
||||
this.agriName = item.agriName;
|
||||
this.imei = item.imei;
|
||||
this.$refs.renameAgri.open();
|
||||
} else if (e.content.text === '删除') { // 删除操作
|
||||
uni.showModal({
|
||||
title: '操作提示:',
|
||||
content: `确定删除大棚【${item.agriName}】?`,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
removeAgri(item).then(response => {
|
||||
if (response.code === 200) {
|
||||
batchUnsubscribe({clientId: mqttUtil.getMqttState().clientId}).then(response => {
|
||||
}).finally(() => {
|
||||
updateSubscribeTopic();
|
||||
})
|
||||
this.$modal.msgSuccess("删除成功!");
|
||||
this.getListData();
|
||||
} else {
|
||||
this.$modal.msgError("删除失败!");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (e.content.text === '邀请用户') {
|
||||
uni.navigateTo({
|
||||
url: `/pages/home/invite/shareDevice/index`
|
||||
})
|
||||
}
|
||||
},
|
||||
// 点击列表项
|
||||
onItemTap(item) {
|
||||
var agri = JSON.stringify(
|
||||
{
|
||||
imei:item.imei,
|
||||
agriName:item.agriName,
|
||||
agriId:item.id,
|
||||
workMode: item.workMode
|
||||
}
|
||||
);
|
||||
this.$tab.navigateTo('/pages/home/control/index?agriInfo='+encodeURIComponent(agri))
|
||||
},
|
||||
getNewSpecialData() {
|
||||
getNewSpecialData().then(response => {
|
||||
if (response.code === 200 && response.data) {
|
||||
this.makeSpecialData(response.data);
|
||||
}
|
||||
this.datas = [...this.listData]
|
||||
})
|
||||
},
|
||||
|
||||
makeSpecialData: function (msgData){
|
||||
const online = TimeUtil.isLessThanSpecifiedSeconds(msgData.ts,90)?'在线':'离线';
|
||||
const deviceConfigs = [
|
||||
{ agriName: "八方南棚", imei: "A", temps: [1,2,3,4] },
|
||||
{ agriName: "九方春棚", imei: "B", temps: [5,6,7,8] },
|
||||
{ agriName: "十二方棚", imei: "C", temps: [9,10,11,12] }
|
||||
];
|
||||
|
||||
const devices = deviceConfigs.map(({agriName, imei, temps}) => ({
|
||||
agriName,
|
||||
imei,
|
||||
temp1: msgData[`temp${temps[0]}`],
|
||||
temp2: msgData[`temp${temps[1]}`],
|
||||
temp3: msgData[`temp${temps[2]}`],
|
||||
temp4: msgData[`temp${temps[3]}`],
|
||||
online
|
||||
}));
|
||||
|
||||
this.listData.push(...devices);
|
||||
// 状态和温度不需要监听不需要搞
|
||||
// devices.forEach(d => this.imeiToDeviceMap.set(d.imei, d));
|
||||
},
|
||||
ackMessage(topic, payload) {
|
||||
const regex = /^device\/\d+\/status$/;
|
||||
const regex1 = /^frontend\/.+\/dtu\/\d+\/listener$/;
|
||||
|
||||
if (!regex.test(topic) && !regex1.test(topic)) return;
|
||||
|
||||
let msgData = {};
|
||||
// 优化:捕获JSON解析异常
|
||||
try {
|
||||
msgData = JSON.parse(payload);
|
||||
} catch (e) {
|
||||
console.error("MQTT消息解析失败:", e, payload);
|
||||
return;
|
||||
}
|
||||
// 设备在线状态
|
||||
if (regex.test(topic) && msgData.online && "time" in msgData && "imei" in msgData) {
|
||||
this.updateDeviceStatusFast(msgData.imei, msgData.online)
|
||||
}
|
||||
// 温度
|
||||
const allKeysNumeric2 = Object.keys(msgData).every(key => /^\d+$/.test(key));
|
||||
if (regex1.test(topic) && Object.keys(msgData).length > 0 && allKeysNumeric2) {
|
||||
this.updateTempFast(msgData, topic)
|
||||
}
|
||||
this.getDataDetails();
|
||||
},
|
||||
// 离线状态查询 是先获取大棚表,无线表不存在大棚表里
|
||||
updateDeviceStatusFast(targetImei, isOnline) {
|
||||
console.info("监听到状态",targetImei, isOnline)
|
||||
// 直接从Map里取设备项,无需遍历数组
|
||||
const targetDevice = this.imeiToDeviceMap.get(targetImei);
|
||||
if (!targetDevice) return;
|
||||
// 直接修改对象属性,响应式正常生效
|
||||
targetDevice.deviceStatus = isOnline;
|
||||
targetDevice.online = isOnline;
|
||||
},
|
||||
|
||||
updateTempFast: function (msgData,topic) {
|
||||
const regexWithGroup = /^frontend\/(.+)\/dtu\/(\d+)\/listener$/;
|
||||
const matchResult = topic.match(regexWithGroup);
|
||||
const imei = matchResult[2]; // 提取结果:'1234567890'
|
||||
// 直接从Map里取设备项,无需遍历数组
|
||||
const targetDevice = this.imeiToDeviceMap.get(imei);
|
||||
if (!targetDevice) return;
|
||||
const div10 = (v) => (v == null ? null : Math.round((Number(v) / 10) * 10) / 10);
|
||||
// 直接修改对象属性,响应式正常生效
|
||||
|
||||
targetDevice.temp1 = div10(msgData['201']);
|
||||
targetDevice.temp2 = div10(msgData['202']);
|
||||
targetDevice.temp3 = div10(msgData['203']);
|
||||
targetDevice.temp4 = div10(msgData['204']);
|
||||
|
||||
// if (imei === '862538065276061') {
|
||||
// const deviceConfigs = [
|
||||
// { agriName: "八方南棚", imei: "A", temps: [1,2,3,4] },
|
||||
// { agriName: "九方春棚", imei: "B", temps: [5,6,7,8] },
|
||||
// { agriName: "十二方棚", imei: "C", temps: [9,10,11,12] }
|
||||
// ];
|
||||
//
|
||||
// deviceConfigs.forEach(({agriName, imei, temps}) => {
|
||||
// var target = this.imeiToDeviceMap.get(imei);
|
||||
// target.temp1 = div10(msgData[`20${temps[0]}`]);
|
||||
// target.temp2 = div10(msgData[`20${temps[1]}`]);
|
||||
// target.temp3 = div10(msgData[`20${temps[2]}`]);
|
||||
// target.temp4 = div10(msgData[`20${temps[3]}`]);
|
||||
// })
|
||||
// }
|
||||
},
|
||||
getDataDetails() {
|
||||
this.dataDetails = {
|
||||
tip: null,
|
||||
online: 0,
|
||||
offline: 0
|
||||
}
|
||||
var online = 0;
|
||||
var offline = 0;
|
||||
this.listData.forEach(item => {
|
||||
if (item.online === '离线') {
|
||||
offline += 1;
|
||||
} else {
|
||||
online += 1;
|
||||
}
|
||||
})
|
||||
this.dataDetails = {
|
||||
tip: null,
|
||||
online: online,
|
||||
offline: offline,
|
||||
}
|
||||
},
|
||||
getAgriStatus() {
|
||||
if (!this.listData || this.listData.length === 0) return []; // 空列表处理
|
||||
const allImei = this.listData.map(item => item.imei).filter(imei => imei);
|
||||
getAgriStatus([...new Set(allImei)])
|
||||
},
|
||||
closeWindow() {
|
||||
this.agriName = null;
|
||||
this.imei = null;
|
||||
this.newAgriName = null;
|
||||
this.$refs.renameAgri.close();
|
||||
},
|
||||
updateAgriName() {
|
||||
if (!this.newAgriName || (this.newAgriName === this.agriName)) {
|
||||
this.$modal.msgError("请输入新名称!");
|
||||
return;
|
||||
}
|
||||
if (this.newAgriName.length > 10) {
|
||||
this.$modal.msgError("新名称长度不能超过10个字符!");
|
||||
return;
|
||||
}
|
||||
uni.showModal({
|
||||
title: '操作提示:',
|
||||
content: `确定将大棚【${this.agriName}】重命名为【${this.newAgriName}】?`,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
renameAgriName({
|
||||
imei: this.imei,
|
||||
newAgriName: this.newAgriName,
|
||||
agriId: this.agriId
|
||||
}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.$modal.msgSuccess("大棚重命名成功!");
|
||||
this.getListData(); // 刷新列表
|
||||
this.closeWindow() // 关闭弹窗
|
||||
} else {
|
||||
this.$modal.msgError("大棚重命名失败!");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 适配小程序/H5的盒模型,避免padding导致宽度溢出 */
|
||||
page {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.page-container {
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.list-scroll {
|
||||
/* 把背景色改为透明,才能透出背景渐变 */
|
||||
background-color: transparent;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
margin-top: 10rpx;
|
||||
/* 新增:H5端避免滚动条样式不一致 */
|
||||
scrollbar-width: thin;
|
||||
/* 去掉固定高度,改为自适应(避免小程序滚动异常) */
|
||||
height: calc(100vh - 180rpx);
|
||||
/* #ifdef MP-WEIXIN */
|
||||
height: calc(100vh - var(--status-bar-height) - 180rpx);
|
||||
/* #endif */
|
||||
}
|
||||
/* H5端滚动条样式优化(可选) */
|
||||
.list-scroll::-webkit-scrollbar {
|
||||
width: 4rpx;
|
||||
}
|
||||
.list-scroll::-webkit-scrollbar-thumb {
|
||||
background-color: #e0e0e0;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
.item-card {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 8rpx 5rpx 10rpx #999;
|
||||
margin-bottom: 20rpx;
|
||||
// #ifndef H5
|
||||
//margin-right: 10rpx;
|
||||
// #endif
|
||||
}
|
||||
/deep/ .uni-swipe {
|
||||
// #ifdef MP-WEIXIN
|
||||
height: 214rpx !important;
|
||||
// #endif
|
||||
}
|
||||
/deep/ .uni-swipe_button {
|
||||
// #ifdef MP-WEIXIN
|
||||
height: 198rpx !important;
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
height: 192rpx !important;
|
||||
// #endif
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 8rpx 5rpx 10rpx #999;
|
||||
}
|
||||
|
||||
.list-item,/deep/.tn-swipe-action-item {
|
||||
height: 190rpx !important;
|
||||
/* 移除border-bottom,改为margin-top,并且把列表项背景设为半透明白色 */
|
||||
// #ifdef H5
|
||||
margin-bottom: 20rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
/* 新增:点击态样式,双端统一 */
|
||||
transition: all 0.2s ease;
|
||||
overflow: hidden;
|
||||
box-shadow: 8rpx 5rpx 10rpx #999;
|
||||
pointer-events: auto !important;
|
||||
// #endif
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
line-height: 52rpx;
|
||||
font-weight: bolder;
|
||||
color: #333;
|
||||
/* 核心:flex布局,让文字和标签在同一行 */
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 14rpx 25rpx 0 25rpx;
|
||||
justify-content: center;
|
||||
}
|
||||
.title-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* 在线标签样式 */
|
||||
.tag-online {
|
||||
font-size: 20rpx;
|
||||
border-radius: 4rpx;
|
||||
background-color: #f0f9eb;
|
||||
color: #67c23a;
|
||||
white-space: nowrap;
|
||||
line-height: 30rpx;
|
||||
/* 兜底:如果flex失效,用margin-left:auto强制居右 */
|
||||
|
||||
.logo {
|
||||
height: 200rpx;
|
||||
width: 200rpx;
|
||||
margin-top: 200rpx;
|
||||
margin-left: auto;
|
||||
display: inline-block; /* 确保样式生效 */
|
||||
}
|
||||
.tag-status.tag-manual {
|
||||
background-color: #dde3f6;
|
||||
color: #7491ef;
|
||||
}
|
||||
/* 离线:深灰色系 */
|
||||
.tag-offline {
|
||||
background-color: #f2f2f2;
|
||||
color: #c5c3c3;
|
||||
}
|
||||
.item-subtitle {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 52rpx;
|
||||
display: block;
|
||||
padding: 0 25rpx;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.item-tags {
|
||||
.text-area {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
flex-wrap: wrap; /* 新增:标签过多时换行,避免溢出 */
|
||||
padding: 14rpx 25rpx 28rpx 25rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 4rpx;
|
||||
display: inline-block; /* 修复H5端padding不生效问题 */
|
||||
}
|
||||
.tag-status {
|
||||
background-color: #f8f1f1;
|
||||
color: #e66060;
|
||||
margin-left: 20rpx;
|
||||
font-weight: lighter;
|
||||
line-height: 30rpx;
|
||||
padding: 4rpx 6rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.tag-temp1 {
|
||||
background-color: #e8f4ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.tag-temp2 {
|
||||
background-color: #fdf4e4;
|
||||
color: #f5961d;
|
||||
}
|
||||
|
||||
.tag-temp3 {
|
||||
background-color: #f4f4f5;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.tag-temp4 {
|
||||
background-color: #e7faf4;
|
||||
color: #1bd1a7;
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 30rpx;
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
gap: 10rpx;
|
||||
margin-top: 20rpx;
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.no-more {
|
||||
/* 核心:flex布局实现居中 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* 占满滚动容器剩余高度,保证垂直居中 */
|
||||
min-height: 200rpx; /* 最小高度,避免内容太少时不居中 */
|
||||
/* 文字样式保留 */
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
/* 可选:增加上下内边距,视觉更舒适 */
|
||||
padding: 30rpx 0;
|
||||
margin-top: 20rpx;
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 12rpx;
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #8f8f94;
|
||||
}
|
||||
|
||||
/* 新增:禁用状态样式 */
|
||||
.list-item-disabled {
|
||||
/* 文字置灰 */
|
||||
color: #999;
|
||||
/* 禁用光标 */
|
||||
cursor: not-allowed;
|
||||
/* 背景色变浅,视觉区分 */
|
||||
background-color: #fafafa;
|
||||
/* 可选:添加透明度 */
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 禁用状态下的子元素也置灰 */
|
||||
.list-item-disabled .tag {
|
||||
color: #ccc;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
/deep/ .uni-searchbar {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
/deep/ .uni-searchbar__box, /deep/ .uni-select {
|
||||
box-shadow: 8rpx 5rpx 10rpx #999;
|
||||
background: #fbfdfe;
|
||||
border-radius: 0 !important;
|
||||
height: 70rpx !important;
|
||||
}
|
||||
/deep/ .uni-searchbar__box {
|
||||
border-radius: 0 10rpx 10rpx 0 !important;
|
||||
}
|
||||
.uni-stat__select {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
/deep/ .uni-select {
|
||||
border: 0;
|
||||
// #ifdef MP-WEIXIN
|
||||
margin-top: 20rpx !important;
|
||||
// #endif
|
||||
border-radius: 10rpx 0 0 10rpx !important;
|
||||
}
|
||||
/deep/ .uni-section {
|
||||
background: transparent !important;
|
||||
}
|
||||
/* 强制隐藏搜索图标容器 */
|
||||
/*/deep/ .uni-searchbar__box-icon-search {
|
||||
display: none !important;
|
||||
}*/
|
||||
/deep/ .uni-section .uni-section-header {
|
||||
padding: 12rpx 10rpx;
|
||||
}
|
||||
|
||||
/* 添加大棚容器样式 */
|
||||
.add-agri {
|
||||
/* 核心:flex布局实现一行显示 + 垂直居中 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* 水平居中(可选,根据需求调整) */
|
||||
justify-content: center;
|
||||
/* 内边距,增加点击区域和视觉间距 */
|
||||
padding: 30rpx 0;
|
||||
/* 可选:添加背景和圆角,和列表项风格统一 */
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 12rpx;
|
||||
/* 点击态效果,和列表项保持一致 */
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.add-agri:active {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* 图片和文字的间距 */
|
||||
.add-agri image {
|
||||
margin-right: 20rpx;
|
||||
/* 可选:图片垂直对齐,兜底兼容 */
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* 文字样式优化 */
|
||||
.add-agri .text {
|
||||
/* 文字大小,和标签风格统一 */
|
||||
font-size: 26rpx;
|
||||
/* 文字颜色(保留原有绿色) */
|
||||
color: #1AAD19;
|
||||
/* 可选:加粗,突出按钮感 */
|
||||
font-weight: 500;
|
||||
/* 兜底:垂直对齐 */
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/deep/ .z-paging-content-fixed {
|
||||
padding: 20rpx !important;
|
||||
/* 可选:防止margin塌陷,加overflow */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -26,22 +26,12 @@
|
|||
</view>
|
||||
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
|
||||
<view class="iconfont icon-code icon"></view>
|
||||
<input v-model="loginForm.code" style="margin-left: auto" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
|
||||
<input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
|
||||
<view class="login-code">
|
||||
<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="login-page" v-if="false">
|
||||
<!-- 授权登录按钮 -->
|
||||
<view class="submit-btn" @tap.stop="openAuthorizationModal">
|
||||
授权登录
|
||||
</view>
|
||||
|
||||
<wx-user-info-modal
|
||||
v-model="showAuthorizationModal"
|
||||
@updated="updatedUserInfoEvent"
|
||||
></wx-user-info-modal>
|
||||
</view>
|
||||
<!-- ========== 新增:uni-data-checkbox 记住密码(可正常勾选) ========== -->
|
||||
<view style="margin: 10px 0 0 10px; text-align: left;">
|
||||
<uni-data-checkbox
|
||||
|
|
@ -60,10 +50,7 @@
|
|||
</view>
|
||||
<view class="reg text-center" v-if="register">
|
||||
<text class="text-grey1">没有账号?</text>
|
||||
<text @click="handleUserRegister" class="text-blue contact-text">立即注册</text>
|
||||
<button size="mini" class="btn-cs" open-type="contact">
|
||||
<text class="text-blue">点击咨询</text>
|
||||
</button>
|
||||
<text @click="handleUserRegister" class="text-blue">立即注册</text>
|
||||
</view>
|
||||
<view class="xieyi text-center">
|
||||
<text class="text-grey1">登录即代表同意</text>
|
||||
|
|
@ -89,8 +76,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import WxUserInfoModal from '@/uni_modules/tuniaoui-wx-user-info/components/tuniaoui-wx-user-info/tuniaoui-wx-user-info.vue'
|
||||
|
||||
import { getCodeImg } from '@/api/login'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import UniIcons from "../uni_modules/uni-icons/components/uni-icons/uni-icons.vue";
|
||||
|
|
@ -100,12 +85,11 @@ import { getCodeImg } from '@/api/login'
|
|||
|
||||
export default {
|
||||
// ========== 新增:注册组件 ==========
|
||||
components: {UniIcons, UniDataCheckbox,WxUserInfoModal},
|
||||
components: {UniIcons, UniDataCheckbox},
|
||||
data() {
|
||||
return {
|
||||
isShowPwd: false,
|
||||
codeUrl: "",
|
||||
showAuthorizationModal: false,
|
||||
captchaEnabled: true,
|
||||
// 用户注册开关
|
||||
register: true,
|
||||
|
|
@ -127,9 +111,11 @@ import { getCodeImg } from '@/api/login'
|
|||
this.getCode()
|
||||
},
|
||||
onLoad() {
|
||||
//#ifdef H5
|
||||
if (getToken()) {
|
||||
this.$tab.reLaunch('/pages/index')
|
||||
this.$tab.reLaunch('/pages/control/index')
|
||||
}
|
||||
//#endif
|
||||
// ========== 新增:页面加载时自动回填密码 ==========
|
||||
this.loadSavedPwd()
|
||||
},
|
||||
|
|
@ -141,15 +127,6 @@ import { getCodeImg } from '@/api/login'
|
|||
this.remember = true ;
|
||||
}
|
||||
},
|
||||
// 打开获取用户信息弹框
|
||||
openAuthorizationModal() {
|
||||
this.showAuthorizationModal = true
|
||||
},
|
||||
|
||||
// 获取到的用户信息
|
||||
updatedUserInfoEvent(info) {
|
||||
console.log('获取到的用户信息', info)
|
||||
},
|
||||
// ========== 新增:密码加密存储核心方法 ==========
|
||||
// ========== 【小程序修复版】生成加密密钥(无崩溃,兼容微信环境) ==========
|
||||
getEncryptKey() {
|
||||
|
|
@ -305,7 +282,7 @@ import { getCodeImg } from '@/api/login'
|
|||
app.loginSuccess(token)
|
||||
|
||||
// ========== 原有逻辑保留 ==========
|
||||
this.$tab.reLaunch('/pages/index')
|
||||
this.$tab.reLaunch('/pages/control/index')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -405,52 +382,5 @@ import { getCodeImg } from '@/api/login'
|
|||
|
||||
.icp { position: fixed; bottom: 0; width: 100%; background: #fff; padding: 12px 0; box-shadow: 0 -2px 4px rgba(0,0,0,0.1); }
|
||||
.icp-one { max-width: 800px; margin: 0 auto; text-align: center; font-size: 12px; color: #666; }
|
||||
.login-page {
|
||||
width: 300rpx;
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 授权按钮 */
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
background-color: #05C160;
|
||||
color: #FFFFFF;
|
||||
margin-top: 60rpx;
|
||||
border-radius: 10rpx;
|
||||
padding: 25rpx;
|
||||
font-size: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 30rpx;
|
||||
}
|
||||
.btn-cs {
|
||||
font-size: 28rpx;
|
||||
background: #FFFFFF;
|
||||
padding: 0;
|
||||
margin-left: 10rpx;
|
||||
border: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
line-height: 1;
|
||||
height: auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.btn-cs::after {
|
||||
border: none;
|
||||
}
|
||||
button.btn-cs {
|
||||
background-color: transparent !important;
|
||||
border-radius: 0 !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
line-height: inherit !important;
|
||||
}
|
||||
.contact-text {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<view class="about-container">
|
||||
<view class="header-section text-center">
|
||||
<image style="width: 160rpx;height: 160rpx;" src="https://img.xiaoces.com/photos/logo200.png" mode="widthFix">
|
||||
<image style="width: 160rpx;height: 160rpx;" src="/static/logo200.png" mode="widthFix">
|
||||
</image>
|
||||
<uni-title type="h2" title="智能农业移动端"></uni-title>
|
||||
</view>
|
||||
|
|
@ -27,22 +27,22 @@
|
|||
|
||||
<view class="content-section">
|
||||
<view class="mine-actions grid col-4 text-center">
|
||||
<button class="action-item contact-btn" @click="handleJiaoLiuQun">
|
||||
<view class="action-item" @click="handleJiaoLiuQun">
|
||||
<view class="iconfont icon-friendfill text-pink icon"></view>
|
||||
<text class="text">交流群</text>
|
||||
</button>
|
||||
<button class="action-item contact-btn" open-type="contact">
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-service text-blue icon"></view>
|
||||
<text class="text">在线客服</text>
|
||||
</button>
|
||||
<button class="action-item contact-btn" @click="handleBuilding">
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-community text-mauve icon"></view>
|
||||
<text class="text">反馈社区</text>
|
||||
</button>
|
||||
<button class="action-item contact-btn" @click="handleBuilding">
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-dianzan text-green icon"></view>
|
||||
<text class="text">点赞我们</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-list">
|
||||
|
|
@ -84,7 +84,6 @@
|
|||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<button class="logout" type="primary" @click="handleLogout">退出登录</button>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -111,50 +110,37 @@
|
|||
|
||||
methods: {
|
||||
handleToInfo() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/info/index')
|
||||
this.$tab.navigateTo('/pages/mine/info/index')
|
||||
},
|
||||
handleToEditInfo() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/info/edit')
|
||||
this.$tab.navigateTo('/pages/mine/info/edit')
|
||||
},
|
||||
handleToSetting() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/setting/index')
|
||||
this.$tab.navigateTo('/pages/mine/setting/index')
|
||||
},
|
||||
handleToLogin() {
|
||||
this.$tab.reLaunch('/pages/login')
|
||||
},
|
||||
handleToAvatar() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/avatar/index')
|
||||
this.$tab.navigateTo('/pages/mine/avatar/index')
|
||||
},
|
||||
handleHelp() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/help/index')
|
||||
this.$tab.navigateTo('/pages/mine/help/index')
|
||||
},
|
||||
handleMqtt() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/mqtt/index')
|
||||
this.$tab.navigateTo('/pages/mine/mqtt/index')
|
||||
},
|
||||
handleRequire() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/require/index')
|
||||
this.$tab.navigateTo('/pages/mine/require/index')
|
||||
},
|
||||
handleAbout() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/about/index')
|
||||
this.$tab.navigateTo('/pages/mine/about/index')
|
||||
},
|
||||
handleJiaoLiuQun() {
|
||||
this.$modal.showToast('QQ群:①133713780(满)、②146013835(满)、③189091635')
|
||||
},
|
||||
handleBuilding() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
},
|
||||
handleLogout() {
|
||||
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
// ========== 新增:登出时断开MQTT并清空状态 ==========
|
||||
// 1. 获取全局App实例
|
||||
const app = getApp()
|
||||
// 2. 调用App.vue的logout方法(断开MQTT+清空订阅列表)
|
||||
app.logout()
|
||||
}).finally(()=>{
|
||||
this.$tab.reLaunch('/pages/index')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -216,26 +202,10 @@
|
|||
.text {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
.contact-btn {
|
||||
background: transparent;
|
||||
line-height: 50rpx;
|
||||
margin: 8px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .action-item.contact-btn::after {
|
||||
border: none !important;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
/* 或者写成 border: 0 !important; */
|
||||
}
|
||||
}
|
||||
|
||||
.logout {
|
||||
margin: 25rpx;
|
||||
background: #4e84f2;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -65,20 +65,7 @@
|
|||
submit() {
|
||||
this.$refs.form.validate().then(res => {
|
||||
updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.$modal.msg("修改成功,正在返回登录页面...")
|
||||
setTimeout(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
// ========== 新增:登出时断开MQTT并清空状态 ==========
|
||||
// 1. 获取全局App实例
|
||||
const app = getApp()
|
||||
// 2. 调用App.vue的logout方法(断开MQTT+清空订阅列表)
|
||||
app.logout()
|
||||
}).finally(()=>{
|
||||
this.$tab.reLaunch('/pages/index')
|
||||
})
|
||||
},1000)
|
||||
}
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -216,16 +216,16 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import UniTh from "../../../../uni_modules/uni-table/components/uni-th/uni-th.vue";
|
||||
import UniTr from "../../../../uni_modules/uni-table/components/uni-tr/uni-tr.vue";
|
||||
import UniTd from "../../../../uni_modules/uni-table/components/uni-td/uni-td.vue";
|
||||
import {addRequire, delRequire, delRequires, listRequire, updateRequire} from "../../../../api/system/require";
|
||||
import UniTable from "../../../../uni_modules/uni-table/components/uni-table/uni-table.vue";
|
||||
import UniPopup from "../../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
|
||||
import UniForms from "../../../../uni_modules/uni-forms/components/uni-forms/uni-forms.vue";
|
||||
import UniFormsItem from "../../../../uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue";
|
||||
import UniEasyinput from "../../../../uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue";
|
||||
import UniDataSelect from "../../../../uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue";
|
||||
import UniTh from "../../../uni_modules/uni-table/components/uni-th/uni-th.vue";
|
||||
import UniTr from "../../../uni_modules/uni-table/components/uni-tr/uni-tr.vue";
|
||||
import UniTd from "../../../uni_modules/uni-table/components/uni-td/uni-td.vue";
|
||||
import {addRequire, delRequire, delRequires, listRequire, updateRequire} from "../../../api/system/require";
|
||||
import UniTable from "../../../uni_modules/uni-table/components/uni-table/uni-table.vue";
|
||||
import UniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
|
||||
import UniForms from "../../../uni_modules/uni-forms/components/uni-forms/uni-forms.vue";
|
||||
import UniFormsItem from "../../../uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue";
|
||||
import UniEasyinput from "../../../uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue";
|
||||
import UniDataSelect from "../../../uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -467,8 +467,7 @@ export default {
|
|||
<style scoped>
|
||||
/* 页面基础样式重置 */
|
||||
page {
|
||||
padding-top: 20rpx !important;
|
||||
padding-bottom: 20rpx !important;
|
||||
padding-top: 0 !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
|
@ -20,6 +20,13 @@
|
|||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cu-list menu">
|
||||
<view class="cu-item item-box">
|
||||
<view class="content text-center" @click="handleLogout">
|
||||
<text class="text-black">退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -32,7 +39,7 @@
|
|||
},
|
||||
methods: {
|
||||
handleToPwd() {
|
||||
this.$tab.navigateTo('/pages/mine/subpages/pwd/index')
|
||||
this.$tab.navigateTo('/pages/mine/pwd/index')
|
||||
},
|
||||
handleToUpgrade() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
|
|
@ -40,7 +47,19 @@
|
|||
handleCleanTmp() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
},
|
||||
|
||||
handleLogout() {
|
||||
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
// ========== 新增:登出时断开MQTT并清空状态 ==========
|
||||
// 1. 获取全局App实例
|
||||
const app = getApp()
|
||||
// 2. 调用App.vue的logout方法(断开MQTT+清空订阅列表)
|
||||
app.logout()
|
||||
}).finally(()=>{
|
||||
this.$tab.reLaunch('/pages/control/index')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,538 +0,0 @@
|
|||
<template>
|
||||
<view class="message-center-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-left">
|
||||
<text class="nav-title" @click="navBack">{{ title }} </text>
|
||||
<image style="width: 25rpx; height: 25rpx; margin-left: 8rpx; "
|
||||
@click="navBack" src="https://img.xiaoces.com/photos/next.png"/>
|
||||
<uni-drawer ref="showLeft" mode="left" :width="320" @change="change($event,'showLeft')">
|
||||
<view class="close">
|
||||
<button @click="closeDrawer('showLeft')"><text class="word-btn-white">暂未开发</text></button>
|
||||
</view>
|
||||
</uni-drawer>
|
||||
</view>
|
||||
<view class="nav-right">
|
||||
<!-- 把文字 + 图标放在同一个 nav-btn 内 -->
|
||||
<view class="nav-btn" @click="clearUnread">
|
||||
<text>清除未读</text>
|
||||
<uni-icons type="trash" size="24" color="#666" />
|
||||
</view>
|
||||
<text class="divider"> | </text>
|
||||
<text class="nav-btn" @click="openSetting">通知设置</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 系统通知提示条 -->
|
||||
<view class="notice-out">
|
||||
<view class="notice-bar" v-if="isShowTips">
|
||||
<view class="notice-left">
|
||||
<uni-icons @click="goToClose" style="margin-right: 12rpx" type="closeempty" color="#FF6D00" size="15"/>
|
||||
<text class="notice-text">系统通知未开启,报警消息无法通知</text>
|
||||
</view>
|
||||
<view class="notice-btn" @click="goToOpen">去开启</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能入口区 -->
|
||||
<view class="func-wrap">
|
||||
<view class="func-item" @click="goToSubscribe">
|
||||
<view class="func-icon">
|
||||
<image style="width: 40rpx; height: 30rpx; "
|
||||
src="https://img.xiaoces.com/photos/news/new2.png"/>
|
||||
</view>
|
||||
<view class="func-title">消息订阅</view>
|
||||
<view class="func-desc">成员收不到设备/应用消息?</view>
|
||||
</view>
|
||||
<view class="func-item" @click="goToAI">
|
||||
<view class="func-icon">
|
||||
<image style="width: 30rpx; height: 30rpx; "
|
||||
src="https://img.xiaoces.com/photos/news/interact.png"/>
|
||||
</view>
|
||||
<view class="func-title">互动消息</view>
|
||||
<view class="func-desc">暂无互动消息...</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<uni-swipe-action ref="swipeAction" class="message-list">
|
||||
<uni-swipe-action-item
|
||||
v-for="(item, index) in msgList"
|
||||
:right-options="options"
|
||||
:key="index"
|
||||
@click="deleteItem($event, item)"
|
||||
>
|
||||
<view class="message-item" @click="handleItemClick(item)">
|
||||
<view class="item-icon-wrap">
|
||||
<view class="item-icon" :style="{ backgroundColor: item.iconBg }">
|
||||
<image mode="aspectFit" :style="{width: item.width,
|
||||
height: '70rpx' }" :src="item.imgUrl" />
|
||||
</view>
|
||||
<view v-if="item.numBadge>0 && item.badge" class="item-badge"></view>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="item-title">
|
||||
<text>{{ item.title }}</text>
|
||||
<view class="item-time">
|
||||
<uni-dateformat v-if="item.createTime" :date="item.createTime" :format="formatTime(item.createTime)"
|
||||
:threshold="[60000, 3600000]"></uni-dateformat>
|
||||
<text v-else>--</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-desc">
|
||||
<view v-if="item.content">{{ item.content }}</view>
|
||||
<view v-else>暂无更多{{item.title.substring(0,2)}}消息 ... </view>
|
||||
<view v-if="item.numBadge>0 && !item.badge">
|
||||
<uni-badge :text="item.numBadge" type="error" size="mini" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<uni-divider-text margin="10rpx 0"
|
||||
width="50%"
|
||||
font-size="24rpx"
|
||||
lineText="只展示最近7天的消息"/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UniDividerText from "../../components/uni-divider/uni-divider-text.vue";
|
||||
import {isTodayDate,formatTime} from "@/utils/agri";
|
||||
import {getMsgOverview, removeBatch, updateRead} from "@/api/warn/message";
|
||||
import * as mqttUtil from "@/utils/mqtt";
|
||||
export default {
|
||||
components: {
|
||||
UniDividerText
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '消息中心',
|
||||
isShowTips: true,
|
||||
messageList: [],
|
||||
msgList: [
|
||||
{
|
||||
// video111 / camera_
|
||||
imgUrl: 'https://img.xiaoces.com/photos/news/camera_.png', // 对应视频中心
|
||||
title: '视频中心',
|
||||
msgType: 'video',
|
||||
badge: true,
|
||||
numBadge: null, // 有值则显示角标
|
||||
width: '60rpx'
|
||||
},
|
||||
{
|
||||
// device imei1 device_status3
|
||||
imgUrl: 'https://img.xiaoces.com/photos/news/device_status3.png', // 对应设备状态
|
||||
// iconBg: '#4285F4',
|
||||
msgType: 'status',
|
||||
title: '设备状态',
|
||||
badge: false,
|
||||
numBadge: null,
|
||||
width: '60rpx'
|
||||
},
|
||||
{
|
||||
// notice_1 notice_2 notice_
|
||||
imgUrl: 'https://img.xiaoces.com/photos/news/notice_.png', // 对应系统通知
|
||||
iconBg: '#FFA726',
|
||||
msgType: 'notice',
|
||||
title: '系统通知',
|
||||
badge: false,
|
||||
numBadge: null, // 无值则不显示
|
||||
width: '40rpx'
|
||||
},
|
||||
{
|
||||
// play1 play
|
||||
imgUrl: 'https://img.xiaoces.com/photos/news/play1.png',
|
||||
// iconBg: '#FFA726',
|
||||
title: '活动中心',
|
||||
msgType: 'event',
|
||||
badge: false,
|
||||
numBadge: null, // 无值则不显示
|
||||
width: '100rpx'
|
||||
}
|
||||
],
|
||||
options: [
|
||||
{
|
||||
text: '删除',
|
||||
style: {
|
||||
backgroundColor: '#E83A30'
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
console.info("消息中心监听")
|
||||
mqttUtil.setOnMessageCallback(this.ackMsg);
|
||||
this.getMsgOverview();
|
||||
},
|
||||
onHide() {
|
||||
console.info("消息中心取消监听")
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
onUnload() {
|
||||
console.info("消息中心取消监听")
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
beforeDestroy() {
|
||||
console.info("消息中心取消监听")
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
methods: {
|
||||
isTodayDate,
|
||||
formatTime,
|
||||
navBack() {
|
||||
// 消息推送设置
|
||||
this.showDrawer('showLeft');
|
||||
console.log('点击消息中心:消息推送设置')
|
||||
},
|
||||
clearUnread() {
|
||||
// 全部已读
|
||||
uni.showModal({
|
||||
title: '操作提示',
|
||||
content: `确认全部已读?`,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
var unReadNum = this.messageList.filter(item => item.readStatus === 0).length;
|
||||
if (unReadNum === 0) {
|
||||
return;
|
||||
}
|
||||
updateRead({}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.getMsgOverview();
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
openSetting() {
|
||||
// 当前页面勿扰模式设置
|
||||
this.$modal.showToast('模块建设中~')
|
||||
console.log('打开通知设置:当前页面勿扰模式设置')
|
||||
},
|
||||
goToOpen() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
console.log('去开启系统通知:同左侧消息中心')
|
||||
},
|
||||
goToClose() {
|
||||
this.isShowTips = false
|
||||
this.$modal.showToast('模块建设中~')
|
||||
console.log('关闭系统通知提示')
|
||||
},
|
||||
goToSubscribe() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
console.log('进入消息订阅:暂未开发')
|
||||
},
|
||||
goToAI() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
console.log('进入AI识别:暂未开发')
|
||||
},
|
||||
getMsgOverview() {
|
||||
getMsgOverview().then(response => {
|
||||
if (response.code === 200) {
|
||||
this.messageList = response.data;
|
||||
// this.messageList.forEach((msgItem, msgIndex) => {
|
||||
// this.msgList[msgItem.msgType].numBadge = msgItem.unreadCount || null
|
||||
// this.msgList[msgItem.msgType].content = msgItem.content || null
|
||||
// this.msgList[msgItem.msgType].createTime = msgItem.createTime || null
|
||||
// })
|
||||
|
||||
this.msgList.forEach((item, index) => {
|
||||
item.numBadge = null;
|
||||
item.content = null;
|
||||
item.createTime = null;
|
||||
this.messageList.forEach((msgItem, msgIndex) => {
|
||||
if (msgItem.msgType === item.msgType) {
|
||||
item.numBadge = msgItem.unreadCount
|
||||
item.content = msgItem.content
|
||||
item.createTime = msgItem.createTime
|
||||
}
|
||||
})
|
||||
});
|
||||
if (this.messageList.filter(item => item.readStatus === 0).length>0) {
|
||||
uni.showTabBarRedDot({index: 2})
|
||||
} else {
|
||||
uni.hideTabBarRedDot({index: 2 })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleItemClick(item) {
|
||||
this.$modal.showToast('敬请期待~')
|
||||
console.log('点击消息:', item.title)
|
||||
if (item.title === '系统通知') {
|
||||
this.$tab.navigateTo('/pages/news/subpages/sysNotice/index');
|
||||
} else if (item.title === '活动中心') {
|
||||
this.$tab.navigateTo('/pages/news/subpages/eventCenter/index');
|
||||
} else if (item.title === '视频中心') {
|
||||
this.$tab.navigateTo('/pages/news/subpages/videoCenter/index');
|
||||
} else if (item.title === '设备状态') {
|
||||
this.$tab.navigateTo('/pages/news/subpages/deviceCenter/index');
|
||||
}
|
||||
},
|
||||
deleteItem(event, item) {
|
||||
// 删除当条消息
|
||||
uni.showModal({
|
||||
title: '操作提示',
|
||||
content: `确认删除${item.title}?`,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
removeBatch({msgType:item.msgType}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.getMsgOverview()
|
||||
}
|
||||
})
|
||||
}
|
||||
},)
|
||||
},
|
||||
// 打开窗口
|
||||
showDrawer(e) {
|
||||
this.$refs[e].open()
|
||||
},
|
||||
// 关闭窗口
|
||||
closeDrawer(e) {
|
||||
this.$refs[e].close()
|
||||
},
|
||||
// 抽屉状态发生变化触发
|
||||
change(e, type) {
|
||||
console.log((type === 'showLeft' ? '左窗口' : '右窗口') + (e ? '打开' : '关闭'));
|
||||
this[type] = e
|
||||
},
|
||||
|
||||
ackMsg(topic, payload) {
|
||||
var words = topic.split("/");
|
||||
if (words.length!==3) {
|
||||
return;
|
||||
}
|
||||
const agriList = uni.getStorageSync('agri_list')
|
||||
if (agriList.length === 0 || !agriList.includes(words[1])) {
|
||||
return;
|
||||
}
|
||||
const tag = topic.match(/[^/]+$/)?.[0]
|
||||
var regex = {
|
||||
alarm:1,
|
||||
notice:2,
|
||||
event:3
|
||||
}
|
||||
if (!regex[tag]) return;
|
||||
|
||||
let msgData = {};
|
||||
// 优化:捕获JSON解析异常
|
||||
try {
|
||||
msgData = JSON.parse(payload);
|
||||
var msg = msgData.msgType;
|
||||
if (!regex[tag]) return;
|
||||
const item = this.msgList[regex[tag]];
|
||||
item.numBadge+=1;
|
||||
item.content = msg.content;
|
||||
item.createTime = msg.createTime;
|
||||
} catch (e) {
|
||||
console.error("MQTT消息解析失败:", e, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 页面全屏 + 禁止横向滚动 */
|
||||
.message-center-page {
|
||||
min-height: 100vh;
|
||||
height: calc(100vh - 180rpx);
|
||||
/* #ifdef MP-WEIXIN */
|
||||
height: calc(100vh - var(--status-bar-height) - 180rpx);
|
||||
/* #endif */
|
||||
background: linear-gradient(180deg, #e8f0ff 0%, #f5f7fa 60%, #ffffff 100%);
|
||||
padding-bottom: 120rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden; /* 禁止横向滚动条 */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 44rpx 30rpx 20rpx;
|
||||
// #ifdef MP-WEIXIN
|
||||
padding-top: calc(140rpx + var(--status-bar-height));
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
padding-top: 44px;
|
||||
// #endif
|
||||
|
||||
.nav-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #333;
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
/* 核心:文字+图标同行+居中 */
|
||||
.nav-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
|
||||
uni-icons {
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 0 20rpx;
|
||||
color: #c5c3c3;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notice-out {
|
||||
margin: 20rpx 30rpx;
|
||||
}
|
||||
/* 系统通知提示条 */
|
||||
.notice-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx;
|
||||
background-color: #FFF3E0;
|
||||
border-radius: 12rpx;
|
||||
.notice-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.notice-text {
|
||||
font-size: 28rpx;
|
||||
color: #E65100;
|
||||
}
|
||||
}
|
||||
.notice-btn {
|
||||
padding: 8rpx 20rpx;
|
||||
background-color: #FF9800;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 功能入口区 */
|
||||
.func-wrap {
|
||||
display: flex;
|
||||
margin: 0 30rpx 20rpx;
|
||||
gap: 20rpx;
|
||||
.func-item {
|
||||
flex: 1;
|
||||
padding: 24rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
.func-icon {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.func-title {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.func-desc {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 消息列表 ———— 核心修复:不挤时间 + 不换行 */
|
||||
.message-list {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
.message-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.item-icon-wrap {
|
||||
position: relative;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0; /* 图片不被挤压 */
|
||||
.item-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.item-badge {
|
||||
position: absolute;
|
||||
top: -6rpx;
|
||||
right: -6rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #f5222d;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/* 核心:内容区域不挤压时间 */
|
||||
.item-content {
|
||||
flex: 1;
|
||||
min-width: 0; /* 必须加,防止flex溢出 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.item-title {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text:first-child {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.item-desc {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
line-height: 1.4;
|
||||
max-height: 18.19px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
view:first-child {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
view:last-child {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.item-time {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
<template>
|
||||
<view class="message-page">
|
||||
<!-- 消息列表 -->
|
||||
<scroll-view
|
||||
v-if="messageList.length > 0"
|
||||
:scroll-y="true"
|
||||
class="message-list"
|
||||
:refresher-enabled="true"
|
||||
refresher-background="#f5f5f5"
|
||||
:refresher-triggered="refreshing"
|
||||
:scroll-into-view="scrollId"
|
||||
:scroll-with-animation="false"
|
||||
@refresherrefresh="getList"
|
||||
@scrolltolower="onLoadMore"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in messageList"
|
||||
:key="index"
|
||||
:id="`item${item.id}`"
|
||||
class="message-item"
|
||||
>
|
||||
<!-- 时间标签:如果是第一条或者与上一条时间不同才显示 -->
|
||||
<view class="time-label" v-if="index === 0 || !isSameMinute(item.createTime, messageList[index - 1].createTime)">
|
||||
{{ formatTime(item.createTime) }}
|
||||
</view>
|
||||
|
||||
<view class="champion-card">
|
||||
<view class="sender-info" @click="gotoDevice(item)">
|
||||
<image class="avatar" :src="avatar" mode="aspectFill"></image>
|
||||
<text class="sender-text">{{ item.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more iconType="auto" v-if="messageList.length >=10"
|
||||
:status="status" :content-text="contentText" />
|
||||
</scroll-view>
|
||||
<view class="empty__item empty__view" v-else>
|
||||
<tn-empty icon="https://resource.tuniaokj.com/images/empty/alien/2.png"
|
||||
text="近7天暂无消息" :imgWidth="200"
|
||||
:imgHeight="200"></tn-empty>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listMessage, updateRead} from "@/api/warn/message";
|
||||
import * as mqttUtil from "@/utils/mqtt";
|
||||
import {formatTime} from "@/utils/agri";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
refreshing: false, // 下拉刷新状态
|
||||
avatar: 'https://img.xiaoces.com/photos/logo.png',
|
||||
messageList: [],
|
||||
isHaveOldData: true,
|
||||
scrollId: null,
|
||||
msgList:[],
|
||||
pageNum: 0,
|
||||
status: 'more',
|
||||
contentText: {
|
||||
contentdown: '查看更多',
|
||||
contentrefresh: '加载中.....',
|
||||
contentnomore: '没有更多数据了'
|
||||
},
|
||||
sortNewId: null,
|
||||
sortOldId: null,
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 只允许查询七天以内消息默认展示今天
|
||||
// this.getSortDate();
|
||||
this.getList();
|
||||
},
|
||||
onShow() {
|
||||
console.info("消息日志监听")
|
||||
mqttUtil.setOnMessageCallback(this.ackAlarm);
|
||||
},
|
||||
onHide() {
|
||||
console.info("消息日志取消监听")
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
onUnload() {
|
||||
console.info("消息日志取消监听")
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
beforeDestroy() {
|
||||
console.info("消息日志取消监听")
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
methods: {
|
||||
formatTime,
|
||||
// 判断两个时间是否是同一天同一分钟
|
||||
isSameMinute(time1, time2) {
|
||||
const date1 = new Date(time1);
|
||||
const date2 = new Date(time2);
|
||||
return date1.getFullYear() === date2.getFullYear() &&
|
||||
date1.getMonth() === date2.getMonth() &&
|
||||
date1.getDate() === date2.getDate() &&
|
||||
date1.getHours() === date2.getHours() &&
|
||||
date1.getMinutes() === date2.getMinutes();
|
||||
},
|
||||
|
||||
getSortDate(messageList) {
|
||||
return messageList.sort((a, b) => new Date(a.createTime) - new Date(b.createTime));
|
||||
},
|
||||
getList() {
|
||||
this.refreshing = true;
|
||||
if (!this.isHaveOldData) {
|
||||
setTimeout(() => {
|
||||
this.$modal.msg("没有更早数据!");
|
||||
// 结束刷新状态
|
||||
this.refreshing = false;
|
||||
}, 800);
|
||||
return;
|
||||
}
|
||||
|
||||
var query = {
|
||||
msgType: "status",
|
||||
pageSize: 10,
|
||||
sortOldId: this.sortOldId
|
||||
}
|
||||
listMessage(query).then(response => {
|
||||
if (response.code === 200) {
|
||||
if (response.rows.length > 0) {
|
||||
var newData = this.getSortDate(response.rows);
|
||||
this.sortOldId = response.rows[0].id
|
||||
this.messageList = [...newData, ...this.msgList];
|
||||
this.msgList = [...newData, ...this.msgList]
|
||||
console.info("时间最早的数据id",this.sortOldId)
|
||||
return;
|
||||
}
|
||||
this.isHaveOldData = false;
|
||||
}
|
||||
}).finally(()=>{
|
||||
this.updateRead();
|
||||
if (this.messageList.length>10) {
|
||||
// 在数据更新后设置 scrollId
|
||||
this.$nextTick(() => {
|
||||
// 更新数据的长度
|
||||
this.scrollId = "item"+this.messageList[9].id;
|
||||
});
|
||||
}
|
||||
// 结束刷新状态
|
||||
this.refreshing = false;
|
||||
})
|
||||
},
|
||||
updateRead() {
|
||||
if (this.msgList.length === 0) return;
|
||||
var unReadList = this.messageList.filter(item => item.readStatus === 0);
|
||||
if (unReadList.length === 0) return;
|
||||
updateRead({msgType: "status"}).then(response => {
|
||||
if (response.code === 200) {
|
||||
console.info("更新已读成功")
|
||||
}
|
||||
})
|
||||
},
|
||||
onLoadMore() {
|
||||
if (!this.sortNewId) {
|
||||
this.sortNewId = this.msgList[this.msgList.length - 1].id;
|
||||
}
|
||||
this.status = 'loading'
|
||||
var query = {
|
||||
msgType: "status",
|
||||
sortNewId: this.sortNewId
|
||||
}
|
||||
listMessage(query).then(response => {
|
||||
if (response.code === 200) {
|
||||
if (response.rows.length > 0) {
|
||||
var newData = this.getSortDate(response.rows);
|
||||
this.messageList = [...this.msgList, ...newData]
|
||||
this.msgList = [...this.msgList, ...newData]
|
||||
this.sortNewId = response.rows[response.rows.length-1].id
|
||||
return;
|
||||
}
|
||||
this.$modal.msg("已是最新数据了!");
|
||||
// 结束刷新状态
|
||||
this.status = 'nomore'
|
||||
}
|
||||
}).finally(()=>{
|
||||
// 在数据更新后设置 scrollId
|
||||
this.$nextTick(() => {
|
||||
// 更新数据的长度
|
||||
this.scrollId = "item"+this.messageList[this.messageList.length-1].id;
|
||||
});
|
||||
// 结束刷新状态
|
||||
this.status = 'more'
|
||||
})
|
||||
},
|
||||
|
||||
gotoDevice(item) {
|
||||
const agriList = uni.getStorageSync('agri_list')
|
||||
if (agriList.length === 0 || !agriList.includes(item.imei)) {
|
||||
return;
|
||||
}
|
||||
this.$tab.navigateTo(item.linkUrl)
|
||||
},
|
||||
ackAlarm(topic, payload) {
|
||||
const regex = /^device\/\d+\/alarm$/;
|
||||
if (!regex.test(topic)) return;
|
||||
var words = topic.split("/");
|
||||
const agriList = uni.getStorageSync('agri_list')
|
||||
if (agriList.length === 0 || !agriList.includes(words[1])) {
|
||||
return;
|
||||
}
|
||||
let msgData = {};
|
||||
// 优化:捕获JSON解析异常
|
||||
try {
|
||||
msgData = JSON.parse(payload);
|
||||
var msg = msgData.msgType
|
||||
this.messageList.push(msg)
|
||||
} catch (e) {
|
||||
console.error("MQTT消息解析失败:", e, payload);
|
||||
} finally {
|
||||
this.$nextTick(() => {
|
||||
this.scrollId
|
||||
= "item"+this.messageList[this.messageList.length-1].id;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import '@/tuniao-ui/index.scss';
|
||||
@import '@/colorui/main.css';
|
||||
@import '@/colorui/icon.css';
|
||||
</style>
|
||||
<style scoped>
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
/* 页面容器 */
|
||||
.message-page {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* 消息列表 */
|
||||
.message-list {
|
||||
padding: 20rpx;
|
||||
height: 100vh; /* 修复scroll-view高度问题 */
|
||||
}
|
||||
.message-item {
|
||||
margin: 30rpx 20rpx;
|
||||
}
|
||||
|
||||
/* 时间标签 */
|
||||
.time-label {
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin: 50rpx 20rpx 20rpx;
|
||||
}
|
||||
|
||||
.message-item:first-child .time-label {
|
||||
margin-top: 0;
|
||||
}
|
||||
/* 通用卡片样式 */
|
||||
.like-card, .champion-card {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 发送者信息(点赞/冠军) */
|
||||
.sender-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.avatar {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.sender-text {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
.arrow {
|
||||
font-size: 32rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -1,434 +0,0 @@
|
|||
<template>
|
||||
<view class="video-page">
|
||||
<!-- 日期选择栏 -->
|
||||
<view class="date-tabs">
|
||||
<view
|
||||
v-for="(item, index) in dateList"
|
||||
:key="index"
|
||||
class="date-tab"
|
||||
:class="{ active: item.active }"
|
||||
@click="handleDateClick(item)"
|
||||
>
|
||||
<text class="week">{{ item.week }}</text>
|
||||
<text class="day">{{ item.day }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日期标题 + 分类筛选(靠右) -->
|
||||
<view class="filter-bar">
|
||||
<uni-dateformat style="font-size: 40rpx;line-height: 60rpx;font-weight: bolder" :date="date" format="MM-dd" />
|
||||
<view class="category-tabs" v-if="isShowSelect">
|
||||
请选择大棚:<uni-data-select align="left" :clear="false" v-model="imei" :localdata="range" @change="getLogInfo" />
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more iconType="auto" v-if="loading" :status="status" :content-text="contentText" />
|
||||
|
||||
<tn-subsection :list="filmRollerList"
|
||||
:current="current"
|
||||
:bold="true"
|
||||
@change="switchTab"
|
||||
class="subsection"
|
||||
v-if="logList.length>0"
|
||||
:height="80"></tn-subsection>
|
||||
<swiper :current="current" :acceleration="true" style="height: calc(100vh - 300rpx)"
|
||||
v-if="logList.length>0"
|
||||
@change="change">
|
||||
<swiper-item v-for="(roller, key) in filmRollerList"
|
||||
@touchmove.stop
|
||||
:key="key">
|
||||
<!-- 视频列表(带时间轴) -->
|
||||
<scroll-view scroll-y class="log-list" v-if="tabList.length>0">
|
||||
<uni-collapse :accordion="true">
|
||||
|
||||
<!-- 循环 tabList(当前卷膜的数据) -->
|
||||
<uni-collapse-item
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index"
|
||||
:title="`【${agriName}】于${formatDate(item.createTime,'hh:mm:ss')}触发自动化条件执行卷膜${current+1}${item.execCmd}!`"
|
||||
title-border="show"
|
||||
:border="true"
|
||||
>
|
||||
<view class="content">
|
||||
<view>大棚:{{ agriName }}</view>
|
||||
<view>卷膜:卷膜{{ current+1 }}</view>
|
||||
<view>监测时段:{{ item.monitorPeriod }}</view>
|
||||
<view>执行时间:{{ item.createTime }}</view>
|
||||
<view>当时温度:{{ item.currentTemp }}℃</view>
|
||||
<view>参考温度计:{{ item.refTemp.replace('20','温度') }} </view>
|
||||
<view>适宜温度:{{ item.suitableTemp }}℃</view>
|
||||
<view>运行风口:{{ item.fanStatus }}cm</view>
|
||||
<view>是否最后一个条件:{{ item.isLast? '是' : '否' }}</view>
|
||||
<view>执行指令:{{ item.execCmd }}</view>
|
||||
</view>
|
||||
</uni-collapse-item>
|
||||
</uni-collapse>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<view class="empty__item tn-margin-top empty__view" v-if="isShowEmpty">
|
||||
<tn-empty icon="https://resource.tuniaokj.com/images/empty/alien/2.png"
|
||||
text="暂无日志" :imgWidth="200"
|
||||
:imgHeight="200"></tn-empty>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listAgri} from "@/api/system/assets/agri";
|
||||
import {formatDate} from "@/uni_modules/uni-dateformat/components/uni-dateformat/date-format";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dateList: this.getDateList(),
|
||||
logList: [],
|
||||
range: [],
|
||||
imei: '',
|
||||
isShowSelect: true,
|
||||
date: Date.now(),
|
||||
filmRollerList:["卷膜1", "卷膜2", "卷膜3"],
|
||||
current: 0,
|
||||
tabList: [],
|
||||
contentText: {
|
||||
contentdown: '查看更多',
|
||||
contentrefresh: '正在加载中....',
|
||||
contentnomore: '没有更多数据了'
|
||||
},
|
||||
status: 'more',
|
||||
loading: false,
|
||||
agriName: null,
|
||||
isShowEmpty: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 只允许查询七天以内消息默认展示今天
|
||||
// this.videoList = this.getDateList1();
|
||||
},
|
||||
onShow() {
|
||||
|
||||
},
|
||||
onLoad(option) {
|
||||
|
||||
if (option.logInfo) {
|
||||
const decodedStr = decodeURIComponent(option.logInfo);
|
||||
const logInfo = JSON.parse(decodedStr); // 反序列化为原对象
|
||||
this.imei=logInfo.imei;
|
||||
console.info(this.imei)
|
||||
this.agriName = logInfo.agriName;
|
||||
this.getLogInfo();
|
||||
this.isShowSelect = false;
|
||||
}
|
||||
if (!this.imei) {
|
||||
this.getImeiList()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatDate,
|
||||
handleDateClick(item) {
|
||||
this.dateList = this.dateList.map(d => ({...d, active: d === item}))
|
||||
var number = new Date();
|
||||
if (item.day !== '今') {
|
||||
number.setDate(item.day)
|
||||
}
|
||||
this.date = +number;
|
||||
this.getLogInfo()
|
||||
},
|
||||
|
||||
getDateList() {
|
||||
const weekMap = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
const today = new Date();
|
||||
const list = [];
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date(today);
|
||||
date.setDate(today.getDate() - i);
|
||||
|
||||
list.push({
|
||||
week: weekMap[date.getDay()],
|
||||
day: i === 0 ? '今' : date.getDate() + '',
|
||||
active: i === 0
|
||||
});
|
||||
}
|
||||
return list;
|
||||
},
|
||||
|
||||
getDateList1() {
|
||||
const w = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
const t = new Date();
|
||||
return Array.from({length: 7}, (_, i) => {
|
||||
const d = new Date(t);
|
||||
d.setDate(t.getDate() - 6 + i);
|
||||
return {week: w[d.getDay()], day: i === 6 ? '今' : d.getDate() + '', active: i === 6}
|
||||
})
|
||||
},
|
||||
|
||||
getImeiList() {
|
||||
// 获取已启用的设备列表
|
||||
listAgri({status: 1}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.range = response.rows.map(item => ({
|
||||
agriId: item.id,
|
||||
text: item.agriName,
|
||||
value: item.imei // 提取并改名
|
||||
}));
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getLogInfo() {
|
||||
if (!this.agriName) {
|
||||
this.agriName = this.range.find(item => item.value === this.imei).text || null
|
||||
}
|
||||
if (!this.imei || !this.date) {
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
this.status = 'loading';
|
||||
var loginfo = {
|
||||
imei: this.imei,
|
||||
monitorDate: formatDate(this.date, "yyyy-MM-dd")
|
||||
}
|
||||
// getAutoLogList(loginfo).then(response => {
|
||||
// if (response.code === 200) {
|
||||
// this.logList = response.data;
|
||||
// }
|
||||
// }).finally(() => {
|
||||
// if (this.logList && this.logList.length>0) {
|
||||
// this.tabList = this.logList.filter(item => item.roll_film === `jm${this.current+1}`);
|
||||
// } else {
|
||||
// this.isShowEmpty = true;
|
||||
// this.loading = false;
|
||||
// }
|
||||
setTimeout(() => {
|
||||
this.logList = [
|
||||
// jm1 卷膜数据
|
||||
{
|
||||
id: 1,
|
||||
imei: "868402051234567",
|
||||
rollFilm: "jm1",
|
||||
monitorPeriod: "08:00-12:00",
|
||||
currentTemp: 25.3,
|
||||
refTemp: "202",
|
||||
suitableTemp: 25.9,
|
||||
fanStatus: 1.0,
|
||||
isLast: 0,
|
||||
execCmd: "开",
|
||||
logId: 1001,
|
||||
createTime: "2026-03-30 09:15:22"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
imei: "868402051234567",
|
||||
rollFilm: "jm1",
|
||||
monitorPeriod: "12:00-16:00",
|
||||
currentTemp: 28.7,
|
||||
refTemp: "201",
|
||||
suitableTemp: 28.7,
|
||||
fanStatus: 1.0,
|
||||
isLast: 0,
|
||||
execCmd: "关",
|
||||
logId: 1002,
|
||||
createTime: "2026-03-30 13:20:45"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
imei: "868402051234567",
|
||||
rollFilm: "jm1",
|
||||
monitorPeriod: "16:00-20:00",
|
||||
currentTemp: 26.2,
|
||||
refTemp: "202",
|
||||
suitableTemp: 28.2,
|
||||
fanStatus: 0.5,
|
||||
isLast: 1,
|
||||
execCmd: "开",
|
||||
logId: 1003,
|
||||
createTime: "2026-03-30 17:40:18"
|
||||
},
|
||||
// jm2 卷膜数据
|
||||
{
|
||||
id: 4,
|
||||
imei: "868402057654321",
|
||||
rollFilm: "jm2",
|
||||
monitorPeriod: "08:00-12:00",
|
||||
currentTemp: 24.1,
|
||||
refTemp: "202",
|
||||
suitableTemp: 34.2,
|
||||
fanStatus: 0.0,
|
||||
isLast: 0,
|
||||
execCmd: "开",
|
||||
logId: 1004,
|
||||
createTime: "2026-03-30 10:05:11"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
imei: "868402057654321",
|
||||
rollFilm: "jm2",
|
||||
monitorPeriod: "12:00-16:00",
|
||||
currentTemp: 25.8,
|
||||
refTemp: "203",
|
||||
suitableTemp: 24.1,
|
||||
fanStatus: 0.5,
|
||||
isLast: 0,
|
||||
execCmd: "关",
|
||||
logId: 1005,
|
||||
createTime: "2026-03-30 14:35:33"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
imei: "868402057654321",
|
||||
rollFilm: "jm2",
|
||||
monitorPeriod: "16:00-20:00",
|
||||
currentTemp: 23.9,
|
||||
refTemp: "204",
|
||||
suitableTemp: 35.2,
|
||||
fanStatus: 0.0,
|
||||
isLast: 1,
|
||||
execCmd: "开",
|
||||
logId: 1006,
|
||||
createTime: "2026-03-30 18:22:09"
|
||||
},
|
||||
// jm3 卷膜数据
|
||||
{
|
||||
id: 7,
|
||||
imei: "868402059876543",
|
||||
rollFilm: "jm3",
|
||||
monitorPeriod: "08:00-12:00",
|
||||
currentTemp: 23.5,
|
||||
refTemp: "203",
|
||||
suitableTemp: 27,
|
||||
fanStatus: 0.0,
|
||||
isLast: 0,
|
||||
execCmd: "开",
|
||||
logId: 1007,
|
||||
createTime: "2026-03-30 08:45:33"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
imei: "868402059876543",
|
||||
rollFilm: "jm3",
|
||||
monitorPeriod: "12:00-16:00",
|
||||
currentTemp: 27.4,
|
||||
refTemp: "202",
|
||||
suitableTemp: 23.3,
|
||||
fanStatus: 1.0,
|
||||
isLast: 0,
|
||||
execCmd: "关",
|
||||
logId: 1008,
|
||||
createTime: "2026-03-30 12:50:27"
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
imei: "868402059876543",
|
||||
rollFilm: "jm3",
|
||||
monitorPeriod: "16:00-20:00",
|
||||
currentTemp: 25.1,
|
||||
refTemp: "202",
|
||||
suitableTemp: 26.5,
|
||||
fanStatus: 0.5,
|
||||
isLast: 1,
|
||||
execCmd: "开",
|
||||
logId: 1009,
|
||||
createTime: "2026-03-30 16:10:55"
|
||||
}
|
||||
];
|
||||
if (this.logList && this.logList.length>0) {
|
||||
this.tabList = this.logList.filter(item => item.rollFilm === `jm${this.current+1}`);
|
||||
}
|
||||
this.loading = false;
|
||||
}, 1000);
|
||||
},
|
||||
switchTab(e) {
|
||||
this.current=e.index
|
||||
},
|
||||
change(e) {
|
||||
this.current=e.detail.current
|
||||
this.tabList = this.logList.filter(item => item.rollFilm === `jm${this.current+1}`);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面容器 */
|
||||
.video-page {
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 日期选择栏 */
|
||||
.date-tabs {
|
||||
display: flex;
|
||||
padding: 30rpx 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.date-tab {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #666;
|
||||
background: #f7f8fd;
|
||||
margin: 0 10rpx;
|
||||
//border: 1rpx solid #e0e0e0;
|
||||
border-radius: 12rpx;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
.date-tab.active {
|
||||
background-color: #1677ff;
|
||||
color: #fff;
|
||||
border-color: #1677ff;
|
||||
}
|
||||
.week {
|
||||
font-size: 25rpx;
|
||||
margin-bottom: 8rpx;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
.day {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 筛选栏 */
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.log-list {
|
||||
flex: 1;
|
||||
padding: 0 30rpx;
|
||||
height: calc(100vh - 300rpx); /* 关键 */
|
||||
}
|
||||
.content {
|
||||
padding: 20rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
/deep/.uni-select__input-placeholder {
|
||||
font-size: 24rpx !important;
|
||||
}
|
||||
/deep/ .uni-swiper-wrapper {
|
||||
height: calc(100vh - 300rpx);
|
||||
}
|
||||
.subsection {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
/deep/ .uni-collapse-item__title-box {
|
||||
padding: 0;
|
||||
|
||||
}
|
||||
/deep/.uni-collapse-item__title-text {
|
||||
font-size: 25rpx!important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
<template>
|
||||
<view class="message-page">
|
||||
<z-paging ref="eventPage" refresher-only
|
||||
:show-empty="false" :show-footer="false" @onRefresh="getEventList">
|
||||
<template #refresher="{refresherStatus}">
|
||||
<!-- 此处的custom-refresh为demo中自定义的组件,非z-paging的内置组件,请在实际项目中自行创建。这里插入什么view,下拉刷新就显示什么view -->
|
||||
<custom-refresher :status="refresherStatus" />
|
||||
</template>
|
||||
<!-- 消息列表 -->
|
||||
<scroll-view :scroll-y="true"
|
||||
class="message-list"
|
||||
:scroll-top="scrollTop"
|
||||
v-if="messageList.length>0">
|
||||
<view
|
||||
v-for="(item, index) in messageList"
|
||||
:key="index"
|
||||
:id="`item${item.id}`"
|
||||
@click="gotoDetail(item)"
|
||||
class="message-item"
|
||||
>
|
||||
<!-- 图片区域(带未读角标 + 渐变遮罩) -->
|
||||
<view class="img-box">
|
||||
<image class="bg-img" :src="item.imageUrl" mode="aspectFill"></image>
|
||||
<!-- 新增:底部渐变遮罩层 -->
|
||||
<view class="gradient-mask"></view>
|
||||
<view class="unread-tag" v-if="item.readStatus===0">未读</view>
|
||||
<text class="img-title">{{ item.title }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 额外描述文本(仅第三条有) -->
|
||||
<view class="desc-text" v-if="item.content">
|
||||
<text>{{ item.content }}</text>
|
||||
<!-- <text class="link-btn" @click="handleLink">立即认证✅</text>-->
|
||||
</view>
|
||||
|
||||
<!-- 底部信息栏 -->
|
||||
<view class="info-bar">
|
||||
<view class="sender-info">
|
||||
<image class="sender-icon" :src="item.imgUrl || avatarUrl" mode="aspectFill"></image>
|
||||
<text class="sender-name">{{ item.createBy || '小策官方团队'}}</text>
|
||||
</view>
|
||||
<text class="send-time">{{ formatTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 底部提示 -->
|
||||
<uni-divider-text margin="10rpx 0"
|
||||
width="50%"
|
||||
font-size="24rpx"
|
||||
v-if="messageList.length>0"
|
||||
lineText="只展示最近7天的消息"/>
|
||||
<!-- <uni-load-more v-if="messageList.length>4" iconType="auto" :status="status" :content-text="contentText" />-->
|
||||
</scroll-view>
|
||||
<view class="empty__item tn-margin-top empty__view" v-else>
|
||||
<tn-empty icon="https://resource.tuniaokj.com/images/empty/alien/2.png"
|
||||
text="近7天暂无消息" :imgWidth="200"
|
||||
:imgHeight="200"></tn-empty>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {formatTime} from "@/utils/agri";
|
||||
import {listMessage} from "@/api/warn/message";
|
||||
import UniDividerText from "@/components/uni-divider/uni-divider-text.vue";
|
||||
export default {
|
||||
components: {UniDividerText},
|
||||
data() {
|
||||
return {
|
||||
avatarUrl: "https://img.xiaoces.com/photos/logo.png",
|
||||
messageList: [],
|
||||
contentText: {
|
||||
contentdown: '查看更多',
|
||||
contentrefresh: '加载中.......',
|
||||
contentnomore: '没有更多数据了'
|
||||
},
|
||||
status: 'more',
|
||||
scrollTop: null
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 只允许查询七天以内消息默认展示今天
|
||||
this.getEventList();
|
||||
},
|
||||
methods: {
|
||||
formatTime,
|
||||
handleLink() {
|
||||
// 点击"立即认证"的跳转逻辑
|
||||
uni.showToast({ title: "跳转到认证页" })
|
||||
},
|
||||
gotoDetail(item) {
|
||||
uni.showToast({ title: "跳转到详情页" })
|
||||
// todo 此处更新已读
|
||||
// 点击消息项跳转详情页
|
||||
uni.navigateTo({
|
||||
url: "/pages/news/subpages/eventDetail/index?title=" + item.title
|
||||
})
|
||||
},
|
||||
getEventList() {
|
||||
this.status = 'loading'
|
||||
// if (!this.isHaveOldData) {
|
||||
// setTimeout(() => {
|
||||
// this.$modal.msg("没有更早数据!");
|
||||
// // 结束刷新状态
|
||||
// this.status = 'nomore'
|
||||
// }, 800);
|
||||
// return;
|
||||
// }
|
||||
var query = {
|
||||
msgType: "event"
|
||||
}
|
||||
listMessage(query).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.messageList = response.rows.sort((a, b) => new Date(a.createTime) - new Date(b.createTime));
|
||||
if (this.messageList.length === 0) {
|
||||
this.$modal.showToast('没有更多数据~')
|
||||
}
|
||||
}
|
||||
}).finally(()=>{
|
||||
// 在数据更新后设置 scrollTop
|
||||
this.$nextTick(() => {
|
||||
// 更新数据的长度
|
||||
this.scrollTop = 9999;
|
||||
});
|
||||
// 结束刷新状态
|
||||
this.status = 'nomore'
|
||||
this.$refs.eventPage.complete();
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
page {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
/* 页面容器 */
|
||||
.message-page {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* 消息列表 */
|
||||
.message-list {
|
||||
height: 100vh;
|
||||
padding: 40rpx 20rpx;
|
||||
}
|
||||
.message-item {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 25rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 图片区域 */
|
||||
.img-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 320rpx;
|
||||
}
|
||||
.bg-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
/* 新增:底部渐变遮罩 */
|
||||
.gradient-mask {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 120rpx; /* 遮罩高度,和标题区域对齐 */
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.6), transparent);
|
||||
pointer-events: none; /* 不影响点击 */
|
||||
}
|
||||
.unread-tag {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
color: #fff;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 6rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.img-title {
|
||||
position: absolute;
|
||||
bottom: 30rpx;
|
||||
left: 30rpx;
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
font-weight: 500;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.5);
|
||||
z-index: 2; /* 确保标题在遮罩上方 */
|
||||
}
|
||||
|
||||
/* 底部信息栏 */
|
||||
.info-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
}
|
||||
.sender-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.sender-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.sender-name {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
.send-time {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 描述文本(仅第三条) */
|
||||
.desc-text {
|
||||
padding: 30rpx 24rpx 0 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.link-btn {
|
||||
color: #007bff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,348 +0,0 @@
|
|||
<template>
|
||||
<view class="notice-page">
|
||||
<z-paging ref="noticePage" refresher-only
|
||||
:show-empty="false" :show-footer="false" @onRefresh="getNoticeList">
|
||||
<template #refresher="{refresherStatus}">
|
||||
<!-- 此处的custom-refresh为demo中自定义的组件,非z-paging的内置组件,请在实际项目中自行创建。这里插入什么view,下拉刷新就显示什么view -->
|
||||
<custom-refresher :status="refresherStatus" />
|
||||
</template>
|
||||
<!-- 未读/已读标签栏 -->
|
||||
<view class="tab-bar">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === 0 }"
|
||||
@click="switchTab(0)"
|
||||
>
|
||||
未读
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === 1 }"
|
||||
@click="switchTab(1)"
|
||||
>
|
||||
已读
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 公告列表 -->
|
||||
<scroll-view :scroll-y="true" class="notice-list" v-if="noticeList.length>0">
|
||||
<view
|
||||
v-for="(item, index) in noticeList"
|
||||
:key="index"
|
||||
class="notice-item"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<!-- 状态点 + 标题 -->
|
||||
<view class="notice-header">
|
||||
<view class="status-dot" :style="{ borderColor: item.dotColor }">
|
||||
<view class="dot-inner" :style="{ backgroundColor: item.dotColor }" v-if="Math.random() < 0.5"></view>
|
||||
</view>
|
||||
<text class="notice-title">{{ item.title }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容描述 -->
|
||||
<view class="notice-content">
|
||||
<text class="content-text">{{ item.content }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部:头像 + 作者 + 时间 -->
|
||||
<view class="notice-footer">
|
||||
<view class="author-info">
|
||||
<image class="avatar" :src=" item.imgUrl || authorList[Math.round(Math.random())].imgUrl" mode="aspectFill"></image>
|
||||
<text class="author-name">{{ item.createBy || authorList[Math.round(Math.random())].createBy }}</text>
|
||||
</view>
|
||||
<text class="notice-time">{{ formatTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <uni-load-more v-if="noticeList.length>8" iconType="auto" :status="status" :content-text="contentText" />-->
|
||||
|
||||
<uni-divider-text margin="10rpx 0"
|
||||
width="50%"
|
||||
font-size="24rpx"
|
||||
v-if="noticeList.length>0"
|
||||
lineText="只展示最近7天的消息"/>
|
||||
</scroll-view>
|
||||
|
||||
|
||||
|
||||
<view class="empty__item tn-margin-top empty__view" v-else>
|
||||
<tn-empty icon="https://resource.tuniaokj.com/images/empty/alien/2.png"
|
||||
text="近7天暂无公告" :imgWidth="200"
|
||||
:imgHeight="200"></tn-empty>
|
||||
</view>
|
||||
|
||||
<uni-popup ref="showNotice" mode="center" >
|
||||
<!-- 新增:修改运行时间的弹窗 -->
|
||||
<view class="modal-container">
|
||||
<view class="modal-title">{{ notice.title }}</view>
|
||||
<view class="modal-content"> {{ notice.content }}</view>
|
||||
<view class="modal-remark">
|
||||
<view class="remark">
|
||||
发布人:{{ notice.createBy }}
|
||||
</view>
|
||||
<view class="remark">
|
||||
发布时间:{{ notice.createTime }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-btn-wrap">
|
||||
<button class="modal-btn confirm" @click="closeShowNotice">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {formatTime} from "@/utils/agri";
|
||||
import UniDividerText from "@/components/uni-divider/uni-divider-text.vue";
|
||||
import {listMessage, updateRead} from "@/api/warn/message";
|
||||
|
||||
export default {
|
||||
components: {UniDividerText},
|
||||
data() {
|
||||
return {
|
||||
currentTab: 0, // 0未读 1已读
|
||||
authorList: [
|
||||
{
|
||||
createBy: '那只猪-图鸟人事部',
|
||||
imgUrl: 'https://img.xiaoces.com/photos/profile.jpg'
|
||||
},
|
||||
{
|
||||
createBy: '你的名字',
|
||||
imgUrl: 'https://img.xiaoces.com/photos/logo200.png'
|
||||
}
|
||||
],
|
||||
dotColorList: [
|
||||
'#e53935',
|
||||
'#007bff',
|
||||
'#4caf50',
|
||||
'#ff9800'
|
||||
],
|
||||
contentList: [
|
||||
{
|
||||
title: '2024年春节放假通知',
|
||||
content: '各位同事:一年一度的春节又到啦,根据国家法定节假日规定,2024年春节放假安排如下:02月01日—...',
|
||||
createBy: '那只猪-图鸟人事部',
|
||||
createTime: '2024-01-26 10:12',
|
||||
dotColor: '#e53935',
|
||||
dotInner: true, // 实心红点
|
||||
readStatus: 0,
|
||||
imgUrl: 'https://img.xiaoces.com/photos/logo.png' // 替换为你的头像路径
|
||||
},
|
||||
{
|
||||
title: '2024年春节放假通知',
|
||||
content: '各位同事:一年一度的春节又到啦,根据国家法定节假日规定,2024年春节放假安排如下:02月01retrt345日—...',
|
||||
createBy: '那只猪-图鸟人事部',
|
||||
createTime: '2024-01-26 10:12',
|
||||
dotColor: '#e53935',
|
||||
dotInner: true, // 实心红点
|
||||
readStatus: 0,
|
||||
imgUrl: 'https://img.xiaoces.com/photos/logo.png' // 替换为你的头像路径
|
||||
}
|
||||
],
|
||||
noticeList: [],
|
||||
notice: {
|
||||
title:null,
|
||||
content:null,
|
||||
createBy:null,
|
||||
createTime:null
|
||||
},
|
||||
status: 'more',
|
||||
contentText: {
|
||||
contentdown: '查看更多',
|
||||
contentrefresh: '加载中.....',
|
||||
contentnomore: '没有更多数据了'
|
||||
},
|
||||
isHaveOldData: true
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 系统通知按照最新时间升序排序
|
||||
// 只允许查询七天以内消息默认展示今天
|
||||
this.getNoticeList();
|
||||
},
|
||||
methods: {
|
||||
formatTime,
|
||||
switchTab(currentTab) {
|
||||
this.currentTab = currentTab;
|
||||
this.noticeList = this.contentList.filter(item =>
|
||||
item.readStatus===currentTab);
|
||||
},
|
||||
handleClick(item) {
|
||||
this.notice = item;
|
||||
if (item.readStatus===0) {
|
||||
updateRead({msgType: "notice",id:item.id}).then(response => {
|
||||
if (response.code === 200) {
|
||||
console.info("更新已读成功")
|
||||
this.getNoticeList();
|
||||
}
|
||||
})
|
||||
}
|
||||
this.$refs.showNotice.open();
|
||||
},
|
||||
closeShowNotice() {
|
||||
this.$refs.showNotice.close();
|
||||
},
|
||||
getNoticeList() {
|
||||
this.status = 'loading'
|
||||
// if (!this.isHaveOldData) {
|
||||
// setTimeout(() => {
|
||||
// this.$modal.msg("没有更早数据!");
|
||||
// // 结束刷新状态
|
||||
// this.status = 'nomore'
|
||||
// }, 800);
|
||||
// return;
|
||||
// }
|
||||
listMessage({msgType: "notice"}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.contentList = response.rows;
|
||||
if (this.contentList.length === 0) {
|
||||
this.$modal.showToast('没有更多数据~')
|
||||
}
|
||||
}
|
||||
}).finally(()=>{
|
||||
this.noticeList = this.contentList.filter(item =>
|
||||
item.readStatus===this.currentTab);
|
||||
this.$refs.noticePage.complete();
|
||||
// 结束刷新状态
|
||||
this.status = 'nomore';
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
page {
|
||||
box-sizing: border-box;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
/* 页面容器 */
|
||||
.notice-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 标签栏 */
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
height: 80rpx;
|
||||
padding: 0 20rpx;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
position: relative;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #007bff;
|
||||
font-weight: 500;
|
||||
}
|
||||
.tab-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background-color: #007bff;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
/* 公告列表 */
|
||||
.notice-list {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
background-color: transparent;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
/* 新增:H5端避免滚动条样式不一致 */
|
||||
scrollbar-width: thin;
|
||||
height: 100vh;
|
||||
}
|
||||
.notice-item {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
/* 公告头部:状态点 + 标题 */
|
||||
.notice-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.status-dot {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border: 2rpx solid;
|
||||
border-radius: 50%;
|
||||
margin-right: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.dot-inner {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.notice-title {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 公告内容 */
|
||||
.notice-content {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.content-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
/* 超出两行隐藏(可按需调整) */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 公告底部:头像 + 作者 + 时间 */
|
||||
.notice-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.author-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.avatar {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
.author-name {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
.notice-time {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,363 +0,0 @@
|
|||
<template>
|
||||
<view class="video-page">
|
||||
<!-- 日期选择栏 -->
|
||||
<view class="date-tabs">
|
||||
<view
|
||||
v-for="(item, index) in dateList"
|
||||
:key="index"
|
||||
class="date-tab"
|
||||
:class="{ active: item.active }"
|
||||
@click="handleDateClick(item)"
|
||||
>
|
||||
<text class="week">{{ item.week }}</text>
|
||||
<text class="day">{{ item.day }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日期标题 + 分类筛选(靠右) -->
|
||||
<view class="filter-bar">
|
||||
<uni-dateformat :date="date" format="MM-dd" />
|
||||
<view class="category-tabs">
|
||||
<view
|
||||
v-for="(item, index) in categoryList"
|
||||
:key="index"
|
||||
class="category-item"
|
||||
:class="{ active: activeCategory === item.type,
|
||||
'filter-btn': item.type === 'filter' }"
|
||||
@click="handleCategoryClick(item.type)"
|
||||
>
|
||||
<image class="icon" :src="item.imgUrl" mode="aspectFill"/>
|
||||
<text>{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 视频列表(带时间轴) -->
|
||||
<scroll-view scroll-y class="video-list" v-if="videoList.length>0">
|
||||
<view
|
||||
v-for="(item, index) in videoList"
|
||||
:key="index"
|
||||
class="video-item"
|
||||
>
|
||||
<!-- 时间轴(uni-icons实现) -->
|
||||
<view class="time-axis">
|
||||
<uni-icons type="circle" size="14" color="#1677ff"></uni-icons>
|
||||
<view class="axis-line" v-if="index !== videoList.length"></view>
|
||||
</view>
|
||||
|
||||
<!-- 时间 + 卡片 垂直排列 -->
|
||||
<view class="time-card-wrap">
|
||||
<!-- 时间 + 未读红点(在卡片正上方) -->
|
||||
<view class="time-info">
|
||||
<text class="time-text">{{ item.time }}</text>
|
||||
<view class="unread-dot" v-if="item.unread"></view>
|
||||
</view>
|
||||
|
||||
<!-- 视频卡片 -->
|
||||
<view class="video-card" @click="gotoDetail(item)">
|
||||
<view class="video-info">
|
||||
<text class="device-name">{{ item.device }}</text>
|
||||
<text class="event-type">{{ item.event }}</text>
|
||||
</view>
|
||||
<image class="video-cover" :src="item.cover" mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="empty__item tn-margin-top empty__view" v-else>
|
||||
<tn-empty icon="https://resource.tuniaokj.com/images/empty/alien/2.png"
|
||||
text="暂无视频" :imgWidth="200"
|
||||
:imgHeight="200"></tn-empty>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {parseTime} from "@/utils/agri";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dateList: this.getDateList(),
|
||||
activeCategory: 'person',
|
||||
categoryList: [
|
||||
{type: 'person', name: '人', imgUrl: "https://picsum.photos/id/1015/400/200"},
|
||||
{type: 'car', name: '车', imgUrl: "https://picsum.photos/id/1015/400/200"},
|
||||
{type: 'animal', name: '动物', imgUrl: "https://picsum.photos/id/1015/400/200"},
|
||||
{type: 'filter', name: '筛选', imgUrl: "https://picsum.photos/id/1015/400/200"}
|
||||
],
|
||||
videoList: [
|
||||
{
|
||||
time: '13:22:04',
|
||||
device: 'DS-2DE4423DW-D/GLT/...',
|
||||
event: '画面变化',
|
||||
cover: 'https://picsum.photos/id/1015/400/200',
|
||||
unread: true
|
||||
},
|
||||
{
|
||||
time: '13:20:20',
|
||||
device: 'DS-2DE4423DW-D/G我饿全额钱31313122LT/...',
|
||||
event: '画面变化',
|
||||
cover: 'https://picsum.photos/id/1015/400/200',
|
||||
unread: true
|
||||
},
|
||||
{
|
||||
time: '13:19:12',
|
||||
device: 'DS-2DE4423DW-D/GLT/...',
|
||||
event: '画面变化',
|
||||
cover: 'https://picsum.photos/id/1015/400/200',
|
||||
unread: true
|
||||
},
|
||||
{
|
||||
time: '13:18:11',
|
||||
device: 'DS-2DE4423DW-D/GLT/...',
|
||||
event: '画面变化',
|
||||
cover: 'https://picsum.photos/id/1015/400/200',
|
||||
unread: true
|
||||
}
|
||||
],
|
||||
date: Date.now()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 只允许查询七天以内消息默认展示今天
|
||||
// this.videoList = this.getDateList1();
|
||||
},
|
||||
methods: {
|
||||
parseTime,
|
||||
handleDateClick(item) {
|
||||
this.dateList = this.dateList.map(d => ({...d, active: d === item}))
|
||||
uni.showToast({title: `切换到${item.day} ${item.week}`})
|
||||
|
||||
var number = new Date();
|
||||
if (item.day !== '今') {
|
||||
number.setDate(item.day)
|
||||
}
|
||||
this.date = +number;
|
||||
// todo 切换日期后,更新视频列表
|
||||
// this.videoList = this.getDateList1();
|
||||
},
|
||||
handleCategoryClick(type) {
|
||||
if (type === 'filter') {
|
||||
this.handleFilterClick();
|
||||
return;
|
||||
}
|
||||
// todo 切换分类后,更新视频列表
|
||||
this.activeCategory = type;
|
||||
uni.showToast({title: `切换到${type === 'person' ? '人' : type === 'car' ? '车' : '动物'}分类`})
|
||||
},
|
||||
handleFilterClick() {
|
||||
// todo 打开筛选面板
|
||||
uni.showToast({title: '打开筛选面板'})
|
||||
},
|
||||
getDateList() {
|
||||
const weekMap = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
const today = new Date();
|
||||
const list = [];
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date(today);
|
||||
date.setDate(today.getDate() - i);
|
||||
|
||||
list.push({
|
||||
week: weekMap[date.getDay()],
|
||||
day: i === 0 ? '今' : date.getDate() + '',
|
||||
active: i === 0
|
||||
});
|
||||
}
|
||||
return list;
|
||||
},
|
||||
|
||||
getDateList1() {
|
||||
const w = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
const t = new Date();
|
||||
return Array.from({length: 7}, (_, i) => {
|
||||
const d = new Date(t);
|
||||
d.setDate(t.getDate() - 6 + i);
|
||||
return {week: w[d.getDay()], day: i === 6 ? '今' : d.getDate() + '', active: i === 6}
|
||||
})
|
||||
},
|
||||
gotoDetail(item) {
|
||||
// 更新已读
|
||||
this.$modal.msg("暂未开放!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面容器 */
|
||||
.video-page {
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 日期选择栏 */
|
||||
.date-tabs {
|
||||
display: flex;
|
||||
padding: 30rpx 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.date-tab {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #666;
|
||||
background: #f7f8fd;
|
||||
margin: 0 10rpx;
|
||||
//border: 1rpx solid #e0e0e0;
|
||||
border-radius: 12rpx;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
.date-tab.active {
|
||||
background-color: #1677ff;
|
||||
color: #fff;
|
||||
border-color: #1677ff;
|
||||
}
|
||||
.week {
|
||||
font-size: 25rpx;
|
||||
margin-bottom: 8rpx;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
.day {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 筛选栏 */
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
.category-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.category-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 20rpx;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 20rpx;
|
||||
margin-left: 15rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
.category-item.active {
|
||||
background-color: #fff7f0;
|
||||
border-color: #ff7d00;
|
||||
color: #ff7d00;
|
||||
}
|
||||
.category-item:nth-child(2).active {
|
||||
background-color: #f0f8ff;
|
||||
border-color: #00a8ff;
|
||||
color: #00a8ff;
|
||||
}
|
||||
.category-item:nth-child(3).active {
|
||||
background-color: #f0f5ff;
|
||||
border-color: #4080ff;
|
||||
color: #4080ff;
|
||||
}
|
||||
.filter-btn {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
.icon {
|
||||
margin-right: 6rpx;
|
||||
width: 16rpx;
|
||||
height: 22rpx;
|
||||
}
|
||||
|
||||
/* 视频列表 */
|
||||
.video-list {
|
||||
flex: 1;
|
||||
padding: 0 30rpx;
|
||||
height: calc(100vh - var(--status-bar-height) - 180rpx);
|
||||
}
|
||||
.video-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
/* 时间轴 */
|
||||
.time-axis {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-right: 14rpx;
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
.axis-line {
|
||||
width: 1rpx;
|
||||
height: 170rpx; /* 适配时间+卡片高度 */
|
||||
background-color: #e0e0e0;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
/* 时间+卡片 垂直包裹 */
|
||||
.time-card-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 时间信息(在卡片正上方) */
|
||||
.time-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15rpx;
|
||||
}
|
||||
.time-text {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.unread-dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background-color: #ff3b30;
|
||||
border-radius: 50%;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
/* 视频卡片 */
|
||||
.video-card {
|
||||
display: flex;
|
||||
background-color: #f7f8fd;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
.video-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 0;
|
||||
}
|
||||
.device-name {
|
||||
font-size: 28rpx;
|
||||
color: #8f8f8f;
|
||||
margin-bottom: 10rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
.event-type {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
.video-cover {
|
||||
width: 320rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -10,13 +10,6 @@
|
|||
<view class="iconfont icon-user icon"></view>
|
||||
<input v-model="registerForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
|
||||
</view>
|
||||
|
||||
<view class="input-item flex align-center">
|
||||
<uni-easyinput prefixIcon="phone" :clearable="false" v-model="registerForm.phonenumber" minlength="11" maxlength="11" placeholder="请输入手机号">
|
||||
</uni-easyinput>
|
||||
<!-- <view class=" phone"></view>-->
|
||||
<!-- <input v-model="registerForm.phonenumber" class="input" type="text" placeholder="" />-->
|
||||
</view>
|
||||
<view class="input-item flex align-center">
|
||||
<view class="iconfont icon-password icon"></view>
|
||||
<input v-model="registerForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
|
||||
|
|
@ -82,10 +75,6 @@
|
|||
async handleRegister() {
|
||||
if (this.registerForm.username === "") {
|
||||
this.$modal.msgError("请输入您的账号")
|
||||
} else if (this.registerForm.phonenumber === "") {
|
||||
this.$modal.msgError("请输入您的手机号")
|
||||
}else if (this.registerForm.phonenumber.length !== 11) {
|
||||
this.$modal.msgError("手机号长度必须为11位")
|
||||
} else if (this.registerForm.password === "") {
|
||||
this.$modal.msgError("请输入您的密码")
|
||||
} else if (this.registerForm.confirmPassword === "") {
|
||||
|
|
@ -197,16 +186,4 @@
|
|||
}
|
||||
}
|
||||
|
||||
/deep/ .is-input-border {
|
||||
border: 0 !important;
|
||||
border-radius: 20px;
|
||||
background: transparent !important;
|
||||
}
|
||||
/deep/ .uniui-phone {
|
||||
margin-left: 8px!important;
|
||||
}
|
||||
/deep/ .uni-input-placeholder {
|
||||
font-size: 14px!important;
|
||||
color: grey!important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -20,19 +20,6 @@ export default {
|
|||
icon: 'success'
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 自定义提示窗
|
||||
* @param content
|
||||
* @param tag
|
||||
*/
|
||||
customMsg(content,tag) {
|
||||
uni.showToast({
|
||||
title: content,
|
||||
icon: tag,
|
||||
duration: 3000
|
||||
})
|
||||
},
|
||||
|
||||
// 隐藏消息
|
||||
hideMsg(content) {
|
||||
uni.hideToast()
|
||||
|
|
|
|||
|
|
@ -1,193 +0,0 @@
|
|||
/* 顶部 start */
|
||||
.header {
|
||||
padding: 80rpx 60rpx 40rpx 60rpx;
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: $tn-font-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 28rpx;
|
||||
color: $tn-content-color;
|
||||
padding-top: 18rpx;
|
||||
}
|
||||
|
||||
.tips-title {
|
||||
font-size: 24rpx;
|
||||
color: $tn-font-sub-color;
|
||||
padding-top: 5rpx;
|
||||
}
|
||||
}
|
||||
/* 顶部 end */
|
||||
|
||||
/* 展示内容容器 start */
|
||||
.show-content-container {
|
||||
|
||||
/* 标题容器 start */
|
||||
.title-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 100rpx;
|
||||
|
||||
// 标题样式
|
||||
.title {
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0 20rpx;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
// 标题前面小点
|
||||
&:before {
|
||||
content: " ";
|
||||
background-color: $tn-main-color;
|
||||
width: 15rpx;
|
||||
height: 15rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 18rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 标题容器 end */
|
||||
|
||||
/* 内容 start */
|
||||
.content {
|
||||
padding: 20rpx;
|
||||
}
|
||||
/* 内容 end */
|
||||
|
||||
}
|
||||
/* 展示内容容器 end */
|
||||
|
||||
/* 内容容器 start */
|
||||
.demo-content-container {
|
||||
border: 1rpx dashed $tn-main-color;
|
||||
margin: 20rpx;
|
||||
margin-top: 0rpx;
|
||||
|
||||
position: fixed;
|
||||
width: 95%;
|
||||
z-index: 10;
|
||||
|
||||
transition: all 0.15s ease-out;
|
||||
|
||||
&.top {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.no-fixed {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* 标题容器 start */
|
||||
.title-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100rpx;
|
||||
|
||||
// 标题样式
|
||||
.title {
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0 30rpx;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
/* 标题容器 end */
|
||||
|
||||
/* 内容 start */
|
||||
.content {
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* 内容 end */
|
||||
|
||||
}
|
||||
/* 内容容器 end */
|
||||
|
||||
/* 可选项内容容器 start */
|
||||
.demo-section-container {
|
||||
margin: 20rpx;
|
||||
height: 100%;
|
||||
|
||||
/* 标题容器 start */
|
||||
.title-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100rpx;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
// 标题样式
|
||||
.title {
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0 30rpx;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: " ";
|
||||
box-sizing: border-box;
|
||||
width: 90%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-bottom: 1rpx solid $tn-border-solid-color;
|
||||
}
|
||||
}
|
||||
/* 标题容器 end */
|
||||
|
||||
/* 参数内容 start*/
|
||||
.content {
|
||||
padding: 0 20rpx 10rpx 20rpx;
|
||||
|
||||
// 标题样式
|
||||
.title {
|
||||
padding-left: 20rpx;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 4rpx;
|
||||
height: 90%;
|
||||
background-color: $tn-main-color;
|
||||
border-radius: 6rpx;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
// 参数样式
|
||||
.section {
|
||||
margin-top: 15rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
/* 参数内容 end*/
|
||||
|
||||
}
|
||||
/* 可选项内容容器 end */
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
.tn-custom-nav-bar__back {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
border-radius: 1000rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.5);
|
||||
color: #FFFFFF;
|
||||
font-size: 18px;
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
flex: 1;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
width: 1rpx;
|
||||
height: 110%;
|
||||
position: absolute;
|
||||
top: 22.5%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
transform: scale(0.5);
|
||||
transform-origin: 0 0;
|
||||
pointer-events: none;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.7;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 619 B |
|
Before Width: | Height: | Size: 619 B |
|
Before Width: | Height: | Size: 781 B After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 781 B After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.9 KiB |
|
|
@ -5,7 +5,7 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="https://img.xiaoces.com/photos/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="<%= BASE_URL %>static/favicon.ico">
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
|
|
@ -17,5 +17,4 @@
|
|||
<strong>本站点必须要开启JavaScript才能运行.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
|
@ -0,0 +1,310 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
|
||||
<title>用户服务条款</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
background-color: #f6f8f9;
|
||||
}
|
||||
|
||||
.main {
|
||||
background-color: #ffffff;
|
||||
width: 90%;
|
||||
max-width: 1600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 24px 40px;
|
||||
padding-bottom: 400px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 90px 75px;
|
||||
}
|
||||
|
||||
p {
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-margin-after: 0;
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
}
|
||||
li{
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
text-align: left;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.content-list-item {
|
||||
text-indent: 2em;
|
||||
}
|
||||
|
||||
.orderly-list {
|
||||
/* margin-left: 2em; */
|
||||
}
|
||||
|
||||
.orderly-list-item {
|
||||
margin-left: 2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.orderly-list-item .index {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.orderly-list-item-desc {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 1.2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: right;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.list-item-inner {
|
||||
position: relative;
|
||||
margin-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.index-inner {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.list-item-desc-inner {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 1em;
|
||||
}
|
||||
|
||||
.content .desc {
|
||||
text-indent: 2em;
|
||||
}
|
||||
|
||||
.informal {
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 1400px;
|
||||
margin: 0 auto;
|
||||
white-space: normal;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-left: 1px dashed #778899;
|
||||
border-top: 1px dashed #778899;
|
||||
position: relative;
|
||||
white-space: normal;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.table-column {
|
||||
text-align: left;
|
||||
font-size: 17px;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 15px 15px;
|
||||
border-right: 1px dashed #778899;
|
||||
border-bottom: 1px dashed #778899;
|
||||
position: relative;
|
||||
width: 670px;
|
||||
background: 0 0;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.table-row.left .table-column {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.table-row.right {
|
||||
left: 700px;
|
||||
}
|
||||
|
||||
.gaizhang {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.qianming {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.inner-span {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.msg{
|
||||
background-color: #fcf8e3;
|
||||
border-color: #faebcc;
|
||||
color: #8a6d3b;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@media screen and (max-width: 760px) {
|
||||
.container{
|
||||
padding: 0;
|
||||
}
|
||||
.main{
|
||||
padding: 24px;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.orderly-list-item{
|
||||
margin-left: 1.2em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="container">
|
||||
<h1 class="title">用户服务条款</h1>
|
||||
<div class="content">
|
||||
<h2 class="content-title">服务条款确认与接纳</h2>
|
||||
<p class="desc">
|
||||
北京小策技术有限公司(以下简称小策技术) 拥有智能农业小程序其涉及到的产品、相关软件的所有权和运作权, 小策技术
|
||||
享有对其产品的上一切活动的监督、提示、检查、纠正及处罚等权利。用户通过注册程序阅读本服务条款并点击"注册"按钮完成注册,即表示用户与 小策技术
|
||||
已达成协议,自愿接受本服务条款的所有内容。如果用户不同意服务条款的条件,则不能获得使用 小策技术 服务以及注册成为 小策技术 用户的权利。
|
||||
</p>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 class="content-title">使用规则</h2>
|
||||
<div class="orderly-list">
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">1.</span>
|
||||
<p class="orderly-list-item-desc">用户注册成功后,小策技术
|
||||
将给予每个用户一个用户账号及相应的密码,该用户账号和密码由用户负责保管;用户应当对以其用户账号进行的所有活动和事件负法律责任。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">2.</span>
|
||||
<p class="orderly-list-item-desc">用户须对在 小策技术
|
||||
的注册信息的真实性、合法性、有效性承担全部责任,用户不得冒充他人;不得利用他人的名义发布任何信息;不得恶意使用注册帐户导致其他用户误认;否则 小策技术
|
||||
有权立即停止提供服务,收回其账号并由用户独自承担由此而产生的一切法律责任。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">3.</span>
|
||||
<p class="orderly-list-item-desc">用户不得使用 小策技术 服务发送或传播敏感信息和违反国家法律制度的信息,包括但不限于下列信息:</p>
|
||||
<div>
|
||||
<ul type="circle">
|
||||
<li>反对宪法所确定的基本原则的;</li>
|
||||
<li>危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;</li>
|
||||
<li>损害国家荣誉和利益的;</li>
|
||||
<li>煽动民族仇恨、民族歧视,破坏民族团结的;</li>
|
||||
<li>破坏国家宗教政策,宣扬邪教和封建迷信的;</li>
|
||||
<li>散布谣言,扰乱社会秩序,破坏社会稳定的;</li>
|
||||
<li>散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;</li>
|
||||
<li>侮辱或者诽谤他人,侵害他人合法权益的;</li>
|
||||
<li>含有法律、行政法规禁止的其他内容的。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">4.</span>
|
||||
<p class="orderly-list-item-desc">小策技术 有权对用户使用 小策技术 产品的情况进行审查和监督,如用户在使用 小策技术
|
||||
产品时违反任何上述规定,小策技术 或其授权的人有权要求用户改正或直接采取一切必要的措施以减轻用户不当行为造成的影响。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">5.</span>
|
||||
<p class="orderly-list-item-desc">盗取他人用户账号或利用网络通讯骚扰他人,均属于非法行为。用户不得采用测试、欺骗等任何非法手段,盗取其他用户的账号和对他人进行骚扰。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 class="content-title">免责声明</h2>
|
||||
<div class="orderly-list">
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">1.</span>
|
||||
<p class="orderly-list-item-desc">小策技术 不能对用户在本社区回答问题的答案或评论的准确性及合理性进行保证。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">2.</span>
|
||||
<p class="orderly-list-item-desc">若 小策技术
|
||||
产品已经明示其网络服务提供方式发生变更并提醒用户应当注意事项,用户未按要求操作所产生的一切后果由用户自行承担。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">3.</span>
|
||||
<p class="orderly-list-item-desc">用户明确同意其使用 小策技术 产品网络服务所存在的风险将完全由其自己承担;因其使用 小策技术
|
||||
服务而产生的一切后果也由其自己承担,小策技术 对用户不承担任何责任。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">4.</span>
|
||||
<p class="orderly-list-item-desc">小策技术
|
||||
不保证网络服务一定能满足用户的要求,也不保证网络服务不会中断,对网络服务的及时性、安全性、准确性也都不作保证。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">5.</span>
|
||||
<p class="orderly-list-item-desc">对于因不可抗力或 小策技术 不能控制的原因造成的网络服务中断或其它缺陷,小策技术
|
||||
不承担任何责任,但将尽力减少因此而给用户造成的损失和影响。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">5.</span>
|
||||
<p class="orderly-list-item-desc">用户同意保障和维护 小策技术 及其他用户的利益,用户在 小策技术
|
||||
发表的内容仅表明其个人的立场和观点,并不代表 小策技术 的立场或观点。由于用户发表内容违法、不真实、不正当、侵犯第三方合法权益,或用户违反本协议项下的任何条款而给
|
||||
小策技术 或任何其他第三人造成损失,用户同意承担由此造成的损害赔偿责任。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 class="content-title">服务条款的修改</h2>
|
||||
<p class="desc">
|
||||
小策技术 会在必要时修改服务条款,服务条款一旦发生变动,小策技术
|
||||
将会在用户进入下一步使用前的页面提示修改内容。如果您同意改动,请再一次激活"我同意"按钮。如果您不接受,请及时取消您的帐户。 用户要继续使用 小策技术 各项服务需要两方面的确认:
|
||||
</p>
|
||||
<div class="orderly-list">
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">1.</span>
|
||||
<p class="orderly-list-item-desc">首先确认 小策技术 服务条款及其变动。</p>
|
||||
</div>
|
||||
<div class="orderly-list-item">
|
||||
<span class="index">2.</span>
|
||||
<p class="orderly-list-item-desc">同意接受所有的服务条款限制。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 class="content-title">隐私政策</h2>
|
||||
<p class="desc">
|
||||
小策技术 非常重视对用户隐私权的保护,承诺不会在未获得用户许可的情况下擅自将用户的个人资料信息出租或出售给任何第三方,但以下情况除外:
|
||||
</p>
|
||||
<ul type="circle">
|
||||
<li>您同意让第三方共享资料;</li>
|
||||
<li>您同意公开你的个人资料,享受为您提供的产品和服务;</li>
|
||||
<li>本站需要听从法庭传票、法律命令或遵循法律程序;</li>
|
||||
<li>本站发现您违反了本站服务条款或本站其它使用规定。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="msg">如果您对此条款有任何疑问或建议,欢迎通过邮件联系我们:346039442@qq.com</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 97 KiB |
|
|
@ -5,91 +5,3 @@
|
|||
// iconfont
|
||||
@import "@/static/font/iconfont.css";
|
||||
@import "@/static/font_ve87r6kfq3/iconfont.css";
|
||||
|
||||
|
||||
|
||||
/* 新增:弹窗样式 */
|
||||
.modal-container {
|
||||
width: 600rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 20rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-input-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 35rpx;
|
||||
font-size: 26rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
line-height: 50rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.modal-remark {
|
||||
text-align: right;
|
||||
color: #999999;
|
||||
font-size: 24rpx;
|
||||
line-height: 30rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
.modal-label {
|
||||
width: 160rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.modal-current {
|
||||
margin-left: 14rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-input {
|
||||
height: 60rpx;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 15rpx;
|
||||
font-size: 26rpx;
|
||||
width: 80rpx;
|
||||
}
|
||||
|
||||
.modal-unit {
|
||||
margin-left: 10rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.modal-btn-wrap {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
flex: 1;
|
||||
height: 70rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.modal-btn.cancel {
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.modal-btn.confirm {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
.empty__view {
|
||||
margin-top: 100rpx;
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import { mapState } from 'vuex'
|
||||
import store from '@/store'
|
||||
|
||||
// 尝试将用户在根目录中的store/index.js的vuex的state变量加载到全局变量中
|
||||
let $tStoreKey = []
|
||||
try {
|
||||
$tStoreKey = store.state ? Object.keys(store.state) : []
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
beforeCreate() {
|
||||
// 将vuex方法挂在在$t中
|
||||
// 使用方法:
|
||||
// 修改vuex的state中的user.name变量为图鸟小菜 => this.$tn.vuex('user.name', '图鸟小菜')
|
||||
// 修改vuexde state中的version变量为1.0.1 => this.$tn.vuex('version', 1.0.1)
|
||||
this.$tn.vuex = (name, value) => {
|
||||
this.$store.commit('$tStore', {
|
||||
name, value
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 将vuex的state中的变量结构到全局混入mixin中
|
||||
...mapState($tStoreKey)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,77 +5,11 @@ import getters from './getters'
|
|||
|
||||
Vue.use(Vuex)
|
||||
|
||||
let lifeData = {}
|
||||
|
||||
// 尝试获取本地是否存在lifeData变量,第一次启动时不存在
|
||||
try {
|
||||
lifeData = uni.getStorageSync('lifeData')
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
|
||||
// 标记需要永久存储的变量,在每次启动时取出,在state中的变量名
|
||||
let saveStateKeys = ['vuex_user']
|
||||
|
||||
// 保存变量到本地存储
|
||||
const saveLifeData = function(key, value) {
|
||||
// 判断变量是否在存储数组中
|
||||
if (saveStateKeys.indexOf(key) != -1) {
|
||||
// 获取本地存储的lifeData对象,将变量添加到对象中
|
||||
let tmpLifeData = uni.getStorageSync('lifeData')
|
||||
// 第一次启动时不存在,则放一个空对象
|
||||
tmpLifeData = tmpLifeData ? tmpLifeData : {},
|
||||
tmpLifeData[key] = value
|
||||
// 将变量再次放回本地存储中
|
||||
uni.setStorageSync('lifeData', tmpLifeData)
|
||||
}
|
||||
}
|
||||
|
||||
const store = new Vuex.Store({
|
||||
modules: {
|
||||
user
|
||||
},
|
||||
getters,
|
||||
state: {
|
||||
// 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量
|
||||
// 加上vuex_前缀,是防止变量名冲突,也让人一目了然
|
||||
vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {name: '图鸟'},
|
||||
|
||||
// 如果vuex_version无需保存到本地永久存储,无需lifeData.vuex_version方式
|
||||
// app版本
|
||||
vuex_version: "1.0.0",
|
||||
// 是否使用自定义导航栏
|
||||
vuex_custom_nav_bar: true,
|
||||
// 状态栏高度
|
||||
vuex_status_bar_height: 0,
|
||||
// 自定义导航栏的高度
|
||||
vuex_custom_bar_height: 0
|
||||
},
|
||||
mutations: {
|
||||
$tStore(state, payload) {
|
||||
// 判断是否多层调用,state中为对象存在的情况,例如user.info.score = 1
|
||||
let nameArr = payload.name.split('.')
|
||||
let saveKey = ''
|
||||
let len = nameArr.length
|
||||
if (len >= 2) {
|
||||
let obj = state[nameArr[0]]
|
||||
for (let i= 1; i < len - 1; i++) {
|
||||
obj = obj[nameArr[i]]
|
||||
}
|
||||
obj[nameArr[len - 1]] = payload.value
|
||||
saveKey = nameArr[0]
|
||||
} else {
|
||||
// 单层级变量
|
||||
state[payload.name] = payload.value
|
||||
saveKey = payload.name
|
||||
}
|
||||
|
||||
// 保存变量到本地中
|
||||
saveLifeData(saveKey, state[saveKey])
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
}
|
||||
getters
|
||||
})
|
||||
|
||||
export default store
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
TuniaoUi for uniApp v1.0.0 | by 图鸟 2021-09-01
|
||||
仅供开发,如作它用所承受的法律责任一概与作者无关
|
||||
|
||||
*使用TuniaoUi开发扩展与插件时,请注明基于tuniao字眼
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
<template>
|
||||
<view v-if="value" class="tn-action-sheet-class tn-action-sheet">
|
||||
<tn-popup
|
||||
v-model="value"
|
||||
mode="bottom"
|
||||
length="auto"
|
||||
:popup="false"
|
||||
:borderRadius="borderRadius"
|
||||
:maskCloseable="maskCloseable"
|
||||
:safeAreaInsetBottom="safeAreaInsetBottom"
|
||||
:zIndex="elZIndex"
|
||||
@close="close"
|
||||
>
|
||||
<!-- 提示信息 -->
|
||||
<view
|
||||
v-if="tips.text"
|
||||
class="tn-action-sheet__tips border-solid-bottom"
|
||||
:style="[tipsStyle]"
|
||||
>
|
||||
{{tips.text}}
|
||||
</view>
|
||||
<!-- 按钮列表 -->
|
||||
<block v-for="(item, index) in list" :key="index">
|
||||
<view
|
||||
class="tn-action-sheet__item tn-text-ellipsis"
|
||||
:class="[ index < list.length - 1 ? 'border-solid-bottom' : '']"
|
||||
:style="[itemStyle(index)]"
|
||||
hover-class="tn-hover-class"
|
||||
:hover-stay-time="150"
|
||||
@tap="itemClick(index)"
|
||||
@touchmove.stop.prevent
|
||||
>
|
||||
<text>{{item.text}}</text>
|
||||
<text v-if="item.subText" class="tn-action-sheet__item__subtext tn-text-ellipsis">{{item.subText}}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 取消按钮 -->
|
||||
<block v-if="cancelBtn">
|
||||
<view class="tn-action-sheet__cancel--gab"></view>
|
||||
<view
|
||||
class="tn-action-sheet__cancel tn-action-sheet__item"
|
||||
hover-class="tn-hover-class"
|
||||
:hover-stay-time="150"
|
||||
@tap="close"
|
||||
>{{cancelText}}</view>
|
||||
</block>
|
||||
|
||||
</tn-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'tn-action-sheet',
|
||||
props: {
|
||||
// 通过v-model控制弹出和收起
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 按钮文字数组,可以自定义颜色和字体大小
|
||||
// return [{
|
||||
// text: '确定',
|
||||
// subText: '这是一个确定按钮',
|
||||
// color: '',
|
||||
// fontSize: '',
|
||||
// disabled: true
|
||||
// }]
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 顶部提示文字
|
||||
tips: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
text: '',
|
||||
color: '',
|
||||
fontSize: 26
|
||||
}
|
||||
}
|
||||
},
|
||||
// 弹出的顶部圆角值
|
||||
borderRadius: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 点击遮罩可以关闭
|
||||
maskCloseable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 底部取消按钮
|
||||
cancelBtn: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 底部取消按钮的文字
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
},
|
||||
// 开启底部安全区域
|
||||
// 在iPhoneX机型底部添加一定的内边距
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// z-index值
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 顶部提示样式
|
||||
tipsStyle() {
|
||||
let style = {}
|
||||
if (this.tips.color) style.color = this.tips.color
|
||||
if (this.tips.fontSize) style.fontSize = this.tips.fontSize + 'rpx'
|
||||
|
||||
return style
|
||||
},
|
||||
// 操作项目的样式
|
||||
itemStyle() {
|
||||
return (index) => {
|
||||
let style = {}
|
||||
if (this.list[index].color) style.color = this.list[index].color
|
||||
if (this.list[index].fontSize) style.fontSize = this.list[index].fontSize + 'rpx'
|
||||
|
||||
// 选项被禁用的样式
|
||||
if (this.list[index].disabled) style.color = '#AAAAAA'
|
||||
|
||||
return style
|
||||
}
|
||||
},
|
||||
elZIndex() {
|
||||
return this.zIndex ? this.zIndex : this.$tn.zIndex.popup
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 点击取消按钮
|
||||
close() {
|
||||
// 发送input事件,并不会作用于父组件,而是要设置组件内部通过props传递的value参数
|
||||
this.popupClose();
|
||||
this.$emit('close');
|
||||
},
|
||||
// 关闭弹窗
|
||||
popupClose() {
|
||||
this.$emit('input', false)
|
||||
},
|
||||
// 点击对应的item
|
||||
itemClick(index) {
|
||||
// 如果是禁用项则不进行操作
|
||||
if (this.list[index].disabled) return
|
||||
this.$emit('click', index)
|
||||
this.popupClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.border-solid-bottom{
|
||||
border-bottom: 1rpx solid #F8F7F8;
|
||||
}
|
||||
|
||||
.tn-action-sheet {
|
||||
&__tips {
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
padding: 34rpx 0;
|
||||
line-height: 1;
|
||||
color: $tn-content-color;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
padding: 34rpx 0;
|
||||
|
||||
&__subtext {
|
||||
font-size: 24rpx;
|
||||
color: $tn-content-color;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&__cancel {
|
||||
color: $tn-font-color;
|
||||
|
||||
&--gab {
|
||||
height: 12rpx;
|
||||
background-color: #F8F7F8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
<template>
|
||||
<view class="tn-avatar-group-class tn-avatar-group">
|
||||
<view v-for="(item, index) in lists" :key="index" class="tn-avatar-group__item" :style="[itemStyle(index)]">
|
||||
<tn-avatar
|
||||
:src="item.src || ''"
|
||||
:text="item.text || ''"
|
||||
:icon="item.icon || ''"
|
||||
:size="size"
|
||||
:shape="shape"
|
||||
:imgMode="imgMode"
|
||||
:border="true"
|
||||
:borderSize="4"
|
||||
></tn-avatar>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'tn-avatar-group',
|
||||
props: {
|
||||
// 头像列表
|
||||
lists: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 头像类型
|
||||
// square 带圆角正方形 circle 圆形
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'circle'
|
||||
},
|
||||
// 大小
|
||||
// sm 小头像 lg 大头像 xl 加大头像
|
||||
// 如果为其他则认为是直接设置大小
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 当设置为显示头像信息时,
|
||||
// 图片的裁剪模式
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
// 头像之间的遮挡比例
|
||||
// 0.4 代表 40%
|
||||
gap: {
|
||||
type: Number,
|
||||
default: 0.4
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
itemStyle() {
|
||||
return (index) => {
|
||||
let style = {}
|
||||
if (this._checkSizeIsInline()) {
|
||||
switch(this.size) {
|
||||
case 'sm':
|
||||
style.marginLeft = index != 0 ? `${-48 * this.gap}rpx` : ''
|
||||
break
|
||||
case 'lg':
|
||||
style.marginLeft = index != 0 ? `${-96 * this.gap}rpx` : ''
|
||||
break
|
||||
case 'xl':
|
||||
style.marginLeft = index != 0 ? `${-128 * this.gap}rpx` : ''
|
||||
break
|
||||
}
|
||||
} else {
|
||||
const size = Number(this.size.replace(/(px|rpx)/g, '')) || 64
|
||||
style.marginLeft = index != 0 ? `-${size * this.gap}rpx` : ''
|
||||
}
|
||||
return style
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 检查是否使用内置的大小进行设置
|
||||
_checkSizeIsInline() {
|
||||
if (/(xs|sm|md|lg|xl|xxl)/.test(this.size)) return true
|
||||
else return false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tn-avatar-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
&__item {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,306 +0,0 @@
|
|||
<template>
|
||||
<view
|
||||
class="tn-avatar-class tn-avatar"
|
||||
:class="[backgroundColorClass,fontColorClass,avatarClass]"
|
||||
:style="[avatarStyle]"
|
||||
@tap="click"
|
||||
>
|
||||
<image
|
||||
v-if="showImg"
|
||||
class="tn-avatar__img"
|
||||
:class="[imgClass]"
|
||||
:src="src"
|
||||
:mode="imgMode || 'aspectFill'"
|
||||
@error="loadImageError"
|
||||
></image>
|
||||
<view v-else class="tn-avatar__text" >
|
||||
<view v-if="text">{{ text }}</view>
|
||||
<view v-else :class="[`tn-icon-${icon}`]"></view>
|
||||
</view>
|
||||
|
||||
<!-- 角标 -->
|
||||
<tn-badge
|
||||
v-if="badge && (badgeIcon || badgeText)"
|
||||
:radius="badgeSize"
|
||||
:backgroundColor="badgeBgColor"
|
||||
:fontColor="badgeColor"
|
||||
:fontSize="badgeSize - 8"
|
||||
:absolute="true"
|
||||
:top="badgePosition[0]"
|
||||
:right="badgePosition[1]"
|
||||
>
|
||||
<view v-if="badgeIcon && badgeText === ''">
|
||||
<view :class="[`tn-icon-${badgeIcon}`]"></view>
|
||||
</view>
|
||||
<view v-else>
|
||||
{{ badgeText }}
|
||||
</view>
|
||||
</tn-badge>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import componentsColorMixin from '../../libs/mixin/components_color.js'
|
||||
export default {
|
||||
mixins: [componentsColorMixin],
|
||||
name: 'tn-avatar',
|
||||
props: {
|
||||
// 序号
|
||||
index: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 头像类型
|
||||
// square 带圆角正方形 circle 圆形
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'circle'
|
||||
},
|
||||
// 大小
|
||||
// sm 小头像 lg 大头像 xl 加大头像
|
||||
// 如果为其他则认为是直接设置大小
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 是否显示阴影
|
||||
shadow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示边框
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 边框颜色
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
// 边框大小, rpx
|
||||
borderSize: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
// 头像路径
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 文字
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 图标
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 当设置为显示头像信息时,
|
||||
// 图片的裁剪模式
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
// 是否显示角标
|
||||
badge: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 设置显示角标后,角标大小
|
||||
badgeSize: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 角标背景颜色
|
||||
badgeBgColor: {
|
||||
type: String,
|
||||
default: '#AAAAAA'
|
||||
},
|
||||
// 角标字体颜色
|
||||
badgeColor: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
},
|
||||
// 角标图标
|
||||
badgeIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 角标文字,优先级比icon高
|
||||
badgeText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 角标坐标
|
||||
// [top, right]
|
||||
badgePosition: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 图片显示是否发生错误
|
||||
imgLoadError: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showImg() {
|
||||
// 如果设置了图片地址,则为显示图片,否则为显示文本
|
||||
return this.text === '' && this.icon === ''
|
||||
},
|
||||
avatarClass() {
|
||||
let clazz = ''
|
||||
clazz += ` tn-avatar--${this.shape}`
|
||||
|
||||
if (this._checkSizeIsInline()) {
|
||||
clazz += ` tn-avatar--${this.size}`
|
||||
}
|
||||
|
||||
if (this.shadow) {
|
||||
clazz += ' tn-avatar--shadow'
|
||||
}
|
||||
|
||||
return clazz
|
||||
},
|
||||
avatarStyle() {
|
||||
let style = {}
|
||||
|
||||
if (this.backgroundColorStyle) {
|
||||
style.background = this.backgroundColorStyle
|
||||
} else if (this.shadow && this.showImg) {
|
||||
style.backgroundImage = `url(${this.src})`
|
||||
}
|
||||
|
||||
if(this.fontColorStyle) {
|
||||
style.color = this.fontColorStyle
|
||||
}
|
||||
|
||||
if(this.fontSizeStyle) {
|
||||
style.fontSize = this.fontSizeStyle
|
||||
}
|
||||
|
||||
if (this.border) {
|
||||
style.border = `${this.borderSize}rpx solid ${this.borderColor}`
|
||||
}
|
||||
|
||||
if (!this._checkSizeIsInline()) {
|
||||
style.width = this.size
|
||||
style.height = this.size
|
||||
}
|
||||
|
||||
return style
|
||||
},
|
||||
imgClass() {
|
||||
let clazz = ''
|
||||
clazz += ` tn-avatar__img--${this.shape}`
|
||||
|
||||
return clazz
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载图片失败
|
||||
loadImageError() {
|
||||
this.imgLoadError = true
|
||||
},
|
||||
// 点击事件
|
||||
click() {
|
||||
this.$emit("click", this.index)
|
||||
},
|
||||
|
||||
// 检查是否使用内置的大小进行设置
|
||||
_checkSizeIsInline() {
|
||||
if (/^(xs|sm|md|lg|xl|xxl)$/.test(this.size)) return true
|
||||
else return false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.tn-avatar {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-flex;
|
||||
/* #endif */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $tn-font-holder-color;
|
||||
// color: #FFFFFF;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
z-index: 1;
|
||||
|
||||
&--sm {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
&--lg {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
&--xl {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
}
|
||||
|
||||
&--square {
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
&--circle {
|
||||
border-radius: 5000rpx;
|
||||
}
|
||||
|
||||
&--shadow {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: " ";
|
||||
display: block;
|
||||
background: inherit;
|
||||
filter: blur(10rpx);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 10rpx;
|
||||
left: 10rpx;
|
||||
z-index: -1;
|
||||
opacity: 0.4;
|
||||
transform-origin: 0 0;
|
||||
border-radius: inherit;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
&__img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&--square {
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
&--circle {
|
||||
border-radius: 5000rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||