feat: 后端国际化、pie图表准备

This commit is contained in:
chenjinsong
2021-06-22 21:19:04 +08:00
parent 5fa8c1d31d
commit b0d72f20e7
13 changed files with 173 additions and 72 deletions

View File

@@ -97,3 +97,6 @@ eg
`el.style.padding = 'xxx'`
`el.style.margin = 'xxx'`
`el.style.border = 'xxx'`
- **布局**
避免使用float视情况使用position建议使用flex和grid

View File

@@ -5,14 +5,10 @@
</template>
<script>
import { get } from '@/utils/http'
import axios from 'axios'
export default {
name: 'App',
setup () {
get(`${process.env.BASE_URL}config.json?Timestamp=${new Date().getTime()}`).then(config => {
axios.defaults.baseURL = config.baseUrl
})
// 处理刷新后 $dayJs的时区变为默认的问题
const timezone = localStorage.getItem('cn-sys-timezone') || ''
if (timezone) {

View File

@@ -43,3 +43,10 @@ body {
display: inline-block;
}
}
th *:first-letter,
.left-menu *:first-letter,
.option-popper *,
.header__operations *:first-letter {
text-transform: capitalize;
}

View File

@@ -3,7 +3,7 @@
<div class="single-value__icon"><i class="el-icon-apple"></i></div>
<div class="single-value__content" v-if="type === 51">
<div class="content__data">11112</div>
<div class="content__title">嘻嘻</div>
<div class="content__title">{{$t('common.save')}}</div>
</div>
<div class="single-value__content" v-if="type === 53">
<div class="content__title">嘻嘻</div>

View File

@@ -0,0 +1,13 @@
<template>
<div class="cn-chart cn-chart__title"></div>
</template>
<script>
export default {
name: 'ChartTitle'
}
</script>
<style scoped>
</style>

View File

@@ -117,9 +117,11 @@ const lineStack = {
}
]
}
const pieWithTable = {}
const typeOptionMappings = [
{ value: 11, option: line }, // 常规折线图
{ value: 13, option: lineStack }, // 常规折线图
{ value: 31, option: pieWithTable }, // 常规折线图
{ value: 91, option: line }, // tab容器
{ value: 92, option: line }, // tab页
{ value: 93, option: line } // 大标题
@@ -157,6 +159,10 @@ export function isSingleValue (type) {
export function isSingleValueWithEcharts (type) {
return type === 52
}
/* 带Table的饼图 */
export function isEchartsWithTable (type) {
return type === 31
}
/* table */
export function isTable (type) {
return type >= 61 && type <= 70
@@ -183,4 +189,4 @@ export function getLayout (type) {
}
return layout
}
export const heightUnit = 150
export const heightUnit = 50

View File

@@ -1,11 +1,20 @@
.cn-panel {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(auto-fill, 50px);
grid-auto-flow: row;
grid-auto-rows: 50px;
grid-gap: 10px;
padding: 20px;
height: 100%;
width: 100%;
overflow: auto;
padding-right: 20px;
position: relative;
.panel__time {
position: absolute;
right: 20px;
top: 20px;
}
.cn-chart {
background-color: #FFFFFF;
@@ -169,8 +178,8 @@
}
.option__select {
.el-input__inner {
width: 80px;
padding-right: 20px;
width: 120px;
border: none;
height: 100%;
line-height: 20px;
@@ -193,9 +202,10 @@
}
}
}
.option__select.select__topn {
.option__select.select-column {
.el-input__inner {
width: 80px;
width: 86px;
padding-left: 8px;
}
}
.icon-group-divide {
@@ -236,7 +246,7 @@
}
}
}
.option__topn-popper {
.option-popper {
.el-select-dropdown__item {
height: 24px;
line-height: 24px;

View File

@@ -1,11 +1,16 @@
import { createI18n } from 'vue-i18n'
import cn from './cn'
import en from './en'
import { storageKey } from '@/utils/constants'
import { getI18n } from '@/utils/api'
const i18n = createI18n({
locale: localStorage.getItem('cn-language') || 'en',
messages: {
cn: cn,
en: en
}
locale: localStorage.getItem(storageKey.language) || 'en',
messages: {}
})
export async function loadI18n () {
const items = await getI18n()
sessionStorage.setItem(storageKey.i18n, 'true')
Object.keys(items).forEach(lang => {
i18n.global.mergeLocaleMessage(lang, items[lang])
})
}
export default i18n

View File

@@ -1,38 +1,42 @@
import router from './router'
import store from './store'
import { get, post } from './utils/http'
import { ElMessage } from 'element-plus'
import { getConfigJson, getPermission } from '@/utils/api'
import axios from 'axios'
import { storageKey } from '@/utils/constants'
import { loadI18n } from '@/i18n'
const loginWhiteList = ['/login'] // 免登陆白名单
const permissionWhiteList = [...loginWhiteList] // 权限白名单
router.beforeEach((to, from, next) => {
if (sessionStorage.getItem('cn-token')) {
router.beforeEach(async (to, from, next) => {
// 加载baseUrl
if (!axios.defaults.baseURL) {
const config = await getConfigJson()
axios.defaults.baseURL = config.baseUrl
}
if (sessionStorage.getItem(storageKey.token)) {
// 加载i18n
if (!sessionStorage.getItem(storageKey.i18n)) {
await loadI18n()
}
if (permissionWhiteList.indexOf(to.path) !== -1) {
next()
} else {
new Promise(resolve => {
if (store.getters.menuList.length === 0) {
get(`${process.env.BASE_URL}config.json?Timestamp=${new Date().getTime()}`).then(config => {
post(config.baseUrl + 'sys/user/permissions', { token: sessionStorage.getItem('cn-token') }).then(res => {
store.commit('setMenuList', sortByOrderNum(res.data.menus))
store.commit('setButtonList', res.data.buttons)
store.commit('setRoleList', res.data.roles)
resolve()
})
})
if (store.getters.menuList.length === 0) {
const { menuList, buttonList, roleList } = await getPermission()
store.commit('setMenuList', menuList)
store.commit('setButtonList', buttonList)
store.commit('setRoleList', roleList)
}
if (to.path) {
if (hasMenu(store.getters.menuList, to.path)) {
next()
} else {
resolve()
ElMessage.error('No access') // TODO 国际化
}
}).then(res => {
if (to.path) {
if (hasMenu(store.getters.menuList, to.path)) {
next()
} else {
ElMessage.error('No access') // TODO 国际化
}
}
})
}
}
} else {
if (loginWhiteList.indexOf(to.path) !== -1) {

View File

@@ -1,11 +1,17 @@
/**
* @author 陈劲松
* @date 2021/6/11
* @description 1.定义api2.定义通用查询函数,函数名应为 获取详情getItem、获取列表getItemList。例如getUser、getUserList
* @description 1.定义api2.定义通用查询方法,函数名应为 获取详情getItem、获取列表getItemList。例如getUser、getUserList
*/
import { get } from '@/utils/http'
import { get, post } from '@/utils/http'
import { sortByOrderNum } from '@/permission'
export const api = {
// 系统相关
permission: '/sys/user/permissions',
i18n: '/sys/i18n/lang',
dict: '/sys/dict',
// 业务
panel: '/visual/panel',
chart: '/visual/chart'
}
@@ -23,9 +29,13 @@ export async function getChartList (params) {
export async function getChart (id) {
return await getData(`${api.chart}/${id}`)
}
/* 字典 */
export async function getDictList (params) {
return await getData(api.dict, params, true)
}
export async function getData (url, params = {}, isQueryList) {
const request = new Promise((resolve, reject) => {
const request = new Promise(resolve => {
get(url, params).then(response => {
if (response.code === 200) {
resolve(isQueryList ? response.data.list : response.data)
@@ -34,3 +44,36 @@ export async function getData (url, params = {}, isQueryList) {
})
return await request
}
export async function getConfigJson () {
const request = new Promise(resolve => {
get(`${process.env.BASE_URL}config.json?Timestamp=${new Date().getTime()}`).then(config => {
resolve(config)
})
})
return await request
}
export async function getPermission () {
const request = new Promise(resolve => {
post(api.permission, { token: sessionStorage.getItem('cn-token') }).then(response => {
resolve({
menuList: sortByOrderNum(response.data.menus),
buttonList: response.data.buttons,
roleList: response.data.roles
})
})
})
return await request
}
export async function getI18n () {
const dictData = await getDictList()
const langs = dictData.map(d => d.value).join(',')
const request = new Promise(resolve => {
get(api.i18n, { l: langs }).then(response => {
resolve(response.data)
})
})
return await request
}

View File

@@ -1,5 +1,21 @@
export const defaultPageSize = 20
export const storageKey = {
i18n: 'cn-i18n',
language: 'cn-language',
timezone: 'cn-timezone',
timezoneOffset: 'cn-timezone-offset',
timezoneLocalOffset: 'cn-timezone-local-offset',
token: 'cn-token',
username: 'cn-username',
sysName: 'cn-sys-name',
sysLogo: 'cn-sys-logo',
tableTitlePrefix: 'cn-table-title',
tablePageSizePrefix: 'cn-page-size',
leftMenuShrink: 'cn-left-menu-shrink',
unsavedChange: 'cn-unsaved-change'
}
// 统一定义跳转来源
export const fromRoute = {
trafficSummary: 'trafficSummary',
@@ -22,4 +38,4 @@ export const position = {
}
export const chartTableDefaultPageSize = 10 // table类型图表默认每页数据量
export const chartTableTopOptions = [10, 50, 100] // table类型图表的TOP-N选项
export const chartTableTopOptions = [10, 100] // table类型图表的TOP-N选项

View File

@@ -1,4 +1,5 @@
<template>
<!-- echarts类的图标如饼图柱状图折线图等 -->
<echarts-frame
v-if="isEcharts"
:layout="layout"
@@ -11,14 +12,20 @@
<template #default>
<div class="chart-drawing" :id="`chart${chartInfo.id}`"></div>
</template>
<template #footer v-if="layout.indexOf(layoutConstant.FOOTER) > -1"></template>
<template #footer v-if="layout.indexOf(layoutConstant.FOOTER) > -1">
<!-- 带Table的饼图展示Table -->
<template v-if="isEchartsWithTable">
</template>
</template>
</echarts-frame>
<!-- 单值图 -->
<single-value
v-else-if="isSingleValue"
:type="chartInfo.type"
:style="computePosition"
>
</single-value>
<!-- 表格 -->
<chart-table
v-else-if="isTable"
:table-columns="table.tableColumns"
@@ -27,16 +34,16 @@
>
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
<template #operations>
<div class="header__operation header__operation--table">
<!-- <div class="header__operation header__operation&#45;&#45;table">
<span class="option__button"><i class="cn-icon cn-icon-download"></i></span>
</div>
</div>-->
<div class="header__operation header__operation--table">
<el-select
size="mini"
v-model="table.limit"
class="option__select select__topn"
class="option__select select-topn"
placeholder=""
popper-class="option__topn-popper"
popper-class="option-popper"
>
<el-option v-for="item in chartTableTopOptions" :key="item" :value="item">TOP&nbsp;{{item}}</el-option>
<template #prefix>TOP&nbsp;</template>
@@ -46,18 +53,18 @@
<el-select
size="mini"
v-model="table.orderBy"
class="option__select"
class="option__select select-column"
placeholder=""
popper-class="option__topn-popper"
popper-class="option-popper"
>
<el-option v-for="item in table.tableColumns" :key="item" :value="item">{{item}}</el-option>
</el-select>
</div>
<div class="header__operation header__operation--table">
<!-- <div class="header__operation header__operation&#45;&#45;table">
<span class="option__button"><i class="cn-icon cn-icon-style"></i></span>
<div class="icon-group-divide"></div>
<span class="option__button"><i class="cn-icon cn-icon-dropdown"></i></span>
</div>
</div>-->
<div class="header__operation header__operation--table">
<span class="option__button"><i class="cn-icon cn-icon-full-screen"></i></span>
</div>
@@ -73,7 +80,7 @@
<script>
import * as echarts from 'echarts'
import { isEcharts, isSingleValue, isTable, getOption, getTypeCategory, getLayout, layoutConstant, heightUnit } from '@/components/charts/chart-options'
import { isEcharts, isSingleValue, isTable, getOption, getTypeCategory, getLayout, layoutConstant, heightUnit, isEchartsWithTable } from '@/components/charts/chart-options'
import EchartsFrame from '@/components/charts/EchartsFrame'
import SingleValue from '@/components/charts/ChartSingleValue'
import Table from '@/components/charts/ChartTable'
@@ -172,6 +179,7 @@ export default {
chartTableTopOptions,
chartOption: getOption(props.chart.type),
isEcharts: isEcharts(props.chart.type),
isEchartsWithTable: isEchartsWithTable(props.chart.type),
isSingleValue: isSingleValue(props.chart.type),
isTable: isTable(props.chart.type),
layout: getLayout(props.chart.type)

View File

@@ -1,10 +1,10 @@
<template>
<div>
<div class="panel-header">
<TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="endTime"/>
<DateTimeRange class="date-time-range" :start-time="startTime" :end-time="endTime" ref="dateTimeRange" @change="reload"/>
</div>
<div style="padding: 10px 0 20px 20px;">
<div class="cn-panel">
<div class="panel__time">
<TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="endTime"/>
<DateTimeRange class="date-time-range" :start-time="startTime" :end-time="endTime" ref="dateTimeRange" @change="reload"/>
</div>
<chart v-for="(chart, index) in chartList" :key="index" :chart="chart"></chart>
<!-- <grid-layout v-model:layout="chartList"
:col-num="12"
@@ -134,14 +134,4 @@ export default {
</script>
<style lang="scss">
@import '~@/components/charts/panel.scss';
.panel-header{
/*overflow: hidden;*/
display: inline-block;
width: 100%;
padding: 20px 20px 0px 20px;
.date-time-range{
float: right;
margin-left: 10px;
}
}
</style>