mirror of
https://gitee.com/dromara/go-view.git
synced 2025-02-24 16:22:57 +08:00
feat: 新增全局请求数据处理
This commit is contained in:
parent
d4bfdd1e91
commit
f178b0e0ca
@ -4,7 +4,7 @@ import { http } from '@/api/http'
|
||||
import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d'
|
||||
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||
import { RequestDataTypeEnum } from '@/enums/httpEnum'
|
||||
import { isPreview, newFunctionHandle } from '@/utils'
|
||||
import { isPreview, newFunctionHandle, intervalUnitHandle } from '@/utils'
|
||||
|
||||
// 获取类型
|
||||
type ChartEditStoreType = typeof useChartEditStore
|
||||
@ -25,7 +25,7 @@ export const useChartDataFetch = (
|
||||
|
||||
const requestIntervalFn = () => {
|
||||
const chartEditStore = useChartEditStore()
|
||||
const { requestOriginUrl, requestInterval } = toRefs(chartEditStore.getRequestGlobalConfig)
|
||||
const { requestOriginUrl, requestInterval, requestIntervalUnit } = toRefs(chartEditStore.getRequestGlobalConfig)
|
||||
// 组件类型
|
||||
const { chartFrame } = targetComponent.chartConfig
|
||||
// 请求配置
|
||||
@ -35,45 +35,52 @@ export const useChartDataFetch = (
|
||||
requestUrl,
|
||||
requestInterval: targetInterval
|
||||
} = toRefs(targetComponent.request)
|
||||
|
||||
// 非请求类型
|
||||
if (requestDataType.value !== RequestDataTypeEnum.AJAX) return
|
||||
// 处理地址
|
||||
if (requestUrl?.value && requestInterval.value > 0) {
|
||||
// requestOriginUrl 允许为空
|
||||
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value
|
||||
if (!completePath) return
|
||||
if (requestDataType.value !== RequestDataTypeEnum.AJAX || !requestInterval) return
|
||||
|
||||
try {
|
||||
// 处理地址
|
||||
// @ts-ignore
|
||||
if (requestUrl?.value && requestInterval && requestInterval.value > 0) {
|
||||
// requestOriginUrl 允许为空
|
||||
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value
|
||||
if (!completePath) return
|
||||
|
||||
clearInterval(fetchInterval)
|
||||
clearInterval(fetchInterval)
|
||||
|
||||
const fetchFn = async () => {
|
||||
const res: any = await http(requestHttpType.value)(completePath || '', {})
|
||||
if (res.data) {
|
||||
try {
|
||||
const filter = targetComponent.filter
|
||||
// 更新回调函数
|
||||
if (updateCallback) {
|
||||
updateCallback(newFunctionHandle(res.data, filter))
|
||||
} else {
|
||||
// eCharts 组件配合 vChart 库更新方式
|
||||
if (chartFrame === ChartFrameEnum.ECHARTS) {
|
||||
if (vChartRef.value) {
|
||||
vChartRef.value.setOption({ dataset: newFunctionHandle(res.data, filter) })
|
||||
const fetchFn = async () => {
|
||||
const res: any = await http(requestHttpType.value)(completePath || '', {})
|
||||
if (res.data) {
|
||||
try {
|
||||
const filter = targetComponent.filter
|
||||
// 更新回调函数
|
||||
if (updateCallback) {
|
||||
updateCallback(newFunctionHandle(res.data, filter))
|
||||
} else {
|
||||
// eCharts 组件配合 vChart 库更新方式
|
||||
if (chartFrame === ChartFrameEnum.ECHARTS) {
|
||||
if (vChartRef.value) {
|
||||
vChartRef.value.setOption({ dataset: newFunctionHandle(res.data, filter) })
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 立即调用
|
||||
fetchFn()
|
||||
|
||||
// 开启定时
|
||||
const time = targetInterval && targetInterval.value ? targetInterval.value : requestInterval.value
|
||||
|
||||
// 处理单位时间
|
||||
fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, requestIntervalUnit.value))
|
||||
}
|
||||
|
||||
// 立即调用
|
||||
fetchFn()
|
||||
|
||||
// 开启定时
|
||||
const time = targetInterval && targetInterval.value ? targetInterval.value : requestInterval.value
|
||||
fetchInterval = setInterval(fetchFn, time * 1000)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
isPreview() && requestIntervalFn()
|
||||
|
@ -53,6 +53,8 @@ import {
|
||||
ArrowForward as ArrowForwardIcon,
|
||||
Planet as PawIcon,
|
||||
Search as SearchIcon,
|
||||
ChevronUpOutline as ChevronUpOutlineIcon,
|
||||
ChevronDownOutline as ChevronDownOutlineIcon
|
||||
} from '@vicons/ionicons5'
|
||||
|
||||
import {
|
||||
@ -194,7 +196,11 @@ const ionicons5 = {
|
||||
// 搜索(放大镜)
|
||||
SearchIcon,
|
||||
// 过滤器
|
||||
FilterIcon
|
||||
FilterIcon,
|
||||
// 向上
|
||||
ChevronUpOutlineIcon,
|
||||
// 向下
|
||||
ChevronDownOutlineIcon
|
||||
}
|
||||
|
||||
const carbon = {
|
||||
|
@ -137,24 +137,24 @@ export enum ChartEditStoreEnum {
|
||||
|
||||
// 请求公共类型
|
||||
type RequestPublicConfigType = {
|
||||
// 组件定制轮询时间
|
||||
requestInterval?: number
|
||||
// 时间单位(时分秒)
|
||||
requestIntervalUnit: RequestHttpIntervalEnum
|
||||
// 请求内容
|
||||
requestParams: RequestParams
|
||||
// 请求体类型
|
||||
requestParamsBodyType: RequestBodyEnum
|
||||
}
|
||||
|
||||
// 全局的图表请求配置
|
||||
export interface RequestGlobalConfigType extends RequestPublicConfigType {
|
||||
// 组件定制轮询时间
|
||||
requestInterval: number
|
||||
// 请求源地址
|
||||
requestOriginUrl?: string
|
||||
}
|
||||
|
||||
// 单个图表请求配置
|
||||
export interface RequestConfigType extends RequestPublicConfigType {
|
||||
// 组件定制轮询时间
|
||||
requestInterval?: number
|
||||
// 获取数据的方式
|
||||
requestDataType: RequestDataTypeEnum
|
||||
// 请求方式 get/post/del/put/patch
|
||||
@ -163,6 +163,8 @@ export interface RequestConfigType extends RequestPublicConfigType {
|
||||
requestUrl?: string
|
||||
// 请求内容主体方式 普通/sql
|
||||
requestContentType: RequestContentTypeEnum
|
||||
// 请求体类型
|
||||
requestParamsBodyType: RequestBodyEnum
|
||||
// SQL 请求对象
|
||||
requestSQLContent: {
|
||||
sql: string
|
||||
|
@ -112,7 +112,6 @@ export const useChartEditStore = defineStore({
|
||||
requestOriginUrl: '',
|
||||
requestInterval: requestInterval,
|
||||
requestIntervalUnit: requestIntervalUnit,
|
||||
requestParamsBodyType: RequestBodyEnum.NONE,
|
||||
requestParams: {
|
||||
Body: {
|
||||
"form-data": {},
|
||||
|
@ -6,7 +6,8 @@ import Image_404 from '../assets/images/exception/image-404.png'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { downloadByA } from './file'
|
||||
import { toString } from './type'
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import { RequestHttpIntervalEnum } from '@/enums/httpEnum'
|
||||
|
||||
/**
|
||||
* * 判断是否是开发环境
|
||||
@ -203,7 +204,7 @@ export const newFunctionHandle = (
|
||||
try {
|
||||
if (!funcStr) return data
|
||||
const fn = new Function('data', funcStr)
|
||||
const fnRes = fn( cloneDeep(data))
|
||||
const fnRes = fn(cloneDeep(data))
|
||||
const resHandle = isToString ? toString(fnRes) : fnRes
|
||||
// 成功回调
|
||||
successCallBack && successCallBack(resHandle)
|
||||
@ -214,3 +215,28 @@ export const newFunctionHandle = (
|
||||
return '函数执行错误'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* * 处理请求事件单位
|
||||
* @param num 时间间隔
|
||||
* @param unit RequestHttpIntervalEnum
|
||||
* @return number 秒数
|
||||
*/
|
||||
export const intervalUnitHandle = (num: number, unit: RequestHttpIntervalEnum) => {
|
||||
switch (unit) {
|
||||
// 秒
|
||||
case RequestHttpIntervalEnum.SECOND:
|
||||
return num * 1000
|
||||
// 分
|
||||
case RequestHttpIntervalEnum.MINUTE:
|
||||
return num * 1000 * 60
|
||||
// 时
|
||||
case RequestHttpIntervalEnum.HOUR:
|
||||
return num * 1000 * 60 * 60
|
||||
// 天
|
||||
case RequestHttpIntervalEnum.DAY:
|
||||
return num * 1000 * 60 * 60 * 24
|
||||
default:
|
||||
return num * 1000
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,6 @@ const fetchTargetData = async () => {
|
||||
try {
|
||||
const { requestUrl, requestHttpType } = targetData.value.request
|
||||
if (!requestUrl) {
|
||||
window['$message'].warning('请求参数不正确,请检查!')
|
||||
sourceData.value = '请求参数不正确,请检查!'
|
||||
return
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
>
|
||||
<!-- 源地址 -->
|
||||
<setting-item name="前置 URL">
|
||||
<n-input v-model:value.trim="requestOriginUrl" :disabled="disabled" placeholder="http://127.0.0.1/"></n-input>
|
||||
<n-input v-model:value.trim="requestOriginUrl" :disabled="editDisabled" placeholder="http://127.0.0.1/"></n-input>
|
||||
</setting-item>
|
||||
<setting-item name="更新间隔(为 0 表示不更新)">
|
||||
<n-input-group>
|
||||
@ -19,7 +19,7 @@
|
||||
v-model:value.trim="requestInterval"
|
||||
min="0"
|
||||
:show-button="false"
|
||||
:disabled="disabled"
|
||||
:disabled="editDisabled"
|
||||
placeholder="请输入数字"
|
||||
>
|
||||
</n-input-number>
|
||||
@ -28,11 +28,11 @@
|
||||
class="select-time-options"
|
||||
v-model:value="requestIntervalUnit"
|
||||
:options="selectTimeOptions"
|
||||
:disabled="disabled"
|
||||
:disabled="editDisabled"
|
||||
/>
|
||||
</n-input-group>
|
||||
</setting-item>
|
||||
<n-button v-show="disabled" type="primary" secondary @click="disabled = false">
|
||||
<n-button v-show="editDisabled" type="primary" secondary @click="editDisabled = false">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<pencil-icon />
|
||||
@ -41,23 +41,61 @@
|
||||
编辑配置
|
||||
</n-button>
|
||||
</setting-item-box>
|
||||
<!-- table 内容体 -->
|
||||
<n-collapse-transition :show="showTable">
|
||||
<request-global-header-table :editDisabled="editDisabled"></request-global-header-table>
|
||||
</n-collapse-transition>
|
||||
<!-- 箭头 -->
|
||||
<div v-if="showTable" class="go-flex-center go-mt-3 down" @click="showTable = false">
|
||||
<n-icon size="32">
|
||||
<chevron-up-outline-icon />
|
||||
</n-icon>
|
||||
</div>
|
||||
<div v-else class="go-flex-center go-mt-3 down" @click="showTable = true">
|
||||
<n-tooltip trigger="hover" placement="top" :keep-alive-on-hover="false">
|
||||
<template #trigger>
|
||||
<n-icon size="32">
|
||||
<chevron-down-outline-icon />
|
||||
</n-icon>
|
||||
</template>
|
||||
展开
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, toRefs } from 'vue'
|
||||
import { useDesignStore } from '@/store/modules/designStore/designStore'
|
||||
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
|
||||
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
|
||||
import { selectTypeOptions, selectTimeOptions } from '@/views/chart/ContentConfigurations/components/ChartData/index.d'
|
||||
import { RequestGlobalHeaderTable } from '../RequestGlobalHeaderTable'
|
||||
import { icon } from '@/plugins'
|
||||
|
||||
const { PencilIcon } = icon.ionicons5
|
||||
const { targetData, chartEditStore } = useTargetData()
|
||||
const { PencilIcon, ChevronDownOutlineIcon, ChevronUpOutlineIcon } = icon.ionicons5
|
||||
const { chartEditStore } = useTargetData()
|
||||
const { requestOriginUrl, requestInterval, requestIntervalUnit } = toRefs(chartEditStore.getRequestGlobalConfig)
|
||||
const disabled = ref(true)
|
||||
const editDisabled = ref(true)
|
||||
|
||||
const designStore = useDesignStore()
|
||||
const themeColor = ref(designStore.getAppTheme)
|
||||
|
||||
const showTable = ref(true)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.n-card-shallow {
|
||||
&:hover {
|
||||
border-color: v-bind('themeColor');
|
||||
}
|
||||
}
|
||||
.down {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: v-bind('themeColor');
|
||||
}
|
||||
}
|
||||
.select-time-number {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
import RequestGlobalHeaderTable from './index.vue'
|
||||
|
||||
export { RequestGlobalHeaderTable }
|
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-tabs type="line" animated v-model:value="tabValue">
|
||||
<n-tab v-for="item in tabs" :key="item" :name="item" :tab="item"> {{ item }} </n-tab>
|
||||
</n-tabs>
|
||||
<div class="go-mt-3">
|
||||
<request-header-table :editDisabled="$attrs.editDisabled" :target="requestParams[tabValue]" @update="updateRequestParams"></request-header-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, toRefs } from 'vue'
|
||||
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
|
||||
import { RequestHeaderTable } from '../RequestHeaderTable'
|
||||
import { RequestParamsTypeEnum, RequestParamsObjType } from '@/enums/httpEnum'
|
||||
|
||||
const { chartEditStore } = useTargetData()
|
||||
const { requestParams } = toRefs(chartEditStore.getRequestGlobalConfig)
|
||||
|
||||
const tabValue = ref<RequestParamsTypeEnum>(RequestParamsTypeEnum.HEADER)
|
||||
const tabs = [RequestParamsTypeEnum.HEADER, RequestParamsTypeEnum.COOKIE]
|
||||
|
||||
// 更新表格参数
|
||||
const updateRequestParams = (paramsObj: RequestParamsObjType) => {
|
||||
if (tabValue.value === RequestParamsTypeEnum.HEADER || tabValue.value === RequestParamsTypeEnum.COOKIE) {
|
||||
requestParams.value[tabValue.value] = paramsObj
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -12,7 +12,7 @@
|
||||
</n-tabs>
|
||||
|
||||
<!-- 各个页面 -->
|
||||
<div class="tabs-content go-mt-3">
|
||||
<div class="go-mt-3">
|
||||
<div v-if="tabValue !== RequestParamsTypeEnum.BODY">
|
||||
<request-header-table :target="requestParams[tabValue]" @update="updateRequestParams"></request-header-table>
|
||||
</div>
|
||||
@ -80,7 +80,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, toRefs } from 'vue'
|
||||
import { ref, toRefs } from 'vue'
|
||||
|
||||
import { MonacoEditor } from '@/components/Pages/MonacoEditor'
|
||||
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
|
||||
|
@ -1,55 +1,54 @@
|
||||
<template>
|
||||
<n-scrollbar style="max-height: 250px">
|
||||
<n-table class="go-request-header-table-box" :single-line="false" size="small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
<th>操作</th>
|
||||
<th>结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in tableArray.content" :key="index">
|
||||
<td>
|
||||
{{ index + 1 }}
|
||||
</td>
|
||||
<td>
|
||||
<n-input v-model:value="item.key" type="text" size="small" @blur="blur" />
|
||||
</td>
|
||||
<td>
|
||||
<n-input v-model:value="item.value" type="text" size="small" @blur="blur" />
|
||||
</td>
|
||||
<td>
|
||||
<div style="width: 80px">
|
||||
<n-button class="go-ml-2" type="primary" size="small" ghost @click="add(index)">+</n-button>
|
||||
<n-button
|
||||
class="go-ml-2"
|
||||
type="warning"
|
||||
size="small"
|
||||
ghost
|
||||
:disabled="index === 0"
|
||||
@click="remove(index)"
|
||||
>
|
||||
-
|
||||
</n-button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<n-button v-if="item.error" class="go-ml-2" text type="error"> 格式错误 </n-button>
|
||||
<n-button v-else class="go-ml-2" text type="success"> 格式通过 </n-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</n-table>
|
||||
</n-scrollbar>
|
||||
<n-table class="go-request-header-table-box" :single-line="false" size="small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
<th>操作</th>
|
||||
<th>结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in tableArray.content" :key="index">
|
||||
<td>
|
||||
{{ index + 1 }}
|
||||
</td>
|
||||
<td>
|
||||
<n-input v-model:value="item.key" :disabled="editDisabled" type="text" size="small" @blur="blur" />
|
||||
</td>
|
||||
<td>
|
||||
<n-input v-model:value="item.value" :disabled="editDisabled" type="text" size="small" @blur="blur" />
|
||||
</td>
|
||||
<td>
|
||||
<div style="width: 80px">
|
||||
<n-button class="go-ml-2" type="primary" size="small" ghost :disabled="editDisabled" @click="add(index)"
|
||||
>+</n-button
|
||||
>
|
||||
<n-button
|
||||
class="go-ml-2"
|
||||
type="warning"
|
||||
size="small"
|
||||
ghost
|
||||
:disabled="index === 0 && editDisabled"
|
||||
@click="remove(index)"
|
||||
>
|
||||
-
|
||||
</n-button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<n-button v-if="item.error" class="go-ml-2" text type="error"> 格式错误 </n-button>
|
||||
<n-button v-else class="go-ml-2" text type="primary"> 格式通过 </n-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</n-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType, reactive, ref, toRefs, watch } from 'vue'
|
||||
import { RequestBodyEnum, RequestParamsObjType } from '@/enums/httpEnum'
|
||||
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
|
||||
import { RequestParamsObjType } from '@/enums/httpEnum'
|
||||
|
||||
const emits = defineEmits(['update'])
|
||||
|
||||
@ -58,12 +57,14 @@ const props = defineProps({
|
||||
type: Object as PropType<RequestParamsObjType>,
|
||||
required: true,
|
||||
default: () => {}
|
||||
},
|
||||
editDisabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const { targetData } = useTargetData()
|
||||
const { requestParams } = toRefs(targetData.value.request)
|
||||
|
||||
// 错误标识
|
||||
const error = ref(false)
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
<n-input-number
|
||||
v-model:value.trim="requestInterval"
|
||||
class="select-time-number"
|
||||
min="5"
|
||||
min="1"
|
||||
:show-button="false"
|
||||
placeholder="可以为空"
|
||||
>
|
||||
|
@ -3,19 +3,23 @@
|
||||
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px">
|
||||
<template #header></template>
|
||||
<template #header-extra> </template>
|
||||
<n-space vertical>
|
||||
<request-global-config></request-global-config>
|
||||
<request-target-config></request-target-config>
|
||||
</n-space>
|
||||
<n-scrollbar style="max-height: 718px">
|
||||
<div class="go-pr-3">
|
||||
<n-space vertical>
|
||||
<request-global-config></request-global-config>
|
||||
<request-target-config></request-target-config>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
<!-- 底部 -->
|
||||
<template #action>
|
||||
<n-space justify="space-between">
|
||||
<div>
|
||||
<n-text>「 {{chartConfig.categoryName ||rename}} 」</n-text>
|
||||
<n-text>「 {{ chartConfig.categoryName || rename }} 」</n-text>
|
||||
<n-text>—— </n-text>
|
||||
<n-tag type="primary" :bordered="false"> {{requestContentTypeObj[requestContentType]}} </n-tag>
|
||||
<n-tag type="primary" :bordered="false"> {{ requestContentTypeObj[requestContentType] }} </n-tag>
|
||||
</div>
|
||||
<n-button type="primary" ghost @click="closeHandle">关闭</n-button>
|
||||
<n-button type="primary" ghost @click="closeHandle">确认</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-card>
|
||||
@ -38,7 +42,7 @@ const { requestContentType } = toRefs(targetData.value.request)
|
||||
|
||||
const requestContentTypeObj = {
|
||||
[RequestContentTypeEnum.DEFAULT]: '普通请求',
|
||||
[RequestContentTypeEnum.SQL]: 'SQL 请求',
|
||||
[RequestContentTypeEnum.SQL]: 'SQL 请求'
|
||||
}
|
||||
|
||||
defineProps({
|
||||
@ -59,5 +63,10 @@ const closeHandle = () => {
|
||||
.n-card-shallow {
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
@include deep() {
|
||||
& > .n-card__content {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user