添加首页.

This commit is contained in:
lijiahang
2024-02-26 18:48:21 +08:00
parent b268db556f
commit b488bb326d
19 changed files with 157 additions and 917 deletions

View File

@@ -6,7 +6,10 @@
<div :key="item.slogan" class="carousel-item">
<div class="carousel-title">{{ item.slogan }}</div>
<div class="carousel-sub-title">{{ item.subSlogan }}</div>
<img class="carousel-image" :src="item.image" />
<img class="carousel-image usn"
draggable="false"
:src="item.image"
:alt="item.slogan" />
</div>
</a-carousel-item>
</a-carousel>

View File

@@ -1,40 +1,30 @@
<template>
<div class="login-form-wrapper">
<div class="login-form-title">{{ $t('login.form.title') }}</div>
<div class="login-form-title usn">{{ $t('login.form.title') }}</div>
<div class="login-form-error-msg">{{ errorMessage }}</div>
<a-form
ref="loginForm"
:model="userInfo"
class="login-form"
layout="vertical"
@submit="handleSubmit"
>
<a-form-item
field="username"
:rules="[{ required: true, message: $t('login.form.userName.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
>
<a-input
v-model="userInfo.username"
:placeholder="$t('login.form.userName.placeholder')"
>
<a-form ref="loginForm"
:model="userInfo"
class="login-form"
layout="vertical"
@submit="handleSubmit">
<a-form-item field="username"
:rules="[{ required: true, message: $t('login.form.userName.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label>
<a-input v-model="userInfo.username"
:placeholder="$t('login.form.userName.placeholder')">
<template #prefix>
<icon-user />
</template>
</a-input>
</a-form-item>
<a-form-item
field="password"
:rules="[{ required: true, message: $t('login.form.password.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
>
<a-input-password
v-model="userInfo.password"
:placeholder="$t('login.form.password.placeholder')"
allow-clear
>
<a-form-item field="password"
:rules="[{ required: true, message: $t('login.form.password.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label>
<a-input-password v-model="userInfo.password"
:placeholder="$t('login.form.password.placeholder')"
allow-clear>
<template #prefix>
<icon-lock />
</template>
@@ -52,7 +42,7 @@
<script lang="ts" setup>
import type { ValidatedError } from '@arco-design/web-vue/es/form/interface';
import type { LoginRequest } from '@/api/user/auth';
import { ref, reactive } from 'vue';
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue';
import { useI18n } from 'vue-i18n';

View File

@@ -7,10 +7,10 @@ export default {
'login.form.userName.placeholder': '用户名',
'login.form.password.placeholder': '密码',
'login.form.login': '登录',
'login.banner.slogan1': '开箱即用的高质量模板',
'login.banner.subSlogan1': '丰富的的页面模板,覆盖大多数典型业务场景',
'login.banner.slogan2': '内置了常见问题的解决方案',
'login.banner.subSlogan2': '国际化,路由配置,状态管理应有尽有',
'login.banner.slogan3': '接入可视化增强工具AUX',
'login.banner.subSlogan3': '实现灵活的区块式开发',
'login.banner.slogan1': '开箱即用的运维平台',
'login.banner.subSlogan1': '一站式操作 智能运维 让运维变得更简单',
'login.banner.slogan2': '内置权限角色管理',
'login.banner.subSlogan2': '让每一次操作都安全可控可追溯',
'login.banner.slogan3': '终端操作无障碍',
'login.banner.subSlogan3': '高效稳定 远程操作 让工作更高效',
};

View File

@@ -1,72 +0,0 @@
<template>
<a-card
class="general-card"
:title="$t('workplace.announcement')"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '15px 20px 13px 20px' }"
>
<template #extra>
<a-link>{{ $t('workplace.viewMore') }}</a-link>
</template>
<div>
<div v-for="(item, idx) in list" :key="idx" class="item">
<a-tag :color="item.type" size="small">{{ item.label }}</a-tag>
<span class="item-content">
{{ item.content }}
</span>
</div>
</div>
</a-card>
</template>
<script lang="ts" setup>
const list = [
{
type: 'orangered',
label: '活动',
content: '内容最新优惠活动',
},
{
type: 'cyan',
label: '消息',
content: '新增内容尚未通过审核,详情请点击查看。',
},
{
type: 'blue',
label: '通知',
content: '当前产品试用期即将结束,如需续费请点击查看。',
},
{
type: 'blue',
label: '通知',
content: '1月新系统升级计划通知',
},
{
type: 'cyan',
label: '消息',
content: '新增内容已经通过审核,详情请点击查看。',
},
];
</script>
<style lang="less" scoped>
.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

@@ -2,7 +2,7 @@
<a-col class="banner">
<a-col :span="8">
<a-typography-title :heading="5" style="margin-top: 0">
{{ $t('workplace.welcome') }} {{ userInfo.name }}
欢迎回来! {{ userInfo.name }}
</a-typography-title>
</a-col>
<a-divider class="panel-border" />

View File

@@ -1,24 +0,0 @@
<template>
<a-carousel
indicator-type="slider"
show-arrow="hover"
auto-play
style="width: 100%; height: 170px; border-radius: 4px; overflow: hidden"
>
<a-carousel-item v-for="(src, idx) in imageSrc" :key="idx">
<div>
<img :src="src" style="width: 100%" />
</div>
</a-carousel-item>
</a-carousel>
</template>
<script lang="ts" setup>
const imageSrc = [
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/5cc3cd1d994b7ef9db6a1f619a22addd.jpg~tplv-49unhts6dw-image.image',
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/f256cbcc287139e191fecea9d255a1f0.jpg~tplv-49unhts6dw-image.image',
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/b557ff0cd44146a2e471b477af2f30d0.jpg~tplv-49unhts6dw-image.image',
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/665106f4bbd2a2df96eaf7aec52f7bc3.jpg~tplv-49unhts6dw-image.image',
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/ea095a2c9c72b5d8f2f2818040db736d.jpg~tplv-49unhts6dw-image.image',
];
</script>

View File

@@ -1,115 +0,0 @@
<template>
<a-spin :loading="loading" style="width: 100%">
<a-card
class="general-card"
:header-style="{ paddingBottom: '0' }"
:body-style="{
padding: '20px',
}"
>
<template #title>
{{ $t('workplace.categoriesPercent') }}
</template>
<chart height="310px" :option="chartOption" />
</a-card>
</a-spin>
</template>
<script lang="ts" setup>
import useLoading from '@/hooks/loading';
import useChartOption from '@/hooks/chart-option';
const { loading } = useLoading();
const { chartOption } = useChartOption((isDark) => {
// echarts support https://echarts.apache.org/zh/theme-builder.html
// It's not used here
return {
legend: {
left: 'center',
data: ['纯文本', '图文类', '视频类'],
bottom: 0,
icon: 'circle',
itemWidth: 8,
textStyle: {
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969',
},
itemStyle: {
borderWidth: 0,
},
},
tooltip: {
show: true,
trigger: 'item',
},
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: '40%',
style: {
text: '内容量',
textAlign: 'center',
fill: isDark ? '#FFFFFFB3' : '#4E5969',
fontSize: 14,
},
},
{
type: 'text',
left: 'center',
top: '50%',
style: {
text: '928,531',
textAlign: 'center',
fill: isDark ? '#FFFFFFB3' : '#1D2129',
fontSize: 16,
fontWeight: 500,
},
},
],
},
series: [
{
type: 'pie',
radius: ['50%', '70%'],
center: ['50%', '50%'],
label: {
formatter: '{d}%',
fontSize: 14,
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969',
},
itemStyle: {
borderColor: isDark ? '#232324' : '#FFF',
borderWidth: 1,
},
data: [
{
value: [148564],
name: '纯文本',
itemStyle: {
color: isDark ? '#3D72F6' : '#249EFF',
},
},
{
value: [334271],
name: '图文类',
itemStyle: {
color: isDark ? '#A079DC' : '#313CA9',
},
},
{
value: [445694],
name: '视频类',
itemStyle: {
color: isDark ? '#6CAAF5' : '#21CCFF',
},
},
],
},
],
};
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -1,203 +0,0 @@
<template>
<a-spin :loading="loading" style="width: 100%">
<a-card
class="general-card"
:header-style="{ paddingBottom: 0 }"
:body-style="{
paddingTop: '20px',
}"
:title="$t('workplace.contentData')"
>
<template #extra>
<a-link>{{ $t('workplace.viewMore') }}</a-link>
</template>
<chart height="289px" :option="chartOption" />
</a-card>
</a-spin>
</template>
<script lang="ts" setup>
import type { ToolTipFormatterParams } from '@/types/echarts';
import type { AnyObject } from '@/types/global';
import type { ContentDataRecord } from '@/api/dashboard';
import { ref } from 'vue';
import { graphic } from 'echarts';
import useLoading from '@/hooks/loading';
import { queryContentData } from '@/api/dashboard';
import useChartOption from '@/hooks/chart-option';
function graphicFactory(side: AnyObject) {
return {
type: 'text',
bottom: '8',
...side,
style: {
text: '',
textAlign: 'center',
fill: '#4E5969',
fontSize: 12,
},
};
}
const { loading, setLoading } = useLoading(true);
const xAxis = ref<string[]>([]);
const chartsData = ref<number[]>([]);
const graphicElements = ref([
graphicFactory({ left: '2.6%' }),
graphicFactory({ right: 0 }),
]);
const { chartOption } = useChartOption(() => {
return {
grid: {
left: '2.6%',
right: '0',
top: '10',
bottom: '30',
},
xAxis: {
type: 'category',
offset: 2,
data: xAxis.value,
boundaryGap: false,
axisLabel: {
color: '#4E5969',
formatter(value: number, idx: number) {
if (idx === 0) return '';
if (idx === xAxis.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 === xAxis.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 ToolTipFormatterParams[];
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: '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)',
},
]),
},
},
],
};
});
const fetchData = async () => {
setLoading(true);
try {
const { data: chartData } = await queryContentData();
chartData.forEach((el: ContentDataRecord, idx: number) => {
xAxis.value.push(el.x);
chartsData.value.push(el.y);
if (idx === 0) {
graphicElements.value[0].style.text = el.x;
}
if (idx === chartData.length - 1) {
graphicElements.value[1].style.text = el.x;
}
});
} catch (err) {
// you can report use errorHandler or other
} finally {
setLoading(false);
}
};
fetchData();
</script>
<style lang="less" scoped>
</style>

View File

@@ -1,138 +0,0 @@
<template>
<a-grid :cols="24" :row-gap="16" class="panel">
<a-grid-item
class="panel-col"
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
>
<a-space>
<a-avatar :size="54" class="col-avatar">
<img
alt="avatar"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/288b89194e657603ff40db39e8072640.svg~tplv-49unhts6dw-image.image"
/>
</a-avatar>
<a-statistic
:title="$t('workplace.onlineContent')"
:value="373.5"
:precision="1"
:value-from="0"
animation
show-group-separator
>
<template #suffix>
W+ <span class="unit">{{ $t('workplace.pecs') }}</span>
</template>
</a-statistic>
</a-space>
</a-grid-item>
<a-grid-item
class="panel-col"
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
>
<a-space>
<a-avatar :size="54" class="col-avatar">
<img
alt="avatar"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/fdc66b07224cdf18843c6076c2587eb5.svg~tplv-49unhts6dw-image.image"
/>
</a-avatar>
<a-statistic
:title="$t('workplace.putIn')"
:value="368"
:value-from="0"
animation
show-group-separator
>
<template #suffix>
<span class="unit">{{ $t('workplace.pecs') }}</span>
</template>
</a-statistic>
</a-space>
</a-grid-item>
<a-grid-item
class="panel-col"
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
>
<a-space>
<a-avatar :size="54" class="col-avatar">
<img
alt="avatar"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/77d74c9a245adeae1ec7fb5d4539738d.svg~tplv-49unhts6dw-image.image"
/>
</a-avatar>
<a-statistic
:title="$t('workplace.newDay')"
:value="8874"
:value-from="0"
animation
show-group-separator
>
<template #suffix>
<span class="unit">{{ $t('workplace.pecs') }}</span>
</template>
</a-statistic>
</a-space>
</a-grid-item>
<a-grid-item
class="panel-col"
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
style="border-right: none"
>
<a-space>
<a-avatar :size="54" class="col-avatar">
<img
alt="avatar"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/c8b36e26d2b9bb5dbf9b74dd6d7345af.svg~tplv-49unhts6dw-image.image"
/>
</a-avatar>
<a-statistic
:title="$t('workplace.newFromYesterday')"
:value="2.8"
:precision="1"
:value-from="0"
animation>
<template #suffix> %
<icon-caret-up class="up-icon" />
</template>
</a-statistic>
</a-space>
</a-grid-item>
<a-grid-item :span="24">
<a-divider class="panel-border" />
</a-grid-item>
</a-grid>
</template>
<script lang="ts" setup>
</script>
<style lang="less" scoped>
.arco-grid.panel {
margin-bottom: 0;
padding: 16px 20px 0 20px;
}
.panel-col {
padding-left: 43px;
border-right: 1px solid rgb(var(--gray-2));
}
.col-avatar {
margin-right: 12px;
background-color: var(--color-fill-2);
}
.up-icon {
color: rgb(var(--red-6));
}
.unit {
margin-left: 8px;
color: rgb(var(--gray-8));
font-size: 12px;
}
:deep(.panel-border) {
margin: 4px 0 0 0;
}
</style>

View File

@@ -1,39 +1,28 @@
<template>
<a-card
class="general-card"
:title="$t('workplace.docs')"
:header-style="{ paddingBottom: 0 }"
:body-style="{ paddingTop: 0 }"
style="height: 166px"
>
<template #extra>
<a-link>{{ $t('workplace.viewMore') }}</a-link>
</template>
<a-card class="general-card"
title="帮助文档"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '8px 20px 8px 20px' }">
<a-row>
<a-col :span="12">
<a-link>
{{ $t('workplace.docs.productOverview') }}
</a-link>
<a-link target="_blank" href="https://github.com/lijiahangmax/orion-ops-pro">github</a-link>
</a-col>
<a-col :span="12">
<a-link>
{{ $t('workplace.docs.userGuide') }}
</a-link>
<a-link target="_blank" href="https://gitee.com/lijiahangmax/orion-ops-pro">gitee</a-link>
</a-col>
<a-col :span="12">
<a-link>
{{ $t('workplace.docs.workflow') }}
</a-link>
<a-link target="_blank" href="https://github.com/lijiahangmax/orion-ops-pro/blob/main/LICENSE">License</a-link>
</a-col>
<a-col :span="12">
<a-link>
{{ $t('workplace.docs.interfaceDocs') }}
</a-link>
<a-link target="_blank" href="https://lijiahangmax.gitee.io/orion-ops-pro">操作文档</a-link>
</a-col>
</a-row>
</a-card>
</template>
<script setup lang="ts">
</script>
<style lang="less" scoped>
.arco-card-body .arco-link {
margin: 10px 0;

View File

@@ -1,122 +0,0 @@
<template>
<a-spin :loading="loading" style="width: 100%">
<a-card
class="general-card"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '17px 20px 21px 20px' }"
>
<template #title>
{{ $t('workplace.popularContent') }}
</template>
<template #extra>
<a-link>{{ $t('workplace.viewMore') }}</a-link>
</template>
<a-space direction="vertical" :size="10" fill>
<a-radio-group
v-model:model-value="type"
type="button"
@change="s => typeChange(s as string)"
>
<a-radio value="text">
{{ $t('workplace.popularContent.text') }}
</a-radio>
<a-radio value="image">
{{ $t('workplace.popularContent.image') }}
</a-radio>
<a-radio value="video">
{{ $t('workplace.popularContent.video') }}
</a-radio>
</a-radio-group>
<a-table
:data="renderList"
:pagination="false"
:bordered="false"
:scroll="{ x: '100%', y: '264px' }"
>
<template #columns>
<a-table-column title="排名" data-index="key"></a-table-column>
<a-table-column title="内容标题" data-index="title">
<template #cell="{ record }">
<a-typography-paragraph
:ellipsis="{
rows: 1,
}"
>
{{ record.title }}
</a-typography-paragraph>
</template>
</a-table-column>
<a-table-column title="点击量" data-index="clickNumber">
</a-table-column>
<a-table-column
title="日涨幅"
data-index="increases"
:sortable="{
sortDirections: ['ascend', 'descend'],
}"
>
<template #cell="{ record }">
<div class="increases-cell">
<span>{{ record.increases }}%</span>
<icon-caret-up
v-if="record.increases !== 0"
style="color: #F53F3F; font-size: 8px"
/>
</div>
</template>
</a-table-column>
</template>
</a-table>
</a-space>
</a-card>
</a-spin>
</template>
<script lang="ts" setup>
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import { queryPopularList } from '@/api/dashboard';
const type = ref('text');
const { loading, setLoading } = useLoading();
const renderList = ref<TableData[]>([]);
const fetchData = async (contentType: string) => {
try {
setLoading(true);
const { data } = await queryPopularList({ type: contentType });
renderList.value = data;
} catch (err) {
// you can report use errorHandler or other
} finally {
setLoading(false);
}
};
const typeChange = (contentType: string) => {
fetchData(contentType);
};
fetchData('text');
</script>
<style lang="less" scoped>
.general-card {
min-height: 395px;
}
:deep(.arco-table-tr) {
height: 44px;
.arco-typography {
margin-bottom: 0;
}
}
.increases-cell {
display: flex;
align-items: center;
span {
margin-right: 4px;
}
}
</style>

View File

@@ -1,35 +1,57 @@
<template>
<a-card
class="general-card"
:title="$t('workplace.quick.operation')"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '24px 20px 0 20px' }"
>
<template #extra>
<a-link>{{ $t('workplace.quickOperation.setup') }}</a-link>
</template>
<a-card class="general-card"
title="快捷操作"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '20px 20px 0 20px' }">
<a-row :gutter="8">
<a-col v-for="link in links" :key="link.text" :span="8" class="wrapper">
<a-col v-for="link in links"
:key="link.meta.locale as string"
:span="8"
class="wrapper"
@click="openRoute($event, link)">
<div class="icon">
<component :is="link.icon" />
<component v-if="link.meta.icon" :is="link.meta.icon" />
</div>
<a-typography-paragraph class="text">
{{ $t(link.text) }}
{{ link.meta.locale }}
</a-typography-paragraph>
</a-col>
</a-row>
<a-divider class="split-line" style="margin: 0" />
</a-card>
</template>
<script lang="ts" setup>
const links = [
{ text: 'workplace.contentManagement', icon: 'icon-file' },
{ text: 'workplace.contentStatistical', icon: 'icon-storage' },
{ text: 'workplace.advanced', icon: 'icon-settings' },
{ text: 'workplace.onlinePromotion', icon: 'icon-mobile' },
{ text: 'workplace.contentPutIn', icon: 'icon-fire' },
];
import type { RouteRecordNormalized } from 'vue-router';
import { useRouter } from 'vue-router';
import { openWindow } from '@/utils';
import { useMenuStore } from '@/store';
const router = useRouter();
const { appMenus } = useMenuStore();
const links = appMenus.map(s => s.children)
.filter(s => s?.length)
.flat(1)
.filter(s => s.meta)
.map(s => s as RouteRecordNormalized)
.slice(0, 12);
// 打开路由
const openRoute = (e: any, route: RouteRecordNormalized) => {
// 新页面打开
if (route.meta.newWindow || e.ctrlKey) {
const { href } = router.resolve({
name: route.name as string,
});
openWindow(href);
return;
}
// 触发跳转
router.push({
name: route.name as string,
});
};
</script>
<style lang="less" scoped>

View File

@@ -1,44 +0,0 @@
<template>
<a-card
class="general-card"
:title="$t('workplace.recently.visited')"
:header-style="{ paddingBottom: '0' }"
:body-style="{ paddingTop: '26px' }"
>
<div style="margin-bottom: -1rem">
<a-row :gutter="8">
<a-col v-for="link in links" :key="link.text" :span="8" class="wrapper">
<div class="icon">
<component :is="link.icon" />
</div>
<a-typography-paragraph class="text">
{{ $t(link.text) }}
</a-typography-paragraph>
</a-col>
</a-row>
</div>
</a-card>
</template>
<script lang="ts" setup>
const links = [
{
text: 'workplace.contentManagement',
icon: 'icon-storage',
},
{
text: 'workplace.contentStatistical',
icon: 'icon-file',
},
{
text: 'workplace.advanced',
icon: 'icon-settings',
},
];
</script>
<style lang="less" scoped>
:deep(.arco-card-header-title) {
line-height: inherit;
}
</style>

View File

@@ -1,40 +1,34 @@
<template>
<div class="layout-container">
<div class="left-side">
<!-- 顶部 -->
<div class="top-side">
<!-- 提示 -->
<div class="panel">
<Banner />
<DataPanel />
<ContentChart />
</div>
<a-grid :cols="24" :col-gap="16" :row-gap="16" style="margin-top: 16px">
<a-grid-item
:span="{ xs: 24, sm: 24, md: 24, lg: 12, xl: 12, xxl: 12 }"
>
<PopularContent />
</a-grid-item>
<a-grid-item
:span="{ xs: 24, sm: 24, md: 24, lg: 12, xl: 12, xxl: 12 }"
>
<CategoriesPercent />
</a-grid-item>
</a-grid>
</div>
<div class="right-side">
<a-grid :cols="24" :row-gap="16">
<a-grid-item :span="24">
<div class="panel moduler-wrap">
<QuickOperation />
<RecentlyVisited />
</div>
<div class="row-wrapper">
<div class="left-side">
<!-- 操作日志 -->
<a-card class="general-card"
title="操作日志"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '8px 20px 8px 20px' }">
<operator-log-table :visible-user="false"
:visible-handle="false"
:current="true" />
</a-card>
</div>
<a-grid class="right-side"
:cols="24"
:row-gap="16">
<!-- 快捷操作 -->
<a-grid-item class="card-wrapper" :span="24">
<quick-operation />
</a-grid-item>
<!-- 文档 -->
<a-grid-item class="panel" :span="24">
<Carousel />
</a-grid-item>
<a-grid-item class="panel" :span="24">
<Announcement />
</a-grid-item>
<a-grid-item class="panel" :span="24">
<Docs />
<docs />
</a-grid-item>
</a-grid>
</div>
@@ -43,15 +37,9 @@
<script lang="ts" setup>
import Banner from './components/banner.vue';
import DataPanel from './components/data-panel.vue';
import ContentChart from './components/content-chart.vue';
import PopularContent from './components/popular-content.vue';
import CategoriesPercent from './components/categories-percent.vue';
import RecentlyVisited from './components/recently-visited.vue';
import QuickOperation from './components/quick-operation.vue';
import Announcement from './components/announcement.vue';
import Carousel from './components/carousel.vue';
import Docs from './components/docs.vue';
import OperatorLogTable from '@/views/user/operator-log/components/operator-log-table.vue';
</script>
<script lang="ts">
@@ -61,15 +49,24 @@
</script>
<style lang="less" scoped>
.left-side {
.top-side {
flex: 1;
overflow: auto;
}
.right-side {
width: 280px;
.row-wrapper {
margin-top: 16px;
width: 100%;
display: flex;
.left-side {
width: calc(100% - 296px);
margin-right: 16px;
}
.right-side {
width: 280px;
}
}
.panel {
@@ -83,7 +80,7 @@
border-bottom: 1px solid rgb(var(--gray-2));
}
.moduler-wrap {
.card-wrapper {
border-radius: 4px;
background-color: var(--color-bg-2);
@@ -130,19 +127,3 @@
}
}
</style>
<style lang="less" scoped>
// responsive
.mobile {
.layout-container {
display: block;
}
.right-side {
// display: none;
width: 100%;
margin-left: 0;
margin-top: 16px;
}
}
</style>

View File

@@ -1,37 +0,0 @@
export default {
'menu.dashboard.workplace': '工作台',
'workplace.welcome': '欢迎回来!',
'workplace.balance': '余额(元)',
'workplace.order.pending': '待支付',
'workplace.order.pendingRenewal': '待续费订单',
'workplace.onlineContent': '线上总内容',
'workplace.putIn': '投放中内容',
'workplace.newDay': '日新增评论',
'workplace.newFromYesterday': '较昨日新增',
'workplace.minute': '分钟',
'workplace.docs': '帮助文档',
'workplace.docs.productOverview': '产品概要',
'workplace.docs.userGuide': '使用指南',
'workplace.docs.workflow': '接入流程',
'workplace.docs.interfaceDocs': '接口文档',
'workplace.contentManagement': '内容管理',
'workplace.contentStatistical': '内容分析',
'workplace.advanced': '高级管理',
'workplace.onlinePromotion': '线上推广',
'workplace.contentPutIn': '内容投放',
'workplace.announcement': '公告',
'workplace.recently.visited': '最近访问',
'workplace.record.nodata': '暂无数据',
'workplace.quick.operation': '快捷操作',
'workplace.quickOperation.setup': '管理',
'workplace.allProject': '所有项目',
'workplace.loadMore': '加载更多',
'workplace.viewMore': '查看更多',
'workplace.contentData': '内容数据',
'workplace.popularContent': '线上热门内容',
'workplace.popularContent.text': '文本',
'workplace.popularContent.image': '图片',
'workplace.popularContent.video': '视频',
'workplace.categoriesPercent': '内容类型占比',
'workplace.pecs': '个',
};

View File

@@ -55,8 +55,8 @@
<script lang="ts" setup>
import type { OperatorLogQueryRequest, OperatorLogQueryResponse } from '@/api/user/operator-log';
import { ref, onMounted } from 'vue';
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey } from '../types/const';
import { ref, onMounted, onBeforeMount } from 'vue';
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey, dictKeys } from '../types/const';
import columns from '../types/table.columns';
import useLoading from '@/hooks/loading';
import { usePagination } from '@/types/table';
@@ -73,6 +73,10 @@
type: Boolean,
default: true
},
visibleHandle: {
type: Boolean,
default: true
},
current: {
type: Boolean,
default: false
@@ -141,12 +145,24 @@
doFetchTableData({ page, limit, ...form });
};
// 加载字典值
onBeforeMount(async () => {
const dictStore = useDictStore();
await dictStore.loadKeys(dictKeys);
});
// 初始化
onMounted(() => {
if (props.visibleUser) {
tableColumns.value = columns;
} else {
tableColumns.value = columns.filter(s => s.dataIndex !== 'username');
let cols = columns;
// 不显示用户
if (!props.visibleUser) {
cols = cols.filter(s => s.dataIndex !== 'username');
}
// 不显示操作
if (!props.visibleHandle) {
cols = cols.filter(s => s.dataIndex !== 'handle');
}
tableColumns.value = cols;
fetchTableData();
});

View File

@@ -1,5 +1,5 @@
<template>
<div class="layout-container" v-if="render">
<div class="layout-container">
<!-- 查询头 -->
<a-card class="general-card table-search-card">
<!-- 查询头组件 -->
@@ -31,30 +31,22 @@
</script>
<script lang="ts" setup>
import { ref, onBeforeMount, onUnmounted } from 'vue';
import { useCacheStore, useDictStore } from '@/store';
import { dictKeys } from './types/const';
import { ref, onUnmounted } from 'vue';
import { useCacheStore } from '@/store';
import OperatorLogQueryHeader from './components/operator-log-query-header.vue';
import OperatorLogTable from './components/operator-log-table.vue';
import JsonEditorModal from '@/components/view/json-editor/json-editor-modal.vue';
const cacheStore = useCacheStore();
const render = ref();
const table = ref();
const view = ref();
onBeforeMount(async () => {
// 加载字典值
const dictStore = useDictStore();
await dictStore.loadKeys(dictKeys);
render.value = true;
});
// 卸载时清除 cache
onUnmounted(() => {
cacheStore.reset('users');
});
</script>
<style lang="less" scoped>

View File

@@ -59,6 +59,7 @@ const columns = [
},
}, {
title: '操作',
dataIndex: 'handle',
slotName: 'handle',
width: 90,
align: 'center',