init Project

This commit is contained in:
2025-04-09 12:10:46 +08:00
parent 505d08443c
commit 75a1447d66
207 changed files with 26387 additions and 13 deletions

View File

@@ -0,0 +1,43 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<el-carousel class="-mt-2">
<el-carousel-item
class="cursor-pointer lg:h-40"
v-for="(item, index) in banners"
:key="index"
@click="openLink(item.link)"
>
<el-image class="h-full w-full" :src="item.img" fit="fill"></el-image>
</el-carousel-item>
</el-carousel>
</template>
<script setup>
import banner from '@/assets/banner.jpg'
import banner2 from '@/assets/banner2.jpg'
const openLink = (link) => {
window.open(link, '_blank')
}
const banners = [
{
img: banner,
link: 'https://gin-vue-admin.com/empower/index.html'
},
{
img: banner2,
link: 'https://plugin.gin-vue-admin.com'
},
{
img: 'https://qmplusimg.henrongyi.top/gvaDemo/k8s.jpg',
link: 'https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=42'
}
]
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,46 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div
class="bg-white dark:bg-slate-900 text-gray-800 dark:text-gray-400 rounded shadow"
:class="[customClass || '', withoutPadding ? 'p-0' : 'p-4']"
>
<div v-if="title" class="flex justify-between items-center">
<div class="text-base font-bold">
{{ title }}
</div>
<div v-if="showAction" class="text-sm text-active cursor-pointer">
查看更多
</div>
</div>
<div class="mt-2">
<slot />
</div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: ''
},
showAction: {
type: Boolean,
default: false
},
customClass: {
type: String,
default: ''
},
withoutPadding: {
type: Boolean,
default: false
}
})
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,189 @@
<!--
本组件参考 arco-pro 的实现 ts 改为 js 写法
https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/views/dashboard/workplace/components/content-chart.vue
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<Chart :height="height" :option="chartOption" />
</template>
<script setup>
import Chart from '@/components/charts/index.vue'
import useChartOption from '@/hooks/charts'
import { graphic } from 'echarts'
import { computed, ref } from 'vue'
import { useAppStore } from '@/pinia'
import { storeToRefs } from 'pinia'
const appStore = useAppStore()
const { config } = storeToRefs(appStore)
defineProps({
height: {
type: String,
default: '128px'
}
})
const dotColor = computed(() => {
return appStore.isDark ? '#333' : '#E5E8EF'
})
const graphicFactory = (side) => {
return {
type: 'text',
bottom: '8',
...side,
style: {
text: '',
textAlign: 'center',
fill: '#4E5969',
fontSize: 12
}
}
}
const xAxis = ref([
'2024-1',
'2024-2',
'2024-3',
'2024-4',
'2024-5',
'2024-6',
'2024-7',
'2024-8'
])
const chartsData = ref([12, 22, 32, 45, 32, 78, 89, 92])
const graphicElements = ref([
graphicFactory({ left: '5%' }),
graphicFactory({ right: 0 })
])
const { chartOption } = useChartOption(() => {
return {
grid: {
left: '40',
right: '0',
top: '10',
bottom: '30'
},
xAxis: {
type: 'category',
offset: 2,
data: xAxis.value,
boundaryGap: false,
axisLabel: {
color: '#4E5969',
formatter(value, idx) {
if (idx === 0) return ''
if (idx === xAxis.value.length - 1) return ''
return `${value}`
}
},
axisLine: {
show: false
},
axisTick: {
show: false
},
splitLine: {
show: true,
interval: (idx) => {
if (idx === 0) return false
if (idx === xAxis.value.length - 1) return false
return true
},
lineStyle: {
color: dotColor.value
}
},
axisPointer: {
show: true,
lineStyle: {
color: `${config.value.primaryColor}FF`,
width: 2
}
}
},
yAxis: {
type: 'value',
axisLine: {
show: false
},
axisLabel: {
formatter(value, idx) {
if (idx === 0) return value
return `${value}k`
}
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
color: dotColor.value
}
}
},
tooltip: {
trigger: 'axis',
formatter(params) {
const [firstElement] = params
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: [
{
data: chartsData.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: `${config.value.primaryColor}80`
},
{
offset: 0.5,
color: `${config.value.primaryColor}92`
},
{
offset: 1,
color: `${config.value.primaryColor}FF`
}
])
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: `${config.value.primaryColor}20`
},
{
offset: 1,
color: `${config.value.primaryColor}08`
}
])
}
}
]
}
})
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,138 @@
<!--
本组件参考 arco-pro 的实现 ts 改为 js 写法
https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/views/dashboard/workplace/components/content-chart.vue
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
@desc: 人数统计图表
!-->
<template>
<Chart :height="height" :option="chartOption" />
</template>
<script setup>
import Chart from '@/components/charts/index.vue'
import useChartOption from '@/hooks/charts'
import { graphic } from 'echarts'
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useAppStore } from '@/pinia'
const appStore = useAppStore()
const { config } = storeToRefs(appStore)
const prop = defineProps({
height: {
type: String,
default: '128px'
},
data: {
type: Array,
default: () => []
}
})
const graphicFactory = (side) => {
return {
type: 'text',
bottom: '8',
...side,
style: {
text: '',
textAlign: 'center',
fill: '#4E5969',
fontSize: 12
}
}
}
const graphicElements = ref([
graphicFactory({ left: '5%' }),
graphicFactory({ right: 0 })
])
const { chartOption } = useChartOption(() => {
return {
grid: {
left: '40',
right: '0',
top: '10',
bottom: '30'
},
xAxis: {
type: 'category',
offset: 2,
show: false,
boundaryGap: false,
axisLine: {
show: false
},
axisTick: {
show: false
},
splitLine: {
show: false
}
},
yAxis: {
type: 'value',
show: false,
axisLine: {
show: false
},
axisLabel: {
show: false
},
splitLine: {
show: false
}
},
graphic: {
elements: graphicElements.value
},
series: [
{
data: prop.data,
type: 'line',
smooth: true,
symbolSize: 12,
emphasis: {
focus: 'series',
itemStyle: {
borderWidth: 2
}
},
lineStyle: {
width: 3,
color: new graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: `${config.value.primaryColor}32`
},
{
offset: 0.5,
color: `${config.value.primaryColor}64`
},
{
offset: 1,
color: `${config.value.primaryColor}FF`
}
])
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: `${config.value.primaryColor}20`
},
{
offset: 1,
color: `${config.value.primaryColor}08`
}
])
}
}
]
}
})
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,54 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div class="">
<div class="flex items-center justify-between mb-2">
<div v-if="title" class="font-bold">
{{ title }}
</div>
<slot v-else name="title" />
</div>
<div class="w-full relative">
<div v-if="type !== 4">
<div class="mt-4 text-gray-600 text-3xl font-mono">
<el-statistic :value="268500" />
</div>
<div class="mt-2 text-green-600 text-sm font-bold font-mono">
+80% <el-icon><TopRight /></el-icon>
</div>
</div>
<div class="absolute top-0 right-2 w-[50%] h-20">
<charts-people-number v-if="type === 1" :data="data[0]" height="100%" />
<charts-people-number v-if="type === 2" :data="data[1]" height="100%" />
<charts-people-number v-if="type === 3" :data="data[2]" height="100%" />
</div>
<charts-content-number v-if="type === 4" height="14rem" />
</div>
</div>
</template>
<script setup>
import chartsPeopleNumber from './charts-people-numbers.vue'
import chartsContentNumber from './charts-content-numbers.vue'
defineProps({
type: {
type: Number,
default: 1
},
title: {
type: String,
default: ''
}
})
const data = [
[12, 22, 32, 45, 32, 78, 89, 92],
[1, 2, 43, 5, 67, 78, 89, 12],
[12, 22, 32, 45, 32, 78, 89, 92]
]
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,19 @@
import GvaBanner from './banner.vue'
import GvaCard from './card.vue'
import GvaChart from './charts.vue'
import GvaTable from './table.vue'
import GvaNotice from './notice.vue'
import GvaQuickLink from './quickLinks.vue'
import GvaWiki from './wiki.vue'
import GvaPluginTable from './pluginTable.vue'
export {
GvaBanner,
GvaCard,
GvaChart,
GvaTable,
GvaNotice,
GvaQuickLink,
GvaWiki,
GvaPluginTable
}

View File

@@ -0,0 +1,80 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<el-scrollbar>
<div
v-for="(item, index) in notices"
:key="index"
class="flex items-center mb-1.5 gap-3"
>
<el-tag :type="item.type" size="small">
{{ item.typeTitle }}
</el-tag>
<el-tooltip effect="light" :content="item.title" placement="top">
<div class="text-xs text-gray-700 dark:text-gray-300 line-clamp-1">
{{ item.title }}
</div>
</el-tooltip>
</div>
</el-scrollbar>
</template>
<script setup>
const notices = [
{
type: 'primary',
typeTitle: '公告',
title: '授权费将在从六月一日起结束第一价格梯度,进入第二价格梯度。'
},
{
type: 'success',
typeTitle: '通知',
title: '授权后将进入专属飞书群,获取官方辅助。'
},
{
type: 'warning',
typeTitle: '警告',
title: '授权可获得插件市场极大优惠价格。'
},
{
type: 'danger',
typeTitle: '违规',
title: '未授权商用将有可能被资源采集工具爬取并追责。'
},
{
type: 'info',
typeTitle: '信息',
title: '再次感谢您对开源事业的支持'
},
{
type: 'primary',
typeTitle: '公告',
title: '让创意更有价值。'
},
{
type: 'success',
typeTitle: '通知',
title: '让劳动更有意义。'
},
{
type: 'warning',
typeTitle: '警告',
title: '让思维更有深度。'
},
{
type: 'danger',
typeTitle: '错误',
title: '让生活更有趣味。'
},
{
type: 'info',
typeTitle: '信息',
title: '让公司更有活力。'
}
]
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,66 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div>
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column prop="ranking" label="排名" width="80" align="center" />
<el-table-column prop="title" label="插件标题" show-overflow-tooltip>
<template #default="{ row }">
<a class="text-active" :href="row.link" target="_blank">{{
row.title
}}</a>
</template>
</el-table-column>
<el-table-column prop="click_num" label="关注度" width="100" />
<el-table-column prop="hot" label="热度值" width="100" />
</el-table>
</div>
</template>
<script setup>
const tableData = [
{
ranking: 1,
title: '组织管理插件:更方便管理组织,分配资源权限。',
click_num: 523,
hot: 263,
link: 'https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=36'
},
{
ranking: 2,
title:
'Kubernetes容器管理:Kubernetes 原生资源管理提供炫酷的YAML 编辑Pod 终端方便运维兄弟管理k8s资源',
click_num: 416,
hot: 223,
link: 'https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=42'
},
{
ranking: 3,
title:
'定时任务配置化管理:本插件用于对系统内部的定时任务进行配置化管理可以配置自定义的函数和HTTP可以配置cron和remark等等',
click_num: 337,
hot: 176,
link: 'https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=67'
},
{
ranking: 4,
title:
'官网CMS系统基于Gin-Vue-Admin 和 插件市场客户端开发基座开发的企业官网类cms系统',
click_num: 292,
hot: 145,
link: 'https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=69'
},
{
ranking: 5,
title: '微信支付插件:提供扫码支付功能(需自行对接业务)',
click_num: 173,
hot: 110,
link: 'https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=28'
}
]
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,111 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div class="mt-8 w-full">
<div class="grid grid-cols-2 md:grid-cols-3 3xl:grid-cols-4">
<div
v-for="(item, index) in shortcuts"
:key="index"
class="flex flex-col items-center mb-3 group cursor-pointer"
@click="toPath(item)"
>
<div
class="w-8 h-8 rounded bg-gray-200 dark:bg-slate-500 flex items-center justify-center group-hover:bg-blue-400 group-hover:text-white"
>
<el-icon><component :is="item.icon" /></el-icon>
</div>
<div class="text-xs mt-2 text-gray-700 dark:text-gray-300">
{{ item.title }}
</div>
</div>
</div>
<div class="grid grid-cols-2 md:grid-cols-3 3xl:grid-cols-4 mt-8">
<div
v-for="(item, index) in recentVisits"
:key="index"
class="flex flex-col items-center mb-3 group cursor-pointer"
@click="openLink(item)"
>
<div
class="w-8 h-8 rounded bg-gray-200 dark:bg-slate-500 flex items-center justify-center group-hover:bg-blue-400 group-hover:text-white"
>
<el-icon><component :is="item.icon" /></el-icon>
</div>
<div class="text-xs mt-2 text-gray-700 dark:text-gray-300">
{{ item.title }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import {
Menu,
Link,
User,
Service,
Document,
Reading,
Files,
Memo
} from '@element-plus/icons-vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const toPath = (item) => {
router.push({ name: item.path })
}
const openLink = (item) => {
window.open(item.path, '_blank')
}
const shortcuts = [
{
icon: Menu,
title: '菜单管理',
path: 'menu'
},
{
icon: Link,
title: 'API管理',
path: 'api'
},
{
icon: Service,
title: '角色管理',
path: 'authority'
},
{
icon: User,
title: '用户管理',
path: 'user'
},
{
icon: Files,
title: '自动化包',
path: 'autoPkg'
},
{
icon: Memo,
title: '自动代码',
path: 'autoCode'
}
]
const recentVisits = [
{
icon: Reading,
title: '授权购买',
path: 'https://gin-vue-admin.com/empower/index.html'
},
{
icon: Document,
title: '插件市场',
path: 'https://plugin.gin-vue-admin.com/#/layout/home'
}
]
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,52 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div>
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column prop="ranking" label="排名" width="80" align="center" />
<el-table-column prop="title" label="内容标题" show-overflow-tooltip />
<el-table-column prop="click_num" label="关注度" width="100" />
<el-table-column prop="hot" label="热度值" width="100" />
</el-table>
</div>
</template>
<script setup>
const tableData = [
{
ranking: 1,
title: '更简洁的使用界面,更快速的操作体验',
click_num: 523,
hot: 263
},
{
ranking: 2,
title: '更优质的服务,更便捷的使用体验',
click_num: 416,
hot: 223
},
{
ranking: 3,
title: '更快速的创意实现,更高效的工作效率',
click_num: 337,
hot: 176
},
{
ranking: 4,
title: '更多的创意资源,更多的创意灵感',
click_num: 292,
hot: 145
},
{
ranking: 5,
title: '更合理的代码结构,更清晰的代码逻辑',
click_num: 173,
hot: 110
}
]
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,45 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div class="grid grid-cols-2 gap-2">
<a
v-for="item in wikis"
:key="item.url"
:href="item.url"
class="text-sm text-gray-700 dark:text-gray-300 no-underline hover:text-active"
target="_blank"
>
{{ item.title }}
</a>
</div>
</template>
<script setup>
const wikis = [
{
title: 'Vue3',
url: 'https://v3.cn.vuejs.org/guide/introduction.html'
},
{
title: 'GIN 文档',
url: 'https://gin-gonic.com/'
},
{
title: 'GVA 文档',
url: 'https://www.gin-vue-admin.com/'
},
{
title: '插件市场',
url: 'https://plugin.gin-vue-admin.com/'
},
{
title: 'github 仓库',
url: 'https://github.com/flipped-aurora/gin-vue-admin'
}
]
</script>
<style scoped lang="scss"></style>