数据源和 oss
This commit is contained in:
@@ -76,14 +76,6 @@ export const updateApi = (data) => {
|
||||
// @Param data body api.CreateApiParams true "更新api"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /api/setAuthApi [post]
|
||||
export const setAuthApi = (data) => {
|
||||
return service({
|
||||
url: '/api/setAuthApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Api
|
||||
// @Summary 获取所有的Api 不分页
|
||||
// @Security ApiKeyAuth
|
||||
|
||||
@@ -102,9 +102,3 @@ export const getSysErrorList = (params) => {
|
||||
// @Param data query systemReq.SysErrorSearch true "分页获取错误日志列表"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /sysError/getSysErrorPublic [get]
|
||||
export const getSysErrorPublic = () => {
|
||||
return service({
|
||||
url: '/sysError/getSysErrorPublic',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<template>
|
||||
<VCharts
|
||||
v-if="renderChart"
|
||||
:option="options"
|
||||
:autoresize="autoResize"
|
||||
:style="{ width, height }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick } from 'vue'
|
||||
import VCharts from 'vue-echarts'
|
||||
import { useWindowResize } from '@/hooks/use-windows-resize'
|
||||
|
||||
defineProps({
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
autoResize: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
}
|
||||
})
|
||||
const renderChart = ref(false)
|
||||
nextTick(() => {
|
||||
renderChart.value = true
|
||||
})
|
||||
useWindowResize(() => {
|
||||
renderChart.value = false
|
||||
nextTick(() => {
|
||||
renderChart.value = true
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
@@ -1,5 +1,5 @@
|
||||
// 基础组件库:以无样式的 reka-ui 原语为底座,仅用项目既有的 UnoCSS 主题 token 上色,
|
||||
// 跟随 themeStore 换肤 / 暗色模式。规范见 aiDoc/frontend-backend/component-library.md。
|
||||
// 跟随 themeStore 换肤与暗色模式。
|
||||
export * from './button'
|
||||
export * from './input'
|
||||
export * from './checkbox'
|
||||
|
||||
@@ -6,7 +6,6 @@ import * as ElIconModules from '@element-plus/icons-vue'
|
||||
import svgIcon from '@/components/svgIcon/svgIcon.vue'
|
||||
// 基础组件库(reka-ui 底座,src/core/componentLibrary):全局以 g- 前缀(kebab-case)注册,
|
||||
// 全站可直接 <g-button /> / <g-select /> 使用,无需逐文件 import。
|
||||
// 规范见 aiDoc/frontend-backend/component-library.md
|
||||
import * as ComponentLibrary from '@/core/componentLibrary'
|
||||
// 导入转换图标名称的函数
|
||||
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
<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 { useThemeStore } from '@/pinia'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { addOpacityToColor } from '@/theme/color'
|
||||
const themeStore = useThemeStore()
|
||||
const { settings } = storeToRefs(themeStore)
|
||||
defineProps({
|
||||
height: {
|
||||
type: String,
|
||||
default: '128px'
|
||||
}
|
||||
})
|
||||
const axisTextColor = computed(() => {
|
||||
return themeStore.isDark ? 'rgba(255,255,255,0.70)' : 'rgba(0,0,0,0.70)'
|
||||
})
|
||||
const dotColor = computed(() => {
|
||||
return themeStore.isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)'
|
||||
})
|
||||
const primaryColor = (opacity) => addOpacityToColor(settings.value.themeColor, opacity)
|
||||
const graphicFactory = (side) => {
|
||||
return {
|
||||
type: 'text',
|
||||
bottom: '8',
|
||||
...side,
|
||||
style: {
|
||||
text: '',
|
||||
textAlign: 'center',
|
||||
fill: axisTextColor.value,
|
||||
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: axisTextColor.value,
|
||||
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: primaryColor(1),
|
||||
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: primaryColor(0.5)
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: primaryColor(0.57)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(1)
|
||||
}
|
||||
])
|
||||
},
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: primaryColor(0.13)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(0.03)
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -1,132 +0,0 @@
|
||||
<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 { useThemeStore } from '@/pinia'
|
||||
import { addOpacityToColor } from '@/theme/color'
|
||||
const themeStore = useThemeStore()
|
||||
const { settings } = storeToRefs(themeStore)
|
||||
|
||||
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: themeStore.isDark ? '#FFFFFF' : '#000000',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
const graphicElements = ref([
|
||||
graphicFactory({ left: '5%' }),
|
||||
graphicFactory({ right: 0 })
|
||||
])
|
||||
const primaryColor = (opacity) => addOpacityToColor(settings.value.themeColor, opacity)
|
||||
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: primaryColor(0.2)
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: primaryColor(0.39)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(1)
|
||||
}
|
||||
])
|
||||
},
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: primaryColor(0.13)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(0.03)
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div v-if="title" class="text-sm font-semibold tracking-tight text-base-text">
|
||||
{{ title }}
|
||||
</div>
|
||||
<slot v-else name="title" />
|
||||
</div>
|
||||
<div class="w-full relative">
|
||||
<div v-if="type !== 4">
|
||||
<div class="mt-4 text-3xl font-mono text-base-text">
|
||||
<el-statistic :value="268500" />
|
||||
</div>
|
||||
<div class="mt-2 text-xs font-mono text-muted-foreground">
|
||||
+80% <el-icon class="align-middle"><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>
|
||||
@@ -1,11 +1,9 @@
|
||||
import KraCard from './card.vue'
|
||||
import KraChart from './charts.vue'
|
||||
import KraNotice from './notice.vue'
|
||||
import KraQuickLink from './quickLinks.vue'
|
||||
|
||||
export {
|
||||
KraCard,
|
||||
KraChart,
|
||||
KraNotice,
|
||||
KraQuickLink,
|
||||
}
|
||||
|
||||
@@ -30,37 +30,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const notices = [
|
||||
{
|
||||
typeTitle: '通知',
|
||||
time: '今天',
|
||||
title: 'Kra 管理后台已完成核心系统、公告和邮件模块接入。',
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { getInfoList } from '@/modules/announcement/api/info'
|
||||
const notices = ref([])
|
||||
onMounted(async () => {
|
||||
const response = await getInfoList({ page: 1, pageSize: 8, status: 1 })
|
||||
if (response.code !== 0) return
|
||||
notices.value = (response.data?.list || []).map((item) => ({
|
||||
typeTitle: item.type === 2 ? '公告' : '通知',
|
||||
time: item.createdAt ? new Date(item.createdAt).toLocaleDateString('zh-CN') : '',
|
||||
title: item.title,
|
||||
dotClass: 'bg-cyan-500',
|
||||
tagClass: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/40 dark:text-cyan-200'
|
||||
},
|
||||
{
|
||||
typeTitle: '安全',
|
||||
time: '2天前',
|
||||
title: '请在部署前修改 JWT 密钥并配置数据库连接信息。',
|
||||
dotClass: 'bg-emerald-500',
|
||||
tagClass: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200'
|
||||
},
|
||||
{
|
||||
typeTitle: '运维',
|
||||
time: '3天前',
|
||||
title: '邮件告警默认关闭,填写 SMTP 配置后自动启用。',
|
||||
dotClass: 'bg-amber-500',
|
||||
tagClass: 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-200'
|
||||
},
|
||||
{
|
||||
typeTitle: '存储',
|
||||
time: '5天前',
|
||||
title: '媒体文件默认存储在本地 uploads/file 目录。',
|
||||
dotClass: 'bg-violet-500',
|
||||
tagClass: 'bg-violet-100 text-violet-700 dark:bg-violet-900/40 dark:text-violet-200'
|
||||
}
|
||||
]
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,81 +1,38 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="h-full kra-container2 overflow-auto bg-main">
|
||||
<div class="space-y-2 py-2">
|
||||
<kra-card
|
||||
class="relative overflow-hidden rounded-xl border border-slate-200/80 bg-white px-5 py-6 shadow-sm dark:border-slate-700 dark:bg-slate-900"
|
||||
>
|
||||
|
||||
<div class="relative flex flex-col gap-2 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs tracking-[0.2em] text-muted-foreground">DASHBOARD</p>
|
||||
<h1 class="mt-2 text-xl font-semibold text-base-text lg:text-2xl">
|
||||
欢迎回来,开始今天的Coding节奏
|
||||
</h1>
|
||||
<p class="mt-2 text-sm text-muted-foreground">
|
||||
{{ today }} · 已为你聚合核心业务数据和系统公告
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<kra-card class="rounded-xl border border-slate-200/80 bg-white px-5 py-6 shadow-sm dark:border-slate-700 dark:bg-slate-900">
|
||||
<p class="text-xs tracking-[0.2em] text-muted-foreground">DASHBOARD</p>
|
||||
<h1 class="mt-2 text-xl font-semibold text-base-text lg:text-2xl">欢迎回来,{{ userStore.userInfo.nickName }}</h1>
|
||||
<p class="mt-2 text-sm text-muted-foreground">{{ today }} · 当前展示后台实时数据</p>
|
||||
</kra-card>
|
||||
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3">
|
||||
<kra-card>
|
||||
<kra-chart :type="1" title="访问人数" />
|
||||
</kra-card>
|
||||
<kra-card>
|
||||
<kra-chart :type="2" title="新增客户" />
|
||||
</kra-card>
|
||||
<kra-card>
|
||||
<kra-chart :type="3" title="解决数量" />
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<kra-card v-for="item in statistics" :key="item.title">
|
||||
<div class="p-2"><div class="text-sm text-muted-foreground">{{ item.title }}</div><div class="mt-3 text-3xl font-semibold text-base-text">{{ item.value }}</div></div>
|
||||
</kra-card>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 items-stretch gap-2 xl:grid-cols-12">
|
||||
<div class="grid grid-cols-1 gap-2 content-start xl:col-span-8 xl:h-full">
|
||||
<kra-card title="内容数据">
|
||||
<kra-chart :type="4" />
|
||||
</kra-card>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2 xl:col-span-4 xl:h-full">
|
||||
<kra-card title="快捷功能" show-action custom-class="min-h-[300px]">
|
||||
<kra-quick-link />
|
||||
</kra-card>
|
||||
<kra-card title="公告" show-action custom-class="min-h-[300px]">
|
||||
<kra-notice />
|
||||
</kra-card>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 items-stretch gap-2 xl:grid-cols-2">
|
||||
<kra-card title="快捷功能" show-action custom-class="min-h-[300px]"><kra-quick-link /></kra-card>
|
||||
<kra-card title="公告" show-action custom-class="min-h-[300px]"><kra-notice /></kra-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import {
|
||||
KraChart,
|
||||
KraNotice,
|
||||
KraQuickLink,
|
||||
KraCard
|
||||
} from './components'
|
||||
|
||||
const today = computed(() => {
|
||||
try {
|
||||
const d = new Date()
|
||||
return d.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit'
|
||||
})
|
||||
} catch (e) {
|
||||
return new Date().toISOString().slice(0, 10)
|
||||
}
|
||||
})
|
||||
|
||||
defineOptions({
|
||||
name: 'Dashboard'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/pinia/modules/user'
|
||||
import { getUserList } from '@/api/user'
|
||||
import { getSysOperationRecordList } from '@/api/sysOperationRecord'
|
||||
import { getFileList } from '@/api/fileUploadAndDownload'
|
||||
import { getInfoList } from '@/modules/announcement/api/info'
|
||||
import { KraNotice, KraQuickLink, KraCard } from './components'
|
||||
defineOptions({ name: 'Dashboard' })
|
||||
const userStore = useUserStore()
|
||||
const today = computed(() => new Date().toLocaleDateString('zh-CN'))
|
||||
const statistics = ref([{ title: '系统用户', value: '—' }, { title: '操作记录', value: '—' }, { title: '媒体文件', value: '—' }, { title: '系统公告', value: '—' }])
|
||||
onMounted(async () => {
|
||||
const results = await Promise.allSettled([getUserList({ page: 1, pageSize: 1 }), getSysOperationRecordList({ page: 1, pageSize: 1 }), getFileList({ page: 1, pageSize: 1 }), getInfoList({ page: 1, pageSize: 1 })])
|
||||
results.forEach((result, index) => { if (result.status === 'fulfilled' && result.value?.code === 0) statistics.value[index].value = result.value.data?.total ?? 0 })
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
<template>
|
||||
<!-- 工具按钮组:扁平图标,无独立描边/阴影,hover 时图标 morph 为文案(取代 tooltip)。 -->
|
||||
<div class="flex items-center gap-1 mx-3">
|
||||
<g-dropdown-menu
|
||||
v-if="isDev"
|
||||
trigger="hover"
|
||||
:items="videoList"
|
||||
@select="(item) => toDoc(item.value)"
|
||||
>
|
||||
<icon-button icon="lucide:clapperboard" label="教程" />
|
||||
</g-dropdown-menu>
|
||||
|
||||
<icon-button
|
||||
v-if="settings.header.search.visible"
|
||||
icon="lucide:search"
|
||||
@@ -50,8 +41,6 @@
|
||||
import { emitter } from '@/utils/bus.js'
|
||||
import CommandMenu from '@/components/commandMenu/index.vue'
|
||||
import IconButton from '@/components/iconButton/index.vue'
|
||||
import { toDoc } from '@/utils/doc'
|
||||
import { isDev } from '@/utils/env.js'
|
||||
|
||||
const themeStore = useThemeStore()
|
||||
const { settings } = storeToRefs(themeStore)
|
||||
@@ -95,62 +84,4 @@
|
||||
|
||||
initPage()
|
||||
|
||||
const videoList = [
|
||||
{
|
||||
label: '1.clone项目和安装依赖',
|
||||
value: 'https://www.bilibili.com/video/BV1jx4y1s7xx'
|
||||
},
|
||||
{
|
||||
label: '2.初始化项目',
|
||||
value: 'https://www.bilibili.com/video/BV1sr421K7sv'
|
||||
},
|
||||
{
|
||||
label: '3.开启调试工具+创建初始化包',
|
||||
value: 'https://www.bilibili.com/video/BV1iH4y1c7Na'
|
||||
},
|
||||
{
|
||||
label: '4.手动使用自动化创建功能',
|
||||
value: 'https://www.bilibili.com/video/BV1UZ421T7fV'
|
||||
},
|
||||
{
|
||||
label: '5.使用已有表格创建业务',
|
||||
value: 'https://www.bilibili.com/video/BV1NE4m1977s'
|
||||
},
|
||||
{
|
||||
label: '6.使用AI创建业务和创建数据源模式的可选项',
|
||||
value: 'https://www.bilibili.com/video/BV17i421a7DE'
|
||||
},
|
||||
{
|
||||
label: '7.创建自己的后端方法',
|
||||
value: 'https://www.bilibili.com/video/BV1Yw4m1k7fg'
|
||||
},
|
||||
{
|
||||
label: '8.新增一个前端页面',
|
||||
value: 'https://www.bilibili.com/video/BV12y411i7oE'
|
||||
},
|
||||
{
|
||||
label: '9.配置一个前端二级页面',
|
||||
value: 'https://www.bilibili.com/video/BV1ZM4m1y7i3'
|
||||
},
|
||||
{
|
||||
label: '10.配置一个前端菜单参数',
|
||||
value: 'https://www.bilibili.com/video/BV1WS42197DZ'
|
||||
},
|
||||
{
|
||||
label: '11.菜单参数实战+动态菜单标题+菜单高亮配置',
|
||||
value: 'https://www.bilibili.com/video/BV1NE4m1979c'
|
||||
},
|
||||
{
|
||||
label: '12.增加菜单可控按钮',
|
||||
value: 'https://www.bilibili.com/video/BV1Sw4m1k746'
|
||||
},
|
||||
{
|
||||
label: '14.新增客户角色和其相关配置教学',
|
||||
value: 'https://www.bilibili.com/video/BV1Ki421a7X2'
|
||||
},
|
||||
{
|
||||
label: '15.发布项目上线',
|
||||
value: 'https://www.bilibili.com/video/BV1Lx4y1s77D'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -32,6 +32,36 @@
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="对象存储" name="storage">
|
||||
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
|
||||
<el-form-item label="存储类型" class="md:col-span-2">
|
||||
<el-select v-model="config.admin.storage.type" class="!w-full">
|
||||
<el-option v-for="item in storageTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<template v-if="config.admin.storage.type === 'qiniu'">
|
||||
<el-form-item label="区域"><el-input v-model.trim="config.admin.storage.qiniu.zone" placeholder="ZoneHuadong" /></el-form-item>
|
||||
<el-form-item label="Bucket"><el-input v-model.trim="config.admin.storage.qiniu.bucket" /></el-form-item>
|
||||
<el-form-item label="访问域名"><el-input v-model.trim="config.admin.storage.qiniu.base_url" /></el-form-item>
|
||||
<el-form-item label="Access Key"><el-input v-model.trim="config.admin.storage.qiniu.access_key" /></el-form-item>
|
||||
<el-form-item label="Secret Key"><el-input v-model="config.admin.storage.qiniu.secret_key" show-password /></el-form-item>
|
||||
<el-form-item label="连接选项"><div class="flex gap-5"><el-switch v-model="config.admin.storage.qiniu.use_https" active-text="HTTPS" /><el-switch v-model="config.admin.storage.qiniu.use_cdn_domains" active-text="CDN 域名" /></div></el-form-item>
|
||||
</template>
|
||||
<template v-else-if="currentObjectStorage">
|
||||
<el-form-item label="Endpoint"><el-input v-model.trim="currentObjectStorage.endpoint" placeholder="可留空使用提供商默认地址" /></el-form-item>
|
||||
<el-form-item label="Region"><el-input v-model.trim="currentObjectStorage.region" /></el-form-item>
|
||||
<el-form-item label="Bucket"><el-input v-model.trim="currentObjectStorage.bucket" /></el-form-item>
|
||||
<el-form-item label="访问域名"><el-input v-model.trim="currentObjectStorage.base_url" /></el-form-item>
|
||||
<el-form-item label="Access Key"><el-input v-model.trim="currentObjectStorage.access_key" /></el-form-item>
|
||||
<el-form-item label="Secret Key"><el-input v-model="currentObjectStorage.secret_key" show-password /></el-form-item>
|
||||
<el-form-item label="对象前缀"><el-input v-model.trim="currentObjectStorage.path_prefix" /></el-form-item>
|
||||
<el-form-item v-if="config.admin.storage.type === 'cloudflare-r2'" label="Account ID"><el-input v-model.trim="currentObjectStorage.account_id" /></el-form-item>
|
||||
<el-form-item label="连接选项"><div class="flex gap-5"><el-switch v-model="currentObjectStorage.use_ssl" active-text="HTTPS" /><el-switch v-model="currentObjectStorage.force_path_style" active-text="Path Style" /></div></el-form-item>
|
||||
</template>
|
||||
<p class="md:col-span-2 text-sm text-gray-500">对象存储客户端在服务启动时创建,保存后需重启服务生效。</p>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="邮件设置" name="email">
|
||||
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
|
||||
<el-form-item label="默认收件人">
|
||||
@@ -68,7 +98,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getSystemConfig, reloadSystem, setSystemConfig } from '@/api/system'
|
||||
import { emailTest } from '@/api/email'
|
||||
@@ -82,13 +112,22 @@
|
||||
admin: {
|
||||
routerPrefix: '',
|
||||
jwt: { signingKey: '******', issuer: 'kra' },
|
||||
local: { storePath: 'uploads/file', pathPrefix: 'uploads/file' }
|
||||
local: { storePath: 'uploads/file', pathPrefix: 'uploads/file' },
|
||||
storage: { type: 'local', qiniu: {}, aliyun_oss: {}, huawei_obs: {}, tencent_cos: {}, aws_s3: {}, cloudflare_r2: {}, minio: {} }
|
||||
},
|
||||
email: {
|
||||
to: '', from: '', host: '', secret: '******', nickname: '', port: 465,
|
||||
'is-ssl': true, 'is-loginauth': false
|
||||
}
|
||||
})
|
||||
const storageTypes = [
|
||||
{ label: '本地存储', value: 'local' }, { label: '七牛云', value: 'qiniu' },
|
||||
{ label: '阿里云 OSS', value: 'aliyun-oss' }, { label: '华为云 OBS', value: 'huawei-obs' },
|
||||
{ label: '腾讯云 COS', value: 'tencent-cos' }, { label: 'AWS S3', value: 'aws-s3' },
|
||||
{ label: 'Cloudflare R2', value: 'cloudflare-r2' }, { label: 'MinIO', value: 'minio' }
|
||||
]
|
||||
const storageKeys = { 'aliyun-oss': 'aliyun_oss', 'huawei-obs': 'huawei_obs', 'tencent-cos': 'tencent_cos', 'aws-s3': 'aws_s3', 'cloudflare-r2': 'cloudflare_r2', minio: 'minio' }
|
||||
const currentObjectStorage = computed(() => config.value.admin.storage[storageKeys[config.value.admin.storage.type]])
|
||||
|
||||
const initForm = async () => {
|
||||
const res = await getSystemConfig()
|
||||
@@ -97,6 +136,15 @@
|
||||
admin: { ...config.value.admin, ...res.data.config.admin,
|
||||
jwt: { ...config.value.admin.jwt, ...res.data.config.admin?.jwt },
|
||||
local: { ...config.value.admin.local, ...res.data.config.admin?.local }
|
||||
, storage: { ...config.value.admin.storage, ...res.data.config.admin?.storage,
|
||||
qiniu: { ...config.value.admin.storage.qiniu, ...res.data.config.admin?.storage?.qiniu },
|
||||
aliyun_oss: { ...config.value.admin.storage.aliyun_oss, ...res.data.config.admin?.storage?.aliyun_oss },
|
||||
huawei_obs: { ...config.value.admin.storage.huawei_obs, ...res.data.config.admin?.storage?.huawei_obs },
|
||||
tencent_cos: { ...config.value.admin.storage.tencent_cos, ...res.data.config.admin?.storage?.tencent_cos },
|
||||
aws_s3: { ...config.value.admin.storage.aws_s3, ...res.data.config.admin?.storage?.aws_s3 },
|
||||
cloudflare_r2: { ...config.value.admin.storage.cloudflare_r2, ...res.data.config.admin?.storage?.cloudflare_r2 },
|
||||
minio: { ...config.value.admin.storage.minio, ...res.data.config.admin?.storage?.minio }
|
||||
}
|
||||
},
|
||||
email: { ...config.value.email, ...res.data.config.email }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user