feature/dev
shenhan 2025-01-03 14:50:49 +08:00
parent 6ecfe7fa5c
commit 24c9ff4a7e
8 changed files with 812 additions and 24 deletions

View File

@ -1,7 +1,7 @@
<template> <template>
<div id="app"> <div id="app">
<router-view /> <router-view />
<theme-picker /> <!-- <theme-picker /> -->
</div> </div>
</template> </template>

View File

@ -0,0 +1,101 @@
<template>
<div class="container ">
<div class="header">
<div class="header-left">
<a class="header-iconLogo" style="background-image: url(&quot;https://liblibai-web-static.liblib.cloud/liblibai_v4_online/static/_next/static/images/icon-logo.e3ce24f316fb81dbde1cafc3bf956080.svg&quot;);"></a>
</div>
<div class="header-right">
<!-- <div>PC客户端</div> -->
<div class="header-right-btn pc-client">
<svg-icon icon-class="404"></svg-icon>
PC客户端
</div>
<div class="header-right-btn" @click="downImg">
<svg-icon icon-class="log"></svg-icon>
教程专区
</div>
<div class="header-right-btn">
<svg-icon icon-class="number"></svg-icon>
发布
</div>
<div class="header-right-btn">
<svg-icon icon-class="pdf"></svg-icon>
登录/注册
</div>
<!-- <div><el-button class="pc-client"><svg-icon icon-class="404" @click="click" />PC客户端</el-button></div>
<div><el-button><svg-icon icon-class="404" @click="click" />教程专区</el-button></div>
<div><el-button><svg-icon icon-class="404" @click="click" />发布</el-button></div>
<div><el-button><svg-icon icon-class="404" @click="click" />登录/注册</el-button></div> -->
</div>
</div>
</div>
</template>
<script>
import { downloadImage } from '@/utils/downloadImage';
export default {
name: "McHeader",
methods: {
test(){
this.$emit('onCloseLogin', true)
},
handleDownload() {
const imageUrl = 'https://img0.baidu.com/it/u=2191392668,814349101&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1399';
const fileName = 'downloaded_image.jpg'; //
downloadImage(imageUrl, fileName);
}
},
};
</script>
<style scoped lang="scss">
.header-btn{
color: #1890ff;
// border-color: #badeff;
}
.container{
position: absolute;
top: 0;
left: 0;
width: 100%;
// padding:0 20px;
.header{
padding: 10px 20px;
border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
.header-left{
.header-iconLogo{
display: block;
background-repeat: no-repeat;
width: 100px;
height: 40px;
}
}
.header-right{
display: flex;
align-items: center;
.header-right-btn{
padding: 6px 10px;
border-radius: 4px;
margin-right: 10px;
color: rgb(51, 51, 51);
font-weight: 500;
font-size: 14px;
background-color: rgb(241, 242, 248);
cursor: pointer;
&:hover{
@extend .header-btn;
}
}
.pc-client{
color: #1890ff;
border: 1px solid #1890ff;
background-color: #fff;
border-color: #badeff;
}
}
}
}
</style>

View File

@ -0,0 +1,322 @@
<template>
<div class="login-container">
<div class="login-box">
<i class="el-icon-close close-login" @click="onCloseLogin(false)"></i>
<div class="login-form">
<h2 class="title">{{ loginModeDescriptions[currentLoginMode].title }}</h2>
<p class="subtitle">{{ loginModeDescriptions[currentLoginMode].des }}</p>
<el-form
v-if="currentLoginMode === 'phone'"
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="0"
>
<el-form-item prop="phone">
<el-input
v-model="ruleForm.phone"
placeholder="请输入手机号"
>
<template #prepend>
+86
</template>
</el-input>
</el-form-item>
<el-form-item prop="code">
<el-input v-model="ruleForm.code" placeholder="请输入验证码">
<template #suffix>
<el-button
type="primary"
size="small"
class="code-button"
:disabled="codeButtonDisabled"
@click="onGetCode"
>
{{ codeButtonText }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item>
<!-- <el-checkbox v-model="form.agree">
点击登录代表同意
<a href="#/user-agreement">用户协议</a>
<a href="#/privacy-policy">隐私政策</a>
</el-checkbox> -->
<div class="agreement-text">
<div>点击登录代表同意</div>
<div class="agreement-link">用户协议隐私政策</div>
</div>
</el-form-item>
<div class="login-button" @click="submitForm(ruleFormRef)"></div>
</el-form>
<div v-if="currentLoginMode === 'wechat'" class="wechat-qs">
<div class="qr-code">
<!-- <WechatLoginQr /> -->
</div>
</div>
</div>
<div class="other-login-methods">
<div>
<span>其他登录方式</span>
</div>
<div class="icons-container">
<div class="icon-item" @click="setCurrentLoginMode(currentLoginMode)">
<img :src="loginModeDescriptions[currentLoginMode].src" alt="" />
</div>
<!-- <div class="icon-item">
<img src="../../assets/images/icon/icon-qq.png" alt="" />
</div> -->
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "McLogin",
data(){
return{
rules:{
phone: [
{ required: true, message: '请输入手机号', trigger: 'change' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'change' },
{ len: 4, message: '验证码长度为4位', trigger: 'blur' }
]
},
codeButtonText:'获取验证码',
codeButtonDisabled:false,
timerCode:null,
ruleForm:{
phone: '15888888882',
code: ''
},
ruleFormRef:null,
currentLoginMode:'phone',
loginModeDescriptions:{
phone: {
title: '登录',
des: '如未注册,验证后自动登录',
// src: phoneIcon
},
wechat: {
title: '微信一键登录',
des: '关注后自动登录',
// src: wechatIcon
}
}
}
},
methods: {
onCloseLogin(ag){
debugger
this.$emit('onCloseLogin', ag)
},
toggleMode(mode){
return mode === 'phone' ? 'wechat' : mode === 'wechat' ? 'phone' : mode
},
setCurrentLoginMode(mode){
this.currentLoginMode = this.toggleMode(mode)
},
async submitForm (formEl){
if (!formEl) return
await formEl.validate(async (valid, fields) => {
if (valid) {
try {
// const res = await phoneLogin({...ruleForm})
if (res.code === 200) {
// ElMessage({
// message: '',
// type: 'success'
// })
store.userInfo = res.data
router.push('./home')
}else{
// ElMessage({
// message: res.msg,
// type: 'error'
// })
}
} catch (error) {
console.log(error)
}
} else {
console.log('error submit!', fields)
}
})
},
async onGetCode () {
if (!ruleForm.phone || !/^1[3-9]\d{9}$/.test(ruleForm.phone)) {
// ElMessage.error('')
return
}
try{
// const res = await getCode({phone:ruleForm.phone})
console.log(res);
codeButtonDisabled = true
let countdown = 60
codeButtonText = `${countdown}秒后重试`
this.timerCode = window.setInterval(() => {
countdown--
if (countdown <= 0) {
clearInterval(this.timerCode)
codeButtonDisabled.value = false
codeButtonText.value = '获取验证码'
} else {
codeButtonText.value = `${countdown}秒后重试`
}
}, 1000)
// ElMessage.success('')
}catch(err){
console.log(err);
}
}
},
};
</script>
<style scoped lang="scss">
.el-input--medium .el-input__inner{
height: 42px !important;
line-height: 42px !important;
}
.login-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba($color: #000000, $alpha: 0.7);
display: flex;
justify-content: center;
align-items: center;
height: calc(100vh - $titleBarHeight);
.login-box {
position: relative;
width: 360px;
padding: 40px;
background: #fff;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
.close-login{
position: absolute;
cursor: pointer;
right: -30px;
color: #fff;
font-size: 24px;
top: 0;
&:hover{
transform: scale(1.2);
}
}
.wechat-qs {
text-align: center;
}
.agreement-text {
font-size: 12px;
color: #999;
display: flex;
.agreement-link {
color: #409eff;
cursor: pointer;
}
}
.login-button {
background-color: rgb(45, 85, 255);
text-align: center;
border-radius: 10px;
cursor: pointer;
color: #fff;
font-size: 20px;
width: 100%;
line-height: 50px;
}
.code-button {
background-color: rgba($color: #fff, $alpha: 0);
border: none;
color: #409eff;
}
.title {
font-size: 24px;
font-weight: bold;
text-align: center;
margin-bottom: 10px;
color: #333;
}
.subtitle {
text-align: center;
color: #666;
margin-bottom: 14px;
font-size: 14px;
}
//
.el-form-item {
margin-bottom: 20px;
.el-input,
.el-select {
height: 40px; //
line-height: 40px;
box-sizing: border-box; //
}
//
.region-select {
.el-input {
height: 40px;
line-height: 40px;
}
}
.el-button {
height: 40px;
line-height: 40px;
padding: 0 15px;
}
}
.el-checkbox {
font-size: 14px;
color: #666;
a {
color: #409eff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
.other-login-methods {
margin-top: 20px;
text-align: center;
font-size: 14px;
color: #999;
.icons-container {
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
.icon-item {
height: 48px;
padding: 0px 20px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
}
}
}
}
</style>

View File

@ -0,0 +1,243 @@
<template>
<div class="container">
<div class="mcwl-siderbar" v-for="(item,index) in sideBarList" :key="index">
<div class="mcwl-siderbar-title">
{{ item.title }}
</div>
<div class="siderbar-item" :style="{border:item.dashed ? '1px dashed #818080' : ''}" v-for="(subItem,subIndex) in item.list" :key="subIndex">
<div class="siderbar-icon">
<svg-icon icon-class="pdf"></svg-icon>
</div>
<div class="siderbar-menu">
<div class="siderbar-menu-text">
{{ subItem.text }}
</div>
<div class="siderbar-menu-des">
{{ subItem.desc }}
</div>
</div>
</div>
</div>
<div class="footer-content">
<div class="join-us">
<svg-icon icon-class="pdf"></svg-icon>
加入我们
</div>
<div class="platform-content">
<div class="platform-item">
微信公众号
</div>
<div class="platform-item">
小红书
</div>
<div class="platform-item">
抖音
</div>
<div class="platform-item">
B站
</div>
</div>
<div class="platform-content">
<div>
用户协议
</div>
<div>
隐私政策
</div>
<div>
关于我们
</div>
</div>
<div class="footer-text">
<div>
上海辰风互娱网络科技有限公司
</div>
<div>
京IPC备234324324号
</div>
<div>
网信算备
</div>
<div>
23423432432432423
</div>
<div>
生成式人工智能服务管理暂行办法
</div>
<div>
备案号shanghai-sefef-23434
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "McSideBar",
data(){
return{
sideBarList:[
{
id:1,
title:'探索',
dashed:false,
list:[
{
id:11,
icon:'',
text:'模型广场',
desc:''
},
{
id:12,
icon:'',
text:'作品灵感',
desc:''
},
{
id:13,
icon:'',
text:'工作流',
desc:''
},
]
},
{
id:2,
title:'创作',
dashed:true,
list:[
{
id:21,
icon:'',
text:'在线生图',
desc:'Web UI'
},
{
id:22,
icon:'',
text:'在线工作流',
desc:'Comfy UI'
},
// {
// id:23,
// icon:'',
// text:'LoRA'
// },
// {
// id:24,
// icon:'',
// text:''
// },
]
},
{
id:3,
title:'其他',
dashed:false,
list:[
// {
// id:31,
// icon:'',
// text:'API',
// },
// {
// id:32,
// icon:'',
// text:'',
// },
{
id:33,
icon:'',
text:'个人中心',
},
{
id:34,
icon:'',
text:'会员中心',
}
]
}
]
}
}
};
</script>
<style scoped lang="scss">
.container{
overflow: auto; /* 内容可滚动 */
scrollbar-width: none; /* 仅适用于 Firefox隐藏滚动条 */
-ms-overflow-style: none; /* 仅适用于 IE 和 Edge */
color: #4e4e4e;
width: 220px;
height: calc(100vh - 61px);
border-right:1px solid #e7e7e7;
.mcwl-siderbar{
font-size: 14px;
padding: 20px;
.mcwl-siderbar-title{
color: rgb(156, 163, 175);
font-size: 12px;
}
.siderbar-item-dashed{
border: 1px dashed #818080;
}
.siderbar-item{
border-radius: 10px;
padding: 10px 8px;
margin-top: 8px;
cursor: pointer;
display: flex;
.siderbar-icon{
margin-right: 4px;
> svg{
font-size: 14px;
margin-right: 8px;
}
}
.siderbar-menu{
display: flex;
flex-direction: column;
justify-content: center;
.siderbar-menu-des{
margin-top: 4px;
color: #8e8e8e;
font-size: 12px;
}
}
}
}
.footer-content{
padding: 20px;
.join-us{
width: auto;
line-height: 30px;
font-size: 14px;
text-align: center;
border: 1px dashed #818080;
border-radius: 6px;
margin-bottom: 10px;
}
.platform-content{
display: flex;
flex-wrap: wrap;
font-size: 12px;
.platform-item{
padding: 4px 6px;
}
}
.footer-text{
font-size:12px;
color:#8e8e8e;
>div{
margin-top: 6px;
}
}
}
}
</style>

View File

@ -0,0 +1,68 @@
<template>
<div class="container">
<div class="wechat-login-container">
<div v-if="bindTimeout" class="tipTimeout" @click="onGetUUid">
刷新二维码
<el-icon :size="30"><Refresh /></el-icon>
<!-- <i class="el-icon-refresh" @click="onGetUUid"></i> -->
</div>
<div class="qrcode-wrapper" v-if="qrUrl">
<!-- <qrcode-vue :value="qrUrl" :size="200" level="M" /> -->
<!-- <div class="refresh-tip" v-if="needRefresh">
二维码已过期点击刷新
<button @click="generateQrCode"></button>
</div> -->
</div>
<div v-else class="loading">
加载中...
</div>
</div>
</div>
</template>
<script>
export default {
name: "McWechatLoginQr",
data(){
return{
pollingTimer:null,
bindTimeout:0,
qrUrl:''
}
}
};
</script>
<style scoped lang="scss">
.wechat-login-container {
width: 280px;
padding:0 20px;
text-align: center;
position: relative;
.tipTimeout{
position: absolute;
top: 0;
left: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 100%;
cursor: pointer;
height: 100%;
color: #fff;
background-color: rgba($color: #000000, $alpha: 0.5);
.qrcode-wrapper {
position: relative;
}
.loading {
padding: 20px;
color: #999;
}
// opacity:0.3
}
}
</style>

View File

@ -1,6 +1,17 @@
<template> <template>
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}"> <div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/> <div>
<McHeader @onCloseLogin="onCloseLogin"></McHeader>
</div>
<div class="main-content">
<div>
<McSidebar></McSidebar>
</div>
<div>
<McLogin @onCloseLogin="onCloseLogin" v-if="isShowLogin"></McLogin>
</div>
</div>
<!-- <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar v-if="!sidebar.hide" class="sidebar-container"/> <sidebar v-if="!sidebar.hide" class="sidebar-container"/>
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container"> <div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
<div :class="{'fixed-header':fixedHeader}"> <div :class="{'fixed-header':fixedHeader}">
@ -11,28 +22,39 @@
<right-panel> <right-panel>
<settings/> <settings/>
</right-panel> </right-panel>
</div> </div> -->
</div> </div>
</template> </template>
<script> <script>
import RightPanel from '@/components/RightPanel' import { getToken } from '@/utils/auth'
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components' // import RightPanel from '@/components/RightPanel'
import ResizeMixin from './mixin/ResizeHandler' import McHeader from '@/components/McHeader'
import McSidebar from '@/components/McSideBar'
import McLogin from '@/components/McLogin'
// import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
// import ResizeMixin from './mixin/ResizeHandler'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import variables from '@/assets/styles/variables.scss' import variables from '@/assets/styles/variables.scss'
export default { export default {
name: 'Layout', name: 'Layout',
components: { data() {
AppMain, return {
Navbar, isShowLogin:false,
RightPanel, }
Settings,
Sidebar,
TagsView
}, },
mixins: [ResizeMixin], components: {
// AppMain,
// Navbar,
// RightPanel,
// Settings,
// Sidebar,
// TagsView,
McHeader,
McSidebar,
McLogin
},
// mixins: [ResizeMixin],
computed: { computed: {
...mapState({ ...mapState({
theme: state => state.settings.theme, theme: state => state.settings.theme,
@ -55,6 +77,9 @@ export default {
} }
}, },
methods: { methods: {
onCloseLogin(ag){
this.isShowLogin = ag
},
handleClickOutside() { handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
} }
@ -65,7 +90,10 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/assets/styles/mixin.scss"; @import "~@/assets/styles/mixin.scss";
@import "~@/assets/styles/variables.scss"; @import "~@/assets/styles/variables.scss";
.main-content{
display: flex;
padding-top: 61px
}
.app-wrapper { .app-wrapper {
@include clearfix; @include clearfix;
position: relative; position: relative;
@ -93,7 +121,7 @@ export default {
top: 0; top: 0;
right: 0; right: 0;
z-index: 9; z-index: 9;
width: calc(100% - #{$base-sidebar-width}); // width: calc(100% - #{$base-sidebar-width});
transition: width 0.28s; transition: width 0.28s;
} }

View File

@ -47,14 +47,16 @@ router.beforeEach((to, from, next) => {
} }
} }
} else { } else {
next()
// 没有token // 没有token
if (isWhiteList(to.path)) { // if (isWhiteList(to.path)) {
// 在免登录白名单,直接进入 // // 在免登录白名单,直接进入
next() // next()
} else { // } else {
next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页 // next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页
NProgress.done() // NProgress.done()
} // }
} }
}) })

View File

@ -0,0 +1,24 @@
// src/utils/downloadImage.js
export async function downloadImage(imageUrl, fileName = 'downloaded_image.jpg') {
try {
// 使用 fetch 获取图片数据
const response = await fetch(imageUrl);
const blob = await response.blob(); // 将响应转为 Blob
// 创建一个 URL 对象
const url = window.URL.createObjectURL(blob);
// 创建一个 <a> 标签并设置其下载属性
const a = document.createElement('a');
a.href = url;
a.download = fileName; // 指定下载的文件名
a.click(); // 触发下载
// 释放对象 URL
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('下载图片失败:', error);
}
}