mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
增加移动端登录页面
This commit is contained in:
parent
acee2d9d81
commit
8af0fec8ec
@ -9,7 +9,8 @@
|
||||
- Bug 修复:修复 Suno 已完成任务删除失败的 错误
|
||||
- 功能新增:支持 OpenAI 实时语音通话功能,目前已经支持按次收费,支持管理员设置每次实时语音通话的算力消耗
|
||||
- 功能新增:生成提示词需要消耗算力,支持管理员设置每次生成提示词的算力消耗,防止被白嫖
|
||||
- 功能新增:DALL-E-3 绘图支持 Gitee AI Flex 接口
|
||||
- 功能新增:DALL-E-3 绘图支持 Flux 绘图模型,支持在管理后添加 Flux,SD 等绘图模型
|
||||
- 功能优化:Markdown 支持解析 emoji 表情
|
||||
|
||||
## v4.1.7
|
||||
|
||||
|
BIN
web/public/images/avatar/default.jpg
Normal file
BIN
web/public/images/avatar/default.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 76 KiB |
@ -2,6 +2,7 @@
|
||||
--sm-txt:rgba(163, 174, 208, 1);
|
||||
--text-secondary: #8a939d;
|
||||
--el-color-primary: rgb(107, 80, 225);
|
||||
--van-primary-color:rgb(107, 80, 225);
|
||||
--theme-textcolor-normal:#b0a0f8;
|
||||
--el-border-radius-base: 5px;
|
||||
--el-color-primary-light-5:rgb(107, 85, 255);
|
||||
|
@ -20,8 +20,12 @@
|
||||
height: 100vh;
|
||||
.title{
|
||||
font-size: 28px
|
||||
height: 40px
|
||||
width 120px
|
||||
text-align: center;
|
||||
word-wrap break-all;
|
||||
overflow hidden
|
||||
font-weight: 700
|
||||
margin-right: 6px
|
||||
color:var(--text-theme-color)
|
||||
}
|
||||
img{
|
||||
|
@ -52,6 +52,8 @@
|
||||
|
||||
.list-item {
|
||||
|
||||
display flex
|
||||
|
||||
.image {
|
||||
overflow hidden
|
||||
|
||||
|
@ -70,8 +70,10 @@
|
||||
}
|
||||
|
||||
.logo {
|
||||
height 50px
|
||||
height 60px
|
||||
width 60px
|
||||
border-radius 50%
|
||||
background: var(--theme-bg)
|
||||
}
|
||||
|
||||
.el-button {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 75 KiB |
@ -1,130 +1,138 @@
|
||||
<template>
|
||||
<el-container class="captcha-box">
|
||||
<el-dialog
|
||||
v-model="show"
|
||||
:close-on-click-modal="true"
|
||||
:show-close="false"
|
||||
style="width: 360px;"
|
||||
>
|
||||
<el-dialog v-model="show" :close-on-click-modal="true" :show-close="isMobileInternal" style="width: 360px; --el-dialog-padding-primary: 5px 15px 15px 15px">
|
||||
<template #title>
|
||||
<div class="text-center p-3" style="color: var(--el-text-color-primary)" v-if="isMobileInternal">
|
||||
<span>人机验证</span>
|
||||
</div>
|
||||
</template>
|
||||
<slide-captcha
|
||||
v-if="isMobile()"
|
||||
:bg-img="bgImg"
|
||||
:bk-img="bkImg"
|
||||
:result="result"
|
||||
@refresh="getSlideCaptcha"
|
||||
@confirm="handleSlideConfirm"
|
||||
@hide="show = false"/>
|
||||
v-if="isMobileInternal"
|
||||
:bg-img="bgImg"
|
||||
:bk-img="bkImg"
|
||||
:result="result"
|
||||
@refresh="getSlideCaptcha"
|
||||
@confirm="handleSlideConfirm"
|
||||
@hide="show = false"
|
||||
/>
|
||||
|
||||
<captcha-plus
|
||||
v-else
|
||||
:max-dot="maxDot"
|
||||
:image-base64="imageBase64"
|
||||
:thumb-base64="thumbBase64"
|
||||
width="300"
|
||||
@close="show = false"
|
||||
@refresh="handleRequestCaptCode"
|
||||
@confirm="handleConfirm"
|
||||
v-else
|
||||
:max-dot="maxDot"
|
||||
:image-base64="imageBase64"
|
||||
:thumb-base64="thumbBase64"
|
||||
width="300"
|
||||
@close="show = false"
|
||||
@refresh="handleRequestCaptCode"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import lodash from 'lodash'
|
||||
import {validateEmail, validateMobile} from "@/utils/validate";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import { ref } from "vue";
|
||||
import lodash from "lodash";
|
||||
import { validateEmail, validateMobile } from "@/utils/validate";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
import CaptchaPlus from "@/components/CaptchaPlus.vue";
|
||||
import SlideCaptcha from "@/components/SlideCaptcha.vue";
|
||||
import {isMobile} from "@/utils/libs";
|
||||
import {showMessageError, showMessageOK} from "@/utils/dialog";
|
||||
import { isMobile } from "@/utils/libs";
|
||||
import { showMessageError, showMessageOK } from "@/utils/dialog";
|
||||
|
||||
const show = ref(false)
|
||||
const maxDot = ref(5)
|
||||
const imageBase64 = ref('')
|
||||
const thumbBase64 = ref('')
|
||||
const captKey = ref('')
|
||||
const dots = ref(null)
|
||||
const show = ref(false);
|
||||
const maxDot = ref(5);
|
||||
const imageBase64 = ref("");
|
||||
const thumbBase64 = ref("");
|
||||
const captKey = ref("");
|
||||
const dots = ref(null);
|
||||
const isMobileInternal = isMobile();
|
||||
|
||||
const emits = defineEmits(['success']);
|
||||
const emits = defineEmits(["success"]);
|
||||
const handleRequestCaptCode = () => {
|
||||
|
||||
httpGet('/api/captcha/get').then(res => {
|
||||
const data = res.data
|
||||
imageBase64.value = data.image
|
||||
thumbBase64.value = data.thumb
|
||||
captKey.value = data.key
|
||||
}).catch(e => {
|
||||
showMessageError('获取人机验证数据失败:' + e.message)
|
||||
})
|
||||
}
|
||||
httpGet("/api/captcha/get")
|
||||
.then((res) => {
|
||||
const data = res.data;
|
||||
imageBase64.value = data.image;
|
||||
thumbBase64.value = data.thumb;
|
||||
captKey.value = data.key;
|
||||
})
|
||||
.catch((e) => {
|
||||
showMessageError("获取人机验证数据失败:" + e.message);
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfirm = (dts) => {
|
||||
if (lodash.size(dts) <= 0) {
|
||||
return showMessageError('请进行人机验证再操作')
|
||||
return showMessageError("请进行人机验证再操作");
|
||||
}
|
||||
|
||||
let dotArr = []
|
||||
let dotArr = [];
|
||||
lodash.forEach(dts, (dot) => {
|
||||
dotArr.push(dot.x, dot.y)
|
||||
})
|
||||
dots.value = dotArr.join(',')
|
||||
httpPost('/api/captcha/check', {
|
||||
dotArr.push(dot.x, dot.y);
|
||||
});
|
||||
dots.value = dotArr.join(",");
|
||||
httpPost("/api/captcha/check", {
|
||||
dots: dots.value,
|
||||
key: captKey.value
|
||||
}).then(() => {
|
||||
// ElMessage.success('人机验证成功')
|
||||
show.value = false
|
||||
emits('success', {key:captKey.value, dots:dots.value})
|
||||
}).catch(() => {
|
||||
showMessageError('人机验证失败')
|
||||
handleRequestCaptCode()
|
||||
key: captKey.value,
|
||||
})
|
||||
}
|
||||
.then(() => {
|
||||
// ElMessage.success('人机验证成功')
|
||||
show.value = false;
|
||||
emits("success", { key: captKey.value, dots: dots.value });
|
||||
})
|
||||
.catch(() => {
|
||||
showMessageError("人机验证失败");
|
||||
handleRequestCaptCode();
|
||||
});
|
||||
};
|
||||
|
||||
const loadCaptcha = () => {
|
||||
show.value = true
|
||||
show.value = true;
|
||||
// 手机用滑动验证码
|
||||
if (isMobile()) {
|
||||
getSlideCaptcha()
|
||||
getSlideCaptcha();
|
||||
} else {
|
||||
handleRequestCaptCode()
|
||||
handleRequestCaptCode();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 滑动验证码
|
||||
const bgImg = ref('')
|
||||
const bkImg = ref('')
|
||||
const result = ref(0)
|
||||
const bgImg = ref("");
|
||||
const bkImg = ref("");
|
||||
const result = ref(0);
|
||||
|
||||
const getSlideCaptcha = () => {
|
||||
result.value = 0
|
||||
httpGet("/api/captcha/slide/get").then(res => {
|
||||
bkImg.value = res.data.bkImg
|
||||
bgImg.value = res.data.bgImg
|
||||
captKey.value = res.data.key
|
||||
}).catch(e => {
|
||||
showMessageError('获取人机验证数据失败:' + e.message)
|
||||
})
|
||||
}
|
||||
result.value = 0;
|
||||
httpGet("/api/captcha/slide/get")
|
||||
.then((res) => {
|
||||
bkImg.value = res.data.bkImg;
|
||||
bgImg.value = res.data.bgImg;
|
||||
captKey.value = res.data.key;
|
||||
})
|
||||
.catch((e) => {
|
||||
showMessageError("获取人机验证数据失败:" + e.message);
|
||||
});
|
||||
};
|
||||
|
||||
const handleSlideConfirm = (x) => {
|
||||
httpPost("/api/captcha/slide/check", {
|
||||
key: captKey.value,
|
||||
x: x
|
||||
}).then(() => {
|
||||
result.value = 1
|
||||
show.value = false
|
||||
emits('success',{key:captKey.value, x:x})
|
||||
}).catch(() => {
|
||||
result.value = 2
|
||||
x: x,
|
||||
})
|
||||
}
|
||||
.then(() => {
|
||||
result.value = 1;
|
||||
show.value = false;
|
||||
emits("success", { key: captKey.value, x: x });
|
||||
})
|
||||
.catch(() => {
|
||||
result.value = 2;
|
||||
});
|
||||
};
|
||||
|
||||
// 导出方法以便父组件调用
|
||||
defineExpose({
|
||||
loadCaptcha
|
||||
loadCaptcha,
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -137,8 +145,9 @@ defineExpose({
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding 0
|
||||
padding-bottom: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,90 +1,93 @@
|
||||
<template>
|
||||
|
||||
<div class="wg-cap-wrap" :style="{width: width}">
|
||||
<div class="wg-cap-wrap" :style="{ width: width }">
|
||||
<div class="wg-cap-wrap__header">
|
||||
<span>请在下图<em>依次</em>点击:</span>
|
||||
<img class="wg-cap-wrap__thumb" v-if="thumbBase64Code" :src="thumbBase64Code" alt=" ">
|
||||
<img class="wg-cap-wrap__thumb" v-if="thumbBase64Code" :src="thumbBase64Code" alt=" " />
|
||||
</div>
|
||||
<div class="wg-cap-wrap__body">
|
||||
<img class="wg-cap-wrap__picture" v-if="imageBase64Code" :src="imageBase64Code" alt=" "
|
||||
@click="handleClickPos($event)">
|
||||
<img class="wg-cap-wrap__loading"
|
||||
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBzdHlsZT0ibWFyZ2luOiBhdXRvOyBiYWNrZ3JvdW5kOiByZ2JhKDI0MSwgMjQyLCAyNDMsIDApOyBkaXNwbGF5OiBibG9jazsgc2hhcGUtcmVuZGVyaW5nOiBhdXRvOyIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjM2LjgxMDEiIHI9IjEzIiBmaWxsPSIjM2U3Y2ZmIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN5IiBkdXI9IjFzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC40NSAwIDAuOSAwLjU1OzAgMC40NSAwLjU1IDAuOSIga2V5VGltZXM9IjA7MC41OzEiIHZhbHVlcz0iMjM7Nzc7MjMiPjwvYW5pbWF0ZT4KICA8L2NpcmNsZT4KPC9zdmc+"
|
||||
alt="正在加载中...">
|
||||
<img class="wg-cap-wrap__picture" v-if="imageBase64Code" :src="imageBase64Code" alt=" " @click="handleClickPos($event)" />
|
||||
<img
|
||||
class="wg-cap-wrap__loading"
|
||||
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBzdHlsZT0ibWFyZ2luOiBhdXRvOyBiYWNrZ3JvdW5kOiByZ2JhKDI0MSwgMjQyLCAyNDMsIDApOyBkaXNwbGF5OiBibG9jazsgc2hhcGUtcmVuZGVyaW5nOiBhdXRvOyIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjM2LjgxMDEiIHI9IjEzIiBmaWxsPSIjM2U3Y2ZmIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN5IiBkdXI9IjFzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC40NSAwIDAuOSAwLjU1OzAgMC40NSAwLjU1IDAuOSIga2V5VGltZXM9IjA7MC41OzEiIHZhbHVlcz0iMjM7Nzc7MjMiPjwvYW5pbWF0ZT4KICA8L2NpcmNsZT4KPC9zdmc+"
|
||||
alt="正在加载中..."
|
||||
/>
|
||||
<div v-for="(dot, key) in dots" :key="key" class="wg-cap-wrap__dot" :style="`top: ${dot.y}px; left:${dot.x}px;`">
|
||||
<span>{{ dot.index }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wg-cap-wrap__footer">
|
||||
<div class="wg-cap-wrap__ico">
|
||||
<img @click="handleCloseEvent"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB0PSIxNjI2NjE0NDM5NDIzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg2NzUiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNNTEyIDIzLjI3MjcyN2MyNjkuOTE3MDkxIDAgNDg4LjcyNzI3MyAyMTguODEwMTgyIDQ4OC43MjcyNzMgNDg4LjcyNzI3M2E0ODYuNjMyNzI3IDQ4Ni42MzI3MjcgMCAwIDEtODQuOTQ1NDU1IDI3NS40MDk0NTUgNDYuNTQ1NDU1IDQ2LjU0NTQ1NSAwIDAgMS03Ni44NDY1NDUtNTIuNTI2NTQ2QTM5My41NDE4MTggMzkzLjU0MTgxOCAwIDAgMCA5MDcuNjM2MzY0IDUxMmMwLTIxOC41MDc2MzYtMTc3LjEyODcyNy0zOTUuNjM2MzY0LTM5NS42MzYzNjQtMzk1LjYzNjM2NFMxMTYuMzYzNjM2IDI5My40OTIzNjQgMTE2LjM2MzYzNiA1MTJzMTc3LjEyODcyNyAzOTUuNjM2MzY0IDM5NS42MzYzNjQgMzk1LjYzNjM2NGEzOTUuMTcwOTA5IDM5NS4xNzA5MDkgMCAwIDAgMTI1LjQ0LTIwLjI5MzgxOSA0Ni41NDU0NTUgNDYuNTQ1NDU1IDAgMCAxIDI5LjQ4NjU0NSA4OC4yOTY3MjhBNDg4LjI2MTgxOCA0ODguMjYxODE4IDAgMCAxIDUxMiAxMDAwLjcyNzI3M0MyNDIuMDgyOTA5IDEwMDAuNzI3MjczIDIzLjI3MjcyNyA3ODEuOTE3MDkxIDIzLjI3MjcyNyA1MTJTMjQyLjA4MjkwOSAyMy4yNzI3MjcgNTEyIDIzLjI3MjcyN3ogbS0xMTUuMiAzMDcuNzEyTDUxMiA0NDYuMTM4MTgybDExNS4yLTExNS4yYTQ2LjU0NTQ1NSA0Ni41NDU0NTUgMCAxIDEgNjUuODE1MjczIDY1Ljg2MTgxOEw1NzcuODYxODE4IDUxMmwxMTUuMiAxMTUuMmE0Ni41NDU0NTUgNDYuNTQ1NDU1IDAgMSAxLTY1Ljg2MTgxOCA2NS44MTUyNzNMNTEyIDU3Ny44NjE4MThsLTExNS4yIDExNS4yYTQ2LjU0NTQ1NSA0Ni41NDU0NTUgMCAxIDEtNjUuODE1MjczLTY1Ljg2MTgxOEw0NDYuMTM4MTgyIDUxMmwtMTE1LjItMTE1LjJhNDYuNTQ1NDU1IDQ2LjU0NTQ1NSAwIDEgMSA2NS44NjE4MTgtNjUuODE1MjczeiIgcC1pZD0iODY3NiIgZmlsbD0iIzcwNzA3MCI+PC9wYXRoPjwvc3ZnPg=="
|
||||
alt="关闭">
|
||||
<img @click="handleRefreshEvent"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB0PSIxNjI2NjE0NDk5NjM4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEzNjAiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMTg3LjQ1NiA0MjUuMDI0YTMzNiAzMzYgMCAwIDAgMzY4LjM4NCA0MjAuMjI0IDQ4IDQ4IDAgMCAxIDEyLjU0NCA5NS4xNjggNDMyIDQzMiAwIDAgMS00NzMuNjY0LTU0MC4xNmwtNTcuMjgtMTUuMzZhMTIuOCAxMi44IDAgMCAxLTYuMjcyLTIwLjkyOGwxNTkuMTY4LTE3OS40NTZhMTIuOCAxMi44IDAgMCAxIDIyLjE0NCA1Ljg4OGw0OC4wNjQgMjM1LjA3MmExMi44IDEyLjggMCAwIDEtMTUuODA4IDE0LjkxMmwtNTcuMjgtMTUuMzZ6TTgzNi40OCA1OTkuMDRhMzM2IDMzNiAwIDAgMC0zNjguMzg0LTQyMC4yMjQgNDggNDggMCAxIDEtMTIuNTQ0LTk1LjE2OCA0MzIgNDMyIDAgMCAxIDQ3My42NjQgNTQwLjE2bDU3LjI4IDE1LjM2YTEyLjggMTIuOCAwIDAgMSA2LjI3MiAyMC45MjhsLTE1OS4xNjggMTc5LjQ1NmExMi44IDEyLjggMCAwIDEtMjIuMTQ0LTUuODg4bC00OC4wNjQtMjM1LjA3MmExMi44IDEyLjggMCAwIDEgMTUuODA4LTE0LjkxMmw1Ny4yOCAxNS4zNnoiIGZpbGw9IiM3MDcwNzAiIHAtaWQ9IjEzNjEiPjwvcGF0aD48L3N2Zz4="
|
||||
alt="刷新">
|
||||
<div class="wg-cap-wrap__ico flex">
|
||||
<img
|
||||
@click="handleCloseEvent"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB0PSIxNjI2NjE0NDM5NDIzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg2NzUiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNNTEyIDIzLjI3MjcyN2MyNjkuOTE3MDkxIDAgNDg4LjcyNzI3MyAyMTguODEwMTgyIDQ4OC43MjcyNzMgNDg4LjcyNzI3M2E0ODYuNjMyNzI3IDQ4Ni42MzI3MjcgMCAwIDEtODQuOTQ1NDU1IDI3NS40MDk0NTUgNDYuNTQ1NDU1IDQ2LjU0NTQ1NSAwIDAgMS03Ni44NDY1NDUtNTIuNTI2NTQ2QTM5My41NDE4MTggMzkzLjU0MTgxOCAwIDAgMCA5MDcuNjM2MzY0IDUxMmMwLTIxOC41MDc2MzYtMTc3LjEyODcyNy0zOTUuNjM2MzY0LTM5NS42MzYzNjQtMzk1LjYzNjM2NFMxMTYuMzYzNjM2IDI5My40OTIzNjQgMTE2LjM2MzYzNiA1MTJzMTc3LjEyODcyNyAzOTUuNjM2MzY0IDM5NS42MzYzNjQgMzk1LjYzNjM2NGEzOTUuMTcwOTA5IDM5NS4xNzA5MDkgMCAwIDAgMTI1LjQ0LTIwLjI5MzgxOSA0Ni41NDU0NTUgNDYuNTQ1NDU1IDAgMCAxIDI5LjQ4NjU0NSA4OC4yOTY3MjhBNDg4LjI2MTgxOCA0ODguMjYxODE4IDAgMCAxIDUxMiAxMDAwLjcyNzI3M0MyNDIuMDgyOTA5IDEwMDAuNzI3MjczIDIzLjI3MjcyNyA3ODEuOTE3MDkxIDIzLjI3MjcyNyA1MTJTMjQyLjA4MjkwOSAyMy4yNzI3MjcgNTEyIDIzLjI3MjcyN3ogbS0xMTUuMiAzMDcuNzEyTDUxMiA0NDYuMTM4MTgybDExNS4yLTExNS4yYTQ2LjU0NTQ1NSA0Ni41NDU0NTUgMCAxIDEgNjUuODE1MjczIDY1Ljg2MTgxOEw1NzcuODYxODE4IDUxMmwxMTUuMiAxMTUuMmE0Ni41NDU0NTUgNDYuNTQ1NDU1IDAgMSAxLTY1Ljg2MTgxOCA2NS44MTUyNzNMNTEyIDU3Ny44NjE4MThsLTExNS4yIDExNS4yYTQ2LjU0NTQ1NSA0Ni41NDU0NTUgMCAxIDEtNjUuODE1MjczLTY1Ljg2MTgxOEw0NDYuMTM4MTgyIDUxMmwtMTE1LjItMTE1LjJhNDYuNTQ1NDU1IDQ2LjU0NTQ1NSAwIDEgMSA2NS44NjE4MTgtNjUuODE1MjczeiIgcC1pZD0iODY3NiIgZmlsbD0iIzcwNzA3MCI+PC9wYXRoPjwvc3ZnPg=="
|
||||
alt="关闭"
|
||||
/>
|
||||
<img
|
||||
@click="handleRefreshEvent"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB0PSIxNjI2NjE0NDk5NjM4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEzNjAiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMTg3LjQ1NiA0MjUuMDI0YTMzNiAzMzYgMCAwIDAgMzY4LjM4NCA0MjAuMjI0IDQ4IDQ4IDAgMCAxIDEyLjU0NCA5NS4xNjggNDMyIDQzMiAwIDAgMS00NzMuNjY0LTU0MC4xNmwtNTcuMjgtMTUuMzZhMTIuOCAxMi44IDAgMCAxLTYuMjcyLTIwLjkyOGwxNTkuMTY4LTE3OS40NTZhMTIuOCAxMi44IDAgMCAxIDIyLjE0NCA1Ljg4OGw0OC4wNjQgMjM1LjA3MmExMi44IDEyLjggMCAwIDEtMTUuODA4IDE0LjkxMmwtNTcuMjgtMTUuMzZ6TTgzNi40OCA1OTkuMDRhMzM2IDMzNiAwIDAgMC0zNjguMzg0LTQyMC4yMjQgNDggNDggMCAxIDEtMTIuNTQ0LTk1LjE2OCA0MzIgNDMyIDAgMCAxIDQ3My42NjQgNTQwLjE2bDU3LjI4IDE1LjM2YTEyLjggMTIuOCAwIDAgMSA2LjI3MiAyMC45MjhsLTE1OS4xNjggMTc5LjQ1NmExMi44IDEyLjggMCAwIDEtMjIuMTQ0LTUuODg4bC00OC4wNjQtMjM1LjA3MmExMi44IDEyLjggMCAwIDEgMTUuODA4LTE0LjkxMmw1Ny4yOCAxNS4zNnoiIGZpbGw9IiM3MDcwNzAiIHAtaWQ9IjEzNjEiPjwvcGF0aD48L3N2Zz4="
|
||||
alt="刷新"
|
||||
/>
|
||||
</div>
|
||||
<div class="wg-cap-wrap__btn">
|
||||
<el-button type="primary" @click="handleConfirmEvent">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CaptchaPlus',
|
||||
name: "CaptchaPlus",
|
||||
mounted() {
|
||||
this.$emit('refresh')
|
||||
this.$emit("refresh");
|
||||
},
|
||||
props: {
|
||||
value: Boolean,
|
||||
width: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
default: "300px",
|
||||
},
|
||||
calcPosType: {
|
||||
type: String,
|
||||
default: 'dom',
|
||||
validator: value => ['dom', 'screen'].includes(value)
|
||||
default: "dom",
|
||||
validator: (value) => ["dom", "screen"].includes(value),
|
||||
},
|
||||
maxDot: {
|
||||
type: Number,
|
||||
default: 5
|
||||
default: 5,
|
||||
// validator: value => value > 10
|
||||
},
|
||||
imageBase64: String,
|
||||
thumbBase64: String
|
||||
thumbBase64: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dots: [],
|
||||
imageBase64Code: '',
|
||||
thumbBase64Code: ''
|
||||
}
|
||||
imageBase64Code: "",
|
||||
thumbBase64Code: "",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.dots = []
|
||||
this.imageBase64Code = ''
|
||||
this.thumbBase64Code = ''
|
||||
this.dots = [];
|
||||
this.imageBase64Code = "";
|
||||
this.thumbBase64Code = "";
|
||||
},
|
||||
imageBase64(val) {
|
||||
this.dots = []
|
||||
this.imageBase64Code = val
|
||||
this.dots = [];
|
||||
this.imageBase64Code = val;
|
||||
},
|
||||
thumbBase64(val) {
|
||||
this.dots = []
|
||||
this.thumbBase64Code = val
|
||||
}
|
||||
this.dots = [];
|
||||
this.thumbBase64Code = val;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @Description: 处理关闭事件
|
||||
*/
|
||||
handleCloseEvent() {
|
||||
this.$emit('close')
|
||||
this.$emit("close");
|
||||
// this.dots = []
|
||||
// this.imageBase64Code = ''
|
||||
// this.thumbBase64Code = ''
|
||||
@ -93,14 +96,14 @@ export default {
|
||||
* @Description: 处理刷新事件
|
||||
*/
|
||||
handleRefreshEvent() {
|
||||
this.dots = []
|
||||
this.$emit('refresh')
|
||||
this.dots = [];
|
||||
this.$emit("refresh");
|
||||
},
|
||||
/**
|
||||
* @Description: 处理确认事件
|
||||
*/
|
||||
handleConfirmEvent() {
|
||||
this.$emit('confirm', this.dots)
|
||||
this.$emit("confirm", this.dots);
|
||||
},
|
||||
/**
|
||||
* @Description: 处理dot
|
||||
@ -108,102 +111,102 @@ export default {
|
||||
*/
|
||||
handleClickPos(ev) {
|
||||
if (this.dots.length >= this.maxDot) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const e = ev || window.event
|
||||
e.preventDefault()
|
||||
const dom = e.currentTarget
|
||||
const e = ev || window.event;
|
||||
e.preventDefault();
|
||||
const dom = e.currentTarget;
|
||||
|
||||
const {domX, domY} = this.getDomXY(dom)
|
||||
const { domX, domY } = this.getDomXY(dom);
|
||||
// ===============================================
|
||||
// @notice 如 getDomXY 不准确可尝试使用 calcLocationLeft 或 calcLocationTop
|
||||
// const domX = this.calcLocationLeft(dom)
|
||||
// const domY = this.calcLocationTop(dom)
|
||||
// ===============================================
|
||||
let mouseX = (navigator.vendor === 'Netscape') ? e.pageX : e.x + document.body.offsetTop
|
||||
let mouseY = (navigator.vendor === 'Netscape') ? e.pageY : e.y + document.body.offsetTop
|
||||
let mouseX = navigator.vendor === "Netscape" ? e.pageX : e.x + document.body.offsetTop;
|
||||
let mouseY = navigator.vendor === "Netscape" ? e.pageY : e.y + document.body.offsetTop;
|
||||
// 兼容移动触摸事件
|
||||
if (e.touches && e.touches.length > 0) {
|
||||
mouseX = e.touches[0].clientX
|
||||
mouseY = e.touches[0].clientY
|
||||
mouseX = e.touches[0].clientX;
|
||||
mouseY = e.touches[0].clientY;
|
||||
} else {
|
||||
mouseX = e.clientX
|
||||
mouseY = e.clientY
|
||||
mouseX = e.clientX;
|
||||
mouseY = e.clientY;
|
||||
}
|
||||
|
||||
// 计算点击的相对位置
|
||||
const xPos = mouseX - domX
|
||||
const yPos = mouseY - domY
|
||||
const xPos = mouseX - domX;
|
||||
const yPos = mouseY - domY;
|
||||
|
||||
// 转整形
|
||||
const xp = parseInt(xPos.toString())
|
||||
const yp = parseInt(yPos.toString())
|
||||
const xp = parseInt(xPos.toString());
|
||||
const yp = parseInt(yPos.toString());
|
||||
|
||||
// 减去点的一半
|
||||
this.dots.push({
|
||||
x: xp - 11,
|
||||
y: yp - 11,
|
||||
index: this.dots.length + 1
|
||||
})
|
||||
return false
|
||||
index: this.dots.length + 1,
|
||||
});
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* @Description: 找到元素的屏幕位置
|
||||
* @param el
|
||||
*/
|
||||
calcLocationLeft(el) {
|
||||
let tmp = el.offsetLeft
|
||||
let val = el.offsetParent
|
||||
let tmp = el.offsetLeft;
|
||||
let val = el.offsetParent;
|
||||
while (val != null) {
|
||||
tmp += val.offsetLeft
|
||||
val = val.offsetParent
|
||||
tmp += val.offsetLeft;
|
||||
val = val.offsetParent;
|
||||
}
|
||||
return tmp
|
||||
return tmp;
|
||||
},
|
||||
/**
|
||||
* @Description: 找到元素的屏幕位置
|
||||
* @param el
|
||||
*/
|
||||
calcLocationTop(el) {
|
||||
let tmp = el.offsetTop
|
||||
let val = el.offsetParent
|
||||
let tmp = el.offsetTop;
|
||||
let val = el.offsetParent;
|
||||
while (val != null) {
|
||||
tmp += val.offsetTop
|
||||
val = val.offsetParent
|
||||
tmp += val.offsetTop;
|
||||
val = val.offsetParent;
|
||||
}
|
||||
return tmp
|
||||
return tmp;
|
||||
},
|
||||
/**
|
||||
* @Description: 找到元素的屏幕位置
|
||||
* @param dom
|
||||
*/
|
||||
getDomXY(dom) {
|
||||
let x = 0
|
||||
let y = 0
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
if (dom.getBoundingClientRect) {
|
||||
let box = dom.getBoundingClientRect();
|
||||
let D = document.documentElement;
|
||||
x = box.left + Math.max(D.scrollLeft, document.body.scrollLeft) - D.clientLeft;
|
||||
y = box.top + Math.max(D.scrollTop, document.body.scrollTop) - D.clientTop
|
||||
y = box.top + Math.max(D.scrollTop, document.body.scrollTop) - D.clientTop;
|
||||
} else {
|
||||
while (dom !== document.body) {
|
||||
x += dom.offsetLeft
|
||||
y += dom.offsetTop
|
||||
dom = dom.offsetParent
|
||||
x += dom.offsetLeft;
|
||||
y += dom.offsetTop;
|
||||
dom = dom.offsetParent;
|
||||
}
|
||||
}
|
||||
return {
|
||||
domX: x,
|
||||
domY: y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
domY: y,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.wg-cap-wrap {
|
||||
background: #ffffff;
|
||||
background: var(--el-bg-color);
|
||||
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
@ -219,14 +222,7 @@ export default {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
|
@ -1,32 +1,9 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="login-dialog"
|
||||
v-model="showDialog"
|
||||
:close-on-click-modal="true"
|
||||
:show-close="false"
|
||||
:before-close="close"
|
||||
>
|
||||
<template #header="{ titleId, titleClass }">
|
||||
<div class="header">
|
||||
<div class="title" v-if="login">用户登录-</div>
|
||||
<div class="title" v-else>用户注册</div>
|
||||
<div class="close-icon">
|
||||
<el-icon @click="close">
|
||||
<Close />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="login-dialog w-full p-8">
|
||||
<div class="login-box" v-if="login">
|
||||
<el-form :model="data" label-width="120px" class="form">
|
||||
<el-form :model="data" class="form">
|
||||
<div class="block">
|
||||
<el-input
|
||||
placeholder="账号"
|
||||
size="large"
|
||||
v-model="data.username"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="账号" size="large" v-model="data.username" autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Iphone />
|
||||
@ -36,14 +13,7 @@
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<el-input
|
||||
placeholder="请输入密码(8-16位)"
|
||||
maxlength="16"
|
||||
size="large"
|
||||
v-model="data.password"
|
||||
show-password
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="请输入密码(8-16位)" maxlength="16" size="large" v-model="data.password" show-password autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Lock />
|
||||
@ -54,67 +24,42 @@
|
||||
|
||||
<el-row class="btn-row" :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-button
|
||||
class="login-btn"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="submitLogin"
|
||||
>登录</el-button
|
||||
>
|
||||
<el-button class="login-btn" type="primary" size="large" @click="submitLogin">登 录</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<div class="reg">
|
||||
还没有账号?
|
||||
<el-button
|
||||
type="primary"
|
||||
class="forget"
|
||||
size="small"
|
||||
@click="login = false"
|
||||
>注册</el-button
|
||||
>
|
||||
<div class="w-full">
|
||||
<div class="text flex justify-center items-center pt-3 text-sm">
|
||||
还没有账号?
|
||||
<el-button size="small" @click="login = false">注册</el-button>
|
||||
|
||||
<el-button
|
||||
type="info"
|
||||
class="forget"
|
||||
size="small"
|
||||
@click="showResetPass = true"
|
||||
>忘记密码?</el-button
|
||||
>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<div class="c-login" v-if="wechatLoginURL !== ''">
|
||||
<div class="text">其他登录方式:</div>
|
||||
<div class="login-type">
|
||||
<a
|
||||
class="wechat-login"
|
||||
:href="wechatLoginURL"
|
||||
@click="setRoute(router.currentRoute.value.path)"
|
||||
><i class="iconfont icon-wechat"></i
|
||||
></a>
|
||||
<el-button type="info" class="forget" size="small" @click="showResetPass = true">忘记密码?</el-button>
|
||||
</div>
|
||||
<div v-if="wechatLoginURL !== ''">
|
||||
<el-divider>
|
||||
<div class="text-center">其他登录方式</div>
|
||||
</el-divider>
|
||||
<div class="c-login flex justify-center">
|
||||
<!-- <div class="login-type mr-2">
|
||||
<a class="wechat-login" :href="wechatLoginURL" @click="setRoute(router.currentRoute.value.path)"><i class="iconfont icon-wechat"></i></a>
|
||||
</div> -->
|
||||
<div class="p-2 w-full">
|
||||
<el-button type="success" class="w-full" size="large" :href="wechatLoginURL" @click="setRoute(router.currentRoute.value.path)"
|
||||
><i class="iconfont icon-wechat mr-2"></i> 微信登录
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="register-box" v-else>
|
||||
<div class="register-box w-full" 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.mobile"
|
||||
maxlength="11"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="手机号码" size="large" v-model="data.mobile" maxlength="11" autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Iphone />
|
||||
@ -125,13 +70,7 @@
|
||||
<div class="block">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-input
|
||||
placeholder="验证码"
|
||||
size="large"
|
||||
maxlength="30"
|
||||
v-model="data.code"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="验证码" size="large" maxlength="30" v-model="data.code" autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Checked />
|
||||
@ -140,23 +79,14 @@
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<send-msg
|
||||
size="large"
|
||||
:receiver="data.mobile"
|
||||
type="mobile"
|
||||
/>
|
||||
<send-msg size="large" :receiver="data.mobile" type="mobile" />
|
||||
</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.email"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="邮箱地址" size="large" v-model="data.email" autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Message />
|
||||
@ -167,13 +97,7 @@
|
||||
<div class="block">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-input
|
||||
placeholder="验证码"
|
||||
size="large"
|
||||
maxlength="30"
|
||||
v-model="data.code"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="验证码" size="large" maxlength="30" v-model="data.code" autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Checked />
|
||||
@ -189,12 +113,7 @@
|
||||
</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"
|
||||
>
|
||||
<el-input placeholder="用户名" size="large" v-model="data.username" autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Iphone />
|
||||
@ -206,14 +125,7 @@
|
||||
</el-tabs>
|
||||
|
||||
<div class="block">
|
||||
<el-input
|
||||
placeholder="请输入密码(8-16位)"
|
||||
maxlength="16"
|
||||
size="large"
|
||||
v-model="data.password"
|
||||
show-password
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="请输入密码(8-16位)" maxlength="16" size="large" v-model="data.password" show-password autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Lock />
|
||||
@ -223,14 +135,7 @@
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<el-input
|
||||
placeholder="重复密码(8-16位)"
|
||||
size="large"
|
||||
maxlength="16"
|
||||
v-model="data.repass"
|
||||
show-password
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="重复密码(8-16位)" size="large" maxlength="16" v-model="data.repass" show-password autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Lock />
|
||||
@ -240,12 +145,7 @@
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<el-input
|
||||
placeholder="邀请码(可选)"
|
||||
size="large"
|
||||
v-model="data.invite_code"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-input placeholder="邀请码(可选)" size="large" v-model="data.invite_code" autocomplete="off">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Message />
|
||||
@ -254,23 +154,14 @@
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div class="w-full">
|
||||
<el-button class="login-btn w-full" type="primary" size="large" @click="submitRegister">注 册</el-button>
|
||||
</div>
|
||||
|
||||
<div class="text text-sm flex justify-center items-center w-full pt-3">
|
||||
已有账号?
|
||||
<el-button size="small" @click="login = true">登录</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div class="tip-result" v-else>
|
||||
@ -291,11 +182,10 @@
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<captcha v-if="enableVerify" @success="submit" ref="captchaRef" />
|
||||
|
||||
<reset-pass @hide="showResetPass = false" :show="showResetPass" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -316,7 +206,7 @@ import { useSharedStore } from "@/store/sharedata";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
show: Boolean
|
||||
show: Boolean,
|
||||
});
|
||||
const showDialog = ref(false);
|
||||
watch(
|
||||
@ -334,7 +224,7 @@ const data = ref({
|
||||
email: "",
|
||||
repass: "",
|
||||
code: "",
|
||||
invite_code: ""
|
||||
invite_code: "",
|
||||
});
|
||||
const enableMobile = ref(false);
|
||||
const enableEmail = ref(false);
|
||||
@ -351,8 +241,6 @@ const enableVerify = ref(false);
|
||||
const showResetPass = ref(false);
|
||||
const router = useRouter();
|
||||
const store = useSharedStore();
|
||||
// 是否需要验证码,输入一次密码错之后就要验证码
|
||||
const needVerify = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
const returnURL = `${location.protocol}//${location.host}/login/callback?action=login`;
|
||||
@ -410,7 +298,7 @@ const submitLogin = () => {
|
||||
if (data.value.password === "") {
|
||||
return ElMessage.error("请输入密码");
|
||||
}
|
||||
if (enableVerify.value && needVerify.value) {
|
||||
if (enableVerify.value) {
|
||||
captchaRef.value.loadCaptcha();
|
||||
action.value = "login";
|
||||
} else {
|
||||
@ -429,11 +317,9 @@ const doLogin = (verifyData) => {
|
||||
ElMessage.success("登录成功!");
|
||||
emits("hide");
|
||||
emits("success");
|
||||
needVerify.value = false;
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("登录失败," + e.message);
|
||||
needVerify.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
@ -458,10 +344,7 @@ const submitRegister = () => {
|
||||
return ElMessage.error("两次输入密码不一致");
|
||||
}
|
||||
|
||||
if (
|
||||
(activeName.value === "mobile" || activeName.value === "email") &&
|
||||
data.value.code === ""
|
||||
) {
|
||||
if ((activeName.value === "mobile" || activeName.value === "email") && data.value.code === "") {
|
||||
return ElMessage.error("请输入验证码");
|
||||
}
|
||||
if (enableVerify.value && activeName.value === "username") {
|
||||
@ -486,52 +369,26 @@ const doRegister = (verifyData) => {
|
||||
emits("hide");
|
||||
emits("success");
|
||||
},
|
||||
duration: 1000
|
||||
duration: 1000,
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("注册失败," + e.message);
|
||||
});
|
||||
};
|
||||
|
||||
const close = function () {
|
||||
emits("hide", false);
|
||||
login.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.login-dialog {
|
||||
border-radius 10px
|
||||
max-width 600px
|
||||
|
||||
.header {
|
||||
position relative
|
||||
|
||||
.title {
|
||||
padding 0
|
||||
font-size 18px
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
cursor pointer
|
||||
position absolute
|
||||
right 0
|
||||
top 0
|
||||
font-weight normal
|
||||
font-size 20px
|
||||
|
||||
&:hover {
|
||||
color #20a0ff
|
||||
}
|
||||
}
|
||||
.el-tabs__nav {
|
||||
display flex
|
||||
width 100%
|
||||
justify-content space-between
|
||||
}
|
||||
|
||||
|
||||
.el-dialog__body {
|
||||
padding 10px 20px 20px 20px
|
||||
}
|
||||
|
||||
.form {
|
||||
.block {
|
||||
margin-bottom 10px
|
||||
@ -541,6 +398,7 @@ const close = function () {
|
||||
display flex
|
||||
|
||||
.login-btn {
|
||||
font-size 16px
|
||||
width 100%
|
||||
}
|
||||
|
||||
@ -566,7 +424,6 @@ const close = function () {
|
||||
align-items: center;
|
||||
}
|
||||
.login-type {
|
||||
padding 15px
|
||||
display flex
|
||||
justify-content center
|
||||
|
||||
@ -582,14 +439,8 @@ const close = function () {
|
||||
}
|
||||
}
|
||||
|
||||
.reg {
|
||||
height 50px
|
||||
display flex
|
||||
align-items center
|
||||
|
||||
.el-button {
|
||||
margin-left 10px
|
||||
}
|
||||
.text {
|
||||
color var(--el-text-color-primary)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,6 @@
|
||||
<template>
|
||||
<div class="reset-pass">
|
||||
<el-dialog
|
||||
v-model="showDialog"
|
||||
:close-on-click-modal="true"
|
||||
width="540px"
|
||||
:before-close="close"
|
||||
:title="title"
|
||||
class="reset-pass-dialog"
|
||||
>
|
||||
<el-dialog v-model="showDialog" :close-on-click-modal="true" width="500px" :before-close="close" :title="title" class="reset-pass-dialog">
|
||||
<div class="form">
|
||||
<el-form :model="form" label-width="80px" label-position="left">
|
||||
<el-tabs v-model="form.type" class="demo-tabs">
|
||||
@ -16,14 +9,10 @@
|
||||
<el-input v-model="form.mobile" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码">
|
||||
<el-row class="code-row">
|
||||
<el-col :span="16">
|
||||
<el-input v-model="form.code" maxlength="6" />
|
||||
</el-col>
|
||||
<el-col :span="8" class="send-button">
|
||||
<send-msg size="" :receiver="form.mobile" type="mobile" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="flex">
|
||||
<el-input v-model="form.code" maxlength="6" class="mr-2 w-1/2" />
|
||||
<send-msg size="" :receiver="form.mobile" type="mobile" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="邮箱验证" name="email">
|
||||
@ -31,14 +20,10 @@
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码">
|
||||
<el-row class="code-row">
|
||||
<el-col :span="16">
|
||||
<el-input v-model="form.code" maxlength="6" />
|
||||
</el-col>
|
||||
<el-col :span="8" class="send-button">
|
||||
<send-msg size="" :receiver="form.email" type="email" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="flex">
|
||||
<el-input v-model="form.code" maxlength="6" class="mr-2 w-1/2" />
|
||||
<send-msg size="" :receiver="form.email" type="email" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@ -70,7 +55,7 @@ import { validateEmail, validateMobile } from "@/utils/validate";
|
||||
|
||||
const props = defineProps({
|
||||
show: Boolean,
|
||||
mobile: String
|
||||
mobile: String,
|
||||
});
|
||||
|
||||
const showDialog = computed(() => {
|
||||
@ -84,7 +69,7 @@ const form = ref({
|
||||
type: "mobile",
|
||||
code: "",
|
||||
password: "",
|
||||
repass: ""
|
||||
repass: "",
|
||||
});
|
||||
|
||||
const emits = defineEmits(["hide"]);
|
||||
@ -105,7 +90,7 @@ const save = () => {
|
||||
ElMessage.success({
|
||||
message: "重置密码成功",
|
||||
duration: 1000,
|
||||
onClose: () => emits("hide", false)
|
||||
onClose: () => emits("hide", false),
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
|
172
web/src/components/SdTaskView.vue
Normal file
172
web/src/components/SdTaskView.vue
Normal file
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<el-dialog v-model="show" :fullscreen="true" @close="close" style="--el-dialog-border-radius: 0px">
|
||||
<template #header>
|
||||
<div class="header">
|
||||
<h3 style="color: var(--text-theme-color)">绘画任务详情</h3>
|
||||
</div>
|
||||
</template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<div class="img-container">
|
||||
<el-image :src="item['img_url']" fit="contain">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">正在加载图片</div>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon>
|
||||
<i class="iconfont icon-image"></i>
|
||||
</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">
|
||||
<i class="iconfont icon-copy"></i>
|
||||
</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">
|
||||
<i class="iconfont icon-copy"></i>
|
||||
</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="drawSame(item)">画一张同款的</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import Clipboard from "clipboard";
|
||||
import { showMessageOK, showMessageError } from "@/utils/dialog";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
data: Object,
|
||||
});
|
||||
|
||||
const item = ref(props.data);
|
||||
const show = ref(props.modelValue);
|
||||
const emit = defineEmits(["drawSame", "close"]);
|
||||
|
||||
const clipboard = ref(null);
|
||||
onMounted(() => {
|
||||
clipboard.value = new Clipboard(".copy-prompt-wall");
|
||||
clipboard.value.on("success", () => {
|
||||
showMessageOK("复制成功!");
|
||||
});
|
||||
|
||||
clipboard.value.on("error", () => {
|
||||
showMessageError("复制失败!");
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
show.value = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(newValue) => {
|
||||
item.value = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
const drawSame = (item) => {
|
||||
emit("drawSame", item);
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
emit("close");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped></style>
|
@ -1,11 +1,6 @@
|
||||
<template>
|
||||
<el-container class="send-verify-code">
|
||||
<el-button
|
||||
type="success"
|
||||
:size="props.size"
|
||||
:disabled="!canSend"
|
||||
@click="sendMsg"
|
||||
>
|
||||
<el-button type="success" :size="props.size" :disabled="!canSend" @click="sendMsg">
|
||||
{{ btnText }}
|
||||
</el-button>
|
||||
|
||||
@ -15,12 +10,12 @@
|
||||
|
||||
<script setup>
|
||||
// 发送短信验证码组件
|
||||
import {ref} from "vue";
|
||||
import {validateEmail, validateMobile} from "@/utils/validate";
|
||||
import {httpPost} from "@/utils/http";
|
||||
import {showMessageError, showMessageOK} from "@/utils/dialog";
|
||||
import { ref } from "vue";
|
||||
import { validateEmail, validateMobile } from "@/utils/validate";
|
||||
import { httpPost } from "@/utils/http";
|
||||
import { ElMessage } from "element-plus";
|
||||
import Captcha from "@/components/Captcha.vue";
|
||||
import {getSystemInfo} from "@/store/cache";
|
||||
import { getSystemInfo } from "@/store/cache";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
@ -28,8 +23,8 @@ const props = defineProps({
|
||||
size: String,
|
||||
type: {
|
||||
type: String,
|
||||
default: "mobile"
|
||||
}
|
||||
default: "mobile",
|
||||
},
|
||||
});
|
||||
const btnText = ref("发送验证码");
|
||||
const canSend = ref(true);
|
||||
@ -42,10 +37,10 @@ getSystemInfo().then((res) => {
|
||||
|
||||
const sendMsg = () => {
|
||||
if (!validateMobile(props.receiver) && props.type === "mobile") {
|
||||
return showMessageError("请输入合法的手机号");
|
||||
return ElMessage.error("请输入合法的手机号");
|
||||
}
|
||||
if (!validateEmail(props.receiver) && props.type === "email") {
|
||||
return showMessageError("请输入合法的邮箱地址");
|
||||
return ElMessage.error("请输入合法的邮箱地址");
|
||||
}
|
||||
|
||||
if (enableVerify.value) {
|
||||
@ -65,10 +60,10 @@ const doSendMsg = (data) => {
|
||||
receiver: props.receiver,
|
||||
key: data.key,
|
||||
dots: data.dots,
|
||||
x: data.x
|
||||
x: data.x,
|
||||
})
|
||||
.then(() => {
|
||||
showMessageOK("验证码发送成功");
|
||||
ElMessage.success("验证码发送成功");
|
||||
let time = 60;
|
||||
btnText.value = time;
|
||||
const handler = setInterval(() => {
|
||||
@ -84,7 +79,7 @@ const doSendMsg = (data) => {
|
||||
})
|
||||
.catch((e) => {
|
||||
canSend.value = true;
|
||||
showMessageError("验证码发送失败:" + e.message);
|
||||
ElMessage.error("验证码发送失败:" + e.message);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
@ -1,169 +1,177 @@
|
||||
<template>
|
||||
<div class="slide-captcha">
|
||||
<div class="bg-img">
|
||||
<el-image :src="backgroundImg" />
|
||||
<div :class="verifyMsgClass" v-if="checked !== 0">
|
||||
<span v-if="checked ===1">{{time}}s</span>
|
||||
{{verifyMsg}}
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="slide-captcha">
|
||||
<div class="bg-img">
|
||||
<el-image :src="backgroundImg" />
|
||||
<div :class="verifyMsgClass" v-if="checked !== 0">
|
||||
<span v-if="checked === 1">{{ time }}s</span>
|
||||
{{ verifyMsg }}
|
||||
</div>
|
||||
<div class="refresh" @click="emits('refresh')">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
</div>
|
||||
<span class="block">
|
||||
<el-image :src="blockImg" :style="{ left: blockLeft + 'px' }" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="refresh" @click="emits('refresh')">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
</div>
|
||||
<span class="block">
|
||||
<el-image :src="blockImg" :style="{left: blockLeft+'px'}" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="verify">
|
||||
<div class="verify-bar-area">
|
||||
<span class="verify-msg">{{verifyText}}</span>
|
||||
<div class="verify">
|
||||
<div class="verify-bar-area">
|
||||
<span class="verify-msg">{{ verifyText }}</span>
|
||||
|
||||
<div :class="leftBarClass" :style="{width: leftBarWidth+'px'}">
|
||||
<div :class="blockClass" id="dragBlock"
|
||||
:style="{left: blockLeft+'px'}">
|
||||
<el-icon v-if="checked === 0"><ArrowRightBold /></el-icon>
|
||||
<el-icon v-if="checked === 1"><CircleCheckFilled /></el-icon>
|
||||
<el-icon v-if="checked === 2"><CircleCloseFilled /></el-icon>
|
||||
<div :class="leftBarClass" :style="{ width: leftBarWidth + 'px' }">
|
||||
<div :class="blockClass" id="dragBlock" :style="{ left: blockLeft + 'px' }">
|
||||
<el-icon v-if="checked === 0"><ArrowRightBold /></el-icon>
|
||||
<el-icon v-if="checked === 1"><CircleCheckFilled /></el-icon>
|
||||
<el-icon v-if="checked === 2"><CircleCloseFilled /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// eslint-disable-next-line no-undef
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {ArrowRightBold, CircleCheckFilled, CircleCloseFilled, Refresh} from "@element-plus/icons-vue";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { ArrowRightBold, CircleCheckFilled, CircleCloseFilled, Refresh } from "@element-plus/icons-vue";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
bgImg: String,
|
||||
bkImg: String,
|
||||
result: Number,
|
||||
})
|
||||
|
||||
const verifyText = ref('向右滑动完成验证')
|
||||
const verifyMsg = ref('')
|
||||
const verifyMsgClass = ref("verify-text success")
|
||||
const blockClass = ref('verify-move-block')
|
||||
const leftBarClass = ref('verify-left-bar')
|
||||
const backgroundImg = ref('')
|
||||
const blockImg = ref('')
|
||||
const leftBarWidth = ref(0)
|
||||
const blockLeft = ref(0)
|
||||
const checked = ref(0)
|
||||
const time = ref('')
|
||||
|
||||
watch(() => props.bgImg, (newVal) => {
|
||||
backgroundImg.value = newVal;
|
||||
});
|
||||
watch(() => props.bkImg, (newVal) => {
|
||||
blockImg.value = newVal;
|
||||
});
|
||||
watch(() => props.result, (newVal) => {
|
||||
checked.value = newVal;
|
||||
if (newVal === 1) {
|
||||
verifyMsgClass.value = "verify-text success"
|
||||
blockClass.value = 'verify-move-block success'
|
||||
leftBarClass.value = 'verify-left-bar success'
|
||||
verifyMsg.value = '验证成功'
|
||||
setTimeout(() => emits('hide'), 1000)
|
||||
} else if (newVal ===2) {
|
||||
verifyMsgClass.value = "verify-text error"
|
||||
blockClass.value = 'verify-move-block error'
|
||||
leftBarClass.value = 'verify-left-bar error'
|
||||
verifyMsg.value = '验证失败'
|
||||
setTimeout(() => {
|
||||
reset()
|
||||
emits('refresh')
|
||||
}, 1000)
|
||||
} else {
|
||||
reset()
|
||||
|
||||
const verifyText = ref("向右滑动完成验证");
|
||||
const verifyMsg = ref("");
|
||||
const verifyMsgClass = ref("verify-text success");
|
||||
const blockClass = ref("verify-move-block");
|
||||
const leftBarClass = ref("verify-left-bar");
|
||||
const backgroundImg = ref("");
|
||||
const blockImg = ref("");
|
||||
const leftBarWidth = ref(0);
|
||||
const blockLeft = ref(0);
|
||||
const checked = ref(0);
|
||||
const time = ref("");
|
||||
|
||||
watch(
|
||||
() => props.bgImg,
|
||||
(newVal) => {
|
||||
backgroundImg.value = newVal;
|
||||
}
|
||||
});
|
||||
|
||||
);
|
||||
watch(
|
||||
() => props.bkImg,
|
||||
(newVal) => {
|
||||
blockImg.value = newVal;
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.result,
|
||||
(newVal) => {
|
||||
checked.value = newVal;
|
||||
if (newVal === 1) {
|
||||
verifyMsgClass.value = "verify-text success";
|
||||
blockClass.value = "verify-move-block success";
|
||||
leftBarClass.value = "verify-left-bar success";
|
||||
verifyMsg.value = "验证成功";
|
||||
setTimeout(() => emits("hide"), 1000);
|
||||
} else if (newVal === 2) {
|
||||
verifyMsgClass.value = "verify-text error";
|
||||
blockClass.value = "verify-move-block error";
|
||||
leftBarClass.value = "verify-left-bar error";
|
||||
verifyMsg.value = "验证失败";
|
||||
setTimeout(() => {
|
||||
reset();
|
||||
emits("refresh");
|
||||
}, 1000);
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const emits = defineEmits(['confirm','refresh','hide']);
|
||||
const emits = defineEmits(["confirm", "refresh", "hide"]);
|
||||
|
||||
let offsetX = 0, isDragging = false
|
||||
let start = 0
|
||||
let offsetX = 0,
|
||||
isDragging = false;
|
||||
let start = 0;
|
||||
onMounted(() => {
|
||||
const dragBlock = document.getElementById('dragBlock');
|
||||
dragBlock.addEventListener('mousedown', (evt) => {
|
||||
blockClass.value = 'verify-move-block active'
|
||||
leftBarClass.value = 'verify-left-bar active'
|
||||
leftBarWidth.value = 32
|
||||
isDragging = true
|
||||
verifyText.value = ""
|
||||
offsetX = evt.clientX
|
||||
start = new Date().getTime()
|
||||
const dragBlock = document.getElementById("dragBlock");
|
||||
dragBlock.addEventListener("mousedown", (evt) => {
|
||||
blockClass.value = "verify-move-block active";
|
||||
leftBarClass.value = "verify-left-bar active";
|
||||
leftBarWidth.value = 32;
|
||||
isDragging = true;
|
||||
verifyText.value = "";
|
||||
offsetX = evt.clientX;
|
||||
start = new Date().getTime();
|
||||
evt.preventDefault();
|
||||
})
|
||||
});
|
||||
|
||||
document.body.addEventListener('mousemove',(evt) => {
|
||||
document.body.addEventListener("mousemove", (evt) => {
|
||||
if (!isDragging) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const x = Math.max(evt.clientX - offsetX, 0)
|
||||
const x = Math.max(evt.clientX - offsetX, 0);
|
||||
blockLeft.value = x;
|
||||
leftBarWidth.value = x + 32
|
||||
})
|
||||
leftBarWidth.value = x + 32;
|
||||
});
|
||||
|
||||
document.body.addEventListener('mouseup', () => {
|
||||
document.body.addEventListener("mouseup", () => {
|
||||
if (!isDragging) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
time.value = ((new Date().getTime() - start)/1000).toFixed(2)
|
||||
isDragging = false
|
||||
emits('confirm', Math.floor(blockLeft.value))
|
||||
})
|
||||
time.value = ((new Date().getTime() - start) / 1000).toFixed(2);
|
||||
isDragging = false;
|
||||
emits("confirm", Math.floor(blockLeft.value));
|
||||
});
|
||||
|
||||
// 触摸事件
|
||||
dragBlock.addEventListener('touchstart', function (e) {
|
||||
dragBlock.addEventListener("touchstart", function (e) {
|
||||
isDragging = true;
|
||||
blockClass.value = 'verify-move-block active'
|
||||
leftBarClass.value = 'verify-left-bar active'
|
||||
leftBarWidth.value = 32
|
||||
isDragging = true
|
||||
verifyText.value = ""
|
||||
blockClass.value = "verify-move-block active";
|
||||
leftBarClass.value = "verify-left-bar active";
|
||||
leftBarWidth.value = 32;
|
||||
isDragging = true;
|
||||
verifyText.value = "";
|
||||
offsetX = e.touches[0].clientX - dragBlock.getBoundingClientRect().left;
|
||||
start = new Date().getTime()
|
||||
start = new Date().getTime();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('touchmove', function (e) {
|
||||
document.addEventListener("touchmove", function (e) {
|
||||
if (!isDragging) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
const x = Math.max(e.touches[0].clientX - offsetX, 0)
|
||||
const x = Math.max(e.touches[0].clientX - offsetX, 0);
|
||||
blockLeft.value = x;
|
||||
leftBarWidth.value = x + 32
|
||||
leftBarWidth.value = x + 32;
|
||||
});
|
||||
|
||||
document.addEventListener('touchend', function () {
|
||||
document.addEventListener("touchend", function () {
|
||||
if (!isDragging) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
time.value = ((new Date().getTime() - start)/1000).toFixed(2)
|
||||
isDragging = false
|
||||
emits('confirm', Math.floor(blockLeft.value))
|
||||
time.value = ((new Date().getTime() - start) / 1000).toFixed(2);
|
||||
isDragging = false;
|
||||
emits("confirm", Math.floor(blockLeft.value));
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
// 重置验证码
|
||||
const reset = () => {
|
||||
blockClass.value = 'verify-move-block'
|
||||
leftBarClass.value = 'verify-left-bar'
|
||||
leftBarWidth.value = 0
|
||||
blockLeft.value = 0
|
||||
checked.value = 0
|
||||
verifyText.value = "向右滑动完成验证"
|
||||
}
|
||||
blockClass.value = "verify-move-block";
|
||||
leftBarClass.value = "verify-left-bar";
|
||||
leftBarWidth.value = 0;
|
||||
blockLeft.value = 0;
|
||||
checked.value = 0;
|
||||
verifyText.value = "向右滑动完成验证";
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
@ -177,6 +185,7 @@ const reset = () => {
|
||||
}
|
||||
|
||||
.slide-captcha {
|
||||
width 310px
|
||||
* {
|
||||
margin 0
|
||||
padding 0
|
||||
@ -294,4 +303,4 @@ const reset = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div :class="'sidebar ' + theme">
|
||||
<a class="logo w-full" href="/" target="_blank">
|
||||
<el-image :src="logo" />
|
||||
<a class="logo w-full flex items-center" href="/" target="_blank">
|
||||
<img :src="logo" style="height: 36px" />
|
||||
<span class="text" v-show="!sidebar.collapse">{{ title }}</span>
|
||||
</a>
|
||||
|
||||
|
@ -5,14 +5,14 @@
|
||||
// * @Author yangjian102621@163.com
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import {createRouter, createWebHistory} from "vue-router";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
name: "Index",
|
||||
path: "/",
|
||||
meta: { title: "首页" },
|
||||
component: () => import("@/views/Index.vue")
|
||||
component: () => import("@/views/Index.vue"),
|
||||
},
|
||||
{
|
||||
name: "home",
|
||||
@ -24,136 +24,136 @@ const routes = [
|
||||
name: "chat",
|
||||
path: "/chat",
|
||||
meta: { title: "创作中心" },
|
||||
component: () => import("@/views/ChatPlus.vue")
|
||||
component: () => import("@/views/ChatPlus.vue"),
|
||||
},
|
||||
{
|
||||
name: "chat-id",
|
||||
path: "/chat/:id",
|
||||
meta: { title: "创作中心" },
|
||||
component: () => import("@/views/ChatPlus.vue")
|
||||
component: () => import("@/views/ChatPlus.vue"),
|
||||
},
|
||||
{
|
||||
name: "image-mj",
|
||||
path: "/mj",
|
||||
meta: { title: "MidJourney 绘画中心" },
|
||||
component: () => import("@/views/ImageMj.vue")
|
||||
component: () => import("@/views/ImageMj.vue"),
|
||||
},
|
||||
{
|
||||
name: "image-sd",
|
||||
path: "/sd",
|
||||
meta: { title: "stable diffusion 绘画中心" },
|
||||
component: () => import("@/views/ImageSd.vue")
|
||||
component: () => import("@/views/ImageSd.vue"),
|
||||
},
|
||||
{
|
||||
name: "member",
|
||||
path: "/member",
|
||||
meta: { title: "会员充值中心" },
|
||||
component: () => import("@/views/Member.vue")
|
||||
component: () => import("@/views/Member.vue"),
|
||||
},
|
||||
{
|
||||
name: "chat-app",
|
||||
path: "/apps",
|
||||
meta: { title: "应用中心" },
|
||||
component: () => import("@/views/ChatApps.vue")
|
||||
component: () => import("@/views/ChatApps.vue"),
|
||||
},
|
||||
{
|
||||
name: "images",
|
||||
path: "/images-wall",
|
||||
meta: { title: "作品展示" },
|
||||
component: () => import("@/views/ImagesWall.vue")
|
||||
component: () => import("@/views/ImagesWall.vue"),
|
||||
},
|
||||
{
|
||||
name: "user-invitation",
|
||||
path: "/invite",
|
||||
meta: { title: "推广计划" },
|
||||
component: () => import("@/views/Invitation.vue")
|
||||
component: () => import("@/views/Invitation.vue"),
|
||||
},
|
||||
{
|
||||
name: "powerLog",
|
||||
path: "/powerLog",
|
||||
meta: { title: "消费日志" },
|
||||
component: () => import("@/views/PowerLog.vue")
|
||||
component: () => import("@/views/PowerLog.vue"),
|
||||
},
|
||||
{
|
||||
name: "xmind",
|
||||
path: "/xmind",
|
||||
meta: { title: "思维导图" },
|
||||
component: () => import("@/views/MarkMap.vue")
|
||||
component: () => import("@/views/MarkMap.vue"),
|
||||
},
|
||||
{
|
||||
name: "dalle",
|
||||
path: "/dalle",
|
||||
meta: { title: "DALLE-3" },
|
||||
component: () => import("@/views/Dalle.vue")
|
||||
component: () => import("@/views/Dalle.vue"),
|
||||
},
|
||||
{
|
||||
name: "suno",
|
||||
path: "/suno",
|
||||
meta: { title: "Suno音乐创作" },
|
||||
component: () => import("@/views/Suno.vue")
|
||||
component: () => import("@/views/Suno.vue"),
|
||||
},
|
||||
{
|
||||
name: "ExternalLink",
|
||||
path: "/external",
|
||||
component: () => import("@/views/ExternalPage.vue")
|
||||
component: () => import("@/views/ExternalPage.vue"),
|
||||
},
|
||||
{
|
||||
name: "song",
|
||||
path: "/song/:id",
|
||||
meta: { title: "Suno音乐播放" },
|
||||
component: () => import("@/views/Song.vue")
|
||||
component: () => import("@/views/Song.vue"),
|
||||
},
|
||||
{
|
||||
name: "luma",
|
||||
path: "/luma",
|
||||
meta: { title: "Luma视频创作" },
|
||||
component: () => import("@/views/Luma.vue")
|
||||
}
|
||||
]
|
||||
component: () => import("@/views/Luma.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "chat-export",
|
||||
path: "/chat/export",
|
||||
meta: { title: "导出会话记录" },
|
||||
component: () => import("@/views/ChatExport.vue")
|
||||
component: () => import("@/views/ChatExport.vue"),
|
||||
},
|
||||
{
|
||||
name: "login",
|
||||
path: "/login",
|
||||
meta: { title: "用户登录" },
|
||||
component: () => import("@/views/Login.vue")
|
||||
component: () => import("@/views/Login.vue"),
|
||||
},
|
||||
|
||||
{
|
||||
name: "login-callback",
|
||||
path: "/login/callback",
|
||||
meta: { title: "用户登录" },
|
||||
component: () => import("@/views/LoginCallback.vue")
|
||||
component: () => import("@/views/LoginCallback.vue"),
|
||||
},
|
||||
{
|
||||
name: "register",
|
||||
path: "/register",
|
||||
|
||||
meta: { title: "用户注册" },
|
||||
component: () => import("@/views/Register.vue")
|
||||
component: () => import("@/views/Register.vue"),
|
||||
},
|
||||
{
|
||||
name: "resetpassword",
|
||||
path: "/resetpassword",
|
||||
meta: { title: "重置密码" },
|
||||
component: () => import("@/views/Resetpassword.vue")
|
||||
component: () => import("@/views/Resetpassword.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/login",
|
||||
name: "admin-login",
|
||||
meta: { title: "控制台登录" },
|
||||
component: () => import("@/views/admin/Login.vue")
|
||||
component: () => import("@/views/admin/Login.vue"),
|
||||
},
|
||||
{
|
||||
path: "/payReturn",
|
||||
name: "pay-return",
|
||||
meta: { title: "支付回调" },
|
||||
component: () => import("@/views/PayReturn.vue")
|
||||
component: () => import("@/views/PayReturn.vue"),
|
||||
},
|
||||
{
|
||||
name: "admin",
|
||||
@ -166,118 +166,112 @@ const routes = [
|
||||
path: "/admin/dashboard",
|
||||
name: "admin-dashboard",
|
||||
meta: { title: "仪表盘" },
|
||||
component: () => import("@/views/admin/Dashboard.vue")
|
||||
component: () => import("@/views/admin/Dashboard.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/system",
|
||||
name: "admin-system",
|
||||
meta: { title: "系统设置" },
|
||||
component: () => import("@/views/admin/SysConfig.vue")
|
||||
component: () => import("@/views/admin/SysConfig.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/user",
|
||||
name: "admin-user",
|
||||
meta: { title: "用户管理" },
|
||||
component: () => import("@/views/admin/Users.vue")
|
||||
component: () => import("@/views/admin/Users.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/app",
|
||||
name: "admin-app",
|
||||
meta: { title: "应用列表" },
|
||||
component: () => import("@/views/admin/Apps.vue")
|
||||
component: () => import("@/views/admin/Apps.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/app/type",
|
||||
name: "admin-app-type",
|
||||
meta: { title: "应用分类" },
|
||||
component: () => import("@/views/admin/AppType.vue")
|
||||
component: () => import("@/views/admin/AppType.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/apikey",
|
||||
name: "admin-apikey",
|
||||
meta: { title: "API-KEY 管理" },
|
||||
component: () => import("@/views/admin/ApiKey.vue")
|
||||
component: () => import("@/views/admin/ApiKey.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/chat/model",
|
||||
name: "admin-chat-model",
|
||||
meta: { title: "语言模型" },
|
||||
component: () => import("@/views/admin/ChatModel.vue")
|
||||
component: () => import("@/views/admin/ChatModel.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/product",
|
||||
name: "admin-product",
|
||||
meta: { title: "充值产品" },
|
||||
component: () => import("@/views/admin/Product.vue")
|
||||
component: () => import("@/views/admin/Product.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/order",
|
||||
name: "admin-order",
|
||||
meta: { title: "充值订单" },
|
||||
component: () => import("@/views/admin/Order.vue")
|
||||
component: () => import("@/views/admin/Order.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/redeem",
|
||||
name: "admin-redeem",
|
||||
meta: { title: "兑换码管理" },
|
||||
component: () => import("@/views/admin/Redeem.vue")
|
||||
component: () => import("@/views/admin/Redeem.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/loginLog",
|
||||
name: "admin-loginLog",
|
||||
meta: { title: "登录日志" },
|
||||
component: () => import("@/views/admin/LoginLog.vue")
|
||||
component: () => import("@/views/admin/LoginLog.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/functions",
|
||||
name: "admin-functions",
|
||||
meta: { title: "函数管理" },
|
||||
component: () => import("@/views/admin/Functions.vue")
|
||||
component: () => import("@/views/admin/Functions.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/chats",
|
||||
name: "admin-chats",
|
||||
meta: { title: "对话管理" },
|
||||
component: () => import("@/views/admin/ChatList.vue")
|
||||
component: () => import("@/views/admin/ChatList.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/images",
|
||||
name: "admin-images",
|
||||
meta: { title: "绘图管理" },
|
||||
component: () => import("@/views/admin/ImageList.vue")
|
||||
component: () => import("@/views/admin/ImageList.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/medias",
|
||||
name: "admin-medias",
|
||||
meta: { title: "音视频管理" },
|
||||
component: () => import("@/views/admin/Medias.vue")
|
||||
component: () => import("@/views/admin/Medias.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/powerLog",
|
||||
name: "admin-power-log",
|
||||
meta: { title: "算力日志" },
|
||||
component: () => import("@/views/admin/PowerLog.vue")
|
||||
component: () => import("@/views/admin/PowerLog.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/manger",
|
||||
name: "admin-manger",
|
||||
meta: { title: "管理员" },
|
||||
component: () => import("@/views/admin/Manager.vue")
|
||||
}
|
||||
]
|
||||
component: () => import("@/views/admin/Manager.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "mobile-login",
|
||||
path: "/mobile/login",
|
||||
meta: { title: "用户登录" },
|
||||
component: () => import("@/views/Login.vue")
|
||||
},
|
||||
{
|
||||
name: "mobile-register",
|
||||
path: "/mobile/register",
|
||||
meta: { title: "用户注册" },
|
||||
component: () => import("@/views/Register.vue")
|
||||
component: () => import("@/views/mobile/Login.vue"),
|
||||
},
|
||||
{
|
||||
name: "mobile",
|
||||
@ -289,65 +283,65 @@ const routes = [
|
||||
{
|
||||
path: "/mobile/index",
|
||||
name: "mobile-index",
|
||||
component: () => import("@/views/mobile/Index.vue")
|
||||
component: () => import("@/views/mobile/Index.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mobile/chat",
|
||||
name: "mobile-chat",
|
||||
component: () => import("@/views/mobile/ChatList.vue")
|
||||
component: () => import("@/views/mobile/ChatList.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mobile/image",
|
||||
name: "mobile-image",
|
||||
component: () => import("@/views/mobile/Image.vue")
|
||||
component: () => import("@/views/mobile/Image.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mobile/profile",
|
||||
name: "mobile-profile",
|
||||
component: () => import("@/views/mobile/Profile.vue")
|
||||
component: () => import("@/views/mobile/Profile.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mobile/imgWall",
|
||||
name: "mobile-img-wall",
|
||||
component: () => import("@/views/mobile/pages/ImgWall.vue")
|
||||
component: () => import("@/views/mobile/pages/ImgWall.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mobile/chat/session",
|
||||
name: "mobile-chat-session",
|
||||
component: () => import("@/views/mobile/ChatSession.vue")
|
||||
component: () => import("@/views/mobile/ChatSession.vue"),
|
||||
},
|
||||
{
|
||||
path: "/mobile/chat/export",
|
||||
name: "mobile-chat-export",
|
||||
component: () => import("@/views/mobile/ChatExport.vue")
|
||||
}
|
||||
]
|
||||
component: () => import("@/views/mobile/ChatExport.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "test",
|
||||
path: "/test",
|
||||
meta: { title: "测试页面" },
|
||||
component: () => import("@/views/Test.vue")
|
||||
component: () => import("@/views/Test.vue"),
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
path: "/test2",
|
||||
meta: { title: "测试页面" },
|
||||
component: () => import("@/views/RealtimeTest.vue")
|
||||
component: () => import("@/views/RealtimeTest.vue"),
|
||||
},
|
||||
{
|
||||
name: "NotFound",
|
||||
path: "/:all(.*)",
|
||||
meta: { title: "页面没有找到" },
|
||||
component: () => import("@/views/404.vue")
|
||||
}
|
||||
component: () => import("@/views/404.vue"),
|
||||
},
|
||||
];
|
||||
|
||||
// console.log(MY_VARIABLE)
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: routes
|
||||
routes: routes,
|
||||
});
|
||||
|
||||
let prevRoute = null;
|
||||
|
@ -1,51 +1,51 @@
|
||||
/**
|
||||
* Util lib functions
|
||||
*/
|
||||
import {showConfirmDialog, showFailToast, showSuccessToast, showToast, showLoadingToast, closeToast} from "vant";
|
||||
import {closeToast, showConfirmDialog, showFailToast, showLoadingToast, showSuccessToast, showToast} from "vant";
|
||||
import {isMobile} from "@/utils/libs";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
export function showLoginDialog(router) {
|
||||
showConfirmDialog({
|
||||
title: '登录',
|
||||
message:
|
||||
'此操作需要登录才能进行,前往登录?',
|
||||
}).then(() => {
|
||||
router.push("/login")
|
||||
}).catch(() => {
|
||||
showConfirmDialog({
|
||||
title: "登录",
|
||||
message: "此操作需要登录才能进行,前往登录?",
|
||||
})
|
||||
.then(() => {
|
||||
router.push("/login");
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
}
|
||||
|
||||
export function showMessageOK(message) {
|
||||
if (isMobile()) {
|
||||
showSuccessToast(message)
|
||||
} else {
|
||||
ElMessage.success(message)
|
||||
}
|
||||
if (isMobile()) {
|
||||
showSuccessToast(message);
|
||||
} else {
|
||||
ElMessage.success(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function showMessageInfo(message) {
|
||||
if (isMobile()) {
|
||||
showToast(message)
|
||||
} else {
|
||||
ElMessage.info(message)
|
||||
}
|
||||
if (isMobile()) {
|
||||
showToast(message);
|
||||
} else {
|
||||
ElMessage.info(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function showMessageError(message) {
|
||||
if (isMobile()) {
|
||||
showFailToast(message)
|
||||
} else {
|
||||
ElMessage.error(message)
|
||||
}
|
||||
if (isMobile()) {
|
||||
showFailToast({message: message, duration: 0});
|
||||
} else {
|
||||
ElMessage.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function showLoading(message = '正在处理...') {
|
||||
showLoadingToast({ message: message, forbidClick: true, duration: 0 })
|
||||
}
|
||||
|
||||
export function closeLoading() {
|
||||
closeToast()
|
||||
}
|
||||
export function showLoading(message = "正在处理...") {
|
||||
showLoadingToast({message: message, forbidClick: true, duration: 0});
|
||||
}
|
||||
|
||||
export function closeLoading() {
|
||||
closeToast();
|
||||
}
|
||||
|
@ -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>
|
Loading…
Reference in New Issue
Block a user