refactor: 重构工作台

This commit is contained in:
2024-10-20 15:03:34 +08:00
parent ae08678fa1
commit 7970940f50
17 changed files with 518 additions and 744 deletions

View File

@@ -1,5 +1,5 @@
<template>
<a-layout class="layout layout-default">
<a-layout class="layout layout-default" :class="{ mobile: isMobile }">
<Asider></Asider>
<a-layout class="layout-default-right">
<Header></Header>
@@ -17,9 +17,11 @@ import Main from './components/Main.vue'
import Tabs from './components/Tabs/index.vue'
import GiFooter from '@/components/GiFooter/index.vue'
import { useAppStore } from '@/stores'
import { useDevice } from '@/hooks'
defineOptions({ name: 'LayoutDefault' })
const appStore = useAppStore()
const { isMobile } = useDevice()
</script>
<style lang="scss" scoped>

View File

@@ -1,219 +0,0 @@
<template>
<a-spin :loading="loading" style="width: 100%">
<a-card title="访问趋势" :bordered="false" class="gi_card_title">
<template #extra>
<a-radio-group v-model:model-value="dateRange" type="button" size="small" @change="onChange">
<a-radio :value="7">近7天</a-radio>
<a-radio :value="30">近30天</a-radio>
</a-radio-group>
</template>
<VCharts :option="chartOption" autoresize :style="{ height: '326px' }"></VCharts>
</a-card>
</a-spin>
</template>
<script lang="ts" setup>
import VCharts from 'vue-echarts'
import { graphic } from 'echarts'
import { type DashboardAccessTrendResp, getDashboardAccessTrend as getData } from '@/apis'
import { useChart } from '@/hooks'
// 提示框
const tooltipItemsHtmlString = (items) => {
return items
.map(
(el) => `<div class="content-panel">
<p>
<span style="background-color: ${el.color}" class="tooltip-item-icon"></span>
<span>${el.seriesName}</span>
</p>
<span class="tooltip-value">
${el.value}
</span>
</div>`
)
.join('')
}
const xData = ref<string[]>([])
const pvStatisticsData = ref<number[]>([])
const ipStatisticsData = ref<number[]>([])
const { chartOption } = useChart((isDark) => {
return {
grid: {
left: '38',
right: '0',
top: '10',
bottom: '50'
},
legend: {
bottom: -3,
icon: 'circle',
textStyle: {
color: '#4E5969'
}
},
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
return idx !== xData.value.length - 1
},
lineStyle: {
color: isDark ? '#3F3F3F' : '#E5E8EF'
}
},
axisPointer: {
show: true,
lineStyle: {
color: '#23ADFF',
width: 2
}
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter(value: any, idx: number) {
if (idx === 0) return value
if (value >= 1000) {
return `${value / 1000}k`
}
return `${value}`
}
},
axisLine: {
show: false
},
splitLine: {
lineStyle: {
type: 'dashed',
color: isDark ? '#3F3F3F' : '#E5E8EF'
}
}
},
tooltip: {
show: true,
trigger: 'axis',
formatter(params) {
const [firstElement] = params
return `<div>
<p class="tooltip-title">${firstElement.axisValueLabel}</p>
${tooltipItemsHtmlString(params)}
</div>`
},
className: 'echarts-tooltip-diy'
},
series: [
{
name: '浏览量(PV)',
data: pvStatisticsData.value,
type: 'line',
smooth: true,
showSymbol: false,
color: '#246EFF',
symbol: 'circle',
symbolSize: 10,
emphasis: {
focus: 'series',
itemStyle: {
borderWidth: 2,
borderColor: '#E0E3FF'
}
},
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: ipStatisticsData.value,
type: 'line',
smooth: true,
showSymbol: false,
color: '#00B2FF',
symbol: 'circle',
symbolSize: 10,
emphasis: {
focus: 'series',
itemStyle: {
borderWidth: 2,
borderColor: '#E2F2FF'
}
},
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)'
}
])
}
}
]
}
})
const loading = ref(false)
const dateRange = ref(30)
// 查询图表数据
const getChartData = async (days: number) => {
try {
loading.value = true
xData.value = []
pvStatisticsData.value = []
ipStatisticsData.value = []
const { data } = await getData(days)
data.forEach((el: DashboardAccessTrendResp) => {
xData.value.push(el.date)
pvStatisticsData.value.push(el.pvCount)
ipStatisticsData.value.push(el.ipCount)
})
} finally {
loading.value = false
}
}
// 切换
const onChange = (days: number) => {
getChartData(days)
}
onMounted(() => {
getChartData(30)
})
</script>

View File

@@ -0,0 +1,33 @@
<template>
<a-carousel
indicator-type="slider"
show-arrow="hover"
auto-play
style="width: 100%; height: 150px; border-radius: 4px; overflow: hidden"
>
<a-carousel-item v-for="(image, idx) in images" :key="idx">
<div>
<a-link
:href="image.url"
target="_blank"
rel="noopener"
>
<img :src="`${image.src}?${new Date().getTime()}`" style="width: 100%" alt="" />
</a-link>
</div>
</a-carousel-item>
</a-carousel>
</template>
<script lang="ts" setup>
const images = [
{
src: 'https://continew.top/qrcode-text.png',
url: 'https://continew.top/about/intro.html'
},
{
src: 'https://continew.top/sponsor.png',
url: 'https://continew.top/sponsor.html'
}
]
</script>

View File

@@ -0,0 +1,42 @@
<template>
<a-card
class="general-card"
title="帮助文档"
:header-style="{ paddingBottom: 0 }"
:body-style="{ paddingTop: '5px' }"
style="height: 166px"
>
<template #extra>
<a-link href="https://continew.top" target="_blank" rel="noopener">更多</a-link>
</template>
<a-row>
<a-col v-for="link in links" :key="link.text" :span="12">
<a-link
:href="link.url"
target="_blank"
rel="noopener"
>
{{ link.text }}
</a-link>
</a-col>
</a-row>
</a-card>
</template>
<script setup lang="ts">
const links = [
{ text: '项目简介', url: 'https://continew.top/admin/intro/what-is.html' },
{ text: '快速开始', url: 'https://continew.top/admin/intro/quick-start.html' },
{ text: '常见问题', url: 'https://continew.top/faq.html' },
{ text: '更新日志', url: 'https://continew.top/admin/other/changelog.html' },
{ text: '贡献指南', url: 'https://continew.top/admin/other/contributing.html' },
{ text: '赞助支持 💖', url: 'https://continew.top/sponsor.html' }
]
</script>
<style lang="less" scoped>
.arco-card-body .arco-link {
margin: 5px 0;
color: rgb(var(--gray-8));
}
</style>

View File

@@ -1,77 +0,0 @@
<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" @click="router.replace({ path: item.path })">
<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 router = useRouter()
const list = [
{ name: '用户管理', icon: 'icon-user', path: '/system/user' },
{ name: '角色管理', icon: 'icon-user-group', path: '/system/role' },
{ name: '菜单管理', icon: 'icon-menu', path: '/system/menu' },
{ name: '文件管理', icon: 'icon-file', path: '/system/file' },
{ name: '系统配置', icon: 'icon-desktop', path: '/system/config' },
{ name: '系统日志', icon: 'icon-history', path: '/monitor/log' }
]
</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>

View File

@@ -1,54 +0,0 @@
<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>

View File

@@ -0,0 +1,89 @@
<template>
<a-card
class="general-card"
title="公告"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '15px 20px 13px 20px' }"
>
<template #extra>
<a-link @click="router.replace({ path: '/system/notice' })">更多</a-link>
</template>
<a-skeleton v-if="loading" :loading="loading" :animation="true">
<a-skeleton-line :rows="5" />
</a-skeleton>
<div v-else>
<a-empty v-if="dataList.length === 0">暂无公告</a-empty>
<div v-else>
<div v-for="(item, idx) in dataList" :key="idx" class="item">
<GiCellTag :value="item.type" :dict="notice_type" />
<a-link class="item-content" @click="onDetail(item.id)">
<a-typography-paragraph
:ellipsis="{
rows: 1,
showTooltip: true,
css: true,
}"
>
{{ item.title }}
</a-typography-paragraph>
</a-link>
</div>
</div>
</div>
</a-card>
<NoticeDetailModal ref="NoticeDetailModalRef" />
</template>
<script lang="ts" setup>
import { type DashboardNoticeResp, listDashboardNotice } from '@/apis'
import { useDict } from '@/hooks/app'
import NoticeDetailModal from '@/views/system/notice/NoticeDetailModal.vue'
const router = useRouter()
const { notice_type } = useDict('notice_type')
const dataList = ref<DashboardNoticeResp[]>([])
const loading = ref(false)
// 查询列表数据
const getDataList = async () => {
try {
loading.value = true
const res = await listDashboardNotice()
dataList.value = res.data
} finally {
loading.value = false
}
}
const NoticeDetailModalRef = ref<InstanceType<typeof NoticeDetailModal>>()
// 详情
const onDetail = (id: string) => {
NoticeDetailModalRef.value?.onDetail(id)
}
onMounted(() => {
getDataList()
})
</script>
<style scoped lang="less">
.item {
display: flex;
align-items: center;
width: 100%;
height: 24px;
margin-bottom: 4px;
.item-content {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 4px;
color: var(--color-text-2);
text-decoration: none;
font-size: 13px;
cursor: pointer;
}
}
</style>

View File

@@ -1,84 +0,0 @@
<template>
<a-card title="公告" :bordered="false" class="gi_card_title">
<template #extra>
<a-link href="/system/notice">更多</a-link>
</template>
<a-empty v-if="dataList.length === 0">暂无公告</a-empty>
<a-comment
v-for="(item, index) in dataList"
:key="index"
align="right"
:class="`animated-fade-up-${index}`"
style="overflow: hidden"
>
<template #content>
<a-space>
<GiCellTag :value="item.type" :dict="notice_type" />
<a-link @click="onDetail(item.id)">
<a-typography-paragraph
:ellipsis="{
rows: 1,
showTooltip: true,
css: true,
}"
>
{{ item.title }}
</a-typography-paragraph>
</a-link>
</a-space>
</template>
</a-comment>
</a-card>
<NoticeDetailModal ref="NoticeDetailModalRef" />
</template>
<script setup lang="ts">
import { type DashboardNoticeResp, listDashboardNotice } from '@/apis'
import { useDict } from '@/hooks/app'
import NoticeDetailModal from '@/views/system/notice/NoticeDetailModal.vue'
const { notice_type } = useDict('notice_type')
const dataList = ref<DashboardNoticeResp[]>([])
// 查询列表数据
const getDataList = async () => {
const res = await listDashboardNotice()
dataList.value = res.data
}
const NoticeDetailModalRef = ref<InstanceType<typeof NoticeDetailModal>>()
// 详情
const onDetail = (id: string) => {
NoticeDetailModalRef.value?.onDetail(id)
}
onMounted(() => {
getDataList()
})
</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);
}
.arco-link {
color: rgb(var(--gray-8));
}
.icon {
margin-right: 3px;
}
.update-time-row {
text-align: right;
}
</style>

View File

@@ -1,42 +0,0 @@
<template>
<div v-if="time" class="now-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>

View File

@@ -0,0 +1,157 @@
<template>
<a-card class="general-card" title="我的项目">
<template #extra>
<a-link href="https://github.com/charles7c" target="_blank" rel="noopener">更多</a-link>
</template>
<a-row :gutter="16">
<a-col
v-for="(item, index) in list"
:key="index"
:xs="12"
:sm="12"
:md="12"
:lg="12"
:xl="8"
:xxl="8"
class="my-project-item"
>
<a-card style="min-height: 204px" :bordered="true" hoverable>
<div class="badge badge-right" :style="`background-color: ${item.statusColor}`">{{ item.status }}</div>
<a :href="item.url" target="_blank">
<a-space direction="vertical">
<a-space>
<img :src="item.logo" width="30px" alt="logo" />
<a-typography-text bold>{{ item.name }}</a-typography-text>
</a-space>
<a-typography-paragraph
:ellipsis="{
rows: 6,
showTooltip: true,
css: true,
}"
>
<a-typography-text type="secondary">
{{ item.desc }}
</a-typography-text>
</a-typography-paragraph>
</a-space>
</a>
</a-card>
</a-col>
</a-row>
</a-card>
</template>
<script lang="ts" setup>
const list = [
{
name: 'ContiNew Admin',
desc: '🔥Almost最佳后端规范🔥持续迭代优化的前后端分离中后台管理系统框架开箱即用持续提供舒适的开发体验。当前采用技术栈Spring Boot3Java17、Vue3 & Arco Design、TS、Vite5 、Sa-Token、MyBatisPlus、Redisson、JetCache、Jackson、SpringDoc、Crane4j、Liquibase、Hutool 等。',
logo: 'https://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-admin',
status: '迭代',
statusColor: 'rgb(var(--primary-6))'
},
{
name: 'ContiNew Starter',
desc: '🔥高质量Starter🔥包含了一系列经过企业实践优化的依赖包如 MyBatis-Plus、SaToken可轻松集成到应用中为开发人员减少手动引入依赖及配置的麻烦为 Spring Boot Web 项目的灵活快速构建提供支持。',
logo: 'https://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-starter',
status: '迭代',
statusColor: 'rgb(var(--primary-6))'
},
{
name: 'ContiNew Admin UI',
desc: '全新 3.x 版本,基于 Gi Demo 前端模板开发的 ContiNew Admin 前端适配项目。',
logo: 'https://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-admin-ui',
status: '迭代',
statusColor: 'rgb(var(--primary-6))'
},
{
name: 'ContiNew Admin UI Arco',
desc: '2.5 版本,基于 Arco Design Pro 前端模板开发的 ContiNew Admin 前端适配项目。',
logo: 'https://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-admin-ui-arco',
status: '归档',
statusColor: 'rgb(var(--warning-6))'
},
{
name: 'ContiNew Cloud',
desc: 'ContiNew Admin 微服务版本。',
logo: 'https://continew.top/logo.svg',
url: '#',
status: '孵化',
statusColor: 'rgb(var(--danger-6))'
},
{
name: 'charles7c.github.io',
desc: '基于 VitePress 构建的个人知识库/博客。扩展 VitePress 默认主题增加ICP备案号、公安备案号显示增加文章元数据信息原创标识、作者、发布时间、分类、标签显示增加文末版权声明增加 Gitalk 评论功能,主页美化、自动生成侧边栏、文章内支持 Mermaid 流程图、MD公式、MD脚注、增加我的标签、我的归档等独立页面以及浏览器滚条等细节优化。查尔斯的个人技术知识库记录 & 分享个人碎片化、结构化、体系化的技术知识内容。',
logo: 'https://blog.charles7c.top/logo.png',
url: 'https://github.com/Charles7c/charles7c.github.io',
status: '归档',
statusColor: 'rgb(var(--warning-6))'
}
]
</script>
<style scoped lang="less">
:deep(.arco-card-body) {
min-height: 128px;
overflow: hidden;
position: relative;
.badge {
position: absolute;
font-size: 12px;
height: 18px;
line-height: 18px;
text-align: center;
width: 74px;
color: #fff;
}
.badge-left {
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
left: -18px;
top: 9px;
}
.badge-right {
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
right: -18px;
top: 9px;
}
}
.my-project {
&-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
&-title {
margin-top: 0 !important;
margin-bottom: 18px !important;
}
&-list {
display: flex;
justify-content: space-between;
}
&-item {
// padding-right: 16px;
margin-bottom: 16px;
&:last-child {
padding-right: 0;
}
}
}
</style>

View File

@@ -1,111 +0,0 @@
<template>
<a-card title="项目" :bordered="false" size="medium" class="gi_card_title" style="overflow: hidden">
<a-row align="stretch">
<a-col v-for="(item, index) in list" :key="item.name" :xs="12" :sm="8" :md="8">
<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://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-admin'
},
{
name: 'ContiNew Starter',
desc: '包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken。',
logo: 'https://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-starter'
},
{
name: 'ContiNew Admin UI',
desc: '全新 3.0 版本,基于 Gi Demo 前端模板开发的 ContiNew Admin 前端适配项目。',
logo: 'https://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-admin-ui'
},
{
name: 'ContiNew Admin UI Arco',
desc: '2.5 版本,基于 Arco Design Pro 前端模板开发的 ContiNew Admin 前端适配项目。',
logo: 'https://continew.top/logo.svg',
url: 'https://gitee.com/continew/continew-admin-ui-arco'
},
{
name: 'ContiNew Cloud孵化中',
desc: 'ContiNew Admin 微服务版本',
logo: 'https://continew.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>

View File

@@ -0,0 +1,40 @@
<template>
<a-card
class="general-card"
title="快捷操作"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '24px 20px 16px 20px' }"
>
<a-row :gutter="8">
<a-col
v-for="link in links"
:key="link.text"
:span="8"
class="wrapper"
@click="router.replace({ path: link.path })"
>
<div class="icon">
<GiSvgIcon :name="link.icon" />
</div>
<a-typography-paragraph class="text">
{{ link.text }}
</a-typography-paragraph>
</a-col>
</a-row>
</a-card>
</template>
<script lang="ts" setup>
const router = useRouter()
const links = [
{ text: '用户管理', icon: 'user', path: '/system/user' },
{ text: '角色管理', icon: 'user-group', path: '/system/role' },
{ text: '菜单管理', icon: 'menu', path: '/system/menu' },
{ text: '文件管理', icon: 'file', path: '/system/file' },
{ text: '代码生成', icon: 'code', path: '/tool/generator' },
{ text: '系统日志', icon: 'history', path: '/monitor/log' }
]
</script>
<style scoped lang="less"></style>

View File

@@ -1,34 +0,0 @@
<template>
<a-card title="特别赞助" :bordered="false" size="medium" class="card gi_card_title">
<template #extra>
<a-link href="https://continew.top/other/sponsor.html" target="_blank" rel="noopener">>></a-link>
</template>
<a-row :gutter="8">
<a-carousel
v-if="imageList.length"
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%; height: 100%" :alt="image.title" />
</a>
</a-carousel-item>
</a-carousel>
<a-empty v-else />
</a-row>
</a-card>
</template>
<script lang="ts" setup>
interface ImageType {
src: string
title: string
link: string
}
const imageList: ImageType[] = [
]
</script>

View File

@@ -1,19 +0,0 @@
<template>
<a-carousel
indicator-type="slider"
show-arrow="hover"
auto-play
style="width: 120px; height: 100px; border-radius: 4px; overflow: hidden"
>
<a-carousel-item v-for="(src, idx) in imageSrc" :key="idx">
<div style="padding-bottom: 10px; text-align: center">
<img :src="src" style="width: 80px" alt="QrCode" />
<div style="text-align: center">备注cnadmin进群</div>
</div>
</a-carousel-item>
</a-carousel>
</template>
<script lang="ts" setup>
const imageSrc = [`https://continew.top/qrcode.jpg?${new Date().getTime()}`]
</script>

View File

@@ -0,0 +1,43 @@
<template>
<a-card class="card" :bordered="false">
<a-row align="center" wrap :gutter="[{ xs: 0, sm: 14, md: 14, lg: 14, xl: 14, xxl: 14 }, 16]" class="content">
<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-row>
</a-card>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores'
import { goodTimeText } from '@/utils'
const userStore = useUserStore()
</script>
<style lang="scss" scoped>
:deep(.arco-statistic-title) {
margin-bottom: 0;
}
.card {
.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>

View File

@@ -1,57 +0,0 @@
<template>
<a-card title="工作台" :bordered="false" 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-col :xs="24" :sm="24" :md="10" :lg="8" :xl="8" :xxl="6" style="margin: -8px -7px">
<a-row justify="end">
<SupportCard v-if="isDesktop" />
</a-row>
</a-col>
</a-row>
</a-card>
</template>
<script setup lang="ts">
import NowTime from './NowTime/index.vue'
import SupportCard from './SupportCard.vue'
import { useDevice } from '@/hooks'
import { useUserStore } from '@/stores'
import { goodTimeText } from '@/utils'
const { isDesktop } = useDevice()
const userStore = useUserStore()
</script>
<style lang="scss" scoped>
:deep(.arco-statistic-title) {
margin-bottom: 0;
}
.card {
.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>

View File

@@ -1,63 +1,128 @@
<template>
<div id="home" class="gi_page home">
<WorkCard />
<a-alert>
全新版本 v3.3.0 已发布重构全局响应处理点击查看
<span class="link" @click="open('https://continew.top/admin/other/changelog.html')">更新日志</span>
</a-alert>
<a-row class="home__content">
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="18" :xxl="18">
<div class="home__item"><ProjectCard /></div>
<div class="home__item"><AccessTrendCard /></div>
</a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="6" :xxl="6">
<div class="home__item"><FastCard /></div>
<div class="home__item"><SponsorCard /></div>
<div class="home__item"><NoticeCard /></div>
</a-col>
</a-row>
<a-back-top :visible-height="100" target-container="#home" />
<div class="gi_page container">
<div class="left-side">
<div class="panel">
<Welcome />
</div>
<div style="margin-top: 14px">
<a-grid :cols="24" :col-gap="14" :row-gap="14">
<a-grid-item :span="24">
<Project />
</a-grid-item>
</a-grid>
</div>
</div>
<div class="right-side">
<a-grid :cols="24" :row-gap="14">
<a-grid-item :span="24">
<div class="panel moduler-wrap">
<QuickOperation />
</div>
</a-grid-item>
<a-grid-item class="panel" :span="24">
<Carousel />
</a-grid-item>
<a-grid-item class="panel" :span="24">
<Notice />
</a-grid-item>
<a-grid-item class="panel" :span="24">
<Docs />
</a-grid-item>
</a-grid>
</div>
</div>
</template>
<script setup lang="ts">
import WorkCard from './components/WorkCard.vue'
import ProjectCard from './components/ProjectCard.vue'
import AccessTrendCard from './components/AccessTrendCard.vue'
import FastCard from './components/FastCard.vue'
import NoticeCard from './components/NoticeCard.vue'
import SponsorCard from './components/SponsorCard.vue'
import Welcome from './components/Welcome.vue'
import Project from './components/Project.vue'
import QuickOperation from './components/QuickOperation.vue'
import Carousel from './components/Carousel.vue'
import Notice from './components/Notice.vue'
import Docs from './components/Docs.vue'
defineOptions({ name: 'Workplace' })
const open = (url: string) => {
window.open(url, '_blank')
}
</script>
<style lang="scss" scoped>
.home {
padding: 0;
span.link {
.container {
display: flex;
}
.left-side {
flex: 1;
}
.right-side {
width: 280px;
margin-left: 14px;
}
.panel {
background-color: var(--color-bg-2);
border-radius: 4px;
overflow: auto;
}
:deep(.panel-border) {
margin-bottom: 0;
border-bottom: 1px solid rgb(var(--gray-2));
}
.moduler-wrap {
border-radius: 4px;
background-color: var(--color-bg-2);
:deep(.text) {
font-size: 12px;
text-align: center;
color: rgb(var(--gray-8));
}
:deep(.wrapper) {
margin-bottom: 8px;
text-align: center;
cursor: pointer;
color: rgb(var(--arcoblue-6));
font-weight: bold;
&:last-child {
.text {
margin-bottom: 0;
}
}
&:hover {
opacity: 0.7;
.icon {
color: rgb(var(--arcoblue-6));
background-color: #e8f3ff;
}
.text {
color: rgb(var(--arcoblue-6));
}
}
}
&__content {
padding: 6px;
}
&__item {
padding: 6px;
box-sizing: border-box;
}
.backtop-icon {
cursor: pointer;
:deep(.icon) {
display: inline-block;
width: 32px;
height: 32px;
margin-bottom: 4px;
color: rgb(var(--dark-gray-1));
line-height: 32px;
font-size: 16px;
text-align: center;
background-color: rgb(var(--gray-1));
border-radius: 4px;
}
}
</style>
<style lang="less" scoped>
// responsive
.mobile {
.container {
display: block;
}
.right-side {
// display: none;
width: 100%;
margin-left: 0;
margin-top: 16px;
}
}
</style>