798 lines
18 KiB
Vue
798 lines
18 KiB
Vue
<template>
|
||
<view class="device-detail">
|
||
<view class="background"></view>
|
||
|
||
<!-- 头部导航 -->
|
||
<view class="header">
|
||
<view class="back-btn" @click="onCancel">
|
||
←
|
||
</view>
|
||
<view class="header-title">{{ $t('addWifiTitle') }}</view>
|
||
<view class="header-placeholder"></view>
|
||
</view>
|
||
|
||
<!-- 蓝牙连接状态 -->
|
||
<view class="bluetooth-status">
|
||
<view class="status-indicator">
|
||
<text v-if="ver_dataflag == 1" class="status-dot status-dot-disconnected"></text>
|
||
<text v-if="ver_dataflag == 2" class="status-dot status-dot-connecting"></text>
|
||
<text v-if="ver_dataflag == 3" class="status-dot status-dot-connected"></text>
|
||
<view v-if="ver_dataflag == 1" class="status-text status-text-disconnected">
|
||
{{ $t('bluetoothConnectionStatus') }}
|
||
</view>
|
||
<view v-if="ver_dataflag == 2" class="status-text status-text-connecting">
|
||
{{ $t('bluetoothStatus') }}
|
||
</view>
|
||
<view v-if="ver_dataflag == 3" class="status-text status-text-connected">
|
||
{{ $t('bluetoothStatus') }}
|
||
</view>
|
||
</view>
|
||
<view class="status-action"
|
||
:class="{
|
||
'status-action-disconnected': ver_dataflag == 1,
|
||
'status-action-connecting': ver_dataflag == 2,
|
||
'status-action-connected': ver_dataflag == 3
|
||
}"
|
||
@click="getlj"
|
||
v-if="ver_dataflag == 1">
|
||
{{ $t('clickToConnect') }}
|
||
</view>
|
||
<view class="status-action status-action-connecting" v-if="ver_dataflag == 2">
|
||
{{ $t('connecting') }}
|
||
</view>
|
||
<view class="status-action status-action-connected" v-if="ver_dataflag == 3">
|
||
{{ $t('connected') }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 内容区域 -->
|
||
<view class="content-container">
|
||
<view class="wifi-form">
|
||
<view class="wifi-item wifi-item-border">
|
||
<view class="wifi-label">
|
||
<image class="wifi-icon" src="https://api.ccttiot.com/smartmeter/img/static/ukxdSJmKDJ8YxvaHcfEb" mode="aspectFit"></image>
|
||
{{ $t('wifiNameLabel') }}
|
||
</view>
|
||
<input class="wifi-input" type="text" v-model="wifiname" :placeholder="$t('wifiNamePlaceholder')" />
|
||
</view>
|
||
<view class="wifi-item">
|
||
<view class="wifi-label">
|
||
<image class="wifi-icon" src="https://api.ccttiot.com/smartmeter/img/static/uibUVxsJvMdNRMXi0TWw" mode="aspectFit"></image>
|
||
{{ $t('wifiPasswordLabel') }}
|
||
</view>
|
||
<input class="wifi-input" type="text" v-model="wifipwd" :placeholder="$t('wifiPasswordPlaceholder')" />
|
||
</view>
|
||
</view>
|
||
<view class="warning-text">
|
||
{{ $t('wifiFrequencyWarning') }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部按钮 -->
|
||
<view class="button-container">
|
||
<view class="btn-secondary" @click="btnsh">
|
||
Позже
|
||
</view>
|
||
<view class="btn-primary" @click="btnjx">
|
||
{{ $t('completeText') }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 蓝牙连接加载遮罩 -->
|
||
<view class="loading-overlay" v-if="tcflag">
|
||
<view class="loading-content" v-if="ver_dataflag == 2">
|
||
<view class="spinner"></view>
|
||
<text class="loading-text">{{ $t('bluetoothConnectingWait') }}</text>
|
||
</view>
|
||
<view class="error-content" v-if="ver_dataflag == 1">
|
||
<view class="error-title">
|
||
{{ $t('bluetoothConnectionFailed') }}
|
||
</view>
|
||
<view class="error-message">{{shibaitxt}}</view>
|
||
<view class="error-actions">
|
||
<view class="error-btn error-btn-cancel" @click="btnqx">
|
||
{{ $t('cancelConnection') }}
|
||
</view>
|
||
<view class="error-btn error-btn-retry" @click="getlj">
|
||
{{ $t('clickToRetry') }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- WiFi配置加载遮罩 -->
|
||
<view class="loading-overlay" v-if="wififlag">
|
||
<view class="loading-content">
|
||
<view class="spinner"></view>
|
||
<text class="loading-text">{{ $t('hubConfiguringWait') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import i18n from '@/common/i18n/index.js';
|
||
var xBlufi = require("@/common/blufi/xBlufi.js")
|
||
export default {
|
||
data() {
|
||
return {
|
||
deviceid: '',
|
||
type: '',
|
||
ver_dataflag: 1,
|
||
devicesarr: [],
|
||
deviceid: '',
|
||
devicename: '',
|
||
mac: '',
|
||
shibaitxt:'',
|
||
tcflag:false,
|
||
shibainum:0,
|
||
wifiname:'',
|
||
wifipwd:'',
|
||
wififlag:false
|
||
}
|
||
},
|
||
computed: {
|
||
$t() {
|
||
return i18n.t;
|
||
}
|
||
},
|
||
onLoad(option) {
|
||
console.log(option);
|
||
xBlufi.initXBlufi(1)
|
||
xBlufi.notifyStartDiscoverBle({
|
||
'isStart': true
|
||
})
|
||
xBlufi.listenDeviceMsgEvent(true, this.funListenDeviceMsgEvent)
|
||
if (option.mac) {
|
||
this.mac = option.mac
|
||
this.getlj()
|
||
}
|
||
},
|
||
methods: {
|
||
// 点击稍后再说
|
||
btnsh(){
|
||
uni.reLaunch({
|
||
url:'/pages/index/index'
|
||
})
|
||
},
|
||
// 点击取消连接
|
||
btnqx(){
|
||
this.ver_dataflag = 1
|
||
this.tcflag = false
|
||
},
|
||
// 点击返回上一级
|
||
onCancel() {
|
||
uni.navigateBack({
|
||
delta: 1
|
||
})
|
||
},
|
||
// 点击继续下一步
|
||
btnjx() {
|
||
if(this.ver_dataflag == 3){
|
||
if(this.wifiname == ''){
|
||
uni.showModal({
|
||
title: this.$t('warmPrompt'),
|
||
content: this.$t('wifiNameCannotBeEmpty'),
|
||
showCancel: false,
|
||
success: function(res) {
|
||
|
||
}
|
||
})
|
||
}else if(this.wifipwd == ''){
|
||
uni.showModal({
|
||
title: this.$t('warmPrompt'),
|
||
content: this.$t('wifiPasswordCannotBeEmpty'),
|
||
showCancel: false,
|
||
success: function(res) {
|
||
|
||
}
|
||
})
|
||
}else{
|
||
this.wififlag = true
|
||
xBlufi.notifySendCustomData({
|
||
customData: "ssid@" + this.wifiname + "pass@" + this.wifipwd
|
||
})
|
||
}
|
||
}else{
|
||
uni.showModal({
|
||
title: this.$t('warmPrompt'),
|
||
content: this.$t('pleaseConnectDeviceFirst'),
|
||
showCancel: false,
|
||
success: function(res) {
|
||
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 进行蓝牙连接
|
||
getlj() {
|
||
console.log('蓝牙连接开始');
|
||
let that = this
|
||
const findDevice = () => {
|
||
that.ver_dataflag = 2
|
||
that.tcflag = true
|
||
const matchedDevice = that.devicesarr.find(device => {
|
||
return device.name.slice(-12) == that.mac.slice(-12)
|
||
})
|
||
if (matchedDevice) {
|
||
xBlufi.notifyStartDiscoverBle({
|
||
'isStart': false
|
||
})
|
||
xBlufi.notifyConnectBle({
|
||
isStart: true,
|
||
deviceId: matchedDevice.deviceId,
|
||
name: matchedDevice.name
|
||
})
|
||
that.deviceid = matchedDevice.deviceId
|
||
that.devicename = matchedDevice.name
|
||
setTimeout(() => {
|
||
if (that.ver_dataflag == 3) {
|
||
that.tcflag = false
|
||
uni.showToast({ title: this.$t('connectionSuccess'), icon: 'success', duration: 3000})
|
||
} else {
|
||
that.ver_dataflag = 1
|
||
that.shibainum = 0
|
||
that.getztm()
|
||
}
|
||
}, 4000)
|
||
} else {
|
||
if (that.shibainum < 7) {
|
||
that.shibainum++
|
||
that.findDeviceTimer = setTimeout(findDevice.bind(that), 1000) // 使用 bind 保持 this 上下文
|
||
} else {
|
||
that.ver_dataflag = 1
|
||
that.shibainum = 0
|
||
that.getztm()
|
||
}
|
||
}
|
||
}
|
||
findDevice()
|
||
},
|
||
// 获取蓝牙失败状态码
|
||
getztm(){
|
||
let that = this
|
||
uni.openBluetoothAdapter({
|
||
success: function (res) {
|
||
console.log('蓝牙适配器正常,但未发现设备');
|
||
that.shibaitxt = that.$t('deviceNotFoundCheck')
|
||
},
|
||
fail: function (err) {
|
||
console.error('蓝牙适配器初始化失败', err)
|
||
console.log('错误码:', err.errCode, '错误信息:', err.errMsg)
|
||
const errMsg = err.errMsg || ''
|
||
if(errMsg.includes('auth deny') || errMsg.includes('authorize')){
|
||
that.shibaitxt = that.$t('bluetoothAuthDenied')
|
||
}else if(errMsg.includes('not available') || errMsg.includes('unavailable')){
|
||
that.shibaitxt = that.$t('bluetoothUnavailable')
|
||
}else if(errMsg.includes('open fail')){
|
||
that.shibaitxt = that.$t('bluetoothOpenFailed')
|
||
}else if(errMsg.includes('already opened')){
|
||
console.log('蓝牙适配器已打开,但未找到设备')
|
||
that.shibaitxt = that.$t('deviceNotFoundCheck')
|
||
}else if(errMsg.includes('system permission denied')){
|
||
that.shibaitxt = that.$t('systemBluetoothPermissionDenied')
|
||
}else if(errMsg.includes('bluetooth service unavailable')){
|
||
that.shibaitxt = that.$t('bluetoothServiceUnavailable')
|
||
}else{
|
||
that.shibaitxt = that.$t('bluetoothInitFailed') + (err.errMsg || '未知错误,请重试')
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 获取附近蓝牙设备列表
|
||
funListenDeviceMsgEvent: function(options) {
|
||
switch (options.type) {
|
||
case xBlufi.XBLUFI_TYPE.TYPE_STATUS_CONNECTED:
|
||
if (!options.result) {
|
||
this.ver_dataflag = 1
|
||
this.tcflag = true
|
||
console.log(this.ver_dataflag, '断开断开');
|
||
}
|
||
break;
|
||
case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS:
|
||
if (options.result) {
|
||
this.devicesarr = options.data
|
||
}
|
||
break;
|
||
case xBlufi.XBLUFI_TYPE.TYPE_CONNECTED:
|
||
console.log("连接回调:" + JSON.stringify(options))
|
||
if (options.result == true) {
|
||
setTimeout(() => {
|
||
this.ver_dataflag = 3
|
||
}, 2000)
|
||
xBlufi.notifyInitBleEsp32({
|
||
deviceId: this.deviceid
|
||
})
|
||
this.deviceid = options.data.deviceId
|
||
} else {
|
||
this.ver_dataflag = 1
|
||
}
|
||
break;
|
||
case xBlufi.XBLUFI_TYPE.TYPE_RECIEVE_CUSTON_DATA:
|
||
console.log("1收到设备发来的自定义数据结果:", options.data)
|
||
break;
|
||
case xBlufi.XBLUFI_TYPE.TYPE_CONNECT_ROUTER_RESULT:
|
||
uni.hideLoading();
|
||
if (!options.result) {
|
||
this.wififlag = false
|
||
let that = this;
|
||
uni.showModal({
|
||
title: this.$t('warmPrompt'),
|
||
content: this.$t('configFailedCheckWifi'),
|
||
showCancel: false,
|
||
success: function(res) {
|
||
|
||
}
|
||
})
|
||
} else {
|
||
if (options.data.progress == 100) {
|
||
this.wififlag = false
|
||
let that = this
|
||
uni.showModal({
|
||
title: this.$t('warmPrompt'),
|
||
content: this.$t('wifiConfigSuccess').replace('{wifiName}', that.wifiname),
|
||
showCancel: false,
|
||
success: function(res) {
|
||
uni.reLaunch({
|
||
url: '/pages/index/index'
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
break;
|
||
case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START:
|
||
if (!options.result) {
|
||
console.log('蓝牙未开启')
|
||
this.tcflag = true
|
||
this.getztm()
|
||
return
|
||
}
|
||
break;
|
||
}
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
// 颜色变量
|
||
@primary-color: #0F0F0F;
|
||
@success-color: #61E24F;
|
||
@warning-color: #FF4444;
|
||
@info-color: #2788F7;
|
||
@bg-color: #F3F5F6;
|
||
@text-primary: #333333;
|
||
@text-secondary: #727272;
|
||
@text-tertiary: #7F7F7F;
|
||
@border-color: #D8D8D8;
|
||
@border-light: #E5E5E5;
|
||
@white: #FFFFFF;
|
||
@shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
|
||
// 基础布局
|
||
.device-detail {
|
||
min-height: 100vh;
|
||
position: relative;
|
||
background-color: @bg-color;
|
||
}
|
||
|
||
.background {
|
||
width: 100%;
|
||
height: 50vh;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 0;
|
||
background: linear-gradient(180deg, @primary-color 0%, @primary-color 100%);
|
||
}
|
||
|
||
// 头部导航
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
height: 126rpx;
|
||
padding: 0 40rpx;
|
||
box-sizing: border-box;
|
||
background-color: @white;
|
||
margin-top: 102rpx;
|
||
border-radius: 30rpx 30rpx 0 0;
|
||
position: relative;
|
||
z-index: 1;
|
||
box-shadow: @shadow;
|
||
|
||
.back-btn {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
cursor: pointer;
|
||
transition: opacity 0.3s;
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
&:active {
|
||
opacity: 0.6;
|
||
}
|
||
}
|
||
|
||
.header-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
.header-placeholder {
|
||
width: 60rpx;
|
||
}
|
||
}
|
||
|
||
// 蓝牙连接状态
|
||
.bluetooth-status {
|
||
width: 100%;
|
||
background-color: @white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 30rpx 40rpx;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
z-index: 1;
|
||
|
||
.status-indicator {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
margin-right: 20rpx;
|
||
flex: 1;
|
||
|
||
.status-dot {
|
||
display: block;
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
border-radius: 50%;
|
||
margin-right: 20rpx;
|
||
flex-shrink: 0;
|
||
|
||
&.status-dot-disconnected {
|
||
background-color: @warning-color;
|
||
}
|
||
|
||
&.status-dot-connecting {
|
||
background-color: @info-color;
|
||
animation: pulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
&.status-dot-connected {
|
||
background-color: @success-color;
|
||
}
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
|
||
&.status-text-disconnected {
|
||
color: @warning-color;
|
||
}
|
||
|
||
&.status-text-connecting {
|
||
color: @info-color;
|
||
}
|
||
|
||
&.status-text-connected {
|
||
color: @success-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
.status-action {
|
||
font-size: 28rpx;
|
||
padding: 16rpx 32rpx;
|
||
border-radius: 12rpx;
|
||
transition: all 0.3s;
|
||
flex-shrink: 0;
|
||
cursor: pointer;
|
||
|
||
&.status-action-disconnected {
|
||
color: @warning-color;
|
||
border: 2rpx solid @warning-color;
|
||
background-color: transparent;
|
||
|
||
&:active {
|
||
background-color: rgba(255, 68, 68, 0.1);
|
||
}
|
||
}
|
||
|
||
&.status-action-connecting {
|
||
color: @info-color;
|
||
border: 2rpx solid @info-color;
|
||
background-color: rgba(39, 136, 247, 0.1);
|
||
}
|
||
|
||
&.status-action-connected {
|
||
color: @success-color;
|
||
border: 2rpx solid @success-color;
|
||
background-color: rgba(97, 226, 79, 0.1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 内容区域
|
||
.content-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: calc(100vh - 400rpx);
|
||
background-color: @bg-color;
|
||
padding-bottom: 200rpx;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.wifi-form {
|
||
width: 100%;
|
||
background: @white;
|
||
border-radius: 0;
|
||
margin-top: 34rpx;
|
||
box-shadow: @shadow;
|
||
|
||
.wifi-item {
|
||
padding: 40rpx 38rpx;
|
||
box-sizing: border-box;
|
||
|
||
&.wifi-item-border {
|
||
border-bottom: 2rpx solid @border-light;
|
||
}
|
||
|
||
.wifi-label {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 28rpx;
|
||
color: @text-tertiary;
|
||
margin-bottom: 24rpx;
|
||
|
||
.wifi-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
margin-right: 16rpx;
|
||
}
|
||
}
|
||
|
||
.wifi-input {
|
||
width: 100%;
|
||
font-size: 32rpx;
|
||
color: @text-primary;
|
||
border: none;
|
||
border-bottom: 2rpx solid @border-light;
|
||
padding: 16rpx 0;
|
||
background: transparent;
|
||
transition: border-color 0.3s;
|
||
|
||
&::placeholder {
|
||
color: #B9B9B9;
|
||
}
|
||
|
||
&:focus {
|
||
border-bottom-color: @primary-color;
|
||
outline: none;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.warning-text {
|
||
width: 100%;
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
color: @text-secondary;
|
||
margin-top: 68rpx;
|
||
padding: 0 70rpx;
|
||
box-sizing: border-box;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
// 底部按钮
|
||
.button-container {
|
||
width: 100%;
|
||
height: 126rpx;
|
||
padding: 24rpx 40rpx 40rpx;
|
||
box-sizing: border-box;
|
||
background: @white;
|
||
border-radius: 0;
|
||
position: fixed;
|
||
left: 0;
|
||
bottom: 0;
|
||
z-index: 10;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
|
||
.btn-secondary {
|
||
width: 200rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
border: 2rpx solid @primary-color;
|
||
background-color: transparent;
|
||
color: @primary-color;
|
||
font-size: 32rpx;
|
||
border-radius: 50rpx;
|
||
transition: all 0.3s;
|
||
cursor: pointer;
|
||
|
||
&:active {
|
||
background-color: rgba(15, 15, 15, 0.1);
|
||
}
|
||
}
|
||
|
||
.btn-primary {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
background: @primary-color;
|
||
color: @white;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
border-radius: 50rpx;
|
||
transition: all 0.3s;
|
||
cursor: pointer;
|
||
box-shadow: 0 4rpx 12rpx rgba(15, 15, 15, 0.2);
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 加载遮罩层
|
||
.loading-overlay {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 100%;
|
||
max-width: 750rpx;
|
||
min-height: 400rpx;
|
||
background: @white;
|
||
border-radius: 30rpx 30rpx 0 0;
|
||
z-index: 1000;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 100rpx 40rpx 60rpx;
|
||
box-sizing: border-box;
|
||
box-shadow: 0 -4rpx 30rpx rgba(0, 0, 0, 0.15);
|
||
animation: slideUp 0.3s ease-out;
|
||
|
||
.loading-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
width: 100%;
|
||
}
|
||
|
||
.error-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
width: 100%;
|
||
|
||
.error-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
margin-bottom: 40rpx;
|
||
color: @warning-color;
|
||
text-align: center;
|
||
}
|
||
|
||
.error-message {
|
||
font-size: 28rpx;
|
||
color: @text-secondary;
|
||
display: block;
|
||
width: 100%;
|
||
padding: 0 30rpx;
|
||
text-align: center;
|
||
line-height: 1.6;
|
||
margin-bottom: 60rpx;
|
||
}
|
||
|
||
.error-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
gap: 20rpx;
|
||
|
||
.error-btn {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
border-radius: 12rpx;
|
||
font-size: 30rpx;
|
||
transition: all 0.3s;
|
||
cursor: pointer;
|
||
|
||
&.error-btn-cancel {
|
||
border: 2rpx solid #8d8d8d;
|
||
color: @text-secondary;
|
||
background-color: transparent;
|
||
|
||
&:active {
|
||
background-color: rgba(0, 0, 0, 0.05);
|
||
}
|
||
}
|
||
|
||
&.error-btn-retry {
|
||
background-color: @primary-color;
|
||
color: @white;
|
||
border: 2rpx solid @primary-color;
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 加载动画
|
||
.spinner {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
border-radius: 50%;
|
||
border: 6rpx solid #E0E0E0;
|
||
border-top-color: @success-color;
|
||
animation: spin 1s linear infinite;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 28rpx;
|
||
color: @text-secondary;
|
||
text-align: center;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
// 动画
|
||
@keyframes spin {
|
||
0% {
|
||
transform: rotate(0deg);
|
||
}
|
||
100% {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
50% {
|
||
opacity: 0.6;
|
||
transform: scale(1.1);
|
||
}
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
transform: translateX(-50%) translateY(100%);
|
||
}
|
||
to {
|
||
transform: translateX(-50%) translateY(0);
|
||
}
|
||
}
|
||
</style> |