mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-12-27 02:25:58 +08:00
增加移动端登录页面
This commit is contained in:
@@ -9,8 +9,10 @@
|
||||
</div>
|
||||
<div class="flex" :class="{ 'top-collapse': !isCollapse }">
|
||||
<div class="top-avatar flex">
|
||||
<span class="title" v-if="!isCollapse">GeekAI</span>
|
||||
<img v-if="loginUser.id" :src="!!loginUser.avatar ? loginUser.avatar : avatarImg" alt="" :class="{ marr: !isCollapse }" />
|
||||
<span class="title" v-if="!isCollapse">{{ title }}</span>
|
||||
<el-tooltip :content="title" placement="right">
|
||||
<img :src="logo" alt="" :class="{ marr: !isCollapse }" v-if="isCollapse" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="menuIcon xxx" @click="isCollapse = !isCollapse">
|
||||
<el-tooltip content="关闭菜单" placement="right" v-if="!isCollapse">
|
||||
@@ -80,6 +82,9 @@
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus setting-menus">
|
||||
<li>
|
||||
<img :src="loginUser.avatar ? loginUser.avatar : avatarImg" />
|
||||
</li>
|
||||
<li>
|
||||
<div @click="showConfigDialog = true" class="flex">
|
||||
<el-icon>
|
||||
@@ -113,12 +118,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<el-scrollbar class="right-main">
|
||||
<div
|
||||
v-if="loginUser.id === undefined || !loginUser.id"
|
||||
class="loginMask"
|
||||
:style="{ left: isCollapse ? '65px' : '170px' }"
|
||||
@click="showNoticeLogin = true"
|
||||
></div>
|
||||
<div class="topheader" v-if="loginUser.id === undefined || !loginUser.id">
|
||||
<el-button @click="router.push('/login')" class="btn-go animate__animated animate__pulse animate__infinite" round>登录</el-button>
|
||||
</div>
|
||||
@@ -132,12 +131,14 @@
|
||||
<!-- </div> -->
|
||||
</el-scrollbar>
|
||||
<config-dialog v-if="loginUser.id" :show="showConfigDialog" @hide="showConfigDialog = false" />
|
||||
<el-dialog v-model="showNoticeLogin">
|
||||
<el-result icon="warning" title="未登录" sub-title="登录后解锁功能">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="router.push('/login')">登录</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
|
||||
<el-dialog v-model="showLoginDialog" width="500px" @close="store.setShowLoginDialog(false)">
|
||||
<template #header>
|
||||
<div class="text-center text-xl" style="color: var(--theme-text-color-primary)">登录后解锁功能</div>
|
||||
</template>
|
||||
<div class="p-4 pt-2 pb-2">
|
||||
<LoginDialog @success="loginSuccess" @hide="store.setShowLoginDialog(false)" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -145,7 +146,6 @@
|
||||
<script setup>
|
||||
import { CirclePlus, Setting, UserFilled } from "@element-plus/icons-vue";
|
||||
import ThemeChange from "@/components/ThemeChange.vue";
|
||||
import avatarImg from "@/assets/img/avatar.jpg";
|
||||
import { useRouter } from "vue-router";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { httpGet } from "@/utils/http";
|
||||
@@ -155,6 +155,8 @@ import { removeUserToken } from "@/store/session";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import ConfigDialog from "@/components/UserInfoDialog.vue";
|
||||
import { showMessageError } from "@/utils/dialog";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import { substr } from "@/utils/libs";
|
||||
|
||||
const isCollapse = ref(true);
|
||||
const router = useRouter();
|
||||
@@ -164,7 +166,14 @@ const moreNavs = ref([]);
|
||||
const curPath = ref();
|
||||
|
||||
const title = ref("");
|
||||
const showNoticeLogin = ref(false);
|
||||
const avatarImg = ref("/images/avatar/default.jpg");
|
||||
const store = useSharedStore();
|
||||
const loginUser = ref({});
|
||||
const routerViewKey = ref(0);
|
||||
const showConfigDialog = ref(false);
|
||||
const license = ref({ de_copy: true });
|
||||
const gitURL = ref(process.env.VUE_APP_GIT_URL);
|
||||
const showLoginDialog = ref(false);
|
||||
|
||||
/**
|
||||
* 从路径名中提取第一个路径段
|
||||
@@ -190,18 +199,11 @@ const getFirstPathSegment = (url) => {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const loginUser = ref({});
|
||||
const routerViewKey = ref(0);
|
||||
const showConfigDialog = ref(false);
|
||||
const license = ref({ de_copy: true });
|
||||
const gitURL = ref(process.env.VUE_APP_GIT_URL);
|
||||
|
||||
const store = useSharedStore();
|
||||
const show = ref(false);
|
||||
watch(
|
||||
() => store.showLoginDialog,
|
||||
(newValue) => {
|
||||
show.value = newValue;
|
||||
showLoginDialog.value = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -216,7 +218,6 @@ if (curPath.value === "/external") {
|
||||
}
|
||||
const changeNav = (item) => {
|
||||
curPath.value = item.url;
|
||||
console.log(item.url);
|
||||
if (item.url.indexOf("http") !== -1) {
|
||||
// 外部链接
|
||||
router.push({ path: "/external", query: { url: item.url, title: item.name } });
|
||||
@@ -280,16 +281,19 @@ const logout = function () {
|
||||
httpGet("/api/user/logout")
|
||||
.then(() => {
|
||||
removeUserToken();
|
||||
store.setShowLoginDialog(true);
|
||||
store.setIsLogin(false);
|
||||
loginUser.value = {};
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1;
|
||||
router.push("/login");
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("注销失败!");
|
||||
});
|
||||
};
|
||||
|
||||
const loginSuccess = () => {
|
||||
init();
|
||||
store.setShowLoginDialog(false);
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
@@ -311,110 +311,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 任务详情弹框 -->
|
||||
<el-dialog v-model="showTaskDialog" title="绘画任务详情" :fullscreen="true">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<div class="img-container" :style="{ maxHeight: fullImgHeight + 'px' }">
|
||||
<el-image :src="item['img_url']" fit="contain" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="task-info">
|
||||
<div class="info-line">
|
||||
<el-divider> 正向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.prompt }}</span>
|
||||
<el-icon class="copy-prompt-sd" :data-clipboard-text="item.prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<el-divider> 反向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.params.neg_prompt }}</span>
|
||||
<el-icon class="copy-prompt-sd" :data-clipboard-text="item.params.neg_prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>采样方法:</label>
|
||||
<div class="item-value">{{ item.params.sampler }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>图片尺寸:</label>
|
||||
<div class="item-value">{{ item.params.width }} x {{ item.params.height }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>迭代步数:</label>
|
||||
<div class="item-value">{{ item.params.steps }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>引导系数:</label>
|
||||
<div class="item-value">{{ item.params.cfg_scale }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>随机因子:</label>
|
||||
<div class="item-value">{{ item.params.seed }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.params.hd_fix">
|
||||
<el-divider> 高清修复 </el-divider>
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>重绘幅度:</label>
|
||||
<div class="item-value">
|
||||
{{ item.params.hd_redraw_rate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>放大算法:</label>
|
||||
<div class="item-value">{{ item.params.hd_scale_alg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>放大倍数:</label>
|
||||
<div class="item-value">{{ item.params.hd_scale }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>迭代步数:</label>
|
||||
<div class="item-value">{{ item.params.hd_steps }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copy-params">
|
||||
<el-button type="primary" round @click="copyParams(item)">画一张同款的</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
<sd-task-view v-model="showTaskDialog" :data="item" @drawSame="copyParams" @close="showTaskDialog = false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -434,7 +331,7 @@ import { useSharedStore } from "@/store/sharedata";
|
||||
import TaskList from "@/components/TaskList.vue";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
import { showMessageError } from "@/utils/dialog";
|
||||
|
||||
import SdTaskView from "@/components/SdTaskView.vue";
|
||||
const listBoxHeight = ref(0);
|
||||
// const paramBoxHeight = ref(0)
|
||||
const fullImgHeight = ref(window.innerHeight - 60);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="inner custom-scroll">
|
||||
<div class="header">
|
||||
<h2 class="text-xl pt-4 pb-4">AI 绘画作品墙</h2>
|
||||
<div class="settings">
|
||||
<div class="settings pr-14">
|
||||
<el-radio-group v-model="imgType" @change="changeImgType">
|
||||
<el-radio value="mj" size="large">MidJourney</el-radio>
|
||||
<el-radio value="sd" size="large">Stable Diffusion</el-radio>
|
||||
@@ -161,120 +161,7 @@
|
||||
<!-- end of waterfall -->
|
||||
</div>
|
||||
<!-- 任务详情弹框 -->
|
||||
<el-dialog v-model="showTaskDialog" title="绘画任务详情" :fullscreen="true">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<div class="img-container" :style="{ maxHeight: fullImgHeight + 'px' }">
|
||||
<el-image :src="item['img_url']" fit="contain">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">正在加载图片</div>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon>
|
||||
<Picture />
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="task-info">
|
||||
<div class="info-line">
|
||||
<el-divider> 正向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.prompt }}</span>
|
||||
<el-icon class="copy-prompt-wall" :data-clipboard-text="item.prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<el-divider> 反向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.params.negative_prompt }}</span>
|
||||
<el-icon class="copy-prompt-wall" :data-clipboard-text="item.params.negative_prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>采样方法:</label>
|
||||
<div class="item-value">{{ item.params.sampler }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>图片尺寸:</label>
|
||||
<div class="item-value">{{ item.params.width }} x {{ item.params.height }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>迭代步数:</label>
|
||||
<div class="item-value">{{ item.params.steps }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>引导系数:</label>
|
||||
<div class="item-value">{{ item.params.cfg_scale }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>随机因子:</label>
|
||||
<div class="item-value">{{ item.params.seed }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.params.hd_fix">
|
||||
<el-divider> 高清修复 </el-divider>
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>重绘幅度:</label>
|
||||
<div class="item-value">{{ item.params.hd_redraw_rate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>放大算法:</label>
|
||||
<div class="item-value">{{ item.params.hd_scale_alg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>放大倍数:</label>
|
||||
<div class="item-value">{{ item.params.hd_scale }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>迭代步数:</label>
|
||||
<div class="item-value">{{ item.params.hd_steps }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copy-params">
|
||||
<el-button type="primary" round @click="drawSameSd(item)">画一张同款的</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
<sd-task-view v-model="showTaskDialog" :data="item" @drawSame="drawSameSd" @close="showTaskDialog = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -287,7 +174,7 @@ import { ElMessage } from "element-plus";
|
||||
import Clipboard from "clipboard";
|
||||
import { useRouter } from "vue-router";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
|
||||
import SdTaskView from "@/components/SdTaskView.vue";
|
||||
const data = ref({
|
||||
mj: [],
|
||||
sd: [],
|
||||
@@ -298,7 +185,6 @@ const isOver = ref(false);
|
||||
const imgType = ref("mj"); // 图片类别
|
||||
const listBoxHeight = window.innerHeight - 124;
|
||||
const colWidth = ref(220);
|
||||
const fullImgHeight = ref(window.innerHeight - 60);
|
||||
const showTaskDialog = ref(false);
|
||||
const item = ref({});
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
<el-menu mode="horizontal" :ellipsis="false">
|
||||
<div class="menu-item">
|
||||
<!-- <el-image :src="logo" class="logo" alt="Geek-AI" /> -->
|
||||
<div class="logo-box">
|
||||
<img src="@/assets/img/logo.png" alt="" />
|
||||
</div>
|
||||
<img :src="logo" class="logo" alt="" />
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<span v-if="!license.de_copy">
|
||||
|
||||
@@ -5,12 +5,7 @@
|
||||
<AccountTop>
|
||||
<template #default>
|
||||
<div class="wechatLog flex-center" v-if="wechatLoginURL !== ''">
|
||||
<a
|
||||
:href="wechatLoginURL"
|
||||
@click="setRoute(router.currentRoute.value.path)"
|
||||
>
|
||||
<i class="iconfont icon-wechat"></i>使用微信登录
|
||||
</a>
|
||||
<a :href="wechatLoginURL" @click="setRoute(router.currentRoute.value.path)"> <i class="iconfont icon-wechat"></i>使用微信登录 </a>
|
||||
</div>
|
||||
</template>
|
||||
</AccountTop>
|
||||
@@ -19,47 +14,26 @@
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules">
|
||||
<el-form-item label="" prop="username">
|
||||
<div class="form-title">账号</div>
|
||||
<el-input
|
||||
v-model="ruleForm.username"
|
||||
size="large"
|
||||
placeholder="请输入账号"
|
||||
@keyup="handleKeyup"
|
||||
/>
|
||||
<el-input v-model="ruleForm.username" size="large" placeholder="请输入账号" @keyup="handleKeyup" />
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="password">
|
||||
<div class="flex-between w100">
|
||||
<div class="form-title">密码</div>
|
||||
<div
|
||||
class="form-forget text-color-primary"
|
||||
@click="router.push('/resetpassword')"
|
||||
>
|
||||
忘记密码?
|
||||
</div>
|
||||
<div class="form-forget text-color-primary" @click="router.push('/resetpassword')">忘记密码?</div>
|
||||
</div>
|
||||
|
||||
<el-input
|
||||
size="large"
|
||||
v-model="ruleForm.password"
|
||||
placeholder="请输入密码"
|
||||
show-password
|
||||
autocomplete="off"
|
||||
@keyup="handleKeyup"
|
||||
/>
|
||||
<el-input size="large" v-model="ruleForm.password" placeholder="请输入密码" show-password autocomplete="off" @keyup="handleKeyup" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
class="login-btn"
|
||||
size="large"
|
||||
type="primary"
|
||||
@click="login"
|
||||
>登录</el-button
|
||||
>
|
||||
<el-button class="login-btn" size="large" type="primary" @click="login">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<account-bg />
|
||||
|
||||
<captcha v-if="enableVerify" @success="doLogin" ref="captchaRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -76,6 +50,7 @@ import { setRoute } from "@/store/system";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
|
||||
import AccountTop from "@/components/AccountTop.vue";
|
||||
import Captcha from "@/components/Captcha.vue";
|
||||
|
||||
const router = useRouter();
|
||||
const title = ref("Geek-AI");
|
||||
@@ -87,16 +62,14 @@ const licenseConfig = ref({});
|
||||
const wechatLoginURL = ref("");
|
||||
const enableVerify = ref(false);
|
||||
const captchaRef = ref(null);
|
||||
// 是否需要验证码,输入一次密码错之后就要验证码
|
||||
const needVerify = ref(false);
|
||||
const ruleFormRef = ref(null);
|
||||
const ruleForm = reactive({
|
||||
username: process.env.VUE_APP_USER,
|
||||
password: process.env.VUE_APP_PASS
|
||||
password: process.env.VUE_APP_PASS,
|
||||
});
|
||||
const rules = {
|
||||
username: [{ required: true, trigger: "blur", message: "请输入账号" }],
|
||||
password: [{ required: true, trigger: "blur", message: "请输入密码" }]
|
||||
password: [{ required: true, trigger: "blur", message: "请输入密码" }],
|
||||
};
|
||||
onMounted(() => {
|
||||
// 获取系统配置
|
||||
@@ -147,7 +120,7 @@ const handleKeyup = (e) => {
|
||||
const login = async function () {
|
||||
await ruleFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (enableVerify.value && needVerify.value) {
|
||||
if (enableVerify.value) {
|
||||
captchaRef.value.loadCaptcha();
|
||||
} else {
|
||||
doLogin({});
|
||||
@@ -163,12 +136,11 @@ const doLogin = (verifyData) => {
|
||||
password: password.value.trim(),
|
||||
key: verifyData.key,
|
||||
dots: verifyData.dots,
|
||||
x: verifyData.x
|
||||
x: verifyData.x,
|
||||
})
|
||||
.then((res) => {
|
||||
setUserToken(res.data.token);
|
||||
store.setIsLogin(true);
|
||||
needVerify.value = false;
|
||||
if (isMobile()) {
|
||||
router.push("/mobile");
|
||||
} else {
|
||||
@@ -177,7 +149,6 @@ const doLogin = (verifyData) => {
|
||||
})
|
||||
.catch((e) => {
|
||||
showMessageError("登录失败," + e.message);
|
||||
needVerify.value = true;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="index container">
|
||||
<h2 class="title">{{title}}</h2>
|
||||
<van-notice-bar left-icon="info-o" :scrollable="true">{{slogan}}}</van-notice-bar>
|
||||
<h2 class="title">{{ title }}</h2>
|
||||
<van-notice-bar left-icon="info-o" :scrollable="true">{{ slogan }}}</van-notice-bar>
|
||||
|
||||
<div class="content">
|
||||
<van-grid :column-num="3" :gutter="10" border>
|
||||
@@ -11,7 +11,6 @@
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="text">AI 对话</div>
|
||||
|
||||
</template>
|
||||
</van-grid-item>
|
||||
|
||||
@@ -35,32 +34,25 @@
|
||||
</van-grid>
|
||||
|
||||
<div class="app-list">
|
||||
<van-list
|
||||
v-model:loading="loading"
|
||||
:finished="true"
|
||||
finished-text=""
|
||||
@load="fetchApps"
|
||||
>
|
||||
<van-list v-model:loading="loading" :finished="true" finished-text="" @load="fetchApps">
|
||||
<van-cell v-for="item in apps" :key="item.id">
|
||||
<div>
|
||||
<div class="item">
|
||||
<div class="image">
|
||||
<div class="image flex justify-center items-center">
|
||||
<van-image :src="item.icon" />
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="info-title">{{item.name}}</div>
|
||||
<div class="info-text">{{item.hello_msg}}</div>
|
||||
<div class="info-title">{{ item.name }}</div>
|
||||
<div class="info-text">{{ item.hello_msg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn">
|
||||
<div v-if="hasRole(item.key)">
|
||||
<van-button size="small" type="success" @click="useRole(item.id)">使用</van-button>
|
||||
<van-button size="small" type="danger" @click="updateRole(item,'remove')">移除</van-button>
|
||||
<van-button size="small" type="danger" @click="updateRole(item, 'remove')">移除</van-button>
|
||||
</div>
|
||||
<van-button v-else size="small"
|
||||
style="--el-color-primary:#009999"
|
||||
@click="updateRole(item, 'add')">
|
||||
<van-button v-else size="small" style="--el-color-primary: #009999" @click="updateRole(item, 'add')">
|
||||
<van-icon name="add-o" />
|
||||
<span>添加应用</span>
|
||||
</van-button>
|
||||
@@ -74,91 +66,98 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, ref} from "vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {arrayContains, removeArrayItem, showLoginDialog, substr} from "@/utils/libs";
|
||||
import {showNotify} from "vant";
|
||||
import {ElMessage} from "element-plus";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { checkSession, getSystemInfo } from "@/store/cache";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
import { arrayContains, removeArrayItem, showLoginDialog, substr } from "@/utils/libs";
|
||||
import { showNotify } from "vant";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const title = ref(process.env.VUE_APP_TITLE)
|
||||
const router = useRouter()
|
||||
const isLogin = ref(false)
|
||||
const apps = ref([])
|
||||
const loading = ref(false)
|
||||
const roles = ref([])
|
||||
const slogan = ref('你有多大想象力,AI就有多大创造力!')
|
||||
const title = ref(process.env.VUE_APP_TITLE);
|
||||
const router = useRouter();
|
||||
const isLogin = ref(false);
|
||||
const apps = ref([]);
|
||||
const loading = ref(false);
|
||||
const roles = ref([]);
|
||||
const slogan = ref("你有多大想象力,AI就有多大创造力!");
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo().then(res => {
|
||||
title.value = res.data.title
|
||||
if (res.data.slogan) {
|
||||
slogan.value = res.data.slogan
|
||||
}
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message)
|
||||
})
|
||||
getSystemInfo()
|
||||
.then((res) => {
|
||||
title.value = res.data.title;
|
||||
if (res.data.slogan) {
|
||||
slogan.value = res.data.slogan;
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message);
|
||||
});
|
||||
|
||||
checkSession().then((user) => {
|
||||
isLogin.value = true
|
||||
roles.value = user.chat_roles
|
||||
}).catch(() => {
|
||||
})
|
||||
fetchApps()
|
||||
})
|
||||
checkSession()
|
||||
.then((user) => {
|
||||
isLogin.value = true;
|
||||
roles.value = user.chat_roles;
|
||||
})
|
||||
.catch(() => {});
|
||||
fetchApps();
|
||||
});
|
||||
|
||||
const fetchApps = () => {
|
||||
httpGet("/api/app/list").then((res) => {
|
||||
const items = res.data
|
||||
// 处理 hello message
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
items[i].intro = substr(items[i].hello_msg, 80)
|
||||
}
|
||||
apps.value = items
|
||||
}).catch(e => {
|
||||
showNotify({type:"danger", message:"获取应用失败:" + e.message})
|
||||
})
|
||||
}
|
||||
httpGet("/api/app/list")
|
||||
.then((res) => {
|
||||
const items = res.data;
|
||||
// 处理 hello message
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
items[i].intro = substr(items[i].hello_msg, 80);
|
||||
}
|
||||
apps.value = items;
|
||||
})
|
||||
.catch((e) => {
|
||||
showNotify({ type: "danger", message: "获取应用失败:" + e.message });
|
||||
});
|
||||
};
|
||||
|
||||
const updateRole = (row, opt) => {
|
||||
if (!isLogin.value) {
|
||||
return showLoginDialog(router)
|
||||
return showLoginDialog(router);
|
||||
}
|
||||
|
||||
const title = ref("")
|
||||
const title = ref("");
|
||||
if (opt === "add") {
|
||||
title.value = "添加应用"
|
||||
const exists = arrayContains(roles.value, row.key)
|
||||
title.value = "添加应用";
|
||||
const exists = arrayContains(roles.value, row.key);
|
||||
if (exists) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
roles.value.push(row.key)
|
||||
roles.value.push(row.key);
|
||||
} else {
|
||||
title.value = "移除应用"
|
||||
const exists = arrayContains(roles.value, row.key)
|
||||
title.value = "移除应用";
|
||||
const exists = arrayContains(roles.value, row.key);
|
||||
if (!exists) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
roles.value = removeArrayItem(roles.value, row.key)
|
||||
roles.value = removeArrayItem(roles.value, row.key);
|
||||
}
|
||||
httpPost("/api/role/update", {keys: roles.value}).then(() => {
|
||||
ElMessage.success({message: title.value + "成功!", duration: 1000})
|
||||
}).catch(e => {
|
||||
ElMessage.error(title.value + "失败:" + e.message)
|
||||
})
|
||||
}
|
||||
httpPost("/api/role/update", { keys: roles.value })
|
||||
.then(() => {
|
||||
ElMessage.success({ message: title.value + "成功!", duration: 1000 });
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error(title.value + "失败:" + e.message);
|
||||
});
|
||||
};
|
||||
|
||||
const hasRole = (roleKey) => {
|
||||
return arrayContains(roles.value, roleKey, (v1, v2) => v1 === v2)
|
||||
}
|
||||
return arrayContains(roles.value, roleKey, (v1, v2) => v1 === v2);
|
||||
};
|
||||
|
||||
const useRole = (roleId) => {
|
||||
if (!isLogin.value) {
|
||||
return showLoginDialog(router)
|
||||
return showLoginDialog(router);
|
||||
}
|
||||
router.push(`/mobile/chat/session?role_id=${roleId}`)
|
||||
}
|
||||
router.push(`/mobile/chat/session?role_id=${roleId}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
|
||||
43
web/src/views/mobile/Login.vue
Normal file
43
web/src/views/mobile/Login.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="login flex w-full flex-col place-content-center h-lvh">
|
||||
<el-image src="/images/logo.png" class="w-1/2 mx-auto logo" />
|
||||
<div class="title text-center text-3xl font-bold mt-8">{{ title }}</div>
|
||||
<login-dialog @success="loginSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import { getSystemInfo } from "@/store/cache";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const router = useRouter();
|
||||
const title = ref("登录");
|
||||
|
||||
const loginSuccess = () => {
|
||||
router.back();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo().then((res) => {
|
||||
title.value = res.data.title;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.login {
|
||||
background: var(--theme-bg);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.logo {
|
||||
background: #ffffff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--text-theme-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user