mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-15 12:59:22 +08:00
first commit
This commit is contained in:
11
src/views/default/error/403.vue
Normal file
11
src/views/default/error/403.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<ErrorPage :code="403" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ErrorPage from './components/ErrorPage.vue'
|
||||
|
||||
defineOptions({ name: 'Error403' })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
11
src/views/default/error/404.vue
Normal file
11
src/views/default/error/404.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<ErrorPage :code="404" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ErrorPage from './components/ErrorPage.vue'
|
||||
|
||||
defineOptions({ name: 'Error404' })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
11
src/views/default/error/500.vue
Normal file
11
src/views/default/error/500.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<ErrorPage :code="500" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ErrorPage from './components/ErrorPage.vue'
|
||||
|
||||
defineOptions({ name: 'Error500' })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
126
src/views/default/error/components/ErrorPage.vue
Normal file
126
src/views/default/error/components/ErrorPage.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div class="error-page">
|
||||
<section class="error__container">
|
||||
<div class="error__img">
|
||||
<component class="error__icon" :is="IconMap[props.code]"></component>
|
||||
</div>
|
||||
|
||||
<div class="error__tip">
|
||||
<div class="error__tip--a">抱歉!</div>
|
||||
<div class="error__tip--b">当前页面不存在...</div>
|
||||
<div class="error__tip--c">请检查您输入的网址是否正确,或点击下面的按钮返回首页</div>
|
||||
<a-button type="primary" shape="round" size="large" @click="back">返回首页</a-button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Component } from 'vue'
|
||||
import Icon403 from '@/components/icons/Icon403.vue'
|
||||
import Icon404 from '@/components/icons/Icon404.vue'
|
||||
import Icon500 from '@/components/icons/Icon500.vue'
|
||||
|
||||
defineOptions({ name: 'ErrorPage' })
|
||||
|
||||
interface Props {
|
||||
code: number
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
code: 403
|
||||
})
|
||||
|
||||
const IconMap: Record<number, Component> = {
|
||||
403: Icon403,
|
||||
404: Icon404,
|
||||
500: Icon500
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
// 返回首页
|
||||
const back = () => {
|
||||
router.replace({ path: '/' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.error-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--color-bg-1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.error {
|
||||
&__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
&__img {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
&__icon {
|
||||
max-width: 90%;
|
||||
height: 50vh;
|
||||
}
|
||||
&__tip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
&--a {
|
||||
margin-bottom: 20px;
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: rgb(var(--primary-6));
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&--b {
|
||||
margin-bottom: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
color: var(--color-text-1);
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&--c {
|
||||
padding: 0 30px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
color: var(--color-text-2);
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(60px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
13
src/views/default/redirect/index.vue
Normal file
13
src/views/default/redirect/index.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const { params, query } = route
|
||||
const { path } = params
|
||||
|
||||
router.replace({ path: '/' + path, query })
|
||||
</script>
|
206
src/views/home/components/AccessTrend.vue
Normal file
206
src/views/home/components/AccessTrend.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<a-card title="访问趋势" :bordered="false" class="gi_card_title">
|
||||
<template #extra>
|
||||
<a-radio-group type="button" size="small" default-value="1">
|
||||
<a-radio value="1">近7天</a-radio>
|
||||
<a-radio value="2">近30天</a-radio>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
<VCharts :option="option" autoresize :style="{ height: '289px' }"></VCharts>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import VCharts from 'vue-echarts'
|
||||
import { graphic } from 'echarts'
|
||||
import { useChart } from '@/hooks'
|
||||
// import { ToolTipFormatterParams } from '@/types/echarts';
|
||||
|
||||
const xData = ref<string[]>([])
|
||||
const yData = ref<number[]>([])
|
||||
|
||||
function graphicFactory(side: AnyObject) {
|
||||
return {
|
||||
type: 'text',
|
||||
bottom: '8',
|
||||
...side,
|
||||
style: {
|
||||
text: '',
|
||||
textAlign: 'center',
|
||||
fill: '#4E5969',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
const graphicElements = ref([graphicFactory({ left: '2.6%' }), graphicFactory({ right: 0 })])
|
||||
const { option } = useChart(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: '40',
|
||||
right: '0',
|
||||
top: '10',
|
||||
bottom: '30'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 2,
|
||||
data: xData.value,
|
||||
boundaryGap: false,
|
||||
axisLabel: {
|
||||
color: '#4E5969',
|
||||
formatter(value: number, idx: number) {
|
||||
if (idx === 0) return ''
|
||||
if (idx === xData.value.length - 1) return ''
|
||||
return `${value}`
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
interval: (idx: number) => {
|
||||
if (idx === 0) return false
|
||||
if (idx === xData.value.length - 1) return false
|
||||
return true
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#E5E8EF'
|
||||
}
|
||||
},
|
||||
axisPointer: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#23ADFF',
|
||||
width: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
formatter(value: any, idx: number) {
|
||||
if (idx === 0) return value
|
||||
return `${value}k`
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
color: '#E5E8EF'
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter(params) {
|
||||
const [firstElement] = params as any[]
|
||||
return `<div>
|
||||
<p class="tooltip-title">${firstElement.axisValueLabel}</p>
|
||||
<div class="content-panel"><span>总内容量</span><span class="tooltip-value">${(
|
||||
Number(firstElement.value) * 10000
|
||||
).toLocaleString()}</span></div>
|
||||
</div>`
|
||||
},
|
||||
className: 'echarts-tooltip-diy'
|
||||
},
|
||||
graphic: {
|
||||
elements: graphicElements.value
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '浏览量(PV)',
|
||||
data: yData.value,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
// symbol: 'circle',
|
||||
symbolSize: 12,
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderWidth: 2
|
||||
}
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: new graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(30, 231, 255, 1)'
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: 'rgba(36, 154, 255, 1)'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(111, 66, 251, 1)'
|
||||
}
|
||||
])
|
||||
},
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(17, 126, 255, 0.16)'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(17, 128, 255, 0)'
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'IP数',
|
||||
data: yData.value,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
color: '#00B2FF',
|
||||
symbolSize: 12,
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderWidth: 2,
|
||||
borderColor: '#E2F2FF'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const getChartData = () => {
|
||||
try {
|
||||
const values = [100, 200, 150, 30, 100, 50, 10, 80]
|
||||
const year = new Date().getFullYear()
|
||||
const data = values.map((i, n) => {
|
||||
const m = n + 1
|
||||
const month = m >= 10 ? m : `0${m}`
|
||||
return { y: i, x: `${year}-${month}` }
|
||||
})
|
||||
data.forEach((item: any, index: number) => {
|
||||
xData.value.push(item.x)
|
||||
yData.value.push(item.y)
|
||||
if (index === 0) {
|
||||
graphicElements.value[0].style.text = item.x
|
||||
}
|
||||
if (index === data.length - 1) {
|
||||
graphicElements.value[1].style.text = item.x
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
}
|
||||
}
|
||||
getChartData()
|
||||
</script>
|
75
src/views/home/components/FastCard.vue
Normal file
75
src/views/home/components/FastCard.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<a-card title="快捷操作" :bordered="false" size="medium" class="card gi_card_title">
|
||||
<a-card-grid v-for="(item, index) in list" :key="item.name" class="card-grid-item" :style="{ width: '33.33%' }">
|
||||
<a-card :bordered="false" hoverable>
|
||||
<a-row justify="center" align="center" :class="'animated-fade-up-' + (index + 1)">
|
||||
<a-space direction="vertical" align="center" class="wrapper">
|
||||
<component :is="item.icon" :size="30" class="icon"></component>
|
||||
<a-typography-text class="text">{{ item.name }}</a-typography-text>
|
||||
</a-space>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</a-card-grid>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const list = [
|
||||
{ name: '用户管理', icon: 'icon-user' },
|
||||
{ name: '部门管理', icon: 'icon-mind-mapping' },
|
||||
{ name: '角色管理', icon: 'icon-user-group' },
|
||||
{ name: '通知公告', icon: 'icon-notification' },
|
||||
{ name: '系统配置', icon: 'icon-desktop' },
|
||||
{ name: '系统日志', icon: 'icon-history' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
overflow: hidden;
|
||||
:deep(.arco-card-header) {
|
||||
border: none;
|
||||
}
|
||||
.card-grid-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:deep(.text) {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
|
||||
:deep(.wrapper) {
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:last-child {
|
||||
.text {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.icon {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
}
|
||||
.text {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.icon) {
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-bottom: 4px;
|
||||
color: rgb(var(--gray-8));
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
54
src/views/home/components/MessageCard.vue
Normal file
54
src/views/home/components/MessageCard.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<a-card title="公告" :bordered="false" size="medium" class="gi_card_title">
|
||||
<template #extra>
|
||||
<a-link>更多</a-link>
|
||||
</template>
|
||||
<a-comment
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
align="right"
|
||||
:class="'animated-fade-up-' + index"
|
||||
style="overflow: hidden"
|
||||
>
|
||||
<template #content>
|
||||
<div class="content">
|
||||
<a-tag v-if="item.type === 1" color="blue" size="small">通知</a-tag>
|
||||
<a-tag v-if="item.type === 2" color="orangered" size="small">活动</a-tag>
|
||||
<a-tag v-if="item.type === 3" color="cyan" size="small">消息</a-tag>
|
||||
<p>{{ item.content }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</a-comment>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const list = [
|
||||
{ type: 1, content: 'v2.4.0 版本发布公告🎉' },
|
||||
{ type: 1, content: 'v2.3.0 版本发布公告🎉' },
|
||||
{ type: 1, content: 'v2.2.0 版本发布公告🎉' },
|
||||
{ type: 2, content: '作者喊你来贡献代码了~' },
|
||||
{ type: 2, content: '作者喊你来提需求了~' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.arco-comment:not(:first-of-type), .arco-comment-inner-comment) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
:deep(.arco-comment-content) {
|
||||
font-size: 14px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
:deep(.arco-comment-datetime) {
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
> p {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
42
src/views/home/components/NowTime/index.vue
Normal file
42
src/views/home/components/NowTime/index.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="now-time" v-if="time">
|
||||
<p class="now-time__time gi_line_1">{{ time }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Dayjs from 'dayjs'
|
||||
|
||||
defineOptions({ name: 'NowTime' })
|
||||
const time = ref('')
|
||||
|
||||
// 获取现在时间
|
||||
const getFormatNowTime = () => {
|
||||
const weekList = ['日', '一', '二', '三', '四', '五', '六']
|
||||
return `${Dayjs(new Date()).format('YYYY年MM月DD日 HH:mm:ss')} 星期${weekList[Dayjs(new Date()).day()]}`
|
||||
}
|
||||
|
||||
// 初始化时间
|
||||
const initTime = () => {
|
||||
time.value = getFormatNowTime()
|
||||
setInterval(() => {
|
||||
time.value = getFormatNowTime()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
initTime()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import url('@/assets/fonts/font.css');
|
||||
.now-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-family: DINPro-Medium;
|
||||
color: var(--color-text-1);
|
||||
&__time {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
111
src/views/home/components/ProjectCard.vue
Normal file
111
src/views/home/components/ProjectCard.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<a-card title="项目" :bordered="false" size="medium" class="gi_card_title" style="overflow: hidden">
|
||||
<a-row align="stretch">
|
||||
<a-col :xs="12" :sm="8" :md="8" v-for="(item, index) in list" :key="item.name">
|
||||
<a-card-grid class="w-full h-full">
|
||||
<a-card :bordered="false" hoverable :class="'animated-fade-up-' + index">
|
||||
<a :href="item.url" target="_blank">
|
||||
<section class="item">
|
||||
<div class="item__header">
|
||||
<img :src="item.logo" width="30px" alt="logo" />
|
||||
<span class="item__name gi_line_1">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="item__middle">
|
||||
<p class="item__desc gi_line_2">{{ item.desc }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</a>
|
||||
</a-card>
|
||||
</a-card-grid>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const list = [
|
||||
{
|
||||
name: 'ContiNew Admin',
|
||||
desc: '持续优化的前后端分离中后台管理框架,开箱即用,持续提供舒适的开发体验。',
|
||||
logo: 'https://doc.charles7c.top/logo.svg',
|
||||
url: 'https://gitee.com/continew/continew-admin'
|
||||
},
|
||||
{
|
||||
name: 'ContiNew Starter',
|
||||
desc: '包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken)。',
|
||||
logo: 'https://doc.charles7c.top/logo.svg',
|
||||
url: 'https://gitee.com/continew/continew-starter'
|
||||
},
|
||||
{
|
||||
name: 'ContiNew Admin UI - GI',
|
||||
desc: '基于 Gi Demo 前端模板开发的 ContiNew Admin 前端适配项目。',
|
||||
logo: 'https://doc.charles7c.top/logo.svg',
|
||||
url: 'https://gitee.com/continew/continew-admin-ui-gi'
|
||||
},
|
||||
{
|
||||
name: 'ContiNew Admin UI',
|
||||
desc: '基于 Arco Design Pro 前端模板开发的 ContiNew Admin 前端适配项目。',
|
||||
logo: 'https://doc.charles7c.top/logo.svg',
|
||||
url: 'https://gitee.com/continew/continew-admin-ui'
|
||||
},
|
||||
{
|
||||
name: 'ContiNew Cloud(孵化中)',
|
||||
desc: 'ContiNew Admin 微服务版本',
|
||||
logo: 'https://doc.charles7c.top/logo.svg',
|
||||
url: '#'
|
||||
},
|
||||
{
|
||||
name: 'charles7c.github.io',
|
||||
desc: '基于 VitePress 构建的个人知识库/博客,扩展 VitePress 默认主题。',
|
||||
logo: 'https://blog.charles7c.top/logo.png',
|
||||
url: 'https://github.com/Charles7c/charles7c.github.io'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.arco-card) {
|
||||
height: 100%;
|
||||
.arco-card-body {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-card-header) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.item {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
&__name {
|
||||
margin-left: 10px;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
color: var(--color-text-1);
|
||||
&:hover {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
}
|
||||
}
|
||||
&__middle {
|
||||
flex: 1;
|
||||
}
|
||||
&__desc {
|
||||
flex: 1;
|
||||
margin-top: 10px;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
&__footer {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-3);
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
37
src/views/home/components/Sponsor.vue
Normal file
37
src/views/home/components/Sponsor.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<a-card title="特别赞助" :bordered="false" size="medium" class="card gi_card_title">
|
||||
<template #extra>
|
||||
<a-link href="https://doc.charles7c.top/other/sponsor.html" target="_blank" rel="noopener">>></a-link>
|
||||
</template>
|
||||
<a-row :gutter="8">
|
||||
<a-carousel
|
||||
indicator-type="slider"
|
||||
show-arrow="always"
|
||||
auto-play
|
||||
style="width: 100%; height: 110px; border-radius: 4px; overflow: hidden"
|
||||
>
|
||||
<a-carousel-item v-for="(image, idx) in imageList" :key="idx">
|
||||
<a :href="image.link" target="_blank" :title="image.title">
|
||||
<img :src="image.src" style="width: 100%" :alt="image.title" />
|
||||
</a>
|
||||
</a-carousel-item>
|
||||
</a-carousel>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
interface ImageType {
|
||||
src: string;
|
||||
title: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
const imageList: ImageType[] = [
|
||||
{
|
||||
src: `https://doc.charles7c.top/img/sponsor/ad/roovps.jpg?${new Date().getTime()}`,
|
||||
title: 'ROOVPS',
|
||||
link: 'https://roovps.com/cart',
|
||||
},
|
||||
];
|
||||
</script>
|
50
src/views/home/components/WorkCard.vue
Normal file
50
src/views/home/components/WorkCard.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<a-card title="工作台" :bordered="false" size="medium" class="card">
|
||||
<template #extra>
|
||||
<NowTime />
|
||||
</template>
|
||||
<a-row align="center" wrap :gutter="[{ xs: 0, sm: 14, md: 14, lg: 14, xl: 14, xxl: 14 }, 16]" class="content">
|
||||
<a-col :xs="24" :sm="24" :md="14" :lg="16" :xl="16" :xxl="18">
|
||||
<a-space size="medium">
|
||||
<a-avatar :size="68">
|
||||
<img :src="userStore.avatar" alt="avatar" />
|
||||
</a-avatar>
|
||||
<div class="welcome">
|
||||
<p class="hello">{{ goodTimeText() }}!{{ userStore.name }}</p>
|
||||
<p>北海虽赊,扶摇可接;东隅已逝,桑榆非晚。</p>
|
||||
</div>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import NowTime from './NowTime/index.vue'
|
||||
import { useUserStore } from '@/stores'
|
||||
import { goodTimeText } from '@/utils'
|
||||
|
||||
const userStore = useUserStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.arco-statistic-title) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-top: $margin;
|
||||
.content {
|
||||
padding: 8px 20px;
|
||||
.welcome {
|
||||
margin: 8px 0;
|
||||
color: $color-text-3;
|
||||
.hello {
|
||||
font-size: 1.25rem;
|
||||
color: $color-text-1;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
48
src/views/home/index.vue
Normal file
48
src/views/home/index.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="gi_page home" id="home">
|
||||
<WorkCard />
|
||||
|
||||
<a-row class="home__content">
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="18" :xxl="20">
|
||||
<div class="home__item"><ProjectCard /></div>
|
||||
<div class="home__item"><AccessTrend /></div>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="6" :xxl="4">
|
||||
<div class="home__item"><FastCard /></div>
|
||||
<div class="home__item"><Sponsor /></div>
|
||||
<div class="home__item"><MessageCard /></div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-back-top :visible-height="100" target-container="#home">
|
||||
<GiSvgIcon name="backtop" :size="50" class="backtop-icon" />
|
||||
</a-back-top>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import WorkCard from './components/WorkCard.vue'
|
||||
import ProjectCard from './components/ProjectCard.vue'
|
||||
import AccessTrend from './components/AccessTrend.vue'
|
||||
import FastCard from './components/FastCard.vue'
|
||||
import MessageCard from './components/MessageCard.vue'
|
||||
import Sponsor from './components/Sponsor.vue'
|
||||
|
||||
defineOptions({ name: 'Home' })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home {
|
||||
padding: 0;
|
||||
&__content {
|
||||
padding: 6px;
|
||||
}
|
||||
&__item {
|
||||
padding: 6px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.backtop-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
80
src/views/login/components/LoginBg/index.vue
Normal file
80
src/views/login/components/LoginBg/index.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="login-bg">
|
||||
<div class="fly bg-fly-circle1"></div>
|
||||
<div class="fly bg-fly-circle2"></div>
|
||||
<div class="fly bg-fly-circle3"></div>
|
||||
<div class="fly bg-fly-circle4"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fly {
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
}
|
||||
.bg-fly-circle1 {
|
||||
left: 40px;
|
||||
top: 100px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(to right, rgba(var(--primary-6), 0.07) 0%, rgba(var(--primary-6), 0.04) 100%);
|
||||
animation: move 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.bg-fly-circle2 {
|
||||
left: 15%;
|
||||
bottom: 5%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(to right, rgba(var(--primary-6), 0.08) 0%, rgba(var(--primary-6), 0.04) 100%);
|
||||
animation: move 3s linear infinite;
|
||||
}
|
||||
|
||||
.bg-fly-circle3 {
|
||||
right: 12%;
|
||||
top: 90px;
|
||||
width: 145px;
|
||||
height: 145px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(to right, rgba(var(--primary-6), 0.1) 0%, rgba(var(--primary-6), 0.04) 100%);
|
||||
animation: move 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.bg-fly-circle4 {
|
||||
right: 5%;
|
||||
top: 60%;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(to right, rgba(var(--primary-6), 0.02) 0%, rgba(var(--primary-6), 0.04) 100%);
|
||||
animation: move 3.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
0% {
|
||||
transform: translateY(0px) scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(25px) scale(1.1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0px) scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
234
src/views/login/index.vue
Normal file
234
src/views/login/index.vue
Normal file
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<a-row align="stretch" class="login-box">
|
||||
<a-col :xs="0" :sm="12" :md="13">
|
||||
<div class="login-left">
|
||||
<img class="login-left__img" src="@/assets/images/banner.png" alt="banner" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="12" :md="11">
|
||||
<div class="login-right">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:style="{ width: '84%' }"
|
||||
:label-col-style="{ display: 'none' }"
|
||||
:wrapper-col-style="{ flex: 1 }"
|
||||
size="large"
|
||||
@submit="handleLogin"
|
||||
>
|
||||
<h3 class="login-right__title">
|
||||
<img class="logo" src="@/assets/images/logo.svg" alt="logo" /><span>ContiNew Admin</span>
|
||||
</h3>
|
||||
<a-form-item field="username">
|
||||
<a-input v-model="form.username" placeholder="请输入用户名" allow-clear>
|
||||
<template #prefix><icon-user :stroke-width="1" :style="{ fontSize: '16px' }" /></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="password">
|
||||
<a-input-password v-model="form.password" placeholder="请输入密码">
|
||||
<template #prefix><icon-lock :stroke-width="1" :style="{ fontSize: '16px' }" /></template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item field="captcha" hide-label>
|
||||
<a-input v-model="form.captcha" placeholder="请输入验证码" :max-length="4" allow-clear style="flex: 1 1">
|
||||
<template #prefix><icon-check-circle :stroke-width="1" :style="{ fontSize: '16px' }" /></template>
|
||||
</a-input>
|
||||
<img :src="captchaImgBase64" alt="验证码" class="captcha" @click="getCaptcha" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-row justify="space-between" align="center" class="w-full">
|
||||
<a-checkbox v-model="loginConfig.rememberMe">记住我</a-checkbox>
|
||||
<a-link>忘记密码</a-link>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space direction="vertical" fill class="w-full">
|
||||
<a-button type="primary" size="large" long :loading="loading" html-type="submit">登录</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<GiThemeBtn class="theme-btn"></GiThemeBtn>
|
||||
<LoginBg></LoginBg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getImageCaptcha } from '@/apis'
|
||||
import { Message, type FormInstance } from '@arco-design/web-vue'
|
||||
import LoginBg from './components/LoginBg/index.vue'
|
||||
import { useUserStore } from '@/stores'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { useLoading } from '@/hooks'
|
||||
import { encryptByRsa } from '@/utils/encrypt'
|
||||
|
||||
defineOptions({ name: 'Login' })
|
||||
|
||||
const loginConfig = useStorage('login-config', {
|
||||
rememberMe: true,
|
||||
username: 'admin', // 演示默认值
|
||||
password: 'admin123' // 演示默认值
|
||||
// username: debug ? 'admin' : '', // 演示默认值
|
||||
// password: debug ? 'admin123' : '', // 演示默认值
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const form = reactive({
|
||||
username: loginConfig.value.username,
|
||||
password: loginConfig.value.password,
|
||||
captcha: '',
|
||||
uuid: ''
|
||||
})
|
||||
|
||||
const rules: FormInstance['rules'] = {
|
||||
username: [{ required: true, message: '请输入用户名' }],
|
||||
password: [{ required: true, message: '请输入密码' }],
|
||||
captcha: [{ required: true, message: '请输入验证码' }]
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const { loading, setLoading } = useLoading()
|
||||
// 登录
|
||||
const handleLogin = async () => {
|
||||
try {
|
||||
const isInvalid = await formRef.value?.validate()
|
||||
if (isInvalid) return
|
||||
setLoading(true)
|
||||
await userStore.accountLogin({
|
||||
username: form.username,
|
||||
password: encryptByRsa(form.password) || '',
|
||||
captcha: form.captcha,
|
||||
uuid: form.uuid
|
||||
})
|
||||
const { redirect, ...othersQuery } = router.currentRoute.value.query
|
||||
router.push({
|
||||
path: (redirect as string) || '/',
|
||||
query: {
|
||||
...othersQuery
|
||||
}
|
||||
})
|
||||
const { rememberMe } = loginConfig.value
|
||||
loginConfig.value.username = rememberMe ? form.username : ''
|
||||
Message.success('欢迎使用')
|
||||
} catch (error) {
|
||||
getCaptcha()
|
||||
form.captcha = ''
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const captchaImgBase64 = ref()
|
||||
// 获取验证码
|
||||
const getCaptcha = () => {
|
||||
getImageCaptcha().then((res) => {
|
||||
form.uuid = res.data.uuid
|
||||
captchaImgBase64.value = res.data.img
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCaptcha()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--color-bg-5);
|
||||
&-box {
|
||||
width: 86%;
|
||||
max-width: 820px;
|
||||
height: 480px;
|
||||
display: flex;
|
||||
z-index: 999;
|
||||
box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.login-left {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(60deg, rgb(var(--primary-6)), rgb(var(--primary-3)));
|
||||
&__img {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
transition: all 0.3s;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.login-right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--color-bg-1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 30px;
|
||||
box-sizing: border-box;
|
||||
&__title {
|
||||
color: var(--color-text-1);
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
.arco-input-wrapper,
|
||||
:deep(.arco-select-view-single) {
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.arco-input-wrapper.arco-input-error {
|
||||
background-color: rgb(var(--danger-1));
|
||||
border-color: rgb(var(--danger-4));
|
||||
}
|
||||
.arco-input-wrapper :deep(.arco-input) {
|
||||
font-size: 13px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
|
||||
.theme-btn {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 30px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.captcha {
|
||||
width: 111px;
|
||||
height: 36px;
|
||||
margin: 0 0 0 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
99
src/views/setting/index.vue
Normal file
99
src/views/setting/index.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="setting" :class="{ 'setting--h5': !isDesktop }">
|
||||
<div class="setting__tabs">
|
||||
<a-tabs hide-content size="medium" :active-key="selectedKeys" @change="(key) => toPage(String(key))">
|
||||
<a-tab-pane v-for="item in list" :key="item.path" :title="item.name"> </a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
|
||||
<div class="setting__main">
|
||||
<div class="setting__main__menu">
|
||||
<a-menu :selected-keys="selectedKeys">
|
||||
<a-menu-item v-for="item in list" :key="item.path" @click="toPage(item.path)">
|
||||
<span>{{ item.name }}</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
<div class="setting__main__content">
|
||||
<ParentView></ParentView>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDevice } from '@/hooks'
|
||||
|
||||
defineOptions({ name: 'Setting' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { isDesktop } = useDevice()
|
||||
|
||||
const selectedKeys = ref('')
|
||||
watch(
|
||||
() => route.path,
|
||||
(newPath) => {
|
||||
selectedKeys.value = newPath
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const list = [
|
||||
{ name: '基本信息', value: 1, path: '/setting/profile' },
|
||||
{ name: '安全设置', value: 2, path: '/setting/security' },
|
||||
{ name: '消息中心', value: 3, path: '/setting/notice' }
|
||||
]
|
||||
|
||||
const toPage = (path: string) => {
|
||||
router.push({ path: path })
|
||||
selectedKeys.value = path
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__tabs {
|
||||
display: none;
|
||||
background-color: var(--color-bg-1);
|
||||
}
|
||||
|
||||
&__main {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
&__menu {
|
||||
width: 200px;
|
||||
margin-top: $margin;
|
||||
margin-left: $margin;
|
||||
}
|
||||
&__content {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
padding: $margin;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting--h5 {
|
||||
flex-direction: column;
|
||||
.setting__tabs {
|
||||
display: block;
|
||||
}
|
||||
.setting__main__menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-menu-vertical .arco-menu-inner) {
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
21
src/views/setting/notice/index.vue
Normal file
21
src/views/setting/notice/index.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
|
||||
defineOptions({ name: 'Notification' })
|
||||
|
||||
const route = useRoute()
|
||||
const form = reactive({ name: '' })
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
padding: $padding;
|
||||
background-color: var(--color-bg-1);
|
||||
}
|
||||
</style>
|
163
src/views/setting/profile/RightBox.vue
Normal file
163
src/views/setting/profile/RightBox.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<div class="right-box">
|
||||
<section class="right-box__header">
|
||||
<a-avatar :size="60" :trigger-icon-style="{ color: '#3491FA' }">
|
||||
<img :src="userStore.avatar" />
|
||||
<template #trigger-icon>
|
||||
<IconCamera />
|
||||
</template>
|
||||
</a-avatar>
|
||||
<section class="username">{{ userStore.name }}</section>
|
||||
<ul class="list">
|
||||
<li><icon-user /><span>前端开发工程师</span></li>
|
||||
<li><icon-safe /><span>前端</span></li>
|
||||
<li><icon-location /><span>广州</span></li>
|
||||
</ul>
|
||||
<a-button type="primary" class="edit-btn"
|
||||
><template #icon> <icon-edit /> </template>编辑信息</a-button
|
||||
>
|
||||
</section>
|
||||
|
||||
<a-tabs hide-content default-active-key="2">
|
||||
<a-tab-pane key="1">
|
||||
<template #title>文章</template>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2">
|
||||
<template #title>项目</template>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3">
|
||||
<template #title>应用(3)</template>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
<section class="right-box__comment">
|
||||
<a-comment
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:author="item.name"
|
||||
datetime="1个小时之前"
|
||||
align="right"
|
||||
class="comment-item"
|
||||
>
|
||||
<template #actions>
|
||||
<a-space :size="20">
|
||||
<span class="action" key="heart">
|
||||
<span><IconHeart /></span>
|
||||
<span>83</span>
|
||||
</span>
|
||||
<span class="action" key="star">
|
||||
<span><IconStar /></span>
|
||||
<span>3</span>
|
||||
</span>
|
||||
<span class="action" key="reply"> <IconMessage /><span>回复</span></span>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #avatar>
|
||||
<a-avatar>
|
||||
<img alt="avatar" :src="item.avatar" />
|
||||
</a-avatar>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="text">{{ item.text }}</div>
|
||||
</template>
|
||||
</a-comment>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/stores'
|
||||
const userStore = useUserStore()
|
||||
|
||||
const list = [
|
||||
{
|
||||
avatar:
|
||||
'https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp',
|
||||
name: 'Lin',
|
||||
text: '生活会让你苦上一阵子,等你适应以后,再让你苦上一辈子'
|
||||
},
|
||||
{
|
||||
avatar:
|
||||
'https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp',
|
||||
name: 'Lin',
|
||||
text: '我从一无所有,到资产过亿,从家徒四壁,到豪车别墅,这些不是靠的别人,完全是靠我自己,一点一滴,想出来的'
|
||||
},
|
||||
{
|
||||
avatar:
|
||||
'https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp',
|
||||
name: 'Lin',
|
||||
text: '有很多事情你当时想不通,别着急,过一段时间你再想,就想不起来了'
|
||||
},
|
||||
{
|
||||
avatar:
|
||||
'https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp',
|
||||
name: 'Lin',
|
||||
text: '⽐你优秀的⼈都⽐你努⼒,你努力还有什么用'
|
||||
},
|
||||
{
|
||||
avatar:
|
||||
'https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp',
|
||||
name: '窃·格瓦拉',
|
||||
text: '打工这辈子是不可能打工的,做生意又不会做,就是偷这种东西,才可以维持生活这样子'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.edit-btn {
|
||||
color: #fff;
|
||||
border-color: #fff;
|
||||
background: transparent;
|
||||
&:hover {
|
||||
background: rgb(var(--primary-5));
|
||||
border-color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
|
||||
.right-box {
|
||||
flex: 1;
|
||||
background-color: var(--color-bg-1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
&__header {
|
||||
min-height: 204px;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-white);
|
||||
background-color: rgb(var(--primary-6));
|
||||
.username {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
> li {
|
||||
margin-right: 15px;
|
||||
span {
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&__comment {
|
||||
flex: 1;
|
||||
padding: 20px 30px;
|
||||
padding-left: 16px;
|
||||
overflow: auto;
|
||||
.comment-item {
|
||||
margin-bottom: 15px;
|
||||
.text {
|
||||
color: $color-text-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
139
src/views/setting/profile/index.vue
Normal file
139
src/views/setting/profile/index.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div class="user">
|
||||
<div class="user__info">
|
||||
<a-row>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="10" :xl="8" :xxl="7">
|
||||
<section class="user-card">
|
||||
<div class="user-card__header">
|
||||
<a-avatar :size="60" :trigger-icon-style="{ color: '#3491FA' }">
|
||||
<img :src="userStore.avatar" />
|
||||
<template #trigger-icon>
|
||||
<IconCamera />
|
||||
</template>
|
||||
</a-avatar>
|
||||
<div class="name">{{ userStore.name }}</div>
|
||||
<p class="desc">尘缘已定,不念过往</p>
|
||||
</div>
|
||||
|
||||
<ul class="user-card__list">
|
||||
<li class="list-item">
|
||||
<span class="icon"><icon-bookmark :stroke-width="1" :size="16" /></span>
|
||||
<span>前端工程师</span>
|
||||
</li>
|
||||
<li class="list-item">
|
||||
<span class="icon"><icon-branch :stroke-width="1" :size="16" /></span
|
||||
><span>中台-数据平台团队-前端创新团队-前端架构和平台工具团队</span>
|
||||
</li>
|
||||
<li class="list-item">
|
||||
<span class="icon"><icon-location :stroke-width="1" :size="16" /></span><span>广州市</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a-row justify="space-around" class="user-card__images">
|
||||
<img src="https://file.iviewui.com/admin-pro-dist/img/icon-social-weibo.cbf658a0.svg" />
|
||||
<img src="https://file.iviewui.com/admin-pro-dist/img/icon-social-zhihu.1dc5a4ff.svg" />
|
||||
<img src="https://file.iviewui.com/admin-pro-dist/img/icon-social-facebook.e95df60e.svg" />
|
||||
<img src="https://file.iviewui.com/admin-pro-dist/img/icon-social-twitter.5db80e81.svg" />
|
||||
</a-row>
|
||||
|
||||
<a-divider style="border-bottom-style: dashed" />
|
||||
|
||||
<a-typography-title :heading="6">标签</a-typography-title>
|
||||
<a-space wrap :size="5">
|
||||
<a-tag>vue3</a-tag>
|
||||
<a-tag>pinia</a-tag>
|
||||
<a-tag>vite</a-tag>
|
||||
<a-tag>ts</a-tag>
|
||||
<a-tag>arco design</a-tag>
|
||||
</a-space>
|
||||
|
||||
<a-descriptions :column="1" style="margin-top: 20px">
|
||||
<a-descriptions-item label="星座">双鱼座</a-descriptions-item>
|
||||
<a-descriptions-item label="生日">07月16日</a-descriptions-item>
|
||||
<a-descriptions-item label="爱好">
|
||||
<a-space wrap :size="5">
|
||||
<a-tag color="purple">王者荣耀</a-tag>
|
||||
<a-tag color="magenta">旅行</a-tag>
|
||||
</a-space>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</section>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="14" :xl="16" :xxl="17">
|
||||
<RightBox></RightBox>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { useUserStore } from '@/stores'
|
||||
import RightBox from './RightBox.vue'
|
||||
|
||||
defineOptions({ name: 'Profile' })
|
||||
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user {
|
||||
background-color: var(--color-bg-1);
|
||||
&__alert {
|
||||
padding: $padding;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
&__info {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.user-card {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
padding: $padding;
|
||||
box-sizing: border-box;
|
||||
background: var(--color-bg-1);
|
||||
border-radius: 2px;
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.name {
|
||||
font-size: 20px;
|
||||
font-weight: bolder;
|
||||
line-height: 1.5;
|
||||
margin: 8px;
|
||||
color: $color-text-1;
|
||||
}
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
color: $color-text-3;
|
||||
}
|
||||
}
|
||||
&__list {
|
||||
margin-top: 20px;
|
||||
.list-item {
|
||||
padding-bottom: 16px;
|
||||
display: flex;
|
||||
> .icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__images {
|
||||
margin: 10px 0;
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
21
src/views/setting/security/index.vue
Normal file
21
src/views/setting/security/index.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
|
||||
defineOptions({ name: 'Security' })
|
||||
|
||||
const route = useRoute()
|
||||
const form = reactive({ name: '' })
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
padding: $padding;
|
||||
background-color: var(--color-bg-1);
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user