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