12 changed files with 1028 additions and 126 deletions
@ -0,0 +1,128 @@ |
|||||
|
<template> |
||||
|
<view |
||||
|
v-if="visible" |
||||
|
class="common-loading" |
||||
|
:class="{'common-loading--mask': mask}" |
||||
|
@touchmove.stop.prevent="noop" |
||||
|
@tap.stop="noop" |
||||
|
> |
||||
|
<view class="common-loading__box"> |
||||
|
<view class="common-loading__halo"></view> |
||||
|
<image class="common-loading__gif" src="/static/images/img/loading.gif" mode="aspectFit"></image> |
||||
|
<view v-if="title" class="common-loading__text">{{ title }}</view> |
||||
|
<view class="common-loading__hint">请稍候,正在为你处理</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { LOADING_HIDE_EVENT, LOADING_SHOW_EVENT, getLoadingState } from '@/utils/loading.js' |
||||
|
|
||||
|
export default { |
||||
|
name: 'CommonLoading', |
||||
|
data() { |
||||
|
return { |
||||
|
visible: false, |
||||
|
title: '加载中...', |
||||
|
mask: true |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
const state = getLoadingState() |
||||
|
this.visible = state.visible |
||||
|
this.title = state.title |
||||
|
this.mask = state.mask |
||||
|
uni.$on(LOADING_SHOW_EVENT, this.show) |
||||
|
uni.$on(LOADING_HIDE_EVENT, this.hide) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
uni.$off(LOADING_SHOW_EVENT, this.show) |
||||
|
uni.$off(LOADING_HIDE_EVENT, this.hide) |
||||
|
}, |
||||
|
methods: { |
||||
|
show(options = {}) { |
||||
|
this.visible = true |
||||
|
this.title = options.title || '加载中...' |
||||
|
this.mask = options.mask !== false |
||||
|
}, |
||||
|
hide() { |
||||
|
this.visible = false |
||||
|
}, |
||||
|
noop() {} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.common-loading { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
z-index: 99999; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
pointer-events: none; |
||||
|
background: rgba(13, 18, 34, 0.46); |
||||
|
backdrop-filter: blur(16rpx); |
||||
|
} |
||||
|
|
||||
|
.common-loading--mask { |
||||
|
pointer-events: auto; |
||||
|
} |
||||
|
|
||||
|
.common-loading__box { |
||||
|
position: relative; |
||||
|
min-width: 280rpx; |
||||
|
min-height: 242rpx; |
||||
|
overflow: hidden; |
||||
|
padding: 44rpx 44rpx 36rpx; |
||||
|
border-radius: 40rpx; |
||||
|
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(245,249,255,.96)); |
||||
|
border: 1rpx solid rgba(255,255,255,.86); |
||||
|
box-shadow: 0 28rpx 82rpx rgba(28, 35, 74, 0.28); |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.common-loading__halo { |
||||
|
position: absolute; |
||||
|
top: -82rpx; |
||||
|
right: -74rpx; |
||||
|
width: 220rpx; |
||||
|
height: 220rpx; |
||||
|
border-radius: 50%; |
||||
|
background: rgba(169, 255, 231, .72); |
||||
|
} |
||||
|
|
||||
|
.common-loading__gif { |
||||
|
position: relative; |
||||
|
width: 92rpx; |
||||
|
height: 92rpx; |
||||
|
} |
||||
|
|
||||
|
.common-loading__text { |
||||
|
position: relative; |
||||
|
margin-top: 18rpx; |
||||
|
max-width: 380rpx; |
||||
|
font-size: 28rpx; |
||||
|
line-height: 40rpx; |
||||
|
color: #11162a; |
||||
|
font-weight: 900; |
||||
|
white-space: pre-line; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.common-loading__hint { |
||||
|
position: relative; |
||||
|
margin-top: 10rpx; |
||||
|
color: rgba(17, 22, 42, .48); |
||||
|
font-size: 22rpx; |
||||
|
line-height: 32rpx; |
||||
|
text-align: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,211 @@ |
|||||
|
<template> |
||||
|
<view v-if="visible" class="auth-mask" @touchmove.stop.prevent="noop" @tap.stop="cancel"> |
||||
|
<view class="auth-card" @tap.stop> |
||||
|
<view class="auth-glow auth-glow-a"></view> |
||||
|
<view class="auth-glow auth-glow-b"></view> |
||||
|
<view class="auth-badge">{{ badge }}</view> |
||||
|
<view class="auth-title">{{ title }}</view> |
||||
|
<view class="auth-content">{{ content }}</view> |
||||
|
<view class="auth-steps" v-if="steps && steps.length"> |
||||
|
<view class="auth-step" v-for="(step, index) in steps" :key="step"> |
||||
|
<view class="step-dot">{{ index + 1 }}</view> |
||||
|
<view>{{ step }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="auth-actions"> |
||||
|
<view class="auth-btn ghost" @tap="cancel">{{ cancelText }}</view> |
||||
|
<view class="auth-btn primary" @tap="confirm">{{ confirmText }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'IeAuthDialog', |
||||
|
data() { |
||||
|
return { |
||||
|
visible: false, |
||||
|
badge: 'i/e 安全认证', |
||||
|
title: '', |
||||
|
content: '', |
||||
|
confirmText: '继续', |
||||
|
cancelText: '暂不继续', |
||||
|
steps: [], |
||||
|
resolver: null |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
uni.$on('ie-auth-dialog:show', this.show) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
uni.$off('ie-auth-dialog:show', this.show) |
||||
|
}, |
||||
|
methods: { |
||||
|
show(options = {}) { |
||||
|
if (typeof options.handled === 'function') { |
||||
|
options.handled() |
||||
|
} |
||||
|
this.badge = options.badge || 'i/e 安全认证' |
||||
|
this.title = options.title || '进阶实名认证' |
||||
|
this.content = options.content || '' |
||||
|
this.confirmText = options.confirmText || '继续' |
||||
|
this.cancelText = options.cancelText || '暂不继续' |
||||
|
this.steps = options.steps || [] |
||||
|
this.resolver = options.resolve || null |
||||
|
this.visible = true |
||||
|
}, |
||||
|
confirm() { |
||||
|
this.close(true) |
||||
|
}, |
||||
|
cancel() { |
||||
|
this.close(false) |
||||
|
}, |
||||
|
close(value) { |
||||
|
this.visible = false |
||||
|
if (this.resolver) { |
||||
|
this.resolver(value) |
||||
|
} |
||||
|
this.resolver = null |
||||
|
}, |
||||
|
noop() {} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.auth-mask { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
z-index: 99998; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 48rpx; |
||||
|
background: rgba(13, 18, 34, 0.48); |
||||
|
backdrop-filter: blur(18rpx); |
||||
|
} |
||||
|
|
||||
|
.auth-card { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
overflow: hidden; |
||||
|
padding: 46rpx 38rpx 34rpx; |
||||
|
border-radius: 46rpx; |
||||
|
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(245,249,255,.96)); |
||||
|
border: 1rpx solid rgba(255,255,255,.88); |
||||
|
box-shadow: 0 34rpx 90rpx rgba(28, 35, 74, .28); |
||||
|
} |
||||
|
|
||||
|
.auth-glow { |
||||
|
position: absolute; |
||||
|
border-radius: 999rpx; |
||||
|
filter: blur(4rpx); |
||||
|
opacity: .72; |
||||
|
} |
||||
|
|
||||
|
.auth-glow-a { |
||||
|
right: -80rpx; |
||||
|
top: -90rpx; |
||||
|
width: 230rpx; |
||||
|
height: 230rpx; |
||||
|
background: rgba(169,255,231,.78); |
||||
|
} |
||||
|
|
||||
|
.auth-glow-b { |
||||
|
left: -70rpx; |
||||
|
bottom: 120rpx; |
||||
|
width: 190rpx; |
||||
|
height: 190rpx; |
||||
|
background: rgba(255,126,179,.22); |
||||
|
} |
||||
|
|
||||
|
.auth-badge { |
||||
|
position: relative; |
||||
|
display: inline-flex; |
||||
|
padding: 10rpx 18rpx; |
||||
|
border-radius: 999rpx; |
||||
|
color: #6c55d8; |
||||
|
background: rgba(122,77,255,.09); |
||||
|
font-size: 22rpx; |
||||
|
font-weight: 800; |
||||
|
} |
||||
|
|
||||
|
.auth-title { |
||||
|
position: relative; |
||||
|
margin-top: 22rpx; |
||||
|
color: #11162a; |
||||
|
font-size: 42rpx; |
||||
|
line-height: 54rpx; |
||||
|
font-weight: 900; |
||||
|
} |
||||
|
|
||||
|
.auth-content { |
||||
|
position: relative; |
||||
|
margin-top: 16rpx; |
||||
|
color: rgba(17,22,42,.62); |
||||
|
font-size: 26rpx; |
||||
|
line-height: 42rpx; |
||||
|
} |
||||
|
|
||||
|
.auth-steps { |
||||
|
position: relative; |
||||
|
margin-top: 26rpx; |
||||
|
padding: 22rpx; |
||||
|
border-radius: 28rpx; |
||||
|
background: rgba(241,247,255,.82); |
||||
|
} |
||||
|
|
||||
|
.auth-step { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 16rpx; |
||||
|
min-height: 48rpx; |
||||
|
color: rgba(17,22,42,.72); |
||||
|
font-size: 24rpx; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
|
||||
|
.step-dot { |
||||
|
width: 34rpx; |
||||
|
height: 34rpx; |
||||
|
line-height: 34rpx; |
||||
|
text-align: center; |
||||
|
border-radius: 50%; |
||||
|
color: #11162a; |
||||
|
background: #a9ffe7; |
||||
|
font-size: 20rpx; |
||||
|
font-weight: 900; |
||||
|
} |
||||
|
|
||||
|
.auth-actions { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
gap: 18rpx; |
||||
|
margin-top: 34rpx; |
||||
|
} |
||||
|
|
||||
|
.auth-btn { |
||||
|
flex: 1; |
||||
|
height: 84rpx; |
||||
|
line-height: 84rpx; |
||||
|
text-align: center; |
||||
|
border-radius: 999rpx; |
||||
|
font-size: 27rpx; |
||||
|
font-weight: 900; |
||||
|
} |
||||
|
|
||||
|
.auth-btn.ghost { |
||||
|
color: rgba(17,22,42,.56); |
||||
|
background: rgba(17,22,42,.06); |
||||
|
} |
||||
|
|
||||
|
.auth-btn.primary { |
||||
|
color: #11162a; |
||||
|
background: linear-gradient(135deg, #a9ffe7, #ffcf7b); |
||||
|
box-shadow: 0 16rpx 32rpx rgba(76, 209, 184, .22); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,155 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view class="safe-tip" v-if="showTip">请勿发送涉黄、涉暴、反动、侮辱等内容,违规会被封禁</view> |
||||
|
<view class="bottom-actions"> |
||||
|
<view class="bottom-item" :class="{ active: active === 'index' }" @tap="goTab('index')"> |
||||
|
<text class="tab-icon">🪐</text> |
||||
|
<text>此刻</text> |
||||
|
</view> |
||||
|
<view class="bottom-item message-tab" :class="{ active: active === 'messages' }" @tap="goTab('messages')"> |
||||
|
<text class="tab-icon">💬</text> |
||||
|
<text>消息</text> |
||||
|
<text class="unread-badge" v-if="displayUnreadCount > 0">{{ unreadText }}</text> |
||||
|
</view> |
||||
|
<view class="bottom-item" :class="{ active: active === 'universe' }" @tap="goTab('universe')"> |
||||
|
<text class="tab-icon">🌙</text> |
||||
|
<text>我的</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
const TAB_PATHS = { |
||||
|
index: '/package1/ieBrowser/index', |
||||
|
messages: '/package1/ieBrowser/messages', |
||||
|
universe: '/package1/ieBrowser/universe' |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
name: 'IeBottomTab', |
||||
|
props: { |
||||
|
active: { |
||||
|
type: String, |
||||
|
default: 'index' |
||||
|
}, |
||||
|
unreadCount: { |
||||
|
type: Number, |
||||
|
default: 0 |
||||
|
}, |
||||
|
showTip: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
displayUnreadCount() { |
||||
|
return Number(this.unreadCount) || 0 |
||||
|
}, |
||||
|
unreadText() { |
||||
|
return this.displayUnreadCount > 99 ? '99+' : String(this.displayUnreadCount) |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
goTab(tab) { |
||||
|
if (tab === this.active) return |
||||
|
const path = TAB_PATHS[tab] |
||||
|
if (!path) return |
||||
|
uni.redirectTo({ |
||||
|
url: path + '?unreadCount=' + encodeURIComponent(String(this.displayUnreadCount)) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.safe-tip { |
||||
|
position: fixed; |
||||
|
left: 36rpx; |
||||
|
right: 36rpx; |
||||
|
bottom: 142rpx; |
||||
|
z-index: 10; |
||||
|
padding: 12rpx 18rpx; |
||||
|
border-radius: 22rpx; |
||||
|
text-align: center; |
||||
|
color: rgba(22, 27, 46, .5); |
||||
|
background: rgba(255, 255, 255, .74); |
||||
|
border: 1rpx solid rgba(255, 255, 255, .9); |
||||
|
backdrop-filter: blur(18rpx); |
||||
|
box-shadow: 0 14rpx 38rpx rgba(96, 112, 160, .1); |
||||
|
font-size: 20rpx; |
||||
|
line-height: 28rpx; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
|
||||
|
.bottom-actions { |
||||
|
position: fixed; |
||||
|
left: 30rpx; |
||||
|
right: 30rpx; |
||||
|
bottom: 30rpx; |
||||
|
z-index: 10; |
||||
|
display: flex; |
||||
|
padding: 12rpx; |
||||
|
border: 1rpx solid rgba(255, 255, 255, .92); |
||||
|
border-radius: 999rpx; |
||||
|
overflow: hidden; |
||||
|
background: rgba(255, 255, 255, .92); |
||||
|
backdrop-filter: blur(24rpx); |
||||
|
box-shadow: 0 24rpx 80rpx rgba(96, 112, 160, .18); |
||||
|
} |
||||
|
|
||||
|
.bottom-item { |
||||
|
position: relative; |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 80rpx; |
||||
|
border-radius: 999rpx; |
||||
|
color: rgba(22, 27, 46, .4); |
||||
|
font-size: 21rpx; |
||||
|
font-weight: 700; |
||||
|
transition: transform .16s ease, background .24s ease; |
||||
|
} |
||||
|
|
||||
|
.bottom-item:active { |
||||
|
transform: scale(.94); |
||||
|
} |
||||
|
|
||||
|
.bottom-item.active { |
||||
|
color: #161b2e; |
||||
|
background: linear-gradient(135deg, #eafff8, #a9ffe7); |
||||
|
box-shadow: 0 12rpx 30rpx rgba(169, 255, 231, .34), inset 0 1rpx 0 rgba(255, 255, 255, .95); |
||||
|
} |
||||
|
|
||||
|
.tab-icon { |
||||
|
margin-bottom: 4rpx; |
||||
|
font-size: 30rpx; |
||||
|
filter: grayscale(1); |
||||
|
opacity: .55; |
||||
|
} |
||||
|
|
||||
|
.bottom-item.active .tab-icon { |
||||
|
filter: none; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
.unread-badge { |
||||
|
position: absolute; |
||||
|
right: 16rpx; |
||||
|
top: 2rpx; |
||||
|
min-width: 30rpx; |
||||
|
height: 30rpx; |
||||
|
line-height: 30rpx; |
||||
|
padding: 0 8rpx; |
||||
|
border-radius: 999rpx; |
||||
|
text-align: center; |
||||
|
color: #fff; |
||||
|
background: linear-gradient(135deg, #ff8ba0, #e85d75); |
||||
|
box-shadow: 0 8rpx 20rpx rgba(232, 93, 117, .28); |
||||
|
font-size: 18rpx; |
||||
|
font-weight: 900; |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue