设置页面更新,修改用户名,用户信息获取

This commit is contained in:
aiShuiJiaoDeXioShou 2024-09-09 14:38:01 +08:00
parent 86752408b8
commit 985a261c6f
10 changed files with 309 additions and 41 deletions

View File

@ -61,5 +61,6 @@
".eslintrc.cjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,.stylelintrc.*,.eslintrc-auto-import.json,.editorconfig,.commitlint.cjs", ".eslintrc.cjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,.stylelintrc.*,.eslintrc-auto-import.json,.editorconfig,.commitlint.cjs",
"vite.config.ts": "tsconfig.*.json,uno.config.ts,tsconfig.json,uni-pages.d.ts", "vite.config.ts": "tsconfig.*.json,uno.config.ts,tsconfig.json,uni-pages.d.ts",
"manifest.config.ts": "manifest.config.ts,pages.config.ts" "manifest.config.ts": "manifest.config.ts,pages.config.ts"
} },
"vue3snippets.enable-compile-vue-file-on-did-save-code": true
} }

View File

@ -0,0 +1,36 @@
<template>
<view
class="pos-fixed z-999 left-[20px] top-[10px] flex gap-[10px] items-center content-center justify-center"
>
<image src="/static/addresscell/location.png" class="w-[30px]" mode="widthFix" />
<view>
{{
!addressDetail.addressComponent
? '正在加载...'
: `${addressDetail.addressComponent.city} ${addressDetail.addressComponent.district}`
}}
</view>
</view>
</template>
<script lang="js" setup>
import { getLocation, getMapDetailAddress } from '@/service/mapService'
const addressDetail = ref({})
//
const getLocationDetail = async () => {
try {
const loction = await getLocation()
const res = await getMapDetailAddress(loction.longitude, loction.latitude)
addressDetail.value = res
} catch (e) {
console.log(e)
}
}
onLoad(async () => {
await getLocationDetail()
})
</script>
<style lang="scss" scoped></style>

View File

@ -299,10 +299,7 @@
{ {
"path": "pages/settings/index", "path": "pages/settings/index",
"type": "page", "type": "page",
"style": { "style": {}
"navigationBarTitleText": "设置页面"
},
"needLogin": true
}, },
{ {
"path": "pages/space/index", "path": "pages/space/index",

View File

@ -10,6 +10,7 @@
<template> <template>
<view class="container"> <view class="container">
<Banner /> <Banner />
<AddressCell />
<loading-animation v-model="isLoading" /> <loading-animation v-model="isLoading" />
<view class="index"> <view class="index">
<view <view
@ -75,37 +76,35 @@ import QuickServiceCarousel from './components/quickServiceCarousel.vue'
import RecommendedServices from './components/recommendedServices.vue' import RecommendedServices from './components/recommendedServices.vue'
import Banner from './components/banner.vue' import Banner from './components/banner.vue'
import LoadingAnimation from '@/components/LoadingAnimation.vue' import LoadingAnimation from '@/components/LoadingAnimation.vue'
import { imgUrl } from '@/utils/commUtils' import { imgUrl, toast } from '@/utils/commUtils'
import { httpGet } from '@/utils/http' import { httpGet } from '@/utils/http'
import Tabbar from '@/components/Tabbar.vue' import Tabbar from '@/components/Tabbar.vue'
import AddressCell from '@/components/AddressCell.vue'
const isLoading = ref(false) const isLoading = ref(false)
const petInfo = ref({}) const petInfo = ref({})
// //
// setTimeout(() => {
// isLoading.value = false
// }, 2000)
const toExtended = () => {
// extended
uni.navigateTo({
url: '/pages/extended/index',
})
}
// //
const getPetInfo = async () => { const getPetInfo = async () => {
try { try {
const res = await httpGet('/petInfo/index') const res = await httpGet('/petInfo/index')
if (res.code === 200) { if (res.code === 200) {
petInfo.value = res.data petInfo.value = res.data
} else {
toast(res.message)
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
} }
const toExtended = () => {
// extended
uni.navigateTo({
url: '/pages/extended/index',
})
}
const toPath = (path) => { const toPath = (path) => {
uni.navigateTo({ uni.navigateTo({
url: path, url: path,
@ -113,7 +112,9 @@ const toPath = (path) => {
} }
onLoad(async () => { onLoad(async () => {
isLoading.value = true
await getPetInfo() await getPetInfo()
isLoading.value = false
}) })
</script> </script>

View File

@ -1,12 +1,3 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '设置页面',
},
needLogin: true,
}
</route>
<template> <template>
<view class="setting-root bg-[#F5F5F5] h-full p-5"> <view class="setting-root bg-[#F5F5F5] h-full p-5">
<!-- 国际化设置 --> <!-- 国际化设置 -->
@ -22,37 +13,64 @@
<!-- 账户设置 --> <!-- 账户设置 -->
<view class="card mb-4 p-4 bg-white rounded-lg shadow"> <view class="card mb-4 p-4 bg-white rounded-lg shadow">
<view class="text-gray-800 text-lg mb-2">账户设置</view> <view class="wd-cell" @click="showUserNamePopup = true">
<text class="text-gray-800">账户名称</text>
<text class="text-gray-400 ml-auto">{{ sinfo.userName }}</text>
<wd-icon name="right" size="16" class="ml-2"></wd-icon>
</view>
<view class="wd-cell" @click="goToBindPhone"> <view class="wd-cell" @click="goToBindPhone">
<text class="text-gray-800">手机号绑定</text> <text class="text-gray-800">手机号绑定</text>
<text class="text-gray-400 ml-auto">191****0915</text> <text class="text-gray-400 ml-auto">{{ sinfo.phone }}</text>
<wd-icon name="right" size="16" class="ml-2"></wd-icon> <wd-icon name="right" size="16" class="ml-2"></wd-icon>
</view> </view>
<view class="wd-cell" @click="goToRealNameAuth"> <view class="wd-cell" @click="!sinfo.isAuth ? goToRealNameAuth() : toast('你已经实名了')">
<text class="text-gray-800">实名认证</text> <text class="text-gray-800">实名认证</text>
<text class="text-gray-400 ml-auto">已实名</text> <text v-if="sinfo.isAuth" class="text-gray-400 ml-auto">已实名</text>
<text v-else class="text-gray-400 ml-auto">未实名</text>
<wd-icon name="right" size="16" class="ml-2"></wd-icon> <wd-icon name="right" size="16" class="ml-2"></wd-icon>
</view> </view>
</view> </view>
<!-- 修改用户名的弹窗 -->
<wd-popup v-model="showUserNamePopup" position="center" custom-style="padding: 20px;">
<view class="flex flex-col gap-3">
<view class="text-gray-800 mb-4">修改用户名</view>
<input v-model="newUserName" type="text" placeholder="请输入新的用户名" />
<view class="btn-group mt-4">
<button class="confirm-btn" @click="updateUserName">确认</button>
<button class="cancel-btn" @click="showUserNamePopup = false">取消</button>
</view>
</view>
</wd-popup>
<!-- 退出登录按钮 --> <!-- 退出登录按钮 -->
<view class="card mb-4 p-4 bg-white rounded-lg shadow"> <view class="card mb-4 p-4 bg-white rounded-lg shadow">
<button class="logout-button" @click="logout">退出登录</button> <button class="logout-button" @click="logout">退出登录</button>
</view> </view>
</view> </view>
<LoadingAnimation v-model="loading" />
</template> </template>
<script setup lang="ts"> <script setup lang="js">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useConfigStore } from '../../store/config' import { useConfigStore } from '@/store/config'
import { useUserStore } from '@/store' import { useUserStore } from '@/store'
import { httpGet } from '@/utils/http' import { httpPost, httpGet } from '@/utils/http'
import LoadingAnimation from '@/components/LoadingAnimation.vue'
import { toast } from '@/utils/commUtils'
const configStore = useConfigStore() const configStore = useConfigStore()
const userStore = useUserStore() const userStore = useUserStore()
const languages = configStore.languages const languages = configStore.languages
const selectedLanguage = ref<string>(languages['zh-Hans']) const selectedLanguage = ref(languages['zh-Hans'])
const sinfo = ref({})
const showUserNamePopup = ref(false)
const newUserName = ref('')
const loading = ref(false)
const languageOptions = computed(() => const languageOptions = computed(() =>
Object.entries(languages).map(([key, value]) => ({ Object.entries(languages).map(([key, value]) => ({
@ -77,21 +95,54 @@ const goToBindPhone = () => {
}) })
} }
//
const getSettingInfo = async () => {
const settingInfo = await httpGet('/setting/info')
try {
if (settingInfo.code === 200) {
sinfo.value = settingInfo.data
} else {
toast(settingInfo.message)
}
} catch (e) {
console.log(e)
}
}
//
const updateUserName = async () => {
if (!newUserName.value) {
toast('用户名不能为空')
return
}
const response = await httpPost('/setting/updateUserName', {}, { userName: newUserName.value })
if (response.code === 200) {
sinfo.value.userName = newUserName.value
toast('用户名修改成功')
showUserNamePopup.value = false
} else {
toast(response.message)
}
}
const logout = async () => { const logout = async () => {
//
const logRes = await httpGet('/user/logout') const logRes = await httpGet('/user/logout')
if (logRes.code == 200) { if (logRes.code == 200) {
uni.showToast({ title: '退出成功', icon: 'none' }) uni.showToast({ title: '退出成功', icon: 'none' })
} else { } else {
uni.showToast({ title: logRes.msg, icon: 'none' }) uni.showToast({ title: logRes.msg, icon: 'none' })
} }
//
userStore.clearUserInfo() userStore.clearUserInfo()
//
uni.reLaunch({ uni.reLaunch({
url: '/pages/login/index', url: '/pages/login/index',
}) })
} }
onLoad(async () => {
loading.value = true
await getSettingInfo()
loading.value = false
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -123,4 +174,23 @@ const logout = async () => {
.logout-button { .logout-button {
@apply w-full py-3 bg-red-500 text-white text-lg rounded; @apply w-full py-3 bg-red-500 text-white text-lg rounded;
} }
.btn-group {
display: flex;
justify-content: space-between;
}
.confirm-btn {
background-color: #4caf50;
color: white;
border: none;
border-radius: 4px;
}
.cancel-btn {
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
}
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,62 @@
package com.yskj.acdr.common.verify;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Getter
@Component
public class UserNameValidator {
private String errorString = "用户名不合法";
// 非法词语列表
private final String[] ILLEGAL_WORDS = {"admin", "root", "illegal"};
@Override
public String toString() {
return errorString;
}
public boolean valid(String userName) {
// 用户名不能为空
if (StrUtil.isBlank(userName)) {
errorString = "用户名不能为空";
System.out.println(errorString);
return false;
}
// 用户名长度不能大于6
if (userName.length() > 6) {
errorString = "用户名长度大于6";
System.out.println(errorString);
return false;
}
// 用户名不能以特殊字符开头仅允许字母或数字开头
if (!Character.isLetterOrDigit(userName.charAt(0))) {
errorString = "用户名不能以特殊字符开头";
System.out.println(errorString);
return false;
}
// 用户名不能全是数字
if (userName.matches("\\d+")) {
errorString = "用户名不能全是数字";
System.out.println(errorString);
return false;
}
// 用户名不能包含违法词语
for (String word : ILLEGAL_WORDS) {
if (userName.toLowerCase().contains(word)) {
errorString = "用户名包含违法词语: " + word;
System.out.println(errorString);
return false;
}
}
// 所有条件都符合验证通过
return true;
}
}

View File

@ -0,0 +1,79 @@
package com.yskj.acdr.master.setting.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.yskj.acdr.common.cache.GlobalRedisCache;
import com.yskj.acdr.common.response.GlobalResponse;
import com.yskj.acdr.common.verify.UserNameValidator;
import com.yskj.acdr.master.setting.entity.AccountInfo;
import com.yskj.acdr.master.user.entity.Users;
import com.yskj.acdr.master.user.service.AuthenticationService;
import com.yskj.acdr.master.user.service.UsersService;
import jakarta.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
@RequestMapping("/setting")
@RestController
public class SettingController {
@Resource
private UsersService usersService;
@Resource
private AuthenticationService authService;
@Resource
private GlobalRedisCache<String> redisCache;
@Resource
private UserNameValidator userValid;
/**
* 获取账户信息
* @return 账户信息类
*/
@GetMapping("/info")
public GlobalResponse<AccountInfo> info() {
Users users = usersService.getById(StpUtil.getLoginIdAsLong());
return GlobalResponse.success(
new AccountInfo().setIsAuth(authService.isRealName())
.setPhone(users.getPhone())
.setUserName(users.getNickname())
);
}
/**
* 修改手机号
*/
@PostMapping("/updatePhone")
@Transactional
public GlobalResponse<Boolean> updatePhone(String code,String newPhone) {
// 验证验证码
String cachedCode = redisCache.get(newPhone);
if (cachedCode == null || !cachedCode.equals(code)) {
return GlobalResponse.failure("验证码错误或已过期");
}
// 更新用户手机号
Users users = usersService.getById(StpUtil.getLoginIdAsLong());
users.setPhone(newPhone);
return GlobalResponse.success(usersService.updateById(users));
}
/**
* 修改用户名称
*/
@PostMapping("/updateUserName")
@Transactional
public GlobalResponse<Boolean> updateUserName(String userName) {
// 这里添加验证信息
if (!userValid.valid(userName)) {
return GlobalResponse.failure("用户名不合理,%s请重新输入!".formatted(userValid.getErrorString()));
}
// 更新用户手机号
Users users = usersService.getById(StpUtil.getLoginIdAsLong());
users.setNickname(userName);
return GlobalResponse.success(usersService.updateById(users));
}
}

View File

@ -0,0 +1,21 @@
package com.yskj.acdr.master.setting.entity;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@ApiModel(value = "AccountInfo", description = "账户信息类")
public class AccountInfo {
// 手机号信息
private String phone;
// 用户名信息
private String userName;
// 是否实名认证
private Boolean isAuth;
}

View File

@ -17,6 +17,7 @@ import com.yskj.acdr.master.user.service.UsersService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yskj.acdr.utils.MutualHttpUtil; import com.yskj.acdr.utils.MutualHttpUtil;
import com.yskj.acdr.utils.ProfileUtil; import com.yskj.acdr.utils.ProfileUtil;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -56,7 +57,7 @@ public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements
@Value("${profiles:active}") @Value("${profiles:active}")
private String debug; private String debug;
@Autowired @Resource
private GlobalRedisCache<String> redisCache; private GlobalRedisCache<String> redisCache;
@Override @Override