This commit is contained in:
2025-05-08 19:54:38 +08:00
parent 055c9a49b7
commit 2e9c403632
16 changed files with 350 additions and 124 deletions

View File

@@ -8,7 +8,48 @@ const system = {
url: "/m1/5995958-5684445-default/getList",
data: data,
});
}
},
getBarrageList: async () => {
return request({
method: MethodsENUM.POST,
url: "/index/getBarrageList",
});
},
sendSms: async (data) => {
return request({
method: MethodsENUM.POST,
url: "/login/sendSms",
data: data,
});
},
register: async (data) => {
return request({
method: MethodsENUM.POST,
url: "/login/register",
data: data,
});
},
smsLogin: async (data) => {
return request({
method: MethodsENUM.POST,
url: "/login/smsLogin",
data: data,
});
},
accountLogin: async (data) => {
return request({
method: MethodsENUM.POST,
url: "/login/login",
data: data,
});
},
editPassword: async (data) => {
return request({
method: MethodsENUM.POST,
url: "/login/editPassword",
data: data,
});
},
}
export default system;

View File

@@ -1,14 +1,49 @@
<script setup>
import {ref} from "vue";
import XLink from "./XLink.vue";
import XInput from "./XInput.vue";
import Api from "../api/index.js";
import {showToast} from "../utils/uils.js";
const modalValue = defineModel();
const timer = ref(null);
const props = defineProps({
mobile: {
type: String,
default: null
},
type: {
type: Number,
default: 1
},
});
const sendMsg = async () => {
const {msg} = await Api.system.sendSms({
mobile: props.mobile,
type: props.type,
});
showToast(msg);
timer.value = 60;
const send = () => {
if (timer.value === 0) {
timer.value = null;
return;
}
timer.value--;
setTimeout(send, 1000);
}
setTimeout(send, 1000);
}
</script>
<template>
<x-input v-bind="$attrs" v-model:model-value="modalValue" placeholder="验证码">
<template #suffix>
<x-link>发送验证码</x-link>
<x-link @click="sendMsg">{{
timer ? `${timer}秒后重试` : `发送验证码`
}}
</x-link>
</template>
</x-input>
</template>

View File

@@ -27,7 +27,7 @@ const modelValue = defineModel();
height: v-bind(height);
:deep(input) {
background-color: rgba(0,0,0,0);
background-color: rgba(0, 0, 0, 0);
flex-grow: 1;
}
}

View File

@@ -3,12 +3,21 @@ import {reactive} from "vue";
import XNav from "../../components/XNav.vue";
import XLink from "../../components/XLink.vue";
import XInput from "../../components/XInput.vue";
import Api from "../../api/index.js";
import {backPage, showToast} from "../../utils/uils.js";
import SendMsg from "../../components/SendMsg.vue";
const form = reactive({
phone: null,
verificationCode: null,
mobile: null,
captcha: null,
password: null,
});
const success = async () => {
const {msg} = await Api.system.editPassword(form);
showToast(msg);
backPage();
}
</script>
<template>
@@ -22,19 +31,21 @@ const form = reactive({
</view>
<view class="!flex flex-col gap-[56rpx] !mt-[60rpx]">
<x-input v-model:model-value="form.phone" placeholder="请输入手机号"></x-input>
<x-input v-model:model-value="form.verificationCode" placeholder="验证码">
<template #suffix>
<x-link>发送验证码</x-link>
</template>
</x-input>
<x-input v-model:model-value="form.phone" placeholder="请输入密码"></x-input>
<x-input v-model:model-value="form.mobile" placeholder="请输入手机号"></x-input>
<send-msg v-model:model-value="form.captcha" :mobile="form.mobile" :type="3"></send-msg>
<x-input v-model:model-value="form.password" placeholder="请输入密码"></x-input>
<tui-button>确定修改</tui-button>
<tui-button @click="success">确定修改</tui-button>
</view>
</view>
</template>
<style lang="scss">
page {
background-color: #fff;
}
</style>
<style lang="scss" scoped>
.title {
color: rgb(29, 33, 41);

View File

@@ -2,23 +2,36 @@
import {reactive} from "vue";
import XInput from "../../components/XInput.vue";
import XLink from "../../components/XLink.vue";
import {toPage} from "../../utils/uils.js";
import {showToast, toPage} from "../../utils/uils.js";
import Api from "../../api/index.js";
import {useUserStore} from "../../pinia/UserStore/index.js";
const UserStore = useUserStore();
const form = reactive({
phone: null,
verificationCode: null,
mobile: null,
password: null,
});
const success = async () => {
const {msg, data} = await Api.system.accountLogin(form);
showToast(msg);
UserStore.isLogin = true;
UserStore.token = data.token;
Object.assign(UserStore.userInfo, data);
await toPage('/pages/home/index');
}
</script>
<template>
<view class="!px-[34rpx] !flex flex-col gap-[40rpx]">
<x-input v-model:model-value="form.phone" placeholder="请输入手机号"></x-input>
<x-input v-model:model-value="form.verificationCode" placeholder="密码">
<x-input v-model:model-value="form.mobile" placeholder="请输入手机号"></x-input>
<x-input v-model:model-value="form.password" placeholder="密码">
<template #suffix>
<x-link @click="toPage('/pages/forgotPassword/index')">忘记密码?</x-link>
</template>
</x-input>
<tui-button class="!mt-[80rpx]">登录</tui-button>
<tui-button class="!mt-[80rpx]" @click="success">登录</tui-button>
<tui-button @click="toPage('/pages/register/index')" class="!mt-[40rpx]" plain link>
<image class="!h-[26rpx]" mode="heightFix" src="../../static/icons/去注册.png"></image>
</tui-button>

View File

@@ -1,42 +1,34 @@
<script setup>
import dm1 from '../../static/icons/弹幕1.png';
import dm2 from '../../static/icons/弹幕2.png';
import dm3 from '../../static/icons/弹幕3.png';
import dm4 from '../../static/icons/弹幕4.png';
import dm5 from '../../static/icons/弹幕5.png';
import dm6 from '../../static/icons/弹幕6.png';
import dm7 from '../../static/icons/弹幕7.png';
import dm8 from '../../static/icons/弹幕8.png';
import dm9 from '../../static/icons/弹幕9.png';
import {reactive} from "vue";
import {reactive, onMounted} from "vue";
import Api from "../../api/index.js";
const MAP = [dm1,dm2,dm3,dm4,dm5,dm6,dm7,dm8,dm9];
const list1 = reactive([]);
const list2 = reactive([]);
for (let i = 0; i <= 100; i++) {
list1.push(MAP[Math.floor(Math.random() * 9)]);
list2.push(MAP[Math.floor(Math.random() * 9)]);
}
onMounted(() => {
Api.system.getBarrageList().then(({data}) => {
for (let i = 0; i <= 1000; i++) {
list1.push(data[Math.floor(Math.random() * data.length)]);
list2.push(data[Math.floor(Math.random() * data.length)]);
}
});
})
</script>
<template>
<view class="!mt-[68rpx] !flex flex-col gap-[20rpx]">
<view class="!flex gap-[30rpx] scrollX">
<image
<view
v-for="item in list1"
class="!h-[72rpx]"
mode="heightFix"
:src="item">
</image>
class="!h-[72rpx] qp">
{{ item }}
</view>
</view>
<view class="!flex gap-[30rpx] scrollX">
<image
<view
v-for="item in list2"
class="!h-[72rpx]"
mode="heightFix"
:src="item">
</image>
class="!h-[72rpx] qp">
{{ item }}
</view>
</view>
</view>
</template>
@@ -44,7 +36,7 @@ for (let i = 0; i <= 100; i++) {
<style lang="scss" scoped>
.scrollX {
width: max-content !important;
animation: scroll 150s linear infinite;
animation: scroll 800s linear infinite;
image {
flex-shrink: 0;
@@ -59,4 +51,11 @@ for (let i = 0; i <= 100; i++) {
transform: translateX(-100%); /* 滚动到左侧结束 */
}
}
.qp {
@apply px-[36rpx] flex items-center rounded-full;
background: linear-gradient(to right, #B8C5E9, #CAE2E9, #D0D6E2);
box-shadow: inset 0 0 10px 15rpx rgba(255, 255, 255);
backdrop-filter: blur(20px);
}
</style>

View File

@@ -1,27 +1,32 @@
<script setup>
import {reactive} from "vue";
import XInput from "../../components/XInput.vue";
import {toPage} from "../../utils/uils.js";
import {showToast, toPage} from "../../utils/uils.js";
import SendMsg from "../../components/SendMsg.vue";
import {useUserStore} from "../../pinia/UserStore/index.js";
import Api from "../../api/index.js";
const {login} = useUserStore();
const UserStore = useUserStore();
const form = reactive({
phone: null,
verificationCode: null,
mobile: null,
captcha: null,
});
const success = async () => {
login();
toPage('/pages/home/index');
const {msg, data} = await Api.system.accountLogin(form);
showToast(msg);
UserStore.isLogin = true;
UserStore.token = data.token;
Object.assign(UserStore.userInfo, data);
await toPage('/pages/home/index');
}
</script>
<template>
<view class="!px-[34rpx] !flex flex-col gap-[40rpx]">
<x-input v-model:model-value="form.phone" placeholder="请输入手机号"></x-input>
<send-msg v-model:model-value="form.verificationCode"></send-msg>
<x-input v-model:model-value="form.mobile" placeholder="请输入手机号"></x-input>
<send-msg v-model:model-value="form.captcha" :mobile="form.mobile" :type="2"></send-msg>
<tui-button class="!mt-[80rpx]" @click="success">登录</tui-button>
<tui-button @click="toPage('/pages/register/index')" class="!mt-[40rpx]" plain link>
<image class="!h-[26rpx]" mode="heightFix" src="../../static/icons/去注册.png"></image>

View File

@@ -1,4 +1,5 @@
<script setup>
import BGICON from "../../static/icons/bg.png";
import XNav from "../../components/XNav.vue";
import BulletChat from "./BulletChat.vue";
import {ref} from "vue";
@@ -20,13 +21,13 @@ const tabs = [
<template>
<!--登陆-->
<XNav></XNav>
<XNav :showBack="false"></XNav>
<view class="h-[390rpx] relative overflow-hidden">
<image class="!absolute left-1/2 top-1/2 -translate-1/2 !w-[1198rpx] !h-[806rpx] -z-10 !pb-[40rpx]"
src="/static/icons/bg.png"></image>
<image class="!absolute left-1/2 top-1/2 -translate-1/2 !w-[1198rpx] !h-[806rpx] !pb-[40rpx]"
:src="BGICON"></image>
<view class="!flex gap-[16rpx] items-center !mt-[56rpx] !ml-[16rpx]">
<view class="!flex gap-[16rpx] items-center !mt-[56rpx] !ml-[16rpx] relative z-10">
<image class="!w-[68rpx] !h-[68rpx]" src="/static/icons/hi.png"></image>
<view class="title">欢迎登录系统</view>
</view>
@@ -34,8 +35,9 @@ const tabs = [
<BulletChat></BulletChat>
</view>
<view class="h-full bg-white !-mt-[20rpx] rounded-t-[20rpx]">
<tui-tabs class="!mx-auto !mb-[40rpx]" :tabs="tabs" :currentTab="currentTab" itemWidth="50%" @change="({index}) => currentTab=index" :width="300" :sliderWidth="130"></tui-tabs>
<view class="h-full bg-white !-mt-[20rpx] rounded-t-[20rpx] relative z-10">
<tui-tabs class="!mx-auto !mb-[40rpx]" :tabs="tabs" :currentTab="currentTab" itemWidth="50%"
@change="({index}) => currentTab=index" :width="300" :sliderWidth="130"></tui-tabs>
<PhoneLogin v-if="currentTab === 0"></PhoneLogin>
<AccountLogin v-else></AccountLogin>

View File

@@ -4,17 +4,20 @@ import XNav from "../../components/XNav.vue";
import BulletChat from "../login/BulletChat.vue";
import XInput from "../../components/XInput.vue";
import SendMsg from "../../components/SendMsg.vue";
import Api from "../../api/index.js";
import {showToast} from "../../utils/uils.js";
const form = reactive({
phone: null,
wxCode: null,
verificationCode: null,
mobile: null,
wechat: null,
captcha: null,
password: null,
code: null,
invite: null,
});
const success = () => {
const success = async () => {
const {msg} = await Api.system.register(form);
showToast(msg);
}
</script>
@@ -23,10 +26,10 @@ const success = () => {
<XNav></XNav>
<view class="h-[390rpx] relative overflow-hidden">
<image class="!absolute left-1/2 top-1/2 -translate-1/2 !w-[1198rpx] !h-[806rpx] -z-10 !pb-[40rpx]"
<image class="!absolute left-1/2 top-1/2 -translate-1/2 !w-[1198rpx] !h-[806rpx] !pb-[40rpx]"
src="/static/icons/bg.png"></image>
<view class="!flex gap-[16rpx] items-center !mt-[56rpx] !ml-[16rpx]">
<view class="!flex gap-[16rpx] items-center !mt-[56rpx] !ml-[16rpx] relative z-10">
<image class="!w-[68rpx] !h-[68rpx]" src="/static/icons/hi.png"></image>
<view class="title">欢迎注册</view>
</view>
@@ -34,13 +37,13 @@ const success = () => {
<BulletChat></BulletChat>
</view>
<view class="h-full bg-white !-mt-[20rpx] rounded-t-[20rpx] !pt-[44rpx]">
<view class="h-full bg-white !-mt-[20rpx] rounded-t-[20rpx] !pt-[44rpx] relative z-10">
<view class="!px-[34rpx] !flex flex-col gap-[40rpx]">
<x-input v-model:model-value="form.wxCode" placeholder="请输入微信号"></x-input>
<x-input v-model:model-value="form.phone" placeholder="请输入手机号"></x-input>
<send-msg v-model:model-value="form.verificationCode"></send-msg>
<x-input v-model:model-value="form.wechat" placeholder="请输入微信号"></x-input>
<x-input v-model:model-value="form.mobile" placeholder="请输入手机号"></x-input>
<send-msg v-model:model-value="form.captcha" :mobile="form.mobile"></send-msg>
<x-input v-model:model-value="form.password" placeholder="请输入登录密码"></x-input>
<x-input v-model:model-value="form.code" placeholder="请输入邀请码"></x-input>
<x-input v-model:model-value="form.invite" placeholder="请输入邀请码"></x-input>
<tui-button class="!mt-[52rpx]" @click="success">确认注册</tui-button>
</view>

View File

@@ -1,22 +1,20 @@
import {defineStore} from "pinia";
import {ref} from "vue";
import {ref, reactive} from "vue";
export const useUserStore = defineStore('UserStore', () => {
const isLogin = ref(false);
const userInfo = ref(null);
const login = () => {
isLogin.value = true;
}
const token = ref(null);
const userInfo = reactive({});
return {
isLogin,
userInfo,
login,
token,
}
}, {
persist: {
key: 'UserStore',
pick: ['isLogin', 'userInfo', 'token'],
storage: {
getItem(key) {
return uni.getStorageSync(key);
@@ -25,6 +23,5 @@ export const useUserStore = defineStore('UserStore', () => {
uni.setStorageSync(key, value);
}
},
pick: ['isLogin', 'userInfo']
}
});

View File

@@ -1,29 +1,58 @@
import AES from 'crypto-js/aes.js';
import utf8 from 'crypto-js/enc-utf8.js';
import Crypto from 'crypto-js';
class AESCrypto {
/**
* 密钥
* @type {string}
*/
static #AES_KEY = import.meta.env.VITE_AES_KEY;
static _AES_KEY = import.meta.env.VITE_AES_KEY;
/**
* AES加密
* @param context {string} 加密内容
*/
static encrypt = (context) => {
return AES.encrypt(context, this.#AES_KEY).toString();
const IV = this.createIV();
return {
context: Crypto.AES.encrypt(
context,
Crypto.enc.Utf8.parse(this._AES_KEY),
{
iv: Crypto.enc.Utf8.parse(IV),
mode: Crypto.mode.CBC,
padding: Crypto.pad.Pkcs7
}
).toString(),
iv: IV,
};
}
/**
* AES解密
* @param context {string}
* @param iv {string}
* @return {string}
*/
static decrypt = (context) => {
const bytes = AES.decrypt(context, this.#AES_KEY);
return bytes.toString(utf8);
static decrypt = (context, iv) => {
return Crypto.AES.decrypt(
context,
Crypto.enc.Utf8.parse(this._AES_KEY),
{
iv: Crypto.enc.Utf8.parse(iv),
mode: Crypto.mode.CBC,
padding: Crypto.pad.Pkcs7
}
).toString(Crypto.enc.Utf8);
};
static createIV = (length = 16) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters[randomIndex];
}
return result;
}
}

View File

@@ -1,26 +1,69 @@
import axios from "axios";
import {UniAdapter} from "uniapp-axios-adapter";
import {showToast} from "./uils.js";
import AESCrypto from "./AESCrypto.js";
import {useUserStore} from "../pinia/UserStore/index.js";
const request = (options) => {
return new Promise((resolve, reject) => {
const {url, method, data, params} = options;
export const BASEURL = import.meta.env.MODE === 'development' ? '/baseApi/api' : import.meta.env.VITE_API_URL;
uni.request({
method: method,
data: data,
params: params,
url: `${import.meta.env.VITE_API_URL}${url}`,
success: ({data}) => {
if (data.code !== 0) {
showToast(data.msg);
reject(data.msg);
}
resolve(data);
},
fail: (err) => {
reject(err);
}
});
});
}
const request = axios.create({
baseURL: BASEURL,
timeout: '6000',
adapter: UniAdapter,
});
request.interceptors.request.use(
(config) => {
const {token} = useUserStore();
// 如果 token 存在,则将其添加到请求头中
if (token) {
config.headers['Access-Token'] = token;
}
console.log('请求拦截器', config.data);
if (!config.UN_AES) {
const {context, iv} = AESCrypto.encrypt(JSON.stringify(config.data));
config.data = {
requestData: context, iv: iv,
};
}
return config;
},
(error) => {
// 处理请求错误
return Promise.reject(error);
}
);
// 响应拦截器
request.interceptors.response.use((response) => {
const {data: {msg, code, data}, config: {url}} = response;
console.log('接口返回', response)
if (code === 401) {
const {logout} = useUserStore();
logout();
}
if (code !== 1) {
showToast(msg);
return Promise.reject(msg);
}
if (!data.data) {
return {msg, code, data}
} else {
const resp = JSON.parse(AESCrypto.decrypt(data.data, data.iv));
console.log(`接口${url}返回`, resp);
return {data: resp};
}
}, (error) => {
if (error.response) {
return Promise.reject(error.response.data); // 返回错误信息
} else { // 网络错误
return Promise.reject(error.message);
}
});
export default request;