Browse Source

feat: 首页添加

master
wangqi 2 months ago
parent
commit
b1e040d920
  1. 1
      components.d.ts
  2. 23
      src/api/module/eam/device/index.ts
  3. 1
      src/api/module/eam/device/storage.ts
  4. 2
      src/api/server/axiosInstance.ts
  5. BIN
      src/assets/image/dashboard/login/bg.png
  6. BIN
      src/assets/image/dashboard/login/logo.png
  7. BIN
      src/assets/image/login/bg.png
  8. BIN
      src/assets/image/login/icon_logo_pg.png
  9. 2
      src/components/dashboard/Edfs-exception.vue
  10. 2
      src/components/dashboard/Edfs-number-item-input.vue
  11. 2
      src/components/dashboard/FormItemInput.vue
  12. 2
      src/components/dashboard/FormItemSelect.vue
  13. 3
      src/components/dashboard/Icon/src/Icon.vue
  14. 2
      src/pages/deviceInfo/components/BasicInfo.vue
  15. 2
      src/pages/deviceInfo/components/DeliveryInfo.vue
  16. 2
      src/pages/deviceInfo/index.vue
  17. 14
      src/pages/deviceInfo/info.vue
  18. 2
      src/pages/deviceStorage/components/data-filter.vue
  19. 2
      src/pages/deviceTest/testPlan/components/InfoDlg.vue
  20. 164
      src/pages/home/components/line-chart.vue
  21. 124
      src/pages/home/components/pie-chart.vue
  22. 15
      src/pages/home/components/utils.ts
  23. 276
      src/pages/home/index.vue
  24. 16
      src/pages/layout.vue
  25. 2
      src/pages/system/config/index.vue
  26. 2
      src/pages/system/deviceField/components/CategoryDlg.vue
  27. 2
      src/pages/system/deviceField/components/CategoryTree.vue
  28. 251
      src/pages/system/login/index.vue
  29. 2
      src/pages/system/manufacturer/components/manufacturerDlg.vue
  30. 2
      src/router/index.ts
  31. 4
      src/styles/var.css

1
components.d.ts vendored

@ -23,6 +23,7 @@ declare module 'vue' {
Editor: typeof import('./src/components/dashboard/Editor/src/Editor.vue')['default'] Editor: typeof import('./src/components/dashboard/Editor/src/Editor.vue')['default']
ElAvatar: typeof import('element-plus/es')['ElAvatar'] ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCascader: typeof import('element-plus/es')['ElCascader'] ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']

23
src/api/module/eam/device/index.ts

@ -57,7 +57,30 @@ export const getDevicePage = (params: PageParam) =>
total: number total: number
}>({ url: '/device/page', method: 'get', params }) }>({ url: '/device/page', method: 'get', params })
export const getDeviceStorageAndMaintainChart = (params: {
type: number
time?: string
year?: number
month?: number
}) =>
eamServer({
url: `/statistics-device/get-device-chart`,
method: 'post',
data: params,
})
export const getDeviceSummary = () =>
eamServer({
url: `/statistics-device/get-device-summary`,
method: 'get',
})
export const getDeviceSummaryByStatus = () => {
return eamServer({
url: `/statistics-device/get-device-summary-by-status`,
method: 'get',
})
}
// ============== 测试工单相关 ============== // ============== 测试工单相关 ==============

1
src/api/module/eam/device/storage.ts

@ -35,3 +35,4 @@ export const getStorageStatics = () =>
url: `/storage/statics`, url: `/storage/statics`,
method: 'get', method: 'get',
}) })

2
src/api/server/axiosInstance.ts

@ -136,7 +136,7 @@ const createAxiosInstance = (module: APIConfigKeys, config: Config) => {
}) })
requestList = [] requestList = []
// 重连接socket // 重连接socket
openSocket() // openSocket()
return service(config) return service(config)
} catch (e) { } catch (e) {
logout() logout()

BIN
src/assets/image/dashboard/login/bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

BIN
src/assets/image/dashboard/login/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/image/login/bg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 KiB

BIN
src/assets/image/login/icon_logo_pg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

2
src/components/dashboard/Edfs-exception.vue

@ -10,7 +10,7 @@
type="primary" type="primary"
@click=" @click="
() => { () => {
router.push('/device/data') router.push('/home')
} }
" "
>返回首页</el-button >返回首页</el-button

2
src/components/dashboard/Edfs-number-item-input.vue

@ -133,7 +133,7 @@ function onChangeTip(visible: boolean) {
box-sizing: border-box; box-sizing: border-box;
.label { .label {
color: var(--label-text); color: var(--label-color);
white-space: nowrap; white-space: nowrap;
text-align: right; text-align: right;
line-height: 33px; line-height: 33px;

2
src/components/dashboard/FormItemInput.vue

@ -106,7 +106,7 @@ watch(
.label { .label {
// font-size: 14px; // font-size: 14px;
color: var(--label-text); color: var(--label-color);
line-height: 33px; line-height: 33px;
text-align: right; text-align: right;
// margin-right: 24px; // margin-right: 24px;

2
src/components/dashboard/FormItemSelect.vue

@ -121,7 +121,7 @@ watch(
box-sizing: border-box; box-sizing: border-box;
.label { .label {
color: var(--label-text); color: var(--label-color);
line-height: 100%; line-height: 100%;
text-align: right; text-align: right;
// font-size: 0.18rem; // font-size: 0.18rem;

3
src/components/dashboard/Icon/src/Icon.vue

@ -15,7 +15,7 @@ const props = defineProps({
// icon color // icon color
color: propTypes.string, color: propTypes.string,
// icon size // icon size
size: propTypes.number.def(12), size: propTypes.number.def(14),
// icon svg class // icon svg class
svgClass: propTypes.string.def(''), svgClass: propTypes.string.def(''),
}) })
@ -33,6 +33,7 @@ const getIconifyStyle = computed(() => {
return { return {
height: '1em', height: '1em',
color, color,
fontSize: `${size}px`,
} }
}) })

2
src/pages/deviceInfo/components/BasicInfo.vue

@ -245,7 +245,7 @@ onMounted(() => {
white-space: nowrap; white-space: nowrap;
width: 70px; width: 70px;
text-align: right; text-align: right;
color: var(--label-text); color: var(--label-color);
margin-right: 8px; margin-right: 8px;
} }
.input { .input {

2
src/pages/deviceInfo/components/DeliveryInfo.vue

@ -145,7 +145,7 @@ onMounted(() => {
white-space: nowrap; white-space: nowrap;
width: 80px; width: 80px;
text-align: right; text-align: right;
color: var(--label-text); color: var(--label-color);
margin-right: 8px; margin-right: 8px;
} }
} }

2
src/pages/deviceInfo/index.vue

@ -273,7 +273,7 @@ function onView(row: any) {
.label { .label {
white-space: nowrap; white-space: nowrap;
width: 70px; width: 70px;
color: var(--label-text); color: var(--label-color);
} }
} }
} }

14
src/pages/deviceInfo/info.vue

@ -1,4 +1,14 @@
<template> <template>
<EdfsButton
inner-text="返 回"
type="success"
style="width: 80px"
@click="
() => {
router.go(-1)
}
"
/>
<el-scrollbar class="demo-collapse"> <el-scrollbar class="demo-collapse">
<el-collapse v-model="activeNames" @change="handleChange"> <el-collapse v-model="activeNames" @change="handleChange">
<template v-for="item in collapses"> <template v-for="item in collapses">
@ -45,7 +55,7 @@ import TestInfo from './components/TestInfo.vue'
import DeliveryInfo from './components/DeliveryInfo.vue' import DeliveryInfo from './components/DeliveryInfo.vue'
import { collapses, openNotMsg, type OptAction } from './utils' import { collapses, openNotMsg, type OptAction } from './utils'
import MaintainInfo from './components/MaintainInfo.vue' import MaintainInfo from './components/MaintainInfo.vue'
import { useRoute } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { getDeviceInfo } from '@/api/module/eam/device' import { getDeviceInfo } from '@/api/module/eam/device'
import { isResError, useMessage } from '@/hooks/useMessage' import { isResError, useMessage } from '@/hooks/useMessage'
import { isEmpty } from '@/utils/is' import { isEmpty } from '@/utils/is'
@ -53,7 +63,7 @@ const route = useRoute()
const type = route.query.type as string const type = route.query.type as string
const action = route.query.action as OptAction const action = route.query.action as OptAction
const deviceInfoId = ref() const deviceInfoId = ref()
const router = useRouter()
const message = useMessage() const message = useMessage()
const isDisabledInfo = computed(() => { const isDisabledInfo = computed(() => {

2
src/pages/deviceStorage/components/data-filter.vue

@ -141,7 +141,7 @@ onMounted(() => {
box-sizing: border-box; box-sizing: border-box;
span { span {
font-size: 14px; font-size: 14px;
color: var(--label-text); color: var(--label-color);
} }
.time-item { .time-item {
margin-bottom: 32px; margin-bottom: 32px;

2
src/pages/deviceTest/testPlan/components/InfoDlg.vue

@ -139,7 +139,7 @@ function onClone() {
} }
.label { .label {
width: 110px; width: 110px;
color: var(--label-text); color: var(--label-color);
line-height: 33px; line-height: 33px;
text-align: right; text-align: right;
.require { .require {

164
src/pages/home/components/line-chart.vue

@ -0,0 +1,164 @@
<template>
<v-chart
class="chart"
:option="chartOption"
autoresize
:loading-options="loadingOpt"
:loading="loading"
ref="chartRef"
/>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
import VChart from 'vue-echarts'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import type { EChartsOption, SeriesOption } from 'echarts/types/dist/shared.js'
import { LineChart } from 'echarts/charts'
import {
TooltipComponent,
LegendComponent,
DataZoomComponent,
GridComponent,
} from 'echarts/components'
use([
CanvasRenderer,
TooltipComponent,
LineChart,
LegendComponent,
GridComponent,
DataZoomComponent,
])
const props = defineProps({
data: {
type: Array as PropType<Array<{ time: string; storage: number; outStorage: number }>>,
default: () => [],
},
filterType: {
type: Number as PropType<Number>,
default: 1,
},
})
const loading = ref(true)
const loadingOpt = {
type: 'default',
text: '暂无数据',
color: '#c23531',
textColor: '#666',
maskColor: 'rgba(255, 255, 255, 0)',
showSpinner: false,
}
const chartRef = ref()
const chartOption = computed<EChartsOption>(() => {
loading.value = false
const option: EChartsOption = {
grid: {
left: 50,
right: 10,
top: '8%',
bottom: 50,
},
tooltip: {
trigger: 'axis',
confine: true,
appendToBody: true,
},
legend: {
type: 'scroll',
formatter: name => {
return name.length > 6 ? name.substring(0, 6) + '...' : name
},
tooltip: {
show: true,
},
right: 0,
itemWidth: 10,
itemHeight: 10,
pageIconSize: 12,
// data: data,
},
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: {
width: 1,
type: 'solid',
},
},
axisLabel: {
fontSize: 12,
},
axisTick: {
show: false,
},
data: props.data.map(r => r.time),
},
yAxis: {
type: 'value',
splitNumber: 6,
axisLine: {
//y线
show: true,
lineStyle: {
width: 1,
type: 'solid',
},
},
axisLabel: {
margin: 12,
fontSize: 12,
},
splitLine: {
show: true,
lineStyle: {},
},
},
series: [
{
name: '入库设备数',
type: 'line',
data: props.data.map(r => r.storage ?? 0),
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: {
width: 2,
},
itemStyle: {
color: '#619925',
},
},
{
name: '出库设备数',
type: 'line',
data: props.data.map((r: any) => r.outStorage ?? 0),
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: {
width: 2,
},
itemStyle: {
color: '#FFA500',
},
},
],
}
return option
})
</script>
<style lang="scss" scoped>
.chart {
width: 100%;
height: 100%;
}
</style>

124
src/pages/home/components/pie-chart.vue

@ -0,0 +1,124 @@
<template>
<v-chart class="chart" :option="option" :autoresize="autoresize" />
</template>
<script setup lang="ts">
import VChart from 'vue-echarts'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
LegendComponent,
GraphicComponent,
} from 'echarts/components'
import type { EChartsOption } from 'echarts'
import { useTheme } from '@/utils/useTheme'
use([
CanvasRenderer,
PieChart,
TitleComponent,
TooltipComponent,
GraphicComponent,
LegendComponent,
])
interface Props {
data: any[]
}
const { chartGraphicTextColor } = useTheme()
const props = withDefaults(defineProps<Props>(), {
data: () => [],
})
const updateTrigger = ref(0)
const autoresize = {
throttle: 2500,
onResize: () => {
updateTrigger.value++
},
}
//
const total = computed(() => props.data.reduce((acc, cur) => acc + cur.value, 0))
const option = computed(() => {
const trigger = updateTrigger.value
const res: EChartsOption = {
tooltip: {
show: false,
},
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: '36%',
style: {
text: total.value.toString(),
fill: chartGraphicTextColor.value,
fontSize: 24,
fontWeight: 700,
},
},
{
type: 'text',
left: 'center',
top: '42%',
style: {
text: '设备总数',
fill: chartGraphicTextColor.value,
fontSize: 20,
},
},
],
},
legend: {
show: true,
bottom: '3%',
itemWidth: 20,
itemHeight: 15,
itemGap: 10,
textStyle: {
fontSize: 16,
},
},
color: ['#4CAF50', '#2196F3', '#FBB852', '#FF9800', '#F44336', '#ccc'],
series: [
{
type: 'pie',
radius: ['40%', '55%'],
center: ['50%', '40%'],
data: props.data,
avoidLabelOverlap: false,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 1,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
label: {
show: true,
position: 'outside',
// formatter: '{b}: {c} ({d}%)',
formatter: ' {c}',
},
labelLine: {
show: true,
},
},
],
}
return res
})
</script>
<style scoped lang="scss">
.chart {
width: 100%;
height: 100%;
}
</style>

15
src/pages/home/components/utils.ts

@ -0,0 +1,15 @@
export const data = [
{time: 1733011200, storage: 188, outStorage: 1407},
{time: 1733097600, storage: 1139, outStorage: 1262},
{time: 1733184000, storage: 733, outStorage: 1498},
{time: 1733270400, storage: 555, outStorage: 376},
{time: 1733356800, storage: 617, outStorage: 791},
{time: 1733443200, storage: 119, outStorage: 418},
{time: 1733529600, storage: 169, outStorage: 1439},
{time: 1733616000, storage: 972, outStorage: 348},
{time: 1733702400, storage: 1087, outStorage: 631},
{time: 1733788800, storage: 229, outStorage: 287},
{time: 1733875200, storage: 472, outStorage: 195},
{time: 1733961600, storage: 790, outStorage: 478},
]

276
src/pages/home/index.vue

@ -0,0 +1,276 @@
<template>
<div class="device-home">
<header class="device-status-blok">
<div
v-for="(item, idx) in summaryList"
:key="idx"
class="val-blok"
:style="{ backgroundColor: item.color }"
>
<div class="val">
<span>{{ item.label }}</span>
<span class="num">{{ item.value }}</span>
</div>
<Icon :icon="item.icon" :size="48" />
<!-- <img :src="item.icon" alt="" /> -->
</div>
</header>
<main>
<EdfsWrap title="库存数据" class="line">
<template #title-right>
<el-button-group size="small">
<template v-for="{ key, name } in filterData" :key="key">
<EdfsButton
:inner-text="name"
@click="onTimeSearch(key)"
:type="currentFilter === key ? 'success' : ''"
/>
</template>
</el-button-group>
</template>
<LineChart :data="lineChartData" :filterType="currentFilter" />
</EdfsWrap>
<EdfsWrap title="设备数据" class="pie">
<PieChart :data="pieData" />
</EdfsWrap>
</main>
<footer>
<EdfsWrap title="快捷入口" class="shortcut">
<div class="shortcut-list">
<template v-for="(item, idx) in shortcutList" :key="idx">
<div class="item" @click="onShortcutClick(item)">
<el-button :type="item.type as any" circle size="large">
<Icon :icon="item.icon" :size="24" />
</el-button>
<span>{{ item.title }}</span>
</div>
</template>
</div>
</EdfsWrap>
</footer>
</div>
</template>
<script setup lang="ts">
import EdfsWrap from '@/components/dashboard/Edfs-wrap.vue'
import { data } from './components/utils'
import LineChart from './components/line-chart.vue'
import PieChart from './components/pie-chart.vue'
import { Icon } from '@/components/dashboard/Icon/index'
import { useRouter } from 'vue-router'
import {
getDeviceStorageAndMaintainChart,
getDeviceSummary,
getDeviceSummaryByStatus,
} from '@/api/module/eam/device'
import dayjs from 'dayjs'
import { isResError } from '@/hooks/useMessage'
const router = useRouter()
const shortcutList = [
{
title: '设备管理',
icon: 'solar:server-square-linear',
type: 'primary',
path: '/device/data',
},
{
title: '设备库存',
icon: 'solar:box-outline',
type: 'success',
path: '/storage',
},
{
title: '测试计划',
icon: 'codicon:code-oss',
type: 'warning',
path: '/testSheet/plan',
},
]
function onShortcutClick(item: any) {
router.push(item.path)
}
const filterData = [
{
key: 0,
name: '本月',
},
{
key: 1,
name: '本年',
},
]
const summaryList = ref([
// {
// label: '',
// value: 0,
// icon: 'solar:server-square-linear',
// color: '#1E90FF',
// },
{
label: '积累入库',
value: 0,
icon: 'solar:inbox-in-broken',
key: 'storage',
color: '#4CAF50',
},
{
label: '积累出库',
value: 0,
key: 'outStorage',
icon: 'solar:inbox-out-outline',
color: '#2196F3',
},
{
label: '测试中设备',
value: 0,
icon: 'solar:document-add-outline',
key: 'testing',
color: '#FF9800',
},
{
label: '维修中',
icon: 'solar:minimalistic-magnifer-bug-outline',
value: 0,
key: 'repair',
color: '#F44336',
},
])
const currentFilter = ref(1)
function onTimeSearch(filter: number) {
currentFilter.value = filter
loadLineChartData()
}
const lineChartData = ref<any>([])
async function loadLineChartData() {
//
const curMonth = dayjs().month() + 1
const curYear = dayjs().year()
const res = await getDeviceStorageAndMaintainChart({
type: currentFilter.value,
year: curYear,
month: curMonth,
})
if (isResError(res)) return
lineChartData.value = res.data
}
const pieData = ref<any>([])
async function loadPieChartData() {
const res = await getDeviceSummaryByStatus()
if (isResError(res)) return
pieData.value = res.data.map((item: any) => {
return {
name: item.statusName,
value: item.status,
}
})
}
async function loadDeviceSummary() {
const res = await getDeviceSummary()
if (isResError(res)) return
const data = res.data
summaryList.value.forEach(item => {
item.value = !!data[item.key] ? data[item.key] : 0
})
}
onMounted(() => {
loadDeviceSummary()
loadLineChartData()
loadPieChartData()
})
</script>
<style scoped lang="scss">
.device-home {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
row-gap: 16px;
.device-status-blok {
box-sizing: border-box;
height: 110px;
display: flex;
user-select: none;
justify-content: space-between;
column-gap: 40px;
.val-blok {
display: flex;
font-size: 16px;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
border-radius: 4px;
overflow: hidden;
background-color: var(--warp-bg);
height: 100%;
width: 33%;
padding: 0 30px;
align-items: center;
img {
width: 130px;
height: 100%;
}
.val {
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
row-gap: 6px;
height: 100%;
.num {
font-size: 30px;
font-weight: 600;
padding-right: 24px;
}
}
}
}
main {
flex: 1;
display: flex;
column-gap: 16px;
.line {
flex: 0.58;
}
.pie {
flex: 0.42;
}
}
footer {
height: 140px;
.shortcut-list {
display: flex;
align-items: center;
column-gap: 18px;
}
.item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
span {
margin-top: 8px;
color: var(--label-color);
}
}
:deep(.wrap-body) {
padding-top: 0;
padding-left: 20px;
}
:deep(.el-button) {
width: 52px;
height: 52px;
}
}
}
</style>

16
src/pages/layout.vue

@ -13,12 +13,12 @@
class="menu-item" class="menu-item"
v-if="!menu?.children?.length" v-if="!menu?.children?.length"
> >
<Icon :size="12" v-if="menu.meta.icon" :icon="menu.meta.icon" /> <Icon :size="16" v-if="menu.meta.icon" :icon="menu.meta.icon" />
<span class="menu-label">{{ menu.meta.title }}</span> <span class="menu-label">{{ menu.meta.title }}</span>
</el-menu-item> </el-menu-item>
<el-sub-menu :index="menu.path" v-else> <el-sub-menu :index="menu.path" v-else>
<template #title> <template #title>
<Icon :size="12" v-if="menu.meta.icon" :icon="menu.meta.icon" /> <Icon :size="16" v-if="menu.meta.icon" :icon="menu.meta.icon" />
<span class="menu-label">{{ menu.meta.title }}</span> <span class="menu-label">{{ menu.meta.title }}</span>
</template> </template>
<template v-for="(child, index) in menu.children" :key="index"> <template v-for="(child, index) in menu.children" :key="index">
@ -27,14 +27,14 @@
:index="`${menu.path}/${child.path}`" :index="`${menu.path}/${child.path}`"
v-if="!child?.children?.length" v-if="!child?.children?.length"
> >
<Icon :size="12" v-if="child.meta.icon" :icon="child.meta.icon" /> <Icon :size="16" v-if="child.meta.icon" :icon="child.meta.icon" />
{{ child.meta.title }} {{ child.meta.title }}
</el-menu-item> </el-menu-item>
<el-sub-menu :index="`${menu.path}/${child.path}`" v-else> <el-sub-menu :index="`${menu.path}/${child.path}`" v-else>
<template #title> <template #title>
<Icon :size="12" v-if="child.meta.icon" :icon="child.meta.icon" /> <Icon :size="16" v-if="child.meta.icon" :icon="child.meta.icon" />
{{ child.meta.title }} {{ child.meta.title }}
</template> </template>
@ -47,7 +47,7 @@
:index="`${menu.path}/${child.path}/${subChild.path}`" :index="`${menu.path}/${child.path}/${subChild.path}`"
> >
<Icon <Icon
:size="12" :size="16"
v-if="subChild.meta.icon" v-if="subChild.meta.icon"
:icon="subChild.meta.icon" :icon="subChild.meta.icon"
/> />
@ -105,7 +105,6 @@ import { RouterView, useRouter, useRoute } from 'vue-router'
import logoIcon from '@/assets/image/dashboard/common/icon_logo_pg.png' import logoIcon from '@/assets/image/dashboard/common/icon_logo_pg.png'
import { usePermissionStore } from '@/stores/permission' import { usePermissionStore } from '@/stores/permission'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { openSocket, closeSocket } from '@/pages/socket_server/index'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { isResError } from '@/hooks/useMessage' import { isResError } from '@/hooks/useMessage'
import Avatar from './avatar.png' import Avatar from './avatar.png'
@ -164,7 +163,6 @@ const userStore = useUserStore()
const { t } = useI18n() // const { t } = useI18n() //
const avatar = computed(() => userStore.user.avatar ?? '') const avatar = computed(() => userStore.user.avatar ?? '')
const userName = computed(() => userStore.user.nickname ?? 'Admin') const userName = computed(() => userStore.user.nickname ?? 'Admin')
openSocket()
const { push, currentRoute } = useRouter() const { push, currentRoute } = useRouter()
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
@ -238,9 +236,7 @@ async function loginOut() {
} catch {} } catch {}
} }
onBeforeUnmount(() => {
closeSocket()
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

2
src/pages/system/config/index.vue

@ -192,7 +192,7 @@ onMounted(() => {
.title { .title {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: var(--label-text); color: var(--label-color);
} }
.desc { .desc {
font-size: 14px; font-size: 14px;

2
src/pages/system/deviceField/components/CategoryDlg.vue

@ -137,7 +137,7 @@ function formatterParams(params: CategoryOV): CategoryOV {
} }
.label { .label {
width: 110px; width: 110px;
color: var(--label-text); color: var(--label-color);
line-height: 33px; line-height: 33px;
text-align: right; text-align: right;
.require { .require {

2
src/pages/system/deviceField/components/CategoryTree.vue

@ -199,7 +199,7 @@ defineExpose({
display: flex; display: flex;
column-gap: 8px; column-gap: 8px;
font-size: 14px; font-size: 14px;
color: var(--label-text); color: var(--label-color);
} }
.description { .description {
font-size: 12px; font-size: 12px;

251
src/pages/system/login/index.vue

@ -1,80 +1,38 @@
<template> <template>
<div class="main"> <div class="main">
<div class="form"> <div class="header">
<div class="head"> <img :src="Logo" class="icon" />
<!-- <img :src="logoIcon" class="logo" /> --> <div class="sys-title">
<div class="title">储能系统-能量管理平台</div> <span class="title-cn">比特电科-设备管理系统</span>
<span class="title-en">BTDK-Equipment Management System</span>
</div>
</div>
<div class="body">
<img :src="bg" class="bg" alt="" />
<div class="form">
<div class="title">账号登录</div>
<el-form label-position="top" :model="account">
<el-form-item class="label">
<el-input v-model="account.username" class="input" placeholder="请输入账号" />
</el-form-item>
<el-form-item class="label">
<el-input
type="password"
v-model="account.password"
class="input"
placeholder="请输入密码"
/>
</el-form-item>
</el-form>
<el-button type="primary" class="login-btn" @click="onLogin" :loading="isLoading"
>登录</el-button
>
</div> </div>
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="tabClick">
<el-tab-pane label="账号登录" name="account">
<el-form label-position="top" :model="account">
<!-- <el-form-item class="label">
<el-input
v-model="account.tenantName"
class="input"
placeholder="请输入租户名称"
/>
</el-form-item> -->
<el-form-item class="label">
<el-input
v-model="account.username"
class="input"
placeholder="请输入账号"
/>
</el-form-item>
<el-form-item class="label">
<el-input
type="password"
v-model="account.password"
class="input"
placeholder="请输入密码"
/>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- <el-tab-pane label="手机登录" name="mobile">
<el-form label-position="top" :model="mobile">
<el-form-item label="租户" class="label">
<el-input v-model="mobile.tenantName" class="input" />
</el-form-item>
<el-form-item label="手机号" class="label">
<el-input v-model="mobile.number" class="input" />
</el-form-item>
<el-form-item label="验证码" class="label">
<el-input v-model="mobile.code" class="input">
<template #append>
<span
v-if="mobileCodeTimer <= 0"
class="getMobileCode"
style="cursor: pointer"
@click="getSmsCode"
>
获取验证码
</span>
<span
v-if="mobileCodeTimer > 0"
class="getMobileCode"
style="cursor: pointer"
>
{{ mobileCodeTimer }}秒后可重新获取
</span>
</template>
</el-input>
</el-form-item>
</el-form>
</el-tab-pane> -->
</el-tabs>
<el-button type="primary" class="login-btn" @click="onLogin" :loading="isLoading"
>登录</el-button
>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import logoIcon from '@/assets/image/login/icon_logo_pg.png'
import { import {
login, login,
getTenantId, getTenantId,
@ -82,23 +40,20 @@ import {
smsLogin, smsLogin,
type LoginRequestData, type LoginRequestData,
} from '@/api/module/system/login' } from '@/api/module/system/login'
import Logo from '@/assets/image/dashboard/login/logo.png'
import bg from '@/assets/image/dashboard/login/bg.png'
import { setRefreshToken, setTenantId } from '@/utils/auth' import { setRefreshToken, setTenantId } from '@/utils/auth'
import { setToken } from '@/utils/auth' import { setToken } from '@/utils/auth'
import { isResError } from '@/hooks/useMessage' import { isResError } from '@/hooks/useMessage'
import { useIcon } from '@/utils/useIcon' import { useIcon } from '@/utils/useIcon'
import message from 'element-plus/es/components/message/index.mjs' import message from 'element-plus/es/components/message/index.mjs'
import { useWindowKeyEnter } from '@/tools/common/hooks' import { useWindowKeyEnter } from '@/tools/common/hooks'
import { RouterView, useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const iconHouse = useIcon({ icon: 'ep:house' })
const iconCellphone = useIcon({ icon: 'ep:cellphone' })
const iconCircleCheck = useIcon({ icon: 'ep:circle-check' })
const isLoading = ref(false) const isLoading = ref(false)
const router = useRouter() const router = useRouter()
const activeName = ref('account') const activeName = ref('account')
function tabClick() {}
const account = reactive<LoginRequestData>({ const account = reactive<LoginRequestData>({
username: '', username: '',
password: '', password: '',
@ -128,7 +83,7 @@ async function onLogin() {
} }
isLoading.value = false isLoading.value = false
router.push('/device/data') router.push('/home')
} }
async function loginByAccount() { async function loginByAccount() {
@ -220,117 +175,71 @@ onMounted(() => {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-image: url('@/assets/image/login/bg.png'); background: linear-gradient(to bottom right, #08a4f0, #7f80fe);
background-repeat: no-repeat; .header {
background-size: cover;
:deep(.el-tabs__nav) {
width: 100%;
}
:deep(.el-tabs__item) {
flex: 1;
font-size: 16px;
}
.form {
position: absolute;
right: 264px;
top: 50%;
transform: translateY(-50%);
width: 520px;
height: 480px;
background: #ffffff;
border-radius: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; height: 92px;
flex-direction: column; padding-left: 24px;
.icon {
width: 52px;
height: 52px;
margin-right: 12px;
}
.head { .sys-title {
display: flex; display: flex;
justify-content: center; flex-direction: column;
align-items: center; font-size: 26px;
margin-bottom: 24px; color: #ffffff;
font-weight: 700;
.logo { .title-cn {
width: 60px; margin-bottom: 8px;
height: 60px;
margin-right: 12px;
} }
.title { .title-en {
font-size: 33px; font-size: 16px;
color: #252525;
font-weight: 700;
} }
} }
}
:deep(.el-tabs__header) { .body {
margin-bottom: 34px; height: calc(100% - 92px);
} display: flex;
justify-content: space-around;
:deep(.el-form-item) { .bg {
margin-bottom: 30px; // max-width: 1010px;
} // max-height: 1010px;
.label {
--el-form-label-font-size: 16px;
--el-text-color-regular: #4d4d4d;
}
.input {
--el-input-text-color: #000;
--el-input-height: 36px;
--el-input-hover-border-color: #2aa2ff;
--el-input-focus-border-color: #2aa2ff;
--el-input-border-color: #ccc;
width: 360px;
} }
}
.code-input { .form {
width: 258px; width: 440px;
padding: 64px 44px;
background-color: #ffffff;
border-radius: 3px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
margin: auto 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
.title {
font-size: 24px;
font-weight: 600;
text-align: center;
color: #333333;
margin-bottom: 48px;
} }
:deep(.el-form-item) {
.code-btn { margin-bottom: 26px;
width: 102px;
height: 36px;
border: 1px solid rgba(204, 204, 204, 1);
border-radius: 2px;
line-height: 36px;
font-size: 9px;
color: rgba(0, 0, 0, 0.65);
font-weight: 400;
margin-left: 8px;
} }
:deep(.el-input) {
.code-img { height: 40px;
width: 102px;
height: 45px;
margin-left: 8px;
} }
:deep(.el-button) {
.login-btn { height: 40px;
width: 360px;
height: 46px;
background-image: linear-gradient(180deg, #31afff 0%, #1677ff 100%);
color: #ffffff;
text-align: center;
line-height: 24px;
font-weight: 600;
margin-top: 20px;
font-family: Alibaba-PuHuiTi-B;
font-size: 16px;
color: #ffffff;
} }
// .input {
// width: 368px;
// }
} }
} }
</style> </style>
<!-- <style>
.custom {
--el-input-text-color: red;
}
</style> -->

2
src/pages/system/manufacturer/components/manufacturerDlg.vue

@ -126,7 +126,7 @@ function validate() {
} }
.label { .label {
width: 110px; width: 110px;
color: var(--label-text); color: var(--label-color);
line-height: 33px; line-height: 33px;
text-align: right; text-align: right;
.require { .require {

2
src/router/index.ts

@ -144,7 +144,7 @@ router.beforeEach(async (to, from, next) => {
} }
if (to.path === '/login' || to.path === '/') { if (to.path === '/login' || to.path === '/') {
next('/device/data') next('/home')
return return
} }
const userStore = useUserStore() const userStore = useUserStore()

4
src/styles/var.css

@ -14,7 +14,7 @@ html[data-theme='dark'] {
--station-header-bg: #363a40; --station-header-bg: #363a40;
--station-header-text-color: #fff; --station-header-text-color: #fff;
--station-info-val-text: #fff; --station-info-val-text: #fff;
--label-text: #c7c8cb; --label-color: #c7c8cb;
--text-desc: #8D9095; --text-desc: #8D9095;
} }
@ -34,7 +34,7 @@ html[data-theme='light'] {
--station-header-bg: #f1f1f1; --station-header-bg: #f1f1f1;
--station-header-text-color: #030303; --station-header-text-color: #030303;
--station-info-val-text: #4d4d4d; --station-info-val-text: #4d4d4d;
--label-text: #4d4d4d; --label-color: #4d4d4d;
--text-desc: #a8abb2; --text-desc: #a8abb2;
} }

Loading…
Cancel
Save