feat: 后端国际化、pie图表准备
This commit is contained in:
@@ -97,3 +97,6 @@ eg:
|
||||
`el.style.padding = 'xxx'`
|
||||
`el.style.margin = 'xxx'`
|
||||
`el.style.border = 'xxx'`
|
||||
|
||||
- **布局**
|
||||
避免使用float,视情况使用position,建议使用flex和grid
|
||||
@@ -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) {
|
||||
|
||||
@@ -43,3 +43,10 @@ body {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
th *:first-letter,
|
||||
.left-menu *:first-letter,
|
||||
.option-popper *,
|
||||
.header__operations *:first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
13
src/components/charts/ChartTitle.vue
Normal file
13
src/components/charts/ChartTitle.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="cn-chart cn-chart__title"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ChartTitle'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
/**
|
||||
* @author 陈劲松
|
||||
* @date 2021/6/11
|
||||
* @description 1.定义api;2.定义通用查询函数,函数名应为 获取详情getItem、获取列表getItemList。例如getUser、getUserList
|
||||
* @description 1.定义api;2.定义通用查询方法,函数名应为 获取详情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
|
||||
}
|
||||
|
||||
@@ -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选项
|
||||
|
||||
@@ -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--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 {{item}}</el-option>
|
||||
<template #prefix>TOP </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--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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user