mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-16 22:51:14 +00:00
feat: 初始化前端项目基础结构
- 添加项目配置文件(tsconfig, eslint, prettier等) - 实现基础路由和页面布局 - 添加全局状态管理和API请求封装 - 集成UI组件库和主题系统 - 添加文档网站和示例页面 - 配置CI/CD和工作流
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
<script setup lang="ts">
|
||||
import { onReady, onShow } from '@dcloudio/uni-app'
|
||||
import { reactive, ref } from 'vue'
|
||||
import LineChart from '@/subPages/echarts/components/LineChart.vue'
|
||||
|
||||
defineOptions({
|
||||
componentPlaceholder: {
|
||||
LineChart: 'view',
|
||||
},
|
||||
})
|
||||
|
||||
definePage({
|
||||
name: 'home',
|
||||
layout: 'tabbar',
|
||||
style: {
|
||||
navigationBarTitleText: '首页',
|
||||
},
|
||||
})
|
||||
|
||||
const current = ref<number>(0)
|
||||
|
||||
/**
|
||||
* 访问统计数据
|
||||
*/
|
||||
interface VisitStatsVO {
|
||||
todayUvCount: number
|
||||
uvGrowthRate: number
|
||||
totalUvCount: number
|
||||
todayPvCount: number
|
||||
pvGrowthRate: number
|
||||
totalPvCount: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 导航项
|
||||
*/
|
||||
interface NavItem {
|
||||
icon: string
|
||||
title: string
|
||||
url: string
|
||||
prem: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷导航列表
|
||||
*/
|
||||
const NAV_LIST: NavItem[] = [
|
||||
{
|
||||
icon: '/static/icons/user.png',
|
||||
title: '用户管理',
|
||||
url: '/pages/work/user/index',
|
||||
prem: 'sys:user:query',
|
||||
},
|
||||
{
|
||||
icon: '/static/icons/role.png',
|
||||
title: '角色管理',
|
||||
url: '/pages/work/role/index',
|
||||
prem: 'sys:role:query',
|
||||
},
|
||||
{
|
||||
icon: '/static/icons/notice.png',
|
||||
title: '通知公告',
|
||||
url: '/pages/work/notice/index',
|
||||
prem: 'sys:notice:query',
|
||||
},
|
||||
{
|
||||
icon: '/static/icons/setting.png',
|
||||
title: '系统配置',
|
||||
url: '/pages/work/config/index',
|
||||
prem: 'sys:config:query',
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 默认访问统计数据
|
||||
*/
|
||||
const DEFAULT_VISIT_STATS: VisitStatsVO = {
|
||||
todayUvCount: 1234,
|
||||
uvGrowthRate: 15.6,
|
||||
totalUvCount: 45678,
|
||||
todayPvCount: 5678,
|
||||
pvGrowthRate: 23.4,
|
||||
totalPvCount: 123456,
|
||||
}
|
||||
|
||||
// 通知公告文本
|
||||
const NOTICE_TEXT = '通知公告: fastapp 是一个基于 Vue3 + UniApp 的前端模板项目,提供了一套完整的前端解决方案'
|
||||
|
||||
// 默认日期范围(天数)
|
||||
const DEFAULT_DAYS_RANGE = 7
|
||||
|
||||
// 访问统计数据
|
||||
const visitStatsData = ref<VisitStatsVO>(DEFAULT_VISIT_STATS)
|
||||
|
||||
// 图表数据
|
||||
const chartData = ref({})
|
||||
|
||||
// 日期范围
|
||||
const recentDaysRange = ref(DEFAULT_DAYS_RANGE)
|
||||
|
||||
// 轮播图列表
|
||||
const swiperList = ref([
|
||||
'/static/images/banner01.jpg',
|
||||
'/static/images/banner02.jpg',
|
||||
])
|
||||
|
||||
// 快捷导航列表
|
||||
const navList = reactive(NAV_LIST)
|
||||
|
||||
// 导航到指定页面
|
||||
function navigateTo(url: string) {
|
||||
// 避免空 URL 导航
|
||||
if (!url)
|
||||
return
|
||||
|
||||
// 根据 URL 类型选择不同的导航方式
|
||||
if (
|
||||
url.startsWith('/pages/')
|
||||
&& (url.includes('/index/')
|
||||
|| url.includes('/mine/')
|
||||
|| url.includes('/work/'))
|
||||
) {
|
||||
uni.switchTab({
|
||||
url,
|
||||
})
|
||||
}
|
||||
else {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 生成静态的访问趋势数据
|
||||
function generateStaticTrendData(days: number) {
|
||||
const ipList = []
|
||||
const pvList = []
|
||||
|
||||
const today = new Date()
|
||||
|
||||
for (let i = days - 1; i >= 0; i--) {
|
||||
const date = new Date(today)
|
||||
date.setDate(today.getDate() - i)
|
||||
|
||||
// 生成模拟数据
|
||||
ipList.push(Math.floor(Math.random() * 500) + 200)
|
||||
pvList.push(Math.floor(Math.random() * 1000) + 500)
|
||||
}
|
||||
|
||||
return {
|
||||
ipList,
|
||||
pvList,
|
||||
}
|
||||
}
|
||||
|
||||
// 加载访问统计数据(使用静态数据)
|
||||
async function loadVisitStatsData() {
|
||||
// 模拟异步加载
|
||||
setTimeout(() => {
|
||||
visitStatsData.value = { ...DEFAULT_VISIT_STATS }
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 加载访问趋势数据(使用静态数据)
|
||||
function loadVisitTrendData() {
|
||||
// 模拟异步加载
|
||||
setTimeout(() => {
|
||||
const data = generateStaticTrendData(recentDaysRange.value)
|
||||
|
||||
const res = {
|
||||
series: [
|
||||
{
|
||||
name: 'UV',
|
||||
data: data.ipList,
|
||||
},
|
||||
{
|
||||
name: 'PV',
|
||||
data: data.pvList,
|
||||
},
|
||||
],
|
||||
}
|
||||
chartData.value = JSON.parse(JSON.stringify(res))
|
||||
}, 100)
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
loadVisitStatsData()
|
||||
loadVisitTrendData()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// 确保 tabbar 状态正确
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 0) {
|
||||
const currentPage = pages[pages.length - 1]
|
||||
if (currentPage.route === 'pages/index/index') {
|
||||
// 通过事件通知 tabbar 布局更新状态
|
||||
uni.$emit('updateTabbar', 'index')
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<!-- 轮播图 -->
|
||||
<view class="swiper-container">
|
||||
<wd-swiper
|
||||
v-model:current="current"
|
||||
:list="swiperList"
|
||||
autoplay
|
||||
indicator
|
||||
image-mode="scaleToFill"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 快捷导航 -->
|
||||
<view class="nav-grid">
|
||||
<wd-grid clickable :column="4">
|
||||
<wd-grid-item
|
||||
v-for="(item, index) in navList"
|
||||
:key="index"
|
||||
@click="navigateTo(item.url)"
|
||||
>
|
||||
<template #icon>
|
||||
<image class="icon-image" :src="item.icon" lazy-load />
|
||||
</template>
|
||||
<template #text>
|
||||
<view class="nav-title">
|
||||
<wd-text :text="item.title" />
|
||||
</view>
|
||||
</template>
|
||||
</wd-grid-item>
|
||||
</wd-grid>
|
||||
</view>
|
||||
|
||||
<!-- 通知公告 -->
|
||||
<view class="notice-text">
|
||||
<wd-notice-bar
|
||||
type="warning"
|
||||
prefix="warn-bold"
|
||||
:text="NOTICE_TEXT"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 数据统计 -->
|
||||
<view class="stats-grid">
|
||||
<view class="stats-item">
|
||||
<image class="stats-icon" src="/static/icons/visitor.png" lazy-load />
|
||||
<view class="stats-info">
|
||||
<wd-text text="访客数" />
|
||||
<view class="stats-value">
|
||||
<wd-text type="warning" :text="visitStatsData.todayUvCount" bold />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<image class="stats-icon" src="/static/icons/browser.png" lazy-load />
|
||||
<view class="stats-info">
|
||||
<wd-text text="浏览量" />
|
||||
<view class="stats-value">
|
||||
<wd-text type="success" :text="visitStatsData.todayPvCount" bold />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 访问趋势 -->
|
||||
<view class="mb-1 rounded-2 bg-gray-50 p-3 shadow-sm">
|
||||
<LineChart />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
padding: 20rpx;
|
||||
|
||||
.swiper-container {
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-grid {
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
|
||||
.icon-image {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-text {
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background-color: var(--bg-color-2);
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.stats-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,386 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CaptchaInfo, LoginFormData } from '@/api/auth'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import AuthAPI from '@/api/auth'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
|
||||
definePage({
|
||||
name: 'login',
|
||||
style: {
|
||||
navigationBarTitleText: '登录',
|
||||
},
|
||||
})
|
||||
|
||||
// 主题相关
|
||||
const loginFormRef = ref()
|
||||
const toast = useToast()
|
||||
const loading = ref(false)
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 登录表单数据
|
||||
const loginFormData = reactive<LoginFormData>({
|
||||
username: 'admin',
|
||||
password: '123456',
|
||||
captcha: '',
|
||||
captcha_key: '',
|
||||
remember: true,
|
||||
login_type: '移动端',
|
||||
})
|
||||
|
||||
// 验证码状态
|
||||
const captchaState = reactive<CaptchaInfo>({
|
||||
enable: false,
|
||||
key: '',
|
||||
img_base: '',
|
||||
})
|
||||
|
||||
// 防重复请求标志
|
||||
const isCaptchaLoading = ref(false)
|
||||
|
||||
// 获取验证码
|
||||
async function getLoginCaptcha() {
|
||||
if (isCaptchaLoading.value) {
|
||||
return
|
||||
}
|
||||
|
||||
isCaptchaLoading.value = true
|
||||
try {
|
||||
const result = await AuthAPI.getCaptcha()
|
||||
|
||||
if (result && typeof result === 'object') {
|
||||
captchaState.enable = Boolean(result.enable)
|
||||
captchaState.key = result.key || ''
|
||||
captchaState.img_base = result.img_base || result.img || ''
|
||||
if (captchaState.enable) {
|
||||
loginFormData.captcha = ''
|
||||
loginFormData.captcha_key = captchaState.key
|
||||
}
|
||||
}
|
||||
else {
|
||||
captchaState.enable = false
|
||||
}
|
||||
}
|
||||
catch (error: any) {
|
||||
console.error('获取验证码失败:', error)
|
||||
toast.error(error?.message || '验证码获取失败')
|
||||
captchaState.img_base = ''
|
||||
captchaState.enable = false
|
||||
}
|
||||
finally {
|
||||
isCaptchaLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 仅使用 query 作为重定向来源
|
||||
const redirect = ref('/pages/index/index')
|
||||
|
||||
// 页面加载时初始化
|
||||
onLoad((options) => {
|
||||
uni.setNavigationBarTitle({ title: '登录' })
|
||||
|
||||
// 处理重定向逻辑
|
||||
const fromQuery
|
||||
= options && options.redirect ? decodeURIComponent(options.redirect) : ''
|
||||
if (fromQuery && fromQuery !== '/pages/login/index') {
|
||||
redirect.value = fromQuery
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
getLoginCaptcha()
|
||||
})
|
||||
|
||||
// 页面挂载时确保验证码正确显示
|
||||
onMounted(() => {
|
||||
// 如果验证码未启用或未获取到,重新获取
|
||||
setTimeout(() => {
|
||||
if (!captchaState.enable || !captchaState.img_base) {
|
||||
getLoginCaptcha()
|
||||
}
|
||||
// 强制清除输入框背景色,优化视觉效果
|
||||
const inputs = document.querySelectorAll('input')
|
||||
inputs.forEach((input) => {
|
||||
input.style.backgroundColor = 'transparent'
|
||||
input.style.boxShadow = 'none'
|
||||
})
|
||||
}, 100)
|
||||
})
|
||||
|
||||
// 账号密码登录
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const { valid, errors } = await loginFormRef.value.validate()
|
||||
if (!valid)
|
||||
return toast.error(errors[0].message)
|
||||
|
||||
loading.value = true
|
||||
await userStore.login(loginFormData)
|
||||
toast.success('登录成功')
|
||||
|
||||
// 登录成功后跳转到指定页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
success: () => toast.success('跳转成功'),
|
||||
fail: err => toast.error(`跳转失败: ${err.errMsg}`),
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
catch (error: any) {
|
||||
toast.error(error?.message || '登录失败')
|
||||
getLoginCaptcha() // 刷新验证码
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 忘记密码
|
||||
async function handleForgotPassword() {
|
||||
toast.error('忘记密码功能开发中...')
|
||||
}
|
||||
|
||||
// 注册账号
|
||||
async function handleRegister() {
|
||||
toast.error('注册账号功能开发中...')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<!-- 背景图 -->
|
||||
<wd-img
|
||||
src="/static/images/login-bg.svg"
|
||||
mode="aspectFill"
|
||||
class="login-bg"
|
||||
/>
|
||||
|
||||
<view class="login-card">
|
||||
<!-- Logo和标题区域 -->
|
||||
<view class="header">
|
||||
<wd-img src="/static/logo.png" class="logo" mode="aspectFit" />
|
||||
<text class="title theme-text-inverse">
|
||||
FastApp管理系统
|
||||
</text>
|
||||
<text class="subtitle theme-text-inverse">
|
||||
欢迎使用移动端管理平台
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 登录表单区域 -->
|
||||
<wd-form
|
||||
ref="loginFormRef"
|
||||
:model="loginFormData"
|
||||
class="login-form"
|
||||
>
|
||||
<wd-cell-group border class="form-group">
|
||||
<wd-input
|
||||
v-model="loginFormData.username"
|
||||
label="账号"
|
||||
label-width="100px"
|
||||
prefix-icon="user"
|
||||
type="text"
|
||||
prop="username"
|
||||
placeholder="请输入账号"
|
||||
clearable
|
||||
center
|
||||
clear-trigger="focus"
|
||||
:rules="[{ required: true, message: '请输入账号' }]"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="loginFormData.password"
|
||||
label="密码"
|
||||
label-width="100px"
|
||||
prefix-icon="lock-on"
|
||||
type="text"
|
||||
prop="password"
|
||||
show-password
|
||||
placeholder="请输入密码"
|
||||
clear-trigger="focus"
|
||||
clearable
|
||||
center
|
||||
:rules="[{ required: true, message: '请输入密码' }]"
|
||||
/>
|
||||
<!-- 验证码输入框 -->
|
||||
<view v-if="captchaState.enable" class="captcha">
|
||||
<wd-input
|
||||
v-model="loginFormData.captcha"
|
||||
label="验证码"
|
||||
label-width="100px"
|
||||
prefix-icon="lock-on"
|
||||
class="captcha-input"
|
||||
type="text"
|
||||
prop="captcha"
|
||||
placeholder="请输入验证码"
|
||||
clear-trigger="focus"
|
||||
clearable
|
||||
center
|
||||
:rules="[{ required: true, message: '请输入验证码' }]"
|
||||
/>
|
||||
<wd-img
|
||||
:src="captchaState.img_base"
|
||||
class="captcha-image"
|
||||
mode="aspectFit"
|
||||
@click="getLoginCaptcha"
|
||||
/>
|
||||
<view
|
||||
v-if="!captchaState.img_base && captchaState.enable"
|
||||
@click="getLoginCaptcha"
|
||||
>
|
||||
<text>点击加载</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<wd-divider />
|
||||
<!-- 忘记密码和注册账号 -->
|
||||
<view class="auth-links">
|
||||
<!-- 记住我 -->
|
||||
<view class="remember-me">
|
||||
<wd-checkbox v-model="loginFormData.remember">
|
||||
<wd-text size="24rpx" text="记住我" />
|
||||
</wd-checkbox>
|
||||
</view>
|
||||
|
||||
<view class="forgot-password">
|
||||
<wd-text
|
||||
type="primary"
|
||||
text="忘记密码?"
|
||||
size="24rpx"
|
||||
@click="handleForgotPassword"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<view class="footer">
|
||||
<wd-button
|
||||
type="primary"
|
||||
size="large"
|
||||
block
|
||||
class="login-button"
|
||||
:loading="loading"
|
||||
loading-size="24"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ loading ? "登录中..." : "账号登录" }}
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<view class="register-link">
|
||||
<wd-text size="24rpx" text="您没有账号?" />
|
||||
<wd-text
|
||||
type="primary"
|
||||
text="注册账号"
|
||||
size="24rpx"
|
||||
@click="handleRegister"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 版权信息 -->
|
||||
<view class="copyright">
|
||||
<wd-text
|
||||
text="Copyright © 2025-2026 service.fastapiadmin.com 版权所有 陕ICP备2025069493号-1"
|
||||
size="20rpx"
|
||||
/>
|
||||
</view>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
.login-bg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
z-index: 2;
|
||||
margin-top: 40rpx;
|
||||
width: 92%;
|
||||
|
||||
.header {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 30rpx;
|
||||
|
||||
.logo {
|
||||
width: 250rpx;
|
||||
height: 250rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form {
|
||||
margin-top: 40rpx;
|
||||
padding: 24rpx;
|
||||
|
||||
.form-group {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.captcha {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.captcha-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.captcha-image {
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.auth-links {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.register-link {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
margin-top: 32rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,168 @@
|
||||
<!-- layout: tabbar -->
|
||||
<script lang="ts" setup>
|
||||
definePage({
|
||||
name: 'mine',
|
||||
layout: 'tabbar',
|
||||
style: {
|
||||
navigationBarTitleText: '我的',
|
||||
},
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const userInfo = computed(() => userStore.userInfo)
|
||||
const isLogin = computed(() => !!userInfo.value)
|
||||
const defaultAvatar = '/static/images/default-avatar.png'
|
||||
const isLoading = ref(false)
|
||||
|
||||
// 退出登录
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认退出登录吗?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
userStore.logout()
|
||||
toast.show('已退出登录')
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 页面跳转方法
|
||||
function navigateTo(name: string) {
|
||||
router.push({
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
function openUrl(url: string) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
|
||||
// 消息按钮点击处理
|
||||
function handleMessagesClick(e?: any) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
navigateToSection('messages')
|
||||
}
|
||||
|
||||
// 导航到各个板块
|
||||
function navigateToSection(section: string, subSection?: string, e?: any) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
// 这里可以根据需要实现具体的导航逻辑
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
// 确保 tabbar 状态正确
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 0) {
|
||||
const currentPage = pages[pages.length - 1]
|
||||
if (currentPage.route === 'pages/mine/index') {
|
||||
// 通过事件通知 tabbar 布局更新状态
|
||||
uni.$emit('updateTabbar', 'mine')
|
||||
}
|
||||
}
|
||||
|
||||
// 每次显示页面时都检查并刷新用户信息
|
||||
loadUserInfo()
|
||||
})
|
||||
|
||||
// 加载用户信息
|
||||
async function loadUserInfo() {
|
||||
if (isLogin.value) {
|
||||
isLoading.value = true
|
||||
try {
|
||||
await userStore.getInfo()
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取用户信息失败', error)
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听用户信息变化,确保数据及时更新
|
||||
watch(
|
||||
() => userInfo.value,
|
||||
() => {},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="p-3">
|
||||
<!-- 用户信息卡片 -->
|
||||
<wd-card type="rectangle">
|
||||
<wd-row class="flex items-center">
|
||||
<wd-col :span="6">
|
||||
<wd-badge v-if="true" model-value="99+" @click="handleMessagesClick">
|
||||
<wd-avatar
|
||||
size="medium"
|
||||
shape="round"
|
||||
:src="isLogin ? userInfo!.avatar : defaultAvatar"
|
||||
/>
|
||||
</wd-badge>
|
||||
</wd-col>
|
||||
<wd-col :span="16">
|
||||
<view class="mt-2 flex flex-col">
|
||||
<wd-text type="success" :text="userInfo?.name || '匿名用户'" size="16" bold />
|
||||
<wd-text :text="`ID: ${userInfo?.username}`" size="12" />
|
||||
</view>
|
||||
</wd-col>
|
||||
<wd-col :span="2">
|
||||
<wd-icon name="setting" size="22" color="#0083ff" @click="navigateTo('settings')" />
|
||||
</wd-col>
|
||||
</wd-row>
|
||||
</wd-card>
|
||||
|
||||
<!-- 常用工具 -->
|
||||
<wd-card type="rectangle" title="常用工具">
|
||||
<wd-grid clickable border :gutter="10" :column="4">
|
||||
<wd-grid-item is-dot icon="user" text="用户管理" clickable @click="navigateTo('user')" />
|
||||
<wd-grid-item is-dot icon="help-circle" text="岗位管理" clickable @click="navigateTo('position')" />
|
||||
<wd-grid-item is-dot icon="check-circle" text="角色管理" clickable @click="navigateTo('role')" />
|
||||
<wd-grid-item is-dot icon="info-circle" text="参数管理" clickable @click="navigateTo('parameter')" />
|
||||
<wd-grid-item is-dot icon="user" text="字典管理" clickable @click="navigateTo('dictionary')" />
|
||||
<wd-grid-item is-dot icon="help-circle" text="部门管理" clickable @click="navigateTo('department')" />
|
||||
<wd-grid-item is-dot icon="check-circle" text="菜单管理" clickable @click="navigateTo('role')" />
|
||||
<wd-grid-item is-dot icon="info-circle" text="日志管理" clickable @click="navigateTo('log')" />
|
||||
</wd-grid>
|
||||
</wd-card>
|
||||
|
||||
<!-- 推荐服务 -->
|
||||
<wd-card type="rectangle" title="推荐服务">
|
||||
<wd-cell-group border custom-class="rounded-2! overflow-hidden">
|
||||
<wd-cell title="项目官网" is-link @click="openUrl('https://service.fastapiadmin.com/')" />
|
||||
<wd-cell title="问题反馈" is-link @click="navigateTo('feedback')" />
|
||||
<wd-cell title="个人资料" is-link @click="navigateTo('profile')" />
|
||||
<wd-cell title="账号设置" is-link @click="navigateTo('account')" />
|
||||
</wd-cell-group>
|
||||
</wd-card>
|
||||
|
||||
<!-- 退出登录按钮 -->
|
||||
<wd-button v-if="isLogin" class="p-3" size="large" block @click="handleLogout">
|
||||
退出登录
|
||||
</wd-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
definePage({
|
||||
name: 'work',
|
||||
layout: 'tabbar',
|
||||
style: {
|
||||
navigationBarTitleText: '工作台',
|
||||
},
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 页面跳转方法
|
||||
function navigateTo(name: string) {
|
||||
router.push({
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
function openUrl(url: string) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="box-border py-3">
|
||||
<view class="mx-3 box-border rounded-3 bg-white px-4 py-6 text-center dark:bg-[var(--wot-dark-background2)]">
|
||||
<text class="mb-3 block text-left text-5 text-gray-800 font-bold dark:text-gray-200">
|
||||
Wot Starter
|
||||
</text>
|
||||
<text class="mb-3 block text-left text-30rpx text-gray-600 leading-relaxed dark:text-gray-300">
|
||||
⚡️ 基于 vitesse-uni-app 由 vite & uni-app 驱动的、深度整合 Wot UI 组件库的快速启动模板
|
||||
</text>
|
||||
<text class="block text-left text-3 text-gray-400 leading-relaxed dark:text-gray-400">
|
||||
背靠 Uni Helper、Wot UI 团队,告别 HBuilderX ,拥抱现代前端开发工具链
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<demo-block title="工具链介绍" transparent>
|
||||
<wd-cell-group border custom-class="rounded-2! overflow-hidden">
|
||||
<wd-cell title="🧩 WotUI组件库" is-link @click="openUrl('https://wot-ui.cn/')" />
|
||||
<wd-cell title="🧠 Agent Skills" is-link @click="navigateTo('skills')" />
|
||||
<wd-cell title="🚦 Router 路由管理" is-link @click="navigateTo('router')" />
|
||||
<wd-cell title="🌐 Alova 网络请求" is-link @click="navigateTo('request')" />
|
||||
<wd-cell title="🎨 Icon 图标" is-link @click="navigateTo('icon')" />
|
||||
<wd-cell title="✨ Unocss 原子化" is-link @click="navigateTo('styles')" />
|
||||
<wd-cell title="🍍 Pinia 持久化" is-link @click="navigateTo('pinia')" />
|
||||
<wd-cell title="🌱 CreateUni 脚手架" is-link @click="navigateTo('create-uni') " />
|
||||
<wd-cell title="🔄 CI/CD 持续集成" is-link @click="navigateTo('ci')" />
|
||||
<wd-cell title="🦾 uni-ku/root" is-link @click="navigateTo('root')" />
|
||||
<wd-cell title="📊 uni-echarts" is-link @click="navigateTo('echarts')" />
|
||||
</wd-cell-group>
|
||||
</demo-block>
|
||||
</view>
|
||||
</template>
|
||||
Reference in New Issue
Block a user