首页调整
This commit is contained in:
parent
b65017c7a2
commit
e19615a89d
|
|
@ -89,4 +89,13 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/*每个页面公共css */
|
/*每个页面公共css */
|
||||||
|
|
||||||
|
/* 隐藏底部tabBar */
|
||||||
|
uni-tabbar {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-tabbar {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
439
xinlidsj/components/LoginModal.vue
Normal file
439
xinlidsj/components/LoginModal.vue
Normal file
|
|
@ -0,0 +1,439 @@
|
||||||
|
<template>
|
||||||
|
<view v-if="visible" class="login-modal-overlay">
|
||||||
|
<view class="login-modal-mask" @tap="onMaskClick"></view>
|
||||||
|
<view class="login-modal-wrapper">
|
||||||
|
<view class="login-modal-content">
|
||||||
|
<view class="login-modal-header">
|
||||||
|
<text class="login-modal-title">请登录</text>
|
||||||
|
<text class="login-modal-close" @tap="onClose">×</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="login-modal-body">
|
||||||
|
<view class="login-form-item">
|
||||||
|
<text class="login-form-label">账号</text>
|
||||||
|
<input
|
||||||
|
class="login-form-input"
|
||||||
|
type="text"
|
||||||
|
v-model="username"
|
||||||
|
placeholder="请输入账号"
|
||||||
|
:focus="autoFocus"
|
||||||
|
:adjust-position="true"
|
||||||
|
confirm-type="next"
|
||||||
|
@focus="onUsernameFocus"
|
||||||
|
@blur="onUsernameBlur"
|
||||||
|
@confirm="focusPassword"
|
||||||
|
@tap.stop
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="login-form-item">
|
||||||
|
<text class="login-form-label">密码</text>
|
||||||
|
<input
|
||||||
|
class="login-form-input"
|
||||||
|
type="text"
|
||||||
|
v-model="password"
|
||||||
|
:password="true"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
:adjust-position="true"
|
||||||
|
:focus="passwordFocus"
|
||||||
|
confirm-type="done"
|
||||||
|
@focus="onPasswordFocus"
|
||||||
|
@blur="onPasswordBlur"
|
||||||
|
@confirm="onLogin"
|
||||||
|
@tap.stop
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 错误提示 -->
|
||||||
|
<view v-if="errorMsg" class="login-error-msg">
|
||||||
|
<text class="error-icon">⚠️</text>
|
||||||
|
<text class="error-text">{{ errorMsg }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 成功提示 -->
|
||||||
|
<view v-if="successMsg" class="login-success-msg">
|
||||||
|
<text class="success-icon">✓</text>
|
||||||
|
<text class="success-text">{{ successMsg }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="login-modal-btn"
|
||||||
|
type="primary"
|
||||||
|
:disabled="loading"
|
||||||
|
@tap.stop="onLogin"
|
||||||
|
>
|
||||||
|
{{ loading ? '登录中...' : '登录' }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- 底部提示 -->
|
||||||
|
<view class="login-modal-footer">
|
||||||
|
<text class="footer-text">首次登录请联系管理员获取账号</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { setToken } from '../utils/auth'
|
||||||
|
import { login } from '../api/system/login'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LoginModal',
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
closable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
loading: false,
|
||||||
|
autoFocus: false,
|
||||||
|
passwordFocus: false,
|
||||||
|
errorMsg: '',
|
||||||
|
successMsg: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show: {
|
||||||
|
handler(val) {
|
||||||
|
console.log('LoginModal show changed:', val)
|
||||||
|
this.visible = val
|
||||||
|
if (val) {
|
||||||
|
// 恢复上次登录的账号
|
||||||
|
this.username = uni.getStorageSync('XINLI_LAST_USERNAME') || ''
|
||||||
|
// 清空错误和成功信息
|
||||||
|
this.errorMsg = ''
|
||||||
|
this.successMsg = ''
|
||||||
|
// 延迟自动聚焦
|
||||||
|
this.$nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('Setting autoFocus to true')
|
||||||
|
this.autoFocus = true
|
||||||
|
}, 600)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.autoFocus = false
|
||||||
|
this.passwordFocus = false
|
||||||
|
this.errorMsg = ''
|
||||||
|
this.successMsg = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onUsernameFocus() {
|
||||||
|
console.log('Username input focused')
|
||||||
|
this.errorMsg = ''
|
||||||
|
this.successMsg = ''
|
||||||
|
},
|
||||||
|
onUsernameBlur() {
|
||||||
|
console.log('Username input blurred')
|
||||||
|
},
|
||||||
|
onPasswordFocus() {
|
||||||
|
console.log('Password input focused')
|
||||||
|
this.errorMsg = ''
|
||||||
|
this.successMsg = ''
|
||||||
|
},
|
||||||
|
onPasswordBlur() {
|
||||||
|
console.log('Password input blurred')
|
||||||
|
},
|
||||||
|
focusPassword() {
|
||||||
|
console.log('Focusing password field')
|
||||||
|
this.autoFocus = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.passwordFocus = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onMaskClick() {
|
||||||
|
console.log('Mask clicked, closable:', this.closable)
|
||||||
|
if (this.closable) {
|
||||||
|
this.onClose()
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先登录后才能使用',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
if (!this.closable) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先登录后才能使用',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
onLogin() {
|
||||||
|
console.log('Login button clicked')
|
||||||
|
if (this.loading) return
|
||||||
|
|
||||||
|
const username = (this.username || '').trim()
|
||||||
|
const password = (this.password || '').trim()
|
||||||
|
|
||||||
|
if (!username) {
|
||||||
|
this.errorMsg = '请输入账号'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
this.errorMsg = '请输入密码'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.errorMsg = ''
|
||||||
|
this.successMsg = ''
|
||||||
|
this.loading = true
|
||||||
|
uni.showLoading({ title: '登录中...' })
|
||||||
|
|
||||||
|
login({ username, password })
|
||||||
|
.then((res) => {
|
||||||
|
this.loading = false
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
const data = res && res.data ? res.data : null
|
||||||
|
if (!data || data.code !== 200) {
|
||||||
|
const errorMsg = (data && data.msg) ? data.msg : '登录失败'
|
||||||
|
this.errorMsg = errorMsg
|
||||||
|
uni.showToast({
|
||||||
|
title: errorMsg,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = data.token
|
||||||
|
if (!token) {
|
||||||
|
this.errorMsg = '登录失败:未返回token'
|
||||||
|
uni.showToast({
|
||||||
|
title: '登录失败:未返回token',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setToken(token)
|
||||||
|
uni.setStorageSync('XINLI_LAST_USERNAME', username)
|
||||||
|
|
||||||
|
// 显示成功提示
|
||||||
|
this.successMsg = '登录成功!正在进入系统...'
|
||||||
|
uni.showToast({
|
||||||
|
title: '登录成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 清空密码和错误信息
|
||||||
|
this.password = ''
|
||||||
|
this.errorMsg = ''
|
||||||
|
|
||||||
|
// 通知父组件登录成功
|
||||||
|
this.$emit('success')
|
||||||
|
|
||||||
|
// 延迟关闭弹窗并刷新页面
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$emit('close')
|
||||||
|
// 刷新当前页面
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
const currentPage = pages[pages.length - 1]
|
||||||
|
if (currentPage && currentPage.$vm && typeof currentPage.$vm.onShow === 'function') {
|
||||||
|
currentPage.$vm.onShow()
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.loading = false
|
||||||
|
uni.hideLoading()
|
||||||
|
const errorMsg = e && e.message ? e.message : '网络错误,请检查网络连接'
|
||||||
|
this.errorMsg = errorMsg
|
||||||
|
uni.showToast({
|
||||||
|
title: errorMsg,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 不使用scoped,确保样式优先级 */
|
||||||
|
.login-modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 9999999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-wrapper {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
width: 600rpx;
|
||||||
|
max-width: 90vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-content {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.3);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 32rpx 32rpx 24rpx;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-close {
|
||||||
|
font-size: 48rpx;
|
||||||
|
color: #8f959e;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-body {
|
||||||
|
padding: 32rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-item {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-input::placeholder {
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-error-msg {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16rpx 20rpx;
|
||||||
|
background: #fff2e8;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
border-left: 4rpx solid #ff7875;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #d4380d;
|
||||||
|
line-height: 1.5;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-success-msg {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16rpx 20rpx;
|
||||||
|
background: #f6ffed;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
border-left: 4rpx solid #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
color: #52c41a;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #389e0d;
|
||||||
|
line-height: 1.5;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-modal-footer {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #8f959e;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
<template>
|
|
||||||
<view class="tabbar" :class="{ h5: isH5 }" :style="safeAreaStyle">
|
|
||||||
<view class="tabbar-item" :class="{ active: active === 'pages/index/index' }" @tap="switchTab('pages/index/index')">
|
|
||||||
<view class="tabbar-icon">
|
|
||||||
<uni-icons type="home" size="20" :color="active === 'pages/index/index' ? selectedColor : color"></uni-icons>
|
|
||||||
</view>
|
|
||||||
<view class="tabbar-text">面板</view>
|
|
||||||
</view>
|
|
||||||
<view class="tabbar-item" :class="{ active: active === 'pages/message/notice' }" @tap="switchTab('pages/message/notice')">
|
|
||||||
<view class="tabbar-icon">
|
|
||||||
<uni-icons type="notification" size="20" :color="active === 'pages/message/notice' ? selectedColor : color"></uni-icons>
|
|
||||||
</view>
|
|
||||||
<view class="tabbar-text">通知</view>
|
|
||||||
</view>
|
|
||||||
<view class="tabbar-item" :class="{ active: active === 'pages/settings/index' }" @tap="switchTab('pages/settings/index')">
|
|
||||||
<view class="tabbar-icon">
|
|
||||||
<uni-icons type="person" size="20" :color="active === 'pages/settings/index' ? selectedColor : color"></uni-icons>
|
|
||||||
</view>
|
|
||||||
<view class="tabbar-text">我的</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import UniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
UniIcons
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
active: 'pages/index/index',
|
|
||||||
isH5: false,
|
|
||||||
color: '#646a73',
|
|
||||||
selectedColor: '#1677ff'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
safeAreaStyle() {
|
|
||||||
try {
|
|
||||||
const info = uni.getSystemInfoSync()
|
|
||||||
const bottom = info && info.safeAreaInsets ? info.safeAreaInsets.bottom : 0
|
|
||||||
return bottom ? `padding-bottom:${bottom}px;` : ''
|
|
||||||
} catch (e) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
try {
|
|
||||||
const info = uni.getSystemInfoSync()
|
|
||||||
this.isH5 = info && info.uniPlatform === 'web'
|
|
||||||
} catch (e) {
|
|
||||||
this.isH5 = false
|
|
||||||
}
|
|
||||||
if (this.isH5) {
|
|
||||||
this.color = 'rgba(201, 242, 255, 0.85)'
|
|
||||||
this.selectedColor = '#74d8ff'
|
|
||||||
}
|
|
||||||
this.updateActive()
|
|
||||||
},
|
|
||||||
onShow() {
|
|
||||||
this.updateActive()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
updateActive() {
|
|
||||||
try {
|
|
||||||
const pages = getCurrentPages()
|
|
||||||
const current = pages && pages.length ? pages[pages.length - 1] : null
|
|
||||||
let route = current && current.route ? current.route : ''
|
|
||||||
if (route && route[0] === '/') route = route.slice(1)
|
|
||||||
this.active = route || this.active
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
},
|
|
||||||
switchTab(url) {
|
|
||||||
this.updateActive()
|
|
||||||
if (this.active === url) return
|
|
||||||
this.active = url
|
|
||||||
uni.switchTab({ url: '/' + url })
|
|
||||||
setTimeout(() => {
|
|
||||||
this.updateActive()
|
|
||||||
}, 50)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.tabbar {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: 56px;
|
|
||||||
background: #ffffff;
|
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
box-sizing: content-box;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar.h5 {
|
|
||||||
background: linear-gradient(180deg, rgba(2, 8, 22, 0.92) 0%, rgba(2, 8, 22, 0.78) 100%);
|
|
||||||
border-top: 1px solid rgba(0, 188, 255, 0.22);
|
|
||||||
box-shadow: 0 -10px 18px rgba(0, 0, 0, 0.35), 0 0 22px rgba(0, 166, 255, 0.10);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar-item {
|
|
||||||
flex: 1;
|
|
||||||
height: 56px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: #646a73;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar.h5 .tabbar-item {
|
|
||||||
color: rgba(201, 242, 255, 0.85);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar-item.active {
|
|
||||||
color: #1677ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar.h5 .tabbar-item.active {
|
|
||||||
color: #74d8ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar-icon {
|
|
||||||
width: 34px;
|
|
||||||
height: 28px;
|
|
||||||
border-radius: 14px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: background-color 120ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar-item.active .tabbar-icon {
|
|
||||||
background: rgba(22, 119, 255, 0.14);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar.h5 .tabbar-item.active .tabbar-icon {
|
|
||||||
background: rgba(116, 216, 255, 0.14);
|
|
||||||
box-shadow: 0 0 14px rgba(116, 216, 255, 0.22);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar-item.active .tabbar-text {
|
|
||||||
color: #1677ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar.h5 .tabbar-item.active .tabbar-text {
|
|
||||||
color: #74d8ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbar-text {
|
|
||||||
margin-top: 2px;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
6
xinlidsj/package-lock.json
generated
Normal file
6
xinlidsj/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "xinlidsj",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
|
|
@ -190,29 +190,11 @@
|
||||||
"backgroundColor": "#050b18"
|
"backgroundColor": "#050b18"
|
||||||
},
|
},
|
||||||
"tabBar": {
|
"tabBar": {
|
||||||
"custom": true,
|
"custom": false,
|
||||||
"color": "#646a73",
|
|
||||||
"selectedColor": "#1677ff",
|
|
||||||
"backgroundColor": "#ffffff",
|
|
||||||
"borderStyle": "black",
|
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"pagePath": "pages/index/index",
|
"pagePath": "pages/index/index",
|
||||||
"text": "面板",
|
"text": "首页"
|
||||||
"iconPath": "static/home.png",
|
|
||||||
"selectedIconPath": "static/home.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pagePath": "pages/message/notice",
|
|
||||||
"text": "通知",
|
|
||||||
"iconPath": "static/notice.png",
|
|
||||||
"selectedIconPath": "static/notice.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pagePath": "pages/settings/index",
|
|
||||||
"text": "我的",
|
|
||||||
"iconPath": "static/my_light.png",
|
|
||||||
"selectedIconPath": "static/my_light.png"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -982,9 +982,9 @@
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 6rpx;
|
top: 10rpx;
|
||||||
width: 6rpx;
|
width: 6rpx;
|
||||||
height: 24rpx;
|
height: 100rpx;
|
||||||
border-radius: 6rpx;
|
border-radius: 6rpx;
|
||||||
background: linear-gradient(180deg, rgba(116, 216, 255, 0.95) 0%, rgba(43, 107, 255, 0.85) 100%);
|
background: linear-gradient(180deg, rgba(116, 216, 255, 0.95) 0%, rgba(43, 107, 255, 0.85) 100%);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="page" v-if="!isH5">
|
<!-- 主内容区域 - 登录时禁用交互 -->
|
||||||
|
<view class="page" v-if="!isH5" :class="{ 'page-disabled': showLoginModal }">
|
||||||
<view class="section">
|
<view class="section">
|
||||||
<view class="section-title">核心功能</view>
|
<view class="section-title">核心功能</view>
|
||||||
<view class="grid">
|
<view class="grid">
|
||||||
|
|
@ -124,6 +125,9 @@
|
||||||
<view class="big-top-action-item" @tap="goInterventionTasks">
|
<view class="big-top-action-item" @tap="goInterventionTasks">
|
||||||
<image class="big-top-action-ico" src="/static/6.png" mode="aspectFit" />
|
<image class="big-top-action-ico" src="/static/6.png" mode="aspectFit" />
|
||||||
</view>
|
</view>
|
||||||
|
<view class="big-top-action-item" @tap="goNotice">
|
||||||
|
<image class="big-top-action-ico" src="/static/7.png" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
@ -231,6 +235,13 @@
|
||||||
<view :id="aiChatBottomId" class="big-ai-bottom"></view>
|
<view :id="aiChatBottomId" class="big-ai-bottom"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view class="big-ai-input">
|
<view class="big-ai-input">
|
||||||
|
<view
|
||||||
|
class="big-ai-voice-btn"
|
||||||
|
:class="{ active: voiceMode }"
|
||||||
|
@tap="toggleVoiceMode"
|
||||||
|
>
|
||||||
|
<uni-icons type="mic-filled" size="20" :color="voiceMode ? '#00f0ff' : '#64748B'"></uni-icons>
|
||||||
|
</view>
|
||||||
<input
|
<input
|
||||||
class="big-ai-text"
|
class="big-ai-text"
|
||||||
v-model="aiChatInput"
|
v-model="aiChatInput"
|
||||||
|
|
@ -307,6 +318,9 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 登录弹窗 - 放在最后确保在最上层 -->
|
||||||
|
<LoginModal :show="showLoginModal" :closable="false" @success="onLoginSuccess" @close="showLoginModal = false" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -315,21 +329,30 @@
|
||||||
import { getUnreadMessageList, markMessageRead } from '../../api/app/message'
|
import { getUnreadMessageList, markMessageRead } from '../../api/app/message'
|
||||||
import { openLink, getMessageWsUrl } from '../../utils/link'
|
import { openLink, getMessageWsUrl } from '../../utils/link'
|
||||||
import { request } from '../../utils/request'
|
import { request } from '../../utils/request'
|
||||||
|
import { getBaseUrl } from '../../utils/config'
|
||||||
import { getReport, listReport } from '../../api/psychology/report'
|
import { getReport, listReport } from '../../api/psychology/report'
|
||||||
import { getStudentOptions, getUserAssessmentSummary } from '../../api/psychology/assessment'
|
import { getStudentOptions, getUserAssessmentSummary } from '../../api/psychology/assessment'
|
||||||
import QiunDataCharts from '../../uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue'
|
import QiunDataCharts from '../../uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue'
|
||||||
import UniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue'
|
import UniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue'
|
||||||
|
import LoginModal from '../../components/LoginModal.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
QiunDataCharts,
|
QiunDataCharts,
|
||||||
UniIcons
|
UniIcons,
|
||||||
|
LoginModal
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
showLoginModal: false,
|
||||||
socketOpen: false,
|
socketOpen: false,
|
||||||
connecting: false,
|
connecting: false,
|
||||||
voiceTipsOpen: false,
|
voiceTipsOpen: false,
|
||||||
|
voiceMode: false,
|
||||||
|
recorder: null,
|
||||||
|
mediaRecorder: null,
|
||||||
|
mediaStream: null,
|
||||||
|
audioChunks: [],
|
||||||
isH5: false,
|
isH5: false,
|
||||||
centerVideoUrl: '',
|
centerVideoUrl: '',
|
||||||
aiChatOpen: true,
|
aiChatOpen: true,
|
||||||
|
|
@ -527,11 +550,32 @@
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.isH5 = false
|
this.isH5 = false
|
||||||
}
|
}
|
||||||
|
// 检查登录状态
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) {
|
||||||
|
this.showLoginModal = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.initApp()
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
// 检查登录状态
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) {
|
||||||
|
this.showLoginModal = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.checkUnreadNoticePopup()
|
||||||
|
this.checkUnreadMessagePopup()
|
||||||
|
if (this.isH5) {
|
||||||
|
this.fetchInboxList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initApp() {
|
||||||
if (this.isH5) {
|
if (this.isH5) {
|
||||||
this.fetchCenterVideo()
|
this.fetchCenterVideo()
|
||||||
}
|
}
|
||||||
const token = getToken()
|
|
||||||
if (!token) return
|
|
||||||
this.initMessageChannel()
|
this.initMessageChannel()
|
||||||
this.checkUnreadNoticePopup()
|
this.checkUnreadNoticePopup()
|
||||||
this.checkUnreadMessagePopup()
|
this.checkUnreadMessagePopup()
|
||||||
|
|
@ -540,16 +584,10 @@
|
||||||
this.fetchInboxList()
|
this.fetchInboxList()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onShow() {
|
onLoginSuccess() {
|
||||||
const token = getToken()
|
// 登录成功后初始化应用
|
||||||
if (!token) return
|
this.initApp()
|
||||||
this.checkUnreadNoticePopup()
|
|
||||||
this.checkUnreadMessagePopup()
|
|
||||||
if (this.isH5) {
|
|
||||||
this.fetchInboxList()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
getBailianConfig() {
|
getBailianConfig() {
|
||||||
const HARDCODED_BAILIAN_API_KEY = 'sk-f991fd13fb044abebeaea81b9848c22b'
|
const HARDCODED_BAILIAN_API_KEY = 'sk-f991fd13fb044abebeaea81b9848c22b'
|
||||||
let env = null
|
let env = null
|
||||||
|
|
@ -716,6 +754,255 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toggleVoiceMode() {
|
||||||
|
this.voiceMode = !this.voiceMode
|
||||||
|
if (this.voiceMode) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '语音对话已开启',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
this.startVoiceRecognition()
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '语音对话已关闭',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
this.stopVoiceRecognition()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startVoiceRecognition() {
|
||||||
|
// H5环境下使用浏览器原生录音API
|
||||||
|
// #ifdef H5
|
||||||
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '浏览器不支持录音功能',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
this.voiceMode = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.mediaDevices.getUserMedia({ audio: true })
|
||||||
|
.then(stream => {
|
||||||
|
console.log('获取麦克风权限成功')
|
||||||
|
this.mediaStream = stream
|
||||||
|
this.mediaRecorder = new MediaRecorder(stream)
|
||||||
|
this.audioChunks = []
|
||||||
|
|
||||||
|
this.mediaRecorder.ondataavailable = (event) => {
|
||||||
|
if (event.data.size > 0) {
|
||||||
|
this.audioChunks.push(event.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mediaRecorder.onstop = () => {
|
||||||
|
console.log('录音停止')
|
||||||
|
const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' })
|
||||||
|
this.uploadAudioBlob(audioBlob)
|
||||||
|
|
||||||
|
// 停止所有音轨
|
||||||
|
if (this.mediaStream) {
|
||||||
|
this.mediaStream.getTracks().forEach(track => track.stop())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mediaRecorder.start()
|
||||||
|
console.log('开始录音...')
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('获取麦克风权限失败:', err)
|
||||||
|
uni.showToast({
|
||||||
|
title: '无法访问麦克风,请检查权限',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
this.voiceMode = false
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef H5
|
||||||
|
// 检查是否支持录音
|
||||||
|
if (typeof uni.getRecorderManager !== 'function') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '当前环境不支持录音功能',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
this.voiceMode = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.recorder) {
|
||||||
|
this.recorder = uni.getRecorderManager()
|
||||||
|
this.recorder.onStop((res) => {
|
||||||
|
console.log('录音停止', res)
|
||||||
|
this.onVoiceRecorderStop(res)
|
||||||
|
})
|
||||||
|
this.recorder.onError((err) => {
|
||||||
|
console.error('录音错误', err)
|
||||||
|
this.voiceMode = false
|
||||||
|
const msg = (err && err.errMsg) ? err.errMsg : '录音失败'
|
||||||
|
uni.showToast({
|
||||||
|
title: msg,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.recorder.onStart(() => {
|
||||||
|
console.log('录音开始')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('开始录音...')
|
||||||
|
this.recorder.start({
|
||||||
|
duration: 60000,
|
||||||
|
sampleRate: 16000,
|
||||||
|
numberOfChannels: 1,
|
||||||
|
encodeBitRate: 96000,
|
||||||
|
format: 'mp3'
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
stopVoiceRecognition() {
|
||||||
|
// #ifdef H5
|
||||||
|
if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {
|
||||||
|
this.mediaRecorder.stop()
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef H5
|
||||||
|
if (this.recorder && this.voiceMode) {
|
||||||
|
this.recorder.stop()
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
onVoiceRecorderStop(res) {
|
||||||
|
if (!res || !res.tempFilePath) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '未获取到录音文件',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.uploadAndAsrForAiChat(res.tempFilePath)
|
||||||
|
},
|
||||||
|
uploadAndAsrForAiChat(tempFilePath) {
|
||||||
|
console.log('开始上传音频文件:', tempFilePath)
|
||||||
|
uni.showLoading({ title: '识别中...' })
|
||||||
|
|
||||||
|
const baseUrl = getBaseUrl()
|
||||||
|
console.log('上传地址:', baseUrl + '/voice/asr')
|
||||||
|
|
||||||
|
uni.uploadFile({
|
||||||
|
url: baseUrl + '/voice/asr',
|
||||||
|
filePath: tempFilePath,
|
||||||
|
name: 'file',
|
||||||
|
formData: { language: 'zh' },
|
||||||
|
header: {
|
||||||
|
'Authorization': getToken() || ''
|
||||||
|
},
|
||||||
|
success: (res) => {
|
||||||
|
console.log('上传成功,响应:', res)
|
||||||
|
uni.hideLoading()
|
||||||
|
let data = null
|
||||||
|
try {
|
||||||
|
data = JSON.parse(res.data)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析响应失败:', e)
|
||||||
|
uni.showToast({
|
||||||
|
title: '服务返回格式错误',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('解析后的数据:', data)
|
||||||
|
|
||||||
|
if (!data || data.code !== 200) {
|
||||||
|
uni.showToast({
|
||||||
|
title: (data && data.msg) ? data.msg : '识别失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const recognizedText = data.text || ''
|
||||||
|
if (!recognizedText) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '未识别到有效文本',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('识别文本:', recognizedText)
|
||||||
|
// 将识别的文本设置到输入框并自动发送
|
||||||
|
this.aiChatInput = recognizedText
|
||||||
|
this.sendAiChat()
|
||||||
|
},
|
||||||
|
fail: (e) => {
|
||||||
|
console.error('上传失败:', e)
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: e && e.errMsg ? e.errMsg : '上传失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
uploadAudioBlob(audioBlob) {
|
||||||
|
console.log('开始上传音频Blob:', audioBlob)
|
||||||
|
uni.showLoading({ title: '识别中...' })
|
||||||
|
|
||||||
|
const baseUrl = getBaseUrl()
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', audioBlob, 'audio.webm')
|
||||||
|
formData.append('language', 'zh')
|
||||||
|
|
||||||
|
fetch(baseUrl + '/voice/asr', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': getToken() || ''
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log('上传成功,响应:', data)
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (!data || data.code !== 200) {
|
||||||
|
uni.showToast({
|
||||||
|
title: (data && data.msg) ? data.msg : '识别失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const recognizedText = data.text || ''
|
||||||
|
if (!recognizedText) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '未识别到有效文本',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('识别文本:', recognizedText)
|
||||||
|
this.aiChatInput = recognizedText
|
||||||
|
this.sendAiChat()
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('上传失败:', err)
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '上传失败: ' + err.message,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
scrollAiChatToBottom() {
|
scrollAiChatToBottom() {
|
||||||
// scroll-into-view 需要一个变化的值触发
|
// scroll-into-view 需要一个变化的值触发
|
||||||
this.aiChatScrollInto = this.aiChatBottomId + '-' + Date.now()
|
this.aiChatScrollInto = this.aiChatBottomId + '-' + Date.now()
|
||||||
|
|
@ -1627,8 +1914,13 @@
|
||||||
url: '/pages/dashboard/index'
|
url: '/pages/dashboard/index'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
goNotice() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/message/notice'
|
||||||
|
})
|
||||||
|
},
|
||||||
goSettings() {
|
goSettings() {
|
||||||
uni.switchTab({
|
uni.navigateTo({
|
||||||
url: '/pages/settings/index'
|
url: '/pages/settings/index'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -1637,20 +1929,41 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* 自适应字体大小变量 - 基于视口宽度 */
|
||||||
|
:root {
|
||||||
|
--font-xs: calc(22rpx + 0.3vw); /* 小字体 */
|
||||||
|
--font-sm: calc(24rpx + 0.35vw); /* 小字体 */
|
||||||
|
--font-base: calc(26rpx + 0.4vw); /* 基础字体 */
|
||||||
|
--font-md: calc(28rpx + 0.45vw); /* 中等字体 */
|
||||||
|
--font-lg: calc(32rpx + 0.6vw); /* 大字体 */
|
||||||
|
--font-xl: calc(38rpx + 0.75vw); /* 超大字体 */
|
||||||
|
--font-2xl: calc(56rpx + 1.2vw); /* 特大字体 */
|
||||||
|
--font-3xl: calc(60rpx + 1.5vw); /* 巨大字体 */
|
||||||
|
}
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 24rpx 24rpx 120rpx;
|
padding: 24rpx 24rpx 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #F4F6FB;
|
background: #F4F6FB;
|
||||||
--c-primary: #1677ff;
|
--c-primary: #1677ff;
|
||||||
--c-danger: #E87A7A;
|
--c-danger: #E87A7A;
|
||||||
|
/* 页面级字体变量 */
|
||||||
|
--font-xs: calc(22rpx + 0.3vw);
|
||||||
|
--font-sm: calc(24rpx + 0.35vw);
|
||||||
|
--font-base: calc(26rpx + 0.4vw);
|
||||||
|
--font-md: calc(28rpx + 0.45vw);
|
||||||
|
--font-lg: calc(32rpx + 0.6vw);
|
||||||
|
--font-xl: calc(38rpx + 0.75vw);
|
||||||
|
--font-2xl: calc(56rpx + 1.2vw);
|
||||||
|
--font-3xl: calc(60rpx + 1.5vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
margin-top: 14rpx;
|
margin-top: 14rpx;
|
||||||
}
|
}
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 28rpx;
|
font-size: var(--font-md);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
color: #111827;
|
||||||
margin: 10rpx 6rpx 14rpx;
|
margin: 10rpx 6rpx 14rpx;
|
||||||
|
|
@ -1739,8 +2052,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-icon {
|
.card-icon {
|
||||||
width: 64rpx;
|
width: calc(64rpx + 1.5vh);
|
||||||
height: 64rpx;
|
height: calc(64rpx + 1.5vh);
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -1777,14 +2090,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 32rpx;
|
font-size: var(--font-lg);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-desc {
|
.card-desc {
|
||||||
margin-top: 10rpx;
|
margin-top: 10rpx;
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
color: #6B7280;
|
color: #6B7280;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1802,7 +2115,7 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.fold-title {
|
.fold-title {
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #334155;
|
color: #334155;
|
||||||
}
|
}
|
||||||
|
|
@ -1810,7 +2123,7 @@
|
||||||
padding: 0 14rpx 14rpx;
|
padding: 0 14rpx 14rpx;
|
||||||
}
|
}
|
||||||
.fold-item {
|
.fold-item {
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
color: #475569;
|
color: #475569;
|
||||||
line-height: 40rpx;
|
line-height: 40rpx;
|
||||||
}
|
}
|
||||||
|
|
@ -1838,7 +2151,7 @@
|
||||||
transform: scale(0.98);
|
transform: scale(0.98);
|
||||||
}
|
}
|
||||||
.mini-text {
|
.mini-text {
|
||||||
font-size: 22rpx;
|
font-size: var(--font-xs);
|
||||||
color: #334155;
|
color: #334155;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1847,8 +2160,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.page.big {
|
.page.big {
|
||||||
min-height: 100vh;
|
height: 100vh;
|
||||||
padding: 18rpx 18rpx 24rpx;
|
padding: 12rpx 12rpx 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
@ -1859,14 +2172,14 @@
|
||||||
--glass-2: rgba(10, 18, 38, 0.52);
|
--glass-2: rgba(10, 18, 38, 0.52);
|
||||||
background: #020610;
|
background: #020610;
|
||||||
color: rgba(242, 252, 255, 0.96);
|
color: rgba(242, 252, 255, 0.96);
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
text-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.55);
|
text-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.55);
|
||||||
}
|
}
|
||||||
.big-center-media {
|
.big-center-media {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(74vh - 360rpx);
|
height: calc(74vh - 360rpx);
|
||||||
max-height: 720rpx;
|
max-height: calc(720rpx + 7vh);
|
||||||
border-radius: 18rpx;
|
border-radius: 18rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
@ -1898,7 +2211,7 @@
|
||||||
padding: 16rpx 18rpx;
|
padding: 16rpx 18rpx;
|
||||||
}
|
}
|
||||||
.big-center-text-title {
|
.big-center-text-title {
|
||||||
font-size: 26rpx;
|
font-size: var(--font-base);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(220, 250, 255, 0.94);
|
color: rgba(220, 250, 255, 0.94);
|
||||||
letter-spacing: 1rpx;
|
letter-spacing: 1rpx;
|
||||||
|
|
@ -1906,16 +2219,16 @@
|
||||||
}
|
}
|
||||||
.big-center-text-desc {
|
.big-center-text-desc {
|
||||||
margin-top: 10rpx;
|
margin-top: 10rpx;
|
||||||
font-size: 22rpx;
|
font-size: var(--font-xs);
|
||||||
line-height: 32rpx;
|
line-height: 32rpx;
|
||||||
color: rgba(201, 242, 255, 0.72);
|
color: rgba(201, 242, 255, 0.72);
|
||||||
}
|
}
|
||||||
.big-panel-inbox {
|
.big-panel-inbox {
|
||||||
margin-top: 16rpx;
|
margin-top: 16rpx;
|
||||||
min-height: 320rpx;
|
min-height: calc(320rpx + 2vh);
|
||||||
}
|
}
|
||||||
.big-panel-portrait .big-chart {
|
.big-panel-portrait .big-chart {
|
||||||
height: 420rpx;
|
height: calc(420rpx + 3vh);
|
||||||
}
|
}
|
||||||
.big-inbox-list {
|
.big-inbox-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -1930,16 +2243,16 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.big-inbox-title {
|
.big-inbox-title {
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.92);
|
color: rgba(242, 252, 255, 0.92);
|
||||||
}
|
}
|
||||||
.big-inbox-desc {
|
.big-inbox-desc {
|
||||||
margin-top: 8rpx;
|
margin-top: 8rpx;
|
||||||
font-size: 22rpx;
|
font-size: var(--font-xs);
|
||||||
color: rgba(242, 252, 255, 0.84);
|
color: rgba(242, 252, 255, 0.84);
|
||||||
line-height: 28rpx;
|
line-height: 28rpx;
|
||||||
max-height: 56rpx;
|
max-height: calc(56rpx + 0.5vh);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.page.big:before {
|
.page.big:before {
|
||||||
|
|
@ -1996,11 +2309,11 @@
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 60rpx;
|
font-size: var(--font-3xl);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
letter-spacing: 2rpx;
|
letter-spacing: 2rpx;
|
||||||
color: rgba(242, 252, 255, 0.98);
|
color: rgba(242, 252, 255, 0.98);
|
||||||
min-height: 120rpx;
|
min-height: calc(120rpx + 1.2vh);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
@ -2025,9 +2338,10 @@
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
padding: 0 40rpx;
|
padding: 0 40rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-size: calc(48rpx + 0.9vw);
|
||||||
}
|
}
|
||||||
.big-title {
|
.big-title {
|
||||||
min-height: 120rpx;
|
min-height: calc(120rpx + 1.2vh);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2048,12 +2362,12 @@
|
||||||
}
|
}
|
||||||
.big-top-action-row {
|
.big-top-action-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||||
gap: 12rpx;
|
gap: 12rpx;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.big-top-action-item {
|
.big-top-action-item {
|
||||||
height: 140rpx;
|
height: calc(140rpx + 1.5vh);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -2063,14 +2377,14 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.big-top-action-ico {
|
.big-top-action-ico {
|
||||||
width: 124rpx;
|
width: calc(124rpx + 1.2vh);
|
||||||
height: 124rpx;
|
height: calc(124rpx + 1.2vh);
|
||||||
filter: drop-shadow(0 0 14rpx rgba(0, 240, 255, 0.26)) drop-shadow(0 0 26rpx rgba(60, 140, 255, 0.18));
|
filter: drop-shadow(0 0 14rpx rgba(0, 240, 255, 0.26)) drop-shadow(0 0 26rpx rgba(60, 140, 255, 0.18));
|
||||||
}
|
}
|
||||||
.big-grid {
|
.big-grid {
|
||||||
margin-top: 14rpx;
|
margin-top: 14rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 22rpx;
|
gap: 12rpx;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|
@ -2078,14 +2392,14 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.big-col {
|
.big-col {
|
||||||
width: 28%;
|
width: 27%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
}
|
}
|
||||||
.big-center {
|
.big-center {
|
||||||
flex: 0 0 44%;
|
flex: 0 0 46%;
|
||||||
width: 44%;
|
width: 46%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -2094,8 +2408,8 @@
|
||||||
.big-panel {
|
.big-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 18rpx;
|
border-radius: 18rpx;
|
||||||
padding: 26rpx 26rpx 22rpx;
|
padding: 18rpx 18rpx 16rpx;
|
||||||
min-height: 320rpx;
|
min-height: calc(320rpx + 2vh);
|
||||||
border: 1px solid rgba(0, 240, 255, 0.16);
|
border: 1px solid rgba(0, 240, 255, 0.16);
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(135deg, rgba(0, 240, 255, 0.08) 0%, rgba(60, 140, 255, 0.05) 35%, rgba(165, 90, 255, 0.06) 100%),
|
linear-gradient(135deg, rgba(0, 240, 255, 0.08) 0%, rgba(60, 140, 255, 0.05) 35%, rgba(165, 90, 255, 0.06) 100%),
|
||||||
|
|
@ -2107,18 +2421,18 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.big-panel-core {
|
.big-panel-core {
|
||||||
min-height: 460rpx;
|
min-height: calc(460rpx + 3vh);
|
||||||
}
|
}
|
||||||
.big-panel-core .big-ring {
|
.big-panel-core .big-ring {
|
||||||
height: 440rpx;
|
height: calc(440rpx + 3vh);
|
||||||
}
|
}
|
||||||
.big-panel-core .big-ring:before {
|
.big-panel-core .big-ring:before {
|
||||||
width: 440rpx;
|
width: calc(440rpx + 3vh);
|
||||||
height: 440rpx;
|
height: calc(440rpx + 3vh);
|
||||||
}
|
}
|
||||||
.big-panel-core .big-ring-center {
|
.big-panel-core .big-ring-center {
|
||||||
width: 250rpx;
|
width: calc(250rpx + 2vh);
|
||||||
height: 250rpx;
|
height: calc(250rpx + 2vh);
|
||||||
}
|
}
|
||||||
.big-metrics {
|
.big-metrics {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -2134,7 +2448,7 @@
|
||||||
min-width: 340rpx;
|
min-width: 340rpx;
|
||||||
}
|
}
|
||||||
.big-metric-label {
|
.big-metric-label {
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: rgba(220, 250, 255, 0.78);
|
color: rgba(220, 250, 255, 0.78);
|
||||||
letter-spacing: 1rpx;
|
letter-spacing: 1rpx;
|
||||||
|
|
@ -2142,7 +2456,7 @@
|
||||||
}
|
}
|
||||||
.big-metric-value {
|
.big-metric-value {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 56rpx;
|
font-size: var(--font-2xl);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: rgba(242, 252, 255, 0.96);
|
color: rgba(242, 252, 255, 0.96);
|
||||||
|
|
@ -2218,7 +2532,7 @@
|
||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
}
|
}
|
||||||
.big-panel-title {
|
.big-panel-title {
|
||||||
font-size: 32rpx;
|
font-size: var(--font-lg);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.94);
|
color: rgba(242, 252, 255, 0.94);
|
||||||
padding-left: 14rpx;
|
padding-left: 14rpx;
|
||||||
|
|
@ -2259,8 +2573,9 @@
|
||||||
}
|
}
|
||||||
.big-panel-report .big-panel-title:before {
|
.big-panel-report .big-panel-title:before {
|
||||||
left: 10rpx;
|
left: 10rpx;
|
||||||
top: 10rpx;
|
top: 50%;
|
||||||
height: 22rpx;
|
transform: translateY(-50%);
|
||||||
|
height: calc(22rpx + 0.8vh);
|
||||||
background: linear-gradient(180deg, rgba(0, 240, 255, 0.95) 0%, rgba(39, 120, 255, 0.85) 55%, rgba(165, 90, 255, 0.85) 100%);
|
background: linear-gradient(180deg, rgba(0, 240, 255, 0.95) 0%, rgba(39, 120, 255, 0.85) 55%, rgba(165, 90, 255, 0.85) 100%);
|
||||||
box-shadow: 0 0 14rpx rgba(0, 240, 255, 0.18);
|
box-shadow: 0 0 14rpx rgba(0, 240, 255, 0.18);
|
||||||
}
|
}
|
||||||
|
|
@ -2282,14 +2597,15 @@
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 6rpx;
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
width: 6rpx;
|
width: 6rpx;
|
||||||
height: 24rpx;
|
height: calc(24rpx + 0.8vh);
|
||||||
border-radius: 6rpx;
|
border-radius: 6rpx;
|
||||||
background: linear-gradient(180deg, rgba(116, 216, 255, 0.95) 0%, rgba(43, 107, 255, 0.85) 100%);
|
background: linear-gradient(180deg, rgba(116, 216, 255, 0.95) 0%, rgba(43, 107, 255, 0.85) 100%);
|
||||||
}
|
}
|
||||||
.big-panel-sub {
|
.big-panel-sub {
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
color: rgba(242, 252, 255, 0.84);
|
color: rgba(242, 252, 255, 0.84);
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
@ -2307,7 +2623,7 @@
|
||||||
.big-panel-clear {
|
.big-panel-clear {
|
||||||
padding: 6rpx 12rpx;
|
padding: 6rpx 12rpx;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
font-size: 22rpx;
|
font-size: var(--font-xs);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.86);
|
color: rgba(242, 252, 255, 0.86);
|
||||||
border: 1px solid rgba(116, 216, 255, 0.20);
|
border: 1px solid rgba(116, 216, 255, 0.20);
|
||||||
|
|
@ -2331,7 +2647,7 @@
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.big-ai-messages {
|
.big-ai-messages {
|
||||||
height: 640rpx;
|
height: calc(640rpx + 4vh);
|
||||||
padding: 8rpx 10rpx;
|
padding: 8rpx 10rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 14rpx;
|
border-radius: 14rpx;
|
||||||
|
|
@ -2340,7 +2656,7 @@
|
||||||
}
|
}
|
||||||
.big-ai-empty {
|
.big-ai-empty {
|
||||||
padding: 16rpx 10rpx;
|
padding: 16rpx 10rpx;
|
||||||
font-size: 22rpx;
|
font-size: var(--font-xs);
|
||||||
color: rgba(242, 252, 255, 0.62);
|
color: rgba(242, 252, 255, 0.62);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
|
@ -2359,7 +2675,7 @@
|
||||||
max-width: 78%;
|
max-width: 78%;
|
||||||
padding: 12rpx 14rpx;
|
padding: 12rpx 14rpx;
|
||||||
border-radius: 14rpx;
|
border-radius: 14rpx;
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
line-height: 1.45;
|
line-height: 1.45;
|
||||||
color: rgba(242, 252, 255, 0.92);
|
color: rgba(242, 252, 255, 0.92);
|
||||||
border: 1px solid rgba(0, 240, 255, 0.16);
|
border: 1px solid rgba(0, 240, 255, 0.16);
|
||||||
|
|
@ -2380,25 +2696,41 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10rpx;
|
gap: 10rpx;
|
||||||
}
|
}
|
||||||
|
.big-ai-voice-btn {
|
||||||
|
width: calc(72rpx + 2vh);
|
||||||
|
height: calc(72rpx + 2vh);
|
||||||
|
border-radius: 14rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid rgba(116, 216, 255, 0.18);
|
||||||
|
background: rgba(7, 13, 28, 0.78);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.big-ai-voice-btn.active {
|
||||||
|
border-color: rgba(0, 240, 255, 0.6);
|
||||||
|
background: rgba(0, 240, 255, 0.12);
|
||||||
|
box-shadow: 0 0 20rpx rgba(0, 240, 255, 0.4);
|
||||||
|
}
|
||||||
.big-ai-text {
|
.big-ai-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 72rpx;
|
height: calc(72rpx + 2vh);
|
||||||
border-radius: 14rpx;
|
border-radius: 14rpx;
|
||||||
border: 1px solid rgba(116, 216, 255, 0.18);
|
border: 1px solid rgba(116, 216, 255, 0.18);
|
||||||
background: rgba(7, 13, 28, 0.78);
|
background: rgba(7, 13, 28, 0.78);
|
||||||
padding: 0 14rpx;
|
padding: 0 14rpx;
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
color: rgba(242, 252, 255, 0.92);
|
color: rgba(242, 252, 255, 0.92);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.big-ai-send {
|
.big-ai-send {
|
||||||
width: 120rpx;
|
width: 120rpx;
|
||||||
height: 72rpx;
|
height: calc(72rpx + 2vh);
|
||||||
border-radius: 14rpx;
|
border-radius: 14rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.92);
|
color: rgba(242, 252, 255, 0.92);
|
||||||
border: 1px solid rgba(0, 240, 255, 0.22);
|
border: 1px solid rgba(0, 240, 255, 0.22);
|
||||||
|
|
@ -2411,7 +2743,7 @@
|
||||||
.big-ring {
|
.big-ring {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
height: 360rpx;
|
height: calc(360rpx + 2.5vh);
|
||||||
}
|
}
|
||||||
.big-ring:before {
|
.big-ring:before {
|
||||||
content: '';
|
content: '';
|
||||||
|
|
@ -2419,8 +2751,8 @@
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 360rpx;
|
width: calc(360rpx + 2.5vh);
|
||||||
height: 360rpx;
|
height: calc(360rpx + 2.5vh);
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
|
@ -2441,8 +2773,8 @@
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 210rpx;
|
width: calc(210rpx + 5vh);
|
||||||
height: 210rpx;
|
height: calc(210rpx + 5vh);
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
background: rgba(7, 13, 28, 0.72);
|
background: rgba(7, 13, 28, 0.72);
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
|
|
@ -2454,12 +2786,12 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.big-ring-center-main {
|
.big-ring-center-main {
|
||||||
font-size: 34rpx;
|
font-size: var(--font-lg);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.96);
|
color: rgba(242, 252, 255, 0.96);
|
||||||
}
|
}
|
||||||
.big-ring-center-sub {
|
.big-ring-center-sub {
|
||||||
font-size: 22rpx;
|
font-size: var(--font-xs);
|
||||||
color: rgba(242, 252, 255, 0.84);
|
color: rgba(242, 252, 255, 0.84);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 26rpx;
|
line-height: 26rpx;
|
||||||
|
|
@ -2468,7 +2800,7 @@
|
||||||
.big-chart {
|
.big-chart {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
height: 340rpx;
|
height: calc(340rpx + 2.5vh);
|
||||||
}
|
}
|
||||||
.big-portrait-cloud {
|
.big-portrait-cloud {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -2510,7 +2842,7 @@
|
||||||
100% { transform: translate(-50%, -50%) translate(0, 0); }
|
100% { transform: translate(-50%, -50%) translate(0, 0); }
|
||||||
}
|
}
|
||||||
.big-chart-xl {
|
.big-chart-xl {
|
||||||
height: 400rpx;
|
height: calc(400rpx + 3vh);
|
||||||
}
|
}
|
||||||
.big-kpis {
|
.big-kpis {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -2533,19 +2865,19 @@
|
||||||
gap: 10rpx;
|
gap: 10rpx;
|
||||||
}
|
}
|
||||||
.big-kpi-ico {
|
.big-kpi-ico {
|
||||||
width: 34rpx;
|
width: calc(34rpx + 1vh);
|
||||||
height: 34rpx;
|
height: calc(34rpx + 1vh);
|
||||||
filter: drop-shadow(0 0 10rpx rgba(0, 200, 255, 0.22));
|
filter: drop-shadow(0 0 10rpx rgba(0, 200, 255, 0.22));
|
||||||
}
|
}
|
||||||
.big-kpi-num {
|
.big-kpi-num {
|
||||||
font-size: 38rpx;
|
font-size: var(--font-xl);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: #74d8ff;
|
color: #74d8ff;
|
||||||
text-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.65);
|
text-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.65);
|
||||||
}
|
}
|
||||||
.big-kpi-lab {
|
.big-kpi-lab {
|
||||||
margin-top: 6rpx;
|
margin-top: 6rpx;
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
color: rgba(242, 252, 255, 0.84);
|
color: rgba(242, 252, 255, 0.84);
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
@ -2563,12 +2895,12 @@
|
||||||
padding: 10rpx 2rpx 2rpx;
|
padding: 10rpx 2rpx 2rpx;
|
||||||
}
|
}
|
||||||
.big-panel-actions {
|
.big-panel-actions {
|
||||||
min-height: 380rpx;
|
min-height: calc(380rpx + 3vh);
|
||||||
}
|
}
|
||||||
.big-action-item {
|
.big-action-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
height: 150rpx;
|
height: calc(150rpx + 1.5vh);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -2584,13 +2916,13 @@
|
||||||
}
|
}
|
||||||
.big-action-item:active { transform: scale(0.98); }
|
.big-action-item:active { transform: scale(0.98); }
|
||||||
.big-action-ico {
|
.big-action-ico {
|
||||||
width: 68rpx;
|
width: calc(68rpx + 2vh);
|
||||||
height: 68rpx;
|
height: calc(68rpx + 2vh);
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
filter: drop-shadow(0 0 10rpx rgba(0, 200, 255, 0.22));
|
filter: drop-shadow(0 0 10rpx rgba(0, 200, 255, 0.22));
|
||||||
}
|
}
|
||||||
.big-action-text {
|
.big-action-text {
|
||||||
font-size: 26rpx;
|
font-size: var(--font-base);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.92);
|
color: rgba(242, 252, 255, 0.92);
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
|
|
@ -2613,8 +2945,8 @@
|
||||||
gap: 10rpx;
|
gap: 10rpx;
|
||||||
}
|
}
|
||||||
.big-tool-icon {
|
.big-tool-icon {
|
||||||
width: 50rpx;
|
width: calc(50rpx + 1.5vh);
|
||||||
height: 50rpx;
|
height: calc(50rpx + 1.5vh);
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
border: 1px solid rgba(116, 216, 255, 0.22);
|
border: 1px solid rgba(116, 216, 255, 0.22);
|
||||||
background: rgba(10, 18, 38, 0.55);
|
background: rgba(10, 18, 38, 0.55);
|
||||||
|
|
@ -2623,7 +2955,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.big-tool-text {
|
.big-tool-text {
|
||||||
font-size: 24rpx;
|
font-size: var(--font-sm);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.92);
|
color: rgba(242, 252, 255, 0.92);
|
||||||
}
|
}
|
||||||
|
|
@ -2642,8 +2974,8 @@
|
||||||
padding: 10rpx 0;
|
padding: 10rpx 0;
|
||||||
}
|
}
|
||||||
.big-hex {
|
.big-hex {
|
||||||
width: 96rpx;
|
width: calc(96rpx + 3vh);
|
||||||
height: 86rpx;
|
height: calc(86rpx + 2.7vh);
|
||||||
clip-path: polygon(25% 6.7%, 75% 6.7%, 100% 50%, 75% 93.3%, 25% 93.3%, 0% 50%);
|
clip-path: polygon(25% 6.7%, 75% 6.7%, 100% 50%, 75% 93.3%, 25% 93.3%, 0% 50%);
|
||||||
border: 1px solid rgba(116, 216, 255, 0.28);
|
border: 1px solid rgba(116, 216, 255, 0.28);
|
||||||
background: radial-gradient(circle at 50% 30%, rgba(116, 216, 255, 0.22) 0%, rgba(10, 18, 38, 0.55) 70%);
|
background: radial-gradient(circle at 50% 30%, rgba(116, 216, 255, 0.22) 0%, rgba(10, 18, 38, 0.55) 70%);
|
||||||
|
|
@ -2653,7 +2985,7 @@
|
||||||
box-shadow: 0 0 18rpx rgba(116, 216, 255, 0.18);
|
box-shadow: 0 0 18rpx rgba(116, 216, 255, 0.18);
|
||||||
}
|
}
|
||||||
.big-nav-text {
|
.big-nav-text {
|
||||||
font-size: 22rpx;
|
font-size: var(--font-xs);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: rgba(242, 252, 255, 0.86);
|
color: rgba(242, 252, 255, 0.86);
|
||||||
}
|
}
|
||||||
|
|
@ -2667,4 +2999,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 登录弹窗显示时禁用页面交互 */
|
||||||
|
.page-disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="page" :class="{ big: isH5 }">
|
<view class="page" :class="{ big: isH5 }">
|
||||||
<view v-if="isH5" class="big-top">
|
<view v-if="isH5" class="big-top">
|
||||||
|
<view class="big-back" @tap="goBack">
|
||||||
|
<uni-icons type="back" size="24" color="rgba(220, 250, 255, 0.95)"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="big-top-content">
|
||||||
<view class="big-title">标签筛选</view>
|
<view class="big-title">标签筛选</view>
|
||||||
<view class="big-sub">按标签快速定位重点人员</view>
|
<view class="big-sub">按标签快速定位重点人员</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="card">
|
<view class="card">
|
||||||
<view class="card-title">筛选条件</view>
|
<view class="card-title">筛选条件</view>
|
||||||
|
|
@ -81,6 +86,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
goBack() {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 1
|
||||||
|
})
|
||||||
|
},
|
||||||
fetchTagOptions() {
|
fetchTagOptions() {
|
||||||
this.tagLoading = true
|
this.tagLoading = true
|
||||||
const defaultOptions = [
|
const defaultOptions = [
|
||||||
|
|
@ -208,11 +218,36 @@
|
||||||
background: linear-gradient(90deg, rgba(2, 8, 22, 0.86) 0%, rgba(2, 8, 22, 0.40) 50%, rgba(2, 8, 22, 0.86) 100%);
|
background: linear-gradient(90deg, rgba(2, 8, 22, 0.86) 0%, rgba(2, 8, 22, 0.40) 50%, rgba(2, 8, 22, 0.86) 100%);
|
||||||
box-shadow: 0 10rpx 22rpx rgba(0, 0, 0, 0.35), 0 0 24rpx rgba(0, 166, 255, 0.14);
|
box-shadow: 0 10rpx 22rpx rgba(0, 0, 0, 0.35), 0 0 24rpx rgba(0, 166, 255, 0.14);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 6rpx;
|
gap: 6rpx;
|
||||||
margin-bottom: 18rpx;
|
margin-bottom: 18rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.page.big .big-back {
|
||||||
|
position: absolute;
|
||||||
|
left: 20rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: rgba(0, 188, 255, 0.12);
|
||||||
|
border: 1px solid rgba(0, 188, 255, 0.22);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.page.big .big-back:active {
|
||||||
|
background: rgba(0, 188, 255, 0.20);
|
||||||
|
}
|
||||||
|
.page.big .big-top-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
}
|
}
|
||||||
.page.big .big-title {
|
.page.big .big-title {
|
||||||
font-size: 34rpx;
|
font-size: 34rpx;
|
||||||
|
|
|
||||||
BIN
xinlidsj/static/7.png
Normal file
BIN
xinlidsj/static/7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
|
|
@ -11,3 +11,4 @@ export function setToken(token) {
|
||||||
export function clearToken() {
|
export function clearToken() {
|
||||||
uni.removeStorageSync(STORAGE_KEYS.token)
|
uni.removeStorageSync(STORAGE_KEYS.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
106
xinlidsj/登录弹窗实现说明.md
Normal file
106
xinlidsj/登录弹窗实现说明.md
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
# 登录弹窗实现说明
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
实现了用户访问系统时,如果未登录则自动弹出登录弹窗,要求用户登录后才能使用系统功能。
|
||||||
|
|
||||||
|
## 实现方式
|
||||||
|
|
||||||
|
### 1. 创建登录弹窗组件
|
||||||
|
**文件**: `xinlidsj/components/LoginModal.vue`
|
||||||
|
|
||||||
|
**功能特点**:
|
||||||
|
- 美观的弹窗UI设计
|
||||||
|
- 支持账号密码登录
|
||||||
|
- 登录状态加载提示
|
||||||
|
- 登录成功后自动关闭弹窗并刷新页面
|
||||||
|
- 可配置是否允许关闭(`closable`属性)
|
||||||
|
- 自动记住上次登录的账号
|
||||||
|
|
||||||
|
**使用方法**:
|
||||||
|
```vue
|
||||||
|
<LoginModal
|
||||||
|
:show="showLoginModal"
|
||||||
|
:closable="false"
|
||||||
|
@success="onLoginSuccess"
|
||||||
|
@close="showLoginModal = false"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Props**:
|
||||||
|
- `show`: Boolean - 是否显示弹窗
|
||||||
|
- `closable`: Boolean - 是否允许关闭弹窗(默认false,强制登录)
|
||||||
|
|
||||||
|
**Events**:
|
||||||
|
- `success`: 登录成功时触发
|
||||||
|
- `close`: 关闭弹窗时触发
|
||||||
|
|
||||||
|
### 2. 首页集成登录弹窗
|
||||||
|
**文件**: `xinlidsj/pages/index/index.vue`
|
||||||
|
|
||||||
|
**修改内容**:
|
||||||
|
1. 引入 `LoginModal` 组件
|
||||||
|
2. 添加 `showLoginModal` 数据属性
|
||||||
|
3. 在 `onLoad` 和 `onShow` 生命周期中检查登录状态
|
||||||
|
4. 未登录时显示登录弹窗
|
||||||
|
5. 登录成功后初始化应用数据
|
||||||
|
|
||||||
|
**核心逻辑**:
|
||||||
|
```javascript
|
||||||
|
onLoad() {
|
||||||
|
// 检查登录状态
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) {
|
||||||
|
this.showLoginModal = true // 显示登录弹窗
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.initApp() // 已登录,初始化应用
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 登录成功处理
|
||||||
|
登录成功后的流程:
|
||||||
|
1. 保存token到本地存储
|
||||||
|
2. 触发 `success` 事件
|
||||||
|
3. 父组件调用 `onLoginSuccess()` 方法
|
||||||
|
4. 初始化应用数据(加载视频、消息、数据等)
|
||||||
|
5. 自动关闭登录弹窗
|
||||||
|
6. 刷新当前页面
|
||||||
|
|
||||||
|
## 用户体验
|
||||||
|
|
||||||
|
### 未登录状态
|
||||||
|
- 访问首页时自动弹出登录弹窗
|
||||||
|
- 弹窗遮罩层阻止用户操作页面内容
|
||||||
|
- 不允许关闭弹窗(`closable=false`),必须登录
|
||||||
|
- 点击遮罩层会提示"请先登录后才能使用"
|
||||||
|
|
||||||
|
### 登录过程
|
||||||
|
- 输入账号密码
|
||||||
|
- 点击登录按钮或按回车键提交
|
||||||
|
- 显示"登录中..."加载状态
|
||||||
|
- 登录失败显示错误提示
|
||||||
|
- 登录成功显示成功提示并自动关闭弹窗
|
||||||
|
|
||||||
|
### 已登录状态
|
||||||
|
- 直接进入系统,不显示登录弹窗
|
||||||
|
- 正常使用所有功能
|
||||||
|
|
||||||
|
## 样式特点
|
||||||
|
- 现代化的弹窗设计
|
||||||
|
- 半透明黑色遮罩层
|
||||||
|
- 圆角卡片式弹窗
|
||||||
|
- 输入框聚焦时高亮效果
|
||||||
|
- 响应式布局,适配不同屏幕尺寸
|
||||||
|
|
||||||
|
## 扩展性
|
||||||
|
如果需要在其他页面也使用登录弹窗,只需:
|
||||||
|
1. 引入 `LoginModal` 组件
|
||||||
|
2. 添加 `showLoginModal` 数据属性
|
||||||
|
3. 在页面加载时检查登录状态
|
||||||
|
4. 根据需要设置 `closable` 属性
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
1. 登录弹窗使用 `z-index: 9999` 确保在最上层显示
|
||||||
|
2. 弹窗不可关闭时,点击遮罩层会有友好提示
|
||||||
|
3. 登录成功后会自动刷新当前页面数据
|
||||||
|
4. 密码输入框使用 `password` 类型,确保安全性
|
||||||
Loading…
Reference in New Issue
Block a user