Compare commits
No commits in common. "0f9a78b64ccbadf711093564e2f209456a31a3e8" and "70d088a4ba62e5cb257dbba96955b4805d3498e8" have entirely different histories.
0f9a78b64c
...
70d088a4ba
61
api/login.js
61
api/login.js
@ -46,7 +46,6 @@ export function logout() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
export function getCodeImg() {
|
export function getCodeImg() {
|
||||||
return request({
|
return request({
|
||||||
@ -57,64 +56,4 @@ export function getCodeImg() {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
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,50 +1,6 @@
|
|||||||
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 = {
|
||||||
@ -83,29 +39,3 @@ 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,42 +256,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
|
|
||||||
"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 {
|
||||||
|
|||||||
298
pages/index.vue
298
pages/index.vue
@ -1,159 +1,179 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<!-- 宫格组件 -->
|
<!-- 宫格组件 -->
|
||||||
<view class="grid-body" v-for="(group, groupIndex) in moudlesGroups">
|
<uni-section title="系统管理" type="line"></uni-section>
|
||||||
<uni-section :title="group.name" type="line"></uni-section>
|
<view class="grid-body">
|
||||||
<uni-grid :column="4" :showBorder="false">
|
<uni-grid :column="4" :showBorder="false" @change="changeGrid">
|
||||||
<uni-grid-item v-for="(item, itemIndex) in group.items" :key="itemIndex"
|
<uni-grid-item>
|
||||||
@click="navigateToMoudles(item)" class="grid-item">
|
<view class="grid-item-box">
|
||||||
<view class="grid-item-box">
|
<uni-icons type="person-filled" size="30"></uni-icons>
|
||||||
<uni-icons :type="item.icon" size="30"></uni-icons>
|
<text class="text">用户管理</text>
|
||||||
<text class="text">{{item.name}}</text>
|
</view>
|
||||||
</view>
|
</uni-grid-item>
|
||||||
</uni-grid-item>
|
<uni-grid-item>
|
||||||
</uni-grid>
|
<view class="grid-item-box">
|
||||||
</view>
|
<uni-icons type="staff-filled" size="30"></uni-icons>
|
||||||
</view>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import { ref } from "vue";
|
||||||
ref,
|
import modal from "@/plugins/modal"
|
||||||
onMounted
|
const current = ref(0);
|
||||||
} from "vue";
|
const swiperDotIndex = ref(0);
|
||||||
import modal from "@/plugins/modal"
|
const data = ref([
|
||||||
|
{ image: '/static/images/banner/banner01.jpg' },
|
||||||
|
{ image: '/static/images/banner/banner02.jpg' },
|
||||||
|
{ image: '/static/images/banner/banner03.jpg' }
|
||||||
|
]);
|
||||||
|
|
||||||
import {
|
function clickBannerItem(item) {
|
||||||
extractModuleData
|
console.info(item)
|
||||||
} 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>
|
||||||
|
|||||||
@ -1,266 +0,0 @@
|
|||||||
<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>
|
|
||||||
809
pages/login.vue
809
pages/login.vue
@ -1,640 +1,219 @@
|
|||||||
|
<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="login-container">
|
<view class="normal-login-container">
|
||||||
<view class="login-header">
|
<view class="logo-content align-center justify-center flex">
|
||||||
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
|
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
|
||||||
<text class="app-name">{{ appName }}</text>
|
</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>
|
||||||
|
|
||||||
<view class="login-content">
|
<view class="xieyi text-center">
|
||||||
<!-- 登录方式切换 -->
|
<text class="text-grey1">登录即代表同意</text>
|
||||||
<view class="login-tabs">
|
<text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
|
||||||
<view
|
<text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: activeTab === 'account' }"
|
|
||||||
@click="switchTab('account')"
|
|
||||||
>
|
|
||||||
账号登录
|
|
||||||
</view>
|
|
||||||
<view
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: activeTab === 'phone' }"
|
|
||||||
@click="switchTab('phone')"
|
|
||||||
>
|
|
||||||
手机登录
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 账号密码登录 -->
|
|
||||||
<view v-if="activeTab === 'account'" class="login-form">
|
|
||||||
<view class="form-item">
|
|
||||||
<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>
|
||||||
<script setup>
|
page {
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
background-color: #ffffff;
|
||||||
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 {
|
||||||
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('验证码获取失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送手机验证码
|
|
||||||
const handlesendPhoneCode = async () => {
|
|
||||||
if (!phoneForm.phone) {
|
|
||||||
modal.alert('请输入手机号')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!/^1[3-9]\d{9}$/.test(phoneForm.phone)) {
|
|
||||||
modal.alert('请输入正确的手机号')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await sendPhoneCode({ phone: phoneForm.phone }, 'login')
|
|
||||||
modal.alert('验证码已发送')
|
|
||||||
|
|
||||||
// 开始倒计时
|
|
||||||
phoneForm.countdown = 60
|
|
||||||
phoneForm.timer = setInterval(() => {
|
|
||||||
phoneForm.countdown--
|
|
||||||
if (phoneForm.countdown <= 0) {
|
|
||||||
clearInterval(phoneForm.timer)
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
} catch (error) {
|
|
||||||
modal.alert(error.message || '验证码发送失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 账号密码登录
|
|
||||||
const handleAccountLogin = async () => {
|
|
||||||
if (!accountForm.username) {
|
|
||||||
modal.alert('请输入账号')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountForm.showCaptcha) {
|
|
||||||
params.code = accountForm.code
|
|
||||||
params.uuid = accountForm.uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await login(params.username, params.password, params.code, params.uuid)
|
|
||||||
|
|
||||||
if (res.token) {
|
|
||||||
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) {
|
|
||||||
// 需要绑定微信
|
|
||||||
modal.confirm('首次使用微信登录,需要绑定账号', () => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages_system/pages/login/bind-account'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
modal.alert(error.message || '微信登录失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 跳转注册页面
|
|
||||||
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%;
|
width: 100%;
|
||||||
height: 80rpx;
|
|
||||||
border: 2rpx solid #e0e0e0;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
padding: 0 20rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
.logo-content {
|
||||||
color: #ccc;
|
width: 100%;
|
||||||
}
|
font-size: 21px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 15%;
|
||||||
|
|
||||||
.captcha-input, .code-input {
|
image {
|
||||||
display: flex;
|
border-radius: 4px;
|
||||||
align-items: center;
|
}
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.captcha-img {
|
.title {
|
||||||
width: 200rpx;
|
margin-left: 10px;
|
||||||
height: 80rpx;
|
}
|
||||||
border-radius: 10rpx;
|
}
|
||||||
border: 2rpx solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-code-btn {
|
.login-form-content {
|
||||||
width: 200rpx;
|
text-align: center;
|
||||||
height: 80rpx;
|
margin: 20px auto;
|
||||||
background: #007aff;
|
margin-top: 15%;
|
||||||
color: #fff;
|
width: 80%;
|
||||||
border: none;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
font-size: 24rpx;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-code-btn[disabled] {
|
.input-item {
|
||||||
background: #ccc;
|
margin: 20px auto;
|
||||||
}
|
background-color: #f5f6f7;
|
||||||
|
height: 45px;
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
.form-options {
|
.icon {
|
||||||
display: flex;
|
font-size: 38rpx;
|
||||||
justify-content: flex-end;
|
margin-left: 10px;
|
||||||
margin-bottom: 40rpx;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.forget-pwd {
|
.input {
|
||||||
color: #007aff;
|
width: 100%;
|
||||||
font-size: 26rpx;
|
font-size: 14px;
|
||||||
}
|
line-height: 20px;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.login-btn {
|
}
|
||||||
width: 100%;
|
|
||||||
height: 88rpx;
|
|
||||||
background: #007aff;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
border-radius: 44rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
margin-bottom: 40rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn[disabled] {
|
.action-btn {
|
||||||
background: #ccc;
|
margin-top: 40px;
|
||||||
}
|
|
||||||
|
|
||||||
.quick-login {
|
.login-btn {
|
||||||
margin-bottom: 40rpx;
|
height: 45px;
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
&+.login-btn {
|
||||||
position: relative;
|
margin-top: 20px;
|
||||||
text-align: center;
|
}
|
||||||
margin: 40rpx 0;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 1rpx;
|
|
||||||
background: #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider-text {
|
.xieyi {
|
||||||
background: #fff;
|
color: #333;
|
||||||
padding: 0 20rpx;
|
margin-top: 20px;
|
||||||
color: #999;
|
}
|
||||||
font-size: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-methods {
|
.login-code {
|
||||||
display: flex;
|
height: 38px;
|
||||||
justify-content: center;
|
float: right;
|
||||||
gap: 80rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.method-item {
|
.login-code-img {
|
||||||
display: flex;
|
height: 38px;
|
||||||
flex-direction: column;
|
position: absolute;
|
||||||
align-items: center;
|
margin-left: 10px;
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
.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>
|
|
||||||
|
|||||||
48
pages/template - 副本.vue
Normal file
48
pages/template - 副本.vue
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<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: '',
|
thumbnail: '/static/charts/heat-value-thumb.jpg',
|
||||||
description: '热值关系图表'
|
description: '热值关系图表'
|
||||||
}],
|
}],
|
||||||
tables: [],
|
tables: [],
|
||||||
|
|||||||
179
pages/work.vue
179
pages/work.vue
@ -1,179 +0,0 @@
|
|||||||
<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: 22rpx;
|
font-size: 26rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.uni-input-input) {
|
:deep(.uni-input-input) {
|
||||||
|
|||||||
@ -27,24 +27,54 @@
|
|||||||
|
|
||||||
<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 = ref([]);
|
const calcGroups = [{
|
||||||
|
name: '流量计算',
|
||||||
onMounted(() => {
|
color: '#007AFF',
|
||||||
calcGroups.value = extractModuleData(['流量计算', '参数计算'], false)
|
items: [{
|
||||||
console.log(calcGroups.value);
|
name: '差压式流量计算',
|
||||||
})
|
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 滑动切换事件
|
||||||
@ -59,10 +89,12 @@
|
|||||||
* @param {Object} item 点击的项
|
* @param {Object} item 点击的项
|
||||||
*/
|
*/
|
||||||
const navigateToCalc = (item) => {
|
const navigateToCalc = (item) => {
|
||||||
const dMeterType = item.params;
|
const dMeterType = item.dMeterType;
|
||||||
|
console.log(`导航到计算页面,dMeterType: ${dMeterType}`);
|
||||||
|
|
||||||
// 使用 UniApp 原生导航 API
|
// 使用 UniApp 原生导航 API
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: item.path + `?dMeterType=${dMeterType}`
|
url: `/pages_caltools/pages/main?dMeterType=${dMeterType}`
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,277 +1,273 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, ref } from "vue";
|
|
||||||
import { getCodeImg, sendEmailCode, sendPhoneCode } from "@/api/login";
|
|
||||||
import useUserStore from "@/store/modules/user";
|
|
||||||
|
|
||||||
// 定义 props
|
|
||||||
const props = defineProps<{
|
|
||||||
register: boolean;
|
|
||||||
captchaEnabled: boolean;
|
|
||||||
method: 'password' | 'phone' | 'email';
|
|
||||||
}>();
|
|
||||||
|
|
||||||
// 响应式数据
|
|
||||||
const registerForm = ref({
|
|
||||||
username: "",
|
|
||||||
password: "",
|
|
||||||
confirmPassword: "",
|
|
||||||
email: '',
|
|
||||||
phonenumber: '',
|
|
||||||
code: "",
|
|
||||||
uuid: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const codeUrl = ref("");
|
|
||||||
|
|
||||||
// 分开控制发送验证码和注册的 loading 状态
|
|
||||||
const sendCodeLoading = ref(false);
|
|
||||||
const registerLoading = ref(false);
|
|
||||||
|
|
||||||
const registerRef = ref<any>(null);
|
|
||||||
|
|
||||||
// 表单校验规则
|
|
||||||
const registerRules = {
|
|
||||||
username: [
|
|
||||||
{ required: true, message: "请输入您的账号", trigger: "blur" },
|
|
||||||
{ min: 2, max: 20, message: "用户账号长度必须介于 2 和 20 之间", trigger: "blur" }
|
|
||||||
],
|
|
||||||
password: [
|
|
||||||
{ required: true, message: "请输入您的密码", trigger: "blur" },
|
|
||||||
{ min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" },
|
|
||||||
{ 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" }]
|
|
||||||
};
|
|
||||||
|
|
||||||
// 用户仓库
|
|
||||||
const userStore = useUserStore();
|
|
||||||
|
|
||||||
// 获取图形验证码
|
|
||||||
function getCode() {
|
|
||||||
if (!props.captchaEnabled) return;
|
|
||||||
getCodeImg().then((res: any) => {
|
|
||||||
codeUrl.value = "data:image/gif;base64," + res.img;
|
|
||||||
registerForm.value.uuid = res.uuid;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送验证码
|
|
||||||
function sendCode() {
|
|
||||||
if (props.method === 'email') {
|
|
||||||
sendEmailCode(registerForm.value, 'register');
|
|
||||||
} else if (props.method === 'phone') {
|
|
||||||
sendPhoneCode(registerForm.value, 'register');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册提交
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面加载时获取验证码
|
|
||||||
onMounted(() => {
|
|
||||||
getCode();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="register-container">
|
<view class="register-container">
|
||||||
<uni-forms
|
<!-- 背景图建议在 pages.json 中配置,这里作为备用 -->
|
||||||
ref="registerRef"
|
<view class="register-form-wrapper">
|
||||||
:model="registerForm"
|
<uni-forms ref="registerFormRef" :modelValue="registerForm" :rules="registerRules" label-width="0">
|
||||||
:rules="registerRules"
|
|
||||||
class="register-form"
|
|
||||||
>
|
|
||||||
<!-- 邮箱输入 -->
|
|
||||||
<uni-forms-item prop="email" v-if="method === 'email'">
|
|
||||||
<uni-easyinput
|
|
||||||
v-model="registerForm.email"
|
|
||||||
type="text"
|
|
||||||
placeholder="邮箱"
|
|
||||||
prefixIcon="email"
|
|
||||||
/>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<!-- 手机号输入 -->
|
<view class="title">天然气工具平台</view>
|
||||||
<uni-forms-item prop="phonenumber" v-else-if="method === 'phone'">
|
|
||||||
<uni-easyinput
|
|
||||||
v-model="registerForm.phonenumber"
|
|
||||||
type="text"
|
|
||||||
placeholder="手机号"
|
|
||||||
prefixIcon="phone"
|
|
||||||
/>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<!-- 用户名输入 -->
|
<uni-forms-item name="username">
|
||||||
<uni-forms-item prop="username" v-else>
|
<uni-easyinput v-model="registerForm.username" type="text" placeholder="账号" prefixIcon="person"
|
||||||
<uni-easyinput
|
@confirm="handleRegister" />
|
||||||
v-model="registerForm.username"
|
</uni-forms-item>
|
||||||
type="text"
|
|
||||||
placeholder="账号"
|
|
||||||
prefixIcon="user"
|
|
||||||
/>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<!-- 密码输入 -->
|
<uni-forms-item name="password">
|
||||||
<uni-forms-item prop="password">
|
<uni-easyinput v-model="registerForm.password" type="password" placeholder="密码" prefixIcon="locked"
|
||||||
<uni-easyinput
|
@confirm="handleRegister" />
|
||||||
v-model="registerForm.password"
|
</uni-forms-item>
|
||||||
type="password"
|
|
||||||
placeholder="密码"
|
|
||||||
prefixIcon="lock"
|
|
||||||
@keyup.enter="handleRegister"
|
|
||||||
/>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<!-- 确认密码输入 -->
|
<uni-forms-item name="confirmPassword">
|
||||||
<uni-forms-item prop="confirmPassword">
|
<uni-easyinput v-model="registerForm.confirmPassword" type="password" placeholder="确认密码"
|
||||||
<uni-easyinput
|
prefixIcon="locked" @confirm="handleRegister" />
|
||||||
v-model="registerForm.confirmPassword"
|
</uni-forms-item>
|
||||||
type="password"
|
|
||||||
placeholder="确认密码"
|
|
||||||
prefixIcon="lock"
|
|
||||||
@keyup.enter="handleRegister"
|
|
||||||
/>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<!-- 验证码输入 -->
|
<uni-forms-item name="code" v-if="captchaEnabled">
|
||||||
<uni-forms-item prop="code">
|
<view class="code-input-wrapper">
|
||||||
<view class="code-input">
|
<uni-easyinput v-model="registerForm.code" type="text" placeholder="验证码" prefixIcon="code"
|
||||||
<uni-easyinput
|
@confirm="handleRegister" />
|
||||||
v-model="registerForm.code"
|
<view class="code-img-wrapper">
|
||||||
type="text"
|
<image :src="codeUrl" class="code-img" @tap="getCode"></image>
|
||||||
placeholder="验证码"
|
</view>
|
||||||
prefixIcon="code"
|
</view>
|
||||||
@keyup.enter="handleRegister"
|
</uni-forms-item>
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 图形验证码或发送按钮 -->
|
|
||||||
<view class="code-action">
|
|
||||||
<uni-image
|
|
||||||
v-if="captchaEnabled && method === 'password'"
|
|
||||||
:src="codeUrl"
|
|
||||||
class="code-img"
|
|
||||||
@click="getCode"
|
|
||||||
mode="aspectFill"
|
|
||||||
/>
|
|
||||||
<uni-button
|
|
||||||
v-else
|
|
||||||
type="primary"
|
|
||||||
size="mini"
|
|
||||||
@click="sendCode"
|
|
||||||
:loading="loading"
|
|
||||||
>
|
|
||||||
发送验证码
|
|
||||||
</uni-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<!-- 注册按钮 -->
|
<uni-forms-item>
|
||||||
<uni-forms-item>
|
<uni-button type="primary" size="default" :loading="loading" @click="handleRegister"
|
||||||
<button
|
class="register-btn">
|
||||||
type="primary"
|
注册
|
||||||
size="default"
|
</uni-button>
|
||||||
class="register-btn"
|
<view class="login-link">
|
||||||
:loading="loading"
|
<text>已有账号?</text>
|
||||||
@click="handleRegister"
|
<navigator url="/pages/login/login" class="link-text">立即登录</navigator>
|
||||||
>
|
</view>
|
||||||
注册
|
</uni-forms-item>
|
||||||
</button>
|
|
||||||
</uni-forms-item>
|
|
||||||
|
|
||||||
<!-- 已有账号跳转 -->
|
</uni-forms>
|
||||||
<view class="login-link">
|
</view>
|
||||||
<text class="question-text">已有账号?</text>
|
|
||||||
<navigator url="/pages/login" class="link-type">立即登录</navigator>
|
<view class="el-register-footer">
|
||||||
</view>
|
<text>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</text>
|
||||||
</uni-forms>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<script setup>
|
||||||
.register-container {
|
import {
|
||||||
padding: 40rpx;
|
ref,
|
||||||
}
|
onReady
|
||||||
|
} from 'vue';
|
||||||
|
import {
|
||||||
|
getCodeImg,
|
||||||
|
register
|
||||||
|
} from '@/api/login'; // 假设 API 适配了 Uniapp
|
||||||
|
|
||||||
.register-form {
|
// 表单引用
|
||||||
background-color: #fff;
|
const registerFormRef = ref(null);
|
||||||
padding: 60rpx;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-input {
|
// 表单数据
|
||||||
display: flex;
|
const registerForm = ref({
|
||||||
align-items: center;
|
username: "",
|
||||||
gap: 20rpx;
|
password: "",
|
||||||
}
|
confirmPassword: "",
|
||||||
|
code: "",
|
||||||
|
uuid: ""
|
||||||
|
});
|
||||||
|
|
||||||
.code-action {
|
// 校验规则
|
||||||
flex-shrink: 0;
|
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: '请输入验证码'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
.code-img {
|
// 状态变量
|
||||||
width: 180rpx;
|
const codeUrl = ref("");
|
||||||
height: 70rpx;
|
const loading = ref(false);
|
||||||
border-radius: 8rpx;
|
const captchaEnabled = ref(true);
|
||||||
}
|
|
||||||
|
|
||||||
.register-btn {
|
// 获取验证码
|
||||||
width: 100%;
|
const getCode = () => {
|
||||||
height: 80rpx;
|
getCodeImg().then(res => {
|
||||||
line-height: 80rpx;
|
// 假设 res.data 结构为 { img: 'base64...', uuid: '...', captchaEnabled: true }
|
||||||
font-size: 30rpx;
|
captchaEnabled.value = res.data.captchaEnabled !== false;
|
||||||
border-radius: 40rpx;
|
if (captchaEnabled.value) {
|
||||||
margin-top: 20rpx;
|
codeUrl.value = res.data.img; // Uniapp 的 image 组件可以直接显示 base64
|
||||||
}
|
registerForm.value.uuid = res.data.uuid;
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("获取验证码失败:", err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取验证码失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
.login-link {
|
// 处理注册
|
||||||
display: flex;
|
const handleRegister = () => {
|
||||||
justify-content: center;
|
// 使用 uni-forms 的 validate 方法
|
||||||
align-items: center;
|
registerFormRef.value.validate().then(() => {
|
||||||
margin-top: 40rpx;
|
loading.value = true;
|
||||||
font-size: 26rpx;
|
register(registerForm.value).then(res => {
|
||||||
color: #666;
|
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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
.link-type {
|
// 页面加载时获取验证码
|
||||||
color: #007aff;
|
onReady(() => {
|
||||||
margin-left: 10rpx;
|
getCode();
|
||||||
text-decoration: underline;
|
});
|
||||||
}
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 页面整体背景,建议在 pages.json 中配置 "style": { "backgroundImage": "url('/static/login-background.jpg')" } */
|
||||||
|
.register-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
/* 备用背景色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单外层容器 */
|
||||||
|
.register-form-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400rpx;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 验证码输入框和图片一行显示 */
|
||||||
|
.code-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-img-wrapper {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 140rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
/* 与输入框高度对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 注册按钮 */
|
||||||
|
.register-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 登录链接 */
|
||||||
|
.login-link {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-text {
|
||||||
|
color: #007aff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页脚 */
|
||||||
|
.el-register-footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #ccc;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,314 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,473 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,524 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -4,6 +4,5 @@ const constant = {
|
|||||||
roles: 'vuex_roles',
|
roles: 'vuex_roles',
|
||||||
permissions: 'vuex_permissions'
|
permissions: 'vuex_permissions'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default constant
|
||||||
export default constant
|
|
||||||
|
|||||||
@ -1,133 +0,0 @@
|
|||||||
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