23 changed files with 722 additions and 289 deletions
@ -0,0 +1,274 @@ |
|||
<template> |
|||
<!-- 首页幻灯片 --> |
|||
<div class="banner sm:pt-[60px] pt-[46px]" v-if="banner"> |
|||
<el-carousel :interval="5000" arrow="always" :height="screenWidth <= 768 ? `170px` : `750px`"> |
|||
<el-carousel-item v-for="item in banner.data" :key="item.uid"> |
|||
<nuxt-link v-if="item.path" :to="item.path"> |
|||
<el-image :src="item.url" style="width: 100%" fit="cover"/> |
|||
</nuxt-link> |
|||
<a v-else :href="item.path"> |
|||
<el-image :src="item.url" style="width: 100%" fit="cover"/> |
|||
</a> |
|||
</el-carousel-item> |
|||
</el-carousel> |
|||
</div> |
|||
|
|||
<!-- 首页新闻栏目 --> |
|||
<div class="category md:w-screen-xl m-auto my-3 p-3 flex flex-col gap-xl"> |
|||
<div class="category-item bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">栏目名称</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="category-item bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2 flex items-center"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">友情链接</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import type {AdItem} from "~/api/cms/ad/model"; |
|||
import type {Link} from "~/api/cms/link/model"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useWebsite} from "~/composables/configState"; |
|||
import {getPath} from "~/utils/common"; |
|||
|
|||
const website = useWebsite(); |
|||
const banner = ref<any>(); |
|||
const links = ref<any>(); |
|||
const movieList = ref<any>(); |
|||
const screenWidth = window.innerWidth; |
|||
const screenHeight = window.innerHeight; |
|||
|
|||
// 获取数据 |
|||
const reload = async () => { |
|||
await nextTick() |
|||
const form = useServerRequest('/cms/navigation/getNavigationByPath',{query: {path: getPath()}}) |
|||
const getSlide = useServerRequest<ApiResult<AdItem[]>>('/cms/ad/side'); |
|||
const getLink = useServerRequest<ApiResult<Link[]>>('/oa/link?linkType=友情链接'); |
|||
const [{data: slide}, {data: link}] = await Promise.all([getSlide, getLink]); |
|||
console.log(slide.value) |
|||
banner.value = slide.value?.data; |
|||
links.value = link.value?.data; |
|||
} |
|||
reload(); |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.index { |
|||
padding-top: 20px; |
|||
|
|||
.banner { |
|||
.el-carousel__container { |
|||
height: 380px; |
|||
} |
|||
|
|||
.el-image { |
|||
height: 380px; |
|||
} |
|||
|
|||
@media (max-width: 768px) { |
|||
.el-carousel__container { |
|||
height: 200px; |
|||
} |
|||
|
|||
.el-image { |
|||
height: 200px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.friendly-link { |
|||
border-bottom: #eee solid 1px; |
|||
padding: 10px; |
|||
font-size: 18px; |
|||
|
|||
> img { |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
&__content { |
|||
padding: 20px; |
|||
|
|||
a { |
|||
padding-right: 15px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.demonstration { |
|||
color: var(--el-text-color-secondary); |
|||
} |
|||
|
|||
.col-pd { |
|||
li { |
|||
a { |
|||
font-size: 14px; |
|||
padding: 10px 0 10px; |
|||
border-bottom: dotted 1px #eeeeee; |
|||
|
|||
.badge { |
|||
display: inline-block; |
|||
margin-right: 10px; |
|||
width: 18px; |
|||
height: 18px; |
|||
text-align: center; |
|||
line-height: 18px; |
|||
border-radius: 2px; |
|||
font-size: 12px; |
|||
background-color: #eee; |
|||
color: #333; |
|||
} |
|||
|
|||
.text-muted { |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(1) { |
|||
.badge { |
|||
background-color: #ff4a4a; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(2) { |
|||
.badge { |
|||
background-color: #ff7701; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(3) { |
|||
.badge { |
|||
background-color: #ffb400; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.panel_hd { |
|||
border-bottom: #eeeeee solid 1px; |
|||
height: 46px; |
|||
margin-bottom: 15px; |
|||
|
|||
.title { |
|||
font-size: 18px; |
|||
line-height: 24px; |
|||
|
|||
img { |
|||
margin-right: 10px; |
|||
} |
|||
} |
|||
|
|||
&__right { |
|||
li { |
|||
position: relative; |
|||
|
|||
&::before { |
|||
content: ''; |
|||
display: block; |
|||
width: 1px; |
|||
height: 10px; |
|||
background: #eee; |
|||
position: absolute; |
|||
top: 50%; |
|||
transform: translateY(-30%); |
|||
right: 0; |
|||
} |
|||
|
|||
&:last-child::before { |
|||
display: none; |
|||
} |
|||
|
|||
a { |
|||
padding: 0 10px; |
|||
color: #999; |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.video-list { |
|||
&__block { |
|||
padding: 10px 0; |
|||
|
|||
&__img { |
|||
width: 100%; |
|||
height: 218px; |
|||
} |
|||
|
|||
.img-box { |
|||
position: relative; |
|||
height: 218px; |
|||
display: block; |
|||
|
|||
span { |
|||
position: absolute; |
|||
bottom: 0; |
|||
width: 100%; |
|||
height: 30px; |
|||
line-height: 30px; |
|||
left: 0; |
|||
display: inline-block; |
|||
background-image: linear-gradient(transparent, rgba(0, 0, 0, .5)); |
|||
color: #fff; |
|||
font-size: 12px; |
|||
text-align: right; |
|||
padding-right: 10px; |
|||
box-sizing: border-box; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&__detail { |
|||
.title { |
|||
font-size: 14px; |
|||
color: #333; |
|||
padding-top: 10px; |
|||
} |
|||
|
|||
p { |
|||
min-height: 19px; |
|||
font-size: 12px; |
|||
margin-bottom: 0; |
|||
margin-top: 5px; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media only screen and (max-width: 991px) { |
|||
.video-list { |
|||
&__block { |
|||
&__img, .img-box { |
|||
height: 170px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -1,274 +1,76 @@ |
|||
<template> |
|||
<!-- 首页幻灯片 --> |
|||
<div class="banner sm:pt-[60px] pt-[46px]" v-if="banner"> |
|||
<el-carousel :interval="5000" arrow="always" :height="screenWidth <= 768 ? `170px` : `750px`"> |
|||
<el-carousel-item v-for="item in banner.data" :key="item.uid"> |
|||
<nuxt-link v-if="item.path" :to="item.path"> |
|||
<el-image :src="item.url" style="width: 100%" fit="cover"/> |
|||
</nuxt-link> |
|||
<a v-else :href="item.path"> |
|||
<el-image :src="item.url" style="width: 100%" fit="cover"/> |
|||
</a> |
|||
</el-carousel-item> |
|||
</el-carousel> |
|||
</div> |
|||
<!-- Banner --> |
|||
<Banner :layout="layout" /> |
|||
|
|||
<!-- 首页新闻栏目 --> |
|||
<div class="category md:w-3/4 m-auto my-3 p-3 flex flex-col gap-xl"> |
|||
<div class="category-item bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">栏目名称</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="category-item bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2 flex items-center"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">友情链接</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- 简单模式(常规单页面) --> |
|||
<PageContainer :form="form" :layout="layout" /> |
|||
|
|||
<!-- 高级模式(自定义组件) --> |
|||
<div class="flex flex-col" v-if="layout?.showLayout"> |
|||
{{ layout }} |
|||
</div> |
|||
</template> |
|||
|
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import type {AdItem} from "~/api/cms/ad/model"; |
|||
import type {Link} from "~/api/cms/link/model"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useWebsite} from "~/composables/configState"; |
|||
import {getPath} from "~/utils/common"; |
|||
|
|||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState"; |
|||
import type {BreadcrumbItem} from "~/types/global"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import {getIdBySpm} from "~/utils/common"; |
|||
import PageContainer from "~/components/PageContainer.vue"; |
|||
|
|||
// 引入状态管理 |
|||
const route = useRoute(); |
|||
const website = useWebsite(); |
|||
const banner = ref<any>(); |
|||
const links = ref<any>(); |
|||
const movieList = ref<any>(); |
|||
const screenWidth = window.innerWidth; |
|||
const screenHeight = window.innerHeight; |
|||
const layout = ref<any>(); |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const form = useForm(); |
|||
const breadcrumb = ref<BreadcrumbItem>(); |
|||
|
|||
// 获取数据 |
|||
// 请求数据 |
|||
const reload = async () => { |
|||
await nextTick() |
|||
const form = useServerRequest('/cms/navigation/getNavigationByPath',{query: {path: getPath()}}) |
|||
const getSlide = useServerRequest<ApiResult<AdItem[]>>('/cms/ad/side'); |
|||
const getLink = useServerRequest<ApiResult<Link[]>>('/oa/link?linkType=友情链接'); |
|||
const [{data: slide}, {data: link}] = await Promise.all([getSlide, getLink]); |
|||
console.log(slide.value) |
|||
banner.value = slide.value?.data; |
|||
links.value = link.value?.data; |
|||
} |
|||
reload(); |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.index { |
|||
padding-top: 20px; |
|||
|
|||
.banner { |
|||
.el-carousel__container { |
|||
height: 380px; |
|||
} |
|||
|
|||
.el-image { |
|||
height: 380px; |
|||
} |
|||
|
|||
@media (max-width: 768px) { |
|||
.el-carousel__container { |
|||
height: 200px; |
|||
} |
|||
|
|||
.el-image { |
|||
height: 200px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.friendly-link { |
|||
border-bottom: #eee solid 1px; |
|||
padding: 10px; |
|||
font-size: 18px; |
|||
|
|||
> img { |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
&__content { |
|||
padding: 20px; |
|||
|
|||
a { |
|||
padding-right: 15px; |
|||
} |
|||
} |
|||
} |
|||
// 存在spm(优先级高) |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + getIdBySpm(5)) |
|||
if (nav.value?.data) { |
|||
form.value = nav.value.data |
|||
}else{ |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: route.path}}) |
|||
if(nav.value?.data){ |
|||
form.value = nav.value?.data; |
|||
} |
|||
} |
|||
// 页面布局 |
|||
if(form.value?.layout){ |
|||
layout.value = JSON.parse(form.value?.layout) |
|||
} |
|||
|
|||
// seo |
|||
useHead({ |
|||
title: `${form.value.title} - ${website.value.websiteName}`, |
|||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }], |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
}, |
|||
script: [ |
|||
{ |
|||
children: `console.log(${JSON.stringify(form.value)})`, |
|||
}, |
|||
], |
|||
}); |
|||
// 面包屑 |
|||
breadcrumb.value = form.value |
|||
} |
|||
|
|||
.demonstration { |
|||
color: var(--el-text-color-secondary); |
|||
} |
|||
|
|||
.col-pd { |
|||
li { |
|||
a { |
|||
font-size: 14px; |
|||
padding: 10px 0 10px; |
|||
border-bottom: dotted 1px #eeeeee; |
|||
|
|||
.badge { |
|||
display: inline-block; |
|||
margin-right: 10px; |
|||
width: 18px; |
|||
height: 18px; |
|||
text-align: center; |
|||
line-height: 18px; |
|||
border-radius: 2px; |
|||
font-size: 12px; |
|||
background-color: #eee; |
|||
color: #333; |
|||
} |
|||
|
|||
.text-muted { |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(1) { |
|||
.badge { |
|||
background-color: #ff4a4a; |
|||
color: #fff; |
|||
} |
|||
} |
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
console.log(path,'=>Path') |
|||
|
|||
&:nth-child(2) { |
|||
.badge { |
|||
background-color: #ff7701; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
&:nth-child(3) { |
|||
.badge { |
|||
background-color: #ffb400; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.panel_hd { |
|||
border-bottom: #eeeeee solid 1px; |
|||
height: 46px; |
|||
margin-bottom: 15px; |
|||
|
|||
.title { |
|||
font-size: 18px; |
|||
line-height: 24px; |
|||
|
|||
img { |
|||
margin-right: 10px; |
|||
} |
|||
} |
|||
|
|||
&__right { |
|||
li { |
|||
position: relative; |
|||
|
|||
&::before { |
|||
content: ''; |
|||
display: block; |
|||
width: 1px; |
|||
height: 10px; |
|||
background: #eee; |
|||
position: absolute; |
|||
top: 50%; |
|||
transform: translateY(-30%); |
|||
right: 0; |
|||
} |
|||
|
|||
&:last-child::before { |
|||
display: none; |
|||
} |
|||
|
|||
a { |
|||
padding: 0 10px; |
|||
color: #999; |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.video-list { |
|||
&__block { |
|||
padding: 10px 0; |
|||
|
|||
&__img { |
|||
width: 100%; |
|||
height: 218px; |
|||
} |
|||
|
|||
.img-box { |
|||
position: relative; |
|||
height: 218px; |
|||
display: block; |
|||
|
|||
span { |
|||
position: absolute; |
|||
bottom: 0; |
|||
width: 100%; |
|||
height: 30px; |
|||
line-height: 30px; |
|||
left: 0; |
|||
display: inline-block; |
|||
background-image: linear-gradient(transparent, rgba(0, 0, 0, .5)); |
|||
color: #fff; |
|||
font-size: 12px; |
|||
text-align: right; |
|||
padding-right: 10px; |
|||
box-sizing: border-box; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&__detail { |
|||
.title { |
|||
font-size: 14px; |
|||
color: #333; |
|||
padding-top: 10px; |
|||
} |
|||
|
|||
p { |
|||
min-height: 19px; |
|||
font-size: 12px; |
|||
margin-bottom: 0; |
|||
margin-top: 5px; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media only screen and (max-width: 991px) { |
|||
.video-list { |
|||
&__block { |
|||
&__img, .img-box { |
|||
height: 170px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
|||
|
@ -0,0 +1,178 @@ |
|||
<template> |
|||
<div class="login flex justify-around mt-[120px] h-[550px]"> |
|||
<div class="flash"> |
|||
|
|||
</div> |
|||
<el-card class="m-2 w-screen-sm sm:w-[430px] flex justify-around"> |
|||
<el-space class="tabs pt-5 text-xl flex justify-center"> |
|||
<el-tabs v-model="activeName" class="demo-tabs"> |
|||
<el-tab-pane label="账号登录" name="first"> |
|||
<div class="custom-style my-4"> |
|||
<el-form :model="form" label-width="auto" class="w-[330px]"> |
|||
<el-form-item> |
|||
<el-input class="w-full" size="large" maxlength="11" placeholder="登录账号|手机号码" v-model="form.username" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-input type="password" size="large" placeholder="登录密码" v-model="form.password" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-space class="flex justify-between w-full"> |
|||
<el-input size="large" placeholder="图形验证码" v-model="form.code" /> |
|||
<el-image alt="" :src="captcha" @click="changeCaptcha" /> |
|||
</el-space> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="记住密码"> |
|||
<el-switch v-model="form.remember" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" size="large" class="w-full" @click="onSubmit">登录</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</div> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="短信登录" name="second"> |
|||
<div class="custom-style my-4"> |
|||
<el-form :model="form" label-width="auto" class="w-[330px]"> |
|||
<el-form-item> |
|||
<el-input class="w-full" size="large" placeholder="手机号码" v-model="form.username" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-input type="password" size="large" placeholder="验证码" v-model="form.password" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" size="large" class="w-full" @click="onSubmit">登录</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</div> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</el-space> |
|||
<div class="clearfix flex justify-center"> |
|||
<el-divider> |
|||
其他登录方式 |
|||
</el-divider> |
|||
</div> |
|||
<div class="clearfix flex justify-center"> |
|||
<el-button circle :icon="ElIconUserFilled"></el-button> |
|||
</div> |
|||
<!-- <div class="clearfix flex justify-center">--> |
|||
<!-- <el-space>--> |
|||
<!-- <a class=" text-gray-400 cursor-pointer">忘记账号</a>--> |
|||
<!-- <a class=" text-gray-400 cursor-pointer">忘记密码</a>--> |
|||
<!-- <a class=" text-gray-400 cursor-pointer">登录异常帮助文档</a>--> |
|||
<!-- </el-space>--> |
|||
<!-- </div>--> |
|||
|
|||
|
|||
<!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">--> |
|||
<!-- <el-tab-pane label="登录" name="first">登录</el-tab-pane>--> |
|||
<!-- <el-tab-pane label="注册" name="second">注册</el-tab-pane>--> |
|||
<!-- </el-tabs>--> |
|||
</el-card> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import {useConfigInfo, useToken, useUserInfo, useWebsite} from "~/composables/configState"; |
|||
import useFormData from '@/utils/use-form-data'; |
|||
import type { User } from '@/api/system/user/model'; |
|||
import { ref } from 'vue' |
|||
import {getCaptcha} from "~/api/passport/login"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult} from "~/api"; |
|||
import type {Website} from "~/api/cms/website/model"; |
|||
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model"; |
|||
|
|||
// 配置信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const website = useWebsite() |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const userInfo = useUserInfo(); |
|||
|
|||
const value = ref('登录') |
|||
const options = ['登录', '注册'] |
|||
const activeName = ref('first') |
|||
|
|||
// 验证码 base64 数据 |
|||
const captcha = ref(''); |
|||
// 验证码内容, 实际项目去掉 |
|||
const text = ref(''); |
|||
// 是否显示图形验证码弹窗 |
|||
const visible = ref(false); |
|||
// 图形验证码 |
|||
const imgCode = ref(''); |
|||
// 发送验证码按钮loading |
|||
const codeLoading = ref(false); |
|||
// 验证码倒计时时间 |
|||
const countdownTime = ref(0); |
|||
// 验证码倒计时定时器 |
|||
let countdownTimer: number | null = null; |
|||
|
|||
// 配置信息 |
|||
const { form } = useFormData<User>({ |
|||
username: '', |
|||
phone: '', |
|||
password: '', |
|||
code: '', |
|||
smsCode: '', |
|||
remember: true, |
|||
isAdmin: true |
|||
}); |
|||
|
|||
|
|||
const navigateTo = (url: string) => { |
|||
window.location.href = url; |
|||
} |
|||
|
|||
/* 获取图形验证码 */ |
|||
const changeCaptcha = async () => { |
|||
const {data: captchaInfo } = await useServerRequest<ApiResult<CaptchaResult>>('/captcha',{baseURL: runtimeConfig.public.apiServer}); |
|||
const captchaData = captchaInfo.value?.data |
|||
if(captchaData){ |
|||
captcha.value = captchaData.base64; |
|||
} |
|||
|
|||
// 已经登录跳转首页 |
|||
if(token.value && token.value.length > 0){ |
|||
navigateTo('/') |
|||
return; |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* 执行登录 |
|||
*/ |
|||
const onSubmit = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/login',{baseURL: runtimeConfig.public.apiServer,method: "post",body: form}) |
|||
console.log(response.value); |
|||
if (response.value?.code == 0) { |
|||
if(response.value.data){ |
|||
const access_token = response.value.data?.access_token |
|||
const user = response.value.data?.user |
|||
if(access_token){ |
|||
token.value = access_token; |
|||
} |
|||
if(user){ |
|||
userInfo.value = user |
|||
} |
|||
} |
|||
ElMessage.success(response.value.message) |
|||
// setTimeout(() => { |
|||
// navigateTo('/') |
|||
// return; |
|||
// },1000) |
|||
} |
|||
if(response.value?.code !== 0){ |
|||
ElMessage.error(response.value?.message) |
|||
} |
|||
} |
|||
|
|||
changeCaptcha(); |
|||
</script> |
|||
<style lang="less"> |
|||
body{ |
|||
background: url("https://oss.wsdns.cn/20240616/4fe7e2e00b7e43e7a5f189fe11b21196.jpeg"); |
|||
background-size: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,134 @@ |
|||
<template> |
|||
<div class="login-layout mt-[100px] m-auto w-screen-xl"> |
|||
<el-container class="mt-[100px] m-auto p-"> |
|||
<el-aside width="170px" class="bg-white p-3 hover:shadow"> |
|||
<div class="flex justify-center pb-4"> |
|||
<img src="https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png" class="w-[80px] h-[80px] rounded-full" /> |
|||
</div> |
|||
<div class="menu"> |
|||
<el-menu |
|||
default-active="1" |
|||
style="border: none" |
|||
> |
|||
<el-menu-item v-for="(item,index) in activities" :index="`${index+1}`" style="border-bottom: 1px solid #f3f3f3"> |
|||
<span class="text-center">{{ item.name }}</span> |
|||
</el-menu-item> |
|||
</el-menu> |
|||
</div> |
|||
</el-aside> |
|||
<div shadow="hover" class="flash bg-white hover:shadow h-[500px] w-full ml-6"> |
|||
={{ userInfo }}= |
|||
</div> |
|||
</el-container> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import {useConfigInfo, useToken, useUserInfo, useWebsite} from "~/composables/configState"; |
|||
import useFormData from '@/utils/use-form-data'; |
|||
import type { User } from '@/api/system/user/model'; |
|||
import { ref } from 'vue' |
|||
import {getCaptcha} from "~/api/passport/login"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult} from "~/api"; |
|||
import type {Website} from "~/api/cms/website/model"; |
|||
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model"; |
|||
|
|||
// 配置信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const website = useWebsite() |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const userInfo = useUserInfo(); |
|||
|
|||
const value = ref('登录') |
|||
const options = ['登录', '注册'] |
|||
const activeName = ref('first') |
|||
|
|||
const activities = [ |
|||
{ |
|||
name: '账号信息' |
|||
}, |
|||
{ |
|||
name: '实名认证' |
|||
}, |
|||
{ |
|||
name: '订单列表' |
|||
}, |
|||
{ |
|||
name: '退出登录' |
|||
}, |
|||
] |
|||
const activeNames = ref(['1']) |
|||
const handleChange = (val: string[]) => { |
|||
console.log(val) |
|||
} |
|||
// 验证码 base64 数据 |
|||
const captcha = ref(''); |
|||
// 验证码内容, 实际项目去掉 |
|||
const text = ref(''); |
|||
// 是否显示图形验证码弹窗 |
|||
const visible = ref(false); |
|||
// 图形验证码 |
|||
const imgCode = ref(''); |
|||
// 发送验证码按钮loading |
|||
const codeLoading = ref(false); |
|||
// 验证码倒计时时间 |
|||
const countdownTime = ref(0); |
|||
// 验证码倒计时定时器 |
|||
let countdownTimer: number | null = null; |
|||
|
|||
// 配置信息 |
|||
const { form } = useFormData<User>({ |
|||
username: '', |
|||
phone: '', |
|||
password: '', |
|||
code: '', |
|||
smsCode: '', |
|||
remember: true, |
|||
isAdmin: true |
|||
}); |
|||
|
|||
|
|||
const navigateTo = (url: string) => { |
|||
window.location.href = url; |
|||
} |
|||
|
|||
/** |
|||
* 执行登录 |
|||
*/ |
|||
const onSubmit = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/login',{baseURL: runtimeConfig.public.apiServer,method: "post",body: form}) |
|||
if (response.value?.code == 0) { |
|||
if(response.value.data){ |
|||
const access_token = response.value.data?.access_token |
|||
const user = response.value.data?.user |
|||
if(access_token){ |
|||
token.value = access_token; |
|||
} |
|||
if(user){ |
|||
userInfo.value = user; |
|||
} |
|||
} |
|||
ElMessage.success(response.value.message) |
|||
setTimeout(() => { |
|||
navigateTo('/') |
|||
return; |
|||
},1000) |
|||
} |
|||
console.log(response.value?.data,'>>>>>sfd') |
|||
ElMessage.error(response.value?.message) |
|||
return false; |
|||
} |
|||
|
|||
useHead({ |
|||
title: `用户中心 - ${config.value?.siteName}`, |
|||
meta: [{ name: website.value.keywords, content: website.value.comments }] |
|||
}); |
|||
|
|||
</script> |
|||
<style lang="less"> |
|||
body{ |
|||
background: url("https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png"); |
|||
background-size: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,29 @@ |
|||
import { reactive } from 'vue'; |
|||
|
|||
/** |
|||
* 表单数据 hook |
|||
* @param initValue 默认值 |
|||
*/ |
|||
export default function <T extends object>(initValue?: T) { |
|||
const form = reactive<T>({ ...initValue } as T); |
|||
|
|||
const resetFields = () => { |
|||
Object.keys(form).forEach((key) => { |
|||
form[key] = initValue ? initValue[key] : void 0; |
|||
}); |
|||
}; |
|||
|
|||
const assignFields = (data: object) => { |
|||
Object.keys(form).forEach((key) => { |
|||
form[key] = data[key]; |
|||
}); |
|||
}; |
|||
|
|||
return { |
|||
form, |
|||
// 重置为初始值
|
|||
resetFields, |
|||
// 赋值不改变字段
|
|||
assignFields |
|||
}; |
|||
} |
Loading…
Reference in new issue