592 lines
16 KiB
Vue
Raw Permalink Normal View History

2025-05-22 16:37:43 +08:00
<template>
<view class="scene-wrap">
<view class="top-container">
<view class="nav-bar">
<navbar color="#ffffff" :isBack="false">{{$tt('scene.sceneLink')}}</navbar>
</view>
<view class="segmented-control">
<view class="segment" :class="{ active: index === tabIndex }" v-for="(item, index) in statusGroup"
:key="index" @click="handleStatusChange(item,index)">
{{ item.name }}
</view>
<view class="active-indicator" :style="{ left: indicatorPosition }"></view>
</view>
</view>
<view class="scene-content">
<view class="nav-bar">
<view class="left-wrap">
<view v-if="!isSearch" style="margin-right: 20rpx;">
<u-icon name="search" size="25" @click="isSearch = true"></u-icon>
</view>
<view v-else style="margin-right: 20rpx; width: 100%;">
<!-- #ifndef APP-NVUE -->
<u-input :customStyle="{ padding: '17rpx 36rpx', background: '#FFFFFF' }"
v-model="queryParams.sceneName" :placeholder="$tt('scene.inputSceneName')" shape="circle"
@clear="handleClearSearch" clearable>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<u--input :customStyle="{ padding: '17rpx 36rpx', background: '#FFFFFF' }"
v-model="queryParams.sceneName" :placeholder="$tt('scene.inputSceneName')"
shape="circle" @clear="handleClearSearch" clearable>
<!-- #endif -->
<template slot="prefix">
<u-icon name="search" color="rgb(192, 196, 204)" size="26"
@click="isSearch = false"></u-icon>
</template>
<template slot="suffix">
<view style="display: flex; flex-direction: row; align-items: center;">
<span
style="width: 0px; height: 14px; border: 1px solid #000000; opacity: 0.1;"></span>
<span
style="font-size: 14px; font-weight: 400; color: #3378FE; margin-left: 24rpx;"
@click="handleSearch">{{$tt('common.search')}}</span>
</view>
</template>
<!-- #ifndef APP-NVUE -->
</u-input>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</u--input>
<!-- #endif -->
</view>
</view>
<u-icon name="plus-circle-fill" size="23" color='#486FF2' bold @tap="handleAddScene"></u-icon>
</view>
<scroll-view v-show="total !== 0" class="data-list" :scroll-y="true" @refresherrefresh="handleRefresher"
:refresher-enabled="true" :refresher-triggered="isTriggeredTop" refresher-background="#F1F3F9"
@scrolltolower="handleScrollToLower">
<view class="item-wrap" v-for="(item, index) in list" :key="index">
<view class="title">
<view class="title-text">{{item.sceneName}}</view>
<u-switch v-model="item.enable" @change="handleSwitchStatus(item)" :activeValue="1"
:inactiveValue="2" activeColor="#486FF2" inactiveColor="#dbdbdb" size="18"></u-switch>
</view>
<u-row :gutter="20">
<u-col :span="6">
<view class="cond">{{$tt('scene.condition')}}{{getCondName(item.cond)}}</view>
</u-col>
<u-col :span="6">
<view class="cond">{{$tt('scene.way')}}{{getExecuteModeName(item.executeMode)}}
</view>
</u-col>
</u-row>
<u-row :gutter="20">
<u-col :span="6">
<view class="cond">{{$tt('scene.task')}}{{item.actionCount}}</view>
</u-col>
<u-col :span="6">
<view class="cond">
<u--text lines="1" prefixIcon="/static/scene/once.png"
iconStyle="width: 14px;height: 14px;margin-right: 5px;" size="13"
:text="$tt('scene.execute')" color='#486FF2' lineHeight="16"
@click="handleRunOnce(item)"></u--text>
</view>
</u-col>
</u-row>
<view style="border: 1rpx solid #ccc;opacity: 0.2;margin: 20rpx 0;"></view>
<view style="display: flex;justify-content: space-between;">
<view class="bottom-title" @click="handleSceneDetail(item.sceneId)">{{$tt('common.viewDetail')}}
</view>
<view class="bottom-wrap">
<u--text type="info" prefixIcon="trash-fill"
iconStyle="font-size: 19px;margin-right: 5px;color:#a6a6a6" :text="$tt('common.delete')"
@click="handleDelete(item)"></u--text>
</view>
</view>
</view>
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize"
:loading-text="$tt('scene.tryingToLoad')" :loadmoreText="$tt('scene.gentlyPullUp')"
:nomoreText="$tt('scene.nothingLeft')" marginTop="20" />
</scroll-view>
<u-empty mode="data" :show="total === 0 && isTriggeredTop === false" marginTop="60"
:text="$tt('scene.emptyData')"></u-empty>
</view>
<u-loading-page :loading="loading" bg-color="#F1F3F9" loadingText="fastbee.cn"></u-loading-page>
<u-modal :show="isDelete" showCancelButton :confirmText="$tt('common.delete')" @cancel="() => isDelete = false"
:title="$tt('sceneDetail.tips')" :content="deleteContent" @confirm="handleConfirmDelete">
</u-modal>
</view>
</template>
<script>
import { listScene, runScene, updateStatus, delScene } from '@/apis/modules/scene';
import navbar from '@/components/navBar/index.vue';
export default {
beforeRouteEnter(to, from, next) {
// 在进入路由前执行一些操作,例如加载数据等
next(vm => {
// 这里可以访问组件实例 `vm`
vm.onLoad() // 执行加载操作
})
},
components: {
navbar
},
data() {
return {
loading: false,
deleteId: '',
isDelete: false,
deleteContent: '',
isSearch: true,
tabIndex: 0,
indicatorPosition: '0%',
isTriggeredTop: false,
// 状态列表
statusGroup: [{
id: null,
name: this.$tt('timing.all')
},
{
id: 1,
name: this.$tt('timing.enable')
},
{
id: 2,
name: this.$tt('timing.notEnabled')
}
],
queryParams: {
pageNum: 1,
pageSize: 4,
enable: null,
sceneName: '',
},
list: [],
total: 0,
loadmoreStatus: 'loadmore', // 刷新和加载相关
};
},
onLoad() {
this.getToken();
if (this.token != '' && this.token != null) {
this.getList();
}
},
onShow() {
// #ifdef MP-WEIXIN
// 检查用户是否登录
if (!this.isLogin()) {
uni.showModal({
title: this.$tt('common.tips'),
content: this.$tt('common.loginTips'),
success: (res) => {
if (res.confirm) {
// 用户点击确定,跳转到登录页面
uni.navigateTo({
url: '/pages/login/index'
});
} else if (res.cancel) {
uni.switchTab({
url: '/pages/tabBar/home/index'
})
console.log('用户点击取消');
}
}
});
}
// #endif
},
watch: {
tabIndex(newIndex) {
const percentage = (newIndex / (this.statusGroup.length)) * 100;
this.indicatorPosition = `${percentage}%`;
this.$emit('change', newIndex);
}
},
mounted() {
this.updateIndicatorPosition();
},
methods: {
updateIndicatorPosition() {
const percentage = (this.tabIndex / this.statusGroup.length) * 100;
this.indicatorPosition = `${percentage}%`;
},
getToken() {
// 本地缓存获取token
this.token = uni.getStorageSync('token');
// vuex存储token
uni.$u.vuex('vuex_token', this.token);
},
//删除
handleDelete(item) {
this.isDelete = true;
this.deleteId = item.sceneId;
this.deleteContent = `确定删除“${item.sceneName}”?`
},
// 确认删除
handleConfirmDelete() {
delScene(this.deleteId).then(res => {
if (res.code === 200) {
this.list = [];
this.queryParams.pageNum = 1;
this.getList();
} else {
uni.showToast({
icon: 'none',
title: res.msg
});
}
this.isDelete = false;
}).catch(e => {
console.log(e);
});
},
//判断是否登录
isLogin() {
if (this.token == '' || this.token == null) {
return false;
} else {
return true;
}
},
// 场景状态改变事件
handleStatusChange(item, index) {
this.tabIndex = index;
this.list = [];
this.queryParams.enable = item.id;
this.queryParams.pageNum = 1;
this.getList();
},
// 新增场景
handleAddScene() {
// 使用vuex会导致小程序性能问题所以存储本地
let sceneData = {
sceneId: null,
sceneName: '',
remark: '',
enable: 1, // 状态1启动2暂停
cond: 1, // 触发条件1任意条件2所有条件3不满足
silentPeriod: 0, // 静默时间
executeMode: 1, // 执行方式1串行2并行
executeDelay: 0, // 延时执行
hasAlert: 2, // 1包含告警2不包含告警
applicationName: 'fastbee', // 固定值
triggers: [],
actions: [],
};
const storage = uni.getStorageSync('sceneData');
if (storage) {
uni.removeStorageSync('sceneData');
}
uni.setStorageSync('sceneData', sceneData);
uni.removeStorageSync('callback');
uni.navigateTo({
url: '/pagesA/scene/detail'
});
},
// 场景详情
handleSceneDetail(id) {
const storage = uni.getStorageSync('sceneData');
if (storage) {
uni.removeStorageSync('sceneData');
}
const sceneData = {
sceneId: null,
sceneName: '',
remark: '',
enable: 1, // 状态1启动2暂停
cond: 1, // 触发条件1任意条件2所有条件3不满足
silentPeriod: 0, // 静默时间
executeMode: 1, // 执行方式1串行2并行
executeDelay: 0, // 延时执行
hasAlert: 2, // 1包含告警2不包含告警
applicationName: 'fastbee', // 固定值
triggers: [],
actions: [],
};
uni.setStorageSync('sceneData', sceneData);
uni.removeStorageSync('callback');
uni.navigateTo({
url: '/pagesA/scene/detail?sceneId=' + id
});
},
// 获取条件
getCondName(cond) {
if (cond === 1) {
return this.$tt('scene.anyCondition')
} else if (cond === 2) {
return this.$tt('scene.allCondition')
} else if (cond === 3) {
return this.$tt('scene.notConditions')
} else {
return this.$tt('scene.unknown')
}
},
getExecuteModeName(mode) {
if (mode === 1) {
return this.$tt('scene.serial')
} else if (mode === 2) {
return this.$tt('scene.parallel')
} else {
return this.$tt('scene.unknown')
}
},
// 获取场景列表
getList() {
// this.loading = true;
listScene(this.queryParams).then(res => {
const { code, rows, total } = res;
if (code === 200) {
if (this.queryParams.pageNum === 1) {
this.list = rows;
} else {
this.list = this.list.concat(rows);
}
this.total = total;
}
this.loading = false;
uni.stopPullDownRefresh();
})
},
// 搜索
handleSearch() {
this.list = [];
this.queryParams.pageNum = 1;
this.getList();
},
handleClearSearch() {
this.handleSearch();
},
// 下拉刷新
handleRefresher() {
this.isTriggeredTop = true;
this.datas = [];
this.total = 0;
this.queryParams.pageNum = 1;
setTimeout(() => {
this.getList();
this.isTriggeredTop = false;
}, 500)
},
// 上拉加载
handleScrollToLower() {
this.loadmoreStatus = 'loading';
this.queryParams.pageNum = this.queryParams.pageNum + 1;
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
this.loadmoreStatus = 'nomore';
} else {
this.getList();
this.loadmoreStatus = 'loadmore';
}
},
//执行一次
handleRunOnce(data) {
const params = {
sceneId: data.sceneId
};
runScene(params).then((res) => {
uni.showToast({
icon: 'none',
title: res.msg
});
});
},
// 切换状态
handleSwitchStatus(data) {
const params = {
sceneId: data.sceneId,
enable: data.enable,
}
updateStatus(params).then((res) => {
if (res.code === 200) {
uni.showToast({
icon: 'none',
title: this.$tt('scene.switchSuccessful')
});
} else {
uni.showToast({
icon: 'none',
title: this.$tt('scene.switchFail')
});
}
});
},
}
};
</script>
<style lang="scss">
page {
height: 100%;
background: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.scene-wrap {
height: 100vh;
display: flex;
flex-direction: column;
overflow-y: hidden;
.top-container {
position: relative;
background-size: cover;
background-image: url('@/static/common/bg.png');
// #ifdef MP-WEIXIN
min-height: 480rpx;
// #endif
// #ifdef H5
min-height: 480rpx;
// #endif
// #ifdef APP-PLUS
min-height: 550rpx;
// #endif
z-index: 0;
.segmented-control {
margin-left: 34rpx;
position: relative;
display: flex;
width: 55%;
height: 68rpx;
margin-top: 20rpx;
align-items: center;
border-radius: 3px;
background: rgba(255, 255, 255, 0.2);
.segment {
flex: 1;
font-weight: 400;
font-size: 24rpx;
line-height: 45rpx;
text-align: center;
font-style: normal;
text-transform: none;
color: #fff;
transition: color 0.3s;
cursor: pointer;
z-index: 1;
}
.segment.active {
color: rgb(51, 120, 254);
font-weight: bold;
}
.active-indicator {
height: 60rpx;
scale: 0.89;
position: absolute;
display: flex;
width: calc(100% / 3);
background: rgba(255, 255, 255, 1);
backdrop-filter: blur(10px);
border-radius: 3px;
z-index: 0;
}
}
}
.scene-content {
flex: 1 1 auto;
background: $uni-bg-color-grey;
z-index: 1;
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
padding: 0 30rpx;
display: flex;
flex-direction: column;
overflow-y: auto;
margin-top: -256rpx;
// #ifndef H5 || APP-PLUS || MP-ALIPAY
margin-top: -178rpx;
// #endif
z-index: 1;
overflow-y: auto;
.nav-bar {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 20rpx 0 0;
.left-wrap {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
}
}
.data-list {
margin-top: 20rpx;
flex: 1 1 auto;
overflow-y: auto;
//#ifdef H5
padding-bottom: 70rpx;
//#endif
.item-wrap {
position: relative;
box-shadow: 0 2rpx 0rpx 0 rgba(0, 0, 0, 0.1);
border-radius: 20rpx;
padding: 30rpx;
padding-bottom: 10rpx;
background-color: #fff;
margin-bottom: 26rpx;
.title {
padding: 10rpx 0;
display: flex;
flex-direction: row;
justify-content: space-between;
.title-text {
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 32rpx;
color: #000000;
line-height: 42rpx;
text-align: left;
font-style: normal;
text-transform: none;
}
}
.cond {
font-size: 12px;
color: #7e7e7e;
margin: 20rpx 0 10rpx 20rpx;
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: rgba(51, 51, 51, 0.6);
line-height: 40rpx;
text-align: left;
font-style: normal;
text-transform: none;
}
.bottom-title {
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: rgba(51, 51, 51, 0.6);
line-height: 40rpx;
text-align: left;
font-style: normal;
text-transform: none;
padding: 2rpx 0 10rpx;
}
.bottom-wrap {
display: flex;
flex-direction: row;
padding: 2rpx 0 10rpx;
}
.delIcon {
position: absolute;
top: -10rpx;
right: -10rpx;
cursor: pointer;
}
}
}
}
}
</style>