Merge branch 'base' into tabbar
This commit is contained in:
commit
c1b12eab84
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "unibest",
|
"name": "unibest",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"version": "2.12.2",
|
"version": "2.13.0",
|
||||||
"description": "unibest - 最好的 uniapp 开发模板",
|
"description": "unibest - 最好的 uniapp 开发模板",
|
||||||
"update-time": "2025-06-17",
|
"update-time": "2025-06-17",
|
||||||
"author": {
|
"author": {
|
||||||
@ -11,14 +11,15 @@
|
|||||||
"github": "https://github.com/feige996",
|
"github": "https://github.com/feige996",
|
||||||
"gitee": "https://gitee.com/feige996"
|
"gitee": "https://gitee.com/feige996"
|
||||||
},
|
},
|
||||||
|
"homepage": "https://unibest.tech",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/feige996/unibest",
|
"repository": "https://github.com/feige996/unibest",
|
||||||
"repository-gitee": "https://gitee.com/feige996/unibest",
|
"repository-gitee": "https://gitee.com/feige996/unibest",
|
||||||
"repository-deprecated": "https://github.com/codercup/unibest",
|
"repository-old": "https://github.com/codercup/unibest",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/feige996/unibest/issues"
|
"url": "https://github.com/feige996/unibest/issues",
|
||||||
|
"url-old": "https://github.com/codercup/unibest/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://feige996.github.io/unibest/",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=18",
|
||||||
"pnpm": ">=7.30"
|
"pnpm": ">=7.30"
|
||||||
|
@ -48,12 +48,12 @@ export default defineUniPages({
|
|||||||
// icon: '/static/logo.svg',
|
// icon: '/static/logo.svg',
|
||||||
// iconType: 'local',
|
// iconType: 'local',
|
||||||
// },
|
// },
|
||||||
{
|
// {
|
||||||
pagePath: 'pages/mine/index',
|
// pagePath: 'pages/mine/index',
|
||||||
text: '我的',
|
// text: '我的',
|
||||||
icon: 'iconfont icon-my',
|
// icon: 'iconfont icon-my',
|
||||||
iconType: 'iconfont',
|
// iconType: 'iconfont',
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
@import 'wot-design-uni/components/wd-button/index.scss';
|
|
||||||
:deep(.wd-privacy-popup) {
|
|
||||||
width: 600rpx;
|
|
||||||
padding: 0 24rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 32rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-privacy-popup {
|
|
||||||
&__header {
|
|
||||||
width: 100%;
|
|
||||||
height: 128rpx;
|
|
||||||
line-height: 128rpx;
|
|
||||||
color: rgba(0, 0, 0, 0.85);
|
|
||||||
font-size: 30rpx;
|
|
||||||
padding: 0 12rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 12rpx;
|
|
||||||
margin-bottom: 32rpx;
|
|
||||||
|
|
||||||
font-size: 28rpx;
|
|
||||||
line-height: 1.8;
|
|
||||||
color: #3e3e3e;
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 550;
|
|
||||||
&-protocol {
|
|
||||||
color: #4d80f0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-bottom: 36rpx;
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view>
|
|
||||||
<wd-popup
|
|
||||||
v-model="showPopup"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
custom-class="wd-privacy-popup"
|
|
||||||
@close="handleClose"
|
|
||||||
>
|
|
||||||
<view class="wd-privacy-popup__header">
|
|
||||||
<!--标题-->
|
|
||||||
<view class="wd-picker__title">{{ title }}</view>
|
|
||||||
</view>
|
|
||||||
<view class="wd-privacy-popup__container">
|
|
||||||
<text>{{ desc }}</text>
|
|
||||||
<text class="wd-privacy-popup__container-protocol" @click="openPrivacyContract">
|
|
||||||
{{ protocol }}
|
|
||||||
</text>
|
|
||||||
<text>{{ subDesc }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="wd-privacy-popup__footer">
|
|
||||||
<button
|
|
||||||
class="wd-privacy-popup__footer-disagree wd-button is-block is-round is-medium is-plain"
|
|
||||||
id="disagree-btn"
|
|
||||||
@click="handleDisagree"
|
|
||||||
>
|
|
||||||
拒绝
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="wd-privacy-popup__footer-agree wd-button is-primary is-block is-round is-medium"
|
|
||||||
id="agree-btn"
|
|
||||||
open-type="agreePrivacyAuthorization"
|
|
||||||
@agreeprivacyauthorization="handleAgree"
|
|
||||||
>
|
|
||||||
同意
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
</wd-popup>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
name: 'privacy-popup',
|
|
||||||
options: {
|
|
||||||
virtualHost: true,
|
|
||||||
addGlobalClass: true,
|
|
||||||
styleIsolation: 'shared',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onBeforeMount, ref } from 'vue'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
title?: string // 标题
|
|
||||||
desc?: string // 描述
|
|
||||||
subDesc?: string // 字描述
|
|
||||||
protocol?: string // 协议名称
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
title: '用户隐私保护提示',
|
|
||||||
desc: '感谢您使用本应用,您使用本应用的服务之前请仔细阅读并同意',
|
|
||||||
subDesc:
|
|
||||||
'。当您点击同意并开始时用产品服务时,即表示你已理解并同息该条款内容,该条款将对您产生法律约束力。如您拒绝,将无法使用相应服务。',
|
|
||||||
protocol: '《用户隐私保护指引》',
|
|
||||||
})
|
|
||||||
|
|
||||||
const showPopup = ref<boolean>(false) // 是否展示popup
|
|
||||||
|
|
||||||
const privacyResolves = ref(new Set()) // onNeedPrivacyAuthorization的reslove
|
|
||||||
|
|
||||||
const privacyHandler = (resolve: any) => {
|
|
||||||
showPopup.value = true
|
|
||||||
privacyResolves.value.add(resolve)
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits(['agree', 'disagree'])
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
// 注册监听
|
|
||||||
if ((wx as any).onNeedPrivacyAuthorization) {
|
|
||||||
;(wx as any).onNeedPrivacyAuthorization((resolve: any) => {
|
|
||||||
if (typeof privacyHandler === 'function') {
|
|
||||||
privacyHandler(resolve)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同意隐私协议
|
|
||||||
*/
|
|
||||||
function handleAgree() {
|
|
||||||
showPopup.value = false
|
|
||||||
privacyResolves.value.forEach((resolve: any) => {
|
|
||||||
resolve({
|
|
||||||
event: 'agree',
|
|
||||||
buttonId: 'agree-btn',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
privacyResolves.value.clear()
|
|
||||||
emit('agree')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 拒绝隐私协议
|
|
||||||
*/
|
|
||||||
function handleDisagree() {
|
|
||||||
showPopup.value = false
|
|
||||||
privacyResolves.value.forEach((resolve: any) => {
|
|
||||||
resolve({
|
|
||||||
event: 'disagree',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
privacyResolves.value.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开隐私协议
|
|
||||||
*/
|
|
||||||
function openPrivacyContract() {
|
|
||||||
;(wx as any).openPrivacyContract({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('openPrivacyContract success')
|
|
||||||
},
|
|
||||||
fail: (res) => {
|
|
||||||
console.error('openPrivacyContract fail', res)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 弹出框关闭时清空
|
|
||||||
*/
|
|
||||||
function handleClose() {
|
|
||||||
privacyResolves.value.clear()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import './index.scss';
|
|
||||||
</style>
|
|
@ -1,584 +0,0 @@
|
|||||||
<route lang="json5" type="page">
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '登录',
|
|
||||||
navigationStyle: 'custom',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
<template>
|
|
||||||
<view class="login-container">
|
|
||||||
<!-- 背景装饰元素 -->
|
|
||||||
<view class="bg-decoration bg-circle-1"></view>
|
|
||||||
<view class="bg-decoration bg-circle-2"></view>
|
|
||||||
<view class="bg-decoration bg-circle-3"></view>
|
|
||||||
|
|
||||||
<view class="login-header">
|
|
||||||
<image class="login-logo" :src="appLogo" mode="aspectFit"></image>
|
|
||||||
<view class="login-title">{{ appTitle }}</view>
|
|
||||||
</view>
|
|
||||||
<view class="login-form">
|
|
||||||
<view class="welcome-text">欢迎登录</view>
|
|
||||||
<view class="login-desc">请输入您的账号和密码</view>
|
|
||||||
<view class="login-input-group">
|
|
||||||
<view class="input-wrapper">
|
|
||||||
<wd-input
|
|
||||||
v-model="loginForm.username"
|
|
||||||
prefix-icon="user"
|
|
||||||
placeholder="请输入用户名"
|
|
||||||
clearable
|
|
||||||
class="login-input"
|
|
||||||
:border="false"
|
|
||||||
required
|
|
||||||
></wd-input>
|
|
||||||
<view class="input-bottom-line"></view>
|
|
||||||
</view>
|
|
||||||
<view class="input-wrapper">
|
|
||||||
<wd-input
|
|
||||||
v-model="loginForm.password"
|
|
||||||
prefix-icon="lock-on"
|
|
||||||
placeholder="请输入密码"
|
|
||||||
clearable
|
|
||||||
show-password
|
|
||||||
class="login-input"
|
|
||||||
:border="false"
|
|
||||||
required
|
|
||||||
></wd-input>
|
|
||||||
<view class="input-bottom-line"></view>
|
|
||||||
</view>
|
|
||||||
<!-- 验证码区域 -->
|
|
||||||
<view class="input-wrapper captcha-wrapper">
|
|
||||||
<wd-input
|
|
||||||
v-if="captcha.captchaEnabled"
|
|
||||||
v-model="loginForm.code"
|
|
||||||
prefix-icon="secured"
|
|
||||||
placeholder="请输入验证码"
|
|
||||||
clearable
|
|
||||||
class="login-input captcha-input"
|
|
||||||
:border="false"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<template #suffix>
|
|
||||||
<image
|
|
||||||
class="captcha-image"
|
|
||||||
:src="'data:image/gif;base64,' + captcha.image"
|
|
||||||
mode="aspectFit"
|
|
||||||
@click="refreshCaptcha"
|
|
||||||
></image>
|
|
||||||
</template>
|
|
||||||
</wd-input>
|
|
||||||
<view class="input-bottom-line"></view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<!-- 登录按钮组 -->
|
|
||||||
<view class="login-buttons">
|
|
||||||
<!-- 账号密码登录按钮 -->
|
|
||||||
<wd-button
|
|
||||||
type="primary"
|
|
||||||
size="large"
|
|
||||||
block
|
|
||||||
@click="handleAccountLogin"
|
|
||||||
class="account-login-btn"
|
|
||||||
>
|
|
||||||
<wd-icon name="right" size="18px" class="login-icon"></wd-icon>
|
|
||||||
登录
|
|
||||||
</wd-button>
|
|
||||||
<!-- 微信小程序一键登录按钮 -->
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<view class="divider">
|
|
||||||
<view class="divider-line"></view>
|
|
||||||
<view class="divider-text">或</view>
|
|
||||||
<view class="divider-line"></view>
|
|
||||||
</view>
|
|
||||||
<wd-button
|
|
||||||
type="info"
|
|
||||||
size="large"
|
|
||||||
block
|
|
||||||
plain
|
|
||||||
@click="handleWechatLogin"
|
|
||||||
class="wechat-login-btn"
|
|
||||||
>
|
|
||||||
微信一键登录
|
|
||||||
</wd-button>
|
|
||||||
<!-- #endif -->
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<!-- 隐私协议勾选 -->
|
|
||||||
<view class="privacy-agreement">
|
|
||||||
<wd-checkbox
|
|
||||||
v-model="agreePrivacy"
|
|
||||||
shape="square"
|
|
||||||
class="privacy-checkbox"
|
|
||||||
active-color="var(--wot-color-theme, #1989fa)"
|
|
||||||
>
|
|
||||||
<view class="agreement-text">
|
|
||||||
我已阅读并同意
|
|
||||||
<text class="agreement-link" @click.stop="handleAgreement('user')">《用户协议》</text>
|
|
||||||
和
|
|
||||||
<text class="agreement-link" @click.stop="handleAgreement('privacy')">《隐私政策》</text>
|
|
||||||
</view>
|
|
||||||
</wd-checkbox>
|
|
||||||
</view>
|
|
||||||
<view class="login-footer"></view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useUserStore } from '@/store/user'
|
|
||||||
import { isMpWeixin } from '@/utils/platform'
|
|
||||||
import { getCode, ILoginForm } from '@/api/login'
|
|
||||||
import { toast } from '@/utils/toast'
|
|
||||||
import { isTableBar } from '@/utils/index'
|
|
||||||
import { ICaptcha } from '@/api/login.typings'
|
|
||||||
const redirectRoute = ref('')
|
|
||||||
|
|
||||||
// 获取环境变量
|
|
||||||
const appTitle = ref(import.meta.env.VITE_APP_TITLE || 'Unibest Login')
|
|
||||||
const appLogo = ref(import.meta.env.VITE_APP_LOGO || '/static/logo.svg')
|
|
||||||
|
|
||||||
// 初始化store
|
|
||||||
const userStore = useUserStore()
|
|
||||||
// 路由位置
|
|
||||||
// 验证码图片
|
|
||||||
const captcha = ref<ICaptcha>({
|
|
||||||
captchaEnabled: false,
|
|
||||||
uuid: '',
|
|
||||||
image: '',
|
|
||||||
})
|
|
||||||
// 登录表单数据
|
|
||||||
const loginForm = ref<ILoginForm>({
|
|
||||||
username: 'admin',
|
|
||||||
password: '123456',
|
|
||||||
code: '',
|
|
||||||
uuid: '',
|
|
||||||
})
|
|
||||||
// 隐私协议勾选状态
|
|
||||||
const agreePrivacy = ref(true)
|
|
||||||
|
|
||||||
// 页面加载完毕时触发
|
|
||||||
onLoad((option) => {
|
|
||||||
// 一进来就刷新验证码
|
|
||||||
captcha.value.captchaEnabled && refreshCaptcha()
|
|
||||||
// 获取跳转路由
|
|
||||||
if (option.redirect) {
|
|
||||||
redirectRoute.value = option.redirect
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 账号密码登录
|
|
||||||
const handleAccountLogin = async () => {
|
|
||||||
if (!agreePrivacy.value) {
|
|
||||||
toast.error('请阅读同意协议')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 表单验证
|
|
||||||
if (!loginForm.value.username) {
|
|
||||||
toast.error('请输入用户名')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!loginForm.value.password) {
|
|
||||||
toast.error('请输入密码')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (captcha.value.captchaEnabled && !loginForm.value.code) {
|
|
||||||
toast.error('请输入验证码')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 执行登录
|
|
||||||
await userStore.login(loginForm.value)
|
|
||||||
// 跳转到首页或重定向页面
|
|
||||||
const targetUrl = redirectRoute.value || '/pages/index/index'
|
|
||||||
if (isTableBar(targetUrl)) {
|
|
||||||
uni.switchTab({ url: targetUrl })
|
|
||||||
} else {
|
|
||||||
uni.redirectTo({ url: targetUrl })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 微信登录
|
|
||||||
const handleWechatLogin = async () => {
|
|
||||||
if (!isMpWeixin) {
|
|
||||||
toast.info('请在微信小程序中使用此功能')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证是否同意隐私协议
|
|
||||||
if (!agreePrivacy.value) {
|
|
||||||
toast.error('请先阅读并同意用户协议和隐私政策')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 微信登录
|
|
||||||
await userStore.wxLogin()
|
|
||||||
// 跳转到首页或重定向页面
|
|
||||||
const targetUrl = redirectRoute.value || '/pages/index/index'
|
|
||||||
if (isTableBar(targetUrl)) {
|
|
||||||
uni.switchTab({ url: targetUrl })
|
|
||||||
} else {
|
|
||||||
uni.redirectTo({ url: targetUrl })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新验证码
|
|
||||||
const refreshCaptcha = () => {
|
|
||||||
// 获取验证码
|
|
||||||
getCode().then((res) => {
|
|
||||||
const { data } = res
|
|
||||||
loginForm.value.uuid = data.uuid
|
|
||||||
captcha.value = data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理协议点击
|
|
||||||
const handleAgreement = (type: 'user' | 'privacy') => {
|
|
||||||
const title = type === 'user' ? '用户协议' : '隐私政策'
|
|
||||||
// showToast(`查看${title}`)
|
|
||||||
// 实际项目中可以跳转到对应的协议页面
|
|
||||||
// uni.navigateTo({
|
|
||||||
// url: `/pages/agreement/${type}`
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* 验证码输入框样式 */
|
|
||||||
.captcha-wrapper {
|
|
||||||
.captcha-input {
|
|
||||||
:deep(.wd-input__suffix) {
|
|
||||||
margin-right: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.captcha-image {
|
|
||||||
width: 100px;
|
|
||||||
height: 36px;
|
|
||||||
margin-left: 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1), transparent);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.8;
|
|
||||||
transform: scale(0.96);
|
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-container {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 0 70rpx;
|
|
||||||
background-color: #ffffff;
|
|
||||||
background-image: linear-gradient(
|
|
||||||
135deg,
|
|
||||||
rgba(25, 137, 250, 0.05) 0%,
|
|
||||||
rgba(255, 255, 255, 0) 100%
|
|
||||||
);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 背景装饰元素 */
|
|
||||||
.bg-decoration {
|
|
||||||
position: absolute;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, rgba(25, 137, 250, 0.05), rgba(25, 137, 250, 0.1));
|
|
||||||
z-index: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-circle-1 {
|
|
||||||
width: 500rpx;
|
|
||||||
height: 500rpx;
|
|
||||||
top: -200rpx;
|
|
||||||
right: -200rpx;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-circle-2 {
|
|
||||||
width: 400rpx;
|
|
||||||
height: 400rpx;
|
|
||||||
bottom: 10%;
|
|
||||||
left: -200rpx;
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-circle-3 {
|
|
||||||
width: 300rpx;
|
|
||||||
height: 300rpx;
|
|
||||||
bottom: -100rpx;
|
|
||||||
right: 10%;
|
|
||||||
opacity: 0.3;
|
|
||||||
background: linear-gradient(135deg, rgba(7, 193, 96, 0.05), rgba(7, 193, 96, 0.1));
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 120rpx;
|
|
||||||
animation: fadeInDown 0.8s ease-out;
|
|
||||||
|
|
||||||
.login-logo {
|
|
||||||
width: 200rpx;
|
|
||||||
height: 200rpx;
|
|
||||||
border-radius: 36rpx;
|
|
||||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.12);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
transform: scale(0.95);
|
|
||||||
box-shadow: 0 6rpx 15rpx rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-title {
|
|
||||||
margin-top: 30rpx;
|
|
||||||
font-size: 46rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333333;
|
|
||||||
letter-spacing: 3rpx;
|
|
||||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form {
|
|
||||||
flex: 1;
|
|
||||||
margin-top: 70rpx;
|
|
||||||
animation: fadeIn 0.8s ease-out 0.2s both;
|
|
||||||
|
|
||||||
.welcome-text {
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
font-size: 48rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333333;
|
|
||||||
text-align: center;
|
|
||||||
letter-spacing: 1rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-desc {
|
|
||||||
margin-bottom: 70rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #888888;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-input-group {
|
|
||||||
margin-bottom: 60rpx;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.input-wrapper {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 50rpx;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-input {
|
|
||||||
padding: 12rpx 20rpx;
|
|
||||||
background-color: rgba(245, 247, 250, 0.7);
|
|
||||||
border-radius: 16rpx;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
:deep(.wd-input__inner) {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wd-input__placeholder) {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #aaaaaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus-within {
|
|
||||||
background-color: rgba(245, 247, 250, 0.95);
|
|
||||||
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.06);
|
|
||||||
transform: translateY(-3rpx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-bottom-line {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -2rpx;
|
|
||||||
left: 5%;
|
|
||||||
width: 90%;
|
|
||||||
height: 2rpx;
|
|
||||||
background: linear-gradient(
|
|
||||||
to right,
|
|
||||||
transparent,
|
|
||||||
var(--wot-color-theme, #1989fa),
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
transition: transform 0.4s ease;
|
|
||||||
transform: scaleX(0);
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus-within .input-bottom-line {
|
|
||||||
transform: scaleX(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-icon {
|
|
||||||
margin-right: 16rpx;
|
|
||||||
color: #666666;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus-within .input-icon {
|
|
||||||
color: var(--wot-color-theme, #1989fa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-buttons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 36rpx;
|
|
||||||
|
|
||||||
.account-login-btn {
|
|
||||||
height: 96rpx;
|
|
||||||
margin-top: 20rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: 2rpx;
|
|
||||||
border-radius: 48rpx;
|
|
||||||
box-shadow: 0 10rpx 20rpx rgba(25, 137, 250, 0.25);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.login-icon {
|
|
||||||
margin-right: 8rpx;
|
|
||||||
opacity: 0.8;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
box-shadow: 0 5rpx 10rpx rgba(25, 137, 250, 0.2);
|
|
||||||
transform: scale(0.98);
|
|
||||||
|
|
||||||
.login-icon {
|
|
||||||
transform: translateX(3rpx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 24rpx 0;
|
|
||||||
|
|
||||||
.divider-line {
|
|
||||||
flex: 1;
|
|
||||||
height: 1px;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider-text {
|
|
||||||
padding: 0 24rpx;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.wechat-login-btn {
|
|
||||||
height: 96rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: #07c160;
|
|
||||||
border-color: #07c160;
|
|
||||||
border-radius: 48rpx;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
.wechat-icon {
|
|
||||||
margin-right: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: rgba(7, 193, 96, 0.08);
|
|
||||||
transform: scale(0.98);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.privacy-agreement {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 30rpx 0 40rpx;
|
|
||||||
animation: fadeIn 0.8s ease-out 0.4s both;
|
|
||||||
|
|
||||||
.privacy-checkbox {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agreement-text {
|
|
||||||
font-size: 26rpx;
|
|
||||||
line-height: 1.6;
|
|
||||||
color: #666666;
|
|
||||||
|
|
||||||
.agreement-link {
|
|
||||||
padding: 0 4rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--wot-color-theme, #1989fa);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.8;
|
|
||||||
transform: scale(0.98);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-footer {
|
|
||||||
padding: 50rpx 0;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 添加动画效果 */
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeInDown {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,173 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '关于我们',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="about-container">
|
|
||||||
<view class="about-card">
|
|
||||||
<!-- 应用信息 -->
|
|
||||||
<view class="app-info">
|
|
||||||
<view class="logo-wrapper">
|
|
||||||
<wd-img :src="appLogo" width="120px" height="120px" radius="24rpx"></wd-img>
|
|
||||||
</view>
|
|
||||||
<view class="app-name">{{ appTitle }}</view>
|
|
||||||
<view class="app-version">版本 {{ packageJson.version }}</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 联系方式 -->
|
|
||||||
<view class="info-section">
|
|
||||||
<view class="section-title">联系我们</view>
|
|
||||||
<view class="section-content">
|
|
||||||
<view class="contact-item">
|
|
||||||
<wd-icon name="phone" size="20px" class="contact-icon"></wd-icon>
|
|
||||||
<text class="contact-text">客服电话:400-XXX-XXXX</text>
|
|
||||||
</view>
|
|
||||||
<view class="contact-item">
|
|
||||||
<wd-icon name="mail" size="20px" class="contact-icon"></wd-icon>
|
|
||||||
<text class="contact-text">邮箱:support@unibest.tech</text>
|
|
||||||
</view>
|
|
||||||
<view class="contact-item">
|
|
||||||
<wd-icon name="location" size="20px" class="contact-icon"></wd-icon>
|
|
||||||
<text class="contact-text">地址:中国·深圳</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 版权信息 -->
|
|
||||||
<view class="copyright">
|
|
||||||
<text>Copyright © 2025-{{ currentYear }} {{ appTitle }}</text>
|
|
||||||
<text>All Rights Reserved</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
import packageJson from '@/../package.json'
|
|
||||||
|
|
||||||
const appTitle = ref(import.meta.env.VITE_APP_TITLE || 'unibest')
|
|
||||||
const appLogo = ref(import.meta.env.VITE_APP_LOGO || '/static/logo.svg')
|
|
||||||
|
|
||||||
// 当前年份
|
|
||||||
const currentYear = computed(() => new Date().getFullYear())
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.about-container {
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
padding: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-card {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 40rpx 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 应用信息 */
|
|
||||||
.app-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding: 30rpx 0 50rpx;
|
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-wrapper {
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-name {
|
|
||||||
font-size: 40rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-version {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 信息区块 */
|
|
||||||
.info-section {
|
|
||||||
padding: 40rpx 0;
|
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 34rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
position: relative;
|
|
||||||
padding-left: 24rpx;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
width: 8rpx;
|
|
||||||
height: 32rpx;
|
|
||||||
background: linear-gradient(135deg, #4a7bff, #6a5acd);
|
|
||||||
border-radius: 4rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-content {
|
|
||||||
padding: 0 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-text {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: #666;
|
|
||||||
line-height: 1.6;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 联系方式 */
|
|
||||||
.contact-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-icon {
|
|
||||||
margin-right: 20rpx;
|
|
||||||
color: #4a7bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-text {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 版权信息 */
|
|
||||||
.copyright {
|
|
||||||
padding-top: 40rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
text {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #999;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,375 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'tabbar',
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '我的',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="profile-container">
|
|
||||||
{{ JSON.stringify(userInfo) }}
|
|
||||||
<!-- 用户信息区域 -->
|
|
||||||
<view class="user-info-section">
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<button class="avatar-button" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
|
|
||||||
<wd-img :src="userInfo.avatar" width="80px" height="80px" radius="50%"></wd-img>
|
|
||||||
</button>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifndef MP-WEIXIN -->
|
|
||||||
<view class="avatar-wrapper" @click="run">
|
|
||||||
<wd-img :src="userInfo.avatar" width="100%" height="100%" radius="50%"></wd-img>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
<view class="user-details">
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<input
|
|
||||||
type="nickname"
|
|
||||||
class="weui-input"
|
|
||||||
placeholder="请输入昵称"
|
|
||||||
v-model="userInfo.username"
|
|
||||||
/>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifndef MP-WEIXIN -->
|
|
||||||
<view class="username">{{ userInfo.username }}</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
<view class="user-id">ID: {{ userInfo.id }}</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 功能区块 -->
|
|
||||||
<view class="function-section">
|
|
||||||
<view class="cell-group">
|
|
||||||
<view class="group-title">账号管理</view>
|
|
||||||
<wd-cell title="个人资料" is-link @click="handleProfileInfo">
|
|
||||||
<template #icon>
|
|
||||||
<wd-icon name="user" size="20px"></wd-icon>
|
|
||||||
</template>
|
|
||||||
</wd-cell>
|
|
||||||
<wd-cell title="账号安全" is-link @click="handlePassword">
|
|
||||||
<template #icon>
|
|
||||||
<wd-icon name="lock-on" size="20px"></wd-icon>
|
|
||||||
</template>
|
|
||||||
</wd-cell>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="cell-group">
|
|
||||||
<view class="group-title">通用设置</view>
|
|
||||||
<wd-cell title="消息通知" is-link @click="handleInform">
|
|
||||||
<template #icon>
|
|
||||||
<wd-icon name="notification" size="20px"></wd-icon>
|
|
||||||
</template>
|
|
||||||
</wd-cell>
|
|
||||||
<wd-cell title="清理缓存" is-link @click="handleClearCache">
|
|
||||||
<template #icon>
|
|
||||||
<wd-icon name="clear" size="20px"></wd-icon>
|
|
||||||
</template>
|
|
||||||
</wd-cell>
|
|
||||||
<wd-cell title="应用更新" is-link @click="handleAppUpdate">
|
|
||||||
<template #icon>
|
|
||||||
<wd-icon name="refresh1" size="20px"></wd-icon>
|
|
||||||
</template>
|
|
||||||
</wd-cell>
|
|
||||||
<wd-cell title="关于我们" is-link @click="handleAbout">
|
|
||||||
<template #icon>
|
|
||||||
<wd-icon name="info-circle" size="20px"></wd-icon>
|
|
||||||
</template>
|
|
||||||
</wd-cell>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="logout-button-wrapper">
|
|
||||||
<wd-button type="error" v-if="hasLogin" block @click="handleLogout">退出登录</wd-button>
|
|
||||||
<wd-button type="primary" v-else block @click="handleLogin">登录</wd-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useUserStore } from '@/store'
|
|
||||||
import { useToast } from 'wot-design-uni'
|
|
||||||
import { useUpload } from '@/utils/uploadFile'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { IUploadSuccessInfo } from '@/api/login.typings'
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
// 使用storeToRefs解构userInfo
|
|
||||||
const { userInfo } = storeToRefs(userStore)
|
|
||||||
const toast = useToast()
|
|
||||||
const hasLogin = ref(false)
|
|
||||||
|
|
||||||
onShow((options) => {
|
|
||||||
hasLogin.value = !!uni.getStorageSync('token')
|
|
||||||
console.log('个人中心onShow', hasLogin.value, options)
|
|
||||||
|
|
||||||
hasLogin.value && useUserStore().getUserInfo()
|
|
||||||
})
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
// 上传头像
|
|
||||||
const { run } = useUpload<IUploadSuccessInfo>(
|
|
||||||
import.meta.env.VITE_UPLOAD_BASEURL,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
onSuccess: (res) => {
|
|
||||||
console.log('h5头像上传成功', res)
|
|
||||||
useUserStore().setUserAvatar(res.url)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// 微信小程序下登录
|
|
||||||
const handleLogin = async () => {
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
|
|
||||||
// 微信登录
|
|
||||||
await userStore.wxLogin()
|
|
||||||
hasLogin.value = true
|
|
||||||
// #endif
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
uni.navigateTo({ url: '/pages/login/index' })
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
|
|
||||||
// 微信小程序下选择头像事件
|
|
||||||
const onChooseAvatar = (e: any) => {
|
|
||||||
console.log('选择头像', e.detail)
|
|
||||||
const { avatarUrl } = e.detail
|
|
||||||
const { run } = useUpload<IUploadSuccessInfo>(
|
|
||||||
import.meta.env.VITE_UPLOAD_BASEURL,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
onSuccess: (res) => {
|
|
||||||
console.log('wx头像上传成功', res)
|
|
||||||
useUserStore().setUserAvatar(res.url)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
avatarUrl,
|
|
||||||
)
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
// #endif
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
// 微信小程序下设置用户名
|
|
||||||
const getUserInfo = (e: any) => {
|
|
||||||
console.log(e.detail)
|
|
||||||
}
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// 个人资料
|
|
||||||
const handleProfileInfo = () => {
|
|
||||||
uni.navigateTo({ url: `/pages/mine/info/index` })
|
|
||||||
}
|
|
||||||
// 账号安全
|
|
||||||
const handlePassword = () => {
|
|
||||||
uni.navigateTo({ url: `/pages/mine/password/index` })
|
|
||||||
}
|
|
||||||
// 消息通知
|
|
||||||
const handleInform = () => {
|
|
||||||
// uni.navigateTo({ url: `/pages/mine/inform/index` })
|
|
||||||
toast.show('功能开发中')
|
|
||||||
}
|
|
||||||
// 应用更新
|
|
||||||
const handleAppUpdate = () => {
|
|
||||||
// #ifdef MP
|
|
||||||
// #ifndef MP-HARMONY
|
|
||||||
const updateManager = uni.getUpdateManager()
|
|
||||||
updateManager.onCheckForUpdate(function (res) {
|
|
||||||
// 请求完新版本信息的回调
|
|
||||||
// console.log(res.hasUpdate)
|
|
||||||
if (res.hasUpdate) {
|
|
||||||
toast.show('检测到新版本,正在下载中...')
|
|
||||||
} else {
|
|
||||||
toast.show('已是最新版本')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
updateManager.onUpdateReady(function (res) {
|
|
||||||
uni.showModal({
|
|
||||||
title: '更新提示',
|
|
||||||
content: '新版本已经准备好,是否重启应用?',
|
|
||||||
success(res) {
|
|
||||||
if (res.confirm) {
|
|
||||||
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
|
|
||||||
updateManager.applyUpdate()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
updateManager.onUpdateFailed(function (res) {
|
|
||||||
// 新的版本下载失败
|
|
||||||
toast.error('新版本下载失败')
|
|
||||||
})
|
|
||||||
// #endif
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifndef MP
|
|
||||||
toast.show('功能开发中')
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
// 关于我们
|
|
||||||
const handleAbout = () => {
|
|
||||||
uni.navigateTo({ url: `/pages/mine/about/index` })
|
|
||||||
}
|
|
||||||
// 清除缓存
|
|
||||||
const handleClearCache = () => {
|
|
||||||
uni.showModal({
|
|
||||||
title: '清除缓存',
|
|
||||||
content: '确定要清除所有缓存吗?\n清除后需要重新登录',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
try {
|
|
||||||
// 清除所有缓存
|
|
||||||
uni.clearStorageSync()
|
|
||||||
// 清除用户信息并跳转到登录页
|
|
||||||
useUserStore().logout()
|
|
||||||
toast.show('清除缓存成功')
|
|
||||||
} catch (err) {
|
|
||||||
console.error('清除缓存失败:', err)
|
|
||||||
toast.error('清除缓存失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 退出登录
|
|
||||||
const handleLogout = () => {
|
|
||||||
uni.showModal({
|
|
||||||
title: '提示',
|
|
||||||
content: '确定要退出登录吗?',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
// 清空用户信息
|
|
||||||
useUserStore().logout()
|
|
||||||
hasLogin.value = false
|
|
||||||
// 执行退出登录逻辑
|
|
||||||
toast.show('退出登录成功')
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
// 微信小程序,去首页
|
|
||||||
// uni.reLaunch({ url: '/pages/index/index' })
|
|
||||||
// #endif
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
// 非微信小程序,去登录页
|
|
||||||
// uni.reLaunch({ url: '/pages/login/index' })
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* 基础样式 */
|
|
||||||
.profile-container {
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
|
||||||
background-color: #f7f8fa;
|
|
||||||
}
|
|
||||||
/* 用户信息区域 */
|
|
||||||
.user-info-section {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 40rpx;
|
|
||||||
margin: 30rpx 30rpx 20rpx;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-wrapper {
|
|
||||||
width: 160rpx;
|
|
||||||
height: 160rpx;
|
|
||||||
margin-right: 40rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 4rpx solid #f5f5f5;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
.avatar-button {
|
|
||||||
height: 160rpx;
|
|
||||||
padding: 0;
|
|
||||||
margin-right: 40rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 4rpx solid #f5f5f5;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
.user-details {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username {
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
font-size: 38rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
letter-spacing: 0.5rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-id {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-created {
|
|
||||||
margin-top: 8rpx;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
/* 功能区块 */
|
|
||||||
.function-section {
|
|
||||||
padding: 0 20rpx;
|
|
||||||
margin-top: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-group {
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-title {
|
|
||||||
padding: 24rpx 30rpx 16rpx;
|
|
||||||
font-size: 30rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #999;
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wd-cell) {
|
|
||||||
border-bottom: 1rpx solid #f5f5f5;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-cell__title {
|
|
||||||
margin-left: 5px;
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-icon {
|
|
||||||
margin-right: 20rpx;
|
|
||||||
font-size: 36rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 退出登录按钮 */
|
|
||||||
.logout-button-wrapper {
|
|
||||||
padding: 40rpx 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wd-button--danger) {
|
|
||||||
height: 88rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
line-height: 88rpx;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #f53f3f;
|
|
||||||
border-radius: 44rpx;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,190 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '个人资料',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="profile-info-container">
|
|
||||||
<view class="profile-card">
|
|
||||||
<view class="form-wrapper">
|
|
||||||
<wd-form ref="formRef" :model="formData" label-width="160rpx" class="profile-form">
|
|
||||||
<wd-cell-group class="form-group">
|
|
||||||
<!-- 昵称 -->
|
|
||||||
<view class="sex-field">
|
|
||||||
<text class="field-label">昵称</text>
|
|
||||||
<wd-input
|
|
||||||
prop="name"
|
|
||||||
clearable
|
|
||||||
v-model="formData.name"
|
|
||||||
placeholder="请输入昵称"
|
|
||||||
:rules="[{ required: true, message: '请填写昵称' }]"
|
|
||||||
class="form-input"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 性别 -->
|
|
||||||
<view class="sex-field">
|
|
||||||
<text class="field-label">性别</text>
|
|
||||||
<wd-radio-group
|
|
||||||
v-model="formData.sex"
|
|
||||||
shape="button"
|
|
||||||
:rules="[{ required: true, message: '请选择性别' }]"
|
|
||||||
>
|
|
||||||
<wd-radio :value="'1'">男</wd-radio>
|
|
||||||
<wd-radio :value="'0'">女</wd-radio>
|
|
||||||
</wd-radio-group>
|
|
||||||
</view>
|
|
||||||
</wd-cell-group>
|
|
||||||
</wd-form>
|
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
|
||||||
<view class="form-actions">
|
|
||||||
<wd-button type="primary" size="large" @click="handleSubmit">保存修改</wd-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useUserStore } from '@/store'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { toast } from '@/utils/toast'
|
|
||||||
import { updateInfo } from '@/api/login'
|
|
||||||
|
|
||||||
// 表单引用
|
|
||||||
const formRef = ref()
|
|
||||||
|
|
||||||
// 用户信息
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const { userInfo } = storeToRefs(userStore)
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const formData = ref({
|
|
||||||
id: userInfo.value.id,
|
|
||||||
name: userInfo.value.name,
|
|
||||||
sex: userInfo.value.sex,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 提交表单
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
// 表单验证
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
const { message } = await updateInfo(formData.value)
|
|
||||||
await useUserStore().getUserInfo()
|
|
||||||
toast.success(message)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.profile-info-container {
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
padding: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-card {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
padding: 40rpx 30rpx 20rpx;
|
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
font-size: 36rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
padding-bottom: 16rpx;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 60rpx;
|
|
||||||
height: 6rpx;
|
|
||||||
background: linear-gradient(90deg, #4a7bff, #6a5acd);
|
|
||||||
border-radius: 6rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-wrapper {
|
|
||||||
padding: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
border-radius: 16rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 40rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input {
|
|
||||||
font-size: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sex-field {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24rpx 30rpx;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-label {
|
|
||||||
width: 160rpx;
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-group {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-btn {
|
|
||||||
flex: 1;
|
|
||||||
height: 80rpx;
|
|
||||||
line-height: 80rpx;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 30rpx;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn {
|
|
||||||
height: 90rpx;
|
|
||||||
border-radius: 45rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
background: linear-gradient(135deg, #4a7bff, #6a5acd);
|
|
||||||
box-shadow: 0 8rpx 16rpx rgba(74, 123, 255, 0.2);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
transform: translateY(2rpx);
|
|
||||||
box-shadow: 0 4rpx 8rpx rgba(74, 123, 255, 0.15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,203 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '修改密码',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="profile-info-container">
|
|
||||||
<view class="profile-card">
|
|
||||||
<view class="form-wrapper">
|
|
||||||
<wd-form ref="formRef" :model="formData" label-width="160rpx" class="profile-form">
|
|
||||||
<wd-cell-group class="form-group">
|
|
||||||
<!-- 昵称 -->
|
|
||||||
<view class="sex-field">
|
|
||||||
<text class="field-label">旧密码</text>
|
|
||||||
<wd-input
|
|
||||||
prop="oldPassword"
|
|
||||||
clearable
|
|
||||||
v-model="formData.oldPassword"
|
|
||||||
placeholder="请输入旧密码"
|
|
||||||
show-password
|
|
||||||
:rules="[{ required: true, message: '请填写旧密码' }]"
|
|
||||||
class="form-input"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
<view class="sex-field">
|
|
||||||
<text class="field-label">新密码</text>
|
|
||||||
<wd-input
|
|
||||||
prop="newPassword"
|
|
||||||
clearable
|
|
||||||
v-model="formData.newPassword"
|
|
||||||
placeholder="请输入新密码"
|
|
||||||
show-password
|
|
||||||
:rules="[{ required: true, message: '请填写新密码' }]"
|
|
||||||
class="form-input"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
<view class="sex-field">
|
|
||||||
<text class="field-label">确认密码</text>
|
|
||||||
<wd-input
|
|
||||||
prop="confirmPassword"
|
|
||||||
clearable
|
|
||||||
v-model="formData.confirmPassword"
|
|
||||||
placeholder="请输入新密码"
|
|
||||||
show-password
|
|
||||||
:rules="[{ required: true, message: '请填写新密码' }]"
|
|
||||||
class="form-input"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</wd-cell-group>
|
|
||||||
</wd-form>
|
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
|
||||||
<view class="form-actions">
|
|
||||||
<wd-button type="primary" size="large" @click="handleSubmit">保存修改</wd-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useUserStore } from '@/store'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { toast } from '@/utils/toast'
|
|
||||||
import { updateInfo, updateUserPassword } from '@/api/login'
|
|
||||||
|
|
||||||
// 表单引用
|
|
||||||
const formRef = ref()
|
|
||||||
|
|
||||||
// 用户信息
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const { userInfo } = storeToRefs(userStore)
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const formData = ref({
|
|
||||||
id: userInfo.value.id,
|
|
||||||
oldPassword: '',
|
|
||||||
newPassword: '',
|
|
||||||
confirmPassword: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
// 提交表单
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
// 表单验证
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
const { message } = await updateUserPassword(formData.value)
|
|
||||||
await useUserStore().logout()
|
|
||||||
toast.success('修改成功,请重新登录')
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.profile-info-container {
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
padding: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-card {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
padding: 40rpx 30rpx 20rpx;
|
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
font-size: 36rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
padding-bottom: 16rpx;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 60rpx;
|
|
||||||
height: 6rpx;
|
|
||||||
background: linear-gradient(90deg, #4a7bff, #6a5acd);
|
|
||||||
border-radius: 6rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-wrapper {
|
|
||||||
padding: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
border-radius: 16rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 40rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input {
|
|
||||||
font-size: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sex-field {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24rpx 30rpx;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-label {
|
|
||||||
width: 160rpx;
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-group {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-btn {
|
|
||||||
flex: 1;
|
|
||||||
height: 80rpx;
|
|
||||||
line-height: 80rpx;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 30rpx;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn {
|
|
||||||
height: 90rpx;
|
|
||||||
border-radius: 45rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
background: linear-gradient(135deg, #4a7bff, #6a5acd);
|
|
||||||
box-shadow: 0 8rpx 16rpx rgba(74, 123, 255, 0.2);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
transform: translateY(2rpx);
|
|
||||||
box-shadow: 0 4rpx 8rpx rgba(74, 123, 255, 0.15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
Loading…
x
Reference in New Issue
Block a user