Browse Source

完成多设备定时任务及回显

voice
zmt 4 years ago
parent
commit
c19e623401
  1. 88
      src/subpackage/device/js/ouxuanac.md
  2. 2
      src/subpackage/device/pages/index/lot_manage.vue
  3. 18
      src/subpackage/device/pages/index/router_manage.vue
  4. 15
      src/subpackage/device/pages/switch_manage/switch_manage.vue
  5. 11
      src/subpackage/device/pages/timing/timing_list.vue
  6. 475
      src/subpackage/device/pages/timing/timing_setting.vue

88
src/subpackage/device/js/ouxuanac.md

@ -325,7 +325,7 @@ https://test.ouxuanzhineng.cn/ouxuanac/tts/textToVoice.wav?text=试试看&voice_
{"code":0,"data":{"DeviceName":"00-de-47-e9-3a-fc","Online":1,"LoginTime":1626126211,"LastUpdateTime":1624102433,"Tags":[{"Tag":"category","Type":2,"Value":"placeholder","Name":""},{"Tag":"note","Type":2,"Value":"锐那东方渔人码头店","Name":""}],"DeviceType":5,"ConnIP":1231216393,"FirstOnlineTime":1602899627,"LastOfflineTime":1626126208,"LastOfflineTimeStr":"2021-07-13 05:43:28","CreateTime":1602899594,"EnableState":1,"ClientIP":"36.113.100.107","RequestId":"fc49e900-2b4c-403b-bf00-d929725d2276"},"message":""}
```
### 更新定时列表 sendPacket
### 获取定时列表 sendPacket
```
请求参数
{
@ -363,4 +363,90 @@ https://test.ouxuanzhineng.cn/ouxuanac/tts/textToVoice.wav?text=试试看&voice_
</dict>
```
### 小程序端定时任务设置 sendPacket(reqData)
> switchData(开关命令数据)->postData(后台定时接口数据)->reqData(组成最终小程序端中控定时数据)
1. 接口复用sendPacket [测试集合:sendPacket](http://api.ouxuan.net/project/233/interface/case/1860)
2. 需了解后台接口SetTimeSelect规则 [SetTimeSelect](http://api.ouxuan.net/project/165/interface/api/8194)
3. 需了解参数中group定义由来: [设备管理-硬件设备-连接方式](http://api.ouxuan.net/project/233/interface/api/11397)
4. 区分开关状态需要传输的value
```
照明,风扇,水阀
//都是GPIO 其他设备应该也可以用这种方式设置
{
"name": "继电器",
"value": "Gpio"
},
//value区分
风扇: 水阀: 照明: data.value.status 开:low , 关:high
```
> 在sendPacket接口中的传递定时任务参数 reqData
```
//接口地址
http://api.ouxuan.net/project/233/interface/api/10012 (原接口地址)
http://api.ouxuan.net/project/233/interface/case/1860 (设置定时任务接口地址: 后台说那边同个接口只能添加一个)
例子:
{
"device": "00-10-7a-0f-6d-7a",
"delay": 1,
"data": {
"name": "set-time-select",
"value": {
"data": "传入设置的json格式字符串,例如:{\"time_arrow_id\":\"c8c39f5c-8dd4-11eb-9aad-4e965989bbce\",\"group\":\"RPIO\",\"type\":\"DAY_OF_WEEK\",\"day_of_week\":[0,1,2,3,6,5,4],\"day_of_month\":null,\"date_slice\":[{\"start\":\"\",\"end\":\"\"}],\"times_on_day\":[\"10:13:00-10:13:10\"],\"expand_tags\":null,\"expand_value\":{\"main\":{\"name\":\"set-rpio\",\"title\":\"测试中控照明9\",\"value\":{\"id\":\"9\",\"status\":\"high\"}}},\"weights\":0,\"extension\":null}"
}
}
}
```
> 定时任务参数参考后台管理端api postData
```
http://api.ouxuan.net/project/165/interface/api/8194
参数规则:
{
"time_arrow_id": "c8c39f5c-8dd4-11eb-9aad-4e965989bbce", //不用填 留空
"group": "RPIO", //照明,风扇,空调固定值
"type": "DAY_OF_WEEK", //DAY_OF_WEEK/DAY_OF_MONTH/DATE_SLICE , 对应 周,月,单独时间.
"day_of_week": [
0,
1,
2,
3,
6,
5,
4
],
"day_of_month": null,
"date_slice": [
{
"start": "",
"end": ""
}
],
"times_on_day": [
"10:13:00-10:13:10"
],
"expand_tags": null,
"expand_value": {
"main": { //中控指令
"name": "set-rpio",
"title": "测试中控照明9",
"value": {
"id": "9",
"status": "high"
}
}
},
"weights": 0,//权重
"extension": null
}
```
> 关于switchData
```
postData.expand_value.main = switchData
switchData 为 switch_manage.vue 中的开关指令
```
###

2
src/subpackage/device/pages/index/lot_manage.vue

@ -296,7 +296,7 @@
url: deviceApi.getLotDetail,
data: {
iccid:this.mac,
is_sync:0
is_sync:1 //,1
},
isDefaultGet: false,
})

18
src/subpackage/device/pages/index/router_manage.vue

@ -227,17 +227,6 @@
util.hideLoad()
})
// this.operateReq({
// data: timingData,
// succFun: (list) => {
// that.infoArr.length = 1;
// list.forEach(async (item,i)=> {
// if(item.day_of_week)item.day_of_week = item.day_of_week.sort((a,b)=>a-b);//
// let timetxt = await that.getTimeTxt(item);
// that.infoArr.push([item.expand_value.main.title||"-",timetxt||"-","",item.time_arrow_id])
// })
// }
// })
},
//
filterRouterData(data){
@ -276,13 +265,6 @@
})
},
//,
getTimeTxt(row) {
return new Promise((rs,rj)=>{
})
},
goBack() {
uni.navigateBack({
delta: 1

15
src/subpackage/device/pages/switch_manage/switch_manage.vue

@ -202,6 +202,14 @@ export default {
// status 0 -> 1 -> ,2 -> ,3 -> , 4 ->()
operateBtn: util.debounce(function({ switchInfo, status }){
console.log("icon tap status: ",status,switchInfo)
//todo page.json ,setting
let _data = this.getOperateReqData({ switchInfo, status })
//->& 20201224 5
///
if(status == 2) return util.showNone("未对接,前端等待接口中...")
if(status == 4){
@ -209,14 +217,12 @@ export default {
if(this.pageInfo.hardware_type=='IotSim')return util.routeTo(`/subpackage/device/pages/index/lot_manage?mac=${switchInfo.hardware_standard}`, 'nT');
}
if(status == 3) return (()=>{
switchInfo.switchData = _data;//
let timing_setting = `/subpackage/device/pages/timing/timing_setting?switchInfo=${encodeURIComponent(JSON.stringify(switchInfo))}`
util.routeTo(`${timing_setting}`, 'nT');
})()
//todo page.json ,setting
let _data = this.getOperateReqData({ switchInfo, status })
//->& 20201224 5
if(switchInfo.hardware_type === 'AccessControl'&&status == 1){
this.operateReq({data: this.getOperateReqData({ switchInfo, status: 0 }), isTip: false, isLoad: false});
}
@ -398,8 +404,9 @@ export default {
//
// Low = "low", //
// High = "high", //
// status 0 -> 1 -> ,2 -> ,3 -> , 4 ->()
getRelayStatus(status){
return [ 'high', 'low' ][status] || ''
return [ 'high', 'low', '', 'high', ''][status] || ''
},
//

11
src/subpackage/device/pages/timing/timing_list.vue

@ -187,8 +187,9 @@
if (row.times_on_day && row.times_on_day.length) {
timeText += ` 当天<strong>${row.times_on_day[0].substring(0, 5)}</strong>`
}
if (!row.expand_value.main) return '历史错误数据';
rs(` ${timeText} 执行 ${row.expand_value.main.value.status === 'high' ? '<strong style="color:gray;">[关闭]</strong>' : '<strong style="color:#009874;">[开启]</strong>'} 操作`)
if (!row.expand_value.main) rs('历史错误结构数据');
let _status = row.expand_value.main.value.status||row.expand_value.main.data.value.status;//
rs(` ${timeText} 执行 ${_status === 'high' ? '<strong style="color:gray;">[关闭]</strong>' : '<strong style="color:#009874;">[开启]</strong>'} 操作`)
})
},
@ -307,11 +308,13 @@
}
.i-box:first-child {
width: 176rpx;
// width: 176rpx;
width: 300rpx;
}
.i-box:nth-child(2) {
width: 100%;
width: 100%;
height: 100%;
border: 1rpx solid #F2F2F7;
border-bottom: none;
border-top: none;

475
src/subpackage/device/pages/timing/timing_setting.vue

@ -1,6 +1,6 @@
<template>
<view class="timing-page">
<store-name></store-name>
<!-- <store-name></store-name> -->
<view class="t-title">{{switchInfo.hardware_name}}定时</view>
<view class="t-box">
@ -25,49 +25,74 @@
</view>
</picker>
</view>
<view class="il-select-week">
<view class="il-select-week" v-if="sIndex==0">
<block v-for="(e,i) in weekArrSelect" :key="i">
<view class="" :class="e?'active':''" @click="clickWeekTab(i)">星期{{weekArrCN[i]}}</view>
</block>
</view>
<view class="il-select">
<text class="">时间点</text>
<picker mode="time" :value="time" start="09:01" end="21:01" @change="bindTimeChange">
<view class="il-input">
<text>{{time}}</text><image src="../../../../static/images/icon/arrow_b2.png" mode=""></image>
<block v-if="sIndex==1">
<view class="il-select" >
<text class="">具体日期</text>
<!-- <picker @change="bindPickerChangeDay" :value="dIndex" :range="dayArr"> -->
<view class="il-input" @click="padConfig.show = true">
<text>请点击选择</text><image src="../../../../static/images/icon/arrow_b2.png" mode=""></image>
</view>
<!-- </picker> -->
</view>
</picker>
</view>
<view class="il-select">
<text class="">具体日期</text>
<picker @change="bindPickerChangeDay" :value="dIndex" :range="dayArr">
<view class="il-input">
<text>{{dayArr[dIndex]}} </text><image src="../../../../static/images/icon/arrow_b2.png" mode=""></image>
<view class="il-select-week">
<block v-for="(e,i) in dayArrSelect" :key="i">
<view v-if="e" class="" :class="e?'active':''" @click="">{{i+1}} </view>
</block>
</view>
</picker>
</view>
</block>
<view class="il-select-double">
<view class="il-select-double" v-if="sIndex==2">
<text class="">具体时间段</text>
<view class="il-input">
<picker mode="time" :value="time" start="00:01" end="23:59" @change="bindTimeChange">
<text>开始时间 {{time}} </text>
<picker mode="date" :value="startTime" :start="startDate" :end="endDate" @change="bindTimeChangeStart">
<text>{{startTime?startTime+'起':'请选择开始时间'}}</text>
</picker>
<view class=""></view>
<picker mode="time" :value="time" start="00:01" end="23:59" @change="bindTimeChange">
<text>结束时间 {{time}} </text>
<picker mode="date" :value="endTime" :start="startDate" :end="endDate" @change="bindTimeChangeEnd">
<text>{{endTime?endTime+"止":"请选择结束时间"}}</text>
</picker>
</view>
</view>
<view class="il-select">
<text class="">时间点</text>
<picker mode="time" :value="time" start="09:01" end="21:01" @change="bindTimeChange">
<view class="il-input">
<text>{{time}}</text><image src="../../../../static/images/icon/arrow_b2.png" mode=""></image>
</view>
</picker>
</view>
</view>
<view class="save-btn">保存</view>
<view class="save-btn" @click="saveAdd">保存</view>
<view class="bottom-tips">保存后发布后将会以最新的定时更新到欧轩智能中控设备首页<text>定时列表</text>可查看最新发布</view>
</view>
<!-- 日期天多选选择弹框 -->
<view class="voice_control_pad" v-if="padConfig.show" @touchmove.stop.prevent="(()=>{})">
<view class="cover_bg" @click.stop="padConfig.show=false" @touchmove.stop.prevent="(()=>{})"></view>
<view class="v_box" >
<scroll-view scroll-y="true" >
<view class="il-select-week">
<block v-for="(e,i) in dayArrSelect" :key="i">
<view class="" :class="e?'active':''" @click.stop="clickDaysTab(i)">{{i+1}} </view>
</block>
</view>
</scroll-view>
<view class="v_btns">
<view class="voice_btn btn_green" @click="padConfig.show=false">确认</view>
</view>
</view>
</view>
</view>
</template>
@ -114,56 +139,171 @@
const currentDate = this.getDate({
format: true
})
const dayArr = (()=>{
let day = []
const dayArrSelect = (()=>{
let days = []
for(var i=1;i<=31;i++){
day.push(i)
days.push(false)
}
return day
return days
})()
return {
// tabArr,
// infoArr,
switchInfo:"",
switchInfo:"",//
on:false,
singleArr:["星期","具体日期","具体时间段"],
sIndex:0,
dayArr:dayArr,
dayArrSelect:dayArrSelect,//31
dIndex:0,
weekArrCN,
weekArrSelect:[false,false,false,false,false,false,false],
date: currentDate,
time: '00:00',
time: '00:00-',
startTime:"",
endTime:"",
padConfig:{
show:false,
data:[]
},
reqData:{// sendPacket switchData->postData->reqData()
"device": "00-10-7a-0f-6d-7a",
"delay": 1,
"data": {
"name": "set-time-select",
"value": {
"data": "传入设置的json格式字符串,例如:{\"time_arrow_id\":\"c8c39f5c-8dd4-11eb-9aad-4e965989bbce\",\"group\":\"RPIO\",\"type\":\"DAY_OF_WEEK\",\"day_of_week\":[0,1,2,3,6,5,4],\"day_of_month\":null,\"date_slice\":[{\"start\":\"\",\"end\":\"\"}],\"times_on_day\":[\"10:13:00-10:13:10\"],\"expand_tags\":null,\"expand_value\":{\"main\":{\"name\":\"set-rpio\",\"title\":\"测试中控照明9\",\"value\":{\"id\":\"9\",\"status\":\"high\"}}},\"weights\":0,\"extension\":null}"
}
}
},
postData:{// reqData.data.value
"time_arrow_id": "", //
"group": "RPIO", //,,,
"type": "DAY_OF_WEEK", //DAY_OF_WEEK/DAY_OF_MONTH/DATE_SLICE , ,,.
"day_of_week": [
],
"day_of_month": null, //
"date_slice": [
{
"start": "",
"end": ""
}
],
"times_on_day": [//
// "10:13:00-10:13:10" //,10s
],
"expand_tags": null,
"expand_value": {
"main": { //
"name": "set-rpio",
"title": "",
"value": {
"id": "",//id
"status": "high" //low:, high:
}
}
},
"weights": 0,//
"extension": null
},
switchData:"",// postData.expand_value.main
}
},
async onLoad(option) {
let switchInfo = JSON.parse(decodeURIComponent(option.switchInfo));
this.switchInfo = switchInfo;
console.log("switchInfo:",switchInfo);
try {
util.showLoad();
let _brandInfo = await this.$store.dispatch('getBrandInfo');
await this.$store.dispatch('getStoreList');
util.hideLoad();
} catch (err) {
util.hideLoad();
}
this.handleOpts(option);
},
onShow() {
this.updateList() //
// this.updateList() //
},
watch: {
curStoreInfo(newVal, oldVal){
// this.infoArr.length = 1
this.updateList()
// this.updateList()
}
},
methods: {
//
handleOpts(option){
let switchInfo = JSON.parse(decodeURIComponent(option.switchInfo));
this.switchInfo = switchInfo // (使hardware_name)
this.switchData = switchInfo.switchData //
//
// try {
// util.showLoad();
// let _brandInfo = await this.$store.dispatch('getBrandInfo');
// await this.$store.dispatch('getStoreList');
// util.hideLoad();
// } catch (err) {
// util.hideLoad();
// }
},
//
saveAdd(){ //switchData()->postData()->reqData()
let {switchData,postData,reqData,curStoreInfo,sIndex,time,getWeekRes,getMonthDaysRes,getSliceRes,getDayTimeRes,sendDataToVC} = this;
// ["","",""],
if(sIndex==0)postData.day_of_week = getWeekRes(); //
if(sIndex==1)postData.day_of_month = getMonthDaysRes(); //
if(sIndex==2)postData.date_slice = getSliceRes(); //
if(time=="00:00-") return util.showNone("请先选择定时时间点!")
postData.times_on_day = getDayTimeRes(); //,
switchData.data.title = `${this.switchInfo.hardware_name}-${this.singleArr[this.sIndex]}定时`;
postData.expand_value.main = switchData.data;
reqData.device = switchData.device
reqData.data.value.data = JSON.stringify(postData)
console.log("reqData:",JSON.stringify(reqData));
uni.showModal({
content: `确认要设置${curStoreInfo.venue_name}中 [ ${this.switchInfo.hardware_name} ] 的[ ${this.singleArr[this.sIndex]} ]计划任务 ?`,
success(res) {
console.log("确认执行", res.confirm)
if(res.confirm){
sendDataToVC();
}
},
})
},
getWeekRes(){
console.log(this.weekArrSelect);
let res = []
this.weekArrSelect.forEach((e,i)=>{
if(e)res.push(i)
})
return res
},
getMonthDaysRes(){
let res = []
this.dayArrSelect.forEach((e,i)=>{
if(e)res.push(i+1)
})
return res
},
getSliceRes(){
let res = [],{startTime,endTime} = this;
if(startTime&&endTime){
res.push({
"start": startTime,
"end": endTime
})
}else{
res.push({
"start": "",
"end": ""
})
}
return res
},
getDayTimeRes(){
let res = []
res.push( `${this.time}:00-${this.time}:10`)
return res
},
tapSwitch(){ //
this.on = !this.on
this.switchData.data.value.status = this.on? "low":"high";
},
clickWeekTab(index) {
if(this.weekArrSelect[index]) {
@ -172,7 +312,52 @@
this.$set(this.weekArrSelect,index,true)
}
},
//
clickDaysTab(index) {
if(this.dayArrSelect[index]) {
this.$set(this.dayArrSelect,index,false)
}else{
this.$set(this.dayArrSelect,index,true)
}
},
bindPickerChangeSingle(e){
console.log('bindPickerChangeSingle,携带值为', e.target.value)
this.sIndex = e.target.value
// DAY_OF_WEEK/DAY_OF_MONTH/DATE_SLICE
this.postData.type = ['DAY_OF_WEEK','DAY_OF_MONTH','DATE_SLICE'][e.target.value]
this.resetPostData();
},
resetPostData(){ //
let _reset = {
"day_of_week": [
],
"day_of_month": null, //
"date_slice": [{
"start": "",
"end": ""
}
],}
for(var i in _reset){
console.log("重置:",i);
this.postData[i] = _reset[i]
}
},
bindTimeChange(e){ //
this.time = e.target.value
},
bindTimeChangeStart(e){ //
if(new Date(this.endTime)-new Date(e.target.value)<0) return util.showNone("开始时间异常")
this.startTime = e.target.value
},
bindTimeChangeEnd(e){ //
if(new Date(e.target.value)-new Date(this.startTime)<0) return util.showNone("结束时间异常")
this.endTime = e.target.value
},
bindPickerChangeDay(e){ //
console.log('bindPickerChangeDay,携带值为', e.target.value)
this.dIndex = e.target.value
},
//
delItem(e,i) {
let that = this
let delData = {
@ -184,10 +369,10 @@
"uuid": e[3]
}
}
}
uni.showModal({
content: `确认要删除: ${e[0]}的计划任务 ?`,
success(res) {
}
uni.showModal({
content: `确认要删除: ${e[0]}的计划任务 ?`,
success(res) {
console.log("确认删除", res.confirm)
if(res.confirm){
that.operateReq({
@ -197,21 +382,9 @@
},
isTip:true
})
}
},
})
},
bindPickerChangeSingle(e){
console.log('bindPickerChangeSingle,携带值为', e.target.value)
this.sIndex = e.target.value
},
bindTimeChange(e){
console.log('bindTimeChange,携带值为', e.target.value)
this.time = e.target.value
},
bindPickerChangeDay(e){
console.log('bindPickerChangeDay,携带值为', e.target.value)
this.dIndex = e.target.value
}
},
})
},
getDate(type) {
const date = new Date();
@ -220,67 +393,31 @@
let day = date.getDate();
if (type === 'start') {
year = year - 60;
year = year - 0;
} else if (type === 'end') {
year = year + 2;
year = year + 10;//10
}
month = month > 9 ? month : '0' + month;;
day = day > 9 ? day : '0' + day;
return `${year}-${month}-${day}`;
},
async updateList() {
async sendDataToVC() {
let that = this
let timingData = {
"device": this.curStoreInfo.device_name,
"data": {
"name": "get-time-select",
"value": {}
}
}
timingData = this.reqData
this.operateReq({
data: timingData,
succFun: (list) => {
that.infoArr.length = 1;
list.forEach(async (item,i)=> {
if(item.day_of_week)item.day_of_week = item.day_of_week.sort((a,b)=>a-b);//
let timetxt = await that.getTimeTxt(item);
that.infoArr.push([item.expand_value.main.title||"-",timetxt||"-","删除",item.time_arrow_id])
})
}
})
},
//,
getTimeTxt(row) {
return new Promise((rs,rj)=>{
let timeText = ""
if (!row.expand_value) {
rj('error')
}
if (row.type === 'DAY_OF_WEEK' && row.day_of_week) {
let arr = []
for (let i = 0; i < row.day_of_week.length; i++) {
arr += ' 周' + ['日', '一', '二', '三', '四', '五', '六'][row.day_of_week[i]];
}
timeText = `每个星期的 <strong>[${arr} ]</strong> `
}
if (row.type === 'DAY_OF_MONTH' && row.day_of_month) {
let arr = []
for (let i = 0; i < row.day_of_month.length; i++) {
arr += row.day_of_month[i] + '号 ';
succFun: (res) => {
console.log("操作结果:",res);
if(res=="ok"){
let timing_list = "/subpackage/device/pages/timing/timing_list"
util.routeTo(`${timing_list}`, 'nT');
}
timeText = `每个月的 <strong>[ ${arr} ]</strong> `
}
if (row.type === 'DATE_SLICE' && row.date_slice) {
timeText = ` ${row.date_slice[0].start.substring(0, 10)}~${row.date_slice[0].end.substring(0, 10)}`
}
if (row.times_on_day && row.times_on_day.length) {
timeText += ` 当天<strong>${row.times_on_day[0].substring(0, 5)}</strong>`
}
if (!row.expand_value.main) return '历史错误数据';
rs(` ${timeText} 执行 ${row.expand_value.main.value.status === 'high' ? '<strong style="color:gray;">[关闭]</strong>' : '<strong style="color:#009874;">[开启]</strong>'} 操作`)
})
},
isTip:true
})
},
goBack() {
uni.navigateBack({
@ -459,5 +596,109 @@
color: #009874;
}
margin: 40rpx;
}
}
//
.voice_control_pad {
position: fixed;
top: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
z-index: 1;
.cover_bg {
position: fixed;
top: 0;
background: rgba($color: #000000, $alpha: .3);
z-index: 2;
width: 100%;
height: 100%;
}
.v_box {
z-index: 3;
width: 630rpx;
background-color: white;
border-radius: 10rpx;
scroll-view{
margin-top: 30rpx;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
border-radius: 5rpx;
flex-wrap: wrap;
width: 100%;
height: 700rpx;
border-bottom: 1rpx solid #F2F2F7;
.il-select-week{
width: 100%;
height: auto;
@include centerFlex(flex-start);
flex-wrap: wrap;
> view{
@include centerFlex(center);
width: 160rpx;
height: 88rpx;
border: 2rpx solid #979797;
border-radius: 6rpx;
color: #979797;
margin-left: 40rpx;
margin-top: 40rpx;
}
.active{
background: rgba(0,152,116,0.12);
border: 2rpx solid #009874;color: #009874;
}
}
}
.voice_btn {
width: 392rpx;
height: 112rpx;
background: #009874;
border-radius: 5rpx;
text-align: center;
line-height: 112rpx;
color: white;
font-size: 32rpx;
}
.btn_active {
color: black;
background-color: rgba($color: #000000, $alpha: .4);
}
.btn_white {
width: 204rpx;
height: 88rpx;
background-color: white;
color: #009874;
border: 1rpx solid #009874;
line-height: 88rpx;
}
.btn_green {
width: 204rpx;
height: 88rpx;
background-color: #009874;
color: white;
line-height: 88rpx;
}
.v_btns {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 50rpx 0;
}
}
}
</style>
Loading…
Cancel
Save