2025-05-22 16:37:43 +08:00

592 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>