feat: load preview page do not require user to login

This commit is contained in:
RockYang
2024-03-19 18:25:01 +08:00
parent 8f47474edd
commit 58e44b7d12
57 changed files with 758 additions and 1550 deletions

View File

@@ -6,55 +6,20 @@
:show-close="false"
:before-close="close"
>
<template #header="{ close, titleId, titleClass }">
<template #header="{titleId, titleClass }">
<div class="header">
<div class="title">用户登录</div>
<div class="title" v-if="login">用户登录</div>
<div class="title" v-else>用户注册</div>
<div class="close-icon">
<el-icon>
<el-icon @click="close">
<Close/>
</el-icon>
</div>
</div>
</template>
<div class="login-box">
<el-form label-width="75px">
<el-form-item>
<template #label>
<div class="label">
<el-icon>
<User/>
</el-icon>
<span>账号</span>
</div>
</template>
<template #default>
<el-input v-model="data.username" size="large" placeholder="账号"/>
</template>
</el-form-item>
<el-form-item>
<template #label>
<div class="label">
<el-icon>
<Lock/>
</el-icon>
<span>密码</span>
</div>
</template>
<template #default>
<el-input v-model="data.password" type="password" size="large" placeholder="密码"/>
</template>
</el-form-item>
<div class="login-btn">
<el-button type="primary" @click="submit" size="large" round>登录</el-button>
<el-button plain @click="submit" size="large" round>注册</el-button>
</div>
</el-form>
</div>
<div class="register-box">
<el-form :model="data" label-width="120px" ref="formRef">
<div class="login-box" v-if="login">
<el-form :model="data" label-width="120px" class="form">
<div class="block">
<el-input placeholder="账号"
size="large"
@@ -81,6 +46,119 @@
</el-input>
</div>
<el-row class="btn-row" :gutter="20">
<el-col :span="12">
<el-button class="login-btn" type="primary" size="large" @click="submitLogin">登录</el-button>
</el-col>
<el-col :span="12">
<div class="text">
还没有账号
<el-tag @click="login = false">注册</el-tag>
</div>
</el-col>
</el-row>
</el-form>
</div>
<div class="register-box" v-else>
<el-form :model="data" class="form" v-if="enableRegister">
<el-tabs v-model="activeName" class="demo-tabs">
<el-tab-pane label="手机注册" name="mobile" v-if="enableMobile">
<div class="block">
<el-input placeholder="手机号码"
size="large"
v-model="data.username"
autocomplete="off">
<template #prefix>
<el-icon>
<Iphone/>
</el-icon>
</template>
</el-input>
</div>
<div class="block">
<el-row :gutter="10">
<el-col :span="12">
<el-input placeholder="验证码"
size="large" maxlength="30"
v-model="data.code"
autocomplete="off">
<template #prefix>
<el-icon>
<Checked/>
</el-icon>
</template>
</el-input>
</el-col>
<el-col :span="12">
<send-msg size="large" :receiver="data.username"/>
</el-col>
</el-row>
</div>
</el-tab-pane>
<el-tab-pane label="邮箱注册" name="email" v-if="enableEmail">
<div class="block">
<el-input placeholder="邮箱地址"
size="large"
v-model="data.username"
autocomplete="off">
<template #prefix>
<el-icon>
<Iphone/>
</el-icon>
</template>
</el-input>
</div>
<div class="block">
<el-row :gutter="10">
<el-col :span="12">
<el-input placeholder="验证码"
size="large" maxlength="30"
v-model="data.code"
autocomplete="off">
<template #prefix>
<el-icon>
<Checked/>
</el-icon>
</template>
</el-input>
</el-col>
<el-col :span="12">
<send-msg size="large" :receiver="data.username"/>
</el-col>
</el-row>
</div>
</el-tab-pane>
<el-tab-pane label="用户名注册" name="username" v-if="enableUser">
<div class="block">
<el-input placeholder="用户名"
size="large"
v-model="data.username"
autocomplete="off">
<template #prefix>
<el-icon>
<Iphone/>
</el-icon>
</template>
</el-input>
</div>
</el-tab-pane>
</el-tabs>
<div class="block">
<el-input placeholder="请输入密码(8-16位)"
maxlength="16" size="large"
v-model="data.password" show-password
autocomplete="off">
<template #prefix>
<el-icon>
<Lock/>
</el-icon>
</template>
</el-input>
</div>
<div class="block">
<el-input placeholder="重复密码(8-16位)"
size="large" maxlength="16" v-model="data.repass" show-password
@@ -93,26 +171,6 @@
</el-input>
</div>
<div class="block">
<el-row :gutter="10">
<el-col :span="12">
<el-input placeholder="验证码"
size="large" maxlength="30"
v-model="data.code"
autocomplete="off">
<template #prefix>
<el-icon>
<Checked/>
</el-icon>
</template>
</el-input>
</el-col>
<el-col :span="12">
<send-msg size="large" :receiver="data.username"/>
</el-col>
</el-row>
</div>
<div class="block">
<el-input placeholder="邀请码(可选)"
size="large"
@@ -126,27 +184,51 @@
</el-input>
</div>
<el-row class="btn-row">
<el-button class="login-btn" type="primary" @click="">注册</el-button>
</el-row>
<el-row class="btn-row" :gutter="20">
<el-col :span="12">
<el-button class="login-btn" type="primary" size="large" @click="submitRegister">注册</el-button>
</el-col>
<el-col :span="12">
<div class="text">
已有账号
<el-tag @click="login = true">登录</el-tag>
</div>
</el-col>
<el-row class="text-line">
已经有账号
<el-link type="primary" @click="">登录</el-link>
</el-row>
</el-form>
<div class="tip-result" v-else>
<el-row :gutter="20">
<el-col :span="12">
<el-result icon="error" title="注册功能已关闭">
<template #sub-title>
<p>抱歉系统已关闭注册功能请联系管理员添加账号</p>
</template>
</el-result>
</el-col>
<el-col :span="12">
<div class="wechat-card">
<el-image :src="wxImg"/>
</div>
</el-col>
</el-row>
</div>
</div>
</el-dialog>
</template>
<script setup>
import {computed, ref} from "vue"
import {httpPost} from "@/utils/http";
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage} from "element-plus";
import {setUserToken} from "@/store/session";
import {validateMobile} from "@/utils/validate";
import {validateEmail, validateMobile} from "@/utils/validate";
import {Checked, Close, Iphone, Lock, Message, Position, User} from "@element-plus/icons-vue";
import SendMsg from "@/components/SendMsg.vue";
import {arrayContains} from "@/utils/libs";
import {useRouter} from "vue-router";
// eslint-disable-next-line no-undef
const props = defineProps({
@@ -155,21 +237,106 @@ const props = defineProps({
const showDialog = computed(() => {
return props.show
})
const login = ref(true)
const data = ref({
username: "",
password: ""
password: "",
repass: "",
code: "",
invite_code: ""
})
const enableMobile = ref(false)
const enableEmail = ref(false)
const enableUser = ref(false)
const enableRegister = ref(false)
const activeName = ref("mobile")
const wxImg = ref("/images/wx.png")
// eslint-disable-next-line no-undef
const emits = defineEmits(['hide']);
const submit = function () {
const emits = defineEmits(['hide', 'success']);
httpGet("/api/config/get?key=system").then(res => {
if (res.data) {
const registerWays = res.data['register_ways']
if (arrayContains(registerWays, "mobile")) {
enableMobile.value = true
}
if (arrayContains(registerWays, "email")) {
enableEmail.value = true
}
if (arrayContains(registerWays, "username")) {
enableUser.value = true
}
// 是否启用注册
enableRegister.value = res.data['enabled_register']
// 使用后台上传的客服微信二维码
if (res.data['wechat_card_url'] !== '') {
wxImg.value = res.data['wechat_card_url']
}
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
// 登录操作
const submitLogin = () => {
if (data.value.username === '') {
return ElMessage.error('请输入用户名');
}
if (data.value.password === '') {
return ElMessage.error('请输入密码');
}
httpPost('/api/user/login', data.value).then((res) => {
setUserToken(res.data)
ElMessage.success("登录成功!")
emits("hide")
emits('success')
}).catch((e) => {
ElMessage.error('登录失败,' + e.message)
})
}
// 注册操作
const submitRegister = () => {
if (data.value.username === '') {
return ElMessage.error('请输入用户名');
}
if (activeName.value === 'mobile' && !validateMobile(data.value.username)) {
return ElMessage.error('请输入合法的手机号');
}
if (activeName.value === 'email' && !validateEmail(data.value.username)) {
return ElMessage.error('请输入合法的邮箱地址');
}
if (data.value.password.length < 8) {
return ElMessage.error('密码的长度为8-16个字符');
}
if (data.value.repass !== data.value.password) {
return ElMessage.error('两次输入密码不一致');
}
if ((activeName.value === 'mobile' || activeName.value === 'email') && data.value.code === '') {
return ElMessage.error('请输入验证码');
}
data.value.reg_way = activeName.value
httpPost('/api/user/register', data.value).then((res) => {
setUserToken(res.data)
ElMessage.success({
"message": "注册成功!",
onClose: () => {
emits("hide")
emits('success')
},
duration: 1000
})
}).catch((e) => {
ElMessage.error('注册失败,' + e.message)
})
}
const close = function () {
emits('hide', false);
}
@@ -191,7 +358,7 @@ const close = function () {
.close-icon {
cursor pointer
position absolute
right 0
right -10px
top 0
font-weight normal
font-size 20px
@@ -202,31 +369,37 @@ const close = function () {
}
}
.login-box {
.label {
padding-top 3px
.el-icon {
position relative
font-size 20px
margin-right 6px
top 4px
}
.el-dialog__body {
padding 10px 20px 20px 20px
}
span {
font-size 16px
}
.form {
.block {
margin-bottom 10px
}
.login-btn {
.btn-row {
display flex
padding-top 10px
justify-content center
.el-button {
font-size 16px
width 100px
width 100%
}
.text {
line-height 40px
.el-tag {
cursor pointer
}
}
}
}
.register-box {
.wechat-card {
text-align center
}
}

View File

@@ -83,7 +83,7 @@
<div class="chat-head">
<div class="chat-config">
<!-- <span class="role-select-label">聊天角色</span>-->
<el-select v-model="roleId" filterable placeholder="角色" class="role-select" @change="newChat">
<el-select v-model="roleId" filterable placeholder="角色" class="role-select" @change="_newChat">
<el-option
v-for="item in roles"
:key="item.id"
@@ -97,7 +97,7 @@
</el-option>
</el-select>
<el-select v-model="modelID" placeholder="模型" @change="newChat">
<el-select v-model="modelID" placeholder="模型" @change="_newChat">
<el-option
v-for="item in models"
:key="item.id"
@@ -238,7 +238,7 @@
<config-dialog v-if="isLogin" :show="showConfigDialog" :models="models" @hide="showConfigDialog = false"/>
<login-dialog :show="true"/>
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
</div>
@@ -292,6 +292,7 @@ const roleId = ref(0)
const newChatItem = ref(null);
const router = useRouter();
const showConfigDialog = ref(false);
const showLoginDialog = ref(false)
const isLogin = ref(false)
const showHello = ref(true)
const textInput = ref(null)
@@ -303,26 +304,65 @@ if (isMobile()) {
router.replace("/mobile")
}
// 获取系统配置
httpGet("/api/config/get?key=system").then(res => {
title.value = res.data.title
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
// 获取系统公告
httpGet("/api/config/get?key=notice").then(res => {
notice.value = md.render(res.data['content'])
const oldNotice = localStorage.getItem(noticeKey.value);
// 如果公告有更新,则显示公告
if (oldNotice !== notice.value && notice.value.length > 10) {
showNotice.value = true
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
onMounted(() => {
resizeElement();
initData()
const clipboard = new Clipboard('.copy-reply, .copy-code-btn');
clipboard.on('success', () => {
ElMessage.success('复制成功!');
})
clipboard.on('error', () => {
ElMessage.error('复制失败!');
})
window.onresize = () => resizeElement();
});
// 初始化数据
const initData = () => {
// 检查会话
checkSession().then((user) => {
loginUser.value = user
isLogin.value = true
// 获取会话列表
httpGet("/api/chat/list?user_id=" + loginUser.value.id).then((res) => {
httpGet("/api/chat/list").then((res) => {
if (res.data) {
chatList.value = res.data;
allChats.value = res.data;
}
// 加载模型
httpGet('/api/model/list?enable=1').then(res => {
httpGet('/api/model/list').then(res => {
models.value = res.data
modelID.value = models.value[0].id
// 加载角色列表
httpGet(`/api/role/list?user_id=${user.id}`).then((res) => {
httpGet(`/api/role/list`).then((res) => {
roles.value = res.data;
roleId.value = roles.value[0]['id'];
const chatId = localStorage.getItem("chat_id")
const chat = getChatById(chatId)
if (chat === null) {
@@ -338,45 +378,38 @@ onMounted(() => {
}).catch(e => {
ElMessage.error("加载模型失败: " + e.message)
})
}).catch(() => {
// TODO: 增加重试按钮
ElMessage.error("加载会话列表失败!")
})
}).catch(() => {
loading.value = false
// 加载会话
httpGet("/api/chat/list").then((res) => {
if (res.data) {
chatList.value = res.data;
allChats.value = res.data;
}
}).catch(() => {
ElMessage.error("加载会话列表失败!")
})
// 获取系统配置
httpGet("/api/config/get?key=system").then(res => {
title.value = res.data.title
// 加载模型
httpGet('/api/model/list').then(res => {
models.value = res.data
modelID.value = models.value[0].id
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
ElMessage.error("加载模型失败: " + e.message)
})
// 获取系统公告
httpGet("/api/config/get?key=notice").then(res => {
notice.value = md.render(res.data['content'])
const oldNotice = localStorage.getItem(noticeKey.value);
// 如果公告有更新,则显示公告
if (oldNotice !== notice.value && notice.value.length > 10) {
showNotice.value = true
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
// 加载角色列表
httpGet(`/api/role/list`).then((res) => {
roles.value = res.data;
roleId.value = roles.value[0]['id'];
}).catch((e) => {
ElMessage.error('获取聊天角色失败: ' + e.messages)
})
}).catch(() => {
router.push('/login')
});
const clipboard = new Clipboard('.copy-reply, .copy-code-btn');
clipboard.on('success', () => {
ElMessage.success('复制成功!');
})
clipboard.on('error', () => {
ElMessage.error('复制失败!');
})
window.onresize = () => resizeElement();
});
}
const getRoleById = function (rid) {
for (let i = 0; i < roles.value.length; i++) {
@@ -393,8 +426,17 @@ const resizeElement = function () {
leftBoxHeight.value = window.innerHeight - 43 - 47 - 45;
};
const _newChat = () => {
if (isLogin.value) {
newChat()
}
}
// 新建会话
const newChat = function () {
const newChat = () => {
if (!isLogin.value) {
showLoginDialog.value = true
return;
}
// 已有新开的会话
if (newChatItem.value !== null && newChatItem.value['role_id'] === roles.value[0]['role_id']) {
return;
@@ -707,6 +749,11 @@ const autofillPrompt = (text) => {
}
// 发送消息
const sendMessage = function () {
if (!isLogin.value) {
showLoginDialog.value = true
return;
}
if (canSend.value === false) {
ElMessage.warning("AI 正在作答中,请稍后...");
return
@@ -769,8 +816,8 @@ const clearAllChats = function () {
const logout = function () {
activelyClose.value = true;
httpGet('/api/user/logout').then(() => {
removeUserToken();
router.push('/login');
removeUserToken()
location.reload()
}).catch(() => {
ElMessage.error('注销失败!');
})

View File

@@ -62,6 +62,7 @@
<el-checkbox-group v-model="system['register_ways']">
<el-checkbox label="mobile">手机注册</el-checkbox>
<el-checkbox label="email">邮箱注册</el-checkbox>
<el-checkbox label="username">用户名注册</el-checkbox>
</el-checkbox-group>
</el-form-item>