Compare commits
2 Commits
70d088a4ba
...
0f9a78b64c
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f9a78b64c | |||
| ad426e3660 |
61
api/login.js
61
api/login.js
@ -46,6 +46,7 @@ export function logout() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
export function getCodeImg() {
|
export function getCodeImg() {
|
||||||
return request({
|
return request({
|
||||||
@ -57,3 +58,63 @@ export function getCodeImg() {
|
|||||||
timeout: 20000
|
timeout: 20000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sendEmailCode(data, type = 'register') {
|
||||||
|
return request({
|
||||||
|
url: `/auth/mail/send/${type}`,
|
||||||
|
headers: {
|
||||||
|
isToken: false
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
timeout: 20000,
|
||||||
|
data,
|
||||||
|
params: {
|
||||||
|
autoRegister: data.autoRegister
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verifyEmailCode(data, type = 'register') {
|
||||||
|
return request({
|
||||||
|
url: `/auth/mail/verify/${type}`,
|
||||||
|
headers: {
|
||||||
|
isToken: false
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
timeout: 20000,
|
||||||
|
data,
|
||||||
|
params: {
|
||||||
|
autoRegister: data.autoRegister
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendPhoneCode(data, type = 'register') {
|
||||||
|
return request({
|
||||||
|
url: `/auth/dySms/send/${type}`,
|
||||||
|
headers: {
|
||||||
|
isToken: false
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
timeout: 20000,
|
||||||
|
data,
|
||||||
|
params: {
|
||||||
|
autoRegister: data.autoRegister
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verifyPhoneCode(data, type = 'register') {
|
||||||
|
return request({
|
||||||
|
url: `/auth/dySms/verify/${type}`,
|
||||||
|
headers: {
|
||||||
|
isToken: false
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
timeout: 20000,
|
||||||
|
data,
|
||||||
|
params: {
|
||||||
|
autoRegister: data.autoRegister
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -1,6 +1,50 @@
|
|||||||
import upload from '@/utils/upload'
|
import upload from '@/utils/upload'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询用户列表
|
||||||
|
export function listUser(query) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询用户详细
|
||||||
|
export function getUser(userId) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/' + parseStrEmpty(userId),
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增用户
|
||||||
|
export function addUser(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改用户
|
||||||
|
export function updateUser(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户
|
||||||
|
export function delUser(userId) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/' + userId,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 用户密码重置
|
// 用户密码重置
|
||||||
export function updateUserPwd(oldPassword, newPassword) {
|
export function updateUserPwd(oldPassword, newPassword) {
|
||||||
const data = {
|
const data = {
|
||||||
@ -39,3 +83,29 @@ export function uploadAvatar(data) {
|
|||||||
filePath: data.filePath
|
filePath: data.filePath
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用户密码重置
|
||||||
|
export function resetUserPwd(userId, password) {
|
||||||
|
const data = {
|
||||||
|
userId,
|
||||||
|
password
|
||||||
|
}
|
||||||
|
return request({
|
||||||
|
url: '/system/user/resetPwd',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户状态修改
|
||||||
|
export function changeUserStatus(userId, status) {
|
||||||
|
const data = {
|
||||||
|
userId,
|
||||||
|
status
|
||||||
|
}
|
||||||
|
return request({
|
||||||
|
url: '/system/user/changeStatus',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
36
pages.json
36
pages.json
@ -256,6 +256,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
"root": "pages_system/pages",
|
||||||
|
"pages": [{
|
||||||
|
"path": "dict/index"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "dict/data",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "计算工具"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "register/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "注册",
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"disableScroll": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "forgot/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "忘记密码",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"path": "bind/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "账号绑定",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="content">
|
<view class="content">
|
||||||
<NG-cal-tools></NG-cal-tools>
|
<ng-cal-tools></ng-cal-tools>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -8,7 +8,7 @@
|
|||||||
ref,
|
ref,
|
||||||
onMounted
|
onMounted
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import NGCalTools from '@/pages_caltools/pages/index'
|
import ngCalTools from '@/pages_caltools/pages/index'
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
296
pages/index.vue
296
pages/index.vue
@ -1,179 +1,159 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="work-container">
|
<view class="work-container">
|
||||||
<!-- 轮播图 -->
|
<!-- 轮播图 -->
|
||||||
<uni-swiper-dot class="uni-swiper-dot-box" :info="data" :current="current" field="content">
|
<uni-swiper-dot class="uni-swiper-dot-box" :info="data" :current="current" field="content">
|
||||||
<swiper class="swiper-box" :current="swiperDotIndex" @change="changeSwiper">
|
<swiper class="swiper-box" :current="swiperDotIndex" @change="changeSwiper">
|
||||||
<swiper-item v-for="(item, index) in data" :key="index">
|
<swiper-item v-for="(item, index) in data" :key="index">
|
||||||
<view class="swiper-item" @click="clickBannerItem(item)">
|
<view class="swiper-item" @click="clickBannerItem(item)">
|
||||||
<image :src="item.image" mode="aspectFill" :draggable="false" />
|
<image :src="item.image" mode="aspectFill" :draggable="false" />
|
||||||
</view>
|
</view>
|
||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
</uni-swiper-dot>
|
</uni-swiper-dot>
|
||||||
|
|
||||||
<!-- 宫格组件 -->
|
<!-- 宫格组件 -->
|
||||||
<uni-section title="系统管理" type="line"></uni-section>
|
<view class="grid-body" v-for="(group, groupIndex) in moudlesGroups">
|
||||||
<view class="grid-body">
|
<uni-section :title="group.name" type="line"></uni-section>
|
||||||
<uni-grid :column="4" :showBorder="false" @change="changeGrid">
|
<uni-grid :column="4" :showBorder="false">
|
||||||
<uni-grid-item>
|
<uni-grid-item v-for="(item, itemIndex) in group.items" :key="itemIndex"
|
||||||
<view class="grid-item-box">
|
@click="navigateToMoudles(item)" class="grid-item">
|
||||||
<uni-icons type="person-filled" size="30"></uni-icons>
|
<view class="grid-item-box">
|
||||||
<text class="text">用户管理</text>
|
<uni-icons :type="item.icon" size="30"></uni-icons>
|
||||||
</view>
|
<text class="text">{{item.name}}</text>
|
||||||
</uni-grid-item>
|
</view>
|
||||||
<uni-grid-item>
|
</uni-grid-item>
|
||||||
<view class="grid-item-box">
|
</uni-grid>
|
||||||
<uni-icons type="staff-filled" size="30"></uni-icons>
|
</view>
|
||||||
<text class="text">角色管理</text>
|
</view>
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
<uni-grid-item>
|
|
||||||
<view class="grid-item-box">
|
|
||||||
<uni-icons type="color" size="30"></uni-icons>
|
|
||||||
<text class="text">菜单管理</text>
|
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
<uni-grid-item>
|
|
||||||
<view class="grid-item-box">
|
|
||||||
<uni-icons type="settings-filled" size="30"></uni-icons>
|
|
||||||
<text class="text">部门管理</text>
|
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
<uni-grid-item>
|
|
||||||
<view class="grid-item-box">
|
|
||||||
<uni-icons type="heart-filled" size="30"></uni-icons>
|
|
||||||
<text class="text">岗位管理</text>
|
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
<uni-grid-item>
|
|
||||||
<view class="grid-item-box">
|
|
||||||
<uni-icons type="bars" size="30"></uni-icons>
|
|
||||||
<text class="text">字典管理</text>
|
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
<uni-grid-item>
|
|
||||||
<view class="grid-item-box">
|
|
||||||
<uni-icons type="gear-filled" size="30"></uni-icons>
|
|
||||||
<text class="text">参数设置</text>
|
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
<uni-grid-item>
|
|
||||||
<view class="grid-item-box">
|
|
||||||
<uni-icons type="chat-filled" size="30"></uni-icons>
|
|
||||||
<text class="text">通知公告</text>
|
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
<uni-grid-item>
|
|
||||||
<view class="grid-item-box">
|
|
||||||
<uni-icons type="wallet-filled" size="30"></uni-icons>
|
|
||||||
<text class="text">日志管理</text>
|
|
||||||
</view>
|
|
||||||
</uni-grid-item>
|
|
||||||
</uni-grid>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import {
|
||||||
import modal from "@/plugins/modal"
|
ref,
|
||||||
const current = ref(0);
|
onMounted
|
||||||
const swiperDotIndex = ref(0);
|
} from "vue";
|
||||||
const data = ref([
|
import modal from "@/plugins/modal"
|
||||||
{ image: '/static/images/banner/banner01.jpg' },
|
|
||||||
{ image: '/static/images/banner/banner02.jpg' },
|
|
||||||
{ image: '/static/images/banner/banner03.jpg' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
function clickBannerItem(item) {
|
import {
|
||||||
console.info(item)
|
extractModuleData
|
||||||
};
|
} from '@/utils/moudlesData.ts';
|
||||||
function changeSwiper(e) {
|
|
||||||
current.value = e.detail.current
|
|
||||||
}
|
|
||||||
function changeGrid(e) {
|
|
||||||
modal.showToast({
|
|
||||||
title: '模块建设中',
|
|
||||||
mask: false,
|
|
||||||
icon: 'loading',
|
|
||||||
duration: 1000
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const current = ref(0);
|
||||||
|
const swiperDotIndex = ref(0);
|
||||||
|
const data = ref([{
|
||||||
|
image: '/static/images/banner/banner01.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: '/static/images/banner/banner02.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: '/static/images/banner/banner03.jpg'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 核心数据:计算分组和功能项定义
|
||||||
|
const moudlesGroups = ref([]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
moudlesGroups.value = extractModuleData(['系统管理'], false) })
|
||||||
|
|
||||||
|
function navigateToMoudles(item) {
|
||||||
|
console.log(item)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: item.path
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function clickBannerItem(item) {
|
||||||
|
console.info(item)
|
||||||
|
};
|
||||||
|
|
||||||
|
function changeSwiper(e) {
|
||||||
|
current.value = e.detail.current
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeGrid(e) {
|
||||||
|
modal.showToast({
|
||||||
|
title: '模块建设中',
|
||||||
|
mask: false,
|
||||||
|
icon: 'loading',
|
||||||
|
duration: 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/* #ifndef APP-NVUE */
|
/* #ifndef APP-NVUE */
|
||||||
page {
|
page {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
view {
|
view {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #endif */
|
/* #endif */
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
margin-top: 10rpx;
|
margin-top: 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-item-box {
|
.grid-item-box {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
/* #ifndef APP-NVUE */
|
/* #ifndef APP-NVUE */
|
||||||
display: flex;
|
display: flex;
|
||||||
/* #endif */
|
/* #endif */
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 15px 0;
|
padding: 15px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-margin-wrap {
|
.uni-margin-wrap {
|
||||||
width: 690rpx;
|
width: 690rpx;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper {
|
.swiper {
|
||||||
height: 300rpx;
|
height: 300rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-box {
|
.swiper-box {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-item {
|
.swiper-item {
|
||||||
/* #ifndef APP-NVUE */
|
/* #ifndef APP-NVUE */
|
||||||
display: flex;
|
display: flex;
|
||||||
/* #endif */
|
/* #endif */
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
height: 300rpx;
|
height: 300rpx;
|
||||||
line-height: 300rpx;
|
line-height: 300rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 500px) {
|
@media screen and (min-width: 500px) {
|
||||||
.uni-swiper-dot-box {
|
.uni-swiper-dot-box {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
/* #ifndef APP-NVUE */
|
/* #ifndef APP-NVUE */
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
/* #endif */
|
/* #endif */
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
266
pages/login - 副本.vue
Normal file
266
pages/login - 副本.vue
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
<script setup>
|
||||||
|
import modal from '@/plugins/modal'
|
||||||
|
import { getCodeImg } from '@/api/login'
|
||||||
|
import { ref } from "vue";
|
||||||
|
import config from '@/config.js'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { getWxCode } from '@/utils/geek';
|
||||||
|
import { wxLogin } from '@/api/oauth';
|
||||||
|
import { setToken } from '@/utils/auth';
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const codeUrl = ref("");
|
||||||
|
const captchaEnabled = ref(true); // 是否开启验证码
|
||||||
|
const useWxLogin = ref(false); // 是否使用微信登录
|
||||||
|
// #if MP-WEIXIN
|
||||||
|
useWxLogin.value = true
|
||||||
|
// #endif
|
||||||
|
const globalConfig = ref(config);
|
||||||
|
const loginForm = ref({
|
||||||
|
username: "admin",
|
||||||
|
password: "admin123",
|
||||||
|
code: "",
|
||||||
|
uuid: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleLoginByWx() {
|
||||||
|
getWxCode("__UNI__A6541FF").then(res => {
|
||||||
|
console.log(res);
|
||||||
|
wxLogin('miniapp', res).then(res => {
|
||||||
|
if (res.token != null) {
|
||||||
|
setToken(res.token);
|
||||||
|
loginSuccess()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取图形验证码
|
||||||
|
function getCode() {
|
||||||
|
getCodeImg().then(res => {
|
||||||
|
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled
|
||||||
|
if (captchaEnabled.value) {
|
||||||
|
codeUrl.value = 'data:image/gif;base64,' + res.img
|
||||||
|
loginForm.value.uuid = res.uuid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
async function handleLogin() {
|
||||||
|
if (loginForm.value.username === "") {
|
||||||
|
modal.msgError("请输入您的账号")
|
||||||
|
} else if (loginForm.value.password === "") {
|
||||||
|
modal.msgError("请输入您的密码")
|
||||||
|
} else if (loginForm.value.code === "" && captchaEnabled.value) {
|
||||||
|
modal.msgError("请输入验证码")
|
||||||
|
} else {
|
||||||
|
modal.loading("登录中,请耐心等待...")
|
||||||
|
pwdLogin()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 密码登录
|
||||||
|
async function pwdLogin() {
|
||||||
|
userStore.login(loginForm.value).then(() => {
|
||||||
|
modal.closeLoading()
|
||||||
|
loginSuccess()
|
||||||
|
}).catch(() => {
|
||||||
|
if (captchaEnabled.value) {
|
||||||
|
modal.closeLoading()
|
||||||
|
getCode()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
function loginSuccess(result) {
|
||||||
|
// 设置用户信息
|
||||||
|
userStore.getInfo().then(res => {
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/index'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐私协议
|
||||||
|
function handlePrivacy() {
|
||||||
|
let site = globalConfig.value.appInfo.agreements[0];
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/common/webview/index?title=${site.title}&url=${site.url}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 用户协议
|
||||||
|
function handleUserAgrement() {
|
||||||
|
let site = globalConfig.value.appInfo.agreements[1]
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/common/webview/index?title=${site.title}&url=${site.url}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getCode();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<view class="normal-login-container">
|
||||||
|
<view class="logo-content align-center justify-center flex">
|
||||||
|
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
|
||||||
|
</image>
|
||||||
|
<text class="title">登录</text>
|
||||||
|
</view>
|
||||||
|
<view class="login-form-content">
|
||||||
|
<view class="input-item flex align-center">
|
||||||
|
<view class="iconfont icon-user icon"></view>
|
||||||
|
<input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
|
||||||
|
</view>
|
||||||
|
<view class="input-item flex align-center">
|
||||||
|
<view class="iconfont icon-password icon"></view>
|
||||||
|
<input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
|
||||||
|
</view>
|
||||||
|
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
|
||||||
|
<view class="iconfont icon-code icon"></view>
|
||||||
|
<input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
|
||||||
|
<view class="login-code">
|
||||||
|
<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="action-btn">
|
||||||
|
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
|
||||||
|
<button @click="handleLoginByWx" v-if="useWxLogin"
|
||||||
|
class="login-btn cu-btn block bg-green lg round">微信一键登录</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="register-link" >
|
||||||
|
<text class="question-text">没有账号?</text>
|
||||||
|
<navigator class="link-type" url="/pages_mine/pages/register/index">立即注册</navigator>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="xieyi text-center">
|
||||||
|
<text class="text-grey1">登录即代表同意</text>
|
||||||
|
<text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
|
||||||
|
<text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
page {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.normal-login-container {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.logo-content {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 21px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 15%;
|
||||||
|
|
||||||
|
image {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-content {
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px auto;
|
||||||
|
margin-top: 15%;
|
||||||
|
width: 80%;
|
||||||
|
|
||||||
|
.input-item {
|
||||||
|
margin: 20px auto;
|
||||||
|
background-color: #f5f6f7;
|
||||||
|
height: 45px;
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 38rpx;
|
||||||
|
margin-left: 10px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
margin-top: 40px;
|
||||||
|
|
||||||
|
.login-btn {
|
||||||
|
height: 45px;
|
||||||
|
|
||||||
|
&+.login-btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.xieyi {
|
||||||
|
color: #333;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-code {
|
||||||
|
height: 38px;
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
.login-code-img {
|
||||||
|
height: 38px;
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 10px;
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.register-link {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.question-text {
|
||||||
|
color: #606266;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-type {
|
||||||
|
color: #409EFF;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
position: relative;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #409EFF;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
801
pages/login.vue
801
pages/login.vue
@ -1,219 +1,640 @@
|
|||||||
<script setup>
|
|
||||||
import modal from '@/plugins/modal'
|
|
||||||
import { getCodeImg } from '@/api/login'
|
|
||||||
import { ref } from "vue";
|
|
||||||
import config from '@/config.js'
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import { getWxCode } from '@/utils/geek';
|
|
||||||
import { wxLogin } from '@/api/oauth';
|
|
||||||
import { setToken } from '@/utils/auth';
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const codeUrl = ref("");
|
|
||||||
const captchaEnabled = ref(true); // 是否开启验证码
|
|
||||||
const useWxLogin = ref(false); // 是否使用微信登录
|
|
||||||
// #if MP-WEIXIN
|
|
||||||
useWxLogin.value = true
|
|
||||||
// #endif
|
|
||||||
const globalConfig = ref(config);
|
|
||||||
const loginForm = ref({
|
|
||||||
username: "admin",
|
|
||||||
password: "admin123",
|
|
||||||
code: "",
|
|
||||||
uuid: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleLoginByWx() {
|
|
||||||
getWxCode().then(res => {
|
|
||||||
console.log(res);
|
|
||||||
wxLogin('miniapp', res).then(res => {
|
|
||||||
if (res.token != null) {
|
|
||||||
setToken(res.token);
|
|
||||||
loginSuccess()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 获取图形验证码
|
|
||||||
function getCode() {
|
|
||||||
getCodeImg().then(res => {
|
|
||||||
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled
|
|
||||||
if (captchaEnabled.value) {
|
|
||||||
codeUrl.value = 'data:image/gif;base64,' + res.img
|
|
||||||
loginForm.value.uuid = res.uuid
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
async function handleLogin() {
|
|
||||||
if (loginForm.value.username === "") {
|
|
||||||
modal.msgError("请输入您的账号")
|
|
||||||
} else if (loginForm.value.password === "") {
|
|
||||||
modal.msgError("请输入您的密码")
|
|
||||||
} else if (loginForm.value.code === "" && captchaEnabled.value) {
|
|
||||||
modal.msgError("请输入验证码")
|
|
||||||
} else {
|
|
||||||
modal.loading("登录中,请耐心等待...")
|
|
||||||
pwdLogin()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 密码登录
|
|
||||||
async function pwdLogin() {
|
|
||||||
userStore.login(loginForm.value).then(() => {
|
|
||||||
modal.closeLoading()
|
|
||||||
loginSuccess()
|
|
||||||
}).catch(() => {
|
|
||||||
if (captchaEnabled.value) {
|
|
||||||
modal.closeLoading()
|
|
||||||
getCode()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
function loginSuccess(result) {
|
|
||||||
// 设置用户信息
|
|
||||||
userStore.getInfo().then(res => {
|
|
||||||
uni.switchTab({
|
|
||||||
url: '/pages/index'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐私协议
|
|
||||||
function handlePrivacy() {
|
|
||||||
let site = globalConfig.value.appInfo.agreements[0];
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pages/common/webview/index?title=${site.title}&url=${site.url}`
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// 用户协议
|
|
||||||
function handleUserAgrement() {
|
|
||||||
let site = globalConfig.value.appInfo.agreements[1]
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pages/common/webview/index?title=${site.title}&url=${site.url}`
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
getCode();
|
|
||||||
</script>
|
|
||||||
<template>
|
<template>
|
||||||
<view class="normal-login-container">
|
<view class="login-container">
|
||||||
<view class="logo-content align-center justify-center flex">
|
<view class="login-header">
|
||||||
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
|
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
|
||||||
</image>
|
<text class="app-name">{{ appName }}</text>
|
||||||
<text class="title">若依移动端登录</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="login-form-content">
|
|
||||||
<view class="input-item flex align-center">
|
<view class="login-content">
|
||||||
<view class="iconfont icon-user icon"></view>
|
<!-- 登录方式切换 -->
|
||||||
<input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
|
<view class="login-tabs">
|
||||||
</view>
|
<view
|
||||||
<view class="input-item flex align-center">
|
class="tab-item"
|
||||||
<view class="iconfont icon-password icon"></view>
|
:class="{ active: activeTab === 'account' }"
|
||||||
<input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
|
@click="switchTab('account')"
|
||||||
</view>
|
>
|
||||||
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
|
账号登录
|
||||||
<view class="iconfont icon-code icon"></view>
|
</view>
|
||||||
<input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
|
<view
|
||||||
<view class="login-code">
|
class="tab-item"
|
||||||
<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
|
:class="{ active: activeTab === 'phone' }"
|
||||||
|
@click="switchTab('phone')"
|
||||||
|
>
|
||||||
|
手机登录
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-btn">
|
|
||||||
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
|
|
||||||
<button @click="handleLoginByWx" v-if="useWxLogin"
|
|
||||||
class="login-btn cu-btn block bg-green lg round">微信一键登录</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="xieyi text-center">
|
<!-- 账号密码登录 -->
|
||||||
<text class="text-grey1">登录即代表同意</text>
|
<view v-if="activeTab === 'account'" class="login-form">
|
||||||
<text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
|
<view class="form-item">
|
||||||
<text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
|
<text class="label">账号</text>
|
||||||
|
<input
|
||||||
|
v-model="accountForm.username"
|
||||||
|
class="input"
|
||||||
|
placeholder="请输入用户名/手机号/邮箱"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">密码</text>
|
||||||
|
<input
|
||||||
|
v-model="accountForm.password"
|
||||||
|
class="input"
|
||||||
|
password
|
||||||
|
placeholder="请输入密码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
@confirm="handleAccountLogin"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item" v-if="accountForm.showCaptcha">
|
||||||
|
<text class="label">验证码</text>
|
||||||
|
<view class="captcha-input">
|
||||||
|
<input
|
||||||
|
v-model="accountForm.code"
|
||||||
|
class="input"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
:src="accountForm.captchaImg"
|
||||||
|
class="captcha-img"
|
||||||
|
@click="getCaptcha"
|
||||||
|
mode="aspectFit"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-options">
|
||||||
|
<text class="forget-pwd" @click="goForgetPwd">忘记密码?</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<button class="login-btn" @click="handleAccountLogin" :disabled="accountLogining">
|
||||||
|
{{ accountLogining ? '登录中...' : '登录' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 手机验证码登录 -->
|
||||||
|
<view v-if="activeTab === 'phone'" class="login-form">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">手机号</text>
|
||||||
|
<input
|
||||||
|
v-model="phoneForm.phone"
|
||||||
|
class="input"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">验证码</text>
|
||||||
|
<view class="code-input">
|
||||||
|
<input
|
||||||
|
v-model="phoneForm.code"
|
||||||
|
class="input"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="send-code-btn"
|
||||||
|
:disabled="phoneForm.countdown > 0"
|
||||||
|
@click="handlesendPhoneCode"
|
||||||
|
>
|
||||||
|
{{ phoneForm.countdown > 0 ? `${phoneForm.countdown}s` : '获取验证码' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<button class="login-btn" @click="handlePhoneLogin" :disabled="phoneLogining">
|
||||||
|
{{ phoneLogining ? '登录中...' : '登录' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 快捷登录方式 -->
|
||||||
|
<view class="quick-login">
|
||||||
|
<view class="divider">
|
||||||
|
<text class="divider-text">快捷登录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="login-methods">
|
||||||
|
<view class="method-item" @click="handleOneClickLogin">
|
||||||
|
<view class="method-icon one-click">
|
||||||
|
<text class="iconfont">⚡</text>
|
||||||
|
</view>
|
||||||
|
<text class="method-text">本机一键登录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="method-item" @click="handleWechatLogin">
|
||||||
|
<view class="method-icon wechat">
|
||||||
|
<text class="iconfont">💬</text>
|
||||||
|
</view>
|
||||||
|
<text class="method-text">微信登录</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 注册链接 -->
|
||||||
|
<view class="register-link">
|
||||||
|
<text>还没有账号? </text>
|
||||||
|
<text class="link" @click="goRegister">立即注册</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
|
||||||
page {
|
<script setup>
|
||||||
background-color: #ffffff;
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
|
import modal from '@/plugins/modal'
|
||||||
|
import { getCodeImg, login, sendPhoneCode, verifyPhoneCode } from '@/api/login'
|
||||||
|
import { getWxCode } from '@/utils/geek'
|
||||||
|
import { wxLogin } from '@/api/oauth'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { setToken } from '@/utils/auth'
|
||||||
|
import config from '@/config.js'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const appName = config.appName || '系统名称'
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const activeTab = ref('account')
|
||||||
|
const accountLogining = ref(false)
|
||||||
|
const phoneLogining = ref(false)
|
||||||
|
|
||||||
|
// 账号登录表单
|
||||||
|
const accountForm = reactive({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
code: '',
|
||||||
|
captchaImg: '',
|
||||||
|
uuid: '',
|
||||||
|
showCaptcha: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 手机登录表单
|
||||||
|
const phoneForm = reactive({
|
||||||
|
phone: '',
|
||||||
|
code: '',
|
||||||
|
countdown: 0,
|
||||||
|
timer: null
|
||||||
|
})
|
||||||
|
|
||||||
|
// 切换登录方式
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
activeTab.value = tab
|
||||||
|
if (tab === 'account' && !accountForm.captchaImg) {
|
||||||
|
getCaptcha()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.normal-login-container {
|
// 获取验证码
|
||||||
width: 100%;
|
const getCaptcha = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getCodeImg()
|
||||||
|
accountForm.captchaImg = 'data:image/gif;base64,' + res.img
|
||||||
|
accountForm.uuid = res.uuid
|
||||||
|
accountForm.showCaptcha = true
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert('验证码获取失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.logo-content {
|
// 发送手机验证码
|
||||||
width: 100%;
|
const handlesendPhoneCode = async () => {
|
||||||
font-size: 21px;
|
if (!phoneForm.phone) {
|
||||||
text-align: center;
|
modal.alert('请输入手机号')
|
||||||
padding-top: 15%;
|
return
|
||||||
|
|
||||||
image {
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-form-content {
|
if (!/^1[3-9]\d{9}$/.test(phoneForm.phone)) {
|
||||||
text-align: center;
|
modal.alert('请输入正确的手机号')
|
||||||
margin: 20px auto;
|
return
|
||||||
margin-top: 15%;
|
}
|
||||||
width: 80%;
|
|
||||||
|
|
||||||
.input-item {
|
try {
|
||||||
margin: 20px auto;
|
await sendPhoneCode({ phone: phoneForm.phone }, 'login')
|
||||||
background-color: #f5f6f7;
|
modal.alert('验证码已发送')
|
||||||
height: 45px;
|
|
||||||
border-radius: 20px;
|
|
||||||
|
|
||||||
.icon {
|
// 开始倒计时
|
||||||
font-size: 38rpx;
|
phoneForm.countdown = 60
|
||||||
margin-left: 10px;
|
phoneForm.timer = setInterval(() => {
|
||||||
color: #999;
|
phoneForm.countdown--
|
||||||
|
if (phoneForm.countdown <= 0) {
|
||||||
|
clearInterval(phoneForm.timer)
|
||||||
}
|
}
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '验证码发送失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.input {
|
// 账号密码登录
|
||||||
width: 100%;
|
const handleAccountLogin = async () => {
|
||||||
font-size: 14px;
|
if (!accountForm.username) {
|
||||||
line-height: 20px;
|
modal.alert('请输入账号')
|
||||||
text-align: left;
|
return
|
||||||
padding-left: 15px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!accountForm.password) {
|
||||||
|
modal.alert('请输入密码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountForm.showCaptcha && !accountForm.code) {
|
||||||
|
modal.alert('请输入验证码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accountLogining.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
username: accountForm.username,
|
||||||
|
password: accountForm.password
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
if (accountForm.showCaptcha) {
|
||||||
margin-top: 40px;
|
params.code = accountForm.code
|
||||||
|
params.uuid = accountForm.uuid
|
||||||
|
}
|
||||||
|
|
||||||
.login-btn {
|
const res = await login(params.username, params.password, params.code, params.uuid)
|
||||||
height: 45px;
|
|
||||||
|
|
||||||
&+.login-btn {
|
if (res.token) {
|
||||||
margin-top: 20px;
|
setToken(res.token)
|
||||||
|
await userStore.getInfo()
|
||||||
|
modal.alert('登录成功', () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 如果需要验证码,重新获取
|
||||||
|
if (error.code === 401 || error.message?.includes('验证码')) {
|
||||||
|
getCaptcha()
|
||||||
|
}
|
||||||
|
modal.alert(error.message || '登录失败')
|
||||||
|
} finally {
|
||||||
|
accountLogining.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手机验证码登录
|
||||||
|
const handlePhoneLogin = async () => {
|
||||||
|
if (!phoneForm.phone) {
|
||||||
|
modal.alert('请输入手机号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!phoneForm.code) {
|
||||||
|
modal.alert('请输入验证码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
phoneLogining.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证验证码
|
||||||
|
await verifyPhoneCode({
|
||||||
|
phone: phoneForm.phone,
|
||||||
|
code: phoneForm.code
|
||||||
|
}, 'login')
|
||||||
|
|
||||||
|
// 验证成功后执行登录
|
||||||
|
const res = await login(phoneForm.phone, '', '', '')
|
||||||
|
|
||||||
|
if (res.token) {
|
||||||
|
setToken(res.token)
|
||||||
|
await userStore.getInfo()
|
||||||
|
modal.alert('登录成功', () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '登录失败')
|
||||||
|
} finally {
|
||||||
|
phoneLogining.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本机一键登录
|
||||||
|
const handleOneClickLogin = async () => {
|
||||||
|
try {
|
||||||
|
// 这里需要调用uni的一键登录API
|
||||||
|
// 由于不同平台实现不同,这里使用模拟实现
|
||||||
|
modal.confirm('是否使用本机号码一键登录?', async () => {
|
||||||
|
try {
|
||||||
|
// 获取本机号码(实际项目中需要调用运营商SDK)
|
||||||
|
const phoneNumber = '' // 从SDK获取
|
||||||
|
|
||||||
|
if (!phoneNumber) {
|
||||||
|
modal.alert('无法获取本机号码')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 执行一键登录
|
||||||
|
const res = await login(phoneNumber, '', '', '')
|
||||||
|
|
||||||
|
if (res.token) {
|
||||||
|
setToken(res.token)
|
||||||
|
await userStore.getInfo()
|
||||||
|
modal.alert('登录成功', () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '一键登录失败')
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert('一键登录功能暂不可用')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 微信登录
|
||||||
|
const handleWechatLogin = async () => {
|
||||||
|
try {
|
||||||
|
const code = await getWxCode()
|
||||||
|
const res = await wxLogin('miniapp', code)
|
||||||
|
|
||||||
|
if (res.token) {
|
||||||
|
setToken(res.token)
|
||||||
|
await userStore.getInfo()
|
||||||
|
modal.alert('登录成功', () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 400) {
|
||||||
.xieyi {
|
// 需要绑定微信
|
||||||
color: #333;
|
modal.confirm('首次使用微信登录,需要绑定账号', () => {
|
||||||
margin-top: 20px;
|
uni.navigateTo({
|
||||||
}
|
url: '/pages_system/pages/login/bind-account'
|
||||||
|
})
|
||||||
.login-code {
|
})
|
||||||
height: 38px;
|
} else {
|
||||||
float: right;
|
modal.alert(error.message || '微信登录失败')
|
||||||
|
|
||||||
.login-code-img {
|
|
||||||
height: 38px;
|
|
||||||
position: absolute;
|
|
||||||
margin-left: 10px;
|
|
||||||
width: 200rpx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 跳转注册页面
|
||||||
|
const goRegister = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_system/pages/register/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转忘记密码页面
|
||||||
|
const goForgetPwd = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_system/pages/forget/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期
|
||||||
|
onMounted(() => {
|
||||||
|
getCaptcha()
|
||||||
|
})
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
// 检查是否有token,如果有则直接跳转
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
if (token) {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
padding: 60rpx 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-name {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-content {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-tabs {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
border-bottom: 2rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #999;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active {
|
||||||
|
color: #007aff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2rpx;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background: #007aff;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
border: 2rpx solid #e0e0e0;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-input, .code-input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-img {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
border: 2rpx solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-code-btn {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-code-btn[disabled] {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-options {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forget-pwd {
|
||||||
|
color: #007aff;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn[disabled] {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-login {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
margin: 40rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1rpx;
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider-text {
|
||||||
|
background: #fff;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
color: #999;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-methods {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-icon {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
font-size: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-icon.one-click {
|
||||||
|
background: #ff9500;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-icon.wechat {
|
||||||
|
background: #07c160;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-link {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,48 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import tab from "@/plugins/tab";
|
|
||||||
import list from "./template.config.js";
|
|
||||||
|
|
||||||
interface ListItem {
|
|
||||||
groupName: string;
|
|
||||||
list: FieldItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldItem {
|
|
||||||
title: string;
|
|
||||||
icon: string;
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const listData = list as ListItem[];
|
|
||||||
const getIcon = (path: string) => `../static/uview/demo/${path}.png`;
|
|
||||||
const openPage = (path: string) => tab.navigateTo(path)
|
|
||||||
const getGroupTitle = (item: ListItem) => item.groupName;
|
|
||||||
const getFieldTitle = (item: FieldItem) => item.title;
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<view class="wrap">
|
|
||||||
<view class="list-wrap">
|
|
||||||
<u-cell-group title-bg-color="rgb(243, 244, 246)" :title="getGroupTitle(item)" v-for="(item, index) in listData"
|
|
||||||
:key="index">
|
|
||||||
<u-cell :titleStyle="{ fontWeight: 500 }" @click="openPage(item1.path)" :title="getFieldTitle(item1)"
|
|
||||||
v-for="(item1, index1) in item.list" :key="index1">
|
|
||||||
<template v-slot:icon>
|
|
||||||
<image class="u-cell-icon" :src="getIcon(item1.icon)" mode="widthFix"></image>
|
|
||||||
</template>
|
|
||||||
</u-cell>
|
|
||||||
</u-cell-group>
|
|
||||||
</view>
|
|
||||||
<u-gap height="70"></u-gap>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
page {
|
|
||||||
background-color: rgb(240, 242, 244);
|
|
||||||
}
|
|
||||||
|
|
||||||
.u-cell-icon {
|
|
||||||
width: 36rpx;
|
|
||||||
height: 36rpx;
|
|
||||||
margin-right: 8rpx;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -192,7 +192,7 @@
|
|||||||
description: '展示不同天然气组分与热值之间的对应关系曲线',
|
description: '展示不同天然气组分与热值之间的对应关系曲线',
|
||||||
images: [{
|
images: [{
|
||||||
url: '/static/charts/heat-value-chart.jpg',
|
url: '/static/charts/heat-value-chart.jpg',
|
||||||
thumbnail: '/static/charts/heat-value-thumb.jpg',
|
thumbnail: '',
|
||||||
description: '热值关系图表'
|
description: '热值关系图表'
|
||||||
}],
|
}],
|
||||||
tables: [],
|
tables: [],
|
||||||
|
|||||||
179
pages/work.vue
Normal file
179
pages/work.vue
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<view class="work-container">
|
||||||
|
<!-- 轮播图 -->
|
||||||
|
<uni-swiper-dot class="uni-swiper-dot-box" :info="data" :current="current" field="content">
|
||||||
|
<swiper class="swiper-box" :current="swiperDotIndex" @change="changeSwiper">
|
||||||
|
<swiper-item v-for="(item, index) in data" :key="index">
|
||||||
|
<view class="swiper-item" @click="clickBannerItem(item)">
|
||||||
|
<image :src="item.image" mode="aspectFill" :draggable="false" />
|
||||||
|
</view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
</uni-swiper-dot>
|
||||||
|
|
||||||
|
<!-- 宫格组件 -->
|
||||||
|
<uni-section title="系统管理" type="line"></uni-section>
|
||||||
|
<view class="grid-body">
|
||||||
|
<uni-grid :column="4" :showBorder="false" @change="changeGrid">
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="person-filled" size="30"></uni-icons>
|
||||||
|
<text class="text">用户管理</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="staff-filled" size="30"></uni-icons>
|
||||||
|
<text class="text">角色管理</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="color" size="30"></uni-icons>
|
||||||
|
<text class="text">菜单管理</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="settings-filled" size="30"></uni-icons>
|
||||||
|
<text class="text">部门管理</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="heart-filled" size="30"></uni-icons>
|
||||||
|
<text class="text">岗位管理</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="bars" size="30"></uni-icons>
|
||||||
|
<text class="text">字典管理</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="gear-filled" size="30"></uni-icons>
|
||||||
|
<text class="text">参数设置</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="chat-filled" size="30"></uni-icons>
|
||||||
|
<text class="text">通知公告</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
<uni-grid-item>
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<uni-icons type="wallet-filled" size="30"></uni-icons>
|
||||||
|
<text class="text">日志管理</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
</uni-grid>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import modal from "@/plugins/modal"
|
||||||
|
const current = ref(0);
|
||||||
|
const swiperDotIndex = ref(0);
|
||||||
|
const data = ref([
|
||||||
|
{ image: '/static/images/banner/banner01.jpg' },
|
||||||
|
{ image: '/static/images/banner/banner02.jpg' },
|
||||||
|
{ image: '/static/images/banner/banner03.jpg' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
function clickBannerItem(item) {
|
||||||
|
console.info(item)
|
||||||
|
};
|
||||||
|
function changeSwiper(e) {
|
||||||
|
current.value = e.detail.current
|
||||||
|
}
|
||||||
|
function changeGrid(e) {
|
||||||
|
modal.showToast({
|
||||||
|
title: '模块建设中',
|
||||||
|
mask: false,
|
||||||
|
icon: 'loading',
|
||||||
|
duration: 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
min-height: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
view {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
.text {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item-box {
|
||||||
|
flex: 1;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-margin-wrap {
|
||||||
|
width: 690rpx;
|
||||||
|
width: 100%;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper {
|
||||||
|
height: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-box {
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
height: 300rpx;
|
||||||
|
line-height: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 500px) {
|
||||||
|
.uni-swiper-dot-box {
|
||||||
|
width: 400px;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
margin: 0 auto;
|
||||||
|
/* #endif */
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -467,7 +467,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.uni-forms-item__label) {
|
:deep(.uni-forms-item__label) {
|
||||||
font-size: 26rpx;
|
font-size: 22rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.uni-input-input) {
|
:deep(.uni-input-input) {
|
||||||
|
|||||||
@ -27,54 +27,24 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
ref
|
ref,
|
||||||
|
onMounted,
|
||||||
|
onBeforeMount,
|
||||||
|
watch
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
import {
|
||||||
|
extractModuleData
|
||||||
|
} from '@/utils/moudlesData.ts';
|
||||||
// 响应式数据:当前激活的 Swiper 标签索引
|
// 响应式数据:当前激活的 Swiper 标签索引
|
||||||
const currentTab = ref(0);
|
const currentTab = ref(0);
|
||||||
|
|
||||||
// 核心数据:计算分组和功能项定义
|
// 核心数据:计算分组和功能项定义
|
||||||
const calcGroups = [{
|
const calcGroups = ref([]);
|
||||||
name: '流量计算',
|
|
||||||
color: '#007AFF',
|
onMounted(() => {
|
||||||
items: [{
|
calcGroups.value = extractModuleData(['流量计算', '参数计算'], false)
|
||||||
name: '差压式流量计算',
|
console.log(calcGroups.value);
|
||||||
icon: 'smallcircle',
|
})
|
||||||
dMeterType: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '速度式流量计算',
|
|
||||||
icon: 'paperplane',
|
|
||||||
dMeterType: 1
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '参数计算',
|
|
||||||
color: '#5AC8FA',
|
|
||||||
items: [{
|
|
||||||
name: '压缩因子',
|
|
||||||
icon: 'pyq',
|
|
||||||
dMeterType: 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '声速计算',
|
|
||||||
icon: 'sound',
|
|
||||||
dMeterType: 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '发热量',
|
|
||||||
icon: 'fire',
|
|
||||||
dMeterType: 6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '其他参数',
|
|
||||||
icon: 'more',
|
|
||||||
dMeterType: 7
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 Swiper 滑动切换事件
|
* 处理 Swiper 滑动切换事件
|
||||||
@ -89,12 +59,10 @@
|
|||||||
* @param {Object} item 点击的项
|
* @param {Object} item 点击的项
|
||||||
*/
|
*/
|
||||||
const navigateToCalc = (item) => {
|
const navigateToCalc = (item) => {
|
||||||
const dMeterType = item.dMeterType;
|
const dMeterType = item.params;
|
||||||
console.log(`导航到计算页面,dMeterType: ${dMeterType}`);
|
|
||||||
|
|
||||||
// 使用 UniApp 原生导航 API
|
// 使用 UniApp 原生导航 API
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages_caltools/pages/main?dMeterType=${dMeterType}`
|
url: item.path + `?dMeterType=${dMeterType}`
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,273 +1,277 @@
|
|||||||
<template>
|
<script setup lang="ts">
|
||||||
<view class="register-container">
|
import { onMounted, ref } from "vue";
|
||||||
<!-- 背景图建议在 pages.json 中配置,这里作为备用 -->
|
import { getCodeImg, sendEmailCode, sendPhoneCode } from "@/api/login";
|
||||||
<view class="register-form-wrapper">
|
import useUserStore from "@/store/modules/user";
|
||||||
<uni-forms ref="registerFormRef" :modelValue="registerForm" :rules="registerRules" label-width="0">
|
|
||||||
|
|
||||||
<view class="title">天然气工具平台</view>
|
// 定义 props
|
||||||
|
const props = defineProps<{
|
||||||
|
register: boolean;
|
||||||
|
captchaEnabled: boolean;
|
||||||
|
method: 'password' | 'phone' | 'email';
|
||||||
|
}>();
|
||||||
|
|
||||||
<uni-forms-item name="username">
|
// 响应式数据
|
||||||
<uni-easyinput v-model="registerForm.username" type="text" placeholder="账号" prefixIcon="person"
|
const registerForm = ref({
|
||||||
@confirm="handleRegister" />
|
username: "",
|
||||||
</uni-forms-item>
|
password: "",
|
||||||
|
confirmPassword: "",
|
||||||
|
email: '',
|
||||||
|
phonenumber: '',
|
||||||
|
code: "",
|
||||||
|
uuid: "",
|
||||||
|
});
|
||||||
|
|
||||||
<uni-forms-item name="password">
|
const codeUrl = ref("");
|
||||||
<uni-easyinput v-model="registerForm.password" type="password" placeholder="密码" prefixIcon="locked"
|
|
||||||
@confirm="handleRegister" />
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<uni-forms-item name="confirmPassword">
|
// 分开控制发送验证码和注册的 loading 状态
|
||||||
<uni-easyinput v-model="registerForm.confirmPassword" type="password" placeholder="确认密码"
|
const sendCodeLoading = ref(false);
|
||||||
prefixIcon="locked" @confirm="handleRegister" />
|
const registerLoading = ref(false);
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<uni-forms-item name="code" v-if="captchaEnabled">
|
const registerRef = ref<any>(null);
|
||||||
<view class="code-input-wrapper">
|
|
||||||
<uni-easyinput v-model="registerForm.code" type="text" placeholder="验证码" prefixIcon="code"
|
|
||||||
@confirm="handleRegister" />
|
|
||||||
<view class="code-img-wrapper">
|
|
||||||
<image :src="codeUrl" class="code-img" @tap="getCode"></image>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<uni-forms-item>
|
// 表单校验规则
|
||||||
<uni-button type="primary" size="default" :loading="loading" @click="handleRegister"
|
const registerRules = {
|
||||||
class="register-btn">
|
username: [
|
||||||
注册
|
{ required: true, message: "请输入您的账号", trigger: "blur" },
|
||||||
</uni-button>
|
{ min: 2, max: 20, message: "用户账号长度必须介于 2 和 20 之间", trigger: "blur" }
|
||||||
<view class="login-link">
|
],
|
||||||
<text>已有账号?</text>
|
password: [
|
||||||
<navigator url="/pages/login/login" class="link-text">立即登录</navigator>
|
{ required: true, message: "请输入您的密码", trigger: "blur" },
|
||||||
</view>
|
{ min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" },
|
||||||
</uni-forms-item>
|
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{ required: true, message: "请再次输入您的密码", trigger: "blur" },
|
||||||
|
{
|
||||||
|
validator: (rule: any, value: any, callback: any) => {
|
||||||
|
if (registerForm.value.password !== value) {
|
||||||
|
callback(new Error("两次输入的密码不一致"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
code: [{ required: true, message: "请输入验证码", trigger: "change" }]
|
||||||
|
};
|
||||||
|
|
||||||
</uni-forms>
|
// 用户仓库
|
||||||
</view>
|
const userStore = useUserStore();
|
||||||
|
|
||||||
<view class="el-register-footer">
|
// 获取图形验证码
|
||||||
<text>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</text>
|
function getCode() {
|
||||||
</view>
|
if (!props.captchaEnabled) return;
|
||||||
</view>
|
getCodeImg().then((res: any) => {
|
||||||
</template>
|
codeUrl.value = "data:image/gif;base64," + res.img;
|
||||||
|
registerForm.value.uuid = res.uuid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
<script setup>
|
// 发送验证码
|
||||||
import {
|
function sendCode() {
|
||||||
ref,
|
if (props.method === 'email') {
|
||||||
onReady
|
sendEmailCode(registerForm.value, 'register');
|
||||||
} from 'vue';
|
} else if (props.method === 'phone') {
|
||||||
import {
|
sendPhoneCode(registerForm.value, 'register');
|
||||||
getCodeImg,
|
}
|
||||||
register
|
}
|
||||||
} from '@/api/login'; // 假设 API 适配了 Uniapp
|
|
||||||
|
|
||||||
// 表单引用
|
// 注册提交
|
||||||
const registerFormRef = ref(null);
|
function handleRegister() {
|
||||||
|
registerRef.value.validate((valid: boolean) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
userStore.register(registerForm.value, props.method).then(() => {
|
||||||
|
uni.showModal({
|
||||||
|
title: "系统提示",
|
||||||
|
content: `<font color='red'>恭喜你,您的账号 ${registerForm.value.username} 注册成功!</font>`,
|
||||||
|
showCancel: false,
|
||||||
|
success: () => {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: "/pages/login/login" // 跳转到登录页
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
getCode();
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 表单数据
|
// 页面加载时获取验证码
|
||||||
const registerForm = ref({
|
onMounted(() => {
|
||||||
username: "",
|
getCode();
|
||||||
password: "",
|
});
|
||||||
confirmPassword: "",
|
|
||||||
code: "",
|
|
||||||
uuid: ""
|
|
||||||
});
|
|
||||||
|
|
||||||
// 校验规则
|
|
||||||
const registerRules = {
|
|
||||||
username: [{
|
|
||||||
required: true,
|
|
||||||
errorMessage: '请输入您的账号'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
minLength: 2,
|
|
||||||
maxLength: 20,
|
|
||||||
errorMessage: '用户账号长度必须介于 2 和 20 之间'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
password: [{
|
|
||||||
required: true,
|
|
||||||
errorMessage: '请输入您的密码'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
minLength: 5,
|
|
||||||
maxLength: 20,
|
|
||||||
errorMessage: '用户密码长度必须介于 5 和 20 之间'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: /^[^<>"'|\\]+$/,
|
|
||||||
errorMessage: '不能包含非法字符:< > " \' \\ |'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
confirmPassword: [{
|
|
||||||
required: true,
|
|
||||||
errorMessage: '请再次输入您的密码'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
validator: (rule, value, callback, source, options) => {
|
|
||||||
if (value !== registerForm.value.password) {
|
|
||||||
callback(new Error('两次输入的密码不一致'));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
errorMessage: '两次输入的密码不一致'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
code: [{
|
|
||||||
required: true,
|
|
||||||
errorMessage: '请输入验证码'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
// 状态变量
|
|
||||||
const codeUrl = ref("");
|
|
||||||
const loading = ref(false);
|
|
||||||
const captchaEnabled = ref(true);
|
|
||||||
|
|
||||||
// 获取验证码
|
|
||||||
const getCode = () => {
|
|
||||||
getCodeImg().then(res => {
|
|
||||||
// 假设 res.data 结构为 { img: 'base64...', uuid: '...', captchaEnabled: true }
|
|
||||||
captchaEnabled.value = res.data.captchaEnabled !== false;
|
|
||||||
if (captchaEnabled.value) {
|
|
||||||
codeUrl.value = res.data.img; // Uniapp 的 image 组件可以直接显示 base64
|
|
||||||
registerForm.value.uuid = res.data.uuid;
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
console.error("获取验证码失败:", err);
|
|
||||||
uni.showToast({
|
|
||||||
title: '获取验证码失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理注册
|
|
||||||
const handleRegister = () => {
|
|
||||||
// 使用 uni-forms 的 validate 方法
|
|
||||||
registerFormRef.value.validate().then(() => {
|
|
||||||
loading.value = true;
|
|
||||||
register(registerForm.value).then(res => {
|
|
||||||
uni.showModal({
|
|
||||||
title: '系统提示',
|
|
||||||
content: `<font color='red'>恭喜你,您的账号 ${registerForm.value.username} 注册成功!</font>`,
|
|
||||||
showCancel: false,
|
|
||||||
success: () => {
|
|
||||||
// 注册成功跳转到登录页,关闭当前页
|
|
||||||
uni.redirectTo({
|
|
||||||
url: '/pages/login/login'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch(err => {
|
|
||||||
// 错误处理,如用户名已存在等
|
|
||||||
uni.showToast({
|
|
||||||
title: err.message || '注册失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
if (captchaEnabled.value) {
|
|
||||||
getCode(); // 刷新验证码
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
}).catch(errors => {
|
|
||||||
// 表单校验失败,由 uni-forms 自动提示错误信息
|
|
||||||
console.log('表单校验失败:', errors);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 页面加载时获取验证码
|
|
||||||
onReady(() => {
|
|
||||||
getCode();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<template>
|
||||||
/* 页面整体背景,建议在 pages.json 中配置 "style": { "backgroundImage": "url('/static/login-background.jpg')" } */
|
<view class="register-container">
|
||||||
.register-container {
|
<uni-forms
|
||||||
display: flex;
|
ref="registerRef"
|
||||||
flex-direction: column;
|
:model="registerForm"
|
||||||
align-items: center;
|
:rules="registerRules"
|
||||||
justify-content: center;
|
class="register-form"
|
||||||
min-height: 100vh;
|
>
|
||||||
padding: 20rpx;
|
<!-- 邮箱输入 -->
|
||||||
box-sizing: border-box;
|
<uni-forms-item prop="email" v-if="method === 'email'">
|
||||||
background-color: #f5f5f5;
|
<uni-easyinput
|
||||||
/* 备用背景色 */
|
v-model="registerForm.email"
|
||||||
}
|
type="text"
|
||||||
|
placeholder="邮箱"
|
||||||
|
prefixIcon="email"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
|
||||||
/* 表单外层容器 */
|
<!-- 手机号输入 -->
|
||||||
.register-form-wrapper {
|
<uni-forms-item prop="phonenumber" v-else-if="method === 'phone'">
|
||||||
width: 100%;
|
<uni-easyinput
|
||||||
max-width: 400rpx;
|
v-model="registerForm.phonenumber"
|
||||||
padding: 40rpx 30rpx;
|
type="text"
|
||||||
background-color: #ffffff;
|
placeholder="手机号"
|
||||||
border-radius: 16rpx;
|
prefixIcon="phone"
|
||||||
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.1);
|
/>
|
||||||
}
|
</uni-forms-item>
|
||||||
|
|
||||||
.title {
|
<!-- 用户名输入 -->
|
||||||
text-align: center;
|
<uni-forms-item prop="username" v-else>
|
||||||
font-size: 32rpx;
|
<uni-easyinput
|
||||||
font-weight: 500;
|
v-model="registerForm.username"
|
||||||
color: #333;
|
type="text"
|
||||||
margin-bottom: 40rpx;
|
placeholder="账号"
|
||||||
}
|
prefixIcon="user"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
|
||||||
/* 验证码输入框和图片一行显示 */
|
<!-- 密码输入 -->
|
||||||
.code-input-wrapper {
|
<uni-forms-item prop="password">
|
||||||
display: flex;
|
<uni-easyinput
|
||||||
align-items: center;
|
v-model="registerForm.password"
|
||||||
gap: 16rpx;
|
type="password"
|
||||||
}
|
placeholder="密码"
|
||||||
|
prefixIcon="lock"
|
||||||
|
@keyup.enter="handleRegister"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
|
||||||
.code-img-wrapper {
|
<!-- 确认密码输入 -->
|
||||||
flex-shrink: 0;
|
<uni-forms-item prop="confirmPassword">
|
||||||
width: 140rpx;
|
<uni-easyinput
|
||||||
height: 72rpx;
|
v-model="registerForm.confirmPassword"
|
||||||
/* 与输入框高度对齐 */
|
type="password"
|
||||||
}
|
placeholder="确认密码"
|
||||||
|
prefixIcon="lock"
|
||||||
|
@keyup.enter="handleRegister"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
|
||||||
.code-img {
|
<!-- 验证码输入 -->
|
||||||
width: 100%;
|
<uni-forms-item prop="code">
|
||||||
height: 100%;
|
<view class="code-input">
|
||||||
border-radius: 8rpx;
|
<uni-easyinput
|
||||||
}
|
v-model="registerForm.code"
|
||||||
|
type="text"
|
||||||
|
placeholder="验证码"
|
||||||
|
prefixIcon="code"
|
||||||
|
@keyup.enter="handleRegister"
|
||||||
|
/>
|
||||||
|
|
||||||
/* 注册按钮 */
|
<!-- 图形验证码或发送按钮 -->
|
||||||
.register-btn {
|
<view class="code-action">
|
||||||
width: 100%;
|
<uni-image
|
||||||
height: 80rpx;
|
v-if="captchaEnabled && method === 'password'"
|
||||||
line-height: 80rpx;
|
:src="codeUrl"
|
||||||
font-size: 28rpx;
|
class="code-img"
|
||||||
border-radius: 40rpx;
|
@click="getCode"
|
||||||
margin-bottom: 20rpx;
|
mode="aspectFill"
|
||||||
}
|
/>
|
||||||
|
<uni-button
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
size="mini"
|
||||||
|
@click="sendCode"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
发送验证码
|
||||||
|
</uni-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-forms-item>
|
||||||
|
|
||||||
/* 登录链接 */
|
<!-- 注册按钮 -->
|
||||||
.login-link {
|
<uni-forms-item>
|
||||||
text-align: center;
|
<button
|
||||||
font-size: 24rpx;
|
type="primary"
|
||||||
color: #999;
|
size="default"
|
||||||
}
|
class="register-btn"
|
||||||
|
:loading="loading"
|
||||||
|
@click="handleRegister"
|
||||||
|
>
|
||||||
|
注册
|
||||||
|
</button>
|
||||||
|
</uni-forms-item>
|
||||||
|
|
||||||
.link-text {
|
<!-- 已有账号跳转 -->
|
||||||
color: #007aff;
|
<view class="login-link">
|
||||||
text-decoration: underline;
|
<text class="question-text">已有账号?</text>
|
||||||
}
|
<navigator url="/pages/login" class="link-type">立即登录</navigator>
|
||||||
|
</view>
|
||||||
|
</uni-forms>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
/* 页脚 */
|
<style scoped lang="scss">
|
||||||
.el-register-footer {
|
.register-container {
|
||||||
position: fixed;
|
padding: 40rpx;
|
||||||
bottom: 0;
|
}
|
||||||
width: 100%;
|
|
||||||
height: 80rpx;
|
.register-form {
|
||||||
line-height: 80rpx;
|
background-color: #fff;
|
||||||
text-align: center;
|
padding: 60rpx;
|
||||||
font-size: 20rpx;
|
border-radius: 16rpx;
|
||||||
color: #ccc;
|
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
|
||||||
box-sizing: border-box;
|
}
|
||||||
}
|
|
||||||
|
.code-input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-action {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-img {
|
||||||
|
width: 180rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-link {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-type {
|
||||||
|
color: #007aff;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
314
pages_system/pages/forget/index.vue
Normal file
314
pages_system/pages/forget/index.vue
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
<template>
|
||||||
|
<view class="forget-container">
|
||||||
|
<view class="forget-header">
|
||||||
|
<text class="back-btn" @click="goBack">‹</text>
|
||||||
|
<text class="title">忘记密码</text>
|
||||||
|
<view class="placeholder"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="forget-content">
|
||||||
|
<view class="forget-form">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">手机号</text>
|
||||||
|
<input
|
||||||
|
v-model="forgetForm.phone"
|
||||||
|
class="input"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入注册手机号"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">验证码</text>
|
||||||
|
<view class="code-input">
|
||||||
|
<input
|
||||||
|
v-model="forgetForm.code"
|
||||||
|
class="input"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="send-code-btn"
|
||||||
|
:disabled="countdown > 0"
|
||||||
|
@click="sendCode"
|
||||||
|
>
|
||||||
|
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">新密码</text>
|
||||||
|
<input
|
||||||
|
v-model="forgetForm.newPassword"
|
||||||
|
class="input"
|
||||||
|
password
|
||||||
|
placeholder="请输入新密码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">确认密码</text>
|
||||||
|
<input
|
||||||
|
v-model="forgetForm.confirmPassword"
|
||||||
|
class="input"
|
||||||
|
password
|
||||||
|
placeholder="请再次输入新密码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<button class="confirm-btn" @click="handleResetPassword" :disabled="resetting">
|
||||||
|
{{ resetting ? '重置中...' : '重置密码' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import modal from '@/plugins/modal'
|
||||||
|
import { sendPhoneCode, verifyPhoneCode } from '@/api/login'
|
||||||
|
import { resetUserPwd } from '@/api/system/user'
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const resetting = ref(false)
|
||||||
|
const countdown = ref(0)
|
||||||
|
let countdownTimer = null
|
||||||
|
|
||||||
|
// 忘记密码表单
|
||||||
|
const forgetForm = reactive({
|
||||||
|
phone: '',
|
||||||
|
code: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 发送验证码
|
||||||
|
const sendCode = async () => {
|
||||||
|
if (!forgetForm.phone) {
|
||||||
|
modal.alert('请输入手机号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(forgetForm.phone)) {
|
||||||
|
modal.alert('请输入正确的手机号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendPhoneCode({ phone: forgetForm.phone }, 'reset')
|
||||||
|
modal.alert('验证码已发送')
|
||||||
|
|
||||||
|
// 开始倒计时
|
||||||
|
countdown.value = 60
|
||||||
|
countdownTimer = setInterval(() => {
|
||||||
|
countdown.value--
|
||||||
|
if (countdown.value <= 0) {
|
||||||
|
clearInterval(countdownTimer)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '验证码发送失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理密码重置
|
||||||
|
const handleResetPassword = async () => {
|
||||||
|
if (!validateForm()) return
|
||||||
|
|
||||||
|
resetting.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证验证码
|
||||||
|
await verifyPhoneCode({
|
||||||
|
phone: forgetForm.phone,
|
||||||
|
code: forgetForm.code
|
||||||
|
}, 'reset')
|
||||||
|
|
||||||
|
// 执行密码重置
|
||||||
|
// 这里需要先找到用户ID,实际项目中可能需要根据手机号查询用户信息
|
||||||
|
// 假设我们已经获得了userId
|
||||||
|
const userId = await getUserIdByPhone(forgetForm.phone)
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
modal.alert('未找到该手机号对应的用户')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await resetUserPwd(userId, forgetForm.newPassword)
|
||||||
|
|
||||||
|
modal.alert('密码重置成功', () => {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/login'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '密码重置失败')
|
||||||
|
} finally {
|
||||||
|
resetting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据手机号获取用户ID(模拟实现)
|
||||||
|
const getUserIdByPhone = async (phone) => {
|
||||||
|
// 实际项目中需要调用API查询用户信息
|
||||||
|
// 这里返回一个模拟的userId
|
||||||
|
return '123' // 实际应该从API获取
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
const validateForm = () => {
|
||||||
|
if (!forgetForm.phone) {
|
||||||
|
modal.alert('请输入手机号')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(forgetForm.phone)) {
|
||||||
|
modal.alert('请输入正确的手机号')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forgetForm.code) {
|
||||||
|
modal.alert('请输入验证码')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forgetForm.newPassword) {
|
||||||
|
modal.alert('请输入新密码')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forgetForm.newPassword.length < 6) {
|
||||||
|
modal.alert('密码长度不能少于6位')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forgetForm.newPassword !== forgetForm.confirmPassword) {
|
||||||
|
modal.alert('两次输入的密码不一致')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回登录页面
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理计时器
|
||||||
|
import { onUnmounted } from 'vue'
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (countdownTimer) {
|
||||||
|
clearInterval(countdownTimer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.forget-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forget-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1rpx solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
font-size: 50rpx;
|
||||||
|
color: #333;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
width: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forget-content {
|
||||||
|
padding: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forget-form {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
border: 2rpx solid #e0e0e0;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-code-btn {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-code-btn[disabled] {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn[disabled] {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
473
pages_system/pages/login/login.vue
Normal file
473
pages_system/pages/login/login.vue
Normal file
@ -0,0 +1,473 @@
|
|||||||
|
<template>
|
||||||
|
<view class="login-container">
|
||||||
|
<!-- 顶部标题 -->
|
||||||
|
<view class="login-header">
|
||||||
|
<text class="login-title">欢迎登录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 登录方式切换 -->
|
||||||
|
<view class="login-tabs">
|
||||||
|
<view
|
||||||
|
class="login-tab"
|
||||||
|
:class="{ 'active': loginType === 'account' }"
|
||||||
|
@click="loginType = 'account'"
|
||||||
|
>
|
||||||
|
账号密码
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="login-tab"
|
||||||
|
:class="{ 'active': loginType === 'phone' }"
|
||||||
|
@click="loginType = 'phone'"
|
||||||
|
>
|
||||||
|
手机验证码
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="login-tab"
|
||||||
|
:class="{ 'active': loginType === 'wx' }"
|
||||||
|
@click="loginType = 'wx'"
|
||||||
|
>
|
||||||
|
微信登录
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 登录表单 -->
|
||||||
|
<view class="login-form">
|
||||||
|
<!-- 账号密码登录 -->
|
||||||
|
<view v-if="loginType === 'account'">
|
||||||
|
<view class="form-item">
|
||||||
|
<input
|
||||||
|
v-model="form.username"
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入用户名/手机号"
|
||||||
|
class="input"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<input
|
||||||
|
v-model="form.password"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
class="input"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="form-item captcha">
|
||||||
|
<input
|
||||||
|
v-model="form.code"
|
||||||
|
type="text"
|
||||||
|
placeholder="验证码"
|
||||||
|
class="input"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
:src="captchaUrl"
|
||||||
|
@click="refreshCaptcha"
|
||||||
|
class="captcha-img"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<button
|
||||||
|
class="login-btn"
|
||||||
|
:disabled="isLogining"
|
||||||
|
@click="handleLogin"
|
||||||
|
>
|
||||||
|
{{ isLogining ? '登录中...' : '登录' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 手机验证码登录 -->
|
||||||
|
<view v-else-if="loginType === 'phone'">
|
||||||
|
<view class="form-item">
|
||||||
|
<input
|
||||||
|
v-model="form.phone"
|
||||||
|
type="tel"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
class="input"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<input
|
||||||
|
v-model="form.code"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
class="input"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
v-if="countdown > 0"
|
||||||
|
class="countdown"
|
||||||
|
>{{ countdown }}s</text>
|
||||||
|
<text
|
||||||
|
v-else
|
||||||
|
class="send-code"
|
||||||
|
@click="sendCode"
|
||||||
|
>发送验证码</text>
|
||||||
|
</view>
|
||||||
|
<button
|
||||||
|
class="login-btn"
|
||||||
|
:disabled="isLogining"
|
||||||
|
@click="handleLogin"
|
||||||
|
>
|
||||||
|
{{ isLogining ? '登录中...' : '登录' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 微信登录 -->
|
||||||
|
<view v-else-if="loginType === 'wx'">
|
||||||
|
<button
|
||||||
|
class="wx-login-btn"
|
||||||
|
@click="wxLogin"
|
||||||
|
:disabled="isLogining"
|
||||||
|
>
|
||||||
|
<image src="/static/icon-wechat.png" class="wx-icon" />
|
||||||
|
微信一键登录
|
||||||
|
</button>
|
||||||
|
<view class="one-click-login" @click="getPhone">
|
||||||
|
<image src="/static/icon-phone.png" class="phone-icon" />
|
||||||
|
本机一键登录
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部链接 -->
|
||||||
|
<view class="login-footer">
|
||||||
|
<text class="link" @click="toRegister">注册新账号</text>
|
||||||
|
<text class="link" @click="toForget">忘记密码</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { login, getCodeImg, sendPhoneCode, verifyPhoneCode } from '@/api/login';
|
||||||
|
import { register } from '@/api/system/user';
|
||||||
|
import { getWxCode } from '@/utils/geek';
|
||||||
|
import { wxLogin, wxRegister } from '@/api/oauth';
|
||||||
|
import { setToken } from '@/utils/auth';
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
import modal from '@/plugins/modal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
// 当前登录方式 (account/phone/wx)
|
||||||
|
const loginType = ref('account');
|
||||||
|
// 表单数据
|
||||||
|
const form = ref({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
phone: '',
|
||||||
|
code: '',
|
||||||
|
captcha: ''
|
||||||
|
});
|
||||||
|
// 验证码图片
|
||||||
|
const captchaUrl = ref('');
|
||||||
|
// 倒计时(用于发送验证码)
|
||||||
|
const countdown = ref(0);
|
||||||
|
// 登录中状态
|
||||||
|
const isLogining = ref(false);
|
||||||
|
// 用户store
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
// 初始化验证码
|
||||||
|
const initCaptcha = () => {
|
||||||
|
getCodeImg().then(res => {
|
||||||
|
captchaUrl.value = res;
|
||||||
|
}).catch(() => {
|
||||||
|
modal.toast('验证码加载失败');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新验证码
|
||||||
|
const refreshCaptcha = () => {
|
||||||
|
initCaptcha();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送手机验证码
|
||||||
|
const sendCode = () => {
|
||||||
|
if (!form.value.phone) {
|
||||||
|
modal.toast('请输入手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendPhoneCode({ phone: form.value.phone }).then(() => {
|
||||||
|
modal.toast('验证码已发送');
|
||||||
|
countdown.value = 60;
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
countdown.value--;
|
||||||
|
if (countdown.value <= 0) clearInterval(timer);
|
||||||
|
}, 1000);
|
||||||
|
}).catch(err => {
|
||||||
|
modal.toast(err.message || '发送失败');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理登录
|
||||||
|
const handleLogin = async () => {
|
||||||
|
isLogining.value = true;
|
||||||
|
try {
|
||||||
|
if (loginType.value === 'account') {
|
||||||
|
// 账号密码登录
|
||||||
|
if (!form.value.username || !form.value.password || !form.value.code) {
|
||||||
|
modal.toast('请填写完整信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await login(
|
||||||
|
form.value.username,
|
||||||
|
form.value.password,
|
||||||
|
form.value.code,
|
||||||
|
''
|
||||||
|
);
|
||||||
|
handleLoginSuccess(res);
|
||||||
|
} else if (loginType.value === 'phone') {
|
||||||
|
// 手机验证码登录
|
||||||
|
if (!form.value.phone || !form.value.code) {
|
||||||
|
modal.toast('请填写完整信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 1. 验证手机验证码
|
||||||
|
await verifyPhoneCode({ phone: form.value.phone, code: form.value.code });
|
||||||
|
// 2. 检查用户是否存在(自动注册逻辑)
|
||||||
|
try {
|
||||||
|
// 尝试用手机号登录(后端会自动注册新用户)
|
||||||
|
const res = await login(
|
||||||
|
form.value.phone,
|
||||||
|
'123456', // 默认密码(首次登录自动注册时使用)
|
||||||
|
'',
|
||||||
|
''
|
||||||
|
);
|
||||||
|
handleLoginSuccess(res);
|
||||||
|
} catch (err) {
|
||||||
|
// 用户不存在,自动注册
|
||||||
|
await register({
|
||||||
|
username: form.value.phone,
|
||||||
|
phonenumber: form.value.phone,
|
||||||
|
password: '123456'
|
||||||
|
});
|
||||||
|
// 注册后重新登录
|
||||||
|
const res = await login(
|
||||||
|
form.value.phone,
|
||||||
|
'123456',
|
||||||
|
'',
|
||||||
|
''
|
||||||
|
);
|
||||||
|
handleLoginSuccess(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
modal.toast(err.message || '登录失败');
|
||||||
|
} finally {
|
||||||
|
isLogining.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理登录成功
|
||||||
|
const handleLoginSuccess = (res) => {
|
||||||
|
setToken(res.token);
|
||||||
|
userStore.setUserInfo(res.user);
|
||||||
|
uni.switchTab({ url: '/pages/index' });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 微信登录
|
||||||
|
const wxLogin = async () => {
|
||||||
|
isLogining.value = true;
|
||||||
|
try {
|
||||||
|
const code = await getWxCode();
|
||||||
|
const res = await wxLogin('pub', code); // pub: 公众号/小程序
|
||||||
|
if (res.token) {
|
||||||
|
handleLoginSuccess(res);
|
||||||
|
} else {
|
||||||
|
modal.toast('微信登录失败,请重试');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
modal.toast('微信登录异常: ' + err.message);
|
||||||
|
} finally {
|
||||||
|
isLogining.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 本机一键登录(获取手机号)
|
||||||
|
const getPhone = async () => {
|
||||||
|
try {
|
||||||
|
const res = await uni.getPhoneNumber();
|
||||||
|
if (res.code === 0) {
|
||||||
|
form.value.phone = res.code;
|
||||||
|
modal.toast('手机号获取成功');
|
||||||
|
} else {
|
||||||
|
modal.toast('获取手机号失败');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
modal.toast('获取手机号异常: ' + err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到注册页面
|
||||||
|
const toRegister = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_system/pages/login/register'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到忘记密码页面
|
||||||
|
const toForget = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_system/pages/login/forget'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initCaptcha();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
loginType,
|
||||||
|
form,
|
||||||
|
captchaUrl,
|
||||||
|
countdown,
|
||||||
|
isLogining,
|
||||||
|
handleLogin,
|
||||||
|
sendCode,
|
||||||
|
refreshCaptcha,
|
||||||
|
wxLogin,
|
||||||
|
getPhone,
|
||||||
|
toRegister,
|
||||||
|
toForget
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login-container {
|
||||||
|
padding: 40rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-tabs {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-tab {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-tab.active {
|
||||||
|
background: #007AFF;
|
||||||
|
color: white;
|
||||||
|
border-color: #007AFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
background: white;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20rpx;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-img {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown {
|
||||||
|
color: #007AFF;
|
||||||
|
font-size: 26rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-code {
|
||||||
|
color: #007AFF;
|
||||||
|
font-size: 26rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn {
|
||||||
|
background: #007AFF;
|
||||||
|
color: white;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wx-login-btn {
|
||||||
|
background: #07C160;
|
||||||
|
color: white;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wx-icon {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.one-click-login {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
color: #666;
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-top: 30rpx;
|
||||||
|
padding: 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-icon {
|
||||||
|
width: 36rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 60rpx;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: #007AFF;
|
||||||
|
margin: 0 20rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
524
pages_system/pages/register/index.vue
Normal file
524
pages_system/pages/register/index.vue
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
<template>
|
||||||
|
<view class="register-container">
|
||||||
|
<view class="register-header">
|
||||||
|
<text class="back-btn" @click="goBack">‹</text>
|
||||||
|
<text class="title">用户注册</text>
|
||||||
|
<view class="placeholder"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="register-content">
|
||||||
|
<view class="register-form">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">用户名</text>
|
||||||
|
<input
|
||||||
|
v-model="registerForm.username"
|
||||||
|
class="input"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">手机号</text>
|
||||||
|
<input
|
||||||
|
v-model="registerForm.phone"
|
||||||
|
class="input"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">验证码</text>
|
||||||
|
<view class="code-input">
|
||||||
|
<input
|
||||||
|
v-model="registerForm.code"
|
||||||
|
class="input"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="send-code-btn"
|
||||||
|
:disabled="countdown > 0"
|
||||||
|
@click="sendCode"
|
||||||
|
>
|
||||||
|
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">密码</text>
|
||||||
|
<input
|
||||||
|
v-model="registerForm.password"
|
||||||
|
class="input"
|
||||||
|
password
|
||||||
|
placeholder="请输入密码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">确认密码</text>
|
||||||
|
<input
|
||||||
|
v-model="registerForm.confirmPassword"
|
||||||
|
class="input"
|
||||||
|
password
|
||||||
|
placeholder="请再次输入密码"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="form-item" v-if="registerForm.showEmail">
|
||||||
|
<text class="label">邮箱</text>
|
||||||
|
<input
|
||||||
|
v-model="registerForm.email"
|
||||||
|
class="input"
|
||||||
|
type="email"
|
||||||
|
placeholder="请输入邮箱(选填)"
|
||||||
|
placeholder-class="placeholder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="agreement">
|
||||||
|
<checkbox-group @change="toggleAgreement">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<checkbox :checked="agreed" color="#007aff" />
|
||||||
|
<text class="agreement-text">我已阅读并同意</text>
|
||||||
|
</label>
|
||||||
|
</checkbox-group>
|
||||||
|
<text class="agreement-link" @click="showAgreement">《用户协议》</text>
|
||||||
|
<text class="agreement-link" @click="showPrivacy">《隐私政策》</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<button class="register-btn" @click="handleRegister" :disabled="registering">
|
||||||
|
{{ registering ? '注册中...' : '注册' }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<view class="login-link">
|
||||||
|
<text>已有账号? </text>
|
||||||
|
<text class="link" @click="goLogin">立即登录</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 第三方注册 -->
|
||||||
|
<view class="third-register">
|
||||||
|
<view class="divider">
|
||||||
|
<text class="divider-text">第三方注册</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="register-methods">
|
||||||
|
<view class="method-item" @click="handleWechatRegister">
|
||||||
|
<view class="method-icon wechat">
|
||||||
|
<text class="iconfont">💬</text>
|
||||||
|
</view>
|
||||||
|
<text class="method-text">微信注册</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
|
import modal from '@/plugins/modal'
|
||||||
|
import { getCodeImg, login, sendPhoneCode, verifyPhoneCode } from '@/api/login'
|
||||||
|
import { getWxCode } from '@/utils/geek'
|
||||||
|
import { wxLogin } from '@/api/oauth'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { setToken } from '@/utils/auth'
|
||||||
|
import config from '@/config.js'
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const registering = ref(false)
|
||||||
|
const agreed = ref(false)
|
||||||
|
const countdown = ref(0)
|
||||||
|
let countdownTimer = null
|
||||||
|
|
||||||
|
// 注册表单
|
||||||
|
const registerForm = reactive({
|
||||||
|
username: '',
|
||||||
|
phone: '',
|
||||||
|
code: '',
|
||||||
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
email: '',
|
||||||
|
showEmail: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 发送验证码
|
||||||
|
const sendCode = async () => {
|
||||||
|
if (!registerForm.phone) {
|
||||||
|
modal.alert('请输入手机号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(registerForm.phone)) {
|
||||||
|
modal.alert('请输入正确的手机号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendPhoneCode({ phone: registerForm.phone }, 'register')
|
||||||
|
modal.alert('验证码已发送')
|
||||||
|
|
||||||
|
// 开始倒计时
|
||||||
|
countdown.value = 60
|
||||||
|
countdownTimer = setInterval(() => {
|
||||||
|
countdown.value--
|
||||||
|
if (countdown.value <= 0) {
|
||||||
|
clearInterval(countdownTimer)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '验证码发送失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理注册
|
||||||
|
const handleRegister = async () => {
|
||||||
|
if (!validateForm()) return
|
||||||
|
|
||||||
|
registering.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证验证码
|
||||||
|
await verifyPhoneCode({
|
||||||
|
phone: registerForm.phone,
|
||||||
|
code: registerForm.code
|
||||||
|
}, 'register')
|
||||||
|
|
||||||
|
// 执行注册
|
||||||
|
const params = {
|
||||||
|
username: registerForm.username,
|
||||||
|
password: registerForm.password,
|
||||||
|
phonenumber: registerForm.phone
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registerForm.email) {
|
||||||
|
params.email = registerForm.email
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await register(params)
|
||||||
|
|
||||||
|
if (res.token) {
|
||||||
|
setToken(res.token)
|
||||||
|
await userStore.getInfo()
|
||||||
|
modal.alert('注册成功', () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '注册失败')
|
||||||
|
} finally {
|
||||||
|
registering.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
const validateForm = () => {
|
||||||
|
if (!registerForm.username) {
|
||||||
|
modal.alert('请输入用户名')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!registerForm.phone) {
|
||||||
|
modal.alert('请输入手机号')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(registerForm.phone)) {
|
||||||
|
modal.alert('请输入正确的手机号')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!registerForm.code) {
|
||||||
|
modal.alert('请输入验证码')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!registerForm.password) {
|
||||||
|
modal.alert('请输入密码')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registerForm.password.length < 6) {
|
||||||
|
modal.alert('密码长度不能少于6位')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registerForm.password !== registerForm.confirmPassword) {
|
||||||
|
modal.alert('两次输入的密码不一致')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!agreed.value) {
|
||||||
|
modal.alert('请同意用户协议和隐私政策')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 微信注册
|
||||||
|
const handleWechatRegister = async () => {
|
||||||
|
try {
|
||||||
|
const code = await getWxCode()
|
||||||
|
const res = await wxRegister('miniapp', code)
|
||||||
|
|
||||||
|
if (res.token) {
|
||||||
|
setToken(res.token)
|
||||||
|
await userStore.getInfo()
|
||||||
|
modal.alert('注册成功', () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
modal.alert(error.message || '微信注册失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换协议同意状态
|
||||||
|
const toggleAgreement = (e) => {
|
||||||
|
agreed.value = e.detail.value.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示用户协议
|
||||||
|
const showAgreement = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_system/pages/login/agreement?type=user'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示隐私政策
|
||||||
|
const showPrivacy = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_system/pages/login/agreement?type=privacy'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回登录页面
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
const goLogin = () => {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/login'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理计时器
|
||||||
|
import { onUnmounted } from 'vue'
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (countdownTimer) {
|
||||||
|
clearInterval(countdownTimer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.register-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1rpx solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
font-size: 50rpx;
|
||||||
|
color: #333;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
width: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-content {
|
||||||
|
padding: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-form {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
border: 2rpx solid #e0e0e0;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-code-btn {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-code-btn[disabled] {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agreement {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 40rpx 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agreement-text {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agreement-link {
|
||||||
|
color: #007aff;
|
||||||
|
margin: 0 5rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-btn[disabled] {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-link {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.third-register {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1rpx;
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider-text {
|
||||||
|
background: #fff;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
color: #999;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-methods {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-icon {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
font-size: 40rpx;
|
||||||
|
background: #07c160;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -5,4 +5,5 @@ const constant = {
|
|||||||
permissions: 'vuex_permissions'
|
permissions: 'vuex_permissions'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default constant
|
|
||||||
|
export default constant
|
||||||
|
|||||||
133
utils/moudlesData.ts
Normal file
133
utils/moudlesData.ts
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
const moudleGroups = [{
|
||||||
|
name: '系统管理',
|
||||||
|
color: '#007AFF',
|
||||||
|
items: [{
|
||||||
|
name: '用户管理',
|
||||||
|
icon: 'person-filled',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '角色管理',
|
||||||
|
icon: 'staff-filled',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
|
||||||
|
}, {
|
||||||
|
name: '菜单管理',
|
||||||
|
icon: 'color',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
|
||||||
|
}, {
|
||||||
|
name: '部门管理',
|
||||||
|
icon: 'settings-filled',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
|
||||||
|
}, {
|
||||||
|
name: '岗位管理',
|
||||||
|
icon: 'heart-filled',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
|
||||||
|
}, {
|
||||||
|
name: '字典管理',
|
||||||
|
icon: 'bars',
|
||||||
|
path: `/pages_system/pages/dict/index`
|
||||||
|
, params: ''
|
||||||
|
|
||||||
|
}, {
|
||||||
|
name: '参数设置',
|
||||||
|
icon: 'gear-filled',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '通知公告',
|
||||||
|
icon: 'chat-filled',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
name: '日志管理',
|
||||||
|
icon: 'wallet-filled',
|
||||||
|
path: '',
|
||||||
|
params: ''
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
name: '流量计算',
|
||||||
|
color: '#007AFF',
|
||||||
|
items: [{
|
||||||
|
name: '差压式流量计算',
|
||||||
|
icon: 'smallcircle',
|
||||||
|
path: `/pages_caltools/pages/main`,
|
||||||
|
params: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '速度式流量计算',
|
||||||
|
icon: 'paperplane',
|
||||||
|
path: `/pages_caltools/pages/main`,
|
||||||
|
params: 1
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '参数计算',
|
||||||
|
color: '#5AC8FA',
|
||||||
|
items: [{
|
||||||
|
name: '压缩因子',
|
||||||
|
icon: 'pyq',
|
||||||
|
path: `/pages_caltools/pages/main`,
|
||||||
|
params: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '声速计算',
|
||||||
|
icon: 'sound',
|
||||||
|
path: `/pages_caltools/pages/main`,
|
||||||
|
params: 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '发热量',
|
||||||
|
icon: 'fire',
|
||||||
|
path: `/pages_caltools/pages/main`,
|
||||||
|
params: 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '其他参数',
|
||||||
|
icon: 'more',
|
||||||
|
path: `/pages_caltools/pages/main`,
|
||||||
|
params: 7
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default moudleGroups
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用方法:从模块组中提取数据
|
||||||
|
* @param {Array} groupNames - 要提取的模块组名称数组
|
||||||
|
* @param {boolean} mergeItems - 是否合并items,true为合并,false为保持原结构
|
||||||
|
* @param {Function} transformFn - 可选的转换函数,用于自定义输出格式
|
||||||
|
* @returns {Array} 提取后的数据
|
||||||
|
*/
|
||||||
|
export function extractModuleData(groupNames, mergeItems = false, transformFn = null) {
|
||||||
|
if (!Array.isArray(groupNames) || groupNames.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤出指定的模块组
|
||||||
|
const filteredGroups = moudleGroups.filter(group => groupNames.includes(group.name));
|
||||||
|
|
||||||
|
if (mergeItems) {
|
||||||
|
// 合并所有items
|
||||||
|
const mergedItems = filteredGroups.flatMap(group => group.items || []);
|
||||||
|
return transformFn ? transformFn(mergedItems) : mergedItems;
|
||||||
|
} else {
|
||||||
|
// 保持原结构
|
||||||
|
return transformFn ? transformFn(filteredGroups) : filteredGroups;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user