feat(api): 添加重置用户密码接口
This commit is contained in:
@@ -143,6 +143,20 @@ const UserAPI = {
|
||||
method: "DELETE",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param password 新密码
|
||||
*/
|
||||
resetPassword(userId: number | string, password: string) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/${userId}/password/reset`,
|
||||
method: "PUT",
|
||||
data: { password },
|
||||
});
|
||||
},
|
||||
};
|
||||
export default UserAPI;
|
||||
|
||||
|
||||
@@ -4,13 +4,6 @@ import { storeToRefs } from "pinia";
|
||||
|
||||
const themeStore = useThemeStore();
|
||||
const { theme, themeVars } = storeToRefs(themeStore);
|
||||
|
||||
// 获取系统状态栏高度
|
||||
const statusBarHeight = ref(0);
|
||||
onMounted(() => {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
statusBarHeight.value = systemInfo.statusBarHeight || 0;
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -24,25 +17,10 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-config-provider
|
||||
:theme-vars="themeVars"
|
||||
:theme="theme"
|
||||
:custom-class="`page-wraper ${theme}`"
|
||||
:style="{
|
||||
'--status-bar-height': statusBarHeight + 'px',
|
||||
}"
|
||||
>
|
||||
<wd-config-provider :theme-vars="themeVars" :custom-class="theme" :theme="theme">
|
||||
<slot />
|
||||
<wd-notify />
|
||||
<wd-toast />
|
||||
<wd-message-box />
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-wraper {
|
||||
box-sizing: border-box;
|
||||
min-height: calc(100vh - var(--window-top));
|
||||
background: var(--wot-color-bg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
<template>
|
||||
<wd-config-provider
|
||||
:theme-vars="themeVars"
|
||||
:custom-class="`page-wraper ${theme}`"
|
||||
:theme="theme"
|
||||
:style="{
|
||||
'--status-bar-height': statusBarHeight + 'px',
|
||||
}"
|
||||
>
|
||||
<wd-config-provider :theme-vars="themeVars" :custom-class="theme" :theme="theme">
|
||||
<slot />
|
||||
<wd-tabbar
|
||||
:model-value="activeTabbar.name"
|
||||
placeholder
|
||||
bordered
|
||||
safe-area-inset-bottom
|
||||
fixed
|
||||
@@ -43,9 +35,6 @@ const themeStore = useThemeStore();
|
||||
const { themeVars, theme } = storeToRefs(themeStore);
|
||||
const { activeTabbar, getTabbarItemValue, setTabbarItemActive, tabbarList } = useTabbar();
|
||||
|
||||
// 获取系统状态栏高度
|
||||
const statusBarHeight = ref(0);
|
||||
|
||||
function handleTabbarChange({ value }: { value: string }) {
|
||||
setTabbarItemActive(value);
|
||||
router.pushTab({ name: value });
|
||||
@@ -63,9 +52,6 @@ watch(
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
statusBarHeight.value = systemInfo.statusBarHeight || 0;
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
uni.hideTabBar();
|
||||
// #endif
|
||||
@@ -81,15 +67,3 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page-wraper {
|
||||
box-sizing: border-box;
|
||||
|
||||
// 页面最小高度 = 视窗高度 - 状态栏高度
|
||||
// --window-top: uni-app提供的状态栏高度CSS变量(包含状态栏+导航栏)
|
||||
min-height: calc(100vh - var(--window-top));
|
||||
|
||||
background: var(--wot-color-bg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="page page--tabbar">
|
||||
<!-- 轮播图 -->
|
||||
<view class="hero">
|
||||
<view class="relative">
|
||||
<wd-swiper
|
||||
v-model:current="current"
|
||||
custom-class="swiper-box"
|
||||
@@ -10,10 +10,11 @@
|
||||
@click="handleSwiperClick"
|
||||
@change="handleSwiperChange"
|
||||
/>
|
||||
<view class="hero-fade"></view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷导航 -->
|
||||
<view class="section section--overlay">
|
||||
<view class="section--overlay">
|
||||
<wd-grid clickable :column="4">
|
||||
<wd-grid-item
|
||||
v-for="(item, index) in quickNavList"
|
||||
@@ -30,7 +31,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 通知公告 -->
|
||||
<view class="section">
|
||||
<view class="m-24rpx">
|
||||
<view class="notice-bar" @click="handleNoticeClick">
|
||||
<view class="notice-bar__icon">
|
||||
<wd-icon name="check-outline" size="32rpx" color="#34D19D" />
|
||||
@@ -42,11 +43,11 @@
|
||||
</view>
|
||||
|
||||
<!-- 数据统计 -->
|
||||
<view class="section">
|
||||
<view class="stat-grid">
|
||||
<view class="stat-card stat-card--green">
|
||||
<image class="stat-card__bg" src="/static/icons/visitor.svg" mode="aspectFit" />
|
||||
<view class="stat-card__head">
|
||||
<view class="m-24rpx">
|
||||
<view class="grid grid-cols-2 gap-16rpx">
|
||||
<view class="stat-card gradient-bg--success">
|
||||
<image class="stat-card__icon" src="/static/icons/visitor.svg" mode="aspectFit" />
|
||||
<view class="stat-card__header">
|
||||
<text class="stat-card__label">访客数</text>
|
||||
<view class="stat-card__dot stat-card__dot--green"></view>
|
||||
</view>
|
||||
@@ -54,9 +55,9 @@
|
||||
{{ visitStatsData.todayUvCount }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="stat-card stat-card--blue">
|
||||
<image class="stat-card__bg" src="/static/icons/browser.svg" mode="aspectFit" />
|
||||
<view class="stat-card__head">
|
||||
<view class="stat-card gradient-bg--primary">
|
||||
<image class="stat-card__icon" src="/static/icons/browser.svg" mode="aspectFit" />
|
||||
<view class="stat-card__header">
|
||||
<text class="stat-card__label">浏览量</text>
|
||||
<view class="stat-card__dot stat-card__dot--blue"></view>
|
||||
</view>
|
||||
@@ -64,22 +65,15 @@
|
||||
{{ visitStatsData.todayPvCount }}
|
||||
</text>
|
||||
</view>
|
||||
<view v-if="appVersion" class="stat-card stat-card--orange stat-card--full">
|
||||
<view class="stat-card__head">
|
||||
<text class="stat-card__label">应用版本</text>
|
||||
<view class="stat-card__dot stat-card__dot--orange"></view>
|
||||
</view>
|
||||
<text class="stat-card__num stat-card__num--orange">{{ appVersion }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 访问趋势图表 -->
|
||||
<view class="section">
|
||||
<view class="m-24rpx">
|
||||
<wd-card custom-class="chart-card">
|
||||
<template #title>
|
||||
<view class="chart-header">
|
||||
<text class="chart-header__title">访问趋势</text>
|
||||
<view class="flex-between">
|
||||
<text class="text-28rpx font-semibold">访问趋势</text>
|
||||
<wd-radio-group
|
||||
v-model="recentDaysRange"
|
||||
shape="button"
|
||||
@@ -92,7 +86,7 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="chart-container">
|
||||
<view class="w-full h-600rpx">
|
||||
<qiun-data-charts type="area" :chartData="chartData" :opts="chartOpts" />
|
||||
</view>
|
||||
</wd-card>
|
||||
@@ -363,11 +357,7 @@ onReady(() => {
|
||||
</route>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hero {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hero::after {
|
||||
.hero-fade {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@@ -375,7 +365,6 @@ onReady(() => {
|
||||
z-index: 1;
|
||||
height: 120rpx;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(245, 247, 250, 0) 0%,
|
||||
@@ -384,67 +373,18 @@ onReady(() => {
|
||||
);
|
||||
}
|
||||
|
||||
:deep(.swiper-box) {
|
||||
height: 420rpx;
|
||||
overflow: hidden;
|
||||
border-bottom-right-radius: 32rpx;
|
||||
border-bottom-left-radius: 32rpx;
|
||||
}
|
||||
|
||||
:deep(.swiper-box),
|
||||
:deep(.swiper-box .wd-swiper__item),
|
||||
:deep(.swiper-box image) {
|
||||
height: 420rpx;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
background-color: rgba(255, 255, 255, 0.92);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 10rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 16rpx;
|
||||
}
|
||||
|
||||
&__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
&__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__search {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
margin: 24rpx;
|
||||
}
|
||||
|
||||
.section--overlay {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 18rpx 8rpx;
|
||||
margin-top: -140rpx;
|
||||
margin: 24rpx;
|
||||
margin-top: -120rpx;
|
||||
background: var(--color-bg);
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 16rpx 36rpx rgba(0, 0, 0, 0.08);
|
||||
@@ -473,8 +413,8 @@ onReady(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 24rpx 24rpx 20rpx;
|
||||
background: #fff;
|
||||
border: 1rpx solid rgba(0, 0, 0, 0.04);
|
||||
background: var(--color-bg);
|
||||
border: 1rpx solid var(--color-border);
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
@@ -486,7 +426,7 @@ onReady(() => {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 16rpx;
|
||||
background: rgba(52, 209, 157, 0.1);
|
||||
background: var(--color-success-light);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
@@ -500,52 +440,25 @@ onReady(() => {
|
||||
overflow: hidden;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
color: var(--color-text);
|
||||
-webkit-line-clamp: 1;
|
||||
line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 24rpx 20rpx;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border: 1rpx solid rgba(0, 0, 0, 0.04);
|
||||
background: var(--color-bg);
|
||||
border: 1rpx solid var(--color-border);
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
&--green {
|
||||
background: linear-gradient(135deg, #f0fdf9 0%, #fff 100%);
|
||||
}
|
||||
|
||||
&--blue {
|
||||
background: linear-gradient(135deg, #f0f7ff 0%, #fff 100%);
|
||||
}
|
||||
|
||||
&--orange {
|
||||
background: linear-gradient(135deg, #fffbf0 0%, #fff 100%);
|
||||
}
|
||||
|
||||
&__bg {
|
||||
position: absolute;
|
||||
right: -10rpx;
|
||||
bottom: -10rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
&__head {
|
||||
&__header {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
@@ -557,7 +470,16 @@ onReady(() => {
|
||||
&__label {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
right: -10rpx;
|
||||
bottom: -10rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
&__dot {
|
||||
@@ -590,18 +512,15 @@ onReady(() => {
|
||||
letter-spacing: -1rpx;
|
||||
|
||||
&--green {
|
||||
color: #2ab789;
|
||||
text-shadow: 0 4rpx 12rpx rgba(42, 183, 137, 0.3);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
&--blue {
|
||||
color: #3a8ee6;
|
||||
text-shadow: 0 4rpx 12rpx rgba(58, 142, 230, 0.3);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
&--orange {
|
||||
color: #e8a838;
|
||||
text-shadow: 0 4rpx 12rpx rgba(232, 168, 56, 0.3);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,45 +531,10 @@ onReady(() => {
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 20rpx;
|
||||
|
||||
.stat-card__head {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.stat-card__num {
|
||||
font-size: 28rpx;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&__title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
:deep(.chart-card) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.chart-card .wd-card) {
|
||||
margin: 0 !important;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
:deep(.chart-card .wd-card__body) {
|
||||
padding: 24rpx !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,98 +1,343 @@
|
||||
<template>
|
||||
<view class="page dark:text-[var(--wot-color-text)]">
|
||||
<view class="page page--padding">
|
||||
<wd-card>
|
||||
<view class="about-header">
|
||||
<view class="flex items-center p-20rpx">
|
||||
<wd-img
|
||||
width="120rpx"
|
||||
height="120rpx"
|
||||
src="/static/logo.png"
|
||||
mode="aspectFit"
|
||||
class="about-logo"
|
||||
class="mr-16rpx"
|
||||
/>
|
||||
<view class="about-meta">
|
||||
<text class="app-title">vue-uniapp-template</text>
|
||||
<view class="flex-1">
|
||||
<text class="app-title">youlai-app</text>
|
||||
<text class="app-version">版本 {{ version }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<!-- 公司信息区域 -->
|
||||
<wd-card custom-style="margin-top: 20rpx">
|
||||
<view class="company-section">
|
||||
<view class="section-title">
|
||||
<wd-icon name="company" size="18" />
|
||||
<text>有来开源组织</text>
|
||||
</view>
|
||||
<text class="section-subtitle">专注于快速构建和高效开发的应用解决方案</text>
|
||||
<view class="px-20rpx pb-20rpx">
|
||||
<text class="intro-title">项目介绍</text>
|
||||
<text class="intro-desc">
|
||||
一个基于 uni-app + Vue 3 + TypeScript 的移动端模板,内置主题系统与 wot-design-uni
|
||||
组件库。同时提供管理端与多后端版本(Java / Gin / Nest / Django / ThinkPHP /
|
||||
ASP.NET),并包含多租户实现。
|
||||
</text>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<!-- 项目列表 -->
|
||||
<wd-card title="优质项目" custom-style="margin-top: 20rpx">
|
||||
<wd-cell-group border>
|
||||
<wd-cell title="vue3-element-admin" icon="desktop">
|
||||
<view slot="label" class="project-desc">
|
||||
基于 Vue3 + Vite7 + TypeScript5 + Element-Plus + Pinia 构建的中后台管理模板
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-card title="仓库" custom-style="margin-top: 20rpx">
|
||||
<wd-collapse v-model="collapseActive" accordion>
|
||||
<wd-collapse-item title="Web 前端" name="web">
|
||||
<wd-cell-group border>
|
||||
<wd-cell
|
||||
title="vue3-element-admin"
|
||||
icon="desktop"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('vue3-element-admin')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="vue3-element-admin-js"
|
||||
icon="desktop"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('vue3-element-admin-js')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="vue3-element-template"
|
||||
icon="desktop"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('vue3-element-template')"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</wd-collapse-item>
|
||||
|
||||
<wd-cell title="vue-uniapp-template" icon="mobile">
|
||||
<view slot="label" class="project-desc">
|
||||
基于 uni-app + Vue 3 + TypeScript,集成多种工具的移动端应用模板
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-collapse-item title="移动端" name="mobile">
|
||||
<wd-cell-group border>
|
||||
<wd-cell
|
||||
title="youlai-app"
|
||||
icon="mobile"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-app')"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</wd-collapse-item>
|
||||
|
||||
<wd-cell title="youlai-boot" icon="server">
|
||||
<view slot="label" class="project-desc">
|
||||
基于 Spring Boot 3 + Vue 3 构建的前后端分离单体权限管理系统
|
||||
</view>
|
||||
</wd-cell>
|
||||
</wd-cell-group>
|
||||
<wd-collapse-item title="后端(6 种技术栈)" name="backend">
|
||||
<wd-cell-group border>
|
||||
<wd-cell
|
||||
title="youlai-boot"
|
||||
icon="server"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-boot')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="youlai-gin"
|
||||
icon="server"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-gin')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="youlai-nest"
|
||||
icon="server"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-nest')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="youlai-django"
|
||||
icon="server"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-django')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="youlai-think"
|
||||
icon="server"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-think')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="youlai-aspnet"
|
||||
icon="server"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-aspnet')"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</wd-collapse-item>
|
||||
|
||||
<wd-collapse-item title="多租户" name="tenant">
|
||||
<wd-cell-group border>
|
||||
<wd-cell
|
||||
title="youlai-boot-tenant"
|
||||
icon="server"
|
||||
title-width="200px"
|
||||
value="简介"
|
||||
clickable
|
||||
@click="openProjectIntro('youlai-boot-tenant')"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</wd-collapse-item>
|
||||
</wd-collapse>
|
||||
</wd-card>
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<wd-card title="联系我们" custom-style="margin-top: 20rpx">
|
||||
<wd-cell-group border>
|
||||
<wd-cell title="官方网站" value="www.youlai.tech" icon="link" />
|
||||
<wd-cell title="GitHub" value="github.com/youlaitech" icon="github" />
|
||||
<wd-cell title="联系邮箱" value="youlaitech@163.com" icon="mail" />
|
||||
<wd-cell
|
||||
title="官方网站"
|
||||
value="www.youlai.tech"
|
||||
icon="link"
|
||||
clickable
|
||||
@click="openUrl('https://www.youlai.tech')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="GitHub"
|
||||
value="github.com/youlaitech"
|
||||
icon="github"
|
||||
clickable
|
||||
@click="openUrl('https://github.com/youlaitech')"
|
||||
/>
|
||||
<wd-cell
|
||||
title="联系邮箱"
|
||||
value="youlaitech@163.com"
|
||||
icon="mail"
|
||||
clickable
|
||||
@click="copyText('youlaitech@163.com')"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</wd-card>
|
||||
|
||||
<!-- 底部版权信息 -->
|
||||
<view class="about-footer">
|
||||
<text class="copyright">Copyright © {{ getYear() }} 有来开源组织</text>
|
||||
<text class="copyright copyright-sub">All Rights Reserved</text>
|
||||
<view class="py-32rpx text-center">
|
||||
<text class="copyright">Copyright {{ getYear() }} 有来开源组织</text>
|
||||
<text class="copyright mt-8rpx">All Rights Reserved</text>
|
||||
</view>
|
||||
|
||||
<wd-popup v-model="introPopupVisible" position="bottom">
|
||||
<view class="p-24rpx">
|
||||
<view class="text-32rpx font-700">{{ currentProject?.title }}</view>
|
||||
<view v-if="currentProject?.stack" class="mt-12rpx text-26rpx text-secondary">
|
||||
技术栈:{{ currentProject.stack }}
|
||||
</view>
|
||||
<view v-if="currentProject?.summary" class="mt-12rpx text-26rpx text-secondary">
|
||||
{{ currentProject.summary }}
|
||||
</view>
|
||||
<view class="mt-20rpx">
|
||||
<wd-button type="primary" block @click="goCurrentProjectRepo">去仓库</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const version = ref("1.0.0");
|
||||
const version = ref(import.meta.env.VITE_APP_VERSION);
|
||||
const collapseActive = ref<string[]>(["web"]);
|
||||
|
||||
type ProjectIntroKey =
|
||||
| "vue3-element-admin"
|
||||
| "vue3-element-admin-js"
|
||||
| "vue3-element-template"
|
||||
| "youlai-app"
|
||||
| "youlai-boot"
|
||||
| "youlai-gin"
|
||||
| "youlai-nest"
|
||||
| "youlai-django"
|
||||
| "youlai-think"
|
||||
| "youlai-aspnet"
|
||||
| "youlai-boot-tenant";
|
||||
|
||||
type ProjectIntroItem = {
|
||||
title: string;
|
||||
stack: string;
|
||||
summary: string;
|
||||
repoUrl: string;
|
||||
};
|
||||
|
||||
const projectIntros: Record<ProjectIntroKey, ProjectIntroItem> = {
|
||||
"vue3-element-admin": {
|
||||
title: "vue3-element-admin",
|
||||
stack: "Vue 3.5 + Vite 7 + TypeScript + Element Plus",
|
||||
summary:
|
||||
"极简开箱即用的企业级后台管理模板,内置用户/角色/菜单/部门等基础模块,支持动态路由与按钮/数据权限,并提供暗黑模式、国际化与代码生成等能力。",
|
||||
repoUrl: "https://github.com/youlaitech/vue3-element-admin",
|
||||
},
|
||||
"vue3-element-admin-js": {
|
||||
title: "vue3-element-admin-js",
|
||||
stack: "Vue 3.5 + Vite 6 + JavaScript + Element Plus",
|
||||
summary:
|
||||
"vue3-element-admin 的 JavaScript 版本,保留核心后台能力与权限体系,适合更轻量的快速开发场景,并支持 Mock 与线上接口文档联调。",
|
||||
repoUrl: "https://github.com/youlaitech/vue3-element-admin-js",
|
||||
},
|
||||
"vue3-element-template": {
|
||||
title: "vue3-element-template",
|
||||
stack: "Vue 3.5 + Vite 6 + TypeScript 5 + Element Plus + Pinia",
|
||||
summary:
|
||||
"从 vue3-element-admin 抽离出的精简中后台模板,预置工程化规范与常用业务基座,专注高频功能与更低的二次开发成本。",
|
||||
repoUrl: "https://github.com/youlaitech/vue3-element-template",
|
||||
},
|
||||
"youlai-app": {
|
||||
title: "youlai-app",
|
||||
stack: "uni-app + Vue 3 + TypeScript + UnoCSS + wot-design-uni",
|
||||
summary:
|
||||
"跨平台移动端模板,内置代码规范与工程化工具链,适配 H5 与微信小程序等多端场景,并提供可复用的基础/业务组件体系。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-app",
|
||||
},
|
||||
"youlai-boot": {
|
||||
title: "youlai-boot",
|
||||
stack: "JDK 17 + Spring Boot 4 + Spring Security + JWT + Redis + MySQL",
|
||||
summary:
|
||||
"配套 vue3-element-admin 的 Java 后端实现,提供 RBAC 权限体系与用户/角色/菜单等核心模块,支持令牌续期与多端会话治理,并内置代码生成能力。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-boot",
|
||||
},
|
||||
"youlai-gin": {
|
||||
title: "youlai-gin",
|
||||
stack: "Go 1.21+ + Gin + GORM + JWT + Redis + MySQL",
|
||||
summary:
|
||||
"配套 vue3-element-admin 的 Go/Gin 后端实现,轻量高性能,提供 RBAC 权限模型与常用系统模块,支持 JWT 与 Redis 会话管理。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-gin",
|
||||
},
|
||||
"youlai-nest": {
|
||||
title: "youlai-nest",
|
||||
stack: "Node.js 20+ + NestJS 11 + TypeScript + TypeORM + JWT + Redis + MySQL",
|
||||
summary:
|
||||
"配套 vue3-element-admin 的 Node.js 后端实现,基于 NestJS 的企业级架构,支持 JWT 与 Redis Token 两种会话模式,并提供完整的 RBAC 权限与核心系统模块。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-nest",
|
||||
},
|
||||
"youlai-django": {
|
||||
title: "youlai-django",
|
||||
stack: "Python 3.12+ + Django 6 + DRF + SimpleJWT + Redis + MySQL",
|
||||
summary:
|
||||
"配套 vue3-element-admin 的 Python 后端实现,基于 DRF 构建,提供 RBAC 权限模型与核心系统模块,采用 JWT 认证并结合 Redis 管理会话。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-django",
|
||||
},
|
||||
"youlai-think": {
|
||||
title: "youlai-think",
|
||||
stack: "PHP 8 + ThinkPHP 8 + JWT + Redis + MySQL",
|
||||
summary:
|
||||
"配套 vue3-element-admin 的 PHP 后端实现,上手成本低,提供 RBAC 权限体系与常用系统模块,支持 JWT 与 Redis 会话双模式。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-think",
|
||||
},
|
||||
"youlai-aspnet": {
|
||||
title: "youlai-aspnet",
|
||||
stack: ".NET 8 + ASP.NET Core 8 + JWT + Redis + MySQL",
|
||||
summary:
|
||||
"配套 vue3-element-admin 的 .NET 后端实现,性能与生态同步升级,提供 RBAC 权限治理与核心系统模块,并支持 JWT 与 Redis 会话模式。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-aspnet",
|
||||
},
|
||||
"youlai-boot-tenant": {
|
||||
title: "youlai-boot-tenant",
|
||||
stack: "JDK 17 + Spring Boot 3 + Spring Security + MyBatis-Plus + JWT + Redis + MySQL",
|
||||
summary:
|
||||
"youlai-boot 的多租户版本,基于单库多租户方案实现租户隔离,提供精细化 RBAC 权限控制,适合 SaaS 场景的快速落地。",
|
||||
repoUrl: "https://github.com/youlaitech/youlai-boot-tenant",
|
||||
},
|
||||
};
|
||||
|
||||
const introPopupVisible = ref(false);
|
||||
const currentProjectKey = ref<ProjectIntroKey>("vue3-element-admin");
|
||||
const currentProject = computed(() => projectIntros[currentProjectKey.value]);
|
||||
|
||||
const openProjectIntro = (key: ProjectIntroKey) => {
|
||||
currentProjectKey.value = key;
|
||||
introPopupVisible.value = true;
|
||||
};
|
||||
|
||||
const goCurrentProjectRepo = () => {
|
||||
if (!currentProject.value) return;
|
||||
openUrl(currentProject.value.repoUrl);
|
||||
};
|
||||
const getYear = () => {
|
||||
return new Date().getFullYear();
|
||||
};
|
||||
|
||||
const openUrl = (url: string) => {
|
||||
// #ifdef H5
|
||||
window.open(url, "_blank");
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
uni.setClipboardData({ data: url });
|
||||
// #endif
|
||||
};
|
||||
|
||||
const copyText = (text: string) => {
|
||||
uni.setClipboardData({ data: text });
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
version.value = uni.getSystemInfoSync().appVersion;
|
||||
const appVersion = uni.getSystemInfoSync().appVersion;
|
||||
if (appVersion) {
|
||||
version.value = appVersion;
|
||||
}
|
||||
// #endif
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.about-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.about-logo {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.about-meta {
|
||||
flex: 1;
|
||||
.page {
|
||||
:deep(.wd-cell__title) {
|
||||
max-width: 360rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.app-title {
|
||||
@@ -103,12 +348,23 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.app-version {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.company-section {
|
||||
padding: 24rpx;
|
||||
.intro-title {
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.intro-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
@@ -127,27 +383,12 @@ onMounted(() => {
|
||||
display: block;
|
||||
padding-left: 32rpx;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.project-desc {
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.about-footer {
|
||||
padding: 32rpx 0;
|
||||
text-align: center;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.copyright {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.copyright-sub {
|
||||
margin-top: 8rpx;
|
||||
color: var(--color-text-placeholder);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page dark:text-[var(--wot-color-text)]">
|
||||
<view class="page page--tabbar">
|
||||
<view class="mine-hero" :style="{ paddingTop: `${navbar.totalHeight.value}px` }">
|
||||
<!-- 蓝色背景 -->
|
||||
<view class="mine-hero__bg" :style="{ background: headerBackground }" />
|
||||
@@ -100,19 +100,23 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="quickCards.length" class="quick-cards">
|
||||
<view
|
||||
v-for="item in quickCards"
|
||||
:key="item.title"
|
||||
class="quick-card"
|
||||
@click="item.action"
|
||||
>
|
||||
<view class="quick-card__icon" :style="{ background: item.tint }">
|
||||
<wd-icon :name="item.icon" size="28" :color="item.iconColor || '#1e293b'" />
|
||||
<view class="quick-cards">
|
||||
<view class="quick-card" @click="openProfile">
|
||||
<view class="quick-card__icon" style="background: #dbeafe">
|
||||
<wd-icon name="user" size="28" color="#1e40af" />
|
||||
</view>
|
||||
<view class="quick-card__body">
|
||||
<text class="quick-card__title">{{ item.title }}</text>
|
||||
<text class="quick-card__desc">{{ item.desc }}</text>
|
||||
<text class="quick-card__title">我的资料</text>
|
||||
<text class="quick-card__desc">修改个人信息</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="quick-card" @click="openAccount">
|
||||
<view class="quick-card__icon" style="background: #d1fae5">
|
||||
<wd-icon name="secured" size="28" color="#065f46" />
|
||||
</view>
|
||||
<view class="quick-card__body">
|
||||
<text class="quick-card__title">账号安全</text>
|
||||
<text class="quick-card__desc">修改密码 / 账号绑定</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -123,20 +127,25 @@
|
||||
<view class="section-card">
|
||||
<text class="section-title">系统工具</text>
|
||||
<view class="menu-list menu-list--flat">
|
||||
<view
|
||||
v-for="item in settingsItems"
|
||||
:key="item.title"
|
||||
class="menu-row"
|
||||
@click="item.action"
|
||||
>
|
||||
<view class="menu-row__icon" :style="{ background: item.tint }">
|
||||
<wd-icon :name="item.icon" size="18" :color="item.iconColor || '#1e293b'" />
|
||||
<view class="menu-row" @click="openNetworkTest">
|
||||
<view class="menu-row__icon" style="background: #fed7aa">
|
||||
<wd-icon name="server" size="18" color="#c2410c" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">{{ item.title }}</text>
|
||||
<text class="menu-row__desc">{{ item.desc }}</text>
|
||||
<text class="menu-row__title">网络检测</text>
|
||||
<text class="menu-row__desc">检测接口连通性</text>
|
||||
</view>
|
||||
<text v-if="item.value" class="menu-row__value">{{ item.value }}</text>
|
||||
<wd-icon name="arrow-right" size="16" color="#94a3b8" />
|
||||
</view>
|
||||
<view class="menu-row" @click="handleClearCache">
|
||||
<view class="menu-row__icon" style="background: #fecdd3">
|
||||
<wd-icon name="delete-thin" size="18" color="#be123c" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">清理缓存</text>
|
||||
<text class="menu-row__desc">显示当前缓存大小</text>
|
||||
</view>
|
||||
<text class="menu-row__value">{{ cacheSize }}</text>
|
||||
<wd-icon name="arrow-right" size="16" color="#94a3b8" />
|
||||
</view>
|
||||
</view>
|
||||
@@ -145,15 +154,25 @@
|
||||
<view class="section-card">
|
||||
<text class="section-title">帮助与支持</text>
|
||||
<view class="menu-list menu-list--flat">
|
||||
<view v-for="item in helpItems" :key="item.title" class="menu-row" @click="item.action">
|
||||
<view class="menu-row__icon" :style="{ background: item.tint }">
|
||||
<wd-icon :name="item.icon" size="18" :color="item.iconColor || '#1e293b'" />
|
||||
<view class="menu-row" @click="openUserAgreement">
|
||||
<view class="menu-row__icon" style="background: #d1fae5">
|
||||
<wd-icon name="secured" size="18" color="#065f46" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">{{ item.title }}</text>
|
||||
<text class="menu-row__desc">{{ item.desc }}</text>
|
||||
<text class="menu-row__title">用户协议</text>
|
||||
<text class="menu-row__desc">了解产品使用规则</text>
|
||||
</view>
|
||||
<text v-if="item.value" class="menu-row__value">{{ item.value }}</text>
|
||||
<wd-icon name="arrow-right" size="16" color="#94a3b8" />
|
||||
</view>
|
||||
<view class="menu-row" @click="openAbout">
|
||||
<view class="menu-row__icon" style="background: #ccfbf1">
|
||||
<wd-icon name="info-circle" size="18" color="#0f766e" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">关于系统</text>
|
||||
<text class="menu-row__desc">产品介绍与联系方式</text>
|
||||
</view>
|
||||
<text class="menu-row__value">v{{ appVersion }}</text>
|
||||
<wd-icon name="arrow-right" size="16" color="#94a3b8" />
|
||||
</view>
|
||||
</view>
|
||||
@@ -426,83 +445,6 @@ const openOfficialAccount = () => {
|
||||
url: "/pages/mine/official/index",
|
||||
});
|
||||
};
|
||||
|
||||
type ActionItem = {
|
||||
title: string;
|
||||
desc?: string;
|
||||
icon: string;
|
||||
tint: string;
|
||||
iconColor?: string;
|
||||
value?: string;
|
||||
action: () => void;
|
||||
};
|
||||
|
||||
const quickCards = computed<ActionItem[]>(() => {
|
||||
const items: ActionItem[] = [
|
||||
{
|
||||
title: "我的资料",
|
||||
desc: "修改个人信息",
|
||||
icon: "user",
|
||||
tint: "#dbeafe",
|
||||
iconColor: "#1e40af",
|
||||
action: openProfile,
|
||||
},
|
||||
{
|
||||
title: "账号安全",
|
||||
desc: "修改密码 / 账号绑定",
|
||||
icon: "secured",
|
||||
tint: "#d1fae5",
|
||||
iconColor: "#065f46",
|
||||
action: openAccount,
|
||||
},
|
||||
];
|
||||
|
||||
if (isLogin.value) {
|
||||
return items;
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
const settingsItems = computed<ActionItem[]>(() => [
|
||||
{
|
||||
title: "网络检测",
|
||||
desc: "检测接口连通性",
|
||||
icon: "server",
|
||||
tint: "#fed7aa",
|
||||
iconColor: "#c2410c",
|
||||
action: openNetworkTest,
|
||||
},
|
||||
{
|
||||
title: "清理缓存",
|
||||
desc: "显示当前缓存大小",
|
||||
icon: "delete-thin",
|
||||
tint: "#fecdd3",
|
||||
iconColor: "#be123c",
|
||||
value: cacheSize.value,
|
||||
action: handleClearCache,
|
||||
},
|
||||
]);
|
||||
|
||||
const helpItems = computed<ActionItem[]>(() => [
|
||||
{
|
||||
title: "用户协议",
|
||||
desc: "了解产品使用规则",
|
||||
icon: "secured",
|
||||
tint: "#d1fae5",
|
||||
iconColor: "#065f46",
|
||||
action: openUserAgreement,
|
||||
},
|
||||
{
|
||||
title: "关于系统",
|
||||
desc: "产品介绍与联系方式",
|
||||
icon: "info-circle",
|
||||
tint: "#ccfbf1",
|
||||
iconColor: "#0f766e",
|
||||
value: `v${appVersion.value}`,
|
||||
action: openAbout,
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -839,7 +781,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
margin-top: 14rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: var(--wot-color-text, #2f3a4a);
|
||||
color: var(--color-text);
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
@@ -854,7 +796,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
overflow: hidden;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
color: var(--wot-color-text-secondary, #64748b);
|
||||
color: var(--color-text-secondary);
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
@@ -862,7 +804,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
|
||||
.section-card {
|
||||
padding: 28rpx;
|
||||
background: var(--wot-color-white, #fff);
|
||||
background: var(--color-bg);
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 0 10rpx 30rpx rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
@@ -876,7 +818,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
margin-bottom: 18rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: var(--wot-color-text, #2f3a4a);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.quick-card {
|
||||
@@ -884,7 +826,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
gap: 20rpx;
|
||||
align-items: center;
|
||||
padding: 28rpx 24rpx;
|
||||
background: var(--wot-color-white, #fff);
|
||||
background: var(--color-bg);
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.04);
|
||||
transition: all 0.2s ease;
|
||||
@@ -915,7 +857,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.35;
|
||||
color: var(--wot-color-text, #2f3a4a);
|
||||
color: var(--color-text);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -926,13 +868,14 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
overflow: hidden;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.6;
|
||||
color: var(--wot-color-text-secondary, #64748b);
|
||||
color: var(--color-text-secondary);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu-row__icon {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 72rpx;
|
||||
@@ -945,7 +888,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
z-index: 0;
|
||||
padding: 0 28rpx calc(env(safe-area-inset-bottom) + 40rpx);
|
||||
margin-top: 0;
|
||||
background: var(--wot-color-bg, #f8fafc);
|
||||
background: var(--color-bg-secondary);
|
||||
border-top-left-radius: 48rpx;
|
||||
border-top-right-radius: 48rpx;
|
||||
}
|
||||
@@ -954,7 +897,7 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
padding: 0 28rpx;
|
||||
margin-top: 24rpx;
|
||||
overflow: hidden;
|
||||
background: var(--wot-color-white, #fff);
|
||||
background: var(--color-bg);
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 0 16rpx 40rpx rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
@@ -978,12 +921,6 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
border-top: 1rpx solid rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.menu-row__icon {
|
||||
flex-shrink: 0;
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
|
||||
.menu-row__main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@@ -993,20 +930,20 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: var(--wot-color-text, #2f3a4a);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.menu-row__desc {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: var(--wot-color-text-secondary, #64748b);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.menu-row__value {
|
||||
margin-left: 12rpx;
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
color: var(--color-text-placeholder);
|
||||
}
|
||||
|
||||
.logout-section {
|
||||
@@ -1028,8 +965,8 @@ const helpItems = computed<ActionItem[]>(() => [
|
||||
width: 100%;
|
||||
height: 88rpx !important;
|
||||
font-size: 28rpx !important;
|
||||
color: #ef4444 !important;
|
||||
background: var(--wot-color-white, #fff) !important;
|
||||
color: var(--color-danger) !important;
|
||||
background: var(--color-bg) !important;
|
||||
border: 1rpx solid rgba(239, 68, 68, 0.18) !important;
|
||||
border-radius: 24rpx !important;
|
||||
box-shadow: 0 12rpx 30rpx rgba(239, 68, 68, 0.08);
|
||||
|
||||
@@ -1,131 +1,105 @@
|
||||
<template>
|
||||
<view class="page-container dark:text-[var(--wot-color-text)]">
|
||||
<view class="page page--padding">
|
||||
<!-- 暗黑模式设置 -->
|
||||
<wd-card custom-class="mx-30rpx my-20rpx">
|
||||
<template #header>
|
||||
<view class="flex-start">
|
||||
<wd-icon name="moon" size="20" />
|
||||
<text class="ml-12rpx text-28rpx font-600">外观模式</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<wd-cell-group title="外观模式" border>
|
||||
<wd-cell title="暗黑模式" :value="isDarkMode ? '已开启' : '已关闭'">
|
||||
<wd-switch :model-value="isDarkMode" @change="handleToggleDarkMode" />
|
||||
</wd-cell>
|
||||
</wd-card>
|
||||
</wd-cell-group>
|
||||
|
||||
<!-- 主题色设置 -->
|
||||
<wd-card custom-class="mx-30rpx my-20rpx">
|
||||
<template #header>
|
||||
<view class="flex-start">
|
||||
<wd-icon name="palette" size="20" />
|
||||
<text class="ml-12rpx text-28rpx font-600">主题色彩</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<wd-cell-group title="主题色彩" border custom-class="mt-24rpx">
|
||||
<!-- 预设颜色选择 -->
|
||||
<view class="theme-colors">
|
||||
<view class="theme-colors__label">预设主题色</view>
|
||||
<wd-grid :column="3" border>
|
||||
<wd-grid-item
|
||||
v-for="(color, index) in themeColorOptions"
|
||||
:key="index"
|
||||
@click="handleSelectColor(color)"
|
||||
>
|
||||
<view class="theme-colors__item">
|
||||
<view
|
||||
class="theme-colors__preview"
|
||||
:class="{ 'theme-colors__preview--active': currentThemeColor === color.primary }"
|
||||
:style="{ backgroundColor: color.primary }"
|
||||
>
|
||||
<text v-if="currentThemeColor === color.primary" class="theme-colors__check">
|
||||
✓
|
||||
</text>
|
||||
</view>
|
||||
<text class="theme-colors__name">{{ color.name }}</text>
|
||||
</view>
|
||||
</wd-grid-item>
|
||||
</wd-grid>
|
||||
</view>
|
||||
|
||||
<!-- 当前主题色显示 -->
|
||||
<view class="current-theme">
|
||||
<view class="current-theme__item">
|
||||
<view class="current-theme__info">
|
||||
<wd-cell title="预设主题色">
|
||||
<template #default>
|
||||
<view class="flex gap-16rpx">
|
||||
<view
|
||||
class="current-theme__preview"
|
||||
:style="{ backgroundColor: currentThemeColor }"
|
||||
></view>
|
||||
<text class="current-theme__label">当前主题色</text>
|
||||
v-for="(color, index) in themeColorOptions"
|
||||
:key="index"
|
||||
class="color-option"
|
||||
:class="{ 'color-option--active': currentThemeColor === color.primary }"
|
||||
:style="{ backgroundColor: color.primary }"
|
||||
@click="handleSelectColor(color)"
|
||||
>
|
||||
<wd-icon
|
||||
v-if="currentThemeColor === color.primary"
|
||||
name="check"
|
||||
size="14"
|
||||
color="#fff"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<text class="current-theme__value">{{ currentThemeColor }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
|
||||
<!-- 当前主题色 -->
|
||||
<wd-cell title="当前主题色">
|
||||
<template #default>
|
||||
<view class="flex items-center gap-12rpx">
|
||||
<view class="color-dot" :style="{ backgroundColor: currentThemeColor }" />
|
||||
<text class="text-24rpx color-text-secondary">{{ currentThemeColor }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
|
||||
<!-- 自定义颜色 -->
|
||||
<wd-cell title="自定义颜色" is-link @click="showCustomInput">
|
||||
<template #icon>
|
||||
<wd-icon name="edit" size="16" />
|
||||
<wd-icon name="edit" size="16" class="mr-12rpx" />
|
||||
</template>
|
||||
</wd-cell>
|
||||
</wd-card>
|
||||
</wd-cell-group>
|
||||
|
||||
<!-- 效果预览 -->
|
||||
<wd-card title="效果预览" custom-class="mx-30rpx my-20rpx">
|
||||
<wd-grid :column="3" border>
|
||||
<wd-grid-item>
|
||||
<view class="flex-col-center py-20rpx">
|
||||
<wd-cell-group title="效果预览" border custom-class="mt-24rpx">
|
||||
<wd-cell>
|
||||
<template #default>
|
||||
<view class="flex gap-24rpx py-12rpx">
|
||||
<wd-button type="primary" size="small">主要按钮</wd-button>
|
||||
</view>
|
||||
</wd-grid-item>
|
||||
<wd-grid-item>
|
||||
<view class="flex-col-center py-20rpx">
|
||||
<text class="text-28rpx font-500" :style="{ color: currentThemeColor }">
|
||||
主题色文本
|
||||
</text>
|
||||
</view>
|
||||
</wd-grid-item>
|
||||
<wd-grid-item>
|
||||
<view class="flex-col-center py-20rpx">
|
||||
<wd-tag type="primary" size="small">标签</wd-tag>
|
||||
</view>
|
||||
</wd-grid-item>
|
||||
</wd-grid>
|
||||
</wd-card>
|
||||
</template>
|
||||
</wd-cell>
|
||||
</wd-cell-group>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="p-30rpx">
|
||||
<view class="mt-32rpx">
|
||||
<wd-button type="info" size="large" block @click="handleResetTheme">重置为默认主题</wd-button>
|
||||
</view>
|
||||
|
||||
<!-- 自定义颜色输入弹窗 -->
|
||||
<wd-popup v-model="showCustomColorInput" position="bottom" :safe-area-inset-bottom="true">
|
||||
<view class="custom-color-popup">
|
||||
<view class="custom-color-popup__header">
|
||||
<text class="custom-color-popup__title">自定义主题色</text>
|
||||
<wd-icon name="close" size="20" @click="showCustomColorInput = false" />
|
||||
</view>
|
||||
|
||||
<view class="custom-color-popup__content">
|
||||
<text class="custom-color-popup__label">请输入十六进制颜色值</text>
|
||||
<view class="custom-color-popup__input-row">
|
||||
<view
|
||||
class="custom-color-popup__preview"
|
||||
:style="{ backgroundColor: customColor || '#FF4757' }"
|
||||
></view>
|
||||
<wd-input
|
||||
v-model="customColor"
|
||||
placeholder="例如: #FF4757"
|
||||
:maxlength="7"
|
||||
custom-class="custom-color-popup__input"
|
||||
/>
|
||||
<wd-popup
|
||||
v-model="showCustomColorInput"
|
||||
position="bottom"
|
||||
custom-style="border-radius: 24rpx 24rpx 0 0"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">自定义主题色</text>
|
||||
<view
|
||||
class="w-64rpx h-64rpx flex-center rounded-full"
|
||||
hover-class="bg-[var(--color-text-placeholder)]/16"
|
||||
@click="showCustomColorInput = false"
|
||||
>
|
||||
<wd-icon name="close" size="18" class="color-text-secondary" />
|
||||
</view>
|
||||
<text class="custom-color-popup__tip">支持格式:#RGB 或 #RRGGBB</text>
|
||||
</view>
|
||||
|
||||
<view class="custom-color-popup__actions">
|
||||
<wd-button type="info" size="large" @click="showCustomColorInput = false">取消</wd-button>
|
||||
<wd-button type="primary" size="large" @click="applyCustomColor">应用</wd-button>
|
||||
<view class="p-4">
|
||||
<text class="text-28rpx color-text-secondary">请输入十六进制颜色值</text>
|
||||
<view class="flex items-center gap-16rpx mt-16rpx">
|
||||
<view class="color-dot-lg" :style="{ backgroundColor: customColor || '#FF4757' }" />
|
||||
<wd-input v-model="customColor" placeholder="例如: #FF4757" :maxlength="7" />
|
||||
</view>
|
||||
<text class="text-24rpx color-text-placeholder mt-12rpx">支持格式:#RGB 或 #RRGGBB</text>
|
||||
</view>
|
||||
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="showCustomColorInput = false">取消</wd-button>
|
||||
<wd-button type="primary" @click="applyCustomColor">应用</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
@@ -262,152 +236,57 @@ onShow(() => {
|
||||
</route>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 主题颜色选择区域
|
||||
.theme-colors {
|
||||
padding: 30rpx;
|
||||
.color-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s;
|
||||
|
||||
&__label {
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: var(--wot-color-text-secondary);
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
&__preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
margin-bottom: 8rpx;
|
||||
border: 2rpx solid var(--wot-color-border);
|
||||
border-radius: 12rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&--active {
|
||||
border-color: var(--wot-color-theme);
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.2);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
&__check {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: 22rpx;
|
||||
color: var(--wot-color-text-secondary);
|
||||
text-align: center;
|
||||
&--active {
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
// 当前主题色显示区域
|
||||
.current-theme {
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid var(--wot-color-border);
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__preview {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
border: 2rpx solid var(--wot-color-border);
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 28rpx;
|
||||
color: var(--wot-color-text);
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: var(--wot-color-text-secondary);
|
||||
}
|
||||
.color-dot {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
// 自定义颜色弹窗
|
||||
.custom-color-popup {
|
||||
min-height: 400rpx;
|
||||
background-color: var(--wot-color-bg);
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
.color-dot-lg {
|
||||
flex-shrink: 0;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid var(--wot-color-border);
|
||||
}
|
||||
.popup-content {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: var(--wot-color-text);
|
||||
}
|
||||
.popup-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 32rpx;
|
||||
border-bottom: 1rpx solid var(--color-border);
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 40rpx 30rpx;
|
||||
}
|
||||
.popup-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: var(--wot-color-text);
|
||||
}
|
||||
|
||||
&__input-row {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
&__preview {
|
||||
flex-shrink: 0;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 2rpx solid var(--wot-color-border);
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
&__input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__tip {
|
||||
font-size: 24rpx;
|
||||
color: var(--wot-color-text-placeholder);
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
padding: 20rpx 30rpx 30rpx;
|
||||
border-top: 1rpx solid var(--wot-color-border);
|
||||
}
|
||||
.popup-actions {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
border-top: 1rpx solid var(--color-border);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="work">
|
||||
<view class="page page--padding page--tabbar">
|
||||
<template v-for="(item, index) in visibleGridList" :key="index">
|
||||
<wd-card :title="item.title">
|
||||
<wd-grid clickable :column="4">
|
||||
@@ -87,15 +87,3 @@ function handleNavClick(item: any) {
|
||||
"layout": "tabbar"
|
||||
}
|
||||
</route>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* stylelint-disable selector-type-no-unknown */
|
||||
page {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
/* stylelint-enable selector-type-no-unknown */
|
||||
|
||||
.work {
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
@@ -19,7 +19,7 @@
|
||||
>
|
||||
<!-- 自定义节点内容:ID + 名称 + 状态 -->
|
||||
<template #content="{ node }">
|
||||
<view class="flex-1 flex flex-nowrap items-center gap-16rpx">
|
||||
<view class="menu-node">
|
||||
<text class="w-120rpx text-24rpx color-text-secondary">{{ node.id }}</text>
|
||||
<wd-icon v-if="node.icon" :name="node.icon" size="16" class="color-primary" />
|
||||
<text class="flex-1 truncate">{{ node.name }}</text>
|
||||
@@ -401,4 +401,12 @@ export default { options: { styleIsolation: "shared" } };
|
||||
gap: 24rpx;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.menu-node {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-wrap: nowrap;
|
||||
gap: 16rpx;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.title"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page page--padding page--pt">
|
||||
<view class="page page--padding">
|
||||
<view>
|
||||
<wd-search
|
||||
v-model="queryParams.keywords"
|
||||
@@ -389,6 +389,12 @@ function showUserActions(item: UserPageVO) {
|
||||
const actions: string[] = [];
|
||||
const actionMap: Record<string, () => void> = {};
|
||||
|
||||
// 重置密码
|
||||
if (hasPermission("sys:user:reset-password")) {
|
||||
actions.push("重置密码");
|
||||
actionMap["重置密码"] = () => handleResetPassword(item);
|
||||
}
|
||||
|
||||
// 编辑
|
||||
if (hasPermission("sys:user:update")) {
|
||||
actions.push("编辑");
|
||||
@@ -425,6 +431,28 @@ function showUserActions(item: UserPageVO) {
|
||||
});
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
async function handleResetPassword(item: UserPageVO) {
|
||||
const { confirm, content: password } = await uni.showModal({
|
||||
title: "重置密码",
|
||||
editable: true,
|
||||
placeholderText: `请输入用户「${item.username}」的新密码`,
|
||||
});
|
||||
if (!confirm || !password) return;
|
||||
|
||||
if (password.length < 6) {
|
||||
toast.error("密码至少需要6位字符");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await UserAPI.resetPassword(item.id, password);
|
||||
toast.success("密码重置成功");
|
||||
} catch (error) {
|
||||
// API 已处理错误提示
|
||||
}
|
||||
}
|
||||
|
||||
onReachBottom(() => {
|
||||
if (queryParams.pageNum * queryParams.pageSize < total.value) {
|
||||
fetchUserList();
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
@use "./theme";
|
||||
|
||||
// ==========================================================================
|
||||
// 全局基础样式
|
||||
// 全局基础样式(uni-app 跨端兼容)
|
||||
// ==========================================================================
|
||||
|
||||
// 页面根元素,小程序和 H5 通用
|
||||
page {
|
||||
font-size: 28rpx;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
// H5 端字体平滑渲染
|
||||
/* #ifdef H5 */
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
body {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -20,13 +29,10 @@ body,
|
||||
"Helvetica Neue",
|
||||
Helvetica,
|
||||
sans-serif;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
// ==========================================================================
|
||||
// 页面容器
|
||||
@@ -34,20 +40,18 @@ body,
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background-color: var(--color-bg-page, var(--color-bg-secondary));
|
||||
}
|
||||
|
||||
.page--auto {
|
||||
min-height: auto;
|
||||
background-color: var(--color-bg-secondary);
|
||||
}
|
||||
|
||||
// 带内边距的页面
|
||||
.page--padding {
|
||||
padding: 0 32rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 16rpx 32rpx 0;
|
||||
}
|
||||
|
||||
.page--pt {
|
||||
box-sizing: border-box;
|
||||
padding-top: 16rpx;
|
||||
// tabbar 页面底部安全区域
|
||||
.page--tabbar {
|
||||
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
@@ -73,10 +77,11 @@ body,
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// 列表卡片
|
||||
// 卡片组件
|
||||
// ==========================================================================
|
||||
|
||||
.item-card.wd-card {
|
||||
// 全局 wd-card 间距
|
||||
.wd-card {
|
||||
margin: 0 !important;
|
||||
margin-bottom: 12rpx !important;
|
||||
}
|
||||
@@ -121,3 +126,25 @@ body,
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// 渐变背景
|
||||
// ==========================================================================
|
||||
|
||||
.gradient-bg {
|
||||
&--primary {
|
||||
background: linear-gradient(135deg, var(--color-primary-light) 0%, var(--color-bg) 100%);
|
||||
}
|
||||
|
||||
&--success {
|
||||
background: linear-gradient(135deg, var(--color-success-light) 0%, var(--color-bg) 100%);
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background: linear-gradient(135deg, var(--color-warning-light) 0%, var(--color-bg) 100%);
|
||||
}
|
||||
|
||||
&--danger {
|
||||
background: linear-gradient(135deg, var(--color-danger-light) 0%, var(--color-bg) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,10 @@ page {
|
||||
--color-danger-light: #fff1f2;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 背景色
|
||||
// 背景色 (Material Design)
|
||||
// -----------------------------------------------------------------------
|
||||
--color-bg: #ffffff;
|
||||
--color-bg-secondary: #f8fafc;
|
||||
--color-bg-page: #f1f5f9;
|
||||
--color-bg-secondary: #f5f5f5;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
@@ -85,11 +84,10 @@ page {
|
||||
--color-danger-light: #7f1d1d;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 背景色
|
||||
// 背景色 (Material Design)
|
||||
// -----------------------------------------------------------------------
|
||||
--color-bg: #111827;
|
||||
--color-bg-secondary: #1f2937;
|
||||
--color-bg-page: #374151;
|
||||
--color-bg: #121212;
|
||||
--color-bg-secondary: #1e1e1e;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
|
||||
3
src/types/env.d.ts
vendored
3
src/types/env.d.ts
vendored
@@ -12,6 +12,9 @@ interface ImportMetaEnv {
|
||||
/** API服务器URL */
|
||||
readonly VITE_APP_API_URL: string;
|
||||
|
||||
/** 应用版本号 */
|
||||
readonly VITE_APP_VERSION: string;
|
||||
|
||||
/**
|
||||
* WebSocket 端点
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,9 @@ import AutoImport from "unplugin-auto-import/vite";
|
||||
import UniLayouts from "@uni-helper/vite-plugin-uni-layouts";
|
||||
import UniPages from "@uni-helper/vite-plugin-uni-pages";
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import Components from "@uni-helper/vite-plugin-uni-components";
|
||||
import { WotResolver } from "@uni-helper/vite-plugin-uni-components/resolvers";
|
||||
|
||||
@@ -12,7 +15,14 @@ export default defineConfig(async ({ mode }: ConfigEnv): Promise<UserConfig> =>
|
||||
const env = loadEnv(mode, process.cwd());
|
||||
const isProd = mode === "production";
|
||||
|
||||
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), "package.json"), "utf-8")) as {
|
||||
version?: string;
|
||||
};
|
||||
|
||||
return {
|
||||
define: {
|
||||
"import.meta.env.VITE_APP_VERSION": JSON.stringify(pkg.version ?? ""),
|
||||
},
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: +env.VITE_APP_PORT,
|
||||
|
||||
Reference in New Issue
Block a user