zmt
4 years ago
8 changed files with 861 additions and 1 deletions
-
6src/pages.json
-
4src/subpackage/device/js/device_api.js
-
849src/subpackage/device/pages/audio_manage/audio_manage.vue
-
3src/subpackage/device/pages/index/index.vue
-
BINsrc/subpackage/device/static/images/i_txt.png
-
BINsrc/subpackage/device/static/images/i_voice_1.png
-
BINsrc/subpackage/device/static/images/i_voice_2.png
-
BINsrc/subpackage/device/static/images/i_voice_3.png
@ -0,0 +1,849 @@ |
|||
<template> |
|||
<view class="switch-manage"> |
|||
<store-name></store-name> |
|||
<view class="sm-tit"> |
|||
<text>{{pageInfo.name || '-'}}</text> |
|||
<text>{{pageInfo.tips || ''}}</text> |
|||
</view> |
|||
<view class="sm-list"> |
|||
<view class="sl-item" v-for="(e, i) in deviceList" :key="i"> |
|||
<view class="si-top"> |
|||
<image mode="aspectFit" :src="getIcon()"></image> |
|||
<view class="st-right"> |
|||
<view class="sr-name">{{e.hardware_name || '-'}}</view> |
|||
<!-- 门闸没有状态查询 --> |
|||
<!-- 请求接口自定义字段设备状态 1->在线,0->离线 --> |
|||
<view class="sr-bot" v-if="pageInfo.id !=5"> |
|||
<view :class="[e.defineStatusCode == 1?'active':'']"> |
|||
<text>{{ e.defineStatusCode == 1 ? '设备在线' : e.defineStatusCode == 0?'设备离线' : '-' }}</text> |
|||
</view> |
|||
<image mode="aspectFit" src="/subpackage/device/static/images/refresh.png" |
|||
@click="refreshStatusBtn({switchInfo:e, index:i})"></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<block v-if="pageInfo.name=='音响管理'"> |
|||
<view class="si-bottom"> |
|||
<view v-if="pageInfo.isOpen" @click="operateBtn({ switchInfo: e, status: 1 })"> |
|||
<image mode="aspectFit" :src="pageInfo.openIcon || ''"></image> |
|||
<view>{{pageInfo.openName || '-'}}</view> |
|||
</view> |
|||
<view v-if="pageInfo.isClose" @click="operateBtn({ switchInfo: e, status: 0 })"> |
|||
<image mode="aspectFit" :src="pageInfo.closeIcon || ''"></image> |
|||
<view>{{pageInfo.closeName || '-'}}</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
<!-- <block v-else> |
|||
<view class="si-bottom" v-if="pageInfo.isOpen || pageInfo.isClose"> |
|||
<view v-if="pageInfo.isOpen" @click="operateBtn({ switchInfo: e, status: 1 })"> |
|||
<image mode="aspectFit" :src="pageInfo.openIcon || ''"></image> |
|||
<view>{{pageInfo.openName || '-'}}</view> |
|||
</view> |
|||
<view v-if="pageInfo.isClose" @click="operateBtn({ switchInfo: e, status: 0 })"> |
|||
<image mode="aspectFit" :src="pageInfo.closeIcon || ''"></image> |
|||
<view>{{pageInfo.closeName || '-'}}</view> |
|||
</view> |
|||
</view> |
|||
</block> --> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 音频控制 --> |
|||
<view class="voice_control_pad" v-if="voicePadConfig.showVoicePad"> |
|||
<!-- 文字转语音 --> |
|||
<view class="cover_bg" @click.stop="voicePadConfig.showVoicePad=false"> |
|||
</view> |
|||
<view class="v_box" v-if="voicePadConfig.step==0"> |
|||
<textarea value="" v-model="voicePadConfig.txt" placeholder="请输您要说的话,输入标点符号,智能语音效果更好哦!" /> |
|||
<view class="v_btns"> |
|||
<view class="voice_btn btn_white" @click="sendVoice(voicePadConfig.txt)">发送</view> |
|||
<view class="voice_btn btn_green" @click="listenVoice(voicePadConfig.txt)">试听</view> |
|||
</view> |
|||
</view> |
|||
<!-- 录音 --> |
|||
<view class="v_box" v-else> |
|||
<view class="voice_title">请说话</view> |
|||
<image class="voice_img" src="../../static/images/i_voice.png" :src="getVoiceIcon(voicePadConfig.step)" |
|||
:class="voicePadConfig.step==2?'voice_img_playing':''"></image> |
|||
<view class="voice_btn " @longpress="longPressHandle" @touchend="touchEndHandle" |
|||
hover-class="btn_active" v-if="voicePadConfig.step<3">按住说话</view> |
|||
<!-- <view class="voice_btn btn_active" v-else-if="voicePadConfig.step==2">按住说话</view> --> |
|||
<!-- hover-class --> |
|||
<view class="v_btns" v-else-if="voicePadConfig.step==3"> |
|||
<view class="voice_btn btn_white" @click="sendVoice()">发送</view> |
|||
<view class="voice_btn btn_green" @click="listenVoice()">听取录音</view> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
const recorderManager = uni.getRecorderManager(); |
|||
const innerAudioContext = uni.createInnerAudioContext(); |
|||
innerAudioContext.autoplay = true; |
|||
|
|||
import store_name from '../../components/store_name/store_name'; |
|||
import deviceServer from '../../js/device_server'; |
|||
import deviceApi from '../../js/device_api'; |
|||
const showArr = { |
|||
|
|||
's14': { |
|||
id: 14, |
|||
name: '音响管理', |
|||
isOpen: true, |
|||
isClose: true, |
|||
openIcon: '/subpackage/device/static/images/i_txt.png', |
|||
closeIcon: '/subpackage/device/static/images/i_voice.png', |
|||
openName: '文转音', |
|||
closeName: '语音', |
|||
hardware_type: 'AudioPlayer', |
|||
tips: "点击对应音响的麦克风可以进行说话,说话内容将通过音响播放。", |
|||
}, |
|||
|
|||
} |
|||
import { |
|||
mapState |
|||
} from 'vuex'; |
|||
import util from '../../../../utils/util'; |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
'store-name': store_name |
|||
}, |
|||
computed: { |
|||
...mapState({ |
|||
curStoreInfo: state => state.device.curStoreInfo, |
|||
}), |
|||
getVoiceIcon() { |
|||
return i => { |
|||
return `../../static/images/i_voice_${i}.png` |
|||
} |
|||
}, |
|||
|
|||
}, |
|||
watch: { |
|||
curStoreInfo(newVal, oldVal) { |
|||
this.deviceList = []; |
|||
this.getDeviceList({ |
|||
stadium_id: newVal.id, |
|||
hardware_type: this.pageInfo.hardware_type |
|||
}) |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
pageInfo: {}, |
|||
deviceList: [], |
|||
voicePadConfig: { |
|||
showVoicePad: true, |
|||
step: 1, // 0,文字转语音 1 未录音, 2 录音中, 3 录音结束 |
|||
txt: "", |
|||
voicePath: "", |
|||
voiceUrl: "", |
|||
} |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
let self = this; |
|||
recorderManager.onStop(function(res) { |
|||
console.log('recorder stop' + JSON.stringify(res)); |
|||
self.voicePadConfig.voicePath = res.tempFilePath; |
|||
}); |
|||
|
|||
let _pageInfo = showArr[`s${options.sid}`] || {}; |
|||
this.pageInfo = _pageInfo; |
|||
uni.setNavigationBarTitle({ |
|||
title: _pageInfo.name |
|||
}); |
|||
|
|||
this.getDeviceList({ |
|||
stadium_id: this.curStoreInfo.id, |
|||
hardware_type: _pageInfo.hardware_type |
|||
}) |
|||
}, |
|||
methods: { |
|||
longPressHandle(e) { |
|||
console.log("长按开始..."); |
|||
this.voicePadConfig.step = 2 |
|||
console.log('开始录音'); |
|||
recorderManager.start({ |
|||
format: "wav" |
|||
}); |
|||
}, |
|||
touchEndHandle(e) { |
|||
console.log("触摸结束..."); |
|||
this.voicePadConfig.step = 3 |
|||
console.log('录音结束'); |
|||
recorderManager.stop(); |
|||
}, |
|||
//处理音响数据 |
|||
HandleVoiceOperate({ |
|||
switchInfo, |
|||
status |
|||
}) { |
|||
console.log(switchInfo, status); |
|||
if (status === 1) { //文字转语音 |
|||
console.log("文字转语音"); |
|||
this.voicePadConfig.showVoicePad = true |
|||
this.voicePadConfig.step = 0 |
|||
// this.voicePadConfig |
|||
} else { //语音发送 |
|||
console.log("语音发送"); |
|||
this.voicePadConfig.showVoicePad = true |
|||
this.voicePadConfig.step = 1 |
|||
} |
|||
}, |
|||
//发送语音到中控 |
|||
async sendVoice() { |
|||
let that = this |
|||
console.log(deviceApi.ORIGIN) |
|||
//文字转语音 |
|||
let _url = ""; //需上传给后台的语音地址 |
|||
if(this.voicePadConfig.step==0){ |
|||
let txt = this.voicePadConfig.txt |
|||
_url = `${deviceApi.ORIGIN}/ouxuanac/tts/textToVoice.wav?text=${txt}&voice_type=4&speed=-1&volume=10`; |
|||
} |
|||
//已录音 |
|||
if(this.voicePadConfig.step==3){ |
|||
console.log("88888",that.voicePadConfig.voicePath); |
|||
let e = await deviceServer.uploadFile({ |
|||
url: deviceApi.uploadAudio, |
|||
filePath: that.voicePadConfig.voicePath |
|||
}); |
|||
let _res = util.jsonPar(e.data); |
|||
if (_res.code == 0) { |
|||
console.log(123, _res) |
|||
that.voicePadConfig.voiceUrl = _res.data.url |
|||
console.log("上传成功:", that.voicePadConfig.voiceUrl); |
|||
_url = _res.data.url // 上传后地址 |
|||
} else { |
|||
console.error('上传录音失败--->', _res); |
|||
util.showNone(_res.message || '上传录音失败,请重试!') |
|||
return |
|||
} |
|||
} |
|||
//组装中控所需data |
|||
let _data = { |
|||
"device": this.curStoreInfo.device_name, |
|||
"data": { |
|||
"name": "audio-player", |
|||
"value": { |
|||
"url": _url |
|||
} |
|||
}, |
|||
"token": "f0d5c19b-b87e-11eb-bc7d-5254005df464" |
|||
} |
|||
//发送命令到中控 |
|||
this.operateReq({ |
|||
data: _data |
|||
}) |
|||
|
|||
}, |
|||
//试听语音 TODO |
|||
async listenVoice() { |
|||
let txt = this.voicePadConfig.txt |
|||
console.log('播放录音', txt); |
|||
if(this.voicePadConfig.step==0){ |
|||
//需要将语音下载到本地,然后播放 |
|||
let url =`${deviceApi.ORIGIN}/ouxuanac/tts/textToVoice.wav?text=${txt}&voice_type=4&speed=-1&volume=10`; |
|||
let updated_url = await this.getDownloadUrl(url) |
|||
innerAudioContext.src = updated_url |
|||
} |
|||
if(this.voicePadConfig.step==3){ |
|||
innerAudioContext.src = this.voicePadConfig.voicePath; |
|||
} |
|||
if(innerAudioContext.src)innerAudioContext.play(); |
|||
}, |
|||
//TODO |
|||
getDownloadUrl(url) { |
|||
console.log('下载录音'); |
|||
return new Promise((rs,rj)=>{ |
|||
|
|||
}) |
|||
return "下载录音" |
|||
}, |
|||
|
|||
getDeviceList({ |
|||
stadium_id, |
|||
hardware_type, |
|||
limit = 100, |
|||
page = 1 |
|||
}) { |
|||
util.showLoad(); |
|||
deviceServer.get({ |
|||
url: deviceApi.hardwareList, |
|||
data: { |
|||
'filter[hardware_type]': hardware_type, |
|||
'filter[stadium_id]': stadium_id, |
|||
'limit': limit, |
|||
'page': page, |
|||
}, |
|||
failMsg: '加载失败!' |
|||
}) |
|||
.then(res => { |
|||
util.hideLoad(); |
|||
let _list = res.list || []; |
|||
this.deviceList = _list; |
|||
console.log(res) |
|||
}) |
|||
.catch(util.hideLoad) |
|||
}, |
|||
// 按钮操作, status 0 -> 关(左), 1 -> 开(右) |
|||
operateBtn: util.debounce(function({ |
|||
switchInfo, |
|||
status |
|||
}) { |
|||
if (switchInfo.hardware_type === "AudioPlayer") return this.HandleVoiceOperate({ |
|||
switchInfo, |
|||
status |
|||
}); //单独处理音响操作0704 |
|||
|
|||
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 |
|||
}); |
|||
} |
|||
|
|||
this.operateReq({ |
|||
data: _data |
|||
}); |
|||
|
|||
}, 300, 300), |
|||
|
|||
// 获取接口参数结构 |
|||
getOperateReqData({ |
|||
switchInfo, |
|||
status |
|||
}) { |
|||
let { |
|||
curStoreInfo |
|||
} = this; |
|||
|
|||
let _query = switchInfo.hardware_type === 'GateControl' ? |
|||
this.getGateQuery({ |
|||
switchInfo, |
|||
status |
|||
}) : |
|||
this.getSwitchQuery({ |
|||
switchInfo, |
|||
status |
|||
}); |
|||
let _data = { |
|||
device: curStoreInfo.device_name, // 中控名, |
|||
data: _query, // 后端数据结构, 参考src\subpackage\device\js\ouxuanac.md |
|||
}; |
|||
|
|||
//针对门禁没有关按钮发两条命令->开&关 20201224 后端: 直接发两条 关的那条这里填5 然后你那边不用管返回 |
|||
if (switchInfo.hardware_type === 'AccessControl' && status == 0) _data['delay'] = '5'; |
|||
|
|||
return _data; |
|||
}, |
|||
// 操作接口请求 |
|||
operateReq({ |
|||
data, |
|||
isTip = true, |
|||
isLoad = true |
|||
}) { |
|||
let that = this |
|||
if (isLoad) util.showLoad(); |
|||
deviceServer.post({ |
|||
url: deviceApi.ouxuanac, |
|||
data: data, |
|||
isDefaultGet: false, |
|||
}) |
|||
.then(res => { |
|||
if (isLoad) util.hideLoad(); |
|||
if (res.data.code == 0) { |
|||
if (isTip) util.showNone(res.data.message || '操作成功!'); |
|||
that.voicePadConfig.showVoicePad = false; //操作成功后关闭voicePad |
|||
} else { |
|||
if (isTip) util.showNone(res.data.message || '操作失败!'); |
|||
} |
|||
}) |
|||
.catch(err => { |
|||
if (isLoad) util.hideLoad() |
|||
}) |
|||
}, |
|||
|
|||
// 门闸数据结构 // 门闸数据结构不统一 |
|||
getGateQuery({ |
|||
switchInfo, |
|||
status |
|||
}) { |
|||
let { |
|||
enter_id, |
|||
leave_id, |
|||
hardware_net_addr |
|||
} = switchInfo; |
|||
// 进出控制ID |进入-> enter_id 离开-> leave_id| |
|||
let _cid = status === 1 ? enter_id : |
|||
status === 0 ? leave_id : ''; |
|||
return { |
|||
name: 'gate', |
|||
value: { |
|||
tcp: hardware_net_addr + '', |
|||
cid: _cid + '' |
|||
}, |
|||
is_delay: true, |
|||
queue_group: 'gate' |
|||
} |
|||
}, |
|||
// |
|||
refreshStatusBtn: util.debounce(function({ |
|||
switchInfo, |
|||
index |
|||
}) { |
|||
this.getStatusReq({ |
|||
data: this.getSwitchStatusQuery(switchInfo), |
|||
index, |
|||
}) |
|||
}, 300, 300), |
|||
// 获取设备状态 |
|||
getStatusReq({ |
|||
data, |
|||
index |
|||
}) { |
|||
let _deviceList = this.deviceList.slice(); |
|||
util.showLoad(); |
|||
deviceServer.post({ |
|||
url: deviceApi.ouxuanac, |
|||
data: data, |
|||
isDefaultGet: false, |
|||
}) |
|||
.then(res => { |
|||
util.hideLoad(); |
|||
let _data = res.data || {}; |
|||
console.log(this.changeLowerCase(_data.data)) |
|||
if (_data.code == 504 || this.changeLowerCase(_data.data).indexOf('timeout') != -1) { |
|||
_deviceList[index]['defineStatusCode'] = 0; |
|||
|
|||
} else if (_data.code == 0 && this.changeLowerCase(_data.data).indexOf('timeout') == -1) { |
|||
_deviceList[index]['defineStatusCode'] = 1; |
|||
} else { |
|||
util.showNone(_data.message || '操作失败!'); |
|||
} |
|||
this.deviceList = _deviceList; |
|||
// if(res.data.code == 0){ |
|||
// if(isTip)util.showNone(res.data.message || '操作成功!'); |
|||
// }else{ |
|||
// if(isTip)util.showNone(res.data.message || '操作失败!'); |
|||
// } |
|||
}) |
|||
.catch(util.hideLoad) |
|||
}, |
|||
|
|||
// 咖啡机和门闸暂时没有状态 |
|||
// 设备状态请求参数数据结构 |
|||
getSwitchStatusQuery(switchInfo) { |
|||
let { |
|||
curStoreInfo |
|||
} = this; |
|||
let { |
|||
hardware_connect_method, |
|||
hardware_type, |
|||
hardware_id, |
|||
node_id, |
|||
hardware_net_addr |
|||
} = switchInfo; |
|||
|
|||
|
|||
const _query = { |
|||
name: this.getStatusQueryName(switchInfo), |
|||
value: { |
|||
id: hardware_id + '', |
|||
} // value 内值全为String |
|||
}; |
|||
|
|||
if (this.changeLowerCase(hardware_connect_method) === 'tcp') _query.value['tcp'] = hardware_net_addr + ''; |
|||
let _flag = this.changeLowerCase(hardware_connect_method) === 'serialport485' || this.changeLowerCase( |
|||
hardware_connect_method) === 'tcp'; |
|||
if (_flag) { |
|||
if (hardware_type === 'Air') { // 空调开关状态 key为 op |
|||
_query.value['op'] = 'status' |
|||
_query['name'] = this.getAirQueryName(switchInfo) |
|||
} else { |
|||
_query.value['p'] = node_id + ''; // 硬件子id |
|||
// postData.value['o'] = this.getRelayStatus(status); // 开关状态 |
|||
} |
|||
} |
|||
|
|||
return { |
|||
device: curStoreInfo.device_name, // 中控名, |
|||
data: _query, // 后端数据结构, 参考src\subpackage\device\js\ouxuanac.md |
|||
} |
|||
|
|||
// this.getStatusReq({ |
|||
// index, |
|||
// data: { |
|||
// device: curStoreInfo.device_name, // 中控名, |
|||
// data: _query, // 后端数据结构, 参考src\subpackage\device\js\ouxuanac.md |
|||
// } |
|||
// }) |
|||
}, |
|||
// switchInfo -> 当前开关信息 |
|||
// status -> 开关状态 0 -> 关(右), 1 -> 开(左) |
|||
// 数据结构参考 src\subpackage\device\js\ouxuanac.md |
|||
// 空调开关数据结构独立判断处理 hardware_type === 'Air' |
|||
getSwitchQuery({ |
|||
switchInfo, |
|||
status |
|||
}) { |
|||
let { |
|||
hardware_connect_method, |
|||
hardware_type, |
|||
hardware_id, |
|||
node_id, |
|||
hardware_net_addr |
|||
} = switchInfo; |
|||
|
|||
const postData = { |
|||
name: this.getQueryName(switchInfo), |
|||
value: { |
|||
id: hardware_id + '', |
|||
} // value 内值全为String |
|||
}; |
|||
|
|||
if (this.changeLowerCase(hardware_connect_method) === 'gpio') postData.value['status'] = this |
|||
.getRelayStatus(status); |
|||
|
|||
// tcp 连接需要 hardware_net_addr |
|||
if (this.changeLowerCase(hardware_connect_method) === 'tcp') postData.value['tcp'] = hardware_net_addr + |
|||
''; |
|||
let _flag = this.changeLowerCase(hardware_connect_method) === 'serialport485' || this.changeLowerCase( |
|||
hardware_connect_method) === 'tcp'; |
|||
if (_flag) { |
|||
if (hardware_type === 'Air') { // 空调开关状态 key为 op |
|||
postData.value['op'] = this.getAirRelayStatus(status); |
|||
postData['name'] = this.getAirQueryName(switchInfo) |
|||
} else { |
|||
// 空调设备不需要以下两个字段 |
|||
postData.value['p'] = node_id + ''; // 硬件子id |
|||
postData.value['o'] = this.getRelayStatus(status); // 开关状态 |
|||
} |
|||
} |
|||
return postData; |
|||
}, |
|||
changeLowerCase(str) { |
|||
return str.toString().toLocaleLowerCase(); |
|||
}, |
|||
// 常规开关状态 |
|||
// Low = "low", // 低电位,为开启 |
|||
// High = "high", // 高电位, 为关闭 |
|||
getRelayStatus(status) { |
|||
return ['high', 'low'][status] || '' |
|||
}, |
|||
|
|||
// 空调状态 |
|||
// status = "status", |
|||
// on = "on", |
|||
// off = "off", |
|||
getAirRelayStatus(status) { |
|||
return ['off', 'on'][status] || '' |
|||
}, |
|||
|
|||
// 非空调获取状态name |
|||
getStatusQueryName(switchInfo) { |
|||
let { |
|||
hardware_connect_method |
|||
} = switchInfo; |
|||
let _obj = { |
|||
'Gpio': 'get-rpio', // 全设备 |
|||
'SerialPort485': 'zzio404d-gpio-status', |
|||
'Tcp': 'zzio404d-gpio-status-tcp', |
|||
}; |
|||
|
|||
return _obj[hardware_connect_method] || '' |
|||
}, |
|||
|
|||
// 非空调获取设置name |
|||
getQueryName(switchInfo) { |
|||
let { |
|||
hardware_connect_method |
|||
} = switchInfo; |
|||
let _obj = { |
|||
'Gpio': 'set-rpio', // 全设备 |
|||
'SerialPort485': 'zzio404d-gpio', |
|||
'Tcp': 'zzio404d-gpio-tcp', |
|||
}; |
|||
|
|||
return _obj[hardware_connect_method] || '' |
|||
}, |
|||
// 空调name获取 |
|||
getAirQueryName(switchInfo) { |
|||
let { |
|||
hardware_connect_method, |
|||
hardware_model |
|||
} = switchInfo; |
|||
let _flag = this.changeLowerCase(hardware_connect_method) === 'tcp' && this.changeLowerCase( |
|||
hardware_model) === 'jianda'; |
|||
if (_flag) return 'ray-air-rs-tcp'; |
|||
let _obj = { |
|||
'acmelec': 'acmelec', |
|||
'zhongnan': 'zhongnan', |
|||
'jianda': 'ray-air-rs' |
|||
}; |
|||
return _obj[this.changeLowerCase(hardware_model)] || ''; |
|||
}, |
|||
|
|||
getIcon() { |
|||
let { |
|||
pageInfo |
|||
} = this; |
|||
if (!pageInfo.id) return ''; |
|||
return `/subpackage/device/static/images/devices/${pageInfo.id}.png` |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
@import '~style/public.scss'; |
|||
|
|||
.sm-tit { |
|||
padding-left: 40upx; |
|||
padding-top: 52upx; |
|||
padding-bottom: 30upx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
flex-wrap: nowrap; |
|||
|
|||
text:first-child { |
|||
line-height: 60upx; |
|||
font-size: 44upx; |
|||
font-weight: 500; |
|||
color: #333; |
|||
} |
|||
|
|||
text:last-child { |
|||
width: 466rpx; |
|||
color: #9C9C9F; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
|
|||
.sm-list { |
|||
padding: 0 32upx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
flex-wrap: wrap; |
|||
|
|||
.sl-item { |
|||
margin-bottom: 30upx; |
|||
width: 328upx; |
|||
border-radius: 10upx; |
|||
background-color: #fff; |
|||
|
|||
.si-top { |
|||
padding: 20upx 20upx 30upx; |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
|
|||
>image { |
|||
flex-shrink: 0; |
|||
flex-grow: 0; |
|||
margin-right: 20upx; |
|||
width: 80upx; |
|||
height: 80upx; |
|||
} |
|||
|
|||
.st-right { |
|||
flex-grow: 1; |
|||
|
|||
>.sr-name { |
|||
margin-bottom: 8upx; |
|||
line-height: 44upx; |
|||
font-size: 32upx; |
|||
color: #333; |
|||
@include textHide(1); |
|||
} |
|||
|
|||
.sr-bot { |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
>view { |
|||
margin-right: 8upx; |
|||
padding: 0 12upx; |
|||
font-size: 20upx; |
|||
line-height: 28upx; |
|||
border-radius: 28upx; |
|||
border: 2upx solid #9A9A9D; |
|||
color: #9A9A9D; |
|||
|
|||
&::before { |
|||
content: ''; |
|||
margin-right: 12upx; |
|||
margin-top: -4upx; |
|||
display: inline-block; |
|||
vertical-align: middle; |
|||
width: 8upx; |
|||
height: 8upx; |
|||
border-radius: 50%; |
|||
background-color: #9A9A9D; |
|||
} |
|||
|
|||
&.active { |
|||
color: #333333; |
|||
|
|||
&::before { |
|||
background-color: $themeColor; |
|||
} |
|||
} |
|||
} |
|||
|
|||
>image { |
|||
flex-shrink: 0; |
|||
flex-grow: 0; |
|||
width: 32upx; |
|||
height: 32upx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.si-bottom { |
|||
padding-top: 30upx; |
|||
padding-bottom: 30upx; |
|||
display: flex; |
|||
justify-content: center; |
|||
border-top: 2upx solid #F2F2F7; |
|||
|
|||
>view { |
|||
flex-shrink: 0; |
|||
flex-grow: 0; |
|||
width: 50%; |
|||
|
|||
>image { |
|||
display: block; |
|||
margin: 0 auto 20upx; |
|||
width: 100upx; |
|||
height: 100upx; |
|||
} |
|||
|
|||
>view { |
|||
font-size: 24upx; |
|||
line-height: 34upx; |
|||
text-align: center; |
|||
color: #9a9a9d; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//语音控制板 |
|||
.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; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
border-radius: 5rpx; |
|||
width: 620rpx; |
|||
height: 550rpx; |
|||
background-color: white; |
|||
|
|||
textarea { |
|||
margin-top: 50rpx; |
|||
width: 560rpx; |
|||
height: 260rpx; |
|||
} |
|||
|
|||
.voice_title { |
|||
max-width: 560rpx; |
|||
color: #1A1A1A; |
|||
font-size: 32rpx; |
|||
font-weight: 800; |
|||
margin-top: 68rpx; |
|||
} |
|||
|
|||
.voice_img { |
|||
margin-top: 70rpx; |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
} |
|||
|
|||
.voice_img_playing { |
|||
width: 206rpx; |
|||
height: 100rpx; |
|||
} |
|||
|
|||
.voice_btn { |
|||
margin-top: 100rpx; |
|||
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: 470rpx; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
} |
|||
} |
|||
</style> |
After Width: 200 | Height: 200 | Size: 11 KiB |
After Width: 200 | Height: 200 | Size: 9.9 KiB |
After Width: 103 | Height: 50 | Size: 2.9 KiB |
After Width: 102 | Height: 100 | Size: 2.4 KiB |
Write
Preview
Loading…
Cancel
Save
Reference in new issue