mirror of
https://gitee.com/dromara/go-view.git
synced 2025-10-13 22:12:11 +08:00
feat: 增加组件
This commit is contained in:
parent
b9268d89ec
commit
72c56f7848
@ -36,6 +36,7 @@
|
|||||||
"iconify-icon": "^1.0.8",
|
"iconify-icon": "^1.0.8",
|
||||||
"keymaster": "^1.6.2",
|
"keymaster": "^1.6.2",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
|
"moment": "^2.29.4",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.33.0",
|
||||||
"naive-ui": "2.34.3",
|
"naive-ui": "2.34.3",
|
||||||
"pinia": "^2.0.13",
|
"pinia": "^2.0.13",
|
||||||
|
@ -12,6 +12,8 @@ export interface MyResponseType<T> {
|
|||||||
code: ResultEnum
|
code: ResultEnum
|
||||||
data: T
|
data: T
|
||||||
message: string
|
message: string
|
||||||
|
// 兼顾主系统
|
||||||
|
errcode: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MyRequestInstance extends Axios {
|
export interface MyRequestInstance extends Axios {
|
||||||
@ -25,17 +27,17 @@ const axiosInstance = axios.create({
|
|||||||
|
|
||||||
axiosInstance.interceptors.request.use(
|
axiosInstance.interceptors.request.use(
|
||||||
(config: InternalAxiosRequestConfig) => {
|
(config: InternalAxiosRequestConfig) => {
|
||||||
// 白名单校验
|
// // 白名单校验
|
||||||
if (includes(fetchAllowList, config.url)) return config
|
// if (includes(fetchAllowList, config.url)) return config
|
||||||
// 获取 token
|
// // 获取 token
|
||||||
const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
|
// const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
|
||||||
// 重新登录
|
// // 重新登录
|
||||||
if (!info) {
|
// if (!info) {
|
||||||
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
|
// routerTurnByName(PageEnum.BASE_LOGIN_NAME)
|
||||||
return config
|
// return config
|
||||||
}
|
// }
|
||||||
const userInfo = info[SystemStoreEnum.USER_INFO]
|
// const userInfo = info[SystemStoreEnum.USER_INFO]
|
||||||
config.headers[userInfo[SystemStoreUserInfoEnum.TOKEN_NAME] || 'token'] = userInfo[SystemStoreUserInfoEnum.USER_TOKEN] || ''
|
// config.headers[userInfo[SystemStoreUserInfoEnum.TOKEN_NAME] || 'token'] = userInfo[SystemStoreUserInfoEnum.USER_TOKEN] || ''
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
(err: AxiosError) => {
|
(err: AxiosError) => {
|
||||||
@ -59,12 +61,12 @@ axiosInstance.interceptors.response.use(
|
|||||||
return Promise.resolve(res.data)
|
return Promise.resolve(res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登录过期
|
// // 登录过期
|
||||||
if (code === ResultEnum.TOKEN_OVERDUE) {
|
// if (code === ResultEnum.TOKEN_OVERDUE) {
|
||||||
window['$message'].error(window['$t']('http.token_overdue_message'))
|
// window['$message'].error(window['$t']('http.token_overdue_message'))
|
||||||
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
|
// routerTurnByName(PageEnum.BASE_LOGIN_NAME)
|
||||||
return Promise.resolve(res.data)
|
// return Promise.resolve(res.data)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 固定错误码重定向
|
// 固定错误码重定向
|
||||||
if (ErrorPageNameMap.get(code)) {
|
if (ErrorPageNameMap.get(code)) {
|
||||||
|
@ -8,6 +8,8 @@ import {
|
|||||||
RequestParamsObjType
|
RequestParamsObjType
|
||||||
} from '@/enums/httpEnum'
|
} from '@/enums/httpEnum'
|
||||||
import type { RequestGlobalConfigType, RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
|
import type { RequestGlobalConfigType, RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
|
||||||
|
import moment from "moment";
|
||||||
|
import {getToken} from "@/api/path";
|
||||||
|
|
||||||
export const get = <T = any>(url: string, params?: object) => {
|
export const get = <T = any>(url: string, params?: object) => {
|
||||||
return axiosInstance<T>({
|
return axiosInstance<T>({
|
||||||
@ -139,7 +141,9 @@ export const customizeHttp = (targetParams: RequestConfigType, globalParams: Req
|
|||||||
// SQL 请求对象
|
// SQL 请求对象
|
||||||
requestSQLContent,
|
requestSQLContent,
|
||||||
// 请求内容 params / cookie / header / body: 同 requestParamsBodyType
|
// 请求内容 params / cookie / header / body: 同 requestParamsBodyType
|
||||||
requestParams: targetRequestParams
|
requestParams: targetRequestParams,
|
||||||
|
// 请求参数body-json预处理
|
||||||
|
requestBodyJSONPre
|
||||||
} = targetParams
|
} = targetParams
|
||||||
|
|
||||||
// 静态排除
|
// 静态排除
|
||||||
@ -172,9 +176,22 @@ export const customizeHttp = (targetParams: RequestConfigType, globalParams: Req
|
|||||||
case RequestBodyEnum.JSON:
|
case RequestBodyEnum.JSON:
|
||||||
headers['Content-Type'] = ContentTypeEnum.JSON
|
headers['Content-Type'] = ContentTypeEnum.JSON
|
||||||
//json对象也能使用'javasctipt:'来动态拼接参数
|
//json对象也能使用'javasctipt:'来动态拼接参数
|
||||||
data = translateStr(targetRequestParams.Body['json'])
|
if(requestBodyJSONPre.enable) {
|
||||||
if(typeof data === 'string') data = JSON.parse(data)
|
const fn = new Function('global', requestBodyJSONPre.handler)
|
||||||
// json 赋值给 data
|
const global = {
|
||||||
|
moment,
|
||||||
|
getToken
|
||||||
|
}
|
||||||
|
const res = fn(global)
|
||||||
|
// @ts-ignore
|
||||||
|
data = JSON.stringify(res, null, 2)
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data = translateStr(targetRequestParams.Body['json'])
|
||||||
|
if(typeof data === 'string') data = JSON.parse(data)
|
||||||
|
// json 赋值给 data
|
||||||
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case RequestBodyEnum.XML:
|
case RequestBodyEnum.XML:
|
||||||
|
40
src/api/path/business.api.ts
Normal file
40
src/api/path/business.api.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// import Cookies from 'js-cookie';
|
||||||
|
import { http } from "@/api/http";
|
||||||
|
import { RequestHttpEnum } from "@/enums/httpEnum";
|
||||||
|
import { httpErrorHandle } from '@/utils'
|
||||||
|
|
||||||
|
const allCookies = document.cookie;
|
||||||
|
function getCookie(name: string) {
|
||||||
|
const cookies = document.cookie.split(';');
|
||||||
|
for (const cookie of cookies) {
|
||||||
|
const [cookieName, cookieValue] = cookie.trim().split('=');
|
||||||
|
if (cookieName === name) {
|
||||||
|
return decodeURIComponent(cookieValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
console.log(allCookies, getCookie('access_token'));
|
||||||
|
|
||||||
|
export function getToken() {
|
||||||
|
const TokenKey = 'access_token'
|
||||||
|
if (getCookie(TokenKey)) {
|
||||||
|
return getCookie(TokenKey)
|
||||||
|
} else {
|
||||||
|
return sessionStorage.getItem(TokenKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const publicInterface = async (paramType:string, interfaceType:string, paramData:any) =>{
|
||||||
|
try {
|
||||||
|
const access_token = getToken()
|
||||||
|
const res = await http(RequestHttpEnum.POST)<any>(paramType, {
|
||||||
|
access_token,
|
||||||
|
type: interfaceType,
|
||||||
|
data: paramData
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
} catch {
|
||||||
|
httpErrorHandle()
|
||||||
|
}
|
||||||
|
}
|
0
src/api/path/business.d.ts
vendored
Normal file
0
src/api/path/business.d.ts
vendored
Normal file
@ -1,2 +1,3 @@
|
|||||||
export * from '@/api/path/project.api'
|
export * from '@/api/path/project.api'
|
||||||
export * from '@/api/path/system.api'
|
export * from '@/api/path/system.api'
|
||||||
|
export * from '@/api/path/business.api'
|
||||||
|
BIN
src/assets/images/chart/charts/line_with_data_zoom.png
Normal file
BIN
src/assets/images/chart/charts/line_with_data_zoom.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -38,7 +38,7 @@ defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
$leftWidth: 60px;
|
$leftWidth: 100px;
|
||||||
@include go('config-item-box') {
|
@include go('config-item-box') {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -72,6 +72,8 @@ export const useChartDataFetch = (
|
|||||||
clearInterval(fetchInterval)
|
clearInterval(fetchInterval)
|
||||||
|
|
||||||
const fetchFn = async () => {
|
const fetchFn = async () => {
|
||||||
|
console.log(1234, JSON.parse(JSON.stringify(toRaw(targetComponent.request))))
|
||||||
|
console.log(1234, JSON.parse(JSON.stringify(toRaw(chartEditStore.getRequestGlobalConfig))))
|
||||||
const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.getRequestGlobalConfig))
|
const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.getRequestGlobalConfig))
|
||||||
if (res) {
|
if (res) {
|
||||||
try {
|
try {
|
||||||
|
154
src/packages/components/Charts/Lines/LineWithDataZoom/api.ts
Normal file
154
src/packages/components/Charts/Lines/LineWithDataZoom/api.ts
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import moment from "moment";
|
||||||
|
import { httpErrorHandle } from "@/utils";
|
||||||
|
import { publicInterface } from '@/api/path/business.api'
|
||||||
|
import { EchartsDataType } from '@/packages/index.d'
|
||||||
|
|
||||||
|
import {
|
||||||
|
RequestBodyEnum,
|
||||||
|
RequestHttpEnum,
|
||||||
|
RequestHttpIntervalEnum,
|
||||||
|
RequestParams, RequestParamsObjType,
|
||||||
|
RequestParamsTypeEnum
|
||||||
|
} from "@/enums/httpEnum";
|
||||||
|
import { getToken } from '@/api/path/business.api'
|
||||||
|
|
||||||
|
// export const getData = async () => {
|
||||||
|
// try {
|
||||||
|
// const methodArr = 'handle'.split('_')
|
||||||
|
// const queryNodeFlowParam = {
|
||||||
|
// types: ['rec'],
|
||||||
|
// start_time: moment(new Date(new Date().getTime() - 10 * 60 * 1000)).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
// end_time: moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
// duration: 15,
|
||||||
|
// query_type: ''
|
||||||
|
// }
|
||||||
|
// queryNodeFlowParam.types = [methodArr[0]]
|
||||||
|
// queryNodeFlowParam.query_type = methodArr[1] || ''
|
||||||
|
// const start_time = moment().subtract(10, 'm').format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
// const end_time = moment().format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
// queryNodeFlowParam.start_time = start_time
|
||||||
|
// queryNodeFlowParam.end_time = end_time
|
||||||
|
// const res = await publicInterface('/dcim/dems/statistic', 'get_point_realtime_data_echarts', queryNodeFlowParam)
|
||||||
|
// const obj:EchartsDataType = {
|
||||||
|
// dimensions: [],
|
||||||
|
// source: []
|
||||||
|
// }
|
||||||
|
// if(res && res.errcode === '00000') {
|
||||||
|
// interface SeriesItemType {
|
||||||
|
// data: Array<any>
|
||||||
|
// }
|
||||||
|
// const x = res.data.echarts.xAxis.data.slice(-10)
|
||||||
|
// const v = res.data.echarts.series.map((_:SeriesItemType) => _.data.slice(-10))
|
||||||
|
// let xAxisName = ''
|
||||||
|
// if(queryNodeFlowParam.query_type === '') xAxisName = '速率(测点/s)'
|
||||||
|
// else xAxisName = '测点总数'
|
||||||
|
// obj.dimensions = ['product', ...Array(v.length).fill({}).map((_, i) => `${xAxisName}_${i + 1}`)]
|
||||||
|
// obj.source = x.map((_:any) => ({product: _}))
|
||||||
|
// obj.dimensions.slice(1).forEach((key, vi) => {
|
||||||
|
// obj.source.forEach((item, i) => {
|
||||||
|
// item[key] = v[vi][i]
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// return obj
|
||||||
|
// } catch {
|
||||||
|
// httpErrorHandle()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const methodArr = 'handle'.split('_')
|
||||||
|
const queryNodeFlowParam = {
|
||||||
|
types: ['rec'],
|
||||||
|
start_time: moment(new Date(new Date().getTime() - 10 * 60 * 1000)).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
end_time: moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
duration: 15,
|
||||||
|
query_type: ''
|
||||||
|
}
|
||||||
|
queryNodeFlowParam.types = [methodArr[0]]
|
||||||
|
queryNodeFlowParam.query_type = methodArr[1] || ''
|
||||||
|
const start_time = moment().subtract(10, 'm').format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
const end_time = moment().format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
queryNodeFlowParam.start_time = start_time
|
||||||
|
queryNodeFlowParam.end_time = end_time
|
||||||
|
|
||||||
|
function requestBodyJSONPreHandler(global:any) {
|
||||||
|
const methodArr = 'handle'.split('_')
|
||||||
|
const obj = {
|
||||||
|
access_token: global.getToken(),
|
||||||
|
type: 'get_point_realtime_data_echarts',
|
||||||
|
data: {
|
||||||
|
types: [methodArr[0]],
|
||||||
|
start_time: global.moment().subtract(10, 'm').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
end_time: global.moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
duration: 15,
|
||||||
|
query_type: methodArr[1] || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
const requestBodyJSONPreHandlerStr = requestBodyJSONPreHandler.toString().replace(/^function[\s\S]*?\{/m, '').replace(/\}$/, '')
|
||||||
|
|
||||||
|
export const requestConfig = {
|
||||||
|
"requestDataType": 1,
|
||||||
|
"requestHttpType": RequestHttpEnum.POST,
|
||||||
|
"requestUrl": "/api/goview/dcim/dems/statistic",
|
||||||
|
"requestInterval": 5,
|
||||||
|
"requestIntervalUnit": RequestHttpIntervalEnum.SECOND,
|
||||||
|
"requestContentType": 0,
|
||||||
|
"requestParamsBodyType": RequestBodyEnum.JSON,
|
||||||
|
"requestSQLContent": {
|
||||||
|
"sql":"select * from where"
|
||||||
|
},
|
||||||
|
"requestParams": {
|
||||||
|
[RequestParamsTypeEnum.BODY]: {
|
||||||
|
[RequestBodyEnum.JSON]: JSON.stringify({
|
||||||
|
access_token: getToken(),
|
||||||
|
type: 'get_point_realtime_data_echarts',
|
||||||
|
data: queryNodeFlowParam
|
||||||
|
}, null, 2)
|
||||||
|
}
|
||||||
|
} as RequestParams,
|
||||||
|
"requestBodyJSONPre": {
|
||||||
|
enable: true,
|
||||||
|
handler: requestBodyJSONPreHandlerStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// js代码配置filter 后面会转成string 忽略ts检查
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
function filterFn(data) {
|
||||||
|
const obj = {}
|
||||||
|
const x = data.echarts.xAxis.data.slice(-10)
|
||||||
|
// @ts-ignore
|
||||||
|
const v = data.echarts.series.map((_) => _.data.slice(-10))
|
||||||
|
// @ts-ignore
|
||||||
|
obj.dimensions = ['product', ...Array(v.length).fill({}).map((_, i) => `测点总数_${i + 1}`)]
|
||||||
|
// @ts-ignore
|
||||||
|
obj.source = x.map((_) => ({product: _}))
|
||||||
|
// @ts-ignore
|
||||||
|
obj.dimensions.slice(1).forEach((key, vi) => {
|
||||||
|
// @ts-ignore
|
||||||
|
obj.source.forEach((item, i) => {
|
||||||
|
item[key] = v[vi][i]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
export const filterConfig = filterFn.toString().replace(/^function[\s\S]*?\{/m, '').replace(/\}$/, '')
|
||||||
|
|
||||||
|
// export const requestConfig = {
|
||||||
|
// "requestDataType": 1,
|
||||||
|
// "requestHttpType": RequestHttpEnum.GET,
|
||||||
|
// "requestUrl": "/api/leftBottom",
|
||||||
|
// "requestInterval": 5,
|
||||||
|
// "requestIntervalUnit": RequestHttpIntervalEnum.SECOND,
|
||||||
|
// "requestContentType": 0,
|
||||||
|
// "requestParamsBodyType": RequestBodyEnum.NONE,
|
||||||
|
// "requestSQLContent": {
|
||||||
|
// "sql":"select * from where"
|
||||||
|
// },
|
||||||
|
// "requestParams": {} as RequestParams
|
||||||
|
// }
|
||||||
|
|
199
src/packages/components/Charts/Lines/LineWithDataZoom/config.ts
Normal file
199
src/packages/components/Charts/Lines/LineWithDataZoom/config.ts
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
import {echartOptionProfixHandle, PublicConfigClass, setData} from '@/packages/public'
|
||||||
|
import {LineWithDataZoomConfig} from './index'
|
||||||
|
import {CreateComponentType} from '@/packages/index.d'
|
||||||
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
|
import dataJson from './data.json'
|
||||||
|
import { graphic } from 'echarts'
|
||||||
|
// import { getData } from './api'
|
||||||
|
|
||||||
|
export const includes = ['legend', 'xAxis', 'yAxis', 'grid', 'tooltip', 'dataZoom']
|
||||||
|
|
||||||
|
export const seriesItem = {
|
||||||
|
type: 'line',
|
||||||
|
showSymbol: false,
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 8,
|
||||||
|
smooth: true,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#4196ff',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#4196ff',
|
||||||
|
borderColor: '#4196ff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
color: new graphic.LinearGradient(0, 0, 0, 1, [{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(65,150,255,0.5)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(65,150,255,0)'
|
||||||
|
}
|
||||||
|
], false),
|
||||||
|
shadowColor: 'rgba(65,150,255,0.3)',
|
||||||
|
shadowBlur: 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// let res = await getData()
|
||||||
|
|
||||||
|
export const option = {
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#ccc',
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
nameTextStyle: {
|
||||||
|
color: '#999'
|
||||||
|
},
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
color: '#ccc',
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed',
|
||||||
|
color: 'rgba(65, 150, 255, 0.2)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: '16px',
|
||||||
|
left: '16px',
|
||||||
|
right: '16px',
|
||||||
|
bottom: '32px',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: null
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
show: true,
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
height: 20,
|
||||||
|
bottom: 10,
|
||||||
|
textStyle: {
|
||||||
|
color: '#ccc'
|
||||||
|
},
|
||||||
|
dataBackground: {
|
||||||
|
areaStyle: {
|
||||||
|
color: '#353549',
|
||||||
|
borderColor: '#34424f'
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
color: '#364e68'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backgroundColor: '#222b35',
|
||||||
|
borderColor: '#34424f',
|
||||||
|
handleStyle: {
|
||||||
|
color: '#222b35',
|
||||||
|
borderColor: '#27558e'
|
||||||
|
},
|
||||||
|
moveHandleStyle: {
|
||||||
|
color: '#1b2e46',
|
||||||
|
borderColor: '#364e68'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
moveHandleStyle: {
|
||||||
|
color: '#2c5a92',
|
||||||
|
borderColor: '#364e68'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
show: true,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
filterMode: 'empty',
|
||||||
|
width: 20,
|
||||||
|
height: '80%',
|
||||||
|
showDataShadow: false,
|
||||||
|
right: 0,
|
||||||
|
textStyle: {
|
||||||
|
color: '#ccc'
|
||||||
|
},
|
||||||
|
dataBackground: {
|
||||||
|
areaStyle: {
|
||||||
|
color: '#353549',
|
||||||
|
// color: '#ff0000',
|
||||||
|
borderColor: '#34424f'
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
color: '#364e68'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backgroundColor: '#222b35',
|
||||||
|
borderColor: '#34424f',
|
||||||
|
handleStyle: {
|
||||||
|
color: '#222b35',
|
||||||
|
borderColor: '#27558e'
|
||||||
|
},
|
||||||
|
moveHandleStyle: {
|
||||||
|
color: '#1b2e46',
|
||||||
|
borderColor: '#364e68'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
moveHandleStyle: {
|
||||||
|
color: '#2c5a92',
|
||||||
|
borderColor: '#364e68'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
dataset: {...dataJson},
|
||||||
|
series: [seriesItem]
|
||||||
|
}
|
||||||
|
|
||||||
|
// setInterval(async() => {
|
||||||
|
// res = await getData()
|
||||||
|
// if (res !== undefined) setData(option, res)
|
||||||
|
// }, 15000)
|
||||||
|
|
||||||
|
export default class Config extends PublicConfigClass implements CreateComponentType {
|
||||||
|
public key: string = LineWithDataZoomConfig.key
|
||||||
|
public chartConfig = cloneDeep(LineWithDataZoomConfig)
|
||||||
|
// 图表配置项
|
||||||
|
public option = echartOptionProfixHandle(option, includes)
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<!-- Echarts 全局设置 -->
|
||||||
|
<global-setting :optionData="optionData"></global-setting>
|
||||||
|
<CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`折线图-${index + 1}`" :expanded="true">
|
||||||
|
<SettingItemBox name="线条">
|
||||||
|
<SettingItem name="宽度">
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="item.lineStyle.width"
|
||||||
|
:min="1"
|
||||||
|
:max="100"
|
||||||
|
size="small"
|
||||||
|
placeholder="自动计算"
|
||||||
|
></n-input-number>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem name="类型">
|
||||||
|
<n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select>
|
||||||
|
</SettingItem>
|
||||||
|
</SettingItemBox>
|
||||||
|
<SettingItemBox name="实心点">
|
||||||
|
<SettingItem name="大小">
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="item.symbolSize"
|
||||||
|
:min="1"
|
||||||
|
:max="100"
|
||||||
|
size="small"
|
||||||
|
placeholder="自动计算"
|
||||||
|
></n-input-number>
|
||||||
|
</SettingItem>
|
||||||
|
</SettingItemBox>
|
||||||
|
<setting-item-box name="标签">
|
||||||
|
<setting-item>
|
||||||
|
<n-space>
|
||||||
|
<n-switch v-model:value="item.label.show" size="small" />
|
||||||
|
<n-text>展示标签</n-text>
|
||||||
|
</n-space>
|
||||||
|
</setting-item>
|
||||||
|
<setting-item name="大小">
|
||||||
|
<n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
|
||||||
|
</setting-item>
|
||||||
|
<setting-item name="颜色">
|
||||||
|
<n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
|
||||||
|
</setting-item>
|
||||||
|
<setting-item name="位置">
|
||||||
|
<n-select
|
||||||
|
v-model:value="item.label.position"
|
||||||
|
:options="[
|
||||||
|
{ label: 'top', value: 'top' },
|
||||||
|
{ label: 'left', value: 'left' },
|
||||||
|
{ label: 'right', value: 'right' },
|
||||||
|
{ label: 'bottom', value: 'bottom' }
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</setting-item>
|
||||||
|
</setting-item-box>
|
||||||
|
</CollapseItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType, computed } from 'vue'
|
||||||
|
import { lineConf } from '@/packages/chartConfiguration/echarts/index'
|
||||||
|
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
|
||||||
|
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
optionData: {
|
||||||
|
type: Object as PropType<GlobalThemeJsonType>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const seriesList = computed(() => {
|
||||||
|
return props.optionData.series
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"dimensions": ["product", "data1"],
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"product": "2023-12-20 18:01:15",
|
||||||
|
"data1": 18.89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product": "2023-12-20 18:02:15",
|
||||||
|
"data1": 18.59
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product": "2023-12-20 18:03:15",
|
||||||
|
"data1": 18.19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product": "2023-12-20 18:04:15",
|
||||||
|
"data1": 19.89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product": "2023-12-20 18:05:15",
|
||||||
|
"data1": 20.89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product": "2023-12-20 18:06:15",
|
||||||
|
"data1": 16.89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product": "2023-12-20 18:07:15",
|
||||||
|
"data1": 18.89
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
|
||||||
|
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
|
||||||
|
|
||||||
|
export const LineWithDataZoomConfig: ConfigType = {
|
||||||
|
key: 'LineWithDataZoom',
|
||||||
|
chartKey: 'VLineWithDataZoom',
|
||||||
|
conKey: 'VCLineWithDataZoom',
|
||||||
|
title: '区域缩放折线图',
|
||||||
|
category: ChatCategoryEnum.LINE,
|
||||||
|
categoryName: ChatCategoryEnumName.LINE,
|
||||||
|
package: PackagesCategoryEnum.CHARTS,
|
||||||
|
chartFrame: ChartFrameEnum.ECHARTS,
|
||||||
|
image: 'line_with_data_zoom.png'
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<v-chart
|
||||||
|
ref="vChartRef"
|
||||||
|
:init-options="initOptions"
|
||||||
|
:theme="themeColor"
|
||||||
|
:option="option"
|
||||||
|
:manual-update="isPreview()"
|
||||||
|
:update-options="{
|
||||||
|
replaceMerge: replaceMergeArr
|
||||||
|
}"
|
||||||
|
autoresize
|
||||||
|
>
|
||||||
|
</v-chart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {PropType, computed, watch, ref, nextTick, onMounted, onBeforeUnmount, Ref} from 'vue'
|
||||||
|
import VChart from 'vue-echarts'
|
||||||
|
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
|
||||||
|
import { use } from 'echarts/core'
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
|
import { LineChart } from 'echarts/charts'
|
||||||
|
import config, { includes, seriesItem } from './config'
|
||||||
|
import { mergeTheme } from '@/packages/public/chart'
|
||||||
|
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||||
|
import { useChartDataFetch } from '@/hooks'
|
||||||
|
import {isPreview, newFunctionHandle} from '@/utils'
|
||||||
|
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
|
||||||
|
import isObject from 'lodash/isObject'
|
||||||
|
|
||||||
|
import { requestConfig, filterConfig } from './api'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
themeSetting: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
themeColor: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
chartConfig: {
|
||||||
|
type: Object as PropType<config>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
props.chartConfig.request = requestConfig
|
||||||
|
props.chartConfig.filter = filterConfig
|
||||||
|
|
||||||
|
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
|
||||||
|
|
||||||
|
console.log(props.chartConfig)
|
||||||
|
|
||||||
|
use([DatasetComponent, CanvasRenderer, LineChart, GridComponent, TooltipComponent, LegendComponent])
|
||||||
|
|
||||||
|
const replaceMergeArr = ref<string[]>()
|
||||||
|
|
||||||
|
const option = computed(() => {
|
||||||
|
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
|
||||||
|
})
|
||||||
|
|
||||||
|
// dataset 无法变更条数的补丁
|
||||||
|
watch(
|
||||||
|
() => props.chartConfig.option.dataset,
|
||||||
|
(newData: { dimensions: any }, oldData) => {
|
||||||
|
try {
|
||||||
|
if (!isObject(newData) || !('dimensions' in newData)) return
|
||||||
|
if (Array.isArray(newData?.dimensions)) {
|
||||||
|
const seriesArr = []
|
||||||
|
for (let i = 0; i < newData.dimensions.length - 1; i++) {
|
||||||
|
seriesArr.push(seriesItem)
|
||||||
|
}
|
||||||
|
replaceMergeArr.value = ['series']
|
||||||
|
props.chartConfig.option.series = seriesArr
|
||||||
|
nextTick(() => {
|
||||||
|
replaceMergeArr.value = []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
|
||||||
|
</script>
|
@ -1,6 +1,7 @@
|
|||||||
|
import { LineWithDataZoomConfig } from "./LineWithDataZoom";
|
||||||
import { LineCommonConfig } from './LineCommon/index'
|
import { LineCommonConfig } from './LineCommon/index'
|
||||||
import { LineLinearSingleConfig } from './LineLinearSingle/index'
|
import { LineLinearSingleConfig } from './LineLinearSingle/index'
|
||||||
import { LineGradientSingleConfig } from './LineGradientSingle/index'
|
import { LineGradientSingleConfig } from './LineGradientSingle/index'
|
||||||
import { LineGradientsConfig } from './LineGradients/index'
|
import { LineGradientsConfig } from './LineGradients/index'
|
||||||
|
|
||||||
export default [LineCommonConfig, LineLinearSingleConfig, LineGradientSingleConfig, LineGradientsConfig]
|
export default [LineWithDataZoomConfig, LineCommonConfig, LineLinearSingleConfig, LineGradientSingleConfig, LineGradientsConfig]
|
||||||
|
@ -39,6 +39,10 @@ export const requestConfig: RequestConfigType = {
|
|||||||
},
|
},
|
||||||
Header: {},
|
Header: {},
|
||||||
Params: {}
|
Params: {}
|
||||||
|
},
|
||||||
|
requestBodyJSONPre: {
|
||||||
|
enable: false,
|
||||||
|
handler: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +94,7 @@ export class PublicConfigClass implements PublicConfigType {
|
|||||||
// 请求
|
// 请求
|
||||||
public request = cloneDeep(requestConfig)
|
public request = cloneDeep(requestConfig)
|
||||||
// 数据过滤
|
// 数据过滤
|
||||||
public filter = undefined
|
public filter: undefined | string = undefined
|
||||||
// 事件
|
// 事件
|
||||||
public events = {
|
public events = {
|
||||||
baseEvent: {
|
baseEvent: {
|
||||||
|
@ -130,7 +130,7 @@ export type EditCanvasConfigType = {
|
|||||||
// 图表主题颜色
|
// 图表主题颜色
|
||||||
[EditCanvasConfigEnum.CHART_THEME_COLOR]: ChartColorsNameType
|
[EditCanvasConfigEnum.CHART_THEME_COLOR]: ChartColorsNameType
|
||||||
// 自定义图表主题颜色
|
// 自定义图表主题颜色
|
||||||
[EditCanvasConfigEnum.CHART_CUSTOM_THEME_COLOR_INFO]?: CustomColorsType[]
|
[EditCanvasConfigEnum.CHART_CUSTOM_THEME_COLOR_INFO]?: CustomColorsType[]
|
||||||
// 图表全局配置
|
// 图表全局配置
|
||||||
[EditCanvasConfigEnum.CHART_THEME_SETTING]: GlobalThemeJsonType
|
[EditCanvasConfigEnum.CHART_THEME_SETTING]: GlobalThemeJsonType
|
||||||
// 图表主题颜色
|
// 图表主题颜色
|
||||||
@ -232,6 +232,10 @@ export interface RequestConfigType extends RequestPublicConfigType {
|
|||||||
requestSQLContent: {
|
requestSQLContent: {
|
||||||
sql: string
|
sql: string
|
||||||
}
|
}
|
||||||
|
requestBodyJSONPre: {
|
||||||
|
enable: boolean
|
||||||
|
handler: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store 类型
|
// Store 类型
|
||||||
|
@ -51,9 +51,10 @@
|
|||||||
<monaco-editor
|
<monaco-editor
|
||||||
v-model:modelValue="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType]"
|
v-model:modelValue="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType]"
|
||||||
width="600px"
|
width="600px"
|
||||||
height="200px"
|
height="300px"
|
||||||
language="json"
|
language="json"
|
||||||
/>
|
/>
|
||||||
|
<request-header-pre :targetDataRequest="targetDataRequest" style="margin-top: 20px" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- xml -->
|
<!-- xml -->
|
||||||
@ -92,6 +93,7 @@ import { RequestHeaderTable } from '../RequestHeaderTable/index'
|
|||||||
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
|
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
|
||||||
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
|
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
|
||||||
import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
|
import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
|
||||||
|
import { RequestHeaderPre } from '../RequestHeaderPre'
|
||||||
import {
|
import {
|
||||||
RequestParamsTypeEnum,
|
RequestParamsTypeEnum,
|
||||||
RequestContentTypeEnum,
|
RequestContentTypeEnum,
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
import RequestHeaderPre from './index.vue'
|
||||||
|
|
||||||
|
export { RequestHeaderPre }
|
@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div style="display: flex;align-items: center; margin-top: 2px;">
|
||||||
|
<n-space :size="5">
|
||||||
|
<n-text class="text" depth="2">动态JSON</n-text>
|
||||||
|
<n-switch v-model:value="requestBodyJSONPre.enable" size="small" />
|
||||||
|
<n-text class="text" depth="2">启用将覆盖原JSON</n-text>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
<n-space vertical>
|
||||||
|
<n-tag type="info">
|
||||||
|
<span class="func-keyword">function</span> preHandler(global) {
|
||||||
|
</n-tag>
|
||||||
|
<monaco-editor v-model:modelValue="requestBodyJSONPre.handler" width="600px" height="300px" language="javascript" />
|
||||||
|
<n-tag type="info">}</n-tag>
|
||||||
|
</n-space>
|
||||||
|
<n-space vertical>
|
||||||
|
<n-text class="text" depth="2">处理结果</n-text>
|
||||||
|
<n-code style="height: 300px" :code="result || '暂无'" language="json" :word-wrap="true"></n-code>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, PropType, ref, toRefs } from 'vue'
|
||||||
|
import { MonacoEditor } from '@/components/Pages/MonacoEditor'
|
||||||
|
import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { getToken } from '@/api/path/business.api'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
targetDataRequest: Object as PropType<RequestConfigType>
|
||||||
|
})
|
||||||
|
const { requestBodyJSONPre } = props.targetDataRequest as RequestConfigType
|
||||||
|
|
||||||
|
const errorFlag = ref(false)
|
||||||
|
const result = computed(() => {
|
||||||
|
try {
|
||||||
|
let str = ''
|
||||||
|
if(requestBodyJSONPre) str = requestBodyJSONPre.handler
|
||||||
|
const fn = new Function('global', str)
|
||||||
|
const global = {
|
||||||
|
moment,
|
||||||
|
getToken
|
||||||
|
}
|
||||||
|
const res = fn(global)
|
||||||
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
|
errorFlag.value = false
|
||||||
|
// @ts-ignore
|
||||||
|
console.log(toString(res))
|
||||||
|
// @ts-ignore
|
||||||
|
return JSON.stringify(res, null, 2)
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
|
errorFlag.value = true
|
||||||
|
return `处理函数错误,日志:${error}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.text{
|
||||||
|
text-align: left;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-modal class="go-chart-data-request" v-model:show="modelShowRef" :mask-closable="false" :closeOnEsc="false">
|
<n-modal class="go-chart-data-request" v-model:show="modelShowRef" :on-esc="closeHandle" :mask-closable="false" :closeOnEsc="true">
|
||||||
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px">
|
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px">
|
||||||
<template #header></template>
|
<template #header></template>
|
||||||
<template #header-extra> </template>
|
<template #header-extra> </template>
|
||||||
|
@ -191,13 +191,13 @@ const btnList: BtnListType[] = [
|
|||||||
icon: DownloadIcon,
|
icon: DownloadIcon,
|
||||||
handle: exportHandle
|
handle: exportHandle
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
key: 'edit',
|
// key: 'edit',
|
||||||
type: TypeEnum.BUTTON,
|
// type: TypeEnum.BUTTON,
|
||||||
name: '编辑',
|
// name: '编辑',
|
||||||
icon: CreateIcon,
|
// icon: CreateIcon,
|
||||||
handle: editHandle
|
// handle: editHandle
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
key: 'setting',
|
key: 'setting',
|
||||||
type: TypeEnum.BUTTON,
|
type: TypeEnum.BUTTON,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { ChartEnum } from '@/enums/pageEnum'
|
import { ChartEnum, PreviewEnum } from '@/enums/pageEnum'
|
||||||
import { fetchPathByName, routerTurnByPath, openNewWindow, previewPath } from '@/utils'
|
import { fetchPathByName, routerTurnByPath, openNewWindow, previewPath } from '@/utils'
|
||||||
import { Chartype } from '../../../index.d'
|
import { Chartype } from '../../../index.d'
|
||||||
export const useModalDataInit = () => {
|
export const useModalDataInit = () => {
|
||||||
@ -28,7 +28,9 @@ export const useModalDataInit = () => {
|
|||||||
|
|
||||||
// 预览处理
|
// 预览处理
|
||||||
const previewHandle = (cardData: Chartype) => {
|
const previewHandle = (cardData: Chartype) => {
|
||||||
openNewWindow(previewPath(cardData.id))
|
const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href')
|
||||||
|
routerTurnByPath(path, [cardData.id], undefined, true, true)
|
||||||
|
// openNewWindow(previewPath(cardData.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user