首次提交
This commit is contained in:
7
cdtestplant/src/views/dashboard/monitor/index.vue
Normal file
7
cdtestplant/src/views/dashboard/monitor/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div><h2>整体项目情况</h2></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,71 @@
|
||||
<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 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>
|
||||
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<a-col class="banner">
|
||||
<a-col :span="8">
|
||||
<a-typography-title :heading="5" style="margin-top: 0">
|
||||
{{ $t('workplace.welcome') }} {{ userInfo.name }}
|
||||
</a-typography-title>
|
||||
</a-col>
|
||||
<a-divider class="panel-border" />
|
||||
</a-col>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { useUserStore } from '@/store'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => {
|
||||
return {
|
||||
name: userStore.name,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.banner {
|
||||
width: 100%;
|
||||
padding: 20px 20px 0 20px;
|
||||
background-color: var(--color-bg-2);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
:deep(.arco-icon-home) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<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>
|
||||
@@ -0,0 +1,114 @@
|
||||
<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 scoped lang="less"></style>
|
||||
@@ -0,0 +1,197 @@
|
||||
<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 { ref } from 'vue'
|
||||
import { graphic } from 'echarts'
|
||||
import useLoading from '@/hooks/loading'
|
||||
import { queryContentData, ContentDataRecord } from '@/api/dashboard'
|
||||
import useChartOption from '@/hooks/chart-option'
|
||||
import { ToolTipFormatterParams } from '@/types/echarts'
|
||||
import { AnyObject } from '@/types/global'
|
||||
|
||||
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 scoped lang="less"></style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<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>
|
||||
@@ -0,0 +1,42 @@
|
||||
<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-row>
|
||||
<a-col :span="12">
|
||||
<a-link>
|
||||
{{ $t('workplace.docs.productOverview') }}
|
||||
</a-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-link>
|
||||
{{ $t('workplace.docs.userGuide') }}
|
||||
</a-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-link>
|
||||
{{ $t('workplace.docs.workflow') }}
|
||||
</a-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-link>
|
||||
{{ $t('workplace.docs.interfaceDocs') }}
|
||||
</a-link>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.arco-card-body .arco-link {
|
||||
margin: 10px 0;
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,108 @@
|
||||
<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="typeChange as any">
|
||||
<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 { ref } from 'vue'
|
||||
import useLoading from '@/hooks/loading'
|
||||
import { queryPopularList } from '@/api/dashboard'
|
||||
import type { TableData } from '@arco-design/web-vue/es/table/interface'
|
||||
|
||||
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 scoped lang="less">
|
||||
.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>
|
||||
@@ -0,0 +1,35 @@
|
||||
<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-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>
|
||||
<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' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<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>
|
||||
144
cdtestplant/src/views/dashboard/workplace/index.vue
Normal file
144
cdtestplant/src/views/dashboard/workplace/index.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="left-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>
|
||||
</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 />
|
||||
</a-grid-item>
|
||||
</a-grid>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<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'
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Dashboard', // If you want the include property of keep-alive to take effect, you must name the component
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
background-color: var(--color-fill-2);
|
||||
padding: 16px 20px;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.left-side {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.right-side {
|
||||
width: 280px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
&:last-child {
|
||||
.text {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.icon {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
.text {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
: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>
|
||||
38
cdtestplant/src/views/dashboard/workplace/locale/en-US.ts
Normal file
38
cdtestplant/src/views/dashboard/workplace/locale/en-US.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
export default {
|
||||
'menu.dashboard.workplace': 'Workplace',
|
||||
'workplace.welcome': 'Welcome!',
|
||||
'workplace.balance': 'Balance (CNY)',
|
||||
'workplace.order.pending': 'Pending',
|
||||
'workplace.order.pendingRenewal': 'Renewal Order',
|
||||
'workplace.onlineContent': 'Online Content',
|
||||
'workplace.putIn': 'Put In',
|
||||
'workplace.newDay': 'Daily Additional Comments',
|
||||
'workplace.newFromYesterday': 'New From Yesterday',
|
||||
'workplace.minute': 'Min',
|
||||
'workplace.docs': 'Documents',
|
||||
'workplace.docs.productOverview': 'Product Overview',
|
||||
'workplace.docs.userGuide': 'User Guide',
|
||||
'workplace.docs.workflow': 'Workflow',
|
||||
'workplace.docs.interfaceDocs': 'Interface Docs',
|
||||
//
|
||||
'workplace.contentManagement': 'Content Management',
|
||||
'workplace.contentStatistical': 'Content Statistical',
|
||||
'workplace.advanced': 'Advanced',
|
||||
'workplace.onlinePromotion': 'Online Promotion',
|
||||
'workplace.contentPutIn': 'Put In',
|
||||
'workplace.announcement': 'Announcement',
|
||||
'workplace.recently.visited': 'Recently Visited',
|
||||
'workplace.record.nodata': 'No data',
|
||||
'workplace.quick.operation': 'Quick Operation',
|
||||
'workplace.quickOperation.setup': 'Setup',
|
||||
'workplace.allProject': 'All',
|
||||
'workplace.loadMore': 'More',
|
||||
'workplace.viewMore': 'More',
|
||||
'workplace.contentData': 'Content Data',
|
||||
'workplace.popularContent': 'Popular Content',
|
||||
'workplace.popularContent.text': 'text',
|
||||
'workplace.popularContent.image': 'image',
|
||||
'workplace.popularContent.video': 'video',
|
||||
'workplace.categoriesPercent': 'Categories Percent',
|
||||
'workplace.pecs': 'pecs',
|
||||
}
|
||||
37
cdtestplant/src/views/dashboard/workplace/locale/zh-CN.ts
Normal file
37
cdtestplant/src/views/dashboard/workplace/locale/zh-CN.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
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': '个',
|
||||
}
|
||||
129
cdtestplant/src/views/dashboard/workplace/mock.ts
Normal file
129
cdtestplant/src/views/dashboard/workplace/mock.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import Mock from 'mockjs'
|
||||
import qs from 'query-string'
|
||||
import dayjs from 'dayjs'
|
||||
import { GetParams } from '@/types/global'
|
||||
import setupMock, { successResponseWrap } from '@/utils/setup-mock'
|
||||
|
||||
const textList = [
|
||||
{
|
||||
key: 1,
|
||||
clickNumber: '346.3w+',
|
||||
title: '经济日报:财政政策要精准提升…',
|
||||
increases: 35,
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
clickNumber: '324.2w+',
|
||||
title: '双12遇冷,消费者厌倦了电商平…',
|
||||
increases: 22,
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
clickNumber: '318.9w+',
|
||||
title: '致敬坚守战“疫”一线的社区工作…',
|
||||
increases: 9,
|
||||
},
|
||||
{
|
||||
key: 4,
|
||||
clickNumber: '257.9w+',
|
||||
title: '普高还是职高?家长们陷入选择…',
|
||||
increases: 17,
|
||||
},
|
||||
{
|
||||
key: 5,
|
||||
clickNumber: '124.2w+',
|
||||
title: '人民快评:没想到“浓眉大眼”的…',
|
||||
increases: 37,
|
||||
},
|
||||
]
|
||||
const imageList = [
|
||||
{
|
||||
key: 1,
|
||||
clickNumber: '15.3w+',
|
||||
title: '杨涛接替陆慷出任外交部美大司…',
|
||||
increases: 15,
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
clickNumber: '12.2w+',
|
||||
title: '图集:龙卷风袭击美国多州房屋…',
|
||||
increases: 26,
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
clickNumber: '18.9w+',
|
||||
title: '52岁大姐贴钱照顾自闭症儿童八…',
|
||||
increases: 9,
|
||||
},
|
||||
{
|
||||
key: 4,
|
||||
clickNumber: '7.9w+',
|
||||
title: '杭州一家三口公园宿营取暖中毒',
|
||||
increases: 0,
|
||||
},
|
||||
{
|
||||
key: 5,
|
||||
clickNumber: '5.2w+',
|
||||
title: '派出所副所长威胁市民?警方调…',
|
||||
increases: 4,
|
||||
},
|
||||
]
|
||||
const videoList = [
|
||||
{
|
||||
key: 1,
|
||||
clickNumber: '367.6w+',
|
||||
title: '这是今日10点的南京',
|
||||
increases: 5,
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
clickNumber: '352.2w+',
|
||||
title: '立陶宛不断挑衅致经济受损民众…',
|
||||
increases: 17,
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
clickNumber: '348.9w+',
|
||||
title: '韩国艺人刘在石确诊新冠',
|
||||
increases: 30,
|
||||
},
|
||||
{
|
||||
key: 4,
|
||||
clickNumber: '346.3w+',
|
||||
title: '关于北京冬奥会,文在寅表态',
|
||||
increases: 12,
|
||||
},
|
||||
{
|
||||
key: 5,
|
||||
clickNumber: '271.2w+',
|
||||
title: '95后现役军人荣立一等功',
|
||||
increases: 2,
|
||||
},
|
||||
]
|
||||
setupMock({
|
||||
setup() {
|
||||
Mock.mock(new RegExp('/api/content-data'), () => {
|
||||
const presetData = [58, 81, 53, 90, 64, 88, 49, 79]
|
||||
const getLineData = () => {
|
||||
const count = 8
|
||||
return new Array(count).fill(0).map((el, idx) => ({
|
||||
x: dayjs()
|
||||
.day(idx - 2)
|
||||
.format('YYYY-MM-DD'),
|
||||
y: presetData[idx],
|
||||
}))
|
||||
}
|
||||
return successResponseWrap([...getLineData()])
|
||||
})
|
||||
Mock.mock(new RegExp('/api/popular/list'), (params: GetParams) => {
|
||||
const { type = 'text' } = qs.parseUrl(params.url).query
|
||||
if (type === 'image') {
|
||||
return successResponseWrap([...videoList])
|
||||
}
|
||||
if (type === 'video') {
|
||||
return successResponseWrap([...imageList])
|
||||
}
|
||||
return successResponseWrap([...textList])
|
||||
})
|
||||
},
|
||||
})
|
||||
84
cdtestplant/src/views/login/components/banner.vue
Normal file
84
cdtestplant/src/views/login/components/banner.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="banner">
|
||||
<div class="banner-inner">
|
||||
<a-carousel class="carousel" animation-name="fade">
|
||||
<a-carousel-item v-for="item in carouselItem" :key="item.slogan">
|
||||
<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" />
|
||||
</div>
|
||||
</a-carousel-item>
|
||||
</a-carousel>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import bannerImage from '@/assets/images/login-banner.png'
|
||||
|
||||
const { t } = useI18n()
|
||||
const carouselItem = computed(() => [
|
||||
{
|
||||
slogan: t('login.banner.slogan1'),
|
||||
subSlogan: t('login.banner.subSlogan1'),
|
||||
image: bannerImage,
|
||||
},
|
||||
{
|
||||
slogan: t('login.banner.slogan2'),
|
||||
subSlogan: t('login.banner.subSlogan2'),
|
||||
image: bannerImage,
|
||||
},
|
||||
{
|
||||
slogan: t('login.banner.slogan3'),
|
||||
subSlogan: t('login.banner.subSlogan3'),
|
||||
image: bannerImage,
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&-inner {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.carousel {
|
||||
height: 100%;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: var(--color-fill-1);
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
&-sub-title {
|
||||
margin-top: 8px;
|
||||
color: var(--color-text-3);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
&-image {
|
||||
width: 320px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
155
cdtestplant/src/views/login/components/login-form.vue
Normal file
155
cdtestplant/src/views/login/components/login-form.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="login-form-wrapper">
|
||||
<div class="login-form-title">{{ $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')">
|
||||
<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
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-lock />
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<a-space :size="16" direction="vertical">
|
||||
<div class="login-form-password-actions">
|
||||
<a-checkbox
|
||||
checked="rememberPassword"
|
||||
:model-value="loginConfig.rememberPassword"
|
||||
@change="setRememberPassword as any"
|
||||
>
|
||||
{{ $t('login.form.rememberPassword') }}
|
||||
</a-checkbox>
|
||||
<a-link>{{ $t('login.form.forgetPassword') }}</a-link>
|
||||
</div>
|
||||
<a-button type="primary" html-type="submit" long :loading="loading">
|
||||
{{ $t('login.form.login') }}
|
||||
</a-button>
|
||||
<a-button type="text" long class="login-form-register-btn">
|
||||
{{ $t('login.form.register') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { ValidatedError } from '@arco-design/web-vue/es/form/interface'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { useUserStore } from '@/store'
|
||||
import useLoading from '@/hooks/loading'
|
||||
import type { LoginData } from '@/api/user'
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
const errorMessage = ref('')
|
||||
const { loading, setLoading } = useLoading()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const loginConfig = useStorage('login-config', {
|
||||
rememberPassword: true,
|
||||
username: 'admin', // 演示默认值
|
||||
password: 'admin', // demo default value
|
||||
})
|
||||
const userInfo = reactive({
|
||||
username: loginConfig.value.username,
|
||||
password: loginConfig.value.password,
|
||||
})
|
||||
|
||||
const handleSubmit = async ({
|
||||
errors,
|
||||
values,
|
||||
}: {
|
||||
errors: Record<string, ValidatedError> | undefined
|
||||
values: Record<string, any>
|
||||
}) => {
|
||||
if (loading.value) return
|
||||
if (!errors) {
|
||||
setLoading(true)
|
||||
try {
|
||||
await userStore.login(values as LoginData)
|
||||
const { redirect, ...othersQuery } = router.currentRoute.value.query
|
||||
router.push({
|
||||
name: (redirect as string) || 'Workplace',
|
||||
query: {
|
||||
...othersQuery,
|
||||
},
|
||||
})
|
||||
Message.success(t('login.form.login.success'))
|
||||
const { rememberPassword } = loginConfig.value
|
||||
const { username, password } = values
|
||||
// 实际生产环境需要进行加密存储。
|
||||
// The actual production environment requires encrypted storage.
|
||||
loginConfig.value.username = rememberPassword ? username : ''
|
||||
loginConfig.value.password = rememberPassword ? password : ''
|
||||
} catch (err) {
|
||||
errorMessage.value = (err as Error).message
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
const setRememberPassword = (value: boolean) => {
|
||||
loginConfig.value.rememberPassword = value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-form {
|
||||
&-wrapper {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: var(--color-text-1);
|
||||
font-weight: 500;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&-sub-title {
|
||||
color: var(--color-text-3);
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
&-error-msg {
|
||||
height: 32px;
|
||||
color: rgb(var(--red-6));
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&-password-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-register-btn {
|
||||
color: var(--color-text-3) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
81
cdtestplant/src/views/login/index.vue
Normal file
81
cdtestplant/src/views/login/index.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<img
|
||||
alt="logo"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
<div class="logo-text">成都测试管理平台</div>
|
||||
</div>
|
||||
<LoginBanner />
|
||||
<div class="content">
|
||||
<div class="content-inner">
|
||||
<LoginForm />
|
||||
</div>
|
||||
<div class="footer">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Footer from '@/components/footer/index.vue'
|
||||
import LoginBanner from './components/banner.vue'
|
||||
import LoginForm from './components/login-form.vue'
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
|
||||
.banner {
|
||||
width: 550px;
|
||||
background: linear-gradient(163.85deg, #1d2129 0%, #00308f 100%);
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: fixed;
|
||||
top: 24px;
|
||||
left: 22px;
|
||||
z-index: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
&-text {
|
||||
margin-right: 4px;
|
||||
margin-left: 4px;
|
||||
color: var(--color-fill-1);
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// responsive
|
||||
@media (max-width: @screen-lg) {
|
||||
.container {
|
||||
.banner {
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
19
cdtestplant/src/views/login/locale/en-US.ts
Normal file
19
cdtestplant/src/views/login/locale/en-US.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export default {
|
||||
'login.form.title': 'Login to Arco Design Pro',
|
||||
'login.form.userName.errMsg': 'Username cannot be empty',
|
||||
'login.form.password.errMsg': 'Password cannot be empty',
|
||||
'login.form.login.errMsg': 'Login error, refresh and try again',
|
||||
'login.form.login.success': 'welcome to use',
|
||||
'login.form.userName.placeholder': 'Username: admin',
|
||||
'login.form.password.placeholder': 'Password: admin',
|
||||
'login.form.rememberPassword': 'Remember password',
|
||||
'login.form.forgetPassword': 'Forgot password',
|
||||
'login.form.login': 'login',
|
||||
'login.form.register': 'register account',
|
||||
'login.banner.slogan1': 'Out-of-the-box high-quality template',
|
||||
'login.banner.subSlogan1': 'Rich page templates, covering most typical business scenarios',
|
||||
'login.banner.slogan2': 'Built-in solutions to common problems',
|
||||
'login.banner.subSlogan2': 'Internationalization, routing configuration, state management everything',
|
||||
'login.banner.slogan3': 'Access visualization enhancement tool AUX',
|
||||
'login.banner.subSlogan3': 'Realize flexible block development',
|
||||
}
|
||||
19
cdtestplant/src/views/login/locale/zh-CN.ts
Normal file
19
cdtestplant/src/views/login/locale/zh-CN.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export default {
|
||||
'login.form.title': '登录平台',
|
||||
'login.form.userName.errMsg': '用户名不能为空',
|
||||
'login.form.password.errMsg': '密码不能为空',
|
||||
'login.form.login.errMsg': '登录出错,轻刷新重试',
|
||||
'login.form.login.success': '欢迎使用',
|
||||
'login.form.userName.placeholder': '用户名:admin',
|
||||
'login.form.password.placeholder': '密码:admin',
|
||||
'login.form.rememberPassword': '记住密码',
|
||||
'login.form.forgetPassword': '忘记密码',
|
||||
'login.form.login': '登录',
|
||||
'login.form.register': '注册账号',
|
||||
'login.banner.slogan1': '自动生成测试文档',
|
||||
'login.banner.subSlogan1': '解放测试文档编写工作',
|
||||
'login.banner.slogan2': '测试项目的合一化管理',
|
||||
'login.banner.subSlogan2': '测试用例-测试项目-问题单-其他管理',
|
||||
'login.banner.slogan3': '后端功能开发中...',
|
||||
'login.banner.subSlogan3': '后端功能开发放后进行',
|
||||
}
|
||||
30
cdtestplant/src/views/not-found/index.vue
Normal file
30
cdtestplant/src/views/not-found/index.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<a-result class="result" status="404" :subtitle="'not found'"> </a-result>
|
||||
<div class="operation-row">
|
||||
<a-button key="back" type="primary" @click="back"> back </a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const back = () => {
|
||||
// warning: Go to the node that has the permission
|
||||
router.push({ name: 'Workplace' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.content {
|
||||
// padding-top: 100px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -95px;
|
||||
margin-top: -121px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
16
cdtestplant/src/views/redirect/index.vue
Normal file
16
cdtestplant/src/views/redirect/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const gotoPath = route.params.path as string
|
||||
|
||||
router.replace({ path: gotoPath })
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
7
cdtestplant/src/views/userAbout/test/index.vue
Normal file
7
cdtestplant/src/views/userAbout/test/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="test"><h2>测试页面</h2> </div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
||||
42
cdtestplant/src/views/userAbout/userManage/config/columns.ts
Normal file
42
cdtestplant/src/views/userAbout/userManage/config/columns.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { computed } from 'vue'
|
||||
// a-table列的配置信息
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '用户名称',
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '启用状态',
|
||||
dataIndex: 'status',
|
||||
align: 'center',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updateDate',
|
||||
slotName: 'updateDate',
|
||||
},
|
||||
{
|
||||
title: '电话号码',
|
||||
dataIndex: 'cellphone',
|
||||
},
|
||||
{
|
||||
title: '系统角色',
|
||||
dataIndex: 'role',
|
||||
slotName: 'role',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations',
|
||||
align: 'center',
|
||||
},
|
||||
])
|
||||
|
||||
export default columns
|
||||
@@ -0,0 +1,54 @@
|
||||
const modelConfig = {
|
||||
formItems: [
|
||||
{
|
||||
field: 'name',
|
||||
type: 'input',
|
||||
label: '用户名称',
|
||||
placeholder: '请输入用户名称',
|
||||
rules: [
|
||||
{ required: true, message: 'name is required' },
|
||||
{ minLength: 2, message: 'must be greater than 2 characters' },
|
||||
],
|
||||
trigger: ['change', 'input'],
|
||||
},
|
||||
{
|
||||
field: 'password',
|
||||
type: 'password',
|
||||
label: '用户密码',
|
||||
placeholder: '请输入密码',
|
||||
isHidden: true,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
type: 'select',
|
||||
label: '启用状态',
|
||||
placeholder: '请输入真实姓名',
|
||||
options: [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 },
|
||||
],
|
||||
rules: [{ required: true, message: '此为必填项' }],
|
||||
trigger: ['blur'],
|
||||
},
|
||||
{
|
||||
field: 'updateDate',
|
||||
type: 'datepicker',
|
||||
label: '更新日期',
|
||||
placeholder: '不填为今日',
|
||||
},
|
||||
{
|
||||
field: 'role',
|
||||
type: 'select',
|
||||
label: '系统角色',
|
||||
placeholder: '请输入系统角色',
|
||||
options: [
|
||||
{ label: '管理员', value: 'admin' },
|
||||
{ label: '用户', value: 'user' },
|
||||
],
|
||||
rules: [{ required: true, message: '此为必填项' }],
|
||||
trigger: ['blur'],
|
||||
},
|
||||
],
|
||||
colLayout: { span: 24 },
|
||||
}
|
||||
export default modelConfig
|
||||
281
cdtestplant/src/views/userAbout/userManage/index.vue
Normal file
281
cdtestplant/src/views/userAbout/userManage/index.vue
Normal file
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div class="userManage">
|
||||
<Breadcrumb :items="['menu.userAbout', 'menu.userAbout.manage']"></Breadcrumb>
|
||||
<a-card :title="$t('menu.userAbout.manage')">
|
||||
<a-row>
|
||||
<a-col :flex="1">
|
||||
<a-form
|
||||
:model="formModel"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
label-align="left"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item field="name" :label="$t('searchTable.form.name')">
|
||||
<a-input
|
||||
v-model="formModel.name"
|
||||
allow-clear
|
||||
:placeholder="$t('searchTable.form.name.placeholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item field="status" :label="$t('searchTable.form.status')">
|
||||
<a-select
|
||||
v-model="formModel.status"
|
||||
allow-clear
|
||||
:options="statusOptions"
|
||||
:placeholder="$t('searchTable.form.status.placeholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item field="updateDate" :label="$t('searchTable.form.date')">
|
||||
<a-range-picker v-model="formModel.updateDate" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item field="name" :label="$t('searchTable.form.cellphone')">
|
||||
<a-input
|
||||
v-model="formModel.cellphone"
|
||||
allow-clear
|
||||
:placeholder="$t('searchTable.form.cellphone.placeholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item field="role" :label="$t('searchTable.form.role')">
|
||||
<a-select
|
||||
v-model="formModel.role"
|
||||
allow-clear
|
||||
:placeholder="$t('searchTable.form.role.placeholder')"
|
||||
><a-option label="管理员" value="admin"></a-option>
|
||||
<a-option label="用户" value="user"></a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-col>
|
||||
<a-divider style="height: 84px" direction="vertical" />
|
||||
<a-col :flex="'86px'" style="text-align: right">
|
||||
<a-space direction="vertical" :size="18">
|
||||
<a-button type="primary" @click="handleSearBtn">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
{{ $t('searchTable.form.search') }}
|
||||
</a-button>
|
||||
<a-button @click="handleResetBtn">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
{{ $t('searchTable.form.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="margin-bottom: 8px">
|
||||
<a-col :span="24" class="buttonRight">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleCreateBtn">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
{{ $t('searchTable.operation.create') }}
|
||||
</a-button>
|
||||
<a-button type="dashed">
|
||||
<template #icon>
|
||||
<icon-minus />
|
||||
</template>
|
||||
{{ $t('searchTable.operation.delete') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-table
|
||||
ellipsis
|
||||
row-key="id"
|
||||
:loading="loading"
|
||||
:pagination="false"
|
||||
:columns="(columns as any)"
|
||||
:data="renderData"
|
||||
:bordered="true"
|
||||
size="medium"
|
||||
>
|
||||
<template #status="row">
|
||||
<a-button v-if="row.record.status" size="mini" type="outline" status="success">已启用</a-button>
|
||||
<a-button v-else size="mini" type="outline" status="danger">未启用</a-button>
|
||||
</template>
|
||||
<template #operations="row">
|
||||
<a-space>
|
||||
<a-button
|
||||
v-permission="['admin', 'user']"
|
||||
type="primary"
|
||||
size="mini"
|
||||
@click="handleEditRowBtn(row.record)"
|
||||
>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button
|
||||
v-permission="['admin']"
|
||||
type="outline"
|
||||
status="danger"
|
||||
size="mini"
|
||||
@click="handleDeleteRowBtn(row.record)"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-row style="margin-top: 8px" class="buttonRight"
|
||||
><a-pagination
|
||||
v-model:current="current"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
style="float: right"
|
||||
show-page-size
|
||||
show-total
|
||||
show-jumper
|
||||
@change="handleChangePage"
|
||||
@page-size-change="handleChangeSize"
|
||||
></a-pagination
|
||||
></a-row>
|
||||
</a-card>
|
||||
<!-- 弹窗组件 -->
|
||||
<page-modal
|
||||
ref="pageModelRef"
|
||||
title="新建用户"
|
||||
:model-config="modelConfig"
|
||||
:default-info="defaultInfo"
|
||||
></page-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import useLoading from '@/hooks/loading'
|
||||
import { getUserList, deleteUserList, getUserListAll } from '@/api/userAbout'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import pageModal from '@/components/page-modal/page-modal.vue'
|
||||
import modelConfig from './config/modelConfig'
|
||||
// 导入a-table列数据
|
||||
import columns from './config/columns'
|
||||
// 定义page-modal的ref调用
|
||||
const pageModelRef = ref<InstanceType<typeof pageModal>>()
|
||||
// 定义a-form的v-model数据
|
||||
const generateFormModel = () => {
|
||||
return {
|
||||
name: '',
|
||||
status: '',
|
||||
password: '',
|
||||
updateDate: [],
|
||||
cellphone: '',
|
||||
role: '',
|
||||
}
|
||||
}
|
||||
const statusOptions: any[] = [
|
||||
{ label: '已启用', value: 1 },
|
||||
{ label: '未启用', value: 0 },
|
||||
]
|
||||
const formModel = ref(generateFormModel())
|
||||
// 定义a-table数据
|
||||
/// 请求的表格数据
|
||||
const renderData: any = ref([])
|
||||
// 请求pagination数据查询数据
|
||||
const total = ref<number>(0)
|
||||
const pageSize = ref<number>(10)
|
||||
const current = ref<number>(1)
|
||||
|
||||
// 请求用户列表数据函数
|
||||
const { loading, setLoading } = useLoading(true)
|
||||
const fetchUserListData = async (params: any = {}) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const data = await getUserList(params)
|
||||
renderData.value = data.data.data
|
||||
total.value = data.data.total
|
||||
Message.success('请求数据成功')
|
||||
} catch (err) {
|
||||
Message.error('请求失败,请检测网络')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchUserListData({ current: current.value, pageSize: pageSize.value })
|
||||
|
||||
// a-pagination分页页面变换处理、a-pagination单页显示大小函数
|
||||
const handleChangePage = (currentPage: number) => {
|
||||
fetchUserListData({ current: currentPage, pageSize: pageSize.value })
|
||||
}
|
||||
const handleChangeSize = (size: number) => {
|
||||
fetchUserListData({ current: current.value, pageSize: size })
|
||||
}
|
||||
// 处理搜索按钮函数
|
||||
const fetchUserListAll = async (params: any) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const data = await getUserListAll(params)
|
||||
renderData.value = data.data.data
|
||||
total.value = data.data.total
|
||||
Message.success('数据请求成功')
|
||||
} catch (err) {
|
||||
Message.error('请求失败')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
const handleSearBtn = () => {
|
||||
// 组装queryInfo
|
||||
const queryInfo = { ...formModel.value, current: current.value, pageSize: pageSize.value }
|
||||
fetchUserListAll(queryInfo)
|
||||
}
|
||||
// 处理重置按钮函数
|
||||
const handleResetBtn = () => {
|
||||
formModel.value = generateFormModel()
|
||||
}
|
||||
// 点击新建按钮
|
||||
const defaultInfo = ref({})
|
||||
const handleCreateBtn = () => {
|
||||
if (pageModelRef.value) {
|
||||
pageModelRef.value.dialogVisible = true
|
||||
}
|
||||
defaultInfo.value = {}
|
||||
}
|
||||
// 用户编辑行按钮
|
||||
const handleEditRowBtn = (item: any) => {
|
||||
if (pageModelRef.value) {
|
||||
pageModelRef.value.dialogVisible = true
|
||||
}
|
||||
defaultInfo.value = { ...item }
|
||||
}
|
||||
// 用户删除行按钮
|
||||
const fetchDeleteUserList = async (params: any) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
await deleteUserList(params)
|
||||
// 再去请求一次
|
||||
fetchUserListData({ current: current.value, pageSize: pageSize.value })
|
||||
Message.success('删除成功')
|
||||
} catch (err) {
|
||||
Message.error('删除失败')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
const handleDeleteRowBtn = (item: any) => {
|
||||
fetchDeleteUserList(item.index)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.userManage {
|
||||
padding: 0 15px 15px 15px;
|
||||
}
|
||||
.buttonRight {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
</style>
|
||||
16
cdtestplant/src/views/userAbout/userManage/locale/en-US.ts
Normal file
16
cdtestplant/src/views/userAbout/userManage/locale/en-US.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export default {
|
||||
'searchTable.form.name': '用户名称',
|
||||
'searchTable.form.name.placeholder': '请输入用户名称',
|
||||
'searchTable.form.status': '启用状态',
|
||||
'searchTable.form.status.placeholder': '请选择启用状态',
|
||||
'searchTable.form.date': '更新日期',
|
||||
'searchTable.form.cellphone': '电话号码',
|
||||
'searchTable.form.cellphone.placeholder': '请输入电话号码',
|
||||
'searchTable.form.role': '系统角色',
|
||||
'searchTable.form.role.placeholder': '请选择系统角色',
|
||||
'searchTable.form.search': '搜索',
|
||||
'searchTable.form.reset': '重置',
|
||||
'searchTable.operation.create': '新建',
|
||||
'searchTable.operation.delete': '批量删除',
|
||||
'searchTable.columns.operations.view': '查看',
|
||||
}
|
||||
16
cdtestplant/src/views/userAbout/userManage/locale/zh-CN.ts
Normal file
16
cdtestplant/src/views/userAbout/userManage/locale/zh-CN.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export default {
|
||||
'searchTable.form.name': '用户名称',
|
||||
'searchTable.form.name.placeholder': '请输入用户名称',
|
||||
'searchTable.form.status': '启用状态',
|
||||
'searchTable.form.status.placeholder': '请选择启用状态',
|
||||
'searchTable.form.date': '更新日期',
|
||||
'searchTable.form.cellphone': '电话号码',
|
||||
'searchTable.form.cellphone.placeholder': '请输入电话号码',
|
||||
'searchTable.form.role': '系统角色',
|
||||
'searchTable.form.role.placeholder': '请选择系统角色',
|
||||
'searchTable.form.search': '搜索',
|
||||
'searchTable.form.reset': '重置',
|
||||
'searchTable.operation.create': '新建',
|
||||
'searchTable.operation.delete': '批量删除',
|
||||
'searchTable.columns.operations.view': '查看',
|
||||
}
|
||||
Reference in New Issue
Block a user